openclaw-cortex-memory 0.1.0-Alpha.3 → 0.1.0-Alpha.31

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 (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +296 -203
  3. package/SIGNATURE.md +7 -0
  4. package/SKILL.md +92 -268
  5. package/dist/index.d.ts +100 -22
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1249 -1252
  8. package/dist/index.js.map +1 -1
  9. package/dist/openclaw.plugin.json +501 -16
  10. package/dist/src/dedup/three_stage_deduplicator.d.ts +25 -0
  11. package/dist/src/dedup/three_stage_deduplicator.d.ts.map +1 -0
  12. package/dist/src/dedup/three_stage_deduplicator.js +224 -0
  13. package/dist/src/dedup/three_stage_deduplicator.js.map +1 -0
  14. package/dist/src/engine/memory_engine.d.ts +6 -1
  15. package/dist/src/engine/memory_engine.d.ts.map +1 -1
  16. package/dist/src/engine/ts_engine.d.ts +242 -0
  17. package/dist/src/engine/ts_engine.d.ts.map +1 -1
  18. package/dist/src/engine/ts_engine.js +1468 -52
  19. package/dist/src/engine/ts_engine.js.map +1 -1
  20. package/dist/src/engine/types.d.ts +29 -0
  21. package/dist/src/engine/types.d.ts.map +1 -1
  22. package/dist/src/graph/ontology.d.ts +125 -0
  23. package/dist/src/graph/ontology.d.ts.map +1 -0
  24. package/dist/src/graph/ontology.js +1237 -0
  25. package/dist/src/graph/ontology.js.map +1 -0
  26. package/dist/src/net/http_post.d.ts +17 -0
  27. package/dist/src/net/http_post.d.ts.map +1 -0
  28. package/dist/src/net/http_post.js +56 -0
  29. package/dist/src/net/http_post.js.map +1 -0
  30. package/dist/src/quality/llm_output_validator.d.ts +66 -0
  31. package/dist/src/quality/llm_output_validator.d.ts.map +1 -0
  32. package/dist/src/quality/llm_output_validator.js +659 -0
  33. package/dist/src/quality/llm_output_validator.js.map +1 -0
  34. package/dist/src/reflect/reflector.d.ts +7 -0
  35. package/dist/src/reflect/reflector.d.ts.map +1 -1
  36. package/dist/src/reflect/reflector.js +352 -8
  37. package/dist/src/reflect/reflector.js.map +1 -1
  38. package/dist/src/rules/rule_store.d.ts.map +1 -1
  39. package/dist/src/rules/rule_store.js +75 -16
  40. package/dist/src/rules/rule_store.js.map +1 -1
  41. package/dist/src/session/session_end.d.ts +33 -0
  42. package/dist/src/session/session_end.d.ts.map +1 -1
  43. package/dist/src/session/session_end.js +67 -64
  44. package/dist/src/session/session_end.js.map +1 -1
  45. package/dist/src/store/archive_store.d.ts +136 -0
  46. package/dist/src/store/archive_store.d.ts.map +1 -0
  47. package/dist/src/store/archive_store.js +635 -0
  48. package/dist/src/store/archive_store.js.map +1 -0
  49. package/dist/src/store/embedding_utils.d.ts +32 -0
  50. package/dist/src/store/embedding_utils.d.ts.map +1 -0
  51. package/dist/src/store/embedding_utils.js +173 -0
  52. package/dist/src/store/embedding_utils.js.map +1 -0
  53. package/dist/src/store/graph_memory_store.d.ts +114 -0
  54. package/dist/src/store/graph_memory_store.d.ts.map +1 -0
  55. package/dist/src/store/graph_memory_store.js +841 -0
  56. package/dist/src/store/graph_memory_store.js.map +1 -0
  57. package/dist/src/store/read_store.d.ts +89 -0
  58. package/dist/src/store/read_store.d.ts.map +1 -1
  59. package/dist/src/store/read_store.js +2459 -28
  60. package/dist/src/store/read_store.js.map +1 -1
  61. package/dist/src/store/vector_store.d.ts +45 -0
  62. package/dist/src/store/vector_store.d.ts.map +1 -0
  63. package/dist/src/store/vector_store.js +202 -0
  64. package/dist/src/store/vector_store.js.map +1 -0
  65. package/dist/src/store/write_store.d.ts +54 -0
  66. package/dist/src/store/write_store.d.ts.map +1 -1
  67. package/dist/src/store/write_store.js +284 -6
  68. package/dist/src/store/write_store.js.map +1 -1
  69. package/dist/src/sync/session_sync.d.ts +119 -2
  70. package/dist/src/sync/session_sync.d.ts.map +1 -1
  71. package/dist/src/sync/session_sync.js +2377 -31
  72. package/dist/src/sync/session_sync.js.map +1 -1
  73. package/dist/src/utils/runtime_env.d.ts +4 -0
  74. package/dist/src/utils/runtime_env.d.ts.map +1 -0
  75. package/dist/src/utils/runtime_env.js +51 -0
  76. package/dist/src/utils/runtime_env.js.map +1 -0
  77. package/dist/src/wiki/wiki_linter.d.ts +25 -0
  78. package/dist/src/wiki/wiki_linter.d.ts.map +1 -0
  79. package/dist/src/wiki/wiki_linter.js +268 -0
  80. package/dist/src/wiki/wiki_linter.js.map +1 -0
  81. package/dist/src/wiki/wiki_logger.d.ts +10 -0
  82. package/dist/src/wiki/wiki_logger.d.ts.map +1 -0
  83. package/dist/src/wiki/wiki_logger.js +78 -0
  84. package/dist/src/wiki/wiki_logger.js.map +1 -0
  85. package/dist/src/wiki/wiki_maintainer.d.ts +36 -0
  86. package/dist/src/wiki/wiki_maintainer.d.ts.map +1 -0
  87. package/dist/src/wiki/wiki_maintainer.js +38 -0
  88. package/dist/src/wiki/wiki_maintainer.js.map +1 -0
  89. package/dist/src/wiki/wiki_projector.d.ts +33 -0
  90. package/dist/src/wiki/wiki_projector.d.ts.map +1 -0
  91. package/dist/src/wiki/wiki_projector.js +633 -0
  92. package/dist/src/wiki/wiki_projector.js.map +1 -0
  93. package/dist/src/wiki/wiki_queue.d.ts +29 -0
  94. package/dist/src/wiki/wiki_queue.d.ts.map +1 -0
  95. package/dist/src/wiki/wiki_queue.js +137 -0
  96. package/dist/src/wiki/wiki_queue.js.map +1 -0
  97. package/openclaw.plugin.json +501 -16
  98. package/package.json +58 -7
  99. package/schema/graph.schema.yaml +330 -0
  100. package/scripts/cli.js +19 -14
  101. package/scripts/repair-memory.js +321 -0
  102. package/scripts/uninstall.js +22 -5
  103. package/skills/cortex-memory/SKILL.md +49 -0
  104. package/skills/cortex-memory/references/agent-manual.md +115 -0
  105. package/skills/cortex-memory/references/configuration.md +92 -0
  106. package/skills/cortex-memory/references/publish-checklist.md +46 -0
  107. package/skills/cortex-memory/references/system-prompt-template.md +27 -0
  108. package/skills/cortex-memory/references/tools.md +181 -0
  109. package/skills/cortex-memory/scripts/smoke-check.ps1 +56 -0
  110. package/index.ts +0 -2142
@@ -0,0 +1,659 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateLlmJsonOutput = validateLlmJsonOutput;
4
+ exports.validateGateDecisionsOutput = validateGateDecisionsOutput;
5
+ exports.validateArchiveEvent = validateArchiveEvent;
6
+ exports.validateJsonlLine = validateJsonlLine;
7
+ exports.validateGraphRewritePayload = validateGraphRewritePayload;
8
+ exports.validateGraphJsonlLine = validateGraphJsonlLine;
9
+ exports.createQualityLogger = createQualityLogger;
10
+ const ontology_1 = require("../graph/ontology");
11
+ const ANOMALY_PATTERNS = [
12
+ /\d+\.\d+,\s*"[^"]+"/,
13
+ /"[^"]+"\s+\d+\.\d+/,
14
+ /,\s*\d+\.\d+,/,
15
+ /"\w+\.\w+\.\w+"/,
16
+ /\d+\.\w+\.\d+/,
17
+ /out\s+\d+\.\d+/,
18
+ /source\s+\d+/,
19
+ /layer\s+\d+/,
20
+ ];
21
+ const REQUIRED_EVENT_FIELDS = ["event_type", "summary"];
22
+ const VALID_TARGET_LAYERS = ["active_only", "archive_event", "skip"];
23
+ function detectDuplicateKeys(jsonString) {
24
+ const duplicates = [];
25
+ const keyPattern = /"([^"\\]*(?:\\.[^"\\]*)*)"\s*:/g;
26
+ const keyCounts = new Map();
27
+ let match;
28
+ while ((match = keyPattern.exec(jsonString)) !== null) {
29
+ const key = match[1];
30
+ keyCounts.set(key, (keyCounts.get(key) || 0) + 1);
31
+ }
32
+ for (const [key, count] of keyCounts) {
33
+ if (count > 1) {
34
+ duplicates.push(key);
35
+ }
36
+ }
37
+ return duplicates;
38
+ }
39
+ function detectAnomalyPatterns(jsonString) {
40
+ const anomalies = [];
41
+ for (const pattern of ANOMALY_PATTERNS) {
42
+ const matches = jsonString.match(pattern);
43
+ if (matches) {
44
+ anomalies.push(`Pattern detected: ${matches[0].slice(0, 50)}`);
45
+ }
46
+ }
47
+ return anomalies;
48
+ }
49
+ function looksLikeMarkdownFrontMatter(raw) {
50
+ const text = String(raw || "").trim();
51
+ if (!text.startsWith("---")) {
52
+ return false;
53
+ }
54
+ const lines = text.split(/\r?\n/);
55
+ if (lines.length < 3) {
56
+ return false;
57
+ }
58
+ let fenceCount = 0;
59
+ for (const line of lines) {
60
+ if (line.trim() === "---") {
61
+ fenceCount += 1;
62
+ if (fenceCount >= 2) {
63
+ return true;
64
+ }
65
+ }
66
+ }
67
+ return false;
68
+ }
69
+ function isValidJsonStructure(parsed) {
70
+ if (!parsed || typeof parsed !== "object") {
71
+ return false;
72
+ }
73
+ const str = JSON.stringify(parsed);
74
+ if (str.length < 2) {
75
+ return false;
76
+ }
77
+ return true;
78
+ }
79
+ function validateEventRecord(event) {
80
+ const errors = [];
81
+ if (!event || typeof event !== "object") {
82
+ errors.push("event is not an object");
83
+ return { valid: false, errors };
84
+ }
85
+ const record = event;
86
+ for (const field of REQUIRED_EVENT_FIELDS) {
87
+ if (record[field] === undefined) {
88
+ errors.push(`missing required field: ${field}`);
89
+ }
90
+ }
91
+ if (record.event_type !== undefined && typeof record.event_type !== "string") {
92
+ errors.push("event_type must be a string");
93
+ }
94
+ if (record.summary !== undefined && typeof record.summary !== "string") {
95
+ errors.push("summary must be a string");
96
+ }
97
+ if (record.cause !== undefined && typeof record.cause !== "string") {
98
+ errors.push("cause must be a string");
99
+ }
100
+ if (record.process !== undefined && typeof record.process !== "string") {
101
+ errors.push("process must be a string");
102
+ }
103
+ if (record.result !== undefined && typeof record.result !== "string") {
104
+ errors.push("result must be a string");
105
+ }
106
+ if (record.entities !== undefined && !Array.isArray(record.entities)) {
107
+ errors.push("entities must be an array");
108
+ }
109
+ if (record.entity_types !== undefined) {
110
+ if (typeof record.entity_types !== "object" || record.entity_types === null || Array.isArray(record.entity_types)) {
111
+ errors.push("entity_types must be an object");
112
+ }
113
+ }
114
+ if (record.relations !== undefined && !Array.isArray(record.relations)) {
115
+ errors.push("relations must be an array");
116
+ }
117
+ if (record.confidence !== undefined) {
118
+ if (typeof record.confidence !== "number" || record.confidence < 0 || record.confidence > 1) {
119
+ errors.push("confidence must be a number between 0 and 1");
120
+ }
121
+ }
122
+ return { valid: errors.length === 0, errors };
123
+ }
124
+ function validateGateDecision(decision, schema) {
125
+ const errors = [];
126
+ if (!decision || typeof decision !== "object") {
127
+ errors.push("decision is not an object");
128
+ return { valid: false, errors };
129
+ }
130
+ const record = decision;
131
+ if (record.target_layer === undefined) {
132
+ errors.push("missing target_layer");
133
+ }
134
+ else if (typeof record.target_layer !== "string") {
135
+ errors.push("target_layer must be a string");
136
+ }
137
+ else if (!VALID_TARGET_LAYERS.includes(record.target_layer)) {
138
+ errors.push(`invalid target_layer: ${record.target_layer}`);
139
+ }
140
+ if (record.target_layer === "archive_event") {
141
+ const eventValidation = validateArchiveEvent(record.event, { schema });
142
+ if (!eventValidation.valid) {
143
+ errors.push(...eventValidation.errors.map(e => `event.${e}`));
144
+ }
145
+ }
146
+ if (record.target_layer === "active_only") {
147
+ if (record.active_text !== undefined && typeof record.active_text !== "string") {
148
+ errors.push("active_text must be a string");
149
+ }
150
+ }
151
+ if (record.target_layer === "skip") {
152
+ if (record.reason !== undefined && typeof record.reason !== "string") {
153
+ errors.push("reason must be a string");
154
+ }
155
+ }
156
+ return { valid: errors.length === 0, errors };
157
+ }
158
+ function validateLlmJsonOutput(rawString) {
159
+ const errors = [];
160
+ const warnings = [];
161
+ if (!rawString || typeof rawString !== "string") {
162
+ errors.push("input is not a string");
163
+ return { valid: false, data: null, errors, warnings };
164
+ }
165
+ const trimmed = rawString.trim();
166
+ if (!trimmed) {
167
+ errors.push("input is empty");
168
+ return { valid: false, data: null, errors, warnings };
169
+ }
170
+ const duplicateKeys = detectDuplicateKeys(trimmed);
171
+ if (duplicateKeys.length > 0) {
172
+ warnings.push(`possible duplicate keys detected (global scan): ${duplicateKeys.slice(0, 5).join(", ")}`);
173
+ }
174
+ const anomalies = detectAnomalyPatterns(trimmed);
175
+ if (anomalies.length > 0) {
176
+ warnings.push(...anomalies);
177
+ }
178
+ let parsed;
179
+ try {
180
+ const fenceMatch = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
181
+ const candidate = fenceMatch?.[1]?.trim() || trimmed;
182
+ parsed = JSON.parse(candidate);
183
+ }
184
+ catch (parseError) {
185
+ if (looksLikeMarkdownFrontMatter(trimmed)) {
186
+ errors.push("markdown_frontmatter_invalid");
187
+ }
188
+ errors.push(`JSON parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
189
+ return { valid: false, data: null, errors, warnings };
190
+ }
191
+ if (!isValidJsonStructure(parsed)) {
192
+ errors.push("parsed JSON has invalid structure");
193
+ return { valid: false, data: null, errors, warnings };
194
+ }
195
+ return { valid: true, data: parsed, errors, warnings };
196
+ }
197
+ function validateGateDecisionsOutput(rawString, options) {
198
+ const baseResult = validateLlmJsonOutput(rawString);
199
+ if (!baseResult.valid) {
200
+ return baseResult;
201
+ }
202
+ const schema = options?.schema || (0, ontology_1.getDefaultGraphSchema)();
203
+ const errors = [...baseResult.errors];
204
+ const warnings = [...baseResult.warnings];
205
+ const parsed = baseResult.data;
206
+ let decisions;
207
+ if (Array.isArray(parsed)) {
208
+ decisions = parsed;
209
+ }
210
+ else if (Array.isArray(parsed.decisions)) {
211
+ decisions = parsed.decisions;
212
+ }
213
+ else {
214
+ errors.push("output must be an array or contain a 'decisions' array");
215
+ return { valid: false, data: null, errors, warnings };
216
+ }
217
+ const validatedDecisions = [];
218
+ for (let i = 0; i < decisions.length; i++) {
219
+ const decisionValidation = validateGateDecision(decisions[i], schema);
220
+ if (!decisionValidation.valid) {
221
+ warnings.push(`decision[${i}]: ${decisionValidation.errors.join("; ")}`);
222
+ }
223
+ else {
224
+ validatedDecisions.push(decisions[i]);
225
+ }
226
+ }
227
+ if (validatedDecisions.length === 0 && decisions.length > 0) {
228
+ errors.push("no valid decisions found in output");
229
+ return { valid: false, data: null, errors, warnings };
230
+ }
231
+ return {
232
+ valid: true,
233
+ data: validatedDecisions,
234
+ errors,
235
+ warnings,
236
+ };
237
+ }
238
+ function cleanRelationRecord(raw, schema) {
239
+ const errors = [];
240
+ if (!raw || typeof raw !== "object") {
241
+ return { errors: ["relation_not_object"] };
242
+ }
243
+ const rel = raw;
244
+ const source = typeof rel.source === "string" ? rel.source.trim() : "";
245
+ const target = typeof rel.target === "string" ? rel.target.trim() : "";
246
+ const typeRaw = typeof rel.type === "string" ? rel.type.trim() : "";
247
+ if (!source || !target) {
248
+ errors.push("invalid_relation_fields");
249
+ return { errors };
250
+ }
251
+ if (!typeRaw) {
252
+ errors.push("missing_relation_type");
253
+ return { errors };
254
+ }
255
+ const type = (0, ontology_1.normalizeRelationType)(typeRaw, schema);
256
+ if (!type) {
257
+ errors.push("invalid_relation_type");
258
+ return { errors };
259
+ }
260
+ if (type === "related_to") {
261
+ errors.push("related_to_detected");
262
+ return { errors };
263
+ }
264
+ const isCanonical = (0, ontology_1.isCanonicalRelationType)(type, schema);
265
+ const relationOriginRaw = typeof rel.relation_origin === "string" ? rel.relation_origin.trim() : "";
266
+ const relationOrigin = relationOriginRaw === "canonical" || relationOriginRaw === "llm_custom"
267
+ ? relationOriginRaw
268
+ : (isCanonical ? "canonical" : "llm_custom");
269
+ const relationDefinition = typeof rel.relation_definition === "string" ? rel.relation_definition.trim() : "";
270
+ const mappingHint = typeof rel.mapping_hint === "string" ? rel.mapping_hint.trim() : "";
271
+ if (relationOrigin === "canonical" && !isCanonical) {
272
+ errors.push("relation_origin_mismatch");
273
+ return { errors };
274
+ }
275
+ if (relationOrigin === "llm_custom" && isCanonical) {
276
+ errors.push("relation_origin_mismatch");
277
+ return { errors };
278
+ }
279
+ if (relationOrigin === "llm_custom" && !relationDefinition) {
280
+ errors.push("llm_custom_missing_definition");
281
+ return { errors };
282
+ }
283
+ const evidenceSpan = typeof rel.evidence_span === "string" ? rel.evidence_span.trim() : "";
284
+ const contextChunk = typeof rel.context_chunk === "string" ? rel.context_chunk.trim() : "";
285
+ const confidence = typeof rel.confidence === "number"
286
+ ? Math.max(0, Math.min(1, rel.confidence))
287
+ : undefined;
288
+ return {
289
+ relation: {
290
+ source,
291
+ target,
292
+ type,
293
+ relation_origin: relationOrigin,
294
+ relation_definition: relationDefinition || undefined,
295
+ mapping_hint: mappingHint || undefined,
296
+ evidence_span: evidenceSpan || undefined,
297
+ context_chunk: contextChunk || undefined,
298
+ confidence,
299
+ },
300
+ errors,
301
+ };
302
+ }
303
+ function validateArchiveEvent(event, options) {
304
+ const schema = options?.schema || (0, ontology_1.getDefaultGraphSchema)();
305
+ const validation = validateEventRecord(event);
306
+ if (!validation.valid) {
307
+ return validation;
308
+ }
309
+ const record = event;
310
+ const cleaned = {
311
+ event_type: typeof record.event_type === "string" ? record.event_type.trim() : "insight",
312
+ summary: typeof record.summary === "string" ? record.summary.trim() : "",
313
+ cause: typeof record.cause === "string" ? record.cause.trim() : "",
314
+ process: typeof record.process === "string" ? record.process.trim() : "",
315
+ result: typeof record.result === "string" ? record.result.trim() : "",
316
+ entities: Array.isArray(record.entities)
317
+ ? record.entities.filter((e) => typeof e === "string" && e.trim().length > 0).map(e => e.trim())
318
+ : [],
319
+ entity_types: typeof record.entity_types === "object" && record.entity_types !== null && !Array.isArray(record.entity_types)
320
+ ? Object.fromEntries(Object.entries(record.entity_types)
321
+ .filter(([key, value]) => typeof key === "string" && key.trim().length > 0 && typeof value === "string" && value.trim().length > 0)
322
+ .map(([key, value]) => [key.trim(), value.trim()]))
323
+ : undefined,
324
+ relations: [],
325
+ outcome: typeof record.outcome === "string" ? record.outcome.trim() : "",
326
+ confidence: typeof record.confidence === "number"
327
+ ? Math.max(0, Math.min(1, record.confidence))
328
+ : 0.6,
329
+ };
330
+ if (!cleaned.summary) {
331
+ return { valid: false, errors: ["summary is empty after cleaning"] };
332
+ }
333
+ if (!cleaned.cause) {
334
+ return { valid: false, errors: ["cause is required for archive_event"] };
335
+ }
336
+ if (!cleaned.process) {
337
+ return { valid: false, errors: ["process is required for archive_event"] };
338
+ }
339
+ if (!cleaned.result) {
340
+ return { valid: false, errors: ["result is required for archive_event"] };
341
+ }
342
+ if (Array.isArray(record.relations)) {
343
+ const relationErrors = [];
344
+ const relations = [];
345
+ for (const relationRaw of record.relations) {
346
+ const cleanedRelation = cleanRelationRecord(relationRaw, schema);
347
+ if (cleanedRelation.relation) {
348
+ relations.push(cleanedRelation.relation);
349
+ }
350
+ if (cleanedRelation.errors.length > 0) {
351
+ relationErrors.push(...cleanedRelation.errors);
352
+ }
353
+ }
354
+ cleaned.relations = relations;
355
+ if (relationErrors.length > 0) {
356
+ return { valid: false, errors: [...new Set(relationErrors)] };
357
+ }
358
+ }
359
+ return { valid: true, errors: [], cleaned };
360
+ }
361
+ function validateJsonlLine(line) {
362
+ const errors = [];
363
+ if (!line || !line.trim()) {
364
+ return { valid: true, errors: [] };
365
+ }
366
+ let record;
367
+ try {
368
+ record = JSON.parse(line);
369
+ }
370
+ catch {
371
+ errors.push("invalid JSON");
372
+ return { valid: false, errors };
373
+ }
374
+ if (!record || typeof record !== "object") {
375
+ errors.push("record is not an object");
376
+ return { valid: false, errors };
377
+ }
378
+ if (typeof record.id !== "string" || !record.id.trim()) {
379
+ errors.push("missing or invalid id field");
380
+ }
381
+ if (typeof record.timestamp !== "string" || !record.timestamp.trim()) {
382
+ errors.push("missing or invalid timestamp field");
383
+ }
384
+ if (typeof record.layer !== "string" || (record.layer !== "active" && record.layer !== "archive")) {
385
+ errors.push("missing or invalid layer field");
386
+ }
387
+ return { valid: errors.length === 0, record, errors };
388
+ }
389
+ const GARBLED_CHAR_PATTERN = /[�锛銆鈥鈩鈹鍚鍙锟馃鉁]/g;
390
+ function textGarbledScore(input) {
391
+ const text = String(input || "").trim();
392
+ if (!text) {
393
+ return { score: 0, suspiciousCount: 0 };
394
+ }
395
+ const suspiciousMatches = text.match(GARBLED_CHAR_PATTERN) || [];
396
+ const suspiciousCount = suspiciousMatches.length;
397
+ const score = suspiciousCount / Math.max(1, text.length);
398
+ return { score, suspiciousCount };
399
+ }
400
+ function hasLikelyGarbledText(input) {
401
+ const { score, suspiciousCount } = textGarbledScore(input);
402
+ if (suspiciousCount === 0)
403
+ return false;
404
+ if (input.includes("�"))
405
+ return true;
406
+ return suspiciousCount >= 2 && score >= 0.05;
407
+ }
408
+ function hasValidSourceTextNav(value) {
409
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
410
+ return false;
411
+ }
412
+ const nav = value;
413
+ const layer = typeof nav.layer === "string" ? nav.layer.trim() : "";
414
+ const sessionId = typeof nav.session_id === "string" ? nav.session_id.trim() : "";
415
+ const sourceFile = typeof nav.source_file === "string" ? nav.source_file.trim() : "";
416
+ const sourceMemoryId = typeof nav.source_memory_id === "string" ? nav.source_memory_id.trim() : "";
417
+ const sourceEventId = typeof nav.source_event_id === "string" ? nav.source_event_id.trim() : "";
418
+ const layerValid = layer === "archive_event" || layer === "active_only";
419
+ return Boolean(layerValid && sessionId && sourceFile && sourceMemoryId && sourceEventId);
420
+ }
421
+ function summaryMentionsEntity(summary, entity) {
422
+ const normalizedSummary = String(summary || "").toLowerCase();
423
+ const normalizedEntity = String(entity || "").toLowerCase();
424
+ if (!normalizedSummary || !normalizedEntity) {
425
+ return false;
426
+ }
427
+ return normalizedSummary.includes(normalizedEntity);
428
+ }
429
+ function validateGraphRewritePayload(payload, options) {
430
+ const errors = [];
431
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
432
+ return { valid: false, errors: ["graph_payload_not_object"] };
433
+ }
434
+ const record = payload;
435
+ const summary = typeof record.summary === "string" ? record.summary.trim() : "";
436
+ if (!summary) {
437
+ errors.push("missing_summary");
438
+ }
439
+ if (!hasValidSourceTextNav(record.source_text_nav)) {
440
+ errors.push("fulltext_navigation_missing");
441
+ }
442
+ const entities = Array.isArray(record.entities)
443
+ ? record.entities.map(item => (typeof item === "string" ? item.trim() : "")).filter(Boolean)
444
+ : [];
445
+ if (entities.length === 0) {
446
+ errors.push("missing_or_invalid_entities");
447
+ }
448
+ const entityNameSet = new Set(entities.map(item => item.toLowerCase()));
449
+ const entityTypes = record.entity_types;
450
+ if (!entityTypes || typeof entityTypes !== "object" || Array.isArray(entityTypes)) {
451
+ errors.push("missing_or_invalid_entity_types");
452
+ }
453
+ else {
454
+ const typeLookup = new Map();
455
+ for (const [entity, type] of Object.entries(entityTypes)) {
456
+ const key = String(entity || "").trim().toLowerCase();
457
+ const val = typeof type === "string" ? type.trim() : "";
458
+ if (!key || !val)
459
+ continue;
460
+ typeLookup.set(key, val);
461
+ }
462
+ for (const entity of entities) {
463
+ if (!typeLookup.has(entity.toLowerCase())) {
464
+ errors.push(`entity_type_missing:${entity}`);
465
+ }
466
+ }
467
+ }
468
+ const schema = options?.schema || (0, ontology_1.getDefaultGraphSchema)();
469
+ const relations = Array.isArray(record.relations) ? record.relations : [];
470
+ if (relations.length === 0) {
471
+ errors.push("missing_or_invalid_relations");
472
+ }
473
+ else {
474
+ for (const relationRaw of relations) {
475
+ if (!relationRaw || typeof relationRaw !== "object" || Array.isArray(relationRaw)) {
476
+ errors.push("relation_not_object");
477
+ continue;
478
+ }
479
+ const relObj = relationRaw;
480
+ const relationOriginRaw = typeof relObj.relation_origin === "string" ? relObj.relation_origin.trim() : "";
481
+ if (!relationOriginRaw) {
482
+ errors.push("relation_missing_origin");
483
+ }
484
+ const cleanedRelation = cleanRelationRecord(relationRaw, schema);
485
+ if (!cleanedRelation.relation) {
486
+ errors.push(...cleanedRelation.errors);
487
+ continue;
488
+ }
489
+ const relation = cleanedRelation.relation;
490
+ const source = relation.source;
491
+ const target = relation.target;
492
+ const evidenceSpan = typeof relation.evidence_span === "string" ? relation.evidence_span.trim() : "";
493
+ const contextChunk = typeof relation.context_chunk === "string" ? relation.context_chunk.trim() : "";
494
+ const confidence = relation.confidence;
495
+ if (!evidenceSpan) {
496
+ errors.push("missing_relation_evidence_span");
497
+ }
498
+ if (!contextChunk) {
499
+ errors.push("missing_context_chunk");
500
+ }
501
+ if (typeof confidence !== "number" || !Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
502
+ errors.push("missing_or_invalid_relation_confidence");
503
+ }
504
+ if (!entityNameSet.has(source.toLowerCase())) {
505
+ errors.push(`relation_source_not_in_entities:${source}`);
506
+ }
507
+ if (!entityNameSet.has(target.toLowerCase())) {
508
+ errors.push(`relation_target_not_in_entities:${target}`);
509
+ }
510
+ }
511
+ }
512
+ const confidence = typeof record.confidence === "number" ? record.confidence : NaN;
513
+ if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
514
+ errors.push("missing_or_invalid_confidence");
515
+ }
516
+ return { valid: errors.length === 0, errors: [...new Set(errors)] };
517
+ }
518
+ function validateGraphJsonlLine(line) {
519
+ const errors = [];
520
+ const warnings = [];
521
+ if (!line || !line.trim()) {
522
+ return { valid: false, errors: ["empty_line"], warnings };
523
+ }
524
+ let record;
525
+ try {
526
+ record = JSON.parse(line);
527
+ }
528
+ catch {
529
+ return { valid: false, errors: ["invalid_json"], warnings };
530
+ }
531
+ if (!record || typeof record !== "object") {
532
+ return { valid: false, errors: ["record_not_object"], warnings };
533
+ }
534
+ if (typeof record.id !== "string" || !record.id.trim()) {
535
+ errors.push("missing_or_invalid_id");
536
+ }
537
+ if (typeof record.timestamp !== "string" || !record.timestamp.trim()) {
538
+ errors.push("missing_or_invalid_timestamp");
539
+ }
540
+ const sourceEventId = typeof record.source_event_id === "string" ? record.source_event_id.trim() : "";
541
+ const archiveEventId = typeof record.archive_event_id === "string" ? record.archive_event_id.trim() : "";
542
+ if (!sourceEventId && !archiveEventId) {
543
+ errors.push("missing_or_invalid_source_event_id");
544
+ }
545
+ const sourceLayer = typeof record.source_layer === "string" ? record.source_layer : "";
546
+ const sourceLayerInvalid = sourceLayer !== "archive_event" && sourceLayer !== "active_only";
547
+ if (sourceEventId && sourceLayerInvalid) {
548
+ if (!archiveEventId) {
549
+ errors.push("missing_or_invalid_source_layer");
550
+ }
551
+ }
552
+ if (typeof record.session_id !== "string" || !record.session_id.trim()) {
553
+ errors.push("missing_or_invalid_session_id");
554
+ }
555
+ const summary = typeof record.summary === "string" ? record.summary.trim() : "";
556
+ if (!summary) {
557
+ errors.push("missing_summary");
558
+ }
559
+ if (!hasValidSourceTextNav(record.source_text_nav)) {
560
+ errors.push("fulltext_navigation_missing");
561
+ }
562
+ const entities = Array.isArray(record.entities) ? record.entities : [];
563
+ if (!Array.isArray(record.entities) || entities.length === 0) {
564
+ errors.push("missing_or_invalid_entities");
565
+ }
566
+ if (summary && Array.isArray(record.entities) && entities.length > 0) {
567
+ const missingSummaryEntities = entities
568
+ .map(item => (typeof item === "string" ? item.trim() : ""))
569
+ .filter(Boolean)
570
+ .filter(entity => !summaryMentionsEntity(summary, entity));
571
+ if (missingSummaryEntities.length > 0) {
572
+ errors.push("summary_missing_entities");
573
+ }
574
+ }
575
+ const entityTypes = record.entity_types;
576
+ if (!entityTypes || typeof entityTypes !== "object" || Array.isArray(entityTypes)) {
577
+ errors.push("missing_or_invalid_entity_types");
578
+ }
579
+ const relations = Array.isArray(record.relations) ? record.relations : [];
580
+ const schema = (0, ontology_1.getDefaultGraphSchema)();
581
+ if (!Array.isArray(record.relations) || relations.length === 0) {
582
+ errors.push("missing_or_invalid_relations");
583
+ }
584
+ for (const entity of entities) {
585
+ if (typeof entity !== "string" || !entity.trim()) {
586
+ errors.push("invalid_entity_item");
587
+ continue;
588
+ }
589
+ if (hasLikelyGarbledText(entity)) {
590
+ errors.push(`garbled_entity:${entity.slice(0, 24)}`);
591
+ }
592
+ }
593
+ for (const relationRaw of relations) {
594
+ const cleanedRelation = cleanRelationRecord(relationRaw, schema);
595
+ if (!cleanedRelation.relation) {
596
+ errors.push(...cleanedRelation.errors);
597
+ continue;
598
+ }
599
+ const relation = cleanedRelation.relation;
600
+ const source = relation.source;
601
+ const target = relation.target;
602
+ const type = relation.type;
603
+ const evidenceSpan = typeof relation.evidence_span === "string" ? relation.evidence_span.trim() : "";
604
+ const confidence = relation.confidence;
605
+ const isCanonical = (0, ontology_1.isCanonicalRelationType)(type, schema);
606
+ if (relation.relation_origin === "canonical" && !isCanonical) {
607
+ errors.push("relation_origin_mismatch");
608
+ }
609
+ if (relation.relation_origin === "llm_custom" && !relation.relation_definition) {
610
+ errors.push("llm_custom_missing_definition");
611
+ }
612
+ if (!evidenceSpan) {
613
+ errors.push("missing_relation_evidence_span");
614
+ }
615
+ const contextChunk = typeof relation.context_chunk === "string" ? relation.context_chunk.trim() : "";
616
+ if (!contextChunk) {
617
+ errors.push("missing_context_chunk");
618
+ }
619
+ else {
620
+ const length = contextChunk.length;
621
+ if (length < 50 || length > 120) {
622
+ warnings.push("context_chunk_length_out_of_range");
623
+ }
624
+ }
625
+ if (typeof confidence !== "number" || !Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
626
+ errors.push("missing_or_invalid_relation_confidence");
627
+ }
628
+ if (hasLikelyGarbledText(source) || hasLikelyGarbledText(target) || hasLikelyGarbledText(type)) {
629
+ errors.push(`garbled_relation:${source.slice(0, 16)}|${type.slice(0, 16)}|${target.slice(0, 16)}`);
630
+ }
631
+ if (evidenceSpan) {
632
+ if (hasLikelyGarbledText(evidenceSpan)) {
633
+ warnings.push("garbled_evidence_span");
634
+ }
635
+ }
636
+ }
637
+ return {
638
+ valid: errors.length === 0,
639
+ record,
640
+ errors,
641
+ warnings,
642
+ };
643
+ }
644
+ function createQualityLogger(baseLogger) {
645
+ return {
646
+ logValidationResult(context, result) {
647
+ if (result.errors.length > 0) {
648
+ baseLogger.warn(`quality_validation_failed context=${context} errors=${result.errors.join("|")}`);
649
+ }
650
+ if (result.warnings.length > 0) {
651
+ baseLogger.debug(`quality_validation_warnings context=${context} warnings=${result.warnings.join("|")}`);
652
+ }
653
+ },
654
+ logInvalidRecord(source, line, errors) {
655
+ baseLogger.warn(`quality_invalid_record source=${source} line=${line} errors=${errors.join("|")}`);
656
+ },
657
+ };
658
+ }
659
+ //# sourceMappingURL=llm_output_validator.js.map