@jhizzard/termdeck 1.6.1 → 1.8.0
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/package.json +1 -1
- package/packages/cli/src/doctor.js +100 -0
- package/packages/cli/src/init-mnestra.js +50 -6
- package/packages/cli/src/init-rumen.js +3 -3
- package/packages/client/public/app.js +341 -30
- package/packages/client/public/index.html +0 -1
- package/packages/client/public/style.css +2 -31
- package/packages/server/src/agent-adapters/agy.js +396 -0
- package/packages/server/src/agent-adapters/gemini.js +309 -42
- package/packages/server/src/agent-adapters/grok-models.js +112 -76
- package/packages/server/src/agent-adapters/index.js +19 -0
- package/packages/server/src/agent-adapters/web-chat-grok.js +259 -0
- package/packages/server/src/index.js +572 -10
- package/packages/server/src/setup/audit-upgrade.js +3 -3
- package/packages/server/src/setup/rumen/functions/graph-inference/index.ts +1 -1
- package/packages/stack-installer/assets/hooks/memory-session-end.js +73 -32
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Sprint 51.5 T1 — schema-introspection audit-upgrade.
|
|
2
2
|
//
|
|
3
|
-
// Brad's 2026-05-02
|
|
3
|
+
// Brad's 2026-05-02 peer install report (INSTALLER-PITFALLS.md ledger #13)
|
|
4
4
|
// surfaced Class A — schema drift. The user upgraded npm packages but the
|
|
5
5
|
// database stayed frozen at first-kickstart: graph-inference Edge Function
|
|
6
6
|
// never deployed, vault key never created, Mnestra migrations 009-015 + TD
|
|
@@ -124,7 +124,7 @@ const PROBES = Object.freeze([
|
|
|
124
124
|
// the rich rag-system column set to memory_sessions; canonical engram
|
|
125
125
|
// mig 001 only ships (id, project, summary, metadata, created_at).
|
|
126
126
|
// Probe for memory_sessions.session_id (the most distinctive of the
|
|
127
|
-
// mig-017 columns) and apply mig 017 if absent. Idempotent on
|
|
127
|
+
// mig-017 columns) and apply mig 017 if absent. Idempotent on the daily-driver project
|
|
128
128
|
// where the columns are already present from hand-applied DDL.
|
|
129
129
|
name: 'memory_sessions.session_id',
|
|
130
130
|
kind: 'mnestra',
|
|
@@ -180,7 +180,7 @@ const PROBES = Object.freeze([
|
|
|
180
180
|
// Sprint 51.6 T3 — Brad's Bug D: function-existence probes (cron schedule
|
|
181
181
|
// checks for jobname presence) are not enough. The deployed Edge Function
|
|
182
182
|
// SOURCE may be stale even when the cron job and function both exist.
|
|
183
|
-
//
|
|
183
|
+
// peer install on 2026-05-03: deployed rumen-tick was missing the
|
|
184
184
|
// SUPABASE_DB_URL fallback that Sprint 51.5 T1 added; cron probe said
|
|
185
185
|
// "present", source was old. The marker check below detects that drift.
|
|
186
186
|
//
|
|
@@ -135,7 +135,7 @@ async function fetchCandidatePairs(
|
|
|
135
135
|
// m2 is the outer m1 (which IS recent). So filtering only m1 by `since`
|
|
136
136
|
// is sufficient and saves ~99% of work in steady state.
|
|
137
137
|
//
|
|
138
|
-
// EXPLAIN ANALYZE on
|
|
138
|
+
// EXPLAIN ANALYZE on the daily-driver project corpus (5,822 active rows, 2026-04-28):
|
|
139
139
|
// 13.5s cold start (since=NULL), HNSW correctly engaged, 718 raw
|
|
140
140
|
// matches → 359 unique pairs at threshold 0.85.
|
|
141
141
|
const result = await sql.unsafe(
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* 9. (Sprint 51.6 T3) POSTs ONE row to Supabase /rest/v1/memory_sessions with
|
|
40
40
|
* Prefer: resolution=merge-duplicates so SessionEnd-fires-twice resolves
|
|
41
41
|
* to a single row. Requires Mnestra migration 017 on canonical installs;
|
|
42
|
-
*
|
|
42
|
+
* the daily-driver project already has the rich schema from rag-system bootstrap.
|
|
43
43
|
* 10. Logs every step to ~/.claude/hooks/memory-hook.log.
|
|
44
44
|
*
|
|
45
45
|
* Version stamp (Sprint 51.6 T3 — hook upgrade gap fix):
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
* to bundled-v2 always passes the `installed >= bundled` short-circuit
|
|
62
62
|
* at init-mnestra.js:550 and reaches the refresh path.
|
|
63
63
|
*
|
|
64
|
-
* @termdeck/stack-installer-hook
|
|
64
|
+
* @termdeck/stack-installer-hook v3
|
|
65
65
|
*
|
|
66
66
|
* Required env vars (validated at entry, after the secrets.env fallback):
|
|
67
67
|
* - SUPABASE_URL e.g. https://<project-ref>.supabase.co
|
|
@@ -305,35 +305,43 @@ function parseCodexJsonl(raw) {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
function parseGeminiJson(raw) {
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
308
|
+
// Sprint 70 T2/T3 cross-lane: handles BOTH transcript shapes —
|
|
309
|
+
// (A) legacy single JSON object {..., messages:[{type,content}]} (.json) +
|
|
310
|
+
// (B) modern JSONL — header line, `{ "$set": ... }` deltas, and message lines
|
|
311
|
+
// {id,timestamp,type:'user'|'gemini'|'info',content} (.jsonl, ships today).
|
|
312
|
+
// Pre-Sprint-70 this did one whole-blob JSON.parse → threw on every modern
|
|
313
|
+
// .jsonl file → returned [] → captured nothing. user content = array of {text};
|
|
314
|
+
// gemini content = string; gemini → assistant.
|
|
315
|
+
// Keep in sync with packages/server/src/agent-adapters/gemini.js::parseTranscript.
|
|
313
316
|
if (typeof raw !== 'string' || raw.length === 0) return [];
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const messages = [];
|
|
318
|
-
for (const msg of obj.messages) {
|
|
319
|
-
if (!msg || typeof msg !== 'object') continue;
|
|
317
|
+
const out = [];
|
|
318
|
+
const pushMsg = (msg) => {
|
|
319
|
+
if (!msg || typeof msg !== 'object') return;
|
|
320
320
|
let role;
|
|
321
321
|
if (msg.type === 'user') role = 'user';
|
|
322
322
|
else if (msg.type === 'gemini' || msg.type === 'assistant') role = 'assistant';
|
|
323
|
-
else
|
|
323
|
+
else return;
|
|
324
324
|
const content = msg.content;
|
|
325
325
|
let text = '';
|
|
326
|
-
if (typeof content === 'string')
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
text = content
|
|
330
|
-
.filter((c) => c && typeof c.text === 'string')
|
|
331
|
-
.map((c) => c.text)
|
|
332
|
-
.join(' ');
|
|
326
|
+
if (typeof content === 'string') text = content;
|
|
327
|
+
else if (Array.isArray(content)) {
|
|
328
|
+
text = content.filter((c) => c && typeof c.text === 'string').map((c) => c.text).join(' ');
|
|
333
329
|
}
|
|
334
|
-
if (text)
|
|
330
|
+
if (text) out.push({ role, content: text.slice(0, 400) });
|
|
331
|
+
};
|
|
332
|
+
const collect = (node) => {
|
|
333
|
+
if (!node || typeof node !== 'object') return;
|
|
334
|
+
if (Array.isArray(node.messages)) node.messages.forEach(pushMsg);
|
|
335
|
+
else pushMsg(node);
|
|
336
|
+
};
|
|
337
|
+
try { collect(JSON.parse(raw)); if (out.length) return out; } catch (_) { /* JSONL */ }
|
|
338
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
339
|
+
const t = line.trim();
|
|
340
|
+
if (!t) continue;
|
|
341
|
+
let node; try { node = JSON.parse(t); } catch (_) { continue; }
|
|
342
|
+
collect(node);
|
|
335
343
|
}
|
|
336
|
-
return
|
|
344
|
+
return out;
|
|
337
345
|
}
|
|
338
346
|
|
|
339
347
|
// Sprint 50 T1 — Grok parser. Mirrors packages/server/src/agent-adapters/grok.js
|
|
@@ -469,7 +477,7 @@ function selectTranscriptParser(sessionType) {
|
|
|
469
477
|
// per-message timestamps. The legacy rag-system writer
|
|
470
478
|
// (~/Documents/Graciella/rag-system/src/scripts/process-session.ts) populated
|
|
471
479
|
// those fields by parsing the transcript JSONL passed to it on stdin, and
|
|
472
|
-
//
|
|
480
|
+
// the daily-driver project's 289 baseline rows carried the rich shape from that writer.
|
|
473
481
|
// v2 closes the gap in pure Node so the bundled hook reaches parity without
|
|
474
482
|
// the rag-system dependency (Class E hidden-dependency rule).
|
|
475
483
|
//
|
|
@@ -637,18 +645,31 @@ async function embedText(text, openaiKey) {
|
|
|
637
645
|
// tag (memory_items.source_agent). Defaults to 'claude' for backwards
|
|
638
646
|
// compat with Claude Code's existing SessionEnd payload, which doesn't
|
|
639
647
|
// supply the field; TermDeck server's per-adapter onPanelClose
|
|
640
|
-
// interceptor (Sprint 50 T1) sets it explicitly to 'codex'/'gemini'/'grok'
|
|
641
|
-
// for non-Claude panels. The set is open-ended on the server
|
|
642
|
-
// constant gates only the spelling-mistake/empty-string case.
|
|
648
|
+
// interceptor (Sprint 50 T1) sets it explicitly to 'codex'/'gemini'/'grok'/
|
|
649
|
+
// 'antigravity' for non-Claude panels. The set is open-ended on the server
|
|
650
|
+
// side; this constant gates only the spelling-mistake/empty-string case.
|
|
651
|
+
//
|
|
652
|
+
// Sprint 70 T3: Antigravity (`agy`) is a first-class source_agent. The CLI
|
|
653
|
+
// binary is `agy` but the canonical provenance tag is `antigravity`, so the
|
|
654
|
+
// alias map below folds `agy` → `antigravity` before the allowlist check —
|
|
655
|
+
// an agy panel's memories must not be mis-tagged `claude`.
|
|
643
656
|
const ALLOWED_SOURCE_AGENTS = new Set([
|
|
644
|
-
'claude', 'codex', 'gemini', 'grok', 'orchestrator',
|
|
657
|
+
'claude', 'codex', 'gemini', 'grok', 'orchestrator', 'antigravity',
|
|
645
658
|
]);
|
|
646
659
|
|
|
660
|
+
// Alias → canonical source_agent. Keeps the binary name (`agy`) and any older
|
|
661
|
+
// callers from being dropped to 'claude' by the allowlist gate. Applied (after
|
|
662
|
+
// lowercasing) before the ALLOWED_SOURCE_AGENTS membership test.
|
|
663
|
+
const SOURCE_AGENT_ALIASES = {
|
|
664
|
+
agy: 'antigravity',
|
|
665
|
+
};
|
|
666
|
+
|
|
647
667
|
function normalizeSourceAgent(raw) {
|
|
648
668
|
if (typeof raw !== 'string') return 'claude';
|
|
649
669
|
const v = raw.trim().toLowerCase();
|
|
650
670
|
if (!v) return 'claude';
|
|
651
|
-
|
|
671
|
+
const canonical = SOURCE_AGENT_ALIASES[v] || v;
|
|
672
|
+
return ALLOWED_SOURCE_AGENTS.has(canonical) ? canonical : 'claude';
|
|
652
673
|
}
|
|
653
674
|
|
|
654
675
|
async function postMemoryItem({ supabaseUrl, supabaseKey, content, embedding, project, sessionId, sourceAgent }) {
|
|
@@ -694,10 +715,10 @@ async function postMemoryItem({ supabaseUrl, supabaseKey, content, embedding, pr
|
|
|
694
715
|
// closes it.
|
|
695
716
|
//
|
|
696
717
|
// Schema target: Mnestra migration 017 brings canonical engram in line with
|
|
697
|
-
//
|
|
718
|
+
// the daily-driver project's rag-system flavor (session_id, summary_embedding, started_at,
|
|
698
719
|
// ended_at, duration_minutes, messages_count, transcript_path, etc). The
|
|
699
720
|
// bundled hook writes the rich shape on every install — fresh-canonical
|
|
700
|
-
// (post-mig-017) and
|
|
721
|
+
// (post-mig-017) and the daily-driver project alike.
|
|
701
722
|
//
|
|
702
723
|
// Idempotency: Prefer: resolution=merge-duplicates relies on the
|
|
703
724
|
// memory_sessions_session_id_key unique constraint. Mig 017 adds it where
|
|
@@ -804,7 +825,27 @@ async function processStdinPayload(input) {
|
|
|
804
825
|
try { stat = statSync(transcriptPath); }
|
|
805
826
|
catch (e) { log(`cannot-stat-transcript: ${transcriptPath} — ${e.message}`); return; }
|
|
806
827
|
|
|
807
|
-
|
|
828
|
+
// Sprint 70 T1 (A1 RED fix — ORCH 2026-06-07 19:21 ET). The raw-byte floor is
|
|
829
|
+
// calibrated for verbose on-disk JSONL (claude/codex/gemini/grok session files
|
|
830
|
+
// run 10s of KB even when short). Antigravity has no on-disk transcript — its
|
|
831
|
+
// capture is a synthesized COMPACT stdout-tee envelope (cleaned, de-chromed
|
|
832
|
+
// content only), so a genuinely-substantive short agy session is legitimately
|
|
833
|
+
// <5KB and the byte floor would wrongly drop it (false zero-row). Exempt
|
|
834
|
+
// sessionType==='antigravity' from the byte floor and gate on parsed CONTENT
|
|
835
|
+
// instead — require >= 1 assistant turn so an empty / no-model-output capture
|
|
836
|
+
// still no-ops. Do NOT lower the global floor; it correctly filters trivial
|
|
837
|
+
// verbose sessions for every other agent.
|
|
838
|
+
if (sessionType === 'antigravity') {
|
|
839
|
+
let agyRaw = '';
|
|
840
|
+
try { agyRaw = readFileSync(transcriptPath, 'utf8'); }
|
|
841
|
+
catch (e) { log(`cannot-read-transcript: ${transcriptPath} — ${e.message}`); return; }
|
|
842
|
+
const agyTurns = selectTranscriptParser(sessionType).parser(agyRaw);
|
|
843
|
+
const assistantTurns = agyTurns.filter((m) => m && m.role === 'assistant').length;
|
|
844
|
+
if (assistantTurns < 1) {
|
|
845
|
+
debug(`antigravity-no-assistant-turn: ${agyTurns.length} parsed, 0 assistant — skipping`);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
} else if (stat.size < MIN_TRANSCRIPT_BYTES) {
|
|
808
849
|
debug(`small-transcript: ${stat.size} bytes < ${MIN_TRANSCRIPT_BYTES}, skipping`);
|
|
809
850
|
return;
|
|
810
851
|
}
|