sidekick-shared 0.21.2 → 0.21.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 +45 -45
- package/dist/accountManager.js +9 -7
- package/dist/accountRegistry.js +18 -14
- package/dist/accountStatus.js +4 -1
- package/dist/accounts.js +60 -25
- package/dist/aggregation/EventAggregator.d.ts +14 -11
- package/dist/aggregation/EventAggregator.js +68 -31
- package/dist/aggregation/PatternExtractor.js +5 -4
- package/dist/autoSwitch.js +6 -12
- package/dist/claudeProfiles.js +3 -3
- package/dist/codexProfiles.js +45 -22
- package/dist/codexQuota.js +44 -16
- package/dist/codexQuotaWatcher.js +10 -3
- package/dist/context/composer.js +8 -6
- package/dist/context/sessionContext.js +12 -9
- package/dist/credentialIO.js +10 -8
- package/dist/extractors/sessionAssets.js +9 -1
- package/dist/extractors/sources/claudeAssets.js +7 -1
- package/dist/extractors/sources/codexAssets.js +14 -3
- package/dist/extractors/toolCall.js +1 -4
- package/dist/formatters/eventHighlighter.js +148 -28
- package/dist/formatters/noiseClassifier.js +3 -11
- package/dist/formatters/sessionDump.js +22 -13
- package/dist/formatters/toolSummary.js +1 -3
- package/dist/index.d.ts +23 -23
- package/dist/index.js +7 -3
- package/dist/modelContext.js +10 -8
- package/dist/modelInfo.js +11 -6
- package/dist/multiProviderQuotaService.js +10 -7
- package/dist/parsers/codexParser.js +170 -103
- package/dist/parsers/debugLogParser.js +5 -4
- package/dist/parsers/openCodeParser.js +66 -48
- package/dist/parsers/planExtractor.js +3 -3
- package/dist/parsers/sessionActivityDetector.js +10 -6
- package/dist/parsers/sessionPathResolver.js +29 -34
- package/dist/parsers/subagentScanner.js +21 -20
- package/dist/parsers/subagentTraceParser.js +7 -4
- package/dist/phrases.js +11 -11
- package/dist/providerStatus.js +3 -4
- package/dist/providers/claudeCode.js +3 -1
- package/dist/providers/codex.js +35 -14
- package/dist/providers/codexDatabase.js +14 -5
- package/dist/providers/detect.js +10 -6
- package/dist/providers/openCode.js +91 -63
- package/dist/providers/openCodeDatabase.js +44 -17
- package/dist/quota.d.ts +19 -0
- package/dist/quota.js +50 -8
- package/dist/quotaHistory.js +10 -3
- package/dist/quotaSnapshots.js +2 -2
- package/dist/readers/decisions.js +1 -1
- package/dist/readers/notes.js +5 -3
- package/dist/readers/plans.js +43 -23
- package/dist/readers/tasks.js +1 -1
- package/dist/report/htmlHelpers.js +135 -16
- package/dist/report/htmlReportGenerator.js +38 -12
- package/dist/report/transcriptParser.js +8 -2
- package/dist/schemas/index.d.ts +4 -4
- package/dist/schemas/quota.js +2 -10
- package/dist/schemas/sessionEvent.js +20 -10
- package/dist/search/advancedFilter.js +3 -3
- package/dist/search/sessionSearch.js +10 -4
- package/dist/terminalSync.js +4 -2
- package/dist/turns/assistantTurn.js +5 -1
- package/dist/watchers/eventBridge.js +56 -26
- package/dist/watchers/factory.js +5 -2
- package/dist/watchers/jsonlTail.js +3 -1
- package/dist/watchers/jsonlWatcher.js +128 -52
- package/dist/watchers/providerReaderWatcher.js +3 -1
- package/dist/watchers/sqliteWatcher.js +52 -22
- package/dist/zaiQuota.js +6 -10
- package/dist/zaiQuotaApi.js +13 -5
- package/dist/zaiQuotaWatcher.js +1 -1
- package/package.json +5 -2
|
@@ -93,7 +93,8 @@ class CodexQuotaWatcher {
|
|
|
93
93
|
running = false;
|
|
94
94
|
constructor(workspacePath, options = {}) {
|
|
95
95
|
this.workspacePath = workspacePath;
|
|
96
|
-
this.discoveryPollIntervalMs =
|
|
96
|
+
this.discoveryPollIntervalMs =
|
|
97
|
+
options.discoveryPollIntervalMs ?? DEFAULT_DISCOVERY_POLL_INTERVAL_MS;
|
|
97
98
|
this.providerFactory = options.providerFactory ?? (() => new codex_1.CodexProvider());
|
|
98
99
|
this.getActiveAccount = options.getActiveAccount ?? codexProfiles_1.getActiveCodexAccount;
|
|
99
100
|
this.readSnapshot = options.readSnapshot ?? quotaSnapshots_1.readQuotaSnapshot;
|
|
@@ -207,8 +208,14 @@ class CodexQuotaWatcher {
|
|
|
207
208
|
runtimeProvider: 'codex',
|
|
208
209
|
providerId: account.id,
|
|
209
210
|
workspaceId: this.workspaceId,
|
|
210
|
-
fiveHour: {
|
|
211
|
-
|
|
211
|
+
fiveHour: {
|
|
212
|
+
utilization: liveQuota.fiveHour.utilization,
|
|
213
|
+
resetsAt: liveQuota.fiveHour.resetsAt,
|
|
214
|
+
},
|
|
215
|
+
sevenDay: {
|
|
216
|
+
utilization: liveQuota.sevenDay.utilization,
|
|
217
|
+
resetsAt: liveQuota.sevenDay.resetsAt,
|
|
218
|
+
},
|
|
212
219
|
available: liveQuota.available,
|
|
213
220
|
error: liveQuota.error,
|
|
214
221
|
source: liveQuota.source,
|
package/dist/context/composer.js
CHANGED
|
@@ -29,7 +29,9 @@ async function composeContext(slug, fidelity, provider, workspacePath) {
|
|
|
29
29
|
try {
|
|
30
30
|
sessionSummaries.push(provider.readSessionStats(sp));
|
|
31
31
|
}
|
|
32
|
-
catch {
|
|
32
|
+
catch {
|
|
33
|
+
/* skip */
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
// Apply fidelity filtering
|
|
35
37
|
let tasks;
|
|
@@ -40,23 +42,23 @@ async function composeContext(slug, fidelity, provider, workspacePath) {
|
|
|
40
42
|
case 'full':
|
|
41
43
|
tasks = allTasks;
|
|
42
44
|
decisions = allDecisions;
|
|
43
|
-
notes = allNotes.filter(n => n.status === 'active' || n.status === 'needs_review');
|
|
45
|
+
notes = allNotes.filter((n) => n.status === 'active' || n.status === 'needs_review');
|
|
44
46
|
if (history) {
|
|
45
47
|
stats = { tokens: { ...history.allTime.tokens }, cost: history.allTime.totalCost };
|
|
46
48
|
}
|
|
47
49
|
break;
|
|
48
50
|
case 'compact':
|
|
49
|
-
tasks = allTasks.filter(t => t.status === 'pending');
|
|
51
|
+
tasks = allTasks.filter((t) => t.status === 'pending');
|
|
50
52
|
decisions = allDecisions.slice(0, 10);
|
|
51
|
-
notes = allNotes.filter(n => n.status === 'active');
|
|
53
|
+
notes = allNotes.filter((n) => n.status === 'active');
|
|
52
54
|
if (history) {
|
|
53
55
|
stats = { tokens: { ...history.allTime.tokens }, cost: history.allTime.totalCost };
|
|
54
56
|
}
|
|
55
57
|
break;
|
|
56
58
|
case 'brief':
|
|
57
|
-
tasks = allTasks.filter(t => t.status === 'pending').slice(0, 3);
|
|
59
|
+
tasks = allTasks.filter((t) => t.status === 'pending').slice(0, 3);
|
|
58
60
|
decisions = []; // Count only
|
|
59
|
-
notes = allNotes.filter(n => n.importance === 'critical');
|
|
61
|
+
notes = allNotes.filter((n) => n.importance === 'critical');
|
|
60
62
|
stats = null;
|
|
61
63
|
break;
|
|
62
64
|
}
|
|
@@ -65,7 +65,7 @@ function buildSessionContextSnapshot(events, options = {}) {
|
|
|
65
65
|
const allSources = sourceState.sources;
|
|
66
66
|
const limitedSources = limitSources(allSources, options.sourceLimit ?? DEFAULT_CONTEXT_SOURCE_LIMIT);
|
|
67
67
|
const breakdown = buildLayerBreakdown(limitedSources);
|
|
68
|
-
const layers = breakdown.map(row => row.layer);
|
|
68
|
+
const layers = breakdown.map((row) => row.layer);
|
|
69
69
|
const sourceTokenTotal = allSources.reduce((sum, source) => sum + source.tokenEstimate, 0);
|
|
70
70
|
const contextTokens = metrics.currentContextSize > 0 ? metrics.currentContextSize : sourceTokenTotal;
|
|
71
71
|
const { pressure, ratio } = calculateSessionContextPressure(contextTokens, contextWindow);
|
|
@@ -381,16 +381,16 @@ function limitSources(sources, limit) {
|
|
|
381
381
|
return [];
|
|
382
382
|
if (sources.length <= limit)
|
|
383
383
|
return [...sources];
|
|
384
|
-
const pinned = sources.filter(source => source.sourceType === 'system' ||
|
|
384
|
+
const pinned = sources.filter((source) => source.sourceType === 'system' ||
|
|
385
385
|
source.sourceType === 'runtime' ||
|
|
386
386
|
source.sourceType === 'rate_limit');
|
|
387
387
|
const pinnedLimit = Math.min(pinned.length, Math.max(1, Math.floor(limit / 4)));
|
|
388
388
|
const pinnedKept = pinned.slice(0, pinnedLimit);
|
|
389
|
-
const pinnedIds = new Set(pinnedKept.map(source => source.id));
|
|
390
|
-
const remaining = sources.filter(source => !pinnedIds.has(source.id));
|
|
389
|
+
const pinnedIds = new Set(pinnedKept.map((source) => source.id));
|
|
390
|
+
const remaining = sources.filter((source) => !pinnedIds.has(source.id));
|
|
391
391
|
const latest = remaining.slice(-(limit - pinnedKept.length));
|
|
392
|
-
const keepIds = new Set([...pinnedKept, ...latest].map(source => source.id));
|
|
393
|
-
return sources.filter(source => keepIds.has(source.id));
|
|
392
|
+
const keepIds = new Set([...pinnedKept, ...latest].map((source) => source.id));
|
|
393
|
+
return sources.filter((source) => keepIds.has(source.id));
|
|
394
394
|
}
|
|
395
395
|
function latestModel(events) {
|
|
396
396
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
@@ -440,7 +440,7 @@ function inferSourceFile(input) {
|
|
|
440
440
|
}
|
|
441
441
|
const files = input.files;
|
|
442
442
|
if (Array.isArray(files)) {
|
|
443
|
-
const first = files.find(value => typeof value === 'string' && value.trim());
|
|
443
|
+
const first = files.find((value) => typeof value === 'string' && value.trim());
|
|
444
444
|
if (typeof first === 'string')
|
|
445
445
|
return first.trim();
|
|
446
446
|
}
|
|
@@ -451,7 +451,10 @@ function inferCommand(input) {
|
|
|
451
451
|
if (typeof command === 'string' && command.trim())
|
|
452
452
|
return command.trim();
|
|
453
453
|
if (Array.isArray(command)) {
|
|
454
|
-
return command
|
|
454
|
+
return (command
|
|
455
|
+
.map((part) => String(part))
|
|
456
|
+
.join(' ')
|
|
457
|
+
.trim() || undefined);
|
|
455
458
|
}
|
|
456
459
|
return undefined;
|
|
457
460
|
}
|
|
@@ -469,7 +472,7 @@ function extractText(content) {
|
|
|
469
472
|
return content;
|
|
470
473
|
if (Array.isArray(content)) {
|
|
471
474
|
return content
|
|
472
|
-
.map(block => isRecord(block) ? blockText(block) : '')
|
|
475
|
+
.map((block) => (isRecord(block) ? blockText(block) : ''))
|
|
473
476
|
.filter(Boolean)
|
|
474
477
|
.join('\n');
|
|
475
478
|
}
|
package/dist/credentialIO.js
CHANGED
|
@@ -66,9 +66,7 @@ function getCredentialsFilePath(configDir) {
|
|
|
66
66
|
function readActiveCredentials(configDir) {
|
|
67
67
|
if (process.platform === 'darwin') {
|
|
68
68
|
try {
|
|
69
|
-
const raw = (0, child_process_1.execFileSync)('security', [
|
|
70
|
-
'find-generic-password', '-s', (0, claudeProfiles_1.claudeKeychainService)(configDir), '-w',
|
|
71
|
-
], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
69
|
+
const raw = (0, child_process_1.execFileSync)('security', ['find-generic-password', '-s', (0, claudeProfiles_1.claudeKeychainService)(configDir), '-w'], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 4000, killSignal: 'SIGKILL' });
|
|
72
70
|
return JSON.parse(raw.trim());
|
|
73
71
|
}
|
|
74
72
|
catch {
|
|
@@ -96,11 +94,15 @@ function writeActiveCredentials(credentials, configDir) {
|
|
|
96
94
|
JSON.parse(json); // validate round-trip
|
|
97
95
|
if (process.platform === 'darwin') {
|
|
98
96
|
(0, child_process_1.execFileSync)('security', [
|
|
99
|
-
'add-generic-password',
|
|
100
|
-
'-
|
|
101
|
-
'-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
'add-generic-password',
|
|
98
|
+
'-U',
|
|
99
|
+
'-s',
|
|
100
|
+
(0, claudeProfiles_1.claudeKeychainService)(configDir),
|
|
101
|
+
'-a',
|
|
102
|
+
process.env.USER || 'user',
|
|
103
|
+
'-w',
|
|
104
|
+
json,
|
|
105
|
+
], { stdio: ['pipe', 'pipe', 'pipe'], timeout: 4000, killSignal: 'SIGKILL' });
|
|
104
106
|
return;
|
|
105
107
|
}
|
|
106
108
|
// Linux / WSL / Windows — file-based
|
|
@@ -62,7 +62,15 @@ const os = __importStar(require("node:os"));
|
|
|
62
62
|
const node_path_1 = require("node:path");
|
|
63
63
|
const URL_RE = /(?:https?|file):\/\/[^\s"'<>)\]`\\]+/g;
|
|
64
64
|
const PATH_RE = /(?:~|\.{0,2}\/)?[A-Za-z0-9_@.-]*(?:\/[A-Za-z0-9_@.-]+)+(?::\d+){0,2}|[A-Za-z0-9_@-]+\.[A-Za-z0-9]{1,8}(?::\d+){0,2}/g;
|
|
65
|
-
const SHELL_TAGS = new Set([
|
|
65
|
+
const SHELL_TAGS = new Set([
|
|
66
|
+
'sh',
|
|
67
|
+
'bash',
|
|
68
|
+
'shell',
|
|
69
|
+
'zsh',
|
|
70
|
+
'console',
|
|
71
|
+
'shellscript',
|
|
72
|
+
'shell-session',
|
|
73
|
+
]);
|
|
66
74
|
exports.DEFAULT_CAPS = {
|
|
67
75
|
command: 60,
|
|
68
76
|
path: 60,
|
|
@@ -149,7 +149,13 @@ function accumClaude(filePath, cwd, acc) {
|
|
|
149
149
|
}
|
|
150
150
|
function readClaudeAssets(cwd, limit = 3) {
|
|
151
151
|
const sessions = claudeSessions(cwd, limit);
|
|
152
|
-
const acc = {
|
|
152
|
+
const acc = {
|
|
153
|
+
urls: [],
|
|
154
|
+
paths: [],
|
|
155
|
+
commands: [],
|
|
156
|
+
plans: [],
|
|
157
|
+
hadSession: sessions.length > 0,
|
|
158
|
+
};
|
|
153
159
|
for (const session of sessions)
|
|
154
160
|
accumClaude(session, cwd, acc);
|
|
155
161
|
return acc;
|
|
@@ -99,7 +99,9 @@ function rolloutFiles(limit = 150) {
|
|
|
99
99
|
if (entry.isDirectory()) {
|
|
100
100
|
walk(fullPath);
|
|
101
101
|
}
|
|
102
|
-
else if (entry.isFile() &&
|
|
102
|
+
else if (entry.isFile() &&
|
|
103
|
+
entry.name.startsWith('rollout-') &&
|
|
104
|
+
entry.name.endsWith('.jsonl')) {
|
|
103
105
|
try {
|
|
104
106
|
if (seen.has(fullPath))
|
|
105
107
|
continue;
|
|
@@ -117,7 +119,10 @@ function rolloutFiles(limit = 150) {
|
|
|
117
119
|
if (dirExists(sessionsDir))
|
|
118
120
|
walk(sessionsDir);
|
|
119
121
|
}
|
|
120
|
-
return entries
|
|
122
|
+
return entries
|
|
123
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
124
|
+
.slice(0, limit)
|
|
125
|
+
.map((entry) => entry.path);
|
|
121
126
|
}
|
|
122
127
|
function codexSessions(cwd, limit = 3) {
|
|
123
128
|
const exactCwd = (0, node_path_1.resolve)(cwd);
|
|
@@ -182,7 +187,13 @@ function addExecutedCommandAssets(acc, command, cwd, timestamp, meta) {
|
|
|
182
187
|
function readCodexAssets(cwd, limit = 3) {
|
|
183
188
|
const exactCwd = (0, node_path_1.resolve)(cwd);
|
|
184
189
|
const files = codexSessions(exactCwd, limit);
|
|
185
|
-
const acc = {
|
|
190
|
+
const acc = {
|
|
191
|
+
urls: [],
|
|
192
|
+
paths: [],
|
|
193
|
+
commands: [],
|
|
194
|
+
plans: [],
|
|
195
|
+
hadSession: files.length > 0,
|
|
196
|
+
};
|
|
186
197
|
const skip = (line) => line.includes('"type":"function_call_output"') ||
|
|
187
198
|
line.includes('"type":"reasoning"') ||
|
|
188
199
|
line.includes('"type":"token_count"');
|
|
@@ -37,10 +37,7 @@ function extractToolCalls(event) {
|
|
|
37
37
|
const timestamp = new Date(event.timestamp);
|
|
38
38
|
const calls = [];
|
|
39
39
|
for (const block of content) {
|
|
40
|
-
if (block &&
|
|
41
|
-
typeof block === 'object' &&
|
|
42
|
-
'type' in block &&
|
|
43
|
-
block.type === 'tool_use') {
|
|
40
|
+
if (block && typeof block === 'object' && 'type' in block && block.type === 'tool_use') {
|
|
44
41
|
const toolBlock = block;
|
|
45
42
|
if (toolBlock.name) {
|
|
46
43
|
calls.push({
|
|
@@ -46,43 +46,163 @@ const HTML_COLORS = {
|
|
|
46
46
|
};
|
|
47
47
|
function getColors(format) {
|
|
48
48
|
switch (format) {
|
|
49
|
-
case 'blessed':
|
|
50
|
-
|
|
51
|
-
case '
|
|
49
|
+
case 'blessed':
|
|
50
|
+
return BLESSED_COLORS;
|
|
51
|
+
case 'ansi':
|
|
52
|
+
return ANSI_COLORS;
|
|
53
|
+
case 'html':
|
|
54
|
+
return HTML_COLORS;
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
const ERROR_KEYWORDS = new Set([
|
|
55
|
-
'error',
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
59
|
-
'
|
|
60
|
-
'
|
|
61
|
-
'
|
|
62
|
-
'
|
|
58
|
+
'error',
|
|
59
|
+
'errors',
|
|
60
|
+
'err',
|
|
61
|
+
'fail',
|
|
62
|
+
'failed',
|
|
63
|
+
'failure',
|
|
64
|
+
'failures',
|
|
65
|
+
'fatal',
|
|
66
|
+
'panic',
|
|
67
|
+
'crash',
|
|
68
|
+
'crashed',
|
|
69
|
+
'exception',
|
|
70
|
+
'exceptions',
|
|
71
|
+
'critical',
|
|
72
|
+
'alert',
|
|
73
|
+
'abort',
|
|
74
|
+
'aborted',
|
|
75
|
+
'denied',
|
|
76
|
+
'forbidden',
|
|
77
|
+
'rejected',
|
|
78
|
+
'invalid',
|
|
79
|
+
'illegal',
|
|
80
|
+
'corrupt',
|
|
81
|
+
'corrupted',
|
|
82
|
+
'broken',
|
|
83
|
+
'bug',
|
|
84
|
+
'segfault',
|
|
85
|
+
'timeout',
|
|
86
|
+
'timedout',
|
|
87
|
+
'timed-out',
|
|
88
|
+
'deadlock',
|
|
89
|
+
'overflow',
|
|
90
|
+
'underflow',
|
|
91
|
+
'null',
|
|
92
|
+
'undefined',
|
|
93
|
+
'nan',
|
|
94
|
+
'unhandled',
|
|
95
|
+
'uncaught',
|
|
96
|
+
'unreachable',
|
|
97
|
+
'missing',
|
|
98
|
+
'notfound',
|
|
99
|
+
'not-found',
|
|
100
|
+
'unavailable',
|
|
101
|
+
'disconnected',
|
|
102
|
+
'dropped',
|
|
63
103
|
]);
|
|
64
104
|
const SUCCESS_KEYWORDS = new Set([
|
|
65
|
-
'success',
|
|
66
|
-
'
|
|
67
|
-
'
|
|
68
|
-
'
|
|
69
|
-
'
|
|
105
|
+
'success',
|
|
106
|
+
'successful',
|
|
107
|
+
'succeeded',
|
|
108
|
+
'ok',
|
|
109
|
+
'done',
|
|
110
|
+
'complete',
|
|
111
|
+
'completed',
|
|
112
|
+
'passed',
|
|
113
|
+
'pass',
|
|
114
|
+
'resolved',
|
|
115
|
+
'fixed',
|
|
116
|
+
'ready',
|
|
117
|
+
'active',
|
|
118
|
+
'running',
|
|
119
|
+
'started',
|
|
120
|
+
'created',
|
|
121
|
+
'connected',
|
|
122
|
+
'established',
|
|
123
|
+
'accepted',
|
|
124
|
+
'approved',
|
|
125
|
+
'merged',
|
|
126
|
+
'deployed',
|
|
127
|
+
'installed',
|
|
128
|
+
'loaded',
|
|
129
|
+
'enabled',
|
|
130
|
+
'healthy',
|
|
131
|
+
'valid',
|
|
132
|
+
'verified',
|
|
70
133
|
]);
|
|
71
134
|
const WARNING_KEYWORDS = new Set([
|
|
72
|
-
'warn',
|
|
73
|
-
'
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
135
|
+
'warn',
|
|
136
|
+
'warning',
|
|
137
|
+
'warnings',
|
|
138
|
+
'caution',
|
|
139
|
+
'deprecated',
|
|
140
|
+
'slow',
|
|
141
|
+
'retry',
|
|
142
|
+
'retrying',
|
|
143
|
+
'retries',
|
|
144
|
+
'pending',
|
|
145
|
+
'waiting',
|
|
146
|
+
'queued',
|
|
147
|
+
'stale',
|
|
148
|
+
'expired',
|
|
149
|
+
'expiring',
|
|
150
|
+
'limited',
|
|
151
|
+
'throttled',
|
|
152
|
+
'throttling',
|
|
153
|
+
'degraded',
|
|
154
|
+
'unstable',
|
|
155
|
+
'flaky',
|
|
156
|
+
'skipped',
|
|
157
|
+
'skip',
|
|
158
|
+
'ignored',
|
|
159
|
+
'unknown',
|
|
160
|
+
'unrecognized',
|
|
77
161
|
]);
|
|
78
162
|
const ACTION_KEYWORDS = new Set([
|
|
79
|
-
'read',
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'
|
|
83
|
-
'
|
|
84
|
-
'
|
|
85
|
-
'
|
|
163
|
+
'read',
|
|
164
|
+
'write',
|
|
165
|
+
'edit',
|
|
166
|
+
'delete',
|
|
167
|
+
'create',
|
|
168
|
+
'update',
|
|
169
|
+
'insert',
|
|
170
|
+
'remove',
|
|
171
|
+
'add',
|
|
172
|
+
'modify',
|
|
173
|
+
'patch',
|
|
174
|
+
'merge',
|
|
175
|
+
'push',
|
|
176
|
+
'pull',
|
|
177
|
+
'fetch',
|
|
178
|
+
'send',
|
|
179
|
+
'receive',
|
|
180
|
+
'request',
|
|
181
|
+
'response',
|
|
182
|
+
'query',
|
|
183
|
+
'search',
|
|
184
|
+
'find',
|
|
185
|
+
'list',
|
|
186
|
+
'get',
|
|
187
|
+
'set',
|
|
188
|
+
'put',
|
|
189
|
+
'post',
|
|
190
|
+
'build',
|
|
191
|
+
'compile',
|
|
192
|
+
'test',
|
|
193
|
+
'deploy',
|
|
194
|
+
'install',
|
|
195
|
+
'run',
|
|
196
|
+
'execute',
|
|
197
|
+
'start',
|
|
198
|
+
'stop',
|
|
199
|
+
'restart',
|
|
200
|
+
'init',
|
|
201
|
+
'initialize',
|
|
202
|
+
'configure',
|
|
203
|
+
'spawn',
|
|
204
|
+
'call',
|
|
205
|
+
'invoke',
|
|
86
206
|
]);
|
|
87
207
|
const HTTP_METHODS = new Map([
|
|
88
208
|
['GET', 'green'],
|
|
@@ -18,11 +18,7 @@ exports.shouldMergeWithPrevious = shouldMergeWithPrevious;
|
|
|
18
18
|
exports.classifyNoise = classifyNoise;
|
|
19
19
|
// ── Hard Noise Detection ──
|
|
20
20
|
/** Hard noise types that should be dropped entirely from display. */
|
|
21
|
-
const HARD_NOISE_EVENT_TYPES = new Set([
|
|
22
|
-
'file-history-snapshot',
|
|
23
|
-
'queue-operation',
|
|
24
|
-
'progress',
|
|
25
|
-
]);
|
|
21
|
+
const HARD_NOISE_EVENT_TYPES = new Set(['file-history-snapshot', 'queue-operation', 'progress']);
|
|
26
22
|
const SYNTHETIC_MODEL_PREFIX = '<synthetic>';
|
|
27
23
|
/**
|
|
28
24
|
* Determines if an event is "hard noise" and should be dropped entirely.
|
|
@@ -70,11 +66,7 @@ const COMMAND_CAVEAT_PATTERNS = [
|
|
|
70
66
|
/IMPORTANT:.*?(?:never|always|must|should)/i,
|
|
71
67
|
/Note:.*?(?:do not|don't|avoid)/i,
|
|
72
68
|
];
|
|
73
|
-
const INTERRUPTION_MARKERS = [
|
|
74
|
-
'interrupted by user',
|
|
75
|
-
'operation cancelled',
|
|
76
|
-
'aborted',
|
|
77
|
-
];
|
|
69
|
+
const INTERRUPTION_MARKERS = ['interrupted by user', 'operation cancelled', 'aborted'];
|
|
78
70
|
/**
|
|
79
71
|
* Checks for soft noise that can be hidden but not dropped.
|
|
80
72
|
*
|
|
@@ -222,5 +214,5 @@ function extractContent(event) {
|
|
|
222
214
|
function hasToolUseBlocks(content) {
|
|
223
215
|
if (!Array.isArray(content))
|
|
224
216
|
return false;
|
|
225
|
-
return content.some(b => b.type === 'tool_use');
|
|
217
|
+
return content.some((b) => b.type === 'tool_use');
|
|
226
218
|
}
|
|
@@ -51,7 +51,7 @@ function formatSessionText(metrics, options = {}) {
|
|
|
51
51
|
// Tool stats
|
|
52
52
|
if (metrics.toolStats.length > 0) {
|
|
53
53
|
lines.push('Tools:');
|
|
54
|
-
const sorted = [...metrics.toolStats].sort((a, b) =>
|
|
54
|
+
const sorted = [...metrics.toolStats].sort((a, b) => b.successCount + b.failureCount - (a.successCount + a.failureCount));
|
|
55
55
|
for (const t of sorted) {
|
|
56
56
|
const total = t.successCount + t.failureCount;
|
|
57
57
|
const failStr = t.failureCount > 0 ? ` (${t.failureCount} failed)` : '';
|
|
@@ -77,7 +77,7 @@ function formatSessionText(metrics, options = {}) {
|
|
|
77
77
|
lines.push(hr);
|
|
78
78
|
const filteredEvents = expand
|
|
79
79
|
? metrics.timeline
|
|
80
|
-
: metrics.timeline.filter(e => e.noiseLevel !== 'noise');
|
|
80
|
+
: metrics.timeline.filter((e) => e.noiseLevel !== 'noise');
|
|
81
81
|
if (filteredEvents.length === 0) {
|
|
82
82
|
lines.push(' (no events)');
|
|
83
83
|
}
|
|
@@ -150,7 +150,7 @@ function formatSessionMarkdown(metrics, options = {}) {
|
|
|
150
150
|
lines.push('');
|
|
151
151
|
lines.push(`| Tool | Total | Failed | Avg Duration |`);
|
|
152
152
|
lines.push(`|------|-------|--------|--------------|`);
|
|
153
|
-
const sorted = [...metrics.toolStats].sort((a, b) =>
|
|
153
|
+
const sorted = [...metrics.toolStats].sort((a, b) => b.successCount + b.failureCount - (a.successCount + a.failureCount));
|
|
154
154
|
for (const t of sorted) {
|
|
155
155
|
const total = t.successCount + t.failureCount;
|
|
156
156
|
const avgMs = t.completedCount > 0 ? Math.round(t.totalDuration / t.completedCount) : 0;
|
|
@@ -190,7 +190,7 @@ function formatSessionMarkdown(metrics, options = {}) {
|
|
|
190
190
|
lines.push('');
|
|
191
191
|
const filteredEvents = expand
|
|
192
192
|
? metrics.timeline
|
|
193
|
-
: metrics.timeline.filter(e => e.noiseLevel !== 'noise');
|
|
193
|
+
: metrics.timeline.filter((e) => e.noiseLevel !== 'noise');
|
|
194
194
|
if (filteredEvents.length === 0) {
|
|
195
195
|
lines.push('_(no events)_');
|
|
196
196
|
}
|
|
@@ -279,15 +279,24 @@ function formatDuration(start, end) {
|
|
|
279
279
|
/** Map TimelineEvent.type to a display icon. */
|
|
280
280
|
function getTimelineEventIcon(type) {
|
|
281
281
|
switch (type) {
|
|
282
|
-
case 'user_prompt':
|
|
283
|
-
|
|
284
|
-
case '
|
|
285
|
-
|
|
286
|
-
case '
|
|
287
|
-
|
|
288
|
-
case '
|
|
289
|
-
|
|
290
|
-
|
|
282
|
+
case 'user_prompt':
|
|
283
|
+
return '\u25b6'; // right-pointing triangle
|
|
284
|
+
case 'assistant_response':
|
|
285
|
+
return '\u2726'; // four-pointed star
|
|
286
|
+
case 'tool_call':
|
|
287
|
+
return '\u2699'; // gear
|
|
288
|
+
case 'tool_result':
|
|
289
|
+
return '\u2190'; // left arrow
|
|
290
|
+
case 'compaction':
|
|
291
|
+
return '\u21bb'; // clockwise arrow
|
|
292
|
+
case 'error':
|
|
293
|
+
return '\u2718'; // heavy ballot X
|
|
294
|
+
case 'session_start':
|
|
295
|
+
return '\u25cf'; // black circle
|
|
296
|
+
case 'session_end':
|
|
297
|
+
return '\u25cb'; // white circle
|
|
298
|
+
default:
|
|
299
|
+
return ' ';
|
|
291
300
|
}
|
|
292
301
|
}
|
|
293
302
|
function truncateToWidth(text, maxLen) {
|
|
@@ -217,9 +217,7 @@ function genericFallback(input) {
|
|
|
217
217
|
*/
|
|
218
218
|
function formatToolSummary(toolName, input) {
|
|
219
219
|
// Strip MCP prefixes: "mcp__server__tool" → use "tool" for matching
|
|
220
|
-
const baseName = toolName.includes('__')
|
|
221
|
-
? toolName.split('__').pop() ?? toolName
|
|
222
|
-
: toolName;
|
|
220
|
+
const baseName = toolName.includes('__') ? (toolName.split('__').pop() ?? toolName) : toolName;
|
|
223
221
|
const formatter = TOOL_FORMATTERS[toolName] ?? TOOL_FORMATTERS[baseName];
|
|
224
222
|
if (formatter) {
|
|
225
223
|
const result = formatter(input);
|