@sema-agent/server 7.10.0 → 7.11.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 (59) hide show
  1. package/dist/adoption/plan.d.ts +152 -0
  2. package/dist/adoption/plan.js +513 -0
  3. package/dist/adoption/runner.d.ts +54 -0
  4. package/dist/adoption/runner.js +505 -0
  5. package/dist/adoption/sql.d.ts +76 -0
  6. package/dist/adoption/sql.js +106 -0
  7. package/dist/adoption/wire.d.ts +250 -0
  8. package/dist/adoption/wire.js +153 -0
  9. package/dist/approval-card.d.ts +24 -0
  10. package/dist/approval-card.js +32 -0
  11. package/dist/boot/adoption.d.ts +30 -0
  12. package/dist/boot/adoption.js +57 -0
  13. package/dist/boot/coordinators.d.ts +4 -0
  14. package/dist/boot/coordinators.js +3 -1
  15. package/dist/boot/parked-revive-gate.d.ts +38 -5
  16. package/dist/boot/parked-revive-gate.js +53 -6
  17. package/dist/boot/runner-deps.d.ts +10 -2
  18. package/dist/boot/runner-deps.js +12 -1
  19. package/dist/config-types.d.ts +16 -1
  20. package/dist/config.js +5 -1
  21. package/dist/http/routes/adoption.d.ts +26 -0
  22. package/dist/http/routes/adoption.js +120 -0
  23. package/dist/http/routes/capabilities.js +12 -0
  24. package/dist/http/routes/rules.d.ts +23 -0
  25. package/dist/http/routes/rules.js +117 -0
  26. package/dist/http/routes/shared-memory.d.ts +31 -0
  27. package/dist/http/routes/shared-memory.js +181 -0
  28. package/dist/http/routes/trace-usage.js +139 -3
  29. package/dist/http/server.d.ts +19 -1
  30. package/dist/http/server.js +42 -0
  31. package/dist/main.js +52 -3
  32. package/dist/observability/fail-open.d.ts +8 -0
  33. package/dist/observability/fail-open.js +8 -0
  34. package/dist/plugins/adoption-log-sql.d.ts +191 -0
  35. package/dist/plugins/adoption-log-sql.js +273 -0
  36. package/dist/plugins/checkpoint-store-sql.d.ts +13 -0
  37. package/dist/plugins/checkpoint-store-sql.js +11 -0
  38. package/dist/plugins/local-checkpoint-store.d.ts +10 -0
  39. package/dist/plugins/local-checkpoint-store.js +8 -0
  40. package/dist/plugins/permission-rule-store-sql.d.ts +242 -0
  41. package/dist/plugins/permission-rule-store-sql.js +817 -0
  42. package/dist/plugins/pg-pool.js +37 -0
  43. package/dist/plugins/session-policy-store-sql.d.ts +6 -0
  44. package/dist/plugins/session-policy-store-sql.js +7 -1
  45. package/dist/plugins/shared-memory-store-sql.d.ts +223 -0
  46. package/dist/plugins/shared-memory-store-sql.js +516 -0
  47. package/dist/plugins/store-backend.d.ts +30 -0
  48. package/dist/plugins/store-backend.js +14 -0
  49. package/dist/plugins/tidb-pool.js +47 -0
  50. package/dist/rules-consent.d.ts +126 -0
  51. package/dist/rules-consent.js +198 -0
  52. package/dist/shared-memory-scope-authorizer.d.ts +29 -0
  53. package/dist/shared-memory-scope-authorizer.js +17 -0
  54. package/dist/tool-approval.d.ts +55 -0
  55. package/dist/tool-approval.js +124 -5
  56. package/dist/trace/core-keyset-guard.d.ts +2 -2
  57. package/dist/trace/project.d.ts +1 -0
  58. package/dist/trace/project.js +1 -0
  59. package/package.json +3 -3
@@ -36,6 +36,8 @@ import { ensurePgOutcomeLedgerSchema as ensureOutcomeLedgerSchema } from "./outc
36
36
  import { ensureSchema as ensureImageIndexSchema } from "./image-index-sql.js";
37
37
  import { ensurePgImageBakeSchema as ensureImageBakeSchema } from "./image-bake-store-sql.js";
38
38
  import { ensurePgApprovalAskSchema } from "./approval-ask-store-sql.js";
39
+ import { ensurePgAdoptionLogSchema } from "./adoption-log-sql.js";
40
+ import { ensurePgPermissionRuleSchema } from "./permission-rule-store-sql.js";
39
41
  export const PG_SCHEMA_STATEMENTS = [
40
42
  `CREATE TABLE IF NOT EXISTS task_run (
41
43
  task_id VARCHAR(64) COLLATE "C" NOT NULL,
@@ -284,6 +286,35 @@ export const PG_SCHEMA_STATEMENTS = [
284
286
  PRIMARY KEY (run_id)
285
287
  )`,
286
288
  `CREATE INDEX IF NOT EXISTS idx_wfnotify_acked ON workflow_notify_journal (acked)`,
289
+ // design/177 shared-memory store —— org 盘三张表的 PG twin(MySQL 侧的逐列注释见 tidb-pool.ts;方言差
290
+ // 只在 TINYINT(1)→SMALLINT 与 inline KEY→独立 CREATE INDEX)。🔴 与 `agent_memory_engine_*` 零关系。
291
+ // 每个字符串列都带 COLLATE "C":core 的 cursor 词表是「UTF-16 code-unit 序」,而它的翻页只在
292
+ // `nextCursor > windowCursor`(JS 比较)成立时才继续 —— 数据库若按 locale 排序,一次列举会静默截断。
293
+ `CREATE TABLE IF NOT EXISTS shared_memory_scope (
294
+ scope_key VARCHAR(190) COLLATE "C" NOT NULL,
295
+ state VARCHAR(16) COLLATE "C" NOT NULL,
296
+ message VARCHAR(512) COLLATE "C",
297
+ updated_at_ms BIGINT NOT NULL,
298
+ PRIMARY KEY (scope_key)
299
+ )`,
300
+ `CREATE TABLE IF NOT EXISTS shared_memory_store (
301
+ store_id VARCHAR(64) COLLATE "C" NOT NULL,
302
+ scope_key VARCHAR(190) COLLATE "C" NOT NULL,
303
+ description VARCHAR(512) COLLATE "C" NOT NULL,
304
+ writable SMALLINT NOT NULL DEFAULT 0,
305
+ updated_at_ms BIGINT NOT NULL,
306
+ PRIMARY KEY (store_id)
307
+ )`,
308
+ `CREATE INDEX IF NOT EXISTS idx_shared_memory_store_scope ON shared_memory_store (scope_key, store_id)`,
309
+ `CREATE TABLE IF NOT EXISTS shared_memory_entry (
310
+ store_id VARCHAR(64) COLLATE "C" NOT NULL,
311
+ path VARCHAR(512) COLLATE "C" NOT NULL,
312
+ content TEXT COLLATE "C" NOT NULL,
313
+ updated_at_iso VARCHAR(64) COLLATE "C",
314
+ size_bytes INTEGER NOT NULL,
315
+ updated_at_ms BIGINT NOT NULL,
316
+ PRIMARY KEY (store_id, path)
317
+ )`,
287
318
  ];
288
319
  /** Build the pg PoolConfig from PG config (pure → unit-testable apart from the live connection) — the PG twin of
289
320
  * tidbPoolOptions. `dbQueryTimeoutMs` (DB_QUERY_TIMEOUT_MS, S9 deeper fix) arms a POOL-WIDE per-query ceiling on
@@ -354,6 +385,12 @@ export async function ensurePgSchema(pool) {
354
385
  // `PgQueryFn`(而非兄弟们收的 client 形),故在这里绑一层 —— DDL 仍然跑在**同一条** client 上,
355
386
  // advisory lock 的 session 语义不受影响(见本函数头注:路由到别的池连接会静默作废那把锁)。
356
387
  await ensurePgApprovalAskSchema((text, params) => client.query(text, params));
388
+ // design/183 form b 的 SQL 收编日志(MySQL twin 走 tidb-pool 的 SCHEMA_STATEMENTS 展开)。
389
+ // 同 approval-ask:收裸 query 形,故在这里绑一层——DDL 仍跑在**同一条** client 上,advisory lock 不失效。
390
+ await ensurePgAdoptionLogSchema((text, params) => client.query(text, params));
391
+ // #154 车二:持久化权限规则店三张表(MySQL twin 走 tidb-pool 的 SCHEMA_STATEMENTS 展开)。
392
+ // 同上一行的绑法 —— DDL 跑在**同一条** client 上,advisory lock 的 session 语义不受影响。
393
+ await ensurePgPermissionRuleSchema((text, params) => client.query(text, params));
357
394
  }
358
395
  finally {
359
396
  if (locked) {
@@ -2,6 +2,12 @@ import { type SessionPolicyStore, type SessionPermissionRules, type StoredSessio
2
2
  import type { Pool as MySqlPool } from "mysql2/promise";
3
3
  import type { Pool as PgPool } from "pg";
4
4
  import { type SqlDriver } from "./sql-driver.js";
5
+ /** `session_policy.policy_key` 的**唯一铸造点**(core 复合键的 sha256)。
6
+ *
7
+ * 🔴 导出而不是让收编腿再抄一份(clay 宪法「schema 单一属主禁复制」):design/183 form b 的身份重绑要
8
+ * 逐行重算这把键(身份在 hash 的**原像**里,等值 UPDATE 够不着)。两处各写一遍 sha256 表达式的下场是
9
+ * 某天有人在这里改了 key 公式而收编腿还在按旧式算 —— 迁完的行从此谁也读不到,且**静默**。 */
10
+ export declare const policyKeyFor: (sessionId: string, principal: string | undefined) => string;
5
11
  /** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
6
12
  export declare class SqlSessionPolicyStore implements SessionPolicyStore {
7
13
  private readonly db;
@@ -43,7 +43,13 @@ import { createHash } from "node:crypto";
43
43
  import { loosenReasons, SessionPolicyError, normalizeRules, stripRev } from "@sema-agent/core";
44
44
  import { pgHasUnstorable } from "./pg-safe-json.js";
45
45
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
46
- const policyKey = (sessionId, principal) => createHash("sha256").update(JSON.stringify([sessionId, principal ?? null])).digest("hex");
46
+ /** `session_policy.policy_key` 的**唯一铸造点**(core 复合键的 sha256)
47
+ *
48
+ * 🔴 导出而不是让收编腿再抄一份(clay 宪法「schema 单一属主禁复制」):design/183 form b 的身份重绑要
49
+ * 逐行重算这把键(身份在 hash 的**原像**里,等值 UPDATE 够不着)。两处各写一遍 sha256 表达式的下场是
50
+ * 某天有人在这里改了 key 公式而收编腿还在按旧式算 —— 迁完的行从此谁也读不到,且**静默**。 */
51
+ export const policyKeyFor = (sessionId, principal) => createHash("sha256").update(JSON.stringify([sessionId, principal ?? null])).digest("hex");
52
+ const policyKey = policyKeyFor;
47
53
  const parseRules = (v) => (typeof v === "string" ? JSON.parse(v) : v); // R7 终形:拒绝式下库内恒干净,直读
48
54
  /** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
49
55
  export class SqlSessionPolicyStore {
@@ -0,0 +1,223 @@
1
+ /**
2
+ * design/177 — the SQL `SharedMemoryStoreProvider`: the deployment's ORG-scoped shared memory libraries
3
+ * (`memory_list` / `memory_read`), served out of the durable store instead of a BYOM host callback.
4
+ *
5
+ * ─────────────────────────────────────────────────────────────────────────────────────────────────
6
+ * WHAT THIS IS (and, just as load-bearing, what it is NOT)
7
+ * ─────────────────────────────────────────────────────────────────────────────────────────────────
8
+ * core's seam is a document-only READ face reached exclusively through two tools. There is no model
9
+ * write channel at all — write governance is the environment's, and the tool face's entire
10
+ * participation in it is relaying each store's `writable` bit verbatim. This module keeps that shape:
11
+ * {@link SqlSharedMemoryStore.snapshot} is the only method core ever calls; the `put*`/`delete*` half
12
+ * is the DEPLOYMENT's admin plane (migrations, an import job, an operator face) and is deliberately
13
+ * NOT reachable from any model-facing surface in this repo.
14
+ *
15
+ * 🔴 ORG PLANE ≠ THE LOCAL MEMORY CHAIN (设计 182 §13). `agent_memory_engine_*` (the design/138
16
+ * MemoryEngine) and these three tables share NOTHING: no join, no view, no shared row, no shared
17
+ * scope registry. The local chain is an entry-transactional index injection with a write path; this
18
+ * one is a read-only document library. Bridging them in EITHER direction would let harvested private
19
+ * notes leak into a team library (or a team library into a model-writable disk), which is exactly the
20
+ * boundary core's seam doc draws. If a future feature needs both, it composes them ABOVE this layer.
21
+ *
22
+ * ─────────────────────────────────────────────────────────────────────────────────────────────────
23
+ * AUTHORIZATION (v2/F4) — server-side, injected, never inferred here
24
+ * ─────────────────────────────────────────────────────────────────────────────────────────────────
25
+ * `SharedMemoryRequestContext` carries the caller's `principal`. Who belongs to `org:acme` is NOT this
26
+ * module's question: it takes a {@link SharedMemoryScopeAuthorizer} and serves exactly the scopes that
27
+ * verdict names. The production binding folds the SAME `OrgMemoryDirectory` instance the memory-policy
28
+ * face and core's admission seam already read (`src/shared-memory-scope-authorizer.ts`), so a process
29
+ * has ONE answer to "who belongs to org:acme".
30
+ *
31
+ * The verdict is a discriminated union on purpose (same 裁定 as org-memory-admission's N3/C5): a
32
+ * directory that CANNOT ANSWER must never collapse into "you belong to nothing". The first renders as
33
+ * `unavailable` ("reads are refused until it is restored" — try again); the second renders as a
34
+ * settled "no memory stores are connected to this session", which teaches the model to give up. Those
35
+ * are opposite instructions and a fail-open collapse between them is a real defect, not a nuance.
36
+ *
37
+ * ─────────────────────────────────────────────────────────────────────────────────────────────────
38
+ * ORDERING / PAGINATION — why the collations are part of the contract
39
+ * ─────────────────────────────────────────────────────────────────────────────────────────────────
40
+ * core's cursor vocabulary is "an EXCLUSIVE lower bound over document paths in plain UTF-16 code-unit
41
+ * order", and its paging loop only advances while `nextCursor > windowCursor` **in JavaScript**. So the
42
+ * SQL ordering must agree with JS string comparison or a listing would silently stop early.
43
+ * · MySQL: the tables are `COLLATE utf8mb4_bin` ⇒ comparison is by code point.
44
+ * · PG: every string column is `COLLATE "C"` ⇒ comparison is by UTF-8 byte, which for valid UTF-8 is
45
+ * also code-point order.
46
+ * Code-point order equals UTF-16 code-unit order for every BMP character, and differs ONLY for
47
+ * supplementary-plane characters (U+10000+, whose surrogates sort before U+E000..U+FFFF in UTF-16).
48
+ * Rather than leave that as an invisible "listing silently truncates" hazard, {@link assertDocumentPath}
49
+ * REFUSES a non-BMP path at write time — the divergence is made impossible by construction, loudly, at
50
+ * the one place a human is watching. (Store ids and org scopes are already ASCII-only by their own
51
+ * shape rules, so this is the only column that needed the ruling.)
52
+ *
53
+ * KEYSET, never OFFSET: `WHERE store_id = ? AND path > ?` walks the PRIMARY KEY. An OFFSET pager over a
54
+ * live library both re-scans the prefix on every page and skips rows when a document is inserted mid-walk.
55
+ *
56
+ * ─────────────────────────────────────────────────────────────────────────────────────────────────
57
+ * 事务与锁 —— 为什么用 `begin()` 而不是 `beginPessimistic()`
58
+ * ─────────────────────────────────────────────────────────────────────────────────────────────────
59
+ * 三个写方法(putStore / putDocument / deleteStore)都是「先读注册行、再按结果写」的形状,并且都用
60
+ * `SELECT … FOR UPDATE` 拿同一把行锁,于是彼此串行。事务动词取**普通** `begin()`:
61
+ * · `BEGIN PESSIMISTIC` 是 TiDB 专有语法,在 MySQL / MariaDB 上是**语法错误**——而本仓 `kind: "mysql"`
62
+ * 按定义覆盖任何 MySQL 协议服务端(本机验收台就是真 MySQL 9.7)。用它等于让这个店在多数目标上直接
63
+ * 不可用,那比它要防的那道窄窗口严重得多;
64
+ * · MySQL InnoDB 的加锁读(`FOR UPDATE`)在 REPEATABLE READ 下**恒是 current read**,读到的是最新已提交
65
+ * 版本,并且对不存在的键取 gap/next-key 锁 ⇒ 并发登记天然串行。TiDB 自 v5 起默认就是悲观事务,同理;
66
+ * · 唯一残留的窄窗口是「TiDB 集群被显式配成乐观模式」。那一格由**第二道防线**兜底:`store_id` 的主键
67
+ * 唯一性是真正的裁决者,撞键被 {@link isDupKey} 翻成一句响亮的拒绝(见 putStore)。也就是说即便快照
68
+ * 读陈旧,结果也只会是「一方被响亮拒绝」,绝不会是一次静默的换属主。
69
+ */
70
+ import type { Pool as MySqlPool } from "mysql2/promise";
71
+ import type { Pool as PgPool } from "pg";
72
+ import { type SharedMemoryRequestContext, type SharedMemorySnapshot, type SharedMemoryStoreProvider } from "@sema-agent/core";
73
+ import { type SqlDriver } from "./sql-driver.js";
74
+ /** Per-org plane state (`connecting` / `unavailable` / `connected`). One row per org scope. */
75
+ export declare const SHARED_MEMORY_SCOPE_TABLE = "shared_memory_scope";
76
+ /** The model-facing store registry: one row per browsable library. */
77
+ export declare const SHARED_MEMORY_STORE_TABLE = "shared_memory_store";
78
+ /** The documents. One row per (store, path). */
79
+ export declare const SHARED_MEMORY_ENTRY_TABLE = "shared_memory_entry";
80
+ /**
81
+ * The plane word set — CLOSED, and consumed through an exhaustive `switch` ({@link foldPlaneState}) so
82
+ * adding a word is a COMPILE error at every fold site rather than a silent default arm.
83
+ */
84
+ export type SharedMemoryPlaneState = "connecting" | "unavailable" | "connected";
85
+ /** The read-authorization verdict for ONE caller. See the header's "AUTHORIZATION" section for why the
86
+ * `unavailable` arm may never collapse into an empty `granted`. */
87
+ export type SharedMemoryScopeGrant = {
88
+ kind: "granted";
89
+ scopes: readonly string[];
90
+ } | {
91
+ kind: "unavailable";
92
+ reason: string;
93
+ };
94
+ /** Injected org-membership fold. `principal` is `undefined` on a single-user deployment. */
95
+ export interface SharedMemoryScopeAuthorizer {
96
+ resolve(principal: string | undefined): Promise<SharedMemoryScopeGrant>;
97
+ }
98
+ /** One store registry row, as the admin plane writes it. */
99
+ export interface SharedMemoryStoreRecord {
100
+ /** The model-facing handle. GLOBALLY unique in the deployment — see {@link assertStoreId}. */
101
+ storeId: string;
102
+ /** The org scope that owns it (`org:acme`). */
103
+ scopeKey: string;
104
+ description: string;
105
+ /** Relayed verbatim to the model; core acts on it in no way whatsoever. */
106
+ writable: boolean;
107
+ }
108
+ /** One document, as the admin plane writes it. */
109
+ export interface SharedMemoryDocumentRecord {
110
+ storeId: string;
111
+ /** 🔴 属主围栏(codex 复审轮3 F1):写方必须说出**它以为的**属主 org。store id 在删除后可复用,只按
112
+ * id 寻址的话,一条迟到的重试会把上一任租户的内容注进新租户的库里。见 {@link SqlSharedMemoryStore.putDocument}。 */
113
+ scopeKey: string;
114
+ path: string;
115
+ content: string;
116
+ /** ISO 8601, round-tripped VERBATIM (core renders only a matching `YYYY-MM-DD` prefix). */
117
+ updatedAtIso?: string;
118
+ }
119
+ /** Tuning knobs. Both have deployment-safe defaults; neither is model-facing. */
120
+ export interface SqlSharedMemoryStoreOptions {
121
+ /** Clock injection (mtime column). Default `Date.now`. */
122
+ now?: () => number;
123
+ /** Hard ceiling on ONE `list` window, whatever the caller asks for. core asks for 51; a bigger
124
+ * request is an admin/HTTP caller. Default 500 — the container is bounded by declaration, not by
125
+ * hope (core puts no cap on the provider's window, so the provider must state its own). */
126
+ maxListLimit?: number;
127
+ }
128
+ export declare function assertStoreId(storeId: string): void;
129
+ export declare function assertScopeKey(scopeKey: string): void;
130
+ /**
131
+ * WRITE-side document-path admission. Deliberately at least as strict as core's READ-side listing gate
132
+ * (`isListablePath`): a row core would silently drop is refused here where an operator can see it. The
133
+ * ONE rule that is stricter on purpose is the non-BMP refusal — see the header's ORDERING section.
134
+ */
135
+ export declare function assertDocumentPath(path: string): void;
136
+ /**
137
+ * The dual-dialect implementation. Every statement is written out in BOTH texts at its call site
138
+ * (design/158 A12 判据:方言差异必须显式) — the `q(tidbSql, pgSql)` helper never builds one text.
139
+ */
140
+ export declare class SqlSharedMemoryStore implements SharedMemoryStoreProvider {
141
+ private readonly db;
142
+ private readonly authorizer;
143
+ private readonly now;
144
+ private readonly maxListLimit;
145
+ constructor(db: SqlDriver, authorizer: SharedMemoryScopeAuthorizer, opts?: SqlSharedMemoryStoreOptions);
146
+ private q;
147
+ /**
148
+ * ONE registry-binding snapshot per tool call, as core's seam requires: the state, the store identity
149
+ * set and each reader binding all come from this one call, so the set cannot change between "list the
150
+ * stores" and "resolve one". Document state stays live — which is why a store can still vanish between
151
+ * binding and read, the case {@link storeGone} names.
152
+ */
153
+ snapshot(ctx: SharedMemoryRequestContext, opts: {
154
+ signal?: AbortSignal;
155
+ }): Promise<SharedMemorySnapshot>;
156
+ /**
157
+ * 盘状态 + 库登记,**一个事务一个快照**(轮2 F3 修)。方言差异显式:
158
+ * · MySQL/TiDB 的默认隔离级别就是 REPEATABLE READ ⇒ 一个事务内的两条 SELECT 天然同快照;
159
+ * · PG 默认 READ COMMITTED,**每条语句各自取快照** ⇒ 必须显式抬到 REPEATABLE READ,否则这个事务
160
+ * 对本条竞态毫无作用(那正是"包了事务就以为一致"最容易踩空的地方)。
161
+ */
162
+ private readBinding;
163
+ private readerFor;
164
+ /** One keyset window. Always the PAGED shape — a large library must never be materialized per call. */
165
+ private listDocuments;
166
+ /** `null` = the document does not exist. Absence is a VALUE here; only a VANISHED STORE is an error. */
167
+ private readDocument;
168
+ private storeExists;
169
+ /** Declare an org's plane state. Absent row ⇒ `connected` (a never-provisioned org simply has no stores). */
170
+ putScopeState(scopeKey: string, state: SharedMemoryPlaneState, message?: string): Promise<void>;
171
+ /**
172
+ * Register (or re-describe) one browsable store.
173
+ *
174
+ * 🔴 codex 复审轮1 F1(真缺陷,已红先复现):**属主 org 不可变**。原式是一条 upsert,`scope_key` 跟着
175
+ * 被改写 —— 而文档行只按 `store_id` 编键、原地不动。于是一次撞 id 的导入(管理面手滑、重试、坏配置,
176
+ * 零竞态)就把 A 的整座库连内容一起交给了 B,同时 A 失去它。所以这里改成:已存在的 id 只允许它自己的
177
+ * org 更新描述/writable 位,换 org 一律响亮拒。真要迁库,那是一次需要显式审计的搬迁(先删再建,内容
178
+ * 由搬迁方决定重导还是丢弃),不能是一条 upsert 的副作用。
179
+ *
180
+ * 事务 + 行锁:属主判定与写入之间不留窗口(同一把锁 {@link deleteStore} 也拿)。并发到达的两个新建
181
+ * 会有一个撞 PK —— 那条也翻成同一句拒绝,不让驱动错误对象漏给调用方。
182
+ */
183
+ putStore(record: SharedMemoryStoreRecord): Promise<void>;
184
+ /**
185
+ * Write one document. Refuses when the store is not registered — an orphan document is unreachable
186
+ * through the read face, so accepting it would be a silent no-op the writer never learns about, AND a
187
+ * live landmine: whoever re-registers that id later inherits it.
188
+ *
189
+ * 🔴 codex 复审轮1 F2/F3 两修:
190
+ * · 帽在**入库**成立 —— 超 `SHARED_MEMORY_READ_CAP_BYTES` 的文档 core 横竖恒拒(从不截断),存下来
191
+ * 只是一行永远读不出来的死数据,却让每次读都要物化它。写面拒 = 那个帽在结构上为真。库里真有超大
192
+ * 文档的话,导入方分块——这是它自己就得做的事,因为模型面本来也读不到整份。
193
+ * · 存在性检查与写入**同事务同行锁**(与 {@link deleteStore} 争同一把):否则一次并发删除可以从检查
194
+ * 与插入之间穿过去,留下一行孤儿文档 —— 被删的内容在 id 复用时复活。
195
+ */
196
+ putDocument(record: SharedMemoryDocumentRecord): Promise<void>;
197
+ /** Remove one document. Scope-fenced like {@link putDocument} (a stale delete must not reach a
198
+ * successor tenant's library). Returns whether a row was actually there. */
199
+ deleteDocument(storeId: string, scopeKey: string, path: string): Promise<boolean>;
200
+ /** 属主围栏的共用断言(轮3 F1)。**不**覆盖同一个 org 内的"删了又建"代际重用——那不是跨租户面,
201
+ * 真要挡住需要一枚不可变的 generation token;这里如实说明边界,不假装它被覆盖了。 */
202
+ private assertOwnedBy;
203
+ /**
204
+ * De-register a store AND its documents, ATOMICALLY.
205
+ *
206
+ * 🔴 codex 复审轮1 F3(真缺陷):原式是两条独立语句。崩在中间会留下「登记着但被悄悄清空」的库,而
207
+ * 并发的 {@link putDocument} 能从检查与插入之间穿过去留下孤儿行 —— 被删的内容会在这个 id 被重新登记时
208
+ * 复活。现在两条删除同事务,并且**先拿注册行的行锁**:写面争的是同一把锁,两者因此串行。
209
+ *
210
+ * 🔴 轮3 F1 同族:必须带 `scopeKey` 围栏。id 删后可复用,而一条**迟到的** deleteStore 若只按 id 寻址,
211
+ * 会把继任租户刚建好的库连内容一起抹掉 —— 一次歧义的重试变成一次跨租户的破坏性操作。
212
+ */
213
+ deleteStore(storeId: string, scopeKey: string): Promise<void>;
214
+ }
215
+ /** MySQL-protocol (MySQL / TiDB) binding. */
216
+ export declare class TiDBSharedMemoryStore extends SqlSharedMemoryStore {
217
+ constructor(pool: MySqlPool, authorizer: SharedMemoryScopeAuthorizer, opts?: SqlSharedMemoryStoreOptions);
218
+ }
219
+ /** PostgreSQL binding. */
220
+ export declare class PgSharedMemoryStore extends SqlSharedMemoryStore {
221
+ constructor(pool: PgPool, authorizer: SharedMemoryScopeAuthorizer, opts?: SqlSharedMemoryStoreOptions);
222
+ }
223
+ //# sourceMappingURL=shared-memory-store-sql.d.ts.map