@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,65 +1,14 @@
1
- /**
2
- * Durable **leader-run** registry — SINGLE-FILE DUAL-DIALECT (design/158 A12 定型半场;先例 =
3
- * `image-bake-store-sql.ts`)。#193 车4 件1(台账 P1-9;设计稿 `docs/DESIGN-193-CAR4-leader-failclosed.md`)。
4
- *
5
- * ── 为什么这张表必须存在 ────────────────────────────────────────────────────────────────────────────
6
- * 一次 leader run = 规划成 N 个子任务 → 起 N 个 worker 沙箱 → 真模型编码 → 合并 → **真 git push**,
7
- * 最长 24h。它此前的登记表是 `leader/endpoint.ts` 里的一只进程内 `Map`,于是三条缺口同源:
8
- * ① 跨副本 GET 404(提交打到 A、轮询打到 B),违反仓级前提「durable state lives in an external SQL store」;
9
- * ② POST 零幂等 —— 每次都铸新 uuidv7,一次网络重试 = 第二次真跑 + 可能第二次 push;
10
- * ③ `fanout.ts` 的 `claim` 面全链路无人传,而它的头注称并发去重「已解决」。
11
- * 一张带 `idem_key UNIQUE` 的表一次解掉三条:①靠行本身,②靠 UNIQUE 的「原子赢或观察」,
12
- * ③ 的真属主就是 ② —— 提交幂等挡在**准入**那一刻,比在 worker 层再补一道 CAS 早得多也准得多
13
- * (worker 的 sessionId 是每次现铸的 `leader-<workerId>-<Date.now()>`,拿它做 CAS 键**永远不会**冲突,
14
- * 那道 claim 由构造即无效;详见同批对 `fanout.ts` 头注的更正)。
15
- *
16
- * ── 这是一张 DEDICATED 表,不是 `task_run` 的复用 ────────────────────────────────────────────────────
17
- * 与 bake 店同一条理由:`task_run` 与 `task_active`/run reaper/`GET /v1/runs` 列表 FK 式耦合,把 leader run
18
- * (无 session、无 spend 账、无 checkpoint)混进去 = 让非任务行漏进 reapStale/listRuns 的类别错误。
19
- *
20
- * ── 两条保留/清算裁定(分开的两件事,别混)────────────────────────────────────────────────────────
21
- * **① 行的保留:不建 TTL 腿。** 进程内 Map 时代那条 1h 的 `RUN_RETENTION_MS` 驱逐,目的是**给进程内存
22
- * 封顶**,不是保留策略。落库后这个理由消失:一行 leader run 对应一次「N 个沙箱 + 数分钟到 24h + 一次真
23
- * push」的动作,它就是这次动作的审计记录;行的产生速率与它的价值都不支持自动清账。要清账时按
24
- * workflow_run/roster 的先例加一个 `*_RETENTION_MS` 旋钮 + reaper 腿,那是独立一件。
25
- *
26
- * **② 陈旧 `running` 的清算:建了**({@link SqlLeaderRunStore.reapStaleRunning},旋钮 `LEADER_RUN_STALE_MS`,
27
- * 腿在 `boot/reapers.ts`)。这条与 ① 无关 —— 它不是为了控表大小,而是因为准入与驱动是两步、驱动腿只活在
28
- * 受理它的那个进程里:副本一死,行就永远停在 `running`,跨副本读面永远显示「在跑」而没有任何东西在跑。
29
- * 那是**静默**,不是保留问题。(本裁定是 codex 对抗复审 R1-high② 逼出来的:初版把两件事合成一件、一起
30
- * 判了「不建」,而第二件的论据完全不同。)
31
- *
32
- * ── 方言差异台账(显式,永不藏进抽象;A12 判据)────────────────────────────────────────────────────
33
- * - `?` 占位符 vs `$n` - `CAST(? AS JSON)` vs `$n::jsonb`
34
- * - 表级 `COLLATE utf8mb4_bin` vs 逐列 `COLLATE "C"`(PG 无表级形;`isolation-key-collation` 门执法)
35
- * - dup-key 判别走 `sql-errors.ts` 单一属主(`isDupKeyError`),本店只有一根 UNIQUE ⇒ 无需归属判别
36
- * - schema 属主:MySQL DDL 由本文件导出、展开进 `tidb-pool.ts` 的 SCHEMA_STATEMENTS(跟着中央
37
- * named-lock 的那条 conn 建);PG DDL 由本文件的 `ensurePgLeaderRunSchema` 导出、由 `pg-pool.ts`
38
- * 的中央 `ensurePgSchema` 组合(与 approval-ask 同姿势)。
39
- */
40
1
  import { uuidv7 } from "@sema-agent/core";
41
2
  import { createHash } from "node:crypto";
42
3
  import { isDupKeyError } from "./sql-errors.js";
43
4
  import { mysqlDriver, pgDriver, dialectJsonEncoder } from "./sql-driver.js";
44
5
  import { parseJsonOr } from "./sql-row-helpers.js";
45
- /** 表名(#192 单数纪律;`schema-naming-invariants` 的闭集词表执法)。 */
46
6
  export const LEADER_RUN_TABLE = "leader_run";
47
7
  const LEADER_RUN_STATUSES = new Set(["running", "completed", "failed", "needs_human"]);
48
- /** `objective_preview` 的字符上限,与 DDL 的 VARCHAR(160) 同源(`task_run.objective_preview` 先例同宽)。 */
49
8
  export const LEADER_OBJECTIVE_PREVIEW_CHARS = 160;
50
- /**
51
- * 幂等键的**落库形** = `sha256(scope \x1f raw)` 的 hex(64 字符,定宽)。
52
- *
53
- * 为什么 hash 而不是像 bake 店那样存 scope 过的明文:明文形的列宽(VARCHAR(255))与「principal(≤190)
54
- * + 调用方自选的 raw 键(≤255)」的上界对不上 —— 溢出时 PG 报 `value too long`、非严格 MySQL **静默截断**,
55
- * 而截断一个去重键的后果是**两个不同的请求折叠成同一次 leader run**(= 一次该跑的 run 被静默吞掉)。
56
- * 定宽 hash 让这条路径按构造不存在;顺带,库里也不再留调用方的原始键字面量。
57
- * (碰撞面:sha256,不在工程风险量级。)
58
- */
59
9
  export function leaderIdemKey(raw, owner) {
60
10
  return createHash("sha256").update(`${owner ?? ""}\x1f${raw}`).digest("hex");
61
11
  }
62
- /** MySQL 协议方言的建表语句(真源;由 `tidb-pool.ts` 展开进中央 SCHEMA_STATEMENTS)。 */
63
12
  export const TIDB_LEADER_RUN_STATEMENTS = [
64
13
  `CREATE TABLE IF NOT EXISTS ${LEADER_RUN_TABLE} (
65
14
  leader_run_id VARCHAR(64) NOT NULL,
@@ -83,7 +32,6 @@ export const TIDB_LEADER_RUN_STATEMENTS = [
83
32
  KEY idx_leader_run_started (started_at_ms)
84
33
  ) COLLATE utf8mb4_bin`,
85
34
  ];
86
- /** PG 方言的建表语句(MySQL 孪生的逐条翻译:JSON→JSONB,逐列 `COLLATE "C"`,内联 KEY→独立 CREATE INDEX)。 */
87
35
  export const PG_LEADER_RUN_SCHEMA = [
88
36
  `CREATE TABLE IF NOT EXISTS ${LEADER_RUN_TABLE} (
89
37
  leader_run_id VARCHAR(64) COLLATE "C" NOT NULL,
@@ -101,16 +49,13 @@ export const PG_LEADER_RUN_SCHEMA = [
101
49
  `CREATE INDEX IF NOT EXISTS idx_leader_run_owner ON ${LEADER_RUN_TABLE} (owner)`,
102
50
  `CREATE INDEX IF NOT EXISTS idx_leader_run_started ON ${LEADER_RUN_TABLE} (started_at_ms)`,
103
51
  ];
104
- /** 幂等的 PG schema 应用(中央 `ensurePgSchema` 组合它;集成套件也直接调)。 */
105
52
  export async function ensurePgLeaderRunSchema(q) {
106
53
  for (const stmt of PG_LEADER_RUN_SCHEMA)
107
54
  await q(stmt);
108
55
  }
109
- /** 清算腿写进 `error` 列的 WHY(消费方读到的就是这句;不是一个空的 failed)。 */
110
56
  export const STALE_SWEEP_REASON = "leader run went stale: no replica settled it within LEADER_RUN_STALE_MS (the replica driving it most likely died). " +
111
57
  "The run was NOT taken over — re-submit with a NEW Idempotency-Key to run it again.";
112
58
  const SELECT_COLS = "leader_run_id, owner, objective_preview, status, result, error, started_at_ms, finished_at_ms";
113
- /** 未知 status 字面量 = 库里躺着一条本进程不认识的行 ⇒ **响亮拒**,不静默折成 failed(闭集词表纪律 #157)。 */
114
59
  function statusOf(raw, id) {
115
60
  const s = String(raw);
116
61
  if (!LEADER_RUN_STATUSES.has(s)) {
@@ -134,24 +79,14 @@ function mapRow(r) {
134
79
  ...(finished === undefined ? {} : { finishedAtMs: finished }),
135
80
  };
136
81
  }
137
- /** 双方言 leader-run 店。方言差异台账见文件头注。 */
138
82
  export class SqlLeaderRunStore {
139
83
  db;
140
84
  constructor(db) {
141
85
  this.db = db;
142
86
  }
143
- /** 挑本方言的 SQL 文本。两条语句**都写在调用点**(A12 判据:读者一眼看到两份)。 */
144
87
  q(tidb, pg) {
145
88
  return this.db.dialect === "tidb" ? tidb : pg;
146
89
  }
147
- /**
148
- * 准入一次 leader run(状态 `running`)。提交幂等照 `createBake`/`createRun` 的**原子赢或观察**:
149
- * `idem_key` 是 UNIQUE,重投的那一方 INSERT 撞 dup-key ⇒ 回读**既有行**返回 `{created:false}`,
150
- * 调用方据此**不再**起第二条后台腿。`idemKey=null` 恒插入(没要求去重)。
151
- *
152
- * 🔴 为什么不是「先 SELECT 再 INSERT」:两个副本的 check-then-act 会双双看到「没有」然后双双插入 ——
153
- * 而这条路径的第二次插入意味着**第二次真 git push**。UNIQUE 是唯一能在两个进程之间成立的判据。
154
- */
155
90
  async createRun(input) {
156
91
  const id = uuidv7();
157
92
  const now = Date.now();
@@ -163,8 +98,6 @@ export class SqlLeaderRunStore {
163
98
  catch (err) {
164
99
  if (isDupKeyError(this.db.dialect, err) && input.idemKey) {
165
100
  const existing = await this.getByIdem(input.idemKey);
166
- // 回读为空 = 赢家的行在这两步之间消失了(本店无删除口 ⇒ 只可能是外部 DELETE)。原样上抛而不是
167
- // 悄悄再插一次:后者会在「运维正在手工清行」的窗口里放出第二次真 push。
168
101
  if (existing)
169
102
  return { created: false, run: existing };
170
103
  }
@@ -175,69 +108,35 @@ export class SqlLeaderRunStore {
175
108
  throw new Error(`createRun: leader_run ${id} vanished right after insert`);
176
109
  return { created: true, run };
177
110
  }
178
- /** 按 id 读一行(跨副本可见性的全部依据)。不存在 ⇒ undefined。 */
179
111
  async getRun(id) {
180
112
  const res = await this.db.query(this.q(`SELECT ${SELECT_COLS} FROM ${LEADER_RUN_TABLE} WHERE leader_run_id = ?`, `SELECT ${SELECT_COLS} FROM ${LEADER_RUN_TABLE} WHERE leader_run_id = $1`), [id]);
181
113
  const row = res.rows[0];
182
114
  return row ? mapRow(row) : undefined;
183
115
  }
184
- /** 按幂等键读回既有行(dup-key 观察臂;也给集成套件直查)。 */
185
116
  async getByIdem(idemKey) {
186
117
  const res = await this.db.query(this.q(`SELECT ${SELECT_COLS} FROM ${LEADER_RUN_TABLE} WHERE idem_key = ?`, `SELECT ${SELECT_COLS} FROM ${LEADER_RUN_TABLE} WHERE idem_key = $1`), [idemKey]);
187
118
  const row = res.rows[0];
188
119
  return row ? mapRow(row) : undefined;
189
120
  }
190
- /**
191
- * 陈旧 `running` 清算(codex 对抗复审 R1-high②,验真后修)。
192
- *
193
- * 病:准入(INSERT)与驱动(后台腿)是**两步**,而驱动腿只活在受理它的那个进程里。副本在这两步之间
194
- * 挂掉、或在一条 24h 的 run 中途挂掉,行就永远停在 `running`:跨副本读面永远显示「在跑」,而没有任何
195
- * 东西在跑;更坏的是那把幂等键被永久绑在这条无人驱动的行上,重投拿回的还是它。
196
- *
197
- * 处置 = **有界地说实话**,不是自动接管:把超期的 `running` 迁成 `failed` 并写清 WHY。
198
- * 🔴 刻意**不做**跨副本接管(claim/lease/queued 状态机):接管一条产物是「真 git push」的 24h run
199
- * 是一个独立的设计决定(要持久化完整可恢复请求、要 fencing、要想清楚两个副本同时以为自己是属主时
200
- * 谁去 push),不是一次清算腿能顺手带过的。本腿把「永远 running」这个**静默**面消灭掉,把接管留给
201
- * 一件具名的后续设计;当前语义写在这里,不藏着。
202
- *
203
- * 幂等键的后果同批写明:键**永久绑定**它当初准入的那条 run(与 `/v1/runs` 的 taskId 幂等同语义)。
204
- * 一条被清算成 `failed` 的 run,用**同一个键**重投拿回的仍是那条 failed —— 调用方要重跑就换一个新键。
205
- *
206
- * @param staleMs 超过这个时长仍是 `running` 就算陈旧(部署旋钮 `LEADER_RUN_STALE_MS`)。
207
- * @param now 注入时钟(测试要确定性)。
208
- * @returns 本次迁走的行数。
209
- */
210
121
  async reapStaleRunning(staleMs, now) {
211
122
  const cutoff = now - staleMs;
212
123
  const res = await this.db.query(this.q(`UPDATE ${LEADER_RUN_TABLE} SET status = 'failed', error = ?, finished_at_ms = ? WHERE status = 'running' AND started_at_ms < ?`, `UPDATE ${LEADER_RUN_TABLE} SET status = 'failed', error = $1, finished_at_ms = $2 WHERE status = 'running' AND started_at_ms < $3`), [STALE_SWEEP_REASON, now, cutoff]);
213
124
  return res.affected;
214
125
  }
215
- /**
216
- * TEST-ONLY:把一行的 `started_at_ms` 推到过去,好让 {@link reapStaleRunning} 的判据在一次测试里成立。
217
- * 生产零调用(`started_at_ms` 只在准入时写一次)——具名 `ForTest` 是为了让这件事在 grep 里一眼可见,
218
- * 而不是长成一个看起来像正经写口的方法(本仓 `setStatus` 那条欠账的教训)。
219
- */
220
126
  async backdateStartedAtForTest(id, startedAtMs) {
221
127
  await this.db.query(this.q(`UPDATE ${LEADER_RUN_TABLE} SET started_at_ms = ? WHERE leader_run_id = ?`, `UPDATE ${LEADER_RUN_TABLE} SET started_at_ms = $1 WHERE leader_run_id = $2`), [startedAtMs, id]);
222
128
  }
223
- /**
224
- * 落终局。**只从 `running` 迁**(`WHERE status = 'running'`):终局是一次性的,一条已经结算过的 run
225
- * 不该被后来的写者改写。回执 = 是否真的迁了,调用方据此决定要不要出声(静默丢一次终局写 = 用户永远
226
- * 看到 `running`)。
227
- */
228
129
  async finishRun(id, patch) {
229
130
  const json = dialectJsonEncoder(this.db.dialect);
230
131
  const res = await this.db.query(this.q(`UPDATE ${LEADER_RUN_TABLE} SET status = ?, result = CAST(? AS JSON), error = ?, finished_at_ms = ? WHERE leader_run_id = ? AND status = 'running'`, `UPDATE ${LEADER_RUN_TABLE} SET status = $1, result = $2::jsonb, error = $3, finished_at_ms = $4 WHERE leader_run_id = $5 AND status = 'running'`), [patch.status, patch.result === undefined ? null : json(patch.result), patch.error ?? null, Date.now(), id]);
231
132
  return res.affected > 0;
232
133
  }
233
134
  }
234
- /** MySQL-协议孪生(TiDB / MySQL / MariaDB 同一只 mysql2 池)。 */
235
135
  export class TiDBLeaderRunStore extends SqlLeaderRunStore {
236
136
  constructor(pool) {
237
137
  super(mysqlDriver(pool));
238
138
  }
239
139
  }
240
- /** PostgreSQL 孪生。 */
241
140
  export class PgLeaderRunStore extends SqlLeaderRunStore {
242
141
  constructor(pool) {
243
142
  super(pgDriver(pool));
@@ -1,70 +1,20 @@
1
- /**
2
- * LOCAL (file-backed) checkpoint store — EnterPlanMode / durable HITL for the TOC local lane.
3
- *
4
- * Shape: core's `FileCheckpointStore` (crash-safe ledger+snapshot, per-token CAS — the ENGINE contract) is the
5
- * inner authority; this wrapper adds the SERVICE half the SQL twins carry (operator queue / session lookups /
6
- * resume ctx / reapers) WITHOUT re-implementing any core abstraction ([[core-service-boundary]]: engine contract
7
- * stays core's; wire/ops glue is ours).
8
- *
9
- * Enumeration seam: core's File store exposes only `listByScope(scope)` (pending-only summaries) + `get(token)`.
10
- * Cross-scope queries (fleet listPending / reapExpired / session lookups) therefore ride a service-side SCOPES
11
- * REGISTRY: `put` records each scope once into `checkpoint-scopes.json` (registered BEFORE the inner put so a
12
- * crash can't leave an unenumerable pending row; a scope whose rows are gone is harmless — its listByScope is
13
- * simply empty). Projection fields come from `get(token)` (the full Checkpoint), so listPending serves the same
14
- * non-secret card as the SQL twins (shared `boundedToolInput` bound, same severity-DESC sort).
15
- *
16
- * Honest degrades (documented, single-user local lane):
17
- * - `deleteBySession` EXPIRES the session's pending gates + unlinks its ctx file — the ledger/snapshot keeps
18
- * terminal history like the local tool-result/transcript stores do (no physical row delete primitive in
19
- * core's File store; the E21 functional contract — "a deleted session has no resumable future" — holds).
20
- * - `taskId` on the pending card comes from an injected `getActiveTaskId` (the local FileRunStore) — `null`
21
- * when not wired, which every consumer already tolerates.
22
- */
23
1
  import { createHash } from "node:crypto";
24
2
  import { existsSync, readFileSync, readdirSync, renameSync, unlinkSync, mkdirSync } from "node:fs";
25
3
  import { join } from "node:path";
26
- import { isApprovalGateKind } from "../tool-approval.js"; // A-002.1 单一属主
4
+ import { isApprovalGateKind } from "../tool-approval.js";
27
5
  import { FileCheckpointStore, atomicWriteFile, sanitizePathComponent, } from "@sema-agent/core";
28
6
  import { boundedRuleSuggestions, boundedToolInput, TERMINAL_BACKSTOP_MS, TERMINAL_GRACE_MS } from "./checkpoint-store-sql.js";
29
7
  import { UNMANAGED_RETENTION } from "./retention-store-sql.js";
30
- /** design/80 D-D read-time twin of the TiDB put-time `terminal_at_ms` column (same formula — anti-drift). */
31
8
  function terminalAtOf(cp) {
32
9
  return Math.max(cp.createdAt + TERMINAL_BACKSTOP_MS, (cp.deadline ?? 0) + TERMINAL_GRACE_MS);
33
10
  }
34
- /** Collision-proof ctx filename: readable prefix + content hash of the FULL sessionId (sanitize can collide). */
35
11
  function ctxFileOf(sessionId) {
36
12
  return `${sanitizePathComponent(sessionId).slice(0, 48)}-${createHash("sha256").update(sessionId).digest("hex").slice(0, 12)}.json`;
37
13
  }
38
14
  export class LocalCheckpointStore {
39
- /** #270 车1:托管留存声明 —— **诚实的 none**(与 local session 店同一条判据:本店的行没有按期删除的腿;
40
- * 留存三方法只实现在 SQL 聚合店上)。locked policy + 本店 ⇒ core 的门拒启,那是契约的正确行为。 */
41
15
  retention = UNMANAGED_RETENTION;
42
- /**
43
- * `CheckpointStore.durability` 声明(#167 欠账,#168 件5)——**如实按内核判**:本类的内核是 core 的
44
- * `FileCheckpointStore`(crash-safe ledger + snapshot,自身声明 `"durable"`),本地 scope 注册表也落
45
- * 在同一个盘上目录里 ⇒ 进程重启后 park 全部还在、还赎得回,`"durable"` 是诚实读数而不是抬举。
46
- *
47
- * 它与「session store 的 local 形声明 durable 就拒启」不矛盾:那条判据管的是**声明 vs 介质**,而这里
48
- * 的介质就是盘。真正的 process-local 形(`InMemoryCheckpointStore`)由 core 自己声明 `"process-local"`。
49
- */
50
16
  durability = "durable";
51
- /**
52
- * `CheckpointStore.fidelity` 声明(core 5.17.0 [3052] 提货批 #172)——**如实按内核判**,与 durability
53
- * 同一条判据:本类的读写内核是 core 的 `FileCheckpointStore`,它自己声明 `"json"`(盘上是 JSON 行),
54
- * 包装层只加 scope/session 索引与 ctx 附属文件,不改这条 round-trip 的宽度 ⇒ 抄内核的读数是诚实的。
55
- *
56
- * ⚠️ 包装类**不继承**被包装者的声明(本类是委派不是子类),所以这一格必须自己写。它与内核那一格
57
- * 是否仍然一致由测试盯着(`wiring-governance-operator.test.ts` 的 #172 组直接读内核那一格对表)——
58
- * 内核哪天改宽度,包装层的声明会被那条钉当场揪出来,而不是靠人记得同步。
59
- */
60
17
  fidelity = "json";
61
- /**
62
- * `CheckpointStore.redecision` 声明(core 5.22.0 F-012 L2/L3;[3342] clay 裁放行)——**如实按内核判**,
63
- * 与 durability/fidelity 同一条判据:内核 core `FileCheckpointStore` **自己**声明 `{ reopen: true }`,
64
- * 本类的 {@link LocalCheckpointStore.reopen} 纯委派给它 ⇒ 抄内核的读数是诚实的。包装类**不继承**被
65
- * 包装者的声明(委派不是子类),所以这一格必须自己写。`validatingLease` 不声明(内核也没声明)。
66
- * 翻向钉:`wiring-governance-operator.test.ts`(修前恰红,与本声明同 commit)。
67
- */
68
18
  redecision = { reopen: true };
69
19
  inner;
70
20
  scopesPath;
@@ -72,14 +22,6 @@ export class LocalCheckpointStore {
72
22
  ctxDir;
73
23
  tmpDir;
74
24
  scopes = new Set();
75
- /** [868] sessionId → every token ever put for it — the service-side index that stands in for the SQL twins'
76
- * `WHERE checkpoint.session_id = ?` on NON-pending rows (core's File store enumerates pending only via
77
- * listByScope; get(token) is the sole read for resolved/expired rows, so cross-status session queries need
78
- * this registry). Registered BEFORE the inner put (same crash direction as the scopes registry: a stale
79
- * index entry is a harmless empty lookup; an unindexed EXPIRED row would silently disable the
80
- * failSuspendedWithExpiredCheckpoint leg — the bad direction). Persisted so a restart keeps the
81
- * suspend→crash→expire-later lifecycle visible. Entries are never dropped while the inner row exists
82
- * (a `resolved` row can be reopen()ed back to pending — pruning it would be wrong). */
83
25
  sessionTokens = new Map();
84
26
  opts;
85
27
  constructor(root, opts = {}) {
@@ -100,18 +42,11 @@ export class LocalCheckpointStore {
100
42
  this.scopes.add(s);
101
43
  }
102
44
  catch (e) {
103
- // C4(clay 裁 2026-07-26,C 先行 + B 根治):此前这里静默吞 ⇒ 存量待决审批整体从枚举面隐身,
104
- // 操作员看到的是**空列表而不是错误**,没人会对空列表排障。处置两层:
105
- // ①fail-visible(C):warn 大声 + 坏文件改名隔离留证(也防下一次 put 的整写把证据盖掉);
106
- // ②重建(B,core 1.424 `listScopes?` seam,[1800]② 索 → [1803] 交付):constructor 是同步的,
107
- // 重建挂在读路径懒执行一次(见 ensureScopesRebuilt)—— 从数据本体枚举 pending scope 集,
108
- // 枚举面完整恢复,不再依赖「下次 suspend 自愈」只救新件。
109
45
  const quarantine = `${this.scopesPath}.corrupt-${Date.now()}`;
110
46
  try {
111
47
  renameSync(this.scopesPath, quarantine);
112
48
  }
113
49
  catch {
114
- /* 移不动就留原地 —— warn 里已给路径,人工可查 */
115
50
  }
116
51
  this.scopeRegistryCorrupt = true;
117
52
  console.warn(`LocalCheckpointStore: checkpoint-scopes.json is corrupt (${e instanceof Error ? e.message : String(e)}) — ` +
@@ -131,23 +66,17 @@ export class LocalCheckpointStore {
131
66
  }
132
67
  }
133
68
  catch {
134
- /* a corrupt index only narrows hasExpiredBySession (fail-safe: the reaper skips, cancel still works) */
135
69
  }
136
70
  }
137
71
  }
138
- /** Persist the session→tokens index (whole-file atomic replace — puts are per-suspend, low frequency). */
139
72
  persistSessionTokens() {
140
73
  atomicWriteFile(this.tmpDir, this.sessionTokensPath, JSON.stringify(Object.fromEntries([...this.sessionTokens].map(([sid, set]) => [sid, [...set]]))));
141
74
  }
142
- // ─────────── core CheckpointStore contract — delegated to core's File store (the engine authority) ───────────
143
75
  async put(token, cp) {
144
- // Register the scope BEFORE the inner commit: a crash between the two leaves a stale scope entry
145
- // (harmless empty enumeration), never an unenumerable pending checkpoint (the bad direction).
146
76
  if (!this.scopes.has(cp.scope)) {
147
77
  this.scopes.add(cp.scope);
148
78
  atomicWriteFile(this.tmpDir, this.scopesPath, JSON.stringify([...this.scopes]));
149
79
  }
150
- // [868] session→token index, same before-the-inner-put crash rationale (see the field doc).
151
80
  if (!this.sessionTokens.get(cp.sessionId)?.has(token)) {
152
81
  const set = this.sessionTokens.get(cp.sessionId) ?? new Set();
153
82
  set.add(token);
@@ -165,10 +94,6 @@ export class LocalCheckpointStore {
165
94
  reopen(token, scope, reason) {
166
95
  return this.inner.reopen(token, scope, reason);
167
96
  }
168
- /** core 5.14.0(#147):入参是 `PendingSteerInput`(text/trusted + 可选 actor/inputId/priority),不是
169
- * 两键单座 —— 形参窄成 `{text,trusted}` 会**静默丢掉**署名与幂等键(TS 的方法参数是双变的,窄形不
170
- * 报错,只是把三个可选键从类型上抹掉,调用点再也传不进来)。队列语义与边界全在被包的 core
171
- * `FileCheckpointStore` 里,本 wrapper 只做转发。 */
172
97
  setPendingSteer(token, scope, steer) {
173
98
  return this.inner.setPendingSteer(token, scope, steer);
174
99
  }
@@ -184,17 +109,13 @@ export class LocalCheckpointStore {
184
109
  close() {
185
110
  this.inner.close();
186
111
  }
187
- // ─────────── service extensions (the SQL twins' operator/ops half) ───────────
188
- /** C4-B:注册表损坏时从**数据本体**懒重建 scope 集(core 1.424 `listScopes?` = 「有 ≥1 条 pending 的
189
- * distinct scope 集」,与 listByScope 可见面对齐 —— 恰好是本注册表要的内容)。一次性:重建后回写
190
- * sidecar,恢复正常路径。seam 缺席(第三方旧 inner)⇒ 保持 C 案的可见降级,不装恢复了。 */
191
112
  scopeRegistryCorrupt = false;
192
113
  async ensureScopesRebuilt() {
193
114
  if (!this.scopeRegistryCorrupt)
194
115
  return;
195
116
  const listScopes = this.inner.listScopes;
196
117
  if (typeof listScopes !== "function")
197
- return; // 无 seam:维持已 warn 的降级
118
+ return;
198
119
  const rebuilt = await listScopes.call(this.inner);
199
120
  for (const s of rebuilt)
200
121
  this.scopes.add(s);
@@ -202,7 +123,6 @@ export class LocalCheckpointStore {
202
123
  atomicWriteFile(this.tmpDir, this.scopesPath, JSON.stringify([...this.scopes]));
203
124
  console.warn(`LocalCheckpointStore: scope registry REBUILT from the data plane (${rebuilt.length} scope(s) with pending gates) — enumeration restored`);
204
125
  }
205
- /** All still-pending checkpoints across the registered scopes (summary + full row via get). */
206
126
  async pendings(scope) {
207
127
  await this.ensureScopesRebuilt();
208
128
  const out = [];
@@ -216,7 +136,6 @@ export class LocalCheckpointStore {
216
136
  }
217
137
  return out;
218
138
  }
219
- /** Operator approval queue — the same NON-secret card as the SQL twins (never the token). */
220
139
  async listPending(scope) {
221
140
  const rows = [];
222
141
  for (const { cp } of await this.pendings(scope)) {
@@ -228,32 +147,20 @@ export class LocalCheckpointStore {
228
147
  scope: cp.scope,
229
148
  toolName: pa?.toolName ?? null,
230
149
  toolCallId,
231
- // [1995]②: the D-1 binding fields are OMITTED when the park has no tool action (plan_review /
232
- // resource_limit / task_done) — the wire contract says an absent nullable field is ABSENT, not `null`
233
- // (a consumer branching on "key present ⇒ there is a binding" would echo `null` back into /decide).
234
150
  ...(toolCallId !== null ? { boundCallId: toolCallId } : {}),
235
151
  ...(pa?.boundInputHash !== undefined ? { boundInputHash: pa.boundInputHash } : {}),
236
- // [1995]③ additive: which gate this row is parked on, so a durable-recovery consumer can route
237
- // plan / tool / resource off the SAME queue (toolName is null for every non-tool gate).
238
152
  ...(gate?.kind !== undefined ? { gateKind: gate.kind } : {}),
239
153
  taskId: (await this.opts.getActiveTaskId?.(cp.sessionId)) ?? null,
240
154
  input: boundedToolInput(pa?.args),
241
155
  createdAt: cp.createdAt,
242
156
  deadline: cp.deadline ?? null,
243
157
  riskDescriptor: gate?.riskDescriptor ?? null,
244
- // [3684]②:与 SQL 孪生**同一个** `boundedRuleSuggestions`(窄读+脱敏+截基数)。本地形没有列,
245
- // 直接从 blob 上的 `pendingAction` 取——投影结果必须与两条 SQL 腿逐字同形,否则 local 档的运维
246
- // 队列会给出一份别人没有的卡面。无供给 ⇒ 省键(绝不 null/[])。
247
- // 🔴 这里**连计数一起**保留(不同于 SQL 腿的回读形 `{ countRedactions: false }` —— 那条腿脱敏
248
- // **照跑**,关掉的只是计数):本地形每次读的是**未脱敏的原始 blob**,这一遍是它唯一的一次脱敏,
249
- // 不是重扫已脱敏文本 ⇒ `redactions_applied_total` 记的是真命中,该计。
250
158
  ...(() => {
251
159
  const rs = boundedRuleSuggestions(pa?.ruleSuggestions);
252
160
  return rs !== undefined ? { ruleSuggestions: rs } : {};
253
161
  })(),
254
162
  });
255
163
  }
256
- // Same triage order as the SQL twins: severity DESC, then oldest-first within a tier.
257
164
  return rows.sort((a, b) => (b.riskDescriptor?.severity ?? 0) - (a.riskDescriptor?.severity ?? 0) || a.createdAt - b.createdAt);
258
165
  }
259
166
  async findPendingTokenBySession(sessionId, scope) {
@@ -262,14 +169,6 @@ export class LocalCheckpointStore {
262
169
  return token;
263
170
  return null;
264
171
  }
265
- /** [868] read-only probe face: does this session have an EXPIRED checkpoint row? The local twin of the SQL
266
- * `EXISTS (SELECT 1 FROM checkpoint ce WHERE ce.session_id = ? AND ce.status = 'expired')` that drives the
267
- * run stores' failSuspendedWithExpiredCheckpoint. Scans the session→token index (get(token) is the only
268
- * cross-status read core's File store offers); an index entry whose inner row vanished is pruned lazily.
269
- * Backed by a persisted registry, so it stays honest — never a guessed "true".
270
- * ⚠️ 存量限制(codex M2,如实):索引随 put 增量建立,无升级迁移——本版本部署前已存在的 checkpoint 不在
271
- * 索引里,其 expired 对本探针不可见(core File store 的 listByScope 是 pending-only,无法枚举历史行回填)。
272
- * 旧存量锁死的恢复把手 = cancel verb([868]①,不依赖本索引);增量行全覆盖。 */
273
172
  async hasExpiredBySession(sessionId) {
274
173
  const tokens = this.sessionTokens.get(sessionId);
275
174
  if (!tokens)
@@ -278,7 +177,7 @@ export class LocalCheckpointStore {
278
177
  for (const t of [...tokens]) {
279
178
  const cp = await this.inner.get(t);
280
179
  if (!cp) {
281
- tokens.delete(t); // inner row gone (e.g. a wiped data plane) — the index entry is stale
180
+ tokens.delete(t);
282
181
  pruned = true;
283
182
  continue;
284
183
  }
@@ -292,24 +191,16 @@ export class LocalCheckpointStore {
292
191
  this.persistSessionTokens();
293
192
  }
294
193
  catch {
295
- /* best-effort — a stale on-disk entry is re-pruned on the next scan */
296
194
  }
297
195
  }
298
196
  return false;
299
197
  }
300
- /** `undefined` = no pending row; else the owner scope (local scopes are always strings — never null). */
301
198
  async peekPendingScope(sessionId) {
302
199
  for (const { cp } of await this.pendings())
303
200
  if (cp.sessionId === sessionId)
304
201
  return cp.scope;
305
202
  return undefined;
306
203
  }
307
- /**
308
- * design/80 D-D global expiry sweep — same kind-split as the TiDB twin: the deadline branch abort-expires
309
- * every kind EXCEPT a human/irreversible_ask approval gate (those get the graceful resolve-DENY via
310
- * listExpiredApprovalGates), with the AskUserQuestion carve-back (denying a question is incoherent → expire);
311
- * the terminal_at_ms branch (derived read-time via the SAME formula the TiDB put stamps) abort-expires ANY kind.
312
- */
313
204
  async reapExpired(cutoff) {
314
205
  let n = 0;
315
206
  for (const { token, cp } of await this.pendings()) {
@@ -324,7 +215,6 @@ export class LocalCheckpointStore {
324
215
  }
325
216
  return n;
326
217
  }
327
- /** design/80 D-D SLA deny-sweep input — pending human/irreversible_ask gates past deadline (excl. AskUserQuestion). */
328
218
  async listExpiredApprovalGates(cutoff, limit = 100) {
329
219
  const hits = [];
330
220
  for (const { cp } of await this.pendings()) {
@@ -342,7 +232,6 @@ export class LocalCheckpointStore {
342
232
  .slice(0, limit)
343
233
  .map(({ sessionId, scope }) => ({ sessionId, scope }));
344
234
  }
345
- // ─────────── checkpoint ctx (resume rebuild inputs; sessionId-keyed, one JSON file per session) ───────────
346
235
  async putCtx(sessionId, ctx) {
347
236
  const rec = { sessionId, updatedAt: Date.now(), ctx };
348
237
  atomicWriteFile(this.tmpDir, join(this.ctxDir, ctxFileOf(sessionId)), JSON.stringify(rec));
@@ -353,13 +242,12 @@ export class LocalCheckpointStore {
353
242
  return null;
354
243
  try {
355
244
  const rec = JSON.parse(readFileSync(p, "utf8"));
356
- return rec.sessionId === sessionId ? (rec.ctx ?? null) : null; // hash-collision paranoia: verify the embedded id
245
+ return rec.sessionId === sessionId ? (rec.ctx ?? null) : null;
357
246
  }
358
247
  catch {
359
- return null; // a torn/corrupt ctx reads as absent — the resume 409s "resume context missing" (fail-loud upstream)
248
+ return null;
360
249
  }
361
250
  }
362
- /** GC ctx files whose lifecycle is over — same three guards as the SQL twins (stale ∧ no pending ∧ no active run). */
363
251
  async reapCtx(cutoff) {
364
252
  let n = 0;
365
253
  for (const f of readdirSync(this.ctxDir)) {
@@ -371,7 +259,7 @@ export class LocalCheckpointStore {
371
259
  rec = JSON.parse(readFileSync(p, "utf8"));
372
260
  }
373
261
  catch {
374
- continue; // unreadable ⇒ leave for inspection (never a blind unlink)
262
+ continue;
375
263
  }
376
264
  if (rec.updatedAt >= cutoff)
377
265
  continue;
@@ -384,27 +272,20 @@ export class LocalCheckpointStore {
384
272
  n++;
385
273
  }
386
274
  catch {
387
- /* racing unlink is fine */
388
275
  }
389
276
  }
390
277
  return n;
391
278
  }
392
- /**
393
- * E21 session purge — functional contract: a deleted session has no resumable future. Expires every pending
394
- * gate of the session (all scopes) + unlinks its ctx file. Owner guard mirrors the SQL EXISTS guard: with an
395
- * `ownerOf` source the owner must null-safe-match; without one, only an ownerless call (owner=null) purges.
396
- * Returns the number of gates expired. (Terminal ledger history stays on disk — local transcripts posture.)
397
- */
398
279
  async deleteBySession(sessionId, owner) {
399
280
  if (this.opts.ownerOf) {
400
281
  const actual = await this.opts.ownerOf(sessionId);
401
282
  if (actual === undefined)
402
- return 0; // no session row ⇒ EXISTS guard fails ⇒ purge nothing
283
+ return 0;
403
284
  if ((actual ?? null) !== (owner ?? null))
404
285
  return 0;
405
286
  }
406
287
  else if (owner !== null) {
407
- return 0; // no owner source to verify a non-null owner against — fail-closed
288
+ return 0;
408
289
  }
409
290
  let n = 0;
410
291
  for (const { token, cp } of await this.pendings()) {
@@ -417,7 +298,6 @@ export class LocalCheckpointStore {
417
298
  unlinkSync(p);
418
299
  }
419
300
  catch {
420
- /* best-effort ctx unlink */
421
301
  }
422
302
  return n;
423
303
  }