sidekick-shared 0.27.6 → 0.28.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/CHANGELOG.md +28 -0
- package/README.md +58 -18
- package/dist/browser.d.ts +13 -2
- package/dist/browser.js +30 -2
- package/dist/claudePaths.d.ts +11 -0
- package/dist/claudePaths.js +61 -0
- package/dist/claudeProfiles.d.ts +2 -9
- package/dist/claudeProfiles.js +10 -25
- package/dist/codexPaths.d.ts +22 -0
- package/dist/codexPaths.js +67 -0
- package/dist/codexProfiles.d.ts +1 -4
- package/dist/codexProfiles.js +5 -43
- package/dist/doctor.js +2 -2
- package/dist/esm/accountChangeSignal.js +15 -0
- package/dist/esm/accountChanges.js +152 -0
- package/dist/esm/accountHealth.js +288 -0
- package/dist/esm/accountLaunch.js +145 -0
- package/dist/esm/accountLease.js +60 -0
- package/dist/esm/accountManager.js +606 -0
- package/dist/esm/accountRegistry.js +214 -0
- package/dist/esm/accountStatus.js +51 -0
- package/dist/esm/accountSwitch.js +74 -0
- package/dist/esm/accountSync.js +156 -0
- package/dist/esm/accountSyncTypes.js +4 -0
- package/dist/esm/accounts.js +1291 -0
- package/dist/esm/aggregation/EventAggregator.js +2034 -0
- package/dist/esm/aggregation/FrequencyTracker.js +69 -0
- package/dist/esm/aggregation/HeatmapTracker.js +89 -0
- package/dist/esm/aggregation/PatternExtractor.js +168 -0
- package/dist/esm/aggregation/costProvenance.js +31 -0
- package/dist/esm/aggregation/snapshot.js +161 -0
- package/dist/esm/aggregation/types.js +5 -0
- package/dist/esm/analytics/codeImpact.js +18 -0
- package/dist/esm/analytics/compactionLedger.js +37 -0
- package/dist/esm/analytics/qualityScore.js +112 -0
- package/dist/esm/autoSwitch.js +185 -0
- package/dist/esm/browser.js +34 -0
- package/dist/esm/claudeCredentialDoctor.js +239 -0
- package/dist/esm/claudeCredentialLedger.js +91 -0
- package/dist/esm/claudeCredentials.js +111 -0
- package/dist/esm/claudeLiveGuard.js +43 -0
- package/dist/esm/claudePaths.js +22 -0
- package/dist/esm/claudeProfiles.js +142 -0
- package/dist/esm/claudeRefreshLock.js +106 -0
- package/dist/esm/codexAuth.js +101 -0
- package/dist/esm/codexCredentialDoctor.js +265 -0
- package/dist/esm/codexCredentialStore.js +164 -0
- package/dist/esm/codexKeepAlive.js +112 -0
- package/dist/esm/codexPaths.js +172 -0
- package/dist/esm/codexProfiles.js +1251 -0
- package/dist/esm/codexQuota.js +564 -0
- package/dist/esm/codexQuotaWatcher.js +363 -0
- package/dist/esm/context/composer.js +71 -0
- package/dist/esm/context/sessionContext.js +546 -0
- package/dist/esm/credentialIO.js +283 -0
- package/dist/esm/credentials.js +49 -0
- package/dist/esm/doctor.js +305 -0
- package/dist/esm/ensureDefaultAccounts.js +62 -0
- package/dist/esm/errorHistory.js +56 -0
- package/dist/esm/extractors/errorTaxonomy.js +43 -0
- package/dist/esm/extractors/gatherAssets.js +32 -0
- package/dist/esm/extractors/sessionAssets.js +176 -0
- package/dist/esm/extractors/sources/claudeAssets.js +158 -0
- package/dist/esm/extractors/sources/codexAssets.js +234 -0
- package/dist/esm/extractors/tokenUsage.js +41 -0
- package/dist/esm/extractors/toolCall.js +142 -0
- package/dist/esm/failingTools.js +65 -0
- package/dist/esm/fileHead.js +27 -0
- package/dist/esm/formatters/eventHighlighter.js +332 -0
- package/dist/esm/formatters/noiseClassifier.js +209 -0
- package/dist/esm/formatters/sessionDump.js +328 -0
- package/dist/esm/formatters/toolSummary.js +226 -0
- package/dist/esm/formatting.js +82 -0
- package/dist/esm/handoffUrl.js +23 -0
- package/dist/esm/historicalStore.js +305 -0
- package/dist/esm/humanPrompt.js +88 -0
- package/dist/esm/index.js +213 -0
- package/dist/esm/keepAlive.js +189 -0
- package/dist/esm/liveCredentialGuard.js +33 -0
- package/dist/esm/modelAliases.js +51 -0
- package/dist/esm/modelCatalog.js +139 -0
- package/dist/esm/modelContext.js +280 -0
- package/dist/esm/modelInfo.js +832 -0
- package/dist/esm/multiProviderQuotaService.js +450 -0
- package/dist/esm/node.js +46 -0
- package/dist/esm/observedContextWindows.js +165 -0
- package/dist/esm/observedSessionCollector.js +939 -0
- package/dist/esm/parsers/changelogParser.js +71 -0
- package/dist/esm/parsers/codexHistory.js +89 -0
- package/dist/esm/parsers/codexParser.js +906 -0
- package/dist/esm/parsers/debugLogParser.js +126 -0
- package/dist/esm/parsers/jsonl.js +66 -0
- package/dist/esm/parsers/mcpToolName.js +28 -0
- package/dist/esm/parsers/openCodeParser.js +625 -0
- package/dist/esm/parsers/planExtractor.js +335 -0
- package/dist/esm/parsers/sessionActivityDetector.js +246 -0
- package/dist/esm/parsers/sessionPathResolver.js +713 -0
- package/dist/esm/parsers/subagentScanner.js +360 -0
- package/dist/esm/parsers/subagentStatsCache.js +45 -0
- package/dist/esm/parsers/subagentTraceParser.js +325 -0
- package/dist/esm/paths.js +134 -0
- package/dist/esm/peakHours.js +194 -0
- package/dist/esm/phrases.js +1330 -0
- package/dist/esm/pricingCatalog.js +247 -0
- package/dist/esm/processDetection.js +200 -0
- package/dist/esm/projectMigration.js +59 -0
- package/dist/esm/promptHistory.js +1221 -0
- package/dist/esm/providerFailure.js +362 -0
- package/dist/esm/providerIds.js +6 -0
- package/dist/esm/providerQuota.js +1 -0
- package/dist/esm/providerServiceStatus.js +145 -0
- package/dist/esm/providerServiceStatusTypes.js +1 -0
- package/dist/esm/providerStatus.js +117 -0
- package/dist/esm/providers/claudeCode.js +575 -0
- package/dist/esm/providers/claudeListing.js +121 -0
- package/dist/esm/providers/codex.js +1034 -0
- package/dist/esm/providers/codexDatabase.js +297 -0
- package/dist/esm/providers/codexRollouts.js +215 -0
- package/dist/esm/providers/detect.js +185 -0
- package/dist/esm/providers/diagnostics.js +83 -0
- package/dist/esm/providers/directoryListingCache.js +206 -0
- package/dist/esm/providers/factory.js +54 -0
- package/dist/esm/providers/openCode.js +2008 -0
- package/dist/esm/providers/openCodeDatabase.js +419 -0
- package/dist/esm/providers/rolloutWalker.js +252 -0
- package/dist/esm/providers/types.js +12 -0
- package/dist/esm/quota.js +182 -0
- package/dist/esm/quotaHistory.js +364 -0
- package/dist/esm/quotaPoller.js +230 -0
- package/dist/esm/quotaPresentation.js +97 -0
- package/dist/esm/quotaResolver.js +231 -0
- package/dist/esm/quotaSnapshots.js +130 -0
- package/dist/esm/quotaThresholds.js +85 -0
- package/dist/esm/readers/decisions.js +24 -0
- package/dist/esm/readers/handoff.js +20 -0
- package/dist/esm/readers/helpers.js +27 -0
- package/dist/esm/readers/history.js +9 -0
- package/dist/esm/readers/notes.js +48 -0
- package/dist/esm/readers/plans.js +225 -0
- package/dist/esm/readers/tasks.js +19 -0
- package/dist/esm/report/htmlHelpers.js +280 -0
- package/dist/esm/report/htmlReportGenerator.js +704 -0
- package/dist/esm/report/index.js +7 -0
- package/dist/esm/report/logo.js +2 -0
- package/dist/esm/report/openBrowser.js +19 -0
- package/dist/esm/report/sessionReportInputs.js +33 -0
- package/dist/esm/report/transcriptParser.js +83 -0
- package/dist/esm/report/types.js +4 -0
- package/dist/esm/schemas/accountManager.js +204 -0
- package/dist/esm/schemas/accountStatus.js +25 -0
- package/dist/esm/schemas/assistantTurn.js +74 -0
- package/dist/esm/schemas/index.js +25 -0
- package/dist/esm/schemas/observedSessionV1.js +77 -0
- package/dist/esm/schemas/providerFailure.js +37 -0
- package/dist/esm/schemas/quota.js +132 -0
- package/dist/esm/schemas/quotaHistory.js +40 -0
- package/dist/esm/schemas/sessionEvent.js +216 -0
- package/dist/esm/schemas/stateFile.js +75 -0
- package/dist/esm/schemas/tokenEstimation.js +7 -0
- package/dist/esm/schemas/transcript.js +86 -0
- package/dist/esm/schemas/usageNormalization.js +38 -0
- package/dist/esm/search/advancedFilter.js +197 -0
- package/dist/esm/search/sessionSearch.js +66 -0
- package/dist/esm/sessionFingerprint.js +40 -0
- package/dist/esm/sessionMonitor.js +198 -0
- package/dist/esm/sessionPreviews.js +522 -0
- package/dist/esm/sessionPromptHistory.js +254 -0
- package/dist/esm/sessionPromptSignals.js +364 -0
- package/dist/esm/sessionStats.js +160 -0
- package/dist/esm/sessionTokenTotals.js +98 -0
- package/dist/esm/sessionTranscripts.js +49 -0
- package/dist/esm/stateFile.js +119 -0
- package/dist/esm/statusline/BurnRateCalculator.js +40 -0
- package/dist/esm/statusline/claudeStatuslinePayload.js +148 -0
- package/dist/esm/statusline/formatter.js +122 -0
- package/dist/esm/statusline/index.js +8 -0
- package/dist/esm/terminalSync.js +95 -0
- package/dist/esm/testing/credentialGuard.js +197 -0
- package/dist/esm/testing/index.js +12 -0
- package/dist/esm/testing/sessionLogFixtures.js +128 -0
- package/dist/esm/tokenEstimation.js +67 -0
- package/dist/esm/tokenSummary.js +113 -0
- package/dist/esm/toolCategory.js +82 -0
- package/dist/esm/transcript.js +257 -0
- package/dist/esm/turns/assistantTurn.js +373 -0
- package/dist/esm/types/codex.js +23 -0
- package/dist/esm/types/decisionLog.js +5 -0
- package/dist/esm/types/historicalData.js +47 -0
- package/dist/esm/types/knowledgeNote.js +15 -0
- package/dist/esm/types/observedSessionV1.js +257 -0
- package/dist/esm/types/opencode.js +12 -0
- package/dist/esm/types/plan.js +7 -0
- package/dist/esm/types/sessionEvent.js +10 -0
- package/dist/esm/types/taskPersistence.js +12 -0
- package/dist/esm/usage/billingBlocks.js +148 -0
- package/dist/esm/usage/claudeUsageDedupe.js +189 -0
- package/dist/esm/usage/importSessionHistory.js +84 -0
- package/dist/esm/usage/subagentUsage.js +18 -0
- package/dist/esm/usage/usageEvents.js +402 -0
- package/dist/esm/usage/usageReports.js +202 -0
- package/dist/esm/usageNormalization.js +95 -0
- package/dist/esm/watchers/eventBridge.js +219 -0
- package/dist/esm/watchers/factory.js +63 -0
- package/dist/esm/watchers/jsonlTail.js +289 -0
- package/dist/esm/watchers/jsonlWatcher.js +435 -0
- package/dist/esm/watchers/providerReaderWatcher.js +118 -0
- package/dist/esm/watchers/sqliteWatcher.js +285 -0
- package/dist/esm/watchers/types.js +4 -0
- package/dist/esm/workspaceRoots.js +56 -0
- package/dist/esm/writers/atomic.js +375 -0
- package/dist/esm/writers/decisions.js +51 -0
- package/dist/esm/writers/notes.js +46 -0
- package/dist/esm/writers/tasks.js +63 -0
- package/dist/esm/zaiQuota.js +323 -0
- package/dist/esm/zaiQuotaApi.js +271 -0
- package/dist/esm/zaiQuotaWatcher.js +265 -0
- package/dist/fileHead.d.ts +22 -0
- package/dist/fileHead.js +33 -0
- package/dist/index.d.ts +16 -7
- package/dist/index.js +49 -15
- package/dist/node.d.ts +13 -1
- package/dist/node.js +43 -2
- package/dist/parsers/codexHistory.js +2 -2
- package/dist/parsers/codexParser.d.ts +12 -0
- package/dist/parsers/codexParser.js +34 -12
- package/dist/parsers/debugLogParser.js +2 -2
- package/dist/parsers/sessionPathResolver.js +5 -4
- package/dist/promptHistory.d.ts +32 -0
- package/dist/promptHistory.js +115 -59
- package/dist/providerFailure.d.ts +12 -6
- package/dist/providerFailure.js +45 -0
- package/dist/providers/claudeCode.js +7 -99
- package/dist/providers/claudeListing.d.ts +22 -0
- package/dist/providers/claudeListing.js +158 -0
- package/dist/providers/codex.d.ts +3 -1
- package/dist/providers/codex.js +26 -227
- package/dist/providers/codexRollouts.d.ts +44 -0
- package/dist/providers/codexRollouts.js +261 -0
- package/dist/providers/detect.js +4 -4
- package/dist/readers/plans.js +3 -3
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +7 -2
- package/dist/schemas/providerFailure.d.ts +154 -0
- package/dist/schemas/providerFailure.js +40 -0
- package/dist/sessionPromptHistory.d.ts +12 -7
- package/dist/sessionPromptHistory.js +15 -3
- package/dist/sessionPromptSignals.d.ts +9 -0
- package/dist/sessionPromptSignals.js +13 -0
- package/dist/testing/credentialGuard.d.ts +23 -0
- package/dist/testing/credentialGuard.js +233 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +22 -0
- package/dist/testing/sessionLogFixtures.d.ts +28 -0
- package/dist/testing/sessionLogFixtures.js +169 -0
- package/dist/tokenEstimation.d.ts +2 -0
- package/dist/tokenEstimation.js +21 -11
- package/dist/toolCategory.d.ts +18 -0
- package/dist/toolCategory.js +86 -0
- package/dist/types/codex.d.ts +4 -0
- package/dist/usage/usageEvents.d.ts +16 -0
- package/dist/usage/usageEvents.js +53 -9
- package/dist/watchers/jsonlTail.d.ts +26 -1
- package/dist/watchers/jsonlTail.js +136 -28
- package/dist/watchers/jsonlWatcher.js +7 -0
- package/dist/watchers/providerReaderWatcher.js +1 -0
- package/dist/watchers/types.d.ts +6 -0
- package/dist/workspaceRoots.d.ts +23 -0
- package/dist/workspaceRoots.js +93 -0
- package/package.json +22 -2
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral session context evidence projection.
|
|
3
|
+
*
|
|
4
|
+
* Converts canonical SessionEvent streams into a compact manifest suitable for
|
|
5
|
+
* UI surfaces that need to explain what an assistant/provider has seen.
|
|
6
|
+
*/
|
|
7
|
+
import { EventAggregator } from '../aggregation/EventAggregator';
|
|
8
|
+
import { getModelContextWindowSize } from '../modelContext';
|
|
9
|
+
import { parseMcpToolName } from '../parsers/mcpToolName';
|
|
10
|
+
import { estimateTextTokens } from '../tokenEstimation';
|
|
11
|
+
const DEFAULT_CONTEXT_SOURCE_LIMIT = 200;
|
|
12
|
+
const DEFAULT_SNIPPET_MAX_CHARS = 240;
|
|
13
|
+
const DEFAULT_BODY_MAX_CHARS = 5000;
|
|
14
|
+
const MEDIUM_PRESSURE_RATIO = 0.6;
|
|
15
|
+
const HIGH_PRESSURE_RATIO = 0.8;
|
|
16
|
+
const EMPTY_ATTRIBUTION = {
|
|
17
|
+
systemPrompt: 0,
|
|
18
|
+
userMessages: 0,
|
|
19
|
+
assistantResponses: 0,
|
|
20
|
+
toolInputs: 0,
|
|
21
|
+
toolOutputs: 0,
|
|
22
|
+
thinking: 0,
|
|
23
|
+
other: 0,
|
|
24
|
+
};
|
|
25
|
+
export function calculateSessionContextPressure(contextTokens, contextWindow) {
|
|
26
|
+
if (!Number.isFinite(contextTokens) || contextTokens <= 0) {
|
|
27
|
+
return { pressure: 'low', ratio: 0 };
|
|
28
|
+
}
|
|
29
|
+
if (!Number.isFinite(contextWindow) || contextWindow <= 0) {
|
|
30
|
+
return { pressure: 'low', ratio: 0 };
|
|
31
|
+
}
|
|
32
|
+
const ratio = Math.max(0, Math.min(1, contextTokens / contextWindow));
|
|
33
|
+
if (ratio >= HIGH_PRESSURE_RATIO)
|
|
34
|
+
return { pressure: 'high', ratio };
|
|
35
|
+
if (ratio >= MEDIUM_PRESSURE_RATIO)
|
|
36
|
+
return { pressure: 'medium', ratio };
|
|
37
|
+
return { pressure: 'low', ratio };
|
|
38
|
+
}
|
|
39
|
+
export function buildSessionContextSnapshot(events, options = {}) {
|
|
40
|
+
const providerId = options.providerId;
|
|
41
|
+
const aggregator = new EventAggregator({
|
|
42
|
+
providerId,
|
|
43
|
+
computeContextSize: options.computeContextSize,
|
|
44
|
+
});
|
|
45
|
+
const sourceState = createSourceExtractionState(options);
|
|
46
|
+
let lastPermissionMode;
|
|
47
|
+
let lastRateLimits;
|
|
48
|
+
for (const event of events) {
|
|
49
|
+
aggregator.processEvent(event);
|
|
50
|
+
extractSourcesFromEvent(event, sourceState);
|
|
51
|
+
if (event.permissionMode)
|
|
52
|
+
lastPermissionMode = event.permissionMode;
|
|
53
|
+
if (event.rateLimits)
|
|
54
|
+
lastRateLimits = event.rateLimits;
|
|
55
|
+
}
|
|
56
|
+
return assembleSessionContextSnapshot(aggregator, sourceState, lastPermissionMode, lastRateLimits, options);
|
|
57
|
+
}
|
|
58
|
+
function assembleSessionContextSnapshot(aggregator, sourceState, lastPermissionMode, lastRateLimits, options) {
|
|
59
|
+
const providerId = options.providerId;
|
|
60
|
+
const metrics = aggregator.getMetrics();
|
|
61
|
+
const model = options.model ?? metrics.currentModel ?? metrics.modelStats[0]?.model;
|
|
62
|
+
const contextWindow = options.contextWindow ??
|
|
63
|
+
options.contextWindowForModel?.(model) ??
|
|
64
|
+
getModelContextWindowSize(model);
|
|
65
|
+
const allSources = sourceState.sources;
|
|
66
|
+
const limitedSources = limitSources(allSources, options.sourceLimit ?? DEFAULT_CONTEXT_SOURCE_LIMIT);
|
|
67
|
+
const breakdown = buildLayerBreakdown(limitedSources);
|
|
68
|
+
const layers = breakdown.map((row) => row.layer);
|
|
69
|
+
const sourceTokenTotal = allSources.reduce((sum, source) => sum + source.tokenEstimate, 0);
|
|
70
|
+
const contextTokens = metrics.currentContextSize > 0 ? metrics.currentContextSize : sourceTokenTotal;
|
|
71
|
+
const { pressure, ratio } = calculateSessionContextPressure(contextTokens, contextWindow);
|
|
72
|
+
return {
|
|
73
|
+
schemaVersion: 1,
|
|
74
|
+
providerId,
|
|
75
|
+
sessionId: options.sessionId,
|
|
76
|
+
sessionPath: options.sessionPath,
|
|
77
|
+
model,
|
|
78
|
+
contextWindow,
|
|
79
|
+
contextTokens,
|
|
80
|
+
pressure,
|
|
81
|
+
pressureRatio: ratio,
|
|
82
|
+
layers,
|
|
83
|
+
breakdown,
|
|
84
|
+
sources: limitedSources,
|
|
85
|
+
capabilities: {
|
|
86
|
+
providerId,
|
|
87
|
+
providerLabel: options.providerLabel ?? providerLabel(providerId),
|
|
88
|
+
model,
|
|
89
|
+
observedTools: [...sourceState.observedTools].sort((a, b) => a.localeCompare(b)),
|
|
90
|
+
mcpServers: [...sourceState.mcpServers].sort((a, b) => a.localeCompare(b)),
|
|
91
|
+
permissionMode: lastPermissionMode,
|
|
92
|
+
rateLimits: lastRateLimits,
|
|
93
|
+
},
|
|
94
|
+
attribution: metrics.contextAttribution ?? { ...EMPTY_ATTRIBUTION },
|
|
95
|
+
tokens: metrics.tokens,
|
|
96
|
+
compactionCount: metrics.compactionCount,
|
|
97
|
+
compactionEvents: metrics.compactionEvents,
|
|
98
|
+
truncationCount: metrics.truncationCount,
|
|
99
|
+
truncationEvents: metrics.truncationEvents,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export function createSessionContextProjector(options = {}) {
|
|
103
|
+
let aggregator = createProjectorAggregator(options);
|
|
104
|
+
let sourceState = createSourceExtractionState(options);
|
|
105
|
+
let lastPermissionMode;
|
|
106
|
+
let lastRateLimits;
|
|
107
|
+
const process = (event) => {
|
|
108
|
+
aggregator.processEvent(event);
|
|
109
|
+
extractSourcesFromEvent(event, sourceState);
|
|
110
|
+
if (event.permissionMode)
|
|
111
|
+
lastPermissionMode = event.permissionMode;
|
|
112
|
+
if (event.rateLimits)
|
|
113
|
+
lastRateLimits = event.rateLimits;
|
|
114
|
+
};
|
|
115
|
+
const snapshot = () => assembleSessionContextSnapshot(aggregator, sourceState, lastPermissionMode, lastRateLimits, options);
|
|
116
|
+
return {
|
|
117
|
+
processEvent(event) {
|
|
118
|
+
process(event);
|
|
119
|
+
return snapshot();
|
|
120
|
+
},
|
|
121
|
+
processEvents(newEvents) {
|
|
122
|
+
for (const event of newEvents)
|
|
123
|
+
process(event);
|
|
124
|
+
return snapshot();
|
|
125
|
+
},
|
|
126
|
+
getSnapshot() {
|
|
127
|
+
return snapshot();
|
|
128
|
+
},
|
|
129
|
+
reset() {
|
|
130
|
+
aggregator = createProjectorAggregator(options);
|
|
131
|
+
sourceState = createSourceExtractionState(options);
|
|
132
|
+
lastPermissionMode = undefined;
|
|
133
|
+
lastRateLimits = undefined;
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function createProjectorAggregator(options) {
|
|
138
|
+
return new EventAggregator({
|
|
139
|
+
providerId: options.providerId,
|
|
140
|
+
computeContextSize: options.computeContextSize,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
export function readSessionContextSnapshot(provider, sessionPath, options = {}) {
|
|
144
|
+
const reader = provider.createReader(sessionPath);
|
|
145
|
+
const events = reader.readAll();
|
|
146
|
+
const model = options.model ?? latestModel(events);
|
|
147
|
+
return buildSessionContextSnapshot(events, {
|
|
148
|
+
...options,
|
|
149
|
+
providerId: provider.id,
|
|
150
|
+
providerLabel: provider.displayName,
|
|
151
|
+
sessionId: provider.getSessionId(sessionPath),
|
|
152
|
+
sessionPath,
|
|
153
|
+
model,
|
|
154
|
+
contextWindow: options.contextWindow ?? provider.getContextWindowLimit?.(model),
|
|
155
|
+
computeContextSize: provider.computeContextSize
|
|
156
|
+
? (usage) => provider.computeContextSize(usage)
|
|
157
|
+
: undefined,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function createSourceExtractionState(options) {
|
|
161
|
+
return {
|
|
162
|
+
options,
|
|
163
|
+
sources: [],
|
|
164
|
+
observedTools: new Set(),
|
|
165
|
+
mcpServers: new Set(),
|
|
166
|
+
counter: 0,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function extractSourcesFromEvent(event, state) {
|
|
170
|
+
switch (event.type) {
|
|
171
|
+
case 'system':
|
|
172
|
+
extractSystemSource(event, state);
|
|
173
|
+
break;
|
|
174
|
+
case 'user':
|
|
175
|
+
extractUserSources(event, state);
|
|
176
|
+
break;
|
|
177
|
+
case 'assistant':
|
|
178
|
+
extractAssistantSources(event, state);
|
|
179
|
+
break;
|
|
180
|
+
case 'tool_use':
|
|
181
|
+
if (event.tool) {
|
|
182
|
+
addToolInputSource(event, state, event.tool.name, event.tool.input, undefined);
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
case 'tool_result':
|
|
186
|
+
addSource(state, {
|
|
187
|
+
event,
|
|
188
|
+
layer: 'tool outputs',
|
|
189
|
+
sourceType: event.result?.is_error ? 'error' : 'tool_output',
|
|
190
|
+
title: event.result?.is_error ? 'Tool error' : 'Tool result',
|
|
191
|
+
sourceRef: event.result?.tool_use_id,
|
|
192
|
+
text: stringifyValue(event.result?.output),
|
|
193
|
+
metadata: { toolUseId: event.result?.tool_use_id },
|
|
194
|
+
});
|
|
195
|
+
break;
|
|
196
|
+
case 'summary':
|
|
197
|
+
addSource(state, {
|
|
198
|
+
event,
|
|
199
|
+
layer: 'summary',
|
|
200
|
+
sourceType: 'summary',
|
|
201
|
+
title: 'Context summary',
|
|
202
|
+
text: extractText(event.message?.content) || 'Context compacted',
|
|
203
|
+
});
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function extractSystemSource(event, state) {
|
|
208
|
+
const message = event.message;
|
|
209
|
+
const text = extractText(message?.content);
|
|
210
|
+
const label = message?.sourceLabel ?? message?.role ?? 'system';
|
|
211
|
+
const usage = message?.usage;
|
|
212
|
+
if (text) {
|
|
213
|
+
addSource(state, {
|
|
214
|
+
event,
|
|
215
|
+
layer: 'system',
|
|
216
|
+
sourceType: 'system',
|
|
217
|
+
title: label,
|
|
218
|
+
sourceRef: message?.id,
|
|
219
|
+
text,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (usage || event.rateLimits) {
|
|
223
|
+
addSource(state, {
|
|
224
|
+
event,
|
|
225
|
+
layer: 'runtime',
|
|
226
|
+
sourceType: event.rateLimits ? 'rate_limit' : 'runtime',
|
|
227
|
+
title: event.rateLimits ? 'Runtime usage and rate limits' : 'Runtime usage',
|
|
228
|
+
sourceRef: message?.id,
|
|
229
|
+
text: describeUsage(usage, event.rateLimits),
|
|
230
|
+
metadata: {
|
|
231
|
+
usage,
|
|
232
|
+
rateLimits: event.rateLimits,
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function extractUserSources(event, state) {
|
|
238
|
+
const content = event.message.content;
|
|
239
|
+
if (Array.isArray(content)) {
|
|
240
|
+
for (const block of content) {
|
|
241
|
+
if (block.type === 'tool_result') {
|
|
242
|
+
const isError = block.is_error === true;
|
|
243
|
+
addSource(state, {
|
|
244
|
+
event,
|
|
245
|
+
layer: 'tool outputs',
|
|
246
|
+
sourceType: isError ? 'error' : 'tool_output',
|
|
247
|
+
title: isError ? 'Tool error' : 'Tool result',
|
|
248
|
+
sourceRef: stringValue(block.tool_use_id),
|
|
249
|
+
text: stringifyValue(block.content),
|
|
250
|
+
metadata: {
|
|
251
|
+
toolUseId: block.tool_use_id,
|
|
252
|
+
duration: block.duration,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const text = blockText(block);
|
|
258
|
+
if (text) {
|
|
259
|
+
addSource(state, {
|
|
260
|
+
event,
|
|
261
|
+
layer: 'user',
|
|
262
|
+
sourceType: 'user_prompt',
|
|
263
|
+
title: event.message.sourceLabel ?? 'User prompt',
|
|
264
|
+
sourceRef: event.message.id,
|
|
265
|
+
text,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const text = extractText(content);
|
|
272
|
+
if (text) {
|
|
273
|
+
addSource(state, {
|
|
274
|
+
event,
|
|
275
|
+
layer: 'user',
|
|
276
|
+
sourceType: 'user_prompt',
|
|
277
|
+
title: event.message.sourceLabel ?? 'User prompt',
|
|
278
|
+
sourceRef: event.message.id,
|
|
279
|
+
text,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function extractAssistantSources(event, state) {
|
|
284
|
+
const content = event.message.content;
|
|
285
|
+
if (!Array.isArray(content)) {
|
|
286
|
+
const text = extractText(content);
|
|
287
|
+
if (text) {
|
|
288
|
+
addSource(state, {
|
|
289
|
+
event,
|
|
290
|
+
layer: 'assistant',
|
|
291
|
+
sourceType: 'assistant_response',
|
|
292
|
+
title: 'Assistant response',
|
|
293
|
+
sourceRef: event.message.id,
|
|
294
|
+
text,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
for (const block of content) {
|
|
300
|
+
if (block.type === 'tool_use' && typeof block.name === 'string') {
|
|
301
|
+
addToolInputSource(event, state, block.name, isRecord(block.input) ? block.input : {}, stringValue(block.id));
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (block.type === 'thinking' || block.type === 'reasoning') {
|
|
305
|
+
const text = blockText(block);
|
|
306
|
+
if (text) {
|
|
307
|
+
addSource(state, {
|
|
308
|
+
event,
|
|
309
|
+
layer: 'thinking',
|
|
310
|
+
sourceType: 'thinking',
|
|
311
|
+
title: 'Reasoning',
|
|
312
|
+
sourceRef: event.message.id,
|
|
313
|
+
text,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const text = blockText(block);
|
|
319
|
+
if (text) {
|
|
320
|
+
addSource(state, {
|
|
321
|
+
event,
|
|
322
|
+
layer: 'assistant',
|
|
323
|
+
sourceType: 'assistant_response',
|
|
324
|
+
title: 'Assistant response',
|
|
325
|
+
sourceRef: event.message.id,
|
|
326
|
+
text,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function addToolInputSource(event, state, toolName, input, sourceRef) {
|
|
332
|
+
state.observedTools.add(toolName);
|
|
333
|
+
const mcpServer = inferMcpServer(toolName, input);
|
|
334
|
+
if (mcpServer)
|
|
335
|
+
state.mcpServers.add(mcpServer);
|
|
336
|
+
const sourceFile = inferSourceFile(input);
|
|
337
|
+
const command = inferCommand(input);
|
|
338
|
+
addSource(state, {
|
|
339
|
+
event,
|
|
340
|
+
layer: 'tool inputs',
|
|
341
|
+
sourceType: 'tool_input',
|
|
342
|
+
title: sourceFile ? `${toolName}: ${sourceFile}` : toolName,
|
|
343
|
+
sourceRef: sourceRef ?? command,
|
|
344
|
+
sourceFile,
|
|
345
|
+
toolName,
|
|
346
|
+
text: stringifyValue(input),
|
|
347
|
+
metadata: {
|
|
348
|
+
rawToolName: input._sidekickRawToolName,
|
|
349
|
+
mcpServer,
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
function addSource(state, seed) {
|
|
354
|
+
const cleanText = clean(seed.text);
|
|
355
|
+
if (!cleanText)
|
|
356
|
+
return;
|
|
357
|
+
const bodyText = seed.text.trim();
|
|
358
|
+
const snippetMaxChars = state.options.snippetMaxChars ?? DEFAULT_SNIPPET_MAX_CHARS;
|
|
359
|
+
const bodyMaxChars = state.options.bodyMaxChars ?? DEFAULT_BODY_MAX_CHARS;
|
|
360
|
+
const bodyInfo = state.options.includeBodies
|
|
361
|
+
? truncateWithInfo(bodyText, bodyMaxChars)
|
|
362
|
+
: undefined;
|
|
363
|
+
const tokenEstimate = estimateTokens(cleanText);
|
|
364
|
+
const metadata = {
|
|
365
|
+
...(seed.metadata ?? {}),
|
|
366
|
+
...(bodyInfo?.truncated ? { bodyTruncated: true, originalChars: bodyText.length } : {}),
|
|
367
|
+
};
|
|
368
|
+
state.sources.push({
|
|
369
|
+
id: `${state.options.sessionId ?? state.options.sessionPath ?? 'session'}:${state.counter++}`,
|
|
370
|
+
providerId: state.options.providerId,
|
|
371
|
+
sessionId: state.options.sessionId,
|
|
372
|
+
sessionPath: state.options.sessionPath,
|
|
373
|
+
eventType: seed.event.type,
|
|
374
|
+
timestamp: seed.event.timestamp,
|
|
375
|
+
layer: seed.layer,
|
|
376
|
+
sourceType: seed.sourceType,
|
|
377
|
+
title: seed.title,
|
|
378
|
+
sourceRef: seed.sourceRef,
|
|
379
|
+
sourceFile: seed.sourceFile,
|
|
380
|
+
toolName: seed.toolName,
|
|
381
|
+
score: seed.score,
|
|
382
|
+
tokenEstimate,
|
|
383
|
+
snippet: truncateClean(cleanText, snippetMaxChars),
|
|
384
|
+
body: bodyInfo?.text,
|
|
385
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
function buildLayerBreakdown(sources) {
|
|
389
|
+
const byLayer = new Map();
|
|
390
|
+
for (const source of sources) {
|
|
391
|
+
const row = byLayer.get(source.layer) ?? {
|
|
392
|
+
layer: source.layer,
|
|
393
|
+
tokenEstimate: 0,
|
|
394
|
+
sourceCount: 0,
|
|
395
|
+
};
|
|
396
|
+
row.tokenEstimate += source.tokenEstimate;
|
|
397
|
+
row.sourceCount++;
|
|
398
|
+
byLayer.set(source.layer, row);
|
|
399
|
+
}
|
|
400
|
+
return [...byLayer.values()].sort((a, b) => b.tokenEstimate - a.tokenEstimate);
|
|
401
|
+
}
|
|
402
|
+
function limitSources(sources, limit) {
|
|
403
|
+
if (!Number.isFinite(limit) || limit <= 0)
|
|
404
|
+
return [];
|
|
405
|
+
if (sources.length <= limit)
|
|
406
|
+
return [...sources];
|
|
407
|
+
const pinned = sources.filter((source) => source.sourceType === 'system' ||
|
|
408
|
+
source.sourceType === 'runtime' ||
|
|
409
|
+
source.sourceType === 'rate_limit');
|
|
410
|
+
const pinnedLimit = Math.min(pinned.length, Math.max(1, Math.floor(limit / 4)));
|
|
411
|
+
const pinnedKept = pinned.slice(0, pinnedLimit);
|
|
412
|
+
const pinnedIds = new Set(pinnedKept.map((source) => source.id));
|
|
413
|
+
const remaining = sources.filter((source) => !pinnedIds.has(source.id));
|
|
414
|
+
const latest = remaining.slice(-(limit - pinnedKept.length));
|
|
415
|
+
const keepIds = new Set([...pinnedKept, ...latest].map((source) => source.id));
|
|
416
|
+
return sources.filter((source) => keepIds.has(source.id));
|
|
417
|
+
}
|
|
418
|
+
function latestModel(events) {
|
|
419
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
420
|
+
const model = events[i].message?.model;
|
|
421
|
+
if (model)
|
|
422
|
+
return model;
|
|
423
|
+
}
|
|
424
|
+
return undefined;
|
|
425
|
+
}
|
|
426
|
+
function providerLabel(providerId) {
|
|
427
|
+
switch (providerId) {
|
|
428
|
+
case 'claude-code':
|
|
429
|
+
return 'Claude Code';
|
|
430
|
+
case 'codex':
|
|
431
|
+
return 'Codex';
|
|
432
|
+
case 'opencode':
|
|
433
|
+
return 'OpenCode';
|
|
434
|
+
default:
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function describeUsage(usage, rateLimits) {
|
|
439
|
+
const parts = [];
|
|
440
|
+
if (usage) {
|
|
441
|
+
parts.push(`Input tokens: ${usage.input_tokens || 0}`);
|
|
442
|
+
parts.push(`Output tokens: ${usage.output_tokens || 0}`);
|
|
443
|
+
if (usage.cache_read_input_tokens)
|
|
444
|
+
parts.push(`Cache read: ${usage.cache_read_input_tokens}`);
|
|
445
|
+
if (usage.cache_creation_input_tokens)
|
|
446
|
+
parts.push(`Cache write: ${usage.cache_creation_input_tokens}`);
|
|
447
|
+
if (usage.reasoning_tokens)
|
|
448
|
+
parts.push(`Reasoning tokens: ${usage.reasoning_tokens}`);
|
|
449
|
+
}
|
|
450
|
+
if (rateLimits?.primary) {
|
|
451
|
+
parts.push(`Primary limit: ${rateLimits.primary.usedPercent}% used`);
|
|
452
|
+
}
|
|
453
|
+
if (rateLimits?.secondary) {
|
|
454
|
+
parts.push(`Secondary limit: ${rateLimits.secondary.usedPercent}% used`);
|
|
455
|
+
}
|
|
456
|
+
return parts.join('\n') || 'Runtime metadata';
|
|
457
|
+
}
|
|
458
|
+
function inferSourceFile(input) {
|
|
459
|
+
for (const key of ['file_path', 'path', 'filename', 'notebook_path', 'target_file']) {
|
|
460
|
+
const value = input[key];
|
|
461
|
+
if (typeof value === 'string' && value.trim())
|
|
462
|
+
return value.trim();
|
|
463
|
+
}
|
|
464
|
+
const files = input.files;
|
|
465
|
+
if (Array.isArray(files)) {
|
|
466
|
+
const first = files.find((value) => typeof value === 'string' && value.trim());
|
|
467
|
+
if (typeof first === 'string')
|
|
468
|
+
return first.trim();
|
|
469
|
+
}
|
|
470
|
+
return undefined;
|
|
471
|
+
}
|
|
472
|
+
function inferCommand(input) {
|
|
473
|
+
const command = input.command ?? input.cmd;
|
|
474
|
+
if (typeof command === 'string' && command.trim())
|
|
475
|
+
return command.trim();
|
|
476
|
+
if (Array.isArray(command)) {
|
|
477
|
+
return (command
|
|
478
|
+
.map((part) => String(part))
|
|
479
|
+
.join(' ')
|
|
480
|
+
.trim() || undefined);
|
|
481
|
+
}
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
function inferMcpServer(toolName, input) {
|
|
485
|
+
const explicit = input._sidekickMcpServerName ?? input.server_name ?? input.serverName;
|
|
486
|
+
if (typeof explicit === 'string' && explicit.trim())
|
|
487
|
+
return explicit.trim();
|
|
488
|
+
return parseMcpToolName(toolName)?.serverName;
|
|
489
|
+
}
|
|
490
|
+
function extractText(content) {
|
|
491
|
+
if (typeof content === 'string')
|
|
492
|
+
return content;
|
|
493
|
+
if (Array.isArray(content)) {
|
|
494
|
+
return content
|
|
495
|
+
.map((block) => (isRecord(block) ? blockText(block) : ''))
|
|
496
|
+
.filter(Boolean)
|
|
497
|
+
.join('\n');
|
|
498
|
+
}
|
|
499
|
+
if (content == null)
|
|
500
|
+
return '';
|
|
501
|
+
return stringifyValue(content);
|
|
502
|
+
}
|
|
503
|
+
function blockText(block) {
|
|
504
|
+
for (const key of ['text', 'thinking', 'content']) {
|
|
505
|
+
const value = block[key];
|
|
506
|
+
if (typeof value === 'string')
|
|
507
|
+
return value;
|
|
508
|
+
}
|
|
509
|
+
return '';
|
|
510
|
+
}
|
|
511
|
+
function stringifyValue(value) {
|
|
512
|
+
if (value == null)
|
|
513
|
+
return '';
|
|
514
|
+
if (typeof value === 'string')
|
|
515
|
+
return value;
|
|
516
|
+
try {
|
|
517
|
+
return JSON.stringify(value, null, 2);
|
|
518
|
+
}
|
|
519
|
+
catch {
|
|
520
|
+
return String(value);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function stringValue(value) {
|
|
524
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
525
|
+
}
|
|
526
|
+
function clean(text) {
|
|
527
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
528
|
+
}
|
|
529
|
+
function truncateClean(text, maxChars) {
|
|
530
|
+
return truncateWithInfo(text, maxChars).text;
|
|
531
|
+
}
|
|
532
|
+
function truncateWithInfo(text, maxChars) {
|
|
533
|
+
if (!Number.isFinite(maxChars) || maxChars <= 0)
|
|
534
|
+
return { text: '', truncated: text.length > 0 };
|
|
535
|
+
if (text.length <= maxChars)
|
|
536
|
+
return { text, truncated: false };
|
|
537
|
+
if (maxChars <= 3)
|
|
538
|
+
return { text: text.slice(0, maxChars), truncated: true };
|
|
539
|
+
return { text: `${text.slice(0, maxChars - 3)}...`, truncated: true };
|
|
540
|
+
}
|
|
541
|
+
function estimateTokens(text) {
|
|
542
|
+
return estimateTextTokens(text).count;
|
|
543
|
+
}
|
|
544
|
+
function isRecord(value) {
|
|
545
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
546
|
+
}
|