aws-runtime-bridge 1.9.51 → 1.9.55
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.
- package/package/acode/README.md +26 -0
- package/package/acode/dist/built-in-file-tools.d.ts +11 -0
- package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
- package/package/acode/dist/built-in-file-tools.js +4 -2
- package/package/acode/dist/built-in-symbols-tool.d.ts +7 -0
- package/package/acode/dist/built-in-symbols-tool.d.ts.map +1 -0
- package/package/acode/dist/built-in-symbols-tool.js +117 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-bash.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-c-sharp.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-c.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-cpp.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-go.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-java.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-javascript.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-jsx.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-lua.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-php.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-python.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-ruby.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-rust.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-tsx.wasm +0 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-typescript.wasm +0 -0
- package/package/acode/dist/index.d.ts +7 -0
- package/package/acode/dist/index.d.ts.map +1 -1
- package/package/acode/dist/index.js +8 -0
- package/package/acode/dist/parsers/ast-cache.d.ts +33 -0
- package/package/acode/dist/parsers/ast-cache.d.ts.map +1 -0
- package/package/acode/dist/parsers/ast-cache.js +76 -0
- package/package/acode/dist/parsers/language-detector.d.ts +7 -0
- package/package/acode/dist/parsers/language-detector.d.ts.map +1 -0
- package/package/acode/dist/parsers/language-detector.js +44 -0
- package/package/acode/dist/parsers/language-queries.d.ts +5 -0
- package/package/acode/dist/parsers/language-queries.d.ts.map +1 -0
- package/package/acode/dist/parsers/language-queries.js +640 -0
- package/package/acode/dist/parsers/symbol-extractor.d.ts +8 -0
- package/package/acode/dist/parsers/symbol-extractor.d.ts.map +1 -0
- package/package/acode/dist/parsers/symbol-extractor.js +164 -0
- package/package/acode/dist/parsers/tree-sitter-loader.d.ts +18 -0
- package/package/acode/dist/parsers/tree-sitter-loader.d.ts.map +1 -0
- package/package/acode/dist/parsers/tree-sitter-loader.js +64 -0
- package/package/acode/dist/parsers/types.d.ts +61 -0
- package/package/acode/dist/parsers/types.d.ts.map +1 -0
- package/package/acode/dist/parsers/types.js +7 -0
- package/package/acode/dist/runtime.d.ts.map +1 -1
- package/package/acode/dist/runtime.js +75 -20
- package/package/acode/dist/sub-agent-manager.d.ts +16 -0
- package/package/acode/dist/sub-agent-manager.d.ts.map +1 -1
- package/package/acode/dist/sub-agent-manager.js +196 -15
- package/package/acode/dist/types.d.ts +1 -1
- package/package/acode/dist/types.d.ts.map +1 -1
- package/package/acode/package.json +4 -1
- package/package/aws-client-agent-mcp/dist/agent-client.test.js +64 -50
- package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +3 -12
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +192 -115
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +38 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.js +7 -7
- package/package/aws-client-agent-mcp/dist/memory-store.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +22 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.js +3 -2
- package/package/aws-client-agent-mcp/dist/memory-tools.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +24 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +61 -2
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +54 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
- package/package.json +5 -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
|
|
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,
|
|
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
|
|
415
|
-
// ACode runtime
|
|
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
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
-
|
|
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
|
-
|
|
664
|
+
// recently_memory / agent_memory:实例级,走服务端
|
|
665
|
+
return await this.memoryStore.readMemory(input);
|
|
661
666
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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
|
-
|
|
670
|
-
|
|
678
|
+
catch {
|
|
679
|
+
// 服务端不可用,降级到本地
|
|
680
|
+
}
|
|
681
|
+
const localLongTerm = await this.roleMemoryStore.readMemory({
|
|
671
682
|
...input,
|
|
672
683
|
memory_type: "project_memory",
|
|
673
684
|
});
|
|
674
|
-
if (
|
|
675
|
-
return
|
|
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
|
-
|
|
689
|
-
//
|
|
690
|
-
//
|
|
699
|
+
// project_memory:服务端为权威源(claim/memory_status/租约均在服务端维护),
|
|
700
|
+
// mutation 必须走服务端,否则本地写入与服务端状态脱节。
|
|
701
|
+
// 本地 roleMemoryStore 仅在“本地存在离线记录但服务端没有”时降级(未同步的离线编辑);
|
|
702
|
+
// 新建(两者均无)必须落到服务端,避免 create 写入本地导致服务端统计/可见性脱节。
|
|
691
703
|
if (input.memory_type === "project_memory") {
|
|
692
|
-
const
|
|
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:
|
|
710
|
+
return { store: this.roleMemoryStore, memory_type: "project_memory" };
|
|
695
711
|
}
|
|
696
712
|
return { store: this.memoryStore, memory_type: "project_memory" };
|
|
697
713
|
}
|
|
698
|
-
|
|
714
|
+
// recently_memory / agent_memory:实例级,走服务端
|
|
715
|
+
return { store: this.memoryStore, memory_type: input.memory_type };
|
|
699
716
|
}
|
|
700
|
-
|
|
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 (
|
|
705
|
-
return { store: this.
|
|
722
|
+
}).catch(() => null);
|
|
723
|
+
if (serverLongTerm) {
|
|
724
|
+
return { store: this.memoryStore, memory_type: "project_memory" };
|
|
706
725
|
}
|
|
707
|
-
|
|
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 (
|
|
713
|
-
return { store: this.
|
|
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
|
-
|
|
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
|
-
|
|
868
|
-
//
|
|
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
|
-
//
|
|
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
|
|
1139
|
+
* 主流程:只允许 recently_memory/agent_memory,拒绝 project_memory(永久记忆仅通过 search 检索)。
|
|
1140
|
+
* MemoryStore.listByType 内部仍允许 project_memory 供文件变更检测使用,但 MCP 层面不暴露给 Agent。
|
|
1106
1141
|
*/
|
|
1107
1142
|
async handleListMemories(args) {
|
|
1108
|
-
|
|
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
|
-
//
|
|
1862
|
-
//
|
|
1863
|
-
//
|
|
1864
|
-
|
|
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
|
|
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
|
-
//
|
|
2022
|
-
|
|
2023
|
-
|
|
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}
|
|
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
|
-
|
|
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
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
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
|
-
|
|
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,
|