@sema-agent/server 7.151.0 → 7.152.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 (286) hide show
  1. package/MIGRATION.md +266 -0
  2. package/README.md +15 -3
  3. package/README.zh-CN.md +10 -2
  4. package/USAGE.md +93 -64
  5. package/deploy/sema-up/README.md +5 -4
  6. package/deploy/sema-up/cluster-up.sh +6 -6
  7. package/deploy/sema-up/kube-up.sh +5 -5
  8. package/deploy/sema-up/lib/flagshape.sh +36 -8
  9. package/deploy/sema-up/machines.example.yaml +3 -1
  10. package/deploy/sema-up/sema-up.sh +22 -16
  11. package/dist/approval-ask-audit-store.d.ts +1 -1
  12. package/dist/approval-ask-machine.d.ts +4 -10
  13. package/dist/approval-ask-retention.d.ts +1 -1
  14. package/dist/approval-card.d.ts +10 -12
  15. package/dist/approval-gate-currency.d.ts +8 -8
  16. package/dist/approval-gate-currency.js +3 -4
  17. package/dist/approval-hmac.d.ts +17 -1
  18. package/dist/approval-hmac.js +7 -1
  19. package/dist/approval-queue-kind.d.ts +1 -1
  20. package/dist/approval.d.ts +2 -2
  21. package/dist/approval.js +2 -2
  22. package/dist/bind-host.d.ts +1 -3
  23. package/dist/bind-host.js +2 -3
  24. package/dist/boot/coordinators.d.ts +1 -1
  25. package/dist/boot/coordinators.js +9 -10
  26. package/dist/boot/device-lane.js +2 -0
  27. package/dist/boot/execution-env.d.ts +1 -0
  28. package/dist/boot/execution-env.js +31 -5
  29. package/dist/boot/leader.d.ts +31 -30
  30. package/dist/boot/leader.js +38 -28
  31. package/dist/boot/leg-path-adjudication.d.ts +40 -0
  32. package/dist/boot/leg-path-adjudication.js +18 -0
  33. package/dist/boot/parked-revive-gate.js +3 -15
  34. package/dist/boot/reapers.d.ts +1 -1
  35. package/dist/boot/reapers.js +1 -1
  36. package/dist/boot/resolve-spec.js +12 -26
  37. package/dist/boot/runner-deps.d.ts +39 -35
  38. package/dist/boot/runner-deps.js +92 -89
  39. package/dist/boot/shutdown.d.ts +6 -0
  40. package/dist/boot/shutdown.js +10 -4
  41. package/dist/boot/stage-02-web-admin-pull.js +4 -5
  42. package/dist/boot/stage-05-execution-env.js +2 -1
  43. package/dist/boot/stage-06-runners.d.ts +2 -0
  44. package/dist/boot/stage-06-runners.js +10 -4
  45. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  46. package/dist/boot/stage-07-capability-layer.js +1 -1
  47. package/dist/boot/stage-08-reapers.d.ts +1 -0
  48. package/dist/boot/stage-09-leader.d.ts +1 -0
  49. package/dist/boot/stage-09-leader.js +2 -2
  50. package/dist/boot/stage-10-http-server.d.ts +8 -0
  51. package/dist/boot/stage-10-http-server.js +11 -15
  52. package/dist/boot/stores.js +7 -4
  53. package/dist/boot/web-admin.js +2 -7
  54. package/dist/capabilities/approvals-push.d.ts +3 -3
  55. package/dist/capabilities/scenarios.d.ts +28 -13
  56. package/dist/capabilities/scenarios.js +2 -2
  57. package/dist/capability-facts.d.ts +22 -20
  58. package/dist/capability-facts.js +18 -34
  59. package/dist/card-rule-offers.d.ts +32 -0
  60. package/dist/card-rule-offers.js +130 -0
  61. package/dist/config-catalog.js +12 -11
  62. package/dist/config-dir-trust.js +4 -2
  63. package/dist/config-invariants.d.ts +18 -7
  64. package/dist/config-invariants.js +51 -8
  65. package/dist/config-lkg.d.ts +5 -19
  66. package/dist/config-lkg.js +8 -62
  67. package/dist/config-provider.d.ts +1 -1
  68. package/dist/config-root-migration.d.ts +2 -2
  69. package/dist/config-types.d.ts +28 -38
  70. package/dist/config.d.ts +7 -5
  71. package/dist/config.js +55 -16
  72. package/dist/control-socket.d.ts +51 -0
  73. package/dist/control-socket.js +115 -0
  74. package/dist/deployment-governance.d.ts +2 -1
  75. package/dist/device-ws-hub.d.ts +7 -0
  76. package/dist/device-ws-hub.js +7 -1
  77. package/dist/elicitation.js +2 -2
  78. package/dist/entry-problems.d.ts +40 -3
  79. package/dist/entry-problems.js +3 -1
  80. package/dist/execution-env-refusal.d.ts +8 -2
  81. package/dist/execution-env-refusal.js +3 -0
  82. package/dist/fleet/engine-signal.d.ts +12 -29
  83. package/dist/fleet/engine-signal.js +1 -7
  84. package/dist/fleet-client.js +2 -5
  85. package/dist/gate-settlement.d.ts +1 -8
  86. package/dist/gate-settlement.js +0 -4
  87. package/dist/history-anchor.js +1 -1
  88. package/dist/hitl-delivery.d.ts +2 -3
  89. package/dist/hitl-delivery.js +0 -2
  90. package/dist/hooks/hook-llm.js +4 -2
  91. package/dist/hooks/hook-runner.d.ts +3 -2
  92. package/dist/hooks/hook-runner.js +16 -1
  93. package/dist/host-decision.d.ts +11 -11
  94. package/dist/http/active-run-conflict.d.ts +35 -2
  95. package/dist/http/active-run-conflict.js +48 -19
  96. package/dist/http/approval-sweeps.d.ts +1 -1
  97. package/dist/http/approval-sweeps.js +5 -2
  98. package/dist/http/checkpoint-ctx.d.ts +2 -0
  99. package/dist/http/checkpoint-ctx.js +1 -0
  100. package/dist/http/dispatch.d.ts +6 -2
  101. package/dist/http/dispatch.js +25 -5
  102. package/dist/http/durable-run-leg.js +0 -1
  103. package/dist/http/health-reader.d.ts +16 -9
  104. package/dist/http/health-reader.js +2 -1
  105. package/dist/http/health-self-report.js +1 -1
  106. package/dist/http/owner-access.d.ts +2 -3
  107. package/dist/http/owner-access.js +3 -4
  108. package/dist/http/principal-gate.d.ts +49 -21
  109. package/dist/http/principal-gate.js +61 -11
  110. package/dist/http/request-channel.d.ts +23 -0
  111. package/dist/http/request-channel.js +9 -0
  112. package/dist/http/request-guards.js +17 -2
  113. package/dist/http/resume-ctx-read.js +20 -3
  114. package/dist/http/resume-legs.d.ts +2 -2
  115. package/dist/http/resume-legs.js +111 -41
  116. package/dist/http/route-ctx.d.ts +73 -7
  117. package/dist/http/route-ctx.js +17 -0
  118. package/dist/http/route-table.d.ts +4 -0
  119. package/dist/http/route-table.js +4 -0
  120. package/dist/http/routes/admin-config-refresh.js +3 -3
  121. package/dist/http/routes/admin-drain.d.ts +3 -0
  122. package/dist/http/routes/admin-drain.js +7 -7
  123. package/dist/http/routes/admin-memory-optout.js +3 -3
  124. package/dist/http/routes/adoption.js +3 -5
  125. package/dist/http/routes/approvals-assistant.d.ts +2 -1
  126. package/dist/http/routes/approvals-assistant.js +95 -48
  127. package/dist/http/routes/capabilities.d.ts +4 -0
  128. package/dist/http/routes/capabilities.js +30 -5
  129. package/dist/http/routes/config-catalog.js +2 -2
  130. package/dist/http/routes/devices.js +1 -1
  131. package/dist/http/routes/diagnostics.d.ts +3 -3
  132. package/dist/http/routes/diagnostics.js +19 -7
  133. package/dist/http/routes/images.js +10 -10
  134. package/dist/http/routes/memory-bundle.js +3 -3
  135. package/dist/http/routes/memory-compliance.js +3 -3
  136. package/dist/http/routes/memory-consolidation.js +2 -2
  137. package/dist/http/routes/memory-origin.js +3 -3
  138. package/dist/http/routes/memory-policy.js +2 -2
  139. package/dist/http/routes/observability.js +3 -3
  140. package/dist/http/routes/retention-ops.js +3 -3
  141. package/dist/http/routes/rules.js +5 -5
  142. package/dist/http/routes/run-permission-mode.d.ts +2 -1
  143. package/dist/http/routes/run-permission-mode.js +2 -2
  144. package/dist/http/routes/runs.d.ts +4 -9
  145. package/dist/http/routes/runs.js +73 -33
  146. package/dist/http/routes/session-sync.d.ts +18 -6
  147. package/dist/http/routes/session-sync.js +38 -36
  148. package/dist/http/routes/sessions.js +20 -9
  149. package/dist/http/routes/tasks.js +28 -37
  150. package/dist/http/routes/workflows.js +1 -1
  151. package/dist/http/server.d.ts +9 -3
  152. package/dist/http/server.js +15 -3
  153. package/dist/http/submit-ctx.d.ts +1 -1
  154. package/dist/http/submit-ctx.js +2 -0
  155. package/dist/human-facing-args.d.ts +33 -0
  156. package/dist/human-facing-args.js +33 -0
  157. package/dist/index.d.ts +1 -1
  158. package/dist/k8s-api-credentials.d.ts +29 -0
  159. package/dist/k8s-api-credentials.js +25 -0
  160. package/dist/lane-home-probe.d.ts +1 -1
  161. package/dist/lane-home-probe.js +2 -1
  162. package/dist/leader/diffup.d.ts +19 -0
  163. package/dist/leader/diffup.js +7 -0
  164. package/dist/leader/leader.d.ts +114 -10
  165. package/dist/leader/leader.js +69 -5
  166. package/dist/leader/repair-wire.js +2 -2
  167. package/dist/leader/run-entry.d.ts +2 -1
  168. package/dist/leader/run-entry.js +1 -1
  169. package/dist/leader/wire.d.ts +42 -132
  170. package/dist/leader/wire.js +81 -67
  171. package/dist/memory-scope.d.ts +20 -9
  172. package/dist/memory-scope.js +15 -3
  173. package/dist/model-entry-headers.d.ts +9 -10
  174. package/dist/model-entry-headers.js +0 -4
  175. package/dist/model-entry-refusal.d.ts +7 -4
  176. package/dist/model-entry-refusal.js +11 -5
  177. package/dist/model-plane-face.d.ts +1 -1
  178. package/dist/model-plane-face.js +3 -1
  179. package/dist/model-route-endpoint.d.ts +13 -7
  180. package/dist/model-route-endpoint.js +14 -11
  181. package/dist/model-select.d.ts +22 -0
  182. package/dist/model-select.js +9 -0
  183. package/dist/model-unreachable-notice.d.ts +11 -0
  184. package/dist/model-unreachable-notice.js +4 -4
  185. package/dist/object-store-buckets.d.ts +640 -6
  186. package/dist/object-store-buckets.js +166 -0
  187. package/dist/observability/fail-open.d.ts +20 -16
  188. package/dist/observability/fail-open.js +20 -16
  189. package/dist/observability/logger.js +3 -1
  190. package/dist/operator-roster.d.ts +68 -0
  191. package/dist/operator-roster.js +32 -0
  192. package/dist/orchestration/workflow-park-decision.d.ts +1 -1
  193. package/dist/outbound-dispatcher.d.ts +71 -8
  194. package/dist/outbound-dispatcher.js +48 -7
  195. package/dist/owner-faces.d.ts +1 -1
  196. package/dist/parked-decide.d.ts +2 -2
  197. package/dist/permission-mode-admission.d.ts +59 -26
  198. package/dist/permission-mode-admission.js +37 -10
  199. package/dist/plugins/approval-ask-store-file.d.ts +4 -0
  200. package/dist/plugins/approval-ask-store-file.js +3 -0
  201. package/dist/plugins/approval-ask-store-memory.d.ts +4 -0
  202. package/dist/plugins/approval-ask-store-memory.js +7 -1
  203. package/dist/plugins/approval-ask-store-sql.d.ts +26 -2
  204. package/dist/plugins/approval-ask-store-sql.js +12 -1
  205. package/dist/plugins/blob-backend.d.ts +83 -7
  206. package/dist/plugins/blob-backend.js +57 -6
  207. package/dist/plugins/checkpoint-index-columns.d.ts +20 -16
  208. package/dist/plugins/checkpoint-index-columns.js +3 -6
  209. package/dist/plugins/checkpoint-store-face.d.ts +119 -61
  210. package/dist/plugins/checkpoint-store-face.js +134 -55
  211. package/dist/plugins/landed-attendants.d.ts +2 -2
  212. package/dist/plugins/local-checkpoint-store.d.ts +13 -3
  213. package/dist/plugins/local-checkpoint-store.js +38 -49
  214. package/dist/plugins/one-time-migrations.d.ts +51 -21
  215. package/dist/plugins/one-time-migrations.js +67 -45
  216. package/dist/plugins/pg-pool.js +11 -9
  217. package/dist/plugins/remote-env-host.d.ts +29 -10
  218. package/dist/plugins/remote-env-host.js +99 -19
  219. package/dist/plugins/remote-env-k8s.d.ts +68 -2
  220. package/dist/plugins/remote-env-k8s.js +107 -24
  221. package/dist/plugins/replica-registration.d.ts +1 -1
  222. package/dist/plugins/replica-registration.js +1 -1
  223. package/dist/plugins/s3-presign.d.ts +23 -0
  224. package/dist/plugins/s3-presign.js +37 -3
  225. package/dist/plugins/scheduler-support.d.ts +2 -2
  226. package/dist/plugins/scheduler-support.js +0 -3
  227. package/dist/plugins/schema-indexes.js +2 -0
  228. package/dist/plugins/send-file-ledger.d.ts +2 -2
  229. package/dist/plugins/send-file-ledger.js +2 -2
  230. package/dist/plugins/send-user-file.d.ts +3 -4
  231. package/dist/plugins/send-user-file.js +56 -52
  232. package/dist/plugins/session-blob-claim-token.d.ts +15 -0
  233. package/dist/plugins/session-blob-claim-token.js +20 -0
  234. package/dist/plugins/session-blob-namespace.d.ts +46 -0
  235. package/dist/plugins/session-blob-namespace.js +116 -0
  236. package/dist/plugins/snapshot-bucket-presence.d.ts +25 -2
  237. package/dist/plugins/snapshot-bucket-presence.js +8 -2
  238. package/dist/plugins/sql-json-column.d.ts +1 -1
  239. package/dist/plugins/store-backend.d.ts +44 -2
  240. package/dist/plugins/store-backend.js +75 -53
  241. package/dist/plugins/task-attachment-store.d.ts +2 -1
  242. package/dist/plugins/task-attachment-store.js +5 -1
  243. package/dist/plugins/tidb-pool.js +19 -24
  244. package/dist/plugins/workspace-custody.d.ts +4 -3
  245. package/dist/question.d.ts +2 -1
  246. package/dist/question.js +5 -13
  247. package/dist/request-form.d.ts +6 -1
  248. package/dist/request-form.js +4 -1
  249. package/dist/rule-issue-reading.d.ts +1 -1
  250. package/dist/rules-consent.d.ts +1 -1
  251. package/dist/run-local.js +8 -9
  252. package/dist/runs.d.ts +17 -8
  253. package/dist/runs.js +15 -2
  254. package/dist/runtime-governance.d.ts +2 -2
  255. package/dist/sema-config-dir.d.ts +2 -1
  256. package/dist/session-sync.d.ts +27 -11
  257. package/dist/session-sync.js +15 -7
  258. package/dist/stores-sql/blob-index-sql.d.ts +30 -9
  259. package/dist/stores-sql/blob-index-sql.js +40 -21
  260. package/dist/stores-sql/checkpoint-store-sql.d.ts +54 -27
  261. package/dist/stores-sql/checkpoint-store-sql.js +70 -146
  262. package/dist/stores-sql/file-history-store-sql.d.ts +64 -16
  263. package/dist/stores-sql/file-history-store-sql.js +100 -28
  264. package/dist/stores-sql/retention-store-sql.js +3 -9
  265. package/dist/task-settings.d.ts +6 -5
  266. package/dist/task-settings.js +3 -3
  267. package/dist/tool-approval.d.ts +342 -518
  268. package/dist/tool-approval.js +498 -1207
  269. package/dist/trace/core-keyset-guard.d.ts +1 -1
  270. package/dist/trace/ledger-events.d.ts +3 -3
  271. package/dist/trace/project.d.ts +2 -3
  272. package/dist/trace/redact.js +3 -2
  273. package/dist/trace/self-minted-wire.d.ts +287 -157
  274. package/dist/trace/self-minted-wire.js +93 -48
  275. package/dist/web-admin-client/apply-effective.js +2 -2
  276. package/dist/web-admin-client/read-warnings.d.ts +2 -2
  277. package/dist/web-admin-client/read-warnings.js +1 -1
  278. package/dist/web-admin-client/types.d.ts +1 -1
  279. package/dist/workspace-space.d.ts +10 -4
  280. package/dist/workspace-space.js +4 -1
  281. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +1 -1
  282. package/package.json +6 -2
  283. package/scripts/embed-minimal.mjs +3 -3
  284. package/wire-changes/server@7.152.0.json +900 -0
  285. package/dist/config-d-migration.d.ts +0 -37
  286. package/dist/config-d-migration.js +0 -60
@@ -1,24 +1,33 @@
1
- import { parkedApprovalCard } from "../approval-content-kind.js";
2
1
  import { createHash } from "node:crypto";
3
2
  import { CHECKPOINT_FACET_SQL, CHECKPOINT_SWEEP_ORDER_SQL, CheckpointIndexRefusedError, checkpointSweepAfterSql, hostSessionOf, judgeCheckpointIndexCells } from "../plugins/checkpoint-index-columns.js";
4
3
  import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, readCheckpointSessionQuery, selectSessionPage, } from "@sema-agent/core";
5
- import { parkRowFacts, skillCardFacts } from "../approval-card.js";
6
- import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, isJsonObject, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
4
+ import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, isJsonObject, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, StoreCorruptError, } from "../plugins/sql-json-column.js";
7
5
  import { recordFailOpen } from "../observability/fail-open.js";
8
- import { durableRowIdentity, withoutCheckpointToken } from "../checkpoint-identity.js";
6
+ import { withoutCheckpointToken } from "../checkpoint-identity.js";
9
7
  import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "../plugins/sql-driver.js";
10
8
  import { isDupKeyError, isMissingColumnError, rowTooLargeOrSelf, StoreRowTooLargeError } from "../plugins/sql-errors.js";
11
9
  import { probedMissingColumnRefusal } from "../plugins/schema-drift-sentence.js";
12
10
  import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
13
11
  import { approvalPayloadFingerprint } from "../plugins/approval-payload-fingerprint.js";
14
- import { approvalDecisionOfWinner, boundedRuleOffers, boundedToolInput, candidateIdentityOf, DECIDED_REPLAY_SCAN_CAP, DENY_SWEEP_PAGE_ROWS, isUnreadableSummaryForm, parkedLegTaskId, queueRowGateKind, queueTriageOrder, readRiskDescriptorBody, TERMINAL_BACKSTOP_MS, TERMINAL_GRACE_MS, UnreadableCheckpointRowError, } from "../plugins/checkpoint-store-face.js";
12
+ import { approvalDecisionOfWinner, buildQueueActionFacts, buildQueueRow, createCardScopeRootSeat, candidateIdentityOf, DECIDED_REPLAY_SCAN_CAP, DENY_SWEEP_PAGE_ROWS, isUnreadableSummaryForm, queueRowGateKind, queueTriageOrder, TERMINAL_BACKSTOP_MS, TERMINAL_GRACE_MS, UnreadableCheckpointRowError, } from "../plugins/checkpoint-store-face.js";
15
13
  const CTX_REWRITE_PAGE = 500;
16
14
  export function tokenFingerprint(token) {
17
15
  return "sha256:" + createHash("sha256").update(token).digest("hex").slice(0, 12);
18
16
  }
19
- const STORED_ROW_MEMO_CAP = 8192;
20
- class StoredRowProjectionMemo {
17
+ export const STORED_ROW_MEMO_MAX_BYTES = 64 * 1024 * 1024;
18
+ export const STORED_ROW_MEMO_MAX_ENTRIES = 8192;
19
+ export class StoredRowProjectionMemo {
20
+ maxBytes;
21
+ maxEntries;
21
22
  rows = new Map();
23
+ total = 0;
24
+ constructor(maxBytes = STORED_ROW_MEMO_MAX_BYTES, maxEntries = STORED_ROW_MEMO_MAX_ENTRIES) {
25
+ this.maxBytes = maxBytes;
26
+ this.maxEntries = maxEntries;
27
+ }
28
+ get bytes() {
29
+ return this.total;
30
+ }
22
31
  keyOf(sha256) {
23
32
  return `${sha256}@${CONTRACT_KIT_ENGINE_VERSION}`;
24
33
  }
@@ -29,28 +38,34 @@ class StoredRowProjectionMemo {
29
38
  this.rows.delete(key);
30
39
  this.rows.set(key, hit);
31
40
  }
32
- return hit;
41
+ return hit?.actionFacts;
33
42
  }
34
- set(sha256, projection) {
43
+ set(sha256, actionFacts) {
44
+ const bytes = Buffer.byteLength(JSON.stringify(actionFacts), "utf8");
45
+ if (bytes > this.maxBytes)
46
+ return;
35
47
  const key = this.keyOf(sha256);
36
- this.rows.delete(key);
37
- this.rows.set(key, projection);
38
- while (this.rows.size > STORED_ROW_MEMO_CAP) {
48
+ this.evict(key);
49
+ this.rows.set(key, { actionFacts, bytes });
50
+ this.total += bytes;
51
+ while (this.rows.size > this.maxEntries || this.total > this.maxBytes) {
39
52
  const oldest = this.rows.keys().next().value;
40
53
  if (oldest === undefined)
41
54
  break;
42
- this.rows.delete(oldest);
55
+ this.evict(oldest);
43
56
  }
44
57
  }
58
+ evict(key) {
59
+ const gone = this.rows.get(key);
60
+ if (gone === undefined)
61
+ return;
62
+ this.rows.delete(key);
63
+ this.total -= gone.bytes;
64
+ }
45
65
  }
46
66
  const STORED_ROW_READ_BATCH = 100;
47
67
  const CHECKPOINT_SHA256_AS = "checkpoint_sha256";
48
68
  export const CHECKPOINT_ADDED_COLUMNS = [
49
- {
50
- column: "has_bidi_controls",
51
- breaksAt: "the park-mint write (put) and the operator-queue reads (GET /v1/approvals, /v1/approvals/stream) name it " +
52
- "unconditionally, so every durable suspend fails at first park and every queue poll 500s",
53
- },
54
69
  {
55
70
  column: "execution_outcome",
56
71
  breaksAt: "the execution-outcome record (CheckpointStore.recordExecutionOutcome, core 7.7.0 #602) writes it under a CAS and " +
@@ -61,6 +76,10 @@ export const CHECKPOINT_ADDED_COLUMNS = [
61
76
  column: "execution_at_ms",
62
77
  breaksAt: "it is written in the same CAS as `execution_outcome` (the record and its stamp are one write group)",
63
78
  },
79
+ {
80
+ column: "card_scope_root",
81
+ breaksAt: "the park-mint write (put) stamps it and the decide door's rule redemption reads it, so every durable suspend fails at first park",
82
+ },
64
83
  {
65
84
  column: "host_session_id",
66
85
  breaksAt: "the park-mint write (put) stamps it, the one-time backfill M49 (which runs right after this check) fills it on the rows already parked, " +
@@ -107,9 +126,6 @@ function decodeOutcomeColumn(cell, where) {
107
126
  function decodeExecutionOutcomeColumn(cell, where) {
108
127
  return decodeNullableJsonColumn(cell, readGateOutcomeBody, `checkpoint.execution_outcome ${where}`);
109
128
  }
110
- function decodeToolInputColumn(cell) {
111
- return decodeNullableJsonColumn(cell, readNonNullJsonValueColumn, "checkpoint.tool_input (operator queue row)");
112
- }
113
129
  function decodeCtxColumn(cell, where) {
114
130
  return decodeJsonColumn(cell, createObjectColumnReader(), `checkpoint_ctx.ctx ${where}`);
115
131
  }
@@ -121,71 +137,6 @@ function refusing(what, read) {
121
137
  throw foldJsonColumn(e, (m) => `SqlCheckpointStore: ${m} — refusing rather than serve ${what}`);
122
138
  }
123
139
  }
124
- function readRuleOffersCell(cell) {
125
- if (cell === null)
126
- return undefined;
127
- let parsed;
128
- try {
129
- parsed = decodeJsonColumn(cell, readJsonArrayColumn, "checkpoint.rule_suggestions (operator queue row)");
130
- }
131
- catch (e) {
132
- if (!isJsonColumnBodyProblem(e))
133
- throw e;
134
- recordFailOpen("server.approvals.rule-suggestions-cell-unreadable");
135
- return undefined;
136
- }
137
- return boundedRuleOffers(parsed, { countRedactions: false });
138
- }
139
- function readRiskDescriptorCell(cell) {
140
- if (cell === null)
141
- return null;
142
- try {
143
- return decodeJsonColumn(cell, readRiskDescriptorBody, "checkpoint.risk_descriptor (operator queue row)");
144
- }
145
- catch (e) {
146
- if (!isJsonColumnBodyProblem(e))
147
- throw e;
148
- recordFailOpen("server.approvals.risk-descriptor-cell-unreadable");
149
- return null;
150
- }
151
- }
152
- function readParkRowActionCell(cell) {
153
- try {
154
- return decodeNullableJsonColumn(cell, readPendingActionMemberBody, "checkpoint.checkpoint $.pendingAction (operator queue row)");
155
- }
156
- catch (e) {
157
- if (!isJsonColumnBodyProblem(e))
158
- throw e;
159
- recordFailOpen("server.approvals.park-row-blob-unreadable");
160
- return undefined;
161
- }
162
- }
163
- const PARK_ROW_PENDING_ACTION = { path: ["pendingAction"], as: "pending_action" };
164
- const PARK_ROW_CHECKPOINT_ID = { path: ["checkpointId"], as: "checkpoint_id" };
165
- const readCheckpointIdMemberBody = (body) => (typeof body === "string" ? { value: body } : { problem: "checkpointId is not a JSON string" });
166
- function readCheckpointIdCell(cell) {
167
- try {
168
- return decodeNullableJsonColumn(cell, readCheckpointIdMemberBody, "checkpoint.checkpoint $.checkpointId (operator queue row)");
169
- }
170
- catch (e) {
171
- if (!isJsonColumnBodyProblem(e))
172
- throw e;
173
- return null;
174
- }
175
- }
176
- const PARK_ROW_RECORDED_TASK_ID = { path: ["state", "rowTaskId"], as: "row_task_id" };
177
- const readRecordedTaskIdMemberBody = (body) => (typeof body === "string" ? { value: body } : { problem: "state.rowTaskId is not a JSON string" });
178
- function readRecordedTaskIdCell(cell) {
179
- try {
180
- return decodeNullableJsonColumn(cell, readRecordedTaskIdMemberBody, "checkpoint.checkpoint $.state.rowTaskId (operator queue row)");
181
- }
182
- catch (e) {
183
- if (!isJsonColumnBodyProblem(e))
184
- throw e;
185
- return undefined;
186
- }
187
- }
188
- const readPendingActionMemberBody = createObjectColumnReader();
189
140
  function candidateIdentityOfColumn(cell, where) {
190
141
  let cp;
191
142
  try {
@@ -260,6 +211,13 @@ export class SqlCheckpointStore {
260
211
  this.db = db;
261
212
  this.logger = logger;
262
213
  this.retention = opts.retention ?? UNMANAGED_RETENTION;
214
+ this.cardScopeRoot = opts.cardScopeRoot ?? createCardScopeRootSeat();
215
+ }
216
+ cardScopeRoot;
217
+ async cardScopeRootOf(token) {
218
+ const { rows } = await this.db.query(this.q("SELECT card_scope_root FROM checkpoint WHERE token = ?", "SELECT card_scope_root FROM checkpoint WHERE token = $1"), [token]);
219
+ const cell = rows[0]?.card_scope_root;
220
+ return typeof cell === "string" && cell !== "" ? cell : null;
263
221
  }
264
222
  q(mysql, pg) {
265
223
  return this.db.dialect === "mysql" ? mysql : pg;
@@ -302,23 +260,18 @@ export class SqlCheckpointStore {
302
260
  throw new CheckpointIndexRefusedError(cp.sessionId, judged);
303
261
  const cells = judged.cells;
304
262
  const hostSessionId = hostSessionOf(cp);
305
- const pa = cp.pendingAction;
306
- const toolInput = boundedToolInput(pa?.args);
307
- const ruleOffers = boundedRuleOffers(pa?.ruleOffers);
308
263
  const executionOutcome = cp.executionOutcome === undefined ? null : this.json(cp.executionOutcome, "execution outcome");
309
264
  const executionAtMs = cp.executionOutcome === undefined ? null : (cp.executionAtMs ?? null);
310
265
  try {
311
- await this.db.query(this.q("INSERT INTO checkpoint (token, scope, session_id, version, status, tool_name, tool_call_id, tool_input, checkpoint, deadline, created_at_ms, terminal_at_ms, gate_kind, approval_card, bound_input_hash, risk_descriptor, rule_suggestions, has_bidi_controls, rev, reopen_reason, execution_outcome, execution_at_ms, host_session_id) " +
312
- "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", "INSERT INTO checkpoint (token, scope, session_id, version, status, tool_name, tool_call_id, tool_input, checkpoint, deadline, created_at_ms, terminal_at_ms, gate_kind, approval_card, bound_input_hash, risk_descriptor, rule_suggestions, has_bidi_controls, rev, reopen_reason, execution_outcome, execution_at_ms, host_session_id) " +
313
- "VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9::jsonb,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21::jsonb,$22,$23)"), [
266
+ await this.db.query(this.q("INSERT INTO checkpoint (token, scope, session_id, version, status, tool_call_id, checkpoint, deadline, created_at_ms, terminal_at_ms, gate_kind, approval_card, bound_input_hash, rev, reopen_reason, execution_outcome, execution_at_ms, host_session_id, card_scope_root) " +
267
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", "INSERT INTO checkpoint (token, scope, session_id, version, status, tool_call_id, checkpoint, deadline, created_at_ms, terminal_at_ms, gate_kind, approval_card, bound_input_hash, rev, reopen_reason, execution_outcome, execution_at_ms, host_session_id, card_scope_root) " +
268
+ "VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8,$9,$10,$11,$12,$13,$14,$15,$16::jsonb,$17,$18,$19)"), [
314
269
  token,
315
270
  cells.scope,
316
271
  cells.session_id,
317
272
  cells.version,
318
273
  cp.status,
319
- cells.tool_name,
320
274
  cells.tool_call_id,
321
- toolInput === null ? null : this.json(toolInput, "approval tool_input"),
322
275
  this.json(cp, "checkpoint"),
323
276
  cells.deadline,
324
277
  cells.created_at_ms,
@@ -326,14 +279,12 @@ export class SqlCheckpointStore {
326
279
  cells.gate_kind,
327
280
  cells.approval_card,
328
281
  cells.bound_input_hash,
329
- cells.risk_descriptor === null ? null : this.json(cells.risk_descriptor, "risk descriptor"),
330
- ruleOffers === undefined ? null : this.json(ruleOffers, "rule offers"),
331
- cells.has_bidi_controls,
332
282
  checkpointRevOf(cp),
333
283
  cp.reopenReason ?? null,
334
284
  executionOutcome,
335
285
  executionAtMs,
336
286
  hostSessionId,
287
+ this.cardScopeRoot.of(cp.sessionId) ?? null,
337
288
  ]);
338
289
  }
339
290
  catch (e) {
@@ -547,10 +498,7 @@ export class SqlCheckpointStore {
547
498
  }
548
499
  async listPending(scope) {
549
500
  const d = this.db.dialect;
550
- const base = `SELECT c.session_id, c.token, c.scope, c.tool_name, c.tool_call_id, ${jsonColumnText(d, "tool_input")}, c.bound_input_hash, ` +
551
- `${jsonColumnText(d, "risk_descriptor")}, ${jsonColumnText(d, "rule_suggestions")}, c.has_bidi_controls, c.created_at_ms, c.deadline, c.gate_kind, c.approval_card, ta.task_id, ` +
552
- `${jsonColumnText(d, "checkpoint", PARK_ROW_PENDING_ACTION)}, ${jsonColumnText(d, "checkpoint", PARK_ROW_CHECKPOINT_ID)}, ${jsonColumnText(d, "checkpoint", PARK_ROW_RECORDED_TASK_ID)}, ` +
553
- `${jsonColumnTextSha256(d, "checkpoint", CHECKPOINT_SHA256_AS)} ` +
501
+ const base = `SELECT c.session_id, c.token, c.scope, c.created_at_ms, c.deadline, c.gate_kind, ta.task_id, ${jsonColumnTextSha256(d, "checkpoint", CHECKPOINT_SHA256_AS)} ` +
554
502
  "FROM checkpoint c LEFT JOIN task_active ta ON ta.session_id = c.host_session_id WHERE c.status='pending'";
555
503
  const { rows } = scope
556
504
  ? await this.db.query(`${base}${this.q(" AND c.scope=?", " AND c.scope=$1")} ORDER BY c.created_at_ms ASC`, [scope])
@@ -559,46 +507,29 @@ export class SqlCheckpointStore {
559
507
  const gateKind = queueRowGateKind(r.gate_kind);
560
508
  if (gateKind === undefined)
561
509
  return [];
562
- const toolName = r.tool_name ?? null;
563
- const toolCallId = r.tool_call_id ?? null;
564
- const boundInputHash = r.bound_input_hash ?? null;
565
- const ruleOffers = readRuleOffersCell(r.rule_suggestions);
566
- const approvalCard = parkedApprovalCard({ kind: gateKind, approvalCard: r.approval_card ?? undefined });
567
- const pendingAction = readParkRowActionCell(r[PARK_ROW_PENDING_ACTION.as]);
568
- const row = {
510
+ const token = String(r.token);
511
+ const address = {
512
+ token,
569
513
  sessionId: String(r.session_id),
570
- ...durableRowIdentity("approvals-queue", readCheckpointIdCell(r[PARK_ROW_CHECKPOINT_ID.as]), r.token),
571
514
  scope: String(r.scope),
572
- toolName,
573
- toolCallId,
574
- ...(toolCallId !== null ? { boundCallId: toolCallId } : {}),
575
- ...(boundInputHash !== null ? { boundInputHash } : {}),
576
515
  gateKind,
577
- taskId: parkedLegTaskId(readRecordedTaskIdCell(r[PARK_ROW_RECORDED_TASK_ID.as]), r.task_id ?? null),
578
- input: refusing("a tool input the operator would be approving unseen", () => decodeToolInputColumn(r.tool_input)) ?? null,
579
516
  createdAt: Number(r.created_at_ms),
580
517
  deadline: r.deadline == null ? null : Number(r.deadline),
581
- riskDescriptor: readRiskDescriptorCell(r.risk_descriptor),
582
- ...(ruleOffers !== undefined ? { ruleOffers } : {}),
583
- ...(Number(r.has_bidi_controls) === 1 ? { hasBidiControls: true } : {}),
584
- ...(approvalCard !== undefined ? { approvalCard } : {}),
585
- ...parkRowFacts(pendingAction),
518
+ claimHolder: r.task_id ?? null,
586
519
  };
587
- return [{ row, token: String(r.token), sha256: readJsonColumnTextSha256(r[CHECKPOINT_SHA256_AS], "checkpoint.checkpoint_sha256 (operator queue row)") }];
520
+ return [{ address, token, sha256: readJsonColumnTextSha256(r[CHECKPOINT_SHA256_AS], "checkpoint.checkpoint_sha256 (operator queue row)") }];
588
521
  });
589
- const projections = await this.storedRowProjections(entries);
590
- const out = entries.map((e) => {
591
- const p = projections.get(e.token);
592
- return p === undefined ? e.row : { ...e.row, ...p.skillCardFacts, rowVerdict: p.rowVerdict };
522
+ const facts = await this.storedRowProjections(entries);
523
+ const out = entries.flatMap((e) => {
524
+ const f = facts.get(e.token);
525
+ return f === undefined ? [] : [buildQueueRow(e.address, f)];
593
526
  });
594
527
  return out.sort(queueTriageOrder);
595
528
  }
596
529
  async storedRowProjections(rows) {
597
530
  const out = new Map();
598
531
  const misses = [];
599
- const expected = new Map();
600
532
  for (const { token, sha256 } of rows) {
601
- expected.set(token, sha256);
602
533
  const hit = this.storedRowMemo.get(sha256);
603
534
  if (hit === undefined)
604
535
  misses.push(token);
@@ -611,19 +542,12 @@ export class SqlCheckpointStore {
611
542
  `WHERE status = 'pending' AND token IN (${batch.map(() => "?").join(", ")})`, `SELECT token, ${jsonColumnText("pg", "checkpoint")}, ${jsonColumnTextSha256("pg", "checkpoint", CHECKPOINT_SHA256_AS)} FROM checkpoint ` +
612
543
  `WHERE status = 'pending' AND token IN (${batch.map((_, j) => `$${j + 1}`).join(", ")})`), batch);
613
544
  for (const r of read) {
614
- const token = String(r.token);
615
545
  const sha256 = readJsonColumnTextSha256(r[CHECKPOINT_SHA256_AS], "checkpoint.checkpoint_sha256 (whole-row read)");
616
- const projection = this.projectStoredRow(r, sha256);
617
- if (sha256 === expected.get(token))
618
- out.set(token, projection);
546
+ out.set(String(r.token), this.readStoredRow(r, "(operator queue row, whole-row read)", sha256).actionFacts);
619
547
  }
620
548
  }
621
549
  return out;
622
550
  }
623
- projectStoredRow(r, sha256) {
624
- const read = this.readStoredRow(r, "(operator queue row, whole-row read)", sha256);
625
- return read.summary === undefined ? { rowVerdict: read.rowVerdict, skillCardFacts: {} } : { rowVerdict: read.rowVerdict, skillCardFacts: skillCardFacts(read.summary) };
626
- }
627
551
  readStoredRow(r, where, sha256, discloseFor) {
628
552
  const memo = this.storedRowMemo.get(sha256);
629
553
  let cp;
@@ -634,31 +558,33 @@ export class SqlCheckpointStore {
634
558
  if (!isJsonColumnBodyProblem(e))
635
559
  throw e;
636
560
  recordFailOpen("server.approvals.park-row-blob-unreadable");
637
- return { rowVerdict: verdictOfUnreadableBody(r.checkpoint) };
561
+ return { actionFacts: buildQueueActionFacts({ rowVerdict: verdictOfUnreadableBody(r.checkpoint) }) };
638
562
  }
639
563
  let summary;
640
564
  const disclosed = [];
641
565
  try {
642
- summary = summarizeCheckpoint(cp, (sentence) => disclosed.push(sentence), memo?.rowVerdict);
566
+ summary = summarizeCheckpoint(cp, (sentence) => disclosed.push(sentence), memo?.row.rowVerdict);
643
567
  }
644
568
  catch {
645
569
  recordFailOpen("server.approvals.park-row-blob-unreadable");
646
- return { rowVerdict: judgeCheckpointRow(cp) };
570
+ return { actionFacts: buildQueueActionFacts({ rowVerdict: judgeCheckpointRow(cp), cp }) };
647
571
  }
648
572
  if (isUnreadableSummaryForm(summary)) {
649
573
  recordFailOpen("server.approvals.park-row-blob-unreadable");
650
- return { rowVerdict: summary.rowVerdict };
574
+ return { actionFacts: buildQueueActionFacts({ rowVerdict: summary.rowVerdict, cp, summary }) };
651
575
  }
652
576
  const seat = discloseFor?.(cp);
653
577
  for (const sentence of disclosed)
654
578
  seat?.(sentence);
655
- if (memo === undefined)
656
- this.storedRowMemo.set(sha256, { rowVerdict: summary.rowVerdict, skillCardFacts: skillCardFacts(summary) });
657
- return { rowVerdict: summary.rowVerdict, summary };
579
+ if (memo !== undefined)
580
+ return { summary, actionFacts: memo };
581
+ const actionFacts = buildQueueActionFacts({ rowVerdict: summary.rowVerdict, cp, summary });
582
+ this.storedRowMemo.set(sha256, actionFacts);
583
+ return { summary, actionFacts };
658
584
  }
659
585
  async listByScope(scope) {
660
586
  const d = this.db.dialect;
661
- const { rows } = await this.db.query(`SELECT ${jsonColumnText(d, "checkpoint")}, ${jsonColumnTextSha256(d, "checkpoint", CHECKPOINT_SHA256_AS)}, ${jsonColumnText(d, "checkpoint", PARK_ROW_CHECKPOINT_ID)}, ` +
587
+ const { rows } = await this.db.query(`SELECT ${jsonColumnText(d, "checkpoint")}, ${jsonColumnTextSha256(d, "checkpoint", CHECKPOINT_SHA256_AS)}, ` +
662
588
  `token, session_id, scope, created_at_ms, deadline, gate_kind FROM checkpoint WHERE status = 'pending' AND scope = ${this.q("?", "$1")} ORDER BY created_at_ms ASC LIMIT 500`, [scope]);
663
589
  const out = [];
664
590
  for (const r of rows) {
@@ -671,11 +597,9 @@ export class SqlCheckpointStore {
671
597
  const gateKind = queueRowGateKind(r.gate_kind);
672
598
  if (gateKind === undefined)
673
599
  continue;
674
- const checkpointId = readCheckpointIdCell(r[PARK_ROW_CHECKPOINT_ID.as]);
675
600
  out.push({
676
601
  token: String(r.token),
677
- rowVerdict: read.rowVerdict,
678
- ...(typeof checkpointId === "string" ? { checkpointId } : {}),
602
+ rowVerdict: read.actionFacts.row.rowVerdict,
679
603
  sessionId: String(r.session_id),
680
604
  scope: String(r.scope),
681
605
  gateKind,
@@ -3,13 +3,20 @@ import type { Pool as PgPool } from "pg";
3
3
  import { type FileHistoryRetentionPolicy, type ExecutionEnv, type FileHistoryStore, type FileHistoryError, type FileHistoryResult, type FileHistoryTrackResult, type FileHistoryRestoreResult, type FileHistoryExport, type FileHistoryExportResult } from "@sema-agent/core";
4
4
  import type { BlobBackend } from "../plugins/blob-backend.js";
5
5
  import { SqlBlobIndex } from "./blob-index-sql.js";
6
- import type { ServiceBlobExchange } from "../plugins/store-backend.js";
6
+ import type { ServiceBlobExchange, ServiceBlobLanding, SqlBlobPutResult, SyncLanding } from "../plugins/store-backend.js";
7
7
  import { type SqlDriver, type SqlTxConn, type SqlDialect } from "../plugins/sql-driver.js";
8
8
  /** The file-history blob domain's SQL table (this store's blob index + the A7 assert). */
9
9
  export declare const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
10
- /** MinIO object-key prefix for this domain (snapshot epoch used "blobs/"; a separate prefix keeps the
11
- * two domains' objects disjoint so neither sweep can see the other's keys). */
12
- export declare const FILE_HISTORY_BLOB_KEY_PREFIX = "file-history/";
10
+ /** The per-landing provenance rows beside it ({@link SqlFileHistoryStore.blobLanding}). */
11
+ export declare const FILE_HISTORY_BLOB_GRANT_TABLE = "file_history_blob_grant";
12
+ /**
13
+ * Every file-history table keyed by `scope` (= the session id) — the ONE list every scope-wide deleter reads: this store's `deleteBySession` (E21 purge /
14
+ * overwrite-dst wipe) and its statement source on a caller's transaction (`deleteScopeOn` — the landed section's wipe), the replace arm of a landed
15
+ * replay (all but the sentinel), and the SQL retention executor's session-tree leg (`stores-sql/retention-store-sql.ts`). Order = delete order:
16
+ * children first, the scope sentinel LAST (a transaction queued on the sentinel restarts from its own ensure — see `lockScope`); every deleter holds
17
+ * the sentinel lock before the first DELETE. The byte index (`file_history_blob`) is NOT scope-keyed: bytes leave by the orphan sweep.
18
+ */
19
+ export declare const FILE_HISTORY_SCOPED_TABLES: readonly ["file_history_boundary_file", "file_history_boundary", "file_history_reaped_entry", "file_history_version", "file_history_tracked", "file_history_blob_grant", "file_history_scope"];
13
20
  /**
14
21
  * {@link SqlFileHistoryStore.prepareImport} 的答:门 / 取值校验的拒(`ok: false`,店码),或这一份信封落进目标要做什么 —— `same`(目标已是这一张图:不写)/
15
22
  * `publish`(目标空,或 `replace` 下目标是另一张图:发布步写)。发布步({@link SqlFileHistoryStore.publishImportOn})在调用方的事务里按它做。
@@ -40,16 +47,60 @@ export interface SqlFileHistoryStoreOptions {
40
47
  warn(msg: string, meta?: Record<string, unknown>): void;
41
48
  };
42
49
  }
50
+ /**
51
+ * The blob exchange (core's `FileHistoryStore.blobExchange` group — `put` / `get` / `has`, all three or none — narrowed per {@link ServiceBlobExchange})
52
+ * over the store's blob index. `get` / `has` are the index's own reads (`has` = SQL INDEX probes, batched, no bytes read).
53
+ */
54
+ export declare class SqlBlobExchange implements ServiceBlobExchange {
55
+ private readonly blobs;
56
+ constructor(blobs: SqlBlobIndex);
57
+ /** Store ONE content-addressed blob ahead of an importHistory (§5 streamed negotiation). The content address is judged HERE and
58
+ * nowhere else (core's `put` law: bytes whose sha256 is not `hash` are refused `adopt_failed` / `terminal`, nothing stored) — the domain's
59
+ * one byte write path ({@link SqlBlobLanding.putAt} writes through it).
60
+ * Never-throw. The SQL blob judge's refusal ({@link BlobTooLargeError}, judged on the connection that would have written the blob)
61
+ * is a terminal refusal that carries `blobCap` — the route answers it 413 with the judge's sentence; every other write failure is
62
+ * `snapshot_failed` with the store's judgment of the fault (the route answers 502, "retry"). */
63
+ put(hash: string, bytes: Uint8Array): Promise<SqlBlobPutResult>;
64
+ get(hash: string): Promise<Uint8Array | undefined>;
65
+ has(hashes: readonly string[]): Promise<ReadonlySet<string>>;
66
+ }
67
+ /**
68
+ * Per-landing provenance over the same blob domain ({@link ServiceBlobLanding}): the session-sync routes' only blob face. Bytes go through the
69
+ * exchange (its `put` is the one write path, its `has` the one presence read); this face adds the grant rows (`file_history_blob_grant`) and the
70
+ * graph read that decide what a landing may see.
71
+ */
72
+ export declare class SqlBlobLanding implements ServiceBlobLanding {
73
+ private readonly db;
74
+ private readonly exchange;
75
+ constructor(db: SqlDriver, exchange: ServiceBlobExchange);
76
+ /** Pick the dialect's SQL text (both statements stay written out at the call site, as in the store). */
77
+ private q;
78
+ /** The landing's owner cell as stored: the principal string, or the ownerless cell's domain key `""` (a principal is never the empty string). */
79
+ private static ownerKey;
80
+ /** `putAt`: verify the address → write the bytes (the exchange's one write path) → ONLY THEN upsert this landing's grant (`granted_at` refreshed
81
+ * monotonically). A refused / failed byte write grants nothing — otherwise a landing whose own write was refused (a per-connection 413) would
82
+ * see bytes another landing stored on a higher wall: an existence oracle again. A grant write that fails answers the write-fault arm (the bytes
83
+ * are then an ungranted orphan the sweep collects — harmless). Never-throw, like the exchange's `put`: a failure is a value. */
84
+ putAt(landing: SyncLanding, hash: string, bytes: Uint8Array): Promise<SqlBlobPutResult>;
85
+ /** `visibleAt`: ((occupied ? this session's graph : ∅) ∪ this landing's grants) ∩ `hashes` ∩ present — three separate reads ON PURPOSE, so the
86
+ * statement shape pins "only candidates are asked about presence" (a JOIN would let the optimizer probe the global index with the whole input
87
+ * first): ① the graph's hashes, one read by the scope prefix (no IN; not repeated per batch); ② this landing's grants by PK, batched;
88
+ * ③ presence of the candidates only, batched — none of these reads depends on what OTHER landings stored, so the time it takes doesn't
89
+ * either. Read faults throw — out of this sealed exit as the store-boundary form (the routes map it by its fault class; never read as "not visible"). */
90
+ visibleAt(landing: SyncLanding, hashes: readonly string[]): Promise<ReadonlySet<string>>;
91
+ }
43
92
  /** Dual-dialect durable FileHistoryStore. See the file header for posture + dialect-delta ledger. */
44
93
  export declare class SqlFileHistoryStore implements FileHistoryStore {
45
94
  protected readonly db: SqlDriver;
46
95
  /** RAW native pool — handed to the blob index (`blob-index-sql.ts`, outside the SqlDriver seam, dialect-switches on
47
96
  * its own string). Not used for this class's own statements (those go through `db`). */
48
97
  protected readonly pool: MySqlPool | PgPool;
49
- /** The blob exchange (core's `FileHistoryStore.blobExchange` group, see {@link ServiceBlobExchange}): how another machine's bytes
98
+ /** The blob exchange (core's `FileHistoryStore.blobExchange` group, see {@link SqlBlobExchange}): how another machine's bytes
50
99
  * come in ahead of an `importHistory`, go out to a puller, and are asked about. Its presence IS the capability — read through
51
100
  * `blobExchangeOf` (`session-sync.ts`), which defers to core's judge; there is no second word for it on this class. */
52
101
  readonly blobExchange: ServiceBlobExchange;
102
+ /** Per-landing provenance over the same blob domain ({@link SqlBlobLanding}): the session-sync routes' only blob face. */
103
+ readonly blobLanding: ServiceBlobLanding;
53
104
  /** This store's blob index (the `file_history_blob` rows; the byte port, when given, behind it). */
54
105
  protected readonly blobs: SqlBlobIndex;
55
106
  /** §10 S-6 in-process lease state (design v1 D2: NOT persisted). Keys = `${scope}\x00${entryId}`. */
@@ -182,21 +233,18 @@ export declare class SqlFileHistoryStore implements FileHistoryStore {
182
233
  * collected by the async grace-window sweep (never a synchronous external-store delete on the purge
183
234
  * path). Also the overwrite-dst session-sync wipe seam (import requires an EMPTY target graph). */
184
235
  deleteBySession(scope: string): Promise<number>;
185
- /** The whole-scope removal as a statement source on the caller's transaction ({@link lockScopeOn} + the six tables, sentinel last):
236
+ /** The whole-scope removal as a statement source on the caller's transaction ({@link lockScopeOn} + {@link FILE_HISTORY_SCOPED_TABLES}, sentinel last):
186
237
  * {@link deleteBySession}'s body, and the landed section's wipe (one transaction with the session's root-row lock). Returns rows removed. */
187
238
  deleteScopeOn(conn: SqlTxConn, scope: string): Promise<number>;
188
- /** Delete one scope's rows from `tables` (caller holds the scope's sentinel lock). 第六表 `file_history_reaped_entry`(spent-entry 台账)随 scope 一起走:
189
- * E21 是删除权,台账不是例外。 */
239
+ /** Delete one scope's rows from `tables` (caller holds the scope's sentinel lock). 第六表 `file_history_reaped_entry`(spent-entry 台账)与按落点的授予行随 scope 一起走:
240
+ * E21 是删除权,台账 / 授予不是例外(表单 = {@link FILE_HISTORY_SCOPED_TABLES})。 */
190
241
  private deleteScopeRowsOn;
191
- /** Async byte-GC: blobs referenced by NO version row (any scope), past the created_at grace window.
192
- * Domain-isolated by construction (this store's blob table only — [ref] D1). Reaper-driven. */
242
+ /** Async byte-GC: one sweep = ONE cutoff (`now − BLOB_GC_GRACE_MS`, the index's single grace source) for both legs —
243
+ * ① grant rows past the cutoff are dropped (an upload lease outlives the grace window, never less; a late / failed sweep only lengthens it,
244
+ * and only at that same landing); ② orphans = blobs referenced by NO version row (any scope) AND by NO grant row (a live grant is an explicit
245
+ * GC root — not inferred from timestamps), removed past the same cutoff on created_at. Domain-isolated by construction (this store's blob
246
+ * tables only — [ref] D1). Reaper-driven. */
193
247
  sweepOrphanBlobs(): Promise<number>;
194
- /** Store ONE content-addressed blob ahead of an importHistory (§5 streamed negotiation). The content address is judged HERE and
195
- * nowhere else (core's `put` law: bytes whose sha256 is not `hash` are refused `adopt_failed` / `terminal`, nothing stored).
196
- * Never-throw. The SQL blob judge's refusal ({@link BlobTooLargeError}, judged on the connection that would have written the blob)
197
- * is a terminal refusal that carries `blobCap` — the route answers it 413 with the judge's sentence; every other write failure is
198
- * `snapshot_failed` with the store's judgment of the fault (the route answers 502, "retry"). */
199
- private putExchangedBlob;
200
248
  adoptScope(fromScope: string, toScope: string): Promise<FileHistoryResult>;
201
249
  private scopeEmpty;
202
250
  /** `forUpdate` — see {@link loadTracked}. Every in-transaction caller passes `true`: the four reads are