agenr 0.8.0 → 0.8.2

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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.2] - 2026-02-22
4
+
5
+ ### Added
6
+ - Per-platform extraction prompt addenda for codex/claude-code (code session rules with inline confidence caps) and plaud (meeting transcript rules)
7
+ - plaud added to KNOWLEDGE_PLATFORMS and normalizeKnowledgePlatform
8
+ - applyConfidenceCap now enforces importance cap for codex and claude-code platforms
9
+ - All CLI --platform help text updated to include plaud
10
+
11
+ ## [0.8.1] - 2026-02-22
12
+
13
+ ### Fixed
14
+ - fix(openclaw-plugin): sync plugin version in openclaw.plugin.json to match npm package (was stale at 0.7.21, now 0.8.1)
15
+
3
16
  ## [0.8.0] - 2026-02-22
4
17
 
5
18
  ### Added
@@ -0,0 +1,608 @@
1
+ // src/version.ts
2
+ import { createRequire } from "module";
3
+ var require2 = createRequire(import.meta.url);
4
+ var APP_VERSION = (() => {
5
+ try {
6
+ const raw = require2("../package.json");
7
+ if (typeof raw.version === "string" && raw.version.trim().length > 0) {
8
+ return raw.version.trim();
9
+ }
10
+ } catch {
11
+ }
12
+ const fromEnv = process.env.npm_package_version;
13
+ if (typeof fromEnv === "string" && fromEnv.trim().length > 0) {
14
+ return fromEnv.trim();
15
+ }
16
+ return "0.0.0";
17
+ })();
18
+
19
+ // src/db/client.ts
20
+ import { createClient } from "@libsql/client";
21
+ import fs from "fs/promises";
22
+ import os from "os";
23
+ import path from "path";
24
+
25
+ // src/db/schema.ts
26
+ var CREATE_IDX_ENTRIES_EMBEDDING_SQL = `
27
+ CREATE INDEX IF NOT EXISTS idx_entries_embedding ON entries (
28
+ libsql_vector_idx(embedding, 'metric=cosine', 'compress_neighbors=float8', 'max_neighbors=50')
29
+ )
30
+ `;
31
+ var CREATE_ENTRIES_FTS_TABLE_SQL = `
32
+ CREATE VIRTUAL TABLE IF NOT EXISTS entries_fts USING fts5(
33
+ content, subject, content=entries, content_rowid=rowid
34
+ )
35
+ `;
36
+ var CREATE_ENTRIES_FTS_TRIGGER_AI_SQL = `
37
+ CREATE TRIGGER IF NOT EXISTS entries_ai AFTER INSERT ON entries BEGIN
38
+ INSERT INTO entries_fts(rowid, content, subject) VALUES (new.rowid, new.content, new.subject);
39
+ END
40
+ `;
41
+ var CREATE_ENTRIES_FTS_TRIGGER_AD_SQL = `
42
+ CREATE TRIGGER IF NOT EXISTS entries_ad AFTER DELETE ON entries BEGIN
43
+ INSERT INTO entries_fts(entries_fts, rowid, content, subject) VALUES ('delete', old.rowid, old.content, old.subject);
44
+ END
45
+ `;
46
+ var CREATE_ENTRIES_FTS_TRIGGER_AU_SQL = `
47
+ CREATE TRIGGER IF NOT EXISTS entries_au AFTER UPDATE ON entries BEGIN
48
+ INSERT INTO entries_fts(entries_fts, rowid, content, subject) VALUES ('delete', old.rowid, old.content, old.subject);
49
+ INSERT INTO entries_fts(rowid, content, subject) VALUES (new.rowid, new.content, new.subject);
50
+ END
51
+ `;
52
+ var REBUILD_ENTRIES_FTS_SQL = "INSERT INTO entries_fts(entries_fts) VALUES ('rebuild')";
53
+ var LEGACY_IMPORTANCE_BACKFILL_META_KEY = "legacy_importance_backfill_from_confidence_v1";
54
+ var CREATE_TABLE_AND_TRIGGER_STATEMENTS = [
55
+ `
56
+ CREATE TABLE IF NOT EXISTS _meta (
57
+ key TEXT PRIMARY KEY,
58
+ value TEXT NOT NULL,
59
+ updated_at TEXT NOT NULL
60
+ )
61
+ `,
62
+ `
63
+ CREATE TABLE IF NOT EXISTS entries (
64
+ id TEXT PRIMARY KEY,
65
+ type TEXT NOT NULL,
66
+ subject TEXT NOT NULL,
67
+ canonical_key TEXT,
68
+ content TEXT NOT NULL,
69
+ importance INTEGER NOT NULL,
70
+ expiry TEXT NOT NULL,
71
+ scope TEXT DEFAULT 'private',
72
+ platform TEXT DEFAULT NULL,
73
+ project TEXT DEFAULT NULL,
74
+ source_file TEXT,
75
+ source_context TEXT,
76
+ embedding F32_BLOB(1024),
77
+ created_at TEXT NOT NULL,
78
+ updated_at TEXT NOT NULL,
79
+ last_recalled_at TEXT,
80
+ recall_count INTEGER DEFAULT 0,
81
+ confirmations INTEGER DEFAULT 0,
82
+ contradictions INTEGER DEFAULT 0,
83
+ superseded_by TEXT,
84
+ content_hash TEXT,
85
+ merged_from INTEGER DEFAULT 0,
86
+ consolidated_at TEXT,
87
+ retired INTEGER NOT NULL DEFAULT 0,
88
+ retired_at TEXT,
89
+ retired_reason TEXT,
90
+ suppressed_contexts TEXT,
91
+ FOREIGN KEY (superseded_by) REFERENCES entries(id)
92
+ )
93
+ `,
94
+ `
95
+ CREATE TABLE IF NOT EXISTS tags (
96
+ entry_id TEXT NOT NULL,
97
+ tag TEXT NOT NULL,
98
+ PRIMARY KEY (entry_id, tag),
99
+ FOREIGN KEY (entry_id) REFERENCES entries(id) ON DELETE CASCADE
100
+ )
101
+ `,
102
+ `
103
+ CREATE TABLE IF NOT EXISTS relations (
104
+ id TEXT PRIMARY KEY,
105
+ source_id TEXT NOT NULL,
106
+ target_id TEXT NOT NULL,
107
+ relation_type TEXT NOT NULL,
108
+ created_at TEXT NOT NULL,
109
+ FOREIGN KEY (source_id) REFERENCES entries(id) ON DELETE CASCADE,
110
+ FOREIGN KEY (target_id) REFERENCES entries(id) ON DELETE CASCADE
111
+ )
112
+ `,
113
+ `
114
+ CREATE TABLE IF NOT EXISTS ingest_log (
115
+ id TEXT PRIMARY KEY,
116
+ file_path TEXT NOT NULL,
117
+ ingested_at TEXT NOT NULL,
118
+ entries_added INTEGER NOT NULL,
119
+ entries_updated INTEGER NOT NULL,
120
+ entries_skipped INTEGER NOT NULL,
121
+ duration_ms INTEGER NOT NULL,
122
+ content_hash TEXT,
123
+ entries_superseded INTEGER NOT NULL DEFAULT 0,
124
+ dedup_llm_calls INTEGER NOT NULL DEFAULT 0
125
+ )
126
+ `,
127
+ `
128
+ CREATE TABLE IF NOT EXISTS entry_sources (
129
+ merged_entry_id TEXT NOT NULL REFERENCES entries(id),
130
+ source_entry_id TEXT NOT NULL REFERENCES entries(id),
131
+ original_confirmations INTEGER NOT NULL DEFAULT 0,
132
+ original_recall_count INTEGER NOT NULL DEFAULT 0,
133
+ original_created_at TEXT,
134
+ PRIMARY KEY (merged_entry_id, source_entry_id)
135
+ )
136
+ `,
137
+ `
138
+ CREATE TABLE IF NOT EXISTS signal_watermarks (
139
+ consumer_id TEXT PRIMARY KEY,
140
+ last_received_seq INTEGER NOT NULL DEFAULT 0,
141
+ updated_at TEXT NOT NULL
142
+ )
143
+ `,
144
+ CREATE_ENTRIES_FTS_TABLE_SQL,
145
+ CREATE_ENTRIES_FTS_TRIGGER_AI_SQL,
146
+ CREATE_ENTRIES_FTS_TRIGGER_AD_SQL,
147
+ CREATE_ENTRIES_FTS_TRIGGER_AU_SQL
148
+ ];
149
+ var CREATE_INDEX_STATEMENTS = [
150
+ CREATE_IDX_ENTRIES_EMBEDDING_SQL,
151
+ "CREATE INDEX IF NOT EXISTS idx_entries_type ON entries(type)",
152
+ "CREATE INDEX IF NOT EXISTS idx_entries_type_canonical_key ON entries(type, canonical_key)",
153
+ "CREATE INDEX IF NOT EXISTS idx_entries_expiry ON entries(expiry)",
154
+ "CREATE INDEX IF NOT EXISTS idx_entries_scope ON entries(scope)",
155
+ "CREATE INDEX IF NOT EXISTS idx_entries_platform ON entries(platform)",
156
+ "CREATE INDEX IF NOT EXISTS idx_entries_project ON entries(project)",
157
+ "CREATE INDEX IF NOT EXISTS idx_entries_created ON entries(created_at)",
158
+ "CREATE INDEX IF NOT EXISTS idx_entries_superseded ON entries(superseded_by)",
159
+ "CREATE INDEX IF NOT EXISTS idx_entries_content_hash ON entries(content_hash)",
160
+ "CREATE INDEX IF NOT EXISTS idx_tags_tag ON tags(tag)",
161
+ "CREATE INDEX IF NOT EXISTS idx_relations_source ON relations(source_id)",
162
+ "CREATE INDEX IF NOT EXISTS idx_relations_target ON relations(target_id)",
163
+ "CREATE UNIQUE INDEX IF NOT EXISTS idx_ingest_log_file_hash ON ingest_log(file_path, content_hash)"
164
+ ];
165
+ var COLUMN_MIGRATIONS = [
166
+ {
167
+ table: "entries",
168
+ column: "importance",
169
+ sql: "ALTER TABLE entries ADD COLUMN importance INTEGER NOT NULL DEFAULT 5"
170
+ },
171
+ {
172
+ table: "entries",
173
+ column: "canonical_key",
174
+ sql: "ALTER TABLE entries ADD COLUMN canonical_key TEXT"
175
+ },
176
+ {
177
+ table: "entries",
178
+ column: "scope",
179
+ sql: "ALTER TABLE entries ADD COLUMN scope TEXT DEFAULT 'private'"
180
+ },
181
+ {
182
+ table: "entries",
183
+ column: "content_hash",
184
+ sql: "ALTER TABLE entries ADD COLUMN content_hash TEXT"
185
+ },
186
+ {
187
+ table: "entries",
188
+ column: "merged_from",
189
+ sql: "ALTER TABLE entries ADD COLUMN merged_from INTEGER DEFAULT 0"
190
+ },
191
+ {
192
+ table: "entries",
193
+ column: "consolidated_at",
194
+ sql: "ALTER TABLE entries ADD COLUMN consolidated_at TEXT"
195
+ },
196
+ {
197
+ table: "entries",
198
+ column: "platform",
199
+ sql: "ALTER TABLE entries ADD COLUMN platform TEXT DEFAULT NULL"
200
+ },
201
+ {
202
+ table: "entries",
203
+ column: "project",
204
+ sql: "ALTER TABLE entries ADD COLUMN project TEXT DEFAULT NULL"
205
+ },
206
+ {
207
+ table: "entries",
208
+ column: "retired",
209
+ sql: "ALTER TABLE entries ADD COLUMN retired INTEGER NOT NULL DEFAULT 0"
210
+ },
211
+ {
212
+ table: "entries",
213
+ column: "idx_entries_retired",
214
+ sql: "CREATE INDEX IF NOT EXISTS idx_entries_retired ON entries (retired) WHERE retired = 0",
215
+ isIndex: true
216
+ },
217
+ {
218
+ table: "entries",
219
+ column: "retired_at",
220
+ sql: "ALTER TABLE entries ADD COLUMN retired_at TEXT"
221
+ },
222
+ {
223
+ table: "entries",
224
+ column: "retired_reason",
225
+ sql: "ALTER TABLE entries ADD COLUMN retired_reason TEXT"
226
+ },
227
+ {
228
+ table: "entries",
229
+ column: "suppressed_contexts",
230
+ sql: "ALTER TABLE entries ADD COLUMN suppressed_contexts TEXT"
231
+ },
232
+ {
233
+ table: "ingest_log",
234
+ column: "content_hash",
235
+ sql: "ALTER TABLE ingest_log ADD COLUMN content_hash TEXT"
236
+ },
237
+ {
238
+ table: "ingest_log",
239
+ column: "entries_superseded",
240
+ sql: "ALTER TABLE ingest_log ADD COLUMN entries_superseded INTEGER NOT NULL DEFAULT 0"
241
+ },
242
+ {
243
+ table: "ingest_log",
244
+ column: "dedup_llm_calls",
245
+ sql: "ALTER TABLE ingest_log ADD COLUMN dedup_llm_calls INTEGER NOT NULL DEFAULT 0"
246
+ },
247
+ {
248
+ table: "entry_sources",
249
+ column: "original_created_at",
250
+ sql: "ALTER TABLE entry_sources ADD COLUMN original_created_at TEXT"
251
+ },
252
+ {
253
+ table: "entries",
254
+ column: "recall_intervals",
255
+ sql: "ALTER TABLE entries ADD COLUMN recall_intervals TEXT DEFAULT NULL"
256
+ }
257
+ ];
258
+ async function initSchema(client) {
259
+ for (const statement of CREATE_TABLE_AND_TRIGGER_STATEMENTS) {
260
+ await client.execute(statement);
261
+ }
262
+ let willRunLegacyBackfill = false;
263
+ try {
264
+ const earlyEntriesInfo = await client.execute("PRAGMA table_info(entries)");
265
+ const earlyColumns = new Set(earlyEntriesInfo.rows.map((row) => String(row.name)));
266
+ if (earlyColumns.has("confidence")) {
267
+ const sentinel = await client.execute({
268
+ sql: "SELECT 1 AS found FROM _meta WHERE key = ? LIMIT 1",
269
+ args: [LEGACY_IMPORTANCE_BACKFILL_META_KEY]
270
+ });
271
+ const alreadyBackfilled = sentinel.rows.length > 0;
272
+ willRunLegacyBackfill = !alreadyBackfilled;
273
+ }
274
+ } catch {
275
+ }
276
+ try {
277
+ const entriesCountResult = await client.execute("SELECT COUNT(*) AS count FROM entries");
278
+ const entriesCount = Number(entriesCountResult.rows[0]?.count ?? 0);
279
+ const ftsCountResult = await client.execute("SELECT COUNT(*) AS count FROM entries_fts");
280
+ const ftsCount = Number(ftsCountResult.rows[0]?.count ?? 0);
281
+ if (entriesCount > 0 && ftsCount === 0 && !willRunLegacyBackfill) {
282
+ await client.execute(REBUILD_ENTRIES_FTS_SQL);
283
+ }
284
+ } catch {
285
+ }
286
+ for (const migration of COLUMN_MIGRATIONS) {
287
+ if (migration.isIndex) {
288
+ const existingIndex = await client.execute({
289
+ sql: "SELECT name FROM sqlite_master WHERE type='index' AND name=?",
290
+ args: [migration.column]
291
+ });
292
+ if (existingIndex.rows.length > 0) {
293
+ continue;
294
+ }
295
+ await client.execute(migration.sql);
296
+ continue;
297
+ }
298
+ const info = await client.execute(`PRAGMA table_info(${migration.table})`);
299
+ const hasColumn = info.rows.some((row) => String(row.name) === migration.column);
300
+ if (!hasColumn) {
301
+ await client.execute(migration.sql);
302
+ }
303
+ }
304
+ const entriesInfo = await client.execute("PRAGMA table_info(entries)");
305
+ const entryColumns = new Set(entriesInfo.rows.map((row) => String(row.name)));
306
+ const backfillSentinel = await client.execute({
307
+ sql: "SELECT 1 AS found FROM _meta WHERE key = ? LIMIT 1",
308
+ args: [LEGACY_IMPORTANCE_BACKFILL_META_KEY]
309
+ });
310
+ const legacyBackfillDone = backfillSentinel.rows.length > 0;
311
+ if (entryColumns.has("confidence") && entryColumns.has("importance") && !legacyBackfillDone) {
312
+ try {
313
+ await client.execute("DROP TRIGGER IF EXISTS entries_ai");
314
+ await client.execute("DROP TRIGGER IF EXISTS entries_ad");
315
+ await client.execute("DROP TRIGGER IF EXISTS entries_au");
316
+ await client.execute("DROP TABLE IF EXISTS entries_fts");
317
+ } catch {
318
+ }
319
+ await client.execute(`
320
+ UPDATE entries
321
+ SET importance = CASE lower(trim(confidence))
322
+ WHEN 'low' THEN 3
323
+ WHEN 'medium' THEN 6
324
+ WHEN 'high' THEN 8
325
+ ELSE
326
+ CASE
327
+ WHEN CAST(confidence AS INTEGER) BETWEEN 1 AND 10 THEN CAST(confidence AS INTEGER)
328
+ ELSE 5
329
+ END
330
+ END
331
+ WHERE importance = 5
332
+ `);
333
+ try {
334
+ await client.execute(CREATE_ENTRIES_FTS_TABLE_SQL);
335
+ await client.execute(CREATE_ENTRIES_FTS_TRIGGER_AI_SQL);
336
+ await client.execute(CREATE_ENTRIES_FTS_TRIGGER_AD_SQL);
337
+ await client.execute(CREATE_ENTRIES_FTS_TRIGGER_AU_SQL);
338
+ await client.execute(REBUILD_ENTRIES_FTS_SQL);
339
+ } catch {
340
+ }
341
+ try {
342
+ await client.execute({
343
+ sql: `
344
+ INSERT INTO _meta (key, value, updated_at)
345
+ VALUES (?, datetime('now'), datetime('now'))
346
+ ON CONFLICT(key) DO NOTHING
347
+ `,
348
+ args: [LEGACY_IMPORTANCE_BACKFILL_META_KEY]
349
+ });
350
+ } catch {
351
+ }
352
+ }
353
+ for (const statement of CREATE_INDEX_STATEMENTS) {
354
+ await client.execute(statement);
355
+ }
356
+ await client.execute({
357
+ sql: `
358
+ INSERT INTO _meta (key, value, updated_at)
359
+ VALUES ('db_created_at', datetime('now'), datetime('now'))
360
+ ON CONFLICT(key) DO NOTHING
361
+ `,
362
+ args: []
363
+ });
364
+ await client.execute({
365
+ sql: `
366
+ INSERT INTO _meta (key, value, updated_at)
367
+ VALUES ('schema_version', ?, datetime('now'))
368
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
369
+ `,
370
+ args: [APP_VERSION]
371
+ });
372
+ }
373
+ async function resetDb(db) {
374
+ const foreignKeysResult = await db.execute("PRAGMA foreign_keys");
375
+ const foreignKeysRow = foreignKeysResult.rows[0];
376
+ const previousForeignKeys = Number(foreignKeysRow?.foreign_keys ?? (foreignKeysRow ? Object.values(foreignKeysRow)[0] : 1)) === 0 ? 0 : 1;
377
+ await db.execute("PRAGMA foreign_keys=OFF");
378
+ try {
379
+ const schemaObjects = await db.execute(`
380
+ SELECT type, name
381
+ FROM sqlite_master
382
+ WHERE name NOT LIKE 'sqlite_%'
383
+ ORDER BY
384
+ CASE type
385
+ WHEN 'trigger' THEN 1
386
+ WHEN 'index' THEN 2
387
+ WHEN 'table' THEN 3
388
+ ELSE 4
389
+ END,
390
+ name
391
+ `);
392
+ for (const row of schemaObjects.rows) {
393
+ const type = String(row.type ?? "");
394
+ const name = String(row.name ?? "");
395
+ if (!type || !name) {
396
+ continue;
397
+ }
398
+ const safeName = name.replace(/"/g, '""');
399
+ if (type === "trigger") {
400
+ await db.execute(`DROP TRIGGER IF EXISTS "${safeName}"`);
401
+ continue;
402
+ }
403
+ if (type === "index") {
404
+ await db.execute(`DROP INDEX IF EXISTS "${safeName}"`);
405
+ continue;
406
+ }
407
+ if (type === "table") {
408
+ await db.execute(`DROP TABLE IF EXISTS "${safeName}"`);
409
+ }
410
+ }
411
+ await initSchema(db);
412
+ } finally {
413
+ await db.execute(`PRAGMA foreign_keys=${previousForeignKeys === 0 ? "OFF" : "ON"}`);
414
+ }
415
+ }
416
+
417
+ // src/db/client.ts
418
+ var DEFAULT_DB_PATH = path.join(os.homedir(), ".agenr", "knowledge.db");
419
+ var walInitByClient = /* @__PURE__ */ new WeakMap();
420
+ var didWarnVectorIndexCorruption = false;
421
+ var WAL_CHECKPOINT_MAX_ATTEMPTS = 5;
422
+ var WAL_CHECKPOINT_RETRY_MS = 50;
423
+ function resolveUserPath(inputPath) {
424
+ if (!inputPath.startsWith("~")) {
425
+ return inputPath;
426
+ }
427
+ return path.join(os.homedir(), inputPath.slice(1));
428
+ }
429
+ function resolveDbPath(dbPath) {
430
+ if (dbPath === ":memory:") {
431
+ return dbPath;
432
+ }
433
+ return resolveUserPath(dbPath);
434
+ }
435
+ function normalizeBackupSourcePath(dbPath) {
436
+ const strippedDbPath = dbPath.startsWith("file:") ? dbPath.slice("file:".length) : dbPath;
437
+ return path.resolve(resolveDbPath(strippedDbPath));
438
+ }
439
+ function isErrnoCode(error, code) {
440
+ return error?.code === code;
441
+ }
442
+ async function copySidecarIfPresent(sourcePath, targetPath) {
443
+ try {
444
+ await fs.copyFile(sourcePath, targetPath);
445
+ } catch (error) {
446
+ if (isErrnoCode(error, "ENOENT")) {
447
+ return;
448
+ }
449
+ throw error;
450
+ }
451
+ }
452
+ function buildBackupPath(dbPath) {
453
+ const resolvedDbPath = normalizeBackupSourcePath(dbPath);
454
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace(/Z$/, "");
455
+ return path.join(
456
+ path.dirname(resolvedDbPath),
457
+ `${path.basename(resolvedDbPath)}.backup-pre-reset-${timestamp}Z`
458
+ );
459
+ }
460
+ function getDb(dbPath) {
461
+ const rawPath = dbPath?.trim() ? dbPath.trim() : DEFAULT_DB_PATH;
462
+ if (rawPath === ":memory:") {
463
+ return createClient({ url: ":memory:" });
464
+ }
465
+ if (rawPath.startsWith("file:")) {
466
+ const client2 = createClient({ url: rawPath });
467
+ walInitByClient.set(
468
+ client2,
469
+ client2.execute("PRAGMA journal_mode=WAL").then(() => client2.execute("PRAGMA busy_timeout=3000")).then(() => void 0)
470
+ );
471
+ return client2;
472
+ }
473
+ const resolvedPath = resolveDbPath(rawPath);
474
+ const client = createClient({ url: `file:${resolvedPath}` });
475
+ walInitByClient.set(
476
+ client,
477
+ client.execute("PRAGMA journal_mode=WAL").then(() => client.execute("PRAGMA busy_timeout=3000")).then(() => void 0)
478
+ );
479
+ return client;
480
+ }
481
+ async function initDb(client) {
482
+ const walInit = walInitByClient.get(client);
483
+ if (walInit) {
484
+ await walInit;
485
+ await client.execute("PRAGMA wal_autocheckpoint=1000");
486
+ }
487
+ await initSchema(client);
488
+ try {
489
+ const hasEntries = await client.execute(
490
+ "SELECT 1 FROM entries WHERE embedding IS NOT NULL LIMIT 1"
491
+ );
492
+ if (hasEntries.rows.length > 0) {
493
+ await client.execute(`
494
+ SELECT count(*) FROM vector_top_k(
495
+ 'idx_entries_embedding',
496
+ (SELECT embedding FROM entries WHERE embedding IS NOT NULL LIMIT 1),
497
+ 1
498
+ )
499
+ `);
500
+ }
501
+ } catch {
502
+ if (!didWarnVectorIndexCorruption) {
503
+ didWarnVectorIndexCorruption = true;
504
+ process.stderr.write(
505
+ "\n\u26A0\uFE0F Vector index may be corrupted. Run `agenr db rebuild-index` to fix.\n\n"
506
+ );
507
+ }
508
+ }
509
+ }
510
+ function toFiniteNumber(value) {
511
+ if (typeof value === "number" && Number.isFinite(value)) {
512
+ return value;
513
+ }
514
+ if (typeof value === "bigint") {
515
+ return Number(value);
516
+ }
517
+ if (typeof value === "string" && value.trim().length > 0) {
518
+ const parsed = Number(value);
519
+ return Number.isFinite(parsed) ? parsed : null;
520
+ }
521
+ return null;
522
+ }
523
+ function extractBusyFromCheckpointRow(row) {
524
+ if (!row || typeof row !== "object") {
525
+ return null;
526
+ }
527
+ const record = row;
528
+ const busyValue = record.busy ?? Object.values(record)[0];
529
+ return toFiniteNumber(busyValue);
530
+ }
531
+ async function validatedWalCheckpoint(client) {
532
+ for (let attempt = 1; attempt <= WAL_CHECKPOINT_MAX_ATTEMPTS; attempt += 1) {
533
+ const result = await client.execute("PRAGMA wal_checkpoint(TRUNCATE)");
534
+ const busy = extractBusyFromCheckpointRow(result.rows[0]);
535
+ if (busy === null) {
536
+ throw new Error("WAL checkpoint returned an unexpected result and could not be validated.");
537
+ }
538
+ if (busy === 0) {
539
+ return;
540
+ }
541
+ if (attempt < WAL_CHECKPOINT_MAX_ATTEMPTS) {
542
+ await new Promise((resolve) => setTimeout(resolve, WAL_CHECKPOINT_RETRY_MS * attempt));
543
+ continue;
544
+ }
545
+ throw new Error(
546
+ `WAL checkpoint did not finish (busy=${busy}). Active readers are blocking backup safety.`
547
+ );
548
+ }
549
+ }
550
+ async function walCheckpoint(client) {
551
+ await validatedWalCheckpoint(client);
552
+ }
553
+ async function backupDb(dbPath) {
554
+ if (dbPath === ":memory:") {
555
+ throw new Error("Cannot back up in-memory databases.");
556
+ }
557
+ const checkpointClient = getDb(dbPath);
558
+ try {
559
+ await walCheckpoint(checkpointClient);
560
+ } finally {
561
+ closeDb(checkpointClient);
562
+ }
563
+ const resolvedDbPath = normalizeBackupSourcePath(dbPath);
564
+ const backupPath = buildBackupPath(dbPath);
565
+ await fs.copyFile(resolvedDbPath, backupPath);
566
+ await copySidecarIfPresent(`${resolvedDbPath}-wal`, `${backupPath}-wal`);
567
+ await copySidecarIfPresent(`${resolvedDbPath}-shm`, `${backupPath}-shm`);
568
+ return backupPath;
569
+ }
570
+ function closeDb(client) {
571
+ client.close();
572
+ }
573
+
574
+ // src/types.ts
575
+ var KNOWLEDGE_TYPES = [
576
+ "fact",
577
+ "decision",
578
+ "preference",
579
+ "todo",
580
+ "relationship",
581
+ "event",
582
+ "lesson"
583
+ ];
584
+ var IMPORTANCE_MIN = 1;
585
+ var IMPORTANCE_MAX = 10;
586
+ var EXPIRY_LEVELS = ["core", "permanent", "temporary"];
587
+ var SCOPE_LEVELS = ["private", "personal", "public"];
588
+ var KNOWLEDGE_PLATFORMS = ["openclaw", "claude-code", "codex", "plaud"];
589
+
590
+ export {
591
+ APP_VERSION,
592
+ CREATE_IDX_ENTRIES_EMBEDDING_SQL,
593
+ initSchema,
594
+ resetDb,
595
+ DEFAULT_DB_PATH,
596
+ buildBackupPath,
597
+ getDb,
598
+ initDb,
599
+ walCheckpoint,
600
+ backupDb,
601
+ closeDb,
602
+ KNOWLEDGE_TYPES,
603
+ IMPORTANCE_MIN,
604
+ IMPORTANCE_MAX,
605
+ EXPIRY_LEVELS,
606
+ SCOPE_LEVELS,
607
+ KNOWLEDGE_PLATFORMS
608
+ };
@@ -5,7 +5,7 @@ import { Client } from '@libsql/client';
5
5
  declare const KNOWLEDGE_TYPES: readonly ["fact", "decision", "preference", "todo", "relationship", "event", "lesson"];
6
6
  declare const EXPIRY_LEVELS: readonly ["core", "permanent", "temporary"];
7
7
  declare const SCOPE_LEVELS: readonly ["private", "personal", "public"];
8
- declare const KNOWLEDGE_PLATFORMS: readonly ["openclaw", "claude-code", "codex"];
8
+ declare const KNOWLEDGE_PLATFORMS: readonly ["openclaw", "claude-code", "codex", "plaud"];
9
9
  type KnowledgeType = (typeof KNOWLEDGE_TYPES)[number];
10
10
  type Expiry = (typeof EXPIRY_LEVELS)[number];
11
11
  type Scope = (typeof SCOPE_LEVELS)[number];
package/dist/cli-main.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  initSchema,
17
17
  resetDb,
18
18
  walCheckpoint
19
- } from "./chunk-VJ2WB7JG.js";
19
+ } from "./chunk-JSPUNQPG.js";
20
20
 
21
21
  // src/cli-main.ts
22
22
  import fs36 from "fs/promises";
@@ -1064,7 +1064,8 @@ function normalizeKnowledgePlatform(value) {
1064
1064
  if (!value) {
1065
1065
  return null;
1066
1066
  }
1067
- const normalized = value.trim().toLowerCase();
1067
+ const trimmed = value.trim();
1068
+ const normalized = trimmed.toLowerCase();
1068
1069
  if (normalized === "openclaw") {
1069
1070
  return "openclaw";
1070
1071
  }
@@ -1074,6 +1075,9 @@ function normalizeKnowledgePlatform(value) {
1074
1075
  if (normalized === "codex") {
1075
1076
  return "codex";
1076
1077
  }
1078
+ if (normalized === "plaud") {
1079
+ return "plaud";
1080
+ }
1077
1081
  return null;
1078
1082
  }
1079
1083
 
@@ -11475,7 +11479,7 @@ PREFERENCE:
11475
11479
  "source_context": "User mentioned scheduling preference during calendar discussion -- scored 6 not 8 because no parallel session needs to act on this immediately; it is a low-urgency convenience preference"
11476
11480
  }
11477
11481
 
11478
- // NOTE: These examples are drawn from OpenClaw transcripts (agent role labels, tool-verified claims). They provide soft cross-platform guidance for hedged-claim handling; mechanical enforcement (importance cap + unverified tag) is only applied for the openclaw platform via applyConfidenceCap().
11482
+ // NOTE: These examples are drawn from OpenClaw transcripts (agent role labels, tool-verified claims). They provide soft cross-platform guidance for hedged-claim handling; mechanical enforcement (importance cap + unverified tag) is applied for openclaw, codex, and claude-code via applyConfidenceCap().
11479
11483
  Example: hedged agent claim (correct handling):
11480
11484
  {
11481
11485
  "type": "fact",
@@ -11613,6 +11617,187 @@ RECOMMENDATION - not a factual claim, never capped:
11613
11617
  [m00050][assistant] I think we should switch to pnpm for consistency.
11614
11618
  Result: this is a suggestion, not a fact - do not apply the cap
11615
11619
  `;
11620
+ var CODE_PLATFORM_ADDENDUM = `
11621
+ ## Code Session Rules (codex / claude-code)
11622
+
11623
+ You are extracting from a coding agent session. These rules take precedence over the base extraction rules where they conflict.
11624
+
11625
+ ### What to capture with elevated importance
11626
+
11627
+ Code audit FINDINGS (confirmed bugs, security issues, correctness flaws):
11628
+ - Importance: 8 minimum. Set expiry field to "permanent" (not "temporary").
11629
+ - Content MUST include: file path, line number or function name, exact nature of the flaw.
11630
+ - Example: "Bug in src/db/recall.ts fetchSessionCandidates(): missing AND retired = 0
11631
+ filter causes retired entries to appear in recall results."
11632
+ - If evidence is missing (no file/line), drop importance to 7.
11633
+
11634
+ Architectural decisions made during exploration:
11635
+ - Importance: 8. Set expiry to "permanent".
11636
+ - Include the specific files or modules involved and the rationale.
11637
+
11638
+ Breaking changes discovered in dependencies or APIs:
11639
+ - Importance: 9 if cross-session impact. Include affected callers.
11640
+
11641
+ Feature implementations completed with tests passing:
11642
+ - Importance: 7 event. Include branch name and what was shipped.
11643
+
11644
+ ### What to skip entirely
11645
+
11646
+ Navigation and exploration events:
11647
+ - "I explored N files", "I read X", "I looked at Y", "I checked the codebase"
11648
+ - These are execution noise. Skip unless a specific finding resulted.
11649
+
11650
+ Build or test status alone:
11651
+ - "Tests pass" or "build succeeded" with no new behavioral change = skip or 6 max.
11652
+ - Only store if it signals a resolved blocker or confirmed fix.
11653
+
11654
+ Routine file reads without findings:
11655
+ - "I found that file X exists" or "I opened Y to understand Z" = skip.
11656
+
11657
+ ### Evidence requirement for importance >= 8
11658
+
11659
+ If you assign importance 8 or higher to a code finding, the content MUST contain
11660
+ at least one of:
11661
+ - A file path
11662
+ - A line number or function name
11663
+ - A quoted snippet of the problematic code
11664
+
11665
+ Without that evidence, cap at 7.
11666
+
11667
+ ### Role labels and confidence caps in code sessions
11668
+
11669
+ Transcript lines are prefixed with [user] or [assistant] role labels.
11670
+ [user] messages are task descriptions or steering from the developer.
11671
+ [assistant] messages are the coding agent findings, reasoning, and actions.
11672
+
11673
+ Apply confidence-cap rules to [assistant] statements only.
11674
+ Never apply this logic to [user] messages.
11675
+
11676
+ An [assistant] statement is verified when it meets ANY of these conditions:
11677
+ - It immediately follows a line matching the pattern [called X: ...] such as
11678
+ [called exec: ...], [called Read: ...], or any [called ...: ...] marker
11679
+ - It uses explicit verification language: "I confirmed", "I verified", "the output shows",
11680
+ "the test shows", "confirmed via", "the file shows", "I ran", "the result shows"
11681
+
11682
+ An [assistant] statement is hedged when it uses speculative language WITHOUT
11683
+ any of the verification signals above:
11684
+ - "I think", "I believe", "I assume", "I'm not sure", "probably", "likely",
11685
+ "it might be", "it could be", "possibly", "maybe", "I recall that"
11686
+
11687
+ Note: The importance cap threshold (5) and the "unverified" tag name must match
11688
+ the rules in applyConfidenceCap() in extractor.ts. If you change one, change both.
11689
+
11690
+ When an [assistant] factual claim is hedged and unverified:
11691
+ - Set importance to min(your assigned importance, 5)
11692
+ - Add the tag "unverified"
11693
+
11694
+ EXCEPTION: Do NOT cap recommendations or opinions. "I think we should refactor X"
11695
+ is a recommendation, not a factual claim. Only cap FACTUAL CLAIMS made with hedging language.
11696
+ `;
11697
+ var PLAUD_PLATFORM_ADDENDUM = `
11698
+ ## Meeting Transcript Rules (plaud)
11699
+
11700
+ You are extracting from a Plaud meeting transcript. These rules take precedence
11701
+ over the base extraction rules where they conflict.
11702
+
11703
+ ### Transcript format
11704
+
11705
+ Plaud transcripts use the format:
11706
+ [HH:MM] Speaker Name: spoken text
11707
+
11708
+ Speaker names appear inline - use them directly for attribution. There are no
11709
+ [user] or [assistant] role labels. Do not apply confidence-cap logic based on
11710
+ role labels. Apply normal human epistemic judgment: uncertain language ("I think",
11711
+ "I assume", "probably") lowers confidence; direct statements and confirmed facts do not.
11712
+
11713
+ Transcripts are generated by speech-to-text and will contain filler words (uh, um,
11714
+ gonna, you know), incomplete sentences, and lowercase runs. Extract the substance
11715
+ beneath these artifacts. Do not penalize transcript quality when deciding what to store.
11716
+
11717
+ ### Calibration for meeting chunks
11718
+
11719
+ Meeting transcript chunks are dense by default. Unlike agent session chunks where
11720
+ most content is tool output and status updates, meeting chunks contain human
11721
+ speech with a high density of decisions, commitments, and action items.
11722
+
11723
+ Override the base calibration for plaud: typical meeting chunk 3-8 entries.
11724
+ Do not suppress legitimate extractions because the base calibration says most
11725
+ chunks should be 0. Extract every action item, commitment, and decision you find.
11726
+
11727
+ ### What to capture with elevated importance
11728
+
11729
+ Action items - explicit OR implicit:
11730
+ - Importance: 8, type TODO. Set expiry to "permanent".
11731
+ - Explicit: a speaker directly states "action item: X will do Y by Z."
11732
+ - Implicit: speaker says "I'll..." or "we need to..." followed by a concrete deliverable.
11733
+ Use the speaker name from the transcript label as the owner.
11734
+ - Content MUST include: speaker name as owner, the action, and deadline if stated.
11735
+ - If no clear owner can be inferred, store as importance 7 TODO anyway.
11736
+ - Example: "Action item: Jordan to create two tracking tickets for the system migration
11737
+ work by end of week."
11738
+
11739
+ Decisions with cross-functional or product impact:
11740
+ - Importance: 8+ if the decision affects multiple teams or external stakeholders.
11741
+ - Importance: 7 if scoped to a single team.
11742
+ - Include who drove the decision and any stated rationale.
11743
+ - Example: "Decision: proceed with data extraction from System A immediately.
11744
+ Hold System B extraction until post-cutover data completeness can be verified.
11745
+ Rationale: System B new instance confirmed to have incomplete historical records
11746
+ (Alex, Sam, Morgan)."
11747
+
11748
+ Commitments made by named speakers:
11749
+ - Importance: 8 if binding (deadline, deliverable, or external party involved).
11750
+ - Importance: 7 if informal.
11751
+ - Use the speaker label from the transcript as the person name.
11752
+
11753
+ Key facts about systems, timelines, or risks surfaced in the meeting:
11754
+ - Importance: 7-8 depending on cross-session impact.
11755
+ - Example: "System B EMR cutover to separate instance on [cutover date]. After
11756
+ cutover, shared warehouse instance goes read-only for System B tenant. New
11757
+ instance may have incomplete historical data due to minimal migration scope,
11758
+ not a full data reload."
11759
+
11760
+ Meeting summary as an event:
11761
+ - Importance: 7 event.
11762
+ - Include: meeting name, date if available (often in filename), attendees by name,
11763
+ top 2-3 outcomes.
11764
+
11765
+ ### What to skip entirely
11766
+
11767
+ Pure filler and crosstalk:
11768
+ - "Hello?", "Oh, sorry.", "Yeah.", "Okay." alone with no content = skip.
11769
+ - Greetings, scheduling, "how was your weekend" = skip.
11770
+
11771
+ Vague mentions without commitment or decision:
11772
+ - "we should think about X someday" = skip.
11773
+ - "that would be interesting" without follow-through = skip.
11774
+
11775
+ Pure status with no decision or action:
11776
+ - "Team A is still working on the integration" alone = skip.
11777
+
11778
+ ### Confidence tag rule
11779
+
11780
+ Do NOT apply the "unverified" tag to meeting transcript entries. The "unverified"
11781
+ tag is for agent sessions where claims can be verified against tool output.
11782
+ Meeting transcripts have no such verification signal. Apply normal epistemic
11783
+ judgment to importance scoring instead.
11784
+
11785
+ ### Speaker name guidance
11786
+
11787
+ Use the name exactly as it appears in the transcript label.
11788
+ If only a first name is available, use it - do not guess the last name.
11789
+ If a full name is available, use it.
11790
+
11791
+ Sometimes Plaud cannot identify a speaker and labels them "Speaker 1", "Speaker 2", etc.
11792
+ When this happens:
11793
+ - If the speaker is later identified by name elsewhere in the transcript (someone
11794
+ addresses them directly, or they introduce themselves), use that real name instead.
11795
+ - If the speaker remains unidentified, use the label as-is: "Speaker 1 committed to..."
11796
+ - For action items or decisions where the owner is only "Speaker X" with no real name
11797
+ ever surfacing, downgrade importance to 7 regardless of content significance.
11798
+ Unknown attribution weakens the reliability of the entry.
11799
+ - Do NOT guess or infer real names from context clues alone.
11800
+ `;
11616
11801
  var CHUNKED_CALIBRATION_BLOCK = `Calibration:
11617
11802
  - Typical chunk: 0-3 entries. Most chunks: 0.
11618
11803
  - Score 9 or 10: very rare, at most 1 per significant session, often 0
@@ -11650,6 +11835,16 @@ function buildExtractionSystemPrompt(platform, wholeFile = false) {
11650
11835
  return `${prompt}
11651
11836
 
11652
11837
  ${OPENCLAW_CONFIDENCE_ADDENDUM}`;
11838
+ }
11839
+ if (platform === "codex" || platform === "claude-code") {
11840
+ return `${prompt}
11841
+
11842
+ ${CODE_PLATFORM_ADDENDUM}`;
11843
+ }
11844
+ if (platform === "plaud") {
11845
+ return `${prompt}
11846
+
11847
+ ${PLAUD_PLATFORM_ADDENDUM}`;
11653
11848
  }
11654
11849
  return prompt;
11655
11850
  }
@@ -11917,7 +12112,7 @@ function validateEntry(entry) {
11917
12112
  return null;
11918
12113
  }
11919
12114
  function applyConfidenceCap(entry, platform) {
11920
- if (platform === "openclaw" && entry.tags.includes("unverified") && entry.importance > 5) {
12115
+ if ((platform === "openclaw" || platform === "codex" || platform === "claude-code") && entry.tags.includes("unverified") && entry.importance > 5) {
11921
12116
  return { ...entry, importance: 5 };
11922
12117
  }
11923
12118
  return entry;
@@ -18412,13 +18607,13 @@ function createProgram() {
18412
18607
  });
18413
18608
  process.exitCode = result.exitCode;
18414
18609
  });
18415
- program.command("store").description("Store extracted knowledge entries in the local database").argument("[files...]", "One or more extraction JSON files").option("--db <path>", "Database path override").option("--dry-run", "Show what would be stored without writing", false).option("--verbose", "Show per-entry dedup decisions", false).option("--force", "Skip dedup and store all entries as new", false).option("--aggressive", "Enable aggressive dedup (lower similarity threshold)", false).option("--platform <name>", "Platform tag: openclaw, claude-code, codex").option("--project <name>", "Project tag (lowercase).", (val, prev) => [...prev, val], []).option("--online-dedup", "Enable online LLM dedup at write time", true).option("--no-online-dedup", "Disable online LLM dedup at write time").option("--dedup-threshold <n>", "Similarity threshold for online dedup (0.0-1.0)").action(
18610
+ program.command("store").description("Store extracted knowledge entries in the local database").argument("[files...]", "One or more extraction JSON files").option("--db <path>", "Database path override").option("--dry-run", "Show what would be stored without writing", false).option("--verbose", "Show per-entry dedup decisions", false).option("--force", "Skip dedup and store all entries as new", false).option("--aggressive", "Enable aggressive dedup (lower similarity threshold)", false).option("--platform <name>", "Platform tag: openclaw, claude-code, codex, plaud").option("--project <name>", "Project tag (lowercase).", (val, prev) => [...prev, val], []).option("--online-dedup", "Enable online LLM dedup at write time", true).option("--no-online-dedup", "Disable online LLM dedup at write time").option("--dedup-threshold <n>", "Similarity threshold for online dedup (0.0-1.0)").action(
18416
18611
  async (files, opts) => {
18417
18612
  const result = await runStoreCommand(files ?? [], opts);
18418
18613
  process.exitCode = result.exitCode;
18419
18614
  }
18420
18615
  );
18421
- program.command("recall").description("Recall knowledge from the local database").argument("[query]", "Natural language query").option("--limit <n>", "Maximum number of results", parseIntOption, 10).option("--type <types>", "Filter by comma-separated entry types").option("--tags <tags>", "Filter by comma-separated tags").option("--min-importance <n>", "Minimum importance: 1-10").option("--since <duration>", "Filter by recency (1h, 7d, 30d, 1y) or ISO timestamp").option("--until <date>", "Only entries at or before this time (ISO date or relative, e.g. 7d, 1m)").option("--expiry <level>", "Filter by expiry: core|permanent|temporary").option("--platform <name>", "Filter by platform: openclaw, claude-code, codex").option("--project <name>", "Filter by project (repeatable)", (val, prev) => [...prev, val], []).option("--exclude-project <name>", "Exclude entries from project (repeatable)", (val, prev) => [...prev, val], []).option("--strict", "Exclude NULL-project entries from results", false).option("--json", "Output JSON", false).option("--db <path>", "Database path override").option("--budget <tokens>", "Approximate token budget", parseIntOption).option("--context <mode>", "Context mode: default|session-start|topic:<query>", "default").option("--scope <level>", "Visibility scope: private|personal|public", "private").option("--no-boost", "Disable scoring boosts and use raw vector similarity", false).option("--no-update", "Do not increment recall metadata", false).action(async (query, opts) => {
18616
+ program.command("recall").description("Recall knowledge from the local database").argument("[query]", "Natural language query").option("--limit <n>", "Maximum number of results", parseIntOption, 10).option("--type <types>", "Filter by comma-separated entry types").option("--tags <tags>", "Filter by comma-separated tags").option("--min-importance <n>", "Minimum importance: 1-10").option("--since <duration>", "Filter by recency (1h, 7d, 30d, 1y) or ISO timestamp").option("--until <date>", "Only entries at or before this time (ISO date or relative, e.g. 7d, 1m)").option("--expiry <level>", "Filter by expiry: core|permanent|temporary").option("--platform <name>", "Filter by platform: openclaw, claude-code, codex, plaud").option("--project <name>", "Filter by project (repeatable)", (val, prev) => [...prev, val], []).option("--exclude-project <name>", "Exclude entries from project (repeatable)", (val, prev) => [...prev, val], []).option("--strict", "Exclude NULL-project entries from results", false).option("--json", "Output JSON", false).option("--db <path>", "Database path override").option("--budget <tokens>", "Approximate token budget", parseIntOption).option("--context <mode>", "Context mode: default|session-start|topic:<query>", "default").option("--scope <level>", "Visibility scope: private|personal|public", "private").option("--no-boost", "Disable scoring boosts and use raw vector similarity", false).option("--no-update", "Do not increment recall metadata", false).action(async (query, opts) => {
18422
18617
  const result = await runRecallCommand(query, {
18423
18618
  limit: opts.limit,
18424
18619
  type: opts.type,
@@ -18476,7 +18671,7 @@ function createProgram() {
18476
18671
  });
18477
18672
  program.command("context").description(
18478
18673
  "Generate a context file for AI tool integration (session-start recall runs locally; no embedding API calls)."
18479
- ).option("--output <path>", "Output file path", "~/.agenr/CONTEXT.md").option("--budget <tokens>", "Approximate token budget", parseIntOption, 2e3).option("--limit <n>", "Max entries per category", parseIntOption, 10).option("--db <path>", "Database path override").option("--platform <name>", "Filter by platform: openclaw, claude-code, codex").option("--project <name>", "Filter by project (repeatable)", (val, prev) => [...prev, val], []).option("--exclude-project <name>", "Exclude entries from project (repeatable)", (val, prev) => [...prev, val], []).option("--strict", "Exclude NULL-project entries from results", false).option("--json", "Output JSON", false).option("--quiet", "Suppress stderr output", false).action(
18674
+ ).option("--output <path>", "Output file path", "~/.agenr/CONTEXT.md").option("--budget <tokens>", "Approximate token budget", parseIntOption, 2e3).option("--limit <n>", "Max entries per category", parseIntOption, 10).option("--db <path>", "Database path override").option("--platform <name>", "Filter by platform: openclaw, claude-code, codex, plaud").option("--project <name>", "Filter by project (repeatable)", (val, prev) => [...prev, val], []).option("--exclude-project <name>", "Exclude entries from project (repeatable)", (val, prev) => [...prev, val], []).option("--strict", "Exclude NULL-project entries from results", false).option("--json", "Output JSON", false).option("--quiet", "Suppress stderr output", false).action(
18480
18675
  async (opts) => {
18481
18676
  const result = await runContextCommand({
18482
18677
  output: opts.output,
@@ -18493,7 +18688,7 @@ function createProgram() {
18493
18688
  process.exitCode = result.exitCode;
18494
18689
  }
18495
18690
  );
18496
- program.command("watch").description("Watch a transcript file and auto-extract knowledge as it grows").argument("[file]", "Transcript file to watch (.jsonl, .md, .txt)").option("--dir <path>", "Sessions directory to watch (resolver picks active file)").option("--platform <name>", "Session platform: openclaw, claude-code, codex, mtime").option("--auto", "Deprecated: use --platform <name> instead", false).option("--interval <seconds>", "Polling interval in seconds", parseIntOption, 300).option("--min-chunk <chars>", "Minimum new chars before extraction", parseIntOption, 2e3).option("--context <path>", "Regenerate context file after each cycle").option("--db <path>", "Database path override").option("--model <model>", "LLM model to use").option("--provider <name>", "LLM provider: anthropic, openai, openai-codex").option("--raw", "Bypass adapter filtering (pass transcripts through unmodified)", false).option("--no-pre-fetch", "Disable elaborative encoding pre-fetch").option("--verbose", "Show extraction progress", false).option("--dry-run", "Extract without storing", false).option("--once", "Run one cycle and exit", false).option("--json", "Output JSON results", false).action(async (file, opts) => {
18691
+ program.command("watch").description("Watch a transcript file and auto-extract knowledge as it grows").argument("[file]", "Transcript file to watch (.jsonl, .md, .txt)").option("--dir <path>", "Sessions directory to watch (resolver picks active file)").option("--platform <name>", "Session platform: openclaw, claude-code, codex, plaud, mtime").option("--auto", "Deprecated: use --platform <name> instead", false).option("--interval <seconds>", "Polling interval in seconds", parseIntOption, 300).option("--min-chunk <chars>", "Minimum new chars before extraction", parseIntOption, 2e3).option("--context <path>", "Regenerate context file after each cycle").option("--db <path>", "Database path override").option("--model <model>", "LLM model to use").option("--provider <name>", "LLM provider: anthropic, openai, openai-codex").option("--raw", "Bypass adapter filtering (pass transcripts through unmodified)", false).option("--no-pre-fetch", "Disable elaborative encoding pre-fetch").option("--verbose", "Show extraction progress", false).option("--dry-run", "Extract without storing", false).option("--once", "Run one cycle and exit", false).option("--json", "Output JSON results", false).action(async (file, opts) => {
18497
18692
  const result = await runWatchCommand(file, {
18498
18693
  ...opts,
18499
18694
  noPreFetch: opts.noPreFetch === true
@@ -18504,7 +18699,7 @@ function createProgram() {
18504
18699
  const result = await runTodoCommand(subcommand, subject, { db: opts.db });
18505
18700
  process.exitCode = result.exitCode;
18506
18701
  });
18507
- program.command("ingest").description("Bulk-ingest knowledge from files and directories").argument("<paths...>", "Files or directories to process").option("--glob <pattern>", "File filter glob", "**/*.{jsonl,md,txt}").option("--db <path>", "Database path override").option("--model <model>", "LLM model to use").option("--provider <name>", "LLM provider: anthropic, openai, openai-codex").option("--platform <name>", "Platform tag: openclaw, claude-code, codex").option("--project <name>", "Project tag (lowercase).", (val, prev) => [...prev, val], []).option("--verbose", "Show per-file details", false).option("--raw", "Bypass adapter filtering (pass transcripts through unmodified)", false).option("--dry-run", "Extract without storing", false).option("--json", "Output JSON results", false).option("--concurrency <n>", "Parallel chunk extractions", parseIntOption, 5).option(
18702
+ program.command("ingest").description("Bulk-ingest knowledge from files and directories").argument("<paths...>", "Files or directories to process").option("--glob <pattern>", "File filter glob", "**/*.{jsonl,md,txt}").option("--db <path>", "Database path override").option("--model <model>", "LLM model to use").option("--provider <name>", "LLM provider: anthropic, openai, openai-codex").option("--platform <name>", "Platform tag: openclaw, claude-code, codex, plaud").option("--project <name>", "Project tag (lowercase).", (val, prev) => [...prev, val], []).option("--verbose", "Show per-file details", false).option("--raw", "Bypass adapter filtering (pass transcripts through unmodified)", false).option("--dry-run", "Extract without storing", false).option("--json", "Output JSON results", false).option("--concurrency <n>", "Parallel chunk extractions", parseIntOption, 5).option(
18508
18703
  "--workers <n>",
18509
18704
  "Number of files to process in parallel (default: 10). Each worker uses --concurrency chunk parallelism. Total concurrent LLM calls = workers x concurrency. Reduce if hitting rate limits. Writes retry once per sub-batch unless --no-retry is set.",
18510
18705
  parseIntOption,
@@ -18540,7 +18735,7 @@ function createProgram() {
18540
18735
  });
18541
18736
  process.exitCode = result.exitCode;
18542
18737
  });
18543
- program.command("consolidate").description("Consolidate and clean up the knowledge database").option("--rules-only", "Only run rule-based cleanup (no LLM)", false).option("--dry-run", "Show what would happen without making changes", false).option("--forget", "Delete forgetting candidates after consolidation", false).option("--report", "Print pre-run stats report (with --dry-run: report only)", false).option("--platform <name>", "Scope consolidation to platform: openclaw, claude-code, codex").option("--project <name>", "Scope consolidation to project (repeatable)", (val, prev) => [...prev, val], []).option("--exclude-project <name>", "Exclude entries from project (repeatable)", (val, prev) => [...prev, val], []).option(
18738
+ program.command("consolidate").description("Consolidate and clean up the knowledge database").option("--rules-only", "Only run rule-based cleanup (no LLM)", false).option("--dry-run", "Show what would happen without making changes", false).option("--forget", "Delete forgetting candidates after consolidation", false).option("--report", "Print pre-run stats report (with --dry-run: report only)", false).option("--platform <name>", "Scope consolidation to platform: openclaw, claude-code, codex, plaud").option("--project <name>", "Scope consolidation to project (repeatable)", (val, prev) => [...prev, val], []).option("--exclude-project <name>", "Exclude entries from project (repeatable)", (val, prev) => [...prev, val], []).option(
18544
18739
  "--min-cluster <n>",
18545
18740
  "Minimum cluster size for merge (default: 2)",
18546
18741
  (value) => Number.parseInt(value, 10)
@@ -18580,7 +18775,7 @@ function createProgram() {
18580
18775
  await runMcpCommand(opts);
18581
18776
  });
18582
18777
  const daemonCommand = program.command("daemon").description("Manage the agenr watch daemon");
18583
- daemonCommand.command("install").description("Install and start the watch daemon (macOS launchd)").option("--force", "Overwrite existing launchd plist", false).option("--interval <seconds>", "Watch interval for daemon mode", parseIntOption, 120).option("--dir <path>", "Sessions directory to watch (overrides auto-detection)").option("--platform <name>", "Platform name (openclaw, codex, claude-code)").option("--node-path <path>", "Node binary path override for launchd").option("--context <path>", "Regenerate context file after each cycle").action(async (opts) => {
18778
+ daemonCommand.command("install").description("Install and start the watch daemon (macOS launchd)").option("--force", "Overwrite existing launchd plist", false).option("--interval <seconds>", "Watch interval for daemon mode", parseIntOption, 120).option("--dir <path>", "Sessions directory to watch (overrides auto-detection)").option("--platform <name>", "Platform name (openclaw, claude-code, codex, plaud)").option("--node-path <path>", "Node binary path override for launchd").option("--context <path>", "Regenerate context file after each cycle").action(async (opts) => {
18584
18779
  const result = await runDaemonInstallCommand(opts);
18585
18780
  process.exitCode = result.exitCode;
18586
18781
  });
@@ -18617,7 +18812,7 @@ function createProgram() {
18617
18812
  await runDbVersionCommand({ db: opts.db });
18618
18813
  process.exitCode = 0;
18619
18814
  });
18620
- dbCommand.command("export").description("Export all non-superseded entries").option("--json", "Export JSON", false).option("--md", "Export markdown", false).option("--db <path>", "Database path override").option("--platform <name>", "Filter by platform: openclaw, claude-code, codex").option("--project <name>", "Filter by project (repeatable)", (val, prev) => [...prev, val], []).option("--exclude-project <name>", "Exclude entries from project (repeatable)", (val, prev) => [...prev, val], []).action(async (opts) => {
18815
+ dbCommand.command("export").description("Export all non-superseded entries").option("--json", "Export JSON", false).option("--md", "Export markdown", false).option("--db <path>", "Database path override").option("--platform <name>", "Filter by platform: openclaw, claude-code, codex, plaud").option("--project <name>", "Filter by project (repeatable)", (val, prev) => [...prev, val], []).option("--exclude-project <name>", "Exclude entries from project (repeatable)", (val, prev) => [...prev, val], []).action(async (opts) => {
18621
18816
  await runDbExportCommand({
18622
18817
  db: opts.db,
18623
18818
  json: opts.json,
@@ -4,7 +4,7 @@ import {
4
4
  closeDb,
5
5
  getDb,
6
6
  initDb
7
- } from "../chunk-VJ2WB7JG.js";
7
+ } from "../chunk-JSPUNQPG.js";
8
8
 
9
9
  // src/openclaw-plugin/index.ts
10
10
  import { Type } from "@sinclair/typebox";
@@ -2,7 +2,7 @@
2
2
  "id": "agenr",
3
3
  "name": "agenr Memory",
4
4
  "description": "Local memory layer - injects agenr context at session start",
5
- "version": "0.7.21",
5
+ "version": "0.8.2",
6
6
  "skills": [
7
7
  "skills"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenr",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "openclaw": {
5
5
  "extensions": [
6
6
  "dist/openclaw-plugin/index.js"