@jhizzard/termdeck-stack 0.5.1 → 0.6.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.
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
* without needing them in the parent shell.
|
|
16
16
|
*
|
|
17
17
|
* Behavior:
|
|
18
|
-
* 1. Reads {transcript_path, cwd, session_id, sessionType?}
|
|
19
|
-
* Code SessionEnd payload, or
|
|
20
|
-
* non-Claude agents).
|
|
18
|
+
* 1. Reads {transcript_path, cwd, session_id, sessionType?, source_agent?}
|
|
19
|
+
* from stdin (Claude Code SessionEnd payload, or — Sprint 50 T1 — a
|
|
20
|
+
* server-driven invocation for non-Claude agents). source_agent
|
|
21
|
+
* defaults to 'claude' when absent (Claude Code's existing hook
|
|
22
|
+
* payload doesn't carry it; the TermDeck server's per-adapter
|
|
23
|
+
* onPanelClose interceptor sets it explicitly for codex/gemini/grok).
|
|
21
24
|
* 2. Loads ~/.termdeck/secrets.env into process.env if any required key is
|
|
22
25
|
* absent OR is a literal `${VAR}` placeholder (Sprint 47.5 hotfix
|
|
23
26
|
* discipline — Claude Code does not expand `${VAR}` in MCP env, and we
|
|
@@ -268,6 +271,57 @@ function parseGeminiJson(raw) {
|
|
|
268
271
|
return messages;
|
|
269
272
|
}
|
|
270
273
|
|
|
274
|
+
// Sprint 50 T1 — Grok parser. Mirrors packages/server/src/agent-adapters/grok.js
|
|
275
|
+
// parseTranscript: accepts either a JSON array or JSONL of `{role, content}`
|
|
276
|
+
// objects, where content is a string OR an array of `{type, text, ...}` parts
|
|
277
|
+
// (AI SDK provider shape). Tool-call / tool-result / reasoning parts are
|
|
278
|
+
// skipped — only the `type:'text'` parts contribute to the summary.
|
|
279
|
+
//
|
|
280
|
+
// The JSON envelope is produced server-side by the Grok adapter's
|
|
281
|
+
// `resolveTranscriptPath` (which extracts from ~/.grok/grok.db SQLite via
|
|
282
|
+
// better-sqlite3 and writes a tempfile). The hook itself never opens grok.db
|
|
283
|
+
// — that would require better-sqlite3 to be reachable from ~/.claude/hooks/,
|
|
284
|
+
// which isn't part of the install contract. The transcript_path the server
|
|
285
|
+
// hands the hook is the tempfile, and the sessionType in the payload is
|
|
286
|
+
// 'grok' so this parser is the one selected.
|
|
287
|
+
function parseGrokJson(raw) {
|
|
288
|
+
if (typeof raw !== 'string' || raw.length === 0) return [];
|
|
289
|
+
let messages = null;
|
|
290
|
+
try {
|
|
291
|
+
const parsed = JSON.parse(raw);
|
|
292
|
+
if (Array.isArray(parsed)) messages = parsed;
|
|
293
|
+
} catch (_) { /* fall through to JSONL */ }
|
|
294
|
+
if (!messages) {
|
|
295
|
+
messages = [];
|
|
296
|
+
for (const line of raw.split('\n')) {
|
|
297
|
+
const trimmed = line.trim();
|
|
298
|
+
if (!trimmed) continue;
|
|
299
|
+
try {
|
|
300
|
+
const obj = JSON.parse(trimmed);
|
|
301
|
+
if (obj && typeof obj === 'object') messages.push(obj);
|
|
302
|
+
} catch (_) { continue; }
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const out = [];
|
|
306
|
+
for (const msg of messages) {
|
|
307
|
+
if (!msg || typeof msg !== 'object') continue;
|
|
308
|
+
const role = msg.role;
|
|
309
|
+
if (role !== 'user' && role !== 'assistant') continue;
|
|
310
|
+
const content = msg.content;
|
|
311
|
+
let text = '';
|
|
312
|
+
if (typeof content === 'string') {
|
|
313
|
+
text = content;
|
|
314
|
+
} else if (Array.isArray(content)) {
|
|
315
|
+
text = content
|
|
316
|
+
.filter((c) => c && c.type === 'text' && typeof c.text === 'string')
|
|
317
|
+
.map((c) => c.text)
|
|
318
|
+
.join(' ');
|
|
319
|
+
}
|
|
320
|
+
if (text) out.push({ role, content: text.slice(0, 400) });
|
|
321
|
+
}
|
|
322
|
+
return out;
|
|
323
|
+
}
|
|
324
|
+
|
|
271
325
|
function parseAutoDetect(raw) {
|
|
272
326
|
// Fallback when sessionType is absent. Tries Gemini's single-JSON shape
|
|
273
327
|
// first (cheap to detect — starts with `{` and has a top-level `messages`
|
|
@@ -327,10 +381,10 @@ const TRANSCRIPT_PARSERS = {
|
|
|
327
381
|
'claude-code': parseClaudeJsonl,
|
|
328
382
|
'codex': parseCodexJsonl,
|
|
329
383
|
'gemini': parseGeminiJson,
|
|
330
|
-
// Sprint
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
|
|
384
|
+
// Sprint 50 T1 — grok parser. Server-side `resolveTranscriptPath` extracts
|
|
385
|
+
// ~/.grok/grok.db rows via better-sqlite3 and writes a JSON envelope to a
|
|
386
|
+
// tempfile; the hook reads that tempfile with parseGrokJson here.
|
|
387
|
+
'grok': parseGrokJson,
|
|
334
388
|
};
|
|
335
389
|
const DEFAULT_SESSION_TYPE = 'auto';
|
|
336
390
|
|
|
@@ -390,7 +444,25 @@ async function embedText(text, openaiKey) {
|
|
|
390
444
|
}
|
|
391
445
|
}
|
|
392
446
|
|
|
393
|
-
|
|
447
|
+
// Sprint 50 T2: every row written by this hook carries an LLM-provenance
|
|
448
|
+
// tag (memory_items.source_agent). Defaults to 'claude' for backwards
|
|
449
|
+
// compat with Claude Code's existing SessionEnd payload, which doesn't
|
|
450
|
+
// supply the field; TermDeck server's per-adapter onPanelClose
|
|
451
|
+
// interceptor (Sprint 50 T1) sets it explicitly to 'codex'/'gemini'/'grok'
|
|
452
|
+
// for non-Claude panels. The set is open-ended on the server side; this
|
|
453
|
+
// constant gates only the spelling-mistake/empty-string case.
|
|
454
|
+
const ALLOWED_SOURCE_AGENTS = new Set([
|
|
455
|
+
'claude', 'codex', 'gemini', 'grok', 'orchestrator',
|
|
456
|
+
]);
|
|
457
|
+
|
|
458
|
+
function normalizeSourceAgent(raw) {
|
|
459
|
+
if (typeof raw !== 'string') return 'claude';
|
|
460
|
+
const v = raw.trim().toLowerCase();
|
|
461
|
+
if (!v) return 'claude';
|
|
462
|
+
return ALLOWED_SOURCE_AGENTS.has(v) ? v : 'claude';
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async function postMemoryItem({ supabaseUrl, supabaseKey, content, embedding, project, sessionId, sourceAgent }) {
|
|
394
466
|
try {
|
|
395
467
|
const res = await fetch(`${supabaseUrl}/rest/v1/memory_items`, {
|
|
396
468
|
method: 'POST',
|
|
@@ -407,6 +479,7 @@ async function postMemoryItem({ supabaseUrl, supabaseKey, content, embedding, pr
|
|
|
407
479
|
category: 'workflow',
|
|
408
480
|
project,
|
|
409
481
|
source_session_id: sessionId || null,
|
|
482
|
+
source_agent: normalizeSourceAgent(sourceAgent),
|
|
410
483
|
}),
|
|
411
484
|
});
|
|
412
485
|
if (!res.ok) {
|
|
@@ -442,6 +515,17 @@ async function processStdinPayload(input) {
|
|
|
442
515
|
process.env.TERMDECK_SESSION_TYPE ||
|
|
443
516
|
DEFAULT_SESSION_TYPE;
|
|
444
517
|
|
|
518
|
+
// Sprint 50 T2: provenance tag the row with the LLM that produced it.
|
|
519
|
+
// Default 'claude' — Claude Code's native SessionEnd payload doesn't
|
|
520
|
+
// carry source_agent, so any unset path is implicitly Claude. The
|
|
521
|
+
// TermDeck server's per-adapter onPanelClose interceptor (Sprint 50 T1)
|
|
522
|
+
// sets it explicitly for non-Claude panels.
|
|
523
|
+
const sourceAgent =
|
|
524
|
+
data.source_agent ||
|
|
525
|
+
data.sourceAgent ||
|
|
526
|
+
process.env.TERMDECK_SOURCE_AGENT ||
|
|
527
|
+
'claude';
|
|
528
|
+
|
|
445
529
|
if (!transcriptPath) { log('no-transcript-path: skipping'); return; }
|
|
446
530
|
|
|
447
531
|
let stat;
|
|
@@ -472,9 +556,10 @@ async function processStdinPayload(input) {
|
|
|
472
556
|
embedding,
|
|
473
557
|
project,
|
|
474
558
|
sessionId,
|
|
559
|
+
sourceAgent,
|
|
475
560
|
});
|
|
476
561
|
|
|
477
|
-
if (ok) log(`ingested: project="${project}" session=${sessionId} bytes=${summary.length} sessionType=${sessionType}`);
|
|
562
|
+
if (ok) log(`ingested: project="${project}" session=${sessionId} bytes=${summary.length} sessionType=${sessionType} sourceAgent=${normalizeSourceAgent(sourceAgent)}`);
|
|
478
563
|
}
|
|
479
564
|
|
|
480
565
|
// Module-export contract for testability. When run as a script (require.main === module),
|
|
@@ -502,7 +587,11 @@ if (require.main === module) {
|
|
|
502
587
|
parseClaudeJsonl,
|
|
503
588
|
parseCodexJsonl,
|
|
504
589
|
parseGeminiJson,
|
|
590
|
+
parseGrokJson,
|
|
505
591
|
parseAutoDetect,
|
|
506
592
|
selectTranscriptParser,
|
|
593
|
+
// Sprint 50 T2 — source_agent provenance plumbing.
|
|
594
|
+
normalizeSourceAgent,
|
|
595
|
+
ALLOWED_SOURCE_AGENTS,
|
|
507
596
|
};
|
|
508
597
|
}
|
package/package.json
CHANGED