@sema-agent/server 7.145.0 → 7.146.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 (275) hide show
  1. package/MIGRATION.md +492 -0
  2. package/USAGE.md +218 -47
  3. package/deploy/facts/CHANGES.json +25 -0
  4. package/deploy/facts/README.md +57 -0
  5. package/deploy/facts/lanes.json +54 -0
  6. package/deploy/facts/lanes.schema.json +57 -0
  7. package/deploy/facts/sandbox-rbac.json +113 -0
  8. package/deploy/facts/sandbox-rbac.schema.json +74 -0
  9. package/deploy/facts/sealed-key.json +9 -0
  10. package/deploy/facts/sealed-key.schema.json +52 -0
  11. package/deploy/sema-up/chart/templates/_helpers.tpl +19 -0
  12. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
  13. package/deploy/sema-up/chart/templates/server-v4pro.yaml +4 -0
  14. package/deploy/sema-up/chart/templates/server.yaml +4 -0
  15. package/deploy/sema-up/chart/values.yaml +7 -0
  16. package/deploy/sema-up/stack/sema-stack-cluster.yaml +30 -14
  17. package/dist/adoption/import-limits.d.ts +6 -0
  18. package/dist/adoption/import-limits.js +7 -0
  19. package/dist/adoption/import.d.ts +62 -0
  20. package/dist/adoption/import.js +107 -0
  21. package/dist/adoption/wire.d.ts +52 -0
  22. package/dist/adoption/wire.js +9 -0
  23. package/dist/approval-ask-machine.d.ts +2 -2
  24. package/dist/approval-card.d.ts +3 -12
  25. package/dist/approval-card.js +0 -4
  26. package/dist/approval-gate-currency.d.ts +1 -1
  27. package/dist/approval-gate-currency.js +1 -1
  28. package/dist/approval-reconciler.d.ts +1 -1
  29. package/dist/boot/deployment-mods.d.ts +11 -1
  30. package/dist/boot/deployment-mods.js +15 -2
  31. package/dist/boot/device-lane.js +2 -0
  32. package/dist/boot/engine-lease.d.ts +193 -21
  33. package/dist/boot/engine-lease.js +146 -20
  34. package/dist/boot/execution-env.d.ts +4 -0
  35. package/dist/boot/execution-env.js +12 -0
  36. package/dist/boot/exit-boundary.d.ts +50 -0
  37. package/dist/boot/exit-boundary.js +32 -0
  38. package/dist/boot/exit-last.d.ts +20 -4
  39. package/dist/boot/exit-last.js +21 -4
  40. package/dist/boot/governance-seams.js +1 -0
  41. package/dist/boot/memory-consolidation.d.ts +29 -17
  42. package/dist/boot/memory-consolidation.js +26 -15
  43. package/dist/boot/reapers.d.ts +12 -4
  44. package/dist/boot/reapers.js +39 -15
  45. package/dist/boot/resolve-spec.d.ts +2 -0
  46. package/dist/boot/resolve-spec.js +38 -15
  47. package/dist/boot/runner-deps.js +1 -1
  48. package/dist/boot/session-faces.js +3 -1
  49. package/dist/boot/shutdown.js +27 -9
  50. package/dist/boot/stage-01-config.js +1 -1
  51. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  52. package/dist/boot/stage-03-store-backend.js +2 -2
  53. package/dist/boot/stage-04-stores.d.ts +1 -0
  54. package/dist/boot/stage-05-execution-env.d.ts +3 -0
  55. package/dist/boot/stage-05-execution-env.js +3 -3
  56. package/dist/boot/stage-06-runners.d.ts +3 -0
  57. package/dist/boot/stage-06-runners.js +11 -10
  58. package/dist/boot/stage-07-capability-layer.d.ts +3 -0
  59. package/dist/boot/stage-07-capability-layer.js +7 -2
  60. package/dist/boot/stage-08-reapers.d.ts +3 -0
  61. package/dist/boot/stage-08-reapers.js +3 -3
  62. package/dist/boot/stage-09-leader.d.ts +3 -0
  63. package/dist/boot/stage-10-http-server.d.ts +3 -0
  64. package/dist/boot/stage-10-http-server.js +3 -2
  65. package/dist/boot/stores.d.ts +1 -0
  66. package/dist/boot/stores.js +35 -19
  67. package/dist/boot/workflow-orchestration.d.ts +1 -1
  68. package/dist/boot/workflow-orchestration.js +2 -1
  69. package/dist/capabilities/composite-members.d.ts +3 -1
  70. package/dist/capabilities/composite-members.js +3 -1
  71. package/dist/capabilities/scenarios.d.ts +2 -2
  72. package/dist/capability-facts.d.ts +18 -1
  73. package/dist/capability-facts.js +25 -7
  74. package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
  75. package/dist/config-catalog.js +13 -4
  76. package/dist/config-provider.d.ts +2 -2
  77. package/dist/config-types.d.ts +43 -4
  78. package/dist/config.d.ts +6 -1
  79. package/dist/config.js +67 -11
  80. package/dist/deleted-session-ids.d.ts +19 -0
  81. package/dist/deleted-session-ids.js +26 -0
  82. package/dist/deploy-facts.d.ts +155 -0
  83. package/dist/deploy-facts.js +94 -0
  84. package/dist/device-ws-hub.d.ts +48 -3
  85. package/dist/device-ws-hub.js +48 -2
  86. package/dist/error-code-family.d.ts +35 -0
  87. package/dist/error-code-family.js +26 -0
  88. package/dist/fleet/drain-entry.d.ts +39 -3
  89. package/dist/fleet/drain-entry.js +6 -0
  90. package/dist/fleet/fleet-bus.d.ts +2 -1
  91. package/dist/hooks/branch-transcript.d.ts +1 -1
  92. package/dist/hooks/branch-transcript.js +1 -0
  93. package/dist/hooks/classic-stations.d.ts +25 -8
  94. package/dist/hooks/classic-stations.js +29 -3
  95. package/dist/hooks/hook-runner.d.ts +33 -16
  96. package/dist/hooks/hook-runner.js +166 -73
  97. package/dist/hooks/hook-source.d.ts +14 -2
  98. package/dist/hooks/hook-source.js +8 -0
  99. package/dist/http/active-run-conflict.d.ts +1 -1
  100. package/dist/http/active-run-conflict.js +1 -1
  101. package/dist/http/admission.js +11 -1
  102. package/dist/http/checkpoint-ctx.d.ts +59 -71
  103. package/dist/http/checkpoint-ctx.js +34 -40
  104. package/dist/http/dispatch.js +18 -2
  105. package/dist/http/durable-run-leg.d.ts +5 -0
  106. package/dist/http/durable-run-leg.js +1 -0
  107. package/dist/http/execution-context.d.ts +4 -0
  108. package/dist/http/execution-context.js +9 -0
  109. package/dist/http/health-self-report.d.ts +6 -2
  110. package/dist/http/health-self-report.js +3 -2
  111. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  112. package/dist/http/memory-compliance-failure-reply.js +2 -15
  113. package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
  114. package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
  115. package/dist/http/principal-gate.d.ts +35 -12
  116. package/dist/http/principal-gate.js +16 -12
  117. package/dist/http/request-guards.d.ts +1 -1
  118. package/dist/http/request-guards.js +5 -5
  119. package/dist/http/resume-legs.d.ts +22 -0
  120. package/dist/http/resume-legs.js +26 -17
  121. package/dist/http/route-ctx.d.ts +11 -10
  122. package/dist/http/route-table.js +2 -0
  123. package/dist/http/routes/adoption.d.ts +11 -0
  124. package/dist/http/routes/adoption.js +80 -6
  125. package/dist/http/routes/agents-roster.js +1 -0
  126. package/dist/http/routes/approvals-assistant.js +2 -1
  127. package/dist/http/routes/background.js +1 -0
  128. package/dist/http/routes/capabilities.js +4 -1
  129. package/dist/http/routes/diagnostics.d.ts +9 -14
  130. package/dist/http/routes/diagnostics.js +10 -20
  131. package/dist/http/routes/engine.d.ts +31 -0
  132. package/dist/http/routes/engine.js +81 -0
  133. package/dist/http/routes/memory-bundle.js +3 -0
  134. package/dist/http/routes/memory-consolidation.js +3 -5
  135. package/dist/http/routes/memory-origin.d.ts +1 -1
  136. package/dist/http/routes/memory-origin.js +2 -4
  137. package/dist/http/routes/memory-policy.js +5 -1
  138. package/dist/http/routes/observability.js +3 -6
  139. package/dist/http/routes/runs.js +7 -3
  140. package/dist/http/routes/session-sync.js +12 -25
  141. package/dist/http/routes/sessions-list.d.ts +1 -1
  142. package/dist/http/routes/sessions.js +4 -0
  143. package/dist/http/routes/tasks.js +23 -5
  144. package/dist/http/routes/trace-usage.js +1 -1
  145. package/dist/http/run-concurrency-cap.d.ts +86 -0
  146. package/dist/http/run-concurrency-cap.js +54 -0
  147. package/dist/http/server.d.ts +15 -12
  148. package/dist/http/server.js +18 -3
  149. package/dist/http/stored-body-replay.d.ts +9 -6
  150. package/dist/http/stored-body-replay.js +19 -5
  151. package/dist/http/wire-types.d.ts +8 -3
  152. package/dist/json-body-limit.d.ts +8 -0
  153. package/dist/json-body-limit.js +2 -0
  154. package/dist/leader/diffout.d.ts +1 -1
  155. package/dist/local-data-root.d.ts +2 -2
  156. package/dist/memory-operator-faces.d.ts +9 -4
  157. package/dist/memory-scope-stores.d.ts +9 -7
  158. package/dist/memory-scope-stores.js +3 -14
  159. package/dist/memory-scope.d.ts +36 -0
  160. package/dist/memory-scope.js +32 -1
  161. package/dist/net-address.d.ts +53 -0
  162. package/dist/net-address.js +111 -0
  163. package/dist/observability/fail-open.d.ts +14 -10
  164. package/dist/observability/fail-open.js +14 -10
  165. package/dist/orchestration/workflow-notify-journal.js +2 -3
  166. package/dist/org-delivery-gate.js +1 -1
  167. package/dist/peer-directory.d.ts +6 -12
  168. package/dist/peer-directory.js +5 -1
  169. package/dist/plugins/caching-session-store.d.ts +3 -1
  170. package/dist/plugins/caching-session-store.js +3 -0
  171. package/dist/plugins/checkpoint-store-face.d.ts +426 -0
  172. package/dist/plugins/checkpoint-store-face.js +152 -0
  173. package/dist/plugins/file-run-store.d.ts +0 -6
  174. package/dist/plugins/file-run-store.js +1 -12
  175. package/dist/plugins/fork-routing-session-store.d.ts +6 -2
  176. package/dist/plugins/fork-routing-session-store.js +2 -2
  177. package/dist/plugins/imported-layer-root-retirement.d.ts +10 -8
  178. package/dist/plugins/local-checkpoint-store.d.ts +2 -2
  179. package/dist/plugins/local-checkpoint-store.js +2 -1
  180. package/dist/plugins/local-session-store.d.ts +28 -5
  181. package/dist/plugins/local-session-store.js +32 -8
  182. package/dist/plugins/memory-engine-pg.d.ts +29 -6
  183. package/dist/plugins/memory-engine-pg.js +96 -214
  184. package/dist/plugins/memory-engine-sql-apply.d.ts +121 -0
  185. package/dist/plugins/memory-engine-sql-apply.js +358 -0
  186. package/dist/plugins/memory-engine-tidb.d.ts +35 -9
  187. package/dist/plugins/memory-engine-tidb.js +97 -227
  188. package/dist/plugins/memory-key-guards.d.ts +1 -1
  189. package/dist/plugins/memory-origin-law.d.ts +3 -3
  190. package/dist/plugins/one-time-migrations.d.ts +32 -56
  191. package/dist/plugins/one-time-migrations.js +25 -117
  192. package/dist/plugins/permission-rule-store-file.d.ts +17 -3
  193. package/dist/plugins/permission-rule-store-file.js +8 -1
  194. package/dist/plugins/pg-pool.js +1 -1
  195. package/dist/plugins/pg-session-storage.d.ts +17 -4
  196. package/dist/plugins/pg-session-storage.js +8 -4
  197. package/dist/plugins/run-store-sql.js +2 -2
  198. package/dist/plugins/schema-ensure-legs.js +2 -2
  199. package/dist/plugins/session-fork.d.ts +31 -9
  200. package/dist/plugins/session-fork.js +3 -2
  201. package/dist/plugins/session-placement.d.ts +4 -2
  202. package/dist/plugins/sql-json-column.d.ts +5 -1
  203. package/dist/plugins/store-backend.d.ts +11 -8
  204. package/dist/plugins/store-backend.js +8 -7
  205. package/dist/plugins/store-contracts.d.ts +1 -11
  206. package/dist/plugins/store-contracts.js +0 -9
  207. package/dist/plugins/store-sweep.d.ts +63 -0
  208. package/dist/plugins/store-sweep.js +48 -0
  209. package/dist/plugins/tidb-pool.js +1 -1
  210. package/dist/plugins/tidb-session-store.d.ts +16 -3
  211. package/dist/plugins/tidb-session-store.js +8 -4
  212. package/dist/prompt-screen.d.ts +6 -9
  213. package/dist/prompt-screen.js +2 -5
  214. package/dist/rules-consent.d.ts +20 -27
  215. package/dist/rules-consent.js +82 -6
  216. package/dist/run-cancel-context.d.ts +4 -1
  217. package/dist/run-cancel-context.js +2 -1
  218. package/dist/runs.js +1 -1
  219. package/dist/sandbox-egress-policy.js +6 -11
  220. package/dist/sandbox-rbac-rules.d.ts +88 -0
  221. package/dist/sandbox-rbac-rules.js +80 -0
  222. package/dist/sealed-key-layout.d.ts +17 -0
  223. package/dist/sealed-key-layout.js +4 -0
  224. package/dist/sealed-key.js +5 -5
  225. package/dist/security.d.ts +38 -6
  226. package/dist/security.js +9 -2
  227. package/dist/session-notify-door.d.ts +5 -3
  228. package/dist/session-resident-background.d.ts +57 -0
  229. package/dist/session-resident-background.js +20 -0
  230. package/dist/session-sync-kernel.d.ts +53 -14
  231. package/dist/session-sync-kernel.js +70 -1
  232. package/dist/session-sync.d.ts +5 -3
  233. package/dist/session-workspace.d.ts +1 -0
  234. package/dist/session-workspace.js +1 -0
  235. package/dist/sighup-idle.d.ts +9 -7
  236. package/dist/sighup-idle.js +4 -5
  237. package/dist/spec-fields.d.ts +16 -0
  238. package/dist/spec-fields.js +7 -0
  239. package/dist/stored-request-env-credentials-m38.d.ts +3 -0
  240. package/dist/stored-request-env-credentials-m38.js +8 -0
  241. package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
  242. package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +13 -427
  243. package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +33 -194
  244. package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
  245. package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
  246. package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
  247. package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
  248. package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +2 -2
  249. package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
  250. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
  251. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
  252. package/dist/task-settings.d.ts +59 -4
  253. package/dist/task-settings.js +50 -4
  254. package/dist/tool-approval.d.ts +2 -2
  255. package/dist/trace/core-keyset-guard.d.ts +8 -7
  256. package/dist/trace/engine-notice-wire.d.ts +1 -1
  257. package/dist/trace/engine-notice-wire.js +2 -0
  258. package/dist/trace/exit-seat.d.ts +6 -0
  259. package/dist/trace/exit-seat.js +1 -0
  260. package/dist/trace/project.js +9 -0
  261. package/dist/trace/redact.d.ts +13 -2
  262. package/dist/trace/redact.js +305 -24
  263. package/dist/trace/self-minted-wire.d.ts +326 -8
  264. package/dist/trace/self-minted-wire.js +125 -7
  265. package/dist/trace/wire-projection-faces.d.ts +1 -1
  266. package/dist/trace/wire-projection-faces.js +1 -1
  267. package/dist/usage-analytics.d.ts +4 -0
  268. package/package.json +12 -4
  269. package/scripts/drain-check-local.mjs +8 -29
  270. package/scripts/drain-check.mysql.sql +3 -24
  271. package/scripts/drain-check.pg.sql +3 -24
  272. package/skills/update-config.md +1 -1
  273. package/wire-changes/server@7.146.0.json +3221 -0
  274. package/dist/approval-batch-census.d.ts +0 -27
  275. package/dist/approval-batch-census.js +0 -27
@@ -4,6 +4,7 @@ import { buildFleetWorkflowRow } from "../fleet/fleet-bus.js";
4
4
  import { isTerminalWorkflowRowStatus, seedTerminalWorkflowRow } from "../fleet/fleet-terminal-window.js";
5
5
  import { buildParkSessionRows } from "./workflow-agent-session-index.js";
6
6
  import { recordFailOpen } from "../observability/fail-open.js";
7
+ import { seatText } from "../trace/exit-seat.js";
7
8
  import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
8
9
  export const WORKFLOW_NOTIFY_KEY_DISPOSITION = {
9
10
  runId: "carry",
@@ -25,10 +26,8 @@ export function recoverySummary(status, error) {
25
26
  if (status === "completed")
26
27
  return "workflow completed";
27
28
  const base = error ? `workflow failed: ${error}` : "workflow failed";
28
- const redacted = base
29
+ const redacted = seatText("workflow-recovery-summary", base)
29
30
  .replace(/https?:\/\/[^\s"'`]+/gi, REDACTION_MARKS.url)
30
- .replace(/\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, "[redacted-token]")
31
- .replace(/\b(?:api[_-]?key|secret|token|password|authorization)\b\s*[:=]\s*"?[^\s"',}]+/gi, "[redacted-secret]")
32
31
  .replace(/(?:\/[A-Za-z0-9_.-]+){2,}/g, REDACTION_MARKS.path)
33
32
  .replace(/(?:[A-Za-z]:)?(?:\\{1,2}[A-Za-z0-9_.$-]+){2,}/g, REDACTION_MARKS.path);
34
33
  return redacted.length > SUMMARY_MAX ? `${redacted.slice(0, SUMMARY_MAX)}…[+${redacted.length - SUMMARY_MAX} chars]` : redacted;
@@ -1,6 +1,6 @@
1
1
  import { CrossSessionCodecError, MAILBOX_TOMBSTONED_RECIPIENT_CODE, PRINCIPAL_INBOX_HANDLE, peerSessionBoxHandle, mintCrossPrincipalRecord, uuidv7, } from "@sema-agent/core";
2
2
  import { principalShapeProblem } from "./security.js";
3
- import { mailboxKeyProblem } from "./plugins/mailbox-store-sql.js";
3
+ import { mailboxKeyProblem } from "./stores-sql/mailbox-store-sql.js";
4
4
  export const ORG_DELIVERY_QUOTA = Object.freeze({ windowMs: 3_600_000, attemptsPerSender: 60, perPair: 10, perRecipient: 30 });
5
5
  export const PRINCIPAL_INBOX_BOUND = 50;
6
6
  export const ORG_DELIVERY_REFUSAL_MESSAGES = Object.freeze({
@@ -63,6 +63,9 @@
63
63
  * · `tempo` = `sessionTempoOfRunStatus(lastStatus)`(`plugins/store-contracts.ts` 的那**一张**表,
64
64
  * `GET /v1/sessions` 的 `tempo` 列读的是同一只函数 —— 一处定义两处读);
65
65
  * · `inboundPosture` = managed 层 `crossSessionInbound === "refuse"` ⇒ `"unavailable"`,否则 `"available"`;
66
+ * · `occupant` = 会话行的化身(`session_meta.incarnation`,与列表行**同一次读** —— lister 交 `{ row, occupant }`,化身从不进行本身)= 会话腿 acquire
67
+ * 读到的那一位(core 7.64.0 起跨会话信与回执按 (会话 id, 住客) 投:目录投的若是别的一格,寄往这只会话的每封信都会被判成「给另一位住客的」而丢弃);
68
+ * 行上没有化身(本仓的店每建一行铸一枚,只有坏行 / 人为改库才缺)⇒ `null`。令牌只进引擎(`ListAgents` 不渲染它),本仓不把它投上 wire / 日志正文;
66
69
  * · `pid` / `procStartMs` / `sockPath` / `cwd` = **恒缺席**(server 会话是耐久可寻址实体,不是进程;
67
70
  * 缺席即 core 的探活臂按 `isPeerSessionProcessAlive` 直接跳过);
68
71
  * · `peerProtocol` / `peerFeatures` = 不铸(v1 不声明 CC 能力位;core 侧全 optional)。
@@ -71,18 +74,9 @@ import { type PeerDirectory } from "@sema-agent/core";
71
74
  import type { CrossSessionInboundSettingLayers } from "@sema-agent/core";
72
75
  import type { Logger } from "./observability/logger.js";
73
76
  import type { MailboxRecipientLifecycle } from "./plugins/mailbox-recipient-lifecycle.js";
74
- import { type SessionListItem } from "./security.js";
75
- /** 会话枚举面(= `GET /v1/sessions` 用的那只 `OwnerAwareSessionStore.listSessions`)。 */
76
- export type PeerSessionLister = (opts: {
77
- owner?: string;
78
- includeUnowned?: boolean;
79
- cursor?: {
80
- lastActivityAt: string;
81
- sessionId: string;
82
- };
83
- limit: number;
84
- q?: string;
85
- }) => Promise<SessionListItem[]>;
77
+ import { type OwnerAwareSessionStore } from "./security.js";
78
+ /** 会话枚举面(= `GET /v1/sessions` 用的那只列表面的带住客形 `OwnerAwareSessionStore.listSessionRows`:每行配读到它时的住客)。 */
79
+ export type PeerSessionLister = NonNullable<OwnerAwareSessionStore["listSessionRows"]>;
86
80
  /**
87
81
  * 一次 `listPeerSessions` 最多铸多少行。
88
82
  *
@@ -14,7 +14,9 @@ export function createPeerDirectory(ctx) {
14
14
  listPeerSessions: async (access) => {
15
15
  const scope = access.scope;
16
16
  const includeUnowned = legacyUnownedAmnestyOpen({ requirePrincipal }, scope);
17
- const rows = await lister({ owner: scope, ...(includeUnowned ? { includeUnowned: true } : {}), limit: PEER_DIRECTORY_CAP + 1 });
17
+ const answers = await lister({ owner: scope, ...(includeUnowned ? { includeUnowned: true } : {}), limit: PEER_DIRECTORY_CAP + 1 });
18
+ const rows = answers.map((a) => a.row);
19
+ const occupantOf = new Map(answers.map((a) => [a.row.sessionId, a.occupant.incarnation ?? null]));
18
20
  const truncated = rows.length > PEER_DIRECTORY_CAP;
19
21
  if (truncated) {
20
22
  logger.warn("peer_directory_truncated", {
@@ -88,6 +90,7 @@ export function createPeerDirectory(ctx) {
88
90
  where: "hosted",
89
91
  ...(tempo !== undefined ? { tempo } : {}),
90
92
  inboundPosture: posture,
93
+ occupant: occupantOf.get(r.sessionId) ?? null,
91
94
  });
92
95
  }
93
96
  if (ungrammatical > 0) {
@@ -129,6 +132,7 @@ export function createPeerDirectory(ctx) {
129
132
  where: "hosted",
130
133
  diedAt: t.retiredAt,
131
134
  inboundPosture: posture,
135
+ occupant: null,
132
136
  });
133
137
  }
134
138
  return out;
@@ -68,7 +68,9 @@ export declare class CachingSessionStore implements SessionStore {
68
68
  limit: number;
69
69
  q?: string;
70
70
  }) => Promise<SessionListItem[]>;
71
- readonly fork?: (sourceId: string, owner: string | null) => Promise<string | null>;
71
+ /** 带住客的同一只列表面(原样转发;理由同 listSessions)。 */
72
+ readonly listSessionRows?: OwnerAwareSessionStore["listSessionRows"];
73
+ readonly fork?: OwnerAwareSessionStore["fork"];
72
74
  /** fork 续写的店契约(原样转发;与 fork 同理绕过暖缓存 —— 读写的是持久行,新 id 本来就不在缓存里)。 */
73
75
  readonly forkTo?: OwnerAwareSessionStore["forkTo"];
74
76
  /** 执行席(原样转发:放置是会话**行**上的一格,缓存只缓存会话日志的暖句柄,不缓存行格 —— 读与写都直达内层店)。 */
@@ -10,6 +10,7 @@ export class CachingSessionStore {
10
10
  headOf;
11
11
  register;
12
12
  listSessions;
13
+ listSessionRows;
13
14
  fork;
14
15
  forkTo;
15
16
  executionPlacement;
@@ -58,6 +59,8 @@ export class CachingSessionStore {
58
59
  this.register = ownerAware.register.bind(inner);
59
60
  if (ownerAware.listSessions)
60
61
  this.listSessions = ownerAware.listSessions.bind(inner);
62
+ if (ownerAware.listSessionRows)
63
+ this.listSessionRows = ownerAware.listSessionRows.bind(inner);
61
64
  if (ownerAware.sessionSummaryOf)
62
65
  this.sessionSummaryOf = ownerAware.sessionSummaryOf.bind(inner);
63
66
  if (ownerAware.writeSessionRowCells)
@@ -0,0 +1,426 @@
1
+ /**
2
+ * 检查点店的**服务面**:两只孪生(SQL 孪生 `stores-sql/checkpoint-store-sql.ts`、LOCAL 孪生 `local-checkpoint-store.ts`)共同实现、服务的读点
3
+ * (运维队列 / 审批决断 / 续跑腿 / 流内审批收敛器 / 起服普查)共同读的那一层 —— 引擎的 `CheckpointStore` 契约之外、本服务自己定的东西:
4
+ * · 行形:运维队列行 {@link PendingCheckpoint}、流内审批候选行 {@link CheckpointAskCandidate}、已决回放行 {@link DecidedApprovalRecord};
5
+ * · 读口的行绑定 {@link PendingRowBinding} 与门型两分({@link LIVE_RUN_GATE_KINDS} / {@link TERMINAL_PARK_GATE_KINDS}),两孪生同序判据 {@link pendingRowPrecedes};
6
+ * · 两孪生同调的纯判据与投影:{@link queueRowGateKind}、{@link boundedToolInput}、{@link boundedRuleOffers}、
7
+ * {@link approvalDecisionOfWinner}、{@link candidateIdentityOf};常量 {@link TERMINAL_BACKSTOP_MS} / {@link TERMINAL_GRACE_MS} / {@link DECIDED_REPLAY_SCAN_CAP};
8
+ * · ctx 两口的结局词 {@link CtxClaimRewrite} 与一次性迁移的 ctx 行改写店腿的型(随最后一条用它的迁移删,见 {@link CheckpointCtxRowsRewriter});
9
+ * · 店拒绝契约里「一行字节读不成检查点」那一只 {@link UnreadableCheckpointRowError}(SQL 孪生抛、续跑腿认)。
10
+ *
11
+ * 为什么单成一只模块(依赖方向律 R8,`test/dependency-direction-gate.test.ts`):SQL 孪生住在 `src/stores-sql/`,那只目录要能原样搬进引擎侧的包,
12
+ * 目录外除组合座外**谁也不得直引它**。这一层的读者遍布服务(LOCAL 孪生、HTTP 续跑腿、审批决断、收敛器、运维队列的 wire 键表),所以它不能住在
13
+ * SQL 孪生的文件里;它住这里,SQL 孪生经一条点名的已知欠边借它 —— 这一条边就是阶段三搬家时「服务特有面」的清单(向引擎请托的候选,只列不发)。
14
+ * 这里零 SQL:SQL 文本、列读法与行投影仍在 SQL 孪生里。
15
+ */
16
+ import { type Checkpoint, type CheckpointGate, type CheckpointRowVerdict, type GateOutcome, type ResolvedOutcome, type RiskDescriptor, type ToolApprovalCard } from "@sema-agent/core";
17
+ import { type ParkRowFacts, type RuleOfferProjection, type SkillCardFacts } from "../approval-card.js";
18
+ import { StoreCorruptError } from "./sql-json-column.js";
19
+ import type { SealedCheckpointCtx } from "../http/checkpoint-ctx.js";
20
+ /** ctx 行改写店腿的读数(两孪生同形):扫到的可读行 · 回写成的行 · 回写成的行里改写函数报的改动数 · 读不出的行 · 改写之后一行装不下的行。 */
21
+ export interface CtxRowsRewrite {
22
+ readonly scanned: number;
23
+ readonly rewritten: number;
24
+ readonly valuesRewritten: number;
25
+ readonly unreadable: number;
26
+ readonly tooLarge: number;
27
+ }
28
+ /** 一行的改写:交回新的落盘形(只有封口模块的铸点产得出 —— 品牌型)与这一行的改动数;`undefined` = 这一行不写。 */
29
+ export type CtxRowRewrite = (sessionId: string, ctx: object) => {
30
+ readonly ctx: SealedCheckpointCtx;
31
+ readonly values: number;
32
+ } | undefined;
33
+ /**
34
+ * 🔴 **一次性迁移的 ctx 行改写店腿**(两孪生:`SqlCheckpointStore` / `LocalCheckpointStore` 的 `rewriteCtxRows`;逐行判词两孪生同):扫全部 `checkpoint_ctx` 行、
35
+ * 交给 `rewrite`、交回新体的行原地回写(续跑时钟不动)。店不知道改写是什么 —— 用它的迁移各自带行改写:M28(凭据位明文封好)、M30(退役请求键改写)。
36
+ * 窗与删法逐条在 `plugins/one-time-migrations.ts` 登记表;登记表里最后一条用它的迁移删码时,本腿、读数型与扫描页一起删。
37
+ */
38
+ export interface CheckpointCtxRowsRewriter {
39
+ rewriteCtxRows(rewrite: CtxRowRewrite): Promise<CtxRowsRewrite>;
40
+ }
41
+ /**
42
+ * [ref] D-1 (§3 invariant #3 — crash-safe reaper backstop): an ABSOLUTE upper bound on a pending
43
+ * checkpoint's lifetime, stamped at put() into `terminal_at_ms` INDEPENDENT of the per-approval `deadline`. The
44
+ * SLA-timer (D-D) does the fine, per-gate-kind resolve-deny; THIS coarse backstop ensures even a pending row
45
+ * with a NULL `deadline` (no approval TTL configured) is eventually GC'd if the SLA service dies — closing a
46
+ * forever-leak of a never-resolved suspension (the current `reap`/`reapExpired` only catch non-NULL deadlines).
47
+ * Generous (default 30d) so it never pre-empts a legitimately long-lived human/irreversible_ask gate;
48
+ * env-overridable. Clamped ≥1min so a misconfig can't expire live suspensions instantly.
49
+ */
50
+ export declare const TERMINAL_BACKSTOP_MS: number;
51
+ /**
52
+ * [ref] D-D (cross-review fix): the crash-safe `terminal_at_ms` backstop must fall STRICTLY AFTER any SLA
53
+ * `deadline`, never AT it. `terminal_at_ms = max(createdAt+backstop, deadline)` made the two coincide whenever an
54
+ * operator tuned APPROVAL_TERMINAL_BACKSTOP_MS at/below the SLA — and reapExpired's terminal_at_ms-branch (which
55
+ * has NO gate_kind filter) then abort-EXPIRED a human/irreversible_ask gate in the SAME tick the deny-sweep
56
+ * wanted to gracefully DENY it, racing it away. Adding this grace to the deadline term guarantees the deny-sweep
57
+ * at least this window of clean ticks before the absolute backstop can fire. Far smaller than the backstop, so
58
+ * it never meaningfully delays the eventual crash-safe GC.
59
+ */
60
+ export declare const TERMINAL_GRACE_MS = 3600000;
61
+ /** Non-secret view of a pending checkpoint for the operator approval queue — deliberately NO token.
62
+ *
63
+ * 🔴 [ref]:行**顶层**另带 {@link ParkRowFacts}(`requiresRealApproval` / `ruleOffersAbsence`,真才带、缺席 ≠ false)——
64
+ * 型与值都只有一个来源:`approval-card.ts` 的 {@link parkRowFacts}(两只店各读自己手里的那只寄存行 `pendingAction`,
65
+ * 交给它、原样展开)。继承而不是在这里逐键再写一遍:core 给这一臂再加一位同类事实时,改的只有那张 Pick 表。
66
+ *
67
+ * [ref]:授予绑定那一臂(`boundGrantDigest` / `boundGrantAbsence` 恰带其一)是 {@link ParkRowFacts} 的一部分,同样读自寄存行。
68
+ * 技能卡两位({@link SkillCardFacts}:`boundGrants` / `classifierNoRuling`)另有一只投影 {@link skillCardFacts},输入是 core 的检查点摘要而不是
69
+ * 寄存行原值(清单只在哈希等于摘要时才配上 wire,校验点单点在 core —— 见 {@link skillCardFacts} 顶注);同一张行上与收件箱行逐字同值,在场才带。 */
70
+ export interface PendingCheckpoint extends ParkRowFacts, SkillCardFacts {
71
+ sessionId: string;
72
+ /**
73
+ * 这道停驻门的**非秘密稳定身份**(core `Checkpoint.checkpointId`,引擎每次停驻铸)—— 三处 durable 读面(本行 = `GET /v1/approvals` 的 `pending[]`
74
+ * 与 `/v1/approvals/stream` 的 `pending` 帧;停驻收件 `GET /v1/assistant/inbox` 的 `inbox[]`)共用的 **join 键**,也是 409 载体 A
75
+ * `pendingGate.checkpointId` / `terminal.paused.checkpointId` 的同一个值。**在场就是可信的那一个值,缺席就是缺席**:读法唯一 = `checkpoint-identity.ts` 的
76
+ * `durableRowIdentity`(过信任边界 —— 不含 token、有界),没有它的行(core 5.42.0 之前停驻的老行 / 没过边界的值)**照列**、键缺席(这一行没有可 join 的身份)、
77
+ * 每行计一次 `server.approvals.durable-row-checkpoint-id-missing`。两只店各读自己手里的那一份(SQL 孪生读 `checkpoint` 列的成员,LOCAL 孪生读 blob)。
78
+ */
79
+ checkpointId?: string;
80
+ scope: string;
81
+ toolName: string | null;
82
+ toolCallId: string | null;
83
+ /** [ref] D-1: the decision-action binding fields the operator/portal must ECHO back on /decide so the
84
+ * resume binds to the EXACT action they saw (a stale view is then rejected fail-closed PRE-CAS). `boundCallId`
85
+ * === `toolCallId` (the pending tool call); `boundInputHash` is the server-minted opaque hash of the shown
86
+ * input (NEVER recomputed by the portal — echoed verbatim). Surfacing them here is what makes the D-1 TOCTOU
87
+ * guard REACHABLE by the portal (without it the portal can only do the unbound legacy fallback).
88
+ * 🔴 [ref]②: ABSENT (key omitted, never `null`) on a park with no tool action (plan_review /
89
+ * resource_limit / task_done) or a pre-D-1 legacy row ⇒ the consumer falls back to the unbound resolve.
90
+ * The wire contract (§1) is "an absent nullable field is OMITTED, not null" — a `null` here invited a
91
+ * consumer to treat the key's PRESENCE as "there is a binding" and echo `null` back into /decide. */
92
+ boundCallId?: string;
93
+ boundInputHash?: string;
94
+ /** 🔴 [ref]③: the checkpoint's gate kind — the operator queue mixes tool approvals with
95
+ * plan_review / resource_limit / task_done parks (all of which carry `toolName: null`), so a durable-recovery
96
+ * consumer needs this to ROUTE a row to the right wire (`/decide` vs `/plan_review` vs `/resume`). ALWAYS present
97
+ * ([ref], 7.132.0): every park stamps it, the one-time migration M21 filled any row without it from the row's gate,
98
+ * and a SQL row that still carries none is not listed by `listPending` (counted: `server.approvals.queue-row-gate-kind-missing`; no decide route binds it either). */
99
+ gateKind: CheckpointGate["kind"];
100
+ /** The suspended run's taskId (task_active join) — the key to the run/trace ("view task context"). */
101
+ taskId: string | null;
102
+ /** The pending tool call's args (post-hook), redacted + bounded — for tool_approval the write payload, for a
103
+ * durable AskUserQuestion the question itself. Lets the approval card render WITHOUT an N+1 trace.turns
104
+ * fetch. `null` for pre-migration rows (suspended before the column existed) ⇒ consumer falls back. */
105
+ input: unknown;
106
+ createdAt: number;
107
+ deadline: number | null;
108
+ /** [ref] riskDescriptor inbox: core's INERT triage descriptor ({severity 1-5, axes, toolName, redacted
109
+ * summary, touchedPaths}) for human/irreversible_ask gates. listPending returns the queue severity-sorted
110
+ * (DESC) so the supervisor inbox triages high-risk asks first. `null` for gates with no descriptor. */
111
+ riskDescriptor: RiskDescriptor | null;
112
+ /**
113
+ * [ref]-2 / [ref]② 起役;core 5.58.0([ref])换 OFFER 判别联合形:这条 park 的「不再询问」
114
+ * offers —— core 在 mint 这道门时挂到 `pendingAction.ruleOffers` 上的那一份(core
115
+ * `checkpoint-store.d.ts` 明写与同步腿的 `AskRequest.ruleOffers` 是**同一条契约**:序即契约——
116
+ * whole-string exact `single` 在场时恒 index 0、`batch` 恒末位,core 契约基数 ≤2 —— server 侧执法帽
117
+ * 是 `MAX_RULE_OFFERS`=4 的容忍余量,两个数的分工见该常量顶注)。
118
+ *
119
+ * 🔴 **本腿的下标不是 core 的 offer index,别把它当选择键**(5.58 复审 F-3,与契约文同批改准)。
120
+ * core 的契约原话是:丢掉不认识的成员时**必须保住每个留下来成员的原始 wire 下标**,做不到的消费端
121
+ * 「must suppress its persistence actions entirely (fail toward asking)」。本腿走的是**后者** ——
122
+ * {@link boundedRuleOffers} 对形不合的 offer 是**逐条丢弃并压紧**的(一条坏 offer 不该让另一条真 offer
123
+ * 消失,这条不变量本身有它自己的红格),压紧就意味着中间剔除会让后面的下标整体前移。之所以站得住,
124
+ * 是因为**本腿没有兑付口**:durable 回决体(`/v1/approvals/:id/decide`)不带规则位,选择键一次都没有
125
+ * 用到本腿的下标。⇒ **硬条款**:哪天 durable 腿真要开兑付位,先把「原始下标随行」显式加进本投影
126
+ * (或按 core 的另一半整格不投),而不是默默拿压紧后的下标当选择键。
127
+ * 与**同步腿**刻意分家:`buildRuleLaneMaterial` 那侧是**纯前缀截、零逐条丢弃**,所以活卡帧 /
128
+ * `card_json` / 重放帧上的下标**恒等于** core 的 offer index —— 兑付真发生在那条腿上。
129
+ *
130
+ * 🔴 **混舰队旧行(pre-5.58 的 `ruleSuggestions` 数组,无 `kind` 判别位)**:按新 Raw 形逐条不合
131
+ * ⇒ 整格落「无供给」——与 [ref] 升级单的舰队条款一致(旧建议面丢失只影响展示,ask 安全面照常;
132
+ * pending 审批短命,重触发即得新形卡)。**不做兼容读**(schema stale=重问,core 同款纪律)。
133
+ *
134
+ * 🔴 **无供给 ≠ 丢失**(本键存在的全部理由):同步腿的卡带这一格(`card_json` 里逐字在),而耐久腿
135
+ * (park 行 → `GET /v1/approvals`)此前**根本没投**,于是同一张卡走 durable 路时「不再询问」那一格
136
+ * 恒空 —— 消费端只能读成「本部署不供候选」。本键把 core 真给的那份带上 wire,缺席从此是**真的**没有
137
+ * 候选(规则车道没武装 / 命令说不出规则 / 这只 ask 规则清不掉),而不是投影层漏了。
138
+ *
139
+ * 🔴 **只在为真时在场**:core 不给 ⇒ 键**缺席**(不铸 `null`、不铸 `[]`)——与 `boundCallId`/`gateKind`
140
+ * 同一条 wire 纪律([ref]②「absent nullable field is OMITTED, not null」)。存量行(本列存在之前
141
+ * park 的)读回同样是缺席,与「这只 ask 没有候选」在消费端读法一致:本卡无此选项。
142
+ *
143
+ * 🔴 **展示用,永不参与 resume**:core 侧同款定性(「never read by the resume path, and never a rule by
144
+ * itself」)。本仓耐久腿今天也没有兑付口(`/v1/approvals/:id/decide` 的体不带规则位),所以它是**分诊
145
+ * 材料**不是承诺 —— 消费端渲这一格时要按「同副本活卡腿才兑得动」读(同 `ApprovalCardSchema.ruleOffers`
146
+ * 顶注那条射程说明)。
147
+ */
148
+ ruleOffers?: readonly RuleOfferProjection[];
149
+ /**
150
+ * [ref]①([ref]② / [ref];core 5.60.0 [ref]):**bidi 在场位**的耐久运维队列投影 —— 这条 park 行的
151
+ * 执行载荷里含至少一个 DIRECTIONAL 格式控制符(人眼读到的顺序与真正执行的字节顺序可能不同)。
152
+ *
153
+ * 值**完全由 core 供**:park mint 时 core 写在 `pendingAction`(tool_approval 臂)上的那一位
154
+ * (`PendingAction.hasBidiControls`,core [ref]),`put()` 在铸行
155
+ * 这一刻反范式成 `checkpoint.has_bidi_controls` 列(同行 `tool_input` / `bound_input_hash` / `gate_kind` /
156
+ * `risk_descriptor` / `rule_suggestions` 五列同一条「铸行时一格、读口零 blob」先例),server 不重算。
157
+ * ([ref] 起本读口**确实**逐行读 blob —— 只为 {@link ParkRowFacts} 那几位,且是按「本仓不发 ALTER TABLE、
158
+ * 加列 = 删库丢在飞 park」裁出来的代价,见 `listPending` 那一段;本位与五列的「铸行时一格」形不变。)
159
+ *
160
+ * 🔴 `true` **或缺席,恒不写 `false`/`null`**(core d.ts 的 OMIT 契约逐字点名 BFF:「never null, never
161
+ * present-but-undefined, never false」):缺席 = **没检出**(干净 / core 的有界扫描没够着 / 本列诞生前
162
+ * park 的老行),消费端**禁**读成「已确认干净」。与 `/v1/assistant/inbox` 那面的同名键同一份语义
163
+ * (那面由 `summarizeCheckpoint` 对老行做投影期回填;本面读列不读 blob,老行恒缺席 —— 两面的缺席
164
+ * 集合因此不完全相等,但两面对「缺席」的读法逐字相同)。同步腿(`tool_approval` 帧 / `approval_request`
165
+ * 卡)的对应键是 `inputHasBidi`(E-14),名字刻意不同:那一位是 server 对帧自身序列化后的 args 算的。
166
+ *
167
+ * 展示/分诊用,永不参与 resume / gate / CAS(core 同款 ECHO-ONLY 定性)。
168
+ */
169
+ hasBidiControls?: true;
170
+ /**
171
+ * core 7.49.0 [ref]:**这一行渲在哪张卡上** —— 门上引擎铸的 `gate.approvalCard`(闭集 core `TOOL_APPROVAL_CARDS`;`question` = 问用户的
172
+ * 问题,不是副作用工具),即 core `summarizeCheckpoint` 投给 `CheckpointSummary.approvalCard` 的同一个词。读法单点 = `approval-content-kind.ts`
173
+ * 的 `parkedApprovalCard`(只在审批门型上读、闭集窄读;两只店同调它:SQL 孪生读 `checkpoint` 列的 `gate` 子树,LOCAL 孪生读 blob 上的门)。
174
+ * 🔴 缺席即缺席([ref]② OMIT 纪律):非审批门、7.49.0 之前停驻的存量行(门上无卡 —— core 的读法「无卡 = 不是一问」,本仓**不**从
175
+ * 工具名回推)。两条 durable 读面(`GET /v1/approvals` 行 + `/stream` `pending` 帧)经同一投影同时携带;live 腿的同名键在
176
+ * `LivePendingRow.approvalCard`。展示/分诊用,永不参与 resume / gate / CAS。(此前此处是按 `tool_name` 列派生的 `contentKind`,随 [ref] 删。)
177
+ */
178
+ approvalCard?: ToolApprovalCard;
179
+ /**
180
+ * 这一行**本引擎续不续得了** —— core 判官 `judgeCheckpointRow` 对这一行盘上字节的判决(core 7.62.0 [ref];与助手收件箱行的 `rowVerdict`、
181
+ * 续跑在第一级读到的拒同一个读者、同一答),逐字投、本服务不重算不筛。判官要整行:SQL 孪生按 (库内算的 `checkpoint` 文本 sha256, 引擎构建)
182
+ * 记忆({@link StoredRowProjectionMemo}),未命中才按令牌批读整行;LOCAL 孪生取 core 店摘要上的同一位。
183
+ * 盘上字节读不成一只检查点的行(体是 `{}`、缺 `state`、JSON 字面 `null` / 标量 / 数组 / 非 JSON)同样判:判官对任何值都答,这种行答 `damaged`,
184
+ * 行照列(计 `server.approvals.park-row-blob-unreadable`)—— 一行坏字节只坏它自己那一行,见 {@link SqlCheckpointStore.readStoredRow}。
185
+ * **在场 ⇔ 这一次列表判过这一行**:缺席只在 SQL 孪生上出现 —— 两次读之间行已离开待决,或两次读之间行的字节变了(判过的是另一份,
186
+ * 不拼到这一行上;下一次列表即带)。缺席**不是** `ok`。非 `ok` 的行照列(人要看到它停在哪)。
187
+ */
188
+ rowVerdict?: CheckpointRowVerdict;
189
+ }
190
+ /**
191
+ * [ref]([ref] / DEBTS [ref]):session 键读口(`findPendingTokenBySession` / `peekPendingScope`)的**行绑定**。
192
+ *
193
+ * 病灶(本仓自己两处成文:`server.ts` mintFreshRun 腿 409 段、`active-run-conflict.ts` `checkpointId` 顶注):
194
+ * `checkpoint` 表**没有** per-session pending 唯一约束 —— 同一 session 下 `task_done` 纯 park(行已终局、
195
+ * `task_active` claim 已释放)与下一条 run 的门 park **合法并存**;而两条读口此前是**无序 `LIMIT 1`**,同
196
+ * session 两条 pending 时选中哪一条由存储引擎的扫描序决定:同一情形两次调用可能答出两张不同的门,路由层
197
+ * 的 owner 门(`peekPendingScope`)与 `resumeCheckpoint` 的载入(`findPendingTokenBySession`)更可能各读到
198
+ * 一条([ref] codex R3 揭的 tokenless TOCTOU 同源;[ref] 在取用点加了行级属主复核 = 下游兜底,本件是源头)。
199
+ *
200
+ * 为什么不是加唯一约束:那两条行的并存是**设计内**的形(见上),约束会把新 run 的 park `put()` 当场打死
201
+ * (core 的 `CheckpointStore.put` 契约不允许);且 MySQL-protocol 侧没有部分唯一索引,TiDB 上要靠生成列
202
+ * 绕,两方言分歧成本高于收益。
203
+ *
204
+ * 修法 = **读口带 gate 坐标 + 显式序**:
205
+ * · `gateKinds` 在场 ⇒ 只认这些门型的 pending 行(每条 resume 腿本来就只服务一种门型 —— decide 只解
206
+ * 审批门、wake 只解 `task_done`、preempt 只解 `resource_limit`、plan_review 只解 `plan_review`;把
207
+ * 这条已成文的守卫**前移到读口**,选错行这件事结构上不再发生)。**空数组 = 什么都不认**(显式 `1=0`,
208
+ * 不静默退化成「任意门型」)。缺席 ⇒ 任意门型(存在性读口:boot reclaim / reaper probe / 断连竞态卫兵)。
209
+ * · 无论绑不绑,序恒为 `created_at_ms DESC, token DESC`(**最新优先**,主键兜平局):同一情形下每次读
210
+ * 都答同一行;两条读口共用同一段谓词 + 同一段序({@link SqlCheckpointStore.pendingRowQuery} 单一属主),
211
+ * 路由门与载入点结构上选中同一行。最新优先的理由:并存形里**老**的那条恒是终局后的 `task_done` 纯 park,
212
+ * 活 run 的门在它之后铸。
213
+ *
214
+ * SQL twin 与 LOCAL twin 同判(local 侧按 `cp.gate.kind` 过滤、按 `createdAt`/token 同序取最新)。
215
+ */
216
+ export interface PendingRowBinding {
217
+ /** gate 坐标:只认这些门型的 pending 行(core `CheckpointGate["kind"]` 闭集)。缺席 ⇒ 任意门型。 */
218
+ gateKinds?: readonly CheckpointGate["kind"][];
219
+ /**
220
+ * 并存候选里取哪一端(缺省 `"newest"`)。两个取向各有一个**结构性**的消费者,别混用:
221
+ * · `"newest"`(缺省)—— 「活 run 的门」族:并存形里老的那条恒是终局 task_done 纯 park,活 run 的门
222
+ * 在它之后铸 ⇒ 最新即正解;
223
+ * · `"oldest"` —— **跨副本收敛**族(wake-mint 赢家复核 + wake 消费腿;codex 审批面轮 R2-[medium],
224
+ * 验真后修):收敛要的是一个**与自己的铸行时刻无关**的不动点。最新优先下「后铸者恒自选」——两副本
225
+ * 并发铸 task_done park 时,后铸的 B 复核必选中刚铸的自己,先铸的 A 若在 B 落盘前复核也选中自己,
226
+ * 两行双双留存、消息被拆到两张卡上。最老优先没有这个洞:**后铸者复核时先铸行必然已在**(它比自己老
227
+ * ⇒ 选中它、expire 自己),先铸者无论何时复核都选中自己 ⇒ 不动点唯一。消费腿(/wake)取同端,
228
+ * 赢家卡与被消费卡恒同一张(FIFO 顺带成立)。
229
+ */
230
+ pick?: "newest" | "oldest";
231
+ }
232
+ /** [ref]:一条**仍持有 claim 的 run** 可能停在的全部门型(= 除 `task_done` 外全部;派生自上表,不手抄)。
233
+ * 给「按 run 行动」的读口用:cancel / steer 的 park 落点、409 `pendingGate` 材料 —— 这些腿手里有 run 行,
234
+ * 而 run 行一旦 parked 就绝不是 `task_done`,绑上它就把同 session 那条终局 park 排除在候选之外。 */
235
+ export declare const LIVE_RUN_GATE_KINDS: readonly CheckpointGate["kind"][];
236
+ /** [ref]:终局后纯 park 的门型(wake 腿 / wake-mint 赢家复核只认它)。 */
237
+ export declare const TERMINAL_PARK_GATE_KINDS: readonly CheckpointGate["kind"][];
238
+ /**
239
+ * [ref]:LOCAL twin 与测试共用的**同序判据**——「候选集里哪一条是读口该答的那条」。与 SQL 的
240
+ * `ORDER BY created_at_ms {DESC|ASC}, token {DESC|ASC} LIMIT 1` 逐字同义(token 列两方言都是二进制序 ——
241
+ * MySQL `utf8mb4_bin` / PG `COLLATE "C"`,token 是 ASCII ⇒ JS 字符串比较与之同序)。
242
+ * 返回 `true` 表示 `a` 应排在 `b` 前面(缺省最新优先;`pick:"oldest"` 时最老优先,取向语义见
243
+ * {@link PendingRowBinding.pick})。
244
+ */
245
+ export declare function pendingRowPrecedes(a: {
246
+ createdAt: number;
247
+ token: string;
248
+ }, b: {
249
+ createdAt: number;
250
+ token: string;
251
+ }, pick?: "newest" | "oldest"): boolean;
252
+ /**
253
+ * `pendingAction.ruleOffers` 的**铸行侧**投影(mint 面,与 {@link boundedToolInput} 同位同待遇)。
254
+ *
255
+ * 四件事,一件都不能少:
256
+ * 1. **读**(宪法 [ref] 边界必判形 / 禁裸 as-cast):blob 里的这一格是 `unknown`(旧 core / 坏行 / 未来加字段都可能)。读者只有一只 =
257
+ * core `readRuleOffers`(经 `approval-card.ts` 的 {@link readRuleOfferList};core 7.55.0 [ref] P1 阶段二起,修前本仓自己一份 zod 形):读不出的**整条丢弃**
258
+ * (不是整只丢弃)——一条坏 offer 不该让另一条真 offer 消失;多余键逐级剥、不拒收(上游 additive 加一键是 core 的常规做法,拒收会把「有供给」静默变成「无供给」);
259
+ * 丢弃**响亮**(`server.rule-offers.offer-dropped`,修前这一腿静默)。
260
+ * 2. **脱敏**:offer 文本是从**命令原文**铸出来的(`Bash(curl -H "Authorization: Bearer …")` 这种 exact
261
+ * 形拼得出秘密),而 `GET /v1/approvals` 是**跨租户可见**的运维队列。落列前过 `redactSecrets` ——
262
+ * 与同行 `tool_input` 在 mint 就 `redactDeep` 是**同一条**纪律(秘密从此连**落库**都不落,不是落了库再在两条读面各洗一遍)。
263
+ * 3. **脱敏之后**才截长(codex 交叉复审 [medium],验真后修):`redactSecrets` 会**变长**,先按限长校验
264
+ * 原文再脱敏会产出超限文本,而 SQL 腿有**回读**(列 → 本函数再跑一遍)、LOCAL 腿没有 ⇒ 同一份素材
265
+ * 两个后端给出不同卡面。顺序反过来之后本函数对自己的输出**幂等**,两条腿同形。
266
+ * 4. **限基数**:core 契约今天 ≤2,这里按 {@link MAX_RULE_OFFERS}(=4,server 侧容忍帽,两个数的
267
+ * 分工逐字见该常量顶注)截;`batch.rules` 成员另按 {@link MAX_RULE_OFFER_BATCH_MEMBERS}(=8)截 ——
268
+ * 一条被改坏/未来放宽的上游不该把运维队列的一行撑爆。两个截都是**前缀截**(保序)。
269
+ * ⚠️ **但本函数整体不保原始下标**:第 1 条的「坏的逐条丢」会压紧数组 ⇒ 中间剔除后下标前移。
270
+ * 本腿因此是**纯展示腿**,下标不得当选择键 —— 硬条款与它站得住的理由逐字见
271
+ * {@link PendingCheckpoint.ruleOffers} 顶注(同步腿相反:那侧零逐条丢弃,下标恒等于 core 的 offer index)。
272
+ * 🔴 **成员帽扣在读之前**(5.58 复审 F-2):见 {@link preboundBatchMembers} —— offer 条数那一维由
273
+ * {@link MAX_RULE_OFFER_SCAN} 管,成员那一维在那之前是**无界**的。
274
+ * 🔴 **扣预算在读出之后**(合并码重扫):旧序先 `slice` 再判形,于是 ≥MAX 条坏 offer 能把唯一的真
275
+ * offer 挤出预算,读数是 `undefined` —— 与上面第 1 条自证的不变量(坏的丢、好的照投)正相反。扫描窗
276
+ * {@link MAX_RULE_OFFER_SCAN} 仍是有界的:一条被改坏成上万条的行不该让每 3s 一拍的读路径去逐条判形。
277
+ *
278
+ * 🔴 `opts.countRedactions`:**两条腿都照常脱敏,只有回读腿不计数**(合并码重扫;codex 复审第一轮
279
+ * 指出「回读不脱敏」会把纵深防御一起关掉,验真后按这条收)。`redactSecrets` 对自己的输出是**值幂等、
280
+ * 计数不幂等**(URL-userinfo / JSON 秘密字段两条 pattern 会命中自己的输出并再计一次),而回读腿正是
281
+ * `/v1/approvals/stream` 每 3s 一拍的热路径 ⇒ 每条订阅每行每拍给 `redactions_applied_total` +1,把那个
282
+ * 用来识别「过度脱敏 / pattern 从不触发」的指标灌成不可读。关掉的是**计数**:重扫仍在,手改过的行 /
283
+ * 将来某条写路径漏了脱敏,读面照样兜住(运维队列跨租户可见,少脱一次的代价永远不能省)。
284
+ *
285
+ * 返回 `undefined` = **无供给**(键不落列):core 没给、给了空数组、或整只读不出。
286
+ */
287
+ export declare function boundedRuleOffers(raw: unknown, opts?: {
288
+ countRedactions?: boolean;
289
+ }): RuleOfferProjection[] | undefined;
290
+ /**
291
+ * {@link SqlCheckpointStore.get} 读到的 `checkpoint` 列字节读不成一只检查点(JSON 字面 `null` / 标量 / 数组 / 非 JSON)时抛的拒 —— 就是店的拒绝契约类
292
+ * {@link StoreCorruptError}(`kind:"corrupt"` + 坐标,所有认它的读口原样认它),外加**引擎判官对这一格原样字节的判决**:列表面对同一格答的就是它
293
+ * ({@link verdictOfUnreadableBody},`rowVerdict`),所以决断口("行自己拒")说的出路句取这一只判决的 `sentence`、不另铸(决断口 {@link rowRefusalMessageOf})。
294
+ * 判决在抛的这一拍现算(纯函数、全函数,不读钟不读库);店自己的诊断(`message`:坐标 + 体的问题)原样保留。
295
+ */
296
+ export declare class UnreadableCheckpointRowError extends StoreCorruptError {
297
+ /** 判官对这份字节的判决(读不成检查点的字节判官恒答 `damaged`;类型面不替它断言)。 */
298
+ readonly rowVerdict: CheckpointRowVerdict;
299
+ constructor(corrupt: StoreCorruptError, rowVerdict: CheckpointRowVerdict);
300
+ }
301
+ /**
302
+ * [ref] —— 运维队列行的门型:**两只孪生同一只判官**(SQL 孪生读 `gate_kind` 列 —— put 时从同一只门反范式下来的那一格,每次停驻都盖,一次性迁移 M21 把没有它的
303
+ * 存量行从门子树补齐;local 孪生读快照上的门 `cp.gate.kind`)。
304
+ * 读到的值是 NULL / 缺席 / 不是 core 登记的门型 ⇒ `undefined`:这一行**不列**(响亮:计一次 `server.approvals.queue-row-gate-kind-missing`)—— 与每一条决断口同一个读法:
305
+ * 按门型绑定取行的读口对这种行本来就不命中(`pendingRowQuery` 的 `gate_kind IN (…)`,证不出门型就不当它是这道门),队列不再把一行谁也决不了的行投出去,
306
+ * 也不按缺键投(消费方按 `gateKind` 路由、缺键即 fail-closed)。不整只拒:一行坏行不许让整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧)消失。
307
+ * 按构造不可达(写口恒盖、M21 补齐);计数非零 = 写腿或手改出了错,按 `SELECT … WHERE status = 'pending' AND gate_kind IS NULL` 查那一行。
308
+ */
309
+ export declare function queueRowGateKind(kind: unknown): CheckpointGate["kind"] | undefined;
310
+ /** Redact + size-bound a pending tool call's args for persistence/serving. JSON-roundtrips so the stored shape
311
+ * matches what the operator queue serves; oversized payloads collapse to a marker rather than bloating the row.
312
+ * Exported for the LOCAL twin (local-checkpoint-store.ts) so the operator-queue projection can't drift. */
313
+ export declare function boundedToolInput(args: unknown): unknown;
314
+ /**
315
+ * 🔴 [ref] 车5 §9 C4:`findCheckpointCandidatesForAsk` 的 typed 候选行。刻意只有对账收敛器判据 1 真需要的
316
+ * 五件——**没有 token 之外的任何凭据面**,`token` 本身是 resume 凭据、只在服务端内部流转(同
317
+ * `findPendingTokenBySession`:它从不出网,§12-C「checkpointToken 从不外发」)。
318
+ */
319
+ export interface CheckpointAskCandidate {
320
+ token: string;
321
+ /** `checkpoint.status` 列(权威;`pending` | `resolved` | `expired`)。不做过滤,由收敛器判活性。 */
322
+ status: string;
323
+ createdAtMs: number;
324
+ /**
325
+ * 🔴 和解三元组的第一维([ref] 件1,黑板 [ref]③①)—— checkpoint 自己带的 `sourceSessionId`(core 在
326
+ * suspendAsk 铸行时恒填该腿的 `sessionId`)。**没有对应的列**,只能从 blob 窄读;读不出/缺席 = null。
327
+ *
328
+ * 为什么必须进候选集:读口按 `(scope, session_id, tool_call_id)` 查,而委派子代那条腿的 ask 落行时
329
+ * `session_id` 记的是**投递上下文**(根会话),park 却发生在**子代自己**的 sessionId 上 —— 只靠
330
+ * (toolCallId, hash) 两维,一条根腿的 park 会被一只子代的 ask 认领(摘要相同的两次同 args 调用完全
331
+ * 正常)。三元组全同才算同一件事。
332
+ */
333
+ sourceSessionId: string | null;
334
+ /** 该 park 绑定的 tool call(= `pendingAction.toolCallId`)。`unparseable` 行为 null。 */
335
+ boundCallId: string | null;
336
+ /** 服务端铸的入参摘要——判据 1 的第二道硬等式(与 `approval_ask.bound_input_hash` 相等才算命中)。 */
337
+ boundInputHash: string | null;
338
+ /** 在场 ⇒ 这行读不出(blob 版本超前 / JSON 坏)。收敛器视同不匹配;**从不是** false,缺席即可读。 */
339
+ unparseable?: true;
340
+ }
341
+ /**
342
+ * [ref] 件1 —— legacy `/v1/approvals/:sessionId/decide` 腿的**幂等回放**判别行(窄读,非凭据面)。
343
+ *
344
+ * 刻意**不带 token**:回放只需要「这条已决的行归谁、绑的是哪一次调用、判词是什么」三件,而 token 是
345
+ * resume 凭据(§12-C「checkpointToken 从不外发」;`CheckpointAskCandidate` 带它是因为收敛器要拿它去
346
+ * `bindBatch`,本口没有那个用途 ⇒ 不给)。
347
+ */
348
+ export interface DecidedApprovalRecord {
349
+ /** 行的属主 scope 列(`encodeCheckpointScope` 的产物:principal 或匿名哨兵 `"_"`)。回放属主门的输入。 */
350
+ scope: string;
351
+ /** 行上的 D-1 入参摘要(`bound_input_hash` 列);pre-D-1 行为 null。 */
352
+ boundInputHash: string | null;
353
+ /**
354
+ * 这条已决行的**工具审批判词**,或 `null` = **判别不出**(codex R1-[medium] 四的收敛形):
355
+ * outcome 读不出 / 没有 winner / winner 绑的是**另一个** callId / 判词是 REVIEW 门那三个词。
356
+ *
357
+ * 🔴 `null` **不是**「无所谓」:调用方必须把它当「这条命中的行我读不懂」⇒ **整次回放拒绝**。
358
+ * 一条读不懂的已决行与一条判词相反的已决行在风险上同级 —— 都意味着这只 callId 上的持久事实
359
+ * 不是本次回放能如实复述的。
360
+ */
361
+ decision: "approve" | "deny" | null;
362
+ /**
363
+ * 判词**之外**的决议载荷指纹(codex R2-[high] 一,验真后补):`updatedInput` / `answer` / `reason`
364
+ * 三件都在 core 的持久 winner 里,而**同判词不等于同决议** —— 「approve + 编辑后的实参 A」与
365
+ * 「approve + 实参 B」是两次不同的放行,只比判词就会把后者谎报成「你的请求已被处理」(而 B 从未执行)。
366
+ * 调用方按**同一份请求重发**的语义要求它与本次请求的同三件逐字相等。
367
+ * `null` = 算不出(载荷序列化不了)⇒ 与 `decision: null` 同判:整次拒绝。
368
+ * 铸法见 {@link approvalPayloadFingerprint}(两条车道共用同一只,免得指纹口径漂)。
369
+ */
370
+ payloadFp: string | null;
371
+ /**
372
+ * [ref](core 7.7.0 [ref])—— 这条已决行上**已决动作最后被怎么处置**的结算记录,`null` = **未知**。
373
+ *
374
+ * 🔴 缺席只有一个含义:未知,**绝不是**「放行过」。`decision` 说的是决定(人批了没有),这一格说的是
375
+ * 那次决定在 resume 腿上执行时的处置(部署自己的复核可能在 post-CAS 把人批过的、改过参数的调用否掉,
376
+ * 于是 `decision: "approve"` 与 denied/policy 的处置**同时**在一行上成立)。[ref] 的病正是「流断之后
377
+ * 从行重建这条 run 的消费者只读得到前半句」——decide 幂等回放正是那种消费者。
378
+ *
379
+ * 铸法 = core 的 {@link executionVerdict} **单口**(两条车道共用),不许任何读点自己判列的在场性。
380
+ */
381
+ executionOutcome: GateOutcome | null;
382
+ }
383
+ /**
384
+ * [ref] 件1 —— {@link SqlCheckpointStore.findDecidedApprovalsForBinding} 的扫描上界。
385
+ *
386
+ * 🔴 它**同时是判据**,不只是一道 LIMIT:调用方按「命中集里每条判词都一致才回放」判,而一次被截断的
387
+ * 集合可能把分歧藏在第 N+1 行 ⇒ 消费点约定「**回满 = 判别不出**」,整次拒绝回放(回落 404)。现网正常形
388
+ * 恒 0 或 1 行,回满只可能是数据异常。
389
+ */
390
+ export declare const DECIDED_REPLAY_SCAN_CAP = 8;
391
+ /**
392
+ * [ref] 件1 —— core 的 `ResolvedOutcome` → 本腿(工具审批 decide)的二值词表。**闭集穷举**([ref]):
393
+ * core 哪天给 `ResolvedOutcome.decision` 加员,下面的 `never` 当场编译期红,而不是让一个新判词被
394
+ * 静默折成 `deny` 或漏成「没有决议」。
395
+ *
396
+ * 三条判据,缺一即 `null`(= 不回放,调用方逐字回落修前 404 —— **绝不**把 not-found 洗成成功):
397
+ * ① 有 winner —— `resource_limit` / `wake` / `task_done` 的 resolve 不记 winner(core 契约),那些行
398
+ * 不是「一次人给的工具审批」,没有可回放的判词;
399
+ * ② winner 绑的正是这次请求回显的那个 `boundCallId` —— 身份第二锚(列谓词是第一锚),两锚同意才算同一件事;
400
+ * ③ 判词属于**工具审批**那两个词(`allow`/`deny`)—— `approve`/`reject`/`edit` 是 REVIEW 门
401
+ * (plan_review / dry_run_review)的判词,那些门的决议入口不是本腿,回放它们等于跨门作答。
402
+ */
403
+ export declare function approvalDecisionOfWinner(winner: ResolvedOutcome | undefined, boundCallId: string): "approve" | "deny" | null;
404
+ /** 收敛器读口对一行的身份判词(三态 + 读不出;读法见 {@link pendingActionToolCallId} / {@link candidateIdentityOf})。 */
405
+ export type CandidateIdentity = {
406
+ readable: true;
407
+ toolCallId: string | null;
408
+ boundInputHash: string | null;
409
+ kind: string | null;
410
+ sourceSessionId: string | null;
411
+ } | {
412
+ readable: false;
413
+ };
414
+ /**
415
+ * 收敛器读口的**身份判词**(两只孪生同一只;`findCheckpointCandidatesForAsk` 的 SQL / LOCAL 两形都只经这里):先问 core 判官 `judgeCheckpointRow`
416
+ * (core 7.62.0 [ref],[ref])—— 判 `lacking`(更新的引擎能续它、本引擎缺读法:格式版本超上限,或要求表点名了本引擎不认的读法词)⇒ `{ readable: false }`:
417
+ * 本副本读不准这一行,交给收敛器当「不确定」,留给能续它的副本去绑(修前 SQL 孪生按 `version` 列比 `MAX_SUPPORTED_CHECKPOINT_VERSION`,
418
+ * 射程只有版本一轴,LOCAL 孪生没有这一臂)。`damaged` **不是**这一臂:本引擎不续这些字节(别的副本续不续,引擎没有说、本服务不替它们断言),身份读得出就照常判 —— 绑上之后续跑由判官给那句固定的拒,
419
+ * 比让 ask 停在 PARKING 诚实。其余三态见 {@link pendingActionToolCallId}。
420
+ */
421
+ export declare function candidateIdentityOf(cp: Checkpoint): CandidateIdentity;
422
+ /** 认领栅栏下改写一行 ctx 的结局(闭集;SQL / local 两孪生同词):`written` 写了;`claim_released` 接下改写的那条腿此刻已不持有这条 run(认领放了 / 不在跑 / 腿判据答否)⇒ 零写;
423
+ * `context_absent` 持有、但会话没有 ctx 行 ⇒ 零写(不建行)。 */
424
+ export declare const CTX_CLAIM_REWRITES: readonly ["written", "claim_released", "context_absent"];
425
+ export type CtxClaimRewrite = (typeof CTX_CLAIM_REWRITES)[number];
426
+ //# sourceMappingURL=checkpoint-store-face.d.ts.map