import type { Pool as MysqlPool } from "mysql2/promise"; import type { Pool as PgPool } from "pg"; import type { SessionStore, SessionPolicyStore, FileHistoryStore, WorkflowJournalStore, ToolResultStore, SessionCaptureRecordStore } from "@sema-agent/core"; import { type OutcomeSink } from "./file-outcome-sink.js"; import { type InboxWarn } from "./workflow-run-store-sql.js"; import { FileRunStore } from "./file-run-store.js"; import { type OneTimeMigrationReport } from "./one-time-migrations.js"; import { LocalCheckpointStore } from "./local-checkpoint-store.js"; import { FileResumeAnchorStore } from "./file-resume-anchor-store.js"; import { type ApprovalExemptionStore } from "./approval-exemption-store.js"; import { type ApprovalNonceStore } from "./approval-nonce-store.js"; import { type ApprovalAskStore as ApprovalAskStoreType } from "./approval-ask-store-sql.js"; import { type MemoryOptOutGrantStore } from "./memory-optout-grant-store-sql.js"; import { type SendFileLedger } from "./send-file-ledger.js"; import { TiDBResumeAnchorStore, PgResumeAnchorStore } from "./resume-anchor-store-sql.js"; import type { ServiceConfig } from "../config-types.js"; import { TiDBRunStore, PgRunStore } from "./run-store-sql.js"; import { TiDBCheckpointStore, PgCheckpointStore } from "./checkpoint-store-sql.js"; import { TiDBImageIndex, PgImageIndex } from "./image-index-sql.js"; import { TiDBImageBake, PgImageBake } from "./image-bake-store-sql.js"; import { TiDBLeaderRunStore, PgLeaderRunStore } from "./leader-run-store-sql.js"; import type { DeviceStore } from "../device-store.js"; import { TiDBBreakerState, PgBreakerState } from "./breaker-state-sql.js"; import { TiDBCostQuota } from "./tidb-cost-quota.js"; import { PgCostQuota } from "./pg-cost-quota.js"; import { TiDBRateLimiter } from "./tidb-rate-limiter.js"; import type { CounterDegradeHook } from "./write-behind-counter.js"; import { PgRateLimiter } from "./pg-rate-limiter.js"; import { type SqlSharedMemoryStore, type SharedMemoryScopeAuthorizer } from "./shared-memory-store-sql.js"; import { type AdoptionLogStore } from "./adoption-log-sql.js"; import { type SqlRetentionStore, type SqlRetentionStoreOptions } from "./retention-store-sql.js"; import { type RetentionLaneStore } from "./retention-lane-store-sql.js"; import type { PermissionRuleStoreBundle } from "../rules-consent.js"; /** boot 期的一条日志座(级别由**座位**决定,不由参数携带):`createStoreBackend` 的 `onWarn` / `onError` * 两参与 `LocalBackend` 的同名字段共用此形。装配层把它们分别接到 `logger.warn` / `logger.error` 上。 */ export type BootLogSink = (msg: string, meta?: Record) => void; export type RunStore = TiDBRunStore | PgRunStore | FileRunStore; /** E18 resume-at eventId→entryId anchor map — tidb/pg/local 3-backend (works LOCAL; needs only the session tree + * this map, no cloud-only checkpoint). Union = the three nominal twins; consumers use put/resolve/deleteBySession. * FileResumeAnchorStore is the FILE-backed local twin (P0.5 variant-1; survives restart). */ export type ResumeAnchorStore = TiDBResumeAnchorStore | PgResumeAnchorStore | FileResumeAnchorStore; export type { ApprovalExemptionStore } from "./approval-exemption-store.js"; export type { ApprovalNonceStore } from "./approval-nonce-store.js"; /** [ref]([ref] v3.1 §3.0-§3.2 流内审批协议持久层):`ApprovalAskStore` 接口 + 行/补丁类型。 * 车3 刀 3a 起 `StoreBackend.approvalAsk()` 已在(见接口体的那条注)。 */ export type { ApprovalAskStore, AskRow, NewAskRow, AskTransitionPatch, DecideAskInput, DecideResult, ExpireResult, BindGateInput, BatchRow, AskDecision } from "./approval-ask-store-sql.js"; /** E6 SessionPolicyStore — core's interface PLUS the service `deleteBySession` (E21 purge; only the durable twins carry * it, core's InMemory omits it → optional). tidb/pg = durable twins; local = core's InMemorySessionPolicyStore (works * local — rules need no cloud-only infra). The shared env-gated equivalence suite keeps the twins byte-matched to InMemory. */ export type ServiceSessionPolicyStore = SessionPolicyStore; /** [ref] per-edited-file rewind FileHistoryStore — core's interface PLUS the service extras only the SQL twins * carry (optional so the local lane's core `FileFileHistoryStore` still fits). Replaces the retired E19 * whole-tree `ServiceFileSnapshotStore` ([ref]: the boundary covers the TRACKED set only — files this * session edited; there is no whole-tree manifest anymore). Also the 2c history-transfer store * (exportHistory/importHistory + the blob negotiation faces below). */ export type ServiceFileHistoryStore = FileHistoryStore & { /** 2c sync blob-face marker — see SqlFileHistoryStore.syncBlobFaces (codex R2-high: name-probing * `putBlob` on an arbitrary store collides with the local core store's PRIVATE void helper). */ syncBlobFaces?: true; /** E21 purge / overwrite-dst sync wipe: drop the WHOLE scope incl. v1 baselines + DV-14 markers * (`reap(scope, [])` deliberately retains those while the scope lives — not a purge). SQL twins only. */ deleteBySession?(scope: string): Promise; /** Async byte-GC for the file_history_blob domain (orphans left by reap/purge/lost mint races; grace-window * protected — never a synchronous external-store delete). Reaper-driven; SQL twins only. */ sweepOrphanBlobs?(): Promise; /** 2c blob negotiation: upload one content-addressed blob ahead of importHistory. SQL twins only. */ putBlob?(hash: string, bytes: Uint8Array): Promise; /** 2c blob read face (scope-checked by the route against the session's history graph). SQL twins only. */ getBlob?(hash: string): Promise; /** The subset of `hashes` already present — a cheap SQL INDEX probe (the import presence pre-check, * never N full getBlob GETs). SQL twins only. */ hasBlobs?(hashes: string[]): Promise>; }; /** SVC-2 (core CORE-7) WorkflowJournalStore — core's interface PLUS the service `deleteByRun` (GC extra). tidb/pg = * durable SQL twins (cross-replica resume journal); local = the service {@link FileWorkflowJournalStore} * (single-box but RESTART-durable crash-safe JSONL, replacing core's InMemory). The shared equivalence suite keeps * all three byte-matched (scope guard / idempotent-per-ordinal / oversize **refusal** —— S-194 / core 7.12.0 * [ref] 起两臂一律抛 `workflow.journal_oversize`,判长口径归 core `assertJournalEntryFits` 单一属主). */ export type ServiceWorkflowJournalStore = WorkflowJournalStore & { /** codex R1-H2(journal 读面界):分页+单行字节门的投影读——SQL 端 LIMIT/OFFSET 定行数、LENGTH 门 * 定单行(超 maxResultBytes 的行 resultJson=null 只回 resultBytes,读面标 truncated)。HTTP 读面 * 优先走此面;缺席(旧实现/in-memory)回落全量 load(元素少的店可受)。 */ loadPage?(runId: string, scope: string, opts: { offset: number; limit: number; maxResultBytes: number; }): Promise>; deleteByRun?(runId: string): Promise; /** SVC-2 GC sweep (durable twins only): purge entries older than `maxAgeMs` — wired into the reaper to bound the * TaskResult-bearing table (the per-run deleteByRun has no run-store reap hook). Local InMemory omits it. */ reapExpired?(now: number, maxAgeMs: number): Promise; }; export type ImageIndex = TiDBImageIndex | PgImageIndex; export type ImageBake = TiDBImageBake | PgImageBake; /** [ref] 车4 件1:durable leader-run 登记表的双方言孪生(union 同族——TS 私有字段让具体类名义上不同)。 */ export type LeaderRunStore = TiDBLeaderRunStore | PgLeaderRunStore; /** The FULL checkpoint store (core CheckpointStore + the service operator-queue/ctx methods listPending/ * listByScope/findPendingTokenBySession/peekPendingScope/peekScopeByToken/putCtx/getCtx/reapCtx) — the SQL twins carry them, * and the LOCAL lane now does too (core FileCheckpointStore + the service half — TOC plan-mode / * durable HITL work on one box). */ export type CheckpointStoreFull = TiDBCheckpointStore | PgCheckpointStore | LocalCheckpointStore; /** The full tool-result store: core `ToolResultStore` + the service maintenance extras. The SQL twins carry * both extras; the LOCAL lane returns core's `FileToolResultStore` (core 1.219 — durable across a * process restart, the TOC "ReadToolResult(ref) 恒空" fix) which since core 5.28.0 carries `deleteBySession` * (owner sidecar 判属主,无边车存量计 unattributable 不删)but still NOT `reapOlderThan` — consumers must * optional-call (`store.reapOlderThan?.()`,local 上恒 no-op)。(R5 复扫纠:旧句「carries NEITHER」自 * core 5.28 起半假——本类型注是消费方第一落点,与 LocalBackend 内部注必须同真。) * Structural (not a nominal union) so all three backends flow through one seam. */ export type ToolResultStoreFull = ToolResultStore & { /** TTL sweep (SQL twins only): purge rows older than the cutoff. Local file store omits it (CC posture: * a single user's tool-result files persist like transcripts; bounded by being text previews). */ reapOlderThan?(cutoffMs: number): Promise; }; /** Cross-replica counter twins expose the write-behind lifecycle (startRefresh/stop) main.ts drives. */ export type BreakerStateStore = TiDBBreakerState | PgBreakerState; export type CostQuotaStore = TiDBCostQuota | PgCostQuota; export type RateLimiterStore = TiDBRateLimiter | PgRateLimiter; /** * S-215 —— capture opt-out **记录载体的按平面取用座**。形与 core 的 * `RunnerDeps.memoryCaptureRecordStore`(`(plane: { controlDir }) => SessionCaptureRecordStore`)同构, * 多一个**不指名平面**的缺省臂:`src()` = 「本部署记忆引擎自己那一只平面」。 * * 🔴 为什么是工厂而不是一只实例(S-215 codex r1 [high] 验真后改形):core 的记忆平面**不止一个** * (`prepare-memory.js` 的 personal 平面自带 `derivePersonalControlDir(engineRoot)`,与 project 平面的 * controlDir 不是同一只)。返回单只实例 ⇒ server 把所有平面折到一只目录上,于是升级前落在 personal * 控制面的旧 opt-out 记录在升级后读作**缺席**=采集恢复(隐私面的静默 fail-open)。按平面取用后, * file 载体与 core 的缺省**逐字节同址**,唯一的变化是这个席位现在**被声明了**(core 的能力信号要的就是它)。 * SQL 载体**刻意忽略** plane:它按 session_id 键控、跨平面全局 —— opt-out 是**会话**事实而不是平面事实 * (逐字理由见 boot/runner-deps.ts 该席的注)。 */ export type SessionCaptureRecordSource = (plane?: { controlDir: string; }) => SessionCaptureRecordStore; export interface StoreBackend { /** Protocol-truth kind (clay 2026-07-06 wording): "mysql" = any MySQL-protocol server (MySQL / TiDB / * MariaDB) behind the one mysql2 pool — the startup summary prints durable(mysql) for all of them. */ readonly kind: "mysql" | "pg" | "local"; /** Connect + create schema (also the reachability probe). */ ensureSchema(): Promise; close(): Promise; run(): RunStore; /** E18 resume-at eventId→entryId map. REQUIRED on all backends (works local — needs only the session tree + this * map, no cloud-only checkpoint), unlike the optional cloud-only stores below. */ resumeAnchor(): ResumeAnchorStore; /** Per-session per-toolName approval exemption ("本会话不再询问") — an approval MEMORY the F4 * ask-gate consults after deny/neverAuto. REQUIRED on all backends (works local, like resumeAnchor). */ approvalExemption(): ApprovalExemptionStore; /** S-280 直连门审批证明的一次性消费记录(`plan_review` 的 nonce)。REQUIRED on all backends —— 与 * `approvalExemption` 同姿势:tidb/pg = SQL 双方言(单类双语句),local = File 追加日志 + 压实。 * **不能**退化成进程内易失形:一次重启若把记录抹掉,JWT TTL 窗内的重放就复活了,而那正是本店要关的洞。 */ approvalNonce(): ApprovalNonceStore; /** [ref]([ref] §3.0-§3.2)流内审批协议持久层:每个流内工具调用一行的 ask 状态机 + 批行。 * REQUIRED **且持久** on all backends — tidb/pg = SQL 双方言 twin;local = {@link FileApprovalAskStore}。 * * 🔴 **「持久」是本口的契约,不是某条腿的巧合**(S-384):`resolveStreamApprovalGate` 自此**不再** * 单独判一次 ask 账的持久性 —— 那条合取项(旧 `volatile_ask_ledger`)存在的唯一理由是 local 车道 * 当年交的是 `InMemoryApprovalAskStore`(两个进程内 Map ⇒ 重启同时丢重放基准、丢已接受的决议; * 「一次真实的人类批准凭空消失」与「对账便利丢失」不是一个量级)。File 形落地后三条腿全是持久店, * 那条合取项没有任何可以让它为假的输入 ⇒ 按「修完让规则更少」删掉,持久性的义务搬到**这里**。 * ⇒ **新加一条 backend 腿时必须给持久店**:进程内易失形会让能力面 `streamApproval` 对消费方撒谎 * (它承诺的是「稍后还能批、崩了行还在」)。机器钉 = `wiring-governance-operator.test.ts` 的 * 「三腿 approvalAsk 全持久」格。 * * 无 backend 的 env-only worker 压根没有 `StoreBackend` ⇒ 协调器拿到 undefined askStore ⇒ 车2 的 * D1 逐字现行为(store 缺席 = 现行 `tool_approval` 活卡腿一字不变)。 */ approvalAsk(): ApprovalAskStoreType; /** * [ref] 车二(core 5.18.0 [ref] + 5.22.0 [ref]):**持久化权限规则店**(「不再询问」车道) * 三面 —— 规则桶 provider / durable 审批记录 / server 自铸的 CC 导入票。 * * 🔴 **可选**(与上面那些 REQUIRED 家族不同),缺席是一个真答案:core 的 `RunnerDeps.permissionRuleStore` * 本身就是 optional,缺席 ⇒ 引擎的 `permissionRules.storeWired` 如实报 `false`,ask 帧不投 `ruleOffers` * (发一格按下去无处可兑的「不再询问」= wire 谎言,比缺席更坏)。 * * [ref](core 7.5.0):束里的规则那一格是 **durable 分区后端**(`PermissionRuleStoreBundle.durable`), * 引擎接缝上的统一店由 `main.ts` 用 `createPermissionRuleStoreProvider({ durable })` 合成一次 —— * 分区构成(durable / org / session)是**部署**决定,backend 只交它自己那一格。 * * `local` 车道**刻意不给 in-memory twin**:一条规则是「人授权过的持久事实」,进程内 Map 形会在重启时 * 静默丢掉那份授权,而消费端(下一次同命令的 ask)读到的是「没有规则」——那是**放宽面**上的静默降级。 * ✅ **[ref] §1 已落 File 形**(那条注写下的解除条件逐字是「先落 File 形,core 现成的 * File durable 分区 provider 即可当模子」——本车照办):`local` 现在返回一个**跨重启存活**的 * 三面束,storeWired 在单机车道上真为 true。in-memory twin 的禁令不变,它禁的是「会遗忘的店」, * 不是「单机的店」。 */ permissionRule(): PermissionRuleStoreBundle | undefined; /** SendUserFile scope↔object ledger (multi-tenant list/revoke handle; the hashed key segment hides the * mapping from URLs). REQUIRED on all backends (works local — one JSONL, like approvalExemption). */ sendFileLedger(): SendFileLedger; /** E6 durable SessionPolicyStore (operator-tightened per-session tool rules). REQUIRED on all backends — works local * (local = core's InMemorySessionPolicyStore), like resumeAnchor. Backs core's RunnerDeps.sessionPolicyStore. */ sessionPolicy(): ServiceSessionPolicyStore; /** [ref] per-edited-file rewind FileHistoryStore (trackEdit/annulTrack/snapshot/restore over the tracked set; * also the 2c history-transfer store). REQUIRED on all backends (local = core's file-backed * `FileFileHistoryStore` under the data root). Backs core's RunnerDeps.fileHistoryStore. */ fileHistory(): ServiceFileHistoryStore; /** [ref] §3.1 / [ref] §一([ref] S-2):per-principal **memory-capture opt-out 授权表** * (`memory_optout_grant`)—— `RuntimeCaps.allowMemoryOptOut` 的本地 verdict 源(三层折叠:per-principal * 行 → 部署缺省哨兵行 → 代码缺省 allow)+ `/v1/admin/memory-optout` 四端点的持久层。 * * 🔴 **可选**(与 permissionRule 同族):SQL 后端 = 双方言 twin;`local` 车道 **undefined**,且缺席是一个 * 真答案 —— 没有 verdict 源时 `allowMemoryOptOut` 键不合成(core 读「无 per-principal 限制」,`open` 姿态 * 下 opt-out 照常生效),而 `MEMORY_CAPTURE_POLICY=governed` 在启动期对无源部署拒启 * (`boot/runtime-caps.ts` `assertMemoryCapturePolicyWirable`)。刻意**不给 local 一个 in-memory twin**: * 一条授权行是「operator 写下的合规事实」,进程内 Map 会在重启时静默丢掉它,下一次解析读到的是 * 「没有行 ⇒ 代码缺省 allow」—— 那是**放宽面**上的静默降级(permissionRule 那条禁令的同一理由)。 * File 形候真实的 local+governed 部署需求出现再落(候需求档)。 */ memoryOptOutGrant(): MemoryOptOutGrantStore | undefined; /** [ref]②([ref] §2.1b,core 7.0.2 [ref] 件2)/ S-215:会话 capture opt-out **记录**的载体 —— * core `RunnerDeps.memoryCaptureRecordStore` / `MemoryEngineOptions.captureRecordStore` 的持久层。 * 与 §3.1 的**授权表**不是一个轴:那张管「谁**可以** opt-out」(operator 写的 verdict),这张管 * 「谁**已经** opt-out」(用户写的一次性单向记录)。 * * 返回的是**按平面取用的源**({@link SessionCaptureRecordSource}),不是一只实例 —— core 的记忆平面 * 不止一只,折平会挪动旧记录的地址(理由全文见该类型的头注)。 * 载体按车道分形,但**三条车道都供**(S-215 起):SQL 两支 = 双方言 twin(`session_capture_optout`, * Promise 形三腿,池即依托 ⇒ 无条件供,`plane` 刻意忽略);`local` = core 的控制面文件三腿 * `fileSessionCaptureRecordStore(controlDir)` 按平面现造,依托是**记忆引擎的控制面目录** ⇒ 记忆面暗的 * 部署上该平面不存在,于是这一支**随引擎接线门缺席**(唯一的 `undefined` 成因,逐字理由见 LocalBackend 的实装注)。 * * 🔴 修前 local 恒 `undefined` 且注称「缺席是一个真答案」—— 那句话在**事实层面**成立(单机的正确载体 * 确实就是文件三腿)、在**接线层面**是假的:core 的 `captureCarrierUnsupported` * (core 7.13.0 `prepare-task.js`)判的是「这个席位在不在」而不是「文件形能不能用」,叠上 * `isRemoteExecutionEnv` 的接口形鸭子判(`remote-env.js`)把本仓 host lane 一并算作 remote, * 于是单机默认部署上 `memoryCapture:"off"` 两条 ingress 结构性不可达(B-085)。 * 刻意仍不给 local 一个**内存** twin:内存形会在重启时把一条隐私记录静默丢掉(比文件三腿严格更差)。 */ sessionCaptureRecords(): SessionCaptureRecordSource | undefined; /** Retired-epoch byte hygiene: GC grace-passed orphan `snapshot_blob` rows/objects — the whole-tree snapshot * epoch is gone (its manifest index table retired with it), so EVERY row in that domain is an orphan and its * BYTES (incl. MinIO objects) would otherwise outlive the epoch forever. SQL backends only; reaper-driven. */ legacySnapshotBlobSweep?(): Promise; /** 🔴 **一次性迁移(随 7.75.0 连同 `one-time-migrations.ts` 一起从接口上删)** —— 本版三处退役落在 * 已写进库里的字节上,升一次级改完、兼容臂当场删干净(硬 breaking 纪律:持久数据面 = 响亮拒 + * 一次性迁移,窗过即删,永不长期双读)。逐条语义、幂等与拒启臂见该模块头注。 * * **必选,不是 optional**:漏实装一支后端 = 那条车道的存量字节永远停在旧读法上,而读腿已经只认新的 * ⇒ 必须是编译期错误,不是运行期的一次静默跳过。 */ runOneTimeMigrations(): Promise; /** SVC-2 (core CORE-7) durable WorkflowJournalStore — the cross-replica resume journal for an LLM-authored workflow * (RunWorkflowOptions.journalStore). REQUIRED on all backends (tidb/pg = durable twins; local = core's * InMemoryWorkflowJournalStore, single-replica/in-process). * * ✅ WIRED (was "NOT YET WIRABLE, as of core 1.144.0" — that claim went STALE and sat here misleading readers * until a 2026-07-25 doc-rot sweep caught it). core 1.145.0 added the seam this comment was waiting for: * `RunnerDeps.workflowJournalStore` (now `@sema-agent/core` `dist/core/types.d.ts`) + `journalStore` on the * run_workflow tool deps (`dist/orchestration/run-workflow-tool.d.ts`), and the boot chain consumes it * end-to-end: built in `boot/workflow-orchestration.ts` (`const workflowJournalStore = …backend.workflowJournal()`), * threaded into RunnerDeps (`boot/runner-deps.ts` 的 `workflowJournalStore`), reaped on the maintenance tick * (`boot/reapers.ts` 同名字段), and read by `GET /v1/workflows/:id/journal` (`http/routes/workflows.ts`, [ref]). */ workflowJournal(): ServiceWorkflowJournalStore; /** [ref] §1 consumption sink for `RunnerDeps.onTaskOutcome` facts. REQUIRED on all backends: * tidb/pg = the outcome-ledger twins (SQL rows, mapped via coreOutcomeToLedgerRow + verbatim core_outcome * JSON); local = an owner-only JSONL File sink. Read-only v1 — records facts, drives no policy. */ outcomeSink(): OutcomeSink; /** P1 (fleet failover): durable cross-replica WorkflowRunStore + completion-inbox twins. OPTIONAL — * SQL backends only (local keeps the File pair: single box, no cross-replica surface). main.ts prefers * these under WORKFLOW_RUN_STORE=auto (the default). */ workflowRun?(onWarn?: InboxWarn): import("@sema-agent/core").WorkflowRunStore; completionInbox?(onWarn?: InboxWarn): import("../orchestration/workflow-completion-inbox.js").WorkflowCompletionInbox; /** 1.108 review fix (lens③ HIGH): the notify-JOURNAL twin — third leg of the same axis (a SQL run store + * inbox with a replica-LOCAL File journal stranded a dead replica's un-acked notify forever). */ notifyJournal?(): import("../orchestration/workflow-notify-journal.js").WorkflowNotifyJournalStore; /** S-185 车CM:workflow 出身 park 的**有界 join** 索引(子会话 id → 它所属的 workflow run)。 * 与 `workflowRun`/`completionInbox`/`notifyJournal` 同一条轴 —— SQL 后端 = 跨副本双生;`local` 车道 * 缺席**是一个真答案**:那里的索引家在 run 店同一间屋子里(`LocalWorkflowAgentSessionIndex`,文件账本 * 或纯内存),不需要经过 backend。契约与单写者纪律的属主 = orchestration/workflow-agent-session-index.ts。 */ workflowAgentSessionIndex?(): import("../orchestration/workflow-agent-session-index.js").WorkflowAgentSessionIndex; /** Cloud-only stores below are OPTIONAL: the `local` (in-memory/file) backend omits them (it has no core * in-memory twin carrying the service extras, and these are fleet/multi-replica concepts), so a local HTTP * service degrades EXACTLY like today's no-DB env (the consumers gate on `backend?.X?.()`). TiDB/PG provide all. */ /** [ref] —— org 共享记忆库的供给面(core `RunnerDeps.sharedMemoryStores` + `/v1/shared-memory/*` * 只读面)。授权折叠**注入**:谁属于哪个 org 归 server 的目录面判,持久层只按判决出的 scope 集查表。 * * 🔴 local 车道诚实缺席(不是"待补"):这个面的全部意义是**跨用户**共享一个团队库,而 local 后端是 * 单机单用户、没有租户边界、也没有 org 目录源。造一个 file 形等于给一个只有一个人的部署做"团队共享", * 能力面会因此对消费方说谎。要在单机真上这条面,先接一个目录源(`MEMORY_ORG_DIRECTORY_JSON`)并选 * SQL 后端 —— 那时它自然点亮。 */ sharedMemoryStore?(authorizer: SharedMemoryScopeAuthorizer): SqlSharedMemoryStore; checkpoint?(logger?: { info?(msg: string, meta?: unknown): void; }): CheckpointStoreFull; toolResult?(): ToolResultStoreFull; imageIndex?(): ImageIndex; imageBake?(): ImageBake; /** * [ref] 车4 件1(台账 P1-9):durable leader-run 登记表。**SQL 后端专有**,`local` 刻意省略。 * * 🔴 local 缺席不是欠账、也不是「以后补个 File 形」:leader run 是 N worker fan-out + 真 git push, * 而 config 层已经把「LEADER_ENABLED=true 且无外部 SQL 店」整个组合**拒启**了(件2 门②)—— * 所以 local 车道上根本不存在一个需要这张表的 leader 端点。消费点按 `backend?.leaderRun?.()` 取, * 缺席 ⇒ endpoint 落到显式的单副本内存降级臂(见 `leader/endpoint.ts` 的 store 缺席注)。 */ leaderRun?(): LeaderRunStore; /** * device lane 的四表店(device-executor-lane-v2 §6,车A-2 交店 / 车A-4 接线)。**SQL 后端专有**, * `local` 刻意省略 —— 与 {@link leaderRun} 逐字同一条理由:config 层已经把「`REMOTE_EXEC=device` 且 * 无外部 SQL 店」整个组合拒启了(`device-durable-store` 不变量),所以 local 车道上根本不存在一个需要 * 这四张表的 device worker。消费点按 `backend?.device?.()` 取,缺席 ⇒ `boot/device-lane.ts` 的 * `assertDeviceLaneWired` 响亮拒启(**不是**静默落进程内 stub)。 */ device?(): DeviceStore; /** * [ref] —— 托管留存的**破坏性**三方法(core `ManagedRetentionCapability` 的 server 侧真身)。 * **SQL 后端专有**,`local` 刻意省略:那三条腿是跨十余张表的单事务级联,file/in-memory 形没有事务面 * 也没有那些表,所以它诚实声明 `retention:"none"`(见 `local-session-store.ts` 的同款注)。 * * `taskAttachmentTable` 必填、无默认值 —— boot 只在配了对象存储时才 ensure 那张表,一条盲发的 * DELETE 在无 MinIO 的部署上会以 unknown-table 打红整轮 sweep(理由逐字见 * `retention-store-sql.ts` 的 `SqlRetentionStoreOptions`)。 */ retention?(opts: SqlRetentionStoreOptions): SqlRetentionStore; /** * [ref] 车2 —— 留存执行面的**非破坏性**半场:sweep 租约、legal-hold 的放置/解除、审计表的读与 * 非破坏行的写。同样 SQL 专有(它写的三张表只在 SQL schema 里)。 * * 🔴 与上一格**分家**是有意的:拿到本对象的消费方(operator 路由)结构上删不掉任何业务数据。 * 两面合并会让「拿到这只店 = 能删」这条读法失真。 */ retentionLane?(): RetentionLaneStore; breaker?(onWriteFail?: (streak: number) => void): BreakerStateStore; costQuota?(limitMicroUsd: number, windowMs: number, onDegraded?: CounterDegradeHook): CostQuotaStore; rateLimiter?(limit: number, onDegraded?: CounterDegradeHook): RateLimiterStore; /** Session store (caller wraps caching). DB-backed on tidb/pg; the in-memory/file `LocalSessionStore` on local. */ session(): SessionStore; /** The raw mysql2 pool — ONLY on the TiDB backend (undefined on PG). For the few TiDB-specific raw-SQL * consumers outside the store abstraction (session audit, degenerate-output instrument); those features are * no-ops on PG until ported. */ mysqlPool(): MysqlPool | undefined; /** S3-TOB(设计 §1.4):the raw PG pool for the memory-engine DB backend's PgQueryFn binding (mirror of * mysqlPool). undefined on non-PG backends. */ pgPool(): PgPool | undefined; /** [ref] form b(纯身份重绑)的 SQL 收编日志。**SQL 后端专有**(mysql/pg),`local` 刻意省略: * 收编重绑的是**多租身份轴**,而 local 后端整个数据根就属那一个用户、库里没有 owner/scope 分区面 * ——在那儿开这道口只会让一个够不到任何东西的动作看起来可用。消费点按 `backend?.adoptionLog?.()` * 缺席即 501(诚实拒绝),与其它云专有 store 同姿势。 */ adoptionLog?(): AdoptionLogStore; /** S10 (SILENT-FALLBACK P1): the DB server's wall clock in epoch ms — the reaper probes it against the local * clock to expose replica clock skew (the write-behind counters bucket on LOCAL floor(now/windowMs); skew * splits a fleet window into disjoint buckets = soft-limit leak). OPTIONAL: local backend omits (no fleet). */ dbNowMs?(): Promise; } /** (c)(clay 拍 a+c,2026-07-27;[ref] 换店后域=file_history_blob,门与旋钮名不变)云形 blob 姿势门—— * boot 时调用(boot/stores.ts),纯函数可单测。SQL 后端(mysql|pg)把 blob 字节落单行的形已被两役 * 实证撞 mysql-协议包墙(D-1 附件 → 对象存储裁定):大字节归对象存储。per-file 历史的 blob=单文件 * 字节,同一堵墙(>6MiB 的被 track 文件在 TiDB 上落不进 SQL 行 ⇒ trackEdit 拒 ⇒ 默认 DV-14 下该次 * 编辑被拒)。云形缺 MinIO ⇒ fail-loud(启动了才是假安全);`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true` = * 显式接受 SQL 店 + per-blob 帽(单机/测试台形)。local 后端不经此门(file 店,无 SQL 包墙)。 */ export declare function assertCloudSnapshotBlobPosture(kind: "mysql" | "pg" | "local", config: Pick): void; /** * 启动日志里「跨副本 or 进程内」那一格的标签。 * * 🔴 判据必须是**那个 store 本身在不在**,不是「有没有 backend」(2026-07-31 缝合审)。 * `LocalBackend` 明确省略 `costQuota`/`rateLimiter`(见它类尾那行 "intentionally absent"), * 而 `DB_BACKEND=local` 是**裸 boot 的默认值** —— 原来 main.ts 那两行用 `backend ? "shared()" : …` 判, * 于是单机默认形配了 `RATE_LIMIT_PER_MIN` 会打出 `shared()`,实际跑的是每副本一份的进程内限流器。 * 运维扩到 N 副本时读这行判定"限流是跨副本的",真实额度是 N 倍。 * (同一个说谎病族在紧邻的 `breakerState` 上修过一次——"was hard-coded 'shared(tidb)' — lied under * DB_BACKEND=pg"——`session:` 也跟着修了,这两行没跟上。顺带:原来的 `shared()` 括号是空的, * 而兄弟行都写 `shared(${kind})`。) */ export declare function counterStoreLabel(store: unknown, kind: string | undefined): string; /** Build the durable-store backend for `config.dbBackend`. Does NOT connect (the pool connects lazily; the * reachability probe is the caller's first `ensureSchema`). Returns undefined when no DB is configured. * * `onWarn` (additive, optional) is the boot-time warning sink the LOCAL backend needs for core's * permission-rule DISCLOSURE face ([ref] §1): an unreadable / checksum-mismatched / symlinked rule file * answers ZERO rules and says so — swallowing that turns a fail-closed degrade into a silent one. * `onError` (additive, optional; F-D) is the **error-level** twin of that seat — 件6 的 * `file_run_store_hydrate_unreadable` 按成文承诺是 error 级,warn 座满足不了那句话。两座都是**加项**: * 既有一/二参调用方逐字不变(缺席 ⇒ 该披露退回 warn 座 / 只剩 fail-open 遥测那一半)。 */ export declare function createStoreBackend(config: ServiceConfig, onWarn?: BootLogSink, onError?: BootLogSink): StoreBackend | undefined; /** boot 期打开 store 后端的**统一入口**:构造 + ensureSchema 同罩一层降级臂。 * * 为什么必须把 `createStoreBackend()` 也罩进来(2026-07-28 修):clay 1.292 拍的口径是「裸 boot * **默认推导**的 local 在只读文件系统/mkdir 失败时不得拒启」,但原先 try 只罩 `ensureSchema()`—— * 而 LocalBackend 的 `ensureSchema` 是**空实现**,真正会抛的 mkdir + 数据根 BootLock 在构造函数里、 * 落在 try 之外。于是那条降级臂对 local 形从来没生效过,注释宣称的行为与代码真做的事撕裂。 * * 降级条件不变(两形同一姿势):`sessionBackend=auto`(配置的 DB 不可达,S5 原形)或**默认推导**的 * local(`dbBackend=local && !dbBackendExplicit`)。**显式** DB_BACKEND=local / tidb 仍 fail-loud—— * operator 要了 durable,静默丢=丢数据。 */ export declare function openStoreBackendWithFallback(config: ServiceConfig, logger: { warn: (msg: string, meta?: Record) => void; error?: (msg: string, meta?: Record) => void; }): Promise<{ backend: StoreBackend | undefined; degraded: boolean; }>; //# sourceMappingURL=store-backend.d.ts.map