@sema-agent/server 7.141.0 → 7.143.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 (341) hide show
  1. package/MIGRATION.md +657 -0
  2. package/USAGE.md +102 -30
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +61 -43
  5. package/dist/approval-ask-audit-store.js +69 -44
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +126 -27
  9. package/dist/approval-card.js +103 -23
  10. package/dist/approval-crash-convergence.d.ts +17 -7
  11. package/dist/approval-crash-convergence.js +94 -22
  12. package/dist/approval-deny-reasons.d.ts +34 -30
  13. package/dist/approval-deny-reasons.js +21 -6
  14. package/dist/approval-policy-settlement.d.ts +1 -1
  15. package/dist/approval-queue-kind.d.ts +27 -0
  16. package/dist/approval-queue-kind.js +6 -0
  17. package/dist/approval-reconciler.d.ts +41 -9
  18. package/dist/approval-reconciler.js +45 -42
  19. package/dist/audit.js +4 -2
  20. package/dist/auth-bridge.d.ts +9 -6
  21. package/dist/auth-bridge.js +9 -3
  22. package/dist/boot/adoption.js +2 -1
  23. package/dist/boot/budget-tracing.d.ts +17 -1
  24. package/dist/boot/budget-tracing.js +10 -6
  25. package/dist/boot/device-lane.d.ts +1 -1
  26. package/dist/boot/execution-env.d.ts +24 -4
  27. package/dist/boot/execution-env.js +26 -23
  28. package/dist/boot/execution-lane.js +2 -1
  29. package/dist/boot/exit-boundary.js +5 -4
  30. package/dist/boot/governance-seams.d.ts +26 -41
  31. package/dist/boot/governance-seams.js +61 -28
  32. package/dist/boot/late-binds.d.ts +7 -4
  33. package/dist/boot/late-binds.js +3 -1
  34. package/dist/boot/leader.js +2 -4
  35. package/dist/boot/memory-consolidation.d.ts +0 -8
  36. package/dist/boot/memory-consolidation.js +5 -4
  37. package/dist/boot/parked-revive-gate.js +2 -1
  38. package/dist/boot/permission-rules-audit.d.ts +0 -28
  39. package/dist/boot/permission-rules-audit.js +2 -1
  40. package/dist/boot/read-deny-anchor-audit.js +2 -1
  41. package/dist/boot/reapers.js +9 -7
  42. package/dist/boot/resolve-spec.d.ts +1 -1
  43. package/dist/boot/resolve-spec.js +19 -10
  44. package/dist/boot/retention-lane.js +5 -3
  45. package/dist/boot/runner-deps.d.ts +10 -7
  46. package/dist/boot/runner-deps.js +20 -29
  47. package/dist/boot/runtime-caps.js +3 -2
  48. package/dist/boot/session-faces.js +4 -3
  49. package/dist/boot/shutdown.d.ts +9 -0
  50. package/dist/boot/shutdown.js +19 -4
  51. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  52. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  53. package/dist/boot/stage-03-store-backend.js +1 -2
  54. package/dist/boot/stage-04-stores.d.ts +5 -2
  55. package/dist/boot/stage-04-stores.js +6 -7
  56. package/dist/boot/stage-05-execution-env.d.ts +6 -3
  57. package/dist/boot/stage-05-execution-env.js +8 -6
  58. package/dist/boot/stage-06-runners.d.ts +6 -3
  59. package/dist/boot/stage-06-runners.js +3 -4
  60. package/dist/boot/stage-07-capability-layer.d.ts +6 -3
  61. package/dist/boot/stage-07-capability-layer.js +7 -2
  62. package/dist/boot/stage-08-reapers.d.ts +6 -3
  63. package/dist/boot/stage-08-reapers.js +3 -2
  64. package/dist/boot/stage-09-leader.d.ts +6 -3
  65. package/dist/boot/stage-10-http-server.d.ts +6 -3
  66. package/dist/boot/stage-10-http-server.js +14 -5
  67. package/dist/boot/stores.d.ts +5 -2
  68. package/dist/boot/stores.js +10 -9
  69. package/dist/boot/web-admin.d.ts +5 -1
  70. package/dist/boot/web-admin.js +48 -36
  71. package/dist/boot/webfetch-summarize-lane.js +2 -1
  72. package/dist/boot/workflow-orchestration.js +3 -2
  73. package/dist/capabilities/builtin-tools.d.ts +2 -1
  74. package/dist/capabilities/builtin-tools.js +2 -4
  75. package/dist/capabilities/composite-members.d.ts +2 -1
  76. package/dist/capabilities/composite-members.js +2 -1
  77. package/dist/capabilities/hands-lane.d.ts +9 -39
  78. package/dist/capabilities/hands-lane.js +8 -13
  79. package/dist/capabilities/mcp-probe.d.ts +3 -2
  80. package/dist/capabilities/scenarios.d.ts +11 -1
  81. package/dist/capabilities/scenarios.js +1 -0
  82. package/dist/capabilities/skills.d.ts +1 -17
  83. package/dist/capabilities/skills.js +0 -8
  84. package/dist/capability-facts.d.ts +31 -7
  85. package/dist/capability-facts.js +27 -4
  86. package/dist/card-void.d.ts +6 -9
  87. package/dist/config-catalog.js +2 -1
  88. package/dist/config-dir-trust.js +58 -29
  89. package/dist/config-invariants.d.ts +7 -1
  90. package/dist/config-invariants.js +7 -0
  91. package/dist/config-provider.d.ts +10 -0
  92. package/dist/config-provider.js +15 -4
  93. package/dist/config-root-migration.d.ts +13 -0
  94. package/dist/config-root-migration.js +99 -0
  95. package/dist/config-types.d.ts +19 -8
  96. package/dist/config.d.ts +10 -3
  97. package/dist/config.js +18 -4
  98. package/dist/delegated-session-owner.d.ts +5 -3
  99. package/dist/delegated-session-owner.js +1 -1
  100. package/dist/device-ws-protocol.d.ts +1 -0
  101. package/dist/device-ws-protocol.js +2 -1
  102. package/dist/env-model-table-gate.d.ts +53 -0
  103. package/dist/env-model-table-gate.js +53 -0
  104. package/dist/execution-lane-caps.d.ts +2 -1
  105. package/dist/fleet/fleet-bus.d.ts +34 -55
  106. package/dist/fleet/fleet-bus.js +35 -100
  107. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  108. package/dist/fleet/fleet-reconciler.js +1 -1
  109. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  110. package/dist/fleet/subagent-tail-bus.js +15 -28
  111. package/dist/hooks/hook-runner.d.ts +6 -0
  112. package/dist/hooks/hook-runner.js +58 -8
  113. package/dist/host-decision.d.ts +54 -20
  114. package/dist/host-decision.js +42 -14
  115. package/dist/http/admission.d.ts +1 -1
  116. package/dist/http/admission.js +7 -3
  117. package/dist/http/approval-sweeps.js +11 -8
  118. package/dist/http/caller-trust.d.ts +1 -1
  119. package/dist/http/dispatch.js +4 -2
  120. package/dist/http/durable-run-leg.js +2 -2
  121. package/dist/http/engine-refusal.d.ts +16 -0
  122. package/dist/http/engine-refusal.js +17 -0
  123. package/dist/http/execution-context.d.ts +2 -2
  124. package/dist/http/idempotency.d.ts +1 -1
  125. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  126. package/dist/http/memory-compliance-failure-reply.js +4 -6
  127. package/dist/http/owner-access.d.ts +11 -0
  128. package/dist/http/owner-access.js +13 -0
  129. package/dist/http/principal-gate.d.ts +3 -1
  130. package/dist/http/principal-gate.js +17 -8
  131. package/dist/http/resume-legs.d.ts +21 -7
  132. package/dist/http/resume-legs.js +76 -58
  133. package/dist/http/resume-restatement.d.ts +39 -0
  134. package/dist/http/resume-restatement.js +25 -0
  135. package/dist/http/route-ctx.d.ts +35 -10
  136. package/dist/http/route-ctx.js +1 -1
  137. package/dist/http/route-table.js +2 -0
  138. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  139. package/dist/http/routes/approvals-assistant.js +142 -31
  140. package/dist/http/routes/attachments.js +18 -0
  141. package/dist/http/routes/capabilities.js +16 -4
  142. package/dist/http/routes/memory-policy.d.ts +2 -2
  143. package/dist/http/routes/memory-policy.js +27 -7
  144. package/dist/http/routes/notify-wake.js +23 -25
  145. package/dist/http/routes/rules.js +48 -13
  146. package/dist/http/routes/runs.js +12 -11
  147. package/dist/http/routes/session-memory-sync.d.ts +91 -0
  148. package/dist/http/routes/session-memory-sync.js +174 -0
  149. package/dist/http/routes/session-sync.js +13 -20
  150. package/dist/http/routes/sessions-list.d.ts +2 -1
  151. package/dist/http/routes/sessions.d.ts +2 -2
  152. package/dist/http/routes/sessions.js +22 -23
  153. package/dist/http/routes/tasks.js +34 -54
  154. package/dist/http/run-read-face.d.ts +7 -0
  155. package/dist/http/run-read-face.js +9 -3
  156. package/dist/http/send.d.ts +5 -0
  157. package/dist/http/send.js +9 -2
  158. package/dist/http/server.d.ts +27 -15
  159. package/dist/http/server.js +4 -6
  160. package/dist/http/stored-body-replay.js +15 -4
  161. package/dist/http/submit-replay.d.ts +4 -3
  162. package/dist/http/submit-replay.js +2 -2
  163. package/dist/http/sync-submit-body.d.ts +21 -4
  164. package/dist/http/sync-submit-body.js +13 -0
  165. package/dist/http/wire-types.d.ts +17 -7
  166. package/dist/identity-source.d.ts +21 -2
  167. package/dist/identity-source.js +6 -0
  168. package/dist/leader/leader.js +5 -2
  169. package/dist/leader/wire.d.ts +7 -1
  170. package/dist/leader/wire.js +6 -5
  171. package/dist/leg-facts.d.ts +37 -0
  172. package/dist/leg-facts.js +15 -0
  173. package/dist/memory-bundle-engine.d.ts +1 -4
  174. package/dist/memory-bundle-engine.js +2 -2
  175. package/dist/memory-incident-sink.d.ts +19 -6
  176. package/dist/memory-incident-sink.js +8 -4
  177. package/dist/memory-operator-faces.d.ts +30 -66
  178. package/dist/memory-operator-faces.js +17 -88
  179. package/dist/memory-scope-stores.d.ts +38 -40
  180. package/dist/memory-scope-stores.js +51 -36
  181. package/dist/memory-scope.d.ts +0 -9
  182. package/dist/memory-scope.js +0 -9
  183. package/dist/memory-sync-client.d.ts +21 -21
  184. package/dist/memory-sync-client.js +62 -43
  185. package/dist/memory-sync-identity.d.ts +24 -0
  186. package/dist/memory-sync-identity.js +57 -0
  187. package/dist/memory-sync.d.ts +27 -5
  188. package/dist/memory-sync.js +29 -7
  189. package/dist/model-plane-face.d.ts +13 -7
  190. package/dist/model-plane-face.js +2 -1
  191. package/dist/observability/fail-open.d.ts +47 -14
  192. package/dist/observability/fail-open.js +37 -11
  193. package/dist/observability/run-terminal.d.ts +92 -34
  194. package/dist/observability/run-terminal.js +171 -162
  195. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  196. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  197. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  198. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  199. package/dist/orchestration/workflow-park-decision.js +5 -3
  200. package/dist/org-delivery-gate.d.ts +11 -0
  201. package/dist/org-delivery-gate.js +19 -1
  202. package/dist/parked-decide.d.ts +13 -9
  203. package/dist/parked-decide.js +7 -7
  204. package/dist/parked-revive-surface.js +1 -1
  205. package/dist/peer-inbound-face.d.ts +127 -0
  206. package/dist/peer-inbound-face.js +158 -0
  207. package/dist/permission-rule-list.d.ts +13 -9
  208. package/dist/permission-rule-list.js +7 -3
  209. package/dist/plugins/approval-ask-store-file.d.ts +14 -4
  210. package/dist/plugins/approval-ask-store-file.js +64 -11
  211. package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
  212. package/dist/plugins/approval-ask-store-memory.js +17 -14
  213. package/dist/plugins/approval-ask-store-sql.d.ts +70 -23
  214. package/dist/plugins/approval-ask-store-sql.js +79 -30
  215. package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
  216. package/dist/plugins/checkpoint-store-sql.js +9 -7
  217. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  218. package/dist/plugins/file-history-store-sql.js +24 -19
  219. package/dist/plugins/file-run-store.d.ts +35 -1
  220. package/dist/plugins/file-run-store.js +108 -9
  221. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  222. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  223. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  224. package/dist/plugins/local-checkpoint-store.js +2 -2
  225. package/dist/plugins/local-session-store.d.ts +42 -9
  226. package/dist/plugins/local-session-store.js +83 -33
  227. package/dist/plugins/local-task-attachment-store.d.ts +12 -2
  228. package/dist/plugins/local-task-attachment-store.js +67 -43
  229. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  230. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  231. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  232. package/dist/plugins/mailbox-store-sql.js +76 -7
  233. package/dist/plugins/memory-key-guards.d.ts +3 -3
  234. package/dist/plugins/memory-run-store.d.ts +6 -1
  235. package/dist/plugins/memory-run-store.js +11 -1
  236. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  237. package/dist/plugins/memory-sync-store-pg.js +21 -0
  238. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  239. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  240. package/dist/plugins/one-time-migrations.d.ts +58 -2
  241. package/dist/plugins/one-time-migrations.js +161 -4
  242. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  243. package/dist/plugins/org-delivery-quota-store.js +2 -12
  244. package/dist/plugins/park-commit-watch.d.ts +1 -1
  245. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  246. package/dist/plugins/permission-rule-store-file.js +15 -2
  247. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  248. package/dist/plugins/permission-rule-store-sql.js +35 -2
  249. package/dist/plugins/remote-env-device.d.ts +8 -11
  250. package/dist/plugins/remote-env-device.js +6 -3
  251. package/dist/plugins/remote-env-e2b.d.ts +23 -4
  252. package/dist/plugins/remote-env-e2b.js +111 -28
  253. package/dist/plugins/remote-env-k8s.d.ts +61 -22
  254. package/dist/plugins/remote-env-k8s.js +133 -33
  255. package/dist/plugins/remote-env-local-docker.d.ts +15 -3
  256. package/dist/plugins/remote-env-local-docker.js +40 -12
  257. package/dist/plugins/retention-store-sql.d.ts +2 -2
  258. package/dist/plugins/run-store-sql.d.ts +9 -1
  259. package/dist/plugins/run-store-sql.js +9 -1
  260. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  261. package/dist/plugins/sandbox-read-floor.js +2 -7
  262. package/dist/plugins/sql-errors.d.ts +10 -1
  263. package/dist/plugins/sql-errors.js +13 -0
  264. package/dist/plugins/store-backend.d.ts +33 -35
  265. package/dist/plugins/store-backend.js +10 -1
  266. package/dist/plugins/store-contracts.d.ts +50 -1
  267. package/dist/plugins/store-contracts.js +6 -0
  268. package/dist/plugins/task-attachment-store.d.ts +112 -14
  269. package/dist/plugins/task-attachment-store.js +207 -29
  270. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  271. package/dist/plugins/tool-result-store-sql.js +23 -2
  272. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  273. package/dist/plugins/wiring-section-codec.js +55 -0
  274. package/dist/plugins/workspace-fill.d.ts +60 -0
  275. package/dist/plugins/workspace-fill.js +106 -0
  276. package/dist/project-bundle.d.ts +38 -0
  277. package/dist/project-bundle.js +609 -0
  278. package/dist/rule-entry-list.d.ts +3 -2
  279. package/dist/rule-entry-list.js +5 -1
  280. package/dist/rules-consent.d.ts +28 -5
  281. package/dist/rules-consent.js +40 -28
  282. package/dist/run-local.js +11 -13
  283. package/dist/runs.d.ts +12 -8
  284. package/dist/runs.js +6 -8
  285. package/dist/runtime-governance.d.ts +1 -1
  286. package/dist/sandbox-egress-policy.d.ts +4 -1
  287. package/dist/sandbox-egress-policy.js +3 -1
  288. package/dist/sandbox-git-plane.d.ts +31 -12
  289. package/dist/sandbox-git-plane.js +24 -4
  290. package/dist/security.d.ts +9 -0
  291. package/dist/security.js +8 -0
  292. package/dist/server-secret-env.d.ts +4 -0
  293. package/dist/session-error-wire.d.ts +36 -0
  294. package/dist/session-error-wire.js +13 -0
  295. package/dist/session-incarnation.d.ts +1 -1
  296. package/dist/session-notify-door.d.ts +106 -0
  297. package/dist/session-notify-door.js +93 -0
  298. package/dist/session-sync.d.ts +9 -14
  299. package/dist/session-sync.js +5 -5
  300. package/dist/session-workspace.d.ts +3 -13
  301. package/dist/session-workspace.js +0 -9
  302. package/dist/standing-answer-grant.d.ts +5 -0
  303. package/dist/standing-answer-grant.js +10 -0
  304. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  305. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  306. package/dist/task-mcp.d.ts +1 -1
  307. package/dist/task-settings.d.ts +21 -10
  308. package/dist/task-settings.js +46 -13
  309. package/dist/terminal.d.ts +39 -19
  310. package/dist/terminal.js +65 -22
  311. package/dist/tool-approval.d.ts +56 -37
  312. package/dist/tool-approval.js +78 -71
  313. package/dist/trace/core-keyset-guard.d.ts +134 -14
  314. package/dist/trace/engine-notice-wire.d.ts +9 -3
  315. package/dist/trace/engine-notice-wire.js +7 -3
  316. package/dist/trace/exit-seat.d.ts +9 -2
  317. package/dist/trace/exit-seat.js +1 -0
  318. package/dist/trace/ledger-sink.d.ts +10 -6
  319. package/dist/trace/ledger-sink.js +2 -2
  320. package/dist/trace/model-segment.d.ts +55 -25
  321. package/dist/trace/model-segment.js +101 -33
  322. package/dist/trace/project.d.ts +25 -5
  323. package/dist/trace/project.js +22 -0
  324. package/dist/trace/redact.d.ts +61 -9
  325. package/dist/trace/redact.js +1490 -468
  326. package/dist/trace/self-minted-wire.d.ts +745 -24
  327. package/dist/trace/self-minted-wire.js +239 -9
  328. package/dist/trace/wire-projection-faces.d.ts +2 -2
  329. package/dist/trace/wire-projection-faces.js +2 -2
  330. package/dist/verifier-pause-card.d.ts +33 -5
  331. package/dist/verifier-pause-card.js +15 -10
  332. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  333. package/dist/web-admin-client/apply-effective.js +109 -9
  334. package/dist/web-admin-client/effective-keys.js +2 -0
  335. package/dist/web-admin-client/types.d.ts +4 -0
  336. package/dist/workspace-space.d.ts +19 -3
  337. package/dist/workspace-space.js +8 -4
  338. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  339. package/package.json +3 -2
  340. package/wire-changes/server@7.142.0.json +639 -0
  341. package/wire-changes/server@7.143.0.json +1167 -0
@@ -15,7 +15,9 @@
15
15
  * - `deny` / `ask` are folded via `tightenTaskSpec` (deny-wins `combinePolicies`); it THROWS on any override that would
16
16
  * LOOSEN a stricter base safety field. Through those two buckets the client can only ADD denies / asks on top of the
17
17
  * deployment + operator baseline.
18
- * - `allow` (server 7.117.0, core 7.42.0) is NOT a policy: the list rides WHOLE and verbatim into `TaskSpec.allowedTools`,
18
+ * - `allow` (server 7.117.0, core 7.42.0) is NOT a policy: the list rides WHOLE, one member per entry in the entry's position, into `TaskSpec.allowedTools`
19
+ * (a string verbatim; a settings layer's `{ rule, layer }` entry as its rule text, or — when its `/` reads at the configuration root — as
20
+ * `{ rule, pathBases: { root } }`, the same anchor its deny / ask twin's set declares: {@link allowMemberOf}),
19
21
  * the request member of the engine's ONE allow layer (beside a deployment's `RunnerDeps.allowedTools`, a person's stored
20
22
  * rows, an active skill's `allowed-tools` and a PreToolUse hook's `allow`), read under one predicate. It IS an accepted
21
23
  * loosening for this run: an entry clears the `unstated` / `ordinary` questions the engine raises for the calls it covers
@@ -52,6 +54,7 @@ import { type EntryProblem } from "./entry-problems.js";
52
54
  import { type RuleListEntry } from "./permission-rule-list.js";
53
55
  import { type KeyClosureIssue } from "./request-key-closure.js";
54
56
  import { type RunnableHooksConfig } from "./hooks/hook-runner.js";
57
+ import { type HttpHookAllowlistLayer } from "@sema-agent/settings-schema";
55
58
  import type { StoredHooksRereadM20 } from "./stored-request-hooks-m20.js";
56
59
  import type { EntryPermissionMode } from "./runtime-governance.js";
57
60
  import { type TightenOnlySeatValues } from "./tighten-only-seats.js";
@@ -197,7 +200,8 @@ export declare function skillListingMaxDescOk(v: unknown): v is number;
197
200
  * deferred (submit 路径另有 400 fail-loud),valid `hooks` 进 applied shape(hook-runner 阶段一)。 */
198
201
  export interface ParsedTaskSettings {
199
202
  permissions?: {
200
- allow?: string[];
203
+ /** 与 `deny` 同一条目形(带层条目的层只选 `/` 的锚);投 `TaskSpec.allowedTools` 的唯一写点 {@link applyTaskSettings}。 */
204
+ allow?: RuleListEntry[];
201
205
  /** 纯串 = 请求自己写的规则;`{ rule, layer }` = 端从某个设置层读来的规则(`permission-rule-list.ts` 的 {@link RuleListEntry};层只选 `/` 的锚)。 */
202
206
  deny?: RuleListEntry[];
203
207
  /** 同 `deny` 的条目形;编译与 `deny` 同一口({@link deriveSettingsPolicy})。 */
@@ -242,6 +246,9 @@ export interface ParsedTaskSettings {
242
246
  * wire 位置由座位表说(三座在 `settings` 顶层,`neverAuto` 在 `settings.permissions`),解析后与位置无关。空数组 ≡ 缺席(不铸键)。
243
247
  * 去处:三座经 {@link applyTaskSettings} 落 core 任务层席;`neverAuto` 经 {@link deriveSettingsPolicy} 落请求叶策略。 */
244
248
  tightenOnly?: TightenOnlySeatValues;
249
+ /** http hook 两张名单(`settings.allowedHttpHookUrls` / `settings.httpHookAllowedEnvVars`)作为**一个设置层**:源名 {@link REQUEST_SETTINGS_SOURCE} + 两键里在场的那几把,值原样(本服务不读内容)。
250
+ * 两键都缺席 ⇒ 不铸键。唯一读者 = `hooks/hook-runner.ts` 的 http 腿(经 hooks 注册上下文的 `httpHookAllowlistLayers`)。 */
251
+ httpHookAllowlist?: HttpHookAllowlistLayer;
245
252
  }
246
253
  /** The result of parsing an untrusted `body.settings`: the applied subset + the names of present-but-deferred
247
254
  * fields (so the caller can log/advertise them — no silent drop). `null` settings ⇒ `{ settings: undefined }`. */
@@ -293,8 +300,8 @@ export interface SettingsPermissionBucketIssue {
293
300
  /**
294
301
  * 三桶的**唯一**判据(纯函数)。`perms` 不是对象 ⇒ 不判(那一形归「settings.permissions must be an object」那道门);桶缺席 / `null`
295
302
  * ⇒ 不判(与 `permissions: null` 的「容器缺席」同一约定)。逐桶按 {@link SETTINGS_PERMISSION_BUCKETS} 的序、按该桶的条目规则:不是数组 ⇒ `bad_entry`
296
- * (无 `index`);`rule_policy` 桶超帽 ⇒ `over_cap`(先判,拒体有界)、首个不合条目律的项(不是非空串、也不是合形的带层条目)⇒ `bad_entry` + 它的下标;`allow_layer` 桶首个非串项 ⇒
297
- * `bad_entry` + 它的下标(整表判据 = `ruleEntryListShapeOk`),超帽同样 `over_cap`(帽的理由不同,见段头)。全合法 ⇒ `null`。
303
+ * (无 `index`);`rule_policy` 桶超帽 ⇒ `over_cap`(先判,拒体有界)、首个不合条目律的项(不是非空串、也不是合形的带层条目)⇒ `bad_entry` + 它的下标;`allow_layer` 桶首个既不是串、
304
+ * 也不是合形带层条目的项 ⇒ `bad_entry` + 它的下标,超帽同样 `over_cap`(帽的理由不同,见段头)。全合法 ⇒ `null`。
298
305
  */
299
306
  export declare function settingsPermissionBucketIssue(perms: unknown): SettingsPermissionBucketIssue | null;
300
307
  /** 本 service 真正**消费**的 `settings` 顶层键 —— 闭集。每一个都有消费点:
@@ -313,8 +320,12 @@ export declare function settingsPermissionBucketIssue(perms: unknown): SettingsP
313
320
  * 🔴 两个 `skillListing*`([ref],7.77.0)= **CC settings 同名键**,值域抄 CC 的 schema 声明
314
321
  * (`skillListingBudgetFraction` 是 `gt(0).lte(1)`、`skillListingMaxDescChars` 是 `int().positive()`);
315
322
  * 消费点 = 本文件的 parse + `boot/resolve-spec.ts` 的 `TaskSpec` 透传。语义(预算怎么花、描述怎么裁、
316
- * 溢出怎么退成 name-only)整个归引擎,本仓一个字节都不渲染,也**不复刻默认值**——缺席即整键不写。 */
317
- export declare const TASK_SETTINGS_KEYS: readonly ["approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
323
+ * 溢出怎么退成 name-only)整个归引擎,本仓一个字节都不渲染,也**不复刻默认值**——缺席即整键不写。
324
+ *
325
+ * 🔴 `allowedHttpHookUrls` / `httpHookAllowedEnvVars` = **CC settings 同名键**(契约包的 http hook 两张名单,键表属主 = 契约包 `HTTP_HOOK_ALLOWLIST_KEYS`):
326
+ * 消费点 = {@link parseTaskSettings} 把它们原样收成一层({@link requestHttpHookAllowlistLayer})→ `boot/resolve-spec.ts` 交给 hooks 注册 → `hooks/hook-runner.ts` 的 http 腿
327
+ * 在拨出之前问契约包判官。受理面只判键名(本门),不判值:名单的读法只有判官那一只(坏形由判官答 `malformed`、拦)。 */
328
+ export declare const TASK_SETTINGS_KEYS: readonly ["allowedHttpHookUrls", "approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "httpHookAllowedEnvVars", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
318
329
  /** 本 service 消费的 `settings.permissions` 子键 —— 闭集(值形校验另有既有门,本门只判键名)。 */
319
330
  export declare const TASK_SETTINGS_PERMISSION_KEYS: readonly ["allow", "ask", "defaultMode", "deny", "disableAutoMode", "neverAuto"];
320
331
  /** CC/SDK 合约里合法、本 service **不接线**的顶层键。词表外的 CC 键落 `unknown` —— 两列都是 400,标签只影响
@@ -451,7 +462,7 @@ export interface FsWriteGateWiring {
451
462
  taskHome?: string;
452
463
  /**
453
464
  * **执行环境的配置根**(唯一属主 `execution-config-root.ts` 的 `executionConfigRootOf`):user 设置层里 `/` 形路径规则的根 —— 带 `layer: "userSettings"`
454
- * 且要 `root` 的条目编进只声明 `{ root: configRoot }` 的那一集(`permission-rule-list.ts` 的 `ruleRootOf`)。缺席(沙箱车道既没转 `SEMA_CONFIG_DIR`、home 也读不出)
465
+ * 且要 `root` 的条目编进只声明 `{ root: configRoot }` 的那一集、allow 桶里的投成带同一声明的那一员(`permission-rule-list.ts` 的 `ruleRootOf`)。缺席(沙箱车道既没转 `SEMA_CONFIG_DIR`、home 也读不出)
455
466
  * ⇒ 这样的条目由 {@link rejectUnmatchableSettingsNames} 响亮拒(422 `home_unresolvable`),不猜目录。拒点与存体预判读同一只函数。
456
467
  */
457
468
  configRoot?: string;
@@ -478,7 +489,7 @@ export type RulePolicyBucket = (typeof RULE_POLICY_BUCKETS)[number];
478
489
  /**
479
490
  * deny / ask 两桶的**完整规则判**,唯一一只(拒点 {@link rejectUnmatchableSettingsNames} 与存体预判 `http/stored-body-replay.ts` 同调):
480
491
  * ② 按 {@link RULE_POLICY_BUCKETS} 的序逐桶、桶内按 `RULE_ROOTS` 的序逐集(`permission-rule-list.ts` 的 `ruleSetsOf`:判的就是要编的那一集)问 `judgeUnmatchableToolNames`(名字层 + core 规则文法);
481
- * ③ 再判「这条规则要的基本部署给不出」那一拒:`~/` 形缺 home;配置根那一集缺配置根(两者都落在执行环境的 home 上,同词 `home_unresolvable`)。答 `{ bucket, message, problems }`:`bucket` 与 `message` 是**第一处**坏处的
492
+ * ③ 再判「这条规则要的基本部署给不出」那一拒:`~/` 形缺 home(deny / ask);配置根那一集缺配置根(三桶,{@link LAYER_ROOT_BUCKETS};两者都落在执行环境的 home 上,同词 `home_unresolvable`)。答 `{ bucket, message, problems }`:`bucket` 与 `message` 是**第一处**坏处的
482
493
  * (句子冻结,先撞上的那一条),`problems` 是**两段、两只桶判下来的全部**坏条目(机读位的原料,形的属主 `entry-problems.ts`;`entry` = 规则原文,带层条目也一样)。
483
494
  * 入参:桶、{@link RulePolicyBases}。**home 同源**:拒点的 home 是 `resolve-spec.ts` 的 `laneHomeBase()` 经 `executionLaneHomeDir(config)` 算出的,预判传同一个函数对同一份部署配置算出的;配置根同理(`executionConfigRootOf`)。
484
495
  * **任务根那一集声明给文法的基**只有拒点有(根 + home 的并集,执行车道才知道根):core 的规则文法对**没声明**的基不下判词(见 `config.ts` `uncompilableRuleGuidance`),所以预判什么也不声明、两腿逐字同句;
@@ -494,8 +505,8 @@ export interface RulePolicyBases {
494
505
  /** 执行环境的配置根(配置根那一集的根;拒点与预判都给);读不出 ⇒ 缺席。 */
495
506
  readonly configRoot?: string;
496
507
  }
497
- export declare function rulePolicyBucketsRefusal(permissions: Readonly<Partial<Record<RulePolicyBucket, readonly RuleListEntry[]>>>, bases: RulePolicyBases): {
498
- readonly bucket: RulePolicyBucket;
508
+ export declare function rulePolicyBucketsRefusal(permissions: Readonly<Partial<Record<SettingsPermissionBucket, readonly RuleListEntry[]>>>, bases: RulePolicyBases): {
509
+ readonly bucket: SettingsPermissionBucket;
499
510
  readonly message: string;
500
511
  readonly problems: readonly EntryProblem[];
501
512
  } | undefined;
@@ -2,13 +2,13 @@ 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, RULE_ROOTS, createRuleListPolicy, ruleListEntryOk, ruleSetsOf, ruleSourceOf, ruleTextOf } from "./permission-rule-list.js";
6
- import { allowLayerEntryCountIssue, describeAllowLayerOverCap, ruleEntryListShapeOk } from "./rule-entry-list.js";
5
+ import { MAX_PERMISSION_RULES_PER_LIST, RULE_ROOTS, allowListEntryOk, createRuleListPolicy, ruleListEntryOk, ruleRootOf, ruleSetsOf, ruleSourceOf, ruleTextOf } from "./permission-rule-list.js";
6
+ import { allowLayerEntryCountIssue, describeAllowLayerOverCap } 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
+ import { HTTP_HOOK_ALLOWLIST_KEYS, SETTINGS_SOURCES } from "@sema-agent/settings-schema";
12
12
  import { heldRuleName } from "./tool-rule-names.js";
13
13
  import { policyAskStandingAnswerFacts, standingAnswerDurablyRecordable } from "./tool-approval.js";
14
14
  import { createRequestNeverAutoPolicy } from "./approval.js";
@@ -113,8 +113,8 @@ function bucketIssueMessage(f, rule) {
113
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`;
114
114
  case "allow_layer":
115
115
  return f.index === undefined
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`
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`;
116
+ ? `${f.field} must be an array of rule entries (a rule string, or { "rule": <rule>, "layer": <settings source> }) — refused rather than ignored, because an ignored list reads as "in effect" to the caller`
117
+ : `${f.field}[${f.index}] is neither a 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`;
118
118
  }
119
119
  }
120
120
  }
@@ -146,8 +146,11 @@ export function settingsPermissionBucketIssue(perms) {
146
146
  const over = allowLayerEntryCountIssue(field, v);
147
147
  if (over !== undefined)
148
148
  fields = { field, reason: "over_cap", count: over.facts.count, cap: over.facts.cap };
149
- else if (!ruleEntryListShapeOk(v))
150
- fields = { field, reason: "bad_entry", index: v.findIndex((x) => typeof x !== "string") };
149
+ else {
150
+ const index = v.findIndex((x) => !allowListEntryOk(x));
151
+ if (index >= 0)
152
+ fields = { field, reason: "bad_entry", index };
153
+ }
151
154
  break;
152
155
  }
153
156
  }
@@ -160,7 +163,7 @@ export function settingsPermissionBucketIssue(perms) {
160
163
  function acceptedRuleList(v) {
161
164
  return Array.isArray(v) && v.length > 0 ? v.slice() : undefined;
162
165
  }
163
- export const TASK_SETTINGS_KEYS = ["approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
166
+ export const TASK_SETTINGS_KEYS = ["allowedHttpHookUrls", "approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "httpHookAllowedEnvVars", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
164
167
  export const TASK_SETTINGS_PERMISSION_KEYS = ["allow", "ask", "defaultMode", "deny", "disableAutoMode", "neverAuto"];
165
168
  export const TASK_SETTINGS_UNSUPPORTED_KEYS = ["apiKeyHelper", "crossSessionInbound", "dialogExpiry", "sandbox", "statusLine"];
166
169
  export const TASK_SETTINGS_UNSUPPORTED_PERMISSION_KEYS = ["additionalDirectories", "disableBypassPermissionsMode"];
@@ -228,6 +231,16 @@ export function hooksKeyIssue(raw, config) {
228
231
  export function taskHooksHonored(config) {
229
232
  return !DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(config);
230
233
  }
234
+ const REQUEST_SETTINGS_SOURCE = "flagSettings";
235
+ function requestHttpHookAllowlistLayer(s) {
236
+ const present = HTTP_HOOK_ALLOWLIST_KEYS.filter((key) => Object.hasOwn(s, key));
237
+ if (present.length === 0)
238
+ return undefined;
239
+ const layer = { source: REQUEST_SETTINGS_SOURCE };
240
+ for (const key of present)
241
+ layer[key] = s[key];
242
+ return layer;
243
+ }
231
244
  function tightenOnlySeatDeferredName(name) {
232
245
  return tightenOnlySeatField(name).replace(/^settings\./, "");
233
246
  }
@@ -303,6 +316,9 @@ export function parseTaskSettings(raw, seats = {}) {
303
316
  deferred.push(tightenOnlySeatDeferredName(name));
304
317
  if (Object.keys(tightenSeats.values).length > 0)
305
318
  out.tightenOnly = tightenSeats.values;
319
+ const httpHookAllowlist = requestHttpHookAllowlistLayer(s);
320
+ if (httpHookAllowlist !== undefined)
321
+ out.httpHookAllowlist = httpHookAllowlist;
306
322
  return { settings: Object.keys(out).length > 0 ? out : undefined, deferred, ...(hooksReread !== undefined ? { hooksReread } : {}) };
307
323
  }
308
324
  export function acceptAppendSystemPrompt(v, warn) {
@@ -359,6 +375,7 @@ export class SettingsRuleRefusalError extends Error {
359
375
  }
360
376
  export const SETTINGS_RULE_REFUSAL_CODE = "request.unprocessable";
361
377
  export const RULE_POLICY_BUCKETS = ["ask", "deny"];
378
+ const LAYER_ROOT_BUCKETS = [...RULE_POLICY_BUCKETS, "allow"];
362
379
  export function rulePolicyBucketsRefusal(permissions, bases) {
363
380
  let first;
364
381
  const problems = [];
@@ -410,7 +427,7 @@ export function rulePolicyBucketsRefusal(permissions, bases) {
410
427
  }
411
428
  }
412
429
  if (bases.configRoot === undefined) {
413
- for (const bucket of RULE_POLICY_BUCKETS) {
430
+ for (const bucket of LAYER_ROOT_BUCKETS) {
414
431
  const field = `permissions.${bucket}`;
415
432
  for (const name of ruleSetsOf(permissions[bucket] ?? [], bucket).configuration.map(ruleTextOf)) {
416
433
  first ??= {
@@ -430,9 +447,25 @@ function ruleSetBases(root, taskBases, configRoot) {
430
447
  case "task":
431
448
  return taskBases;
432
449
  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 };
450
+ return configurationSetBases(configRoot);
451
+ default: {
452
+ const unhandled = root;
453
+ return unhandled;
454
+ }
455
+ }
456
+ }
457
+ function configurationSetBases(configRoot) {
458
+ if (configRoot === undefined)
459
+ 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");
460
+ return { root: configRoot };
461
+ }
462
+ function allowMemberOf(entry, configRoot) {
463
+ const root = ruleRootOf(entry, "allow");
464
+ switch (root) {
465
+ case "task":
466
+ return ruleTextOf(entry);
467
+ case "configuration":
468
+ return { rule: ruleTextOf(entry), pathBases: configurationSetBases(configRoot) };
436
469
  default: {
437
470
  const unhandled = root;
438
471
  return unhandled;
@@ -490,7 +523,7 @@ export function applyTaskSettings(base, settings, bases) {
490
523
  next = tightenGoverned(next, { ...(toolPolicy !== undefined ? { toolPolicy } : {}), ...seatSpec });
491
524
  const allow = settings.permissions?.allow;
492
525
  if (allow !== undefined)
493
- next = { ...next, allowedTools: allow };
526
+ next = { ...next, allowedTools: allow.map((entry) => allowMemberOf(entry, bases?.configRoot)) };
494
527
  if (settings.outputStyle) {
495
528
  const appended = next.appendSystemPrompt ? `${next.appendSystemPrompt}\n\n${settings.outputStyle}` : settings.outputStyle;
496
529
  next = { ...next, appendSystemPrompt: appended };
@@ -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,18 +58,30 @@ export declare const STORE_ERROR_CODE = "internal.store_error";
57
58
  *
58
59
  * ⚠️ **辖域如实**:本臂触发条件是「码出了本仓的词法」,所以任何依赖铸出的域外码都落这里,不只 SQL 驱动。
59
60
  * 今天可观察的产出方**只有**耐久店那一层(引擎自己的传输/brain 错误在 core 侧已被分类成小写码),故码名
60
- * 取 `internal.store_error`;原码逐字进 message 尾注(随结果过汇点脱敏)、是码 token 时进运维 warn 行({@link isCodeToken}),归因从不丢失。
61
+ * 取 `internal.store_error`;原码逐字进 message 尾注(随结果过汇点脱敏)、是码时进运维线索({@link isCodeToken}),归因从不丢失。
61
62
  */
62
63
  export declare function outwardFailureCode(code: string): string;
63
64
  /**
64
- * 一个错误的 `code` 只在它**是**码(短 token)时才说出口 —— 码形座里装着文字的,不是码(驱动码 `ER_*` / SQLSTATE / `ECONNRESET`
65
- * 全在这一形里;带空白、`=`、换行或超过 64 字符的就是一句话)。读者两只,同一条判据:折码的运维 warn 行(本文件 {@link sealResultForExit})
66
- * 与执行车道占位读的故障词(`boot/execution-env.ts`)。
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`)。
67
71
  */
68
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
+ };
69
82
  /**
70
83
  * 存量结果被**再观看**的读座(闭集):`GET /v1/runs/:id → result`(终态行与停驻行同一座)与账本 `done` 行的回放(三条耐久读腿共用)。
71
- * 封口在读座上折码时,运维行按 (读座, 原码) 只报一次 —— 见 {@link sealResultForExit}。
84
+ * 读入口 {@link sealStoredResult} 必须点名其一(类型级必填);`exit` 不在这里 —— 它只由写入口 {@link sealResultForExit} 给。
72
85
  */
73
86
  export type StoredResultReadSeat = "run.result" | "ev.done.result";
74
87
  /**
@@ -111,7 +124,7 @@ export declare function mapPauseSeats<T>(result: T, f: (seat: SeatObject, keys:
111
124
  /**
112
125
  * **一条结果离开引擎、进入本仓(可重放账本 / run 行 / wire)的唯一封口** —— 两件事,一个出口:
113
126
  * ①摘掉**耐久恢复能力凭据**(`CheckpointToken`)—— 座表 {@link PAUSE_SEATS} 上的每一座;②把**域外的失败码**折成本仓的
114
- * `internal.store_error`({@link outwardFailureCode},[ref])。
127
+ * `internal.store_error`({@link outwardFailureCode},[ref])—— 码座两只:因由的 `terminal.code` 与 cascade 每一级的 `attempts[].errorCode`(同一类码,见 {@link foldOutwardFailureCodes})。
115
128
  *
116
129
  * 🔴 为什么两件事合在一只函数里而不是并列两只(7.92.2 改名 `stripResumeToken` → `sealResultForExit`):
117
130
  * 它们是**同一个出口上的同一件事** —— 「这条结果可以见人了吗」。并列两只 = 六个结算点各自要记得调两次,
@@ -129,23 +142,30 @@ export declare function mapPauseSeats<T>(result: T, f: (seat: SeatObject, keys:
129
142
  * `/v1/approvals` 决策端点,凭 `checkpointId` 对账,从来不是拿 token 直连引擎。
130
143
  *
131
144
  * **出口全集都经这一口**:三家 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 上永远是对外词),去重的只是日志行。
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 让日志不随读次放大;去重归记录器一只,不另造表。
143
156
  *
144
157
  * 对任何输入安全(非普通对象 / 无可摘的座 / 码在词法内 ⇒ **同一引用**原样返回,不重塑)。
145
158
  */
146
159
  export declare function sealResultForExit<T extends {
147
160
  readonly terminal?: unknown;
148
- }>(result: T, readSeat?: StoredResultReadSeat): T;
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;
149
169
  /**
150
170
  * **投影出口的唯一属主**([ref])—— core 的 `terminalProjection` + 对外码归一,一次。
151
171
  *
package/dist/terminal.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { terminalProjection } from "@sema-agent/core";
2
+ import { errorCodeOf } from "./observability/err-text.js";
2
3
  import { recordFailOpen } from "./observability/fail-open.js";
4
+ import { redactSecrets } from "./trace/redact.js";
3
5
  export function failureCodeOf(terminal) {
4
6
  return terminal.kind === "failed" ? terminal.code : undefined;
5
7
  }
@@ -9,9 +11,13 @@ export function outwardFailureCode(code) {
9
11
  return OUTWARD_CODE_SHAPE.test(code) ? code : STORE_ERROR_CODE;
10
12
  }
11
13
  export function isCodeToken(code) {
12
- return typeof code === "string" && CODE_TOKEN_SHAPE.test(code);
14
+ return typeof code === "string" && CODE_TOKEN_SHAPE.test(code) && redactSecrets(code, { observe: false }) === code;
13
15
  }
14
16
  const CODE_TOKEN_SHAPE = /^[A-Za-z0-9_.:-]{1,64}$/;
17
+ export function codeFieldOf(err) {
18
+ const code = errorCodeOf(err);
19
+ return isCodeToken(code) ? { code } : {};
20
+ }
15
21
  export const PAUSE_SEATS = [
16
22
  { path: ["terminal"], token: "token", gate: "gate" },
17
23
  { path: ["terminal", "nestedPause"], token: "token", gate: "gate" },
@@ -41,36 +47,73 @@ function dropCapability(seat, keys) {
41
47
  const { [keys.token]: _drop, ...rest } = seat;
42
48
  return rest;
43
49
  }
44
- export function sealResultForExit(result, readSeat) {
50
+ export function sealResultForExit(result) {
51
+ return seal(result, "exit");
52
+ }
53
+ export function sealStoredResult(result, seat) {
54
+ return seal(result, seat);
55
+ }
56
+ function seal(result, at) {
45
57
  if (result === null || typeof result !== "object")
46
58
  return result;
47
- return foldOutwardFailureCode(mapPauseSeats(result, dropCapability), readSeat);
59
+ return foldOutwardFailureCodes(mapPauseSeats(result, dropCapability), at);
48
60
  }
49
- function foldOutwardFailureCode(result, readSeat) {
50
- const t = failedCauseWithCode(result.terminal);
51
- if (t === undefined)
61
+ function foldOutwardFailureCodes(result, at) {
62
+ const terminal = foldCauseCode(result.terminal, at);
63
+ const priorAttempts = result.attempts;
64
+ const attempts = foldAttemptCodes(priorAttempts, at);
65
+ const terminalKept = Object.is(terminal, result.terminal);
66
+ const attemptsKept = Object.is(attempts, priorAttempts);
67
+ if (terminalKept && attemptsKept)
52
68
  return result;
69
+ return { ...result, ...(terminalKept ? {} : { terminal }), ...(attemptsKept ? {} : { attempts }) };
70
+ }
71
+ function foldCauseCode(terminal, at) {
72
+ const t = failedCauseWithCode(terminal);
73
+ if (t === undefined)
74
+ return terminal;
53
75
  const outward = outwardFailureCode(t.code);
54
76
  if (outward === t.code)
55
- return result;
56
- reportFold(t.code, outward, t.message, readSeat);
77
+ return terminal;
78
+ reportFold(t.code, outward, t.message, at);
57
79
  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))
80
+ return { ...t, code: outward, message: `${message} [original code: ${t.code}]` };
81
+ }
82
+ function foldAttemptCodes(attempts, at) {
83
+ if (!Array.isArray(attempts))
84
+ return attempts;
85
+ let folded;
86
+ attempts.forEach((attempt, i) => {
87
+ if (attempt === null || typeof attempt !== "object")
67
88
  return;
68
- if (readFoldLatch.size >= READ_FOLD_LATCH_CAP)
69
- readFoldLatch.delete(readFoldLatch.values().next().value);
70
- readFoldLatch.add(key);
71
- }
89
+ const code = attempt.errorCode;
90
+ if (typeof code !== "string")
91
+ return;
92
+ const outward = outwardFailureCode(code);
93
+ if (outward === code)
94
+ return;
95
+ reportFold(code, outward, undefined, at);
96
+ (folded ??= [...attempts])[i] = { ...attempt, errorCode: outward };
97
+ });
98
+ return folded ?? attempts;
99
+ }
100
+ const STORED_RESULT_FOLD_TAG = "server.terminal.stored-result-code-folded";
101
+ function reportFold(code, outward, message, at) {
72
102
  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"}`);
103
+ const fields = `original=${isCodeToken(code) ? code : "<not-a-code>"} outward=${outward} messageBytes=${messageBytes} at=${at}`;
104
+ switch (at) {
105
+ case "exit":
106
+ console.warn(`[terminal] outward_failure_code_folded ${fields}`);
107
+ return;
108
+ case "run.result":
109
+ case "ev.done.result":
110
+ recordFailOpen(STORED_RESULT_FOLD_TAG, fields);
111
+ return;
112
+ default: {
113
+ const unhandled = at;
114
+ throw new Error(`unhandled fold sighting: ${String(unhandled)}`);
115
+ }
116
+ }
74
117
  }
75
118
  function failedCauseWithCode(t) {
76
119
  if (t === null || typeof t !== "object")