@jhizzard/termdeck 0.4.2 → 0.4.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.
package/README.md CHANGED
@@ -139,7 +139,7 @@ Restart Claude Code. Six MCP tools appear: `memory_remember`, `memory_recall`, `
139
139
 
140
140
  ### Tier 3 — Add Rumen for async learning
141
141
 
142
- Rumen is a separate npm package — `@jhizzard/rumen@0.4.2` — that ships as a Supabase Edge Function designed to run on a 15-minute `pg_cron` schedule. It's the async reflection layer over Mnestra: it reads recent session memories, cross-references them with your entire historical corpus via hybrid search, synthesizes insights via Claude Haiku, and writes the results back into `rumen_insights` (a new table alongside Mnestra's `memory_items`). TermDeck's Flashback and Claude Code's `memory_recall` both automatically benefit because insights flow back into the same database.
142
+ Rumen is a separate npm package — `@jhizzard/rumen@0.4.3` — that ships as a Supabase Edge Function designed to run on a 15-minute `pg_cron` schedule. It's the async reflection layer over Mnestra: it reads recent session memories, cross-references them with your entire historical corpus via hybrid search, synthesizes insights via Claude Haiku, and writes the results back into `rumen_insights` (a new table alongside Mnestra's `memory_items`). TermDeck's Flashback and Claude Code's `memory_recall` both automatically benefit because insights flow back into the same database.
143
143
 
144
144
  **Rumen is live.** First full-kickstart run against a production Mnestra store on 2026-04-15 19:47 UTC: **111 sessions processed, 111 insights generated** in one pass. Insights surfaced patterns like "the error detection regex in Flashback misses `No such file or directory` — same class of blind spot as X" and "Practice sessions exist as a separate model but frontend components were built and never wired into the schedule view." The cognitive loop is closed.
145
145
 
@@ -163,7 +163,7 @@ Honest limits, stated upfront so the skeptic has nothing to chase:
163
163
  - **Not a replacement for reading docs.** It's the shortest path to a memory you already wrote. If the memory isn't there, the feature does nothing.
164
164
  - **Not fully local by default.** Tier 2+ reaches out to Supabase for storage and OpenAI for embeddings. Tier 1 is fully local. A fully-local Tier 2 (local Postgres + local embeddings) is on the roadmap.
165
165
  - **Not free forever.** Tier 2+ pays OpenAI fractions of a cent per memory for embeddings. Self-hosted embeddings via Ollama are on the roadmap.
166
- - **Not proven at scale.** v0.4.2, validated against 3,527 memories in one developer's production store. First full Rumen kickstart on 2026-04-15 processed 111 sessions into 111 insights in one pass. No multi-user data yet. Bug reports and issues welcome.
166
+ - **Not proven at scale.** v0.4.3, validated against 3,527 memories in one developer's production store. First full Rumen kickstart on 2026-04-15 processed 111 sessions into 111 insights in one pass. No multi-user data yet. Bug reports and issues welcome.
167
167
 
168
168
  ---
169
169
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jhizzard/termdeck",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Browser-based terminal multiplexer with metadata overlays, panel flashback memory recall, and AI-aware session management",
5
5
  "bin": {
6
6
  "termdeck": "./packages/cli/src/index.js"
@@ -375,6 +375,7 @@ function createServer(config) {
375
375
  // configured, regardless of the push-loop flag.
376
376
  session.onErrorDetected = (sess, ctx) => {
377
377
  const question = `${sess.meta.type} error ${ctx.lastCommand || ''} ${ctx.tail || ''}`.trim();
378
+ console.log(`[flashback] error detected in session ${sess.id} (type=${sess.meta.type}, project=${sess.meta.project || 'none'}), querying Mnestra via ${mnestraBridge.mode}…`);
378
379
  mnestraBridge.queryMnestra({
379
380
  question,
380
381
  project: sess.meta.project,
@@ -386,16 +387,26 @@ function createServer(config) {
386
387
  status: 'errored'
387
388
  }
388
389
  }).then((result) => {
390
+ const count = (result.memories || []).length;
391
+ console.log(`[flashback] query returned ${count} matches for session ${sess.id}`);
389
392
  const hit = (result.memories || [])[0];
390
- if (!hit) return;
393
+ if (!hit) {
394
+ console.log(`[flashback] no matches — skipping proactive_memory send for session ${sess.id}`);
395
+ return;
396
+ }
391
397
  if (sess.ws && sess.ws.readyState === 1) {
392
398
  try {
393
399
  sess.ws.send(JSON.stringify({ type: 'proactive_memory', hit }));
400
+ console.log(`[flashback] proactive_memory sent to session ${sess.id} (source_type=${hit.source_type}, project=${hit.project})`);
394
401
  } catch (err) {
402
+ console.error('[flashback] proactive_memory send failed:', err);
395
403
  console.error('[ws] proactive_memory send failed:', err);
396
404
  }
405
+ } else {
406
+ console.log(`[flashback] ws not open for session ${sess.id} (readyState=${sess.ws ? sess.ws.readyState : 'null'}) — dropped hit`);
397
407
  }
398
408
  }).catch((err) => {
409
+ console.error(`[flashback] query failed for session ${sess.id}: ${err.message}`);
399
410
  console.warn('[mnestra-bridge] proactive query failed:', err.message);
400
411
  });
401
412
  };
@@ -47,6 +47,24 @@ function createBridge(config) {
47
47
  const embeddingData = await embeddingRes.json();
48
48
  const embedding = embeddingData.data[0].embedding;
49
49
 
50
+ // NOTE: memory_hybrid_search (migrations/004) accepts exactly 8 named params:
51
+ // query_text, query_embedding, match_count, full_text_weight,
52
+ // semantic_weight, rrf_k, filter_project, filter_source_type.
53
+ // PostgREST matches RPC functions by the set of JSON keys in the body — any
54
+ // extra key (e.g. recency_weight, decay_days) makes it fail to resolve the
55
+ // overload and return 404 "Could not find the function". That was silently
56
+ // killing every Flashback query for 15 sprints.
57
+ const rpcBody = {
58
+ query_text: question,
59
+ query_embedding: `[${embedding.join(',')}]`,
60
+ match_count: 10,
61
+ full_text_weight: 1.0,
62
+ semantic_weight: 1.0,
63
+ rrf_k: 60,
64
+ filter_project: searchAll ? null : (project || null),
65
+ filter_source_type: null
66
+ };
67
+ console.log(`[flashback] direct RPC → memory_hybrid_search project=${rpcBody.filter_project ?? 'ALL'} q="${question.slice(0, 60)}"`);
50
68
  const searchRes = await fetch(`${supabaseUrl}/rest/v1/rpc/memory_hybrid_search`, {
51
69
  method: 'POST',
52
70
  headers: {
@@ -54,31 +72,23 @@ function createBridge(config) {
54
72
  'apikey': supabaseKey,
55
73
  'Authorization': `Bearer ${supabaseKey}`
56
74
  },
57
- body: JSON.stringify({
58
- query_text: question,
59
- query_embedding: `[${embedding.join(',')}]`,
60
- match_count: 10,
61
- full_text_weight: 1.0,
62
- semantic_weight: 1.0,
63
- rrf_k: 60,
64
- filter_project: searchAll ? null : (project || null),
65
- filter_source_type: null,
66
- recency_weight: 0.15,
67
- decay_days: 30.0
68
- })
75
+ body: JSON.stringify(rpcBody)
69
76
  });
70
77
  if (!searchRes.ok) {
71
78
  const err = await searchRes.text();
79
+ console.error(`[flashback] direct RPC failed ${searchRes.status}:`, err);
72
80
  console.error('[mnestra-bridge:direct] supabase search failed:', err);
73
- throw new Error('Memory search failed');
81
+ throw new Error(`Memory search failed (${searchRes.status})`);
74
82
  }
75
83
  const rows = await searchRes.json();
84
+ console.log(`[flashback] direct RPC returned ${rows.length} rows`);
76
85
  return {
77
86
  memories: rows.map((m) => ({
78
87
  content: m.content,
79
88
  source_type: m.source_type,
80
89
  project: m.project,
81
- similarity: m.similarity,
90
+ // memory_hybrid_search returns `score`, not `similarity`.
91
+ similarity: m.similarity ?? m.score ?? null,
82
92
  created_at: m.created_at
83
93
  })),
84
94
  total: rows.length
@@ -322,7 +322,10 @@ class Session {
322
322
 
323
323
  // Server-side rate limit: at most one error_detected event every 30s per session
324
324
  const now = Date.now();
325
- if (now - this._lastErrorFireAt < 30000) return;
325
+ if (now - this._lastErrorFireAt < 30000) {
326
+ console.log(`[flashback] error detected in session ${this.id} but rate-limited (${Math.round((30000 - (now - this._lastErrorFireAt)) / 1000)}s left)`);
327
+ return;
328
+ }
326
329
  this._lastErrorFireAt = now;
327
330
 
328
331
  if (this.onErrorDetected) {
@@ -333,8 +336,11 @@ class Session {
333
336
  try {
334
337
  this.onErrorDetected(this, { lastCommand, tail });
335
338
  } catch (err) {
339
+ console.error('[flashback] onErrorDetected handler threw:', err);
336
340
  console.error('[session] onErrorDetected handler error:', err);
337
341
  }
342
+ } else {
343
+ console.log(`[flashback] error detected in session ${this.id} but no onErrorDetected handler wired`);
338
344
  }
339
345
  }
340
346