Replies: 1 comment
|
补充一个同机理但触发面更广的维度:进程重启 / 会话 resume。
详细机理与上游侧的设计选项(含「观察态随 resume/fork seed 重放」的正解形态)见新开的 #3853。 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
摘要
@deepseek-ai/dsh-fs-observation-policy的读取观察状态("哪些文件已被本会话读过")是按 session 对象弱引用的内存态(WeakMap<session, Map<targetKey, observation>>),只通过 read 工具运行时的fs/observed事件写入。分叉会话(fork)在dsh-session里会新建一个 session 对象,只复制事件日志(对话历史),不继承任何观察状态。于是 fork 子会话的观察表从空开始:模型看到的历史里明明有"读过该文件"的结果,实际 edit 却每次都抛FS_NOT_OBSERVED("edit requires reading ... first"),形成"历史说读过、策略说没读过"的矛盾。这与 #275 报告的「分叉会话下每次调用 edit 工具都报错」完全吻合。根因(源码定位,rc.6)
dsh-fs-observation-policy/lib/index.js:ObservedStateGate.observed = new WeakMap()(L22),按owner = actor.agent.session(L29-31)分会话记录;editIntent()(L64-70):!owner || prior === void 0→ 抛FsError("edit requires reading ... first", "FS_NOT_OBSERVED");fs/observed事件(read 工具执行时ctx.emit,见下)会写入状态。dsh-tool-fs/lib/index.jsL432-435:read 工具成功读取后ctx.emit("fs/observed", target, { kind: "present", version }, exec),actor 是当前会话的 exec 上下文——所以 fork 子会话里新读的文件会正常记账,但fork 点之前(父会话里)读过的文件不会。dsh-session/lib/index.jsfork()(L1840+):_forkSeed复制事件日志生成子会话,没有任何观察状态的搬运/重建逻辑(观察状态在 fs-observation-policy 插件内部,session 层根本不可见)。editIntent查子会话观察表(空)→FS_NOT_OBSERVED。策略的错误文案又指示模型"先读再改",模型在子会话重读后 edit 能通过——但历史与策略的矛盾已经造成一轮失败/重试,且模型并不总按提示重读(它认为读过了),于是表现为"每次 edit 都报错"。复现步骤(逻辑复现 + 最小验证)
edit requires reading "<path>" first(FS_NOT_OBSERVED)——尽管对话历史中该文件已被读过。建议修复
方案 A(推荐)· fork 时重建观察状态:
fs-observation-policy监听会话 fork(或dsh-session在 fork 时发出携带种子事件的通知),从被复制的事件日志中重放 read 工具调用,把对应文件的观察记录(present/absent + version)写入子会话的观察表。version 可安全取事件里记录的版本;若担心版本过期,edit 的 CAS 校验会兜底拒绝为 stale,语义上等价于"子会话继承了父会话的读取事实"——与"历史里读过了"一致。方案 B · 策略放宽:fork 子会话对"历史中出现过 read 结果的文件"视为已观察(由会话层在 fork 时打标)。改动面大、语义更松散,不推荐。
方案 C(兜底性提示改进):若短期不修,至少让 FS_NOT_OBSERVED 的错误文案区分"从未读过"与"在父会话读过(fork 继承缺口)",并明确指示重读一次即可。低成本、可立即缓解。
影响
环境
验证材料
First analysis of discussion #275. Happy to open a PR with fix option A.
All reactions