@sema-agent/server 7.137.0 → 7.139.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 (328) hide show
  1. package/MIGRATION.md +600 -142
  2. package/README.md +7 -3
  3. package/README.zh-CN.md +6 -3
  4. package/USAGE.md +132 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/wire.d.ts +3 -3
  8. package/dist/approval-card.d.ts +23 -16
  9. package/dist/approval-card.js +1 -0
  10. package/dist/audit.js +1 -0
  11. package/dist/background-agent-name.d.ts +5 -0
  12. package/dist/background-agent-name.js +5 -0
  13. package/dist/bench/s1/arms.d.ts +1 -1
  14. package/dist/bench/s1/arms.js +1 -3
  15. package/dist/bench/s1/run-firm.js +2 -2
  16. package/dist/boot/budget-tracing.d.ts +1 -12
  17. package/dist/boot/deployment-mods.d.ts +46 -16
  18. package/dist/boot/deployment-mods.js +82 -6
  19. package/dist/boot/device-lane.d.ts +8 -3
  20. package/dist/boot/device-lane.js +7 -2
  21. package/dist/boot/execution-env.d.ts +4 -2
  22. package/dist/boot/execution-env.js +16 -24
  23. package/dist/boot/execution-lane.d.ts +1 -1
  24. package/dist/boot/governance-seams.d.ts +12 -18
  25. package/dist/boot/governance-seams.js +10 -24
  26. package/dist/boot/leader.d.ts +3 -0
  27. package/dist/boot/leader.js +1 -0
  28. package/dist/boot/memory-consolidation.js +2 -2
  29. package/dist/boot/org-delivery.js +15 -2
  30. package/dist/boot/resolve-spec.d.ts +4 -1
  31. package/dist/boot/resolve-spec.js +29 -19
  32. package/dist/boot/retention-lane.d.ts +14 -8
  33. package/dist/boot/retention-lane.js +2 -8
  34. package/dist/boot/runner-deps.d.ts +10 -4
  35. package/dist/boot/runner-deps.js +2 -0
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-04-stores.d.ts +1 -12
  40. package/dist/boot/stage-05-execution-env.d.ts +3 -12
  41. package/dist/boot/stage-05-execution-env.js +8 -5
  42. package/dist/boot/stage-06-runners.d.ts +3 -12
  43. package/dist/boot/stage-06-runners.js +5 -3
  44. package/dist/boot/stage-07-capability-layer.d.ts +3 -12
  45. package/dist/boot/stage-07-capability-layer.js +2 -2
  46. package/dist/boot/stage-08-reapers.d.ts +3 -12
  47. package/dist/boot/stage-09-leader.d.ts +3 -12
  48. package/dist/boot/stage-09-leader.js +2 -3
  49. package/dist/boot/stage-10-http-server.d.ts +3 -12
  50. package/dist/boot/stage-10-http-server.js +5 -3
  51. package/dist/boot/stores.js +4 -4
  52. package/dist/boot/web-admin.d.ts +5 -2
  53. package/dist/boot/web-admin.js +5 -5
  54. package/dist/brain.js +5 -5
  55. package/dist/budget.d.ts +17 -15
  56. package/dist/budget.js +2 -2
  57. package/dist/capabilities/mcp-probe.d.ts +36 -11
  58. package/dist/capabilities/mcp-probe.js +44 -5
  59. package/dist/capabilities/scenarios.d.ts +1 -1
  60. package/dist/capability-facts.d.ts +21 -7
  61. package/dist/capability-facts.js +13 -3
  62. package/dist/checkpoint-identity.d.ts +13 -6
  63. package/dist/config-catalog.js +14 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +19 -24
  67. package/dist/config-provider.js +27 -12
  68. package/dist/config-types.d.ts +25 -12
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +52 -20
  71. package/dist/delegated-session-owner.d.ts +2 -2
  72. package/dist/deployment-shape.d.ts +7 -7
  73. package/dist/device-enrollment.d.ts +41 -2
  74. package/dist/device-enrollment.js +27 -12
  75. package/dist/device-store.d.ts +58 -85
  76. package/dist/device-store.js +19 -49
  77. package/dist/device-ws-hub.js +2 -1
  78. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  79. package/dist/exec-cut.d.ts +60 -0
  80. package/dist/exec-cut.js +36 -0
  81. package/dist/execution-lane-caps.d.ts +19 -0
  82. package/dist/execution-lane-caps.js +10 -0
  83. package/dist/execution-placement.d.ts +259 -0
  84. package/dist/execution-placement.js +199 -0
  85. package/dist/field-conflict.d.ts +3 -2
  86. package/dist/field-conflict.js +1 -0
  87. package/dist/gate-settlement.d.ts +71 -0
  88. package/dist/gate-settlement.js +40 -0
  89. package/dist/hooks/classic-stations.d.ts +1 -0
  90. package/dist/hooks/classic-stations.js +1 -0
  91. package/dist/hooks/command-spawn.d.ts +2 -2
  92. package/dist/hooks/hook-runner.js +14 -14
  93. package/dist/hosted-posture.d.ts +2 -2
  94. package/dist/http/admission.js +10 -19
  95. package/dist/http/checkpoint-ctx.d.ts +21 -12
  96. package/dist/http/checkpoint-ctx.js +4 -2
  97. package/dist/http/claimed-run.d.ts +1 -1
  98. package/dist/http/dispatch.d.ts +4 -0
  99. package/dist/http/dispatch.js +3 -0
  100. package/dist/http/durable-run-leg.js +12 -6
  101. package/dist/http/execution-context.d.ts +218 -0
  102. package/dist/http/execution-context.js +330 -0
  103. package/dist/http/memory-compliance-failure-reply.js +3 -0
  104. package/dist/http/resume-legs.d.ts +5 -11
  105. package/dist/http/resume-legs.js +179 -104
  106. package/dist/http/route-ctx.d.ts +44 -59
  107. package/dist/http/route-ctx.js +2 -5
  108. package/dist/http/routes/a2a-serve.js +10 -4
  109. package/dist/http/routes/agents-roster.d.ts +1 -1
  110. package/dist/http/routes/agents-roster.js +5 -1
  111. package/dist/http/routes/approvals-assistant.js +6 -6
  112. package/dist/http/routes/background.d.ts +29 -1
  113. package/dist/http/routes/background.js +29 -2
  114. package/dist/http/routes/capabilities.js +12 -10
  115. package/dist/http/routes/devices.d.ts +13 -19
  116. package/dist/http/routes/devices.js +21 -26
  117. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  118. package/dist/http/routes/mcp-reconnect.js +7 -5
  119. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  120. package/dist/http/routes/memory-origin.d.ts +1 -1
  121. package/dist/http/routes/rules.js +3 -3
  122. package/dist/http/routes/runs.js +11 -7
  123. package/dist/http/routes/session-sync.js +110 -15
  124. package/dist/http/routes/sessions.d.ts +6 -0
  125. package/dist/http/routes/sessions.js +119 -13
  126. package/dist/http/routes/side-query.d.ts +12 -4
  127. package/dist/http/routes/side-query.js +6 -8
  128. package/dist/http/routes/tasks.js +800 -782
  129. package/dist/http/server.d.ts +61 -39
  130. package/dist/http/server.js +11 -0
  131. package/dist/http/session-inherit-env.d.ts +3 -4
  132. package/dist/http/session-inherit-env.js +1 -1
  133. package/dist/http/wire-types.d.ts +22 -18
  134. package/dist/identity-source.d.ts +1 -1
  135. package/dist/index.d.ts +3 -0
  136. package/dist/index.js +1 -0
  137. package/dist/leader/wire.d.ts +7 -0
  138. package/dist/leader/wire.js +5 -4
  139. package/dist/mcp-assertion-header.d.ts +51 -40
  140. package/dist/mcp-assertion-header.js +10 -11
  141. package/dist/mcp-authorization-face.d.ts +27 -0
  142. package/dist/mcp-authorization-face.js +15 -0
  143. package/dist/memory-operator-faces.d.ts +13 -4
  144. package/dist/memory-operator-faces.js +1 -17
  145. package/dist/memory-sync-client.d.ts +53 -14
  146. package/dist/memory-sync-client.js +187 -92
  147. package/dist/memory-sync.d.ts +1 -1
  148. package/dist/memory-sync.js +3 -2
  149. package/dist/model-route-gap.d.ts +6 -22
  150. package/dist/model-route-gap.js +7 -5
  151. package/dist/mods-declaration.d.ts +110 -0
  152. package/dist/mods-declaration.js +109 -0
  153. package/dist/observability/cost-taxonomy.d.ts +2 -2
  154. package/dist/observability/err-text.d.ts +5 -0
  155. package/dist/observability/err-text.js +9 -0
  156. package/dist/observability/fail-open.d.ts +17 -1
  157. package/dist/observability/fail-open.js +17 -1
  158. package/dist/observability/run-terminal.js +0 -1
  159. package/dist/observability/secret-env-scrub.d.ts +1 -1
  160. package/dist/observability/secret-env-scrub.js +1 -1
  161. package/dist/orchestration/workflow-notify-journal.js +4 -4
  162. package/dist/org-memory-admission.d.ts +4 -3
  163. package/dist/org-memory-admission.js +6 -6
  164. package/dist/parked-decide.d.ts +49 -11
  165. package/dist/parked-decide.js +25 -26
  166. package/dist/parked-revive-surface.d.ts +12 -15
  167. package/dist/parked-revive-surface.js +3 -6
  168. package/dist/peer-seat.d.ts +156 -0
  169. package/dist/peer-seat.js +273 -0
  170. package/dist/permission-rule-vocab.d.ts +7 -7
  171. package/dist/permission-rule-vocab.js +3 -3
  172. package/dist/plain-string-record.d.ts +38 -0
  173. package/dist/plain-string-record.js +38 -0
  174. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  175. package/dist/plugins/approval-ask-store-file.js +11 -0
  176. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  177. package/dist/plugins/approval-ask-store-memory.js +12 -0
  178. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  179. package/dist/plugins/approval-ask-store-sql.js +30 -12
  180. package/dist/plugins/background-agent-store-sql.js +6 -3
  181. package/dist/plugins/caching-session-store.d.ts +11 -2
  182. package/dist/plugins/caching-session-store.js +15 -3
  183. package/dist/plugins/checkpoint-store-sql.d.ts +21 -25
  184. package/dist/plugins/checkpoint-store-sql.js +13 -15
  185. package/dist/plugins/device-store-sql.d.ts +25 -51
  186. package/dist/plugins/device-store-sql.js +49 -103
  187. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  188. package/dist/plugins/execution-placement-sql.js +223 -0
  189. package/dist/plugins/file-history-store-sql.d.ts +7 -6
  190. package/dist/plugins/file-history-store-sql.js +8 -7
  191. package/dist/plugins/file-run-store.d.ts +6 -1
  192. package/dist/plugins/file-run-store.js +4 -0
  193. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  194. package/dist/plugins/fork-routing-session-store.js +16 -0
  195. package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
  196. package/dist/plugins/k8s-pod-reaper.js +3 -3
  197. package/dist/plugins/local-checkpoint-store.d.ts +1 -11
  198. package/dist/plugins/local-checkpoint-store.js +0 -1
  199. package/dist/plugins/local-session-store.d.ts +183 -45
  200. package/dist/plugins/local-session-store.js +412 -105
  201. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  202. package/dist/plugins/mailbox-store-sql.js +103 -15
  203. package/dist/plugins/memory-key-guards.d.ts +4 -0
  204. package/dist/plugins/memory-key-guards.js +1 -0
  205. package/dist/plugins/memory-run-store.d.ts +1 -1
  206. package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
  207. package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
  208. package/dist/plugins/one-time-migrations.d.ts +117 -6
  209. package/dist/plugins/one-time-migrations.js +383 -14
  210. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  211. package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
  212. package/dist/plugins/permission-rule-store-sql.js +34 -29
  213. package/dist/plugins/pg-pool.d.ts +7 -3
  214. package/dist/plugins/pg-session-storage.d.ts +41 -22
  215. package/dist/plugins/pg-session-storage.js +136 -55
  216. package/dist/plugins/remote-env-adb.d.ts +22 -4
  217. package/dist/plugins/remote-env-adb.js +70 -62
  218. package/dist/plugins/remote-env-device.d.ts +32 -12
  219. package/dist/plugins/remote-env-device.js +18 -17
  220. package/dist/plugins/remote-env-e2b.d.ts +19 -3
  221. package/dist/plugins/remote-env-e2b.js +41 -42
  222. package/dist/plugins/remote-env-file-error.d.ts +2 -0
  223. package/dist/plugins/remote-env-file-error.js +1 -1
  224. package/dist/plugins/remote-env-host.d.ts +23 -8
  225. package/dist/plugins/remote-env-host.js +66 -70
  226. package/dist/plugins/remote-env-k8s.d.ts +13 -16
  227. package/dist/plugins/remote-env-k8s.js +61 -54
  228. package/dist/plugins/remote-env-local-docker.d.ts +30 -5
  229. package/dist/plugins/remote-env-local-docker.js +46 -59
  230. package/dist/plugins/remote-env-ssh.d.ts +18 -4
  231. package/dist/plugins/remote-env-ssh.js +72 -56
  232. package/dist/plugins/remote-shell.d.ts +100 -3
  233. package/dist/plugins/remote-shell.js +166 -2
  234. package/dist/plugins/retention-store-sql.d.ts +23 -8
  235. package/dist/plugins/retention-store-sql.js +13 -1
  236. package/dist/plugins/run-store-sql.d.ts +13 -3
  237. package/dist/plugins/run-store-sql.js +5 -0
  238. package/dist/plugins/schema-consistency.d.ts +15 -5
  239. package/dist/plugins/schema-consistency.js +14 -8
  240. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  241. package/dist/plugins/schema-drift-sentence.js +38 -0
  242. package/dist/plugins/session-fork.d.ts +63 -12
  243. package/dist/plugins/session-fork.js +38 -21
  244. package/dist/plugins/session-ownership.d.ts +7 -0
  245. package/dist/plugins/session-ownership.js +5 -0
  246. package/dist/plugins/session-row-sql.d.ts +22 -0
  247. package/dist/plugins/session-row-sql.js +81 -0
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  250. package/dist/plugins/session-workspace-cell.js +38 -0
  251. package/dist/plugins/store-backend.d.ts +1 -1
  252. package/dist/plugins/store-backend.js +26 -14
  253. package/dist/plugins/store-contracts.d.ts +25 -5
  254. package/dist/plugins/tidb-pool.d.ts +12 -7
  255. package/dist/plugins/tidb-pool.js +15 -3
  256. package/dist/plugins/tidb-session-store.d.ts +74 -28
  257. package/dist/plugins/tidb-session-store.js +132 -53
  258. package/dist/plugins/tool-result-store-sql.d.ts +16 -8
  259. package/dist/plugins/tool-result-store-sql.js +26 -17
  260. package/dist/plugins/worktree-isolation.d.ts +3 -2
  261. package/dist/plugins/worktree-isolation.js +1 -7
  262. package/dist/project-identity.d.ts +1 -14
  263. package/dist/project-identity.js +0 -4
  264. package/dist/project-memory.d.ts +9 -15
  265. package/dist/project-memory.js +5 -4
  266. package/dist/request-key-closure.js +1 -1
  267. package/dist/request-value-closure.d.ts +6 -7
  268. package/dist/request-value-closure.js +1 -3
  269. package/dist/rules-consent.js +6 -4
  270. package/dist/run-local.d.ts +6 -0
  271. package/dist/run-local.js +36 -26
  272. package/dist/runs.d.ts +9 -2
  273. package/dist/runs.js +9 -2
  274. package/dist/sandbox-git-plane.d.ts +2 -2
  275. package/dist/security.d.ts +38 -12
  276. package/dist/security.js +8 -2
  277. package/dist/server-secret-env.d.ts +15 -8
  278. package/dist/server-secret-env.js +4 -2
  279. package/dist/session-children-retire.d.ts +27 -9
  280. package/dist/session-children-retire.js +9 -5
  281. package/dist/session-incarnation.d.ts +19 -10
  282. package/dist/session-incarnation.js +5 -3
  283. package/dist/session-row.d.ts +114 -5
  284. package/dist/session-row.js +55 -5
  285. package/dist/session-titler.d.ts +2 -2
  286. package/dist/session-workspace.d.ts +130 -0
  287. package/dist/session-workspace.js +83 -0
  288. package/dist/spec-fields.d.ts +1 -1
  289. package/dist/standing-answer-grant.d.ts +2 -13
  290. package/dist/standing-answer-grant.js +0 -7
  291. package/dist/task-a2a.d.ts +44 -14
  292. package/dist/task-a2a.js +46 -20
  293. package/dist/task-cwd.d.ts +6 -1
  294. package/dist/task-mcp.d.ts +33 -20
  295. package/dist/task-mcp.js +46 -22
  296. package/dist/tool-approval.d.ts +38 -8
  297. package/dist/tool-approval.js +57 -13
  298. package/dist/trace/core-keyset-guard.d.ts +33 -18
  299. package/dist/trace/engine-notice-wire.d.ts +1 -1
  300. package/dist/trace/engine-notice-wire.js +2 -0
  301. package/dist/trace/exit-seat.d.ts +20 -1
  302. package/dist/trace/exit-seat.js +3 -0
  303. package/dist/trace/extension-call-face.d.ts +33 -0
  304. package/dist/trace/extension-call-face.js +39 -0
  305. package/dist/trace/project.d.ts +26 -7
  306. package/dist/trace/project.js +30 -3
  307. package/dist/trace/redact.js +42 -13
  308. package/dist/trace/self-minted-wire.d.ts +240 -12
  309. package/dist/trace/self-minted-wire.js +100 -4
  310. package/dist/trace/wire-projection-faces.d.ts +6 -6
  311. package/dist/trace/wire-projection-faces.js +6 -5
  312. package/dist/url-refusal.d.ts +1 -1
  313. package/dist/user-authored-text.d.ts +2 -2
  314. package/dist/user-authored-text.js +16 -1
  315. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  316. package/dist/web-admin-client/apply-effective.js +20 -10
  317. package/dist/web-admin-client/center-request.d.ts +3 -2
  318. package/dist/web-admin-client/center-request.js +2 -3
  319. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  320. package/dist/web-admin-client/skills-mcp.js +32 -18
  321. package/dist/web-admin-client/types.d.ts +11 -0
  322. package/package.json +3 -3
  323. package/wire-changes/server@7.138.0.json +446 -0
  324. package/wire-changes/server@7.139.0.json +348 -0
  325. package/dist/http/device-placement.d.ts +0 -175
  326. package/dist/http/device-placement.js +0 -145
  327. package/dist/http/mcp-remote-text.d.ts +0 -5
  328. package/dist/http/mcp-remote-text.js +0 -7
@@ -1,4 +1,5 @@
1
1
  import { closeSync, copyFileSync, openSync, readSync, statSync, existsSync } from "node:fs";
2
+ import { isExecutionLane } from "../execution-lane-caps.js";
2
3
  import { join } from "node:path";
3
4
  import { AppendLog, atomicWriteFile, ensureDir } from "@sema-agent/core";
4
5
  import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
@@ -295,6 +296,16 @@ export class FileApprovalAskStore {
295
296
  replayStats() {
296
297
  return { rejected: this.replayRejectedCount, corruptLines: this.corruptLineCount, records: this.records };
297
298
  }
299
+ backfillExecutionLanes(lanes, refuse) {
300
+ const pending = this.core.exportRows().asks.filter((r) => !isExecutionLane(String(r.executionLane ?? ""))).length;
301
+ if (pending === 0)
302
+ return 0;
303
+ if (lanes.length !== 1)
304
+ refuse(pending);
305
+ const filled = this.core.fillMissingExecutionLanes(lanes[0]);
306
+ this.compact();
307
+ return filled;
308
+ }
298
309
  dispose() {
299
310
  this.log?.close();
300
311
  this.log = undefined;
@@ -29,6 +29,7 @@
29
29
  * - resolveProvisional 故意不经 canAskTransition(同 SQL twin 头注:这是版本化补偿的例外通道)。
30
30
  */
31
31
  import { type AskState, type BatchState } from "../approval-ask-machine.js";
32
+ import { type ExecutionLane } from "../execution-lane-caps.js";
32
33
  import type { AskCallCoordinate, AskDecision, AskRow, AskTerminalClaimIntent, AskTerminalClaimOpts, AskTerminalClaimOutcome, AskTransitionPatch, ApprovalAskStore, BatchRow, BindGateInput, BindResult, DecideAskInput, EnsureAskResult, DecideResult, ExpireResult, NewAskRow } from "./approval-ask-store-sql.js";
33
34
  /**
34
35
  * [ref] 的 `updatedInput` **入店编码**——两 twin 共用的那一只(本函数是唯一真源)。
@@ -72,6 +73,11 @@ export declare class InMemoryApprovalAskStore implements ApprovalAskStore {
72
73
  * 两种可变性)。
73
74
  */
74
75
  exportRows(): ApprovalAskRowsSnapshot;
76
+ /**
77
+ * 执行放置切换 ④b 的 local 腿(File 形的内核口;`FileApprovalAskStore.backfillExecutionLanes` 调):出处车道还没记下的行(升级前写下的日志 / 快照行
78
+ * 装回后这一格缺席)记成 `lane`(一元车道表的唯一项 —— 调用方判过)。返回改了的行数;只碰缺这一格的行。
79
+ */
80
+ fillMissingExecutionLanes(lane: ExecutionLane): number;
75
81
  /** [ref]:一行的**同步**点读(拷贝;纯读,零判定)—— File 形在写口的同步前缀里用它(见类顶注)。 */
76
82
  peekAsk(askId: string): AskRow | null;
77
83
  /** [ref]:把快照里的行**原样**装回两张 Map(同键覆盖)。零谓词、零状态机 —— 它不是一条转移,是重放
@@ -1,4 +1,5 @@
1
1
  import { ASK_TERMINAL, canAskTransition } from "../approval-ask-machine.js";
2
+ import { isExecutionLane } from "../execution-lane-caps.js";
2
3
  import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
3
4
  import { askRowCarriesOwner, askRowOnCall, assertDecideSettledBy, assertIdempotencyKeyShape, assertIntegralEpochMs, assertReapBounds, ApprovalAskReadAbortedError, claimTargetState, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth, projectDecideReading } from "./approval-ask-store-sql.js";
4
5
  function applyPatch(row, patch) {
@@ -41,6 +42,16 @@ export class InMemoryApprovalAskStore {
41
42
  exportRows() {
42
43
  return { asks: [...this.asks.values()].map((r) => ({ ...r })), batches: [...this.batches.values()].map((b) => ({ ...b })) };
43
44
  }
45
+ fillMissingExecutionLanes(lane) {
46
+ let filled = 0;
47
+ for (const [askId, row] of this.asks) {
48
+ if (isExecutionLane(String(row.executionLane ?? "")))
49
+ continue;
50
+ this.asks.set(askId, { ...row, executionLane: lane });
51
+ filled += 1;
52
+ }
53
+ return filled;
54
+ }
44
55
  peekAsk(askId) {
45
56
  const row = this.asks.get(askId);
46
57
  return row ? { ...row } : null;
@@ -74,6 +85,7 @@ export class InMemoryApprovalAskStore {
74
85
  sourceTaskId: row.sourceTaskId,
75
86
  sessionId: row.sessionId,
76
87
  owner: row.owner ?? null,
88
+ executionLane: row.executionLane,
77
89
  batchId: row.batchId,
78
90
  toolCallId: row.toolCallId,
79
91
  legKey: row.legKey,
@@ -44,6 +44,7 @@ import { type AskState, type BatchState } from "../approval-ask-machine.js";
44
44
  import type { HostSettledBy } from "../host-decision.js";
45
45
  import { type AskRowCause, type SettleCause } from "../approval-deny-reasons.js";
46
46
  import { type IndexSpec } from "./ensure-index.js";
47
+ import { type ExecutionLane } from "../execution-lane-caps.js";
47
48
  export type AskDecision = "approve" | "deny";
48
49
  export interface AskRow {
49
50
  askId: string;
@@ -52,6 +53,11 @@ export interface AskRow {
52
53
  sessionId: string;
53
54
  /** 租户 scope——哨兵归一在消费层(车2+),店里存原值(设计定稿 §3)。 */
54
55
  owner: string | null;
56
+ /**
57
+ * 造这只问的那条执行的车道(审批出处带车道:问行唯一铸点 `ensureAsk` 与 `taskId` / `sessionId` 同处写;既有行命中一个字节不改)。
58
+ * 读根授权只读问自己记下的车道。存量行随执行放置切换的一次性迁移回填(SQL `approval_ask.execution_lane`、local 问账日志压实)。
59
+ */
60
+ executionLane: ExecutionLane;
55
61
  batchId: string;
56
62
  toolCallId: string;
57
63
  /** 车3 §3.2′ 换轴:数值 `leg` → `legKey`(= `sha256(resume token)` 的 hex,首腿空串)。语义与派生
@@ -174,6 +180,8 @@ export interface NewAskRow {
174
180
  sourceTaskId: string;
175
181
  sessionId: string;
176
182
  owner?: string | null;
183
+ /** 出处的执行车道(必填;语义见 {@link AskRow.executionLane})。 */
184
+ executionLane: ExecutionLane;
177
185
  batchId: string;
178
186
  toolCallId: string;
179
187
  legKey: string;
@@ -562,6 +570,14 @@ export interface ApprovalAskStore {
562
570
  getBatch(batchId: string): Promise<BatchRow | null>;
563
571
  }
564
572
  export declare const APPROVAL_ASK_TABLE = "approval_ask";
573
+ /**
574
+ * `approval_ask.execution_lane` 的列定义(两方言;唯一属主)—— 建表 DDL 内联它,存量库的加列只由执行放置切换的一次性迁移做(`one-time-migrations.ts` M32 步 ②),
575
+ * 那一步也读这里的定义。`DEFAULT ''` 只为存量库一句加列;回填(同迁移步 ④b)之后没有 '' 行,读口对 '' 与词表外的值一律响亮拒({@link executionLaneColumnOf})。
576
+ */
577
+ export declare const APPROVAL_ASK_EXECUTION_LANE_COLUMN: {
578
+ readonly mysql: "execution_lane VARCHAR(32) NOT NULL DEFAULT ''";
579
+ readonly pg: "execution_lane VARCHAR(32) COLLATE \"C\" NOT NULL DEFAULT ''";
580
+ };
565
581
  export declare const APPROVAL_BATCH_TABLE = "approval_batch";
566
582
  /**
567
583
  * TiDB/MySQL-protocol 侧的两条建表语句,**抽成导出数组**(车3 刀 3a):`tidb-pool.ts` 的
@@ -571,7 +587,8 @@ export declare const APPROVAL_BATCH_TABLE = "approval_batch";
571
587
  * 下面的 `ensureTiDBApprovalAskSchema` 保留为遍历这个数组的薄壳(集成测试直接调它建表,
572
588
  * 不必为了建两张表去拉起整套中央 schema)。
573
589
  *
574
- * SCHEMA POLICY 同 `tidb-pool.ts` 头注:纯 CREATE,禁 ALTER 增量 seam,改列直接改这里 + 重建库
590
+ * SCHEMA POLICY 同 `tidb-pool.ts` 头注:纯 CREATE,禁 ALTER 增量 seam,加列直接改这里(新建库随它到货;带数据的存量库由列集对账拒启并打印 `ALTER`,部署者自己执行;
591
+ * 改列类型 / 可空性列集对账看不见,另走一次性迁移;只有空库 / 开发机可以整库重建〔销毁全部数据〕)
575
592
  * (`docs/schema/baseline-mysql.sql` 是由这些文本机器录制出的**产物**,永不手改)。
576
593
  *
577
594
  * ── 列宽的**测量依据**([ref] A10;U2 逐列取证在档)──────────────────────────────────────────────────
@@ -619,18 +636,18 @@ export declare function ensurePgApprovalAskSchema(q: PgQueryFn): Promise<void>;
619
636
  * [ref] 升级前置断言 —— **拒启**,不是 warn(形照 [ref] 的 `assertPermissionRuleApprovalSchema` /
620
637
  * [ref] 的 `assertToolResultProvenanceSchema` 两条先例)。
621
638
  *
622
- * 病:本仓不发 `ALTER TABLE`(SCHEMA POLICY:改列就改 CREATE + 删库重建)。于是一台**没删表**就升上来
639
+ * 病:本仓不发 `ALTER TABLE`(SCHEMA POLICY:代码是唯一真源,存量库缺列由部署者按拒启句打印的 ALTER 自己执行)。于是一台**没跟上**就升上来
623
640
  * 的部署,`CREATE TABLE IF NOT EXISTS` 对存量 `approval_ask` 是空操作 —— 本车新加的
624
641
  * `rule_command` / `rule_scope_root` 两列不在,而 `ensureAsk` 的 INSERT 与 `askCols` 的每一条读
625
642
  * **无条件**引用它们。后果不是「少一个新功能」,是**整条流内审批协议**在那台机器上是坏的:
626
643
  * · 每一只 ask 的 `ensureAsk` 撞 unknown column ⇒ 被 `noteStoreError` 吞掉 ⇒ 整只 ask 退回 park 路由
627
644
  * (卡不落库、`GET /v1/approvals` 的重放面恒空、迟到决议腿恒 404);
628
645
  * · 而 `/v1/capabilities` 的 `streamApproval` 仍然报 active ⇒「says yes ⟺ route works」整句为假。
629
- * 没有这道断言,运维得不到**任何**「这次升级必须重建表」的信号 —— 正是 [ref]「禁静默降级」要挡的形。
646
+ * 没有这道断言,运维得不到**任何**「这次升级必须先给表加列」的信号 —— 正是 [ref]「禁静默降级」要挡的形。
630
647
  *
631
648
  * 判据 = **能力探测**(恒空的 `WHERE 1=0` 读)+ **方言的缺列错误码**(判据属主 `sql-errors.ts` 的
632
- * {@link isMissingColumnError});其余错误**原样 rethrow**,一个字都不加工 —— 一句破坏性的
633
- * 「去 DROP TABLE」指路比它要挡的缺陷更贵([ref] 那两轮收窄的结论逐字继承)。
649
+ * {@link isMissingColumnError});其余错误**原样 rethrow**,一个字都不加工 —— 把一次瞬时故障诊断成「缺列」的拒因
650
+ * 比它要挡的缺陷更贵([ref] 那两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link describeProbedTableDrift}),没有销毁性的一条。
634
651
  *
635
652
  * 🔴 位置与两条先例同款:**不在** `ensureSchema` 里(那条通道的契约是「只发 CREATE」,有运行时门看着),
636
653
  * 放在 DDL 之后、任何路由装配之前;且**只在流内审批协议真会上场时**跑(判据 = 与协调器同一个符号
@@ -2,8 +2,10 @@ import { mysqlDriver, pgDriver, dialectJsonEncoder } from "./sql-driver.js";
2
2
  import { ASK_TERMINAL, canAskTransition } from "../approval-ask-machine.js";
3
3
  import { APPROVAL_ENDING_CAUSES, causeOfAskRow } from "../approval-deny-reasons.js";
4
4
  import { isDupKeyError, isMissingColumnError } from "./sql-errors.js";
5
+ import { describeProbedTableDrift } from "./schema-drift-sentence.js";
5
6
  import { decodeJsonColumn, foldJsonColumn, jsonColumnText, readJsonObjectColumn, readTriStateJsonValueColumn } from "./sql-json-column.js";
6
7
  import { ensureIndexes } from "./ensure-index.js";
8
+ import { isExecutionLane } from "../execution-lane-caps.js";
7
9
  export function assertIntegralEpochMs(field, value) {
8
10
  if (!Number.isSafeInteger(value)) {
9
11
  throw new TypeError(`approval-ask store: ${field} must be an integer epoch-ms (BIGINT column), got ${String(value)}`);
@@ -99,6 +101,17 @@ export function projectDecideReading(row, owner) {
99
101
  return askRowCarriesOwner(row, owner) ? row : null;
100
102
  }
101
103
  export const APPROVAL_ASK_TABLE = "approval_ask";
104
+ export const APPROVAL_ASK_EXECUTION_LANE_COLUMN = {
105
+ mysql: "execution_lane VARCHAR(32) NOT NULL DEFAULT ''",
106
+ pg: `execution_lane VARCHAR(32) COLLATE "C" NOT NULL DEFAULT ''`,
107
+ };
108
+ function executionLaneColumnOf(raw, askId) {
109
+ const lane = typeof raw === "string" ? raw : "";
110
+ if (!isExecutionLane(lane)) {
111
+ throw new Error(`approval-ask-store: ask ${askId} carries execution_lane ${JSON.stringify(lane)}, which is not an execution lane — the execution placement switch migration backfills every row; refusing rather than guess the lane this ask was raised on`);
112
+ }
113
+ return lane;
114
+ }
102
115
  export const APPROVAL_BATCH_TABLE = "approval_batch";
103
116
  export const TIDB_APPROVAL_ASK_STATEMENTS = [
104
117
  `CREATE TABLE IF NOT EXISTS ${APPROVAL_BATCH_TABLE} (
@@ -118,6 +131,9 @@ export const TIDB_APPROVAL_ASK_STATEMENTS = [
118
131
  source_task_id VARCHAR(255) NOT NULL,
119
132
  session_id VARCHAR(64) NOT NULL,
120
133
  owner VARCHAR(190) NULL,
134
+ -- execution_lane:造这只问的那条执行的车道(审批出处带车道;ensureAsk 铸行时写,既有行命中不改)。DEFAULT '' 只为存量库一句加列
135
+ -- (一次性迁移加列并按根会话放置回填,迁移后没有 '' 行)。
136
+ ${APPROVAL_ASK_EXECUTION_LANE_COLUMN.mysql},
121
137
  batch_id VARCHAR(64) NOT NULL,
122
138
  tool_call_id VARCHAR(255) NOT NULL,
123
139
  -- leg_key: 车3 §3.2′ 身份轴——sha256(resume checkpoint token) 的 hex(64 字符),首腿存空串。
@@ -172,7 +188,7 @@ export const TIDB_APPROVAL_ASK_STATEMENTS = [
172
188
  UNIQUE KEY uq_approval_ask_idem (task_id, idempotency_key),
173
189
  KEY idx_ask_task_state (task_id, state),
174
190
  -- idx_ask_session_state: 车3 §5.1 的 sync 腿开流重放读口(listPendingBySession)的支撑索引。
175
- -- 表尚未进生产 + SCHEMA POLICY 是删库重建 ⇒ 此刻加索引零迁移成本,晚一车加就要动生产表。
191
+ -- 表尚未进生产 ⇒ 此刻加索引零迁移成本,晚一车加就要动生产表。
176
192
  KEY idx_ask_session_state (session_id, state),
177
193
  KEY idx_ask_batch (batch_id),
178
194
  KEY idx_ask_state (state)
@@ -206,6 +222,8 @@ export async function ensurePgApprovalAskSchema(q) {
206
222
  source_task_id VARCHAR(255) COLLATE "C" NOT NULL,
207
223
  session_id VARCHAR(64) COLLATE "C" NOT NULL,
208
224
  owner VARCHAR(190) COLLATE "C",
225
+ -- execution_lane —— MySQL twin 逐字同形(语义见那侧注)。
226
+ ${APPROVAL_ASK_EXECUTION_LANE_COLUMN.pg},
209
227
  batch_id VARCHAR(64) COLLATE "C" NOT NULL,
210
228
  tool_call_id VARCHAR(255) COLLATE "C" NOT NULL,
211
229
  -- leg_key: 车3 §3.2′ 身份轴——sha256(resume checkpoint token) 的 hex(64 字符),首腿存空串
@@ -258,18 +276,16 @@ export async function assertApprovalAskRuleMaterialSchema(query, dialect) {
258
276
  `persisted on the row too, so a replica that does not hold the waiting leg replays the approved bytes; every ask-row ` +
259
277
  `write and read names these columns unconditionally, so on this table the whole in-stream approval protocol degrades silently (no card ` +
260
278
  `is persisted, the replay list stays empty, late decisions 404) while /v1/capabilities still reports it active. ` +
261
- `This repository ships no ALTER TABLE migrations, so the table must be recreated: run ` +
262
- `\`DROP TABLE ${APPROVAL_ASK_TABLE}; DROP TABLE ${APPROVAL_BATCH_TABLE};\` on the ` +
263
- `${dialect === "pg" ? "PostgreSQL" : "MySQL-protocol"} backend and restart — the schema is recreated at boot. ` +
264
- `The cost is bounded: these two tables hold IN-FLIGHT approval asks (cards awaiting an answer) plus settled ` +
265
- `bookkeeping; the durable park face (checkpoint rows) and the persisted rules themselves are untouched, so an ` +
266
- `in-flight card falls back to the durable gate (\`POST /v1/approvals/:sessionId/decide\`). ` +
267
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}`);
279
+ `The table holds IN-FLIGHT approval asks (cards awaiting an answer) plus settled bookkeeping; the durable park face ` +
280
+ `(checkpoint rows) and the persisted rules themselves are untouched, so an in-flight card falls back to the durable gate ` +
281
+ `(\`POST /v1/approvals/:sessionId/decide\`) until the columns exist.\n` +
282
+ `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
283
+ (await describeProbedTableDrift(query, dialect, [APPROVAL_ASK_TABLE])));
268
284
  }
269
285
  }
270
286
  function askCols(dialect) {
271
287
  return [
272
- "ask_id, task_id, source_task_id, session_id, owner, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state",
288
+ "ask_id, task_id, source_task_id, session_id, owner, execution_lane, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state",
273
289
  "provisional, rev, decision",
274
290
  jsonColumnText(dialect, "decision_actor"),
275
291
  "decision_note, settled_by",
@@ -307,6 +323,7 @@ function mapAskRowBody(r, askId) {
307
323
  sourceTaskId: String(r.source_task_id),
308
324
  sessionId: String(r.session_id),
309
325
  owner: strOrNull(r.owner),
326
+ executionLane: executionLaneColumnOf(r.execution_lane, askId),
310
327
  batchId: String(r.batch_id),
311
328
  toolCallId: String(r.tool_call_id),
312
329
  legKey: String(r.leg_key),
@@ -410,14 +427,15 @@ export class SqlApprovalAskStore {
410
427
  try {
411
428
  await conn.begin();
412
429
  await conn.query(this.q(`INSERT IGNORE INTO ${APPROVAL_BATCH_TABLE} (batch_id, task_id, state, rev, created_at_ms, updated_at_ms) VALUES (?, ?, 'OPEN', 0, ?, ?)`, `INSERT INTO ${APPROVAL_BATCH_TABLE} (batch_id, task_id, state, rev, created_at_ms, updated_at_ms) VALUES ($1, $2, 'OPEN', 0, $3, $4) ON CONFLICT (batch_id) DO NOTHING`), [row.batchId, row.taskId, now, now]);
413
- const askInsert = await conn.query(this.q(`INSERT IGNORE INTO ${APPROVAL_ASK_TABLE} (ask_id, task_id, source_task_id, session_id, owner, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state, provisional, rev, decision, decision_actor, decision_note, settled_by, decided_at_ms, denied_reason, gate_token, gate_bound_call_id, gate_bound_input_hash, idempotency_key, card_json, rule_command, rule_scope_root, schema_version, expires_at_ms, created_at_ms, updated_at_ms) ` +
414
- "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'STREAM_PENDING', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ?, ?, ?, ?, ?, ?, ?)", `INSERT INTO ${APPROVAL_ASK_TABLE} (ask_id, task_id, source_task_id, session_id, owner, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state, provisional, rev, decision, decision_actor, decision_note, settled_by, decided_at_ms, denied_reason, gate_token, gate_bound_call_id, gate_bound_input_hash, idempotency_key, card_json, rule_command, rule_scope_root, schema_version, expires_at_ms, created_at_ms, updated_at_ms) ` +
415
- "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'STREAM_PENDING', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, $11, $12, $13, $14, $15, $16, $17) ON CONFLICT (ask_id) DO NOTHING"), [
430
+ const askInsert = await conn.query(this.q(`INSERT IGNORE INTO ${APPROVAL_ASK_TABLE} (ask_id, task_id, source_task_id, session_id, owner, execution_lane, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state, provisional, rev, decision, decision_actor, decision_note, settled_by, decided_at_ms, denied_reason, gate_token, gate_bound_call_id, gate_bound_input_hash, idempotency_key, card_json, rule_command, rule_scope_root, schema_version, expires_at_ms, created_at_ms, updated_at_ms) ` +
431
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'STREAM_PENDING', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ?, ?, ?, ?, ?, ?, ?)", `INSERT INTO ${APPROVAL_ASK_TABLE} (ask_id, task_id, source_task_id, session_id, owner, execution_lane, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state, provisional, rev, decision, decision_actor, decision_note, settled_by, decided_at_ms, denied_reason, gate_token, gate_bound_call_id, gate_bound_input_hash, idempotency_key, card_json, rule_command, rule_scope_root, schema_version, expires_at_ms, created_at_ms, updated_at_ms) ` +
432
+ "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, 'STREAM_PENDING', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, $12, $13, $14, $15, $16, $17, $18) ON CONFLICT (ask_id) DO NOTHING"), [
416
433
  row.askId,
417
434
  row.taskId,
418
435
  row.sourceTaskId,
419
436
  row.sessionId,
420
437
  row.owner ?? null,
438
+ row.executionLane,
421
439
  row.batchId,
422
440
  row.toolCallId,
423
441
  row.legKey,
@@ -3,6 +3,7 @@ import { pgSafeJsonStringify, pgSanitizeText } from "./pg-safe-json.js";
3
3
  import { isMysqlDupKeyError } from "./sql-errors.js";
4
4
  import { ensureIndexes } from "./ensure-index.js";
5
5
  import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, jsonColumnText } from "./sql-json-column.js";
6
+ import { backgroundAgentNameOf } from "../background-agent-name.js";
6
7
  export const BACKGROUND_AGENT_TABLE = "background_agent";
7
8
  function summaryCols(dialect) {
8
9
  return ("handle, owner, session_scoped, session_id, parent_session_id, root_session_id, name, agent_type, status, " +
@@ -11,12 +12,14 @@ function summaryCols(dialect) {
11
12
  const readAgentRecordBody = createObjectColumnReader();
12
13
  const readUsageBody = createObjectColumnReader();
13
14
  function readRecordColumn(v, handle) {
15
+ let record;
14
16
  try {
15
- return decodeJsonColumn(v, readAgentRecordBody, `${BACKGROUND_AGENT_TABLE}.record_json for handle ${handle}`);
17
+ record = decodeJsonColumn(v, readAgentRecordBody, `${BACKGROUND_AGENT_TABLE}.record_json for handle ${handle}`);
16
18
  }
17
19
  catch (e) {
18
20
  throw foldJsonColumn(e, (m) => `BackgroundAgentStore: ${m} — refusing rather than serve a hollow agent record`);
19
21
  }
22
+ return { ...record, name: backgroundAgentNameOf(record) };
20
23
  }
21
24
  function readUsageColumn(v, handle) {
22
25
  try {
@@ -45,7 +48,7 @@ function projParams(stored, json, text) {
45
48
  stored.parentSessionId ?? null,
46
49
  stored.rootSessionId ?? null,
47
50
  (stored.sessionScoped ? stored.owner : stored.parentSessionId) ?? null,
48
- stored.name !== undefined ? text(stored.name).slice(0, 255) : null,
51
+ text(backgroundAgentNameOf(stored)).slice(0, 255),
49
52
  stored.agentType !== undefined ? text(stored.agentType).slice(0, 190) : null,
50
53
  stored.status,
51
54
  stored.spawnedAt,
@@ -68,7 +71,7 @@ function rowToSummary(r) {
68
71
  ...(r.session_id !== null && r.session_id !== undefined ? { sessionId: String(r.session_id) } : {}),
69
72
  ...(r.parent_session_id !== null && r.parent_session_id !== undefined ? { parentSessionId: String(r.parent_session_id) } : {}),
70
73
  ...(r.root_session_id !== null && r.root_session_id !== undefined ? { rootSessionId: String(r.root_session_id) } : {}),
71
- ...(r.name !== null && r.name !== undefined ? { name: String(r.name) } : {}),
74
+ name: backgroundAgentNameOf({ handle: String(r.handle), ...(r.name !== null && r.name !== undefined ? { name: String(r.name) } : {}) }),
72
75
  ...(r.agent_type !== null && r.agent_type !== undefined ? { agentType: String(r.agent_type) } : {}),
73
76
  status: String(r.status),
74
77
  spawnedAt: Number(r.spawned_at_ms),
@@ -3,6 +3,7 @@ import type { OwnerAwareSessionStore, SessionListItem } from "../security.js";
3
3
  import type { StagingHandle } from "../session-sync-kernel.js";
4
4
  import { type PlacementAcquireOpts } from "./session-placement.js";
5
5
  import type { InstructionSourceStore } from "../instruction-source-notice.js";
6
+ import type { SessionWorkspaceStore } from "../session-workspace.js";
6
7
  import type { OwnerlessClaim } from "../delegated-session-owner.js";
7
8
  export interface CachingOptions {
8
9
  /** Idle seconds a woken session is kept warm. 0 disables caching (pure passthrough). */
@@ -53,7 +54,7 @@ export declare class CachingSessionStore implements SessionStore {
53
54
  private readonly onStaleHit?;
54
55
  /** core `SessionStore.headOf` —— 按 id 读一行(属主 / 住客 / 叶子)的唯一读口,**纯转发、不经暖缓存**(答的是行,绝不从暖缓存里那只会话的元数据答 ——
55
56
  * 缓存的会话活得比「另一只店删掉又重建了这一行」长)。内层店必有(本类的暖命中按它核新鲜)。🔴 必须转发:生产默认把真店包进本类,授权口 / 属主门 /
56
- * parked 两读 / k8s 工作区空间 / SSE 探针 / resume-at 读的都是本类。 */
57
+ * k8s 工作区空间 / SSE 探针 / resume-at 读的都是本类。 */
57
58
  readonly headOf: SessionHeadReader["headOf"];
58
59
  readonly register?: (sessionId: string, owner: string | null) => Promise<void>;
59
60
  /** [ref] 委派继承律的认领口,纯转发(写的是行上的属主那一格 —— 只 CAS 子行自己,不碰暖缓存里的会话;停驻挂点在默认部署上读的就是本类 —— 与 `placementOf` 同转发同缺:内店记录委派树 ⇒ 两只都在)。 */
@@ -71,12 +72,16 @@ export declare class CachingSessionStore implements SessionStore {
71
72
  q?: string;
72
73
  }) => Promise<SessionListItem[]>;
73
74
  readonly fork?: (sourceId: string, owner: string | null) => Promise<string | null>;
75
+ /** fork 续写的店契约(原样转发;与 fork 同理绕过暖缓存 —— 读写的是持久行,新 id 本来就不在缓存里)。 */
76
+ readonly forkTo?: OwnerAwareSessionStore["forkTo"];
77
+ /** 执行席(原样转发:放置是会话**行**上的一格,缓存只缓存会话日志的暖句柄,不缓存行格 —— 读与写都直达内层店)。 */
78
+ readonly executionPlacement?: OwnerAwareSessionStore["executionPlacement"];
74
79
  readonly deleteSession?: (sessionId: string, owner: string | null) => Promise<boolean>;
75
80
  /** The session row's list projection for one id + its writable bits (`title` / `pinned`) — DURABLE, pure pass-through
76
81
  * (they read / write `session_meta` / the local sidecars, never the warm cache). 🔴 Must forward (the retention / placements
77
82
  * lesson): the production default wraps the real store in this class, and the route's fact `sessionPatch` reads THIS object. */
78
83
  readonly sessionSummaryOf?: (sessionId: string) => Promise<SessionListItem | undefined>;
79
- readonly patchSessionMeta?: OwnerAwareSessionStore["patchSessionMeta"];
84
+ readonly writeSessionRowCells?: OwnerAwareSessionStore["writeSessionRowCells"];
80
85
  /** The auto-titler's NULL-only title write (SQL twins + local sidecar carry it; pure pass-through). */
81
86
  readonly setTitleIfNull?: (sessionId: string, title: string) => Promise<boolean>;
82
87
  readonly probeTitle?: (sessionId: string) => Promise<"none" | "untitled" | "titled">;
@@ -85,6 +90,10 @@ export declare class CachingSessionStore implements SessionStore {
85
90
  * 而装配点按「店上两个方法都在」判席 —— 不转发 = 默认部署上换源通告**静默不发**,且没有任何一处报错。 */
86
91
  readonly readInstructionSource?: InstructionSourceStore["readInstructionSource"];
87
92
  readonly recordInstructionSource?: InstructionSourceStore["recordInstructionSource"];
93
+ /** 会话行的位置与项目那一格(`session_meta.workspace` / local sidecar;`session-workspace.ts`),纯转发、不经暖缓存。
94
+ * 🔴 必须转发(同上一条教训):装配点与 resolveSpec 按「店上两个方法都在」判这一面 —— 不转发 = 默认部署上不记行、续跑腿不续声明项目,且没有任何一处报错。 */
95
+ readonly readSessionWorkspace?: SessionWorkspaceStore["readSessionWorkspace"];
96
+ readonly recordSessionWorkspace?: SessionWorkspaceStore["recordSessionWorkspace"];
88
97
  /** 2c session-sync — delegated export/import seam, DURABLE (bypasses the warm cache exactly like
89
98
  * list/fork: an export reads the full durable log, an import writes durable state). */
90
99
  readonly exportEntries?: (sessionId: string) => Promise<SessionTreeEntry[] | null>;
@@ -12,13 +12,17 @@ export class CachingSessionStore {
12
12
  claimOwnerless;
13
13
  listSessions;
14
14
  fork;
15
+ forkTo;
16
+ executionPlacement;
15
17
  deleteSession;
16
18
  sessionSummaryOf;
17
- patchSessionMeta;
19
+ writeSessionRowCells;
18
20
  setTitleIfNull;
19
21
  probeTitle;
20
22
  readInstructionSource;
21
23
  recordInstructionSource;
24
+ readSessionWorkspace;
25
+ recordSessionWorkspace;
22
26
  exportEntries;
23
27
  listEntryIds;
24
28
  exportEntriesStream;
@@ -55,8 +59,8 @@ export class CachingSessionStore {
55
59
  this.listSessions = ownerAware.listSessions.bind(inner);
56
60
  if (ownerAware.sessionSummaryOf)
57
61
  this.sessionSummaryOf = ownerAware.sessionSummaryOf.bind(inner);
58
- if (ownerAware.patchSessionMeta)
59
- this.patchSessionMeta = ownerAware.patchSessionMeta.bind(inner);
62
+ if (ownerAware.writeSessionRowCells)
63
+ this.writeSessionRowCells = ownerAware.writeSessionRowCells.bind(inner);
60
64
  if (ownerAware.setTitleIfNull)
61
65
  this.setTitleIfNull = ownerAware.setTitleIfNull.bind(inner);
62
66
  if (ownerAware.probeTitle)
@@ -65,8 +69,16 @@ export class CachingSessionStore {
65
69
  this.readInstructionSource = ownerAware.readInstructionSource.bind(inner);
66
70
  if (ownerAware.recordInstructionSource)
67
71
  this.recordInstructionSource = ownerAware.recordInstructionSource.bind(inner);
72
+ if (ownerAware.readSessionWorkspace)
73
+ this.readSessionWorkspace = ownerAware.readSessionWorkspace.bind(inner);
74
+ if (ownerAware.recordSessionWorkspace)
75
+ this.recordSessionWorkspace = ownerAware.recordSessionWorkspace.bind(inner);
68
76
  if (ownerAware.fork)
69
77
  this.fork = ownerAware.fork.bind(inner);
78
+ if (ownerAware.forkTo)
79
+ this.forkTo = ownerAware.forkTo.bind(inner);
80
+ if (ownerAware.executionPlacement)
81
+ this.executionPlacement = ownerAware.executionPlacement;
70
82
  if (ownerAware.exportEntries)
71
83
  this.exportEntries = ownerAware.exportEntries.bind(inner);
72
84
  if (ownerAware.listEntryIds)
@@ -3,6 +3,7 @@ import type { Pool as PgPool } from "pg";
3
3
  import { type ExecutionOutcomeRecordWord, type GateOutcome, type Checkpoint, type CheckpointGate, type CheckpointState, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type PendingSteerInput, type ResumeOutcome, type ResolvedOutcome, type ResolveExpectation, type ReopenReason, type RiskDescriptor, type StoreDurability, type StoreFidelity, type ToolApprovalCard } from "@sema-agent/core";
4
4
  import { type ParkRowFacts, type RuleOfferProjection, type SkillCardFacts } from "../approval-card.js";
5
5
  import { type SqlDialect, type SqlDriver } from "./sql-driver.js";
6
+ import type { RetentionDeclaration } from "@sema-agent/core";
6
7
  import type { SealedCheckpointCtx } from "../http/checkpoint-ctx.js";
7
8
  /** ctx 行改写店腿的读数(两孪生同形):扫到的可读行 · 回写成的行 · 回写成的行里改写函数报的改动数 · 读不出的行 · 改写之后一行装不下的行。 */
8
9
  export interface CtxRowsRewrite {
@@ -239,8 +240,8 @@ export declare function pendingRowPrecedes(a: {
239
240
  *
240
241
  * 🔴 为什么是一张表而不是每列一个 `assert*ColumnSchema` 函数([ref] 归一):[ref]① 那次是一列一函数,
241
242
  * 本批 [ref] 又要加两列 —— 照抄就是第二、第三份三十行的同形代码,而它们**唯一的差别只有列名与那句话**。
242
- * 探测法(恒空 `WHERE 1=0` 读 + 方言缺列码)、失败方向(只在**可证**缺列时才给删表指路)、恢复动作
243
- * (DROP TABLE checkpoint 的代价交代)三者对整张表是同一条,所以规则只留一条,列变成数据:下一次加列
243
+ * 探测法(恒空 `WHERE 1=0` 读 + 方言缺列码)、失败方向(只在**可证**缺列时才拒)、恢复动作
244
+ * (部署者按拒因打印的 ALTER 在原表上加列;表里是在飞的停驻,不动它)三者对整张表是同一条,所以规则只留一条,列变成数据:下一次加列
244
245
  * 只加一行,不再多一条规则。
245
246
  */
246
247
  export declare const CHECKPOINT_ADDED_COLUMNS: ReadonlyArray<{
@@ -253,14 +254,15 @@ export declare const CHECKPOINT_ADDED_COLUMNS: ReadonlyArray<{
253
254
  * 依据是本仓 assert*Schema 探针族的真先例与失败方向本身)。[ref] 起判据对 {@link CHECKPOINT_ADDED_COLUMNS}
254
255
  * **整张列册**跑(见那张表的头注:归一的理由)。
255
256
  *
256
- * 病:本仓不发 `ALTER TABLE`(SCHEMA POLICY:改列就改 CREATE + 删库重建)。一台**没删表**就升上来的
257
+ * 病:本仓不发 `ALTER TABLE`(SCHEMA POLICY:代码是唯一真源,存量库缺列由部署者按拒启句打印的 ALTER 自己执行)。一台**没跟上**就升上来的
257
258
  * 部署,`CREATE TABLE IF NOT EXISTS` 对存量 `checkpoint` 是空操作 —— 后加的列不在,而写口与读口
258
259
  * **无条件**引用它们。没有这道断言,服务照常过启动与健康检查,直到运行期才响,且首撞点在用户的
259
260
  * 审批 / resume 路径上。
260
261
  *
261
262
  * 判据 = **能力探测**(恒空的 `WHERE 1=0` 读)+ **方言的缺列错误码**({@link isMissingColumnError},
262
- * 判据属主 `sql-errors.ts`);其余错误**原样 rethrow**,一个字都不加工 —— 一句破坏性的「去 DROP TABLE」
263
- * 指路比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。
263
+ * 判据属主 `sql-errors.ts`);其余错误**原样 rethrow**,一个字都不加工 —— 把一次瞬时故障诊断成「缺列」的拒因
264
+ * 比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link describeProbedTableDrift}),
265
+ * 没有销毁性的一条:这张表里是在飞的停驻,出路是原表上加列。
264
266
  *
265
267
  * 位置:与三条先例同款,不在 `ensureSchema` 里(那条通道的契约是「只发 CREATE」),放在 boot 的
266
268
  * store 装配段、路由装配之前;**无条件**跑(与 [ref] tool_result 探针同档)——checkpoint 表在两方言的
@@ -464,9 +466,9 @@ export declare class SqlCheckpointStore implements CheckpointStore {
464
466
  protected readonly logger?: {
465
467
  info?(msg: string, meta?: unknown): void;
466
468
  } | undefined;
467
- /** [ref] 车1:托管留存声明。读法与「为什么 SQL 店答 managed 而三方法的实现体在 `retention-store-sql.ts`」
468
- * 逐字见 {@link MANAGED_RETENTION}(本店的 `checkpoint`/`checkpoint_ctx` 两表由那只聚合店按期清)。 */
469
- readonly retention: import("@sema-agent/core").RetentionDeclaration;
469
+ /** 托管留存声明 = 装配交给它的词(后端按扫车道开不开给;不给 ⇒ `"none"`)。读法与「为什么 SQL 店答 managed 而三方法的实现体在
470
+ * `retention-store-sql.ts`」逐字见 {@link MANAGED_RETENTION}(扫车道开着时本店的 `checkpoint`/`checkpoint_ctx` 两表由那只聚合店按期清)。 */
471
+ readonly retention: RetentionDeclaration;
470
472
  /**
471
473
  * `CheckpointStore.durability` 声明([ref] 欠账,[ref] 件5)—— 行落在 MySQL-protocol / PostgreSQL 的
472
474
  * `checkpoint` 表里,进程重启、副本轮换、整机重建都不丢 ⇒ `"durable"`,如实。
@@ -500,25 +502,13 @@ export declare class SqlCheckpointStore implements CheckpointStore {
500
502
  * `wiring-governance-operator.test.ts` 的 [ref] 组;已上报上游求一个能表达该宽度的词。
501
503
  */
502
504
  readonly fidelity: StoreFidelity;
503
- /**
504
- * `CheckpointStore.redecision` 声明(core 5.22.0 F-012 L2/L3;[ref] clay 裁放行)——**如实按能力判**:
505
- * 本店的 {@link SqlCheckpointStore.reopen} 是真 CAS 实现(`resolved`→`pending` 原子翻回 + `reopen_reason`
506
- * 权威列,[ref] D-1 的两条重开腿都走它),声明 `reopen: true` 是读数不是抬举。core 的沙箱准入
507
- * 模式(pre-flight 要求 `redecision.reopen === true`,declaration never duck-typing——方法在场不算数)
508
- * 由这一格武装:Kata 腿(`capabilities.isolation` 真声明)+ durable park 部署下,`sandbox_local` ask
509
- * 自动放行并以 `permission.sandbox_admitted` durable 事件披露(缺席披露=core 侧变异恰红)。
510
- * `validatingLease` **不声明**:本店没有 durable validating-lease 纪律,不承诺没有的东西。
511
- * 翻向钉:`wiring-governance-operator.test.ts`(修前恰红,与本声明同 commit)。
512
- */
513
- readonly redecision: {
514
- readonly reopen: true;
515
- };
516
505
  /**
517
506
  * `CheckpointStore.execution` 声明(core 7.7.0 [ref];[ref])——**必填**,且必须恰是 `{ outcome: true }`:
518
507
  * 店在此承诺 {@link SqlCheckpointStore.recordExecutionOutcome} 是契约描述的那条真 CAS(谓词、四词、
519
- * 冲突抛),且 {@link SqlCheckpointStore.reopen} 认这条记录。与 `durability`/`fidelity`/`redecision`
520
- * 三格同一条「声明而非鸭子判」姿势,区别只在这一格**不可缺席**:缺席的店会被 core 在 store 席的
521
- * 第一次读(`resolveCheckpointStore`)以 `config.invalid_checkpoint_store` 拒启 —— 那是契约的正确行为,
508
+ * 冲突抛),且 {@link SqlCheckpointStore.reopen} 认这条记录。与 `durability`/`fidelity` 两格同一条
509
+ * 「声明而非鸭子判」姿势,区别只在这一格**不可缺席**:缺席的店(以及缺 `reopen` 动词的店 —— core 7.57.0 起它是必备
510
+ * 动词,原先的 `redecision` 声明格随之退役)会被 core 在 store 席的第一次读(`resolveCheckpointStore`)以
511
+ * `config.invalid_checkpoint_store` 拒启、句子点名缺的每一个成员 —— 那是契约的正确行为,
522
512
  * 因为一台记不下「已决动作最后被怎么处置」的部署,它手里每一条 resolved 行在这条轴上都永远读不出来。
523
513
  */
524
514
  readonly execution: {
@@ -526,7 +516,9 @@ export declare class SqlCheckpointStore implements CheckpointStore {
526
516
  };
527
517
  constructor(db: SqlDriver, logger?: {
528
518
  info?(msg: string, meta?: unknown): void;
529
- } | undefined);
519
+ } | undefined, opts?: {
520
+ readonly retention?: RetentionDeclaration;
521
+ });
530
522
  /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
531
523
  private q;
532
524
  /** JSON column binding. TiDB: plain `JSON.stringify`, stored verbatim. PG: `pgProtocolJsonStringify` —
@@ -891,12 +883,16 @@ export declare class SqlCheckpointStore implements CheckpointStore {
891
883
  export declare class TiDBCheckpointStore extends SqlCheckpointStore {
892
884
  constructor(pool: MySqlPool, logger?: {
893
885
  info?(msg: string, meta?: unknown): void;
886
+ }, opts?: {
887
+ readonly retention?: RetentionDeclaration;
894
888
  });
895
889
  }
896
890
  /** PostgreSQL binding — historical class name + ctor shape preserved. */
897
891
  export declare class PgCheckpointStore extends SqlCheckpointStore {
898
892
  constructor(pool: PgPool, logger?: {
899
893
  info?(msg: string, meta?: unknown): void;
894
+ }, opts?: {
895
+ readonly retention?: RetentionDeclaration;
900
896
  });
901
897
  }
902
898
  //# sourceMappingURL=checkpoint-store-sql.d.ts.map
@@ -9,7 +9,8 @@ import { recordFailOpen } from "../observability/fail-open.js";
9
9
  import { durableRowIdentity, withoutCheckpointToken } from "../checkpoint-identity.js";
10
10
  import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "./sql-driver.js";
11
11
  import { isDupKeyError, isMissingColumnError, rowTooLargeOrSelf, StoreRowTooLargeError } from "./sql-errors.js";
12
- import { MANAGED_RETENTION } from "./retention-store-sql.js";
12
+ import { describeProbedTableDrift } from "./schema-drift-sentence.js";
13
+ import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "./retention-store-sql.js";
13
14
  import { approvalPayloadFingerprint } from "./approval-payload-fingerprint.js";
14
15
  const CTX_REWRITE_PAGE = 500;
15
16
  export { approvalPayloadFingerprint } from "./approval-payload-fingerprint.js";
@@ -61,13 +62,10 @@ export async function assertCheckpointColumnSchema(query, dialect) {
61
62
  throw err;
62
63
  throw new Error(`checkpoint is missing the column ${column} — refusing to start. ` +
63
64
  `On this table ${breaksAt} — at runtime, after a healthy-looking boot. ` +
64
- "This repository ships no ALTER TABLE migrations, so the table must be recreated: run " +
65
- "`DROP TABLE checkpoint;` on the " +
66
- (dialect === "pg" ? "PostgreSQL" : "MySQL-protocol") +
67
- " backend and restart — the schema is recreated at boot. The cost is bounded but real: the table holds IN-FLIGHT " +
68
- "durable parks (suspended approvals/reviews awaiting a decision) — those cards are lost and their runs must be " +
69
- "re-triggered; `checkpoint_ctx`, run rows and every other persistent face are untouched. " +
70
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}`);
65
+ "The table holds IN-FLIGHT durable parks (suspended approvals/reviews awaiting a decision), so the way forward is an in-place " +
66
+ "ALTER that keeps them; `checkpoint_ctx`, run rows and every other persistent face are untouched.\n" +
67
+ `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
68
+ (await describeProbedTableDrift(query, dialect, ["checkpoint"])));
71
69
  }
72
70
  }
73
71
  }
@@ -336,14 +334,14 @@ const EXECUTION_RECORD_CAS_ATTEMPTS = 8;
336
334
  export class SqlCheckpointStore {
337
335
  db;
338
336
  logger;
339
- retention = MANAGED_RETENTION;
337
+ retention;
340
338
  durability = "durable";
341
339
  fidelity = "json";
342
- redecision = { reopen: true };
343
340
  execution = { outcome: true };
344
- constructor(db, logger) {
341
+ constructor(db, logger, opts = {}) {
345
342
  this.db = db;
346
343
  this.logger = logger;
344
+ this.retention = opts.retention ?? UNMANAGED_RETENTION;
347
345
  }
348
346
  q(tidb, pg) {
349
347
  return this.db.dialect === "tidb" ? tidb : pg;
@@ -798,13 +796,13 @@ export class SqlCheckpointStore {
798
796
  }
799
797
  }
800
798
  export class TiDBCheckpointStore extends SqlCheckpointStore {
801
- constructor(pool, logger) {
802
- super(mysqlDriver(pool), logger);
799
+ constructor(pool, logger, opts) {
800
+ super(mysqlDriver(pool), logger, opts);
803
801
  }
804
802
  }
805
803
  export class PgCheckpointStore extends SqlCheckpointStore {
806
- constructor(pool, logger) {
807
- super(pgDriver(pool), logger);
804
+ constructor(pool, logger, opts) {
805
+ super(pgDriver(pool), logger, opts);
808
806
  }
809
807
  }
810
808
  //# sourceMappingURL=checkpoint-store-sql.js.map