sidekick-shared 0.29.0 → 0.30.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 (142) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +198 -56
  3. package/dist/aggregation/EventAggregator.d.ts +96 -2
  4. package/dist/aggregation/EventAggregator.js +535 -42
  5. package/dist/aggregation/snapshot.d.ts +37 -8
  6. package/dist/aggregation/snapshot.js +31 -9
  7. package/dist/aggregation/types.d.ts +38 -1
  8. package/dist/browser.d.ts +9 -1
  9. package/dist/browser.js +29 -3
  10. package/dist/context/sessionContext.js +8 -0
  11. package/dist/esm/aggregation/EventAggregator.js +535 -42
  12. package/dist/esm/aggregation/snapshot.js +31 -9
  13. package/dist/esm/browser.js +3 -0
  14. package/dist/esm/context/sessionContext.js +8 -0
  15. package/dist/esm/fileContinuity.js +149 -0
  16. package/dist/esm/formatters/toolSummary.js +4 -1
  17. package/dist/esm/index.js +7 -2
  18. package/dist/esm/modelCatalog.js +5 -4
  19. package/dist/esm/modelContext.js +4 -3
  20. package/dist/esm/modelInfo.js +4 -3
  21. package/dist/esm/node.js +3 -1
  22. package/dist/esm/observedContextWindows.js +2 -1
  23. package/dist/esm/ownKey.js +26 -0
  24. package/dist/esm/parsers/codexParser.js +372 -7
  25. package/dist/esm/parsers/openCodeParser.js +2 -1
  26. package/dist/esm/parsers/subagentEndState.js +61 -0
  27. package/dist/esm/parsers/subagentScanner.js +37 -12
  28. package/dist/esm/parsers/subagentTraceParser.js +103 -37
  29. package/dist/esm/pricingCatalog.js +3 -2
  30. package/dist/esm/promptHistory.js +22 -11
  31. package/dist/esm/providers/claudeCode.js +60 -29
  32. package/dist/esm/providers/codex.js +235 -201
  33. package/dist/esm/providers/codexCompressedReader.js +184 -0
  34. package/dist/esm/providers/codexDatabase.js +47 -2
  35. package/dist/esm/providers/codexRollouts.js +20 -2
  36. package/dist/esm/providers/codexSubagents.js +456 -0
  37. package/dist/esm/providers/compressedRollouts.js +330 -0
  38. package/dist/esm/providers/factory.js +4 -1
  39. package/dist/esm/providers/rolloutWalker.js +32 -4
  40. package/dist/esm/schemas/sessionEvent.js +63 -14
  41. package/dist/esm/sessionMonitor.js +7 -1
  42. package/dist/esm/sessionPreviews.js +9 -0
  43. package/dist/esm/sessionPromptHistory.js +5 -3
  44. package/dist/esm/subagentActivity.js +125 -0
  45. package/dist/esm/subagentDisplayState.js +81 -0
  46. package/dist/esm/subagentSpawn.js +363 -0
  47. package/dist/esm/testing/codexSubagentFixtures.js +336 -0
  48. package/dist/esm/toolCallOrigins.js +17 -10
  49. package/dist/esm/toolCategory.js +6 -2
  50. package/dist/esm/transcript.js +8 -0
  51. package/dist/esm/turns/assistantTurn.js +6 -2
  52. package/dist/esm/usage/claudeUsageDedupe.js +68 -0
  53. package/dist/esm/usage/usageEvents.js +4 -1
  54. package/dist/esm/watchers/eventBridge.js +45 -0
  55. package/dist/esm/watchers/factory.js +5 -3
  56. package/dist/esm/watchers/jsonlTail.js +82 -48
  57. package/dist/esm/watchers/jsonlWatcher.js +61 -4
  58. package/dist/esm/watchers/providerReaderWatcher.js +18 -4
  59. package/dist/esm/zaiQuota.js +2 -1
  60. package/dist/fileContinuity.d.ts +95 -0
  61. package/dist/fileContinuity.js +187 -0
  62. package/dist/formatters/toolSummary.js +4 -1
  63. package/dist/index.d.ts +19 -7
  64. package/dist/index.js +48 -10
  65. package/dist/modelCatalog.js +5 -4
  66. package/dist/modelContext.js +4 -3
  67. package/dist/modelInfo.js +4 -3
  68. package/dist/node.d.ts +5 -1
  69. package/dist/node.js +18 -2
  70. package/dist/observedContextWindows.js +2 -1
  71. package/dist/ownKey.d.ts +16 -0
  72. package/dist/ownKey.js +30 -0
  73. package/dist/parsers/codexParser.d.ts +107 -0
  74. package/dist/parsers/codexParser.js +373 -7
  75. package/dist/parsers/openCodeParser.js +2 -1
  76. package/dist/parsers/subagentEndState.d.ts +20 -0
  77. package/dist/parsers/subagentEndState.js +65 -0
  78. package/dist/parsers/subagentScanner.d.ts +19 -6
  79. package/dist/parsers/subagentScanner.js +37 -12
  80. package/dist/parsers/subagentTraceParser.d.ts +33 -5
  81. package/dist/parsers/subagentTraceParser.js +103 -37
  82. package/dist/pricingCatalog.js +3 -2
  83. package/dist/promptHistory.d.ts +13 -3
  84. package/dist/promptHistory.js +22 -11
  85. package/dist/providers/claudeCode.js +59 -28
  86. package/dist/providers/codex.d.ts +35 -5
  87. package/dist/providers/codex.js +232 -198
  88. package/dist/providers/codexCompressedReader.d.ts +58 -0
  89. package/dist/providers/codexCompressedReader.js +221 -0
  90. package/dist/providers/codexDatabase.d.ts +25 -0
  91. package/dist/providers/codexDatabase.js +48 -2
  92. package/dist/providers/codexRollouts.d.ts +16 -2
  93. package/dist/providers/codexRollouts.js +20 -2
  94. package/dist/providers/codexSubagents.d.ts +66 -0
  95. package/dist/providers/codexSubagents.js +495 -0
  96. package/dist/providers/compressedRollouts.d.ts +114 -0
  97. package/dist/providers/compressedRollouts.js +380 -0
  98. package/dist/providers/factory.d.ts +5 -0
  99. package/dist/providers/factory.js +4 -1
  100. package/dist/providers/rolloutWalker.d.ts +15 -2
  101. package/dist/providers/rolloutWalker.js +33 -4
  102. package/dist/providers/types.d.ts +36 -1
  103. package/dist/schemas/index.d.ts +1 -0
  104. package/dist/schemas/sessionEvent.d.ts +51 -10
  105. package/dist/schemas/sessionEvent.js +63 -14
  106. package/dist/sessionMonitor.d.ts +6 -0
  107. package/dist/sessionMonitor.js +7 -1
  108. package/dist/sessionPreviews.js +9 -0
  109. package/dist/sessionPromptHistory.d.ts +5 -0
  110. package/dist/sessionPromptHistory.js +5 -36
  111. package/dist/subagentActivity.d.ts +91 -0
  112. package/dist/subagentActivity.js +132 -0
  113. package/dist/subagentDisplayState.d.ts +81 -0
  114. package/dist/subagentDisplayState.js +86 -0
  115. package/dist/subagentSpawn.d.ts +175 -0
  116. package/dist/subagentSpawn.js +385 -0
  117. package/dist/testing/codexSubagentFixtures.d.ts +48 -0
  118. package/dist/testing/codexSubagentFixtures.js +344 -0
  119. package/dist/toolCallOrigins.d.ts +5 -0
  120. package/dist/toolCallOrigins.js +17 -10
  121. package/dist/toolCategory.js +6 -2
  122. package/dist/transcript.js +8 -0
  123. package/dist/turns/assistantTurn.d.ts +4 -0
  124. package/dist/turns/assistantTurn.js +6 -2
  125. package/dist/types/codex.d.ts +71 -2
  126. package/dist/types/sessionEvent.d.ts +38 -3
  127. package/dist/usage/claudeUsageDedupe.d.ts +31 -0
  128. package/dist/usage/claudeUsageDedupe.js +68 -0
  129. package/dist/usage/usageEvents.d.ts +4 -1
  130. package/dist/usage/usageEvents.js +4 -1
  131. package/dist/watchers/eventBridge.js +45 -0
  132. package/dist/watchers/factory.d.ts +6 -0
  133. package/dist/watchers/factory.js +5 -3
  134. package/dist/watchers/jsonlTail.d.ts +27 -4
  135. package/dist/watchers/jsonlTail.js +84 -48
  136. package/dist/watchers/jsonlWatcher.d.ts +22 -1
  137. package/dist/watchers/jsonlWatcher.js +60 -3
  138. package/dist/watchers/providerReaderWatcher.d.ts +7 -0
  139. package/dist/watchers/providerReaderWatcher.js +18 -4
  140. package/dist/watchers/types.d.ts +12 -0
  141. package/dist/zaiQuota.js +2 -1
  142. package/package.json +1 -1
@@ -49,6 +49,7 @@ const string_decoder_1 = require("string_decoder");
49
49
  const sessionContext_1 = require("../context/sessionContext");
50
50
  const sessionStats_1 = require("../sessionStats");
51
51
  const jsonl_1 = require("../parsers/jsonl");
52
+ const fileContinuity_1 = require("../fileContinuity");
52
53
  const claudeUsageDedupe_1 = require("../usage/claudeUsageDedupe");
53
54
  const diagnostics_1 = require("./diagnostics");
54
55
  const directoryListingCache_1 = require("./directoryListingCache");
@@ -146,7 +147,10 @@ class ClaudeCodeReader {
146
147
  /** Cursor after the last complete line, safe to persist across restarts. */
147
148
  committedPosition = 0;
148
149
  events = [];
149
- _wasTruncated = false;
150
+ /** Why the last `readNew()` started over from offset 0, if it did. */
151
+ _lastReset = null;
152
+ /** Whether the file is still the one the positions refer to. */
153
+ continuity = new fileContinuity_1.FileContinuity();
150
154
  decoder = new string_decoder_1.StringDecoder('utf8');
151
155
  /** Split lines of one response repeat its usage; count each response once. */
152
156
  usageDeduper;
@@ -155,7 +159,8 @@ class ClaudeCodeReader {
155
159
  this.usageDeduper = usageDeduper ?? new claudeUsageDedupe_1.ClaudeUsageDeduper();
156
160
  this.parser = new jsonl_1.JsonlParser({
157
161
  onEvent: (raw) => {
158
- for (const event of (0, sessionEvent_1.extractSessionEvents)(raw)) {
162
+ // Mid-turn task notifications finish background subagents.
163
+ for (const event of (0, sessionEvent_1.extractSessionEvents)(raw, { includeTaskNotifications: true })) {
159
164
  this.events.push(this.usageDeduper.apply((0, claudeUsageDedupe_1.normalizeClaudeUsage)(event)));
160
165
  }
161
166
  },
@@ -166,37 +171,28 @@ class ClaudeCodeReader {
166
171
  }
167
172
  readNew() {
168
173
  this.events = [];
169
- this._wasTruncated = false;
174
+ this._lastReset = null;
175
+ let fd = null;
170
176
  try {
171
- if (!fs.existsSync(this.sessionPath)) {
172
- return [];
173
- }
174
- const stats = fs.statSync(this.sessionPath);
177
+ // Open first and stat the descriptor: the size and identity checked
178
+ // are those of the file actually read, even if the path is replaced.
179
+ fd = fs.openSync(this.sessionPath, 'r');
180
+ const stats = fs.fstatSync(fd);
181
+ const reason = this.continuity.check(fd, stats, this.filePosition, this.committedPosition);
182
+ // Truncated, rewritten, or replaced: what was read no longer describes
183
+ // the file, so start over from its beginning.
184
+ if (reason !== null)
185
+ this.restart(reason);
186
+ this.continuity.adopt(stats);
175
187
  const currentSize = stats.size;
176
- // Handle truncation
177
- if (currentSize < this.filePosition) {
178
- this._wasTruncated = true;
179
- this.filePosition = 0;
180
- this.committedPosition = 0;
181
- this.parser.reset();
182
- this.decoder = new string_decoder_1.StringDecoder('utf8');
183
- this.usageDeduper.reset();
184
- }
185
188
  // No new content
186
189
  if (currentSize <= this.filePosition) {
187
- return [];
190
+ return this.events;
188
191
  }
189
192
  // Read new bytes from last position
190
- const fd = fs.openSync(this.sessionPath, 'r');
191
193
  const bufferSize = currentSize - this.filePosition;
192
194
  const buffer = Buffer.alloc(bufferSize);
193
- let bytesRead = 0;
194
- try {
195
- bytesRead = fs.readSync(fd, buffer, 0, bufferSize, this.filePosition);
196
- }
197
- finally {
198
- fs.closeSync(fd);
199
- }
195
+ const bytesRead = fs.readSync(fd, buffer, 0, bufferSize, this.filePosition);
200
196
  const bytes = buffer.subarray(0, bytesRead);
201
197
  const newline = bytes.lastIndexOf(0x0a);
202
198
  if (newline >= 0)
@@ -206,10 +202,31 @@ class ClaudeCodeReader {
206
202
  this.filePosition += bytesRead;
207
203
  }
208
204
  catch (error) {
209
- console.error(`ClaudeCodeReader: error reading ${this.sessionPath}: ${error}`);
205
+ if (error?.code !== 'ENOENT') {
206
+ console.error(`ClaudeCodeReader: error reading ${this.sessionPath}: ${error}`);
207
+ }
208
+ }
209
+ finally {
210
+ if (fd !== null) {
211
+ try {
212
+ fs.closeSync(fd);
213
+ }
214
+ catch {
215
+ // Ignore close errors.
216
+ }
217
+ }
210
218
  }
211
219
  return this.events;
212
220
  }
221
+ /** Starts over from offset 0 of a file that was truncated, rewritten, or replaced. */
222
+ restart(reason) {
223
+ this._lastReset = { reason, previousPosition: this.committedPosition };
224
+ this.filePosition = 0;
225
+ this.committedPosition = 0;
226
+ this.parser.reset();
227
+ this.decoder = new string_decoder_1.StringDecoder('utf8');
228
+ this.usageDeduper.reset();
229
+ }
213
230
  readAll() {
214
231
  this.reset();
215
232
  return this.readNew();
@@ -220,7 +237,8 @@ class ClaudeCodeReader {
220
237
  this.parser.reset();
221
238
  this.decoder = new string_decoder_1.StringDecoder('utf8');
222
239
  this.usageDeduper.reset();
223
- this._wasTruncated = false;
240
+ this._lastReset = null;
241
+ this.continuity.forget();
224
242
  }
225
243
  exists() {
226
244
  return fs.existsSync(this.sessionPath);
@@ -240,9 +258,22 @@ class ClaudeCodeReader {
240
258
  this.committedPosition = position;
241
259
  this.parser.reset();
242
260
  this.decoder = new string_decoder_1.StringDecoder('utf8');
261
+ // The position comes from elsewhere (a snapshot): adopt the file the next
262
+ // read finds rather than compare it with one read before.
263
+ this.continuity.forget();
243
264
  }
244
265
  wasTruncated() {
245
- return this._wasTruncated;
266
+ return this._lastReset !== null;
267
+ }
268
+ lastReset() {
269
+ return this._lastReset ? { ...this._lastReset } : null;
270
+ }
271
+ /** The usage deduper's recent message ids, so a resumed reader does not recount a split response. */
272
+ getResumeState() {
273
+ return this.usageDeduper.exportState();
274
+ }
275
+ restoreResumeState(state) {
276
+ return this.usageDeduper.importState(state);
246
277
  }
247
278
  }
248
279
  /**
@@ -4,7 +4,9 @@
4
4
  * Implements SessionProviderBase for monitoring Codex CLI sessions.
5
5
  * Codex stores sessions as JSONL rollout files in:
6
6
  * ~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<uuid>.jsonl
7
- * with an optional SQLite index at ~/.codex/state.sqlite.
7
+ * with an optional SQLite index at ~/.codex/state.sqlite. Older rollouts are
8
+ * compressed in place to `….jsonl.zst`; those are read only with the
9
+ * `includeCompressedRollouts` option (see `providers/compressedRollouts`).
8
10
  *
9
11
  * Ported from sidekick-vscode/src/services/providers/CodexSessionProvider.ts
10
12
  */
@@ -15,6 +17,19 @@ import type { SubagentStats, TokenUsage } from '../types/sessionEvent';
15
17
  import type { CodexRateLimits } from '../types/codex';
16
18
  export { findCodexRolloutFile };
17
19
  export type { FindCodexRolloutFileOptions } from './codexRollouts';
20
+ /** Options for {@link CodexProvider}; every field is optional and additive. */
21
+ export interface CodexProviderOptions extends SessionProviderOptions {
22
+ /**
23
+ * Also discover and read compressed rollouts (`rollout-….jsonl.zst`,
24
+ * which Codex writes for older sessions). Listings, session lookups,
25
+ * labels, `createReader()` (a whole-file reader; live tailing stays
26
+ * `.jsonl`-only), search, and subagent stats then include them. Reading
27
+ * needs `zlib.zstdDecompressSync` (Node 22.15+); without it they are
28
+ * skipped and reported once through `onDiagnostic`. Default false, so
29
+ * session and history counts are unchanged.
30
+ */
31
+ includeCompressedRollouts?: boolean;
32
+ }
18
33
  /**
19
34
  * Session provider for Codex CLI.
20
35
  *
@@ -33,7 +48,17 @@ export declare class CodexProvider implements SessionProviderBase {
33
48
  private lastTokenUsageData;
34
49
  /** Dated rollout-directory listings reused across async enumerations. */
35
50
  private readonly directoryCache;
36
- constructor(options?: SessionProviderOptions);
51
+ /** See {@link CodexProviderOptions.includeCompressedRollouts}. */
52
+ private readonly compressedRequested;
53
+ private readonly onDiagnostic;
54
+ constructor(options?: CodexProviderOptions);
55
+ /**
56
+ * Whether compressed rollouts are discovered and read: requested, and the
57
+ * runtime can decompress them (otherwise reported once, on first use).
58
+ */
59
+ private get includeCompressed();
60
+ /** A stored rollout path as it exists now (its `.zst` when compressed and enabled). */
61
+ private resolveRollout;
37
62
  /** Lazy-initialize database connection. */
38
63
  private ensureDb;
39
64
  private ensureDbAsync;
@@ -70,11 +95,16 @@ export declare class CodexProvider implements SessionProviderBase {
70
95
  extractSessionLabel(sessionPath: string): string | null;
71
96
  extractSessionLabelsAsync(sessionPaths: readonly string[]): Promise<Map<string, string | null>>;
72
97
  createReader(sessionPath: string): SessionReader;
73
- scanSubagents(_sessionDir: string, sessionId: string): SubagentStats[];
74
98
  /**
75
- * Builds SubagentStats for a forked session by reading its rollout file.
99
+ * Every subagent below the session, any depth: spawned threads (the
100
+ * database's `threads.source`, else the newest rollouts' session_meta)
101
+ * and, on older schemas, forks (`forked_from_id`). Each carries its
102
+ * rollout (`filePath`), the parent's spawn call (`spawnToolUseId`),
103
+ * `agentNickname`, `agentPath`, `parentAgentId`, and `depth`; tool calls
104
+ * and tokens come from the child's own records, not the parent history a
105
+ * forked child embeds.
76
106
  */
77
- private buildSubagentStats;
107
+ scanSubagents(_sessionDir: string, sessionId: string): SubagentStats[];
78
108
  searchInSession(sessionPath: string, query: string, maxResults: number): SearchHit[];
79
109
  getProjectsBaseDir(): string;
80
110
  getWatchRoots(): string[];