agenr 0.7.1 → 0.7.3
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 +20 -0
- package/dist/cli-main.js +126 -8
- package/openclaw.plugin.json +20 -1
- package/package.json +2 -1
- package/skills/SKILL.md +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.3] - 2026-02-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- feat(plugin): bundled OpenClaw skill (skills/SKILL.md) - teaches agents when to call agenr_store and agenr_recall as MCP tools; automatically available when plugin is installed
|
|
7
|
+
- feat(plugin): complete configSchema in openclaw.plugin.json (signalMinImportance, signalMaxPerSignal, signalsEnabled, dbPath)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- fix(init): removed AGENTS.md auto-detection heuristic for openclaw platform - openclaw must be specified explicitly via --platform openclaw (AGENTS.md is also used by Codex; the heuristic was unreliable)
|
|
11
|
+
- fix(init): agenr init --platform openclaw no longer writes to AGENTS.md - the OpenClaw plugin handles memory injection via prependContext; AGENTS.md write was redundant
|
|
12
|
+
|
|
13
|
+
### Internal
|
|
14
|
+
- chore(plugin): bump openclaw.plugin.json version to 0.7.3
|
|
15
|
+
|
|
16
|
+
## [0.7.2] - 2026-02-20
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- fix(store): within-batch deduplication - entries with the same subject+type+source file in a single storeEntries() call are now deduplicated before processing, preventing same-batch signal duplicates (entries from different source files with the same subject are kept as distinct)
|
|
20
|
+
- fix(store): re-extraction guard - entries with the same subject+type+source_file extracted within 24 hours now increment confirmations instead of adding a new entry
|
|
21
|
+
- fix(mcp): append-only MCP access log at ~/.agenr/mcp-access.log for observability of agenr_recall and agenr_store tool calls
|
|
22
|
+
|
|
3
23
|
## [0.7.1] - 2026-02-20
|
|
4
24
|
|
|
5
25
|
### Added
|
package/dist/cli-main.js
CHANGED
|
@@ -4469,6 +4469,7 @@ var AUTO_SKIP_THRESHOLD = 0.95;
|
|
|
4469
4469
|
var SMART_DEDUP_THRESHOLD = 0.88;
|
|
4470
4470
|
var DEFAULT_DEDUP_THRESHOLD = 0.8;
|
|
4471
4471
|
var DEFAULT_SIMILAR_LIMIT = 5;
|
|
4472
|
+
var RECENCY_DEDUP_HOURS = 24;
|
|
4472
4473
|
var CANONICAL_KEY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+){2,4}$/;
|
|
4473
4474
|
var CROSS_TYPE_TODO_SUPERSEDE_TYPES = /* @__PURE__ */ new Set(["event", "fact", "decision"]);
|
|
4474
4475
|
var TODO_COMPLETION_SIGNALS = ["done", "fixed", "resolved", "completed", "shipped", "closed", "merged"];
|
|
@@ -5066,6 +5067,47 @@ async function findEntryByTypeAndCanonicalKey(db, type, canonicalKey) {
|
|
|
5066
5067
|
const tagsById = await getTagsForEntryIds2(db, [entryId]);
|
|
5067
5068
|
return mapStoredEntry2(row, tagsById.get(entryId) ?? []);
|
|
5068
5069
|
}
|
|
5070
|
+
async function findRecentEntryBySubjectTypeAndSourceFile(db, normalizedSubject, type, sourceFile, withinHours) {
|
|
5071
|
+
const result = await db.execute({
|
|
5072
|
+
sql: `
|
|
5073
|
+
SELECT
|
|
5074
|
+
id,
|
|
5075
|
+
type,
|
|
5076
|
+
subject,
|
|
5077
|
+
canonical_key,
|
|
5078
|
+
content,
|
|
5079
|
+
importance,
|
|
5080
|
+
expiry,
|
|
5081
|
+
source_file,
|
|
5082
|
+
source_context,
|
|
5083
|
+
embedding,
|
|
5084
|
+
created_at,
|
|
5085
|
+
updated_at,
|
|
5086
|
+
last_recalled_at,
|
|
5087
|
+
recall_count,
|
|
5088
|
+
confirmations,
|
|
5089
|
+
contradictions,
|
|
5090
|
+
superseded_by
|
|
5091
|
+
FROM entries e
|
|
5092
|
+
WHERE lower(trim(e.subject)) = ?
|
|
5093
|
+
AND e.type = ?
|
|
5094
|
+
AND e.source_file = ?
|
|
5095
|
+
AND e.retired = 0
|
|
5096
|
+
AND e.superseded_by IS NULL
|
|
5097
|
+
AND e.created_at > datetime('now', '-' || ? || ' hours')
|
|
5098
|
+
ORDER BY e.rowid DESC
|
|
5099
|
+
LIMIT 1
|
|
5100
|
+
`,
|
|
5101
|
+
args: [normalizedSubject, type, sourceFile, withinHours]
|
|
5102
|
+
});
|
|
5103
|
+
const row = result.rows[0];
|
|
5104
|
+
if (!row) {
|
|
5105
|
+
return null;
|
|
5106
|
+
}
|
|
5107
|
+
const entryId = toStringValue2(row.id);
|
|
5108
|
+
const tagsById = await getTagsForEntryIds2(db, [entryId]);
|
|
5109
|
+
return mapStoredEntry2(row, tagsById.get(entryId) ?? []);
|
|
5110
|
+
}
|
|
5069
5111
|
async function findActiveTodoByCanonicalKey(db, canonicalKey) {
|
|
5070
5112
|
return findEntryByTypeAndCanonicalKey(db, "todo", canonicalKey);
|
|
5071
5113
|
}
|
|
@@ -5474,6 +5516,28 @@ async function storeEntries(db, entries, apiKey, options = {}) {
|
|
|
5474
5516
|
let superseded = 0;
|
|
5475
5517
|
let relationsCreated = 0;
|
|
5476
5518
|
let llmDedupCalls = 0;
|
|
5519
|
+
if (!options.force && !onlineDedup) {
|
|
5520
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5521
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
5522
|
+
const key = `${entries[i].subject.trim().toLowerCase()}:${entries[i].type}:${entries[i].source.file}`;
|
|
5523
|
+
seen.set(key, i);
|
|
5524
|
+
}
|
|
5525
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
5526
|
+
const key = `${entries[i].subject.trim().toLowerCase()}:${entries[i].type}:${entries[i].source.file}`;
|
|
5527
|
+
if (seen.get(key) !== i) {
|
|
5528
|
+
skipped += 1;
|
|
5529
|
+
options.onDecision?.({
|
|
5530
|
+
entry: entries[i],
|
|
5531
|
+
action: "skipped",
|
|
5532
|
+
reason: "within-batch duplicate (same subject+type+source.file)"
|
|
5533
|
+
});
|
|
5534
|
+
}
|
|
5535
|
+
}
|
|
5536
|
+
entries = entries.filter((entry, i) => {
|
|
5537
|
+
const key = `${entry.subject.trim().toLowerCase()}:${entry.type}:${entry.source.file}`;
|
|
5538
|
+
return seen.get(key) === i;
|
|
5539
|
+
});
|
|
5540
|
+
}
|
|
5477
5541
|
const processOne = async (entry) => {
|
|
5478
5542
|
const normalizedEntry = {
|
|
5479
5543
|
...entry,
|
|
@@ -5489,6 +5553,28 @@ async function storeEntries(db, entries, apiKey, options = {}) {
|
|
|
5489
5553
|
});
|
|
5490
5554
|
return;
|
|
5491
5555
|
}
|
|
5556
|
+
if (!options.force && !onlineDedup && normalizedEntry.source.file) {
|
|
5557
|
+
const recentMatch = await findRecentEntryBySubjectTypeAndSourceFile(
|
|
5558
|
+
db,
|
|
5559
|
+
normalizedEntry.subject.trim().toLowerCase(),
|
|
5560
|
+
normalizedEntry.type,
|
|
5561
|
+
normalizedEntry.source.file,
|
|
5562
|
+
RECENCY_DEDUP_HOURS
|
|
5563
|
+
);
|
|
5564
|
+
if (recentMatch) {
|
|
5565
|
+
await incrementConfirmations(db, recentMatch.id, contentHash);
|
|
5566
|
+
updated += 1;
|
|
5567
|
+
options.onDecision?.({
|
|
5568
|
+
entry: normalizedEntry,
|
|
5569
|
+
action: "updated",
|
|
5570
|
+
reason: "re-extraction guard: same subject+type+source within 24h",
|
|
5571
|
+
matchedEntryId: recentMatch.id,
|
|
5572
|
+
matchedEntry: recentMatch,
|
|
5573
|
+
sameSubject: true
|
|
5574
|
+
});
|
|
5575
|
+
return;
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5492
5578
|
if (!options.force && normalizedEntry.canonical_key) {
|
|
5493
5579
|
const canonicalMatch = await findEntryByTypeAndCanonicalKey(db, normalizedEntry.type, normalizedEntry.canonical_key);
|
|
5494
5580
|
if (canonicalMatch) {
|
|
@@ -13226,9 +13312,6 @@ async function detectPlatform(projectDir) {
|
|
|
13226
13312
|
if (await isDirectory(path28.join(projectDir, ".cursor"))) {
|
|
13227
13313
|
return "cursor";
|
|
13228
13314
|
}
|
|
13229
|
-
if (await pathExists(path28.join(projectDir, "AGENTS.md"))) {
|
|
13230
|
-
return "openclaw";
|
|
13231
|
-
}
|
|
13232
13315
|
if (await pathExists(path28.join(projectDir, ".windsurfrules"))) {
|
|
13233
13316
|
return "windsurf";
|
|
13234
13317
|
}
|
|
@@ -13251,6 +13334,9 @@ async function resolveInstructionsPath(projectDir, platform) {
|
|
|
13251
13334
|
if (platform === "codex") {
|
|
13252
13335
|
return path28.join(os16.homedir(), ".codex", "AGENTS.md");
|
|
13253
13336
|
}
|
|
13337
|
+
if (platform === "openclaw") {
|
|
13338
|
+
return null;
|
|
13339
|
+
}
|
|
13254
13340
|
return path28.join(projectDir, "AGENTS.md");
|
|
13255
13341
|
}
|
|
13256
13342
|
function withMarkers(promptBlock) {
|
|
@@ -13400,9 +13486,13 @@ function formatInitSummary(result) {
|
|
|
13400
13486
|
const lines = [
|
|
13401
13487
|
`agenr init: platform=${result.platform} project=${result.project} dependencies=${dependencyLabel}`,
|
|
13402
13488
|
`- Wrote .agenr/config.json`,
|
|
13403
|
-
`- Wrote system prompt block to ${path28.basename(result.instructionsPath)}`,
|
|
13404
13489
|
`- Wrote MCP config to ${path28.relative(result.projectDir, result.mcpPath) || path28.basename(result.mcpPath)}`
|
|
13405
13490
|
];
|
|
13491
|
+
if (result.instructionsPath !== null) {
|
|
13492
|
+
lines.splice(2, 0, `- Wrote system prompt block to ${path28.basename(result.instructionsPath)}`);
|
|
13493
|
+
} else {
|
|
13494
|
+
lines.splice(2, 0, `- Memory injection: handled automatically by ${result.platform} plugin (no instructions file needed)`);
|
|
13495
|
+
}
|
|
13406
13496
|
if (result.gitignoreUpdated) {
|
|
13407
13497
|
lines.push("- Added .agenr/knowledge.db to .gitignore");
|
|
13408
13498
|
}
|
|
@@ -13430,12 +13520,14 @@ async function runInitCommand(options) {
|
|
|
13430
13520
|
const dependencies = options.dependsOn !== void 0 ? normalizeSlugList(options.dependsOn) : void 0;
|
|
13431
13521
|
const configResult = await writeAgenrConfig(projectDir, project, resolvedPlatform, dependencies);
|
|
13432
13522
|
const instructionsPath = await resolveInstructionsPath(projectDir, resolvedPlatform);
|
|
13433
|
-
|
|
13523
|
+
if (instructionsPath !== null) {
|
|
13524
|
+
await upsertPromptBlock(instructionsPath, buildSystemPromptBlock(project));
|
|
13525
|
+
}
|
|
13434
13526
|
const mcpPath = await writeMcpConfig(projectDir, resolvedPlatform);
|
|
13435
13527
|
const gitignoreEntries = [".agenr/knowledge.db"];
|
|
13436
13528
|
if (resolvedPlatform === "cursor") {
|
|
13437
13529
|
gitignoreEntries.push(".cursor/rules/agenr.mdc");
|
|
13438
|
-
if (isPathInsideProject(projectDir, instructionsPath)) {
|
|
13530
|
+
if (instructionsPath !== null && isPathInsideProject(projectDir, instructionsPath)) {
|
|
13439
13531
|
const relativeInstructionsPath = path28.relative(projectDir, instructionsPath).split(path28.sep).join("/");
|
|
13440
13532
|
gitignoreEntries.push(relativeInstructionsPath);
|
|
13441
13533
|
}
|
|
@@ -13462,6 +13554,14 @@ import os17 from "os";
|
|
|
13462
13554
|
import path29 from "path";
|
|
13463
13555
|
import * as readline from "readline";
|
|
13464
13556
|
import { once } from "events";
|
|
13557
|
+
async function appendMcpLog(line) {
|
|
13558
|
+
try {
|
|
13559
|
+
const logPath = path29.join(os17.homedir(), ".agenr", "mcp-access.log");
|
|
13560
|
+
const entry = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...line }) + "\n";
|
|
13561
|
+
await fs30.appendFile(logPath, entry, "utf8");
|
|
13562
|
+
} catch {
|
|
13563
|
+
}
|
|
13564
|
+
}
|
|
13465
13565
|
var MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
13466
13566
|
var JSON_RPC_PARSE_ERROR = -32700;
|
|
13467
13567
|
var JSON_RPC_INVALID_REQUEST = -32600;
|
|
@@ -14277,13 +14377,31 @@ function createMcpServer(options = {}, deps = {}) {
|
|
|
14277
14377
|
async function dispatchToolCall(params) {
|
|
14278
14378
|
try {
|
|
14279
14379
|
if (params.name === "agenr_recall") {
|
|
14380
|
+
const result = await callRecallTool(params.args);
|
|
14381
|
+
await appendMcpLog({
|
|
14382
|
+
tool: "agenr_recall",
|
|
14383
|
+
query: typeof params.args.query === "string" ? params.args.query.slice(0, 120) : void 0,
|
|
14384
|
+
context: typeof params.args.context === "string" ? params.args.context : void 0,
|
|
14385
|
+
project: typeof params.args.project === "string" ? params.args.project : void 0
|
|
14386
|
+
});
|
|
14280
14387
|
return {
|
|
14281
|
-
content: [{ type: "text", text:
|
|
14388
|
+
content: [{ type: "text", text: result }]
|
|
14282
14389
|
};
|
|
14283
14390
|
}
|
|
14284
14391
|
if (params.name === "agenr_store") {
|
|
14392
|
+
const result = await callStoreTool(params.args);
|
|
14393
|
+
const storeArgsEntries = Array.isArray(params.args.entries) ? params.args.entries : [];
|
|
14394
|
+
const firstEntry = storeArgsEntries[0];
|
|
14395
|
+
await appendMcpLog({
|
|
14396
|
+
tool: "agenr_store",
|
|
14397
|
+
count: storeArgsEntries.length,
|
|
14398
|
+
firstType: typeof firstEntry?.type === "string" ? firstEntry.type : void 0,
|
|
14399
|
+
firstSubject: typeof firstEntry?.subject === "string" ? firstEntry.subject.slice(0, 80) : void 0,
|
|
14400
|
+
firstImportance: typeof firstEntry?.importance === "number" ? firstEntry.importance : void 0,
|
|
14401
|
+
project: typeof params.args.project === "string" ? params.args.project : void 0
|
|
14402
|
+
});
|
|
14285
14403
|
return {
|
|
14286
|
-
content: [{ type: "text", text:
|
|
14404
|
+
content: [{ type: "text", text: result }]
|
|
14287
14405
|
};
|
|
14288
14406
|
}
|
|
14289
14407
|
if (params.name === "agenr_extract") {
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
"id": "agenr",
|
|
3
3
|
"name": "agenr Memory",
|
|
4
4
|
"description": "Local memory layer - injects agenr context at session start",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.7.3",
|
|
6
|
+
"skills": [
|
|
7
|
+
"skills"
|
|
8
|
+
],
|
|
6
9
|
"configSchema": {
|
|
7
10
|
"type": "object",
|
|
8
11
|
"additionalProperties": false,
|
|
@@ -18,6 +21,22 @@
|
|
|
18
21
|
"enabled": {
|
|
19
22
|
"type": "boolean",
|
|
20
23
|
"description": "Set false to disable memory injection without uninstalling."
|
|
24
|
+
},
|
|
25
|
+
"signalMinImportance": {
|
|
26
|
+
"type": "number",
|
|
27
|
+
"description": "Minimum importance for mid-session signals (1-10, default: 7). Raise to reduce noise."
|
|
28
|
+
},
|
|
29
|
+
"signalMaxPerSignal": {
|
|
30
|
+
"type": "number",
|
|
31
|
+
"description": "Max entries per signal notification (default: 5)."
|
|
32
|
+
},
|
|
33
|
+
"signalsEnabled": {
|
|
34
|
+
"type": "boolean",
|
|
35
|
+
"description": "Set false to disable mid-session signals without uninstalling."
|
|
36
|
+
},
|
|
37
|
+
"dbPath": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Path to agenr DB. Defaults to AGENR_DB_PATH env or ~/.agenr/knowledge.db."
|
|
21
40
|
}
|
|
22
41
|
}
|
|
23
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agenr",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"openclaw": {
|
|
5
5
|
"extensions": [
|
|
6
6
|
"dist/openclaw-plugin/index.js"
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
],
|
|
48
48
|
"files": [
|
|
49
49
|
"dist",
|
|
50
|
+
"skills",
|
|
50
51
|
"openclaw.plugin.json",
|
|
51
52
|
"CHANGELOG.md",
|
|
52
53
|
"LICENSE",
|
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agenr
|
|
3
|
+
description: Use when storing new knowledge (decisions, preferences, lessons, todos) or recalling context mid-session. The agenr plugin auto-injects memory at session start - this skill covers proactive store and on-demand recall.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use `agenr_store` proactively. Call it immediately after any decision, user preference, lesson learned, important event, or fact worth remembering. Do not ask first.
|
|
7
|
+
|
|
8
|
+
Required fields: `type`, `content`, `importance`.
|
|
9
|
+
Types: `fact | decision | preference | todo | lesson | event`.
|
|
10
|
+
Importance: `1-10` (default `7`), use `9` for critical items, use `10` sparingly.
|
|
11
|
+
|
|
12
|
+
Do not store secrets/credentials, temporary state, or verbatim conversation.
|
|
13
|
+
|
|
14
|
+
Use `agenr_recall` mid-session when you need context you do not already have. Use a specific query so results stay relevant.
|
|
15
|
+
|
|
16
|
+
Session-start recall is already handled automatically by the OpenClaw plugin. Do not call `agenr_recall` at turn 1 unless you need extra context beyond the injected summary.
|