openclaw-cortex-memory 0.1.0-Alpha.2 → 0.1.0-Alpha.21
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 +163 -203
- package/SKILL.md +71 -268
- package/dist/index.d.ts +88 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +859 -1189
- package/dist/index.js.map +1 -1
- package/dist/openclaw.plugin.json +362 -14
- package/dist/src/dedup/three_stage_deduplicator.d.ts +25 -0
- package/dist/src/dedup/three_stage_deduplicator.d.ts.map +1 -0
- package/dist/src/dedup/three_stage_deduplicator.js +224 -0
- package/dist/src/dedup/three_stage_deduplicator.js.map +1 -0
- package/dist/src/engine/memory_engine.d.ts +2 -1
- package/dist/src/engine/memory_engine.d.ts.map +1 -1
- package/dist/src/engine/ts_engine.d.ts +126 -0
- package/dist/src/engine/ts_engine.d.ts.map +1 -1
- package/dist/src/engine/ts_engine.js +1172 -44
- package/dist/src/engine/ts_engine.js.map +1 -1
- package/dist/src/engine/types.d.ts +12 -0
- package/dist/src/engine/types.d.ts.map +1 -1
- package/dist/src/graph/ontology.d.ts +103 -0
- package/dist/src/graph/ontology.d.ts.map +1 -0
- package/dist/src/graph/ontology.js +564 -0
- package/dist/src/graph/ontology.js.map +1 -0
- package/dist/src/quality/llm_output_validator.d.ts +48 -0
- package/dist/src/quality/llm_output_validator.d.ts.map +1 -0
- package/dist/src/quality/llm_output_validator.js +404 -0
- package/dist/src/quality/llm_output_validator.js.map +1 -0
- package/dist/src/reflect/reflector.d.ts +7 -0
- package/dist/src/reflect/reflector.d.ts.map +1 -1
- package/dist/src/reflect/reflector.js +358 -8
- package/dist/src/reflect/reflector.js.map +1 -1
- package/dist/src/rules/rule_store.d.ts.map +1 -1
- package/dist/src/rules/rule_store.js +75 -16
- package/dist/src/rules/rule_store.js.map +1 -1
- package/dist/src/session/session_end.d.ts +33 -0
- package/dist/src/session/session_end.d.ts.map +1 -1
- package/dist/src/session/session_end.js +67 -64
- package/dist/src/session/session_end.js.map +1 -1
- package/dist/src/store/archive_store.d.ts +128 -0
- package/dist/src/store/archive_store.d.ts.map +1 -0
- package/dist/src/store/archive_store.js +481 -0
- package/dist/src/store/archive_store.js.map +1 -0
- package/dist/src/store/embedding_utils.d.ts +32 -0
- package/dist/src/store/embedding_utils.d.ts.map +1 -0
- package/dist/src/store/embedding_utils.js +173 -0
- package/dist/src/store/embedding_utils.js.map +1 -0
- package/dist/src/store/graph_memory_store.d.ts +44 -0
- package/dist/src/store/graph_memory_store.d.ts.map +1 -0
- package/dist/src/store/graph_memory_store.js +168 -0
- package/dist/src/store/graph_memory_store.js.map +1 -0
- package/dist/src/store/read_store.d.ts +86 -0
- package/dist/src/store/read_store.d.ts.map +1 -1
- package/dist/src/store/read_store.js +1681 -25
- package/dist/src/store/read_store.js.map +1 -1
- package/dist/src/store/vector_store.d.ts +44 -0
- package/dist/src/store/vector_store.d.ts.map +1 -0
- package/dist/src/store/vector_store.js +201 -0
- package/dist/src/store/vector_store.js.map +1 -0
- package/dist/src/store/write_store.d.ts +52 -0
- package/dist/src/store/write_store.d.ts.map +1 -1
- package/dist/src/store/write_store.js +245 -3
- package/dist/src/store/write_store.js.map +1 -1
- package/dist/src/sync/session_sync.d.ts +100 -2
- package/dist/src/sync/session_sync.d.ts.map +1 -1
- package/dist/src/sync/session_sync.js +673 -22
- package/dist/src/sync/session_sync.js.map +1 -1
- package/dist/src/utils/runtime_env.d.ts +4 -0
- package/dist/src/utils/runtime_env.d.ts.map +1 -0
- package/dist/src/utils/runtime_env.js +51 -0
- package/dist/src/utils/runtime_env.js.map +1 -0
- package/openclaw.plugin.json +362 -14
- package/package.json +23 -6
- package/scripts/cli.js +19 -14
- package/scripts/uninstall.js +22 -5
- package/index.ts +0 -2092
- package/scripts/install.js +0 -27
|
@@ -37,6 +37,7 @@ exports.createSessionSync = createSessionSync;
|
|
|
37
37
|
const crypto = __importStar(require("crypto"));
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
+
const llm_output_validator_1 = require("../quality/llm_output_validator");
|
|
40
41
|
function asRecord(value) {
|
|
41
42
|
if (typeof value === "object" && value !== null) {
|
|
42
43
|
return value;
|
|
@@ -51,23 +52,33 @@ function firstString(values) {
|
|
|
51
52
|
}
|
|
52
53
|
return undefined;
|
|
53
54
|
}
|
|
55
|
+
const SYNC_STATE_VERSION = "2";
|
|
56
|
+
function createDefaultState() {
|
|
57
|
+
return { version: SYNC_STATE_VERSION, files: {}, markdowns: {} };
|
|
58
|
+
}
|
|
54
59
|
function readState(filePath) {
|
|
55
60
|
try {
|
|
56
61
|
if (!fs.existsSync(filePath)) {
|
|
57
|
-
return
|
|
62
|
+
return createDefaultState();
|
|
58
63
|
}
|
|
59
64
|
const content = fs.readFileSync(filePath, "utf-8").trim();
|
|
60
65
|
if (!content) {
|
|
61
|
-
return
|
|
66
|
+
return createDefaultState();
|
|
62
67
|
}
|
|
63
68
|
const parsed = JSON.parse(content);
|
|
64
69
|
if (!parsed.files || typeof parsed.files !== "object") {
|
|
65
|
-
return
|
|
70
|
+
return createDefaultState();
|
|
71
|
+
}
|
|
72
|
+
if (!parsed.markdowns || typeof parsed.markdowns !== "object") {
|
|
73
|
+
parsed.markdowns = {};
|
|
66
74
|
}
|
|
75
|
+
parsed.version = typeof parsed.version === "string" && parsed.version.trim()
|
|
76
|
+
? parsed.version
|
|
77
|
+
: SYNC_STATE_VERSION;
|
|
67
78
|
return parsed;
|
|
68
79
|
}
|
|
69
80
|
catch {
|
|
70
|
-
return
|
|
81
|
+
return createDefaultState();
|
|
71
82
|
}
|
|
72
83
|
}
|
|
73
84
|
function writeState(filePath, state) {
|
|
@@ -75,6 +86,7 @@ function writeState(filePath, state) {
|
|
|
75
86
|
if (!fs.existsSync(dir)) {
|
|
76
87
|
fs.mkdirSync(dir, { recursive: true });
|
|
77
88
|
}
|
|
89
|
+
state.version = SYNC_STATE_VERSION;
|
|
78
90
|
fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
79
91
|
}
|
|
80
92
|
function gatherSessionFiles(openclawBasePath, memoryRoot) {
|
|
@@ -93,10 +105,35 @@ function gatherSessionFiles(openclawBasePath, memoryRoot) {
|
|
|
93
105
|
}
|
|
94
106
|
return [...results];
|
|
95
107
|
}
|
|
108
|
+
function gatherDailySummaryFiles(openclawBasePath) {
|
|
109
|
+
const summaryDir = path.join(openclawBasePath, "workspace", "memory");
|
|
110
|
+
if (!fs.existsSync(summaryDir) || !fs.statSync(summaryDir).isDirectory()) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const files = [];
|
|
114
|
+
for (const entry of fs.readdirSync(summaryDir)) {
|
|
115
|
+
if (!entry.toLowerCase().endsWith(".md")) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const filePath = path.join(summaryDir, entry);
|
|
119
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
120
|
+
files.push(filePath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return files;
|
|
124
|
+
}
|
|
96
125
|
function inferOpenclawBasePath(projectRoot) {
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
return
|
|
126
|
+
const configPath = process.env.OPENCLAW_CONFIG_PATH;
|
|
127
|
+
if (configPath && fs.existsSync(configPath)) {
|
|
128
|
+
return path.dirname(configPath);
|
|
129
|
+
}
|
|
130
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR;
|
|
131
|
+
if (stateDir && fs.existsSync(stateDir)) {
|
|
132
|
+
return stateDir;
|
|
133
|
+
}
|
|
134
|
+
const basePath = process.env.OPENCLAW_BASE_PATH;
|
|
135
|
+
if (basePath && fs.existsSync(basePath)) {
|
|
136
|
+
return basePath;
|
|
100
137
|
}
|
|
101
138
|
const home = process.env.USERPROFILE || process.env.HOME || "";
|
|
102
139
|
if (home) {
|
|
@@ -143,16 +180,595 @@ function getSessionId(record, fallbackSeed) {
|
|
|
143
180
|
record.id,
|
|
144
181
|
]) || `sync:${fallbackSeed}`);
|
|
145
182
|
}
|
|
183
|
+
function parseDailySummary(content) {
|
|
184
|
+
const normalized = content
|
|
185
|
+
.replace(/\r\n/g, "\n")
|
|
186
|
+
.split("\n")
|
|
187
|
+
.map(line => line.trim())
|
|
188
|
+
.filter(Boolean)
|
|
189
|
+
.filter(line => !line.startsWith("```"));
|
|
190
|
+
const chunks = [];
|
|
191
|
+
let current = [];
|
|
192
|
+
for (const line of normalized) {
|
|
193
|
+
const isHeader = line.startsWith("#");
|
|
194
|
+
const isBullet = /^[-*]\s+/.test(line);
|
|
195
|
+
if (isHeader && current.length > 0) {
|
|
196
|
+
chunks.push(current.join("\n"));
|
|
197
|
+
current = [];
|
|
198
|
+
}
|
|
199
|
+
current.push(line);
|
|
200
|
+
if (isBullet && current.length >= 6) {
|
|
201
|
+
chunks.push(current.join("\n"));
|
|
202
|
+
current = [];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (current.length > 0) {
|
|
206
|
+
chunks.push(current.join("\n"));
|
|
207
|
+
}
|
|
208
|
+
return chunks.map(chunk => chunk.trim()).filter(chunk => chunk.length >= 10);
|
|
209
|
+
}
|
|
210
|
+
function normalizeBaseUrl(value) {
|
|
211
|
+
if (!value)
|
|
212
|
+
return "";
|
|
213
|
+
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
214
|
+
}
|
|
215
|
+
function buildEventSnippet(text) {
|
|
216
|
+
const lines = text
|
|
217
|
+
.split(/\r?\n/)
|
|
218
|
+
.map(line => line.trim())
|
|
219
|
+
.filter(Boolean)
|
|
220
|
+
.filter(line => line.length >= 8);
|
|
221
|
+
const actionPattern = /(决定|完成|修复|阻塞|失败|成功|上线|部署|实现|依赖|owner|blocked|resolved|fixed|depends|decide|complete)/i;
|
|
222
|
+
const picked = lines.filter(line => actionPattern.test(line));
|
|
223
|
+
const use = picked.length > 0 ? picked : lines.slice(-20);
|
|
224
|
+
return use.slice(-30).join("\n").slice(-8000);
|
|
225
|
+
}
|
|
226
|
+
const WRITE_GATE_PROMPT_VERSION = "write-gate.v1.3.0";
|
|
227
|
+
const WRITE_GATE_REGRESSION_SAMPLES = [
|
|
228
|
+
"鏍蜂緥A: 鈥滀粖澶╄璁轰簡涓夌鏂规锛屽皻鏈喅绛栤€?=> active_only",
|
|
229
|
+
"鏍蜂緥B: 鈥滃喅瀹氶噰鐢˙鏂规骞跺畬鎴愪笂绾匡紝閿欒鐜囦笅闄嶅埌0.2%鈥?=> archive_event",
|
|
230
|
+
"鏍蜂緥C: 鈥滃ソ鐨勬敹鍒拌阿璋⑩€?=> skip",
|
|
231
|
+
];
|
|
232
|
+
function parseArchiveEventPayload(value) {
|
|
233
|
+
if (!value || typeof value !== "object") {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const obj = value;
|
|
237
|
+
const eventType = typeof obj.event_type === "string" ? obj.event_type.trim() : "";
|
|
238
|
+
const summary = typeof obj.summary === "string" ? obj.summary.trim() : "";
|
|
239
|
+
if (!eventType || !summary) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
const entities = Array.isArray(obj.entities)
|
|
243
|
+
? obj.entities.map(v => (typeof v === "string" ? v.trim() : "")).filter(Boolean)
|
|
244
|
+
: [];
|
|
245
|
+
const relations = Array.isArray(obj.relations)
|
|
246
|
+
? obj.relations
|
|
247
|
+
.map(valueItem => {
|
|
248
|
+
if (!valueItem || typeof valueItem !== "object")
|
|
249
|
+
return null;
|
|
250
|
+
const relation = valueItem;
|
|
251
|
+
const source = typeof relation.source === "string" ? relation.source.trim() : "";
|
|
252
|
+
const target = typeof relation.target === "string" ? relation.target.trim() : "";
|
|
253
|
+
const type = typeof relation.type === "string" ? relation.type.trim() : "related_to";
|
|
254
|
+
const evidenceSpan = typeof relation.evidence_span === "string" ? relation.evidence_span.trim() : "";
|
|
255
|
+
const confidence = typeof relation.confidence === "number"
|
|
256
|
+
? Math.max(0, Math.min(1, relation.confidence))
|
|
257
|
+
: undefined;
|
|
258
|
+
if (!source || !target)
|
|
259
|
+
return null;
|
|
260
|
+
return { source, target, type, evidence_span: evidenceSpan || undefined, confidence };
|
|
261
|
+
})
|
|
262
|
+
.filter(Boolean)
|
|
263
|
+
: [];
|
|
264
|
+
const entity_types = typeof obj.entity_types === "object" && obj.entity_types !== null && !Array.isArray(obj.entity_types)
|
|
265
|
+
? Object.fromEntries(Object.entries(obj.entity_types)
|
|
266
|
+
.filter(([key, value]) => typeof key === "string" && key.trim().length > 0 && typeof value === "string" && value.trim().length > 0)
|
|
267
|
+
.map(([key, value]) => [key.trim(), value.trim()]))
|
|
268
|
+
: undefined;
|
|
269
|
+
return {
|
|
270
|
+
event_type: eventType,
|
|
271
|
+
summary,
|
|
272
|
+
entities,
|
|
273
|
+
entity_types,
|
|
274
|
+
relations,
|
|
275
|
+
outcome: typeof obj.outcome === "string" ? obj.outcome.trim() : "",
|
|
276
|
+
confidence: typeof obj.confidence === "number" ? Math.max(0, Math.min(1, obj.confidence)) : 0.6,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function parseGraphPayload(value) {
|
|
280
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
const obj = value;
|
|
284
|
+
const entities = Array.isArray(obj.entities)
|
|
285
|
+
? obj.entities.map(v => (typeof v === "string" ? v.trim() : "")).filter(Boolean)
|
|
286
|
+
: [];
|
|
287
|
+
const entity_types = typeof obj.entity_types === "object" && obj.entity_types !== null && !Array.isArray(obj.entity_types)
|
|
288
|
+
? Object.fromEntries(Object.entries(obj.entity_types)
|
|
289
|
+
.filter(([key, val]) => typeof key === "string" && key.trim() && typeof val === "string" && val.trim())
|
|
290
|
+
.map(([key, val]) => [key.trim(), val.trim()]))
|
|
291
|
+
: undefined;
|
|
292
|
+
const relations = Array.isArray(obj.relations)
|
|
293
|
+
? obj.relations
|
|
294
|
+
.map(item => {
|
|
295
|
+
if (!item || typeof item !== "object")
|
|
296
|
+
return null;
|
|
297
|
+
const rel = item;
|
|
298
|
+
const source = typeof rel.source === "string" ? rel.source.trim() : "";
|
|
299
|
+
const target = typeof rel.target === "string" ? rel.target.trim() : "";
|
|
300
|
+
const type = typeof rel.type === "string" && rel.type.trim() ? rel.type.trim() : "related_to";
|
|
301
|
+
if (!source || !target)
|
|
302
|
+
return null;
|
|
303
|
+
const evidenceSpan = typeof rel.evidence_span === "string" ? rel.evidence_span.trim() : "";
|
|
304
|
+
const confidence = typeof rel.confidence === "number"
|
|
305
|
+
? Math.max(0, Math.min(1, rel.confidence))
|
|
306
|
+
: undefined;
|
|
307
|
+
return {
|
|
308
|
+
source,
|
|
309
|
+
target,
|
|
310
|
+
type,
|
|
311
|
+
...(evidenceSpan ? { evidence_span: evidenceSpan } : {}),
|
|
312
|
+
...(typeof confidence === "number" ? { confidence } : {}),
|
|
313
|
+
};
|
|
314
|
+
})
|
|
315
|
+
.filter((item) => item !== null)
|
|
316
|
+
: [];
|
|
317
|
+
if (entities.length === 0 || relations.length === 0) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
entities,
|
|
322
|
+
entity_types,
|
|
323
|
+
relations,
|
|
324
|
+
confidence: typeof obj.confidence === "number" ? Math.max(0, Math.min(1, obj.confidence)) : undefined,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function parseLlmGateDecisions(raw, logger) {
|
|
328
|
+
const validation = (0, llm_output_validator_1.validateLlmJsonOutput)(raw);
|
|
329
|
+
if (!validation.valid) {
|
|
330
|
+
if (logger) {
|
|
331
|
+
logger.warn(`quality_gate_decisions_invalid errors=${validation.errors.join("|")}`);
|
|
332
|
+
}
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
if (validation.warnings.length > 0 && logger) {
|
|
336
|
+
logger.debug(`quality_gate_decisions_warnings warnings=${validation.warnings.join("|")}`);
|
|
337
|
+
}
|
|
338
|
+
const root = validation.data;
|
|
339
|
+
const parsed = Array.isArray(root) ? root : root;
|
|
340
|
+
const output = [];
|
|
341
|
+
const pushDecision = (obj, target) => {
|
|
342
|
+
let event = null;
|
|
343
|
+
if (target === "archive_event") {
|
|
344
|
+
const eventValidation = (0, llm_output_validator_1.validateArchiveEvent)(obj.event || obj);
|
|
345
|
+
if (eventValidation.valid && eventValidation.cleaned) {
|
|
346
|
+
event = {
|
|
347
|
+
event_type: eventValidation.cleaned.event_type || "insight",
|
|
348
|
+
summary: eventValidation.cleaned.summary,
|
|
349
|
+
entities: eventValidation.cleaned.entities,
|
|
350
|
+
entity_types: eventValidation.cleaned.entity_types,
|
|
351
|
+
relations: eventValidation.cleaned.relations,
|
|
352
|
+
outcome: eventValidation.cleaned.outcome || "",
|
|
353
|
+
confidence: eventValidation.cleaned.confidence,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
if (logger) {
|
|
358
|
+
logger.warn(`quality_event_invalid errors=${eventValidation.errors.join("|")}`);
|
|
359
|
+
}
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
output.push({
|
|
364
|
+
candidate_id: typeof obj.candidate_id === "string" ? obj.candidate_id.trim() : "",
|
|
365
|
+
target_layer: target,
|
|
366
|
+
active_text: typeof obj.active_text === "string" ? obj.active_text.trim() : "",
|
|
367
|
+
event: event || undefined,
|
|
368
|
+
graph: parseGraphPayload(obj.graph) || undefined,
|
|
369
|
+
reason: typeof obj.reason === "string" ? obj.reason.trim() : "",
|
|
370
|
+
});
|
|
371
|
+
};
|
|
372
|
+
if (Array.isArray(parsed)) {
|
|
373
|
+
if (logger) {
|
|
374
|
+
logger.warn("quality_gate_decisions_invalid format=array_not_supported_require_routing_plan");
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else if (parsed && typeof parsed === "object") {
|
|
378
|
+
const rootObj = parsed;
|
|
379
|
+
const routingPlan = (typeof rootObj.routing_plan === "object" && rootObj.routing_plan !== null)
|
|
380
|
+
? rootObj.routing_plan
|
|
381
|
+
: null;
|
|
382
|
+
if (routingPlan) {
|
|
383
|
+
const buckets = [
|
|
384
|
+
{ key: "archive_event", items: routingPlan.archive_event },
|
|
385
|
+
{ key: "active_only", items: routingPlan.active_only },
|
|
386
|
+
{ key: "skip", items: routingPlan.skip },
|
|
387
|
+
];
|
|
388
|
+
for (const bucket of buckets) {
|
|
389
|
+
if (!Array.isArray(bucket.items))
|
|
390
|
+
continue;
|
|
391
|
+
for (const item of bucket.items) {
|
|
392
|
+
if (!item || typeof item !== "object")
|
|
393
|
+
continue;
|
|
394
|
+
pushDecision(item, bucket.key);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
else if (logger) {
|
|
399
|
+
logger.warn("quality_gate_decisions_invalid missing_routing_plan");
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (output.length === 0 && logger) {
|
|
403
|
+
logger.warn("quality_gate_decisions_empty");
|
|
404
|
+
}
|
|
405
|
+
const deduped = [];
|
|
406
|
+
const seen = new Set();
|
|
407
|
+
for (const item of output) {
|
|
408
|
+
const key = `${item.target_layer}|${item.event?.summary || item.active_text || item.reason || ""}`;
|
|
409
|
+
if (seen.has(key))
|
|
410
|
+
continue;
|
|
411
|
+
seen.add(key);
|
|
412
|
+
deduped.push(item);
|
|
413
|
+
}
|
|
414
|
+
return deduped;
|
|
415
|
+
}
|
|
416
|
+
async function extractGateDecisionsWithLlm(args) {
|
|
417
|
+
const endpoint = args.llm.baseUrl.endsWith("/chat/completions")
|
|
418
|
+
? args.llm.baseUrl
|
|
419
|
+
: `${args.llm.baseUrl}/chat/completions`;
|
|
420
|
+
const body = {
|
|
421
|
+
model: args.llm.model,
|
|
422
|
+
temperature: 0.1,
|
|
423
|
+
response_format: { type: "json_object" },
|
|
424
|
+
messages: [
|
|
425
|
+
{ role: "system", content: "You are a memory write-gate router. Output JSON only." },
|
|
426
|
+
{
|
|
427
|
+
role: "user",
|
|
428
|
+
content: [
|
|
429
|
+
`prompt_version=${WRITE_GATE_PROMPT_VERSION}`,
|
|
430
|
+
"Execute in 3 stages:",
|
|
431
|
+
"Stage1) Split transcript into candidate_events[]. One candidate should contain one principal event only.",
|
|
432
|
+
"Stage2) Route each candidate_event into target_layer: active_only | archive_event | skip.",
|
|
433
|
+
"Stage3) Output routing plan only. Do NOT claim data has been written.",
|
|
434
|
+
"Classification:",
|
|
435
|
+
"A) active_only: process context, ongoing discussion, temporary status, no stable conclusion.",
|
|
436
|
+
"B) archive_event: reusable event with clear subject + action/decision + outcome/phase conclusion.",
|
|
437
|
+
"C) skip: noise/repetition/chitchat/no clear business value.",
|
|
438
|
+
"Constraints:",
|
|
439
|
+
"- For archive_event: if confidence < 0.35, prefer skip.",
|
|
440
|
+
"- Relations must be grounded in source text. Do not fabricate.",
|
|
441
|
+
"- For active_only: active_text is required.",
|
|
442
|
+
"- Optional graph for active_only: graph={entities[],entity_types,relations[],confidence}.",
|
|
443
|
+
"- For relations: each relation should include source,target,type,evidence_span,confidence.",
|
|
444
|
+
"- If evidence_span or confidence is missing, do not output that relation.",
|
|
445
|
+
"Output JSON schema:",
|
|
446
|
+
"{\"candidate_events\":[{\"candidate_id\":\"c1\",\"span\":\"...\",\"normalized_text\":\"...\"}],\"routing_plan\":{\"archive_event\":[{\"candidate_id\":\"c1\",\"event\":{\"event_type\":\"decision\",\"summary\":\"...\",\"entities\":[\"A\"],\"entity_types\":{\"A\":\"Project\"},\"relations\":[],\"outcome\":\"...\",\"confidence\":0.82}}],\"active_only\":[],\"skip\":[]}}",
|
|
447
|
+
"routing_plan.archive_event[] item: {candidate_id,event}",
|
|
448
|
+
"routing_plan.active_only[] item: {candidate_id,active_text,graph}",
|
|
449
|
+
"routing_plan.skip[] item: {candidate_id,reason}",
|
|
450
|
+
...WRITE_GATE_REGRESSION_SAMPLES,
|
|
451
|
+
"Output JSON only.",
|
|
452
|
+
"",
|
|
453
|
+
buildEventSnippet(args.transcript),
|
|
454
|
+
].join("\n"),
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
};
|
|
458
|
+
let lastError = null;
|
|
459
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
460
|
+
const controller = new AbortController();
|
|
461
|
+
const timeoutId = setTimeout(() => controller.abort(), 25000);
|
|
462
|
+
try {
|
|
463
|
+
const response = await fetch(endpoint, {
|
|
464
|
+
method: "POST",
|
|
465
|
+
headers: {
|
|
466
|
+
"content-type": "application/json",
|
|
467
|
+
authorization: `Bearer ${args.llm.apiKey}`,
|
|
468
|
+
},
|
|
469
|
+
body: JSON.stringify(body),
|
|
470
|
+
signal: controller.signal,
|
|
471
|
+
});
|
|
472
|
+
clearTimeout(timeoutId);
|
|
473
|
+
if (!response.ok) {
|
|
474
|
+
lastError = new Error(`sync_llm_http_${response.status}`);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const json = await response.json();
|
|
478
|
+
const content = json?.choices?.[0]?.message?.content || "";
|
|
479
|
+
if (!content.trim()) {
|
|
480
|
+
lastError = new Error("sync_llm_empty");
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
return parseLlmGateDecisions(content, args.logger);
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
clearTimeout(timeoutId);
|
|
487
|
+
lastError = error;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
args.logger.warn(`Sync LLM extraction failed: ${String(lastError || "unknown")}`);
|
|
491
|
+
return [];
|
|
492
|
+
}
|
|
146
493
|
function createSessionSync(options) {
|
|
147
494
|
const memoryRoot = options.dbPath ? path.resolve(options.dbPath) : path.join(options.projectRoot, "data", "memory");
|
|
148
495
|
const statePath = path.join(memoryRoot, ".sync_state.json");
|
|
149
496
|
const openclawBasePath = inferOpenclawBasePath(options.projectRoot);
|
|
497
|
+
const llmModel = options.llm?.model || "";
|
|
498
|
+
const llmApiKey = options.llm?.apiKey || "";
|
|
499
|
+
const llmBaseUrl = normalizeBaseUrl(options.llm?.baseURL || options.llm?.baseUrl);
|
|
500
|
+
const requireLlmForWrite = options.requireLlmForWrite !== false;
|
|
501
|
+
options.logger.info(`sync_gate_prompt_version=${WRITE_GATE_PROMPT_VERSION}`);
|
|
502
|
+
if (!fs.existsSync(statePath)) {
|
|
503
|
+
options.logger.warn("sync_state_missing: deleting state file triggers full re-import");
|
|
504
|
+
}
|
|
505
|
+
async function storeFromTranscript(args) {
|
|
506
|
+
const skipReasons = {};
|
|
507
|
+
const activeTextMaxChars = typeof options.writePolicy?.activeTextMaxChars === "number"
|
|
508
|
+
? Math.max(500, Math.min(20000, Math.floor(options.writePolicy.activeTextMaxChars)))
|
|
509
|
+
: 4000;
|
|
510
|
+
const archiveSourceTextMaxChars = typeof options.writePolicy?.archiveSourceTextMaxChars === "number"
|
|
511
|
+
? Math.max(1000, Math.min(30000, Math.floor(options.writePolicy.archiveSourceTextMaxChars)))
|
|
512
|
+
: 8000;
|
|
513
|
+
function bumpReason(reason) {
|
|
514
|
+
const key = reason || "unknown";
|
|
515
|
+
skipReasons[key] = (skipReasons[key] || 0) + 1;
|
|
516
|
+
}
|
|
517
|
+
if (!args.transcript.trim()) {
|
|
518
|
+
options.logger.info(`sync_skip reason=no_active_records session=${args.sessionId}`);
|
|
519
|
+
bumpReason("no_active_records");
|
|
520
|
+
return { imported: 0, skipped: 1, ok: true, llmDecisions: 0, activeOnly: 0, archiveEvent: 0, skipReasons };
|
|
521
|
+
}
|
|
522
|
+
if (!llmModel || !llmApiKey || !llmBaseUrl) {
|
|
523
|
+
if (requireLlmForWrite) {
|
|
524
|
+
options.logger.warn(`sync_skip reason=llm_not_configured session=${args.sessionId}`);
|
|
525
|
+
bumpReason("llm_not_configured");
|
|
526
|
+
return { imported: 0, skipped: 1, ok: false, llmDecisions: 0, activeOnly: 0, archiveEvent: 0, skipReasons };
|
|
527
|
+
}
|
|
528
|
+
options.logger.warn(`Sync gate degraded to active_only for ${args.sessionId}: llm_not_configured`);
|
|
529
|
+
const fallbackWrite = await options.writeStore.writeMemory({
|
|
530
|
+
text: args.transcript.slice(-activeTextMaxChars),
|
|
531
|
+
role: "system",
|
|
532
|
+
source: `sync_gate_fallback:${args.sourceFile}`,
|
|
533
|
+
sessionId: args.sessionId,
|
|
534
|
+
});
|
|
535
|
+
if (fallbackWrite.status === "ok") {
|
|
536
|
+
return { imported: 1, skipped: 0, ok: true, llmDecisions: 1, activeOnly: 1, archiveEvent: 0, skipReasons };
|
|
537
|
+
}
|
|
538
|
+
bumpReason(fallbackWrite.reason || "active_only_fallback_failed");
|
|
539
|
+
return { imported: 0, skipped: 1, ok: false, llmDecisions: 1, activeOnly: 0, archiveEvent: 0, skipReasons };
|
|
540
|
+
}
|
|
541
|
+
const decisions = await extractGateDecisionsWithLlm({
|
|
542
|
+
llm: { model: llmModel, apiKey: llmApiKey, baseUrl: llmBaseUrl },
|
|
543
|
+
transcript: args.transcript,
|
|
544
|
+
logger: options.logger,
|
|
545
|
+
});
|
|
546
|
+
if (decisions.length === 0) {
|
|
547
|
+
options.logger.info(`sync_skip reason=llm_extract_empty session=${args.sessionId}`);
|
|
548
|
+
bumpReason("llm_extract_empty");
|
|
549
|
+
return { imported: 0, skipped: 1, ok: true, llmDecisions: 0, activeOnly: 0, archiveEvent: 0, skipReasons };
|
|
550
|
+
}
|
|
551
|
+
let llmDecisions = 0;
|
|
552
|
+
let imported = 0;
|
|
553
|
+
let skipped = 0;
|
|
554
|
+
let activeOnly = 0;
|
|
555
|
+
let archiveEvent = 0;
|
|
556
|
+
let activeAttempted = 0;
|
|
557
|
+
let archiveAttempted = 0;
|
|
558
|
+
let graphAttempted = 0;
|
|
559
|
+
let graphStored = 0;
|
|
560
|
+
let graphSkipped = 0;
|
|
561
|
+
const archiveInputs = [];
|
|
562
|
+
for (const decision of decisions) {
|
|
563
|
+
llmDecisions += 1;
|
|
564
|
+
if (decision.target_layer === "skip") {
|
|
565
|
+
skipped += 1;
|
|
566
|
+
bumpReason(decision.reason || "llm_gate_skip");
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (decision.target_layer === "active_only") {
|
|
570
|
+
activeAttempted += 1;
|
|
571
|
+
const activeText = (decision.active_text || args.transcript).trim().slice(-activeTextMaxChars);
|
|
572
|
+
if (!activeText) {
|
|
573
|
+
skipped += 1;
|
|
574
|
+
bumpReason("active_only_empty");
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
const writeResult = await options.writeStore.writeMemory({
|
|
578
|
+
text: activeText,
|
|
579
|
+
role: "system",
|
|
580
|
+
source: `sync_gate_active:${args.sourceFile}`,
|
|
581
|
+
sessionId: args.sessionId,
|
|
582
|
+
});
|
|
583
|
+
if (writeResult.status === "ok") {
|
|
584
|
+
imported += 1;
|
|
585
|
+
activeOnly += 1;
|
|
586
|
+
if (options.graphMemoryStore && decision.graph) {
|
|
587
|
+
graphAttempted += 1;
|
|
588
|
+
const relationFingerprint = (decision.graph.relations || [])
|
|
589
|
+
.map(rel => `${rel.source}|${rel.type}|${rel.target}|${rel.evidence_span || ""}`)
|
|
590
|
+
.sort()
|
|
591
|
+
.join("||");
|
|
592
|
+
const activeSourceEventId = `active:${args.sessionId}:${crypto.createHash("sha1").update(relationFingerprint || activeText).digest("hex").slice(0, 16)}`;
|
|
593
|
+
const graphResult = await options.graphMemoryStore.append({
|
|
594
|
+
sourceEventId: activeSourceEventId,
|
|
595
|
+
sourceLayer: "active_only",
|
|
596
|
+
sessionId: args.sessionId,
|
|
597
|
+
sourceFile: args.sourceFile,
|
|
598
|
+
eventType: "insight",
|
|
599
|
+
entities: decision.graph.entities,
|
|
600
|
+
entity_types: decision.graph.entity_types,
|
|
601
|
+
relations: decision.graph.relations,
|
|
602
|
+
gateSource: "sync",
|
|
603
|
+
confidence: decision.graph.confidence,
|
|
604
|
+
sourceText: activeText,
|
|
605
|
+
});
|
|
606
|
+
if (!graphResult.success) {
|
|
607
|
+
graphSkipped += 1;
|
|
608
|
+
options.logger.info(`graph_skip_reason=${graphResult.reason} source_event_id=${activeSourceEventId}`);
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
graphStored += 1;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
skipped += 1;
|
|
617
|
+
bumpReason(writeResult.reason || "active_only_write_skipped");
|
|
618
|
+
}
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
if (decision.target_layer === "archive_event") {
|
|
622
|
+
archiveAttempted += 1;
|
|
623
|
+
if (!decision.event) {
|
|
624
|
+
skipped += 1;
|
|
625
|
+
bumpReason("archive_event_missing_payload");
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
archiveInputs.push({
|
|
629
|
+
event_type: decision.event.event_type,
|
|
630
|
+
summary: decision.event.summary,
|
|
631
|
+
entities: decision.event.entities,
|
|
632
|
+
relations: decision.event.relations,
|
|
633
|
+
entity_types: decision.event.entity_types,
|
|
634
|
+
outcome: decision.event.outcome,
|
|
635
|
+
confidence: decision.event.confidence,
|
|
636
|
+
session_id: args.sessionId,
|
|
637
|
+
source_file: args.sourceFile,
|
|
638
|
+
source_text: args.transcript.slice(-archiveSourceTextMaxChars),
|
|
639
|
+
source_event_id: decision.candidate_id
|
|
640
|
+
? `candidate:${args.sessionId}:${decision.candidate_id}`
|
|
641
|
+
: `candidate:${args.sessionId}:${crypto.createHash("sha1").update(decision.event.summary).digest("hex").slice(0, 16)}`,
|
|
642
|
+
actor: "sync_llm_gate",
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (archiveInputs.length > 0) {
|
|
647
|
+
let archivedSuccess = 0;
|
|
648
|
+
let archivedSkipped = 0;
|
|
649
|
+
for (const inputRecord of archiveInputs) {
|
|
650
|
+
const archiveResult = await options.archiveStore.storeEvents([inputRecord]);
|
|
651
|
+
imported += archiveResult.stored.length;
|
|
652
|
+
skipped += archiveResult.skipped.length;
|
|
653
|
+
archiveEvent += archiveResult.stored.length;
|
|
654
|
+
archivedSuccess += archiveResult.stored.length;
|
|
655
|
+
archivedSkipped += archiveResult.skipped.length;
|
|
656
|
+
for (const skip of archiveResult.skipped) {
|
|
657
|
+
bumpReason(skip.reason || "archive_store_skipped");
|
|
658
|
+
}
|
|
659
|
+
const archiveRecord = archiveResult.stored[0];
|
|
660
|
+
if (!archiveRecord) {
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (!options.graphMemoryStore) {
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
graphAttempted += 1;
|
|
667
|
+
const graphResult = await options.graphMemoryStore.append({
|
|
668
|
+
// Graph trace points to persisted archive record id for stable lookup.
|
|
669
|
+
sourceEventId: archiveRecord.id,
|
|
670
|
+
sourceLayer: "archive_event",
|
|
671
|
+
archiveEventId: archiveRecord.id,
|
|
672
|
+
sessionId: args.sessionId,
|
|
673
|
+
sourceFile: args.sourceFile,
|
|
674
|
+
eventType: inputRecord.event_type,
|
|
675
|
+
entities: inputRecord.entities,
|
|
676
|
+
entity_types: inputRecord.entity_types,
|
|
677
|
+
relations: inputRecord.relations,
|
|
678
|
+
gateSource: "sync",
|
|
679
|
+
confidence: inputRecord.confidence,
|
|
680
|
+
sourceText: args.transcript,
|
|
681
|
+
});
|
|
682
|
+
if (!graphResult.success) {
|
|
683
|
+
graphSkipped += 1;
|
|
684
|
+
options.logger.info(`graph_skip_reason=${graphResult.reason} source_event_id=${archiveRecord.id}`);
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
graphStored += 1;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
options.logger.info(`sync_archive_result session=${args.sessionId} archived_success=${archivedSuccess} skipped=${archivedSkipped}`);
|
|
691
|
+
}
|
|
692
|
+
options.logger.info(`sync_gate_result session=${args.sessionId} llm_decisions=${llmDecisions} active_only=${activeOnly} archive_event=${archiveEvent} skipped=${skipped}`);
|
|
693
|
+
options.logger.info(`sync_gate_metrics session=${args.sessionId} active_attempted=${activeAttempted} archive_attempted=${archiveAttempted} graph_attempted=${graphAttempted} graph_stored=${graphStored} graph_skipped=${graphSkipped} skip_reason_kinds=${Object.keys(skipReasons).length}`);
|
|
694
|
+
return {
|
|
695
|
+
imported,
|
|
696
|
+
skipped,
|
|
697
|
+
ok: true,
|
|
698
|
+
llmDecisions,
|
|
699
|
+
activeOnly,
|
|
700
|
+
archiveEvent,
|
|
701
|
+
skipReasons,
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
async function syncDailySummaries() {
|
|
705
|
+
const files = gatherDailySummaryFiles(openclawBasePath);
|
|
706
|
+
const state = readState(statePath);
|
|
707
|
+
if (!state.markdowns || typeof state.markdowns !== "object") {
|
|
708
|
+
state.markdowns = {};
|
|
709
|
+
}
|
|
710
|
+
let imported = 0;
|
|
711
|
+
let skipped = 0;
|
|
712
|
+
let filesProcessed = 0;
|
|
713
|
+
let llmDecisions = 0;
|
|
714
|
+
let activeOnly = 0;
|
|
715
|
+
let archiveEvent = 0;
|
|
716
|
+
const skipReasons = {};
|
|
717
|
+
for (const filePath of files) {
|
|
718
|
+
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
722
|
+
const digest = crypto.createHash("sha1").update(content).digest("hex");
|
|
723
|
+
const prev = state.markdowns[filePath];
|
|
724
|
+
if (prev && prev.digest === digest) {
|
|
725
|
+
skipped += 1;
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
const chunks = parseDailySummary(content);
|
|
729
|
+
if (chunks.length === 0) {
|
|
730
|
+
state.markdowns[filePath] = { digest, importedAt: new Date().toISOString() };
|
|
731
|
+
skipped += 1;
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
const summarySessionId = `daily_summary:${path.basename(filePath)}`;
|
|
735
|
+
const transcript = chunks.join("\n");
|
|
736
|
+
const result = await storeFromTranscript({
|
|
737
|
+
sessionId: summarySessionId,
|
|
738
|
+
sourceFile: `daily_summary_sync:${path.basename(filePath)}`,
|
|
739
|
+
transcript,
|
|
740
|
+
});
|
|
741
|
+
imported += result.imported;
|
|
742
|
+
skipped += result.skipped;
|
|
743
|
+
llmDecisions += result.llmDecisions;
|
|
744
|
+
activeOnly += result.activeOnly;
|
|
745
|
+
archiveEvent += result.archiveEvent;
|
|
746
|
+
for (const [key, count] of Object.entries(result.skipReasons)) {
|
|
747
|
+
skipReasons[key] = (skipReasons[key] || 0) + count;
|
|
748
|
+
}
|
|
749
|
+
if (!result.ok) {
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
state.markdowns[filePath] = { digest, importedAt: new Date().toISOString() };
|
|
753
|
+
filesProcessed += 1;
|
|
754
|
+
}
|
|
755
|
+
writeState(statePath, state);
|
|
756
|
+
options.logger.info(`TS daily summary sync completed: imported=${imported}, skipped=${skipped}, files=${filesProcessed}`);
|
|
757
|
+
return { imported, skipped, filesProcessed, llmDecisions, activeOnly, archiveEvent, skipReasons };
|
|
758
|
+
}
|
|
150
759
|
async function syncMemory() {
|
|
151
760
|
const files = gatherSessionFiles(openclawBasePath, memoryRoot);
|
|
761
|
+
if (files.length === 0) {
|
|
762
|
+
options.logger.info("sync_skip reason=no_active_records");
|
|
763
|
+
}
|
|
152
764
|
const state = readState(statePath);
|
|
153
765
|
let imported = 0;
|
|
154
766
|
let skipped = 0;
|
|
155
767
|
let filesProcessed = 0;
|
|
768
|
+
let llmDecisions = 0;
|
|
769
|
+
let activeOnly = 0;
|
|
770
|
+
let archiveEvent = 0;
|
|
771
|
+
const skipReasons = {};
|
|
156
772
|
for (const filePath of files) {
|
|
157
773
|
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
158
774
|
continue;
|
|
@@ -169,6 +785,8 @@ function createSessionSync(options) {
|
|
|
169
785
|
state.files[filePath] = { size: stat.size, lineCount: lines.length };
|
|
170
786
|
continue;
|
|
171
787
|
}
|
|
788
|
+
const bySession = new Map();
|
|
789
|
+
let fileHasFailure = false;
|
|
172
790
|
for (let i = startIndex; i < lines.length; i++) {
|
|
173
791
|
const line = lines[i].trim();
|
|
174
792
|
if (!line)
|
|
@@ -183,18 +801,10 @@ function createSessionSync(options) {
|
|
|
183
801
|
}
|
|
184
802
|
const sessionId = getSessionId(record, `${path.basename(filePath)}:${hash}`);
|
|
185
803
|
for (const msg of messages) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
role: msg.role,
|
|
189
|
-
source: "sync",
|
|
190
|
-
sessionId,
|
|
191
|
-
});
|
|
192
|
-
if (result.status === "ok") {
|
|
193
|
-
imported += 1;
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
skipped += 1;
|
|
804
|
+
if (!bySession.has(sessionId)) {
|
|
805
|
+
bySession.set(sessionId, []);
|
|
197
806
|
}
|
|
807
|
+
bySession.get(sessionId)?.push(`[${msg.role}] ${msg.text}`);
|
|
198
808
|
}
|
|
199
809
|
}
|
|
200
810
|
catch (error) {
|
|
@@ -202,13 +812,54 @@ function createSessionSync(options) {
|
|
|
202
812
|
skipped += 1;
|
|
203
813
|
}
|
|
204
814
|
}
|
|
815
|
+
for (const [sessionId, messages] of bySession.entries()) {
|
|
816
|
+
const transcript = messages.join("\n");
|
|
817
|
+
const result = await storeFromTranscript({
|
|
818
|
+
sessionId,
|
|
819
|
+
sourceFile: `sync:${path.basename(filePath)}`,
|
|
820
|
+
transcript,
|
|
821
|
+
});
|
|
822
|
+
imported += result.imported;
|
|
823
|
+
skipped += result.skipped;
|
|
824
|
+
llmDecisions += result.llmDecisions;
|
|
825
|
+
activeOnly += result.activeOnly;
|
|
826
|
+
archiveEvent += result.archiveEvent;
|
|
827
|
+
for (const [key, count] of Object.entries(result.skipReasons)) {
|
|
828
|
+
skipReasons[key] = (skipReasons[key] || 0) + count;
|
|
829
|
+
}
|
|
830
|
+
if (!result.ok) {
|
|
831
|
+
fileHasFailure = true;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
205
834
|
filesProcessed += 1;
|
|
206
|
-
|
|
835
|
+
if (!fileHasFailure) {
|
|
836
|
+
state.files[filePath] = { size: stat.size, lineCount: lines.length };
|
|
837
|
+
}
|
|
207
838
|
}
|
|
208
839
|
writeState(statePath, state);
|
|
209
|
-
|
|
210
|
-
|
|
840
|
+
const summary = await syncDailySummaries();
|
|
841
|
+
llmDecisions += summary.llmDecisions;
|
|
842
|
+
activeOnly += summary.activeOnly;
|
|
843
|
+
archiveEvent += summary.archiveEvent;
|
|
844
|
+
for (const [key, count] of Object.entries(summary.skipReasons)) {
|
|
845
|
+
skipReasons[key] = (skipReasons[key] || 0) + count;
|
|
846
|
+
}
|
|
847
|
+
options.logger.info(`TS sync completed: imported=${imported}, skipped=${skipped}, files=${filesProcessed}, summaryImported=${summary.imported}, summarySkipped=${summary.skipped}, llmDecisions=${llmDecisions}, activeOnly=${activeOnly}, archiveEvent=${archiveEvent}`);
|
|
848
|
+
return {
|
|
849
|
+
imported,
|
|
850
|
+
skipped,
|
|
851
|
+
filesProcessed,
|
|
852
|
+
summaryImported: summary.imported,
|
|
853
|
+
summarySkipped: summary.skipped,
|
|
854
|
+
llmDecisions,
|
|
855
|
+
activeOnly,
|
|
856
|
+
archiveEvent,
|
|
857
|
+
skipReasons,
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
async function routeTranscript(args) {
|
|
861
|
+
return storeFromTranscript(args);
|
|
211
862
|
}
|
|
212
|
-
return { syncMemory };
|
|
863
|
+
return { syncMemory, syncDailySummaries, routeTranscript };
|
|
213
864
|
}
|
|
214
865
|
//# sourceMappingURL=session_sync.js.map
|