Repository navigation
Releases: Dingpenghui-good/dsh-obsidian-sync
Release list
v3.4.7
发布说明 v3.4.7
日期: 2026-10-03
修复 / Fixes
- 依赖模型重构:
@deepseek-ai/schemastery和react从运行时dependencies移到peerDependencies(标optional: true),仅保留已有宿主模块 peer 不变。- 根因:本插件是 DSH 宿主扩展,运行时在宿主进程内执行。宿主已经把
schemastery加载进同一进程作为宿主模块。本插件若再以dependencies携带同版本包,pnpm 会在插件树里再实例化一份,与宿主单实例不一致。 - 影响:插件自身代码行为不变,只改变了"谁提供这些包"——现在统一由 DSH 宿主提供,单实例。
- 根因:本插件是 DSH 宿主扩展,运行时在宿主进程内执行。宿主已经把
不变 / Unchanged
dsh-tools/dsh-fs/dsh-sandbox/dsh-util-values等宿主模块 peer 全部保持不变(>=0.2.0-rc.1,optional)。- Obsidian 知识库搜索、会话归档、PARA 规则匹配等核心功能全部不变。
smoke-test不变。
升级指引 / Upgrade
- 从 3.4.6 升到 3.4.7 无 breaking change,直接
pnpm update @dingpenghui/dsh-obsidian-sync即可。 - 插件安装后宿主行为不变,但不再在插件树里携带
schemastery副本。
v3.4.6 — normalizeShortId 剥离 session- 前缀(修复 sessionX 短 ID 与重复归档)
@dingpenghui/dsh-obsidian-sync v3.4.6
修复 obsidian_sync_session 的会话 ID 归一化:带 session- 前缀的完整 ID 不再把前缀字样计入短 ID。
修复
session-<uuid> 被误归一化为 sessionX,产生重复归档
- 现象:调用
obsidian_sync_session并传session_id: "session-9f10d319-…"(完整会话 ID,带session-前缀)时,生成的文件名短 ID 段是session9,而非预期的9f10d319。若同一会话此前已按裸 uuid 归档过,就会多出一份session9命名的重复笔记。 - 根因:
normalizeShortId直接replace(/[^A-Za-z0-9]/g, '')取前 8 位。"session-9f10d319-…"去非字母数字后是"session9f10d319…",前 8 位恰好是session9—— 前缀的 7 个字母把真正的 uuid 前 8 位挤掉了。 - 修复:归一化前先剥离约定的
session-前缀(/^session-/i),再取 uuid 前 8 位。session-9f10d319-…→9f10d3199f10d319-…(裸 uuid)→9f10d319(不变,向后兼容)- 带前缀与裸 uuid 现在落到同一幂等键(
date + shortId),不再产生重复。
- 短 ID 补位 hash 也改用剥离前缀后的字符串,保证确定性不变。
测试
scripts/test-idempotent.mjs同步新逻辑,新增断言:session-9f10d319-…→9f10d319、session-905ba53a-…→905ba53a、带前缀与裸 uuid 幂等键一致。scripts/test-sync-session-date-idempotent.mjs新增第 9 组:端到端验证session-<uuid>与<uuid>落到同一归档文件。pnpm build、pnpm typecheck、smoke-test、client-smoke-test、contract-test(ALL PASS)、test-tokenize、test-idempotent、test-index-boundary、test-sync-session-date-idempotent全部通过。
升级
dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.6@dingpenghui/dsh-obsidian-sync v3.4.6
Fixes obsidian_sync_session session-ID normalization so a full ID with the session- prefix no longer pollutes the short ID.
Fixed
session-<uuid> mis-normalized to sessionX, producing duplicate archives
- Symptom: calling
obsidian_sync_sessionwithsession_id: "session-9f10d319-…"(the full session ID,session-prefix included) produced a filename whose short-ID segment wassession9instead of the expected9f10d319. If the same session had already been archived from the bare UUID, this created an extrasession9-named duplicate note. - Root cause:
normalizeShortIddidreplace(/[^A-Za-z0-9]/g, '')and took the first 8 chars."session-9f10d319-…"stripped to"session9f10d319…", whose first 8 chars aresession9— the 7-letter prefix shoved out the real UUID prefix. - Fix: strip the conventional
session-prefix (/^session-/i) before normalizing.session-9f10d319-…→9f10d3199f10d319-…(bare UUID) →9f10d319(unchanged, backward compatible)- Prefixed and bare UUIDs now land on the same idempotent key (
date + shortId), no more duplicates.
- The short-ID backfill hash also uses the prefix-stripped string, keeping determinism.
Tests
scripts/test-idempotent.mjsmirrors the new logic; new assertions:session-9f10d319-…→9f10d319,session-905ba53a-…→905ba53a, prefixed vs bare UUID share the same idempotent key.scripts/test-sync-session-date-idempotent.mjsgains a 9th group: end-to-end check thatsession-<uuid>and<uuid>produce the same archive file.pnpm build,pnpm typecheck,smoke-test,client-smoke-test,contract-test(ALL PASS),test-tokenize,test-idempotent,test-index-boundary,test-sync-session-date-idempotentall pass.
Upgrade
dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.6v3.4.5 — date 参数(回填历史会话)+ 摘要幂等修复
@dingpenghui/dsh-obsidian-sync v3.4.5
新增归档日期回填能力,并修复「摘要未变则跳过」长期失效的缺陷。
新增
obsidian_sync_session 支持可选参数 date
- 传
date: "YYYY-MM-DD"时,该日期同时决定四件事:文件名前缀、frontmatterdate、正文「基本信息」表格日期、以及归档索引挂载的日期段。 - 省略该参数,或值不符合
YYYY-MM-DD格式时,回退为运行当天(UTC),行为与旧版本完全一致。 - 典型用途:回填历史会话。此前插件只能把历史会话归档到"同步执行日",导致索引日期与实际发生日不符。
{
"session_id": "0c0c4b4e-361d-440b-ab58-948a2ee43a26",
"title": "Proton VPN 单机逐步诊断",
"summary": "…",
"date": "2026-09-12"
}修复
「摘要未变则跳过」实际从未生效
- 现象:同一会话重复同步时,每次都返回
skipped: false并重写笔记、追加一次 git 提交,幂等只在文件名层面成立。 - 根因:判断逻辑把
## 摘要之后的全部剩余内容与本次摘要比较,而这段内容还包含## 关联区块与> 📎 原始日志落款,因此永不相等。 - 修复:比较范围截取到摘要区块末尾 —— 取「下一个
##标题」「\n---\n分隔线」「空行起的>落款行」三者中最早出现的位置;三者都不存在时截到文末。 - 边界细节:只判断前两种边界是不够的 —— 未传
related时摘要后直接跟落款,仍会带上落款,故补入第三种边界。
测试
- 新增回归脚本
scripts/test-sync-session-date-idempotent.mjs(8 组断言):显式date的四处落点、同摘要跳过、摘要变化重写、5 种非法date回退且仍幂等、不传date保持旧行为、含## 关联/ 含落款 / 噪声过滤三种场景。 pnpm typecheck、pnpm build、node smoke-test.mjs、node client-smoke-test.mjs、node contract-test.mjs(ALL PASS)、scripts/test-tokenize.mjs、scripts/test-idempotent.mjs、scripts/test-index-boundary.mjs全部通过。
升级
dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.5@dingpenghui/dsh-obsidian-sync v3.4.5
Adds backdated archiving and fixes a long-broken "skip when summary unchanged" check.
Added
Optional date parameter for obsidian_sync_session
- When
date: "YYYY-MM-DD"is supplied it drives four things at once: the filename prefix, the frontmatterdate, the date row in the note body, and the day section in the archive index. - Omit it (or pass a malformed value) and it falls back to today's UTC date — identical to previous behaviour.
- Typical use: backfilling historical sessions. Previously every archived session landed on the day the sync ran, so index dates did not match when the session actually happened.
Fixed
"Skip when summary unchanged" never actually triggered
- Symptom: re-syncing the same session always returned
skipped: false, rewrote the note and produced another git commit; idempotency only held at the filename level. - Root cause: the comparison took everything after
## 摘要and compared it with the incoming summary — but that span also contains the## 关联section and the> 📎 原始日志footer, so it never matched. - Fix: the comparison now stops at the end of the summary block — the earliest of the next
##heading, a\n---\nseparator, or a blank-line-prefixed>footer line; falling back to end-of-file when none exist. - Boundary note: handling only the first two cases was insufficient — without
related, the footer follows the summary directly and was still included, hence the third boundary.
Tests
- New regression script
scripts/test-sync-session-date-idempotent.mjs(8 assertion groups): the four placesdatelands, skip on unchanged summary, rewrite on changed summary, five malformed dates falling back while staying idempotent, omitteddatekeeping old behaviour, and the## 关联/ footer / noise-filter scenarios. pnpm typecheck,pnpm build,node smoke-test.mjs,node client-smoke-test.mjs,node contract-test.mjs(ALL PASS),scripts/test-tokenize.mjs,scripts/test-idempotent.mjs,scripts/test-index-boundary.mjsall pass.
Upgrade
dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.5v3.4.4 — 修复宿主包副本导致的工具调用崩溃
dsh-obsidian-sync v3.4.4
修复一个会让任何工具调用直接崩溃的缺陷:插件把宿主包写进了 dependencies,在 profile 中装出第二份 @deepseek-ai/dsh-tools 副本,破坏了 Symbol 身份。
修复
宿主包出现在 dependencies,导致 tools 服务被"同名副本"覆盖
-
现象:插件安装后,会话一开始调用工具就整轮失败,报:
Cannot read properties of undefined (reading 'prepare')模型能正常看到
obsidian_search/obsidian_brief/obsidian_read_note/obsidian_sync_session(说明工具注册本身没问题),但只要执行工具就崩,且卸载插件后仍然复现,必须重启 DSH 才能恢复。 -
根因:
@deepseek-ai/dsh-tools被声明在dependencies中。pnpm 于是在 profile 内额外安装一份@deepseek-ai/dsh-tools(版本与宿主相同,都是 0.2.0-rc.2,但是两个不同的模块实例)。而工具调度器是通过模块级 Symbol 定位的:
export const TOOL_RUNTIME_SCHEDULER: unique symbol = Symbol('@deepseek-ai/dsh-tools.scheduler')
Symbol()(而非Symbol.for())在不同模块副本之间不共享身份。副本被纳入运行时后,tools服务与该 Symbol 来自不同副本,于是 agent-loop 中的ctx.tools[TOOL_RUNTIME_SCHEDULER].prepare(call.exec)
取到
undefined,抛出上述错误。 -
修复:按 DSH 官方插件的既定做法,把宿主提供的包从
dependencies移入peerDependencies:包 v3.4.3 v3.4.4 @deepseek-ai/dsh-toolsdependencies peerDependencies(+ devDependencies 供本地构建) @deepseek-ai/cordisdependencies peerDependencies(+ devDependencies) dependencies现在只剩@deepseek-ai/schemastery与react—— 与官方dsh-tool-jobs、dsh-web-search-deepseek的声明方式一致。 -
对照证据:官方插件的声明方式(从 app.asar 读取)
@deepseek-ai/dsh-tool-cordis dependencies={} peerDependencies={..., dsh-tools} @deepseek-ai/dsh-tool-jobs dependencies={schemastery} peerDependencies={..., dsh-tools} @deepseek-ai/dsh-agent dependencies={} peerDependencies={..., cordis} -
重要:在升级前必须完全重启 DSH。旧版本已经污染了当前进程的服务实例,仅卸载插件不会恢复。
验证
- 修复后安装产物中不再包含任何宿主包副本(实测
node_modules/@deepseek-ai/下只剩schemastery及其纯库依赖cosmokit)。 tsdown构建通过、tsc -p tsconfig.json类型检查通过。- 宿主侧冒烟测试(4 个 tool + 定时 effect)通过。
- 客户端 bundle 冒烟测试、契约测试(ALL PASS)、
SettingsFormModel激活复现探针全部通过。 scripts/test-tokenize/test-idempotent/test-index-boundary全部通过。
升级
dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.4升级后请完全退出并重启 DSH。
dsh-obsidian-sync v3.4.4
Fixes a defect that made every tool call crash: the plugin listed host packages under dependencies, producing a second @deepseek-ai/dsh-tools copy in the profile and breaking Symbol identity.
Fixes
Host packages declared in dependencies caused the tools service to be shadowed by a duplicate
-
Symptom: after installing the plugin, any session that invoked a tool failed the whole turn with:
Cannot read properties of undefined (reading 'prepare')The model could see
obsidian_search/obsidian_brief/obsidian_read_note/obsidian_sync_sessionnormally (so tool registration was fine), but executing any tool crashed — and it kept crashing after uninstalling the plugin until DSH was restarted. -
Root cause:
@deepseek-ai/dsh-toolswas declared underdependencies. pnpm therefore installed an extra@deepseek-ai/dsh-toolsinside the profile. Same version as the host (0.2.0-rc.2 both), but a different module instance.The tool scheduler is located through a module-level symbol:
export const TOOL_RUNTIME_SCHEDULER: unique symbol = Symbol('@deepseek-ai/dsh-tools.scheduler')
Symbol()— notSymbol.for()— has no shared identity across module copies. Once the duplicate entered the runtime, thetoolsservice and that symbol came from different copies, so this line in the agent loopctx.tools[TOOL_RUNTIME_SCHEDULER].prepare(call.exec)
evaluated to
undefinedand threw. -
Fix: following the established convention of DSH's own plugins, host-provided packages moved out of
dependenciesintopeerDependencies:Package v3.4.3 v3.4.4 @deepseek-ai/dsh-toolsdependencies peerDependencies (+ devDependencies for local builds) @deepseek-ai/cordisdependencies peerDependencies (+ devDependencies) dependenciesnow contains only@deepseek-ai/schemasteryandreact— matching officialdsh-tool-jobsanddsh-web-search-deepseek. -
Reference: how official plugins declare these (read from app.asar):
@deepseek-ai/dsh-tool-cordis dependencies={} peerDependencies={..., dsh-tools} @deepseek-ai/dsh-tool-jobs dependencies={schemastery} peerDependencies={..., dsh-tools} @deepseek-ai/dsh-agent dependencies={} peerDependencies={..., cordis} -
Important: you must fully restart DSH when upgrading. Previous versions already poisoned the running process's service instance; uninstalling alone will not recover it.
Verification
- The installed artifact no longer contains any host package copy (measured:
node_modules/@deepseek-ai/contains onlyschemasteryand its pure-library dependencycosmokit). tsdownbuild andtsc -p tsconfig.jsontypecheck pass.- Host smoke test (4 tools + timer effect) passes.
- Client bundle smoke test, contract test (ALL PASS), and the
SettingsFormModelactivation repro probe all pass. scripts/test-tokenize/test-idempotent/test-index-boundaryall pass.
Upgrade
dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.4Fully quit and restart DSH after upgrading.
v3.4.3 — 修复工具名导致会话全阻断
dsh-obsidian-sync v3.4.3
修复一个会让整个 DSH 会话无法对话的严重缺陷:工具名含点号,被模型 provider 的 function-name 校验直接拒绝。
修复
工具名违反 provider 命名约束,导致所有请求 400
-
现象:安装并启用本插件后,任何会话发出的每一条消息都立刻失败,模型一个字都吐不出来。会话记录中的报错为:
Invalid 'tools[14].name': string does not match pattern. Expected a string that matches the pattern '^[a-zA-Z0-9_-]+$'. code: INVALID_REQUEST, status: 400 -
根因:v3.4.2 及更早版本把工具注册为
obsidian.search/obsidian.brief/obsidian.read_note/obsidian.sync_session。点号.不在 provider 允许的 function-name 字符集^[a-zA-Z0-9_-]+$内。由于工具表是随每个请求整体上报的,只要插件处于启用状态,请求在模型侧校验阶段就被拒绝,与用户说什么内容无关。 -
修复:4 个工具名统一改为合法字符集:
v3.4.2 及更早 v3.4.3 obsidian.searchobsidian_searchobsidian.briefobsidian_briefobsidian.read_noteobsidian_read_noteobsidian.sync_sessionobsidian_sync_session同时在
src/index.ts工具注册处留下防回归注释,说明该字符集约束。 -
影响面:v3.4.2 及更早全部版本。属于“插件一装就坏”级别,请务必升级。
-
不兼容变更:工具调用名发生变化,请同步更新你自己的提示词、脚本或文档中出现的旧名。
其他改动
包名 scoped 化
包名由 dsh-obsidian-sync 变更为 @dingpenghui/dsh-obsidian-sync,插件 id、plugins.bundle.config / plugins.row.config slot key、安装文档与测试断言已同步更新。
验证
tsdown构建通过、tsc -p tsconfig.json类型检查通过。- 宿主侧冒烟测试(4 个 tool + 定时 effect,断言新工具名)通过。
- 客户端 bundle 冒烟测试通过。
- 真实
SettingsFormModel激活复现探针通过。 scripts/test-tokenize/test-idempotent/test-index-boundary全部通过。- 契约测试全绿:修正了 v3.1.0 时代遗留、与 v3.4.x 客户端设计相悖的一条断言(旧断言要求“namespace 未 serve 前详情页必须隐藏”,而 v3.4.x 是“
configForms存在即注册、缺失时降级为只读占位表单”)。
升级
dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.3dsh-obsidian-sync v3.4.3
Fixes a severe defect that made every DSH session unable to talk to the model: tool names contained dots, which the provider's function-name validation rejects.
Fixes
Tool names violated the provider naming constraint, failing every request with 400
-
Symptom: with the plugin installed and enabled, every message in any session failed instantly — the model never produced a single token. The session log showed:
Invalid 'tools[14].name': string does not match pattern. Expected a string that matches the pattern '^[a-zA-Z0-9_-]+$'. code: INVALID_REQUEST, status: 400 -
Root cause: v3.4.2 and earlier registered tools as
obsidian.search,obsidian.brief,obsidian.read_noteandobsidian.sync_session. A dot is not in the allowed function-name character set^[a-zA-Z0-9_-]+$. Because the tool list is sent with every request, the request was rejected during model-side validation whenever the plugin was enabled — regardless of what the user typed. -
Fix: all four tool names now use legal characters:
v3.4.2 and earlier v3.4.3 obsidian.searchobsidian_searchobsidian.briefobsidian_briefobsidian.read_noteobsidian_read_noteobsidian.sync_sessionobsidian_sync_sessionA regression-guard comment documenting the character-set constraint was added at the tool registration site in
src/index.ts. -
Affected: all versions up to and including v3.4.2. This is a "breaks on install" bug — upgrading is strongly recommended.
-
Breaking change: tool call names changed. Update any prompts, scripts or docs that referenced the old names.
Other changes
Scoped package name
The package is now @dingpenghui/dsh-obsidian-sync instead of dsh-obsidian-sync; the plugin id, plugins.bundle.config / plugins.row.config slot keys, install docs and test assertions were updated accordingly.
Verification
tsdownbuild andtsc -p tsconfig.jsontypecheck pass.- Host smoke test (4 tools + timer effect, asserting the new names) passes.
- Client bundle smoke test passes.
- Real
SettingsFormModelactivation repro probe passes. scripts/test-tokenize/test-idempotent/test-index-boundaryall pass.- Contract test is fully green: one v3.1.0-era assertion that contradicted the v3.4.x client design was corrected (the old assertion required detail pages to stay hidden until the namespace was served, whereas v3.4.x registers them as soon as
configFormsis available and degrades to a read-only placeholder form when it is missing).
Upgrade
dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.3dsh-obsidian-sync v3.4.2 — 测试脚本可移植化 / portable test scripts
dsh-obsidian-sync v3.4.2
测试脚本可移植化:去掉两处硬编码的绝对路径。
修复
测试脚本硬编码了作者机器的绝对路径
contract-test.mjs:C:/Users/braindge/.dsh/profiles/web/node_modules→ 改为按优先级解析客户端 bundle:DSH_OBSIDIAN_SYNC_CLIENT环境变量 → 本地构建产物lib/client.js→ DSH web profile 中的安装副本;全部候选都不存在时打印尝试过的路径并以退出码 1 结束。repro-client-activation.mjs:D:/DSH/workspace/dsh-obsidian-sync→ 改为以脚本自身所在目录推导(可用DSH_OBSIDIAN_SYNC_ROOT覆盖)。
两处均只改路径解析,断言与测试逻辑完全未变。修复前这两个脚本在非作者机器上都会以 ENOENT 直接崩溃。
已知问题(本次未处理)
contract-test.mjs仍有一条断言与 v3.4.x 的客户端设计不符:它断言"详情页在 namespace 未 serve 前必须隐藏",而 v3.4.x 已刻意改为"configForms存在即注册、缺失时降级为只读占位表单"(见src/client/index.ts)。该文件是 v3.1.0 时代的产物,本次未改动其断言。最终态断言(slot 数量与 key 正确)是通过的。
验证
tsdown构建通过、tsc -p tsconfig.json类型检查通过。- 宿主侧冒烟测试(4 个 tool + 3 个定时 effect)、客户端 bundle 冒烟测试通过。
- 真实
SettingsFormModel激活复现探针通过(repro-client-activation.mjs)。 scripts/test-tokenize/test-idempotent/test-index-boundary全部通过。
升级
dsh plugin --profile web add dsh-obsidian-sync@3.4.2dsh-obsidian-sync v3.4.2
Portable test scripts: two hardcoded absolute paths removed.
Fixes
Test scripts hardcoded the author's absolute paths
contract-test.mjs:C:/Users/braindge/.dsh/profiles/web/node_modules→ the client bundle is now resolved in priority order:DSH_OBSIDIAN_SYNC_CLIENT→ locally builtlib/client.js→ installed copy in a DSH web profile; when none exists it prints the tried paths and exits 1.repro-client-activation.mjs:D:/DSH/workspace/dsh-obsidian-sync→ now derived from the script's own directory (overridable viaDSH_OBSIDIAN_SYNC_ROOT).
Both changes touch path resolution only — assertions and test logic are unchanged. Before this fix both scripts crashed with ENOENT on any machine other than the author's.
Known issue (not addressed here)
contract-test.mjsstill carries one assertion that contradicts the v3.4.x client design: it expects the detail pages to stay hidden until the namespace is served, whereas v3.4.x deliberately registers them as soon asconfigFormsis available (degrading to a read-only placeholder form when it is missing) — seesrc/client/index.ts. That file is a v3.1.0-era artifact and its assertions were left untouched. The final-state assertions (slot count and keys) do pass.
Verification
tsdownbuild andtsc -p tsconfig.jsontypecheck pass.- Host smoke test (4 tools + 3 timer effects) and client bundle smoke test pass.
- Real
SettingsFormModelactivation repro probe passes (repro-client-activation.mjs). scripts/test-tokenize/test-idempotent/test-index-boundaryall pass.
Upgrade
dsh plugin --profile web add dsh-obsidian-sync@3.4.2dsh-obsidian-sync v3.4.1
dsh-obsidian-sync v3.4.1
修复插件详情页保存 vault 路径后不生效的问题。
修复
vault 路径保存后运行中实例仍用旧路径(v3.4.0 遗留问题)
- 现象:在插件详情页修改 vaultPath 并保存后,
cordis.patch.yml已正确写入新路径,但obsidian.search/obsidian.sync_session等工具仍在使用旧路径搜索/归档,必须重启整个应用才能生效。 - 根因:
src/index.ts的apply()在插件激活时把 vault 路径求值成模块级const resolvedVaultPath,之后所有索引重建、外部变更感知、归档/git 操作闭包引用的都是这个冻结值。设计注释假设"保存后 volatile HMR 会重新挂载本插件",但 0.2.0-rc.2 实际行为是:保存只写盘 + 更新 settings 镜像,不会自动重激活运行中的插件 fiber(重激活只在整应用重启或手动卸载/重载时发生),因此运行中的实例一直拿旧路径在跑。 - 修复:
- 路径改为惰性读取:
resolvedVaultPath(const)→currentVaultPath()(每次调用重读 settings 镜像),全部使用点替换为函数调用,每次索引重建/感知/归档都拿当前值。 - 新增
markDirtyOnVaultChange():在定时器 tick 与ensureIndex()入口比对当前路径与上次观测值,变更即置dirty,下一周期按新路径全量重建,无需重启。 - 顺带修正两个 typecheck 错误:
tsconfig.json加noEmit(TS5096:allowImportingTsExtensions需搭配noEmit/emitDeclarationOnly);客户端占位表单unavailableForm()的subscribe签名与ConfigFormSnapshot的mode: 'host'字段对齐。
- 路径改为惰性读取:
行为变化
- 保存新 vault 路径后无需重启:最多等一个索引周期(默认 60s)或下次调用任意 obsidian 工具时,自动按新路径重建索引。
- 其余功能不变。
验证
tsc类型检查通过、tsdown构建通过。- 主机侧冒烟测试通过(4 个工具 + 3 个定时 effect)。
- 客户端 bundle 冒烟测试 + 真实
SettingsFormModel激活复现探针通过。 - 安装目录
lib/index.js与工作区哈希一致(pnpm junction 同步)。
升级
dsh plugin --profile web add dsh-obsidian-sync@3.4.1升级后对 dsh-obsidian-sync 执行一次卸载/重新加载即可;之后改路径无需再重启。
dsh-obsidian-sync v3.4.1
Fixes the issue where saving a new vault path in the plugin detail page did not take effect until the whole app was restarted.
Fixes
Saved vault path not picked up by the running instance (v3.4.0 regression)
- Symptom: after editing
vaultPathand saving on the plugin detail page,cordis.patch.ymlwas written with the new path, yet theobsidian.search/obsidian.sync_sessiontools kept using the old path — a full app restart was required. - Root cause:
apply()insrc/index.tsfroze the vault path into a module-levelconst resolvedVaultPathat activation time; every index rebuild, external-change scan, and archive/git call closed over that frozen value. The design comment assumed "volatile HMR remounts the plugin after save", but on 0.2.0-rc.2 a save only writes the document and updates the settings mirror — it does not re-activate the running plugin fiber (that only happens on full app restart or manual unload/reload), so the live instance kept running with the stale path. - Fix:
- Path is now read lazily:
resolvedVaultPath(const) replaced bycurrentVaultPath()(re-reads the settings mirror on every call); all call sites use the function, so every index rebuild / scan / archive picks up the current value. - Added
markDirtyOnVaultChange(): called at the timer tick andensureIndex()entry, it compares the current path against the last observed one and marks the index dirty on change, forcing a full rebuild from the new vault on the next cycle — no restart needed. - Also fixed two typecheck errors found along the way:
tsconfig.jsongainednoEmit(TS5096:allowImportingTsExtensionsrequiresnoEmitoremitDeclarationOnly); the client-sideunavailableForm()placeholder now matches theConfigFormSnapshotsubscribesignature and includes the requiredmode: 'host'field.
- Path is now read lazily:
Behavior change
- Saving a new vault path now takes effect without restarting: within one index cycle (default 60 s) or on the next obsidian tool call, the index is automatically rebuilt from the new vault.
- Everything else is unchanged.
Verification
tsctypecheck andtsdownbuild pass.- Host smoke test passes (4 tools + 3 scheduled effects).
- Client bundle smoke test + real
SettingsFormModelactivation repro harness pass. - Installed
lib/index.jshash matches the workspace build (pnpm junction sync).
Upgrade
dsh plugin --profile web add dsh-obsidian-sync@3.4.1After upgrading, do one unload/reload of dsh-obsidian-sync; afterwards, path changes no longer need a restart.
dsh-obsidian-sync v3.4.0
dsh-obsidian-sync v3.4.0
本次发布包含 0.2.0-rc.2 激活修复的全量内容(v3.3.0 依赖声明修复 + v3.3.1 宿主 peer 依赖修复 + 本轮 web-boot 客户端修复)。
修复
web boot 客户端激活失败(dsh-obsidian-sync: failed)
- 根因:
plugin-detail-controller.ts的projection()读取了searchEnabled字段,但SettingsFormModel只注册了vaultPath和indexRefreshMs两个 spec。真实的SettingsFormModel.field(name)对未注册 spec 的字段会throw new Error('plugin card has no field <name>');而 controller 构造函数里this.store = this.form.bind(() => this.projection())会立即调用一次projection(),导致激活阶段抛错,fiber 标记为FAILED,web boot 报dsh-obsidian-sync: failed,整个应用无法启动。 - 修复:新增
searchEnabledFieldSpec()(布尔开关:format把true/false映射为'true'/'false'文本,parse反向解析,空串清除回默认),并注册进SettingsFormModel的 specs 数组——三个投影字段全部有 spec,不再抛错。
激活健壮性双保险
configForms不再是硬 inject(inject数组只保留slots、locale)。宿主未提供 settings 传输时ctx.get('configForms')为 undefined,降级为只读占位表单(status: 'unavailable',writable: false),条目永不因服务缺失而抛错。- slot 注册本身不读
configForms,随页面打开才惰性拉取快照,因此条目永远能激活。
宿主半激活失败(v3.3.1 继承修复)
- 把宿主服务包(
dsh-fs、dsh-sandbox、dsh-util-values等)从dependencies移到peerDependencies,runtime 依赖收敛到@deepseek-ai/cordis、@deepseek-ai/schemastery、@deepseek-ai/dsh-tools、react四个,修复 DSH 0.2.0-rc.2 fiber graph 激活时的 peer 依赖解析失败。
bundle patch 编码损坏(v3.3.0 继承修复)
cordis.patch.yml注释改纯 ASCII,修复 pnpm 复制 file: 包到 web profile 时中文注释编码损坏导致 YAML 解析失败、settings 行未加载的问题。
新增
repro-client-activation.mjs:客户端激活复现探针,从真实的@deepseek-ai/dsh-client-ui-primitives包提取SettingsFormModel源码 + 模拟启动期configForms镜像(status: 'loading'),完整跑一遍apply(),可在本地复现/验证 web-boot 激活问题。
验证
- 工作区
lib/client.js与安装目录(pnpm junction 同一文件)哈希一致,均为修复后版本。 - 真实
SettingsFormModel激活复现:APPLY OK(3 个 effect 全部执行:dictionaries / detail form subscriptions / detail page)。 - 主机侧冒烟测试通过(4 个工具 + 3 个定时 effect)。
- 客户端 bundle 冒烟测试通过。
安装 / 升级
dsh plugin --profile web add dsh-obsidian-sync@3.4.0或从本地:
cd dsh-obsidian-sync
pnpm install
pnpm build
dsh plugin --profile web add .dsh-obsidian-sync v3.4.0
This release bundles the full 0.2.0-rc.2 activation fix (v3.3.0 dependency-declaration fix + v3.3.1 host peer-dependency fix + this round's web-boot client fix).
Fixes
Web-boot client activation failure (dsh-obsidian-sync: failed)
- Root cause:
plugin-detail-controller.ts'sprojection()read thesearchEnabledfield, butSettingsFormModelonly had specs forvaultPathandindexRefreshMs. The realSettingsFormModel.field(name)throwsError('plugin card has no field <name>')for an unregistered spec; and the controller constructor'sthis.store = this.form.bind(() => this.projection())invokesprojection()immediately, so the throw fired during activation, the fiber was markedFAILED, web boot reporteddsh-obsidian-sync: failed, and the whole app failed to start. - Fix: Added
searchEnabledFieldSpec()(boolean switch:formatmapstrue/falseto the strings'true'/'false',parsereverses it, empty string clears back to default) and registered it in theSettingsFormModelspecs array — all three projection fields now have specs, so the throw is gone.
Activation robustness (belt-and-suspenders)
configFormsis no longer a hard inject (injectarray now only containsslotsandlocale). When the host provides no settings transport,ctx.get('configForms')is undefined and the entry falls back to a read-only placeholder form (status: 'unavailable',writable: false) — the entry never throws due to a missing service.- Slot registration itself does not read
configForms; the snapshot is pulled lazily when the page opens, so the entry always activates.
Host half activation failure (carried from v3.3.1)
- Moved host service packages (
dsh-fs,dsh-sandbox,dsh-util-values, etc.) fromdependenciestopeerDependencies, converging runtime deps to@deepseek-ai/cordis,@deepseek-ai/schemastery,@deepseek-ai/dsh-tools, andreact, fixing the peer-dependency resolution failure during DSH 0.2.0-rc.2 fiber-graph activation.
Bundle patch encoding corruption (carried from v3.3.0)
cordis.patch.ymlcomments switched to pure ASCII, fixing the pnpm file:-package copy encoding corruption that broke YAML parsing and left the settings line unloaded.
Additions
repro-client-activation.mjs: client activation reproduction harness that extracts the realSettingsFormModelsource from@deepseek-ai/dsh-client-ui-primitivesand runs a fullapply()against a boot-timeconfigFormsmirror (status: 'loading'), letting you reproduce/verify the web-boot activation issue locally.
Verification
- Workspace
lib/client.jsand the installed copy (same file via pnpm junction) have matching hashes, both the post-fix build. - Real
SettingsFormModelactivation repro:APPLY OK(all 3 effects ran: dictionaries / detail form subscriptions / detail page). - Host smoke test passed (4 tools + 3 scheduled effects).
- Client bundle smoke test passed.
Install / upgrade
dsh plugin --profile web add dsh-obsidian-sync@3.4.0or from local:
cd dsh-obsidian-sync
pnpm install
pnpm build
dsh plugin --profile web add .v3.2.0 - Detail page prerequisites actually mount
简体中文
修复"插件详情页不包含任何组件":让详情页前提条件设置真正生效(v3.1.0 的客户端半未被宿主加载的根因修复)。
根因
v3.1.0 的 package.json 声明 dsh.bundle.patch: ./cordis.yml,而 cordis.yml 是说明性文档(注释 + 非 loader 形态的条目)。
宿主从该文件派生 settings 命名空间行 dsh-obsidian-sync 时失败 → 行未服务 → 客户端 whileServed 门控永远不触发
→ plugins.bundle.config / plugins.row.config 槽位未注册 → 详情页继续显示"不包含任何组件"。
修复
- bundle patch 对齐:
dsh.bundle.patch现指向cordis.patch.yml(与dsh-conversation-language相同的 loader patch 文档:
顶层- id: dsh-obsidian-sync+ 完整config,重述行拥有的全部 key——patch 整体替换行配置) - 安装方式对齐:通过
pnpm add dsh-obsidian-sync@file:...正式安装到 web profile(取代此前手动拷入 node_modules 的非正式路径) - cordis.yml 降为参考文档:保留 4 个 Tool 与使用前提的说明性索引,不再被宿主加载
- 宿主半 apply() 对齐:去掉 config 参数,改经
settings.describe()读命名空间行值(volatile HMR 保存即重建) - 冒烟测试增强:验证 bundle patch 形态(loader 文档 + 全 key 重述 +
dsh.bundle.patch指针对齐)
验证
tsc --noEmit PASS · tsdown 双构建 PASS · 宿主冒烟 PASS(含 patch 形态)· 客户端冒烟 PASS · 契约测试 8/8 PASS · 安装后验证 PASS
使用前提条件(详情页可设定,保存后无需重启 DSH)
- vaultPath —— 必须指向已存在的 Obsidian vault 目录(PARA 结构),错则搜索/归档全失败
- searchEnabled —— 是否启用倒排索引搜索(关闭后仅保留 read_note 与 sync_session)
- indexRefreshMs —— 索引增量重建间隔(≥5000ms,默认 60000ms)
- git 自动推送(隐含前提,页面有说明):vault 为 git 仓库且有 remote 时归档自动 commit + push
English
Fixes the detail page still showing "This plugin contains no components": the v3.1.0 client half was never mounted by the host (root-cause fix).
Root cause
v3.1.0 declared dsh.bundle.patch: ./cordis.yml, but cordis.yml is a descriptive doc, not a loader patch document.
The host failed to derive the settings row dsh-obsidian-sync → namespace unserved → the client whileServed gate never fired
→ plugins.bundle.config / plugins.row.config slots never registered → detail page kept showing "no components".
Fix
- Bundle patch alignment:
dsh.bundle.patchnow points tocordis.patch.yml(same loader patch document shape asdsh-conversation-language:
top-level- id: dsh-obsidian-sync+ fullconfig, restating every key the row owns) - Install alignment: installed into the web profile via
pnpm add dsh-obsidian-sync@file:...(replaces the earlier ad-hoc manual copy) cordis.ymldemoted to a reference doc: keeps the 4-tool index and prerequisite notes, no longer host-loaded- Host
apply()alignment: dropped the config parameter; reads the namespace row viasettings.describe()(volatile HMR re-mounts on save) - Enhanced smoke test: validates bundle patch shape (loader doc + full key restatement + pointer alignment)
Verification
tsc --noEmit PASS · tsdown dual build PASS · host smoke PASS (incl. patch shape) · client smoke PASS · contract 8/8 PASS · post-install verify PASS
Prerequisites (settable on the detail page; live apply, no DSH restart)
- vaultPath — must point to an existing Obsidian vault (PARA structure); a wrong path breaks all search/archiving
- searchEnabled — toggle the inverted-index search tools (off leaves only read_note + sync_session)
- indexRefreshMs — index refresh interval (min 5000ms, default 60000ms)
- Git auto-push (implicit prerequisite, noted on the page): vault as a git repo with a remote enables auto commit + push on archive
v3.1.0 - Detail-page prerequisites settings
简体中文
为插件详情页新增「使用前提条件」设置表单(v3.0.0 适配 0.2.0-rc.1 后的 UI 增强)。
新增内容
- 插件详情页前提条件表单:点击插件列表中的
dsh-obsidian-sync名称即打开详情页,可设置:- vaultPath:Obsidian vault 绝对路径(核心前提,路径错则搜索/归档全部失败)
- searchEnabled:是否启用倒排索引搜索(关闭后仅保留 read_note 与 sync_session)
- indexRefreshMs:索引增量重建间隔(≥5000ms,默认 60000ms)
- Volatile 即时生效:保存后无需重启 DSH,经 profile Cordis patch + volatile HMR 即时应用
- git 自动推送前提说明:详情页展示 vault 需为 git 仓库且有 remote 的说明
技术变更
Configschema 字段标记.volatile(),宿主 settings 框架投影到浏览器设置页- 新增客户端半(
src/client/):注册plugins.bundle.config/plugins.row.config槽位 - tsdown 双构建:宿主 ESM + 客户端 CJS(
window.__ModuleLoader__.load工厂包装) - 新增 4 项测试:宿主冒烟 / 客户端冒烟 / 运行时契约(8/8 PASS)/ 类型检查
验证
tsc --noEmit PASS · tsdown 双构建 PASS · 宿主冒烟 PASS · 客户端冒烟 PASS · 契约测试 8/8 PASS
English
Adds a "Prerequisites" settings form to the plugin detail page (UI enhancement after the 0.2.0-rc.1 adaptation in v3.0.0).
New features
- Detail-page prerequisites form: Click the plugin name in the Plugins list to open the detail page, where you can configure:
- vaultPath: Absolute path to the Obsidian vault (core prerequisite — a wrong path breaks all search and archiving)
- searchEnabled: Toggle the inverted-index search tools off (only read_note and sync_session remain)
- indexRefreshMs: Index refresh interval (min 5000ms, default 60000ms)
- Volatile live apply: Changes take effect immediately after saving — no DSH restart needed
- Git push prerequisite note: The detail page documents that the vault must be a git repo with a remote for auto-push to work
Technical changes
Configschema fields marked.volatile()for browser-side settings projection- New client half (
src/client/): registersplugins.bundle.config/plugins.row.configslots - tsdown dual build: host ESM + client CJS (wrapped in
window.__ModuleLoader__.loadfactory) - Four new tests: host smoke / client smoke / runtime contract (8/8 PASS) / type check
Verification
tsc --noEmit PASS · tsdown dual build PASS · host smoke PASS · client smoke PASS · contract 8/8 PASS