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 +13 -0
- package/dist/chunk-JSPUNQPG.js +608 -0
- package/dist/cli-main.d.ts +1 -1
- package/dist/cli-main.js +207 -12
- package/dist/openclaw-plugin/index.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
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
|
+
};
|
package/dist/cli-main.d.ts
CHANGED
|
@@ -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-
|
|
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
|
|
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
|
|
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,
|
|
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,
|
package/openclaw.plugin.json
CHANGED