Replies: 3 comments
|
Verified against the installed One extra consideration for the fix: any third-party plugin that imports Great find and thorough reproduction — this one would have been painful to debug. |
|
这条的根因分析我完全同意,而且能给你一份独立佐证:我们在自己的发版回归里为同一族失败专门写了一条断言,因为被它坑过。 我们踩的那一脚我们是 DSH 的外部插件(Pi 生态兼容层),核心包对我们是 peer。规矩本来很清楚:DSH core 由 CLI 的依赖树提供,profile 里只该有 surface 和插件。但 profile 有时会解析出自己的一份 core 拷贝——一旦如此,树里就同时存在两代/两份核心包, 所以我们的真机 E2E 里现在硬钉着这几句: // profile 不许有自己的 core 拷贝——它会遮住 CLI 那份
const profileCore = sh(`find ${profileRoot}/node_modules -maxdepth 4 -path '*/@deepseek-ai/dsh-agent' -print`)
if (profileCore !== '') throw new Error(`the profile resolved its own core copy at ${profileCore} — it would shadow the CLI's core`)
// CLI 树里的 dsh-agent 必须和 CLI 同版本线
if (agentVersion !== expectedCoreVersion) throw new Error(`resolved dsh-agent@${agentVersion}, expected ${expectedCoreVersion}`)也就是说:"同一个包被求值两次"在 DSH 的依赖布局下不是理论风险,是一个我们必须每次发版都主动检查的现实。你这条把它从"版本混装"推进到了"同一版本、peer 提升差异也能触发",这比我们那条更狠——我们的断言只挡得住版本不同的那种。 一个可能更彻底的修法如果你的补丁还没定型,考虑把 export const TOOL_RUNTIME_SCHEDULER: unique symbol = Symbol('@deepseek-ai/dsh-tools.scheduler')换成 export const TOOL_RUNTIME_SCHEDULER = Symbol.for('@deepseek-ai/dsh-tools.scheduler')
代价要说清: 兜底建议不管走哪条,我建议再加一句 fail loud: 边界我们不修 DSH 自家组件,上面全是佐证和建议,不是我们能替你落地的东西。 利益相关:我维护 pi2dsh。这条不推销——重复实例是依赖布局层的问题,多装一个插件只会让树更复杂,帮不上你。 |
|
更新:这条已经被完整解决了,而且我上面给的建议不完整——需要更正一下。 #1697 报的是同一个符号、同一个失败( 那边的结论走得比我远,有两点值得搬回来: 一、我漏了
|
Uh oh!
There was an error while loading. Please reload this page.
Component:
@deepseek-ai/dsh-tools+@deepseek-ai/dsh-agent-loop(tested at0.1.0-rc.8, the synceddsh-*ecosystem version as of 2026-08-21, with@deepseek-ai/dsh@0.1.0-rc.7)I know external PRs aren't accepted right now (per CONTRIBUTING.md), so posting the finding + a working fix here instead, per that same doc's guidance to report bugs in Discussions.
Summary
Every subagent tool call (
ctx.subagents.start('spawn' | 'fork', ...), any tool, any model) crashes the turn with:Root cause
packages/core/tools/src/index.tsdeclares:as a module-scoped
Symbol().dsh-agent-loop's tool dispatch readsctx.tools[TOOL_RUNTIME_SCHEDULER](runGroup/startCall) to get the scheduler off the liveToolRuntimeinstance. That index read returnsundefinedwhenever theToolRuntimeinstance and thedsh-agent-loopconsumer resolvedTOOL_RUNTIME_SCHEDULERfrom two different evaluations of thatSymbol(...)call — which happens in at least two real ways:packages/bundle/base/package.jsondepends ondsh-toolsdirectly;packages/core/agent-loop/package.jsononly has it as apeerDependency. Differing transitive peer sets across consumers can make pnpm install more than one physical copy ofdsh-tools.dsh-toolshas multipleexportsentry points (.,./invariant,./types,./presentation). Confirmed empirically: after a cleanpnpm run typecheckbuild, both the.entry'slib/index.jsand the internallib/types/index.jschunk each contain their own independently-evaluatedSymbol(...)call for this constant — tsdown/rolldown inlined the shared declaration into two separate output chunks instead of sharing one module instance.dshand an active profile are two separate dependency trees. The CLI launcher (wherever@deepseek-ai/dshitself is installed) and$DSH_HOME/profiles/<name>/each resolvedsh-toolsindependently — a fix/patch applied to only one of the two won't fully resolve the symptom, sincedsh-agent-loopcan load from one tree while the liveToolRuntime(ctx.tools) is constructed viadsh-basein the other.Confirmed via a targeted diagnostic comparing
Object.getOwnPropertySymbols(ctx.tools)against the importedTOOL_RUNTIME_SCHEDULER: same.toString()description,Symbol.keyFor()returningundefinedon the live instance's own symbol (proving it's a plainSymbol(), notSymbol.for()), andidentityEqual === false.Fix
Symbol.for()resolves to the same process-wide value for a given key regardless of how many separate module instances (pnpm copies or bundler chunks) evaluate it, so all three duplication paths above stop mattering.Branch with the fix (commit + comment explaining why): https://github.com/rohit267/deepseek-harness/commits/fix/tool-runtime-scheduler-symbol-identity
Verification
pnpm exec tsc -b tsconfig.host.json— zero errorspnpm run typecheck(full:tsc -b tsconfig.host.json && tsdown --env.DSH_BUILD_FACE host && tsc -b tsconfig.client.json) — exit 0pnpm exec vitest run packages/core/tools packages/core/agent-loop— 30 files, 723 tests, all passinglib/index.jsandlib/types/index.jsboth readSymbol.for(...)after the build)ctx.tools's own scheduler symbol becomes identity-equal to the imported constant) after patching all resolution points involved (the package itself, both its bundled entry chunks, and both dependency trees a CLI + profile setup involves)Reproduction
dshprofile with@deepseek-ai/dsh-base(or any composition withdsh-agent-loop+ a subagent provider + real tool plugins).ctx.subagents.start('spawn' | 'fork', {...})with a persona that causes the model to call any real tool.{ kind: 'error', error: { message: "Cannot read properties of undefined (reading 'prepare')", code: 'UNKNOWN' } }.Doesn't depend on
outputSchema,toolFilter, subagent provider (spawn/forkboth crash identically), or model — reproduced with two different models via a custom OpenAI-compatible Ollama endpoint.Happy to answer questions or send the full local repro if useful — thanks for the project, this took a while to fully pin down so hopefully saves someone else the trip!
All reactions