@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,12 +1,10 @@
1
1
  import { pgSafeJsonStringify, pgProtocolJsonStringify } from "./pg-safe-json.js";
2
- import { assertMemoryScopeWidth } from "./memory-key-guards.js"; // #271 件1:scope 列宽写前守卫(engine 两表同守)
3
- /** Table names (single source). SAME names as the TiDB twin (the two dialects never share one DB). */
2
+ import { assertMemoryScopeWidth } from "./memory-key-guards.js";
4
3
  export const PG_MEMORY_SYNC_TABLES = {
5
4
  syncCursor: "agent_memory_engine_sync_cursor",
6
5
  pushQueue: "agent_memory_engine_push_queue",
7
6
  history: "agent_memory_engine_history",
8
7
  };
9
- /** Idempotent DDL for the sync plane (PG dialect). Call once at startup. */
10
8
  export async function ensurePgMemorySyncSchema(query) {
11
9
  await query(`CREATE TABLE IF NOT EXISTS ${PG_MEMORY_SYNC_TABLES.syncCursor} (
12
10
  scope varchar(190) COLLATE "C" NOT NULL,
@@ -24,18 +22,13 @@ export async function ensurePgMemorySyncSchema(query) {
24
22
  next_attempt_at_ms bigint NOT NULL,
25
23
  last_error text COLLATE "C" NULL
26
24
  )`);
27
- // Scan face (§1.2): the reaper/consumer sweeps by due time. Perf add-on, never correctness.
28
25
  await query(`CREATE INDEX IF NOT EXISTS idx_${PG_MEMORY_SYNC_TABLES.pushQueue}_next_attempt_ms ON ${PG_MEMORY_SYNC_TABLES.pushQueue} (next_attempt_at_ms)`);
29
26
  }
30
- /** jsonb defense: node-pg hands back parsed objects, but a text-typed round-trip (or a foreign
31
- * writer) may yield a string — same posture as parseJson in run-store-sql.ts. */
32
27
  function fromJsonb(v) {
33
28
  if (typeof v === "string")
34
29
  return JSON.parse(v);
35
30
  return v;
36
31
  }
37
- /** node-pg returns bigint columns as strings (int8 overflows JS numbers in general; ours are epoch
38
- * ms, safely within 2^53) — normalize defensively. */
39
32
  function toMs(v) {
40
33
  return typeof v === "number" ? v : Number(v);
41
34
  }
@@ -50,7 +43,6 @@ export class PgMemorySyncStore {
50
43
  }
51
44
  this.claimLeaseMs = lease;
52
45
  }
53
- // ── cursors ────────────────────────────────────────────────────────────────
54
46
  async getCursor(scope, peer) {
55
47
  const res = await this.query(`SELECT base_revs, updated_at_ms FROM ${PG_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = $1 AND peer = $2`, [scope, peer]);
56
48
  const row = res.rows[0];
@@ -64,44 +56,20 @@ export class PgMemorySyncStore {
64
56
  };
65
57
  }
66
58
  async putCursor(cursor) {
67
- // #271 件1:scope 是 PK 的一半(varchar(190))——超宽此前落不可分类的裸 22001,而 MySQL-protocol
68
- // twin 上同一份数据会**静默截断**成另一个盘的基线。两方言同点、同文案拒(engine 两表同守)。
69
59
  assertMemoryScopeWidth(cursor.scope);
70
- // PK (scope,peer) is the table's ONLY unique key ⇒ ON CONFLICT has exactly one target, no
71
- // hijack face (纪律:the entries-table ban on blind upserts does NOT apply here).
72
60
  await this.query(`INSERT INTO ${PG_MEMORY_SYNC_TABLES.syncCursor} (scope, peer, base_revs, updated_at_ms)
73
61
  VALUES ($1, $2, $3::jsonb, $4)
74
- ON CONFLICT (scope, peer) DO UPDATE SET base_revs = EXCLUDED.base_revs, updated_at_ms = EXCLUDED.updated_at_ms`,
75
- // codex R11:baseRevs=对账基线(entry id→rev,协议数据)——有损改写=peer 收到的与库里存的
76
- // 是两份 map,对账决策对着被改写的身份/rev 做。拒绝式(与 outbox/add/update 同族)。
77
- [cursor.scope, cursor.peer, pgProtocolJsonStringify(cursor.baseRevs, "memory sync cursor baseRevs"), cursor.updatedAtMs]);
62
+ ON CONFLICT (scope, peer) DO UPDATE SET base_revs = EXCLUDED.base_revs, updated_at_ms = EXCLUDED.updated_at_ms`, [cursor.scope, cursor.peer, pgProtocolJsonStringify(cursor.baseRevs, "memory sync cursor baseRevs"), cursor.updatedAtMs]);
78
63
  }
79
- // ── push queue ─────────────────────────────────────────────────────────────
80
64
  async enqueue(item) {
81
- assertMemoryScopeWidth(item.scope); // #271 件1:出站队列行的 scope 截断 = 推给对端时说错了盘
82
- // Plain INSERT, fail-loud on a duplicate id: the caller mints uuidv7 per enqueue, so a dup is a
83
- // caller bug (or a replay the caller must decide about) — never silently absorbed here.
65
+ assertMemoryScopeWidth(item.scope);
84
66
  await this.query(`INSERT INTO ${PG_MEMORY_SYNC_TABLES.pushQueue} (id, scope, entry_id, payload, attempts, next_attempt_at_ms, last_error)
85
- VALUES ($1, $2, $3, $4::jsonb, 0, $5, NULL)`,
86
- // codex R10:payload=协议数据(文档明言 verbatim;rev=内容哈希)——jsonb 列存不了脏值,但有损
87
- // 改写=payload 与 rev 内部矛盾(消费端永拒/应用脏内容)。协议纪律:拒绝式(与 memory add/update
88
- // 的 unstorable_bytes 同族;上游 add/update 已拒,此处=纵深防御)。
89
- [item.id, item.scope, item.entryId, pgProtocolJsonStringify(item.payload, "memory outbox payload"), item.nextAttemptAtMs]);
67
+ VALUES ($1, $2, $3, $4::jsonb, 0, $5, NULL)`, [item.id, item.scope, item.entryId, pgProtocolJsonStringify(item.payload, "memory outbox payload"), item.nextAttemptAtMs]);
90
68
  }
91
69
  async claimDue(nowMs, limit) {
92
70
  if (limit <= 0)
93
71
  return [];
94
72
  const leasedUntilMs = nowMs + this.claimLeaseMs;
95
- // ONE statement = one implicit transaction: SKIP LOCKED excludes claimers overlapping in time,
96
- // the next_attempt_at_ms bump (lease) excludes everyone arriving after we commit. RETURNING gives
97
- // the post-update row, so attempts/last_error are the pre-claim values (fail() is the only
98
- // incrementer) and next_attempt_at_ms is the lease we just wrote.
99
- // ⚠️ MATERIALIZED CTE, NOT `WHERE id IN (SELECT … LIMIT n FOR UPDATE SKIP LOCKED)`: the IN-subquery
100
- // form lets the planner RESCAN the locking subquery (e.g. as a rescanned semi-join inner), and each
101
- // rescan skips the rows the previous pass locked and picks the NEXT due ones — an over-claim beyond
102
- // LIMIT. Plan-dependent ⇒ intermittent: caught live on the build-host test DB (claimDue(limit=2) returned
103
- // 3 rows, serial single-claimer run — the "sync-store env flake" real root cause). AS MATERIALIZED
104
- // (PG12+) pins one evaluation; UPDATE … FROM joins the pinned id set.
105
73
  const res = await this.query(`WITH due AS MATERIALIZED (
106
74
  SELECT id FROM ${PG_MEMORY_SYNC_TABLES.pushQueue}
107
75
  WHERE next_attempt_at_ms <= $1
@@ -126,19 +94,12 @@ export class PgMemorySyncStore {
126
94
  await this.query(`DELETE FROM ${PG_MEMORY_SYNC_TABLES.pushQueue} WHERE id = $1`, [id]);
127
95
  }
128
96
  async fail(id, error, nextAttemptAtMs) {
129
- // attempts = attempts + 1 is SQL-atomic — a read-modify-write in JS would race a concurrent
130
- // failer and lose increments (the attempt cap is the fail-loud stop, it must count honestly).
131
97
  await this.query(`UPDATE ${PG_MEMORY_SYNC_TABLES.pushQueue}
132
98
  SET attempts = attempts + 1, last_error = $2, next_attempt_at_ms = $3
133
99
  WHERE id = $1`, [id, error, nextAttemptAtMs]);
134
100
  }
135
101
  }
136
- /** Idempotent DDL for the history table (PG dialect). Separate from the sync-plane ensure so S5.1
137
- * can wire the write point independently of the S4 sync wiring. */
138
102
  export async function ensurePgMemoryHistorySchema(query) {
139
- // scope column = design-doc amendment (see MemoryHistoryAppendRow.scope). written_at_ms BIGINT
140
- // (caller-injected epoch ms) instead of the draft's TIMESTAMP — the family's clock discipline
141
- // (sync_cursors.updated_at_ms precedent; DB clocks never judge anything).
142
103
  await query(`CREATE TABLE IF NOT EXISTS ${PG_MEMORY_SYNC_TABLES.history} (
143
104
  id char(36) COLLATE "C" NOT NULL,
144
105
  rev varchar(128) COLLATE "C" NOT NULL,
@@ -149,7 +110,6 @@ export async function ensurePgMemoryHistorySchema(query) {
149
110
  written_at_ms bigint NOT NULL,
150
111
  PRIMARY KEY (id, rev)
151
112
  )`);
152
- // countByScope / future per-scope export-with-history scan face. Perf add-on, never correctness.
153
113
  await query(`CREATE INDEX IF NOT EXISTS idx_${PG_MEMORY_SYNC_TABLES.history}_scope ON ${PG_MEMORY_SYNC_TABLES.history} (scope)`);
154
114
  }
155
115
  export class PgMemoryHistoryStore {
@@ -160,9 +120,6 @@ export class PgMemoryHistoryStore {
160
120
  async appendHistory(rows) {
161
121
  if (rows.length === 0)
162
122
  return;
163
- // ONE multi-row INSERT (the batch is "one applyPatches commit's worth" — small). ON CONFLICT
164
- // (id, rev) DO NOTHING = idempotent replay; PK is the table's ONLY unique key, so the conflict
165
- // target is unambiguous (no hijack face — append-only, nothing is ever updated).
166
123
  const params = [];
167
124
  const tuples = rows.map((r, i) => {
168
125
  params.push(r.id, r.rev, r.prevRev ?? null, r.op, r.scope, r.snapshot === undefined || r.snapshot === null ? null : pgSafeJsonStringify(r.snapshot), r.writtenAtMs);
@@ -181,12 +138,10 @@ export class PgMemoryHistoryStore {
181
138
  return res.rows.map((r) => historyRowFrom(r));
182
139
  }
183
140
  async countByScope(scope) {
184
- // COUNT(*) comes back as a bigint-string from node-pg — normalize (toMs posture).
185
141
  const res = await this.query(`SELECT COUNT(*) AS n FROM ${PG_MEMORY_SYNC_TABLES.history} WHERE scope = $1`, [scope]);
186
142
  return toMs(res.rows[0]?.n ?? 0);
187
143
  }
188
144
  }
189
- /** Shared row mapper (both dialects' SELECTs project the same column set). */
190
145
  export function historyRowFrom(r) {
191
146
  return {
192
147
  id: String(r.id),
@@ -1,14 +1,10 @@
1
1
  import { historyRowFrom } from "./memory-sync-store-pg.js";
2
- import { assertMemoryScopeWidth } from "./memory-key-guards.js"; // #271 件1:scope 列宽写前守卫(engine 两表同守)
3
- /** Table names (single source) — SAME names as PG_MEMORY_SYNC_TABLES (the two dialects never share
4
- * one database). */
2
+ import { assertMemoryScopeWidth } from "./memory-key-guards.js";
5
3
  export const TIDB_MEMORY_SYNC_TABLES = {
6
4
  syncCursor: "agent_memory_engine_sync_cursor",
7
5
  pushQueue: "agent_memory_engine_push_queue",
8
6
  history: "agent_memory_engine_history",
9
7
  };
10
- /** Idempotent DDL for the sync plane (TiDB dialect). Call once at startup.
11
- * Key budget: utf8mb4 = 4 bytes/char, index cap 3072 bytes ⇒ PK(scope,peer) = 380 chars = 1520 B ✓. */
12
8
  export async function ensureTiDBMemorySyncSchema(pool) {
13
9
  await pool.query(`CREATE TABLE IF NOT EXISTS ${TIDB_MEMORY_SYNC_TABLES.syncCursor} (
14
10
  scope VARCHAR(190) NOT NULL,
@@ -29,14 +25,11 @@ export async function ensureTiDBMemorySyncSchema(pool) {
29
25
  KEY idx_agent_memory_engine_push_queue_next_attempt_ms (next_attempt_at_ms)
30
26
  ) COLLATE utf8mb4_bin`);
31
27
  }
32
- /** JSON-column defense: mysql2 normally hands back parsed objects for JSON columns, but string
33
- * shapes leak through (connection flags/proxies) — same posture as memory-engine-tidb.ts. */
34
28
  function fromJson(v) {
35
29
  if (typeof v === "string")
36
30
  return JSON.parse(v);
37
31
  return v;
38
32
  }
39
- /** BIGINT columns may arrive as string depending on driver flags — normalize defensively. */
40
33
  function toMs(v) {
41
34
  return typeof v === "number" ? v : Number(v);
42
35
  }
@@ -51,7 +44,6 @@ export class TiDBMemorySyncStore {
51
44
  }
52
45
  this.claimLeaseMs = lease;
53
46
  }
54
- // ── cursors ────────────────────────────────────────────────────────────────
55
47
  async getCursor(scope, peer) {
56
48
  const [rows] = await this.pool.query(`SELECT base_revs, updated_at_ms FROM ${TIDB_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = ? AND peer = ?`, [scope, peer]);
57
49
  const row = rows[0];
@@ -65,20 +57,13 @@ export class TiDBMemorySyncStore {
65
57
  };
66
58
  }
67
59
  async putCursor(cursor) {
68
- // #271 件1:scope 是 PK 的一半(VARCHAR(190))——非严格 MySQL 的静默截断会把两个盘的同步基线
69
- // 折叠成一行(对账基线互串),写前响亮拒(engine 两表同守;Pg twin 同注)。
70
60
  assertMemoryScopeWidth(cursor.scope);
71
- // The table's ONLY unique key is PK(scope,peer) ⇒ ON DUPLICATE KEY UPDATE has exactly one
72
- // possible trigger — no hijack face (the entries-table ban does NOT apply; see header).
73
61
  await this.pool.query(`INSERT INTO ${TIDB_MEMORY_SYNC_TABLES.syncCursor} (scope, peer, base_revs, updated_at_ms)
74
62
  VALUES (?, ?, ?, ?)
75
63
  ON DUPLICATE KEY UPDATE base_revs = VALUES(base_revs), updated_at_ms = VALUES(updated_at_ms)`, [cursor.scope, cursor.peer, JSON.stringify(cursor.baseRevs), cursor.updatedAtMs]);
76
64
  }
77
- // ── push queue ─────────────────────────────────────────────────────────────
78
65
  async enqueue(item) {
79
- assertMemoryScopeWidth(item.scope); // #271 件1:出站队列行的 scope 截断 = 推给对端时说错了盘
80
- // Plain INSERT, fail-loud on a duplicate id (ER_DUP_ENTRY 1062) — caller mints uuidv7 per
81
- // enqueue, a dup is a caller bug, never silently absorbed. Same posture as the PG twin.
66
+ assertMemoryScopeWidth(item.scope);
82
67
  await this.pool.query(`INSERT INTO ${TIDB_MEMORY_SYNC_TABLES.pushQueue} (id, scope, entry_id, payload, attempts, next_attempt_at_ms, last_error)
83
68
  VALUES (?, ?, ?, ?, 0, ?, NULL)`, [item.id, item.scope, item.entryId, JSON.stringify(item.payload), item.nextAttemptAtMs]);
84
69
  }
@@ -86,10 +71,6 @@ export class TiDBMemorySyncStore {
86
71
  if (limit <= 0)
87
72
  return [];
88
73
  const leasedUntilMs = nowMs + this.claimLeaseMs;
89
- // Optimistic CAS claim (header rationale): read the due candidates, then win each row with a
90
- // conditional UPDATE on the exact next_attempt_at_ms we read. Losers (another worker claimed, or
91
- // fail()/a re-schedule moved the row) see affectedRows=0 and are skipped — the SKIP LOCKED
92
- // equivalent, without pessimistic locks or a held connection.
93
74
  const [candidates] = await this.pool.query(`SELECT id, scope, entry_id, payload, attempts, next_attempt_at_ms, last_error
94
75
  FROM ${TIDB_MEMORY_SYNC_TABLES.pushQueue}
95
76
  WHERE next_attempt_at_ms <= ?
@@ -97,13 +78,11 @@ export class TiDBMemorySyncStore {
97
78
  LIMIT ?`, [nowMs, limit]);
98
79
  const claimed = [];
99
80
  for (const row of candidates) {
100
- // affectedRows=CHANGED 语义坑防御:lease = nowMs + claimLeaseMs > nowMs ≥ 读到的 next_attempt_at_ms
101
- // (WHERE 已保证),新值严格大于旧值 ⇒ 匹配必 change ⇒ affectedRows=1 恒等于「真的赢了」。
102
81
  const [res] = await this.pool.query(`UPDATE ${TIDB_MEMORY_SYNC_TABLES.pushQueue}
103
82
  SET next_attempt_at_ms = ?
104
83
  WHERE id = ? AND next_attempt_at_ms = ?`, [leasedUntilMs, row.id, toMs(row.next_attempt_at_ms)]);
105
84
  if (res.affectedRows !== 1)
106
- continue; // lost the race — skip, never block
85
+ continue;
107
86
  claimed.push({
108
87
  id: row.id,
109
88
  scope: row.scope,
@@ -120,20 +99,12 @@ export class TiDBMemorySyncStore {
120
99
  await this.pool.query(`DELETE FROM ${TIDB_MEMORY_SYNC_TABLES.pushQueue} WHERE id = ?`, [id]);
121
100
  }
122
101
  async fail(id, error, nextAttemptAtMs) {
123
- // attempts = attempts + 1 is SQL-atomic (never read-modify-write in JS); the increment also
124
- // guarantees the row CHANGES — the claim verdict is honest under BOTH affectedRows semantics
125
- // (mysql2 default = FOUND_ROWS/matched; changed-only would also work here).
126
102
  await this.pool.query(`UPDATE ${TIDB_MEMORY_SYNC_TABLES.pushQueue}
127
103
  SET attempts = attempts + 1, last_error = ?, next_attempt_at_ms = ?
128
104
  WHERE id = ?`, [error, nextAttemptAtMs, id]);
129
105
  }
130
106
  }
131
- // ── 142-S5 §1.3 — history table (tombstone + rev chain), TiDB dialect ───────────────────────────
132
- /** Idempotent DDL for the history table (TiDB dialect). Key budget: PK(id,rev) = CHAR(36)+
133
- * VARCHAR(128) = 164 chars ≤ 3072-byte utf8mb4 index cap ✓; KEY(scope) = 190 chars ✓. */
134
107
  export async function ensureTiDBMemoryHistorySchema(pool) {
135
- // scope column + written_at_ms BIGINT = the same design-doc amendments as the Pg twin (see
136
- // MemoryHistoryAppendRow.scope / ensurePgMemoryHistorySchema).
137
108
  await pool.query(`CREATE TABLE IF NOT EXISTS ${TIDB_MEMORY_SYNC_TABLES.history} (
138
109
  id CHAR(36) NOT NULL,
139
110
  rev VARCHAR(128) NOT NULL,
@@ -154,9 +125,6 @@ export class TiDBMemoryHistoryStore {
154
125
  async appendHistory(rows) {
155
126
  if (rows.length === 0)
156
127
  return;
157
- // ONE multi-row INSERT; dup (id,rev) replay = no-op via `ON DUPLICATE KEY UPDATE id = id`
158
- // (header rationale: NOT INSERT IGNORE — IGNORE swallows non-dup errors too). PK(id,rev) is the
159
- // only unique key ⇒ the ODKU trigger is unambiguous, and the "update" writes nothing.
160
128
  const params = [];
161
129
  for (const r of rows) {
162
130
  params.push(r.id, r.rev, r.prevRev ?? null, r.op, r.scope, r.snapshot === undefined || r.snapshot === null ? null : JSON.stringify(r.snapshot), r.writtenAtMs);
@@ -1,41 +1,5 @@
1
- /**
2
- * Outcome ledger (design/73 §7) — the read-only first step of grounded self-evolution. SINGLE-FILE
3
- * DUAL-DIALECT (design/158 A12 定型半场): ONE implementation, TWO dialects; the historical
4
- * `TiDBOutcomeLedger` / `PgOutcomeLedger` class names survive as thin ctor subclasses so every consumer
5
- * (store-backend.ts, pg-pool.ts, the unit + real-DB suites) is untouched.
6
- *
7
- * One row per graded task run, keyed by (taskSignature, model). v1 is STRICTLY READ-ONLY: it records
8
- * outcomes for a dashboard + as the dataset for an offline backtest (bandit vs fixed routing). It drives
9
- * NO automatic policy change — that is flag-gated and must first pass the offline-backtest gate
10
- * (council blocker 3 / major 7).
11
- *
12
- * 🔴 Signal-hardness split is the load-bearing invariant (council blocker 1): only `mechanical` fields
13
- * (oracle pass/total, build exits, mergeOk, cost, turns, route, replan) may ever drive an automatic
14
- * policy change. `llmAssisted` (stuck counts, verifier verdict+confidence) is dashboard-only, carried in
15
- * its own blob, never co-weighted with mechanical. `outcome` is DERIVED from mechanical fields ONLY.
16
- *
17
- * ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
18
- * - `?` placeholders vs `$n` - JSON columns bound `CAST(? AS JSON)`-free (TiDB
19
- * stores the string verbatim) vs `$n::jsonb`
20
- * - JSON encode: TiDB `JSON.stringify` verbatim vs PG `pgSafeJsonStringify` (deep NUL/lone-surrogate
21
- * sanitize — a raw tool_result/core blob can carry bytes PG's text/jsonb columns reject, ERRCODE 22P05)
22
- * - affected/SELECT rows via `result.rowCount` / `result.rows` (not RowDataPacket/ResultSetHeader) — via SqlDriver
23
- * - BIGINT (cost_micro_usd) read back as a STRING by node-pg → wrapped in `Number(...)`
24
- * - `AVG(outcome = 'pass')` (MySQL boolean→1/0) vs `AVG((outcome = 'pass')::int)` (PG has no boolean→int coercion)
25
- * - merge_ok TINYINT(1) vs SMALLINT (0/1 in/out) - DATETIME(3) vs TIMESTAMPTZ(3) (Date in/out)
26
- * - schema ownership: TiDB DDL lives in tidb-pool.ts SCHEMA_STATEMENTS; the PG DDL is exported from HERE
27
- * (`PG_OUTCOME_LEDGER_SCHEMA` + `ensurePgOutcomeLedgerSchema`) for pg-pool.ts + the integration test.
28
- *
29
- * Kept in lock-step across dialects by the shared real-DB integration suite
30
- * (test/pg-outcome-ledger-integration.test.ts, both engines run the SAME scenarios in parallel).
31
- */
32
1
  import { randomUUID, createHash } from "node:crypto";
33
2
  import { mysqlDriver, pgDriver, dialectJsonEncoder } from "./sql-driver.js";
34
- /**
35
- * v1 task-signature (council Q11, recommended candidate (a)): a deterministic structural hash — zero
36
- * embedder, coarse on purpose (routing is a coarse decision). An explicit `tag` short-circuits to the
37
- * tag itself (candidate (c)).
38
- */
39
3
  export function taskSignature(s) {
40
4
  if (s.tag)
41
5
  return `tag:${s.tag}`;
@@ -47,14 +11,7 @@ export function taskSignature(s) {
47
11
  });
48
12
  return `h:${createHash("sha256").update(canon).digest("hex").slice(0, 16)}`;
49
13
  }
50
- /**
51
- * Derive the coarse outcome from MECHANICAL fields only (council blocker 1: llmAssisted never participates).
52
- * pass = all hidden tests green AND both build exits 0 AND merge ok; fail = nothing graded green; else partial.
53
- */
54
14
  export function deriveOutcome(m, runStatus = "completed-graded") {
55
- // An infra-aborted run never reached the grader — its 0/total is infra, not task quality. Don't let it
56
- // read as "fail" (which a self-scheduler would mistake for task difficulty); mark it "aborted" so the
57
- // backtest can exclude it (council blocker 3).
58
15
  if (runStatus === "infra-aborted")
59
16
  return "aborted";
60
17
  const oracleGreen = m.oracleTotal != null && m.oracleTotal > 0 && m.oraclePassed === m.oracleTotal;
@@ -62,24 +19,9 @@ export function deriveOutcome(m, runStatus = "completed-graded") {
62
19
  const mergeGreen = m.mergeOk !== false;
63
20
  if (oracleGreen && buildGreen && mergeGreen)
64
21
  return "pass";
65
- // "partial" needs POSITIVE green evidence. An OMITTED mergeOk is non-evidence — counting it green (the
66
- // pre-2026-07 behavior) made "fail" unreachable for any row without an explicit mergeOk:false, so a
67
- // fully-red oracle read as "partial" (caught wiring the design/73 §1 bridge, whose rows carry no mergeOk).
68
22
  const anyGreen = oracleGreen || (m.oraclePassed != null && m.oraclePassed > 0) || m.mergeOk === true;
69
23
  return anyGreen ? "partial" : "fail";
70
24
  }
71
- /**
72
- * design/73 §1 → §7 bridge: project one CORE `TaskOutcome` fact (the `RunnerDeps.onTaskOutcome` seam,
73
- * core 1.226) onto a v1 ledger row. Lossless: the verbatim fact rides `core` → the `core_outcome` JSON
74
- * column (red-line ② `oracleHadRedRun` + the free-form oracle blob have no v1 columns); the mapped
75
- * mechanical fields keep the existing aggregate surface working. Mapping rules:
76
- * - `signatureInputs.tag = o.taskSignature` — core already emits THE aggregation key, so the ledger's
77
- * tag short-circuit adopts it verbatim (no re-derivation drift);
78
- * - oracle counts: `oracle.oraclePassed/oracleTotal` when the harness supplied numeric counts, else the
79
- * required `green` bit as 1/0 over 1 (an honest degenerate count, not a fabrication);
80
- * - `runStatus` stays "completed-graded": core's red line ③ means emission only happens at a REAL
81
- * mechanical-oracle terminal (an infra-aborted run never reaches the emit point).
82
- */
83
25
  export function coreOutcomeToLedgerRow(o) {
84
26
  const passed = typeof o.oracle?.oraclePassed === "number" ? o.oracle.oraclePassed : o.oracle?.green ? 1 : 0;
85
27
  const total = typeof o.oracle?.oracleTotal === "number" ? o.oracle.oracleTotal : 1;
@@ -87,8 +29,6 @@ export function coreOutcomeToLedgerRow(o) {
87
29
  taskId: o.runId.slice(0, 64),
88
30
  model: (o.model ?? "unknown").slice(0, 190),
89
31
  signatureInputs: {
90
- // 🔴 `taskSignature()` prepends "tag:" → the final task_signature must fit VARCHAR(190).
91
- // Cap the raw tag at 190 − len("tag:") = 186 so the prefixed key never overflows/truncates the column.
92
32
  tag: o.taskSignature.slice(0, 186),
93
33
  ...(o.signatureInputs?.toolset ? { toolset: o.signatureInputs.toolset } : {}),
94
34
  ...(o.signatureInputs?.targetLang ? { targetLang: o.signatureInputs.targetLang } : {}),
@@ -103,14 +43,6 @@ export function coreOutcomeToLedgerRow(o) {
103
43
  core: o,
104
44
  };
105
45
  }
106
- /**
107
- * PG translation of the `outcome_ledger` DDL in tidb-pool.ts SCHEMA_STATEMENTS:
108
- * JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), TINYINT(1)→SMALLINT, INT→INTEGER, inline KEY→separate CREATE INDEX.
109
- * The table is DISJOINT from every other store's tables, so this self-contained set is safe (no shadowing).
110
- *
111
- * SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
112
- * drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead.
113
- */
114
46
  export const PG_OUTCOME_LEDGER_SCHEMA = [
115
47
  `CREATE TABLE IF NOT EXISTS outcome_ledger (
116
48
  id VARCHAR(64) COLLATE "C" NOT NULL,
@@ -142,44 +74,31 @@ export const PG_OUTCOME_LEDGER_SCHEMA = [
142
74
  `CREATE INDEX IF NOT EXISTS idx_outcome_ledger_outcome ON outcome_ledger (outcome)`,
143
75
  `CREATE INDEX IF NOT EXISTS idx_outcome_ledger_created ON outcome_ledger (created_at)`,
144
76
  ];
145
- /** Idempotent self-contained PG schema apply (for the integration test; central aggregation via pg-pool.ts). */
146
77
  export async function ensurePgOutcomeLedgerSchema(pool) {
147
78
  for (const stmt of PG_OUTCOME_LEDGER_SCHEMA)
148
79
  await pool.query(stmt);
149
80
  }
150
- /** Dual-dialect outcome ledger. See the file header for the dialect-delta ledger. */
151
81
  export class SqlOutcomeLedger {
152
82
  db;
153
83
  constructor(db) {
154
84
  this.db = db;
155
85
  }
156
- /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
157
86
  q(tidb, pg) {
158
87
  return this.db.dialect === "tidb" ? tidb : pg;
159
88
  }
160
- /** JSON column encode: TiDB stores `JSON.stringify` verbatim; PG runs the deep NUL/lone-surrogate
161
- * sanitize (a raw tool_result/core blob can carry bytes PG's jsonb column rejects, ERRCODE 22P05) — this
162
- * store's blobs are observability/dashboard data, so the lossy (U+FFFD) sanitize is the right tier
163
- * (not the lossless envelope checkpoint-store-sql.ts uses for the approval trust boundary). */
164
89
  json(value) {
165
90
  return dialectJsonEncoder(this.db.dialect)(value);
166
91
  }
167
- /** Record one graded task run. Idempotent only by row id; callers pass distinct runs. */
168
92
  async record(o) {
169
93
  const id = randomUUID().replace(/-/g, "").slice(0, 32);
170
94
  const sig = taskSignature(o.signatureInputs);
171
95
  const m = o.mechanical;
172
96
  const runStatus = o.runStatus ?? "completed-graded";
173
- await this.db.query(this.q(
174
- // 🔴 byte-for-byte preserved from tidb-outcome-ledger.ts (pre-collapse) — the parity harness
175
- // diffs this literally, so its indentation is NOT reflowed to the new call-site nesting.
176
- `INSERT INTO outcome_ledger
97
+ await this.db.query(this.q(`INSERT INTO outcome_ledger
177
98
  (id, task_signature, model, task_id, run_status, route, worker_count, oracle_passed, oracle_total,
178
99
  assemble_exit, lint_exit, merge_ok, replan_triggers, cost_micro_usd, turns, outcome,
179
100
  llm_assisted, signature_inputs, core_outcome, created_at)
180
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
181
- // 🔴 byte-for-byte preserved from pg-outcome-ledger.ts (pre-collapse).
182
- `INSERT INTO outcome_ledger
101
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, `INSERT INTO outcome_ledger
183
102
  (id, task_signature, model, task_id, run_status, route, worker_count, oracle_passed, oracle_total,
184
103
  assemble_exit, lint_exit, merge_ok, replan_triggers, cost_micro_usd, turns, outcome,
185
104
  llm_assisted, signature_inputs, core_outcome, created_at)
@@ -194,16 +113,9 @@ export class SqlOutcomeLedger {
194
113
  ]);
195
114
  return id;
196
115
  }
197
- /** design/73 §1 seam consumption: record one CORE outcome fact (see {@link coreOutcomeToLedgerRow}). */
198
116
  async recordCore(o) {
199
117
  await this.record(coreOutcomeToLedgerRow(o));
200
118
  }
201
- /**
202
- * Read-only aggregate for the dashboard / offline backtest: pass-rate + mean cost per (signature, model).
203
- * 🔴 Defaults to `completed-graded` rows ONLY — the route/budget-learning signal must exclude infra-aborted
204
- * runs (council blocker 3: a basework death is not a task-difficulty signal). Pass
205
- * `includeAborted: true` only for the infra-health view, never for training.
206
- */
207
119
  async summary(opts = {}) {
208
120
  const clauses = [];
209
121
  const params = [];
@@ -211,14 +123,10 @@ export class SqlOutcomeLedger {
211
123
  clauses.push("run_status = 'completed-graded'");
212
124
  if (opts.taskSignature) {
213
125
  params.push(opts.taskSignature);
214
- // 🔴 placeholder ARITY differs, not just the token: TiDB's `?` is positional-anonymous (always just
215
- // "?"), while PG's `$n` must name the actual ordinal — `params.length` after the push IS that ordinal.
216
126
  clauses.push(this.q("task_signature = ?", `task_signature = $${params.length}`));
217
127
  }
218
128
  const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
219
- const { rows } = await this.db.query(this.q(
220
- // 🔴 byte-for-byte preserved (both statements) — not reflowed to the new call-site nesting.
221
- `SELECT task_signature, model, COUNT(*) n,
129
+ const { rows } = await this.db.query(this.q(`SELECT task_signature, model, COUNT(*) n,
222
130
  AVG(outcome = 'pass') pass_rate, AVG(cost_micro_usd) mean_cost
223
131
  FROM outcome_ledger ${where}
224
132
  GROUP BY task_signature, model
@@ -236,13 +144,11 @@ export class SqlOutcomeLedger {
236
144
  }));
237
145
  }
238
146
  }
239
- /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
240
147
  export class TiDBOutcomeLedger extends SqlOutcomeLedger {
241
148
  constructor(pool) {
242
149
  super(mysqlDriver(pool));
243
150
  }
244
151
  }
245
- /** PostgreSQL binding — historical class name + ctor shape preserved. */
246
152
  export class PgOutcomeLedger extends SqlOutcomeLedger {
247
153
  constructor(pool) {
248
154
  super(pgDriver(pool));