@zhushanwen/zcode-session-source 0.2.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.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # @zhushanwen/zcode-session-source
2
+
3
+ zcode subagent 会话库(SQLite)的唯一只读读取基座。三个消费面:session-reader 扩展
4
+ (bun 宿主)、runtime 会话导入薄包装(node 侧)、未来新增的 coding-agent source。
5
+
6
+ ## 内容
7
+
8
+ - **sqlite-driver**(D3 双驱动):运行时探测 bun/node,动态 import 经变量间接
9
+ (esbuild 规约防护);适配层只暴露 `open / prepare / all / get / close` 公共子集。
10
+ - **四级恢复阶梯**(设计 §3.5 单一规格,全网唯一):
11
+ - L1 直开(readonly,`-wal` 在场正常工作)
12
+ - L2 immutable 逃逸(常态恢复,零拷贝;仅当开库前确认 `-wal` 不存在——有内容
13
+ `-wal` 下 immutable 静默丢行,必须门控。darwin bun 常态;linux bun 的
14
+ `file:` URI 开库不可用,直开失败直接落 L3——bun 平台分叉矩阵登记在
15
+ `src/__tests__/platform-matrix.ts`)
16
+ - L3 小库快照兜底(db 拷 mkdtemp 固定前缀 `taiji-zcode-snap-` + 自建 0 字节
17
+ `-wal` → 开库后验证 `sqlite_master` 表集合含 `session/message/part`;规模门
18
+ 256MB 拒拷;读后 finally 清理。**$TMPDIR 治理面**:快照属「读快照」非对话数据
19
+ 存放——源库只读零触碰、副本即弃,taiji 侧登记为 data-source-registry §4 ⑰
20
+ 〔ADR-0063 I2 的登记例外〕,详见 recovery.ts 头注)
21
+ - L4 错误面(`SqliteUnreadableError`,`attempted` 记录已尝试级别,供上层映射
22
+ 消费侧错误码——本包不私建错误码词表)
23
+ - 拷贝集唯一定义:db only + 自建 0 字节 `-wal`;`-wal`/`-shm` 在场绝不自动拷贝
24
+ (防静默回滚),`-shm` 永不进拷贝集。
25
+ - **sqlite-access**:行集查询(`getSessionTranscript`:session → message → part
26
+ 三级表,联合序)、schema 版本已知集闸门(`KNOWN_ZCODE_SCHEMA_VERSIONS`)、
27
+ 库路径投影与白名单集合(路径段常量同源 `@zhushanwen/subagent-engine-sdk`,
28
+ `dataDir` 由调用方传入,本包不做宿主路径推导)。
29
+
30
+ ## 测试(D3 源级双跑)
31
+
32
+ ```bash
33
+ pnpm vitest run # node 趟(node:sqlite 驱动路径)
34
+ bunx vitest run # bun 趟(bun:sqlite 驱动路径,挂 pre-commit/CI)
35
+ ```
36
+
37
+ 同一断言集双趟执行,分别覆盖两条驱动路径;fixture 建库同样经变量间接取当前
38
+ 运行时的可写连接(bun 下 `node:sqlite` 不可用)。测试只写删 `os.tmpdir()` 下
39
+ 自建自删目录,不触真实数据目录。
@@ -0,0 +1,323 @@
1
+ import { NormalizedSession } from '@zhushanwen/session-core';
2
+
3
+ /**
4
+ * 四级恢复阶梯(设计 §3.5 单一规格,全网唯一——其他位置只引用不另立):
5
+ *
6
+ * L1 直开(readonly):-wal 在场时正常工作(实测:写者活跃 + -wal 有内容可开
7
+ * 且读到已提交行)。失败多因 -wal 缺失(静息态 darwin bun 抛 CANTOPEN——F22;
8
+ * linux bun 与 node 直开成功——bun 平台分叉矩阵登记在 __tests__/platform-matrix.ts)。
9
+ * L2 immutable 逃逸(常态恢复,零拷贝):直开失败 ∧ 开库前确认 -wal 不存在
10
+ * → file:<db>?immutable=1(免拷贝、数据完整、不创建 -shm——F25)。
11
+ * ⚠ 门控:immutable 仅在确认 -wal 缺失时用——有内容 -wal 下 immutable 静默丢行。
12
+ * (darwin bun 的常态恢复路径;linux bun 的 file: URI 开库不可用——直开
13
+ * 失败直接落 L3,平台分叉矩阵同上)
14
+ * L3 小库快照兜底(罕见):immutable 也失败 → db 拷 mkdtemp(固定前缀
15
+ * taiji-zcode-snap-)+ 自建 0 字节 -wal → 开库后验证 sqlite_master 表集合含
16
+ * session/message/part(防「开库成功但缺表」半残态)→ 通过才读。
17
+ * 规模门:db 文件 > 256MB 不拷 → 直接错误面。
18
+ * L4 错误面:SqliteUnreadableError(attempted 记录已尝试的等级、跳过原因并入
19
+ * message,供上层映射 zcode_db_unreadable——错误码词表属消费侧,本包不私建)。
20
+ *
21
+ * 拷贝集唯一定义:db only + 自建 0 字节 -wal;-wal/-shm 在场绝不自动拷贝
22
+ * (旧 wal + 新 db 顺序错配会静默回滚到旧数据,实测证实),-shm 永不进拷贝集
23
+ * (竞态共享索引,副本目录可写即可重建)。
24
+ *
25
+ * 实现注记(node:sqlite 实测,2026-09-21 探针):DatabaseSync 打开是惰性的——
26
+ * 损坏文件 open 成功、首次查询才报 NOTADB。因此每级「成功」判定 = open 不抛
27
+ * ∧ 探测查询(读 sqlite_master)不抛,否则 bun/node 双端成功语义不可对齐。
28
+ *
29
+ * 崩溃残留显式「不接管」:mkdtemp 固定前缀 taiji-zcode-snap- + 读后 finally 清理;
30
+ * os.tmpdir() 由 OS 回收,前缀可人工识别。不为此新增清理器(新机制新义务,超出
31
+ * 读取链职责——设计 §3.4 CANTOPEN 四要素「恢复路径」节)。
32
+ *
33
+ * $TMPDIR 治理面登记(2026-09-23 review round 1 MF-1-4):L3 快照把会话库整库拷入
34
+ * $TMPDIR 属「读快照」非「对话数据存放」——源库只读零触碰、副本即弃(OS 清空
35
+ * $TMPDIR 无任何权威数据丢失面),已按此语义登记为 data-source-registry §4 ⑰
36
+ * (ADR-0063 I2「对话数据禁入 $TMPDIR」的登记例外,taiji 侧消费面经该条豁免
37
+ * review MUST「会话内容入 $TMPDIR」)。迁移 dataDir scratch 的长期方向与代价
38
+ * 见该条目处置列——跨包 API 变更,另批裁决。
39
+ */
40
+
41
+ /** 阶梯级别标识(attempted 记录与调用方结构化日志消费)。 */
42
+ type RecoveryLevel = 'L1-direct' | 'L2-immutable' | 'L3-snapshot';
43
+ /**
44
+ * 阶梯整体失败的错误形态。attempted = 已尝试的级别序列(被门控跳过的级别不入列,
45
+ * 跳过原因并入 message);上层映射 zcode_db_unreadable 时把 attempted 与 message
46
+ * 一起带进结构化日志/恢复指引(设计 §3.4「可观测性」)。
47
+ */
48
+ declare class SqliteUnreadableError extends Error {
49
+ readonly attempted: RecoveryLevel[];
50
+ constructor(attempted: RecoveryLevel[], message: string);
51
+ }
52
+
53
+ /**
54
+ * zcode 会话库只读访问层(自 runtime services/session/zcode-import/sqlite-access.ts
55
+ * 迁入改造;runtime 侧旧文件已随共享基座实施收口删除(git 可追溯),本包为其唯一现行承载)。
56
+ *
57
+ * 访问模式(沿用既有先例):
58
+ * - 只读连接(readonly 语义 = WAL 只读不阻塞宿主写入);转换/查询全程不写库;
59
+ * - close 失败吞错(WAL 并发读下只读连接 close 失败不影响已读结果)。
60
+ *
61
+ * 相对 runtime 旧版的改造:
62
+ * - 驱动来自本包 sqlite-driver(D3 双驱动),不再固定 node:sqlite;
63
+ * - 开库统一走四级恢复阶梯(recovery.ts,设计 §3.5 单一规格)——runtime(node)
64
+ * 侧直开从来可用(F24),阶梯主要服务 bun 宿主侧的静息态 CANTOPEN(darwin bun;
65
+ * linux bun 静息态直开同样可用——bun 平台分叉矩阵登记在 __tests__/platform-matrix.ts);
66
+ * - schema 版本从「best-effort 诊断」升级为已知集闸门(KNOWN_ZCODE_SCHEMA_
67
+ * VERSIONS,版本超出已知集抛 ZcodeSchemaDriftError——错误码词表属消费侧,
68
+ * 本包不私建 zcode_* 码)。
69
+ *
70
+ * 本模块不 import 引擎包(zcode-subagent-cli 不是依赖);库路径段常量与引擎侧
71
+ * db-path.ts 同源自 @zhushanwen/subagent-engine-sdk 的 zcode-db-paths.ts(跨侧
72
+ * 契约根,两侧 import 同一常量——非各自重声明)。
73
+ */
74
+
75
+ /** 宿主库默认路径(运行时动态推导,禁硬编码绝对路径——组合根注入给消费方的默认值)。 */
76
+ declare function hostZcodeDbPath(): string;
77
+ /**
78
+ * zcode 隔离会话库绝对路径(`<dataDir>/engines/zcode/session-db/db.sqlite`)。
79
+ * 与引擎侧 zcodeSessionDbPath 同布局(引擎 dataDir = TAIJI_AGENT_DATA_DIR =
80
+ * shared getDataDir(),同一目录两侧各自推导),路径段常量同源(ZCODE_ISOLATED_DB_SEGMENTS)。
81
+ * @param dataDir taiji 数据根(getDataDir() 产物,由调用方传入——本包不做宿主路径推导)
82
+ */
83
+ declare function zcodeIsolatedDbPath(dataDir: string): string;
84
+ /**
85
+ * zcode 会话库白名单集合(封闭集合):`[隔离库(现役), 宿主库(仅存量兼容)]`。
86
+ * 与引擎侧 zcodeDbPathAllowlist 同构——wire 帧 / session 数据里的 dbPath 来自
87
+ * 不可信面,仅放行集合内精确绝对路径。集合形态(而非 `||` 列表)使未来第三个
88
+ * 合法路径只需改本函数。
89
+ * @param dataDir taiji 数据根(getDataDir() 产物,由调用方传入)
90
+ */
91
+ declare function zcodeImportDbAllowlist(dataDir: string): readonly string[];
92
+ /** schema 版本不认识(读 schema_migration 失败或版本超出已知集)。 */
93
+ declare class ZcodeSchemaDriftError extends Error {
94
+ /** 观测到的版本(表不可读时为 undefined——两种归因经此字段区分,不拆两个错误类)。 */
95
+ readonly observedVersion: string | undefined;
96
+ constructor(observedVersion: string | undefined, message: string);
97
+ }
98
+ /** 候选/会话行的最小消费列(camelCase,source 层不再触 sqlite 原始 snake_case)。 */
99
+ interface ZcodeSessionRow {
100
+ id: string;
101
+ title: string;
102
+ directory: string;
103
+ taskType: string;
104
+ /** 毫秒时间戳(session.time_created/time_updated,schema integer) */
105
+ timeCreated: number;
106
+ timeUpdated: number;
107
+ }
108
+ /**
109
+ * 单会话全量转换行集的 message 行(converter 输入)。parts 元素 = part.data 已解析
110
+ * JSON 对象本体(不包 {sequence, data} 行壳——转换器把 parts 元素直接当 part 消费,
111
+ * 包装壳会让 part.type 全体读成 undefined;part 序即数组序 = 联合序,sequence 冗余不导出)。
112
+ */
113
+ interface ZcodeTranscriptMessageRow {
114
+ id: string;
115
+ sequence: number;
116
+ data: Record<string, unknown>;
117
+ parts: Array<Record<string, unknown>>;
118
+ }
119
+ /** 只读查询面:候选行 / 单会话行 / 字节聚合 / schema 版本诊断。 */
120
+ interface ZcodeReadonlyDb {
121
+ /**
122
+ * 候选会话行(排除 subagent_child;time_updated 降序)。
123
+ * limit 可选:SQL 级截断。调用方需要全量语义(query 过滤 / total / dirs 聚合与
124
+ * pi 源同构)时不传——全表毫秒级,JS 侧再过滤后截断。
125
+ */
126
+ listCandidateSessions(opts?: {
127
+ limit?: number;
128
+ }): ZcodeSessionRow[];
129
+ /** 按原始 id 取单行(导入定位校验用);不存在返回 undefined。 */
130
+ getSessionRow(id: string): ZcodeSessionRow | undefined;
131
+ /**
132
+ * 单会话全量转换行集(converter 输入):message 按 sequence 升序,每条 message 的
133
+ * parts 按 (message.sequence, part.sequence) 联合序排好。LEFT JOIN 保留无 part 的
134
+ * message(parts 空数组)。data 列 JSON 解析失败抛 Error(调用方按 schema 漂移
135
+ * 映射消费侧错误码,本层不静默跳过——整行丢失会破坏切段配对)。
136
+ */
137
+ getSessionTranscript(sessionId: string): ZcodeTranscriptMessageRow[];
138
+ /**
139
+ * 真字节口径的会话体量:SUM(length(CAST(part.data AS BLOB)))——TEXT 直接
140
+ * length() 返回字符数,CJK 内容会低估;GROUP BY session_id。无 part 的会话不在
141
+ * 返回 Map 中(调用方按 0 处理)。
142
+ */
143
+ candidatesByteSize(sessionIds: readonly string[]): Map<string, number>;
144
+ /** best-effort 读 schema_migration 版本(诊断用,失败返回 undefined)。 */
145
+ readSchemaVersion(): string | undefined;
146
+ /** 关闭连接(吞错语义见模块头注;幂等无害——重复 close 由驱动抛错吞掉)。 */
147
+ close(): void;
148
+ }
149
+ /** openZcodeSessionDb 产物:查询面 + 实际命中的阶梯级别 + 统一收尾。 */
150
+ interface ZcodeSessionDbHandle {
151
+ db: ZcodeReadonlyDb;
152
+ /** 实际命中的四级恢复阶梯级别(恢复成功非静默事件:调用方记结构化日志)。 */
153
+ via: RecoveryLevel;
154
+ /** 幂等收尾:close + L3 快照目录清理。finally 必调。 */
155
+ dispose: () => void;
156
+ }
157
+ /**
158
+ * 打开 zcode 会话库只读连接(编排入口:存在性检查 → 四级恢复阶梯 → schema 已知集
159
+ * 闸门)。dbPath 不存在直接抛 Error(调用方映射「库不存在」语义);阶梯耗尽抛
160
+ * SqliteUnreadableError(attempted 含已尝试级别);schema 超出已知集抛
161
+ * ZcodeSchemaDriftError。其余查询期错误原样上抛(驱动错误 / data 列 JSON 非法等),
162
+ * 调用方按消费侧错误规格映射。
163
+ */
164
+ declare function openZcodeSessionDb(dbPath: string): Promise<ZcodeSessionDbHandle>;
165
+
166
+ /**
167
+ * zcode → canonical session 转换器(自 runtime services/session/zcode-import/converter.ts
168
+ * 迁入改造,session-reader-shared-core 设计 §3.3 D1 + §5 Phase 2;runtime 侧旧文件已
169
+ * 随共享基座实施收口删除(git 可追溯),本包为其唯一现行承载)。
170
+ *
171
+ * 输入 = 单会话全量行集(message 按 sequence、part 按 (message.sequence, part.sequence)
172
+ * 联合序,sqlite-access.getSessionTranscript);输出 = canonical `Entry[]`(session-core
173
+ * 严格 Entry 模型,parentId 顺序链完整)。三层映射:
174
+ * ① 消息级投影分派——逐条消息先经 classifyMessage()(projection.ts,asar 移植体)判定
175
+ * 六策略之一(或 unclassified),按消息投影对齐设计 §7.3 映射表落点(三类丢弃整条不产
176
+ * entry + L1/L2 聚合降级,unclassified 丢弃 + 独立告警码);② part 级映射——消息内部
177
+ * 按 session-import-unified 设计 §3.4 权威映射表逐条实现:T1 header/session_info /
178
+ * T2 user / T3 assistant 按 step-finish 切段 / T3b 工具名映射 / T3c stopReason 映射 /
179
+ * T4 tool output 三形态 / T5 其余 part / T6 entry id 递增链;③ compact_summary 合并——
180
+ * D2 完整规格(三路关联判据 + 单条 compaction entry + firstKeptEntryId 三级锚悬空门 +
181
+ * 1:N 最早联合序锚 + 孤儿二分),关联在发射前预扫描趟建立(compaction.ts)。
182
+ *
183
+ * 来源注记:消息投影分派 / 结构化降级(ImportDegradation 五码闭集)/ compaction D2 全规格 /
184
+ * usage RT-5#1 缺省语义 / 时间戳 RT-5#8 不伪造 1970,均自 runtime zcode-import dev 演进版
185
+ * 移植(git 可追溯);dev 版内部的降级通道(孤儿/悬空/summary 不可用 → custom +
186
+ * compaction_unlinked)随规格一并移植。
187
+ *
188
+ * 相对 dev 版的改造(canonical 契约,设计 D1「canonical 模型 = pi 形态的严格 Entry 树」):
189
+ * - emitEntry 不再 stringify 落 JSONL 行,改产 Entry 对象(toCanonicalEntry 收口,键序仍按
190
+ * `{type, id, parentId, timestamp, ...payload}` 构造——serializeSession 的字节契约
191
+ * 责任在构造方,导入侧薄包装拼 header 行 + serializeSession 即得与旧产物逐字节
192
+ * 兼容的 JSONL);message 内 pi 透传字段(timestamp/provider/model/usage/stopReason、
193
+ * toolResult 的 details/isError、session_info 的 name)是 canonical Entry 冻结接口外
194
+ * 的合法载荷字段——经宽形态构造 + 边界收口守卫承载,序列化原样落盘、reader 渲染链
195
+ * 可直接消费,不做有损裁剪。
196
+ * - entry id 生成收敛基座单点:normalizeZcodeRowId(8-hex 零填充,与 pi 自身
197
+ * randomUUID().slice(0,8) 同形态;pi 侧对 entry id 仅作 opaque map key 消费)。
198
+ * - 对外主函数 readZcodeSession(dbPath, sessionId) → NormalizedSession
199
+ * ({header, entries, degradations} 三键,形状由 session-core 类型强制;承载于
200
+ * read.ts——converter 聚焦纯转换映射,开库→查询→转换→dispose 组合编排在彼)。
201
+ * header 由库内 session 行构造:id 归一化(normalizeZcodeSessionId)、timestamp 取
202
+ * session 创建时间、cwd 缺省留空——zcode 会话无 cwd 概念,不伪造(D1)。
203
+ * 首行 `{type:'session', version:3, ...header}` 不由本模块产出(属序列化装配面:
204
+ * 导入薄包装 / reader 侧各自拼装)。
205
+ * - 时间不可解不伪造(RT-5#8):session.timeCreated / message.time.created 不可解时
206
+ * timestamp 键缺省(header.timestamp / entry.timestamp / message.timestamp 均可选),
207
+ * 降级登记一次,不以 0 产出 1970 假时间戳污染排序与按日统计。
208
+ * - 错误面归消费侧(§1.5 契约第 4 条):session 行不存在、归一化失败、查询/JSON
209
+ * 解析失败均抛普通 Error 原样上抛——reader 侧映射 zcode_* 词表、runtime 导入侧
210
+ * 映射 import_* 词表,本包不私建错误码与恢复话术(dev 版的 convertZcodeSession
211
+ * db 组合 + ImportServiceError 不移植)。
212
+ *
213
+ * 排序锚(C1 探针结论,2026-09-19 宿主库只读抽样):3 个 interactive 会话(1534/1852/29
214
+ * parts)在 ORDER BY message.sequence, part.sequence 联合序下,携带 time 字段的 part
215
+ * (text/reasoning/compaction/timeline;tool/step-* 无 time 字段)其 time.start 全程
216
+ * 非降(3415 parts 零逆序)——联合 sequence 序即事件时序,转换器按输入数组序线性消费,
217
+ * 无需二次按时间排序。
218
+ *
219
+ * 产物正确性锚(D1):产物必须能被 pi entry 消费链重放(parentId 链完整 + toolCall
220
+ * 配对)——converter.test 以最小重放器 + parseSessionContent round-trip 为可证伪断言。
221
+ */
222
+
223
+ /**
224
+ * 纯转换函数的 message 输入(内存行结构——测试直接构造,不建真库)。parts 元素 =
225
+ * part.data 已解析对象本体(与 sqlite-access ZcodeTranscriptMessageRow.parts 同构——
226
+ * 曾因行壳 {sequence, data} 包装让结构类型静默可赋值、part.type 全体读成 undefined,
227
+ * 见 sqlite-access 该接口注释;勿再包壳)。
228
+ */
229
+ interface ZcodeMessageInput {
230
+ id: string;
231
+ data: Record<string, unknown>;
232
+ parts: Array<Record<string, unknown>>;
233
+ }
234
+ /** 纯转换函数的 session 行输入(ZcodeSessionRow 的消费列子集)。 */
235
+ interface ZcodeSessionInput {
236
+ /** 原始 session.id(`sess_<uuid>` 形态)——header.id 由 normalizeZcodeSessionId 归一化。 */
237
+ id: string;
238
+ title: string;
239
+ /** 毫秒时间戳(session.time_created);header.timestamp = ISO 串、消息级时间兜底。 */
240
+ timeCreated: number;
241
+ }
242
+ /**
243
+ * 把联合序排好的 message 行集转换为 canonical entries(纯函数,映射全表)。
244
+ *
245
+ * 结构(复杂度门禁拆分后):本函数只保留装配骨架——T1 session_info / 消息循环趟 /
246
+ * 聚合并入;D2 关联预扫描 → precomputeCompactionAssociation,逐消息分派 →
247
+ * convertMessageByPolicy(compactSummary 合并发射在 emitCompactSummary),拆分不改
248
+ * 任何判定与发射顺序。
249
+ *
250
+ * @param messages 联合序排好的 message 行集(含 parts)
251
+ * @param session session 行消费列(header 来源:id 归一化 / timestamp = timeCreated ISO)
252
+ */
253
+ declare function convertZcodeTranscript(messages: ZcodeMessageInput[], session: ZcodeSessionInput): NormalizedSession;
254
+
255
+ /**
256
+ * db 读取 + 纯转换组合(zcode source 包对外主函数 readZcodeSession 的承载,自
257
+ * converter.ts 拆出:converter 聚焦纯转换映射,本文件持有开库 → 查 session 行 →
258
+ * 转换 → dispose 的组合编排)。
259
+ */
260
+
261
+ /**
262
+ * 读单会话并转换为 NormalizedSession(zcode source 包对外主函数,D1)。
263
+ *
264
+ * 契约面说明:现生产消费方(reader 扩展 / runtime 导入薄包装)均走
265
+ * openZcodeSessionDb + convertZcodeTranscript 组合形态(各自持有分相位错误映射),
266
+ * 本函数当前无生产调用方——保留导出是设计 §1.5 声明的对外主函数(未来第三源契约锚),
267
+ * 第二源落地前的契约面。
268
+ *
269
+ * 开库走 sqlite-access 的 openZcodeSessionDb(存在性 → 四级恢复阶梯 → schema 已知集
270
+ * 闸门);返回值三键 {header, entries, degradations}(NormalizedSession,session-core
271
+ * 类型强制)。db 行存在性在查询阶段复查(定位校验与会话读取间的竞态窗口)。
272
+ *
273
+ * 错误面(归消费侧,调用方按各自词表映射——reader → zcode_* / runtime 导入 → import_*):
274
+ * - db 文件不存在 / 恢复阶梯耗尽 → Error(sqlite-access 抛出,消息含路径与已尝试级别)
275
+ * - schema 版本超出已知集 → ZcodeSchemaDriftError(观测版本在 observedVersion 字段)
276
+ * - session 行不存在(zcode 侧 GC / 从未落库)→ Error,消息含 sessionId 与事实归因
277
+ * - 行 data 列 JSON 非法(schema 漂移域)→ 原始 Error 上抛(sqlite-access 不静默跳过)
278
+ */
279
+ declare function readZcodeSession(dbPath: string, sessionId: string): Promise<NormalizedSession>;
280
+
281
+ /**
282
+ * zcode session id 归一化(自 runtime services/session/zcode-import/normalize.ts 迁入
283
+ * 改造;runtime 侧旧文件已随共享基座实施收口删除(git 可追溯),本包为其唯一现行承载。
284
+ * 设计 §1.5「id 归一化
285
+ * 收敛为基座/source 单点」——候选打标与转换 header 两个调用点 import 同源实现,
286
+ * 各内联一份则单边漂移、打标失配即幂等失效)。
287
+ *
288
+ * 背景(为什么必须归一化):归一化产物是 canonical header.id = 导入幂等键,且必须
289
+ * 维持全仓不变量「session 文件名(剥 .jsonl 后)最后一个 `_` 之后的尾段 == header.id」
290
+ * (image-cache 孤儿判定/删除级联、session-reader 文件名提取、parent-session 兜底
291
+ * 匹配、短名展示共 4 处消费点)。zcode 原始 id 形如 `sess_<uuid>`,`_` 前缀会打破
292
+ * 该不变量。pi 侧对 header.id 无格式校验(恢复路径不校验——assertValidSessionId
293
+ * 仅在显式指定 id 时调用),本函数的字符集是 taiji 自定约束(pi
294
+ * assertValidSessionId 允许集的真子集,收紧方向安全;锚点
295
+ * dist/core/session-manager.js:15-19 正则字面量 :16,verifiedWith 0.84.4)。
296
+ *
297
+ * 相对 runtime 旧版的改造(设计 §1.5 source 包契约第 4 条「错误面归消费侧」):不再
298
+ * 抛 runtime services 层的 ImportServiceError(本包不私建 import_* / zcode_* 错误码),
299
+ * 归一化失败抛普通 Error——消费方(session-reader 路由 / runtime 导入薄包装)按各自
300
+ * 错误词表映射(reader → zcode_param_invalid;runtime → import_invalid_session)。
301
+ */
302
+ /**
303
+ * 归一化(防二义读法,替换规则无条件施加于结果串):
304
+ * ① 若以 `sess_` 开头则剥该前缀一次(无前缀不剥;双前缀只剥一次——`sess_sess_x` →
305
+ * `sess_x` → 步骤② → `sess-x`,函数幂等:normalize(normalize(x)) === normalize(x))
306
+ * ② 对①结果串全部 `_` → `-`
307
+ * ③ 后置条件校验:非空 ∧ 不含 `_` ∧ 字符集/首尾合法,不满足 → fail-fast(防 id 值
308
+ * 形态漂移静默打破文件名不变量——静默跳过或宽松放行会让落地文件被自家消费链误
309
+ * 解析)。错误消息只含现象 + 事实归因 + 原始 id;恢复指引归消费侧(契约④不私建
310
+ * 恢复话术),消费方按各自词表映射后自行组织。
311
+ *
312
+ * 候选域(排除 subagent_child 后)剥后为裸 uuid(v4),与 pi 自建 uuidv7 域版本位
313
+ * 不相交不撞。
314
+ */
315
+ declare function normalizeZcodeSessionId(raw: string): string;
316
+ /**
317
+ * 候选打标域转换(同一函数别名导出):候选列表的 alreadyImported 打标把原始
318
+ * session.id 映射到太极扫描集(header.id 域 = 归一化域)后比对——直接用原始 id 比对
319
+ * 会失配。与 normalizeZcodeSessionId 同一实现,确保打标域与转换 header 域构造性一致。
320
+ */
321
+ declare const zcodeCandidateKey: typeof normalizeZcodeSessionId;
322
+
323
+ export { type RecoveryLevel, SqliteUnreadableError, type ZcodeReadonlyDb, ZcodeSchemaDriftError, type ZcodeSessionRow, convertZcodeTranscript, hostZcodeDbPath, normalizeZcodeSessionId, openZcodeSessionDb, readZcodeSession, zcodeCandidateKey, zcodeImportDbAllowlist, zcodeIsolatedDbPath };