@sema-agent/server 7.50.0 → 7.52.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 (222) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/USAGE.md +83 -20
  4. package/dist/adoption/plan.d.ts +4 -4
  5. package/dist/adoption/plan.js +11 -0
  6. package/dist/approval-card.d.ts +26 -2
  7. package/dist/approval-card.js +9 -0
  8. package/dist/approval-hmac.d.ts +1 -1
  9. package/dist/approval-reconciler.d.ts +2 -2
  10. package/dist/approval-reconciler.js +1 -1
  11. package/dist/bench/l8/artifact.d.ts +1 -1
  12. package/dist/bench/s1/arms.d.ts +1 -1
  13. package/dist/bench/s1/live-deps.d.ts +1 -1
  14. package/dist/bench/s1/oracle.d.ts +1 -1
  15. package/dist/bench/s1/row.d.ts +1 -1
  16. package/dist/boot/budget-tracing.d.ts +1 -1
  17. package/dist/boot/budget-tracing.js +2 -5
  18. package/dist/boot/coordinators.js +3 -1
  19. package/dist/boot/deferred-sandbox-path-env.d.ts +1 -1
  20. package/dist/boot/device-lane.d.ts +79 -0
  21. package/dist/boot/device-lane.js +63 -0
  22. package/dist/boot/engine-lease.d.ts +1 -1
  23. package/dist/boot/execution-env.d.ts +48 -2
  24. package/dist/boot/execution-env.js +57 -5
  25. package/dist/boot/governance-seams.d.ts +2 -2
  26. package/dist/boot/leader.d.ts +1 -1
  27. package/dist/boot/memory-boundary.d.ts +2 -2
  28. package/dist/boot/memory-consolidation.d.ts +6 -6
  29. package/dist/boot/org-memory.d.ts +1 -1
  30. package/dist/boot/parked-revive-gate.d.ts +3 -3
  31. package/dist/boot/reapers.d.ts +2 -2
  32. package/dist/boot/reapers.js +17 -4
  33. package/dist/boot/resolve-spec.d.ts +15 -0
  34. package/dist/boot/resolve-spec.js +40 -15
  35. package/dist/boot/retention-lane.d.ts +2 -2
  36. package/dist/boot/runner-deps.d.ts +17 -7
  37. package/dist/boot/runner-deps.js +4 -2
  38. package/dist/boot/runtime-caps.d.ts +23 -0
  39. package/dist/boot/runtime-caps.js +24 -2
  40. package/dist/boot/session-faces.d.ts +6 -1
  41. package/dist/boot/session-faces.js +17 -3
  42. package/dist/boot/shutdown.d.ts +8 -0
  43. package/dist/boot/shutdown.js +3 -1
  44. package/dist/boot/stores.js +40 -1
  45. package/dist/boot/task-list-lane.d.ts +2 -2
  46. package/dist/brain.js +51 -31
  47. package/dist/budget.d.ts +2 -2
  48. package/dist/capabilities/center-plugins.d.ts +1 -1
  49. package/dist/capabilities/collab-workflows.d.ts +1 -1
  50. package/dist/capabilities/hands-lane.d.ts +3 -3
  51. package/dist/capabilities/scenarios.d.ts +3 -3
  52. package/dist/capabilities/skills.d.ts +1 -1
  53. package/dist/config-center/hot-keys-registry.js +5 -0
  54. package/dist/config-center/http-client.d.ts +2 -2
  55. package/dist/config-center/read-face.d.ts +1 -1
  56. package/dist/config-center/restart-signal.d.ts +1 -1
  57. package/dist/config-center/types.d.ts +10 -3
  58. package/dist/config-invariants.d.ts +2 -2
  59. package/dist/config-invariants.js +18 -0
  60. package/dist/config-types.d.ts +90 -18
  61. package/dist/config.js +91 -19
  62. package/dist/deployment-governance.d.ts +1 -1
  63. package/dist/device-enrollment.d.ts +125 -0
  64. package/dist/device-enrollment.js +156 -0
  65. package/dist/device-store.d.ts +385 -0
  66. package/dist/device-store.js +407 -0
  67. package/dist/device-ws-hub.d.ts +182 -0
  68. package/dist/device-ws-hub.js +1012 -0
  69. package/dist/device-ws-protocol.d.ts +429 -0
  70. package/dist/device-ws-protocol.js +464 -0
  71. package/dist/env-facts.d.ts +5 -2
  72. package/dist/env-facts.js +1 -0
  73. package/dist/execution-lane-caps.d.ts +152 -0
  74. package/dist/execution-lane-caps.js +166 -0
  75. package/dist/fleet/fleet-bus.d.ts +1 -1
  76. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  77. package/dist/fleet/fleet-terminal-window.d.ts +1 -1
  78. package/dist/http/active-run-conflict.d.ts +2 -2
  79. package/dist/http/principal-gate.d.ts +1 -1
  80. package/dist/http/route-ctx.d.ts +24 -4
  81. package/dist/http/routes/admin-memory-optout.d.ts +48 -0
  82. package/dist/http/routes/admin-memory-optout.js +75 -0
  83. package/dist/http/routes/agents-roster.d.ts +1 -1
  84. package/dist/http/routes/approvals-assistant.js +21 -21
  85. package/dist/http/routes/capabilities.js +27 -11
  86. package/dist/http/routes/diagnostics.d.ts +1 -1
  87. package/dist/http/routes/leader.d.ts +1 -1
  88. package/dist/http/routes/memory-bundle.d.ts +1 -1
  89. package/dist/http/routes/memory-bundle.js +3 -1
  90. package/dist/http/routes/memory-compliance.d.ts +4 -4
  91. package/dist/http/routes/memory-origin.d.ts +4 -4
  92. package/dist/http/routes/memory-origin.js +3 -1
  93. package/dist/http/routes/memory-policy.js +6 -2
  94. package/dist/http/routes/retention-ops.d.ts +1 -1
  95. package/dist/http/routes/rules.d.ts +5 -2
  96. package/dist/http/routes/rules.js +84 -5
  97. package/dist/http/routes/run-memory-capture.d.ts +34 -0
  98. package/dist/http/routes/run-memory-capture.js +97 -0
  99. package/dist/http/routes/runs.d.ts +1 -0
  100. package/dist/http/routes/runs.js +212 -6
  101. package/dist/http/routes/session-sync.d.ts +4 -6
  102. package/dist/http/routes/session-sync.js +60 -47
  103. package/dist/http/routes/sessions.js +61 -188
  104. package/dist/http/routes/shared-memory.d.ts +1 -1
  105. package/dist/http/routes/tasks.js +1 -1
  106. package/dist/http/routes/workflows.js +19 -0
  107. package/dist/http/server.d.ts +49 -13
  108. package/dist/http/server.js +109 -13
  109. package/dist/http/verify-rounds.d.ts +1 -1
  110. package/dist/http/wire-types.d.ts +41 -8
  111. package/dist/leader/endpoint.d.ts +1 -1
  112. package/dist/leader/leader.d.ts +1 -1
  113. package/dist/leader/repair-oracle.d.ts +2 -2
  114. package/dist/leader/repair-oracle.js +1 -1
  115. package/dist/leader/wire.js +4 -2
  116. package/dist/main.js +23 -9
  117. package/dist/memory-bundle-engine.d.ts +1 -1
  118. package/dist/memory-operator-faces.d.ts +5 -5
  119. package/dist/memory-origin-fence.d.ts +50 -0
  120. package/dist/memory-origin-fence.js +15 -0
  121. package/dist/memory-posture.d.ts +13 -1
  122. package/dist/memory-posture.js +1 -0
  123. package/dist/memory-scope.d.ts +6 -1
  124. package/dist/memory-scope.js +3 -2
  125. package/dist/model-select.d.ts +2 -2
  126. package/dist/observability/fail-open.d.ts +17 -5
  127. package/dist/observability/fail-open.js +17 -5
  128. package/dist/orchestration/hardened-vm-runner.d.ts +9 -2
  129. package/dist/orchestration/hardened-vm-runner.js +11 -1
  130. package/dist/orchestration/hardened-vm-worker-runner.js +2 -2
  131. package/dist/orchestration/workflow-completion-inbox.d.ts +2 -2
  132. package/dist/plugins/background-shell-support.d.ts +2 -2
  133. package/dist/plugins/blob-backend.d.ts +7 -2
  134. package/dist/plugins/blob-backend.js +22 -18
  135. package/dist/plugins/caching-session-store.d.ts +7 -3
  136. package/dist/plugins/checkpoint-store-sql.d.ts +2 -2
  137. package/dist/plugins/device-store-sql.d.ts +130 -0
  138. package/dist/plugins/device-store-sql.js +574 -0
  139. package/dist/plugins/file-history-store-sql.d.ts +170 -0
  140. package/dist/plugins/file-history-store-sql.js +827 -0
  141. package/dist/plugins/file-run-store.d.ts +44 -5
  142. package/dist/plugins/file-run-store.js +85 -13
  143. package/dist/plugins/fork-routing-session-store.d.ts +1 -1
  144. package/dist/plugins/fork-routing-session-store.js +2 -2
  145. package/dist/plugins/leader-run-store-sql.d.ts +1 -1
  146. package/dist/plugins/local-session-store.d.ts +9 -1
  147. package/dist/plugins/memory-embedder-fingerprint.d.ts +1 -1
  148. package/dist/plugins/memory-optout-grant-store-sql.d.ts +139 -0
  149. package/dist/plugins/memory-optout-grant-store-sql.js +157 -0
  150. package/dist/plugins/memory-origin-law.d.ts +2 -2
  151. package/dist/plugins/memory-run-store.d.ts +4 -3
  152. package/dist/plugins/memory-run-store.js +2 -1
  153. package/dist/plugins/permission-rule-store-file.d.ts +16 -6
  154. package/dist/plugins/permission-rule-store-file.js +77 -13
  155. package/dist/plugins/permission-rule-store-sql.d.ts +156 -8
  156. package/dist/plugins/permission-rule-store-sql.js +123 -21
  157. package/dist/plugins/pg-pool.js +59 -0
  158. package/dist/plugins/pg-safe-json.d.ts +1 -1
  159. package/dist/plugins/remote-env-device.d.ts +271 -0
  160. package/dist/plugins/remote-env-device.js +727 -0
  161. package/dist/plugins/remote-env-k8s.d.ts +1 -1
  162. package/dist/plugins/remote-scratchpad.js +1 -1
  163. package/dist/plugins/retention-lane-store-sql.d.ts +3 -3
  164. package/dist/plugins/retention-store-sql.d.ts +7 -7
  165. package/dist/plugins/retention-store-sql.js +6 -0
  166. package/dist/plugins/run-store-sql.d.ts +9 -3
  167. package/dist/plugins/run-store-sql.js +2 -2
  168. package/dist/plugins/send-user-file.d.ts +1 -1
  169. package/dist/plugins/session-placement.d.ts +1 -1
  170. package/dist/plugins/sql-errors.d.ts +1 -1
  171. package/dist/plugins/store-backend.d.ts +68 -29
  172. package/dist/plugins/store-backend.js +78 -15
  173. package/dist/plugins/store-contracts.d.ts +5 -3
  174. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  175. package/dist/plugins/tidb-pool.js +59 -0
  176. package/dist/plugins/web-search.d.ts +1 -1
  177. package/dist/prompts-domain-validate.js +1 -1
  178. package/dist/resource-suspend.d.ts +1 -1
  179. package/dist/resource-window.d.ts +3 -3
  180. package/dist/rules-consent.d.ts +231 -9
  181. package/dist/rules-consent.js +411 -13
  182. package/dist/run-local.js +1 -0
  183. package/dist/runs.d.ts +15 -1
  184. package/dist/runs.js +19 -0
  185. package/dist/runtime-caps-resolver.d.ts +36 -1
  186. package/dist/runtime-caps-resolver.js +25 -0
  187. package/dist/runtime-governance.d.ts +1 -1
  188. package/dist/security.d.ts +5 -5
  189. package/dist/security.js +1 -1
  190. package/dist/session-sync.d.ts +52 -37
  191. package/dist/session-sync.js +19 -16
  192. package/dist/shared-memory-scope-authorizer.d.ts +1 -1
  193. package/dist/sighup-idle.d.ts +1 -1
  194. package/dist/spec-fields.d.ts +22 -1
  195. package/dist/spec-fields.js +11 -0
  196. package/dist/store-live-probe.d.ts +2 -2
  197. package/dist/task-a2a.d.ts +1 -1
  198. package/dist/task-cwd.d.ts +25 -0
  199. package/dist/task-cwd.js +3 -0
  200. package/dist/task-mcp.d.ts +3 -3
  201. package/dist/task-settings.d.ts +3 -3
  202. package/dist/task-settings.js +3 -1
  203. package/dist/task-workflow.d.ts +1 -1
  204. package/dist/text-bidi.d.ts +2 -2
  205. package/dist/tool-approval.d.ts +191 -9
  206. package/dist/tool-approval.js +248 -29
  207. package/dist/trace/core-keyset-guard.d.ts +1 -1
  208. package/dist/trace/engine-notice-wire.d.ts +6 -4
  209. package/dist/trace/engine-notice-wire.js +2 -0
  210. package/dist/trace/ledger-events.d.ts +1 -1
  211. package/dist/trace/ledger-sink.d.ts +2 -2
  212. package/dist/trace/project.d.ts +3 -2
  213. package/dist/trace/project.js +1 -0
  214. package/dist/turn-activity.d.ts +1 -1
  215. package/dist/wall-clock-jump-guard.d.ts +3 -3
  216. package/package.json +2 -2
  217. package/dist/http/tar.d.ts +0 -17
  218. package/dist/http/tar.js +0 -53
  219. package/dist/http/workspace-content.d.ts +0 -11
  220. package/dist/http/workspace-content.js +0 -20
  221. package/dist/plugins/file-snapshot-store-sql.d.ts +0 -170
  222. package/dist/plugins/file-snapshot-store-sql.js +0 -213
@@ -50,7 +50,9 @@ export declare class SqlBlobBackend implements BlobBackend {
50
50
  private readonly pool;
51
51
  /** Effective per-blob cap: explicit ctor value wins; else tidb=SQL_BLOB_DEFAULT_MAX_BYTES, pg=uncapped. */
52
52
  private readonly capBytes;
53
- constructor(dialect: "tidb" | "pg", pool: MysqlPool | PgPool, maxBytes?: number);
53
+ /** Blob-domain table (S-15 blob 域隔离:file-history 域用自己的表,reap/GC 不跨域)。代码内字面量,非用户输入。 */
54
+ private readonly table;
55
+ constructor(dialect: "tidb" | "pg", pool: MysqlPool | PgPool, maxBytes?: number, table?: string);
54
56
  /** The created_at-refreshing content-addressed upsert — VERBATIM the tidb/pg stores' `putBlobRow`. Refreshes
55
57
  * created_at even on a dedup hit so a re-used/just-written blob is "touched=now" and GC-grace-protected.
56
58
  * (a): an over-cap blob is REJECTED (typed {@link BlobTooLargeError}) BEFORE the driver ever sees the bytes —
@@ -103,7 +105,7 @@ export interface MinioBlobBackendConfig {
103
105
  * importManifest content-address check the SQL stores already do.
104
106
  */
105
107
  /** 鲁棒性批2 C2/C3/C6(2026-08-03):S3 兼容端点 IO 的统一 abort 上界(fleet-client C1 定常量形
106
- * 先例)。半开端点此前各挂 undici 默认 ~300s headersTimeout——blob GET 钉死 restore/session-import
108
+ * 先例)。半开端点此前各挂 undici 默认 ~300s headersTimeout——blob GET 卡住 restore/session-import
107
109
  * 同步路径,SendUserFile 工具调用永不返回。120s 对慢链路 ~64MiB 单 PUT/GET 仍宽裕(presignTtlSec
108
110
  * 注释同口径),远小于病形。send-user-file 与 leader/diffup 同 import 此常量。 */
109
111
  export declare const OBJECT_STORE_IO_TIMEOUT_MS = 120000;
@@ -117,9 +119,12 @@ export declare class MinioBlobBackend implements BlobBackend {
117
119
  * path). The file-snapshot stores ALWAYS pass the pool, so the live reference-tracking always has the index. */
118
120
  private readonly indexPool?;
119
121
  private readonly indexDialect?;
122
+ /** Blob-domain index table (S-15 域隔离,SqlBlobBackend.table 同款;默认 = E19 纪元的 snapshot_blob)。 */
123
+ private readonly indexTable;
120
124
  constructor(cfg: MinioBlobBackendConfig, index?: {
121
125
  dialect: "tidb" | "pg";
122
126
  pool: MysqlPool | PgPool;
127
+ table?: string;
123
128
  });
124
129
  private objectKey;
125
130
  private presign;
@@ -27,48 +27,50 @@ export class SqlBlobBackend {
27
27
  dialect;
28
28
  pool;
29
29
  capBytes;
30
- constructor(dialect, pool, maxBytes) {
30
+ table;
31
+ constructor(dialect, pool, maxBytes, table = "snapshot_blob") {
31
32
  this.dialect = dialect;
32
33
  this.pool = pool;
33
34
  this.capBytes = maxBytes ?? (dialect === "tidb" ? SQL_BLOB_DEFAULT_MAX_BYTES : Number.POSITIVE_INFINITY);
35
+ this.table = table;
34
36
  }
35
37
  async putBlob(hash, bytes) {
36
38
  if (bytes.byteLength > this.capBytes)
37
39
  throw new BlobTooLargeError(bytes.byteLength, this.capBytes);
38
40
  if (this.dialect === "tidb") {
39
- await this.pool.query("INSERT INTO snapshot_blob (blob_hash, byte_len, bytes, created_at) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)", [hash, bytes.byteLength, Buffer.from(bytes), new Date()]);
41
+ await this.pool.query(`INSERT INTO ${this.table} (blob_hash, byte_len, bytes, created_at) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)`, [hash, bytes.byteLength, Buffer.from(bytes), new Date()]);
40
42
  }
41
43
  else {
42
- await this.pool.query("INSERT INTO snapshot_blob (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()]);
44
+ await this.pool.query(`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()]);
43
45
  }
44
46
  }
45
47
  async getBlob(hash) {
46
48
  if (this.dialect === "tidb") {
47
- const [rows] = await this.pool.query("SELECT bytes FROM snapshot_blob WHERE blob_hash = ?", [hash]);
49
+ const [rows] = await this.pool.query(`SELECT bytes FROM ${this.table} WHERE blob_hash = ?`, [hash]);
48
50
  return rows[0]?.bytes != null ? new Uint8Array(rows[0].bytes) : undefined;
49
51
  }
50
- const b = await this.pool.query("SELECT bytes FROM snapshot_blob WHERE blob_hash = $1", [hash]);
52
+ const b = await this.pool.query(`SELECT bytes FROM ${this.table} WHERE blob_hash = $1`, [hash]);
51
53
  return b.rows[0]?.bytes != null ? new Uint8Array(b.rows[0].bytes) : undefined;
52
54
  }
53
55
  async hasBlobs(hashes) {
54
- return sqlHasBlobs(this.dialect, this.pool, hashes);
56
+ return sqlHasBlobs(this.dialect, this.pool, hashes, this.table);
55
57
  }
56
58
  async deleteBlobs(hashes) {
57
- const deleted = await sqlDeleteIndexRowsPastGrace(this.dialect, this.pool, hashes);
59
+ const deleted = await sqlDeleteIndexRowsPastGrace(this.dialect, this.pool, hashes, this.table);
58
60
  return deleted.length;
59
61
  }
60
62
  }
61
- async function sqlHasBlobs(dialect, pool, hashes) {
63
+ async function sqlHasBlobs(dialect, pool, hashes, table = "snapshot_blob") {
62
64
  if (hashes.length === 0)
63
65
  return new Set();
64
66
  const distinct = [...new Set(hashes)];
65
67
  if (dialect === "tidb") {
66
68
  const ph = distinct.map(() => "?").join(",");
67
- const [rows] = await pool.query(`SELECT blob_hash FROM snapshot_blob WHERE blob_hash IN (${ph})`, distinct);
69
+ const [rows] = await pool.query(`SELECT blob_hash FROM ${table} WHERE blob_hash IN (${ph})`, distinct);
68
70
  return new Set(rows.map((r) => String(r.blob_hash)));
69
71
  }
70
72
  const ph = distinct.map((_, i) => `$${i + 1}`).join(",");
71
- const r = await pool.query(`SELECT blob_hash FROM snapshot_blob WHERE blob_hash IN (${ph})`, distinct);
73
+ const r = await pool.query(`SELECT blob_hash FROM ${table} WHERE blob_hash IN (${ph})`, distinct);
72
74
  return new Set(r.rows.map((row) => row.blob_hash));
73
75
  }
74
76
  export async function sqlBlobSizes(dialect, pool, hashes) {
@@ -94,7 +96,7 @@ export async function sqlBlobSizes(dialect, pool, hashes) {
94
96
  }
95
97
  return out;
96
98
  }
97
- async function sqlDeleteIndexRowsPastGrace(dialect, pool, hashes) {
99
+ async function sqlDeleteIndexRowsPastGrace(dialect, pool, hashes, table = "snapshot_blob") {
98
100
  if (hashes.length === 0)
99
101
  return [];
100
102
  const distinct = [...new Set(hashes)];
@@ -102,20 +104,20 @@ async function sqlDeleteIndexRowsPastGrace(dialect, pool, hashes) {
102
104
  if (dialect === "tidb") {
103
105
  const ph = distinct.map(() => "?").join(",");
104
106
  const grace = await pool
105
- .query(`SELECT blob_hash FROM snapshot_blob WHERE created_at < ? AND blob_hash IN (${ph})`, [cutoff, ...distinct])
107
+ .query(`SELECT blob_hash FROM ${table} WHERE created_at < ? AND blob_hash IN (${ph})`, [cutoff, ...distinct])
106
108
  .then(([rows]) => rows.map((r) => String(r.blob_hash)))
107
109
  .catch(() => []);
108
110
  if (grace.length === 0)
109
111
  return [];
110
112
  const gph = grace.map(() => "?").join(",");
111
113
  await pool
112
- .query(`DELETE FROM snapshot_blob WHERE created_at < ? AND blob_hash IN (${gph})`, [cutoff, ...grace])
114
+ .query(`DELETE FROM ${table} WHERE created_at < ? AND blob_hash IN (${gph})`, [cutoff, ...grace])
113
115
  .catch(() => undefined);
114
116
  return grace;
115
117
  }
116
118
  const ph = distinct.map((_, i) => `$${i + 2}`).join(",");
117
119
  const r = await pool
118
- .query(`DELETE FROM snapshot_blob WHERE created_at < $1 AND blob_hash IN (${ph}) RETURNING blob_hash`, [cutoff, ...distinct])
120
+ .query(`DELETE FROM ${table} WHERE created_at < $1 AND blob_hash IN (${ph}) RETURNING blob_hash`, [cutoff, ...distinct])
119
121
  .catch(() => undefined);
120
122
  return (r?.rows ?? []).map((row) => row.blob_hash);
121
123
  }
@@ -126,10 +128,12 @@ export class MinioBlobBackend {
126
128
  ttl;
127
129
  indexPool;
128
130
  indexDialect;
131
+ indexTable;
129
132
  constructor(cfg, index) {
130
133
  this.cfg = cfg;
131
134
  this.keyPrefix = cfg.keyPrefix ?? "blobs/";
132
135
  this.ttl = cfg.presignTtlSec ?? 3600;
136
+ this.indexTable = index?.table ?? "snapshot_blob";
133
137
  if (index) {
134
138
  this.indexDialect = index.dialect;
135
139
  this.indexPool = index.pool;
@@ -159,10 +163,10 @@ export class MinioBlobBackend {
159
163
  }
160
164
  if (this.indexPool && this.indexDialect) {
161
165
  if (this.indexDialect === "tidb") {
162
- await this.indexPool.query("INSERT INTO snapshot_blob (blob_hash, byte_len, bytes, created_at) VALUES (?,?,NULL,?) ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)", [hash, bytes.byteLength, new Date()]);
166
+ 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()]);
163
167
  }
164
168
  else {
165
- await this.indexPool.query("INSERT INTO snapshot_blob (blob_hash, byte_len, bytes, created_at) VALUES ($1,$2,NULL,$3) ON CONFLICT (blob_hash) DO UPDATE SET created_at = EXCLUDED.created_at", [hash, bytes.byteLength, new Date()]);
169
+ await this.indexPool.query(`INSERT INTO ${this.indexTable} (blob_hash, byte_len, bytes, created_at) VALUES ($1,$2,NULL,$3) ON CONFLICT (blob_hash) DO UPDATE SET created_at = EXCLUDED.created_at`, [hash, bytes.byteLength, new Date()]);
166
170
  }
167
171
  }
168
172
  }
@@ -188,7 +192,7 @@ export class MinioBlobBackend {
188
192
  }
189
193
  async hasBlobs(hashes) {
190
194
  if (this.indexPool && this.indexDialect)
191
- return sqlHasBlobs(this.indexDialect, this.indexPool, hashes);
195
+ return sqlHasBlobs(this.indexDialect, this.indexPool, hashes, this.indexTable);
192
196
  const distinct = [...new Set(hashes)];
193
197
  const present = new Set();
194
198
  await mapBounded(distinct, MINIO_OP_CONCURRENCY, async (hash) => {
@@ -254,7 +258,7 @@ export class MinioBlobBackend {
254
258
  return 0;
255
259
  let toDelete;
256
260
  if (this.indexPool && this.indexDialect) {
257
- toDelete = await sqlDeleteIndexRowsPastGrace(this.indexDialect, this.indexPool, hashes);
261
+ toDelete = await sqlDeleteIndexRowsPastGrace(this.indexDialect, this.indexPool, hashes, this.indexTable);
258
262
  }
259
263
  else {
260
264
  toDelete = [...new Set(hashes)];
@@ -55,8 +55,12 @@ export declare class CachingSessionStore implements SessionStore {
55
55
  * run per session (→ listSessions `lastRunId`). MUST be forwarded: the local backend's Runner store is THIS cache
56
56
  * wrapper, so without forwarding `noteTaskRun?.()` silently no-ops and lastRunId stays null (review BLOCKER). Pure
57
57
  * pass-through to the inner store's in-mem map — no cache interaction. (TiDB/PG don't implement it → not bound.)
58
- * Return type matches core's `SessionStore.noteTaskRun?(): void | Promise<void>` (review: don't narrow to void). */
59
- readonly noteTaskRun?: (sessionId: string, taskId: string) => void | Promise<void>;
58
+ * Return type matches core's `SessionStore.noteTaskRun?(): void | Promise<void>` (review: don't narrow to void).
59
+ * #499(core 5.65 增量批):第三参 = engine runId。绑定用的是 `.bind(inner)`(整函数),运行期本就
60
+ * 三参全转 —— 这里把**声明**对齐到 core 的三参形,免得读这行类型的人以为缝上只有两参(fork-routing
61
+ * 的两参形正是在运行期真丢过第三参的那个病,同批修)。内店(LocalSessionStore)今日仍是两参消费,
62
+ * runId 落地候 sessions 列表 wire 键的 spec 节拍(见本批提货报告件1b)。 */
63
+ readonly noteTaskRun?: (sessionId: string, taskId: string, runId?: string) => void | Promise<void>;
60
64
  /** Write-once auto-title (SQL twins + local sidecar carry it; pure pass-through). */
61
65
  readonly setTitleIfNull?: (sessionId: string, title: string) => Promise<boolean>;
62
66
  readonly probeTitle?: (sessionId: string) => Promise<"none" | "untitled" | "titled">;
@@ -85,7 +89,7 @@ export declare class CachingSessionStore implements SessionStore {
85
89
  /** [2024](cli 取证)2c session-sync P1d-β — delegated STAGED import seam. 此前 staging 族不在本类的逐方法
86
90
  * 转发白名单里 ⇒ 默认部署(SESSION_CACHE_TTL_SEC=300 把真店包进本类)PUSH Phase A 恒 501
87
91
  * `capability.session_store_required`,而 capabilities().sessionSync 谓词三项(backend/exportEntries/
88
- * fileSnapshotStore)都被转发了 ⇒ 照报 true(能力面谎)。转发形:handle 的 `commit` 是唯一写真 id 的
92
+ * fileHistoryStore)都被转发了 ⇒ 照报 true(能力面谎)。转发形:handle 的 `commit` 是唯一写真 id 的
89
93
  * 时刻,成功后逐出该 id 的暖 handle + S25 指纹(importEntries/replaceEntries 同纪律);`abort` 只丢
90
94
  * staging 行、不碰真 id ⇒ 不逐出。 */
91
95
  readonly beginImportStaging?: (realSessionId: string, token: string) => StagingHandle;
@@ -14,7 +14,7 @@ import { type SqlDriver } from "./sql-driver.js";
14
14
  */
15
15
  export declare const TERMINAL_BACKSTOP_MS: number;
16
16
  /**
17
- * design/80 D-D (adversarial fix): the crash-safe `terminal_at_ms` backstop must fall STRICTLY AFTER any SLA
17
+ * design/80 D-D (cross-review fix): the crash-safe `terminal_at_ms` backstop must fall STRICTLY AFTER any SLA
18
18
  * `deadline`, never AT it. `terminal_at_ms = max(createdAt+backstop, deadline)` made the two coincide whenever an
19
19
  * operator tuned APPROVAL_TERMINAL_BACKSTOP_MS at/below the SLA — and reapExpired's terminal_at_ms-branch (which
20
20
  * has NO gate_kind filter) then abort-EXPIRED a human/irreversible_ask gate in the SAME tick the deny-sweep
@@ -116,7 +116,7 @@ export interface PendingCheckpoint {
116
116
  * 与同行 `tool_input` 在 mint 就 `redactDeep` 是**同一条**纪律(#209 件4 把 `shadowedRule` 修在读面
117
117
  * 是因为那条路的行早已落库、改铸点救不了存量;本键是**新**列,没有存量行要救,修在铸点更彻底:
118
118
  * 秘密从此连**落库**都不落,不是落了库再在两条读面各洗一遍)。
119
- * 3. **脱敏之后**才截长(codex 对抗复审 [medium],验真后修):`redactSecrets` 会**变长**,先按限长校验
119
+ * 3. **脱敏之后**才截长(codex 交叉复审 [medium],验真后修):`redactSecrets` 会**变长**,先按限长校验
120
120
  * 原文再脱敏会产出超限文本,而 SQL 腿有**回读**(列 → 本函数再跑一遍)、LOCAL 腿没有 ⇒ 同一份素材
121
121
  * 两个后端给出不同卡面。顺序反过来之后本函数对自己的输出**幂等**,两条腿同形。完整论证见
122
122
  * `RuleOfferRawSchema` 顶注。
@@ -0,0 +1,130 @@
1
+ /**
2
+ * device lane 四表持久层(design/device-executor-lane-v2 §6)—— `DeviceStore` 双方言实现
3
+ * (`TiDBDeviceStore` / `PgDeviceStore`,SINGLE-FILE DUAL-DIALECT,design/158 A12 形:一个
4
+ * `SqlDeviceStore` 接 `SqlDriver`,两个薄 ctor 子类落方言绑定)。语义真源 = `../device-store.js`
5
+ * (类型 + 闭集 + InMemory 参照实现),准入链 = `../device-enrollment.js`。
6
+ *
7
+ * 四张表:
8
+ * · `devices` —— 设备注册簿 + presence 投影(单设备单活连 ⇒ 1:1,省一次 join,§6 注①)
9
+ * · `device_enroll_tokens` —— 一次性 enrollment 凭据(明文一现,落库只有 sha256)
10
+ * · `device_sessions` —— **根会话**↔设备绑定(resume 路由真源,§4.5)
11
+ * · `device_audit` —— 安全轴事件审计(§4.8 标「审计行」的 8 个 event)
12
+ * 刻意**没有** `device_instructions` 表:指令 pending 表是内存态(§6 注③——指令比 run 短命,落库只会
13
+ * 造「表里 pending、run 已死」的僵尸行;at-most-once 靠 §5.3 投递边界,不靠指令落库)。
14
+ *
15
+ * ── CAS 铁则(与 approval-ask-store-sql.ts 头注同源,写在这里防遗忘)─────────────────────────────
16
+ * 1. 一切转移带 `WHERE <前态>`,赢 = affected 恰为 1。禁读-改-写两步(TOCTOU 窗口)。
17
+ * 2. 🔴 MySQL/TiDB `affectedRows` 坑:同值 UPDATE(SET 的新值与当前列逐字节相同)affectedRows=0
18
+ * ——**所有** CAS UPDATE 因此必须带一个恒变列(`rev = rev + 1`)。本文件每一条 CAS UPDATE 都带。
19
+ * 在本店这条尤其致命:心跳续租在「同一毫秒内重复心跳」时 SET 的值可能与当前值相同,没有 rev
20
+ * 就会被判成「本连接已失权」⇒ 设备被自己的心跳踢下线。
21
+ * 3. 事务:`SqlDriver.connect()` → `begin()`/`beginPessimistic()` → `query()` → `commit()`,失败
22
+ * `rollback()`。**失败臂的回读必须走当前这条 `conn`**(approval 店复审 F1 的真缺陷:走池级 query
23
+ * 会与自己持有的连接死锁),且**先 rollback 再读**(读到最新已提交视图)。
24
+ *
25
+ * ── 方言差异(A12 doctrine:每处显式写在调用点)────────────────────────────────────────────────────
26
+ * - `?`(位置序) vs `$n`(显式编号);动态 WHERE(`listAudit`)靠 `ph(dialect, n)` 生成。
27
+ * - `INSERT IGNORE` vs `ON CONFLICT (pk) DO NOTHING`(`bindSession` 的首绑 CAS)。
28
+ * - `AUTO_INCREMENT` vs `BIGSERIAL`;`JSON` vs `JSONB`(+ `$n::jsonb` 显式 cast)。
29
+ * - `KEY …` 内联 vs 独立 `CREATE INDEX IF NOT EXISTS`。
30
+ * - 回读名单:PG 用 `UPDATE … RETURNING`,TiDB 无 RETURNING ⇒ 同事务 `UPDATE` 后 `SELECT`。
31
+ * - 时长运算:`NOW(3) + INTERVAL ? SECOND` vs `now() + ($n::double precision * INTERVAL '1 second')`。
32
+ * - 键列字节等价:MySQL `VARBINARY` vs PG `VARCHAR … COLLATE "C"`(理由见 ../device-store.ts 头注;
33
+ * `utf8mb4_bin` **不够**,它仍是 PAD SPACE collation)。
34
+ * - VARBINARY 的读回:mysql2 给 **Buffer**,PG 给 string ⇒ 一律过 `bufToStr`(mailbox 真库坑同解)。
35
+ *
36
+ * ── 时间列的存取形(§4.6 task-R3-F18「时间权威 = DB」的落地)──────────────────────────────────────
37
+ * 列是原生 `DATETIME(3)` / `TIMESTAMPTZ(3)`,但**跨边界一律是 epoch 毫秒**:写侧送**时长秒**由 SQL 从
38
+ * DB 的 now 起算,读侧在 SELECT 里就转成 `*_ms`。
39
+ * 🔴 为什么不让驱动把时间戳解成 JS `Date` 再比:①mysql2 按**进程**时区解析 DATETIME,DB 会话时区与
40
+ * Node 时区不同即整体偏移几小时(而这条偏移落在「租约还有效吗」上就是双主);②`Date.now()` 一旦进入
41
+ * 判据,副本时钟落后即越租。两条都在 SELECT 表达式里就地关掉。
42
+ */
43
+ import type { Pool as MySqlPool } from "mysql2/promise";
44
+ import type { Pool as PgPool } from "pg";
45
+ import { type SqlDriver } from "./sql-driver.js";
46
+ import { type AcquireConnectionInput, type AcquireConnectionResult, type AppendAuditInput, type BindSessionInput, type BindSessionResult, type ConsumeEnrollTokenInput, type ConsumeEnrollTokenResult, type DeviceAdmissionView, type DeviceAuditRow, type DeviceEnrollTokenRow, type DeviceOwner, type DeviceRow, type DeviceSessionRow, type DeviceStore, type HeartbeatConnectionInput, type IssueEnrollTokenInput, type ListAuditFilter, type RevokeDeviceInput, type RevokeDeviceResult } from "../device-store.js";
47
+ /**
48
+ * ── 命名(**落库一律单数**,与设计稿的复数形不同)──────────────────────────────────────────────────
49
+ * design v2 §6 的 DDL 写的是 `devices` / `device_sessions` / `device_enrollment_tokens`(复数),但本仓有一条
50
+ * 常驻机器门:`test/schema-naming-invariants.test.ts` 判据① —— 建表的表名必须在 `TABLE_VOCABULARY` 闭集里
51
+ * **且词表全是单数形**。#270 车1 的 `retention_hold` / `retention_tombstone` 是同一处先例(那份设计稿同样
52
+ * 写的复数,落库单数)。所以四张表落库名为 `device` / `device_session` / `device_enroll_token` /
53
+ * `device_audit`,**表结构与设计稿逐列一致**,只有名字随仓规。新表 = 先进词表再建。
54
+ */
55
+ export declare const DEVICES_TABLE = "device";
56
+ export declare const DEVICE_ENROLL_TOKENS_TABLE = "device_enroll_token";
57
+ export declare const DEVICE_SESSIONS_TABLE = "device_session";
58
+ export declare const DEVICE_AUDIT_TABLE = "device_audit";
59
+ export declare const TIDB_DEVICE_STATEMENTS: readonly string[];
60
+ /** {@link TIDB_DEVICE_STATEMENTS} 的遍历壳(集成测试用;生产装配由中央 ensureSchema 展开同一份数组)。 */
61
+ export declare function ensureTiDBDeviceSchema(pool: MySqlPool): Promise<void>;
62
+ type PgQuery = (sql: string, params?: unknown[]) => Promise<unknown>;
63
+ export declare function ensurePgDeviceSchema(q: PgQuery): Promise<void>;
64
+ export declare class SqlDeviceStore implements DeviceStore {
65
+ private readonly db;
66
+ constructor(db: SqlDriver);
67
+ private q;
68
+ private json;
69
+ issueEnrollToken(input: IssueEnrollTokenInput): Promise<DeviceEnrollTokenRow>;
70
+ getEnrollToken(tokenHash: string): Promise<DeviceEnrollTokenRow | null>;
71
+ /**
72
+ * §6 注册协议 —— **单事务**:消费 CAS + INSERT devices + 审计行一起提交。
73
+ *
74
+ * 谓词四件(缺一即 affected=0):`consumed_at IS NULL`(一次性)、`expires_at > NOW(3)`(TTL,DB 钟)、
75
+ * `target_tenant/target_subject` 等值(绑定 principal)。affected=0 的**理由**靠回滚后的回读判别 ——
76
+ * 顺序刻意是「先 principal 后消费态」:token 不是你的就连「它被没被用过」都不告诉你。
77
+ */
78
+ consumeEnrollToken(input: ConsumeEnrollTokenInput): Promise<ConsumeEnrollTokenResult>;
79
+ /** 事务内/持连接期间的设备回读(见 consumeEnrollToken 的 F2 注:此期间**禁**走池级读)。 */
80
+ private readDeviceOn;
81
+ readDeviceForAdmission(deviceId: string, owner: DeviceOwner): Promise<DeviceAdmissionView | null>;
82
+ getDeviceUnscoped(deviceId: string): Promise<DeviceRow | null>;
83
+ listDevicesByOwner(owner: DeviceOwner): Promise<DeviceRow[]>;
84
+ /**
85
+ * 吊销 = 终态 CAS(`status='active'` 前态)+ **租约作废 + 世代推进**(§8-R4 在途语义):
86
+ * 世代一推,在途连接的每一次带世代谓词的写(心跳/结果帧入账)当场影响 0 行 ⇒ 设备侧立即失权。
87
+ */
88
+ revokeDevice(input: RevokeDeviceInput): Promise<RevokeDeviceResult>;
89
+ /**
90
+ * 连接建立 = 带 expiry 的租约 CAS。谓词 `lease_expires_at IS NULL OR <= NOW(3)`:**活租约恒不被撕**
91
+ * (§5.2 单活连不变量;`DEVICE_SUPERSEDE=stale_only` 的语义就在这一句),过期租约才可被新连夺取。
92
+ * 赢家 `conn_generation` 单调 +1,后续所有写都带这个世代谓词。
93
+ */
94
+ acquireConnection(input: AcquireConnectionInput): Promise<AcquireConnectionResult>;
95
+ /**
96
+ * 心跳续租 —— 谓词 = **世代等值 + active**。影响 0 行 = 本连接已失权(被顶替 / 被吊销)⇒ 调用方
97
+ * 主动断开(§4.6 步 2)。
98
+ *
99
+ * 🔴 允许续一条**已过期但没被别人夺走**的租约(generation 未变 ⇒ 排他性仍在本连接手上);夺租者
100
+ * 一旦成功,generation 就变了,这条心跳当场 0 行。`rev = rev + 1` 是 CAS 铁则②:同毫秒重复心跳的
101
+ * SET 值可能与当前逐字节相同,没有它 MySQL 会把「成功」报成 affectedRows=0。
102
+ */
103
+ heartbeatConnection(input: HeartbeatConnectionInput): Promise<boolean>;
104
+ releaseConnection(deviceId: string, generation: number): Promise<boolean>;
105
+ /**
106
+ * §4.3.2 首绑写协议 —— **单事务**:设备校验(active ∧ owner)与 `INSERT IGNORE` 在一个事务里。
107
+ *
108
+ * 🔴 `beginPessimistic` + `FOR UPDATE`:设备行必须是**当前读**。快照读下,一次与吊销并发的绑定会
109
+ * 读到「还 active」的旧版本,于是把 session 绑到一台**刚被吊销**的设备上(sql-driver.ts 头注点名的
110
+ * double-admit 形)。
111
+ * 并发首绑:`INSERT IGNORE` / `ON CONFLICT DO NOTHING` 的 affected 是引擎对「谁赢」的权威回答;
112
+ * 输家回读既有行 —— 同 deviceId = 幂等成功,异 deviceId = `conflict`(无未定义态)。
113
+ */
114
+ bindSession(input: BindSessionInput): Promise<BindSessionResult>;
115
+ getSessionBinding(rootSessionId: string): Promise<DeviceSessionRow | null>;
116
+ deleteByRootSession(rootSessionId: string): Promise<number>;
117
+ appendAudit(input: AppendAuditInput): Promise<void>;
118
+ /** 审计写的**唯一**语句(池级与事务内共用;`SqlExec` 是两者的公共面)。 */
119
+ private insertAuditOn;
120
+ listAudit(filter?: ListAuditFilter): Promise<DeviceAuditRow[]>;
121
+ }
122
+ /** 方言绑定薄壳(A12 形:ctor 收池,行为全在基类)。 */
123
+ export declare class TiDBDeviceStore extends SqlDeviceStore {
124
+ constructor(pool: MySqlPool);
125
+ }
126
+ export declare class PgDeviceStore extends SqlDeviceStore {
127
+ constructor(pool: PgPool);
128
+ }
129
+ export {};
130
+ //# sourceMappingURL=device-store-sql.d.ts.map