技术文档:跨工具会话迁移插件 session-import

August 14, 2026 · View on GitHub

版本:v0.1(Draft) 日期:2026-08-14 对应需求:plugin-req-session-import.md 插件代号:session-import


1. 技术栈与运行时

  • 运行时:Node 22.19+ / 24+(与 dsh dev-preview 对齐,NFR-03)。
  • DI 框架:Cordis(插件容器、依赖注入、生命周期)。
  • 语言:TypeScript(strict)。
  • 解析:基于流式读取(按消息块增量解析),避免整文件载入。
  • 知识抽取:调用 dsh 内置 LLM 通道(复用 llm-streaming seam)完成抽取,结果经 memory seam 写入。
  • 加密:Web Crypto(AES-GCM)做原文加密归档。

2. 解析适配器接口规范

export type SourceType =
  | 'claude_code' | 'codex' | 'chatgpt' | 'cursor' | 'gemini'
  | 'reasonix' | 'opencode' | 'continue' | 'aider' | 'cline'
  | 'github_copilot' | 'generic_json' | 'generic_markdown' | 'generic_transcript';

export interface RawInput {
  path: string;
  buf: Buffer;          // 原始字节(用于 detect 嗅探)
  text: string;         // 解码文本
}

export interface ParserAdapter {
  readonly source: SourceType;
  readonly schemaVersion: string;   // 版本门:源格式变更需升版
  detect(raw: RawInput): boolean;
  parse(raw: RawInput): NormalizedConversation;
}
  • detect() 应快速、无副作用(读 magic header / 特定 key)。
  • parse() 抛错需可被上层捕获,标记为「坏文件」并继续(FR-16)。
  • 注册:container.set(ParserAdapter, new ClaudeCodeAdapter())Source Ingest 收集全部实例。

3. 解析要素数据模型

映射后的统一中间表示(详见架构文档 §5.1)。解析器需尽量还原以下要素(FR-03):

要素字段说明
角色roleuser / assistant / system
文本content允许为空(如纯工具调用消息)
推理thinkingClaude/Gemini 的 thinking/reasoning 块
工具调用toolCalls[]name / args(对象)/ result(对象或原始 JSON)
文件引用attachments[]路径或内联内容引用
代码块codeBlocks[]language / code
源专有extensionscitation、diff 等原样保留(FR-06)

4. 保真映射规则(FR-04 / FR-05 / FR-06)

映射层 mapToEvents(nc): SessionEvent[] 遵循:

  1. 顺序保真:按 messages 顺序逐个产出事件,不重排。
  2. 角色保真role 直接映射到 dsh 事件角色。
  3. thinking 保真:落 thinking 事件(或并入 assistant 事件的 meta)。
  4. 工具调用还原
    • dsh 存在对应工具 → 还原为 dsh tool_call + tool_result 事件;
    • dsh 无对应工具 → 保留原始 JSON 于 tool_call.result,并置 meta.unknownTool=true,供 UI 查看(FR-05)。
  5. 附件:作为 attachment 事件或 meta,保留引用路径。
  6. 源专有字段:全部落 meta.extensions,不丢弃信息(FR-06)。

5. 流式分片导入实现(FR-10 / NFR-02)

大会话采用「解析—映射—写入」三段流水线,分段 flush:

async function* streamImport(raw: RawInput, adapter: ParserAdapter) {
  const nc = adapter.parse(raw);            // 若支持流式则增量产出
  for (const msg of nc.messages) {
    const events = mapToEvents({ messages: [msg] });
    await session.append(events);           // 增量写入事件溯源日志
    yield msg.id;                           // 进度反馈
  }
}
  • 不在内存中持有整段 NormalizedConversation 的完整体积峰值;逐消息 map + append。
  • UI 通过 yield 进度条展示。

6. 知识抽取与聚类去重(FR-11 / FR-12)

  • 抽取:对 NormalizedConversation 调用 LLM,prompt 限定输出结构化 KnowledgeItem[]
    interface KnowledgeItem {
      type: 'decision' | 'code' | 'convention';
      content: string;
      confidence: number;
      sourceRef: { sessionId: string; msgId: string };
    }
    
  • 去重聚类(FR-12):对新 item 与 memory seam 中已有条目做 embedding 相似度比对(阈值可调),命中则跳过或合并,避免重复沉淀。
  • 人工确认开关:高敏感项目可开启「抽取后人工确认再写入」,防止误抽污染记忆。

7. 隐私脱敏检测(FR-15 / AC-3)

Privacy Scanner 实现:

  • 规则集:正则匹配 API Key(sk-...、AWS、ghp_...)、手机号、邮箱、身份证、内网 IP。
  • 行为:返回 SensitiveHit[] { field, type, snippet, position }
  • 交互:UI 弹窗列出命中项,用户可「全部脱敏 / 逐项脱敏 / 忽略」。
  • 加密归档:勾选时原文以 AES-GCM 加密落本地;密钥由用户管理,不进日志。
  • 不落明文(NFR-01):密钥在内存临时处理,写入前必须脱敏或加密。

8. 双向导出格式规范(FR-13)

Exporter.export(sessionId, fmt) 支持:

格式形态用途
dsh_jsondsh 原生 session-log同 dsh 版本迁移/备份
generic_jsonNormalizedConversation跨工具通用
generic_markdown角色分段 MD阅读/迁回支持 MD 的工具
transcript行式对话轻量归档

实现要点:从 dsh session-log 逆向构建 NormalizedConversation,再序列化;meta.extensions 一并带出,保证往返不丢信息。


9. 错误处理与健壮性(FR-16)

  • 坏 / 截断文件:parser parse() 抛错 → 捕获为 SkipReport { path, reason },导入任务不中断,继续下一个文件。
  • 部分失败:单条消息映射失败 → 记录到该会话的 import_warnings,不整体回滚。
  • 冲突(FR-09):同名会话触发策略 rename | merge | skip,默认 rename(追加 -imported-N)。

10. 配置与持久化

  • 配置项:默认冲突策略、是否开启知识抽取、是否人工确认、是否加密归档、脱敏规则开关。
  • 持久化
    • 会话本体 → dsh persistence(事件溯源日志)。
    • 导入历史 → ImportHistory[] { id, source, time, archivePath, status },原文归档可回溯(FR-14)。
    • 加密归档密钥 → 用户本地密钥库,不进 dsh 日志。