@sema-agent/server 7.135.0 → 7.137.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 (342) hide show
  1. package/MIGRATION.md +237 -2
  2. package/USAGE.md +146 -41
  3. package/deploy/sema-up/README.md +15 -1
  4. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  5. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
  6. package/deploy/sema-up/chart/values.yaml +1 -1
  7. package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
  8. package/dist/approval-card.d.ts +60 -4
  9. package/dist/approval-card.js +18 -1
  10. package/dist/approval-content-kind.d.ts +7 -0
  11. package/dist/approval-content-kind.js +8 -1
  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/auth-bridge.d.ts +34 -5
  18. package/dist/auth-bridge.js +22 -5
  19. package/dist/boot/budget-tracing.d.ts +1 -2
  20. package/dist/boot/budget-tracing.js +2 -2
  21. package/dist/boot/execution-env.d.ts +24 -1
  22. package/dist/boot/execution-env.js +69 -6
  23. package/dist/boot/org-memory.js +1 -1
  24. package/dist/boot/reapers.d.ts +3 -0
  25. package/dist/boot/reapers.js +15 -1
  26. package/dist/boot/resolve-spec.d.ts +4 -0
  27. package/dist/boot/resolve-spec.js +24 -22
  28. package/dist/boot/runner-deps.js +4 -2
  29. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  30. package/dist/boot/stage-04-stores.d.ts +2 -2
  31. package/dist/boot/stage-05-execution-env.d.ts +4 -2
  32. package/dist/boot/stage-05-execution-env.js +11 -2
  33. package/dist/boot/stage-06-runners.d.ts +4 -2
  34. package/dist/boot/stage-06-runners.js +5 -1
  35. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  36. package/dist/boot/stage-07-capability-layer.js +1 -1
  37. package/dist/boot/stage-08-reapers.d.ts +5 -2
  38. package/dist/boot/stage-08-reapers.js +2 -1
  39. package/dist/boot/stage-09-leader.d.ts +5 -2
  40. package/dist/boot/stage-10-http-server.d.ts +5 -2
  41. package/dist/boot/stage-10-http-server.js +3 -2
  42. package/dist/boot/stores.d.ts +1 -1
  43. package/dist/boot/stores.js +2 -3
  44. package/dist/boot/web-admin.js +10 -17
  45. package/dist/brain.js +12 -3
  46. package/dist/broker/broker.d.ts +89 -0
  47. package/dist/broker/broker.js +287 -0
  48. package/dist/broker/constants.d.ts +39 -0
  49. package/dist/broker/constants.js +15 -0
  50. package/dist/broker/http1.d.ts +111 -0
  51. package/dist/broker/http1.js +256 -0
  52. package/dist/broker/lease-book.d.ts +87 -0
  53. package/dist/broker/lease-book.js +142 -0
  54. package/dist/broker/lease-file.d.ts +60 -0
  55. package/dist/broker/lease-file.js +127 -0
  56. package/dist/broker/main.d.ts +2 -0
  57. package/dist/broker/main.js +20 -0
  58. package/dist/budget.d.ts +3 -3
  59. package/dist/budget.js +4 -0
  60. package/dist/capabilities/composite-members.d.ts +2 -1
  61. package/dist/capabilities/composite-members.js +2 -1
  62. package/dist/capabilities/engine-capabilities.js +1 -0
  63. package/dist/capabilities/mcp-probe.d.ts +5 -4
  64. package/dist/capabilities/scenarios.d.ts +23 -8
  65. package/dist/capabilities/scenarios.js +13 -8
  66. package/dist/capabilities/skills.d.ts +28 -5
  67. package/dist/capabilities/skills.js +8 -0
  68. package/dist/capability-facts.d.ts +3 -7
  69. package/dist/capability-facts.js +3 -5
  70. package/dist/center-credential-renewer.js +5 -1
  71. package/dist/center-credential.d.ts +18 -4
  72. package/dist/center-credential.js +18 -3
  73. package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
  74. package/dist/checkpoint-ctx-seal-m28.js +1 -1
  75. package/dist/checkpoint-identity.d.ts +3 -3
  76. package/dist/config-catalog.js +21 -16
  77. package/dist/config-d-migration.js +1 -1
  78. package/dist/config-invariants.d.ts +1 -1
  79. package/dist/config-invariants.js +8 -0
  80. package/dist/config-lkg.d.ts +0 -38
  81. package/dist/config-lkg.js +1 -83
  82. package/dist/config-types.d.ts +21 -4
  83. package/dist/config.d.ts +1 -1
  84. package/dist/config.js +60 -16
  85. package/dist/deployment-governance.d.ts +2 -2
  86. package/dist/deployment-governance.js +7 -9
  87. package/dist/deployment-shape.d.ts +1 -1
  88. package/dist/device-ws-protocol.d.ts +1 -0
  89. package/dist/device-ws-protocol.js +2 -1
  90. package/dist/elicitation.d.ts +8 -7
  91. package/dist/elicitation.js +9 -8
  92. package/dist/file-history-disclosure.d.ts +8 -4
  93. package/dist/file-history-disclosure.js +24 -6
  94. package/dist/fleet-client.d.ts +32 -1
  95. package/dist/fleet-client.js +24 -4
  96. package/dist/fleet-lease.d.ts +7 -3
  97. package/dist/fleet-lease.js +31 -23
  98. package/dist/hooks/classic-stations.d.ts +11 -54
  99. package/dist/hooks/hook-llm.js +14 -8
  100. package/dist/hooks/hook-runner.d.ts +4 -4
  101. package/dist/hooks/hook-runner.js +2 -2
  102. package/dist/http/admission.js +10 -7
  103. package/dist/http/approval-sweeps.js +4 -3
  104. package/dist/http/caller-trust.d.ts +2 -1
  105. package/dist/http/caller-trust.js +1 -3
  106. package/dist/http/checkpoint-ctx.d.ts +76 -16
  107. package/dist/http/checkpoint-ctx.js +45 -1
  108. package/dist/http/dispatch.js +24 -19
  109. package/dist/http/durable-run-leg.d.ts +5 -1
  110. package/dist/http/durable-run-leg.js +1 -1
  111. package/dist/http/health-self-report.d.ts +2 -2
  112. package/dist/http/mcp-remote-text.d.ts +5 -0
  113. package/dist/http/mcp-remote-text.js +7 -0
  114. package/dist/http/principal-gate.d.ts +15 -10
  115. package/dist/http/principal-gate.js +48 -9
  116. package/dist/http/resume-ctx-read.d.ts +2 -16
  117. package/dist/http/resume-ctx-read.js +2 -3
  118. package/dist/http/resume-legs.d.ts +2 -20
  119. package/dist/http/resume-legs.js +52 -38
  120. package/dist/http/route-ctx.d.ts +8 -2
  121. package/dist/http/routes/approvals-assistant.d.ts +0 -2
  122. package/dist/http/routes/approvals-assistant.js +65 -16
  123. package/dist/http/routes/capabilities.js +11 -1
  124. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  125. package/dist/http/routes/mcp-reconnect.js +5 -7
  126. package/dist/http/routes/runs.js +4 -3
  127. package/dist/http/routes/session-sync.js +70 -12
  128. package/dist/http/routes/sessions.js +20 -15
  129. package/dist/http/routes/tasks.js +8 -6
  130. package/dist/http/server.d.ts +4 -0
  131. package/dist/http/stored-body-replay.d.ts +3 -3
  132. package/dist/http/stored-body-replay.js +6 -1
  133. package/dist/http/submit-ctx.js +1 -1
  134. package/dist/http/wire-types.d.ts +57 -30
  135. package/dist/identity-source.d.ts +65 -0
  136. package/dist/identity-source.js +45 -0
  137. package/dist/index.d.ts +1 -0
  138. package/dist/index.js +1 -0
  139. package/dist/memory-store-refusal.d.ts +3 -3
  140. package/dist/model-entry-refusal.d.ts +2 -0
  141. package/dist/model-entry-refusal.js +11 -0
  142. package/dist/model-route-gap.d.ts +49 -4
  143. package/dist/model-route-gap.js +34 -1
  144. package/dist/model-select.d.ts +6 -17
  145. package/dist/model-select.js +0 -3
  146. package/dist/namespaced-rule-name.d.ts +1 -1
  147. package/dist/object-store-buckets.d.ts +13 -0
  148. package/dist/object-store-buckets.js +3 -0
  149. package/dist/observability/fail-open.d.ts +19 -7
  150. package/dist/observability/fail-open.js +19 -7
  151. package/dist/observability/metrics.js +2 -2
  152. package/dist/observability/principal-context.d.ts +4 -1
  153. package/dist/observability/principal-context.js +6 -1
  154. package/dist/observability/run-terminal.js +1 -0
  155. package/dist/operator-ask.d.ts +2 -2
  156. package/dist/org-delivery-gate.js +1 -1
  157. package/dist/org-memory-admission.d.ts +2 -2
  158. package/dist/org-memory-admission.js +39 -2
  159. package/dist/parked-decide.d.ts +4 -3
  160. package/dist/parked-decide.js +29 -9
  161. package/dist/peer-directory.d.ts +2 -1
  162. package/dist/peer-directory.js +7 -5
  163. package/dist/permission-rule-list.d.ts +2 -2
  164. package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
  165. package/dist/plugins/approval-ask-store-sql.js +3 -0
  166. package/dist/plugins/blob-backend.js +2 -1
  167. package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
  168. package/dist/plugins/checkpoint-index-columns.js +28 -0
  169. package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
  170. package/dist/plugins/checkpoint-store-sql.js +64 -35
  171. package/dist/plugins/file-history-store-sql.d.ts +6 -0
  172. package/dist/plugins/file-history-store-sql.js +37 -37
  173. package/dist/plugins/file-run-store.js +8 -2
  174. package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
  175. package/dist/plugins/k8s-exec-protocol.js +116 -28
  176. package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
  177. package/dist/plugins/k8s-pod-delete-failure.js +17 -0
  178. package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
  179. package/dist/plugins/k8s-pod-reaper.js +122 -0
  180. package/dist/plugins/local-checkpoint-store.d.ts +3 -6
  181. package/dist/plugins/local-checkpoint-store.js +7 -6
  182. package/dist/plugins/memory-run-store.js +2 -0
  183. package/dist/plugins/one-time-migrations.d.ts +49 -37
  184. package/dist/plugins/one-time-migrations.js +87 -128
  185. package/dist/plugins/permission-rule-store-file.js +2 -2
  186. package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
  187. package/dist/plugins/permission-rule-store-sql.js +16 -17
  188. package/dist/plugins/pg-pool.js +4 -0
  189. package/dist/plugins/pg-session-storage.js +2 -2
  190. package/dist/plugins/remote-env-adb.d.ts +3 -0
  191. package/dist/plugins/remote-env-adb.js +1 -0
  192. package/dist/plugins/remote-env-device.d.ts +3 -0
  193. package/dist/plugins/remote-env-device.js +4 -0
  194. package/dist/plugins/remote-env-e2b.d.ts +3 -0
  195. package/dist/plugins/remote-env-e2b.js +1 -0
  196. package/dist/plugins/remote-env-file-error.d.ts +8 -1
  197. package/dist/plugins/remote-env-file-error.js +7 -1
  198. package/dist/plugins/remote-env-host.d.ts +7 -1
  199. package/dist/plugins/remote-env-host.js +7 -1
  200. package/dist/plugins/remote-env-k8s.d.ts +154 -14
  201. package/dist/plugins/remote-env-k8s.js +300 -21
  202. package/dist/plugins/remote-env-local-docker.d.ts +3 -0
  203. package/dist/plugins/remote-env-local-docker.js +1 -0
  204. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  205. package/dist/plugins/remote-env-ssh.js +1 -0
  206. package/dist/plugins/run-store-sql.js +10 -4
  207. package/dist/plugins/s3-answer.d.ts +15 -0
  208. package/dist/plugins/s3-answer.js +5 -0
  209. package/dist/plugins/session-ownership.d.ts +17 -3
  210. package/dist/plugins/session-ownership.js +19 -5
  211. package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
  212. package/dist/plugins/snapshot-bucket-presence.js +35 -7
  213. package/dist/plugins/sql-errors.d.ts +60 -0
  214. package/dist/plugins/sql-errors.js +64 -1
  215. package/dist/plugins/store-contracts.d.ts +10 -1
  216. package/dist/plugins/store-fault.d.ts +4 -0
  217. package/dist/plugins/store-fault.js +10 -0
  218. package/dist/plugins/tidb-pool.js +8 -1
  219. package/dist/plugins/tidb-session-store.js +2 -2
  220. package/dist/plugins/web-search.d.ts +1 -1
  221. package/dist/plugins/web-search.js +2 -2
  222. package/dist/request-declaration-refusal.d.ts +94 -0
  223. package/dist/request-declaration-refusal.js +78 -0
  224. package/dist/request-image-urls.d.ts +6 -0
  225. package/dist/request-image-urls.js +27 -0
  226. package/dist/request-key-closure.d.ts +5 -0
  227. package/dist/request-key-closure.js +5 -2
  228. package/dist/request-value-closure.d.ts +31 -2
  229. package/dist/request-value-closure.js +7 -1
  230. package/dist/rule-entry-list.d.ts +3 -2
  231. package/dist/rule-entry-list.js +2 -2
  232. package/dist/rules-consent.js +7 -4
  233. package/dist/run-local.d.ts +2 -2
  234. package/dist/runs.js +2 -1
  235. package/dist/runtime-caps-resolver.js +1 -1
  236. package/dist/runtime-governance.d.ts +5 -5
  237. package/dist/sandbox-git-lease.d.ts +49 -0
  238. package/dist/sandbox-git-lease.js +60 -0
  239. package/dist/sandbox-git-plane.d.ts +84 -0
  240. package/dist/sandbox-git-plane.js +105 -0
  241. package/dist/sandbox-pkg-source.d.ts +28 -1
  242. package/dist/sandbox-pkg-source.js +1 -2
  243. package/dist/sealed-key.d.ts +19 -0
  244. package/dist/sealed-key.js +47 -4
  245. package/dist/security.d.ts +16 -4
  246. package/dist/security.js +8 -3
  247. package/dist/server-secret-env.d.ts +6 -0
  248. package/dist/server-secret-env.js +14 -0
  249. package/dist/session-error-wire.d.ts +14 -6
  250. package/dist/session-error-wire.js +2 -1
  251. package/dist/session-incarnation.d.ts +16 -5
  252. package/dist/session-incarnation.js +7 -4
  253. package/dist/session-policy-wire.d.ts +1 -1
  254. package/dist/session-policy-wire.js +2 -2
  255. package/dist/session-row.d.ts +3 -17
  256. package/dist/session-row.js +4 -0
  257. package/dist/session-sync.d.ts +15 -3
  258. package/dist/session-sync.js +5 -2
  259. package/dist/spec-fields.d.ts +43 -13
  260. package/dist/spec-fields.js +33 -12
  261. package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
  262. package/dist/stored-request-prompt-profile-m30.js +25 -0
  263. package/dist/task-a2a.d.ts +52 -81
  264. package/dist/task-a2a.js +219 -89
  265. package/dist/task-mcp.d.ts +7 -15
  266. package/dist/task-mcp.js +16 -51
  267. package/dist/task-settings.d.ts +2 -2
  268. package/dist/tool-approval.d.ts +35 -12
  269. package/dist/tool-approval.js +59 -39
  270. package/dist/tool-rule-names.d.ts +5 -0
  271. package/dist/trace/core-keyset-guard.d.ts +5 -5
  272. package/dist/trace/engine-notice-wire.d.ts +1 -1
  273. package/dist/trace/engine-notice-wire.js +2 -0
  274. package/dist/trace/exit-seat.js +37 -13
  275. package/dist/trace/ledger-sink.d.ts +4 -5
  276. package/dist/trace/ledger-sink.js +3 -7
  277. package/dist/trace/project.d.ts +37 -3
  278. package/dist/trace/project.js +20 -6
  279. package/dist/trace/redact.d.ts +40 -12
  280. package/dist/trace/redact.js +332 -128
  281. package/dist/trace/self-minted-wire.d.ts +641 -0
  282. package/dist/trace/self-minted-wire.js +201 -0
  283. package/dist/trace/wire-projection-faces.d.ts +5 -3
  284. package/dist/trace/wire-projection-faces.js +7 -3
  285. package/dist/url-credential-refusal.d.ts +52 -0
  286. package/dist/url-credential-refusal.js +14 -0
  287. package/dist/url-refusal.d.ts +13 -4
  288. package/dist/url-refusal.js +8 -2
  289. package/dist/web-admin-client/apply-effective.js +0 -1
  290. package/dist/web-admin-client/center-request.d.ts +20 -2
  291. package/dist/web-admin-client/center-request.js +40 -5
  292. package/dist/web-admin-client/http-client.js +6 -6
  293. package/dist/web-admin-client/types.d.ts +5 -5
  294. package/dist/workspace-space.d.ts +27 -0
  295. package/dist/workspace-space.js +26 -0
  296. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
  297. package/package.json +4 -3
  298. package/scripts/drain-check-local.mjs +2 -1
  299. package/scripts/drain-check.mysql.sql +3 -1
  300. package/scripts/drain-check.pg.sql +3 -1
  301. package/skills/schedule.md +1 -1
  302. package/wire-changes/server@7.104.0.json +134 -0
  303. package/wire-changes/server@7.105.0.json +53 -0
  304. package/wire-changes/server@7.106.0.json +475 -0
  305. package/wire-changes/server@7.107.0.json +64 -0
  306. package/wire-changes/server@7.108.0.json +153 -0
  307. package/wire-changes/server@7.109.0.json +46 -0
  308. package/wire-changes/server@7.110.0.json +90 -0
  309. package/wire-changes/server@7.111.0.json +86 -0
  310. package/wire-changes/server@7.112.0.json +60 -0
  311. package/wire-changes/server@7.113.0.json +26 -0
  312. package/wire-changes/server@7.114.0.json +26 -0
  313. package/wire-changes/server@7.115.0.json +97 -0
  314. package/wire-changes/server@7.116.0.json +218 -0
  315. package/wire-changes/server@7.116.1.json +26 -0
  316. package/wire-changes/server@7.117.0.json +65 -0
  317. package/wire-changes/server@7.118.0.json +129 -0
  318. package/wire-changes/server@7.119.0.json +65 -0
  319. package/wire-changes/server@7.120.0.json +89 -0
  320. package/wire-changes/server@7.121.0.json +219 -0
  321. package/wire-changes/server@7.122.0.json +54 -0
  322. package/wire-changes/server@7.123.0.json +26 -0
  323. package/wire-changes/server@7.124.0.json +336 -0
  324. package/wire-changes/server@7.124.1.json +26 -0
  325. package/wire-changes/server@7.125.0.json +111 -0
  326. package/wire-changes/server@7.126.0.json +285 -0
  327. package/wire-changes/server@7.127.0.json +77 -0
  328. package/wire-changes/server@7.127.1.json +26 -0
  329. package/wire-changes/server@7.127.2.json +53 -0
  330. package/wire-changes/server@7.128.0.json +26 -0
  331. package/wire-changes/server@7.129.0.json +106 -0
  332. package/wire-changes/server@7.129.1.json +26 -0
  333. package/wire-changes/server@7.130.0.json +26 -0
  334. package/wire-changes/server@7.131.0.json +118 -0
  335. package/wire-changes/server@7.132.0.json +226 -0
  336. package/wire-changes/server@7.133.0.json +1155 -0
  337. package/wire-changes/server@7.134.0.json +107 -0
  338. package/wire-changes/server@7.135.0.json +174 -0
  339. package/wire-changes/server@7.136.0.json +448 -0
  340. package/wire-changes/server@7.137.0.json +825 -0
  341. package/dist/url-credential.d.ts +0 -52
  342. package/dist/url-credential.js +0 -49
@@ -1,13 +1,13 @@
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
- import { APPROVAL_CARD_COLUMN_MAX_BYTES, buildCheckpointIndexCells } from "./checkpoint-index-columns.js";
3
+ import { judgeCheckpointIndexCells } from "./checkpoint-index-columns.js";
4
4
  import { dialectProtocolJsonEncoder } from "./sql-driver.js";
5
5
  import { SCHEMA_STATEMENTS, acquireEnsureSchemaLock, releaseEnsureSchemaLock } from "./tidb-pool.js";
6
6
  import { PG_SCHEMA_STATEMENTS, acquirePgEnsureSchemaLock, releasePgEnsureSchemaLock } from "./pg-pool.js";
7
7
  import { CENTRAL_SCHEMA_INDEXES } from "./schema-indexes.js";
8
8
  import { ensureIndexes } from "./ensure-index.js";
9
9
  import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextExpr, readNonNullJsonValueColumn, readOpaqueJsonValueColumn, } from "./sql-json-column.js";
10
- import { SESSION_META_INCARNATION_COLUMN, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
10
+ import { incarnationFromCell, sessionFaceFromRow } from "../session-incarnation.js";
11
11
  import { RUN_HOLDER_DEATH_CODES, RUN_ORPHANED_ERROR, RUN_STALLED_ERROR } from "./store-contracts.js";
12
12
  import { PERMISSION_RULE_TABLE, purgeUndurableScopeRows } from "./permission-rule-store-sql.js";
13
13
  import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
@@ -19,7 +19,6 @@ import { MAILBOX_KEYED_TABLES, mailboxKeyDeleteSql, storedMailboxKeyless } from
19
19
  const RETIRED_SESSION_RULE_BUCKETS = ["commandAllow", "commandDeny"];
20
20
  export function buildMigrationReceipts() {
21
21
  return {
22
- sessionIncarnationsBackfilled: 0,
23
22
  holderDeathCodeRowsBackfilled: 0,
24
23
  allowRuleShadowRowsMigrated: 0,
25
24
  allowRuleShadowRowsUnreadable: 0,
@@ -49,16 +48,14 @@ export function buildMigrationReceipts() {
49
48
  };
50
49
  }
51
50
  function completedReport(r) {
52
- const { sessionIncarnationColumn, workflowParkIndexKey, sessionRuleColumn, workflowJournalScopeColumn } = r;
53
- if (sessionIncarnationColumn === undefined || workflowParkIndexKey === undefined || sessionRuleColumn === undefined || workflowJournalScopeColumn === undefined) {
54
- throw new Error(`one-time migrations finished without settling a shape reading (M5 ${String(sessionIncarnationColumn)} / M9 ${String(workflowParkIndexKey)} / M17 ${String(sessionRuleColumn)} / M24 ${String(workflowJournalScopeColumn)}) — a step did not record it`);
51
+ const { workflowParkIndexKey, sessionRuleColumn, workflowJournalScopeColumn } = r;
52
+ if (workflowParkIndexKey === undefined || sessionRuleColumn === undefined || workflowJournalScopeColumn === undefined) {
53
+ throw new Error(`one-time migrations finished without settling a shape reading (M9 ${String(workflowParkIndexKey)} / M17 ${String(sessionRuleColumn)} / M24 ${String(workflowJournalScopeColumn)}) — a step did not record it`);
55
54
  }
56
- return { ...r, sessionIncarnationColumn, workflowParkIndexKey, sessionRuleColumn, workflowJournalScopeColumn };
55
+ return { ...r, workflowParkIndexKey, sessionRuleColumn, workflowJournalScopeColumn };
57
56
  }
58
57
  function changedAnything(r) {
59
- return (r.sessionIncarnationColumn === "added" ||
60
- r.sessionIncarnationsBackfilled > 0 ||
61
- r.holderDeathCodeRowsBackfilled > 0 ||
58
+ return (r.holderDeathCodeRowsBackfilled > 0 ||
62
59
  r.allowRuleShadowRowsMigrated > 0 ||
63
60
  r.allowRuleShadowRowsUnreadable > 0 ||
64
61
  r.workflowParkIndexKey === "reshaped" ||
@@ -122,18 +119,6 @@ export class OneTimeMigrationInterruptedError extends Error {
122
119
  }
123
120
  }
124
121
  export const ONE_TIME_MIGRATIONS = [
125
- {
126
- id: "M5",
127
- introducedIn: "7.104.0",
128
- releasedOn: "2026-09-28",
129
- removableAfter: "2026-10-28",
130
- removal: "删 migrateOn 的 M5 步(`session_meta` 在场探针、`incarnation` 列在场探针 + `ADD COLUMN`、空化身回填循环)与 `backfillSessionIncarnations`;" +
131
- "报告、日志行与 `NO_SQL_MIGRATIONS` 的 `sessionIncarnationColumn` / `sessionIncarnationsBackfilled`;**列本身留在中央 DDL 里**(它不是迁移的一部分)",
132
- afterRemoval: "① 列不在的存量库:起服列册探针 `schema-consistency.ts`(S-547)的缺列臂拒启 `config.schema_mismatch`,拒句里的 `ALTER TABLE … ADD COLUMN` " +
133
- "由同一份 DDL 生成(响亮);② 列在但行上仍是空化身(跳过了带 M5 的版本直接升):会话店头读投成活住客(`sessionFaceOfHead`)时对那一行响亮拒 " +
134
- "`session.incarnation_missing`(parked 决定腿因此拒、记忆座退回按 id 绑定并 warn)—— 运维手工给那些行填一只随机令牌即可",
135
- reportKeys: ["sessionIncarnationColumn", "sessionIncarnationsBackfilled"],
136
- },
137
122
  {
138
123
  id: "M6",
139
124
  introducedIn: "7.116.0",
@@ -225,7 +210,8 @@ export const ONE_TIME_MIGRATIONS = [
225
210
  removal: "删模式词读法记号的判据与拒臂:`http/stored-body-replay.ts` 的 `permissionModeReadingUnstated`;`http/checkpoint-ctx.ts` 的 `permissionModeReadingStated` / `permissionModeReadingRefusal` / `PERMISSION_MODE_READINGS`、" +
226
211
  "`StoredBodyReplayRefusal` 的 `permission_mode_reading` 臂及其日志字段与答复臂、`buildCheckpointCtx` 与 `withPermissionModeRewritten` 两处记号写点" +
227
212
  "(后者删后 `resume-legs.ts` `drivePlanReview` 里对它的那一处调用回到只换体的 `{ ...ctx, body }`)、`CheckpointCtx.permissionModeReading`、" +
228
- "`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 数组)",
229
215
  afterRemoval: "没有响亮臂:删码之后仍停驻、在 7.121.x 及以前写下的行(把 `APPROVAL_TERMINAL_BACKSTOP_MS` 调长的部署,或跳过了带本条的版本直接升的库)在设了 " +
230
216
  "`AUTONOMY=plan` 的部署上按体里的原词重放(运维的 plan 不再被按段重钳)—— 升级前把这些停驻的 run 决掉 / 取消,或升级窗内设 `AUTONOMY=read-only`",
231
217
  reportKeys: [],
@@ -237,26 +223,13 @@ export const ONE_TIME_MIGRATIONS = [
237
223
  removableAfter: "2026-11-09",
238
224
  removal: "删 `config-lkg.ts` 的 formatVersion 2 读回迁移:`migrateV2Lkg` / `migrateDroppedIdentityRow` / `RETIRED_CATALOG_COORDINATE` 与其上的窗注;`loadLkg` 的 2 臂" +
239
225
  "(版本判据里的 `2` 与 `formatVersion === 2 ? migrateV2Lkg(…)` 那一支 —— 2 回到「unknown formatVersion」拒)、返回型的 `migrated` 位与 JSDoc 里说它的那一行、" +
240
- "文件头注「2 读回时一次性迁移」那一句;`boot/web-admin.ts` 读 `read.migrated` 打 `config_lkg_migrated` 的那一行(formatVersion 1 迁移的同名事件那一行留);" +
241
- "`migrateV1Lkg` 里不带 v1 的 etag 那一句旁注对 `migrateV2Lkg` 的引用(不带 etag 的规则本身留 —— v1 载荷同样没有完整的被丢条目记录);契约 §A.1d " +
226
+ "文件头注「2 读回时一次性迁移」那一句;`boot/web-admin.ts` 读 `read.migrated` 打 `config_lkg_migrated` 的那一行(这一行与 M15 / M22 的读回迁移共用 —— 三条都删完才整段删,先删 M13 时只去掉信封 2 → 3 那一路的 `migrated` 来源);契约 §A.1d " +
242
227
  "`droppedEntries` 一节「升级窗(如实)」那一条与 §A.1d `[config.model_refused_name_minted]` 那一条里「LKG 载体 `droppedIdentities` 读回时一次性迁移」那半句;手写 formatVersion 2 信封的测试夹具" +
243
228
  "(`test/config-lkg.test.ts`、`test/config-hot/batch1-version-ordinal.test.ts` 改写成 3;`test/models-dropped-entries-s1295.test.ts` 的旧 LKG 迁移格改钉 2 读回不可用)",
244
229
  afterRemoval: "既有响亮臂 `config_lkg_unusable`(`loadLkg` 对 2 答「unknown formatVersion 2」):2 读回 ⇒ 不可用,中心可达照拉(整拉,读腿铸出完整载体,新 LKG 按 3 覆盖)、" +
245
230
  "断着则 env 兜底,下一份干净候选重写 —— 与回滚到 7.122.0 读 3 同一种处置",
246
231
  reportKeys: [],
247
232
  },
248
- {
249
- id: "M14",
250
- introducedIn: "7.99.0",
251
- releasedOn: "2026-09-24",
252
- removableAfter: "2026-10-24",
253
- removal: "删 `config-lkg.ts` 的 formatVersion 1 迁移:`legacyV1LkgPath` / `migrateV1Lkg` 与其上的窗注(`loadLkg` 的 1 臂今天已是拒句,不动);" +
254
- "`boot/web-admin.ts` `bootFromLkg` 里调用 `migrateV1Lkg` 的那一段(含它打 `config_lkg_migrated` / 推不出身份时响亮拒的那两行);" +
255
- "`migrateV1Lkg` 的测试格(`test/config-lkg.test.ts` v1 迁移组)",
256
- afterRemoval: "既有响亮臂 `config_lkg_unusable`(`loadLkg` 对 1 答「pre-identity LKG (formatVersion 1) — not read back …」):旧默认路径上的 v1 文件不再被迁进箱、" +
257
- "原样留在盘上;中心可达照拉(整拉,新 LKG 按现行箱名落盘)、断着则 env 兜底",
258
- reportKeys: [],
259
- },
260
233
  {
261
234
  id: "M15",
262
235
  introducedIn: "7.124.0",
@@ -322,10 +295,15 @@ export const ONE_TIME_MIGRATIONS = [
322
295
  releasedOn: "2026-10-16",
323
296
  removableAfter: "2026-11-15",
324
297
  removal: "删 `src/stored-request-tool-axes-m19.ts` 整只文件(`storedToolAxesRereadM19` / `readToolAxesAsServer7129M19` / `readA2aToolAxesAsServer7131M19` / `StoredToolAxesRereadM19`);" +
325
- "`boot/resolve-spec.ts` 里两条请求腿共用的那一行(`const storedToolAxes = opts?.leg === \"fresh\" ? undefined : storedToolAxesRereadM19(…)`)、递给 `resolveRequestMcp` / `resolveRequestA2a` 的两处实参与它的 import;" +
298
+ "`boot/resolve-spec.ts` 里两条请求腿共用的那一行(`const storedToolAxes = opts?.leg === \"fresh\" ? undefined : storedToolAxesRereadM19(…)`,在腿首、A2A 快照之前求值)、" +
299
+ "`snapshotRequestA2a(…, { reread: storedToolAxes !== undefined })` 的第二实参、`parts` 里的 `storedToolAxes`(类型与解构)、递给 `resolveRequestMcp` / `resolveRequestA2a` 的两处实参与它的 import;" +
326
300
  "`task-mcp.ts` 里 `resolveRequestMcp` / `classifyRequestMcp` / `classifyHonored` / `validateRequestMcpEntries` / `normalizeServer` 的 `storedToolAxes` 形参、" +
327
301
  "`toolAxesReread` 事实与运维行 `mcp_tool_axes_read_as_before_upgrade`,以及对本模块的型 import(升级前读法的词表与两只谓词住在本模块里,随它一起删);" +
328
- "`task-a2a.ts` 里 `resolveRequestA2a` / `validateRequestA2aEntries` / `normalizeA2aPeer` 的 `storedToolAxes` 形参、`toolAxesReread` 事实与运维行 `a2a_tool_axes_read_as_before_upgrade`、对本模块的型 import;" +
302
+ "`task-a2a.ts` 里 `resolveRequestA2a` / `validateRequestA2aEntries` / `normalizeA2aPeer` 的 `storedToolAxes` 形参、`toolAxesReread` 事实与运维行 `a2a_tool_axes_read_as_before_upgrade`、对本模块的型 import," +
303
+ "以及只为重读座存在的克隆机器的全部名字:`cloneDeclaration` / `DeclarationClone` / `CloneFrame` / `openCloneNode` / `putCloneMember`、快照类型 `PeerSnapshot` 的字段 `toolAxesDeclared` 与 `mintPeerSnapshot` / `snapshotPeer` / `snapshotRequestA2a` 的 `reread` 形参、`toolAxesNotCloneableIssue`、读器里 `declared` 那一臂(`node:util` 的 `types` 与 `errorKindOf` 的 import 若再无别的读者一并删);" +
304
+ "随克隆一起退役的两句例外文字:`USAGE.md` 里 `a2a_injection_dropped` 条目 `reason` 那句「只有一臂例外 … `declaration not cloneable (<because>) — …`」与 `task-a2a.ts` 里 `A2aInjectionDrop.reason` 注的同一句例外(删后 `reason` 恒与 400 拒句同一句,回到「逐字同一句」不带例外);" +
305
+ "`test/task-a2a.test.ts` 里的克隆钉(标题去掉开头的 `🔴 ` 后以 `克隆钉:` 开头的各格,整格删;按这个精确前缀删,不按标题里的「M19 重读」之类泛词删 —— 那会误删同文件里的通用不变式钉),以及三格不带前缀、删码后要改写的钉:`需要「读」的两个键…` 那格的 `readRequestToolAxes` 调用方名单(`normalizeA2aPeer` 只在 M19 重读臂里调它,删后名单只剩 `mintPeerSnapshot`,标题里「与读器里 M19 重读产物的那一次」半句同删)、`读器与门的差别只有三格…` 改成两格(删 ③ 的注释行与标题里的 ③)、`请求入口快照的形…` 里 `snapshotRequestA2a(…, { reread: false })` 的第二实参(删 `reread` 形参后改成单参);" +
306
+ "`test/one-time-migration-window-gate.test.ts` 里 M19 一节中与克隆有关的三格(声明抽取器正负控 · 删法条与克隆名字两向对账 · 克隆钉标题前缀对账)与它们共用的帮手 `declaredNames`;" +
329
307
  "`test/mcp-tool-axes-m19.test.ts`(窗关那一格 —— 存量坏形整条不进、warn 与通告点名 `field: toolAxes` —— 改钉到 `test/task-mcp.test.ts`);" +
330
308
  "本文件的 `migrationWindowOpen`(若再无别的读者)",
331
309
  afterRemoval: "没有新的臂,接住它的是今天的读法(删码零行为变化:窗关那一刻起重读臂已经不生效):升级前存下的请求体里引擎判官判不过的 `toolAxes`,续跑重放时这一条 server 整条不进," +
@@ -451,8 +429,13 @@ export const ONE_TIME_MIGRATIONS = [
451
429
  introducedIn: "7.134.0",
452
430
  releasedOn: "2026-10-21",
453
431
  removableAfter: "2026-11-20",
454
- removal: "删 `src/checkpoint-ctx-seal-m28.ts` 整个文件与起服第 6 段对它的那一处调用;两孪生的店腿 `rewriteCtxRowsM28` 与读数型 `CtxRowsRewriteM28`;" +
455
- "`http/checkpoint-ctx.ts` 的 `sealStoredCheckpointCtxM28`;`test/checkpoint-ctx-seal-m28.test.ts` 与 `test/checkpoint-ctx-credential-seal-db-integration.test.ts` 的 M28 格。" +
432
+ 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`:「另有各随一条一次性迁移的格」起到「别的文件的行一个都不碰);」止整段删;" +
433
+ "`http/checkpoint-ctx.ts` 的 `sealStoredCheckpointCtxM28`;`test/checkpoint-ctx-seal-m28.test.ts` 整个文件与 `test/checkpoint-ctx-credential-seal-db-integration.test.ts` 里的「[M28] 明文旧行」那一格(它调 `sealStoredCheckpointCtxM28`);" +
434
+ "`test/doc-claims-upgrade-notes.test.ts` ㊵ 组里读这处调用与日志行的三行断言随删(两格本身与其余断言留着;不删就是删码后这两格红):「锚(嵌入方段的每件事实读实现)」格的 `callSites(\"sealCheckpointCtxCredentialsM28\")` 一行;" +
435
+ "「锚(恢复句的每件事实读实现)」格里 guarded 上对 `sealCheckpointCtxCredentialsM28` 调用的断言与对 `logger.info(\"checkpoint_ctx_credentials_sealed\"` 日志行的断言两行;" +
436
+ "`test/stored-request-prompt-profile-m30.test.ts` 里钉起服第 6 段调用次序的那一格(`describe` 名含「起服接线」;M30 删码时才删它,M28 的窗先关,先删 M28 时它要活着)里读 M28 调用位置的 `const m28` 那一行(它用 indexOf 找 `sealCheckpointCtxCredentialsM28` 的调用)与「M28 在那一臂里」「M30 在那一臂里、M28 之后」两条断言,改成只相对 `guard` 比较 M30 的位置;" +
437
+ "依赖本行的格(行一删就红):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 的登记行组已先删时该表已不在,这一句无事可做);" +
438
+ "同族注释(每个半句只归本条或 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`)」删;" +
456
439
  "**不随本条删**:凭据位密封的常设规则(封口 / 开口 / 闭集 / 读口第四态 / 409 码 / 装配期拒启)",
457
440
  afterRemoval: "读口对凭据位上明文的常设拒臂接住:跳过了带本条的版本直接升的库,带明文凭据的停驻行续跑答 409 `conflict.checkpoint_ctx_unsealable`(`reason:\"plaintext\"`)," +
458
441
  "行字节不动 —— 出路 = 作为新任务重提(仍停驻的先取消),或升级时先过一次带本条的版本",
@@ -463,20 +446,54 @@ export const ONE_TIME_MIGRATIONS = [
463
446
  introducedIn: "7.135.0",
464
447
  releasedOn: "2026-10-22",
465
448
  removableAfter: "2026-11-21",
466
- removal: "删 migrateOn 的 M29 步(`backfillParkedApprovalCards`:列在场探针、pending 候选分页扫描、整只文档读 + 同一只派生 + CAS 写列)与 `PARKED_CARD_BACKFILL_PAGE` / `readM29Document` / `m29CardOf`;" +
449
+ removal: "删 migrateOn 的 M29 步(`backfillParkedApprovalCards`:列在场探针、pending 候选分页扫描、整只文档读 + 写口那一只判官 + CAS 写列)与 `PARKED_CARD_BACKFILL_PAGE` / `readM29Document` / `m29Judge`;" +
467
450
  "报告、日志行(含逐行的 `parked_approval_card_row_unreadable` / `parked_approval_card_row_too_long`)与 `NO_SQL_MIGRATIONS` 的 `parkedApprovalCardsBackfilled` / `parkedApprovalCardRowsUnreadable` / `parkedApprovalCardRowsTooLong`;`test/one-time-migrations.test.ts` 的 M29 组与 " +
468
451
  "`test/checkpoint-index-columns-db-integration.test.ts` 的 M29 格。**不随本条删**:`approval_card` 列、写口的派生、到期 facet 的生成谓词(常设)",
469
452
  afterRemoval: "没有新的臂(如实):跳过了带本条的版本直接升的库,升级前停驻、升级后仍挂着的行卡列是 NULL —— 生成谓词读门型的 facet(审批门 ⇒ SLA 拒扫腿),问答行到期被拒扫" +
470
453
  "(引擎对问答拒收 `sla_timeout`,拒扫腿每拍一条失败,由 `terminal_at_ms` 兜底收割);运维队列行不带卡词。出路 = 升级前答完 / 拒掉停驻的问答,或升级时先过一次带本条的版本",
471
454
  reportKeys: ["parkedApprovalCardsBackfilled", "parkedApprovalCardRowsUnreadable", "parkedApprovalCardRowsTooLong"],
472
455
  },
456
+ {
457
+ id: "M30",
458
+ introducedIn: "7.137.0",
459
+ releasedOn: "2026-10-25",
460
+ removableAfter: "2026-11-24",
461
+ 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`);" +
462
+ "`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`:「另有各随一条一次性迁移的格」起到「别的文件的行一个都不碰);」止整段删;" +
463
+ "`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).」," +
464
+ "它的第二参数 `rewriteWindowEnd` 同删(定义处与私有的 `storedBodyReplayArmAnswer` 里 `retired_request_key` 臂上的那一处调用),`StoredBodyReplayRefusal` 的 `retired_request_key` 臂上的必填字段 `rewriteWindowEnd` 同删;" +
465
+ "`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);" +
466
+ "`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 的「出路 = 重提」矛盾;" +
467
+ "改后句:「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)」);" +
468
+ "同族注释(每个半句只归本条或 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 没改写到的残留」改成「没有迁移改写的残留」、「洞 = 窗的最后一天(绝对日期)」起到「不手写。」止整段删;" +
469
+ "`http/stored-body-replay.ts` 里 `RETIRED_KEY_REWRITE_MIGRATION` 的 JSDoc 随表删;" +
470
+ "`http/stored-body-replay.ts` 的 `RETIRED_KEY_REWRITE_MIGRATION`(`satisfies Record<RetiredRequestKey, string>`:M30 删后 `promptProfile` 仍是退役键,这张表只能整张删)、" +
471
+ "判据里 `rewriteWindowEnd: migrationWindowEnd(…)` 那一项与 `migrationWindowEnd` 的引入行;`plugins/one-time-migrations.ts` 导出的 `migrationWindowEnd` 在无别的读者时一起删;" +
472
+ "冻结表 `test/api-error-text-freeze.test.ts` 的 `FROZEN_RESULT_BODY_TEMPLATE` 里那一行搬回字面表(删完半句后拒句是整句字面);" +
473
+ "`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` 助手随之删;" +
474
+ "`test/checkpoint-ctx-credential-seal-db-integration.test.ts` 的 M30 格与文件头对 `retirePromptProfileRowM30` 的引入行(模块删后这一行不删就是编译红);" +
475
+ "`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)随登记行组删;" +
476
+ "「窗关后的残留」组留下,留下的引入:storedBodyReplayRefusal、storedBodyReplayAnswer、makeConfig、CheckpointCtx 与 vitest 的 describe / expect / it;legacyBody 助手与它用的 TaskRequestBody 也留着;" +
477
+ "`test/doc-claims-upgrade-notes.test.ts` ㊵ 组里读这处调用与日志行的三行断言随删(两格本身与其余断言留着;不删就是删码后这两格红):「锚(嵌入方段的每件事实读实现)」格的 `callSites(\"retirePromptProfileM30\")` 一行;" +
478
+ "「锚(恢复句的每件事实读实现)」格里 guarded 上对 `retirePromptProfileM30` 调用的断言与对 `logger.info(\"checkpoint_ctx_prompt_profile_rewritten\"` 日志行的断言两行。" +
479
+ "**不随本条删**:续跑判据的第四问(退役请求键闭集 `RETIRED_REQUEST_KEYS` / `retiredRequestKeyOf` / `retiredRequestKeyRefusal`,常设)",
480
+ afterRemoval: "续跑判据第四问的常设拒臂接住:跳过了带本条的版本直接升的库(或窗内从没起过本版的部署),体里仍带 `promptProfile` 的停驻行续跑在五个问点上答 409 `checkpoint.unsupported_version`" +
481
+ "(判据 `retired_request_key`,`http/stored-body-replay.ts`)、行字节不动 —— 出路 = 作为新任务重提(`promptLean` 代替 `promptProfile`;仍停驻的先取消),或升级时先过一次带本条的版本并在窗内起服",
482
+ reportKeys: [],
483
+ },
473
484
  ];
474
485
  export function migrationWindowOpen(window, today) {
475
486
  return window.removableAfter === null || today <= window.removableAfter;
476
487
  }
488
+ export function migrationWindowEnd(id) {
489
+ const row = ONE_TIME_MIGRATIONS.find((w) => w.id === id);
490
+ if (row === undefined)
491
+ 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`);
492
+ if (row.removableAfter === null)
493
+ throw new Error(`one-time-migrations: ${id} is in the unreleased form (removableAfter: null) — there is no window end to quote yet`);
494
+ return row.removableAfter;
495
+ }
477
496
  export const NO_SQL_MIGRATIONS = {
478
- sessionIncarnationColumn: "lazy-on-read",
479
- sessionIncarnationsBackfilled: 0,
480
497
  allowRuleShadowRowsMigrated: 0,
481
498
  allowRuleShadowRowsUnreadable: 0,
482
499
  workflowParkIndexKey: "file-ledger",
@@ -592,7 +609,6 @@ async function migrateOn(db, dialect) {
592
609
  return completedReport(receipts);
593
610
  }
594
611
  async function migrateStepsOn(db, dialect, receipts) {
595
- await migrateSessionIncarnations(db, dialect, receipts);
596
612
  receipts.holderDeathCodeRowsBackfilled += await db.exec(dialect === "tidb"
597
613
  ? "UPDATE task_run SET error_code = ? WHERE status = 'failed' AND error_code IS NULL AND error = ?"
598
614
  : "UPDATE task_run SET error_code = $1 WHERE status = 'failed' AND error_code IS NULL AND error = $2", [RUN_HOLDER_DEATH_CODES.stalled, RUN_STALLED_ERROR]);
@@ -751,8 +767,9 @@ export function oldGrammarEntriesOf(body, rev) {
751
767
  const toolAllowDropped = (shaped.rules.toolAllow ?? []).filter((e) => earlier.has(e));
752
768
  if (toolAllowDropped.length === 0 && body.grammar !== undefined)
753
769
  return { toolAllowDropped };
754
- return { toolAllowDropped, rewritten: { ...body, ...(toolAllow !== undefined ? { toolAllow } : {}), grammar: SESSION_RULES_GRAMMAR } };
770
+ return { toolAllowDropped, rewritten: { ...body, ...(toolAllow !== undefined ? { toolAllow } : {}), grammar: M17_PROVEN_GRAMMAR } };
755
771
  }
772
+ const M17_PROVEN_GRAMMAR = 1;
756
773
  const RETIRED_BUCKET_PATTERNS = ['%"commandDeny"%', '%"commandAllow"%'];
757
774
  async function scanSessionRuleRows(db, dialect, column, patterns, visit) {
758
775
  const text = jsonColumnTextExpr(dialect, column);
@@ -1113,25 +1130,32 @@ export async function backfillParkedApprovalCards(db, dialect, receipts) {
1113
1130
  const [full] = await db.rows(`SELECT session_id, ${jsonColumnText(dialect, "checkpoint")} FROM checkpoint WHERE token = ${ph(1)} AND status = 'pending'`, [token]);
1114
1131
  if (full === undefined)
1115
1132
  continue;
1116
- const reading = m29CardOf(full.checkpoint);
1117
- if (reading.kind === "unreadable") {
1118
- receipts.parkedApprovalCardRowsUnreadable.push({ sessionId: String(full.session_id) });
1119
- continue;
1120
- }
1121
- if (reading.card === null)
1122
- continue;
1123
- const bytes = Buffer.byteLength(reading.card, "utf8");
1124
- if (bytes > APPROVAL_CARD_COLUMN_MAX_BYTES[dialect]) {
1125
- receipts.parkedApprovalCardRowsTooLong.push({ sessionId: String(full.session_id), bytes });
1126
- continue;
1133
+ const reading = m29Judge(full.checkpoint, dialect);
1134
+ switch (reading.kind) {
1135
+ case "unreadable":
1136
+ receipts.parkedApprovalCardRowsUnreadable.push({ sessionId: String(full.session_id) });
1137
+ continue;
1138
+ case "card_too_long":
1139
+ receipts.parkedApprovalCardRowsTooLong.push({ sessionId: String(full.session_id), bytes: reading.bytes });
1140
+ continue;
1141
+ case "cells": {
1142
+ const card = reading.cells.approval_card;
1143
+ if (card === null)
1144
+ continue;
1145
+ receipts.parkedApprovalCardsBackfilled += await db.exec(`UPDATE checkpoint SET approval_card = ${ph(1)} WHERE token = ${ph(2)} AND status = 'pending' AND approval_card IS NULL`, [card, token]);
1146
+ continue;
1147
+ }
1148
+ default: {
1149
+ const unhandled = reading;
1150
+ throw new Error(`M29: unhandled judgement ${JSON.stringify(unhandled)}`);
1151
+ }
1127
1152
  }
1128
- receipts.parkedApprovalCardsBackfilled += await db.exec(`UPDATE checkpoint SET approval_card = ${ph(1)} WHERE token = ${ph(2)} AND status = 'pending' AND approval_card IS NULL`, [reading.card, token]);
1129
1153
  }
1130
1154
  if (page.length < PARKED_CARD_BACKFILL_PAGE)
1131
1155
  return;
1132
1156
  }
1133
1157
  }
1134
- function m29CardOf(cell) {
1158
+ function m29Judge(cell, dialect) {
1135
1159
  let doc;
1136
1160
  try {
1137
1161
  doc = decodeJsonColumn(cell, readM29Document, "checkpoint.checkpoint (M29 approval-card backfill)");
@@ -1141,11 +1165,7 @@ function m29CardOf(cell) {
1141
1165
  throw e;
1142
1166
  return { kind: "unreadable" };
1143
1167
  }
1144
- const gate = doc.gate;
1145
- const pendingAction = doc.pendingAction;
1146
- if (typeof gate !== "object" || gate === null || typeof pendingAction !== "object" || pendingAction === null)
1147
- return { kind: "unreadable" };
1148
- return { kind: "card", card: buildCheckpointIndexCells(doc).approval_card };
1168
+ return judgeCheckpointIndexCells(doc, dialect);
1149
1169
  }
1150
1170
  const PLACED_OWNER_SCAN_PAGE = 500;
1151
1171
  function placedOwnerScanSql(dialect) {
@@ -1207,68 +1227,8 @@ export async function inheritPlacedSessionOwners(db, dialect, receipts) {
1207
1227
  return;
1208
1228
  }
1209
1229
  }
1210
- const INCARNATION_BACKFILL_BATCH = 500;
1211
- async function migrateSessionIncarnations(db, dialect, receipts) {
1212
- if (!(await tableExists(db, dialect, "session_meta"))) {
1213
- receipts.sessionIncarnationColumn = "no-table";
1214
- return;
1215
- }
1216
- const col = SESSION_META_INCARNATION_COLUMN;
1217
- let column = "present";
1218
- if (!(await columnExists(db, dialect, "session_meta", col.name))) {
1219
- try {
1220
- await db.exec(`ALTER TABLE session_meta ADD COLUMN ${dialect === "tidb" ? col.tidb : col.pg}`);
1221
- column = "added";
1222
- }
1223
- catch (e) {
1224
- if (!(await columnExists(db, dialect, "session_meta", col.name)))
1225
- throw e;
1226
- }
1227
- }
1228
- receipts.sessionIncarnationColumn = column;
1229
- await backfillSessionIncarnations(db, dialect, receipts);
1230
- }
1231
- async function backfillSessionIncarnations(db, dialect, receipts) {
1232
- const remaining = async () => Number((await db.rows("SELECT COUNT(*) AS n FROM session_meta WHERE incarnation = ''"))[0]?.n ?? 0);
1233
- const initial = await remaining();
1234
- if (initial === 0)
1235
- return;
1236
- const ceiling = Math.ceil(initial / INCARNATION_BACKFILL_BATCH) * 2 + 2;
1237
- let written = 0;
1238
- let after;
1239
- for (let round = 0; round < ceiling; round++) {
1240
- const ids = (after === undefined
1241
- ? await db.rows(`SELECT session_id FROM session_meta WHERE incarnation = '' ORDER BY session_id LIMIT ${INCARNATION_BACKFILL_BATCH}`)
1242
- : await db.rows(`SELECT session_id FROM session_meta WHERE incarnation = '' AND session_id > ${dialect === "pg" ? "$1" : "?"} ORDER BY session_id LIMIT ${INCARNATION_BACKFILL_BATCH}`, [after])).map((r) => String(r.session_id));
1243
- if (ids.length === 0) {
1244
- if (after === undefined)
1245
- return;
1246
- after = undefined;
1247
- continue;
1248
- }
1249
- const params = [];
1250
- const bind = (v) => {
1251
- params.push(v);
1252
- return dialect === "pg" ? `$${params.length}` : "?";
1253
- };
1254
- const cases = ids.map((id) => `WHEN ${bind(id)} THEN ${bind(mintSessionIncarnation())}`).join(" ");
1255
- const inList = ids.map((id) => bind(id)).join(", ");
1256
- const landed = await db.exec(`UPDATE session_meta SET incarnation = CASE session_id ${cases} END WHERE incarnation = '' AND session_id IN (${inList})`, params);
1257
- written += landed;
1258
- receipts.sessionIncarnationsBackfilled += landed;
1259
- after = ids.length < INCARNATION_BACKFILL_BATCH ? undefined : ids[ids.length - 1];
1260
- }
1261
- const left = await remaining();
1262
- if (left > 0) {
1263
- throw new Error(`one-time migration M5: ${left} session_meta row(s) still carry an empty incarnation after ${ceiling} backfill round(s) ` +
1264
- `(${written} written) — refusing to start rather than serve sessions whose occupant cannot be told apart. Retry the boot; ` +
1265
- `if it repeats, something keeps writing rows without an incarnation (an older replica still inserting?).`);
1266
- }
1267
- }
1268
1230
  function readingFields(r) {
1269
1231
  return {
1270
- ...(r.sessionIncarnationColumn !== undefined ? { sessionIncarnationColumn: r.sessionIncarnationColumn } : {}),
1271
- sessionIncarnationsBackfilled: r.sessionIncarnationsBackfilled,
1272
1232
  holderDeathCodeRowsBackfilled: r.holderDeathCodeRowsBackfilled,
1273
1233
  allowRuleShadowRowsMigrated: r.allowRuleShadowRowsMigrated,
1274
1234
  allowRuleShadowRowsUnreadable: r.allowRuleShadowRowsUnreadable,
@@ -1305,8 +1265,7 @@ export function logOneTimeMigrations(log, backend, report) {
1305
1265
  backend,
1306
1266
  ...readingFields(report),
1307
1267
  removableAfter: Object.fromEntries(ONE_TIME_MIGRATIONS.map((w) => [w.id, w.removableAfter])),
1308
- note: "one-time persisted-data migrations (M5: the " +
1309
- "session incarnation column added and every session row without one minted its own token; M6: run rows failed by a " +
1268
+ note: "one-time persisted-data migrations (M6: run rows failed by a " +
1310
1269
  "holder-death verdict written by an earlier version with only the sentence got the machine-readable code run.stalled / " +
1311
1270
  "run.orphaned; M7 writes nothing — it is the window of the refusal that keeps a suspended task whose stored allow list was written " +
1312
1271
  "under an earlier reading from resuming under the current one; M8: parked rows' allow-layer disclosure written by an earlier engine " +
@@ -1463,7 +1422,7 @@ function logOneTimeMigrationsInterrupted(log, backend, error) {
1463
1422
  ...readingFields(error.receipts),
1464
1423
  note: "the one-time migrations failed after committing changes: the readings on this line and the lines that follow are what this boot already committed — a retried " +
1465
1424
  "boot finds that work done and does not report it again, so these lines are the only record of them, written once by this boot — save them. The failure itself follows " +
1466
- "as this boot's refusal. A shape reading absent from this line (sessionIncarnationColumn / workflowParkIndexKey / sessionRuleColumn / workflowJournalScopeColumn) belongs to a step that did not " +
1425
+ "as this boot's refusal. A shape reading absent from this line (workflowParkIndexKey / sessionRuleColumn / workflowJournalScopeColumn) belongs to a step that did not " +
1467
1426
  "finish; sessionRuleColumn absent after session_rule_row_old_grammar_entries lines means the column still carries its in-progress name — the next boot rewrites the " +
1468
1427
  "remaining rows (rows already rewritten are left alone) and publishes the final name",
1469
1428
  });
@@ -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,6 +1,6 @@
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";
@@ -90,19 +90,14 @@ const RedeemedDotsSchema = z.record(z.string(), RuleDotSchema);
90
90
  const RuleEditedSchema = z
91
91
  .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
92
  .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]);
93
+ export const RuleOfferColumn = z.unknown().transform((raw, ctx) => {
94
+ const read = readRuleOffer2(raw);
95
+ if ("problem" in read) {
96
+ ctx.addIssue({ code: "custom", message: `the stored rule offer does not read (${read.problem})` });
97
+ return z.NEVER;
98
+ }
99
+ return read.offer;
100
+ });
106
101
  export const PERMISSION_RULE_TABLE = "permission_rule";
107
102
  export const PERMISSION_RULE_APPROVAL_TABLE = "permission_rule_approval";
108
103
  export const PERMISSION_RULE_TICKET_TABLE = "permission_rule_ticket";
@@ -364,7 +359,7 @@ const readTombstonesJson = createZodColumnReader(z.array(RuleTombstoneSchema));
364
359
  const readQuarantinedJson = createZodColumnReader(z.array(QuarantinedRuleAddRowSchema));
365
360
  const readObservedVectorJson = createZodColumnReader(FrontierSchema);
366
361
  const readCandidatesJson = createZodColumnReader(z.array(RuleCandidateSchema));
367
- const readOffersJson = createZodColumnReader(z.array(RuleOffer2Schema));
362
+ const readOffersJson = createZodColumnReader(z.array(RuleOfferColumn));
368
363
  const readRedeemedDotsJson = createZodColumnReader(RedeemedDotsSchema);
369
364
  const readEditedJson = createZodColumnReader(RuleEditedSchema);
370
365
  async function refusingUnreadableColumns(what, fn) {
@@ -578,9 +573,13 @@ export class SqlDurableRulePartitionProvider {
578
573
  this.now = now;
579
574
  }
580
575
  forPrincipal(principal) {
581
- if (typeof principal !== "string" || principal === "")
576
+ if (principal === undefined || principal === "")
582
577
  return EMPTY_DURABLE_RULE_PARTITION;
583
- return new SqlDurableRulePartition(this.db, { kind: "principal", principal }, this.now);
578
+ const name = principalNameOf(principal);
579
+ if (name === undefined) {
580
+ 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"');
581
+ }
582
+ return new SqlDurableRulePartition(this.db, { kind: "principal", principal: name }, this.now);
584
583
  }
585
584
  forLocalOwner() {
586
585
  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 映射 ⇒
@@ -17,7 +17,7 @@ import { BASELINE_IO_DEADLINE_MS, decodeInstructionSource, encodeInstructionSour
17
17
  import { MANAGED_RETENTION } from "./retention-store-sql.js";
18
18
  import { SESSION_META_INCARNATION_COLUMN, carriedIncarnation, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
19
19
  import { ensureIndexes } from "./ensure-index.js";
20
- import { assertFirstOwnerWritable, forkOwnerOf } from "./session-ownership.js";
20
+ import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue } from "./session-ownership.js";
21
21
  import { forkCopyOf, forkPointOf } from "./session-fork.js";
22
22
  const isDupKey = isPgUniqueViolation;
23
23
  function baselineQuery(text, values) {
@@ -219,7 +219,7 @@ export class PgSessionStore {
219
219
  return { session: new StoredSession(storage), sessionId };
220
220
  }
221
221
  async register(sessionId, owner) {
222
- if (typeof owner !== "string" && owner !== null) {
222
+ if (!isSessionOwnerValue(owner)) {
223
223
  if ((await this.headOf(sessionId)) !== undefined)
224
224
  return;
225
225
  assertFirstOwnerWritable(sessionId, owner);
@@ -85,6 +85,9 @@ export declare class RemoteAdbExecutionEnv implements RemoteExecutionEnv {
85
85
  /** core 7.49.0 [ref]:shell 子进程真拿到的 `CDPATH` 条目 —— 本车道的命令跑在 Android 设备的 shell 里(设备自己的 env),本类不读那份 env ⇒ `"unknown"`(core 契约:说不出就说 unknown,
86
86
  * 不许报一张没读过的空表;删除守卫因此对裸 `cd <相对名>` 之后的删除问人,而不是按「没有 CDPATH」放行)。 */
87
87
  readonly cdPath: "unknown";
88
+ /** core 7.56.0 [ref]:shell 子进程继承的环境里收不到的条目 —— 本车道 shell 的环境在 Android 设备上**构建**(本进程的 env 从不注入,只有引擎逐次交的 `env`
89
+ * 这条显式授予),没有一条是从本进程继承的 ⇒ `[]`(core 契约:「`[]` when its shell environment is built rather than inherited」)。 */
90
+ readonly withheldEnv: readonly [];
88
91
  private readonly cfg;
89
92
  private readonly spawnFn;
90
93
  private readonly injectedConnectOnce?;
@@ -37,6 +37,7 @@ export class RemoteAdbExecutionEnv {
37
37
  }
38
38
  homeSeat;
39
39
  cdPath = "unknown";
40
+ withheldEnv = [];
40
41
  cfg;
41
42
  spawnFn;
42
43
  injectedConnectOnce;
@@ -89,6 +89,9 @@ export declare class RemoteDeviceExecutionEnv implements RemoteExecutionEnv {
89
89
  /** core 7.49.0 [ref]:shell 子进程真拿到的 `CDPATH` 条目 —— 本车道的命令跑在设备上(设备自己的 env),本类不读那份 env ⇒ `"unknown"`(core 契约:说不出就说 unknown,
90
90
  * 不许报一张没读过的空表;删除守卫因此对裸 `cd <相对名>` 之后的删除问人,而不是按「没有 CDPATH」放行)。 */
91
91
  readonly cdPath: "unknown";
92
+ /** core 7.56.0 [ref]:shell 子进程继承的环境里收不到的条目 —— 本车道 shell 的环境在设备上**构建**(本进程的 env 从不注入,只有引擎逐次交的 `env`
93
+ * 这条显式授予),没有一条是从本进程继承的 ⇒ `[]`(core 契约:「`[]` when its shell environment is built rather than inherited」)。 */
94
+ readonly withheldEnv: readonly [];
92
95
  /**
93
96
  * 🔴 `suspendable: false` 在 core 契约里**同时断言**「工作区在目标上外部持久」(remote-env.ts 的
94
97
  * load-bearing 不变量)—— 设备工作区 = 员工机磁盘,真持久 ✅,park-only 降级腿因此合法可用。