aws-runtime-bridge 1.9.52 → 1.9.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/package/acode/README.md +26 -0
  2. package/package/acode/dist/built-in-file-tools.d.ts +11 -0
  3. package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
  4. package/package/acode/dist/built-in-file-tools.js +4 -2
  5. package/package/acode/dist/built-in-symbols-tool.d.ts +7 -0
  6. package/package/acode/dist/built-in-symbols-tool.d.ts.map +1 -0
  7. package/package/acode/dist/built-in-symbols-tool.js +117 -0
  8. package/package/acode/dist/grammars/grammars/tree-sitter-bash.wasm +0 -0
  9. package/package/acode/dist/grammars/grammars/tree-sitter-c-sharp.wasm +0 -0
  10. package/package/acode/dist/grammars/grammars/tree-sitter-c.wasm +0 -0
  11. package/package/acode/dist/grammars/grammars/tree-sitter-cpp.wasm +0 -0
  12. package/package/acode/dist/grammars/grammars/tree-sitter-go.wasm +0 -0
  13. package/package/acode/dist/grammars/grammars/tree-sitter-java.wasm +0 -0
  14. package/package/acode/dist/grammars/grammars/tree-sitter-javascript.wasm +0 -0
  15. package/package/acode/dist/grammars/grammars/tree-sitter-jsx.wasm +0 -0
  16. package/package/acode/dist/grammars/grammars/tree-sitter-lua.wasm +0 -0
  17. package/package/acode/dist/grammars/grammars/tree-sitter-php.wasm +0 -0
  18. package/package/acode/dist/grammars/grammars/tree-sitter-python.wasm +0 -0
  19. package/package/acode/dist/grammars/grammars/tree-sitter-ruby.wasm +0 -0
  20. package/package/acode/dist/grammars/grammars/tree-sitter-rust.wasm +0 -0
  21. package/package/acode/dist/grammars/grammars/tree-sitter-tsx.wasm +0 -0
  22. package/package/acode/dist/grammars/grammars/tree-sitter-typescript.wasm +0 -0
  23. package/package/acode/dist/index.d.ts +7 -0
  24. package/package/acode/dist/index.d.ts.map +1 -1
  25. package/package/acode/dist/index.js +8 -0
  26. package/package/acode/dist/parsers/ast-cache.d.ts +33 -0
  27. package/package/acode/dist/parsers/ast-cache.d.ts.map +1 -0
  28. package/package/acode/dist/parsers/ast-cache.js +76 -0
  29. package/package/acode/dist/parsers/language-detector.d.ts +7 -0
  30. package/package/acode/dist/parsers/language-detector.d.ts.map +1 -0
  31. package/package/acode/dist/parsers/language-detector.js +44 -0
  32. package/package/acode/dist/parsers/language-queries.d.ts +5 -0
  33. package/package/acode/dist/parsers/language-queries.d.ts.map +1 -0
  34. package/package/acode/dist/parsers/language-queries.js +640 -0
  35. package/package/acode/dist/parsers/symbol-extractor.d.ts +8 -0
  36. package/package/acode/dist/parsers/symbol-extractor.d.ts.map +1 -0
  37. package/package/acode/dist/parsers/symbol-extractor.js +164 -0
  38. package/package/acode/dist/parsers/tree-sitter-loader.d.ts +18 -0
  39. package/package/acode/dist/parsers/tree-sitter-loader.d.ts.map +1 -0
  40. package/package/acode/dist/parsers/tree-sitter-loader.js +64 -0
  41. package/package/acode/dist/parsers/types.d.ts +61 -0
  42. package/package/acode/dist/parsers/types.d.ts.map +1 -0
  43. package/package/acode/dist/parsers/types.js +7 -0
  44. package/package/acode/dist/runtime.d.ts.map +1 -1
  45. package/package/acode/dist/runtime.js +75 -20
  46. package/package/acode/dist/sub-agent-manager.d.ts +16 -0
  47. package/package/acode/dist/sub-agent-manager.d.ts.map +1 -1
  48. package/package/acode/dist/sub-agent-manager.js +196 -15
  49. package/package/acode/dist/types.d.ts +1 -1
  50. package/package/acode/dist/types.d.ts.map +1 -1
  51. package/package/acode/package.json +4 -1
  52. package/package/aws-client-agent-mcp/dist/agent-client.test.js +64 -50
  53. package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +1 -1
  54. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +3 -12
  55. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
  56. package/package/aws-client-agent-mcp/dist/mcp-server.js +192 -115
  57. package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
  58. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +38 -0
  59. package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
  60. package/package/aws-client-agent-mcp/dist/memory-store.js +7 -7
  61. package/package/aws-client-agent-mcp/dist/memory-store.js.map +1 -1
  62. package/package/aws-client-agent-mcp/dist/memory-store.test.js +22 -0
  63. package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +1 -1
  64. package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +1 -1
  65. package/package/aws-client-agent-mcp/dist/memory-tools.js +3 -2
  66. package/package/aws-client-agent-mcp/dist/memory-tools.js.map +1 -1
  67. package/package.json +6 -3
@@ -54,8 +54,6 @@ export declare class McpServer {
54
54
  * 仅缓存 claim 成功的 URI,在 lease 到期前不再调 server。
55
55
  */
56
56
  private claimedCache;
57
- /** syncClaimStatusFromServer 互斥锁,防止后台定时器与 poll 路径并发执行 (v1.21.0) */
58
- private syncClaimMutex;
59
57
  /** profile 是否已同步(防止 activelySyncProfile 与 profileHandler 双重同步) */
60
58
  private profileSynced;
61
59
  /**
@@ -253,7 +251,8 @@ export declare class McpServer {
253
251
  private mergeSearchResults;
254
252
  /**
255
253
  * 列出短期类型记忆。
256
- * 主流程:只允许 recently_memory/agent_memory/project_memory,由 MemoryStore 阻止 session_context 的全量列出。
254
+ * 主流程:只允许 recently_memory/agent_memory,拒绝 project_memory(永久记忆仅通过 search 检索)。
255
+ * MemoryStore.listByType 内部仍允许 project_memory 供文件变更检测使用,但 MCP 层面不暴露给 Agent。
257
256
  */
258
257
  private handleListMemories;
259
258
  /**
@@ -395,20 +394,12 @@ export declare class McpServer {
395
394
  * 构建 Memory 文件变更检测通知。
396
395
  * Memory 增强功能 (v1.6.0)
397
396
  *
398
- * 检测流程:
399
- * 0. 同步 Server 的 claim 状态到本地,清理过期 claim
397
+ * 检测流程(所有数据均从服务端读取,不降级到本地):
400
398
  * 1. 检查本 Agent 标记为 updating 但未处理的 Memory
401
399
  * 2. 检查短期记忆、近期记忆绑定的文件是否变更
402
400
  * 3. 检查永久记忆的文件变更,先走 Server 原子 claim,再标记本地
403
401
  */
404
402
  private buildMemoryFileChangeNotification;
405
- /**
406
- * Step 0: 同步 Server 的 claim 状态到本地 roleMemoryStore (v1.21.0)。
407
- * 查询 Server 上当前 agent 持有的 updating 记忆 URI 列表,
408
- * 本地中标记为 updating 但 Server 不持有的 → 重置为 normal。
409
- * 处理场景:agent crash 后重启,Server 租约已过期被清理,但本地 JSON 仍卡在 updating。
410
- */
411
- private syncClaimStatusFromServer;
412
403
  /**
413
404
  * 检查指定 URI 是否在 claim 去重缓存内 (v1.21.0)。
414
405
  * 在 lease 有效期内不再重复 claim。
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA4EH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,yBAAyB,CAAc;IAC/C,OAAO,CAAC,qBAAqB,CAAU;IACvC,OAAO,CAAC,eAAe,CAAc;IACrC,kDAAkD;IAClD,OAAO,CAAC,WAAW,CAAkB;IACrC,sBAAsB;IACtB,OAAO,CAAC,cAAc,CAAiB;IACvC,+CAA+C;IAC/C,OAAO,CAAC,UAAU,CAAU;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B,CAAkC;IAErE;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB,CAAkC;IAEnE,mEAAmE;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iCAAiC,CAAiB;IAE1E;;;;OAIG;IACH,OAAO,CAAC,gBAAgB,CAAkD;IAE1E;;;OAGG;IACH,OAAO,CAAC,iBAAiB,CAA+C;IAExE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAM;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAU;IAEzD;;;;;OAKG;IACH,OAAO,CAAC,YAAY,CAA6B;IAEjD,mEAAmE;IACnE,OAAO,CAAC,cAAc,CAAoC;IAE1D,kEAAkE;IAClE,OAAO,CAAC,aAAa,CAAS;IAE9B;;;;OAIG;IACH,OAAO,CAAC,2BAA2B,CAA6B;IAChE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,+BAA+B,CAAU;IAEjE;;;OAGG;IACH,OAAO,CAAC,kBAAkB,CAAiC;IAQ3D;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAyB3B;;;;;;;OAOG;IACH,OAAO,CAAC,gCAAgC;YAU1B,kCAAkC;IAgHhD;;;;;;;OAOG;IACH,OAAO,CAAC,sBAAsB;IAU9B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAK3B;;;OAGG;YACW,uBAAuB;IAuBrC;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAkB3B;;;OAGG;IACH,OAAO,CAAC,0BAA0B;;IAoDlC,OAAO,CAAC,aAAa;IAwDrB;;OAEG;YACW,cAAc;IAmD5B;;OAEG;YACW,eAAe;IAmH7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAKlC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CActC;IAEH;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAapC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAgBxC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,8BAA8B;IAM7C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAWvC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,uBAAuB;IA4BtC,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,YAAY;IAIpB;;;OAGG;IACH;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAO7B;;;OAGG;YACW,0BAA0B;IAuCxC;;;OAGG;YACW,0BAA0B;IAuCxC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAkC1B;;;OAGG;YACW,yBAAyB;IAevC;;;;;;;OAOG;IAEH;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA6B3B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,6BAA6B;IAmCrC;;;OAGG;YACW,kBAAkB;IAyDhC;;;OAGG;YACW,gBAAgB;IAU9B;;;;;OAKG;YACW,kBAAkB;IA0GhC;;;OAGG;YACW,kBAAkB;IAmChC;;;;OAIG;YACW,kBAAkB;IAwChC;;;OAGG;YACW,iBAAiB;IAuC/B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAwB1B;;;OAGG;YACW,kBAAkB;IAQhC;;;OAGG;YACW,oBAAoB;IAoBlC;;;OAGG;IACH;;;;;;;;OAQG;YACW,wBAAwB;IA8DtC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAgBrB;;;;;;;;;;;;OAYG;YACW,8BAA8B;IA2G5C;;;OAGG;YACW,wBAAwB;IAyBtC;;;OAGG;YACW,yBAAyB;IA2CvC;;;;;OAKG;YACW,sBAAsB;IAmEpC;;;OAGG;YACW,8BAA8B;IAwB5C;;;OAGG;YACW,mBAAmB;YAenB,eAAe;IAe7B;;;OAGG;YACW,uBAAuB;YAcvB,mBAAmB;YAOnB,yBAAyB;YA4BzB,iBAAiB;YAoBjB,+BAA+B;IAW7C;;;OAGG;YACW,yBAAyB;IAYvC;;;;;OAKG;YACW,2BAA2B;YAiB3B,iBAAiB;IAkB/B;;;;;;;;;;;;;;;;OAgBG;YACW,aAAa;IAqE3B;;;OAGG;YACW,mCAAmC;IAwBjD,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,KAAK;IAOb;;;OAGG;YACW,8BAA8B;IA8D5C;;;;;;;;;;OAUG;YACW,iCAAiC;IA8D/C;;;;;;;;;OASG;YACW,iCAAiC;IA+T/C;;;;;OAKG;YACW,yBAAyB;IA0CvC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAUrB;;;OAGG;YACW,wBAAwB;IAgFtC;;;OAGG;YACW,gBAAgB;IA6B9B;;;;OAIG;IACH,OAAO,CAAC,gCAAgC;YAQ1B,gBAAgB;IAuB9B;;;OAGG;YACW,+BAA+B;YA4C/B,kBAAkB;YAQlB,qBAAqB;YAUrB,sBAAsB;YAmBtB,uBAAuB;YASvB,wBAAwB;YAUxB,gBAAgB;YAYhB,mBAAmB;IAejC;;OAEG;YACW,qBAAqB;IAsBnC;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IA6BnC;;;OAGG;IACH,OAAO,CAAC,cAAc;IAMtB;;;OAGG;IACH,OAAO,CAAC,kCAAkC;IAS1C;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAIlC;;;;;;;;;;;;;;OAcG;YACW,6BAA6B;IAkErC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B5B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IA+BzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;;OAGG;IACH,IAAI,IAAI,IAAI;YAsBE,YAAY;IAqF1B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAmC9B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
@@ -87,8 +87,6 @@ export class McpServer {
87
87
  * 仅缓存 claim 成功的 URI,在 lease 到期前不再调 server。
88
88
  */
89
89
  claimedCache = new Map();
90
- /** syncClaimStatusFromServer 互斥锁,防止后台定时器与 poll 路径并发执行 (v1.21.0) */
91
- syncClaimMutex = Promise.resolve();
92
90
  /** profile 是否已同步(防止 activelySyncProfile 与 profileHandler 双重同步) */
93
91
  profileSynced = false;
94
92
  /**
@@ -228,15 +226,15 @@ export class McpServer {
228
226
  * xxx → ./xxx (裸路径视为当前工作区相对)
229
227
  */
230
228
  normalizeBoundFilePath(filePath) {
231
- const trimmed = String(filePath || "").trim();
229
+ const trimmed = String(filePath || "").trim().replace(/\\/g, "/");
232
230
  if (!trimmed)
233
231
  return trimmed;
234
232
  // 去掉 @/ 前缀(@ 视为工作区根路径)
235
233
  const noAt = trimmed.startsWith("@/") ? trimmed.substring(1) : trimmed;
236
234
  if (noAt.startsWith("./"))
237
235
  return noAt;
238
- // /xxx 和 xxx 都统一成 ./xxx
239
- return `./${noAt.replace(/^\//, "")}`;
236
+ // /xxx 和 xxx 都统一成 ./xxx(与 Java normalizeBoundFilePath 保持一致,处理多前导斜杠)
237
+ return `./${noAt.replace(/^\/+/, "")}`;
240
238
  }
241
239
  /**
242
240
  * 规范化绑定的文件路径数组。
@@ -409,10 +407,10 @@ export class McpServer {
409
407
  if (reportableResult !== null) {
410
408
  // 正常结果:上报 toolResult + thinking(工具调用完成,回到思考状态)
411
409
  statusReporter.reportToolResult(reportableResult);
412
- statusReporter.reportThinking();
413
410
  }
414
- // 委托消息(reportableResult === null):跳过 reportToolResult 和 reportThinking
415
- // ACode runtime 会重新 poll_message,工具调用未真正结束,保持"调用MCP"状态
411
+ // 委托消息(reportableResult === null):跳过 reportToolResult(避免委托通知泄露到时间线),
412
+ // 但仍调用 reportThinking(工具调用已完成,Agent 回到思考状态等待 ACode runtime 重新 poll)
413
+ statusReporter.reportThinking();
416
414
  }
417
415
  return result;
418
416
  }
@@ -647,32 +645,45 @@ export class McpServer {
647
645
  return await this.memoryStore.readMemory(input);
648
646
  }
649
647
  if (input.memory_type) {
650
- const localStore = this.getMemoryStoreForType(input.memory_type);
651
- const localRecord = await localStore.readMemory(input);
652
- if (localRecord) {
653
- return localRecord;
654
- }
655
- // project_memory 跨实例可见性:本地未命中时回退服务端角色级 scope,
656
- // 使兄弟实例创建的记忆也能被读取(文件变更通知后 agent 需 read_memory)。
648
+ // project_memory:服务端为权威源(claim/租约/memory_status 均在服务端维护),
649
+ // 本地 roleMemoryStore 仅作服务端不可用时的离线降级副本。
650
+ // 之前本地优先会导致 read_memory 读到过时的 memory_status='normal',
651
+ // 而服务端 DB 已是 'updating'(文件变更 claim 只写服务端),状态脱节。
657
652
  if (input.memory_type === "project_memory") {
658
- return await this.memoryStore.readMemory(input);
653
+ try {
654
+ const serverRecord = await this.memoryStore.readMemory(input);
655
+ if (serverRecord) {
656
+ return serverRecord;
657
+ }
658
+ }
659
+ catch {
660
+ // 服务端不可用,降级到本地
661
+ }
662
+ return await this.roleMemoryStore.readMemory(input);
659
663
  }
660
- return null;
664
+ // recently_memory / agent_memory:实例级,走服务端
665
+ return await this.memoryStore.readMemory(input);
661
666
  }
662
- const longTermRecord = await this.roleMemoryStore.readMemory({
663
- ...input,
664
- memory_type: "project_memory",
665
- });
666
- if (longTermRecord) {
667
- return longTermRecord;
667
+ // 未指定 memory_type:优先查服务端 project_memory(权威源),
668
+ // 降级本地 roleMemoryStore(离线未同步记录),最后查服务端实例级记忆。
669
+ try {
670
+ const serverLongTerm = await this.memoryStore.readMemory({
671
+ ...input,
672
+ memory_type: "project_memory",
673
+ });
674
+ if (serverLongTerm) {
675
+ return serverLongTerm;
676
+ }
668
677
  }
669
- // 本地角色级未命中,回退服务端角色级 project_memory(兄弟实例创建的记忆)
670
- const serverLongTerm = await this.memoryStore.readMemory({
678
+ catch {
679
+ // 服务端不可用,降级到本地
680
+ }
681
+ const localLongTerm = await this.roleMemoryStore.readMemory({
671
682
  ...input,
672
683
  memory_type: "project_memory",
673
684
  });
674
- if (serverLongTerm) {
675
- return serverLongTerm;
685
+ if (localLongTerm) {
686
+ return localLongTerm;
676
687
  }
677
688
  return await this.memoryStore.readMemory(input);
678
689
  }
@@ -685,32 +696,39 @@ export class McpServer {
685
696
  return { store: this.memoryStore, memory_type: input.memory_type };
686
697
  }
687
698
  if (input.memory_type) {
688
- const localStore = this.getMemoryStoreForType(input.memory_type);
689
- // project_memory 跨实例:本地有则用本地,否则用服务端角色级 scope
690
- // (兄弟实例创建的记忆仅在服务端可见,需路由到 server 才能 update/delete)
699
+ // project_memory:服务端为权威源(claim/memory_status/租约均在服务端维护),
700
+ // mutation 必须走服务端,否则本地写入与服务端状态脱节。
701
+ // 本地 roleMemoryStore 仅在“本地存在离线记录但服务端没有”时降级(未同步的离线编辑);
702
+ // 新建(两者均无)必须落到服务端,避免 create 写入本地导致服务端统计/可见性脱节。
691
703
  if (input.memory_type === "project_memory") {
692
- const localRecord = await localStore.readMemory(input);
704
+ const serverRecord = await this.memoryStore.readMemory(input).catch(() => null);
705
+ if (serverRecord) {
706
+ return { store: this.memoryStore, memory_type: "project_memory" };
707
+ }
708
+ const localRecord = await this.roleMemoryStore.readMemory(input).catch(() => null);
693
709
  if (localRecord) {
694
- return { store: localStore, memory_type: "project_memory" };
710
+ return { store: this.roleMemoryStore, memory_type: "project_memory" };
695
711
  }
696
712
  return { store: this.memoryStore, memory_type: "project_memory" };
697
713
  }
698
- return { store: localStore, memory_type: input.memory_type };
714
+ // recently_memory / agent_memory:实例级,走服务端
715
+ return { store: this.memoryStore, memory_type: input.memory_type };
699
716
  }
700
- const longTermRecord = await this.roleMemoryStore.readMemory({
717
+ // 未指定 memory_type:优先查服务端 project_memory(权威源),
718
+ // 降级本地 roleMemoryStore(离线未同步记录),最后回退服务端实例级。
719
+ const serverLongTerm = await this.memoryStore.readMemory({
701
720
  uri: input.uri,
702
721
  memory_type: "project_memory",
703
- });
704
- if (longTermRecord) {
705
- return { store: this.roleMemoryStore, memory_type: "project_memory" };
722
+ }).catch(() => null);
723
+ if (serverLongTerm) {
724
+ return { store: this.memoryStore, memory_type: "project_memory" };
706
725
  }
707
- // 回退服务端角色级 project_memory(兄弟实例创建的记忆)
708
- const serverLongTerm = await this.memoryStore.readMemory({
726
+ const localLongTerm = await this.roleMemoryStore.readMemory({
709
727
  uri: input.uri,
710
728
  memory_type: "project_memory",
711
729
  });
712
- if (serverLongTerm) {
713
- return { store: this.memoryStore, memory_type: "project_memory" };
730
+ if (localLongTerm) {
731
+ return { store: this.roleMemoryStore, memory_type: "project_memory" };
714
732
  }
715
733
  return { store: this.memoryStore };
716
734
  }
@@ -792,7 +810,9 @@ export class McpServer {
792
810
  if (name === "search_memory") {
793
811
  sanitized.queryProvided =
794
812
  typeof args.query === "string" && args.query.length > 0;
795
- sanitized.keyword_mode = args.keyword_mode;
813
+ if (args.keyword_mode !== undefined) {
814
+ sanitized.keyword_mode = args.keyword_mode;
815
+ }
796
816
  }
797
817
  return sanitized;
798
818
  }
@@ -864,8 +884,9 @@ export class McpServer {
864
884
  last_check_time: lastCheckTime,
865
885
  };
866
886
  const created = await this.getMemoryStoreForType(input.memory_type).createMemory(input);
867
- await this.reportCombinedMemoryStats();
868
- // Dual-write permanent memory to server
887
+ // Dual-write permanent memory to server BEFORE reporting stats,
888
+ // 确保 reportCombinedMemoryStats 上报时 server 端已包含 project_memory 记录,
889
+ // 避免 Dashboard 统计与本地写入脱节。
869
890
  if (input.memory_type === "project_memory") {
870
891
  try {
871
892
  await this.memoryStore.createMemory(input);
@@ -874,6 +895,7 @@ export class McpServer {
874
895
  logger.warn("[McpServer] Failed to sync permanent memory to server:", e);
875
896
  }
876
897
  }
898
+ await this.reportCombinedMemoryStats();
877
899
  // v1.13.0: recently_memory 过期判定、agent_memory 过期/回忆状态由服务端 memory_status 字段管理,
878
900
  // 无需 TS 侧本地缓存。服务端默认按类型填充正确状态(recently_memory='normal', agent_memory='normal')。
879
901
  return this.toMemoryWriteResult(created);
@@ -923,7 +945,17 @@ export class McpServer {
923
945
  uri: input.uri,
924
946
  memory_type: target.memory_type ?? input.memory_type,
925
947
  });
926
- // 当显式传入新 bound_files 时验证文件是否存在
948
+ // 当记忆已绑定文件或处于更新中状态时,要求显式指定 bound_files 并校验文件存在性。
949
+ // 这确保文件被删除后,Agent 必须更新 bound_files 列表(移除已删除文件)才能完成更新,
950
+ // 避免已删除文件路径作为"僵尸绑定"长期保留。
951
+ const hasExistingBoundFiles = !!existingForMd5?.bound_files && existingForMd5.bound_files.length > 0;
952
+ const isUpdatingState = existingForMd5?.memory_status === "updating";
953
+ if ((hasExistingBoundFiles || isUpdatingState) &&
954
+ input.bound_files === undefined) {
955
+ throw new Error("此记忆已绑定文件或处于更新中状态,更新时必须指定 bound_files 列表。" +
956
+ "请提供 bound_files 参数(如需移除已删除的文件,请更新列表仅保留当前存在的文件)。");
957
+ }
958
+ // 校验 bound_files 中所有文件是否存在:不存在的文件会导致更新失败
927
959
  if (input.bound_files !== undefined) {
928
960
  await this.validateBoundFilesExist(input.bound_files);
929
961
  }
@@ -946,9 +978,9 @@ export class McpServer {
946
978
  file_md5_map: fileMd5Map,
947
979
  last_check_time: lastCheckTime,
948
980
  });
949
- await this.reportCombinedMemoryStats();
950
981
  // Dual-write permanent memory update to server(同步 MD5 map 避免服务端重复检测)
951
- // 当 target.store 已是服务端(兄弟实例创建的记忆回退场景)时跳过,避免重复写
982
+ // 当 target.store 已是服务端(兄弟实例创建的记忆回退场景)时跳过,避免重复写。
983
+ // 必须在 reportCombinedMemoryStats 之前完成,确保上报时 server 端统计已同步。
952
984
  if (target.memory_type === "project_memory" && target.store !== this.memoryStore) {
953
985
  try {
954
986
  await this.memoryStore.updateMemory({
@@ -962,6 +994,7 @@ export class McpServer {
962
994
  logger.warn("[McpServer] Failed to sync permanent memory update to server:", e);
963
995
  }
964
996
  }
997
+ await this.reportCombinedMemoryStats();
965
998
  // v1.13.0: agent_memory 过期与回忆状态由服务端 memory_status 字段管理。
966
999
  // v1.21.0: 如果是 project_memory 且之前是 updating 状态,释放 Server claim
967
1000
  if (target.memory_type === 'project_memory' && existingForMd5?.memory_status === 'updating') {
@@ -995,11 +1028,11 @@ export class McpServer {
995
1028
  memory_type: effectiveType,
996
1029
  });
997
1030
  if (deleted) {
998
- await this.reportCombinedMemoryStats();
999
1031
  // v1.13.0: 服务端 deleteMemory 内部已处理相关清理逻辑,
1000
1032
  // TS 侧无需任何本地元数据清理。
1001
1033
  // Dual-write permanent memory delete to server
1002
- // 当 target.store 已是服务端时跳过,避免重复删除
1034
+ // 当 target.store 已是服务端时跳过,避免重复删除。
1035
+ // 必须在 reportCombinedMemoryStats 之前完成,确保上报时 server 端统计已同步。
1003
1036
  if (effectiveType === "project_memory" && target.store !== this.memoryStore) {
1004
1037
  try {
1005
1038
  await this.memoryStore.deleteMemory({ uri: input.uri, memory_type: "project_memory" });
@@ -1008,6 +1041,7 @@ export class McpServer {
1008
1041
  logger.warn("[McpServer] Failed to sync permanent memory delete to server:", e);
1009
1042
  }
1010
1043
  }
1044
+ await this.reportCombinedMemoryStats();
1011
1045
  }
1012
1046
  return { deleted };
1013
1047
  }
@@ -1102,10 +1136,15 @@ export class McpServer {
1102
1136
  }
1103
1137
  /**
1104
1138
  * 列出短期类型记忆。
1105
- * 主流程:只允许 recently_memory/agent_memory/project_memory,由 MemoryStore 阻止 session_context 的全量列出。
1139
+ * 主流程:只允许 recently_memory/agent_memory,拒绝 project_memory(永久记忆仅通过 search 检索)。
1140
+ * MemoryStore.listByType 内部仍允许 project_memory 供文件变更检测使用,但 MCP 层面不暴露给 Agent。
1106
1141
  */
1107
1142
  async handleListMemories(args) {
1108
- return await this.memoryStore.listByType(this.getRequiredString(args, "memory_type"));
1143
+ const memoryType = this.getRequiredString(args, "memory_type");
1144
+ if (memoryType === "project_memory" || memoryType === "session_context") {
1145
+ throw new Error(`Invalid memory_type: ${memoryType}`);
1146
+ }
1147
+ return await this.memoryStore.listByType(memoryType);
1109
1148
  }
1110
1149
  /**
1111
1150
  * 获取本地记忆容量统计。
@@ -1354,11 +1393,17 @@ export class McpServer {
1354
1393
  async handleAbandonMemoryUpdate(args) {
1355
1394
  const uri = this.getRequiredString(args, "uri");
1356
1395
  const memoryType = this.getOptionalMemoryType(args);
1396
+ // 关闭更新前先读取记忆,校验已绑定文件的存在性。
1397
+ // 若存在已删除的文件,放弃更新将失败——Agent 需先通过 update_memory 修正 bound_files 列表
1398
+ // (移除不存在的文件)后,由 update_memory 自动释放 claim。
1399
+ const record = await this.memoryStore.readMemory({ uri, memory_type: memoryType });
1400
+ if (record?.bound_files && record.bound_files.length > 0) {
1401
+ await this.validateBoundFilesExist(record.bound_files);
1402
+ }
1357
1403
  // 调 Server release API 释放 claim
1358
1404
  await this.memoryStore.releaseMemoryClaim(uri);
1359
1405
  // 同时清除本地 roleMemoryStore 的 updating 状态(如果是 project_memory)
1360
1406
  // 兄弟实例创建的记忆本地不存在时静默跳过,服务端 release 才是权威操作
1361
- const record = await this.memoryStore.readMemory({ uri, memory_type: memoryType });
1362
1407
  if (record?.memory_type === 'project_memory') {
1363
1408
  try {
1364
1409
  await this.roleMemoryStore.updateMemory({
@@ -1839,8 +1884,7 @@ export class McpServer {
1839
1884
  * 构建 Memory 文件变更检测通知。
1840
1885
  * Memory 增强功能 (v1.6.0)
1841
1886
  *
1842
- * 检测流程:
1843
- * 0. 同步 Server 的 claim 状态到本地,清理过期 claim
1887
+ * 检测流程(所有数据均从服务端读取,不降级到本地):
1844
1888
  * 1. 检查本 Agent 标记为 updating 但未处理的 Memory
1845
1889
  * 2. 检查短期记忆、近期记忆绑定的文件是否变更
1846
1890
  * 3. 检查永久记忆的文件变更,先走 Server 原子 claim,再标记本地
@@ -1858,10 +1902,10 @@ export class McpServer {
1858
1902
  logger.info('[McpServer] 文件变更检测跳过: agentId 为空');
1859
1903
  return null;
1860
1904
  }
1861
- // Step 0: 同步 Server 的 claim 状态到本地 roleMemoryStore(可选,失败降级)。
1862
- // 清理那些 Server 上已释放(租约过期或被清理)但本地仍标记为 updating 的记录。
1863
- // Server API 不可用时降级,依赖本地状态,不影响检测流程。
1864
- await this.syncClaimStatusFromServer(agentId).catch(e => logger.info("[McpServer] Step 0 同步 claim 状态失败(降级):", e));
1905
+ // 所有检查均从服务端读取(memory_status/claim/租约/file_md5_map 均以服务端 DB 为权威源)。
1906
+ // 原Step 0 syncClaimStatusFromServer 读本地 roleMemoryStore 清理本地过时 claim,
1907
+ // 服务端权威后本地 updating 状态不再被任何决策路径读取,该同步已无意义,移除。
1908
+ // 服务端不可用时直接 catch 返回 null(下方 catch),不降级到本地。
1865
1909
  // 步骤1: 检查是否有 pending 的 updating memories(服务端角色级永久记忆),
1866
1910
  // 同时检测绑定文件是否再次变更(用户可能在 Agent 处理前又改了文件)
1867
1911
  // 数据源切换:从服务端角色级 scope 读取(同角色所有实例共享),
@@ -1880,6 +1924,7 @@ export class McpServer {
1880
1924
  const memory = updatingMemories.find(m => m.id === change.memoryId);
1881
1925
  if (memory) {
1882
1926
  // MD5 回写到服务端角色级 scope(显式传 memory_status:updating 防止 auto-release)
1927
+ // bound_files 同步回写:checkMemoriesFileChanges 可能已修剪掉消失的文件路径(消除悬空引用)
1883
1928
  await this.memoryStore.updateMemory({
1884
1929
  uri: memory.uri,
1885
1930
  memory_type: 'project_memory',
@@ -1887,6 +1932,7 @@ export class McpServer {
1887
1932
  updating_agent_id: agentId,
1888
1933
  file_md5_map: memory.file_md5_map,
1889
1934
  last_check_time: memory.last_check_time,
1935
+ bound_files: memory.bound_files,
1890
1936
  });
1891
1937
  }
1892
1938
  }
@@ -1959,7 +2005,7 @@ export class McpServer {
1959
2005
  logger.info(`[McpServer] 文件变更检测 Step2: recently=${recentlyMemoryMemories.length}, agent=${agentMemoryMemories.length}, ` +
1960
2006
  `有绑定文件=${listableWithFiles.length}/${listableMemories.length}。` +
1961
2007
  `详情: ${listableMemories.map(m => `${m.uri}(bound=${m.bound_files?.length ?? 0},md5=${Object.keys(m.file_md5_map ?? {}).length})`).join('; ') || '无 memory'}`);
1962
- // 用本地 MD5 缓存补全 listable 记忆(服务端可能不持久化 file_md5_map 字段)
2008
+ // 用本地 MD5 缓存补全 listable 记录(服务端可能不持久化 file_md5_map 字段)
1963
2009
  for (const m of listableMemories) {
1964
2010
  if (!m.file_md5_map) {
1965
2011
  const cached = this.listableMd5Cache.get(m.id);
@@ -1968,6 +2014,14 @@ export class McpServer {
1968
2014
  }
1969
2015
  }
1970
2016
  }
2017
+ // 捕获检测前的 md5 map 快照,用于检测 checkMemoriesFileChanges 是否修复了键缺失
2018
+ // (路径规范化不一致导致 bound_files 与 file_md5_map 键不匹配时的静默修正)
2019
+ const listableMd5Snapshots = new Map();
2020
+ for (const m of listableMemories) {
2021
+ if (m.bound_files && m.bound_files.length > 0) {
2022
+ listableMd5Snapshots.set(m.id, JSON.stringify(m.file_md5_map ?? {}));
2023
+ }
2024
+ }
1971
2025
  const listableChanges = await this.checkMemoriesFileChanges(listableMemories);
1972
2026
  const newListableChanges = listableChanges.filter(c => {
1973
2027
  const memory = listableMemories.find(m => m.id === c.memoryId);
@@ -1982,6 +2036,36 @@ export class McpServer {
1982
2036
  });
1983
2037
  logger.info(`[McpServer] 文件变更检测 Step2: 检测到变更=${listableChanges.length}, 去重后=${newListableChanges.length}` +
1984
2038
  (listableChanges.length > 0 ? ` 变更项: ${listableChanges.map(c => `${c.memoryUri}(${c.changedFiles.join(',')})`).join('; ')}` : ''));
2039
+ // 回写被修复的 md5 map(键缺失修正)到服务端,避免后续轮次因键不匹配反复误判。
2040
+ // 仅回写未发生真实变更的 memory(发生变更的已在下方 claim/通知流程中处理)。
2041
+ const repairedListableMemories = listableMemories.filter(m => {
2042
+ if (!m.bound_files || m.bound_files.length === 0)
2043
+ return false;
2044
+ if (listableChanges.some(c => c.memoryId === m.id))
2045
+ return false;
2046
+ const before = listableMd5Snapshots.get(m.id);
2047
+ const after = JSON.stringify(m.file_md5_map ?? {});
2048
+ return before !== after;
2049
+ });
2050
+ if (repairedListableMemories.length > 0) {
2051
+ logger.info(`[McpServer] 文件变更检测 Step2: 回写 ${repairedListableMemories.length} 个 listable memory 的修正 MD5 map`);
2052
+ for (const m of repairedListableMemories) {
2053
+ try {
2054
+ await this.memoryStore.updateMemory({
2055
+ uri: m.uri,
2056
+ memory_type: m.memory_type,
2057
+ file_md5_map: m.file_md5_map,
2058
+ last_check_time: m.last_check_time,
2059
+ });
2060
+ if (m.file_md5_map) {
2061
+ this.listableMd5Cache.set(m.id, m.file_md5_map);
2062
+ }
2063
+ }
2064
+ catch (e) {
2065
+ logger.info(`[McpServer] 回写 listable MD5 map 失败: ${m.uri}`, e);
2066
+ }
2067
+ }
2068
+ }
1985
2069
  if (newListableChanges.length > 0) {
1986
2070
  for (const c of newListableChanges) {
1987
2071
  const memory = listableMemories.find(m => m.id === c.memoryId);
@@ -2017,12 +2101,24 @@ export class McpServer {
2017
2101
  logger.info(`[McpServer] 文件变更检测 Step3: project_memory 总数=${permanentList.length}, ` +
2018
2102
  `有绑定文件=${permanentTotalWithFiles.length}, 待检测(过滤updating/claimed后)=${permanentMemoriesWithFiles.length}。` +
2019
2103
  `被过滤原因: ${permanentTotalWithFiles.length - permanentMemoriesWithFiles.length} 项(memory_status=updating 或 isClaimCached)`);
2104
+ // 捕获检测前的 md5 map 快照,用于检测 checkMemoriesFileChanges 是否修复了无基线/键缺失
2105
+ const permanentMd5Snapshots = new Map();
2106
+ for (const m of permanentMemoriesWithFiles) {
2107
+ permanentMd5Snapshots.set(m.id, JSON.stringify(m.file_md5_map ?? {}));
2108
+ }
2020
2109
  const permanentChanges = await this.checkMemoriesFileChanges(permanentMemoriesWithFiles);
2021
- // 回写无基线 memory 的新 MD5 到服务端(修复前创建的旧数据补基线)
2022
- const baselineUpdatedMemories = permanentMemoriesWithFiles.filter(m => m.file_md5_map && Object.keys(m.file_md5_map).length > 0
2023
- && !permanentChanges.some(c => c.memoryId === m.id));
2110
+ // 回写被修复的 md5 map(无基线建基线或键缺失修正)到服务端。
2111
+ // 通过快照对比精确识别被修正的 memory,避免每轮回写所有未变更记录。
2112
+ const baselineUpdatedMemories = permanentMemoriesWithFiles.filter(m => {
2113
+ if (permanentChanges.some(c => c.memoryId === m.id))
2114
+ return false;
2115
+ const before = permanentMd5Snapshots.get(m.id);
2116
+ const after = JSON.stringify(m.file_md5_map ?? {});
2117
+ return before !== after
2118
+ && m.file_md5_map && Object.keys(m.file_md5_map).length > 0;
2119
+ });
2024
2120
  if (baselineUpdatedMemories.length > 0) {
2025
- logger.info(`[McpServer] 文件变更检测 Step3: 回写 ${baselineUpdatedMemories.length} 个无基线 memory MD5 基线到服务端`);
2121
+ logger.info(`[McpServer] 文件变更检测 Step3: 回写 ${baselineUpdatedMemories.length} memory 的修正 MD5 map 到服务端(无基线/键缺失修复)`);
2026
2122
  for (const m of baselineUpdatedMemories) {
2027
2123
  try {
2028
2124
  await this.memoryStore.updateMemory({
@@ -2064,6 +2160,7 @@ export class McpServer {
2064
2160
  if (memory) {
2065
2161
  // claim 状态由 batchClaimUpdate 在服务端原子写入;此处仅回写新 MD5 基线到服务端角色级 scope,
2066
2162
  // 显式传 memory_status:updating/updating_agent_id 防止 Java updateMemory auto-release 误释放 claim。
2163
+ // bound_files 同步回写:checkMemoriesFileChanges 可能已修剪掉消失的文件路径(消除悬空引用)
2067
2164
  await this.memoryStore.updateMemory({
2068
2165
  uri: memory.uri,
2069
2166
  memory_type: 'project_memory',
@@ -2071,6 +2168,7 @@ export class McpServer {
2071
2168
  updating_agent_id: agentId,
2072
2169
  file_md5_map: memory.file_md5_map,
2073
2170
  last_check_time: memory.last_check_time,
2171
+ bound_files: memory.bound_files,
2074
2172
  });
2075
2173
  // 加入去重缓存(lease 有效期内不再重复 claim)
2076
2174
  this.claimedCache.set(memory.uri, Date.now() + McpServer.LEASE_DURATION_MINUTES * 60 * 1000);
@@ -2107,46 +2205,6 @@ export class McpServer {
2107
2205
  return null;
2108
2206
  }
2109
2207
  }
2110
- /**
2111
- * Step 0: 同步 Server 的 claim 状态到本地 roleMemoryStore (v1.21.0)。
2112
- * 查询 Server 上当前 agent 持有的 updating 记忆 URI 列表,
2113
- * 本地中标记为 updating 但 Server 不持有的 → 重置为 normal。
2114
- * 处理场景:agent crash 后重启,Server 租约已过期被清理,但本地 JSON 仍卡在 updating。
2115
- */
2116
- async syncClaimStatusFromServer(agentId) {
2117
- // 互斥锁:防止后台定时器与 poll 路径并发执行导致 TOCTOU
2118
- const previous = this.syncClaimMutex;
2119
- let release;
2120
- this.syncClaimMutex = new Promise((resolve) => { release = resolve; });
2121
- await previous;
2122
- try {
2123
- // 从 Server 查询当前 agent 持有的 claimed URIs
2124
- const serverClaimedUris = await this.memoryStore.getMyClaimedUris();
2125
- const serverClaimedSet = new Set(serverClaimedUris);
2126
- // 读取本地 roleMemoryStore 中所有 project_memory
2127
- const localMemories = await this.roleMemoryStore.listByType('project_memory');
2128
- const staleLocalClaims = localMemories.filter(m => m.memory_status === 'updating'
2129
- && m.updating_agent_id === agentId
2130
- && !serverClaimedSet.has(m.uri));
2131
- if (staleLocalClaims.length > 0) {
2132
- logger.info(`[McpServer] Step 0: 清理 ${staleLocalClaims.length} 条本地过期 claim(Server 已释放)`);
2133
- for (const m of staleLocalClaims) {
2134
- await this.roleMemoryStore.updateMemory({
2135
- uri: m.uri,
2136
- memory_type: 'project_memory',
2137
- });
2138
- // 不传 memory_status → MemoryStore 默认重置为 normal
2139
- }
2140
- }
2141
- }
2142
- catch (e) {
2143
- // Server 不可用时降级,依赖本地状态
2144
- logger.warn('[McpServer] Step 0: 同步 claim 状态失败(降级): %s', e instanceof Error ? e.message : String(e));
2145
- }
2146
- finally {
2147
- release();
2148
- }
2149
- }
2150
2208
  /**
2151
2209
  * 检查指定 URI 是否在 claim 去重缓存内 (v1.21.0)。
2152
2210
  * 在 lease 有效期内不再重复 claim。
@@ -2177,6 +2235,8 @@ export class McpServer {
2177
2235
  const oldMd5Map = memory.file_md5_map || {};
2178
2236
  const workspacePath = process.env.AWS_WORKSPACE_PATH || process.cwd();
2179
2237
  const hasNoBaseline = Object.keys(oldMd5Map).length === 0;
2238
+ // 标记本轮是否修复了单文件基线(键缺失/空值),用于回写修正后的 map 但不计入变更
2239
+ let baselineRepaired = false;
2180
2240
  for (const filePath of memory.bound_files) {
2181
2241
  try {
2182
2242
  const currentMd5 = await this.calculateFileMd5(filePath);
@@ -2184,28 +2244,39 @@ export class McpServer {
2184
2244
  // 文件不存在或无法读取
2185
2245
  const resolvedPath = path.resolve(workspacePath, filePath);
2186
2246
  logger.info(`[McpServer] MD5 计算返回 null: ${filePath} -> ${resolvedPath} (workspace=${workspacePath})`);
2187
- if (!hasNoBaseline) {
2188
- // 已有基线但文件读不到 → 视为变更(文件被删除/移动)
2247
+ const oldMd5ForMissing = oldMd5Map[filePath];
2248
+ if (oldMd5ForMissing) {
2249
+ // 该文件曾有基线,现在读不到 -> 视为变更(文件被删除/移动)
2189
2250
  changedFiles.push(`${filePath} (文件不存在或无法访问)`);
2251
+ // 收口:文件已消失,从 bound_files 移除该路径,消除悬空引用。
2252
+ // 否则 listByFile/findByBoundFile 会持续命中无效记录,且文件恢复后因基线已清而变更检测失效。
2253
+ // bound_files 修剪结果由调用方在变更回写分支同步持久化(见 buildMemoryFileChangeNotification)。
2254
+ if (memory.bound_files) {
2255
+ memory.bound_files = memory.bound_files.filter(f => f !== filePath);
2256
+ }
2190
2257
  }
2258
+ // 无该文件基线时静默跳过,避免对未知状态误报
2191
2259
  continue;
2192
2260
  }
2193
2261
  newMd5Map[filePath] = currentMd5;
2194
2262
  const oldMd5 = oldMd5Map[filePath];
2195
- if (!oldMd5 || oldMd5 !== currentMd5) {
2196
- if (hasNoBaseline) {
2197
- // 无基线(修复前创建的旧数据或新建未计算 MD5):静默建立基线,不视为变更
2198
- logger.info(`[McpServer] 建立基线: ${filePath} new=${currentMd5} (workspace=${workspacePath}, memory=${memory.uri})`);
2199
- }
2200
- else {
2201
- logger.info(`[McpServer] 文件变更: ${filePath} old=${oldMd5} new=${currentMd5} (workspace=${workspacePath})`);
2202
- changedFiles.push(filePath);
2203
- }
2263
+ if (!oldMd5) {
2264
+ // 该文件无可用基线(键缺失或空值)。常见原因:
2265
+ // 1) 新增到 bound_files 的文件尚未建基线;
2266
+ // 2) 历史 TS/Java 路径规范化不一致(如 Windows 反斜杠)导致键不匹配。
2267
+ // 静默建立基线,不视为变更,避免 md5 实际未变却误报需要更新。
2268
+ baselineRepaired = true;
2269
+ logger.info(`[McpServer] 建立单文件基线: ${filePath} new=${currentMd5} (workspace=${workspacePath}, memory=${memory.uri})`);
2270
+ }
2271
+ else if (oldMd5 !== currentMd5) {
2272
+ logger.info(`[McpServer] 文件变更: ${filePath} old=${oldMd5} new=${currentMd5} (workspace=${workspacePath})`);
2273
+ changedFiles.push(filePath);
2204
2274
  }
2205
2275
  }
2206
2276
  catch (error) {
2207
2277
  logger.info(`[McpServer] 计算文件 MD5 失败: ${filePath}`, error);
2208
- if (!hasNoBaseline) {
2278
+ const oldMd5ForError = oldMd5Map[filePath];
2279
+ if (oldMd5ForError) {
2209
2280
  changedFiles.push(`${filePath} (MD5计算失败)`);
2210
2281
  }
2211
2282
  }
@@ -2216,6 +2287,12 @@ export class McpServer {
2216
2287
  memory.last_check_time = new Date().toISOString();
2217
2288
  continue; // 不 push 到 changes
2218
2289
  }
2290
+ // 键缺失修复:map 非空但部分文件缺少基线(路径规范化不一致或新增文件)。
2291
+ // 回写修正后的 map 供调用方持久化,但不计入变更,避免误报。
2292
+ if (baselineRepaired && Object.keys(newMd5Map).length > 0) {
2293
+ memory.file_md5_map = newMd5Map;
2294
+ memory.last_check_time = new Date().toISOString();
2295
+ }
2219
2296
  if (changedFiles.length > 0) {
2220
2297
  changes.push({
2221
2298
  memoryId: memory.id,