@herdctl/core 5.6.0 → 5.7.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 (54) hide show
  1. package/dist/config/__tests__/merge.test.js +1 -1
  2. package/dist/config/__tests__/merge.test.js.map +1 -1
  3. package/dist/config/schema.d.ts +10 -2
  4. package/dist/config/schema.d.ts.map +1 -1
  5. package/dist/config/schema.js +6 -2
  6. package/dist/config/schema.js.map +1 -1
  7. package/dist/scheduler/schedule-runner.d.ts.map +1 -1
  8. package/dist/scheduler/schedule-runner.js +6 -5
  9. package/dist/scheduler/schedule-runner.js.map +1 -1
  10. package/dist/state/__tests__/jsonl-parser.test.d.ts +5 -0
  11. package/dist/state/__tests__/jsonl-parser.test.d.ts.map +1 -0
  12. package/dist/state/__tests__/jsonl-parser.test.js +332 -0
  13. package/dist/state/__tests__/jsonl-parser.test.js.map +1 -0
  14. package/dist/state/__tests__/session-attribution.test.d.ts +2 -0
  15. package/dist/state/__tests__/session-attribution.test.d.ts.map +1 -0
  16. package/dist/state/__tests__/session-attribution.test.js +567 -0
  17. package/dist/state/__tests__/session-attribution.test.js.map +1 -0
  18. package/dist/state/__tests__/session-discovery.test.d.ts +2 -0
  19. package/dist/state/__tests__/session-discovery.test.d.ts.map +1 -0
  20. package/dist/state/__tests__/session-discovery.test.js +953 -0
  21. package/dist/state/__tests__/session-discovery.test.js.map +1 -0
  22. package/dist/state/__tests__/session-metadata.test.d.ts +2 -0
  23. package/dist/state/__tests__/session-metadata.test.d.ts.map +1 -0
  24. package/dist/state/__tests__/session-metadata.test.js +474 -0
  25. package/dist/state/__tests__/session-metadata.test.js.map +1 -0
  26. package/dist/state/__tests__/tool-parsing.test.d.ts +5 -0
  27. package/dist/state/__tests__/tool-parsing.test.d.ts.map +1 -0
  28. package/dist/state/__tests__/tool-parsing.test.js +315 -0
  29. package/dist/state/__tests__/tool-parsing.test.js.map +1 -0
  30. package/dist/state/index.d.ts +5 -0
  31. package/dist/state/index.d.ts.map +1 -1
  32. package/dist/state/index.js +10 -0
  33. package/dist/state/index.js.map +1 -1
  34. package/dist/state/jsonl-parser.d.ts +126 -0
  35. package/dist/state/jsonl-parser.d.ts.map +1 -0
  36. package/dist/state/jsonl-parser.js +482 -0
  37. package/dist/state/jsonl-parser.js.map +1 -0
  38. package/dist/state/session-attribution.d.ts +35 -0
  39. package/dist/state/session-attribution.d.ts.map +1 -0
  40. package/dist/state/session-attribution.js +179 -0
  41. package/dist/state/session-attribution.js.map +1 -0
  42. package/dist/state/session-discovery.d.ts +198 -0
  43. package/dist/state/session-discovery.d.ts.map +1 -0
  44. package/dist/state/session-discovery.js +555 -0
  45. package/dist/state/session-discovery.js.map +1 -0
  46. package/dist/state/session-metadata.d.ts +240 -0
  47. package/dist/state/session-metadata.d.ts.map +1 -0
  48. package/dist/state/session-metadata.js +377 -0
  49. package/dist/state/session-metadata.js.map +1 -0
  50. package/dist/state/tool-parsing.d.ts +88 -0
  51. package/dist/state/tool-parsing.d.ts.map +1 -0
  52. package/dist/state/tool-parsing.js +199 -0
  53. package/dist/state/tool-parsing.js.map +1 -0
  54. package/package.json +1 -1
@@ -0,0 +1,555 @@
1
+ /**
2
+ * Session Discovery Service
3
+ *
4
+ * Orchestrates session enumeration by tying together JSONL parsing,
5
+ * session attribution, and CLI session path utilities. Provides cached
6
+ * discovery of Claude Code sessions from the filesystem.
7
+ */
8
+ import { readdir, stat } from "node:fs/promises";
9
+ import os from "node:os";
10
+ import path from "node:path";
11
+ import { encodePathForCli, getCliSessionFile } from "../runner/runtime/cli-session-path.js";
12
+ import { createLogger } from "../utils/logger.js";
13
+ import { extractFirstMessagePreview, extractLastSummary, extractSessionMetadata, extractSessionUsage, isSidechainSession, parseSessionMessages, } from "./jsonl-parser.js";
14
+ import { buildAttributionIndex, } from "./session-attribution.js";
15
+ import { SessionMetadataStore } from "./session-metadata.js";
16
+ // =============================================================================
17
+ // Logger
18
+ // =============================================================================
19
+ const logger = createLogger("SessionDiscoveryService");
20
+ // =============================================================================
21
+ // Helper Functions
22
+ // =============================================================================
23
+ /**
24
+ * Decode an encoded path back to a display path.
25
+ *
26
+ * The encoded path (e.g., "-Users-ed-Code-herdctl") is decoded by:
27
+ * - Replacing leading "-" with "/" (Unix)
28
+ * - Replacing remaining "-" with "/"
29
+ *
30
+ * This is lossy but good enough for display purposes.
31
+ */
32
+ function decodePathForDisplay(encodedPath) {
33
+ // Handle Unix paths: leading "-" becomes "/"
34
+ if (encodedPath.startsWith("-")) {
35
+ return "/" + encodedPath.slice(1).replace(/-/g, "/");
36
+ }
37
+ // Handle Windows paths: "C:-Users-..." becomes "C:/Users/..."
38
+ // Check for drive letter pattern
39
+ if (/^[A-Za-z]:-/.test(encodedPath)) {
40
+ return encodedPath[0] + ":" + encodedPath.slice(2).replace(/-/g, "/");
41
+ }
42
+ // Fallback: just replace all hyphens
43
+ return encodedPath.replace(/-/g, "/");
44
+ }
45
+ /**
46
+ * Check if a path is a temp directory that should be filtered out
47
+ */
48
+ function isTempDirectory(decodedPath) {
49
+ const tmpDir = os.tmpdir();
50
+ const tempPatterns = ["/tmp/", "/private/tmp/", "/var/folders/", tmpDir];
51
+ for (const pattern of tempPatterns) {
52
+ if (decodedPath.startsWith(pattern)) {
53
+ return true;
54
+ }
55
+ }
56
+ return false;
57
+ }
58
+ // =============================================================================
59
+ // SessionDiscoveryService
60
+ // =============================================================================
61
+ /**
62
+ * Service for discovering and enumerating Claude Code sessions.
63
+ *
64
+ * Provides cached access to session files, with attribution from job metadata
65
+ * and platform session files, plus custom names from the session metadata store.
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const discovery = new SessionDiscoveryService({
70
+ * stateDir: '/path/to/.herdctl',
71
+ * });
72
+ *
73
+ * // Get sessions for a specific agent
74
+ * const sessions = await discovery.getAgentSessions('my-agent', '/path/to/workspace', false);
75
+ *
76
+ * // Get all sessions grouped by directory
77
+ * const groups = await discovery.getAllSessions([
78
+ * { name: 'agent-1', workingDirectory: '/path/to/project', dockerEnabled: false }
79
+ * ]);
80
+ * ```
81
+ */
82
+ export class SessionDiscoveryService {
83
+ claudeHomePath;
84
+ stateDir;
85
+ cacheTtlMs;
86
+ attributionIndex = null;
87
+ attributionFetchedAt = 0;
88
+ directoryCache = new Map();
89
+ metadataCache = new Map();
90
+ sessionMetadataStore;
91
+ /**
92
+ * Create a new SessionDiscoveryService
93
+ *
94
+ * @param options - Configuration options
95
+ */
96
+ constructor(options) {
97
+ this.claudeHomePath = options.claudeHomePath ?? path.join(os.homedir(), ".claude");
98
+ this.stateDir = options.stateDir;
99
+ this.cacheTtlMs = options.cacheTtlMs ?? 30_000;
100
+ this.sessionMetadataStore = new SessionMetadataStore(options.stateDir);
101
+ }
102
+ /**
103
+ * Check if the attribution index cache is valid
104
+ */
105
+ isAttributionCacheValid() {
106
+ return (this.attributionIndex !== null && Date.now() - this.attributionFetchedAt < this.cacheTtlMs);
107
+ }
108
+ /**
109
+ * Check if a directory cache entry is valid
110
+ */
111
+ isDirectoryCacheValid(entry) {
112
+ return entry !== undefined && Date.now() - entry.fetchedAt < this.cacheTtlMs;
113
+ }
114
+ /**
115
+ * Get or refresh the attribution index
116
+ */
117
+ async getAttributionIndex() {
118
+ if (this.isAttributionCacheValid()) {
119
+ return this.attributionIndex;
120
+ }
121
+ logger.debug("Building attribution index");
122
+ this.attributionIndex = await buildAttributionIndex(this.stateDir);
123
+ this.attributionFetchedAt = Date.now();
124
+ logger.debug(`Attribution index built with ${this.attributionIndex.size} entries`);
125
+ return this.attributionIndex;
126
+ }
127
+ /**
128
+ * List session files in a directory with their modification times
129
+ */
130
+ async listSessionFiles(sessionDir) {
131
+ // Check cache first
132
+ const cached = this.directoryCache.get(sessionDir);
133
+ if (this.isDirectoryCacheValid(cached)) {
134
+ return cached.sessions;
135
+ }
136
+ // Read directory
137
+ let fileNames;
138
+ try {
139
+ fileNames = await readdir(sessionDir);
140
+ }
141
+ catch (error) {
142
+ if (error.code === "ENOENT") {
143
+ logger.debug(`Session directory does not exist: ${sessionDir}`);
144
+ return [];
145
+ }
146
+ logger.warn(`Failed to read session directory: ${sessionDir}: ${error.message}`);
147
+ return [];
148
+ }
149
+ // Filter to .jsonl files and get stats
150
+ const jsonlFiles = fileNames.filter((name) => name.endsWith(".jsonl"));
151
+ const sessions = [];
152
+ for (const fileName of jsonlFiles) {
153
+ const filePath = path.join(sessionDir, fileName);
154
+ try {
155
+ const stats = await stat(filePath);
156
+ sessions.push({
157
+ sessionId: fileName.replace(/\.jsonl$/, ""),
158
+ mtime: stats.mtime,
159
+ });
160
+ }
161
+ catch (error) {
162
+ // File may have been deleted between readdir and stat
163
+ logger.debug(`Failed to stat session file: ${filePath}: ${error.message}`);
164
+ }
165
+ }
166
+ // Sort by mtime descending (newest first)
167
+ sessions.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
168
+ // Cache the result
169
+ this.directoryCache.set(sessionDir, {
170
+ sessions,
171
+ fetchedAt: Date.now(),
172
+ });
173
+ return sessions;
174
+ }
175
+ /**
176
+ * Resolve the auto-generated name for a session.
177
+ *
178
+ * Checks if the cached autoName is still valid (based on file mtime).
179
+ * If not, extracts a new name from the JSONL summary field.
180
+ *
181
+ * @param agentName - The agent's qualified name (use "adhoc" for unattributed sessions)
182
+ * @param sessionId - The session ID
183
+ * @param fileMtime - ISO 8601 timestamp of the session file's modification time
184
+ * @param workingDirectory - The session's working directory
185
+ * @returns Object with autoName and whether an update is needed
186
+ */
187
+ async resolveAutoName(agentName, sessionId, fileMtime, workingDirectory) {
188
+ // Check cache
189
+ const cached = await this.sessionMetadataStore.getAutoName(agentName, sessionId);
190
+ if (cached?.autoNameMtime && cached.autoNameMtime >= fileMtime) {
191
+ // Cache is valid
192
+ return { autoName: cached.autoName, needsUpdate: false };
193
+ }
194
+ // Need to extract from JSONL
195
+ const filePath = getCliSessionFile(workingDirectory, sessionId);
196
+ const summary = await extractLastSummary(filePath);
197
+ if (summary) {
198
+ return { autoName: summary, needsUpdate: true };
199
+ }
200
+ return { autoName: undefined, needsUpdate: false };
201
+ }
202
+ /**
203
+ * Resolve the preview (first user message text) for a session, using cache when valid.
204
+ *
205
+ * @param agentName - The agent's qualified name (or "adhoc" for unattributed)
206
+ * @param sessionId - The session ID
207
+ * @param fileMtime - ISO 8601 timestamp of the session file's modification time
208
+ * @param workingDirectory - The session's working directory
209
+ * @returns Object with preview and whether an update is needed
210
+ */
211
+ async resolvePreview(agentName, sessionId, fileMtime, workingDirectory) {
212
+ // Check cache
213
+ const cached = await this.sessionMetadataStore.getPreview(agentName, sessionId);
214
+ if (cached?.previewMtime && cached.previewMtime >= fileMtime) {
215
+ // Cache is valid
216
+ return { preview: cached.preview, needsUpdate: false };
217
+ }
218
+ // Need to extract from JSONL
219
+ const filePath = getCliSessionFile(workingDirectory, sessionId);
220
+ const preview = await extractFirstMessagePreview(filePath);
221
+ if (preview) {
222
+ return { preview, needsUpdate: true };
223
+ }
224
+ return { preview: undefined, needsUpdate: false };
225
+ }
226
+ /**
227
+ * Get sessions for a specific agent.
228
+ *
229
+ * Returns sessions from the agent's working directory, attributed and
230
+ * enriched with custom names from the metadata store.
231
+ *
232
+ * @param agentName - The agent's qualified name
233
+ * @param workingDirectory - The agent's working directory
234
+ * @param dockerEnabled - Whether Docker is enabled for the agent (affects resumability)
235
+ * @param options - Optional settings (limit for top-N optimization)
236
+ * @returns Array of discovered sessions sorted by mtime descending
237
+ */
238
+ async getAgentSessions(agentName, workingDirectory, dockerEnabled, options) {
239
+ const limit = options?.limit;
240
+ const encodedPath = encodePathForCli(workingDirectory);
241
+ const sessionDir = path.join(this.claudeHomePath, "projects", encodedPath);
242
+ logger.debug(`Getting sessions for agent ${agentName}`, { sessionDir });
243
+ // Get session files (already sorted by mtime descending)
244
+ const sessionFiles = await this.listSessionFiles(sessionDir);
245
+ if (sessionFiles.length === 0) {
246
+ return [];
247
+ }
248
+ // Only enrich the top N sessions when limit is set
249
+ const filesToEnrich = limit !== undefined ? sessionFiles.slice(0, limit) : sessionFiles;
250
+ // Get attribution index
251
+ const attributionIndex = await this.getAttributionIndex();
252
+ // Build discovered sessions and collect cache updates
253
+ const sessions = [];
254
+ const autoNameUpdates = [];
255
+ const previewUpdates = [];
256
+ for (const { sessionId, mtime } of filesToEnrich) {
257
+ // Filter out sidechain (sub-agent) sessions. Claude Code marks sessions
258
+ // as sidechain when they're Task tool sub-agents or when --resume is used.
259
+ // These are mostly prompt-cache warmup sessions ("Warmup" + single response)
260
+ // that clutter the UI with no useful content. We check only the first JSONL
261
+ // line so this is O(1) per file.
262
+ const filePath = getCliSessionFile(workingDirectory, sessionId);
263
+ if (await isSidechainSession(filePath)) {
264
+ continue;
265
+ }
266
+ const attribution = attributionIndex.getAttribute(sessionId);
267
+ // Only show sessions that are attributed to this specific agent.
268
+ // When multiple agents share a working directory, this prevents the same
269
+ // native CLI sessions from appearing under every agent. Unattributed sessions
270
+ // are still visible in the global recent sessions list and All Chats view.
271
+ if (attribution.agentName !== agentName) {
272
+ continue;
273
+ }
274
+ const customName = await this.sessionMetadataStore.getCustomName(agentName, sessionId);
275
+ const mtimeStr = mtime.toISOString();
276
+ // Resolve autoName with caching
277
+ const { autoName, needsUpdate } = await this.resolveAutoName(agentName, sessionId, mtimeStr, workingDirectory);
278
+ if (needsUpdate && autoName) {
279
+ autoNameUpdates.push({ sessionId, autoName, mtime: mtimeStr });
280
+ }
281
+ // Resolve preview with caching
282
+ const { preview, needsUpdate: previewNeedsUpdate } = await this.resolvePreview(agentName, sessionId, mtimeStr, workingDirectory);
283
+ if (previewNeedsUpdate && preview) {
284
+ previewUpdates.push({ sessionId, preview, mtime: mtimeStr });
285
+ }
286
+ sessions.push({
287
+ sessionId,
288
+ workingDirectory,
289
+ mtime: mtimeStr,
290
+ origin: attribution.origin,
291
+ agentName: attribution.agentName ?? agentName,
292
+ resumable: !dockerEnabled,
293
+ customName,
294
+ autoName,
295
+ preview,
296
+ });
297
+ }
298
+ // Batch write any cache updates
299
+ if (autoNameUpdates.length > 0) {
300
+ await this.sessionMetadataStore.batchSetAutoNames(agentName, autoNameUpdates);
301
+ }
302
+ if (previewUpdates.length > 0) {
303
+ await this.sessionMetadataStore.batchSetPreviews(agentName, previewUpdates);
304
+ }
305
+ return sessions;
306
+ }
307
+ /**
308
+ * Get all sessions grouped by working directory.
309
+ *
310
+ * Scans the Claude projects directory and groups sessions by their
311
+ * working directories. Filters out temp directories and enriches
312
+ * sessions with attribution and custom names.
313
+ *
314
+ * When a limit is provided, only the most recent `limit` sessions
315
+ * (by mtime) are enriched with names, avoiding expensive JSONL parsing
316
+ * for sessions that won't be returned.
317
+ *
318
+ * @param agents - Array of known agents for matching sessions
319
+ * @param options - Optional settings (limit for top-N optimization)
320
+ * @returns Array of directory groups sorted by most recent session
321
+ */
322
+ async getAllSessions(agents, options) {
323
+ const limit = options?.limit;
324
+ // Build agent lookup by encoded path
325
+ const agentLookup = new Map();
326
+ for (const agent of agents) {
327
+ const encodedPath = encodePathForCli(agent.workingDirectory);
328
+ agentLookup.set(encodedPath, {
329
+ agentName: agent.name,
330
+ dockerEnabled: agent.dockerEnabled,
331
+ });
332
+ }
333
+ // Scan projects directory
334
+ const projectsDir = path.join(this.claudeHomePath, "projects");
335
+ let encodedPaths;
336
+ try {
337
+ encodedPaths = await readdir(projectsDir);
338
+ }
339
+ catch (error) {
340
+ if (error.code === "ENOENT") {
341
+ logger.debug(`Projects directory does not exist: ${projectsDir}`);
342
+ return [];
343
+ }
344
+ logger.warn(`Failed to read projects directory: ${projectsDir}: ${error.message}`);
345
+ return [];
346
+ }
347
+ // Get attribution index
348
+ const attributionIndex = await this.getAttributionIndex();
349
+ const directories = [];
350
+ for (const encodedPath of encodedPaths) {
351
+ const sessionDir = path.join(projectsDir, encodedPath);
352
+ // Check if it's a directory
353
+ try {
354
+ const stats = await stat(sessionDir);
355
+ if (!stats.isDirectory()) {
356
+ continue;
357
+ }
358
+ }
359
+ catch (error) {
360
+ logger.debug(`Failed to stat: ${sessionDir}: ${error.message}`);
361
+ continue;
362
+ }
363
+ // Decode path for display and filtering
364
+ const decodedPath = decodePathForDisplay(encodedPath);
365
+ // Filter out temp directories
366
+ if (isTempDirectory(decodedPath)) {
367
+ logger.debug(`Skipping temp directory: ${decodedPath}`);
368
+ continue;
369
+ }
370
+ // Get session files (already sorted by mtime descending)
371
+ const sessionFiles = await this.listSessionFiles(sessionDir);
372
+ if (sessionFiles.length === 0) {
373
+ continue;
374
+ }
375
+ // Check if this matches a known agent
376
+ const agentMatch = agentLookup.get(encodedPath);
377
+ const agentName = agentMatch?.agentName;
378
+ const dockerEnabled = agentMatch?.dockerEnabled ?? false;
379
+ const metadataKey = agentName ?? "adhoc";
380
+ directories.push({
381
+ encodedPath,
382
+ decodedPath,
383
+ agentName,
384
+ metadataKey,
385
+ dockerEnabled,
386
+ sessionFiles,
387
+ });
388
+ }
389
+ // Phase 2: If limit is set, find the top N sessions by mtime across all directories
390
+ // Each directory's sessionFiles are already sorted by mtime descending,
391
+ // so we merge-select the top N using pointers into each sorted list.
392
+ let selectedSessionIds;
393
+ if (limit !== undefined) {
394
+ // Merge pointers: index into each directory's sorted sessionFiles
395
+ const pointers = directories.map(() => 0);
396
+ selectedSessionIds = new Set();
397
+ for (let picked = 0; picked < limit; picked++) {
398
+ let bestDir = -1;
399
+ let bestMtime = null;
400
+ for (let d = 0; d < directories.length; d++) {
401
+ const dir = directories[d];
402
+ if (pointers[d] >= dir.sessionFiles.length)
403
+ continue;
404
+ const candidate = dir.sessionFiles[pointers[d]];
405
+ if (bestMtime === null || candidate.mtime > bestMtime) {
406
+ bestMtime = candidate.mtime;
407
+ bestDir = d;
408
+ }
409
+ }
410
+ if (bestDir === -1)
411
+ break; // No more sessions
412
+ selectedSessionIds.add(directories[bestDir].sessionFiles[pointers[bestDir]].sessionId);
413
+ pointers[bestDir]++;
414
+ }
415
+ }
416
+ // Phase 3: Enrich sessions (only selected ones when limit is set)
417
+ const groups = [];
418
+ for (const dir of directories) {
419
+ const sessions = [];
420
+ const autoNameUpdates = [];
421
+ const previewUpdates = [];
422
+ for (const { sessionId, mtime } of dir.sessionFiles) {
423
+ // Skip sessions not in the selected set when limit is active
424
+ if (selectedSessionIds && !selectedSessionIds.has(sessionId)) {
425
+ continue;
426
+ }
427
+ // Filter out sidechain (sub-agent) sessions — see comment in getAgentSessions()
428
+ const filePath = getCliSessionFile(dir.decodedPath, sessionId);
429
+ if (await isSidechainSession(filePath)) {
430
+ continue;
431
+ }
432
+ const attribution = attributionIndex.getAttribute(sessionId);
433
+ const mtimeStr = mtime.toISOString();
434
+ // Get custom name (works for both attributed and unattributed sessions)
435
+ const customName = await this.sessionMetadataStore.getCustomName(dir.metadataKey, sessionId);
436
+ // Resolve autoName with caching
437
+ const { autoName, needsUpdate } = await this.resolveAutoName(dir.metadataKey, sessionId, mtimeStr, dir.decodedPath);
438
+ if (needsUpdate && autoName) {
439
+ autoNameUpdates.push({ sessionId, autoName, mtime: mtimeStr });
440
+ }
441
+ // Resolve preview with caching
442
+ const { preview, needsUpdate: previewNeedsUpdate } = await this.resolvePreview(dir.metadataKey, sessionId, mtimeStr, dir.decodedPath);
443
+ if (previewNeedsUpdate && preview) {
444
+ previewUpdates.push({ sessionId, preview, mtime: mtimeStr });
445
+ }
446
+ sessions.push({
447
+ sessionId,
448
+ workingDirectory: dir.decodedPath,
449
+ mtime: mtimeStr,
450
+ origin: attribution.origin,
451
+ agentName: attribution.agentName ?? dir.agentName,
452
+ resumable: !dir.dockerEnabled,
453
+ customName,
454
+ autoName,
455
+ preview,
456
+ });
457
+ }
458
+ // Batch write any cache updates for this directory
459
+ if (autoNameUpdates.length > 0) {
460
+ await this.sessionMetadataStore.batchSetAutoNames(dir.metadataKey, autoNameUpdates);
461
+ }
462
+ if (previewUpdates.length > 0) {
463
+ await this.sessionMetadataStore.batchSetPreviews(dir.metadataKey, previewUpdates);
464
+ }
465
+ if (sessions.length > 0) {
466
+ groups.push({
467
+ workingDirectory: dir.decodedPath,
468
+ encodedPath: dir.encodedPath,
469
+ agentName: dir.agentName,
470
+ sessionCount: dir.sessionFiles.length,
471
+ sessions,
472
+ });
473
+ }
474
+ }
475
+ // Sort groups by most recent session mtime descending
476
+ groups.sort((a, b) => {
477
+ const aLatest = a.sessions[0]?.mtime ?? "";
478
+ const bLatest = b.sessions[0]?.mtime ?? "";
479
+ return bLatest.localeCompare(aLatest);
480
+ });
481
+ return groups;
482
+ }
483
+ /**
484
+ * Get parsed chat messages from a session.
485
+ *
486
+ * Delegates to the JSONL parser.
487
+ *
488
+ * @param workingDirectory - The session's working directory
489
+ * @param sessionId - The session ID
490
+ * @returns Array of chat messages
491
+ */
492
+ async getSessionMessages(workingDirectory, sessionId) {
493
+ const filePath = getCliSessionFile(workingDirectory, sessionId);
494
+ return parseSessionMessages(filePath);
495
+ }
496
+ /**
497
+ * Get metadata for a session.
498
+ *
499
+ * Caches the result for efficiency when called repeatedly.
500
+ *
501
+ * @param workingDirectory - The session's working directory
502
+ * @param sessionId - The session ID
503
+ * @returns Session metadata
504
+ */
505
+ async getSessionMetadata(workingDirectory, sessionId) {
506
+ const filePath = getCliSessionFile(workingDirectory, sessionId);
507
+ // Check cache
508
+ const cached = this.metadataCache.get(filePath);
509
+ if (cached !== undefined) {
510
+ return cached;
511
+ }
512
+ // Extract metadata
513
+ const metadata = await extractSessionMetadata(filePath);
514
+ // Cache and return
515
+ this.metadataCache.set(filePath, metadata);
516
+ return metadata;
517
+ }
518
+ /**
519
+ * Get usage data for a session.
520
+ *
521
+ * Delegates to the JSONL parser.
522
+ *
523
+ * @param workingDirectory - The session's working directory
524
+ * @param sessionId - The session ID
525
+ * @returns Session usage data
526
+ */
527
+ async getSessionUsage(workingDirectory, sessionId) {
528
+ const filePath = getCliSessionFile(workingDirectory, sessionId);
529
+ return extractSessionUsage(filePath);
530
+ }
531
+ /**
532
+ * Invalidate cached data.
533
+ *
534
+ * If a working directory is provided, only that directory's cache entry
535
+ * is cleared. Otherwise, all caches are cleared.
536
+ *
537
+ * @param workingDirectory - Optional working directory to clear cache for
538
+ */
539
+ invalidateCache(workingDirectory) {
540
+ if (workingDirectory !== undefined) {
541
+ const encodedPath = encodePathForCli(workingDirectory);
542
+ const sessionDir = path.join(this.claudeHomePath, "projects", encodedPath);
543
+ this.directoryCache.delete(sessionDir);
544
+ logger.debug(`Invalidated cache for directory: ${sessionDir}`);
545
+ }
546
+ else {
547
+ this.directoryCache.clear();
548
+ this.attributionIndex = null;
549
+ this.attributionFetchedAt = 0;
550
+ this.metadataCache.clear();
551
+ logger.debug("Invalidated all caches");
552
+ }
553
+ }
554
+ }
555
+ //# sourceMappingURL=session-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-discovery.js","sourceRoot":"","sources":["../../src/state/session-discovery.ts"],"names":[],"mappings":"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"}