One harness home resolver
单一 harness home 解析器
The harness had two inconsistent conventions for "where does DeepSeek Harness user data live": - `@deepseek-ai/dsh-home` resolved `configured ?? $DSH_HOME ?? ~/.dsh`. - `@deepseek-ai/dsh-home-paths` shipped a **second** `resolveDshHome` with the same precedence plus tilde expansion — a near-duplicate of `dsh-home` that no gate flagged because the two lived in different packages and had already drifted (only one expan
English
Problem
The harness had two inconsistent conventions for "where does DeepSeek Harness user data live":
@deepseek-ai/dsh-homeresolvedconfigured ?? $DSH_HOME ?? ~/.dsh.@deepseek-ai/dsh-home-pathsshipped a secondresolveDshHomewith the same precedence plus tilde expansion — a near-duplicate ofdsh-homethat no gate flagged because the two lived in different packages and had already drifted (only one expanded tildes).
Two resolvers for the same cross-cutting fact meant there was no single home policy.
Decision
One resolver owns the harness home, in @deepseek-ai/dsh-home-paths, single-root:
explicit configured path > $DSH_HOME > ~/.dsh
An empty or whitespace-only $DSH_HOME is treated as unset; otherwise resolve('') would silently place the home at the current working directory. The harness keeps all user data under one root; there is no XDG config/data/cache split. dshHomePath(...segments) joins deployment-owned children onto that root, and dsh-app-boot exposes it to Loader !!js config expressions before mounting entries, so shipped compositions derive sessions and storages without copying the resolver. dshHomeDisplay() names a resolved root symbolically for user-facing paths — ~/.dsh for the default home, $DSH_HOME for any configured home — so the user-global AGENTS.md label never leaks an absolute machine path. It replaces agent-instructions's bespoke default-vs-$DSH_HOME check. Home is process policy rather than row configuration: dsh-agent-instructions accepts no home override, so a composition cannot point instruction loading at a root that sessions, skills, and storage would not read. resolveAgentsHome() and agentsHomeDisplay() apply the same precedence, tilde expansion, and symbolic labelling to the shared agent configuration root ($DSH_AGENTS_HOME or ~/.agents), which stays outside the harness home because other agent tools share it (shared agents root); dsh-skill-filesystem still accepts its own dshHome and agentsHome fields, which select its own skill roots and resolve through the shared resolver in the instruction-loading change.
dshCachePath(...segments) derives paths below the resolved home's cache directory. An initial { dshHome } option preserves a provider's explicit home override. It resolves paths without creating directories; callers own directory creation. attachment-local uses this helper for regenerable request-image variants while retaining durable attachment objects in their versioned storage tree, so clearing the cache cannot remove Session attachments. Existing request-image cache entries are left in place and are not read or copied; a cache miss regenerates the variant from its durable attachment.
@deepseek-ai/dsh-home is deleted. Home-owning providers and boot packages import resolveDshHome from dsh-home-paths; composition bundles contain only the resolved configuration rows.
dsh-telemetry and its separate home policy are absent under the SDK project toolchain removal, leaving this resolver as the sole home policy.
Alternatives considered
Leave the two resolveDshHome copies in place. They had already drifted (one expands tildes, one didn't) and encode the same cross-cutting fact twice. Consolidation is the point of the util/ layer; a duplicate resolver is a latent divergence bug.
Adopt XDG (honor $XDG_CONFIG_HOME, or split config/data/cache into separate trees). Considered and dropped in favor of one obvious root. A single $DSH_HOME || ~/.dsh ground truth matches ~/.claude / ~/.aws, needs no per-kind reclassification of every ~/.dsh consumer, and leaves no resolver asymmetry to reconcile.
Consequences
- One home fact, one resolver.
dsh-home-pathsis the sole owner; theutil/group loses thehomepackage.
中文
问题
对于"DeepSeek Harness 用户数据存放在哪里",harness 里存在两套互不一致的约定:
@deepseek-ai/dsh-home按configured ?? $DSH_HOME ?? ~/.dsh解析。@deepseek-ai/dsh-home-paths又提供了第二个resolveDshHome,优先级相同但额外做了波浪号展开——它几乎是dsh-home的重复实现,却没有任何门禁发现,因为两者分属不同的包,而且早已漂移(只有一个会展开波浪号)。
同一条横切事实有两个解析器,意味着不存在单一的 home 策略。
决策
由一个解析器统一掌管 harness home,落在 @deepseek-ai/dsh-home-paths,采用单一根目录:
explicit configured path > $DSH_HOME > ~/.dsh
空或仅含空白的 $DSH_HOME 被当作未设置处理;否则,resolve('') 会悄悄把 home 落在当前工作目录。harness 把所有用户数据都放在同一个根目录下;不存在 XDG 的 config/data/cache 拆分。dshHomePath(...segments) 将部署负责的子路径拼接到该根目录下,dsh-app-boot 在挂载条目前向 Loader !!js 配置表达式暴露它,因此出厂组合无需复制解析器即可派生 sessions 和 storages。dshHomeDisplay() 为面向用户的路径以符号形式命名已解析的根目录——默认 home 显示为 ~/.dsh,任何已配置的 home 显示为 $DSH_HOME——这样用户全局的 AGENTS.md 标签就绝不会泄露机器上的绝对路径。它取代了 agent-instructions 中自定义的「默认值 vs $DSH_HOME」判断。home 属于进程策略而非配置行:dsh-agent-instructions 不接受 home 覆盖,因此组合无法把指令加载指向会话、skill 与存储不会读取的根目录。resolveAgentsHome() 与 agentsHomeDisplay() 对共享 agent 配置根目录($DSH_AGENTS_HOME 或 ~/.agents)采用相同的优先级、波浪号展开与符号化标签;该根目录位于 harness home 之外,因为其他 agent 工具会共享它(共享 agents 根目录);dsh-skill-filesystem 仍接受自己的 dshHome 与 agentsHome 字段,用于选择它自己的 skill 根目录,并在指令加载改动中改用共享解析器。
dshCachePath(...segments) 在解析出的主目录下的 cache 目录中派生路径。首个 { dshHome } 选项保留提供方显式配置的主目录覆盖值。它只解析路径,不创建目录;目录创建由调用方负责。attachment-local 将此函数用于可重新生成的请求图片版本,持久附件对象仍保留在其带版本的存储树中,因此清空缓存不会删除 Session 附件。已有请求图片缓存条目保留在原处,不再读取或复制;缓存未命中时从持久附件重新生成请求版本。
@deepseek-ai/dsh-home 被删除。拥有 home 配置的提供方与 boot 包从 dsh-home-paths 导入 resolveDshHome;组合包只包含解析后的配置行。
dsh-telemetry 及其独立 home 策略已随 SDK 项目工具链移除一并消失,因此该解析器是唯一的 home 策略。
备选方案
保留两份 resolveDshHome 副本。 它们早已漂移(一个展开波浪号,一个不展开),并把同一条横切事实编码了两遍。util/ 层的意义正是在于合并,重复的解析器是一个潜在的分歧 bug。
采用 XDG(遵从 $XDG_CONFIG_HOME,或把 config/data/cache 拆分到各自的目录树)。 经过考虑后放弃,转而采用一个显而易见的根目录。单一的 $DSH_HOME || ~/.dsh 基准事实与 ~/.claude / ~/.aws 一致,无需对每个 ~/.dsh 消费方按类别重新归类,也不留下任何需要协调的解析器不对称。
影响
- 单一 home 事实,单一解析器。
dsh-home-paths是唯一归属方;util/组失去了home包。