@sema-agent/server 7.43.0 → 7.44.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 (345) hide show
  1. package/USAGE.md +18 -0
  2. package/dist/adoption/plan.js +31 -192
  3. package/dist/adoption/quiesce.js +3 -86
  4. package/dist/adoption/runner.js +3 -140
  5. package/dist/adoption/sql.js +0 -74
  6. package/dist/adoption/wire.js +0 -80
  7. package/dist/approval-ask-machine.js +0 -75
  8. package/dist/approval-card.js +0 -323
  9. package/dist/approval-deny-reasons.js +0 -42
  10. package/dist/approval-hmac.js +0 -35
  11. package/dist/approval-reconciler.js +8 -166
  12. package/dist/approval.js +4 -67
  13. package/dist/audit.js +1 -44
  14. package/dist/auth-bridge.js +4 -47
  15. package/dist/auth-keys.js +0 -23
  16. package/dist/bake-runner/main.js +4 -65
  17. package/dist/bake-runner/protocol.js +3 -58
  18. package/dist/bake-runner/runner.js +5 -91
  19. package/dist/bench/l8/artifact.js +2 -61
  20. package/dist/bench/l8/escape.js +0 -25
  21. package/dist/bench/l8/index.js +0 -14
  22. package/dist/bench/l8/probes.js +0 -60
  23. package/dist/bench/l8/run-probes.js +2 -45
  24. package/dist/bench/s1/arms.js +14 -189
  25. package/dist/bench/s1/live-deps.js +11 -253
  26. package/dist/bench/s1/oracle.js +1 -36
  27. package/dist/bench/s1/repair-oracle-adapter.js +0 -30
  28. package/dist/bench/s1/reviewer.js +0 -34
  29. package/dist/bench/s1/row.js +0 -62
  30. package/dist/bench/s1/run-firm.js +4 -70
  31. package/dist/bench/s1/runner-ctx.js +0 -40
  32. package/dist/bench/s1/tasks.js +0 -115
  33. package/dist/boot/adoption.js +0 -20
  34. package/dist/boot/budget-tracing.js +3 -55
  35. package/dist/boot/config-center.js +43 -551
  36. package/dist/boot/coordinators.js +1 -69
  37. package/dist/boot/crash-last.js +0 -18
  38. package/dist/boot/deferred-sandbox-path-env.js +3 -119
  39. package/dist/boot/execution-env.js +5 -171
  40. package/dist/boot/governance-seams.js +0 -92
  41. package/dist/boot/leader.js +0 -69
  42. package/dist/boot/limit-sync.js +0 -2
  43. package/dist/boot/memory-boundary.js +3 -91
  44. package/dist/boot/org-memory.js +1 -25
  45. package/dist/boot/parked-revive-gate.js +0 -150
  46. package/dist/boot/permission-rules-audit.js +1 -56
  47. package/dist/boot/reapers.js +15 -273
  48. package/dist/boot/resolve-spec.js +9 -768
  49. package/dist/boot/retention-lane.js +4 -87
  50. package/dist/boot/runner-deps.js +3 -297
  51. package/dist/boot/runtime-caps.js +0 -40
  52. package/dist/boot/session-faces.js +2 -133
  53. package/dist/boot/shutdown.js +9 -83
  54. package/dist/boot/side-query-lane.js +2 -137
  55. package/dist/boot/stores.js +15 -215
  56. package/dist/boot/task-list-lane.js +0 -18
  57. package/dist/boot/webfetch-summarize-lane.js +0 -47
  58. package/dist/boot/workflow-orchestration.js +3 -99
  59. package/dist/boot-reclaim.js +0 -20
  60. package/dist/bounded-session-map.js +0 -19
  61. package/dist/brain.js +2 -139
  62. package/dist/budget.js +9 -244
  63. package/dist/capabilities/builtin-tools.js +0 -3
  64. package/dist/capabilities/center-plugins.js +6 -51
  65. package/dist/capabilities/center-prompts.js +6 -71
  66. package/dist/capabilities/code-review-council.js +7 -40
  67. package/dist/capabilities/collab-workflows.js +1 -44
  68. package/dist/capabilities/hands-lane.js +0 -65
  69. package/dist/capabilities/memory-notice.js +0 -70
  70. package/dist/capabilities/prompt.js +0 -14
  71. package/dist/capabilities/prompts/code-review.js +0 -14
  72. package/dist/capabilities/prompts/identity.js +0 -6
  73. package/dist/capabilities/prompts/team.js +0 -4
  74. package/dist/capabilities/repo-tools.js +0 -34
  75. package/dist/capabilities/sandbox-file-send.js +3 -92
  76. package/dist/capabilities/scenarios.d.ts +0 -1
  77. package/dist/capabilities/scenarios.js +18 -279
  78. package/dist/capabilities/select-environment-tool.js +0 -32
  79. package/dist/capabilities/send-user-file-tool.js +2 -66
  80. package/dist/capabilities/skills.d.ts +1 -2
  81. package/dist/capabilities/skills.js +3 -21
  82. package/dist/capabilities/team.d.ts +3 -11
  83. package/dist/capabilities/team.js +3 -51
  84. package/dist/capabilities/tool-defer.js +0 -3
  85. package/dist/config-center/apply-effective.js +15 -330
  86. package/dist/config-center/apply-ledger.js +5 -38
  87. package/dist/config-center/facade.js +0 -41
  88. package/dist/config-center/hot-keys-registry.js +0 -20
  89. package/dist/config-center/http-client.js +2 -125
  90. package/dist/config-center/mcp-revocation.js +2 -34
  91. package/dist/config-center/read-face.js +0 -59
  92. package/dist/config-center/restart-signal.js +1 -85
  93. package/dist/config-center/skills-mcp.d.ts +1 -1
  94. package/dist/config-center/skills-mcp.js +9 -76
  95. package/dist/config-center/stage-limits.js +8 -35
  96. package/dist/config-invariants.js +0 -16
  97. package/dist/config-lkg.js +0 -42
  98. package/dist/config-provider.js +3 -186
  99. package/dist/config-types.js +0 -5
  100. package/dist/config.js +107 -1145
  101. package/dist/degenerate-instrument.js +3 -67
  102. package/dist/deployment-governance.js +0 -124
  103. package/dist/digest-form.js +0 -12
  104. package/dist/elicitation.js +3 -86
  105. package/dist/env-facts.js +7 -75
  106. package/dist/fleet/fleet-bus.js +34 -507
  107. package/dist/fleet/fleet-reconciler.js +9 -149
  108. package/dist/fleet/fleet-terminal-window.js +10 -178
  109. package/dist/fleet/subagent-tail-bus.js +3 -72
  110. package/dist/fleet-client.js +10 -70
  111. package/dist/fleet-lease.js +5 -79
  112. package/dist/git-api-kind.js +0 -3
  113. package/dist/governance-ask-marks.js +2 -78
  114. package/dist/hooks/branch-transcript.js +0 -74
  115. package/dist/hooks/cc-agent-hook-prompt.js +0 -29
  116. package/dist/hooks/cc-stop-prompt.js +1 -46
  117. package/dist/hooks/hook-llm.js +1 -53
  118. package/dist/hooks/hook-runner.js +20 -414
  119. package/dist/http/active-run-conflict.js +4 -129
  120. package/dist/http/cursor-fingerprint.d.ts +5 -0
  121. package/dist/http/cursor-fingerprint.js +5 -0
  122. package/dist/http/idempotency.js +0 -37
  123. package/dist/http/principal-gate.js +3 -40
  124. package/dist/http/route-ctx.js +0 -9
  125. package/dist/http/routes/a2a-serve.js +7 -319
  126. package/dist/http/routes/admin-config-refresh.js +0 -4
  127. package/dist/http/routes/admin-drain.js +0 -10
  128. package/dist/http/routes/adoption.js +1 -29
  129. package/dist/http/routes/agents-roster.js +1 -47
  130. package/dist/http/routes/approvals-assistant.js +32 -395
  131. package/dist/http/routes/attachments.js +4 -22
  132. package/dist/http/routes/capabilities.js +3 -420
  133. package/dist/http/routes/diagnostics.js +1 -81
  134. package/dist/http/routes/fleet.js +9 -185
  135. package/dist/http/routes/images.js +14 -239
  136. package/dist/http/routes/leader.js +0 -13
  137. package/dist/http/routes/memory-bundle.js +1 -61
  138. package/dist/http/routes/memory-policy.js +9 -99
  139. package/dist/http/routes/notify-wake.js +3 -37
  140. package/dist/http/routes/observability.js +2 -19
  141. package/dist/http/routes/retention-ops.js +2 -34
  142. package/dist/http/routes/rules.js +1 -93
  143. package/dist/http/routes/runs.js +64 -873
  144. package/dist/http/routes/session-sync.js +19 -258
  145. package/dist/http/routes/sessions-list.js +17 -43
  146. package/dist/http/routes/sessions.js +34 -226
  147. package/dist/http/routes/shared-memory.js +5 -36
  148. package/dist/http/routes/side-query.js +1 -87
  149. package/dist/http/routes/tasks.js +52 -722
  150. package/dist/http/routes/trace-usage.js +32 -195
  151. package/dist/http/routes/workflows.js +19 -195
  152. package/dist/http/run-meta.js +0 -6
  153. package/dist/http/send.js +0 -32
  154. package/dist/http/server.js +92 -1597
  155. package/dist/http/sse-lifecycle.js +2 -13
  156. package/dist/http/sse-log.js +3 -48
  157. package/dist/http/tar.js +5 -21
  158. package/dist/http/verify-rounds.js +0 -5
  159. package/dist/http/wire-gate.js +0 -9
  160. package/dist/http/workspace-content.js +0 -10
  161. package/dist/images/bake-validate.js +1 -70
  162. package/dist/images/manifest.js +1 -6
  163. package/dist/index.js +0 -21
  164. package/dist/key-resolver.js +2 -17
  165. package/dist/leader/diffout.js +1 -20
  166. package/dist/leader/diffup.js +0 -47
  167. package/dist/leader/endpoint.js +2 -57
  168. package/dist/leader/fanout.js +3 -45
  169. package/dist/leader/grader-env-factory.js +3 -72
  170. package/dist/leader/leader.js +5 -156
  171. package/dist/leader/merge.js +8 -115
  172. package/dist/leader/planner.js +3 -54
  173. package/dist/leader/repair-oracle.js +1 -60
  174. package/dist/leader/repair-wire.js +2 -79
  175. package/dist/leader/wire.js +8 -307
  176. package/dist/lsp/e2b-bridge.js +4 -64
  177. package/dist/lsp/e2b-manager.js +6 -94
  178. package/dist/lsp/lsp-frames.js +0 -12
  179. package/dist/lsp/manager.js +4 -96
  180. package/dist/lsp/ws-transport.js +5 -55
  181. package/dist/lsp-evict.js +1 -15
  182. package/dist/main.js +57 -765
  183. package/dist/memory-bundle-engine.js +0 -55
  184. package/dist/memory-export.js +0 -4
  185. package/dist/memory-posture.js +1 -15
  186. package/dist/memory-scope.js +11 -148
  187. package/dist/memory-sync-client.js +2 -44
  188. package/dist/memory-sync.js +1 -80
  189. package/dist/model-select.js +3 -80
  190. package/dist/observability/cost-quota.js +1 -17
  191. package/dist/observability/cost-taxonomy.js +0 -34
  192. package/dist/observability/fail-open.js +7 -86
  193. package/dist/observability/logger.js +0 -6
  194. package/dist/observability/metrics.js +0 -94
  195. package/dist/observability/otel-exporter.js +3 -13
  196. package/dist/observability/principal-context.js +0 -9
  197. package/dist/observability/prompt-manifest.js +1 -37
  198. package/dist/observability/rate-limit.js +0 -4
  199. package/dist/observability/secret-env-scrub.js +2 -56
  200. package/dist/observability/tool-trace.js +1 -70
  201. package/dist/orchestration/hardened-vm-runner.js +4 -118
  202. package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
  203. package/dist/orchestration/hardened-vm-worker.js +0 -27
  204. package/dist/orchestration/subagent-steer.js +1 -45
  205. package/dist/orchestration/workflow-agent-steer.js +1 -80
  206. package/dist/orchestration/workflow-completion-inbox.js +32 -285
  207. package/dist/orchestration/workflow-notify-journal.js +16 -259
  208. package/dist/org-memory-admission.js +3 -47
  209. package/dist/parent-watch.js +2 -48
  210. package/dist/parked-decide.js +1 -109
  211. package/dist/per-task-image.js +0 -57
  212. package/dist/plan-cache-probe.js +3 -27
  213. package/dist/plugins/adoption-log-sql.js +2 -119
  214. package/dist/plugins/approval-ask-store-memory.js +3 -38
  215. package/dist/plugins/approval-ask-store-sql.js +6 -188
  216. package/dist/plugins/approval-exemption-store.js +2 -28
  217. package/dist/plugins/background-agent-store-sql.js +4 -105
  218. package/dist/plugins/background-shell-support.js +14 -122
  219. package/dist/plugins/blob-backend.js +6 -169
  220. package/dist/plugins/breaker-state-sql.js +8 -46
  221. package/dist/plugins/caching-session-store.js +4 -106
  222. package/dist/plugins/checkpoint-store-sql.js +18 -505
  223. package/dist/plugins/e2b-orphan-reclaim.js +0 -45
  224. package/dist/plugins/file-outcome-sink.js +0 -9
  225. package/dist/plugins/file-resume-anchor-store.js +4 -43
  226. package/dist/plugins/file-run-store.js +26 -364
  227. package/dist/plugins/file-snapshot-store-sql.js +10 -181
  228. package/dist/plugins/fork-routing-session-store.js +8 -111
  229. package/dist/plugins/host-platform.js +2 -91
  230. package/dist/plugins/image-bake-store-sql.js +7 -250
  231. package/dist/plugins/image-index-sql.js +4 -123
  232. package/dist/plugins/k8s-bg-scripts.js +4 -88
  233. package/dist/plugins/k8s-exec-protocol.js +0 -41
  234. package/dist/plugins/leader-run-store-sql.js +0 -101
  235. package/dist/plugins/local-checkpoint-store.js +8 -128
  236. package/dist/plugins/local-session-store.js +34 -296
  237. package/dist/plugins/local-task-attachment-store.js +2 -16
  238. package/dist/plugins/mailbox-store-sql.js +8 -65
  239. package/dist/plugins/memory-embedder-fingerprint.js +5 -166
  240. package/dist/plugins/memory-embedder.js +4 -70
  241. package/dist/plugins/memory-engine-pg.js +6 -170
  242. package/dist/plugins/memory-engine-tidb.js +7 -155
  243. package/dist/plugins/memory-engine-vector-util.js +0 -10
  244. package/dist/plugins/memory-key-guards.js +0 -34
  245. package/dist/plugins/memory-origin-law.js +0 -187
  246. package/dist/plugins/memory-resume-anchor-store.js +0 -17
  247. package/dist/plugins/memory-run-store.js +12 -89
  248. package/dist/plugins/memory-session-policy-store.js +0 -17
  249. package/dist/plugins/memory-sync-store-pg.js +4 -49
  250. package/dist/plugins/memory-sync-store-tidb.js +3 -35
  251. package/dist/plugins/outcome-ledger-sql.js +3 -97
  252. package/dist/plugins/permission-rule-store-file.js +5 -133
  253. package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
  254. package/dist/plugins/permission-rule-store-sql.js +50 -307
  255. package/dist/plugins/pg-cost-quota.js +0 -7
  256. package/dist/plugins/pg-pool.js +0 -92
  257. package/dist/plugins/pg-rate-limiter.js +2 -13
  258. package/dist/plugins/pg-safe-json.js +4 -40
  259. package/dist/plugins/pg-session-storage.js +25 -189
  260. package/dist/plugins/posix-shell-fs.js +1 -31
  261. package/dist/plugins/remote-env-adb.js +12 -101
  262. package/dist/plugins/remote-env-e2b.js +40 -371
  263. package/dist/plugins/remote-env-file-error.js +0 -37
  264. package/dist/plugins/remote-env-host.js +63 -483
  265. package/dist/plugins/remote-env-k8s.js +32 -326
  266. package/dist/plugins/remote-env-local-docker.js +24 -157
  267. package/dist/plugins/remote-env-ssh.js +20 -128
  268. package/dist/plugins/remote-scratchpad.js +2 -32
  269. package/dist/plugins/remote-shell.js +1 -32
  270. package/dist/plugins/resume-anchor-store-sql.js +0 -11
  271. package/dist/plugins/retention-lane-store-sql.js +0 -108
  272. package/dist/plugins/retention-store-sql.js +4 -383
  273. package/dist/plugins/roster-store-sql.js +0 -55
  274. package/dist/plugins/run-store-sql.js +14 -251
  275. package/dist/plugins/s3-presign.js +2 -49
  276. package/dist/plugins/scheduler-support.js +3 -80
  277. package/dist/plugins/send-file-ledger.js +4 -53
  278. package/dist/plugins/send-user-file.js +4 -94
  279. package/dist/plugins/session-placement.js +1 -89
  280. package/dist/plugins/session-policy-store-sql.js +3 -81
  281. package/dist/plugins/session-store.js +0 -57
  282. package/dist/plugins/shared-memory-store-sql.js +4 -167
  283. package/dist/plugins/sql-driver.js +0 -17
  284. package/dist/plugins/sql-errors.js +0 -7
  285. package/dist/plugins/sql-escape.js +0 -8
  286. package/dist/plugins/sql-row-helpers.js +0 -25
  287. package/dist/plugins/store-backend.js +42 -222
  288. package/dist/plugins/store-contracts.js +2 -46
  289. package/dist/plugins/task-attachment-store.js +3 -49
  290. package/dist/plugins/task-list-store-sql.js +0 -79
  291. package/dist/plugins/tidb-cost-quota.js +1 -4
  292. package/dist/plugins/tidb-pool.js +1 -207
  293. package/dist/plugins/tidb-rate-limiter.js +3 -9
  294. package/dist/plugins/tidb-session-storage.js +4 -70
  295. package/dist/plugins/tidb-session-store.js +19 -341
  296. package/dist/plugins/tool-result-store-sql.js +4 -179
  297. package/dist/plugins/usage-window-store-sql.js +0 -8
  298. package/dist/plugins/web-search.js +10 -132
  299. package/dist/plugins/workflow-journal-store-sql.js +2 -58
  300. package/dist/plugins/workflow-run-store-sql.js +6 -91
  301. package/dist/plugins/worktree-isolation.js +6 -126
  302. package/dist/plugins/write-behind-counter.js +16 -75
  303. package/dist/principal-jwt.js +5 -60
  304. package/dist/project-memory.js +15 -146
  305. package/dist/prompts-domain-validate.js +1 -51
  306. package/dist/question.js +2 -111
  307. package/dist/resource-suspend.js +0 -18
  308. package/dist/router/route-orchestration.js +0 -77
  309. package/dist/rules-consent.d.ts +44 -5
  310. package/dist/rules-consent.js +75 -215
  311. package/dist/run-local.js +14 -381
  312. package/dist/runs.js +29 -617
  313. package/dist/runtime-caps-resolver.js +6 -132
  314. package/dist/runtime-governance.js +1 -232
  315. package/dist/sandbox-pkg-source.js +0 -37
  316. package/dist/sealed-key.js +3 -68
  317. package/dist/security.js +6 -291
  318. package/dist/session-leaf-bus.js +0 -32
  319. package/dist/session-sync-content.js +1 -69
  320. package/dist/session-sync-kernel.js +3 -60
  321. package/dist/session-sync.js +3 -66
  322. package/dist/session-titler.js +7 -41
  323. package/dist/session-watch.js +9 -73
  324. package/dist/shared-memory-scope-authorizer.js +0 -18
  325. package/dist/sighup-idle.js +1 -12
  326. package/dist/spec-fields.js +4 -143
  327. package/dist/store-live-probe.js +3 -49
  328. package/dist/task-a2a.js +1 -126
  329. package/dist/task-cwd.js +1 -103
  330. package/dist/task-mcp.js +1 -89
  331. package/dist/task-settings.js +10 -300
  332. package/dist/task-workflow.js +8 -75
  333. package/dist/tool-approval.d.ts +28 -1
  334. package/dist/tool-approval.js +70 -1430
  335. package/dist/trace/artifacts.js +5 -20
  336. package/dist/trace/engine-notice-wire.js +6 -138
  337. package/dist/trace/ledger-sink.js +7 -96
  338. package/dist/trace/project.js +4 -448
  339. package/dist/trace/redact.js +9 -77
  340. package/dist/turn-activity.js +1 -27
  341. package/dist/usage-analytics.js +4 -34
  342. package/dist/wall-clock-jump-guard.js +1 -68
  343. package/package.json +3 -3
  344. package/dist/capabilities/scenario-alias.d.ts +0 -27
  345. package/dist/capabilities/scenario-alias.js +0 -61
@@ -1,7 +1,4 @@
1
1
  export const TASK_ATTACHMENT_TABLE = "task_attachment";
2
- /** 物化相对路径(确定性,notice 与真实写盘共用一份推导——两处各算一遍会漂):按 listBySession 序
3
- * (created_at_ms, id)分配 `attachments/<name>`;同名撞车时后者前缀 id 头 8 位。名字在上传时已
4
- * 消毒为安全 basename([A-Za-z0-9._-]),拼接后仍是安全相对路径。 */
5
2
  export function materializedRelPaths(atts) {
6
3
  const used = new Set();
7
4
  const out = new Map();
@@ -26,9 +23,6 @@ function rowToRecord(r) {
26
23
  ...(r.bound_at_ms !== null && r.bound_at_ms !== undefined ? { boundAtMs: Number(r.bound_at_ms) } : {}),
27
24
  };
28
25
  }
29
- /** 半场②(提交时绑定 + objective 告知)的可测核。fresh 腿硬验证(未知/他人 id → throw(mapTo400));
30
- * resume 腿降级(warn+跳过——4xx 会砖存量 task;记录不可变 ⇒ 在场行的 notice 字节恒稳)。
31
- * notice 路径从**绑定后的全 session 集**推导(与物化共用 materializedRelPaths),只罗列本次引用的文件。 */
32
26
  export async function bindAttachmentsForTask(opts) {
33
27
  const found = [];
34
28
  for (const id of opts.ids) {
@@ -47,8 +41,6 @@ export async function bindAttachmentsForTask(opts) {
47
41
  const lines = found.map((r) => `- ${paths.get(r.id) ?? `attachments/${r.name}`} (${r.mime}, ${r.sizeBytes} bytes)`);
48
42
  return { notice: `[attachments] ${found.length} uploaded file(s) are available in the \`attachments/\` directory of your working directory:\n${lines.join("\n")}` };
49
43
  }
50
- /** 半场③(env 建立时物化)的可测核。**fail-loud**:objective 已宣告文件在场,静默缺文件=模型按幻影
51
- * 文件行动,比 env 建立失败更糟 ⇒ 任一写失败即 throw。幂等:重建沙箱/resume 重写同路径同字节。 */
52
44
  export async function materializeAttachmentsInto(env, store, sessionId) {
53
45
  const atts = await store.listBySession(sessionId);
54
46
  if (atts.length === 0)
@@ -102,7 +94,6 @@ export async function ensurePgTaskAttachmentSchema(q) {
102
94
  await q(`CREATE INDEX IF NOT EXISTS idx_att_session ON ${TASK_ATTACHMENT_TABLE} (session_id)`);
103
95
  await q(`CREATE INDEX IF NOT EXISTS idx_att_unbound ON ${TASK_ATTACHMENT_TABLE} (created_at_ms)`);
104
96
  }
105
- /** bytes → 4 MiB 块序列(共享切块器,两方言 + 完整性校验共用)。 */
106
97
  export class TiDBTaskAttachmentStore {
107
98
  pool;
108
99
  bytes;
@@ -111,8 +102,6 @@ export class TiDBTaskAttachmentStore {
111
102
  this.bytes = bytes;
112
103
  }
113
104
  async put(rec) {
114
- // 次序=bytes 先、meta 后:meta 行在场 ⇒ 对象在场(读侧无半态)。对象键=sha256(去重;
115
- // MinioBlobBackend putBlob 幂等,重复内容=同键重放)。半途崩溃只留无 meta 的对象——经 API 不可达。
116
105
  await this.bytes.putBlob(rec.sha256, rec.content);
117
106
  await this.pool.execute(`INSERT INTO ${TASK_ATTACHMENT_TABLE} (${COLS}) VALUES (?,?,?,?,?,?,?,?,?)`, [rec.id, rec.owner, rec.name, rec.mime, rec.sha256, rec.sizeBytes, rec.sessionId ?? null, rec.createdAtMs, rec.boundAtMs ?? null]);
118
107
  }
@@ -121,15 +110,12 @@ export class TiDBTaskAttachmentStore {
121
110
  return rows.length ? rowToRecord(rows[0]) : null;
122
111
  }
123
112
  async getContent(owner, id) {
124
- const meta = await this.get(owner, id); // owner 门先过(对象键是 sha,不携 owner——门恒在 meta)
113
+ const meta = await this.get(owner, id);
125
114
  if (!meta)
126
115
  return null;
127
- const b = await this.bytes.getBlob(meta.sha256); // 实现内含 sha 校验(损坏/污染 ⇒ undefined)
116
+ const b = await this.bytes.getBlob(meta.sha256);
128
117
  return b && b.byteLength === meta.sizeBytes ? b : null;
129
118
  }
130
- /** 零引用对象删除(best-effort):meta 已删后,sha 仍被其他行引用则对象保留(跨行去重共享)。
131
- * 失败只吞不抛——E21 purge 不被对象存储抖动卡死(blob-backend 异步删裁定同旨);无 meta 的对象
132
- * 经 API 不可达,彻底清收敛记 follow-up。 */
133
119
  async deleteBytesIfUnreferenced(shas) {
134
120
  for (const sha of new Set(shas)) {
135
121
  try {
@@ -138,7 +124,6 @@ export class TiDBTaskAttachmentStore {
138
124
  await this.bytes.deleteBlobs([sha]);
139
125
  }
140
126
  catch {
141
- /* best-effort(见上) */
142
127
  }
143
128
  }
144
129
  }
@@ -174,17 +159,10 @@ export class TiDBTaskAttachmentStore {
174
159
  await this.deleteBytesIfUnreferenced(rows.map((r) => r.sha256));
175
160
  return res.affectedRows ?? 0;
176
161
  }
177
- /** D-1 孤儿对象彻底 GC(clay 拍 2026-07-28):对象存储列举 × meta 行对账。判据保守三层:
178
- * ①只删**真列举到**且 ②零 meta 行引用 且 ③LastModified 过 grace 的对象(上传先行窗口:对象先落、
179
- * meta 行后写——grace 护住 put 竞态,宁漏勿误删,下轮再收);列举失败=整轮 throw(调用方 catch,
180
- * 「列不出来」绝不当「没有孤儿」)。 */
181
162
  async sweepOrphanObjects(graceMs, nowMs = Date.now()) {
182
163
  const lister = this.bytes;
183
164
  if (typeof lister.listObjects !== "function")
184
- return 0; // 无列举面=无孤儿类(local/桩)
185
- // 🔴 复审 F2:**边列举边消费**——grace 判据是「对象年龄」不是「孤儿性」,稳态下整个存量语料
186
- // (凡超过 grace)都会先进候选集;攒满再分批 ⇒ 内存与语料同阶(20 万对象实测堆 Δ58MB,百万级
187
- // ≈290MB,且每 tick 每副本各一份)。改成满 200 就消费,峰值降到 O(200)。
165
+ return 0;
188
166
  let removed = 0;
189
167
  let batch = [];
190
168
  const flush = async () => {
@@ -192,11 +170,6 @@ export class TiDBTaskAttachmentStore {
192
170
  return;
193
171
  const referenced = await this.referencedShas(batch);
194
172
  const orphans = batch.filter((h) => !referenced.has(h));
195
- // 🔴 复审 B-F1(数据安全):**删前逐对象复查**。grace 只在列举时刻判过一次,而
196
- // 「referencedShas 判零引用 → deleteBlobs」之间落地的同 sha 上传(内容去重 ⇒ 同 key)会让
197
- // meta 行在场而对象被本轮删掉 = 永久丢字节(getContent 恒 null、物化 fail-loud)。put 会 PUT
198
- // 同键刷新 LastModified,所以删前再 HEAD 一次即可识别;窗口压到 HEAD→DELETE 毫秒级。
199
- // 无 headObject 面 ⇒ 保守**不删**(宁留孤儿不误删)。
200
173
  const safe = await this.filterStillOrphan(orphans, graceMs, nowMs);
201
174
  if (safe.length > 0)
202
175
  removed += await this.bytes.deleteBlobs(safe);
@@ -212,7 +185,6 @@ export class TiDBTaskAttachmentStore {
212
185
  await flush();
213
186
  return removed;
214
187
  }
215
- /** sweep 的引用对账半场:批内 sha 中仍有 meta 行的子集(单 IN 查询;DISTINCT 免重复行膨胀)。 */
216
188
  async referencedShas(shas) {
217
189
  if (shas.length === 0)
218
190
  return new Set();
@@ -220,8 +192,6 @@ export class TiDBTaskAttachmentStore {
220
192
  const [rows] = await this.pool.query(`SELECT DISTINCT sha256 FROM ${TASK_ATTACHMENT_TABLE} WHERE sha256 IN (${ph})`, shas);
221
193
  return new Set(rows.map((r) => String(r.sha256)));
222
194
  }
223
- /** 删前复查(复审 B-F1):只保留「HEAD 仍在 且 LastModified 仍过 grace」的 hash。无 headObject
224
- * 面 ⇒ 返回空集(保守不删);HEAD 抛错 ⇒ 该对象本轮跳过(下轮再收)。 */
225
195
  async filterStillOrphan(hashes, graceMs, nowMs) {
226
196
  if (hashes.length === 0)
227
197
  return [];
@@ -236,7 +206,6 @@ export class TiDBTaskAttachmentStore {
236
206
  out.push(h);
237
207
  }
238
208
  catch {
239
- /* 复查失败=本轮跳过(宁漏勿误删) */
240
209
  }
241
210
  }
242
211
  return out;
@@ -250,7 +219,6 @@ export class PgTaskAttachmentStore {
250
219
  this.bytes = bytes;
251
220
  }
252
221
  async put(rec) {
253
- // bytes 先、meta 后(TiDB 同姿,注同)
254
222
  await this.bytes.putBlob(rec.sha256, rec.content);
255
223
  await this.q(`INSERT INTO ${TASK_ATTACHMENT_TABLE} (${COLS}) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`, [rec.id, rec.owner, rec.name, rec.mime, rec.sha256, rec.sizeBytes, rec.sessionId ?? null, rec.createdAtMs, rec.boundAtMs ?? null]);
256
224
  }
@@ -273,14 +241,12 @@ export class PgTaskAttachmentStore {
273
241
  await this.bytes.deleteBlobs([sha]);
274
242
  }
275
243
  catch {
276
- /* best-effort(TiDB 同注) */
277
244
  }
278
245
  }
279
246
  }
280
247
  async bind(owner, ids, sessionId, boundAtMs) {
281
248
  if (ids.length === 0)
282
249
  return 0;
283
- // PgQueryFn 有意无 rowCount(pg-query.ts 契约)⇒ RETURNING id 数行
284
250
  const res = await this.q(`UPDATE ${TASK_ATTACHMENT_TABLE} SET session_id = $1, bound_at_ms = $2 WHERE owner = $3 AND id = ANY($4::varchar[]) RETURNING id`, [sessionId, boundAtMs, owner, ids]);
285
251
  return res.rows.length;
286
252
  }
@@ -305,14 +271,10 @@ export class PgTaskAttachmentStore {
305
271
  await this.deleteBytesIfUnreferenced(res.rows.map((r) => r.sha256));
306
272
  return res.rows.length;
307
273
  }
308
- /** D-1 孤儿对象彻底 GC —— twin of the TiDB store(判据三层同注:真列举/零引用/过 grace)。 */
309
274
  async sweepOrphanObjects(graceMs, nowMs = Date.now()) {
310
275
  const lister = this.bytes;
311
276
  if (typeof lister.listObjects !== "function")
312
277
  return 0;
313
- // 🔴 复审 F2:**边列举边消费**——grace 判据是「对象年龄」不是「孤儿性」,稳态下整个存量语料
314
- // (凡超过 grace)都会先进候选集;攒满再分批 ⇒ 内存与语料同阶(20 万对象实测堆 Δ58MB,百万级
315
- // ≈290MB,且每 tick 每副本各一份)。改成满 200 就消费,峰值降到 O(200)。
316
278
  let removed = 0;
317
279
  let batch = [];
318
280
  const flush = async () => {
@@ -320,11 +282,6 @@ export class PgTaskAttachmentStore {
320
282
  return;
321
283
  const referenced = await this.referencedShas(batch);
322
284
  const orphans = batch.filter((h) => !referenced.has(h));
323
- // 🔴 复审 B-F1(数据安全):**删前逐对象复查**。grace 只在列举时刻判过一次,而
324
- // 「referencedShas 判零引用 → deleteBlobs」之间落地的同 sha 上传(内容去重 ⇒ 同 key)会让
325
- // meta 行在场而对象被本轮删掉 = 永久丢字节(getContent 恒 null、物化 fail-loud)。put 会 PUT
326
- // 同键刷新 LastModified,所以删前再 HEAD 一次即可识别;窗口压到 HEAD→DELETE 毫秒级。
327
- // 无 headObject 面 ⇒ 保守**不删**(宁留孤儿不误删)。
328
285
  const safe = await this.filterStillOrphan(orphans, graceMs, nowMs);
329
286
  if (safe.length > 0)
330
287
  removed += await this.bytes.deleteBlobs(safe);
@@ -347,8 +304,6 @@ export class PgTaskAttachmentStore {
347
304
  const r = await this.q(`SELECT DISTINCT sha256 FROM ${TASK_ATTACHMENT_TABLE} WHERE sha256 IN (${ph})`, shas);
348
305
  return new Set(r.rows.map((row) => String(row.sha256)));
349
306
  }
350
- /** 删前复查(复审 B-F1):只保留「HEAD 仍在 且 LastModified 仍过 grace」的 hash。无 headObject
351
- * 面 ⇒ 返回空集(保守不删);HEAD 抛错 ⇒ 该对象本轮跳过(下轮再收)。 */
352
307
  async filterStillOrphan(hashes, graceMs, nowMs) {
353
308
  if (hashes.length === 0)
354
309
  return [];
@@ -363,7 +318,6 @@ export class PgTaskAttachmentStore {
363
318
  out.push(h);
364
319
  }
365
320
  catch {
366
- /* 复查失败=本轮跳过(宁漏勿误删) */
367
321
  }
368
322
  }
369
323
  return out;
@@ -1,62 +1,13 @@
1
- /**
2
- * TaskListStore SQL twins(design/151 S3c server pg 半场,[1530] 提货单②)——core 团队共享任务
3
- * 清单(TaskCreate/TaskUpdate/TaskList 工具族)的 TiDB/PG 双方言实现。挂载缝 = S3c 行为车定
4
- * (createTaskListTools(store) 已收 store 参数);本文件先就绪 store 半场。
5
- *
6
- * 语义真源 = core file 实现(dist/stores/file/task-list-store.js,durable 对 durable)+ [1530] 定谳:
7
- * 1. **`mutate` 必须后端事务原子**(接口 JSDoc codex S4 F2 句):read-modify-write 全序列进一个
8
- * 事务;分区锁 = meta 行 `SELECT … FOR UPDATE`(天然 advisory lock,同清单 mutate 串行)。
9
- * 2. **nextId 只增不回退、与分配同事务**(删最高 id 不复用):`task_list_meta` 单行携 `next_id`,
10
- * **永不从存量 MAX 派生**。
11
- * 3. metadata = JSON 列(`assertJsonMetadata` 在 set 面拦,file/memory 同形);id 任意 string
12
- * (工具层走 allocateId 产数字串,接口不排除外部 id)。
13
- *
14
- * `list` 序:两个 core 参照本就不同(memory=Map 纯插入序;file=JS 对象序 = integer-like 键数字
15
- * 升序、其余按插入序殿后)——SQL twin 对齐 **file 形**(durable 参照):`id_num` 物化列(canonical
16
- * array-index 形才非 NULL)数字升序,非数字 id 按 `sort_seq`(插入序)殿后。set 覆盖已有 id 保位
17
- * (sort_seq 不变)= 两参照一致的保位语义。`sort_seq` 从 **meta 行计数器同事务分配**(复审 F6:
18
- * TiDB AUTO_INCREMENT 按 tidb-server 分段缓存,跨节点不单调——「插入序」在多网关部署下会倒序;
19
- * meta 计数器 = allocateId 同姿势,真单调;覆盖写浪费一个号,单调性无损)。
20
- *
21
- * 分区键 `listKey` = 构造参数(file 形的 root 目录对位):谁的清单由挂载点决定(per-team 组装,
22
- * S3c 行为车),store 层只管字节隔离(VARBINARY / COLLATE "C",roster F2 案)。PG unstorable
23
- * bytes([1439]):item 正文(subject/description/metadata)= 内容面 lossy(pgSafeJsonStringify,
24
- * U+FFFD);**身份键 listKey/id 拒绝式**(清洗形变 = 行与查询键错位)。TiDB verbatim。
25
- */
26
1
  import { createHash } from "node:crypto";
27
- // assertJsonMetadata:core 1.374 起包根导出([1531]§一3——私拷镜像即换,DURABLE_AGENT_HANDLE_RE 同姿势)。
28
2
  import { assertJsonMetadata } from "@sema-agent/core";
29
3
  import { pgHasUnstorable, pgSafeJsonStringify, PgUnstorableError } from "./pg-safe-json.js";
30
4
  export const TASK_LIST_META_TABLE = "task_list_meta";
31
5
  export const TASK_LIST_ITEM_TABLE = "task_list_item";
32
- /**
33
- * #318 `sessionId ↦ 本表的身份键`:`session:<可读前缀>-<sha256(sessionId) 前 16 位>`。
34
- *
35
- * 🔴 为什么不直接把 sessionId 当 listKey:sessionId 是**调用方可自报**的不透明串(提交门
36
- * `http/server.ts` 只卡 ≤64 **字符** —— [#15] 刻意的宽松提交契约,不卡字节数也不卡字符集)。
37
- * 而本文件的身份键是字节面严格的:{@link assertListKeyBytes} 对 >190 字节当场拒(TiDB
38
- * `INSERT IGNORE` 静默截断案),PG twin 对孤代理/NUL 走 {@link PgUnstorableError} 拒。直接透传
39
- * 的话,一个 64 字符全四字节 emoji 的会话(256 字节)会让**整条请求**在场景装配处 500 —— 而那条腿
40
- * 只是待办清单。派生一个恒可存的键把这一整类失败模式消掉(既不是 fail-open 也不是 fail-loud,是让
41
- * 那条臂不存在)。
42
- *
43
- * 判据:确定性(同 sessionId 跨副本/跨重启恒同键)、字节安全(纯 ASCII,≤ 8+24+1+16 = 49 字节)、
44
- * 抗碰撞(摘要取**全串**,可读前缀只为运维肉眼对账)。
45
- *
46
- * `session:` 是**命名空间**位:design/147 §6 D2 的团队共享清单将来若按 team 分区,两族键同表不撞。
47
- * 同族先例 = `env-facts.ts` 的 `scratchpadSessionSegment`(文件名面)——刻意**不**复用彼此的实现:
48
- * 一个键空间改了算法不该把另一个键空间的存量行全孤儿化。
49
- *
50
- * 🔴 属主在**本文件**(而不是消费它的 boot 车道):留存腿(`retention-store-sql.ts`)按同一派生式
51
- * 批量删本表的行,两个消费点必须读同一个函数 —— 键派生有两份就是「在线删除删得掉、留存腿删不掉」。
52
- */
53
6
  export function taskListKeyFor(sessionId) {
54
7
  const readable = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 24);
55
8
  const digest = createHash("sha256").update(sessionId).digest("hex").slice(0, 16);
56
9
  return `session:${readable.length > 0 ? readable : "_"}-${digest}`;
57
10
  }
58
- /** JS 对象 integer-like(array-index)键判別——file 参照的遍历序里数字升序的那一族:canonical
59
- * 十进制、0 ≤ n < 2^32-1。"01"/"1e3"/负数/超界 = 普通串键(插入序殿后)。 */
60
11
  function arrayIndexOf(id) {
61
12
  if (!/^(0|[1-9][0-9]*)$/.test(id))
62
13
  return null;
@@ -66,16 +17,12 @@ function arrayIndexOf(id) {
66
17
  function parseItem(v) {
67
18
  return JSON.parse(String(v));
68
19
  }
69
- /** 键长守卫(mailbox twin F5 同案):TiDB `INSERT IGNORE` 对超 190 字节键静默截断——截断 listKey
70
- * 与全长查询参数字节比对恒 miss(mutate 拿不到分区锁 = 无锁运行)。构造点一次拒清。item 的 id
71
- * 列超长走非 IGNORE 语句 = 两方言原生 fail-loud,无需前置守卫。 */
72
20
  function assertListKeyBytes(listKey) {
73
21
  if (Buffer.byteLength(listKey, "utf8") > 190) {
74
22
  throw new Error(`TaskListStore: listKey exceeds the 190-byte key column (refusing — a truncated key would desync from its byte-exact lookups)`);
75
23
  }
76
24
  }
77
25
  export async function ensureTiDBTaskListSchema(pool) {
78
- // next_sort = sort_seq 计数器(F6:不用 AUTO_INCREMENT——TiDB 分段缓存跨节点不单调)。
79
26
  await pool.query(`CREATE TABLE IF NOT EXISTS ${TASK_LIST_META_TABLE} (
80
27
  list_key VARBINARY(190) NOT NULL,
81
28
  next_id BIGINT NOT NULL,
@@ -107,14 +54,11 @@ export async function ensurePgTaskListSchema(q) {
107
54
  PRIMARY KEY (list_key, id)
108
55
  )`);
109
56
  }
110
- /** meta 行 ensure(ensure-first 形:先幂等 INSERT 再 FOR UPDATE——「无行 FOR UPDATE + INSERT」的
111
- * 经典并发死锁/dup 撞车形整个消掉;mutate 已持 meta 行锁时重入 = 同事务无害)。 */
112
57
  async function ensureMeta(exec, dialect, listKey) {
113
58
  await exec(dialect === "tidb"
114
59
  ? `INSERT IGNORE INTO ${TASK_LIST_META_TABLE} (list_key, next_id, next_sort) VALUES (?, 1, 1)`
115
60
  : `INSERT INTO ${TASK_LIST_META_TABLE} (list_key, next_id, next_sort) VALUES ($1, 1, 1) ON CONFLICT (list_key) DO NOTHING`, [listKey]);
116
61
  }
117
- /** 事务内的 nextId 分配(定谳 2:与分配同事务、永不 MAX 派生)。 */
118
62
  async function allocateOn(exec, dialect, listKey) {
119
63
  await ensureMeta(exec, dialect, listKey);
120
64
  const rows = await exec(dialect === "tidb"
@@ -126,8 +70,6 @@ async function allocateOn(exec, dialect, listKey) {
126
70
  : `UPDATE ${TASK_LIST_META_TABLE} SET next_id = $1 WHERE list_key = $2`, [id + 1, listKey]);
127
71
  return String(id);
128
72
  }
129
- /** 事务内的 set(F6:sort_seq 从 meta 计数器分配,与写同事务——meta 行锁顺带把同清单写串行;
130
- * 覆盖写时 ON DUPLICATE/CONFLICT 不动 sort_seq(保位),浪费的号只增不回,单调性无损)。 */
131
73
  async function setOn(exec, dialect, listKey, id, itemJson) {
132
74
  await ensureMeta(exec, dialect, listKey);
133
75
  const rows = await exec(dialect === "tidb"
@@ -143,17 +85,6 @@ async function setOn(exec, dialect, listKey, id, itemJson) {
143
85
  ? `UPDATE ${TASK_LIST_META_TABLE} SET next_sort = ? WHERE list_key = ?`
144
86
  : `UPDATE ${TASK_LIST_META_TABLE} SET next_sort = $1 WHERE list_key = $2`, [sort + 1, listKey]);
145
87
  }
146
- /**
147
- * 整份清单删除(#318 / E21 会话删除级联;codex R1-[high] 验真后加)。
148
- *
149
- * 语义 = 「这个 listKey 的清单**不存在过**」:item 行 + meta 行(高水位 `next_id` / 插入序
150
- * `next_sort`)一并抹掉。⚠️ **meta 行必须一起删**:留着它,同一个 listKey 被重新登记后
151
- * (会话 id 由调用方自选、删除后可被别人 `register`)新主的第一条任务会从旧主的高水位续号 ——
152
- * 那是一条跨化身的信息泄漏(「上一位在这里建过 47 条」)。
153
- *
154
- * 事务内先对 meta 行 `FOR UPDATE`(与 `mutate` 同一把分区锁)⇒ 与并发的清单写串行,不会删到
155
- * 一半被插回新行。meta 行不在(从未写过任何任务)= 无锁可拿,DELETE 命中 0 行,幂等。
156
- */
157
88
  export async function deleteTiDBTaskList(pool, listKey) {
158
89
  assertListKeyBytes(listKey);
159
90
  const c = await pool.getConnection();
@@ -172,7 +103,6 @@ export async function deleteTiDBTaskList(pool, listKey) {
172
103
  c.release();
173
104
  }
174
105
  }
175
- /** {@link deleteTiDBTaskList} 的 PG 孪生(同语义、同事务姿势)。 */
176
106
  export async function deletePgTaskList(pool, listKey) {
177
107
  if (pgHasUnstorable(listKey))
178
108
  throw new PgUnstorableError("task-list listKey");
@@ -215,9 +145,6 @@ export function createTiDBTaskListStore(pool, listKey) {
215
145
  const [rows] = (await q.query(sql, params));
216
146
  return rows;
217
147
  };
218
- // mutate 事务内的读必须**当前读**(FOR UPDATE):TiDB 悲观事务的普通 SELECT 按 start_ts 快照读
219
- // (事务开始时点)——分区锁串行后普通读仍看见开锁前的旧值,read-modify-write 静默丢更新(真双库
220
- // 套件抓获:8 并发 increment 终值 1)。MySQL 的快照点(首次快照读)不同但同修;顶层单语句无此臂。
221
148
  const build = (q, inTx) => ({
222
149
  get: async (id) => {
223
150
  const [rows] = (await q.query(`SELECT item_json FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = ? AND id = ?${inTx ? " FOR UPDATE" : ""}`, [listKey, id]));
@@ -242,7 +169,6 @@ export function createTiDBTaskListStore(pool, listKey) {
242
169
  ? {}
243
170
  : {
244
171
  mutate: (fn) => tx(async (c) => {
245
- // 分区锁(定谳 1):ensure + FOR UPDATE——同 listKey 的并发 mutate 在此串行。
246
172
  await ensureMeta(execOn(c), "tidb", listKey);
247
173
  await c.query(`SELECT next_id FROM ${TASK_LIST_META_TABLE} WHERE list_key = ? FOR UPDATE`, [listKey]);
248
174
  return fn(build(c, true));
@@ -275,17 +201,12 @@ export function createPgTaskListStore(pool, listKey) {
275
201
  const res = await q.query(sql, params);
276
202
  return res.rows;
277
203
  };
278
- // 事务内当前读(TiDB 侧同注——PG READ COMMITTED 语句级快照本已看见锁前提交,FOR UPDATE 统一
279
- // 双方言姿势,顺带把行锁真拿住)。
280
204
  const build = (q, inTx) => ({
281
205
  get: async (id) => {
282
206
  const { rows } = await q.query(`SELECT item_json FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = $1 AND id = $2${inTx ? " FOR UPDATE" : ""}`, [listKey, id]);
283
207
  return rows.length > 0 ? parseItem(rows[0].item_json) : undefined;
284
208
  },
285
209
  set: async (id, item) => {
286
- // 身份/引用键拒绝式(复审 F8):行键 id 之外,blob 内的 item.id/owner(工具层 ifOwnerIs 守卫
287
- // 的比对对象)与 blocks/blockedBy(id 引用)清洗形变会与真实键错位——同落拒绝层;正文
288
- // (subject/description/metadata)维持内容面 lossy。
289
210
  if (pgHasUnstorable(id) ||
290
211
  pgHasUnstorable(item.id) ||
291
212
  (item.owner !== undefined && pgHasUnstorable(item.owner)) ||
@@ -1,7 +1,6 @@
1
1
  import { WriteBehindCounter } from "./write-behind-counter.js";
2
2
  export class TiDBCostQuota {
3
3
  counter;
4
- /** #322:限额=比较参数(行里只有花费);窗长换代经 counter.setWindowMs(桶键语义见那里)。 */
5
4
  limitMicroUsd;
6
5
  refreshRequested = false;
7
6
  refreshIntervalMs;
@@ -23,7 +22,6 @@ export class TiDBCostQuota {
23
22
  this.counter.stop();
24
23
  }
25
24
  check(scopeKey) {
26
- // 关断哨兵:零 counter 访问、零 SQL(恒构造的代价必须是 0)。
27
25
  if (this.limitMicroUsd <= 0)
28
26
  return { allowed: true, usedMicroUsd: 0, limitMicroUsd: 0, retryAfterSec: 0 };
29
27
  const used = this.counter.used(scopeKey);
@@ -32,10 +30,9 @@ export class TiDBCostQuota {
32
30
  }
33
31
  add(scopeKey, micro) {
34
32
  if (this.limitMicroUsd <= 0)
35
- return; // off ⇒ 不攒本地增量(也就没有要 flush 的东西)
33
+ return;
36
34
  this.counter.addLocal(scopeKey, micro);
37
35
  }
38
- // write-behind lifecycle (delegated; exposed for the reaper/tests)
39
36
  flush() {
40
37
  return this.counter.flush();
41
38
  }