[Bug] pnpm dsh web 从源码启动时工具调用全线崩溃:tsx/esm 加载 TS 源码与插件加载编译产物导致 TOOL_RUNTIME_SCHEDULER Symbol 分裂 (Cannot read properties of undefined (reading 'prepare')) #7079
World-sudo
started this conversation in
General
Replies: 1 comment 1 reply
|
此项目由deepseekv4.1修复(莫名喜感),经测试可以正常运行,启动命令:node apps\cli\lib\bin.js web (!!此为临时解决方案!!) |
1 reply
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.
Uh oh!
There was an error while loading. Please reload this page.
🔍 问题摘要
从 monorepo 源码仓库使用
pnpm dsh web(内部执行node --import tsx/esm apps/cli/src/bin.ts web)启动 DSH 时,会出现一条完整的故障链:核心诱因是
TOOL_RUNTIME_SCHEDULER使用 module-local 的Symbol()定义:每个物理模块副本在求值时都会创建一个独立 Symbol 实例,描述字符串相同但引用不相等。只要@deepseek-ai/dsh-tools被解析到两个不同文件(例如主进程走src/index.ts、插件走lib/index.js),Agent Loop 用其中一份的 Symbol 去读ctx.tools[...]就必然得到undefined。🔄 复现步骤
git clone https://github.com/deepseek-ai/deepseek-harness.gitcd deepseek-harness && pnpm install && pnpm build@deepseek-ai/dsh-tools出现两份物理副本(任选其一):@deepseek-ai/dsh-tools为dependencies的第三方插件(pnpm 物化出第二份副本);或paths的解析路径(原生 Node 解析 / 指向lib/index.js的插件)pnpm dsh web用 pwsh 执行 echo hello)Cannot read properties of undefined (reading 'prepare')DeepSeek Messages tool calls need immediate results(会话已毒化)🔬 根因分析
核心根因:同一包的两份物理副本 → 两个不同 Symbol
TOOL_RUNTIME_SCHEDULER定义在packages/core/tools/src/index.ts:463:Agent Loop 通过该 Symbol 读取 ToolRuntime(
packages/core/agent-loop/src/tool-calls.ts:170):而
ToolRuntime实例把调度器挂在同一个 Symbol 键上(packages/core/tools/src/index.ts:798):两份副本的分工时:
packages/core/tools/src/index.tspackages/core/tools/lib/index.js即使两个文件源码完全一致,
Symbol()也会各自创建一个实例。主进程用 TS 版 Symbol 读ctx.tools[Symbol]得到undefined,访问.prepare()即崩溃。可复现的验证证据
在同一个 Node 进程中先后加载两份副本(
node --import tsx/esm):Symbol())falseSymbol.for())true并且修复后,用任一副本的 Symbol 去查另一副本注册的调度器都能命中(
undefined→ 正常对象),即崩溃点消除。与已有讨论的区别
社区已有多个关于
prepare崩溃的讨论(见文末引用),多数聚焦 pnpm 物化出第二份物理副本。本报告补充的是一份最小、可离线复现的判定脚本(上面的两行import对比),它不依赖安装第三方插件即可证明 Symbol 分裂是否存在,也可用于回归验证。顺带一提:仓库自身的测试配置已经意识到这个风险——
vitest.config.ts:18-19明确注释paths must win over package exports so built lib/ never loads a second module-singleton copy。本次修复把这个"靠解析配置规避"的约束,在类型层面不改变公开 API 的前提下收敛到符号本身。🛠️ 建议修复方向
方案一:
TOOL_RUNTIME_SCHEDULER改用Symbol.for()(根除 Symbol 分裂)Symbol.for()使用全局 Symbol 注册表,跨模块实例返回同一个 Symbol。即使两份副本同时被加载,两边也能通过同一个键访问 ToolRuntime。关于
as typeof TOOL_RUNTIME_SCHEDULER:Symbol.for()的返回类型是symbol而非unique symbol,因此需要断言。这里断言回typeof TOOL_RUNTIME_SCHEDULER是刻意的——它保留了unique symbol类型,公开 API 的.d.ts输出与修复前逐字节相同(export declare const TOOL_RUNTIME_SCHEDULER: unique symbol与readonly [TOOL_RUNTIME_SCHEDULER]: ToolRuntimeScheduler均不变),因此不会波及消费者或生成的 API 目录。该写法与仓库内既有惯例一致(vendor/cordis/src/utils.ts:60-72的Symbol.for('cordis.effect') as typeof Context.effect、packages/typert/protocol/src/owned-value.ts、packages/subagent/subagent/src/internal.ts)。方案二:统一
pnpm dsh web的模块解析路径让 tsx/esm 的
resolvehook 强制将@deepseek-ai/*解析到同一份实现(src或lib),或改用node apps/cli/lib/bin.js作为默认入口。这治标——vitest.config.ts已在用同样手段规避。方案三:错误路径中闭合挂起的工具调用
在结束轮次的错误路径中,写入
turn/end之前先闭合挂起的工具调用,复用interruptedTurnClosers的合成tool/result生成逻辑。方案四:
listArtifacts对坏文件宽容单个会话文件的 header 读取/校验失败应被逐文件捕获,记录警告并跳过,而不是让整个
list()抛错。🖥️ 环境信息
pnpm dsh web→node --import tsx/esm apps/cli/src/bin.ts webnode apps/cli/lib/bin.js web✅ 本次验证结果
补丁改动为单文件单行(
packages/core/tools/src/index.ts:463)。tsc -b --force packages/core/tools/tsconfig.jsonlib/types/index.d.ts声明unique symbol)vitest run packages/core/agent-loop/tests/tool-calls.spec.tspnpm dsh --profile headless "用 pwsh 执行 echo hello"hello(exit 0)node apps/cli/lib/bin.js --profile headless "用 pwsh 执行 echo hello"hello(exit 0)src与lib两份副本比对 Symbolfalse变为true需要说明的是:GUI(浏览器)内的逐次点击验证本次未执行,上表两条端到端结果是在
headlessprofile 下、经真实模型与真实工具执行取得的,与pnpm dsh web共用同一条工具调用与调度器代码路径。🔗 参考修复分支
已在 fork 中验证
Symbol.for()方案可修复此问题:9b11327538补丁文件
如维护者需要采纳,可 cherry-pick 该 commit 或应用补丁文件。
📎 相关讨论引用
Symbol.for一行修复方案:重复副本下的 Symbol 键不匹配prepare崩溃在工具派发时发生(ctx.tools存在但 Symbol 槽为 undefined)@deepseek-ai/dsh-tools为 dependencies 的插件后所有工具调用崩溃(模块双实例)tool_calls,现有interruptedTurnClosers覆盖不到dsh web启动dsh-messages-sanitizer:自动修复孤立 tool_callsAll reactions