sidekick-shared 0.24.0 → 0.24.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.
package/README.md CHANGED
@@ -69,17 +69,17 @@ npm install sidekick-shared
69
69
 
70
70
  `sidekick-shared` ships three public entry points plus a few convenience subpaths. Pick the one that matches your runtime.
71
71
 
72
- | Path | Runtime | What it exposes |
73
- | ------------------------------ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
74
- | `sidekick-shared` | Node (CLI, extension host) | Full public API (readers, providers, parsers, pricing, …). |
75
- | `sidekick-shared/browser` | **Browser / webview** | Pure helpers: usage normalization/pricing, token estimation, transcript and assistant-turn projection, model/context math. |
76
- | `sidekick-shared/node` | Node only | LiteLLM pricing hydration, recent-session/transcript reads, and resilient observed-session collection (`fs` + providers). |
77
- | `sidekick-shared/phrases` | Any runtime | Phrase arrays + `getRandomPhrase()`. |
78
- | `sidekick-shared/modelContext` | Any runtime | Direct access to the context-window module. |
79
- | `sidekick-shared/modelInfo` | Any runtime | Direct access to model parsing and cost math. |
80
- | `sidekick-shared/formatting` | Any runtime | Direct access to pure token and duration display helpers. |
81
- | `sidekick-shared/schemas` | Any runtime | Pure Zod boundary schemas (session events, assistant turns, observed-session V1, quota, account status, quota history) — fs-free, no Node builtins. |
82
- | `sidekick-shared/statusline` | Node only | Cache-only statusline surface: account status + quota snapshot readers, `formatStatusline()`, and burn-rate estimates. |
72
+ | Path | Runtime | What it exposes |
73
+ | ------------------------------ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
74
+ | `sidekick-shared` | Node (CLI, extension host) | Full public API (readers, providers, parsers, pricing, …). |
75
+ | `sidekick-shared/browser` | **Browser / webview** | Pure helpers: usage normalization/pricing, token estimation, transcript and assistant-turn projection, model/context math. |
76
+ | `sidekick-shared/node` | Node only | LiteLLM pricing and context-window hydration, observed context-window persistence, recent-session/transcript reads, and resilient observed-session collection (`fs` + providers). |
77
+ | `sidekick-shared/phrases` | Any runtime | Phrase arrays + `getRandomPhrase()`. |
78
+ | `sidekick-shared/modelContext` | Any runtime | Direct access to the context-window module. |
79
+ | `sidekick-shared/modelInfo` | Any runtime | Direct access to model parsing and cost math. |
80
+ | `sidekick-shared/formatting` | Any runtime | Direct access to pure token and duration display helpers. |
81
+ | `sidekick-shared/schemas` | Any runtime | Pure Zod boundary schemas (session events, assistant turns, observed-session V1, quota, account status, quota history) — fs-free, no Node builtins. |
82
+ | `sidekick-shared/statusline` | Node only | Cache-only statusline surface: account status + quota snapshot readers, `formatStatusline()`, and burn-rate estimates. |
83
83
 
84
84
  ### Browser / webview runtimes
85
85
 
@@ -103,14 +103,23 @@ import {
103
103
 
104
104
  ### Node / CLI / extension host
105
105
 
106
- Hydrate the pricing catalog from the `node` subpath:
106
+ Hydrate the model catalog from the `node` subpath. One fetch covers both prices and context window sizes:
107
107
 
108
108
  ```typescript
109
- import { hydratePricingCatalog } from 'sidekick-shared/node';
109
+ import { hydratePricingCatalog, loadObservedContextWindows } from 'sidekick-shared/node';
110
110
 
111
- await hydratePricingCatalog({ cacheDir: '~/.config/sidekick' });
111
+ const { entries, contextWindowEntries } = await hydratePricingCatalog({
112
+ cacheDir: '~/.config/sidekick',
113
+ });
114
+
115
+ // Context windows a provider reported on an earlier run. These outrank the
116
+ // catalog, which only knows each model's published maximum — Codex reports the
117
+ // window your account tier actually gets. Local read, offline-safe.
118
+ await loadObservedContextWindows();
112
119
  ```
113
120
 
121
+ Both calls always resolve; a network failure, missing cache, or malformed store leaves lookups on the static tables. To record a window a provider reports at runtime, call `recordObservedContextWindow(modelId, window)` — it persists only when the value changes.
122
+
114
123
  ### Normalize and price provider usage
115
124
 
116
125
  ```typescript
@@ -165,6 +174,18 @@ const transcript = readSessionTranscript(provider, recent.sessionPath, {
165
174
 
166
175
  The same API works with `ClaudeCodeProvider` and `OpenCodeProvider`. It covers provider sessions discoverable by those readers. Remote Claude SDK-only history and Codex history entries whose rollout file has disappeared are not synthesized.
167
176
 
177
+ Canonical transcript messages retain provider-origin facts on `message.source`, including the original role, entrypoint, meta/sidechain flags, and source identifiers. Transcript-level `cwd` and `gitBranch` use the latest non-empty value reported by the provider. Consumers can therefore distinguish a human Claude Code CLI prompt without reparsing JSONL:
178
+
179
+ ```typescript
180
+ const humanCliPrompts = transcript.messages.filter(
181
+ (message) =>
182
+ message.source.originalRole === 'user' &&
183
+ message.source.entrypoint === 'cli' &&
184
+ message.source.isMeta !== true &&
185
+ message.source.isSidechain !== true,
186
+ );
187
+ ```
188
+
168
189
  For an already-open incremental Codex rollout, keep one `CodexRolloutParser` per stream and pass each parsed `SessionEvent[]` batch to `projectSessionTranscript()` (or append the events to an existing projection). This preserves parser state for paired tool calls, model context, and cumulative token deltas; consumers should not convert raw `response_item` rows themselves.
169
190
 
170
191
  ### Collect observed sessions with isolation and retry
@@ -181,6 +202,13 @@ const collector = new ObservedSessionCollector({
181
202
  await collector.collect(); // scheduling remains the host's responsibility
182
203
  ```
183
204
 
205
+ Each provider-backed source also exposes the same adapter's `ProviderCapabilitiesV1` record used for its reads and lifecycle:
206
+
207
+ ```typescript
208
+ const source = observedSessionSourceFromProvider(provider, workspacePath);
209
+ applyReadOnlyProjection(source.capabilities);
210
+ ```
211
+
184
212
  Discovery and reads are isolated, retries use bounded 30-second-to-5-minute exponential backoff, file changes bypass delay, duplicate identical failures are suppressed, and recovery emits a content-free diagnostic. Clock and fingerprint callbacks are injectable for deterministic hosts/tests.
185
213
 
186
214
  ## Usage Examples
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export type { TranscriptFidelity, TranscriptSourceProvenance, CanonicalTranscrip
10
10
  export { listRecentSessions, readSessionTranscript } from './sessionTranscripts';
11
11
  export type { ProviderSessionIndex, ListRecentSessionsOptions } from './sessionTranscripts';
12
12
  export { ObservedSessionCollector, observedSessionSourceFromProvider, fileFingerprint, } from './observedSessionCollector';
13
- export type { ObservedSessionReference, ObservedSessionCollectionSource, ObservedSessionDiagnosticKind, ObservedSessionDiagnostic, ObservedSessionCollection, ObservedSessionCollectorOptions, } from './observedSessionCollector';
13
+ export type { ObservedSessionReference, ObservedSessionCollectionSource, ProviderObservedSessionCollectionSource, ObservedSessionDiagnosticKind, ObservedSessionDiagnostic, ObservedSessionCollection, ObservedSessionCollectorOptions, } from './observedSessionCollector';
14
14
  export type { SessionEvent, ClaudeSessionEvent, MessageUsage, SessionMessage, SessionEventBase, MessageSessionEvent, UserSessionEvent, AssistantSessionEvent, SummarySessionEvent, SystemSessionEvent, ToolUseSessionEvent, ToolResultSessionEvent, TokenUsage, ToolCall, ToolAnalytics, TimelineEvent, PendingToolCall, TaskStatus as SessionTaskStatus, TrackedTask, TaskState, SubagentStats, PendingUserRequest, ResponseLatency, LatencyStats, PlanStep, PlanState, SessionStats, CompactionEvent, TruncationEvent, ContextAttribution, TurnAttribution, ContextSizePoint, PermissionMode, PermissionModeChange, } from './types/sessionEvent';
15
15
  export type { OpenCodeSession, OpenCodeMessage, OpenCodePart, OpenCodeTextPart, OpenCodeReasoningPart, OpenCodeToolInvocationPart, OpenCodeCompactionPart, OpenCodeDbToolPart, OpenCodeStepStartPart, OpenCodeStepFinishPart, OpenCodePatchPart, OpenCodeSubtaskPart, OpenCodeAgentPart, OpenCodeFilePart, OpenCodeRetryPart, OpenCodeSnapshotPart, OpenCodeProject, DbProject as OpenCodeDbProject, DbSession as OpenCodeDbSession, DbMessage as OpenCodeDbMessage, DbPart as OpenCodeDbPart, } from './types/opencode';
16
16
  export type { CodexRolloutLine, CodexSessionMeta, CodexResponseItem, CodexMessageItem, CodexContentPart, CodexReasoningItem, CodexFunctionCallItem, CodexFunctionCallOutputItem, CodexLocalShellCallItem, CodexWebSearchCallItem, CodexCustomToolCallItem, CodexCustomToolCallOutputItem, CodexCompacted, CodexTurnContext, CodexEventMsg, CodexEvent, CodexTurnStartedEvent, CodexTurnCompleteEvent, CodexTaskStartedEvent, CodexTaskCompleteEvent, CodexTurnAbortedEvent, CodexTokenCountEvent, CodexTokenUsage, CodexRateLimits, CodexAgentMessageEvent, CodexAgentReasoningEvent, CodexUserMessageEvent, CodexExecCommandBeginEvent, CodexExecCommandEndEvent, CodexMcpToolCallBeginEvent, CodexMcpToolCallEndEvent, CodexErrorEvent, CodexContextCompactedEvent, CodexPatchAppliedEvent, CodexBackgroundEvent, CodexGenericEvent, CodexDbThread, CodexReasoningSummary, } from './types/codex';
@@ -164,8 +164,10 @@ export type { StatuslineInput, StatuslineSelection } from './statusline/formatte
164
164
  export { getModelContextWindowSize, DEFAULT_CONTEXT_WINDOW } from './modelContext';
165
165
  export { parseModelId, getModelPricing, getModelInfo, calculateCost, calculateCostWithPricing, calculateCostWithProvenance, mergeCostSources, shortModelName, getModelDisplayInfo, compareModelIds, sortModelIds, formatCost, } from './modelInfo';
166
166
  export type { ModelPricing, CostTokenUsage, CostSource, CostProvenanceInput, CostWithProvenance, ModelInfo, ModelProvider, ParsedModelId, ModelDisplayInfo, } from './modelInfo';
167
- export { hydratePricingCatalog, normalizeLiteLlmCatalog, LITELLM_CATALOG_URL, } from './pricingCatalog';
167
+ export { hydratePricingCatalog, normalizeLiteLlmCatalog, normalizeLiteLlmContextWindows, LITELLM_CATALOG_URL, } from './pricingCatalog';
168
168
  export type { HydrateOptions, HydrateResult } from './pricingCatalog';
169
+ export { loadObservedContextWindows, recordObservedContextWindow, getObservedContextWindowPath, } from './observedContextWindows';
170
+ export type { ObservedContextWindowOptions, ObservedContextWindowStore, } from './observedContextWindows';
169
171
  export { extractTokenUsage } from './extractors/tokenUsage';
170
172
  export { extractUrls, extractFilePaths, extractCommands } from './extractors/sessionAssets';
171
173
  export type { AssetAgent, ExtractedAsset, ExtractedAssetProvenance, ExtractedAssetType, ExtractedAssets, SourceAssets, } from './extractors/sessionAssets';
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ exports.createSessionContextProjector = exports.calculateSessionContextPressure
8
8
  exports.scoreSessionQuality = exports.calculateQualityTrend = exports.PatternExtractor = exports.HeatmapTracker = exports.FrequencyTracker = exports.readErrorHistory = exports.getTopFailingTools = exports.appendErrorHistory = exports.MAX_ERROR_HISTORY_SESSIONS = exports.ERROR_HISTORY_SCHEMA_VERSION = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.SessionMonitor = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.PHRASE_CATEGORIES = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = exports.highlightEvent = exports.formatSessionJson = exports.formatSessionMarkdown = exports.formatSessionText = exports.classifyNoise = exports.shouldMergeWithPrevious = exports.classifyFollowEvent = exports.classifyMessage = exports.getSoftNoiseReason = exports.isHardNoiseFollowEvent = exports.isHardNoise = exports.formatToolSummary = exports.formatTokenCount = exports.formatDurationMs = exports.createJsonlTail = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.readSessionContextSnapshot = void 0;
9
9
  exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.readClaudeProfileIdentity = exports.isClaudeProfileAuthenticated = exports.claudeKeychainService = exports.claudeKeychainSuffix = exports.getClaudeProfileHome = exports.getClaudeProfilesDir = exports.getActiveAccountStatus = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.resolveClaudeLoginCommand = exports.listAllAccounts = exports.switchAccount = exports.spawnAccountLogin = exports.finalizeAccountLogin = exports.getAccountLoginStatus = exports.beginAccountLogin = exports.isMultiAccountEnabled = exports.resolveActiveClaudeAccount = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.reconcileClaudeAuthState = exports.applyActiveClaudeToLiveHome = exports.resolveActiveClaudeHome = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscriptFromEvents = exports.parseTranscript = exports.generateHtmlReport = exports.formatCompactionLedger = exports.calculateCompactionLedger = exports.calculateCodeImpact = void 0;
10
10
  exports.selectStatuslineAccount = exports.formatStatusline = exports.estimateTimeToQuota = exports.BurnRateCalculator = exports.MultiProviderQuotaService = exports.resolveZaiQuota = exports.readZaiCredentials = exports.quotaStateFromZaiQuotaLimitPayload = exports.fetchZaiQuotaFromApi = exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = exports.resolveCodexQuota = exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.fetchCodexResetCreditsFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.withQuotaProjections = exports.projectQuotaWindow = exports.fetchQuota = exports.SEVEN_DAY_WINDOW_MS = exports.FIVE_HOUR_WINDOW_MS = exports.AutoSwitchController = exports.decideAutoSwitch = exports.DEFAULT_AUTO_SWITCH_CONFIG = exports.removeLauncher = exports.writeLauncher = exports.isShellHookInstalled = exports.uninstallShellHook = exports.installShellHook = exports.setTerminalActiveProfile = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.resolveActiveCodexAccount = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = void 0;
11
- exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.codexResetCreditsSnapshotSchema = exports.codexResetCreditSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.ToolCallTracker = exports.extractToolCalls = exports.extractToolCall = exports.extractErrorMessage = exports.categorizeError = exports.gatherAssetsForCwd = exports.codexSessions = exports.readCodexAssets = exports.claudeSessions = exports.readClaudeAssets = exports.extractCommands = exports.extractFilePaths = exports.extractUrls = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = void 0;
12
- exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.runDoctor = exports.getSessionDiagnostics = exports.formatHealthReport = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = void 0;
11
+ exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.codexResetCreditsSnapshotSchema = exports.codexResetCreditSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.ToolCallTracker = exports.extractToolCalls = exports.extractToolCall = exports.extractErrorMessage = exports.categorizeError = exports.gatherAssetsForCwd = exports.codexSessions = exports.readCodexAssets = exports.claudeSessions = exports.readClaudeAssets = exports.extractCommands = exports.extractFilePaths = exports.extractUrls = exports.extractTokenUsage = exports.getObservedContextWindowPath = exports.recordObservedContextWindow = exports.loadObservedContextWindows = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmContextWindows = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = void 0;
12
+ exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.runDoctor = exports.getSessionDiagnostics = exports.formatHealthReport = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = void 0;
13
13
  var usageNormalization_1 = require("./usageNormalization");
14
14
  Object.defineProperty(exports, "normalizeProviderUsage", { enumerable: true, get: function () { return usageNormalization_1.normalizeProviderUsage; } });
15
15
  Object.defineProperty(exports, "extractNormalizedUsage", { enumerable: true, get: function () { return usageNormalization_1.extractNormalizedUsage; } });
@@ -397,7 +397,13 @@ Object.defineProperty(exports, "formatCost", { enumerable: true, get: function (
397
397
  var pricingCatalog_1 = require("./pricingCatalog");
398
398
  Object.defineProperty(exports, "hydratePricingCatalog", { enumerable: true, get: function () { return pricingCatalog_1.hydratePricingCatalog; } });
399
399
  Object.defineProperty(exports, "normalizeLiteLlmCatalog", { enumerable: true, get: function () { return pricingCatalog_1.normalizeLiteLlmCatalog; } });
400
+ Object.defineProperty(exports, "normalizeLiteLlmContextWindows", { enumerable: true, get: function () { return pricingCatalog_1.normalizeLiteLlmContextWindows; } });
400
401
  Object.defineProperty(exports, "LITELLM_CATALOG_URL", { enumerable: true, get: function () { return pricingCatalog_1.LITELLM_CATALOG_URL; } });
402
+ // Observed context windows (provider-reported, persisted) — Node-only.
403
+ var observedContextWindows_1 = require("./observedContextWindows");
404
+ Object.defineProperty(exports, "loadObservedContextWindows", { enumerable: true, get: function () { return observedContextWindows_1.loadObservedContextWindows; } });
405
+ Object.defineProperty(exports, "recordObservedContextWindow", { enumerable: true, get: function () { return observedContextWindows_1.recordObservedContextWindow; } });
406
+ Object.defineProperty(exports, "getObservedContextWindowPath", { enumerable: true, get: function () { return observedContextWindows_1.getObservedContextWindowPath; } });
401
407
  // Extractors — per-event token usage and tool call extraction
402
408
  var tokenUsage_1 = require("./extractors/tokenUsage");
403
409
  Object.defineProperty(exports, "extractTokenUsage", { enumerable: true, get: function () { return tokenUsage_1.extractTokenUsage; } });
@@ -1,19 +1,66 @@
1
1
  /**
2
2
  * Centralized model ID → context window size lookup.
3
3
  * Single source of truth used by all providers and the CLI dashboard.
4
+ *
5
+ * Resolution is layered, most-trusted first:
6
+ * 1. Runtime-reported — providers that self-report their window (Codex) apply
7
+ * it in `getContextWindowLimit`, ahead of this module.
8
+ * 2. Observed overrides — the last window a provider actually reported for a
9
+ * model, persisted across runs by `observedContextWindows.ts`. Reflects the
10
+ * effective window for this account/tier, which can be well below the
11
+ * model's published maximum.
12
+ * 3. Catalog overrides — `max_input_tokens` from the LiteLLM catalog, hydrated
13
+ * by `pricingCatalog.ts`. Keeps new models correct without a code change.
14
+ * 4. The static table below — offline baseline of last resort.
15
+ *
16
+ * Within that order, match quality comes first: every layer is tried for an
17
+ * exact hit before any layer is tried for a prefix hit. See
18
+ * `getModelContextWindowSize`.
19
+ *
20
+ * Layers 2 and 3 are pushed in by Node-only modules through the `_set*` hooks,
21
+ * so this file stays browser-safe (no node:fs, no fetch) and can be bundled for
22
+ * webviews. Same pattern as `_setPricingOverrides` in `modelInfo.ts`.
4
23
  */
5
24
  /** Default context window size when model is unknown. */
6
25
  export declare const DEFAULT_CONTEXT_WINDOW = 200000;
26
+ /**
27
+ * Internal: replace the observed-context-window map. Called by
28
+ * `observedContextWindows.ts` on load and on each newly observed value.
29
+ * Not part of the stable public API.
30
+ */
31
+ export declare function _setObservedContextWindows(overrides: Record<string, number>): void;
32
+ /** Internal: snapshot observed overrides (test + diagnostics). */
33
+ export declare function _getObservedContextWindows(): Record<string, number>;
34
+ /** Internal: clear observed overrides (used by tests). */
35
+ export declare function _clearObservedContextWindows(): void;
36
+ /**
37
+ * Internal: replace the catalog-context-window map. Called by
38
+ * `pricingCatalog.ts` after a successful LiteLLM hydration.
39
+ * Not part of the stable public API.
40
+ */
41
+ export declare function _setCatalogContextWindows(overrides: Record<string, number>): void;
42
+ /** Internal: snapshot catalog overrides (test + diagnostics). */
43
+ export declare function _getCatalogContextWindows(): Record<string, number>;
44
+ /** Internal: clear catalog overrides (used by tests and when disabling hydration). */
45
+ export declare function _clearCatalogContextWindows(): void;
7
46
  /**
8
47
  * Returns the context window size for a model ID.
9
48
  *
10
49
  * Input is trimmed and lowercased before lookup, so padded or mixed-case
11
- * IDs (e.g. "Claude-Opus-4-8 ") resolve without caller-side normalization.
50
+ * IDs (e.g. "Claude-Opus-5 ") resolve without caller-side normalization.
12
51
  *
13
52
  * Lookup order:
14
53
  * 1. Explicit "[1m]" suffix (Claude Code's 1M-variant marker) → 1_000_000
15
- * 2. Exact match against MODEL_CONTEXT_SIZES
16
- * 3. Longest-prefix match (e.g. "claude-opus-4-6-20250414" → "claude-opus-4-6")
54
+ * 2. Exact match, most-trusted layer first: observed → catalog → static
55
+ * 3. Longest-prefix match, same layer order
56
+ * (e.g. "claude-opus-4-6-20250414" → "claude-opus-4-6")
17
57
  * 4. DEFAULT_CONTEXT_WINDOW
58
+ *
59
+ * Match quality outranks layer trust: an exact hit in *any* layer beats a
60
+ * prefix guess in *every* layer. Resolving each layer fully before moving on
61
+ * would let a catalog key that merely happens to be a prefix shadow a curated
62
+ * static entry — e.g. the catalog has no "claude-sonnet-4-7", and prefix-first
63
+ * would hand it "claude-sonnet-4" (128K, a GitHub Copilot deployment) instead
64
+ * of the static table's exact 1M.
18
65
  */
19
66
  export declare function getModelContextWindowSize(modelId?: string): number;
@@ -2,17 +2,44 @@
2
2
  /**
3
3
  * Centralized model ID → context window size lookup.
4
4
  * Single source of truth used by all providers and the CLI dashboard.
5
+ *
6
+ * Resolution is layered, most-trusted first:
7
+ * 1. Runtime-reported — providers that self-report their window (Codex) apply
8
+ * it in `getContextWindowLimit`, ahead of this module.
9
+ * 2. Observed overrides — the last window a provider actually reported for a
10
+ * model, persisted across runs by `observedContextWindows.ts`. Reflects the
11
+ * effective window for this account/tier, which can be well below the
12
+ * model's published maximum.
13
+ * 3. Catalog overrides — `max_input_tokens` from the LiteLLM catalog, hydrated
14
+ * by `pricingCatalog.ts`. Keeps new models correct without a code change.
15
+ * 4. The static table below — offline baseline of last resort.
16
+ *
17
+ * Within that order, match quality comes first: every layer is tried for an
18
+ * exact hit before any layer is tried for a prefix hit. See
19
+ * `getModelContextWindowSize`.
20
+ *
21
+ * Layers 2 and 3 are pushed in by Node-only modules through the `_set*` hooks,
22
+ * so this file stays browser-safe (no node:fs, no fetch) and can be bundled for
23
+ * webviews. Same pattern as `_setPricingOverrides` in `modelInfo.ts`.
5
24
  */
6
25
  Object.defineProperty(exports, "__esModule", { value: true });
7
26
  exports.DEFAULT_CONTEXT_WINDOW = void 0;
27
+ exports._setObservedContextWindows = _setObservedContextWindows;
28
+ exports._getObservedContextWindows = _getObservedContextWindows;
29
+ exports._clearObservedContextWindows = _clearObservedContextWindows;
30
+ exports._setCatalogContextWindows = _setCatalogContextWindows;
31
+ exports._getCatalogContextWindows = _getCatalogContextWindows;
32
+ exports._clearCatalogContextWindows = _clearCatalogContextWindows;
8
33
  exports.getModelContextWindowSize = getModelContextWindowSize;
9
34
  /** Known model context window sizes (in tokens). */
10
35
  const MODEL_CONTEXT_SIZES = {
11
36
  // Claude — native 1M context (Fable 5, Opus 4.6+, Sonnet 4.6+)
12
37
  'claude-fable-5': 1_000_000,
38
+ 'claude-opus-5': 1_000_000,
13
39
  'claude-opus-4-8': 1_000_000,
14
40
  'claude-opus-4-7': 1_000_000,
15
41
  'claude-opus-4-6': 1_000_000,
42
+ 'claude-sonnet-5': 1_000_000,
16
43
  'claude-sonnet-4-7': 1_000_000,
17
44
  'claude-sonnet-4-6': 1_000_000,
18
45
  // Claude 4 family
@@ -30,7 +57,14 @@ const MODEL_CONTEXT_SIZES = {
30
57
  // OpenAI GPT-4.1 series (1M context)
31
58
  'gpt-4.1': 1_048_576,
32
59
  // OpenAI GPT-5 series (keys sorted longest-first below; explicit entries
33
- // for every variant so prefix matching can't misclassify a new one)
60
+ // for every variant so prefix matching can't misclassify a new one).
61
+ // These are published model maxima. Codex reports a smaller *effective*
62
+ // window per account tier via token_count; that value wins through the
63
+ // observed-override layer.
64
+ 'gpt-5.6-sol': 1_050_000,
65
+ 'gpt-5.6-terra': 1_050_000,
66
+ 'gpt-5.6': 1_050_000,
67
+ 'gpt-5.5': 1_050_000,
34
68
  'gpt-5.4': 1_050_000,
35
69
  'gpt-5.3-codex-spark': 128_000,
36
70
  'gpt-5.3-codex': 400_000,
@@ -49,20 +83,100 @@ const MODEL_CONTEXT_SIZES = {
49
83
  deepseek: 128_000,
50
84
  };
51
85
  /** Prefix keys sorted longest-first for correct prefix matching. */
52
- const SORTED_KEYS = Object.keys(MODEL_CONTEXT_SIZES).sort((a, b) => b.length - a.length);
86
+ const SORTED_KEYS = sortKeysLongestFirst(MODEL_CONTEXT_SIZES);
53
87
  /** Default context window size when model is unknown. */
54
88
  exports.DEFAULT_CONTEXT_WINDOW = 200_000;
89
+ // ── Override Tables (populated at runtime by Node-only modules) ──
90
+ let observedTable = {};
91
+ let observedSortedKeys = [];
92
+ let catalogTable = {};
93
+ let catalogSortedKeys = [];
94
+ function sortKeysLongestFirst(table) {
95
+ return Object.keys(table).sort((a, b) => b.length - a.length);
96
+ }
97
+ /**
98
+ * Lowercase keys so lookups match regardless of how the source spelled them.
99
+ * The LiteLLM catalog publishes some IDs mixed-case; the static table and the
100
+ * lookup path are both lowercase. Drops non-positive and non-finite values.
101
+ */
102
+ function normalizeOverrides(overrides) {
103
+ const out = {};
104
+ for (const [key, value] of Object.entries(overrides)) {
105
+ if (!Number.isFinite(value) || value <= 0)
106
+ continue;
107
+ out[key.trim().toLowerCase()] = value;
108
+ }
109
+ return out;
110
+ }
111
+ /**
112
+ * Internal: replace the observed-context-window map. Called by
113
+ * `observedContextWindows.ts` on load and on each newly observed value.
114
+ * Not part of the stable public API.
115
+ */
116
+ function _setObservedContextWindows(overrides) {
117
+ observedTable = normalizeOverrides(overrides);
118
+ observedSortedKeys = sortKeysLongestFirst(observedTable);
119
+ }
120
+ /** Internal: snapshot observed overrides (test + diagnostics). */
121
+ function _getObservedContextWindows() {
122
+ return { ...observedTable };
123
+ }
124
+ /** Internal: clear observed overrides (used by tests). */
125
+ function _clearObservedContextWindows() {
126
+ observedTable = {};
127
+ observedSortedKeys = [];
128
+ }
129
+ /**
130
+ * Internal: replace the catalog-context-window map. Called by
131
+ * `pricingCatalog.ts` after a successful LiteLLM hydration.
132
+ * Not part of the stable public API.
133
+ */
134
+ function _setCatalogContextWindows(overrides) {
135
+ catalogTable = normalizeOverrides(overrides);
136
+ catalogSortedKeys = sortKeysLongestFirst(catalogTable);
137
+ }
138
+ /** Internal: snapshot catalog overrides (test + diagnostics). */
139
+ function _getCatalogContextWindows() {
140
+ return { ...catalogTable };
141
+ }
142
+ /** Internal: clear catalog overrides (used by tests and when disabling hydration). */
143
+ function _clearCatalogContextWindows() {
144
+ catalogTable = {};
145
+ catalogSortedKeys = [];
146
+ }
147
+ // ── Lookup ──
148
+ /** Exact match against one table. */
149
+ function exactWindow(table, modelId) {
150
+ const hit = table[modelId];
151
+ return hit === undefined ? null : hit;
152
+ }
153
+ /** Longest-prefix match against one table. */
154
+ function prefixWindow(table, sortedKeys, modelId) {
155
+ for (const key of sortedKeys) {
156
+ if (modelId.startsWith(key))
157
+ return table[key];
158
+ }
159
+ return null;
160
+ }
55
161
  /**
56
162
  * Returns the context window size for a model ID.
57
163
  *
58
164
  * Input is trimmed and lowercased before lookup, so padded or mixed-case
59
- * IDs (e.g. "Claude-Opus-4-8 ") resolve without caller-side normalization.
165
+ * IDs (e.g. "Claude-Opus-5 ") resolve without caller-side normalization.
60
166
  *
61
167
  * Lookup order:
62
168
  * 1. Explicit "[1m]" suffix (Claude Code's 1M-variant marker) → 1_000_000
63
- * 2. Exact match against MODEL_CONTEXT_SIZES
64
- * 3. Longest-prefix match (e.g. "claude-opus-4-6-20250414" → "claude-opus-4-6")
169
+ * 2. Exact match, most-trusted layer first: observed → catalog → static
170
+ * 3. Longest-prefix match, same layer order
171
+ * (e.g. "claude-opus-4-6-20250414" → "claude-opus-4-6")
65
172
  * 4. DEFAULT_CONTEXT_WINDOW
173
+ *
174
+ * Match quality outranks layer trust: an exact hit in *any* layer beats a
175
+ * prefix guess in *every* layer. Resolving each layer fully before moving on
176
+ * would let a catalog key that merely happens to be a prefix shadow a curated
177
+ * static entry — e.g. the catalog has no "claude-sonnet-4-7", and prefix-first
178
+ * would hand it "claude-sonnet-4" (128K, a GitHub Copilot deployment) instead
179
+ * of the static table's exact 1M.
66
180
  */
67
181
  function getModelContextWindowSize(modelId) {
68
182
  if (!modelId)
@@ -72,21 +186,24 @@ function getModelContextWindowSize(modelId) {
72
186
  if (/\[1m\]/i.test(modelId))
73
187
  return 1_000_000;
74
188
  // Strip the suffix if present, so the normal lookup still succeeds when
75
- // a caller passes e.g. "claude-opus-4-7[1m]" and we've already handled it.
189
+ // a caller passes e.g. "claude-opus-5[1m]" and we've already handled it.
76
190
  // Trim/lowercase so padded or mixed-case IDs match the lowercase table keys.
77
191
  const normalized = modelId
78
192
  .replace(/\[1m\]/gi, '')
79
193
  .trim()
80
194
  .toLowerCase();
81
- // Exact match
82
- if (MODEL_CONTEXT_SIZES[normalized] !== undefined) {
83
- return MODEL_CONTEXT_SIZES[normalized];
84
- }
85
- // Longest-prefix match
86
- for (const key of SORTED_KEYS) {
87
- if (normalized.startsWith(key)) {
88
- return MODEL_CONTEXT_SIZES[key];
89
- }
90
- }
195
+ // Pass 1 — exact matches, most-trusted layer first.
196
+ const exact = exactWindow(observedTable, normalized) ??
197
+ exactWindow(catalogTable, normalized) ??
198
+ exactWindow(MODEL_CONTEXT_SIZES, normalized);
199
+ if (exact !== null)
200
+ return exact;
201
+ // Pass 2 — longest-prefix matches, same layer order. Resolves versioned IDs
202
+ // that no layer spells out, e.g. "claude-opus-4-6-20250414".
203
+ const prefix = prefixWindow(observedTable, observedSortedKeys, normalized) ??
204
+ prefixWindow(catalogTable, catalogSortedKeys, normalized) ??
205
+ prefixWindow(MODEL_CONTEXT_SIZES, SORTED_KEYS, normalized);
206
+ if (prefix !== null)
207
+ return prefix;
91
208
  return exports.DEFAULT_CONTEXT_WINDOW;
92
209
  }
package/dist/modelInfo.js CHANGED
@@ -76,6 +76,14 @@ const PRICING_TABLE = {
76
76
  cacheWriteCostPerMillion: 1.0,
77
77
  cacheReadCostPerMillion: 0.08,
78
78
  },
79
+ // Sonnet 5 carries the standard rate here. The introductory $2/$10 (through
80
+ // 2026-08-31) arrives via catalog hydration, which supersedes this baseline.
81
+ 'claude-sonnet-5': {
82
+ inputCostPerMillion: 3.0,
83
+ outputCostPerMillion: 15.0,
84
+ cacheWriteCostPerMillion: 3.75,
85
+ cacheReadCostPerMillion: 0.3,
86
+ },
79
87
  'claude-sonnet-4-6': {
80
88
  inputCostPerMillion: 3.0,
81
89
  outputCostPerMillion: 15.0,
@@ -106,6 +114,12 @@ const PRICING_TABLE = {
106
114
  cacheWriteCostPerMillion: 3.75,
107
115
  cacheReadCostPerMillion: 0.3,
108
116
  },
117
+ 'claude-opus-5': {
118
+ inputCostPerMillion: 5.0,
119
+ outputCostPerMillion: 25.0,
120
+ cacheWriteCostPerMillion: 6.25,
121
+ cacheReadCostPerMillion: 0.5,
122
+ },
109
123
  'claude-opus-4-8': {
110
124
  inputCostPerMillion: 5.0,
111
125
  outputCostPerMillion: 25.0,
@@ -186,14 +200,45 @@ const PRICING_TABLE = {
186
200
  cacheWriteCostPerMillion: 0,
187
201
  cacheReadCostPerMillion: 0,
188
202
  },
189
- // ── OpenAI: GPT-5 family (baseline estimate; LiteLLM catalog overrides) ──
190
- // Codex emits `gpt-5.4`, `gpt-5.3-codex`, `gpt-5`. Anchoring the static
191
- // estimate to gpt-4o-tier rates so offline users see a reasonable ballpark.
203
+ // ── OpenAI: GPT-5 family ──
204
+ // Published rates, verified against the LiteLLM catalog. Runtime hydration
205
+ // still supersedes these; they are the offline baseline. Entries below
206
+ // `gpt-5.4` predate the published-rate pass and remain rough estimates.
207
+ 'gpt-5.6-sol': {
208
+ inputCostPerMillion: 5.0,
209
+ outputCostPerMillion: 30.0,
210
+ cacheWriteCostPerMillion: 6.25,
211
+ cacheReadCostPerMillion: 0.5,
212
+ },
213
+ 'gpt-5.6-terra': {
214
+ inputCostPerMillion: 2.5,
215
+ outputCostPerMillion: 15.0,
216
+ cacheWriteCostPerMillion: 3.13,
217
+ cacheReadCostPerMillion: 0.25,
218
+ },
219
+ 'gpt-5.6': {
220
+ inputCostPerMillion: 5.0,
221
+ outputCostPerMillion: 30.0,
222
+ cacheWriteCostPerMillion: 6.25,
223
+ cacheReadCostPerMillion: 0.5,
224
+ },
225
+ 'gpt-5.5': {
226
+ inputCostPerMillion: 5.0,
227
+ outputCostPerMillion: 30.0,
228
+ cacheWriteCostPerMillion: 0,
229
+ cacheReadCostPerMillion: 0.5,
230
+ },
231
+ 'gpt-5.4-mini': {
232
+ inputCostPerMillion: 0.75,
233
+ outputCostPerMillion: 4.5,
234
+ cacheWriteCostPerMillion: 0,
235
+ cacheReadCostPerMillion: 0.07,
236
+ },
192
237
  'gpt-5.4': {
193
- inputCostPerMillion: 1.25,
194
- outputCostPerMillion: 10.0,
238
+ inputCostPerMillion: 2.5,
239
+ outputCostPerMillion: 15.0,
195
240
  cacheWriteCostPerMillion: 0,
196
- cacheReadCostPerMillion: 0.125,
241
+ cacheReadCostPerMillion: 0.25,
197
242
  },
198
243
  'gpt-5.3-codex': {
199
244
  inputCostPerMillion: 1.25,
package/dist/node.d.ts CHANGED
@@ -5,9 +5,11 @@
5
5
  * and `node:path`; safe for extension-host and CLI consumers. Not safe
6
6
  * for browser bundles — use `sidekick-shared/browser` there.
7
7
  */
8
- export { hydratePricingCatalog, normalizeLiteLlmCatalog, LITELLM_CATALOG_URL, } from './pricingCatalog';
8
+ export { hydratePricingCatalog, normalizeLiteLlmCatalog, normalizeLiteLlmContextWindows, LITELLM_CATALOG_URL, } from './pricingCatalog';
9
9
  export type { HydrateOptions, HydrateResult } from './pricingCatalog';
10
+ export { loadObservedContextWindows, recordObservedContextWindow, getObservedContextWindowPath, } from './observedContextWindows';
11
+ export type { ObservedContextWindowOptions, ObservedContextWindowStore, } from './observedContextWindows';
10
12
  export { listRecentSessions, readSessionTranscript } from './sessionTranscripts';
11
13
  export type { ProviderSessionIndex, ListRecentSessionsOptions } from './sessionTranscripts';
12
14
  export { ObservedSessionCollector, observedSessionSourceFromProvider, fileFingerprint, } from './observedSessionCollector';
13
- export type { ObservedSessionReference, ObservedSessionCollectionSource, ObservedSessionDiagnosticKind, ObservedSessionDiagnostic, ObservedSessionCollection, ObservedSessionCollectorOptions, } from './observedSessionCollector';
15
+ export type { ObservedSessionReference, ObservedSessionCollectionSource, ProviderObservedSessionCollectionSource, ObservedSessionDiagnosticKind, ObservedSessionDiagnostic, ObservedSessionCollection, ObservedSessionCollectorOptions, } from './observedSessionCollector';
package/dist/node.js CHANGED
@@ -7,11 +7,16 @@
7
7
  * for browser bundles — use `sidekick-shared/browser` there.
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.fileFingerprint = exports.observedSessionSourceFromProvider = exports.ObservedSessionCollector = exports.readSessionTranscript = exports.listRecentSessions = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = void 0;
10
+ exports.fileFingerprint = exports.observedSessionSourceFromProvider = exports.ObservedSessionCollector = exports.readSessionTranscript = exports.listRecentSessions = exports.getObservedContextWindowPath = exports.recordObservedContextWindow = exports.loadObservedContextWindows = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmContextWindows = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = void 0;
11
11
  var pricingCatalog_1 = require("./pricingCatalog");
12
12
  Object.defineProperty(exports, "hydratePricingCatalog", { enumerable: true, get: function () { return pricingCatalog_1.hydratePricingCatalog; } });
13
13
  Object.defineProperty(exports, "normalizeLiteLlmCatalog", { enumerable: true, get: function () { return pricingCatalog_1.normalizeLiteLlmCatalog; } });
14
+ Object.defineProperty(exports, "normalizeLiteLlmContextWindows", { enumerable: true, get: function () { return pricingCatalog_1.normalizeLiteLlmContextWindows; } });
14
15
  Object.defineProperty(exports, "LITELLM_CATALOG_URL", { enumerable: true, get: function () { return pricingCatalog_1.LITELLM_CATALOG_URL; } });
16
+ var observedContextWindows_1 = require("./observedContextWindows");
17
+ Object.defineProperty(exports, "loadObservedContextWindows", { enumerable: true, get: function () { return observedContextWindows_1.loadObservedContextWindows; } });
18
+ Object.defineProperty(exports, "recordObservedContextWindow", { enumerable: true, get: function () { return observedContextWindows_1.recordObservedContextWindow; } });
19
+ Object.defineProperty(exports, "getObservedContextWindowPath", { enumerable: true, get: function () { return observedContextWindows_1.getObservedContextWindowPath; } });
15
20
  var sessionTranscripts_1 = require("./sessionTranscripts");
16
21
  Object.defineProperty(exports, "listRecentSessions", { enumerable: true, get: function () { return sessionTranscripts_1.listRecentSessions; } });
17
22
  Object.defineProperty(exports, "readSessionTranscript", { enumerable: true, get: function () { return sessionTranscripts_1.readSessionTranscript; } });
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Persisted record of context windows providers actually reported (Node-only).
3
+ *
4
+ * Some agents report the real context window for the running session — Codex
5
+ * emits `model_context_window` on every `token_count` event. That number is the
6
+ * *effective* window for the account and tier, which can sit well below the
7
+ * model's published maximum (e.g. 258,400 observed for `gpt-5.6-sol` against a
8
+ * catalog maximum of 1,050,000).
9
+ *
10
+ * A live session uses that value directly. This module keeps it across runs, so
11
+ * historical and static views don't fall back to a published maximum that would
12
+ * make the context gauge read far too empty.
13
+ *
14
+ * Precedence, most-trusted first: runtime → observed (here) → LiteLLM catalog →
15
+ * static table. See `modelContext.ts`.
16
+ *
17
+ * @module observedContextWindows
18
+ */
19
+ export interface ObservedContextWindowOptions {
20
+ /** Directory for the store (defaults to the Sidekick config dir). */
21
+ cacheDir?: string;
22
+ /** Optional logger (useful for diagnostics without pulling a logger dep). */
23
+ logger?: (msg: string) => void;
24
+ }
25
+ export interface ObservedContextWindowEntry {
26
+ contextWindow: number;
27
+ /** ISO timestamp of the most recent observation. */
28
+ observedAt: string;
29
+ }
30
+ export interface ObservedContextWindowStore {
31
+ version: 1;
32
+ models: Record<string, ObservedContextWindowEntry>;
33
+ }
34
+ /** Resolve the on-disk store path. */
35
+ export declare function getObservedContextWindowPath(cacheDir?: string): string;
36
+ /**
37
+ * Load persisted observations and apply them as context-window overrides.
38
+ *
39
+ * Always resolves — never throws. A missing, unreadable, or malformed store
40
+ * leaves the overrides empty and lookups fall through to catalog/static.
41
+ */
42
+ export declare function loadObservedContextWindows(options?: ObservedContextWindowOptions): Promise<Record<string, number>>;
43
+ /**
44
+ * Record a provider-reported context window for a model.
45
+ *
46
+ * Applies in memory immediately, then persists — but only when the value
47
+ * actually changed. Codex reports a window on every `token_count` event, so an
48
+ * unconditional write would mean disk I/O many times per session for a value
49
+ * that almost never moves.
50
+ *
51
+ * Always resolves — never throws. Persistence failures are logged and dropped;
52
+ * the in-memory override still stands for this process.
53
+ */
54
+ export declare function recordObservedContextWindow(modelId: string, contextWindow: number, options?: ObservedContextWindowOptions): Promise<void>;