sidekick-shared 0.22.0 → 0.23.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 (120) hide show
  1. package/README.md +75 -63
  2. package/dist/accountManager.js +10 -0
  3. package/dist/accountRegistry.d.ts +2 -0
  4. package/dist/accountStatus.d.ts +1 -0
  5. package/dist/accountStatus.js +6 -0
  6. package/dist/accounts.js +2 -0
  7. package/dist/aggregation/EventAggregator.d.ts +36 -2
  8. package/dist/aggregation/EventAggregator.js +259 -33
  9. package/dist/aggregation/snapshot.d.ts +1 -1
  10. package/dist/aggregation/snapshot.js +2 -1
  11. package/dist/aggregation/types.d.ts +25 -1
  12. package/dist/analytics/codeImpact.d.ts +20 -0
  13. package/dist/analytics/codeImpact.js +21 -0
  14. package/dist/analytics/compactionLedger.d.ts +11 -0
  15. package/dist/analytics/compactionLedger.js +31 -0
  16. package/dist/analytics/qualityScore.d.ts +26 -0
  17. package/dist/analytics/qualityScore.js +115 -0
  18. package/dist/autoSwitch.d.ts +1 -1
  19. package/dist/autoSwitch.js +14 -3
  20. package/dist/codexProfiles.js +2 -0
  21. package/dist/codexQuota.d.ts +2 -0
  22. package/dist/codexQuota.js +27 -0
  23. package/dist/codexQuotaWatcher.d.ts +7 -0
  24. package/dist/codexQuotaWatcher.js +37 -19
  25. package/dist/context/composer.d.ts +3 -2
  26. package/dist/context/composer.js +5 -5
  27. package/dist/context/sessionContext.js +35 -9
  28. package/dist/credentialIO.js +19 -5
  29. package/dist/doctor.d.ts +55 -0
  30. package/dist/doctor.js +244 -0
  31. package/dist/errorHistory.d.ts +33 -0
  32. package/dist/errorHistory.js +95 -0
  33. package/dist/extractors/errorTaxonomy.d.ts +6 -0
  34. package/dist/extractors/errorTaxonomy.js +47 -0
  35. package/dist/extractors/toolCall.d.ts +9 -0
  36. package/dist/extractors/toolCall.js +79 -0
  37. package/dist/handoffUrl.d.ts +7 -0
  38. package/dist/handoffUrl.js +26 -0
  39. package/dist/index.d.ts +32 -9
  40. package/dist/index.js +64 -24
  41. package/dist/modelInfo.js +6 -0
  42. package/dist/multiProviderQuotaService.js +13 -2
  43. package/dist/parsers/codexParser.js +17 -1
  44. package/dist/parsers/openCodeParser.js +13 -0
  45. package/dist/parsers/planExtractor.js +17 -6
  46. package/dist/parsers/sessionActivityDetector.js +2 -0
  47. package/dist/parsers/sessionPathResolver.js +19 -16
  48. package/dist/parsers/subagentScanner.d.ts +1 -1
  49. package/dist/parsers/subagentScanner.js +4 -5
  50. package/dist/parsers/subagentTraceParser.js +4 -7
  51. package/dist/paths.d.ts +10 -0
  52. package/dist/paths.js +18 -5
  53. package/dist/projectMigration.d.ts +23 -0
  54. package/dist/projectMigration.js +95 -0
  55. package/dist/providerStatus.js +13 -2
  56. package/dist/providers/claudeCode.js +18 -4
  57. package/dist/providers/codex.js +42 -13
  58. package/dist/providers/codexDatabase.d.ts +1 -11
  59. package/dist/providers/detect.js +32 -3
  60. package/dist/providers/openCodeDatabase.d.ts +1 -31
  61. package/dist/providers/openCodeDatabase.js +21 -11
  62. package/dist/providers/types.d.ts +1 -0
  63. package/dist/quota.js +8 -0
  64. package/dist/quotaPoller.js +17 -6
  65. package/dist/quotaSnapshots.js +80 -26
  66. package/dist/readers/decisions.d.ts +2 -1
  67. package/dist/readers/decisions.js +5 -5
  68. package/dist/readers/handoff.d.ts +2 -1
  69. package/dist/readers/handoff.js +13 -8
  70. package/dist/readers/helpers.d.ts +3 -0
  71. package/dist/readers/helpers.js +12 -0
  72. package/dist/readers/notes.d.ts +2 -1
  73. package/dist/readers/notes.js +11 -8
  74. package/dist/readers/plans.js +3 -5
  75. package/dist/readers/tasks.d.ts +2 -1
  76. package/dist/readers/tasks.js +5 -5
  77. package/dist/report/htmlHelpers.js +6 -1
  78. package/dist/report/openBrowser.js +3 -3
  79. package/dist/schemas/index.d.ts +2 -0
  80. package/dist/schemas/index.js +10 -1
  81. package/dist/schemas/observedSessionV1.d.ts +499 -0
  82. package/dist/schemas/observedSessionV1.js +69 -0
  83. package/dist/schemas/sessionEvent.d.ts +13 -0
  84. package/dist/schemas/sessionEvent.js +19 -0
  85. package/dist/search/sessionSearch.d.ts +2 -2
  86. package/dist/search/sessionSearch.js +2 -3
  87. package/dist/sessionMonitor.d.ts +29 -0
  88. package/dist/sessionMonitor.js +71 -0
  89. package/dist/statusline/BurnRateCalculator.d.ts +14 -0
  90. package/dist/statusline/BurnRateCalculator.js +45 -0
  91. package/dist/statusline/formatter.d.ts +16 -0
  92. package/dist/statusline/formatter.js +84 -0
  93. package/dist/statusline/index.d.ts +7 -0
  94. package/dist/statusline/index.js +13 -0
  95. package/dist/terminalSync.js +16 -7
  96. package/dist/types/codex.d.ts +2 -0
  97. package/dist/types/historicalData.d.ts +49 -1
  98. package/dist/types/historicalData.js +28 -2
  99. package/dist/types/observedSessionV1.d.ts +70 -0
  100. package/dist/types/observedSessionV1.js +186 -0
  101. package/dist/types/opencode.d.ts +1 -0
  102. package/dist/types/sessionEvent.d.ts +19 -1
  103. package/dist/watchers/eventBridge.js +1 -0
  104. package/dist/watchers/factory.js +14 -3
  105. package/dist/watchers/jsonlTail.js +34 -14
  106. package/dist/watchers/jsonlWatcher.js +5 -3
  107. package/dist/watchers/providerReaderWatcher.js +4 -2
  108. package/dist/watchers/sqliteWatcher.d.ts +2 -0
  109. package/dist/watchers/sqliteWatcher.js +27 -14
  110. package/dist/watchers/types.d.ts +5 -0
  111. package/dist/writers/atomic.d.ts +4 -0
  112. package/dist/writers/atomic.js +200 -0
  113. package/dist/writers/decisions.d.ts +12 -0
  114. package/dist/writers/decisions.js +88 -0
  115. package/dist/writers/notes.d.ts +8 -0
  116. package/dist/writers/notes.js +82 -0
  117. package/dist/writers/tasks.d.ts +6 -0
  118. package/dist/writers/tasks.js +100 -0
  119. package/dist/zaiQuotaWatcher.js +6 -0
  120. package/package.json +5 -1
package/README.md CHANGED
@@ -9,12 +9,13 @@ Types, parsers, providers, readers, formatters, aggregation, search, reporting,
9
9
 
10
10
  ## Recent additions
11
11
 
12
+ - **Observed-session V1 contracts** — versioned provider-neutral `ObservedAgentSessionV1` / `ProviderCapabilitiesV1` / `PendingUserRequestV1` shapes with `createProviderSessionAdapterV1()` adapters and matching Zod schemas in [`sidekick-shared/schemas`](#supported-import-paths).
13
+ - **Atomic writers** — `addTask()`, `completeTask()`, `addDecision()`, and `addNote()` merge into the shared task/decision/note stores atomically, so concurrent CLI and extension captures are never lost.
14
+ - **Doctor report** — `runDoctor()` builds a typed cross-provider health report (project identity, sessions, accounts, providers, dependencies); `formatHealthReport()` renders it for terminals.
15
+ - **Analytics engines** — `scoreSessionQuality()` (beta) with weekly trends via `calculateQualityTrend()`, `calculateCodeImpact()` for cost per changed line and per-model churn, and `calculateCompactionLedger()` for reported-vs-heuristic compaction accounting.
16
+ - **Error taxonomy & history** — `categorizeError()` assigns one shared taxonomy at ingest, and `appendErrorHistory()` / `getTopFailingTools()` keep an append-only per-project failure history.
17
+ - **Statusline & burn rate** — `formatStatusline()` and `BurnRateCalculator` render a cache-only account/quota/burn footer, importable from the new [`sidekick-shared/statusline`](#supported-import-paths) subpath.
12
18
  - **Codex reset credits** — `fetchCodexResetCreditsFromApi()` reads ChatGPT's reset-credit endpoint into a `CodexResetCreditsSnapshot`, and `fetchCodexQuotaFromApi()` attaches it to the returned quota state (preserved across session-sourced snapshot writes).
13
- - **Assistant-turn projection** — `segmentAssistantTurn()` / `assistantTurnEventsFromSessionEvents()` segment a provider-normalized turn into a browser-safe Timeline + Process + Answer shape (see [Assistant Turns](#api-overview)).
14
- - **Session-context snapshots** — `buildSessionContextSnapshot()` / `calculateSessionContextPressure()` expose layered evidence sources and low/medium/high context pressure.
15
- - **Zod boundary schemas** — runtime validation for data crossing process/IPC boundaries, importable fs-free from [`sidekick-shared/schemas`](#supported-import-paths).
16
- - **Opus 4.8 & Fable 5 model info** — 1M-token context windows and pricing, plus forgiving (trim/lowercase) model-ID lookups.
17
- - **Node-only asset extraction** — `gatherAssetsForCwd()` collects URLs, file paths, commands, and plans from recent Claude Code and Codex sessions.
18
19
 
19
20
  See the [full changelog](https://github.com/cesarandreslopez/sidekick-agent-hub/blob/main/CHANGELOG.md) for everything.
20
21
 
@@ -26,49 +27,56 @@ npm install sidekick-shared
26
27
 
27
28
  ## API Overview
28
29
 
29
- | Module | Description |
30
- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31
- | **Types** | Session events, OpenCode/Codex format types, persistence schemas (tasks, decisions, notes, plans, historical data) |
32
- | **Paths** | Config directory resolution, project data paths, workspace encoding |
33
- | **Readers** | Read tasks, decisions, notes, history, handoff, and plans from `~/.config/sidekick/` |
34
- | **Providers** | Session provider abstraction with Claude Code, OpenCode, and Codex implementations; auto-detection via filesystem |
35
- | **Parsers** | JSONL event parsing, OpenCode/Codex format normalization, subagent scanning, session path resolution, debug log parsing |
36
- | **Watchers** | Live session file watching with event bridging, plus `createJsonlTail()` for raw incremental JSONL consumers |
37
- | **Formatters** | Display helpers (`formatTokenCount()`, `formatDurationMs()`), tool summary, noise classification, session dump (text/markdown/JSON), event highlighting |
38
- | **Search** | Cross-session full-text search, advanced filtering (substring, fuzzy, regex, date) |
39
- | **Aggregation** | Event aggregation, frequency tracking, activity heatmaps, pattern extraction |
40
- | **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) |
41
- | **Assistant Turns** | Browser-safe timeline/process/answer projection for provider-normalized assistant turns (`segmentAssistantTurn()`, `assistantTurnEventsFromSessionEvents()`), including interleaved reasoning, compact tool groups, and Claude `Task` subagent refs without prompt leakage |
42
- | **Report** | Self-contained HTML session report generation |
43
- | **Credentials** | Claude Max OAuth credential reading from `~/.claude/.credentials.json` |
44
- | **Quota** | Claude Max subscription quota fetching (5-hour and 7-day windows), Codex rate-limit extraction from event streams (plus Codex reset credits via `fetchCodexResetCreditsFromApi()`, attached to quota state by `fetchCodexQuotaFromApi()`), and authoritative z.ai Coding Plan quota fetching via `resolveZaiQuota()` / `fetchZaiQuotaFromApi()` (reads z.ai's `api/monitor/usage/quota/limit` endpoint with cached-snapshot fallback). The deprecated observed-traffic z.ai estimator helpers remain exported for backward compatibility only |
45
- | **Provider Status** | API health checking via status.claude.com and status.openai.com (indicator, components, incidents) |
46
- | **Schemas** | Zod schemas for runtime validation of data crossing process/IPC boundaries — JSONL session events (`sessionEventSchema`, `messageUsageSchema`, `sessionMessageSchema`), assistant turns, quota, account status, and quota history — plus `extractSessionEvents()` to unwrap `progress`-wrapped events. Also published fs-free via the [`sidekick-shared/schemas`](#supported-import-paths) subpath |
47
- | **Extractors** | Pure functions for single-event processing: `extractTokenUsage()`, `extractToolCall()` (top-level `tool_use`), `extractToolCalls()` (assistant content blocks), plus Node-only session asset extraction (`gatherAssetsForCwd()`, `readClaudeAssets()`, `readCodexAssets()`) for URLs, file paths, commands, and plans |
48
- | **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()`) |
49
- | **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
50
- | **Multi-Provider Quota** | `MultiProviderQuotaService` orchestrates Claude polling + peak-hours + account labels + Codex/z.ai quota updates behind one typed event stream. `CodexQuotaWatcher` watches the active Codex rollout for live rate limits with snapshot fallback; z.ai quota uses the API resolver with snapshot fallback |
51
- | **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, `getActiveAccountStatus()` for a single-pass active-account read across providers, and live-first `resolveActiveClaudeAccount()` / `resolveActiveCodexAccount()` that report the currently logged-in account (self-healing the saved pointer) for display |
52
- | **Account Management 2.0** | Provider-neutral acquisition + switching: `beginAccountLogin()` / `getAccountLoginStatus()` / `finalizeAccountLogin()` / `spawnAccountLogin()` drive a TTY-less, profile-isolated login that doesn't disturb the active account until finalization; `listAllAccounts()` and `switchAccount()` expose a shared switcher across Claude and Codex. Claude accounts get canonical profile homes with account-scoped macOS keychain suffixes and startup migration from legacy flat backups |
53
- | **Terminal sync & auto-switch** | Opt-in terminal profile pointers, shell-hook/launcher helpers, and a default-off `AutoSwitchController` (`decideAutoSwitch()`) that moves to a healthier saved account when quota crosses a configured threshold |
54
- | **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 |
55
- | **Quota Snapshots** | Persistent quota caching per provider/account for offline fallback |
56
- | **Phrases** | Curated humorous phrases for loading/idle states, available as a flat `ALL_PHRASES` array or grouped via `PHRASE_CATEGORIES` for category-aware UI |
30
+ | Module | Description |
31
+ | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
32
+ | **Types** | Session events, OpenCode/Codex format types, persistence schemas (tasks, decisions, notes, plans, historical data) |
33
+ | **Paths** | Config directory resolution, project data paths, workspace encoding |
34
+ | **Readers** | Read tasks, decisions, notes, history, handoff, and plans from `~/.config/sidekick/` |
35
+ | **Writers** | Atomic persistence writers — `atomicWriteJson()` / `updateJsonStoreAtomic()` plus lock-coordinated capture helpers `addTask()`, `completeTask()`, `addDecision()`, `addNote()` that merge into the shared stores without clobbering concurrent writers |
36
+ | **Providers** | Session provider abstraction with Claude Code, OpenCode, and Codex implementations; auto-detection via filesystem |
37
+ | **Observed Sessions (V1)** | Versioned provider-neutral observation contracts — `createProviderSessionAdapterV1()` / `derivePendingUserRequestV1()` with `ObservedAgentSessionV1`, `ProviderCapabilitiesV1`, `PendingUserRequestV1`, `SessionEvidenceRefV1`, and provenance/confidence wrapper types (Zod schemas in `sidekick-shared/schemas`) |
38
+ | **Parsers** | JSONL event parsing, OpenCode/Codex format normalization, subagent scanning, session path resolution, debug log parsing |
39
+ | **Watchers** | Live session file watching with event bridging, plus `createJsonlTail()` for raw incremental JSONL consumers |
40
+ | **Formatters** | Display helpers (`formatTokenCount()`, `formatDurationMs()`), tool summary, noise classification, session dump (text/markdown/JSON), event highlighting |
41
+ | **Search** | Cross-session full-text search, advanced filtering (substring, fuzzy, regex, date) |
42
+ | **Aggregation** | Event aggregation, frequency tracking, activity heatmaps, pattern extraction |
43
+ | **Analytics** | Session-quality scoring (beta) with factor contributions and weekly trends (`scoreSessionQuality()`, `calculateQualityTrend()`), code-impact metrics including cost per changed line and per-model churn/cost (`calculateCodeImpact()`), and reported-vs-heuristic compaction accounting (`calculateCompactionLedger()`, `formatCompactionLedger()`) |
44
+ | **Error History** | Shared error taxonomy (`categorizeError()`, `extractErrorMessage()`) plus append-only per-project failure history (`appendErrorHistory()`, `readErrorHistory()`, `getTopFailingTools()`) behind per-tool/hour/model error rollups |
45
+ | **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) |
46
+ | **Assistant Turns** | Browser-safe timeline/process/answer projection for provider-normalized assistant turns (`segmentAssistantTurn()`, `assistantTurnEventsFromSessionEvents()`), including interleaved reasoning, compact tool groups, and Claude `Task` subagent refs without prompt leakage |
47
+ | **Report** | Self-contained HTML session report generation |
48
+ | **Credentials** | Claude Max OAuth credential reading from `~/.claude/.credentials.json` |
49
+ | **Quota** | Claude Max subscription quota fetching (5-hour and 7-day windows), Codex rate-limit extraction from event streams (plus Codex reset credits via `fetchCodexResetCreditsFromApi()`, attached to quota state by `fetchCodexQuotaFromApi()`), and authoritative z.ai Coding Plan quota fetching via `resolveZaiQuota()` / `fetchZaiQuotaFromApi()` (reads z.ai's `api/monitor/usage/quota/limit` endpoint with cached-snapshot fallback). The deprecated observed-traffic z.ai estimator exports were removed in 0.23.0 — `resolveZaiQuota()` is the single z.ai quota API |
50
+ | **Provider Status** | API health checking via status.claude.com and status.openai.com (indicator, components, incidents) |
51
+ | **Doctor** | Typed cross-provider health diagnostics — `runDoctor()` checks project identity, sessions, accounts, providers, and dependencies into a `HealthReport`; `formatHealthReport()` renders it and `getSessionDiagnostics()` exposes the per-provider session probe |
52
+ | **Schemas** | Zod schemas for runtime validation of data crossing process/IPC boundaries — JSONL session events (`sessionEventSchema`, `messageUsageSchema`, `sessionMessageSchema`), assistant turns, observed-session V1 records, quota, account status, and quota history — plus `extractSessionEvents()` to unwrap `progress`-wrapped events. Also published fs-free via the [`sidekick-shared/schemas`](#supported-import-paths) subpath |
53
+ | **Extractors** | Pure functions for single-event processing: `extractTokenUsage()`, `extractToolCall()` (top-level `tool_use`), `extractToolCalls()` (assistant content blocks), plus Node-only session asset extraction (`gatherAssetsForCwd()`, `readClaudeAssets()`, `readCodexAssets()`) for URLs, file paths, commands, and plans |
54
+ | **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()`) |
55
+ | **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
56
+ | **Multi-Provider Quota** | `MultiProviderQuotaService` orchestrates Claude polling + peak-hours + account labels + Codex/z.ai quota updates behind one typed event stream. `CodexQuotaWatcher` watches the active Codex rollout for live rate limits with snapshot fallback; z.ai quota uses the API resolver with snapshot fallback |
57
+ | **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, `getActiveAccountStatus()` for a single-pass active-account read across providers, and live-first `resolveActiveClaudeAccount()` / `resolveActiveCodexAccount()` that report the currently logged-in account (self-healing the saved pointer) for display |
58
+ | **Account Management 2.0** | Provider-neutral acquisition + switching: `beginAccountLogin()` / `getAccountLoginStatus()` / `finalizeAccountLogin()` / `spawnAccountLogin()` drive a TTY-less, profile-isolated login that doesn't disturb the active account until finalization; `listAllAccounts()` and `switchAccount()` expose a shared switcher across Claude and Codex. Claude accounts get canonical profile homes with account-scoped macOS keychain suffixes and startup migration from legacy flat backups |
59
+ | **Terminal sync & auto-switch** | Opt-in terminal profile pointers, shell-hook/launcher helpers, and a default-off `AutoSwitchController` (`decideAutoSwitch()`) that moves to a healthier saved account when quota crosses a configured threshold |
60
+ | **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 |
61
+ | **Quota Snapshots** | Persistent quota caching per provider/account for offline fallback |
62
+ | **Statusline** | Cache-only one-line account/quota/burn footer — `formatStatusline()`, `selectStatuslineAccount()`, `BurnRateCalculator`, and `estimateTimeToQuota()`, also published via the [`sidekick-shared/statusline`](#supported-import-paths) subpath for fast per-prompt rendering |
63
+ | **Phrases** | Curated humorous phrases for loading/idle states, available as a flat `ALL_PHRASES` array or grouped via `PHRASE_CATEGORIES` for category-aware UI |
57
64
 
58
65
  ## Supported import paths
59
66
 
60
67
  `sidekick-shared` ships three public entry points plus a few convenience subpaths. Pick the one that matches your runtime.
61
68
 
62
- | Path | Runtime | What it exposes |
63
- | ------------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
64
- | `sidekick-shared` | Node (CLI, extension host) | Full public API (readers, providers, parsers, pricing, …). |
65
- | `sidekick-shared/browser` | **Browser / webview** | Pure helpers: context-window lookup, model parsing, cost math, assistant turn projection. |
66
- | `sidekick-shared/node` | Node only | LiteLLM pricing catalog hydration (`fs` + `path`). |
67
- | `sidekick-shared/phrases` | Any runtime | Phrase arrays + `getRandomPhrase()`. |
68
- | `sidekick-shared/modelContext` | Any runtime | Direct access to the context-window module. |
69
- | `sidekick-shared/modelInfo` | Any runtime | Direct access to model parsing and cost math. |
70
- | `sidekick-shared/formatting` | Any runtime | Direct access to pure token and duration display helpers. |
71
- | `sidekick-shared/schemas` | Any runtime | Pure Zod boundary schemas (session events, assistant turns, quota, account status, quota history) — fs-free, no Node builtins. |
69
+ | Path | Runtime | What it exposes |
70
+ | ------------------------------ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
71
+ | `sidekick-shared` | Node (CLI, extension host) | Full public API (readers, providers, parsers, pricing, …). |
72
+ | `sidekick-shared/browser` | **Browser / webview** | Pure helpers: context-window lookup, model parsing, cost math, assistant turn projection. |
73
+ | `sidekick-shared/node` | Node only | LiteLLM pricing catalog hydration (`fs` + `path`). |
74
+ | `sidekick-shared/phrases` | Any runtime | Phrase arrays + `getRandomPhrase()`. |
75
+ | `sidekick-shared/modelContext` | Any runtime | Direct access to the context-window module. |
76
+ | `sidekick-shared/modelInfo` | Any runtime | Direct access to model parsing and cost math. |
77
+ | `sidekick-shared/formatting` | Any runtime | Direct access to pure token and duration display helpers. |
78
+ | `sidekick-shared/schemas` | Any runtime | Pure Zod boundary schemas (session events, assistant turns, observed-session V1, quota, account status, quota history) — fs-free, no Node builtins. |
79
+ | `sidekick-shared/statusline` | Node only | Cache-only statusline surface: account status + quota snapshot readers, `formatStatusline()`, and burn-rate estimates. |
72
80
 
73
81
  ### Browser / webview runtimes
74
82
 
@@ -102,13 +110,23 @@ await hydratePricingCatalog({ cacheDir: '~/.config/sidekick' });
102
110
  ### Detect the active session provider
103
111
 
104
112
  ```typescript
105
- import { detectProvider } from 'sidekick-shared';
106
-
107
- const provider = await detectProvider('/path/to/project');
108
- if (provider) {
109
- console.log(`Active provider: ${provider.id}`);
110
- const sessions = await provider.listSessions();
111
- }
113
+ import {
114
+ detectProvider,
115
+ ClaudeCodeProvider,
116
+ OpenCodeProvider,
117
+ CodexProvider,
118
+ } from 'sidekick-shared';
119
+
120
+ const providerId = detectProvider(); // 'claude-code' | 'opencode' | 'codex'
121
+ const provider =
122
+ providerId === 'opencode'
123
+ ? new OpenCodeProvider()
124
+ : providerId === 'codex'
125
+ ? new CodexProvider()
126
+ : new ClaudeCodeProvider();
127
+
128
+ console.log(`Active provider: ${provider.displayName}`);
129
+ const sessions = provider.findAllSessions('/path/to/project');
112
130
  ```
113
131
 
114
132
  ### Read persisted tasks
@@ -229,17 +247,15 @@ The extractor is safe for CLI and VS Code extension-host code, but not for brows
229
247
  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[]`.
230
248
 
231
249
  ```typescript
232
- import { detectProvider, readSessionContextSnapshot } from 'sidekick-shared';
250
+ import { ClaudeCodeProvider, readSessionContextSnapshot } from 'sidekick-shared';
233
251
 
234
- const provider = await detectProvider('/path/to/project');
235
- if (provider) {
236
- const snapshot = readSessionContextSnapshot(provider, '/path/to/session.jsonl');
252
+ const provider = new ClaudeCodeProvider();
253
+ const snapshot = readSessionContextSnapshot(provider, '/path/to/session.jsonl');
237
254
 
238
- console.log(snapshot.pressure); // 'low' | 'medium' | 'high'
239
- console.log(snapshot.contextTokens, '/', snapshot.contextWindow);
240
- console.log(snapshot.capabilities.tools, snapshot.capabilities.mcpServers);
241
- console.log(snapshot.sources.length, 'evidence sources');
242
- }
255
+ console.log(snapshot.pressure); // 'low' | 'medium' | 'high'
256
+ console.log(snapshot.contextTokens, '/', snapshot.contextWindow);
257
+ console.log(snapshot.capabilities.tools, snapshot.capabilities.mcpServers);
258
+ console.log(snapshot.sources.length, 'evidence sources');
243
259
  ```
244
260
 
245
261
  Use `createSessionContextProjector()` for incremental updates as new events stream in, or `calculateSessionContextPressure(contextTokens, contextWindow)` for the pressure band alone.
@@ -395,10 +411,6 @@ const totalSource = mergeCostSources(a.source, b.source); // 'unpriced' wins (le
395
411
  console.log(formatCost(total), totalSource);
396
412
  ```
397
413
 
398
- ### Deferred Contextful adoption note
399
-
400
- `sidekick-shared@0.18.x` already exposes the quota primitives Contextful needs: `MultiProviderQuotaService`, `ProviderQuotaMap`, `ProviderQuotaState`, and `CodexQuotaWatcher`. Contextful should keep its local integration unchanged until a newer `sidekick-shared` release is published to npm, then migrate thin wrappers to these public APIs plus `formatTokenCount()`, `formatDurationMs()`, and `createJsonlTail()`.
401
-
402
414
  ## Building
403
415
 
404
416
  ```bash
@@ -281,6 +281,7 @@ async function spawnAccountLogin(provider, label, opts = {}) {
281
281
  }
282
282
  let childExited = false;
283
283
  let childExitCode = null;
284
+ const childState = {};
284
285
  const timeoutMs = opts.timeoutMs ?? 180_000;
285
286
  const deadline = Date.now() + timeoutMs;
286
287
  let child;
@@ -297,6 +298,10 @@ async function spawnAccountLogin(provider, label, opts = {}) {
297
298
  childExited = true;
298
299
  childExitCode = code;
299
300
  });
301
+ child.on('error', (error) => {
302
+ childExited = true;
303
+ childState.spawnError = error;
304
+ });
300
305
  while (true) {
301
306
  if (opts.signal?.aborted) {
302
307
  child.kill();
@@ -308,6 +313,11 @@ async function spawnAccountLogin(provider, label, opts = {}) {
308
313
  return finalizeAccountLogin(provider, begin.loginId, { activate: opts.activate ?? true });
309
314
  }
310
315
  if (childExited) {
316
+ if (childState.spawnError) {
317
+ const error = `Could not spawn account login: ${childState.spawnError.message}`;
318
+ emitStatus(opts, { state: 'failed', error });
319
+ return { success: false, error };
320
+ }
311
321
  const finalStatus = emitStatus(opts, getAccountLoginStatus(provider, begin.loginId));
312
322
  if (finalStatus.state === 'authenticated') {
313
323
  return finalizeAccountLogin(provider, begin.loginId, { activate: opts.activate ?? true });
@@ -34,6 +34,8 @@ export interface ResolvedActiveAccount {
34
34
  email?: string;
35
35
  label?: string;
36
36
  providerAccountId?: string;
37
+ /** Stable Sidekick registry ID used by cache/history stores. */
38
+ registryAccountId?: string;
37
39
  source: 'live' | 'registry' | 'none';
38
40
  }
39
41
  export declare function getAccountsDir(): string;
@@ -1,5 +1,6 @@
1
1
  export interface ActiveProviderAccountStatus {
2
2
  present: boolean;
3
+ accountId?: string;
3
4
  email?: string;
4
5
  label?: string;
5
6
  }
@@ -19,6 +19,9 @@ function getActiveAccountStatus(error) {
19
19
  const claude = claudeAccount.source !== 'none'
20
20
  ? {
21
21
  present: true,
22
+ ...(claudeAccount.registryAccountId
23
+ ? { accountId: claudeAccount.registryAccountId }
24
+ : {}),
22
25
  email: claudeAccount.email,
23
26
  label: claudeAccount.label ?? claudeAccount.email,
24
27
  }
@@ -26,6 +29,9 @@ function getActiveAccountStatus(error) {
26
29
  const codex = codexAccount.source !== 'none'
27
30
  ? {
28
31
  present: true,
32
+ ...(codexAccount.registryAccountId
33
+ ? { accountId: codexAccount.registryAccountId }
34
+ : {}),
29
35
  email: codexAccount.email,
30
36
  label: codexAccount.label ?? codexAccount.email ?? codexAccount.providerAccountId,
31
37
  }
package/dist/accounts.js CHANGED
@@ -549,6 +549,7 @@ function resolveActiveClaudeAccount() {
549
549
  email: live.email,
550
550
  label: match?.label,
551
551
  providerAccountId: live.uuid,
552
+ registryAccountId: match?.id,
552
553
  source: 'live',
553
554
  };
554
555
  }
@@ -558,6 +559,7 @@ function resolveActiveClaudeAccount() {
558
559
  email: active.email,
559
560
  label: active.label,
560
561
  providerAccountId: active.uuid,
562
+ registryAccountId: active.uuid,
561
563
  source: 'registry',
562
564
  };
563
565
  }
@@ -9,10 +9,12 @@
9
9
  */
10
10
  import type { SessionEvent, ToolAnalytics, TimelineEvent, TrackedTask, TaskState, PlanState, CompactionEvent, TruncationEvent, ContextAttribution, ContextSizePoint, ResponseLatency, LatencyStats, PermissionMode, PermissionModeChange } from '../types/sessionEvent';
11
11
  import type { FollowEvent } from '../watchers/types';
12
- import type { EventAggregatorOptions, AggregatedTokens, ModelUsageStats, BurnRateInfo, SubagentLifecycle, AggregatedMetrics } from './types';
12
+ import type { EventAggregatorOptions, AggregatedTokens, ModelUsageStats, BurnRateInfo, SubagentLifecycle, AggregatedMetrics, ErrorRollup, ErrorRollupEntry, ErrorBucketMetric } from './types';
13
13
  import type { SerializedFrequencyState } from './FrequencyTracker';
14
14
  import type { SerializedHeatmapState } from './HeatmapTracker';
15
15
  import type { SerializedPatternState } from './PatternExtractor';
16
+ /** Schema version for serialized snapshots. */
17
+ export declare const SNAPSHOT_SCHEMA_VERSION = 3;
16
18
  /**
17
19
  * JSON-serializable snapshot of EventAggregator state.
18
20
  * Used by session snapshot sidecar files for fast resume.
@@ -45,6 +47,10 @@ export interface SerializedAggregatorState {
45
47
  compactionEvents: CompactionEvent[];
46
48
  truncationEvents: TruncationEvent[];
47
49
  toolAnalytics: Array<[string, ToolAnalytics]>;
50
+ errorByToolCategory: Array<[string, ErrorRollupEntry]>;
51
+ errorBuckets: Array<[string, ErrorBucketMetric]>;
52
+ retryAttempts: number;
53
+ finishReasons: Array<[string, number]>;
48
54
  contextAttribution: ContextAttribution;
49
55
  burnSamples: BurnSample[];
50
56
  lastBurnSampleTime: number;
@@ -94,10 +100,20 @@ export declare class EventAggregator {
94
100
  private modelUsage;
95
101
  private currentContextSize;
96
102
  private previousContextSize;
103
+ /**
104
+ * Index of a heuristic compaction event still awaiting its real
105
+ * post-compaction size from the next usage event. Prevents the usage-drop
106
+ * detector from double-counting an explicit compaction marker.
107
+ */
108
+ private pendingHeuristicCompactionIndex;
97
109
  private compactionEvents;
98
110
  private truncationEvents;
99
111
  private toolAnalytics;
100
112
  private pendingToolCalls;
113
+ private errorByToolCategory;
114
+ private errorBuckets;
115
+ private retryAttempts;
116
+ private finishReasons;
101
117
  private burnSamples;
102
118
  private lastBurnSampleTime;
103
119
  private tokensSinceLastSample;
@@ -107,10 +123,13 @@ export declare class EventAggregator {
107
123
  private subagents;
108
124
  private pendingSubagents;
109
125
  private planExtractor;
126
+ private planState;
127
+ private planEnteredAt;
110
128
  private contextAttribution;
111
129
  private currentPermissionMode;
112
130
  private permissionModeHistory;
113
131
  private contextTimeline;
132
+ private readonly contextTimelineCap;
114
133
  private contextTurnIndex;
115
134
  private timeline;
116
135
  private pendingUserRequest;
@@ -131,6 +150,7 @@ export declare class EventAggregator {
131
150
  getAggregatedTokens(): AggregatedTokens;
132
151
  getModelStats(): ModelUsageStats[];
133
152
  getToolStats(): ToolAnalytics[];
153
+ getErrorRollup(): ErrorRollup;
134
154
  getCompactionEvents(): CompactionEvent[];
135
155
  getTruncationEvents(): TruncationEvent[];
136
156
  getBurnRate(): BurnRateInfo;
@@ -149,7 +169,7 @@ export declare class EventAggregator {
149
169
  /** Serializes all mutable state to a JSON-safe object for snapshot persistence. */
150
170
  serialize(): SerializedAggregatorState;
151
171
  /** Restores mutable state from a serialized snapshot. Clears transient state (pending calls). */
152
- restore(state: SerializedAggregatorState): void;
172
+ restore(state: SerializedAggregatorState): boolean;
153
173
  private accumulateUsage;
154
174
  private updateBurnRate;
155
175
  private processLatency;
@@ -157,10 +177,20 @@ export declare class EventAggregator {
157
177
  private detectTruncationInContent;
158
178
  private recordFollowToolUse;
159
179
  private recordFollowToolResult;
180
+ private recordProviderMetadata;
181
+ private recordError;
160
182
  private detectTruncationFromFollowEvent;
183
+ private recordExplicitCompaction;
184
+ /**
185
+ * Fills a pending heuristic compaction with the first observed
186
+ * post-compaction context size. Returns true when the observed drop was
187
+ * consumed by that pending event, so the caller must not record it again.
188
+ */
189
+ private resolvePendingHeuristicCompaction;
161
190
  private extractTaskStateFromEvent;
162
191
  private extractTaskStateFromFollowEvent;
163
192
  private applyTaskUpdate;
193
+ private syncPlanStepFromTaskUpdate;
164
194
  private resolveTaskCreate;
165
195
  private handleTodoWrite;
166
196
  private handleUpdatePlan;
@@ -171,7 +201,11 @@ export declare class EventAggregator {
171
201
  private extractSubagentFromFollowEvent;
172
202
  private completeSubagent;
173
203
  private extractPlanFromSessionEvent;
204
+ private processPlanFollowEvent;
174
205
  private convertExtractedPlanToPlanState;
206
+ private getActivePlanStep;
207
+ private attributePlanUsage;
208
+ private attributePlanToolCall;
175
209
  private attributeContextFromEvent;
176
210
  private attributeContextFromFollowEvent;
177
211
  private trackPermissionMode;