@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,17 +1,3 @@
1
- /**
2
- * design/158 A10:composition root 分段 —— 持久层装配(StoreBackend / memory engine / roster /
3
- * background-agent / attachment / mailbox / memory sync / session store + 姿势断言 / breaker)。
4
- *
5
- * 纯搬运:函数体逐字来自 `main.ts`(原 627-932 行),缩进不变;新增的只有 import 与包壳。
6
- *
7
- * ⚠️ **位置即契约**:
8
- * 1. 本段**就地改写 `config.sessionBackend`**(`auto` → `tidb|memory`,local backend 再归一为 `tidb`)。
9
- * 这两行是后续所有消费点(createSessionStore / ensureChildSessionDurable 的 tidb 门 / /health 标签)
10
- * 的前置条件,必须留在本段这个位置,不能上移(backend 还没开)也不能下移(消费点已读)。
11
- * 2. `assertCloudSnapshotBlobPosture` 与两条 REQUIRE_PRINCIPAL 拒启断言必须在 sessionStore 造出来之后、
12
- * 任何路由装配之前 —— fail-loud 拒启的意义就在于"还没开始服务"。
13
- * 3. `breakerState` 放在段尾:brain(下一段)要它,而它要 backend。
14
- */
15
1
  import { join } from "node:path";
16
2
  import { FileBackgroundAgentStore, FileMailboxStore, FileRosterStore, FileUsageWindowStore, InMemoryUsageWindowStore } from "@sema-agent/core";
17
3
  import { createMemorySyncRunner, createMemorySyncTransport } from "../memory-sync-client.js";
@@ -34,10 +20,9 @@ import { ensurePgTaskListSchema, ensureTiDBTaskListSchema } from "../plugins/tas
34
20
  import { assertCloudSnapshotBlobPosture, openStoreBackendWithFallback } from "../plugins/store-backend.js";
35
21
  import { buildMemoryRemoteLaneWarn, memoryEngineBackendFor, memoryEngineRemoteLanePosture } from "../memory-scope.js";
36
22
  import { assertToolResultProvenanceSchema } from "../plugins/tool-result-store-sql.js";
23
+ import { assertPermissionRuleApprovalSchema } from "../plugins/permission-rule-store-sql.js";
37
24
  import { buildMemoryWriteBoundaryAudit, effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
38
25
  import { buildMemoryPosture } from "../memory-posture.js";
39
- /** #252:dark 三词 → 启动日志的人话(词表是机器面的真源,散文是它的**投影**,不是第二个真源)。
40
- * 穷举 `Record<MemoryDarkReason, …>`:词表加员 ⇒ 这里编译红,不会静默漏一句人话。 */
41
26
  const DARK_REASON_PROSE = {
42
27
  engine_off: () => "MEMORY_ENGINE=off",
43
28
  multi_tenant_file_backend: () => "multi-tenant on the FILE backend (no tenant isolation; set MEMORY_ENGINE_BACKEND=pg|tidb to light up)",
@@ -45,32 +30,17 @@ const DARK_REASON_PROSE = {
45
30
  };
46
31
  export async function openStores(ctx) {
47
32
  const { config, logger, metrics, localRoot } = ctx;
48
- // One shared SQL store backend for L1 + L2 (TiDB/MySQL or PostgreSQL per DB_BACKEND). Owned here.
49
33
  let backend;
50
- let storeBackendDegraded = false; // S5: auto-probe degraded this replica to in-memory (surfaced via gauge + /health)
51
- // `auto` probes a configured DB and degrades to in-memory if it's unreachable (local/intranet binaries:
52
- // "use my DB if I can reach it, else memory"). Explicit `tidb` fails fast instead — silently dropping
53
- // persistence the operator asked for would lose runs. (`tidb` here = "the SQL DB"; DB_BACKEND picks the engine.)
34
+ let storeBackendDegraded = false;
54
35
  const wantDb = config.sessionBackend === "tidb" ||
55
- config.dbBackend === "local" || // local (clay 2026-06-25): always build the DB-less in-memory/file StoreBackend
36
+ config.dbBackend === "local" ||
56
37
  (config.sessionBackend === "auto" && !!(config.tidb || config.pg));
57
38
  if (wantDb) {
58
- // 构造 + ensureSchema 同罩一层降级臂(2026-07-28 修):原先 try 只罩 ensureSchema,而 local 形真正
59
- // 会抛的是构造里的 mkdir + 数据根 BootLock ——「裸 boot 不得拒启」那条口径对 local 从未生效过。
60
- // 降级/fail-loud 的判据与理由见 openStoreBackendWithFallback 顶注。
61
39
  const opened = await openStoreBackendWithFallback(config, logger);
62
40
  backend = opened.backend;
63
41
  storeBackendDegraded = opened.degraded;
64
42
  }
65
- // S5 review LOW-1: always render the series (0 = healthy) — gauge absence is indistinguishable from
66
- // "old build without this metric", which breaks `== 0`-style alert rules.
67
43
  metrics.setGauge("store_backend_degraded", storeBackendDegraded ? 1 : 0);
68
- // #119 升级前置断言(core 5.26.0 提货):`CREATE TABLE IF NOT EXISTS` 对**存量** tool_result 是空操作 ⇒
69
- // 没删表就升上来的部署,出处两列与加宽的 ref 列都不在,而每一次 offload 写都会撞 unknown column、被
70
- // core 的失败臂吞成一条内联占位(服务照跑、工具产物全丢)。判据与恢复动作见
71
- // assertToolResultProvenanceSchema 的头注。
72
- // 🔴 位置:**不在** `ensureSchema` 里 —— 那条通道的契约是「只发 CREATE」(test/ensure-schema.test.ts 的
73
- // 运行时门),一条只读探针进去也算破例。放在 DDL 之后、任何路由装配之前,拒启的意义仍在(还没开始服务)。
74
44
  {
75
45
  const mysqlPool = backend?.mysqlPool?.();
76
46
  const pgPool = backend?.pgPool?.();
@@ -78,9 +48,13 @@ export async function openStores(ctx) {
78
48
  await assertToolResultProvenanceSchema(async (sql) => ({ rows: (await pgPool.query(sql)).rows }), "pg");
79
49
  else if (mysqlPool)
80
50
  await assertToolResultProvenanceSchema(async (sql) => ({ rows: (await mysqlPool.query(sql))[0] }), "tidb");
51
+ if (config.permissionRulesEnabled) {
52
+ if (pgPool)
53
+ await assertPermissionRuleApprovalSchema(async (sql) => ({ rows: (await pgPool.query(sql)).rows }), "pg");
54
+ else if (mysqlPool)
55
+ await assertPermissionRuleApprovalSchema(async (sql) => ({ rows: (await mysqlPool.query(sql))[0] }), "tidb");
56
+ }
81
57
  }
82
- // S21 (SILENT-FALLBACK P1): MINIO_* partially set silently falls back to SQL-blob snapshots. Warn on the
83
- // partial config and always render which backend blobs actually use.
84
58
  {
85
59
  const minioReq = ["MINIO_ENDPOINT", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY"];
86
60
  const present = minioReq.filter((v) => (process.env[v] ?? "") !== "");
@@ -89,36 +63,15 @@ export async function openStores(ctx) {
89
63
  }
90
64
  metrics.setGauge("snapshot_blob_backend", 1, { backend: config.snapshotBlobStore ? "minio" : "sql" });
91
65
  }
92
- // Resolve `auto` to the concrete backend the rest of the wiring understands (createSessionStore,
93
- // runStore, the startup summary). A DB-backed L2 with no backend degrades to in-memory the same way.
94
66
  if (config.sessionBackend === "auto")
95
67
  config.sessionBackend = backend ? "tidb" : "memory";
96
- // local backend: route the session store through backend.session() (LocalSessionStore — the §0.5 OwnerAware twin),
97
- // NOT the bare TtlSessionStore the "memory" path returns; "tidb" here means "the backend's durable store" generically.
98
68
  if (config.dbBackend === "local" && backend)
99
69
  config.sessionBackend = "tidb";
100
- // Long-term memory (design/138 S1, clay 2026-07-08): the injection-first file-based memory ENGINE is the
101
- // only memory plane — `RunnerDeps.memoryBackend` is core's switch (materialize → session file ops →
102
- // harvest; no remember/recall tools). SINGLE-USER TURNKEY ONLY: the file basement has no tenant
103
- // isolation, so a multi-tenant deployment (requirePrincipal) gets `undefined` = memory dark, fail-closed
104
- // (memoryEngineBackendFor + memoryScopeFor enforce the same gate). The legacy MemoryStore plane
105
- // (MEMORY_BACKEND/EMBEDDING_*) was dropped without migration; `scripts/drop-memory-tables.sql` drops the table.
106
- // S3-TOB(设计 §1.3,后经改判与边界重切):backend 选择器——file=现状(单用户/host);pg|tidb=
107
- // DB durable 真身(零卷主档:memory/ 文件面=materialize 的 ephemeral 工作副本,harvest 回 DB=持久化
108
- // 时点)。DB backend 是多租户点亮的唯一门(显式 opt-in);选了 DB 但池不可用/方言不匹配=fail-loud 拒启
109
- // (no-stopgap:绝不静默降 File/dark)。
110
70
  let memoryEngine;
111
- // 142-S2.5:per-(scope,peer) 同步游标面(sync_cursors)——POST /v1/memory/sync/:scope 的持久化半场。
112
- // 只在 DB memory plane 上点亮(file 形态路由 501,不接);与 entry plane 同池同方言。
113
71
  let memorySyncCursors;
114
- /** #252:两支 SQL 后端从实例 getter 读到的档位真值;file/off 腿留 undefined(该腿无此指示面)。 */
115
72
  let vectorMode;
116
73
  if (config.memoryEngineBackend !== "file" && config.memoryEngineEnabled) {
117
74
  const dialect = config.memoryEngineBackend;
118
- // 142-S5.1 production history sink: every applied patch appends one
119
- // agent_memory_engine_history row via the backend's optional history opts. The wrapper's ONLY
120
- // job is the metrics leg — count lost rows BEFORE rethrowing (the backend swallows + warns with
121
- // its own cumulative count; the audit line's breakage must itself be auditable).
122
75
  const countedHistorySink = (store) => ({
123
76
  appendHistory: async (rows) => {
124
77
  try {
@@ -130,30 +83,15 @@ export async function openStores(ctx) {
130
83
  }
131
84
  },
132
85
  });
133
- // #228:检索档位(lexical|portable|native)是「后端上限 × embedder 在场」的**推断结果**,不是旋钮
134
- // ([3590] 裁①)。下面两支各自从**实例的 getter** 读真值——不在这里自己推断,推断出的档与实际跑的档
135
- // 一旦分家,启动日志就成了另一句谎([3606]①:lexical 在跑必须运维可见)。
136
- // ⚠️ #252:声明上移到函数作用域(启动日志之外,`buildMemoryPosture` 也要读这个真值)——两支的赋值
137
- // 逐字不变,file/off 那一臂照旧留 `undefined`(file 引擎没有档位指示面,不折成 lexical)。
138
86
  if (dialect === "pg") {
139
87
  const pool = backend?.pgPool();
140
88
  if (!pool)
141
89
  throw new Error("MEMORY_ENGINE_BACKEND=pg requires DB_BACKEND=pg (the memory engine binds the same PG pool) — refusing to start half-configured");
142
90
  const q = async (text, params) => { const r = await pool.query(text, params); return { rows: r.rows }; };
143
- // ⚠️ 维度守卫的**位置**(诚实记档,不编造对表):这里 ensure schema 不传 `memoryVector`,即 embedding
144
- // 列是 `jsonb`(portable 档),schema 侧**没有维度**可与 MEMORY_EMBEDDER_DIM 对表。boot 期能做的
145
- // 只有「DIM 是正整数」(config.ts parseMemoryEmbedder);真正的维度守卫在写入路径两处:
146
- // ① 本仓 embedder 拿到长度不符的响应直接抛(绝不截断/补零);
147
- // ② `PgMemoryEngineBackend.embeddingParam` 对长度不符的向量返回 null,该行退回词面档、不写坏向量。
148
91
  await ensurePgMemoryEngineSchema(q);
149
92
  await ensurePgMemoryHistorySchema(q);
150
- await ensurePgMemorySyncSchema(q); // 142-S2.5: sync_cursors(+push_queue)同池 ensure,幂等
93
+ await ensurePgMemorySyncSchema(q);
151
94
  memorySyncCursors = new PgMemorySyncStore(q);
152
- // #228:配了 MEMORY_EMBEDDER_*(config 层已把半配/坏值/错后端全拒掉)⇒ 注入,vectorMode 由 lexical
153
- // 升到 portable;没配 ⇒ undefined,逐字保持改前行为。
154
- // onFailure(codex 复审 F2):embed 抛出去之后,applyPatches 的外层 catch 会把它折成一条
155
- // `io error: …` **冲突**(core 的冲突词表把它讲成「并发改动」),search 那腿则直接退回词面档 ——
156
- // 两条路都不会在部署面留下「向量供应商挂了」的信号。这条腿就是那个信号(抛照抛,只是被看见)。
157
95
  const embedder = memoryEmbedderFor(config, {
158
96
  onFailure: (err) => {
159
97
  metrics.inc("memory_embed_failed_total", { backend: dialect });
@@ -164,14 +102,6 @@ export async function openStores(ctx) {
164
102
  });
165
103
  },
166
104
  });
167
- // design/234 embedder 指纹门(clay 终裁 [3647]⑤ 方向=清空重建):同维度换模型是**静默**混空间形
168
- // ——旧空间与新空间的向量在同一列里做 cosine,排序变噪音,没有任何一层会说出来。指纹
169
- // `{model,dimensions}` 明文存单行元表,boot 一次比对:不等/无主/不可解析 ⇒ 先清 `embedding` 列、
170
- // 后写元表(次序即正确性,反序中途崩溃=新 identity 已记而旧向量还在=正是被修的病),写后 CAS
171
- // 重读收敛并发副本。⚠️ 位置:ensure*Schema 之后、`new PgMemoryEngineBackend` **之前** —— 比对跑在
172
- // backend 构造之后 = 服务已经能用旧空间向量检索了。门自身失败一律抛(拒启)。
173
- // A 态(embedder 缺席)门整个不装:连 ensure 都不调,元表不动(旧 identity 保留,配置回归同
174
- // identity 时存量向量直接续用)。⚠️ ③b 扫的是**源码文本**,调用写在 if 内照样入册(K1)。
175
105
  if (embedder !== undefined && config.memoryEmbedder !== undefined) {
176
106
  await ensurePgMemoryEmbedderMetaSchema(q);
177
107
  await reconcileEmbedderFingerprint(q, { model: config.memoryEmbedder.model, dimensions: config.memoryEmbedder.dimensions }, { log: logger });
@@ -183,26 +113,11 @@ export async function openStores(ctx) {
183
113
  "SAME identity later reuses vectors only for rows that have not been touched in the meantime",
184
114
  });
185
115
  }
186
- // #319 copy-out 生产化站(core 5.47/5.48 契约冻结面)—— **本装配点即换装点,不另起一支**:
187
- // 这条腿上的 `PgMemoryEngineBackend` 就是 [4706] 点名的 PG twin(它是 core 1.267 整段移交过来的
188
- // copy-out;生产化=把它补到冻结形,不是在旁边再写一只)。本站补的两条行为条款(header 携
189
- // `exposure` / `search` 两带序)落在**读面**,所以换装的实际形状是:
190
- // · DDL 零变化 —— origin 在库里是 frontmatter jsonb 里的普通字段(138 S3 形),没有新列、
191
- // 没有迁移、没有回填;ensure*Schema 的调用序逐字未动。
192
- // · 旋钮零新增 —— 两带序不是部署开关:后端只在调用方**显式**传 `opts.exposureBands` 时分带,
193
- // 缺席 = pre-336 单带字节形。请求方是 core 引擎的读侧,按 `RunnerDeps.memoryProvenance`
194
- // (本仓 MEMORY_PROVENANCE=off|carry,见 boot/runner-deps.ts)自己决定 —— server 不代它翻。
195
- // · 因此本行**不需要**任何前置开关或灰度位:同一只 backend,老调用方拿到逐字节相同的答复,
196
- // 新调用方(升到 5.47+ 的 core)拿到带序与 exposure 事实。
197
- // 验收面 = `test/memory-engine-db-integration.test.ts`:core 自己的 28 格契约(两方言真库)+
198
- // 钉1-钉7(重启存续/并发抢 slug/无副作用读/跨 scope 拒/原生档两带序/谓词对表/同分截断)。
199
116
  const pgMem = new PgMemoryEngineBackend(q, {
200
117
  history: countedHistorySink(new PgMemoryHistoryStore(q)),
201
118
  ...(embedder !== undefined ? { embedder } : {}),
202
119
  });
203
120
  vectorMode = pgMem.vectorMode;
204
- // 工作面根:DB 形态下 memory/ 目录=per-worker ephemeral 物化区(丢了重建,控制面同);
205
- // 复用 localDataRoot 下独立子树,绝不与 File backend 的持久 memory/ 混写。
206
121
  memoryEngine = { backend: pgMem, root: join(config.localDataRoot ?? localRoot, "memory-work") };
207
122
  }
208
123
  else {
@@ -211,18 +126,12 @@ export async function openStores(ctx) {
211
126
  throw new Error("MEMORY_ENGINE_BACKEND=tidb requires DB_BACKEND=mysql — refusing to start half-configured");
212
127
  await ensureTiDBMemoryEngineSchema(pool);
213
128
  await ensureTiDBMemoryHistorySchema(pool);
214
- await ensureTiDBMemorySyncSchema(pool); // 142-S2.5: sync_cursors(+push_queue)同池 ensure,幂等
129
+ await ensureTiDBMemorySyncSchema(pool);
215
130
  memorySyncCursors = new TiDBMemorySyncStore(pool);
216
- // tidb 记忆后端 v1 无向量档(传 embedder 它自己 fail-loud 拒);config 层已保证 MEMORY_EMBEDDER_*
217
- // 只可能与 pg 后端同时在场,所以这一支不注入,恒 lexical。
218
131
  const tidbMem = new TiDBMemoryEngineBackend(pool, { history: countedHistorySink(new TiDBMemoryHistoryStore(pool)) });
219
132
  vectorMode = tidbMem.vectorMode;
220
133
  memoryEngine = { backend: tidbMem, root: join(config.localDataRoot ?? localRoot, "memory-work") };
221
134
  }
222
- // embedder 身份(model + dim)一并自报(codex 复审 F1 的**部分**缓解,记档):本仓没有向量指纹列
223
- // (要加列 = 动 SQL,超出 #228 单),所以「同维度换模型 ⇒ 旧向量与新查询向量不同空间、排序变噪音」
224
- // 这件事在库里查不出来;能做的是把当前跑的模型写进启动日志,让换模型这件事在运维面留痕(USAGE 里
225
- // 的对应纪律:换模型/换维度前先清 `agent_memory_engine_entry.embedding`)。
226
135
  logger.info("memory_engine_enabled", {
227
136
  enabled: true,
228
137
  backend: dialect,
@@ -234,10 +143,6 @@ export async function openStores(ctx) {
234
143
  }
235
144
  else {
236
145
  memoryEngine = memoryEngineBackendFor(config);
237
- // [1845]②(cli 桌面撞坑;壳侧此前同坑已各自修过——两个宿主都踩=第三个宿主还会踩):操作员用
238
- // LOCAL_DATA_ROOT 明确要了隔离数据根,MEMORY_ENGINE_DIR 却缺省 ⇒ file 形 memory engine 落 core
239
- // 默认链(AGENT_DATA_DIR → ~/.ai-agent)= **静默共享全机 memory 库**。不改行为(单机用户可能就要
240
- // 共享),只把「你现在在共享」说出来。
241
146
  if (memoryEngine && process.env.LOCAL_DATA_ROOT && !config.memoryEngineDir) {
242
147
  logger.warn("memory_engine_dir_defaulted", {
243
148
  root: memoryEngine.root,
@@ -245,40 +150,22 @@ export async function openStores(ctx) {
245
150
  });
246
151
  }
247
152
  }
248
- // #214 记忆边界不变式([3492]② 立案):记忆没有写工具 —— 模型是用 fs 工具往记忆根写文件的,所以整条
249
- // 「记住 X」链的成立条件是**记忆根落在该次任务的 fs 授权边界内**。判据本体(含为什么是 warn 不是拒启、
250
- // 为什么围栏根未知时闭嘴)在 boot/memory-boundary.ts;这里只负责把 boot 期的真事实喂进去。
251
- // 围栏根的口径与 resolve-spec 的 fs 写门同源:host 语义车道才有本机围栏可谈 —— `REMOTE_EXEC` 未设的
252
- // in-process 形,手就在本进程 cwd 跑;显式 `host` 形取 operator 配的 workspaceBase(没配 = 每任务临时
253
- // 目录,boot 期不可知 ⇒ 空数组 ⇒ 不判)。其它车道一律非 host 语义,走 remote 那一臂。
254
153
  {
255
154
  const lane = config.remoteExec?.provider;
256
155
  const hostSemanticsLane = lane === undefined || lane === "host";
257
- // core 的 remote 判决(与文件平面是两根轴):`isRemoteExecutionEnv` 是鸭子类型,我方**所有**
258
- // REMOTE_EXEC 车道(含 `host` —— remote-env-host.ts 实现了 suspendVM/workspaceHandle/execStream,
259
- // 亲验)都满足;未设 provider = in-process(core 拿 StubExecutionEnv)。
260
156
  const coreRemoteExecutionEnv = config.remoteExec !== undefined;
261
- // 🔴 codex 复审(medium,已核真):`REMOTE_EXEC=host` 的 `workspaceBase` **不是**任务围栏 —— host
262
- // factory 每任务在它下面另铸一个 `sema-host-*` 子目录,而且调用方的 per-session cwd 还能整个换掉它。
263
- // 拿 workspaceBase 当围栏,会把「记忆根是真任务目录的**兄弟**」这一形误判成「在围栏内」而闭嘴 ——
264
- // 恰恰是本判据要抓的那种坏部署。所以 host 腿一律报**未知**(空数组 ⇒ 不判),只有 in-process 腿
265
- // (手就在本进程 cwd 上跑,boot 期这一位是真的)才给围栏根。宁可少说,不可说错。
266
157
  const containmentRoots = lane === undefined ? [process.cwd()] : [];
267
158
  const audit = buildMemoryWriteBoundaryAudit({
268
159
  memoryRoot: memoryEngine?.root,
269
160
  lane: lane ?? "in-process",
270
161
  hostSemanticsLane,
271
162
  coreRemoteExecutionEnv,
272
- // #266 A3 源头修:审计读**有效值**(host 腿 env 缺席时装配层自动 true)——自动补位后 ① 号 warn
273
- // 不再响(它警的正是「写指令被撤且无人声明」,现在装配层声明了);显式 env 恒赢在 derive 单源里执法。
274
163
  declaredCapable: effectiveMemoryPersistenceCapable(config),
275
164
  containmentRoots,
276
- engineBackend: config.memoryEngineBackend, // 只进文案:哪个旋钮真能挪动这条腿的根
165
+ engineBackend: config.memoryEngineBackend,
277
166
  });
278
167
  for (const w of audit.warnings)
279
168
  logger.warn(w.tag, { detail: w.detail, lane: lane ?? "in-process", root: memoryEngine?.root ?? null });
280
- // #266:自动补位必须在 boot 日志可见(感知链路纪律)——部署方一眼可核「这台机的 capable 是装配层
281
- // 按 host 腿车道事实补的」,并知道显式 MEMORY_PERSISTENCE_CAPABLE 可覆盖。只在真自动时发一行。
282
169
  if (config.memoryPersistenceCapable === undefined && effectiveMemoryPersistenceCapable(config) === true) {
283
170
  logger.info?.("memory_persistence_capable_auto_declared", {
284
171
  lane: lane ?? "in-process",
@@ -286,10 +173,6 @@ export async function openStores(ctx) {
286
173
  });
287
174
  }
288
175
  }
289
- // RosterStore(agent-team S1 server 半场,[1070]① 提货单;core 1.316 `RunnerDeps.rosterStore`):
290
- // 具名子 agent 持久名册。形态跟 StoreBackend:tidb/pg=SQL twins(语义三条+真双库验证,
291
- // roster-store-sql.ts 顶注);local=core FileRosterStore(dataRoot 下 roster.json,core 自带原子写/
292
- // 损坏安全);无 backend(纯内存 dev)=不挂(core 用活注册表,跨 run 指针面自然缺席——诚实)。
293
176
  let rosterStore;
294
177
  {
295
178
  const mysqlPool = backend?.mysqlPool?.();
@@ -304,8 +187,6 @@ export async function openStores(ctx) {
304
187
  rosterStore = new TiDBRosterStore(mysqlPool);
305
188
  }
306
189
  else if (backend?.kind === "local") {
307
- // #109([2392] 提货):onCorruptRead 披露座接线——腐读响亮 warn([2195] 族:不静默折空)。
308
- // 三挂点里 server 真用的只有这一个(session-policy=自家 SQL 双店;mailbox 用的 FileMailboxStore 无此座)。
309
190
  rosterStore = new FileRosterStore(join(config.localDataRoot ?? localRoot, "roster.json"), {
310
191
  onCorruptRead: (info) => logger.warn("roster_corrupt_read", { path: info.path, reason: info.reason }),
311
192
  });
@@ -313,15 +194,6 @@ export async function openStores(ctx) {
313
194
  if (rosterStore)
314
195
  logger.info("roster_store_enabled", { backend: pgPool ? "pg" : mysqlPool ? "tidb" : "file" });
315
196
  }
316
- // BackgroundAgentStore(design/151 S1 server 半场,[1503] 提货单;core 1.364 durable background agents):
317
- // 后台子代 `a*` 行的 durable 执行记录——settle 后转录 session 不再 eager release(TaskOutput/
318
- // AgentTranscript 完成后照读全程),清理移交 reapDurableAgents(reaper 区)。形态跟 roster:
319
- // tidb/pg=SQL twins(真双库验证,background-agent-store-sql.ts 顶注);local=core
320
- // FileBackgroundAgentStore(dataRoot 下 background-agents/,core 自带 ledger+snapshot 原子写);
321
- // 无 backend(纯内存 dev)=不挂(core 契约:store 不接线=pre-151 逐字节等价——单进程 live 注册表
322
- // 已覆盖读面,诚实缺席)。⚠️ 同实例双点挂载:RunnerDeps.backgroundAgentStore(读半场)+
323
- // ScenarioDeps→SubagentToolOptions.background.agentStore(写半场)都用这一个引用——engine 无法核对
324
- // 配对,半接=静默死特性(RB-37①)。
325
197
  let backgroundAgentStore;
326
198
  {
327
199
  const mysqlPool = backend?.mysqlPool?.();
@@ -336,28 +208,16 @@ export async function openStores(ctx) {
336
208
  backgroundAgentStore = new TiDBBackgroundAgentStore(mysqlPool);
337
209
  }
338
210
  else if (backend?.kind === "local") {
339
- // core 1.368([1516]②)listScopes 落地:file 实现自带枚举——1.248 拍的「local+多租户 reap 缺口」
340
- // warn-once 已撤(reaper 腿现在真枚举,不再假设单 scope)。
341
211
  backgroundAgentStore = new FileBackgroundAgentStore(config.localDataRoot ?? localRoot);
342
212
  }
343
213
  if (backgroundAgentStore)
344
214
  logger.info("background_agent_store_enabled", { backend: pgPool ? "pg" : mysqlPool ? "tidb" : "file" });
345
215
  }
346
- // D-1 通用文件上传(clay 拍 2026-07-27):独立附件 store(不骑 snapshot blob 面——那套的 orphan GC
347
- // 会把非 manifest 引用的 bytes 误收;生命周期也不同:附件跟 task/session 绑定)。形态跟 StoreBackend
348
- // 三态:tidb/pg=SQL twins(真双库验证)、local=文件店;无 backend(纯内存 dev)=不挂 ⇒ 路由 501。
349
- // 四半场:①上传/取回/删除(http/server.ts 路由)②提交时绑定+objective 告知+host lane 物化
350
- // (resolveSpec 内,下方)③远程 lane env 建立时物化(executionEnvFactory 包装,下方)④生命周期
351
- // (E21 purge 级联 + 未绑定 TTL reaper)。
352
216
  let taskAttachmentStore;
353
217
  {
354
218
  const mysqlPool = backend?.mysqlPool?.();
355
219
  const pgPool = backend?.pgPool?.();
356
220
  if (pgPool || mysqlPool) {
357
- // clay 裁(2026-07-27):**云形态对象存储必配**——附件字节本体进 MinIO(SQL 只存 meta/门/生命周期)。
358
- // 复用快照 lane 的同一 MinIO 部署配置(MINIO_ENDPOINT/ACCESS/SECRET,零重映射),但**附件专属
359
- // keyPrefix**:对象键=sha256,与快照 blob 同 sha 不同生命周期,同名字空间会互删。未配 ⇒ 附件面
360
- // 不接线(路由 501)+ error 级日志——这是部署配置错误,不静默降级回「字节进 DB」(该形已被裁掉)。
361
221
  const minio = config.snapshotBlobStore;
362
222
  if (!minio) {
363
223
  logger.error("attachments_disabled_object_store_required", {
@@ -383,11 +243,6 @@ export async function openStores(ctx) {
383
243
  if (taskAttachmentStore)
384
244
  logger.info("task_attachment_store_enabled", { backend: pgPool ? "pg+minio" : mysqlPool ? "tidb+minio" : "file", maxBytes: config.attachmentMaxBytes });
385
245
  }
386
- // S3c(core 1.374 行为车,[1531]):teammate 信箱——SendMessage 对 SETTLED named teammate 的 tier-3
387
- // 懒复活链(durable 行解析→claim-CAS→mailbox append→reviveSpawn→attach 屏障后 ack)。激活三件套=
388
- // RunnerDeps.mailboxStore + backgroundAgentStore 同挂 + delegation 工具在场,三缺一=1.373 文本字节
389
- // 不变(诚实拒)。形态同上:tidb/pg=SQL twins(mailbox-store-sql.ts,发车复审八修顶注);local=
390
- // core FileMailboxStore;纯内存 dev=不挂。回执句族(details.error 枚举)wire 透传零改动。
391
246
  let mailboxStore;
392
247
  {
393
248
  const mysqlPool = backend?.mysqlPool?.();
@@ -401,7 +256,6 @@ export async function openStores(ctx) {
401
256
  mailboxStore = new TiDBMailboxStore(mysqlPool);
402
257
  }
403
258
  else if (backend?.kind === "local") {
404
- // #113([2399]③→[2408] core 5.2.0 补座):腐读披露——照 roster 同形;torn-tail 静默=core 有意边界。
405
259
  mailboxStore = new FileMailboxStore(config.localDataRoot ?? localRoot, {
406
260
  onCorruptRead: (info) => logger.warn("mailbox_corrupt_read", { path: info.path, reason: info.reason }),
407
261
  });
@@ -409,11 +263,6 @@ export async function openStores(ctx) {
409
263
  if (mailboxStore)
410
264
  logger.info("mailbox_store_enabled", { backend: pgPool ? "pg" : mysqlPool ? "tidb" : "file" });
411
265
  }
412
- // design/166-T1:治理窗账本店——只在 USAGE_WINDOWS 配置时构造(两键成对进 RunnerDeps,core 的
413
- // 「windows 有 store 无=INACTIVE 警告」在构造上被排除)。形态跟 mailbox:tidb/pg=SQL twins
414
- // (ensure-first+行锁,真双库验证,usage-window-store-sql.ts 顶注);local=core FileUsageWindowStore
415
- // (构造器内部自拼 usage-windows/,直传数据根——复审新6);纯内存 dev=core InMemoryUsageWindowStore
416
- // (进程内计数,单进程 dev 的诚实形——治理窗设了就得生效,不因无持久层而静默失活)。
417
266
  let usageWindowStore;
418
267
  if (config.usageWindows) {
419
268
  const mysqlPool = backend?.mysqlPool?.();
@@ -434,15 +283,8 @@ export async function openStores(ctx) {
434
283
  }
435
284
  logger.info("usage_window_store_enabled", { backend: pgPool ? "pg" : mysqlPool ? "tidb" : backend?.kind === "local" ? "file" : "memory", windows: config.usageWindows.length });
436
285
  }
437
- // 142-S5 §1.4: the per-scope export READ face lights up only on the DB memory plane (the multi-tenant
438
- // truth lives in the scope-partitioned tables; the file posture exports by copying the memory dir, so
439
- // the route 501s honestly there). History-table WRITE wiring: inside the backend construction above
440
- // (S5.1, opts.history — the write point lives in the handed-over backend code).
441
286
  const memoryExportBackend = config.memoryEngineBackend !== "file" ? memoryEngine?.backend : undefined;
442
- const memoryLane = memoryEngineRemoteLanePosture(config); // N0: worker/sandbox file-plane split posture
443
- // #252 件2/件3:记忆面姿态 —— **单一推导点**(纯,memory-posture.ts),两个读面(operator 诊断 /
444
- // 能力位)都投影它,启动日志的 dark 报因也走它的闭集词,于是「日志说暗、能力位说亮」不可能发生。
445
- // 位置:两支引擎装配都结束之后(要 `memoryEngine` 与 `vectorMode` 两件装配结果)。
287
+ const memoryLane = memoryEngineRemoteLanePosture(config);
446
288
  const memoryPosture = buildMemoryPosture({
447
289
  config,
448
290
  engineWired: memoryEngine !== undefined,
@@ -454,23 +296,12 @@ export async function openStores(ctx) {
454
296
  ? { enabled: true, backend: "file", dir: memoryEngine.root }
455
297
  : {
456
298
  enabled: false,
457
- // 复审 F-11:按最终门结果报因,多租户 file=隔离判据,其余照旧。
458
- // #252:散文由**闭集词**派生(`memoryPosture.darkReason`)——机器面与人面从此不会各报各的因。
459
299
  reason: DARK_REASON_PROSE[memoryPosture.darkReason ?? "engine_off"](memoryLane?.lane),
460
300
  });
461
301
  }
462
- // N0 boot warn: loud in BOTH postures — "dark" so an upgrade that silently turns memory off is visible,
463
- // "forced" so an operator override states what it depends on (harvest only sees the WORKER fs). 文案与分腿
464
- // 判据在 memory-scope.buildMemoryRemoteLaneWarn(纯,可单测);这里只喂**装配结果**(引擎真接上了没有)——
465
- // DB 腿的引擎在 :113 就点亮了,与车道姿态无关,照旧打 dark 文案会与上面的 memory_engine_enabled 对撞。
466
302
  const laneWarn = buildMemoryRemoteLaneWarn(config, memoryEngine !== undefined);
467
303
  if (laneWarn)
468
304
  logger.warn("memory_engine_remote_lane", laneWarn);
469
- // 142-S2.5-W1: TOC 同步 client 腿——只在 file memory 形态接线(loadConfig 已拒 DB backend
470
- // 上的 MEMORY_SYNC_*,这条分支到不了)。boot 后 fire-and-forget 一轮(失败 warn 不阻断——纯本地现状
471
- // 是安全降级面);之后 harvest 真有 patch 落地时再触发(onMemoryHarvestReport 站点,inflight 节流)。
472
- // 引擎 dark(多租户/MEMORY_ENGINE=off/remote lane)⇒ 无本地盘可同步:warn 不 throw(dark 的三个成因
473
- // 各有自己的 loud 日志在上方,这里补“sync 因此没跑”这半句,operator 可见不半配)。
474
305
  let memorySyncRunner;
475
306
  if (config.memorySync) {
476
307
  if (memoryEngine) {
@@ -480,7 +311,6 @@ export async function openStores(ctx) {
480
311
  memoryRoot: memoryEngine.root,
481
312
  transport: createMemorySyncTransport({ url: config.memorySync.url, token: config.memorySync.token }),
482
313
  log: logger,
483
- // S2.5 分批(core 1.284):未设=不分批 wire 字节不变;设了=续轮 loop 至收敛(MAX_SYNC_ROUNDS 警戒)。
484
314
  ...(config.memorySync.maxPushEntries !== undefined ? { maxPushEntries: config.memorySync.maxPushEntries } : {}),
485
315
  ...(config.memorySync.maxPullEntries !== undefined ? { maxPullEntries: config.memorySync.maxPullEntries } : {}),
486
316
  });
@@ -491,13 +321,6 @@ export async function openStores(ctx) {
491
321
  logger.warn("memory_sync_configured_but_memory_dark", { note: "MEMORY_SYNC_URL is set but the file memory engine is dark (multi-tenant / MEMORY_ENGINE=off / remote exec lane) — no sync rounds will run" });
492
322
  }
493
323
  }
494
- // #318([4659] F2 → [4692] 定谳):会话级任务清单车道。形态与 roster/mailbox/background-agent 同族
495
- // (pg/tidb = SQL twins;无 SQL 后端 = 进程级 per-session 内存店 —— 这一支**刻意不缺席**:缺席就是
496
- // 修前那个「每 turn 一只新私店」的病本身,而 local/内存形至少要把同一会话的跨 turn 连续性拿回来)。
497
- // 全部判据(listKey 派生、LRU、为什么 SQL 形也缓存实例)见 boot/task-list-lane.ts 顶注。
498
- // ⚠️ 建表在**本文件**发 —— `test/schema-baseline-generated.test.ts` 判据 ③b 拿「boot/stores.ts 里真被
499
- // await 的 ensure 名」与 schema 基线生成器的驱动清单做集合相等对账(消灭「两条方言同时漏一族表」的
500
- // 对称盲区,`usage_window` 曾整族漏在基线外)。DDL 藏进车道文件 = 那道门看不见它。
501
324
  const taskListLane = await (async () => {
502
325
  const mysqlPool = backend?.mysqlPool?.();
503
326
  const pgPool = backend?.pgPool?.();
@@ -507,20 +330,7 @@ export async function openStores(ctx) {
507
330
  await ensureTiDBTaskListSchema(mysqlPool);
508
331
  return createTaskListLane({ mysqlPool, pgPool, logger });
509
332
  })();
510
- const sessionStore = createSessionStore(config, backend, metrics); // S25: stale-affinity evict fingerprint
511
- // 🪦 S6 启动门(旧):判据曾是 `!sessionStore.ownerOf`,core 2.11.0 给 TtlSessionStore 补了 owner 面后**恒假**。
512
- // 2026-08-01 复审一度把判据改成「归属能否跨重启存活」以复活它 —— **那是错的,已撤**(cli [C46] 的
513
- // 多租真机围栏当场证伪:`REQUIRE_PRINCIPAL=true` + 内存店必须**起得来**且真隔离)。
514
- // 错在威胁模型:内存店重启后**会话数据本身也没了**,"先到者认领 session id" 认领到的是空壳,
515
- // alice 的历史随进程消失 —— 归属丢失 ≠ 数据泄露。真正危险的是**数据留存而归属丢失**的 file 形,
516
- // 那一形由下面那道 local 门拦住(它的论证里 "session/run CONTENT is durable there" 正是这个区别)。
517
- // ⇒ 这道门防的威胁(store 没有归属能力)已被 core 2.11.0 消除,补偿该撤,不该换个判据续命。
518
- // 判据跟目的走、不跟补偿走(cli [C46] 的原话,我抄下)。反向钉见 test/security.test.ts。
519
- // The local in-memory backend HAS an ownerOf (LocalSessionStore), so the guard above passes — but its owner map is
520
- // process-local + lost on restart, so it cannot DURABLY enforce multi-tenant ownership (post-restart a session id is
521
- // re-claimable by whoever attaches first). Refuse REQUIRE_PRINCIPAL on it: a real multi-tenant
522
- // boundary needs the durable DB backend; single-user local runs with REQUIRE_PRINCIPAL=false (the principal may
523
- // still ride for memory-scoping). The file-backed follow-on (durable owners) can revisit this.
333
+ const sessionStore = createSessionStore(config, backend, metrics);
524
334
  if (config.requirePrincipal && backend?.kind === "local") {
525
335
  throw new Error("REQUIRE_PRINCIPAL=true is not supported on the local file backend (DB_BACKEND=local): session/run CONTENT is " +
526
336
  "durable there, but OWNER attribution is process-local and lost on restart (store-backend.ts §0.5 — durable " +
@@ -528,27 +338,17 @@ export async function openStores(ctx) {
528
338
  "(DB_BACKEND=mysql|pg) for multi-tenant, or run local single-user with REQUIRE_PRINCIPAL=false (a BFF may " +
529
339
  "still inject x-agent-principal per request for memory scoping and audit attribution).");
530
340
  }
531
- // (c)(clay 拍 a+c,2026-07-27)云形快照 blob 姿势门:mysql|pg 后端缺 MinIO ⇒ fail-loud(bytes-in-DB
532
- // 撞包墙已两役实证);SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true 显式逃生(单机/测试台,吃 (a) 的 per-blob 帽)。
533
341
  if (backend)
534
342
  assertCloudSnapshotBlobPosture(backend.kind, config);
535
- // Posture warning (audit B, security.ts): an owner-aware store without REQUIRE_PRINCIPAL means the
536
- // principal layer is optional per request. Owned sessions are still protected (the authorizer rejects
537
- // anonymous/mismatched attach), but new headerless submissions create anonymous sessions any token
538
- // holder can attach to — a multi-tenant deployment should set REQUIRE_PRINCIPAL=true.
539
343
  if (!config.requirePrincipal && sessionStore.ownerOf) {
540
344
  logger.warn("principal_optional", {
541
345
  note: "owner-aware session store with REQUIRE_PRINCIPAL=false — owned sessions are protected, but headerless callers can create/share anonymous sessions; set REQUIRE_PRINCIPAL=true for multi-tenant",
542
346
  });
543
347
  }
544
348
  metrics.dynamicGauge("sessions_warm", "Sessions held in the warm cache", () => sessionStore.size);
545
- // 1.38 cross-replica circuit-breaker state: shared via TiDB when a pool exists AND the breaker is
546
- // enabled; otherwise core's per-process Map (single replica / no DB / breaker off). Refresh loop is
547
- // unref'd so it never holds the process open.
548
349
  const breakerState = backend?.breaker && config.resilience.circuitBreaker
549
350
  ? backend
550
351
  .breaker((streak) => {
551
- // LOW (SILENT-FALLBACK P1): cross-replica breaker write-through failures were a bare swallow.
552
352
  metrics.setGauge("counter_flush_fail_streak", streak, { table: "circuit_breaker", kind: "write_through" });
553
353
  if (streak > 0)
554
354
  metrics.inc("breaker_writethrough_failed_total", { backend: backend.kind });
@@ -558,7 +358,7 @@ export async function openStores(ctx) {
558
358
  logger.info("breaker_writethrough_recovered", {});
559
359
  })
560
360
  .startRefresh()
561
- : undefined; // local omits breaker() → core's per-process Map
361
+ : undefined;
562
362
  return {
563
363
  backend, storeBackendDegraded, memoryEngine, memorySyncCursors, rosterStore, backgroundAgentStore,
564
364
  taskAttachmentStore, mailboxStore, memoryExportBackend, memorySyncRunner, sessionStore, breakerState,
@@ -1,28 +1,14 @@
1
1
  import { createMemoryTaskListStore } from "@sema-agent/core";
2
2
  import { BoundedSessionMap } from "../bounded-session-map.js";
3
3
  import { createPgTaskListStore, createTiDBTaskListStore, deletePgTaskList, deleteTiDBTaskList, taskListKeyFor, } from "../plugins/task-list-store-sql.js";
4
- /** 同时活着的会话清单实例上限(LRU,逐最老)。与 perSessionCwd/perSessionShellEnv 同量级。 */
5
4
  export const MAX_TASK_LIST_SESSIONS = 4096;
6
- // listKey 派生式的**属主是 store 文件**(`plugins/task-list-store-sql.ts` 的 {@link taskListKeyFor}
7
- // ——字节约束在那边,留存腿也按同一只函数批量删行;派生有两份 = 在线删得掉、留存腿删不掉)。
8
- // 这里只是把它一并导出,消费方(index.ts / 测试)不必知道它住在哪一层。
9
5
  export { taskListKeyFor } from "../plugins/task-list-store-sql.js";
10
- /**
11
- * 车道构造(boot 期一次)。
12
- *
13
- * ⚠️ **建表不在这里**:`ensure{TiDB,Pg}TaskListSchema` 由 `boot/stores.ts` 在构造之前 await
14
- * (与 roster/mailbox/background-agent/usage-window 四族逐字同姿势)。理由不是风格——
15
- * `test/schema-baseline-generated.test.ts` 判据 ③b 拿「`boot/stores.ts` 里真被 await 的 ensure 名」
16
- * 与「生成器驱动的清单」做**集合相等**对账,那是消灭「两条方言同时漏一族表」这个对称盲区的唯一
17
- * 不对称真源(`usage_window` 曾整族漏在基线外)。DDL 藏在本文件里 = 那道门看不见它。
18
- */
19
6
  export function createTaskListLane(opts = {}) {
20
7
  const { mysqlPool, pgPool, logger } = opts;
21
8
  const maxSessions = opts.maxSessions ?? MAX_TASK_LIST_SESSIONS;
22
9
  const cache = new BoundedSessionMap(maxSessions);
23
10
  let backend;
24
11
  let mint;
25
- /** 后端侧的整份清单抹除(memory 形无后端 ⇒ 只靠下面的缓存逐出)。 */
26
12
  let purge;
27
13
  if (pgPool) {
28
14
  backend = "pg";
@@ -41,7 +27,6 @@ export function createTaskListLane(opts = {}) {
41
27
  }
42
28
  const storeFor = (sessionId) => {
43
29
  const hit = cache.get(sessionId);
44
- // 命中即回写 = 刷新 MRU(BoundedSessionMap 的 `get` 刻意不动顺序,逐出闩只看 `set` 的插入序)。
45
30
  if (hit !== undefined) {
46
31
  cache.set(sessionId, hit);
47
32
  return hit;
@@ -51,9 +36,6 @@ export function createTaskListLane(opts = {}) {
51
36
  return fresh;
52
37
  };
53
38
  const deleteBySession = async (sessionId) => {
54
- // 先逐出缓存再删后端:反过来的话,两者之间的窗口里一次并发解析会拿到**旧实例**(memory 形
55
- // 是旧数据本身;SQL 形是刚被删空的分区,新写会把行插回去)。缓存先走 = 那扇窗里的解析看到的
56
- // 是一份新铸的空清单,与「会话已删」一致。
57
39
  cache.delete(sessionId);
58
40
  await purge(sessionId);
59
41
  };
@@ -1,50 +1,6 @@
1
- /**
2
- * A-057.5 [CONFIRMED high](2026-08-19 三轴组复审,真机探针复现)—— WebFetch 摘要面的
3
- * **per-model key plane**,与 `boot/side-query-lane.ts` 同一只 wrapper、同一条纪律。
4
- *
5
- * ## 病灶(#303 的第四面,逐字同形)
6
- *
7
- * #303 的头注写「side-query 是 key plane 三面里**唯一**缺席的那一面」—— 那句话是假的。
8
- * `createWebFetchSummarizer(brain, model)` 的 `brain` 是 `RunnerDeps.brain` 那只**裸网关 brain**
9
- * (`boot/budget-tracing.ts` 的 `createBrain(config, …)`,`get apiKey()` 恒回 `config.gatewayApiKey`),
10
- * 而 core 的摘要腿(`dist/tools/web.js`)铸 options 只放 `{ signal }`——`WebFetchSummarizerOptions`
11
- * 结构性没有 key 座位。于是 openai brain 的那两行照旧:
12
- * `const apiKey = options?.apiKey ?? config.apiKey;` ← 缺席即回落**主网关 key**
13
- * `const root = (model.baseUrl || config.baseUrl || "")` ← 却**按模型**选路
14
- * ⇒ summarize 角色解析到一只带自有 `baseUrl` + `apiKeyEnv/sealedApiKey` 的模型时,**主网关 key 被发到
15
- * 那只模型自己的外部 URL**;而且这不落在 #303 已具名接受的那条残余里(那条说的是「**没有** per-model
16
- * key 的 off-route 模型仍收网关 key」)—— 这只模型**有**自己的 key,却拿不到。
17
- *
18
- * 第二半同样成立:毒丸(sealed key 解不开)在本面**静默回落网关账号** —— 本面根本不经
19
- * `resolveModelApiKey`,`key-resolver.ts` 顶注那句「a broken sealed key must NEVER silently burn the
20
- * shared gateway account」在这条路上不成立。
21
- *
22
- * ## 修形 = 照 #303 的配方接第四面(不是新造语义)
23
- *
24
- * brain 换成 `createPerModelKeyBrain(brain, getKeyResolver)`:
25
- * · 有 per-model key ⇒ 注入 `options.apiKey`,请求带模型自己的凭据;
26
- * · 无 ⇒ 不注入 ⇒ 回落网关 key(与主推理链/side-query 逐字同语义,additive 契约;该残余的成文与
27
- * 跨面收严件见 side-query-lane.ts 顶注「具名残余」段与 A-057.59);
28
- * · 毒丸 ⇒ **上抛**,一个字节都不发(CLAUDE.md #157 安全轴 fail-closed)。
29
- * wrapper 只包本席这一只引用,`RunnerDeps.brain` 零扰动(与 side-query 席同形)。
30
- *
31
- * ## 为什么住在 `boot/` 而不是继续躺在 `main()` 的闭包里
32
- *
33
- * ① 这是一条**可发射的 LLM 面**,必须能被黑盒探针(真 fetchImpl 捕 Authorization)直接驱动 —— 躺在
34
- * `main()` 里只能靠源码字面锚,而本仓已成文「源码字面锚有致命假绿形」(A-057.4/.11 那一族);
35
- * ② 发射面枚举门(`test/per-model-key.test.ts` 的 `DIRECT_EMISSION_FACES`)按**文件**登记在册,第四面
36
- * 有自己的文件才登记得住。
37
- *
38
- * 原地保留的两条既有语义(E-MED-2/E-MED-3,[1900]/[1904] 复审批):
39
- * · **惰性解析**:模型每次调用现取 —— `config.models`/`config.roles` 会被 center 的 effective 热应用
40
- * `mutateInPlace` 整表替换,boot 期急求值会把摘要器永久 pin 在占位/旧模型上;
41
- * · **fail-soft**:`resolveTaskModel` 在无可解析角色时 throw,这是全仓唯一 boot 期裸调用点;解析失败
42
- * ⇒ 本次不摘要(core 的 summarize 抛错是 fail-open:回退原文 + note),绝不崩 boot。
43
- */
44
1
  import { createWebFetchSummarizer, resolveTaskModel as coreResolveTaskModel } from "@sema-agent/core";
45
2
  import { createPerModelKeyBrain } from "./side-query-lane.js";
46
3
  export function createWebFetchSummarizeLane(ctx) {
47
- // 与 side-query 席同形:wrapper 装配一次,resolver 靠 `getKeyResolver` 活取(热应用后下一次调用即生效)。
48
4
  const brain = createPerModelKeyBrain(ctx.brain, ctx.getKeyResolver, { gatewayBaseUrl: ctx.config.gatewayBaseUrl });
49
5
  return async (content, prompt, signal) => {
50
6
  const model = (() => {
@@ -58,9 +14,6 @@ export function createWebFetchSummarizeLane(ctx) {
58
14
  })();
59
15
  if (!model)
60
16
  throw new Error("summarize model unresolved for this deployment");
61
- // core 2.13.0:返回形放宽为 additive union(`string | { text, truncated? }`)—— 这里**原样转发**
62
- // core summarizer 的返回值,不在本仓收窄成 string(收窄会把 core 的截断披露 `truncated` 吃掉,
63
- // 让「内容被截断」这个事实在 fence 外消失)。
64
17
  return createWebFetchSummarizer(brain, model)(content, prompt, signal);
65
18
  };
66
19
  }