sidekick-shared 0.13.2

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.
Files changed (126) hide show
  1. package/README.md +92 -0
  2. package/dist/aggregation/EventAggregator.d.ts +172 -0
  3. package/dist/aggregation/EventAggregator.js +1443 -0
  4. package/dist/aggregation/FrequencyTracker.d.ts +42 -0
  5. package/dist/aggregation/FrequencyTracker.js +73 -0
  6. package/dist/aggregation/HeatmapTracker.d.ts +40 -0
  7. package/dist/aggregation/HeatmapTracker.js +93 -0
  8. package/dist/aggregation/PatternExtractor.d.ts +51 -0
  9. package/dist/aggregation/PatternExtractor.js +171 -0
  10. package/dist/aggregation/snapshot.d.ts +64 -0
  11. package/dist/aggregation/snapshot.js +151 -0
  12. package/dist/aggregation/types.d.ts +121 -0
  13. package/dist/aggregation/types.js +6 -0
  14. package/dist/context/composer.d.ts +31 -0
  15. package/dist/context/composer.js +72 -0
  16. package/dist/credentials.d.ts +23 -0
  17. package/dist/credentials.js +96 -0
  18. package/dist/formatters/eventHighlighter.d.ts +30 -0
  19. package/dist/formatters/eventHighlighter.js +217 -0
  20. package/dist/formatters/noiseClassifier.d.ts +73 -0
  21. package/dist/formatters/noiseClassifier.js +226 -0
  22. package/dist/formatters/sessionDump.d.ts +38 -0
  23. package/dist/formatters/sessionDump.js +313 -0
  24. package/dist/formatters/toolSummary.d.ts +23 -0
  25. package/dist/formatters/toolSummary.js +230 -0
  26. package/dist/index.d.ts +85 -0
  27. package/dist/index.js +182 -0
  28. package/dist/parsers/changelogParser.d.ts +25 -0
  29. package/dist/parsers/changelogParser.js +74 -0
  30. package/dist/parsers/codexParser.d.ts +76 -0
  31. package/dist/parsers/codexParser.js +653 -0
  32. package/dist/parsers/debugLogParser.d.ts +63 -0
  33. package/dist/parsers/debugLogParser.js +164 -0
  34. package/dist/parsers/jsonl.d.ts +45 -0
  35. package/dist/parsers/jsonl.js +57 -0
  36. package/dist/parsers/openCodeParser.d.ts +64 -0
  37. package/dist/parsers/openCodeParser.js +581 -0
  38. package/dist/parsers/planExtractor.d.ts +63 -0
  39. package/dist/parsers/planExtractor.js +330 -0
  40. package/dist/parsers/sessionActivityDetector.d.ts +31 -0
  41. package/dist/parsers/sessionActivityDetector.js +184 -0
  42. package/dist/parsers/sessionPathResolver.d.ts +230 -0
  43. package/dist/parsers/sessionPathResolver.js +753 -0
  44. package/dist/parsers/subagentScanner.d.ts +43 -0
  45. package/dist/parsers/subagentScanner.js +366 -0
  46. package/dist/parsers/subagentTraceParser.d.ts +58 -0
  47. package/dist/parsers/subagentTraceParser.js +346 -0
  48. package/dist/paths.d.ts +38 -0
  49. package/dist/paths.js +107 -0
  50. package/dist/phrases.d.ts +52 -0
  51. package/dist/phrases.js +1333 -0
  52. package/dist/providers/claudeCode.d.ts +48 -0
  53. package/dist/providers/claudeCode.js +465 -0
  54. package/dist/providers/codex.d.ts +57 -0
  55. package/dist/providers/codex.js +944 -0
  56. package/dist/providers/codexDatabase.d.ts +37 -0
  57. package/dist/providers/codexDatabase.js +148 -0
  58. package/dist/providers/detect.d.ts +16 -0
  59. package/dist/providers/detect.js +162 -0
  60. package/dist/providers/openCode.d.ts +70 -0
  61. package/dist/providers/openCode.js +1524 -0
  62. package/dist/providers/openCodeDatabase.d.ts +87 -0
  63. package/dist/providers/openCodeDatabase.js +232 -0
  64. package/dist/providers/types.d.ts +154 -0
  65. package/dist/providers/types.js +12 -0
  66. package/dist/quota.d.ts +34 -0
  67. package/dist/quota.js +80 -0
  68. package/dist/readers/decisions.d.ts +10 -0
  69. package/dist/readers/decisions.js +27 -0
  70. package/dist/readers/handoff.d.ts +4 -0
  71. package/dist/readers/handoff.js +51 -0
  72. package/dist/readers/helpers.d.ts +7 -0
  73. package/dist/readers/helpers.js +52 -0
  74. package/dist/readers/history.d.ts +5 -0
  75. package/dist/readers/history.js +12 -0
  76. package/dist/readers/notes.d.ts +10 -0
  77. package/dist/readers/notes.js +46 -0
  78. package/dist/readers/plans.d.ts +35 -0
  79. package/dist/readers/plans.js +247 -0
  80. package/dist/readers/tasks.d.ts +8 -0
  81. package/dist/readers/tasks.js +22 -0
  82. package/dist/report/htmlHelpers.d.ts +18 -0
  83. package/dist/report/htmlHelpers.js +166 -0
  84. package/dist/report/htmlReportGenerator.d.ts +11 -0
  85. package/dist/report/htmlReportGenerator.js +650 -0
  86. package/dist/report/index.d.ts +8 -0
  87. package/dist/report/index.js +16 -0
  88. package/dist/report/logo.d.ts +2 -0
  89. package/dist/report/logo.js +5 -0
  90. package/dist/report/openBrowser.d.ts +5 -0
  91. package/dist/report/openBrowser.js +22 -0
  92. package/dist/report/transcriptParser.d.ts +12 -0
  93. package/dist/report/transcriptParser.js +177 -0
  94. package/dist/report/types.d.ts +43 -0
  95. package/dist/report/types.js +5 -0
  96. package/dist/search/advancedFilter.d.ts +62 -0
  97. package/dist/search/advancedFilter.js +201 -0
  98. package/dist/search/sessionSearch.d.ts +16 -0
  99. package/dist/search/sessionSearch.js +93 -0
  100. package/dist/types/codex.d.ts +276 -0
  101. package/dist/types/codex.js +14 -0
  102. package/dist/types/decisionLog.d.ts +23 -0
  103. package/dist/types/decisionLog.js +8 -0
  104. package/dist/types/historicalData.d.ts +74 -0
  105. package/dist/types/historicalData.js +17 -0
  106. package/dist/types/knowledgeNote.d.ts +40 -0
  107. package/dist/types/knowledgeNote.js +18 -0
  108. package/dist/types/opencode.d.ts +268 -0
  109. package/dist/types/opencode.js +13 -0
  110. package/dist/types/plan.d.ts +49 -0
  111. package/dist/types/plan.js +10 -0
  112. package/dist/types/sessionEvent.d.ts +562 -0
  113. package/dist/types/sessionEvent.js +11 -0
  114. package/dist/types/taskPersistence.d.ts +33 -0
  115. package/dist/types/taskPersistence.js +16 -0
  116. package/dist/watchers/eventBridge.d.ts +19 -0
  117. package/dist/watchers/eventBridge.js +162 -0
  118. package/dist/watchers/factory.d.ts +15 -0
  119. package/dist/watchers/factory.js +85 -0
  120. package/dist/watchers/jsonlWatcher.d.ts +30 -0
  121. package/dist/watchers/jsonlWatcher.js +444 -0
  122. package/dist/watchers/sqliteWatcher.d.ts +30 -0
  123. package/dist/watchers/sqliteWatcher.js +278 -0
  124. package/dist/watchers/types.d.ts +60 -0
  125. package/dist/watchers/types.js +5 -0
  126. package/package.json +31 -0
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Aggregated metrics output types for EventAggregator.
3
+ * Re-exports canonical types from sessionEvent and defines aggregation-specific shapes.
4
+ */
5
+ export type { SessionEvent, ClaudeSessionEvent, MessageUsage, TokenUsage, ToolCall, ToolAnalytics, TimelineEvent, PendingToolCall, TrackedTask, TaskState, TaskStatus, SubagentStats, PlanState, PlanStep, CompactionEvent, TruncationEvent, ContextAttribution, TurnAttribution, ContextSizePoint, PendingUserRequest, ResponseLatency, LatencyStats, SessionStats, PermissionMode, PermissionModeChange, } from '../types/sessionEvent';
6
+ import type { ContextAttribution, ContextSizePoint, CompactionEvent, TruncationEvent, ToolAnalytics, TimelineEvent, TaskState, PlanState, LatencyStats, PermissionMode, PermissionModeChange } from '../types/sessionEvent';
7
+ /** Configuration options for EventAggregator. */
8
+ export interface EventAggregatorOptions {
9
+ /** Maximum timeline events to retain (default 200). */
10
+ timelineCap?: number;
11
+ /** Maximum latency records to retain (default 100). */
12
+ latencyCap?: number;
13
+ /** Burn rate sliding window in ms (default 5 * 60_000). */
14
+ burnWindowMs?: number;
15
+ /** Burn rate sample interval in ms (default 10_000). */
16
+ burnSampleMs?: number;
17
+ /** Provider-specific context size computation (default: input + cacheWrite + cacheRead). */
18
+ computeContextSize?: (usage: {
19
+ inputTokens: number;
20
+ outputTokens: number;
21
+ cacheWriteTokens: number;
22
+ cacheReadTokens: number;
23
+ reasoningTokens?: number;
24
+ }) => number;
25
+ /** Provider ID for timeline events. */
26
+ providerId?: 'claude-code' | 'opencode' | 'codex';
27
+ /** Read a plan file from disk (fallback when Edit tool is used instead of Write). */
28
+ readPlanFile?: (path: string) => string | null;
29
+ }
30
+ /** Token accumulation totals (aggregation-specific, includes reportedCost). */
31
+ export interface AggregatedTokens {
32
+ inputTokens: number;
33
+ outputTokens: number;
34
+ cacheWriteTokens: number;
35
+ cacheReadTokens: number;
36
+ reportedCost: number;
37
+ }
38
+ /** Per-model usage breakdown. */
39
+ export interface ModelUsageStats {
40
+ model: string;
41
+ calls: number;
42
+ tokens: number;
43
+ inputTokens: number;
44
+ outputTokens: number;
45
+ cacheWriteTokens: number;
46
+ cacheReadTokens: number;
47
+ cost: number;
48
+ }
49
+ /** Burn rate info. */
50
+ export interface BurnRateInfo {
51
+ /** Current tokens per minute. */
52
+ tokensPerMinute: number;
53
+ /** Sampled burn rate points for charting. */
54
+ points: number[];
55
+ /** Number of samples in the window. */
56
+ sampleCount: number;
57
+ }
58
+ /** Subagent lifecycle info (for tracking spawn/completion from tool events). */
59
+ export interface SubagentLifecycle {
60
+ id: string;
61
+ description: string;
62
+ subagentType: string;
63
+ spawnTime: string;
64
+ completionTime?: string;
65
+ status: 'running' | 'completed';
66
+ durationMs?: number;
67
+ }
68
+ /** Frequency entry for tool/word frequency tracking. */
69
+ export interface FrequencyMetric {
70
+ name: string;
71
+ count: number;
72
+ }
73
+ /** Pattern cluster from event summary clustering. */
74
+ export interface PatternMetric {
75
+ template: string;
76
+ count: number;
77
+ examples: string[];
78
+ }
79
+ /** Heatmap bucket for activity visualization. */
80
+ export interface HeatmapBucketMetric {
81
+ timestamp: string;
82
+ count: number;
83
+ }
84
+ /** Full aggregated metrics snapshot. */
85
+ export interface AggregatedMetrics {
86
+ sessionStartTime: string | null;
87
+ lastEventTime: string | null;
88
+ messageCount: number;
89
+ eventCount: number;
90
+ currentModel: string | null;
91
+ providerId: string | null;
92
+ tokens: AggregatedTokens;
93
+ modelStats: ModelUsageStats[];
94
+ currentContextSize: number;
95
+ contextAttribution: ContextAttribution;
96
+ compactionCount: number;
97
+ compactionEvents: CompactionEvent[];
98
+ truncationCount: number;
99
+ truncationEvents: TruncationEvent[];
100
+ toolStats: ToolAnalytics[];
101
+ burnRate: BurnRateInfo;
102
+ taskState: TaskState;
103
+ subagents: SubagentLifecycle[];
104
+ plan: PlanState | null;
105
+ /** Current permission mode (default | acceptEdits | bypassPermissions | plan) */
106
+ permissionMode: PermissionMode | null;
107
+ /** History of permission mode changes */
108
+ permissionModeHistory: PermissionModeChange[];
109
+ /** Context size over time for visualization */
110
+ contextTimeline: ContextSizePoint[];
111
+ timeline: TimelineEvent[];
112
+ latencyStats: LatencyStats | null;
113
+ /** Top tool names by usage frequency. */
114
+ toolFrequency: FrequencyMetric[];
115
+ /** Top words from event summaries. */
116
+ wordFrequency: FrequencyMetric[];
117
+ /** Detected event patterns from summary clustering. */
118
+ patterns: PatternMetric[];
119
+ /** Rolling activity heatmap buckets (minute resolution). */
120
+ heatmapBuckets: HeatmapBucketMetric[];
121
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Aggregated metrics output types for EventAggregator.
4
+ * Re-exports canonical types from sessionEvent and defines aggregation-specific shapes.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Context composer — aggregates all readers with fidelity tiers.
3
+ */
4
+ import type { PersistedTask } from '../types/taskPersistence';
5
+ import type { DecisionEntry } from '../types/decisionLog';
6
+ import type { KnowledgeNote } from '../types/knowledgeNote';
7
+ import type { TokenTotals } from '../types/historicalData';
8
+ import type { SessionProvider, SessionFileStats, ProviderId } from '../providers/types';
9
+ export type Fidelity = 'full' | 'compact' | 'brief';
10
+ export interface ContextResult {
11
+ provider: ProviderId;
12
+ tasks: {
13
+ items: PersistedTask[];
14
+ total: number;
15
+ };
16
+ decisions: {
17
+ items: DecisionEntry[];
18
+ total: number;
19
+ };
20
+ notes: {
21
+ items: KnowledgeNote[];
22
+ total: number;
23
+ };
24
+ handoff: string | null;
25
+ stats: {
26
+ tokens: TokenTotals;
27
+ cost: number;
28
+ } | null;
29
+ sessionSummaries: SessionFileStats[];
30
+ }
31
+ export declare function composeContext(slug: string, fidelity: Fidelity, provider: SessionProvider, workspacePath?: string): Promise<ContextResult>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ /**
3
+ * Context composer — aggregates all readers with fidelity tiers.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.composeContext = composeContext;
7
+ const tasks_1 = require("../readers/tasks");
8
+ const decisions_1 = require("../readers/decisions");
9
+ const notes_1 = require("../readers/notes");
10
+ const history_1 = require("../readers/history");
11
+ const handoff_1 = require("../readers/handoff");
12
+ async function composeContext(slug, fidelity, provider, workspacePath) {
13
+ // Fetch all data in parallel
14
+ const [allTasks, allDecisions, allNotes, history, handoff] = await Promise.all([
15
+ (0, tasks_1.readTasks)(slug),
16
+ (0, decisions_1.readDecisions)(slug),
17
+ (0, notes_1.readNotes)(slug),
18
+ (0, history_1.readHistory)(),
19
+ (0, handoff_1.readLatestHandoff)(slug),
20
+ ]);
21
+ // Get session summaries
22
+ const sessionFiles = workspacePath ? provider.findAllSessions(workspacePath) : [];
23
+ const sessionSummaries = [];
24
+ // Read stats for session files based on fidelity
25
+ const FIDELITY_SESSION_LIMIT = { full: 5, compact: 2, brief: 1 };
26
+ const sessionLimit = FIDELITY_SESSION_LIMIT[fidelity];
27
+ const sessionPaths = sessionFiles.slice(0, sessionLimit);
28
+ for (const sp of sessionPaths) {
29
+ try {
30
+ sessionSummaries.push(provider.readSessionStats(sp));
31
+ }
32
+ catch { /* skip */ }
33
+ }
34
+ // Apply fidelity filtering
35
+ let tasks;
36
+ let decisions;
37
+ let notes;
38
+ let stats = null;
39
+ switch (fidelity) {
40
+ case 'full':
41
+ tasks = allTasks;
42
+ decisions = allDecisions;
43
+ notes = allNotes.filter(n => n.status === 'active' || n.status === 'needs_review');
44
+ if (history) {
45
+ stats = { tokens: { ...history.allTime.tokens }, cost: history.allTime.totalCost };
46
+ }
47
+ break;
48
+ case 'compact':
49
+ tasks = allTasks.filter(t => t.status === 'pending');
50
+ decisions = allDecisions.slice(0, 10);
51
+ notes = allNotes.filter(n => n.status === 'active');
52
+ if (history) {
53
+ stats = { tokens: { ...history.allTime.tokens }, cost: history.allTime.totalCost };
54
+ }
55
+ break;
56
+ case 'brief':
57
+ tasks = allTasks.filter(t => t.status === 'pending').slice(0, 3);
58
+ decisions = []; // Count only
59
+ notes = allNotes.filter(n => n.importance === 'critical');
60
+ stats = null;
61
+ break;
62
+ }
63
+ return {
64
+ provider: provider.id,
65
+ tasks: { items: tasks, total: allTasks.length },
66
+ decisions: { items: decisions, total: allDecisions.length },
67
+ notes: { items: notes, total: allNotes.length },
68
+ handoff,
69
+ stats,
70
+ sessionSummaries,
71
+ };
72
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Read Claude Max OAuth credentials from disk.
3
+ *
4
+ * Shared by sidekick-cli, sidekick-vscode, and any external consumer.
5
+ */
6
+ export interface ClaudeMaxCredentials {
7
+ accessToken: string;
8
+ refreshToken?: string;
9
+ expiresAt?: number;
10
+ scopes?: string[];
11
+ subscriptionType?: string;
12
+ }
13
+ /**
14
+ * Reads Claude Max OAuth credentials from `~/.claude/.credentials.json`.
15
+ *
16
+ * Returns `null` if the file does not exist, the token is missing, or the
17
+ * token is expired. Never throws.
18
+ */
19
+ export declare function readClaudeMaxCredentials(): Promise<ClaudeMaxCredentials | null>;
20
+ /**
21
+ * Synchronous convenience — returns just the access token or `null`.
22
+ */
23
+ export declare function readClaudeMaxAccessTokenSync(): string | null;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /**
3
+ * Read Claude Max OAuth credentials from disk.
4
+ *
5
+ * Shared by sidekick-cli, sidekick-vscode, and any external consumer.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.readClaudeMaxCredentials = readClaudeMaxCredentials;
42
+ exports.readClaudeMaxAccessTokenSync = readClaudeMaxAccessTokenSync;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const os = __importStar(require("os"));
46
+ /**
47
+ * Reads Claude Max OAuth credentials from `~/.claude/.credentials.json`.
48
+ *
49
+ * Returns `null` if the file does not exist, the token is missing, or the
50
+ * token is expired. Never throws.
51
+ */
52
+ async function readClaudeMaxCredentials() {
53
+ const credPath = path.join(os.homedir(), '.claude', '.credentials.json');
54
+ try {
55
+ if (!fs.existsSync(credPath))
56
+ return null;
57
+ const content = await fs.promises.readFile(credPath, 'utf8');
58
+ const parsed = JSON.parse(content);
59
+ const oauth = parsed?.claudeAiOauth;
60
+ if (!oauth?.accessToken)
61
+ return null;
62
+ if (oauth.expiresAt && Date.now() > oauth.expiresAt)
63
+ return null;
64
+ return {
65
+ accessToken: oauth.accessToken,
66
+ refreshToken: oauth.refreshToken,
67
+ expiresAt: oauth.expiresAt,
68
+ scopes: oauth.scopes,
69
+ subscriptionType: oauth.subscriptionType,
70
+ };
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ /**
77
+ * Synchronous convenience — returns just the access token or `null`.
78
+ */
79
+ function readClaudeMaxAccessTokenSync() {
80
+ const credPath = path.join(os.homedir(), '.claude', '.credentials.json');
81
+ try {
82
+ if (!fs.existsSync(credPath))
83
+ return null;
84
+ const content = fs.readFileSync(credPath, 'utf8');
85
+ const parsed = JSON.parse(content);
86
+ const oauth = parsed?.claudeAiOauth;
87
+ if (!oauth?.accessToken)
88
+ return null;
89
+ if (oauth.expiresAt && Date.now() > oauth.expiresAt)
90
+ return null;
91
+ return oauth.accessToken;
92
+ }
93
+ catch {
94
+ return null;
95
+ }
96
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Keyword-based semantic syntax highlighting for session event text.
3
+ *
4
+ * Applies cascading word-by-word colorization based on keyword categories:
5
+ * errors=red, success=green, warnings=yellow, actions=cyan, numbers=blue,
6
+ * URLs/paths=magenta, HTTP status codes and methods colored by semantics.
7
+ *
8
+ * Supports blessed (CLI), ANSI (terminal), and HTML (VS Code webview) output formats.
9
+ *
10
+ * @module formatters/eventHighlighter
11
+ */
12
+ export type HighlightFormat = 'blessed' | 'ansi' | 'html';
13
+ /**
14
+ * Apply keyword-based semantic highlighting to text.
15
+ *
16
+ * Splits text on whitespace and colors each word based on keyword category.
17
+ * Words not matching any category pass through uncolored.
18
+ *
19
+ * @param text - The text to highlight.
20
+ * @param format - Output format: 'blessed' (CLI), 'ansi' (terminal), or 'html' (webview).
21
+ * @returns The highlighted text with format-appropriate tags.
22
+ */
23
+ export declare function highlight(text: string, format: HighlightFormat): string;
24
+ /**
25
+ * CSS class definitions for HTML highlight format.
26
+ * Include this in webview stylesheets.
27
+ */
28
+ export declare const HIGHLIGHT_CSS = "\n.sk-hl-error { color: var(--vscode-charts-red, #f44747); }\n.sk-hl-success { color: var(--vscode-charts-green, #89d185); }\n.sk-hl-warning { color: var(--vscode-charts-yellow, #cca700); }\n.sk-hl-action { color: var(--vscode-charts-blue, #4fc1ff); }\n.sk-hl-number { color: var(--vscode-charts-purple, #b180d7); }\n.sk-hl-path { color: var(--vscode-charts-orange, #cca700); }\n.sk-hl-muted { opacity: 0.6; }\n.sk-hl-default { }\n.sk-search-match { background-color: var(--vscode-editor-findMatchHighlightBackground, rgba(234, 92, 0, 0.33)); }\n";
29
+ /** Clear the highlight cache (for testing or memory pressure). */
30
+ export declare function clearHighlightCache(): void;
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ /**
3
+ * Keyword-based semantic syntax highlighting for session event text.
4
+ *
5
+ * Applies cascading word-by-word colorization based on keyword categories:
6
+ * errors=red, success=green, warnings=yellow, actions=cyan, numbers=blue,
7
+ * URLs/paths=magenta, HTTP status codes and methods colored by semantics.
8
+ *
9
+ * Supports blessed (CLI), ANSI (terminal), and HTML (VS Code webview) output formats.
10
+ *
11
+ * @module formatters/eventHighlighter
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.HIGHLIGHT_CSS = void 0;
15
+ exports.highlight = highlight;
16
+ exports.clearHighlightCache = clearHighlightCache;
17
+ const BLESSED_COLORS = {
18
+ red: ['{red-fg}', '{/red-fg}'],
19
+ green: ['{green-fg}', '{/green-fg}'],
20
+ yellow: ['{yellow-fg}', '{/yellow-fg}'],
21
+ cyan: ['{cyan-fg}', '{/cyan-fg}'],
22
+ blue: ['{blue-fg}', '{/blue-fg}'],
23
+ magenta: ['{magenta-fg}', '{/magenta-fg}'],
24
+ grey: ['{grey-fg}', '{/grey-fg}'],
25
+ white: ['{white-fg}', '{/white-fg}'],
26
+ };
27
+ const ANSI_COLORS = {
28
+ red: ['\x1b[31m', '\x1b[0m'],
29
+ green: ['\x1b[32m', '\x1b[0m'],
30
+ yellow: ['\x1b[33m', '\x1b[0m'],
31
+ cyan: ['\x1b[36m', '\x1b[0m'],
32
+ blue: ['\x1b[34m', '\x1b[0m'],
33
+ magenta: ['\x1b[35m', '\x1b[0m'],
34
+ grey: ['\x1b[90m', '\x1b[0m'],
35
+ white: ['\x1b[37m', '\x1b[0m'],
36
+ };
37
+ const HTML_COLORS = {
38
+ red: ['<span class="sk-hl-error">', '</span>'],
39
+ green: ['<span class="sk-hl-success">', '</span>'],
40
+ yellow: ['<span class="sk-hl-warning">', '</span>'],
41
+ cyan: ['<span class="sk-hl-action">', '</span>'],
42
+ blue: ['<span class="sk-hl-number">', '</span>'],
43
+ magenta: ['<span class="sk-hl-path">', '</span>'],
44
+ grey: ['<span class="sk-hl-muted">', '</span>'],
45
+ white: ['<span class="sk-hl-default">', '</span>'],
46
+ };
47
+ function getColors(format) {
48
+ switch (format) {
49
+ case 'blessed': return BLESSED_COLORS;
50
+ case 'ansi': return ANSI_COLORS;
51
+ case 'html': return HTML_COLORS;
52
+ }
53
+ }
54
+ const ERROR_KEYWORDS = new Set([
55
+ 'error', 'errors', 'err', 'fail', 'failed', 'failure', 'failures',
56
+ 'fatal', 'panic', 'crash', 'crashed', 'exception', 'exceptions',
57
+ 'critical', 'alert', 'abort', 'aborted', 'denied', 'forbidden',
58
+ 'rejected', 'invalid', 'illegal', 'corrupt', 'corrupted',
59
+ 'broken', 'bug', 'segfault', 'timeout', 'timedout', 'timed-out',
60
+ 'deadlock', 'overflow', 'underflow', 'null', 'undefined', 'nan',
61
+ 'unhandled', 'uncaught', 'unreachable', 'missing', 'notfound',
62
+ 'not-found', 'unavailable', 'disconnected', 'dropped',
63
+ ]);
64
+ const SUCCESS_KEYWORDS = new Set([
65
+ 'success', 'successful', 'succeeded', 'ok', 'done', 'complete',
66
+ 'completed', 'passed', 'pass', 'resolved', 'fixed', 'ready',
67
+ 'active', 'running', 'started', 'created', 'connected',
68
+ 'established', 'accepted', 'approved', 'merged', 'deployed',
69
+ 'installed', 'loaded', 'enabled', 'healthy', 'valid', 'verified',
70
+ ]);
71
+ const WARNING_KEYWORDS = new Set([
72
+ 'warn', 'warning', 'warnings', 'caution', 'deprecated',
73
+ 'slow', 'retry', 'retrying', 'retries', 'pending', 'waiting',
74
+ 'queued', 'stale', 'expired', 'expiring', 'limited',
75
+ 'throttled', 'throttling', 'degraded', 'unstable', 'flaky',
76
+ 'skipped', 'skip', 'ignored', 'unknown', 'unrecognized',
77
+ ]);
78
+ const ACTION_KEYWORDS = new Set([
79
+ 'read', 'write', 'edit', 'delete', 'create', 'update', 'insert',
80
+ 'remove', 'add', 'modify', 'patch', 'merge', 'push', 'pull',
81
+ 'fetch', 'send', 'receive', 'request', 'response', 'query',
82
+ 'search', 'find', 'list', 'get', 'set', 'put', 'post',
83
+ 'build', 'compile', 'test', 'deploy', 'install', 'run',
84
+ 'execute', 'start', 'stop', 'restart', 'init', 'initialize',
85
+ 'configure', 'spawn', 'call', 'invoke',
86
+ ]);
87
+ const HTTP_METHODS = new Map([
88
+ ['GET', 'green'],
89
+ ['HEAD', 'green'],
90
+ ['OPTIONS', 'green'],
91
+ ['POST', 'yellow'],
92
+ ['PUT', 'yellow'],
93
+ ['PATCH', 'yellow'],
94
+ ['DELETE', 'red'],
95
+ ]);
96
+ // ── Patterns ──
97
+ const NUMBER_PATTERN = /^-?\d+(\.\d+)?(%|ms|s|m|h|d|k|K|M|G|T|B)?$/;
98
+ const PATH_PATTERN = /^[.~]?\/[\w./-]+|^[\w-]+\/[\w./-]+|^[\w-]+\.[\w.]+$/;
99
+ const URL_PATTERN = /^https?:\/\//;
100
+ /**
101
+ * Classify an HTTP status code by color.
102
+ * Returns null if the word is not a status code.
103
+ */
104
+ function httpStatusColor(word) {
105
+ if (!/^\d{3}$/.test(word))
106
+ return null;
107
+ const code = parseInt(word, 10);
108
+ if (code >= 200 && code < 300)
109
+ return 'green';
110
+ if (code >= 300 && code < 400)
111
+ return 'yellow';
112
+ if (code >= 400)
113
+ return 'red';
114
+ return null;
115
+ }
116
+ /**
117
+ * Determine the color for a single word based on keyword categories.
118
+ * Priority: errors > warnings > success > HTTP > actions > paths > numbers
119
+ */
120
+ function classifyWord(word) {
121
+ const lower = word.toLowerCase();
122
+ // Strip common punctuation for keyword matching
123
+ const stripped = lower.replace(/[.,;:!?()[\]{}'"]+$/g, '').replace(/^['"([\]{}]+/, '');
124
+ if (ERROR_KEYWORDS.has(stripped))
125
+ return 'red';
126
+ if (WARNING_KEYWORDS.has(stripped))
127
+ return 'yellow';
128
+ if (SUCCESS_KEYWORDS.has(stripped))
129
+ return 'green';
130
+ // HTTP methods
131
+ const httpMethod = HTTP_METHODS.get(word.toUpperCase());
132
+ if (httpMethod)
133
+ return httpMethod;
134
+ // HTTP status codes
135
+ const statusColor = httpStatusColor(stripped);
136
+ if (statusColor)
137
+ return statusColor;
138
+ if (ACTION_KEYWORDS.has(stripped))
139
+ return 'cyan';
140
+ // Paths and URLs
141
+ if (URL_PATTERN.test(word) || PATH_PATTERN.test(word))
142
+ return 'magenta';
143
+ // Numbers
144
+ if (NUMBER_PATTERN.test(stripped))
145
+ return 'blue';
146
+ return null;
147
+ }
148
+ // ── LRU cache for highlighted results ──
149
+ const CACHE_MAX = 500;
150
+ const highlightCache = new Map();
151
+ function cacheKey(text, format) {
152
+ return `${format}:${text}`;
153
+ }
154
+ /**
155
+ * Apply keyword-based semantic highlighting to text.
156
+ *
157
+ * Splits text on whitespace and colors each word based on keyword category.
158
+ * Words not matching any category pass through uncolored.
159
+ *
160
+ * @param text - The text to highlight.
161
+ * @param format - Output format: 'blessed' (CLI), 'ansi' (terminal), or 'html' (webview).
162
+ * @returns The highlighted text with format-appropriate tags.
163
+ */
164
+ function highlight(text, format) {
165
+ if (!text)
166
+ return text;
167
+ const key = cacheKey(text, format);
168
+ const cached = highlightCache.get(key);
169
+ if (cached !== undefined)
170
+ return cached;
171
+ const colors = getColors(format);
172
+ const words = text.split(/(\s+)/); // Split preserving whitespace
173
+ const parts = [];
174
+ for (const segment of words) {
175
+ // Preserve whitespace segments as-is
176
+ if (/^\s+$/.test(segment)) {
177
+ parts.push(segment);
178
+ continue;
179
+ }
180
+ const color = classifyWord(segment);
181
+ if (color) {
182
+ const [open, close] = colors[color];
183
+ parts.push(`${open}${segment}${close}`);
184
+ }
185
+ else {
186
+ parts.push(segment);
187
+ }
188
+ }
189
+ const result = parts.join('');
190
+ // LRU cache: evict oldest if full
191
+ if (highlightCache.size >= CACHE_MAX) {
192
+ const first = highlightCache.keys().next().value;
193
+ if (first !== undefined)
194
+ highlightCache.delete(first);
195
+ }
196
+ highlightCache.set(key, result);
197
+ return result;
198
+ }
199
+ /**
200
+ * CSS class definitions for HTML highlight format.
201
+ * Include this in webview stylesheets.
202
+ */
203
+ exports.HIGHLIGHT_CSS = `
204
+ .sk-hl-error { color: var(--vscode-charts-red, #f44747); }
205
+ .sk-hl-success { color: var(--vscode-charts-green, #89d185); }
206
+ .sk-hl-warning { color: var(--vscode-charts-yellow, #cca700); }
207
+ .sk-hl-action { color: var(--vscode-charts-blue, #4fc1ff); }
208
+ .sk-hl-number { color: var(--vscode-charts-purple, #b180d7); }
209
+ .sk-hl-path { color: var(--vscode-charts-orange, #cca700); }
210
+ .sk-hl-muted { opacity: 0.6; }
211
+ .sk-hl-default { }
212
+ .sk-search-match { background-color: var(--vscode-editor-findMatchHighlightBackground, rgba(234, 92, 0, 0.33)); }
213
+ `;
214
+ /** Clear the highlight cache (for testing or memory pressure). */
215
+ function clearHighlightCache() {
216
+ highlightCache.clear();
217
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Noise classification pipeline for session events.
3
+ *
4
+ * Multi-layer classification system that filters system noise, sidechains,
5
+ * synthetic messages, and soft noise (system-reminder tags, empty outputs).
6
+ * Inspired by tail-claude's noise filtering.
7
+ *
8
+ * @module formatters/noiseClassifier
9
+ */
10
+ import type { SessionEvent } from '../types/sessionEvent';
11
+ import type { FollowEvent } from '../watchers/types';
12
+ export type MessageClassification = 'user' | 'ai' | 'system' | 'teammate' | 'compact';
13
+ export interface NoiseResult {
14
+ /** Whether this event should be dropped entirely */
15
+ isHardNoise: boolean;
16
+ /** Reason for soft noise classification, or null if not soft noise */
17
+ softNoiseReason: string | null;
18
+ /** Semantic message classification */
19
+ messageClassification: MessageClassification;
20
+ }
21
+ /**
22
+ * Determines if an event is "hard noise" and should be dropped entirely.
23
+ *
24
+ * Hard noise includes:
25
+ * - System-type events (non-content)
26
+ * - Specific infrastructure event types (file-history-snapshot, queue-operation, progress)
27
+ * - Sidechain/subagent events (when filtering to main conversation)
28
+ * - Synthetic model entries (model starts with `<synthetic>`)
29
+ */
30
+ export declare function isHardNoise(event: SessionEvent): boolean;
31
+ /**
32
+ * FollowEvent variant of hard noise detection.
33
+ */
34
+ export declare function isHardNoiseFollowEvent(event: FollowEvent): boolean;
35
+ /**
36
+ * Checks for soft noise that can be hidden but not dropped.
37
+ *
38
+ * Soft noise includes:
39
+ * - system-reminder XML tags in content
40
+ * - Command caveats (IMPORTANT/Note instructions)
41
+ * - Empty tool outputs
42
+ * - User interruption markers
43
+ *
44
+ * @returns A reason string if soft noise is detected, null otherwise.
45
+ */
46
+ export declare function getSoftNoiseReason(event: SessionEvent): string | null;
47
+ /**
48
+ * Classifies a SessionEvent into semantic message categories.
49
+ *
50
+ * Categories:
51
+ * - `user` — human prompts
52
+ * - `ai` — assistant responses (text or tool calls)
53
+ * - `system` — tool results, compaction, infrastructure
54
+ * - `teammate` — teammate/subagent messages (from `<teammate-message>` blocks)
55
+ * - `compact` — compaction/summary events
56
+ */
57
+ export declare function classifyMessage(event: SessionEvent): MessageClassification;
58
+ /**
59
+ * FollowEvent variant of message classification.
60
+ */
61
+ export declare function classifyFollowEvent(event: FollowEvent): MessageClassification;
62
+ /**
63
+ * Determines if the current event should be merged with the previous one.
64
+ *
65
+ * Consecutive assistant text messages (without tool calls between them)
66
+ * can be merged into a single display entry.
67
+ */
68
+ export declare function shouldMergeWithPrevious(current: SessionEvent, previous: SessionEvent | null): boolean;
69
+ /**
70
+ * Performs full noise classification on a SessionEvent.
71
+ * Combines hard noise detection, soft noise detection, and message classification.
72
+ */
73
+ export declare function classifyNoise(event: SessionEvent): NoiseResult;