sidekick-shared 0.27.3 → 0.27.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to sidekick-shared will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.27.4] - 2026-09-23
9
+
10
+ ### Added
11
+
12
+ - `sidekick-shared/node` exports `createSessionProviders()`, the `CreateSessionProvidersOptions` / `CreateSessionProvidersResult` types, `SYNTHETIC_MODEL`, and the `ProviderId` / `SessionProviderBase` / `SessionProviderDiagnostic` types, re-exported from their own modules. `collectUsageEvents()` callers no longer need the package root, a CommonJS barrel that bundlers cannot tree-shake (about 420 KB minified saved in one esbuild consumer). Root exports are unchanged.
13
+ - `CollectUsageEventsOptions.signal` (optional `AbortSignal`): `collectUsageEvents()` checks it before listing sessions and between sessions, and rejects with the signal's reason (`AbortError` by default). Only fully read sessions are cached. Without a signal nothing changes; counting, pricing, and the cache format (`USAGE_CACHE_VERSION` 2) are the same.
14
+ - A packaging test walks every relative `require` reachable from `dist/node.js` and fails if it reaches `dist/index.js`.
15
+
8
16
  ## [0.27.3] - 2026-09-23
9
17
 
10
18
  ### Fixed
package/README.md CHANGED
@@ -9,6 +9,7 @@ Types, parsers, providers, readers, formatters, aggregation, search, reporting,
9
9
 
10
10
  ## Recent additions
11
11
 
12
+ - **0.27.4: usage events from the Node entry** — `sidekick-shared/node` now exports `createSessionProviders()`, `SYNTHETIC_MODEL`, and the `ProviderId` / `SessionProviderBase` / `SessionProviderDiagnostic` / `CreateSessionProviders*` types, so `collectUsageEvents()` callers never import the package root (a CommonJS barrel that bundlers cannot tree-shake). `collectUsageEvents()` accepts an optional `signal` that stops the collection before listing and between sessions.
12
13
  - **0.27.3: honest session totals** — Claude Code split lines are counted once: `ClaudeUsageDeduper` keeps a `message.id`'s first usage, strips identical repeats, and turns streamed growth into a `usageKind: 'correction'` delta that `EventAggregator` adds to tokens and cost without counting another call. `resolveCodexCallUsage()` drops duplicate Codex `token_count` events. `collectSessionTokenTotals()` (root and Node) returns `mainThread`, per-`subagents`, `subagentTotal`, and `combined` in the `summarizeTokens()` vocabulary, and `formatTokenBreakdown()` renders one line whose parts add up to the headline. `TokenSummary` gains `reasoning` / `billedOutsideBuckets`, and `SubagentStats` from every provider now carries cache, reasoning, and total tokens. The session dumps and `HtmlReportOptions` accept `subagents`, `readSessionReportInputs()` returns them, `collectUsageEvents()` reads Claude Code subagent transcripts, and `USAGE_CACHE_VERSION` 2 re-reads caches built with the old counting.
13
14
  - **0.27.2: streamed prompt history** — `collectPromptHistory()` reads session logs of any size in bounded, resumable passes instead of skipping files over `maxFileBytes`: every call keeps what it read, the cursor (`version: 3`) continues where each file stopped, and a pending prompt keeps its answer state so a far-away answer still settles it. Adds `maxRecordBytes` and `maxEntries` bounds, `hasMore`, permanently skipped records in `exclusions`, and rewrite detection.
14
15
  - **0.27.1: late prompt metadata** — `collectPromptHistory()` returns a prompt again, with the same provider, sessionId, ordinal, timestamp, and text, when its first answering call is written after an earlier collection, and entries gain `metadataStatus` (`pending` / `provisional` / `final`). The cursor becomes `version: 2`, keeps a replay offset and metadata digest for the one open prompt per file (no prompt text), and still accepts 0.27.0 cursors.
@@ -130,17 +131,17 @@ returns the platform default that both overrides bypass.
130
131
 
131
132
  `sidekick-shared` ships three public entry points plus a few convenience subpaths. Pick the one that matches your runtime. As of 0.25.0 every documented subpath declares explicit `types` / `import` / `require` / `default` export conditions and resolves under Vite and TypeScript's legacy `moduleResolution: node` without consumer-side aliases.
132
133
 
133
- | Path | Runtime | What it exposes |
134
- | ------------------------------ | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
- | `sidekick-shared` | Node (CLI, extension host) | Full public API (readers, providers, parsers, pricing, …). |
136
- | `sidekick-shared/browser` | **Browser / webview** | Pure helpers: usage normalization/pricing, token estimation, transcript and assistant-turn projection, model/context math, `parseMcpToolName()`. |
137
- | `sidekick-shared/node` | Node only | LiteLLM pricing and context-window hydration, public provider service status (`fetchProviderServiceStatus()`), observed context-window persistence, recent-session/transcript reads, human prompt history (`collectPromptHistory()`), session previews (`listSessionPreviews()` / `readSessionPreview()` plus the bounded-concurrency `listSessionPreviewsAsync()` / `readSessionPreviewAsync()`), Codex history/rollout lookup (`readCodexHistory()`, `findCodexRolloutFile()`), and resilient observed-session collection (`fs` + providers). |
138
- | `sidekick-shared/phrases` | Any runtime | Phrase arrays + `getRandomPhrase()`. |
139
- | `sidekick-shared/modelContext` | Any runtime | Direct access to the context-window module. |
140
- | `sidekick-shared/modelInfo` | Any runtime | Direct access to model parsing and cost math. |
141
- | `sidekick-shared/formatting` | Any runtime | Direct access to pure token and duration display helpers. |
142
- | `sidekick-shared/schemas` | Any runtime | Pure Zod boundary schemas (session events, assistant turns, observed-session V1, quota, account status, account management, quota history, the `state.json` contract) — fs-free, no Node builtins. |
143
- | `sidekick-shared/statusline` | Node only | Cache-only statusline surface: account status + quota snapshot readers and writers, `formatStatusline()`, burn-rate estimates, quota freshness helpers, `parseClaudeStatuslinePayload()` / `quotaFromStatuslinePayload()`, and `readStateFile()` / `writeStateFile()`. |
134
+ | Path | Runtime | What it exposes |
135
+ | ------------------------------ | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
136
+ | `sidekick-shared` | Node (CLI, extension host) | Full public API (readers, providers, parsers, pricing, …). |
137
+ | `sidekick-shared/browser` | **Browser / webview** | Pure helpers: usage normalization/pricing, token estimation, transcript and assistant-turn projection, model/context math, `parseMcpToolName()`. |
138
+ | `sidekick-shared/node` | Node only | LiteLLM pricing and context-window hydration, public provider service status (`fetchProviderServiceStatus()`), observed context-window persistence, recent-session/transcript reads, human prompt history (`collectPromptHistory()`), usage events from session logs (`collectUsageEvents()` with `createSessionProviders()` and `SYNTHETIC_MODEL`), session previews (`listSessionPreviews()` / `readSessionPreview()` plus the bounded-concurrency `listSessionPreviewsAsync()` / `readSessionPreviewAsync()`), Codex history/rollout lookup (`readCodexHistory()`, `findCodexRolloutFile()`), and resilient observed-session collection (`fs` + providers). |
139
+ | `sidekick-shared/phrases` | Any runtime | Phrase arrays + `getRandomPhrase()`. |
140
+ | `sidekick-shared/modelContext` | Any runtime | Direct access to the context-window module. |
141
+ | `sidekick-shared/modelInfo` | Any runtime | Direct access to model parsing and cost math. |
142
+ | `sidekick-shared/formatting` | Any runtime | Direct access to pure token and duration display helpers. |
143
+ | `sidekick-shared/schemas` | Any runtime | Pure Zod boundary schemas (session events, assistant turns, observed-session V1, quota, account status, account management, quota history, the `state.json` contract) — fs-free, no Node builtins. |
144
+ | `sidekick-shared/statusline` | Node only | Cache-only statusline surface: account status + quota snapshot readers and writers, `formatStatusline()`, burn-rate estimates, quota freshness helpers, `parseClaudeStatuslinePayload()` / `quotaFromStatuslinePayload()`, and `readStateFile()` / `writeStateFile()`. |
144
145
 
145
146
  ### Browser / webview runtimes
146
147
 
@@ -481,6 +482,27 @@ const { providers, diagnostics } = createSessionProviders({
481
482
  Repeated identical degradations are coalesced, so the diagnostic stream stays
482
483
  bounded across long-lived polling and subscription cycles.
483
484
 
485
+ ### Collect usage events from the Node entry
486
+
487
+ Everything `collectUsageEvents()` needs is exported from `sidekick-shared/node`,
488
+ which never loads the package root, so bundlers keep only what you use:
489
+
490
+ ```typescript
491
+ import { collectUsageEvents, createSessionProviders, SYNTHETIC_MODEL } from 'sidekick-shared/node';
492
+
493
+ const { providers } = createSessionProviders({ providerIds: ['claude-code', 'codex'] });
494
+ const { events } = await collectUsageEvents({
495
+ providers,
496
+ since: '2026-09-01',
497
+ workspacePath: '/path/to/repo',
498
+ signal: AbortSignal.timeout(30_000), // optional: stop before listing and between sessions
499
+ });
500
+ const priced = events.filter((event) => event.model !== SYNTHETIC_MODEL);
501
+ ```
502
+
503
+ On abort the promise rejects with the signal's reason (an `AbortError` by
504
+ default); sessions read before the abort keep their cache entries.
505
+
484
506
  ### Detect the active session provider
485
507
 
486
508
  For a single-provider surface, auto-detect and construct just one:
package/dist/node.d.ts CHANGED
@@ -46,6 +46,9 @@ export { ObservedSessionCollector, observedSessionSourceFromProvider, fileFinger
46
46
  export type { KnownObservedSessionFingerprint, ObservedSessionChange, ObservedSessionChangeBatch, ObservedSessionChangeType, ObservedSessionReference, ObservedSessionFingerprintParts, ObservedSessionSourceSubscribeOptions, ObservedSessionSubscribeOptions, ObservedSessionCollectionSource, ProviderObservedSessionCollectionSource, ObservedSessionDiagnosticKind, ObservedSessionDiagnosticPhase, ObservedSessionDiagnosticSeverity, ObservedSessionDiagnostic, ObservedSessionCollection, ObservedSessionCollectorOptions, ObservedSessionCollectOptions, ObservedSessionDiscoverOptions, ObservedSessionDiscoveryStats, ObservedSessionDiscoveryTrigger, ObservedSessionSourceSignal, ObservedSessionSourceListener, ObservedSessionResolution, } from './observedSessionCollector';
47
47
  export { DirectoryListingCache, splitWatchedPath } from './providers/directoryListingCache';
48
48
  export type { CachedDirectoryListing, CachedFileStat, DirectoryListingCacheOptions, DirectoryListingCounters, ListDirectoryOptions, } from './providers/directoryListingCache';
49
- export type { ListSessionFilesOptions, SessionEnumerationCounters, WatchedSessionFile, } from './providers/types';
49
+ export type { ListSessionFilesOptions, ProviderId, SessionEnumerationCounters, SessionProviderBase, SessionProviderDiagnostic, WatchedSessionFile, } from './providers/types';
50
+ export { createSessionProviders } from './providers/factory';
51
+ export type { CreateSessionProvidersOptions, CreateSessionProvidersResult, } from './providers/factory';
52
+ export { SYNTHETIC_MODEL } from './usage/claudeUsageDedupe';
50
53
  export type { ProviderServiceSeverity, ProviderServiceComponent, ProviderServiceIncident, ProviderServiceStatusFailureReason, ObservedProviderServiceStatus, UnavailableProviderServiceStatus, ProviderServiceStatus, FetchProviderServiceStatusOptions, } from './providerServiceStatusTypes';
51
54
  export { fetchProviderServiceStatus } from './providerServiceStatus';
package/dist/node.js CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.summarizeUsageRows = exports.bucketUsage = exports.importSessionHistory = exports.ACTIVE_SESSION_MTIME_THRESHOLD_MS = exports.sessionSummaryFromStats = exports.removeSessionSummary = exports.markFileImported = exports.isFileImported = exports.applySessionSummary = exports.subagentTokenTotals = exports.scanSessionSubagents = exports.readMainThreadTokenTotals = exports.combineSessionTokenTotals = exports.collectSessionTokenTotals = 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.isHumanPrompt = exports.humanPromptText = exports.HUMAN_CODEX_SOURCES = exports.HUMAN_CLAUDE_ENTRYPOINTS = exports.collectPromptHistory = exports.readSessionTranscript = exports.listRecentSessions = exports.getObservedContextWindowPath = exports.recordObservedContextWindow = exports.loadObservedContextWindows = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmContextWindows = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = void 0;
11
- exports.fetchProviderServiceStatus = exports.splitWatchedPath = exports.DirectoryListingCache = exports.fileFingerprintParts = exports.fileFingerprint = exports.observedSessionSourceFromProvider = exports.ObservedSessionCollector = exports.readSessionPreviewAsync = exports.readSessionPreview = exports.listSessionPreviewsAsync = exports.listSessionPreviews = exports.readCodexHistory = exports.walkRolloutFilesAsync = exports.walkRolloutFiles = exports.findCodexRolloutFile = exports.weekKey = exports.usageBucketKey = void 0;
11
+ exports.fetchProviderServiceStatus = exports.SYNTHETIC_MODEL = exports.createSessionProviders = exports.splitWatchedPath = exports.DirectoryListingCache = exports.fileFingerprintParts = exports.fileFingerprint = exports.observedSessionSourceFromProvider = exports.ObservedSessionCollector = exports.readSessionPreviewAsync = exports.readSessionPreview = exports.listSessionPreviewsAsync = exports.listSessionPreviews = exports.readCodexHistory = exports.walkRolloutFilesAsync = exports.walkRolloutFiles = exports.findCodexRolloutFile = exports.weekKey = exports.usageBucketKey = void 0;
12
12
  var pricingCatalog_1 = require("./pricingCatalog");
13
13
  Object.defineProperty(exports, "hydratePricingCatalog", { enumerable: true, get: function () { return pricingCatalog_1.hydratePricingCatalog; } });
14
14
  Object.defineProperty(exports, "normalizeLiteLlmCatalog", { enumerable: true, get: function () { return pricingCatalog_1.normalizeLiteLlmCatalog; } });
@@ -96,5 +96,9 @@ Object.defineProperty(exports, "fileFingerprintParts", { enumerable: true, get:
96
96
  var directoryListingCache_1 = require("./providers/directoryListingCache");
97
97
  Object.defineProperty(exports, "DirectoryListingCache", { enumerable: true, get: function () { return directoryListingCache_1.DirectoryListingCache; } });
98
98
  Object.defineProperty(exports, "splitWatchedPath", { enumerable: true, get: function () { return directoryListingCache_1.splitWatchedPath; } });
99
+ var factory_1 = require("./providers/factory");
100
+ Object.defineProperty(exports, "createSessionProviders", { enumerable: true, get: function () { return factory_1.createSessionProviders; } });
101
+ var claudeUsageDedupe_1 = require("./usage/claudeUsageDedupe");
102
+ Object.defineProperty(exports, "SYNTHETIC_MODEL", { enumerable: true, get: function () { return claudeUsageDedupe_1.SYNTHETIC_MODEL; } });
99
103
  var providerServiceStatus_1 = require("./providerServiceStatus");
100
104
  Object.defineProperty(exports, "fetchProviderServiceStatus", { enumerable: true, get: function () { return providerServiceStatus_1.fetchProviderServiceStatus; } });
@@ -78,6 +78,13 @@ export interface CollectUsageEventsOptions {
78
78
  /** Skip the on-disk cache entirely. */
79
79
  noCache?: boolean;
80
80
  onDiagnostic?: (diagnostic: SessionProviderDiagnostic) => void;
81
+ /**
82
+ * Stops the collection before listing and between sessions; the promise
83
+ * rejects with the signal's reason (an `AbortError` by default). Sessions
84
+ * read before the abort keep their cache entries; no partial session is
85
+ * cached.
86
+ */
87
+ signal?: AbortSignal;
81
88
  }
82
89
  export interface CollectUsageEventsResult {
83
90
  /** Events within the window, oldest first. */
@@ -290,12 +290,15 @@ async function collectUsageEvents(options) {
290
290
  const untilMs = toMs(options.until, 'until');
291
291
  const cacheDir = options.cacheDir ?? getUsageCacheDir();
292
292
  const useCache = !options.noCache;
293
+ const signal = options.signal;
294
+ signal?.throwIfAborted();
293
295
  const listed = await (0, sessionPreviews_1.listSessionPreviewsAsync)(options.providers, {
294
296
  since: options.since,
295
297
  workspacePath: options.workspacePath,
296
298
  limit: Number.MAX_SAFE_INTEGER,
297
299
  concurrency: options.concurrency,
298
300
  });
301
+ signal?.throwIfAborted();
299
302
  const diagnostics = [...listed.diagnostics];
300
303
  for (const diagnostic of listed.diagnostics)
301
304
  options.onDiagnostic?.(diagnostic);
@@ -306,6 +309,7 @@ async function collectUsageEvents(options) {
306
309
  let cacheMisses = 0;
307
310
  let wroteCache = false;
308
311
  for (const preview of listed.previews) {
312
+ signal?.throwIfAborted();
309
313
  const provider = providerById.get(preview.provider);
310
314
  if (!provider)
311
315
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-shared",
3
- "version": "0.27.3",
3
+ "version": "0.27.4",
4
4
  "description": "Shared data access layer for Sidekick — readers, types, providers, credentials, quota",
5
5
  "keywords": [
6
6
  "claude-code",