agenr 0.9.79 → 0.9.81
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 +15 -0
- package/README.md +4 -3
- package/dist/{chunk-JJZHOS4U.js → chunk-CEPFOYOL.js} +111 -3
- package/dist/cli-main.js +183 -45
- package/dist/openclaw-plugin/index.js +176 -47
- package/package.json +13 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.81] - 2026-03-08
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added in-place entry metadata updates for memory hygiene, starting with `importance`, via the new `agenr update --id <id> --importance <n>` CLI command.
|
|
8
|
+
- Added the `agenr_update` MCP tool and native OpenClaw plugin tool so agents can demote stale-but-still-true entries without retiring them.
|
|
9
|
+
- Added focused coverage for CLI and MCP update flows plus OpenClaw plugin tool wiring.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Added startup-only `SQLITE_BUSY` retry/backoff for OpenClaw session-start browse and memory-index reads, reducing transient lock-induced degradation immediately after handoff recovery.
|
|
14
|
+
- Session-start browse now logs explicit retry and unavailable states instead of collapsing lock failures into the ordinary `browse returned 0 entries` path.
|
|
15
|
+
- Preserved existing startup fail-open rendering behavior by continuing to omit unavailable browse or memory-index sections while still completing session-start with the remaining recovered context.
|
|
16
|
+
- Added focused regression coverage for strict startup browse execution, compatibility preservation in `runRecall()`, and session-start retry/degradation logging for browse and memory-index lock contention.
|
|
17
|
+
|
|
3
18
|
## [0.9.79] - 2026-03-08
|
|
4
19
|
|
|
5
20
|
### Fixed
|
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ export OPENAI_API_KEY=sk-... # for embeddings + extraction
|
|
|
56
56
|
|
|
57
57
|
### OpenClaw (recommended)
|
|
58
58
|
|
|
59
|
-
`agenr init` auto-detects OpenClaw, installs the native plugin, and restarts the gateway. The plugin handles everything automatically: session-start context injection (recent turns, core recall, browse recall, and a memory index), mid-session signals when important entries arrive, cross-session handoff summaries, and native `agenr_recall`, `agenr_store`, `agenr_extract`, `agenr_retire`, and session-project tools.
|
|
59
|
+
`agenr init` auto-detects OpenClaw, installs the native plugin, and restarts the gateway. The plugin handles everything automatically: session-start context injection (recent turns, core recall, browse recall, and a memory index), mid-session signals when important entries arrive, cross-session handoff summaries, and native `agenr_recall`, `agenr_store`, `agenr_extract`, `agenr_retire`, `agenr_update`, and session-project tools.
|
|
60
60
|
|
|
61
61
|
No AGENTS.md edits needed. No MCP config needed. The bundled SKILL.md loads automatically and instructs the agent when to call `agenr_store` proactively.
|
|
62
62
|
|
|
@@ -127,7 +127,7 @@ Adds instructions to `~/.codeium/windsurf/memories/global_rules.md` and wires `.
|
|
|
127
127
|
agenr init # auto-detects platform, falls back to generic AGENTS.md
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
Or start `agenr mcp` as a stdio MCP server and configure it in your tool's MCP settings manually. Your agent gets `agenr_recall`, `agenr_store`, `agenr_extract`, and `
|
|
130
|
+
Or start `agenr mcp` as a stdio MCP server and configure it in your tool's MCP settings manually. Your agent gets `agenr_recall`, `agenr_store`, `agenr_extract`, `agenr_retire`, and `agenr_update` as tools.
|
|
131
131
|
|
|
132
132
|
## How Memory Works
|
|
133
133
|
|
|
@@ -236,7 +236,7 @@ If you prefer manual MCP setup over `agenr init`, start the stdio server:
|
|
|
236
236
|
agenr mcp
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
-
This exposes
|
|
239
|
+
This exposes five tools: `agenr_recall`, `agenr_store`, `agenr_extract`, `agenr_retire`, `agenr_update`. Configure it in your tool's MCP settings as a stdio server.
|
|
240
240
|
|
|
241
241
|
## Commands
|
|
242
242
|
|
|
@@ -252,6 +252,7 @@ This exposes four tools: `agenr_recall`, `agenr_store`, `agenr_extract`, `agenr_
|
|
|
252
252
|
| `agenr store [files...]` | Store entries with semantic dedup |
|
|
253
253
|
| `agenr recall [query]` | Semantic + memory-aware recall. Use `--since`/`--until` for date ranges, `--around` for target-date ranking, `--browse` for temporal mode. |
|
|
254
254
|
| `agenr retire [subject]` | Retire a stale entry (hidden, not deleted). Match by subject or `--id`. |
|
|
255
|
+
| `agenr update --id <id> --importance <n>` | Update an entry in place. Currently supports importance only. |
|
|
255
256
|
| `agenr watch [file]` | Live-watch files/directories, auto-extract knowledge |
|
|
256
257
|
| `agenr watcher install` | Install background watch daemon (macOS launchd) |
|
|
257
258
|
| `agenr watcher status` | Show daemon status (running/stopped, pid, watched file, recent logs) |
|
|
@@ -6289,6 +6289,112 @@ async function executeRetire(request, deps) {
|
|
|
6289
6289
|
}
|
|
6290
6290
|
}
|
|
6291
6291
|
|
|
6292
|
+
// src/app/update-service.ts
|
|
6293
|
+
function validateEntryId(id) {
|
|
6294
|
+
const entryId = id?.trim() ?? "";
|
|
6295
|
+
if (!entryId) {
|
|
6296
|
+
throw new Error("id is required");
|
|
6297
|
+
}
|
|
6298
|
+
return entryId;
|
|
6299
|
+
}
|
|
6300
|
+
function normalizeImportance(value) {
|
|
6301
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
6302
|
+
if (!Number.isInteger(parsed) || parsed < IMPORTANCE_MIN || parsed > IMPORTANCE_MAX) {
|
|
6303
|
+
throw new Error(`importance must be an integer between ${IMPORTANCE_MIN} and ${IMPORTANCE_MAX}`);
|
|
6304
|
+
}
|
|
6305
|
+
return parsed;
|
|
6306
|
+
}
|
|
6307
|
+
async function queryById2(db, id) {
|
|
6308
|
+
const result = await db.execute({
|
|
6309
|
+
sql: `
|
|
6310
|
+
SELECT id, type, subject, importance, content
|
|
6311
|
+
FROM entries
|
|
6312
|
+
WHERE retired = 0 AND id = ?
|
|
6313
|
+
LIMIT 1
|
|
6314
|
+
`,
|
|
6315
|
+
args: [id]
|
|
6316
|
+
});
|
|
6317
|
+
const row = result.rows[0];
|
|
6318
|
+
if (!row) {
|
|
6319
|
+
return null;
|
|
6320
|
+
}
|
|
6321
|
+
return {
|
|
6322
|
+
id: toStringValue(row.id),
|
|
6323
|
+
type: toStringValue(row.type),
|
|
6324
|
+
subject: toStringValue(row.subject),
|
|
6325
|
+
importance: toNumber(row.importance),
|
|
6326
|
+
content: toStringValue(row.content)
|
|
6327
|
+
};
|
|
6328
|
+
}
|
|
6329
|
+
async function lookupUpdateCandidate(request, deps) {
|
|
6330
|
+
const resolvedDeps = {
|
|
6331
|
+
readConfigFn: deps?.readConfigFn ?? readConfig,
|
|
6332
|
+
getDbFn: deps?.getDbFn ?? getDb,
|
|
6333
|
+
initDbFn: deps?.initDbFn ?? initDb,
|
|
6334
|
+
closeDbFn: deps?.closeDbFn ?? closeDb
|
|
6335
|
+
};
|
|
6336
|
+
const runtimeEnv = request.env ?? process.env;
|
|
6337
|
+
const id = validateEntryId(request.id);
|
|
6338
|
+
const config = resolvedDeps.readConfigFn(runtimeEnv);
|
|
6339
|
+
const db = resolvedDeps.getDbFn(request.dbPath?.trim() || config?.db?.path);
|
|
6340
|
+
try {
|
|
6341
|
+
await resolvedDeps.initDbFn(db);
|
|
6342
|
+
return { candidate: await queryById2(db, id) };
|
|
6343
|
+
} finally {
|
|
6344
|
+
resolvedDeps.closeDbFn(db);
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
async function executeUpdate(request, deps) {
|
|
6348
|
+
const resolvedDeps = {
|
|
6349
|
+
readConfigFn: deps?.readConfigFn ?? readConfig,
|
|
6350
|
+
getDbFn: deps?.getDbFn ?? getDb,
|
|
6351
|
+
initDbFn: deps?.initDbFn ?? initDb,
|
|
6352
|
+
closeDbFn: deps?.closeDbFn ?? closeDb
|
|
6353
|
+
};
|
|
6354
|
+
const runtimeEnv = request.env ?? process.env;
|
|
6355
|
+
const id = validateEntryId(request.id);
|
|
6356
|
+
const importance = normalizeImportance(request.importance);
|
|
6357
|
+
const config = resolvedDeps.readConfigFn(runtimeEnv);
|
|
6358
|
+
const db = resolvedDeps.getDbFn(request.dbPath?.trim() || config?.db?.path);
|
|
6359
|
+
try {
|
|
6360
|
+
await resolvedDeps.initDbFn(db);
|
|
6361
|
+
const existing = await queryById2(db, id);
|
|
6362
|
+
if (!existing) {
|
|
6363
|
+
return { id, previousImportance: importance, importance, updated: false };
|
|
6364
|
+
}
|
|
6365
|
+
if (existing.importance === importance) {
|
|
6366
|
+
return { id, previousImportance: existing.importance, importance, updated: true };
|
|
6367
|
+
}
|
|
6368
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6369
|
+
const result = await db.execute({
|
|
6370
|
+
sql: `
|
|
6371
|
+
UPDATE entries
|
|
6372
|
+
SET importance = ?,
|
|
6373
|
+
updated_at = ?
|
|
6374
|
+
WHERE id = ?
|
|
6375
|
+
AND retired = 0
|
|
6376
|
+
`,
|
|
6377
|
+
args: [importance, now, id]
|
|
6378
|
+
});
|
|
6379
|
+
if (toRowsAffected(result.rowsAffected) === 0) {
|
|
6380
|
+
return {
|
|
6381
|
+
id,
|
|
6382
|
+
previousImportance: existing.importance,
|
|
6383
|
+
importance: existing.importance,
|
|
6384
|
+
updated: false
|
|
6385
|
+
};
|
|
6386
|
+
}
|
|
6387
|
+
return {
|
|
6388
|
+
id,
|
|
6389
|
+
previousImportance: existing.importance,
|
|
6390
|
+
importance,
|
|
6391
|
+
updated: true
|
|
6392
|
+
};
|
|
6393
|
+
} finally {
|
|
6394
|
+
resolvedDeps.closeDbFn(db);
|
|
6395
|
+
}
|
|
6396
|
+
}
|
|
6397
|
+
|
|
6292
6398
|
// src/app/store-service.ts
|
|
6293
6399
|
var KNOWLEDGE_TYPE_SET = new Set(KNOWLEDGE_TYPES);
|
|
6294
6400
|
var EXPIRY_LEVEL_SET = new Set(EXPIRY_LEVELS);
|
|
@@ -6325,7 +6431,7 @@ function normalizeCanonicalKey(value) {
|
|
|
6325
6431
|
}
|
|
6326
6432
|
return normalized;
|
|
6327
6433
|
}
|
|
6328
|
-
function
|
|
6434
|
+
function normalizeImportance2(value) {
|
|
6329
6435
|
const importanceRaw = typeof value === "number" ? value : Number(value);
|
|
6330
6436
|
return Number.isInteger(importanceRaw) && importanceRaw >= IMPORTANCE_MIN && importanceRaw <= IMPORTANCE_MAX ? importanceRaw : 5;
|
|
6331
6437
|
}
|
|
@@ -6358,7 +6464,7 @@ function normalizeStoreJsonEntry(raw, fallbackFile) {
|
|
|
6358
6464
|
subject,
|
|
6359
6465
|
canonical_key: normalizeCanonicalKey(record.canonical_key),
|
|
6360
6466
|
content,
|
|
6361
|
-
importance:
|
|
6467
|
+
importance: normalizeImportance2(record.importance),
|
|
6362
6468
|
expiry: normalizeExpiry(record.expiry),
|
|
6363
6469
|
tags: normalizeTags(record.tags),
|
|
6364
6470
|
created_at: normalizeCreatedAt(record.created_at),
|
|
@@ -6485,7 +6591,7 @@ function normalizeStoreToolPayload(params, fallbackSourceFile, warn = () => void
|
|
|
6485
6591
|
type,
|
|
6486
6592
|
subject,
|
|
6487
6593
|
content,
|
|
6488
|
-
importance:
|
|
6594
|
+
importance: normalizeImportance2(entry.importance),
|
|
6489
6595
|
expiry: normalizedExpiry,
|
|
6490
6596
|
tags: normalizeTags(entry.tags),
|
|
6491
6597
|
source: {
|
|
@@ -6702,6 +6808,8 @@ export {
|
|
|
6702
6808
|
recallSectionOrder,
|
|
6703
6809
|
lookupRetireCandidates,
|
|
6704
6810
|
executeRetire,
|
|
6811
|
+
lookupUpdateCandidate,
|
|
6812
|
+
executeUpdate,
|
|
6705
6813
|
parseStoreJsonInput,
|
|
6706
6814
|
normalizeStoreToolPayload,
|
|
6707
6815
|
buildVirtualStoreBatch,
|
package/dist/cli-main.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
deduplicateEntries,
|
|
9
9
|
ensurePluginDbTables,
|
|
10
10
|
executeRetire,
|
|
11
|
+
executeUpdate,
|
|
11
12
|
expandInputFiles,
|
|
12
13
|
extractKnowledgeFromChunks,
|
|
13
14
|
extractTextEntries,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
getPendingReviews,
|
|
28
29
|
isProjectWorthyOpenClawStoreEntry,
|
|
29
30
|
lookupRetireCandidates,
|
|
31
|
+
lookupUpdateCandidate,
|
|
30
32
|
normalizeKnowledgePlatform,
|
|
31
33
|
parseStoreJsonInput,
|
|
32
34
|
parseTranscriptFile,
|
|
@@ -39,7 +41,7 @@ import {
|
|
|
39
41
|
runStoreService,
|
|
40
42
|
runTraceService,
|
|
41
43
|
ui
|
|
42
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-CEPFOYOL.js";
|
|
43
45
|
import {
|
|
44
46
|
ConflictAlreadyResolvedError,
|
|
45
47
|
REVIEW_QUEUE_PATH,
|
|
@@ -64,6 +66,8 @@ import {
|
|
|
64
66
|
ENTRY_SELECT_COLUMNS_WITH_EMBEDDING,
|
|
65
67
|
EXPIRY_LEVELS,
|
|
66
68
|
EmbeddingCache,
|
|
69
|
+
IMPORTANCE_MAX,
|
|
70
|
+
IMPORTANCE_MIN,
|
|
67
71
|
KNOWLEDGE_PLATFORMS,
|
|
68
72
|
KNOWLEDGE_TYPES,
|
|
69
73
|
SCOPE_LEVELS,
|
|
@@ -170,7 +174,7 @@ import {
|
|
|
170
174
|
} from "./chunk-UGIJJOPV.js";
|
|
171
175
|
|
|
172
176
|
// src/cli-main.ts
|
|
173
|
-
import * as
|
|
177
|
+
import * as clack19 from "@clack/prompts";
|
|
174
178
|
import { Command } from "commander";
|
|
175
179
|
|
|
176
180
|
// src/auth-status.ts
|
|
@@ -6066,10 +6070,10 @@ function renderTextReport(stats, dryRun) {
|
|
|
6066
6070
|
function createLogger2(jsonMode) {
|
|
6067
6071
|
const useClack = process.stderr.isTTY && !jsonMode;
|
|
6068
6072
|
if (!useClack) {
|
|
6069
|
-
const
|
|
6073
|
+
const log23 = createLogger("consolidate-cmd");
|
|
6070
6074
|
return {
|
|
6071
|
-
info: (message) =>
|
|
6072
|
-
warn: (message) =>
|
|
6075
|
+
info: (message) => log23.info(message),
|
|
6076
|
+
warn: (message) => log23.warn(message)
|
|
6073
6077
|
};
|
|
6074
6078
|
}
|
|
6075
6079
|
const clackOutput = { output: process.stderr };
|
|
@@ -14010,6 +14014,27 @@ var TOOL_DEFINITIONS = [
|
|
|
14010
14014
|
}
|
|
14011
14015
|
}
|
|
14012
14016
|
}
|
|
14017
|
+
},
|
|
14018
|
+
{
|
|
14019
|
+
name: "agenr_update",
|
|
14020
|
+
description: "Update an existing memory entry in place. Currently supports importance only.",
|
|
14021
|
+
inputSchema: {
|
|
14022
|
+
type: "object",
|
|
14023
|
+
additionalProperties: false,
|
|
14024
|
+
required: ["entry_id", "importance"],
|
|
14025
|
+
properties: {
|
|
14026
|
+
entry_id: {
|
|
14027
|
+
type: "string",
|
|
14028
|
+
description: "Entry id to update."
|
|
14029
|
+
},
|
|
14030
|
+
importance: {
|
|
14031
|
+
type: "integer",
|
|
14032
|
+
description: "New importance score.",
|
|
14033
|
+
minimum: 1,
|
|
14034
|
+
maximum: 10
|
|
14035
|
+
}
|
|
14036
|
+
}
|
|
14037
|
+
}
|
|
14013
14038
|
}
|
|
14014
14039
|
];
|
|
14015
14040
|
function hasOwn(record, key) {
|
|
@@ -14259,12 +14284,12 @@ function createMcpServer(options = {}, deps = {}) {
|
|
|
14259
14284
|
let dbInitPromise = null;
|
|
14260
14285
|
let isStopped = false;
|
|
14261
14286
|
let lineReader = null;
|
|
14262
|
-
function
|
|
14287
|
+
function log23(line) {
|
|
14263
14288
|
errorOutput.write(`${line}
|
|
14264
14289
|
`);
|
|
14265
14290
|
}
|
|
14266
14291
|
if (!scopedProjectDir) {
|
|
14267
|
-
|
|
14292
|
+
log23("warn: AGENR_PROJECT_DIR not set -- recall will return global (unscoped) results. Run agenr init to configure project scoping.");
|
|
14268
14293
|
}
|
|
14269
14294
|
async function ensureDb() {
|
|
14270
14295
|
if (!dbClient) {
|
|
@@ -14513,6 +14538,41 @@ function createMcpServer(options = {}, deps = {}) {
|
|
|
14513
14538
|
const text4 = persist ? `${messageBase} Retirement will survive database re-ingest.` : messageBase;
|
|
14514
14539
|
return text4;
|
|
14515
14540
|
}
|
|
14541
|
+
async function callUpdateTool(args) {
|
|
14542
|
+
const entryId = typeof args.entry_id === "string" ? args.entry_id.trim() : "";
|
|
14543
|
+
if (!entryId) {
|
|
14544
|
+
throw new RpcError(JSON_RPC_INVALID_PARAMS, "entry_id is required");
|
|
14545
|
+
}
|
|
14546
|
+
const rawImportance = typeof args.importance === "number" ? args.importance : Number(args.importance);
|
|
14547
|
+
if (!Number.isInteger(rawImportance) || rawImportance < 1 || rawImportance > 10) {
|
|
14548
|
+
throw new RpcError(JSON_RPC_INVALID_PARAMS, "importance must be an integer between 1 and 10");
|
|
14549
|
+
}
|
|
14550
|
+
const db = await ensureDb();
|
|
14551
|
+
const updateDeps = {
|
|
14552
|
+
readConfigFn: resolvedDeps.readConfigFn,
|
|
14553
|
+
getDbFn: () => db,
|
|
14554
|
+
initDbFn: async () => void 0,
|
|
14555
|
+
closeDbFn: () => void 0
|
|
14556
|
+
};
|
|
14557
|
+
const lookup = await lookupUpdateCandidate({
|
|
14558
|
+
id: entryId,
|
|
14559
|
+
dbPath: options.dbPath,
|
|
14560
|
+
env
|
|
14561
|
+
}, updateDeps);
|
|
14562
|
+
if (!lookup.candidate) {
|
|
14563
|
+
throw new RpcError(JSON_RPC_INVALID_PARAMS, `No active entry found with id: ${entryId}`);
|
|
14564
|
+
}
|
|
14565
|
+
const updated = await executeUpdate({
|
|
14566
|
+
id: entryId,
|
|
14567
|
+
importance: rawImportance,
|
|
14568
|
+
dbPath: options.dbPath,
|
|
14569
|
+
env
|
|
14570
|
+
}, updateDeps);
|
|
14571
|
+
if (!updated.updated) {
|
|
14572
|
+
return `Not updated: ${lookup.candidate.subject} (type: ${lookup.candidate.type}) is no longer active.`;
|
|
14573
|
+
}
|
|
14574
|
+
return updated.previousImportance === updated.importance ? `Entry ${entryId} already has importance ${updated.importance}.` : `Updated: ${lookup.candidate.subject} (type: ${lookup.candidate.type}) importance ${updated.previousImportance} -> ${updated.importance}.`;
|
|
14575
|
+
}
|
|
14516
14576
|
async function dispatchToolCall(params) {
|
|
14517
14577
|
try {
|
|
14518
14578
|
if (params.name === "agenr_recall") {
|
|
@@ -14537,6 +14597,11 @@ function createMcpServer(options = {}, deps = {}) {
|
|
|
14537
14597
|
content: [{ type: "text", text: await callRetireTool(params.args) }]
|
|
14538
14598
|
};
|
|
14539
14599
|
}
|
|
14600
|
+
if (params.name === "agenr_update") {
|
|
14601
|
+
return {
|
|
14602
|
+
content: [{ type: "text", text: await callUpdateTool(params.args) }]
|
|
14603
|
+
};
|
|
14604
|
+
}
|
|
14540
14605
|
throw new RpcError(JSON_RPC_INVALID_PARAMS, `Unknown tool: ${params.name}`);
|
|
14541
14606
|
} catch (error) {
|
|
14542
14607
|
if (error instanceof RpcError) {
|
|
@@ -14588,9 +14653,9 @@ function createMcpServer(options = {}, deps = {}) {
|
|
|
14588
14653
|
const startupConfig = await resolvedDeps.readConfigFn();
|
|
14589
14654
|
resolvedDeps.resolveEmbeddingApiKeyFn(startupConfig, env);
|
|
14590
14655
|
} catch {
|
|
14591
|
-
|
|
14592
|
-
|
|
14593
|
-
|
|
14656
|
+
log23("[mcp] ERROR: OPENAI_API_KEY is required for embeddings but was not found.");
|
|
14657
|
+
log23("[mcp] Set it via environment variable or run: agenr setup");
|
|
14658
|
+
log23("[mcp] If using MCP, add it to your MCP server config env block.");
|
|
14594
14659
|
process.exitCode = 1;
|
|
14595
14660
|
return;
|
|
14596
14661
|
}
|
|
@@ -14611,7 +14676,7 @@ function createMcpServer(options = {}, deps = {}) {
|
|
|
14611
14676
|
continue;
|
|
14612
14677
|
}
|
|
14613
14678
|
if (verbose) {
|
|
14614
|
-
|
|
14679
|
+
log23(`[mcp] <= ${trimmed}`);
|
|
14615
14680
|
}
|
|
14616
14681
|
let parsedJson;
|
|
14617
14682
|
try {
|
|
@@ -14620,7 +14685,7 @@ function createMcpServer(options = {}, deps = {}) {
|
|
|
14620
14685
|
const parseError = makeErrorResponse(null, JSON_RPC_PARSE_ERROR, "Parse error");
|
|
14621
14686
|
await writeLine(output, parseError);
|
|
14622
14687
|
if (verbose) {
|
|
14623
|
-
|
|
14688
|
+
log23(`[mcp] => ${JSON.stringify(parseError)}`);
|
|
14624
14689
|
}
|
|
14625
14690
|
continue;
|
|
14626
14691
|
}
|
|
@@ -14632,13 +14697,13 @@ function createMcpServer(options = {}, deps = {}) {
|
|
|
14632
14697
|
}
|
|
14633
14698
|
await writeLine(output, response);
|
|
14634
14699
|
if (verbose) {
|
|
14635
|
-
|
|
14700
|
+
log23(`[mcp] => ${JSON.stringify(response)}`);
|
|
14636
14701
|
}
|
|
14637
14702
|
} catch (error) {
|
|
14638
14703
|
const normalized = error instanceof RpcError ? makeErrorResponse(idForError, error.code, error.message) : makeErrorResponse(idForError, JSON_RPC_INTERNAL_ERROR, "Internal error");
|
|
14639
14704
|
await writeLine(output, normalized);
|
|
14640
14705
|
if (verbose) {
|
|
14641
|
-
|
|
14706
|
+
log23(`[mcp] => ${JSON.stringify(normalized)}`);
|
|
14642
14707
|
}
|
|
14643
14708
|
}
|
|
14644
14709
|
}
|
|
@@ -15767,11 +15832,83 @@ function registerTraceCommand(program) {
|
|
|
15767
15832
|
);
|
|
15768
15833
|
}
|
|
15769
15834
|
|
|
15835
|
+
// src/commands/update.ts
|
|
15836
|
+
import * as clack17 from "@clack/prompts";
|
|
15837
|
+
async function runUpdateCommand(options) {
|
|
15838
|
+
const entryId = options.id?.trim() ?? "";
|
|
15839
|
+
if (!entryId) {
|
|
15840
|
+
throw new Error("id is required");
|
|
15841
|
+
}
|
|
15842
|
+
const importance = options.importance;
|
|
15843
|
+
if (!Number.isInteger(importance) || importance < IMPORTANCE_MIN || importance > IMPORTANCE_MAX) {
|
|
15844
|
+
throw new Error(`importance must be an integer between ${IMPORTANCE_MIN} and ${IMPORTANCE_MAX}`);
|
|
15845
|
+
}
|
|
15846
|
+
const clackOutput = { output: process.stderr };
|
|
15847
|
+
clack17.intro(banner(), clackOutput);
|
|
15848
|
+
const lookup = await lookupUpdateCandidate({
|
|
15849
|
+
id: entryId,
|
|
15850
|
+
dbPath: options.db
|
|
15851
|
+
});
|
|
15852
|
+
if (!lookup.candidate) {
|
|
15853
|
+
clack17.log.warn(`No active entry found with id: ${entryId}`, clackOutput);
|
|
15854
|
+
clack17.outro(void 0, clackOutput);
|
|
15855
|
+
return { exitCode: 1 };
|
|
15856
|
+
}
|
|
15857
|
+
const candidate = lookup.candidate;
|
|
15858
|
+
clack17.log.info(
|
|
15859
|
+
`Entry: ${candidate.type} (importance: ${candidate.importance}) "${candidate.subject}"`,
|
|
15860
|
+
clackOutput
|
|
15861
|
+
);
|
|
15862
|
+
if (options.force !== true) {
|
|
15863
|
+
const confirmed = await clack17.confirm({
|
|
15864
|
+
message: `Update importance from ${candidate.importance} to ${importance}?`
|
|
15865
|
+
});
|
|
15866
|
+
if (confirmed !== true) {
|
|
15867
|
+
clack17.log.warn("Update canceled.", clackOutput);
|
|
15868
|
+
clack17.outro(void 0, clackOutput);
|
|
15869
|
+
return { exitCode: 1 };
|
|
15870
|
+
}
|
|
15871
|
+
}
|
|
15872
|
+
const updated = await executeUpdate({
|
|
15873
|
+
id: entryId,
|
|
15874
|
+
importance,
|
|
15875
|
+
dbPath: options.db
|
|
15876
|
+
});
|
|
15877
|
+
if (!updated.updated) {
|
|
15878
|
+
clack17.log.warn(`No active entry found with id: ${entryId}`, clackOutput);
|
|
15879
|
+
clack17.outro(void 0, clackOutput);
|
|
15880
|
+
return { exitCode: 1 };
|
|
15881
|
+
}
|
|
15882
|
+
if (updated.previousImportance === updated.importance) {
|
|
15883
|
+
clack17.log.success(`Entry ${entryId} already has importance ${updated.importance}.`, clackOutput);
|
|
15884
|
+
} else {
|
|
15885
|
+
clack17.log.success(
|
|
15886
|
+
`Updated entry ${entryId} importance: ${updated.previousImportance} -> ${updated.importance}.`,
|
|
15887
|
+
clackOutput
|
|
15888
|
+
);
|
|
15889
|
+
}
|
|
15890
|
+
clack17.outro(void 0, clackOutput);
|
|
15891
|
+
return { exitCode: 0 };
|
|
15892
|
+
}
|
|
15893
|
+
|
|
15894
|
+
// src/cli/update.ts
|
|
15895
|
+
function registerUpdateCommand(program) {
|
|
15896
|
+
program.command("update").description("Update mutable entry metadata in place").requiredOption("--id <id>", "Update a specific entry by its ID").requiredOption("--importance <n>", "New importance: 1-10").option("--force", "Skip confirmation prompt").option("--db <path>", "Path to database file").action(async (opts) => {
|
|
15897
|
+
const result = await runUpdateCommand({
|
|
15898
|
+
id: opts.id,
|
|
15899
|
+
importance: Number(opts.importance),
|
|
15900
|
+
force: opts.force === true,
|
|
15901
|
+
db: opts.db
|
|
15902
|
+
});
|
|
15903
|
+
process.exitCode = result.exitCode;
|
|
15904
|
+
});
|
|
15905
|
+
}
|
|
15906
|
+
|
|
15770
15907
|
// src/commands/watch.ts
|
|
15771
15908
|
import fs26 from "fs/promises";
|
|
15772
15909
|
import os12 from "os";
|
|
15773
15910
|
import path25 from "path";
|
|
15774
|
-
import * as
|
|
15911
|
+
import * as clack18 from "@clack/prompts";
|
|
15775
15912
|
|
|
15776
15913
|
// src/watch/watcher.ts
|
|
15777
15914
|
import { watch as watchFs } from "fs";
|
|
@@ -16425,7 +16562,7 @@ async function runWatcher(options, deps) {
|
|
|
16425
16562
|
}
|
|
16426
16563
|
|
|
16427
16564
|
// src/commands/watch.ts
|
|
16428
|
-
var
|
|
16565
|
+
var log21 = createLogger("watch");
|
|
16429
16566
|
function formatBytes2(value) {
|
|
16430
16567
|
return value.toLocaleString("en-US");
|
|
16431
16568
|
}
|
|
@@ -16697,11 +16834,11 @@ async function runWatchCommand(file, options, deps) {
|
|
|
16697
16834
|
const clackOutput = { output: process.stderr };
|
|
16698
16835
|
warnIfLocked();
|
|
16699
16836
|
installSignalHandlers();
|
|
16700
|
-
|
|
16837
|
+
clack18.intro(banner(), clackOutput);
|
|
16701
16838
|
try {
|
|
16702
16839
|
await resolvedDeps.writeWatcherPidFn();
|
|
16703
16840
|
} catch (error) {
|
|
16704
|
-
|
|
16841
|
+
clack18.log.error(
|
|
16705
16842
|
formatError(`Failed to write watcher PID file: ${toErrorMessage(error)}`),
|
|
16706
16843
|
clackOutput
|
|
16707
16844
|
);
|
|
@@ -16736,7 +16873,7 @@ async function runWatchCommand(file, options, deps) {
|
|
|
16736
16873
|
return { version: 1, files: {} };
|
|
16737
16874
|
});
|
|
16738
16875
|
if (stateWarning) {
|
|
16739
|
-
|
|
16876
|
+
clack18.log.warn(formatWarn(stateWarning), clackOutput);
|
|
16740
16877
|
await resolvedDeps.saveWatchStateFn(state);
|
|
16741
16878
|
}
|
|
16742
16879
|
const fileState = modeConfig.filePath ? getFileState(state, modeConfig.filePath) : void 0;
|
|
@@ -16744,11 +16881,11 @@ async function runWatchCommand(file, options, deps) {
|
|
|
16744
16881
|
const config = resolvedDeps.readConfigFn(process.env);
|
|
16745
16882
|
const dbPath = options.db?.trim() || config?.db?.path || "~/.agenr/knowledge.db";
|
|
16746
16883
|
if (modeConfig.mode === "file") {
|
|
16747
|
-
|
|
16884
|
+
clack18.log.info(formatLabel("Watching", modeConfig.filePath ?? "(unknown)"), clackOutput);
|
|
16748
16885
|
} else if (modeConfig.mode === "dir") {
|
|
16749
|
-
|
|
16750
|
-
|
|
16751
|
-
|
|
16886
|
+
clack18.log.info(formatLabel("Watching directory", modeConfig.sessionsDir ?? "(unknown)"), clackOutput);
|
|
16887
|
+
clack18.log.info(formatLabel("Platform", modeConfig.platform ?? "mtime"), clackOutput);
|
|
16888
|
+
clack18.log.info(
|
|
16752
16889
|
formatLabel(
|
|
16753
16890
|
"Active file",
|
|
16754
16891
|
modeConfig.filePath ? formatSwitchLabel(modeConfig.filePath) : "(waiting for session file)"
|
|
@@ -16756,22 +16893,22 @@ async function runWatchCommand(file, options, deps) {
|
|
|
16756
16893
|
clackOutput
|
|
16757
16894
|
);
|
|
16758
16895
|
}
|
|
16759
|
-
|
|
16896
|
+
clack18.log.info(
|
|
16760
16897
|
`${formatLabel("Interval", formatInterval(intervalMs))} | ${formatLabel("Min chunk", `${minChunkChars} chars`)}`,
|
|
16761
16898
|
clackOutput
|
|
16762
16899
|
);
|
|
16763
|
-
|
|
16900
|
+
clack18.log.info(
|
|
16764
16901
|
`${formatLabel("Offset", `${formatBytes2(offset)} bytes (${fileState ? "resume" : "fresh"})`)} | ${formatLabel("DB", dbPath)}`,
|
|
16765
16902
|
clackOutput
|
|
16766
16903
|
);
|
|
16767
|
-
|
|
16768
|
-
|
|
16904
|
+
clack18.log.info("", clackOutput);
|
|
16905
|
+
clack18.log.info("Waiting for changes...", clackOutput);
|
|
16769
16906
|
const emitWatchWarning = (message) => {
|
|
16770
16907
|
if (message.startsWith("Filtered:")) {
|
|
16771
|
-
|
|
16908
|
+
clack18.log.info(message, clackOutput);
|
|
16772
16909
|
return;
|
|
16773
16910
|
}
|
|
16774
|
-
|
|
16911
|
+
clack18.log.warn(formatWarn(message), clackOutput);
|
|
16775
16912
|
};
|
|
16776
16913
|
let cycleCount = 0;
|
|
16777
16914
|
let contextChain = Promise.resolve();
|
|
@@ -16799,7 +16936,7 @@ async function runWatchCommand(file, options, deps) {
|
|
|
16799
16936
|
onSwitch: (from, to, platform) => {
|
|
16800
16937
|
const fromLabel = from ? formatSwitchLabel(from) : "(none)";
|
|
16801
16938
|
const platformLabel = platform ? ` [${platform}]` : "";
|
|
16802
|
-
|
|
16939
|
+
clack18.log.info(`Switched watch file${platformLabel}: ${fromLabel} -> ${formatSwitchLabel(to)}`, clackOutput);
|
|
16803
16940
|
},
|
|
16804
16941
|
onCycle: (result, ctx) => {
|
|
16805
16942
|
cycleCount += 1;
|
|
@@ -16811,29 +16948,29 @@ async function runWatchCommand(file, options, deps) {
|
|
|
16811
16948
|
`);
|
|
16812
16949
|
}
|
|
16813
16950
|
if (result.error) {
|
|
16814
|
-
|
|
16951
|
+
clack18.log.warn(formatWarn(`[${timestamp}] Cycle ${cycleCount}: ${result.error}${fileLabel}`), clackOutput);
|
|
16815
16952
|
return;
|
|
16816
16953
|
}
|
|
16817
16954
|
if (result.skipped) {
|
|
16818
16955
|
if (result.bytesRead > 0) {
|
|
16819
|
-
|
|
16956
|
+
clack18.log.info(
|
|
16820
16957
|
`[${timestamp}] Cycle ${cycleCount}: +${formatBytes2(result.bytesRead)} bytes (below threshold, skipping)${fileLabel}`,
|
|
16821
16958
|
clackOutput
|
|
16822
16959
|
);
|
|
16823
16960
|
} else if (once2 || verbose) {
|
|
16824
|
-
|
|
16961
|
+
clack18.log.info(`[${timestamp}] Cycle ${cycleCount}: no new content${fileLabel}`, clackOutput);
|
|
16825
16962
|
}
|
|
16826
16963
|
return;
|
|
16827
16964
|
}
|
|
16828
16965
|
if (dryRun) {
|
|
16829
|
-
|
|
16966
|
+
clack18.log.info(
|
|
16830
16967
|
`[${timestamp}] Cycle ${cycleCount}: +${formatBytes2(result.bytesRead)} bytes | ${result.entriesExtracted} entries extracted (dry-run)${fileLabel}`,
|
|
16831
16968
|
clackOutput
|
|
16832
16969
|
);
|
|
16833
16970
|
return;
|
|
16834
16971
|
}
|
|
16835
16972
|
const deduped = Math.max(0, result.entriesExtracted - result.entriesStored);
|
|
16836
|
-
|
|
16973
|
+
clack18.log.info(
|
|
16837
16974
|
`[${timestamp}] Cycle ${cycleCount}: +${formatBytes2(result.bytesRead)} bytes | ${result.entriesExtracted} entries extracted | ${result.entriesStored} stored, ${deduped} deduped${fileLabel}`,
|
|
16838
16975
|
clackOutput
|
|
16839
16976
|
);
|
|
@@ -16885,11 +17022,11 @@ async function runWatchCommand(file, options, deps) {
|
|
|
16885
17022
|
emitWatchWarning(`Context DB close failed: ${toErrorMessage(error)}`);
|
|
16886
17023
|
}
|
|
16887
17024
|
}
|
|
16888
|
-
|
|
17025
|
+
clack18.log.info(
|
|
16889
17026
|
`Summary: ${summary.cycles} cycles | ${summary.entriesStored} entries stored | watched for ${formatDuration(summary.durationMs)}`,
|
|
16890
17027
|
clackOutput
|
|
16891
17028
|
);
|
|
16892
|
-
|
|
17029
|
+
clack18.outro(void 0, clackOutput);
|
|
16893
17030
|
return {
|
|
16894
17031
|
exitCode: 0,
|
|
16895
17032
|
cycles: summary.cycles,
|
|
@@ -16912,7 +17049,7 @@ async function runWatchCommand(file, options, deps) {
|
|
|
16912
17049
|
}
|
|
16913
17050
|
} else {
|
|
16914
17051
|
const forceExitTimer = setTimeout(() => {
|
|
16915
|
-
|
|
17052
|
+
log21.warn("Force exit after timeout");
|
|
16916
17053
|
resolvedDeps.exitProcessFn(1);
|
|
16917
17054
|
}, resolvedDeps.shutdownTimeoutMs);
|
|
16918
17055
|
forceExitTimer.unref();
|
|
@@ -16983,12 +17120,12 @@ function createProgram() {
|
|
|
16983
17120
|
program.enablePositionalOptions();
|
|
16984
17121
|
program.name("agenr").description("Memory infrastructure for AI agents").version(APP_VERSION).action(async () => {
|
|
16985
17122
|
const quick = getQuickStatus(process.env);
|
|
16986
|
-
|
|
17123
|
+
clack19.intro(banner());
|
|
16987
17124
|
if (!quick.configured) {
|
|
16988
|
-
|
|
16989
|
-
|
|
17125
|
+
clack19.note("Not configured", "Status");
|
|
17126
|
+
clack19.log.warn("Run " + ui.bold("agenr setup") + " to get started.");
|
|
16990
17127
|
} else if (quick.credentialAvailable) {
|
|
16991
|
-
|
|
17128
|
+
clack19.note(
|
|
16992
17129
|
[
|
|
16993
17130
|
formatLabel("Provider", quick.provider ?? "(not set)"),
|
|
16994
17131
|
formatLabel("Model", quick.model ?? "(not set)"),
|
|
@@ -16998,7 +17135,7 @@ function createProgram() {
|
|
|
16998
17135
|
"Ready"
|
|
16999
17136
|
);
|
|
17000
17137
|
} else {
|
|
17001
|
-
|
|
17138
|
+
clack19.note(
|
|
17002
17139
|
[
|
|
17003
17140
|
formatLabel("Provider", quick.provider ?? "(not set)"),
|
|
17004
17141
|
formatLabel("Model", quick.model ?? "(not set)"),
|
|
@@ -17006,16 +17143,17 @@ function createProgram() {
|
|
|
17006
17143
|
].join("\n"),
|
|
17007
17144
|
"Not authenticated"
|
|
17008
17145
|
);
|
|
17009
|
-
|
|
17146
|
+
clack19.log.warn(quick.guidance);
|
|
17010
17147
|
}
|
|
17011
17148
|
program.outputHelp();
|
|
17012
|
-
|
|
17149
|
+
clack19.outro(ui.dim("https://agenr.ai"));
|
|
17013
17150
|
});
|
|
17014
17151
|
registerExtractCommand(program);
|
|
17015
17152
|
registerStoreCommand(program);
|
|
17016
17153
|
registerRecallCommand(program);
|
|
17017
17154
|
registerRetireCommand(program);
|
|
17018
17155
|
registerTraceCommand(program);
|
|
17156
|
+
registerUpdateCommand(program);
|
|
17019
17157
|
registerReviewCommand(program);
|
|
17020
17158
|
registerEdgesCommand(program);
|
|
17021
17159
|
registerSyntheticCommand(program);
|
|
@@ -5,17 +5,19 @@ import {
|
|
|
5
5
|
clearSessionProject,
|
|
6
6
|
ensurePluginDb,
|
|
7
7
|
executeRetire,
|
|
8
|
+
executeUpdate,
|
|
8
9
|
extractTextEntries,
|
|
9
10
|
getSessionProject,
|
|
10
11
|
getSessionProjectState,
|
|
11
12
|
lookupRetireCandidates,
|
|
13
|
+
lookupUpdateCandidate,
|
|
12
14
|
normalizeStoreToolPayload,
|
|
13
15
|
resolveProjectWorthySessionProject,
|
|
14
16
|
runRecallService,
|
|
15
17
|
runStoreService,
|
|
16
18
|
runTraceService,
|
|
17
19
|
setSessionProject
|
|
18
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-CEPFOYOL.js";
|
|
19
21
|
import {
|
|
20
22
|
KNOWLEDGE_TYPES,
|
|
21
23
|
SCOPE_LEVELS,
|
|
@@ -635,7 +637,8 @@ __export(recall_exports, {
|
|
|
635
637
|
resolveAgenrPath: () => resolveAgenrPath,
|
|
636
638
|
resolveBudget: () => resolveBudget,
|
|
637
639
|
runMemoryIndex: () => runMemoryIndex,
|
|
638
|
-
runRecall: () => runRecall
|
|
640
|
+
runRecall: () => runRecall,
|
|
641
|
+
runRecallStrict: () => runRecallStrict
|
|
639
642
|
});
|
|
640
643
|
import path from "path";
|
|
641
644
|
import { fileURLToPath } from "url";
|
|
@@ -773,34 +776,45 @@ function buildSpawnArgs(agenrPath) {
|
|
|
773
776
|
}
|
|
774
777
|
return { cmd: agenrPath, args: [] };
|
|
775
778
|
}
|
|
776
|
-
|
|
779
|
+
function buildRecallServiceRequest(budget, project, query, options, dbPath) {
|
|
780
|
+
const isBrowse = options?.context === "browse";
|
|
781
|
+
const trimmedQuery = query?.trim() ?? "";
|
|
782
|
+
const truncatedQuery = trimmedQuery.length > RECALL_QUERY_MAX_CHARS ? trimmedQuery.slice(0, RECALL_QUERY_MAX_CHARS) : trimmedQuery;
|
|
783
|
+
return {
|
|
784
|
+
queryInput: isBrowse ? void 0 : truncatedQuery || void 0,
|
|
785
|
+
context: isBrowse ? "browse" : "session-start",
|
|
786
|
+
browse: isBrowse,
|
|
787
|
+
since: isBrowse ? options?.since ?? "1d" : void 0,
|
|
788
|
+
limit: options?.limit,
|
|
789
|
+
minImportance: options?.minImportance,
|
|
790
|
+
expiry: options?.expiry,
|
|
791
|
+
universalOnly: options?.nullProjectOnly === true || options?.universalOnly === true,
|
|
792
|
+
project: options?.nullProjectOnly || options?.universalOnly || !project ? void 0 : [project],
|
|
793
|
+
projectStrict: Boolean(project && !options?.nullProjectOnly && !options?.universalOnly),
|
|
794
|
+
budget: isBrowse ? void 0 : budget,
|
|
795
|
+
dbPath,
|
|
796
|
+
metadataMode: "non-browse"
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
function shapeRecallResult(result) {
|
|
800
|
+
return {
|
|
801
|
+
query: result.payload.query,
|
|
802
|
+
results: result.payload.results.map((item) => ({
|
|
803
|
+
entry: item.entry,
|
|
804
|
+
score: item.score,
|
|
805
|
+
category: item.category
|
|
806
|
+
}))
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
async function runRecallStrict(_agenrPath, budget, project, query, options, dbPath) {
|
|
810
|
+
const result = await runRecallService(
|
|
811
|
+
buildRecallServiceRequest(budget, project, query, options, dbPath)
|
|
812
|
+
);
|
|
813
|
+
return shapeRecallResult(result);
|
|
814
|
+
}
|
|
815
|
+
async function runRecall(agenrPath, budget, project, query, options, dbPath) {
|
|
777
816
|
try {
|
|
778
|
-
|
|
779
|
-
const trimmedQuery = query?.trim() ?? "";
|
|
780
|
-
const truncatedQuery = trimmedQuery.length > RECALL_QUERY_MAX_CHARS ? trimmedQuery.slice(0, RECALL_QUERY_MAX_CHARS) : trimmedQuery;
|
|
781
|
-
const result = await runRecallService({
|
|
782
|
-
queryInput: isBrowse ? void 0 : truncatedQuery || void 0,
|
|
783
|
-
context: isBrowse ? "browse" : "session-start",
|
|
784
|
-
browse: isBrowse,
|
|
785
|
-
since: isBrowse ? options?.since ?? "1d" : void 0,
|
|
786
|
-
limit: options?.limit,
|
|
787
|
-
minImportance: options?.minImportance,
|
|
788
|
-
expiry: options?.expiry,
|
|
789
|
-
universalOnly: options?.nullProjectOnly === true || options?.universalOnly === true,
|
|
790
|
-
project: options?.nullProjectOnly || options?.universalOnly || !project ? void 0 : [project],
|
|
791
|
-
projectStrict: Boolean(project && !options?.nullProjectOnly && !options?.universalOnly),
|
|
792
|
-
budget: isBrowse ? void 0 : budget,
|
|
793
|
-
dbPath,
|
|
794
|
-
metadataMode: "non-browse"
|
|
795
|
-
});
|
|
796
|
-
return {
|
|
797
|
-
query: result.payload.query,
|
|
798
|
-
results: result.payload.results.map((item) => ({
|
|
799
|
-
entry: item.entry,
|
|
800
|
-
score: item.score,
|
|
801
|
-
category: item.category
|
|
802
|
-
}))
|
|
803
|
-
};
|
|
817
|
+
return await runRecallStrict(agenrPath, budget, project, query, options, dbPath);
|
|
804
818
|
} catch {
|
|
805
819
|
return null;
|
|
806
820
|
}
|
|
@@ -1668,6 +1682,53 @@ async function runRetireTool(_agenrPath, params, dbPath) {
|
|
|
1668
1682
|
};
|
|
1669
1683
|
}
|
|
1670
1684
|
}
|
|
1685
|
+
async function runUpdateTool(_agenrPath, params, dbPath) {
|
|
1686
|
+
try {
|
|
1687
|
+
const entryId = asString(params.entry_id);
|
|
1688
|
+
const importance = asNumber(params.importance);
|
|
1689
|
+
if (!entryId) {
|
|
1690
|
+
return {
|
|
1691
|
+
content: [{ type: "text", text: "agenr_update failed: entry_id is required" }]
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
if (!Number.isInteger(importance) || importance < 1 || importance > 10) {
|
|
1695
|
+
return {
|
|
1696
|
+
content: [{ type: "text", text: "agenr_update failed: importance must be an integer between 1 and 10" }]
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
const lookup = await lookupUpdateCandidate({
|
|
1700
|
+
id: entryId,
|
|
1701
|
+
dbPath
|
|
1702
|
+
});
|
|
1703
|
+
if (!lookup.candidate) {
|
|
1704
|
+
return {
|
|
1705
|
+
content: [{ type: "text", text: `agenr_update failed: No active entry found with id: ${entryId}` }]
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
const updated = await executeUpdate({
|
|
1709
|
+
id: entryId,
|
|
1710
|
+
importance,
|
|
1711
|
+
dbPath
|
|
1712
|
+
});
|
|
1713
|
+
if (!updated.updated) {
|
|
1714
|
+
return {
|
|
1715
|
+
content: [{ type: "text", text: `Entry ${entryId} was not updated because it is no longer active.` }]
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
return {
|
|
1719
|
+
content: [
|
|
1720
|
+
{
|
|
1721
|
+
type: "text",
|
|
1722
|
+
text: updated.previousImportance === updated.importance ? `Entry ${entryId} already has importance ${updated.importance}.` : `Updated entry ${entryId} importance: ${updated.previousImportance} -> ${updated.importance}.`
|
|
1723
|
+
}
|
|
1724
|
+
]
|
|
1725
|
+
};
|
|
1726
|
+
} catch (error) {
|
|
1727
|
+
return {
|
|
1728
|
+
content: [{ type: "text", text: `agenr_update failed: ${toErrorMessage(error)}` }]
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1671
1732
|
async function runTraceTool(_agenrPath, params, dbPath) {
|
|
1672
1733
|
try {
|
|
1673
1734
|
const entryId = asString(params.entry_id);
|
|
@@ -4592,6 +4653,7 @@ function diversifySessionStartBrowseCandidates(browseResult, limit = SESSION_STA
|
|
|
4592
4653
|
|
|
4593
4654
|
// src/openclaw-plugin/hooks/before-prompt-build-session-start-recall.ts
|
|
4594
4655
|
var SESSION_START_BROWSE_SINCE = "30d";
|
|
4656
|
+
var SQLITE_BUSY_RETRY_DELAYS_MS = [100, 200, 400];
|
|
4595
4657
|
var sessionStartLog3 = createLogger("session-start");
|
|
4596
4658
|
function formatConfiguredCoreProjects(coreProjects) {
|
|
4597
4659
|
return `[${coreProjects.join(",")}]`;
|
|
@@ -4611,6 +4673,59 @@ function describeMemoryIndexResult(result) {
|
|
|
4611
4673
|
return "memory index unavailable: invalid response";
|
|
4612
4674
|
}
|
|
4613
4675
|
}
|
|
4676
|
+
function isSqliteBusyMessage(message) {
|
|
4677
|
+
return message.toUpperCase().includes("SQLITE_BUSY");
|
|
4678
|
+
}
|
|
4679
|
+
async function sleepMs(ms) {
|
|
4680
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
4681
|
+
}
|
|
4682
|
+
async function loadSessionStartBrowseWithRetry(params, project, options) {
|
|
4683
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
4684
|
+
try {
|
|
4685
|
+
return await runRecallStrict(
|
|
4686
|
+
params.agenrPath,
|
|
4687
|
+
params.budget,
|
|
4688
|
+
project,
|
|
4689
|
+
void 0,
|
|
4690
|
+
options,
|
|
4691
|
+
params.config?.dbPath
|
|
4692
|
+
);
|
|
4693
|
+
} catch (error) {
|
|
4694
|
+
const message = toErrorMessage(error);
|
|
4695
|
+
const isBusy = isSqliteBusyMessage(message);
|
|
4696
|
+
if (!isBusy || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) {
|
|
4697
|
+
sessionStartLog3.warn(`browse unavailable: ${message}`);
|
|
4698
|
+
return null;
|
|
4699
|
+
}
|
|
4700
|
+
const delayMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt] ?? 400;
|
|
4701
|
+
sessionStartLog3.warn(
|
|
4702
|
+
`browse hit SQLITE_BUSY, retrying in ${delayMs}ms (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1})`
|
|
4703
|
+
);
|
|
4704
|
+
await sleepMs(delayMs);
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4708
|
+
async function loadMemoryIndexWithRetry(params) {
|
|
4709
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
4710
|
+
const result = await runMemoryIndex(
|
|
4711
|
+
params.agenrPath,
|
|
4712
|
+
params.config?.dbPath
|
|
4713
|
+
);
|
|
4714
|
+
const message = result.status === "error" ? result.error : "";
|
|
4715
|
+
const isBusy = result.status === "error" && isSqliteBusyMessage(message);
|
|
4716
|
+
if (!isBusy || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) {
|
|
4717
|
+
if (result.status !== "ok") {
|
|
4718
|
+
sessionStartLog3.warn(describeMemoryIndexResult(result));
|
|
4719
|
+
}
|
|
4720
|
+
return result;
|
|
4721
|
+
}
|
|
4722
|
+
const delayMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt] ?? 400;
|
|
4723
|
+
sessionStartLog3.warn(
|
|
4724
|
+
`memory index hit SQLITE_BUSY, retrying in ${delayMs}ms (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1})`
|
|
4725
|
+
);
|
|
4726
|
+
await sleepMs(delayMs);
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4614
4729
|
async function fetchSessionStartRecallData(currentSessionProjectKey, params) {
|
|
4615
4730
|
const coreProjects = params.config?.coreProjects ?? [];
|
|
4616
4731
|
const sessionStartBrowseProject = await resolveSessionStartBrowseProject(
|
|
@@ -4629,18 +4744,8 @@ async function fetchSessionStartRecallData(currentSessionProjectKey, params) {
|
|
|
4629
4744
|
}
|
|
4630
4745
|
const [coreSettled, browseSettled, memoryIndexSettled] = await Promise.allSettled([
|
|
4631
4746
|
fetchCoreEntries(params.agenrPath, coreProjects, params.config?.dbPath),
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
params.budget,
|
|
4635
|
-
sessionStartBrowseProject,
|
|
4636
|
-
void 0,
|
|
4637
|
-
browseRecallOptions,
|
|
4638
|
-
params.config?.dbPath
|
|
4639
|
-
),
|
|
4640
|
-
runMemoryIndex(
|
|
4641
|
-
params.agenrPath,
|
|
4642
|
-
params.config?.dbPath
|
|
4643
|
-
)
|
|
4747
|
+
loadSessionStartBrowseWithRetry(params, sessionStartBrowseProject, browseRecallOptions),
|
|
4748
|
+
loadMemoryIndexWithRetry(params)
|
|
4644
4749
|
]);
|
|
4645
4750
|
if (coreSettled.status === "rejected") {
|
|
4646
4751
|
sessionStartLog3.error(`core recall failed: ${toErrorMessage(coreSettled.reason)}`);
|
|
@@ -4660,12 +4765,16 @@ async function fetchSessionStartRecallData(currentSessionProjectKey, params) {
|
|
|
4660
4765
|
"session-start",
|
|
4661
4766
|
`core recall returned ${coreResult?.results.length ?? 0} entries (configured coreProjects=${formatConfiguredCoreProjects(coreProjects)})`
|
|
4662
4767
|
);
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4768
|
+
if (browseResult) {
|
|
4769
|
+
debugLog(params.debug, "session-start", `browse returned ${browseResult.results.length} entries`);
|
|
4770
|
+
}
|
|
4771
|
+
if (memoryIndexResult?.status === "ok") {
|
|
4772
|
+
debugLog(
|
|
4773
|
+
params.debug,
|
|
4774
|
+
"session-start",
|
|
4775
|
+
describeMemoryIndexResult(memoryIndexResult)
|
|
4776
|
+
);
|
|
4777
|
+
}
|
|
4669
4778
|
return {
|
|
4670
4779
|
coreResult,
|
|
4671
4780
|
browseResult,
|
|
@@ -6279,6 +6388,26 @@ function registerAgenrTools(api, params) {
|
|
|
6279
6388
|
}
|
|
6280
6389
|
}
|
|
6281
6390
|
);
|
|
6391
|
+
api.registerTool(
|
|
6392
|
+
{
|
|
6393
|
+
name: "agenr_update",
|
|
6394
|
+
label: "Agenr Update",
|
|
6395
|
+
description: "Update an existing memory entry in place. Currently supports importance only.",
|
|
6396
|
+
parameters: Type.Object({
|
|
6397
|
+
entry_id: Type.String({ description: "Entry ID to update." }),
|
|
6398
|
+
importance: Type.Integer({ minimum: 1, maximum: 10, description: "New importance score." })
|
|
6399
|
+
}),
|
|
6400
|
+
async execute(_toolCallId, toolParams) {
|
|
6401
|
+
const runtimeConfig = api.pluginConfig;
|
|
6402
|
+
if (runtimeConfig?.enabled === false) {
|
|
6403
|
+
return makeDisabledToolResult();
|
|
6404
|
+
}
|
|
6405
|
+
const agenrPath = resolveAgenrPath(runtimeConfig);
|
|
6406
|
+
const dbPath = runtimeConfig?.dbPath;
|
|
6407
|
+
return runUpdateTool(agenrPath, toolParams, dbPath);
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
);
|
|
6282
6411
|
api.registerTool(
|
|
6283
6412
|
{
|
|
6284
6413
|
name: "agenr_trace",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agenr",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.81",
|
|
4
4
|
"openclaw": {
|
|
5
5
|
"extensions": [
|
|
6
6
|
"dist/openclaw-plugin/index.js"
|
|
@@ -11,19 +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 --clean",
|
|
16
|
-
"check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test",
|
|
17
|
-
"dev": "tsup src/cli.ts src/cli-main.ts --format esm --watch",
|
|
18
|
-
"lint": "eslint .",
|
|
19
|
-
"format": "prettier --write .",
|
|
20
|
-
"format:check": "prettier --check .",
|
|
21
|
-
"test": "vitest run",
|
|
22
|
-
"test:watch": "vitest",
|
|
23
|
-
"typecheck": "tsc --noEmit",
|
|
24
|
-
"verify:dist": "node scripts/verify-dist.js",
|
|
25
|
-
"prepack": "pnpm build && pnpm verify:dist"
|
|
26
|
-
},
|
|
27
14
|
"dependencies": {
|
|
28
15
|
"@clack/prompts": "^1.0.1",
|
|
29
16
|
"@libsql/client": "^0.17.0",
|
|
@@ -72,9 +59,16 @@
|
|
|
72
59
|
"README.md"
|
|
73
60
|
],
|
|
74
61
|
"author": "agenr-ai",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsup src/cli.ts src/cli-main.ts src/openclaw-plugin/index.ts --format esm --dts --clean",
|
|
64
|
+
"check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test",
|
|
65
|
+
"dev": "tsup src/cli.ts src/cli-main.ts --format esm --watch",
|
|
66
|
+
"lint": "eslint .",
|
|
67
|
+
"format": "prettier --write .",
|
|
68
|
+
"format:check": "prettier --check .",
|
|
69
|
+
"test": "vitest run",
|
|
70
|
+
"test:watch": "vitest",
|
|
71
|
+
"typecheck": "tsc --noEmit",
|
|
72
|
+
"verify:dist": "node scripts/verify-dist.js"
|
|
79
73
|
}
|
|
80
|
-
}
|
|
74
|
+
}
|