Repository navigation
[Bug] Hermes adapter: _rebuild_if_stale() checks dist/bridge.cjs, but the runtime entry is dist/bridge.mjs — a stale ESM build never triggers a rebuild #2470
Description
Activity
- addedai:taskDispatched to AI coding agent | 已派发给 AI 编码任务Dispatched to AI coding agent | 已派发给 AI 编码任务area:pluginOpenClaw & HermesOpenClaw & Hermesstatus:in-progressSomeone or AI is working on it | 人工或 AI 正在处理Someone or AI is working on it | 人工或 AI 正在处理types:bugSomething isn't working | 功能异常Something isn't working | 功能异常
on Oct 8, 2026 🤖 AutoDev has picked up this issue and started working on it.
Task ID:
beae6a34d8139399
Working branch:bugfix/autodev-2470-20261008201724571
Target branch:latest dev* branch
Workflow: opsp (analysis → coding → testing → PR)I will post the PR link here once done. If I need more information, I will ask in the comments.
- added a commit that references this issue
on Oct 8, 2026 ✅ AutoDev task
beae6a34d8139399completed.Summary: Fix Issue #2470:
_rebuild_if_stale()in the Hermes adapter (apps/memos-local-plugin/adapters/hermes/memos_provider/daemon_manager.py) was comparing TypeScript source timestamps againstdist/bridge.cjs, but both spawn paths (daemon_manager._bridge_script()andbridge_client._bridge_script()) preferdist/bridge.mjssince the ESM migration (#1736 / #1998). This let a stalebridge.mjsnext to a freshbridge.cjsslip past the rebuild guard — the daemon then kept booting the stale ESM artifact without any warning, which is exactly the failure mode #2399 / #2465 triggered.The fix mirrors
_bridge_script()'s compiled-entry precedence (dist/bridge.mjs,dist/bridge.cjs) and adopts the stricter variant from the issue: rebuild when any compiled entry on disk is older than the newest*.tssource, treat a missingdist/as stale, and name the stale artifacts in the log line for easier diagnosis of future partial-build regressions.Behaviour change is narrow — only two matrix rows flip: (a) the headline bug "stale .mjs + fresh .cjs + newer source" now rebuilds, and (b) "only .mjs present + fresh" now correctly skips the rebuild (old code treated missing
.cjsas stale). All other branches (missingnpm, build failure, build timeout, both compiled fresh, etc.) keep identical behaviour.Tests: new file
apps/memos-local-plugin/tests/python/test_rebuild_if_stale.pywith 9 unittest cases covers every branch, including the exact failure scenario from the issue (TDD: 2 cases went red before the fix, all 9 green after). Full plugin python suite (141 tests) passes with no regressions; siblingtest_bridge_script_resolution(11 tests) also passes.ruff check+ruff format --checkboth clean. No API / schema / deps change. Reviewers: @whipser030, @hijzy.Base branch:
main
Branch:bugfix/autodev-2470-20261008201724571
Commit:b9b63ff
PR: #2472
Assigned to: @syzsunshine219
Reviewers: @whipser030, @hijzy- addedai:testingAI agent is running tests | AI 正在运行测试AI agent is running tests | AI 正在运行测试and removedai:codingAI agent is coding | AI 正在编码AI agent is coding | AI 正在编码
on Oct 8, 2026 - added a commit that references this issue
on Oct 8, 2026 - addedai:failedAI task failed | AI 任务失败AI task failed | AI 任务失败and removedai:testingAI agent is running tests | AI 正在运行测试AI agent is running tests | AI 正在运行测试ai:taskDispatched to AI coding agent | 已派发给 AI 编码任务Dispatched to AI coding agent | 已派发给 AI 编码任务
on Oct 8, 2026
Summary
_rebuild_if_stale()inapps/memos-local-plugin/adapters/hermes/memos_provider/daemon_manager.pychecks whetherdist/is stale againstdist/bridge.cjs. Since the ESM migration (#1736 / #1998), both spawn paths preferdist/bridge.mjs:daemon_manager._bridge_script():dist/bridge.mjs→dist/bridge.cjs→bridge.mtsbridge_client._bridge_script(): same orderSo the rebuild guard watches a file that isn't the one that runs.
Failure mode
If
dist/bridge.cjsis newer than the TypeScript sources butdist/bridge.mjsis older (for example, a partial build, or a build step that writes only the CJS entry), the guard returnsTrueand no rebuild happens. The daemon then starts the stalebridge.mjswithout any warning. Source changes that touch onlybridge.mts, such as #2399 / #2465 (initLoggingmissing from the ESM entry), are the case where this matters most: the fixed source exists, and the runtime keeps running the old ESM artifact.Suggested fix
Compare against the same entry the spawner will use, by reusing
_bridge_script()precedence, and treat a missing compiled entry as stale:A stricter option is to rebuild when either compiled entry is older than the newest source, since
dist/can hold both.Test idea
Make
dist/bridge.cjsnewer than the TypeScript sources anddist/bridge.mjsolder than them. Today_rebuild_if_stale()returnsTruewithout building; after the fix it should rebuild. Therglob("*.ts")source scan can stay as it is.