@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
@@ -21,7 +21,7 @@ function withDeadline(op, label, timeoutMs = RECONCILE_STORE_TIMEOUT_MS) {
21
21
  });
22
22
  }
23
23
  export function isAncestorFoldMint(ask) {
24
- return ask.parentToolCallId === null && ask.sourceTaskId !== ask.sessionId;
24
+ return ask.parentToolCallId === null && ask.sourceSessionId !== ask.sessionId;
25
25
  }
26
26
  export function classifyGateMatch(ask, candidates) {
27
27
  const outcome = classifyGateMatchBeforeFoldVeto(ask, candidates);
@@ -30,7 +30,7 @@ export function classifyGateMatch(ask, candidates) {
30
30
  return outcome;
31
31
  }
32
32
  function classifyGateMatchBeforeFoldVeto(ask, candidates) {
33
- const sameIdentity = candidates.filter((c) => c.unparseable !== true && c.sourceTaskId !== null && c.sourceTaskId === ask.sourceTaskId && c.boundCallId === ask.toolCallId && c.createdAtMs >= ask.createdAtMs);
33
+ const sameIdentity = candidates.filter((c) => c.unparseable !== true && c.sourceSessionId !== null && c.sourceSessionId === ask.sourceSessionId && c.boundCallId === ask.toolCallId && c.createdAtMs >= ask.createdAtMs);
34
34
  const askHash = ask.boundInputHash;
35
35
  if (sameIdentity.length === 0) {
36
36
  if (candidates.length > 0)
@@ -79,6 +79,45 @@ export function decideReconcileAction(input) {
79
79
  void candidates;
80
80
  return { kind: "hold" };
81
81
  }
82
+ export async function bindParkingAskAtGate(deps, ask, nowMs, deadlineMs) {
83
+ const { askStore, checkpoints, warn, revoke } = deps;
84
+ const expired = () => deadlineMs !== undefined && Date.now() >= deadlineMs;
85
+ const batchRow = await withDeadline(askStore.getBatch(ask.batchId), "getBatch");
86
+ let batchState = batchRow?.state ?? "MISSING";
87
+ let batchBoundAskId = batchRow?.boundAskId ?? null;
88
+ const irreversibleBatch = batchState === "ABORTED" || (batchState === "ROUTING_BOUND" && batchBoundAskId !== null && batchBoundAskId !== ask.askId);
89
+ let gateMatch;
90
+ if (checkpoints && !irreversibleBatch && !expired()) {
91
+ const candidates = await withDeadline(checkpoints.findCheckpointCandidatesForAsk(encodeCheckpointScope(ask.owner), ask.sessionId, ask.toolCallId, ask.createdAtMs), "findCheckpointCandidatesForAsk");
92
+ gateMatch = classifyGateMatch(ask, candidates);
93
+ if (gateMatch.kind === "hash_mismatch") {
94
+ warn("approval_reconcile_hash_mismatch", {
95
+ askId: ask.askId,
96
+ taskId: ask.taskId,
97
+ toolCallId: ask.toolCallId,
98
+ candidates: gateMatch.candidates,
99
+ reason: "ask and checkpoint minted the same bound input under the same (sourceSessionId, toolCallId) identity but the digests differ — " +
100
+ "the known causes are deployment-side (a hook/policy mutating an object it still holds a reference to between the two mints, " +
101
+ "or a stateful getter returning a fresh value per read), not out-of-band interference; this ask keeps its fail-safe route (no park, no denial)",
102
+ });
103
+ }
104
+ const match = gateMatch.kind === "match" ? gateMatch.candidate : undefined;
105
+ if (match && !expired()) {
106
+ const res = await withDeadline(askStore.bindBatch(ask.batchId, ask.askId, {
107
+ gateToken: match.token,
108
+ gateBoundCallId: match.boundCallId,
109
+ gateBoundInputHash: match.boundInputHash,
110
+ }), "bindBatch");
111
+ if (res.ok) {
112
+ revoke(ask, res.voidedSiblings, "superseded_by_park", nowMs);
113
+ return { bound: true, match: gateMatch };
114
+ }
115
+ batchState = res.batchState;
116
+ batchBoundAskId = res.boundAskId ?? null;
117
+ }
118
+ }
119
+ return { bound: false, ...(gateMatch !== undefined ? { match: gateMatch } : {}), batchState, batchBoundAskId, irreversibleBatch };
120
+ }
82
121
  export function createApprovalReconciler(deps) {
83
122
  const { askStore, checkpoints, runs, logger, metrics, batchLimit, pendingGraceMs, adhocGraceMs, orphanTtlMs, unattendedPolicy, emitRevoke } = deps;
84
123
  const warn = (event, fields) => {
@@ -98,44 +137,8 @@ export function createApprovalReconciler(deps) {
98
137
  warn("approval_revoke_emit_failed", { batchId: row.batchId, count: askIds.length, err: err instanceof Error ? err.message : String(err) });
99
138
  }
100
139
  };
101
- const bindAtGate = async (ask, nowMs, deadlineMs) => {
102
- const expired = () => deadlineMs !== undefined && Date.now() >= deadlineMs;
103
- const batchRow = await withDeadline(askStore.getBatch(ask.batchId), "getBatch");
104
- let batchState = batchRow?.state ?? "MISSING";
105
- let batchBoundAskId = batchRow?.boundAskId ?? null;
106
- const irreversibleBatch = batchState === "ABORTED" || (batchState === "ROUTING_BOUND" && batchBoundAskId !== null && batchBoundAskId !== ask.askId);
107
- let gateMatch;
108
- if (checkpoints && !irreversibleBatch && !expired()) {
109
- const candidates = await withDeadline(checkpoints.findCheckpointCandidatesForAsk(encodeCheckpointScope(ask.owner), ask.sessionId, ask.toolCallId, ask.createdAtMs), "findCheckpointCandidatesForAsk");
110
- gateMatch = classifyGateMatch(ask, candidates);
111
- if (gateMatch.kind === "hash_mismatch") {
112
- warn("approval_reconcile_hash_mismatch", {
113
- askId: ask.askId,
114
- taskId: ask.taskId,
115
- toolCallId: ask.toolCallId,
116
- candidates: gateMatch.candidates,
117
- reason: "ask and checkpoint minted the same bound input under the same (sourceTaskId, toolCallId) identity but the digests differ — " +
118
- "the known causes are deployment-side (a hook/policy mutating an object it still holds a reference to between the two mints, " +
119
- "or a stateful getter returning a fresh value per read), not out-of-band interference; this ask keeps its fail-safe route (no park, no denial)",
120
- });
121
- }
122
- const match = gateMatch.kind === "match" ? gateMatch.candidate : undefined;
123
- if (match && !expired()) {
124
- const res = await withDeadline(askStore.bindBatch(ask.batchId, ask.askId, {
125
- gateToken: match.token,
126
- gateBoundCallId: match.boundCallId,
127
- gateBoundInputHash: match.boundInputHash,
128
- }), "bindBatch");
129
- if (res.ok) {
130
- revoke(ask, res.voidedSiblings, "superseded_by_park", nowMs);
131
- return { bound: true, match: gateMatch };
132
- }
133
- batchState = res.batchState;
134
- batchBoundAskId = res.boundAskId ?? null;
135
- }
136
- }
137
- return { bound: false, ...(gateMatch !== undefined ? { match: gateMatch } : {}), batchState, batchBoundAskId, irreversibleBatch };
138
- };
140
+ const gate = { askStore, checkpoints, warn, revoke };
141
+ const bindAtGate = (ask, nowMs, deadlineMs) => bindParkingAskAtGate(gate, ask, nowMs, deadlineMs);
139
142
  const reconcileOne = async (ask, nowMs) => {
140
143
  const attempt = await bindAtGate(ask, nowMs);
141
144
  if (attempt.bound)
@@ -150,11 +153,11 @@ export function createApprovalReconciler(deps) {
150
153
  return { outcome: "held", match: gateMatch };
151
154
  }
152
155
  case "deny": {
153
- const won = await withDeadline(askStore.transitionAsk(ask.askId, "PARKING", "DENIED", { deniedReason: action.reason, updatedAtMs: nowMs }), "transitionAsk(DENIED)");
156
+ const won = await withDeadline(askStore.transitionAsk(ask.askId, "PARKING", "DENIED", { endingCause: action.reason, updatedAtMs: nowMs }), "transitionAsk(DENIED)");
154
157
  return { outcome: won ? "denied" : "held", match: gateMatch };
155
158
  }
156
159
  case "void": {
157
- const won = await withDeadline(askStore.transitionAsk(ask.askId, "PARKING", "VOID", { deniedReason: action.reason, updatedAtMs: nowMs }), "transitionAsk(VOID)");
160
+ const won = await withDeadline(askStore.transitionAsk(ask.askId, "PARKING", "VOID", { endingCause: action.reason, updatedAtMs: nowMs }), "transitionAsk(VOID)");
158
161
  if (!won)
159
162
  return { outcome: "held", match: gateMatch };
160
163
  if (action.reason === APPROVAL_ENDING_CAUSES.ORPHAN_TTL_EXCEEDED) {
package/dist/audit.js CHANGED
@@ -129,8 +129,9 @@ const MESSAGE_READ_TREATMENT = {
129
129
  runOrigin: "verbatim",
130
130
  taskId: "verbatim",
131
131
  attachment: "verbatim",
132
+ rewrittenBy: "verbatim",
132
133
  },
133
- custom: { role: "verbatim", customType: "verbatim", content: "verbatim", display: "verbatim", details: "verbatim", provenance: "verbatim", timestamp: "verbatim" },
134
+ custom: { role: "verbatim", customType: "verbatim", content: "verbatim", display: "verbatim", details: "verbatim", provenance: "verbatim", timestamp: "verbatim", rewrittenBy: "verbatim" },
134
135
  assistant: {
135
136
  role: "verbatim", content: "parts", errorMessage: "text", diagnostics: "structure", repetition: "structure",
136
137
  api: "verbatim", provider: "verbatim", model: "verbatim", responseModel: "verbatim", responseId: "verbatim", requestId: "verbatim",
@@ -139,8 +140,9 @@ const MESSAGE_READ_TREATMENT = {
139
140
  staticReasoningCut: "verbatim", timestamp: "verbatim",
140
141
  providerCondition: "verbatim", providerConditionSource: "verbatim", providerResetAtMs: "verbatim", apiErrorRoute: "verbatim",
141
142
  errorBodyIncomplete: "verbatim", modelOutputInvalid: "verbatim", finishReasonUnknown: "verbatim", streamEndIgnored: "verbatim",
143
+ refusedTool: "verbatim", rewrittenBy: "verbatim",
142
144
  },
143
- toolResult: { role: "verbatim", toolCallId: "verbatim", toolName: "verbatim", content: "parts", details: "toolCard", isError: "verbatim", timestamp: "verbatim" },
145
+ toolResult: { role: "verbatim", toolCallId: "verbatim", toolName: "verbatim", content: "parts", details: "toolCard", isError: "verbatim", timestamp: "verbatim", rewrittenBy: "verbatim" },
144
146
  bashExecution: {
145
147
  role: "verbatim", command: "text", output: "text", exitCode: "verbatim", cancelled: "verbatim", truncated: "verbatim",
146
148
  fullOutputPath: "verbatim", timestamp: "verbatim", excludeFromContext: "verbatim",
@@ -1,4 +1,4 @@
1
- import { type AuthBridgeErrorCode, type VerifiedIdentity } from "@sema-agent/settings-schema/api/auth-bridge";
1
+ import { VerifiedIdentity, type AuthBridgeErrorCode } from "@sema-agent/settings-schema/api/auth-bridge";
2
2
  import { type TokenScopeRefusal } from "@sema-agent/settings-schema/api/scopes";
3
3
  import type { Logger } from "./observability/logger.js";
4
4
  import type { Metrics } from "./observability/metrics.js";
@@ -52,12 +52,13 @@ type BridgeSinks = {
52
52
  readonly logger?: Logger | undefined;
53
53
  readonly metrics?: Pick<Metrics, "inc"> | undefined;
54
54
  };
55
- /** 注入边界收的结局:与 {@link VerifyOutcome} 同形,只是成功身份的 `scope` 是 `unknown` —— 这一位正是边界要判的形,它不信型面
56
- * (席是嵌入方可换的:`""` 过得了 `scope: string`,JS 侧连缺省 / 非串都交得进来)。`VerifyOutcome` 可赋给它,调用方零改动。 */
55
+ /** 注入边界收的结局:与 {@link VerifyOutcome} 同形,只是成功身份的 `scope` 与 `principalInc` 是 `unknown` —— 这两位正是边界要判的形,它不信型面
56
+ * (席是嵌入方可换的:`""` 过得了 `scope: string` / `principalInc?: string`,JS 侧连缺省 / 非串都交得进来)。`VerifyOutcome` 可赋给它,调用方零改动。 */
57
57
  type ReportedOutcome = {
58
58
  ok: true;
59
- identity: Omit<BridgeIdentity, "scope"> & {
59
+ identity: Omit<BridgeIdentity, "scope" | "principalInc"> & {
60
60
  scope?: unknown;
61
+ principalInc?: unknown;
61
62
  };
62
63
  } | {
63
64
  ok: false;
@@ -65,8 +66,10 @@ type ReportedOutcome = {
65
66
  };
66
67
  /**
67
68
  * 注入边界:任何验证器(内建 {@link createWebAdminJwtVerifier},或嵌入方注入的 `ServiceDeps.webAdminJwtVerifier`)交来的结局,在这里定「装不装身份」。
68
- * 成功身份的 scope 经**同一只** `readTokenScope` 再读一次(入参型见 {@link ReportedOutcome})。
69
- * 读不出 ⇒ 同一只记账口、不交身份(调用方按验证失败处置:不设身份 ⇒ 门答 generic 401)。验证失败的结局验证器自己已记过账,这里只答「不装」。
69
+ * 成功身份的 scope 经**同一只** `readTokenScope` 再读一次;化身号经契约的**同一只**判官 `VerifiedIdentity.shape.principalInc`(= `PrincipalIncarnation.optional()`,
70
+ * 契约验证器读 `sub_inc` 用的就是它)再判一次(入参型见 {@link ReportedOutcome})。
71
+ * 读不出 scope ⇒ 记 `readTokenScope` 的拒词;化身号在场而不合形 ⇒ 记 `malformed`(与契约验证器同一条律:声明在场坏形 ⇒ 令牌 malformed)。两种都经同一只记账口、不交身份
72
+ * (调用方按验证失败处置:不设身份 ⇒ 门答 generic 401)。验证失败的结局验证器自己已记过账,这里只答「不装」。
70
73
  */
71
74
  export declare function admitBridgeIdentity(outcome: ReportedOutcome, sinks: BridgeSinks): BridgeIdentity | undefined;
72
75
  /** The live object `createWebAdminJwtVerifier` produces — a single `verify` entry point over the process-level JWKS cache. */
@@ -1,5 +1,5 @@
1
1
  import { createPublicKey, verify as cryptoVerify } from "node:crypto";
2
- import { AUTH_BRIDGE_ALGS, DEFAULT_CLOCK_SKEW_SEC, DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC, DEFAULT_OFFLINE_GRACE_SEC, WEB_ADMIN_JWKS_PATH, WebAdminJwks, classifyTimeClaims, jwksWithinOfflineGrace, shouldForceJwksRefresh, toVerifiedIdentity, } from "@sema-agent/settings-schema/api/auth-bridge";
2
+ import { AUTH_BRIDGE_ALGS, DEFAULT_CLOCK_SKEW_SEC, DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC, DEFAULT_OFFLINE_GRACE_SEC, WEB_ADMIN_JWKS_PATH, WebAdminJwks, classifyTimeClaims, jwksWithinOfflineGrace, shouldForceJwksRefresh, toVerifiedIdentity, VerifiedIdentity, } from "@sema-agent/settings-schema/api/auth-bridge";
3
3
  import { readTokenScope } from "@sema-agent/settings-schema/api/scopes";
4
4
  import { buildDisplaySafeUrl } from "./trace/redact.js";
5
5
  import { centerJwksFetch } from "./web-admin-client/center-request.js";
@@ -17,12 +17,18 @@ function recordBridgeRefusal(sinks, error, at = {}) {
17
17
  export function admitBridgeIdentity(outcome, sinks) {
18
18
  if (!outcome.ok)
19
19
  return undefined;
20
- const scoped = readTokenScope({ scope: outcome.identity.scope });
20
+ const { scope, principalInc, ...rest } = outcome.identity;
21
+ const scoped = readTokenScope({ scope });
21
22
  if ("refused" in scoped) {
22
23
  recordBridgeRefusal(sinks, scoped.refused);
23
24
  return undefined;
24
25
  }
25
- return { ...outcome.identity, scope: scoped.scope };
26
+ const inc = VerifiedIdentity.shape.principalInc.safeParse(principalInc);
27
+ if (!inc.success) {
28
+ recordBridgeRefusal(sinks, "malformed");
29
+ return undefined;
30
+ }
31
+ return { ...rest, scope: scoped.scope, ...(inc.data !== undefined ? { principalInc: inc.data } : {}) };
26
32
  }
27
33
  export const DEFAULT_JWKS_TTL_SEC = 15 * 60;
28
34
  export const JWKS_FETCH_TIMEOUT_MS = 10_000;
@@ -1,4 +1,5 @@
1
1
  import { createAdoptionRunner } from "../adoption/runner.js";
2
+ import { seatText } from "../trace/exit-seat.js";
2
3
  export async function runAdoptionBootScan(ctx) {
3
4
  const store = ctx.backend?.adoptionLog?.();
4
5
  if (!store)
@@ -29,7 +30,7 @@ export async function runAdoptionBootScan(ctx) {
29
30
  return reading;
30
31
  }
31
32
  catch (err) {
32
- const error = err instanceof Error ? err.message : String(err);
33
+ const error = seatText("operator-log-text", err instanceof Error ? err.message : String(err));
33
34
  ctx.logger.error?.("adoption_boot_scan_failed", { error });
34
35
  return { scanned: 0, resumed: [], stalled: [], rejected: [], failed: [], error };
35
36
  }
@@ -11,6 +11,22 @@ export interface BudgetTracingCtx {
11
11
  backend: StoreBackend | undefined;
12
12
  breakerState: ReturnType<BreakerStateStore["startRefresh"]> | undefined;
13
13
  }
14
+ /**
15
+ * S9 (SILENT-FALLBACK P0-e): write-behind flush/refresh failures previously vanished in a bare catch —
16
+ * the fleet silently degrades to per-replica counting (soft limits stop being fleet-wide). Every failure
17
+ * ticks the counter + streak gauge; warn once per episode (streak hits 3), info on the recovery edge.
18
+ * `info.error` is the client exception's text (the counter turns the rejection into it) ⇒ the warn's field passes the exit seat.
19
+ */
20
+ export declare function createCounterDegradeHook({ logger, metrics }: {
21
+ logger: Pick<Logger, "warn" | "info">;
22
+ metrics: Pick<Metrics, "setGauge" | "inc">;
23
+ }): (info: {
24
+ table: string;
25
+ kind: string;
26
+ streak: number;
27
+ prevStreak: number;
28
+ error?: string;
29
+ }) => void;
14
30
  export declare function createBudgetAndTracing(ctx: BudgetTracingCtx): {
15
31
  brain: import("@sema-agent/core").Brain;
16
32
  degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
@@ -29,7 +45,7 @@ export declare function createBudgetAndTracing(ctx: BudgetTracingCtx): {
29
45
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
30
46
  tracer: import("@sema-agent/core").TracerHook;
31
47
  sideQueryAccounting: import("../budget.js").SideQueryAccounting;
32
- toolResultStore: import("../plugins/store-backend.js").ToolResultStoreFull | undefined;
48
+ toolResultStore: import("@sema-agent/core").ToolResultStore | undefined;
33
49
  sessionPolicyStore: import("@sema-agent/core").SessionPolicyStore | undefined;
34
50
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
35
51
  fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
@@ -6,19 +6,23 @@ import { centerLaneConfigured } from "../web-admin-client/center-request.js";
6
6
  import { CostQuota } from "../observability/cost-quota.js";
7
7
  import { buildQuotaBounds } from "./limit-sync.js";
8
8
  import { assembleFileHistory } from "../file-history-capture.js";
9
- export function createBudgetAndTracing(ctx) {
10
- const { config, logger, metrics, backend, breakerState } = ctx;
11
- const { brain, degradeRoute: degradeRouteWatch } = createBrainAssembly(config, { breakerState });
12
- const pricing = buildPricing(config.models);
13
- const counterDegradeHook = (info) => {
9
+ import { seatText } from "../trace/exit-seat.js";
10
+ export function createCounterDegradeHook({ logger, metrics }) {
11
+ return (info) => {
14
12
  metrics.setGauge("counter_flush_fail_streak", info.streak, { table: info.table, kind: info.kind });
15
13
  if (info.streak > 0)
16
14
  metrics.inc("counter_flush_failed_total", { table: info.table, kind: info.kind });
17
15
  if (info.streak === 3)
18
- logger.warn("counter_flush_degraded", { table: info.table, kind: info.kind, streak: info.streak, ...(info.error ? { error: info.error } : {}) });
16
+ logger.warn("counter_flush_degraded", { table: info.table, kind: info.kind, streak: info.streak, ...(info.error ? { error: seatText("operator-log-text", info.error) } : {}) });
19
17
  else if (info.streak === 0 && info.prevStreak >= 3)
20
18
  logger.info("counter_flush_recovered", { table: info.table, kind: info.kind, prevStreak: info.prevStreak });
21
19
  };
20
+ }
21
+ export function createBudgetAndTracing(ctx) {
22
+ const { config, logger, metrics, backend, breakerState } = ctx;
23
+ const { brain, degradeRoute: degradeRouteWatch } = createBrainAssembly(config, { breakerState });
24
+ const pricing = buildPricing(config.models);
25
+ const counterDegradeHook = createCounterDegradeHook({ logger, metrics });
22
26
  const bounds = buildQuotaBounds(config);
23
27
  const costQuota = backend?.costQuota
24
28
  ? backend.costQuota(bounds.limitMicroUsd, bounds.windowMs, counterDegradeHook).startRefresh()
@@ -77,7 +77,7 @@ export declare function createDeviceLane(ctx: DeviceLaneCtx): DeviceLane | undef
77
77
  */
78
78
  export declare function assertDeviceLaneWired(isDeviceLane: boolean, lane: DeviceLane | undefined, hasFactory: boolean): void;
79
79
  /**
80
- * 设备入口 × 已绑设备的起服一行(拒的那一半在配置期:device 车道上 `DEVICE_RELAY_URL` 必填,缺席拒起 —— `config.ts` `parseDeviceRelayUrlEnv`;
80
+ * 设备入口 × 已绑设备的起服一行(拒的那一半在配置期:device 车道上 `DEVICE_RELAY_URL` 必填 —— 缺席拒起是车道必需 env 门(`config.ts` `declaredExecutionLane` → `laneMissingEnv`),坏形 / 带凭据形拒启是 `parseDeviceRelayUrlEnv`;
81
81
  * 起不起服不随表里有几条绑定变)。
82
82
  *
83
83
  * 字面 `none` = 运维声明「设备拨本引擎自己的地址 + `wsPath`」。此刻根会话行上若有设备绑定(writer(device, D) 的格),这些会话的设备从此只能拨那个入口 ——
@@ -8,10 +8,13 @@ import type { DeviceEnrollment } from "../device-enrollment.js";
8
8
  import type { DeviceStore } from "../device-store.js";
9
9
  import type { ExecutionPlacementSeat } from "../execution-placement.js";
10
10
  import type { DeviceWsHub } from "../device-ws-hub.js";
11
+ import { type RemoteContainerEnvDeps } from "../plugins/remote-env-e2b.js";
12
+ import { type WorkspaceFillOf } from "../plugins/workspace-fill.js";
11
13
  import { k8sExecutionEnvFactory, type K8sEnvConfig, type K8sEnvDeps, type SandboxSecretSeat } from "../plugins/remote-env-k8s.js";
12
- import { type SandboxGitPlane, type SandboxGitRunRows } from "../sandbox-git-plane.js";
14
+ import { type SandboxGitPlaneSeat, type SandboxGitRunRows } from "../sandbox-git-plane.js";
13
15
  import { type SandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
14
16
  import { type SpaceCommitRows } from "../workspace-space-commit.js";
17
+ import { type LocalDockerEnvDeps } from "../plugins/remote-env-local-docker.js";
15
18
  import * as facts from "../capability-facts.js";
16
19
  import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
17
20
  import type { TaskInheritEnv } from "../request-value-closure.js";
@@ -77,10 +80,19 @@ export declare function createSessionScopedRegistries(maxEntries?: number): Sess
77
80
  * · 运维行座(`logger`)随部署级配置走。
78
81
  * `deps` = 适配器自己的测试缝(假集群 / 假对象存储),生产装配不传。返回的是工厂闭包 ⇒ `create*`。
79
82
  */
83
+ /**
84
+ * 一条 run 是不是它工作区的属主:它的放置根就是它自己的会话(core `ExecutionEnvFactoryContext.placementRootSessionId`)。委派子代 / 后台代理与父 run 并发
85
+ * (一只工作区两个写者)、本服务为自己起的 run(放置根 = `INTERNAL_RUN_PLACEMENT_ROOT`)都不是。两个读者同一判:k8s 工作区空间(子代依律关)与
86
+ * 恢复点的播种(只有属主播种)。
87
+ */
88
+ export declare function ownsItsWorkspace(ctx: {
89
+ readonly sessionId: string;
90
+ readonly placementRootSessionId: string;
91
+ }): boolean;
80
92
  export declare function createK8sRunEnvFactory(k8sCfg: K8sEnvConfig, seats: {
81
93
  imageOf(sessionId: string): string | undefined;
82
94
  /** The member git-credential broker seat for the pod's run (`runId` = the one reading of the run that owns the pod, the same value its
83
- * `sema.io/run` label carries; `sandbox-git-plane.ts` `sandboxBrokerSeatOf` over the boot's plane reading and the run row's identity);
95
+ * `sema.io/run` label carries; `sandbox-git-plane.ts` `createSandboxBrokerSeatOf` over the plane seat's reading at that pod and the run row's identity);
84
96
  * absent seat / `undefined` answer ⇒ the pod carries no broker. */
85
97
  brokerSeatOf?(runId: string | undefined): Promise<K8sEnvConfig["sandboxBroker"]>;
86
98
  /** The session store's by-id head read (`SessionStore.headOf`, picked once by `headReadOf`); the occupant is its live-occupant projection
@@ -90,11 +102,13 @@ export declare function createK8sRunEnvFactory(k8sCfg: K8sEnvConfig, seats: {
90
102
  /** The run store's commit-fence rows (`workspace_space_commit`); answers `undefined` only before stage 7 binds the run store
91
103
  * or without a store backend — the latter `assertWorkspaceSpaceReadable` refuses together with `space`. */
92
104
  spaceCommits(): SpaceCommitRows | undefined;
105
+ /** The restore point's fill seat of the run (`workspace-fill.ts`; `createExecutionEnv` mints the one reading). Absent / `undefined` ⇒ the pod as it comes. */
106
+ workspaceFillOf?: WorkspaceFillOf;
93
107
  }, deps?: K8sEnvDeps): ReturnType<typeof k8sExecutionEnvFactory>;
94
108
  /**
95
109
  * k8s 车道沙箱秘密座的**起服前提**(装配后的真门,第 5 段 await)。法只有一条:**秘密座非空(`K8S_SANDBOX_ENV` 点到了本进程环境里有的键)⇒
96
110
  * 问一次 SelfSubjectAccessReview(env 座那一行动词 {@link SANDBOX_SECRET_SEAT_VERBS}`.env`,沙箱命名空间)** —— 只问这一座自己发的调用,
97
- * 不问租约座的 `patch`(那一座由凭据平面的判官在它的前提都成立时另问,见 `readSandboxGitPlane`):
111
+ * 不问租约座的 `patch`(那一座由凭据平面座在它的前提都成立时另问一次,见 `sandbox-git-plane.ts` 的 `createSandboxGitPlaneSeat` 的 `settle`):
98
112
  * · 答了「不许」(任一动词)⇒ 拒启,一行拒句点名缺的动词、命名空间、Role 规则原文与出路(`[sandbox-secret-rbac]`)—— 不等第一个 run 才失败;
99
113
  * · 问不到(请求失败 / 审查本身被拒 / 答复没有 `status.allowed`)⇒ 照常起服:F 类兜底口 `server.sandbox-secret.seat-unverified` 计数 + 一行
100
114
  * `sandbox_secret_seat_unverified`。放行的最坏后果:RBAC 真缺时第一个要建 Secret 的 run 连接即拒(`auth_failed`,pod 被删)—— 秘密值仍然
@@ -141,6 +155,12 @@ export interface ExecutionEnvCtx {
141
155
  /** run 店的按 id 读(成员凭据代理座读 run 行上受理时记下的身份元组 `RunRecord.identity`)。取值闭包:run 店第 7 段才绑(`late-binds.ts`),
142
156
  * 平面开着时每个 run 的 pod 建起前求值一次。不传 / 答 `undefined`(没有存储后端)⇒ 读不到身份 ⇒ 不挂代理。 */
143
157
  runRows?: () => SandboxGitRunRows | undefined;
158
+ /** 隔离车道三只适配器自己的测试缝(假 e2b SDK / 假集群 / 假 docker CLI),原样交给各自的工厂 —— 与 `K8sEnvDeps` 同一条律:生产装配不传。 */
159
+ laneDeps?: {
160
+ readonly e2b?: RemoteContainerEnvDeps;
161
+ readonly k8s?: K8sEnvDeps;
162
+ readonly localDocker?: LocalDockerEnvDeps;
163
+ };
144
164
  }
145
165
  export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
146
166
  perTaskImage: PerTaskImageRegistry;
@@ -158,6 +178,6 @@ export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
158
178
  spaceReclaimer: SpaceReclaimer | undefined;
159
179
  sandboxSecretSeatGate: (() => Promise<void>) | undefined;
160
180
  sandboxPodReaper: SandboxPodReaper | undefined;
161
- readSandboxGitPlane: () => Promise<SandboxGitPlane>;
181
+ sandboxGitPlane: SandboxGitPlaneSeat;
162
182
  };
163
183
  //# sourceMappingURL=execution-env.d.ts.map
@@ -11,10 +11,11 @@ import { PerTaskImageRegistry } from "../per-task-image.js";
11
11
  import { adbExecutionEnvFactory } from "../plugins/remote-env-adb.js";
12
12
  import { deviceExecutionEnvFactory } from "../plugins/remote-env-device.js";
13
13
  import { e2bExecutionEnvFactory } from "../plugins/remote-env-e2b.js";
14
+ import { createWorkspaceFill } from "../plugins/workspace-fill.js";
14
15
  import { hostExecutionEnvFactory, RemoteHostExecutionEnv } from "../plugins/remote-env-host.js";
15
16
  import { boundedStoreCall, checkSandboxEgressPolicy, createSandboxPodApi, createSpaceReclaimer, k8sExecutionEnvFactory, readSandboxSecretSeat, rpcTimeoutMsOf } from "../plugins/remote-env-k8s.js";
16
17
  import { podSpecPatchBrokerRefusal, SANDBOX_SECRET_SEAT_VERBS } from "../plugins/k8s-exec-protocol.js";
17
- import { judgeSandboxGitPlane, runIdentitySourceOf, sandboxBrokerSeatOf, sandboxGitPlaneAsksSecretSeat, sandboxGitPlanePremisesOf } from "../sandbox-git-plane.js";
18
+ import { createSandboxBrokerSeatOf, createSandboxGitPlaneSeat, sandboxGitPlanePremisesOf } from "../sandbox-git-plane.js";
18
19
  import { createSandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
19
20
  import { recordFailOpen } from "../observability/fail-open.js";
20
21
  import { createRunWorkspaceSnapshotSeat } from "../fleet/workspace-snapshot-residual.js";
@@ -38,6 +39,7 @@ import { sandboxPathEnvSlots, withSandboxPathEnvSlot } from "./deferred-sandbox-
38
39
  import { createLaneHomeAbsenceReporter } from "../lane-home-probe.js";
39
40
  import { REMOTE_EXEC_PROVIDER_WORDS, buildLaneHomeBootNote, buildLaneHomeClaim, capabilitiesFor, executionLaneOf, laneBindsReads, laneIsolated, laneSandboxReadFloorState } from "../execution-lane-caps.js";
40
41
  import { warnReadDenyFloorUnkept } from "./execution-lane.js";
42
+ import { seatText } from "../trace/exit-seat.js";
41
43
  function boundedSessionSetter(map, maxEntries) {
42
44
  return (sid, value) => {
43
45
  map.delete(sid);
@@ -105,15 +107,19 @@ export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
105
107
  dropSession,
106
108
  };
107
109
  }
110
+ export function ownsItsWorkspace(ctx) {
111
+ return ctx.placementRootSessionId === ctx.sessionId;
112
+ }
108
113
  export function createK8sRunEnvFactory(k8sCfg, seats, deps) {
109
114
  const cacheMisses = createCacheMissMemory();
110
115
  return async (ctx) => {
111
116
  const onWorkspaceSnapshot = createRunWorkspaceSnapshotSeat(ctx.sessionId);
112
- const child = ctx.placementRootSessionId !== ctx.sessionId;
117
+ const child = !ownsItsWorkspace(ctx);
113
118
  const s3Snapshot = child && k8sCfg.s3Snapshot !== undefined ? spaceOff(k8sCfg.s3Snapshot) : k8sCfg.s3Snapshot;
114
119
  const spaceOccupant = child ? undefined : await occupantOf(seats, ctx.sessionId, ctx.taskId, rpcTimeoutMsOf(k8sCfg));
115
120
  const runId = !child && ctx.taskId !== undefined ? ctx.taskId : undefined;
116
121
  const sandboxBroker = await seats.brokerSeatOf?.(runId);
122
+ const workspaceFill = seats.workspaceFillOf?.(ctx);
117
123
  const { cache, ...deploymentCfg } = k8sCfg;
118
124
  return k8sExecutionEnvFactory({
119
125
  ...deploymentCfg,
@@ -123,6 +129,7 @@ export function createK8sRunEnvFactory(k8sCfg, seats, deps) {
123
129
  ...(spaceOccupant !== undefined ? { spaceOccupant } : {}),
124
130
  ...(runId !== undefined ? { runId } : {}),
125
131
  ...(sandboxBroker !== undefined ? { sandboxBroker } : {}),
132
+ ...(workspaceFill !== undefined ? { workspaceFill } : {}),
126
133
  onWorkspaceSnapshot,
127
134
  }, deps)(ctx);
128
135
  };
@@ -171,7 +178,7 @@ export async function ensureSandboxSecretSeat(k8sCfg, logger, deps, read = () =>
171
178
  recordFailOpen("server.sandbox-secret.seat-unverified", `namespace=${namespace} ${seat.detail}`);
172
179
  logger.warn("sandbox_secret_seat_unverified", {
173
180
  namespace,
174
- detail: seat.detail,
181
+ detail: seatText("operator-log-text", seat.detail),
175
182
  effect: `the boot could not confirm the sandbox Secret RBAC (secrets ${SANDBOX_SECRET_SEAT_VERBS.env.join(" / ")}) — the service starts; if the Role lacks them, the first run that forwards K8S_SANDBOX_ENV is refused at connect (auth_failed) and its pod deleted — the values never go into the pod spec`,
176
183
  });
177
184
  return;
@@ -239,9 +246,9 @@ export function createExecutionEnv(ctx) {
239
246
  let spaceReclaimer;
240
247
  let sandboxSecretSeatGate;
241
248
  let sandboxPodReaper;
242
- const planeCell = {};
243
- let readSandboxGitPlane = async () => (planeCell.current = judgeSandboxGitPlane(sandboxGitPlanePremisesOf(config, {})));
249
+ let sandboxGitPlane = createSandboxGitPlaneSeat((r) => sandboxGitPlanePremisesOf(config, r), {});
244
250
  let worktreeReap;
251
+ const workspaceFillOf = taskAttachmentStore === undefined ? undefined : (fctx) => createWorkspaceFill({ store: taskAttachmentStore, sessionId: fctx.sessionId, seeds: ownsItsWorkspace(fctx), logger });
245
252
  if (config.remoteExec?.provider === "e2b") {
246
253
  executionEnvFactory = e2bExecutionEnvFactory({
247
254
  apiKey: config.remoteExec.apiKey,
@@ -253,9 +260,10 @@ export function createExecutionEnv(ctx) {
253
260
  ...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
254
261
  ...((env) => (env !== undefined ? { sandboxEnv: env } : {}))(sandboxLaneEnvMerged(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv))),
255
262
  sandboxReadFloor: config.sandboxReadFloor,
263
+ ...(workspaceFillOf !== undefined ? { workspaceFillOf } : {}),
256
264
  logger,
257
265
  metrics,
258
- });
266
+ }, ctx.laneDeps?.e2b);
259
267
  }
260
268
  else if (config.remoteExec?.provider === "k8s") {
261
269
  const k8sCfg = {
@@ -281,30 +289,24 @@ export function createExecutionEnv(ctx) {
281
289
  ...((lane) => ({ ...(lane.env !== undefined ? { podEnv: lane.env } : {}), ...(lane.secretEnv !== undefined ? { podSecretEnv: lane.secretEnv } : {}) }))(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv)),
282
290
  sandboxReadFloor: config.sandboxReadFloor,
283
291
  };
284
- const brokerImage = config.remoteExec.brokerImage;
292
+ sandboxGitPlane = createSandboxGitPlaneSeat((r) => sandboxGitPlanePremisesOf(config, r), {
293
+ patchRefusal: podSpecPatchBrokerRefusal(k8sCfg.podSpecPatch),
294
+ askSecretSeat: () => readSandboxSecretSeat(k8sCfg, "lease"),
295
+ });
285
296
  executionEnvFactory = createK8sRunEnvFactory(k8sCfg, {
286
297
  imageOf: (sid) => perTaskImage.get(sid),
287
298
  headOf: ctx.headOf,
288
299
  spaceCommits: ctx.spaceCommits ?? (() => undefined),
289
- brokerSeatOf: async (runId) => {
290
- const plane = planeCell.current;
291
- if (plane === undefined || plane.state !== "on")
292
- return undefined;
293
- return sandboxBrokerSeatOf(plane, brokerImage, await runIdentitySourceOf(runId, ctx.runRows?.(), logger));
294
- },
295
- });
300
+ brokerSeatOf: createSandboxBrokerSeatOf(sandboxGitPlane.now, config.remoteExec.brokerImage, ctx.runRows, logger),
301
+ ...(workspaceFillOf !== undefined ? { workspaceFillOf } : {}),
302
+ }, ctx.laneDeps?.k8s);
296
303
  if (k8sCfg.cache !== undefined || k8sCfg.s3Snapshot !== undefined)
297
304
  spaceReclaimer = createSpaceReclaimer(k8sCfg);
298
305
  sandboxSecretSeatGate = () => ensureSandboxSecretSeat(k8sCfg, logger, undefined, () => readSandboxSecretSeat(k8sCfg, "env"));
299
- readSandboxGitPlane = async () => {
300
- const premises = sandboxGitPlanePremisesOf(config, { ...((r) => (r !== undefined ? { patchRefusal: r } : {}))(podSpecPatchBrokerRefusal(k8sCfg.podSpecPatch)) });
301
- const secretSeat = sandboxGitPlaneAsksSecretSeat(premises) ? await readSandboxSecretSeat(k8sCfg, "lease") : undefined;
302
- return (planeCell.current = judgeSandboxGitPlane({ ...premises, secretSeat }));
303
- };
304
306
  sandboxPodReaper = createSandboxPodReaper({ api: createSandboxPodApi(k8sCfg), logger, metrics });
305
307
  void checkSandboxEgressPolicy(k8sCfg)
306
308
  .then((gaps) => sayEgressPolicyReading(gaps, logger, k8sCfg.namespace))
307
- .catch((e) => logger.error("sandbox_egress_policy_check_crashed", { error: e instanceof Error ? e.message : String(e), effect: "the sandbox egress self-check did not complete this boot — the lane runs; the egress policy was not read" }));
309
+ .catch((e) => logger.error("sandbox_egress_policy_check_crashed", { error: seatText("operator-log-text", e instanceof Error ? e.message : String(e)), effect: "the sandbox egress self-check did not complete this boot — the lane runs; the egress policy was not read" }));
308
310
  }
309
311
  else if (config.remoteExec?.provider === "ssh") {
310
312
  executionEnvFactory = sshExecutionEnvFactory({
@@ -397,7 +399,8 @@ export function createExecutionEnv(ctx) {
397
399
  ...(config.remoteExec.commandTimeoutMs != null ? { commandTimeoutMs: config.remoteExec.commandTimeoutMs } : {}),
398
400
  ...(config.remoteExec.env ? { env: config.remoteExec.env } : {}),
399
401
  sandboxReadFloor: config.sandboxReadFloor,
400
- });
402
+ ...(workspaceFillOf !== undefined ? { workspaceFillOf } : {}),
403
+ }, ctx.laneDeps?.localDocker);
401
404
  }
402
405
  if (executionEnvFactory && DEPLOYMENT_SHAPE_PREDICATES.remoteScratchpad.holds(config)) {
403
406
  executionEnvFactory = withRemoteScratchpad(executionEnvFactory, logger);
@@ -451,7 +454,7 @@ export function createExecutionEnv(ctx) {
451
454
  executionEnvFactory = registry.wrapFactory(executionEnvFactory);
452
455
  return registry;
453
456
  })();
454
- if (taskAttachmentStore && executionEnvFactory) {
457
+ if (taskAttachmentStore && executionEnvFactory && !laneIsolated(executionLaneOf(config.remoteExec.provider))) {
455
458
  const inner = executionEnvFactory;
456
459
  const attStore = taskAttachmentStore;
457
460
  executionEnvFactory = async (ctx) => {
@@ -507,7 +510,7 @@ export function createExecutionEnv(ctx) {
507
510
  perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv, setSessionInheritEnv,
508
511
  fenceSessionOwner: sessionScoped.fenceSessionOwner,
509
512
  dropSessionScoped: sessionScoped.dropSession,
510
- executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer, sandboxSecretSeatGate, sandboxPodReaper, readSandboxGitPlane,
513
+ executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer, sandboxSecretSeatGate, sandboxPodReaper, sandboxGitPlane,
511
514
  };
512
515
  }
513
516
  //# sourceMappingURL=execution-env.js.map
@@ -4,6 +4,7 @@ import { crossInvariant } from "../config-invariants.js";
4
4
  import { buildReadDenyFloorUnkeptNote, executionLaneOf, laneIsolated } from "../execution-lane-caps.js";
5
5
  import { buildReadFacePostureNotice, settleDerivedReadFace } from "../read-face-posture.js";
6
6
  import { settleHostShellAtBoot } from "../plugins/host-platform.js";
7
+ import { seatText } from "../trace/exit-seat.js";
7
8
  export const FILE_SOURCED_LANES = ["host", "local-docker"];
8
9
  export async function composeExecutionLane(config, ctx) {
9
10
  if (ctx.configSource === "local")
@@ -55,7 +56,7 @@ async function applyRemoteExecFile(config, ctx) {
55
56
  fileRx = await loadRemoteExec(localRoot);
56
57
  }
57
58
  catch (err) {
58
- logger.error("remote_exec_file_invalid", { root: localRoot, err: String(err) });
59
+ logger.error("remote_exec_file_invalid", { root: localRoot, err: seatText("operator-log-text", String(err)) });
59
60
  throw new Error(`config.d/remote-exec.json under ${localRoot} is invalid: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
60
61
  }
61
62
  if (!fileRx)
@@ -2,6 +2,7 @@ import { defaultTaskRegistry } from "@sema-agent/core";
2
2
  import { reportHardKillReceipts } from "../background-session-reap.js";
3
3
  import { liveHostForegroundChildCount, reapHostForegroundChildren } from "../plugins/host-platform.js";
4
4
  import { liveEphemeralHostWorkspaceCount, reapEphemeralHostWorkspaces } from "../plugins/host-workspace-registry.js";
5
+ import { seatText } from "../trace/exit-seat.js";
5
6
  const PROCESS_EXIT_INCLUDES_RETAINED = false;
6
7
  export function installProcessExitBoundary({ logger }) {
7
8
  const reapOwnChildProcesses = () => {
@@ -13,7 +14,7 @@ export function installProcessExitBoundary({ logger }) {
13
14
  logger.info("shutdown_background_reaped", { reaped });
14
15
  }
15
16
  catch (err) {
16
- logger.warn("shutdown_background_reap_failed", { err: String(err) });
17
+ logger.warn("shutdown_background_reap_failed", { err: seatText("operator-log-text", String(err)) });
17
18
  }
18
19
  try {
19
20
  const fg = reapHostForegroundChildren();
@@ -23,7 +24,7 @@ export function installProcessExitBoundary({ logger }) {
23
24
  logger.warn("shutdown_foreground_reap_incomplete", { groups: fg.failed });
24
25
  }
25
26
  catch (err) {
26
- logger.warn("shutdown_foreground_reap_failed", { err: String(err) });
27
+ logger.warn("shutdown_foreground_reap_failed", { err: seatText("operator-log-text", String(err)) });
27
28
  }
28
29
  return backgroundSoftStopped;
29
30
  };
@@ -33,7 +34,7 @@ export function installProcessExitBoundary({ logger }) {
33
34
  reportHardKillReceipts(logger, receipts, "process_exit");
34
35
  }
35
36
  catch (err) {
36
- logger.warn("shutdown_background_hard_kill_failed", { err: String(err) });
37
+ logger.warn("shutdown_background_hard_kill_failed", { err: seatText("operator-log-text", String(err)) });
37
38
  }
38
39
  };
39
40
  const reapOwnHostWorkspaces = () => {
@@ -45,7 +46,7 @@ export function installProcessExitBoundary({ logger }) {
45
46
  logger.warn("shutdown_host_workspace_reap_incomplete", { dirs: failed.map((f) => f.dir) });
46
47
  }
47
48
  catch (err) {
48
- logger.warn("shutdown_host_workspace_reap_failed", { err: String(err) });
49
+ logger.warn("shutdown_host_workspace_reap_failed", { err: seatText("operator-log-text", String(err)) });
49
50
  }
50
51
  };
51
52
  const onProcessExit = () => {