sidekick-shared 0.25.0 → 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 +57 -0
- package/README.md +49 -3
- package/dist/accountChanges.js +22 -3
- package/dist/accountRegistry.d.ts +22 -1
- package/dist/accountRegistry.js +37 -14
- 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 +11 -1
- package/dist/browser.js +29 -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/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 +46 -12
- package/dist/index.js +112 -24
- package/dist/node.d.ts +19 -0
- package/dist/node.js +47 -1
- package/dist/observedContextWindows.d.ts +7 -0
- package/dist/observedContextWindows.js +17 -2
- package/dist/observedSessionCollector.d.ts +3 -8
- package/dist/observedSessionCollector.js +10 -27
- package/dist/parsers/sessionActivityDetector.d.ts +24 -2
- package/dist/parsers/sessionActivityDetector.js +95 -17
- 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/providers/claudeCode.js +11 -96
- package/dist/providers/codex.d.ts +2 -0
- package/dist/providers/codex.js +94 -259
- package/dist/providers/detect.d.ts +2 -0
- package/dist/providers/detect.js +29 -24
- package/dist/providers/factory.d.ts +6 -0
- package/dist/providers/openCode.d.ts +2 -0
- package/dist/providers/openCode.js +105 -121
- package/dist/providers/rolloutWalker.d.ts +53 -0
- package/dist/providers/rolloutWalker.js +209 -0
- package/dist/providers/types.d.ts +30 -0
- package/dist/quota.d.ts +30 -3
- package/dist/quota.js +35 -3
- package/dist/quotaHistory.d.ts +1 -1
- package/dist/quotaHistory.js +36 -35
- 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/index.d.ts +2 -0
- package/dist/schemas/index.js +10 -1
- package/dist/schemas/quota.d.ts +139 -0
- package/dist/schemas/quota.js +17 -2
- package/dist/schemas/quotaHistory.d.ts +1 -0
- 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/transcript.js +2 -0
- package/dist/sessionFingerprint.d.ts +25 -0
- package/dist/sessionFingerprint.js +46 -0
- package/dist/sessionPreviews.d.ts +7 -1
- package/dist/sessionPreviews.js +15 -3
- 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 +2 -0
- package/dist/types/observedSessionV1.js +75 -18
- 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 +1 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for the JSON Claude Code pipes to its status-line command.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code runs the configured `statusLine.command` after every response
|
|
5
|
+
* (debounced at 300 ms) with a JSON document on stdin describing the session:
|
|
6
|
+
* model, workspace, context-window usage, estimated session cost, and — for
|
|
7
|
+
* Claude.ai Pro and Max subscribers — the *official* five-hour and seven-day
|
|
8
|
+
* rate-limit windows. That last block is the same data `/usage` shows and
|
|
9
|
+
* needs no network call, so Sidekick treats it as its most authoritative
|
|
10
|
+
* quota source whenever the status line is installed.
|
|
11
|
+
*
|
|
12
|
+
* Only the fields Sidekick reads are typed here; the raw document is kept on
|
|
13
|
+
* `raw` for callers that need more. Browser-safe: no Node imports.
|
|
14
|
+
*
|
|
15
|
+
* @module statusline/claudeStatuslinePayload
|
|
16
|
+
*/
|
|
17
|
+
import type { QuotaState } from '../quota';
|
|
18
|
+
/** One official rate-limit window. */
|
|
19
|
+
export interface ClaudeStatuslineRateLimitWindow {
|
|
20
|
+
/** Percentage of the window consumed, 0–100 (spend limits may exceed 100). */
|
|
21
|
+
usedPercentage: number;
|
|
22
|
+
/** Unix epoch seconds when the window resets. */
|
|
23
|
+
resetsAt: number;
|
|
24
|
+
}
|
|
25
|
+
export interface ClaudeStatuslinePayload {
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
transcriptPath?: string;
|
|
28
|
+
cwd?: string;
|
|
29
|
+
model?: {
|
|
30
|
+
id?: string;
|
|
31
|
+
displayName?: string;
|
|
32
|
+
};
|
|
33
|
+
workspace?: {
|
|
34
|
+
currentDir?: string;
|
|
35
|
+
projectDir?: string;
|
|
36
|
+
gitWorktree?: string;
|
|
37
|
+
};
|
|
38
|
+
contextWindow?: {
|
|
39
|
+
usedPercentage?: number | null;
|
|
40
|
+
remainingPercentage?: number | null;
|
|
41
|
+
contextWindowSize?: number;
|
|
42
|
+
totalInputTokens?: number;
|
|
43
|
+
totalOutputTokens?: number;
|
|
44
|
+
};
|
|
45
|
+
cost?: {
|
|
46
|
+
totalCostUsd?: number;
|
|
47
|
+
totalDurationMs?: number;
|
|
48
|
+
totalApiDurationMs?: number;
|
|
49
|
+
totalLinesAdded?: number;
|
|
50
|
+
totalLinesRemoved?: number;
|
|
51
|
+
};
|
|
52
|
+
rateLimits?: {
|
|
53
|
+
fiveHour?: ClaudeStatuslineRateLimitWindow;
|
|
54
|
+
sevenDay?: ClaudeStatuslineRateLimitWindow;
|
|
55
|
+
spendLimit?: ClaudeStatuslineRateLimitWindow;
|
|
56
|
+
};
|
|
57
|
+
promptCache?: {
|
|
58
|
+
warm?: boolean;
|
|
59
|
+
cachingObserved?: boolean;
|
|
60
|
+
hitRatio?: number | null;
|
|
61
|
+
requests?: number;
|
|
62
|
+
misses?: number;
|
|
63
|
+
expectedRebuilds?: number;
|
|
64
|
+
};
|
|
65
|
+
/** The parsed document as received. */
|
|
66
|
+
raw: Record<string, unknown>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse a status-line document. Returns `null` for empty input, non-JSON, or
|
|
70
|
+
* JSON that is not an object; never throws.
|
|
71
|
+
*/
|
|
72
|
+
export declare function parseClaudeStatuslinePayload(text: string): ClaudeStatuslinePayload | null;
|
|
73
|
+
export interface QuotaFromStatuslineOptions {
|
|
74
|
+
/** Capture time; defaults to now. */
|
|
75
|
+
now?: Date;
|
|
76
|
+
/**
|
|
77
|
+
* A previously known quota (typically the persisted snapshot). Windows the
|
|
78
|
+
* payload omits are carried over from it, since Claude Code drops a window
|
|
79
|
+
* from the document once its reset time has passed.
|
|
80
|
+
*/
|
|
81
|
+
fallback?: QuotaState | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Build a `QuotaState` from the official rate-limit block. Returns `null`
|
|
85
|
+
* when the payload carries no rate limits at all (API-key users, or before
|
|
86
|
+
* the first response of a session), so callers fall back to their other
|
|
87
|
+
* sources.
|
|
88
|
+
*/
|
|
89
|
+
export declare function quotaFromStatuslinePayload(payload: ClaudeStatuslinePayload, options?: QuotaFromStatuslineOptions): QuotaState | null;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for the JSON Claude Code pipes to its status-line command.
|
|
4
|
+
*
|
|
5
|
+
* Claude Code runs the configured `statusLine.command` after every response
|
|
6
|
+
* (debounced at 300 ms) with a JSON document on stdin describing the session:
|
|
7
|
+
* model, workspace, context-window usage, estimated session cost, and — for
|
|
8
|
+
* Claude.ai Pro and Max subscribers — the *official* five-hour and seven-day
|
|
9
|
+
* rate-limit windows. That last block is the same data `/usage` shows and
|
|
10
|
+
* needs no network call, so Sidekick treats it as its most authoritative
|
|
11
|
+
* quota source whenever the status line is installed.
|
|
12
|
+
*
|
|
13
|
+
* Only the fields Sidekick reads are typed here; the raw document is kept on
|
|
14
|
+
* `raw` for callers that need more. Browser-safe: no Node imports.
|
|
15
|
+
*
|
|
16
|
+
* @module statusline/claudeStatuslinePayload
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.parseClaudeStatuslinePayload = parseClaudeStatuslinePayload;
|
|
20
|
+
exports.quotaFromStatuslinePayload = quotaFromStatuslinePayload;
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function num(value) {
|
|
25
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
26
|
+
}
|
|
27
|
+
function nullableNum(value) {
|
|
28
|
+
if (value === null)
|
|
29
|
+
return null;
|
|
30
|
+
return num(value);
|
|
31
|
+
}
|
|
32
|
+
function str(value) {
|
|
33
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
34
|
+
}
|
|
35
|
+
function bool(value) {
|
|
36
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
37
|
+
}
|
|
38
|
+
function window(value) {
|
|
39
|
+
if (!isRecord(value))
|
|
40
|
+
return undefined;
|
|
41
|
+
const usedPercentage = num(value.used_percentage);
|
|
42
|
+
const resetsAt = num(value.resets_at);
|
|
43
|
+
if (usedPercentage === undefined || resetsAt === undefined)
|
|
44
|
+
return undefined;
|
|
45
|
+
return { usedPercentage, resetsAt };
|
|
46
|
+
}
|
|
47
|
+
function defined(value) {
|
|
48
|
+
return Object.values(value).some((entry) => entry !== undefined) ? value : undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parse a status-line document. Returns `null` for empty input, non-JSON, or
|
|
52
|
+
* JSON that is not an object; never throws.
|
|
53
|
+
*/
|
|
54
|
+
function parseClaudeStatuslinePayload(text) {
|
|
55
|
+
if (typeof text !== 'string' || !text.trim())
|
|
56
|
+
return null;
|
|
57
|
+
let raw;
|
|
58
|
+
try {
|
|
59
|
+
raw = JSON.parse(text);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (!isRecord(raw))
|
|
65
|
+
return null;
|
|
66
|
+
const model = isRecord(raw.model) ? raw.model : undefined;
|
|
67
|
+
const workspace = isRecord(raw.workspace) ? raw.workspace : undefined;
|
|
68
|
+
const contextWindow = isRecord(raw.context_window) ? raw.context_window : undefined;
|
|
69
|
+
const cost = isRecord(raw.cost) ? raw.cost : undefined;
|
|
70
|
+
const rateLimits = isRecord(raw.rate_limits) ? raw.rate_limits : undefined;
|
|
71
|
+
const promptCache = isRecord(raw.prompt_cache) ? raw.prompt_cache : undefined;
|
|
72
|
+
return {
|
|
73
|
+
sessionId: str(raw.session_id),
|
|
74
|
+
transcriptPath: str(raw.transcript_path),
|
|
75
|
+
cwd: str(raw.cwd),
|
|
76
|
+
model: model ? defined({ id: str(model.id), displayName: str(model.display_name) }) : undefined,
|
|
77
|
+
workspace: workspace
|
|
78
|
+
? defined({
|
|
79
|
+
currentDir: str(workspace.current_dir),
|
|
80
|
+
projectDir: str(workspace.project_dir),
|
|
81
|
+
gitWorktree: str(workspace.git_worktree),
|
|
82
|
+
})
|
|
83
|
+
: undefined,
|
|
84
|
+
contextWindow: contextWindow
|
|
85
|
+
? defined({
|
|
86
|
+
usedPercentage: nullableNum(contextWindow.used_percentage),
|
|
87
|
+
remainingPercentage: nullableNum(contextWindow.remaining_percentage),
|
|
88
|
+
contextWindowSize: num(contextWindow.context_window_size),
|
|
89
|
+
totalInputTokens: num(contextWindow.total_input_tokens),
|
|
90
|
+
totalOutputTokens: num(contextWindow.total_output_tokens),
|
|
91
|
+
})
|
|
92
|
+
: undefined,
|
|
93
|
+
cost: cost
|
|
94
|
+
? defined({
|
|
95
|
+
totalCostUsd: num(cost.total_cost_usd),
|
|
96
|
+
totalDurationMs: num(cost.total_duration_ms),
|
|
97
|
+
totalApiDurationMs: num(cost.total_api_duration_ms),
|
|
98
|
+
totalLinesAdded: num(cost.total_lines_added),
|
|
99
|
+
totalLinesRemoved: num(cost.total_lines_removed),
|
|
100
|
+
})
|
|
101
|
+
: undefined,
|
|
102
|
+
rateLimits: rateLimits
|
|
103
|
+
? defined({
|
|
104
|
+
fiveHour: window(rateLimits.five_hour),
|
|
105
|
+
sevenDay: window(rateLimits.seven_day),
|
|
106
|
+
spendLimit: window(rateLimits.spend_limit),
|
|
107
|
+
})
|
|
108
|
+
: undefined,
|
|
109
|
+
promptCache: promptCache
|
|
110
|
+
? defined({
|
|
111
|
+
warm: bool(promptCache.warm),
|
|
112
|
+
cachingObserved: bool(promptCache.caching_observed),
|
|
113
|
+
hitRatio: nullableNum(promptCache.hit_ratio),
|
|
114
|
+
requests: num(promptCache.requests),
|
|
115
|
+
misses: num(promptCache.misses),
|
|
116
|
+
expectedRebuilds: num(promptCache.expected_rebuilds),
|
|
117
|
+
})
|
|
118
|
+
: undefined,
|
|
119
|
+
raw,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function toWindow(live, fallback) {
|
|
123
|
+
if (live) {
|
|
124
|
+
return {
|
|
125
|
+
utilization: Math.max(0, live.usedPercentage),
|
|
126
|
+
resetsAt: new Date(live.resetsAt * 1000).toISOString(),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return fallback ?? { utilization: 0, resetsAt: '' };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Build a `QuotaState` from the official rate-limit block. Returns `null`
|
|
133
|
+
* when the payload carries no rate limits at all (API-key users, or before
|
|
134
|
+
* the first response of a session), so callers fall back to their other
|
|
135
|
+
* sources.
|
|
136
|
+
*/
|
|
137
|
+
function quotaFromStatuslinePayload(payload, options = {}) {
|
|
138
|
+
const limits = payload.rateLimits;
|
|
139
|
+
if (!limits?.fiveHour && !limits?.sevenDay)
|
|
140
|
+
return null;
|
|
141
|
+
const now = options.now ?? new Date();
|
|
142
|
+
const fallback = options.fallback ?? undefined;
|
|
143
|
+
return {
|
|
144
|
+
fiveHour: toWindow(limits.fiveHour, fallback?.fiveHour),
|
|
145
|
+
sevenDay: toWindow(limits.sevenDay, fallback?.sevenDay),
|
|
146
|
+
available: true,
|
|
147
|
+
providerId: 'claude-code',
|
|
148
|
+
source: 'statusline',
|
|
149
|
+
capturedAt: now.toISOString(),
|
|
150
|
+
stale: false,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import type { ActiveAccountStatus } from '../accountStatus';
|
|
2
2
|
import type { QuotaState } from '../quota';
|
|
3
|
+
import type { ClaudeStatuslinePayload } from './claudeStatuslinePayload';
|
|
3
4
|
export interface StatuslineInput {
|
|
4
5
|
accounts: ActiveAccountStatus;
|
|
5
6
|
claudeQuota?: QuotaState | null;
|
|
6
7
|
codexQuota?: QuotaState | null;
|
|
8
|
+
/**
|
|
9
|
+
* The document Claude Code piped to the status-line command, when running
|
|
10
|
+
* as one. Adds context usage, session cost, and prompt-cache hit rate.
|
|
11
|
+
*/
|
|
12
|
+
live?: ClaudeStatuslinePayload | null;
|
|
7
13
|
now?: Date;
|
|
8
14
|
}
|
|
9
15
|
export interface StatuslineSelection {
|
|
@@ -13,4 +19,11 @@ export interface StatuslineSelection {
|
|
|
13
19
|
quota: QuotaState | null;
|
|
14
20
|
}
|
|
15
21
|
export declare function selectStatuslineAccount(input: StatuslineInput): StatuslineSelection | null;
|
|
22
|
+
/**
|
|
23
|
+
* One-line status. Segments, in order, each omitted when unknown:
|
|
24
|
+
* `acct:<label> · 5h 42% resets 14:00 · ~1h20m left · 7d 61% · (2h ago) · ctx 37% · $0.42 · cache 92%`
|
|
25
|
+
*
|
|
26
|
+
* The age segment appears only for cached quota older than five minutes, so
|
|
27
|
+
* a snapshot from yesterday can no longer read like a live figure.
|
|
28
|
+
*/
|
|
16
29
|
export declare function formatStatusline(input: StatuslineInput): string;
|
|
@@ -23,20 +23,62 @@ function selectStatuslineAccount(input) {
|
|
|
23
23
|
}
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* One-line status. Segments, in order, each omitted when unknown:
|
|
28
|
+
* `acct:<label> · 5h 42% resets 14:00 · ~1h20m left · 7d 61% · (2h ago) · ctx 37% · $0.42 · cache 92%`
|
|
29
|
+
*
|
|
30
|
+
* The age segment appears only for cached quota older than five minutes, so
|
|
31
|
+
* a snapshot from yesterday can no longer read like a live figure.
|
|
32
|
+
*/
|
|
26
33
|
function formatStatusline(input) {
|
|
27
34
|
const selected = selectStatuslineAccount(input);
|
|
35
|
+
const now = input.now ?? new Date();
|
|
36
|
+
const liveSegments = formatLiveSegments(input.live);
|
|
28
37
|
if (!selected)
|
|
29
|
-
return 'acct:none
|
|
38
|
+
return joinSegments(['acct:none', 'quota unavailable', ...liveSegments]);
|
|
30
39
|
const account = compactLabel(selected.accountLabel);
|
|
31
40
|
const quota = selected.quota;
|
|
32
|
-
if (!quota?.available)
|
|
33
|
-
return `acct:${account}
|
|
34
|
-
|
|
41
|
+
if (!quota?.available) {
|
|
42
|
+
return joinSegments([`acct:${account}`, 'quota unavailable', ...liveSegments]);
|
|
43
|
+
}
|
|
35
44
|
const utilization = clampPercent(quota.fiveHour.utilization);
|
|
36
45
|
const reset = formatReset(quota.fiveHour.resetsAt, now);
|
|
37
46
|
const eta = estimateWindowEta(quota, now);
|
|
38
|
-
const
|
|
39
|
-
|
|
47
|
+
const segments = [`acct:${account}`, `5h ${Math.round(utilization)}% resets ${reset}`];
|
|
48
|
+
if (eta != null)
|
|
49
|
+
segments.push(`~${formatMinutes(eta)} left`);
|
|
50
|
+
if (quota.sevenDay.resetsAt && quota.sevenDay.utilization > 0) {
|
|
51
|
+
segments.push(`7d ${Math.round(clampPercent(quota.sevenDay.utilization))}%`);
|
|
52
|
+
}
|
|
53
|
+
if (quota.freshness && quota.freshness !== 'fresh') {
|
|
54
|
+
segments.push(`(${(0, quota_1.formatQuotaAge)(quota.ageMs)})`);
|
|
55
|
+
}
|
|
56
|
+
return joinSegments([...segments, ...liveSegments]);
|
|
57
|
+
}
|
|
58
|
+
function formatLiveSegments(live) {
|
|
59
|
+
if (!live)
|
|
60
|
+
return [];
|
|
61
|
+
const segments = [];
|
|
62
|
+
const context = live.contextWindow?.usedPercentage;
|
|
63
|
+
if (typeof context === 'number')
|
|
64
|
+
segments.push(`ctx ${Math.round(clampPercent(context))}%`);
|
|
65
|
+
const cost = live.cost?.totalCostUsd;
|
|
66
|
+
if (typeof cost === 'number' && cost > 0)
|
|
67
|
+
segments.push(`$${formatCost(cost)}`);
|
|
68
|
+
const hitRatio = live.promptCache?.hitRatio;
|
|
69
|
+
if (typeof hitRatio === 'number')
|
|
70
|
+
segments.push(`cache ${Math.round(clampPercent(hitRatio * 100))}%`);
|
|
71
|
+
return segments;
|
|
72
|
+
}
|
|
73
|
+
function joinSegments(segments) {
|
|
74
|
+
return segments.join(' · ');
|
|
75
|
+
}
|
|
76
|
+
function formatCost(value) {
|
|
77
|
+
if (value >= 100)
|
|
78
|
+
return value.toFixed(0);
|
|
79
|
+
if (value >= 10)
|
|
80
|
+
return value.toFixed(1);
|
|
81
|
+
return value.toFixed(2);
|
|
40
82
|
}
|
|
41
83
|
function estimateWindowEta(quota, now) {
|
|
42
84
|
const resetAt = Date.parse(quota.fiveHour.resetsAt);
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
export { getActiveAccountStatus } from '../accountStatus';
|
|
2
|
-
export type { ActiveAccountStatus, ActiveProviderAccountStatus } from '../accountStatus';
|
|
3
|
-
export { readQuotaSnapshot } from '../quotaSnapshots';
|
|
2
|
+
export type { ActiveAccountStatus, ActiveAccountStatusOptions, ActiveProviderAccountStatus, } from '../accountStatus';
|
|
3
|
+
export { readQuotaSnapshot, writeQuotaSnapshot } from '../quotaSnapshots';
|
|
4
4
|
export type { QuotaSnapshotProviderId } from '../quotaSnapshots';
|
|
5
|
+
export { appendQuotaHistorySample, getWorkspaceIdFromPath } from '../quotaHistory';
|
|
6
|
+
export type { QuotaHistorySample } from '../quotaHistory';
|
|
7
|
+
export { classifyQuotaFreshness, formatQuotaAge } from '../quota';
|
|
8
|
+
export type { QuotaFreshness, QuotaState } from '../quota';
|
|
5
9
|
export { BurnRateCalculator, estimateTimeToQuota } from './BurnRateCalculator';
|
|
6
10
|
export { formatStatusline, selectStatuslineAccount } from './formatter';
|
|
7
11
|
export type { StatuslineInput, StatuslineSelection } from './formatter';
|
|
12
|
+
export { parseClaudeStatuslinePayload, quotaFromStatuslinePayload, } from './claudeStatuslinePayload';
|
|
13
|
+
export type { ClaudeStatuslinePayload, ClaudeStatuslineRateLimitWindow, QuotaFromStatuslineOptions, } from './claudeStatuslinePayload';
|
|
14
|
+
export { STATE_FILE_NAME, STATE_FILE_SCHEMA_VERSION, billingBlockToStateFile, getStateFilePath, quotaToStateFile, readStateFile, writeStateFile, } from '../stateFile';
|
|
15
|
+
export type { SidekickStateFile, SidekickStateInput, StateFileAccount, StateFileBillingBlock, StateFileContext, StateFileQuota, StateFileQuotaWindow, StateFileSession, StateFileWriter, WriteStateFileOptions, } from '../stateFile';
|
package/dist/statusline/index.js
CHANGED
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.selectStatuslineAccount = exports.formatStatusline = exports.estimateTimeToQuota = exports.BurnRateCalculator = exports.readQuotaSnapshot = exports.getActiveAccountStatus = void 0;
|
|
3
|
+
exports.writeStateFile = exports.readStateFile = exports.quotaToStateFile = exports.getStateFilePath = exports.billingBlockToStateFile = exports.STATE_FILE_SCHEMA_VERSION = exports.STATE_FILE_NAME = exports.quotaFromStatuslinePayload = exports.parseClaudeStatuslinePayload = exports.selectStatuslineAccount = exports.formatStatusline = exports.estimateTimeToQuota = exports.BurnRateCalculator = exports.formatQuotaAge = exports.classifyQuotaFreshness = exports.getWorkspaceIdFromPath = exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.getActiveAccountStatus = void 0;
|
|
4
4
|
var accountStatus_1 = require("../accountStatus");
|
|
5
5
|
Object.defineProperty(exports, "getActiveAccountStatus", { enumerable: true, get: function () { return accountStatus_1.getActiveAccountStatus; } });
|
|
6
6
|
var quotaSnapshots_1 = require("../quotaSnapshots");
|
|
7
7
|
Object.defineProperty(exports, "readQuotaSnapshot", { enumerable: true, get: function () { return quotaSnapshots_1.readQuotaSnapshot; } });
|
|
8
|
+
Object.defineProperty(exports, "writeQuotaSnapshot", { enumerable: true, get: function () { return quotaSnapshots_1.writeQuotaSnapshot; } });
|
|
9
|
+
var quotaHistory_1 = require("../quotaHistory");
|
|
10
|
+
Object.defineProperty(exports, "appendQuotaHistorySample", { enumerable: true, get: function () { return quotaHistory_1.appendQuotaHistorySample; } });
|
|
11
|
+
Object.defineProperty(exports, "getWorkspaceIdFromPath", { enumerable: true, get: function () { return quotaHistory_1.getWorkspaceIdFromPath; } });
|
|
12
|
+
var quota_1 = require("../quota");
|
|
13
|
+
Object.defineProperty(exports, "classifyQuotaFreshness", { enumerable: true, get: function () { return quota_1.classifyQuotaFreshness; } });
|
|
14
|
+
Object.defineProperty(exports, "formatQuotaAge", { enumerable: true, get: function () { return quota_1.formatQuotaAge; } });
|
|
8
15
|
var BurnRateCalculator_1 = require("./BurnRateCalculator");
|
|
9
16
|
Object.defineProperty(exports, "BurnRateCalculator", { enumerable: true, get: function () { return BurnRateCalculator_1.BurnRateCalculator; } });
|
|
10
17
|
Object.defineProperty(exports, "estimateTimeToQuota", { enumerable: true, get: function () { return BurnRateCalculator_1.estimateTimeToQuota; } });
|
|
11
18
|
var formatter_1 = require("./formatter");
|
|
12
19
|
Object.defineProperty(exports, "formatStatusline", { enumerable: true, get: function () { return formatter_1.formatStatusline; } });
|
|
13
20
|
Object.defineProperty(exports, "selectStatuslineAccount", { enumerable: true, get: function () { return formatter_1.selectStatuslineAccount; } });
|
|
21
|
+
var claudeStatuslinePayload_1 = require("./claudeStatuslinePayload");
|
|
22
|
+
Object.defineProperty(exports, "parseClaudeStatuslinePayload", { enumerable: true, get: function () { return claudeStatuslinePayload_1.parseClaudeStatuslinePayload; } });
|
|
23
|
+
Object.defineProperty(exports, "quotaFromStatuslinePayload", { enumerable: true, get: function () { return claudeStatuslinePayload_1.quotaFromStatuslinePayload; } });
|
|
24
|
+
var stateFile_1 = require("../stateFile");
|
|
25
|
+
Object.defineProperty(exports, "STATE_FILE_NAME", { enumerable: true, get: function () { return stateFile_1.STATE_FILE_NAME; } });
|
|
26
|
+
Object.defineProperty(exports, "STATE_FILE_SCHEMA_VERSION", { enumerable: true, get: function () { return stateFile_1.STATE_FILE_SCHEMA_VERSION; } });
|
|
27
|
+
Object.defineProperty(exports, "billingBlockToStateFile", { enumerable: true, get: function () { return stateFile_1.billingBlockToStateFile; } });
|
|
28
|
+
Object.defineProperty(exports, "getStateFilePath", { enumerable: true, get: function () { return stateFile_1.getStateFilePath; } });
|
|
29
|
+
Object.defineProperty(exports, "quotaToStateFile", { enumerable: true, get: function () { return stateFile_1.quotaToStateFile; } });
|
|
30
|
+
Object.defineProperty(exports, "readStateFile", { enumerable: true, get: function () { return stateFile_1.readStateFile; } });
|
|
31
|
+
Object.defineProperty(exports, "writeStateFile", { enumerable: true, get: function () { return stateFile_1.writeStateFile; } });
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One vocabulary for token totals across every Sidekick surface.
|
|
3
|
+
*
|
|
4
|
+
* Every report, dashboard, status bar, and export derives its headline token
|
|
5
|
+
* numbers from here so "Total" means the same thing everywhere:
|
|
6
|
+
*
|
|
7
|
+
* - `total` — every token the provider billed: uncached input, cache writes,
|
|
8
|
+
* cache reads, and output. Matches `NormalizedUsage.totalTokens`
|
|
9
|
+
* and the per-model `tokens` the `EventAggregator` keeps.
|
|
10
|
+
* - `context` — tokens that occupied the context window on the last request:
|
|
11
|
+
* uncached input, cache writes, and cache reads. Output is
|
|
12
|
+
* excluded, matching the input-only formula Claude Code uses for
|
|
13
|
+
* its status-line `used_percentage`.
|
|
14
|
+
* - `output` — output tokens as billed.
|
|
15
|
+
*
|
|
16
|
+
* Cache reads are usually the majority of a coding-agent session's tokens, so
|
|
17
|
+
* an "input + output" total silently drops most of the work. Sidekick used to
|
|
18
|
+
* do that on some surfaces and not others; this helper is the single place the
|
|
19
|
+
* arithmetic lives.
|
|
20
|
+
*
|
|
21
|
+
* Browser-safe: no Node imports.
|
|
22
|
+
*
|
|
23
|
+
* @module tokenSummary
|
|
24
|
+
*/
|
|
25
|
+
/** Any object carrying the four billable token buckets. */
|
|
26
|
+
export interface TokenTotalsLike {
|
|
27
|
+
inputTokens: number;
|
|
28
|
+
outputTokens: number;
|
|
29
|
+
cacheWriteTokens?: number;
|
|
30
|
+
cacheReadTokens?: number;
|
|
31
|
+
/**
|
|
32
|
+
* A provider-semantics-aware total, when the source already computed one
|
|
33
|
+
* (for example `AggregatedTokens.totalTokens`). Preferred over the
|
|
34
|
+
* four-bucket sum because it already accounts for whether reasoning tokens
|
|
35
|
+
* were billed inside or outside `outputTokens`.
|
|
36
|
+
*/
|
|
37
|
+
totalTokens?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface TokenSummary {
|
|
40
|
+
/** Everything billed: uncached input + cache writes + cache reads + output. */
|
|
41
|
+
total: number;
|
|
42
|
+
/** Tokens occupying the context window: uncached input + cache writes + cache reads. */
|
|
43
|
+
context: number;
|
|
44
|
+
/** Uncached input tokens. */
|
|
45
|
+
input: number;
|
|
46
|
+
/** Output tokens as billed. */
|
|
47
|
+
output: number;
|
|
48
|
+
/** Tokens read from the prompt cache. */
|
|
49
|
+
cacheRead: number;
|
|
50
|
+
/** Tokens written to the prompt cache. */
|
|
51
|
+
cacheWrite: number;
|
|
52
|
+
/** Fraction of context tokens that came from cache reads (0..1), or null with no input. */
|
|
53
|
+
cacheHitRatio: number | null;
|
|
54
|
+
}
|
|
55
|
+
/** Column label for `TokenSummary.total` on any surface. */
|
|
56
|
+
export declare const TOKEN_TOTAL_LABEL = "Total (incl. cache)";
|
|
57
|
+
/** Column label for `TokenSummary.context` on any surface. */
|
|
58
|
+
export declare const TOKEN_CONTEXT_LABEL = "Context";
|
|
59
|
+
/** Derive the shared token vocabulary from any four-bucket token record. */
|
|
60
|
+
export declare function summarizeTokens(totals: TokenTotalsLike): TokenSummary;
|
|
61
|
+
/** Sum several token records bucket-by-bucket before summarizing. */
|
|
62
|
+
export declare function sumTokenTotals(records: readonly TokenTotalsLike[]): TokenTotalsLike;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* One vocabulary for token totals across every Sidekick surface.
|
|
4
|
+
*
|
|
5
|
+
* Every report, dashboard, status bar, and export derives its headline token
|
|
6
|
+
* numbers from here so "Total" means the same thing everywhere:
|
|
7
|
+
*
|
|
8
|
+
* - `total` — every token the provider billed: uncached input, cache writes,
|
|
9
|
+
* cache reads, and output. Matches `NormalizedUsage.totalTokens`
|
|
10
|
+
* and the per-model `tokens` the `EventAggregator` keeps.
|
|
11
|
+
* - `context` — tokens that occupied the context window on the last request:
|
|
12
|
+
* uncached input, cache writes, and cache reads. Output is
|
|
13
|
+
* excluded, matching the input-only formula Claude Code uses for
|
|
14
|
+
* its status-line `used_percentage`.
|
|
15
|
+
* - `output` — output tokens as billed.
|
|
16
|
+
*
|
|
17
|
+
* Cache reads are usually the majority of a coding-agent session's tokens, so
|
|
18
|
+
* an "input + output" total silently drops most of the work. Sidekick used to
|
|
19
|
+
* do that on some surfaces and not others; this helper is the single place the
|
|
20
|
+
* arithmetic lives.
|
|
21
|
+
*
|
|
22
|
+
* Browser-safe: no Node imports.
|
|
23
|
+
*
|
|
24
|
+
* @module tokenSummary
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.TOKEN_CONTEXT_LABEL = exports.TOKEN_TOTAL_LABEL = void 0;
|
|
28
|
+
exports.summarizeTokens = summarizeTokens;
|
|
29
|
+
exports.sumTokenTotals = sumTokenTotals;
|
|
30
|
+
/** Column label for `TokenSummary.total` on any surface. */
|
|
31
|
+
exports.TOKEN_TOTAL_LABEL = 'Total (incl. cache)';
|
|
32
|
+
/** Column label for `TokenSummary.context` on any surface. */
|
|
33
|
+
exports.TOKEN_CONTEXT_LABEL = 'Context';
|
|
34
|
+
function count(value) {
|
|
35
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
|
|
36
|
+
}
|
|
37
|
+
/** Derive the shared token vocabulary from any four-bucket token record. */
|
|
38
|
+
function summarizeTokens(totals) {
|
|
39
|
+
const input = count(totals.inputTokens);
|
|
40
|
+
const output = count(totals.outputTokens);
|
|
41
|
+
const cacheRead = count(totals.cacheReadTokens);
|
|
42
|
+
const cacheWrite = count(totals.cacheWriteTokens);
|
|
43
|
+
const context = input + cacheWrite + cacheRead;
|
|
44
|
+
const fourBucket = context + output;
|
|
45
|
+
const provided = totals.totalTokens;
|
|
46
|
+
const total = typeof provided === 'number' && Number.isFinite(provided) && provided >= 0
|
|
47
|
+
? Math.floor(provided)
|
|
48
|
+
: fourBucket;
|
|
49
|
+
return {
|
|
50
|
+
total,
|
|
51
|
+
context,
|
|
52
|
+
input,
|
|
53
|
+
output,
|
|
54
|
+
cacheRead,
|
|
55
|
+
cacheWrite,
|
|
56
|
+
cacheHitRatio: context > 0 ? cacheRead / context : null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Sum several token records bucket-by-bucket before summarizing. */
|
|
60
|
+
function sumTokenTotals(records) {
|
|
61
|
+
let inputTokens = 0;
|
|
62
|
+
let outputTokens = 0;
|
|
63
|
+
let cacheWriteTokens = 0;
|
|
64
|
+
let cacheReadTokens = 0;
|
|
65
|
+
for (const record of records) {
|
|
66
|
+
inputTokens += count(record.inputTokens);
|
|
67
|
+
outputTokens += count(record.outputTokens);
|
|
68
|
+
cacheWriteTokens += count(record.cacheWriteTokens);
|
|
69
|
+
cacheReadTokens += count(record.cacheReadTokens);
|
|
70
|
+
}
|
|
71
|
+
return { inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens };
|
|
72
|
+
}
|
package/dist/transcript.d.ts
CHANGED
|
@@ -77,7 +77,12 @@ export interface CanonicalSessionTranscript {
|
|
|
77
77
|
events: NormalizedUsage[];
|
|
78
78
|
costs: NormalizedUsageCost[];
|
|
79
79
|
totals: CanonicalTranscriptUsageTotals;
|
|
80
|
+
/** Strict total: `null` unless every usage event could be priced. */
|
|
80
81
|
totalCostUsd: number | null;
|
|
82
|
+
/** Sum of the events that could be priced; pair with `unpricedEvents`. */
|
|
83
|
+
pricedCostUsd: number;
|
|
84
|
+
/** Usage events with neither a reported cost nor a catalog price. */
|
|
85
|
+
unpricedEvents: number;
|
|
81
86
|
};
|
|
82
87
|
provenance: {
|
|
83
88
|
source: 'session-events';
|
package/dist/transcript.js
CHANGED
|
@@ -144,6 +144,8 @@ function projectSessionTranscript(events, options = {}) {
|
|
|
144
144
|
totalCostUsd: usageCosts.length > 0 && usageCosts.every((cost) => cost.costUsd !== null)
|
|
145
145
|
? usageCosts.reduce((sum, cost) => sum + (cost.costUsd ?? 0), 0)
|
|
146
146
|
: null,
|
|
147
|
+
pricedCostUsd: usageCosts.reduce((sum, cost) => sum + (cost.costUsd ?? 0), 0),
|
|
148
|
+
unpricedEvents: usageCosts.filter((cost) => cost.costUsd === null).length,
|
|
147
149
|
},
|
|
148
150
|
provenance: { source: 'session-events', fidelity, eventCount: events.length },
|
|
149
151
|
};
|
|
@@ -69,6 +69,8 @@ export interface ProviderSessionAdapterV1 {
|
|
|
69
69
|
dispose(): void;
|
|
70
70
|
}
|
|
71
71
|
export declare function derivePendingUserRequestV1(provider: ProviderId, sessionId: string, sourcePath: string, events: SessionEvent[]): PendingUserRequestV1 | null;
|
|
72
|
+
/** The activity detector's reason for a session produced by this adapter, if known. */
|
|
73
|
+
export declare function getObservedActivityReason(session: ObservedAgentSessionV1): string | undefined;
|
|
72
74
|
export interface ProviderSessionAdapterV1Options {
|
|
73
75
|
pollIntervalMs?: number;
|
|
74
76
|
observationOnly?: boolean;
|