sidekick-shared 0.18.4 → 0.19.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/README.md +23 -2
- package/dist/accounts.d.ts +1 -0
- package/dist/aggregation/EventAggregator.js +26 -11
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +5 -1
- package/dist/codexProfiles.d.ts +3 -2
- package/dist/codexProfiles.js +382 -52
- package/dist/context/sessionContext.d.ts +99 -0
- package/dist/context/sessionContext.js +523 -0
- package/dist/ensureDefaultAccounts.js +6 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +9 -3
- package/dist/modelContext.js +3 -1
- package/dist/modelInfo.js +69 -9
- package/dist/parsers/codexParser.d.ts +2 -0
- package/dist/parsers/codexParser.js +129 -63
- package/dist/providers/claudeCode.d.ts +2 -0
- package/dist/providers/claudeCode.js +4 -0
- package/dist/providers/codex.d.ts +2 -0
- package/dist/providers/codex.js +125 -91
- package/dist/providers/openCode.d.ts +2 -0
- package/dist/providers/openCode.js +4 -0
- package/dist/providers/types.d.ts +4 -0
- package/dist/report/htmlReportGenerator.js +3 -0
- package/dist/report/index.d.ts +1 -1
- package/dist/report/index.js +2 -1
- package/dist/report/transcriptParser.d.ts +3 -0
- package/dist/report/transcriptParser.js +25 -3
- package/dist/report/types.d.ts +2 -0
- package/dist/schemas/sessionEvent.d.ts +15 -0
- package/dist/schemas/sessionEvent.js +14 -1
- package/dist/types/sessionEvent.d.ts +16 -1
- package/dist/watchers/eventBridge.js +24 -0
- package/dist/watchers/factory.d.ts +2 -2
- package/dist/watchers/factory.js +7 -5
- package/dist/watchers/providerReaderWatcher.d.ts +27 -0
- package/dist/watchers/providerReaderWatcher.js +148 -0
- package/package.json +1 -1
package/dist/providers/codex.js
CHANGED
|
@@ -46,6 +46,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
46
46
|
exports.CodexProvider = void 0;
|
|
47
47
|
const fs = __importStar(require("fs"));
|
|
48
48
|
const path = __importStar(require("path"));
|
|
49
|
+
const EventAggregator_1 = require("../aggregation/EventAggregator");
|
|
50
|
+
const sessionContext_1 = require("../context/sessionContext");
|
|
49
51
|
const codexParser_1 = require("../parsers/codexParser");
|
|
50
52
|
const codexDatabase_1 = require("./codexDatabase");
|
|
51
53
|
const codexProfiles_1 = require("../codexProfiles");
|
|
@@ -284,8 +286,8 @@ function extractFirstUserMessage(rolloutPath) {
|
|
|
284
286
|
// Check event_msg with user_message
|
|
285
287
|
if (parsed.type === 'event_msg') {
|
|
286
288
|
const payload = parsed.payload;
|
|
287
|
-
if (payload.
|
|
288
|
-
return truncate(payload.
|
|
289
|
+
if (payload.type === 'user_message' && payload.message?.trim()) {
|
|
290
|
+
return truncate(payload.message, 60);
|
|
289
291
|
}
|
|
290
292
|
}
|
|
291
293
|
}
|
|
@@ -301,47 +303,106 @@ function extractFirstUserMessage(rolloutPath) {
|
|
|
301
303
|
}
|
|
302
304
|
/** Extract searchable text from a rollout line. */
|
|
303
305
|
function extractSearchableText(line) {
|
|
306
|
+
const parts = [];
|
|
304
307
|
switch (line.type) {
|
|
305
|
-
case '
|
|
308
|
+
case 'session_meta': {
|
|
306
309
|
const payload = line.payload;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
+
if (payload.cwd)
|
|
311
|
+
parts.push(payload.cwd);
|
|
312
|
+
if (payload.source)
|
|
313
|
+
parts.push(payload.source);
|
|
314
|
+
if (payload.base_instructions?.text)
|
|
315
|
+
parts.push(payload.base_instructions.text);
|
|
316
|
+
if (payload.git?.branch)
|
|
317
|
+
parts.push(payload.git.branch);
|
|
318
|
+
if (payload.git?.commit)
|
|
319
|
+
parts.push(payload.git.commit);
|
|
320
|
+
return parts.join(' ');
|
|
321
|
+
}
|
|
322
|
+
case 'response_item': {
|
|
323
|
+
const item = line.payload;
|
|
310
324
|
if (item.type === 'message') {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
325
|
+
parts.push(extractTextFromUnknownContent(item.content));
|
|
326
|
+
}
|
|
327
|
+
if (item.type === 'reasoning') {
|
|
328
|
+
const summary = item.summary;
|
|
329
|
+
if (Array.isArray(summary)) {
|
|
330
|
+
for (const entry of summary) {
|
|
331
|
+
if (typeof entry.text === 'string')
|
|
332
|
+
parts.push(entry.text);
|
|
333
|
+
}
|
|
318
334
|
}
|
|
319
335
|
}
|
|
320
|
-
if (item.type === 'function_call'
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
336
|
+
if (item.type === 'function_call') {
|
|
337
|
+
if (typeof item.name === 'string')
|
|
338
|
+
parts.push(item.name);
|
|
339
|
+
if (typeof item.arguments === 'string')
|
|
340
|
+
parts.push(item.arguments);
|
|
341
|
+
}
|
|
342
|
+
if (item.type === 'function_call_output' && typeof item.output === 'string') {
|
|
343
|
+
parts.push(item.output);
|
|
344
|
+
}
|
|
345
|
+
if (item.type === 'local_shell_call') {
|
|
346
|
+
const action = item.action;
|
|
347
|
+
const command = action?.command;
|
|
348
|
+
if (Array.isArray(command))
|
|
349
|
+
parts.push(command.map(String).join(' '));
|
|
350
|
+
if (typeof action?.workdir === 'string')
|
|
351
|
+
parts.push(action.workdir);
|
|
352
|
+
}
|
|
353
|
+
if (item.type === 'custom_tool_call') {
|
|
354
|
+
if (typeof item.name === 'string')
|
|
355
|
+
parts.push(item.name);
|
|
356
|
+
if (typeof item.input === 'string')
|
|
357
|
+
parts.push(item.input);
|
|
358
|
+
}
|
|
359
|
+
if (item.type === 'custom_tool_call_output' && typeof item.output === 'string') {
|
|
360
|
+
parts.push(item.output);
|
|
361
|
+
}
|
|
362
|
+
return parts.filter(Boolean).join(' ');
|
|
325
363
|
}
|
|
326
364
|
case 'event_msg': {
|
|
327
|
-
const
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
|
|
365
|
+
const event = line.payload;
|
|
366
|
+
for (const key of ['type', 'message', 'result', 'stdout', 'stderr', 'summary', 'error', 'code', 'tool_name', 'server_name']) {
|
|
367
|
+
if (typeof event[key] === 'string')
|
|
368
|
+
parts.push(event[key]);
|
|
369
|
+
}
|
|
370
|
+
const command = event.command;
|
|
371
|
+
if (Array.isArray(command))
|
|
372
|
+
parts.push(command.map(String).join(' '));
|
|
373
|
+
if (event.arguments && typeof event.arguments === 'object')
|
|
374
|
+
parts.push(JSON.stringify(event.arguments));
|
|
375
|
+
return parts.filter(Boolean).join(' ');
|
|
336
376
|
}
|
|
337
377
|
case 'compacted': {
|
|
338
378
|
const payload = line.payload;
|
|
339
379
|
return payload.summary || '';
|
|
340
380
|
}
|
|
381
|
+
case 'turn_context': {
|
|
382
|
+
const payload = line.payload;
|
|
383
|
+
for (const key of ['model', 'cwd', 'approval_policy', 'sandbox_policy', 'effort']) {
|
|
384
|
+
if (typeof payload[key] === 'string')
|
|
385
|
+
parts.push(payload[key]);
|
|
386
|
+
}
|
|
387
|
+
return parts.join(' ');
|
|
388
|
+
}
|
|
341
389
|
default:
|
|
342
390
|
return '';
|
|
343
391
|
}
|
|
344
392
|
}
|
|
393
|
+
function extractTextFromUnknownContent(content) {
|
|
394
|
+
if (typeof content === 'string')
|
|
395
|
+
return content;
|
|
396
|
+
if (!Array.isArray(content))
|
|
397
|
+
return '';
|
|
398
|
+
return content
|
|
399
|
+
.map(part => {
|
|
400
|
+
const p = part;
|
|
401
|
+
return typeof p.text === 'string' ? p.text : '';
|
|
402
|
+
})
|
|
403
|
+
.filter(Boolean)
|
|
404
|
+
.join(' ');
|
|
405
|
+
}
|
|
345
406
|
// ---------------------------------------------------------------------------
|
|
346
407
|
// CodexReader — incremental JSONL reader wrapping CodexRolloutParser
|
|
347
408
|
// ---------------------------------------------------------------------------
|
|
@@ -827,82 +888,55 @@ class CodexProvider {
|
|
|
827
888
|
// --- Stats ---
|
|
828
889
|
readSessionStats(sessionPath) {
|
|
829
890
|
const sessionId = extractSessionId(path.basename(sessionPath));
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const modelUsage = {};
|
|
835
|
-
const toolUsage = {};
|
|
836
|
-
let compactionEstimate = 0;
|
|
837
|
-
let currentModel = 'unknown';
|
|
891
|
+
const aggregator = new EventAggregator_1.EventAggregator({
|
|
892
|
+
providerId: 'codex',
|
|
893
|
+
computeContextSize: usage => usage.inputTokens,
|
|
894
|
+
});
|
|
838
895
|
try {
|
|
839
|
-
const
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
const trimmed = line.trim();
|
|
843
|
-
if (!trimmed || !trimmed.startsWith('{'))
|
|
844
|
-
continue;
|
|
845
|
-
try {
|
|
846
|
-
const parsed = JSON.parse(trimmed);
|
|
847
|
-
if (!startTime && parsed.timestamp)
|
|
848
|
-
startTime = parsed.timestamp;
|
|
849
|
-
if (parsed.timestamp)
|
|
850
|
-
endTime = parsed.timestamp;
|
|
851
|
-
if (parsed.type === 'turn_context' && parsed.payload?.model)
|
|
852
|
-
currentModel = parsed.payload.model;
|
|
853
|
-
if (parsed.type === 'compacted')
|
|
854
|
-
compactionEstimate++;
|
|
855
|
-
if (parsed.type === 'response_item') {
|
|
856
|
-
const p = parsed.payload;
|
|
857
|
-
if (p?.role === 'user' || p?.role === 'assistant')
|
|
858
|
-
messageCount++;
|
|
859
|
-
if (p?.type === 'function_call') {
|
|
860
|
-
const name = p.name || 'unknown';
|
|
861
|
-
toolUsage[name] = (toolUsage[name] || 0) + 1;
|
|
862
|
-
}
|
|
863
|
-
if (p?.type === 'local_shell_call')
|
|
864
|
-
toolUsage['Bash'] = (toolUsage['Bash'] || 0) + 1;
|
|
865
|
-
if (p?.type === 'custom_tool_call') {
|
|
866
|
-
const name = p.name || 'unknown';
|
|
867
|
-
toolUsage[name] = (toolUsage[name] || 0) + 1;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
if (parsed.type === 'event_msg') {
|
|
871
|
-
const evt = parsed.payload;
|
|
872
|
-
if (evt?.type === 'token_count') {
|
|
873
|
-
const usage = evt.info?.last_token_usage || evt.info?.total_token_usage;
|
|
874
|
-
if (usage) {
|
|
875
|
-
tokens.input = usage.input_tokens || 0;
|
|
876
|
-
tokens.output = usage.output_tokens || 0;
|
|
877
|
-
tokens.cacheRead = usage.cached_input_tokens || 0;
|
|
878
|
-
if (!modelUsage[currentModel])
|
|
879
|
-
modelUsage[currentModel] = { calls: 0, tokens: 0 };
|
|
880
|
-
modelUsage[currentModel].calls++;
|
|
881
|
-
modelUsage[currentModel].tokens = (usage.input_tokens || 0) + (usage.output_tokens || 0);
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
catch { /* skip */ }
|
|
896
|
+
const reader = this.createReader(sessionPath);
|
|
897
|
+
for (const event of reader.readAll()) {
|
|
898
|
+
aggregator.processEvent(event);
|
|
887
899
|
}
|
|
888
900
|
}
|
|
889
|
-
catch {
|
|
901
|
+
catch (err) {
|
|
902
|
+
// Return an empty stats shell below. Surface the cause under DEBUG so a
|
|
903
|
+
// malformed rollout doesn't fail silently during dashboard refreshes.
|
|
904
|
+
if (process.env.DEBUG)
|
|
905
|
+
console.error(`CodexProvider.readSessionStats: ${err}`);
|
|
906
|
+
}
|
|
907
|
+
const metrics = aggregator.getMetrics();
|
|
908
|
+
const modelUsage = {};
|
|
909
|
+
for (const model of metrics.modelStats) {
|
|
910
|
+
modelUsage[model.model] = { calls: model.calls, tokens: model.tokens };
|
|
911
|
+
}
|
|
912
|
+
const toolUsage = {};
|
|
913
|
+
for (const tool of metrics.toolStats) {
|
|
914
|
+
toolUsage[tool.name] = tool.successCount + tool.failureCount + tool.pendingCount;
|
|
915
|
+
}
|
|
890
916
|
return {
|
|
891
917
|
providerId: 'codex',
|
|
892
918
|
sessionId,
|
|
893
919
|
filePath: sessionPath,
|
|
894
920
|
label: this.extractSessionLabel(sessionPath),
|
|
895
|
-
startTime,
|
|
896
|
-
endTime,
|
|
897
|
-
messageCount,
|
|
898
|
-
tokens
|
|
921
|
+
startTime: metrics.sessionStartTime || '',
|
|
922
|
+
endTime: metrics.lastEventTime || '',
|
|
923
|
+
messageCount: metrics.messageCount,
|
|
924
|
+
tokens: {
|
|
925
|
+
input: metrics.tokens.inputTokens,
|
|
926
|
+
output: metrics.tokens.outputTokens,
|
|
927
|
+
cacheWrite: metrics.tokens.cacheWriteTokens,
|
|
928
|
+
cacheRead: metrics.tokens.cacheReadTokens,
|
|
929
|
+
},
|
|
899
930
|
modelUsage,
|
|
900
931
|
toolUsage,
|
|
901
|
-
compactionEstimate,
|
|
902
|
-
truncationCount:
|
|
903
|
-
reportedCost:
|
|
932
|
+
compactionEstimate: metrics.compactionCount,
|
|
933
|
+
truncationCount: metrics.truncationCount,
|
|
934
|
+
reportedCost: metrics.tokens.reportedCost,
|
|
904
935
|
};
|
|
905
936
|
}
|
|
937
|
+
readSessionContextSnapshot(sessionPath, options = {}) {
|
|
938
|
+
return (0, sessionContext_1.readSessionContextSnapshot)(this, sessionPath, options);
|
|
939
|
+
}
|
|
906
940
|
// --- Optional methods ---
|
|
907
941
|
getContextWindowLimit(modelId) {
|
|
908
942
|
// Prefer actual model_context_window reported by token_count events
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* @module providers/openCode
|
|
17
17
|
*/
|
|
18
|
+
import type { ReadSessionContextSnapshotOptions, SessionContextSnapshot } from '../context/sessionContext';
|
|
18
19
|
import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileStats, ProviderId, ProviderRuntimeStatus } from './types';
|
|
19
20
|
import type { TokenUsage, SubagentStats, ContextAttribution } from '../types/sessionEvent';
|
|
20
21
|
/**
|
|
@@ -61,6 +62,7 @@ export declare class OpenCodeProvider implements SessionProviderBase {
|
|
|
61
62
|
private searchInSessionFromFiles;
|
|
62
63
|
getProjectsBaseDir(): string;
|
|
63
64
|
readSessionStats(sessionPath: string): SessionFileStats;
|
|
65
|
+
readSessionContextSnapshot(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
|
|
64
66
|
getSessionMetadata(sessionPath: string): {
|
|
65
67
|
mtime: Date;
|
|
66
68
|
} | null;
|
|
@@ -55,6 +55,7 @@ const fs = __importStar(require("fs"));
|
|
|
55
55
|
const os = __importStar(require("os"));
|
|
56
56
|
const path = __importStar(require("path"));
|
|
57
57
|
const child_process_1 = require("child_process");
|
|
58
|
+
const sessionContext_1 = require("../context/sessionContext");
|
|
58
59
|
const openCodeParser_1 = require("../parsers/openCodeParser");
|
|
59
60
|
const openCodeDatabase_1 = require("./openCodeDatabase");
|
|
60
61
|
const modelContext_1 = require("../modelContext");
|
|
@@ -1367,6 +1368,9 @@ class OpenCodeProvider {
|
|
|
1367
1368
|
reportedCost,
|
|
1368
1369
|
};
|
|
1369
1370
|
}
|
|
1371
|
+
readSessionContextSnapshot(sessionPath, options = {}) {
|
|
1372
|
+
return (0, sessionContext_1.readSessionContextSnapshot)(this, sessionPath, options);
|
|
1373
|
+
}
|
|
1370
1374
|
// --- Optional methods ---
|
|
1371
1375
|
getSessionMetadata(sessionPath) {
|
|
1372
1376
|
// Check cache first
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* SessionProvider is the original simplified interface maintained for backward
|
|
9
9
|
* compatibility with existing CLI code.
|
|
10
10
|
*/
|
|
11
|
+
import type { ReadSessionContextSnapshotOptions, SessionContextSnapshot } from '../context/sessionContext';
|
|
11
12
|
import type { SessionEvent, SubagentStats, TokenUsage, ContextAttribution } from '../types/sessionEvent';
|
|
12
13
|
export type ProviderId = 'claude-code' | 'opencode' | 'codex';
|
|
13
14
|
export interface SearchHit {
|
|
@@ -138,6 +139,8 @@ export interface SessionProviderBase {
|
|
|
138
139
|
getCurrentUsageSnapshot?(sessionPath: string): TokenUsage | null;
|
|
139
140
|
/** Gets context attribution breakdown from provider data, if available. */
|
|
140
141
|
getContextAttribution?(sessionPath: string): ContextAttribution | null;
|
|
142
|
+
/** Reads a provider-neutral context evidence snapshot for a session. */
|
|
143
|
+
readSessionContextSnapshot?(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
|
|
141
144
|
/** Reports provider runtime readiness for DB-backed providers. */
|
|
142
145
|
getRuntimeStatus?(): ProviderRuntimeStatus;
|
|
143
146
|
/** Tests whether a provider can monitor a directory path, including synthetic DB-backed paths. */
|
|
@@ -156,6 +159,7 @@ export interface SessionProvider {
|
|
|
156
159
|
findAllSessions(workspacePath: string): string[];
|
|
157
160
|
getProjectsBaseDir(): string;
|
|
158
161
|
readSessionStats(sessionPath: string): SessionFileStats;
|
|
162
|
+
readSessionContextSnapshot?(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
|
|
159
163
|
extractSessionLabel(sessionPath: string): string | null;
|
|
160
164
|
searchInSession(sessionPath: string, query: string, maxResults: number): SearchHit[];
|
|
161
165
|
getAllProjectFolders(workspacePath?: string): ProjectFolderInfo[];
|
|
@@ -217,6 +217,7 @@ tr:hover td { background: rgba(155,109,255,0.05); }
|
|
|
217
217
|
.role-badge.summary { background: rgba(107,95,128,0.2); color: var(--text-muted); }
|
|
218
218
|
.message-ts { color: var(--text-muted); margin-left: auto; }
|
|
219
219
|
.message-model { color: var(--text-secondary); font-family: monospace; font-size: 11px; }
|
|
220
|
+
.message-source { color: var(--text-muted); font-family: monospace; font-size: 11px; font-style: italic; }
|
|
220
221
|
.message-tokens { color: var(--text-muted); font-size: 11px; }
|
|
221
222
|
|
|
222
223
|
.message-body { padding: 12px 14px; }
|
|
@@ -498,6 +499,7 @@ function generateTranscript(transcript, includeThinking, includeToolDetail) {
|
|
|
498
499
|
function renderMessage(entry, includeThinking, includeToolDetail) {
|
|
499
500
|
const ts = entry.timestamp ? (0, htmlHelpers_1.formatTimestamp)(entry.timestamp) : '';
|
|
500
501
|
const roleBadge = `<span class="role-badge ${entry.type}">${entry.type}</span>`;
|
|
502
|
+
const sourceStr = entry.sourceLabel ? `<span class="message-source">${(0, htmlHelpers_1.escapeHtml)(entry.sourceLabel)}</span>` : '';
|
|
501
503
|
const modelStr = entry.model ? `<span class="message-model">${(0, htmlHelpers_1.escapeHtml)(entry.model)}</span>` : '';
|
|
502
504
|
const tokenStr = entry.usage
|
|
503
505
|
? `<span class="message-tokens">${(0, htmlHelpers_1.fmtTokens)(entry.usage.input_tokens + entry.usage.output_tokens)} tokens</span>`
|
|
@@ -512,6 +514,7 @@ function renderMessage(entry, includeThinking, includeToolDetail) {
|
|
|
512
514
|
return `<div class="message ${entry.type}" data-type="${entry.type}">
|
|
513
515
|
<div class="message-header">
|
|
514
516
|
${roleBadge}
|
|
517
|
+
${sourceStr}
|
|
515
518
|
${modelStr}
|
|
516
519
|
${tokenStr}
|
|
517
520
|
<span class="message-ts">${ts}</span>
|
package/dist/report/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Public API for the report module.
|
|
3
3
|
*/
|
|
4
4
|
export type { TranscriptContentBlock, TranscriptEntry, HtmlReportOptions } from './types';
|
|
5
|
-
export { parseTranscript } from './transcriptParser';
|
|
5
|
+
export { parseTranscript, parseTranscriptFromEvents } from './transcriptParser';
|
|
6
6
|
export { generateHtmlReport } from './htmlReportGenerator';
|
|
7
7
|
export { openInBrowser } from './openBrowser';
|
|
8
8
|
export { escapeHtml, simpleMarkdownToHtml, highlightCodeBlock } from './htmlHelpers';
|
package/dist/report/index.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Public API for the report module.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.highlightCodeBlock = exports.simpleMarkdownToHtml = exports.escapeHtml = exports.openInBrowser = exports.generateHtmlReport = exports.parseTranscript = void 0;
|
|
6
|
+
exports.highlightCodeBlock = exports.simpleMarkdownToHtml = exports.escapeHtml = exports.openInBrowser = exports.generateHtmlReport = exports.parseTranscriptFromEvents = exports.parseTranscript = void 0;
|
|
7
7
|
var transcriptParser_1 = require("./transcriptParser");
|
|
8
8
|
Object.defineProperty(exports, "parseTranscript", { enumerable: true, get: function () { return transcriptParser_1.parseTranscript; } });
|
|
9
|
+
Object.defineProperty(exports, "parseTranscriptFromEvents", { enumerable: true, get: function () { return transcriptParser_1.parseTranscriptFromEvents; } });
|
|
9
10
|
var htmlReportGenerator_1 = require("./htmlReportGenerator");
|
|
10
11
|
Object.defineProperty(exports, "generateHtmlReport", { enumerable: true, get: function () { return htmlReportGenerator_1.generateHtmlReport; } });
|
|
11
12
|
var openBrowser_1 = require("./openBrowser");
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Parse Claude Code JSONL session files into typed transcript entries
|
|
3
3
|
* with full, untruncated content suitable for HTML report rendering.
|
|
4
4
|
*/
|
|
5
|
+
import type { SessionEvent } from '../types/sessionEvent';
|
|
5
6
|
import type { TranscriptEntry } from './types';
|
|
6
7
|
/**
|
|
7
8
|
* Parse a JSONL session file into an array of TranscriptEntry objects.
|
|
@@ -10,3 +11,5 @@ import type { TranscriptEntry } from './types';
|
|
|
10
11
|
* this preserves full message content for HTML report rendering.
|
|
11
12
|
*/
|
|
12
13
|
export declare function parseTranscript(sessionPath: string): TranscriptEntry[];
|
|
14
|
+
/** Parse canonical provider events into transcript entries. */
|
|
15
|
+
export declare function parseTranscriptFromEvents(events: SessionEvent[]): TranscriptEntry[];
|
|
@@ -38,6 +38,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.parseTranscript = parseTranscript;
|
|
41
|
+
exports.parseTranscriptFromEvents = parseTranscriptFromEvents;
|
|
41
42
|
const fs = __importStar(require("fs"));
|
|
42
43
|
const jsonl_1 = require("../parsers/jsonl");
|
|
43
44
|
/**
|
|
@@ -69,11 +70,23 @@ function parseTranscript(sessionPath) {
|
|
|
69
70
|
}
|
|
70
71
|
return entries;
|
|
71
72
|
}
|
|
73
|
+
/** Parse canonical provider events into transcript entries. */
|
|
74
|
+
function parseTranscriptFromEvents(events) {
|
|
75
|
+
const entries = [];
|
|
76
|
+
for (const event of events) {
|
|
77
|
+
const entry = eventToTranscriptEntry(event);
|
|
78
|
+
if (entry) {
|
|
79
|
+
entries.push(entry);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return entries;
|
|
83
|
+
}
|
|
72
84
|
function eventToTranscriptEntry(event) {
|
|
73
85
|
if (!event.type || !event.message)
|
|
74
86
|
return null;
|
|
75
87
|
const role = event.message.role;
|
|
76
88
|
const timestamp = event.timestamp || '';
|
|
89
|
+
const sourceLabel = event.message.sourceLabel;
|
|
77
90
|
const model = event.message.model;
|
|
78
91
|
const usage = event.message.usage
|
|
79
92
|
? {
|
|
@@ -85,13 +98,22 @@ function eventToTranscriptEntry(event) {
|
|
|
85
98
|
: undefined;
|
|
86
99
|
const content = extractContentBlocks(event.message.content);
|
|
87
100
|
// Skip empty entries (warmup messages, etc.)
|
|
88
|
-
if (content.length === 0)
|
|
89
|
-
|
|
101
|
+
if (content.length === 0) {
|
|
102
|
+
if (usage || sourceLabel) {
|
|
103
|
+
content.push({ type: 'text', text: sourceLabel ? `${sourceLabel} updated` : 'Usage updated' });
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
90
109
|
let type;
|
|
91
110
|
// Check event.type first — summary events have role 'assistant' but should be typed as 'summary'
|
|
92
111
|
if (event.type === 'summary') {
|
|
93
112
|
type = 'summary';
|
|
94
113
|
}
|
|
114
|
+
else if (event.type === 'system') {
|
|
115
|
+
type = 'system';
|
|
116
|
+
}
|
|
95
117
|
else if (role === 'user') {
|
|
96
118
|
type = 'user';
|
|
97
119
|
}
|
|
@@ -101,7 +123,7 @@ function eventToTranscriptEntry(event) {
|
|
|
101
123
|
else {
|
|
102
124
|
type = 'system';
|
|
103
125
|
}
|
|
104
|
-
return { type, timestamp, model, usage, content };
|
|
126
|
+
return { type, timestamp, sourceLabel, model, usage, content };
|
|
105
127
|
}
|
|
106
128
|
function extractContentBlocks(content) {
|
|
107
129
|
if (!content)
|
package/dist/report/types.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface TranscriptContentBlock {
|
|
|
21
21
|
export interface TranscriptEntry {
|
|
22
22
|
type: 'user' | 'assistant' | 'system' | 'summary';
|
|
23
23
|
timestamp: string;
|
|
24
|
+
/** Optional human-readable source label (e.g. "developer", "base instructions"). */
|
|
25
|
+
sourceLabel?: string;
|
|
24
26
|
model?: string;
|
|
25
27
|
usage?: {
|
|
26
28
|
input_tokens: number;
|
|
@@ -18,6 +18,7 @@ export declare const messageUsageSchema: z.ZodObject<{
|
|
|
18
18
|
export declare const sessionMessageSchema: z.ZodObject<{
|
|
19
19
|
role: z.ZodString;
|
|
20
20
|
id: z.ZodOptional<z.ZodString>;
|
|
21
|
+
sourceLabel: z.ZodOptional<z.ZodString>;
|
|
21
22
|
model: z.ZodOptional<z.ZodString>;
|
|
22
23
|
usage: z.ZodOptional<z.ZodObject<{
|
|
23
24
|
input_tokens: z.ZodNumber;
|
|
@@ -42,10 +43,12 @@ export declare const sessionEventSchema: z.ZodObject<{
|
|
|
42
43
|
tool_use: "tool_use";
|
|
43
44
|
tool_result: "tool_result";
|
|
44
45
|
summary: "summary";
|
|
46
|
+
system: "system";
|
|
45
47
|
}>;
|
|
46
48
|
message: z.ZodObject<{
|
|
47
49
|
role: z.ZodString;
|
|
48
50
|
id: z.ZodOptional<z.ZodString>;
|
|
51
|
+
sourceLabel: z.ZodOptional<z.ZodString>;
|
|
49
52
|
model: z.ZodOptional<z.ZodString>;
|
|
50
53
|
usage: z.ZodOptional<z.ZodObject<{
|
|
51
54
|
input_tokens: z.ZodNumber;
|
|
@@ -65,6 +68,18 @@ export declare const sessionEventSchema: z.ZodObject<{
|
|
|
65
68
|
bypassPermissions: "bypassPermissions";
|
|
66
69
|
plan: "plan";
|
|
67
70
|
}>>;
|
|
71
|
+
rateLimits: z.ZodOptional<z.ZodObject<{
|
|
72
|
+
primary: z.ZodOptional<z.ZodObject<{
|
|
73
|
+
usedPercent: z.ZodNumber;
|
|
74
|
+
windowMinutes: z.ZodNumber;
|
|
75
|
+
resetsAt: z.ZodNumber;
|
|
76
|
+
}, z.core.$strip>>;
|
|
77
|
+
secondary: z.ZodOptional<z.ZodObject<{
|
|
78
|
+
usedPercent: z.ZodNumber;
|
|
79
|
+
windowMinutes: z.ZodNumber;
|
|
80
|
+
resetsAt: z.ZodNumber;
|
|
81
|
+
}, z.core.$strip>>;
|
|
82
|
+
}, z.core.$strip>>;
|
|
68
83
|
tool: z.ZodOptional<z.ZodObject<{
|
|
69
84
|
name: z.ZodString;
|
|
70
85
|
input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -23,6 +23,7 @@ exports.messageUsageSchema = zod_1.z.object({
|
|
|
23
23
|
exports.sessionMessageSchema = zod_1.z.object({
|
|
24
24
|
role: zod_1.z.string(),
|
|
25
25
|
id: zod_1.z.string().optional(),
|
|
26
|
+
sourceLabel: zod_1.z.string().optional(),
|
|
26
27
|
model: zod_1.z.string().optional(),
|
|
27
28
|
usage: exports.messageUsageSchema.optional(),
|
|
28
29
|
content: zod_1.z.unknown().optional(),
|
|
@@ -36,11 +37,23 @@ exports.permissionModeSchema = zod_1.z.enum([
|
|
|
36
37
|
]);
|
|
37
38
|
// ── SessionEvent ──
|
|
38
39
|
exports.sessionEventSchema = zod_1.z.object({
|
|
39
|
-
type: zod_1.z.enum(['user', 'assistant', 'tool_use', 'tool_result', 'summary']),
|
|
40
|
+
type: zod_1.z.enum(['user', 'assistant', 'tool_use', 'tool_result', 'summary', 'system']),
|
|
40
41
|
message: exports.sessionMessageSchema,
|
|
41
42
|
timestamp: zod_1.z.string(),
|
|
42
43
|
isSidechain: zod_1.z.boolean().optional(),
|
|
43
44
|
permissionMode: exports.permissionModeSchema.optional(),
|
|
45
|
+
rateLimits: zod_1.z.object({
|
|
46
|
+
primary: zod_1.z.object({
|
|
47
|
+
usedPercent: zod_1.z.number(),
|
|
48
|
+
windowMinutes: zod_1.z.number(),
|
|
49
|
+
resetsAt: zod_1.z.number(),
|
|
50
|
+
}).optional(),
|
|
51
|
+
secondary: zod_1.z.object({
|
|
52
|
+
usedPercent: zod_1.z.number(),
|
|
53
|
+
windowMinutes: zod_1.z.number(),
|
|
54
|
+
resetsAt: zod_1.z.number(),
|
|
55
|
+
}).optional(),
|
|
56
|
+
}).optional(),
|
|
44
57
|
tool: zod_1.z.object({
|
|
45
58
|
name: zod_1.z.string(),
|
|
46
59
|
input: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()),
|
|
@@ -37,6 +37,8 @@ export interface SessionMessage {
|
|
|
37
37
|
role: string;
|
|
38
38
|
/** Optional message identifier (for deduplication) */
|
|
39
39
|
id?: string;
|
|
40
|
+
/** Optional human-readable source label (e.g. "developer", "base instructions"). */
|
|
41
|
+
sourceLabel?: string;
|
|
40
42
|
/** Model identifier (e.g., "claude-opus-4-20250514") */
|
|
41
43
|
model?: string;
|
|
42
44
|
/** Token usage statistics (only present in assistant messages) */
|
|
@@ -52,7 +54,7 @@ export interface SessionMessage {
|
|
|
52
54
|
*/
|
|
53
55
|
export interface SessionEvent {
|
|
54
56
|
/** Event type discriminator */
|
|
55
|
-
type: 'user' | 'assistant' | 'tool_use' | 'tool_result' | 'summary';
|
|
57
|
+
type: 'user' | 'assistant' | 'tool_use' | 'tool_result' | 'summary' | 'system';
|
|
56
58
|
/** Message data containing role, model, usage */
|
|
57
59
|
message: SessionMessage;
|
|
58
60
|
/** ISO 8601 timestamp of event */
|
|
@@ -61,6 +63,19 @@ export interface SessionEvent {
|
|
|
61
63
|
isSidechain?: boolean;
|
|
62
64
|
/** Permission mode active when this event occurred */
|
|
63
65
|
permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
|
|
66
|
+
/** Subscription rate limits, when provided by the session source. */
|
|
67
|
+
rateLimits?: {
|
|
68
|
+
primary?: {
|
|
69
|
+
usedPercent: number;
|
|
70
|
+
windowMinutes: number;
|
|
71
|
+
resetsAt: number;
|
|
72
|
+
};
|
|
73
|
+
secondary?: {
|
|
74
|
+
usedPercent: number;
|
|
75
|
+
windowMinutes: number;
|
|
76
|
+
resetsAt: number;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
64
79
|
/** Tool use details (when type is 'tool_use') */
|
|
65
80
|
tool?: {
|
|
66
81
|
name: string;
|
|
@@ -30,6 +30,7 @@ function toFollowEvents(event, providerId) {
|
|
|
30
30
|
: undefined;
|
|
31
31
|
const cost = usage?.reported_cost;
|
|
32
32
|
const model = event.message?.model;
|
|
33
|
+
const rateLimits = event.rateLimits;
|
|
33
34
|
switch (event.type) {
|
|
34
35
|
case 'user': {
|
|
35
36
|
const content = event.message?.content;
|
|
@@ -118,6 +119,24 @@ function toFollowEvents(event, providerId) {
|
|
|
118
119
|
});
|
|
119
120
|
break;
|
|
120
121
|
}
|
|
122
|
+
case 'system': {
|
|
123
|
+
const text = extractTextContent(event.message?.content);
|
|
124
|
+
const label = event.message?.sourceLabel || event.message?.role || 'system';
|
|
125
|
+
const summary = text || (usage
|
|
126
|
+
? `Tokens: ${usage.input_tokens || 0} in / ${usage.output_tokens || 0} out`
|
|
127
|
+
: label);
|
|
128
|
+
events.push({
|
|
129
|
+
providerId, type: 'system', timestamp: ts,
|
|
130
|
+
summary: label && text ? `${label}: ${text}` : summary,
|
|
131
|
+
tokens,
|
|
132
|
+
cacheTokens,
|
|
133
|
+
cost,
|
|
134
|
+
model,
|
|
135
|
+
rateLimits,
|
|
136
|
+
raw: event,
|
|
137
|
+
});
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
121
140
|
default: {
|
|
122
141
|
// Handle 'result' and other types as system events
|
|
123
142
|
const evtType = event.type;
|
|
@@ -136,6 +155,11 @@ function toFollowEvents(event, providerId) {
|
|
|
136
155
|
e.permissionMode = permissionMode;
|
|
137
156
|
}
|
|
138
157
|
}
|
|
158
|
+
if (rateLimits) {
|
|
159
|
+
for (const e of events) {
|
|
160
|
+
e.rateLimits = e.rateLimits ?? rateLimits;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
139
163
|
return events;
|
|
140
164
|
}
|
|
141
165
|
// ── Helpers ──
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Factory for creating the correct session watcher by provider.
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type { SessionProviderBase } from '../providers/types';
|
|
5
5
|
import type { SessionWatcher, SessionWatcherCallbacks } from './types';
|
|
6
6
|
export interface CreateWatcherOptions {
|
|
7
|
-
provider:
|
|
7
|
+
provider: SessionProviderBase;
|
|
8
8
|
workspacePath: string;
|
|
9
9
|
sessionId?: string;
|
|
10
10
|
callbacks: SessionWatcherCallbacks;
|