sidekick-shared 0.24.5 → 0.26.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 (157) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/README.md +205 -52
  3. package/dist/accountChangeSignal.d.ts +3 -0
  4. package/dist/accountChangeSignal.js +19 -0
  5. package/dist/accountChanges.d.ts +21 -0
  6. package/dist/accountChanges.js +170 -0
  7. package/dist/accountRegistry.d.ts +25 -2
  8. package/dist/accountRegistry.js +41 -13
  9. package/dist/accountStatus.d.ts +9 -1
  10. package/dist/accountStatus.js +3 -3
  11. package/dist/accounts.d.ts +3 -1
  12. package/dist/accounts.js +84 -64
  13. package/dist/aggregation/EventAggregator.d.ts +15 -2
  14. package/dist/aggregation/EventAggregator.js +54 -9
  15. package/dist/aggregation/costProvenance.d.ts +14 -0
  16. package/dist/aggregation/costProvenance.js +35 -0
  17. package/dist/aggregation/snapshot.d.ts +13 -2
  18. package/dist/aggregation/snapshot.js +57 -6
  19. package/dist/aggregation/types.d.ts +30 -1
  20. package/dist/analytics/compactionLedger.js +15 -5
  21. package/dist/analytics/qualityScore.js +2 -1
  22. package/dist/browser.d.ts +19 -4
  23. package/dist/browser.js +43 -1
  24. package/dist/codexProfiles.d.ts +3 -1
  25. package/dist/codexProfiles.js +4 -2
  26. package/dist/codexQuota.d.ts +2 -0
  27. package/dist/codexQuota.js +11 -66
  28. package/dist/codexQuotaWatcher.d.ts +6 -0
  29. package/dist/codexQuotaWatcher.js +101 -47
  30. package/dist/failingTools.d.ts +37 -0
  31. package/dist/failingTools.js +70 -0
  32. package/dist/formatters/sessionDump.js +13 -7
  33. package/dist/formatting.d.ts +13 -0
  34. package/dist/formatting.js +35 -0
  35. package/dist/historicalStore.d.ts +47 -0
  36. package/dist/historicalStore.js +312 -0
  37. package/dist/index.d.ts +67 -23
  38. package/dist/index.js +138 -26
  39. package/dist/modelAliases.d.ts +13 -0
  40. package/dist/modelAliases.js +58 -0
  41. package/dist/modelCatalog.d.ts +32 -0
  42. package/dist/modelCatalog.js +145 -0
  43. package/dist/modelContext.d.ts +36 -3
  44. package/dist/modelContext.js +109 -34
  45. package/dist/modelInfo.d.ts +30 -1
  46. package/dist/modelInfo.js +116 -26
  47. package/dist/multiProviderQuotaService.d.ts +8 -0
  48. package/dist/multiProviderQuotaService.js +64 -3
  49. package/dist/node.d.ts +23 -4
  50. package/dist/node.js +50 -1
  51. package/dist/observedContextWindows.d.ts +7 -0
  52. package/dist/observedContextWindows.js +17 -2
  53. package/dist/observedSessionCollector.d.ts +72 -6
  54. package/dist/observedSessionCollector.js +440 -47
  55. package/dist/parsers/codexHistory.d.ts +4 -2
  56. package/dist/parsers/codexHistory.js +9 -2
  57. package/dist/parsers/sessionActivityDetector.d.ts +28 -0
  58. package/dist/parsers/sessionActivityDetector.js +110 -15
  59. package/dist/parsers/sessionPathResolver.d.ts +11 -0
  60. package/dist/parsers/sessionPathResolver.js +13 -10
  61. package/dist/peakHours.d.ts +34 -3
  62. package/dist/peakHours.js +92 -7
  63. package/dist/pricingCatalog.d.ts +7 -0
  64. package/dist/pricingCatalog.js +8 -5
  65. package/dist/providerIds.d.ts +11 -0
  66. package/dist/providerIds.js +9 -0
  67. package/dist/providerQuota.d.ts +3 -1
  68. package/dist/providerQuota.js +3 -0
  69. package/dist/providers/claudeCode.d.ts +10 -1
  70. package/dist/providers/claudeCode.js +211 -131
  71. package/dist/providers/codex.d.ts +20 -1
  72. package/dist/providers/codex.js +308 -244
  73. package/dist/providers/codexDatabase.d.ts +9 -0
  74. package/dist/providers/codexDatabase.js +107 -2
  75. package/dist/providers/detect.d.ts +2 -0
  76. package/dist/providers/detect.js +29 -24
  77. package/dist/providers/diagnostics.d.ts +13 -0
  78. package/dist/providers/diagnostics.js +88 -0
  79. package/dist/providers/factory.d.ts +20 -0
  80. package/dist/providers/factory.js +57 -0
  81. package/dist/providers/openCode.d.ts +22 -1
  82. package/dist/providers/openCode.js +460 -126
  83. package/dist/providers/openCodeDatabase.d.ts +10 -0
  84. package/dist/providers/openCodeDatabase.js +69 -0
  85. package/dist/providers/rolloutWalker.d.ts +53 -0
  86. package/dist/providers/rolloutWalker.js +209 -0
  87. package/dist/providers/types.d.ts +82 -2
  88. package/dist/providers/types.js +4 -0
  89. package/dist/quota.d.ts +34 -4
  90. package/dist/quota.js +37 -9
  91. package/dist/quotaHistory.d.ts +3 -2
  92. package/dist/quotaHistory.js +36 -35
  93. package/dist/quotaPoller.d.ts +13 -1
  94. package/dist/quotaPoller.js +65 -3
  95. package/dist/quotaResolver.d.ts +81 -0
  96. package/dist/quotaResolver.js +213 -0
  97. package/dist/quotaSnapshots.d.ts +15 -2
  98. package/dist/quotaSnapshots.js +25 -3
  99. package/dist/quotaThresholds.d.ts +60 -0
  100. package/dist/quotaThresholds.js +90 -0
  101. package/dist/report/htmlReportGenerator.js +10 -4
  102. package/dist/report/sessionReportInputs.d.ts +23 -0
  103. package/dist/report/sessionReportInputs.js +34 -0
  104. package/dist/schemas/accountManager.d.ts +2 -2
  105. package/dist/schemas/accountManager.js +2 -4
  106. package/dist/schemas/accountStatus.d.ts +3 -0
  107. package/dist/schemas/accountStatus.js +1 -0
  108. package/dist/schemas/index.d.ts +3 -0
  109. package/dist/schemas/index.js +16 -2
  110. package/dist/schemas/observedSessionV1.d.ts +27 -26
  111. package/dist/schemas/observedSessionV1.js +6 -4
  112. package/dist/schemas/quota.d.ts +141 -2
  113. package/dist/schemas/quota.js +20 -8
  114. package/dist/schemas/quotaHistory.d.ts +3 -2
  115. package/dist/schemas/quotaHistory.js +1 -1
  116. package/dist/schemas/sessionEvent.d.ts +6 -0
  117. package/dist/schemas/sessionEvent.js +1 -0
  118. package/dist/schemas/stateFile.d.ts +198 -0
  119. package/dist/schemas/stateFile.js +77 -0
  120. package/dist/schemas/tokenEstimation.d.ts +1 -1
  121. package/dist/schemas/transcript.js +2 -0
  122. package/dist/sessionFingerprint.d.ts +25 -0
  123. package/dist/sessionFingerprint.js +46 -0
  124. package/dist/sessionMonitor.d.ts +15 -0
  125. package/dist/sessionMonitor.js +142 -0
  126. package/dist/sessionPreviews.d.ts +33 -2
  127. package/dist/sessionPreviews.js +333 -6
  128. package/dist/sessionStats.d.ts +51 -0
  129. package/dist/sessionStats.js +166 -0
  130. package/dist/stateFile.d.ts +113 -0
  131. package/dist/stateFile.js +160 -0
  132. package/dist/statusline/claudeStatuslinePayload.d.ts +89 -0
  133. package/dist/statusline/claudeStatuslinePayload.js +152 -0
  134. package/dist/statusline/formatter.d.ts +13 -0
  135. package/dist/statusline/formatter.js +48 -6
  136. package/dist/statusline/index.d.ts +10 -2
  137. package/dist/statusline/index.js +19 -1
  138. package/dist/tokenSummary.d.ts +62 -0
  139. package/dist/tokenSummary.js +72 -0
  140. package/dist/transcript.d.ts +5 -0
  141. package/dist/transcript.js +2 -0
  142. package/dist/types/observedSessionV1.d.ts +8 -2
  143. package/dist/types/observedSessionV1.js +88 -25
  144. package/dist/types/sessionEvent.d.ts +2 -0
  145. package/dist/usage/billingBlocks.d.ts +85 -0
  146. package/dist/usage/billingBlocks.js +153 -0
  147. package/dist/usage/importSessionHistory.d.ts +53 -0
  148. package/dist/usage/importSessionHistory.js +88 -0
  149. package/dist/usage/usageEvents.d.ts +92 -0
  150. package/dist/usage/usageEvents.js +322 -0
  151. package/dist/usage/usageReports.d.ts +76 -0
  152. package/dist/usage/usageReports.js +208 -0
  153. package/dist/watchers/factory.d.ts +6 -0
  154. package/dist/watchers/factory.js +30 -25
  155. package/dist/zaiQuotaApi.d.ts +2 -0
  156. package/dist/zaiQuotaApi.js +2 -0
  157. package/package.json +22 -1
@@ -4,6 +4,7 @@ import type { PeakHoursState } from './peakHours';
4
4
  import type { QuotaState } from './quota';
5
5
  import type { Disposable } from './quotaPoller';
6
6
  import type { ProviderQuotaMap, ProviderQuotaState } from './providerQuota';
7
+ import { type AccountsChangedEvent } from './accountChanges';
7
8
  type Logger = (message: string, error?: unknown) => void;
8
9
  interface CodexWatcherLike extends Disposable {
9
10
  start(): void;
@@ -28,6 +29,7 @@ export interface MultiProviderQuotaServiceOptions {
28
29
  readClaudeAccount?: typeof resolveActiveClaudeAccount;
29
30
  fetchClaudeQuota?: (accessToken: string) => Promise<QuotaState>;
30
31
  fetchPeakHours?: () => Promise<PeakHoursState>;
32
+ subscribeAccountsChanged?: (listener: (event: AccountsChangedEvent) => void) => Disposable;
31
33
  log?: Logger;
32
34
  }
33
35
  export declare class MultiProviderQuotaService implements Disposable {
@@ -41,6 +43,7 @@ export declare class MultiProviderQuotaService implements Disposable {
41
43
  private readonly fetchClaudeQuota;
42
44
  private readonly fetchPeakHours;
43
45
  private readonly log?;
46
+ private readonly subscribeAccountsChanged;
44
47
  private readonly listeners;
45
48
  private readonly codexWatcher;
46
49
  private readonly ownsCodexWatcher;
@@ -60,6 +63,10 @@ export declare class MultiProviderQuotaService implements Disposable {
60
63
  private currentPeakHours;
61
64
  private lastFreshPeakHoursAt;
62
65
  private peakHoursErrorLogged;
66
+ private claudeDormant;
67
+ private accountSubscription;
68
+ private pollInFlight;
69
+ private pendingWake;
63
70
  constructor(options?: MultiProviderQuotaServiceOptions);
64
71
  startPolling(): void;
65
72
  stopPolling(): void;
@@ -73,6 +80,7 @@ export declare class MultiProviderQuotaService implements Disposable {
73
80
  dispose(): void;
74
81
  private refreshPeakHours;
75
82
  private poll;
83
+ private wakeForAccountChange;
76
84
  private emitClaudeState;
77
85
  private handleUnavailableState;
78
86
  private handleTransientFailure;
@@ -7,10 +7,12 @@ const credentials_1 = require("./credentials");
7
7
  const peakHours_1 = require("./peakHours");
8
8
  const quota_1 = require("./quota");
9
9
  const quotaPresentation_1 = require("./quotaPresentation");
10
+ const accountChanges_1 = require("./accountChanges");
10
11
  const ACTIVE_POLL_INTERVAL_MS = 60_000;
11
12
  const IDLE_POLL_INTERVAL_MS = 300_000;
12
13
  const TRANSIENT_FAILURE_BACKOFF_MS = [30_000, 60_000, 120_000, 240_000, 300_000];
13
14
  const PEAK_HOURS_CACHE_MAX_AGE_MS = 10 * 60 * 1000;
15
+ const SIGNED_OUT_ERROR = 'Sign in to a Claude account to view quota';
14
16
  function makeUnavailableClaudeState(error, failureKind) {
15
17
  return {
16
18
  runtimeProvider: 'claude',
@@ -55,6 +57,7 @@ class MultiProviderQuotaService {
55
57
  fetchClaudeQuota;
56
58
  fetchPeakHours;
57
59
  log;
60
+ subscribeAccountsChanged;
58
61
  listeners = [];
59
62
  codexWatcher;
60
63
  ownsCodexWatcher;
@@ -74,6 +77,10 @@ class MultiProviderQuotaService {
74
77
  currentPeakHours;
75
78
  lastFreshPeakHoursAt = 0;
76
79
  peakHoursErrorLogged = false;
80
+ claudeDormant = false;
81
+ accountSubscription = null;
82
+ pollInFlight = false;
83
+ pendingWake = false;
77
84
  constructor(options = {}) {
78
85
  this.activeIntervalMs = options.activeIntervalMs ?? ACTIVE_POLL_INTERVAL_MS;
79
86
  this.idleIntervalMs = options.idleIntervalMs ?? IDLE_POLL_INTERVAL_MS;
@@ -85,6 +92,7 @@ class MultiProviderQuotaService {
85
92
  this.readClaudeAccount = options.readClaudeAccount ?? accounts_1.resolveActiveClaudeAccount;
86
93
  this.fetchClaudeQuota = options.fetchClaudeQuota ?? quota_1.fetchQuota;
87
94
  this.fetchPeakHours = options.fetchPeakHours ?? peakHours_1.fetchPeakHoursStatus;
95
+ this.subscribeAccountsChanged = options.subscribeAccountsChanged ?? accountChanges_1.onAccountsChanged;
88
96
  this.log = options.log;
89
97
  if (options.codexWatcher !== undefined) {
90
98
  this.codexWatcher = options.codexWatcher;
@@ -119,21 +127,25 @@ class MultiProviderQuotaService {
119
127
  if (this.polling)
120
128
  return;
121
129
  this.polling = true;
130
+ this.accountSubscription ??= this.subscribeAccountsChanged(() => this.wakeForAccountChange());
122
131
  this.codexWatcher?.start();
123
132
  this.zaiWatcher?.start();
124
- void this.poll();
133
+ this.wakeForAccountChange();
125
134
  this.log?.('[Quota] Polling started');
126
135
  }
127
136
  stopPolling() {
128
137
  if (!this.polling)
129
138
  return;
130
139
  this.polling = false;
140
+ this.claudeDormant = false;
131
141
  if (this.timer) {
132
142
  clearTimeout(this.timer);
133
143
  this.timer = undefined;
134
144
  }
135
145
  this.codexWatcher?.stop();
136
146
  this.zaiWatcher?.stop();
147
+ this.accountSubscription?.dispose();
148
+ this.accountSubscription = null;
137
149
  this.log?.('[Quota] Polling stopped');
138
150
  }
139
151
  setPollingMode(mode) {
@@ -250,8 +262,35 @@ class MultiProviderQuotaService {
250
262
  this.currentPeakHours = result;
251
263
  }
252
264
  async poll() {
253
- await this.refreshPeakHours();
265
+ if (!this.polling || this.pollInFlight) {
266
+ this.pendingWake = this.polling;
267
+ return;
268
+ }
269
+ this.pollInFlight = true;
270
+ this.pendingWake = false;
254
271
  try {
272
+ let accountPresent = false;
273
+ try {
274
+ accountPresent = this.readClaudeAccount().source !== 'none';
275
+ }
276
+ catch {
277
+ accountPresent = false;
278
+ }
279
+ if (!accountPresent) {
280
+ this.claudeDormant = true;
281
+ this.resetTransientFailureState();
282
+ this.authUnavailableActive = false;
283
+ this.lastSuccessfulClaudeState = null;
284
+ // Stay visible while dormant: consumers render their sign-in prompt
285
+ // from an unavailable auth entry, not from a missing map key.
286
+ if (this.quotaByProvider.claude?.error !== SIGNED_OUT_ERROR) {
287
+ this.lastEmittedClaudeState = null;
288
+ this.emitClaudeState(makeUnavailableClaudeState(SIGNED_OUT_ERROR, 'auth'));
289
+ }
290
+ return;
291
+ }
292
+ this.claudeDormant = false;
293
+ await this.refreshPeakHours();
255
294
  const creds = await this.readClaudeCredentials();
256
295
  if (!creds) {
257
296
  this.handleUnavailableState(makeUnavailableClaudeState('Renew your Claude Max OAuth to view quota', 'auth'));
@@ -289,9 +328,31 @@ class MultiProviderQuotaService {
289
328
  this.handleTransientFailure(makeUnavailableClaudeState(message));
290
329
  }
291
330
  finally {
292
- this.scheduleNextPoll();
331
+ this.pollInFlight = false;
332
+ if (this.pendingWake) {
333
+ this.pendingWake = false;
334
+ void this.poll();
335
+ }
336
+ else if (!this.claudeDormant) {
337
+ this.scheduleNextPoll();
338
+ }
293
339
  }
294
340
  }
341
+ wakeForAccountChange() {
342
+ if (!this.polling)
343
+ return;
344
+ if (this.timer) {
345
+ clearTimeout(this.timer);
346
+ this.timer = undefined;
347
+ }
348
+ this.codexWatcher?.start();
349
+ this.zaiWatcher?.start();
350
+ if (this.pollInFlight) {
351
+ this.pendingWake = true;
352
+ return;
353
+ }
354
+ void this.poll();
355
+ }
295
356
  emitClaudeState(state) {
296
357
  const withPeakHours = this.currentPeakHours !== undefined ? { ...state, peakHours: this.currentPeakHours } : state;
297
358
  const withFailure = {
package/dist/node.d.ts CHANGED
@@ -10,12 +10,31 @@ export type { HydrateOptions, HydrateResult } from './pricingCatalog';
10
10
  export { loadObservedContextWindows, recordObservedContextWindow, getObservedContextWindowPath, } from './observedContextWindows';
11
11
  export type { ObservedContextWindowOptions, ObservedContextWindowStore, } from './observedContextWindows';
12
12
  export { listRecentSessions, readSessionTranscript } from './sessionTranscripts';
13
+ export { computeSessionFileStats, firstUserPrompt, providerContextSizeFn, readSessionFileStats, } from './sessionStats';
14
+ export { readSessionReportInputs } from './report/sessionReportInputs';
15
+ export type { SessionReportInputs } from './report/sessionReportInputs';
16
+ export { fingerprintString, sessionFingerprintParts } from './sessionFingerprint';
17
+ export { STATE_FILE_NAME, STATE_FILE_SCHEMA_VERSION, billingBlockToStateFile, getStateFilePath, quotaToStateFile, readStateFile, writeStateFile, } from './stateFile';
18
+ export type { SidekickStateFile, SidekickStateInput, StateFileAccount, StateFileBillingBlock, StateFileContext, StateFileQuota, StateFileQuotaWindow, StateFileSession, StateFileWriter, WriteStateFileOptions, } from './stateFile';
19
+ export type { ComputeSessionFileStatsOptions, ReadSessionFileStatsOptions } from './sessionStats';
20
+ export { BILLING_BLOCK_DURATION_MS, computeBillingBlocks, findActiveBillingBlock, } from './usage/billingBlocks';
21
+ export type { BillingBlock, BillingBlockInput, BillingBlockModelUsage, BillingBlockTokens, ComputeBillingBlocksOptions, } from './usage/billingBlocks';
22
+ export { MAX_USAGE_CACHE_FILES, USAGE_CACHE_VERSION, collectUsageEvents, getUsageCacheDir, pruneUsageCache, } from './usage/usageEvents';
23
+ export type { CollectUsageEventsOptions, CollectUsageEventsResult, UsageEventRecord, UsageSessionRecord, } from './usage/usageEvents';
24
+ export { applySessionSummary, isFileImported, markFileImported, removeSessionSummary, sessionSummaryFromStats, } from './historicalStore';
25
+ export type { ApplySessionSummaryOptions, SessionSummaryFromStatsOptions } from './historicalStore';
26
+ export { ACTIVE_SESSION_MTIME_THRESHOLD_MS, importSessionHistory, } from './usage/importSessionHistory';
27
+ export type { ImportSessionHistoryOptions, ImportSessionHistoryResult, } from './usage/importSessionHistory';
28
+ export { bucketUsage, summarizeUsageRows, usageBucketKey, weekKey } from './usage/usageReports';
29
+ export type { BucketUsageOptions, UsageBucketRow, UsageGranularity, UsageGroupDimension, UsageTotals, } from './usage/usageReports';
13
30
  export type { ProviderSessionIndex, ListRecentSessionsOptions } from './sessionTranscripts';
14
31
  export { findCodexRolloutFile } from './providers/codex';
32
+ export { walkRolloutFiles, walkRolloutFilesAsync } from './providers/rolloutWalker';
33
+ export type { RolloutFileInfo, WalkRolloutFilesOptions } from './providers/rolloutWalker';
15
34
  export type { FindCodexRolloutFileOptions } from './providers/codex';
16
35
  export { readCodexHistory } from './parsers/codexHistory';
17
36
  export type { CodexHistoryEntry, ReadCodexHistoryOptions } from './parsers/codexHistory';
18
- export { listSessionPreviews, readSessionPreview } from './sessionPreviews';
19
- export type { ListSessionPreviewsOptions, ReadSessionPreviewOptions, SessionPreview, } from './sessionPreviews';
20
- export { ObservedSessionCollector, observedSessionSourceFromProvider, fileFingerprint, } from './observedSessionCollector';
21
- export type { ObservedSessionReference, ObservedSessionCollectionSource, ProviderObservedSessionCollectionSource, ObservedSessionDiagnosticKind, ObservedSessionDiagnostic, ObservedSessionCollection, ObservedSessionCollectorOptions, } from './observedSessionCollector';
37
+ export { listSessionPreviews, listSessionPreviewsAsync, readSessionPreview, readSessionPreviewAsync, } from './sessionPreviews';
38
+ export type { AsyncSessionPreviewOptions, ListSessionPreviewsAsyncOptions, ListSessionPreviewsOptions, ReadSessionPreviewAsyncOptions, ReadSessionPreviewOptions, SessionPreview, SessionPreviewListResult, SessionPreviewReadResult, } from './sessionPreviews';
39
+ export { ObservedSessionCollector, observedSessionSourceFromProvider, fileFingerprint, fileFingerprintParts, } from './observedSessionCollector';
40
+ export type { KnownObservedSessionFingerprint, ObservedSessionChange, ObservedSessionChangeBatch, ObservedSessionChangeType, ObservedSessionReference, ObservedSessionFingerprintParts, ObservedSessionSourceSubscribeOptions, ObservedSessionSubscribeOptions, ObservedSessionCollectionSource, ProviderObservedSessionCollectionSource, ObservedSessionDiagnosticKind, ObservedSessionDiagnosticPhase, ObservedSessionDiagnosticSeverity, ObservedSessionDiagnostic, ObservedSessionCollection, ObservedSessionCollectorOptions, } from './observedSessionCollector';
package/dist/node.js CHANGED
@@ -7,7 +7,8 @@
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.readSessionPreview = exports.listSessionPreviews = exports.readCodexHistory = exports.findCodexRolloutFile = exports.readSessionTranscript = exports.listRecentSessions = exports.getObservedContextWindowPath = exports.recordObservedContextWindow = exports.loadObservedContextWindows = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmContextWindows = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = void 0;
10
+ exports.readSessionPreviewAsync = exports.readSessionPreview = exports.listSessionPreviewsAsync = exports.listSessionPreviews = exports.readCodexHistory = exports.walkRolloutFilesAsync = exports.walkRolloutFiles = exports.findCodexRolloutFile = exports.weekKey = exports.usageBucketKey = exports.summarizeUsageRows = exports.bucketUsage = exports.importSessionHistory = exports.ACTIVE_SESSION_MTIME_THRESHOLD_MS = exports.sessionSummaryFromStats = exports.removeSessionSummary = exports.markFileImported = exports.isFileImported = exports.applySessionSummary = exports.pruneUsageCache = exports.getUsageCacheDir = exports.collectUsageEvents = exports.USAGE_CACHE_VERSION = exports.MAX_USAGE_CACHE_FILES = exports.findActiveBillingBlock = exports.computeBillingBlocks = exports.BILLING_BLOCK_DURATION_MS = exports.writeStateFile = exports.readStateFile = exports.quotaToStateFile = exports.getStateFilePath = exports.billingBlockToStateFile = exports.STATE_FILE_SCHEMA_VERSION = exports.STATE_FILE_NAME = exports.sessionFingerprintParts = exports.fingerprintString = exports.readSessionReportInputs = exports.readSessionFileStats = exports.providerContextSizeFn = exports.firstUserPrompt = exports.computeSessionFileStats = exports.readSessionTranscript = exports.listRecentSessions = exports.getObservedContextWindowPath = exports.recordObservedContextWindow = exports.loadObservedContextWindows = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmContextWindows = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = void 0;
11
+ exports.fileFingerprintParts = exports.fileFingerprint = exports.observedSessionSourceFromProvider = exports.ObservedSessionCollector = void 0;
11
12
  var pricingCatalog_1 = require("./pricingCatalog");
12
13
  Object.defineProperty(exports, "hydratePricingCatalog", { enumerable: true, get: function () { return pricingCatalog_1.hydratePricingCatalog; } });
13
14
  Object.defineProperty(exports, "normalizeLiteLlmCatalog", { enumerable: true, get: function () { return pricingCatalog_1.normalizeLiteLlmCatalog; } });
@@ -20,14 +21,62 @@ Object.defineProperty(exports, "getObservedContextWindowPath", { enumerable: tru
20
21
  var sessionTranscripts_1 = require("./sessionTranscripts");
21
22
  Object.defineProperty(exports, "listRecentSessions", { enumerable: true, get: function () { return sessionTranscripts_1.listRecentSessions; } });
22
23
  Object.defineProperty(exports, "readSessionTranscript", { enumerable: true, get: function () { return sessionTranscripts_1.readSessionTranscript; } });
24
+ var sessionStats_1 = require("./sessionStats");
25
+ Object.defineProperty(exports, "computeSessionFileStats", { enumerable: true, get: function () { return sessionStats_1.computeSessionFileStats; } });
26
+ Object.defineProperty(exports, "firstUserPrompt", { enumerable: true, get: function () { return sessionStats_1.firstUserPrompt; } });
27
+ Object.defineProperty(exports, "providerContextSizeFn", { enumerable: true, get: function () { return sessionStats_1.providerContextSizeFn; } });
28
+ Object.defineProperty(exports, "readSessionFileStats", { enumerable: true, get: function () { return sessionStats_1.readSessionFileStats; } });
29
+ var sessionReportInputs_1 = require("./report/sessionReportInputs");
30
+ Object.defineProperty(exports, "readSessionReportInputs", { enumerable: true, get: function () { return sessionReportInputs_1.readSessionReportInputs; } });
31
+ var sessionFingerprint_1 = require("./sessionFingerprint");
32
+ Object.defineProperty(exports, "fingerprintString", { enumerable: true, get: function () { return sessionFingerprint_1.fingerprintString; } });
33
+ Object.defineProperty(exports, "sessionFingerprintParts", { enumerable: true, get: function () { return sessionFingerprint_1.sessionFingerprintParts; } });
34
+ var stateFile_1 = require("./stateFile");
35
+ Object.defineProperty(exports, "STATE_FILE_NAME", { enumerable: true, get: function () { return stateFile_1.STATE_FILE_NAME; } });
36
+ Object.defineProperty(exports, "STATE_FILE_SCHEMA_VERSION", { enumerable: true, get: function () { return stateFile_1.STATE_FILE_SCHEMA_VERSION; } });
37
+ Object.defineProperty(exports, "billingBlockToStateFile", { enumerable: true, get: function () { return stateFile_1.billingBlockToStateFile; } });
38
+ Object.defineProperty(exports, "getStateFilePath", { enumerable: true, get: function () { return stateFile_1.getStateFilePath; } });
39
+ Object.defineProperty(exports, "quotaToStateFile", { enumerable: true, get: function () { return stateFile_1.quotaToStateFile; } });
40
+ Object.defineProperty(exports, "readStateFile", { enumerable: true, get: function () { return stateFile_1.readStateFile; } });
41
+ Object.defineProperty(exports, "writeStateFile", { enumerable: true, get: function () { return stateFile_1.writeStateFile; } });
42
+ var billingBlocks_1 = require("./usage/billingBlocks");
43
+ Object.defineProperty(exports, "BILLING_BLOCK_DURATION_MS", { enumerable: true, get: function () { return billingBlocks_1.BILLING_BLOCK_DURATION_MS; } });
44
+ Object.defineProperty(exports, "computeBillingBlocks", { enumerable: true, get: function () { return billingBlocks_1.computeBillingBlocks; } });
45
+ Object.defineProperty(exports, "findActiveBillingBlock", { enumerable: true, get: function () { return billingBlocks_1.findActiveBillingBlock; } });
46
+ var usageEvents_1 = require("./usage/usageEvents");
47
+ Object.defineProperty(exports, "MAX_USAGE_CACHE_FILES", { enumerable: true, get: function () { return usageEvents_1.MAX_USAGE_CACHE_FILES; } });
48
+ Object.defineProperty(exports, "USAGE_CACHE_VERSION", { enumerable: true, get: function () { return usageEvents_1.USAGE_CACHE_VERSION; } });
49
+ Object.defineProperty(exports, "collectUsageEvents", { enumerable: true, get: function () { return usageEvents_1.collectUsageEvents; } });
50
+ Object.defineProperty(exports, "getUsageCacheDir", { enumerable: true, get: function () { return usageEvents_1.getUsageCacheDir; } });
51
+ Object.defineProperty(exports, "pruneUsageCache", { enumerable: true, get: function () { return usageEvents_1.pruneUsageCache; } });
52
+ var historicalStore_1 = require("./historicalStore");
53
+ Object.defineProperty(exports, "applySessionSummary", { enumerable: true, get: function () { return historicalStore_1.applySessionSummary; } });
54
+ Object.defineProperty(exports, "isFileImported", { enumerable: true, get: function () { return historicalStore_1.isFileImported; } });
55
+ Object.defineProperty(exports, "markFileImported", { enumerable: true, get: function () { return historicalStore_1.markFileImported; } });
56
+ Object.defineProperty(exports, "removeSessionSummary", { enumerable: true, get: function () { return historicalStore_1.removeSessionSummary; } });
57
+ Object.defineProperty(exports, "sessionSummaryFromStats", { enumerable: true, get: function () { return historicalStore_1.sessionSummaryFromStats; } });
58
+ var importSessionHistory_1 = require("./usage/importSessionHistory");
59
+ Object.defineProperty(exports, "ACTIVE_SESSION_MTIME_THRESHOLD_MS", { enumerable: true, get: function () { return importSessionHistory_1.ACTIVE_SESSION_MTIME_THRESHOLD_MS; } });
60
+ Object.defineProperty(exports, "importSessionHistory", { enumerable: true, get: function () { return importSessionHistory_1.importSessionHistory; } });
61
+ var usageReports_1 = require("./usage/usageReports");
62
+ Object.defineProperty(exports, "bucketUsage", { enumerable: true, get: function () { return usageReports_1.bucketUsage; } });
63
+ Object.defineProperty(exports, "summarizeUsageRows", { enumerable: true, get: function () { return usageReports_1.summarizeUsageRows; } });
64
+ Object.defineProperty(exports, "usageBucketKey", { enumerable: true, get: function () { return usageReports_1.usageBucketKey; } });
65
+ Object.defineProperty(exports, "weekKey", { enumerable: true, get: function () { return usageReports_1.weekKey; } });
23
66
  var codex_1 = require("./providers/codex");
24
67
  Object.defineProperty(exports, "findCodexRolloutFile", { enumerable: true, get: function () { return codex_1.findCodexRolloutFile; } });
68
+ var rolloutWalker_1 = require("./providers/rolloutWalker");
69
+ Object.defineProperty(exports, "walkRolloutFiles", { enumerable: true, get: function () { return rolloutWalker_1.walkRolloutFiles; } });
70
+ Object.defineProperty(exports, "walkRolloutFilesAsync", { enumerable: true, get: function () { return rolloutWalker_1.walkRolloutFilesAsync; } });
25
71
  var codexHistory_1 = require("./parsers/codexHistory");
26
72
  Object.defineProperty(exports, "readCodexHistory", { enumerable: true, get: function () { return codexHistory_1.readCodexHistory; } });
27
73
  var sessionPreviews_1 = require("./sessionPreviews");
28
74
  Object.defineProperty(exports, "listSessionPreviews", { enumerable: true, get: function () { return sessionPreviews_1.listSessionPreviews; } });
75
+ Object.defineProperty(exports, "listSessionPreviewsAsync", { enumerable: true, get: function () { return sessionPreviews_1.listSessionPreviewsAsync; } });
29
76
  Object.defineProperty(exports, "readSessionPreview", { enumerable: true, get: function () { return sessionPreviews_1.readSessionPreview; } });
77
+ Object.defineProperty(exports, "readSessionPreviewAsync", { enumerable: true, get: function () { return sessionPreviews_1.readSessionPreviewAsync; } });
30
78
  var observedSessionCollector_1 = require("./observedSessionCollector");
31
79
  Object.defineProperty(exports, "ObservedSessionCollector", { enumerable: true, get: function () { return observedSessionCollector_1.ObservedSessionCollector; } });
32
80
  Object.defineProperty(exports, "observedSessionSourceFromProvider", { enumerable: true, get: function () { return observedSessionCollector_1.observedSessionSourceFromProvider; } });
33
81
  Object.defineProperty(exports, "fileFingerprint", { enumerable: true, get: function () { return observedSessionCollector_1.fileFingerprint; } });
82
+ Object.defineProperty(exports, "fileFingerprintParts", { enumerable: true, get: function () { return observedSessionCollector_1.fileFingerprintParts; } });
@@ -44,6 +44,13 @@ export interface ObservedContextWindowStore {
44
44
  version: 1;
45
45
  models: Record<string, ObservedContextWindowEntry>;
46
46
  }
47
+ /**
48
+ * Observations older than this are ignored on load. A provider reports its
49
+ * context window on every session, so a live model refreshes its entry
50
+ * constantly; an entry that has gone quiet this long belongs to a tier or
51
+ * model the user no longer runs and must not outrank the catalog forever.
52
+ */
53
+ export declare const OBSERVED_CONTEXT_WINDOW_TTL_MS: number;
47
54
  /** Resolve the on-disk store path. */
48
55
  export declare function getObservedContextWindowPath(cacheDir?: string): string;
49
56
  /** Reset the per-store write dedupe. Test-only; not part of the public API. */
@@ -64,6 +64,7 @@ var __importStar = (this && this.__importStar) || (function () {
64
64
  };
65
65
  })();
66
66
  Object.defineProperty(exports, "__esModule", { value: true });
67
+ exports.OBSERVED_CONTEXT_WINDOW_TTL_MS = void 0;
67
68
  exports.getObservedContextWindowPath = getObservedContextWindowPath;
68
69
  exports._clearObservedContextWindowWriteCache = _clearObservedContextWindowWriteCache;
69
70
  exports.loadObservedContextWindows = loadObservedContextWindows;
@@ -76,6 +77,13 @@ const atomic_1 = require("./writers/atomic");
76
77
  // ── Constants ──
77
78
  const STORE_FILE_NAME = 'observed-context-windows.json';
78
79
  const STORE_VERSION = 1;
80
+ /**
81
+ * Observations older than this are ignored on load. A provider reports its
82
+ * context window on every session, so a live model refreshes its entry
83
+ * constantly; an entry that has gone quiet this long belongs to a tier or
84
+ * model the user no longer runs and must not outrank the catalog forever.
85
+ */
86
+ exports.OBSERVED_CONTEXT_WINDOW_TTL_MS = 30 * 86_400_000;
79
87
  /** Resolve the on-disk store path. */
80
88
  function getObservedContextWindowPath(cacheDir) {
81
89
  return path.join(cacheDir ?? (0, paths_1.getConfigDir)(), STORE_FILE_NAME);
@@ -98,13 +106,20 @@ const lastPersistedByStore = new Map();
98
106
  function _clearObservedContextWindowWriteCache() {
99
107
  lastPersistedByStore.clear();
100
108
  }
101
- /** Keep only well-formed entries; a hand-edited or partly-corrupt file shouldn't poison lookups. */
102
- function toOverrideMap(store) {
109
+ /**
110
+ * Keep only well-formed, recent entries; a hand-edited or partly-corrupt file
111
+ * shouldn't poison lookups, and a stale observation shouldn't outrank the
112
+ * catalog (see {@link OBSERVED_CONTEXT_WINDOW_TTL_MS}).
113
+ */
114
+ function toOverrideMap(store, now = Date.now()) {
103
115
  const out = {};
104
116
  for (const [modelId, entry] of Object.entries(store.models ?? {})) {
105
117
  const window = entry?.contextWindow;
106
118
  if (typeof window !== 'number' || !Number.isFinite(window) || window <= 0)
107
119
  continue;
120
+ const observedAt = typeof entry.observedAt === 'string' ? Date.parse(entry.observedAt) : NaN;
121
+ if (Number.isFinite(observedAt) && now - observedAt > exports.OBSERVED_CONTEXT_WINDOW_TTL_MS)
122
+ continue;
108
123
  out[modelId] = window;
109
124
  }
110
125
  return out;
@@ -1,24 +1,42 @@
1
- import type { ObservedAgentSessionV1, ProviderCapabilitiesV1 } from './types/observedSessionV1';
1
+ import type { ObservedAgentSessionV1, ProviderCapabilitiesV1, ProviderSessionAdapterV1Options } from './types/observedSessionV1';
2
2
  import type { SessionProviderBase } from './providers/types';
3
+ import type { ObservedSessionFingerprintParts } from './sessionFingerprint';
4
+ export { fileFingerprint, fileFingerprintParts } from './sessionFingerprint';
5
+ export type { ObservedSessionFingerprintParts } from './sessionFingerprint';
3
6
  export interface ObservedSessionReference {
4
7
  sessionId: string;
5
8
  /** Opaque source locator used only by the source implementation. Never emitted in diagnostics. */
6
9
  sourceKey?: string;
10
+ /** Backward-compatible opaque fingerprint. */
7
11
  fingerprintHint?: string;
12
+ /** Structured fingerprint for consumers that previously parsed fingerprintHint. */
13
+ fingerprintParts?: ObservedSessionFingerprintParts;
14
+ }
15
+ export interface ObservedSessionSourceSubscribeOptions {
16
+ pollIntervalMs?: number;
8
17
  }
9
18
  export interface ObservedSessionCollectionSource<T = unknown> {
10
19
  providerId: string;
11
20
  discover(): Promise<ObservedSessionReference[]> | ObservedSessionReference[];
12
21
  read(reference: ObservedSessionReference): Promise<T> | T;
22
+ refreshCached?(reference: ObservedSessionReference, value: T, observedAt: string): Promise<T> | T;
23
+ /** Low-level invalidation signal; the collector performs fingerprint reconciliation. */
24
+ subscribe?(listener: () => void, options?: ObservedSessionSourceSubscribeOptions): {
25
+ dispose(): void;
26
+ };
13
27
  dispose?(): void;
14
28
  }
15
29
  export interface ProviderObservedSessionCollectionSource extends ObservedSessionCollectionSource<ObservedAgentSessionV1> {
16
30
  capabilities: ProviderCapabilitiesV1;
17
31
  }
18
32
  export type ObservedSessionDiagnosticKind = 'provider-discovery-failed' | 'session-read-failed' | 'provider-recovered' | 'session-recovered';
33
+ export type ObservedSessionDiagnosticSeverity = 'info' | 'warning' | 'error';
34
+ export type ObservedSessionDiagnosticPhase = 'discover' | 'read' | 'recover';
19
35
  /** Content-safe diagnostics: identifiers and retry metadata only. */
20
36
  export interface ObservedSessionDiagnostic {
21
37
  kind: ObservedSessionDiagnosticKind;
38
+ severity: ObservedSessionDiagnosticSeverity;
39
+ phase: ObservedSessionDiagnosticPhase;
22
40
  providerId: string;
23
41
  sessionId?: string;
24
42
  attempt?: number;
@@ -28,6 +46,13 @@ export interface ObservedSessionCollection<T = unknown> {
28
46
  providerId: string;
29
47
  sessionId: string;
30
48
  value: T;
49
+ fingerprint: string | null;
50
+ fingerprintParts: ObservedSessionFingerprintParts | null;
51
+ cacheHit: boolean;
52
+ /** Time this observation was returned. */
53
+ observedAt: string;
54
+ /** Time content was last parsed; unchanged on a cache hit. */
55
+ contentObservedAt: string;
31
56
  }
32
57
  export interface ObservedSessionCollectorOptions<T = unknown> {
33
58
  sources: ObservedSessionCollectionSource<T>[];
@@ -37,26 +62,69 @@ export interface ObservedSessionCollectorOptions<T = unknown> {
37
62
  now(): number;
38
63
  };
39
64
  fingerprint?: (source: ObservedSessionCollectionSource<T>, reference: ObservedSessionReference) => Promise<string | null> | string | null;
65
+ maxConcurrentReads?: number;
66
+ yieldBetweenReads?: () => Promise<void> | void;
40
67
  onObservation?: (observation: ObservedSessionCollection<T>) => void;
41
68
  onDiagnostic?: (diagnostic: ObservedSessionDiagnostic) => void;
42
69
  }
70
+ export type ObservedSessionChangeType = 'added' | 'changed' | 'removed';
71
+ export interface ObservedSessionChange {
72
+ type: ObservedSessionChangeType;
73
+ reference: ObservedSessionReference;
74
+ previousFingerprint: string | null;
75
+ previousFingerprintParts: ObservedSessionFingerprintParts | null;
76
+ fingerprint: string | null;
77
+ fingerprintParts: ObservedSessionFingerprintParts | null;
78
+ }
79
+ export interface ObservedSessionChangeBatch {
80
+ providerId: string;
81
+ changes: ObservedSessionChange[];
82
+ observedAt: string;
83
+ }
84
+ export interface KnownObservedSessionFingerprint {
85
+ providerId: string;
86
+ sessionId: string;
87
+ fingerprint: string;
88
+ fingerprintParts?: ObservedSessionFingerprintParts;
89
+ }
90
+ export interface ObservedSessionSubscribeOptions {
91
+ debounceMs?: number;
92
+ pollIntervalMs?: number;
93
+ knownFingerprints?: readonly KnownObservedSessionFingerprint[];
94
+ }
43
95
  /**
44
- * One-shot, host-scheduled observed-session collection with independent
45
- * provider/session isolation and bounded retry state.
96
+ * Fail-soft observed-session collection with fingerprint-keyed parse caching,
97
+ * cooperative reads, and optional provider-level subscriptions.
46
98
  */
47
99
  export declare class ObservedSessionCollector<T = unknown> {
48
100
  private readonly options;
49
101
  private readonly failures;
102
+ private readonly cache;
103
+ private readonly subscriptions;
50
104
  private readonly initialBackoffMs;
51
105
  private readonly maxBackoffMs;
52
106
  private readonly clock;
107
+ private readonly maxConcurrentReads;
108
+ private readonly yieldBetweenReads;
53
109
  constructor(options: ObservedSessionCollectorOptions<T>);
54
110
  collect(): Promise<ObservedSessionCollection<T>[]>;
111
+ /**
112
+ * Subscribe to debounced provider changes. The listener receives fingerprints
113
+ * only; call collect() to consume changed sessions through the parse cache.
114
+ */
115
+ subscribe(listener: (batch: ObservedSessionChangeBatch) => void, options?: ObservedSessionSubscribeOptions): {
116
+ dispose(): void;
117
+ };
55
118
  reset(): void;
56
119
  dispose(): void;
57
120
  private collectSource;
58
121
  private collectSession;
122
+ private makeObservation;
123
+ private emitObservation;
124
+ private reconcileSource;
59
125
  private fingerprint;
126
+ private evictMissing;
127
+ private mapWithConcurrency;
60
128
  private fail;
61
129
  private recover;
62
130
  private emitDiagnostic;
@@ -66,6 +134,4 @@ export declare class ObservedSessionCollector<T = unknown> {
66
134
  * Adapt a provider using path-only discovery and independent adapter reads, so
67
135
  * one malformed session cannot reject discovery of its healthy siblings.
68
136
  */
69
- export declare function observedSessionSourceFromProvider(provider: SessionProviderBase, cwd: string): ProviderObservedSessionCollectionSource;
70
- /** Size/mtime fingerprint suitable for retry bypass; returns null for synthetic DB locators. */
71
- export declare function fileFingerprint(sourcePath: string): string | null;
137
+ export declare function observedSessionSourceFromProvider(provider: SessionProviderBase, cwd: string, options?: ProviderSessionAdapterV1Options): ProviderObservedSessionCollectionSource;