agenr 0.7.8 → 0.7.10
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 +21 -0
- package/dist/cli-main.js +96 -6
- package/dist/openclaw-plugin/index.js +37 -55
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.10] - 2026-02-20
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- fix(init): codex platform now writes MCP entry directly to ~/.codex/config.toml instead of .mcp.json (which Codex does not read)
|
|
7
|
+
- fix(init): openclaw platform no longer writes a .mcp.json file (OpenClaw native plugin handles MCP registration via openclaw plugins install agenr)
|
|
8
|
+
- fix(init): agenr binary path is now resolved at init time via which or PNPM_HOME fallback -- GUI clients that launch with a restricted PATH will now find the correct binary
|
|
9
|
+
- fix(init): codex config.toml write is idempotent -- re-running init replaces the agenr line without duplicating it
|
|
10
|
+
- docs: remove redundant Memory (agenr) AGENTS.md block from OPENCLAW.md -- OpenClaw plugin handles agent instruction injection automatically via the built-in skill
|
|
11
|
+
|
|
12
|
+
## [0.7.9] - 2026-02-20
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- fix(openclaw-plugin): moved session-start recall injection from before_agent_start to before_prompt_build -- recall now fires exactly once per session instead of twice due to OpenClaw calling before_agent_start twice (once for model-resolve where prependContext is discarded, once for prompt-build where it is used)
|
|
16
|
+
|
|
17
|
+
## [0.7.8] - 2026-02-20
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- fix(openclaw-plugin): session-start recall dedup now keys on sessionId instead of a shared seen-Set -- each new session (including after /new) correctly receives injected context instead of being silently skipped on the second run
|
|
21
|
+
- fix(extractor): normalizeImportance now defaults to 7 instead of 5 -- aligns runtime default with schema declaration and coaching guidance
|
|
22
|
+
|
|
23
|
+
|
|
3
24
|
## [0.7.7] - 2026-02-20
|
|
4
25
|
|
|
5
26
|
### Fixed
|
package/dist/cli-main.js
CHANGED
|
@@ -13367,8 +13367,40 @@ async function runContextCommand(options, deps) {
|
|
|
13367
13367
|
|
|
13368
13368
|
// src/commands/init.ts
|
|
13369
13369
|
import fs29 from "fs/promises";
|
|
13370
|
+
import { accessSync, constants } from "fs";
|
|
13370
13371
|
import os16 from "os";
|
|
13371
13372
|
import path28 from "path";
|
|
13373
|
+
import { execFileSync } from "child_process";
|
|
13374
|
+
function escapeTomlString(value) {
|
|
13375
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
13376
|
+
}
|
|
13377
|
+
function isExecutableFile2(filePath) {
|
|
13378
|
+
try {
|
|
13379
|
+
if (process.platform === "win32") {
|
|
13380
|
+
accessSync(filePath, constants.F_OK);
|
|
13381
|
+
} else {
|
|
13382
|
+
accessSync(filePath, constants.X_OK);
|
|
13383
|
+
}
|
|
13384
|
+
return true;
|
|
13385
|
+
} catch {
|
|
13386
|
+
return false;
|
|
13387
|
+
}
|
|
13388
|
+
}
|
|
13389
|
+
function resolveAgenrBinary() {
|
|
13390
|
+
const lookupCommand = process.platform === "win32" ? "where.exe" : "which";
|
|
13391
|
+
try {
|
|
13392
|
+
return execFileSync(lookupCommand, ["agenr"], { encoding: "utf8" }).trim();
|
|
13393
|
+
} catch {
|
|
13394
|
+
const pnpmHome = process.env.PNPM_HOME;
|
|
13395
|
+
if (pnpmHome) {
|
|
13396
|
+
const candidate = path28.join(pnpmHome, "agenr");
|
|
13397
|
+
if (isExecutableFile2(candidate)) {
|
|
13398
|
+
return candidate;
|
|
13399
|
+
}
|
|
13400
|
+
}
|
|
13401
|
+
return "agenr";
|
|
13402
|
+
}
|
|
13403
|
+
}
|
|
13372
13404
|
var AGENR_START_MARKER = "<!-- agenr:start -->";
|
|
13373
13405
|
var AGENR_END_MARKER = "<!-- agenr:end -->";
|
|
13374
13406
|
var PLATFORM_VALUES = ["claude-code", "cursor", "openclaw", "windsurf", "codex", "generic"];
|
|
@@ -13527,9 +13559,9 @@ async function writeAgenrConfig(projectDir, project, platform, dependencies) {
|
|
|
13527
13559
|
await writeJsonFile(configPath, next);
|
|
13528
13560
|
return { configPath, config: next };
|
|
13529
13561
|
}
|
|
13530
|
-
function buildMcpEntry(projectDir) {
|
|
13562
|
+
function buildMcpEntry(projectDir, command) {
|
|
13531
13563
|
return {
|
|
13532
|
-
command: "agenr",
|
|
13564
|
+
command: command ?? "agenr",
|
|
13533
13565
|
args: ["mcp"],
|
|
13534
13566
|
env: {
|
|
13535
13567
|
AGENR_PROJECT_DIR: projectDir
|
|
@@ -13556,11 +13588,20 @@ function mergeMcpConfig(existing, entry) {
|
|
|
13556
13588
|
};
|
|
13557
13589
|
}
|
|
13558
13590
|
async function writeMcpConfig(projectDir, platform) {
|
|
13591
|
+
if (platform === "codex") {
|
|
13592
|
+
const agenrBin2 = resolveAgenrBinary();
|
|
13593
|
+
const mcpPath2 = await writeCodexConfig(projectDir, agenrBin2);
|
|
13594
|
+
return { mcpPath: mcpPath2, skipped: false };
|
|
13595
|
+
}
|
|
13596
|
+
if (platform === "openclaw") {
|
|
13597
|
+
return { mcpPath: "", skipped: true };
|
|
13598
|
+
}
|
|
13559
13599
|
const mcpPath = platform === "cursor" ? path28.join(projectDir, ".cursor", "mcp.json") : path28.join(projectDir, ".mcp.json");
|
|
13600
|
+
const agenrBin = resolveAgenrBinary();
|
|
13560
13601
|
const existing = await readJsonRecord(mcpPath) ?? {};
|
|
13561
|
-
const next = mergeMcpConfig(existing, buildMcpEntry(projectDir));
|
|
13602
|
+
const next = mergeMcpConfig(existing, buildMcpEntry(projectDir, agenrBin));
|
|
13562
13603
|
await writeJsonFile(mcpPath, next);
|
|
13563
|
-
return mcpPath;
|
|
13604
|
+
return { mcpPath, skipped: false };
|
|
13564
13605
|
}
|
|
13565
13606
|
function isPathInsideProject(projectDir, targetPath) {
|
|
13566
13607
|
const relative = path28.relative(projectDir, targetPath);
|
|
@@ -13599,12 +13640,60 @@ function resolveProjectSlug(projectDir, explicitProject) {
|
|
|
13599
13640
|
}
|
|
13600
13641
|
return slug;
|
|
13601
13642
|
}
|
|
13643
|
+
async function writeCodexConfig(projectDir, agenrBin) {
|
|
13644
|
+
const configPath = path28.join(os16.homedir(), ".codex", "config.toml");
|
|
13645
|
+
const escapedBin = escapeTomlString(agenrBin);
|
|
13646
|
+
const escapedProjectDir = escapeTomlString(projectDir);
|
|
13647
|
+
const newLine = `agenr = { command = "${escapedBin}", args = ["mcp"], env = { AGENR_PROJECT_DIR = "${escapedProjectDir}" } }`;
|
|
13648
|
+
let existing = "";
|
|
13649
|
+
try {
|
|
13650
|
+
existing = await fs29.readFile(configPath, "utf8");
|
|
13651
|
+
} catch (error) {
|
|
13652
|
+
const code = error.code;
|
|
13653
|
+
if (code !== "ENOENT") throw error;
|
|
13654
|
+
}
|
|
13655
|
+
let next;
|
|
13656
|
+
if (existing.trim().length === 0) {
|
|
13657
|
+
next = `[mcp]
|
|
13658
|
+
${newLine}
|
|
13659
|
+
`;
|
|
13660
|
+
} else {
|
|
13661
|
+
const lines = existing.split("\n");
|
|
13662
|
+
const mcpSectionIdx = lines.findIndex((l) => l.trim() === "[mcp]");
|
|
13663
|
+
if (mcpSectionIdx !== -1) {
|
|
13664
|
+
let mcpSectionEnd = lines.length;
|
|
13665
|
+
for (let i = mcpSectionIdx + 1; i < lines.length; i += 1) {
|
|
13666
|
+
if (/^\s*\[/.test(lines[i] ?? "")) {
|
|
13667
|
+
mcpSectionEnd = i;
|
|
13668
|
+
break;
|
|
13669
|
+
}
|
|
13670
|
+
}
|
|
13671
|
+
const relativeAgenrIdx = lines.slice(mcpSectionIdx + 1, mcpSectionEnd).findIndex((l) => l.trimStart().startsWith("agenr ="));
|
|
13672
|
+
if (relativeAgenrIdx !== -1) {
|
|
13673
|
+
const agenrLineIdx = mcpSectionIdx + 1 + relativeAgenrIdx;
|
|
13674
|
+
lines[agenrLineIdx] = newLine;
|
|
13675
|
+
} else {
|
|
13676
|
+
lines.splice(mcpSectionIdx + 1, 0, newLine);
|
|
13677
|
+
}
|
|
13678
|
+
next = lines.join("\n");
|
|
13679
|
+
} else {
|
|
13680
|
+
const suffix = existing.endsWith("\n") ? "" : "\n";
|
|
13681
|
+
next = `${existing}${suffix}
|
|
13682
|
+
[mcp]
|
|
13683
|
+
${newLine}
|
|
13684
|
+
`;
|
|
13685
|
+
}
|
|
13686
|
+
}
|
|
13687
|
+
await fs29.mkdir(path28.dirname(configPath), { recursive: true });
|
|
13688
|
+
await fs29.writeFile(configPath, next, "utf8");
|
|
13689
|
+
return configPath;
|
|
13690
|
+
}
|
|
13602
13691
|
function formatInitSummary(result) {
|
|
13603
13692
|
const dependencyLabel = result.dependencies.length > 0 ? `[${result.dependencies.join(",")}]` : "[]";
|
|
13604
13693
|
const lines = [
|
|
13605
13694
|
`agenr init: platform=${result.platform} project=${result.project} dependencies=${dependencyLabel}`,
|
|
13606
13695
|
`- Wrote .agenr/config.json`,
|
|
13607
|
-
`- Wrote MCP config to ${path28.relative(result.projectDir, result.mcpPath) || path28.basename(result.mcpPath)}`
|
|
13696
|
+
result.platform === "codex" ? "- Wrote MCP entry to ~/.codex/config.toml" : result.mcpSkipped ? "- MCP: handled by OpenClaw native plugin (openclaw plugins install agenr)" : `- Wrote MCP config to ${path28.relative(result.projectDir, result.mcpPath) || path28.basename(result.mcpPath)}`
|
|
13608
13697
|
];
|
|
13609
13698
|
if (result.instructionsPath !== null) {
|
|
13610
13699
|
lines.splice(2, 0, `- Wrote system prompt block to ${path28.basename(result.instructionsPath)}`);
|
|
@@ -13641,7 +13730,7 @@ async function runInitCommand(options) {
|
|
|
13641
13730
|
if (instructionsPath !== null) {
|
|
13642
13731
|
await upsertPromptBlock(instructionsPath, buildSystemPromptBlock(project));
|
|
13643
13732
|
}
|
|
13644
|
-
const mcpPath = await writeMcpConfig(projectDir, resolvedPlatform);
|
|
13733
|
+
const { mcpPath, skipped: mcpSkipped } = await writeMcpConfig(projectDir, resolvedPlatform);
|
|
13645
13734
|
const gitignoreEntries = [".agenr/knowledge.db"];
|
|
13646
13735
|
if (resolvedPlatform === "cursor") {
|
|
13647
13736
|
gitignoreEntries.push(".cursor/rules/agenr.mdc");
|
|
@@ -13659,6 +13748,7 @@ async function runInitCommand(options) {
|
|
|
13659
13748
|
configPath: configResult.configPath,
|
|
13660
13749
|
instructionsPath,
|
|
13661
13750
|
mcpPath,
|
|
13751
|
+
mcpSkipped,
|
|
13662
13752
|
gitignoreUpdated
|
|
13663
13753
|
};
|
|
13664
13754
|
}
|
|
@@ -624,46 +624,8 @@ async function ensurePluginDb(config) {
|
|
|
624
624
|
var plugin = {
|
|
625
625
|
id: "agenr",
|
|
626
626
|
name: "agenr memory context",
|
|
627
|
-
description: "Injects agenr long-term memory into every agent session via
|
|
627
|
+
description: "Injects agenr long-term memory into every agent session via before_prompt_build",
|
|
628
628
|
register(api) {
|
|
629
|
-
api.on(
|
|
630
|
-
"before_agent_start",
|
|
631
|
-
async (_event, ctx) => {
|
|
632
|
-
try {
|
|
633
|
-
const sessionKey = ctx.sessionKey ?? "";
|
|
634
|
-
const dedupeKey = ctx.sessionId ?? sessionKey;
|
|
635
|
-
if (shouldSkipSession(sessionKey)) {
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
if (dedupeKey && hasSeenSession(dedupeKey)) {
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
const config = api.pluginConfig;
|
|
642
|
-
if (config?.enabled === false) {
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
if (dedupeKey) {
|
|
646
|
-
markSessionSeen(dedupeKey);
|
|
647
|
-
}
|
|
648
|
-
const agenrPath = resolveAgenrPath(config);
|
|
649
|
-
const budget = resolveBudget(config);
|
|
650
|
-
const result = await runRecall(agenrPath, budget);
|
|
651
|
-
if (!result) {
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
const markdown = formatRecallAsMarkdown(result);
|
|
655
|
-
if (!markdown.trim()) {
|
|
656
|
-
return;
|
|
657
|
-
}
|
|
658
|
-
return { prependContext: markdown };
|
|
659
|
-
} catch (err) {
|
|
660
|
-
api.logger.warn(
|
|
661
|
-
`agenr plugin before_agent_start recall failed: ${err instanceof Error ? err.message : String(err)}`
|
|
662
|
-
);
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
);
|
|
667
629
|
api.on(
|
|
668
630
|
"before_prompt_build",
|
|
669
631
|
async (_event, ctx) => {
|
|
@@ -679,26 +641,46 @@ var plugin = {
|
|
|
679
641
|
if (config?.enabled === false) {
|
|
680
642
|
return;
|
|
681
643
|
}
|
|
682
|
-
|
|
683
|
-
|
|
644
|
+
const dedupeKey = ctx.sessionId ?? sessionKey;
|
|
645
|
+
let markdown;
|
|
646
|
+
const isFirstInSession = dedupeKey ? !hasSeenSession(dedupeKey) : true;
|
|
647
|
+
if (isFirstInSession) {
|
|
648
|
+
if (dedupeKey) {
|
|
649
|
+
markSessionSeen(dedupeKey);
|
|
650
|
+
}
|
|
651
|
+
const agenrPath = resolveAgenrPath(config);
|
|
652
|
+
const budget = resolveBudget(config);
|
|
653
|
+
const recallResult = await runRecall(agenrPath, budget);
|
|
654
|
+
if (recallResult) {
|
|
655
|
+
const formatted = formatRecallAsMarkdown(recallResult);
|
|
656
|
+
if (formatted.trim()) {
|
|
657
|
+
markdown = formatted;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
684
660
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
661
|
+
let signal;
|
|
662
|
+
if (config?.signalsEnabled !== false) {
|
|
663
|
+
const signalConfig = resolveSignalConfig(config);
|
|
664
|
+
const db = await ensurePluginDb(config);
|
|
665
|
+
const candidateSignal = await checkSignals(db, sessionKey, signalConfig);
|
|
666
|
+
if (candidateSignal) {
|
|
667
|
+
const state = sessionSignalState.get(sessionKey) ?? { lastSignalAt: 0, signalCount: 0 };
|
|
668
|
+
const inCooldown = signalConfig.cooldownMs > 0 && Date.now() - state.lastSignalAt < signalConfig.cooldownMs;
|
|
669
|
+
const overCap = signalConfig.maxPerSession > 0 && state.signalCount >= signalConfig.maxPerSession;
|
|
670
|
+
if (!inCooldown && !overCap) {
|
|
671
|
+
sessionSignalState.set(sessionKey, {
|
|
672
|
+
lastSignalAt: Date.now(),
|
|
673
|
+
signalCount: state.signalCount + 1
|
|
674
|
+
});
|
|
675
|
+
signal = candidateSignal;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
690
678
|
}
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
const overCap = signalConfig.maxPerSession > 0 && state.signalCount >= signalConfig.maxPerSession;
|
|
694
|
-
if (inCooldown || overCap) {
|
|
679
|
+
const prependContext = [markdown, signal].filter(Boolean).join("\n\n");
|
|
680
|
+
if (!prependContext) {
|
|
695
681
|
return;
|
|
696
682
|
}
|
|
697
|
-
|
|
698
|
-
lastSignalAt: Date.now(),
|
|
699
|
-
signalCount: state.signalCount + 1
|
|
700
|
-
});
|
|
701
|
-
return { prependContext: signal };
|
|
683
|
+
return { prependContext };
|
|
702
684
|
} catch (err) {
|
|
703
685
|
api.logger.warn(
|
|
704
686
|
`agenr plugin before_prompt_build signal check failed: ${err instanceof Error ? err.message : String(err)}`
|