agenr 0.8.12 → 0.8.15
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 +14 -0
- package/dist/cli-main.js +14 -5
- package/dist/openclaw-plugin/index.d.ts +1 -0
- package/dist/openclaw-plugin/index.js +79 -1
- package/package.json +8 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.15] - 2026-02-23
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- fix(consolidate): switch GROUP_CONCAT separator from comma to pipe in buildClusters to prevent silent tag corruption when tag values contain commas (issue #155)
|
|
7
|
+
- fix(consolidate): Tier 1 near-exact duplicate merge now preserves the highest importance across merged entries by raising the keeper's `importance` floor to the group max (issue #156)
|
|
8
|
+
- fix(consolidate): Tier 1 near-exact duplicate merge now preserves oldest provenance by inheriting the oldest `created_at` across the merge group into the keeper (issue #156)
|
|
9
|
+
- test(consolidate): new cluster.test.ts with pipe-separator roundtrip and comma-in-tag regression coverage (issue #155)
|
|
10
|
+
- test(consolidate): added merge coverage for tag union transfer, keeper importance floor, and keeper `created_at` inheritance in rules consolidation tests (issue #156)
|
|
11
|
+
|
|
12
|
+
## [0.8.13] - 2026-02-23
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- fix(openclaw-plugin): session-start recall now falls back to reading the most recent archived OpenClaw session file (`*.reset.*`) when webchat `/new` bypasses `before_reset`. If stash-based seeding is unavailable and the opening prompt is short (< 40 characters), recall query text is built from the last 3 user messages in the archived session.
|
|
16
|
+
|
|
3
17
|
## [0.8.12]
|
|
4
18
|
|
|
5
19
|
### Fixed
|
package/dist/cli-main.js
CHANGED
|
@@ -6540,7 +6540,7 @@ async function mergeNearExactDuplicates(db, options) {
|
|
|
6540
6540
|
args.push(...projectSql.args);
|
|
6541
6541
|
const result = await db.execute({
|
|
6542
6542
|
sql: `
|
|
6543
|
-
SELECT id, type, subject, content, project, embedding, confirmations, recall_count, created_at
|
|
6543
|
+
SELECT id, type, subject, content, project, importance, embedding, confirmations, recall_count, created_at
|
|
6544
6544
|
FROM entries
|
|
6545
6545
|
WHERE superseded_by IS NULL
|
|
6546
6546
|
AND retired = 0
|
|
@@ -6556,6 +6556,7 @@ async function mergeNearExactDuplicates(db, options) {
|
|
|
6556
6556
|
subject: toStringValue2(row.subject),
|
|
6557
6557
|
content: toStringValue2(row.content),
|
|
6558
6558
|
project: toProjectValue(row.project),
|
|
6559
|
+
importance: Number.isFinite(toNumber3(row.importance)) ? toNumber3(row.importance) : 5,
|
|
6559
6560
|
embedding: mapBufferToVector3(row.embedding),
|
|
6560
6561
|
confirmations: Number.isFinite(toNumber3(row.confirmations)) ? toNumber3(row.confirmations) : 0,
|
|
6561
6562
|
recallCount: Number.isFinite(toNumber3(row.recall_count)) ? toNumber3(row.recall_count) : 0,
|
|
@@ -6628,6 +6629,12 @@ async function mergeNearExactDuplicates(db, options) {
|
|
|
6628
6629
|
continue;
|
|
6629
6630
|
}
|
|
6630
6631
|
const totalConfirmations = sorted.reduce((sum, entry) => sum + entry.confirmations, 0);
|
|
6632
|
+
const maxImportance = sorted.reduce((max, entry) => Math.max(max, entry.importance ?? 5), 0);
|
|
6633
|
+
const oldestCreatedAt = sorted.reduce((oldest, entry) => {
|
|
6634
|
+
const t = Date.parse(entry.createdAt);
|
|
6635
|
+
const o = Date.parse(oldest);
|
|
6636
|
+
return Number.isFinite(t) && t < (Number.isFinite(o) ? o : Infinity) ? entry.createdAt : oldest;
|
|
6637
|
+
}, keeper.createdAt);
|
|
6631
6638
|
for (const source of sources) {
|
|
6632
6639
|
await db.execute({
|
|
6633
6640
|
sql: `
|
|
@@ -6662,10 +6669,12 @@ async function mergeNearExactDuplicates(db, options) {
|
|
|
6662
6669
|
UPDATE entries
|
|
6663
6670
|
SET merged_from = ?,
|
|
6664
6671
|
consolidated_at = datetime('now'),
|
|
6665
|
-
confirmations =
|
|
6672
|
+
confirmations = ?,
|
|
6673
|
+
importance = CASE WHEN importance < ? THEN ? ELSE importance END,
|
|
6674
|
+
created_at = CASE WHEN created_at > ? THEN ? ELSE created_at END
|
|
6666
6675
|
WHERE id = ?
|
|
6667
6676
|
`,
|
|
6668
|
-
args: [sources.length, totalConfirmations, keeper.id]
|
|
6677
|
+
args: [sources.length, totalConfirmations, maxImportance, maxImportance, oldestCreatedAt, oldestCreatedAt, keeper.id]
|
|
6669
6678
|
});
|
|
6670
6679
|
}
|
|
6671
6680
|
return mergedCount;
|
|
@@ -6882,7 +6891,7 @@ function mapActiveEmbeddedEntry(row) {
|
|
|
6882
6891
|
const projectRaw = toStringValue3(row.project);
|
|
6883
6892
|
const project = projectRaw.trim().length > 0 ? projectRaw.trim().toLowerCase() : null;
|
|
6884
6893
|
const tagsRaw = toStringValue3(row.tags_csv);
|
|
6885
|
-
const tags = tagsRaw.length > 0 ? tagsRaw.split("
|
|
6894
|
+
const tags = tagsRaw.length > 0 ? tagsRaw.split("|").map((tag) => tag.trim()).filter(Boolean) : [];
|
|
6886
6895
|
return {
|
|
6887
6896
|
id,
|
|
6888
6897
|
type: toStringValue3(row.type),
|
|
@@ -6943,7 +6952,7 @@ async function buildClusters(db, options = {}) {
|
|
|
6943
6952
|
e.merged_from,
|
|
6944
6953
|
e.consolidated_at,
|
|
6945
6954
|
(
|
|
6946
|
-
SELECT GROUP_CONCAT(t.tag, '
|
|
6955
|
+
SELECT GROUP_CONCAT(t.tag, '|')
|
|
6947
6956
|
FROM tags t
|
|
6948
6957
|
WHERE t.entry_id = e.id
|
|
6949
6958
|
) AS tags_csv
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
} from "../chunk-L7VVZDWF.js";
|
|
8
8
|
|
|
9
9
|
// src/openclaw-plugin/index.ts
|
|
10
|
+
import os from "os";
|
|
11
|
+
import path3 from "path";
|
|
10
12
|
import { Type } from "@sinclair/typebox";
|
|
11
13
|
|
|
12
14
|
// src/openclaw-plugin/recall.ts
|
|
@@ -140,8 +142,11 @@ function formatRecallAsMarkdown(result) {
|
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
// src/openclaw-plugin/session-query.ts
|
|
145
|
+
import { readFile, readdir, stat } from "fs/promises";
|
|
146
|
+
import path2 from "path";
|
|
143
147
|
var SESSION_TOPIC_TTL_MS = 60 * 60 * 1e3;
|
|
144
148
|
var SESSION_TOPIC_MIN_LENGTH = 40;
|
|
149
|
+
var ARCHIVED_SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
145
150
|
var SESSION_QUERY_LOOKBACK = 3;
|
|
146
151
|
var sessionTopicStash = /* @__PURE__ */ new Map();
|
|
147
152
|
function isRecord(value) {
|
|
@@ -216,6 +221,58 @@ function extractLastUserText(messages) {
|
|
|
216
221
|
return "";
|
|
217
222
|
}
|
|
218
223
|
}
|
|
224
|
+
async function readLatestArchivedUserMessages(sessionsDir, maxAgeMs = ARCHIVED_SESSION_MAX_AGE_MS) {
|
|
225
|
+
try {
|
|
226
|
+
const now = Date.now();
|
|
227
|
+
const archivedFiles = [];
|
|
228
|
+
const entries = await readdir(sessionsDir, { withFileTypes: true });
|
|
229
|
+
for (const entry of entries) {
|
|
230
|
+
if (!entry.isFile() || !entry.name.includes(".reset.")) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const filePath = path2.join(sessionsDir, entry.name);
|
|
234
|
+
const fileStats = await stat(filePath);
|
|
235
|
+
if (now - fileStats.mtimeMs > maxAgeMs) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
archivedFiles.push({ filePath, mtimeMs: fileStats.mtimeMs });
|
|
239
|
+
}
|
|
240
|
+
if (archivedFiles.length === 0) {
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
archivedFiles.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
244
|
+
for (const candidate of archivedFiles) {
|
|
245
|
+
const raw = await readFile(candidate.filePath, "utf8");
|
|
246
|
+
const userMessages = [];
|
|
247
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
248
|
+
const trimmed = line.trim();
|
|
249
|
+
if (!trimmed) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
let parsed;
|
|
253
|
+
try {
|
|
254
|
+
parsed = JSON.parse(trimmed);
|
|
255
|
+
} catch {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (!isRecord(parsed) || parsed["type"] !== "message") {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const text = extractTextFromUserMessage(parsed["message"]);
|
|
262
|
+
if (!text) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
userMessages.push(text);
|
|
266
|
+
}
|
|
267
|
+
if (userMessages.length > 0) {
|
|
268
|
+
return userMessages.slice(-SESSION_QUERY_LOOKBACK);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return [];
|
|
272
|
+
} catch {
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
}
|
|
219
276
|
function shouldStashTopic(text) {
|
|
220
277
|
if (text.length < SESSION_TOPIC_MIN_LENGTH) {
|
|
221
278
|
return false;
|
|
@@ -890,7 +947,28 @@ var plugin = {
|
|
|
890
947
|
const project = config?.project?.trim() || void 0;
|
|
891
948
|
const userPrompt = stripPromptMetadata(event.prompt ?? "");
|
|
892
949
|
const queryText = resolveSessionQuery(userPrompt, ctx.sessionKey);
|
|
893
|
-
|
|
950
|
+
let recallQueryText = queryText;
|
|
951
|
+
if (isFirstInSession && queryText === userPrompt && userPrompt.length < SESSION_TOPIC_MIN_LENGTH) {
|
|
952
|
+
const agentId = ctx.agentId?.trim() || "main";
|
|
953
|
+
if (!ctx.agentId?.trim()) {
|
|
954
|
+
api.logger.debug?.("[agenr] session-start: agentId not provided, defaulting to 'main'");
|
|
955
|
+
}
|
|
956
|
+
const sessionsDir = path3.join(
|
|
957
|
+
os.homedir(),
|
|
958
|
+
".openclaw",
|
|
959
|
+
"agents",
|
|
960
|
+
agentId,
|
|
961
|
+
"sessions"
|
|
962
|
+
);
|
|
963
|
+
const archivedMessages = await readLatestArchivedUserMessages(sessionsDir);
|
|
964
|
+
if (archivedMessages.length > 0) {
|
|
965
|
+
recallQueryText = `${archivedMessages.join(" ")} ${userPrompt}`;
|
|
966
|
+
api.logger.info?.(
|
|
967
|
+
`[agenr] session-start: archived session fallback applied, ${archivedMessages.length} messages`
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
const recallResult = await runRecall(agenrPath, budget, project, recallQueryText);
|
|
894
972
|
if (recallResult) {
|
|
895
973
|
const formatted = formatRecallAsMarkdown(recallResult);
|
|
896
974
|
if (formatted.trim()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agenr",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.15",
|
|
4
4
|
"openclaw": {
|
|
5
5
|
"extensions": [
|
|
6
6
|
"dist/openclaw-plugin/index.js"
|
|
@@ -11,13 +11,6 @@
|
|
|
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
|
-
},
|
|
21
14
|
"dependencies": {
|
|
22
15
|
"@clack/prompts": "^1.0.1",
|
|
23
16
|
"@libsql/client": "^0.17.0",
|
|
@@ -61,9 +54,11 @@
|
|
|
61
54
|
"README.md"
|
|
62
55
|
],
|
|
63
56
|
"author": "agenr-ai",
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsup src/cli.ts src/cli-main.ts src/openclaw-plugin/index.ts --format esm --dts",
|
|
59
|
+
"dev": "tsup src/cli.ts src/cli-main.ts --format esm --watch",
|
|
60
|
+
"test": "vitest run",
|
|
61
|
+
"test:watch": "vitest",
|
|
62
|
+
"typecheck": "tsc --noEmit"
|
|
68
63
|
}
|
|
69
|
-
}
|
|
64
|
+
}
|