You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[web] cold resume fails: "agent-presets: refusing to compose an unscoped context" — dsh-scope dual-instance kScope Symbol split across the web profile's two node_modules planes
#927
dsh 0.1.0-rc.6, launched via npx @deepseek-ai/dsh web
Windows, Node.js, pnpm-installed profile under $DSH_HOME/profiles/web (nodeLinker: hoisted)
Symptom
Resuming an existing session in the web UI throws:
resume failed for session "...": Error: agent-presets: refusing to compose an unscoped context; the scope key is what joins an agent to its preset
The error comes from dsh-agent-presets in mount() / composeFrom() when scopeOf(agentCtx) returns undefined.
Key observation: live agents work fine (their ReactLoopAgent stays in memory). Only a cold resume — an agent that must be reconstructed through ctx.agents.resume -> composeAgent() -> presets.mount(agentCtx) — fails. The first resume after a restart reproduces it every time.
Root cause: dsh-scope dual instance -> kScope Symbol split
@deepseek-ai/dsh-scope tags every agent context with kScope = Symbol("dsh.scope"), a module-level constant in dsh-scope/lib/index.js. dsh-agent-loop writes the tag via createScope(loopCtx, this) when constructing ReactLoopAgent; dsh-agent-presets reads it back via scopeOf(agentCtx).
The web profile is its own pnpm workspace ($DSH_HOME/profiles/web). Its package.json depends on @deepseek-ai/dsh-agent-presets and @deepseek-ai/dsh-scope, so those get their own copy under profiles/web/node_modules — while @deepseek-ai/dsh-agent-loop exists only in the root plane (profiles/node_modules). That yields two independent node_modules planes:
package
root plane (profiles/node_modules)
web plane (profiles/web/node_modules)
dsh-agent-loop (writes the scope tag)
yes
no
dsh-agent-presets (reads the scope tag)
yes
yes (independent copy)
dsh-scope (defines kScope)
yes (junction)
yes (independent copy)
Node caches ESM modules by resolved path, not by inode. The two dsh-scope files are byte-identical (hardlinks of the same file), yet they load as two distinct module instances, each with its own kScope Symbol. dsh-agent-loop writes the tag with instance A's Symbol, dsh-agent-presets reads with instance B's Symbol -> ctx[kScope] is always undefined -> "unscoped context".
Minimal reproduction (dynamic-importing the same physical file through both resolution paths):
same module instance (a === b)? false
a.createScope === b.createScope? false <- two distinct kScope Symbols
cross-instance scopeOf(ctx) reads: undefined <- exactly the failing condition
Suggested fixes
The web profile should not reinstall host-plane packages.profiles/web/package.json should only carry client packages and user plugins; host-plane packages (dsh-scope, dsh-agent-presets, dsh-agent, dsh-api-remotes, ...) should resolve from the single root plane so the loader never sees two copies of dsh-scope.
Make the scope tag resilient to dual instances.kScope Symbol identity currently depends on a single module instance. A shared registry (e.g. a WeakMap keyed by ctx, or a string-keyed tag) would survive dual-instance loading.
Workaround
Delete the web-plane copy so imports bubble up to the root instance:
Verified: after removal, require.resolve('@deepseek-ai/dsh-scope') from the web profile resolves to the single root instance and cold resume succeeds. (A pnpm reinstall recreates the copy, so this is a stopgap.)
Lovely harness — happy to provide more detail or test a fix. 💜
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Environment
0.1.0-rc.6, launched vianpx @deepseek-ai/dsh web$DSH_HOME/profiles/web(nodeLinker: hoisted)Symptom
Resuming an existing session in the web UI throws:
The error comes from
dsh-agent-presetsinmount()/composeFrom()whenscopeOf(agentCtx)returnsundefined.Key observation: live agents work fine (their
ReactLoopAgentstays in memory). Only a cold resume — an agent that must be reconstructed throughctx.agents.resume -> composeAgent() -> presets.mount(agentCtx)— fails. The first resume after a restart reproduces it every time.Root cause: dsh-scope dual instance -> kScope Symbol split
@deepseek-ai/dsh-scopetags every agent context withkScope = Symbol("dsh.scope"), a module-level constant indsh-scope/lib/index.js.dsh-agent-loopwrites the tag viacreateScope(loopCtx, this)when constructingReactLoopAgent;dsh-agent-presetsreads it back viascopeOf(agentCtx).The web profile is its own pnpm workspace (
$DSH_HOME/profiles/web). Itspackage.jsondepends on@deepseek-ai/dsh-agent-presetsand@deepseek-ai/dsh-scope, so those get their own copy underprofiles/web/node_modules— while@deepseek-ai/dsh-agent-loopexists only in the root plane (profiles/node_modules). That yields two independent node_modules planes:profiles/node_modules)profiles/web/node_modules)kScope)Node caches ESM modules by resolved path, not by inode. The two
dsh-scopefiles are byte-identical (hardlinks of the same file), yet they load as two distinct module instances, each with its ownkScopeSymbol.dsh-agent-loopwrites the tag with instance A's Symbol,dsh-agent-presetsreads with instance B's Symbol ->ctx[kScope]is alwaysundefined-> "unscoped context".Minimal reproduction (dynamic-importing the same physical file through both resolution paths):
Suggested fixes
profiles/web/package.jsonshould only carry client packages and user plugins; host-plane packages (dsh-scope,dsh-agent-presets,dsh-agent,dsh-api-remotes, ...) should resolve from the single root plane so the loader never sees two copies ofdsh-scope.kScopeSymbol identity currently depends on a single module instance. A shared registry (e.g. a WeakMap keyed byctx, or a string-keyed tag) would survive dual-instance loading.Workaround
Delete the web-plane copy so imports bubble up to the root instance:
rm -rf "$DSH_HOME/profiles/web/node_modules/@deepseek-ai/dsh-scope"Verified: after removal,
require.resolve('@deepseek-ai/dsh-scope')from the web profile resolves to the single root instance and cold resume succeeds. (A pnpm reinstall recreates the copy, so this is a stopgap.)Lovely harness — happy to provide more detail or test a fix. 💜
All reactions