clementine-agent 1.18.19 → 1.18.21

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.
Files changed (37) hide show
  1. package/README.md +17 -0
  2. package/dist/agent/action-enforcer.d.ts +29 -0
  3. package/dist/agent/action-enforcer.js +120 -0
  4. package/dist/agent/assistant.d.ts +14 -0
  5. package/dist/agent/assistant.js +190 -35
  6. package/dist/agent/auto-update.js +46 -2
  7. package/dist/agent/local-turn.d.ts +16 -0
  8. package/dist/agent/local-turn.js +54 -1
  9. package/dist/agent/route-classifier.d.ts +1 -0
  10. package/dist/agent/route-classifier.js +30 -3
  11. package/dist/agent/toolsets.d.ts +14 -0
  12. package/dist/agent/toolsets.js +68 -0
  13. package/dist/brain/ingestion-pipeline.d.ts +7 -0
  14. package/dist/brain/ingestion-pipeline.js +107 -21
  15. package/dist/channels/discord.js +38 -7
  16. package/dist/channels/telegram.js +5 -6
  17. package/dist/cli/dashboard.js +112 -6
  18. package/dist/cli/index.js +174 -0
  19. package/dist/cli/ingest.js +8 -2
  20. package/dist/gateway/context-hygiene.d.ts +17 -0
  21. package/dist/gateway/context-hygiene.js +31 -0
  22. package/dist/gateway/heartbeat-scheduler.d.ts +20 -0
  23. package/dist/gateway/heartbeat-scheduler.js +27 -10
  24. package/dist/gateway/router.d.ts +8 -1
  25. package/dist/gateway/router.js +326 -12
  26. package/dist/gateway/turn-ledger.d.ts +32 -0
  27. package/dist/gateway/turn-ledger.js +55 -0
  28. package/dist/memory/embeddings.d.ts +2 -0
  29. package/dist/memory/embeddings.js +8 -1
  30. package/dist/memory/store.d.ts +88 -1
  31. package/dist/memory/store.js +349 -18
  32. package/dist/memory/write-queue.d.ts +16 -0
  33. package/dist/memory/write-queue.js +5 -0
  34. package/dist/tools/shared.d.ts +89 -0
  35. package/dist/types.d.ts +11 -0
  36. package/package.json +1 -1
  37. package/scripts/postinstall.js +56 -6
@@ -105,6 +105,8 @@ export type MemoryStoreType = {
105
105
  accessLogRetentionDays?: number;
106
106
  transcriptRetentionDays?: number;
107
107
  behavioralRetentionDays?: number;
108
+ recallTraceRetentionDays?: number;
109
+ memoryEventRetentionDays?: number;
108
110
  }): {
109
111
  episodicPruned: number;
110
112
  accessLogPruned: number;
@@ -113,6 +115,8 @@ export type MemoryStoreType = {
113
115
  feedbackPruned: number;
114
116
  reflectionsPruned: number;
115
117
  usageLogPruned: number;
118
+ recallTracesPruned: number;
119
+ memoryEventsPruned: number;
116
120
  };
117
121
  checkDuplicate(content: string, sourceFile?: string): {
118
122
  isDuplicate: boolean;
@@ -286,6 +290,8 @@ export type MemoryStoreType = {
286
290
  recordsWritten?: number;
287
291
  recordsSkipped?: number;
288
292
  recordsFailed?: number;
293
+ recordsUnchanged?: number;
294
+ recallCheckStatus?: string | null;
289
295
  overviewNotePath?: string | null;
290
296
  errorsJson?: string | null;
291
297
  status?: 'running' | 'ok' | 'error' | 'partial';
@@ -300,6 +306,8 @@ export type MemoryStoreType = {
300
306
  recordsWritten: number;
301
307
  recordsSkipped: number;
302
308
  recordsFailed: number;
309
+ recordsUnchanged: number;
310
+ recallCheckStatus: string | null;
303
311
  overviewNotePath: string | null;
304
312
  errorsJson: string | null;
305
313
  status: string;
@@ -368,6 +376,23 @@ export type MemoryStoreType = {
368
376
  chunkIds: number[];
369
377
  scores: number[];
370
378
  agentSlug?: string | null;
379
+ matchTypes?: string[];
380
+ backendCounts?: {
381
+ fts: number;
382
+ vector: number;
383
+ graph: number;
384
+ recency: number;
385
+ } | null;
386
+ evidence?: Array<{
387
+ chunkId: number;
388
+ matchType: string;
389
+ score: number;
390
+ sourceFile?: string;
391
+ section?: string;
392
+ }>;
393
+ confidence?: number | null;
394
+ emptyReason?: string | null;
395
+ allowEmpty?: boolean;
371
396
  }): void;
372
397
  getRecentRecallTraces(sessionKey: string, limit?: number): Array<{
373
398
  id: number;
@@ -375,6 +400,21 @@ export type MemoryStoreType = {
375
400
  query: string;
376
401
  chunkIds: number[];
377
402
  scores: number[];
403
+ backendCounts: {
404
+ fts: number;
405
+ vector: number;
406
+ graph: number;
407
+ recency: number;
408
+ } | null;
409
+ evidence: Array<{
410
+ chunkId: number;
411
+ matchType: string;
412
+ score: number;
413
+ sourceFile?: string;
414
+ section?: string;
415
+ }>;
416
+ confidence: number | null;
417
+ emptyReason: string | null;
378
418
  retrievedAt: string;
379
419
  }>;
380
420
  getRecallTrace(traceId: number): {
@@ -383,6 +423,21 @@ export type MemoryStoreType = {
383
423
  messageId: string | null;
384
424
  query: string;
385
425
  retrievedAt: string;
426
+ backendCounts: {
427
+ fts: number;
428
+ vector: number;
429
+ graph: number;
430
+ recency: number;
431
+ } | null;
432
+ evidence: Array<{
433
+ chunkId: number;
434
+ matchType: string;
435
+ score: number;
436
+ sourceFile?: string;
437
+ section?: string;
438
+ }>;
439
+ confidence: number | null;
440
+ emptyReason: string | null;
386
441
  chunks: Array<{
387
442
  id: number;
388
443
  sourceFile: string;
@@ -503,6 +558,19 @@ export type MemoryStoreType = {
503
558
  recallTracesLast30d: number;
504
559
  extractionSkipsLast30d: number;
505
560
  };
561
+ retrievalProof: {
562
+ tracesLast7d: number;
563
+ emptyTracesLast7d: number;
564
+ tracedChunksLast7d: number;
565
+ };
566
+ memoryEvents: {
567
+ total: number;
568
+ indexed: number;
569
+ bySourceType: Array<{
570
+ sourceType: string;
571
+ count: number;
572
+ }>;
573
+ };
506
574
  topCitedLast30d: Array<{
507
575
  chunkId: number;
508
576
  sourceFile: string;
@@ -556,8 +624,29 @@ export type MemoryStoreType = {
556
624
  }>;
557
625
  currentModel: string;
558
626
  ready: boolean;
627
+ cacheDir: string;
628
+ cacheExists: boolean;
629
+ cacheBytes: number;
630
+ cacheSize: string;
631
+ installed: boolean;
559
632
  };
560
633
  };
634
+ recordMemoryEvent?(input: {
635
+ sourceType: string;
636
+ sourceId?: number | null;
637
+ sessionKey?: string | null;
638
+ agentSlug?: string | null;
639
+ content: string;
640
+ indexed?: boolean;
641
+ }): void;
642
+ getMemoryEventStats(): {
643
+ total: number;
644
+ indexed: number;
645
+ bySourceType: Array<{
646
+ sourceType: string;
647
+ count: number;
648
+ }>;
649
+ };
561
650
  logAutonomyEvent(row: {
562
651
  component: string;
563
652
  event: string;
package/dist/types.d.ts CHANGED
@@ -351,6 +351,15 @@ export interface SessionSummary {
351
351
  exchangeCount: number;
352
352
  createdAt: string;
353
353
  }
354
+ export interface SessionLineageEntry {
355
+ sessionKey: string;
356
+ parentSessionId: string | null;
357
+ childSessionId: string | null;
358
+ reason: string;
359
+ summary: string;
360
+ exchangeCount: number;
361
+ createdAt: string;
362
+ }
354
363
  export interface WikilinkConnection {
355
364
  direction: 'incoming' | 'outgoing';
356
365
  file: string;
@@ -887,6 +896,8 @@ export interface IngestionRun {
887
896
  recordsWritten: number;
888
897
  recordsSkipped: number;
889
898
  recordsFailed: number;
899
+ recordsUnchanged?: number;
900
+ recallCheckStatus?: string | null;
890
901
  overviewNotePath?: string | null;
891
902
  errorsJson?: string | null;
892
903
  status: 'running' | 'ok' | 'error' | 'partial';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.19",
3
+ "version": "1.18.21",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -6,14 +6,15 @@
6
6
  * 1. Rebuild native modules (better-sqlite3) for the current Node version
7
7
  * 2. Initialize ~/.clementine/ directory structure if it doesn't exist
8
8
  * 3. Copy default vault templates from package to data home
9
- * 4. Check for `claude` CLI on PATH (needed for OAuth login)
10
- * 5. Print first-run instructions
9
+ * 4. Optionally prefetch the local dense embedding model
10
+ * 5. Check for `claude` CLI on PATH (needed for OAuth login)
11
+ * 6. Print first-run instructions
11
12
  *
12
13
  * Safe to re-run — skips steps already completed.
13
14
  */
14
15
 
15
- import { execSync } from 'node:child_process';
16
- import { cpSync, existsSync, mkdirSync, readdirSync } from 'node:fs';
16
+ import { execFileSync, execSync } from 'node:child_process';
17
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
17
18
  import os from 'node:os';
18
19
  import path from 'node:path';
19
20
  import { fileURLToPath } from 'node:url';
@@ -22,6 +23,30 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
23
  const PKG_DIR = path.resolve(__dirname, '..');
23
24
  const DATA_HOME = process.env.CLEMENTINE_HOME || path.join(os.homedir(), '.clementine');
24
25
 
26
+ function readDataEnv() {
27
+ const envPath = path.join(DATA_HOME, '.env');
28
+ if (!existsSync(envPath)) return {};
29
+ try {
30
+ return Object.fromEntries(
31
+ readFileSync(envPath, 'utf-8')
32
+ .split(/\r?\n/)
33
+ .map((line) => line.trim())
34
+ .filter((line) => line && !line.startsWith('#') && line.includes('='))
35
+ .map((line) => {
36
+ const idx = line.indexOf('=');
37
+ return [line.slice(0, idx).trim(), line.slice(idx + 1).trim().replace(/^["']|["']$/g, '')];
38
+ }),
39
+ );
40
+ } catch {
41
+ return {};
42
+ }
43
+ }
44
+
45
+ function flagEnabled(name, envFile) {
46
+ const raw = process.env[name] ?? envFile[name];
47
+ return /^(1|true|yes|on)$/i.test(String(raw ?? ''));
48
+ }
49
+
25
50
  // ── Step 1: Rebuild better-sqlite3 ─────────────────────────────────
26
51
  try {
27
52
  process.stdout.write('Rebuilding native modules...');
@@ -80,14 +105,39 @@ if (existsSync(srcVault)) {
80
105
  }
81
106
  }
82
107
 
83
- // ── Step 4: Check for claude CLI ────────────────────────────────────
108
+ // ── Step 4: Optional local embedding model prefetch ─────────────────
109
+ // Model weights are intentionally not bundled into the npm tarball. Users
110
+ // who want repo/npm updates to keep the local dense model warm can opt in:
111
+ // CLEMENTINE_INSTALL_EMBEDDINGS=1 npm install -g clementine-agent
112
+ // or put CLEMENTINE_PREFETCH_EMBEDDINGS=1 in ~/.clementine/.env.
113
+ const dataEnv = readDataEnv();
114
+ if (flagEnabled('CLEMENTINE_INSTALL_EMBEDDINGS', dataEnv) || flagEnabled('CLEMENTINE_PREFETCH_EMBEDDINGS', dataEnv)) {
115
+ const cliPath = path.join(PKG_DIR, 'dist', 'cli', 'index.js');
116
+ if (existsSync(cliPath)) {
117
+ try {
118
+ console.log('Prefetching Clementine local embedding model...');
119
+ execFileSync(process.execPath, [cliPath, 'memory', 'model', 'install'], {
120
+ cwd: PKG_DIR,
121
+ stdio: 'inherit',
122
+ env: { ...process.env, CLEMENTINE_HOME: DATA_HOME },
123
+ timeout: 10 * 60_000,
124
+ });
125
+ } catch {
126
+ console.log('Embedding model prefetch skipped/failed. Run `clementine memory model install` later.');
127
+ }
128
+ } else {
129
+ console.log('Embedding model prefetch skipped: built CLI not found yet.');
130
+ }
131
+ }
132
+
133
+ // ── Step 5: Check for claude CLI ────────────────────────────────────
84
134
  let claudeOnPath = false;
85
135
  try {
86
136
  execSync('claude --version', { stdio: 'pipe' });
87
137
  claudeOnPath = true;
88
138
  } catch { /* not on PATH */ }
89
139
 
90
- // ── Step 5: Print instructions ──────────────────────────────────────
140
+ // ── Step 6: Print instructions ──────────────────────────────────────
91
141
  const alreadyConfigured = existsSync(path.join(DATA_HOME, '.env'));
92
142
 
93
143
  if (alreadyConfigured) {