sidekick-shared 0.24.3 → 0.24.5

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 (52) hide show
  1. package/CHANGELOG.md +436 -0
  2. package/LICENSE +21 -0
  3. package/README.md +172 -60
  4. package/dist/accountManager.d.ts +13 -0
  5. package/dist/accountManager.js +60 -32
  6. package/dist/accountRegistry.js +59 -41
  7. package/dist/accounts.js +40 -19
  8. package/dist/autoSwitch.d.ts +2 -1
  9. package/dist/autoSwitch.js +32 -7
  10. package/dist/browser.d.ts +2 -0
  11. package/dist/browser.js +3 -1
  12. package/dist/codexProfiles.d.ts +14 -0
  13. package/dist/codexProfiles.js +268 -116
  14. package/dist/context/sessionContext.js +2 -4
  15. package/dist/ensureDefaultAccounts.js +4 -4
  16. package/dist/index.d.ts +14 -7
  17. package/dist/index.js +33 -7
  18. package/dist/modelInfo.js +11 -2
  19. package/dist/node.d.ts +6 -0
  20. package/dist/node.js +8 -1
  21. package/dist/observedContextWindows.d.ts +20 -4
  22. package/dist/observedContextWindows.js +47 -7
  23. package/dist/parsers/codexHistory.d.ts +30 -0
  24. package/dist/parsers/codexHistory.js +118 -0
  25. package/dist/parsers/mcpToolName.d.ts +26 -0
  26. package/dist/parsers/mcpToolName.js +31 -0
  27. package/dist/parsers/sessionPathResolver.d.ts +11 -4
  28. package/dist/parsers/sessionPathResolver.js +11 -4
  29. package/dist/paths.d.ts +43 -4
  30. package/dist/paths.js +66 -6
  31. package/dist/peakHours.d.ts +16 -2
  32. package/dist/peakHours.js +18 -3
  33. package/dist/pricingCatalog.d.ts +7 -3
  34. package/dist/pricingCatalog.js +3 -2
  35. package/dist/providers/claudeCode.d.ts +2 -1
  36. package/dist/providers/claudeCode.js +18 -0
  37. package/dist/providers/codex.d.ts +22 -1
  38. package/dist/providers/codex.js +58 -0
  39. package/dist/providers/types.d.ts +5 -0
  40. package/dist/quotaHistory.d.ts +1 -1
  41. package/dist/quotaHistory.js +2 -2
  42. package/dist/quotaSnapshots.js +8 -77
  43. package/dist/sessionPreviews.d.ts +60 -0
  44. package/dist/sessionPreviews.js +234 -0
  45. package/dist/sessionTranscripts.js +10 -4
  46. package/dist/writers/atomic.d.ts +20 -0
  47. package/dist/writers/atomic.js +190 -19
  48. package/dist/zaiQuota.d.ts +87 -0
  49. package/dist/zaiQuota.js +49 -0
  50. package/dist/zaiQuotaWatcher.d.ts +7 -0
  51. package/dist/zaiQuotaWatcher.js +3 -0
  52. package/package.json +60 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,436 @@
1
+ # Changelog
2
+
3
+ All notable changes to sidekick-shared will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.24.5] - 2026-08-18
9
+
10
+ ### Added
11
+
12
+ - Session-preview index: `listSessionPreviews()` enumerates session files stat-first across providers, sorts by mtime, applies `since`/`limit`, and content-reads only the post-limit survivors — so cost tracks the size of the answer, not the size of the history. `readSessionPreview()` reads one file's bounded preview (label, first prompt, first timestamp, workspace path) and degrades to `null` fields instead of throwing. Both are exported from the root and `sidekick-shared/node`
13
+ - `readCodexHistory()` returns the newest entries of `~/.codex/history.jsonl` from a bounded tail read, dropping the leading partial line and tolerating multibyte characters straddling the boundary. `findCodexRolloutFile()` resolves a Codex session id to its `sessions/YYYY/MM/DD/rollout-*.jsonl` transcript by filename only, newest mtime winning across monitored Codex homes
14
+ - `parseMcpToolName()` is the canonical splitter for `mcp__<server>__<tool>` identifiers, exported browser-safe; the session-context projector's MCP-server inference delegates to it. The degenerate `mcp__server__` shape (empty tool part) now parses as `null` rather than yielding a server name
15
+ - Async account entry points: `getAccountLoginStatusAsync`, `finalizeAccountLoginAsync`, `switchAccountAsync`, `prepareCodexAccountAsync`, `finalizeCodexAccountAsync`, `switchToCodexAccountAsync`. Each runs the codex CLI probes through `execFile` so the caller's event loop stays free, and probes are resolved before any lock is taken — never while holding one. Sync entry points keep their exact signatures for CLI callers
16
+ - Synchronous locked writers: `withFileLockSync()`, `updateJsonStoreAtomicSync()`, and `atomicWriteFileSync()` share the async path's cross-process lock-file protocol (sync and async waiters exclude each other), with an absolute 15s wait ceiling because a sync waiter blocks its event loop
17
+ - `AutoSwitchController.switchAccount` accepts a Promise-returning callback and defaults to `switchAccountAsync`, so a threshold crossing no longer runs blocking codex probes on the caller's loop. Overlapping quota updates are skipped while a switch is in flight, and a switch that lands after `dispose()` records its cooldown state without emitting a stale transition
18
+ - `SessionProviderBase` gains an optional `listAllSessionFiles()` so providers can expose native session-file enumeration to the preview index
19
+
20
+ ### Fixed
21
+
22
+ - The store lock's timeout is measured from the last time the lock changed hands rather than from a waiter's first attempt, so a burst of concurrent writers no longer manufactures "Timed out waiting for store lock" once the queue outlives the flat budget — a wedged holder still trips it in 3s
23
+ - A lock whose heartbeat mtime has been frozen past two minutes is reclaimed even when its recorded PID probes alive: PID recycling made `process.kill(pid, 0)` an unreliable liveness signal, and a crashed owner with a recycled PID used to wedge the store until a human deleted the lock
24
+ - The quota-snapshot store drops its private copy of the lock — which still had the flat 15s-from-first-attempt bug — and delegates to the shared one, keeping the same lock path so mixed-version processes still exclude each other. Snapshot writes pick up the shared writer's fsync and directory sync
25
+ - Four account modules each carried their own lock-free atomic writer with a fixed `.tmp` suffix, so cross-process account writes were last-writer-wins and two processes could interleave the same temp file. All account writes go through the shared fsynced writers, registry read-modify-write cycles hold the registry lock, and live credential swaps hold a per-provider auth-swap lock so two switchers cannot interleave a stash/restore of a rotated Codex refresh token
26
+ - `switchToAccount()` and `applyActiveClaudeToLiveHome()` threw on a held auth-swap lock where the Codex twins return a failed `AccountManagerResult`; they now keep the non-throwing contract their callers rely on
27
+ - Codex login-status and `pgrep` probes no longer freeze the caller's event loop for up to 4s; `ensureDefaultAccounts` and `spawnAccountLogin` run on the async path
28
+ - `AutoSwitchController`'s fire-and-forget update handler could surface a throwing registry read, snapshot read, or consumer `onTransition` as an unhandled promise rejection — process-fatal on modern Node. Those errors are now caught and logged
29
+ - `readCodexHistory()` rejects entries whose `ts` is not a finite number — `JSON.parse` yields `Infinity` for out-of-range literals, which later RangeErrored out of `Date` serialization in consumers. `readSessionPreview()` likewise returns `null` for a file whose mtime cannot be serialized, per its degrade-don't-throw contract
30
+ - `listRecentSessions()` sorts before labeling, so per-file content reads no longer scale with total session count, and a corrupt session beyond the limit no longer poisons the whole call
31
+
32
+ ## [0.24.4] - 2026-07-25
33
+
34
+ ### Fixed
35
+
36
+ - `fetchPeakHoursStatus()` had no request timeout. It was the only network call in the package without one, it targets a third-party host (promoclock.co), and `MultiProviderQuotaService` calls it on every poll, so a hung host stalled the caller indefinitely and repeatedly. It now uses the same abort-timeout shape as every other fetch here, configurable via `FetchPeakHoursOptions.timeoutMs` (default 10s)
37
+ - `recordObservedContextWindow()` deduplicated its write against the process-global override table from `modelContext`, which is not keyed by `cacheDir`. Two cache directories in one process — two temp dirs in a test suite, a multi-tenant host, a CLI and extension host sharing a process — meant the second store was silently never written. The same early return also swallowed retries: because the global was updated before the write was attempted, a transient `EACCES` left the value marked as recorded and no later call tried again. The persistence dedupe is now keyed by resolved store path and recorded only after a successful write
38
+ - `parseModelId()` reported version `"4"` for every hyphenated Anthropic model ID (`claude-sonnet-4-6-20260321`, `claude-sonnet-4-5-20250929`, `claude-opus-4-1-20250805`). The version pattern accepted dots but not hyphens, while the adjacent legacy pattern already normalized both. Pricing and context windows are unaffected — both resolve from the full ID by longest-prefix match — but `parseModelId().version`, `getModelInfo().version`, and the version ordering behind model comparison all change, where 4.5 and 4.6 previously tied at `"4"`
39
+
40
+ ### Added
41
+
42
+ - The config directory is now overridable. `setConfigDir()` redirects it for the process and `SIDEKICK_CONFIG_DIR` does the same from the environment, with `getDefaultConfigDir()` exposing the unmodified platform default. Both routes trim their input, so a padded value resolves to the same root rather than to a relative path under the current directory. Every reader, writer, account-registry path, quota snapshot, and migration resolves the root lazily through `getConfigDir()`, so a single seam covers the whole package. Consumers previously had no way to point the library anywhere but `~/.config/sidekick`, which blocked test isolation, sandboxing, and multi-tenant use
43
+ - The published tarball ships `LICENSE` and `CHANGELOG.md`. `files: ["dist"]` excluded both, and npm only auto-includes a LICENSE from the package directory — so the MIT declaration in `package.json` shipped with nothing behind it
44
+ - `repository` (with `directory`, so npm's source link lands on this package), `homepage`, `bugs`, `keywords`, `author`, and `engines` are declared
45
+ - `typesVersions` maps every non-wildcard `exports` subpath for consumers whose tsconfig still uses `moduleResolution: "node"`, which cannot read `exports` maps at all. Modern resolvers ignore it entirely
46
+ - Every export of the legacy z.ai estimator (`zaiQuota`, `zaiQuotaWatcher`) is marked `@deprecated`. The deprecation was previously recorded in module prose only, so consumers reaching it through the `./dist/*` wildcard got no editor signal
47
+
48
+ ### Changed
49
+
50
+ - `engines` declares Node >= 20, driven by zod 4 and matching the sibling CLI. It is advisory unless a consumer sets `engine-strict`, but it will surface an `EBADENGINE` warning on Node 18
51
+ - `HydrateOptions.cacheDir` is optional and defaults to the config directory, so pricing hydration follows the same override seam
52
+
53
+ ### Documentation
54
+
55
+ - Four README examples were wrong and are corrected: `readTasks` was shown with the wrong first argument and no `await`; `readClaudeMaxCredentials` was shown without `await`, so the always-truthy Promise made `accessToken` undefined and surfaced later as an auth failure; the documented context window was 200000 rather than 1000000; and the documented cost output was never reachable from the stated inputs
56
+ - The headline cost example uses `normalizeProviderUsage` and `calculateNormalizedUsageCost` instead of the deprecated `calculateCost`
57
+ - The external `sqlite3` requirement is documented. `OpenCodeProvider` and `CodexProvider` shell out to it and return empty results rather than an error when it is missing, which is indistinguishable from an empty workspace unless the consumer knows to call `getRuntimeStatus()`
58
+
59
+ ## [0.24.3] - 2026-07-24
60
+
61
+ ### Fixed
62
+
63
+ - Static pricing for `gpt-5.6-luna` ($1/$6 in/out, $1.25/$0.10 cache write/read). Without an entry of its own, `getModelPricing('gpt-5.6-luna')` longest-prefix-matched `gpt-5.6` and returned `gpt-5.6-sol`'s $5/$30 — roughly 5× too high, and indistinguishable from a correct result at the call site
64
+ - Static pricing for `gpt-5.5-pro` and `gpt-5.4-pro` ($30/$180 each) and `gpt-5.4-nano` ($0.20/$1.25), which inherited `gpt-5.5` and `gpt-5.4` the same way. Rates verified against the LiteLLM catalog's bare top-level keys
65
+ - `getModelPricing('claude-sonnet-5')` returned $3/$15 against its actual $2/$10 (cache write $2.50, cache read $0.20). The entry assumed the $3/$15 shared by every earlier Sonnet; all eleven Sonnet 5 rows in the catalog agree on $2/$10
66
+ - A sweep of every bare catalog key against the static table found twelve more chat models resolving through a shorter prefix. New entries: `gpt-5-mini`, `gpt-5-nano`, `gpt-5-pro`, `gpt-5.1-codex-mini`, `gpt-5.2`, `gpt-5.2-pro`, `gpt-5.3-chat-latest`, `gpt-4.1-mini`, `gpt-4.1-nano`, `gpt-4o-2024-05-13`, `o1-pro`, `o3-pro`, and `o3-deep-research`. Overstatements reached 25× (`gpt-5-nano`) and understatements 17× (`gpt-5.2-pro`)
67
+ - Corrected values on two keys that already existed: `gpt-5.3-codex` to $1.75/$14 and `o1-mini` to $1.10/$4.40. No bare catalog key settles `o1-mini` — Azure's $1.21/$4.84 is its standard 1.1× uplift on that rate, and Replicate's OpenAI passthrough reports it directly
68
+ - Because keys are matched longest-first, each new entry also captures its own dated and fine-tuned variants — `gpt-5.6-luna-20260501` now resolves to Luna rather than to `gpt-5.6`, and one `gpt-5.2` entry covers `gpt-5.2-codex` and the `gpt-5.2-chat*` ids, which bill at the same rate
69
+
70
+ ### Added
71
+
72
+ - `MODEL_CONTEXT_SIZES` entry for `gpt-5.6-luna` at its published 1,050,000 maximum. The value equals what the `gpt-5.6` prefix already returned; it is pinned so a later edit to that entry cannot silently move Luna. Observed per-tier windows still outrank it
73
+
74
+ ### Notes
75
+
76
+ - The `-tts`, `-transcribe`, and `-realtime-preview` variants of `gpt-4o`/`gpt-4o-mini` also bill above the key they inherit and are deliberately not carried in the static table: no coding agent routes to them, and catalog hydration prices them correctly. `gpt-5.3` keeps an estimated rate because the catalog ships no bare key for it
77
+
78
+ ## [0.24.2] - 2026-07-24
79
+
80
+ ### Added
81
+
82
+ - `hydratePricingCatalog()` also builds a context-window override map from the LiteLLM catalog's `max_input_tokens`, so new models resolve without a code change. `HydrateResult` gains `contextWindowEntries`
83
+ - `normalizeLiteLlmContextWindows()` — catalog payload to model → context-window map, exported alongside `normalizeLiteLlmCatalog()`
84
+ - `loadObservedContextWindows()`, `recordObservedContextWindow()`, and `getObservedContextWindowPath()` on `sidekick-shared/node` — a persisted record of the context window a provider actually reported, which outranks the catalog's published maximum. The Codex provider records it from `model_context_window` on `token_count` events
85
+ - Static pricing for `claude-opus-5`, `claude-sonnet-5`, `gpt-5.6`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.5`, and `gpt-5.4-mini`. Sonnet 5 carries the standard $3/$15 rather than the introductory rate, which arrives via hydration
86
+
87
+ ### Changed
88
+
89
+ - `getModelContextWindowSize()` resolution is `[1m]` marker → observed → catalog → static, and tries every layer for an exact match before any layer's prefix match. `GPT-5.4` pricing is corrected to its published $2.50/$15
90
+ - Cache files written before this release have no `contextWindows` key and still load; they contribute no context overrides until the next refresh
91
+
92
+ ### Fixed
93
+
94
+ - An exact entry in the static table is no longer shadowed by a catalog key that happens to be a prefix of the model ID. The catalog has no `claude-sonnet-4-7`, so Sonnet 4.7 was resolving through `claude-sonnet-4` to 128K instead of its real 1,000,000
95
+ - `normalizeLiteLlmContextWindows()` drops a bare-model alias when the `provider/model` entries behind it disagree, rather than taking whichever appeared first in the JSON. A `provider/model` entry describes that provider's deployment window — often a truncation — and the live catalog carries five conflicting windows for `claude-sonnet-4`, from GitHub Copilot's 128K to Vertex's 1M. A top-level catalog entry always wins over a derived alias
96
+
97
+ ## [0.24.1] - 2026-07-22
98
+
99
+ ### Added
100
+
101
+ - Canonical transcripts retain provider/session provenance for safe human-prompt filtering, including entrypoint, meta/sidechain flags, original message roles, cwd, and git branch
102
+ - Provider-backed observed-session collection sources expose their adapter's `ProviderCapabilitiesV1` record
103
+
104
+ ## [0.24.0] - 2026-07-22
105
+
106
+ ### Added
107
+
108
+ - `normalizeProviderUsage()`, `extractNormalizedUsage()`, and `calculateNormalizedUsageCost()` with explicit cache/reasoning semantics and pricing provenance
109
+ - `estimateTextTokens()` / `estimateSerializedTokens()`, canonical transcript/history APIs, transcript schemas, and `ObservedSessionCollector`
110
+
111
+ ### Changed
112
+
113
+ - `SessionEvent` and its Zod schema are discriminated; valid message-less summaries and nested progress envelopes parse directly
114
+ - Claude Code, Codex, OpenCode, report transcripts, observed sessions, and EventAggregator share normalized usage and canonical projection paths
115
+ - Aggregator snapshots advance to schema v4 so older ambiguous-cost snapshots are replayed from source events
116
+ - Provider-reported session costs — including zero — are authoritative and skip catalog pricing, so subscription sessions that report $0 display $0 instead of a catalog estimate
117
+ - `ObservedAgentSessionV1` `usage.costUsd` is now nullable for unpriced usage (still `schemaVersion: 1`); validators built against the earlier number-only shape should accept `null`
118
+
119
+ ### Deprecated
120
+
121
+ - `calculateCost()`, `calculateCostWithPricing()`, `calculateCostWithProvenance()`, `CostTokenUsage`, and `CostProvenanceInput` retain their legacy reasoning-additive behavior for compatibility
122
+
123
+ ## [0.23.1] - 2026-07-21
124
+
125
+ ### Security
126
+
127
+ - Markdown report links refuse `javascript:`/`data:` URLs, and browser opening spawns the platform opener with argument arrays instead of an interpolated shell string
128
+
129
+ ### Fixed
130
+
131
+ - **2026-07 review backlog (shared portion)**: crash guards across parsers, watchers, and readers for malformed rows; index-safe SQL substitution; hardened atomic writers and quota snapshot persistence; and hardened quota polling and provider watchers
132
+ - **Observed-session message-less events**: `derivePendingUserRequestV1()` now tolerates Claude Code summary and other bookkeeping rows without a `message`, continues scanning for unresolved requests, and retains top-level tool requests with a stable event-index fallback ID
133
+ - Hyphenated `claude-opus-4-5` model IDs price at Opus 4.5 rates instead of falling back to legacy Opus pricing, and Codex cache tokens are counted once in usage rollups
134
+ - macOS keychain credential writes escape the secret correctly for the `security -i` tokenizer (single quotes and backslashes in stored JSON no longer corrupt the entry), and the secret is passed over stdin instead of argv
135
+
136
+ ### Changed
137
+
138
+ - Token metric definitions are more truthful: Codex `input_tokens` excludes cached input, per-model totals include cache read/write tokens, and follow-event message counts include only user/assistant messages — dashboard numbers may shift after upgrade
139
+
140
+ ## [0.23.0] - 2026-07-18
141
+
142
+ ### Added
143
+
144
+ - **Observed-session V1 public API**: `ProviderSessionAdapterV1`, `ObservedAgentSessionV1`, `ProviderCapabilitiesV1`, `PendingUserRequestV1`, `SessionEvidenceRefV1`, provenance/confidence values, adapters for all session providers, and corresponding Zod schemas
145
+ - **Shared operational primitives**: atomic and lock-coordinated task/decision/note writers, canonical project identity, typed doctor reports, status-line and burn-rate formatting, identifier-only external-handoff URL rendering, and a public `SessionMonitor`
146
+ - **Analytics engines**: categorized per-tool/hour/model failure rollups and append-only error history, beta quality scoring with weekly trends, historical schema v3 session records, code-impact and per-model churn/cost metrics, and compaction-ledger estimates
147
+ - **Consumer contract tests** for quota, accounts, sessions, assets, costs, turn/reasoning, and observed-session APIs
148
+
149
+ ### Changed
150
+
151
+ - Codex compaction events retain exact before/after counts with reported/heuristic provenance; aggregator snapshots advance to v3; heuristic compactions are recorded once and completed with the next observed context size instead of double-counting
152
+ - Shared ingest owns tool-error taxonomy across Claude Code, OpenCode, and Codex; OpenCode provider errors, retries, and finish reasons are normalized for analytics
153
+ - Canonical symlink-aware project identity replaces duplicated slug probing; OpenCode SQL substitution is index-safe; Codex/OpenCode database row shapes have one source
154
+ - Legacy raw-slug project stores migrate automatically to the canonical slug on first write (`migrateLegacyProjectStores`), so symlinked workspaces keep their existing tasks, decisions, notes, and handoffs
155
+ - Plan aggregation now records per-step start/completion time, duration, tokens, tool calls, and cost
156
+
157
+ ### Removed
158
+
159
+ - **Potentially breaking**: deprecated observed-traffic z.ai estimator exports were removed from the package root. Use `resolveZaiQuota()` and the authoritative quota API surface instead
160
+
161
+ ## [0.22.0] - 2026-07-02
162
+
163
+ ### Changed
164
+
165
+ - **Version bump to 0.22.0**: No functional changes. This release only keeps the shared library in lockstep with the VS Code extension and CLI (which ship user-facing changes this cycle); the published API and behavior are identical to 0.21.6
166
+
167
+ ## [0.21.6] - 2026-07-02
168
+
169
+ ### Added
170
+
171
+ - **Codex reset credits**: New `fetchCodexResetCreditsFromApi()` reads ChatGPT's `wham/rate-limit-reset-credits` endpoint and returns a `CodexResetCreditsSnapshot` (available count plus per-credit title/status/expiry), with the new `CodexResetCredit` / `CodexResetCreditsSnapshot` types and `codexResetCreditSchema` / `codexResetCreditsSnapshotSchema` Zod validators. `fetchCodexQuotaFromApi()` now attaches this snapshot to the returned `QuotaState` (`resetCredits`), authenticating with the Codex `auth.json` `account_id` (sent as `ChatGPT-Account-ID`) when present. `writeQuotaSnapshot`, the `CodexQuotaWatcher`, and the Codex session provider preserve an existing reset-credits snapshot when a session-sourced write omits it (Codex-only), so local session updates never wipe API-fetched credits. Degrades gracefully — every reset-credit fetch failure is swallowed and leaves the quota otherwise intact
172
+
173
+ ## [0.21.5] - 2026-06-30
174
+
175
+ ### Added
176
+
177
+ - **`isAggregateCodexLimit(limitId)`**: Exported helper that identifies the aggregate Codex plan-quota family (`limit_id` `codex`, or absent on older payloads) versus model/feature-specific families (e.g. `codex_bengalfox`, `premium`)
178
+
179
+ ### Fixed
180
+
181
+ - **Prefer the aggregate Codex rate-limit family over model-specific ones**: Codex emits multiple rate-limit families per session keyed by `limit_id` — the aggregate plan quota plus per-model/feature families that can read 0% while the plan is busy. Every local-data selector previously read "the latest `rate_limits`" without inspecting `limit_id`, so a freshly-used per-model family at 0% with a later-resetting window could win the newest-window comparison and mask the real plan quota. The aggregate family is now ranked first in every selection site — `readLatestQuotaFromRollout`, the live `CodexRolloutParser`, the cross-file `isPreferredQuotaHit` ranker, and the `shouldKeepExistingSnapshot` retention guard — falling back to a model-specific sample only when no aggregate one exists. No-op for Claude (no `limit_id`) and z.ai (`zai-*`), where every sample shares the same aggregate-ness
182
+
183
+ ## [0.21.4] - 2026-06-30
184
+
185
+ ### Fixed
186
+
187
+ - **Live-first active-account resolution**: New `resolveActiveClaudeAccount()` / `resolveActiveCodexAccount()` and the `ResolvedActiveAccount` type resolve the currently logged-in account from the live provider auth (`~/.claude/.claude.json` oauthAccount; the `~/.codex/auth.json` id_token JWT — a cheap JWT decode, never the slow `codex login status` subprocess) and fall back to the saved registry. On an unambiguous match they self-heal the stale active pointer (best-effort, never throwing, never creating or deleting profiles) so quota history and auto-switch track the real account. `getActiveAccountStatus()`, the multi-provider Claude path, and the Codex quota watcher now route through them
188
+
189
+ ## [0.21.3] - 2026-06-23
190
+
191
+ ### Added
192
+
193
+ - **Quota projection helpers**: New exported `projectQuotaWindow()` and `withQuotaProjections()`, plus the `FIVE_HOUR_WINDOW_MS` / `SEVEN_DAY_WINDOW_MS` constants and the `QuotaProjectionInput` type, generalize the previously Claude-only end-of-window utilization projection. Codex (`quotaFromCodexRateLimits`, using each window's real `window_minutes`) and z.ai (`quotaStateFromZaiQuotaLimitPayload`) quota states now populate `projectedFiveHour` / `projectedSevenDay`. Projection is idempotent (it only fills fields that are still null, so it never double-counts) and honors a `capturedAt` timestamp so cached snapshots project from capture time
194
+
195
+ ### Fixed
196
+
197
+ - **Bounded synchronous CLI probes**: Every synchronous `execFileSync` / `spawnSync` / `execSync` probe — keychain reads/writes (`credentialIO`, `claudeProfiles`), Codex login status and `pgrep` (`codexProfiles`), `git rev-list` (`providers/openCode`), and `sqlite3` (`providers/codexDatabase`, `providers/openCodeDatabase`) — now runs with `timeout: 4000` and `killSignal: 'SIGKILL'`, so a hung CLI, keychain prompt, or database can no longer block the caller indefinitely
198
+
199
+ ### Security
200
+
201
+ - **npm audit**: Bumped `vitest` to `^4.1.9` to clear reported dev-dependency advisories
202
+
203
+ ## [0.21.2] - 2026-06-22
204
+
205
+ ### Changed
206
+
207
+ - **Authoritative z.ai quota API**: new `zaiQuotaApi.ts` reads z.ai's `api/monitor/usage/quota/limit` endpoint and exports `resolveZaiQuota()`, `fetchZaiQuotaFromApi()`, `readZaiCredentials()`, and `quotaStateFromZaiQuotaLimitPayload()`, mapping the returned `TOKENS_LIMIT` percentages and `nextResetTime` values into the 5-Hour / Weekly `QuotaState` model. Credentials are discovered from OpenCode's stored `zai-coding-plan` → `zai` token, then `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN`; API failures fall back to a cached snapshot
208
+ - **Deprecated observed-traffic estimator**: `zaiQuota.ts` and `zaiQuotaWatcher.ts` (and their exports — `accumulateZaiUsage`, `inferZaiQuotaState`, `resolveZaiTier`, `ZaiQuotaWatcher`, `ZAI_TIER_BUDGETS`, …) remain available for compatibility but are no longer used for product quota display
209
+
210
+ ## [0.21.1] - 2026-06-21
211
+
212
+ ### Added
213
+
214
+ - **z.ai Coding Plan quota derivation**: New `zaiQuota.ts` and `zaiQuotaWatcher.ts` modules derive an estimated `QuotaState` for z.ai coding plans from OpenCode assistant turns tagged `providerID ∈ {zai, zai-coding-plan}`. Because z.ai exposes no quota/usage HTTP API (verified against `docs.z.ai/openapi.json`), utilization is computed by accumulating per-turn tokens into 5-hour and 7-day rolling windows and comparing against the published per-tier prompt budgets (Lite 80/400, Pro 400/2000, Max 1600/8000 prompts per 5h/week). Authoritative reset timestamps are extracted from trapped `1308`/`1310`/`1313`/`1309` business error codes when present. Exports include `accumulateZaiUsage()`, `inferZaiQuotaState()`, `parseZaiQuotaError()`, `resolveZaiTier()`, `ZaiQuotaWatcher`, `ZAI_TIER_BUDGETS`, and `ZAI_PROMPT_INVOCATIONS`
215
+ - **`OpenCodeProvider.getZaiQuotaState()`**: The shared OpenCode provider now derives z.ai quota on demand from the on-disk `opencode.db`. `OpenCodeSessionProvider.getQuotaFromSession()` (VS Code wrapper) wires the derived state into the existing session-based quota pipeline so the dashboard, snapshot, and history see z.ai samples automatically
216
+ - **`OpenCodeDatabase.getAssistantMessagesByProviderId()`**: New query method returns assistant rows tagged with the given providerID(s), used by the z.ai accumulator to walk per-turn token records
217
+ - **Runtime quota provider `'zai'`**: `RuntimeQuotaProvider`, `QuotaHistoryRuntimeProvider`, `QuotaState.providerId`, `ProviderQuotaMap`, and the corresponding Zod schemas all accept `'zai'`. `MultiProviderQuotaService` accepts an optional `zaiWatcher` and a new `updateProviderQuota('zai', …)` overload. `QuotaSnapshotProviderId` widens the snapshot store key to `AccountProviderId | 'zai'` so the derived z.ai quota persists across sessions
218
+ - **`getOpenCodeDataDir()` exported**: Now a public helper
219
+
220
+ ### Fixed
221
+
222
+ - **OpenCode data directory resolution**: `getOpenCodeDataDir()` (now exported) probes both the macOS-default `~/Library/Application Support/opencode` and Linux-style `~/.local/share/opencode` candidate paths and returns whichever actually contains `opencode.db`. Previously it returned the platform default unconditionally, which failed on machines where OpenCode writes to the Linux-style path even on macOS
223
+
224
+ ### Limitations
225
+
226
+ - z.ai quota is **estimated, not authoritative**: it is derived only from OpenCode traffic observed on this machine (z.ai exposes no usage API) and compared against provisional per-tier prompt budgets (`ZAI_PROMPT_INVOCATIONS` is a midpoint estimate, not validated). z.ai is **observed-only** — there is no z.ai inference provider and no z.ai account management in this release (`zaiQuotaWatcher.ts`: "z.ai has no full account management in v1"). Auto-tier resolution under-reports early in a weekly cycle; reset times are approximate unless a `1308`/`1310`/`1313`/`1309` error is trapped
227
+
228
+ ## [0.21.0] - 2026-06-21
229
+
230
+ ### Added
231
+
232
+ - **Account Management 2.0 acquisition facade**: New provider-neutral helpers `beginAccountLogin()`, `getAccountLoginStatus()`, `finalizeAccountLogin()`, and `spawnAccountLogin()` let hosts acquire Claude Max and Codex accounts through isolated profile directories before activating them
233
+ - **Provider-neutral account switching**: `listAllAccounts()` and `switchAccount()` expose a single surface over Claude saved accounts and Codex saved profiles. Claude switching now applies canonical profile homes back to the live Claude home, with legacy flat-backup migration handled by `reconcileClaudeAuthState()`
234
+ - **Claude profile primitives**: `getClaudeProfilesDir()`, `getClaudeProfileHome()`, `claudeKeychainSuffix()`, `claudeKeychainService()`, `isClaudeProfileAuthenticated()`, and `readClaudeProfileIdentity()` are exported for hosts that need lower-level profile inspection
235
+ - **Terminal sync helpers**: Opt-in terminal account pointers, shell hook installation/removal, and launcher creation/removal are available through `setTerminalActiveProfile()`, `installShellHook()`, `uninstallShellHook()`, `isShellHookInstalled()`, `writeLauncher()`, and `removeLauncher()`
236
+ - **Quota auto-switch primitives**: `decideAutoSwitch()` and `AutoSwitchController` provide a default-off policy for switching to a healthier saved account after quota crosses a configured threshold
237
+ - **Account Zod schemas**: `sidekick-shared/schemas` and the package root now export `accountProviderIdSchema`, `beginAccountLoginResultSchema`, `accountLoginStatusSchema`, `accountManagerResultSchema`, `accountEntrySchema`, `savedAccountProfileSchema`, and `listAllAccountsResultSchema`
238
+
239
+ ## [0.20.0] - 2026-06-17
240
+
241
+ ### Added
242
+
243
+ - **Actionable session asset extraction**: New Node-only APIs for extracting URLs, filesystem-validated file paths, commands the agent suggested for the user to run, and plan-mode plans from recent Claude Code and Codex sessions for exactly one cwd. `gatherAssetsForCwd()` merges supported agents with recency sorting, dedupe, and per-type caps; `readClaudeAssets()`, `readCodexAssets()`, `claudeSessions()`, `codexSessions()`, `extractUrls()`, `extractFilePaths()`, and `extractCommands()` are exported for lower-level use. These APIs are safe for CLI and VS Code extension-host code, but not for browser/webview bundles
244
+ - **Session asset provenance**: `ExtractedAsset` now includes optional `agent`, `sessionPath`, and `source` metadata while keeping `type`, `text`, `display`, and `timestamp` stable for existing consumers
245
+ - **Test coverage**: Added extractor tests for command parsing, URL cleanup, filesystem-validated paths, message-text file paths, exact-cwd isolation, Codex `CODEX_HOME` discovery, merge caps, and packaging-contract exports
246
+
247
+ Thanks to [@B33pBeeps](https://github.com/B33pBeeps) (Juan Fourie) for contributing this feature in [#17](https://github.com/cesarandreslopez/sidekick-agent-hub/pull/17), adapted from his MIT-licensed [`trawl`](https://github.com/B33pBeeps/trawl) project.
248
+
249
+ ## [0.19.3] - 2026-06-17
250
+
251
+ ### Added
252
+
253
+ - **Assistant turn timeline**: `segmentAssistantTurn()` now emits a required v2 `timeline` array that preserves reasoning, narration, and tool groups in original arrival order while keeping the final answer text excluded. The mirrored Zod schemas and public type exports include the new timeline contract for browser and IPC consumers
254
+
255
+ ## [0.18.1] - 2026-05-08
256
+
257
+ ### Added
258
+
259
+ - **Shared display formatting**: `formatTokenCount()` and `formatDurationMs()` are now public from the root, browser, and `formatting` entrypoints, giving CLI, webview, and downstream consumers a single source of truth for compact token and duration rendering
260
+ - **Raw JSONL tailing (`createJsonlTail()`)**: offset-tracked incremental JSONL reads with optional Zod validation, debounced `fs.watch` plus catch-up polling, and a post-batch callback for aggregation-driven consumers that need to defer expensive work until parsing for a chunk is complete
261
+
262
+ ## [0.18.0] - 2026-05-08
263
+
264
+ ### Added
265
+
266
+ - **Provider-aware quota orchestration**: `MultiProviderQuotaService` coordinates Claude polling, peak-hours enrichment, account labels, transient-failure fallback, and optional Codex quota watcher updates behind one typed `{ claude?, codex? }` event stream
267
+ - **Codex quota watcher**: `CodexQuotaWatcher` discovers the active Codex rollout for a workspace, watches it for live rate-limit updates, persists account-scoped snapshots, and falls back to cached or unavailable states when no live data exists
268
+ - **Account status helper**: `getActiveAccountStatus()` returns a single Claude/Codex account status shape for startup and setup flows
269
+ - **Tool-call extraction helper**: `extractToolCall()` extracts top-level `tool_use` events, complementing the existing `extractToolCalls()` assistant-content-block helper
270
+ - **Cost/model helpers**: `calculateCostWithProvenance()`, `mergeCostSources()`, `shortModelName()`, `getModelDisplayInfo()`, `compareModelIds()`, and `sortModelIds()` provide reusable UI and accounting primitives next to pricing
271
+ - **Phrase categories**: `PHRASE_CATEGORIES` exposes the category structure behind the existing flat `ALL_PHRASES`
272
+
273
+ ### Changed
274
+
275
+ - **Model parsing**: `parseModelId()` now recognizes legacy Claude IDs such as `claude-3-opus-20240229` and `claude-3-5-sonnet-20241022`
276
+
277
+ ## [0.17.7] - 2026-04-28
278
+
279
+ ### Fixed
280
+
281
+ - **Quota snapshot write race**: `writeQuotaSnapshot()` now writes through a per-process unique temp suffix (PID + timestamp + 8 bytes from `crypto.randomBytes`) before atomically renaming to `quota-snapshots.json`, and best-effort removes the temp file if the rename fails. This eliminates fixed-temp collisions and `ENOENT` when multiple Node processes (e.g., the VS Code extension and the CLI) write cached Codex quota snapshots at the same time, and prevents partial writes from leaking orphan `.tmp` files into the config directory
282
+
283
+ ## [0.17.6] - 2026-04-19
284
+
285
+ ### Added
286
+
287
+ - **`fetchPeakHoursStatus()` API**: New top-level export from `sidekick-shared` that fetches Claude's current peak-hours state from the public `promoclock.co/api/status` endpoint (third-party, unaffiliated with Anthropic). Single-shot fetcher — callers own polling. Returns a fully-normalized `PeakHoursState` with a `unavailable: true` fallback on network errors, HTTP non-2xx, or parse failures, so call sites never need try/catch
288
+ - **`PeakHoursState` type**: Exported type covering `status` (`'peak' | 'off_peak' | 'unknown'`), `isPeak`, `sessionLimitSpeed` (`'normal' | 'faster' | 'unknown'`), `label`, `peakHoursDescription`, `nextChange`, `minutesUntilChange`, `note`, `updatedAt`, and `unavailable`. Unexpected upstream values collapse to `'unknown'` rather than widening the union
289
+ - **Test coverage**: `peakHours.test.ts` adds five vitest cases — peak, off-peak, HTTP 500, network error, and unexpected-enum-value handling — mirroring the existing `providerStatus.test.ts` pattern with `vi.stubGlobal('fetch', …)`
290
+
291
+ ## [0.17.5] - 2026-04-18
292
+
293
+ ### Added
294
+
295
+ - **`ensureDefaultAccounts()` API**: New top-level export from `sidekick-shared` that auto-registers the first system Claude Code and Codex credentials as a "Default" saved account when no active account exists for that provider. Idempotent across repeated calls; never overwrites accounts that were manually saved; cleans up orphaned Codex profile directories if `prepareCodexAccount` succeeds but still reports `needsLogin`. Accepts an optional `{ logger }` for diagnostic output — every failure path returns a `'error'` status and is routed through the logger rather than thrown
296
+ - **Account bootstrap types**: `EnsureDefaultAccountsResult`, `EnsureDefaultAccountStatus` (`'registered' | 'skipped' | 'error'`), and `EnsureDefaultAccountsOptions` exported from the package root
297
+ - **Packaging contract coverage**: `packagingContract.test.ts` now asserts `ensureDefaultAccounts` is reachable from the built `dist/index.js`, so the API can't silently drop out of the published artifact
298
+ - **Test coverage**: `ensureDefaultAccounts.test.ts` adds six vitest cases covering happy path, idempotency, Claude-only, Codex-only, respect-existing-accounts, and error-swallowing — using the existing `credentialIO` mock pattern so the suite stays portable across macOS and file-based platforms
299
+
300
+ Thanks to [@B33pBeeps](https://github.com/B33pBeeps) (Juan Fourie) for contributing this feature in [#16](https://github.com/cesarandreslopez/sidekick-agent-hub/pull/16).
301
+
302
+ ## [0.16.1] - 2026-03-27
303
+
304
+ ### Fixed
305
+
306
+ - **Account test portability**: `accounts.test.ts` now mocks credential storage through `credentialIO`, so the shared test suite passes consistently on macOS and file-based platforms
307
+
308
+ ## [0.16.0] - 2026-03-23
309
+
310
+ ### Added
311
+
312
+ - **Zod schemas for session events**: `sessionEventSchema`, `messageUsageSchema`, `sessionMessageSchema`, and `permissionModeSchema` for runtime JSONL validation
313
+ - **Token usage extractor**: Pure function `extractTokenUsage()` normalizes snake_case API usage fields from a single event
314
+ - **Tool call extractor**: Pure function `extractToolCalls()` extracts tool_use content blocks with `toolUseId` from a single event
315
+ - **Model info & pricing module**: `getModelInfo()`, `parseModelId()`, `getModelPricing()`, `calculateCost()`, `calculateCostWithPricing()`, and `formatCost()` — ported from VS Code extension with zero VS Code dependencies
316
+ - **Typed JSONL parser**: Optional `schema` parameter on `JsonlParser` for Zod-validated event parsing; invalid events route to `onError()`
317
+ - **QuotaPoller class**: Reusable polling service with exponential backoff, active/idle interval switching, cached fallback on transient errors, and automatic stop on auth failures
318
+
319
+ ### Changed
320
+
321
+ - **ToolCall type**: Added optional `toolUseId` and `output` fields for tool result correlation
322
+
323
+ ## [0.13.8] - 2026-03-12
324
+
325
+ ### Added
326
+
327
+ - **Structured quota failure metadata**: `QuotaState` unavailable results now optionally include `failureKind`, `httpStatus`, and `retryAfterMs`, and `fetchQuota()` classifies `401`, `429`, `5xx`, other non-OK responses, and transport failures without changing its non-throwing contract
328
+ - **Quota failure presentation helper**: New `describeQuotaFailure()` export maps unavailable quota states to consistent first-party severity, title/message/detail copy, retryability hints, and stable alert keys for CLI and VS Code consumers
329
+
330
+ ## [0.13.7] - 2026-03-11
331
+
332
+ _No shared-specific changes in this release._
333
+
334
+ ## [0.13.6] - 2026-03-11
335
+
336
+ ### Fixed
337
+
338
+ - **OpenCode DB-backed resolution**: OpenCode project discovery now matches DB projects by worktree, sandboxes, and session directory, and synthetic `db-sessions/<projectId>` folders are treated as monitorable paths
339
+ - **OpenCode runtime status**: DB-backed consumers can now distinguish `db_missing` from `sqlite_missing`, `sqlite_blocked`, and `query_failed`, enabling OpenCode-only actionable runtime notices instead of silent fallback to legacy file scanning
340
+
341
+ ## [0.13.5] - 2026-03-10
342
+
343
+ ### Added
344
+
345
+ - **Provider Status module**: `fetchProviderStatus()` for checking Claude API health via `status.claude.com` — returns indicator, affected components, active incidents, with graceful fallback on errors
346
+
347
+ ## [0.13.4] - 2026-03-08
348
+
349
+ _No shared-specific changes in this release._
350
+
351
+ ## [0.13.2] - 2026-03-04
352
+
353
+ ### Added
354
+
355
+ - **Credentials module**: `readClaudeMaxCredentials()` and `readClaudeMaxAccessTokenSync()` for reading Claude Max OAuth credentials from `~/.claude/.credentials.json`
356
+ - **Quota module**: `fetchQuota()` for fetching Claude Max subscription quota (5-hour and 7-day windows)
357
+ - **Vitest config**: Added vitest configuration for shared library tests
358
+
359
+ ## [0.13.1] - 2026-03-04
360
+
361
+ ### Added
362
+
363
+ - **Quota types**: `QuotaWindow` and `QuotaState` types exported for CLI and extension consumption
364
+
365
+ ## [0.13.0] - 2026-03-03
366
+
367
+ ### Changed
368
+
369
+ - **Refactoring**: Removed dead code, cached hot paths, centralized constants across shared modules
370
+
371
+ ## [0.12.10] - 2026-03-01
372
+
373
+ ### Added
374
+
375
+ - **Event aggregation**: `EventAggregator` with frequency tracking, activity heatmaps, and pattern extraction
376
+ - **Analytics engines**: `FrequencyTracker`, `HeatmapTracker`, `PatternExtractor` for session analytics
377
+ - **Snapshot persistence**: `saveSnapshot` / `loadSnapshot` for aggregator state serialization
378
+
379
+ ## [0.12.9] - 2026-02-28
380
+
381
+ ### Added
382
+
383
+ - **Cross-session search**: `searchSessions()` for full-text search across session data
384
+ - **Advanced filter**: `FilterEngine` with substring, fuzzy, regex, and date range modes
385
+ - **Context composer**: `composeContext()` for assembling tasks, decisions, notes, and handoff into a single output
386
+
387
+ ## [0.12.8] - 2026-02-28
388
+
389
+ _No shared-specific changes in this release._
390
+
391
+ ## [0.12.7] - 2026-02-27
392
+
393
+ ### Added
394
+
395
+ - **HTML report generation**: `generateHtmlReport()` produces self-contained HTML session reports with transcript, stats, and tool summaries
396
+ - **Plan extraction**: `PlanExtractor` for cross-provider plan capture (Claude Code, OpenCode, Codex)
397
+ - **Changelog parser**: `parseChangelog()` for reading Keep a Changelog formatted files
398
+
399
+ ## [0.12.6] - 2026-02-26
400
+
401
+ ### Added
402
+
403
+ - **Session dump formatters**: `formatSessionText`, `formatSessionMarkdown`, `formatSessionJson` for exporting session data
404
+ - **Noise classifier**: Event classification for filtering noise from session streams
405
+ - **Event highlighter**: Keyword coloring for session event summaries
406
+ - **Plans reader**: `readPlans`, `readClaudeCodePlanFiles` for plan file discovery
407
+ - **Phrase library**: Shared phrase content moved from CLI to shared library
408
+
409
+ ## [0.12.5] - 2026-02-24
410
+
411
+ _No shared-specific changes in this release._
412
+
413
+ ## [0.12.4] - 2026-02-24
414
+
415
+ _No shared-specific changes in this release._
416
+
417
+ ## [0.12.3] - 2026-02-24
418
+
419
+ ### Added
420
+
421
+ - **Subagent trace parser**: `scanSubagentTraces()` for parsing agent trace files
422
+ - **Session activity detector**: `detectSessionActivity()` for determining session state
423
+ - **Debug log parser**: `parseDebugLog`, `filterByLevel`, `discoverDebugLogs` for Claude Code debug log analysis
424
+
425
+ ## [0.11.0] - 2026-02-19
426
+
427
+ ### Added
428
+
429
+ - **Initial release**: Shared data access library extracted from the VS Code extension
430
+ - Session event types and persistence schemas
431
+ - JSONL, OpenCode, and Codex parsers
432
+ - Claude Code, OpenCode, and Codex session providers
433
+ - Task, decision, note, history, and handoff readers
434
+ - Session path resolution and subagent scanning
435
+ - Watcher framework for live session file monitoring
436
+ - Tool summary formatter
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Cesar Andres Lopez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.