@sema-agent/server 7.139.0 → 7.140.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 (194) hide show
  1. package/MIGRATION.md +181 -0
  2. package/USAGE.md +19 -13
  3. package/dist/adoption/runner.js +1 -1
  4. package/dist/adoption/sql.js +1 -1
  5. package/dist/approval-card.d.ts +6 -6
  6. package/dist/bench/s1/live-deps.js +3 -2
  7. package/dist/bench/s1/row.d.ts +5 -2
  8. package/dist/bench/s1/row.js +2 -22
  9. package/dist/boot/adoption.js +1 -1
  10. package/dist/boot/execution-env.js +12 -10
  11. package/dist/boot/execution-lane.d.ts +8 -0
  12. package/dist/boot/execution-lane.js +6 -1
  13. package/dist/boot/memory-boundary.d.ts +14 -51
  14. package/dist/boot/memory-boundary.js +10 -20
  15. package/dist/boot/memory-consolidation.d.ts +15 -11
  16. package/dist/boot/memory-consolidation.js +23 -12
  17. package/dist/boot/resolve-spec.d.ts +3 -5
  18. package/dist/boot/resolve-spec.js +2 -4
  19. package/dist/boot/runner-deps.d.ts +3 -3
  20. package/dist/boot/runner-deps.js +1 -0
  21. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  22. package/dist/boot/stage-03-store-backend.js +2 -3
  23. package/dist/boot/stage-04-stores.d.ts +2 -6
  24. package/dist/boot/stage-04-stores.js +5 -5
  25. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  26. package/dist/boot/stage-06-runners.d.ts +2 -6
  27. package/dist/boot/stage-06-runners.js +2 -2
  28. package/dist/boot/stage-07-capability-layer.d.ts +2 -6
  29. package/dist/boot/stage-08-reapers.d.ts +2 -6
  30. package/dist/boot/stage-09-leader.d.ts +2 -6
  31. package/dist/boot/stage-10-http-server.d.ts +2 -6
  32. package/dist/boot/stage-10-http-server.js +5 -4
  33. package/dist/boot/stores.d.ts +3 -6
  34. package/dist/boot/stores.js +27 -40
  35. package/dist/boot/task-list-lane.d.ts +1 -1
  36. package/dist/boot/task-list-lane.js +1 -1
  37. package/dist/boot/web-admin.js +35 -9
  38. package/dist/capabilities/execution-lane.d.ts +2 -2
  39. package/dist/config-catalog.js +2 -1
  40. package/dist/config-provider.d.ts +14 -13
  41. package/dist/config-provider.js +24 -18
  42. package/dist/config-types.d.ts +11 -2
  43. package/dist/config.js +2 -1
  44. package/dist/delegated-session-owner.d.ts +2 -1
  45. package/dist/delegated-session-owner.js +1 -1
  46. package/dist/deployment-shape.d.ts +9 -8
  47. package/dist/deployment-shape.js +4 -4
  48. package/dist/execution-lane-caps.d.ts +97 -106
  49. package/dist/execution-lane-caps.js +67 -83
  50. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  51. package/dist/http/memory-compliance-failure-reply.js +5 -0
  52. package/dist/http/memory-layer-reply.d.ts +30 -0
  53. package/dist/http/memory-layer-reply.js +24 -0
  54. package/dist/http/resume-legs.js +13 -16
  55. package/dist/http/retired-service-deps.d.ts +1 -1
  56. package/dist/http/retired-service-deps.js +1 -1
  57. package/dist/http/routes/adoption.js +1 -1
  58. package/dist/http/routes/memory-bundle.js +3 -0
  59. package/dist/http/routes/memory-compliance.js +8 -1
  60. package/dist/http/routes/memory-consolidation.js +12 -1
  61. package/dist/http/routes/memory-origin.js +14 -2
  62. package/dist/http/routes/memory-policy.js +5 -0
  63. package/dist/http/routes/sessions.js +28 -11
  64. package/dist/http/routes/tasks.js +4 -2
  65. package/dist/http/server.d.ts +5 -25
  66. package/dist/http/server.js +2 -2
  67. package/dist/identity-source.d.ts +1 -1
  68. package/dist/identity-source.js +1 -1
  69. package/dist/lane-home-probe.d.ts +68 -42
  70. package/dist/lane-home-probe.js +53 -30
  71. package/dist/leader/wire.js +5 -4
  72. package/dist/memory-bundle-engine.d.ts +16 -9
  73. package/dist/memory-bundle-engine.js +29 -6
  74. package/dist/memory-operator-faces.d.ts +85 -30
  75. package/dist/memory-operator-faces.js +136 -30
  76. package/dist/memory-project-layer.d.ts +3 -21
  77. package/dist/memory-project-layer.js +11 -8
  78. package/dist/memory-scope-stores.d.ts +73 -0
  79. package/dist/memory-scope-stores.js +86 -0
  80. package/dist/memory-scope.d.ts +29 -5
  81. package/dist/memory-scope.js +16 -4
  82. package/dist/memory-sync-client.d.ts +20 -7
  83. package/dist/memory-sync-client.js +31 -13
  84. package/dist/plugins/adoption-log-sql.js +4 -4
  85. package/dist/plugins/approval-ask-store-sql.js +14 -14
  86. package/dist/plugins/approval-nonce-store.js +4 -4
  87. package/dist/plugins/blob-backend.d.ts +4 -4
  88. package/dist/plugins/blob-backend.js +7 -7
  89. package/dist/plugins/breaker-state-sql.js +2 -2
  90. package/dist/plugins/checkpoint-index-columns.js +2 -2
  91. package/dist/plugins/checkpoint-store-sql.js +16 -16
  92. package/dist/plugins/device-store-sql.js +19 -19
  93. package/dist/plugins/ensure-index.d.ts +1 -2
  94. package/dist/plugins/execution-placement-sql.d.ts +8 -8
  95. package/dist/plugins/execution-placement-sql.js +8 -8
  96. package/dist/plugins/file-history-store-sql.d.ts +2 -2
  97. package/dist/plugins/file-history-store-sql.js +4 -4
  98. package/dist/plugins/image-bake-store-sql.js +9 -9
  99. package/dist/plugins/image-index-sql.js +11 -11
  100. package/dist/plugins/leader-run-store-sql.js +4 -4
  101. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  102. package/dist/plugins/mailbox-store-sql.js +4 -4
  103. package/dist/plugins/memory-engine-tidb.js +5 -5
  104. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  105. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  106. package/dist/plugins/one-time-migrations.d.ts +10 -9
  107. package/dist/plugins/one-time-migrations.js +36 -36
  108. package/dist/plugins/org-delivery-quota-store.js +2 -2
  109. package/dist/plugins/outcome-ledger-sql.js +2 -2
  110. package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
  111. package/dist/plugins/permission-rule-store-sql.js +16 -16
  112. package/dist/plugins/remote-env-adb.d.ts +7 -24
  113. package/dist/plugins/remote-env-adb.js +22 -5
  114. package/dist/plugins/remote-env-device.d.ts +2 -28
  115. package/dist/plugins/remote-env-device.js +2 -1
  116. package/dist/plugins/remote-env-e2b.d.ts +18 -26
  117. package/dist/plugins/remote-env-e2b.js +51 -4
  118. package/dist/plugins/remote-env-file-error.d.ts +1 -1
  119. package/dist/plugins/remote-env-file-error.js +8 -2
  120. package/dist/plugins/remote-env-host.d.ts +19 -35
  121. package/dist/plugins/remote-env-host.js +26 -3
  122. package/dist/plugins/remote-env-k8s.d.ts +3 -14
  123. package/dist/plugins/remote-env-k8s.js +3 -2
  124. package/dist/plugins/remote-env-local-docker.d.ts +4 -15
  125. package/dist/plugins/remote-env-local-docker.js +5 -4
  126. package/dist/plugins/remote-env-ssh.d.ts +4 -24
  127. package/dist/plugins/remote-env-ssh.js +4 -3
  128. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  129. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  130. package/dist/plugins/retention-lane-store-sql.js +2 -2
  131. package/dist/plugins/retention-store-sql.d.ts +1 -1
  132. package/dist/plugins/retention-store-sql.js +6 -6
  133. package/dist/plugins/run-store-sql.js +15 -15
  134. package/dist/plugins/schema-consistency.d.ts +6 -6
  135. package/dist/plugins/schema-consistency.js +1 -1
  136. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  137. package/dist/plugins/schema-ensure-legs.js +2 -2
  138. package/dist/plugins/schema-live-columns.d.ts +1 -1
  139. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  140. package/dist/plugins/session-rules-column.d.ts +1 -1
  141. package/dist/plugins/session-rules-column.js +1 -1
  142. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  143. package/dist/plugins/session-workspace-cell.js +1 -1
  144. package/dist/plugins/shared-memory-store-sql.js +2 -2
  145. package/dist/plugins/sql-driver.d.ts +10 -5
  146. package/dist/plugins/sql-driver.js +6 -6
  147. package/dist/plugins/sql-errors.js +1 -1
  148. package/dist/plugins/store-backend.d.ts +4 -6
  149. package/dist/plugins/store-backend.js +6 -6
  150. package/dist/plugins/task-list-store-sql.js +11 -11
  151. package/dist/plugins/tidb-pool.js +5 -5
  152. package/dist/plugins/tidb-session-storage.js +4 -4
  153. package/dist/plugins/tidb-session-store.js +13 -13
  154. package/dist/plugins/tool-result-store-sql.d.ts +2 -2
  155. package/dist/plugins/tool-result-store-sql.js +3 -3
  156. package/dist/plugins/user-input-ledger-sql.js +2 -2
  157. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  158. package/dist/plugins/workflow-run-store-sql.js +19 -19
  159. package/dist/plugins/write-behind-counter.d.ts +2 -3
  160. package/dist/plugins/write-behind-counter.js +5 -5
  161. package/dist/resource-suspend.d.ts +14 -12
  162. package/dist/resource-suspend.js +1 -1
  163. package/dist/resource-window.d.ts +4 -3
  164. package/dist/run-local.js +14 -19
  165. package/dist/runs.d.ts +8 -4
  166. package/dist/runs.js +49 -65
  167. package/dist/session-incarnation.d.ts +1 -1
  168. package/dist/session-incarnation.js +1 -1
  169. package/dist/session-policy-wire.d.ts +1 -1
  170. package/dist/sql-engine-posture.d.ts +1 -1
  171. package/dist/sql-engine-posture.js +1 -1
  172. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  173. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  174. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  175. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  176. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  177. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  178. package/dist/task-a2a.js +5 -6
  179. package/dist/task-mcp.d.ts +20 -7
  180. package/dist/task-mcp.js +17 -9
  181. package/dist/terminal.d.ts +27 -0
  182. package/dist/terminal.js +6 -0
  183. package/dist/trace/redact.d.ts +3 -1
  184. package/dist/trace/redact.js +80 -28
  185. package/dist/trace/self-minted-wire.d.ts +58 -0
  186. package/dist/trace/self-minted-wire.js +16 -0
  187. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  188. package/dist/web-admin-client/candidate-gate.js +1 -1
  189. package/dist/web-admin-client/http-client.d.ts +5 -4
  190. package/dist/web-admin-client/http-client.js +2 -2
  191. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  192. package/dist/web-admin-client/read-warnings.js +5 -9
  193. package/package.json +2 -2
  194. package/wire-changes/server@7.140.0.json +184 -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
  *
@@ -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;
@@ -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,6 +14,18 @@ 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) {
@@ -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 {
@@ -41,9 +43,9 @@ 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;
47
49
  /** M31 一次的读数(测试按它断言)。`window: "closed"` ⇒ 一行都没读。 */
48
50
  export interface SyncCursorRelocationM31Report {
49
51
  readonly window: "open" | "closed";
@@ -59,7 +61,7 @@ export interface SyncCursorRelocationM31Report {
59
61
  * 与同一数据根上并跑的另一个进程(server + run-local)同搬:源已被对方搬走(`ENOENT`)⇒ 跳过。残余(如实):先查目标再 `rename` 之间若有写者在目标路径上
60
62
  * 落下游标,会被盖掉 —— 写者只有本模块的 runner,而 runner 写任何游标之前先跑完本步。别的读写错 ⇒ 抛(这一轮整轮失败、下一轮再搬)。
61
63
  */
62
- export declare function relocateSyncCursorsM31(memoryRoot: string, log: MemorySyncLog, opts: {
64
+ export declare function relocateSyncCursorsM31(memoryEngineDir: string, log: MemorySyncLog, opts: {
63
65
  readonly today: string;
64
66
  readonly window?: Pick<OneTimeMigrationWindow, "removableAfter">;
65
67
  }): Promise<SyncCursorRelocationM31Report>;
@@ -91,13 +93,13 @@ export interface MemorySyncRunner {
91
93
  export declare const MAX_SYNC_ROUNDS = 32;
92
94
  /**
93
95
  * 组装同步执行器:同步层 `scope` + 同属主的项目层,逐层一轮(cursor 持久化 + core `syncMemoryScope` + conflicts 上报),串行化/节流。
94
- * `backend` = 本地 File authority plane(memoryEngineBackendFor 的产物);`memoryRoot` = resolved config root(cursor sidecar 落在 memory/ 目录旁)。
96
+ * 每一层同步的店由记忆作用域店答(⑥);同步层建时就问(坏的层键 ⇒ 这里抛引擎的 `config.memory_scope_key`,原样)。
95
97
  */
96
98
  export declare function createMemorySyncRunner(opts: {
97
- backend: MemoryBackend;
99
+ /** 记忆作用域店(本进程那一只):每一层的店问它的 `storeOf`;项目层的发现列它的 `backend`(部署的店本身);游标的根 = 它的 `root`。 */
100
+ stores: MemoryScopeStores;
98
101
  /** 同步层(`config.memorySync.scope`);它同属主的项目层随它一起同步。 */
99
102
  scope: string;
100
- memoryRoot: string;
101
103
  /** 对端 = 它的 `peer`(这一轮的对端只有这一个来源;游标绑的对端由引擎对它判)。 */
102
104
  transport: MemorySyncTransport;
103
105
  log: MemorySyncLog;
@@ -106,4 +108,15 @@ export declare function createMemorySyncRunner(opts: {
106
108
  maxPushEntries?: number;
107
109
  maxPullEntries?: number;
108
110
  }): MemorySyncRunner;
111
+ /**
112
+ * 两个入口(HTTP 服务 `boot/stores.ts` 与 `run-local.ts`)起同步执行器的**唯一一处**:建 transport(对端身份在这里铸一次)、建执行器、
113
+ * 起服日志一行 `memory_sync_enabled`、fire-and-forget 起服那一轮。两处此前各手抄一份同样的装配(上限两键漏过一次);
114
+ * 店只经入口装配出的那只记忆作用域店取 —— 它的两件事实就是交给 Runner 的那两只,会话把一层放在哪只店由同样的两件事实决定。
115
+ */
116
+ export declare function startMemorySyncRunner(deps: {
117
+ sync: NonNullable<ServiceConfig["memorySync"]>;
118
+ /** 入口装配出的记忆作用域店(= 本入口的记忆引擎对象)。 */
119
+ stores: MemoryScopeStores;
120
+ log: MemorySyncLog;
121
+ }): MemorySyncRunner;
109
122
  //# sourceMappingURL=memory-sync-client.d.ts.map
@@ -50,8 +50,8 @@ 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
56
  function unusableCursor(scope, path, why) {
57
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: ` +
@@ -120,7 +120,7 @@ function scopeOfLegacyCursor(raw) {
120
120
  const scope = parsed["scope"];
121
121
  return typeof scope === "string" && scope.length > 0 ? scope : undefined;
122
122
  }
123
- export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
123
+ export async function relocateSyncCursorsM31(memoryEngineDir, log, opts) {
124
124
  if (!migrationWindowOpen(opts.window ?? m31Window(), opts.today))
125
125
  return { window: "closed", moved: 0, left: 0 };
126
126
  let moved = 0;
@@ -155,7 +155,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
155
155
  moved += 1;
156
156
  log.info("memory_sync_cursor_relocated", { from, to });
157
157
  };
158
- const legacyDir = join(memoryRoot, SYNC_CURSOR_DIR, LEGACY_PEER_SEGMENT);
158
+ const legacyDir = join(memoryEngineDir, SYNC_CURSOR_DIR, LEGACY_PEER_SEGMENT);
159
159
  let names = [];
160
160
  try {
161
161
  names = await readdir(legacyDir);
@@ -166,7 +166,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
166
166
  }
167
167
  for (const name of [...names].sort()) {
168
168
  if (/^[0-9a-f]{32}\.json$/.test(name))
169
- await move(join(legacyDir, name), join(memoryRoot, SYNC_CURSOR_DIR, name));
169
+ await move(join(legacyDir, name), join(memoryEngineDir, SYNC_CURSOR_DIR, name));
170
170
  }
171
171
  if (names.length > 0) {
172
172
  try {
@@ -177,7 +177,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
177
177
  throw err;
178
178
  }
179
179
  }
180
- const legacyFile = join(memoryRoot, LEGACY_ROOT_CURSOR_FILE);
180
+ const legacyFile = join(memoryEngineDir, LEGACY_ROOT_CURSOR_FILE);
181
181
  let raw;
182
182
  try {
183
183
  raw = await readFile(legacyFile, "utf8");
@@ -193,7 +193,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
193
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" });
194
194
  }
195
195
  else {
196
- await move(legacyFile, memorySyncCursorPath(memoryRoot, scope));
196
+ await move(legacyFile, memorySyncCursorPath(memoryEngineDir, scope));
197
197
  }
198
198
  }
199
199
  return { window: "open", moved, left };
@@ -209,8 +209,11 @@ export function createMemorySyncRunner(opts) {
209
209
  const tracked = new Set();
210
210
  let enumerationWarned = false;
211
211
  const isCoveredProjectLayer = (scope) => scope !== opts.scope && parseProjectLayerKey(scope) !== undefined && replicationCovers(coverage, scope);
212
+ const storeOf = (scope) => opts.stores.storeOf(scope);
213
+ const memoryEngineDir = opts.stores.root;
214
+ storeOf(opts.scope);
212
215
  const layersOfRound = () => {
213
- const local = enumerateScopes(opts.backend);
216
+ const local = enumerateScopes(opts.stores.backend);
214
217
  if (!local.supported && !enumerationWarned) {
215
218
  enumerationWarned = true;
216
219
  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,7 +222,7 @@ export function createMemorySyncRunner(opts) {
219
222
  return [opts.scope, ...[...projectLayers].sort()];
220
223
  };
221
224
  const syncLayer = async (scope, reason) => {
222
- const cursorPath = memorySyncCursorPath(opts.memoryRoot, scope);
225
+ const cursorPath = memorySyncCursorPath(memoryEngineDir, scope);
223
226
  const baseline = await loadLayerBaseline(cursorPath, scope);
224
227
  let cursor = baseline.kind === "cursor" ? baseline.cursor : undefined;
225
228
  let result;
@@ -227,7 +230,7 @@ export function createMemorySyncRunner(opts) {
227
230
  for (;;) {
228
231
  subRound += 1;
229
232
  result = await syncMemoryScope({
230
- backend: opts.backend,
233
+ backend: storeOf(scope),
231
234
  scope,
232
235
  transport: opts.transport,
233
236
  ...(cursor !== undefined ? { cursor } : {}),
@@ -288,7 +291,7 @@ export function createMemorySyncRunner(opts) {
288
291
  let relocated = false;
289
292
  const round = async (reason) => {
290
293
  if (!relocated) {
291
- await relocateSyncCursorsM31(opts.memoryRoot, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
294
+ await relocateSyncCursorsM31(memoryEngineDir, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
292
295
  relocated = true;
293
296
  }
294
297
  const layers = [];
@@ -299,7 +302,7 @@ export function createMemorySyncRunner(opts) {
299
302
  }
300
303
  catch (err) {
301
304
  failures.push({ scope, err });
302
- opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(opts.memoryRoot, scope), err: String(err) });
305
+ opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(memoryEngineDir, scope), err: String(err) });
303
306
  }
304
307
  }
305
308
  if (failures.length > 0) {
@@ -339,6 +342,21 @@ export function createMemorySyncRunner(opts) {
339
342
  tracked.add(scope);
340
343
  queueRound("track");
341
344
  };
342
- return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(opts.memoryRoot, scope), pending: () => pending };
345
+ return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), pending: () => pending };
346
+ }
347
+ export function startMemorySyncRunner(deps) {
348
+ const { sync } = deps;
349
+ const transport = createMemorySyncTransport({ url: sync.url, token: sync.token });
350
+ const runner = createMemorySyncRunner({
351
+ stores: deps.stores,
352
+ scope: sync.scope,
353
+ transport,
354
+ log: deps.log,
355
+ ...(sync.maxPushEntries !== undefined ? { maxPushEntries: sync.maxPushEntries } : {}),
356
+ ...(sync.maxPullEntries !== undefined ? { maxPullEntries: sync.maxPullEntries } : {}),
357
+ });
358
+ deps.log.info("memory_sync_enabled", buildMemorySyncEnabledFields(transport.peer, sync.scope, runner.cursorPathFor(sync.scope)));
359
+ runner.trigger("boot");
360
+ return runner;
343
361
  }
344
362
  //# sourceMappingURL=memory-sync-client.js.map
@@ -86,8 +86,8 @@ export class SqlAdoptionLogStore {
86
86
  constructor(db) {
87
87
  this.db = db;
88
88
  }
89
- q(tidb, pg) {
90
- return this.db.dialect === "tidb" ? tidb : pg;
89
+ q(mysql, pg) {
90
+ return this.db.dialect === "mysql" ? mysql : pg;
91
91
  }
92
92
  get reader() {
93
93
  return this.db;
@@ -191,7 +191,7 @@ export class SqlAdoptionLogStore {
191
191
  try {
192
192
  lockName = qualifyAdoptionLockName(await this.resolveLockKeyspace(conn), name);
193
193
  for (let i = 0; i < attempts && !held; i += 1) {
194
- const res = await conn.query(this.q("SELECT GET_LOCK(?, 0) AS got", "SELECT pg_try_advisory_lock($1, $2) AS got"), this.db.dialect === "tidb" ? [lockName] : [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(lockName)]);
194
+ const res = await conn.query(this.q("SELECT GET_LOCK(?, 0) AS got", "SELECT pg_try_advisory_lock($1, $2) AS got"), this.db.dialect === "mysql" ? [lockName] : [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(lockName)]);
195
195
  const got = res.rows[0]?.got;
196
196
  held = got === 1 || got === true || got === "1";
197
197
  if (!held)
@@ -204,7 +204,7 @@ export class SqlAdoptionLogStore {
204
204
  finally {
205
205
  if (held) {
206
206
  try {
207
- await conn.query(this.q("SELECT RELEASE_LOCK(?) AS released", "SELECT pg_advisory_unlock($1, $2) AS released"), this.db.dialect === "tidb" ? [lockName] : [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(lockName)]);
207
+ await conn.query(this.q("SELECT RELEASE_LOCK(?) AS released", "SELECT pg_advisory_unlock($1, $2) AS released"), this.db.dialect === "mysql" ? [lockName] : [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(lockName)]);
208
208
  }
209
209
  catch (releaseErr) {
210
210
  console.warn("[adoption-log] releasing the adoption arc lock failed (it auto-frees when the connection closes):", releaseErr);
@@ -386,7 +386,7 @@ function patchColumns(patch, json) {
386
386
  return out;
387
387
  }
388
388
  function ph(dialect, n) {
389
- return dialect === "tidb" ? "?" : `$${n}`;
389
+ return dialect === "mysql" ? "?" : `$${n}`;
390
390
  }
391
391
  function pendingClaimWhere(dialect, firstParam) {
392
392
  return `ask_id = ${ph(dialect, firstParam)} AND batch_id = ${ph(dialect, firstParam + 1)} AND state = 'STREAM_PENDING'`;
@@ -394,15 +394,15 @@ function pendingClaimWhere(dialect, firstParam) {
394
394
  function decideOwnerWhere(dialect, owner, param) {
395
395
  if (owner === null)
396
396
  return { sql: "owner IS NULL", params: [] };
397
- return dialect === "tidb" ? { sql: "CAST(owner AS BINARY) = CAST(? AS BINARY)", params: [owner] } : { sql: `owner = ${ph(dialect, param)}`, params: [owner] };
397
+ return dialect === "mysql" ? { sql: "CAST(owner AS BINARY) = CAST(? AS BINARY)", params: [owner] } : { sql: `owner = ${ph(dialect, param)}`, params: [owner] };
398
398
  }
399
399
  export class SqlApprovalAskStore {
400
400
  db;
401
401
  constructor(db) {
402
402
  this.db = db;
403
403
  }
404
- q(tidb, pg) {
405
- return this.db.dialect === "tidb" ? tidb : pg;
404
+ q(mysql, pg) {
405
+ return this.db.dialect === "mysql" ? mysql : pg;
406
406
  }
407
407
  json(value) {
408
408
  return dialectJsonEncoder(this.db.dialect)(value);
@@ -411,7 +411,7 @@ export class SqlApprovalAskStore {
411
411
  return isDupKeyError(this.db.dialect, e);
412
412
  }
413
413
  async getAskOn(conn, askId) {
414
- const { rows } = await conn.query(this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = $1`), [askId]);
414
+ const { rows } = await conn.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = $1`), [askId]);
415
415
  return rows[0] ? mapAskRow(rows[0]) : null;
416
416
  }
417
417
  async getBatchOn(conn, batchId) {
@@ -704,40 +704,40 @@ export class SqlApprovalAskStore {
704
704
  }
705
705
  }
706
706
  async listByState(state, limit) {
707
- const { rows } = await this.db.query(this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE state = ? ORDER BY updated_at_ms ASC, created_at_ms ASC LIMIT ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE state = $1 ORDER BY updated_at_ms ASC, created_at_ms ASC LIMIT $2`), [state, limit]);
707
+ const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE state = ? ORDER BY updated_at_ms ASC, created_at_ms ASC LIMIT ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE state = $1 ORDER BY updated_at_ms ASC, created_at_ms ASC LIMIT $2`), [state, limit]);
708
708
  return rows.map(mapAskRow);
709
709
  }
710
710
  async listPendingByTask(taskId, signal) {
711
711
  if (signal?.aborted === true)
712
712
  throw abortedReadError("listPendingByTask");
713
- const { rows } = await withReadSignal(this.db.query(this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND state = 'STREAM_PENDING' ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND state = 'STREAM_PENDING' ORDER BY created_at_ms ASC`), [taskId]), signal, "listPendingByTask");
713
+ const { rows } = await withReadSignal(this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND state = 'STREAM_PENDING' ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND state = 'STREAM_PENDING' ORDER BY created_at_ms ASC`), [taskId]), signal, "listPendingByTask");
714
714
  return rows.map(mapAskRow);
715
715
  }
716
716
  async listPendingBySession(sessionId, owner, signal) {
717
717
  if (signal?.aborted === true)
718
718
  throw abortedReadError("listPendingBySession");
719
719
  const { rows } = await withReadSignal(this.db.query(owner === null
720
- ? this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'STREAM_PENDING' AND owner IS NULL ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'STREAM_PENDING' AND owner IS NULL ORDER BY created_at_ms ASC`)
721
- : this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'STREAM_PENDING' AND owner = ? ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'STREAM_PENDING' AND owner = $2 ORDER BY created_at_ms ASC`), owner === null ? [sessionId] : [sessionId, owner]), signal, "listPendingBySession");
720
+ ? this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'STREAM_PENDING' AND owner IS NULL ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'STREAM_PENDING' AND owner IS NULL ORDER BY created_at_ms ASC`)
721
+ : this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'STREAM_PENDING' AND owner = ? ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'STREAM_PENDING' AND owner = $2 ORDER BY created_at_ms ASC`), owner === null ? [sessionId] : [sessionId, owner]), signal, "listPendingBySession");
722
722
  return rows.map(mapAskRow);
723
723
  }
724
724
  async listByCall(call) {
725
- const { rows } = await this.db.query(this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND leg_key = ? AND tool_call_id = ? AND source_task_id = ? ORDER BY created_at_ms ASC, ask_id ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND leg_key = $2 AND tool_call_id = $3 AND source_task_id = $4 ORDER BY created_at_ms ASC, ask_id ASC`), [call.taskId, call.legKey, call.toolCallId, call.sourceTaskId]);
725
+ const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND leg_key = ? AND tool_call_id = ? AND source_task_id = ? ORDER BY created_at_ms ASC, ask_id ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND leg_key = $2 AND tool_call_id = $3 AND source_task_id = $4 ORDER BY created_at_ms ASC, ask_id ASC`), [call.taskId, call.legKey, call.toolCallId, call.sourceTaskId]);
726
726
  return rows.map(mapAskRow).filter((r) => askRowOnCall(r, call));
727
727
  }
728
728
  async listParkingBySession(sessionId) {
729
- const { rows } = await this.db.query(this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'PARKING' ORDER BY created_at_ms ASC, ask_id ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'PARKING' ORDER BY created_at_ms ASC, ask_id ASC`), [sessionId]);
729
+ const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'PARKING' ORDER BY created_at_ms ASC, ask_id ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'PARKING' ORDER BY created_at_ms ASC, ask_id ASC`), [sessionId]);
730
730
  return rows.map(mapAskRow).filter((r) => r.sessionId === sessionId);
731
731
  }
732
732
  async getAsk(askId) {
733
- const { rows } = await this.db.query(this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = $1`), [
733
+ const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = $1`), [
734
734
  askId,
735
735
  ]);
736
736
  return rows[0] ? mapAskRow(rows[0]) : null;
737
737
  }
738
738
  async getByIdempotencyKey(taskId, key) {
739
739
  assertIdempotencyKeyShape(key);
740
- const { rows } = await this.db.query(this.q(`SELECT ${askCols("tidb")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND idempotency_key = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND idempotency_key = $2`), [taskId, key]);
740
+ const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND idempotency_key = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND idempotency_key = $2`), [taskId, key]);
741
741
  return rows[0] ? mapAskRow(rows[0]) : null;
742
742
  }
743
743
  async deferReconcile(askId, expectedState, expectedRev, nowMs) {
@@ -772,7 +772,7 @@ export class SqlApprovalAskStore {
772
772
  const ANCHOR_COLUMNS = ["task_id", "session_id", "source_task_id"];
773
773
  let keepSql = "";
774
774
  let keepParams = [];
775
- if (keep.length > 0 && dialect === "tidb") {
775
+ if (keep.length > 0 && dialect === "mysql") {
776
776
  const list = keep.map(() => "?").join(", ");
777
777
  keepSql = ANCHOR_COLUMNS.map((c) => ` AND ${c} NOT IN (${list})`).join("");
778
778
  keepParams = ANCHOR_COLUMNS.flatMap(() => keep);
@@ -10,15 +10,15 @@ export class SqlApprovalNonceStore {
10
10
  constructor(db) {
11
11
  this.db = db;
12
12
  }
13
- q(tidb, pg) {
14
- return this.db.dialect === "tidb" ? tidb : pg;
13
+ q(mysql, pg) {
14
+ return this.db.dialect === "mysql" ? mysql : pg;
15
15
  }
16
16
  async nowMs() {
17
17
  return sqlDbNowMs(this.db, APPROVAL_NONCE_TABLE);
18
18
  }
19
19
  async consume(sessionId, nonce, expiresAtMs) {
20
20
  try {
21
- await this.db.query(this.q(`INSERT INTO ${APPROVAL_NONCE_TABLE} (session_id, nonce, consumed_at_ms, expires_at_ms) VALUES (?,?,${SQL_DB_NOW_MS.tidb},?)`, `INSERT INTO ${APPROVAL_NONCE_TABLE} (session_id, nonce, consumed_at_ms, expires_at_ms) VALUES ($1,$2,${SQL_DB_NOW_MS.pg},$3)`), [sessionId, nonce, expiresAtMs]);
21
+ await this.db.query(this.q(`INSERT INTO ${APPROVAL_NONCE_TABLE} (session_id, nonce, consumed_at_ms, expires_at_ms) VALUES (?,?,${SQL_DB_NOW_MS.mysql},?)`, `INSERT INTO ${APPROVAL_NONCE_TABLE} (session_id, nonce, consumed_at_ms, expires_at_ms) VALUES ($1,$2,${SQL_DB_NOW_MS.pg},$3)`), [sessionId, nonce, expiresAtMs]);
22
22
  return true;
23
23
  }
24
24
  catch (err) {
@@ -28,7 +28,7 @@ export class SqlApprovalNonceStore {
28
28
  }
29
29
  }
30
30
  async reapExpired() {
31
- const res = await this.db.query(this.q(`DELETE FROM ${APPROVAL_NONCE_TABLE} WHERE expires_at_ms <= ${SQL_DB_NOW_MS.tidb}`, `DELETE FROM ${APPROVAL_NONCE_TABLE} WHERE expires_at_ms <= ${SQL_DB_NOW_MS.pg}`), []);
31
+ const res = await this.db.query(this.q(`DELETE FROM ${APPROVAL_NONCE_TABLE} WHERE expires_at_ms <= ${SQL_DB_NOW_MS.mysql}`, `DELETE FROM ${APPROVAL_NONCE_TABLE} WHERE expires_at_ms <= ${SQL_DB_NOW_MS.pg}`), []);
32
32
  return res.affected;
33
33
  }
34
34
  }
@@ -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 SqlEngineFacts, type SqlEngineReading, type SqlWallSource, type SqlWallsReading } from "./sql-driver.js";
3
+ import { type SqlEngineFacts, type SqlEngineReading, type SqlWallSource, type SqlWallsReading, type SqlDialect } from "./sql-driver.js";
4
4
  import { type Logger } from "../observability/logger.js";
5
5
  /**
6
6
  * 文本协议的 blob 膨胀律 —— mysql2 `query()` 把 Buffer 参数写成 `X'<hex>'`,一字节两个字符,所以承载一个 blob 的语句至少是它的 2 倍。
@@ -115,7 +115,7 @@ export declare class SqlBlobBackend implements BlobBackend {
115
115
  private readonly db;
116
116
  /** Where a stale connection's replacement is logged ({@link connectJudged}); the boot assembly passes the service logger. */
117
117
  private readonly log;
118
- constructor(dialect: "tidb" | "pg", pool: MysqlPool | PgPool, maxBytes?: number, table?: string, log?: Pick<Logger, "warn">);
118
+ constructor(dialect: SqlDialect, pool: MysqlPool | PgPool, maxBytes?: number, table?: string, log?: Pick<Logger, "warn">);
119
119
  /** The created_at-refreshing content-addressed upsert — VERBATIM the tidb/pg stores' `putBlobRow`. Refreshes
120
120
  * created_at even on a dedup hit so a re-used/just-written blob is "touched=now" and GC-grace-protected.
121
121
  * (a): an over-cap blob is REJECTED (typed {@link BlobTooLargeError}) BEFORE the driver ever sees the bytes —
@@ -140,7 +140,7 @@ export declare class SqlBlobBackend implements BlobBackend {
140
140
  /** SHARED index op(workspace 浏览面 #3,[ref]②)—— per-blob 字节数,读 SQL INDEX 的 `byte_len`
141
141
  * 列(sqlHasBlobs 同款单 IN 查询;MinIO 后端的 bytes-NULL 索引行同样带 byte_len,两后端同源)。
142
142
  * tree/file 面的 size 投影与「超限先拒后拉」都靠它——绝不为拿大小去 getBlob 整块字节。 */
143
- export declare function sqlBlobSizes(dialect: "tidb" | "pg", pool: MysqlPool | PgPool, hashes: string[]): Promise<Map<string, number>>;
143
+ export declare function sqlBlobSizes(dialect: SqlDialect, pool: MysqlPool | PgPool, hashes: string[]): Promise<Map<string, number>>;
144
144
  export interface MinioBlobBackendConfig {
145
145
  endpoint: string;
146
146
  bucket: string;
@@ -198,7 +198,7 @@ export declare class MinioBlobBackend implements BlobBackend {
198
198
  /** Blob-domain index table ([ref] 域隔离,SqlBlobBackend.table 同款;默认 = E19 纪元的 snapshot_blob)。 */
199
199
  private readonly indexTable;
200
200
  constructor(cfg: MinioBlobBackendConfig, index?: {
201
- dialect: "tidb" | "pg";
201
+ dialect: SqlDialect;
202
202
  pool: MysqlPool | PgPool;
203
203
  table?: string;
204
204
  });
@@ -76,7 +76,7 @@ export class SqlBlobBackend {
76
76
  this.pool = pool;
77
77
  this.explicitCapBytes = maxBytes;
78
78
  this.table = table;
79
- this.db = dialect === "tidb" ? mysqlDriver(pool) : pgDriver(pool);
79
+ this.db = dialect === "mysql" ? mysqlDriver(pool) : pgDriver(pool);
80
80
  this.log = log ?? createLogger();
81
81
  }
82
82
  async putBlob(hash, bytes) {
@@ -90,7 +90,7 @@ export class SqlBlobBackend {
90
90
  if (judged.refusal !== undefined)
91
91
  throw new BlobTooLargeError(bytes.byteLength, judged.refusal.cap);
92
92
  try {
93
- await judged.conn.query(this.dialect === "tidb"
93
+ await judged.conn.query(this.dialect === "mysql"
94
94
  ? `INSERT INTO ${this.table} (blob_hash, byte_len, bytes, created_at) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)`
95
95
  : `INSERT INTO ${this.table} (blob_hash, byte_len, bytes, created_at) VALUES ($1,$2,$3,$4) ON CONFLICT (blob_hash) DO UPDATE SET created_at = EXCLUDED.created_at`, [hash, bytes.byteLength, Buffer.from(bytes), new Date()]);
96
96
  }
@@ -99,7 +99,7 @@ export class SqlBlobBackend {
99
99
  }
100
100
  }
101
101
  async getBlob(hash) {
102
- if (this.dialect === "tidb") {
102
+ if (this.dialect === "mysql") {
103
103
  const [rows] = await this.pool.query(`SELECT bytes FROM ${this.table} WHERE blob_hash = ?`, [hash]);
104
104
  return rows[0]?.bytes != null ? new Uint8Array(rows[0].bytes) : undefined;
105
105
  }
@@ -118,7 +118,7 @@ async function sqlHasBlobs(dialect, pool, hashes, table = "snapshot_blob") {
118
118
  if (hashes.length === 0)
119
119
  return new Set();
120
120
  const distinct = [...new Set(hashes)];
121
- if (dialect === "tidb") {
121
+ if (dialect === "mysql") {
122
122
  const ph = distinct.map(() => "?").join(",");
123
123
  const [rows] = await pool.query(`SELECT blob_hash FROM ${table} WHERE blob_hash IN (${ph})`, distinct);
124
124
  return new Set(rows.map((r) => String(r.blob_hash)));
@@ -135,7 +135,7 @@ export async function sqlBlobSizes(dialect, pool, hashes) {
135
135
  const out = new Map();
136
136
  for (let i = 0; i < distinct.length; i += CHUNK) {
137
137
  const part = distinct.slice(i, i + CHUNK);
138
- if (dialect === "tidb") {
138
+ if (dialect === "mysql") {
139
139
  const ph = part.map(() => "?").join(",");
140
140
  const [rows] = await pool.query(`SELECT blob_hash, byte_len FROM snapshot_blob WHERE blob_hash IN (${ph})`, part);
141
141
  for (const r of rows)
@@ -155,7 +155,7 @@ async function sqlDeleteIndexRowsPastGrace(dialect, pool, hashes, table = "snaps
155
155
  return [];
156
156
  const distinct = [...new Set(hashes)];
157
157
  const cutoff = new Date(Date.now() - BLOB_GC_GRACE_MS);
158
- if (dialect === "tidb") {
158
+ if (dialect === "mysql") {
159
159
  const ph = distinct.map(() => "?").join(",");
160
160
  const grace = await pool
161
161
  .query(`SELECT blob_hash FROM ${table} WHERE created_at < ? AND blob_hash IN (${ph})`, [cutoff, ...distinct])
@@ -216,7 +216,7 @@ export class MinioBlobBackend {
216
216
  throw new Error(`MinioBlobBackend.putBlob ${hash}: HTTP ${res.status} ${detail.slice(0, 200)}`);
217
217
  }
218
218
  if (this.indexPool && this.indexDialect) {
219
- if (this.indexDialect === "tidb") {
219
+ if (this.indexDialect === "mysql") {
220
220
  await this.indexPool.query(`INSERT INTO ${this.indexTable} (blob_hash, byte_len, bytes, created_at) VALUES (?,?,NULL,?) ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)`, [hash, bytes.byteLength, new Date()]);
221
221
  }
222
222
  else {