sidekick-shared 0.18.4 → 0.19.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 (38) hide show
  1. package/README.md +23 -2
  2. package/dist/accounts.d.ts +1 -0
  3. package/dist/aggregation/EventAggregator.js +26 -11
  4. package/dist/browser.d.ts +2 -0
  5. package/dist/browser.js +5 -1
  6. package/dist/codexProfiles.d.ts +3 -2
  7. package/dist/codexProfiles.js +382 -52
  8. package/dist/context/sessionContext.d.ts +99 -0
  9. package/dist/context/sessionContext.js +523 -0
  10. package/dist/ensureDefaultAccounts.js +6 -0
  11. package/dist/index.d.ts +3 -1
  12. package/dist/index.js +9 -3
  13. package/dist/modelContext.js +3 -1
  14. package/dist/modelInfo.js +69 -9
  15. package/dist/parsers/codexParser.d.ts +2 -0
  16. package/dist/parsers/codexParser.js +129 -63
  17. package/dist/providers/claudeCode.d.ts +2 -0
  18. package/dist/providers/claudeCode.js +4 -0
  19. package/dist/providers/codex.d.ts +2 -0
  20. package/dist/providers/codex.js +125 -91
  21. package/dist/providers/openCode.d.ts +2 -0
  22. package/dist/providers/openCode.js +4 -0
  23. package/dist/providers/types.d.ts +4 -0
  24. package/dist/report/htmlReportGenerator.js +3 -0
  25. package/dist/report/index.d.ts +1 -1
  26. package/dist/report/index.js +2 -1
  27. package/dist/report/transcriptParser.d.ts +3 -0
  28. package/dist/report/transcriptParser.js +25 -3
  29. package/dist/report/types.d.ts +2 -0
  30. package/dist/schemas/sessionEvent.d.ts +15 -0
  31. package/dist/schemas/sessionEvent.js +14 -1
  32. package/dist/types/sessionEvent.d.ts +16 -1
  33. package/dist/watchers/eventBridge.js +24 -0
  34. package/dist/watchers/factory.d.ts +2 -2
  35. package/dist/watchers/factory.js +7 -5
  36. package/dist/watchers/providerReaderWatcher.d.ts +27 -0
  37. package/dist/watchers/providerReaderWatcher.js +148 -0
  38. package/package.json +1 -1
package/README.md CHANGED
@@ -26,17 +26,18 @@ npm install sidekick-shared
26
26
  | **Formatters** | Display helpers (`formatTokenCount()`, `formatDurationMs()`), tool summary, noise classification, session dump (text/markdown/JSON), event highlighting |
27
27
  | **Search** | Cross-session full-text search, advanced filtering (substring, fuzzy, regex, date) |
28
28
  | **Aggregation** | Event aggregation, frequency tracking, activity heatmaps, pattern extraction |
29
+ | **Session Context** | Provider-neutral context evidence snapshots (`buildSessionContextSnapshot()`, `calculateSessionContextPressure()`, `createSessionContextProjector()`, `readSessionContextSnapshot()`): layered evidence sources, low/medium/high context pressure, and observed capabilities (tools, MCP servers, permission mode, rate limits) |
29
30
  | **Report** | Self-contained HTML session report generation |
30
31
  | **Credentials** | Claude Max OAuth credential reading from `~/.claude/.credentials.json` |
31
32
  | **Quota** | Claude Max subscription quota fetching (5-hour and 7-day windows) and Codex rate-limit extraction from event streams |
32
33
  | **Provider Status** | API health checking via status.claude.com and status.openai.com (indicator, components, incidents) |
33
34
  | **Schemas** | Zod schemas for runtime JSONL event validation (`sessionEventSchema`, `messageUsageSchema`, `sessionMessageSchema`) |
34
35
  | **Extractors** | Pure functions for single-event processing: `extractTokenUsage()`, `extractToolCall()` (top-level `tool_use`), `extractToolCalls()` (assistant content blocks) |
35
- | **Model Info & Pricing** | Model family parsing (Anthropic / OpenAI / Google, including legacy `claude-3-opus-…` and `claude-3-5-sonnet-…` IDs), context-window lookup (including Opus 4.7 / Sonnet 4.7 1M and GPT-5.x variants), pricing tables with optional LiteLLM hydration, null-aware cost (`calculateCost()`), provenance-preserving cost (`calculateCostWithProvenance()`, `mergeCostSources()`), and display helpers (`shortModelName()`, `getModelDisplayInfo()`, `compareModelIds()`, `sortModelIds()`, `formatCost()`) |
36
+ | **Model Info & Pricing** | Model family parsing (Anthropic / OpenAI / Google, including legacy `claude-3-opus-…` and `claude-3-5-sonnet-…` IDs), context-window lookup (including Fable 5 / Opus 4.8 / Opus 4.7 / Sonnet 4.7 1M and GPT-5.x variants), pricing tables with optional LiteLLM hydration, null-aware cost (`calculateCost()`), provenance-preserving cost (`calculateCostWithProvenance()`, `mergeCostSources()`), and display helpers (`shortModelName()`, `getModelDisplayInfo()`, `compareModelIds()`, `sortModelIds()`, `formatCost()`) |
36
37
  | **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
37
38
  | **Multi-Provider Quota** | `MultiProviderQuotaService` orchestrates Claude polling + peak-hours + account labels + Codex quota updates behind one typed `{ claude?, codex? }` event stream. `CodexQuotaWatcher` watches the active Codex rollout for live rate limits with snapshot fallback |
38
39
  | **Accounts** | Multi-provider account registry (v2) with per-provider active account, save/switch/remove, v1 migration, `ensureDefaultAccounts()` for first-run bootstrap of the active system Claude/Codex credentials as a "Default" saved account, and `getActiveAccountStatus()` for a single-pass active-account read across providers |
39
- | **Codex Profiles** | Codex account lifecycle — prepare, finalize, switch, remove — with isolated `CODEX_HOME` directories and multi-home monitoring support |
40
+ | **Codex Profiles** | Codex account lifecycle — prepare, finalize, switch, remove — switching atomically swaps the profile's backed-up credentials into the system `~/.codex/auth.json`, with rotated-token staleness protection, one-time dual-home migration, and legacy multi-home session monitoring |
40
41
  | **Quota Snapshots** | Persistent quota caching per provider/account for offline fallback |
41
42
  | **Phrases** | Curated humorous phrases for loading/idle states, available as a flat `ALL_PHRASES` array or grouped via `PHRASE_CATEGORIES` for category-aware UI |
42
43
 
@@ -180,6 +181,26 @@ const tools = extractToolCalls(event); // ToolCall[] — assistant
180
181
  const toolFromEvent = extractToolCall(event); // ToolCall | null — top-level `tool_use` events
181
182
  ```
182
183
 
184
+ ### Project session context evidence
185
+
186
+ Build a provider-neutral snapshot of what an assistant has "seen" in a session — layered evidence sources, context pressure, and observed capabilities. Read it through any session provider, or build it directly from a canonical `SessionEvent[]`.
187
+
188
+ ```typescript
189
+ import { detectProvider, readSessionContextSnapshot } from 'sidekick-shared';
190
+
191
+ const provider = await detectProvider('/path/to/project');
192
+ if (provider) {
193
+ const snapshot = readSessionContextSnapshot(provider, '/path/to/session.jsonl');
194
+
195
+ console.log(snapshot.pressure); // 'low' | 'medium' | 'high'
196
+ console.log(snapshot.contextTokens, '/', snapshot.contextWindow);
197
+ console.log(snapshot.capabilities.tools, snapshot.capabilities.mcpServers);
198
+ console.log(snapshot.sources.length, 'evidence sources');
199
+ }
200
+ ```
201
+
202
+ Use `createSessionContextProjector()` for incremental updates as new events stream in, or `calculateSessionContextPressure(contextTokens, contextWindow)` for the pressure band alone.
203
+
183
204
  ### Format shared dashboard values
184
205
 
185
206
  ```typescript
@@ -22,6 +22,7 @@ export interface ActiveAccountInfo {
22
22
  export interface AccountManagerResult {
23
23
  success: boolean;
24
24
  error?: string;
25
+ warning?: string;
25
26
  needsLogin?: boolean;
26
27
  profileId?: string;
27
28
  codexHome?: string;
@@ -157,25 +157,29 @@ class EventAggregator {
157
157
  if (event.message.model) {
158
158
  this.currentModel = event.message.model;
159
159
  }
160
- // 3. Skip synthetic token-count events for messageCount
160
+ // 3. Skip system/audit events and synthetic token-count events for messageCount
161
161
  const msgId = event.message.id ?? '';
162
- if (!msgId.startsWith('token-count-')) {
162
+ if (event.type !== 'system' && !msgId.startsWith('token-count-')) {
163
163
  this.messageCount++;
164
164
  }
165
165
  // 4. Latency tracking
166
- this.processLatency(event);
166
+ if (event.type !== 'system') {
167
+ this.processLatency(event);
168
+ }
167
169
  // 5. Token accumulation
168
170
  if (event.message.usage) {
169
171
  this.accumulateUsage(event.message.usage, event.timestamp, event.message.model);
170
172
  }
171
- // 6. Tool extraction from content blocks
172
- this.extractToolsFromContent(event);
173
- // 7. Task state
174
- this.extractTaskStateFromEvent(event);
175
- // 8. Subagent tracking
176
- this.extractSubagentFromEvent(event);
177
- // 9. Plan extraction — convert SessionEvent to a FollowEvent shape for PlanExtractor
178
- this.extractPlanFromSessionEvent(event);
173
+ if (event.type !== 'system') {
174
+ // 6. Tool extraction from content blocks
175
+ this.extractToolsFromContent(event);
176
+ // 7. Task state
177
+ this.extractTaskStateFromEvent(event);
178
+ // 8. Subagent tracking
179
+ this.extractSubagentFromEvent(event);
180
+ // 9. Plan extraction — convert SessionEvent to a FollowEvent shape for PlanExtractor
181
+ this.extractPlanFromSessionEvent(event);
182
+ }
179
183
  // 10. Context attribution
180
184
  this.attributeContextFromEvent(event);
181
185
  // 11. Permission mode tracking
@@ -1447,6 +1451,12 @@ class EventAggregator {
1447
1451
  this.contextAttribution.other += this.estimateTokens(text);
1448
1452
  }
1449
1453
  }
1454
+ else if (event.type === 'system') {
1455
+ const text = this.extractTextContent(event) || '';
1456
+ if (text) {
1457
+ this.contextAttribution.systemPrompt += this.estimateTokens(text);
1458
+ }
1459
+ }
1450
1460
  }
1451
1461
  // ═══════════════════════════════════════════════════════════════════════
1452
1462
  // Private: Context Attribution from FollowEvent
@@ -1591,6 +1601,11 @@ class EventAggregator {
1591
1601
  description = 'Context compacted';
1592
1602
  noiseLevel = 'system';
1593
1603
  break;
1604
+ case 'system':
1605
+ tlType = 'session_start';
1606
+ description = this.extractTextContent(event) ?? event.message.sourceLabel ?? 'System event';
1607
+ noiseLevel = 'system';
1608
+ break;
1594
1609
  default:
1595
1610
  tlType = 'session_start';
1596
1611
  description = 'Event';
package/dist/browser.d.ts CHANGED
@@ -10,5 +10,7 @@
10
10
  export { getModelContextWindowSize, DEFAULT_CONTEXT_WINDOW } from './modelContext';
11
11
  export { formatDurationMs, formatTokenCount } from './formatting';
12
12
  export type { FormatDurationMsOptions, FormatTokenCountOptions } from './formatting';
13
+ export { buildSessionContextSnapshot, calculateSessionContextPressure, createSessionContextProjector, } from './context/sessionContext';
14
+ export type { BuildSessionContextSnapshotOptions, SessionContextCapabilities, SessionContextLayerBreakdown, SessionContextPressure, SessionContextProjector, SessionContextSnapshot, SessionContextSource, SessionContextSourceType, } from './context/sessionContext';
13
15
  export { parseModelId, getModelPricing, getModelInfo, calculateCost, calculateCostWithPricing, calculateCostWithProvenance, mergeCostSources, shortModelName, getModelDisplayInfo, compareModelIds, sortModelIds, formatCost, } from './modelInfo';
14
16
  export type { ModelPricing, CostTokenUsage, CostSource, CostProvenanceInput, CostWithProvenance, ModelProvider, ParsedModelId, ModelInfo, ModelDisplayInfo, } from './modelInfo';
package/dist/browser.js CHANGED
@@ -9,13 +9,17 @@
9
9
  * For pricing hydration (LiteLLM catalog refresh), use `sidekick-shared/node`.
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.formatTokenCount = exports.formatDurationMs = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = void 0;
12
+ exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = exports.formatTokenCount = exports.formatDurationMs = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = void 0;
13
13
  var modelContext_1 = require("./modelContext");
14
14
  Object.defineProperty(exports, "getModelContextWindowSize", { enumerable: true, get: function () { return modelContext_1.getModelContextWindowSize; } });
15
15
  Object.defineProperty(exports, "DEFAULT_CONTEXT_WINDOW", { enumerable: true, get: function () { return modelContext_1.DEFAULT_CONTEXT_WINDOW; } });
16
16
  var formatting_1 = require("./formatting");
17
17
  Object.defineProperty(exports, "formatDurationMs", { enumerable: true, get: function () { return formatting_1.formatDurationMs; } });
18
18
  Object.defineProperty(exports, "formatTokenCount", { enumerable: true, get: function () { return formatting_1.formatTokenCount; } });
19
+ var sessionContext_1 = require("./context/sessionContext");
20
+ Object.defineProperty(exports, "buildSessionContextSnapshot", { enumerable: true, get: function () { return sessionContext_1.buildSessionContextSnapshot; } });
21
+ Object.defineProperty(exports, "calculateSessionContextPressure", { enumerable: true, get: function () { return sessionContext_1.calculateSessionContextPressure; } });
22
+ Object.defineProperty(exports, "createSessionContextProjector", { enumerable: true, get: function () { return sessionContext_1.createSessionContextProjector; } });
19
23
  var modelInfo_1 = require("./modelInfo");
20
24
  Object.defineProperty(exports, "parseModelId", { enumerable: true, get: function () { return modelInfo_1.parseModelId; } });
21
25
  Object.defineProperty(exports, "getModelPricing", { enumerable: true, get: function () { return modelInfo_1.getModelPricing; } });
@@ -14,6 +14,7 @@ export declare function getActiveCodexAccount(): SavedAccountProfile | null;
14
14
  export declare function resolveSidekickCodexHome(): string;
15
15
  export declare function getCodexExecutionEnv(baseEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
16
16
  export declare function prepareCodexAccount(label: string): CodexAccountManagerResult;
17
- export declare function finalizeCodexAccount(profileId: string): AccountManagerResult;
18
- export declare function switchToCodexAccount(profileId: string): AccountManagerResult;
17
+ export declare function finalizeCodexAccount(profileId: string): CodexAccountManagerResult;
18
+ export declare function switchToCodexAccount(profileId: string): CodexAccountManagerResult;
19
+ export declare function reconcileCodexAuthState(): void;
19
20
  export declare function removeCodexAccount(profileId: string): AccountManagerResult;