sidekick-shared 0.23.0 → 0.24.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/README.md +139 -57
- package/dist/accountManager.js +10 -0
- package/dist/aggregation/EventAggregator.d.ts +8 -1
- package/dist/aggregation/EventAggregator.js +82 -136
- package/dist/aggregation/snapshot.d.ts +1 -1
- package/dist/aggregation/snapshot.js +2 -1
- package/dist/aggregation/types.d.ts +5 -0
- package/dist/autoSwitch.d.ts +1 -1
- package/dist/autoSwitch.js +14 -3
- package/dist/browser.d.ts +6 -0
- package/dist/browser.js +10 -1
- package/dist/codexQuota.d.ts +2 -0
- package/dist/codexQuota.js +27 -0
- package/dist/codexQuotaWatcher.d.ts +7 -0
- package/dist/codexQuotaWatcher.js +37 -19
- package/dist/context/sessionContext.js +43 -15
- package/dist/credentialIO.js +19 -5
- package/dist/errorHistory.d.ts +1 -0
- package/dist/errorHistory.js +3 -2
- package/dist/extractors/tokenUsage.js +1 -1
- package/dist/extractors/toolCall.js +2 -2
- package/dist/index.d.ts +14 -4
- package/dist/index.js +25 -7
- package/dist/modelInfo.d.ts +17 -7
- package/dist/modelInfo.js +11 -2
- package/dist/multiProviderQuotaService.js +13 -2
- package/dist/node.d.ts +4 -0
- package/dist/node.js +8 -1
- package/dist/observedSessionCollector.d.ts +68 -0
- package/dist/observedSessionCollector.js +178 -0
- package/dist/parsers/codexParser.js +24 -6
- package/dist/parsers/openCodeParser.js +22 -1
- package/dist/parsers/sessionActivityDetector.js +2 -0
- package/dist/parsers/sessionPathResolver.js +19 -16
- package/dist/parsers/subagentScanner.d.ts +1 -1
- package/dist/parsers/subagentScanner.js +4 -5
- package/dist/parsers/subagentTraceParser.js +15 -12
- package/dist/providerStatus.js +13 -2
- package/dist/providers/claudeCode.js +63 -5
- package/dist/providers/codex.js +56 -17
- package/dist/providers/detect.js +32 -3
- package/dist/providers/openCodeDatabase.js +12 -3
- package/dist/quota.js +8 -0
- package/dist/quotaPoller.js +17 -6
- package/dist/quotaSnapshots.js +80 -26
- package/dist/readers/decisions.js +3 -1
- package/dist/readers/handoff.js +1 -1
- package/dist/readers/notes.js +9 -4
- package/dist/readers/plans.js +2 -4
- package/dist/readers/tasks.js +3 -1
- package/dist/report/htmlHelpers.js +6 -1
- package/dist/report/openBrowser.js +3 -3
- package/dist/report/transcriptParser.d.ts +3 -11
- package/dist/report/transcriptParser.js +69 -154
- package/dist/schemas/index.d.ts +7 -1
- package/dist/schemas/index.js +18 -2
- package/dist/schemas/observedSessionV1.d.ts +32 -1
- package/dist/schemas/observedSessionV1.js +11 -1
- package/dist/schemas/sessionEvent.d.ts +353 -12
- package/dist/schemas/sessionEvent.js +76 -14
- package/dist/schemas/tokenEstimation.d.ts +14 -0
- package/dist/schemas/tokenEstimation.js +10 -0
- package/dist/schemas/transcript.d.ts +24 -0
- package/dist/schemas/transcript.js +81 -0
- package/dist/schemas/usageNormalization.d.ts +55 -0
- package/dist/schemas/usageNormalization.js +41 -0
- package/dist/sessionTranscripts.d.ts +16 -0
- package/dist/sessionTranscripts.js +47 -0
- package/dist/statusline/formatter.js +8 -1
- package/dist/terminalSync.js +16 -7
- package/dist/tokenEstimation.d.ts +24 -0
- package/dist/tokenEstimation.js +62 -0
- package/dist/transcript.d.ts +92 -0
- package/dist/transcript.js +240 -0
- package/dist/types/historicalData.d.ts +14 -0
- package/dist/types/historicalData.js +22 -0
- package/dist/types/observedSessionV1.d.ts +3 -1
- package/dist/types/observedSessionV1.js +20 -6
- package/dist/types/sessionEvent.d.ts +66 -7
- package/dist/usageNormalization.d.ts +69 -0
- package/dist/usageNormalization.js +100 -0
- package/dist/watchers/factory.js +14 -3
- package/dist/watchers/jsonlTail.js +34 -14
- package/dist/watchers/jsonlWatcher.js +5 -3
- package/dist/watchers/providerReaderWatcher.js +4 -2
- package/dist/watchers/sqliteWatcher.d.ts +2 -0
- package/dist/watchers/sqliteWatcher.js +27 -14
- package/dist/writers/atomic.js +100 -13
- package/dist/zaiQuotaWatcher.js +6 -0
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @module aggregation/EventAggregator
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.EventAggregator = void 0;
|
|
12
|
+
exports.EventAggregator = exports.SNAPSHOT_SCHEMA_VERSION = void 0;
|
|
13
13
|
exports.parseTodoDependencies = parseTodoDependencies;
|
|
14
14
|
const jsonl_1 = require("../parsers/jsonl");
|
|
15
15
|
const planExtractor_1 = require("../parsers/planExtractor");
|
|
@@ -19,16 +19,18 @@ const noiseClassifier_1 = require("../formatters/noiseClassifier");
|
|
|
19
19
|
const FrequencyTracker_1 = require("./FrequencyTracker");
|
|
20
20
|
const HeatmapTracker_1 = require("./HeatmapTracker");
|
|
21
21
|
const PatternExtractor_1 = require("./PatternExtractor");
|
|
22
|
-
const modelInfo_1 = require("../modelInfo");
|
|
23
22
|
const errorTaxonomy_1 = require("../extractors/errorTaxonomy");
|
|
23
|
+
const tokenEstimation_1 = require("../tokenEstimation");
|
|
24
|
+
const usageNormalization_1 = require("../usageNormalization");
|
|
24
25
|
// ── Defaults ──
|
|
25
26
|
const DEFAULT_TIMELINE_CAP = 200;
|
|
26
27
|
const DEFAULT_LATENCY_CAP = 100;
|
|
28
|
+
const DEFAULT_CONTEXT_TIMELINE_CAP = 2_000;
|
|
27
29
|
const DEFAULT_BURN_WINDOW_MS = 5 * 60_000;
|
|
28
30
|
const DEFAULT_BURN_SAMPLE_MS = 10_000;
|
|
29
31
|
const COMPACTION_DROP_THRESHOLD = 0.8; // >20% drop
|
|
30
32
|
/** Schema version for serialized snapshots. */
|
|
31
|
-
|
|
33
|
+
exports.SNAPSHOT_SCHEMA_VERSION = 4;
|
|
32
34
|
// ── Goal gate detection regex ──
|
|
33
35
|
const GOAL_GATE_REGEX = /\b(CRITICAL|MUST|blocker|required|must.?complete|goal.?gate|essential|do.?not.?skip|blocking)\b/i;
|
|
34
36
|
// ── Todo dependency parsing ──
|
|
@@ -74,6 +76,8 @@ class EventAggregator {
|
|
|
74
76
|
outputTokens = 0;
|
|
75
77
|
cacheWriteTokens = 0;
|
|
76
78
|
cacheReadTokens = 0;
|
|
79
|
+
reasoningTokens = 0;
|
|
80
|
+
totalTokens = 0;
|
|
77
81
|
reportedCost = 0;
|
|
78
82
|
// Per-model usage
|
|
79
83
|
modelUsage = new Map();
|
|
@@ -127,6 +131,7 @@ class EventAggregator {
|
|
|
127
131
|
permissionModeHistory = [];
|
|
128
132
|
// Context timeline
|
|
129
133
|
contextTimeline = [];
|
|
134
|
+
contextTimelineCap;
|
|
130
135
|
contextTurnIndex = 0;
|
|
131
136
|
// Timeline
|
|
132
137
|
timeline = [];
|
|
@@ -150,6 +155,7 @@ class EventAggregator {
|
|
|
150
155
|
this.latencyCap = options?.latencyCap ?? DEFAULT_LATENCY_CAP;
|
|
151
156
|
this.burnWindowMs = options?.burnWindowMs ?? DEFAULT_BURN_WINDOW_MS;
|
|
152
157
|
this.burnSampleMs = options?.burnSampleMs ?? DEFAULT_BURN_SAMPLE_MS;
|
|
158
|
+
this.contextTimelineCap = options?.contextTimelineCap ?? DEFAULT_CONTEXT_TIMELINE_CAP;
|
|
153
159
|
this.computeContextSize = options?.computeContextSize ?? null;
|
|
154
160
|
this.providerId = options?.providerId ?? null;
|
|
155
161
|
this._providerId = this.providerId;
|
|
@@ -165,18 +171,25 @@ class EventAggregator {
|
|
|
165
171
|
this.sessionStartTime = event.timestamp;
|
|
166
172
|
}
|
|
167
173
|
this.lastEventTime = event.timestamp;
|
|
168
|
-
|
|
174
|
+
this.recordProviderMetadata(event);
|
|
175
|
+
// Bookkeeping rows can be useful without a message payload.
|
|
169
176
|
if (!event.message) {
|
|
177
|
+
if (event.type === 'summary') {
|
|
178
|
+
this.recordExplicitCompaction(event.timestamp, event.compaction);
|
|
179
|
+
}
|
|
180
|
+
this.trackPermissionMode(event);
|
|
181
|
+
this.addTimelineFromSessionEvent(event);
|
|
182
|
+
this.feedAnalytics(event.timestamp, event.type === 'summary' ? (event.summary ?? '') : '', this.extractToolNameFromEvent(event));
|
|
170
183
|
return;
|
|
171
184
|
}
|
|
172
185
|
// 2. Track model
|
|
173
186
|
if (event.message.model) {
|
|
174
187
|
this.currentModel = event.message.model;
|
|
175
188
|
}
|
|
176
|
-
this.recordProviderMetadata(event);
|
|
177
189
|
// 3. Skip system/audit events and synthetic token-count events for messageCount
|
|
178
190
|
const msgId = event.message.id ?? '';
|
|
179
|
-
if (event.type
|
|
191
|
+
if ((event.type === 'user' || event.type === 'assistant') &&
|
|
192
|
+
!msgId.startsWith('token-count-')) {
|
|
180
193
|
this.messageCount++;
|
|
181
194
|
}
|
|
182
195
|
// 4. Latency tracking
|
|
@@ -184,9 +197,10 @@ class EventAggregator {
|
|
|
184
197
|
this.processLatency(event);
|
|
185
198
|
}
|
|
186
199
|
// 5. Token accumulation
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
this.
|
|
200
|
+
const normalizedUsage = (0, usageNormalization_1.extractNormalizedUsage)(event);
|
|
201
|
+
if (normalizedUsage) {
|
|
202
|
+
const usageCost = this.accumulateUsage(normalizedUsage, event.timestamp, event.message.model);
|
|
203
|
+
this.attributePlanUsage(normalizedUsage.totalTokens, usageCost);
|
|
190
204
|
}
|
|
191
205
|
if (event.type !== 'system') {
|
|
192
206
|
// 6. Tool extraction from content blocks
|
|
@@ -232,91 +246,25 @@ class EventAggregator {
|
|
|
232
246
|
}
|
|
233
247
|
// 3. Token accumulation from FollowEvent fields
|
|
234
248
|
if (event.tokens) {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
contextSize = this.computeContextSize({
|
|
250
|
-
inputTokens: inputTok,
|
|
251
|
-
outputTokens: outputTok,
|
|
252
|
-
cacheWriteTokens: cacheWrite,
|
|
253
|
-
cacheReadTokens: cacheRead,
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
contextSize = inputTok + cacheWrite + cacheRead;
|
|
258
|
-
}
|
|
259
|
-
// Compaction detection
|
|
260
|
-
if (this.resolvePendingHeuristicCompaction(contextSize)) {
|
|
261
|
-
// The drop belongs to the explicit compaction marker just recorded.
|
|
262
|
-
}
|
|
263
|
-
else if (this.previousContextSize > 0 &&
|
|
264
|
-
contextSize < this.previousContextSize * COMPACTION_DROP_THRESHOLD) {
|
|
265
|
-
this.compactionEvents.push({
|
|
266
|
-
timestamp: new Date(event.timestamp),
|
|
267
|
-
contextBefore: this.previousContextSize,
|
|
268
|
-
contextAfter: contextSize,
|
|
269
|
-
tokensReclaimed: this.previousContextSize - contextSize,
|
|
270
|
-
source: 'heuristic',
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
this.previousContextSize = contextSize;
|
|
274
|
-
this.currentContextSize = contextSize;
|
|
275
|
-
// Per-model usage. If the provider reported a cost, use it; otherwise
|
|
276
|
-
// resolve pricing from the catalog (null = unpriced → priced: false,
|
|
277
|
-
// UI renders "—").
|
|
278
|
-
if (event.model) {
|
|
279
|
-
const model = event.model;
|
|
280
|
-
const pricing = event.cost ? null : (0, modelInfo_1.getModelPricing)(model);
|
|
281
|
-
const computed = event.cost
|
|
282
|
-
? event.cost
|
|
283
|
-
: pricing
|
|
284
|
-
? (0, modelInfo_1.calculateCostWithPricing)({
|
|
285
|
-
inputTokens: inputTok,
|
|
286
|
-
outputTokens: outputTok,
|
|
287
|
-
cacheWriteTokens: cacheWrite,
|
|
288
|
-
cacheReadTokens: cacheRead,
|
|
289
|
-
}, pricing)
|
|
290
|
-
: 0;
|
|
291
|
-
const evPriced = event.cost != null || pricing != null;
|
|
292
|
-
const acc = this.modelUsage.get(model) ?? {
|
|
293
|
-
calls: 0,
|
|
294
|
-
tokens: 0,
|
|
295
|
-
inputTokens: 0,
|
|
296
|
-
outputTokens: 0,
|
|
297
|
-
cacheWriteTokens: 0,
|
|
298
|
-
cacheReadTokens: 0,
|
|
299
|
-
cost: 0,
|
|
300
|
-
priced: true,
|
|
301
|
-
};
|
|
302
|
-
acc.calls++;
|
|
303
|
-
acc.tokens += inputTok + outputTok;
|
|
304
|
-
acc.inputTokens += inputTok;
|
|
305
|
-
acc.outputTokens += outputTok;
|
|
306
|
-
acc.cacheWriteTokens += cacheWrite;
|
|
307
|
-
acc.cacheReadTokens += cacheRead;
|
|
308
|
-
acc.cost += computed;
|
|
309
|
-
if (!evPriced)
|
|
310
|
-
acc.priced = false;
|
|
311
|
-
this.modelUsage.set(model, acc);
|
|
312
|
-
}
|
|
249
|
+
const normalized = (0, usageNormalization_1.normalizeProviderUsage)({
|
|
250
|
+
semantics: 'sidekick',
|
|
251
|
+
provider: event.providerId,
|
|
252
|
+
source: 'follow-event',
|
|
253
|
+
model: event.model,
|
|
254
|
+
inputTokens: event.tokens.input,
|
|
255
|
+
outputTokens: event.tokens.output,
|
|
256
|
+
cacheReadTokens: event.cacheTokens?.read,
|
|
257
|
+
cacheWriteTokens: event.cacheTokens?.write,
|
|
258
|
+
reasoningIncludedInOutput: false,
|
|
259
|
+
reportedCostUsd: event.cost,
|
|
260
|
+
});
|
|
261
|
+
const cost = this.accumulateUsage(normalized, event.timestamp, event.model);
|
|
262
|
+
this.attributePlanUsage(normalized.totalTokens, cost);
|
|
313
263
|
}
|
|
314
|
-
|
|
315
|
-
|
|
264
|
+
else if (typeof event.cost === 'number' && Number.isFinite(event.cost) && event.cost >= 0) {
|
|
265
|
+
// Compatibility for cost-only follow events.
|
|
316
266
|
this.reportedCost += event.cost;
|
|
317
267
|
}
|
|
318
|
-
// Burn rate sampling
|
|
319
|
-
this.updateBurnRate(event.timestamp);
|
|
320
268
|
// 4. Tool tracking from FollowEvent
|
|
321
269
|
if (event.type === 'tool_use' && event.toolName) {
|
|
322
270
|
this.recordFollowToolUse(event);
|
|
@@ -346,7 +294,7 @@ class EventAggregator {
|
|
|
346
294
|
// 12. Gonzo/Lazyjournal analytics
|
|
347
295
|
this.feedAnalytics(event.timestamp, event.summary, event.toolName);
|
|
348
296
|
// Message count (skip system events)
|
|
349
|
-
if (event.type
|
|
297
|
+
if (event.type === 'user' || event.type === 'assistant') {
|
|
350
298
|
this.messageCount++;
|
|
351
299
|
}
|
|
352
300
|
}
|
|
@@ -359,6 +307,8 @@ class EventAggregator {
|
|
|
359
307
|
outputTokens: this.outputTokens,
|
|
360
308
|
cacheWriteTokens: this.cacheWriteTokens,
|
|
361
309
|
cacheReadTokens: this.cacheReadTokens,
|
|
310
|
+
reasoningTokens: this.reasoningTokens,
|
|
311
|
+
totalTokens: this.totalTokens,
|
|
362
312
|
reportedCost: this.reportedCost,
|
|
363
313
|
};
|
|
364
314
|
}
|
|
@@ -475,6 +425,8 @@ class EventAggregator {
|
|
|
475
425
|
this.outputTokens = 0;
|
|
476
426
|
this.cacheWriteTokens = 0;
|
|
477
427
|
this.cacheReadTokens = 0;
|
|
428
|
+
this.reasoningTokens = 0;
|
|
429
|
+
this.totalTokens = 0;
|
|
478
430
|
this.reportedCost = 0;
|
|
479
431
|
this.modelUsage.clear();
|
|
480
432
|
this.currentContextSize = 0;
|
|
@@ -543,12 +495,14 @@ class EventAggregator {
|
|
|
543
495
|
/** Serializes all mutable state to a JSON-safe object for snapshot persistence. */
|
|
544
496
|
serialize() {
|
|
545
497
|
return {
|
|
546
|
-
version: SNAPSHOT_SCHEMA_VERSION,
|
|
498
|
+
version: exports.SNAPSHOT_SCHEMA_VERSION,
|
|
547
499
|
tokens: {
|
|
548
500
|
input: this.inputTokens,
|
|
549
501
|
output: this.outputTokens,
|
|
550
502
|
cacheWrite: this.cacheWriteTokens,
|
|
551
503
|
cacheRead: this.cacheReadTokens,
|
|
504
|
+
reasoning: this.reasoningTokens,
|
|
505
|
+
total: this.totalTokens,
|
|
552
506
|
reportedCost: this.reportedCost,
|
|
553
507
|
},
|
|
554
508
|
modelUsage: Array.from(this.modelUsage.entries()),
|
|
@@ -587,13 +541,15 @@ class EventAggregator {
|
|
|
587
541
|
}
|
|
588
542
|
/** Restores mutable state from a serialized snapshot. Clears transient state (pending calls). */
|
|
589
543
|
restore(state) {
|
|
590
|
-
if (state.version !== SNAPSHOT_SCHEMA_VERSION) {
|
|
591
|
-
return;
|
|
544
|
+
if (state.version !== exports.SNAPSHOT_SCHEMA_VERSION) {
|
|
545
|
+
return false;
|
|
592
546
|
}
|
|
593
547
|
this.inputTokens = state.tokens.input;
|
|
594
548
|
this.outputTokens = state.tokens.output;
|
|
595
549
|
this.cacheWriteTokens = state.tokens.cacheWrite;
|
|
596
550
|
this.cacheReadTokens = state.tokens.cacheRead;
|
|
551
|
+
this.reasoningTokens = state.tokens.reasoning;
|
|
552
|
+
this.totalTokens = state.tokens.total;
|
|
597
553
|
this.reportedCost = state.tokens.reportedCost;
|
|
598
554
|
this.modelUsage = new Map(state.modelUsage);
|
|
599
555
|
this.currentContextSize = state.contextSize;
|
|
@@ -617,7 +573,9 @@ class EventAggregator {
|
|
|
617
573
|
this.permissionModeHistory = state.permissionModeHistory
|
|
618
574
|
? [...state.permissionModeHistory]
|
|
619
575
|
: [];
|
|
620
|
-
this.contextTimeline = state.contextTimeline
|
|
576
|
+
this.contextTimeline = state.contextTimeline
|
|
577
|
+
? state.contextTimeline.slice(-this.contextTimelineCap)
|
|
578
|
+
: [];
|
|
621
579
|
this.contextTurnIndex = state.contextTurnIndex ?? 0;
|
|
622
580
|
this.timeline = [...state.timeline];
|
|
623
581
|
this.messageCount = state.messageCount;
|
|
@@ -640,46 +598,30 @@ class EventAggregator {
|
|
|
640
598
|
this.pendingSubagents.clear();
|
|
641
599
|
this.pendingUserRequest = null;
|
|
642
600
|
this.planExtractor.reset();
|
|
601
|
+
return true;
|
|
643
602
|
}
|
|
644
603
|
// ═══════════════════════════════════════════════════════════════════════
|
|
645
604
|
// Private: Token & Context
|
|
646
605
|
// ═══════════════════════════════════════════════════════════════════════
|
|
647
606
|
accumulateUsage(usage, timestamp, model) {
|
|
648
|
-
const inputTok = usage.
|
|
649
|
-
const outputTok = usage.
|
|
650
|
-
const cacheWrite = usage.
|
|
651
|
-
const cacheRead = usage.
|
|
652
|
-
const reasoningTok = usage.
|
|
653
|
-
const reported = usage.reported_cost ?? 0;
|
|
654
|
-
// Resolve provider pricing when we have a model ID. Lets sessions with no
|
|
655
|
-
// reported_cost (Claude Code, Codex) still show real dollar figures.
|
|
607
|
+
const inputTok = usage.uncachedInputTokens;
|
|
608
|
+
const outputTok = usage.outputTokens;
|
|
609
|
+
const cacheWrite = usage.cacheWriteTokens;
|
|
610
|
+
const cacheRead = usage.cacheReadTokens;
|
|
611
|
+
const reasoningTok = usage.reasoningTokens;
|
|
656
612
|
const modelKey = model ?? this.currentModel;
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (reported > 0) {
|
|
661
|
-
cost = reported;
|
|
662
|
-
}
|
|
663
|
-
else if (pricing) {
|
|
664
|
-
cost = (0, modelInfo_1.calculateCostWithPricing)({
|
|
665
|
-
inputTokens: inputTok,
|
|
666
|
-
outputTokens: outputTok,
|
|
667
|
-
cacheWriteTokens: cacheWrite,
|
|
668
|
-
cacheReadTokens: cacheRead,
|
|
669
|
-
reasoningTokens: reasoningTok,
|
|
670
|
-
}, pricing);
|
|
671
|
-
}
|
|
672
|
-
else {
|
|
673
|
-
cost = 0;
|
|
674
|
-
priced = false;
|
|
675
|
-
}
|
|
613
|
+
const pricedUsage = (0, usageNormalization_1.calculateNormalizedUsageCost)({ usage, modelId: modelKey ?? undefined });
|
|
614
|
+
const priced = pricedUsage.source !== 'unpriced';
|
|
615
|
+
const cost = pricedUsage.costUsd ?? 0;
|
|
676
616
|
this.inputTokens += inputTok;
|
|
677
617
|
this.outputTokens += outputTok;
|
|
678
618
|
this.cacheWriteTokens += cacheWrite;
|
|
679
619
|
this.cacheReadTokens += cacheRead;
|
|
620
|
+
this.reasoningTokens += reasoningTok;
|
|
621
|
+
this.totalTokens += usage.totalTokens;
|
|
680
622
|
this.reportedCost += cost;
|
|
681
623
|
// Burn rate sample accumulation
|
|
682
|
-
this.tokensSinceLastSample +=
|
|
624
|
+
this.tokensSinceLastSample += usage.totalTokens;
|
|
683
625
|
// Context size computation
|
|
684
626
|
let contextSize;
|
|
685
627
|
if (this.computeContextSize) {
|
|
@@ -688,7 +630,7 @@ class EventAggregator {
|
|
|
688
630
|
outputTokens: outputTok,
|
|
689
631
|
cacheWriteTokens: cacheWrite,
|
|
690
632
|
cacheReadTokens: cacheRead,
|
|
691
|
-
reasoningTokens:
|
|
633
|
+
reasoningTokens: reasoningTok,
|
|
692
634
|
});
|
|
693
635
|
}
|
|
694
636
|
else {
|
|
@@ -716,6 +658,9 @@ class EventAggregator {
|
|
|
716
658
|
inputTokens: contextSize,
|
|
717
659
|
turnIndex: this.contextTurnIndex++,
|
|
718
660
|
});
|
|
661
|
+
if (this.contextTimeline.length > this.contextTimelineCap) {
|
|
662
|
+
this.contextTimeline.splice(0, this.contextTimeline.length - this.contextTimelineCap);
|
|
663
|
+
}
|
|
719
664
|
// Per-model usage. Accumulate reasoning tokens and an inverted-sticky
|
|
720
665
|
// `priced` flag so one unpriced event taints the aggregate (UI renders "—").
|
|
721
666
|
const perModelKey = model ?? this.currentModel ?? 'unknown';
|
|
@@ -731,7 +676,7 @@ class EventAggregator {
|
|
|
731
676
|
priced: true,
|
|
732
677
|
};
|
|
733
678
|
acc.calls++;
|
|
734
|
-
acc.tokens +=
|
|
679
|
+
acc.tokens += usage.totalTokens;
|
|
735
680
|
acc.inputTokens += inputTok;
|
|
736
681
|
acc.outputTokens += outputTok;
|
|
737
682
|
acc.cacheWriteTokens += cacheWrite;
|
|
@@ -808,7 +753,7 @@ class EventAggregator {
|
|
|
808
753
|
// Private: Tool Extraction from SessionEvent content blocks
|
|
809
754
|
// ═══════════════════════════════════════════════════════════════════════
|
|
810
755
|
extractToolsFromContent(event) {
|
|
811
|
-
const content = event.message
|
|
756
|
+
const content = event.message?.content;
|
|
812
757
|
if (!Array.isArray(content))
|
|
813
758
|
return;
|
|
814
759
|
for (const block of content) {
|
|
@@ -961,7 +906,7 @@ class EventAggregator {
|
|
|
961
906
|
this.finishReasons.set(reason, (this.finishReasons.get(reason) ?? 0) + 1);
|
|
962
907
|
}
|
|
963
908
|
if (metadata.providerError) {
|
|
964
|
-
this.recordError('ProviderAPI', (0, errorTaxonomy_1.categorizeError)(metadata.providerError, metadata.providerError.type), event.timestamp, event.message
|
|
909
|
+
this.recordError('ProviderAPI', (0, errorTaxonomy_1.categorizeError)(metadata.providerError, metadata.providerError.type), event.timestamp, event.message?.model ?? this.currentModel ?? 'unknown');
|
|
965
910
|
}
|
|
966
911
|
}
|
|
967
912
|
recordError(tool, category, timestamp, model) {
|
|
@@ -1065,7 +1010,7 @@ class EventAggregator {
|
|
|
1065
1010
|
// Private: Task State from SessionEvent
|
|
1066
1011
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1067
1012
|
extractTaskStateFromEvent(event) {
|
|
1068
|
-
const content = event.message
|
|
1013
|
+
const content = event.message?.content;
|
|
1069
1014
|
if (!Array.isArray(content))
|
|
1070
1015
|
return;
|
|
1071
1016
|
for (const block of content) {
|
|
@@ -1480,7 +1425,7 @@ class EventAggregator {
|
|
|
1480
1425
|
// Private: Subagent Tracking from SessionEvent
|
|
1481
1426
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1482
1427
|
extractSubagentFromEvent(event) {
|
|
1483
|
-
const content = event.message
|
|
1428
|
+
const content = event.message?.content;
|
|
1484
1429
|
if (!Array.isArray(content))
|
|
1485
1430
|
return;
|
|
1486
1431
|
for (const block of content) {
|
|
@@ -1646,7 +1591,7 @@ class EventAggregator {
|
|
|
1646
1591
|
// Private: Context Attribution from SessionEvent
|
|
1647
1592
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1648
1593
|
attributeContextFromEvent(event) {
|
|
1649
|
-
const content = event.message
|
|
1594
|
+
const content = event.message?.content;
|
|
1650
1595
|
if (event.type === 'user') {
|
|
1651
1596
|
if (Array.isArray(content)) {
|
|
1652
1597
|
for (const block of content) {
|
|
@@ -1853,7 +1798,8 @@ class EventAggregator {
|
|
|
1853
1798
|
break;
|
|
1854
1799
|
case 'system':
|
|
1855
1800
|
tlType = 'session_start';
|
|
1856
|
-
description =
|
|
1801
|
+
description =
|
|
1802
|
+
this.extractTextContent(event) ?? event.message?.sourceLabel ?? 'System event';
|
|
1857
1803
|
noiseLevel = 'system';
|
|
1858
1804
|
break;
|
|
1859
1805
|
default:
|
|
@@ -1945,10 +1891,10 @@ class EventAggregator {
|
|
|
1945
1891
|
// Private: Helper methods
|
|
1946
1892
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1947
1893
|
estimateTokens(text) {
|
|
1948
|
-
return
|
|
1894
|
+
return (0, tokenEstimation_1.estimateTextTokens)(text).count;
|
|
1949
1895
|
}
|
|
1950
1896
|
hasTextContent(event) {
|
|
1951
|
-
const content = event.message
|
|
1897
|
+
const content = event.message?.content;
|
|
1952
1898
|
if (typeof content === 'string')
|
|
1953
1899
|
return content.length > 0;
|
|
1954
1900
|
if (Array.isArray(content)) {
|
|
@@ -1957,7 +1903,7 @@ class EventAggregator {
|
|
|
1957
1903
|
return false;
|
|
1958
1904
|
}
|
|
1959
1905
|
extractTextContent(event) {
|
|
1960
|
-
const content = event.message
|
|
1906
|
+
const content = event.message?.content;
|
|
1961
1907
|
if (typeof content === 'string')
|
|
1962
1908
|
return content;
|
|
1963
1909
|
if (Array.isArray(content)) {
|
|
@@ -1986,7 +1932,7 @@ class EventAggregator {
|
|
|
1986
1932
|
// Private: Gonzo/Lazyjournal analytics
|
|
1987
1933
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1988
1934
|
extractToolNameFromEvent(event) {
|
|
1989
|
-
const content = event.message
|
|
1935
|
+
const content = event.message?.content;
|
|
1990
1936
|
if (!Array.isArray(content))
|
|
1991
1937
|
return undefined;
|
|
1992
1938
|
for (const block of content) {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module aggregation/snapshot
|
|
9
9
|
*/
|
|
10
|
-
import type
|
|
10
|
+
import { type SerializedAggregatorState } from './EventAggregator';
|
|
11
11
|
/** Snapshot sidecar file content. */
|
|
12
12
|
export interface SessionSnapshot {
|
|
13
13
|
/** Schema version for forward-compatibility checks. */
|
|
@@ -50,8 +50,9 @@ exports.isSnapshotValid = isSnapshotValid;
|
|
|
50
50
|
const fs = __importStar(require("fs"));
|
|
51
51
|
const path = __importStar(require("path"));
|
|
52
52
|
const paths_1 = require("../paths");
|
|
53
|
+
const EventAggregator_1 = require("./EventAggregator");
|
|
53
54
|
/** Current schema version — bump when format changes. */
|
|
54
|
-
const SNAPSHOT_VERSION =
|
|
55
|
+
const SNAPSHOT_VERSION = EventAggregator_1.SNAPSHOT_SCHEMA_VERSION;
|
|
55
56
|
/** Returns the directory for snapshot files. */
|
|
56
57
|
function getSnapshotsDir() {
|
|
57
58
|
return path.join((0, paths_1.getConfigDir)(), 'snapshots');
|
|
@@ -31,6 +31,8 @@ export interface EventAggregatorOptions {
|
|
|
31
31
|
timelineCap?: number;
|
|
32
32
|
/** Maximum latency records to retain (default 100). */
|
|
33
33
|
latencyCap?: number;
|
|
34
|
+
/** Maximum context-size points to retain (default 2000). */
|
|
35
|
+
contextTimelineCap?: number;
|
|
34
36
|
/** Burn rate sliding window in ms (default 5 * 60_000). */
|
|
35
37
|
burnWindowMs?: number;
|
|
36
38
|
/** Burn rate sample interval in ms (default 10_000). */
|
|
@@ -54,6 +56,9 @@ export interface AggregatedTokens {
|
|
|
54
56
|
outputTokens: number;
|
|
55
57
|
cacheWriteTokens: number;
|
|
56
58
|
cacheReadTokens: number;
|
|
59
|
+
reasoningTokens: number;
|
|
60
|
+
/** Overall total after applying provider reasoning-in-output semantics. */
|
|
61
|
+
totalTokens: number;
|
|
57
62
|
reportedCost: number;
|
|
58
63
|
}
|
|
59
64
|
/** Per-model usage breakdown. */
|
package/dist/autoSwitch.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export interface AutoSwitchControllerOptions {
|
|
|
42
42
|
cooldownMs?: number;
|
|
43
43
|
now?: () => number;
|
|
44
44
|
}
|
|
45
|
-
export declare function decideAutoSwitch(_provider: AccountProviderId, active: AutoSwitchActiveAccount, candidates: AutoSwitchCandidate[], config?: AutoSwitchConfig): AutoSwitchDecision | null;
|
|
45
|
+
export declare function decideAutoSwitch(_provider: AccountProviderId, active: AutoSwitchActiveAccount, candidates: AutoSwitchCandidate[], config?: AutoSwitchConfig, nowMs?: number): AutoSwitchDecision | null;
|
|
46
46
|
export declare class AutoSwitchController implements Disposable {
|
|
47
47
|
private readonly quotaService;
|
|
48
48
|
private readonly getClaudeAccounts;
|
package/dist/autoSwitch.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.DEFAULT_AUTO_SWITCH_CONFIG = {
|
|
|
11
11
|
thresholdPct: 90,
|
|
12
12
|
};
|
|
13
13
|
const MATERIAL_REMAINING_IMPROVEMENT_PCT = 5;
|
|
14
|
+
const MAX_STALE_SNAPSHOT_AGE_MS = 5 * 60 * 60 * 1000;
|
|
14
15
|
function mergedConfig(config) {
|
|
15
16
|
return {
|
|
16
17
|
...exports.DEFAULT_AUTO_SWITCH_CONFIG,
|
|
@@ -27,7 +28,17 @@ function quotaRemaining(quota) {
|
|
|
27
28
|
return 0;
|
|
28
29
|
return Math.max(0, 100 - quotaUtilization(quota));
|
|
29
30
|
}
|
|
30
|
-
function
|
|
31
|
+
function isUsableCandidateQuota(quota, nowMs) {
|
|
32
|
+
if (!quota?.available)
|
|
33
|
+
return false;
|
|
34
|
+
if (quota.stale !== true)
|
|
35
|
+
return true;
|
|
36
|
+
const capturedAt = Date.parse(quota.capturedAt ?? '');
|
|
37
|
+
if (!Number.isFinite(capturedAt) || capturedAt > nowMs)
|
|
38
|
+
return false;
|
|
39
|
+
return nowMs - capturedAt <= MAX_STALE_SNAPSHOT_AGE_MS;
|
|
40
|
+
}
|
|
41
|
+
function decideAutoSwitch(_provider, active, candidates, config, nowMs = Date.now()) {
|
|
31
42
|
const cfg = mergedConfig(config);
|
|
32
43
|
if (!cfg.enabled)
|
|
33
44
|
return null;
|
|
@@ -40,7 +51,7 @@ function decideAutoSwitch(_provider, active, candidates, config) {
|
|
|
40
51
|
const best = candidates
|
|
41
52
|
.filter((candidate) => candidate.accountId !== active.accountId &&
|
|
42
53
|
candidate.switchable !== false &&
|
|
43
|
-
candidate.quota
|
|
54
|
+
isUsableCandidateQuota(candidate.quota, nowMs))
|
|
44
55
|
.sort((a, b) => quotaRemaining(b.quota) - quotaRemaining(a.quota))[0];
|
|
45
56
|
if (!best)
|
|
46
57
|
return null;
|
|
@@ -129,7 +140,7 @@ class AutoSwitchController {
|
|
|
129
140
|
quota: accountId === activeAccountId ? activeQuota : this.readSnapshot(providerId, accountId),
|
|
130
141
|
};
|
|
131
142
|
});
|
|
132
|
-
const decision = decideAutoSwitch(providerId, { accountId: activeAccountId, quota: activeQuota }, candidates, this.config);
|
|
143
|
+
const decision = decideAutoSwitch(providerId, { accountId: activeAccountId, quota: activeQuota }, candidates, this.config, this.now());
|
|
133
144
|
if (!decision)
|
|
134
145
|
return;
|
|
135
146
|
if (this.switchedDuringCrossing.has(providerId))
|
package/dist/browser.d.ts
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* For pricing hydration (LiteLLM catalog refresh), use `sidekick-shared/node`.
|
|
9
9
|
*/
|
|
10
10
|
export { getModelContextWindowSize, DEFAULT_CONTEXT_WINDOW } from './modelContext';
|
|
11
|
+
export { normalizeProviderUsage, extractNormalizedUsage, calculateNormalizedUsageCost, } from './usageNormalization';
|
|
12
|
+
export type { UsageProvider, UsageSemantics, UsageNormalizationProvenance, ProviderUsageInput, AnthropicUsageInput, OpenAIUsageInput, SidekickUsageInput, NormalizedUsage, PricingProvenance, NormalizedUsageCostInput, NormalizedUsageCost, } from './usageNormalization';
|
|
13
|
+
export { estimateTextTokens, estimateSerializedTokens } from './tokenEstimation';
|
|
14
|
+
export type { ExactTokenCounterContext, ExactTokenCounter, TokenEstimationMethod, TokenEstimationConfidence, TokenEstimationOptions, TokenEstimate, } from './tokenEstimation';
|
|
15
|
+
export { projectSessionTranscript } from './transcript';
|
|
16
|
+
export type { TranscriptFidelity, TranscriptSourceProvenance, CanonicalTranscriptBlock, CanonicalTranscriptMessage, CanonicalToolCall, CanonicalTranscriptUsageTotals, CanonicalSessionTranscript, ProjectSessionTranscriptOptions, } from './transcript';
|
|
11
17
|
export { formatDurationMs, formatTokenCount } from './formatting';
|
|
12
18
|
export type { FormatDurationMsOptions, FormatTokenCountOptions } from './formatting';
|
|
13
19
|
export { buildSessionContextSnapshot, calculateSessionContextPressure, createSessionContextProjector, } from './context/sessionContext';
|
package/dist/browser.js
CHANGED
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
* For pricing hydration (LiteLLM catalog refresh), use `sidekick-shared/node`.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = exports.formatTokenCount = exports.formatDurationMs = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = void 0;
|
|
12
|
+
exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = exports.formatTokenCount = exports.formatDurationMs = exports.projectSessionTranscript = exports.estimateSerializedTokens = exports.estimateTextTokens = exports.calculateNormalizedUsageCost = exports.extractNormalizedUsage = exports.normalizeProviderUsage = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = void 0;
|
|
13
13
|
var modelContext_1 = require("./modelContext");
|
|
14
14
|
Object.defineProperty(exports, "getModelContextWindowSize", { enumerable: true, get: function () { return modelContext_1.getModelContextWindowSize; } });
|
|
15
15
|
Object.defineProperty(exports, "DEFAULT_CONTEXT_WINDOW", { enumerable: true, get: function () { return modelContext_1.DEFAULT_CONTEXT_WINDOW; } });
|
|
16
|
+
var usageNormalization_1 = require("./usageNormalization");
|
|
17
|
+
Object.defineProperty(exports, "normalizeProviderUsage", { enumerable: true, get: function () { return usageNormalization_1.normalizeProviderUsage; } });
|
|
18
|
+
Object.defineProperty(exports, "extractNormalizedUsage", { enumerable: true, get: function () { return usageNormalization_1.extractNormalizedUsage; } });
|
|
19
|
+
Object.defineProperty(exports, "calculateNormalizedUsageCost", { enumerable: true, get: function () { return usageNormalization_1.calculateNormalizedUsageCost; } });
|
|
20
|
+
var tokenEstimation_1 = require("./tokenEstimation");
|
|
21
|
+
Object.defineProperty(exports, "estimateTextTokens", { enumerable: true, get: function () { return tokenEstimation_1.estimateTextTokens; } });
|
|
22
|
+
Object.defineProperty(exports, "estimateSerializedTokens", { enumerable: true, get: function () { return tokenEstimation_1.estimateSerializedTokens; } });
|
|
23
|
+
var transcript_1 = require("./transcript");
|
|
24
|
+
Object.defineProperty(exports, "projectSessionTranscript", { enumerable: true, get: function () { return transcript_1.projectSessionTranscript; } });
|
|
16
25
|
var formatting_1 = require("./formatting");
|
|
17
26
|
Object.defineProperty(exports, "formatDurationMs", { enumerable: true, get: function () { return formatting_1.formatDurationMs; } });
|
|
18
27
|
Object.defineProperty(exports, "formatTokenCount", { enumerable: true, get: function () { return formatting_1.formatTokenCount; } });
|
package/dist/codexQuota.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface CodexQuotaApiOptions {
|
|
|
32
32
|
usageUrl?: string;
|
|
33
33
|
resetCreditsUrl?: string;
|
|
34
34
|
capturedAt?: string;
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
timeoutMs?: number;
|
|
35
37
|
}
|
|
36
38
|
export declare function quotaFromCodexRateLimits(rateLimits: CodexRateLimits | null | undefined, source?: 'api' | 'session' | 'cache', capturedAt?: string): QuotaState | null;
|
|
37
39
|
export declare function readLatestCodexQuotaFromRollouts(sessionPaths: string[], options?: {
|
package/dist/codexQuota.js
CHANGED
|
@@ -414,10 +414,19 @@ async function fetchCodexQuotaFromApi(options = {}) {
|
|
|
414
414
|
sevenDayLabel: 'Secondary',
|
|
415
415
|
};
|
|
416
416
|
}
|
|
417
|
+
const controller = new AbortController();
|
|
418
|
+
const onAbort = () => controller.abort();
|
|
419
|
+
if (options.signal?.aborted)
|
|
420
|
+
controller.abort();
|
|
421
|
+
else
|
|
422
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
423
|
+
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 10_000);
|
|
424
|
+
timeout.unref();
|
|
417
425
|
try {
|
|
418
426
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
419
427
|
const response = await fetchImpl(options.usageUrl ?? CHATGPT_USAGE_URL, {
|
|
420
428
|
method: 'GET',
|
|
429
|
+
signal: controller.signal,
|
|
421
430
|
headers: {
|
|
422
431
|
Authorization: `Bearer ${accessToken}`,
|
|
423
432
|
Accept: 'application/json',
|
|
@@ -468,6 +477,7 @@ async function fetchCodexQuotaFromApi(options = {}) {
|
|
|
468
477
|
accessToken,
|
|
469
478
|
accountId: auth?.accountId,
|
|
470
479
|
capturedAt,
|
|
480
|
+
signal: controller.signal,
|
|
471
481
|
});
|
|
472
482
|
if (resetCredits) {
|
|
473
483
|
quota.resetCredits = resetCredits;
|
|
@@ -488,6 +498,10 @@ async function fetchCodexQuotaFromApi(options = {}) {
|
|
|
488
498
|
sevenDayLabel: 'Secondary',
|
|
489
499
|
};
|
|
490
500
|
}
|
|
501
|
+
finally {
|
|
502
|
+
clearTimeout(timeout);
|
|
503
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
504
|
+
}
|
|
491
505
|
}
|
|
492
506
|
async function fetchCodexResetCreditsFromApi(options = {}) {
|
|
493
507
|
const capturedAt = options.capturedAt ?? new Date().toISOString();
|
|
@@ -497,6 +511,14 @@ async function fetchCodexResetCreditsFromApi(options = {}) {
|
|
|
497
511
|
const accessToken = auth?.accessToken;
|
|
498
512
|
if (!accessToken)
|
|
499
513
|
return undefined;
|
|
514
|
+
const controller = new AbortController();
|
|
515
|
+
const onAbort = () => controller.abort();
|
|
516
|
+
if (options.signal?.aborted)
|
|
517
|
+
controller.abort();
|
|
518
|
+
else
|
|
519
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
520
|
+
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 10_000);
|
|
521
|
+
timeout.unref();
|
|
500
522
|
try {
|
|
501
523
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
502
524
|
const headers = {
|
|
@@ -511,6 +533,7 @@ async function fetchCodexResetCreditsFromApi(options = {}) {
|
|
|
511
533
|
const response = await fetchImpl(options.resetCreditsUrl ?? CHATGPT_RESET_CREDITS_URL, {
|
|
512
534
|
method: 'GET',
|
|
513
535
|
headers,
|
|
536
|
+
signal: controller.signal,
|
|
514
537
|
});
|
|
515
538
|
if (!response.ok)
|
|
516
539
|
return undefined;
|
|
@@ -519,6 +542,10 @@ async function fetchCodexResetCreditsFromApi(options = {}) {
|
|
|
519
542
|
catch {
|
|
520
543
|
return undefined;
|
|
521
544
|
}
|
|
545
|
+
finally {
|
|
546
|
+
clearTimeout(timeout);
|
|
547
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
548
|
+
}
|
|
522
549
|
}
|
|
523
550
|
function readCodexAuth(codexHome) {
|
|
524
551
|
try {
|