sidekick-shared 0.24.5 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -0
- package/README.md +205 -52
- package/dist/accountChangeSignal.d.ts +3 -0
- package/dist/accountChangeSignal.js +19 -0
- package/dist/accountChanges.d.ts +21 -0
- package/dist/accountChanges.js +170 -0
- package/dist/accountRegistry.d.ts +25 -2
- package/dist/accountRegistry.js +41 -13
- package/dist/accountStatus.d.ts +9 -1
- package/dist/accountStatus.js +3 -3
- package/dist/accounts.d.ts +3 -1
- package/dist/accounts.js +84 -64
- package/dist/aggregation/EventAggregator.d.ts +15 -2
- package/dist/aggregation/EventAggregator.js +54 -9
- package/dist/aggregation/costProvenance.d.ts +14 -0
- package/dist/aggregation/costProvenance.js +35 -0
- package/dist/aggregation/snapshot.d.ts +13 -2
- package/dist/aggregation/snapshot.js +57 -6
- package/dist/aggregation/types.d.ts +30 -1
- package/dist/analytics/compactionLedger.js +15 -5
- package/dist/analytics/qualityScore.js +2 -1
- package/dist/browser.d.ts +19 -4
- package/dist/browser.js +43 -1
- package/dist/codexProfiles.d.ts +3 -1
- package/dist/codexProfiles.js +4 -2
- package/dist/codexQuota.d.ts +2 -0
- package/dist/codexQuota.js +11 -66
- package/dist/codexQuotaWatcher.d.ts +6 -0
- package/dist/codexQuotaWatcher.js +101 -47
- package/dist/failingTools.d.ts +37 -0
- package/dist/failingTools.js +70 -0
- package/dist/formatters/sessionDump.js +13 -7
- package/dist/formatting.d.ts +13 -0
- package/dist/formatting.js +35 -0
- package/dist/historicalStore.d.ts +47 -0
- package/dist/historicalStore.js +312 -0
- package/dist/index.d.ts +67 -23
- package/dist/index.js +138 -26
- package/dist/modelAliases.d.ts +13 -0
- package/dist/modelAliases.js +58 -0
- package/dist/modelCatalog.d.ts +32 -0
- package/dist/modelCatalog.js +145 -0
- package/dist/modelContext.d.ts +36 -3
- package/dist/modelContext.js +109 -34
- package/dist/modelInfo.d.ts +30 -1
- package/dist/modelInfo.js +116 -26
- package/dist/multiProviderQuotaService.d.ts +8 -0
- package/dist/multiProviderQuotaService.js +64 -3
- package/dist/node.d.ts +23 -4
- package/dist/node.js +50 -1
- package/dist/observedContextWindows.d.ts +7 -0
- package/dist/observedContextWindows.js +17 -2
- package/dist/observedSessionCollector.d.ts +72 -6
- package/dist/observedSessionCollector.js +440 -47
- package/dist/parsers/codexHistory.d.ts +4 -2
- package/dist/parsers/codexHistory.js +9 -2
- package/dist/parsers/sessionActivityDetector.d.ts +28 -0
- package/dist/parsers/sessionActivityDetector.js +110 -15
- package/dist/parsers/sessionPathResolver.d.ts +11 -0
- package/dist/parsers/sessionPathResolver.js +13 -10
- package/dist/peakHours.d.ts +34 -3
- package/dist/peakHours.js +92 -7
- package/dist/pricingCatalog.d.ts +7 -0
- package/dist/pricingCatalog.js +8 -5
- package/dist/providerIds.d.ts +11 -0
- package/dist/providerIds.js +9 -0
- package/dist/providerQuota.d.ts +3 -1
- package/dist/providerQuota.js +3 -0
- package/dist/providers/claudeCode.d.ts +10 -1
- package/dist/providers/claudeCode.js +211 -131
- package/dist/providers/codex.d.ts +20 -1
- package/dist/providers/codex.js +308 -244
- package/dist/providers/codexDatabase.d.ts +9 -0
- package/dist/providers/codexDatabase.js +107 -2
- package/dist/providers/detect.d.ts +2 -0
- package/dist/providers/detect.js +29 -24
- package/dist/providers/diagnostics.d.ts +13 -0
- package/dist/providers/diagnostics.js +88 -0
- package/dist/providers/factory.d.ts +20 -0
- package/dist/providers/factory.js +57 -0
- package/dist/providers/openCode.d.ts +22 -1
- package/dist/providers/openCode.js +460 -126
- package/dist/providers/openCodeDatabase.d.ts +10 -0
- package/dist/providers/openCodeDatabase.js +69 -0
- package/dist/providers/rolloutWalker.d.ts +53 -0
- package/dist/providers/rolloutWalker.js +209 -0
- package/dist/providers/types.d.ts +82 -2
- package/dist/providers/types.js +4 -0
- package/dist/quota.d.ts +34 -4
- package/dist/quota.js +37 -9
- package/dist/quotaHistory.d.ts +3 -2
- package/dist/quotaHistory.js +36 -35
- package/dist/quotaPoller.d.ts +13 -1
- package/dist/quotaPoller.js +65 -3
- package/dist/quotaResolver.d.ts +81 -0
- package/dist/quotaResolver.js +213 -0
- package/dist/quotaSnapshots.d.ts +15 -2
- package/dist/quotaSnapshots.js +25 -3
- package/dist/quotaThresholds.d.ts +60 -0
- package/dist/quotaThresholds.js +90 -0
- package/dist/report/htmlReportGenerator.js +10 -4
- package/dist/report/sessionReportInputs.d.ts +23 -0
- package/dist/report/sessionReportInputs.js +34 -0
- package/dist/schemas/accountManager.d.ts +2 -2
- package/dist/schemas/accountManager.js +2 -4
- package/dist/schemas/accountStatus.d.ts +3 -0
- package/dist/schemas/accountStatus.js +1 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.js +16 -2
- package/dist/schemas/observedSessionV1.d.ts +27 -26
- package/dist/schemas/observedSessionV1.js +6 -4
- package/dist/schemas/quota.d.ts +141 -2
- package/dist/schemas/quota.js +20 -8
- package/dist/schemas/quotaHistory.d.ts +3 -2
- package/dist/schemas/quotaHistory.js +1 -1
- package/dist/schemas/sessionEvent.d.ts +6 -0
- package/dist/schemas/sessionEvent.js +1 -0
- package/dist/schemas/stateFile.d.ts +198 -0
- package/dist/schemas/stateFile.js +77 -0
- package/dist/schemas/tokenEstimation.d.ts +1 -1
- package/dist/schemas/transcript.js +2 -0
- package/dist/sessionFingerprint.d.ts +25 -0
- package/dist/sessionFingerprint.js +46 -0
- package/dist/sessionMonitor.d.ts +15 -0
- package/dist/sessionMonitor.js +142 -0
- package/dist/sessionPreviews.d.ts +33 -2
- package/dist/sessionPreviews.js +333 -6
- package/dist/sessionStats.d.ts +51 -0
- package/dist/sessionStats.js +166 -0
- package/dist/stateFile.d.ts +113 -0
- package/dist/stateFile.js +160 -0
- package/dist/statusline/claudeStatuslinePayload.d.ts +89 -0
- package/dist/statusline/claudeStatuslinePayload.js +152 -0
- package/dist/statusline/formatter.d.ts +13 -0
- package/dist/statusline/formatter.js +48 -6
- package/dist/statusline/index.d.ts +10 -2
- package/dist/statusline/index.js +19 -1
- package/dist/tokenSummary.d.ts +62 -0
- package/dist/tokenSummary.js +72 -0
- package/dist/transcript.d.ts +5 -0
- package/dist/transcript.js +2 -0
- package/dist/types/observedSessionV1.d.ts +8 -2
- package/dist/types/observedSessionV1.js +88 -25
- package/dist/types/sessionEvent.d.ts +2 -0
- package/dist/usage/billingBlocks.d.ts +85 -0
- package/dist/usage/billingBlocks.js +153 -0
- package/dist/usage/importSessionHistory.d.ts +53 -0
- package/dist/usage/importSessionHistory.js +88 -0
- package/dist/usage/usageEvents.d.ts +92 -0
- package/dist/usage/usageEvents.js +322 -0
- package/dist/usage/usageReports.d.ts +76 -0
- package/dist/usage/usageReports.js +208 -0
- package/dist/watchers/factory.d.ts +6 -0
- package/dist/watchers/factory.js +30 -25
- package/dist/zaiQuotaApi.d.ts +2 -0
- package/dist/zaiQuotaApi.js +2 -0
- package/package.json +22 -1
|
@@ -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
|
};
|
|
@@ -45,6 +45,8 @@ export interface ObservedAgentSessionV1 {
|
|
|
45
45
|
costUsd: ObservedValueV1<number | null>;
|
|
46
46
|
};
|
|
47
47
|
pendingUserRequest: PendingUserRequestV1 | null;
|
|
48
|
+
/** When model/usage content was last read; unchanged on collector cache hits. */
|
|
49
|
+
contentObservedAt?: string;
|
|
48
50
|
observedAt: string;
|
|
49
51
|
}
|
|
50
52
|
export interface ProviderCapabilitiesV1 {
|
|
@@ -67,6 +69,10 @@ export interface ProviderSessionAdapterV1 {
|
|
|
67
69
|
dispose(): void;
|
|
68
70
|
}
|
|
69
71
|
export declare function derivePendingUserRequestV1(provider: ProviderId, sessionId: string, sourcePath: string, events: SessionEvent[]): PendingUserRequestV1 | null;
|
|
70
|
-
|
|
72
|
+
/** The activity detector's reason for a session produced by this adapter, if known. */
|
|
73
|
+
export declare function getObservedActivityReason(session: ObservedAgentSessionV1): string | undefined;
|
|
74
|
+
export interface ProviderSessionAdapterV1Options {
|
|
71
75
|
pollIntervalMs?: number;
|
|
72
|
-
|
|
76
|
+
observationOnly?: boolean;
|
|
77
|
+
}
|
|
78
|
+
export declare function createProviderSessionAdapterV1(sessionProvider: SessionProviderBase, options?: ProviderSessionAdapterV1Options): ProviderSessionAdapterV1;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.derivePendingUserRequestV1 = derivePendingUserRequestV1;
|
|
4
|
+
exports.getObservedActivityReason = getObservedActivityReason;
|
|
4
5
|
exports.createProviderSessionAdapterV1 = createProviderSessionAdapterV1;
|
|
5
6
|
const transcript_1 = require("../transcript");
|
|
7
|
+
const sessionActivityDetector_1 = require("../parsers/sessionActivityDetector");
|
|
8
|
+
const sessionStats_1 = require("../sessionStats");
|
|
9
|
+
const sessionFingerprint_1 = require("../sessionFingerprint");
|
|
6
10
|
function evidence(provider, sessionId, sourcePath, event, eventIndex) {
|
|
7
11
|
return [
|
|
8
12
|
{
|
|
@@ -98,52 +102,78 @@ function waitingToolPrompt(input, toolName) {
|
|
|
98
102
|
return 'Approve the proposed plan?';
|
|
99
103
|
return null;
|
|
100
104
|
}
|
|
101
|
-
function providerCapabilities(provider) {
|
|
105
|
+
function providerCapabilities(provider, observationOnly) {
|
|
102
106
|
const refs = [];
|
|
103
107
|
const known = (value) => observed(value, 'inferred', 1, refs);
|
|
104
108
|
return {
|
|
105
109
|
schemaVersion: 1,
|
|
106
110
|
provider,
|
|
107
|
-
resume: known(
|
|
111
|
+
resume: known(!observationOnly),
|
|
108
112
|
forkLineage: known(provider !== 'opencode'),
|
|
109
113
|
quotaSource: known(provider === 'claude-code' ? 'api' : provider === 'codex' ? 'mixed' : 'none'),
|
|
110
114
|
assetExtraction: known(true),
|
|
111
115
|
};
|
|
112
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Detector reason behind each observed session's activity, kept off the
|
|
119
|
+
* versioned V1 shape so cache refreshes can tell a grace-period "active" from
|
|
120
|
+
* a genuinely ongoing one.
|
|
121
|
+
*/
|
|
122
|
+
const activityReasons = new WeakMap();
|
|
123
|
+
/** The activity detector's reason for a session produced by this adapter, if known. */
|
|
124
|
+
function getObservedActivityReason(session) {
|
|
125
|
+
return activityReasons.get(session);
|
|
126
|
+
}
|
|
113
127
|
function createProviderSessionAdapterV1(sessionProvider, options = {}) {
|
|
114
128
|
const pollIntervalMs = options.pollIntervalMs ?? 2_000;
|
|
115
129
|
const adapter = {
|
|
116
130
|
schemaVersion: 1,
|
|
117
131
|
provider: sessionProvider.id,
|
|
118
|
-
capabilities: providerCapabilities(sessionProvider.id),
|
|
132
|
+
capabilities: providerCapabilities(sessionProvider.id, options.observationOnly === true),
|
|
119
133
|
async discover(cwd) {
|
|
120
134
|
return Promise.all(sessionProvider.findAllSessions(cwd).map((sessionPath) => adapter.read(sessionPath, cwd)));
|
|
121
135
|
},
|
|
122
136
|
async read(sessionPath, cwd = '') {
|
|
123
|
-
|
|
124
|
-
|
|
137
|
+
// One reader pass feeds stats, transcript, activity, and the pending
|
|
138
|
+
// request; the reader is flushed so a trailing partial line is included.
|
|
139
|
+
const sessionId = sessionProvider.getSessionId(sessionPath);
|
|
140
|
+
const reader = sessionProvider.createReader(sessionPath);
|
|
141
|
+
const events = reader.readAll();
|
|
142
|
+
reader.flush();
|
|
143
|
+
const stats = (0, sessionStats_1.computeSessionFileStats)(events, {
|
|
144
|
+
providerId: sessionProvider.id,
|
|
145
|
+
sessionId,
|
|
146
|
+
filePath: sessionPath,
|
|
147
|
+
label: '',
|
|
148
|
+
computeContextSize: (0, sessionStats_1.providerContextSizeFn)(sessionProvider),
|
|
149
|
+
});
|
|
125
150
|
const transcript = (0, transcript_1.projectSessionTranscript)(events, {
|
|
126
151
|
provider: sessionProvider.id,
|
|
127
|
-
sessionId
|
|
152
|
+
sessionId,
|
|
128
153
|
sourcePath: sessionPath,
|
|
129
154
|
});
|
|
130
|
-
const refs = evidence(sessionProvider.id,
|
|
155
|
+
const refs = evidence(sessionProvider.id, sessionId, sessionPath);
|
|
131
156
|
const model = Object.entries(stats.modelUsage).sort((left, right) => right[1].calls - left[1].calls)[0]?.[0] ?? null;
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
157
|
+
const parts = (0, sessionFingerprint_1.sessionFingerprintParts)(sessionProvider, sessionPath);
|
|
158
|
+
const activityResult = (0, sessionActivityDetector_1.classifySessionActivity)({ events, mtimeMs: parts?.mtimeMs ?? null });
|
|
159
|
+
const activity = activityResult.state === 'ongoing'
|
|
160
|
+
? 'active'
|
|
161
|
+
: activityResult.state === 'stale'
|
|
162
|
+
? 'idle'
|
|
163
|
+
: 'ended';
|
|
135
164
|
const allCostsReported = transcript.usage.costs.length > 0 &&
|
|
136
165
|
transcript.usage.costs.every((cost) => cost.source === 'provider-reported');
|
|
137
|
-
|
|
166
|
+
const observedAt = new Date().toISOString();
|
|
167
|
+
const session = {
|
|
138
168
|
schemaVersion: 1,
|
|
139
169
|
identity: {
|
|
140
170
|
provider: sessionProvider.id,
|
|
141
|
-
sessionId
|
|
171
|
+
sessionId,
|
|
142
172
|
sourcePath: sessionPath,
|
|
143
173
|
},
|
|
144
174
|
cwd: observed(cwd, 'inferred', cwd ? 0.95 : 0.4, refs),
|
|
145
175
|
model: observed(model, model ? 'reported' : 'inferred', model ? 1 : 0.2, refs),
|
|
146
|
-
activity: observed(
|
|
176
|
+
activity: observed(activity, 'inferred', 0.85, refs),
|
|
147
177
|
usage: {
|
|
148
178
|
inputTokens: observed(stats.tokens.input, 'reported', 1, refs),
|
|
149
179
|
outputTokens: observed(stats.tokens.output, 'reported', 1, refs),
|
|
@@ -156,28 +186,61 @@ function createProviderSessionAdapterV1(sessionProvider, options = {}) {
|
|
|
156
186
|
? 'inferred'
|
|
157
187
|
: 'estimated', allCostsReported ? 1 : transcript.usage.totalCostUsd === null ? 0.5 : 0.9, refs),
|
|
158
188
|
},
|
|
159
|
-
pendingUserRequest: derivePendingUserRequestV1(sessionProvider.id,
|
|
160
|
-
|
|
189
|
+
pendingUserRequest: derivePendingUserRequestV1(sessionProvider.id, sessionId, sessionPath, events),
|
|
190
|
+
contentObservedAt: observedAt,
|
|
191
|
+
observedAt,
|
|
161
192
|
};
|
|
193
|
+
activityReasons.set(session, activityResult.reason);
|
|
194
|
+
return session;
|
|
162
195
|
},
|
|
163
196
|
watch(sessionPath, listener, cwd) {
|
|
164
197
|
let disposed = false;
|
|
165
198
|
let lastSignature = '';
|
|
199
|
+
let lastFingerprint = null;
|
|
200
|
+
let lastSession = null;
|
|
201
|
+
const emitIfChanged = (session) => {
|
|
202
|
+
const signature = JSON.stringify({
|
|
203
|
+
activity: session.activity,
|
|
204
|
+
model: session.model,
|
|
205
|
+
usage: session.usage,
|
|
206
|
+
pendingUserRequest: session.pendingUserRequest,
|
|
207
|
+
});
|
|
208
|
+
if (signature !== lastSignature) {
|
|
209
|
+
lastSignature = signature;
|
|
210
|
+
listener(session);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
166
213
|
const update = async () => {
|
|
167
214
|
if (disposed)
|
|
168
215
|
return;
|
|
169
216
|
try {
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
217
|
+
const parts = (0, sessionFingerprint_1.sessionFingerprintParts)(sessionProvider, sessionPath);
|
|
218
|
+
const fingerprint = parts ? (0, sessionFingerprint_1.fingerprintString)(parts) : null;
|
|
219
|
+
if (lastSession && parts && fingerprint === lastFingerprint) {
|
|
220
|
+
// Unchanged content: only the activity can drift with time, so
|
|
221
|
+
// refresh it from the cached classification instead of re-reading.
|
|
222
|
+
const previousReason = activityReasons.get(lastSession);
|
|
223
|
+
const refreshed = (0, sessionActivityDetector_1.refreshSessionActivityState)(lastSession.activity.value, parts.mtimeMs, Date.now(), previousReason);
|
|
224
|
+
if (refreshed !== lastSession.activity.value) {
|
|
225
|
+
const session = {
|
|
226
|
+
...lastSession,
|
|
227
|
+
activity: { ...lastSession.activity, value: refreshed },
|
|
228
|
+
observedAt: new Date().toISOString(),
|
|
229
|
+
};
|
|
230
|
+
activityReasons.set(session, refreshed === 'idle'
|
|
231
|
+
? 'mtime-stale'
|
|
232
|
+
: previousReason === 'grace-period'
|
|
233
|
+
? 'ending-event'
|
|
234
|
+
: 'no-activity-signal');
|
|
235
|
+
lastSession = session;
|
|
236
|
+
emitIfChanged(session);
|
|
237
|
+
}
|
|
238
|
+
return;
|
|
180
239
|
}
|
|
240
|
+
const session = await adapter.read(sessionPath, cwd);
|
|
241
|
+
lastFingerprint = fingerprint;
|
|
242
|
+
lastSession = session;
|
|
243
|
+
emitIfChanged(session);
|
|
181
244
|
}
|
|
182
245
|
catch {
|
|
183
246
|
// Observed sources can disappear between polling iterations.
|
|
@@ -47,6 +47,8 @@ export interface SessionMessage {
|
|
|
47
47
|
normalizedUsage?: import('../usageNormalization').NormalizedUsage;
|
|
48
48
|
/** Message content (may be string or structured content) */
|
|
49
49
|
content?: unknown;
|
|
50
|
+
/** Provider stop reason (`end_turn`, `tool_use`, …) when the transcript carries one. */
|
|
51
|
+
stop_reason?: string;
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
52
54
|
* Canonical session event from JSONL/DB log files.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Five-hour billing blocks computed from usage events.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the ccusage convention: a block opens at the first usage event
|
|
5
|
+
* (aligned down to the UTC hour), lasts five hours, and an event that lands
|
|
6
|
+
* after the block's end or more than five hours after the previous event opens
|
|
7
|
+
* a new block. The active block also reports a burn rate and an end-of-block
|
|
8
|
+
* projection. Browser-safe: no Node imports.
|
|
9
|
+
*
|
|
10
|
+
* @module usage/billingBlocks
|
|
11
|
+
*/
|
|
12
|
+
import type { AggregatedCostProvenance } from '../aggregation/types';
|
|
13
|
+
import type { TokenTotalsLike } from '../tokenSummary';
|
|
14
|
+
import type { PricingProvenance } from '../usageNormalization';
|
|
15
|
+
/** Length of one billing block. */
|
|
16
|
+
export declare const BILLING_BLOCK_DURATION_MS: number;
|
|
17
|
+
/** Minimal usage event the block calculator needs. */
|
|
18
|
+
export interface BillingBlockInput {
|
|
19
|
+
/** Event time (ms since epoch, ISO string, or Date). */
|
|
20
|
+
timestamp: number | string | Date;
|
|
21
|
+
tokens: TokenTotalsLike;
|
|
22
|
+
/** Priced cost for the event, or null when it could not be priced. */
|
|
23
|
+
costUsd: number | null;
|
|
24
|
+
costProvenance?: PricingProvenance;
|
|
25
|
+
model?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface BillingBlockTokens {
|
|
28
|
+
inputTokens: number;
|
|
29
|
+
outputTokens: number;
|
|
30
|
+
cacheWriteTokens: number;
|
|
31
|
+
cacheReadTokens: number;
|
|
32
|
+
/** Cache-inclusive total (`summarizeTokens().total`). */
|
|
33
|
+
total: number;
|
|
34
|
+
}
|
|
35
|
+
export interface BillingBlockModelUsage {
|
|
36
|
+
calls: number;
|
|
37
|
+
/** Cache-inclusive tokens. */
|
|
38
|
+
tokens: number;
|
|
39
|
+
costUsd: number;
|
|
40
|
+
}
|
|
41
|
+
export interface BillingBlock {
|
|
42
|
+
/** Block identifier: the ISO start time. */
|
|
43
|
+
id: string;
|
|
44
|
+
/** ISO start (aligned to the hour when `alignToHour` is set). */
|
|
45
|
+
start: string;
|
|
46
|
+
/** ISO end (`start` + block duration). */
|
|
47
|
+
end: string;
|
|
48
|
+
firstEvent: string;
|
|
49
|
+
lastEvent: string;
|
|
50
|
+
/** `now` is inside the block and within one block length of the last event. */
|
|
51
|
+
isActive: boolean;
|
|
52
|
+
calls: number;
|
|
53
|
+
tokens: BillingBlockTokens;
|
|
54
|
+
costUsd: number;
|
|
55
|
+
costProvenance: AggregatedCostProvenance;
|
|
56
|
+
unpricedCalls: number;
|
|
57
|
+
models: Record<string, BillingBlockModelUsage>;
|
|
58
|
+
/** Time from the first event to `now` (active) or to the last event (closed). */
|
|
59
|
+
elapsedMs: number;
|
|
60
|
+
/** Time left until `end` for the active block; 0 otherwise. */
|
|
61
|
+
remainingMs: number;
|
|
62
|
+
/** Tokens per minute over `elapsedMs`. */
|
|
63
|
+
burnRatePerMinute: number;
|
|
64
|
+
/** Tokens expected by `end` at the current burn rate (equals `tokens.total` for closed blocks). */
|
|
65
|
+
projectedTokens: number;
|
|
66
|
+
/** Cost expected by `end` at the current cost per token (equals `costUsd` for closed blocks). */
|
|
67
|
+
projectedCostUsd: number;
|
|
68
|
+
}
|
|
69
|
+
export interface ComputeBillingBlocksOptions {
|
|
70
|
+
/** Clock used for activity and projections (default `Date.now()`). */
|
|
71
|
+
now?: Date | number;
|
|
72
|
+
/** Block length (default five hours). */
|
|
73
|
+
blockDurationMs?: number;
|
|
74
|
+
/** Align each block's start down to the UTC hour (default true, as ccusage does). */
|
|
75
|
+
alignToHour?: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Group usage events into billing blocks, oldest first.
|
|
79
|
+
*
|
|
80
|
+
* Events with an unparseable timestamp are ignored. Events are sorted before
|
|
81
|
+
* grouping, so callers may pass them in any order.
|
|
82
|
+
*/
|
|
83
|
+
export declare function computeBillingBlocks(events: readonly BillingBlockInput[], options?: ComputeBillingBlocksOptions): BillingBlock[];
|
|
84
|
+
/** The block that is still open at the calculator's `now`, if any. */
|
|
85
|
+
export declare function findActiveBillingBlock(blocks: readonly BillingBlock[]): BillingBlock | null;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Five-hour billing blocks computed from usage events.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the ccusage convention: a block opens at the first usage event
|
|
6
|
+
* (aligned down to the UTC hour), lasts five hours, and an event that lands
|
|
7
|
+
* after the block's end or more than five hours after the previous event opens
|
|
8
|
+
* a new block. The active block also reports a burn rate and an end-of-block
|
|
9
|
+
* projection. Browser-safe: no Node imports.
|
|
10
|
+
*
|
|
11
|
+
* @module usage/billingBlocks
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.BILLING_BLOCK_DURATION_MS = void 0;
|
|
15
|
+
exports.computeBillingBlocks = computeBillingBlocks;
|
|
16
|
+
exports.findActiveBillingBlock = findActiveBillingBlock;
|
|
17
|
+
const costProvenance_1 = require("../aggregation/costProvenance");
|
|
18
|
+
const BurnRateCalculator_1 = require("../statusline/BurnRateCalculator");
|
|
19
|
+
const tokenSummary_1 = require("../tokenSummary");
|
|
20
|
+
/** Length of one billing block. */
|
|
21
|
+
exports.BILLING_BLOCK_DURATION_MS = 5 * 3_600_000;
|
|
22
|
+
function toMs(value) {
|
|
23
|
+
if (value instanceof Date)
|
|
24
|
+
return value.getTime();
|
|
25
|
+
if (typeof value === 'number')
|
|
26
|
+
return value;
|
|
27
|
+
return Date.parse(value);
|
|
28
|
+
}
|
|
29
|
+
function normalizeInput(event) {
|
|
30
|
+
const timestamp = toMs(event.timestamp);
|
|
31
|
+
if (!Number.isFinite(timestamp))
|
|
32
|
+
return null;
|
|
33
|
+
const costUsd = typeof event.costUsd === 'number' && Number.isFinite(event.costUsd) ? event.costUsd : null;
|
|
34
|
+
return {
|
|
35
|
+
timestamp,
|
|
36
|
+
tokens: event.tokens,
|
|
37
|
+
costUsd,
|
|
38
|
+
costProvenance: costUsd === null ? 'unpriced' : (event.costProvenance ?? 'model-catalog'),
|
|
39
|
+
model: event.model ?? 'unknown',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function floorToUtcHour(ms) {
|
|
43
|
+
return ms - (ms % 3_600_000);
|
|
44
|
+
}
|
|
45
|
+
function buildBlock(events, start, durationMs, nowMs) {
|
|
46
|
+
const end = start + durationMs;
|
|
47
|
+
const first = events[0].timestamp;
|
|
48
|
+
const last = events[events.length - 1].timestamp;
|
|
49
|
+
const isActive = nowMs >= start && nowMs < end && nowMs - last < durationMs;
|
|
50
|
+
const tokens = { inputTokens: 0, outputTokens: 0, cacheWriteTokens: 0, cacheReadTokens: 0 };
|
|
51
|
+
let total = 0;
|
|
52
|
+
let costUsd = 0;
|
|
53
|
+
let reportedCalls = 0;
|
|
54
|
+
let estimatedCalls = 0;
|
|
55
|
+
let unpricedCalls = 0;
|
|
56
|
+
const models = {};
|
|
57
|
+
const burn = new BurnRateCalculator_1.BurnRateCalculator(durationMs / 60_000);
|
|
58
|
+
for (const event of events) {
|
|
59
|
+
const summary = (0, tokenSummary_1.summarizeTokens)(event.tokens);
|
|
60
|
+
tokens.inputTokens += summary.input;
|
|
61
|
+
tokens.outputTokens += summary.output;
|
|
62
|
+
tokens.cacheWriteTokens += summary.cacheWrite;
|
|
63
|
+
tokens.cacheReadTokens += summary.cacheRead;
|
|
64
|
+
total += summary.total;
|
|
65
|
+
burn.addEvent(summary.total, new Date(event.timestamp));
|
|
66
|
+
const model = models[event.model] ?? { calls: 0, tokens: 0, costUsd: 0 };
|
|
67
|
+
model.calls += 1;
|
|
68
|
+
model.tokens += summary.total;
|
|
69
|
+
if (event.costUsd !== null) {
|
|
70
|
+
costUsd += event.costUsd;
|
|
71
|
+
model.costUsd += event.costUsd;
|
|
72
|
+
if (event.costProvenance === 'provider-reported')
|
|
73
|
+
reportedCalls += 1;
|
|
74
|
+
else
|
|
75
|
+
estimatedCalls += 1;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
unpricedCalls += 1;
|
|
79
|
+
}
|
|
80
|
+
models[event.model] = model;
|
|
81
|
+
}
|
|
82
|
+
const rateAt = isActive ? nowMs : last;
|
|
83
|
+
const elapsedMs = Math.max(0, rateAt - first);
|
|
84
|
+
const burnRatePerMinute = burn.calculateBurnRate(new Date(rateAt));
|
|
85
|
+
const remainingMs = isActive ? Math.max(0, end - nowMs) : 0;
|
|
86
|
+
const projectedTokens = isActive
|
|
87
|
+
? Math.round(total + burnRatePerMinute * (remainingMs / 60_000))
|
|
88
|
+
: total;
|
|
89
|
+
const costPerToken = total > 0 ? costUsd / total : 0;
|
|
90
|
+
const projectedCostUsd = isActive ? costUsd + costPerToken * (projectedTokens - total) : costUsd;
|
|
91
|
+
const startIso = new Date(start).toISOString();
|
|
92
|
+
return {
|
|
93
|
+
id: startIso,
|
|
94
|
+
start: startIso,
|
|
95
|
+
end: new Date(end).toISOString(),
|
|
96
|
+
firstEvent: new Date(first).toISOString(),
|
|
97
|
+
lastEvent: new Date(last).toISOString(),
|
|
98
|
+
isActive,
|
|
99
|
+
calls: events.length,
|
|
100
|
+
tokens: { ...tokens, total },
|
|
101
|
+
costUsd,
|
|
102
|
+
costProvenance: (0, costProvenance_1.classifyCostProvenance)({ reportedCalls, estimatedCalls, unpricedCalls }),
|
|
103
|
+
unpricedCalls,
|
|
104
|
+
models,
|
|
105
|
+
elapsedMs,
|
|
106
|
+
remainingMs,
|
|
107
|
+
burnRatePerMinute,
|
|
108
|
+
projectedTokens,
|
|
109
|
+
projectedCostUsd,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Group usage events into billing blocks, oldest first.
|
|
114
|
+
*
|
|
115
|
+
* Events with an unparseable timestamp are ignored. Events are sorted before
|
|
116
|
+
* grouping, so callers may pass them in any order.
|
|
117
|
+
*/
|
|
118
|
+
function computeBillingBlocks(events, options = {}) {
|
|
119
|
+
const durationMs = options.blockDurationMs ?? exports.BILLING_BLOCK_DURATION_MS;
|
|
120
|
+
const alignToHour = options.alignToHour ?? true;
|
|
121
|
+
const nowMs = options.now === undefined ? Date.now() : toMs(options.now);
|
|
122
|
+
const sorted = events
|
|
123
|
+
.map(normalizeInput)
|
|
124
|
+
.filter((event) => event !== null)
|
|
125
|
+
.sort((a, b) => a.timestamp - b.timestamp);
|
|
126
|
+
const blocks = [];
|
|
127
|
+
let current = [];
|
|
128
|
+
let currentStart = 0;
|
|
129
|
+
for (const event of sorted) {
|
|
130
|
+
const previous = current[current.length - 1];
|
|
131
|
+
const opensNewBlock = !previous ||
|
|
132
|
+
event.timestamp >= currentStart + durationMs ||
|
|
133
|
+
event.timestamp - previous.timestamp > durationMs;
|
|
134
|
+
if (opensNewBlock) {
|
|
135
|
+
if (current.length > 0)
|
|
136
|
+
blocks.push(buildBlock(current, currentStart, durationMs, nowMs));
|
|
137
|
+
current = [];
|
|
138
|
+
currentStart = alignToHour ? floorToUtcHour(event.timestamp) : event.timestamp;
|
|
139
|
+
}
|
|
140
|
+
current.push(event);
|
|
141
|
+
}
|
|
142
|
+
if (current.length > 0)
|
|
143
|
+
blocks.push(buildBlock(current, currentStart, durationMs, nowMs));
|
|
144
|
+
return blocks;
|
|
145
|
+
}
|
|
146
|
+
/** The block that is still open at the calculator's `now`, if any. */
|
|
147
|
+
function findActiveBillingBlock(blocks) {
|
|
148
|
+
for (let index = blocks.length - 1; index >= 0; index -= 1) {
|
|
149
|
+
if (blocks[index].isActive)
|
|
150
|
+
return blocks[index];
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import finished sessions from every provider into the history store.
|
|
3
|
+
*
|
|
4
|
+
* The VS Code extension used to import Claude Code only, by hand-parsing
|
|
5
|
+
* JSONL files; the CLI could not import at all. This walks the sessions
|
|
6
|
+
* every provider knows about, reads each once through the unified stats
|
|
7
|
+
* path, and hands a `SessionSummary` to the caller, which applies it to the
|
|
8
|
+
* store it owns (the extension's debounced persistence service or the CLI's
|
|
9
|
+
* locked atomic writer). Dedupe is the caller's `isImported` answer
|
|
10
|
+
* (imported file paths plus persisted session ids); the capped per-session
|
|
11
|
+
* list is deliberately not used as the dedupe key.
|
|
12
|
+
*
|
|
13
|
+
* Node-only: reads session files.
|
|
14
|
+
*
|
|
15
|
+
* @module usage/importSessionHistory
|
|
16
|
+
*/
|
|
17
|
+
import type { SessionProviderBase, SessionProviderDiagnostic } from '../providers/types';
|
|
18
|
+
import type { SessionSummary } from '../types/historicalData';
|
|
19
|
+
/** Files modified more recently than this are skipped: they may still be live. */
|
|
20
|
+
export declare const ACTIVE_SESSION_MTIME_THRESHOLD_MS = 60000;
|
|
21
|
+
export interface ImportSessionHistoryOptions {
|
|
22
|
+
providers: SessionProviderBase[];
|
|
23
|
+
/** Only sessions modified after this instant. */
|
|
24
|
+
since?: Date | string;
|
|
25
|
+
/** Restrict to one workspace's sessions. */
|
|
26
|
+
workspacePath?: string;
|
|
27
|
+
/** Clock used for the active-file check (default `new Date()`). */
|
|
28
|
+
now?: Date;
|
|
29
|
+
/** Override of {@link ACTIVE_SESSION_MTIME_THRESHOLD_MS}. */
|
|
30
|
+
activeFileThresholdMs?: number;
|
|
31
|
+
/** Already imported? Checked before the session is read. */
|
|
32
|
+
isImported: (sessionId: string, filePath: string) => boolean;
|
|
33
|
+
/** Apply one credited session to the store. */
|
|
34
|
+
applySummary: (summary: SessionSummary, filePath: string) => void;
|
|
35
|
+
/** Record a file as imported (called after `applySummary`, and alone for usage-free files). */
|
|
36
|
+
markImported: (filePath: string) => void;
|
|
37
|
+
onProgress?: (loaded: number, total: number) => void;
|
|
38
|
+
}
|
|
39
|
+
export interface ImportSessionHistoryResult {
|
|
40
|
+
/** Session files the providers listed in the window. */
|
|
41
|
+
filesFound: number;
|
|
42
|
+
/** Files read and applied. */
|
|
43
|
+
filesProcessed: number;
|
|
44
|
+
/** Files skipped because they were already imported or still live. */
|
|
45
|
+
filesSkipped: number;
|
|
46
|
+
/** Files that could not be read (`availability: 'unavailable'`) or held no usage. */
|
|
47
|
+
filesUnavailable: number;
|
|
48
|
+
sessionsImported: number;
|
|
49
|
+
/** Messages across the imported sessions. */
|
|
50
|
+
messagesImported: number;
|
|
51
|
+
diagnostics: SessionProviderDiagnostic[];
|
|
52
|
+
}
|
|
53
|
+
export declare function importSessionHistory(options: ImportSessionHistoryOptions): Promise<ImportSessionHistoryResult>;
|