Skip to content

Repository files navigation

dsh-assistant-message-forge(消息锻造台)

双面(Host + Client)DSH 插件:在会话区新增一个 消息锻造台 页签,核心是 详细上下文解析 → 记录 → 卡片化 → 动态编辑 → 开放修改

  1. context/refresh 把当前会话完整解析成一张张 ContextCard (turn/step 边界、user/assistant 消息、tool call/result、request header、 其他 log-only 事件;assistant/chunk 按轮折叠计数);
  2. 解析结果连同卡片 overrides 一起 记录$DSH_HOME/assistant-message-forge/context-<sessionId>.json
  3. 页签内按轮分组 卡片化 展示,含 surface 折叠状态(原始被替换 / 替换生效中);
  4. 卡片可 动态编辑:文本/reasoning/provider/model/usage、工具名与 arguments、工具结果文本;编辑先保存为记录,也可 保存并应用到会话 (surface replace 直接改写当前会话中的原消息)。

此外保留 v1 能力:创建/修改/删除/注入 assistant 草稿、上传识别 session.jsonl(.zstd);导入区还会做完整性预览,并可把安全可解释的损坏 恢复为新的 session-repaired-* 会话。

能力

能力 入口 说明
上下文解析+记录 会话上下文 → 刷新解析 全事件分类解析并落盘为 context 记录
上下文卡片化 会话上下文卡片流 按轮分组,边界/消息/工具/请求/其他卡片 + surface 状态
卡片动态编辑 卡片 → 编辑保存记录 修改只写入记录 override,刷新解析不丢
应用到会话 卡片 → 保存并应用到会话 / 应用到会话 user/assistant/tool-result 用 surface replace 替换原消息,模型上下文即时生效
添加/修改/删除草稿 草稿区按钮 草稿持久化在 drafts.json
注入到会话 草稿行 → 注入到当前会话 完整合成轮 + flush
导入识别 sessionlog 底部文件选择 → 导入并识别 zstd/JSONL 解码 + packed chunk 展开 + 事件识别
修复损坏 sessionlog 完整性预览 → 创建修复会话 后写分支覆盖 + 官方 crash closer + 官方 seed 校验;原日志只读
复用识别结果 识别行 → 存为草稿 / 注入 保留 reasoning+text/provider/model/usage

架构

浏览器客户端                                  DSH host (Node)
ForgeView(conversation.view 页签)
  ├─ ContextPanel:解析记录卡片流 + 动态编辑
  ├─ 草稿区:添加/修改/删除/注入按钮
  ├─ sessionlog 文件选择 + 识别/修复预览
  └── connection.rpc.call(AMF_RPC_CHANNEL) ──▶ /dsh-assistant-message-forge
                                                   ├─ DraftStore(drafts.json)
                                                   ├─ ContextRecordStore(context-*.json + overrides)
                                                   ├─ parseContext / surface replace apply
                                                   ├─ parse/repairSessionLogBytes(node:zlib zstd)
                                                   └─ injectMessage(session.append + flush)
  • Host 半通过 ctx.connection.rpc.handle() 注册通用 Connection RPC 通道(loopback 信任边界),不修改 apiproxy 允许列表。
  • Client 半注册 conversation.view 页签(scope: session),组件经 sessionId 标准 prop 拿到当前会话。

RPC 端点

通道:/dsh-assistant-message-forge

endpoint payload 返回
drafts/list {} AssistantDraft[]
drafts/save { draft: { id?, title?, content, reasoning?, provider?, model? } } 保存后的 AssistantDraft
drafts/delete { id } boolean
session/inject { sessionId, message: { content, reasoning?, provider?, model?, usage? } } { sessionId, turn, step, seq, messageId, flushed }
context/load { sessionId } 已记录的 ContextSnapshotnull
context/refresh { sessionId } 重新解析并记录后的 ContextSnapshot
records/update { sessionId, key, patch } 合并卡片 override 后的 ContextSnapshot
records/reset { sessionId } 丢弃全部 override 后的 ContextSnapshotnull
context/apply { sessionId, key } { sessionId, replacedSeq, newSeq, type, turn?, step?, flushed }
sessionlog/parse { name, dataBase64 } SessionLogParseResponse
sessionlog/repair-preview { name, dataBase64 } SessionLogRepairReport
sessionlog/repair-create { name, dataBase64 } { sessionId, flushed, report }

错误统一走 RpcResult{ ok:false, error:{ code:'internal', ... } } 分支;参数校验失败、会话不存在、当前会话存在未闭合 turn 时都会返回可读 message。

卡片与修改语义

  • 卡片解析覆盖turn/start|endstep/start|enduser/messageassistant/message(reasoning/text/provider/model/usage)、tool/calltool/resultrequest/headerrequest/context、其余 log-only 事件。 assistant/chunk 不逐条成卡,按轮统计 chunkCount
  • 记录优先records/update 只改 override;之后 context/refresh 重新解析时 会合并 override,不会丢。records/reset 丢弃全部记录修改。
  • 应用到会话 = surface replace:对 user/messageassistant/messagetool/result 追加一个 surfaceOp: {op:'replace', start:seq, end:seq} 事件, 原节点被 shadow(模型 deriveMessages 用新内容),原始 append 事件仍保留在 日志与官方对话流中;卡片流会同时显示「原始已被替换」和「替换生效」两张卡。
  • tool/result 特殊限制:DSH surface 校验只允许改写 tool-result 的 content;message id/source/isError/error wrapper 必须保持原值,因此 isError 在记录里可改,但应用时按原值写回。
  • 会话正在运行(存在未闭合 turn)时注入和应用都会拒绝,避免破坏执行边界。
  • 导入识别的投影:识别结果把 content 块投影为 text + reasoning 两条; tool-callimage 等非文本块计数为 unsupportedBlocks,注入时不会携带。
  • 传输上限:上传 base64 ≤ 128 MiB;解压后 ≤ 128 MiB;识别列表每批最多 200 条(truncated 标记)。

会话修复语义与边界

  • 后写分支覆盖:物理日志出现 seq < 当前逻辑长度 时,从该 seq 回滚候选尾部, 再接入后写分支;报告会列出 fromSeq 和丢弃事件数。
  • 不猜缺口:坏 JSON、坏 packed row 或向前跳号会让扫描保守停止;不会伪造缺失业务事件。
  • 官方闭合:候选前缀交给 @deepseek-ai/dsh-sessioninterruptedTurnClosers() 补齐未完成 tool/step/turn。
  • 官方终审:使用 Session.create(seed) 重新验证从 0 连续的 seq、事件 envelope、 surface append/replace 引用和 JSON 可持久化约束;失败则禁用创建按钮。
  • 不可覆盖:插件不截断、不重命名、不写回源 session.jsonl(.zstd);只创建新的 session-repaired-* 会话,并把原 session id 记录为 parentSession

目录结构

assistant-message-forge/
├── cordis.patch.yml          # bundle patch:insert 一行,包名即 loader 行名
├── package.json              # dsh.client + dsh.bundle + exports["./client"]
├── tsconfig.json             # tsc:lib/index.js + lib/types(Host 半 + 中间 Client JS)
├── tsdown.config.ts          # tsdown:lib/client.js 浏览器闭包工厂
├── scripts/
│   ├── check-client-bundle.mjs  # 校验 client bundle 只 require 模块表内依赖
│   ├── test-session-repair.mjs  # 分支回退 + crash closer 纯函数回归
│   └── e2e-smoke.mjs            # 编辑/apply/导入/修复新会话全流程冒烟
└── src/
    ├── index.ts              # Host 半:RPC 通道 + 全部端点路由
    ├── store.ts              # 草稿 JSON 持久化
    ├── context.ts            # 详细上下文解析 + context 记录 + surface replace apply
    ├── sessionlog.ts         # zstd 帧扫描 + JSONL 解码 + 事件识别
    ├── shared-types.ts       # 双面共享 wire 类型
    └── client/
        ├── index.ts          # Client 半:注册 conversation.view 页签
        ├── ForgeView.tsx     # 锻造台三段式 UI(上下文/草稿/导入)
        ├── ContextPanel.tsx  # 解析记录卡片流 + 动态编辑
        ├── ForgeView.module.css
        └── locales.ts        # zh/en + LocaleNamespaceMap 增广

构建

cd D:/codeproject/dsh-plugin/plugins/assistant-message-forge
pnpm install
pnpm run build          # rm -rf lib && tsc && tsdown
pnpm run test:session-repair
pnpm run test:client-bundle

产物:

  • lib/index.js — Host 半(@deepseek-ai/* import 保持外部,由安装 profile 解析)
  • lib/client.js — 浏览器闭包工厂(window.__ModuleLoader__.load({id:"dsh-assistant-message-forge"})
  • lib/types/** — 声明文件

激活(Web UI 需要 bundle 安装,不能用裸 --patch

--patch 只能把 Host 源码挂进插件树;dsh.client 扫描器按包名require.resolve 发现 exports["./client"],因此浏览器半必须作为 bundle 安装进 profile:

cd D:/codeproject/deepseek-harness

# 独立测试 profile(第一次会只初始化 base,手动把 web-app bundle 加进 dsh.profile.bundles)
pnpm dsh plugin --profile amf-test add D:/codeproject/dsh-plugin/plugins/assistant-message-forge
pnpm dsh --profile amf-test --port 3090

# 正式使用:装进默认 web profile,重启 dsh web 生效
pnpm dsh plugin --profile web add D:/codeproject/dsh-plugin/plugins/assistant-message-forge
pnpm dsh web

dsh web--profile web 的硬别名;给自定义 profile 启动 Web 应用要用 pnpm dsh --profile <name> --port <port>,并保证该 profile 的 dsh.profile.bundles 依次包含 @deepseek-ai/dsh-base@deepseek-ai/dsh-web-appdsh-assistant-message-forge

开发期热更

  • Client 半:client-hmr 会 watch 所有已挂载 bundle 的 lib/client.js。改完 src/client/**pnpm run build,浏览器自动拉取新 rev(本插件构建约 1 秒)。
  • Host 半:web profile 的 loader HMR 关闭;改 src/index.ts / src/store.ts / src/sessionlog.ts 后需重启 dsh web

E2E 冒烟

对运行中的实例执行按钮级验证(创建一次性会话 → 上下文刷新解析 → 助手卡片记录编辑/应用 → 丢弃记录修改 → 草稿添加/修改/注入 → 导入识别 → 损坏日志预览/创建修复会话 → 删除草稿):

$env:AMF_URL='http://127.0.0.1:3090'
$env:SESSION_LOG=$env:DSH_SESSION_JSONL
node scripts/e2e-smoke.mjs

脚本用 D:/codeproject/dsh-browser/node_modules/playwright(可用 PLAYWRIGHT_ROOT 覆盖), 先经 RPC 创建一个 workspace-attached 的一次性会话再逐项点击中文 UI,避免污染真实会话; PLAYWRIGHT_CHANNEL=chrome 可显式使用本机 Chrome,AMF_HEADED=1 可打开可见浏览器窗口。

About

DSH assistant message forge: create/modify/inject test assistant messages, import session.jsonl(.zstd) session logs (client plugin)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages