sidekick-shared 0.24.5 → 0.26.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.
- package/CHANGELOG.md +92 -0
- package/README.md +205 -52
- package/dist/accountChangeSignal.d.ts +3 -0
- package/dist/accountChangeSignal.js +19 -0
- package/dist/accountChanges.d.ts +21 -0
- package/dist/accountChanges.js +170 -0
- package/dist/accountRegistry.d.ts +25 -2
- package/dist/accountRegistry.js +41 -13
- package/dist/accountStatus.d.ts +9 -1
- package/dist/accountStatus.js +3 -3
- package/dist/accounts.d.ts +3 -1
- package/dist/accounts.js +84 -64
- package/dist/aggregation/EventAggregator.d.ts +15 -2
- package/dist/aggregation/EventAggregator.js +54 -9
- package/dist/aggregation/costProvenance.d.ts +14 -0
- package/dist/aggregation/costProvenance.js +35 -0
- package/dist/aggregation/snapshot.d.ts +13 -2
- package/dist/aggregation/snapshot.js +57 -6
- package/dist/aggregation/types.d.ts +30 -1
- package/dist/analytics/compactionLedger.js +15 -5
- package/dist/analytics/qualityScore.js +2 -1
- package/dist/browser.d.ts +19 -4
- package/dist/browser.js +43 -1
- package/dist/codexProfiles.d.ts +3 -1
- package/dist/codexProfiles.js +4 -2
- package/dist/codexQuota.d.ts +2 -0
- package/dist/codexQuota.js +11 -66
- package/dist/codexQuotaWatcher.d.ts +6 -0
- package/dist/codexQuotaWatcher.js +101 -47
- package/dist/failingTools.d.ts +37 -0
- package/dist/failingTools.js +70 -0
- package/dist/formatters/sessionDump.js +13 -7
- package/dist/formatting.d.ts +13 -0
- package/dist/formatting.js +35 -0
- package/dist/historicalStore.d.ts +47 -0
- package/dist/historicalStore.js +312 -0
- package/dist/index.d.ts +67 -23
- package/dist/index.js +138 -26
- package/dist/modelAliases.d.ts +13 -0
- package/dist/modelAliases.js +58 -0
- package/dist/modelCatalog.d.ts +32 -0
- package/dist/modelCatalog.js +145 -0
- package/dist/modelContext.d.ts +36 -3
- package/dist/modelContext.js +109 -34
- package/dist/modelInfo.d.ts +30 -1
- package/dist/modelInfo.js +116 -26
- package/dist/multiProviderQuotaService.d.ts +8 -0
- package/dist/multiProviderQuotaService.js +64 -3
- package/dist/node.d.ts +23 -4
- package/dist/node.js +50 -1
- package/dist/observedContextWindows.d.ts +7 -0
- package/dist/observedContextWindows.js +17 -2
- package/dist/observedSessionCollector.d.ts +72 -6
- package/dist/observedSessionCollector.js +440 -47
- package/dist/parsers/codexHistory.d.ts +4 -2
- package/dist/parsers/codexHistory.js +9 -2
- package/dist/parsers/sessionActivityDetector.d.ts +28 -0
- package/dist/parsers/sessionActivityDetector.js +110 -15
- package/dist/parsers/sessionPathResolver.d.ts +11 -0
- package/dist/parsers/sessionPathResolver.js +13 -10
- package/dist/peakHours.d.ts +34 -3
- package/dist/peakHours.js +92 -7
- package/dist/pricingCatalog.d.ts +7 -0
- package/dist/pricingCatalog.js +8 -5
- package/dist/providerIds.d.ts +11 -0
- package/dist/providerIds.js +9 -0
- package/dist/providerQuota.d.ts +3 -1
- package/dist/providerQuota.js +3 -0
- package/dist/providers/claudeCode.d.ts +10 -1
- package/dist/providers/claudeCode.js +211 -131
- package/dist/providers/codex.d.ts +20 -1
- package/dist/providers/codex.js +308 -244
- package/dist/providers/codexDatabase.d.ts +9 -0
- package/dist/providers/codexDatabase.js +107 -2
- package/dist/providers/detect.d.ts +2 -0
- package/dist/providers/detect.js +29 -24
- package/dist/providers/diagnostics.d.ts +13 -0
- package/dist/providers/diagnostics.js +88 -0
- package/dist/providers/factory.d.ts +20 -0
- package/dist/providers/factory.js +57 -0
- package/dist/providers/openCode.d.ts +22 -1
- package/dist/providers/openCode.js +460 -126
- package/dist/providers/openCodeDatabase.d.ts +10 -0
- package/dist/providers/openCodeDatabase.js +69 -0
- package/dist/providers/rolloutWalker.d.ts +53 -0
- package/dist/providers/rolloutWalker.js +209 -0
- package/dist/providers/types.d.ts +82 -2
- package/dist/providers/types.js +4 -0
- package/dist/quota.d.ts +34 -4
- package/dist/quota.js +37 -9
- package/dist/quotaHistory.d.ts +3 -2
- package/dist/quotaHistory.js +36 -35
- package/dist/quotaPoller.d.ts +13 -1
- package/dist/quotaPoller.js +65 -3
- package/dist/quotaResolver.d.ts +81 -0
- package/dist/quotaResolver.js +213 -0
- package/dist/quotaSnapshots.d.ts +15 -2
- package/dist/quotaSnapshots.js +25 -3
- package/dist/quotaThresholds.d.ts +60 -0
- package/dist/quotaThresholds.js +90 -0
- package/dist/report/htmlReportGenerator.js +10 -4
- package/dist/report/sessionReportInputs.d.ts +23 -0
- package/dist/report/sessionReportInputs.js +34 -0
- package/dist/schemas/accountManager.d.ts +2 -2
- package/dist/schemas/accountManager.js +2 -4
- package/dist/schemas/accountStatus.d.ts +3 -0
- package/dist/schemas/accountStatus.js +1 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.js +16 -2
- package/dist/schemas/observedSessionV1.d.ts +27 -26
- package/dist/schemas/observedSessionV1.js +6 -4
- package/dist/schemas/quota.d.ts +141 -2
- package/dist/schemas/quota.js +20 -8
- package/dist/schemas/quotaHistory.d.ts +3 -2
- package/dist/schemas/quotaHistory.js +1 -1
- package/dist/schemas/sessionEvent.d.ts +6 -0
- package/dist/schemas/sessionEvent.js +1 -0
- package/dist/schemas/stateFile.d.ts +198 -0
- package/dist/schemas/stateFile.js +77 -0
- package/dist/schemas/tokenEstimation.d.ts +1 -1
- package/dist/schemas/transcript.js +2 -0
- package/dist/sessionFingerprint.d.ts +25 -0
- package/dist/sessionFingerprint.js +46 -0
- package/dist/sessionMonitor.d.ts +15 -0
- package/dist/sessionMonitor.js +142 -0
- package/dist/sessionPreviews.d.ts +33 -2
- package/dist/sessionPreviews.js +333 -6
- package/dist/sessionStats.d.ts +51 -0
- package/dist/sessionStats.js +166 -0
- package/dist/stateFile.d.ts +113 -0
- package/dist/stateFile.js +160 -0
- package/dist/statusline/claudeStatuslinePayload.d.ts +89 -0
- package/dist/statusline/claudeStatuslinePayload.js +152 -0
- package/dist/statusline/formatter.d.ts +13 -0
- package/dist/statusline/formatter.js +48 -6
- package/dist/statusline/index.d.ts +10 -2
- package/dist/statusline/index.js +19 -1
- package/dist/tokenSummary.d.ts +62 -0
- package/dist/tokenSummary.js +72 -0
- package/dist/transcript.d.ts +5 -0
- package/dist/transcript.js +2 -0
- package/dist/types/observedSessionV1.d.ts +8 -2
- package/dist/types/observedSessionV1.js +88 -25
- package/dist/types/sessionEvent.d.ts +2 -0
- package/dist/usage/billingBlocks.d.ts +85 -0
- package/dist/usage/billingBlocks.js +153 -0
- package/dist/usage/importSessionHistory.d.ts +53 -0
- package/dist/usage/importSessionHistory.js +88 -0
- package/dist/usage/usageEvents.d.ts +92 -0
- package/dist/usage/usageEvents.js +322 -0
- package/dist/usage/usageReports.d.ts +76 -0
- package/dist/usage/usageReports.js +208 -0
- package/dist/watchers/factory.d.ts +6 -0
- package/dist/watchers/factory.js +30 -25
- package/dist/zaiQuotaApi.d.ts +2 -0
- package/dist/zaiQuotaApi.js +2 -0
- package/package.json +22 -1
|
@@ -48,7 +48,9 @@ const os = __importStar(require("os"));
|
|
|
48
48
|
const path = __importStar(require("path"));
|
|
49
49
|
const string_decoder_1 = require("string_decoder");
|
|
50
50
|
const sessionContext_1 = require("../context/sessionContext");
|
|
51
|
+
const sessionStats_1 = require("../sessionStats");
|
|
51
52
|
const jsonl_1 = require("../parsers/jsonl");
|
|
53
|
+
const diagnostics_1 = require("./diagnostics");
|
|
52
54
|
const sessionPathResolver_1 = require("../parsers/sessionPathResolver");
|
|
53
55
|
const subagentScanner_1 = require("../parsers/subagentScanner");
|
|
54
56
|
const modelContext_1 = require("../modelContext");
|
|
@@ -88,6 +90,42 @@ function extractSearchableText(event) {
|
|
|
88
90
|
}
|
|
89
91
|
return '';
|
|
90
92
|
}
|
|
93
|
+
function extractClaudeLabelFromPrefix(chunk) {
|
|
94
|
+
for (const line of chunk.split('\n')) {
|
|
95
|
+
const trimmed = line.trim();
|
|
96
|
+
if (!trimmed)
|
|
97
|
+
continue;
|
|
98
|
+
try {
|
|
99
|
+
const event = JSON.parse(trimmed);
|
|
100
|
+
if (event.type !== 'user')
|
|
101
|
+
continue;
|
|
102
|
+
const content = event.message?.content;
|
|
103
|
+
if (!content)
|
|
104
|
+
continue;
|
|
105
|
+
let text = null;
|
|
106
|
+
if (typeof content === 'string') {
|
|
107
|
+
text = content.trim();
|
|
108
|
+
}
|
|
109
|
+
else if (Array.isArray(content)) {
|
|
110
|
+
const textBlock = content.find((block) => isTypedBlock(block) &&
|
|
111
|
+
block.type === 'text' &&
|
|
112
|
+
typeof block.text === 'string' &&
|
|
113
|
+
block.text.trim().length > 0);
|
|
114
|
+
if (textBlock && isTypedBlock(textBlock) && typeof textBlock.text === 'string') {
|
|
115
|
+
text = textBlock.text.trim();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (text) {
|
|
119
|
+
const compact = text.replace(/\s+/g, ' ');
|
|
120
|
+
return compact.length > 60 ? compact.substring(0, 57) + '...' : compact;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// Skip malformed lines.
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
91
129
|
/**
|
|
92
130
|
* Incremental JSONL reader for Claude Code session files.
|
|
93
131
|
*
|
|
@@ -234,8 +272,12 @@ function normalizeClaudeUsage(event) {
|
|
|
234
272
|
class ClaudeCodeProvider {
|
|
235
273
|
id = 'claude-code';
|
|
236
274
|
displayName = 'Claude Code';
|
|
275
|
+
diagnostics;
|
|
237
276
|
/** Runtime-reported context window limit (overrides static map when set). */
|
|
238
277
|
dynamicContextWindowLimit = null;
|
|
278
|
+
constructor(options = {}) {
|
|
279
|
+
this.diagnostics = new diagnostics_1.ProviderDiagnosticTracker(this.id, options);
|
|
280
|
+
}
|
|
239
281
|
// --- Path resolution ---
|
|
240
282
|
getSessionDirectory(workspacePath) {
|
|
241
283
|
return (0, sessionPathResolver_1.getSessionDirectory)(workspacePath);
|
|
@@ -250,6 +292,43 @@ class ClaudeCodeProvider {
|
|
|
250
292
|
findAllSessions(workspacePath) {
|
|
251
293
|
return (0, sessionPathResolver_1.findAllSessions)(workspacePath);
|
|
252
294
|
}
|
|
295
|
+
findSessionById(workspacePath, sessionId) {
|
|
296
|
+
const normalizedId = sessionId.trim();
|
|
297
|
+
if (!normalizedId || path.basename(normalizedId) !== normalizedId)
|
|
298
|
+
return null;
|
|
299
|
+
try {
|
|
300
|
+
const directories = new Set();
|
|
301
|
+
directories.add(this.getSessionDirectory(workspacePath));
|
|
302
|
+
const discovered = this.discoverSessionDirectory(workspacePath);
|
|
303
|
+
if (discovered)
|
|
304
|
+
directories.add(discovered);
|
|
305
|
+
for (const folder of this.getAllProjectFolders(workspacePath))
|
|
306
|
+
directories.add(folder.dir);
|
|
307
|
+
for (const directory of directories) {
|
|
308
|
+
const direct = path.join(directory, `${normalizedId}.jsonl`);
|
|
309
|
+
try {
|
|
310
|
+
if (fs.statSync(direct).isFile()) {
|
|
311
|
+
this.diagnostics.available('findSessionById');
|
|
312
|
+
return direct;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
// Continue with directory-shaped lookup for worktree layouts.
|
|
317
|
+
}
|
|
318
|
+
const match = this.findSessionsInDirectory(directory).find((candidate) => this.getSessionId(candidate) === normalizedId);
|
|
319
|
+
if (match) {
|
|
320
|
+
this.diagnostics.available('findSessionById');
|
|
321
|
+
return match;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
this.recordHomeStatus('findSessionById');
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
this.recordHomeStatus('findSessionById');
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
253
332
|
/** Backward-compatible alias for findAllSessions. */
|
|
254
333
|
findSessionFiles(workspacePath) {
|
|
255
334
|
return this.findAllSessions(workspacePath);
|
|
@@ -264,19 +343,89 @@ class ClaudeCodeProvider {
|
|
|
264
343
|
const results = [];
|
|
265
344
|
const seen = new Set();
|
|
266
345
|
for (const folder of this.getAllProjectFolders()) {
|
|
267
|
-
|
|
268
|
-
|
|
346
|
+
// The directory listing already stat'ed each file; reuse it instead of
|
|
347
|
+
// stat'ing every session a second time.
|
|
348
|
+
for (const file of (0, sessionPathResolver_1.findSessionFilesWithStats)(folder.dir)) {
|
|
349
|
+
if (seen.has(file.path))
|
|
269
350
|
continue;
|
|
270
|
-
seen.add(
|
|
351
|
+
seen.add(file.path);
|
|
352
|
+
results.push({ path: file.path, mtime: file.mtime, sizeBytes: file.sizeBytes });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
this.recordHomeStatus('listAllSessionFiles');
|
|
356
|
+
return results;
|
|
357
|
+
}
|
|
358
|
+
async listSessionFilesAsync(workspacePath) {
|
|
359
|
+
const projectsRoot = this.getProjectsBaseDir();
|
|
360
|
+
let projectEntries = [];
|
|
361
|
+
try {
|
|
362
|
+
projectEntries = await fs.promises.readdir(projectsRoot, { withFileTypes: true });
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
this.recordHomeStatus('listSessionFilesAsync');
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
const encodedWorkspace = workspacePath ? (0, sessionPathResolver_1.encodeWorkspacePath)(workspacePath).toLowerCase() : null;
|
|
369
|
+
const workspaceBasename = workspacePath
|
|
370
|
+
? path
|
|
371
|
+
.basename(workspacePath)
|
|
372
|
+
.replace(/[^a-zA-Z0-9]/g, '-')
|
|
373
|
+
.toLowerCase()
|
|
374
|
+
: null;
|
|
375
|
+
const directories = [];
|
|
376
|
+
for (const entry of projectEntries) {
|
|
377
|
+
const directory = path.join(projectsRoot, entry.name);
|
|
378
|
+
try {
|
|
379
|
+
const stat = entry.isDirectory() ? null : await fs.promises.stat(directory);
|
|
380
|
+
if (!entry.isDirectory() && !stat?.isDirectory())
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const name = entry.name.toLowerCase();
|
|
387
|
+
if (encodedWorkspace &&
|
|
388
|
+
name !== encodedWorkspace &&
|
|
389
|
+
!name.startsWith(`${encodedWorkspace}-`) &&
|
|
390
|
+
name !== workspaceBasename &&
|
|
391
|
+
!name.endsWith(`-${workspaceBasename}`)) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
directories.push(directory);
|
|
395
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
396
|
+
}
|
|
397
|
+
const results = [];
|
|
398
|
+
for (const directory of directories) {
|
|
399
|
+
let entries = [];
|
|
400
|
+
try {
|
|
401
|
+
entries = await fs.promises.readdir(directory, { withFileTypes: true });
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
for (const entry of entries) {
|
|
407
|
+
if (!entry.isFile() || !this.isSessionFile(entry.name))
|
|
408
|
+
continue;
|
|
409
|
+
const sessionPath = path.join(directory, entry.name);
|
|
271
410
|
try {
|
|
272
|
-
|
|
411
|
+
const stat = await fs.promises.stat(sessionPath);
|
|
412
|
+
if (stat.size > 0) {
|
|
413
|
+
results.push({
|
|
414
|
+
path: sessionPath,
|
|
415
|
+
mtime: stat.mtime,
|
|
416
|
+
sizeBytes: stat.size,
|
|
417
|
+
sessionId: this.getSessionId(sessionPath),
|
|
418
|
+
});
|
|
419
|
+
}
|
|
273
420
|
}
|
|
274
421
|
catch {
|
|
275
|
-
// Skip files that vanish between
|
|
422
|
+
// Skip files that vanish between discovery and stat.
|
|
276
423
|
}
|
|
424
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
277
425
|
}
|
|
278
426
|
}
|
|
279
|
-
|
|
427
|
+
this.recordHomeStatus('listSessionFilesAsync');
|
|
428
|
+
return results.sort((left, right) => right.mtime.getTime() - left.mtime.getTime());
|
|
280
429
|
}
|
|
281
430
|
// --- File identification ---
|
|
282
431
|
isSessionFile(filename) {
|
|
@@ -296,50 +445,32 @@ class ClaudeCodeProvider {
|
|
|
296
445
|
fs.closeSync(fd);
|
|
297
446
|
if (bytesRead === 0)
|
|
298
447
|
return null;
|
|
299
|
-
|
|
300
|
-
const lines = chunk.split('\n');
|
|
301
|
-
for (const line of lines) {
|
|
302
|
-
const trimmed = line.trim();
|
|
303
|
-
if (!trimmed)
|
|
304
|
-
continue;
|
|
305
|
-
try {
|
|
306
|
-
const event = JSON.parse(trimmed);
|
|
307
|
-
if (event.type !== 'user')
|
|
308
|
-
continue;
|
|
309
|
-
const content = event.message?.content;
|
|
310
|
-
if (!content)
|
|
311
|
-
continue;
|
|
312
|
-
let text = null;
|
|
313
|
-
if (typeof content === 'string') {
|
|
314
|
-
text = content.trim();
|
|
315
|
-
}
|
|
316
|
-
else if (Array.isArray(content)) {
|
|
317
|
-
const textBlock = content.find((block) => isTypedBlock(block) &&
|
|
318
|
-
block.type === 'text' &&
|
|
319
|
-
typeof block.text === 'string' &&
|
|
320
|
-
block.text.trim().length > 0);
|
|
321
|
-
if (textBlock && isTypedBlock(textBlock) && typeof textBlock.text === 'string') {
|
|
322
|
-
text = textBlock.text.trim();
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
if (text && text.length > 0) {
|
|
326
|
-
text = text.replace(/\s+/g, ' ');
|
|
327
|
-
if (text.length > 60) {
|
|
328
|
-
text = text.substring(0, 57) + '...';
|
|
329
|
-
}
|
|
330
|
-
return text;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
// Skip malformed lines
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return null;
|
|
448
|
+
return extractClaudeLabelFromPrefix(buffer.toString('utf-8', 0, bytesRead));
|
|
338
449
|
}
|
|
339
450
|
catch {
|
|
340
451
|
return null;
|
|
341
452
|
}
|
|
342
453
|
}
|
|
454
|
+
async extractSessionLabelsAsync(sessionPaths) {
|
|
455
|
+
const results = new Map();
|
|
456
|
+
for (const sessionPath of sessionPaths) {
|
|
457
|
+
let handle = null;
|
|
458
|
+
try {
|
|
459
|
+
handle = await fs.promises.open(sessionPath, 'r');
|
|
460
|
+
const buffer = Buffer.alloc(8192);
|
|
461
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
462
|
+
results.set(sessionPath, extractClaudeLabelFromPrefix(buffer.toString('utf8', 0, bytesRead)));
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
results.set(sessionPath, null);
|
|
466
|
+
}
|
|
467
|
+
finally {
|
|
468
|
+
await handle?.close().catch(() => undefined);
|
|
469
|
+
}
|
|
470
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
471
|
+
}
|
|
472
|
+
return results;
|
|
473
|
+
}
|
|
343
474
|
// --- Data reading ---
|
|
344
475
|
createReader(sessionPath) {
|
|
345
476
|
return new ClaudeCodeReader(sessionPath);
|
|
@@ -397,100 +528,36 @@ class ClaudeCodeProvider {
|
|
|
397
528
|
getProjectsBaseDir() {
|
|
398
529
|
return path.join(os.homedir(), '.claude', 'projects');
|
|
399
530
|
}
|
|
531
|
+
getWatchRoots() {
|
|
532
|
+
return [this.getProjectsBaseDir()];
|
|
533
|
+
}
|
|
400
534
|
// --- Stats ---
|
|
401
535
|
readSessionStats(sessionPath) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
536
|
+
// One reader pass through the shared aggregator: cache-inclusive per-model
|
|
537
|
+
// totals, cost with provenance, a tool success/failure split, and the label
|
|
538
|
+
// from the first user prompt in the events already read.
|
|
539
|
+
return (0, sessionStats_1.readSessionFileStats)(this, sessionPath);
|
|
540
|
+
}
|
|
541
|
+
readSessionContextSnapshot(sessionPath, options = {}) {
|
|
542
|
+
return (0, sessionContext_1.readSessionContextSnapshot)(this, sessionPath, options);
|
|
543
|
+
}
|
|
544
|
+
// --- Optional methods ---
|
|
545
|
+
getRuntimeStatus() {
|
|
412
546
|
try {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
for (const line of lines) {
|
|
416
|
-
const trimmed = line.trim();
|
|
417
|
-
if (!trimmed || !trimmed.startsWith('{'))
|
|
418
|
-
continue;
|
|
419
|
-
try {
|
|
420
|
-
const event = JSON.parse(trimmed);
|
|
421
|
-
if (!startTime && event.timestamp)
|
|
422
|
-
startTime = event.timestamp;
|
|
423
|
-
if (event.timestamp)
|
|
424
|
-
endTime = event.timestamp;
|
|
425
|
-
if (event.type === 'assistant' && event.message?.usage) {
|
|
426
|
-
messageCount++;
|
|
427
|
-
const u = event.message.usage;
|
|
428
|
-
tokens.input += u.input_tokens || 0;
|
|
429
|
-
tokens.output += u.output_tokens || 0;
|
|
430
|
-
tokens.cacheWrite += u.cache_creation_input_tokens || 0;
|
|
431
|
-
tokens.cacheRead += u.cache_read_input_tokens || 0;
|
|
432
|
-
if (u.reported_cost)
|
|
433
|
-
reportedCost += u.reported_cost;
|
|
434
|
-
const model = event.message.model || 'unknown';
|
|
435
|
-
if (!modelUsage[model])
|
|
436
|
-
modelUsage[model] = { calls: 0, tokens: 0 };
|
|
437
|
-
modelUsage[model].calls++;
|
|
438
|
-
modelUsage[model].tokens += (u.input_tokens || 0) + (u.output_tokens || 0);
|
|
439
|
-
// Check content for tool_use blocks
|
|
440
|
-
if (Array.isArray(event.message.content)) {
|
|
441
|
-
for (const block of event.message.content) {
|
|
442
|
-
if (block.type === 'tool_use' && typeof block.name === 'string') {
|
|
443
|
-
toolUsage[block.name] = (toolUsage[block.name] || 0) + 1;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
if (event.type === 'user')
|
|
449
|
-
messageCount++;
|
|
450
|
-
if (event.type === 'summary')
|
|
451
|
-
compactionEstimate++;
|
|
452
|
-
// Check for truncation in tool results
|
|
453
|
-
if (event.type === 'user' && Array.isArray(event.message?.content)) {
|
|
454
|
-
for (const block of event.message.content) {
|
|
455
|
-
if (block.type === 'tool_result' && typeof block.content === 'string') {
|
|
456
|
-
for (const pattern of jsonl_1.TRUNCATION_PATTERNS) {
|
|
457
|
-
if (pattern.regex.test(block.content)) {
|
|
458
|
-
truncationCount++;
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
catch {
|
|
467
|
-
// Skip malformed lines
|
|
468
|
-
}
|
|
469
|
-
}
|
|
547
|
+
fs.accessSync(this.getProjectsBaseDir(), fs.constants.R_OK);
|
|
548
|
+
return { available: true, kind: 'available' };
|
|
470
549
|
}
|
|
471
550
|
catch {
|
|
472
|
-
|
|
551
|
+
return {
|
|
552
|
+
available: false,
|
|
553
|
+
kind: 'home_unavailable',
|
|
554
|
+
message: 'Claude Code session home is unavailable.',
|
|
555
|
+
};
|
|
473
556
|
}
|
|
474
|
-
return {
|
|
475
|
-
providerId: 'claude-code',
|
|
476
|
-
sessionId,
|
|
477
|
-
filePath: sessionPath,
|
|
478
|
-
label: this.extractSessionLabel(sessionPath),
|
|
479
|
-
startTime,
|
|
480
|
-
endTime,
|
|
481
|
-
messageCount,
|
|
482
|
-
tokens,
|
|
483
|
-
modelUsage,
|
|
484
|
-
toolUsage,
|
|
485
|
-
compactionEstimate,
|
|
486
|
-
truncationCount,
|
|
487
|
-
reportedCost,
|
|
488
|
-
};
|
|
489
557
|
}
|
|
490
|
-
|
|
491
|
-
return
|
|
558
|
+
getLastOperationStatus() {
|
|
559
|
+
return this.diagnostics.getLastOperationStatus();
|
|
492
560
|
}
|
|
493
|
-
// --- Optional methods ---
|
|
494
561
|
getContextWindowLimit(modelId) {
|
|
495
562
|
if (this.dynamicContextWindowLimit)
|
|
496
563
|
return this.dynamicContextWindowLimit;
|
|
@@ -552,5 +619,18 @@ class ClaudeCodeProvider {
|
|
|
552
619
|
dispose() {
|
|
553
620
|
this.dynamicContextWindowLimit = null;
|
|
554
621
|
}
|
|
622
|
+
recordHomeStatus(operation) {
|
|
623
|
+
const status = this.getRuntimeStatus();
|
|
624
|
+
if (status.available) {
|
|
625
|
+
this.diagnostics.available(operation);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
this.diagnostics.degraded(operation, status, {
|
|
629
|
+
kind: 'home_unavailable',
|
|
630
|
+
severity: 'info',
|
|
631
|
+
phase: 'enumerate',
|
|
632
|
+
message: status.message ?? 'Claude Code session home is unavailable.',
|
|
633
|
+
});
|
|
634
|
+
}
|
|
555
635
|
}
|
|
556
636
|
exports.ClaudeCodeProvider = ClaudeCodeProvider;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Ported from sidekick-vscode/src/services/providers/CodexSessionProvider.ts
|
|
10
10
|
*/
|
|
11
11
|
import type { ReadSessionContextSnapshotOptions, SessionContextSnapshot } from '../context/sessionContext';
|
|
12
|
-
import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileInfo, SessionFileStats, ProviderId } from './types';
|
|
12
|
+
import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileInfo, SessionFileStats, ProviderId, ProviderOperationStatus, ProviderRuntimeStatus, SessionProviderOptions } from './types';
|
|
13
13
|
import type { SubagentStats, TokenUsage } from '../types/sessionEvent';
|
|
14
14
|
import type { CodexRateLimits } from '../types/codex';
|
|
15
15
|
export interface FindCodexRolloutFileOptions {
|
|
@@ -43,24 +43,41 @@ export declare class CodexProvider implements SessionProviderBase {
|
|
|
43
43
|
readonly displayName = "Codex CLI";
|
|
44
44
|
private db;
|
|
45
45
|
private dbInitialized;
|
|
46
|
+
private dbStatus;
|
|
47
|
+
private readonly diagnostics;
|
|
46
48
|
private dynamicContextWindowLimit;
|
|
47
49
|
private lastRateLimitsData;
|
|
48
50
|
private lastTokenUsageData;
|
|
51
|
+
constructor(options?: SessionProviderOptions);
|
|
49
52
|
/** Lazy-initialize database connection. */
|
|
50
53
|
private ensureDb;
|
|
54
|
+
private ensureDbAsync;
|
|
55
|
+
/**
|
|
56
|
+
* Cache an async-created database only after an operation proved it usable.
|
|
57
|
+
* Sync paths treat a cached `db` as "open() succeeded", so adopting an
|
|
58
|
+
* unproven instance would silently disable their file fallbacks.
|
|
59
|
+
*/
|
|
60
|
+
private adoptDbIfAvailable;
|
|
61
|
+
getRuntimeStatus(): ProviderRuntimeStatus;
|
|
62
|
+
getLastOperationStatus(): ProviderOperationStatus;
|
|
51
63
|
getSessionDirectory(workspacePath: string): string;
|
|
52
64
|
discoverSessionDirectory(workspacePath: string): string | null;
|
|
53
65
|
findActiveSession(workspacePath: string): string | null;
|
|
54
66
|
findAllSessions(workspacePath: string): string[];
|
|
67
|
+
findSessionById(_workspacePath: string, sessionId: string): string | null;
|
|
55
68
|
/** Backward-compatible alias for findAllSessions. */
|
|
56
69
|
findSessionFiles(workspacePath: string): string[];
|
|
57
70
|
findSessionsInDirectory(dir: string): string[];
|
|
58
71
|
listAllSessionFiles(): SessionFileInfo[];
|
|
72
|
+
listSessionFilesAsync(workspacePath?: string): Promise<SessionFileInfo[]>;
|
|
59
73
|
getAllProjectFolders(workspacePath?: string): ProjectFolderInfo[];
|
|
60
74
|
isSessionFile(filename: string): boolean;
|
|
61
75
|
getSessionId(sessionPath: string): string;
|
|
62
76
|
encodeWorkspacePath(workspacePath: string): string;
|
|
77
|
+
/** Thread title or first user message from the SQLite index; null without it. */
|
|
78
|
+
private extractSessionLabelFromDb;
|
|
63
79
|
extractSessionLabel(sessionPath: string): string | null;
|
|
80
|
+
extractSessionLabelsAsync(sessionPaths: readonly string[]): Promise<Map<string, string | null>>;
|
|
64
81
|
createReader(sessionPath: string): SessionReader;
|
|
65
82
|
scanSubagents(_sessionDir: string, sessionId: string): SubagentStats[];
|
|
66
83
|
/**
|
|
@@ -69,6 +86,7 @@ export declare class CodexProvider implements SessionProviderBase {
|
|
|
69
86
|
private buildSubagentStats;
|
|
70
87
|
searchInSession(sessionPath: string, query: string, maxResults: number): SearchHit[];
|
|
71
88
|
getProjectsBaseDir(): string;
|
|
89
|
+
getWatchRoots(): string[];
|
|
72
90
|
readSessionStats(sessionPath: string): SessionFileStats;
|
|
73
91
|
readSessionContextSnapshot(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
|
|
74
92
|
getContextWindowLimit(modelId?: string): number;
|
|
@@ -77,4 +95,5 @@ export declare class CodexProvider implements SessionProviderBase {
|
|
|
77
95
|
/** Get the last rate limits data received from token_count events. */
|
|
78
96
|
getLastRateLimits(): CodexRateLimits | null;
|
|
79
97
|
dispose(): void;
|
|
98
|
+
private recordDatabaseFallback;
|
|
80
99
|
}
|