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.
Files changed (269) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +58 -18
  3. package/dist/browser.d.ts +13 -2
  4. package/dist/browser.js +30 -2
  5. package/dist/claudePaths.d.ts +11 -0
  6. package/dist/claudePaths.js +61 -0
  7. package/dist/claudeProfiles.d.ts +2 -9
  8. package/dist/claudeProfiles.js +10 -25
  9. package/dist/codexPaths.d.ts +22 -0
  10. package/dist/codexPaths.js +67 -0
  11. package/dist/codexProfiles.d.ts +1 -4
  12. package/dist/codexProfiles.js +5 -43
  13. package/dist/doctor.js +2 -2
  14. package/dist/esm/accountChangeSignal.js +15 -0
  15. package/dist/esm/accountChanges.js +152 -0
  16. package/dist/esm/accountHealth.js +288 -0
  17. package/dist/esm/accountLaunch.js +145 -0
  18. package/dist/esm/accountLease.js +60 -0
  19. package/dist/esm/accountManager.js +606 -0
  20. package/dist/esm/accountRegistry.js +214 -0
  21. package/dist/esm/accountStatus.js +51 -0
  22. package/dist/esm/accountSwitch.js +74 -0
  23. package/dist/esm/accountSync.js +156 -0
  24. package/dist/esm/accountSyncTypes.js +4 -0
  25. package/dist/esm/accounts.js +1291 -0
  26. package/dist/esm/aggregation/EventAggregator.js +2034 -0
  27. package/dist/esm/aggregation/FrequencyTracker.js +69 -0
  28. package/dist/esm/aggregation/HeatmapTracker.js +89 -0
  29. package/dist/esm/aggregation/PatternExtractor.js +168 -0
  30. package/dist/esm/aggregation/costProvenance.js +31 -0
  31. package/dist/esm/aggregation/snapshot.js +161 -0
  32. package/dist/esm/aggregation/types.js +5 -0
  33. package/dist/esm/analytics/codeImpact.js +18 -0
  34. package/dist/esm/analytics/compactionLedger.js +37 -0
  35. package/dist/esm/analytics/qualityScore.js +112 -0
  36. package/dist/esm/autoSwitch.js +185 -0
  37. package/dist/esm/browser.js +34 -0
  38. package/dist/esm/claudeCredentialDoctor.js +239 -0
  39. package/dist/esm/claudeCredentialLedger.js +91 -0
  40. package/dist/esm/claudeCredentials.js +111 -0
  41. package/dist/esm/claudeLiveGuard.js +43 -0
  42. package/dist/esm/claudePaths.js +22 -0
  43. package/dist/esm/claudeProfiles.js +142 -0
  44. package/dist/esm/claudeRefreshLock.js +106 -0
  45. package/dist/esm/codexAuth.js +101 -0
  46. package/dist/esm/codexCredentialDoctor.js +265 -0
  47. package/dist/esm/codexCredentialStore.js +164 -0
  48. package/dist/esm/codexKeepAlive.js +112 -0
  49. package/dist/esm/codexPaths.js +172 -0
  50. package/dist/esm/codexProfiles.js +1251 -0
  51. package/dist/esm/codexQuota.js +564 -0
  52. package/dist/esm/codexQuotaWatcher.js +363 -0
  53. package/dist/esm/context/composer.js +71 -0
  54. package/dist/esm/context/sessionContext.js +546 -0
  55. package/dist/esm/credentialIO.js +283 -0
  56. package/dist/esm/credentials.js +49 -0
  57. package/dist/esm/doctor.js +305 -0
  58. package/dist/esm/ensureDefaultAccounts.js +62 -0
  59. package/dist/esm/errorHistory.js +56 -0
  60. package/dist/esm/extractors/errorTaxonomy.js +43 -0
  61. package/dist/esm/extractors/gatherAssets.js +32 -0
  62. package/dist/esm/extractors/sessionAssets.js +176 -0
  63. package/dist/esm/extractors/sources/claudeAssets.js +158 -0
  64. package/dist/esm/extractors/sources/codexAssets.js +234 -0
  65. package/dist/esm/extractors/tokenUsage.js +41 -0
  66. package/dist/esm/extractors/toolCall.js +142 -0
  67. package/dist/esm/failingTools.js +65 -0
  68. package/dist/esm/fileHead.js +27 -0
  69. package/dist/esm/formatters/eventHighlighter.js +332 -0
  70. package/dist/esm/formatters/noiseClassifier.js +209 -0
  71. package/dist/esm/formatters/sessionDump.js +328 -0
  72. package/dist/esm/formatters/toolSummary.js +226 -0
  73. package/dist/esm/formatting.js +82 -0
  74. package/dist/esm/handoffUrl.js +23 -0
  75. package/dist/esm/historicalStore.js +305 -0
  76. package/dist/esm/humanPrompt.js +88 -0
  77. package/dist/esm/index.js +213 -0
  78. package/dist/esm/keepAlive.js +189 -0
  79. package/dist/esm/liveCredentialGuard.js +33 -0
  80. package/dist/esm/modelAliases.js +51 -0
  81. package/dist/esm/modelCatalog.js +139 -0
  82. package/dist/esm/modelContext.js +280 -0
  83. package/dist/esm/modelInfo.js +832 -0
  84. package/dist/esm/multiProviderQuotaService.js +450 -0
  85. package/dist/esm/node.js +46 -0
  86. package/dist/esm/observedContextWindows.js +165 -0
  87. package/dist/esm/observedSessionCollector.js +939 -0
  88. package/dist/esm/parsers/changelogParser.js +71 -0
  89. package/dist/esm/parsers/codexHistory.js +89 -0
  90. package/dist/esm/parsers/codexParser.js +906 -0
  91. package/dist/esm/parsers/debugLogParser.js +126 -0
  92. package/dist/esm/parsers/jsonl.js +66 -0
  93. package/dist/esm/parsers/mcpToolName.js +28 -0
  94. package/dist/esm/parsers/openCodeParser.js +625 -0
  95. package/dist/esm/parsers/planExtractor.js +335 -0
  96. package/dist/esm/parsers/sessionActivityDetector.js +246 -0
  97. package/dist/esm/parsers/sessionPathResolver.js +713 -0
  98. package/dist/esm/parsers/subagentScanner.js +360 -0
  99. package/dist/esm/parsers/subagentStatsCache.js +45 -0
  100. package/dist/esm/parsers/subagentTraceParser.js +325 -0
  101. package/dist/esm/paths.js +134 -0
  102. package/dist/esm/peakHours.js +194 -0
  103. package/dist/esm/phrases.js +1330 -0
  104. package/dist/esm/pricingCatalog.js +247 -0
  105. package/dist/esm/processDetection.js +200 -0
  106. package/dist/esm/projectMigration.js +59 -0
  107. package/dist/esm/promptHistory.js +1221 -0
  108. package/dist/esm/providerFailure.js +362 -0
  109. package/dist/esm/providerIds.js +6 -0
  110. package/dist/esm/providerQuota.js +1 -0
  111. package/dist/esm/providerServiceStatus.js +145 -0
  112. package/dist/esm/providerServiceStatusTypes.js +1 -0
  113. package/dist/esm/providerStatus.js +117 -0
  114. package/dist/esm/providers/claudeCode.js +575 -0
  115. package/dist/esm/providers/claudeListing.js +121 -0
  116. package/dist/esm/providers/codex.js +1034 -0
  117. package/dist/esm/providers/codexDatabase.js +297 -0
  118. package/dist/esm/providers/codexRollouts.js +215 -0
  119. package/dist/esm/providers/detect.js +185 -0
  120. package/dist/esm/providers/diagnostics.js +83 -0
  121. package/dist/esm/providers/directoryListingCache.js +206 -0
  122. package/dist/esm/providers/factory.js +54 -0
  123. package/dist/esm/providers/openCode.js +2008 -0
  124. package/dist/esm/providers/openCodeDatabase.js +419 -0
  125. package/dist/esm/providers/rolloutWalker.js +252 -0
  126. package/dist/esm/providers/types.js +12 -0
  127. package/dist/esm/quota.js +182 -0
  128. package/dist/esm/quotaHistory.js +364 -0
  129. package/dist/esm/quotaPoller.js +230 -0
  130. package/dist/esm/quotaPresentation.js +97 -0
  131. package/dist/esm/quotaResolver.js +231 -0
  132. package/dist/esm/quotaSnapshots.js +130 -0
  133. package/dist/esm/quotaThresholds.js +85 -0
  134. package/dist/esm/readers/decisions.js +24 -0
  135. package/dist/esm/readers/handoff.js +20 -0
  136. package/dist/esm/readers/helpers.js +27 -0
  137. package/dist/esm/readers/history.js +9 -0
  138. package/dist/esm/readers/notes.js +48 -0
  139. package/dist/esm/readers/plans.js +225 -0
  140. package/dist/esm/readers/tasks.js +19 -0
  141. package/dist/esm/report/htmlHelpers.js +280 -0
  142. package/dist/esm/report/htmlReportGenerator.js +704 -0
  143. package/dist/esm/report/index.js +7 -0
  144. package/dist/esm/report/logo.js +2 -0
  145. package/dist/esm/report/openBrowser.js +19 -0
  146. package/dist/esm/report/sessionReportInputs.js +33 -0
  147. package/dist/esm/report/transcriptParser.js +83 -0
  148. package/dist/esm/report/types.js +4 -0
  149. package/dist/esm/schemas/accountManager.js +204 -0
  150. package/dist/esm/schemas/accountStatus.js +25 -0
  151. package/dist/esm/schemas/assistantTurn.js +74 -0
  152. package/dist/esm/schemas/index.js +25 -0
  153. package/dist/esm/schemas/observedSessionV1.js +77 -0
  154. package/dist/esm/schemas/providerFailure.js +37 -0
  155. package/dist/esm/schemas/quota.js +132 -0
  156. package/dist/esm/schemas/quotaHistory.js +40 -0
  157. package/dist/esm/schemas/sessionEvent.js +216 -0
  158. package/dist/esm/schemas/stateFile.js +75 -0
  159. package/dist/esm/schemas/tokenEstimation.js +7 -0
  160. package/dist/esm/schemas/transcript.js +86 -0
  161. package/dist/esm/schemas/usageNormalization.js +38 -0
  162. package/dist/esm/search/advancedFilter.js +197 -0
  163. package/dist/esm/search/sessionSearch.js +66 -0
  164. package/dist/esm/sessionFingerprint.js +40 -0
  165. package/dist/esm/sessionMonitor.js +198 -0
  166. package/dist/esm/sessionPreviews.js +522 -0
  167. package/dist/esm/sessionPromptHistory.js +254 -0
  168. package/dist/esm/sessionPromptSignals.js +364 -0
  169. package/dist/esm/sessionStats.js +160 -0
  170. package/dist/esm/sessionTokenTotals.js +98 -0
  171. package/dist/esm/sessionTranscripts.js +49 -0
  172. package/dist/esm/stateFile.js +119 -0
  173. package/dist/esm/statusline/BurnRateCalculator.js +40 -0
  174. package/dist/esm/statusline/claudeStatuslinePayload.js +148 -0
  175. package/dist/esm/statusline/formatter.js +122 -0
  176. package/dist/esm/statusline/index.js +8 -0
  177. package/dist/esm/terminalSync.js +95 -0
  178. package/dist/esm/testing/credentialGuard.js +197 -0
  179. package/dist/esm/testing/index.js +12 -0
  180. package/dist/esm/testing/sessionLogFixtures.js +128 -0
  181. package/dist/esm/tokenEstimation.js +67 -0
  182. package/dist/esm/tokenSummary.js +113 -0
  183. package/dist/esm/toolCategory.js +82 -0
  184. package/dist/esm/transcript.js +257 -0
  185. package/dist/esm/turns/assistantTurn.js +373 -0
  186. package/dist/esm/types/codex.js +23 -0
  187. package/dist/esm/types/decisionLog.js +5 -0
  188. package/dist/esm/types/historicalData.js +47 -0
  189. package/dist/esm/types/knowledgeNote.js +15 -0
  190. package/dist/esm/types/observedSessionV1.js +257 -0
  191. package/dist/esm/types/opencode.js +12 -0
  192. package/dist/esm/types/plan.js +7 -0
  193. package/dist/esm/types/sessionEvent.js +10 -0
  194. package/dist/esm/types/taskPersistence.js +12 -0
  195. package/dist/esm/usage/billingBlocks.js +148 -0
  196. package/dist/esm/usage/claudeUsageDedupe.js +189 -0
  197. package/dist/esm/usage/importSessionHistory.js +84 -0
  198. package/dist/esm/usage/subagentUsage.js +18 -0
  199. package/dist/esm/usage/usageEvents.js +402 -0
  200. package/dist/esm/usage/usageReports.js +202 -0
  201. package/dist/esm/usageNormalization.js +95 -0
  202. package/dist/esm/watchers/eventBridge.js +219 -0
  203. package/dist/esm/watchers/factory.js +63 -0
  204. package/dist/esm/watchers/jsonlTail.js +289 -0
  205. package/dist/esm/watchers/jsonlWatcher.js +435 -0
  206. package/dist/esm/watchers/providerReaderWatcher.js +118 -0
  207. package/dist/esm/watchers/sqliteWatcher.js +285 -0
  208. package/dist/esm/watchers/types.js +4 -0
  209. package/dist/esm/workspaceRoots.js +56 -0
  210. package/dist/esm/writers/atomic.js +375 -0
  211. package/dist/esm/writers/decisions.js +51 -0
  212. package/dist/esm/writers/notes.js +46 -0
  213. package/dist/esm/writers/tasks.js +63 -0
  214. package/dist/esm/zaiQuota.js +323 -0
  215. package/dist/esm/zaiQuotaApi.js +271 -0
  216. package/dist/esm/zaiQuotaWatcher.js +265 -0
  217. package/dist/fileHead.d.ts +22 -0
  218. package/dist/fileHead.js +33 -0
  219. package/dist/index.d.ts +16 -7
  220. package/dist/index.js +49 -15
  221. package/dist/node.d.ts +13 -1
  222. package/dist/node.js +43 -2
  223. package/dist/parsers/codexHistory.js +2 -2
  224. package/dist/parsers/codexParser.d.ts +12 -0
  225. package/dist/parsers/codexParser.js +34 -12
  226. package/dist/parsers/debugLogParser.js +2 -2
  227. package/dist/parsers/sessionPathResolver.js +5 -4
  228. package/dist/promptHistory.d.ts +32 -0
  229. package/dist/promptHistory.js +115 -59
  230. package/dist/providerFailure.d.ts +12 -6
  231. package/dist/providerFailure.js +45 -0
  232. package/dist/providers/claudeCode.js +7 -99
  233. package/dist/providers/claudeListing.d.ts +22 -0
  234. package/dist/providers/claudeListing.js +158 -0
  235. package/dist/providers/codex.d.ts +3 -1
  236. package/dist/providers/codex.js +26 -227
  237. package/dist/providers/codexRollouts.d.ts +44 -0
  238. package/dist/providers/codexRollouts.js +261 -0
  239. package/dist/providers/detect.js +4 -4
  240. package/dist/readers/plans.js +3 -3
  241. package/dist/schemas/index.d.ts +2 -0
  242. package/dist/schemas/index.js +7 -2
  243. package/dist/schemas/providerFailure.d.ts +154 -0
  244. package/dist/schemas/providerFailure.js +40 -0
  245. package/dist/sessionPromptHistory.d.ts +12 -7
  246. package/dist/sessionPromptHistory.js +15 -3
  247. package/dist/sessionPromptSignals.d.ts +9 -0
  248. package/dist/sessionPromptSignals.js +13 -0
  249. package/dist/testing/credentialGuard.d.ts +23 -0
  250. package/dist/testing/credentialGuard.js +233 -0
  251. package/dist/testing/index.d.ts +14 -0
  252. package/dist/testing/index.js +22 -0
  253. package/dist/testing/sessionLogFixtures.d.ts +28 -0
  254. package/dist/testing/sessionLogFixtures.js +169 -0
  255. package/dist/tokenEstimation.d.ts +2 -0
  256. package/dist/tokenEstimation.js +21 -11
  257. package/dist/toolCategory.d.ts +18 -0
  258. package/dist/toolCategory.js +86 -0
  259. package/dist/types/codex.d.ts +4 -0
  260. package/dist/usage/usageEvents.d.ts +16 -0
  261. package/dist/usage/usageEvents.js +53 -9
  262. package/dist/watchers/jsonlTail.d.ts +26 -1
  263. package/dist/watchers/jsonlTail.js +136 -28
  264. package/dist/watchers/jsonlWatcher.js +7 -0
  265. package/dist/watchers/providerReaderWatcher.js +1 -0
  266. package/dist/watchers/types.d.ts +6 -0
  267. package/dist/workspaceRoots.d.ts +23 -0
  268. package/dist/workspaceRoots.js +93 -0
  269. package/package.json +22 -2
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Centralized model ID → context window size lookup.
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. Imported resolutions — exact entries hydrated from another realm via
13
+ * `importResolvedModelCatalog()`. Below local observed data (a window this
14
+ * realm saw first-hand is fresher than a transferred snapshot), above the
15
+ * catalog.
16
+ * 4. Catalog overrides — `max_input_tokens` from the LiteLLM catalog, hydrated
17
+ * by `pricingCatalog.ts`. Keeps new models correct without a code change.
18
+ * 5. The static table below — offline baseline of last resort.
19
+ *
20
+ * Within that order, match quality comes first: every layer is tried for an
21
+ * exact hit before any layer is tried for a prefix hit. See
22
+ * `getModelContextWindowSize`. The raw (alias) id is tried before its
23
+ * canonical target at every step, so registering an alias never orphans data
24
+ * recorded under the alias itself.
25
+ *
26
+ * Layers 2 and 3 are pushed in by Node-only modules through the `_set*` hooks,
27
+ * so this file stays browser-safe (no node:fs, no fetch) and can be bundled for
28
+ * webviews. Same pattern as `_setPricingOverrides` in `modelInfo.ts`.
29
+ */
30
+ import { resolveModelAlias } from './modelAliases';
31
+ /** Known model context window sizes (in tokens); current models verified 2026-09-05. */
32
+ const MODEL_CONTEXT_SIZES = {
33
+ // Claude — native 1M context (Fable 5+, Opus 4.6+, Sonnet 4.6+)
34
+ 'claude-fable-5-1': 1_000_000,
35
+ 'claude-fable-5.1': 1_000_000,
36
+ 'claude-fable-5': 1_000_000,
37
+ 'claude-opus-5-5': 1_000_000,
38
+ 'claude-opus-5.5': 1_000_000,
39
+ 'claude-opus-5': 1_000_000,
40
+ 'claude-opus-4-8': 1_000_000,
41
+ 'claude-opus-4-7': 1_000_000,
42
+ 'claude-opus-4-6': 1_000_000,
43
+ 'claude-sonnet-5': 1_000_000,
44
+ 'claude-sonnet-4-7': 1_000_000,
45
+ 'claude-sonnet-4-6': 1_000_000,
46
+ // Claude 4 family
47
+ 'claude-opus-4': 200_000,
48
+ 'claude-sonnet-4': 200_000,
49
+ // Claude 3.5 family
50
+ 'claude-3-5-sonnet': 200_000,
51
+ 'claude-3-5-haiku': 200_000,
52
+ // Claude 4.5 / Haiku
53
+ 'claude-haiku-4-5': 200_000,
54
+ // Claude 3 family
55
+ 'claude-3-opus': 200_000,
56
+ 'claude-3-sonnet': 200_000,
57
+ 'claude-3-haiku': 200_000,
58
+ // OpenAI GPT-4.1 series (1M context)
59
+ 'gpt-4.1': 1_048_576,
60
+ // OpenAI GPT-5/6 series (keys sorted longest-first below; explicit entries
61
+ // for every variant so prefix matching can't misclassify a new one).
62
+ // These are published model maxima. Codex reports a smaller *effective*
63
+ // window per account tier via token_count; that value wins through the
64
+ // observed-override layer.
65
+ 'gpt-6-astra': 1_050_000,
66
+ 'gpt-6-sol': 1_050_000,
67
+ 'gpt-6-luna': 1_050_000,
68
+ 'gpt-5.6-sol': 1_050_000,
69
+ 'gpt-5.6-terra': 1_050_000,
70
+ 'gpt-5.6-luna': 1_050_000,
71
+ 'gpt-5.6': 1_050_000,
72
+ 'gpt-5.5': 1_050_000,
73
+ 'gpt-5.4-mini': 400_000,
74
+ 'gpt-5.4-nano': 400_000,
75
+ 'gpt-5.4': 1_050_000,
76
+ 'gpt-5.3-codex-spark': 128_000,
77
+ 'gpt-5.3-codex': 400_000,
78
+ 'gpt-5': 400_000,
79
+ // OpenAI reasoning
80
+ o1: 200_000,
81
+ o3: 200_000,
82
+ o4: 200_000,
83
+ // OpenAI GPT-4 series
84
+ 'gpt-4o': 128_000,
85
+ 'gpt-4-turbo': 128_000,
86
+ 'gpt-4': 8_192,
87
+ // Gemini
88
+ gemini: 1_000_000,
89
+ // DeepSeek
90
+ deepseek: 128_000,
91
+ };
92
+ /** Prefix keys sorted longest-first for correct prefix matching. */
93
+ const SORTED_KEYS = sortKeysLongestFirst(MODEL_CONTEXT_SIZES);
94
+ /** Default context window size when model is unknown. */
95
+ export const DEFAULT_CONTEXT_WINDOW = 200_000;
96
+ // ── Override Tables (populated at runtime by Node-only modules) ──
97
+ let observedTable = {};
98
+ let observedSortedKeys = [];
99
+ let catalogTable = {};
100
+ let catalogSortedKeys = [];
101
+ let importedTable = {};
102
+ function sortKeysLongestFirst(table) {
103
+ return Object.keys(table).sort((a, b) => b.length - a.length);
104
+ }
105
+ /**
106
+ * Lowercase keys so lookups match regardless of how the source spelled them.
107
+ * The LiteLLM catalog publishes some IDs mixed-case; the static table and the
108
+ * lookup path are both lowercase. Drops non-positive and non-finite values.
109
+ */
110
+ function normalizeOverrides(overrides) {
111
+ const out = {};
112
+ for (const [key, value] of Object.entries(overrides)) {
113
+ if (!Number.isFinite(value) || value <= 0)
114
+ continue;
115
+ out[key.trim().toLowerCase()] = value;
116
+ }
117
+ return out;
118
+ }
119
+ /**
120
+ * Internal: replace the observed-context-window map. Called by
121
+ * `observedContextWindows.ts` on load and on each newly observed value.
122
+ * Not part of the stable public API.
123
+ */
124
+ export function _setObservedContextWindows(overrides) {
125
+ observedTable = normalizeOverrides(overrides);
126
+ observedSortedKeys = sortKeysLongestFirst(observedTable);
127
+ }
128
+ /** Internal: snapshot observed overrides (test + diagnostics). */
129
+ export function _getObservedContextWindows() {
130
+ return { ...observedTable };
131
+ }
132
+ /** Internal: clear observed overrides (used by tests). */
133
+ export function _clearObservedContextWindows() {
134
+ observedTable = {};
135
+ observedSortedKeys = [];
136
+ }
137
+ /**
138
+ * Internal: replace the catalog-context-window map. Called by
139
+ * `pricingCatalog.ts` after a successful LiteLLM hydration.
140
+ * Not part of the stable public API.
141
+ */
142
+ export function _setCatalogContextWindows(overrides) {
143
+ catalogTable = normalizeOverrides(overrides);
144
+ catalogSortedKeys = sortKeysLongestFirst(catalogTable);
145
+ }
146
+ /** Internal: snapshot catalog overrides (test + diagnostics). */
147
+ export function _getCatalogContextWindows() {
148
+ return { ...catalogTable };
149
+ }
150
+ /** Internal: clear catalog overrides (used by tests and when disabling hydration). */
151
+ export function _clearCatalogContextWindows() {
152
+ catalogTable = {};
153
+ catalogSortedKeys = [];
154
+ }
155
+ /**
156
+ * Internal: hydrate exact, already-resolved context data from another realm.
157
+ * Merges so successive partial imports accumulate (aliases already merge);
158
+ * use `_clearImportedResolvedContextWindows` to reset.
159
+ */
160
+ export function _setImportedResolvedContextWindows(values) {
161
+ importedTable = { ...importedTable, ...values };
162
+ }
163
+ /** Internal test hook. */
164
+ export function _clearImportedResolvedContextWindows() {
165
+ importedTable = {};
166
+ }
167
+ /** Internal: list every ID known to the context resolver. */
168
+ export function _getModelContextIds() {
169
+ return Array.from(new Set([
170
+ ...Object.keys(MODEL_CONTEXT_SIZES),
171
+ ...Object.keys(catalogTable),
172
+ ...Object.keys(observedTable),
173
+ ...Object.keys(importedTable),
174
+ ]));
175
+ }
176
+ // ── Lookup ──
177
+ /** Exact match against one table. */
178
+ function exactWindow(table, modelId) {
179
+ const hit = table[modelId];
180
+ return hit === undefined ? null : hit;
181
+ }
182
+ function exactWindowMatch(table, modelId) {
183
+ const exact = exactWindow(table, modelId);
184
+ return exact === null ? null : { value: exact, match: 'exact', matchedModelId: modelId };
185
+ }
186
+ function prefixWindowMatch(table, sortedKeys, modelId) {
187
+ for (const key of sortedKeys) {
188
+ if (modelId !== key && modelId.startsWith(key)) {
189
+ return { value: table[key], match: 'prefix', matchedModelId: key };
190
+ }
191
+ }
192
+ return null;
193
+ }
194
+ /** Resolve published, observed, and tier-effective context windows with provenance. */
195
+ export function resolveModelContextWindow(modelId) {
196
+ const rawModelId = modelId ?? '';
197
+ const stripped = rawModelId
198
+ .replace(/\[1m\]/gi, '')
199
+ .trim()
200
+ .toLowerCase();
201
+ const canonicalModelId = resolveModelAlias(stripped);
202
+ if (/\[1m\]/i.test(rawModelId) || /\[1m\]/i.test(canonicalModelId)) {
203
+ return {
204
+ modelId: rawModelId,
205
+ canonicalModelId: canonicalModelId.replace(/\[1m\]/gi, ''),
206
+ published: 1_000_000,
207
+ observed: null,
208
+ tierEffective: 1_000_000,
209
+ provenance: { source: 'explicit-1m', match: 'exact', matchedModelId: canonicalModelId },
210
+ };
211
+ }
212
+ // The raw id is tried before its alias target at every step: data recorded
213
+ // for the alias id itself is more specific than the canonical fallback.
214
+ const candidateIds = stripped === canonicalModelId ? [canonicalModelId] : [stripped, canonicalModelId];
215
+ const firstMatch = (lookup) => {
216
+ for (const id of candidateIds) {
217
+ const hit = lookup(id);
218
+ if (hit)
219
+ return hit;
220
+ }
221
+ return null;
222
+ };
223
+ const observedExact = firstMatch((id) => exactWindowMatch(observedTable, id));
224
+ // Imported resolutions rank below local observed data: a window this realm
225
+ // saw first-hand is fresher than a snapshot transferred from another realm.
226
+ if (!observedExact) {
227
+ const imported = importedTable[stripped] ?? importedTable[canonicalModelId];
228
+ if (imported) {
229
+ return { ...imported, modelId: rawModelId, canonicalModelId };
230
+ }
231
+ }
232
+ const catalogExact = firstMatch((id) => exactWindowMatch(catalogTable, id));
233
+ const staticExact = firstMatch((id) => exactWindowMatch(MODEL_CONTEXT_SIZES, id));
234
+ const observedPrefix = firstMatch((id) => prefixWindowMatch(observedTable, observedSortedKeys, id));
235
+ const catalogPrefix = firstMatch((id) => prefixWindowMatch(catalogTable, catalogSortedKeys, id));
236
+ const staticPrefix = firstMatch((id) => prefixWindowMatch(MODEL_CONTEXT_SIZES, SORTED_KEYS, id));
237
+ const published = catalogExact ?? staticExact ?? catalogPrefix ?? staticPrefix;
238
+ const effective = observedExact ?? catalogExact ?? staticExact ?? observedPrefix ?? catalogPrefix ?? staticPrefix;
239
+ return {
240
+ modelId: rawModelId,
241
+ canonicalModelId,
242
+ published: published?.value ?? null,
243
+ observed: (observedExact ?? observedPrefix)?.value ?? null,
244
+ tierEffective: effective?.value ?? DEFAULT_CONTEXT_WINDOW,
245
+ provenance: effective
246
+ ? {
247
+ source: effective === observedExact || effective === observedPrefix
248
+ ? 'observed'
249
+ : effective === catalogExact || effective === catalogPrefix
250
+ ? 'catalog'
251
+ : 'static',
252
+ match: effective.match,
253
+ matchedModelId: effective.matchedModelId,
254
+ }
255
+ : { source: 'default', match: 'default' },
256
+ };
257
+ }
258
+ /**
259
+ * Returns the context window size for a model ID.
260
+ *
261
+ * Input is trimmed and lowercased before lookup, so padded or mixed-case
262
+ * IDs (e.g. "Claude-Opus-5 ") resolve without caller-side normalization.
263
+ *
264
+ * Lookup order:
265
+ * 1. Explicit "[1m]" suffix (Claude Code's 1M-variant marker) → 1_000_000
266
+ * 2. Exact match, most-trusted layer first: observed → catalog → static
267
+ * 3. Longest-prefix match, same layer order
268
+ * (e.g. "claude-opus-4-6-20250414" → "claude-opus-4-6")
269
+ * 4. DEFAULT_CONTEXT_WINDOW
270
+ *
271
+ * Match quality outranks layer trust: an exact hit in *any* layer beats a
272
+ * prefix guess in *every* layer. Resolving each layer fully before moving on
273
+ * would let a catalog key that merely happens to be a prefix shadow a curated
274
+ * static entry — e.g. the catalog has no "claude-sonnet-4-7", and prefix-first
275
+ * would hand it "claude-sonnet-4" (128K, a GitHub Copilot deployment) instead
276
+ * of the static table's exact 1M.
277
+ */
278
+ export function getModelContextWindowSize(modelId) {
279
+ return resolveModelContextWindow(modelId).tierEffective;
280
+ }