Replies: 1 comment
|
Thanks — the three-arm table is what makes this actionable, and it pins down something the earlier reports of this signature did not: the unit here is a copy on disk, not one realpath evaluated twice. Here are the source anchors for the mechanism you traced from the bundled output, plus one thing about your fix (2) that I think is worth knowing before anyone writes it. The identity, in source. Why the copy appears, precisely. Every profile nodeLinker: hoisted
autoInstallPeers: falsewith the intent stated in the comment above it ( The line that invites it. On your fix (2): the data already exists. What I published, because the second branch of your "Expected behavior" is directly satisfiable. "Either the duplicate cannot arise … or it is detected and reported as what it is: a profile-local copy of a harness package shadowing the installation's." The second branch is a plugin:
What it does, in the terms your report uses:
Mount: # cordis.yml
plugins:
'@argszero/cordis-plugin-profile-duplicate-doctor': {}The honest boundary. This is detection and attribution, not repair — expect (2), not (1). A plugin cannot remove the copy, and should not: by the time anything is loaded the symbol has already been evaluated twice, and rewriting another package's Evidence, if it is useful: 43 behaviour arms driven through a real cordis context, the real If the wording or the severity split should say something different for a shipped template like |
Uh oh!
There was an error while loading. Please reload this page.
Summary
Installing a third-party plugin that declares an in-box package (here
@deepseek-ai/dsh-tools) as a dependency makes pnpm materialize a second copy inside the profile. Module identity then splits for a module-localSymbolthat keys a runtime surface of the tool registry, soctx.tools[TOOL_RUNTIME_SCHEDULER]reads backundefinedand every tool call in that profile fails —bash,read, and the plugin's own tool alike. The surfaced error namesprepare, not the duplicated package, so a plugin author gets no signal that theirpackage.jsoncaused a harness-wide breakage.This is the highest-value finding of the three: it punishes exactly the behaviour the extension docs encourage (
defineToolfrom@deepseek-ai/dsh-tools), and the crash is unattributable.Reproduction
Isolation on the same profile, same task, only the dependency varying:
dsh-toolsdependencyCannot read properties of undefined (reading 'prepare')pwdruns, exit 0bashtool and the plugin's own tool run, exit 0Current behavior
The model's tool call is reached and logged, then the turn dies before dispatch:
{"type":"tool/call","seq":20,"data":{"name":"set_session_title","arguments":"{\"title\":\"端到端改名验证\"}"}} {"type":"turn/end","seq":23,"data":{"reason":{"kind":"error","error":{"message":"Cannot read properties of undefined (reading 'prepare')","code":"UNKNOWN"}}}}A first-party-only task in the same state fails identically:
Expected behavior
Either the duplicate cannot arise (in-box packages resolve from the installation only), or it is detected and reported as what it is: a profile-local copy of a harness package shadowing the installation's. A bare
TypeErrorfrom.prepareis the one outcome that should not be reachable.Environment
@deepseek-ai/dsh-desktop-runtime0.2.0-rc.2headless(shipped template), also reproducible in any profile that installs such a plugin~/.dshRoot cause
@deepseek-ai/dsh-agent-loopreads a symbol-keyed surface off the registry:and that symbol is module-local, not global:
Identity of a plain
Symbol(...)is per module instance, so the reader and the registry can bind different copies ofdsh-tools. Becausedsh plugin --profile <name> add <pkg>runs pnpm in the profile, any plugin that declares@deepseek-ai/dsh-tools(the documented home ofdefineTool) installs a profile-local copy that shadows the installation's.The failure mode is not specific to
dsh-tools: every in-box module pair that defines and consumes a module-localSymbol, class brand, orinstanceoftarget has it once a profile carries a duplicate.Suggested fix
Symbol.for("…")for cross-module runtime seams so identity survives two copies — smallest change, closes the whole class of bug.@deepseek-ai/*specifiers from the installation only, and warn (or fail loud) when a profile'snode_modulesshadows one. Silent shadowing is what makes this expensive to diagnose.undefined.prepareescape.Workaround for plugin authors
Declare no dependencies and hand-write the tool definition —
ctx.tools.register()only requiresoutput: { schema, render }plus a schema inside the supported subset (type/properties/required/additionalProperties/items/enum/const+ thedescriptionannotation), which is whatdefineToolcompiles a declaration into anyway:The trade-off is losing
defineTool's argument-validation wrapper, soexecutevalidates by hand. Worth documenting as a hazard until (1) or (2) lands.All reactions