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
package/dist/providers/codex.js
CHANGED
|
@@ -48,11 +48,13 @@ exports.findCodexRolloutFile = findCodexRolloutFile;
|
|
|
48
48
|
const fs = __importStar(require("fs"));
|
|
49
49
|
const path = __importStar(require("path"));
|
|
50
50
|
const string_decoder_1 = require("string_decoder");
|
|
51
|
-
const EventAggregator_1 = require("../aggregation/EventAggregator");
|
|
52
51
|
const sessionContext_1 = require("../context/sessionContext");
|
|
52
|
+
const sessionStats_1 = require("../sessionStats");
|
|
53
53
|
const codexParser_1 = require("../parsers/codexParser");
|
|
54
54
|
const codexDatabase_1 = require("./codexDatabase");
|
|
55
55
|
const codexProfiles_1 = require("../codexProfiles");
|
|
56
|
+
const rolloutWalker_1 = require("./rolloutWalker");
|
|
57
|
+
const diagnostics_1 = require("./diagnostics");
|
|
56
58
|
const modelContext_1 = require("../modelContext");
|
|
57
59
|
const observedContextWindows_1 = require("../observedContextWindows");
|
|
58
60
|
const usageNormalization_1 = require("../usageNormalization");
|
|
@@ -72,28 +74,6 @@ function getCodexHomes() {
|
|
|
72
74
|
function getSessionsDirs() {
|
|
73
75
|
return getCodexHomes().map((home) => path.join(home, 'sessions'));
|
|
74
76
|
}
|
|
75
|
-
/** Test if a filename is a Codex rollout file. */
|
|
76
|
-
function isRolloutFile(filename) {
|
|
77
|
-
return filename.startsWith('rollout-') && filename.endsWith('.jsonl');
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Extract session UUID from a rollout filename.
|
|
81
|
-
* Format: rollout-<timestamp>-<uuid>.jsonl -> <uuid>
|
|
82
|
-
*/
|
|
83
|
-
function extractSessionId(filename) {
|
|
84
|
-
const base = path.basename(filename, '.jsonl');
|
|
85
|
-
// rollout-YYYYMMDD-HHMMSS-<uuid> or rollout-<timestamp>-<uuid>
|
|
86
|
-
const parts = base.split('-');
|
|
87
|
-
// The UUID is typically the last 5 segments (8-4-4-4-12)
|
|
88
|
-
if (parts.length >= 6) {
|
|
89
|
-
const possibleUuid = parts.slice(-5).join('-');
|
|
90
|
-
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(possibleUuid)) {
|
|
91
|
-
return possibleUuid;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// Fallback: use everything after "rollout-"
|
|
95
|
-
return base.replace(/^rollout-/, '');
|
|
96
|
-
}
|
|
97
77
|
/** Truncate a string to maxLen with ellipsis. */
|
|
98
78
|
function truncate(text, maxLen) {
|
|
99
79
|
const trimmed = text.trim().replace(/\s+/g, ' ');
|
|
@@ -102,68 +82,16 @@ function truncate(text, maxLen) {
|
|
|
102
82
|
}
|
|
103
83
|
return trimmed;
|
|
104
84
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Recursively find all rollout files under a directory.
|
|
107
|
-
* Handles the YYYY/MM/DD subdirectory structure.
|
|
108
|
-
*/
|
|
85
|
+
/** Every rollout under a directory, newest first (one capped walk). */
|
|
109
86
|
function findRolloutFiles(dir) {
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
if (!fs.existsSync(dir))
|
|
113
|
-
return results;
|
|
114
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
115
|
-
for (const entry of entries) {
|
|
116
|
-
const fullPath = path.join(dir, entry.name);
|
|
117
|
-
if (entry.isDirectory()) {
|
|
118
|
-
results.push(...findRolloutFiles(fullPath));
|
|
119
|
-
}
|
|
120
|
-
else if (entry.isFile() && isRolloutFile(entry.name)) {
|
|
121
|
-
try {
|
|
122
|
-
const stats = fs.statSync(fullPath);
|
|
123
|
-
if (stats.size > 0) {
|
|
124
|
-
results.push({ path: fullPath, mtime: stats.mtime });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
// Skip inaccessible files
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
// Skip inaccessible directories
|
|
135
|
-
}
|
|
136
|
-
return results;
|
|
87
|
+
return (0, rolloutWalker_1.walkRolloutFiles)([dir]);
|
|
137
88
|
}
|
|
138
89
|
function hasRolloutFile(dir) {
|
|
139
|
-
|
|
140
|
-
if (!fs.existsSync(dir))
|
|
141
|
-
return false;
|
|
142
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
143
|
-
const fullPath = path.join(dir, entry.name);
|
|
144
|
-
if (entry.isDirectory() && hasRolloutFile(fullPath))
|
|
145
|
-
return true;
|
|
146
|
-
if (entry.isFile() && isRolloutFile(entry.name))
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
return false;
|
|
90
|
+
return (0, rolloutWalker_1.walkRolloutFiles)([dir], { limit: 1 }).length > 0;
|
|
154
91
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
for (const sessionsDir of getSessionsDirs()) {
|
|
159
|
-
for (const file of findRolloutFiles(sessionsDir)) {
|
|
160
|
-
if (seen.has(file.path))
|
|
161
|
-
continue;
|
|
162
|
-
seen.add(file.path);
|
|
163
|
-
results.push(file);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return results;
|
|
92
|
+
/** Every rollout across the monitored homes, newest first, deduplicated by path. */
|
|
93
|
+
function findRolloutFilesInConfiguredHomes(options = {}) {
|
|
94
|
+
return (0, rolloutWalker_1.walkRolloutFiles)(getSessionsDirs(), options);
|
|
167
95
|
}
|
|
168
96
|
/**
|
|
169
97
|
* Locates the rollout file for a Codex session id by walking
|
|
@@ -183,41 +111,8 @@ function findCodexRolloutFile(sessionId, options = {}) {
|
|
|
183
111
|
const sessionsDirs = options.codexHome
|
|
184
112
|
? [path.join(options.codexHome, 'sessions')]
|
|
185
113
|
: getSessionsDirs();
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
for (const candidate of findRolloutFilesById(dir, normalizedId)) {
|
|
189
|
-
if (!best || candidate.mtimeMs > best.mtimeMs)
|
|
190
|
-
best = candidate;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return best?.path ?? null;
|
|
194
|
-
}
|
|
195
|
-
function findRolloutFilesById(dir, normalizedId) {
|
|
196
|
-
const results = [];
|
|
197
|
-
try {
|
|
198
|
-
if (!fs.existsSync(dir))
|
|
199
|
-
return results;
|
|
200
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
201
|
-
const fullPath = path.join(dir, entry.name);
|
|
202
|
-
if (entry.isDirectory()) {
|
|
203
|
-
results.push(...findRolloutFilesById(fullPath, normalizedId));
|
|
204
|
-
}
|
|
205
|
-
else if (entry.isFile() &&
|
|
206
|
-
isRolloutFile(entry.name) &&
|
|
207
|
-
extractSessionId(entry.name).toLowerCase() === normalizedId) {
|
|
208
|
-
try {
|
|
209
|
-
results.push({ path: fullPath, mtimeMs: fs.statSync(fullPath).mtimeMs });
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
// Skip files that vanish between readdir and stat.
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
// Skip inaccessible directories.
|
|
219
|
-
}
|
|
220
|
-
return results;
|
|
114
|
+
// Name-only filter, one walk across the homes, newest modification first.
|
|
115
|
+
return (0, rolloutWalker_1.walkRolloutFiles)(sessionsDirs, { sessionId: normalizedId })[0]?.path ?? null;
|
|
221
116
|
}
|
|
222
117
|
/** Pick the best sessions directory for UI/search entrypoints. */
|
|
223
118
|
function getSessionsDir() {
|
|
@@ -298,15 +193,73 @@ function readSessionMeta(rolloutPath) {
|
|
|
298
193
|
}
|
|
299
194
|
return null;
|
|
300
195
|
}
|
|
301
|
-
|
|
196
|
+
async function readSessionMetaAsync(rolloutPath) {
|
|
197
|
+
// Mirrors readSessionMeta()/readFirstLines(): chunk-read until the first
|
|
198
|
+
// line is complete under the same 256 KiB cap — session_meta carries
|
|
199
|
+
// `instructions` and can far exceed a single chunk.
|
|
200
|
+
const maxBytes = 256 * 1024;
|
|
201
|
+
const chunkSize = 64 * 1024;
|
|
202
|
+
let handle = null;
|
|
203
|
+
try {
|
|
204
|
+
handle = await fs.promises.open(rolloutPath, 'r');
|
|
205
|
+
const chunks = [];
|
|
206
|
+
let totalRead = 0;
|
|
207
|
+
let newlineFound = false;
|
|
208
|
+
while (totalRead < maxBytes && !newlineFound) {
|
|
209
|
+
const buffer = Buffer.alloc(Math.min(chunkSize, maxBytes - totalRead));
|
|
210
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, totalRead);
|
|
211
|
+
if (bytesRead === 0)
|
|
212
|
+
break;
|
|
213
|
+
const chunk = buffer.subarray(0, bytesRead);
|
|
214
|
+
chunks.push(chunk);
|
|
215
|
+
totalRead += bytesRead;
|
|
216
|
+
newlineFound = chunk.includes(0x0a);
|
|
217
|
+
}
|
|
218
|
+
const firstLine = Buffer.concat(chunks).toString('utf8').split('\n', 1)[0]?.trim();
|
|
219
|
+
if (!firstLine)
|
|
220
|
+
return null;
|
|
221
|
+
const parsed = JSON.parse(firstLine);
|
|
222
|
+
return parsed.type === 'session_meta' ? parsed.payload : null;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
await handle?.close().catch(() => undefined);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Read the CWD from a rollout file's session_meta. Cached per path, bounded
|
|
233
|
+
* so a long-lived host watching a large history cannot grow it without limit
|
|
234
|
+
* (insertion order doubles as an eviction order).
|
|
235
|
+
*/
|
|
236
|
+
const CWD_CACHE_LIMIT = 5000;
|
|
302
237
|
const cwdCache = new Map();
|
|
238
|
+
function rememberCwd(rolloutPath, cwd) {
|
|
239
|
+
if (!cwdCache.has(rolloutPath) && cwdCache.size >= CWD_CACHE_LIMIT) {
|
|
240
|
+
const oldest = cwdCache.keys().next().value;
|
|
241
|
+
if (oldest !== undefined)
|
|
242
|
+
cwdCache.delete(oldest);
|
|
243
|
+
}
|
|
244
|
+
cwdCache.set(rolloutPath, cwd);
|
|
245
|
+
}
|
|
303
246
|
function readSessionCwd(rolloutPath) {
|
|
304
247
|
const cached = cwdCache.get(rolloutPath);
|
|
305
248
|
if (cached !== undefined)
|
|
306
249
|
return cached;
|
|
307
250
|
const meta = readSessionMeta(rolloutPath);
|
|
308
251
|
const cwd = meta?.cwd || null;
|
|
309
|
-
|
|
252
|
+
rememberCwd(rolloutPath, cwd);
|
|
253
|
+
return cwd;
|
|
254
|
+
}
|
|
255
|
+
/** Async twin of readSessionCwd, sharing its cache so repeat listings do no content reads. */
|
|
256
|
+
async function readSessionCwdAsync(rolloutPath) {
|
|
257
|
+
const cached = cwdCache.get(rolloutPath);
|
|
258
|
+
if (cached !== undefined)
|
|
259
|
+
return cached;
|
|
260
|
+
const meta = await readSessionMetaAsync(rolloutPath);
|
|
261
|
+
const cwd = meta?.cwd || null;
|
|
262
|
+
rememberCwd(rolloutPath, cwd);
|
|
310
263
|
return cwd;
|
|
311
264
|
}
|
|
312
265
|
/** Check if a session's CWD matches a workspace path. */
|
|
@@ -333,48 +286,65 @@ function normalizePath(input) {
|
|
|
333
286
|
*/
|
|
334
287
|
function extractFirstUserMessage(rolloutPath) {
|
|
335
288
|
try {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
289
|
+
return extractFirstUserMessageFromLines(readFirstLines(rolloutPath, 20));
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function extractFirstUserMessageFromLines(lines) {
|
|
296
|
+
for (const line of lines) {
|
|
297
|
+
const trimmed = line.trim();
|
|
298
|
+
if (!trimmed)
|
|
299
|
+
continue;
|
|
300
|
+
try {
|
|
301
|
+
const parsed = JSON.parse(trimmed);
|
|
302
|
+
// Check response_item with user message
|
|
303
|
+
if (parsed.type === 'response_item') {
|
|
304
|
+
const payload = parsed.payload;
|
|
305
|
+
if (payload.role === 'user') {
|
|
306
|
+
const content = payload.content;
|
|
307
|
+
if (typeof content === 'string' && content.trim() && !isSystemInjection(content)) {
|
|
308
|
+
return truncate(content, 60);
|
|
309
|
+
}
|
|
310
|
+
if (Array.isArray(content)) {
|
|
311
|
+
for (const part of content) {
|
|
312
|
+
const p = part;
|
|
313
|
+
if (p.text?.trim() && !isSystemInjection(p.text)) {
|
|
314
|
+
return truncate(p.text, 60);
|
|
357
315
|
}
|
|
358
316
|
}
|
|
359
317
|
}
|
|
360
318
|
}
|
|
361
|
-
// Check event_msg with user_message
|
|
362
|
-
if (parsed.type === 'event_msg') {
|
|
363
|
-
const payload = parsed.payload;
|
|
364
|
-
if (payload.type === 'user_message' && payload.message?.trim()) {
|
|
365
|
-
return truncate(payload.message, 60);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
319
|
}
|
|
369
|
-
|
|
370
|
-
|
|
320
|
+
// Check event_msg with user_message
|
|
321
|
+
if (parsed.type === 'event_msg') {
|
|
322
|
+
const payload = parsed.payload;
|
|
323
|
+
if (payload.type === 'user_message' && payload.message?.trim()) {
|
|
324
|
+
return truncate(payload.message, 60);
|
|
325
|
+
}
|
|
371
326
|
}
|
|
372
327
|
}
|
|
373
|
-
|
|
328
|
+
catch {
|
|
329
|
+
// Skip malformed lines
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
async function extractFirstUserMessageAsync(rolloutPath) {
|
|
335
|
+
let handle = null;
|
|
336
|
+
try {
|
|
337
|
+
handle = await fs.promises.open(rolloutPath, 'r');
|
|
338
|
+
const buffer = Buffer.alloc(256 * 1024);
|
|
339
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
340
|
+
return extractFirstUserMessageFromLines(buffer.toString('utf8', 0, bytesRead).split('\n').slice(0, 20));
|
|
374
341
|
}
|
|
375
342
|
catch {
|
|
376
343
|
return null;
|
|
377
344
|
}
|
|
345
|
+
finally {
|
|
346
|
+
await handle?.close().catch(() => undefined);
|
|
347
|
+
}
|
|
378
348
|
}
|
|
379
349
|
/** Extract searchable text from a rollout line. */
|
|
380
350
|
function extractSearchableText(line) {
|
|
@@ -661,9 +631,14 @@ class CodexProvider {
|
|
|
661
631
|
displayName = 'Codex CLI';
|
|
662
632
|
db = null;
|
|
663
633
|
dbInitialized = false;
|
|
634
|
+
dbStatus = { available: false, kind: 'db_missing' };
|
|
635
|
+
diagnostics;
|
|
664
636
|
dynamicContextWindowLimit = null;
|
|
665
637
|
lastRateLimitsData = null;
|
|
666
638
|
lastTokenUsageData = null;
|
|
639
|
+
constructor(options = {}) {
|
|
640
|
+
this.diagnostics = new diagnostics_1.ProviderDiagnosticTracker(this.id, options);
|
|
641
|
+
}
|
|
667
642
|
// --- Database ---
|
|
668
643
|
/** Lazy-initialize database connection. */
|
|
669
644
|
ensureDb() {
|
|
@@ -675,8 +650,34 @@ class CodexProvider {
|
|
|
675
650
|
if (db.isAvailable() && db.open()) {
|
|
676
651
|
this.db = db;
|
|
677
652
|
}
|
|
653
|
+
if (typeof db.getRuntimeStatus === 'function') {
|
|
654
|
+
this.dbStatus = db.getRuntimeStatus();
|
|
655
|
+
}
|
|
678
656
|
return this.db;
|
|
679
657
|
}
|
|
658
|
+
async ensureDbAsync() {
|
|
659
|
+
if (this.db)
|
|
660
|
+
return this.db;
|
|
661
|
+
return new codexDatabase_1.CodexDatabase(getCodexHome());
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Cache an async-created database only after an operation proved it usable.
|
|
665
|
+
* Sync paths treat a cached `db` as "open() succeeded", so adopting an
|
|
666
|
+
* unproven instance would silently disable their file fallbacks.
|
|
667
|
+
*/
|
|
668
|
+
adoptDbIfAvailable(db) {
|
|
669
|
+
if (this.db || !db.getRuntimeStatus().available)
|
|
670
|
+
return;
|
|
671
|
+
this.db = db;
|
|
672
|
+
this.dbInitialized = true;
|
|
673
|
+
}
|
|
674
|
+
getRuntimeStatus() {
|
|
675
|
+
const db = this.ensureDb();
|
|
676
|
+
return db && typeof db.getRuntimeStatus === 'function' ? db.getRuntimeStatus() : this.dbStatus;
|
|
677
|
+
}
|
|
678
|
+
getLastOperationStatus() {
|
|
679
|
+
return this.diagnostics.getLastOperationStatus();
|
|
680
|
+
}
|
|
680
681
|
// --- Path resolution ---
|
|
681
682
|
getSessionDirectory(workspacePath) {
|
|
682
683
|
// Codex doesn't use workspace-encoded directories like Claude Code.
|
|
@@ -686,10 +687,13 @@ class CodexProvider {
|
|
|
686
687
|
if (db) {
|
|
687
688
|
const thread = db.getMostRecentThread(workspacePath);
|
|
688
689
|
if (thread?.rollout_path) {
|
|
690
|
+
this.recordDatabaseFallback('getSessionDirectory', true);
|
|
689
691
|
return path.dirname(thread.rollout_path);
|
|
690
692
|
}
|
|
691
693
|
}
|
|
692
|
-
|
|
694
|
+
const directory = getSessionsDir();
|
|
695
|
+
this.recordDatabaseFallback('getSessionDirectory', true);
|
|
696
|
+
return directory;
|
|
693
697
|
}
|
|
694
698
|
discoverSessionDirectory(workspacePath) {
|
|
695
699
|
// DB: find latest rollout directory for this workspace
|
|
@@ -697,23 +701,28 @@ class CodexProvider {
|
|
|
697
701
|
if (db) {
|
|
698
702
|
const thread = db.getMostRecentThread(workspacePath);
|
|
699
703
|
if (thread?.rollout_path && fs.existsSync(thread.rollout_path)) {
|
|
704
|
+
this.recordDatabaseFallback('discoverSessionDirectory', true);
|
|
700
705
|
return path.dirname(thread.rollout_path);
|
|
701
706
|
}
|
|
702
707
|
}
|
|
703
708
|
// File scan: look for any rollout file whose session_meta.cwd matches
|
|
709
|
+
// (newest first; the cwd read is cached per path).
|
|
704
710
|
const files = findRolloutFilesInConfiguredHomes();
|
|
705
|
-
if (files.length === 0)
|
|
711
|
+
if (files.length === 0) {
|
|
712
|
+
this.recordDatabaseFallback('discoverSessionDirectory', true);
|
|
706
713
|
return null;
|
|
707
|
-
|
|
708
|
-
files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
714
|
+
}
|
|
709
715
|
for (const file of files) {
|
|
710
|
-
const
|
|
711
|
-
if (
|
|
716
|
+
const cwd = readSessionCwd(file.path);
|
|
717
|
+
if (cwd && cwdMatches(cwd, workspacePath)) {
|
|
718
|
+
this.recordDatabaseFallback('discoverSessionDirectory', true);
|
|
712
719
|
return path.dirname(file.path);
|
|
713
720
|
}
|
|
714
721
|
}
|
|
715
722
|
// Fallback: return the most relevant base sessions dir if one exists
|
|
716
|
-
|
|
723
|
+
const directory = getSessionsDir();
|
|
724
|
+
this.recordDatabaseFallback('discoverSessionDirectory', true);
|
|
725
|
+
return directory;
|
|
717
726
|
}
|
|
718
727
|
// --- Session discovery ---
|
|
719
728
|
findActiveSession(workspacePath) {
|
|
@@ -722,18 +731,19 @@ class CodexProvider {
|
|
|
722
731
|
if (db) {
|
|
723
732
|
const thread = db.getMostRecentThread(workspacePath);
|
|
724
733
|
if (thread?.rollout_path && fs.existsSync(thread.rollout_path)) {
|
|
734
|
+
this.recordDatabaseFallback('findActiveSession', true);
|
|
725
735
|
return thread.rollout_path;
|
|
726
736
|
}
|
|
727
737
|
}
|
|
728
|
-
// File scan fallback
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
if (meta && cwdMatches(meta.cwd, workspacePath)) {
|
|
738
|
+
// File scan fallback (newest first; cwd reads are cached per path)
|
|
739
|
+
for (const file of findRolloutFilesInConfiguredHomes()) {
|
|
740
|
+
const cwd = readSessionCwd(file.path);
|
|
741
|
+
if (cwd && cwdMatches(cwd, workspacePath)) {
|
|
742
|
+
this.recordDatabaseFallback('findActiveSession', true);
|
|
734
743
|
return file.path;
|
|
735
744
|
}
|
|
736
745
|
}
|
|
746
|
+
this.recordDatabaseFallback('findActiveSession', true);
|
|
737
747
|
return null;
|
|
738
748
|
}
|
|
739
749
|
findAllSessions(workspacePath) {
|
|
@@ -744,30 +754,62 @@ class CodexProvider {
|
|
|
744
754
|
const dbPaths = threads
|
|
745
755
|
.filter((t) => t.rollout_path && fs.existsSync(t.rollout_path))
|
|
746
756
|
.map((t) => t.rollout_path);
|
|
747
|
-
if (dbPaths.length > 0)
|
|
757
|
+
if (dbPaths.length > 0) {
|
|
758
|
+
this.recordDatabaseFallback('findAllSessions', true);
|
|
748
759
|
return dbPaths;
|
|
760
|
+
}
|
|
749
761
|
}
|
|
750
|
-
// File scan
|
|
751
|
-
const
|
|
752
|
-
files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
753
|
-
return files
|
|
762
|
+
// File scan (newest first; cwd reads are cached per path)
|
|
763
|
+
const sessions = findRolloutFilesInConfiguredHomes()
|
|
754
764
|
.filter((f) => {
|
|
755
|
-
const
|
|
756
|
-
return
|
|
765
|
+
const cwd = readSessionCwd(f.path);
|
|
766
|
+
return cwd !== null && cwdMatches(cwd, workspacePath);
|
|
757
767
|
})
|
|
758
768
|
.map((f) => f.path);
|
|
769
|
+
this.recordDatabaseFallback('findAllSessions', true);
|
|
770
|
+
return sessions;
|
|
771
|
+
}
|
|
772
|
+
findSessionById(_workspacePath, sessionId) {
|
|
773
|
+
try {
|
|
774
|
+
const found = findCodexRolloutFile(sessionId);
|
|
775
|
+
this.recordDatabaseFallback('findSessionById', true);
|
|
776
|
+
return found;
|
|
777
|
+
}
|
|
778
|
+
catch {
|
|
779
|
+
this.recordDatabaseFallback('findSessionById', false);
|
|
780
|
+
return null;
|
|
781
|
+
}
|
|
759
782
|
}
|
|
760
783
|
/** Backward-compatible alias for findAllSessions. */
|
|
761
784
|
findSessionFiles(workspacePath) {
|
|
762
785
|
return this.findAllSessions(workspacePath);
|
|
763
786
|
}
|
|
764
787
|
findSessionsInDirectory(dir) {
|
|
765
|
-
|
|
766
|
-
files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
767
|
-
return files.map((f) => f.path);
|
|
788
|
+
return findRolloutFiles(dir).map((f) => f.path);
|
|
768
789
|
}
|
|
769
790
|
listAllSessionFiles() {
|
|
770
|
-
|
|
791
|
+
const files = findRolloutFilesInConfiguredHomes();
|
|
792
|
+
this.recordDatabaseFallback('listAllSessionFiles', true);
|
|
793
|
+
return files;
|
|
794
|
+
}
|
|
795
|
+
async listSessionFilesAsync(workspacePath) {
|
|
796
|
+
const results = [];
|
|
797
|
+
// One capped walk across the homes (deduplicated, newest first).
|
|
798
|
+
for (const file of await (0, rolloutWalker_1.walkRolloutFilesAsync)(getSessionsDirs())) {
|
|
799
|
+
const info = { ...file };
|
|
800
|
+
if (workspacePath) {
|
|
801
|
+
const cwd = await readSessionCwdAsync(file.path);
|
|
802
|
+
if (!cwd || !cwdMatches(cwd, workspacePath))
|
|
803
|
+
continue;
|
|
804
|
+
info.workspacePath = cwd;
|
|
805
|
+
}
|
|
806
|
+
results.push(info);
|
|
807
|
+
}
|
|
808
|
+
// Filesystem enumeration bypasses the database, so record that here as
|
|
809
|
+
// the sync listing paths do; otherwise `getLastOperationStatus()` keeps
|
|
810
|
+
// describing whichever operation ran before this one.
|
|
811
|
+
this.recordDatabaseFallback('listSessionFilesAsync', true);
|
|
812
|
+
return results;
|
|
771
813
|
}
|
|
772
814
|
getAllProjectFolders(workspacePath) {
|
|
773
815
|
const folders = [];
|
|
@@ -787,12 +829,17 @@ class CodexProvider {
|
|
|
787
829
|
});
|
|
788
830
|
}
|
|
789
831
|
}
|
|
790
|
-
// File scan supplement
|
|
832
|
+
// File scan supplement. When the index answered, only rollouts written
|
|
833
|
+
// after its last update can be missing from it, so older files are not
|
|
834
|
+
// opened; the cwd read itself is cached per path either way.
|
|
835
|
+
const indexedUpTo = db ? db.getDbMtime() : 0;
|
|
791
836
|
for (const file of findRolloutFilesInConfiguredHomes()) {
|
|
792
|
-
|
|
793
|
-
|
|
837
|
+
if (db && indexedUpTo > 0 && file.mtime.getTime() <= indexedUpTo)
|
|
838
|
+
continue;
|
|
839
|
+
const cwd = readSessionCwd(file.path);
|
|
840
|
+
if (!cwd)
|
|
794
841
|
continue;
|
|
795
|
-
const existing = seenCwds.get(
|
|
842
|
+
const existing = seenCwds.get(cwd);
|
|
796
843
|
if (existing) {
|
|
797
844
|
// Update mtime if newer
|
|
798
845
|
if (file.mtime > existing.lastModified) {
|
|
@@ -800,10 +847,10 @@ class CodexProvider {
|
|
|
800
847
|
}
|
|
801
848
|
}
|
|
802
849
|
else {
|
|
803
|
-
seenCwds.set(
|
|
850
|
+
seenCwds.set(cwd, {
|
|
804
851
|
dir: path.dirname(file.path),
|
|
805
|
-
name:
|
|
806
|
-
encodedName:
|
|
852
|
+
name: cwd,
|
|
853
|
+
encodedName: cwd,
|
|
807
854
|
sessionCount: 1,
|
|
808
855
|
lastModified: file.mtime,
|
|
809
856
|
});
|
|
@@ -823,32 +870,69 @@ class CodexProvider {
|
|
|
823
870
|
}
|
|
824
871
|
return b.lastModified.getTime() - a.lastModified.getTime();
|
|
825
872
|
});
|
|
873
|
+
this.recordDatabaseFallback('getAllProjectFolders', true);
|
|
826
874
|
return folders;
|
|
827
875
|
}
|
|
828
876
|
// --- File identification ---
|
|
829
877
|
isSessionFile(filename) {
|
|
830
|
-
return isRolloutFile(filename);
|
|
878
|
+
return (0, rolloutWalker_1.isRolloutFile)(filename);
|
|
831
879
|
}
|
|
832
880
|
getSessionId(sessionPath) {
|
|
833
|
-
return
|
|
881
|
+
return (0, rolloutWalker_1.extractRolloutSessionId)(path.basename(sessionPath));
|
|
834
882
|
}
|
|
835
883
|
encodeWorkspacePath(workspacePath) {
|
|
836
884
|
// Codex doesn't encode workspace paths in directory names
|
|
837
885
|
return workspacePath;
|
|
838
886
|
}
|
|
839
|
-
|
|
840
|
-
|
|
887
|
+
/** Thread title or first user message from the SQLite index; null without it. */
|
|
888
|
+
extractSessionLabelFromDb(sessionPath) {
|
|
841
889
|
const db = this.ensureDb();
|
|
842
|
-
if (db)
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
890
|
+
if (!db)
|
|
891
|
+
return null;
|
|
892
|
+
const sessionId = this.getSessionId(sessionPath);
|
|
893
|
+
const thread = db.getThread(sessionId);
|
|
894
|
+
if (thread?.title) {
|
|
895
|
+
this.recordDatabaseFallback('extractSessionLabel', true);
|
|
896
|
+
return truncate(thread.title, 60);
|
|
897
|
+
}
|
|
898
|
+
if (thread?.first_user_message) {
|
|
899
|
+
this.recordDatabaseFallback('extractSessionLabel', true);
|
|
900
|
+
return truncate(thread.first_user_message, 60);
|
|
849
901
|
}
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
extractSessionLabel(sessionPath) {
|
|
905
|
+
const fromDb = this.extractSessionLabelFromDb(sessionPath);
|
|
906
|
+
if (fromDb)
|
|
907
|
+
return fromDb;
|
|
850
908
|
// File fallback: parse first user message
|
|
851
|
-
|
|
909
|
+
const label = extractFirstUserMessage(sessionPath);
|
|
910
|
+
this.recordDatabaseFallback('extractSessionLabel', true);
|
|
911
|
+
return label;
|
|
912
|
+
}
|
|
913
|
+
async extractSessionLabelsAsync(sessionPaths) {
|
|
914
|
+
const results = new Map();
|
|
915
|
+
const byId = new Map(sessionPaths.map((sessionPath) => [this.getSessionId(sessionPath), sessionPath]));
|
|
916
|
+
const db = await this.ensureDbAsync();
|
|
917
|
+
const threads = await db.getThreadsByIdsAsync([...byId.keys()]);
|
|
918
|
+
for (const thread of threads) {
|
|
919
|
+
const sessionPath = byId.get(thread.id);
|
|
920
|
+
if (!sessionPath)
|
|
921
|
+
continue;
|
|
922
|
+
const label = thread.title ?? thread.first_user_message;
|
|
923
|
+
if (label)
|
|
924
|
+
results.set(sessionPath, truncate(label, 60));
|
|
925
|
+
}
|
|
926
|
+
this.dbStatus = db.getRuntimeStatus();
|
|
927
|
+
this.adoptDbIfAvailable(db);
|
|
928
|
+
for (const sessionPath of sessionPaths) {
|
|
929
|
+
if (!results.has(sessionPath)) {
|
|
930
|
+
results.set(sessionPath, await extractFirstUserMessageAsync(sessionPath));
|
|
931
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
this.recordDatabaseFallback('extractSessionLabelsAsync', true);
|
|
935
|
+
return results;
|
|
852
936
|
}
|
|
853
937
|
// --- Data reading ---
|
|
854
938
|
createReader(sessionPath) {
|
|
@@ -884,15 +968,13 @@ class CodexProvider {
|
|
|
884
968
|
if (forkedThreads.length > 0)
|
|
885
969
|
return subagents;
|
|
886
970
|
}
|
|
887
|
-
// Filesystem fallback: scan
|
|
888
|
-
//
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
const filesToCheck = files.slice(0, 50);
|
|
892
|
-
for (const file of filesToCheck) {
|
|
971
|
+
// Filesystem fallback: scan recent rollout files for a matching
|
|
972
|
+
// forked_from_id. The walk stops after the 50 newest-dated files instead
|
|
973
|
+
// of enumerating the whole history and slicing afterwards.
|
|
974
|
+
for (const file of findRolloutFilesInConfiguredHomes({ limit: 50 })) {
|
|
893
975
|
const meta = readSessionMeta(file.path);
|
|
894
976
|
if (meta?.forked_from_id === sessionId) {
|
|
895
|
-
const childId =
|
|
977
|
+
const childId = (0, rolloutWalker_1.extractRolloutSessionId)(path.basename(file.path));
|
|
896
978
|
const stats = this.buildSubagentStats(childId, file.path, meta.source);
|
|
897
979
|
if (stats)
|
|
898
980
|
subagents.push(stats);
|
|
@@ -1015,54 +1097,17 @@ class CodexProvider {
|
|
|
1015
1097
|
getProjectsBaseDir() {
|
|
1016
1098
|
return getSessionsDir();
|
|
1017
1099
|
}
|
|
1100
|
+
getWatchRoots() {
|
|
1101
|
+
return getSessionsDirs();
|
|
1102
|
+
}
|
|
1018
1103
|
// --- Stats ---
|
|
1019
1104
|
readSessionStats(sessionPath) {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1105
|
+
// One reader pass computes stats and the label: the SQLite thread title is
|
|
1106
|
+
// used when the index is available, otherwise the first user prompt comes
|
|
1107
|
+
// from the events already read, so the rollout is never opened twice.
|
|
1108
|
+
return (0, sessionStats_1.readSessionFileStats)(this, sessionPath, {
|
|
1109
|
+
resolveLabel: () => this.extractSessionLabelFromDb(sessionPath),
|
|
1024
1110
|
});
|
|
1025
|
-
try {
|
|
1026
|
-
const reader = this.createReader(sessionPath);
|
|
1027
|
-
for (const event of reader.readAll()) {
|
|
1028
|
-
aggregator.processEvent(event);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
catch (err) {
|
|
1032
|
-
// Return an empty stats shell below. Surface the cause under DEBUG so a
|
|
1033
|
-
// malformed rollout doesn't fail silently during dashboard refreshes.
|
|
1034
|
-
if (process.env.DEBUG)
|
|
1035
|
-
console.error(`CodexProvider.readSessionStats: ${err}`);
|
|
1036
|
-
}
|
|
1037
|
-
const metrics = aggregator.getMetrics();
|
|
1038
|
-
const modelUsage = {};
|
|
1039
|
-
for (const model of metrics.modelStats) {
|
|
1040
|
-
modelUsage[model.model] = { calls: model.calls, tokens: model.tokens };
|
|
1041
|
-
}
|
|
1042
|
-
const toolUsage = {};
|
|
1043
|
-
for (const tool of metrics.toolStats) {
|
|
1044
|
-
toolUsage[tool.name] = tool.successCount + tool.failureCount + tool.pendingCount;
|
|
1045
|
-
}
|
|
1046
|
-
return {
|
|
1047
|
-
providerId: 'codex',
|
|
1048
|
-
sessionId,
|
|
1049
|
-
filePath: sessionPath,
|
|
1050
|
-
label: this.extractSessionLabel(sessionPath),
|
|
1051
|
-
startTime: metrics.sessionStartTime || '',
|
|
1052
|
-
endTime: metrics.lastEventTime || '',
|
|
1053
|
-
messageCount: metrics.messageCount,
|
|
1054
|
-
tokens: {
|
|
1055
|
-
input: metrics.tokens.inputTokens,
|
|
1056
|
-
output: metrics.tokens.outputTokens,
|
|
1057
|
-
cacheWrite: metrics.tokens.cacheWriteTokens,
|
|
1058
|
-
cacheRead: metrics.tokens.cacheReadTokens,
|
|
1059
|
-
},
|
|
1060
|
-
modelUsage,
|
|
1061
|
-
toolUsage,
|
|
1062
|
-
compactionEstimate: metrics.compactionCount,
|
|
1063
|
-
truncationCount: metrics.truncationCount,
|
|
1064
|
-
reportedCost: metrics.tokens.reportedCost,
|
|
1065
|
-
};
|
|
1066
1111
|
}
|
|
1067
1112
|
readSessionContextSnapshot(sessionPath, options = {}) {
|
|
1068
1113
|
return (0, sessionContext_1.readSessionContextSnapshot)(this, sessionPath, options);
|
|
@@ -1094,9 +1139,28 @@ class CodexProvider {
|
|
|
1094
1139
|
this.db?.close();
|
|
1095
1140
|
this.db = null;
|
|
1096
1141
|
this.dbInitialized = false;
|
|
1142
|
+
this.dbStatus = { available: false, kind: 'db_missing' };
|
|
1097
1143
|
this.dynamicContextWindowLimit = null;
|
|
1098
1144
|
this.lastRateLimitsData = null;
|
|
1099
1145
|
this.lastTokenUsageData = null;
|
|
1100
1146
|
}
|
|
1147
|
+
recordDatabaseFallback(operation, usable) {
|
|
1148
|
+
if (!this.dbInitialized) {
|
|
1149
|
+
this.diagnostics.available(operation);
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
const status = this.getRuntimeStatus();
|
|
1153
|
+
const diagnostic = (0, diagnostics_1.diagnosticFromRuntimeStatus)(this.id, 'query', status);
|
|
1154
|
+
if (!diagnostic) {
|
|
1155
|
+
this.diagnostics.available(operation);
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
this.diagnostics.degraded(operation, status, {
|
|
1159
|
+
kind: diagnostic.kind,
|
|
1160
|
+
severity: diagnostic.severity,
|
|
1161
|
+
phase: diagnostic.phase,
|
|
1162
|
+
message: diagnostic.message,
|
|
1163
|
+
}, usable);
|
|
1164
|
+
}
|
|
1101
1165
|
}
|
|
1102
1166
|
exports.CodexProvider = CodexProvider;
|