Repository navigation
Two preset-mounting bugs in 0.1.1-rc.2 (symlinked preset dirs skipped in discovery; duplicate inspect provider registration) — with patches #4542
Replies: 1 comment
|
Short answer: both bugs are still present on master (0.1.6-alpha.1, commit 0d1f500). Your analysis is right, and the discovery.ts:288 citation still lands exactly on the readdir call. Bug 1 — symlinked preset dirs skipped. packages/preset/agent-presets/src/discovery.ts:288 reads the root with dirents and :295 filters: A symlink Dirent reports Bug 2 — duplicate inspect provider registration. packages/extensions/tool-cordis/src/index.ts:40-42 still registers every host provider on every mount, with no idempotency guard: and the registry still throws on duplicates — packages/extensions/cordis-host-runner/src/inspect-registry.ts:63: The registry instance is created once in DynamicCordisRunnerService's constructor (cordis-host-runner/src/index.ts:143), so it's process-global as you said. The shipped cordis preset still composes dsh-tool-cordis (packages/preset/agent-presets/presets/cordis/agent.cordis.yml:253-254), so cordis + a user preset carrying dsh-tool-cordis still collides on master. About your patches: 0001 targets the right spot (discovery.ts:288-295). For 0002 I did not apply it to master (not verified), but the surrounding code has moved since b150a55: providers now live in tool-cordis/src/providers.ts:26 ( Your third finding (one invalid Not verified from my side: I haven't reproduced either bug on master myself (no fleet here). If you have logs from 0.1.6-alpha.1 showing the preset-mount failure, that would close the loop. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Two independent bugs in agent-preset mounting, both reproduced in a live two-device fleet deployment of
dsh0.1.1-rc.2. Both are small, self-contained fixes; patches and a tested branch are linked below.1. Preset discovery silently skips preset directories that are symlinks (
@deepseek-ai/dsh-agent-presets)Symptom. A user preset materialized as a symlink (
~/.dsh/.agent-presets/<id>→ a directory in a synced shared tree) is absent from the roster with nobroken:marker. Defaulting the preset to it fails withagent-preset-not-found, and the web GUI's New Session flow cannot commit a workspace (initial workspace selection failed: … agent-preset-not-found).Root cause.
scanRoot(packages/preset/agent-presets/src/discovery.ts) iteratesreaddir(dir, { withFileTypes: true })and filters onchild.isDirectory(). For a symlink Dirent,isDirectory()isfalse— so a linked preset directory is skipped entirely.Fix. Follow symlink children with
stat(which resolves links) before the directory test; a broken link skips exactly like a non-directory. Roster paths remain rooted at the scanned root (the link), which is the path a user edits.2.
tool-cordisre-registration fails the second preset mount (@deepseek-ai/dsh-tool-cordis)Symptom. With two presets that both compose the Cordis toolset mounted in one process (e.g. the shipped
cordispreset plus a user preset includingdsh-tool-cordis), the second preset mount fails:In the web GUI this surfaces as
Model operation failed: internal: resume failed for session …— the session becomes unusable and its preset is not selectable in the composer.Root cause. The Host inspect registry is process-global and rejects duplicate provider ids.
apply()inpackages/extensions/tool-cordis/src/index.tsunconditionally registers all four first-party Host inspect providers on every preset mount, so the second mount throws.Fix + scoping note. The first-party Host providers are safe to register once per process:
Service/Event/Builtinanswer from static catalogs, and theToolprovider resolves the requesting agent at query time (ctx.tools.schemas(context.agent)).apply()now skips providers already registered by an earlier mount and registers the rest as before. If per-mount providers are ever added, they belong in a different registration path (a refcount was considered and rejected: the registrations carry no per-mount state).Verification
packages/preset/agent-presets: two regression tests added totests/discovery.spec.ts(symlink to a preset outside the root is discovered healthy with the link path; broken symlink skipped). Full file passes 23/23 under vitest.packages/extensions/tool-cordis:tests/cordis-lifecycle.spec.tspasses 11/11;tsc --noEmitclean for both packages against the built workspace (tsc -b tsconfig.host.json).cordis+ a fleet preset coexisting in one process, second mount applies cleanly, session resumes, preset picker round-trips.Patches
Branch: https://github.com/boeykokchin1969/deepseek-harness/tree/fix/preset-symlink-discovery-and-inspect-reregister (2 commits on
b150a55)0001-agent-presets-discover-preset-directories-that-are-symlinks.patch0002-tool-cordis-reuse-existing-first-party-inspect-registrations.patchHappy to rework the second fix as a refcounting variant if the team prefers symmetric register/unregister semantics, or to split the changes differently for review.
Follow-up (2026-08-26): a third silent-failure mode found while fleet-testing — one invalid YAML key kills the entire llm-pi-ai namespace
While debugging "custom providers disappear from the Models UI" on one of two machines (identical dsh 0.1.1-rc.2, identical composed configs, byte-identical provider blocks — except one), I instrumented dsh-settings + dsh-llm-pi-ai and traced it to a single model entry in settings.yaml:
The schema expects the enum keys
off | minimal | low | medium | high | xhigh | max(plus a boolean-false literal, which is unreachable from a parsed file, since JS object keys are strings). The failure mode is what I'd like to flag:SettingsProvider.register()resolves the stored section synchronously; a schema failure throws, which kills the owning plugin'sctx.inject(["settings"])fiber beforesetSource/onChange/watchare wired. The adapter then stays at zero routes forever.NO_ADAPTERat session start for any route that provider owns.publish()keeps the last good value for a namespace whose stored section fails, so a process that registered before the bad data landed keeps working. My second machine (valid key) ran fine the whole time — which made the failure look machine-dependent.Suggested improvements:
settings.mutate->settings-rejected); a read that fails registration could arguably keep a last-good/empty resolution instead of killing the namespace's consumer.false:key came from (the "no effort" case); emittingoff:(the enum string) would have been valid.Repro: put any llm-pi-ai provider model entry with a
reasoningEffortskey outside the enum (e.g.false) in settings.yaml, startdsh web-> Models page shows built-ins only;dsh --profile headlesswith that provider as default ->NO_ADAPTER.Local fix: change the key to
off:(or remove the entry). Happy to share the instrumented patches if useful.All reactions