@sema-agent/server 7.31.0 → 7.32.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 (80) hide show
  1. package/dist/boot/lexical-path-env.d.ts +10 -0
  2. package/dist/boot/lexical-path-env.js +88 -0
  3. package/dist/capabilities/oa-tools.d.ts +15 -0
  4. package/dist/capabilities/oa-tools.js +54 -0
  5. package/dist/capabilities/tool-defer.d.ts +10 -0
  6. package/dist/finance/cost-taxonomy.d.ts +34 -0
  7. package/dist/finance/cost-taxonomy.js +26 -0
  8. package/dist/http/routes/capabilities.js +11 -0
  9. package/dist/http/routes/memory-bundle.d.ts +109 -0
  10. package/dist/http/routes/memory-bundle.js +178 -0
  11. package/dist/http/server.d.ts +29 -3
  12. package/dist/http/server.js +39 -4
  13. package/dist/main.js +16 -0
  14. package/dist/memory-bundle-engine.d.ts +64 -0
  15. package/dist/memory-bundle-engine.js +67 -0
  16. package/dist/plugins/approval-store-sql.d.ts +116 -0
  17. package/dist/plugins/approval-store-sql.js +151 -0
  18. package/dist/plugins/file-workflow-journal-store.d.ts +12 -0
  19. package/dist/plugins/file-workflow-journal-store.js +12 -0
  20. package/dist/plugins/pg-approval-store.d.ts +9 -0
  21. package/dist/plugins/pg-approval-store.js +9 -0
  22. package/dist/plugins/pg-breaker-state.d.ts +8 -0
  23. package/dist/plugins/pg-breaker-state.js +8 -0
  24. package/dist/plugins/pg-checkpoint-store.d.ts +10 -0
  25. package/dist/plugins/pg-checkpoint-store.js +10 -0
  26. package/dist/plugins/pg-file-snapshot-store.d.ts +8 -0
  27. package/dist/plugins/pg-file-snapshot-store.js +8 -0
  28. package/dist/plugins/pg-image-bake.d.ts +12 -0
  29. package/dist/plugins/pg-image-bake.js +11 -0
  30. package/dist/plugins/pg-image-index.d.ts +12 -0
  31. package/dist/plugins/pg-image-index.js +11 -0
  32. package/dist/plugins/pg-outcome-ledger.d.ts +12 -0
  33. package/dist/plugins/pg-outcome-ledger.js +11 -0
  34. package/dist/plugins/pg-resume-anchor-store.d.ts +7 -0
  35. package/dist/plugins/pg-resume-anchor-store.js +7 -0
  36. package/dist/plugins/pg-run-store.d.ts +9 -0
  37. package/dist/plugins/pg-run-store.js +9 -0
  38. package/dist/plugins/pg-session-policy-store.d.ts +7 -0
  39. package/dist/plugins/pg-session-policy-store.js +7 -0
  40. package/dist/plugins/pg-session-store.d.ts +12 -0
  41. package/dist/plugins/pg-session-store.js +12 -0
  42. package/dist/plugins/pg-tool-result-store.d.ts +9 -0
  43. package/dist/plugins/pg-tool-result-store.js +9 -0
  44. package/dist/plugins/pg-workflow-journal-store.d.ts +9 -0
  45. package/dist/plugins/pg-workflow-journal-store.js +9 -0
  46. package/dist/plugins/pg-workflow-run-store.d.ts +9 -0
  47. package/dist/plugins/pg-workflow-run-store.js +9 -0
  48. package/dist/plugins/tidb-approval-store.d.ts +8 -0
  49. package/dist/plugins/tidb-approval-store.js +8 -0
  50. package/dist/plugins/tidb-breaker-state.d.ts +7 -0
  51. package/dist/plugins/tidb-breaker-state.js +7 -0
  52. package/dist/plugins/tidb-checkpoint-store.d.ts +9 -0
  53. package/dist/plugins/tidb-checkpoint-store.js +9 -0
  54. package/dist/plugins/tidb-file-snapshot-store.d.ts +8 -0
  55. package/dist/plugins/tidb-file-snapshot-store.js +8 -0
  56. package/dist/plugins/tidb-image-bake.d.ts +12 -0
  57. package/dist/plugins/tidb-image-bake.js +11 -0
  58. package/dist/plugins/tidb-image-index.d.ts +12 -0
  59. package/dist/plugins/tidb-image-index.js +11 -0
  60. package/dist/plugins/tidb-outcome-ledger.d.ts +12 -0
  61. package/dist/plugins/tidb-outcome-ledger.js +12 -0
  62. package/dist/plugins/tidb-resume-anchor-store.d.ts +7 -0
  63. package/dist/plugins/tidb-resume-anchor-store.js +7 -0
  64. package/dist/plugins/tidb-run-store.d.ts +10 -0
  65. package/dist/plugins/tidb-run-store.js +9 -0
  66. package/dist/plugins/tidb-session-policy-store.d.ts +7 -0
  67. package/dist/plugins/tidb-session-policy-store.js +7 -0
  68. package/dist/plugins/tidb-tool-result-store.d.ts +8 -0
  69. package/dist/plugins/tidb-tool-result-store.js +10 -0
  70. package/dist/plugins/tidb-workflow-journal-store.d.ts +9 -0
  71. package/dist/plugins/tidb-workflow-journal-store.js +9 -0
  72. package/dist/plugins/tidb-workflow-run-store.d.ts +10 -0
  73. package/dist/plugins/tidb-workflow-run-store.js +10 -0
  74. package/dist/plugins/workflow-journal-limits.d.ts +12 -0
  75. package/dist/plugins/workflow-journal-limits.js +12 -0
  76. package/dist/sema-registry.d.ts +41 -0
  77. package/dist/sema-registry.js +40 -0
  78. package/dist/spec-fields.d.ts +7 -0
  79. package/dist/spec-fields.js +7 -0
  80. package/package.json +2 -2
@@ -41,6 +41,7 @@ import { handleDiagnostics } from "./routes/diagnostics.js";
41
41
  import { handleAdoption } from "./routes/adoption.js";
42
42
  import { handleRetention } from "./routes/retention-ops.js";
43
43
  import { handleMemoryPolicy } from "./routes/memory-policy.js";
44
+ import { handleMemoryBundle } from "./routes/memory-bundle.js";
44
45
  import { handleSharedMemory } from "./routes/shared-memory.js";
45
46
  import { handleRules } from "./routes/rules.js";
46
47
  import { handleSessionsList } from "./routes/sessions-list.js";
@@ -213,6 +214,7 @@ const ROUTE_DOMAINS = [
213
214
  handleAdoption,
214
215
  handleRetention,
215
216
  handleMemoryPolicy,
217
+ handleMemoryBundle,
216
218
  handleRules,
217
219
  handleSharedMemory,
218
220
  handleSessionsList,
@@ -704,6 +706,14 @@ export function createHttpServer(rawDeps) {
704
706
  // design/158 A9:memory 导出/同步 + /v1/policy 只读面(routes/memory-policy.ts)。
705
707
  if (await handleMemoryPolicy(req, res, url, ctx))
706
708
  return;
709
+ // #264 v2-c:治理携出 bundle 的 operator 面(routes/memory-bundle.ts;POST 两口)。
710
+ // 🔴 位置在 memory-policy **之后**是有意的:`/v1/memory/export` 这条路径上住着两个不同的面 ——
711
+ // GET = 142-S5 的属主自助条目读(owner 门,零存在性 oracle),POST = 本件的治理包(operator-only)。
712
+ // 两者按**方法**互斥,谁先谁后都不改判;把既有的 GET 面留在前面,是让新面在任何情况下都不可能
713
+ // 遮住它(本模块只认 POST,一个字都不碰 GET 的匹配式)。族属上它与 adoption / retention-ops 同类:
714
+ // 部署级 operator 动作、同一条 explicitOperatorOk 门、同样 billable=false。
715
+ if (await handleMemoryBundle(req, res, url, ctx))
716
+ return;
707
717
  // #154 车二:CC settings 导入两口(lane=principal,billable=false)。
708
718
  if (await handleRules(req, res, url, ctx))
709
719
  return;
@@ -3262,13 +3272,32 @@ export function isBillableSubmitPath(url) {
3262
3272
  * 🔴 **方法感知**(codex 对抗复审 R2-[high],验真后修):撤销走的是 `DELETE`,而本门最初只挂在 `POST` 的
3263
3273
  * 合取里 —— 于是一个自称「持久改写」的谓词把爆炸半径最大的那条动词漏在门外,正是 `isDestructiveSessionWrite`
3264
3274
  * 当年被抓到的同一形。签名因此收方法,与那只谓词逐字同形。
3265
- * 🔴 读面不进本表:`GET /v1/adoption/:id` 与 `GET /v1/rules` 各有自己的属主/operator 门,本门只拦写。
3266
- * 行为面钉:`test/rewrite-door-service-token-gate.test.ts`(四扇门各一正一反 + 逃生口格 + 读面负控)。
3275
+ * 🔴 读面**原则上**不进本表:`GET /v1/adoption/:id` 与 `GET /v1/rules` 各有自己的**属主门**兜底,本门只拦写。
3276
+ *
3277
+ * 🔴 **第二个合取项的加宽**(codex 对抗复审 [high],#277 提货批,验真后修)——判据现在是
3278
+ * 「持久改写 **或** 授权唯一输入是 principal 头的**跨租户治理读**」。加宽是为了收进 #264 v2-c 的两口:
3279
+ * · `POST /v1/memory/import` —— 直接落在**原**判据上,而且是本仓最深的那一种持久改写:它把别处铸的
3280
+ * 条目 / 挑战 / 污染标记 / 托管链落进本部署的记忆平面。伪造一个在册 operator 头 ⇒ 一份**攻击者自制的
3281
+ * 治理史**成为本店的账;而记忆是模型**每次挂载都读**的面,比种一条常驻 allow 规则更深(那条只影响
3282
+ * ask 会不会问,这条影响模型相信什么)。
3283
+ * · `POST /v1/memory/export` —— 是**读**,按原措辞本不该进。但被点名豁免的那两条读各有属主门兜底,
3284
+ * 而本条**没有**:它的授权判据就是 `explicitOperatorOk`,过了就是**全库**——一次调用取走每个 scope 的
3285
+ * 条目正文 + 托管链 + 跨会话污染标记 + lineage。把一次全租户记忆外泄留在门外、只为守住「本门只拦写」
3286
+ * 这句措辞,是把措辞看得比它要保护的东西更重。加宽后的判据仍然是**闭**的(不是「写门以外都进」):
3287
+ * 它要求「授权的唯一输入是那个头」∧「一次调用的爆炸半径跨租户」,`GET /v1/rules` 那种属主门读不满足。
3288
+ * 行为面钉:`test/rewrite-door-service-token-gate.test.ts`(六扇门各一正一反 + 逃生口格 + 读面负控)。
3267
3289
  */
3268
3290
  export function isCredentialGatedRewrite(method, url) {
3269
3291
  const m = method.toUpperCase();
3270
- if (m === "POST")
3271
- return url === "/v1/adoption" || url.startsWith("/v1/rules/cc-import/");
3292
+ if (m === "POST") {
3293
+ return (url === "/v1/adoption" ||
3294
+ url.startsWith("/v1/rules/cc-import/") ||
3295
+ // #264 v2-c 的两口(codex 对抗复审 [high],验真后修)。判据见上方头注**加宽**的那一段:
3296
+ // import 是本仓最深的持久改写(外来治理史成为本店的账,而记忆是模型每次挂载都读的面);
3297
+ // export 虽是读,但它是**没有属主门兜底**的跨租户治理外泄——两者的授权唯一输入都是那个头。
3298
+ url === "/v1/memory/import" ||
3299
+ url === "/v1/memory/export");
3300
+ }
3272
3301
  if (m === "DELETE")
3273
3302
  return url === "/v1/rules";
3274
3303
  return false;
@@ -3343,7 +3372,13 @@ const ROUTE_LABEL_LITERALS = new Set([
3343
3372
  // 桶会把该桶的 P99 拉爆,运营看不出是谁。批1 建表时漏掉它不是遗忘:fleet 域用 `pathname === …` 判路,
3344
3373
  // 名册门的枚举器当时只认 `url === …`,整条路由对门隐形(枚举器已一并补上)。
3345
3374
  "/v1/fleet/stream",
3375
+ // ⚠️ `/v1/memory/export` 是**两个面**共用的标签(GET=142-S5 属主自助条目读,POST=#264 治理包)——
3376
+ // 标签粒度粗于面的边界,但两面同 billable(false)、同为低频运维/壳读,合桶不会掩盖任何一方的信号。
3346
3377
  "/v1/memory/export", "/v1/sendfile-links",
3378
+ // #264 v2-c:治理包的导入口(operator-only,POST)。低频,但它是**治理写面** —— 它的 4xx/5xx 分布
3379
+ // 是「这批迁移到底成没成」的第一手机器信号,落进 `other` 桶等于迁移现场看不见它。同族纪律:写
3380
+ // **字面量**不引常量(名册门与 billable 申明门都扫源码文本)。
3381
+ "/v1/memory/import",
3347
3382
  // design/177:共享记忆库清单面(具名两条走上面的模式表)。
3348
3383
  "/v1/shared-memory/stores",
3349
3384
  // #154:装配自证读面(operator-only)。低频但**每次都在排障现场被打**,落进 `other` 桶等于排障时看不见。
package/dist/main.js CHANGED
@@ -29,6 +29,7 @@ import { setRedactionObserver, redactSecrets } from "./trace/redact.js";
29
29
  import { RateLimiter } from "./observability/rate-limit.js";
30
30
  import { createHttpServer, explicitOperatorOk } from "./http/server.js";
31
31
  import { exportMemoryScope } from "./memory-export.js";
32
+ import { createMemoryBundleFaces } from "./memory-bundle-engine.js";
32
33
  import { performMemorySync } from "./memory-sync.js";
33
34
  import { startOtlpExporter } from "./observability/otel-exporter.js";
34
35
  import { HEARTBEAT_MS, backgroundAgentOutput, taskHandleOutput, taskHandleStop } from "./runs.js";
@@ -182,6 +183,11 @@ async function main() {
182
183
  // design/183 I6(server 同族):**每副本 boot 必查**收编日志的在飞行 —— 续跑或响亮留痕,禁静默跳过。
183
184
  // 位置:store 开完之后(要 backend)、任何路由装配之前(半迁移状态绝不带进服务期)。判据与「为什么
184
185
  // 是续跑而不是拒启」见 boot/adoption.ts 顶注。
186
+ // #264 v2-c:治理携出 bundle 两面(POST /v1/memory/{export,import})的引擎接线。**一次**构造、两口共用
187
+ // —— 与 orgMemoryDirectory / sharedMemoryStore 同姿势(一个进程对同一件事只有一个实例)。
188
+ // 引擎为什么在这里现构、以及「这两条路径是店级的、不读挂载树」的亲读依据,全文见 memory-bundle-engine.ts。
189
+ // 缺席(记忆引擎未接线)⇒ 两口 501 `capability.memory_engine_required`。
190
+ const memoryBundleFaces = memoryEngine ? createMemoryBundleFaces(memoryEngine) : undefined;
185
191
  const adoptionBoot = await runAdoptionBootScan({ backend, logger });
186
192
  if (adoptionBoot.scanned > 0 || adoptionBoot.error !== undefined) {
187
193
  logger.info("adoption_boot_scan", { ...adoptionBoot });
@@ -1073,6 +1079,16 @@ async function main() {
1073
1079
  memorySync: memoryExportBackend && memorySyncCursors
1074
1080
  ? (scope, syncReq) => performMemorySync(memoryExportBackend, memorySyncCursors, scope, syncReq)
1075
1081
  : undefined,
1082
+ // #264 v2-c(core 5.40.0):治理携出 bundle 的两口 —— POST /v1/memory/export · POST /v1/memory/import。
1083
+ // 数据源是**装配好的记忆引擎本体**(不是 memoryExportBackend 那条 DB entry plane):bundle 是引擎
1084
+ // 的单锁复合面(条目 + 托管链 + 挑战 + lineage + 污染标记同一个 epoch),不是条目读的超集。
1085
+ // 🔴 两口共用一个在场判据,因为它们是同一个引擎面的两半(半挂 = 导得出导不进,而这条产品面的全部
1086
+ // 意义就是那趟往返)。判据是**两项**:引擎在场 ∧ 其后端实现了 bundle 复合面 —— 后者由
1087
+ // createMemoryBundleFaces 在**挂载期**判(返回 undefined ⇒ 两口不挂、能力位翻假、打上去诚实 501),
1088
+ // 而不是照常挂着让每次调用确定性 409(codex 对抗复审 [medium] 驳回的那一版:一个恒假的 yes)。
1089
+ // 绝不在本层为缺面的后端现造替代实现——理由逐字见 routes/memory-bundle.ts 的「能力面的诚实形」。
1090
+ memoryBundleExport: memoryBundleFaces ? memoryBundleFaces.exportScopes : undefined,
1091
+ memoryBundleImport: memoryBundleFaces ? memoryBundleFaces.importBundle : undefined,
1076
1092
  // design/170 件A §7 收编:memory-policy 面的 `org:` 属主门与 core 准入 seam 读**同一个**目录实例
1077
1093
  // (`createOrgMemoryAdmissionWiring` 的产物),两面因此共享 TTL 缓存/退避窗/gen 高水位 —— 一个进程
1078
1094
  // 对「谁属于 org:acme」只有一个答案。缺席(无 center 且无 env 表)⇒ 策略面 `org:` 键仍 operator-only。
@@ -0,0 +1,64 @@
1
+ /**
2
+ * #264 v2-c —— `POST /v1/memory/{export,import}` 两口的**引擎接线**(core 5.40.0 design/178 v2-c)。
3
+ *
4
+ * ## 为什么需要这个模块(而不是在 main.ts 里直接 new)
5
+ *
6
+ * 本仓**没有**一个长命的 `MemoryEngine` 实例:`boot/stores.ts` 装配出来的 `memoryEngine` 是
7
+ * `{ backend, root }` 二元组,真正的引擎由 **core 的 Runner 每任务现构**
8
+ * (`core/runner/prepare-memory.js`),而且构出来的 `memoryDir` 是**按任务的 scope 平面**决定的
9
+ * (personal / project / dual-root 三形,还要看仓根、项目标记、pinned 目录)。
10
+ * 也就是说「这个部署的那一个记忆引擎」这个东西**在本仓不存在** —— HTTP 面若要一个引擎,必须自己构。
11
+ *
12
+ * ## 为什么自己构是安全的:这两个面是 **backend-scoped**,不碰 `memoryDir`
13
+ *
14
+ * 亲读 core 5.40.0 的 `memory-engine/engine.js`(不信 JSDoc,读实现):
15
+ * · `exportMemoryScopes` 全身只用 `this.backend.exportSnapshotOf` 与 `this.now()`;
16
+ * · `importMemoryBundle` 全身只用 `this.backend.importBundleCommit`(校验段是纯函数 `memoryBundleInvalid`)。
17
+ * 两条路径**都不读** `memoryDir` / `controlDir` —— 它们是**店级**的单锁复合面(一个 epoch 里把五张
18
+ * 店面围起来),不是「某个任务挂载的那棵树」上的操作。因此本模块传给构造器的 `memoryDir` 只是**构造器
19
+ * 的必填座**,对这两条路径无影响。
20
+ *
21
+ * 🔴 这条判断是承重的,所以它有**机器钉**而不是只有这段注释:`test/memory-bundle-engine.test.ts` 用一只
22
+ * 假 backend 断言两条路径确实落到 `exportSnapshotOf` / `importBundleCommit`,且给一个**不存在的**
23
+ * `memoryDir` 也照样工作。core 哪天让这两条路径去读挂载树,那道钉当场红 —— 而不是在某个部署上静默
24
+ * 读错一棵树。
25
+ *
26
+ * ## 能力缺席在**构造期**判定(不挂一个注定失败的口)
27
+ *
28
+ * backend 没有那两个可选面(本仓两只 SQL 记忆孪生就没有)⇒ 本工厂返回 **undefined**,两口整个不挂载,
29
+ * HTTP 面诚实 501。
30
+ *
31
+ * 🔴 首版把这件事当成**运行期**事实(照常挂载,让 core 每次抛 `memory.export_incomplete` / 422),
32
+ * 被 codex 对抗复审驳倒(#277 提货批 [medium],验真后修):`typeof backend.exportSnapshotOf === "function"`
33
+ * 是一次**属性检查**,boot 期就判得出;而且它是**永久**缺席(换后端才会变),不是瞬时状态。照旧挂着
34
+ * 就成了「能力位说 yes、每一次调用都确定性失败」——本仓「says yes ⟺ route works」是结构性承诺,
35
+ * 一个恒假的 yes 会让壳给用户开出一条走不通的迁移流程。
36
+ *
37
+ * ⚠️ 两面是**一个**能力,判据取合取:只有一半也不挂。半挂的形是「导得出、导不进」,而这条产品面的
38
+ * 全部意义是那趟往返。
39
+ *
40
+ * ⚠️ 与**运行期**拒不是一回事,别合并:面齐备但店自陈说不清(chain-degraded)时,core 照样抛
41
+ * `memory.export_incomplete` —— 那是诚实的运行期拒,附录 A 单列一条。绝不在这里回落到「用通用读面拼
42
+ * 一个包」:core 明写那正是它拒绝生产的形(a governance-less export is the laundering shape)。
43
+ */
44
+ import { type MemoryBackend, type MemoryExportBundle, type MemoryImportReport } from "@sema-agent/core";
45
+ /** 两口 HTTP 面消费的窄能力面(`ServiceDeps.memoryBundle{Export,Import}` 的实参来源)。 */
46
+ export interface MemoryBundleFaces {
47
+ exportScopes: (scopes: readonly string[]) => Promise<MemoryExportBundle>;
48
+ /** 入参 `unknown`:调用方递交的是未校验的对象,整包判决归 core(理由见 ServiceDeps 同名 seam 的注)。 */
49
+ importBundle: (bundle: unknown, opts?: {
50
+ expectedScopes?: readonly string[];
51
+ }) => Promise<MemoryImportReport>;
52
+ }
53
+ /**
54
+ * 在**已装配的记忆后端**上建 bundle 两面。命名照 CLAUDE.md 工厂律:返回的是带行为的活对象 ⇒ `create*`。
55
+ *
56
+ * `memoryDir` = `boot/stores.ts` 的 `memoryEngine.root`(该部署的记忆工作根)。见文件头:这两条路径
57
+ * 不读它,传它是为了不发明第二个「引擎根」的概念 —— 万一 core 日后真用上,它拿到的也是本部署唯一的
58
+ * 那个根,而不是一个本模块凭空编出来的路径。
59
+ */
60
+ export declare function createMemoryBundleFaces(store: {
61
+ backend: MemoryBackend;
62
+ root: string;
63
+ }): MemoryBundleFaces | undefined;
64
+ //# sourceMappingURL=memory-bundle-engine.d.ts.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * #264 v2-c —— `POST /v1/memory/{export,import}` 两口的**引擎接线**(core 5.40.0 design/178 v2-c)。
3
+ *
4
+ * ## 为什么需要这个模块(而不是在 main.ts 里直接 new)
5
+ *
6
+ * 本仓**没有**一个长命的 `MemoryEngine` 实例:`boot/stores.ts` 装配出来的 `memoryEngine` 是
7
+ * `{ backend, root }` 二元组,真正的引擎由 **core 的 Runner 每任务现构**
8
+ * (`core/runner/prepare-memory.js`),而且构出来的 `memoryDir` 是**按任务的 scope 平面**决定的
9
+ * (personal / project / dual-root 三形,还要看仓根、项目标记、pinned 目录)。
10
+ * 也就是说「这个部署的那一个记忆引擎」这个东西**在本仓不存在** —— HTTP 面若要一个引擎,必须自己构。
11
+ *
12
+ * ## 为什么自己构是安全的:这两个面是 **backend-scoped**,不碰 `memoryDir`
13
+ *
14
+ * 亲读 core 5.40.0 的 `memory-engine/engine.js`(不信 JSDoc,读实现):
15
+ * · `exportMemoryScopes` 全身只用 `this.backend.exportSnapshotOf` 与 `this.now()`;
16
+ * · `importMemoryBundle` 全身只用 `this.backend.importBundleCommit`(校验段是纯函数 `memoryBundleInvalid`)。
17
+ * 两条路径**都不读** `memoryDir` / `controlDir` —— 它们是**店级**的单锁复合面(一个 epoch 里把五张
18
+ * 店面围起来),不是「某个任务挂载的那棵树」上的操作。因此本模块传给构造器的 `memoryDir` 只是**构造器
19
+ * 的必填座**,对这两条路径无影响。
20
+ *
21
+ * 🔴 这条判断是承重的,所以它有**机器钉**而不是只有这段注释:`test/memory-bundle-engine.test.ts` 用一只
22
+ * 假 backend 断言两条路径确实落到 `exportSnapshotOf` / `importBundleCommit`,且给一个**不存在的**
23
+ * `memoryDir` 也照样工作。core 哪天让这两条路径去读挂载树,那道钉当场红 —— 而不是在某个部署上静默
24
+ * 读错一棵树。
25
+ *
26
+ * ## 能力缺席在**构造期**判定(不挂一个注定失败的口)
27
+ *
28
+ * backend 没有那两个可选面(本仓两只 SQL 记忆孪生就没有)⇒ 本工厂返回 **undefined**,两口整个不挂载,
29
+ * HTTP 面诚实 501。
30
+ *
31
+ * 🔴 首版把这件事当成**运行期**事实(照常挂载,让 core 每次抛 `memory.export_incomplete` / 422),
32
+ * 被 codex 对抗复审驳倒(#277 提货批 [medium],验真后修):`typeof backend.exportSnapshotOf === "function"`
33
+ * 是一次**属性检查**,boot 期就判得出;而且它是**永久**缺席(换后端才会变),不是瞬时状态。照旧挂着
34
+ * 就成了「能力位说 yes、每一次调用都确定性失败」——本仓「says yes ⟺ route works」是结构性承诺,
35
+ * 一个恒假的 yes 会让壳给用户开出一条走不通的迁移流程。
36
+ *
37
+ * ⚠️ 两面是**一个**能力,判据取合取:只有一半也不挂。半挂的形是「导得出、导不进」,而这条产品面的
38
+ * 全部意义是那趟往返。
39
+ *
40
+ * ⚠️ 与**运行期**拒不是一回事,别合并:面齐备但店自陈说不清(chain-degraded)时,core 照样抛
41
+ * `memory.export_incomplete` —— 那是诚实的运行期拒,附录 A 单列一条。绝不在这里回落到「用通用读面拼
42
+ * 一个包」:core 明写那正是它拒绝生产的形(a governance-less export is the laundering shape)。
43
+ */
44
+ import { MemoryEngine } from "@sema-agent/core";
45
+ /**
46
+ * 在**已装配的记忆后端**上建 bundle 两面。命名照 CLAUDE.md 工厂律:返回的是带行为的活对象 ⇒ `create*`。
47
+ *
48
+ * `memoryDir` = `boot/stores.ts` 的 `memoryEngine.root`(该部署的记忆工作根)。见文件头:这两条路径
49
+ * 不读它,传它是为了不发明第二个「引擎根」的概念 —— 万一 core 日后真用上,它拿到的也是本部署唯一的
50
+ * 那个根,而不是一个本模块凭空编出来的路径。
51
+ */
52
+ export function createMemoryBundleFaces(store) {
53
+ // 构造期能力判定(见文件头):两个可选面缺任一 ⇒ 整个不挂。判据直接读 backend 的面,不看后端方言名
54
+ // ——「认能力不认实现名」是本仓的既有教训(按部署名判「组件已装」认不出 helm 装的那次)。
55
+ const b = store.backend;
56
+ if (typeof b.exportSnapshotOf !== "function" || typeof b.importBundleCommit !== "function")
57
+ return undefined;
58
+ const engine = new MemoryEngine({ backend: store.backend, memoryDir: store.root });
59
+ return {
60
+ exportScopes: (scopes) => engine.exportMemoryScopes(scopes),
61
+ // 唯一一次窄化,就发生在**引擎边界上**:core 的 d.ts 要 MemoryExportBundle,而它的实装第一件事
62
+ // 就是 JSON 深拷 + `memoryBundleInvalid` 整包校验 —— 也就是说这个形参在运行期本就接受任意值,
63
+ // 由它自己裁。把窄化收在这一行(而不是让它一路污染 HTTP 层的类型)是「谁裁决谁承型」。
64
+ importBundle: (bundle, opts) => engine.importMemoryBundle(bundle, opts),
65
+ };
66
+ }
67
+ //# sourceMappingURL=memory-bundle-engine.js.map
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Durable approval store for the human-in-the-loop write gate (F4) — SINGLE-FILE DUAL-DIALECT
3
+ * (design/158 A12 定型半场). ONE implementation, TWO dialects; the historical `TiDBApprovalStore` /
4
+ * `PgApprovalStore` class names survive as thin ctor subclasses so every consumer (store-backend.ts,
5
+ * src/index.ts, the approval/audit/security-coverage suites) is untouched.
6
+ *
7
+ * When a gated tool call is about to run, a `pending` row is recorded here; the instance running the
8
+ * task polls it for a decision. Because the row is durable and shared, an operator's approve/deny can
9
+ * land on ANY instance — critical under async runs + a load balancer, where the deciding request and
10
+ * the waiting run are usually on different instances.
11
+ *
12
+ * Unlike the run/checkpoint PG store (whose schema is owned centrally by pg-pool.ts), this file also
13
+ * exports a self-contained PG schema (`PG_APPROVAL_SCHEMA` + `ensureSchema`) — the `approval` table is
14
+ * DISJOINT from the other PG stores' tables, so a CREATE IF NOT EXISTS here shadows nothing. Central
15
+ * aggregation into pg-pool.ts composes it alongside the others. Kept in lock-step with the TiDB store
16
+ * by the shared real-DB integration suite (test/pg-approval-store-integration.test.ts).
17
+ *
18
+ * ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
19
+ * - `?` placeholders vs `$n`
20
+ * - null-safe scope compare: `<=>` vs `IS NOT DISTINCT FROM` (a NULL scope row — legacy/untenanted
21
+ * run — is matched by a NULL scope arg; one tenant's scope NEVER matches another's — the
22
+ * single-DB fleet read guard, defense-in-depth)
23
+ * - JSON binding: TiDB `JSON.stringify` verbatim vs PG `pgProtocolJsonStringify` (codex R4-H2: the
24
+ * persisted approval `args` is the operator's REVIEW surface and must agree byte-for-byte with the
25
+ * policy-executed args — a lossy/rewriting envelope would let an operator approve one payload while
26
+ * a different one runs)
27
+ * - affectedRows vs rowCount (via SqlDriver)
28
+ * - schema ownership: TiDB DDL lives in tidb-pool.ts SCHEMA_STATEMENTS; PG DDL is this file's own
29
+ * self-contained `PG_APPROVAL_SCHEMA` (see the PG twin's original header — unlike the other PG
30
+ * stores, `approval` is NOT centrally owned by pg-pool.ts's DDL array, only aggregated into its
31
+ * `ensureSchema` composition).
32
+ * - `getStatus` result shape: `RowDataPacket[]` (mysql2 typed query) vs a plain-record `pg` query —
33
+ * normalized to `SqlRow` by the shared driver, so both arms read the same field names.
34
+ */
35
+ import type { Pool as MySqlPool } from "mysql2/promise";
36
+ import type { Pool as PgPool, PoolClient } from "pg";
37
+ import { type SqlDriver } from "./sql-driver.js";
38
+ export type ApprovalStatus = "pending" | "approved" | "denied" | "expired";
39
+ export interface ApprovalRow {
40
+ id: string;
41
+ taskId: string | null;
42
+ sessionId: string | null;
43
+ owner: string | null;
44
+ /** Single-DB fleet scope guard (defense-in-depth): the run's tenant identity, owner-sourced. Reads filter on it. */
45
+ scope: string | null;
46
+ toolName: string;
47
+ args: unknown;
48
+ status: ApprovalStatus;
49
+ reason: string | null;
50
+ decidedBy: string | null;
51
+ createdAt: string;
52
+ decidedAt: string | null;
53
+ }
54
+ /** PG translation of the `approval` DDL in tidb-pool.ts SCHEMA_STATEMENTS (JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3),
55
+ * inline KEY→separate CREATE INDEX).
56
+ *
57
+ * SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
58
+ * drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead. */
59
+ export declare const PG_APPROVAL_SCHEMA: string[];
60
+ /** Idempotent self-contained schema apply (for the integration test; central aggregation is done separately). */
61
+ export declare function ensureSchema(pool: PgPool | PoolClient): Promise<void>;
62
+ /** Dual-dialect durable approval store. See the file header for the dialect-delta ledger. */
63
+ export declare class SqlApprovalStore {
64
+ protected readonly db: SqlDriver;
65
+ constructor(db: SqlDriver);
66
+ /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
67
+ private q;
68
+ /** JSON column binding: TiDB stores the string verbatim; PG goes through the LOSSLESS protocol envelope
69
+ * (codex R4-H2 — the approval `args` is the operator's review surface and must agree with the
70
+ * policy-executed args at the NUL position). */
71
+ private json;
72
+ createPending(id: string, scope: string | null, ctx: {
73
+ taskId?: string | null;
74
+ sessionId?: string | null;
75
+ owner?: string | null;
76
+ toolName: string;
77
+ args: unknown;
78
+ }): Promise<void>;
79
+ getStatus(id: string, scope: string | null): Promise<{
80
+ status: ApprovalStatus;
81
+ reason: string | null;
82
+ decidedBy: string | null;
83
+ } | undefined>;
84
+ /** Apply a terminal decision, but only to a still-pending row (CAS). Returns whether it applied. The scope
85
+ * guard (null-safe) is carried on the operator path too for consistency with the read paths. */
86
+ decide(id: string, scope: string | null, decision: "approved" | "denied" | "expired", reason: string | null, decidedBy: string | null): Promise<boolean>;
87
+ /** Pending approvals (operator queue), scoped to one tenant (single-DB fleet read guard, null-safe), optionally
88
+ * further filtered to one owner. */
89
+ listPending(scope: string | null, owner?: string): Promise<ApprovalRow[]>;
90
+ get(id: string, scope: string | null): Promise<ApprovalRow | undefined>;
91
+ /** Operator-only CROSS-tenant by-id read (no scope filter). The F4 operator queue serves/decides across
92
+ * tenants by design; the HTTP layer gates this to an operator and then re-binds decide() to the row's OWN
93
+ * scope. Tenants NEVER reach this — they use the scope-bound {@link get}. */
94
+ getById(id: string): Promise<ApprovalRow | undefined>;
95
+ /** Operator-only CROSS-tenant pending list (no scope filter) — the unfiltered operator queue across all
96
+ * tenants. Tenants use the scope-bound {@link listPending}; this is HTTP-gated to an operator.
97
+ * 🔴 identical SQL text on both dialects (no placeholders, nothing dialect-specific) — kept as ONE literal
98
+ * rather than a `q()` pair so a reader isn't misled into looking for a divergence that isn't there. */
99
+ listPendingAll(): Promise<ApprovalRow[]>;
100
+ /** Expire pending approvals older than `olderThanMs` (a never-answered operator request). Deliberately GLOBAL
101
+ * (NOT scope-filtered): staleness expiry is fleet-level maintenance that protects the worker regardless of
102
+ * tenant — a stale row in any scope must be reaped. Scope isolation applies to the per-tenant READ paths, not
103
+ * to this maintenance sweep. */
104
+ expireStale(olderThanMs: number): Promise<number>;
105
+ }
106
+ /** approval row → {@link ApprovalRow} — shared by BOTH dialects. design/158 S8 归位 / A12 收编. */
107
+ export declare function mapRow(r: Record<string, unknown>): ApprovalRow;
108
+ /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
109
+ export declare class TiDBApprovalStore extends SqlApprovalStore {
110
+ constructor(pool: MySqlPool);
111
+ }
112
+ /** PostgreSQL binding — historical class name + ctor shape preserved. */
113
+ export declare class PgApprovalStore extends SqlApprovalStore {
114
+ constructor(pool: PgPool);
115
+ }
116
+ //# sourceMappingURL=approval-store-sql.d.ts.map
@@ -0,0 +1,151 @@
1
+ import { parseJsonStrict as parseJson, toIsoOrNull as iso } from "./sql-row-helpers.js";
2
+ import { pgProtocolJsonStringify } from "./pg-safe-json.js";
3
+ import { mysqlDriver, pgDriver } from "./sql-driver.js";
4
+ /** PG translation of the `approval` DDL in tidb-pool.ts SCHEMA_STATEMENTS (JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3),
5
+ * inline KEY→separate CREATE INDEX).
6
+ *
7
+ * SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
8
+ * drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead. */
9
+ export const PG_APPROVAL_SCHEMA = [
10
+ `CREATE TABLE IF NOT EXISTS approval (
11
+ id VARCHAR(64) NOT NULL,
12
+ task_id VARCHAR(64),
13
+ session_id VARCHAR(64),
14
+ owner VARCHAR(190),
15
+ -- Single-DB fleet scope guard (tenant isolation). NULLABLE on purpose: a NULL-scope row is an untenanted
16
+ -- run, and every read/decide path matches it with \`scope IS NOT DISTINCT FROM $n\` (the PG twin of TiDB
17
+ -- \`<=>\`) so NULL matches only NULL and one tenant's scope NEVER matches another's.
18
+ scope VARCHAR(190),
19
+ tool_name VARCHAR(190) NOT NULL,
20
+ args JSONB,
21
+ status VARCHAR(16) NOT NULL,
22
+ reason TEXT,
23
+ decided_by VARCHAR(190),
24
+ created_at TIMESTAMPTZ(3) NOT NULL,
25
+ decided_at TIMESTAMPTZ(3),
26
+ PRIMARY KEY (id)
27
+ )`,
28
+ `CREATE INDEX IF NOT EXISTS idx_approval_owner_status ON approval (owner, status)`,
29
+ `CREATE INDEX IF NOT EXISTS idx_approval_status ON approval (status)`,
30
+ `CREATE INDEX IF NOT EXISTS idx_approval_scope_status ON approval (scope, status)`,
31
+ ];
32
+ /** Idempotent self-contained schema apply (for the integration test; central aggregation is done separately). */
33
+ export async function ensureSchema(pool) {
34
+ for (const stmt of PG_APPROVAL_SCHEMA)
35
+ await pool.query(stmt);
36
+ }
37
+ /** Dual-dialect durable approval store. See the file header for the dialect-delta ledger. */
38
+ export class SqlApprovalStore {
39
+ db;
40
+ constructor(db) {
41
+ this.db = db;
42
+ }
43
+ /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
44
+ q(tidb, pg) {
45
+ return this.db.dialect === "tidb" ? tidb : pg;
46
+ }
47
+ /** JSON column binding: TiDB stores the string verbatim; PG goes through the LOSSLESS protocol envelope
48
+ * (codex R4-H2 — the approval `args` is the operator's review surface and must agree with the
49
+ * policy-executed args at the NUL position). */
50
+ json(value, label) {
51
+ return this.db.dialect === "tidb" ? JSON.stringify(value) : pgProtocolJsonStringify(value, label);
52
+ }
53
+ async createPending(id, scope, ctx) {
54
+ await this.db.query(this.q("INSERT INTO approval (id, task_id, session_id, owner, scope, tool_name, args, status, created_at) " +
55
+ "VALUES (?,?,?,?,?,?,?,'pending',?)", "INSERT INTO approval (id, task_id, session_id, owner, scope, tool_name, args, status, created_at) " +
56
+ "VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,'pending',$8)"), [
57
+ id,
58
+ ctx.taskId ?? null,
59
+ ctx.sessionId ?? null,
60
+ ctx.owner ?? null,
61
+ scope,
62
+ ctx.toolName,
63
+ ctx.args == null ? null : this.json(ctx.args, "approval args"),
64
+ new Date(),
65
+ ]);
66
+ }
67
+ async getStatus(id, scope) {
68
+ // null-safe scope match (`<=>` / `IS NOT DISTINCT FROM`): a NULL scope row (legacy / untenanted run) is
69
+ // matched by a NULL scope arg, and a tenant's scope NEVER matches another tenant's — the single-DB fleet
70
+ // read guard (defense-in-depth).
71
+ const { rows } = await this.db.query(this.q("SELECT status, reason, decided_by FROM approval WHERE id = ? AND scope <=> ?", "SELECT status, reason, decided_by FROM approval WHERE id = $1 AND scope IS NOT DISTINCT FROM $2"), [id, scope]);
72
+ const r = rows[0];
73
+ if (!r)
74
+ return undefined;
75
+ return { status: r.status, reason: r.reason ?? null, decidedBy: r.decided_by ?? null };
76
+ }
77
+ /** Apply a terminal decision, but only to a still-pending row (CAS). Returns whether it applied. The scope
78
+ * guard (null-safe) is carried on the operator path too for consistency with the read paths. */
79
+ async decide(id, scope, decision, reason, decidedBy) {
80
+ const res = await this.db.query(this.q("UPDATE approval SET status = ?, reason = ?, decided_by = ?, decided_at = ? WHERE id = ? AND scope <=> ? AND status = 'pending'", "UPDATE approval SET status = $1, reason = $2, decided_by = $3, decided_at = $4 WHERE id = $5 AND scope IS NOT DISTINCT FROM $6 AND status = 'pending'"), [decision, reason, decidedBy, new Date(), id, scope]);
81
+ return res.affected === 1;
82
+ }
83
+ /** Pending approvals (operator queue), scoped to one tenant (single-DB fleet read guard, null-safe), optionally
84
+ * further filtered to one owner. */
85
+ async listPending(scope, owner) {
86
+ const { rows } = owner
87
+ ? await this.db.query(this.q("SELECT * FROM approval WHERE status = 'pending' AND scope <=> ? AND owner = ? ORDER BY created_at ASC", "SELECT * FROM approval WHERE status = 'pending' AND scope IS NOT DISTINCT FROM $1 AND owner = $2 ORDER BY created_at ASC"), [scope, owner])
88
+ : await this.db.query(this.q("SELECT * FROM approval WHERE status = 'pending' AND scope <=> ? ORDER BY created_at ASC", "SELECT * FROM approval WHERE status = 'pending' AND scope IS NOT DISTINCT FROM $1 ORDER BY created_at ASC"), [scope]);
89
+ return rows.map(mapRow);
90
+ }
91
+ async get(id, scope) {
92
+ const { rows } = await this.db.query(this.q("SELECT * FROM approval WHERE id = ? AND scope <=> ?", "SELECT * FROM approval WHERE id = $1 AND scope IS NOT DISTINCT FROM $2"), [id, scope]);
93
+ return rows[0] ? mapRow(rows[0]) : undefined;
94
+ }
95
+ /** Operator-only CROSS-tenant by-id read (no scope filter). The F4 operator queue serves/decides across
96
+ * tenants by design; the HTTP layer gates this to an operator and then re-binds decide() to the row's OWN
97
+ * scope. Tenants NEVER reach this — they use the scope-bound {@link get}. */
98
+ async getById(id) {
99
+ const { rows } = await this.db.query(this.q("SELECT * FROM approval WHERE id = ?", "SELECT * FROM approval WHERE id = $1"), [id]);
100
+ return rows[0] ? mapRow(rows[0]) : undefined;
101
+ }
102
+ /** Operator-only CROSS-tenant pending list (no scope filter) — the unfiltered operator queue across all
103
+ * tenants. Tenants use the scope-bound {@link listPending}; this is HTTP-gated to an operator.
104
+ * 🔴 identical SQL text on both dialects (no placeholders, nothing dialect-specific) — kept as ONE literal
105
+ * rather than a `q()` pair so a reader isn't misled into looking for a divergence that isn't there. */
106
+ async listPendingAll() {
107
+ const { rows } = await this.db.query("SELECT * FROM approval WHERE status = 'pending' ORDER BY created_at ASC");
108
+ return rows.map(mapRow);
109
+ }
110
+ /** Expire pending approvals older than `olderThanMs` (a never-answered operator request). Deliberately GLOBAL
111
+ * (NOT scope-filtered): staleness expiry is fleet-level maintenance that protects the worker regardless of
112
+ * tenant — a stale row in any scope must be reaped. Scope isolation applies to the per-tenant READ paths, not
113
+ * to this maintenance sweep. */
114
+ async expireStale(olderThanMs) {
115
+ const cutoff = new Date(Date.now() - olderThanMs);
116
+ const res = await this.db.query(this.q("UPDATE approval SET status = 'expired', reason = 'timed out before decision', decided_at = ? " +
117
+ "WHERE status = 'pending' AND created_at < ?", "UPDATE approval SET status = 'expired', reason = 'timed out before decision', decided_at = $1 " +
118
+ "WHERE status = 'pending' AND created_at < $2"), [new Date(), cutoff]);
119
+ return res.affected;
120
+ }
121
+ }
122
+ /** approval row → {@link ApprovalRow} — shared by BOTH dialects. design/158 S8 归位 / A12 收编. */
123
+ export function mapRow(r) {
124
+ return {
125
+ id: String(r.id),
126
+ taskId: r.task_id ?? null,
127
+ sessionId: r.session_id ?? null,
128
+ owner: r.owner ?? null,
129
+ scope: r.scope ?? null,
130
+ toolName: String(r.tool_name),
131
+ args: parseJson(r.args),
132
+ status: r.status,
133
+ reason: r.reason ?? null,
134
+ decidedBy: r.decided_by ?? null,
135
+ createdAt: iso(r.created_at),
136
+ decidedAt: iso(r.decided_at),
137
+ };
138
+ }
139
+ /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
140
+ export class TiDBApprovalStore extends SqlApprovalStore {
141
+ constructor(pool) {
142
+ super(mysqlDriver(pool));
143
+ }
144
+ }
145
+ /** PostgreSQL binding — historical class name + ctor shape preserved. */
146
+ export class PgApprovalStore extends SqlApprovalStore {
147
+ constructor(pool) {
148
+ super(pgDriver(pool));
149
+ }
150
+ }
151
+ //# sourceMappingURL=approval-store-sql.js.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * SVC-2 — FILE-backed durable WorkflowJournalStore for the LOCAL (TOC) backend.
3
+ *
4
+ * Now a RE-EXPORT of core's copy (the write-here-then-core-adopts 剧本): the implementation was written here for server
5
+ * 1.89.0, then adopted verbatim by core (`@sema-agent/core` 1.223.0, file
6
+ * `src/stores/file/workflow-journal-store.ts` — crash-safe per-run JSONL, scope-guarded [CORE-9],
7
+ * idempotent-per-ordinal, oversize skip-journal, traversal-runId load degrade, 0o600/0o700). One source of
8
+ * truth in core; the SQL twins (tidb/pg) stay service-side (dialect = deployment glue). The service test
9
+ * suite (test/file-workflow-journal-store.test.ts) keeps pinning the contract through this re-export.
10
+ */
11
+ export { FileWorkflowJournalStore } from "@sema-agent/core";
12
+ //# sourceMappingURL=file-workflow-journal-store.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * SVC-2 — FILE-backed durable WorkflowJournalStore for the LOCAL (TOC) backend.
3
+ *
4
+ * Now a RE-EXPORT of core's copy (the write-here-then-core-adopts 剧本): the implementation was written here for server
5
+ * 1.89.0, then adopted verbatim by core (`@sema-agent/core` 1.223.0, file
6
+ * `src/stores/file/workflow-journal-store.ts` — crash-safe per-run JSONL, scope-guarded [CORE-9],
7
+ * idempotent-per-ordinal, oversize skip-journal, traversal-runId load degrade, 0o600/0o700). One source of
8
+ * truth in core; the SQL twins (tidb/pg) stay service-side (dialect = deployment glue). The service test
9
+ * suite (test/file-workflow-journal-store.test.ts) keeps pinning the contract through this re-export.
10
+ */
11
+ export { FileWorkflowJournalStore } from "@sema-agent/core";
12
+ //# sourceMappingURL=file-workflow-journal-store.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Thin re-export shim (design/158 A12 收编 — see src/plugins/approval-store-sql.ts for the
3
+ * implementation, now SINGLE-FILE DUAL-DIALECT alongside the TiDB twin). Class name, ctor arity,
4
+ * and every exported type/const/helper stay EXACTLY as they were so no consumer (pg-pool.ts's
5
+ * central schema composition, store-backend.ts, test/pg-approval-store-integration.test.ts) has
6
+ * to move.
7
+ */
8
+ export { PgApprovalStore, PG_APPROVAL_SCHEMA, ensureSchema, type ApprovalRow, type ApprovalStatus, } from "./approval-store-sql.js";
9
+ //# sourceMappingURL=pg-approval-store.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Thin re-export shim (design/158 A12 收编 — see src/plugins/approval-store-sql.ts for the
3
+ * implementation, now SINGLE-FILE DUAL-DIALECT alongside the TiDB twin). Class name, ctor arity,
4
+ * and every exported type/const/helper stay EXACTLY as they were so no consumer (pg-pool.ts's
5
+ * central schema composition, store-backend.ts, test/pg-approval-store-integration.test.ts) has
6
+ * to move.
7
+ */
8
+ export { PgApprovalStore, PG_APPROVAL_SCHEMA, ensureSchema, } from "./approval-store-sql.js";
9
+ //# sourceMappingURL=pg-approval-store.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Re-export shim (design/158 A12 收编) — the real implementation moved to breaker-state-sql.ts
3
+ * (single-file dual-dialect: `SqlBreakerState` + the `TiDBBreakerState`/`PgBreakerState` ctor subclasses).
4
+ * Kept so every consumer's `from "./pg-breaker-state.js"` import (incl. pg-pool.ts's central ensureSchema
5
+ * composition and the real dual-DB integration suite) keeps working unchanged.
6
+ */
7
+ export { PgBreakerState, PG_BREAKER_STATE_SCHEMA, ensureSchema } from "./breaker-state-sql.js";
8
+ //# sourceMappingURL=pg-breaker-state.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Re-export shim (design/158 A12 收编) — the real implementation moved to breaker-state-sql.ts
3
+ * (single-file dual-dialect: `SqlBreakerState` + the `TiDBBreakerState`/`PgBreakerState` ctor subclasses).
4
+ * Kept so every consumer's `from "./pg-breaker-state.js"` import (incl. pg-pool.ts's central ensureSchema
5
+ * composition and the real dual-DB integration suite) keeps working unchanged.
6
+ */
7
+ export { PgBreakerState, PG_BREAKER_STATE_SCHEMA, ensureSchema } from "./breaker-state-sql.js";
8
+ //# sourceMappingURL=pg-breaker-state.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * PostgreSQL `CheckpointStore` — the implementation now lives in the single-file dual-dialect
3
+ * `checkpoint-store-sql.ts` (design/158 A12 定型半场), alongside its TiDB twin so the dialect deltas
4
+ * (placeholders, ON CONFLICT, `IS NOT DISTINCT FROM` + its placeholder-arity consequence, the lossless
5
+ * JSON envelope) read side by side instead of drifting across two files.
6
+ *
7
+ * This file is the historical import path: class name and ctor shape unchanged.
8
+ */
9
+ export { PgCheckpointStore } from "./checkpoint-store-sql.js";
10
+ //# sourceMappingURL=pg-checkpoint-store.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * PostgreSQL `CheckpointStore` — the implementation now lives in the single-file dual-dialect
3
+ * `checkpoint-store-sql.ts` (design/158 A12 定型半场), alongside its TiDB twin so the dialect deltas
4
+ * (placeholders, ON CONFLICT, `IS NOT DISTINCT FROM` + its placeholder-arity consequence, the lossless
5
+ * JSON envelope) read side by side instead of drifting across two files.
6
+ *
7
+ * This file is the historical import path: class name and ctor shape unchanged.
8
+ */
9
+ export { PgCheckpointStore } from "./checkpoint-store-sql.js";
10
+ //# sourceMappingURL=pg-checkpoint-store.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Thin re-export shim (design/158 A12 定型半场) — the real implementation collapsed into
3
+ * `file-snapshot-store-sql.ts` (single file, dual dialect). Kept so every existing import path
4
+ * (store-backend.ts, the migration script, the contract/integration/minio-equivalence suites) is
5
+ * untouched: same class name, same ctor arity, same exported surface.
6
+ */
7
+ export { PgFileSnapshotStore } from "./file-snapshot-store-sql.js";
8
+ //# sourceMappingURL=pg-file-snapshot-store.d.ts.map