sidekick-shared 0.25.0 → 0.26.1

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 (119) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +50 -3
  3. package/dist/accountChanges.js +22 -3
  4. package/dist/accountRegistry.d.ts +22 -1
  5. package/dist/accountRegistry.js +37 -14
  6. package/dist/accountStatus.d.ts +9 -1
  7. package/dist/accountStatus.js +3 -3
  8. package/dist/accounts.d.ts +3 -1
  9. package/dist/accounts.js +84 -64
  10. package/dist/aggregation/EventAggregator.d.ts +15 -2
  11. package/dist/aggregation/EventAggregator.js +54 -9
  12. package/dist/aggregation/costProvenance.d.ts +14 -0
  13. package/dist/aggregation/costProvenance.js +35 -0
  14. package/dist/aggregation/snapshot.d.ts +13 -2
  15. package/dist/aggregation/snapshot.js +57 -6
  16. package/dist/aggregation/types.d.ts +30 -1
  17. package/dist/analytics/compactionLedger.js +15 -5
  18. package/dist/analytics/qualityScore.js +2 -1
  19. package/dist/browser.d.ts +11 -1
  20. package/dist/browser.js +29 -1
  21. package/dist/codexProfiles.d.ts +3 -1
  22. package/dist/codexProfiles.js +4 -2
  23. package/dist/codexQuota.d.ts +2 -0
  24. package/dist/codexQuota.js +11 -66
  25. package/dist/failingTools.d.ts +37 -0
  26. package/dist/failingTools.js +70 -0
  27. package/dist/formatters/sessionDump.js +13 -7
  28. package/dist/formatting.d.ts +13 -0
  29. package/dist/formatting.js +35 -0
  30. package/dist/historicalStore.d.ts +47 -0
  31. package/dist/historicalStore.js +312 -0
  32. package/dist/index.d.ts +46 -12
  33. package/dist/index.js +112 -24
  34. package/dist/modelContext.js +8 -3
  35. package/dist/modelInfo.js +45 -27
  36. package/dist/node.d.ts +19 -0
  37. package/dist/node.js +47 -1
  38. package/dist/observedContextWindows.d.ts +7 -0
  39. package/dist/observedContextWindows.js +17 -2
  40. package/dist/observedSessionCollector.d.ts +3 -8
  41. package/dist/observedSessionCollector.js +10 -27
  42. package/dist/parsers/sessionActivityDetector.d.ts +24 -2
  43. package/dist/parsers/sessionActivityDetector.js +95 -17
  44. package/dist/parsers/sessionPathResolver.d.ts +11 -0
  45. package/dist/parsers/sessionPathResolver.js +13 -10
  46. package/dist/peakHours.d.ts +34 -3
  47. package/dist/peakHours.js +92 -7
  48. package/dist/pricingCatalog.d.ts +7 -0
  49. package/dist/pricingCatalog.js +8 -5
  50. package/dist/providers/claudeCode.js +11 -96
  51. package/dist/providers/codex.d.ts +2 -0
  52. package/dist/providers/codex.js +94 -259
  53. package/dist/providers/detect.d.ts +2 -0
  54. package/dist/providers/detect.js +29 -24
  55. package/dist/providers/factory.d.ts +6 -0
  56. package/dist/providers/openCode.d.ts +2 -0
  57. package/dist/providers/openCode.js +105 -121
  58. package/dist/providers/rolloutWalker.d.ts +53 -0
  59. package/dist/providers/rolloutWalker.js +209 -0
  60. package/dist/providers/types.d.ts +30 -0
  61. package/dist/quota.d.ts +30 -3
  62. package/dist/quota.js +35 -3
  63. package/dist/quotaHistory.d.ts +1 -1
  64. package/dist/quotaHistory.js +36 -35
  65. package/dist/quotaPresentation.js +17 -5
  66. package/dist/quotaResolver.d.ts +83 -0
  67. package/dist/quotaResolver.js +234 -0
  68. package/dist/quotaSnapshots.d.ts +15 -2
  69. package/dist/quotaSnapshots.js +25 -3
  70. package/dist/quotaThresholds.d.ts +60 -0
  71. package/dist/quotaThresholds.js +90 -0
  72. package/dist/report/htmlReportGenerator.js +10 -4
  73. package/dist/report/sessionReportInputs.d.ts +23 -0
  74. package/dist/report/sessionReportInputs.js +34 -0
  75. package/dist/schemas/index.d.ts +2 -0
  76. package/dist/schemas/index.js +10 -1
  77. package/dist/schemas/quota.d.ts +139 -0
  78. package/dist/schemas/quota.js +17 -2
  79. package/dist/schemas/quotaHistory.d.ts +1 -0
  80. package/dist/schemas/quotaHistory.js +1 -1
  81. package/dist/schemas/sessionEvent.d.ts +6 -0
  82. package/dist/schemas/sessionEvent.js +1 -0
  83. package/dist/schemas/stateFile.d.ts +198 -0
  84. package/dist/schemas/stateFile.js +77 -0
  85. package/dist/schemas/transcript.js +2 -0
  86. package/dist/sessionFingerprint.d.ts +25 -0
  87. package/dist/sessionFingerprint.js +46 -0
  88. package/dist/sessionPreviews.d.ts +7 -1
  89. package/dist/sessionPreviews.js +15 -3
  90. package/dist/sessionStats.d.ts +51 -0
  91. package/dist/sessionStats.js +166 -0
  92. package/dist/stateFile.d.ts +113 -0
  93. package/dist/stateFile.js +160 -0
  94. package/dist/statusline/claudeStatuslinePayload.d.ts +89 -0
  95. package/dist/statusline/claudeStatuslinePayload.js +152 -0
  96. package/dist/statusline/formatter.d.ts +13 -0
  97. package/dist/statusline/formatter.js +48 -6
  98. package/dist/statusline/index.d.ts +10 -2
  99. package/dist/statusline/index.js +19 -1
  100. package/dist/tokenSummary.d.ts +62 -0
  101. package/dist/tokenSummary.js +72 -0
  102. package/dist/transcript.d.ts +5 -0
  103. package/dist/transcript.js +2 -0
  104. package/dist/types/observedSessionV1.d.ts +2 -0
  105. package/dist/types/observedSessionV1.js +75 -18
  106. package/dist/types/sessionEvent.d.ts +2 -0
  107. package/dist/usage/billingBlocks.d.ts +85 -0
  108. package/dist/usage/billingBlocks.js +153 -0
  109. package/dist/usage/importSessionHistory.d.ts +53 -0
  110. package/dist/usage/importSessionHistory.js +88 -0
  111. package/dist/usage/usageEvents.d.ts +92 -0
  112. package/dist/usage/usageEvents.js +322 -0
  113. package/dist/usage/usageReports.d.ts +76 -0
  114. package/dist/usage/usageReports.js +208 -0
  115. package/dist/watchers/factory.d.ts +6 -0
  116. package/dist/watchers/factory.js +30 -25
  117. package/dist/zaiQuotaApi.d.ts +2 -0
  118. package/dist/zaiQuotaApi.js +2 -0
  119. package/package.json +1 -1
@@ -60,6 +60,7 @@ const path = __importStar(require("node:path"));
60
60
  const modelContext_1 = require("./modelContext");
61
61
  const modelInfo_1 = require("./modelInfo");
62
62
  const paths_1 = require("./paths");
63
+ const atomic_1 = require("./writers/atomic");
63
64
  // ── Constants ──
64
65
  exports.LITELLM_CATALOG_URL = 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json';
65
66
  const CACHE_FILE_NAME = 'pricing-catalog.json';
@@ -77,16 +78,16 @@ const DEFAULT_TIMEOUT_MS = 3_000;
77
78
  * Always resolves — never throws. Offline-safe.
78
79
  */
79
80
  async function hydratePricingCatalog(options = {}) {
80
- const { cacheDir = (0, paths_1.getConfigDir)(), fetchImpl = globalThis.fetch, ttlMs = DEFAULT_TTL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, url = exports.LITELLM_CATALOG_URL, logger, } = options;
81
+ const { cacheDir = (0, paths_1.getConfigDir)(), fetchImpl = globalThis.fetch, ttlMs = DEFAULT_TTL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, url = exports.LITELLM_CATALOG_URL, logger, offline = false, } = options;
81
82
  const cachePath = path.join(cacheDir, CACHE_FILE_NAME);
82
83
  const now = Date.now();
83
- // 1. Fresh cache?
84
+ // 1. Fresh cache? (Offline callers take any cache, however old.)
84
85
  const cached = await readCache(cachePath);
85
- if (cached && now - Date.parse(cached.fetchedAt) < ttlMs) {
86
+ if (cached && (offline || now - Date.parse(cached.fetchedAt) < ttlMs)) {
86
87
  return applyCacheFile(cached, 'cache');
87
88
  }
88
89
  // 2. Try network.
89
- if (fetchImpl) {
90
+ if (fetchImpl && !offline) {
90
91
  const fetched = await fetchCatalog(url, fetchImpl, timeoutMs, logger);
91
92
  if (fetched) {
92
93
  const payload = {
@@ -256,7 +257,9 @@ async function readCache(cachePath) {
256
257
  async function writeCache(cachePath, payload, logger) {
257
258
  try {
258
259
  await node_fs_1.promises.mkdir(path.dirname(cachePath), { recursive: true });
259
- await node_fs_1.promises.writeFile(cachePath, JSON.stringify(payload, null, 2), 'utf8');
260
+ // The CLI and the extension host can both refresh the catalog at once;
261
+ // the atomic writer keeps a reader from ever seeing a half-written file.
262
+ await (0, atomic_1.atomicWriteJson)(cachePath, payload);
260
263
  }
261
264
  catch (err) {
262
265
  logger?.(`pricingCatalog: failed to write cache: ${String(err)}`);
@@ -48,6 +48,7 @@ const os = __importStar(require("os"));
48
48
  const path = __importStar(require("path"));
49
49
  const string_decoder_1 = require("string_decoder");
50
50
  const sessionContext_1 = require("../context/sessionContext");
51
+ const sessionStats_1 = require("../sessionStats");
51
52
  const jsonl_1 = require("../parsers/jsonl");
52
53
  const diagnostics_1 = require("./diagnostics");
53
54
  const sessionPathResolver_1 = require("../parsers/sessionPathResolver");
@@ -342,16 +343,13 @@ class ClaudeCodeProvider {
342
343
  const results = [];
343
344
  const seen = new Set();
344
345
  for (const folder of this.getAllProjectFolders()) {
345
- for (const sessionPath of this.findSessionsInDirectory(folder.dir)) {
346
- if (seen.has(sessionPath))
346
+ // The directory listing already stat'ed each file; reuse it instead of
347
+ // stat'ing every session a second time.
348
+ for (const file of (0, sessionPathResolver_1.findSessionFilesWithStats)(folder.dir)) {
349
+ if (seen.has(file.path))
347
350
  continue;
348
- seen.add(sessionPath);
349
- try {
350
- results.push({ path: sessionPath, mtime: fs.statSync(sessionPath).mtime });
351
- }
352
- catch {
353
- // Skip files that vanish between listing and stat.
354
- }
351
+ seen.add(file.path);
352
+ results.push({ path: file.path, mtime: file.mtime, sizeBytes: file.sizeBytes });
355
353
  }
356
354
  }
357
355
  this.recordHomeStatus('listAllSessionFiles');
@@ -535,93 +533,10 @@ class ClaudeCodeProvider {
535
533
  }
536
534
  // --- Stats ---
537
535
  readSessionStats(sessionPath) {
538
- const sessionId = path.basename(sessionPath, '.jsonl');
539
- let messageCount = 0;
540
- let startTime = '';
541
- let endTime = '';
542
- const tokens = { input: 0, output: 0, cacheWrite: 0, cacheRead: 0 };
543
- const modelUsage = {};
544
- const toolUsage = {};
545
- let compactionEstimate = 0;
546
- let truncationCount = 0;
547
- let reportedCost = 0;
548
- try {
549
- const content = fs.readFileSync(sessionPath, 'utf8');
550
- const lines = content.split('\n');
551
- for (const line of lines) {
552
- const trimmed = line.trim();
553
- if (!trimmed || !trimmed.startsWith('{'))
554
- continue;
555
- try {
556
- const event = JSON.parse(trimmed);
557
- if (!startTime && event.timestamp)
558
- startTime = event.timestamp;
559
- if (event.timestamp)
560
- endTime = event.timestamp;
561
- if (event.type === 'assistant' && event.message?.usage) {
562
- messageCount++;
563
- const u = event.message.usage;
564
- tokens.input += u.input_tokens || 0;
565
- tokens.output += u.output_tokens || 0;
566
- tokens.cacheWrite += u.cache_creation_input_tokens || 0;
567
- tokens.cacheRead += u.cache_read_input_tokens || 0;
568
- if (u.reported_cost)
569
- reportedCost += u.reported_cost;
570
- const model = event.message.model || 'unknown';
571
- if (!modelUsage[model])
572
- modelUsage[model] = { calls: 0, tokens: 0 };
573
- modelUsage[model].calls++;
574
- modelUsage[model].tokens += (u.input_tokens || 0) + (u.output_tokens || 0);
575
- // Check content for tool_use blocks
576
- if (Array.isArray(event.message.content)) {
577
- for (const block of event.message.content) {
578
- if (block.type === 'tool_use' && typeof block.name === 'string') {
579
- toolUsage[block.name] = (toolUsage[block.name] || 0) + 1;
580
- }
581
- }
582
- }
583
- }
584
- if (event.type === 'user')
585
- messageCount++;
586
- if (event.type === 'summary')
587
- compactionEstimate++;
588
- // Check for truncation in tool results
589
- if (event.type === 'user' && Array.isArray(event.message?.content)) {
590
- for (const block of event.message.content) {
591
- if (block.type === 'tool_result' && typeof block.content === 'string') {
592
- for (const pattern of jsonl_1.TRUNCATION_PATTERNS) {
593
- if (pattern.regex.test(block.content)) {
594
- truncationCount++;
595
- break;
596
- }
597
- }
598
- }
599
- }
600
- }
601
- }
602
- catch {
603
- // Skip malformed lines
604
- }
605
- }
606
- }
607
- catch {
608
- // Skip unreadable files
609
- }
610
- return {
611
- providerId: 'claude-code',
612
- sessionId,
613
- filePath: sessionPath,
614
- label: this.extractSessionLabel(sessionPath),
615
- startTime,
616
- endTime,
617
- messageCount,
618
- tokens,
619
- modelUsage,
620
- toolUsage,
621
- compactionEstimate,
622
- truncationCount,
623
- reportedCost,
624
- };
536
+ // One reader pass through the shared aggregator: cache-inclusive per-model
537
+ // totals, cost with provenance, a tool success/failure split, and the label
538
+ // from the first user prompt in the events already read.
539
+ return (0, sessionStats_1.readSessionFileStats)(this, sessionPath);
625
540
  }
626
541
  readSessionContextSnapshot(sessionPath, options = {}) {
627
542
  return (0, sessionContext_1.readSessionContextSnapshot)(this, sessionPath, options);
@@ -74,6 +74,8 @@ export declare class CodexProvider implements SessionProviderBase {
74
74
  isSessionFile(filename: string): boolean;
75
75
  getSessionId(sessionPath: string): string;
76
76
  encodeWorkspacePath(workspacePath: string): string;
77
+ /** Thread title or first user message from the SQLite index; null without it. */
78
+ private extractSessionLabelFromDb;
77
79
  extractSessionLabel(sessionPath: string): string | null;
78
80
  extractSessionLabelsAsync(sessionPaths: readonly string[]): Promise<Map<string, string | null>>;
79
81
  createReader(sessionPath: string): SessionReader;