依据:RESEARCH_DEEPSEEK_HARNESS.md(2026-08-13 调研,2026-08-14 引入本仓) 日期:2026-08-14 · 基线:v0.23.0(
9cfe2a3)· 关联:ROADMAP_v1.0.md、PLAN_FOUNDATIONS_v1.0.md、PLAN_DISPATCH_v1.0.md、PLAN_CLOUD_v1.0.md 文中行号以当前树为准,后续修改会漂移。
对 LISA 而言,dsh 不是引擎候选,是一面镜子 + 一个新的被指挥对象。
- 生态兼容层无事可做。 dsh 花大力气建的兼容面(多提供方 +
apiKeyEnv引用、Claude Codehooks.json、SKILL.md、mcp__<server>__<tool>命名、把 Claude Code / Codex CLI 当子代理),LISA 已经全部有了,部分还更全。这一层照抄没有增量。 - 真差距只有三处,且每一处恰好卡在 1.0 的一条主线上:
- H1 能力 seam — 工具直接
import fs/spawn,没有可替换的执行世界 → 卡住 Dispatch 的远程执行与 Cloud 的多租户隔离; - H2 权限与沙箱 — 默认关、只包
bash、非 macOS 静默降级 → 卡住 Dispatch「闭合本地命令回路」的安全地板与 Sense 的隐私地板; - H3 会话日志的真源地位 — 系统提示词一个字节都不进日志 → 卡住 Reve 的 drift/coherence 度量,也卡住论文的测量工具。
- H1 能力 seam — 工具直接
- 唯一值得写代码"接入"的:把
dsh加成src/integrations/的第 11 个 observer / dispatch 目标。既有注册表天然支持,成本一天,收益直接落在 Dispatch 支柱上。 - 明确不做:不 fork、不把 dsh 当执行引擎、不引入 Cordis、不做 Code Mode(暂缓)。
一句话:dsh 免费化的是 harness 管道,而 LISA 的护城河从来不是管道,是 soul / desire / mood / KB / Reve 这一层持久身份——dsh 这一层是零。 这次调研对 LISA 的价值是"体检报告",不是"选型报告"。
| dsh 概念 | LISA 现状 | 差距 | 结论 |
|---|---|---|---|
能力 seam(ctx.fs / ctx.shell / ctx.subprocess / ctx.sandbox) |
ToolContext = { cwd, signal, log, onObjection? }(types.ts:24);工具各自 import fs from "node:fs" / spawn |
大 | H1 抄 |
权限:sandbox/mode ×3 + approval/policy ×2 + preset,fail-closed |
LISA_SANDBOX 布尔且默认关(sandbox.ts:50);只有 bash 走沙箱(bash.ts:34);非 macOS 静默降级(sandbox.ts:46);--approval 默认 auto(cli-args.ts:81) |
大 | H2 抄 |
| 会话日志唯一真源 / 模型可见 ⟺ 已记录 | JSONL 只写 header / message / reflection(sessions/store.ts);tool_use/tool_result 在 message 的 content block 里已在;但系统提示词不在,且中途会热重载(agent.ts:225) |
中 | H3 抄 |
| Agent Preset(用户可复制修改的模式) | 编译期 subset 函数:readOnlySubset / autonomousSubset / remoteSafeSubset / cloudSafeSubset / desireReviewSubset(tools/registry.ts:128–306) |
中 | P2 缓(见 §5) |
| 多提供方 + 凭据只存引用 | OPENAI_COMPAT_PRESETS 20+ 家含 DeepSeek,apiKeyEnv 引用制(providers/registry.ts:38+) |
无(LISA 更全) | 不动 |
Claude Code hooks.json |
已支持同形状 6 事件(plugins/types.ts:27、hooks/runner.ts) | 无 | 不动 |
SKILL.md |
已支持;但只有单层 ~/.lisa/skills(skills/manager.ts),无项目级、无热监听 |
小 | P1 小补 |
MCP mcp__<server>__<tool> |
已同形状(mcp/client.ts:76) | 无 | 不动 |
| 把别家 CLI 当子代理 | src/agents/pty.ts + managed.ts + dispatch_agent 已可拉起/接管 claude / codex |
无(等价能力已有) | 不动 |
AGENTS.md / CLAUDE.md 指令链 |
无(全仓无加载逻辑) | 小 | P1 小补 |
| goal 三件套(跨轮次目标 + 人类根权限) | soul/desires + desire_progress/revise/close + autonomy/runs |
无(LISA 更强:desire 是人格化长期目标,不只是任务) | 不动 |
session_search / session_trace 模型可见 |
无会话检索工具;有 memory_search / kb_search |
小 | P2 缓(依赖 H3) |
| 压缩:pruner + spill store | --compact 直接开 Anthropic 原生 context management(providers/anthropic.ts:85) |
中 | P2 缓 |
Code Mode(run_code) |
无 | — | 不做(见 §4) |
| ACP / SDK 程序化入口 | web server + contracts/ API contract |
小 | 不动 |
| 桌面 / 移动 / 语音 / 常驻感知 | Mac Island、iOS Pocket、voice、Sense observers | 无(dsh 完全没有) | 不动 |
读法:右侧只有三行写着"大/中 + 抄"。这就是本计划的全部实质。
LISA 的工具直接 import fs from "node:fs/promises"、直接 spawn。后果有三个,都已经在路线图上咬人:
- Dispatch 想把执行搬到远程/容器里,就得给每个工具写第二份实现;
- Cloud 的多租户隔离目前靠
homeScopeAsyncLocalStorage 兜住lisaHome()(paths.ts:20),但write/edit/bash的ctx.cwd不受它管——所以云端只能用cloudSafeSubset白名单硬砍掉这些工具(tools/registry.ts:283)。白名单是补丁,seam 才是解; - 测试只能靠真实文件系统,没有内存实现。
dsh 的答案:ctx.fs / ctx.shell / ctx.subprocess 是接口,local / sandbox / e2b 是可换的提供方,工具只消费接口。换提供方 = 换整个执行世界,一行工具代码都不改。
// src/types.ts — 扩 ToolContext,不改现有字段
export interface ToolContext {
cwd: string;
signal: AbortSignal;
log: (msg: string) => void;
onObjection?: (o: {...}) => void;
/** 执行世界。缺省 = local 提供方(现行为,逐字等价)。 */
caps?: Capabilities;
}
export interface Capabilities {
fs: FsCapability; // readFile/writeFile/stat/readdir/rm,带根目录约束
shell: ShellCapability; // run(cmd) → {stdout, stderr, code}
}- 第一步只做
fs与shell两个 seam,提供方只做local; - 迁移顺序:
read/write/edit/apply_patch/ls/grep/bash七个工具改成ctx.caps.fs/ctx.caps.shell;其余工具(soul / memory / kb / github / …)不动——它们操作的是 LISA 自己的 home,本来就该走lisaHome(); caps可选、缺省注入 local 提供方,所以这一步对外行为零变化,是纯重构。
它同时解锁 H2(沙箱提供方 = 换一个 fs/shell 实现,而不是在每个工具里加 if)、Cloud 的真隔离(per-uid 根目录的 fs 提供方,白名单可以退回成纵深防御而不是唯一防线)、以及远程执行(Dispatch 的容器化目标)。一次重构,三条主线受益——这是整份计划里性价比最高的一项。
-
ToolContext.caps落地,local 提供方通过全部现有测试且无行为变化;
证据:src/types.ts:43caps?:为可选字段(不传 = local 磁盘,既有构造点无需改动),提供方在src/capabilities/{local,memory}.ts。 - 七个 fs/shell 工具不再直接
import node:fs/node:child_process(留一条 lint/grep 断言防回归);
证据:七个工具源文件里node:fs/node:child_process的 import 计数为 0;防回归断言是src/capabilities/seam.test.ts:35(“migrated tools import neither node:fs nor node:child_process”)与:56(每个工具都过capsOf)。 - 新增一个内存 fs 提供方,至少 3 个工具测试用它跑,不落盘。
证据:src/capabilities/memory.ts;seam.test.ts:80–134用它跑了 read / write / edit / ls / apply_patch 五个工具(:87断言“write never touches the disk”),另有 bash 的失败路径:176。
write/edit/apply_patch完全不受沙箱约束。wrapForSandbox唯一调用点是bash.ts:34;三个写工具都是path.resolve(ctx.cwd, input.path)(write.ts:24、edit.ts:33、apply_patch.ts:58),绝对路径与../一律照收。即使LISA_SANDBOX=1,bash被关进 cwd 而write能写满盘——正是 dsh 明确要避免的「bash 与 fs 限制到不同的根目录」。(对照:KB 写工具是路径 jail 的,kb/tool.ts:5——说明这个模式在本仓已被证明可行。)- 非 macOS 静默降级成不受限
/bin/bash -lc(sandbox.ts:46)。dsh 的规矩是SANDBOX_UNAVAILABLE直接拒绝执行。"以为开了沙箱其实没开"比"没有沙箱"更危险。 - 默认全关:
LISA_SANDBOX不设 = 无沙箱,--approval默认auto= 无审批。本地 attended REPL 这样没问题(和 Claude Code 同一姿态),但 1.0 要闭合的是本地 agent 命令回路——dispatch 出去的是无人值守的运行,那时"默认全关"就不再是合理默认。
-
三档
sandboxMode(对齐 dsh 词汇,别自造):read-only/workspace-write/danger-full-access; -
在 H1 的 seam 上实现:
fs-sandbox与shell-sandbox两个提供方共享同一份根目录策略,天然保证两者边界一致; -
fail-closed:
workspace-write在无可用强制机制的平台(Linux 无 bwrap/Landlock、Windows)报错拒绝,不降级。想在这些平台跑,用户须显式选danger-full-access; -
会话级固定:模式写进
SessionHeader,会话创建时定死,中途改配置不影响进行中的会话; -
默认值按 surface 分层(LISA 已有 surface 概念,只是没连到沙箱):
surface sandboxMode 默认 approval 默认 本地 attended REPL / Island danger-full-accessauto(现状不变)dispatch / 无人值守运行 workspace-writeautoidle / heartbeat(Reve) read-onlyauto(已有autonomousSubset兜底)渠道(remote-origin) read-onlyauto(已有remoteSafeSubset兜底)cloud 不适用(无 fs/shell 工具) — 注意最后三行已经有工具级边界了——加沙箱是把"能不能拿到工具"的单层防御,变成"拿到了也出不去"的纵深防御。
-
write/edit/apply_patch在workspace-write下拒绝写出根目录(含../与符号链接逃逸),有测试;
证据:src/capabilities/sandboxed.ts:53–72比较 realpath;测试sandboxed.test.ts:66(../逃逸)、:75(绝对路径)、:87(符号链接)、:99(edit 与 apply_patch 同样受限)。 - Linux 无 bwrap/Landlock 时
workspace-write抛SANDBOX_UNAVAILABLE,不静默降级,有测试;
证据:src/sandbox/mode.ts:38code = "SANDBOX_UNAVAILABLE";测试mode.test.ts:108(“a bounded mode on an unsupported platform refuses instead of degrading”)与:170。 -
SessionHeader记录sandboxMode,version升到 2 且旧会话可读;
证据:src/sessions/store.ts:51/65/72(字段 +version: 2);v1 兼容读在src/sessions/replay.ts:40,由replay.test.ts的V1_HEADER覆盖。 -
docs/FOOTPRINT.md/ README 的安全叙事同步更新(1.0 判定标准第 2 条:叙事 = 代码)。
LISA 的会话 JSONL 已经记录了 tool_use / tool_result(它们是 StoredMessage 的 content block),这点比预想的好。真正缺的是系统提示词:
- 系统提示词由 soul + memory + skills + mood + KB 摘要动态构建(prompt.ts:66);
- 而且会话中途会热重载——
soul_patch/memory写入后,下一轮就换了一份系统提示词(agent.ts:225–242),只发一个system_prompt_rebuilt事件,不落盘; - 结果:拿着一条历史会话,无法重建当时模型看到的东西。dsh 的不变量「模型可见 ⟺ 已记录」在 LISA 这里对消息成立、对提示词不成立。
这不是洁癖。ROADMAP §1.0 判定标准第 4 条要求「可复现的自主性度量:Reve 能产出 drift / coherence 指标」,而 drift 的定义就是"她的自我描述随时间怎么变"——如果日志里没有当时的自我描述,这个指标根本无法离线复算。同一条也直接卡住论文:长时程连贯性的任何测量,都要求知道每个时点模型看到的 persona 是什么。
Step 1(低成本,先做):新增一类日志条目 {type:"prompt", ts, fingerprint, text, reason},reason 区分 initial(本次运行的开局提示词)与 rebuilt(中途热重载)。
getPromptFingerprint 已存在(prompt.ts:237),热重载点也已存在(agent.ts:228 的 if (next.fingerprint !== currentFingerprint)),改动面很小。体积可控:一份系统提示词几 KB,一次会话通常个位数次重载。fingerprint 用正文的内容哈希、连续相同不重复写,所以"第 N 条时生效的提示词"= 最近的前一条 prompt 条目,从不自我修改的长对话只花一条。
Step 2(可选,之后再说):把 SessionStore 从"消息日志"推向"事件日志",加一条运行时断言——任何进入 provider.runTurn 的输入都必须能从日志重建。这是 dsh 的完整形态,成本高,不是 1.0 必需。
明确不做:不抄 assistant/chunk 级别的流式保真(LISA 的 UI 不需要逐 chunk 回放),不引入 SQLite 持久化(JSONL 够用,projection cache 等有性能问题再说)。
systemPrompt 是逐字精确的。但 messages 只是会话正典全量,不等于每轮真正发出去的消息窗口:web 表层把 modelContext.history(历史的有界后缀)传给 runAgent,而会话文件保留完整历史(web/server.ts:4060 的注释自己写明了这点);CLI 与渠道表层不做窗口,两者一致。
也就是说 H3 的不变量目前对提示词成立、对消息窗口不成立。做 token 级精确重放的调用方需自行处理;做"她被告知自己是谁"分析的不受影响——而后者正是本项的目的。补上窗口边界记录属于 Step 2。
- 会话 JSONL 含
prompt条目,SessionHeader.version同步升级(v1 → v2,旧会话仍可读); - 写一个离线脚本:读一条历史会话 → 重建每一轮的
(system, messages)输入(src/sessions/replay.ts+scripts/replay-session.ts,随 #357 落地); - 该脚本成为 Reve drift 指标与论文实验的取数入口(这是本项的真实交付物,不是日志本身)——还没接上,是下一步。
LISA 的 src/integrations/ 已有 10 个内建 observer(claude-code / codex / github-pr / opencode / aider / git / shell / takoapi / managed / pty,见 integrations/registry.ts:52),AgentKind 是开放字符串联合(integrations/types.ts:14),加一种不用改类型。
dsh 是目前唯一一个既有本地会话文件、又有 headless CLI、还带 star 势能的新 harness。把它接成 LISA 能观察和指挥的对象,直接服务 Dispatch 支柱「用户跟 LISA 对话即可指挥本机全部其它 agent」。
两个层级,按成本:
- L1 OBSERVE:写
src/integrations/dsh/observer.ts,读 dsh 的会话日志映射到AgentSession。 - L3 COMMAND(L1 之后):
dispatch_agent增加dsh后端,用dsh --profile headless "job"拉起一次性运行。这是 dsh 最稳定的接口(无服务器、跑完退出),比接 ACP/SDK 风险低得多。
不做:不接 ACP、不接 dsh SDK、不做 resume-adopt(同 codex 的理由,见 PTY_AGENTS.md)。
本节原估 L1「约 1 天」,并注明「做之前先确认 dsh 的磁盘格式」。核查做了(读 deepseek-ai/deepseek-harness 的 packages/session/session-persistence-jsonl/src/{format,index}.ts 与 docs/subsystems/persistence.md),结论推翻了估算:
| 核查项 | 实际 | 对适配器的影响 |
|---|---|---|
| 物理编码 | 默认 Zstandard(session.jsonl.zstd),且 header 单独占一个可独立解码的首帧;dsh 自带 zstd-*-decoder.ts |
不是"读一个 JSONL"。要么实现兼容其分帧约定的 zstd 解码,要么只支持用户显式配了 compression: 'none' 的部署 |
| 日志根目录 | root 是必填插件配置、无默认值(他们明确拒绝默认 process.cwd()) |
没有 ~/.claude/projects 那样的公知路径可监听。适配器得解析当前 profile 的 cordis.patch.yml 才知道去哪找 |
| 事件行 | packChunks 默认 true,delta 会被打包成 text-chunks/reasoning-chunks/tool-call-chunks 行,需 decodeStorageRecord |
第二层解码 |
| 目录布局 | <root>/--<cwd 转义>--/<会话 id 转义>/session.jsonl[.zstd],转义规则自定义(~XXXX) |
可实现,但要照抄两套转义函数 |
| 版本 | SESSION_FORMAT_VERSION,遇到不认识的版本主动拒绝,且 README 不给兼容承诺 |
每次 dsh 升版都可能要跟 |
决定:L1 暂缓,不写投机适配器。 在本机没有装 dsh、无法对真实产物验证的情况下照着源码猜一个解析器,正是 OBSERVER_FIDELITY.md 记录过的那类问题——单测能证明解析逻辑对得上 fixture,证明不了 fixture 对得上真实工具写出来的东西。
重新排期:
- 先做 L3(
dispatch_agent的 dsh 后端)。它依赖的是 README 明文承诺的 CLI 契约(dsh --profile headless "job"→ 跑完打印最终答案退出),面比磁盘格式窄得多、也稳定得多,而且不需要先有 L1。原文把 L1 排在 L3 前面是错的。 - L1 的前置条件:本机装一个真实 dsh、跑出几个会话、按
scripts/verify-observers.ts的路子对真实产物核对字段。没有这一步就不要开工。
- (L3)
dispatch_agent(backend:"dsh")能跑通一个真实任务并回收输出;dsh 未安装时给出清晰错误而非崩溃; - (L1,前置满足后)
lisa能在 hub 里列出正在跑的 dsh 会话,字段深度对齐 Tier-2 基线; - (L1)dsh 未安装 / 格式变更时适配器降级为"不可见",不报错、不崩 hub。
| 项 | 理由 |
|---|---|
| fork dsh 主干 | Developer Preview + 219 包 + 每文件 100% 覆盖率门禁;LISA 是单人维护 |
| 把 dsh 当 LISA 的执行引擎 | LISA 自己就是 harness,不是需要引擎的产品壳。接进来等于两套工具层 + soul/memory 状态被劈成两半,是净负债 |
| 引入 Cordis | 元框架的收益在"219 个包要解耦"时才兑现;LISA 32 个 src/ 模块、单人维护,收益 < 认知成本。只抄 seam 这一个概念,不抄框架 |
Code Mode(run_code) |
省 token 是真的,但它要求工具管线可重入 + worker vm 沙箱,前置是 H1+H2 全做完。H1/H2 落地后再评估,不进 1.0 |
| 抄 Agent Preset 的完整形态 | LISA 的 subset 函数已经覆盖了核心用例,且安全边界写在代码里比写在用户可编辑的 yml 里更难被绕过。用户可编辑的 preset 是 2.0 话题 |
| 追 dsh 的 rc 版本 | 观察 1–2 个月的破坏性变更频率,再决定 I1 的适配器要不要加固 |
状态列更新于 2026-08-14。
| 优先级 | 项 | 规模 | 服务的支柱 | 状态 |
|---|---|---|---|---|
| P0 | H3 Step 1 提示词入日志 + 离线重建脚本 | 小 | Reve / 论文 | ✅ #357 |
| P0 | H1 能力 seam(fs + shell,local 提供方) | 中(纯重构,行为零变化) | Dispatch / Cloud / 测试 | ✅ #358 |
| P0 | H2 沙箱三档 + fail-closed + 会话级固定 | 中(依赖 H1) | Dispatch / Sense / Foundations | ✅ #359(栈在 #358 上) |
| P1 | AGENTS.md / CLAUDE.md 指令链加载 |
小 | 生态兼容 | ✅ #360 |
| P1 | Skills 加项目级目录 | 小 | 生态兼容 | ✅ #360 |
| — | — | ❌ 不做:skills 本来就经提示词指纹每轮重读,加 chokidar 依赖是纯冗余 | ||
| P1 | H3 的取数入口接进 Reve drift 指标 / 论文实验 | 小 | 论文 | 待做(H3 的真实交付物) |
| P1 | I1 L3 dsh dispatch 后端 | 小 | Dispatch | 待做(已提到 L1 前面,见 §5 的格式核查) |
| P2 | 按 surface 收紧无人值守沙箱默认值(§3 的表) | 小 | Foundations | 待做(会改行为,单独一步) |
| P2 | H3 Step 2:记录消息窗口边界 | 中 | 论文精确性 | 待做(见 §4 的边界说明) |
| P2 | 工具结果 pruner + spill store | 中 | 成本 | 待做 |
| P2 | session_search 模型可见工具 |
小(依赖 H3) | 记忆 | 待做 |
| 暂缓 | I1 L1 dsh observer | 大(原估"小",核查后推翻) | Dispatch | ⛔ 前置未满足,见 §5 |
| 缓 | Code Mode / 用户可编辑 preset | 大 | — | — |
建议起手:H3 Step 1。它最小、最独立(不依赖 H1/H2)、且直接产出论文要用的测量工具——先拿到"能离线重建每一轮模型输入"这个能力,后面的 drift/coherence 实验才有地基。然后做 H1,H2 自然长在 H1 上。
(回看:这个顺序是对的。H3 独立落地,H1 是纯重构、行为零变化,H2 只是在 H1 的 seam 上加一个提供方——如果先做 H2,那三个洞就得在七个工具里各补一遍。)
| 风险 | 说明 | 处置 |
|---|---|---|
| H1 重构面广 | 七个高频工具 + 全部相关测试 | 分两个 PR:先加 caps 与 local 提供方(不改工具),再逐个迁移工具;每步跑全量测试 |
| H2 收紧默认值会破坏现有用户流程 | dispatch / idle 收到 read-only 后可能失败 |
本地 attended 姿态保持不变;只对无人值守 surface 收紧,且先出一版只 warn 不 enforce |
| H3 日志体积 | 每次热重载写全文系统提示词 | 只在 fingerprint 变化时写;观察实际体积,超标再改为 fingerprint 引用 + 单独的 prompt 内容池 |
| dsh 磁盘格式变更 | SESSION_FORMAT_VERSION = 0,无兼容承诺 |
I1 适配器对未知字段宽容、解析失败降级为"不可见";不把 hub 的稳定性押在它上面 |
| 被"抄 dsh"带偏节奏 | ROADMAP 的核心诊断是"每版加一个新大件而不打深" | 本计划的 P0 三项全部是把已声称的能力做实(沙箱、隔离、可复现度量),没有一项是新大件。I1 是唯一新增,且落在既有注册表里 |
用户的论文方向是长时程连贯性(主线)+ soul 稳定性机制(消融),目标 COLM/ICLR 2027。本计划里与之直接相关的只有一项,但很关键:
H3 = 论文的测量工具。 没有"每一轮模型看到的 persona 是什么"的完整记录,drift 与 coherence 都只能在线测、无法离线复算、无法对同一批会话跑多种指标、无法做消融。H3 Step 1 是几百行的改动,却是把 LISA 的纵向部署数据从"日志"变成"数据集"的那一步。
其余各项(H1/H2/I1)是工程债与产品能力,与论文无关——不要为了论文去做它们,也不要用论文当它们的理由。