@sema-agent/server 7.130.0 → 7.132.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 (294) hide show
  1. package/MIGRATION.md +396 -0
  2. package/README.md +13 -6
  3. package/README.zh-CN.md +9 -5
  4. package/USAGE.md +187 -50
  5. package/dist/approval-card.d.ts +3 -1
  6. package/dist/approval-card.js +2 -3
  7. package/dist/audit.d.ts +7 -10
  8. package/dist/audit.js +4 -1
  9. package/dist/auth-bridge.d.ts +4 -0
  10. package/dist/auth-bridge.js +10 -11
  11. package/dist/bench/s1/live-deps.js +2 -0
  12. package/dist/boot/deployment-mods.d.ts +31 -0
  13. package/dist/boot/deployment-mods.js +26 -0
  14. package/dist/boot/execution-env.d.ts +1 -1
  15. package/dist/boot/execution-env.js +19 -6
  16. package/dist/boot/memory-boundary.d.ts +2 -2
  17. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  18. package/dist/boot/read-deny-anchor-audit.js +36 -0
  19. package/dist/boot/resolve-spec.d.ts +1 -5
  20. package/dist/boot/resolve-spec.js +56 -61
  21. package/dist/boot/runner-deps.d.ts +19 -8
  22. package/dist/boot/runner-deps.js +14 -2
  23. package/dist/boot/session-faces.d.ts +1 -7
  24. package/dist/boot/stage-02-web-admin-pull.js +9 -1
  25. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  26. package/dist/boot/stage-03-store-backend.js +2 -2
  27. package/dist/boot/stage-04-stores.d.ts +1 -1
  28. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  29. package/dist/boot/stage-05-execution-env.js +7 -10
  30. package/dist/boot/stage-06-runners.d.ts +2 -2
  31. package/dist/boot/stage-06-runners.js +5 -4
  32. package/dist/boot/stage-07-capability-layer.d.ts +2 -2
  33. package/dist/boot/stage-07-capability-layer.js +3 -3
  34. package/dist/boot/stage-08-reapers.d.ts +2 -2
  35. package/dist/boot/stage-09-leader.d.ts +3 -9
  36. package/dist/boot/stage-10-http-server.d.ts +3 -9
  37. package/dist/boot/stage-10-http-server.js +12 -10
  38. package/dist/boot/stores.d.ts +1 -1
  39. package/dist/boot/stores.js +5 -6
  40. package/dist/boot/web-admin.d.ts +0 -3
  41. package/dist/boot/web-admin.js +35 -35
  42. package/dist/brain.js +17 -22
  43. package/dist/budget.js +5 -4
  44. package/dist/caller-text-echo.d.ts +20 -0
  45. package/dist/caller-text-echo.js +24 -0
  46. package/dist/capabilities/brand-identity.d.ts +26 -0
  47. package/dist/capabilities/brand-identity.js +27 -0
  48. package/dist/capabilities/builtin-tools.js +2 -1
  49. package/dist/capabilities/center-plugins.js +1 -1
  50. package/dist/capabilities/center-prompts.d.ts +32 -59
  51. package/dist/capabilities/center-prompts.js +18 -32
  52. package/dist/capabilities/composite-members.d.ts +1 -0
  53. package/dist/capabilities/composite-members.js +1 -0
  54. package/dist/capabilities/engine-capabilities.js +2 -1
  55. package/dist/capabilities/hands-lane.d.ts +9 -6
  56. package/dist/capabilities/hands-lane.js +3 -3
  57. package/dist/capabilities/host-tool-names.d.ts +17 -0
  58. package/dist/capabilities/host-tool-names.js +15 -0
  59. package/dist/capabilities/mcp-probe.d.ts +14 -1
  60. package/dist/capabilities/mcp-probe.js +22 -133
  61. package/dist/capabilities/repo-tools.js +4 -3
  62. package/dist/capabilities/scenarios.d.ts +42 -47
  63. package/dist/capabilities/scenarios.js +19 -35
  64. package/dist/capabilities/select-environment-tool.js +2 -1
  65. package/dist/capabilities/send-user-file-tool.d.ts +0 -1
  66. package/dist/capabilities/send-user-file-tool.js +2 -2
  67. package/dist/capabilities/skills.d.ts +19 -4
  68. package/dist/capabilities/skills.js +30 -10
  69. package/dist/capabilities/tool-defer.js +2 -1
  70. package/dist/config-catalog.js +22 -11
  71. package/dist/config-invariants.js +2 -2
  72. package/dist/config-lkg.d.ts +9 -9
  73. package/dist/config-lkg.js +20 -4
  74. package/dist/config-provider.d.ts +3 -1
  75. package/dist/config-provider.js +14 -6
  76. package/dist/config-types.d.ts +13 -9
  77. package/dist/config.d.ts +2 -2
  78. package/dist/config.js +34 -20
  79. package/dist/device-ws-protocol.d.ts +1 -1
  80. package/dist/device-ws-protocol.js +1 -0
  81. package/dist/elicitation.d.ts +7 -0
  82. package/dist/elicitation.js +20 -5
  83. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  84. package/dist/env-name-allowlist-knobs.js +5 -4
  85. package/dist/exec-bounds.d.ts +15 -3
  86. package/dist/exec-bounds.js +4 -0
  87. package/dist/execution-lane-caps.d.ts +9 -2
  88. package/dist/execution-lane-caps.js +11 -0
  89. package/dist/hooks/classic-stations.d.ts +86 -0
  90. package/dist/hooks/classic-stations.js +27 -0
  91. package/dist/hooks/hook-llm.d.ts +8 -5
  92. package/dist/hooks/hook-llm.js +80 -37
  93. package/dist/hooks/hook-runner.d.ts +273 -75
  94. package/dist/hooks/hook-runner.js +293 -317
  95. package/dist/http/admission.d.ts +8 -2
  96. package/dist/http/admission.js +103 -59
  97. package/dist/http/approval-sweeps.js +59 -18
  98. package/dist/http/checkpoint-ctx.d.ts +36 -18
  99. package/dist/http/checkpoint-ctx.js +14 -10
  100. package/dist/http/claimed-run.d.ts +13 -0
  101. package/dist/http/claimed-run.js +15 -0
  102. package/dist/http/device-placement.js +5 -10
  103. package/dist/http/dispatch.d.ts +0 -4
  104. package/dist/http/dispatch.js +6 -10
  105. package/dist/http/durable-run-leg.d.ts +2 -1
  106. package/dist/http/durable-run-leg.js +5 -3
  107. package/dist/http/health-self-report.d.ts +7 -5
  108. package/dist/http/health-self-report.js +4 -18
  109. package/dist/http/principal-gate.d.ts +23 -6
  110. package/dist/http/principal-gate.js +24 -5
  111. package/dist/http/refusal-body.d.ts +21 -0
  112. package/dist/http/refusal-body.js +4 -0
  113. package/dist/http/request-guards.d.ts +27 -0
  114. package/dist/http/request-guards.js +24 -3
  115. package/dist/http/resume-legs.d.ts +25 -0
  116. package/dist/http/resume-legs.js +48 -26
  117. package/dist/http/routes/approvals-assistant.js +1 -1
  118. package/dist/http/routes/capabilities.js +7 -17
  119. package/dist/http/routes/diagnostics.js +1 -1
  120. package/dist/http/routes/rules.js +2 -2
  121. package/dist/http/routes/runs.js +13 -17
  122. package/dist/http/routes/session-sync.js +4 -2
  123. package/dist/http/routes/sessions.js +23 -11
  124. package/dist/http/routes/shared-memory.js +2 -13
  125. package/dist/http/routes/side-query.js +2 -3
  126. package/dist/http/routes/tasks.js +34 -19
  127. package/dist/http/scenario-read-face.d.ts +14 -0
  128. package/dist/http/scenario-read-face.js +5 -0
  129. package/dist/http/send.d.ts +13 -1
  130. package/dist/http/send.js +12 -1
  131. package/dist/http/server.d.ts +37 -23
  132. package/dist/http/server.js +30 -4
  133. package/dist/http/stored-body-replay.d.ts +10 -3
  134. package/dist/http/stored-body-replay.js +27 -6
  135. package/dist/http/submit-ctx.d.ts +25 -0
  136. package/dist/http/submit-ctx.js +39 -0
  137. package/dist/http/wire-types.d.ts +18 -5
  138. package/dist/index.d.ts +3 -2
  139. package/dist/index.js +3 -2
  140. package/dist/lane-home-probe.d.ts +43 -0
  141. package/dist/lane-home-probe.js +35 -0
  142. package/dist/limit-refusal.d.ts +47 -0
  143. package/dist/limit-refusal.js +12 -0
  144. package/dist/main.js +1 -1
  145. package/dist/mcp-assertion-header.d.ts +3 -11
  146. package/dist/mcp-assertion-header.js +3 -9
  147. package/dist/mcp-tool-faces.d.ts +5 -0
  148. package/dist/mcp-tool-faces.js +5 -1
  149. package/dist/memory-scope.d.ts +1 -1
  150. package/dist/model-entry-refusal.d.ts +1 -7
  151. package/dist/model-provider.d.ts +39 -12
  152. package/dist/model-provider.js +19 -2
  153. package/dist/model-route-endpoint.d.ts +52 -12
  154. package/dist/model-route-endpoint.js +48 -26
  155. package/dist/model-route-gap.d.ts +5 -4
  156. package/dist/model-route-gap.js +1 -1
  157. package/dist/model-select.d.ts +58 -0
  158. package/dist/model-select.js +31 -0
  159. package/dist/namespaced-rule-name.d.ts +7 -4
  160. package/dist/namespaced-rule-name.js +4 -3
  161. package/dist/observability/cost-taxonomy.d.ts +7 -7
  162. package/dist/observability/fail-open.d.ts +53 -12
  163. package/dist/observability/fail-open.js +47 -7
  164. package/dist/observability/metrics.js +3 -1
  165. package/dist/observability/prompt-manifest.d.ts +47 -82
  166. package/dist/observability/prompt-manifest.js +119 -17
  167. package/dist/observability/run-terminal.js +14 -6
  168. package/dist/observability/secret-env-scrub.d.ts +13 -1
  169. package/dist/observability/secret-env-scrub.js +13 -1
  170. package/dist/observability/tool-trace.d.ts +16 -21
  171. package/dist/observability/tool-trace.js +51 -42
  172. package/dist/per-task-image.d.ts +7 -1
  173. package/dist/per-task-image.js +3 -3
  174. package/dist/plain-string-record.d.ts +25 -0
  175. package/dist/plain-string-record.js +32 -0
  176. package/dist/plugins/blob-backend.d.ts +68 -13
  177. package/dist/plugins/blob-backend.js +53 -10
  178. package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
  179. package/dist/plugins/checkpoint-store-sql.js +39 -26
  180. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  181. package/dist/plugins/k8s-bg-scripts.js +22 -0
  182. package/dist/plugins/local-checkpoint-store.js +5 -2
  183. package/dist/plugins/local-session-store.js +3 -3
  184. package/dist/plugins/one-time-migrations.d.ts +147 -91
  185. package/dist/plugins/one-time-migrations.js +372 -208
  186. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  187. package/dist/plugins/permission-rule-store-sql.js +4 -18
  188. package/dist/plugins/pg-session-storage.d.ts +4 -5
  189. package/dist/plugins/pg-session-storage.js +7 -17
  190. package/dist/plugins/remote-env-adb.d.ts +6 -0
  191. package/dist/plugins/remote-env-adb.js +9 -4
  192. package/dist/plugins/remote-env-device.js +3 -0
  193. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  194. package/dist/plugins/remote-env-e2b.js +21 -9
  195. package/dist/plugins/remote-env-host.d.ts +9 -9
  196. package/dist/plugins/remote-env-host.js +10 -32
  197. package/dist/plugins/remote-env-k8s.d.ts +95 -26
  198. package/dist/plugins/remote-env-k8s.js +265 -90
  199. package/dist/plugins/remote-env-local-docker.d.ts +20 -1
  200. package/dist/plugins/remote-env-local-docker.js +77 -9
  201. package/dist/plugins/remote-env-ssh.d.ts +15 -0
  202. package/dist/plugins/remote-env-ssh.js +109 -3
  203. package/dist/plugins/remote-shell.d.ts +0 -1
  204. package/dist/plugins/remote-shell.js +1 -2
  205. package/dist/plugins/retention-store-sql.js +6 -2
  206. package/dist/plugins/schema-consistency.d.ts +54 -0
  207. package/dist/plugins/schema-consistency.js +52 -0
  208. package/dist/plugins/session-epoch-anchor.d.ts +13 -0
  209. package/dist/plugins/session-epoch-anchor.js +23 -0
  210. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  211. package/dist/plugins/shared-memory-store-sql.js +5 -16
  212. package/dist/plugins/sql-driver.d.ts +141 -0
  213. package/dist/plugins/sql-driver.js +115 -1
  214. package/dist/plugins/sql-errors.d.ts +41 -0
  215. package/dist/plugins/sql-errors.js +36 -0
  216. package/dist/plugins/store-backend.js +2 -1
  217. package/dist/plugins/tidb-session-storage.d.ts +7 -11
  218. package/dist/plugins/tidb-session-storage.js +6 -16
  219. package/dist/plugins/tidb-session-store.js +1 -1
  220. package/dist/prompts-domain-validate.d.ts +17 -61
  221. package/dist/prompts-domain-validate.js +9 -119
  222. package/dist/question.d.ts +18 -9
  223. package/dist/question.js +21 -13
  224. package/dist/request-key-closure.d.ts +4 -4
  225. package/dist/request-key-closure.js +18 -13
  226. package/dist/rule-entry-list.d.ts +134 -24
  227. package/dist/rule-entry-list.js +162 -30
  228. package/dist/run-local.d.ts +3 -3
  229. package/dist/run-local.js +26 -25
  230. package/dist/runs.d.ts +48 -2
  231. package/dist/runs.js +61 -2
  232. package/dist/server-secret-env.d.ts +44 -18
  233. package/dist/server-secret-env.js +49 -8
  234. package/dist/session-incarnation.d.ts +15 -5
  235. package/dist/session-incarnation.js +16 -9
  236. package/dist/session-titler.js +12 -2
  237. package/dist/spec-fields.d.ts +34 -18
  238. package/dist/spec-fields.js +46 -53
  239. package/dist/sql-engine-posture.d.ts +29 -4
  240. package/dist/sql-engine-posture.js +36 -1
  241. package/dist/stored-request-hooks-m20.d.ts +23 -0
  242. package/dist/stored-request-hooks-m20.js +124 -0
  243. package/dist/stored-request-tool-axes-m19.d.ts +20 -5
  244. package/dist/stored-request-tool-axes-m19.js +30 -2
  245. package/dist/task-a2a.d.ts +9 -1
  246. package/dist/task-a2a.js +73 -47
  247. package/dist/task-mcp.d.ts +54 -102
  248. package/dist/task-mcp.js +194 -92
  249. package/dist/task-settings.d.ts +47 -25
  250. package/dist/task-settings.js +46 -61
  251. package/dist/text-bidi.d.ts +3 -46
  252. package/dist/text-bidi.js +3 -3
  253. package/dist/tool-approval.d.ts +1 -1
  254. package/dist/trace/core-keyset-guard.d.ts +24 -4
  255. package/dist/trace/engine-notice-wire.d.ts +11 -3
  256. package/dist/trace/engine-notice-wire.js +8 -1
  257. package/dist/trace/ledger-events.d.ts +3 -1
  258. package/dist/trace/ledger-sink.d.ts +29 -0
  259. package/dist/trace/ledger-sink.js +15 -1
  260. package/dist/trace/model-segment.js +1 -1
  261. package/dist/trace/project.d.ts +25 -6
  262. package/dist/trace/project.js +68 -22
  263. package/dist/trace/wire-projection-faces.d.ts +80 -2
  264. package/dist/trace/wire-projection-faces.js +23 -1
  265. package/dist/user-authored-text.d.ts +7 -0
  266. package/dist/user-authored-text.js +3 -0
  267. package/dist/web-admin-client/apply-effective.js +10 -5
  268. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  269. package/dist/web-admin-client/candidate-gate.js +3 -3
  270. package/dist/web-admin-client/http-client.js +0 -6
  271. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  272. package/dist/web-admin-client/read-warnings.js +10 -0
  273. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  274. package/dist/web-admin-client/skills-mcp.js +9 -6
  275. package/dist/web-admin-client/types.d.ts +3 -4
  276. package/dist/workspace-cache.d.ts +35 -0
  277. package/dist/workspace-cache.js +36 -0
  278. package/dist/workspace-space.d.ts +3 -2
  279. package/dist/workspace-space.js +2 -0
  280. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
  281. package/package.json +4 -4
  282. package/scripts/drain-check-local.mjs +37 -3
  283. package/scripts/drain-check.mysql.sql +30 -2
  284. package/scripts/drain-check.pg.sql +29 -2
  285. package/dist/capabilities/memory-notice.d.ts +0 -138
  286. package/dist/capabilities/memory-notice.js +0 -64
  287. package/dist/capabilities/prompt.d.ts +0 -17
  288. package/dist/capabilities/prompt.js +0 -6
  289. package/dist/mcp-content-origin.d.ts +0 -34
  290. package/dist/mcp-content-origin.js +0 -10
  291. package/dist/tool-axis-words.d.ts +0 -27
  292. package/dist/tool-axis-words.js +0 -9
  293. package/dist/wire-name-bound.d.ts +0 -14
  294. package/dist/wire-name-bound.js +0 -13
@@ -157,7 +157,148 @@ export interface SqlEngineFacts {
157
157
  readonly isolation: string;
158
158
  /** TiDB's session `tidb_txn_mode`; `null` = this engine has no such indicator (NOT "optimistic"). */
159
159
  readonly txnMode: "pessimistic" | null;
160
+ /**
161
+ * The largest single row this engine stores, in bytes — the **one** source of that number in this repo. Every
162
+ * row this service writes from a submitted request (the user message in `session_event`, the resume inputs in
163
+ * `checkpoint_ctx`) has to fit it, so the submit admission reads it (`http/request-guards.ts`
164
+ * `refuseSubmitBody`) instead of carrying a copy of the number.
165
+ * · **TiDB** — the effective `txn-entry-size-limit`, read at initialization: the session variable
166
+ * `@@tidb_txn_entry_size_limit` when it is non-zero (TiDB 7.6+; 0 there means "use the config item"), else
167
+ * `performance.txn-entry-size-limit` from `SHOW CONFIG` (the smallest value across TiDB instances: a request
168
+ * may land on any of them). Neither readable (the config read needs the `CONFIG` privilege; older TiDB has no
169
+ * session variable) ⇒ TiDB's own default {@link TIDB_DEFAULT_TXN_ENTRY_SIZE_LIMIT}, recorded through
170
+ * `recordFailOpen("server.sql.tidb-row-cap-unread")` (a per-hit count and one warn line naming both reads).
171
+ * · **`null`** — this engine has **no single-row wall** below the request cap: PostgreSQL (`jsonb` holds 255 MiB)
172
+ * and InnoDB (it has no row-size wall of this kind; its wall is the statement-level one, a different fact:
173
+ * {@link maxPacketBytes}). `null` is the ONLY spelling of "no wall" — an unreadable value is never folded into it.
174
+ * Only two writes translate a row-size refusal into the coded
175
+ * `StoreRowTooLargeError` (`sql-errors.ts`): the checkpoint store's `putCtx` (resume inputs) and `put` (the INSERT of
176
+ * a pending row). Every other write — the checkpoint store's updates (`resolve` / `reopen` / …), the session log —
177
+ * keeps its existing driver error.
178
+ */
179
+ readonly maxRowBytes: number | null;
180
+ /**
181
+ * The largest single statement the server accepts, in bytes — the session's `@@max_allowed_packet`. A second wall,
182
+ * so a second fact (one fact per wall; `null` always and only means "this engine has no such wall").
183
+ * · **MySQL protocol — InnoDB and TiDB alike**: read at connection initialization, in the same place and with the
184
+ * same unreadable disposition as {@link maxRowBytes} on TiDB: the variable unknown / the query failing / a value that
185
+ * is not a positive integer ⇒ the documented default of that server family
186
+ * ({@link PACKET_CAP_UNREAD_LANDING_BYTES}), recorded through `recordFailOpen("server.sql.packet-cap-unread")` (a per-hit
187
+ * count and one warn line naming the reason). Never `null`: on these engines the wall exists, only its size may be unread.
188
+ * · **`null`** — PostgreSQL: the extended protocol has no statement-size wall.
189
+ * Its readers: the submit admission (`http/request-guards.ts` `refuseSubmitBody`) and the SQL snapshot-blob default cap
190
+ * (`plugins/blob-backend.ts` `sqlBlobDefaultCapBytes`) — both through {@link packetWallStatementBytes}, and both read the walls (this one and
191
+ * {@link maxRowBytes}) through the one judge {@link sqlWallsOf}, which also says what holds when the facts themselves are absent.
192
+ */
193
+ readonly maxPacketBytes: number | null;
194
+ /**
195
+ * Where each wall's number came from — the **one** record of "read, or fell back to a documented default", parallel to {@link maxRowBytes} and
196
+ * {@link maxPacketBytes} (see {@link SqlWallSource}). `"reported"` = the database answered at connection initialization (or, for a `null` wall,
197
+ * the engine has no such wall by construction); `"assumed"` = the reading failed and the documented default of the server family stands in for the
198
+ * number (the same two reads that go through `recordFailOpen`). The submit admission and the SQL snapshot-blob cap report it as `wallSource`, so a
199
+ * refusal never presents a default as the database's own limit.
200
+ */
201
+ readonly wallSources: SqlWallSources;
202
+ }
203
+ /** TiDB's documented default for `performance.txn-entry-size-limit` (6 MiB): the cap a single row (key + value)
204
+ * may reach. Used only when the engine's own reading cannot be had ({@link SqlEngineFacts.maxRowBytes}). */
205
+ export declare const TIDB_DEFAULT_TXN_ENTRY_SIZE_LIMIT = 6291456;
206
+ /**
207
+ * 一份载荷落进库行时,行里**载荷之外**的那部分的余量(字节)—— **按行的形各一只**:行墙只有一堵(`SqlEngineFacts.maxRowBytes`),余量是落进它的那种行的物理,
208
+ * 两种行的余量差两个数量级,借用就会让一个读者的门限偏离它自己那种行能装下的量。
209
+ * · `json` —— 提交体落进的两种行:会话日志里的用户消息(`session_event.payload`:条目把内联图 / 附件 / objective 包上 id 与时间戳)与 durable 审批部署上的续跑输入
210
+ * (`checkpoint_ctx.ctx`:体 + 写者版本 / memoryScope / 两枚读法记号)。余量盖住其余全部:
211
+ * - 包装键与行上的其余列:几百字节(TiDB 7.5.1 实测:体里带一只 6 815 544 字符串的续跑输入行,库报 6 815 845 字节 —— 比那只串多 301 字节,其中约 105 字节是串外面的 JSON 文本);
212
+ * - TiDB 以二进制形存 JSON,每个数组元素 / 对象成员比文本多约 5–11 字节;一份提交体的结构(三只规则桶各 ≤ 1000 条、agents、skills…)在几千个元素以内 ⇒ 几十 KiB。
213
+ * 64 KiB 盖住这两件还有富余;结构超出它的体照样会被库在写的那一刻拒 —— 提交时写续跑输入的那一笔(durable 审批部署)答同一个 413(店的带码拒,`submit-ctx.ts`),
214
+ * 开跑后的写(会话日志的用户消息)按各自的既有失败面。
215
+ * · `blob` —— SQL 快照 blob 的行(`snapshot_blob` / `file_history_blob`:`blob_hash CHAR(64)` 主键 + `byte_len BIGINT` + `created_at DATETIME(3)` + `bytes LONGBLOB`)。
216
+ * TiDB 的单行上限量的是这一行的键值对。键 ≈ 表前缀 `t` + 8 字节表 id + `_r` + 行句柄(非聚簇 8 字节;聚簇时是 CHAR(64) 主键的可比较编码,每 8 字节加 1 字节组标 = 72 字节)
217
+ * ⇒ 至多约 83 字节;值 = 行格式 v2:头 6 字节 + 4 列的列 id 与偏移(大行形各 4 字节 = 32 字节)+ 三列定长数据(哈希 64 + 长度 8 + 时间 8 = 80 字节)+ 可选行校验 5 字节
218
+ * ⇒ 约 123 字节。合计约 206 字节;字节列按原字节存,没有 JSON 的逐元素开销。取 1 KiB = 实需的近 5 倍(与包墙那只语句余量 {@link STATEMENT_ALLOWANCE_BYTES} 同一量级),
219
+ * 真库读数由 db 组 `sql-txn-semantics-integration` ①c 钉(恰在帽上的 blob 真写得进)。
220
+ *
221
+ * 余量是**墙的物理**:每堵墙、每种行一只余量,各只在这里定义一次;两个读者(提交体受理口读 `json`、blob 缺省帽读 `blob`)经 {@link rowWallPayloadBytes} 按行的形取。
222
+ */
223
+ export declare const ROW_WALL_ALLOWANCE_BYTES: {
224
+ readonly json: number;
225
+ readonly blob: 1024;
226
+ };
227
+ /** 落进行墙的两种行(见 {@link ROW_WALL_ALLOWANCE_BYTES})。 */
228
+ export type RowWallRowShape = keyof typeof ROW_WALL_ALLOWANCE_BYTES;
229
+ /**
230
+ * 一条语句里**载荷之外**的字节余量 —— 固定语句文本 + 表名 + 64 字符哈希 + 长度 + 时间戳 + 行包装(mysql2 真格式化量过:最长表名
231
+ * `file_history_blob` 的 blob INSERT 在 blob 之外 240 字节)。1 KiB 盖住它的 4 倍还多,又不松到压低可用量(16 MiB 包墙下少 1 KiB)。
232
+ * 不扣的话恰在包墙上的载荷整条语句会超出包墙几百字节:放行、库在写那一刻拒。经 {@link packetWallStatementBytes} 被两个读者共读。
233
+ */
234
+ export declare const STATEMENT_ALLOWANCE_BYTES = 1024;
235
+ /** 行墙下一份载荷最多多少字节:`max(0, 行墙 − 这种行的余量)`({@link ROW_WALL_ALLOWANCE_BYTES})。受理口的提交体门限读 `json`、blob 缺省帽的行墙腿读 `blob`。 */
236
+ export declare function rowWallPayloadBytes(maxRowBytes: number, row: RowWallRowShape): number;
237
+ /** 包墙下一条语句里的载荷最多多少字节:`max(0, 包墙 − {@link STATEMENT_ALLOWANCE_BYTES})`。受理口的提交体门限(量的是转义后的语句字节,{@link escapedStatementBytes})与 blob 缺省帽的包墙腿(再按膨胀律折半)读它。 */
238
+ export declare function packetWallStatementBytes(maxPacketBytes: number): number;
239
+ /**
240
+ * 一段文本作为**一只字符串字面**进 MySQL 协议语句时占的字节数(不含外层两只引号)—— 店真正发出去的字节,不是文本自己的字节数。
241
+ * 店用 `pool.query(sql, [text])`,mysql2 在客户端把参数格式化进语句(`sql-escaper` 的 `escapeString`):`\0 \b \t \n \r \x1a " ' \` 九个字符各变成两个字符
242
+ * (多一个反斜杠字节),其余字符原样 —— 多字节 UTF-8 不受影响。所以一份 JSON 里引号 / 反斜杠越多,语句越比文本长(上界 2×:每个字符至多多一字节)。
243
+ *
244
+ * 受理口的包墙腿量它(`http/request-guards.ts` `refuseSubmitBody`):门承诺「写不进去的体在任何副作用之前 413」,量的就必须是语句字节。
245
+ * 这是 mysql2 转义表的**镜像**,不是第二份读法:`test/store-row-cap.test.ts` 把它逐字钉在 mysql2 自己的 `escape` 上(`escapedStatementBytes(s) ===
246
+ * Buffer.byteLength(mysql.escape(s)) − 2`,含全码点表扫),mysql2 升级改转义表时那格先红。不直接调 `escape` 取长度,是因为那会为一份 8 MiB 的体再造一份 8–16 MiB 的字符串。
247
+ */
248
+ export declare function escapedStatementBytes(text: string): number;
249
+ /**
250
+ * The documented default `max_allowed_packet`, per MySQL-protocol engine family — the landing when the live reading cannot be
251
+ * had ({@link SqlEngineFacts.maxPacketBytes}). Closed over the MySQL-protocol engines (an engine added to {@link SqlEngine} is a
252
+ * compile error here until its row is written).
253
+ * · **tidb** — 64 MiB (67 108 864): TiDB's system-variable reference, `max_allowed_packet`, default `67108864`.
254
+ * · **innodb** — 16 MiB (16 777 216): the smaller of the two documented defaults among the servers behind this word — MariaDB
255
+ * 16 MiB (its server-system-variables reference, `max_allowed_packet`, default `16777216`) and MySQL 8.0 64 MiB (its
256
+ * server-system-variables reference, default `67108864`; 5.7 and before shipped 4 MiB, outside the supported set). A landing above
257
+ * the true wall lets through a write the server refuses; below it only refuses early, so the smaller one is the landing.
258
+ */
259
+ export declare const PACKET_CAP_UNREAD_LANDING_BYTES: {
260
+ readonly tidb: 67108864;
261
+ readonly innodb: 16777216;
262
+ };
263
+ /** 一份写入要装进的两堵墙 —— {@link SqlEngineFacts} 里的那两位(`null` 恒且只表示「这堵墙不存在」)。 */
264
+ export type SqlWalls = Pick<SqlEngineFacts, "maxRowBytes" | "maxPacketBytes">;
265
+ /**
266
+ * 一个墙数字从哪来(上 wire:413 拒体的 `wallSource`)—— **只看「这个数是不是库自己报的」**:
267
+ * · `reported` = 库报的:引擎在连接初始化时读到的(或按构造没有这堵墙:没有 SQL 后端、PostgreSQL、InnoDB 的行墙);
268
+ * · `assumed` = 不是库报的、是受支持引擎里最保守的缺省顶上的:引擎事实还没建立(连是哪个引擎都不知道,{@link SQL_FACTS_ABSENT_WALLS}),
269
+ * 或事实在场但这一堵墙读失败、退回了文档缺省({@link SqlEngineFacts.wallSources} 逐墙记着,登记过的 fail-open)。
270
+ */
271
+ export type SqlWallSource = "reported" | "assumed";
272
+ /** 两堵墙各自的出处(与 {@link SqlWalls} 同键):事实里一处记录,判官 {@link sqlWallsOf} 原样带出。 */
273
+ export type SqlWallSources = {
274
+ readonly [K in keyof SqlWalls]: SqlWallSource;
275
+ };
276
+ /** {@link sqlWallsOf} 的读数:两堵墙 + 它们各自从哪来(逐墙:一堵读到、另一堵退缺省时,拒哪一堵就报哪一堵的出处)。 */
277
+ export interface SqlWallsReading extends SqlWalls {
278
+ readonly sources: SqlWallSources;
160
279
  }
280
+ /**
281
+ * 一只 SQL 池的**引擎读口**:方言(SQL 文本轴,装配事实,恒在)+ 此刻的引擎事实(`null` = 没建立)。墙的方言只从这里来 —— 与事实同出一只驱动
282
+ * ({@link SqlDriver} 结构上就是它;`facts` 是活的,池重连后随之换)。HTTP 面的读口(`ServiceDeps.sqlEngine`)与店的写前帽(`SqlBlobBackend`)各读本池的这一只。
283
+ */
284
+ export type SqlEngineReading = Pick<SqlDriver, "dialect" | "facts">;
285
+ /**
286
+ * MySQL 协议方言上**事实缺席**时的墙(池上还没建立过,或最近一次连接初始化被拒:`SqlDriver.facts === null`)。这时连引擎是哪一个都不知道,
287
+ * 每堵墙各取受支持引擎里最矮的一格:行墙 = TiDB 自己的缺省(InnoDB 没有行墙,保守取有墙的那一个),包墙 = 各引擎族文档缺省里最小的那个
288
+ * ({@link PACKET_CAP_UNREAD_LANDING_BYTES})—— 没读到事实时的墙不会比读到任何一个缺省事实时的墙更松。它们是**假定**的墙(两堵的出处都是 `assumed`),
289
+ * 拒句不能把它们说成库报的墙。
290
+ */
291
+ export declare const SQL_FACTS_ABSENT_WALLS: SqlWallsReading;
292
+ /**
293
+ * 🔴 **此刻一份写入要装进的墙 —— 唯一判官**。两个读者同调它、不各读 `facts`:提交受理口(`http/request-guards.ts` `refuseSubmitBody`)与 SQL 快照 blob 的缺省帽
294
+ * (`plugins/blob-backend.ts` `sqlBlobCapBytes`,店的写前帽与 `PUT /sync/blobs` 的前置 413 都经它)。输入是一只引擎读口({@link SqlEngineReading}:方言与事实同出一只驱动),一种读法:
295
+ * · 读口缺席 = 没有 SQL 后端 ⇒ 没有墙(「忘了给」读不成这一臂:后端交出了 SQL 池而没给读口 ⇒ `createHttpServer` 起服拒,见 `ServiceDeps.sqlEngine`);
296
+ * · 事实在场 ⇒ 事实里的两位,出处逐墙取事实里记的那一份({@link SqlEngineFacts.wallSources}:读到的 `reported`,读失败退文档缺省的 `assumed`);
297
+ * · 事实缺席(`null`)⇒ 按读口的方言:PG 没有墙(扩展协议没有这两堵墙 —— 方言事实,不随引擎事实缺席);MySQL 协议 ⇒ {@link SQL_FACTS_ABSENT_WALLS}(两堵都 `assumed`;
298
+ * fail-closed:按最保守的墙,不是「没有墙」)。池拒绝初始化本身已在 {@link establishSession} 里响亮报过。
299
+ * 「没有 SQL 后端」与「事实还没建立」是两个可区分的值:前者是读口缺席,后者是 `facts === null`。
300
+ */
301
+ export declare function sqlWallsOf(engine: SqlEngineReading | undefined): SqlWallsReading;
161
302
  /**
162
303
  * **数据库自己的钟**(毫秒),两方言各一句 —— 本仓这口钟在 SQL 面的**唯一**铸点([ref] A7 / [ref] /
163
304
  * 合并复审 R2 把 S-280 的 nonce 店并进来)。
@@ -1,4 +1,59 @@
1
1
  import { pgSafeJsonStringify, pgProtocolJsonStringify } from "./pg-safe-json.js";
2
+ import { recordFailOpen } from "../observability/fail-open.js";
3
+ export const TIDB_DEFAULT_TXN_ENTRY_SIZE_LIMIT = 6_291_456;
4
+ export const ROW_WALL_ALLOWANCE_BYTES = { json: 64 * 1024, blob: 1024 };
5
+ export const STATEMENT_ALLOWANCE_BYTES = 1024;
6
+ export function rowWallPayloadBytes(maxRowBytes, row) {
7
+ return Math.max(0, maxRowBytes - ROW_WALL_ALLOWANCE_BYTES[row]);
8
+ }
9
+ export function packetWallStatementBytes(maxPacketBytes) {
10
+ return Math.max(0, maxPacketBytes - STATEMENT_ALLOWANCE_BYTES);
11
+ }
12
+ export function escapedStatementBytes(text) {
13
+ let escaped = 0;
14
+ for (let i = 0; i < text.length; i++) {
15
+ switch (text.charCodeAt(i)) {
16
+ case 0:
17
+ case 8:
18
+ case 9:
19
+ case 10:
20
+ case 13:
21
+ case 26:
22
+ case 34:
23
+ case 39:
24
+ case 92:
25
+ escaped++;
26
+ }
27
+ }
28
+ return Buffer.byteLength(text, "utf8") + escaped;
29
+ }
30
+ export const PACKET_CAP_UNREAD_LANDING_BYTES = {
31
+ tidb: 67_108_864,
32
+ innodb: 16_777_216,
33
+ };
34
+ const NO_WALLS = Object.freeze({ maxRowBytes: null, maxPacketBytes: null, sources: Object.freeze({ maxRowBytes: "reported", maxPacketBytes: "reported" }) });
35
+ export const SQL_FACTS_ABSENT_WALLS = Object.freeze({
36
+ maxRowBytes: TIDB_DEFAULT_TXN_ENTRY_SIZE_LIMIT,
37
+ maxPacketBytes: Math.min(...Object.values(PACKET_CAP_UNREAD_LANDING_BYTES)),
38
+ sources: Object.freeze({ maxRowBytes: "assumed", maxPacketBytes: "assumed" }),
39
+ });
40
+ export function sqlWallsOf(engine) {
41
+ if (engine === undefined)
42
+ return NO_WALLS;
43
+ const facts = engine.facts;
44
+ if (facts !== null)
45
+ return { maxRowBytes: facts.maxRowBytes, maxPacketBytes: facts.maxPacketBytes, sources: facts.wallSources };
46
+ switch (engine.dialect) {
47
+ case "pg":
48
+ return NO_WALLS;
49
+ case "tidb":
50
+ return SQL_FACTS_ABSENT_WALLS;
51
+ default: {
52
+ const unhandled = engine.dialect;
53
+ throw new Error(`sql walls: unknown dialect ${String(unhandled)}`);
54
+ }
55
+ }
56
+ }
2
57
  export const SQL_DB_NOW_MS = {
3
58
  tidb: "CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS SIGNED)",
4
59
  pg: "(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint",
@@ -90,7 +145,63 @@ async function initMySqlSession(c) {
90
145
  if (isolation !== "REPEATABLE-READ") {
91
146
  throw refuseSession(`this session reports isolation "${isolation}" after SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ`);
92
147
  }
93
- return { engine, version: (await mysqlVar(c, "SELECT VERSION() AS v")) ?? "", isolation, txnMode: engine === "tidb" ? "pessimistic" : null };
148
+ const version = (await mysqlVar(c, "SELECT VERSION() AS v")) ?? "";
149
+ const rowWall = engine === "tidb" ? await readTidbEntrySizeLimit(c) : null;
150
+ const packetWall = await readMaxAllowedPacket(c, engine);
151
+ return {
152
+ engine,
153
+ version,
154
+ isolation,
155
+ txnMode: engine === "tidb" ? "pessimistic" : null,
156
+ maxRowBytes: rowWall === null ? null : rowWall.bytes,
157
+ maxPacketBytes: packetWall.bytes,
158
+ wallSources: { maxRowBytes: rowWall === null ? "reported" : rowWall.source, maxPacketBytes: packetWall.source },
159
+ };
160
+ }
161
+ function positiveIntOf(v) {
162
+ const n = typeof v === "number" ? v : typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v.trim()) : Number.NaN;
163
+ return Number.isSafeInteger(n) && n > 0 ? n : undefined;
164
+ }
165
+ async function readTidbEntrySizeLimit(c) {
166
+ const unread = [];
167
+ try {
168
+ const v = await mysqlVar(c, "SELECT @@tidb_txn_entry_size_limit AS v");
169
+ const n = positiveIntOf(v);
170
+ if (n !== undefined)
171
+ return { bytes: n, source: "reported" };
172
+ unread.push(v === null ? "@@tidb_txn_entry_size_limit is unknown (TiDB before 7.6)" : `@@tidb_txn_entry_size_limit=${JSON.stringify(v)} (0 = use the config item)`);
173
+ }
174
+ catch (err) {
175
+ unread.push(`@@tidb_txn_entry_size_limit: ${err.message}`);
176
+ }
177
+ try {
178
+ const r = await mysqlQuery(c, "SHOW CONFIG WHERE type = 'tidb' AND name = 'performance.txn-entry-size-limit'");
179
+ const values = r.rows.map((row) => positiveIntOf(row.Value ?? row.value)).filter((n) => n !== undefined);
180
+ if (values.length > 0)
181
+ return { bytes: Math.min(...values), source: "reported" };
182
+ unread.push(`SHOW CONFIG returned no readable performance.txn-entry-size-limit (${r.rows.length} row(s))`);
183
+ }
184
+ catch (err) {
185
+ unread.push(`SHOW CONFIG: ${err.message}`);
186
+ }
187
+ recordFailOpen("server.sql.tidb-row-cap-unread", `assumed=${TIDB_DEFAULT_TXN_ENTRY_SIZE_LIMIT} ${unread.join("; ")}`);
188
+ return { bytes: TIDB_DEFAULT_TXN_ENTRY_SIZE_LIMIT, source: "assumed" };
189
+ }
190
+ async function readMaxAllowedPacket(c, engine) {
191
+ const landing = PACKET_CAP_UNREAD_LANDING_BYTES[engine];
192
+ let unread;
193
+ try {
194
+ const v = await mysqlVar(c, "SELECT @@max_allowed_packet AS v");
195
+ const n = positiveIntOf(v);
196
+ if (n !== undefined)
197
+ return { bytes: n, source: "reported" };
198
+ unread = v === null ? "@@max_allowed_packet is unknown" : `@@max_allowed_packet=${JSON.stringify(v)} (not a positive integer)`;
199
+ }
200
+ catch (err) {
201
+ unread = `@@max_allowed_packet: ${err.message}`;
202
+ }
203
+ recordFailOpen("server.sql.packet-cap-unread", `assumed=${landing} engine=${engine} ${unread}`);
204
+ return { bytes: landing, source: "assumed" };
94
205
  }
95
206
  export function mysqlDriver(pool) {
96
207
  const session = poolSession(pool);
@@ -130,6 +241,9 @@ async function initPgSession(c) {
130
241
  version: String(ver.rows[0]?.server_version ?? ""),
131
242
  isolation,
132
243
  txnMode: null,
244
+ maxRowBytes: null,
245
+ maxPacketBytes: null,
246
+ wallSources: { maxRowBytes: "reported", maxPacketBytes: "reported" },
133
247
  };
134
248
  }
135
249
  export function pgDriver(pool) {
@@ -20,6 +20,9 @@
20
20
  * 于是一次 dup 变成一个裸驱动对象漏给调用方。`Reflect.get` 与属性访问一样走原型链,写成 `Reflect.get`
21
21
  * 是把"必须走原型链"这件事写在码面上(收编前唯一的硬化形 `shared-memory-store-sql.ts` 即此形)。
22
22
  *
23
+ * 例外只有一只**类**:{@link StoreRowTooLargeError} —— 「一行装不下」这条判据的带码产物,与它的谓词住在一起
24
+ * (判据与产物分家 = 两处各写一份「哪些驱动错算这一类」)。
25
+ *
23
26
  * ## 本模块**不**管的事(与 `sql-driver.ts` 头注的分工一致)
24
27
  * 「撞的是**哪一个** UNIQUE 键」留在店内:mysql2 把键名写进 `sqlMessage` 文本、node-pg 写进
25
28
  * `err.constraint`,而怎么按键名分诊(良性重发 vs 必须重试的竞态)是 store-specific 的判别规则,
@@ -93,4 +96,42 @@ export declare const PG_DEADLOCK_DETECTED_SQLSTATE = "40P01";
93
96
  * **MySQL 协议腿**,两方言都认只是为了让店不必自己判引擎。
94
97
  */
95
98
  export declare function isDeadlockError(dialect: SqlDialect, err: unknown): boolean;
99
+ /** TiDB `ErrEntryTooLarge` (one row's key + value over `txn-entry-size-limit`, 6 MiB by default) — TiDB's own
100
+ * errno, which mysql2 carries without a symbolic name. */
101
+ export declare const TIDB_ER_ENTRY_TOO_LARGE_ERRNO = 8025;
102
+ /** MySQL/MariaDB `ER_NET_PACKET_TOO_LARGE` (one statement over `max_allowed_packet`). */
103
+ export declare const MYSQL_ER_NET_PACKET_TOO_LARGE_ERRNO = 1153;
104
+ /** MySQL/MariaDB `ER_NET_PACKET_TOO_LARGE`'s symbolic code (mysql2 `code`). */
105
+ export declare const MYSQL_ER_NET_PACKET_TOO_LARGE_CODE = "ER_NET_PACKET_TOO_LARGE";
106
+ /** PostgreSQL `program_limit_exceeded` — a value or row over one of the engine's hard size limits (e.g. a `jsonb`
107
+ * value over 255 MiB, an index row over its page budget). */
108
+ export declare const PG_PROGRAM_LIMIT_EXCEEDED_SQLSTATE = "54000";
109
+ /**
110
+ * 「一行装不下」是被哪一堵墙拒的(闭集;加一堵 ⇒ 读它的 switch 编译红)—— 出路随墙不同,所以墙是这类拒的一位事实:
111
+ * · `row` —— TiDB 的单行墙(`txn-entry-size-limit`,errno 8025;出路:抬它);
112
+ * · `statement` —— MySQL 协议的语句墙(`max_allowed_packet`,errno 1153 / `ER_NET_PACKET_TOO_LARGE`;InnoDB 与 TiDB 都有;出路:抬它);
113
+ * · `value` —— PostgreSQL 的硬上限(SQLSTATE `54000`,如一只 `jsonb` 值超 255 MiB;没有可调的墙,出路只有缩小)。
114
+ */
115
+ export type RowTooLargeWall = "row" | "statement" | "value";
116
+ /**
117
+ * 「这个写**是**被引擎以『一行装不下』拒的吗、被哪堵墙」—— 单一判据(与本文件其余谓词同一条 single-semantic-multi-site-drift 纪律)。
118
+ * MySQL 协议两条墙都认 —— 两条墙对**一条单行 INSERT** 说的是同一件事(这一行写不进),出路不同;PG 认 `54000`。认不出的一律 `undefined`(原始错误原样上抛)。
119
+ */
120
+ export declare function rowTooLargeWallOf(dialect: SqlDialect, err: unknown): RowTooLargeWall | undefined;
121
+ /**
122
+ * 检查点店的两笔写(续跑输入 `putCtx` / 停驻行 `put`)被引擎以「一行装不下」拒 ⇒ **这一只**带码错误(码 `store.row_too_large`),不以裸驱动错上抛
123
+ * (其余店写今天仍是各自的驱动错 —— 未收,登记在案)。
124
+ * `row` = 哪一张表的哪一格、为谁写(进句子与运维日志);`wall` = 拒它的那堵墙({@link rowTooLargeWallOf} 的读数;提交腿按它选 413 的出路);
125
+ * `dbMessage` = 引擎自己的那句(TiDB 的句子带着上限与这一行的实际字节数),截到 200 字符。
126
+ * 提交腿把它答成 413 `request.payload_too_large`(`http/submit-ctx.ts`);停驻那一拍它经引擎的错误座落进运维日志。
127
+ */
128
+ export declare class StoreRowTooLargeError extends Error {
129
+ readonly row: string;
130
+ readonly wall: RowTooLargeWall;
131
+ readonly code: "store.row_too_large";
132
+ readonly dbMessage: string;
133
+ constructor(row: string, cause: unknown, wall: RowTooLargeWall);
134
+ }
135
+ /** 店写口的翻译:{@link rowTooLargeWallOf} 认得出 ⇒ {@link StoreRowTooLargeError}(带那堵墙);其余原样交回(调用方 `throw` 它)。 */
136
+ export declare function rowTooLargeOrSelf(dialect: SqlDialect, err: unknown, row: string): unknown;
96
137
  //# sourceMappingURL=sql-errors.d.ts.map
@@ -45,4 +45,40 @@ export function isDeadlockError(dialect, err) {
45
45
  return keyOf(err, "code") === PG_DEADLOCK_DETECTED_SQLSTATE;
46
46
  return keyOf(err, "errno") === MYSQL_ER_LOCK_DEADLOCK_ERRNO || keyOf(err, "code") === MYSQL_ER_LOCK_DEADLOCK_CODE;
47
47
  }
48
+ export const TIDB_ER_ENTRY_TOO_LARGE_ERRNO = 8025;
49
+ export const MYSQL_ER_NET_PACKET_TOO_LARGE_ERRNO = 1153;
50
+ export const MYSQL_ER_NET_PACKET_TOO_LARGE_CODE = "ER_NET_PACKET_TOO_LARGE";
51
+ export const PG_PROGRAM_LIMIT_EXCEEDED_SQLSTATE = "54000";
52
+ export function rowTooLargeWallOf(dialect, err) {
53
+ if (err === null || typeof err !== "object")
54
+ return undefined;
55
+ if (dialect === "pg")
56
+ return keyOf(err, "code") === PG_PROGRAM_LIMIT_EXCEEDED_SQLSTATE ? "value" : undefined;
57
+ const errno = keyOf(err, "errno");
58
+ if (errno === TIDB_ER_ENTRY_TOO_LARGE_ERRNO)
59
+ return "row";
60
+ if (errno === MYSQL_ER_NET_PACKET_TOO_LARGE_ERRNO || keyOf(err, "code") === MYSQL_ER_NET_PACKET_TOO_LARGE_CODE)
61
+ return "statement";
62
+ return undefined;
63
+ }
64
+ export class StoreRowTooLargeError extends Error {
65
+ row;
66
+ wall;
67
+ code = "store.row_too_large";
68
+ dbMessage;
69
+ constructor(row, cause, wall) {
70
+ const dbMessage = (cause instanceof Error ? cause.message : String(cause)).replace(/[\u0000-\u001f\u007f]+/g, " ").slice(0, 200);
71
+ super(`${row} was refused by the database: the row is larger than the database stores in one row (${dbMessage}). ` +
72
+ "Shrink what the request carries inline (images / userAttachments), or raise the database's single-row limit " +
73
+ "(TiDB: performance.txn-entry-size-limit, or the tidb_txn_entry_size_limit variable on TiDB 7.6+; MySQL / MariaDB: max_allowed_packet)");
74
+ this.row = row;
75
+ this.wall = wall;
76
+ this.name = "StoreRowTooLargeError";
77
+ this.dbMessage = dbMessage;
78
+ }
79
+ }
80
+ export function rowTooLargeOrSelf(dialect, err, row) {
81
+ const wall = rowTooLargeWallOf(dialect, err);
82
+ return wall !== undefined ? new StoreRowTooLargeError(row, err, wall) : err;
83
+ }
48
84
  //# sourceMappingURL=sql-errors.js.map
@@ -12,7 +12,8 @@ import { FileRunStore } from "./file-run-store.js";
12
12
  import { StoreBootRefusalError } from "./store-contracts.js";
13
13
  import { buildStoreOpenRefusalTail } from "./store-open-refusal.js";
14
14
  import { acquireLocalBackendWithWait } from "./store-lock-wait.js";
15
- import { NO_SQL_MIGRATIONS, assertRetiredSchemaAbsentOnPool, runSqlOneTimeMigrations } from "./one-time-migrations.js";
15
+ import { NO_SQL_MIGRATIONS, runSqlOneTimeMigrations } from "./one-time-migrations.js";
16
+ import { assertRetiredSchemaAbsentOnPool } from "./schema-consistency.js";
16
17
  import { LocalSessionStore, createLocalSessionRepo } from "./local-session-store.js";
17
18
  import { LocalCheckpointStore } from "./local-checkpoint-store.js";
18
19
  import { FileResumeAnchorStore } from "./file-resume-anchor-store.js";
@@ -1,4 +1,4 @@
1
- import { BaseSessionStorage, type PromptEpochDescriptor, type SessionMetadata, type SessionTreeEntry, type SessionWriteOptions } from "@sema-agent/core";
1
+ import { BaseSessionStorage, type SessionMetadata, type SessionTreeEntry, type SessionWriteOptions } from "@sema-agent/core";
2
2
  import type { Pool } from "mysql2/promise";
3
3
  export declare const SESSION_EVENT_LATEST_BY_TYPE_SQL: {
4
4
  readonly floor: `SELECT /*+ USE_INDEX(session_event, idx_session_event_type) */ ${string} FROM session_event WHERE session_id = ? AND type = 'compaction' ORDER BY seq DESC LIMIT 1`;
@@ -24,17 +24,13 @@ export declare class TiDBSessionStorage extends BaseSessionStorage {
24
24
  private readonly floorSeq;
25
25
  constructor(pool: Pool, sessionId: string, metadata: SessionMetadata, entries: SessionTreeEntry[], leafId: string | null, floorEntryId?: string | null, floorSeq?: number);
26
26
  /**
27
- * [ref] S3 [ref] pull seam — `SessionStorage.getEpochAnchor?()`: the nearest epoch carrier BELOW the
28
- * bounded-wake floor. core's `Session.getPromptEpoch()` (≥1.305) calls this only when the in-window
29
- * branch walk finds no carrier — i.e. a rewind/resumeAt cut inside the kept tail, where the compaction
30
- * restatement is a DESCENDANT of the cut and the root pin sits under the floor. Semantics per core's
31
- * ruling: highest-seq valid carrier below the floor (not an ancestor-chain walk — fallback of last
32
- * resort); malformed rows are skipped (#13: fall back to the older valid pin — the write gate refuses
33
- * malformed carriers, so these only exist via out-of-band writes); none ⇒ undefined = a genuine
34
- * pre-epoch session (core mints legacy_migration, the correct meaning). core re-normalizes whatever we
35
- * return, so this stays honest even if the two sides ever disagree on strictness.
27
+ * [ref] S3 [ref] pull seam — `SessionStorage.getEpochAnchor?()`: the nearest prompt-epoch carrier BELOW the
28
+ * bounded-wake floor, AS STORED (engine 7.52.0: `Promise<unknown>`; the engine reads the record, a retired shape
29
+ * included). The engine's `Session.getPromptEpoch()` asks for it only when the in-window branch walk finds no carrier
30
+ * (a rewind/resumeAt cut inside the kept tail; compactions without a restatement). The one reading of both SQL twins
31
+ * is {@link scanEpochAnchor}; this backend supplies its page query (keyset-paged to exhaustion).
36
32
  */
37
- getEpochAnchor(): Promise<PromptEpochDescriptor | undefined>;
33
+ getEpochAnchor(): Promise<unknown>;
38
34
  /**
39
35
  * Rebuild a session from its event log (= wake). Returns null if the session does not exist.
40
36
  *
@@ -1,9 +1,10 @@
1
1
  import { emitLeafAdvance } from "../session-leaf-bus.js";
2
- import { BaseSessionStorage, SessionError, leafIdAfterEntry, normalizePromptEpoch, } from "@sema-agent/core";
2
+ import { BaseSessionStorage, SessionError, leafIdAfterEntry, } from "@sema-agent/core";
3
3
  import { toIso } from "./sql-row-helpers.js";
4
4
  import { isMysqlDupKeyError } from "./sql-errors.js";
5
5
  import { PLACEMENT_SELECT_COLUMNS, placementFromRow } from "./session-placement.js";
6
6
  import { decodeSessionEventPayload } from "./session-event-payload.js";
7
+ import { scanEpochAnchor } from "./session-epoch-anchor.js";
7
8
  import { jsonColumnText } from "./sql-json-column.js";
8
9
  import { incarnationFromCell } from "../session-incarnation.js";
9
10
  const SESSION_EVENT_BY_TYPE_HINT = "/*+ USE_INDEX(session_event, idx_session_event_type) */";
@@ -23,21 +24,10 @@ export class TiDBSessionStorage extends BaseSessionStorage {
23
24
  this.floorSeq = floorSeq;
24
25
  }
25
26
  async getEpochAnchor() {
26
- if (this.floorSeq <= 0)
27
- return undefined;
28
- let cursor = this.floorSeq;
29
- for (;;) {
30
- const [rows] = await this.pool.query(SESSION_EVENT_LATEST_BY_TYPE_SQL.epochAnchor, [this.sessionId, cursor]);
31
- if (rows.length === 0)
32
- return undefined;
33
- for (const r of rows) {
34
- const e = decodeSessionEventPayload(r.payload, this.sessionId, r.seq);
35
- const d = e.type === "prompt_epoch" ? normalizePromptEpoch(e) : normalizePromptEpoch(e.details?.promptEpoch);
36
- if (d)
37
- return d;
38
- }
39
- cursor = Number(rows[rows.length - 1].seq);
40
- }
27
+ return scanEpochAnchor(this.sessionId, this.floorSeq, async (beforeSeq) => {
28
+ const [rows] = await this.pool.query(SESSION_EVENT_LATEST_BY_TYPE_SQL.epochAnchor, [this.sessionId, beforeSeq]);
29
+ return rows.map((r) => ({ seq: r.seq, payload: r.payload }));
30
+ });
41
31
  }
42
32
  static async wake(pool, sessionId) {
43
33
  const [metaRows] = await pool.query(`SELECT created_at, leaf_id, leaf_seq, incarnation, ${PLACEMENT_SELECT_COLUMNS} FROM session_meta WHERE session_id = ?`, [sessionId]);
@@ -334,7 +334,7 @@ export class TiDBSessionStore {
334
334
  }
335
335
  await conn.query("DELETE FROM session_event WHERE session_id = ?", [sessionId]);
336
336
  await conn.query("DELETE FROM session_meta WHERE session_id = ? AND owner <=> ? AND placement_kind IS NULL", [sessionId, owner]);
337
- await this.insertValidatedLog(conn, sessionId, owner, validated, carriedIncarnation(placedRows[0] === undefined ? undefined : { owner: placedRows[0].owner, incarnation: placedRows[0].incarnation }, owner));
337
+ await this.insertValidatedLog(conn, sessionId, owner, validated, carriedIncarnation(sessionId, placedRows[0] === undefined ? undefined : { owner: placedRows[0].owner, incarnation: placedRows[0].incarnation }, owner));
338
338
  await conn.commit();
339
339
  }
340
340
  catch (err) {
@@ -1,66 +1,22 @@
1
- import { type PromptTextDeclaration, type PublishedPromptArtifactEnvelope } from "@sema-agent/core";
2
- /** [ref]⓪ 下发形(remote effective 的 additive key,JSON verbatim 面——不经 registry-core parse,所以
3
- * cli 补位前 center 发的 `contentHash` 也原样到达)。 */
4
- export interface EffectiveCenterPrompts {
5
- /** `center:<contentDigest 前 12>` — 纯内容寻址(同内容重发布=同 packId,epoch 不误重 pin)。 */
6
- packId: string;
7
- /** canonical JSON 全量 sha256(非盐化,operator 对账位 [ref]②)。 */
8
- contentDigest: string;
9
- /** covering 已在 center 走完的基线终形(层信息不下发,worker 零解析义务)。 */
10
- sections: PromptTextDeclaration[];
11
- /** per-scenario 完整重算终形(整包替换基线,非增量);无键=用基线。 */
12
- scenarioOverrides?: Record<string, PromptTextDeclaration[]>;
13
- }
14
1
  /**
15
- * 结构判形(防炸底线,非语义复检)。`ok:false` 时调用方拒绝采用该候选的 prompts 面并 warn——绝不让
16
- * 一个坏 pack 把每个任务的 prepare 炸掉。校验与 core assemble 的 THROW 条件同形(id 规范/长度/core/
17
- * 保留、slot 闭集)+ 下发信封两键形;`contentHash` 只查形(值的校验归 core 1.315,错值 core 丢弃+warn)。
2
+ * prompts 域候选门 —— 中心提示层的**唯一判官**(中立叶子:只依赖 `@sema-agent/core` 与契约包 `@sema-agent/settings-schema`,
3
+ * 不依赖 capabilities / config 任何东西;候选判官 `web-admin-client/candidate-gate.ts` 是它唯一的调用点,LKG 读回经同一只候选判官)。
4
+ *
5
+ * 律:中心层只有一种 wire 形 —— `{ schemaVersion: 2, artifact }`(契约包 `EffectivePromptsWire`;artifact 是引擎当期 schema 的发布形)。
6
+ * 判法两步一线:先过契约包的 wire 形(外壳、键闭集、结构与上界;旧两轴 / v2 artifact 的拒句是契约包的原句),再交引擎
7
+ * `verifyPromptArtifact`(语义:每条 overlay 在中心类的档权内、锁定文本、requires 复算、同目标冲突、digest —— 拒句是引擎的原句)。
8
+ * 本模块不写任何形判、不铸任何句子、不归一任何别的形:中心不带 prompts 键 = 不下发中心层(调用方据此撤下);带了就必须是这一种形,
9
+ * 否则整份候选拒(一个域都不应用)。
18
10
  */
19
- export declare function validateCenterPrompts(raw: unknown): {
20
- ok: true;
21
- value: EffectiveCenterPrompts;
11
+ import { type PublishedPromptArtifact } from "@sema-agent/core";
12
+ /** 判官的结论:收下 ⇒ 引擎 verify 过的发布形 artifact(调用方交给引擎的中心源采用);拒 ⇒ 拒因(契约包 / 引擎的原句)。 */
13
+ export type PromptsWireVerdict = {
14
+ readonly ok: true;
15
+ readonly artifact: PublishedPromptArtifact;
22
16
  } | {
23
- ok: false;
24
- error: string;
17
+ readonly ok: false;
18
+ readonly error: string;
25
19
  };
26
- /** core 引擎版本(verifyPromptArtifact 的 compatibleCore range 输入)——经 exports map 解析包根读
27
- * package.json(`require('@sema-agent/core/package.json')` 被 exports 封死,ERR_PACKAGE_PATH_NOT_EXPORTED
28
- * 第一手教训)。解析失败回 "0.0.0" = range-gated artifact 被诚实拒(fail-closed 响,绝不静默采用)。 */
29
- export declare const CORE_ENGINE_VERSION: string;
30
- /** validatePromptsDomain 的归一产物:两轴的已判形面 + 变更检测身份(adopt 去重键,跨轴稳定)。 */
31
- export interface PromptsDomainFaces {
32
- /** A 形(declaration 轴)——centerPromptProvider 继续消费(现行链路零改动)。 */
33
- declarations?: EffectiveCenterPrompts;
34
- /** B 形(catalog 轴)——core S1 verify 已过(digest 复算/上限/range 全管线);server 不解释内容,
35
- * 原样交 core artifact 仓(e/b 班车接线位)。 */
36
- catalog?: PublishedPromptArtifactEnvelope;
37
- /** 采用去重键:两轴 digest 拼接(轴缺席记 "-")。 */
38
- identity: string;
39
- /** 观测面([ref]a §3:`center_prompts_adopted` 增 axes,看两轴共存窗)。 */
40
- axes: Array<"declarations" | "catalog">;
41
- }
42
- /**
43
- * prompts 域候选门 v2(validateCenterPrompts 的双轴升级;整拒语义不变=任一在场轴坏形 ⇒ 整个 prompts
44
- * 面拒绝采用,LKG/内置继续服务)。唯一认可形=`{schemaVersion:1, declarations?, catalog?}`
45
- * (registry-core `EffectivePromptsWire`)至少一轴。[ref]B-9(5.0.0):legacy 平铺兼容窗到期删——
46
- * 0.13.0 起 EffectivePromptsWire 首字段是 `schemaVersion: z.ZodLiteral<1>`,与 server 共用 ^0.13.0 的
47
- * 合规 center 不可能再发平铺;非合规体=响亮拒绝(整拒落 LKG/内置),不再静默吸收。
48
- * catalog 轴的语义权威=core `verifyPromptArtifact`(形/digest 逐位复算/ARTIFACT_LIMITS/compatibleCore
49
- * range 全在 core 一处——[ref]③ a 解,上限单真源永不漂)。
50
- */
51
- export declare function validatePromptsDomain(raw: unknown): {
52
- ok: true;
53
- value: PromptsDomainFaces;
54
- } | {
55
- ok: false;
56
- error: string;
57
- };
58
- /** [ref](docs/R100-PROMPTS-LANE-FIX.md §V2-B)worker 腿 effective 上 `prompts` 键的**形-肯定**判别:
59
- * 消费形特征(v2 `schemaVersion` 键,或 legacy 三键 packId/contentDigest/sections)⇒ 原样返回交
60
- * {@link validatePromptsDomain}(收或拒,方向不变);其余=center 存储原文(global lane [ref]①e 下发
61
- * 存储形是合法输入)——catalog 在场则投影 `{schemaVersion:1, catalog}`(config-provider 本地腿先例),
62
- * 否则视为「center 未发」⇒ undefined。null=有意 cleared 语义([ref] 前 null 走拒,方向为宽是设计变更,
63
- * 留痕于设计档)。垃圾标量原样返回(fail-loud 归 validate)。
64
- * 属主放本中立叶:config-lkg 与 boot/refresh 同吃,capabilities 层会重开 S6 刚消的反向依赖边。 */
65
- export declare function centerPromptsFromEffective(eff: unknown): unknown | undefined;
20
+ /** 一份候选 `prompts` 键的值(在场才调)→ 判官结论。 */
21
+ export declare function judgePromptsWire(raw: unknown): PromptsWireVerdict;
66
22
  //# sourceMappingURL=prompts-domain-validate.d.ts.map