@sema-agent/server 7.135.0 → 7.137.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 (342) hide show
  1. package/MIGRATION.md +237 -2
  2. package/USAGE.md +146 -41
  3. package/deploy/sema-up/README.md +15 -1
  4. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  5. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
  6. package/deploy/sema-up/chart/values.yaml +1 -1
  7. package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
  8. package/dist/approval-card.d.ts +60 -4
  9. package/dist/approval-card.js +18 -1
  10. package/dist/approval-content-kind.d.ts +7 -0
  11. package/dist/approval-content-kind.js +8 -1
  12. package/dist/approval-gate-currency.d.ts +48 -6
  13. package/dist/approval-gate-currency.js +16 -9
  14. package/dist/approval-hmac.d.ts +13 -1
  15. package/dist/approval-hmac.js +1 -1
  16. package/dist/approval.js +3 -3
  17. package/dist/auth-bridge.d.ts +34 -5
  18. package/dist/auth-bridge.js +22 -5
  19. package/dist/boot/budget-tracing.d.ts +1 -2
  20. package/dist/boot/budget-tracing.js +2 -2
  21. package/dist/boot/execution-env.d.ts +24 -1
  22. package/dist/boot/execution-env.js +69 -6
  23. package/dist/boot/org-memory.js +1 -1
  24. package/dist/boot/reapers.d.ts +3 -0
  25. package/dist/boot/reapers.js +15 -1
  26. package/dist/boot/resolve-spec.d.ts +4 -0
  27. package/dist/boot/resolve-spec.js +24 -22
  28. package/dist/boot/runner-deps.js +4 -2
  29. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  30. package/dist/boot/stage-04-stores.d.ts +2 -2
  31. package/dist/boot/stage-05-execution-env.d.ts +4 -2
  32. package/dist/boot/stage-05-execution-env.js +11 -2
  33. package/dist/boot/stage-06-runners.d.ts +4 -2
  34. package/dist/boot/stage-06-runners.js +5 -1
  35. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  36. package/dist/boot/stage-07-capability-layer.js +1 -1
  37. package/dist/boot/stage-08-reapers.d.ts +5 -2
  38. package/dist/boot/stage-08-reapers.js +2 -1
  39. package/dist/boot/stage-09-leader.d.ts +5 -2
  40. package/dist/boot/stage-10-http-server.d.ts +5 -2
  41. package/dist/boot/stage-10-http-server.js +3 -2
  42. package/dist/boot/stores.d.ts +1 -1
  43. package/dist/boot/stores.js +2 -3
  44. package/dist/boot/web-admin.js +10 -17
  45. package/dist/brain.js +12 -3
  46. package/dist/broker/broker.d.ts +89 -0
  47. package/dist/broker/broker.js +287 -0
  48. package/dist/broker/constants.d.ts +39 -0
  49. package/dist/broker/constants.js +15 -0
  50. package/dist/broker/http1.d.ts +111 -0
  51. package/dist/broker/http1.js +256 -0
  52. package/dist/broker/lease-book.d.ts +87 -0
  53. package/dist/broker/lease-book.js +142 -0
  54. package/dist/broker/lease-file.d.ts +60 -0
  55. package/dist/broker/lease-file.js +127 -0
  56. package/dist/broker/main.d.ts +2 -0
  57. package/dist/broker/main.js +20 -0
  58. package/dist/budget.d.ts +3 -3
  59. package/dist/budget.js +4 -0
  60. package/dist/capabilities/composite-members.d.ts +2 -1
  61. package/dist/capabilities/composite-members.js +2 -1
  62. package/dist/capabilities/engine-capabilities.js +1 -0
  63. package/dist/capabilities/mcp-probe.d.ts +5 -4
  64. package/dist/capabilities/scenarios.d.ts +23 -8
  65. package/dist/capabilities/scenarios.js +13 -8
  66. package/dist/capabilities/skills.d.ts +28 -5
  67. package/dist/capabilities/skills.js +8 -0
  68. package/dist/capability-facts.d.ts +3 -7
  69. package/dist/capability-facts.js +3 -5
  70. package/dist/center-credential-renewer.js +5 -1
  71. package/dist/center-credential.d.ts +18 -4
  72. package/dist/center-credential.js +18 -3
  73. package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
  74. package/dist/checkpoint-ctx-seal-m28.js +1 -1
  75. package/dist/checkpoint-identity.d.ts +3 -3
  76. package/dist/config-catalog.js +21 -16
  77. package/dist/config-d-migration.js +1 -1
  78. package/dist/config-invariants.d.ts +1 -1
  79. package/dist/config-invariants.js +8 -0
  80. package/dist/config-lkg.d.ts +0 -38
  81. package/dist/config-lkg.js +1 -83
  82. package/dist/config-types.d.ts +21 -4
  83. package/dist/config.d.ts +1 -1
  84. package/dist/config.js +60 -16
  85. package/dist/deployment-governance.d.ts +2 -2
  86. package/dist/deployment-governance.js +7 -9
  87. package/dist/deployment-shape.d.ts +1 -1
  88. package/dist/device-ws-protocol.d.ts +1 -0
  89. package/dist/device-ws-protocol.js +2 -1
  90. package/dist/elicitation.d.ts +8 -7
  91. package/dist/elicitation.js +9 -8
  92. package/dist/file-history-disclosure.d.ts +8 -4
  93. package/dist/file-history-disclosure.js +24 -6
  94. package/dist/fleet-client.d.ts +32 -1
  95. package/dist/fleet-client.js +24 -4
  96. package/dist/fleet-lease.d.ts +7 -3
  97. package/dist/fleet-lease.js +31 -23
  98. package/dist/hooks/classic-stations.d.ts +11 -54
  99. package/dist/hooks/hook-llm.js +14 -8
  100. package/dist/hooks/hook-runner.d.ts +4 -4
  101. package/dist/hooks/hook-runner.js +2 -2
  102. package/dist/http/admission.js +10 -7
  103. package/dist/http/approval-sweeps.js +4 -3
  104. package/dist/http/caller-trust.d.ts +2 -1
  105. package/dist/http/caller-trust.js +1 -3
  106. package/dist/http/checkpoint-ctx.d.ts +76 -16
  107. package/dist/http/checkpoint-ctx.js +45 -1
  108. package/dist/http/dispatch.js +24 -19
  109. package/dist/http/durable-run-leg.d.ts +5 -1
  110. package/dist/http/durable-run-leg.js +1 -1
  111. package/dist/http/health-self-report.d.ts +2 -2
  112. package/dist/http/mcp-remote-text.d.ts +5 -0
  113. package/dist/http/mcp-remote-text.js +7 -0
  114. package/dist/http/principal-gate.d.ts +15 -10
  115. package/dist/http/principal-gate.js +48 -9
  116. package/dist/http/resume-ctx-read.d.ts +2 -16
  117. package/dist/http/resume-ctx-read.js +2 -3
  118. package/dist/http/resume-legs.d.ts +2 -20
  119. package/dist/http/resume-legs.js +52 -38
  120. package/dist/http/route-ctx.d.ts +8 -2
  121. package/dist/http/routes/approvals-assistant.d.ts +0 -2
  122. package/dist/http/routes/approvals-assistant.js +65 -16
  123. package/dist/http/routes/capabilities.js +11 -1
  124. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  125. package/dist/http/routes/mcp-reconnect.js +5 -7
  126. package/dist/http/routes/runs.js +4 -3
  127. package/dist/http/routes/session-sync.js +70 -12
  128. package/dist/http/routes/sessions.js +20 -15
  129. package/dist/http/routes/tasks.js +8 -6
  130. package/dist/http/server.d.ts +4 -0
  131. package/dist/http/stored-body-replay.d.ts +3 -3
  132. package/dist/http/stored-body-replay.js +6 -1
  133. package/dist/http/submit-ctx.js +1 -1
  134. package/dist/http/wire-types.d.ts +57 -30
  135. package/dist/identity-source.d.ts +65 -0
  136. package/dist/identity-source.js +45 -0
  137. package/dist/index.d.ts +1 -0
  138. package/dist/index.js +1 -0
  139. package/dist/memory-store-refusal.d.ts +3 -3
  140. package/dist/model-entry-refusal.d.ts +2 -0
  141. package/dist/model-entry-refusal.js +11 -0
  142. package/dist/model-route-gap.d.ts +49 -4
  143. package/dist/model-route-gap.js +34 -1
  144. package/dist/model-select.d.ts +6 -17
  145. package/dist/model-select.js +0 -3
  146. package/dist/namespaced-rule-name.d.ts +1 -1
  147. package/dist/object-store-buckets.d.ts +13 -0
  148. package/dist/object-store-buckets.js +3 -0
  149. package/dist/observability/fail-open.d.ts +19 -7
  150. package/dist/observability/fail-open.js +19 -7
  151. package/dist/observability/metrics.js +2 -2
  152. package/dist/observability/principal-context.d.ts +4 -1
  153. package/dist/observability/principal-context.js +6 -1
  154. package/dist/observability/run-terminal.js +1 -0
  155. package/dist/operator-ask.d.ts +2 -2
  156. package/dist/org-delivery-gate.js +1 -1
  157. package/dist/org-memory-admission.d.ts +2 -2
  158. package/dist/org-memory-admission.js +39 -2
  159. package/dist/parked-decide.d.ts +4 -3
  160. package/dist/parked-decide.js +29 -9
  161. package/dist/peer-directory.d.ts +2 -1
  162. package/dist/peer-directory.js +7 -5
  163. package/dist/permission-rule-list.d.ts +2 -2
  164. package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
  165. package/dist/plugins/approval-ask-store-sql.js +3 -0
  166. package/dist/plugins/blob-backend.js +2 -1
  167. package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
  168. package/dist/plugins/checkpoint-index-columns.js +28 -0
  169. package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
  170. package/dist/plugins/checkpoint-store-sql.js +64 -35
  171. package/dist/plugins/file-history-store-sql.d.ts +6 -0
  172. package/dist/plugins/file-history-store-sql.js +37 -37
  173. package/dist/plugins/file-run-store.js +8 -2
  174. package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
  175. package/dist/plugins/k8s-exec-protocol.js +116 -28
  176. package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
  177. package/dist/plugins/k8s-pod-delete-failure.js +17 -0
  178. package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
  179. package/dist/plugins/k8s-pod-reaper.js +122 -0
  180. package/dist/plugins/local-checkpoint-store.d.ts +3 -6
  181. package/dist/plugins/local-checkpoint-store.js +7 -6
  182. package/dist/plugins/memory-run-store.js +2 -0
  183. package/dist/plugins/one-time-migrations.d.ts +49 -37
  184. package/dist/plugins/one-time-migrations.js +87 -128
  185. package/dist/plugins/permission-rule-store-file.js +2 -2
  186. package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
  187. package/dist/plugins/permission-rule-store-sql.js +16 -17
  188. package/dist/plugins/pg-pool.js +4 -0
  189. package/dist/plugins/pg-session-storage.js +2 -2
  190. package/dist/plugins/remote-env-adb.d.ts +3 -0
  191. package/dist/plugins/remote-env-adb.js +1 -0
  192. package/dist/plugins/remote-env-device.d.ts +3 -0
  193. package/dist/plugins/remote-env-device.js +4 -0
  194. package/dist/plugins/remote-env-e2b.d.ts +3 -0
  195. package/dist/plugins/remote-env-e2b.js +1 -0
  196. package/dist/plugins/remote-env-file-error.d.ts +8 -1
  197. package/dist/plugins/remote-env-file-error.js +7 -1
  198. package/dist/plugins/remote-env-host.d.ts +7 -1
  199. package/dist/plugins/remote-env-host.js +7 -1
  200. package/dist/plugins/remote-env-k8s.d.ts +154 -14
  201. package/dist/plugins/remote-env-k8s.js +300 -21
  202. package/dist/plugins/remote-env-local-docker.d.ts +3 -0
  203. package/dist/plugins/remote-env-local-docker.js +1 -0
  204. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  205. package/dist/plugins/remote-env-ssh.js +1 -0
  206. package/dist/plugins/run-store-sql.js +10 -4
  207. package/dist/plugins/s3-answer.d.ts +15 -0
  208. package/dist/plugins/s3-answer.js +5 -0
  209. package/dist/plugins/session-ownership.d.ts +17 -3
  210. package/dist/plugins/session-ownership.js +19 -5
  211. package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
  212. package/dist/plugins/snapshot-bucket-presence.js +35 -7
  213. package/dist/plugins/sql-errors.d.ts +60 -0
  214. package/dist/plugins/sql-errors.js +64 -1
  215. package/dist/plugins/store-contracts.d.ts +10 -1
  216. package/dist/plugins/store-fault.d.ts +4 -0
  217. package/dist/plugins/store-fault.js +10 -0
  218. package/dist/plugins/tidb-pool.js +8 -1
  219. package/dist/plugins/tidb-session-store.js +2 -2
  220. package/dist/plugins/web-search.d.ts +1 -1
  221. package/dist/plugins/web-search.js +2 -2
  222. package/dist/request-declaration-refusal.d.ts +94 -0
  223. package/dist/request-declaration-refusal.js +78 -0
  224. package/dist/request-image-urls.d.ts +6 -0
  225. package/dist/request-image-urls.js +27 -0
  226. package/dist/request-key-closure.d.ts +5 -0
  227. package/dist/request-key-closure.js +5 -2
  228. package/dist/request-value-closure.d.ts +31 -2
  229. package/dist/request-value-closure.js +7 -1
  230. package/dist/rule-entry-list.d.ts +3 -2
  231. package/dist/rule-entry-list.js +2 -2
  232. package/dist/rules-consent.js +7 -4
  233. package/dist/run-local.d.ts +2 -2
  234. package/dist/runs.js +2 -1
  235. package/dist/runtime-caps-resolver.js +1 -1
  236. package/dist/runtime-governance.d.ts +5 -5
  237. package/dist/sandbox-git-lease.d.ts +49 -0
  238. package/dist/sandbox-git-lease.js +60 -0
  239. package/dist/sandbox-git-plane.d.ts +84 -0
  240. package/dist/sandbox-git-plane.js +105 -0
  241. package/dist/sandbox-pkg-source.d.ts +28 -1
  242. package/dist/sandbox-pkg-source.js +1 -2
  243. package/dist/sealed-key.d.ts +19 -0
  244. package/dist/sealed-key.js +47 -4
  245. package/dist/security.d.ts +16 -4
  246. package/dist/security.js +8 -3
  247. package/dist/server-secret-env.d.ts +6 -0
  248. package/dist/server-secret-env.js +14 -0
  249. package/dist/session-error-wire.d.ts +14 -6
  250. package/dist/session-error-wire.js +2 -1
  251. package/dist/session-incarnation.d.ts +16 -5
  252. package/dist/session-incarnation.js +7 -4
  253. package/dist/session-policy-wire.d.ts +1 -1
  254. package/dist/session-policy-wire.js +2 -2
  255. package/dist/session-row.d.ts +3 -17
  256. package/dist/session-row.js +4 -0
  257. package/dist/session-sync.d.ts +15 -3
  258. package/dist/session-sync.js +5 -2
  259. package/dist/spec-fields.d.ts +43 -13
  260. package/dist/spec-fields.js +33 -12
  261. package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
  262. package/dist/stored-request-prompt-profile-m30.js +25 -0
  263. package/dist/task-a2a.d.ts +52 -81
  264. package/dist/task-a2a.js +219 -89
  265. package/dist/task-mcp.d.ts +7 -15
  266. package/dist/task-mcp.js +16 -51
  267. package/dist/task-settings.d.ts +2 -2
  268. package/dist/tool-approval.d.ts +35 -12
  269. package/dist/tool-approval.js +59 -39
  270. package/dist/tool-rule-names.d.ts +5 -0
  271. package/dist/trace/core-keyset-guard.d.ts +5 -5
  272. package/dist/trace/engine-notice-wire.d.ts +1 -1
  273. package/dist/trace/engine-notice-wire.js +2 -0
  274. package/dist/trace/exit-seat.js +37 -13
  275. package/dist/trace/ledger-sink.d.ts +4 -5
  276. package/dist/trace/ledger-sink.js +3 -7
  277. package/dist/trace/project.d.ts +37 -3
  278. package/dist/trace/project.js +20 -6
  279. package/dist/trace/redact.d.ts +40 -12
  280. package/dist/trace/redact.js +332 -128
  281. package/dist/trace/self-minted-wire.d.ts +641 -0
  282. package/dist/trace/self-minted-wire.js +201 -0
  283. package/dist/trace/wire-projection-faces.d.ts +5 -3
  284. package/dist/trace/wire-projection-faces.js +7 -3
  285. package/dist/url-credential-refusal.d.ts +52 -0
  286. package/dist/url-credential-refusal.js +14 -0
  287. package/dist/url-refusal.d.ts +13 -4
  288. package/dist/url-refusal.js +8 -2
  289. package/dist/web-admin-client/apply-effective.js +0 -1
  290. package/dist/web-admin-client/center-request.d.ts +20 -2
  291. package/dist/web-admin-client/center-request.js +40 -5
  292. package/dist/web-admin-client/http-client.js +6 -6
  293. package/dist/web-admin-client/types.d.ts +5 -5
  294. package/dist/workspace-space.d.ts +27 -0
  295. package/dist/workspace-space.js +26 -0
  296. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
  297. package/package.json +4 -3
  298. package/scripts/drain-check-local.mjs +2 -1
  299. package/scripts/drain-check.mysql.sql +3 -1
  300. package/scripts/drain-check.pg.sql +3 -1
  301. package/skills/schedule.md +1 -1
  302. package/wire-changes/server@7.104.0.json +134 -0
  303. package/wire-changes/server@7.105.0.json +53 -0
  304. package/wire-changes/server@7.106.0.json +475 -0
  305. package/wire-changes/server@7.107.0.json +64 -0
  306. package/wire-changes/server@7.108.0.json +153 -0
  307. package/wire-changes/server@7.109.0.json +46 -0
  308. package/wire-changes/server@7.110.0.json +90 -0
  309. package/wire-changes/server@7.111.0.json +86 -0
  310. package/wire-changes/server@7.112.0.json +60 -0
  311. package/wire-changes/server@7.113.0.json +26 -0
  312. package/wire-changes/server@7.114.0.json +26 -0
  313. package/wire-changes/server@7.115.0.json +97 -0
  314. package/wire-changes/server@7.116.0.json +218 -0
  315. package/wire-changes/server@7.116.1.json +26 -0
  316. package/wire-changes/server@7.117.0.json +65 -0
  317. package/wire-changes/server@7.118.0.json +129 -0
  318. package/wire-changes/server@7.119.0.json +65 -0
  319. package/wire-changes/server@7.120.0.json +89 -0
  320. package/wire-changes/server@7.121.0.json +219 -0
  321. package/wire-changes/server@7.122.0.json +54 -0
  322. package/wire-changes/server@7.123.0.json +26 -0
  323. package/wire-changes/server@7.124.0.json +336 -0
  324. package/wire-changes/server@7.124.1.json +26 -0
  325. package/wire-changes/server@7.125.0.json +111 -0
  326. package/wire-changes/server@7.126.0.json +285 -0
  327. package/wire-changes/server@7.127.0.json +77 -0
  328. package/wire-changes/server@7.127.1.json +26 -0
  329. package/wire-changes/server@7.127.2.json +53 -0
  330. package/wire-changes/server@7.128.0.json +26 -0
  331. package/wire-changes/server@7.129.0.json +106 -0
  332. package/wire-changes/server@7.129.1.json +26 -0
  333. package/wire-changes/server@7.130.0.json +26 -0
  334. package/wire-changes/server@7.131.0.json +118 -0
  335. package/wire-changes/server@7.132.0.json +226 -0
  336. package/wire-changes/server@7.133.0.json +1155 -0
  337. package/wire-changes/server@7.134.0.json +107 -0
  338. package/wire-changes/server@7.135.0.json +174 -0
  339. package/wire-changes/server@7.136.0.json +448 -0
  340. package/wire-changes/server@7.137.0.json +825 -0
  341. package/dist/url-credential.d.ts +0 -52
  342. package/dist/url-credential.js +0 -49
@@ -1,6 +1,8 @@
1
1
  import { createPublicKey, verify as cryptoVerify } from "node:crypto";
2
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";
3
+ import { readTokenScope } from "@sema-agent/settings-schema/api/scopes";
3
4
  import { buildDisplaySafeUrl } from "./trace/redact.js";
5
+ import { centerJwksFetch } from "./web-admin-client/center-request.js";
4
6
  export function authBridgeIssuerOf(config) {
5
7
  if (config.authBridgeIssuer !== undefined)
6
8
  return { issuer: config.authBridgeIssuer, source: "explicit" };
@@ -8,6 +10,20 @@ export function authBridgeIssuerOf(config) {
8
10
  return { issuer: config.webAdmin.baseUrl, source: "fallback" };
9
11
  return undefined;
10
12
  }
13
+ function recordBridgeRefusal(sinks, error, at = {}) {
14
+ sinks.metrics?.inc("auth_bridge_verify_total", { outcome: error });
15
+ sinks.logger?.warn?.("auth_bridge_rejected", { error, ...(at.kid ? { kid: at.kid.slice(0, 128) } : {}), ...(at.iss !== undefined ? { iss: at.iss } : {}) });
16
+ }
17
+ export function admitBridgeIdentity(outcome, sinks) {
18
+ if (!outcome.ok)
19
+ return undefined;
20
+ const scoped = readTokenScope({ scope: outcome.identity.scope });
21
+ if ("refused" in scoped) {
22
+ recordBridgeRefusal(sinks, scoped.refused);
23
+ return undefined;
24
+ }
25
+ return { ...outcome.identity, scope: scoped.scope };
26
+ }
11
27
  export const DEFAULT_JWKS_TTL_SEC = 15 * 60;
12
28
  export const JWKS_FETCH_TIMEOUT_MS = 10_000;
13
29
  const MAX_JWKS_BYTES = 256 * 1024;
@@ -34,7 +50,6 @@ export function createWebAdminJwtVerifier(opts) {
34
50
  const refreshMin = opts.jwksRefreshMinIntervalSec ?? DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC;
35
51
  const offlineGraceSec = opts.offlineGraceSec ?? DEFAULT_OFFLINE_GRACE_SEC;
36
52
  const jwksTtlSec = opts.jwksTtlSec ?? DEFAULT_JWKS_TTL_SEC;
37
- const doFetch = opts.fetchImpl ?? fetch;
38
53
  const now = opts.nowMs ?? Date.now;
39
54
  const keys = new Map();
40
55
  let lastFetchAttemptMs;
@@ -42,8 +57,7 @@ export function createWebAdminJwtVerifier(opts) {
42
57
  let inflight;
43
58
  const withinGrace = () => lastFetchSuccessMs !== undefined && jwksWithinOfflineGrace({ lastFetchSuccessMs, nowMs: now() }, { offlineGraceSec });
44
59
  const fail = (error, kid) => {
45
- opts.metrics?.inc("auth_bridge_verify_total", { outcome: error });
46
- opts.logger?.warn?.("auth_bridge_rejected", { error, ...(kid ? { kid: kid.slice(0, 128) } : {}), iss: displayIssuer });
60
+ recordBridgeRefusal(opts, error, { kid, iss: displayIssuer });
47
61
  return { ok: false, error };
48
62
  };
49
63
  async function refreshJwks() {
@@ -52,7 +66,7 @@ export function createWebAdminJwtVerifier(opts) {
52
66
  inflight = (async () => {
53
67
  lastFetchAttemptMs = now();
54
68
  try {
55
- const res = await doFetch(jwksUrl, { redirect: "error", signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS) });
69
+ const res = await centerJwksFetch(jwksUrl, { timeoutMs: JWKS_FETCH_TIMEOUT_MS, fetchImpl: opts.fetchImpl });
56
70
  if (!res.ok)
57
71
  throw new Error(`jwks HTTP ${res.status}`);
58
72
  const cl = Number(res.headers.get("content-length"));
@@ -144,8 +158,11 @@ export function createWebAdminJwtVerifier(opts) {
144
158
  const mapped = toVerifiedIdentity(payload, opts.audience ? { audience: opts.audience } : undefined);
145
159
  if (!mapped.ok)
146
160
  return fail(mapped.error, kid);
161
+ const scoped = readTokenScope(payload);
162
+ if ("refused" in scoped)
163
+ return fail(scoped.refused, kid);
147
164
  opts.metrics?.inc("auth_bridge_verify_total", { outcome: "ok" });
148
- return { ok: true, identity: mapped.identity };
165
+ return { ok: true, identity: { ...mapped.identity, scope: scoped.scope } };
149
166
  }
150
167
  return { verify };
151
168
  }
@@ -1,6 +1,5 @@
1
1
  import { ModelUsageTracker, PromptManifestTracker } from "../budget.js";
2
2
  import type { ServiceConfig } from "../config.js";
3
- import { FleetUsageAccumulator } from "../fleet-client.js";
4
3
  import { CostQuota } from "../observability/cost-quota.js";
5
4
  import type { Logger } from "../observability/logger.js";
6
5
  import type { Metrics } from "../observability/metrics.js";
@@ -26,7 +25,7 @@ export declare function createBudgetAndTracing(ctx: BudgetTracingCtx): {
26
25
  costQuota: CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
27
26
  modelUsageTracker: ModelUsageTracker;
28
27
  promptManifestTracker: PromptManifestTracker;
29
- fleetUsage: FleetUsageAccumulator | undefined;
28
+ fleetUsage: import("../fleet-client.js").FleetUsageBins | undefined;
30
29
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
31
30
  tracer: import("@sema-agent/core").TracerHook;
32
31
  sideQueryAccounting: (principal: string | undefined, r: {
@@ -1,6 +1,6 @@
1
1
  import { createBrainAssembly } from "../brain.js";
2
2
  import { ModelUsageTracker, PromptManifestTracker, buildPricing, createSideQueryAccountant, createTracer } from "../budget.js";
3
- import { FleetUsageAccumulator } from "../fleet-client.js";
3
+ import { createFleetUsageBins } from "../fleet-client.js";
4
4
  import { createFleetLeaseFromEnv } from "../fleet-lease.js";
5
5
  import { centerLaneConfigured } from "../web-admin-client/center-request.js";
6
6
  import { CostQuota } from "../observability/cost-quota.js";
@@ -25,7 +25,7 @@ export function createBudgetAndTracing(ctx) {
25
25
  : new CostQuota(bounds.limitMicroUsd, bounds.windowMs);
26
26
  const modelUsageTracker = new ModelUsageTracker();
27
27
  const promptManifestTracker = new PromptManifestTracker();
28
- const fleetUsage = centerLaneConfigured(config) ? new FleetUsageAccumulator() : undefined;
28
+ const fleetUsage = centerLaneConfigured(config) ? createFleetUsageBins(config.webAdmin.credential) : undefined;
29
29
  const fleetLease = createFleetLeaseFromEnv(config, { logger, metrics });
30
30
  const tracer = createTracer(metrics, costQuota, modelUsageTracker, fleetUsage, fleetLease, (m) => config.modelQuotaWeights[m] ?? 1, promptManifestTracker);
31
31
  const sideQueryAccounting = createSideQueryAccountant(metrics, costQuota, fleetUsage, fleetLease, (m) => config.modelQuotaWeights[m] ?? 1);
@@ -7,7 +7,9 @@ import { PerTaskImageRegistry } from "../per-task-image.js";
7
7
  import type { DeviceEnrollment } from "../device-enrollment.js";
8
8
  import type { DeviceStore } from "../device-store.js";
9
9
  import type { DeviceWsHub } from "../device-ws-hub.js";
10
- import { k8sExecutionEnvFactory, type K8sEnvConfig, type K8sEnvDeps } from "../plugins/remote-env-k8s.js";
10
+ import { k8sExecutionEnvFactory, type K8sEnvConfig, type K8sEnvDeps, type SandboxSecretSeat } from "../plugins/remote-env-k8s.js";
11
+ import { type SandboxGitPlane, type SandboxGitRunRows } from "../sandbox-git-plane.js";
12
+ import { type SandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
11
13
  import { type SpaceCommitRows } from "../workspace-space-commit.js";
12
14
  import * as facts from "../capability-facts.js";
13
15
  import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
@@ -76,6 +78,10 @@ export declare function createSessionScopedRegistries(maxEntries?: number): Sess
76
78
  */
77
79
  export declare function createK8sRunEnvFactory(k8sCfg: K8sEnvConfig, seats: {
78
80
  imageOf(sessionId: string): string | undefined;
81
+ /** 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
82
+ * `sema.io/run` label carries; `sandbox-git-plane.ts` `sandboxBrokerSeatOf` over the boot's plane reading and the run row's identity);
83
+ * absent seat / `undefined` answer ⇒ the pod carries no broker. */
84
+ brokerSeatOf?(runId: string | undefined): Promise<K8sEnvConfig["sandboxBroker"]>;
79
85
  /** The session store's by-id head read (`SessionStore.headOf`, picked once by `headReadOf`); the occupant is its live-occupant projection
80
86
  * (`sessionFaceOfHead`). Absent ⇔ the store has none, which `assertWorkspaceSpaceReadable` refuses together with `space` (embeddings that
81
87
  * wire another lane pass none). */
@@ -84,6 +90,17 @@ export declare function createK8sRunEnvFactory(k8sCfg: K8sEnvConfig, seats: {
84
90
  * or without a store backend — the latter `assertWorkspaceSpaceReadable` refuses together with `space`. */
85
91
  spaceCommits(): SpaceCommitRows | undefined;
86
92
  }, deps?: K8sEnvDeps): ReturnType<typeof k8sExecutionEnvFactory>;
93
+ /**
94
+ * k8s 车道沙箱秘密座的**起服前提**(装配后的真门,第 5 段 await)。法只有一条:**秘密座非空(`K8S_SANDBOX_ENV` 点到了本进程环境里有的键)⇒
95
+ * 问一次 SelfSubjectAccessReview(env 座那一行动词 {@link SANDBOX_SECRET_SEAT_VERBS}`.env`,沙箱命名空间)** —— 只问这一座自己发的调用,
96
+ * 不问租约座的 `patch`(那一座由凭据平面的判官在它的前提都成立时另问,见 `readSandboxGitPlane`):
97
+ * · 答了「不许」(任一动词)⇒ 拒启,一行拒句点名缺的动词、命名空间、Role 规则原文与出路(`[sandbox-secret-rbac]`)—— 不等第一个 run 才失败;
98
+ * · 问不到(请求失败 / 审查本身被拒 / 答复没有 `status.allowed`)⇒ 照常起服:F 类兜底口 `server.sandbox-secret.seat-unverified` 计数 + 一行
99
+ * `sandbox_secret_seat_unverified`。放行的最坏后果:RBAC 真缺时第一个要建 Secret 的 run 连接即拒(`auth_failed`,pod 被删)—— 秘密值仍然
100
+ * 只经 Secret,不回落明文;
101
+ * · 秘密座空 ⇒ 一个请求都不发(没有 Secret 要建)。
102
+ */
103
+ export declare function ensureSandboxSecretSeat(k8sCfg: K8sEnvConfig, logger: Pick<Logger, "warn">, deps?: K8sEnvDeps, read?: () => Promise<SandboxSecretSeat>): Promise<void>;
87
104
  /**
88
105
  * k8s 工作区空间的**起服前提**(装配后的真门;同段先例 = `assertDeviceLaneWired`)。法只有一条:
89
106
  * **生效策略 `space`(事实 `workspaceSpace`)∧ {@link createK8sRunEnvFactory} 的两只座缺一 ⇒ 拒启。**
@@ -119,6 +136,9 @@ export interface ExecutionEnvCtx {
119
136
  spaceCommits?: () => SpaceCommitRows | undefined;
120
137
  /** k8s 工作区空间的住客读:会话店的按 id 读(`headReadOf(sessionStore)`,第 5 段判一次、与起服门同一只)。不传 ⇒ 同上,不开、响亮。 */
121
138
  headOf?: HeadRead;
139
+ /** run 店的按 id 读(成员凭据代理座读 run 行上受理时记下的身份元组 `RunRecord.identity`)。取值闭包:run 店第 7 段才绑(`late-binds.ts`),
140
+ * 平面开着时每个 run 的 pod 建起前求值一次。不传 / 答 `undefined`(没有存储后端)⇒ 读不到身份 ⇒ 不挂代理。 */
141
+ runRows?: () => SandboxGitRunRows | undefined;
122
142
  }
123
143
  export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
124
144
  perTaskImage: PerTaskImageRegistry;
@@ -134,5 +154,8 @@ export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
134
154
  sendUserFileTaskEnvs: TaskEnvRegistry | undefined;
135
155
  lspManager: import("@sema-agent/core").NodeLspManager | import("../lsp/manager.js").E2bLspManager | undefined;
136
156
  spaceReclaimer: SpaceReclaimer | undefined;
157
+ sandboxSecretSeatGate: (() => Promise<void>) | undefined;
158
+ sandboxPodReaper: SandboxPodReaper | undefined;
159
+ readSandboxGitPlane: () => Promise<SandboxGitPlane>;
137
160
  };
138
161
  //# sourceMappingURL=execution-env.d.ts.map
@@ -11,7 +11,11 @@ import { adbExecutionEnvFactory } from "../plugins/remote-env-adb.js";
11
11
  import { deviceExecutionEnvFactory } from "../plugins/remote-env-device.js";
12
12
  import { e2bExecutionEnvFactory } from "../plugins/remote-env-e2b.js";
13
13
  import { hostExecutionEnvFactory, RemoteHostExecutionEnv } from "../plugins/remote-env-host.js";
14
- import { boundedStoreCall, checkSandboxEgressPolicy, createSpaceReclaimer, K8S_SANDBOX_NAMESPACE_DEFAULT, k8sExecutionEnvFactory, rpcTimeoutMsOf } from "../plugins/remote-env-k8s.js";
14
+ import { boundedStoreCall, checkSandboxEgressPolicy, createSandboxPodApi, createSpaceReclaimer, K8S_SANDBOX_NAMESPACE_DEFAULT, k8sExecutionEnvFactory, readSandboxSecretSeat, rpcTimeoutMsOf } from "../plugins/remote-env-k8s.js";
15
+ import { podSpecPatchBrokerRefusal, SANDBOX_SECRET_SEAT_VERBS } from "../plugins/k8s-exec-protocol.js";
16
+ import { judgeSandboxGitPlane, runIdentitySourceOf, sandboxBrokerSeatOf, sandboxGitPlaneAsksSecretSeat, sandboxGitPlanePremisesOf } from "../sandbox-git-plane.js";
17
+ import { createSandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
18
+ import { recordFailOpen } from "../observability/fail-open.js";
15
19
  import { createRunWorkspaceSnapshotSeat } from "../fleet/workspace-snapshot-residual.js";
16
20
  import { createSpaceCommitFence } from "../workspace-space-commit.js";
17
21
  import { localDockerExecutionEnvFactory } from "../plugins/remote-env-local-docker.js";
@@ -58,7 +62,12 @@ function forwardedSandboxEnvKeys(remoteExec) {
58
62
  }
59
63
  }
60
64
  function buildSandboxLaneEnv(config, explicit) {
61
- const merged = { ...derivePkgSourceEnv(config.sandboxPkgSource, config.sandboxPkgSourceCustom), ...(explicit ?? {}) };
65
+ const secretEnv = { ...(explicit ?? {}) };
66
+ const env = Object.fromEntries(Object.entries(derivePkgSourceEnv(config.sandboxPkgSource, config.sandboxPkgSourceCustom)).filter(([name]) => !Object.hasOwn(secretEnv, name)));
67
+ return { ...(Object.keys(env).length > 0 ? { env } : {}), ...(Object.keys(secretEnv).length > 0 ? { secretEnv } : {}) };
68
+ }
69
+ function sandboxLaneEnvMerged(lane) {
70
+ const merged = { ...(lane.env ?? {}), ...(lane.secretEnv ?? {}) };
62
71
  return Object.keys(merged).length > 0 ? merged : undefined;
63
72
  }
64
73
  const MAX_CWD_SESSIONS = 4096;
@@ -118,6 +127,8 @@ export function createK8sRunEnvFactory(k8sCfg, seats, deps) {
118
127
  const child = ctx.placementRootSessionId !== ctx.sessionId;
119
128
  const s3Snapshot = child && k8sCfg.s3Snapshot !== undefined ? spaceOff(k8sCfg.s3Snapshot) : k8sCfg.s3Snapshot;
120
129
  const spaceOccupant = child ? undefined : await occupantOf(seats, ctx.sessionId, ctx.taskId, rpcTimeoutMsOf(k8sCfg));
130
+ const runId = !child && ctx.taskId !== undefined ? ctx.taskId : undefined;
131
+ const sandboxBroker = await seats.brokerSeatOf?.(runId);
121
132
  const { cache, ...deploymentCfg } = k8sCfg;
122
133
  return k8sExecutionEnvFactory({
123
134
  ...deploymentCfg,
@@ -125,6 +136,8 @@ export function createK8sRunEnvFactory(k8sCfg, seats, deps) {
125
136
  ...(s3Snapshot !== undefined ? { s3Snapshot } : {}),
126
137
  image: seats.imageOf(ctx.sessionId) ?? k8sCfg.image,
127
138
  ...(spaceOccupant !== undefined ? { spaceOccupant } : {}),
139
+ ...(runId !== undefined ? { runId } : {}),
140
+ ...(sandboxBroker !== undefined ? { sandboxBroker } : {}),
128
141
  onWorkspaceSnapshot,
129
142
  }, deps)(ctx);
130
143
  };
@@ -156,6 +169,34 @@ function spaceOff(store) {
156
169
  const { space: _off, ...parkOnly } = store;
157
170
  return parkOnly;
158
171
  }
172
+ export async function ensureSandboxSecretSeat(k8sCfg, logger, deps, read = () => readSandboxSecretSeat(k8sCfg, "env", deps)) {
173
+ const names = Object.keys(k8sCfg.podSecretEnv ?? {});
174
+ if (names.length === 0)
175
+ return;
176
+ const namespace = k8sCfg.namespace ?? K8S_SANDBOX_NAMESPACE_DEFAULT;
177
+ const seat = await read();
178
+ switch (seat.state) {
179
+ case "granted":
180
+ return;
181
+ case "denied":
182
+ throw new Error(`K8S_SANDBOX_ENV forwards ${names.length} variable(s) into the k8s sandbox, but this service account may not ${seat.verbs.join(", ")} secrets ` +
183
+ `in namespace ${namespace} [sandbox-secret-rbac]: the forwarded values reach a sandbox pod only through that pod's own Secret (the pod spec carries ` +
184
+ `references, never the values). Add to the sandbox Role in ${namespace}: \`- apiGroups: [""] resources: [secrets] verbs: [${SANDBOX_SECRET_SEAT_VERBS.env.join(", ")}]\` ` +
185
+ "(or unset K8S_SANDBOX_ENV), and restart");
186
+ case "unverified":
187
+ recordFailOpen("server.sandbox-secret.seat-unverified", `namespace=${namespace} ${seat.detail}`);
188
+ logger.warn("sandbox_secret_seat_unverified", {
189
+ namespace,
190
+ detail: seat.detail,
191
+ 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`,
192
+ });
193
+ return;
194
+ default: {
195
+ const unreachable = seat;
196
+ throw new Error(`unhandled sandbox secret seat reading: ${String(unreachable.state)}`);
197
+ }
198
+ }
199
+ }
159
200
  export function assertWorkspaceSpaceReadable(space, seats, cache, sessionStoreLabel) {
160
201
  if (space.available && persistNeedsOccupant(space.policy.persist)) {
161
202
  const missing = seats.headOf === undefined ? `the session store (${sessionStoreLabel}) has no by-id occupant read (headOf)`
@@ -207,6 +248,10 @@ export function createExecutionEnv(ctx) {
207
248
  const { perSessionInheritEnv, setSessionInheritEnv } = sessionScoped;
208
249
  let executionEnvFactory;
209
250
  let spaceReclaimer;
251
+ let sandboxSecretSeatGate;
252
+ let sandboxPodReaper;
253
+ const planeCell = {};
254
+ let readSandboxGitPlane = async () => (planeCell.current = judgeSandboxGitPlane(sandboxGitPlanePremisesOf(config, {})));
210
255
  let worktreeReap;
211
256
  if (config.remoteExec?.provider === "e2b") {
212
257
  executionEnvFactory = e2bExecutionEnvFactory({
@@ -217,7 +262,7 @@ export function createExecutionEnv(ctx) {
217
262
  ...(config.remoteExec.livenessMs != null ? { livenessMs: config.remoteExec.livenessMs } : {}),
218
263
  ...(config.remoteExec.allowInternetAccess != null ? { allowInternetAccess: config.remoteExec.allowInternetAccess } : {}),
219
264
  ...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
220
- ...((env) => (env !== undefined ? { sandboxEnv: env } : {}))(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv)),
265
+ ...((env) => (env !== undefined ? { sandboxEnv: env } : {}))(sandboxLaneEnvMerged(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv))),
221
266
  logger,
222
267
  metrics,
223
268
  });
@@ -243,11 +288,29 @@ export function createExecutionEnv(ctx) {
243
288
  ...((f) => (f.available ? { cache: f.cache } : {}))(facts.workspaceCache(config)),
244
289
  ...(config.remoteExec.workspaceSnapshotExclude ? { snapshotExclude: config.remoteExec.workspaceSnapshotExclude } : {}),
245
290
  logger,
246
- ...((env) => (env !== undefined ? { podEnv: env } : {}))(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv)),
291
+ ...((lane) => ({ ...(lane.env !== undefined ? { podEnv: lane.env } : {}), ...(lane.secretEnv !== undefined ? { podSecretEnv: lane.secretEnv } : {}) }))(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv)),
247
292
  };
248
- executionEnvFactory = createK8sRunEnvFactory(k8sCfg, { imageOf: (sid) => perTaskImage.get(sid), headOf: ctx.headOf, spaceCommits: ctx.spaceCommits ?? (() => undefined) });
293
+ const brokerImage = config.remoteExec.brokerImage;
294
+ executionEnvFactory = createK8sRunEnvFactory(k8sCfg, {
295
+ imageOf: (sid) => perTaskImage.get(sid),
296
+ headOf: ctx.headOf,
297
+ spaceCommits: ctx.spaceCommits ?? (() => undefined),
298
+ brokerSeatOf: async (runId) => {
299
+ const plane = planeCell.current;
300
+ if (plane === undefined || plane.state !== "on")
301
+ return undefined;
302
+ return sandboxBrokerSeatOf(plane, brokerImage, await runIdentitySourceOf(runId, ctx.runRows?.(), logger));
303
+ },
304
+ });
249
305
  if (k8sCfg.cache !== undefined || k8sCfg.s3Snapshot !== undefined)
250
306
  spaceReclaimer = createSpaceReclaimer(k8sCfg);
307
+ sandboxSecretSeatGate = () => ensureSandboxSecretSeat(k8sCfg, logger, undefined, () => readSandboxSecretSeat(k8sCfg, "env"));
308
+ readSandboxGitPlane = async () => {
309
+ const premises = sandboxGitPlanePremisesOf(config, { ...((r) => (r !== undefined ? { patchRefusal: r } : {}))(podSpecPatchBrokerRefusal(k8sCfg.podSpecPatch)) });
310
+ const secretSeat = sandboxGitPlaneAsksSecretSeat(premises) ? await readSandboxSecretSeat(k8sCfg, "lease") : undefined;
311
+ return (planeCell.current = judgeSandboxGitPlane({ ...premises, secretSeat }));
312
+ };
313
+ sandboxPodReaper = createSandboxPodReaper({ api: createSandboxPodApi(k8sCfg), logger, metrics });
251
314
  void checkSandboxEgressPolicy(k8sCfg)
252
315
  .then((gaps) => sayEgressPolicyReading(gaps, logger, k8sCfg.namespace ?? K8S_SANDBOX_NAMESPACE_DEFAULT))
253
316
  .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" }));
@@ -446,7 +509,7 @@ export function createExecutionEnv(ctx) {
446
509
  perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv, setSessionInheritEnv,
447
510
  fenceSessionOwner: sessionScoped.fenceSessionOwner,
448
511
  dropSessionScoped: sessionScoped.dropSession,
449
- executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer,
512
+ executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer, sandboxSecretSeatGate, sandboxPodReaper, readSandboxGitPlane,
450
513
  };
451
514
  }
452
515
  //# sourceMappingURL=execution-env.js.map
@@ -31,7 +31,7 @@ export function createOrgMemoryAdmissionWiring(opts) {
31
31
  fetchSection: (principal) => fetchPrincipalOrgMemory({ baseUrl: centerLeg.baseUrl, credential }, principal, fetch, centerLeg.worker),
32
32
  grantTtlMs: config.memoryOrgGrantTtlMs,
33
33
  unavailableBackoffMs: config.memoryOrgUnavailableBackoffMs,
34
- }));
34
+ })).now;
35
35
  return { lookup: (principal) => directoryNow().lookup(principal) };
36
36
  })()
37
37
  : staticJson !== undefined
@@ -25,6 +25,7 @@ import type { FileHistoryErasureFace } from "../file-history-capture.js";
25
25
  import type { ApprovalNonceStore, CheckpointStoreFull, CostQuotaStore, ImageBake, RateLimiterStore, ServiceWorkflowJournalStore, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
26
26
  import type { ToolApprovalCoordinator } from "../tool-approval.js";
27
27
  import type { ParkCommitWatch } from "../plugins/park-commit-watch.js";
28
+ import { type SandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
28
29
  import { type AuditRotationPort } from "../approval-ask-retention.js";
29
30
  /** [ref] / [ref]§五(sema-comms `audits/failopen-governance-176.md` §7 "Z 类 1 条"):default consecutive-failure
30
31
  * count before a reaper leg's swallowed error gets a `warn` line. The ~15 `.catch(() => undefined)` sites
@@ -113,6 +114,8 @@ export interface ReapersCtx {
113
114
  approvalAskAudit: AuditRotationPort | undefined;
114
115
  /** 晚绑(server 造出来才有)——见文件头「位置即契约」①。 */
115
116
  getRunDenySweep: () => ((now: number) => Promise<void>) | undefined;
117
+ /** k8s 车道的沙箱 Pod reaper(`plugins/k8s-pod-reaper.ts`;第 5 段与 run 工厂同一份 k8s 配置铸)。缺席 = 非 k8s 车道 ⇒ 本腿零调用。 */
118
+ sandboxPodReaper?: SandboxPodReaper;
116
119
  }
117
120
  /** core 5.48 `reapDurableAgents` 的 `deps.sessions` 面(结构形,只列本仓要填的三格)。 */
118
121
  export interface ReapSessionsFacade {
@@ -4,6 +4,7 @@ import { CostQuota } from "../observability/cost-quota.js";
4
4
  import { isAdoptionQuiescing, trackMaintenanceLeg } from "../adoption/quiesce.js";
5
5
  import { createApprovalReconciler } from "../approval-reconciler.js";
6
6
  import { createE2bOrphanSandboxProvider, reclaimOrphanE2bSandboxes, E2B_ORPHAN_GRACE_MS } from "../plugins/e2b-orphan-reclaim.js";
7
+ import { SANDBOX_POD_REAP_PERIOD_MS } from "../plugins/k8s-pod-reaper.js";
7
8
  import { APPROVAL_ASK_RETENTION_PASS_MS, runApprovalAskRetentionPass } from "../approval-ask-retention.js";
8
9
  export const REAPER_FAILURE_WARN_THRESHOLD = 5;
9
10
  export function createThrottledReaperCatch(name, logger, threshold = REAPER_FAILURE_WARN_THRESHOLD) {
@@ -57,7 +58,7 @@ export function createPlacementAwareReapSessions(deps) {
57
58
  };
58
59
  }
59
60
  export function startReapers(ctx) {
60
- const { config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore, parkCommits, rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap, workflowNotifyGate, workflowRecoverOpts, workflowJournalStore, sqlWorkflowRunStore, workflowNotifyJournal, rosterStore, backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore, getRunDenySweep, userInputLedger, approvalAskAudit, } = ctx;
61
+ const { config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore, parkCommits, rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap, workflowNotifyGate, workflowRecoverOpts, workflowJournalStore, sqlWorkflowRunStore, workflowNotifyJournal, rosterStore, backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore, getRunDenySweep, userInputLedger, approvalAskAudit, sandboxPodReaper, } = ctx;
61
62
  const leg = (p) => {
62
63
  if (p !== undefined)
63
64
  trackMaintenanceLeg(p);
@@ -73,6 +74,8 @@ export function startReapers(ctx) {
73
74
  : undefined;
74
75
  let e2bOrphanLastRunMs = Date.now() - E2B_ORPHAN_GRACE_MS;
75
76
  let e2bOrphanInFlight = false;
77
+ let sandboxPodReapLastRunMs = Date.now() - SANDBOX_POD_REAP_PERIOD_MS;
78
+ let sandboxPodReapInFlight = false;
76
79
  let wfRunReapInFlight = false;
77
80
  let attachmentSweepInFlight = false;
78
81
  let bgAgentReapInFlight = false;
@@ -80,6 +83,7 @@ export function startReapers(ctx) {
80
83
  const probeClockSkewGuard = createThrottledReaperCatch("probe_clock_skew", logger);
81
84
  const runsReapStaleGuard = createThrottledReaperCatch("runs_reap_stale", logger);
82
85
  const e2bOrphanGuard = createThrottledReaperCatch("e2b_orphan_reclaim", logger);
86
+ const sandboxPodReapGuard = createThrottledReaperCatch("sandbox_pod_reap", logger);
83
87
  const checkpointsReapExpiredGuard = createThrottledReaperCatch("checkpoints_reap_expired", logger);
84
88
  const approvalNonceReapExpiredGuard = createThrottledReaperCatch("approval_nonce_reap_expired", logger);
85
89
  const runsFailSuspendedExpiredCheckpointGuard = createThrottledReaperCatch("runs_fail_suspended_expired_checkpoint", logger);
@@ -366,6 +370,16 @@ export function startReapers(ctx) {
366
370
  e2bOrphanInFlight = false;
367
371
  }));
368
372
  }
373
+ if (sandboxPodReaper && Date.now() - sandboxPodReapLastRunMs >= SANDBOX_POD_REAP_PERIOD_MS && !sandboxPodReapInFlight) {
374
+ sandboxPodReapInFlight = true;
375
+ sandboxPodReapLastRunMs = Date.now();
376
+ leg(sandboxPodReaper
377
+ .reapOnce(runStore)
378
+ .then(() => sandboxPodReapGuard.onSuccess(), sandboxPodReapGuard.onError)
379
+ .finally(() => {
380
+ sandboxPodReapInFlight = false;
381
+ }));
382
+ }
369
383
  }, config.reapIntervalSec * 1000);
370
384
  reaper.unref?.();
371
385
  return reaper;
@@ -2,6 +2,7 @@ import { type MemoryBackend } from "@sema-agent/core";
2
2
  import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
3
3
  import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
4
4
  import { selectScenario } from "../capabilities/scenarios.js";
5
+ import { type LoadedSkill } from "../capabilities/skills.js";
5
6
  import type { ServiceConfig } from "../config.js";
6
7
  import { type LiveQuestionFace } from "../deployment-governance.js";
7
8
  import { FleetEventBus } from "../fleet/fleet-bus.js";
@@ -83,6 +84,9 @@ export interface ResolveSpecCtx {
83
84
  /** [ref] 件C:本部署已校验的锁集(boot/governance-seams.ts 的产物,与 `RunnerDeps.lockedConfig`
84
85
  * 同源一次解析)。空集 = 无锁 = 现行为。本域只用它做**同步拒面**(§4.3),不做第二次裁决。 */
85
86
  lockedKeys: ReadonlySet<import("@sema-agent/core").LockedKey>;
87
+ /** 本部署**装载簿**(stage-07 的出厂技能 + 配置源覆盖之后的终表,场景工厂读的同一份)。本装配器只拿它答「场景交来的这只技能来自哪里」
88
+ * (run 名册 `wiring_manifest.skills[].source`,`buildRunSkills`);名字在装载簿里唯一(装载器同名即抛、配置源按名覆盖)。 */
89
+ loadedSkills: readonly LoadedSkill[];
86
90
  }
87
91
  export declare function createResolveSpec(ctx: ResolveSpecCtx): ServiceDeps["resolveSpec"];
88
92
  export {};
@@ -5,7 +5,8 @@ import { cappedCeiling } from "../budget.js";
5
5
  import { recordFailOpen } from "../observability/fail-open.js";
6
6
  import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
7
7
  import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
8
- import { buildExcludeToolsSeat, buildPromptSeats, createScenarioGate, mergeUserSkills, selectScenario } from "../capabilities/scenarios.js";
8
+ import { buildExcludeToolsSeat, buildPromptSeats, buildRunSkills, createScenarioGate, selectScenario } from "../capabilities/scenarios.js";
9
+ import { stampRunSkills } from "../capabilities/skills.js";
9
10
  import { stampHandsLane, toolPolicyForHands } from "../capabilities/hands-lane.js";
10
11
  import { applyLongtailDefer } from "../capabilities/tool-defer.js";
11
12
  import { assertGuardPatternsUsable, buildApprovalGrantNotice, buildOnlySensitiveBaselineWarning, createApprovalBaselinePolicy, createDeploymentGovernanceInputs } from "../deployment-governance.js";
@@ -16,7 +17,7 @@ import { createSettingsHooksRegistration, settingsHooksBudgetMs } from "../hooks
16
17
  import { HOST_POSTURE_REFUSED_CODE, taskInheritEnvRefusal, taskWriteFaceRefusal } from "../hosted-posture.js";
17
18
  import { headerStr } from "../http/principal-gate.js";
18
19
  import { explicitOperatorOk } from "../http/server.js";
19
- import { isAtMentionable, matchCatalogModel, modelSupportsImages, refusedCatalogEntry, resolveTaskModel, selectableCatalog, terminalCatalogName } from "../model-select.js";
20
+ import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, resolveTaskModel, selectableCatalog, terminalCatalogName } from "../model-select.js";
20
21
  import { echoModelRef, modelUnavailableMessage } from "../model-entry-refusal.js";
21
22
  import { PerTaskImageRegistry, resolveSandboxImageRef } from "../per-task-image.js";
22
23
  import { remoteScratchpadDirFor } from "../plugins/remote-scratchpad.js";
@@ -30,13 +31,14 @@ import { HttpError, createCodedHttpError, encodeCheckpointScope } from "../secur
30
31
  import { headReadOf, sessionFaceOfHead } from "../session-incarnation.js";
31
32
  import { memorySpecForRequest } from "../memory-scope.js";
32
33
  import { createMemoryProjectLayers } from "../memory-project-layer.js";
33
- import { inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
34
+ import { inheritEnvFacts, inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
34
35
  import { a2aForScenario, mcpForScenario } from "../web-admin-client/facade.js";
35
- import { normalizeAttachments, normalizeResilience, normalizeResumeAtMode, normalizeSuggestNextPrompts, promptProfileFromBody, resolveTaskLimits, retainBackgroundProcessesFromBody, taskAgentsSpecFragment, toolMaterializeStrategyFromBody, allowToolsSpecFragment, toolNameListFromBody } from "../spec-fields.js";
36
+ import { normalizeAttachments, normalizeResilience, normalizeResumeAtMode, normalizeSuggestNextPrompts, promptLeanSpecFragment, resolveTaskLimits, retainBackgroundProcessesFromBody, taskAgentsSpecFragment, toolMaterializeStrategyFromBody, allowToolsSpecFragment, toolNameListFromBody } from "../spec-fields.js";
36
37
  import { isAbsoluteHostPath } from "@sema-agent/settings-schema";
37
38
  import { effectiveHostWorkspace, parseAdditionalDirectories, satisfiedByProcessCwd, shellEnvMismatchCount } from "../task-cwd.js";
38
39
  import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
39
- import { assertRequestA2aPeerUrls, assertRequestA2aUnlocked, resolveRequestA2a } from "../task-a2a.js";
40
+ import { assertRequestA2aEntries, resolveRequestA2a, snapshotRequestA2a } from "../task-a2a.js";
41
+ import { assertRequestImageUrls } from "../request-image-urls.js";
40
42
  import { assertRequestMcpEntries, assertRequestMcpUnlocked, resolveRequestMcp } from "../task-mcp.js";
41
43
  import { storedToolAxesRereadM19 } from "../stored-request-tool-axes-m19.js";
42
44
  import { storedHooksRereadM20 } from "../stored-request-hooks-m20.js";
@@ -52,7 +54,8 @@ import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
52
54
  import { withUnattendedLeg } from "../unattended-permission-mode.js";
53
55
  import { fieldConflictFacts } from "../field-conflict.js";
54
56
  export function createResolveSpec(ctx) {
55
- const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, memorySyncRunner, durableEnabled, approvalExemptionStore, checkpointStore, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
57
+ const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, memorySyncRunner, durableEnabled, approvalExemptionStore, checkpointStore, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, loadedSkills, } = ctx;
58
+ const deploymentSkillSources = new Map(loadedSkills.map((k) => [k.spec.name, k.source]));
56
59
  const scenarioGate = createScenarioGate({ config, scenarios, principalCaps, logger }, "enforce");
57
60
  const sessionHeadOf = headReadOf(ownerAware);
58
61
  assertGuardPatternsUsable(config);
@@ -99,17 +102,17 @@ export function createResolveSpec(ctx) {
99
102
  {
100
103
  const inheritEnvIssue = inheritEnvFieldIssue(body);
101
104
  if (inheritEnvIssue)
102
- throw new HttpError(400, inheritEnvIssue, { code: "request.field_invalid" });
105
+ throw new HttpError(400, inheritEnvIssue.message, { code: "request.field_invalid", extra: inheritEnvIssue.facts });
103
106
  if (body.inheritEnv === "all") {
104
107
  const lane = executionLaneOf(config.remoteExec?.provider);
105
108
  if (lane !== "host") {
106
- throw new HttpError(400, `inheritEnv:"all" applies to the host lane only (this deployment runs ${lane}) — only the host lane builds the model-driven shell's environment from this machine's own, so the declaration has no effect here; omit it (or send "scrub", the default, which every lane accepts)`, { code: "request.field_invalid" });
109
+ throw new HttpError(400, `inheritEnv:"all" applies to the host lane only (this deployment runs ${lane}) — only the host lane builds the model-driven shell's environment from this machine's own, so the declaration has no effect here; omit it (or send "scrub", the default, which every lane accepts)`, { code: "request.field_invalid", extra: inheritEnvFacts("lane_not_host") });
107
110
  }
108
111
  const why = taskInheritEnvRefusal(config);
109
112
  if (why !== undefined)
110
113
  throw new HttpError(422, why, { code: HOST_POSTURE_REFUSED_CODE });
111
114
  if (!auth?.sessionId)
112
- throw new HttpError(400, `inheritEnv:"all" requires a session-resolving deployment (no authorizer session) — the host shell's environment is bound per session`, { code: "request.field_invalid" });
115
+ throw new HttpError(400, `inheritEnv:"all" requires a session-resolving deployment (no authorizer session) — the host shell's environment is bound per session`, { code: "request.field_invalid", extra: inheritEnvFacts("no_authorizer_session") });
113
116
  }
114
117
  }
115
118
  {
@@ -139,9 +142,12 @@ export function createResolveSpec(ctx) {
139
142
  }
140
143
  }
141
144
  }
142
- assertRequestA2aUnlocked(body.a2aPeers, lockedKeys);
145
+ const storedToolAxes = opts?.leg === "fresh" ? undefined : storedToolAxesRereadM19(new Date().toISOString().slice(0, 10));
146
+ const a2aPeers = snapshotRequestA2a(body.a2aPeers, { reread: storedToolAxes !== undefined });
147
+ if (opts?.leg === "fresh")
148
+ assertRequestA2aEntries(a2aPeers);
143
149
  if (opts?.leg === "fresh")
144
- assertRequestA2aPeerUrls(body.a2aPeers);
150
+ assertRequestImageUrls(body.images);
145
151
  const gated = await gateScenarioAndAppend(body, auth, opts);
146
152
  const effMode = effectivePermissionMode(body, gated.parsedSettings.settings);
147
153
  if (effMode !== undefined && modePolicyOf(effMode).writeFace === "open") {
@@ -152,7 +158,8 @@ export function createResolveSpec(ctx) {
152
158
  const lane = bindSettingsCwdEnvAndModel(body, auth, gated, opts);
153
159
  const anchors = await resolveHistoryAnchors(body, auth);
154
160
  const projectKey = memoryEngine ? await memoryProjectLayers.prepare(config, auth?.memoryScope, realTaskRootOf(auth)) : undefined;
155
- const spec = assembleSpecLiteral(body, auth, req, opts, { ...gated, ...lane, ...anchors, projectKey }, effMode);
161
+ const runSkills = buildRunSkills({ scenario: gated.cap.skills, request: body.skills, disable: body.disableSkills === true, deploymentSources: deploymentSkillSources }, logger);
162
+ const spec = assembleSpecLiteral(body, auth, req, opts, { ...gated, ...lane, ...anchors, projectKey, a2aPeers, storedToolAxes, runSkills }, effMode);
156
163
  const folded = await foldGovernanceAndSettings(body, auth, spec, gated);
157
164
  const final = await applyImageFactsAndRouting(body, auth, folded.governed, {
158
165
  scenarioName: gated.scenarioName,
@@ -160,7 +167,7 @@ export function createResolveSpec(ctx) {
160
167
  hostSemanticsLane: folded.hostSemanticsLane,
161
168
  });
162
169
  const stamped = stampHandsLane(final, gated.hands);
163
- return stamped;
170
+ return stampRunSkills(stamped, runSkills.roster);
164
171
  };
165
172
  const gateScenarioAndAppend = async (body, auth, opts) => {
166
173
  const scenarioName = await scenarioGate(auth?.principal, body.scenario);
@@ -308,10 +315,6 @@ export function createResolveSpec(ctx) {
308
315
  metrics.inc("task_model_unknown_fallback_total");
309
316
  logger.warn("task_model_unknown_fallback", { requested: echoModelRef(picked.unknownExplicit), fallback: picked.model, sessionId: auth?.sessionId ?? null });
310
317
  }
311
- if (Array.isArray(body.images) && body.images.length > 0 && !modelSupportsImages(wireCatalog[picked.model])) {
312
- metrics.inc("images_omitted_total", { model: picked.model });
313
- logger.warn("images_omitted_no_vision_model", { model: picked.model, count: body.images.length });
314
- }
315
318
  return { taskHooks, additionalDirectories, additionalReadDirectories, wireCatalog, picked };
316
319
  };
317
320
  const NO_ANCHOR_HINT = "no rewind anchor is recorded under that handle (a prompt that carried attachments records none when its attachment frames were interrupted or their count did not match)";
@@ -355,8 +358,7 @@ export function createResolveSpec(ctx) {
355
358
  return deriveAskEffective(askForm.form, "unresolved") === "human_reachable";
356
359
  };
357
360
  const assembleSpecLiteral = (body, auth, req, opts, parts, effMode) => {
358
- const { scenarioName, cap, hands, acceptedAppend, parsedSettings, attachmentNotice, taskHooks, additionalDirectories, additionalReadDirectories, wireCatalog, picked, resumeAtEntryId, rewindFilesToEntryId, s4DefaultScopes, projectKey, } = parts;
359
- const storedToolAxes = opts?.leg === "fresh" ? undefined : storedToolAxesRereadM19(new Date().toISOString().slice(0, 10));
361
+ const { scenarioName, cap, hands, acceptedAppend, parsedSettings, attachmentNotice, taskHooks, additionalDirectories, additionalReadDirectories, wireCatalog, picked, resumeAtEntryId, rewindFilesToEntryId, s4DefaultScopes, projectKey, a2aPeers, storedToolAxes, runSkills, } = parts;
360
362
  const spec = {
361
363
  objective: attachmentNotice ? `${picked.cleanedObjective}\n\n${attachmentNotice}` : picked.cleanedObjective,
362
364
  ...buildPromptSeats(scenarioName, cap.prompt, typeof body.systemPrompt === "string" ? body.systemPrompt : undefined),
@@ -401,7 +403,7 @@ export function createResolveSpec(ctx) {
401
403
  ...buildExcludeToolsSeat(cap.excludeTools, toolNameListFromBody(body.excludeTools)),
402
404
  ...allowToolsSpecFragment(body.allowTools),
403
405
  ...(() => { const v = toolNameListFromBody(body.deferTools); return v ? { deferTools: v } : {}; })(),
404
- ...(() => { const v = promptProfileFromBody(body.promptProfile); return v ? { promptProfile: v } : {}; })(),
406
+ ...promptLeanSpecFragment(body),
405
407
  ...(() => { const v = toolMaterializeStrategyFromBody(body.toolMaterializeStrategy); return v ? { toolMaterializeStrategy: v } : {}; })(),
406
408
  ...(typeof body.interactiveTools === "boolean" ? { interactiveTools: body.interactiveTools } : {}),
407
409
  ...(declaresInteractivePosture(body, req, opts) ? { interactionPosture: "interactive" } : {}),
@@ -485,12 +487,12 @@ export function createResolveSpec(ctx) {
485
487
  const merged = extra.length > 0 ? [...(base ?? []), ...extra] : base;
486
488
  return merged && config.toolDeferLongtail ? applyLongtailDefer(merged, true) : merged;
487
489
  })(cap.tools),
488
- skills: body.disableSkills === true ? [] : mergeUserSkills(cap.skills, body.skills, logger),
490
+ skills: runSkills.specs,
489
491
  ...(() => { const v = toolNameListFromBody(body.userInvokedSkills); return v ? { userInvokedSkills: v } : {}; })(),
490
492
  ...(parsedSettings.settings?.skillListingBudgetFraction !== undefined ? { skillListingBudgetFraction: parsedSettings.settings.skillListingBudgetFraction } : {}),
491
493
  ...(parsedSettings.settings?.skillListingMaxDescChars !== undefined ? { skillListingMaxDescChars: parsedSettings.settings.skillListingMaxDescChars } : {}),
492
494
  mcp: resolveRequestMcp(mcpForScenario(config.mcpServers, scenarioName), body.mcpServers, config, logger, opts?.onNotice !== undefined ? { sessionId: auth?.sessionId, emit: opts.onNotice } : undefined, storedToolAxes),
493
- a2a: resolveRequestA2a(a2aForScenario(config.a2aPeers, scenarioName), body.a2aPeers, config, logger, opts?.leg === "fresh" ? "fresh" : "resume", storedToolAxes),
495
+ a2a: resolveRequestA2a(a2aForScenario(config.a2aPeers, scenarioName), a2aPeers, config, logger, opts?.leg === "fresh" ? "fresh" : "resume", storedToolAxes),
494
496
  toolPolicy: toolPolicyForHands(hands, durableEnabled
495
497
  ? createApprovalBaselinePolicy(config, {
496
498
  kind: "durable",
@@ -20,6 +20,8 @@ import { storeFormRefusalInIncident } from "../memory-store-refusal.js";
20
20
  import { coreFileHistoryCaptureSeat } from "../file-history-capture.js";
21
21
  import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
22
22
  import { rewindFailureCode } from "../file-history-disclosure.js";
23
+ import { errorKindOf } from "../observability/err-text.js";
24
+ import { buildShownUrlsInText } from "../url-refusal.js";
23
25
  export function createRunnerDepsOnAsk(toolApproval) {
24
26
  if (!toolApproval)
25
27
  return undefined;
@@ -263,12 +265,12 @@ export function createRunnerDeps(ctx) {
263
265
  return;
264
266
  }
265
267
  if (ctx.phase === "mcp") {
266
- logger.warn("mcp_server_unavailable", { sessionId: ctx.sessionId, info: String(err) });
268
+ logger.warn("mcp_server_unavailable", { sessionId: ctx.sessionId, kind: errorKindOf(err), info: buildShownUrlsInText(String(err)) });
267
269
  metrics.inc("mcp_server_unavailable_total");
268
270
  return;
269
271
  }
270
272
  if (ctx.phase === "a2a") {
271
- logger.warn("a2a_peer_unavailable", { sessionId: ctx.sessionId, info: String(err) });
273
+ logger.warn("a2a_peer_unavailable", { sessionId: ctx.sessionId, kind: errorKindOf(err), info: buildShownUrlsInText(String(err)) });
272
274
  metrics.inc("a2a_peer_unavailable_total");
273
275
  return;
274
276
  }
@@ -18,7 +18,7 @@ export declare function runStage03(ctx: Awaited<ReturnType<typeof runStage02>>):
18
18
  rosterStore: import("@sema-agent/core").RosterStore | undefined;
19
19
  sessionCaptureRecordStore: import("@sema-agent/core").SessionCaptureRecordStore | undefined;
20
20
  sessionCaptureRecords: import("../index.js").SessionCaptureRecordSource | undefined;
21
- sessionStore: import("@sema-agent/core").SessionStore;
21
+ sessionStore: import("@sema-agent/core").SessionStore<import("@sema-agent/core").SessionStoreSummary, import("@sema-agent/core").SessionListQuery>;
22
22
  sqlEngine: import("../plugins/sql-driver.js").SqlEngineReading | undefined;
23
23
  storePosture: import("../store-posture.js").StorePosture;
24
24
  taskAttachmentStore: import("../plugins/task-attachment-store.js").TaskAttachmentStore | undefined;
@@ -15,7 +15,7 @@ export declare function runStage04(ctx: Awaited<ReturnType<typeof runStage03>>):
15
15
  fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
16
16
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
17
17
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
18
- fleetUsage: import("../fleet-client.js").FleetUsageAccumulator | undefined;
18
+ fleetUsage: import("../fleet-client.js").FleetUsageBins | undefined;
19
19
  memoryBundleFaces: import("../memory-bundle-engine.js").MemoryBundleFaces | undefined;
20
20
  memoryComplianceFaces: import("../memory-operator-faces.js").MemoryComplianceFaces | undefined;
21
21
  memoryOriginFaces: import("../memory-operator-faces.js").MemoryOriginFaces | undefined;
@@ -59,7 +59,7 @@ export declare function runStage04(ctx: Awaited<ReturnType<typeof runStage03>>):
59
59
  rosterStore: import("@sema-agent/core").RosterStore | undefined;
60
60
  sessionCaptureRecordStore: import("@sema-agent/core").SessionCaptureRecordStore | undefined;
61
61
  sessionCaptureRecords: import("../index.js").SessionCaptureRecordSource | undefined;
62
- sessionStore: import("@sema-agent/core").SessionStore;
62
+ sessionStore: import("@sema-agent/core").SessionStore<import("@sema-agent/core").SessionStoreSummary, import("@sema-agent/core").SessionListQuery>;
63
63
  sqlEngine: import("../plugins/sql-driver.js").SqlEngineReading | undefined;
64
64
  storePosture: import("../store-posture.js").StorePosture;
65
65
  taskAttachmentStore: import("../plugins/task-attachment-store.js").TaskAttachmentStore | undefined;