@sema-agent/server 7.139.0 → 7.141.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 (328) hide show
  1. package/MIGRATION.md +471 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +94 -26
  5. package/dist/adoption/runner.js +1 -1
  6. package/dist/adoption/sql.js +1 -1
  7. package/dist/approval-batch-census.d.ts +27 -0
  8. package/dist/approval-batch-census.js +27 -0
  9. package/dist/approval-card.d.ts +37 -22
  10. package/dist/approval-card.js +6 -3
  11. package/dist/approval.d.ts +15 -0
  12. package/dist/approval.js +10 -0
  13. package/dist/audit.d.ts +4 -2
  14. package/dist/audit.js +2 -2
  15. package/dist/bench/s1/live-deps.js +3 -2
  16. package/dist/bench/s1/row.d.ts +5 -2
  17. package/dist/bench/s1/row.js +2 -22
  18. package/dist/boot/adoption.js +1 -1
  19. package/dist/boot/device-lane.d.ts +11 -0
  20. package/dist/boot/device-lane.js +16 -0
  21. package/dist/boot/engine-lease.d.ts +24 -3
  22. package/dist/boot/engine-lease.js +9 -2
  23. package/dist/boot/execution-env.js +18 -12
  24. package/dist/boot/execution-lane.d.ts +8 -0
  25. package/dist/boot/execution-lane.js +6 -1
  26. package/dist/boot/memory-boundary.d.ts +16 -53
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +16 -12
  29. package/dist/boot/memory-consolidation.js +24 -13
  30. package/dist/boot/reapers.d.ts +2 -2
  31. package/dist/boot/resolve-spec.d.ts +3 -5
  32. package/dist/boot/resolve-spec.js +9 -8
  33. package/dist/boot/runner-deps.d.ts +3 -3
  34. package/dist/boot/runner-deps.js +1 -0
  35. package/dist/boot/session-faces.d.ts +1 -1
  36. package/dist/boot/session-faces.js +4 -3
  37. package/dist/boot/shutdown.js +5 -0
  38. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  39. package/dist/boot/stage-03-store-backend.js +2 -3
  40. package/dist/boot/stage-04-stores.d.ts +2 -6
  41. package/dist/boot/stage-04-stores.js +5 -5
  42. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  43. package/dist/boot/stage-05-execution-env.js +3 -1
  44. package/dist/boot/stage-06-runners.d.ts +2 -8
  45. package/dist/boot/stage-06-runners.js +3 -3
  46. package/dist/boot/stage-07-capability-layer.d.ts +2 -7
  47. package/dist/boot/stage-07-capability-layer.js +8 -10
  48. package/dist/boot/stage-08-reapers.d.ts +2 -7
  49. package/dist/boot/stage-09-leader.d.ts +2 -7
  50. package/dist/boot/stage-10-http-server.d.ts +2 -7
  51. package/dist/boot/stage-10-http-server.js +6 -4
  52. package/dist/boot/stores.d.ts +3 -6
  53. package/dist/boot/stores.js +37 -44
  54. package/dist/boot/task-list-lane.d.ts +1 -1
  55. package/dist/boot/task-list-lane.js +1 -1
  56. package/dist/boot/web-admin.js +36 -9
  57. package/dist/capabilities/execution-lane.d.ts +2 -2
  58. package/dist/capabilities/scenarios.d.ts +1 -1
  59. package/dist/capability-facts.d.ts +9 -0
  60. package/dist/capability-facts.js +9 -0
  61. package/dist/card-void.d.ts +38 -0
  62. package/dist/card-void.js +23 -0
  63. package/dist/config-catalog.js +12 -8
  64. package/dist/config-provider.d.ts +18 -17
  65. package/dist/config-provider.js +28 -36
  66. package/dist/config-types.d.ts +36 -9
  67. package/dist/config.js +56 -46
  68. package/dist/delegated-session-owner.d.ts +23 -20
  69. package/dist/delegated-session-owner.js +2 -2
  70. package/dist/deployment-shape.d.ts +9 -8
  71. package/dist/deployment-shape.js +4 -4
  72. package/dist/device-store.d.ts +7 -0
  73. package/dist/device-store.js +3 -0
  74. package/dist/execution-config-root.d.ts +22 -0
  75. package/dist/execution-config-root.js +35 -0
  76. package/dist/execution-lane-caps.d.ts +145 -107
  77. package/dist/execution-lane-caps.js +144 -83
  78. package/dist/execution-placement.d.ts +3 -0
  79. package/dist/execution-placement.js +7 -0
  80. package/dist/fleet/drain-entry.d.ts +3 -0
  81. package/dist/hosted-posture.d.ts +8 -0
  82. package/dist/hosted-posture.js +4 -0
  83. package/dist/http/admission.js +3 -3
  84. package/dist/http/caller-trust.d.ts +3 -2
  85. package/dist/http/caller-trust.js +2 -1
  86. package/dist/http/dispatch.js +4 -0
  87. package/dist/http/live-frame.d.ts +24 -0
  88. package/dist/http/live-frame.js +16 -0
  89. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  90. package/dist/http/memory-compliance-failure-reply.js +5 -0
  91. package/dist/http/memory-layer-reply.d.ts +30 -0
  92. package/dist/http/memory-layer-reply.js +24 -0
  93. package/dist/http/resume-legs.js +49 -35
  94. package/dist/http/retired-service-deps.d.ts +1 -1
  95. package/dist/http/retired-service-deps.js +1 -1
  96. package/dist/http/route-table.js +2 -0
  97. package/dist/http/routes/adoption.js +1 -1
  98. package/dist/http/routes/approvals-assistant.js +1 -0
  99. package/dist/http/routes/capabilities.js +5 -4
  100. package/dist/http/routes/diagnostics.d.ts +0 -19
  101. package/dist/http/routes/diagnostics.js +16 -2
  102. package/dist/http/routes/memory-bundle.d.ts +2 -2
  103. package/dist/http/routes/memory-bundle.js +4 -1
  104. package/dist/http/routes/memory-compliance.js +9 -2
  105. package/dist/http/routes/memory-consolidation.js +12 -1
  106. package/dist/http/routes/memory-origin.js +14 -2
  107. package/dist/http/routes/memory-policy.js +7 -2
  108. package/dist/http/routes/run-permission-mode.d.ts +75 -0
  109. package/dist/http/routes/run-permission-mode.js +170 -0
  110. package/dist/http/routes/session-sync.d.ts +3 -6
  111. package/dist/http/routes/session-sync.js +7 -2
  112. package/dist/http/routes/sessions.js +33 -15
  113. package/dist/http/routes/tasks.js +67 -68
  114. package/dist/http/run-read-face.js +3 -3
  115. package/dist/http/send.d.ts +1 -1
  116. package/dist/http/send.js +2 -2
  117. package/dist/http/server.d.ts +14 -27
  118. package/dist/http/server.js +2 -2
  119. package/dist/http/sse-log.d.ts +3 -8
  120. package/dist/http/stored-body-replay.d.ts +3 -3
  121. package/dist/http/stored-body-replay.js +3 -1
  122. package/dist/identity-source.d.ts +1 -1
  123. package/dist/identity-source.js +1 -1
  124. package/dist/instruction-source-notice.d.ts +7 -0
  125. package/dist/instruction-source-notice.js +6 -6
  126. package/dist/lane-home-probe.d.ts +68 -42
  127. package/dist/lane-home-probe.js +53 -30
  128. package/dist/leader/wire.js +5 -4
  129. package/dist/mcp-assertion-header.d.ts +4 -0
  130. package/dist/mcp-assertion-header.js +5 -0
  131. package/dist/memory-bundle-engine.d.ts +16 -9
  132. package/dist/memory-bundle-engine.js +29 -6
  133. package/dist/memory-operator-faces.d.ts +85 -30
  134. package/dist/memory-operator-faces.js +136 -30
  135. package/dist/memory-posture.d.ts +1 -1
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +33 -9
  141. package/dist/memory-scope.js +17 -5
  142. package/dist/memory-sync-client.d.ts +34 -9
  143. package/dist/memory-sync-client.js +108 -28
  144. package/dist/observability/fail-open.d.ts +30 -2
  145. package/dist/observability/fail-open.js +30 -2
  146. package/dist/observability/metrics.js +0 -1
  147. package/dist/observability/run-terminal.d.ts +6 -2
  148. package/dist/observability/run-terminal.js +25 -26
  149. package/dist/observability/secret-env-scrub.d.ts +20 -1
  150. package/dist/observability/secret-env-scrub.js +14 -0
  151. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
  152. package/dist/orchestration/workflow-completion-inbox.js +13 -5
  153. package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
  154. package/dist/orchestration/workflow-notify-journal.js +2 -1
  155. package/dist/permission-mode-admission.d.ts +68 -0
  156. package/dist/permission-mode-admission.js +23 -0
  157. package/dist/permission-rule-list.d.ts +55 -6
  158. package/dist/permission-rule-list.js +52 -5
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-sql.js +14 -14
  161. package/dist/plugins/approval-nonce-store.js +4 -4
  162. package/dist/plugins/blob-backend.d.ts +4 -4
  163. package/dist/plugins/blob-backend.js +7 -7
  164. package/dist/plugins/breaker-state-sql.js +2 -2
  165. package/dist/plugins/caching-session-store.d.ts +6 -0
  166. package/dist/plugins/caching-session-store.js +3 -0
  167. package/dist/plugins/checkpoint-index-columns.js +2 -2
  168. package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
  169. package/dist/plugins/checkpoint-store-sql.js +90 -19
  170. package/dist/plugins/device-store-sql.d.ts +5 -2
  171. package/dist/plugins/device-store-sql.js +23 -20
  172. package/dist/plugins/ensure-index.d.ts +1 -2
  173. package/dist/plugins/execution-placement-sql.d.ts +19 -9
  174. package/dist/plugins/execution-placement-sql.js +20 -9
  175. package/dist/plugins/file-history-store-sql.d.ts +2 -2
  176. package/dist/plugins/file-history-store-sql.js +4 -4
  177. package/dist/plugins/file-run-store.d.ts +2 -2
  178. package/dist/plugins/file-run-store.js +1 -1
  179. package/dist/plugins/fork-routing-session-store.d.ts +10 -32
  180. package/dist/plugins/fork-routing-session-store.js +2 -61
  181. package/dist/plugins/image-bake-store-sql.js +9 -9
  182. package/dist/plugins/image-index-sql.js +11 -11
  183. package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
  184. package/dist/plugins/k8s-exec-protocol.js +6 -1
  185. package/dist/plugins/leader-run-store-sql.js +4 -4
  186. package/dist/plugins/local-checkpoint-store.d.ts +10 -1
  187. package/dist/plugins/local-checkpoint-store.js +22 -0
  188. package/dist/plugins/local-session-store.d.ts +227 -35
  189. package/dist/plugins/local-session-store.js +518 -37
  190. package/dist/plugins/local-session-window.d.ts +17 -3
  191. package/dist/plugins/local-session-window.js +7 -13
  192. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  193. package/dist/plugins/mailbox-store-sql.js +4 -4
  194. package/dist/plugins/memory-engine-tidb.js +5 -5
  195. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  196. package/dist/plugins/memory-run-store.d.ts +2 -2
  197. package/dist/plugins/memory-run-store.js +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +52 -11
  200. package/dist/plugins/one-time-migrations.js +101 -39
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.js +2 -2
  203. package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
  204. package/dist/plugins/permission-rule-store-sql.js +16 -16
  205. package/dist/plugins/pg-pool.js +3 -2
  206. package/dist/plugins/pg-session-storage.d.ts +2 -0
  207. package/dist/plugins/pg-session-storage.js +3 -2
  208. package/dist/plugins/remote-env-adb.d.ts +7 -24
  209. package/dist/plugins/remote-env-adb.js +22 -5
  210. package/dist/plugins/remote-env-device.d.ts +2 -28
  211. package/dist/plugins/remote-env-device.js +2 -1
  212. package/dist/plugins/remote-env-e2b.d.ts +40 -27
  213. package/dist/plugins/remote-env-e2b.js +180 -20
  214. package/dist/plugins/remote-env-file-error.d.ts +1 -1
  215. package/dist/plugins/remote-env-file-error.js +8 -2
  216. package/dist/plugins/remote-env-host.d.ts +19 -35
  217. package/dist/plugins/remote-env-host.js +26 -3
  218. package/dist/plugins/remote-env-k8s.d.ts +22 -14
  219. package/dist/plugins/remote-env-k8s.js +150 -26
  220. package/dist/plugins/remote-env-local-docker.d.ts +27 -24
  221. package/dist/plugins/remote-env-local-docker.js +91 -24
  222. package/dist/plugins/remote-env-ssh.d.ts +4 -24
  223. package/dist/plugins/remote-env-ssh.js +4 -3
  224. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  225. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  226. package/dist/plugins/retention-lane-store-sql.js +2 -2
  227. package/dist/plugins/retention-store-sql.d.ts +1 -1
  228. package/dist/plugins/retention-store-sql.js +6 -6
  229. package/dist/plugins/run-store-sql.d.ts +3 -3
  230. package/dist/plugins/run-store-sql.js +19 -18
  231. package/dist/plugins/sandbox-read-floor.d.ts +273 -0
  232. package/dist/plugins/sandbox-read-floor.js +608 -0
  233. package/dist/plugins/schema-consistency.d.ts +6 -6
  234. package/dist/plugins/schema-consistency.js +1 -1
  235. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  236. package/dist/plugins/schema-ensure-legs.js +2 -2
  237. package/dist/plugins/schema-live-columns.d.ts +1 -1
  238. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  239. package/dist/plugins/session-ownership.d.ts +2 -1
  240. package/dist/plugins/session-placement.d.ts +17 -4
  241. package/dist/plugins/session-placement.js +2 -1
  242. package/dist/plugins/session-rules-column.d.ts +1 -1
  243. package/dist/plugins/session-rules-column.js +1 -1
  244. package/dist/plugins/session-store.d.ts +18 -20
  245. package/dist/plugins/session-store.js +4 -12
  246. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  247. package/dist/plugins/session-workspace-cell.js +1 -1
  248. package/dist/plugins/shared-memory-store-sql.js +2 -2
  249. package/dist/plugins/sql-driver.d.ts +10 -5
  250. package/dist/plugins/sql-driver.js +6 -6
  251. package/dist/plugins/sql-errors.js +1 -1
  252. package/dist/plugins/store-backend.d.ts +35 -6
  253. package/dist/plugins/store-backend.js +27 -10
  254. package/dist/plugins/store-contracts.d.ts +9 -2
  255. package/dist/plugins/task-list-store-sql.js +11 -11
  256. package/dist/plugins/tidb-pool.js +7 -6
  257. package/dist/plugins/tidb-session-storage.js +4 -4
  258. package/dist/plugins/tidb-session-store.d.ts +4 -1
  259. package/dist/plugins/tidb-session-store.js +16 -15
  260. package/dist/plugins/tool-result-store-sql.d.ts +2 -2
  261. package/dist/plugins/tool-result-store-sql.js +3 -3
  262. package/dist/plugins/user-input-ledger-sql.js +2 -2
  263. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  264. package/dist/plugins/workflow-run-store-sql.js +19 -19
  265. package/dist/plugins/write-behind-counter.d.ts +2 -3
  266. package/dist/plugins/write-behind-counter.js +5 -5
  267. package/dist/resource-suspend.d.ts +14 -12
  268. package/dist/resource-suspend.js +1 -1
  269. package/dist/resource-window.d.ts +4 -3
  270. package/dist/run-cancel-context.d.ts +34 -0
  271. package/dist/run-cancel-context.js +7 -0
  272. package/dist/run-local.js +14 -19
  273. package/dist/runs.d.ts +47 -16
  274. package/dist/runs.js +112 -113
  275. package/dist/security.d.ts +2 -2
  276. package/dist/server-secret-env.d.ts +2 -1
  277. package/dist/server-secret-env.js +2 -4
  278. package/dist/session-error-wire.d.ts +22 -4
  279. package/dist/session-error-wire.js +16 -0
  280. package/dist/session-incarnation.d.ts +10 -4
  281. package/dist/session-incarnation.js +1 -1
  282. package/dist/session-policy-wire.d.ts +1 -1
  283. package/dist/session-sync-kernel.d.ts +42 -1
  284. package/dist/session-sync-kernel.js +17 -0
  285. package/dist/session-sync.js +16 -4
  286. package/dist/sql-engine-coordinates.d.ts +49 -0
  287. package/dist/sql-engine-coordinates.js +27 -0
  288. package/dist/sql-engine-posture.d.ts +1 -1
  289. package/dist/sql-engine-posture.js +1 -1
  290. package/dist/standing-answer-grant.d.ts +31 -2
  291. package/dist/standing-answer-grant.js +14 -2
  292. package/dist/store-posture.d.ts +1 -1
  293. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  294. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  295. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  296. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  297. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  298. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  299. package/dist/task-a2a.d.ts +1 -1
  300. package/dist/task-a2a.js +5 -6
  301. package/dist/task-mcp.d.ts +21 -8
  302. package/dist/task-mcp.js +17 -9
  303. package/dist/task-settings.d.ts +52 -25
  304. package/dist/task-settings.js +91 -28
  305. package/dist/terminal.d.ts +103 -19
  306. package/dist/terminal.js +72 -20
  307. package/dist/trace/core-keyset-guard.d.ts +79 -2
  308. package/dist/trace/exit-seat.d.ts +7 -0
  309. package/dist/trace/exit-seat.js +1 -0
  310. package/dist/trace/model-segment.d.ts +2 -2
  311. package/dist/trace/redact.d.ts +3 -1
  312. package/dist/trace/redact.js +395 -110
  313. package/dist/trace/self-minted-wire.d.ts +226 -2
  314. package/dist/trace/self-minted-wire.js +79 -0
  315. package/dist/verifier-pause-card.d.ts +8 -0
  316. package/dist/verifier-pause-card.js +19 -0
  317. package/dist/web-admin-client/apply-effective.d.ts +5 -7
  318. package/dist/web-admin-client/apply-effective.js +2 -2
  319. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  320. package/dist/web-admin-client/candidate-gate.js +1 -1
  321. package/dist/web-admin-client/http-client.d.ts +5 -4
  322. package/dist/web-admin-client/http-client.js +2 -2
  323. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  324. package/dist/web-admin-client/read-warnings.js +5 -9
  325. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
  326. package/package.json +2 -2
  327. package/wire-changes/server@7.140.0.json +184 -0
  328. package/wire-changes/server@7.141.0.json +367 -0
@@ -0,0 +1,86 @@
1
+ import { resolveMemoryScopeStore } from "@sema-agent/core";
2
+ import { memoryScopeContractOf } from "./memory-scope.js";
3
+ export function createMemoryScopeStores(input) {
4
+ const byPlane = new Map();
5
+ const resolve = (layer, backend) => resolveMemoryScopeStore({
6
+ scope: layer,
7
+ scopeContract: memoryScopeContractOf([layer]),
8
+ backend,
9
+ memoryEngineDir: input.root,
10
+ ...(input.onIncident !== undefined ? { onIncident: input.onIncident } : {}),
11
+ });
12
+ return {
13
+ backend: input.backend,
14
+ root: input.root,
15
+ namedLayers: input.namedLayers,
16
+ storeOf(layer) {
17
+ const { plane } = resolve(layer, PLANE_PROBE);
18
+ const known = byPlane.get(plane);
19
+ if (known !== undefined)
20
+ return known;
21
+ const store = resolve(layer, input.backend).backend;
22
+ byPlane.set(plane, store);
23
+ return store;
24
+ },
25
+ };
26
+ }
27
+ const PLANE_PROBE = Object.freeze({});
28
+ export class MemoryLayerRefusal extends Error {
29
+ code = "request.memory_layer";
30
+ constructor(message, cause) {
31
+ super(message, cause !== undefined ? { cause } : undefined);
32
+ this.name = "MemoryLayerRefusal";
33
+ }
34
+ }
35
+ const quoted = (layers) => layers.map((l) => JSON.stringify(l)).join(", ");
36
+ function storeOfNamed(stores, layer, whose, seat) {
37
+ try {
38
+ return stores.storeOf(layer);
39
+ }
40
+ catch (err) {
41
+ if (err?.code !== "config.memory_scope_key")
42
+ throw err;
43
+ const why = err instanceof Error ? err.message : String(err);
44
+ throw new MemoryLayerRefusal(whose === "request"
45
+ ? `the memory layer ${JSON.stringify(layer)} cannot be read: ${why}`
46
+ : `this deployment's configured memory layer ${JSON.stringify(layer)} cannot be read (${why}), so the store a request that names no layer belongs to cannot be told — name the layer in the request (${seat})`, err);
47
+ }
48
+ }
49
+ export function memoryStoreForRequest(stores, layers, seat = "`scope`") {
50
+ if (layers.length > 0) {
51
+ const groups = new Map();
52
+ for (const layer of layers) {
53
+ const store = storeOfNamed(stores, layer, "request", seat);
54
+ const group = groups.get(store);
55
+ if (group === undefined)
56
+ groups.set(store, [layer]);
57
+ else
58
+ group.push(layer);
59
+ }
60
+ if (groups.size === 1)
61
+ return [...groups.keys()][0];
62
+ const split = [...groups.values()].map((g) => `[${quoted(g)}]`).join(" / ");
63
+ throw new MemoryLayerRefusal(`the memory layers ${quoted(layers)} live in ${groups.size} different memory stores on this deployment and one request reaches one store — send one request per store: ${split}`);
64
+ }
65
+ const groups = deploymentStoreGroups(stores, seat);
66
+ if (groups.size === 1)
67
+ return stores.backend;
68
+ const elsewhere = [...groups.entries()].filter(([store]) => store !== stores.backend).flatMap(([, g]) => g);
69
+ throw new MemoryLayerRefusal(`this deployment keeps memory layers in ${groups.size} stores — the project layers${groups.get(stores.backend).length > 0 ? ` and ${quoted(groups.get(stores.backend))}` : ""} in the deployment's store, ${quoted(elsewhere)} in another — so a request that names no layer cannot be told which store it means; name the layer in the request (${seat})`);
70
+ }
71
+ export function memoryStoresOfDeployment(stores, seat = "`scope`") {
72
+ return [...deploymentStoreGroups(stores, seat).keys()];
73
+ }
74
+ function deploymentStoreGroups(stores, seat) {
75
+ const groups = new Map([[stores.backend, []]]);
76
+ for (const layer of stores.namedLayers()) {
77
+ const store = storeOfNamed(stores, layer, "deployment", seat);
78
+ const group = groups.get(store);
79
+ if (group === undefined)
80
+ groups.set(store, [layer]);
81
+ else
82
+ group.push(layer);
83
+ }
84
+ return groups;
85
+ }
86
+ //# sourceMappingURL=memory-scope-stores.js.map
@@ -8,11 +8,35 @@ export declare const V2_SCOPE_PREFIXES: readonly string[];
8
8
  * 一个 scope 键是否带 v2 typed 前缀。
9
9
  *
10
10
  * ⚠️ 判据刻意停在**前缀**层,不升级成 core 的 `parseScopeKey`:后者对「带可识别前缀但结构坏」的键
11
- * 是 fail-loud 抛错,而本谓词的两个调用点都只是在决定「要不要挂 v2 标 / 要不要再包一层」——把抛点
12
- * 挪到这里会把 core 的校验时机整体前移(行为面改动,不在本条案的射程内)。坏键仍由 core 在它自己的
13
- * 校验点 fail-loud。
11
+ * 是 fail-loud 抛错,而本谓词的两个读者都只是在决定「要不要挂 v2 标({@link memoryScopeContractOf})/
12
+ * 同步层要不要另给(`config.ts`)」——把抛点挪到这里会把 core 的校验时机整体前移(行为面改动,不在本条案的射程内)。
13
+ * 坏键仍由 core 在它自己的校验点 fail-loud。
14
14
  */
15
15
  export declare function isV2ScopeKey(key: string): boolean;
16
+ /**
17
+ * 本服务给一组记忆层盖的**作用域契约**(core `TaskSpec.memory.scopeContract`)—— 一个判据、两个读者:
18
+ * · {@link memorySpecForRequest} 铸 spec 时盖在会话上的那一个(读列表 = 全局层 + 登记簿种子);
19
+ * · 同步执行器替一层问「这一层在哪只店」时报给引擎的那一个(`resolveMemoryScopeStore` 的 `scopeContract`,`memory-sync-client.ts`)。
20
+ * 契约决定**类型化键落哪个平面**(v2 下 `user:` / `userproj:` / `org:` 在个人平面,不透明键在项目平面;不声明则一律项目平面),
21
+ * 所以两个读者必须读同一只:会话把一层放在哪只店,同步就同步哪只店。
22
+ *
23
+ * 判据:列表里有类型化键(前缀表 {@link V2_SCOPE_PREFIXES})⇒ `"v2"`;没有 ⇒ `undefined`(不声明;显式 opt-in,不嗅探调用方数据 ——
24
+ * 这些键只来自本服务自己的铸点与运维的登记簿,见 {@link memorySpecForRequest} 的 142-S1.5 注)。
25
+ * 按层问与按会话问答同一只店:平面按**每把键自己的种类**分,契约只挪类型化键,而本判据下带类型化键的会话恒盖 `"v2"`。
26
+ * 🚧 前提是键为规范形:引擎读键前去掉首尾空白,本判据看原串 —— 带首尾空白的类型化键(`" user:alice"`,配置边界今天放行)本判据不认、引擎在 v2 下读得出,
27
+ * 于是同一把键在带类型化种子的会话里落个人平面、在不带种子的会话与同步执行器里落项目平面。这是会话面先有的不一致(不同项目的会话对同一层就答两只店,
28
+ * 同步执行器无从同时对上两者),收口点在配置边界(拒或规范化这一形,后者会挪存量记忆的平面),不在本谓词;未闭环。
29
+ */
30
+ export declare function memoryScopeContractOf(scopes: readonly string[]): "v2" | undefined;
31
+ /**
32
+ * 本部署**自己点名**的记忆层 —— 配置里写着的每一层(去重、去空串、保序):单用户的全局层(`MEMORY_SCOPE`,{@link memoryScopeFor} 单用户臂的答案)、
33
+ * 同步层(`MEMORY_SYNC_SCOPE`,缺省即全局层)、项目登记簿的种子层(`config.projects[*].defaultScopes`,{@link memorySpecForRequest} 盖进会话的读列表)、
34
+ * 整理层(`MEMORY_CONSOLIDATION_SCOPES`)。项目层(`userrepo:…`)按任务根铸、不在配置里,也就不在这里 —— 它们住部署的店。
35
+ *
36
+ * 唯一读者:记忆作用域店的「不点名层的动词答在哪只店」(`memory-scope-stores.ts` 的 {@link memoryStoreForRequest} 无层那一臂)。
37
+ * 登记簿随中心配置热换(`applyEffective` 在同一只 `config.projects` 对象上原地整表换内容),所以读者每次现调本函数,不在起服时冻一份。
38
+ */
39
+ export declare function memoryLayersNamedBy(config: Pick<ServiceConfig, "requirePrincipal" | "memoryScope" | "memorySync" | "projects" | "memoryConsolidationDriver">): string[];
16
40
  /** Resolve the memory scope for a request —— {@link memoryScopeFor} 的三形(按实现逐臂写;[ref] 合并前主会话点名:
17
41
  * 旧版这里写「多租户恒 `undefined` ⇒ 记忆暗」,与下面 DB 后端那一臂正好相反,test [ref] 就是被这句带偏的)。
18
42
  *
@@ -22,7 +46,7 @@ export declare function isV2ScopeKey(key: string): boolean;
22
46
  * **读列表的第二层**,不是写家:写家是该任务的**项目层**,由 {@link memoryProjectKeyFor} 按任务根派生、经
23
47
  * {@link memorySpecForRequest} 的 `projectKey` 位铸进 spec(读 = [项目, 本 scope],`writeScope` 显式 = 项目)。
24
48
  * 本函数在授权期调(还不知道任务根),所以项目层不在这里。
25
- * ② MULTI-TENANT + DB 后端(`requirePrincipal === true` ∧ `memoryEngineBackend` ∈ {`pg`, `tidb`}):按**验过的**
49
+ * ② MULTI-TENANT + DB 后端(`requirePrincipal === true` ∧ `memoryEngineBackend` ∈ {`pg`, `mysql`}):按**验过的**
26
50
  * principal 铸 v2 typed 键 —— 无 projectId ⇒ `user:<tenant>`(core `formatUserScope`),有形状合法的
27
51
  * projectId ⇒ `proj:<tenant>/<projectId>`(core `formatProjScope`);principal 缺席(匿名 run)⇒ `undefined`
28
52
  * (无记忆)。scope 列是租户分区,授权就是「验过的 principal 进、一个租户的 scope 出」这一绑定;身份永远不来自
@@ -93,8 +117,8 @@ export declare function replicatesProjectLayersOf(coverage: MemoryReplicationCov
93
117
  * Pure posture probe (memoryEngineBackendFor consumes it; main.ts logs it): `undefined` = no plane split
94
118
  * (the lane's file plane IS this worker's fs — `in-process` / `host`), `dark` = split + fail-closed, `forced` =
95
119
  * split + operator override.
96
- * [ref]: the plane question reads the lane-caps table (`hostFilePlane`) — it used to be the hand-written synonym
97
- * `lane === undefined || lane === "host"` (fold-ban gate D, R3); readings are unchanged lane by lane.
120
+ * The plane question reads the execution environment block (`hostLocalPaths`, sole author `capabilitiesFor`) — it used to be the
121
+ * hand-written synonym `lane === undefined || lane === "host"`; readings are unchanged lane by lane.
98
122
  */
99
123
  export declare function memoryEngineRemoteLanePosture(config: ServiceConfig): {
100
124
  lane: ExecutionLane;
@@ -104,7 +128,7 @@ export declare function memoryEngineRemoteLanePosture(config: ServiceConfig): {
104
128
  * N0 启动告警的**文案**(纯,可单测;boot/stores.ts 的装配点只负责喂「引擎这一腿真的接上了吗」并打日志)。
105
129
  *
106
130
  * 为什么要按引擎真身分腿(2026-08-12 复扫,已核真):{@link memoryEngineRemoteLanePosture} 的 `dark` 只对
107
- * **file** 引擎腿有裁决权 —— 车道门的唯一消费者是 {@link memoryEngineBackendFor}。`MEMORY_ENGINE_BACKEND=pg|tidb`
131
+ * **file** 引擎腿有裁决权 —— 车道门的唯一消费者是 {@link memoryEngineBackendFor}。`MEMORY_ENGINE_BACKEND=pg|mysql`
108
132
  * 的两条腿在 boot/stores.ts 里**先于**任何车道判断就被点亮(库是持久真身,与手的文件平面无关),于是同一次
109
133
  * 启动会先打 `memory_engine_enabled {enabled:true, backend:"pg"}`,几行之后再打一句「memory dark
110
134
  * (fail-closed) … Set MEMORY_ENGINE_REMOTE_LANE=allow」—— 两句直接对撞,而且把运维指向一个在该腿上**不接线**
@@ -148,13 +172,13 @@ engineWired: boolean): {
148
172
  * (壳布局 `~/.sema/engine-data`)」时分家 ⇒ 记忆库每一次 Write 被硬拒(7.58.0 放行 ⇒ 回归);反向
149
173
  * (`CONFIG_LOCAL_DIR=~/.sema` 当 dataRoot)则让配置根自身免检。两根本就是两件事:店根归誊本门,引擎根归本函数。
150
174
  *
151
- * 形与 boot/stores.ts 逐字同律:DB 记忆腿(pg/tidb)的根 = `<localDataRoot ?? localRoot>/memory-work`
175
+ * 形与 boot/stores.ts 逐字同律:DB 记忆腿(pg/mysql)的根 = `<localDataRoot ?? localRoot>/memory-work`
152
176
  * (per-worker 物化区);file 腿 = boot file 后端的链 `resolveMemoryEngineRoot(memoryEngineDir)`。**不判引擎是否
153
177
  * 启用**——纯路径推导,禁用态下豁免根仍以「若开引擎会写到哪」为根(boot 的 `engineDataRoot` 恒铸,经共享基座交每只 Runner)。
154
178
  */
155
179
  export declare function memoryEngineRootFor(config: Pick<ServiceConfig, "memoryEngineBackend" | "memoryEngineDir" | "localDataRoot">, localRoot?: string): string;
156
180
  /** file 后端的根链(`MEMORY_ENGINE_DIR ?? fallback ?? $AGENT_DATA_DIR ?? ~/.ai-agent`)—— 与后端选择器**无关**:
157
- * `memoryEngineBackendFor` 恒造 file 后端(run-local 从 env 继承 `MEMORY_ENGINE_BACKEND=pg|tidb` 时仍是 file),
181
+ * `memoryEngineBackendFor` 恒造 file 后端(run-local 从 env 继承 `MEMORY_ENGINE_BACKEND=pg|mysql` 时仍是 file),
158
182
  * 所以它的根必须走这条链而不是 `memoryEngineRootFor` 的分派(codex 修复验证轮 [medium]:分派会把 run-local 的
159
183
  * 记忆搬到 `<localDataRoot>/memory-work`,存量记忆与控制面升级后隐形)。 */
160
184
  export declare function fileMemoryEngineRoot(config: Pick<ServiceConfig, "memoryEngineDir">, fallbackRoot?: string): string;
@@ -1,6 +1,6 @@
1
1
  import { join } from "node:path";
2
2
  import { FileMemoryEngineBackend, resolveMemoryEngineRoot, deriveControlPlaneDir, formatUserScope, formatProjScope, normalizeMemorySpec } from "@sema-agent/core";
3
- import { executionLaneCaps, executionLaneOf } from "./execution-lane-caps.js";
3
+ import { capabilitiesFor, executionLaneOf } from "./execution-lane-caps.js";
4
4
  import { assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
5
5
  import { projectLayerForTaskRoot } from "./project-identity.js";
6
6
  import { parseProjectLayerKey, projectLayerOwnerOf } from "./project-layer-key.js";
@@ -14,10 +14,22 @@ export const V2_SCOPE_PREFIXES = Object.freeze(Object.values(V2_SCOPE_PREFIX_BY_
14
14
  export function isV2ScopeKey(key) {
15
15
  return V2_SCOPE_PREFIXES.some((prefix) => key.startsWith(prefix));
16
16
  }
17
+ export function memoryScopeContractOf(scopes) {
18
+ return scopes.some(isV2ScopeKey) ? "v2" : undefined;
19
+ }
20
+ export function memoryLayersNamedBy(config) {
21
+ const named = [
22
+ ...(config.requirePrincipal === true ? [] : [config.memoryScope]),
23
+ ...(config.memorySync !== undefined ? [config.memorySync.scope] : []),
24
+ ...Object.values(config.projects ?? {}).flatMap((p) => p.defaultScopes ?? []),
25
+ ...config.memoryConsolidationDriver.scopes,
26
+ ];
27
+ return named.filter((s, i) => typeof s === "string" && s.length > 0 && named.indexOf(s) === i);
28
+ }
17
29
  export { MEMORY_SCOPE_COLUMN_CHARS, assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
18
30
  export function memoryScopeFor(config, principal, projectId) {
19
31
  if (config.requirePrincipal === true) {
20
- if (config.memoryEngineBackend === "pg" || config.memoryEngineBackend === "tidb") {
32
+ if (config.memoryEngineBackend === "pg" || config.memoryEngineBackend === "mysql") {
21
33
  if (!principal)
22
34
  return undefined;
23
35
  const scope = projectId ? formatProjScope(principal, projectId) : formatUserScope(principal);
@@ -64,7 +76,7 @@ export function replicatesProjectLayersOf(coverage, owner) {
64
76
  }
65
77
  export function memoryEngineRemoteLanePosture(config) {
66
78
  const lane = executionLaneOf(config.remoteExec?.provider);
67
- if (executionLaneCaps(lane).hostFilePlane === "supported")
79
+ if (capabilitiesFor(config).hostLocalPaths)
68
80
  return undefined;
69
81
  return { lane, posture: config.memoryEngineRemoteLaneAllowed === true ? "forced" : "dark" };
70
82
  }
@@ -158,14 +170,14 @@ export function memorySpecForRequest(scope, projectKey, memoryWrite, defaultScop
158
170
  throw new Error(`memory project scope ${JSON.stringify(projectScope)} equals the global memory scope (MEMORY_SCOPE) — the project layer and the global layer would be one directory. ` +
159
171
  "Set MEMORY_SCOPE to a name outside the server-minted project-key families (see src/project-layer-key.ts).");
160
172
  }
161
- const isV2 = isV2ScopeKey;
162
173
  const extras = (defaultScopes ?? []).filter((s, i, arr) => typeof s === "string" && s.length > 0 && s !== scope && s !== projectScope && arr.indexOf(s) === i);
163
174
  const layered = projectKey !== undefined
164
175
  ? { scopes: projectScope !== undefined ? [projectScope, scope, ...extras] : [scope, ...extras], writeScope: projectScope ?? scope, projectKey }
165
176
  : extras.length > 0
166
177
  ? { scopes: [scope, ...extras], writeScope: scope }
167
178
  : { scopes: [scope] };
168
- const v2 = isV2(scope) || extras.some(isV2) ? { scopeContract: "v2" } : {};
179
+ const contract = memoryScopeContractOf([scope, ...extras]);
180
+ const v2 = contract !== undefined ? { scopeContract: contract } : {};
169
181
  const stamped = {};
170
182
  if (originPolicy !== undefined) {
171
183
  if (scope.startsWith("org:"))
@@ -1,4 +1,6 @@
1
- import { type MemoryBackend, type MemorySyncClientResult, type MemorySyncTransport } from "@sema-agent/core";
1
+ import { type MemorySyncClientResult, type MemorySyncTransport } from "@sema-agent/core";
2
+ import type { ServiceConfig } from "./config-types.js";
3
+ import type { MemoryScopeStores } from "./memory-scope-stores.js";
2
4
  import { type OneTimeMigrationWindow } from "./plugins/one-time-migrations.js";
3
5
  /** 结构化日志面(src/observability/logger.ts Logger 的结构子集——测试可注入捕获器)。 */
4
6
  export interface MemorySyncLog {
@@ -18,7 +20,7 @@ export interface MemorySyncLog {
18
20
  */
19
21
  export declare function memorySyncPeerOf(url: string): string;
20
22
  /**
21
- * 起服日志行 `memory_sync_enabled` 的字段(两处起服 —— `boot/stores.ts` 与 `run-local.ts` —— 同读这一只)。`peer` 是运维改绑游标时要抄的那个值:
23
+ * 起服日志行 `memory_sync_enabled` 的字段(两处起服 —— `boot/stores.ts` 与 `run-local.ts` —— 同读这一只)。`peer` 是这一腿的对端身份(游标绑的就是它;排障时与停驻行的轴对读):
22
24
  * 它已过 {@link memorySyncPeerOf} 的两道判(带凭据的身份起不来);仍过展示铸点({@link buildDisplaySafeUrl}),日志里的 URL 值一律经它 —— 两只判官
23
25
  * (引擎的出口脱敏与本仓的展示形状律)不是同一份实现,本仓的那一只若遮了引擎放行的一段,这一行带遮记号而不是逐字(响亮,不漏)。
24
26
  */
@@ -41,9 +43,19 @@ export declare function createMemorySyncTransport(opts: {
41
43
  /** The one call shape this transport makes (`fetch` itself fits it). */
42
44
  fetchImpl?: (url: string, init: RequestInit) => Promise<Response>;
43
45
  }): MemorySyncTransport;
44
- /** 一层的游标文件路径:`memoryRoot`(= RunnerDeps.memoryEngineDir 的 resolved config root)下 `sync-cursors/<scope 摘要>.json`。一层一只,不随对端变。
46
+ /** 一层的游标文件路径:引擎根 `memoryEngineDir`(= `RunnerDeps.memoryEngineDir` 的那一个值)下 `sync-cursors/<scope 摘要>.json`。一层一只,不随对端变、不随店变。
45
47
  * 落在子目录里:与更早的根下一层文件名 `sync-cursor-<peer>.json` 的空间不相交。 */
46
- export declare function memorySyncCursorPath(memoryRoot: string, scope: string): string;
48
+ export declare function memorySyncCursorPath(memoryEngineDir: string, scope: string): string;
49
+ /**
50
+ * ⑦ 停驻层的出路(闭集;今天唯一成员)。`delete_cursor` = 删掉这一层的游标文件,下一轮从空基线重收敛。读面 `memorySync.parked[].wayOut` 与运维行同读这只词;
51
+ * 加一个词 ⇒ {@link wayOutSentence} 的穷尽 switch 编译红(句子先写好,词才上得了 wire)。
52
+ */
53
+ export type MemorySyncWayOut = "delete_cursor";
54
+ /** ⑦ 一层停驻的读数(读面与运维行同读这一只记录;不带盘路径 —— 路径只进运维行)。 */
55
+ export interface MemorySyncParkedLayer {
56
+ readonly scope: string;
57
+ readonly wayOut: MemorySyncWayOut;
58
+ }
47
59
  /** M31 一次的读数(测试按它断言)。`window: "closed"` ⇒ 一行都没读。 */
48
60
  export interface SyncCursorRelocationM31Report {
49
61
  readonly window: "open" | "closed";
@@ -54,12 +66,12 @@ export interface SyncCursorRelocationM31Report {
54
66
  * ⑤ 一次性迁移 M31:把升级前的游标搬到各自那一层的路径(`rename`,字节不变;只搬不改,判归引擎)。`today` = UTC 日历日;窗关 ⇒ 不搬。
55
67
  * · 上一版的分区目录 `sync-cursors/central/<scope 摘要>.json` ⇒ `sync-cursors/<同名>`(两种布局的文件名是同一只 scope 摘要);搬空的目录删掉;
56
68
  * 名字不是摘要形的(写游标的 staging 残片)原地不动。
57
- * · 更早的 `sync-cursor-central.json` ⇒ 它记的那一层的路径;读不成对象 / 没有 scope ⇒ 原地不动、一行 warn(没法说它是哪一层的基线)。
69
+ * · 更早的 `sync-cursor-central.json` ⇒ 它记的那一层的路径;读不成对象 / 没有 scope ⇒ 原地不动、一行 warn(没法说它是哪一层的基线;句子只点名删掉它,⑦ 一出路律)。
58
70
  * · 目标路径上已有游标 ⇒ 不搬、不删,一行 warn(这一层用路径上的那一份;留下的那只没有读者)。
59
71
  * 与同一数据根上并跑的另一个进程(server + run-local)同搬:源已被对方搬走(`ENOENT`)⇒ 跳过。残余(如实):先查目标再 `rename` 之间若有写者在目标路径上
60
72
  * 落下游标,会被盖掉 —— 写者只有本模块的 runner,而 runner 写任何游标之前先跑完本步。别的读写错 ⇒ 抛(这一轮整轮失败、下一轮再搬)。
61
73
  */
62
- export declare function relocateSyncCursorsM31(memoryRoot: string, log: MemorySyncLog, opts: {
74
+ export declare function relocateSyncCursorsM31(memoryEngineDir: string, log: MemorySyncLog, opts: {
63
75
  readonly today: string;
64
76
  readonly window?: Pick<OneTimeMigrationWindow, "removableAfter">;
65
77
  }): Promise<SyncCursorRelocationM31Report>;
@@ -84,6 +96,8 @@ export interface MemorySyncRunner {
84
96
  exclusive<T>(work: () => Promise<T>): Promise<T>;
85
97
  /** 某一层的 cursor sidecar 路径(boot 日志/测试可见性)。 */
86
98
  cursorPathFor(scope: string): string;
99
+ /** ⑦ 停驻的层(最近一轮被拒在游标上的那些;按轮序:同步层在前、项目层按键)。停驻事实的唯一读口 —— 读面与运维行读同一份。 */
100
+ parked(): readonly MemorySyncParkedLayer[];
87
101
  /** 排队+在飞的轮数(测试的确定性等待面;0 = 完全空闲)。 */
88
102
  pending(): number;
89
103
  }
@@ -91,13 +105,13 @@ export interface MemorySyncRunner {
91
105
  export declare const MAX_SYNC_ROUNDS = 32;
92
106
  /**
93
107
  * 组装同步执行器:同步层 `scope` + 同属主的项目层,逐层一轮(cursor 持久化 + core `syncMemoryScope` + conflicts 上报),串行化/节流。
94
- * `backend` = 本地 File authority plane(memoryEngineBackendFor 的产物);`memoryRoot` = resolved config root(cursor sidecar 落在 memory/ 目录旁)。
108
+ * 每一层同步的店由记忆作用域店答(⑥);同步层建时就问(坏的层键 ⇒ 这里抛引擎的 `config.memory_scope_key`,原样)。
95
109
  */
96
110
  export declare function createMemorySyncRunner(opts: {
97
- backend: MemoryBackend;
111
+ /** 记忆作用域店(本进程那一只):每一层的店问它的 `storeOf`;项目层的发现列它的 `backend`(部署的店本身);游标的根 = 它的 `root`。 */
112
+ stores: MemoryScopeStores;
98
113
  /** 同步层(`config.memorySync.scope`);它同属主的项目层随它一起同步。 */
99
114
  scope: string;
100
- memoryRoot: string;
101
115
  /** 对端 = 它的 `peer`(这一轮的对端只有这一个来源;游标绑的对端由引擎对它判)。 */
102
116
  transport: MemorySyncTransport;
103
117
  log: MemorySyncLog;
@@ -106,4 +120,15 @@ export declare function createMemorySyncRunner(opts: {
106
120
  maxPushEntries?: number;
107
121
  maxPullEntries?: number;
108
122
  }): MemorySyncRunner;
123
+ /**
124
+ * 两个入口(HTTP 服务 `boot/stores.ts` 与 `run-local.ts`)起同步执行器的**唯一一处**:建 transport(对端身份在这里铸一次)、建执行器、
125
+ * 起服日志一行 `memory_sync_enabled`、fire-and-forget 起服那一轮。两处此前各手抄一份同样的装配(上限两键漏过一次);
126
+ * 店只经入口装配出的那只记忆作用域店取 —— 它的两件事实就是交给 Runner 的那两只,会话把一层放在哪只店由同样的两件事实决定。
127
+ */
128
+ export declare function startMemorySyncRunner(deps: {
129
+ sync: NonNullable<ServiceConfig["memorySync"]>;
130
+ /** 入口装配出的记忆作用域店(= 本入口的记忆引擎对象)。 */
131
+ stores: MemoryScopeStores;
132
+ log: MemorySyncLog;
133
+ }): MemorySyncRunner;
109
134
  //# sourceMappingURL=memory-sync-client.d.ts.map
@@ -50,13 +50,57 @@ const SYNC_CURSOR_DIR = "sync-cursors";
50
50
  function scopeDigest(scope) {
51
51
  return createHash("sha256").update(scope, "utf8").digest("hex").slice(0, 32);
52
52
  }
53
- export function memorySyncCursorPath(memoryRoot, scope) {
54
- return join(memoryRoot, SYNC_CURSOR_DIR, `${scopeDigest(scope)}.json`);
53
+ export function memorySyncCursorPath(memoryEngineDir, scope) {
54
+ return join(memoryEngineDir, SYNC_CURSOR_DIR, `${scopeDigest(scope)}.json`);
55
55
  }
56
- function unusableCursor(scope, path, why) {
57
- return new Error(`memory sync: layer ${JSON.stringify(scope)} not synced — the cursor file ${path} cannot be read as a sync cursor (${why}); the layer stays unsynced until the operator picks one of two ways out: ` +
58
- "(1) restore the file from a backup; (2) delete it to accept re-convergence from an empty baseline — entries changed on both sides are reported as conflicts, " +
59
- "and entries deleted on one side since the last round come back from the other side");
56
+ const CURSOR_AXES = { scope: true, peer: true, store: true };
57
+ const CURSOR_FOUND = { absent: true, other: true };
58
+ function wayOutSentence(wayOut, cursorPath) {
59
+ switch (wayOut) {
60
+ case "delete_cursor":
61
+ return (`one way out: delete the cursor file ${cursorPath} — the next round re-converges from an empty baseline: it pulls what the center holds for this layer, ` +
62
+ "pushes what this store holds and pushes no deletes; entries changed on both sides are reported as conflicts, and an entry deleted on one side since the last round comes back from the other side");
63
+ default: {
64
+ const unhandled = wayOut;
65
+ throw new Error(`memory sync: no sentence for way out ${String(unhandled)}`);
66
+ }
67
+ }
68
+ }
69
+ function parkCauseSentence(cause) {
70
+ switch (cause.kind) {
71
+ case "cursor_refused":
72
+ return cause.detail === undefined
73
+ ? "the sync engine refused its cursor"
74
+ : `the sync engine refused its cursor on the ${cause.detail.axis} axis (${cause.detail.found === "absent" ? `the cursor has no ${cause.detail.axis} binding` : `the cursor is bound to another ${cause.detail.axis}`})`;
75
+ case "cursor_unusable":
76
+ return `the cursor file cannot be read as a sync cursor (${cause.why})`;
77
+ default: {
78
+ const unhandled = cause;
79
+ throw new Error(`memory sync: unknown park cause ${JSON.stringify(unhandled)}`);
80
+ }
81
+ }
82
+ }
83
+ class MemorySyncLayerParked extends Error {
84
+ scope;
85
+ cursorPath;
86
+ parkCause;
87
+ wayOut;
88
+ constructor(scope, cursorPath, parkCause, wayOut = "delete_cursor") {
89
+ super(`memory sync: layer ${JSON.stringify(scope)} parked — ${parkCauseSentence(parkCause)}; ${wayOutSentence(wayOut, cursorPath)}`);
90
+ this.scope = scope;
91
+ this.cursorPath = cursorPath;
92
+ this.parkCause = parkCause;
93
+ this.wayOut = wayOut;
94
+ }
95
+ }
96
+ function cursorRefusalOf(err) {
97
+ if (err === null || typeof err !== "object" || err.code !== "config.memory_sync_cursor_mismatch")
98
+ return undefined;
99
+ const d = err.detail;
100
+ const axis = d?.axis;
101
+ const found = d?.found;
102
+ const known = typeof axis === "string" && Object.hasOwn(CURSOR_AXES, axis) && typeof found === "string" && Object.hasOwn(CURSOR_FOUND, found);
103
+ return { detail: known ? { axis, found } : undefined };
60
104
  }
61
105
  function cursorOf(parsed) {
62
106
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
@@ -86,11 +130,11 @@ async function loadLayerBaseline(path, scope) {
86
130
  parsed = JSON.parse(raw);
87
131
  }
88
132
  catch {
89
- throw unusableCursor(scope, path, "not JSON");
133
+ throw new MemorySyncLayerParked(scope, path, { kind: "cursor_unusable", why: "not JSON" });
90
134
  }
91
135
  const cursor = cursorOf(parsed);
92
136
  if (typeof cursor === "string")
93
- throw unusableCursor(scope, path, cursor);
137
+ throw new MemorySyncLayerParked(scope, path, { kind: "cursor_unusable", why: cursor });
94
138
  return { kind: "cursor", cursor };
95
139
  }
96
140
  async function saveCursor(path, cursor) {
@@ -120,7 +164,7 @@ function scopeOfLegacyCursor(raw) {
120
164
  const scope = parsed["scope"];
121
165
  return typeof scope === "string" && scope.length > 0 ? scope : undefined;
122
166
  }
123
- export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
167
+ export async function relocateSyncCursorsM31(memoryEngineDir, log, opts) {
124
168
  if (!migrationWindowOpen(opts.window ?? m31Window(), opts.today))
125
169
  return { window: "closed", moved: 0, left: 0 };
126
170
  let moved = 0;
@@ -155,7 +199,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
155
199
  moved += 1;
156
200
  log.info("memory_sync_cursor_relocated", { from, to });
157
201
  };
158
- const legacyDir = join(memoryRoot, SYNC_CURSOR_DIR, LEGACY_PEER_SEGMENT);
202
+ const legacyDir = join(memoryEngineDir, SYNC_CURSOR_DIR, LEGACY_PEER_SEGMENT);
159
203
  let names = [];
160
204
  try {
161
205
  names = await readdir(legacyDir);
@@ -166,7 +210,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
166
210
  }
167
211
  for (const name of [...names].sort()) {
168
212
  if (/^[0-9a-f]{32}\.json$/.test(name))
169
- await move(join(legacyDir, name), join(memoryRoot, SYNC_CURSOR_DIR, name));
213
+ await move(join(legacyDir, name), join(memoryEngineDir, SYNC_CURSOR_DIR, name));
170
214
  }
171
215
  if (names.length > 0) {
172
216
  try {
@@ -177,7 +221,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
177
221
  throw err;
178
222
  }
179
223
  }
180
- const legacyFile = join(memoryRoot, LEGACY_ROOT_CURSOR_FILE);
224
+ const legacyFile = join(memoryEngineDir, LEGACY_ROOT_CURSOR_FILE);
181
225
  let raw;
182
226
  try {
183
227
  raw = await readFile(legacyFile, "utf8");
@@ -190,10 +234,10 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
190
234
  const scope = scopeOfLegacyCursor(raw);
191
235
  if (scope === undefined) {
192
236
  left += 1;
193
- log.warn("memory_sync_cursor_not_relocated", { path: legacyFile, note: "a cursor file written by an earlier version that is not a JSON object naming its scope, so no layer can take it as its baseline; restore it from a backup and restart, or delete it" });
237
+ log.warn("memory_sync_cursor_not_relocated", { path: legacyFile, note: "a cursor file written by an earlier version that is not a JSON object naming its scope, so no layer can take it as its baseline and no round reads it; delete it" });
194
238
  }
195
239
  else {
196
- await move(legacyFile, memorySyncCursorPath(memoryRoot, scope));
240
+ await move(legacyFile, memorySyncCursorPath(memoryEngineDir, scope));
197
241
  }
198
242
  }
199
243
  return { window: "open", moved, left };
@@ -209,8 +253,11 @@ export function createMemorySyncRunner(opts) {
209
253
  const tracked = new Set();
210
254
  let enumerationWarned = false;
211
255
  const isCoveredProjectLayer = (scope) => scope !== opts.scope && parseProjectLayerKey(scope) !== undefined && replicationCovers(coverage, scope);
256
+ const storeOf = (scope) => opts.stores.storeOf(scope);
257
+ const memoryEngineDir = opts.stores.root;
258
+ storeOf(opts.scope);
212
259
  const layersOfRound = () => {
213
- const local = enumerateScopes(opts.backend);
260
+ const local = enumerateScopes(opts.stores.backend);
214
261
  if (!local.supported && !enumerationWarned) {
215
262
  enumerationWarned = true;
216
263
  opts.log.warn("memory_sync_layer_enumeration_unsupported", { scope: opts.scope, reason: local.reason, note: "only the sync layer and tracked project layers are synced" });
@@ -219,22 +266,30 @@ export function createMemorySyncRunner(opts) {
219
266
  return [opts.scope, ...[...projectLayers].sort()];
220
267
  };
221
268
  const syncLayer = async (scope, reason) => {
222
- const cursorPath = memorySyncCursorPath(opts.memoryRoot, scope);
269
+ const cursorPath = memorySyncCursorPath(memoryEngineDir, scope);
223
270
  const baseline = await loadLayerBaseline(cursorPath, scope);
224
271
  let cursor = baseline.kind === "cursor" ? baseline.cursor : undefined;
225
272
  let result;
226
273
  let subRound = 0;
227
274
  for (;;) {
228
275
  subRound += 1;
229
- result = await syncMemoryScope({
230
- backend: opts.backend,
231
- scope,
232
- transport: opts.transport,
233
- ...(cursor !== undefined ? { cursor } : {}),
234
- ...(opts.now !== undefined ? { now: opts.now } : {}),
235
- ...(opts.maxPushEntries !== undefined ? { maxPushEntries: opts.maxPushEntries } : {}),
236
- ...(opts.maxPullEntries !== undefined ? { maxPullEntries: opts.maxPullEntries } : {}),
237
- });
276
+ try {
277
+ result = await syncMemoryScope({
278
+ backend: storeOf(scope),
279
+ scope,
280
+ transport: opts.transport,
281
+ ...(cursor !== undefined ? { cursor } : {}),
282
+ ...(opts.now !== undefined ? { now: opts.now } : {}),
283
+ ...(opts.maxPushEntries !== undefined ? { maxPushEntries: opts.maxPushEntries } : {}),
284
+ ...(opts.maxPullEntries !== undefined ? { maxPullEntries: opts.maxPullEntries } : {}),
285
+ });
286
+ }
287
+ catch (err) {
288
+ const refusal = cursorRefusalOf(err);
289
+ if (refusal !== undefined)
290
+ throw new MemorySyncLayerParked(scope, cursorPath, { kind: "cursor_refused", detail: refusal.detail });
291
+ throw err;
292
+ }
238
293
  for (const w of result.warnings ?? [])
239
294
  opts.log.warn("memory_sync_warning", { scope, subRound, warning: w });
240
295
  if (!(result.pushTruncated === true || result.pullTruncated === true))
@@ -286,22 +341,32 @@ export function createMemorySyncRunner(opts) {
286
341
  return result;
287
342
  };
288
343
  let relocated = false;
344
+ let parkedLayers = [];
289
345
  const round = async (reason) => {
290
346
  if (!relocated) {
291
- await relocateSyncCursorsM31(opts.memoryRoot, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
347
+ await relocateSyncCursorsM31(memoryEngineDir, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
292
348
  relocated = true;
293
349
  }
294
350
  const layers = [];
295
351
  const failures = [];
352
+ const parkedNow = [];
296
353
  for (const scope of layersOfRound()) {
297
354
  try {
298
355
  layers.push({ scope, result: await syncLayer(scope, reason) });
299
356
  }
300
357
  catch (err) {
301
358
  failures.push({ scope, err });
302
- opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(opts.memoryRoot, scope), err: String(err) });
359
+ if (err instanceof MemorySyncLayerParked) {
360
+ parkedNow.push({ scope, wayOut: err.wayOut });
361
+ const refused = err.parkCause.kind === "cursor_refused" ? err.parkCause.detail : undefined;
362
+ opts.log.warn("memory_sync_layer_parked", { reason, scope, cursorPath: err.cursorPath, ...(refused !== undefined ? { axis: refused.axis, found: refused.found } : {}), wayOut: err.wayOut, note: err.message });
363
+ }
364
+ else {
365
+ opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(memoryEngineDir, scope), err: String(err) });
366
+ }
303
367
  }
304
368
  }
369
+ parkedLayers = Object.freeze(parkedNow);
305
370
  if (failures.length > 0) {
306
371
  throw new Error(`memory sync round failed for ${failures.length} layer(s): ${failures.map((f) => `${f.scope}: ${f.err instanceof Error ? f.err.message : String(f.err)}`).join("; ")}`);
307
372
  }
@@ -339,6 +404,21 @@ export function createMemorySyncRunner(opts) {
339
404
  tracked.add(scope);
340
405
  queueRound("track");
341
406
  };
342
- return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(opts.memoryRoot, scope), pending: () => pending };
407
+ return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), parked: () => parkedLayers, pending: () => pending };
408
+ }
409
+ export function startMemorySyncRunner(deps) {
410
+ const { sync } = deps;
411
+ const transport = createMemorySyncTransport({ url: sync.url, token: sync.token });
412
+ const runner = createMemorySyncRunner({
413
+ stores: deps.stores,
414
+ scope: sync.scope,
415
+ transport,
416
+ log: deps.log,
417
+ ...(sync.maxPushEntries !== undefined ? { maxPushEntries: sync.maxPushEntries } : {}),
418
+ ...(sync.maxPullEntries !== undefined ? { maxPullEntries: sync.maxPullEntries } : {}),
419
+ });
420
+ deps.log.info("memory_sync_enabled", buildMemorySyncEnabledFields(transport.peer, sync.scope, runner.cursorPathFor(sync.scope)));
421
+ runner.trigger("boot");
422
+ return runner;
343
423
  }
344
424
  //# sourceMappingURL=memory-sync-client.js.map