@sema-agent/server 7.136.0 → 7.138.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 (327) hide show
  1. package/MIGRATION.md +501 -139
  2. package/README.md +1 -0
  3. package/README.zh-CN.md +1 -0
  4. package/USAGE.md +149 -34
  5. package/deploy/sema-up/README.md +14 -0
  6. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
  7. package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
  8. package/dist/approval-card.d.ts +21 -14
  9. package/dist/approval-card.js +1 -0
  10. package/dist/approval-content-kind.d.ts +6 -0
  11. package/dist/approval-content-kind.js +7 -0
  12. package/dist/approval-gate-currency.d.ts +48 -6
  13. package/dist/approval-gate-currency.js +16 -9
  14. package/dist/approval-hmac.d.ts +13 -1
  15. package/dist/approval-hmac.js +1 -1
  16. package/dist/approval.js +3 -3
  17. package/dist/audit.js +1 -0
  18. package/dist/auth-bridge.js +2 -2
  19. package/dist/background-agent-name.d.ts +5 -0
  20. package/dist/background-agent-name.js +5 -0
  21. package/dist/boot/budget-tracing.d.ts +1 -12
  22. package/dist/boot/deployment-mods.d.ts +46 -16
  23. package/dist/boot/deployment-mods.js +82 -6
  24. package/dist/boot/execution-env.d.ts +24 -1
  25. package/dist/boot/execution-env.js +69 -6
  26. package/dist/boot/governance-seams.d.ts +10 -17
  27. package/dist/boot/governance-seams.js +5 -17
  28. package/dist/boot/leader.d.ts +3 -0
  29. package/dist/boot/leader.js +1 -0
  30. package/dist/boot/org-delivery.js +15 -2
  31. package/dist/boot/reapers.d.ts +3 -0
  32. package/dist/boot/reapers.js +15 -1
  33. package/dist/boot/resolve-spec.d.ts +7 -0
  34. package/dist/boot/resolve-spec.js +36 -26
  35. package/dist/boot/retention-lane.d.ts +11 -5
  36. package/dist/boot/retention-lane.js +2 -2
  37. package/dist/boot/runner-deps.d.ts +7 -3
  38. package/dist/boot/runner-deps.js +1 -0
  39. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  40. package/dist/boot/stage-04-stores.d.ts +2 -13
  41. package/dist/boot/stage-05-execution-env.d.ts +6 -13
  42. package/dist/boot/stage-05-execution-env.js +17 -5
  43. package/dist/boot/stage-06-runners.d.ts +6 -13
  44. package/dist/boot/stage-06-runners.js +10 -3
  45. package/dist/boot/stage-07-capability-layer.d.ts +7 -13
  46. package/dist/boot/stage-07-capability-layer.js +3 -3
  47. package/dist/boot/stage-08-reapers.d.ts +7 -13
  48. package/dist/boot/stage-08-reapers.js +2 -1
  49. package/dist/boot/stage-09-leader.d.ts +7 -13
  50. package/dist/boot/stage-09-leader.js +2 -2
  51. package/dist/boot/stage-10-http-server.d.ts +7 -13
  52. package/dist/boot/stage-10-http-server.js +6 -3
  53. package/dist/boot/stores.d.ts +1 -1
  54. package/dist/boot/web-admin.d.ts +5 -2
  55. package/dist/boot/web-admin.js +3 -3
  56. package/dist/brain.js +5 -5
  57. package/dist/broker/broker.d.ts +89 -0
  58. package/dist/broker/broker.js +287 -0
  59. package/dist/broker/constants.d.ts +39 -0
  60. package/dist/broker/constants.js +15 -0
  61. package/dist/broker/http1.d.ts +111 -0
  62. package/dist/broker/http1.js +256 -0
  63. package/dist/broker/lease-book.d.ts +87 -0
  64. package/dist/broker/lease-book.js +142 -0
  65. package/dist/broker/lease-file.d.ts +60 -0
  66. package/dist/broker/lease-file.js +127 -0
  67. package/dist/broker/main.d.ts +2 -0
  68. package/dist/broker/main.js +20 -0
  69. package/dist/budget.d.ts +17 -15
  70. package/dist/budget.js +2 -2
  71. package/dist/capabilities/composite-members.d.ts +2 -1
  72. package/dist/capabilities/composite-members.js +2 -1
  73. package/dist/capabilities/engine-capabilities.js +1 -0
  74. package/dist/capabilities/mcp-probe.d.ts +36 -11
  75. package/dist/capabilities/mcp-probe.js +44 -5
  76. package/dist/capabilities/scenarios.d.ts +23 -8
  77. package/dist/capabilities/scenarios.js +13 -8
  78. package/dist/capabilities/skills.d.ts +28 -5
  79. package/dist/capabilities/skills.js +8 -0
  80. package/dist/capability-facts.d.ts +6 -0
  81. package/dist/capability-facts.js +5 -0
  82. package/dist/center-credential-renewer.js +5 -1
  83. package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
  84. package/dist/checkpoint-ctx-seal-m28.js +1 -1
  85. package/dist/checkpoint-identity.d.ts +13 -6
  86. package/dist/config-catalog.js +16 -7
  87. package/dist/config-types.d.ts +24 -7
  88. package/dist/config.d.ts +1 -1
  89. package/dist/config.js +42 -13
  90. package/dist/deployment-governance.d.ts +2 -2
  91. package/dist/deployment-governance.js +7 -9
  92. package/dist/deployment-shape.d.ts +6 -6
  93. package/dist/device-ws-protocol.d.ts +1 -0
  94. package/dist/device-ws-protocol.js +2 -1
  95. package/dist/file-history-disclosure.d.ts +4 -2
  96. package/dist/file-history-disclosure.js +2 -0
  97. package/dist/gate-settlement.d.ts +71 -0
  98. package/dist/gate-settlement.js +40 -0
  99. package/dist/hooks/classic-stations.d.ts +12 -54
  100. package/dist/hooks/classic-stations.js +1 -0
  101. package/dist/hooks/command-spawn.d.ts +2 -2
  102. package/dist/hooks/hook-runner.js +14 -14
  103. package/dist/http/admission.js +14 -9
  104. package/dist/http/approval-sweeps.js +4 -3
  105. package/dist/http/caller-trust.d.ts +2 -1
  106. package/dist/http/caller-trust.js +1 -3
  107. package/dist/http/checkpoint-ctx.d.ts +48 -7
  108. package/dist/http/checkpoint-ctx.js +40 -0
  109. package/dist/http/dispatch.d.ts +4 -0
  110. package/dist/http/dispatch.js +18 -10
  111. package/dist/http/durable-run-leg.d.ts +5 -1
  112. package/dist/http/durable-run-leg.js +5 -3
  113. package/dist/http/health-self-report.d.ts +2 -2
  114. package/dist/http/memory-compliance-failure-reply.js +3 -0
  115. package/dist/http/principal-gate.d.ts +15 -10
  116. package/dist/http/principal-gate.js +48 -9
  117. package/dist/http/resume-legs.d.ts +7 -31
  118. package/dist/http/resume-legs.js +185 -126
  119. package/dist/http/route-ctx.d.ts +38 -53
  120. package/dist/http/route-ctx.js +2 -5
  121. package/dist/http/routes/a2a-serve.js +10 -4
  122. package/dist/http/routes/agents-roster.d.ts +1 -1
  123. package/dist/http/routes/agents-roster.js +5 -1
  124. package/dist/http/routes/approvals-assistant.js +24 -10
  125. package/dist/http/routes/capabilities.js +23 -11
  126. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  127. package/dist/http/routes/mcp-reconnect.js +7 -7
  128. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  129. package/dist/http/routes/memory-origin.d.ts +1 -1
  130. package/dist/http/routes/rules.js +3 -3
  131. package/dist/http/routes/runs.js +13 -8
  132. package/dist/http/routes/session-sync.js +42 -6
  133. package/dist/http/routes/sessions.js +6 -3
  134. package/dist/http/routes/side-query.js +2 -3
  135. package/dist/http/routes/tasks.js +794 -780
  136. package/dist/http/server.d.ts +35 -25
  137. package/dist/http/stored-body-replay.d.ts +3 -3
  138. package/dist/http/stored-body-replay.js +6 -1
  139. package/dist/http/wire-types.d.ts +66 -33
  140. package/dist/identity-source.d.ts +65 -0
  141. package/dist/identity-source.js +45 -0
  142. package/dist/index.d.ts +2 -0
  143. package/dist/index.js +1 -0
  144. package/dist/leader/wire.d.ts +7 -0
  145. package/dist/leader/wire.js +5 -4
  146. package/dist/mcp-assertion-header.d.ts +51 -40
  147. package/dist/mcp-assertion-header.js +10 -11
  148. package/dist/mcp-authorization-face.d.ts +27 -0
  149. package/dist/mcp-authorization-face.js +15 -0
  150. package/dist/memory-operator-faces.d.ts +13 -4
  151. package/dist/memory-operator-faces.js +1 -17
  152. package/dist/memory-store-refusal.d.ts +3 -3
  153. package/dist/memory-sync-client.js +72 -57
  154. package/dist/memory-sync.d.ts +1 -1
  155. package/dist/model-entry-refusal.d.ts +2 -0
  156. package/dist/model-entry-refusal.js +11 -0
  157. package/dist/model-route-gap.d.ts +6 -22
  158. package/dist/model-route-gap.js +7 -5
  159. package/dist/model-select.d.ts +6 -17
  160. package/dist/model-select.js +0 -3
  161. package/dist/mods-declaration.d.ts +110 -0
  162. package/dist/mods-declaration.js +109 -0
  163. package/dist/namespaced-rule-name.d.ts +1 -1
  164. package/dist/observability/err-text.d.ts +5 -0
  165. package/dist/observability/err-text.js +9 -0
  166. package/dist/observability/fail-open.d.ts +17 -1
  167. package/dist/observability/fail-open.js +17 -1
  168. package/dist/observability/metrics.js +1 -1
  169. package/dist/observability/run-terminal.js +1 -0
  170. package/dist/observability/secret-env-scrub.d.ts +1 -1
  171. package/dist/observability/secret-env-scrub.js +1 -1
  172. package/dist/operator-ask.d.ts +2 -2
  173. package/dist/org-delivery-gate.js +1 -1
  174. package/dist/org-memory-admission.d.ts +2 -2
  175. package/dist/org-memory-admission.js +39 -2
  176. package/dist/parked-decide.d.ts +15 -2
  177. package/dist/parked-decide.js +17 -10
  178. package/dist/peer-directory.js +2 -0
  179. package/dist/peer-seat.d.ts +150 -0
  180. package/dist/peer-seat.js +239 -0
  181. package/dist/permission-rule-list.d.ts +2 -2
  182. package/dist/permission-rule-vocab.d.ts +7 -7
  183. package/dist/permission-rule-vocab.js +3 -3
  184. package/dist/plain-string-record.d.ts +38 -0
  185. package/dist/plain-string-record.js +38 -0
  186. package/dist/plugins/background-agent-store-sql.js +6 -3
  187. package/dist/plugins/caching-session-store.d.ts +5 -0
  188. package/dist/plugins/caching-session-store.js +6 -0
  189. package/dist/plugins/checkpoint-store-sql.d.ts +34 -29
  190. package/dist/plugins/checkpoint-store-sql.js +14 -14
  191. package/dist/plugins/file-history-store-sql.js +10 -16
  192. package/dist/plugins/file-run-store.js +8 -2
  193. package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
  194. package/dist/plugins/k8s-exec-protocol.js +116 -28
  195. package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
  196. package/dist/plugins/k8s-pod-delete-failure.js +17 -0
  197. package/dist/plugins/k8s-pod-reaper.d.ts +106 -0
  198. package/dist/plugins/k8s-pod-reaper.js +122 -0
  199. package/dist/plugins/local-checkpoint-store.d.ts +4 -17
  200. package/dist/plugins/local-checkpoint-store.js +3 -4
  201. package/dist/plugins/local-session-store.d.ts +66 -27
  202. package/dist/plugins/local-session-store.js +60 -28
  203. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  204. package/dist/plugins/mailbox-store-sql.js +103 -15
  205. package/dist/plugins/memory-run-store.js +2 -0
  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 +34 -12
  209. package/dist/plugins/one-time-migrations.js +51 -9
  210. package/dist/plugins/permission-rule-store-file.js +2 -2
  211. package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
  212. package/dist/plugins/permission-rule-store-sql.js +37 -30
  213. package/dist/plugins/pg-pool.js +4 -0
  214. package/dist/plugins/pg-session-storage.d.ts +10 -2
  215. package/dist/plugins/pg-session-storage.js +20 -7
  216. package/dist/plugins/remote-env-adb.d.ts +6 -1
  217. package/dist/plugins/remote-env-adb.js +2 -1
  218. package/dist/plugins/remote-env-device.d.ts +6 -1
  219. package/dist/plugins/remote-env-device.js +5 -1
  220. package/dist/plugins/remote-env-e2b.d.ts +5 -0
  221. package/dist/plugins/remote-env-e2b.js +2 -1
  222. package/dist/plugins/remote-env-file-error.d.ts +8 -1
  223. package/dist/plugins/remote-env-file-error.js +7 -1
  224. package/dist/plugins/remote-env-host.d.ts +10 -2
  225. package/dist/plugins/remote-env-host.js +8 -2
  226. package/dist/plugins/remote-env-k8s.d.ts +140 -11
  227. package/dist/plugins/remote-env-k8s.js +277 -15
  228. package/dist/plugins/remote-env-local-docker.d.ts +5 -0
  229. package/dist/plugins/remote-env-local-docker.js +2 -1
  230. package/dist/plugins/remote-env-ssh.d.ts +6 -1
  231. package/dist/plugins/remote-env-ssh.js +2 -1
  232. package/dist/plugins/retention-store-sql.d.ts +14 -2
  233. package/dist/plugins/retention-store-sql.js +6 -0
  234. package/dist/plugins/run-store-sql.js +10 -4
  235. package/dist/plugins/session-fork.d.ts +10 -13
  236. package/dist/plugins/session-fork.js +5 -25
  237. package/dist/plugins/session-ownership.d.ts +24 -3
  238. package/dist/plugins/session-ownership.js +24 -5
  239. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  240. package/dist/plugins/session-workspace-cell.js +38 -0
  241. package/dist/plugins/sql-errors.d.ts +23 -2
  242. package/dist/plugins/sql-errors.js +15 -0
  243. package/dist/plugins/store-backend.js +7 -7
  244. package/dist/plugins/store-contracts.d.ts +10 -1
  245. package/dist/plugins/store-fault.d.ts +2 -2
  246. package/dist/plugins/store-fault.js +4 -2
  247. package/dist/plugins/tidb-pool.js +13 -0
  248. package/dist/plugins/tidb-session-store.d.ts +17 -2
  249. package/dist/plugins/tidb-session-store.js +18 -7
  250. package/dist/plugins/tool-result-store-sql.d.ts +13 -6
  251. package/dist/plugins/tool-result-store-sql.js +8 -7
  252. package/dist/plugins/worktree-isolation.d.ts +3 -2
  253. package/dist/plugins/worktree-isolation.js +1 -7
  254. package/dist/project-identity.d.ts +1 -14
  255. package/dist/project-identity.js +0 -4
  256. package/dist/request-key-closure.js +1 -1
  257. package/dist/request-value-closure.d.ts +31 -2
  258. package/dist/request-value-closure.js +7 -1
  259. package/dist/rule-entry-list.d.ts +3 -2
  260. package/dist/rule-entry-list.js +2 -2
  261. package/dist/rules-consent.js +12 -7
  262. package/dist/run-local.d.ts +2 -2
  263. package/dist/run-local.js +12 -6
  264. package/dist/runs.d.ts +7 -1
  265. package/dist/runs.js +8 -2
  266. package/dist/runtime-governance.d.ts +5 -5
  267. package/dist/sandbox-git-lease.d.ts +49 -0
  268. package/dist/sandbox-git-lease.js +60 -0
  269. package/dist/sandbox-git-plane.d.ts +84 -0
  270. package/dist/sandbox-git-plane.js +105 -0
  271. package/dist/security.d.ts +16 -4
  272. package/dist/security.js +8 -3
  273. package/dist/server-secret-env.d.ts +12 -3
  274. package/dist/server-secret-env.js +18 -2
  275. package/dist/session-error-wire.d.ts +14 -6
  276. package/dist/session-error-wire.js +2 -1
  277. package/dist/session-incarnation.d.ts +4 -5
  278. package/dist/session-incarnation.js +2 -2
  279. package/dist/session-policy-wire.d.ts +1 -1
  280. package/dist/session-policy-wire.js +2 -2
  281. package/dist/session-sync.d.ts +1 -1
  282. package/dist/session-workspace.d.ts +130 -0
  283. package/dist/session-workspace.js +83 -0
  284. package/dist/spec-fields.d.ts +44 -14
  285. package/dist/spec-fields.js +33 -12
  286. package/dist/standing-answer-grant.d.ts +2 -13
  287. package/dist/standing-answer-grant.js +0 -7
  288. package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
  289. package/dist/stored-request-prompt-profile-m30.js +25 -0
  290. package/dist/task-a2a.d.ts +44 -14
  291. package/dist/task-a2a.js +46 -20
  292. package/dist/task-mcp.d.ts +34 -21
  293. package/dist/task-mcp.js +46 -22
  294. package/dist/task-settings.d.ts +2 -2
  295. package/dist/tool-approval.d.ts +43 -9
  296. package/dist/tool-approval.js +57 -15
  297. package/dist/tool-rule-names.d.ts +5 -0
  298. package/dist/trace/core-keyset-guard.d.ts +34 -19
  299. package/dist/trace/engine-notice-wire.d.ts +1 -1
  300. package/dist/trace/engine-notice-wire.js +4 -0
  301. package/dist/trace/exit-seat.d.ts +20 -1
  302. package/dist/trace/exit-seat.js +5 -2
  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/ledger-sink.d.ts +4 -5
  306. package/dist/trace/ledger-sink.js +3 -7
  307. package/dist/trace/project.d.ts +62 -10
  308. package/dist/trace/project.js +49 -9
  309. package/dist/trace/redact.d.ts +18 -6
  310. package/dist/trace/redact.js +154 -60
  311. package/dist/trace/self-minted-wire.d.ts +500 -7
  312. package/dist/trace/self-minted-wire.js +182 -1
  313. package/dist/trace/wire-projection-faces.d.ts +7 -5
  314. package/dist/trace/wire-projection-faces.js +9 -4
  315. package/dist/user-authored-text.d.ts +2 -2
  316. package/dist/user-authored-text.js +16 -1
  317. package/dist/web-admin-client/apply-effective.js +0 -1
  318. package/dist/web-admin-client/center-request.d.ts +23 -4
  319. package/dist/web-admin-client/center-request.js +42 -8
  320. package/dist/web-admin-client/http-client.js +6 -6
  321. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  322. package/dist/web-admin-client/skills-mcp.js +31 -18
  323. package/dist/web-admin-client/types.d.ts +10 -5
  324. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
  325. package/package.json +3 -3
  326. package/wire-changes/server@7.137.0.json +825 -0
  327. package/wire-changes/server@7.138.0.json +446 -0
@@ -1,4 +1,3 @@
1
- import type { MemorySyncCursor } from "@sema-agent/core";
2
1
  import type { PgQueryFn } from "./pg-query.js";
3
2
  import { type IndexSpec } from "./ensure-index.js";
4
3
  /** Table names (single source). SAME names as the TiDB twin (the two dialects never share one DB). */
@@ -31,12 +30,24 @@ export interface ClaimedPushQueueItem {
31
30
  /** The lease deadline this claim holds the row until (nowMs + claimLeaseMs). */
32
31
  leasedUntilMs: number;
33
32
  }
33
+ /**
34
+ * 中心侧的同步游标 = 本服务作同步**中心**时,每个 (scope, peer) 的基线(`sync_cursors` 一行)—— 本服务自己的事实,本服务自己的型。
35
+ * 它**不是**引擎的 `MemorySyncCursor`:那一只是同步**客户端**的游标,引擎 7.59.0 起必带 `store`(客户端本地 File 店的物理根,`memorySyncStoreOf` 铸),
36
+ * 绑的是「这份基线是对着哪一只本地店取的」;中心这一侧的条目平面是部署的 DB 后端,没有本地店根可绑,游标也从不交给引擎判。
37
+ * 此前本文件借用了引擎那只型名(两者当时同形),引擎给它加了只属于客户端的成员之后,借用就成了两个事实共用一个名字 —— 本型收回。
38
+ */
39
+ export interface CentralSyncCursor {
40
+ readonly scope: string;
41
+ readonly peer: string;
42
+ readonly baseRevs: Readonly<Record<string, string>>;
43
+ readonly updatedAtMs: number;
44
+ }
34
45
  /** The dialect-neutral contract both twins implement (anti-drift: one shared integration suite,
35
46
  * test/memory-sync-store-db-integration.test.ts, runs the SAME scenarios against BOTH engines). */
36
47
  export interface MemorySyncStore {
37
- getCursor(scope: string, peer: string): Promise<MemorySyncCursor | undefined>;
48
+ getCursor(scope: string, peer: string): Promise<CentralSyncCursor | undefined>;
38
49
  /** Whole-row replace (round semantics — baseRevs is swapped as one map, never merged). */
39
- putCursor(cursor: MemorySyncCursor): Promise<void>;
50
+ putCursor(cursor: CentralSyncCursor): Promise<void>;
40
51
  enqueue(item: PushQueueItem): Promise<void>;
41
52
  /** Lease-claim up to `limit` due rows (next_attempt_at_ms <= nowMs), oldest-due first. Claimed rows
42
53
  * are invisible to other claimers until `complete`/`fail` or lease expiry (crash self-heal). */
@@ -66,8 +77,8 @@ export declare class PgMemorySyncStore implements MemorySyncStore {
66
77
  private readonly query;
67
78
  private readonly claimLeaseMs;
68
79
  constructor(query: PgQueryFn, opts?: PgMemorySyncStoreOptions);
69
- getCursor(scope: string, peer: string): Promise<MemorySyncCursor | undefined>;
70
- putCursor(cursor: MemorySyncCursor): Promise<void>;
80
+ getCursor(scope: string, peer: string): Promise<CentralSyncCursor | undefined>;
81
+ putCursor(cursor: CentralSyncCursor): Promise<void>;
71
82
  enqueue(item: PushQueueItem): Promise<void>;
72
83
  claimDue(nowMs: number, limit: number): Promise<ClaimedPushQueueItem[]>;
73
84
  complete(id: string): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import type { ClaimedPushQueueItem, MemoryHistoryAppendRow, MemoryHistoryRow, MemoryHistoryStore, MemorySyncStore, PushQueueItem } from "./memory-sync-store-pg.js";
2
- import type { MemorySyncCursor } from "@sema-agent/core";
2
+ import { type CentralSyncCursor } from "./memory-sync-store-pg.js";
3
3
  import type { Pool } from "mysql2/promise";
4
4
  /** Table names (single source) — SAME names as PG_MEMORY_SYNC_TABLES (the two dialects never share
5
5
  * one database). */
@@ -21,8 +21,8 @@ export declare class TiDBMemorySyncStore implements MemorySyncStore {
21
21
  private readonly pool;
22
22
  private readonly claimLeaseMs;
23
23
  constructor(pool: Pool, opts?: TiDBMemorySyncStoreOptions);
24
- getCursor(scope: string, peer: string): Promise<MemorySyncCursor | undefined>;
25
- putCursor(cursor: MemorySyncCursor): Promise<void>;
24
+ getCursor(scope: string, peer: string): Promise<CentralSyncCursor | undefined>;
25
+ putCursor(cursor: CentralSyncCursor): Promise<void>;
26
26
  enqueue(item: PushQueueItem): Promise<void>;
27
27
  claimDue(nowMs: number, limit: number): Promise<ClaimedPushQueueItem[]>;
28
28
  complete(id: string): Promise<void>;
@@ -109,11 +109,12 @@
109
109
  * 改名之后它们找不到那一列:已在跑的旧副本每一条规则语句响亮失败(新任务起不来、规则面 500,在跑的腿留着它已执行的快照),旧二进制起服被列册探针拒启 ——
110
110
  * 没有一只副本按旧文法读到新文法的行。行里不带文法版本(一个旧写者维护不了的第二个事实,恰在混跑窗里说谎),也不读规则体猜版本。
111
111
  * MySQL 协议方言 `CHANGE COLUMN`(MariaDB 10.5 以前没有 `RENAME COLUMN`;定义内插同一串),PG `RENAME COLUMN`;成功判据是后置条件(列名换过来了)。
112
- * ② **改名那一拍的改写:每一行在它自己的文法里由引擎读,改写成当前文法的同一句话**(core 7.51.2 [ref],行自带规则文法戳 `grammar`:写口判官盖、店整只存、
112
+ * ② **改名那一拍的改写:每一行在它自己的文法里由引擎读,改写成文法 1 里的同一句话**(core 7.51.2 [ref],行自带规则文法戳 `grammar`:写口判官盖、店整只存、
113
113
  * 引擎按行自带的戳读;无戳行 = 戳之前写的行,引擎读成文法 0)。改名那一刻库里每一行都是无戳行(≤7.128.x 写的)—— 判据只有引擎的:行(体 + `rev`)经
114
114
  * `readSessionRulesShape` 读成它自己的文法,`compileSessionRuleBucket` 在那种文法下编译 `toolAllow`,归 `earlier_grammar` 的条目(文法 0 里拼写不是它所指名字的条目
115
- * —— `Bash(rm:*)` / `Edit(src/**)` / `Bash(*)`:什么都不放行,文法 1 里是一条放行规则)删掉,其余原样,盖 `grammar: SESSION_RULES_GRAMMAR` —— 文法 0 与 1 在引擎里只差
116
- * 这一条子句,所以改写后的行是引擎对原行读法的逐调用同义重述(名单保持在场:删到空 = 在场的空名单,什么都不放行)。**每一行**都改写(只盖戳的也写):改写之后
115
+ * —— `Bash(rm:*)` / `Edit(src/**)` / `Bash(*)`:什么都不放行,文法 1 里是一条放行规则)删掉,其余原样,盖 `grammar: 1` —— 文法 0 与 1 在引擎里只差
116
+ * 这一条子句,所以改写后的行在文法 1 里是原行的逐调用同义重述(名单保持在场:删到空 = 在场的空名单,什么都不放行);之后几次修订由引擎按行戳读、保住写者的意思
117
+ * (7.137.0 起;此前盖的是当时的当前文法,引擎 7.56.0 把名内 `*` 的意思改了之后那一盖会越过修订、静默把 `toolDeny: ["*"]` 变成拒每一只工具)。**每一行**都改写(只盖戳的也写):改写之后
117
118
  * 部署里零无戳行,除下一条留下的行(引擎 7.52.0 [ref] 起文法 0 里的 `*` 也是 `earlier_grammar` 惰性条目、只作废它自己 —— 与别的读不出的 allow 条目同一条判:删、盖戳;7.129.0–7.131.x 引擎把带 `*` 的名单整张读成作废,本步对那种行曾不改写、只点名,那一臂随引擎修正退役)。
118
119
  * **盖戳前先问引擎读者**([ref],引擎 7.52.0 [ref]):行的 `toolDeny` 里有 `unreadableDenyEntries` 答的条目(引擎在这一行的文法里读成「不是任何一条规则」:空串、`-deploy`、非 ASCII 名、`(` 前带不可见空白 ……
119
120
  * 7.129.0–7.131.x 的写口收下过)⇒ 引擎对这一行的**每一次工具调用都拒**(读成更少的规则是 fail-open,引擎不这么读),并按行自带的旧文法通告(`config.session_rules_earlier_grammar`,只对戳早于当前文法的行发)。
@@ -226,9 +227,12 @@
226
227
  * · **M25 单用户记忆项目层换键形**(7.133.0)—— 不在店里、不在起服里(与 M10 同为**窗内才存在的行为臂**,但它是改写):项目层键从 `repo:<sha256(项目根)[:24]>`
227
228
  * (7.98.0–7.132.x)换成带主体、跨设备的 `userrepo:<属主段>/<档>.<摘要>`(文法 `project-layer-key.ts`)。退役键是路径哈希、反推不出路径 ⇒ 起服没法枚举改写;
228
229
  * 改为**每个项目第一次跑任务时**由项目层准备一步(`memory-project-layer.ts` `createMemoryProjectLayers`)把该 checkout 的 `repo:` 层经 core `migrateScope`
229
- * 折进新键(File 与两只 SQL 孪生同一条;id 保留、CAS,冲突留原地下次再试)。同一窗同删的另一半:同步客户端腿的旧游标文件 `sync-cursor-<peer>.json`(只按 peer 分区)
230
- * 第一轮收编为同步层的新游标(`memory-sync-client.ts` `adoptLegacyCursor`)。本条不写起服读数(`reportKeys` 空,与 M7 / M10 同形);折了什么由运行期 info
231
- * `memory_project_layer_folded` / `memory_sync_cursor_adopted` 说。常设的另一条律(`r` 档时本 checkout 的 `p` 档折进当前档)不是迁移,不随本条删。
230
+ * 折进新键(File 与两只 SQL 孪生同一条;id 保留、CAS,冲突留原地下次再试)。本条不写起服读数(`reportKeys` 空,与 M7 / M10 同形);折了什么由运行期 info
231
+ * `memory_project_layer_folded` 说。常设的另一条律(`r` 档时本 checkout 的 `p` 档折进当前档)不是迁移,不随本条删。
232
+ * **曾属本条的另一半已退役(如实)**:同步客户端腿的旧游标文件 `sync-cursor-<peer>.json`(只按 peer 分区)的收编臂。引擎 7.59.0 起游标必带店绑定 `store`,
233
+ * 升级前落盘的旧文件都没有这一员,收编的形判从不认它 ⇒ 臂对任何存量文件都不可达,已整条删码(收编函数、守卫旗标、日志行一并删,零引用有钉:
234
+ * `test/memory-sync-client.test.ts`)。旧文件不再被当游标;它只回答「同步层有没有一份店绑定之前的基线」—— 新路径上没有文件而它是同步层的无绑定基线 ⇒
235
+ * 同步层不同步、运维通告点名它(与新路径上的无绑定游标同一条律,`memory-sync-client.ts` 头注 ⑤;不是本条的迁移臂,不随本条删)。
232
236
  * · **M26 删掉退役的共享断路器表 `circuit_breaker`**(7.133.0)—— 断路器键的路由段从 baseUrl **原串**换成路由身份的摘要(铸点与形 `breaker-key.ts`),
233
237
  * 新一代键写**新表** `breaker_state`(中央 DDL 建)。旧表 `circuit_breaker` 的行是旧键(`<provider>:<id>@<baseUrl>`,网关根原串 query 可带凭据),
234
238
  * 新代码不读不写它、它也没有 TTL 腿 ⇒ 不删就永远在共享库里。**整只删表**(`DROP TABLE IF EXISTS circuit_breaker`,不看任何一行的内容):
@@ -252,7 +256,7 @@
252
256
  * `http/checkpoint-ctx.ts` 末段「凭据位密封」;本处不重列)在 `checkpoint_ctx` 里只以封签存在,续跑上下文读口对凭据位上的明文答 409 `conflict.checkpoint_ctx_unsealable`(`reason:"plaintext"`)。升级之前写下的
253
257
  * 行是明文 ⇒ **起服扫一遍、原地封好**:不在本文件的迁移段里(封要保管店的封签器,而本段跑在建店的 web-admin 运行时能交给它之前的店层 ——
254
258
  * 与 M10 / M19 / M25 同为在本段之外跑的条目),编排在 `checkpoint-ctx-seal-m28.ts`(起服第 6 段,checkpoint 店建成之后、任何路由装配之前调一次),
255
- * 店腿是两孪生各自的 `rewriteCtxRowsM28`(SQL:键集分页 + 按原文 CAS、`updated_at_ms` 不动;local:逐档原子重写、信封的 `updatedAt` 不动),
259
+ * 店腿是两孪生共有的 ctx 行改写腿 `rewriteCtxRows`(`checkpoint-store-sql.ts` `CheckpointCtxRowsRewriter`;M30 用同一只;SQL:键集分页 + 按原文 CAS、`updated_at_ms` 不动;local:逐档原子重写、信封的 `updatedAt` 不动),
256
260
  * 行改写是读写两侧同一只封口(`sealStoredCheckpointCtxM28`),封的是「读口会答 `plaintext` 的每一个值」(与开口同一只判词)—— 7.135.0 闭集加了一位
257
261
  * (command 条目的 `plugin.options`)之后,7.134.0 写下的带记号行在那一位上的明文也落在这一条里、同窗补封(本条没有新窗);7.136.0 再加四位(子进程的命令行:MCP stdio 传输与
258
262
  * command 条目各自的 `command` / `args`,第 3 代)同理 —— 无记号行与 7.134.0 / 7.135.0 写下的第 1 / 2 代行上的明文命令行同窗补封。不删行、不改续跑语义;读不出的行计数不动;已封与不带凭据的行零写。读数不进
@@ -271,6 +275,18 @@
271
275
  * 迁移段先于起服列册对账,缺列表上的扫描会抛裸的「未知列」、盖住随后的拒启句)。到期 facet 的生成谓词与运维队列行的卡读这一列 —— 本条跑完之前它们不运行
272
276
  * (迁移段先于任何路由与收割器装配)。**够不到的一格(如实登记)**:已决 / 已作废的行不扫(没有读者读它们的卡);local 车道没有这一列(停驻行在引擎自己的 File 店,
273
277
  * 卡从文档读)。窗 = 发布日 + 30 天(与 M21 同一条论证:窗关时缺省部署上升级前停驻的行已被终局兜底收走)。
278
+ * · **M30 停驻行请求体里的退役键 `promptProfile` 原地改写**(7.137.0,引擎 7.56.0)—— 提示词形收成布尔 `promptLean`,双形词 `promptProfile` 整键退役(新提交在键闭集门 400)。
279
+ * 7.136.x 及以前停驻的任务,`checkpoint_ctx` 存的请求体带旧键 —— 续跑腿重放它而没有读者 ⇒ `"classic"`(要长形)静默回到模型声明的短形。⇒ **起服扫一遍、原地改写**
280
+ * (与 M28 同形同处:不在本文件的迁移段里 —— 那一段跑在 checkpoint 店建成之前;编排在 `stored-request-prompt-profile-m30.ts`,起服第 6 段 M28 之后调一次,
281
+ * 早于任何路由装配;店腿是两孪生共有的 `rewriteCtxRows`,与 M28 同一只;行改写 = `http/checkpoint-ctx.ts` `rewriteStoredBodyKeys`,只动体的这两个顶层键,
282
+ * 读法记号与凭据位原样随行)。映射与 MIGRATION 写给调用方的同一张:`"classic"` 且体里没有布尔 `promptLean` ⇒ 删旧键、写 `promptLean: false`;`"simple"` / 词表外 /
283
+ * 体里已有布尔 `promptLean` ⇒ 只删旧键。已改写与不带旧键的行零写;读不出的行计数不动;续跑时钟不动。读数不进 {@link OneTimeMigrationReport}(`reportKeys` 空,与 M28 同形),
284
+ * 由起服 info 一行 `checkpoint_ctx_prompt_profile_rewritten` 说(行数,不带值)。窗外不扫。**续跑腿不读旧键**(没有按日历分的第二种读法);存体里仍带它的行
285
+ * (窗内从没起过本版的部署;滚动升级里旧副本在本版起服扫描之后才写下的行)由续跑判据响亮拒(`http/stored-body-replay.ts` 第四问,409 `checkpoint.unsupported_version`),
286
+ * 窗内再起一次本版即被改写。窗 = 发布日 + 30 天(与 M28 / M29 同一条论证:窗关时缺省部署上升级前停驻的行已被终局兜底收走)。**本条不覆盖混跑(如实登记)**:
287
+ * ≤7.136.x 的读者只认 `promptProfile`,读到本条改写过的体(旧键已删、换成 `promptLean`)回到缺省形 —— 本版副本起服之后仍在续跑的 7.136.x 副本、回滚到 7.136.x 之后,
288
+ * 被改写过的 `"classic"` 行静默回短形;也不能给旧读者留旧键(那是同一事实的两份表示,本版读者对仍带旧键的体响亮拒)⇒ 升级序是**先停光 ≤7.136.x 副本、再起第一个 7.137.0 副本**,
289
+ * 回滚之前先决完或取消停驻的任务(随包 MIGRATION 7.137.0 第 4 条;与 M28 的混跑登记同形)。
274
290
  *
275
291
  * ## 收据账(一次起服一只,每一条迁移同一只)
276
292
  * 每一步把它**已提交**的改写 / 删除与点名读数当场记进同一只收据账({@link MigrationReceipts}):计数随每次提交累加、行随每次提交追加、形读数在那一步落定时写。走完 ⇒
@@ -460,9 +476,15 @@ export declare const ONE_TIME_MIGRATIONS: readonly OneTimeMigrationWindow[];
460
476
  /**
461
477
  * 一条登记行的窗**此刻**开着没有(`today` = UTC 日历日 YYYY-MM-DD):未发布形(`removableAfter: null`)⇒ 开;否则今天不晚于 `removableAfter` ⇒ 开
462
478
  * (窗关当天仍开,与到期门「今天严格晚于才红」同一条边界)。
463
- * 只给**窗内才生效的行为臂**用(M10 的认领、M19 / M20 的续跑重读):那种臂过了窗就停,不等删码车 —— 删码因此是零行为变化,而窗只写在本表这一处。
479
+ * 给**窗内才生效的行为臂**(M10 的认领、M19 / M20 的续跑重读)与本文件迁移段之外的起服扫描(M28 / M30)用:过了窗就停,不等删码车 —— 删码因此是零行为变化,而窗只写在本表这一处。
464
480
  */
465
481
  export declare function migrationWindowOpen(window: Pick<OneTimeMigrationWindow, "removableAfter">, today: string): boolean;
482
+ /**
483
+ * 一条登记行的窗的**最后一天**(= `removableAfter`,UTC 日历日 YYYY-MM-DD;与 {@link migrationWindowOpen} 同一条边界:这一天仍开)—— 给**要在句子里写绝对日期**的读者读
484
+ * (续跑拒句「在这一天之前重启即被改写」):窗只写在本表这一处,句子不手写第二份。行不在(读者活过了它的登记行,该随那一行一起删)或未发布形(`removableAfter: null`,
485
+ * 还没有日期可写)⇒ 响亮抛,不铸一句带空日期的句子。
486
+ */
487
+ export declare function migrationWindowEnd(id: string): string;
466
488
  /** local 车道上 SQL 独有的几条(M8 / M9 / M11 / M17 / M18 / M21 / M23 / M24 / M26 / M27 / M29)的读数(M6 两车道都有,local 腿的读数由 `LocalBackend.runOneTimeMigrations` 现算):那几张表 / 列在文件底座上从来不存在 ⇒ 如实答 absent / 0。 */
467
489
  export declare const NO_SQL_MIGRATIONS: Omit<OneTimeMigrationReport, "holderDeathCodeRowsBackfilled">;
468
490
  /** 启动期的日志口(logger 的子集)。两级各有其用,见 {@link logOneTimeMigrations}。 */
@@ -520,26 +542,26 @@ export declare function moveWorkflowJournalScope(db: MigrationDb, dialect: "tidb
520
542
  * 不是对象 / 没有这两个键 / 条目文字里出现同名字样 ⇒ 空。不改写:新文法写不出旧命令桶的拒(头注 M17 段 ③)。
521
543
  */
522
544
  export declare function retiredSessionRuleBucketsOf(body: unknown): readonly RetiredSessionRuleBucket[];
523
- /** M17 —— 一份存量规则体按引擎在它自己的文法里的读法改写成当前文法的同一句话({@link oldGrammarEntriesOf} 的读数)。 */
545
+ /** M17 —— 一份存量规则体按引擎在它自己的文法里的读法改写成文法 1 里的同一句话({@link oldGrammarEntriesOf} 的读数)。 */
524
546
  export interface OldGrammarEntries {
525
547
  /** toolAllow 里删掉的条目(引擎的 `earlier_grammar` 读数;见 {@link SessionRuleRowOldGrammarEntries.toolAllowDropped})。行因 {@link toolDenyUnreadable} 整行不动时恒空(没写 ⇒ 没删)。 */
526
548
  readonly toolAllowDropped: readonly string[];
527
549
  /** toolDeny 里引擎 `unreadableDenyEntries` 读成「不是任何一条规则」的条目(条目 + 引擎读者的原因词)。**非空 ⇒ 这一行不改写、不盖戳**({@link rewritten} 缺席):
528
550
  * 这种行引擎对每一次工具调用都拒(读成更少的规则是 fail-open,引擎不这么读),并按行自带的旧文法通告;盖成当前文法戳只会让通告消失、行照旧拒一切。缺席 = 没有这样的条目。 */
529
551
  readonly toolDenyUnreadable?: readonly UnreadableDenyEntry[];
530
- /** 这一行要落的新体 —— 被删条目去掉、其余键原样(退役命令桶也原样)、`grammar: SESSION_RULES_GRAMMAR`;在场 = 要写。缺席 = 零动作:行已带戳且没有可删的条目
552
+ /** 这一行要落的新体 —— 被删条目去掉、其余键原样(退役命令桶也原样)、`grammar: 1`(本步证过同义的那一版);在场 = 要写。缺席 = 零动作:行已带戳且没有可删的条目
531
553
  * (上一次没走完的判读已改写过的行)。 */
532
554
  readonly rewritten?: Record<string, unknown>;
533
555
  }
534
556
  /**
535
557
  * M17 —— 一行存量规则(体 + 它的 `rev`)的改写判读(纯函数,判据只有引擎的):
536
558
  * · 行按引擎的形读法在**它自己的文法**里读 —— `readSessionRulesShape` 对与 `rev` 并排的值按戳读,无戳 ⇒ 文法 0(戳之前写的行);退役命令桶先放一边(形读法拒它们,
537
- * 而改写的是同一行其余的桶 —— 人按出路删掉退役桶的那一刻,行已是当前文法的真话);
559
+ * 而改写的是同一行其余的桶 —— 人按出路删掉退役桶的那一刻,行已是文法 1 的真话);
538
560
  * · `toolAllow` 经 `compileSessionRuleBucket` 在那种文法下编译,归 `earlier_grammar` 的条目删掉(文法 0 与 1 在引擎里只差这一条子句:拼写不是它所指名字的 allow 条目
539
561
  * 在文法 0 里什么都不放行、在文法 1 里是一条放行规则),名单**保持在场**(在场的空 `toolAllow` 引擎读作「什么都不放行」);别的桶一个字节不动;
540
562
  * · **盖戳前先问引擎读者**([ref]):行的 `toolDeny` 里有 `unreadableDenyEntries` 答的条目(引擎在这一行的文法里读成「不是任何一条规则」)⇒ **整行不改写、不盖戳、不删任何条目**,
541
563
  * 条目与读者的原因词随 {@link OldGrammarEntries.toolDenyUnreadable} 交出(收据点名)。引擎对这种行本来就拒一切 —— 本函数只问、不判:不复刻引擎的条目读者,不替运维删条目或改写。
542
- * · 新体盖 `grammar: SESSION_RULES_GRAMMAR`:无戳行每一行都写(改写之后部署里零无戳行 —— 除上一条留下的行);已带戳且没有可删条目的行零动作(幂等)。
564
+ * · 新体盖 `grammar: 1`({@link M17_PROVEN_GRAMMAR}:本步证过同义的那一版):无戳行每一行都写(改写之后部署里零无戳行 —— 除上一条留下的行);已带戳且没有可删条目的行零动作(幂等)。
543
565
  * 读不出形(不是对象、有别的非桶键、桶不是一列字符串、坏戳 / 比引擎新的戳)⇒ `undefined`,不判(读回照旧 `corrupt`)。本函数零处读 / 比较戳的数值:
544
566
  * 「有没有戳」是形读法自己判戳之前的同一问;条目在哪种文法下算什么只有引擎读。
545
567
  */
@@ -1,4 +1,4 @@
1
- import { SESSION_RULES_GRAMMAR, compileSessionRuleBucket, isGateKind, readSessionRulesShape, unreadableDenyEntries } from "@sema-agent/core";
1
+ import { compileSessionRuleBucket, isGateKind, readSessionRulesShape, unreadableDenyEntries } from "@sema-agent/core";
2
2
  import { APPROVAL_GATE_KINDS, ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind } from "../approval-content-kind.js";
3
3
  import { judgeCheckpointIndexCells } from "./checkpoint-index-columns.js";
4
4
  import { dialectProtocolJsonEncoder } from "./sql-driver.js";
@@ -210,7 +210,8 @@ export const ONE_TIME_MIGRATIONS = [
210
210
  removal: "删模式词读法记号的判据与拒臂:`http/stored-body-replay.ts` 的 `permissionModeReadingUnstated`;`http/checkpoint-ctx.ts` 的 `permissionModeReadingStated` / `permissionModeReadingRefusal` / `PERMISSION_MODE_READINGS`、" +
211
211
  "`StoredBodyReplayRefusal` 的 `permission_mode_reading` 臂及其日志字段与答复臂、`buildCheckpointCtx` 与 `withPermissionModeRewritten` 两处记号写点" +
212
212
  "(后者删后 `resume-legs.ts` `drivePlanReview` 里对它的那一处调用回到只换体的 `{ ...ctx, body }`)、`CheckpointCtx.permissionModeReading`、" +
213
- "`http/stored-body-replay.ts` 里 `StoredReplayDeployment` 的 `autonomy` 位与 `storedBodyReplayRefusal` 读 `deployment.autonomy` 的那一句(这一位只服务这一问;四个问点传 `deps.config` 整份,home 位还服务 `permissions` 一问,不动)",
213
+ "`http/stored-body-replay.ts` 里 `StoredReplayDeployment` 的 `autonomy` 位与 `storedBodyReplayRefusal` 读 `deployment.autonomy` 的那一句(这一位只服务这一问;四个问点传 `deps.config` 整份,home 位还服务 `permissions` 一问,不动);" +
214
+ "`test/stored-request-prompt-profile-m30.test.ts` 的 SHARED 表里 `StoredBodyReplayRefusal` 那一行(rows 含 M12;M30 的登记行组已先删时该表已不在,这一句无事可做):rows 去掉 M12、整行并入同文件的 SITES(值 = 该行的 files 数组)",
214
215
  afterRemoval: "没有响亮臂:删码之后仍停驻、在 7.121.x 及以前写下的行(把 `APPROVAL_TERMINAL_BACKSTOP_MS` 调长的部署,或跳过了带本条的版本直接升的库)在设了 " +
215
216
  "`AUTONOMY=plan` 的部署上按体里的原词重放(运维的 plan 不再被按段重钳)—— 升级前把这些停驻的 run 决掉 / 取消,或升级窗内设 `AUTONOMY=read-only`",
216
217
  reportKeys: [],
@@ -392,11 +393,10 @@ export const ONE_TIME_MIGRATIONS = [
392
393
  releasedOn: "2026-10-21",
393
394
  removableAfter: "2026-11-20",
394
395
  removal: "删 `project-identity.ts` 的 `mintRepoScopeKey`(与它的窗注;`RETIRED_PROJECT_KEY_PREFIX` 留给 `isServerMintedProjectKey` 的起服拒)与 `projectLayerForTaskRoot` 的 `foldFrom` 里它那一员" +
395
- "(`p` 档那一员留 —— 常设律);`memory-sync-client.ts` 的 `legacyMemorySyncCursorPath` / `adoptLegacyCursor` 与 runner 里调它的那一段(`legacyCursorChecked`);" +
396
- "`test/memory-project-layer-fold.test.ts` 的 ① 格与 `test/memory-project-layer-fold-db-integration.test.ts` 整份、`test/memory-sync-client.test.ts` 的旧游标收编格、" +
396
+ "(`p` 档那一员留 —— 常设律);" +
397
+ "`test/memory-project-layer-fold.test.ts` 的 ① 格与 `test/memory-project-layer-fold-db-integration.test.ts` 整份、" +
397
398
  "`test/project-identity.test.ts` 的 `repo:` 金样格(公式金样改钉 `p` 档摘要)",
398
- afterRemoval: "没有新的臂(如实):窗内一次都没跑过任务的项目,它的 `repo:` 层留在库里、不再被任何读列表包含(不读、不删;起服拒只管运维点名这一族);出路 = 升级时先过一次带本条的版本并在每个项目跑一次任务。" +
399
- "同步腿的旧游标文件不再收编:同步层从空基线首轮重收敛(两边都改过的条目按冲突上报,不静默丢)",
399
+ afterRemoval: "没有新的臂(如实):窗内一次都没跑过任务的项目,它的 `repo:` 层留在库里、不再被任何读列表包含(不读、不删;起服拒只管运维点名这一族);出路 = 升级时先过一次带本条的版本并在每个项目跑一次任务",
400
400
  reportKeys: [],
401
401
  },
402
402
  {
@@ -428,8 +428,13 @@ export const ONE_TIME_MIGRATIONS = [
428
428
  introducedIn: "7.134.0",
429
429
  releasedOn: "2026-10-21",
430
430
  removableAfter: "2026-11-20",
431
- removal: "删 `src/checkpoint-ctx-seal-m28.ts` 整个文件与起服第 6 段对它的那一处调用;两孪生的店腿 `rewriteCtxRowsM28` 与读数型 `CtxRowsRewriteM28`;" +
432
- "`http/checkpoint-ctx.ts` 的 `sealStoredCheckpointCtxM28`;`test/checkpoint-ctx-seal-m28.test.ts` 与 `test/checkpoint-ctx-credential-seal-db-integration.test.ts` 的 M28 格。" +
431
+ removal: "删 `src/checkpoint-ctx-seal-m28.ts` 整个文件(`sealCheckpointCtxCredentialsM28`)与起服第 6 段(`src/boot/stage-06-runners.ts`)顶部对 `sealCheckpointCtxCredentialsM28` 的 import 行与对它的那一处调用(连同起服 info 行 `checkpoint_ctx_credentials_sealed`);两孪生共有的 ctx 行改写店腿(`src/plugins/checkpoint-store-sql.ts` 与 `src/plugins/local-checkpoint-store.ts` 里的 `rewriteCtxRows` / `CtxRowsRewrite` / `CtxRowRewrite` / `CheckpointCtxRowsRewriter` / 扫描页 `CTX_REWRITE_PAGE`)只在本表再无别的条目用它时(今天 M30 也用)一起删;店腿删时 `test/checkpoint-ctx-sealed-brand.test.ts` 里 `rewriteCtxRows` 的断言(`@ts-expect-error` 与 `void local.rewriteCtxRows(` 两行)一起删 —— 店腿不在后那一行的 tsc 错变成「属性不存在」,`@ts-expect-error` 照样满足,断言静默变空 —— 同文件 `putCtx` 的常设断言保留;**店腿格不随本条删**:`test/checkpoint-ctx-rows-rewrite-db-integration.test.ts`(店腿自己的 CAS / 分页 / 一行装不下三格)与 `test/checkpoint-ctx-rows-rewrite-local.test.ts`(读不出的档 / 信封 updatedAt 不动两格)用中性的行改写、不借任何一条迁移的函数,店腿还有用户时它们要活着;店腿删时两个文件整文件随店腿删,`scripts/db-integration-files.json` 里 db 那个文件的行同删;店腿删时的同族注释(店腿是两条迁移共用的,谁后删谁做;两条 removal 都点名,逐条同字面):`src/http/checkpoint-ctx.ts`:「与一次性迁移的 ctx 行改写店腿(`rewriteCtxRows`)」删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 ctx 行改写回调(M28 / M30 共用)」删、「,M28 行改写回调交回裸对象 —— 三处都是 tsc 错」改成「 —— 两处都是 tsc 错」;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「另有各随一条一次性迁移的格」起到「别的文件的行一个都不碰);」止整段删;" +
432
+ "`http/checkpoint-ctx.ts` 的 `sealStoredCheckpointCtxM28`;`test/checkpoint-ctx-seal-m28.test.ts` 整个文件与 `test/checkpoint-ctx-credential-seal-db-integration.test.ts` 里的「[M28] 明文旧行」那一格(它调 `sealStoredCheckpointCtxM28`);" +
433
+ "`test/doc-claims-upgrade-notes.test.ts` ㊵ 组里读这处调用与日志行的三行断言随删(两格本身与其余断言留着;不删就是删码后这两格红):「锚(嵌入方段的每件事实读实现)」格的 `callSites(\"sealCheckpointCtxCredentialsM28\")` 一行;" +
434
+ "「锚(恢复句的每件事实读实现)」格里 guarded 上对 `sealCheckpointCtxCredentialsM28` 调用的断言与对 `logger.info(\"checkpoint_ctx_credentials_sealed\"` 日志行的断言两行;" +
435
+ "`test/stored-request-prompt-profile-m30.test.ts` 里钉起服第 6 段调用次序的那一格(`describe` 名含「起服接线」;M30 删码时才删它,M28 的窗先关,先删 M28 时它要活着)里读 M28 调用位置的 `const m28` 那一行(它用 indexOf 找 `sealCheckpointCtxCredentialsM28` 的调用)与「M28 在那一臂里」「M30 在那一臂里、M28 之后」两条断言,改成只相对 `guard` 比较 M30 的位置;" +
436
+ "依赖本行的格(行一删就红):doc-claims ㊵ 的「嵌入方一段分清两只迁移的窗」格(读 M28 / M30 两行的窗末日;哪一行先删就先删这一格,MIGRATION 7.137.0 节的文字是已发布的历史、留着不动),`test/stored-request-prompt-profile-m30.test.ts` 里「M28 登记行」组整组随删(组内三格都读 M28 行的 `removal`:「M28 的 removal 同样点名」「M28 删法条补四类漏项」「同一注释半句只归一条 removal」)、同文件 SHARED 表里 rows 含 M28 的五行改成只剩 M30 并入 SITES(M30 的登记行组已先删时该表已不在,这一句无事可做);" +
437
+ "同族注释(每个半句只归本条或 M30 条之一,不重复改;标〔另一条的〕的几处住在另一条迁移的文件 / 头注段里,那条已先删则宿主已不在,跳过):`src/http/checkpoint-ctx.ts`:「一次性迁移 M28 对存量行用同一只走法」删、「({@link sealStoredCheckpointCtxM28})」删、「、{@link sealStoredCheckpointCtxM28}」删、「(开口与 M28 同一只)」删、「(更早版本写下、M28 还没扫到)」改成「(更早版本写下)」、「读写两侧与一次性迁移 M28 共用」改成「读写两侧共用」、「已落盘的行由 M28 补封」改成「已落盘的行不补封(要补封须另起迁移)」、「M28 封的是」起到「窗外加位要另起迁移)。」止整段删、「(读写两侧与 M28 共用这一只)」改成「(读写两侧共用这一只)」、「(开口与 M28 共用这一只判词,闭集三词)」改成「(开口用这一只判词,闭集三词)」、「开口去开它;M28 不动它;」改成「开口去开它;」、「M28 把它封好;」删、「M28 不动它(留给读口响亮拒)」改成「(留给读口响亮拒)」、「封口 / 开口 / M28 共用的那一只」改成「封口 / 开口共用的那一只」;`src/sealed-key.ts`:「起服的一次性迁移 M28 会用它把还读得懂的明文行换成谁都开不开的密文」改成「新写的停驻行会被封成谁都开不开的密文」;`src/boot/web-admin.ts`:「与一次性迁移 M28(stage-06)」删、「M28 会拿它毁掉还读得懂的行」改成「新写的停驻行会被封成谁都开不开的密文」;`src/boot/stage-06-runners.ts`:「也是一次性迁移 M28 要它的地方。M28 在任何路由装配之前跑(窗外不扫;读数一行 info)。」删、「排在 M28 之后」删;`src/plugins/checkpoint-store-sql.ts`:「M28(凭据位明文封好)」删;`src/stored-request-prompt-profile-m30.ts`:〔另一条的〕「,与 M28 同一只」删、〔另一条的〕「本条与 M28 同在起服第 6 段」改成「本条在起服第 6 段」、〔另一条的〕「(M28 用同一只)」删;`src/plugins/one-time-migrations.ts`:〔另一条的〕「起服第 6 段 M28 之后调一次」改成「起服第 6 段调一次」、〔另一条的〕「店腿是两孪生共有的 `rewriteCtxRows`,与 M28 同一只」改成「店腿是两孪生共有的 `rewriteCtxRows`」;`test/owner-gate-single-owner.test.ts`:「两只封口 `sealCheckpointCtx` / `sealStoredCheckpointCtxM28` 经它」改成「封口 `sealCheckpointCtx` 经它」;`test/drain-check-local.test.ts`:「封口 / 开口 / M28 共用的凭据位遍历器」改成「封口 / 开口共用的凭据位遍历器」;`test/checkpoint-ctx-credential-seal.test.ts`:「(否则 M28 会把可用的明文行换成谁都开不开的密文)」改成「(否则新写的停驻行会被封成谁都开不开的密文)」;`test/helpers/ctx-sealing.ts`:「 / M28 的行改写」删;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「/ `checkpoint-ctx-seal-m28.test.ts`」删、「窗与读数那一层在 local 半场(`checkpoint-ctx-seal-m28.test.ts`)钉」起到「两半场共用同一只行改写 `sealStoredCheckpointCtxM28`。」止整段删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 M28 的行改写(`sealStoredCheckpointCtxM28`)」删;" +
433
438
  "**不随本条删**:凭据位密封的常设规则(封口 / 开口 / 闭集 / 读口第四态 / 409 码 / 装配期拒启)",
434
439
  afterRemoval: "读口对凭据位上明文的常设拒臂接住:跳过了带本条的版本直接升的库,带明文凭据的停驻行续跑答 409 `conflict.checkpoint_ctx_unsealable`(`reason:\"plaintext\"`)," +
435
440
  "行字节不动 —— 出路 = 作为新任务重提(仍停驻的先取消),或升级时先过一次带本条的版本",
@@ -447,10 +452,46 @@ export const ONE_TIME_MIGRATIONS = [
447
452
  "(引擎对问答拒收 `sla_timeout`,拒扫腿每拍一条失败,由 `terminal_at_ms` 兜底收割);运维队列行不带卡词。出路 = 升级前答完 / 拒掉停驻的问答,或升级时先过一次带本条的版本",
448
453
  reportKeys: ["parkedApprovalCardsBackfilled", "parkedApprovalCardRowsUnreadable", "parkedApprovalCardRowsTooLong"],
449
454
  },
455
+ {
456
+ id: "M30",
457
+ introducedIn: "7.137.0",
458
+ releasedOn: "2026-10-25",
459
+ removableAfter: "2026-11-24",
460
+ removal: "删 `src/stored-request-prompt-profile-m30.ts` 整个文件(`retirePromptProfileM30` / `retirePromptProfileRowM30` / `PromptProfileRetireM30Report`)与起服第 6 段(`src/boot/stage-06-runners.ts`)顶部对 `retirePromptProfileM30` 的 import 行与对它的那一处调用(连同起服 info 行 `checkpoint_ctx_prompt_profile_rewritten`);" +
461
+ "`http/checkpoint-ctx.ts` 的 `rewriteStoredBodyKeys` 与 `CREDENTIAL_SLOT_ROOTS`(若再无别的调用方);两孪生共有的 ctx 行改写店腿(`src/plugins/checkpoint-store-sql.ts` 与 `src/plugins/local-checkpoint-store.ts` 里的 `rewriteCtxRows` / `CtxRowsRewrite` / `CtxRowRewrite` / `CheckpointCtxRowsRewriter` / `CTX_REWRITE_PAGE`)只在本表再无别的条目用它时一起删(今天 M28 也用);店腿删时 `test/checkpoint-ctx-sealed-brand.test.ts` 里 `rewriteCtxRows` 的断言(`@ts-expect-error` 与 `void local.rewriteCtxRows(` 两行)一起删 —— 店腿不在后那一行的 tsc 错变成「属性不存在」,`@ts-expect-error` 照样满足,断言静默变空 —— 同文件 `putCtx` 的常设断言保留;**店腿格不随本条删**:`test/checkpoint-ctx-rows-rewrite-db-integration.test.ts`(店腿自己的 CAS / 分页 / 一行装不下三格)与 `test/checkpoint-ctx-rows-rewrite-local.test.ts`(读不出的档 / 信封 updatedAt 不动两格)用中性的行改写、不借任何一条迁移的函数,店腿还有用户时它们要活着;店腿删时两个文件整文件随店腿删,`scripts/db-integration-files.json` 里 db 那个文件的行同删;店腿删时的同族注释(店腿是两条迁移共用的,谁后删谁做;两条 removal 都点名,逐条同字面):`src/http/checkpoint-ctx.ts`:「与一次性迁移的 ctx 行改写店腿(`rewriteCtxRows`)」删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 ctx 行改写回调(M28 / M30 共用)」删、「,M28 行改写回调交回裸对象 —— 三处都是 tsc 错」改成「 —— 两处都是 tsc 错」;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「另有各随一条一次性迁移的格」起到「别的文件的行一个都不碰);」止整段删;" +
462
+ "`http/checkpoint-ctx.ts` 的拒句铸点 `retiredRequestKeyRefusal`:模板串里从「If whoever operates this deployment restarts this server on or before 〈窗末日〉 (UTC), the stored request is rewritten in place and the same request can be sent again; otherwise submit」起的整段换成大写开头的「Submit」(窗关后重启不再改写;只删半句会留下句首小写、悬空的 otherwise),模板串变回整句双引号字面 —— 删后整句(逐字;冻结表那一行搬回字面表时也是这一句):「this task was suspended by an earlier server version and its stored request still carries promptProfile, a request key this version no longer reads (it was replaced by the boolean promptLean: classic is promptLean: false, simple is leaving promptLean out), so continuing would run the task with a prompt form it was not submitted with. Nothing was decided and nothing was resumed. Submit the work again as a new task with promptLean in place of promptProfile, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel).」," +
463
+ "它的第二参数 `rewriteWindowEnd` 同删(定义处与私有的 `storedBodyReplayArmAnswer` 里 `retired_request_key` 臂上的那一处调用),`StoredBodyReplayRefusal` 的 `retired_request_key` 臂上的必填字段 `rewriteWindowEnd` 同删;" +
464
+ "`src/trace/self-minted-wire.ts` 的 `refusalSentenceForm` 里「409 checkpoint.unsupported_version#retired_request_key」那一行(自铸句型登记):骨架 attr 尾上的「submit the work again」改成「Submit the work again」(删后整句的形),since 改写成删码所在的版本(该表的律:改 attr 的同时改 since);" +
465
+ "`http/checkpoint-ctx.ts` 的日志字段铸点 `storedBodyReplayLogFields` 里 `retired_request_key` 臂的 `note` 句改成常设拒臂的句子(原句里「the one-time rewrite at startup did not reach it」与「or a restart of this version inside the window rewrites the row」两半删码后成假话,与本行 afterRemoval 的「出路 = 重提」矛盾;" +
466
+ "改后句:「this session's stored request still carries a request key this version retired and nothing rewrites it any more; it is not replayed — the task stays suspended until someone cancels it and resubmits it as a new task (promptLean in place of promptProfile)」);" +
467
+ "同族注释(每个半句只归本条或 M28 条之一,不重复改;标〔另一条的〕的几处住在另一条迁移的文件 / 头注段里,那条已先删则宿主已不在,跳过):`src/http/checkpoint-ctx.ts`:「两条出路(运维在一次性迁移的窗内重启本服务 ⇒ 起服改写存体、同一请求可再发;否则作为新任务重提,仍停驻的先取消)」改成「出路(作为新任务重提,仍停驻的先取消)」、「**窗的日期不手写**」起到「删法见登记表 M30 的」止整段删、「存体里的旧键由一次性迁移在起服时原地改写」改成「存体里的旧键没有迁移改写」、「(M30,`stored-request-prompt-profile-m30.ts`,行改写经本模块的 {@link rewriteStoredBodyKeys})。改写不到的残留行」改成「仍带旧键的残留行」、「;存体由 M30 起服改写」删、「与 {@link rewriteStoredBodyKeys}」删、「(存量行非凭据顶层体键的改写,凭据位与读法记号原样随行)」删;`src/http/wire-types.ts`:「由一次性迁移 M30 在起服时按同一张映射原地改写」改成「没有迁移改写」、「(`stored-request-prompt-profile-m30.ts`;M30 · 窗口见 `plugins/one-time-migrations.ts` 登记表)」删;`src/spec-fields.ts`:「由一次性迁移 M30 在起服时改写成 `promptLean`(`stored-request-prompt-profile-m30.ts`),」改成「不改写,」;`src/boot/stage-06-runners.ts`:「M30(存体里的退役请求键 `promptProfile` 原地改写)同在这里、」删、「(同一只 ctx 行改写店腿;不要封签器),同样早于任何续跑入口。」删;`src/plugins/checkpoint-store-sql.ts`:「、M30(退役请求键改写)」删;`src/plugins/one-time-migrations.ts`:〔另一条的〕「M30 用同一只;」删;`src/checkpoint-ctx-seal-m28.ts`:〔另一条的〕「(M30 用同一只)」删;`test/task-agents-876.test.ts`:「体由一次性迁移 M30 在起服时原地改写」改成「体没有迁移改写」;`test/allow-list-reading-park-refusal.test.ts`:「一次性迁移 M30 没改写到的残留」改成「没有迁移改写的残留」;`test/api-error-text-freeze.test.ts`:「一次性迁移 M30 没改写到的残留」改成「没有迁移改写的残留」、「洞 = 窗的最后一天(绝对日期)」起到「不手写。」止整段删;" +
468
+ "`http/stored-body-replay.ts` 里 `RETIRED_KEY_REWRITE_MIGRATION` 的 JSDoc 随表删;" +
469
+ "`http/stored-body-replay.ts` 的 `RETIRED_KEY_REWRITE_MIGRATION`(`satisfies Record<RetiredRequestKey, string>`:M30 删后 `promptProfile` 仍是退役键,这张表只能整张删)、" +
470
+ "判据里 `rewriteWindowEnd: migrationWindowEnd(…)` 那一项与 `migrationWindowEnd` 的引入行;`plugins/one-time-migrations.ts` 导出的 `migrationWindowEnd` 在无别的读者时一起删;" +
471
+ "冻结表 `test/api-error-text-freeze.test.ts` 的 `FROZEN_RESULT_BODY_TEMPLATE` 里那一行搬回字面表(删完半句后拒句是整句字面);" +
472
+ "`test/stored-request-prompt-profile-m30.test.ts` 的起服改写组、登记行组、「M28 登记行」组(M28 没先删时它还在:它读本文件顶部的引入与 COMMENTS 表,与登记行组同命)、「M30 起服接线」组(它钉的就是被删的那处调用与起服 info 行)与「M30 的混跑与回滚」组;「M30 的混跑与回滚」组随删:它读登记表头注 M30 段(随本行一起删)与 MIGRATION 7.137.0 节第 4 条(已发布的历史文字,留着不动);doc-claims ㊵ 的「嵌入方一段分清两只迁移的窗」格同样读本行的窗末日,随删(它与 M28 行共读,哪一行先删就先删这一格);「窗关后的残留」组保留,但它里面 (d) 读登记行的 `m30Row()` 用处(期望里的 `rewriteWindowEnd`)与 (d′) 整格随删,`m30Row` 助手随之删;" +
473
+ "`test/checkpoint-ctx-credential-seal-db-integration.test.ts` 的 M30 格与文件头对 `retirePromptProfileRowM30` 的引入行(模块删后这一行不删就是编译红);" +
474
+ "`test/stored-request-prompt-profile-m30.test.ts` 顶部:beforeEach / afterEach 钩子与 store / root 夹具、ctxFiles / bodyOf 助手只服务已删各组 ⇒ 随删;连同已删各组用的引入行一起删(对 `retirePromptProfileM30` 与 `migrationWindowEnd` 的引入、ONE_TIME_MIGRATIONS、vitest 的 beforeEach / afterEach / vi、node:fs、node:path、对 `migrationSectionFor`(`./helpers/migration-section.js`,只服务混跑组)的引入与各 helpers 的引入等;模块与导出删后这几行不删就是编译红),本钉自己的 SITES / SHARED / LOCATORS / COMMENTS 四张表与 codeMentions / commentText / renderComment / checkCommentEdit / checkMintSentence 助手(含 CommentEdit 型)与 LEG_COMMENTS 表及它们的引入(ts / eachNode / parseSource / existsSync)随登记行组删;" +
475
+ "「窗关后的残留」组留下,留下的引入:storedBodyReplayRefusal、storedBodyReplayAnswer、makeConfig、CheckpointCtx 与 vitest 的 describe / expect / it;legacyBody 助手与它用的 TaskRequestBody 也留着;" +
476
+ "`test/doc-claims-upgrade-notes.test.ts` ㊵ 组里读这处调用与日志行的三行断言随删(两格本身与其余断言留着;不删就是删码后这两格红):「锚(嵌入方段的每件事实读实现)」格的 `callSites(\"retirePromptProfileM30\")` 一行;" +
477
+ "「锚(恢复句的每件事实读实现)」格里 guarded 上对 `retirePromptProfileM30` 调用的断言与对 `logger.info(\"checkpoint_ctx_prompt_profile_rewritten\"` 日志行的断言两行。" +
478
+ "**不随本条删**:续跑判据的第四问(退役请求键闭集 `RETIRED_REQUEST_KEYS` / `retiredRequestKeyOf` / `retiredRequestKeyRefusal`,常设)",
479
+ afterRemoval: "续跑判据第四问的常设拒臂接住:跳过了带本条的版本直接升的库(或窗内从没起过本版的部署),体里仍带 `promptProfile` 的停驻行续跑在五个问点上答 409 `checkpoint.unsupported_version`" +
480
+ "(判据 `retired_request_key`,`http/stored-body-replay.ts`)、行字节不动 —— 出路 = 作为新任务重提(`promptLean` 代替 `promptProfile`;仍停驻的先取消),或升级时先过一次带本条的版本并在窗内起服",
481
+ reportKeys: [],
482
+ },
450
483
  ];
451
484
  export function migrationWindowOpen(window, today) {
452
485
  return window.removableAfter === null || today <= window.removableAfter;
453
486
  }
487
+ export function migrationWindowEnd(id) {
488
+ const row = ONE_TIME_MIGRATIONS.find((w) => w.id === id);
489
+ if (row === undefined)
490
+ throw new Error(`one-time-migrations: the registry has no ${id} row — a sentence that quotes its window outlived the row; delete it with that row`);
491
+ if (row.removableAfter === null)
492
+ throw new Error(`one-time-migrations: ${id} is in the unreleased form (removableAfter: null) — there is no window end to quote yet`);
493
+ return row.removableAfter;
494
+ }
454
495
  export const NO_SQL_MIGRATIONS = {
455
496
  allowRuleShadowRowsMigrated: 0,
456
497
  allowRuleShadowRowsUnreadable: 0,
@@ -725,8 +766,9 @@ export function oldGrammarEntriesOf(body, rev) {
725
766
  const toolAllowDropped = (shaped.rules.toolAllow ?? []).filter((e) => earlier.has(e));
726
767
  if (toolAllowDropped.length === 0 && body.grammar !== undefined)
727
768
  return { toolAllowDropped };
728
- return { toolAllowDropped, rewritten: { ...body, ...(toolAllow !== undefined ? { toolAllow } : {}), grammar: SESSION_RULES_GRAMMAR } };
769
+ return { toolAllowDropped, rewritten: { ...body, ...(toolAllow !== undefined ? { toolAllow } : {}), grammar: M17_PROVEN_GRAMMAR } };
729
770
  }
771
+ const M17_PROVEN_GRAMMAR = 1;
730
772
  const RETIRED_BUCKET_PATTERNS = ['%"commandDeny"%', '%"commandAllow"%'];
731
773
  async function scanSessionRuleRows(db, dialect, column, patterns, visit) {
732
774
  const text = jsonColumnTextExpr(dialect, column);
@@ -4,7 +4,7 @@ import { readdirSync } from "node:fs";
4
4
  import { performance } from "node:perf_hooks";
5
5
  import { FileDurableRulePartitionProvider, AppendLog, ensureDir, } from "@sema-agent/core";
6
6
  import { z } from "zod";
7
- import { DurableRuleScopeSchema, RuleOffer2Schema, buildRulePayloadHash, recordOwnerOf, refusedOwnerSentence } from "./permission-rule-store-sql.js";
7
+ import { DurableRuleScopeSchema, RuleOfferColumn, buildRulePayloadHash, recordOwnerOf, refusedOwnerSentence } from "./permission-rule-store-sql.js";
8
8
  import { replayJsonlGuarded } from "../observability/corrupt-read-seat.js";
9
9
  import { EDITED_RULE_BREADTH_WARNING_CODES, RULE_APPROVAL_KINDS, RULE_APPROVAL_RECORD_SCHEMA, RULE_APPROVAL_STATES, RULE_BEHAVIORS, readRuleOwner } from "@sema-agent/core";
10
10
  const RuleDotSchema = z.object({ actor: z.string().min(1), counter: z.number().int().nonnegative().safe() }).strict();
@@ -26,7 +26,7 @@ const ApprovalRecordSchema = z
26
26
  kind: z.enum(RULE_APPROVAL_KINDS),
27
27
  state: z.enum(RULE_APPROVAL_STATES),
28
28
  candidates: z.array(RuleCandidateSchema),
29
- offers: z.array(RuleOffer2Schema),
29
+ offers: z.array(RuleOfferColumn),
30
30
  createdAt: z.string(),
31
31
  toolCallId: z.string().optional(),
32
32
  boundInputHash: z.string().optional(),
@@ -31,23 +31,23 @@ export declare function writerOfSqlRuleStore(store: DurableRulePartition): Permi
31
31
  * (下方 `RuleCandidateSchema`;File 店的审批记录读同一只)。
32
32
  */
33
33
  export declare const DurableRuleScopeSchema: z.ZodType<RuleScope>;
34
- export declare const RuleOffer2Schema: z.ZodUnion<readonly [z.ZodObject<{
35
- kind: z.ZodLiteral<"single">;
36
- candidate: z.ZodNumber;
37
- }, z.core.$strict>, z.ZodObject<{
38
- kind: z.ZodLiteral<"batch">;
39
- candidates: z.ZodArray<z.ZodNumber>;
40
- segments: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
- uncoveredSegments: z.ZodOptional<z.ZodNumber>;
42
- uncoveredDetail: z.ZodOptional<z.ZodArray<z.ZodObject<{
43
- segment: z.ZodString;
44
- reason: z.ZodEnum<{
45
- redirection: "redirection";
46
- no_rule_form: "no_rule_form";
47
- cap_overflow: "cap_overflow";
48
- }>;
49
- }, z.core.$strict>>>;
50
- }, z.core.$strict>]>;
34
+ /** [ref] §4.1 —— 记录上的 OPTION 结构(core `RuleOffer2`):按 index 引用 `candidates`,绝不携带规则文本副本。
35
+ *
36
+ * 🔴 **形的读法只有 core 的一只**(core 7.56.0 [ref] `readRuleOffer2`;它也是 core 同意入口的记录检查读选项的那一只):不是平记录 / 形不声明的成员 /
37
+ * 下标不是非负安全整数 / 无成员的批 / 段不是串列 / 理由行不恰是段 + 理由 ⇒ `malformed`;种类词或理由词出闭集 ⇒ `unknown_word`。任何一个 problem ⇒ 本列读不出 ⇒
38
+ * 整行 / 整面拒(照本店 JSON 列读回的既有姿势,响亮,不端半截)。本仓不再手写臂表(修前一份逐臂 `.strict()` 的 zod 联合 = 同一个形的第二个读者;
39
+ * 唯一的具名差异是「无成员的批」—— zod 收、读法拒,而 core 的记录检查本来就拒)。成员 disjoint 等语义完整性仍归 core 的读侧检查,店只担保形。
40
+ * 这只 zod 型只是把 core 读法接进本店既有的列读者(`createZodColumnReader`)与记录 schema 的适配,不判任何一格;File 店读本店导出的这一只。 */
41
+ export declare const RuleOfferColumn: z.ZodPipe<z.ZodUnknown, z.ZodTransform<{
42
+ readonly kind: "single";
43
+ readonly candidate: number;
44
+ } | {
45
+ readonly kind: "batch";
46
+ readonly candidates: readonly number[];
47
+ readonly segments?: readonly string[];
48
+ readonly uncoveredSegments?: number;
49
+ readonly uncoveredDetail?: readonly import("@sema-agent/core").UncoveredSegmentDetail[];
50
+ }, unknown>>;
51
51
  export declare const PERMISSION_RULE_TABLE = "permission_rule";
52
52
  export declare const PERMISSION_RULE_APPROVAL_TABLE = "permission_rule_approval";
53
53
  export declare const PERMISSION_RULE_TICKET_TABLE = "permission_rule_ticket";
@@ -1,10 +1,9 @@
1
1
  import { createHash, randomBytes, randomUUID } from "node:crypto";
2
2
  import { z } from "zod";
3
- import { applyTombstones, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, foldDelta, isValidDurableScope, parseRuleText, readRowBehavior, screenRuleSyncState, PERMISSION_RULE_WRITER, RULE_ADD_ORIGINS, RULE_APPROVAL_KINDS, RULE_APPROVAL_RECORD_SCHEMA, RULE_APPROVAL_STATES, RULE_BEHAVIORS, RULE_SYNC_DROP_CODES, EDITED_RULE_BREADTH_WARNING_CODES, PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS, EMPTY_DURABLE_RULE_PARTITION, PERMISSION_RULES_LATER_GRAMMAR_CODE, RULE_OFFER_KINDS, SESSION_RULES_GRAMMAR, readRuleGrammarStamp, readRuleOwner, } from "@sema-agent/core";
3
+ import { applyTombstones, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, foldDelta, isValidDurableScope, parseRuleText, readRowBehavior, screenRuleSyncState, PERMISSION_RULE_WRITER, RULE_ADD_ORIGINS, RULE_APPROVAL_KINDS, RULE_APPROVAL_RECORD_SCHEMA, RULE_APPROVAL_STATES, RULE_BEHAVIORS, RULE_SYNC_DROP_CODES, EDITED_RULE_BREADTH_WARNING_CODES, PERSISTED_RULE_MATCHES, EMPTY_DURABLE_RULE_PARTITION, PERMISSION_RULES_LATER_GRAMMAR_CODE, SESSION_RULES_GRAMMAR, readRuleGrammarStamp, readRuleOwner, principalNameOf, readRuleOffer2, } from "@sema-agent/core";
4
4
  import { dialectProtocolJsonEncoder, sqlDbNowMs, SQL_DB_NOW_MS } from "./sql-driver.js";
5
5
  import { isMissingColumnError } from "./sql-errors.js";
6
6
  import { canonicalJsonStringify } from "@sema-agent/core";
7
- import { isPersistedRuleTool } from "../permission-rule-vocab.js";
8
7
  import { ensureIndexes } from "./ensure-index.js";
9
8
  import { createZodColumnReader, decodeJsonColumn, foldJsonColumn, jsonColumnText, readJsonObjectColumn, SqlJsonColumnError } from "./sql-json-column.js";
10
9
  import { redactSecrets } from "../trace/redact.js";
@@ -31,7 +30,7 @@ const PersistedRuleRowSchema = z
31
30
  .object({
32
31
  behavior: z.string().optional(),
33
32
  rule: z.string(),
34
- tool: z.string().min(1).refine(isPersistedRuleTool, { message: "not a tool the persisted-rule grammar speaks for" }),
33
+ tool: z.string().min(1),
35
34
  match: z.enum(PERSISTED_RULE_MATCHES),
36
35
  command: z.string(),
37
36
  scope: DurableRuleScopeSchema,
@@ -90,19 +89,14 @@ const RedeemedDotsSchema = z.record(z.string(), RuleDotSchema);
90
89
  const RuleEditedSchema = z
91
90
  .object({ index: z.number().int().nonnegative().safe(), text: z.string(), at: z.string(), warnings: z.array(z.enum(EDITED_RULE_BREADTH_WARNING_CODES)).optional() })
92
91
  .strict();
93
- const RULE_OFFER2_ARMS = {
94
- single: z.object({ kind: z.literal("single"), candidate: z.number().int().nonnegative().safe() }).strict(),
95
- batch: z
96
- .object({
97
- kind: z.literal("batch"),
98
- candidates: z.array(z.number().int().nonnegative().safe()),
99
- segments: z.array(z.string()).optional(),
100
- uncoveredSegments: z.number().int().nonnegative().safe().optional(),
101
- uncoveredDetail: z.array(z.object({ segment: z.string(), reason: z.enum(UNCOVERED_SEGMENT_REASONS) }).strict()).optional(),
102
- })
103
- .strict(),
104
- };
105
- export const RuleOffer2Schema = z.union([RULE_OFFER2_ARMS.single, RULE_OFFER2_ARMS.batch]);
92
+ export const RuleOfferColumn = z.unknown().transform((raw, ctx) => {
93
+ const read = readRuleOffer2(raw);
94
+ if ("problem" in read) {
95
+ ctx.addIssue({ code: "custom", message: `the stored rule offer does not read (${read.problem})` });
96
+ return z.NEVER;
97
+ }
98
+ return read.offer;
99
+ });
106
100
  export const PERMISSION_RULE_TABLE = "permission_rule";
107
101
  export const PERMISSION_RULE_APPROVAL_TABLE = "permission_rule_approval";
108
102
  export const PERMISSION_RULE_TICKET_TABLE = "permission_rule_ticket";
@@ -364,7 +358,7 @@ const readTombstonesJson = createZodColumnReader(z.array(RuleTombstoneSchema));
364
358
  const readQuarantinedJson = createZodColumnReader(z.array(QuarantinedRuleAddRowSchema));
365
359
  const readObservedVectorJson = createZodColumnReader(FrontierSchema);
366
360
  const readCandidatesJson = createZodColumnReader(z.array(RuleCandidateSchema));
367
- const readOffersJson = createZodColumnReader(z.array(RuleOffer2Schema));
361
+ const readOffersJson = createZodColumnReader(z.array(RuleOfferColumn));
368
362
  const readRedeemedDotsJson = createZodColumnReader(RedeemedDotsSchema);
369
363
  const readEditedJson = createZodColumnReader(RuleEditedSchema);
370
364
  async function refusingUnreadableColumns(what, fn) {
@@ -392,6 +386,19 @@ function decodeBucketRow(ownerKey, row) {
392
386
  const rules = rulesRaw.map((r) => ({ ...r, behavior: readBucketRowBehavior(r.behavior, "rule row", ownerKey) }));
393
387
  const tombstones = tombstonesRaw.map((t) => ({ ...t, behavior: readBucketRowBehavior(t.behavior, "tombstone", ownerKey) }));
394
388
  const quarantined = quarantinedRaw.map((q) => ({ ...q, behavior: readBucketRowBehavior(q.behavior, "quarantined row", ownerKey) }));
389
+ refuseRowsTheValidatorRefuses(ownerKey, rules, tombstones);
390
+ return {
391
+ actor: String(row.actor ?? ""),
392
+ counter: Number(row.counter ?? 0),
393
+ rev: Number(row.rev ?? 0),
394
+ rules,
395
+ tombstones,
396
+ quarantined,
397
+ ...(observedVector !== undefined ? { observedVector } : {}),
398
+ ...(grammar !== undefined ? { grammar } : {}),
399
+ };
400
+ }
401
+ function refuseRowsTheValidatorRefuses(ownerKey, rules, tombstones) {
395
402
  const badTombstone = tombstones.find((t) => {
396
403
  const parsed = parseRuleText(t.rule, t.behavior);
397
404
  return "reject" in parsed || parsed.rule.rule !== t.rule;
@@ -399,21 +406,11 @@ function decodeBucketRow(ownerKey, row) {
399
406
  if (badTombstone !== undefined) {
400
407
  throw new BucketRefusedError(`permission_rule bucket ${ownerKey} carries a tombstone whose rule text the single validator refuses or does not spell canonically`, `(${badTombstone.rule}) — refusing the whole bucket rather than keeping a rule alive under a tombstone that can never match it`);
401
408
  }
402
- const screened = screenRuleSyncState({ rules, tombstones });
409
+ const screened = screenRuleSyncState({ rules: [...rules], tombstones: [...tombstones] });
403
410
  if (screened.rejected.length > 0) {
404
411
  const names = screened.rejected.map((r) => `${r.rule}(${r.reason})`).join(", ");
405
412
  throw new BucketRefusedError(`permission_rule bucket ${ownerKey} carries rows the single validator refuses`, `[${names}] — refusing the whole bucket rather than authorizing a rule whose text and matcher metadata disagree`);
406
413
  }
407
- return {
408
- actor: String(row.actor ?? ""),
409
- counter: Number(row.counter ?? 0),
410
- rev: Number(row.rev ?? 0),
411
- rules,
412
- tombstones,
413
- quarantined,
414
- ...(observedVector !== undefined ? { observedVector } : {}),
415
- ...(grammar !== undefined ? { grammar } : {}),
416
- };
417
414
  }
418
415
  function fencedCasWhere(dialect, firstPlaceholder, ownerKey, cas) {
419
416
  const ph = (offset) => (dialect === "tidb" ? "?" : `$${firstPlaceholder + offset}`);
@@ -427,6 +424,12 @@ async function writeBucket(exec, dialect, ownerKey, cur, next, nowMs) {
427
424
  if (!writable.success) {
428
425
  throw new Error(`refusing to write a permission_rule bucket this backend could not read back: ${writable.error.message}`);
429
426
  }
427
+ try {
428
+ refuseRowsTheValidatorRefuses(ownerKey, next.rules, next.tombstones);
429
+ }
430
+ catch (e) {
431
+ throw new Error(`refusing to write a permission_rule bucket this backend could not read back: ${e.message}`);
432
+ }
430
433
  const body = { rules: next.rules, tombstones: next.tombstones, quarantined: cur.quarantined, ...(cur.observedVector !== undefined ? { observedVector: cur.observedVector } : {}) };
431
434
  const enc = dialectProtocolJsonEncoder(dialect);
432
435
  const where = fencedCasWhere(dialect, 7, ownerKey, { column: "rev", expected: cur.rev });
@@ -578,9 +581,13 @@ export class SqlDurableRulePartitionProvider {
578
581
  this.now = now;
579
582
  }
580
583
  forPrincipal(principal) {
581
- if (typeof principal !== "string" || principal === "")
584
+ if (principal === undefined || principal === "")
582
585
  return EMPTY_DURABLE_RULE_PARTITION;
583
- return new SqlDurableRulePartition(this.db, { kind: "principal", principal }, this.now);
586
+ const name = principalNameOf(principal);
587
+ if (name === undefined) {
588
+ throw new Error('the principal given is not a principal name — refusing to resolve a bucket for it; a provided value that names nobody is not "no principal"');
589
+ }
590
+ return new SqlDurableRulePartition(this.db, { kind: "principal", principal: name }, this.now);
584
591
  }
585
592
  forLocalOwner() {
586
593
  return new SqlDurableRulePartition(this.db, { kind: "local-owner" }, this.now);
@@ -1,6 +1,7 @@
1
1
  import pg from "pg";
2
2
  import { recordFailOpen } from "../observability/fail-open.js";
3
3
  import { pollUntilLocked } from "./ensure-schema-lock-policy.js";
4
+ import { RUN_IDENTITY_COLUMNS } from "../identity-source.js";
4
5
  import { ensureSchema as ensureSessionSchema } from "./pg-session-storage.js";
5
6
  import { ensureSchema as ensureToolResultSchema } from "./tool-result-store-sql.js";
6
7
  import { ensureSchema as ensureCostQuotaSchema } from "./pg-cost-quota.js";
@@ -32,6 +33,9 @@ export const PG_SCHEMA_STATEMENTS = [
32
33
  instance_id VARCHAR(64) COLLATE "C",
33
34
  job_id VARCHAR(64) COLLATE "C",
34
35
  source VARCHAR(64) COLLATE "C",
36
+ -- identity_*: the run's identity tuple (semantics in the MySQL twin, tidb-pool.ts task_run; column definitions' single owner =
37
+ -- src/identity-source.ts RUN_IDENTITY_COLUMNS).
38
+ ${RUN_IDENTITY_COLUMNS.pg},
35
39
  -- 🔴 **刻意不写 COLLATE C** —— 本表其余字符串列都钉了,只有这一根是例外,理由是实测出来的
36
40
  -- (#192,codex 复审 round5;证据 scratchpad/evidence/u6-lower-collate-c.txt):
37
41
  -- PG 的 lower() 按**表达式的排序规则**决定大小写映射,而 C 只带 ASCII 映射 ⇒
@@ -6,6 +6,8 @@ import { type PlacementAcquireOpts } from "./session-placement.js";
6
6
  import { type OrphanOwnerLog } from "../delegated-session-owner.js";
7
7
  import { type StagingHandle } from "../session-sync-kernel.js";
8
8
  import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
9
+ import type { SessionWorkspace } from "../session-workspace.js";
10
+ import type { RetentionDeclaration } from "@sema-agent/core";
9
11
  import { type SessionFace } from "../session-incarnation.js";
10
12
  import { type IndexSpec } from "./ensure-index.js";
11
13
  /**
@@ -55,8 +57,8 @@ export declare class PgSessionStorage extends BaseSessionStorage {
55
57
  */
56
58
  export declare class PgSessionStore implements SessionStore {
57
59
  private readonly pool;
58
- /** [ref] 车1:托管留存声明(TiDB 孪生同格)。读法见 `retention-store-sql.ts` 的 {@link MANAGED_RETENTION}。 */
59
- readonly retention: import("@sema-agent/core").RetentionDeclaration;
60
+ /** 托管留存声明 = 装配交给它的词(TiDB 孪生同格)。读法见 `retention-store-sql.ts` 的 {@link MANAGED_RETENTION}。 */
61
+ readonly retention: RetentionDeclaration;
60
62
  /** [ref] C18(core 5.48.0 [ref]):placement 声明(TiDB 孪生同格,义务逐条见
61
63
  * `src/plugins/session-placement.ts` 顶注)。 */
62
64
  readonly placements: {
@@ -70,6 +72,7 @@ export declare class PgSessionStore implements SessionStore {
70
72
  private readonly log;
71
73
  constructor(pool: Pool, opts?: {
72
74
  readonly log?: OrphanOwnerLog;
75
+ readonly retention?: RetentionDeclaration;
73
76
  });
74
77
  acquire(sessionId?: string, opts?: PlacementAcquireOpts): Promise<AcquiredSession>;
75
78
  private load;
@@ -135,6 +138,11 @@ export declare class PgSessionStore implements SessionStore {
135
138
  * `query_timeout` rejects AND pg-pool destroys that client, so a lock-stuck baseline write does not keep a pooled
136
139
  * connection). */
137
140
  recordInstructionSource(sessionId: string, expected: InstructionSourceBaseline, source: string | null): Promise<void>;
141
+ /** @see TiDBSessionStore.readSessionWorkspace — the PG twin (same projection-as-text, same cell owner). */
142
+ readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
143
+ /** @see TiDBSessionStore.recordSessionWorkspace — the PG twin (whole-cell overwrite fenced to the occupant; `pgSafeJsonStringify` + `::jsonb`, like every PG JSON write;
144
+ * the general query surface — the pool-wide `DB_QUERY_TIMEOUT_MS` server-side `statement_timeout` bounds it, no per-call client timer). */
145
+ recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
138
146
  /** @see TiDBSessionStore.fork — copy the WHOLE durable history (full session_event log, compaction floor bypassed,
139
147
  * NOT wake's bounded tail) to a NEW uuidv7 session, re-seq dense from 0, ids/parents verbatim, owner=caller. */
140
148
  fork(sourceId: string, owner?: string | null): Promise<string | null>;