让新会话继承上次进度,召回项目背景与偏好
25.9/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/12 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add qinghui316/dsh-memory该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · chat
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 73 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
⚠︎ 实装验证未通过(dependency_conflict · 2026/10/12) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 176 处,展示前 91 条)
- 发起网络请求packages/client/src/controller.ts:9
const response = await fetch(`/api/dsh-memory${path}`, { - 发起网络请求packages/client/src/controller.ts:25
const value = await this.request(path, { signal: controller.signal }) - 发起网络请求packages/client/src/controller.ts:36
return this.request(path, { method: 'POST', body: JSON.stringify(payload) }) - 写入本地文件packages/core/src/store.ts:348
mkdirSync(dirname(path), { recursive: true, mode: 0o700 }) - 写入本地文件packages/core/src/store.ts:351
mkdirSync(this.skillsRoot, { recursive: true, mode: 0o700 }) - 写入本地文件packages/core/src/store.ts:353
mkdirSync(this.knowledgeRoot, { recursive: true, mode: 0o700 }) - 写入本地文件packages/core/src/store.ts:356
mkdirSync(this.offloadRoot, { recursive: true, mode: 0o700 }) - 删除本地文件packages/core/src/store.ts:366
rmSync(`${path}-wal`, { force: true }) - 删除本地文件packages/core/src/store.ts:367
rmSync(`${path}-shm`, { force: true }) - 写入本地文件packages/core/src/store.ts:368
copyFileSync(backup, path) - 读取本地文件packages/core/src/store.ts:381
if (!existsSync(this.path)) return undefined - 写入本地文件packages/core/src/store.ts:394
if (!existsSync(backup)) copyFileSync(this.path, backup) - 读取本地文件packages/core/src/store.ts:394
if (!existsSync(backup)) copyFileSync(this.path, backup) - 读取本地文件packages/core/src/store.ts:400
if (process.platform === 'win32' || !existsSync(path)) return - 删除本地文件递归packages/core/src/store.ts:2453
rmSync(stage, { recursive: true, force: true }) - 删除本地文件递归packages/core/src/store.ts:2454
rmSync(destination, { recursive: true, force: true }) - 读取本地文件packages/core/src/store.ts:2460
if (lstatSync(source).isSymbolicLink()) throw new Error('Skill resources may not be symbolic links') - 读取本地文件packages/core/src/store.ts:2461
files.set(resource.path, { bytes: readFileSync(source), ...(resource.mimeType === undefined ? {} : { mimeType: resource.mimeType }) }) - 删除本地文件递归packages/core/src/store.ts:2481
rmSync(stage, { recursive: true, force: true }) - 删除本地文件递归packages/core/src/store.ts:2546
rmSync(this.versionRoot(skillId, version), { recursive: true, force: true }) - 删除本地文件packages/plugin/src/artifacts.ts:288
else await rm(join(directory, 'content.txt'), { force: true }) - 写入本地文件packages/plugin/src/artifacts.ts:313
await mkdir(path, { recursive: false, mode: 0o700 }).catch((error: NodeJS.ErrnoException) => { - 读取本地文件packages/plugin/src/artifacts.ts:316
const stat = await lstat(path) - 写入本地文件packages/plugin/src/artifacts.ts:318
await chmod(path, 0o700) - 读取本地文件packages/plugin/src/artifacts.ts:331
const existing = await lstat(path).catch((error: NodeJS.ErrnoException) => error.code === 'ENOENT' ? undefined : Promise.reject(error)) - 删除本地文件packages/plugin/src/artifacts.ts:332
if (existing?.isSymbolicLink()) { await rm(temp, { force: true }); throw new Error('refusing to replace Artifact symlink') } - 写入本地文件packages/plugin/src/artifacts.ts:333
await rename(temp, path) - 写入本地文件packages/plugin/src/artifacts.ts:334
await chmod(path, 0o600) - 读取本地文件packages/plugin/src/artifacts.ts:368
const stat = await lstat(path) - 读取本地文件packages/plugin/src/artifacts.ts:370
return JSON.parse(await readFile(path, 'utf8')) as ArtifactEnvelope - 读取本地文件packages/plugin/src/artifacts.ts:412
payload = await readFile(join(directory, 'content.txt'), 'utf8').catch(async (error: NodeJS.ErrnoException) => { - 写入本地文件packages/plugin/src/artifacts.ts:420
await chmod(directory, 0o700) - 写入本地文件packages/plugin/src/artifacts.ts:421
await chmod(join(directory, 'envelope.json'), 0o600) - 读取本地文件packages/plugin/src/artifacts.ts:437
const stat = await lstat(directory) - 删除本地文件递归packages/plugin/src/artifacts.ts:454
if (artifact.relativePath !== undefined) await rm(await this.resolveRecordedDirectory(artifact.relativePath), { recursive: true, force: true }) - 执行外部命令固定命令packages/plugin/src/git-snapshot.ts:10
const child = spawn('git', ['-c', 'core.hooksPath=/dev/null', ...args], { - 写入本地文件packages/plugin/src/git-snapshot.ts:63
mkdirSync(parent, { recursive: true, mode: 0o700 }) - 删除本地文件递归packages/plugin/src/git-snapshot.ts:64
rmSync(destination, { recursive: true, force: true }) - 写入本地文件packages/plugin/src/git-snapshot.ts:65
mkdirSync(destination, { recursive: true, mode: 0o700 }) - 执行外部命令固定命令packages/plugin/src/git-snapshot.ts:69
const git = spawn('git', ['-c', 'core.hooksPath=/dev/null', 'archive', '--format=tar', commit], { - 执行外部命令固定命令packages/plugin/src/git-snapshot.ts:72
const tar = spawn('tar', ['-xf', '-', '-C', destination], { stdio: ['pipe', 'ignore', 'pipe'] }) - 读取本地文件packages/plugin/src/http.ts:276
await new Promise<void>((resolvePromise, reject) => createReadStream(downloadPath).on('error', reject).on('end', resolvePromise).pipe(res)) - 读取本地文件packages/plugin/src/http.ts:294
await new Promise<void>((resolvePromise, reject) => createReadStream(path).on('error', reject).on('end', resolvePromise).pipe(res)) - 读取本地文件packages/plugin/src/knowledge-worker.ts:24
if (!existsSync(path)) return 0 - 读取本地文件packages/plugin/src/knowledge-worker.ts:25
const entry = lstatSync(path) - 读取本地文件packages/plugin/src/knowledge-worker.ts:28
return readdirSync(path).reduce((total, name) => total + directorySize(join(path, name)), 0) - 删除本地文件递归packages/plugin/src/knowledge-worker.ts:33
rmSync(dirname(request.sourceRoot), { recursive: true, force: true }) - 写入本地文件packages/plugin/src/knowledge-worker.ts:34
mkdirSync(dirname(request.sourceRoot), { recursive: true, mode: 0o700 }) - 读取本地文件packages/plugin/src/knowledge-worker.ts:37
if (existsSync(previousIndex) && statSync(previousIndex).isDirectory()) cpSync(previousIndex, savedIndex, { recursive: true }) - 读取本地文件packages/plugin/src/knowledge-worker.ts:37
if (existsSync(previousIndex) && statSync(previousIndex).isDirectory()) cpSync(previousIndex, savedIndex, { recursive: true }) - 读取本地文件packages/plugin/src/knowledge-worker.ts:44
if (existsSync(savedIndex)) { - 删除本地文件递归packages/plugin/src/knowledge-worker.ts:46
rmSync(savedIndex, { recursive: true, force: true }) - 删除本地文件递归packages/plugin/src/knowledge-worker.ts:53
rmSync(join(request.sourceRoot, '.codegraph'), { recursive: true, force: true }) - 读取本地文件packages/plugin/src/knowledge.ts:255
const entry = lstatSync(absolute) - 读取本地文件packages/plugin/src/maintenance.ts:53
if (!existsSync(root)) return { bytes: 0, files: 0 } - 读取本地文件packages/plugin/src/maintenance.ts:58
try { directory = await opendir(current) } catch { continue } - 读取本地文件packages/plugin/src/maintenance.ts:62
try { info = await lstat(path) } catch { continue } - 读取本地文件packages/plugin/src/maintenance.ts:75
for await (const chunk of createReadStream(path)) { const buffer = Buffer.from(chunk); bytes += buffer.length; hash.update(buffer) } - 读取本地文件packages/plugin/src/maintenance.ts:205
const bytes = statSync(path).size - 读取本地文件packages/plugin/src/maintenance.ts:223
.reduce((sum, path) => sum + (existsSync(path) ? statSync(path).size : 0), 0) - 写入本地文件packages/plugin/src/maintenance.ts:314
const root = join(dirname(this.store.path), '.cleanup', runId); await mkdir(root, { recursive: true, mode: 0o700 }) - 删除本地文件递归packages/plugin/src/maintenance.ts:339
await rm(root, { recursive: true, force: true }) - 写入本地文件packages/plugin/src/maintenance.ts:352
await rename(source, destination); journal.moves.push({ from: source, to: destination, kind: candidate.kind, recordId: candidate.recordId }) - 删除本地文件递归packages/plugin/src/maintenance.ts:386
if (candidate?.status === 'deleted') { await rm(item.to, { recursive: true, force: true }); continue } - 写入本地文件packages/plugin/src/maintenance.ts:387
if (existsSync(item.to) && !existsSync(item.from)) { await mkdir(dirname(item.from), { recursive: true }); await rename(item.to, item.from) } - 写入本地文件packages/plugin/src/maintenance.ts:387
if (existsSync(item.to) && !existsSync(item.from)) { await mkdir(dirname(item.from), { recursive: true }); await rename(item.to, item.from) } - 写入本地文件packages/plugin/src/maintenance.ts:505
await mkdir(dirname(outputPath), { recursive: true, mode: 0o700 }) - 写入本地文件packages/plugin/src/maintenance.ts:508
const zip = new yazl.ZipFile(); const output = createWriteStream(outputPath, { mode: 0o600 }) - 删除本地文件递归packages/plugin/src/portability-archive.ts:73
await rm(options.extractTo, { recursive: true, force: true }) - 写入本地文件packages/plugin/src/portability-archive.ts:74
await mkdir(options.extractTo, { recursive: true, mode: 0o700 }) - 写入本地文件packages/plugin/src/portability-archive.ts:150
mkdirSync(dirname(destination), { recursive: true, mode: 0o700 }) - 写入本地文件packages/plugin/src/portability-archive.ts:152
const output = destination === undefined ? undefined : createWriteStream(destination, { flags: 'wx', mode: 0o600 }) - 读取本地文件packages/plugin/src/portability.ts:104
async function hashFile(path: string): Promise<{ bytes: number; sha256: string }> { const hash = createHash('sha256'); let bytes = 0; for await (const - 读取本地文件packages/plugin/src/portability.ts:106
function walk(root: string): string[] { if (!existsSync(root)) return []; const result: string[] = []; const visit = (current: string): void => { for - 读取本地文件packages/plugin/src/portability.ts:106
function walk(root: string): string[] { if (!existsSync(root)) return []; const result: string[] = []; const visit = (current: string): void => { for - 读取本地文件packages/plugin/src/portability.ts:106
function walk(root: string): string[] { if (!existsSync(root)) return []; const result: string[] = []; const visit = (current: string): void => { for - 写入本地文件packages/plugin/src/portability.ts:107
function copyTreeSync(source: string, target: string): void { mkdirSync(target, { recursive: true, mode: 0o700 }); for (const file of walk(source)) { - 写入本地文件packages/plugin/src/portability.ts:107
function copyTreeSync(source: string, target: string): void { mkdirSync(target, { recursive: true, mode: 0o700 }); for (const file of walk(source)) { - 读取本地文件packages/plugin/src/portability.ts:108
function verifyManifestFiles(root: string, manifest: PortabilityManifest, activated = false): void { for (const file of manifest.files) { const safe = - 读取本地文件packages/plugin/src/portability.ts:111
const markerPath = join(memoryRoot, RESTORE_MARKER); if (!existsSync(markerPath)) return { status: 'none' } - 写入本地文件packages/plugin/src/portability.ts:121
mkdirSync(rollback, { recursive: true, mode: 0o700 }) - 写入本地文件packages/plugin/src/portability.ts:122
for (const name of ['memory.sqlite3', 'skills', 'knowledge', 'offload']) { const active = join(memoryRoot, name); if (!existsSync(active)) continue; c - 写入本地文件packages/plugin/src/portability.ts:122
for (const name of ['memory.sqlite3', 'skills', 'knowledge', 'offload']) { const active = join(memoryRoot, name); if (!existsSync(active)) continue; c - 写入本地文件packages/plugin/src/portability.ts:123
copyFileSync(join(marker.stagedPath, 'database.sqlite3'), join(memoryRoot, 'memory.sqlite3')) - 删除本地文件递归packages/plugin/src/portability.ts:128
rmSync(markerPath, { force: true }); rmSync(marker.stagedPath, { recursive: true, force: true }); return { status: 'restored' } - 删除本地文件递归packages/plugin/src/portability.ts:130
for (const name of ['memory.sqlite3', 'skills', 'knowledge', 'offload']) rmSync(join(memoryRoot, name), { recursive: true, force: true }); for (const - 删除本地文件packages/plugin/src/portability.ts:147
for (const created of [...journal.createdFiles].reverse()) rmSync(created, { force: true }) - 删除本地文件packages/plugin/src/portability.ts:148
for (const directory of [...journal.createdDirectories].reverse()) { try { rmSync(directory) } catch { } } - 删除本地文件递归packages/plugin/src/portability.ts:149
rmSync(journal.stagingPath, { recursive: true, force: true }) - 删除本地文件packages/plugin/src/portability.ts:153
rmSync(file, { force: true }) - 读取本地文件packages/plugin/src/wiki.ts:89
const text = readFileSync(absolute, 'utf8').slice(0, 500_000)
扫描于 2026/11/2(1 天前) · 范围 全包扫描(包内未声明入口) · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-client-ui-layout@deepseek-ai/dsh-client-ui-sidebar@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-compaction@deepseek-ai/dsh-llm@deepseek-ai/dsh-scope给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 继承上次会话进度
- 召回项目背景
- 召回用户偏好
DeepSeek Harness 本地单用户长期记忆插件
README
由 DeepSeek 最新模型翻译生成dsh-memory
让下一次对话继承上一次工作的结果。
Local memory and project knowledge for DeepSeek Harness.
核心能力 ·工作原理 ·安装 ·模式 ·安全 ·文档
它解决什么问题
一个项目做得越久,新会话的启动成本就越高:你需要重新解释项目结构、当前进度、个人偏好、已经验证过的做法,以及上一次工具调用留下了什么结果。
dsh-memory 将这些内容整理为本地、可查询、可维护的长期资产。它不只是保存聊天记录,还会把事实、工作方式、代码知识、工具产物和任务进度连接起来,让后续会话能够召回背景、复用经验并继续未完成的工作。
它是 DeepSeek Harness 的外部插件,通过 Harness 提供的 Bundle、Agent、Tool、Skill 和 Web 扩展接口接入,不修改或 fork DeepSeek Harness。
展开完整文档(README 剩余 1 节)
dsh-memory 让下一次对话继承上一次工作的结果。 Local memory and project knowledge for DeepSeek Harness. 核心能力 ·工作原理 ·安装 ·模式 ·安全 ·文档 它解决什么问题 一个项目做得越久,新会话的启动成本就越高:你需要重新解释项目结构、当前进度、个人偏好、已经验证过的做法,以及上一次工具调用留下了什么结果。 dsh-memory 将这些内容整理为本地、可查询、可维护的长期资产。它不只是保存聊天记录,还会把事实、工作方式、代码知识、工具产物和任务进度连接起来,让后续会话能够召回背景、复用经验并继续未完成的工作。 它是 DeepSeek Harness 的外部插件,通过 Harness 提供的 Bundle、Agent、Tool、Skill 和 Web 扩展接口接入,不修改或 fork DeepSeek Harness。 核心能力 | 能力 | 它会保存什么 | 下一次任务如何使用 | | 分层记忆 | 带事件时间的 L0 对话、七类结构化 L1、L2 项目 Scene、L3 个人 Persona | 按 workspace、类型、现实有效期和状态召回事实、项目状态、偏好与工作方式 | | Skill | 从完整工作轨迹中沉淀的 SOP、背景知识和偏好 | Harness 按当前问题路由相关 Skill,需要时再加载正文 | | 项目知识 | CodeGraph、Wiki、源码索引、调用关系和影响范围 | 搜索符号、浏览源码、追踪 callers/callees、分析改动影响 | | 任务连续性 | 完整工具结果 Artifact 与版本化 Task Canvas | 找回证据、产物、依赖、进度和下一步,继续历史任务 | | 本地工作台 | 记忆、技能、项目知识、产物、任务、运行与设置 | 浏览、纠正、保护、回滚、备份、导入和维护全部本地数据 | 界面 工作原理 flowchart LR A["DSH 会话与工具事件"] --> B["本地采集与后台任务"] B --> C["SQLite、FTS5 与私有资源文件"] C --> D["Recall、Skill、CodeGraph、Wiki 与 Canvas"] D --> E["Agent 工具与记忆工作台"] - 记忆索引、数据库、代码快照和工具产物均保存在本机。 - L1、Reviewer、Scene、Persona、Wiki 和 Task Canvas 等后台任务使用 DSH 已配置的模型;插件不保存 Provider URL 或 API Key。 - 后台任务首次解析模型后会固定该模型,重试或重启不会静默切换。 - L1 使用本地 BM25 候选检索、现实时间/系统时间和不可变替代链;不需要 Embedding、向量数据库或 RRF。 - 采集、归档、建图和模型任务失败不会改变工具结果,也不会阻塞当前聊天。 更完整的数据流见运行流程,模块关系见架构说明。 快速开始 前置条件 - Node.js 24 - pnpm - DeepSeek Harness dsh-v0.1.0-rc.8 从源码安装 git clone https://github.com/qinghui316/dsh-memory.git cd dsh-memory pnpm install pnpm test pnpm typecheck pnpm build dsh plugin --profile web add . 启动 Web profile: dsh --profile web 首次使用: 1. 在 DeepSeek Harness 中配置 Provider 并导入模型。 2. 从侧边栏打开“记忆”。 3. 为会话选择 full 或 recall 模式。 4. 需要项目代码知识时,在“项目知识”中构建 CodeGraph 和 Wiki。 移除插件: dsh plugin --profile web remove dsh-memory 在仓库目录重新安装: dsh plugin --profile web remove dsh-memory dsh plugin --profile web add . 普通移除不会删除已有记忆,重新安装后可以继续使用原数据。 模式 | 模式 | 召回已有数据 | 自动写入与后台演进 | 适合场景 | | off | 否 | 否 | 当前会话不使用记忆能力 | | recall | 是 | 否 | 只读取已有记忆、Skill、项目知识和任务产物 | | full | 是 | 是 | 持续采集对话与工具轨迹,并生成记忆、Skill、Scene、Persona 和 Canvas | 模式在会话首个 step 后锁定,避免同一条执行链中途改变采集边界。工作台始终可以浏览和手动管理已有数据。 数据与隐私 数据默认存放在 $DSH_HOME/memory/;未设置 DSH_HOME 时位于 ~/.dsh/memory/。其中包括 SQLite 数据库、Skill 资源、CodeGraph/Wiki generation、Artifact、备份、导出和维护记录。 需要注意: - full 会保存模型实际看到的完整工作轨迹以及工具参数、结果、错误和产物,这些内容可能包含敏感信息。 - 项目知识只从当前 workspace 已提交的 Git 内容构建私有快照,不读取 dirty 或 untracked 文件。 - 插件不会把 Provider 凭据保存进自己的数据库,也不提供通用密钥脱敏。 - 普通卸载会保留数据;只有在工作台执行并确认 purge 才会物理删除。 - 这是本地单用户实现,不包含团队 ACL、云端 Memory 服务、远程仓库注册或跨机器同步。 写操作受 loopback 与同源边界保护,Agent 只获得 workspace-scoped 的只读项目知识工具。完整约束见安全边界。 致谢 感谢 TencentDB Agent Memory 的开源实践,为 dsh-memory 的设计提供了启发。 深入文档 - 架构 - 数据模型 - 运行流程 - 安全边界 - 路线图 - 腾讯参考映射 社区 欢迎通过 GitHub Issues 报告问题、讨论需求或提交改进建议。 贡献前请先说明问题和预期行为,便于对齐实现边界。项目采用 MIT License。
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
qinghui316/dsh-memory
让新会话继承上次进度,召回项目背景与偏好
相关插件 · 对话 / 记忆
上下文中枢oceanbase/powercontext
上下文跟着工作走 —— 跨会话、跨智能体、跨人。
★ 1,489已汉化DSH
73.1
智能体记忆库plastic-labs/honcho
Honcho 是记忆基础设施,用来构建有状态的智能体。这些智能体能理解不断变化的人、智能体、群组、项目和想法。
★ 7,918已汉化DSH
71.7
长程智能体控制面loopx-project/loopx
给 Agent 一个目标,让工作持续向前。
★ 6,358已汉化DSH
71.3
智能体上下文数据库volcengine/OpenViking
给 AI 智能体用的上下文数据库
★ 41,453已汉化DSH
71.3
长效机器人团队Fei2411/DS-Bot
Download DS Bot for Windows 10/11 64-bit / 下载 Windows…
★ 5,293已汉化DSH
71.2
可携带记忆层EverMind-AI/EverOS
为什么选择 EverOS
★ 13,797已汉化DSH
71.1
这是你的仓库?认领条目