agenr 0.9.24 → 0.9.25
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/CHANGELOG.md +13 -0
- package/dist/cli-main.js +63 -11
- package/dist/openclaw-plugin/index.js +9 -0
- package/package.json +13 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.25 (2026-02-28)
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
- Health command now reads conflict data from conflict_log table instead of
|
|
7
|
+
the never-incremented entries.contradictions column. Shows total conflicts,
|
|
8
|
+
breakdown by relation type, pending count, and auto-resolved count. (#334)
|
|
9
|
+
- High-confidence coexists conflicts (>0.8) now auto-resolve instead of being
|
|
10
|
+
flagged for review. Previously, any coexists involving decision or lesson
|
|
11
|
+
types was flagged regardless of confidence, creating a false backlog. (#335)
|
|
12
|
+
- OpenClaw plugin: system messages (e.g. subagent completions) now classify
|
|
13
|
+
as trivial for mid-session recall, preventing wasted embedding API calls
|
|
14
|
+
on garbage queries containing session IDs and boilerplate text.
|
|
15
|
+
|
|
3
16
|
## 0.9.24 (2026-02-28)
|
|
4
17
|
|
|
5
18
|
### Bug Fixes
|
package/dist/cli-main.js
CHANGED
|
@@ -5486,6 +5486,38 @@ async function getConflictStats(db) {
|
|
|
5486
5486
|
userResolved: Number.isFinite(userResolvedRaw) ? userResolvedRaw : 0
|
|
5487
5487
|
};
|
|
5488
5488
|
}
|
|
5489
|
+
async function getConflictLogSummary(db) {
|
|
5490
|
+
const result = await db.execute({
|
|
5491
|
+
sql: `
|
|
5492
|
+
SELECT relation, resolution, COUNT(*) as cnt
|
|
5493
|
+
FROM conflict_log
|
|
5494
|
+
GROUP BY relation, resolution
|
|
5495
|
+
`
|
|
5496
|
+
});
|
|
5497
|
+
const byRelation = {};
|
|
5498
|
+
const byResolution = {};
|
|
5499
|
+
let total = 0;
|
|
5500
|
+
for (const row of result.rows) {
|
|
5501
|
+
const record = row;
|
|
5502
|
+
const relation = toStringValue(record.relation);
|
|
5503
|
+
const resolution = toStringValue(record.resolution);
|
|
5504
|
+
const countRaw = toNumber(record.cnt);
|
|
5505
|
+
const count = Number.isFinite(countRaw) ? countRaw : 0;
|
|
5506
|
+
total += count;
|
|
5507
|
+
if (relation) {
|
|
5508
|
+
byRelation[relation] = (byRelation[relation] ?? 0) + count;
|
|
5509
|
+
}
|
|
5510
|
+
if (resolution) {
|
|
5511
|
+
byResolution[resolution] = (byResolution[resolution] ?? 0) + count;
|
|
5512
|
+
}
|
|
5513
|
+
}
|
|
5514
|
+
return {
|
|
5515
|
+
total,
|
|
5516
|
+
byRelation,
|
|
5517
|
+
byResolution,
|
|
5518
|
+
pending: byResolution.pending ?? 0
|
|
5519
|
+
};
|
|
5520
|
+
}
|
|
5489
5521
|
|
|
5490
5522
|
// src/db/contradiction.ts
|
|
5491
5523
|
var CONTRADICTION_JUDGE_SYSTEM_PROMPT = [
|
|
@@ -5805,6 +5837,20 @@ async function resolveConflict(db, newEntryId, newEntry, conflict, subjectIndex,
|
|
|
5805
5837
|
reason: "temporal type with high confidence supersession"
|
|
5806
5838
|
};
|
|
5807
5839
|
}
|
|
5840
|
+
if (conflict.result.relation === "coexists" && conflict.result.confidence > 0.8) {
|
|
5841
|
+
await logConflict(
|
|
5842
|
+
db,
|
|
5843
|
+
newEntryId,
|
|
5844
|
+
conflict.existingEntryId,
|
|
5845
|
+
"coexists",
|
|
5846
|
+
conflict.result.confidence,
|
|
5847
|
+
"coexist"
|
|
5848
|
+
);
|
|
5849
|
+
console.error(
|
|
5850
|
+
`[contradiction] resolution: coexist (high-confidence) entry=${conflict.existingEntryId.slice(0, 8)} (confidence=${conflict.result.confidence.toFixed(2)})`
|
|
5851
|
+
);
|
|
5852
|
+
return { action: "coexist", reason: "high-confidence coexistence" };
|
|
5853
|
+
}
|
|
5808
5854
|
if (conflict.result.relation === "contradicts" || conflict.result.confidence <= 0.75 || conflict.existingType === "decision" || conflict.existingType === "lesson") {
|
|
5809
5855
|
await logConflict(
|
|
5810
5856
|
db,
|
|
@@ -17711,7 +17757,6 @@ async function collectHealthStats(db, now, fileSizeBytes, forgettingThreshold, f
|
|
|
17711
17757
|
let recalledFivePlus = 0;
|
|
17712
17758
|
let forgettingCandidates = 0;
|
|
17713
17759
|
let forgettingProtected = 0;
|
|
17714
|
-
let contradictionFlags = 0;
|
|
17715
17760
|
let staleTodos = 0;
|
|
17716
17761
|
let qualityHigh = 0;
|
|
17717
17762
|
let qualityMedium = 0;
|
|
@@ -17749,9 +17794,6 @@ async function collectHealthStats(db, now, fileSizeBytes, forgettingThreshold, f
|
|
|
17749
17794
|
} else {
|
|
17750
17795
|
recalledFivePlus += 1;
|
|
17751
17796
|
}
|
|
17752
|
-
if (entry.contradictions > 0) {
|
|
17753
|
-
contradictionFlags += 1;
|
|
17754
|
-
}
|
|
17755
17797
|
if (entry.type === "todo" && ageDays2 > 30 && entry.recall_count <= 0) {
|
|
17756
17798
|
staleTodos += 1;
|
|
17757
17799
|
}
|
|
@@ -17810,6 +17852,7 @@ async function collectHealthStats(db, now, fileSizeBytes, forgettingThreshold, f
|
|
|
17810
17852
|
const totalPending = pendingByReason.reduce((sum, item) => sum + item.count, 0);
|
|
17811
17853
|
const oldestPendingCreatedAt = await getOldestPendingReviewCreatedAt(db);
|
|
17812
17854
|
const oldestPendingAgeDays = oldestPendingCreatedAt && totalPending > 0 ? parseDaysBetween(now, oldestPendingCreatedAt) : null;
|
|
17855
|
+
const conflictLog = await getConflictLogSummary(db);
|
|
17813
17856
|
return {
|
|
17814
17857
|
total,
|
|
17815
17858
|
todos,
|
|
@@ -17834,10 +17877,8 @@ async function collectHealthStats(db, now, fileSizeBytes, forgettingThreshold, f
|
|
|
17834
17877
|
estimatedFreedBytes: estimateFreedBytes2(forgettingCandidates, total, fileSizeBytes),
|
|
17835
17878
|
threshold: forgettingThreshold
|
|
17836
17879
|
},
|
|
17837
|
-
|
|
17838
|
-
|
|
17839
|
-
staleTodos
|
|
17840
|
-
},
|
|
17880
|
+
staleTodos,
|
|
17881
|
+
conflictLog,
|
|
17841
17882
|
quality: {
|
|
17842
17883
|
high: qualityHigh,
|
|
17843
17884
|
medium: qualityMedium,
|
|
@@ -17871,6 +17912,12 @@ function formatAgeDays(ageDays2) {
|
|
|
17871
17912
|
function renderHealthOutput(stats, now) {
|
|
17872
17913
|
const overTarget = (stats.fileSizeBytes ?? 0) > 200 * MB2;
|
|
17873
17914
|
const fileSizeNote = overTarget ? " (\u26A0 over 200MB target)" : "";
|
|
17915
|
+
const conflictByRelation = `coexists=${stats.conflictLog.byRelation.coexists ?? 0}, supersedes=${stats.conflictLog.byRelation.supersedes ?? 0}, contradicts=${stats.conflictLog.byRelation.contradicts ?? 0}`;
|
|
17916
|
+
const autoResolvedCoexist = stats.conflictLog.byResolution.coexist ?? 0;
|
|
17917
|
+
const autoResolvedSuperseded = stats.conflictLog.byResolution["auto-superseded"] ?? 0;
|
|
17918
|
+
const autoResolvedKeepBoth = stats.conflictLog.byResolution["keep-both"] ?? 0;
|
|
17919
|
+
const autoResolvedKeepNew = stats.conflictLog.byResolution["keep-new"] ?? 0;
|
|
17920
|
+
const autoResolvedTotal = autoResolvedCoexist + autoResolvedSuperseded + autoResolvedKeepBoth + autoResolvedKeepNew;
|
|
17874
17921
|
const lines = [
|
|
17875
17922
|
"DB Health \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
17876
17923
|
`Entries: ${formatInt(stats.total)} total | ${formatInt(stats.todos)} todos | ${formatInt(stats.preferences)} preferences`,
|
|
@@ -17892,9 +17939,14 @@ function renderHealthOutput(stats, now) {
|
|
|
17892
17939
|
`- score < ${stats.forgetting.threshold}: ${formatInt(stats.forgetting.candidates)} entries (would free ~${formatFileSize(stats.forgetting.estimatedFreedBytes)})`,
|
|
17893
17940
|
`- Protected: ${formatInt(stats.forgetting.protected)} entries`,
|
|
17894
17941
|
"",
|
|
17895
|
-
"
|
|
17896
|
-
`-
|
|
17897
|
-
`-
|
|
17942
|
+
"Conflict Log",
|
|
17943
|
+
`- Total conflicts: ${formatInt(stats.conflictLog.total)}`,
|
|
17944
|
+
`- By relation: ${conflictByRelation}`,
|
|
17945
|
+
`- Pending resolution: ${formatInt(stats.conflictLog.pending)}`,
|
|
17946
|
+
`- Auto-resolved: ${formatInt(autoResolvedTotal)} (coexist=${autoResolvedCoexist}, auto-superseded=${autoResolvedSuperseded}, keep-both=${autoResolvedKeepBoth}, keep-new=${autoResolvedKeepNew})`,
|
|
17947
|
+
"",
|
|
17948
|
+
"Stale Todos",
|
|
17949
|
+
`- Over 30d old, not recalled: ${formatInt(stats.staleTodos)}`,
|
|
17898
17950
|
"",
|
|
17899
17951
|
"Quality Score Distribution",
|
|
17900
17952
|
`- High (>= 0.7): ${formatInt(stats.quality.high)} entries`,
|
|
@@ -160,6 +160,9 @@ function normalizeBufferedMessage(text) {
|
|
|
160
160
|
return "";
|
|
161
161
|
}
|
|
162
162
|
if (trimmed.length > MAX_BUFFERED_MESSAGE_CHARS) {
|
|
163
|
+
if (trimmed.startsWith("[System Message]")) {
|
|
164
|
+
return "trivial";
|
|
165
|
+
}
|
|
163
166
|
return trimmed.slice(0, MAX_BUFFERED_MESSAGE_CHARS);
|
|
164
167
|
}
|
|
165
168
|
return trimmed;
|
|
@@ -230,6 +233,9 @@ function classifyMessage(text) {
|
|
|
230
233
|
if (!trimmed) {
|
|
231
234
|
return "trivial";
|
|
232
235
|
}
|
|
236
|
+
if (trimmed.startsWith("[System Message]")) {
|
|
237
|
+
return "trivial";
|
|
238
|
+
}
|
|
233
239
|
const words = trimmed.split(/\s+/).map((word) => word.replace(/^[.!?,;:]+|[.!?,;:]+$/g, "")).filter(Boolean);
|
|
234
240
|
const wordCount = words.length;
|
|
235
241
|
const lower = trimmed.toLowerCase();
|
|
@@ -272,6 +278,9 @@ function buildQuery(message) {
|
|
|
272
278
|
return "";
|
|
273
279
|
}
|
|
274
280
|
return trimmed.slice(0, MAX_QUERY_CHARS);
|
|
281
|
+
if (trimmed.startsWith("[System Message]")) {
|
|
282
|
+
return "trivial";
|
|
283
|
+
}
|
|
275
284
|
}
|
|
276
285
|
function tokenizeForSimilarity(text) {
|
|
277
286
|
const tokens = text.toLowerCase().split(/\s+/).map((token) => token.replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, "")).filter((token) => token.length > 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agenr",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.25",
|
|
4
4
|
"openclaw": {
|
|
5
5
|
"extensions": [
|
|
6
6
|
"dist/openclaw-plugin/index.js"
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
"bin": {
|
|
12
12
|
"agenr": "dist/cli.js"
|
|
13
13
|
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup src/cli.ts src/cli-main.ts src/openclaw-plugin/index.ts --format esm --dts",
|
|
16
|
+
"dev": "tsup src/cli.ts src/cli-main.ts --format esm --watch",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"typecheck": "tsc --noEmit"
|
|
20
|
+
},
|
|
14
21
|
"dependencies": {
|
|
15
22
|
"@clack/prompts": "^1.0.1",
|
|
16
23
|
"@libsql/client": "^0.17.0",
|
|
@@ -54,11 +61,9 @@
|
|
|
54
61
|
"README.md"
|
|
55
62
|
],
|
|
56
63
|
"author": "agenr-ai",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"test:watch": "vitest",
|
|
62
|
-
"typecheck": "tsc --noEmit"
|
|
64
|
+
"pnpm": {
|
|
65
|
+
"overrides": {
|
|
66
|
+
"fast-xml-parser": "^5.3.6"
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
|
-
}
|
|
69
|
+
}
|