@sema-agent/server 7.142.0 → 7.144.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 (301) hide show
  1. package/MIGRATION.md +608 -0
  2. package/USAGE.md +63 -36
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +7 -5
  5. package/dist/approval-ask-audit-store.js +12 -6
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +155 -39
  9. package/dist/approval-card.js +111 -31
  10. package/dist/approval-queue-kind.d.ts +27 -0
  11. package/dist/approval-queue-kind.js +6 -0
  12. package/dist/approval-reconciler.d.ts +8 -8
  13. package/dist/approval-reconciler.js +3 -3
  14. package/dist/audit.js +4 -2
  15. package/dist/boot/budget-tracing.d.ts +1 -1
  16. package/dist/boot/execution-env.js +1 -1
  17. package/dist/boot/governance-seams.d.ts +26 -41
  18. package/dist/boot/governance-seams.js +61 -28
  19. package/dist/boot/late-binds.d.ts +7 -4
  20. package/dist/boot/late-binds.js +3 -1
  21. package/dist/boot/leader.js +2 -4
  22. package/dist/boot/memory-consolidation.d.ts +0 -8
  23. package/dist/boot/memory-consolidation.js +3 -3
  24. package/dist/boot/reapers.js +5 -4
  25. package/dist/boot/resolve-spec.d.ts +1 -1
  26. package/dist/boot/resolve-spec.js +8 -8
  27. package/dist/boot/runner-deps.d.ts +14 -6
  28. package/dist/boot/runner-deps.js +6 -17
  29. package/dist/boot/shutdown.d.ts +9 -0
  30. package/dist/boot/shutdown.js +18 -2
  31. package/dist/boot/stage-01-config.js +2 -0
  32. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  33. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  34. package/dist/boot/stage-03-store-backend.js +1 -2
  35. package/dist/boot/stage-04-stores.d.ts +5 -2
  36. package/dist/boot/stage-04-stores.js +3 -5
  37. package/dist/boot/stage-05-execution-env.d.ts +5 -2
  38. package/dist/boot/stage-06-runners.d.ts +5 -2
  39. package/dist/boot/stage-06-runners.js +1 -3
  40. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  41. package/dist/boot/stage-07-capability-layer.js +6 -2
  42. package/dist/boot/stage-08-reapers.d.ts +5 -2
  43. package/dist/boot/stage-09-leader.d.ts +5 -2
  44. package/dist/boot/stage-10-http-server.d.ts +5 -2
  45. package/dist/boot/stage-10-http-server.js +4 -1
  46. package/dist/boot/stores.d.ts +5 -2
  47. package/dist/boot/stores.js +7 -7
  48. package/dist/boot/web-admin.d.ts +5 -1
  49. package/dist/boot/web-admin.js +31 -19
  50. package/dist/capabilities/hands-lane.d.ts +9 -39
  51. package/dist/capabilities/hands-lane.js +8 -13
  52. package/dist/capabilities/mcp-probe.d.ts +3 -2
  53. package/dist/capabilities/scenarios.d.ts +23 -17
  54. package/dist/capabilities/scenarios.js +2 -2
  55. package/dist/capabilities/skills.d.ts +1 -17
  56. package/dist/capabilities/skills.js +0 -8
  57. package/dist/capability-facts.d.ts +8 -8
  58. package/dist/capability-facts.js +4 -4
  59. package/dist/card-void.d.ts +6 -9
  60. package/dist/config-catalog.js +1 -1
  61. package/dist/config-d-migration.d.ts +2 -0
  62. package/dist/config-dir-trust.js +58 -29
  63. package/dist/config-invariants.d.ts +7 -1
  64. package/dist/config-invariants.js +7 -0
  65. package/dist/config-provider.d.ts +10 -0
  66. package/dist/config-provider.js +15 -4
  67. package/dist/config-root-migration.d.ts +13 -0
  68. package/dist/config-root-migration.js +99 -0
  69. package/dist/config-types.d.ts +5 -5
  70. package/dist/config.d.ts +4 -3
  71. package/dist/config.js +1 -1
  72. package/dist/delegated-session-owner.d.ts +35 -151
  73. package/dist/delegated-session-owner.js +1 -83
  74. package/dist/device-ws-protocol.d.ts +1 -0
  75. package/dist/device-ws-protocol.js +2 -1
  76. package/dist/entry-mode-clamp-notice.d.ts +1 -1
  77. package/dist/env-model-table-gate.d.ts +53 -0
  78. package/dist/env-model-table-gate.js +53 -0
  79. package/dist/execution-placement.d.ts +7 -4
  80. package/dist/fleet/fleet-bus.d.ts +28 -52
  81. package/dist/fleet/fleet-bus.js +35 -100
  82. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  83. package/dist/fleet/fleet-reconciler.js +1 -1
  84. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  85. package/dist/fleet/subagent-tail-bus.js +15 -28
  86. package/dist/hooks/classic-stations.d.ts +3 -1
  87. package/dist/hooks/classic-stations.js +2 -0
  88. package/dist/hooks/hook-runner.js +28 -7
  89. package/dist/host-decision.d.ts +54 -20
  90. package/dist/host-decision.js +42 -14
  91. package/dist/http/admission.d.ts +1 -1
  92. package/dist/http/admission.js +7 -3
  93. package/dist/http/approval-sweeps.js +11 -8
  94. package/dist/http/caller-trust.d.ts +1 -1
  95. package/dist/http/durable-run-leg.js +2 -2
  96. package/dist/http/engine-refusal.d.ts +16 -0
  97. package/dist/http/engine-refusal.js +17 -0
  98. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  99. package/dist/http/memory-compliance-failure-reply.js +4 -6
  100. package/dist/http/owner-access.d.ts +11 -0
  101. package/dist/http/owner-access.js +13 -0
  102. package/dist/http/principal-gate.d.ts +1 -1
  103. package/dist/http/refusal-body.d.ts +7 -3
  104. package/dist/http/refusal-body.js +1 -0
  105. package/dist/http/resume-legs.d.ts +21 -7
  106. package/dist/http/resume-legs.js +82 -53
  107. package/dist/http/resume-restatement.d.ts +39 -0
  108. package/dist/http/resume-restatement.js +25 -0
  109. package/dist/http/route-ctx.d.ts +35 -10
  110. package/dist/http/route-ctx.js +1 -1
  111. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  112. package/dist/http/routes/approvals-assistant.js +143 -31
  113. package/dist/http/routes/capabilities.js +11 -2
  114. package/dist/http/routes/memory-policy.d.ts +2 -2
  115. package/dist/http/routes/memory-policy.js +1 -1
  116. package/dist/http/routes/notify-wake.js +23 -25
  117. package/dist/http/routes/rules.js +48 -13
  118. package/dist/http/routes/runs.js +9 -8
  119. package/dist/http/routes/session-memory-sync.d.ts +34 -6
  120. package/dist/http/routes/session-memory-sync.js +27 -10
  121. package/dist/http/routes/session-sync.js +13 -20
  122. package/dist/http/routes/sessions.d.ts +2 -2
  123. package/dist/http/routes/sessions.js +22 -23
  124. package/dist/http/routes/tasks.js +27 -49
  125. package/dist/http/server.d.ts +13 -7
  126. package/dist/http/server.js +2 -2
  127. package/dist/http/stored-body-replay.js +15 -4
  128. package/dist/http/wire-types.d.ts +17 -7
  129. package/dist/index.d.ts +1 -0
  130. package/dist/index.js +1 -0
  131. package/dist/lane-home-probe.d.ts +15 -0
  132. package/dist/lane-home-probe.js +4 -0
  133. package/dist/leader/leader.js +5 -2
  134. package/dist/leader/wire.d.ts +7 -1
  135. package/dist/leader/wire.js +6 -5
  136. package/dist/leg-facts.d.ts +37 -0
  137. package/dist/leg-facts.js +15 -0
  138. package/dist/memory-bundle-engine.d.ts +1 -4
  139. package/dist/memory-bundle-engine.js +2 -2
  140. package/dist/memory-operator-faces.d.ts +30 -66
  141. package/dist/memory-operator-faces.js +17 -88
  142. package/dist/memory-scope-stores.d.ts +38 -40
  143. package/dist/memory-scope-stores.js +51 -36
  144. package/dist/memory-scope.d.ts +0 -9
  145. package/dist/memory-scope.js +0 -9
  146. package/dist/memory-sync-client.js +14 -3
  147. package/dist/memory-sync.d.ts +20 -4
  148. package/dist/memory-sync.js +23 -4
  149. package/dist/model-plane-face.d.ts +13 -7
  150. package/dist/model-plane-face.js +2 -1
  151. package/dist/observability/corrupt-read-seat.d.ts +5 -9
  152. package/dist/observability/corrupt-read-seat.js +1 -2
  153. package/dist/observability/fail-open.d.ts +30 -10
  154. package/dist/observability/fail-open.js +27 -10
  155. package/dist/observability/run-terminal.d.ts +1 -1
  156. package/dist/observability/run-terminal.js +6 -2
  157. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  158. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  159. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  160. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  161. package/dist/orchestration/workflow-park-decision.js +5 -3
  162. package/dist/org-delivery-gate.d.ts +11 -0
  163. package/dist/org-delivery-gate.js +19 -1
  164. package/dist/parked-decide.d.ts +13 -9
  165. package/dist/parked-decide.js +7 -7
  166. package/dist/parked-revive-surface.d.ts +6 -7
  167. package/dist/parked-revive-surface.js +1 -1
  168. package/dist/peer-inbound-face.d.ts +122 -0
  169. package/dist/peer-inbound-face.js +123 -0
  170. package/dist/permission-rule-list.d.ts +13 -9
  171. package/dist/permission-rule-list.js +7 -3
  172. package/dist/plugins/approval-ask-store-file.js +18 -6
  173. package/dist/plugins/approval-ask-store-memory.js +8 -6
  174. package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
  175. package/dist/plugins/approval-ask-store-sql.js +30 -11
  176. package/dist/plugins/caching-session-store.d.ts +0 -3
  177. package/dist/plugins/caching-session-store.js +0 -3
  178. package/dist/plugins/checkpoint-store-sql.d.ts +45 -38
  179. package/dist/plugins/checkpoint-store-sql.js +108 -45
  180. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  181. package/dist/plugins/file-history-store-sql.js +24 -19
  182. package/dist/plugins/file-run-store.d.ts +29 -0
  183. package/dist/plugins/file-run-store.js +97 -8
  184. package/dist/plugins/fork-routing-session-store.d.ts +8 -1
  185. package/dist/plugins/fork-routing-session-store.js +6 -0
  186. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  187. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  188. package/dist/plugins/local-checkpoint-store.d.ts +6 -8
  189. package/dist/plugins/local-checkpoint-store.js +5 -4
  190. package/dist/plugins/local-session-store.d.ts +69 -30
  191. package/dist/plugins/local-session-store.js +133 -57
  192. package/dist/plugins/local-session-window.d.ts +4 -2
  193. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  194. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  195. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  196. package/dist/plugins/mailbox-store-sql.js +76 -7
  197. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  198. package/dist/plugins/memory-sync-store-pg.js +21 -0
  199. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  200. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  201. package/dist/plugins/one-time-migrations.d.ts +44 -14
  202. package/dist/plugins/one-time-migrations.js +118 -15
  203. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  204. package/dist/plugins/org-delivery-quota-store.js +2 -12
  205. package/dist/plugins/park-commit-watch.d.ts +1 -1
  206. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  207. package/dist/plugins/permission-rule-store-file.js +15 -2
  208. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  209. package/dist/plugins/permission-rule-store-sql.js +35 -2
  210. package/dist/plugins/pg-session-storage.d.ts +1 -9
  211. package/dist/plugins/pg-session-storage.js +2 -19
  212. package/dist/plugins/remote-env-device.d.ts +8 -11
  213. package/dist/plugins/remote-env-device.js +6 -3
  214. package/dist/plugins/remote-env-host.d.ts +8 -4
  215. package/dist/plugins/remote-env-host.js +7 -9
  216. package/dist/plugins/remote-env-k8s.d.ts +37 -24
  217. package/dist/plugins/remote-env-k8s.js +50 -17
  218. package/dist/plugins/remote-shell.js +12 -2
  219. package/dist/plugins/retention-store-sql.d.ts +2 -2
  220. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  221. package/dist/plugins/sandbox-read-floor.js +2 -7
  222. package/dist/plugins/schema-consistency.d.ts +5 -1
  223. package/dist/plugins/session-fork.d.ts +1 -1
  224. package/dist/plugins/session-ownership.d.ts +9 -0
  225. package/dist/plugins/session-ownership.js +6 -0
  226. package/dist/plugins/session-store.d.ts +13 -16
  227. package/dist/plugins/session-store.js +9 -10
  228. package/dist/plugins/sql-errors.d.ts +10 -1
  229. package/dist/plugins/sql-errors.js +13 -0
  230. package/dist/plugins/sql-json-column.d.ts +24 -2
  231. package/dist/plugins/sql-json-column.js +38 -3
  232. package/dist/plugins/store-backend.d.ts +23 -28
  233. package/dist/plugins/store-backend.js +12 -3
  234. package/dist/plugins/store-contracts.d.ts +29 -5
  235. package/dist/plugins/store-contracts.js +4 -1
  236. package/dist/plugins/task-attachment-store.d.ts +2 -2
  237. package/dist/plugins/task-attachment-store.js +37 -52
  238. package/dist/plugins/tidb-session-store.d.ts +2 -14
  239. package/dist/plugins/tidb-session-store.js +2 -20
  240. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  241. package/dist/plugins/tool-result-store-sql.js +23 -2
  242. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  243. package/dist/plugins/wiring-section-codec.js +55 -0
  244. package/dist/plugins/workspace-fill.d.ts +1 -1
  245. package/dist/rule-entry-list.d.ts +3 -2
  246. package/dist/rule-entry-list.js +5 -1
  247. package/dist/rules-consent.d.ts +28 -5
  248. package/dist/rules-consent.js +40 -28
  249. package/dist/run-local.js +11 -6
  250. package/dist/runs.d.ts +12 -8
  251. package/dist/runs.js +10 -7
  252. package/dist/runtime-governance.d.ts +1 -1
  253. package/dist/security.d.ts +0 -4
  254. package/dist/server-secret-env.d.ts +4 -0
  255. package/dist/session-error-wire.d.ts +34 -23
  256. package/dist/session-error-wire.js +8 -3
  257. package/dist/session-incarnation.d.ts +3 -3
  258. package/dist/session-notify-door.d.ts +106 -0
  259. package/dist/session-notify-door.js +97 -0
  260. package/dist/session-sync.d.ts +9 -14
  261. package/dist/session-sync.js +5 -5
  262. package/dist/session-workspace.d.ts +3 -13
  263. package/dist/session-workspace.js +0 -9
  264. package/dist/standing-answer-grant.d.ts +5 -0
  265. package/dist/standing-answer-grant.js +10 -0
  266. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  267. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  268. package/dist/task-mcp.d.ts +1 -1
  269. package/dist/task-settings.d.ts +11 -8
  270. package/dist/task-settings.js +31 -11
  271. package/dist/tool-approval.d.ts +55 -35
  272. package/dist/tool-approval.js +112 -74
  273. package/dist/trace/core-keyset-guard.d.ts +12 -9
  274. package/dist/trace/engine-notice-wire.d.ts +1 -1
  275. package/dist/trace/engine-notice-wire.js +4 -2
  276. package/dist/trace/exit-seat.d.ts +7 -1
  277. package/dist/trace/exit-seat.js +1 -0
  278. package/dist/trace/ledger-sink.d.ts +2 -2
  279. package/dist/trace/ledger-sink.js +12 -4
  280. package/dist/trace/model-segment.d.ts +74 -25
  281. package/dist/trace/model-segment.js +141 -32
  282. package/dist/trace/project.d.ts +15 -6
  283. package/dist/trace/project.js +12 -4
  284. package/dist/trace/redact.d.ts +72 -9
  285. package/dist/trace/redact.js +1509 -471
  286. package/dist/trace/self-minted-wire.d.ts +598 -47
  287. package/dist/trace/self-minted-wire.js +190 -20
  288. package/dist/trace/wire-projection-faces.d.ts +1 -1
  289. package/dist/trace/wire-projection-faces.js +1 -1
  290. package/dist/verifier-pause-card.d.ts +33 -5
  291. package/dist/verifier-pause-card.js +15 -10
  292. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  293. package/dist/web-admin-client/apply-effective.js +90 -10
  294. package/dist/workspace-space.d.ts +19 -3
  295. package/dist/workspace-space.js +8 -4
  296. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  297. package/package.json +3 -2
  298. package/wire-changes/server@7.143.0.json +1167 -0
  299. package/wire-changes/server@7.144.0.json +291 -0
  300. package/dist/session-records-skipped-notice.d.ts +0 -21
  301. package/dist/session-records-skipped-notice.js +0 -27
@@ -1,8 +1,6 @@
1
1
  import { type SessionStore } from "@sema-agent/core";
2
2
  import type { ServiceConfig } from "../config-types.js";
3
3
  import type { StoreBackend } from "./store-backend.js";
4
- import { type DelegationRootWitness, type OrphanOwnerLog } from "../delegated-session-owner.js";
5
- import type { OwnerAwareSessionStore } from "../security.js";
6
4
  /**
7
5
  * Session-store plugin seam.
8
6
  *
@@ -43,23 +41,22 @@ export declare function createSessionStore(config: Pick<ServiceConfig, "sessionB
43
41
  */
44
42
  export declare function ensureSessionDurable(hostStore: SessionStore, sessionId: string): Promise<void>;
45
43
  /**
46
- * core `SubagentToolOptions.background.ensureChildSessionDurable` 的实现体 —— 停驻挂点的**见证**:这只后台子代的转录此刻是 host 店里一只有真内容、
47
- * 属主按委派继承律定得下来的会话,才放行停驻;任何一步不成立 ⇒ 抛 = 否决(core 收 veto 即 expire checkpoint + settle failed,pre-153 行为,不放大)。
44
+ * core `SubagentToolOptions.background.ensureChildSessionDurable(sessionId, given)` 的实现体 —— 停驻挂点的**见证**:这只后台子代的转录此刻是 host 店里一只有真内容、
45
+ * 属主恰是引擎交来的委派树属主(`given.owner`)的会话,才放行停驻;任何一步不成立 ⇒ 抛 = 否决(core 收 veto 即 expire checkpoint + settle failed,pre-153 行为,不放大)。
48
46
  *
49
47
  * 规则(`fork-routing-session-store.ts` 头注):一只后台子代的 durable 行在,它的转录就是 host 店委派分区里的一只 placed 行。挂点只装在声明了
50
48
  * durable 委派分区的部署上(stage-07:core `resolveSubagentTranscriptTier(...) === "full"`),那里 core 给每只新起的后台子代铸 placement、
51
- * 转录生来就在 host 店 —— 所以这里**只验、不搬**(此前的「晋升」把私店的转录整树拷进 host 店,随这条规则退役)。
52
- * 0. 属主(`delegated-session-owner.ts`):按委派继承律判一次(唯一判据 `judgeDelegatedOwner`)—— 来源集 = 见证的根这一只(调用方交出的委派树根 + 调用方判断那一刻
53
- * 读到的根行住客:请求腿 = 本请求经鉴权解析的会话与受理时那一读,`/decide` 赎回腿 = 行上的 `rootSessionId` 与写口放行的那一位(认领之前解析器那一读);core 的挂点签名只给子代 id,
54
- * 场景层把见证绑进闭包交下来)。此刻根行在而住客不是见证的那一位(删了、同 id 被重新登记)⇒ **拒**(`assertRootStillWitnessed`);
55
- * 缺席(行上没有根)或根行此刻不在 ⇒ 孤行哨兵。
49
+ * 转录生来就在 host 店 —— 所以这里**只验、不搬、不判属主**:属主是引擎判的(core 7.62.0 [ref] L1:没人派生的 run 读自己那一行、按化身绑一次,原样带给每一层子代;
50
+ * 复活的子代 = 它自己那一行的属主),placed 行建行那一笔原样记下了它(盒义务 7,三只持久会话店 + `session-ownership.ts` 的 `placedSessionOwnerOf`)。
51
+ * 挂点不认领、不推断、不收委派树根见证([ref] P-5;本仓的委派继承律在这一写点退役 —— 存量无主 placed 行:SQL 两店由一次性迁移 M23 在窗内回填,本机文件店没有回填臂,见 `local-session-store.ts` 头注)。
52
+ * 0. 宿主店读不出属主(没有 `headOf`,唯一判法 `headReadOf`)⇒ 响亮拒,一次读都不发。
56
53
  * 1. 在不在、有没有真内容:{@link ensureSessionDurable}(`requireExisting` 打开 + 叶子非空 + forget 收尾)。任何失败(不在、空壳、读故障)⇒ 原样上抛 = 否决
57
- * (读故障也否决:说不准转录在不在,就不让一次审批押在它上面)。这里**不判它是不是 placed 行**:复活的是旧行时 id 取行上的会话(升级前停驻、当年晋升进来的普通行),
58
- * 它照样是这只子代的转录;判据只问「host 店里有这份真内容」。
59
- * 2. 属主定夺(`settleDelegatedOwner`):记着别的委派树 ⇒ 抛;记着这一棵 ⇒ 按它记录的根、父再判一次(与 M23 同读数),无主 ⇒ 认领(CAS 子行,写判断时的读数)、没落下 ⇒ 抛;
60
- * 此刻属主不是律给的那一只 ⇒ 那是别人的同 id 会话,不是这只子代的转录 ⇒ 抛 = 停驻否决。认领写下的是哨兵 ⇒ 写落下之后一行 warn(`site: "park"`)。
61
- * 宿主店必须读得出属主(`delegatedOwnerHostOf`:`headOf`;记录委派树的店另交 `placementOf` + `claimOwnerless`,两只同在或同缺;生产的 SQL / local 店与缓存包装层都满足),
62
- * 否则响亮拒。
54
+ * (读故障也否决:说不准转录在不在,就不让一次审批押在它上面)。不判它是不是 placed 行:复活的是旧行时 id 取行上的会话(升级前停驻、当年晋升进来的普通行),
55
+ * 它照样是这只子代的转录。
56
+ * 2. 此刻行上的属主 ≠ `given.owner` ⇒ 那是别人的同 id 会话,或这一行在引擎读之后被改写 ⇒ 抛 = 停驻否决(拒句不带任何一方的属主值 —— 它会进停驻否决的错误面)。
57
+ * 契约只承诺属主值相等(化身不比:复活腿的 `given` 只带属主)。
63
58
  */
64
- export declare function attestChildSessionDurable(hostStore: OwnerAwareSessionStore, sessionId: string, root: DelegationRootWitness | undefined, log: OrphanOwnerLog): Promise<void>;
59
+ export declare function attestChildSessionDurable(hostStore: SessionStore, sessionId: string, given: {
60
+ readonly owner: string | null;
61
+ }): Promise<void>;
65
62
  //# sourceMappingURL=session-store.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { TtlSessionStore } from "@sema-agent/core";
2
2
  import { CachingSessionStore } from "./caching-session-store.js";
3
- import { assertRootStillWitnessed, delegatedOwnerHostOf, readDelegatedOwner, reportWrittenOwner, settleDelegatedOwner, sourceFacesVia, } from "../delegated-session-owner.js";
3
+ import { headReadOf } from "../session-incarnation.js";
4
4
  export function createSessionStore(config, backend, metrics) {
5
5
  if (config.sessionBackend === "tidb") {
6
6
  if (!backend)
@@ -23,16 +23,15 @@ export async function ensureSessionDurable(hostStore, sessionId) {
23
23
  await hostStore.forget?.(acquired.sessionId);
24
24
  }
25
25
  }
26
- export async function attestChildSessionDurable(hostStore, sessionId, root, log) {
27
- const host = delegatedOwnerHostOf(hostStore);
28
- if (host === undefined) {
29
- throw new Error(`session "${sessionId}" cannot be parked: the host session store cannot read a session's owner (headOf), or records delegation trees without being able to claim under them (placementOf / claimOwnerless), so the delegated transcript's owner cannot be derived or checked`);
26
+ export async function attestChildSessionDurable(hostStore, sessionId, given) {
27
+ const headOf = headReadOf(hostStore);
28
+ if (headOf === undefined) {
29
+ throw new Error(`session "${sessionId}" cannot be parked: the host session store cannot read a session's owner (headOf), so the delegated transcript's owner cannot be checked`);
30
30
  }
31
- const judged = await readDelegatedOwner(sessionId, root === undefined ? {} : { rootSessionId: root.sessionId }, sourceFacesVia(host.headOf));
32
- assertRootStillWitnessed(sessionId, root, judged);
33
31
  await ensureSessionDurable(hostStore, sessionId);
34
- const settled = await settleDelegatedOwner(host, sessionId, judged.inherited.owner, judged.tree);
35
- if (settled.claimed)
36
- reportWrittenOwner(log, "park", sessionId, settled.judged ?? judged);
32
+ if ((await headOf(sessionId))?.owner !== given.owner) {
33
+ throw new Error(`session "${sessionId}" cannot be parked as this delegated session: the host session store holds a session with this id whose owner is not this delegation tree's owner, ` +
34
+ "so it does not attest this delegated transcript");
35
+ }
37
36
  }
38
37
  //# sourceMappingURL=session-store.js.map
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * SQL 驱动错误的**方言判别**——「这是不是唯一键冲突」的单一属主([ref] P1-①,病族
3
3
  * single-semantic-multi-site-drift)。`sql-escape.ts` / `sql-row-helpers.ts` / `store-contracts.ts`
4
- * 的同级叶子:零 store 逻辑、零驱动依赖(不 import mysql2/pg),纯谓词。
4
+ * 的同级叶子:零 store 逻辑、零驱动依赖(不 import mysql2/pg),纯谓词 —— 外加死锁判据唯一的消费形 {@link retryOnDeadlock}
5
+ * (「撞死锁重来几次」与「什么算死锁」住在一处:上界只有一个数,不在各店各写一个)。
5
6
  *
6
7
  * ## 为什么必须只有一份
7
8
  * 收编前这条谓词在 src/ 手铸 ≥7 处,而且**识别集互相漂移**:一半站点只认 `code === "ER_DUP_ENTRY"`,
@@ -96,6 +97,14 @@ export declare const PG_DEADLOCK_DETECTED_SQLSTATE = "40P01";
96
97
  * **MySQL 协议腿**,两方言都认只是为了让店不必自己判引擎。
97
98
  */
98
99
  export declare function isDeadlockError(dialect: SqlDialect, err: unknown): boolean;
100
+ /** 引擎仲裁之后整段重来的**唯一**上界(全仓每一条「撞死锁就重来」的腿共用这一个数)。8 = 锁形并发套的并发度:缺行首写的 N 路竞争每轮至多多走一轮([ref] 读数 1 成 7 死锁)。 */
101
+ export declare const DEADLOCK_RETRY_LIMIT = 8;
102
+ /**
103
+ * 「引擎判了死锁 ⇒ 整段重来」的**唯一**写法:有界({@link DEADLOCK_RETRY_LIMIT}),判据 {@link isDeadlockError};用尽 / 其它错误原样抛。
104
+ * `attempt` 必须整段重做语义等价(读-判-写全在里面的一只事务,或一条幂等语句)。`alsoArbitrated`:店自己认的另一种仲裁产物(例:缺行首写撞主键 ——
105
+ * 并发的另一只首写赢了,重读即走更新腿),与死锁同族、同一个上界。锁等待超时不在任何一族里(延迟信号,见 {@link isDeadlockError} 的注):它照抛。
106
+ */
107
+ export declare function retryOnDeadlock<T>(dialect: SqlDialect, attempt: () => Promise<T>, alsoArbitrated?: (err: unknown) => boolean): Promise<T>;
99
108
  /** TiDB `ErrEntryTooLarge` (one row's key + value over `txn-entry-size-limit`, 6 MiB by default) — TiDB's own
100
109
  * errno, which mysql2 carries without a symbolic name. */
101
110
  export declare const TIDB_ER_ENTRY_TOO_LARGE_ERRNO = 8025;
@@ -45,6 +45,19 @@ export function isDeadlockError(dialect, err) {
45
45
  return keyOf(err, "code") === PG_DEADLOCK_DETECTED_SQLSTATE;
46
46
  return keyOf(err, "errno") === MYSQL_ER_LOCK_DEADLOCK_ERRNO || keyOf(err, "code") === MYSQL_ER_LOCK_DEADLOCK_CODE;
47
47
  }
48
+ export const DEADLOCK_RETRY_LIMIT = 8;
49
+ export async function retryOnDeadlock(dialect, attempt, alsoArbitrated) {
50
+ for (let tries = 0;; tries++) {
51
+ try {
52
+ return await attempt();
53
+ }
54
+ catch (err) {
55
+ if (tries < DEADLOCK_RETRY_LIMIT && (isDeadlockError(dialect, err) || alsoArbitrated?.(err) === true))
56
+ continue;
57
+ throw err;
58
+ }
59
+ }
60
+ }
48
61
  export const TIDB_ER_ENTRY_TOO_LARGE_ERRNO = 8025;
49
62
  export const MYSQL_ER_NET_PACKET_TOO_LARGE_ERRNO = 1153;
50
63
  export const MYSQL_ER_NET_PACKET_TOO_LARGE_CODE = "ER_NET_PACKET_TOO_LARGE";
@@ -68,12 +68,13 @@ export interface JsonColumnMember {
68
68
  * 同一只投影的**裸表达式**(不带别名)—— 给 `WHERE` / `UPDATE … WHERE` 这种**没有别名位**的地方用。
69
69
  *
70
70
  * 🔴 存在的唯一理由是「别名位在那里不合法」,**不是**给读腿留第二条路:读腿一律用 {@link jsonColumnText}
71
- * (别名是它的产物的一部分,理由见那只函数)。今天整仓**两处**消费方,各自的「别名位不合法」都是真的:
71
+ * (别名是它的产物的一部分,理由见那只函数)。今天整仓**三处**消费方,各自的「别名位不合法」都是真的:
72
72
  * · `one-time-migrations.ts` 的几条改写(M8 / M17 / M18 / M21 的候选扫描与按原文 CAS 回写)——
73
73
  * 它们要在 `WHERE` 里判这一列的**文本**,而 `WHERE` 没有别名位;
74
74
  * · `workflow-journal-store-sql.ts` 的 `loadPage`([ref] 车③a)—— 那一列的投影裹在一条
75
75
  * `CASE WHEN LENGTH(result) > ? THEN NULL ELSE <expr> END AS result_json` 里:**别名属于整条 CASE**
76
76
  * (`result_json`,因为超字节门的那一支要给 `NULL`),CASE 里面的表达式位上写别名是语法错。
77
+ * · 本模块的 {@link jsonColumnTextSha256} —— 投影裹在摘要函数里,**别名属于整条摘要表达式**(产物是摘要不是体)。
77
78
  * 而「怎么把一列 JSON 取成文本」这件事整仓只能有一种写法:两种承载(真 JSON 列 / LONGTEXT 别名)在这条
78
79
  * 表达式下合成同一只串,迁移的判等、分页腿的取文本因此与读腿的判形**看同一份字节**。
79
80
  *
@@ -82,6 +83,20 @@ export interface JsonColumnMember {
82
83
  * 误用的落点是真库上当场的 `SqlJsonColumnError("not-text")` 响亮抛(行上没有那个键 ⇒ `undefined`),不是静默。
83
84
  */
84
85
  export declare function jsonColumnTextExpr(dialect: SqlDialect, column: string, memberKey?: string): string;
86
+ /**
87
+ * 一列 JSON 的**投影文本的 sha256**(十六进制小写,引擎内算 —— 整只文档不出库),带别名。输入恰是 {@link jsonColumnTextExpr} 那一只投影:
88
+ * 同一行、同一次读里,本表达式的摘要就是 {@link jsonColumnText} 交给读腿的那串字节的摘要,于是「字节没变 ⇒ 摘要没变」只有一种读法。
89
+ * MySQL 协议 `SHA2(<文本>, 256)`(TiDB / MySQL / MariaDB 都认);PG `encode(sha256(convert_to(<文本>, 'UTF8')), 'hex')`(PG 11+)。
90
+ *
91
+ * 读者今天一处:`checkpoint-store-sql.ts` 的停驻行投影记忆(运维队列 / 调度视图对一份停驻行字节的判决按 (摘要, 引擎构建) 记,
92
+ * 记忆命中就不拉整只文档)。产物是摘要不是体,所以它**不是**体的第二条投影路;回读走 {@link readJsonColumnTextSha256}。
93
+ */
94
+ export declare function jsonColumnTextSha256(dialect: SqlDialect, column: string, as: string): string;
95
+ /**
96
+ * {@link jsonColumnTextSha256} 那一格的回读:恒是 64 位十六进制小写串。别的一切(键不在 = 读腿漏了投影;串形不对 = 驱动换了行为)都是**码坏**,
97
+ * 响亮抛 —— 不当作「这一行没有摘要」退成每次整行读(那会让记忆静默失效,代价无声地涨回去)。
98
+ */
99
+ export declare function readJsonColumnTextSha256(cell: unknown, where: string): string;
85
100
  /**
86
101
  * 一次读回失败的**种别**(店层码,**闭集**;运行期词表在这里,类型从它推导 —— 一张词表只有一个写者):
87
102
  * · `not-text` —— 拿到的不是串 = **读腿漏了投影**,那是**码的问题不是数据的问题** ⇒ 响亮的程序错,
@@ -114,7 +129,12 @@ export declare class SqlJsonColumnError extends Error {
114
129
  readonly problem: SqlJsonColumnProblem;
115
130
  /** 出事的坐标(`<table>.<column> for <identity>`)—— 进错误文案,让运维知道去看哪一行。 */
116
131
  readonly where: string;
117
- constructor(problem: SqlJsonColumnProblem, where: string, detail: string);
132
+ /**
133
+ * `malformed` 时形读者交出的「它不认得的键」({@link JsonColumnShapeBad.unknownKeys};别的种别 / 读者说不出 ⇒ 空)—— 体是更新的写者写下的那一形的判据
134
+ * (例:local 数据根的严格边档据它把拒句点名成「比本构建新」)。判词只有形读者一个写者,本类原样携带。
135
+ */
136
+ readonly unknownKeys: readonly string[];
137
+ constructor(problem: SqlJsonColumnProblem, where: string, detail: string, unknownKeys?: readonly string[]);
118
138
  }
119
139
  /** 形读者认了:带上收窄后的值。 */
120
140
  export interface JsonColumnShapeOk<T> {
@@ -125,6 +145,8 @@ export interface JsonColumnShapeOk<T> {
125
145
  export interface JsonColumnShapeBad {
126
146
  readonly value?: undefined;
127
147
  readonly problem: string;
148
+ /** 读者因为**不认得**而拒的键(去重、排序;没有 / 读者说不出 ⇒ 缺)—— 更新的写者给这一形加了位。{@link decodeJsonColumn} 原样交给 {@link SqlJsonColumnError.unknownKeys}。 */
149
+ readonly unknownKeys?: readonly string[];
128
150
  }
129
151
  /** {@link JsonColumnReader} 的答复。 */
130
152
  export type JsonColumnShape<T> = JsonColumnShapeOk<T> | JsonColumnShapeBad;
@@ -20,15 +20,30 @@ export function jsonColumnTextExpr(dialect, column, memberKey) {
20
20
  }
21
21
  return dialect === "pg" ? `(${column}::jsonb -> '${memberKey}')::text` : `CAST(JSON_EXTRACT(${column}, '$.${memberKey}') AS CHAR)`;
22
22
  }
23
+ export function jsonColumnTextSha256(dialect, column, as) {
24
+ if (!MEMBER_ALIAS_RE.test(as)) {
25
+ throw new Error(`jsonColumnTextSha256: alias ${JSON.stringify(as)} must be a lower-case bare identifier — PostgreSQL folds unquoted aliases to lower case, so any other spelling would give the two dialects different row keys`);
26
+ }
27
+ const text = jsonColumnTextExpr(dialect, column);
28
+ return dialect === "pg" ? `encode(sha256(convert_to(${text}, 'UTF8')), 'hex') AS ${as}` : `SHA2(${text}, 256) AS ${as}`;
29
+ }
30
+ const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
31
+ export function readJsonColumnTextSha256(cell, where) {
32
+ if (typeof cell === "string" && SHA256_HEX_RE.test(cell))
33
+ return cell;
34
+ throw new Error(`${where}: expected a lower-case sha256 hex digest from the read leg, got ${cell === undefined ? "no such key (the SELECT dropped the projection)" : typeof cell} — refusing rather than guess`);
35
+ }
23
36
  export const SQL_JSON_COLUMN_PROBLEMS = ["not-text", "not-json", "malformed"];
24
37
  export class SqlJsonColumnError extends Error {
25
38
  problem;
26
39
  where;
27
- constructor(problem, where, detail) {
40
+ unknownKeys;
41
+ constructor(problem, where, detail, unknownKeys = []) {
28
42
  super(`${where}: ${detail}`);
29
43
  this.name = "SqlJsonColumnError";
30
44
  this.problem = problem;
31
45
  this.where = where;
46
+ this.unknownKeys = unknownKeys;
32
47
  }
33
48
  }
34
49
  function describeBody(body) {
@@ -53,7 +68,7 @@ export function decodeJsonColumn(text, read, where) {
53
68
  }
54
69
  const shape = read(body);
55
70
  if (shape.problem !== undefined) {
56
- throw new SqlJsonColumnError("malformed", where, `carries ${describeBody(body)} that this store cannot read (${shape.problem}) — refusing rather than serve it as an empty/partial value`);
71
+ throw new SqlJsonColumnError("malformed", where, `carries ${describeBody(body)} that this store cannot read (${shape.problem}) — refusing rather than serve it as an empty/partial value`, shape.unknownKeys);
57
72
  }
58
73
  return shape.value;
59
74
  }
@@ -85,8 +100,28 @@ export function assertJsonColumnWritable(text, read, where) {
85
100
  export function createZodColumnReader(schema) {
86
101
  return (body) => {
87
102
  const parsed = schema.safeParse(body);
88
- return parsed.success ? { value: parsed.data } : { problem: parsed.error.message };
103
+ if (parsed.success)
104
+ return { value: parsed.data };
105
+ const unknownKeys = unrecognizedKeysOf(parsed.error.issues);
106
+ return { problem: parsed.error.message, ...(unknownKeys.length > 0 ? { unknownKeys } : {}) };
107
+ };
108
+ }
109
+ function unrecognizedKeysOf(issues) {
110
+ const keys = new Set();
111
+ const walk = (list) => {
112
+ for (const issue of list) {
113
+ if (issue.code === "unrecognized_keys")
114
+ for (const key of issue.keys)
115
+ keys.add(key);
116
+ else if (issue.code === "invalid_union")
117
+ for (const branch of issue.errors)
118
+ walk(branch);
119
+ else if (issue.code === "invalid_key" || issue.code === "invalid_element")
120
+ walk(issue.issues);
121
+ }
89
122
  };
123
+ walk(issues);
124
+ return [...keys].sort();
90
125
  }
91
126
  export function createObjectColumnReader() {
92
127
  return (body) => (isJsonObject(body) ? { value: body } : { problem: "expected a JSON object" });
@@ -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 { SessionStore, SessionPolicyStore, FileHistoryStore, WorkflowJournalStore, ToolResultStore, SessionCaptureRecordStore, UsageWindowStore } from "@sema-agent/core";
3
+ import type { SessionStore, SessionPolicyStore, FileHistoryStore, FileHistoryBlobExchange, FileHistoryError, WorkflowJournalStore, ToolResultStore, SessionCaptureRecordStore, UsageWindowStore } from "@sema-agent/core";
4
4
  import { type OutcomeSink } from "./file-outcome-sink.js";
5
5
  import { type InboxWarn } from "./workflow-run-store-sql.js";
6
6
  import { FileRunStore } from "./file-run-store.js";
@@ -60,37 +60,37 @@ export type ServiceSessionPolicyStore = SessionPolicyStore;
60
60
  * carry (optional so the local lane's core `FileFileHistoryStore` still fits). Replaces the retired E19
61
61
  * whole-tree `ServiceFileSnapshotStore` ([ref]: the boundary covers the TRACKED set only — files this
62
62
  * session edited; there is no whole-tree manifest anymore). Also the 2c history-transfer store
63
- * (exportHistory/importHistory + the blob negotiation faces below). */
63
+ * (exportHistory/importHistory + the blob exchange below). */
64
64
  /**
65
- * The 2c `putBlob` answer: core's `FileHistoryResult` plus ONE arm — the SQL blob judge refused the blob on the connection that
66
- * would have written it ({@link import("./blob-backend.js").BlobTooLargeError}, code `blob_too_large_for_sql`). `PUT /sync/blobs`
67
- * answers that arm 413 (the store's sentence, plus the machine-readable `wallSource` = where the cap came from) and every other failure 502 (retryable).
65
+ * The SQL twins' blob exchange — core's {@link FileHistoryBlobExchange} group (`put` / `get` / `has`, all three or none, read only
66
+ * through `blobExchangeOf` in `session-sync.ts`, which defers to core's judge) with ONE narrowing: a `put` refusal may carry
67
+ * `blobCap`, the SQL blob judge's refusal on the connection that would have written the blob
68
+ * ({@link import("./blob-backend.js").BlobTooLargeError}). Present ⟺ that refusal; `PUT /sync/blobs` answers it 413
69
+ * `blob_too_large_for_sql` (the judge's sentence, plus the machine-readable `wallSource` = where the cap came from). The content-address
70
+ * refusal is core's own (`adopt_failed`, `permanence: "terminal"`, nothing stored); every other failure is a write fault the route answers 502.
68
71
  */
69
- export type SyncBlobPutResult = import("@sema-agent/core").FileHistoryResult | {
72
+ export type SqlBlobPutResult = {
73
+ readonly ok: true;
74
+ } | {
70
75
  readonly ok: false;
71
- readonly error: {
72
- readonly code: import("./blob-backend.js").BlobTooLargeError["code"];
73
- readonly message: string;
74
- readonly wallSource: import("./blob-backend.js").SqlBlobCapReading["source"];
76
+ readonly error: FileHistoryError & {
77
+ readonly blobCap?: {
78
+ readonly wallSource: import("./blob-backend.js").SqlBlobCapReading["source"];
79
+ };
75
80
  };
76
81
  };
82
+ export interface ServiceBlobExchange extends FileHistoryBlobExchange {
83
+ put(hash: string, bytes: Uint8Array): Promise<SqlBlobPutResult>;
84
+ }
77
85
  export type ServiceFileHistoryStore = FileHistoryStore & {
78
- /** 2c sync blob-face marker — see SqlFileHistoryStore.syncBlobFaces (codex R2-high: name-probing
79
- * `putBlob` on an arbitrary store collides with the local core store's PRIVATE void helper). */
80
- syncBlobFaces?: true;
86
+ /** The blob exchange (SQL twins only; core's local File store carries none). Narrowed per {@link ServiceBlobExchange}. */
87
+ readonly blobExchange?: ServiceBlobExchange;
81
88
  /** E21 purge / overwrite-dst sync wipe: drop the WHOLE scope incl. v1 baselines + DV-14 markers
82
89
  * (`reap(scope, [])` deliberately retains those while the scope lives — not a purge). SQL twins only. */
83
90
  deleteBySession?(scope: string): Promise<number>;
84
91
  /** Async byte-GC for the file_history_blob domain (orphans left by reap/purge/lost mint races; grace-window
85
92
  * protected — never a synchronous external-store delete). Reaper-driven; SQL twins only. */
86
93
  sweepOrphanBlobs?(): Promise<number>;
87
- /** 2c blob negotiation: upload one content-addressed blob ahead of importHistory. SQL twins only. */
88
- putBlob?(hash: string, bytes: Uint8Array): Promise<SyncBlobPutResult>;
89
- /** 2c blob read face (scope-checked by the route against the session's history graph). SQL twins only. */
90
- getBlob?(hash: string): Promise<Uint8Array | undefined>;
91
- /** The subset of `hashes` already present — a cheap SQL INDEX probe (the import presence pre-check,
92
- * never N full getBlob GETs). SQL twins only. */
93
- hasBlobs?(hashes: string[]): Promise<Set<string>>;
94
94
  };
95
95
  /** SVC-2 (core CORE-7) WorkflowJournalStore — core's interface PLUS the service `deleteByRun` (GC extra). tidb/pg =
96
96
  * durable SQL twins (cross-replica resume journal); local = the service {@link FileWorkflowJournalStore}
@@ -128,15 +128,10 @@ export type CheckpointStoreFull = TiDBCheckpointStore | PgCheckpointStore | Loca
128
128
  /** The full tool-result store: core `ToolResultStore` + the service maintenance extras. The SQL twins carry
129
129
  * both extras; the LOCAL lane returns core's `FileToolResultStore` (core 1.219 — durable across a
130
130
  * process restart, the TOC "ReadToolResult(ref) 恒空" fix) which since core 5.28.0 carries `deleteBySession`
131
- * (owner sidecar 判属主,无边车存量计 unattributable 不删)but still NOT `reapOlderThan` — consumers must
132
- * optional-call (`store.reapOlderThan?.()`,local 上恒 no-op)。(R5 复扫纠:旧句「carries NEITHER」自
133
- * core 5.28 起半假——本类型注是消费方第一落点,与 LocalBackend 内部注必须同真。)
131
+ * (owner sidecar 判属主,无边车存量计 unattributable 不删)and since core 7.61.0 the age bound `reap(now, { maxAgeMs })`
132
+ * — both are core `ToolResultStore`'s own optional members, so consumers optional-call them (`store.reap?.(…)`).
134
133
  * Structural (not a nominal union) so all three backends flow through one seam. */
135
- export type ToolResultStoreFull = ToolResultStore & {
136
- /** TTL sweep (SQL twins only): purge rows older than the cutoff. Local file store omits it (CC posture:
137
- * a single user's tool-result files persist like transcripts; bounded by being text previews). */
138
- reapOlderThan?(cutoffMs: number): Promise<number>;
139
- };
134
+ export type ToolResultStoreFull = ToolResultStore & {};
140
135
  /** Cross-replica counter twins expose the write-behind lifecycle (startRefresh/stop) main.ts drives. */
141
136
  export type BreakerStateStore = TiDBBreakerState | PgBreakerState;
142
137
  export type CostQuotaStore = TiDBCostQuota | PgCostQuota;
@@ -2,6 +2,7 @@ import { homedir } from "node:os";
2
2
  import { createCorruptReadSeat, createDurableAbsenceSeat } from "../observability/corrupt-read-seat.js";
3
3
  import { createLogger } from "../observability/logger.js";
4
4
  import { join } from "node:path";
5
+ import { existsSync } from "node:fs";
5
6
  import { AdoptionError, FileStorageBackend } from "@sema-agent/core";
6
7
  import { FileWorkflowJournalStore } from "@sema-agent/core";
7
8
  import { fileSessionCaptureRecordStore } from "@sema-agent/core";
@@ -58,7 +59,8 @@ import { TiDBAdoptionLogStore, PgAdoptionLogStore } from "./adoption-log-sql.js"
58
59
  import { TiDBRetentionStore, PgRetentionStore, sqlStoreRetention } from "./retention-store-sql.js";
59
60
  import { TiDBRetentionLaneStore, PgRetentionLaneStore } from "./retention-lane-store-sql.js";
60
61
  import { createSqlPermissionRuleStores } from "./permission-rule-store-sql.js";
61
- import { createFilePermissionRuleStores } from "./permission-rule-store-file.js";
62
+ import { FILE_PERMISSION_RULE_DIR, createFilePermissionRuleStores } from "./permission-rule-store-file.js";
63
+ import { retireImportedLayerRootAllows } from "./imported-layer-root-retirement.js";
62
64
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
63
65
  import { storeDegradedReasonFor } from "../store-posture.js";
64
66
  import { openWithinWait } from "./store-open-wait.js";
@@ -163,7 +165,7 @@ class TiDBBackend {
163
165
  breaker(onWriteFail) { return new TiDBBreakerState(this.pool, onWriteFail); }
164
166
  costQuota(limitMicroUsd, windowMs, onDegraded) { return new TiDBCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
165
167
  rateLimiter(limit, onDegraded) { return new TiDBRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
166
- session() { return new TiDBSessionStore(this.pool, { log: createLogger(this.config.logLevel), retention: sqlStoreRetention(this.config.retentionSweep) }); }
168
+ session() { return new TiDBSessionStore(this.pool, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
167
169
  adoptionLog() { return new TiDBAdoptionLogStore(this.pool); }
168
170
  mysqlPool() { return this.pool; }
169
171
  pgPool() { return undefined; }
@@ -217,7 +219,7 @@ class PgBackend {
217
219
  breaker(onWriteFail) { return new PgBreakerState(this.pool, onWriteFail); }
218
220
  costQuota(limitMicroUsd, windowMs, onDegraded) { return new PgCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
219
221
  rateLimiter(limit, onDegraded) { return new PgRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
220
- session() { return new PgSessionStore(this.pool, { log: createLogger(this.config.logLevel), retention: sqlStoreRetention(this.config.retentionSweep) }); }
222
+ session() { return new PgSessionStore(this.pool, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
221
223
  adoptionLog() { return new PgAdoptionLogStore(this.pool); }
222
224
  mysqlPool() { return undefined; }
223
225
  pgPool() { return this.pool; }
@@ -292,12 +294,18 @@ class LocalBackend {
292
294
  : (plane) => fileSessionCaptureRecordStore(plane?.controlDir ?? ownControlDir);
293
295
  }
294
296
  async ensureSchema() {
297
+ await this.sessionStore.assertStrictSidecarsReadable();
295
298
  await this.sessionStore.settleArchiveSnapshots();
296
299
  }
297
300
  async runOneTimeMigrations() {
298
301
  const holderDeathCodeRowsBackfilled = this.runStore.migrateLegacyHolderDeathCodes();
299
302
  const archives = await this.sessionStore.migrateLocalArchives();
300
303
  const placements = await backfillLocalExecutionPlacements(this.runStore.sessionIdsWithRuns(), this.sessionStore.executionPlacementBackfill, { lanes: this.executionLanes });
304
+ const importedLayerRoot = { importedLayerRootAllowsRetired: [], importedLayerRootBucketsSkipped: [] };
305
+ if (existsSync(join(this.fileBackend.root, FILE_PERMISSION_RULE_DIR))) {
306
+ const rules = this.permissionRule();
307
+ await retireImportedLayerRootAllows({ candidateOwners: () => rules.approvals.importRecordOwners(), durable: rules.durable, isWholeBucketRefusal: () => false, isVanishedBucket: () => false }, importedLayerRoot);
308
+ }
301
309
  return {
302
310
  ...NO_SQL_MIGRATIONS,
303
311
  holderDeathCodeRowsBackfilled,
@@ -310,6 +318,7 @@ class LocalBackend {
310
318
  localArchivesKeptStamped: archives.keptStamped,
311
319
  localArchivesUnreadable: archives.unreadable,
312
320
  localArchiveSnapshotsRecovered: archives.snapshotsRecovered,
321
+ ...importedLayerRoot,
313
322
  };
314
323
  }
315
324
  async close() {
@@ -578,6 +578,24 @@ export interface LatestRunRef {
578
578
  }
579
579
  /** 引擎对一条腿实际挂上的记忆读集的观测(core `TaskResult.effectiveMemoryScopes` 的值;core 包根不单独导出这只型,从结果型取)。 */
580
580
  export type EffectiveMemoryScopes = NonNullable<PersistedTaskResult["effectiveMemoryScopes"]>;
581
+ /**
582
+ * [ref] —— **空观测**的因由(闭集;词表属主 = 本文件,唯一铸点 = {@link settledLegObservationOf})。两词都只说**那条已结腿的记录里有什么**,不猜它为什么停 ——
583
+ * 停因的真源是那一条 run 自己的终局(`GET /v1/runs/:id` 的 `status` / `errorCode`),这里不复述第二份:
584
+ * · `run_result_missing` —— 记录里**没有结果**:本服务结算它时手里没有引擎的结果(持有它的实例丢了被收割、腿在引擎答出之前抛),或收割时写空了它(停驻过期被收割 ——
585
+ * [ref] 律:收割把停驻结果写空,停驻时那份结果里的观测随之不在;本读口不另存一份);
586
+ * · `run_result_unobserved` —— 记录里**有结果、结果里没有观测**:引擎的律是「缺席 ⇔ prepare 没走完」(引擎在准备阶段拒了 / 失败了它),
587
+ * 另两形同答 —— 引擎答出之前就被取消、由本服务铸的取消终局,与比这一观测更早的引擎写下的结果。
588
+ * 自铸 wire 值登记 = `trace/self-minted-wire.ts` 的 `sessionMemoryUnobservedReason`(`satisfies Record<…>`:加 / 删一个词而登记没跟 ⇒ tsc 红)。
589
+ */
590
+ export declare const MEMORY_OBSERVATION_ABSENT_REASONS: readonly ["run_result_missing", "run_result_unobserved"];
591
+ export type MemoryObservationAbsentReason = (typeof MEMORY_OBSERVATION_ABSENT_REASONS)[number];
592
+ /** [ref] —— 空观测记号:与引擎三态同一只字段、同一个判别位 `state`(闭集加一词 `absent`;引擎哪天自己长出同名一态 ⇒ `trace/core-keyset-guard.ts` 的跨界钉当场红)。 */
593
+ export interface AbsentMemoryObservation {
594
+ readonly state: "absent";
595
+ readonly reason: MemoryObservationAbsentReason;
596
+ }
597
+ /** 一条已结腿的记忆观测 —— 四态闭集:引擎铸的三态原样(`mounted` / `memoryless` / `none`)∪ 空观测({@link AbsentMemoryObservation})。消费方按 `state` 穷举分支。 */
598
+ export type LegMemoryObservation = EffectiveMemoryScopes | AbsentMemoryObservation;
581
599
  /**
582
600
  * 「这个会话**最近一条已结腿**与它的记忆观测」的窄读面(run 店的 `latestMemoryObservationForSession` 的回值)—— 会话记忆同步触发口
583
601
  * (`POST /v1/sessions/:id/memory/sync`)的**会话读集**只读这一只:读集 = 最近一条已结腿结果里引擎铸的观测(引擎一处铸、随 run 行持久;路由不重铸 spec)。
@@ -586,17 +604,23 @@ export type EffectiveMemoryScopes = NonNullable<PersistedTaskResult["effectiveMe
586
604
  * · 已结 = {@link isTerminalRunStatus}(成功 / 非成功终局;在飞与停在门上的腿还占着会话的认领,不算;读不懂的词同向保守判「未结」)。两孪生调这只谓词;
587
605
  * SQL 两方言按本文件的惯例写字面词集 `status IN ('completed','failed','blocked')`,与谓词的同源由 `test/source-hygiene-gate.test.ts` 的方言对 parity 门钉;
588
606
  * 取最近一条(排序口径与 {@link LatestRunRef} 逐字同一句:`created_at DESC, task_id DESC`);
589
- * · `effectiveMemoryScopes` = 那一条腿结果里的观测**原样**(三态:`mounted` / `memoryless` / `none`,读法归调用方);
590
- * · 那一条腿结了而**没有观测**(结果为空 / 结果里没有这一键 —— prepare 没走完)⇒ 交回那一条、观测缺席,**不往更早的腿找**
591
- * (读集是「最近一条已结腿」的,不是「最近一条有观测的腿」的)。
607
+ * · `observation` = 那一条腿的观测,**恒在场**,四态之一({@link LegMemoryObservation}):结果里有引擎的观测 ⇒ 原样;没有 ⇒ 空观测记号,因由说记录里有什么
608
+ * ({@link MemoryObservationAbsentReason})。**不往更早的腿找**:读集是「最近一条已结腿」的,不是「最近一条有观测的腿」的 —— 空观测是那一条的值,不是去找下一条的信号。
609
+ * 空观测**不落盘**:它是行上已有事实(结果列、结果里那一键的缺席)的一只纯函数,铸在读口({@link settledLegObservationOf},三店共用);落一份 = 第二真源
610
+ * (与引擎「缺席 ⇔ prepare 没走完」那条律并存、两份要对得上),且终局写口二十余处、收割器整行写空结果的 SQL 两方言语句与存量行都得各补一份。
592
611
  * 结果列读不出 ⇒ 抛(与 `getRun` 同一个失败方向:不把一条结了的腿当成没有结果)。
593
612
  */
594
613
  export interface SettledLegMemoryObservation {
595
614
  readonly taskId: string;
596
- readonly effectiveMemoryScopes: EffectiveMemoryScopes | undefined;
615
+ readonly observation: LegMemoryObservation;
597
616
  }
598
- /** {@link SettledLegMemoryObservation} 的一只投影(三只实现共用):一条已结腿的结果 → 读口的回值。 */
617
+ /** {@link SettledLegMemoryObservation} 的**唯一铸点**(三只实现共用):一条已结腿的结果 → 读口的回值(空观测记号只在这里铸)。 */
599
618
  export declare function settledLegObservationOf(taskId: string, result: PersistedTaskResult | null): SettledLegMemoryObservation;
619
+ /**
620
+ * run 店账本的一条事件 —— 三只孪生的 `getEvents` 都交出写口收下的那一形(按 seq 升序、每读一份新拷贝)。存形是孪生的私事、读者看不见:
621
+ * File 孪生把 `wiring_manifest` 行的容器段按会话存成块(`<root>/run-blobs/<会话>.jsonl`,律与唯一编解码 = `wiring-section-codec.ts`),行里只留引用,
622
+ * 读口装回整帧;装不回的那一帧不交出(F 类留痕),绝不交出缺段的清单。SQL / Memory 孪生整帧存。
623
+ */
600
624
  export interface RunEvent {
601
625
  seq: number;
602
626
  type: string;
@@ -56,7 +56,10 @@ export function sessionTempoOfRunStatus(lastStatus) {
56
56
  export function runProvenElsewhere(run, thisInstanceId) {
57
57
  return typeof run.instanceId === "string" && run.instanceId.length > 0 && thisInstanceId !== undefined && run.instanceId !== thisInstanceId;
58
58
  }
59
+ export const MEMORY_OBSERVATION_ABSENT_REASONS = ["run_result_missing", "run_result_unobserved"];
59
60
  export function settledLegObservationOf(taskId, result) {
60
- return { taskId, effectiveMemoryScopes: result?.effectiveMemoryScopes ?? undefined };
61
+ if (result === null)
62
+ return { taskId, observation: { state: "absent", reason: "run_result_missing" } };
63
+ return { taskId, observation: result.effectiveMemoryScopes ?? { state: "absent", reason: "run_result_unobserved" } };
61
64
  }
62
65
  //# sourceMappingURL=store-contracts.js.map
@@ -23,7 +23,7 @@
23
23
  * - 布尔列:无(遵 1.287.0 归一约定,若未来加,PG 用 SMALLINT 0/1)。
24
24
  * - 绑定:`bind` 把行钉到 sessionId,两条律都在店里、都不是先读后写(契约见 {@link TaskAttachmentStore.bind}):
25
25
  * **一经绑定不再改绑**(UPDATE 谓词只放行未绑的行与已绑本会话的行,行级原子;同会话重绑 —— 首交重试、续跑腿重放存量体 —— 不改 `bound_at_ms`);
26
- * **一只会话一只种子**(未绑的项目包只在这只会话还没有项目包时绑上:SQL 两方言是锚行加锁的一只事务,进程内的店是一段不被打断的判与改:内存店同一段同步,local 店的变更口同排一条串行段、bind 判在副本上、落成的行才合回)。
26
+ * **一只会话一只种子**(未绑的项目包只在这只会话还没有项目包时绑上:SQL 两方言是表上的唯一键 —— 种子键 `seed_session_id`(项目包行的会话,由引擎从 `mime` 与 `session_id` 推导)上的 `uq_att_seed`,第二只项目包的绑定被引擎拒;进程内的店是一段不被打断的判与改:内存店同一段同步,local 店的变更口同排一条串行段、bind 判在副本上、落成的行才合回)。
27
27
  * 同 session 多 task 共享自然成立(listBySession 是物化输入)。
28
28
  * 未绑定行 = 上传后从未被任何 task 引用 ⇒ reapUnbound(TTL)。
29
29
  */
@@ -259,7 +259,7 @@ export declare class PgTaskAttachmentStore implements TaskAttachmentStore {
259
259
  private readonly bytes;
260
260
  private readonly db;
261
261
  private readonly q;
262
- /** 收池(不是裸 `PgQueryFn`):绑定的种子腿要一只事务({@link bindOnSql}),事务要一条借出的连接。 */
262
+ /** 收池:绑定({@link bindOnSql})走与 TiDB 孪生同一只驱动形({@link pgDriver})。 */
263
263
  constructor(pool: PgPool, bytes: AttachmentBytesStore);
264
264
  put(rec: TaskAttachmentRecord & {
265
265
  content: Uint8Array;