coding-agents
vectorize-io/hindsightHindsight:会学习的 Agent 长期记忆系统,自动召回/保存、知识页、深度反思与按仓库隔离的记忆银行。
DeepSeek Harness 上下文管理:每轮对话自动总结为记录并按优先级排序注入,⭐ 置顶记录跨会话继承,自定义文本每轮注入;真实对话任意范围折叠成摘要真省 token,全状态持久化重启不丢。注意:非标准一键安装——需手动将三个包目录复制到 profiles/node_modules、粘贴 cordis.patch.yml 接线并重启 DSH。
DeepSeek Harness 的自定义插件:自动记录每次对话交换,按优先级注入 agent 的 prompt,并提供浏览器管理窗口(搜索、编辑、跨会话置顶、拖拽排序、导出、清空)。同时直接控制实际的对话上下文文本:查看模型真正看到的每条消息、把选中的历史范围折叠成摘要、把置顶记录/自定义文本注入真实消息流。
conversationList):列出模型实际看到的每条消息(角色 / 文本 / token 估算 / 总占用),含之前折叠留下的摘要节点。foldRange + foldStatus):在「真实对话」里选一段消息范围,排队到下一次对话开始时由服务端在 agent/pre-step 内执行 compaction.compactRegion(),把旧文本真正折叠成一条摘要(追加式日志下唯一合法的移除方式)。折叠需在回合内执行,所以是「下一条消息后生效」。setInjectionText / getInjectionText + injectIntoMessages):置顶(⭐)记录、本会话记录、以及「注入设置」里的自定义文本,会作为一条真实消息前置注入每次模型请求(pre-step 修改 messages),而不是只放在辅助提示块里。第 1 步:把插件文件放进 DSH 的插件目录
C:\Users\你的用户名\.dsh\profiles\node_modules(没有 node_modules 文件夹就新建一个);dsh-context-manager-service-luxidsh-context-manager-agent-luxidsh-context-manager-ui-luxinode_modules 里应该有这三个同名文件夹,里面各有一个 package.json,就对了。偷懒法:在本仓库目录双击运行
install.ps1(PowerShell 里执行.\install.ps1),它会自动帮你复制三个包。如果它提示[FAIL]某包,说明该包的源目录缺少package.json(目录没放对)。
第 2 步:告诉 DSH 要用这三个插件(只做一次)
C:\Users\你的用户名\.dsh\profiles\web\cordis.patch.yml;- insert: 开头的内容,把里面三行插件并进去,别重复 - insert:):- insert:
- id: compaction-passive
name: '@deepseek-ai/dsh-compaction-basic'
config:
auto: false
- id: context-manager
name: 'dsh-context-manager-service-luxi'
config:
maxRecordsPerSession: 200
injectIntoMessages: true
maxInjectionChars: 800
maxInjected: 5
maxGlobalInjected: 3
maxCharsPerRecord: 200
summarize: true
- id: context-manager-ui
name: 'dsh-context-manager-ui-luxi'
第 3 步:重启 DSH,打开界面
出问题先看:文件路径对不对、
cordis.patch.yml有没有保存、DSH 有没有真重启。还不行就把报错发给 AI 帮你查。
目标:把本仓库的三个 npm 包安装进 DeepSeek Harness 运行实例,并在 web 组合中接线三个插件行。
环境变量:DSH_HOME 未设置时使用 %USERPROFILE%\.dsh(Windows)。下文中 <DSH> = ${USERPROFILE}\.dsh。
步骤 1 — 同步包文件
把以下三个目录完整复制(含子文件,覆盖同名文件)到 <DSH>\profiles\node_modules\:
| 源(仓库内) | 目标 |
|---|---|
dsh-context-manager-service-luxi\ | <DSH>\profiles\node_modules\dsh-context-manager-service-luxi\ |
dsh-context-manager-agent-luxi\ | <DSH>\profiles\node_modules\dsh-context-manager-agent-luxi\ |
dsh-context-manager-ui-luxi\ | <DSH>\profiles\node_modules\dsh-context-manager-ui-luxi\ |
等价命令(仓库根目录,PowerShell):powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1
install.ps1 会自动创建目标目录并复制三个包;仅当某包源目录缺少 package.json 时才输出 [FAIL]——此时请检查第一步的目录摆放。该脚本只负责复制,不修改 cordis.patch.yml(接线需手动完成)。
步骤 2 — 接线 cordis.patch.yml(仅首次安装)
编辑 <DSH>\profiles\web\cordis.patch.yml(UTF-8,不存在则创建)。向 insert 列表追加三个条目,不得覆盖现有条目;若文件尚无 - insert:,先写 - insert: 作为键。追加内容:
- id: compaction-passive
name: '@deepseek-ai/dsh-compaction-basic'
config:
auto: false
- id: context-manager
name: 'dsh-context-manager-service-luxi'
config:
maxRecordsPerSession: 200
injectIntoMessages: true
maxInjectionChars: 800
maxInjected: 5
maxGlobalInjected: 3
maxCharsPerRecord: 200
summarize: true
maxSummaryChars: 400
maxDescriptionChars: 200
summarizeTimeoutMs: 20000
summarizeMaxInputChars: 6000
summarizeMaxOutputTokens: 300
- id: context-manager-ui
name: 'dsh-context-manager-ui-luxi'
约束与原因:
compaction-passive 必须存在:浏览器折叠/压缩按钮需要根平面可解析 compaction 服务;该行 auto: false 只响应显式调用,不会与 preset 内自动压缩实例冲突。服务端对它是可选依赖(运行时 ctx.get),缺行时只影响折叠按钮,不影响记录。clientModules 扫描,挂那里 bundle 会 404。dsh-context-manager-agent-luxi(仅回退注入,injectIntoMessages: false 时生效),不挂不影响功能。步骤 3 — 重启与验证
升级:仅改动包内代码时,重复步骤 1 与步骤 3(步骤 2 只做一次)。Service 改动必须重启 DSH;纯 UI(client.js)改动刷新页面即可。
故障排查:
typert gateway: ... business result failed boundary validation:Remote 方法返回了 undefined 值键——只附加有值的键,或排查对应返回构造。compaction-passive 行。cordis.patch.yml 与浏览器刷新。| 包 | 平面 | 职责 |
|---|---|---|
dsh-context-manager-service-luxi | Host | 持久化存储(context_manager domain)+ 全局记录(所有 preset 的根会话都自动记录,summarize 可选 LLM 提炼)+ Remote API:list / count / record / remove / reorder / update / setGlobal / clear / listGlobal / compact / conversationList / foldRange / foldStatus / setInjectionText / getInjectionText / getSettings / setSettings;根平面 agent/pre-step 监听器:执行排队的历史折叠、把记录/自定义文本注入真实消息流 |
dsh-context-manager-agent-luxi(lib/index.js) | Agent preset 行 | 仅保留回退注入:当服务端消息注入关闭(injectIntoMessages: false)时,把记录注入 system-prompt 快照。记录逻辑已退役(2026-08-17 起由服务端全局接管) |
dsh-context-manager-ui-luxi | Web 层行(不是 preset 行) | 浏览器 UI:输入框右下角「上下文」按钮 + 管理窗口(记录 / 真实对话 / 注入设置 三个标签页)。host 半是空操作,纯为把客户端 bundle 送进浏览器 |
⚠️ 为什么 UI 必须是 Web 层行而不是 preset 行:浏览器客户端 bundle 由
clientModules服务发现,它只扫描 loader 条目(web 组合的行);agent preset 的组成树是直接插入的作用域子树,明确不在ctx.loader.entries()里(见dsh-agent-presets/lib/types/mount.js注释),所以 preset 行里挂dsh.client永远不会被扫描到、bundle 永远 404、页面清单window.__DSH_BOOT__里也没有它。dsh-context-manager-ui-luxi作为 web patch 行挂载后,UI 在所有会话都可用。
💡 记录范围(2026-08-17 方案 B):记录从 agent 行搬进 host 服务——所有 preset 的根会话都自动记录(子代理/工作流子会话不记录,避免污染与 token 浪费)。之前"只有 my-agent preset 会话才记录"的限制已消除。
每条记录:
{
"id": "uuid",
"summary": "对话总结(加粗,在上)",
"description": "对话简要描述(在下)",
"global": false, // true = 跨会话置顶,注入所有会话,永不被自动清理
"startSeq": 12, // 该记录对应的真实对话消息范围(surface seq,自动记录)
"endSeq": 18, // 删记录时可选择同时折叠此范围(remove alsoFold)
"createdAt": 1786817005199
}
maxRecordsPerSession 时删除 createdAt 最旧的非置顶记录(⭐ 置顶记录不受上限影响)。summarize: true):每条交换异步调用当前默认模型路由提炼两行(总结: / 描述:),失败自动回退为截断。connection.rpc.call('/api', 'contextManager/<method>', { args }) 调用)| 方法 | 参数 | 返回 |
|---|---|---|
list | sessionId | { records } |
count | sessionId | { count } |
record | sessionId, summary, description? | { id } |
remove | sessionId, id | { removed } |
reorder | sessionId, orderedIds[] | { records } |
update | sessionId, id, summary?, description? | { updated } |
setGlobal | sessionId, id, global | { global } |
clear | sessionId | { cleared } |
listGlobal | — | { records } |
compact | sessionId | { compacted }(自动选最旧可用范围折叠,需空闲) |
conversationList | sessionId | { nodes: [{seq, role, text, hasBlocks, tokens}], totalTokens, surfaceTokens } |
foldRange | sessionId, start, end | { queued, requestId }(下一条消息时执行) |
foldStatus | sessionId | { status: none|queued|running|done|failed, message?, at? } |
setInjectionText | sessionId, text | { saved } |
getInjectionText | sessionId | { text } |
getSettings | — | { settings: { maxInjected, maxGlobalInjected, maxInjectionChars, maxCharsPerRecord, maxRecordsPerSession, injectIntoMessages } }(含静态配置默认值) |
setSettings | patch | { saved }(运行时覆盖注入参数,持久化,重启不丢;未知键忽略) |
clearAll | — | { cleared }(清空所有会话记录,保留运行时设置) |
previewInjection | sessionId | { text }(当前注入块合成效果预览) |
foldHistory | sessionId | { history: [{ at, startSeq, endSeq }] }(最近的折叠审计,最新在前) |
「注入设置」标签页除了自定义文本,还能调注入量参数(默认值与 cordis 配置一致,保存后覆盖静态配置,无需重启):
| 键 | 默认 | 含义 |
|---|---|---|
maxInjected | 5 | 每轮注入的本会话记录条数 |
maxGlobalInjected | 3 | 每轮注入的跨会话置顶记录条数 |
maxInjectionChars | 800 | 注入块总字符预算(截断整块) |
maxCharsPerRecord | 200 | 每条记录注入时的字符预算 |
maxRecordsPerSession | 200 | 每会话记录上限(超出清理最旧非置顶记录) |
injectIntoMessages | true | 是否在每轮模型请求前置注入块(临时省 token 可关) |
同页还有「预览注入块」按钮:直接看当前设置 + 记录合成出来的注入文本长什么样。
startSeq/endSeq),记录卡片上显示「对话第 X–Y 条消息」(surface 1-based 位置),点击标签直接跳到真实对话对应位置(高亮 3 秒)。remove(alsoFold)):摘要删除 + 底层对话文本从模型上下文移除,下一条消息时执行。afterFold)自动清理受影响记录的范围字段——被折叠的消息 seq 已从 surface 消失,相关记录退化为「仅摘要」,避免旧范围再次折叠时报错;同时记入折叠审计(真实对话页底部显示最近折叠)。profile 值仅作旧数据兼容、行为同全局。expiresAt;过期记录不再出现在记录列表或真实注入块中,且不会占用记录上限名额。tokenMeter 不可用时按字符数/4 估算,不再显示全 0。# 语法检查
node --check .\dsh-context-manager-service-luxi\lib\index.js
node --check .\dsh-context-manager-agent-luxi\lib\index.js
node --check .\dsh-context-manager-agent-luxi\lib\client.js
# 单元测试(node:test,纯逻辑部分;测试 import 已安装副本,改源码后先跑 .\install.ps1 同步)
.\test\run.ps1
# 等价的直接命令(普通终端可用;受限/沙箱 shell 下 node --test 的子进程管道会被拦截,改用上面脚本)
node --test .\test\
turn/end 时落库;若回合无回复则下一条用户消息或会话关闭时落库)。回合内多次助手文本输出(工具调用步骤)合并为一条。所以记录条数 ≈ 对话轮数,而不是消息条数——「真实对话」页的消息数(含工具调用/结果)会明显多于记录数,这是预期的。记录卡片上的「对话第 X–Y 条消息」是 surface 中的 1-based 位置,与「真实对话」页序号一致;tooltip 里保留原始事件 seq。agentDefaultModel),不可用时自动回退截断。compactRegion 只能在回合内(open turn)执行,所以 foldRange 先排队、由根平面 pre-step 监听器在下次对话开始时执行;期间不产生新对话则一直排队。排队前会预检所选 seq 是否还在 surface(已折叠过的范围立即报错而不是排队后失败)。shrinkRange)或整体移除,避免旧范围再次折叠时报错;同时记入审计(「压缩对话」显示为「自动压缩」)。maxInjectionChars 是整条消息的总预算——分节标题、来源/可信标记、安全样板行都算在内。自定义注入优先预留(至少 maxCharsPerRecord、至多半预算),记录头部只分剩余空间,最后还有一次仅收缩记录头部的硬截断——记录再多也永远挤不掉自定义注入。source.kind: "plugin" 标记,不会被误记为真实用户输入。messages 的最前面,真实用户输入保持在最后。setGlobal(false) 取消置顶时直接删除字段而非存 false;导出 MD/JSON 包含对话范围信息。