sidekick-shared 0.21.2 → 0.21.3
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 +45 -45
- package/dist/accountManager.js +9 -7
- package/dist/accountRegistry.js +18 -14
- package/dist/accountStatus.js +4 -1
- package/dist/accounts.js +60 -25
- package/dist/aggregation/EventAggregator.d.ts +14 -11
- package/dist/aggregation/EventAggregator.js +68 -31
- package/dist/aggregation/PatternExtractor.js +5 -4
- package/dist/autoSwitch.js +6 -12
- package/dist/claudeProfiles.js +3 -3
- package/dist/codexProfiles.js +45 -22
- package/dist/codexQuota.js +44 -16
- package/dist/codexQuotaWatcher.js +10 -3
- package/dist/context/composer.js +8 -6
- package/dist/context/sessionContext.js +12 -9
- package/dist/credentialIO.js +10 -8
- package/dist/extractors/sessionAssets.js +9 -1
- package/dist/extractors/sources/claudeAssets.js +7 -1
- package/dist/extractors/sources/codexAssets.js +14 -3
- package/dist/extractors/toolCall.js +1 -4
- package/dist/formatters/eventHighlighter.js +148 -28
- package/dist/formatters/noiseClassifier.js +3 -11
- package/dist/formatters/sessionDump.js +22 -13
- package/dist/formatters/toolSummary.js +1 -3
- package/dist/index.d.ts +23 -23
- package/dist/index.js +7 -3
- package/dist/modelContext.js +10 -8
- package/dist/modelInfo.js +11 -6
- package/dist/multiProviderQuotaService.js +10 -7
- package/dist/parsers/codexParser.js +170 -103
- package/dist/parsers/debugLogParser.js +5 -4
- package/dist/parsers/openCodeParser.js +66 -48
- package/dist/parsers/planExtractor.js +3 -3
- package/dist/parsers/sessionActivityDetector.js +10 -6
- package/dist/parsers/sessionPathResolver.js +29 -34
- package/dist/parsers/subagentScanner.js +21 -20
- package/dist/parsers/subagentTraceParser.js +7 -4
- package/dist/phrases.js +11 -11
- package/dist/providerStatus.js +3 -4
- package/dist/providers/claudeCode.js +3 -1
- package/dist/providers/codex.js +35 -14
- package/dist/providers/codexDatabase.js +14 -5
- package/dist/providers/detect.js +10 -6
- package/dist/providers/openCode.js +91 -63
- package/dist/providers/openCodeDatabase.js +44 -17
- package/dist/quota.d.ts +19 -0
- package/dist/quota.js +50 -8
- package/dist/quotaHistory.js +10 -3
- package/dist/quotaSnapshots.js +2 -2
- package/dist/readers/decisions.js +1 -1
- package/dist/readers/notes.js +5 -3
- package/dist/readers/plans.js +43 -23
- package/dist/readers/tasks.js +1 -1
- package/dist/report/htmlHelpers.js +135 -16
- package/dist/report/htmlReportGenerator.js +38 -12
- package/dist/report/transcriptParser.js +8 -2
- package/dist/schemas/index.d.ts +4 -4
- package/dist/schemas/quota.js +2 -10
- package/dist/schemas/sessionEvent.js +20 -10
- package/dist/search/advancedFilter.js +3 -3
- package/dist/search/sessionSearch.js +10 -4
- package/dist/terminalSync.js +4 -2
- package/dist/turns/assistantTurn.js +5 -1
- package/dist/watchers/eventBridge.js +56 -26
- package/dist/watchers/factory.js +5 -2
- package/dist/watchers/jsonlTail.js +3 -1
- package/dist/watchers/jsonlWatcher.js +128 -52
- package/dist/watchers/providerReaderWatcher.js +3 -1
- package/dist/watchers/sqliteWatcher.js +52 -22
- package/dist/zaiQuota.js +6 -10
- package/dist/zaiQuotaApi.js +13 -5
- package/dist/zaiQuotaWatcher.js +1 -1
- package/package.json +5 -2
package/dist/quota.js
CHANGED
|
@@ -6,26 +6,60 @@
|
|
|
6
6
|
* Polling / eventing is the caller's responsibility.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.SEVEN_DAY_WINDOW_MS = exports.FIVE_HOUR_WINDOW_MS = void 0;
|
|
10
|
+
exports.projectQuotaWindow = projectQuotaWindow;
|
|
11
|
+
exports.withQuotaProjections = withQuotaProjections;
|
|
9
12
|
exports.fetchQuota = fetchQuota;
|
|
10
13
|
const USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
|
|
11
14
|
const BETA_HEADER = 'oauth-2025-04-20';
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
exports.FIVE_HOUR_WINDOW_MS = 5 * 3_600_000;
|
|
16
|
+
exports.SEVEN_DAY_WINDOW_MS = 7 * 86_400_000;
|
|
14
17
|
/**
|
|
15
18
|
* Projects utilization at end of window based on elapsed time.
|
|
16
19
|
*
|
|
17
20
|
* Formula: `current * (windowDuration / elapsed)`, capped at 200%.
|
|
18
21
|
*/
|
|
19
|
-
function
|
|
20
|
-
|
|
22
|
+
function projectQuotaWindow(input) {
|
|
23
|
+
const { utilization, resetsAt, windowMs, capturedAt } = input;
|
|
24
|
+
if (!resetsAt || utilization <= 0 || !Number.isFinite(utilization) || windowMs <= 0)
|
|
21
25
|
return undefined;
|
|
22
26
|
const resetTime = new Date(resetsAt).getTime();
|
|
23
|
-
|
|
27
|
+
if (!Number.isFinite(resetTime))
|
|
28
|
+
return undefined;
|
|
29
|
+
const capturedTime = capturedAt ? Date.parse(capturedAt) : NaN;
|
|
30
|
+
const now = Number.isFinite(capturedTime) ? capturedTime : Date.now();
|
|
24
31
|
const elapsed = windowMs - (resetTime - now);
|
|
25
32
|
if (elapsed <= 0)
|
|
26
33
|
return undefined;
|
|
27
34
|
return Math.min(Math.round(utilization * (windowMs / elapsed)), 200);
|
|
28
35
|
}
|
|
36
|
+
function withQuotaProjections(state, options = {}) {
|
|
37
|
+
if (!state.available)
|
|
38
|
+
return state;
|
|
39
|
+
const next = { ...state };
|
|
40
|
+
const capturedAt = options.capturedAt ?? state.capturedAt;
|
|
41
|
+
if (next.projectedFiveHour == null) {
|
|
42
|
+
const projected = projectQuotaWindow({
|
|
43
|
+
utilization: next.fiveHour.utilization,
|
|
44
|
+
resetsAt: next.fiveHour.resetsAt,
|
|
45
|
+
windowMs: options.fiveHourWindowMs ?? exports.FIVE_HOUR_WINDOW_MS,
|
|
46
|
+
capturedAt,
|
|
47
|
+
});
|
|
48
|
+
if (projected !== undefined)
|
|
49
|
+
next.projectedFiveHour = projected;
|
|
50
|
+
}
|
|
51
|
+
if (next.projectedSevenDay == null) {
|
|
52
|
+
const projected = projectQuotaWindow({
|
|
53
|
+
utilization: next.sevenDay.utilization,
|
|
54
|
+
resetsAt: next.sevenDay.resetsAt,
|
|
55
|
+
windowMs: options.sevenDayWindowMs ?? exports.SEVEN_DAY_WINDOW_MS,
|
|
56
|
+
capturedAt,
|
|
57
|
+
});
|
|
58
|
+
if (projected !== undefined)
|
|
59
|
+
next.projectedSevenDay = projected;
|
|
60
|
+
}
|
|
61
|
+
return next;
|
|
62
|
+
}
|
|
29
63
|
/**
|
|
30
64
|
* Helper to build an unavailable QuotaState.
|
|
31
65
|
*/
|
|
@@ -64,7 +98,7 @@ async function fetchQuota(accessToken) {
|
|
|
64
98
|
const res = await fetch(USAGE_URL, {
|
|
65
99
|
method: 'GET',
|
|
66
100
|
headers: {
|
|
67
|
-
|
|
101
|
+
Authorization: `Bearer ${accessToken}`,
|
|
68
102
|
'anthropic-beta': BETA_HEADER,
|
|
69
103
|
'Content-Type': 'application/json',
|
|
70
104
|
},
|
|
@@ -103,8 +137,16 @@ async function fetchQuota(accessToken) {
|
|
|
103
137
|
fiveHour: { utilization: fiveUtil, resetsAt: fiveResetsAt },
|
|
104
138
|
sevenDay: { utilization: sevenUtil, resetsAt: sevenResetsAt },
|
|
105
139
|
available: true,
|
|
106
|
-
projectedFiveHour:
|
|
107
|
-
|
|
140
|
+
projectedFiveHour: projectQuotaWindow({
|
|
141
|
+
utilization: fiveUtil,
|
|
142
|
+
resetsAt: fiveResetsAt,
|
|
143
|
+
windowMs: exports.FIVE_HOUR_WINDOW_MS,
|
|
144
|
+
}),
|
|
145
|
+
projectedSevenDay: projectQuotaWindow({
|
|
146
|
+
utilization: sevenUtil,
|
|
147
|
+
resetsAt: sevenResetsAt,
|
|
148
|
+
windowMs: exports.SEVEN_DAY_WINDOW_MS,
|
|
149
|
+
}),
|
|
108
150
|
};
|
|
109
151
|
}
|
|
110
152
|
catch {
|
package/dist/quotaHistory.js
CHANGED
|
@@ -78,7 +78,10 @@ function getHistoryFilePath(workspaceId, provider) {
|
|
|
78
78
|
return path.join((0, paths_1.getConfigDir)(), 'quota-history', workspaceId, `${provider}.jsonl`);
|
|
79
79
|
}
|
|
80
80
|
function ensureHistoryDir(workspaceId) {
|
|
81
|
-
fs.mkdirSync(path.join((0, paths_1.getConfigDir)(), 'quota-history', workspaceId), {
|
|
81
|
+
fs.mkdirSync(path.join((0, paths_1.getConfigDir)(), 'quota-history', workspaceId), {
|
|
82
|
+
recursive: true,
|
|
83
|
+
mode: 0o700,
|
|
84
|
+
});
|
|
82
85
|
}
|
|
83
86
|
function parseSampleLine(line) {
|
|
84
87
|
const trimmed = line.trim();
|
|
@@ -235,7 +238,9 @@ async function runAppend(sample, filePath, options) {
|
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
240
|
const sampleTs = Date.parse(sample.timestamp);
|
|
238
|
-
if (lastTs !== undefined &&
|
|
241
|
+
if (lastTs !== undefined &&
|
|
242
|
+
Number.isFinite(sampleTs) &&
|
|
243
|
+
sampleTs - lastTs < options.minIntervalMs) {
|
|
239
244
|
return;
|
|
240
245
|
}
|
|
241
246
|
ensureHistoryDir(sample.workspaceId);
|
|
@@ -269,7 +274,9 @@ async function appendQuotaHistorySample(sample, options = {}) {
|
|
|
269
274
|
};
|
|
270
275
|
const filePath = getHistoryFilePath(sample.workspaceId, sample.runtimeProvider);
|
|
271
276
|
const previous = appendChains.get(filePath) ?? Promise.resolve();
|
|
272
|
-
const next = previous
|
|
277
|
+
const next = previous
|
|
278
|
+
.then(() => runAppend(sample, filePath, resolved))
|
|
279
|
+
.catch(() => {
|
|
273
280
|
// Swallow chain-level errors so a single failure doesn't break subsequent appends.
|
|
274
281
|
});
|
|
275
282
|
appendChains.set(filePath, next);
|
package/dist/quotaSnapshots.js
CHANGED
|
@@ -117,7 +117,7 @@ function writeQuotaSnapshot(providerId, accountId, quota) {
|
|
|
117
117
|
source: quota.source ?? 'session',
|
|
118
118
|
stale: false,
|
|
119
119
|
};
|
|
120
|
-
const index = store.snapshots.findIndex(item => item.providerId === providerId && item.accountId === accountId);
|
|
120
|
+
const index = store.snapshots.findIndex((item) => item.providerId === providerId && item.accountId === accountId);
|
|
121
121
|
if (index >= 0 && shouldKeepExistingSnapshot(store.snapshots[index].quota, snapshot)) {
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
@@ -136,7 +136,7 @@ function writeQuotaSnapshot(providerId, accountId, quota) {
|
|
|
136
136
|
}
|
|
137
137
|
function readQuotaSnapshot(providerId, accountId) {
|
|
138
138
|
const store = readStore();
|
|
139
|
-
const snapshot = store.snapshots.find(item => item.providerId === providerId && item.accountId === accountId);
|
|
139
|
+
const snapshot = store.snapshots.find((item) => item.providerId === providerId && item.accountId === accountId);
|
|
140
140
|
if (!snapshot)
|
|
141
141
|
return null;
|
|
142
142
|
return {
|
|
@@ -16,7 +16,7 @@ async function readDecisions(slug, opts) {
|
|
|
16
16
|
decisions.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
17
17
|
if (opts?.search) {
|
|
18
18
|
const q = opts.search.toLowerCase();
|
|
19
|
-
decisions = decisions.filter(d => d.description.toLowerCase().includes(q) ||
|
|
19
|
+
decisions = decisions.filter((d) => d.description.toLowerCase().includes(q) ||
|
|
20
20
|
d.rationale.toLowerCase().includes(q) ||
|
|
21
21
|
d.chosenOption.toLowerCase().includes(q));
|
|
22
22
|
}
|
package/dist/readers/notes.js
CHANGED
|
@@ -17,7 +17,9 @@ async function readNotes(slug, opts) {
|
|
|
17
17
|
const normalizedFile = opts.file.replace(/\\/g, '/');
|
|
18
18
|
for (const [filePath, fileNotes] of Object.entries(store.notesByFile)) {
|
|
19
19
|
const normalizedKey = filePath.replace(/\\/g, '/');
|
|
20
|
-
if (normalizedKey === normalizedFile ||
|
|
20
|
+
if (normalizedKey === normalizedFile ||
|
|
21
|
+
normalizedKey.endsWith('/' + normalizedFile) ||
|
|
22
|
+
normalizedFile.endsWith('/' + normalizedKey)) {
|
|
21
23
|
notes.push(...fileNotes);
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -28,10 +30,10 @@ async function readNotes(slug, opts) {
|
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
if (opts?.type) {
|
|
31
|
-
notes = notes.filter(n => n.noteType === opts.type);
|
|
33
|
+
notes = notes.filter((n) => n.noteType === opts.type);
|
|
32
34
|
}
|
|
33
35
|
if (opts?.status) {
|
|
34
|
-
notes = notes.filter(n => n.status === opts.status);
|
|
36
|
+
notes = notes.filter((n) => n.status === opts.status);
|
|
35
37
|
}
|
|
36
38
|
// Sort by importance then updatedAt
|
|
37
39
|
const importanceOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
package/dist/readers/plans.js
CHANGED
|
@@ -56,10 +56,10 @@ async function readPlans(slug, opts) {
|
|
|
56
56
|
let plans = [...store.plans];
|
|
57
57
|
const status = opts?.status ?? 'all';
|
|
58
58
|
if (status !== 'all') {
|
|
59
|
-
plans = plans.filter(p => p.status === status);
|
|
59
|
+
plans = plans.filter((p) => p.status === status);
|
|
60
60
|
}
|
|
61
61
|
if (opts?.source) {
|
|
62
|
-
plans = plans.filter(p => p.source === opts.source);
|
|
62
|
+
plans = plans.filter((p) => p.source === opts.source);
|
|
63
63
|
}
|
|
64
64
|
// Already sorted by createdAt desc in storage
|
|
65
65
|
if (opts?.limit && opts.limit > 0) {
|
|
@@ -75,26 +75,35 @@ function getPlanAnalytics(plans) {
|
|
|
75
75
|
const totalPlans = plans.length;
|
|
76
76
|
if (totalPlans === 0) {
|
|
77
77
|
return {
|
|
78
|
-
totalPlans: 0,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
totalPlans: 0,
|
|
79
|
+
completedPlans: 0,
|
|
80
|
+
failedPlans: 0,
|
|
81
|
+
abandonedPlans: 0,
|
|
82
|
+
avgCompletionRate: 0,
|
|
83
|
+
avgDurationMs: 0,
|
|
84
|
+
avgStepsPerPlan: 0,
|
|
85
|
+
avgTokensPerPlan: 0,
|
|
86
|
+
avgCostPerPlan: 0,
|
|
87
|
+
plansBySource: {},
|
|
88
|
+
mostCommonFailureStep: null,
|
|
89
|
+
completionTrend: [],
|
|
82
90
|
};
|
|
83
91
|
}
|
|
84
|
-
const completedPlans = plans.filter(p => p.status === 'completed').length;
|
|
85
|
-
const failedPlans = plans.filter(p => p.status === 'failed').length;
|
|
86
|
-
const abandonedPlans = plans.filter(p => p.status === 'abandoned').length;
|
|
92
|
+
const completedPlans = plans.filter((p) => p.status === 'completed').length;
|
|
93
|
+
const failedPlans = plans.filter((p) => p.status === 'failed').length;
|
|
94
|
+
const abandonedPlans = plans.filter((p) => p.status === 'abandoned').length;
|
|
87
95
|
const avgCompletionRate = plans.reduce((s, p) => s + p.completionRate, 0) / totalPlans;
|
|
88
|
-
const plansWithDuration = plans.filter(p => p.totalDurationMs != null && p.totalDurationMs > 0);
|
|
96
|
+
const plansWithDuration = plans.filter((p) => p.totalDurationMs != null && p.totalDurationMs > 0);
|
|
89
97
|
const avgDurationMs = plansWithDuration.length > 0
|
|
90
|
-
? plansWithDuration.reduce((s, p) => s + (p.totalDurationMs || 0), 0) /
|
|
98
|
+
? plansWithDuration.reduce((s, p) => s + (p.totalDurationMs || 0), 0) /
|
|
99
|
+
plansWithDuration.length
|
|
91
100
|
: 0;
|
|
92
101
|
const avgStepsPerPlan = plans.reduce((s, p) => s + p.steps.length, 0) / totalPlans;
|
|
93
|
-
const plansWithTokens = plans.filter(p => p.totalTokensUsed != null && p.totalTokensUsed > 0);
|
|
102
|
+
const plansWithTokens = plans.filter((p) => p.totalTokensUsed != null && p.totalTokensUsed > 0);
|
|
94
103
|
const avgTokensPerPlan = plansWithTokens.length > 0
|
|
95
104
|
? plansWithTokens.reduce((s, p) => s + (p.totalTokensUsed || 0), 0) / plansWithTokens.length
|
|
96
105
|
: 0;
|
|
97
|
-
const plansWithCost = plans.filter(p => p.totalCostUsd != null && p.totalCostUsd > 0);
|
|
106
|
+
const plansWithCost = plans.filter((p) => p.totalCostUsd != null && p.totalCostUsd > 0);
|
|
98
107
|
const avgCostPerPlan = plansWithCost.length > 0
|
|
99
108
|
? plansWithCost.reduce((s, p) => s + (p.totalCostUsd || 0), 0) / plansWithCost.length
|
|
100
109
|
: 0;
|
|
@@ -137,10 +146,18 @@ function getPlanAnalytics(plans) {
|
|
|
137
146
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
138
147
|
.map(([date, { sum, count }]) => ({ date, rate: sum / count }));
|
|
139
148
|
return {
|
|
140
|
-
totalPlans,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
totalPlans,
|
|
150
|
+
completedPlans,
|
|
151
|
+
failedPlans,
|
|
152
|
+
abandonedPlans,
|
|
153
|
+
avgCompletionRate,
|
|
154
|
+
avgDurationMs,
|
|
155
|
+
avgStepsPerPlan,
|
|
156
|
+
avgTokensPerPlan,
|
|
157
|
+
avgCostPerPlan,
|
|
158
|
+
plansBySource,
|
|
159
|
+
mostCommonFailureStep,
|
|
160
|
+
completionTrend,
|
|
144
161
|
};
|
|
145
162
|
}
|
|
146
163
|
async function writePlans(slug, plans) {
|
|
@@ -176,8 +193,7 @@ async function readClaudeCodePlanFiles(workspacePath) {
|
|
|
176
193
|
const slugSet = new Set();
|
|
177
194
|
let jsonlFiles;
|
|
178
195
|
try {
|
|
179
|
-
jsonlFiles = (await fs.promises.readdir(projectDir))
|
|
180
|
-
.filter(f => f.endsWith('.jsonl'));
|
|
196
|
+
jsonlFiles = (await fs.promises.readdir(projectDir)).filter((f) => f.endsWith('.jsonl'));
|
|
181
197
|
}
|
|
182
198
|
catch {
|
|
183
199
|
return [];
|
|
@@ -205,7 +221,9 @@ async function readClaudeCodePlanFiles(workspacePath) {
|
|
|
205
221
|
await handle.close();
|
|
206
222
|
}
|
|
207
223
|
}
|
|
208
|
-
catch {
|
|
224
|
+
catch {
|
|
225
|
+
/* skip unreadable files */
|
|
226
|
+
}
|
|
209
227
|
}));
|
|
210
228
|
if (slugSet.size === 0)
|
|
211
229
|
return [];
|
|
@@ -217,14 +235,14 @@ async function readClaudeCodePlanFiles(workspacePath) {
|
|
|
217
235
|
const stat = await fs.promises.stat(planPath);
|
|
218
236
|
const content = await fs.promises.readFile(planPath, 'utf-8');
|
|
219
237
|
const parsed = (0, planExtractor_1.parsePlanMarkdown)(content);
|
|
220
|
-
const steps = parsed.steps.map(s => ({
|
|
238
|
+
const steps = parsed.steps.map((s) => ({
|
|
221
239
|
id: s.id,
|
|
222
240
|
description: s.description,
|
|
223
241
|
status: s.status,
|
|
224
242
|
phase: s.phase,
|
|
225
243
|
complexity: s.complexity,
|
|
226
244
|
}));
|
|
227
|
-
const completed = steps.filter(s => s.status === 'completed').length;
|
|
245
|
+
const completed = steps.filter((s) => s.status === 'completed').length;
|
|
228
246
|
const total = steps.length;
|
|
229
247
|
plans.push({
|
|
230
248
|
id: `claude-plan-${slug}`,
|
|
@@ -239,7 +257,9 @@ async function readClaudeCodePlanFiles(workspacePath) {
|
|
|
239
257
|
rawMarkdown: content,
|
|
240
258
|
});
|
|
241
259
|
}
|
|
242
|
-
catch {
|
|
260
|
+
catch {
|
|
261
|
+
/* plan file missing or unreadable */
|
|
262
|
+
}
|
|
243
263
|
}
|
|
244
264
|
// Sort by createdAt descending
|
|
245
265
|
plans.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
package/dist/readers/tasks.js
CHANGED
|
@@ -14,7 +14,7 @@ async function readTasks(slug, opts) {
|
|
|
14
14
|
let tasks = Object.values(store.tasks);
|
|
15
15
|
const status = opts?.status ?? 'all';
|
|
16
16
|
if (status !== 'all') {
|
|
17
|
-
tasks = tasks.filter(t => t.status === status);
|
|
17
|
+
tasks = tasks.filter((t) => t.status === status);
|
|
18
18
|
}
|
|
19
19
|
// Sort by updatedAt descending
|
|
20
20
|
tasks.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
@@ -70,30 +70,144 @@ function simpleMarkdownToHtml(text) {
|
|
|
70
70
|
// Single newlines to <br> within paragraphs (but not before block elements)
|
|
71
71
|
result = result.replace(/(?<!<\/(?:h[1-4]|ul|li|blockquote|hr|div|pre)>)\n(?!<(?:h[1-4]|ul|li|blockquote|hr|div|pre|p|\/))/g, '<br>\n');
|
|
72
72
|
// Restore code blocks
|
|
73
|
+
result = result.replace(
|
|
73
74
|
// eslint-disable-next-line no-control-regex
|
|
74
|
-
|
|
75
|
+
/\x00CODEBLOCK(\d+)\x00/g, (_match, idx) => codeBlocks[parseInt(idx, 10)]);
|
|
75
76
|
return result;
|
|
76
77
|
}
|
|
77
78
|
// Keyword sets for syntax highlighting
|
|
78
79
|
const JS_KEYWORDS = new Set([
|
|
79
|
-
'const',
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'
|
|
83
|
-
'
|
|
84
|
-
'
|
|
80
|
+
'const',
|
|
81
|
+
'let',
|
|
82
|
+
'var',
|
|
83
|
+
'function',
|
|
84
|
+
'return',
|
|
85
|
+
'if',
|
|
86
|
+
'else',
|
|
87
|
+
'for',
|
|
88
|
+
'while',
|
|
89
|
+
'do',
|
|
90
|
+
'switch',
|
|
91
|
+
'case',
|
|
92
|
+
'break',
|
|
93
|
+
'continue',
|
|
94
|
+
'new',
|
|
95
|
+
'this',
|
|
96
|
+
'class',
|
|
97
|
+
'extends',
|
|
98
|
+
'super',
|
|
99
|
+
'import',
|
|
100
|
+
'export',
|
|
101
|
+
'from',
|
|
102
|
+
'default',
|
|
103
|
+
'try',
|
|
104
|
+
'catch',
|
|
105
|
+
'finally',
|
|
106
|
+
'throw',
|
|
107
|
+
'async',
|
|
108
|
+
'await',
|
|
109
|
+
'yield',
|
|
110
|
+
'typeof',
|
|
111
|
+
'instanceof',
|
|
112
|
+
'in',
|
|
113
|
+
'of',
|
|
114
|
+
'delete',
|
|
115
|
+
'void',
|
|
116
|
+
'true',
|
|
117
|
+
'false',
|
|
118
|
+
'null',
|
|
119
|
+
'undefined',
|
|
120
|
+
'interface',
|
|
121
|
+
'type',
|
|
122
|
+
'enum',
|
|
123
|
+
'implements',
|
|
124
|
+
'public',
|
|
125
|
+
'private',
|
|
126
|
+
'protected',
|
|
127
|
+
'readonly',
|
|
128
|
+
'static',
|
|
129
|
+
'abstract',
|
|
130
|
+
'as',
|
|
85
131
|
]);
|
|
86
132
|
const PYTHON_KEYWORDS = new Set([
|
|
87
|
-
'def',
|
|
88
|
-
'
|
|
89
|
-
'
|
|
90
|
-
'
|
|
133
|
+
'def',
|
|
134
|
+
'class',
|
|
135
|
+
'return',
|
|
136
|
+
'if',
|
|
137
|
+
'elif',
|
|
138
|
+
'else',
|
|
139
|
+
'for',
|
|
140
|
+
'while',
|
|
141
|
+
'import',
|
|
142
|
+
'from',
|
|
143
|
+
'as',
|
|
144
|
+
'try',
|
|
145
|
+
'except',
|
|
146
|
+
'finally',
|
|
147
|
+
'raise',
|
|
148
|
+
'with',
|
|
149
|
+
'yield',
|
|
150
|
+
'lambda',
|
|
151
|
+
'pass',
|
|
152
|
+
'break',
|
|
153
|
+
'continue',
|
|
154
|
+
'and',
|
|
155
|
+
'or',
|
|
156
|
+
'not',
|
|
157
|
+
'in',
|
|
158
|
+
'is',
|
|
159
|
+
'None',
|
|
160
|
+
'True',
|
|
161
|
+
'False',
|
|
162
|
+
'self',
|
|
163
|
+
'async',
|
|
164
|
+
'await',
|
|
165
|
+
'global',
|
|
166
|
+
'nonlocal',
|
|
167
|
+
'del',
|
|
168
|
+
'assert',
|
|
91
169
|
]);
|
|
92
170
|
const BASH_KEYWORDS = new Set([
|
|
93
|
-
'if',
|
|
94
|
-
'
|
|
95
|
-
'
|
|
96
|
-
'
|
|
171
|
+
'if',
|
|
172
|
+
'then',
|
|
173
|
+
'else',
|
|
174
|
+
'elif',
|
|
175
|
+
'fi',
|
|
176
|
+
'for',
|
|
177
|
+
'while',
|
|
178
|
+
'do',
|
|
179
|
+
'done',
|
|
180
|
+
'case',
|
|
181
|
+
'esac',
|
|
182
|
+
'function',
|
|
183
|
+
'return',
|
|
184
|
+
'exit',
|
|
185
|
+
'echo',
|
|
186
|
+
'export',
|
|
187
|
+
'source',
|
|
188
|
+
'local',
|
|
189
|
+
'readonly',
|
|
190
|
+
'set',
|
|
191
|
+
'unset',
|
|
192
|
+
'shift',
|
|
193
|
+
'cd',
|
|
194
|
+
'pwd',
|
|
195
|
+
'mkdir',
|
|
196
|
+
'rm',
|
|
197
|
+
'cp',
|
|
198
|
+
'mv',
|
|
199
|
+
'cat',
|
|
200
|
+
'grep',
|
|
201
|
+
'sed',
|
|
202
|
+
'awk',
|
|
203
|
+
'find',
|
|
204
|
+
'xargs',
|
|
205
|
+
'chmod',
|
|
206
|
+
'chown',
|
|
207
|
+
'sudo',
|
|
208
|
+
'apt',
|
|
209
|
+
'npm',
|
|
210
|
+
'git',
|
|
97
211
|
]);
|
|
98
212
|
/**
|
|
99
213
|
* Minimal keyword-based syntax highlighting.
|
|
@@ -135,7 +249,12 @@ function highlightWithKeywords(escaped, keywords, lang) {
|
|
|
135
249
|
// Highlight strings
|
|
136
250
|
let result = escaped.replace(/('(?:[^&#]|&(?!#39;))*'|"(?:[^&]|&(?!quot;))*")/g, '<span class="hl-string">$1</span>');
|
|
137
251
|
// Highlight comments
|
|
138
|
-
if (lang === 'python' ||
|
|
252
|
+
if (lang === 'python' ||
|
|
253
|
+
lang === 'py' ||
|
|
254
|
+
lang === 'bash' ||
|
|
255
|
+
lang === 'sh' ||
|
|
256
|
+
lang === 'shell' ||
|
|
257
|
+
lang === 'zsh') {
|
|
139
258
|
result = result.replace(/(#[^\n]*)/g, '<span class="hl-comment">$1</span>');
|
|
140
259
|
}
|
|
141
260
|
else {
|
|
@@ -424,27 +424,47 @@ function generateStatsCards(metrics, totalTokens, duration) {
|
|
|
424
424
|
const toolCount = metrics.toolStats.reduce((sum, t) => sum + t.successCount + t.failureCount, 0);
|
|
425
425
|
const cards = [
|
|
426
426
|
{ label: 'Messages', value: String(metrics.messageCount), sub: `${metrics.eventCount} events` },
|
|
427
|
-
{
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
427
|
+
{
|
|
428
|
+
label: 'Duration',
|
|
429
|
+
value: duration,
|
|
430
|
+
sub: metrics.sessionStartTime ? (0, htmlHelpers_1.formatTimestamp)(metrics.sessionStartTime) + ' start' : '',
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
label: 'Total Tokens',
|
|
434
|
+
value: (0, htmlHelpers_1.fmtTokens)(totalTokens),
|
|
435
|
+
sub: `${(0, htmlHelpers_1.fmtTokens)(metrics.tokens.inputTokens)} in / ${(0, htmlHelpers_1.fmtTokens)(metrics.tokens.outputTokens)} out`,
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
label: 'Cache',
|
|
439
|
+
value: (0, htmlHelpers_1.fmtTokens)(metrics.tokens.cacheReadTokens),
|
|
440
|
+
sub: `${(0, htmlHelpers_1.fmtTokens)(metrics.tokens.cacheWriteTokens)} written`,
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
label: 'Tool Calls',
|
|
444
|
+
value: String(toolCount),
|
|
445
|
+
sub: `${metrics.toolStats.length} unique tools`,
|
|
446
|
+
},
|
|
431
447
|
];
|
|
432
448
|
if (metrics.tokens.reportedCost > 0) {
|
|
433
449
|
cards.push({ label: 'Cost', value: (0, htmlHelpers_1.fmtCost)(metrics.tokens.reportedCost), sub: 'reported' });
|
|
434
450
|
}
|
|
435
451
|
return `<div class="container">
|
|
436
452
|
<div class="stats-grid">
|
|
437
|
-
${cards
|
|
453
|
+
${cards
|
|
454
|
+
.map((c) => ` <div class="stat-card">
|
|
438
455
|
<div class="label">${c.label}</div>
|
|
439
456
|
<div class="value">${c.value}</div>
|
|
440
457
|
${c.sub ? `<div class="sub">${c.sub}</div>` : ''}
|
|
441
|
-
</div>`)
|
|
458
|
+
</div>`)
|
|
459
|
+
.join('\n')}
|
|
442
460
|
</div>`;
|
|
443
461
|
}
|
|
444
462
|
function generateModelBreakdown(metrics) {
|
|
445
463
|
if (metrics.modelStats.length === 0)
|
|
446
464
|
return '';
|
|
447
|
-
const rows = metrics.modelStats
|
|
465
|
+
const rows = metrics.modelStats
|
|
466
|
+
.map((m) => `<tr><td>${(0, htmlHelpers_1.escapeHtml)(m.model)}</td><td>${m.calls}</td><td>${(0, htmlHelpers_1.fmtTokens)(m.tokens)}</td><td>${(0, htmlHelpers_1.fmtTokens)(m.inputTokens)} / ${(0, htmlHelpers_1.fmtTokens)(m.outputTokens)}</td><td>${(0, htmlHelpers_1.fmtCost)(m.cost)}</td></tr>`)
|
|
467
|
+
.join('\n');
|
|
448
468
|
return `<div class="section">
|
|
449
469
|
<div class="section-title">Model Breakdown</div>
|
|
450
470
|
<table>
|
|
@@ -456,13 +476,15 @@ function generateModelBreakdown(metrics) {
|
|
|
456
476
|
function generateToolBreakdown(metrics) {
|
|
457
477
|
if (metrics.toolStats.length === 0)
|
|
458
478
|
return '';
|
|
459
|
-
const sorted = [...metrics.toolStats].sort((a, b) =>
|
|
460
|
-
const rows = sorted
|
|
479
|
+
const sorted = [...metrics.toolStats].sort((a, b) => b.successCount + b.failureCount - (a.successCount + a.failureCount));
|
|
480
|
+
const rows = sorted
|
|
481
|
+
.map((t) => {
|
|
461
482
|
const total = t.successCount + t.failureCount;
|
|
462
483
|
const avgMs = t.completedCount > 0 ? Math.round(t.totalDuration / t.completedCount) : 0;
|
|
463
484
|
const failHtml = t.failureCount > 0 ? `<span style="color:var(--accent-red)">${t.failureCount}</span>` : '0';
|
|
464
485
|
return `<tr><td><code>${(0, htmlHelpers_1.escapeHtml)(t.name)}</code></td><td>${total}</td><td>${failHtml}</td><td>${avgMs > 0 ? `${avgMs}ms` : '-'}</td></tr>`;
|
|
465
|
-
})
|
|
486
|
+
})
|
|
487
|
+
.join('\n');
|
|
466
488
|
return `<div class="section">
|
|
467
489
|
<div class="section-title">Tool Breakdown</div>
|
|
468
490
|
<table>
|
|
@@ -499,8 +521,12 @@ function generateTranscript(transcript, includeThinking, includeToolDetail) {
|
|
|
499
521
|
function renderMessage(entry, includeThinking, includeToolDetail) {
|
|
500
522
|
const ts = entry.timestamp ? (0, htmlHelpers_1.formatTimestamp)(entry.timestamp) : '';
|
|
501
523
|
const roleBadge = `<span class="role-badge ${entry.type}">${entry.type}</span>`;
|
|
502
|
-
const sourceStr = entry.sourceLabel
|
|
503
|
-
|
|
524
|
+
const sourceStr = entry.sourceLabel
|
|
525
|
+
? `<span class="message-source">${(0, htmlHelpers_1.escapeHtml)(entry.sourceLabel)}</span>`
|
|
526
|
+
: '';
|
|
527
|
+
const modelStr = entry.model
|
|
528
|
+
? `<span class="message-model">${(0, htmlHelpers_1.escapeHtml)(entry.model)}</span>`
|
|
529
|
+
: '';
|
|
504
530
|
const tokenStr = entry.usage
|
|
505
531
|
? `<span class="message-tokens">${(0, htmlHelpers_1.fmtTokens)(entry.usage.input_tokens + entry.usage.output_tokens)} tokens</span>`
|
|
506
532
|
: '';
|
|
@@ -100,7 +100,10 @@ function eventToTranscriptEntry(event) {
|
|
|
100
100
|
// Skip empty entries (warmup messages, etc.)
|
|
101
101
|
if (content.length === 0) {
|
|
102
102
|
if (usage || sourceLabel) {
|
|
103
|
-
content.push({
|
|
103
|
+
content.push({
|
|
104
|
+
type: 'text',
|
|
105
|
+
text: sourceLabel ? `${sourceLabel} updated` : 'Usage updated',
|
|
106
|
+
});
|
|
104
107
|
}
|
|
105
108
|
else {
|
|
106
109
|
return null;
|
|
@@ -189,7 +192,10 @@ function extractToolResultContent(content) {
|
|
|
189
192
|
if (Array.isArray(content)) {
|
|
190
193
|
const parts = [];
|
|
191
194
|
for (const part of content) {
|
|
192
|
-
if (part &&
|
|
195
|
+
if (part &&
|
|
196
|
+
typeof part === 'object' &&
|
|
197
|
+
part.type === 'text' &&
|
|
198
|
+
typeof part.text === 'string') {
|
|
193
199
|
parts.push(part.text);
|
|
194
200
|
}
|
|
195
201
|
}
|
package/dist/schemas/index.d.ts
CHANGED
|
@@ -16,14 +16,14 @@ export { quotaWindowSchema, quotaStateSchema, quotaFailureKindSchema, quotaProvi
|
|
|
16
16
|
export type { QuotaWindow, QuotaState } from '../quota';
|
|
17
17
|
export type { PeakHoursState } from '../peakHours';
|
|
18
18
|
export type { QuotaFailureDescriptor } from '../quotaPresentation';
|
|
19
|
-
export type { ProviderQuotaState, ProviderQuotaMap, RuntimeQuotaProvider
|
|
19
|
+
export type { ProviderQuotaState, ProviderQuotaMap, RuntimeQuotaProvider } from '../providerQuota';
|
|
20
20
|
export { quotaHistoryRuntimeProviderSchema, quotaHistorySampleSchema, quotaHistoryDailyBucketSchema, } from './quotaHistory';
|
|
21
21
|
export type { QuotaHistoryRuntimeProvider, QuotaHistorySample, QuotaHistoryDailyBucket, } from '../quotaHistory';
|
|
22
|
-
export { activeProviderAccountStatusSchema, activeAccountStatusSchema
|
|
23
|
-
export type { ActiveProviderAccountStatus, ActiveAccountStatus
|
|
22
|
+
export { activeProviderAccountStatusSchema, activeAccountStatusSchema } from './accountStatus';
|
|
23
|
+
export type { ActiveProviderAccountStatus, ActiveAccountStatus } from '../accountStatus';
|
|
24
24
|
export { accountProviderIdSchema, accountManagerResultSchema, beginAccountLoginResultSchema, accountLoginStatusSchema, accountEntrySchema, savedAccountProfileSchema, listAllAccountsResultSchema, } from './accountManager';
|
|
25
25
|
export type { AccountEntry, AccountManagerResult } from '../accounts';
|
|
26
|
-
export type { AccountProviderId, SavedAccountProfile
|
|
26
|
+
export type { AccountProviderId, SavedAccountProfile } from '../accountRegistry';
|
|
27
27
|
export type { AccountLoginStatus, BeginAccountLoginResult, ListAllAccountsResult, } from '../accountManager';
|
|
28
28
|
export { assistantTurnEventSchema, assistantTurnEventTypeSchema, assistantTurnNarrationStepSchema, assistantTurnProcessSchema, assistantTurnProcessStepSchema, assistantTurnProjectionSchema, assistantTurnReasoningTimelineItemSchema, assistantTurnSubagentSchema, assistantTurnSubagentStatusSchema, assistantTurnTimelineItemSchema, assistantTurnToolGroupStepSchema, assistantTurnToolRefSchema, } from './assistantTurn';
|
|
29
29
|
export type { AssistantTurnEvent, AssistantTurnEventType, AssistantTurnProcess, AssistantTurnProcessStep, AssistantTurnProjection, AssistantTurnSubagent, AssistantTurnSubagentStatus, AssistantTurnTimelineItem, AssistantTurnToolRef, } from '../turns/assistantTurn';
|
package/dist/schemas/quota.js
CHANGED
|
@@ -25,16 +25,8 @@ exports.quotaFailureKindSchema = zod_1.z.enum([
|
|
|
25
25
|
'server',
|
|
26
26
|
'unknown',
|
|
27
27
|
]);
|
|
28
|
-
exports.quotaProviderIdSchema = zod_1.z.enum([
|
|
29
|
-
|
|
30
|
-
'codex',
|
|
31
|
-
'zai',
|
|
32
|
-
]);
|
|
33
|
-
exports.quotaSourceSchema = zod_1.z.enum([
|
|
34
|
-
'api',
|
|
35
|
-
'session',
|
|
36
|
-
'cache',
|
|
37
|
-
]);
|
|
28
|
+
exports.quotaProviderIdSchema = zod_1.z.enum(['claude-code', 'codex', 'zai']);
|
|
29
|
+
exports.quotaSourceSchema = zod_1.z.enum(['api', 'session', 'cache']);
|
|
38
30
|
exports.quotaStateSchema = zod_1.z.object({
|
|
39
31
|
fiveHour: exports.quotaWindowSchema,
|
|
40
32
|
sevenDay: exports.quotaWindowSchema,
|