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/dist/modelInfo.js CHANGED
@@ -44,10 +44,26 @@ const modelContext_1 = require("./modelContext");
44
44
  * Sources:
45
45
  * - Anthropic: https://www.anthropic.com/pricing
46
46
  * - OpenAI: https://openai.com/api/pricing/
47
- * Snapshot taken: 2026-04-17. Runtime LiteLLM hydration refreshes this.
47
+ * Snapshot taken: 2026-06-09. Runtime LiteLLM hydration refreshes this.
48
+ *
49
+ * Anthropic keys appear in both dashed (`claude-opus-4-8`, the real model-ID
50
+ * form) and dotted (`claude-opus-4.8`, the LiteLLM catalog form) spellings —
51
+ * prefix matching cannot bridge the two, so both are needed.
48
52
  */
49
53
  const PRICING_TABLE = {
50
54
  // ── Anthropic: Claude ──
55
+ 'claude-fable-5': {
56
+ inputCostPerMillion: 10.0,
57
+ outputCostPerMillion: 50.0,
58
+ cacheWriteCostPerMillion: 12.5,
59
+ cacheReadCostPerMillion: 1.0,
60
+ },
61
+ 'claude-haiku-4-5': {
62
+ inputCostPerMillion: 1.0,
63
+ outputCostPerMillion: 5.0,
64
+ cacheWriteCostPerMillion: 1.25,
65
+ cacheReadCostPerMillion: 0.1,
66
+ },
51
67
  'claude-haiku-4.5': {
52
68
  inputCostPerMillion: 1.0,
53
69
  outputCostPerMillion: 5.0,
@@ -60,12 +76,24 @@ const PRICING_TABLE = {
60
76
  cacheWriteCostPerMillion: 1.0,
61
77
  cacheReadCostPerMillion: 0.08,
62
78
  },
79
+ 'claude-sonnet-4-6': {
80
+ inputCostPerMillion: 3.0,
81
+ outputCostPerMillion: 15.0,
82
+ cacheWriteCostPerMillion: 3.75,
83
+ cacheReadCostPerMillion: 0.3,
84
+ },
63
85
  'claude-sonnet-4.6': {
64
86
  inputCostPerMillion: 3.0,
65
87
  outputCostPerMillion: 15.0,
66
88
  cacheWriteCostPerMillion: 3.75,
67
89
  cacheReadCostPerMillion: 0.3,
68
90
  },
91
+ 'claude-sonnet-4-5': {
92
+ inputCostPerMillion: 3.0,
93
+ outputCostPerMillion: 15.0,
94
+ cacheWriteCostPerMillion: 3.75,
95
+ cacheReadCostPerMillion: 0.3,
96
+ },
69
97
  'claude-sonnet-4.5': {
70
98
  inputCostPerMillion: 3.0,
71
99
  outputCostPerMillion: 15.0,
@@ -78,11 +106,41 @@ const PRICING_TABLE = {
78
106
  cacheWriteCostPerMillion: 3.75,
79
107
  cacheReadCostPerMillion: 0.3,
80
108
  },
109
+ 'claude-opus-4-8': {
110
+ inputCostPerMillion: 5.0,
111
+ outputCostPerMillion: 25.0,
112
+ cacheWriteCostPerMillion: 6.25,
113
+ cacheReadCostPerMillion: 0.5,
114
+ },
115
+ 'claude-opus-4.8': {
116
+ inputCostPerMillion: 5.0,
117
+ outputCostPerMillion: 25.0,
118
+ cacheWriteCostPerMillion: 6.25,
119
+ cacheReadCostPerMillion: 0.5,
120
+ },
121
+ 'claude-opus-4-7': {
122
+ inputCostPerMillion: 5.0,
123
+ outputCostPerMillion: 25.0,
124
+ cacheWriteCostPerMillion: 6.25,
125
+ cacheReadCostPerMillion: 0.5,
126
+ },
127
+ 'claude-opus-4.7': {
128
+ inputCostPerMillion: 5.0,
129
+ outputCostPerMillion: 25.0,
130
+ cacheWriteCostPerMillion: 6.25,
131
+ cacheReadCostPerMillion: 0.5,
132
+ },
133
+ 'claude-opus-4-6': {
134
+ inputCostPerMillion: 5.0,
135
+ outputCostPerMillion: 25.0,
136
+ cacheWriteCostPerMillion: 6.25,
137
+ cacheReadCostPerMillion: 0.5,
138
+ },
81
139
  'claude-opus-4.6': {
82
- inputCostPerMillion: 15.0,
83
- outputCostPerMillion: 75.0,
84
- cacheWriteCostPerMillion: 18.75,
85
- cacheReadCostPerMillion: 1.5,
140
+ inputCostPerMillion: 5.0,
141
+ outputCostPerMillion: 25.0,
142
+ cacheWriteCostPerMillion: 6.25,
143
+ cacheReadCostPerMillion: 0.5,
86
144
  },
87
145
  'claude-opus-4.5': {
88
146
  inputCostPerMillion: 5.0,
@@ -90,6 +148,7 @@ const PRICING_TABLE = {
90
148
  cacheWriteCostPerMillion: 6.25,
91
149
  cacheReadCostPerMillion: 0.5,
92
150
  },
151
+ // Opus 4.0 / 4.1 — pre-4.5 pricing tier
93
152
  'claude-opus-4': {
94
153
  inputCostPerMillion: 15.0,
95
154
  outputCostPerMillion: 75.0,
@@ -199,7 +258,7 @@ function _clearPricingOverrides() {
199
258
  overrideSortedKeys = [];
200
259
  }
201
260
  // ── Model ID Parsing ──
202
- const CLAUDE_RE = /^claude-(haiku|sonnet|opus)-([0-9.]+)/i;
261
+ const CLAUDE_RE = /^claude-(haiku|sonnet|opus|fable)-([0-9.]+)/i;
203
262
  const LEGACY_CLAUDE_RE = /^claude-([0-9]+(?:[-.][0-9]+)?)-(haiku|sonnet|opus)(?:-|$)/i;
204
263
  const GPT_RE = /^gpt-([0-9][0-9.A-Za-z-]*)/i;
205
264
  const O_SERIES_RE = /^o([0-9]+)(-mini|-pro)?/i;
@@ -390,9 +449,10 @@ function shortModelName(modelId) {
390
449
  return modelId;
391
450
  }
392
451
  const CLAUDE_FAMILY_RANK = {
393
- opus: 0,
394
- sonnet: 1,
395
- haiku: 2,
452
+ fable: 0,
453
+ opus: 1,
454
+ sonnet: 2,
455
+ haiku: 3,
396
456
  };
397
457
  function versionRank(version) {
398
458
  if (!version)
@@ -44,6 +44,8 @@ export declare class CodexRolloutParser {
44
44
  private modelContextWindow;
45
45
  private lastRateLimits;
46
46
  private inPlanMode;
47
+ private emittedToolUseIds;
48
+ private patchExpandedToolUseIds;
47
49
  /** Get stored session metadata. */
48
50
  getSessionMeta(): CodexSessionMeta | null;
49
51
  /** Get current model from turn_context. */
@@ -17,6 +17,22 @@ exports.normalizeCodexToolName = normalizeCodexToolName;
17
17
  exports.normalizeCodexToolInput = normalizeCodexToolInput;
18
18
  exports.extractPatchFilePaths = extractPatchFilePaths;
19
19
  const openCodeParser_1 = require("./openCodeParser");
20
+ function normalizeRateLimits(rateLimits) {
21
+ if (!rateLimits?.primary && !rateLimits?.secondary)
22
+ return undefined;
23
+ return {
24
+ primary: rateLimits.primary ? {
25
+ usedPercent: rateLimits.primary.used_percent || 0,
26
+ windowMinutes: rateLimits.primary.window_minutes || 0,
27
+ resetsAt: rateLimits.primary.resets_at || 0,
28
+ } : undefined,
29
+ secondary: rateLimits.secondary ? {
30
+ usedPercent: rateLimits.secondary.used_percent || 0,
31
+ windowMinutes: rateLimits.secondary.window_minutes || 0,
32
+ resetsAt: rateLimits.secondary.resets_at || 0,
33
+ } : undefined,
34
+ };
35
+ }
20
36
  /**
21
37
  * Codex-specific tool name normalization.
22
38
  * Extends the shared normalizeToolName with Codex-specific mappings.
@@ -105,6 +121,8 @@ class CodexRolloutParser {
105
121
  modelContextWindow = null;
106
122
  lastRateLimits = null;
107
123
  inPlanMode = false;
124
+ emittedToolUseIds = new Set();
125
+ patchExpandedToolUseIds = new Map();
108
126
  /** Get stored session metadata. */
109
127
  getSessionMeta() {
110
128
  return this.sessionMeta;
@@ -131,7 +149,7 @@ class CodexRolloutParser {
131
149
  convertLine(line) {
132
150
  switch (line.type) {
133
151
  case 'session_meta':
134
- return this.handleSessionMeta(line.payload);
152
+ return this.handleSessionMeta(line.timestamp, line.payload);
135
153
  case 'response_item':
136
154
  return this.handleResponseItem(line.timestamp, line.payload);
137
155
  case 'compacted':
@@ -154,11 +172,25 @@ class CodexRolloutParser {
154
172
  this.modelContextWindow = null;
155
173
  this.lastRateLimits = null;
156
174
  this.inPlanMode = false;
175
+ this.emittedToolUseIds.clear();
176
+ this.patchExpandedToolUseIds.clear();
157
177
  }
158
178
  // --- Handlers ---
159
- handleSessionMeta(payload) {
179
+ handleSessionMeta(timestamp, payload) {
160
180
  this.sessionMeta = payload;
161
- return [];
181
+ const text = payload.base_instructions?.text?.trim();
182
+ if (!text)
183
+ return [];
184
+ return [{
185
+ type: 'system',
186
+ message: {
187
+ role: 'system',
188
+ id: `${payload.id}:base-instructions`,
189
+ sourceLabel: 'base instructions',
190
+ content: [{ type: 'text', text }],
191
+ },
192
+ timestamp,
193
+ }];
162
194
  }
163
195
  handleResponseItem(timestamp, payload) {
164
196
  // payload IS the item directly (no .item wrapper)
@@ -210,6 +242,18 @@ class CodexRolloutParser {
210
242
  timestamp,
211
243
  }];
212
244
  }
245
+ if (item.role === 'developer' || item.role === 'system') {
246
+ return [{
247
+ type: 'system',
248
+ message: {
249
+ role: item.role,
250
+ id: item.id,
251
+ sourceLabel: item.role,
252
+ content: [{ type: 'text', text }],
253
+ },
254
+ timestamp,
255
+ }];
256
+ }
213
257
  return [];
214
258
  }
215
259
  handleReasoning(timestamp, item) {
@@ -239,6 +283,7 @@ class CodexRolloutParser {
239
283
  }
240
284
  const canonicalName = normalizeCodexToolName(item.name);
241
285
  const normalizedInput = normalizeCodexToolInput(canonicalName, item.name, parsedArgs);
286
+ this.emittedToolUseIds.add(item.call_id);
242
287
  return [{
243
288
  type: 'assistant',
244
289
  message: {
@@ -274,6 +319,7 @@ class CodexRolloutParser {
274
319
  }
275
320
  handleLocalShellCall(timestamp, item) {
276
321
  const command = item.action?.command?.join(' ') || '';
322
+ this.emittedToolUseIds.add(item.call_id);
277
323
  return [{
278
324
  type: 'assistant',
279
325
  message: {
@@ -299,6 +345,11 @@ class CodexRolloutParser {
299
345
  const filePaths = extractPatchFilePaths(item.input);
300
346
  if (filePaths.length === 0)
301
347
  return [];
348
+ const toolUseIds = filePaths.map(fp => `${item.call_id}-${fp}`);
349
+ this.patchExpandedToolUseIds.set(item.call_id, toolUseIds);
350
+ for (const id of toolUseIds) {
351
+ this.emittedToolUseIds.add(id);
352
+ }
302
353
  return filePaths.map(fp => ({
303
354
  type: 'assistant',
304
355
  message: {
@@ -328,6 +379,7 @@ class CodexRolloutParser {
328
379
  }
329
380
  const canonicalName = normalizeCodexToolName(item.name);
330
381
  const normalizedInput = normalizeCodexToolInput(canonicalName, item.name, parsedInput);
382
+ this.emittedToolUseIds.add(item.call_id);
331
383
  return [{
332
384
  type: 'assistant',
333
385
  message: {
@@ -357,21 +409,23 @@ class CodexRolloutParser {
357
409
  catch {
358
410
  isError = parsePlainTextFailure(item.output);
359
411
  }
360
- return [{
361
- type: 'user',
362
- message: {
363
- role: 'user',
364
- id: `${item.call_id}:result`,
365
- content: [{
366
- type: 'tool_result',
367
- tool_use_id: item.call_id,
368
- content: item.output,
369
- is_error: isError,
370
- duration,
371
- }],
372
- },
373
- timestamp,
374
- }];
412
+ const toolUseIds = this.patchExpandedToolUseIds.get(item.call_id) ?? [item.call_id];
413
+ this.patchExpandedToolUseIds.delete(item.call_id);
414
+ return toolUseIds.map(toolUseId => ({
415
+ type: 'user',
416
+ message: {
417
+ role: 'user',
418
+ id: `${toolUseId}:result`,
419
+ content: [{
420
+ type: 'tool_result',
421
+ tool_use_id: toolUseId,
422
+ content: item.output,
423
+ is_error: isError,
424
+ duration,
425
+ }],
426
+ },
427
+ timestamp,
428
+ }));
375
429
  }
376
430
  handleCompacted(timestamp, payload) {
377
431
  return [{
@@ -407,7 +461,7 @@ class CodexRolloutParser {
407
461
  }
408
462
  // Usage data is nested under info.last_token_usage (info can be null)
409
463
  const usage = e.info?.last_token_usage || e.info?.total_token_usage;
410
- return this.handleTokenCount(timestamp, usage ?? null);
464
+ return this.handleTokenCount(timestamp, usage ?? null, e.rate_limits);
411
465
  }
412
466
  // agent_message and user_message are suppressed — they duplicate
413
467
  // response_item/message events which carry richer metadata (id, content parts, role).
@@ -427,25 +481,28 @@ class CodexRolloutParser {
427
481
  this.pendingExecCommands.delete(e.call_id);
428
482
  const command = pending?.command?.join(' ') || '';
429
483
  const events = [];
430
- events.push({
431
- type: 'assistant',
432
- message: {
433
- role: 'assistant',
434
- id: `exec-${e.call_id}`,
435
- model: this.currentModel || undefined,
436
- content: [{
437
- type: 'tool_use',
438
- id: e.call_id,
439
- name: 'Bash',
440
- input: {
441
- command,
442
- workdir: pending?.workdir,
443
- _sidekickRawToolName: 'exec_command',
444
- },
445
- }],
446
- },
447
- timestamp: pending?.timestamp || timestamp,
448
- });
484
+ if (!this.emittedToolUseIds.has(e.call_id)) {
485
+ this.emittedToolUseIds.add(e.call_id);
486
+ events.push({
487
+ type: 'assistant',
488
+ message: {
489
+ role: 'assistant',
490
+ id: `exec-${e.call_id}`,
491
+ model: this.currentModel || undefined,
492
+ content: [{
493
+ type: 'tool_use',
494
+ id: e.call_id,
495
+ name: 'Bash',
496
+ input: {
497
+ command,
498
+ workdir: pending?.workdir,
499
+ _sidekickRawToolName: 'exec_command',
500
+ },
501
+ }],
502
+ },
503
+ timestamp: pending?.timestamp || timestamp,
504
+ });
505
+ }
449
506
  const output = [e.stdout, e.stderr].filter(Boolean).join('\n') || '';
450
507
  events.push({
451
508
  type: 'user',
@@ -481,24 +538,28 @@ class CodexRolloutParser {
481
538
  this.pendingMcpToolCalls.delete(e.call_id);
482
539
  const toolName = pendingMcp?.tool_name || 'McpTool';
483
540
  const events = [];
484
- events.push({
485
- type: 'assistant',
486
- message: {
487
- role: 'assistant',
488
- id: `mcp-${e.call_id}`,
489
- model: this.currentModel || undefined,
490
- content: [{
491
- type: 'tool_use',
492
- id: e.call_id,
493
- name: normalizeCodexToolName(toolName),
494
- input: {
495
- ...(pendingMcp?.arguments || {}),
496
- _sidekickRawToolName: toolName,
497
- },
498
- }],
499
- },
500
- timestamp: pendingMcp?.timestamp || timestamp,
501
- });
541
+ if (!this.emittedToolUseIds.has(e.call_id)) {
542
+ this.emittedToolUseIds.add(e.call_id);
543
+ events.push({
544
+ type: 'assistant',
545
+ message: {
546
+ role: 'assistant',
547
+ id: `mcp-${e.call_id}`,
548
+ model: this.currentModel || undefined,
549
+ content: [{
550
+ type: 'tool_use',
551
+ id: e.call_id,
552
+ name: normalizeCodexToolName(toolName),
553
+ input: {
554
+ ...(pendingMcp?.arguments || {}),
555
+ _sidekickRawToolName: toolName,
556
+ _sidekickMcpServerName: pendingMcp?.server_name,
557
+ },
558
+ }],
559
+ },
560
+ timestamp: pendingMcp?.timestamp || timestamp,
561
+ });
562
+ }
502
563
  events.push({
503
564
  type: 'user',
504
565
  message: {
@@ -626,27 +687,32 @@ class CodexRolloutParser {
626
687
  return [];
627
688
  }
628
689
  }
629
- handleTokenCount(timestamp, usage) {
630
- if (!usage)
690
+ handleTokenCount(timestamp, usage, rateLimits) {
691
+ const normalizedRateLimits = normalizeRateLimits(rateLimits);
692
+ if (!usage && !normalizedRateLimits)
631
693
  return [];
632
- const mappedUsage = {
694
+ const mappedUsage = usage ? {
633
695
  input_tokens: usage.input_tokens || 0,
634
696
  output_tokens: usage.output_tokens || 0,
635
697
  cache_read_input_tokens: usage.cached_input_tokens || 0,
636
698
  cache_creation_input_tokens: 0,
637
699
  reasoning_tokens: usage.reasoning_output_tokens || 0,
638
- };
639
- this.lastTokenUsage = usage;
700
+ } : undefined;
701
+ if (usage) {
702
+ this.lastTokenUsage = usage;
703
+ }
640
704
  return [{
641
- type: 'assistant',
705
+ type: 'system',
642
706
  message: {
643
- role: 'assistant',
707
+ role: 'system',
644
708
  id: `token-count-${timestamp}`,
709
+ sourceLabel: 'token count',
645
710
  model: this.currentModel || undefined,
646
711
  usage: mappedUsage,
647
712
  content: [],
648
713
  },
649
714
  timestamp,
715
+ rateLimits: normalizedRateLimits,
650
716
  }];
651
717
  }
652
718
  }
@@ -7,6 +7,7 @@
7
7
  *
8
8
  * Ported from sidekick-vscode/src/services/providers/ClaudeCodeSessionProvider.ts
9
9
  */
10
+ import type { ReadSessionContextSnapshotOptions, SessionContextSnapshot } from '../context/sessionContext';
10
11
  import type { SubagentStats, TokenUsage } from '../types/sessionEvent';
11
12
  import type { SessionProviderBase, SessionReader, SessionFileStats, SearchHit, ProjectFolderInfo, ProviderId } from './types';
12
13
  /**
@@ -38,6 +39,7 @@ export declare class ClaudeCodeProvider implements SessionProviderBase {
38
39
  searchInSession(sessionPath: string, query: string, maxResults: number): SearchHit[];
39
40
  getProjectsBaseDir(): string;
40
41
  readSessionStats(sessionPath: string): SessionFileStats;
42
+ readSessionContextSnapshot(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
41
43
  getContextWindowLimit(modelId?: string): number;
42
44
  /** Set a runtime-reported context window limit (overrides static map). */
43
45
  setDynamicContextWindowLimit(limit: number): void;
@@ -46,6 +46,7 @@ exports.ClaudeCodeProvider = void 0;
46
46
  const fs = __importStar(require("fs"));
47
47
  const os = __importStar(require("os"));
48
48
  const path = __importStar(require("path"));
49
+ const sessionContext_1 = require("../context/sessionContext");
49
50
  const jsonl_1 = require("../parsers/jsonl");
50
51
  const sessionPathResolver_1 = require("../parsers/sessionPathResolver");
51
52
  const subagentScanner_1 = require("../parsers/subagentScanner");
@@ -408,6 +409,9 @@ class ClaudeCodeProvider {
408
409
  reportedCost,
409
410
  };
410
411
  }
412
+ readSessionContextSnapshot(sessionPath, options = {}) {
413
+ return (0, sessionContext_1.readSessionContextSnapshot)(this, sessionPath, options);
414
+ }
411
415
  // --- Optional methods ---
412
416
  getContextWindowLimit(modelId) {
413
417
  if (this.dynamicContextWindowLimit)
@@ -8,6 +8,7 @@
8
8
  *
9
9
  * Ported from sidekick-vscode/src/services/providers/CodexSessionProvider.ts
10
10
  */
11
+ import type { ReadSessionContextSnapshotOptions, SessionContextSnapshot } from '../context/sessionContext';
11
12
  import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileStats, ProviderId } from './types';
12
13
  import type { SubagentStats, TokenUsage } from '../types/sessionEvent';
13
14
  import type { CodexRateLimits } from '../types/codex';
@@ -48,6 +49,7 @@ export declare class CodexProvider implements SessionProviderBase {
48
49
  searchInSession(sessionPath: string, query: string, maxResults: number): SearchHit[];
49
50
  getProjectsBaseDir(): string;
50
51
  readSessionStats(sessionPath: string): SessionFileStats;
52
+ readSessionContextSnapshot(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
51
53
  getContextWindowLimit(modelId?: string): number;
52
54
  getCurrentUsageSnapshot(_sessionPath: string): TokenUsage | null;
53
55
  computeContextSize(usage: TokenUsage): number;