@sema-agent/server 7.148.0 → 7.150.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 (360) hide show
  1. package/MIGRATION.md +551 -9
  2. package/README.md +17 -2
  3. package/README.zh-CN.md +5 -1
  4. package/USAGE.md +126 -68
  5. package/dist/adoption/wire.d.ts +3 -3
  6. package/dist/approval-card.d.ts +33 -33
  7. package/dist/approval-gate-currency.d.ts +1 -1
  8. package/dist/approval-leg-origin.d.ts +33 -0
  9. package/dist/approval-leg-origin.js +4 -0
  10. package/dist/audit.d.ts +1 -1
  11. package/dist/audit.js +2 -2
  12. package/dist/auth-bridge.d.ts +9 -1
  13. package/dist/auth-bridge.js +4 -4
  14. package/dist/auth-keys.d.ts +10 -1
  15. package/dist/auth-keys.js +8 -0
  16. package/dist/background-session-reap.d.ts +1 -1
  17. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  18. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  19. package/dist/boot/execution-env.js +10 -11
  20. package/dist/boot/execution-lane.d.ts +6 -4
  21. package/dist/boot/execution-lane.js +3 -1
  22. package/dist/boot/org-delivery.d.ts +3 -2
  23. package/dist/boot/parked-revive-gate.d.ts +5 -5
  24. package/dist/boot/parked-revive-gate.js +2 -3
  25. package/dist/boot/resolve-spec.d.ts +4 -5
  26. package/dist/boot/resolve-spec.js +66 -28
  27. package/dist/boot/retention-lane.d.ts +1 -1
  28. package/dist/boot/runner-deps.js +3 -1
  29. package/dist/boot/session-faces.js +21 -21
  30. package/dist/boot/stage-01-config.d.ts +1 -0
  31. package/dist/boot/stage-01-config.js +4 -3
  32. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
  33. package/dist/boot/stage-02-web-admin-pull.js +10 -2
  34. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  35. package/dist/boot/stage-03-store-backend.js +4 -4
  36. package/dist/boot/stage-04-stores.d.ts +1 -0
  37. package/dist/boot/stage-05-execution-env.d.ts +1 -0
  38. package/dist/boot/stage-06-runners.d.ts +2 -1
  39. package/dist/boot/stage-06-runners.js +1 -2
  40. package/dist/boot/stage-07-capability-layer.d.ts +2 -1
  41. package/dist/boot/stage-07-capability-layer.js +1 -1
  42. package/dist/boot/stage-08-reapers.d.ts +2 -1
  43. package/dist/boot/stage-09-leader.d.ts +2 -1
  44. package/dist/boot/stage-10-http-server.d.ts +35 -2
  45. package/dist/boot/stage-10-http-server.js +9 -4
  46. package/dist/boot/stores.d.ts +3 -0
  47. package/dist/boot/stores.js +22 -19
  48. package/dist/boot/web-admin.d.ts +1 -1
  49. package/dist/capabilities/tool-defer.d.ts +1 -1
  50. package/dist/capability-facts.d.ts +70 -60
  51. package/dist/capability-facts.js +45 -49
  52. package/dist/card-settle-stored-scope.d.ts +56 -0
  53. package/dist/card-settle-stored-scope.js +27 -0
  54. package/dist/card-void.d.ts +7 -12
  55. package/dist/card-void.js +2 -7
  56. package/dist/config-catalog.d.ts +12 -0
  57. package/dist/config-catalog.js +37 -33
  58. package/dist/config-invariants.d.ts +5 -4
  59. package/dist/config-invariants.js +4 -4
  60. package/dist/config-types.d.ts +49 -16
  61. package/dist/config.d.ts +6 -5
  62. package/dist/config.js +35 -24
  63. package/dist/context-window-notice.d.ts +31 -0
  64. package/dist/context-window-notice.js +28 -0
  65. package/dist/deployment-governance.d.ts +7 -28
  66. package/dist/deployment-governance.js +1 -14
  67. package/dist/deployment-shape.d.ts +20 -11
  68. package/dist/deployment-shape.js +1 -0
  69. package/dist/device-ws-hub.d.ts +1 -0
  70. package/dist/device-ws-hub.js +64 -28
  71. package/dist/device-ws-protocol.d.ts +20 -6
  72. package/dist/device-ws-protocol.js +9 -2
  73. package/dist/engine-identity.d.ts +7 -1
  74. package/dist/engine-identity.js +5 -0
  75. package/dist/env-name-allowlist-knobs.d.ts +6 -5
  76. package/dist/env-name-allowlist-knobs.js +5 -0
  77. package/dist/execution-lane-caps.d.ts +20 -5
  78. package/dist/execution-lane-caps.js +18 -4
  79. package/dist/field-conflict.d.ts +2 -2
  80. package/dist/fleet/engine-signal.d.ts +14 -1
  81. package/dist/fleet/engine-signal.js +5 -1
  82. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  83. package/dist/fleet/serving-lease.d.ts +18 -3
  84. package/dist/fleet/serving-lease.js +81 -35
  85. package/dist/fleet-client.d.ts +13 -11
  86. package/dist/fleet-client.js +53 -33
  87. package/dist/history-anchor.js +1 -1
  88. package/dist/hitl-delivery.d.ts +1 -3
  89. package/dist/hitl-delivery.js +1 -1
  90. package/dist/hooks/command-spawn.d.ts +7 -2
  91. package/dist/hooks/command-spawn.js +13 -6
  92. package/dist/hooks/hook-runner.d.ts +5 -0
  93. package/dist/hooks/hook-runner.js +11 -7
  94. package/dist/http/active-run-conflict.d.ts +3 -7
  95. package/dist/http/active-run-conflict.js +2 -2
  96. package/dist/http/admission.js +5 -5
  97. package/dist/http/approval-sweeps.js +5 -3
  98. package/dist/http/caller-trust.d.ts +2 -2
  99. package/dist/http/caller-trust.js +3 -3
  100. package/dist/http/checkpoint-ctx.d.ts +18 -63
  101. package/dist/http/checkpoint-ctx.js +1 -24
  102. package/dist/http/claim-heartbeat.d.ts +6 -6
  103. package/dist/http/claim-heartbeat.js +6 -6
  104. package/dist/http/claimed-run.d.ts +16 -9
  105. package/dist/http/claimed-run.js +8 -0
  106. package/dist/http/dispatch.js +7 -5
  107. package/dist/http/durable-run-leg.d.ts +8 -1
  108. package/dist/http/durable-run-leg.js +34 -15
  109. package/dist/http/execution-context.d.ts +115 -14
  110. package/dist/http/execution-context.js +130 -40
  111. package/dist/http/owner-access.d.ts +246 -21
  112. package/dist/http/owner-access.js +186 -8
  113. package/dist/http/principal-gate.d.ts +18 -11
  114. package/dist/http/principal-gate.js +3 -1
  115. package/dist/http/readiness.d.ts +4 -2
  116. package/dist/http/resume-ctx-read.d.ts +14 -3
  117. package/dist/http/resume-ctx-read.js +24 -3
  118. package/dist/http/resume-legs.d.ts +35 -3
  119. package/dist/http/resume-legs.js +262 -106
  120. package/dist/http/route-ctx.d.ts +29 -11
  121. package/dist/http/routes/a2a-serve.js +17 -14
  122. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  123. package/dist/http/routes/approvals-assistant.js +11 -11
  124. package/dist/http/routes/capabilities.js +5 -4
  125. package/dist/http/routes/config-catalog.d.ts +1 -1
  126. package/dist/http/routes/diagnostics.d.ts +3 -1
  127. package/dist/http/routes/diagnostics.js +30 -5
  128. package/dist/http/routes/mcp-reconnect.js +2 -2
  129. package/dist/http/routes/memory-origin.d.ts +1 -1
  130. package/dist/http/routes/notify-wake.js +2 -3
  131. package/dist/http/routes/principal-inbox.d.ts +3 -1
  132. package/dist/http/routes/principal-inbox.js +3 -2
  133. package/dist/http/routes/projects.d.ts +1 -0
  134. package/dist/http/routes/projects.js +5 -3
  135. package/dist/http/routes/retention-ops.d.ts +1 -1
  136. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  137. package/dist/http/routes/run-permission-mode.js +2 -2
  138. package/dist/http/routes/runs.d.ts +1 -1
  139. package/dist/http/routes/runs.js +33 -37
  140. package/dist/http/routes/session-memory-sync.js +4 -5
  141. package/dist/http/routes/session-sync.d.ts +24 -5
  142. package/dist/http/routes/session-sync.js +45 -37
  143. package/dist/http/routes/sessions-list.d.ts +1 -1
  144. package/dist/http/routes/sessions.d.ts +1 -1
  145. package/dist/http/routes/sessions.js +18 -44
  146. package/dist/http/routes/tasks.js +136 -101
  147. package/dist/http/routes/trace-usage.js +14 -14
  148. package/dist/http/routes/workflows.d.ts +1 -0
  149. package/dist/http/routes/workflows.js +20 -38
  150. package/dist/http/server.d.ts +12 -9
  151. package/dist/http/server.js +35 -6
  152. package/dist/http/stored-body-replay.d.ts +3 -24
  153. package/dist/http/stored-body-replay.js +2 -12
  154. package/dist/http/submit-ctx.d.ts +2 -2
  155. package/dist/http/submit-ctx.js +5 -5
  156. package/dist/http/submit-replay.d.ts +15 -2
  157. package/dist/http/submit-replay.js +10 -4
  158. package/dist/http/wire-types.d.ts +6 -3
  159. package/dist/index.d.ts +3 -1
  160. package/dist/index.js +1 -0
  161. package/dist/leader/endpoint.js +9 -8
  162. package/dist/leg-facts.d.ts +13 -2
  163. package/dist/leg-facts.js +2 -0
  164. package/dist/local-store-declaration.d.ts +54 -0
  165. package/dist/local-store-declaration.js +59 -0
  166. package/dist/mcp-assertion-header.d.ts +1 -1
  167. package/dist/mcp-assertion-header.js +1 -1
  168. package/dist/mcp-authorization-face.d.ts +18 -14
  169. package/dist/mcp-authorization-face.js +3 -2
  170. package/dist/memory-project-layer.js +1 -1
  171. package/dist/memory-scope.d.ts +4 -4
  172. package/dist/memory-scope.js +4 -4
  173. package/dist/memory-store-refusal.d.ts +1 -1
  174. package/dist/memory-sync-client.js +2 -2
  175. package/dist/model-context-window.d.ts +50 -11
  176. package/dist/model-context-window.js +20 -1
  177. package/dist/model-entry-refusal.d.ts +5 -4
  178. package/dist/model-entry-refusal.js +17 -3
  179. package/dist/model-reach-scope.d.ts +2 -1
  180. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  181. package/dist/observability/fail-open.d.ts +19 -7
  182. package/dist/observability/fail-open.js +19 -7
  183. package/dist/observability/logger.d.ts +7 -0
  184. package/dist/observability/logger.js +3 -1
  185. package/dist/observability/metrics.js +1 -1
  186. package/dist/observability/run-terminal.d.ts +1 -1
  187. package/dist/observability/run-terminal.js +1 -0
  188. package/dist/observability/secret-env-scrub.d.ts +1 -1
  189. package/dist/observability/secret-env-scrub.js +1 -1
  190. package/dist/org-delivery-gate.d.ts +31 -6
  191. package/dist/org-delivery-gate.js +7 -4
  192. package/dist/owner-faces.d.ts +30 -5
  193. package/dist/owner-faces.js +2 -0
  194. package/dist/park-row-commit.d.ts +67 -0
  195. package/dist/park-row-commit.js +76 -0
  196. package/dist/parked-decide.d.ts +9 -7
  197. package/dist/parked-decide.js +25 -6
  198. package/dist/peer-directory.d.ts +1 -1
  199. package/dist/peer-seat.d.ts +1 -1
  200. package/dist/peer-seat.js +52 -59
  201. package/dist/plugins/adoption-log-sql.d.ts +3 -3
  202. package/dist/plugins/adoption-log-sql.js +19 -19
  203. package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
  204. package/dist/plugins/approval-ask-store-sql.js +3 -5
  205. package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
  206. package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
  207. package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
  208. package/dist/plugins/approval-exemption-skill-m46.js +6 -10
  209. package/dist/plugins/approval-exemption-store.d.ts +26 -1
  210. package/dist/plugins/approval-exemption-store.js +28 -19
  211. package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
  212. package/dist/plugins/ensure-schema-lock-policy.js +75 -9
  213. package/dist/plugins/exec-path-law.d.ts +34 -0
  214. package/dist/plugins/exec-path-law.js +13 -0
  215. package/dist/plugins/file-run-store.d.ts +22 -9
  216. package/dist/plugins/file-run-store.js +51 -23
  217. package/dist/plugins/local-checkpoint-store.d.ts +8 -1
  218. package/dist/plugins/local-checkpoint-store.js +5 -1
  219. package/dist/plugins/local-data-root-admission.d.ts +52 -0
  220. package/dist/plugins/local-data-root-admission.js +100 -0
  221. package/dist/plugins/local-engine-high-water.d.ts +28 -7
  222. package/dist/plugins/local-engine-high-water.js +27 -22
  223. package/dist/plugins/local-session-store.d.ts +134 -49
  224. package/dist/plugins/local-session-store.js +149 -108
  225. package/dist/plugins/memory-run-store.d.ts +14 -9
  226. package/dist/plugins/memory-run-store.js +48 -22
  227. package/dist/plugins/one-time-migration-marker.d.ts +19 -3
  228. package/dist/plugins/one-time-migration-marker.js +7 -1
  229. package/dist/plugins/one-time-migrations.d.ts +110 -59
  230. package/dist/plugins/one-time-migrations.js +224 -136
  231. package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
  232. package/dist/plugins/org-delivery-quota-store.js +29 -14
  233. package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
  234. package/dist/plugins/permission-rule-store-sql.js +5 -9
  235. package/dist/plugins/pg-pool.d.ts +1 -1
  236. package/dist/plugins/pg-pool.js +14 -12
  237. package/dist/plugins/posix-shell-fs.js +4 -2
  238. package/dist/plugins/remote-env-adb.js +3 -2
  239. package/dist/plugins/remote-env-device.d.ts +3 -2
  240. package/dist/plugins/remote-env-device.js +8 -6
  241. package/dist/plugins/remote-env-e2b.d.ts +4 -8
  242. package/dist/plugins/remote-env-e2b.js +21 -15
  243. package/dist/plugins/remote-env-host.d.ts +51 -10
  244. package/dist/plugins/remote-env-host.js +97 -31
  245. package/dist/plugins/remote-env-k8s.js +7 -4
  246. package/dist/plugins/remote-env-local-docker.d.ts +6 -3
  247. package/dist/plugins/remote-env-local-docker.js +11 -25
  248. package/dist/plugins/remote-env-ssh.js +4 -3
  249. package/dist/plugins/remote-shell.d.ts +3 -1
  250. package/dist/plugins/remote-shell.js +3 -3
  251. package/dist/plugins/replica-registration.d.ts +35 -0
  252. package/dist/plugins/replica-registration.js +48 -0
  253. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  254. package/dist/plugins/run-store-sql.d.ts +27 -8
  255. package/dist/plugins/run-store-sql.js +46 -18
  256. package/dist/plugins/scenario-prefix-switch-m42.d.ts +8 -12
  257. package/dist/plugins/scenario-prefix-switch-m42.js +9 -6
  258. package/dist/plugins/schema-consistency.d.ts +91 -5
  259. package/dist/plugins/schema-consistency.js +189 -7
  260. package/dist/plugins/schema-drift-sentence.d.ts +6 -5
  261. package/dist/plugins/schema-drift-sentence.js +13 -5
  262. package/dist/plugins/serving-requirement.d.ts +49 -0
  263. package/dist/plugins/serving-requirement.js +53 -0
  264. package/dist/plugins/session-placement.d.ts +5 -4
  265. package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
  266. package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
  267. package/dist/plugins/stop-then-start-sentence.d.ts +55 -0
  268. package/dist/plugins/stop-then-start-sentence.js +51 -0
  269. package/dist/plugins/store-backend.d.ts +23 -9
  270. package/dist/plugins/store-backend.js +58 -24
  271. package/dist/plugins/store-contracts.d.ts +45 -5
  272. package/dist/plugins/store-contracts.js +19 -0
  273. package/dist/plugins/tidb-pool.d.ts +11 -6
  274. package/dist/plugins/tidb-pool.js +22 -12
  275. package/dist/plugins/workspace-custody.d.ts +44 -0
  276. package/dist/plugins/workspace-custody.js +47 -0
  277. package/dist/process-instance.d.ts +3 -0
  278. package/dist/process-instance.js +7 -0
  279. package/dist/project-context.d.ts +66 -0
  280. package/dist/project-context.js +70 -0
  281. package/dist/project-identity.d.ts +8 -7
  282. package/dist/project-identity.js +40 -17
  283. package/dist/project-layer-key.d.ts +13 -2
  284. package/dist/project-layer-key.js +9 -1
  285. package/dist/question.d.ts +9 -21
  286. package/dist/question.js +28 -19
  287. package/dist/read-deny-floor-notice.d.ts +36 -0
  288. package/dist/read-deny-floor-notice.js +40 -0
  289. package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
  290. package/dist/request-declaration-disposition.js +39 -0
  291. package/dist/run-cancel-card.d.ts +28 -0
  292. package/dist/run-cancel-card.js +20 -0
  293. package/dist/run-cancel-context.d.ts +10 -0
  294. package/dist/run-cancel-context.js +1 -0
  295. package/dist/run-local.js +28 -13
  296. package/dist/runs.d.ts +31 -7
  297. package/dist/runs.js +34 -16
  298. package/dist/security.d.ts +3 -3
  299. package/dist/server-secret-env.d.ts +34 -20
  300. package/dist/server-secret-env.js +61 -18
  301. package/dist/session-agent.d.ts +4 -12
  302. package/dist/session-agent.js +0 -6
  303. package/dist/session-children-retire.d.ts +6 -3
  304. package/dist/session-children-retire.js +2 -5
  305. package/dist/session-incarnation.d.ts +18 -4
  306. package/dist/session-incarnation.js +4 -1
  307. package/dist/session-notify-door.d.ts +4 -4
  308. package/dist/session-notify-door.js +5 -6
  309. package/dist/session-rules-seat.d.ts +4 -3
  310. package/dist/session-rules-seat.js +3 -11
  311. package/dist/session-sync.js +3 -3
  312. package/dist/session-workspace.d.ts +3 -3
  313. package/dist/store-admission.d.ts +33 -0
  314. package/dist/store-admission.js +49 -0
  315. package/dist/store-posture.d.ts +5 -0
  316. package/dist/store-posture.js +18 -1
  317. package/dist/stores-sql/checkpoint-store-sql.d.ts +22 -3
  318. package/dist/stores-sql/checkpoint-store-sql.js +109 -23
  319. package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
  320. package/dist/stores-sql/file-history-store-sql.js +3 -5
  321. package/dist/stores-sql/retention-store-sql.d.ts +1 -1
  322. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  323. package/dist/stores-sql/tool-result-store-sql.js +4 -6
  324. package/dist/task-a2a.d.ts +3 -8
  325. package/dist/task-a2a.js +25 -27
  326. package/dist/task-cwd.d.ts +27 -10
  327. package/dist/task-cwd.js +3 -0
  328. package/dist/task-mcp.d.ts +3 -3
  329. package/dist/task-mcp.js +1 -1
  330. package/dist/task-settings.d.ts +13 -0
  331. package/dist/task-settings.js +14 -7
  332. package/dist/terminal.d.ts +2 -0
  333. package/dist/terminal.js +21 -6
  334. package/dist/tool-approval.d.ts +50 -41
  335. package/dist/tool-approval.js +96 -73
  336. package/dist/trace/engine-notice-wire.d.ts +38 -21
  337. package/dist/trace/engine-notice-wire.js +25 -4
  338. package/dist/trace/ledger-sink.d.ts +3 -1
  339. package/dist/trace/project.d.ts +8 -5
  340. package/dist/trace/project.js +11 -9
  341. package/dist/trace/projection-table.d.ts +33 -2
  342. package/dist/trace/redact.d.ts +12 -0
  343. package/dist/trace/redact.js +5 -1
  344. package/dist/trace/self-minted-wire.d.ts +268 -56
  345. package/dist/trace/self-minted-wire.js +110 -40
  346. package/dist/trace/tool-card.d.ts +3 -0
  347. package/dist/trace/tool-card.js +24 -0
  348. package/dist/trace/tool-output-media.d.ts +2 -1
  349. package/dist/unattended-permission-mode.d.ts +1 -1
  350. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  351. package/dist/web-admin-client/apply-effective.js +22 -9
  352. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  353. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  354. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
  355. package/docs/error-text-lexicon.json +317 -0
  356. package/package.json +4 -2
  357. package/scripts/embed-minimal.mjs +183 -0
  358. package/wire-changes/server@7.149.0.json +372 -0
  359. package/wire-changes/server@7.150.0.json +335 -0
  360. package/dist/plain-string-record.js +0 -70
@@ -4,6 +4,7 @@ import type { StoreBackend } from "./plugins/store-backend.js";
4
4
  import type { SqlEngineFacts } from "./plugins/sql-driver.js";
5
5
  import { type SqlEngineCapability } from "./sql-engine-posture.js";
6
6
  import { type MountRow, type MountTable } from "./mountinfo.js";
7
+ import type { LocalStoreDeclaration } from "./local-store-declaration.js";
7
8
  /**
8
9
  * 降级词闭集(**按后端族**,不按失败发生在哪一步):
9
10
  * · `local_unwritable` —— **缺省推导**的 local 文件店在**空 / 不存在**的数据根上构造期失败(mkdir / 权限 / 只读挂载 / 磁盘满)。
@@ -124,10 +125,13 @@ export interface StorePosture {
124
125
  readonly durability: StoreDurability;
125
126
  /** 逐根明细:恒以数据根开头;有起服证据时再接封签钥匙目录与(接线时的)file 记忆根。 */
126
127
  readonly roots: readonly StoreRootDurability[];
128
+ /** local 店交给两半店的声明(present-iff 装配出 local 后端;值 = 后端构造时 `localStoreDeclarationOf` 的那一份,装配方从后端读回,不重算)。 */
129
+ readonly localDeclaration?: LocalStoreDeclaration;
127
130
  }
128
131
  /**
129
132
  * `build*`(纯数据)。`assembled` = 装配出的 `StoreBackend.kind`;缺席 = 没装出后端 ⇒ `memory`。`evidence` 缺席 = 这个宿主没有起服证据(测试夹具 / 桩):
130
133
  * 只判数据根、且只按后端规则判(local 无证据即抛)。`memoryRootWired` = file 记忆引擎在本部署上真接了线(配了才算;没接线的记忆根不进最弱律)。
134
+ * `localDeclaration` = 装配出的 local 后端交给两半店的那一份声明(装配出 local 而缺席 = 装配缺陷,响亮抛;别的后端递了也不收)。
131
135
  */
132
136
  export declare function buildStorePosture(a: {
133
137
  readonly declared: StoreBackendWord;
@@ -135,6 +139,7 @@ export declare function buildStorePosture(a: {
135
139
  readonly degradedReason: StoreDegradedReason | undefined;
136
140
  readonly evidence: StoreEvidence | undefined;
137
141
  readonly memoryRootWired: boolean;
142
+ readonly localDeclaration: LocalStoreDeclaration | undefined;
138
143
  }): StorePosture;
139
144
  /** 读面桩的诚实下限(没有装配值的宿主:声明 = 配置词,后端 = memory ⇒ `ephemeral`)。caps 与 `/health` 同调这一只。 */
140
145
  export declare function unwiredStorePosture(declared: StoreBackendWord): StorePosture;
@@ -176,10 +176,27 @@ export function buildStorePosture(a) {
176
176
  ...(a.degradedReason !== undefined ? { degradedReason: a.degradedReason } : {}),
177
177
  durability: weakestDurability(roots.map((r) => r.verdict)),
178
178
  roots,
179
+ ...localDeclarationOf(backend, a.localDeclaration),
179
180
  };
180
181
  }
182
+ function localDeclarationOf(backend, declaration) {
183
+ switch (backend) {
184
+ case "local":
185
+ if (declaration === undefined)
186
+ throw new Error("store posture: a local backend was assembled without the declaration it handed its stores — wiring defect (read it back from the backend)");
187
+ return { localDeclaration: declaration };
188
+ case "memory":
189
+ case "mysql":
190
+ case "pg":
191
+ return {};
192
+ default: {
193
+ const unhandled = backend;
194
+ throw new Error(`store posture: unknown backend kind ${String(unhandled)}`);
195
+ }
196
+ }
197
+ }
181
198
  export function unwiredStorePosture(declared) {
182
- return buildStorePosture({ declared, assembled: undefined, degradedReason: undefined, evidence: undefined, memoryRootWired: false });
199
+ return buildStorePosture({ declared, assembled: undefined, degradedReason: undefined, evidence: undefined, memoryRootWired: false, localDeclaration: undefined });
183
200
  }
184
201
  export function buildStoreFreshStartLine(posture, evidence) {
185
202
  if (posture.backend !== "local" || evidence.dataRootHeldData)
@@ -1,6 +1,6 @@
1
1
  import type { Pool as MySqlPool } from "mysql2/promise";
2
2
  import type { Pool as PgPool } from "pg";
3
- import { type ExecutionOutcomeRecordWord, type GateOutcome, type Checkpoint, type CheckpointState, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type PendingSteerInput, type ResumeOutcome, type ResolveExpectation, type ReopenReason, type StoreDurability, type StoreFidelity } from "@sema-agent/core";
3
+ import { type CheckpointPage, type CheckpointRowRead, type CheckpointSessionCursor, type CheckpointSessionQuery, type ExecutionOutcomeRecordWord, type GateOutcome, type Checkpoint, type CheckpointState, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type PendingSteerInput, type ResumeOutcome, type ResolveExpectation, type ReopenReason, type StoreDurability, type StoreFidelity } from "@sema-agent/core";
4
4
  import { type SqlDialect, type SqlDriver } from "../plugins/sql-driver.js";
5
5
  import type { CtxWriteClaim } from "../plugins/store-contracts.js";
6
6
  import type { RetentionDeclaration } from "@sema-agent/core";
@@ -40,7 +40,7 @@ export declare const CHECKPOINT_ADDED_COLUMNS: ReadonlyArray<{
40
40
  *
41
41
  * 判据 = **能力探测**(恒空的 `WHERE 1=0` 读)+ **方言的缺列错误码**({@link isMissingColumnError},
42
42
  * 判据属主 `sql-errors.ts`);其余错误**原样 rethrow**,一个字都不加工 —— 把一次瞬时故障诊断成「缺列」的拒因
43
- * 比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link describeProbedTableDrift}),
43
+ * 比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link probedMissingColumnRefusal}),
44
44
  * 没有销毁性的一条:这张表里是在飞的停驻,出路是原表上加列。
45
45
  *
46
46
  * 位置:与三条先例同款,不在 `ensureSchema` 里(那条通道的契约是「只发 CREATE」),放在 boot 的
@@ -72,7 +72,6 @@ interface SteerColumnRead {
72
72
  * 列有内容却读不出 ⇒ `lossy`(两个调用点对它的处置见 {@link SteerColumnRead})。
73
73
  */
74
74
  export declare function readPendingSteerQueueColumn(queueRaw: unknown): SteerColumnRead;
75
- /** SQL checkpoint 店的装配选项:托管留存声明。(ctx 栅栏写口的认领由调用方每次交进来 —— [ref]:栅栏的来源是认领本身,店不另绑 run 店。) */
76
75
  export interface SqlCheckpointStoreOptions {
77
76
  readonly retention?: RetentionDeclaration;
78
77
  }
@@ -162,7 +161,27 @@ export declare class SqlCheckpointStore implements CheckpointStore {
162
161
  private pendingRowQuery;
163
162
  /** Create-once. core mints the token (`mintCheckpointToken`) and calls this during suspend. */
164
163
  put(token: CheckpointToken, cp: Checkpoint): Promise<void>;
164
+ /** 一行的列组 —— `get` 与按会话列表读同一份投影、交同一只解码({@link readStoredCheckpoint})。 */
165
+ private storedCheckpointColumns;
165
166
  get(token: CheckpointToken): Promise<Checkpoint | null>;
167
+ /**
168
+ * 一行字节 → 检查点 —— **`get` 与按会话列表({@link listBySession})的唯一解码**(列组 = {@link storedCheckpointColumns})。读不成的格交回 `damaged`
169
+ * (`get` 原样抛那只拒绝,列表把这一行答 `unreadable`);非体问题(读腿漏投影 `not-text` / 连接)原样抛 —— 码坏不冒充一行数据坏。
170
+ */
171
+ private readStoredCheckpoint;
172
+ /**
173
+ * core `CheckpointStore.listBySession`(可选动词,按在不在探;core 7.66.0 契约):一只会话的行,任何状态,按查询的成员收窄、按 `createdAt` 再按令牌排、键集分页。
174
+ * **读法只有 core 那一只**:查询经 `readCheckpointSessionQuery` 读(坏查询在任何 SQL 之前带码拒),过滤 / 排序 / 游标 / 截断 / 损坏判 全由 core 的 `selectSessionPage` 答
175
+ * (内存店与文件店同调它);本店只交「这只会话的行」—— 按会话列(`session_id`,本店每一只按会话的读口同读这一列)取行,经 `get` 的同一只解码({@link readStoredCheckpoint})成检查点。
176
+ * SQL 只按会话收窄,别的成员(状态 / scope / 门型 / 工具调用 / 建行时刻)一律由 core 判 —— 列上的比较不是 core 的比较(MySQL 协议的 `utf8mb4_bin` 按补空格比,
177
+ * 带尾空格的集外状态词会被 SQL 当成三词之一排除,而 core 要把它答成 `unreadable`),所以 SQL 不预筛任何一格,预筛不可能排除 core 会留下的行。
178
+ * 读成本:这只会话的每一行读一份字节(检查点体 + 旁格)—— 只在页里的行交出去;会话的行数由留存扫车道界住。
179
+ * 一行读不成(体不是一只检查点 / 决议或执行记录那一格读不出 / 体没说出它归在的这只会话)⇒ 这一行**照列**为 `unreadable`(令牌 / scope / 会话 / 状态 / 建行时刻取本店在字节之外
180
+ * 握着的列组;体读成、归属对而旁格坏 ⇒ 过滤器照读体上的成员),不跳过、不让整页失败;拿它的令牌去 `get` 撞见同一只拒。体是对象而成员形读不动 ⇒ core 自己答 `unreadable`。
181
+ * 页里每一只这样的行计一次 `server.approvals.park-row-blob-unreadable`(本店停驻行读者的那一条律:一行的字节只坏它自己那一行):core 判的经它给的座(句子去掉令牌后作探针),
182
+ * 本店判、core 看着读得动的那几行由本店计。只读:列表不改任何一行;交出的行是这一次解码的新对象,改它不改库。
183
+ */
184
+ listBySession(query: CheckpointSessionQuery): Promise<CheckpointPage<CheckpointRowRead, CheckpointSessionCursor>>;
166
185
  /**
167
186
  * Atomic CAS: pending → resolved, scoped. Returns true for the single winner, false if already
168
187
  * resolved/expired (AlreadyResolved → caller no-ops, never re-executes). `outcome` is persisted atomically
@@ -1,14 +1,14 @@
1
1
  import { parkedApprovalCard } from "../approval-content-kind.js";
2
2
  import { createHash } from "node:crypto";
3
3
  import { CHECKPOINT_FACET_SQL, CHECKPOINT_SWEEP_ORDER_SQL, CheckpointIndexRefusedError, checkpointSweepAfterSql, hostSessionOf, judgeCheckpointIndexCells } from "../plugins/checkpoint-index-columns.js";
4
- import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, } from "@sema-agent/core";
4
+ import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, readCheckpointSessionQuery, selectSessionPage, } from "@sema-agent/core";
5
5
  import { parkRowFacts, skillCardFacts } from "../approval-card.js";
6
- import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
6
+ import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, isJsonObject, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
7
7
  import { recordFailOpen } from "../observability/fail-open.js";
8
8
  import { durableRowIdentity, withoutCheckpointToken } from "../checkpoint-identity.js";
9
9
  import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "../plugins/sql-driver.js";
10
10
  import { isDupKeyError, isMissingColumnError, rowTooLargeOrSelf, StoreRowTooLargeError } from "../plugins/sql-errors.js";
11
- import { describeProbedTableDrift } from "../plugins/schema-drift-sentence.js";
11
+ import { probedMissingColumnRefusal } from "../plugins/schema-drift-sentence.js";
12
12
  import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
13
13
  import { approvalPayloadFingerprint } from "../plugins/approval-payload-fingerprint.js";
14
14
  import { approvalDecisionOfWinner, boundedRuleOffers, boundedToolInput, candidateIdentityOf, DECIDED_REPLAY_SCAN_CAP, DENY_SWEEP_PAGE_ROWS, isUnreadableSummaryForm, parkedLegTaskId, queueRowGateKind, queueTriageOrder, readRiskDescriptorBody, TERMINAL_BACKSTOP_MS, TERMINAL_GRACE_MS, UnreadableCheckpointRowError, } from "../plugins/checkpoint-store-face.js";
@@ -77,12 +77,10 @@ export async function assertCheckpointColumnSchema(query, dialect) {
77
77
  catch (err) {
78
78
  if (!isMissingColumnError(err, dialect))
79
79
  throw err;
80
- throw new Error(`checkpoint is missing the column ${column} — refusing to start. ` +
80
+ throw await probedMissingColumnRefusal(query, dialect, ["checkpoint"], `checkpoint is missing the column ${column} — refusing to start. ` +
81
81
  `On this table ${breaksAt} — at runtime, after a healthy-looking boot. ` +
82
82
  "The table holds IN-FLIGHT durable parks (suspended approvals/reviews awaiting a decision), so the way forward is an in-place " +
83
- "ALTER that keeps them; `checkpoint_ctx`, run rows and every other persistent face are untouched.\n" +
84
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
85
- (await describeProbedTableDrift(query, dialect, ["checkpoint"])));
83
+ "ALTER that keeps them; `checkpoint_ctx`, run rows and every other persistent face are untouched.", err);
86
84
  }
87
85
  }
88
86
  }
@@ -244,6 +242,12 @@ export function readPendingSteerQueueColumn(queueRaw) {
244
242
  }
245
243
  const STEER_CAS_ATTEMPTS = 8;
246
244
  const EXECUTION_RECORD_CAS_ATTEMPTS = 8;
245
+ const STORED_ROW_DAMAGE = {
246
+ body: "its stored body is not a checkpoint object",
247
+ decision: "its stored decision record cannot be read",
248
+ execution: "its stored execution record cannot be read",
249
+ filing: "its stored body does not name the session the row is filed under",
250
+ };
247
251
  export class SqlCheckpointStore {
248
252
  db;
249
253
  logger;
@@ -339,21 +343,62 @@ export class SqlCheckpointStore {
339
343
  throw rowTooLargeOrSelf(this.db.dialect, e, `the checkpoint row for session ${JSON.stringify(cp.sessionId)}`);
340
344
  }
341
345
  }
346
+ storedCheckpointColumns() {
347
+ const d = this.db.dialect;
348
+ return (`status, ${jsonColumnText(d, "checkpoint")}, ${jsonColumnText(d, "outcome")}, reopen_reason, rev, ` +
349
+ `${jsonColumnText(d, "pending_steer_queue")}, ${jsonColumnText(d, "execution_outcome")}, execution_at_ms`);
350
+ }
342
351
  async get(token) {
343
- const { rows } = await this.db.query(this.q(`SELECT status, ${jsonColumnText("mysql", "checkpoint")}, ${jsonColumnText("mysql", "outcome")}, reopen_reason, rev, ` +
344
- `${jsonColumnText("mysql", "pending_steer_queue")}, ${jsonColumnText("mysql", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = ?`, `SELECT status, ${jsonColumnText("pg", "checkpoint")}, ${jsonColumnText("pg", "outcome")}, reopen_reason, rev, ` +
345
- `${jsonColumnText("pg", "pending_steer_queue")}, ${jsonColumnText("pg", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = $1`), [token]);
352
+ const { rows } = await this.db.query(`SELECT ${this.storedCheckpointColumns()} FROM checkpoint WHERE token = ${this.ph(1)}`, [token]);
346
353
  const r = rows[0];
347
354
  if (!r)
348
355
  return null;
349
- const cp = readCheckpointBodyOrRefuse(r.checkpoint, `for token ${tokenFingerprint(token)}`);
356
+ const read = this.readStoredCheckpoint(r, token);
357
+ if (read.kind === "damaged")
358
+ throw read.error;
359
+ const cp = read.checkpoint;
360
+ this.logger?.info?.("checkpoint_get", {
361
+ tokenFp: tokenFingerprint(token),
362
+ status: cp.status,
363
+ hasWorkspaceHandle: !!cp.state?.workspaceHandle,
364
+ snapshotId: cp.state?.workspaceHandle?.snapshotId,
365
+ });
366
+ return cp;
367
+ }
368
+ readStoredCheckpoint(r, token) {
369
+ const where = `for token ${tokenFingerprint(token)}`;
370
+ let cp;
371
+ try {
372
+ cp = readCheckpointBodyOrRefuse(r.checkpoint, where);
373
+ }
374
+ catch (e) {
375
+ if (e instanceof StoreCorruptError)
376
+ return { kind: "damaged", member: "body", error: e };
377
+ throw e;
378
+ }
350
379
  cp.status = r.status;
351
- const outcomeRaw = refusing("a decision record nobody can read", () => decodeOutcomeColumn(r.outcome, `for token ${tokenFingerprint(token)}`));
380
+ let outcomeRaw;
381
+ try {
382
+ outcomeRaw = refusing("a decision record nobody can read", () => decodeOutcomeColumn(r.outcome, where));
383
+ }
384
+ catch (e) {
385
+ if (e instanceof StoreCorruptError)
386
+ return { kind: "damaged", member: "decision", error: e, body: cp };
387
+ throw e;
388
+ }
352
389
  if (outcomeRaw)
353
390
  cp.resolvedOutcome = winnerFromOutcome(outcomeRaw);
354
391
  cp.reopenReason = r.reopen_reason ?? undefined;
355
392
  cp.rev = r.rev == null ? 0 : Number(r.rev);
356
- const executionOutcome = refusing("an execution record nobody can read", () => decodeExecutionOutcomeColumn(r.execution_outcome, `for token ${tokenFingerprint(token)}`));
393
+ let executionOutcome;
394
+ try {
395
+ executionOutcome = refusing("an execution record nobody can read", () => decodeExecutionOutcomeColumn(r.execution_outcome, where));
396
+ }
397
+ catch (e) {
398
+ if (e instanceof StoreCorruptError)
399
+ return { kind: "damaged", member: "execution", error: e, body: cp };
400
+ throw e;
401
+ }
357
402
  if (executionOutcome !== undefined) {
358
403
  cp.executionOutcome = executionOutcome;
359
404
  if (r.execution_at_ms != null)
@@ -364,17 +409,58 @@ export class SqlCheckpointStore {
364
409
  recordFailOpen("server.sql-json.checkpoint.pending-steer-queue-lossy");
365
410
  this.logger?.info?.("checkpoint_pending_steer_unreadable", { tokenFp: tokenFingerprint(token) });
366
411
  }
367
- if (cp.state) {
368
- if (steer.state.pendingSteerQueue !== undefined)
369
- cp.state.pendingSteerQueue = steer.state.pendingSteerQueue;
370
- }
371
- this.logger?.info?.("checkpoint_get", {
372
- tokenFp: tokenFingerprint(token),
373
- status: cp.status,
374
- hasWorkspaceHandle: !!cp.state?.workspaceHandle,
375
- snapshotId: cp.state?.workspaceHandle?.snapshotId,
412
+ if (isJsonObject(cp.state) && steer.state.pendingSteerQueue !== undefined)
413
+ cp.state.pendingSteerQueue = steer.state.pendingSteerQueue;
414
+ return { kind: "row", checkpoint: cp };
415
+ }
416
+ async listBySession(query) {
417
+ const read = readCheckpointSessionQuery(query);
418
+ const { rows } = await this.db.query(`SELECT token, scope, session_id, created_at_ms, ${this.storedCheckpointColumns()} FROM checkpoint WHERE session_id = ${this.ph(1)}`, [read.sessionId]);
419
+ const tokens = rows.map((r) => String(r.token));
420
+ const withoutTokens = (sentence) => tokens.reduce((out, t) => withoutCheckpointToken(out, t), sentence);
421
+ const damaged = new Map();
422
+ const candidates = [];
423
+ rows.forEach((r, i) => {
424
+ const token = tokens[i];
425
+ const got = this.readStoredCheckpoint(r, token);
426
+ const body = got.kind === "row" ? got.checkpoint : got.member === "body" ? undefined : got.body;
427
+ const filedHere = body !== undefined && body.sessionId === String(r.session_id);
428
+ if (got.kind === "row" && filedHere) {
429
+ candidates.push([token, body]);
430
+ return;
431
+ }
432
+ damaged.set(token, { problem: STORED_ROW_DAMAGE[got.kind === "damaged" ? got.member : "filing"], r, counted: filedHere });
433
+ if (filedHere) {
434
+ candidates.push([token, body]);
435
+ return;
436
+ }
437
+ const filing = { token: token, sessionId: String(r.session_id), scope: String(r.scope), status: String(r.status), createdAt: Number(r.created_at_ms) };
438
+ candidates.push([token, filing]);
376
439
  });
377
- return cp;
440
+ const page = selectSessionPage(candidates, read, (cp) => cp, (sentence) => recordFailOpen("server.approvals.park-row-blob-unreadable", withoutTokens(sentence)));
441
+ if (damaged.size === 0)
442
+ return page;
443
+ return {
444
+ ...page,
445
+ rows: page.rows.map((row) => {
446
+ const token = row.kind === "row" ? row.checkpoint.token : row.token;
447
+ const hit = damaged.get(token);
448
+ if (hit === undefined)
449
+ return row;
450
+ if (hit.counted && row.kind === "row")
451
+ recordFailOpen("server.approvals.park-row-blob-unreadable", `session listing, session ${JSON.stringify(String(hit.r.session_id))}: ${hit.problem} — listed as unreadable`);
452
+ const createdAt = Number(hit.r.created_at_ms);
453
+ return {
454
+ kind: "unreadable",
455
+ token: token,
456
+ scope: String(hit.r.scope),
457
+ sessionId: String(hit.r.session_id),
458
+ status: String(hit.r.status),
459
+ createdAt: Number.isFinite(createdAt) ? createdAt : null,
460
+ problem: hit.problem,
461
+ };
462
+ }),
463
+ };
378
464
  }
379
465
  async resolve(token, scope, outcome, expect) {
380
466
  const params = [this.json(outcome, "checkpoint outcome"), Date.now(), token, scope];
@@ -208,7 +208,7 @@ export declare class PgFileHistoryStore extends SqlFileHistoryStore {
208
208
  * ONLY a provable missing-column error gets the missing-column refusal — everything else
209
209
  * (timeout / reset / permission / missing table) rethrows untouched: diagnosing a connection blip as a
210
210
  * schema drift is more expensive than the defect this gate exists to stop. The refusal's ways out are the
211
- * closed set of three shared with the whole-database check (`describeProbedTableDrift`); none of them
211
+ * closed set of three shared with the whole-database check (`probedMissingColumnRefusal`); none of them
212
212
  * is destructive. Honest cost statement: the history lives in these tables (per-file REWIND history,
213
213
  * working-tree convenience state) — session transcripts and every other durable subsystem are untouched.
214
214
  */
@@ -3,7 +3,7 @@ import { captureFileBackupViaEnv, applyFileRestoreViaEnv, fileHistoryModeProvenD
3
3
  import { BlobTooLargeError, SqlBlobIndex } from "./blob-index-sql.js";
4
4
  import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
5
5
  import { isMissingColumnError, retryOnDeadlock } from "../plugins/sql-errors.js";
6
- import { describeProbedTableDrift } from "../plugins/schema-drift-sentence.js";
6
+ import { probedMissingColumnRefusal } from "../plugins/schema-drift-sentence.js";
7
7
  import { recordFailOpen } from "../observability/fail-open.js";
8
8
  import { foldCaughtFailure } from "../file-history-disclosure.js";
9
9
  export const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
@@ -844,12 +844,10 @@ export async function assertFileHistorySchema(query, dialect) {
844
844
  catch (err) {
845
845
  if (!isMissingColumnError(err, dialect))
846
846
  throw err;
847
- throw new Error(`${table} is missing required columns — refusing to start. This build's per-file rewind history (S-15) ` +
847
+ throw await probedMissingColumnRefusal(query, dialect, [table], `${table} is missing required columns — refusing to start. This build's per-file rewind history (S-15) ` +
848
848
  `references every probed column unconditionally; a partial table would fail mid-write and could publish a ` +
849
849
  `half-shaped history graph. The file_history_* tables hold the per-file REWIND history (working-tree convenience ` +
850
- `state); session transcripts and all other durable subsystems are untouched, and an in-place ALTER keeps the history.\n` +
851
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
852
- (await describeProbedTableDrift(query, dialect, [table])));
850
+ `state); session transcripts and all other durable subsystems are untouched, and an in-place ALTER keeps the history.`, err);
853
851
  }
854
852
  }
855
853
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 托管留存(managed retention)的**执行器** —— 引擎座表那一半(阶段三逐字搬进 `@sema-agent/core-stores-sql` 的那一半)。
3
3
  * SINGLE-FILE DUAL-DIALECT([ref] A12 定型半场;先例 = `checkpoint-store-sql.ts`)。[ref] 车1,设计稿
4
- * `docs/DESIGN-270-retention-lane.md` v1.2。上游契约真源 = `@sema-agent/core` 的 `core/retention.d.ts`
4
+ * `docs/[ref]-retention-lane.md` v1.2。上游契约真源 = `@sema-agent/core` 的 `core/retention.d.ts`
5
5
  * (`ManagedRetentionCapability` / `RetentionReceipt`);事务内钩表的形 = core-stores-sql 的 `SqlTransactionHooks`(本文件的同名型是它的形,见下)。
6
6
  *
7
7
  * ── 一个事务、两个属主:执行器 + 部署钩 ─────────────────────────────────────────────────────────────
@@ -22,7 +22,7 @@ export declare function ensureSchema(pool: PgPool | PgPoolClient): Promise<void>
22
22
  * 一句看不出根因的话。这正是「响亮或 fail-closed,禁静默降级」那条要挡的形态。
23
23
  *
24
24
  * 判据用**能力探测**而不是版本号/information_schema:发一条恒空的 `WHERE 1=0` 读,列不在就报错。
25
- * 探针读抛且是**可证**的缺列 ⇒ 拒启(其余错误原样上抛,与兄弟探针同一条规则)。拒因的出路是**闭集三条**(与整库对账同一份,{@link describeProbedTableDrift} / {@link schemaWaysOut}),没有销毁性的一条:
25
+ * 探针读抛且是**可证**的缺列 ⇒ 拒启(其余错误原样上抛,与兄弟探针同一条规则)。拒因的出路是**闭集三条**(与整库对账同一份,{@link probedMissingColumnRefusal} / {@link schemaWaysOut}),没有销毁性的一条:
26
26
  * 缺的出处两列由对账同一套件逐条打印 `ALTER TABLE tool_result ADD COLUMN …`;键宽不够则第 (2) 条是部署者自己把 `ref` 加宽到本构建的定义。
27
27
  */
28
28
  export declare function assertToolResultProvenanceSchema(query: (sql: string) => Promise<{
@@ -3,7 +3,7 @@ import { pgSanitizeText } from "../plugins/pg-safe-json.js";
3
3
  import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
4
4
  import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
5
5
  import { ensureIndexes } from "../plugins/ensure-index.js";
6
- import { describeProbedTableDrift, schemaWaysOut } from "../plugins/schema-drift-sentence.js";
6
+ import { probedMissingColumnRefusal, schemaMismatchRefusal, schemaWaysOut } from "../plugins/schema-drift-sentence.js";
7
7
  import { isMissingColumnError } from "../plugins/sql-errors.js";
8
8
  export const PG_TOOL_RESULT_SCHEMA = [
9
9
  `CREATE TABLE IF NOT EXISTS tool_result (
@@ -29,7 +29,7 @@ export async function assertToolResultProvenanceSchema(query, dialect) {
29
29
  : "SELECT character_maximum_length AS len FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'tool_result' AND column_name = 'ref'").catch(() => ({ rows: [] }));
30
30
  const width = Number(widthRow.rows[0]?.len ?? NaN);
31
31
  if (Number.isFinite(width) && width < MAX_MINTED_TOOL_RESULT_REF_CHARS) {
32
- throw new Error(`tool_result.ref is VARCHAR(${width}) but core 5.26.0 mints refs up to ${MAX_MINTED_TOOL_RESULT_REF_CHARS} characters ` +
32
+ throw schemaMismatchRefusal(`tool_result.ref is VARCHAR(${width}) but core 5.26.0 mints refs up to ${MAX_MINTED_TOOL_RESULT_REF_CHARS} characters ` +
33
33
  `(#119 made the ref injective: four \`~\`-separated segments) — refusing to start. A truncating primary key aliases two distinct refs onto ` +
34
34
  `one row — one run's tool output reads back from another run's ref. ` +
35
35
  `The ${dialect === "pg" ? "PostgreSQL" : "MySQL-protocol"} database is behind this build on that key column; offloaded results are a recoverable-window cache, ` +
@@ -46,13 +46,11 @@ export async function assertToolResultProvenanceSchema(query, dialect) {
46
46
  catch (err) {
47
47
  if (!isMissingColumnError(err, dialect))
48
48
  throw err;
49
- throw new Error(`tool_result is missing the #119 provenance columns (owner_session_id / owner_task_id) — refusing to start. ` +
49
+ throw await probedMissingColumnRefusal(query, dialect, ["tool_result"], `tool_result is missing the #119 provenance columns (owner_session_id / owner_task_id) — refusing to start. ` +
50
50
  `This release picks up @sema-agent/core 5.26.0, which changed how an offloaded tool result is addressed ` +
51
51
  `(the ref grew past the old VARCHAR(190) key) and made the store record who wrote each row. Without the columns every offload write ` +
52
52
  `fails with unknown column and core's failure arm turns it into an inline placeholder — the service keeps running and the tool output is lost. ` +
53
- `Offloaded tool results are a recoverable-window cache (the inline previews in transcripts are unaffected).\n` +
54
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
55
- (await describeProbedTableDrift(query, dialect, ["tool_result"])));
53
+ `Offloaded tool results are a recoverable-window cache (the inline previews in transcripts are unaffected).`, err);
56
54
  }
57
55
  }
58
56
  function toolResultAgeCutoff(now, opts) {
@@ -57,7 +57,7 @@ export declare const A2A_PEER_DISPOSITION: {
57
57
  readonly toolAxes: "engine";
58
58
  };
59
59
  /** 请求体 `a2aPeers[i]` 的键闭集 = 处置表的键(书写序即判序)。 */
60
- export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" | "cardUrl" | "principalHeader" | "allowSkills" | "toolAxes")[];
60
+ export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" | "toolAxes" | "principalHeader" | "cardUrl" | "allowSkills")[];
61
61
  /**
62
62
  * **请求入口的一次读**(`boot/resolve-spec.ts` 在腿首铸一份,受理门与读器——续跑重放腿只有读器——都吃这一份):`body.a2aPeers` 若是数组,返回同形数组
63
63
  * (每个对象条目换成它的 {@link PeerSnapshot};非对象条目原样;长度保真 —— 界外的条数仍是受理门撞帽拒与读器丢弃留声的依据,只快照界内的条目);不是数组(缺席 / `null` / 别的形)原样返回。
@@ -68,8 +68,6 @@ export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" |
68
68
  export declare function snapshotRequestA2a(raw: unknown, options: {
69
69
  readonly reread: boolean;
70
70
  }): unknown;
71
- /** 读这条对端的是哪条腿:`fresh` = 新提交;`resume` = 续跑重放升级前存下的请求体(`boot/resolve-spec.ts` 的 `opts.leg` 分流,同一个读法)。 */
72
- type A2aRequestLeg = "fresh" | "resume";
73
71
  /** 请求面对端 → 草稿:按 {@link A2A_PEER_DISPOSITION} 拆(`headers` 进件,其余进声明;拆法与 MCP 请求腿同一只 `splitDeclaration`)。件缺席或空 ⇒ 草稿不带件。纯函数,不登记。 */
74
72
  export declare function toA2aDraft(peer: RequestA2aPeer): A2aDraft;
75
73
  /**
@@ -81,8 +79,8 @@ export declare function validateRequestA2a(raw: unknown): {
81
79
  dropped: A2aInjectionDrop[];
82
80
  } | null;
83
81
  /**
84
- * 一份请求的 `a2aPeers` 里,读器挂得上、`headers` 却剔掉了成员的那些坐标(`a2aPeers[<下标>].headers.__proto__`:成员名过回显清洗,只会是读器的保留名,不带值、不带调用方写的对端名)——
85
- * 「请求成员读不出」那一只判据(`unread-request-members.ts`)的 A2A 一座。读器与挂载同一只({@link normalizeA2aPeer},续跑重放腿的读法;新提交腿上同一形由判官表 400)。
82
+ * 一份请求的 `a2aPeers` 里,读器挂得上、`headers` 却剔掉了成员的那些坐标(`a2aPeers[<下标>].headers.<保留名>`:成员名只会是契约包保留成员名表里的名字,不带值、不带调用方写的对端名)——
83
+ * 「请求成员读不出」那一只判据(`unread-request-members.ts`)的 A2A 一座。读器与挂载同一只({@link normalizeA2aPeer},只有一种读法;新提交腿上同一形先由判官表 400)。
86
84
  * 挂不上的对端不算(整条不进,有自己的留声)。`storedToolAxes` = 续跑重放腿递的 M19 重读座。不是数组 ⇒ 空。
87
85
  */
88
86
  export declare function requestA2aHeadersUnread(raw: unknown, storedToolAxes?: StoredToolAxesRereadM19): string[];
@@ -123,9 +121,6 @@ desk: Pick<PeerDesk, "declare">, lease: PeerLease, logger?: {
123
121
  info?(m: string, meta?: unknown): void;
124
122
  warn(m: string, meta?: unknown): void;
125
123
  },
126
- /** 哪条腿在读(`boot/resolve-spec.ts` 按 `opts.leg` 递;缺省 = 新提交,最严的读法):只决定 `headers` 里 `__proto__` 名成员的处置(见 {@link normalizeA2aPeer})。 */
127
- leg?: A2aRequestLeg,
128
124
  /** 一次性迁移 M19 的重读座(窗口见 `plugins/one-time-migrations.ts` 登记表):只在续跑重放腿、只在窗开着时由 `boot/resolve-spec.ts` 递进来 —— 与 MCP 请求腿同一份。 */
129
125
  storedToolAxes?: StoredToolAxesRereadM19): A2aServerSpec[] | undefined;
130
- export {};
131
126
  //# sourceMappingURL=task-a2a.d.ts.map
package/dist/task-a2a.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { types } from "node:util";
2
2
  import { resolveComplianceDenies } from "@sema-agent/core";
3
- import { describeNotStringArray, describeValueShape, findUrlCredential, isStringArray } from "@sema-agent/settings-schema";
3
+ import { describeNotStringArray, describeValueShape, findUrlCredential, isStringArray, readPlainStringRecord } from "@sema-agent/settings-schema";
4
4
  import { errorKindOf } from "./observability/err-text.js";
5
- import { declarationKeys, readPlainStringRecord, splitDeclaration } from "./plain-string-record.js";
5
+ import { declarationKeys, splitDeclaration } from "./request-declaration-disposition.js";
6
6
  import { mintDeclaration, peerSentence } from "./peer-seat.js";
7
7
  import { readRequestToolAxes } from "./request-tool-axes.js";
8
- import { MAX_KEY_ECHO, echoKeyName, keyClosureClause, scanKeyNames } from "./request-key-closure.js";
8
+ import { MAX_KEY_ECHO, keyClosureClause, scanKeyNames } from "./request-key-closure.js";
9
9
  import { urlCredentialHeaderExit, urlCredentialRefusalReason } from "./url-credential-refusal.js";
10
10
  import { createMountSeat, declarationEntryNotObject, declarationIssueFacts, declarationNameIssue, declarationOverCapFacts, declaredName, judgeDeclarationKeys, requestDeclarationRefusal, throwDeclarationRefusal, } from "./request-declaration-refusal.js";
11
11
  import { sendHeaderOwnershipIssue } from "./task-mcp.js";
@@ -53,7 +53,12 @@ function peerUrlIssue(key) {
53
53
  };
54
54
  }
55
55
  const A2A_SEND_HEADER_PATHS = { headers: "headers", principalHeader: "principalHeader" };
56
- const HEADERS_RESERVED_MEMBER_PATH = "headers.__proto__";
56
+ function readPeerHeaders(raw) {
57
+ const read = readPlainStringRecord(raw);
58
+ if (!read.ok)
59
+ return { record: undefined, issues: [{ path: "headers", reason: `must be a plain object of string values — ${read.detail}` }] };
60
+ return { record: read.record, issues: read.reservedMembers.map((name) => ({ path: `headers.${name}`, reason: `no member of a declaration may be named "${name}"` })) };
61
+ }
57
62
  const PEER_SNAPSHOT = Symbol("a2aPeerSnapshot");
58
63
  function openCloneNode(v, ancestors) {
59
64
  if (v === null || v === undefined || typeof v === "string" || typeof v === "number" || typeof v === "boolean")
@@ -134,7 +139,7 @@ function mintPeerSnapshot(entry, reread) {
134
139
  cardUrl: source.cardUrl,
135
140
  principalHeader: source.principalHeader,
136
141
  allowSkills: Array.isArray(allowSkills) ? Array.from(allowSkills) : allowSkills,
137
- headers: headers === undefined ? undefined : readPlainStringRecord(headers),
142
+ headers: headers === undefined ? undefined : readPeerHeaders(headers),
138
143
  toolAxes: toolAxesRead,
139
144
  toolAxesDeclared: reread && toolAxesRead !== undefined && !toolAxesRead.ok ? cloneDeclaration(toolAxes) : undefined,
140
145
  unknownKeys: Object.keys(source).filter((name) => !A2A_PEER_ENTRY_KEY_SET.has(name)),
@@ -162,18 +167,10 @@ const A2A_PEER_KEY_JUDGES = {
162
167
  name: declarationNameIssue,
163
168
  url: peerUrlIssue("url"),
164
169
  cardUrl: peerUrlIssue("cardUrl"),
165
- headers: (o) => {
166
- const read = o.headers;
167
- if (read === undefined)
168
- return undefined;
169
- if (!read.ok)
170
- return { path: "headers", reason: `must be a plain object of string values — ${read.member === undefined ? read.fault : `its member "${echoKeyName("", read.member)}" ${read.fault}`}` };
171
- const reserved = read.stripped[0];
172
- return reserved === undefined ? undefined : { path: HEADERS_RESERVED_MEMBER_PATH, reason: reserved.fault };
173
- },
170
+ headers: (o) => o.headers?.issues[0],
174
171
  principalHeader: (o) => o.principalHeader !== undefined && typeof o.principalHeader !== "string"
175
172
  ? { path: "principalHeader", reason: `must be a string (a header name) — got ${describeValueShape(o.principalHeader)}` }
176
- : sendHeaderOwnershipIssue({ headers: o.headers?.ok === true ? o.headers.record : undefined, principalHeader: o.principalHeader }, "a2a", A2A_SEND_HEADER_PATHS),
173
+ : sendHeaderOwnershipIssue({ headers: o.headers?.record, principalHeader: o.principalHeader }, "a2a", A2A_SEND_HEADER_PATHS),
177
174
  allowSkills: (o) => o.allowSkills === undefined || isStringArray(o.allowSkills)
178
175
  ? undefined
179
176
  : { path: "allowSkills", reason: `must be an array of strings (omit the key to leave the peer's skills unrestricted) — got ${describeNotStringArray(o.allowSkills)}` },
@@ -182,19 +179,20 @@ const A2A_PEER_KEY_JUDGES = {
182
179
  return read === undefined || read.ok ? undefined : toolAxesIssue(read);
183
180
  },
184
181
  };
185
- function normalizeA2aPeer(e, leg, at, storedToolAxes) {
182
+ function normalizeA2aPeer(e, at, storedToolAxes) {
186
183
  const dropBecause = (issue) => ({ draft: null, because: declarationIssueFacts(at, issue) });
187
184
  const notObject = declarationEntryNotObject(A2A_SEAT, e, at);
188
185
  if (notObject !== undefined)
189
186
  return { draft: null, because: { field: at, reason: notObject } };
190
187
  const o = snapshotPeer(e, storedToolAxes !== undefined);
188
+ const lost = o.headers?.record === undefined ? [] : o.headers.issues;
191
189
  for (const key of A2A_PEER_ENTRY_KEYS) {
192
190
  const issue = A2A_PEER_KEY_JUDGES[key](o, at);
193
191
  if (issue === undefined)
194
192
  continue;
195
193
  if (issue.credential !== undefined)
196
194
  continue;
197
- if (leg === "resume" && issue.path === HEADERS_RESERVED_MEMBER_PATH)
195
+ if (lost.includes(issue))
198
196
  continue;
199
197
  if (key === "toolAxes" && storedToolAxes !== undefined)
200
198
  continue;
@@ -204,9 +202,9 @@ function normalizeA2aPeer(e, leg, at, storedToolAxes) {
204
202
  if (o.cardUrl !== undefined)
205
203
  spec.cardUrl = o.cardUrl;
206
204
  const { headers, toolAxes: axes } = o;
207
- if (headers?.ok === true)
205
+ if (headers?.record !== undefined)
208
206
  spec.headers = headers.record;
209
- const lost = headers?.ok === true && headers.stripped.length > 0 ? { headersLost: headers.stripped.map((m) => echoKeyName("headers.", m.name)) } : {};
207
+ const headersLost = lost.length > 0 ? { headersLost: lost.map((issue) => issue.path) } : {};
210
208
  if (o.principalHeader !== undefined)
211
209
  spec.principalHeader = o.principalHeader;
212
210
  if (o.allowSkills !== undefined)
@@ -227,22 +225,22 @@ function normalizeA2aPeer(e, leg, at, storedToolAxes) {
227
225
  return unread;
228
226
  if (again.toolAxes !== undefined)
229
227
  spec.toolAxes = again.toolAxes;
230
- return { draft: toA2aDraft(spec), toolAxesReread: peerSentence(axes.problem), ...lost };
228
+ return { draft: toA2aDraft(spec), toolAxesReread: peerSentence(axes.problem), ...headersLost };
231
229
  }
232
230
  if (axes.toolAxes !== undefined)
233
231
  spec.toolAxes = axes.toolAxes;
234
232
  }
235
- return { draft: toA2aDraft(spec), ...lost };
233
+ return { draft: toA2aDraft(spec), ...headersLost };
236
234
  }
237
235
  export function toA2aDraft(peer) {
238
236
  const split = splitDeclaration(peer, A2A_PEER_DISPOSITION);
239
237
  return { spec: split.engine, ...(split.pieces === undefined ? {} : { pieces: split.pieces }) };
240
238
  }
241
239
  export function validateRequestA2a(raw) {
242
- const v = validateRequestA2aEntries(raw, "fresh");
240
+ const v = validateRequestA2aEntries(raw);
243
241
  return v === null ? null : { ok: v.ok, dropped: v.dropped };
244
242
  }
245
- function validateRequestA2aEntries(raw, leg, storedToolAxes) {
243
+ function validateRequestA2aEntries(raw, storedToolAxes) {
246
244
  if (raw === undefined || raw === null)
247
245
  return { ok: [], dropped: [], toolAxesReread: [] };
248
246
  if (!Array.isArray(raw))
@@ -251,7 +249,7 @@ function validateRequestA2aEntries(raw, leg, storedToolAxes) {
251
249
  const dropped = [];
252
250
  const toolAxesReread = [];
253
251
  for (const [i, e] of raw.slice(0, MAX_REQUEST_A2A_PEERS).entries()) {
254
- const read = normalizeA2aPeer(e, leg, `${A2A_SEAT.list}[${i}]`, storedToolAxes);
252
+ const read = normalizeA2aPeer(e, `${A2A_SEAT.list}[${i}]`, storedToolAxes);
255
253
  if (read.draft !== null) {
256
254
  ok.push(read.draft);
257
255
  if (read.toolAxesReread !== undefined)
@@ -271,7 +269,7 @@ export function requestA2aHeadersUnread(raw, storedToolAxes) {
271
269
  const out = [];
272
270
  for (const [i, e] of raw.slice(0, MAX_REQUEST_A2A_PEERS).entries()) {
273
271
  const at = `${A2A_SEAT.list}[${i}]`;
274
- const read = normalizeA2aPeer(e, "resume", at, storedToolAxes);
272
+ const read = normalizeA2aPeer(e, at, storedToolAxes);
275
273
  if (read.draft !== null)
276
274
  for (const member of read.headersLost ?? [])
277
275
  out.push(`${at}.${member}`);
@@ -307,14 +305,14 @@ export function mergeRequestA2a(baseline, drafts, desk, lease) {
307
305
  const winners = drafts.filter((d) => !taken.has(d.spec.name));
308
306
  return winners.length === 0 ? baseline : [...(baseline ?? []), ...winners.map((d) => mintDeclaration(desk, d, lease))];
309
307
  }
310
- export function resolveRequestA2a(baseline, bodyA2a, config, desk, lease, logger, leg = "fresh", storedToolAxes) {
308
+ export function resolveRequestA2a(baseline, bodyA2a, config, desk, lease, logger, storedToolAxes) {
311
309
  if (bodyA2a === undefined || bodyA2a === null)
312
310
  return baseline;
313
311
  if (!a2aInjectionHonored(config)) {
314
312
  logger?.warn("a2a_injection_ignored", { reason: "gate_closed (per-request A2A peers honored only on a single-user, unlocked, compliance-permitted deployment)" });
315
313
  return baseline;
316
314
  }
317
- const v = validateRequestA2aEntries(bodyA2a, leg, storedToolAxes);
315
+ const v = validateRequestA2aEntries(bodyA2a, storedToolAxes);
318
316
  if (v === null) {
319
317
  logger?.warn("a2a_injection_ignored", { reason: "a2aPeers not an array" });
320
318
  return baseline;