mnueron 0.6.1 → 0.6.3

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.
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Provider-agnostic recall logger for the MCP server.
3
+ *
4
+ * Why this exists: in hosted mode (MNUERON_API_URL + MNUERON_API_TOKEN set)
5
+ * the MCP server's `provider` is RemoteProvider — every memory_recall call
6
+ * goes over HTTP to a hosted backend. That hosted backend may or may not
7
+ * capture recall_events, and even if it does, the local user has no way to
8
+ * see "how many times my Claude Desktop recalled from mnueron today"
9
+ * unless we ALSO log it locally.
10
+ *
11
+ * In local mode, LocalProvider used to capture inside .search(). That's now
12
+ * removed in favor of this logger so we have a single capture point for
13
+ * BOTH modes. No double-counting, no provider-dependent behavior.
14
+ *
15
+ * The logger opens its own better-sqlite3 connection to cfg.dbPath (which
16
+ * is defined regardless of mode — defaults to ~/.mnueron/memories.db). Runs
17
+ * RECALL_EVENTS_DDL on construction so the table exists even on a fresh
18
+ * install. Fail-open on every insert: a bad log row never breaks recall.
19
+ */
20
+ import Database from 'better-sqlite3';
21
+ import { RECALL_EVENTS_DDL, buildRecallEvent, approximateTokens, } from './recall-event.js';
22
+ export class RecallLogger {
23
+ db;
24
+ client;
25
+ defaultModelId;
26
+ insertStmt = null;
27
+ nsBaselineStmt = null;
28
+ globalBaselineStmt = null;
29
+ ready = false;
30
+ warned = false;
31
+ constructor(opts) {
32
+ this.client = opts.client ?? null;
33
+ this.defaultModelId = opts.defaultModelId ?? null;
34
+ try {
35
+ this.db = new Database(opts.dbPath);
36
+ this.db.exec(RECALL_EVENTS_DDL);
37
+ this.insertStmt = this.db.prepare(`INSERT INTO recall_events
38
+ (id, created_at, namespace, query_hash, tokens_returned,
39
+ tokens_baseline_namespace, tokens_baseline_capped, model_id,
40
+ context_limit, client)
41
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
42
+ this.nsBaselineStmt = this.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) AS chars
43
+ FROM memories
44
+ WHERE namespace = ?`);
45
+ this.globalBaselineStmt = this.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) AS chars FROM memories`);
46
+ this.ready = true;
47
+ process.stderr.write(`[mnueron/recall-logger] ready dbPath=${opts.dbPath} client=${this.client}\n`);
48
+ }
49
+ catch (e) {
50
+ // Don't crash the MCP server if SQLite is unavailable for whatever
51
+ // reason (file locked, disk full, etc.). The MCP service stays up;
52
+ // dashboard just won't show new events until the underlying issue
53
+ // resolves.
54
+ this.db = undefined;
55
+ this.warnOnce(`init failed: ${e instanceof Error ? e.message : e}`);
56
+ }
57
+ }
58
+ /**
59
+ * Write one row for a completed memory_recall call. Fail-open. Called
60
+ * AFTER the underlying provider.search() succeeds so we never log a row
61
+ * for a failed recall (cleaner aggregates).
62
+ */
63
+ logRecall(input, returned) {
64
+ process.stderr.write(`[mnueron/recall-logger] logRecall called: ns=${input.namespace ?? '-'} returned=${returned.length} ready=${this.ready}\n`);
65
+ if (!this.ready || !this.insertStmt) {
66
+ process.stderr.write(`[mnueron/recall-logger] SKIPPED (not ready)\n`);
67
+ return;
68
+ }
69
+ try {
70
+ const tokens_returned = returned.reduce((sum, m) => sum + approximateTokens(m.content ?? ''), 0);
71
+ // Baseline = sum of all content tokens in the namespace (or global if
72
+ // no namespace). LENGTH() over text is cheap; sub-millisecond even on
73
+ // tens of thousands of rows.
74
+ const ns = input.namespace ?? null;
75
+ let baseline_chars = 0;
76
+ if (ns && this.nsBaselineStmt) {
77
+ const row = this.nsBaselineStmt.get(ns);
78
+ baseline_chars = row?.chars ?? 0;
79
+ }
80
+ else if (this.globalBaselineStmt) {
81
+ const row = this.globalBaselineStmt.get();
82
+ baseline_chars = row?.chars ?? 0;
83
+ }
84
+ const tokens_baseline_namespace = Math.ceil(baseline_chars / 4);
85
+ const event = {
86
+ namespace: ns,
87
+ query: input.query,
88
+ tokens_returned,
89
+ tokens_baseline_namespace,
90
+ model_id: input.model_id ?? this.defaultModelId ?? null,
91
+ client: this.client,
92
+ };
93
+ const ev = buildRecallEvent(event);
94
+ this.insertStmt.run(ev.id, ev.created_at, ev.namespace, ev.query_hash, ev.tokens_returned, ev.tokens_baseline_namespace, ev.tokens_baseline_capped, ev.model_id, ev.context_limit, ev.client);
95
+ process.stderr.write(`[mnueron/recall-logger] INSERTED id=${ev.id.slice(0, 8)} tokens_returned=${ev.tokens_returned} tokens_saved=${ev.tokens_baseline_capped - ev.tokens_returned}\n`);
96
+ }
97
+ catch (e) {
98
+ this.warnOnce(`insert failed: ${e instanceof Error ? e.message : e}`);
99
+ }
100
+ }
101
+ close() {
102
+ if (this.ready && this.db) {
103
+ try {
104
+ this.db.close();
105
+ }
106
+ catch { /* ignore */ }
107
+ this.ready = false;
108
+ }
109
+ }
110
+ warnOnce(msg) {
111
+ if (this.warned)
112
+ return;
113
+ this.warned = true;
114
+ // stderr only — stdout is the JSON-RPC channel.
115
+ process.stderr.write(`[mnueron/recall-logger] ${msg}\n`);
116
+ }
117
+ }
118
+ /**
119
+ * Detect the calling client. MCP doesn't pass a User-Agent equivalent on
120
+ * every request, so we use this precedence:
121
+ *
122
+ * 1. MNUERON_CLIENT env var (explicit override — users can set this in
123
+ * claude_desktop_config.json's env section)
124
+ * 2. Best-guess from launch context (CODEX_ env, CURSOR_TRACE_ID, etc.)
125
+ * 3. "mnueron-mcp" as a safe generic default
126
+ *
127
+ * Returns a lowercase slug-shaped string the dashboard can group on.
128
+ */
129
+ export function detectMcpClient() {
130
+ const explicit = process.env.MNUERON_CLIENT;
131
+ if (explicit && explicit.trim().length > 0)
132
+ return explicit.trim().toLowerCase();
133
+ // Cursor sets CURSOR_TRACE_ID on its MCP child processes.
134
+ if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_USER)
135
+ return 'cursor';
136
+ // Claude Code (CLI) sets CLAUDE_CODE_SSE_PORT or CODEX_HOME.
137
+ if (process.env.CLAUDE_CODE_SSE_PORT || process.env.CODEX_HOME)
138
+ return 'claude-code';
139
+ // Cline / VS Code extensions usually inherit VSCODE_PID.
140
+ if (process.env.VSCODE_PID || process.env.VSCODE_INJECTION) {
141
+ return process.env.CLINE_INSTALLED ? 'cline' : 'vscode';
142
+ }
143
+ // Windsurf identifies via CODEIUM_API_URL or similar.
144
+ if (process.env.CODEIUM_API_URL)
145
+ return 'windsurf';
146
+ return 'mnueron-mcp';
147
+ }
148
+ //# sourceMappingURL=recall-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recall-logger.js","sourceRoot":"","sources":["../../src/savings/recall-logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Aggregation queries for the recall savings dashboard.
3
+ *
4
+ * Reads recall_events (capture lives in recall-event.ts) and produces
5
+ * the shape the dashboard widget consumes. Time-windowed counts +
6
+ * top-N savings + a daily sparkline series.
7
+ *
8
+ * Pure-SQLite — no LLM calls, fast enough to call on every dashboard
9
+ * page load. If the recall_events table doesn't exist yet (very fresh
10
+ * install), every query returns zeros so the UI renders cleanly.
11
+ */
12
+ import { tokensToDollars, DEFAULT_MODEL_ID } from './pricing.js';
13
+ const WINDOW_MS = {
14
+ day: 24 * 60 * 60 * 1000,
15
+ week: 7 * 24 * 60 * 60 * 1000,
16
+ month: 30 * 24 * 60 * 60 * 1000,
17
+ };
18
+ function windowSinceMs(window) {
19
+ if (window === 'all')
20
+ return 0;
21
+ return Date.now() - WINDOW_MS[window];
22
+ }
23
+ function tableExists(db, name) {
24
+ const row = db
25
+ .prepare(`SELECT 1 AS ok FROM sqlite_master WHERE type='table' AND name = ?`)
26
+ .get(name);
27
+ return Boolean(row?.ok);
28
+ }
29
+ function emptySummary(window, modelId) {
30
+ return {
31
+ window,
32
+ recalls_count: 0,
33
+ tokens_returned: 0,
34
+ tokens_baseline_capped: 0,
35
+ tokens_saved: 0,
36
+ dollars_saved: 0,
37
+ ide_crashes_avoided: 0,
38
+ default_model_id: modelId,
39
+ trend: [],
40
+ top_saves: [],
41
+ };
42
+ }
43
+ /**
44
+ * Aggregate savings for `window`. `defaultModelId` is what cost-per-token
45
+ * to apply to recalls where the captured row didn't know the caller's
46
+ * model (the user picks this in their dashboard settings — we just take
47
+ * it as input here).
48
+ */
49
+ export function getSavingsSummary(db, window = 'month', defaultModelId = DEFAULT_MODEL_ID) {
50
+ if (!tableExists(db, 'recall_events'))
51
+ return emptySummary(window, defaultModelId);
52
+ const since = windowSinceMs(window);
53
+ // Aggregate over the window. We compute dollars saved in JS rather than
54
+ // SQL because the model_id varies per row and we want per-row pricing.
55
+ const rows = db
56
+ .prepare(`SELECT namespace, client, tokens_returned, tokens_baseline_capped,
57
+ model_id, context_limit, tokens_baseline_namespace, created_at
58
+ FROM recall_events
59
+ WHERE created_at >= ?`)
60
+ .all(since);
61
+ let tokens_returned = 0;
62
+ let tokens_baseline_capped = 0;
63
+ let dollars_saved = 0;
64
+ let ide_crashes_avoided = 0;
65
+ const perDay = new Map();
66
+ const topSaves = [];
67
+ for (const r of rows) {
68
+ tokens_returned += r.tokens_returned;
69
+ tokens_baseline_capped += r.tokens_baseline_capped;
70
+ const saved_tokens = Math.max(0, r.tokens_baseline_capped - r.tokens_returned);
71
+ const model = r.model_id ?? defaultModelId;
72
+ const saved_usd = tokensToDollars(saved_tokens, model);
73
+ dollars_saved += saved_usd;
74
+ if (r.tokens_baseline_namespace > r.context_limit)
75
+ ide_crashes_avoided++;
76
+ const day = new Date(r.created_at).toISOString().slice(0, 10);
77
+ const bucket = perDay.get(day) ?? { usd: 0, recalls: 0 };
78
+ bucket.usd += saved_usd;
79
+ bucket.recalls += 1;
80
+ perDay.set(day, bucket);
81
+ topSaves.push({
82
+ namespace: r.namespace,
83
+ client: r.client,
84
+ tokens_returned: r.tokens_returned,
85
+ tokens_baseline_capped: r.tokens_baseline_capped,
86
+ dollars_saved: saved_usd,
87
+ created_at: r.created_at,
88
+ });
89
+ }
90
+ topSaves.sort((a, b) => b.dollars_saved - a.dollars_saved);
91
+ // Build a 14-day trend (most recent last). Fill missing days with zeros
92
+ // so the sparkline doesn't have gaps.
93
+ const trend = [];
94
+ const today = new Date();
95
+ for (let i = 13; i >= 0; i--) {
96
+ const d = new Date(today.getTime() - i * 24 * 60 * 60 * 1000);
97
+ const key = d.toISOString().slice(0, 10);
98
+ const bucket = perDay.get(key);
99
+ trend.push({
100
+ day: key,
101
+ savings_usd: Math.round((bucket?.usd ?? 0) * 100) / 100,
102
+ recalls: bucket?.recalls ?? 0,
103
+ });
104
+ }
105
+ return {
106
+ window,
107
+ recalls_count: rows.length,
108
+ tokens_returned,
109
+ tokens_baseline_capped,
110
+ tokens_saved: Math.max(0, tokens_baseline_capped - tokens_returned),
111
+ dollars_saved: Math.round(dollars_saved * 100) / 100,
112
+ ide_crashes_avoided,
113
+ default_model_id: defaultModelId,
114
+ trend,
115
+ top_saves: topSaves.slice(0, 5),
116
+ };
117
+ }
118
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/savings/summary.ts"],"names":[],"mappings":"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"}