openclaw-cortex-memory 0.1.0-Alpha.27 → 0.1.0-Alpha.28
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/README.md +7 -7
- package/dist/openclaw.plugin.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +13 -11
- package/schema/graph.schema.yaml +175 -0
- package/scripts/repair-memory.js +321 -0
package/README.md
CHANGED
|
@@ -109,16 +109,16 @@ openclaw plugins enable openclaw-cortex-memory
|
|
|
109
109
|
如果 `clawhub:` 安装出现 `fetch failed`,可改用 npm 包本地安装(推荐兜底):
|
|
110
110
|
|
|
111
111
|
```bash
|
|
112
|
-
bash -lc 'set -e; TMP="$(mktemp -d)"; cd "$TMP"; npm pack openclaw-cortex-memory@0.1.0-Alpha.
|
|
112
|
+
bash -lc 'set -e; TMP="$(mktemp -d)"; cd "$TMP"; npm pack openclaw-cortex-memory@0.1.0-Alpha.28 >/dev/null; PKG="$(ls openclaw-cortex-memory-*.tgz | head -n1)"; openclaw plugins install "$TMP/$PKG"; openclaw plugins enable openclaw-cortex-memory'
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
也可分步执行(便于排错):
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
npm pack openclaw-cortex-memory@0.1.0-Alpha.
|
|
119
|
-
openclaw plugins install ./openclaw-cortex-memory-0.1.0-Alpha.
|
|
118
|
+
npm pack openclaw-cortex-memory@0.1.0-Alpha.28
|
|
119
|
+
openclaw plugins install ./openclaw-cortex-memory-0.1.0-Alpha.28.tgz
|
|
120
120
|
openclaw plugins enable openclaw-cortex-memory
|
|
121
|
-
rm ./openclaw-cortex-memory-0.1.0-Alpha.
|
|
121
|
+
rm ./openclaw-cortex-memory-0.1.0-Alpha.28.tgz
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
完成安装后,请先按下方“最小配置”示例配置 `openclaw.json`,确认配置无误后再启动 gateway。
|
|
@@ -127,10 +127,10 @@ rm ./openclaw-cortex-memory-0.1.0-Alpha.27.tgz
|
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
129
|
rm -r ~/.openclaw/extensions/openclaw-cortex-memory
|
|
130
|
-
npm pack openclaw-cortex-memory@0.1.0-Alpha.
|
|
131
|
-
openclaw plugins install ./openclaw-cortex-memory-0.1.0-Alpha.
|
|
130
|
+
npm pack openclaw-cortex-memory@0.1.0-Alpha.28
|
|
131
|
+
openclaw plugins install ./openclaw-cortex-memory-0.1.0-Alpha.28.tgz
|
|
132
132
|
openclaw plugins enable openclaw-cortex-memory
|
|
133
|
-
rm ./openclaw-cortex-memory-0.1.0-Alpha.
|
|
133
|
+
rm ./openclaw-cortex-memory-0.1.0-Alpha.28.tgz
|
|
134
134
|
openclaw plugins list
|
|
135
135
|
openclaw gateway restart
|
|
136
136
|
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-cortex-memory",
|
|
3
3
|
"name": "Cortex Memory",
|
|
4
|
-
"version": "0.1.0-Alpha.
|
|
4
|
+
"version": "0.1.0-Alpha.28",
|
|
5
5
|
"description": "Long-term memory system with semantic, episodic, and procedural memory for AI Agents",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"author": "deki18",
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-cortex-memory",
|
|
3
3
|
"name": "Cortex Memory",
|
|
4
|
-
"version": "0.1.0-Alpha.
|
|
4
|
+
"version": "0.1.0-Alpha.28",
|
|
5
5
|
"description": "Long-term memory system with semantic, episodic, and procedural memory for AI Agents",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"author": "deki18",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-cortex-memory",
|
|
3
|
-
"version": "0.1.0-Alpha.
|
|
3
|
+
"version": "0.1.0-Alpha.28",
|
|
4
4
|
"description": "Long-term memory system for OpenClaw AI Agent",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
"dist/",
|
|
53
53
|
"scripts/cli.js",
|
|
54
54
|
"scripts/uninstall.js",
|
|
55
|
+
"scripts/repair-memory.js",
|
|
56
|
+
"schema/",
|
|
55
57
|
"openclaw.plugin.json",
|
|
56
58
|
"SKILL.md"
|
|
57
59
|
],
|
|
@@ -85,16 +87,16 @@
|
|
|
85
87
|
"reranker API endpoint (/rerank)"
|
|
86
88
|
]
|
|
87
89
|
},
|
|
88
|
-
"build": {
|
|
89
|
-
"openclawVersion": "2026.4.5",
|
|
90
|
-
"pluginSdkVersion": "2026.4.5"
|
|
91
|
-
},
|
|
92
|
-
"install": {
|
|
93
|
-
"minHostVersion": ">=2026.4.5"
|
|
94
|
-
},
|
|
95
|
-
"compat": {
|
|
96
|
-
"pluginApi": ">=2026.4.5",
|
|
97
|
-
"minGatewayVersion": "2026.4.5"
|
|
90
|
+
"build": {
|
|
91
|
+
"openclawVersion": "2026.4.5",
|
|
92
|
+
"pluginSdkVersion": "2026.4.5"
|
|
93
|
+
},
|
|
94
|
+
"install": {
|
|
95
|
+
"minHostVersion": ">=2026.4.5"
|
|
96
|
+
},
|
|
97
|
+
"compat": {
|
|
98
|
+
"pluginApi": ">=2026.4.5",
|
|
99
|
+
"minGatewayVersion": "2026.4.5"
|
|
98
100
|
},
|
|
99
101
|
"extensions": [
|
|
100
102
|
"./dist/index.js"
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"eventTypes": [
|
|
3
|
+
"decision",
|
|
4
|
+
"issue",
|
|
5
|
+
"fix",
|
|
6
|
+
"preference",
|
|
7
|
+
"plan",
|
|
8
|
+
"risk",
|
|
9
|
+
"insight",
|
|
10
|
+
"action_item",
|
|
11
|
+
"conversation_summary",
|
|
12
|
+
"constraint",
|
|
13
|
+
"requirement",
|
|
14
|
+
"milestone",
|
|
15
|
+
"blocker",
|
|
16
|
+
"dependency",
|
|
17
|
+
"assumption",
|
|
18
|
+
"retrospective",
|
|
19
|
+
"follow_up"
|
|
20
|
+
],
|
|
21
|
+
"eventTypeAliases": {
|
|
22
|
+
"problem": "issue",
|
|
23
|
+
"error": "issue",
|
|
24
|
+
"bug": "issue",
|
|
25
|
+
"solution": "fix",
|
|
26
|
+
"workaround": "fix",
|
|
27
|
+
"todo": "action_item",
|
|
28
|
+
"next_step": "action_item",
|
|
29
|
+
"limitation": "constraint",
|
|
30
|
+
"guardrail": "constraint",
|
|
31
|
+
"spec": "requirement",
|
|
32
|
+
"acceptance_criteria": "requirement",
|
|
33
|
+
"deadline": "milestone",
|
|
34
|
+
"target": "milestone",
|
|
35
|
+
"roadblock": "blocker",
|
|
36
|
+
"stuck": "blocker",
|
|
37
|
+
"depends_on": "dependency",
|
|
38
|
+
"upstream": "dependency",
|
|
39
|
+
"hypothesis": "assumption",
|
|
40
|
+
"lesson": "retrospective",
|
|
41
|
+
"postmortem": "retrospective",
|
|
42
|
+
"followup": "follow_up",
|
|
43
|
+
"next_action": "follow_up"
|
|
44
|
+
},
|
|
45
|
+
"entityTypes": [
|
|
46
|
+
"Person",
|
|
47
|
+
"FamilyMember",
|
|
48
|
+
"Friend",
|
|
49
|
+
"Team",
|
|
50
|
+
"Project",
|
|
51
|
+
"Task",
|
|
52
|
+
"Plan",
|
|
53
|
+
"Milestone",
|
|
54
|
+
"Location",
|
|
55
|
+
"Event",
|
|
56
|
+
"Schedule",
|
|
57
|
+
"Habit",
|
|
58
|
+
"HealthItem",
|
|
59
|
+
"FinanceItem",
|
|
60
|
+
"Issue",
|
|
61
|
+
"Fix",
|
|
62
|
+
"Decision",
|
|
63
|
+
"Action",
|
|
64
|
+
"Risk",
|
|
65
|
+
"Blocker",
|
|
66
|
+
"Assumption",
|
|
67
|
+
"Concept",
|
|
68
|
+
"Resource",
|
|
69
|
+
"Document"
|
|
70
|
+
],
|
|
71
|
+
"entityAliases": {
|
|
72
|
+
"OpenClaw": ["openclaw", "插件", "该项目", "本项目"],
|
|
73
|
+
"FamilyMember": ["家人", "家庭成员", "亲人"],
|
|
74
|
+
"Friend": ["朋友", "好友"],
|
|
75
|
+
"Location": ["地点", "位置", "住址", "地址"],
|
|
76
|
+
"Event": ["活动", "事情", "事项"],
|
|
77
|
+
"Schedule": ["日程", "安排", "计划表"],
|
|
78
|
+
"Habit": ["习惯", "作息"],
|
|
79
|
+
"HealthItem": ["健康", "体检", "药物", "锻炼"],
|
|
80
|
+
"FinanceItem": ["账单", "支出", "收入", "预算"],
|
|
81
|
+
"Person": ["我", "自己", "本人", "同事", "客户", "用户"],
|
|
82
|
+
"Project": ["项目", "工程"],
|
|
83
|
+
"Task": ["任务", "待办", "todo"],
|
|
84
|
+
"Milestone": ["里程碑", "节点"],
|
|
85
|
+
"Issue": ["问题", "故障", "报错"],
|
|
86
|
+
"Fix": ["修复", "解决方案"]
|
|
87
|
+
},
|
|
88
|
+
"relationTypes": [
|
|
89
|
+
"depends_on",
|
|
90
|
+
"blocks",
|
|
91
|
+
"related_to",
|
|
92
|
+
"causes",
|
|
93
|
+
"resolves",
|
|
94
|
+
"plans_to",
|
|
95
|
+
"scheduled_for",
|
|
96
|
+
"lives_in",
|
|
97
|
+
"cares_for",
|
|
98
|
+
"pays_for",
|
|
99
|
+
"supports",
|
|
100
|
+
"conflicts_with",
|
|
101
|
+
"belongs_to",
|
|
102
|
+
"owned_by",
|
|
103
|
+
"references",
|
|
104
|
+
"prefers",
|
|
105
|
+
"implements",
|
|
106
|
+
"requires"
|
|
107
|
+
],
|
|
108
|
+
"relationTypeAliases": {
|
|
109
|
+
"dependency": "depends_on",
|
|
110
|
+
"blocked_by": "blocks",
|
|
111
|
+
"linked_to": "related_to",
|
|
112
|
+
"plan_to": "plans_to",
|
|
113
|
+
"schedule_for": "scheduled_for",
|
|
114
|
+
"located_in": "lives_in",
|
|
115
|
+
"care_for": "cares_for",
|
|
116
|
+
"pay_for": "pays_for",
|
|
117
|
+
"support": "supports",
|
|
118
|
+
"conflict_with": "conflicts_with",
|
|
119
|
+
"依赖于": "depends_on",
|
|
120
|
+
"依赖": "depends_on",
|
|
121
|
+
"取决于": "depends_on",
|
|
122
|
+
"阻塞": "blocks",
|
|
123
|
+
"卡住": "blocks",
|
|
124
|
+
"导致": "causes",
|
|
125
|
+
"引起": "causes",
|
|
126
|
+
"解决": "resolves",
|
|
127
|
+
"修复": "resolves",
|
|
128
|
+
"属于": "belongs_to",
|
|
129
|
+
"归属": "belongs_to",
|
|
130
|
+
"负责": "owned_by",
|
|
131
|
+
"由": "owned_by",
|
|
132
|
+
"参考": "references",
|
|
133
|
+
"引用": "references",
|
|
134
|
+
"偏好": "prefers",
|
|
135
|
+
"更喜欢": "prefers",
|
|
136
|
+
"实现": "implements",
|
|
137
|
+
"需要": "requires",
|
|
138
|
+
"计划做": "plans_to",
|
|
139
|
+
"打算": "plans_to",
|
|
140
|
+
"安排在": "scheduled_for",
|
|
141
|
+
"约在": "scheduled_for",
|
|
142
|
+
"住在": "lives_in",
|
|
143
|
+
"居住在": "lives_in",
|
|
144
|
+
"照顾": "cares_for",
|
|
145
|
+
"看护": "cares_for",
|
|
146
|
+
"支付": "pays_for",
|
|
147
|
+
"付款": "pays_for",
|
|
148
|
+
"支持": "supports",
|
|
149
|
+
"冲突": "conflicts_with",
|
|
150
|
+
"矛盾": "conflicts_with",
|
|
151
|
+
"相关": "related_to",
|
|
152
|
+
"有关": "related_to",
|
|
153
|
+
"belongs": "belongs_to",
|
|
154
|
+
"owner_of": "owned_by",
|
|
155
|
+
"refer_to": "references",
|
|
156
|
+
"preference_for": "prefers",
|
|
157
|
+
"implement": "implements",
|
|
158
|
+
"need": "requires"
|
|
159
|
+
},
|
|
160
|
+
"relationRules": [
|
|
161
|
+
{ "type": "depends_on", "fromTypes": ["Task", "Plan", "Milestone"], "toTypes": ["Task", "Plan", "Milestone"], "allowSelfLoop": false },
|
|
162
|
+
{ "type": "blocks", "fromTypes": ["Issue", "Task", "Risk"], "toTypes": ["Task", "Plan"], "allowSelfLoop": false },
|
|
163
|
+
{ "type": "causes", "fromTypes": ["Issue", "Risk", "Assumption"], "toTypes": ["Issue", "Risk"], "allowSelfLoop": false },
|
|
164
|
+
{ "type": "resolves", "fromTypes": ["Fix", "Decision", "Action"], "toTypes": ["Issue", "Blocker"], "allowSelfLoop": false },
|
|
165
|
+
{ "type": "belongs_to", "fromTypes": ["Task", "Issue", "Fix", "Decision"], "toTypes": ["Project", "Plan", "Milestone"], "allowSelfLoop": false },
|
|
166
|
+
{ "type": "owned_by", "fromTypes": ["Task", "Plan", "Project", "Issue"], "toTypes": ["Person", "Team"], "allowSelfLoop": false }
|
|
167
|
+
],
|
|
168
|
+
"highValueRelationTypes": ["depends_on", "blocks", "resolves", "owned_by"],
|
|
169
|
+
"relatedToMaxRatio": 0.35,
|
|
170
|
+
"relatedToMaxAbsolute": 2,
|
|
171
|
+
"minRelationConfidence": 0.35,
|
|
172
|
+
"evidenceSpanRequired": true,
|
|
173
|
+
"endpointMentionRequired": true,
|
|
174
|
+
"defaultEntityType": "Concept"
|
|
175
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const isDryRun = args.includes('--dry-run');
|
|
8
|
+
const isFix = args.includes('--fix');
|
|
9
|
+
const targetArg = args.find(a => a.startsWith('--target='));
|
|
10
|
+
const target = targetArg ? targetArg.split('=')[1] : 'all';
|
|
11
|
+
|
|
12
|
+
const PROJECT_ROOT = process.cwd();
|
|
13
|
+
const MEMORY_ROOT = path.join(PROJECT_ROOT, 'data', 'memory');
|
|
14
|
+
|
|
15
|
+
const VALID_TARGETS = ['all', 'archive', 'active', 'vector'];
|
|
16
|
+
|
|
17
|
+
function printUsage() {
|
|
18
|
+
console.log(`
|
|
19
|
+
Memory Data Repair Tool
|
|
20
|
+
|
|
21
|
+
Usage: node scripts/repair-memory.js [options]
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--dry-run Scan and report issues without making changes
|
|
25
|
+
--fix Remove invalid records and create quarantine file
|
|
26
|
+
--target=<target> Specify target: all, archive, active, vector (default: all)
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
node scripts/repair-memory.js --dry-run
|
|
30
|
+
node scripts/repair-memory.js --fix --target=archive
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!VALID_TARGETS.includes(target)) {
|
|
35
|
+
console.error(`Invalid target: ${target}. Valid targets: ${VALID_TARGETS.join(', ')}`);
|
|
36
|
+
printUsage();
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!isDryRun && !isFix) {
|
|
41
|
+
printUsage();
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function validateJsonlLine(line, lineNumber) {
|
|
46
|
+
const errors = [];
|
|
47
|
+
if (!line || !line.trim()) {
|
|
48
|
+
return { valid: true, errors: [], record: null };
|
|
49
|
+
}
|
|
50
|
+
let record;
|
|
51
|
+
try {
|
|
52
|
+
record = JSON.parse(line);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
errors.push(`JSON parse error: ${e.message}`);
|
|
55
|
+
return { valid: false, errors, record: null };
|
|
56
|
+
}
|
|
57
|
+
if (!record || typeof record !== 'object') {
|
|
58
|
+
errors.push('Record is not an object');
|
|
59
|
+
return { valid: false, errors, record: null };
|
|
60
|
+
}
|
|
61
|
+
if (typeof record.id !== 'string' || !record.id.trim()) {
|
|
62
|
+
errors.push('Missing or invalid id field');
|
|
63
|
+
}
|
|
64
|
+
if (typeof record.timestamp !== 'string' || !record.timestamp.trim()) {
|
|
65
|
+
errors.push('Missing or invalid timestamp field');
|
|
66
|
+
}
|
|
67
|
+
if (record.layer !== 'active' && record.layer !== 'archive') {
|
|
68
|
+
errors.push('Missing or invalid layer field');
|
|
69
|
+
}
|
|
70
|
+
const anomalyPatterns = [
|
|
71
|
+
/\d+\.\d+,\s*"[^"]+"/,
|
|
72
|
+
/"[^"]+"\s+\d+\.\d+/,
|
|
73
|
+
/,\s*\d+\.\d+,/,
|
|
74
|
+
/"\w+\.\w+\.\w+"/,
|
|
75
|
+
/\d+\.\w+\.\d+/,
|
|
76
|
+
];
|
|
77
|
+
const lineStr = JSON.stringify(record);
|
|
78
|
+
for (const pattern of anomalyPatterns) {
|
|
79
|
+
if (pattern.test(lineStr)) {
|
|
80
|
+
errors.push('Anomaly pattern detected in record');
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { valid: errors.length === 0, errors, record };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function scanJsonlFile(filePath) {
|
|
88
|
+
const results = {
|
|
89
|
+
path: filePath,
|
|
90
|
+
exists: false,
|
|
91
|
+
totalLines: 0,
|
|
92
|
+
validLines: 0,
|
|
93
|
+
invalidLines: 0,
|
|
94
|
+
emptyLines: 0,
|
|
95
|
+
issues: [],
|
|
96
|
+
};
|
|
97
|
+
if (!fs.existsSync(filePath)) {
|
|
98
|
+
return results;
|
|
99
|
+
}
|
|
100
|
+
results.exists = true;
|
|
101
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
102
|
+
const lines = content.split(/\r?\n/);
|
|
103
|
+
for (let i = 0; i < lines.length; i++) {
|
|
104
|
+
const line = lines[i];
|
|
105
|
+
if (!line.trim()) {
|
|
106
|
+
results.emptyLines++;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
results.totalLines++;
|
|
110
|
+
const validation = validateJsonlLine(line, i + 1);
|
|
111
|
+
if (validation.valid) {
|
|
112
|
+
results.validLines++;
|
|
113
|
+
} else {
|
|
114
|
+
results.invalidLines++;
|
|
115
|
+
results.issues.push({
|
|
116
|
+
lineNumber: i + 1,
|
|
117
|
+
errors: validation.errors,
|
|
118
|
+
preview: line.slice(0, 100) + (line.length > 100 ? '...' : ''),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return results;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function repairJsonlFile(filePath, dryRun) {
|
|
126
|
+
const results = {
|
|
127
|
+
path: filePath,
|
|
128
|
+
exists: false,
|
|
129
|
+
totalLines: 0,
|
|
130
|
+
validLines: 0,
|
|
131
|
+
removedLines: 0,
|
|
132
|
+
quarantineLines: [],
|
|
133
|
+
};
|
|
134
|
+
if (!fs.existsSync(filePath)) {
|
|
135
|
+
return results;
|
|
136
|
+
}
|
|
137
|
+
results.exists = true;
|
|
138
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
139
|
+
const lines = content.split(/\r?\n/);
|
|
140
|
+
const validRecords = [];
|
|
141
|
+
for (let i = 0; i < lines.length; i++) {
|
|
142
|
+
const line = lines[i];
|
|
143
|
+
if (!line.trim()) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
results.totalLines++;
|
|
147
|
+
const validation = validateJsonlLine(line, i + 1);
|
|
148
|
+
if (validation.valid) {
|
|
149
|
+
results.validLines++;
|
|
150
|
+
validRecords.push(line);
|
|
151
|
+
} else {
|
|
152
|
+
results.removedLines++;
|
|
153
|
+
results.quarantineLines.push({
|
|
154
|
+
lineNumber: i + 1,
|
|
155
|
+
content: line,
|
|
156
|
+
errors: validation.errors,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!dryRun && results.removedLines > 0) {
|
|
161
|
+
const newContent = validRecords.join('\n') + (validRecords.length > 0 ? '\n' : '');
|
|
162
|
+
fs.writeFileSync(filePath, newContent, 'utf-8');
|
|
163
|
+
const quarantinePath = filePath + '.quarantine.jsonl';
|
|
164
|
+
const quarantineContent = results.quarantineLines.map(q =>
|
|
165
|
+
JSON.stringify({ lineNumber: q.lineNumber, errors: q.errors, content: q.content })
|
|
166
|
+
).join('\n');
|
|
167
|
+
fs.writeFileSync(quarantinePath, quarantineContent + '\n', 'utf-8');
|
|
168
|
+
}
|
|
169
|
+
return results;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function scanVectorFallback(filePath) {
|
|
173
|
+
const results = {
|
|
174
|
+
path: filePath,
|
|
175
|
+
exists: false,
|
|
176
|
+
totalRecords: 0,
|
|
177
|
+
validRecords: 0,
|
|
178
|
+
orphanRecords: 0,
|
|
179
|
+
issues: [],
|
|
180
|
+
};
|
|
181
|
+
if (!fs.existsSync(filePath)) {
|
|
182
|
+
return results;
|
|
183
|
+
}
|
|
184
|
+
results.exists = true;
|
|
185
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
186
|
+
const lines = content.split(/\r?\n/);
|
|
187
|
+
for (let i = 0; i < lines.length; i++) {
|
|
188
|
+
const line = lines[i];
|
|
189
|
+
if (!line.trim()) continue;
|
|
190
|
+
results.totalRecords++;
|
|
191
|
+
try {
|
|
192
|
+
const record = JSON.parse(line);
|
|
193
|
+
if (!record.id || !record.embedding) {
|
|
194
|
+
results.issues.push({
|
|
195
|
+
lineNumber: i + 1,
|
|
196
|
+
error: 'Missing id or embedding',
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
results.validRecords++;
|
|
200
|
+
}
|
|
201
|
+
} catch (e) {
|
|
202
|
+
results.issues.push({
|
|
203
|
+
lineNumber: i + 1,
|
|
204
|
+
error: `JSON parse error: ${e.message}`,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return results;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log('='.repeat(60));
|
|
212
|
+
console.log('Memory Data Repair Tool');
|
|
213
|
+
console.log('='.repeat(60));
|
|
214
|
+
console.log(`Mode: ${isDryRun ? 'DRY RUN (no changes)' : 'FIX (will modify files)'}`);
|
|
215
|
+
console.log(`Target: ${target}`);
|
|
216
|
+
console.log(`Memory Root: ${MEMORY_ROOT}`);
|
|
217
|
+
console.log('='.repeat(60));
|
|
218
|
+
|
|
219
|
+
const archivePath = path.join(MEMORY_ROOT, 'sessions', 'archive', 'archive.jsonl');
|
|
220
|
+
const activePath = path.join(MEMORY_ROOT, 'sessions', 'active', 'sessions.jsonl');
|
|
221
|
+
const vectorFallbackPath = path.join(MEMORY_ROOT, 'vector', 'lancedb_events.jsonl');
|
|
222
|
+
|
|
223
|
+
let totalIssues = 0;
|
|
224
|
+
const report = {
|
|
225
|
+
archive: null,
|
|
226
|
+
active: null,
|
|
227
|
+
vector: null,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
if (target === 'all' || target === 'archive') {
|
|
231
|
+
console.log('\n[Archive Layer]');
|
|
232
|
+
if (isDryRun) {
|
|
233
|
+
report.archive = scanJsonlFile(archivePath);
|
|
234
|
+
} else {
|
|
235
|
+
report.archive = repairJsonlFile(archivePath, false);
|
|
236
|
+
}
|
|
237
|
+
if (!report.archive.exists) {
|
|
238
|
+
console.log(' File does not exist');
|
|
239
|
+
} else {
|
|
240
|
+
console.log(` Total lines: ${report.archive.totalLines}`);
|
|
241
|
+
console.log(` Valid lines: ${report.archive.validLines}`);
|
|
242
|
+
console.log(` Invalid lines: ${report.archive.invalidLines || report.archive.removedLines}`);
|
|
243
|
+
if (report.archive.issues && report.archive.issues.length > 0) {
|
|
244
|
+
console.log(' Issues found:');
|
|
245
|
+
report.archive.issues.slice(0, 5).forEach(issue => {
|
|
246
|
+
console.log(` Line ${issue.lineNumber}: ${issue.errors.join(', ')}`);
|
|
247
|
+
});
|
|
248
|
+
if (report.archive.issues.length > 5) {
|
|
249
|
+
console.log(` ... and ${report.archive.issues.length - 5} more`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
totalIssues += report.archive.invalidLines || report.archive.removedLines || 0;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (target === 'all' || target === 'active') {
|
|
257
|
+
console.log('\n[Active Layer]');
|
|
258
|
+
if (isDryRun) {
|
|
259
|
+
report.active = scanJsonlFile(activePath);
|
|
260
|
+
} else {
|
|
261
|
+
report.active = repairJsonlFile(activePath, false);
|
|
262
|
+
}
|
|
263
|
+
if (!report.active.exists) {
|
|
264
|
+
console.log(' File does not exist');
|
|
265
|
+
} else {
|
|
266
|
+
console.log(` Total lines: ${report.active.totalLines}`);
|
|
267
|
+
console.log(` Valid lines: ${report.active.validLines}`);
|
|
268
|
+
console.log(` Invalid lines: ${report.active.invalidLines || report.active.removedLines}`);
|
|
269
|
+
if (report.active.issues && report.active.issues.length > 0) {
|
|
270
|
+
console.log(' Issues found:');
|
|
271
|
+
report.active.issues.slice(0, 5).forEach(issue => {
|
|
272
|
+
console.log(` Line ${issue.lineNumber}: ${issue.errors.join(', ')}`);
|
|
273
|
+
});
|
|
274
|
+
if (report.active.issues.length > 5) {
|
|
275
|
+
console.log(` ... and ${report.active.issues.length - 5} more`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
totalIssues += report.active.invalidLines || report.active.removedLines || 0;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (target === 'all' || target === 'vector') {
|
|
283
|
+
console.log('\n[Vector Fallback]');
|
|
284
|
+
report.vector = scanVectorFallback(vectorFallbackPath);
|
|
285
|
+
if (!report.vector.exists) {
|
|
286
|
+
console.log(' File does not exist');
|
|
287
|
+
} else {
|
|
288
|
+
console.log(` Total records: ${report.vector.totalRecords}`);
|
|
289
|
+
console.log(` Valid records: ${report.vector.validRecords}`);
|
|
290
|
+
console.log(` Issues: ${report.vector.issues.length}`);
|
|
291
|
+
if (report.vector.issues.length > 0) {
|
|
292
|
+
console.log(' Issues found:');
|
|
293
|
+
report.vector.issues.slice(0, 5).forEach(issue => {
|
|
294
|
+
console.log(` Line ${issue.lineNumber}: ${issue.error}`);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
totalIssues += report.vector.issues.length;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log('\n' + '='.repeat(60));
|
|
302
|
+
console.log('Summary');
|
|
303
|
+
console.log('='.repeat(60));
|
|
304
|
+
console.log(`Total issues found: ${totalIssues}`);
|
|
305
|
+
|
|
306
|
+
if (isDryRun) {
|
|
307
|
+
if (totalIssues > 0) {
|
|
308
|
+
console.log('\nRun with --fix to repair these issues.');
|
|
309
|
+
} else {
|
|
310
|
+
console.log('\nNo issues found. Memory data is healthy.');
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
if (totalIssues > 0) {
|
|
314
|
+
console.log('\nRepair completed. Invalid records have been quarantined.');
|
|
315
|
+
console.log('Check .quarantine.jsonl files for removed records.');
|
|
316
|
+
} else {
|
|
317
|
+
console.log('\nNo repairs needed. Memory data is healthy.');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
process.exit(totalIssues > 0 && isDryRun ? 1 : 0);
|