Skip to content

Releases: Dingpenghui-good/dsh-obsidian-sync

v3.4.7

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 03 Oct 07:13

发布说明 v3.4.7

日期: 2026-10-03

修复 / Fixes

  • 依赖模型重构:@deepseek-ai/schemastery 和 react 从运行时 dependencies 移到 peerDependencies(标 optional: true),仅保留已有宿主模块 peer 不变。
    • 根因:本插件是 DSH 宿主扩展,运行时在宿主进程内执行。宿主已经把 schemastery 加载进同一进程作为宿主模块。本插件若再以 dependencies 携带同版本包,pnpm 会在插件树里再实例化一份,与宿主单实例不一致。
    • 影响:插件自身代码行为不变,只改变了"谁提供这些包"——现在统一由 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 与重复归档)

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 01 Oct 17:52

@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-… → 9f10d319
    • 9f10d319-…(裸 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_session with session_id: "session-9f10d319-…" (the full session ID, session- prefix included) produced a filename whose short-ID segment was session9 instead of the expected 9f10d319. If the same session had already been archived from the bare UUID, this created an extra session9-named duplicate note.
  • Root cause: normalizeShortId did replace(/[^A-Za-z0-9]/g, '') and took the first 8 chars. "session-9f10d319-…" stripped to "session9f10d319…", whose first 8 chars are session9 — the 7-letter prefix shoved out the real UUID prefix.
  • Fix: strip the conventional session- prefix (/^session-/i) before normalizing.
    • session-9f10d319-… → 9f10d319
    • 9f10d319-… (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.mjs mirrors 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.mjs gains a 9th group: end-to-end check that session-<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-idempotent all pass.

Upgrade

dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.6

v3.4.5 — date 参数(回填历史会话)+ 摘要幂等修复

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 01 Oct 11:20

@dingpenghui/dsh-obsidian-sync v3.4.5

新增归档日期回填能力,并修复「摘要未变则跳过」长期失效的缺陷。

新增

obsidian_sync_session 支持可选参数 date

  • 传 date: "YYYY-MM-DD" 时,该日期同时决定四件事:文件名前缀、frontmatter date、正文「基本信息」表格日期、以及归档索引挂载的日期段。
  • 省略该参数,或值不符合 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 frontmatter date, 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---\n separator, 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 places date lands, skip on unchanged summary, rewrite on changed summary, five malformed dates falling back while staying idempotent, omitted date keeping 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.mjs all pass.

Upgrade

dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.5

v3.4.4 — 修复宿主包副本导致的工具调用崩溃

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 01 Oct 10:45

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-tools dependencies peerDependencies(+ devDependencies 供本地构建)
    @deepseek-ai/cordis dependencies 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_session normally (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-tools was declared under dependencies. pnpm therefore installed an extra @deepseek-ai/dsh-tools inside 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() — not Symbol.for() — has no shared identity across module copies. Once the duplicate entered the runtime, the tools service and that symbol came from different copies, so this line in the agent loop

    ctx.tools[TOOL_RUNTIME_SCHEDULER].prepare(call.exec)

    evaluated to undefined and threw.

  • Fix: following the established convention of DSH's own plugins, host-provided packages moved out of dependencies into peerDependencies:

    Package v3.4.3 v3.4.4
    @deepseek-ai/dsh-tools dependencies peerDependencies (+ devDependencies for local builds)
    @deepseek-ai/cordis dependencies peerDependencies (+ devDependencies)

    dependencies now contains only @deepseek-ai/schemastery and react — matching official dsh-tool-jobs and dsh-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 only schemastery and its pure-library dependency cosmokit).
  • tsdown build and tsc -p tsconfig.json typecheck pass.
  • Host smoke test (4 tools + timer effect) passes.
  • Client bundle smoke test, contract test (ALL PASS), and the SettingsFormModel activation repro probe all pass.
  • scripts/test-tokenize / test-idempotent / test-index-boundary all pass.

Upgrade

dsh plugin --profile web add @dingpenghui/dsh-obsidian-sync@3.4.4

Fully quit and restart DSH after upgrading.

v3.4.3 — 修复工具名导致会话全阻断

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 01 Oct 10:18

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.search obsidian_search
    obsidian.brief obsidian_brief
    obsidian.read_note obsidian_read_note
    obsidian.sync_session obsidian_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.3

dsh-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_note and obsidian.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.search obsidian_search
    obsidian.brief obsidian_brief
    obsidian.read_note obsidian_read_note
    obsidian.sync_session obsidian_sync_session

    A 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

  • tsdown build and tsc -p tsconfig.json typecheck pass.
  • Host smoke test (4 tools + timer effect, asserting the new names) passes.
  • Client bundle smoke test passes.
  • Real SettingsFormModel activation repro probe passes.
  • scripts/test-tokenize / test-idempotent / test-index-boundary all 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 configForms is 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.3

dsh-obsidian-sync v3.4.2 — 测试脚本可移植化 / portable test scripts

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 01 Oct 09:08

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.2

dsh-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 built lib/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 via DSH_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.mjs still 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 as configForms is available (degrading to a read-only placeholder form when it is missing) — see src/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

  • tsdown build and tsc -p tsconfig.json typecheck pass.
  • Host smoke test (4 tools + 3 timer effects) and client bundle smoke test pass.
  • Real SettingsFormModel activation repro probe passes (repro-client-activation.mjs).
  • scripts/test-tokenize / test-idempotent / test-index-boundary all pass.

Upgrade

dsh plugin --profile web add dsh-obsidian-sync@3.4.2

dsh-obsidian-sync v3.4.1

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 30 Sep 04:44

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(重激活只在整应用重启或手动卸载/重载时发生),因此运行中的实例一直拿旧路径在跑。
  • 修复:
    1. 路径改为惰性读取:resolvedVaultPath(const)→ currentVaultPath()(每次调用重读 settings 镜像),全部使用点替换为函数调用,每次索引重建/感知/归档都拿当前值。
    2. 新增 markDirtyOnVaultChange():在定时器 tick 与 ensureIndex() 入口比对当前路径与上次观测值,变更即置 dirty,下一周期按新路径全量重建,无需重启。
    3. 顺带修正两个 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 vaultPath and saving on the plugin detail page, cordis.patch.yml was written with the new path, yet the obsidian.search / obsidian.sync_session tools kept using the old path — a full app restart was required.
  • Root cause: apply() in src/index.ts froze the vault path into a module-level const resolvedVaultPath at 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:
    1. Path is now read lazily: resolvedVaultPath (const) replaced by currentVaultPath() (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.
    2. Added markDirtyOnVaultChange(): called at the timer tick and ensureIndex() 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.
    3. Also fixed two typecheck errors found along the way: tsconfig.json gained noEmit (TS5096: allowImportingTsExtensions requires noEmit or emitDeclarationOnly); the client-side unavailableForm() placeholder now matches the ConfigFormSnapshot subscribe signature and includes the required mode: 'host' field.

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

  • tsc typecheck and tsdown build pass.
  • Host smoke test passes (4 tools + 3 scheduled effects).
  • Client bundle smoke test + real SettingsFormModel activation repro harness pass.
  • Installed lib/index.js hash matches the workspace build (pnpm junction sync).

Upgrade

dsh plugin --profile web add dsh-obsidian-sync@3.4.1

After upgrading, do one unload/reload of dsh-obsidian-sync; afterwards, path changes no longer need a restart.

dsh-obsidian-sync v3.4.0

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 30 Sep 04:21

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's projection() read the searchEnabled field, but SettingsFormModel only had specs for vaultPath and indexRefreshMs. The real SettingsFormModel.field(name) throws Error('plugin card has no field <name>') for an unregistered spec; and the controller constructor's this.store = this.form.bind(() => this.projection()) invokes projection() immediately, so the throw fired during activation, the fiber was marked FAILED, web boot reported dsh-obsidian-sync: failed, and the whole app failed to start.
  • Fix: Added searchEnabledFieldSpec() (boolean switch: format maps true/false to the strings 'true'/'false', parse reverses it, empty string clears back to default) and registered it in the SettingsFormModel specs array — all three projection fields now have specs, so the throw is gone.

Activation robustness (belt-and-suspenders)

  • configForms is no longer a hard inject (inject array now only contains slots and locale). 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.) from dependencies to peerDependencies, converging runtime deps to @deepseek-ai/cordis, @deepseek-ai/schemastery, @deepseek-ai/dsh-tools, and react, 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.yml comments 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 real SettingsFormModel source from @deepseek-ai/dsh-client-ui-primitives and runs a full apply() against a boot-time configForms mirror (status: 'loading'), letting you reproduce/verify the web-boot activation issue locally.

Verification

  • Workspace lib/client.js and the installed copy (same file via pnpm junction) have matching hashes, both the post-fix build.
  • Real SettingsFormModel activation 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.0

or from local:

cd dsh-obsidian-sync
pnpm install
pnpm build
dsh plugin --profile web add .

v3.2.0 - Detail page prerequisites actually mount

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 29 Sep 07:13

简体中文

修复"插件详情页不包含任何组件":让详情页前提条件设置真正生效(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)

  1. vaultPath —— 必须指向已存在的 Obsidian vault 目录(PARA 结构),错则搜索/归档全失败
  2. searchEnabled —— 是否启用倒排索引搜索(关闭后仅保留 read_note 与 sync_session)
  3. indexRefreshMs —— 索引增量重建间隔(≥5000ms,默认 60000ms)
  4. 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.patch now points to cordis.patch.yml (same loader patch document shape as dsh-conversation-language:
    top-level - id: dsh-obsidian-sync + full config, 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.yml demoted 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 via settings.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)

  1. vaultPath — must point to an existing Obsidian vault (PARA structure); a wrong path breaks all search/archiving
  2. searchEnabled — toggle the inverted-index search tools (off leaves only read_note + sync_session)
  3. indexRefreshMs — index refresh interval (min 5000ms, default 60000ms)
  4. 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

Choose a tag to compare

@Dingpenghui-good Dingpenghui-good released this 29 Sep 07:00

简体中文

为插件详情页新增「使用前提条件」设置表单(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 的说明

技术变更

  • Config schema 字段标记 .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

  • Config schema fields marked .volatile() for browser-side settings projection
  • New client half (src/client/): registers plugins.bundle.config / plugins.row.config slots
  • tsdown dual build: host ESM + client CJS (wrapped in window.__ModuleLoader__.load factory)
  • 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