@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,9 +1,12 @@
1
1
  import { FileSessionRepo, type AcquiredSession, type SessionHead, type SessionStore, type SessionRepo, type SessionTreeEntry } from "@sema-agent/core";
2
- import type { SessionRunRollupSource, SessionSummary } from "./store-contracts.js";
3
- import type { SessionRowPatch } from "../session-row.js";
2
+ import type { SessionRunClaimSource, SessionRunRollupSource, SessionSummary } from "./store-contracts.js";
3
+ import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
4
4
  import { type Logger } from "../observability/logger.js";
5
5
  import { type SessionFace } from "../session-incarnation.js";
6
+ import { type ExecutionPlacementSeat, type LocalPlacementBackfillSeat } from "../execution-placement.js";
7
+ import { type ForkTarget, type ForkToOutcome } from "./session-fork.js";
6
8
  import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
9
+ import type { SessionWorkspace } from "../session-workspace.js";
7
10
  import { type StagedCommitOptions, type StagingHandle, type SyncRelation } from "../session-sync-kernel.js";
8
11
  /**
9
12
  * local 车道会话存档的**唯一 repo 出口**:本仓里每一只读 `<root>/sessions/*.jsonl` 的 `FileSessionRepo` 都从这里取(`LocalSessionStore` 的底座与会话视图口
@@ -53,8 +56,8 @@ export declare class LocalSessionStore implements SessionStore {
53
56
  /** Absolute path to the lastActivity JSON sidecar, or undefined when not persisting (in-memory-only deployment). */
54
57
  private readonly sidecarPath?;
55
58
  /** Title map (`session-titles.json`) — the local twin of the SQL `session_meta.title` column: one cell, two writers
56
- * (the user's {@link patchSessionMeta}, unconditional; the auto-titler's {@link setTitleIfNull}, fills an absent cell
57
- * only — writer order: `src/session-row.ts`). Separate file from the lastActivity sidecar ON PURPOSE (that format is a
59
+ * (the user's {@link writeSessionRowCells}; the auto-titler's {@link setTitleIfNull}, fills an absent cell
60
+ * only — writer order: `src/session-row.ts`; both under the session write lock). Separate file from the lastActivity sidecar ON PURPOSE (that format is a
58
61
  * #10 clobber area); ultra-low-rate. Cross-instance sharing: see {@link SidecarMap}. */
59
62
  private readonly titles;
60
63
  /** Pin map (`session-pinned.json`, {@link readPinnedSidecar}) — the local twin of `session_meta.pinned`. Its own file for
@@ -64,6 +67,9 @@ export declare class LocalSessionStore implements SessionStore {
64
67
  * run selected, encoded like the SQL twins' `session_meta.instruction_source` column ('' = none; missing key = never
65
68
  * recorded). Its own file for the same reason titles have theirs: one small map per concern, no format coupling. */
66
69
  private readonly instructionSources;
70
+ /** 会话行的位置与项目那一格(`session-workspaces.json`)—— SQL 孪生 `session_meta.workspace` 的 local 对位:`{ [sessionId]: 那一格的文本 }`,
71
+ * 文本与 SQL 列同一只编解码(`session-workspace-cell.ts`,一格读不出 ⇒ 读作没有记录并计一次)。缺键 = 没有记录。一事一档,理由同 titles。 */
72
+ private readonly workspaces;
67
73
  /**
68
74
  * 会话元数据(`session-incarnations.json`,一格的形与两种读法见 {@link SessionMetaCell}):**属主与化身的唯一真源**,SQL 孪生
69
75
  * `session_meta` 行上 `owner` / `incarnation` 两列在本店的对位。两样都**必须落盘**:
@@ -78,13 +84,27 @@ export declare class LocalSessionStore implements SessionStore {
78
84
  /** 认领行(`session_owner_claimed`)的出口 —— 一次性迁移 M10 的认领臂专用(窗口见 `plugins/one-time-migrations.ts` 登记表)。 */
79
85
  private readonly log;
80
86
  /**
81
- * 按会话的写锁(表见 {@link SESSION_WRITE_LOCKS_BY_ROOT})—— 会话行两位的**每一笔**写(住客判 → 边档草稿 → 落盘 → 发布)与同一 id 上住客的**每一次**换人
87
+ * 按会话的写锁(表见 {@link SESSION_WRITE_LOCKS_BY_ROOT})—— 会话行两位的**每一笔**写(用户的行格写原语:判 → 边档草稿 → 落盘 → 发布;自动标题器的
88
+ * `setTitleIfNull`:见格在就不写)与同一 id 上住客的**每一次**换人
82
89
  * (删除:存档 + 三只行边档 + 元数据格;按 id 建会话:打开即建、登记;导入:铸新住客;换装:删后插回;staged 导入的提交)排在同一条链上:判过的住客
83
90
  * 在那一笔发布完之前不会被换掉,删除不会落在「判过、未发布」之间,同 id 的新住客也不会在删除做到一半时生出来、再被它的收尾抹掉。只有这一把;
84
91
  * SQL 孪生是一条带化身条件的 UPDATE、一条事务里的删除,没有这个窗,不需要它。按 id 的 acquire(打开 / 首建)也在这条链上:交出的会话挂的住客与打开
85
92
  * 同一节读到(见 `load`)。新 id 的建会话(不带 id 的 acquire、fork)不与任何人争同一 id,不上锁。
86
93
  */
87
94
  private readonly sessionWriteLocks;
95
+ /**
96
+ * 执行席(规则在 `src/execution-placement.ts`):读 = 那一格的 `execution` 位(fork 创建的格而存档不在 ⇒ `forking`);place / markMirror 在这只会话的写锁里判住客
97
+ * (化身 + 头读投影的属主 `owner ?? null`)再只改 `execution` 那一位 —— 住客换了(删后重建、M10 窗内认领)⇒ `gone`,不写。
98
+ * 认领(run 店的单活槽,{@link SessionRunClaimSource}):`markMirror` 在同一把写锁里读它、只拦「absent → mirror」这一笔(判序与 SQL 孪生同,见 {@link markMirrorExecution});`place` 是认领持有者自己的初始放置写,不读认领。
99
+ * local 的导入三写口不读认领 —— 它们的认领判在路由(第一阶段写前判 + 提交前复判),没有 SQL 孪生那样的写事务内读。
100
+ */
101
+ readonly executionPlacement: ExecutionPlacementSeat;
102
+ /**
103
+ * 一次性迁移 M32 的 local 腿用的席(随 M32 · 窗口见 `one-time-migrations.ts` 登记表):`read` 与 {@link executionPlacement} 的 `read` 是**同一读**({@link placementRead}),只差「这只会话的存档读不出」这一形
104
+ * (格缺而探针读不出;fork 创建的格而探针说不出在不在)—— 席的读照旧抛那只故障,本席答 {@link UnusableArchive}(开机回填不动存档、只补一格放置:读不出的会话由回填点名跳过,不拒启)。
105
+ * `place` 就是席的 `place`。
106
+ */
107
+ readonly executionPlacementBackfill: LocalPlacementBackfillSeat;
88
108
  /** @param repo the core SessionRepo this store projects into the §0.5 SessionStore shape.
89
109
  * @param runs the run store whose per-session projection is the ONLY source of a row's `runCount` / `lastStatus` / `lastRunId` /
90
110
  * `lastTaskRunId` (LocalBackend passes its `FileRunStore`). Required — a run-less local session store does not exist.
@@ -93,9 +113,41 @@ export declare class LocalSessionStore implements SessionStore {
93
113
  * restart (the file-backed deployment); omit it for the in-memory deployment (nothing survives).
94
114
  * @param opts.log the `session_owner_claimed` line's sink (LocalBackend passes a logger at the deployment's LOG_LEVEL;
95
115
  * absent ⇒ an info-level JSON-line logger — a claim is never silent). */
96
- constructor(repo: HeadReadingSessionRepo, runs: SessionRunRollupSource, dataDir?: string, opts?: {
116
+ constructor(repo: HeadReadingSessionRepo, runs: SessionRunRollupSource & SessionRunClaimSource, dataDir?: string, opts?: {
97
117
  log?: Pick<Logger, "info">;
98
118
  });
119
+ /**
120
+ * 这一格此刻是不是 `forking`(fork 创建的格 {@link isForkCreatedCell} ∧ 存档不在)。存档在不在只问 repo 的唯一探针 {@link probeArchive} 的「不在」—— **只问有没有**:存档在而用不了(会话头读不出 / 截断)
121
+ * 是「在」,内容读不读得出归真正读内容的那一读(门之后、体之前读一次叶子,读不出 ⇒ `corrupt_session`)。所以带键的会话与不带键的会话在执行席上同形,坏档照样能被换装修好
122
+ * (换装是修坏档的唯一路径)。**唯一的例外是探针说不出在不在**(`storage` 词:读的时候抛了别的故障)⇒ 不替它答 forking 与否:{@link forkingOf} 把那只故障上抛(要对这只会话动手的读者 ——
123
+ * 写口的判、登记、缺档新建臂 —— 看到它就抛),{@link forkingRead} 交出 {@link UnusableArchive}(执行席的读上抛它,回填席点名它)。持这只会话的写锁时读数稳定(改存档在不在的写者都持这把锁);锁外是一次观察。
124
+ */
125
+ private forkingRead;
126
+ /** {@link forkingRead} 的上抛一形(与 {@link metaOf} 之于 {@link metaRead} 同一条规矩):探针说不出在不在 ⇒ 抛那只故障、原样。 */
127
+ private forkingOf;
128
+ /**
129
+ * 执行放置的读**本体**(一次:那一格 + 必要时的存档探针;两只席的读共用这一只)。行不在 ⇒ `undefined`;盘上有会话而格缺 ⇒ 补铸(与 {@link headOf} 同一只 {@link metaRead});
130
+ * 存档读不出({@link UnusableArchive}:格缺而探针读不出 ⇒ 不补铸;fork 创建的格而探针说不出在不在 ⇒ {@link forkingRead} 交出)⇒ 交出这只 {@link UnusableArchive}。
131
+ * 上抛还是点名,归两只席各自的读者({@link readExecutionPlacement} 上抛那只故障;{@link executionPlacementBackfill} 原样交出)。
132
+ */
133
+ private placementRead;
134
+ /** 执行席的读(core 席的合同:读故障抛,不吞成 absent):{@link placementRead} 的本体,存档读不出 ⇒ 上抛那只故障、原样(与 {@link metaOf} 同一条规矩:要动存档的读者看到读不出就抛)。 */
135
+ private readExecutionPlacement;
136
+ /** place(锁内):判官 {@link settledPlaceOutcome} 在写链的草稿上判(与登记的防 clobber 同一处),可写 ⇒ 只改 `execution` 位。 */
137
+ private placeExecution;
138
+ /**
139
+ * identical 臂补 mirror(锁内):先在这只会话的写锁里读 run 认领(行在才读;SQL 孪生:写事务内锁根行后读 `task_active`,根行不在 ⇒ 不读),再交判官 {@link settledMirrorOutcome};
140
+ * 判官的答案先于认领 —— 行不在 / 住客不同 ⇒ `gone`、writer ⇒ `writer`、已是 mirror ⇒ `mirrored`(幂等零写)都不被认领改写;只有「absent → mirror」这一笔写
141
+ * 遇上认领 ⇒ `active`(带那只认领的 task id,格不写)。可写且无认领 ⇒ 只改 `execution` 位为 mirror。
142
+ */
143
+ private markMirrorExecution;
144
+ /**
145
+ * 执行位两只写口的锁内一形(调用方持这只会话的写锁):在写链的草稿上读出这一格(`forking` 在锁内先判 —— 改存档在不在的写者都持这把锁)、交判官,
146
+ * 判官给 `execution` ⇒ 只改那一位。判官 / 解码抛的错**不在写链里抛**(写链一节抛了会把共享链留成拒态):收下、写链这一节不写、出链后原样上抛。
147
+ */
148
+ private settleExecutionHeld;
149
+ /** 已发布的那一格(同步读;调用方先 `meta.ensureLoaded()`)。 */
150
+ private metaCellOf;
99
151
  /**
100
152
  * 把 `sessionId` 那一格写成 `next(before)` 的结果并**落盘**(写链里在草稿上判、写、落盘后才发布给读者 —— 与 instruction-source 同一只 `commit`;
101
153
  * 判据建立在每一节之前的盘上状态上,所以防 clobber 的「已记下就不动」写在 `next` 里,不写在链外)。`next` 返回 `undefined` = 不改。
@@ -110,7 +162,15 @@ export declare class LocalSessionStore implements SessionStore {
110
162
  * 不可重入 —— 锁里的调用方只调 `…Held` 那一形(它们假定锁已在手),不再回到上锁的公开口。
111
163
  */
112
164
  private withSessionWriteLock;
113
- /** 盘上有没有这只会话(repo 的唯一探针;`open` 读整只存档,所以只在边档里没有那一格时才走到这里)。 */
165
+ /**
166
+ * 盘上这只会话的存档此刻是什么状态(repo 的**唯一探针**;`open` 读整只存档,所以只在边档里没有那一格、或要判 `forking` 时才走到这里)。三种答案:
167
+ * 不在(`not_found`)· 在而读得出 · 读不出({@link UnusableArchive}:`corrupt_session` / `invalid_session` = 在而内容用不了;`storage` = 读的时候抛了别的故障,I/O 级,
168
+ * 说不出在不在 —— 词的读法见 {@link archiveFaultWord})。探针**不吞也不改**那只故障:{@link UnusableArchive.fault} 就是抛出来的那一只,每个读者各自决定上抛还是点名 ——
169
+ * 要对这份存档动手的({@link existsInRepo};{@link metaOf};{@link readExecutionPlacement})一律上抛它;一次性迁移 M32 的回填({@link executionPlacementBackfill})经
170
+ * {@link metaRead} / {@link forkingRead} 收下它、点名跳过,不上抛。只问有没有的 {@link forkingRead} 把 `corrupt_session` / `invalid_session` 读作「在」(存档找到了),`storage` 不替它答。
171
+ */
172
+ private probeArchive;
173
+ /** 盘上有没有一份**读得出**的这只会话的存档(补铸 / 登记 / 导入的落点判 / fork 目标这些要对存档动手的读者用):用不了的存档照旧上抛它的故障。 */
114
174
  private existsInRepo;
115
175
  /**
116
176
  * 一只会话的元数据 —— 属主与化身的**唯一读口**(按 id 读 {@link headOf} / 删除与换装的属主门都经它)。`undefined` = 无此会话。
@@ -118,6 +178,12 @@ export declare class LocalSessionStore implements SessionStore {
118
178
  * ⇒ 补铸一格只有化身的(属主没有事实可写,不伪造)并落盘后再答 —— 之后的读都命中那一格,不再回 repo。
119
179
  */
120
180
  private metaOf;
181
+ /**
182
+ * {@link metaOf} 的本体(三态,不抛「用不了」):边档里有那一格 ⇒ 读它;没有 ⇒ 问**唯一探针** {@link probeArchive} 一次 —— 不在 ⇒ 无此会话;在而读得出 ⇒ 补铸({@link cellOfPresent});
183
+ * 读不出 ⇒ {@link UnusableArchive}(**不补铸**:不替一份读不出的存档铸化身)。要动存档的读者({@link metaOf})把它上抛;不动存档、只要「有没有」的读者
184
+ * ({@link executionPlacementBackfill})原样收下。
185
+ */
186
+ private metaRead;
121
187
  /**
122
188
  * 一只**已证明在盘上**的会话的那一格:有 ⇒ 读它;没有(7.104.0 之前写下、此后没被读过)⇒ 补铸一格只有化身的(属主没有事实可写,不伪造)
123
189
  * 并落盘后再答。证明由调用方给:{@link metaOf} 探过 repo;acquire 的打开腿在这只会话的写锁里刚打开它({@link openForItselfHeld})。
@@ -146,21 +212,35 @@ export declare class LocalSessionStore implements SessionStore {
146
212
  probeTitle(sessionId: string): Promise<"none" | "untitled" | "titled">;
147
213
  /** The auto-titler's write (the local twin of the SQL setTitleIfNull): lands only on an absent cell — a title already
148
214
  * there (the user's included) wins. Persists the whole (small) map via a SERIALIZED tmp+rename snapshot; a write
149
- * failure keeps the in-memory title (degrades to re-title after restart). */
215
+ * failure keeps the in-memory title (degrades to re-title after restart). Runs under this session's write lock — the
216
+ * user's conditional write ({@link writeSessionRowCells}) judges its row tag and writes under the same lock, so the two
217
+ * writers of the title cell never interleave between a judgement and its write (the SQL twins get this from the row lock). */
150
218
  setTitleIfNull(sessionId: string, title: string): Promise<boolean>;
151
219
  /**
152
- * 会话行的可写位 —— 用户标题 / 置顶的**唯一**写口(SQL 孪生 `patchSessionMeta` 的 local 对位;写者次序见 `src/session-row.ts`)。
153
- * 只写列表面上的会话(会话仓里有它),且只写调用方验过的那一位住客(`face.incarnation` 与会话元数据那一格的化身相等 —— SQL 孪生的
154
- * `AND incarnation = ?`)。住客判、两只边档的草稿 → 落盘 → 发布、读回,**整段在这只会话的写锁里**({@link withSessionWriteLock}):删除 / 导入 /
155
- * 换装同锁,判过的住客在这一笔发布完之前换不掉 —— 所以住客只判一次,读回的就是那一位的行;判不过(期间被删 / 被谁重新登记)⇒ `undefined`,什么都没写。
156
- * `title` 串 ⇒ 无条件写进那一格(自动标题器的 {@link setTitleIfNull} 见格在就不写 ⇒ 用户那笔恒留下);`title: null` ⇒ 删那一格(清回自动标题);
220
+ * 会话行格写原语 —— 用户标题 / 置顶的**唯一**写口(SQL 孪生 `writeSessionRowCells` 的 local 对位;规则与判序:`src/session-row.ts` 头注「写原语」)。
221
+ * **整段在这只会话的写锁里**({@link withSessionWriteLock}):读出锁住的那一行(会话元数据那一格的属主 + 化身 + 执行位、列表面上有它、两只边档的格)→
222
+ * 同一只判官 `judgeSessionRowWrite`(行期望 → 写者身份 → 标签)→ 写 → 应答行(同锁内另读,经 `settleSessionRowWrite` 复核住客)。删除 / 导入 /
223
+ * 换装 / 登记 / 自动标题器同锁,判过的那一行在这一笔发布完之前换不掉。行期望带 `log` 时读这只会话存档的 mark(存档的追加不走这把锁 —— 那一维在 local 上是一次观察)。
224
+ * `title` 串 ⇒ 写进那一格(自动标题器的 {@link setTitleIfNull} 见格在就不写 ⇒ 用户那笔恒留下);`title: null` ⇒ 删那一格(清回自动标题);
157
225
  * `pinned:true` 写 `true`、`false` 删键。
158
- * 落不了盘 ⇒ 抛、内存不动(用户的一笔写,不做「内存里先记着」的降级)。⚠️ 两键同一体时是两只文件、两次落盘:第二只落不了时第一只已在盘上
159
- * (SQL 孪生是一条 UPDATE);抛出的错点名哪一只,重发同一体收敛。`lastActivity` 不动(改名不是活动 —— 列表的新近序不变)。
226
+ * local 的保证是「互斥 + 先判后写」,**不是多文件原子提交**:落不了盘 ⇒ 抛、那一只边档的内存不动;两键同一体时是两只文件、两次落盘,第二只落不了时第一只
227
+ * 已在盘上、标签已变(SQL 孪生是一条 UPDATE)—— 恢复 = 读当前行(或下一次带旧标签得 `changed` 的应答行)按它重发,收敛。`lastActivity` 不动(改名不是活动)。
228
+ * 重载按入口分:`replay` 的结局只有 `written` / `absent`。
229
+ */
230
+ writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ClientSessionRowWrite): Promise<SessionRowWriteOutcome<SessionSummary>>;
231
+ writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ReplaySessionRowWrite): Promise<ReplaySessionRowWriteOutcome<SessionSummary>>;
232
+ /**
233
+ * 写原语锁住的那一行(调用方持这只会话的写锁并已 {@link loadRowSidecars}):会话元数据那一格(属主 `owner ?? null` —— 与头读、列表行同读;化身经
234
+ * `incarnationFromCell` 的 `absent` 读法;执行位 `mirror`)+ 列表面上有它(会话仓里有;`forking` 中间态的存档不在 ⇒ 不在列表上)+ 两只边档的格。
235
+ * 没有那一格或不在列表面上 ⇒ `undefined`(没有行)。local 没有委派分区行。
160
236
  */
161
- patchSessionMeta(sessionId: string, patch: SessionRowPatch, face: SessionFace): Promise<SessionSummary | undefined>;
162
- /** 会话元数据那一格此刻坐的是不是 `face` 那一位住客(同步读已发布的格;调用方先 `meta.ensureLoaded()`)。 */
163
- private occupiedBy;
237
+ private lockedRowOf;
238
+ /** 两只边档上这只会话的可写格(已发布的值;缺键 = 缺省)。调用方先 {@link loadRowSidecars}。 */
239
+ private rowCellsOf;
240
+ /** 写原语的格写(锁内一形,调用方持这只会话的写锁):两只边档各经自己的写链(草稿 → 落盘 → 发布);落不了盘 ⇒ 抛、那一只的内存不动。 */
241
+ private writeRowCellsHeld;
242
+ /** 应答行(锁内另读):列表行同一只合成 + 读到它时那一格上的住客。不在列表面上 / 没有那一格 ⇒ `undefined`。 */
243
+ private listRowOf;
164
244
  /** ONE session's list row — {@link listSessions}' projection ({@link summaryOf}, the same synthesis) for one id;
165
245
  * `undefined` = not a row of the list face (not in the session repo). */
166
246
  sessionSummaryOf(sessionId: string): Promise<SessionSummary | undefined>;
@@ -175,6 +255,13 @@ export declare class LocalSessionStore implements SessionStore {
175
255
  * 开始之前共享的那张表只含落过盘的值,判定永远不会建立在一个没落过盘的值上(codex che-NM r2 [medium])。
176
256
  */
177
257
  recordInstructionSource(sessionId: string, expected: InstructionSourceBaseline, source: string | null): Promise<void>;
258
+ /** 会话行的位置与项目那一格(local 孪生;读法与 SQL 两孪生同一只 `decodeSessionWorkspaceCell`)。缺键 ⇒ `undefined`。 */
259
+ readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
260
+ /**
261
+ * 整格覆写(最后一帧胜;唯一调用方 = 腿的记录座)。与 SQL 孪生「`UPDATE … WHERE session_id = ? AND incarnation = ?`」同义:**会话不在、或同 id
262
+ * 已换了住客 ⇒ 不写**,判与写排在这只会话的写锁里(删除 / 换人同锁 —— 判过的住客在这一笔落盘之前换不掉)。落不了盘 ⇒ 内存不动、抛。
263
+ */
264
+ recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
178
265
  /** Best-effort, one-shot hydrate of the persisted lastActivity map (no-op when not persisting / no sidecar yet). A
179
266
  * read/parse error degrades to an empty map (createdAt ordering) rather than failing the list — the picker still
180
267
  * works, just without restart-persisted recency. Memoized so concurrent listSessions share one read. */
@@ -235,11 +322,6 @@ export declare class LocalSessionStore implements SessionStore {
235
322
  * `REQUIRE_PRINCIPAL=true`,授权口也只在那时才把 principal 递过来认领(`security.ts` `createAuthorizer`)。
236
323
  */
237
324
  register(sessionId: string, owner: string | null): Promise<void>;
238
- /** 2c P1d-β — re-stamp the session's owner WITHOUT rewriting entries (the staged-commit `identical` path: the log is
239
- * already present & equal, so only the owner needs re-stamping to the importing principal, §9). Persisted like every
240
- * owner write; the incarnation is untouched (same occupant). No cell (no such session) ⇒ no-op. Only caller:
241
- * {@link commitStagedImport}, inside the session's write lock. */
242
- private restampOwner;
243
325
  touch(sessionId: string): Promise<void>;
244
326
  /** Durable history is retained by the repo; just drop the in-flight cache entry (mirrors TiDBSessionStore.release). */
245
327
  release(sessionId: string): Promise<void>;
@@ -281,6 +363,8 @@ export declare class LocalSessionStore implements SessionStore {
281
363
  private ownerCellOf;
282
364
  /** 一只会话的列表行 —— {@link listSessions} 与 {@link sessionSummaryOf} 的**同一只**合成(字段来源逐位见 listSessions 的头注)。调用方先 {@link loadRowSidecars}。 */
283
365
  private summaryOf;
366
+ /** 列表行的 `rowTag` 键(规则 `session-row.ts`):化身出自会话元数据那一格(与写原语锁住的那一行同读),属主与两格由调用方交 —— 就是这一行交出去的那两位(同一刻读);没有那一格 ⇒ 键缺席。 */
367
+ private rowTagKeyOf;
284
368
  /** [ref]:run 店里每个会话的 run 投影(按 sessionId 索引)。
285
369
  * **一次读**:run 店的 `listSessions` 本来就按会话聚合全账(无会话过滤口),这里取全量再按会话仓的 id 查表 —— 比逐会话 N 次读便宜,
286
370
  * 也不在会话店里另存一份 run 账。读失败 ⇒ 抛(不吞成 `0 / ""`:那会把「读不到」说成「没跑过」)。 */
@@ -302,6 +386,19 @@ export declare class LocalSessionStore implements SessionStore {
302
386
  * 把它的答案原样交给引擎自己的 `repo.fork`(与 core 内存店 `TtlSessionStore.fork` 同一条调用链);两只 SQL 孪生同读它(`session-fork.ts`)。钉:
303
387
  * core 店契约盒的 `[fork]` 格(`test/session-store-contract-kit.test.ts`)。 */
304
388
  fork(sourceId: string, owner?: string | null): Promise<string | null>;
389
+ /**
390
+ * **fork 续写的店契约**(local 一形;规则与结果闭集在 `session-fork.ts`,SQL 孪生同义):
391
+ * · **两把写锁**:按 id 字典序依次取源与目标的写锁(锁不可重入;同 id 只取一把)—— 同 id 的每个写者(删除 / 登记 / 导入 / 按 id 首建 / 别的 fork)都在目标那把锁里,
392
+ * core `repo.fork` 的改名覆盖写只在锁内被调;
393
+ * · **源先判**:源存档不在 ⇒ `null`(源被删之后的同键重试答「源不在」);属主读在拷贝之前(源锁持到拷贝落定)—— 唯一读法 `forkOwnerOf`;
394
+ * · **唯一创建点 = 格先、存档后**:锁内「目标格不在 ∧ 目标存档不在」才写目标格(带 fork 键;新住客,执行位 absent),再交 core `repo.fork` 写存档。
395
+ * 崩在两步之间 = fork 创建的格而存档不在 = `forking`:普通取用一律 {@link SessionForkInProgressError};同键重试在两把锁内补拷存档、答 `replayed`。
396
+ * 拷贝抛错(不是崩溃)⇒ 撤掉刚写的目标格再上抛(撤不掉 ⇒ 留下的就是 `forking`,与崩在中间同形);
397
+ * · **撞已在** ⇒ 按目标格的属主与 fork 键分类({@link classifyForkTarget})。
398
+ */
399
+ forkTo(sourceId: string, owner: string | null, target: ForkTarget): Promise<ForkToOutcome | null>;
400
+ /** {@link forkTo} 的锁内一形(调用方持源与目标两把写锁)。 */
401
+ private forkToHeld;
305
402
  /** 2c session-sync — EXPORT a session's full durable entry log (core `SessionRepo.exportEntries`; the
306
403
  * in-memory/file repo holds the whole log so there is no floor to bypass). null if the source is unknown / the
307
404
  * underlying repo lacks the optional seam. The owner is NOT exported — import re-stamps it. */
@@ -322,27 +419,67 @@ export declare class LocalSessionStore implements SessionStore {
322
419
  afterSeq?: number;
323
420
  batchSize?: number;
324
421
  }): Promise<AsyncIterable<SessionTreeEntry> | null>;
325
- /** 2c session-sync — IMPORT a verbatim entry log into `sessionId` via core `SessionRepo.importEntries`
326
- * (which runs the `validateEntriesForImport` fail-closed gate before writing). `owner` is the authenticated
327
- * importing principal (re-stamped into the service-side owner map; core's local repo has no owner column, §9).
328
- * `owner=null` (single-user local) is passed as `undefined` to the core seam (its owner param is `string | undefined`).
329
- * The owner is recorded in the session-meta cell (persisted), together with the incarnation. */
422
+ /**
423
+ * 2c session-sync — IMPORT a verbatim entry log into a **new** session `sessionId`(`owner` = 鉴权过的导入者)。按接口句(`importEntries` 只建新会话;改写既有会话走
424
+ * {@link replaceEntries} / 暂存提交),SQL 孪生同判(占行不成 ⇒ 拒):id 上已有行(或格缺而存档在)⇒ `conflict`,一个字节不写;新行的属主 = 导入者、铸一只化身、
425
+ * 执行位 mirror。属主在本店的属主格上判(会话元数据那一格是本店唯一的属主真源;本店建的存档在引擎仓里不带属主戳 —— `register` 只 `repo.create({ id })` ——
426
+ * 引擎仓的属主比较替这一格答不了)。整段在这只会话的写锁里,写序见 {@link importHeld}。
427
+ */
330
428
  importEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
331
- /** importEntries / replaceEntries 的共用写半场:导日志 + 一格写全属主与化身(新建铸的那只,或换装沿用的那只)。调用方持这只会话的写锁。 */
332
- private importEntriesAs;
333
- /** 2c session-sync (§7/§8) — IDEMPOTENT REPLACE. The local twin's replace is DELETE-then-IMPORT: core's
334
- * InMemorySessionRepo / FileSessionRepo expose no single replace primitive, but the local backend is single-writer
335
- * with NO compaction floor / cross-instance race (a process-local in-memory map / a single-process file repo), so
336
- * deleteSession-then-importEntries is effectively atomic for the local case (no concurrent reader can observe the
337
- * gap, unlike the durable cross-instance backends which do it in ONE SQL txn). Both halves are best-effort feature-
338
- * gated: a repo lacking either seam throws the same clear error importEntries does (no silent half-write). */
429
+ /**
430
+ * 2c session-sync (§7/§8) — IDEMPOTENT REPLACE,按接口句(`SessionStorage.replaceEntries`:先清这只会话既有的对话日志,再导入 `entries`)。
431
+ * 与 {@link importEntries} 同一个入口、同一只写序,只差落点(行不在,或行上的属主就是导入者)与内容那一半(先清内容存档再导入,见 {@link importHeld} ③)——
432
+ * 所以一只会话头读不出的存档也换得掉;存档是重建的那一只,建档时刻 = 换装时刻(SQL 孪生 replace 重插行、`created_at` 重记,同义)。行的四只边档原样。
433
+ */
339
434
  replaceEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
340
- /** {@link replaceEntries} 的锁内一形(调用方持这只会话的写锁)。 */
341
- private replaceEntriesHeld;
435
+ /**
436
+ * 换装三条写腿(公开 {@link importEntries} / {@link replaceEntries} 与暂存提交 {@link commitStagedImport})的**同一个入口**:调用方给的属主先过
437
+ * {@link importerOwnerOf}(主体名或 `null`;否则 `TypeError`,什么都没读、一个字节没写),再进这只会话的写锁 —— 锁里的写体只见判过的那一个值,
438
+ * 写进行的属主就是它。引擎导入口的属主判管不到这一格:换装交给引擎的是存档自己的戳(见 {@link importHeld} ③),不是调用方的属主。
439
+ */
440
+ private withImportLock;
441
+ /**
442
+ * 三个导入写口(`importEntries` / `replaceEntries` / 暂存提交的换装臂)的共用写序(调用方持这只会话的写锁,属主的形已在入口判过):
443
+ * ① 只读、零写:引擎闸 `validateEntriesForImport`(坏包 ⇒ `invalid_session`)· 本处写者 ⇒ {@link SessionWriterPresentError} · `forking` ⇒
444
+ * {@link SessionForkInProgressError} · 不是这个写口的门判过的行 ⇒ {@link importRowRefused}(判据 {@link importMayLand},按写口带来的 {@link ImportLanding};
445
+ * 这条路径上唯一一次判属主);
446
+ * ② 行先:{@link landImportCell}(属主重戳为导入者、化身与 fork 键沿用或新铸、执行位 mirror);
447
+ * ③ 内容,按写口的动词(唯一读法在下面这一只 switch;这里不读那一格、不判属主,只换**内容**):
448
+ * · `create` / `judged`(导入):原地换锁里读到的那一份存档的日志(没有存档 ⇒ 导入新建一只),存档的元数据整只留着(其中的建档时刻是列表行的
449
+ * firstActivityAt,与 SQL 孪生留在行上的 `created_at` 同义)。存档上的属主戳不是本店的事实(本店建的存档一律不带戳,存量存档可能带着此前导入者的戳),
450
+ * 只是引擎仓原地换的条件:按那一份存档自己的戳导入,并以它为 `expect`(戳 + 存档的令牌)—— 读与写之间它换了一代 ⇒ 引擎仓拒 `conflict`。
451
+ * 读存档、校验、存档判与写在引擎仓里是一步,抛在哪一处内容都一个字节没动 ⇒ 撤回 ②({@link withdrawImportCell}:行原样,SQL 孪生的事务回滚同义)再原样上抛;
452
+ * · `replace`(接口句「先清既有对话日志,再导入」):先清掉内容存档(不带 `expect` —— 带了就得先读它的头,读不出的存档就清不掉),再导入一只新存档。
453
+ * 清不可撤:清过之后导入再抛(I/O 故障),内容就没了,行留 ② 落下的 mirror(只读方向,下次推送补齐)—— 已发版本的 replace 同形(删后导入)。
454
+ * 带 fork 键的行(fork 建出的目标)留下的是「mirror、键仍在、存档不在」:不读作 `forking`({@link isForkCreatedCell}:fork 创建点写下的格执行位恒缺,② 落下 mirror 之后它就不再是那一形),
455
+ * 下一次 replace / 暂存提交照常落定,同键 fork 重试仍 replayed(SQL 孪生一个事务里删重插、整段回滚,没有这个窗);
456
+ * 收尾:新近 = 此刻(推送即活动)。标题 / 置顶 / 指令来源 / 工作区四只行边档一个字节不碰。
457
+ * 崩在 ②③ 之间 = 格已是 mirror、内容旧或空 = 只读方向,下次推送补齐(SQL 孪生在一个事务里写行与内容,没有这个中间态)。
458
+ */
459
+ private importHeld;
460
+ /**
461
+ * ① 的属主 / 住客条件:这一行此刻是不是这个写口的门判过的行({@link ImportLanding};一次读、零写 —— 格缺时「行在不在」只问存档的唯一探针
462
+ * {@link existsInRepo},不补铸那一格)。属主比的是头读的读法 `owner ?? null`(从没被记下 = 无主;格缺而存档在 = 从没被记下),比较器是删除门的那一只
463
+ * {@link ownerEq}。M10 的认领只归登记({@link register}),导入不认领:遗留的「属主未记」格对带 principal 的 `replace` 读作别人的行,拒了之后那一格
464
+ * 原样仍是「未记」,窗内登记照样认领。
465
+ */
466
+ private importMayLand;
467
+ /**
468
+ * ② 推送落下的那一格 —— 三个导入写口与暂存提交「内容相同、日志不动」那一支都经这**一处**(调用方持写锁、① 已判过):属主重戳为导入者;
469
+ * 化身与 fork 键经 {@link carriedRowCells}(三只店同一句)读那一格自己 —— 行在 ⇒ 沿用(同一行 = 同一位住客,属主重戳与否不改这一判),刚建 ⇒ 新铸、不带键;
470
+ * 执行位 = mirror(导入落下的是「写者在别处」;SQL 孪生的提交点同一句写)。只写这一格:四只行边档与新近不在这里。
471
+ * 交回写之前的那一格(`undefined` = 原本没有),③ 撤回用。
472
+ */
473
+ private landImportCell;
474
+ /**
475
+ * 撤回 ② —— ③ 的原地换抛了(内容一个字节没动):那一格回到 ② 之前(原本没有 ⇒ 删掉),行原样。撤不掉 ⇒ 响亮:两只故障都点名
476
+ * (留下的格是 mirror —— 只读方向,与崩在 ②③ 之间同形,下次推送补齐)。
477
+ */
478
+ private withdrawImportCell;
342
479
  /** 2c session-sync P1d-β (PUSH streaming) — begin a STAGED import. The LOCAL backend is single-writer with no
343
480
  * cross-instance / cross-replica concurrency story (the durable twins carry that), so the staging handle simply
344
- * ACCUMULATES the streamed batches in memory then, on commit, runs the SAME validate-first → replaceEntries the
345
- * whole-bundle local import uses (effectively atomic locally — no concurrent reader can observe the gap). Keep it
481
+ * ACCUMULATES the streamed batches in memory then, on commit, runs the SAME in-place log swap ({@link importHeld} ③: one
482
+ * engine-repo import onto the archive as it stands — validate, judge and write in one step) the whole-bundle local import uses. Keep it
346
483
  * simple per §1: the durable twins are where the SHADOW-id swap + bounded-memory + concurrency matter. */
347
484
  beginImportStaging(realSessionId: string, token: string): StagingHandle;
348
485
  /** 2c P1d-β (staged-row inspection) — the in-memory staged entries for a local staging id. No longer the import gate
@@ -354,7 +491,8 @@ export declare class LocalSessionStore implements SessionStore {
354
491
  /**
355
492
  * {@link LocalStagingHandle.commit} 的整段 —— **一次持锁**(这只会话的写锁,{@link withSessionWriteLock}):属主 CAS(此刻的属主格 = 这次导入的门判过的那一格,
356
493
  * 否则抛 {@link StagedCommitOwnerChangedError},一个字节不写;在判目的端日志之前判,SQL 孪生同序)→ 提交时重新分类(目的端此刻的日志 vs staged)
357
- * → 拒未经同意的 fork / stale / 载荷分叉 → 换装(validate-first → 删 → 导入,化身按换装律沿用)或 identical 且内容相同时只重戳属主 → **在同一次持锁里**
494
+ * → 拒未经同意的 fork / stale / 载荷分叉 → 本处写者拒 → 换装(导入写序 ①②③,落点 `judged` = 上面这一判;化身与 fork 键按换装律沿用)或 identical 且内容相同时日志不动、
495
+ * 行照样落(② 那一格:重戳属主 + mirror)→ 两支同记新近(推送即活动)→ **在同一次持锁里**
358
496
  * 读出落定的会话面交回。锁放下之前谁也换不掉这一位住客,所以交回的面就是这次提交落下的那一位(SQL 孪生在提交事务里读同一行,同义);
359
497
  * 提交之后按住客条件写的重放(会话行两位)只认这只面,不再重读。改属主的每一条写腿(登记 / 删除 / 导入 / 换装)都在同一把锁里,所以锁内这一读到锁放下都成立。
360
498
  */
@@ -367,10 +505,10 @@ export declare class LocalSessionStore implements SessionStore {
367
505
  * tool-result rows are purged separately by the route's purgeSession coordinator. 整段在这只会话的写锁里(换人)。 */
368
506
  deleteSession(sessionId: string, owner: string | null): Promise<boolean>;
369
507
  /**
370
- * {@link deleteSession} 的锁内一形(调用方持这只会话的写锁;换装 {@link replaceEntriesHeld} 也走它)。
371
- * 删的是 SQL 孪生 `session_meta` 那一行在本店的全部对位:会话存档、三只行边档(标题 / 置顶 / [ref] 的指令来源基线)、属主 + 化身那一格。
372
- * 三只边档的那一键**都经各自的写链删**(`commit`:草稿上删、落盘、再发布 —— 与写它们的那一笔同一条链、同一把会话锁),落不了盘 ⇒ 抛:
373
- * 留在盘上的值会挂到同 id 的下一位住客身上(E21 的删除权残留)。元数据格**最后**删:三只边档任一只落不了而抛时,那一格还在 ⇒ 会话对属主门仍
508
+ * {@link deleteSession} 的锁内一形(调用方持这只会话的写锁;换装不走它 —— 换装只换内容存档,行原样,见 {@link importHeld})。
509
+ * 删的是 SQL 孪生 `session_meta` 那一行在本店的全部对位:会话存档、四只行边档(标题 / 置顶 / 指令来源基线 / 工作区)、新近标记、属主 + 化身那一格。
510
+ * 四只边档的那一键**都经各自的写链删**(`commit`:草稿上删、落盘、再发布 —— 与写它们的那一笔同一条链、同一把会话锁),落不了盘 ⇒ 抛:
511
+ * 留在盘上的值会挂到同 id 的下一位住客身上(E21 的删除权残留)。元数据格**最后**删:四只边档任一只落不了而抛时,那一格还在 ⇒ 会话对属主门仍
374
512
  * 「在」,重发删除照样走得进来、收敛;反过来先删格,重发就成了 no-op,残留永远留下。
375
513
  * 🔴 hydrate FIRST (workflow audit 2026-07-13): after a restart the maps start empty — an un-hydrated delete would find nothing and
376
514
  * the sidecar would keep the value (an E21 right-to-delete residue that would RESURRECT onto a same-id re-import). ensureLoaded is