sidekick-shared 0.28.3 → 0.29.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 +64 -0
- package/README.md +149 -19
- package/dist/authStatus.d.ts +54 -0
- package/dist/authStatus.js +91 -0
- package/dist/browser.d.ts +12 -6
- package/dist/browser.js +17 -1
- package/dist/codex.d.ts +23 -0
- package/dist/codex.js +44 -0
- package/dist/codexProfiles.d.ts +1 -0
- package/dist/codexProfiles.js +8 -11
- package/dist/esm/authStatus.js +87 -0
- package/dist/esm/browser.js +4 -2
- package/dist/esm/codex.js +18 -0
- package/dist/esm/codexProfiles.js +8 -11
- package/dist/esm/formatting.js +10 -1
- package/dist/esm/index.js +9 -6
- package/dist/esm/modelCatalog.js +2 -1
- package/dist/esm/modelInfo.js +422 -45
- package/dist/esm/node.js +9 -3
- package/dist/esm/parsers/codexParser.js +175 -54
- package/dist/esm/parsers/codexRolloutOrigin.js +107 -0
- package/dist/esm/parsers/openCodeParser.js +83 -21
- package/dist/esm/pricing.js +18 -0
- package/dist/esm/pricingCatalog.js +14 -7
- package/dist/esm/promptHistory.js +66 -4
- package/dist/esm/providerFailure.js +124 -3
- package/dist/esm/providers/codex.js +2 -24
- package/dist/esm/providers/codexDatabase.js +3 -19
- package/dist/esm/providers/codexRollouts.js +24 -1
- package/dist/esm/providers/openCode.js +9 -17
- package/dist/esm/report/htmlReportGenerator.js +42 -5
- package/dist/esm/report/transcriptParser.js +13 -11
- package/dist/esm/schemas/providerFailure.js +3 -1
- package/dist/esm/schemas/transcript.js +4 -0
- package/dist/esm/sessionPromptHistory.js +2 -2
- package/dist/esm/statusline/payload.js +9 -0
- package/dist/esm/testing/credentialGuard.js +87 -5
- package/dist/esm/toolCallOrigins.js +310 -0
- package/dist/esm/transcript.js +42 -3
- package/dist/esm/usage/usageEvents.js +146 -17
- package/dist/esm/usage/usageReports.js +39 -6
- package/dist/esm/watchers/jsonlTail.js +378 -59
- package/dist/formatting.d.ts +26 -1
- package/dist/formatting.js +11 -1
- package/dist/index.d.ts +16 -10
- package/dist/index.js +35 -12
- package/dist/modelCatalog.js +2 -1
- package/dist/modelInfo.d.ts +185 -16
- package/dist/modelInfo.js +431 -47
- package/dist/node.d.ts +16 -5
- package/dist/node.js +31 -2
- package/dist/parsers/codexParser.d.ts +55 -0
- package/dist/parsers/codexParser.js +175 -54
- package/dist/parsers/codexRolloutOrigin.d.ts +49 -0
- package/dist/parsers/codexRolloutOrigin.js +111 -0
- package/dist/parsers/openCodeParser.d.ts +38 -0
- package/dist/parsers/openCodeParser.js +86 -21
- package/dist/pricing.d.ts +22 -0
- package/dist/pricing.js +50 -0
- package/dist/pricingCatalog.d.ts +10 -0
- package/dist/pricingCatalog.js +13 -6
- package/dist/promptHistory.d.ts +65 -4
- package/dist/promptHistory.js +68 -5
- package/dist/providerFailure.d.ts +44 -0
- package/dist/providerFailure.js +126 -4
- package/dist/providers/codex.d.ts +3 -22
- package/dist/providers/codex.js +3 -26
- package/dist/providers/codexDatabase.d.ts +3 -2
- package/dist/providers/codexDatabase.js +4 -22
- package/dist/providers/codexRollouts.d.ts +22 -0
- package/dist/providers/codexRollouts.js +24 -0
- package/dist/providers/openCode.js +8 -16
- package/dist/report/htmlReportGenerator.js +42 -5
- package/dist/report/transcriptParser.d.ts +10 -1
- package/dist/report/transcriptParser.js +13 -11
- package/dist/schemas/providerFailure.d.ts +39 -0
- package/dist/schemas/providerFailure.js +2 -0
- package/dist/schemas/transcript.d.ts +2 -0
- package/dist/schemas/transcript.js +4 -0
- package/dist/sessionPromptHistory.d.ts +7 -1
- package/dist/sessionPromptHistory.js +1 -1
- package/dist/statusline/payload.d.ts +10 -0
- package/dist/statusline/payload.js +14 -0
- package/dist/testing/credentialGuard.d.ts +14 -0
- package/dist/testing/credentialGuard.js +87 -5
- package/dist/toolCallOrigins.d.ts +70 -0
- package/dist/toolCallOrigins.js +346 -0
- package/dist/transcript.d.ts +12 -0
- package/dist/transcript.js +42 -3
- package/dist/types/opencode.d.ts +2 -0
- package/dist/usage/usageEvents.d.ts +54 -6
- package/dist/usage/usageEvents.js +146 -17
- package/dist/usage/usageReports.d.ts +35 -4
- package/dist/usage/usageReports.js +39 -6
- package/dist/watchers/jsonlTail.d.ts +101 -4
- package/dist/watchers/jsonlTail.js +380 -60
- package/package.json +41 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a Codex rollout came from, read from its `session_meta` record:
|
|
3
|
+
* a session a person started, a fork of another session, or a subagent.
|
|
4
|
+
*
|
|
5
|
+
* Codex writes `session_meta.source` as a plain string for sessions started
|
|
6
|
+
* from a surface (`cli`, `vscode`, `exec`, …) and as an object for subagents:
|
|
7
|
+
* `{ subagent: { thread_spawn: { parent_thread_id, … } } }` for a spawned
|
|
8
|
+
* agent, `{ subagent: { other: 'guardian' } }` for internal reviewers, or
|
|
9
|
+
* `{ subagent: 'review' }`. The same JSON appears as a string in the state
|
|
10
|
+
* database's `threads.source`. A forked rollout records `forked_from_id`, and
|
|
11
|
+
* embeds its parent's `session_meta` after its own, so only the first counts.
|
|
12
|
+
*
|
|
13
|
+
* Pure: no filesystem access. Browser-safe.
|
|
14
|
+
*
|
|
15
|
+
* @module parsers/codexRolloutOrigin
|
|
16
|
+
*/
|
|
17
|
+
export interface CodexRolloutOrigin {
|
|
18
|
+
/** `session_meta.forked_from_id`: the session this rollout was forked from. */
|
|
19
|
+
forkedFromId?: string;
|
|
20
|
+
/**
|
|
21
|
+
* `session_meta.source` when it is a string (`cli`, `vscode`, `exec`, …),
|
|
22
|
+
* or `'subagent'` when it is a subagent object.
|
|
23
|
+
*/
|
|
24
|
+
source?: string;
|
|
25
|
+
/** The rollout was written by a subagent (`source: { subagent: … }`), not a person. */
|
|
26
|
+
isSubagent: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The parent session: `forkedFromId`, else the spawn's `parent_thread_id`,
|
|
29
|
+
* else (subagents only) the top-level `parent_thread_id`.
|
|
30
|
+
*/
|
|
31
|
+
parentId?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Subagent variant: `thread_spawn`, `review`, `compact`, or the name of an
|
|
34
|
+
* `other` subagent (for example `guardian`).
|
|
35
|
+
*/
|
|
36
|
+
subagentKind?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Parent thread id of a spawned subagent's `source` (object, or JSON text as
|
|
40
|
+
* stored in `threads.source`): `subagent.thread_spawn.parent_thread_id`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function codexSpawnParentFromSource(source: unknown): string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Origin of a Codex rollout from its `session_meta`. Accepts the parsed
|
|
45
|
+
* `session_meta` record, its payload, or the rollout's records in order (the
|
|
46
|
+
* first `session_meta` wins: a fork embeds its parent's after its own).
|
|
47
|
+
* Anything else yields `{ isSubagent: false }`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function codexRolloutOrigin(input: unknown | readonly unknown[]): CodexRolloutOrigin;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Where a Codex rollout came from, read from its `session_meta` record:
|
|
4
|
+
* a session a person started, a fork of another session, or a subagent.
|
|
5
|
+
*
|
|
6
|
+
* Codex writes `session_meta.source` as a plain string for sessions started
|
|
7
|
+
* from a surface (`cli`, `vscode`, `exec`, …) and as an object for subagents:
|
|
8
|
+
* `{ subagent: { thread_spawn: { parent_thread_id, … } } }` for a spawned
|
|
9
|
+
* agent, `{ subagent: { other: 'guardian' } }` for internal reviewers, or
|
|
10
|
+
* `{ subagent: 'review' }`. The same JSON appears as a string in the state
|
|
11
|
+
* database's `threads.source`. A forked rollout records `forked_from_id`, and
|
|
12
|
+
* embeds its parent's `session_meta` after its own, so only the first counts.
|
|
13
|
+
*
|
|
14
|
+
* Pure: no filesystem access. Browser-safe.
|
|
15
|
+
*
|
|
16
|
+
* @module parsers/codexRolloutOrigin
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.codexSpawnParentFromSource = codexSpawnParentFromSource;
|
|
20
|
+
exports.codexRolloutOrigin = codexRolloutOrigin;
|
|
21
|
+
function record(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
23
|
+
? value
|
|
24
|
+
: undefined;
|
|
25
|
+
}
|
|
26
|
+
function nonEmpty(value) {
|
|
27
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
28
|
+
}
|
|
29
|
+
/** A `source` value as an object: rollouts store it parsed, the database as JSON text. */
|
|
30
|
+
function sourceObject(source) {
|
|
31
|
+
if (typeof source !== 'string')
|
|
32
|
+
return record(source);
|
|
33
|
+
if (!source.startsWith('{'))
|
|
34
|
+
return undefined;
|
|
35
|
+
try {
|
|
36
|
+
return record(JSON.parse(source));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parent thread id of a spawned subagent's `source` (object, or JSON text as
|
|
44
|
+
* stored in `threads.source`): `subagent.thread_spawn.parent_thread_id`.
|
|
45
|
+
*/
|
|
46
|
+
function codexSpawnParentFromSource(source) {
|
|
47
|
+
const spawn = record(record(sourceObject(source)?.subagent)?.thread_spawn);
|
|
48
|
+
const parent = spawn?.parent_thread_id;
|
|
49
|
+
return typeof parent === 'string' ? parent : undefined;
|
|
50
|
+
}
|
|
51
|
+
function subagentKindOf(subagent) {
|
|
52
|
+
if (typeof subagent === 'string')
|
|
53
|
+
return nonEmpty(subagent);
|
|
54
|
+
const variant = record(subagent);
|
|
55
|
+
if (!variant)
|
|
56
|
+
return undefined;
|
|
57
|
+
const other = nonEmpty(variant.other);
|
|
58
|
+
if (other)
|
|
59
|
+
return other;
|
|
60
|
+
return Object.keys(variant)[0];
|
|
61
|
+
}
|
|
62
|
+
/** The `session_meta` payload of a record, of a bare payload, or of the first in a list. */
|
|
63
|
+
function metaPayload(input) {
|
|
64
|
+
if (Array.isArray(input)) {
|
|
65
|
+
for (const item of input) {
|
|
66
|
+
const row = record(item);
|
|
67
|
+
if (row?.type === 'session_meta')
|
|
68
|
+
return record(row.payload);
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const row = record(input);
|
|
73
|
+
if (!row)
|
|
74
|
+
return undefined;
|
|
75
|
+
if (row.type === 'session_meta')
|
|
76
|
+
return record(row.payload);
|
|
77
|
+
// A payload on its own, as `readSessionMeta()` returns it.
|
|
78
|
+
if (row.type === undefined && row.payload === undefined)
|
|
79
|
+
return row;
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Origin of a Codex rollout from its `session_meta`. Accepts the parsed
|
|
84
|
+
* `session_meta` record, its payload, or the rollout's records in order (the
|
|
85
|
+
* first `session_meta` wins: a fork embeds its parent's after its own).
|
|
86
|
+
* Anything else yields `{ isSubagent: false }`.
|
|
87
|
+
*/
|
|
88
|
+
function codexRolloutOrigin(input) {
|
|
89
|
+
const payload = metaPayload(input);
|
|
90
|
+
if (!payload)
|
|
91
|
+
return { isSubagent: false };
|
|
92
|
+
const forkedFromId = nonEmpty(payload.forked_from_id);
|
|
93
|
+
const object = sourceObject(payload.source);
|
|
94
|
+
const isSubagent = object !== undefined && 'subagent' in object;
|
|
95
|
+
const source = typeof payload.source === 'string' && object === undefined
|
|
96
|
+
? nonEmpty(payload.source)
|
|
97
|
+
: isSubagent
|
|
98
|
+
? 'subagent'
|
|
99
|
+
: undefined;
|
|
100
|
+
const parentId = forkedFromId ??
|
|
101
|
+
nonEmpty(codexSpawnParentFromSource(object)) ??
|
|
102
|
+
(isSubagent ? nonEmpty(payload.parent_thread_id) : undefined);
|
|
103
|
+
const subagentKind = isSubagent ? subagentKindOf(object?.subagent) : undefined;
|
|
104
|
+
return {
|
|
105
|
+
...(forkedFromId ? { forkedFromId } : {}),
|
|
106
|
+
...(source ? { source } : {}),
|
|
107
|
+
isSubagent,
|
|
108
|
+
...(parentId ? { parentId } : {}),
|
|
109
|
+
...(subagentKind ? { subagentKind } : {}),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { SessionEvent } from '../types/sessionEvent';
|
|
14
14
|
import type { OpenCodeMessage, OpenCodePart, DbMessage, DbPart } from '../types/opencode';
|
|
15
|
+
import { type NormalizedUsage } from '../usageNormalization';
|
|
15
16
|
/**
|
|
16
17
|
* Normalizes an OpenCode tool name to PascalCase.
|
|
17
18
|
*
|
|
@@ -38,6 +39,43 @@ export declare function detectPlanModeFromText(text: string): 'enter' | 'exit' |
|
|
|
38
39
|
* This adds snake_case aliases without removing the originals.
|
|
39
40
|
*/
|
|
40
41
|
export declare function normalizeToolInput(input: Record<string, unknown>): Record<string, unknown>;
|
|
42
|
+
/** OpenCode token counts as stored on an assistant message or step. */
|
|
43
|
+
export interface OpenCodeTokenCounts {
|
|
44
|
+
input?: number;
|
|
45
|
+
output?: number;
|
|
46
|
+
reasoning?: number;
|
|
47
|
+
cacheRead?: number;
|
|
48
|
+
cacheWrite?: number;
|
|
49
|
+
/** Provider-reported total, stored by OpenCode since late 2025. */
|
|
50
|
+
total?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Token counts from a message in either storage shape: database rows are
|
|
54
|
+
* mapped to flat `cacheRead` / `cacheWrite`, while file-storage messages keep
|
|
55
|
+
* OpenCode's nested `cache: { read, write }`.
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
export declare function openCodeTokenCounts(tokens: unknown): OpenCodeTokenCounts;
|
|
59
|
+
/**
|
|
60
|
+
* How a stored OpenCode message splits its tokens. Current OpenCode stores
|
|
61
|
+
* every bucket disjoint for every provider: `input` excludes cache reads and
|
|
62
|
+
* writes (since 2025-11) and `output` excludes `reasoning` (since 2026-04),
|
|
63
|
+
* with `total` equal to their sum. Older messages either counted cached input
|
|
64
|
+
* in `input` (OpenAI-compatible providers) or reasoning in `output`. The
|
|
65
|
+
* stored `total` tells the layouts apart; without it the current layout is
|
|
66
|
+
* assumed.
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export declare function openCodeTokenLayout(tokens: OpenCodeTokenCounts): {
|
|
70
|
+
inputIncludesCache: boolean;
|
|
71
|
+
reasoningInOutput: boolean;
|
|
72
|
+
};
|
|
73
|
+
/** Normalizes one OpenCode message's or step's stored tokens. @internal */
|
|
74
|
+
export declare function normalizeOpenCodeUsage(tokens: OpenCodeTokenCounts, providerID: string | undefined, extra?: {
|
|
75
|
+
source?: string;
|
|
76
|
+
model?: string;
|
|
77
|
+
reportedCostUsd?: number;
|
|
78
|
+
}): NormalizedUsage;
|
|
41
79
|
/**
|
|
42
80
|
* Converts an OpenCode message and its parts into SessionEvent array.
|
|
43
81
|
*
|
|
@@ -15,6 +15,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.normalizeToolName = normalizeToolName;
|
|
16
16
|
exports.detectPlanModeFromText = detectPlanModeFromText;
|
|
17
17
|
exports.normalizeToolInput = normalizeToolInput;
|
|
18
|
+
exports.openCodeTokenCounts = openCodeTokenCounts;
|
|
19
|
+
exports.openCodeTokenLayout = openCodeTokenLayout;
|
|
20
|
+
exports.normalizeOpenCodeUsage = normalizeOpenCodeUsage;
|
|
18
21
|
exports.convertOpenCodeMessage = convertOpenCodeMessage;
|
|
19
22
|
exports.parseDbMessageData = parseDbMessageData;
|
|
20
23
|
exports.parseDbPartData = parseDbPartData;
|
|
@@ -123,6 +126,78 @@ function toISOString(time) {
|
|
|
123
126
|
return time;
|
|
124
127
|
return new Date(time).toISOString();
|
|
125
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Token counts from a message in either storage shape: database rows are
|
|
131
|
+
* mapped to flat `cacheRead` / `cacheWrite`, while file-storage messages keep
|
|
132
|
+
* OpenCode's nested `cache: { read, write }`.
|
|
133
|
+
* @internal
|
|
134
|
+
*/
|
|
135
|
+
function openCodeTokenCounts(tokens) {
|
|
136
|
+
const raw = (tokens ?? {});
|
|
137
|
+
const cache = (raw.cache ?? {});
|
|
138
|
+
const num = (value) => (typeof value === 'number' ? value : undefined);
|
|
139
|
+
return {
|
|
140
|
+
input: num(raw.input),
|
|
141
|
+
output: num(raw.output),
|
|
142
|
+
reasoning: num(raw.reasoning),
|
|
143
|
+
cacheRead: num(raw.cacheRead) ?? num(cache.read),
|
|
144
|
+
cacheWrite: num(raw.cacheWrite) ?? num(cache.write),
|
|
145
|
+
total: num(raw.total),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* How a stored OpenCode message splits its tokens. Current OpenCode stores
|
|
150
|
+
* every bucket disjoint for every provider: `input` excludes cache reads and
|
|
151
|
+
* writes (since 2025-11) and `output` excludes `reasoning` (since 2026-04),
|
|
152
|
+
* with `total` equal to their sum. Older messages either counted cached input
|
|
153
|
+
* in `input` (OpenAI-compatible providers) or reasoning in `output`. The
|
|
154
|
+
* stored `total` tells the layouts apart; without it the current layout is
|
|
155
|
+
* assumed.
|
|
156
|
+
* @internal
|
|
157
|
+
*/
|
|
158
|
+
function openCodeTokenLayout(tokens) {
|
|
159
|
+
const n = (value) => typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
|
|
160
|
+
const input = n(tokens.input);
|
|
161
|
+
const output = n(tokens.output);
|
|
162
|
+
const reasoning = n(tokens.reasoning);
|
|
163
|
+
const cache = n(tokens.cacheRead) + n(tokens.cacheWrite);
|
|
164
|
+
const total = n(tokens.total);
|
|
165
|
+
if (total > 0 && total !== input + cache + output + reasoning) {
|
|
166
|
+
if (reasoning > 0 && total === input + cache + output) {
|
|
167
|
+
return { inputIncludesCache: false, reasoningInOutput: true };
|
|
168
|
+
}
|
|
169
|
+
if (cache > 0 && total === input + output + reasoning) {
|
|
170
|
+
return { inputIncludesCache: true, reasoningInOutput: false };
|
|
171
|
+
}
|
|
172
|
+
if (cache > 0 && reasoning > 0 && total === input + output) {
|
|
173
|
+
return { inputIncludesCache: true, reasoningInOutput: true };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return { inputIncludesCache: false, reasoningInOutput: false };
|
|
177
|
+
}
|
|
178
|
+
/** Normalizes one OpenCode message's or step's stored tokens. @internal */
|
|
179
|
+
function normalizeOpenCodeUsage(tokens, providerID, extra = {}) {
|
|
180
|
+
const layout = openCodeTokenLayout(tokens);
|
|
181
|
+
return (0, usageNormalization_1.normalizeProviderUsage)({
|
|
182
|
+
// `openai` semantics subtract cache reads from input; only an old
|
|
183
|
+
// cache-inclusive message needs that.
|
|
184
|
+
semantics: layout.inputIncludesCache
|
|
185
|
+
? 'openai'
|
|
186
|
+
: providerID === 'anthropic'
|
|
187
|
+
? 'anthropic'
|
|
188
|
+
: 'sidekick',
|
|
189
|
+
provider: providerID ?? 'opencode',
|
|
190
|
+
...(extra.source ? { source: extra.source } : {}),
|
|
191
|
+
...(extra.model ? { model: extra.model } : {}),
|
|
192
|
+
inputTokens: tokens.input,
|
|
193
|
+
outputTokens: tokens.output,
|
|
194
|
+
cacheReadTokens: tokens.cacheRead,
|
|
195
|
+
cacheWriteTokens: tokens.cacheWrite,
|
|
196
|
+
reasoningTokens: tokens.reasoning,
|
|
197
|
+
reasoningIncludedInOutput: layout.reasoningInOutput,
|
|
198
|
+
...(extra.reportedCostUsd !== undefined ? { reportedCostUsd: extra.reportedCostUsd } : {}),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
126
201
|
/**
|
|
127
202
|
* Converts an OpenCode message and its parts into SessionEvent array.
|
|
128
203
|
*
|
|
@@ -328,31 +403,20 @@ function convertAssistantMessage(message, parts) {
|
|
|
328
403
|
: stepFinishCost > 0
|
|
329
404
|
? stepFinishCost
|
|
330
405
|
: undefined;
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
output_tokens: message.tokens.output || 0,
|
|
334
|
-
cache_creation_input_tokens: message.tokens.cacheWrite || 0,
|
|
335
|
-
cache_read_input_tokens: message.tokens.cacheRead || 0,
|
|
336
|
-
reported_cost: reportedCost,
|
|
337
|
-
reasoning_tokens: message.tokens.reasoning || 0,
|
|
338
|
-
};
|
|
339
|
-
const normalizedUsage = (0, usageNormalization_1.normalizeProviderUsage)({
|
|
340
|
-
semantics: message.providerID === 'openai'
|
|
341
|
-
? 'openai'
|
|
342
|
-
: message.providerID === 'anthropic'
|
|
343
|
-
? 'anthropic'
|
|
344
|
-
: 'sidekick',
|
|
345
|
-
provider: message.providerID ?? 'opencode',
|
|
406
|
+
const counts = openCodeTokenCounts(message.tokens);
|
|
407
|
+
const normalizedUsage = normalizeOpenCodeUsage(counts, message.providerID, {
|
|
346
408
|
source: 'opencode-message',
|
|
347
409
|
model: message.modelID,
|
|
348
|
-
inputTokens: message.tokens.input,
|
|
349
|
-
outputTokens: message.tokens.output,
|
|
350
|
-
cacheReadTokens: message.tokens.cacheRead,
|
|
351
|
-
cacheWriteTokens: message.tokens.cacheWrite,
|
|
352
|
-
reasoningTokens: message.tokens.reasoning,
|
|
353
|
-
reasoningIncludedInOutput: message.providerID === 'openai',
|
|
354
410
|
reportedCostUsd: reportedCost,
|
|
355
411
|
});
|
|
412
|
+
const usage = {
|
|
413
|
+
input_tokens: normalizedUsage.uncachedInputTokens,
|
|
414
|
+
output_tokens: counts.output || 0,
|
|
415
|
+
cache_creation_input_tokens: counts.cacheWrite || 0,
|
|
416
|
+
cache_read_input_tokens: counts.cacheRead || 0,
|
|
417
|
+
reported_cost: reportedCost,
|
|
418
|
+
reasoning_tokens: counts.reasoning || 0,
|
|
419
|
+
};
|
|
356
420
|
const timestamp = toISOString(message.time.completed || message.time.created);
|
|
357
421
|
const retryAttempts = parts
|
|
358
422
|
.filter((part) => part.type === 'retry')
|
|
@@ -491,6 +555,7 @@ function parseDbMessageData(row) {
|
|
|
491
555
|
cacheRead: cache?.read || 0,
|
|
492
556
|
cacheWrite: cache?.write || 0,
|
|
493
557
|
reasoning: tokens?.reasoning || 0,
|
|
558
|
+
...(typeof tokens?.total === 'number' ? { total: tokens.total } : {}),
|
|
494
559
|
},
|
|
495
560
|
time: {
|
|
496
561
|
created: time?.created || row.time_created,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sidekick-shared/pricing` — model pricing, model-id identity, usage
|
|
3
|
+
* normalization and cost, cost provenance, cost/token formatting, and text
|
|
4
|
+
* token estimates, as one small pure entry (no filesystem, network, or Zod).
|
|
5
|
+
*
|
|
6
|
+
* For hosts that price usage or format costs without session reading: it
|
|
7
|
+
* loads a handful of modules instead of the full browser surface. LiteLLM
|
|
8
|
+
* catalog hydration stays Node-only in `sidekick-shared/node`
|
|
9
|
+
* (`hydratePricingCatalog`); once hydrated, lookups here see the catalog.
|
|
10
|
+
* Everything here is also exported from the package root and
|
|
11
|
+
* `sidekick-shared/browser`.
|
|
12
|
+
*
|
|
13
|
+
* @module pricing
|
|
14
|
+
*/
|
|
15
|
+
export { MODEL_PROVIDER_IDS, canonicalModelId, isMovingModelAlias, parseModelId, compareModelIds, sortModelIds, shortModelName, getModelDisplayInfo, getModelInfo, getModelPricing, resolveModelPricing, registerModelPricing, clearRegisteredModelPricing, getPricingCatalogInfo, calculateCost, calculateCostWithPricing, calculateCostWithProvenance, mergeCostSources, costSourceFromPricingProvenance, mergeCostComponents, formatCost, } from './modelInfo';
|
|
16
|
+
export type { ModelPricing, ModelPricingSource, ModelPricingMatch, ModelPricingProvenance, ResolvedModelPricing, ResolveModelPricingOptions, RegisterModelPricingOptions, PricingCatalogSource, PricingCatalogInfo, CostComponent, CostTokenUsage, ModelProvider, ParsedModelId, ModelInfo, CostSource, CostProvenanceInput, CostWithProvenance, ModelDisplayInfo, FormatCostOptions, } from './modelInfo';
|
|
17
|
+
export { normalizeProviderUsage, extractNormalizedUsage, calculateNormalizedUsageCost, } from './usageNormalization';
|
|
18
|
+
export type { UsageProvider, UsageSemantics, UsageNormalizationProvenance, AnthropicUsageInput, OpenAIUsageInput, SidekickUsageInput, ProviderUsageInput, NormalizedUsage, PricingProvenance, NormalizedUsageCostInput, NormalizedUsageCost, } from './usageNormalization';
|
|
19
|
+
export { formatTokenCount, formatDurationMs } from './formatting';
|
|
20
|
+
export type { FormatTokenCountOptions, FormatDurationMsOptions } from './formatting';
|
|
21
|
+
export { ASCII_CHARS_PER_TOKEN, estimateTextTokens, estimateSerializedTokens, } from './tokenEstimation';
|
|
22
|
+
export type { TokenEstimationMethod, TokenEstimationConfidence, ExactTokenCounterContext, ExactTokenCounter, TokenEstimationOptions, TokenEstimate, } from './tokenEstimation';
|
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `sidekick-shared/pricing` — model pricing, model-id identity, usage
|
|
4
|
+
* normalization and cost, cost provenance, cost/token formatting, and text
|
|
5
|
+
* token estimates, as one small pure entry (no filesystem, network, or Zod).
|
|
6
|
+
*
|
|
7
|
+
* For hosts that price usage or format costs without session reading: it
|
|
8
|
+
* loads a handful of modules instead of the full browser surface. LiteLLM
|
|
9
|
+
* catalog hydration stays Node-only in `sidekick-shared/node`
|
|
10
|
+
* (`hydratePricingCatalog`); once hydrated, lookups here see the catalog.
|
|
11
|
+
* Everything here is also exported from the package root and
|
|
12
|
+
* `sidekick-shared/browser`.
|
|
13
|
+
*
|
|
14
|
+
* @module pricing
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.estimateSerializedTokens = exports.estimateTextTokens = exports.ASCII_CHARS_PER_TOKEN = exports.formatDurationMs = exports.formatTokenCount = exports.calculateNormalizedUsageCost = exports.extractNormalizedUsage = exports.normalizeProviderUsage = exports.formatCost = exports.mergeCostComponents = exports.costSourceFromPricingProvenance = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getPricingCatalogInfo = exports.clearRegisteredModelPricing = exports.registerModelPricing = exports.resolveModelPricing = exports.getModelPricing = exports.getModelInfo = exports.getModelDisplayInfo = exports.shortModelName = exports.sortModelIds = exports.compareModelIds = exports.parseModelId = exports.isMovingModelAlias = exports.canonicalModelId = exports.MODEL_PROVIDER_IDS = void 0;
|
|
18
|
+
var modelInfo_1 = require("./modelInfo");
|
|
19
|
+
Object.defineProperty(exports, "MODEL_PROVIDER_IDS", { enumerable: true, get: function () { return modelInfo_1.MODEL_PROVIDER_IDS; } });
|
|
20
|
+
Object.defineProperty(exports, "canonicalModelId", { enumerable: true, get: function () { return modelInfo_1.canonicalModelId; } });
|
|
21
|
+
Object.defineProperty(exports, "isMovingModelAlias", { enumerable: true, get: function () { return modelInfo_1.isMovingModelAlias; } });
|
|
22
|
+
Object.defineProperty(exports, "parseModelId", { enumerable: true, get: function () { return modelInfo_1.parseModelId; } });
|
|
23
|
+
Object.defineProperty(exports, "compareModelIds", { enumerable: true, get: function () { return modelInfo_1.compareModelIds; } });
|
|
24
|
+
Object.defineProperty(exports, "sortModelIds", { enumerable: true, get: function () { return modelInfo_1.sortModelIds; } });
|
|
25
|
+
Object.defineProperty(exports, "shortModelName", { enumerable: true, get: function () { return modelInfo_1.shortModelName; } });
|
|
26
|
+
Object.defineProperty(exports, "getModelDisplayInfo", { enumerable: true, get: function () { return modelInfo_1.getModelDisplayInfo; } });
|
|
27
|
+
Object.defineProperty(exports, "getModelInfo", { enumerable: true, get: function () { return modelInfo_1.getModelInfo; } });
|
|
28
|
+
Object.defineProperty(exports, "getModelPricing", { enumerable: true, get: function () { return modelInfo_1.getModelPricing; } });
|
|
29
|
+
Object.defineProperty(exports, "resolveModelPricing", { enumerable: true, get: function () { return modelInfo_1.resolveModelPricing; } });
|
|
30
|
+
Object.defineProperty(exports, "registerModelPricing", { enumerable: true, get: function () { return modelInfo_1.registerModelPricing; } });
|
|
31
|
+
Object.defineProperty(exports, "clearRegisteredModelPricing", { enumerable: true, get: function () { return modelInfo_1.clearRegisteredModelPricing; } });
|
|
32
|
+
Object.defineProperty(exports, "getPricingCatalogInfo", { enumerable: true, get: function () { return modelInfo_1.getPricingCatalogInfo; } });
|
|
33
|
+
Object.defineProperty(exports, "calculateCost", { enumerable: true, get: function () { return modelInfo_1.calculateCost; } });
|
|
34
|
+
Object.defineProperty(exports, "calculateCostWithPricing", { enumerable: true, get: function () { return modelInfo_1.calculateCostWithPricing; } });
|
|
35
|
+
Object.defineProperty(exports, "calculateCostWithProvenance", { enumerable: true, get: function () { return modelInfo_1.calculateCostWithProvenance; } });
|
|
36
|
+
Object.defineProperty(exports, "mergeCostSources", { enumerable: true, get: function () { return modelInfo_1.mergeCostSources; } });
|
|
37
|
+
Object.defineProperty(exports, "costSourceFromPricingProvenance", { enumerable: true, get: function () { return modelInfo_1.costSourceFromPricingProvenance; } });
|
|
38
|
+
Object.defineProperty(exports, "mergeCostComponents", { enumerable: true, get: function () { return modelInfo_1.mergeCostComponents; } });
|
|
39
|
+
Object.defineProperty(exports, "formatCost", { enumerable: true, get: function () { return modelInfo_1.formatCost; } });
|
|
40
|
+
var usageNormalization_1 = require("./usageNormalization");
|
|
41
|
+
Object.defineProperty(exports, "normalizeProviderUsage", { enumerable: true, get: function () { return usageNormalization_1.normalizeProviderUsage; } });
|
|
42
|
+
Object.defineProperty(exports, "extractNormalizedUsage", { enumerable: true, get: function () { return usageNormalization_1.extractNormalizedUsage; } });
|
|
43
|
+
Object.defineProperty(exports, "calculateNormalizedUsageCost", { enumerable: true, get: function () { return usageNormalization_1.calculateNormalizedUsageCost; } });
|
|
44
|
+
var formatting_1 = require("./formatting");
|
|
45
|
+
Object.defineProperty(exports, "formatTokenCount", { enumerable: true, get: function () { return formatting_1.formatTokenCount; } });
|
|
46
|
+
Object.defineProperty(exports, "formatDurationMs", { enumerable: true, get: function () { return formatting_1.formatDurationMs; } });
|
|
47
|
+
var tokenEstimation_1 = require("./tokenEstimation");
|
|
48
|
+
Object.defineProperty(exports, "ASCII_CHARS_PER_TOKEN", { enumerable: true, get: function () { return tokenEstimation_1.ASCII_CHARS_PER_TOKEN; } });
|
|
49
|
+
Object.defineProperty(exports, "estimateTextTokens", { enumerable: true, get: function () { return tokenEstimation_1.estimateTextTokens; } });
|
|
50
|
+
Object.defineProperty(exports, "estimateSerializedTokens", { enumerable: true, get: function () { return tokenEstimation_1.estimateSerializedTokens; } });
|
package/dist/pricingCatalog.d.ts
CHANGED
|
@@ -51,6 +51,12 @@ export interface HydrateResult {
|
|
|
51
51
|
contextWindowEntries: number;
|
|
52
52
|
/** ISO timestamp of the fetch that produced the cache. */
|
|
53
53
|
fetchedAt: string;
|
|
54
|
+
/**
|
|
55
|
+
* `getPricingCatalogInfo().digest` after hydration: a stable fingerprint of
|
|
56
|
+
* every active pricing table, so callers can record which prices produced
|
|
57
|
+
* an estimate. See `getPricingCatalogInfo()` for the source and staleness.
|
|
58
|
+
*/
|
|
59
|
+
digest: string;
|
|
54
60
|
}
|
|
55
61
|
export declare const LITELLM_CATALOG_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
56
62
|
/**
|
|
@@ -61,6 +67,10 @@ export declare const LITELLM_CATALOG_URL = "https://raw.githubusercontent.com/Be
|
|
|
61
67
|
* 2. Otherwise attempt a network fetch; on success, persist + apply.
|
|
62
68
|
* 3. On any failure, load a stale cache if present; else leave overrides empty.
|
|
63
69
|
*
|
|
70
|
+
* `getPricingCatalogInfo().source` afterwards reads `litellm` for a fresh fetch
|
|
71
|
+
* or a cache within `ttlMs`, and `stale-cache` for a cache older than `ttlMs`
|
|
72
|
+
* — whether the refresh failed or `offline` skipped it.
|
|
73
|
+
*
|
|
64
74
|
* Always resolves — never throws. Offline-safe.
|
|
65
75
|
*/
|
|
66
76
|
export declare function hydratePricingCatalog(options?: HydrateOptions): Promise<HydrateResult>;
|
package/dist/pricingCatalog.js
CHANGED
|
@@ -75,6 +75,10 @@ const DEFAULT_TIMEOUT_MS = 3_000;
|
|
|
75
75
|
* 2. Otherwise attempt a network fetch; on success, persist + apply.
|
|
76
76
|
* 3. On any failure, load a stale cache if present; else leave overrides empty.
|
|
77
77
|
*
|
|
78
|
+
* `getPricingCatalogInfo().source` afterwards reads `litellm` for a fresh fetch
|
|
79
|
+
* or a cache within `ttlMs`, and `stale-cache` for a cache older than `ttlMs`
|
|
80
|
+
* — whether the refresh failed or `offline` skipped it.
|
|
81
|
+
*
|
|
78
82
|
* Always resolves — never throws. Offline-safe.
|
|
79
83
|
*/
|
|
80
84
|
async function hydratePricingCatalog(options = {}) {
|
|
@@ -83,8 +87,9 @@ async function hydratePricingCatalog(options = {}) {
|
|
|
83
87
|
const now = Date.now();
|
|
84
88
|
// 1. Fresh cache? (Offline callers take any cache, however old.)
|
|
85
89
|
const cached = await readCache(cachePath);
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
const cacheFresh = cached !== null && now - Date.parse(cached.fetchedAt) < ttlMs;
|
|
91
|
+
if (cached && (offline || cacheFresh)) {
|
|
92
|
+
return applyCacheFile(cached, 'cache', cacheFresh ? 'litellm' : 'stale-cache');
|
|
88
93
|
}
|
|
89
94
|
// 2. Try network.
|
|
90
95
|
if (fetchImpl && !offline) {
|
|
@@ -97,12 +102,12 @@ async function hydratePricingCatalog(options = {}) {
|
|
|
97
102
|
contextWindows: normalizeLiteLlmContextWindows(fetched),
|
|
98
103
|
};
|
|
99
104
|
await writeCache(cachePath, payload, logger);
|
|
100
|
-
return applyCacheFile(payload, 'network');
|
|
105
|
+
return applyCacheFile(payload, 'network', 'litellm');
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
// 3. Fall back to stale cache if present.
|
|
104
109
|
if (cached) {
|
|
105
|
-
return applyCacheFile(cached, 'cache');
|
|
110
|
+
return applyCacheFile(cached, 'cache', 'stale-cache');
|
|
106
111
|
}
|
|
107
112
|
// 4. Nothing we can do — stay on static tables.
|
|
108
113
|
return {
|
|
@@ -110,6 +115,7 @@ async function hydratePricingCatalog(options = {}) {
|
|
|
110
115
|
entries: 0,
|
|
111
116
|
contextWindowEntries: 0,
|
|
112
117
|
fetchedAt: new Date(now).toISOString(),
|
|
118
|
+
digest: (0, modelInfo_1.getPricingCatalogInfo)().digest,
|
|
113
119
|
};
|
|
114
120
|
}
|
|
115
121
|
/**
|
|
@@ -117,9 +123,9 @@ async function hydratePricingCatalog(options = {}) {
|
|
|
117
123
|
* `contextWindows` is absent on caches written before context hydration; those
|
|
118
124
|
* contribute no overrides rather than clearing whatever is already loaded.
|
|
119
125
|
*/
|
|
120
|
-
function applyCacheFile(cache, source) {
|
|
126
|
+
function applyCacheFile(cache, source, basis) {
|
|
121
127
|
const contextWindows = cache.contextWindows ?? {};
|
|
122
|
-
(0, modelInfo_1._setPricingOverrides)(cache.overrides);
|
|
128
|
+
(0, modelInfo_1._setPricingOverrides)(cache.overrides, { source: basis, fetchedAt: cache.fetchedAt });
|
|
123
129
|
if (Object.keys(contextWindows).length > 0) {
|
|
124
130
|
(0, modelContext_1._setCatalogContextWindows)(contextWindows);
|
|
125
131
|
}
|
|
@@ -128,6 +134,7 @@ function applyCacheFile(cache, source) {
|
|
|
128
134
|
entries: Object.keys(cache.overrides).length,
|
|
129
135
|
contextWindowEntries: Object.keys(contextWindows).length,
|
|
130
136
|
fetchedAt: cache.fetchedAt,
|
|
137
|
+
digest: (0, modelInfo_1.getPricingCatalogInfo)().digest,
|
|
131
138
|
};
|
|
132
139
|
}
|
|
133
140
|
/**
|
package/dist/promptHistory.d.ts
CHANGED
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
* record over `maxRecordBytes` is skipped without being buffered and reported
|
|
15
15
|
* in `exclusions`, the only way a record is permanently left out.
|
|
16
16
|
*
|
|
17
|
+
* `includeText: false` returns the same entries, ordinals, timestamps,
|
|
18
|
+
* metadata, exclusions, and cursor without prompt text, for consumers that
|
|
19
|
+
* only count or attribute prompts. Cursors are interchangeable between modes.
|
|
20
|
+
*
|
|
17
21
|
* A prompt can be written before its answer. The cursor then keeps the byte
|
|
18
22
|
* offset of that prompt's line and its answer-tracking state (never text), and
|
|
19
23
|
* the prompt is returned again under the same identity once its first
|
|
@@ -34,8 +38,14 @@ export interface PromptHistoryBounds {
|
|
|
34
38
|
/** Bytes read across all files in this call. Default 256 MiB. */
|
|
35
39
|
maxTotalBytes?: number;
|
|
36
40
|
/**
|
|
37
|
-
* Largest single log record that is parsed. Default 16 MiB
|
|
38
|
-
*
|
|
41
|
+
* Largest single log record that is parsed. Default 16 MiB
|
|
42
|
+
* (`PROMPT_HISTORY_ORDINAL_MAX_RECORD_BYTES`). A longer record is skipped
|
|
43
|
+
* without being buffered and reported in `exclusions`.
|
|
44
|
+
*
|
|
45
|
+
* Ordinals depend on this bound: a prompt inside a skipped record is never
|
|
46
|
+
* seen, so later prompts in that file do not count it. Ordinals are only
|
|
47
|
+
* stable across runs and across consumers (for example usage events'
|
|
48
|
+
* `promptOrdinal`) when every reader uses the default.
|
|
39
49
|
*/
|
|
40
50
|
maxRecordBytes?: number;
|
|
41
51
|
/** Entries returned by one call. Default 10 000. */
|
|
@@ -139,8 +149,10 @@ export interface PromptHistoryEntry {
|
|
|
139
149
|
ordinal: number;
|
|
140
150
|
/** ISO timestamp from the log line; never synthesized. */
|
|
141
151
|
timestamp: string;
|
|
142
|
-
/** Full prompt text, untruncated
|
|
152
|
+
/** Full prompt text, untruncated; `''` when the call passed `includeText: false`. */
|
|
143
153
|
text: string;
|
|
154
|
+
/** Set when `text` was left out by `includeText: false`; the prompt was not empty. */
|
|
155
|
+
textOmitted?: true;
|
|
144
156
|
/** Realpath of the working directory; always inside an allowed root. */
|
|
145
157
|
cwd: string;
|
|
146
158
|
gitBranch?: string;
|
|
@@ -170,7 +182,9 @@ export interface PromptHistorySessionRef {
|
|
|
170
182
|
/**
|
|
171
183
|
* A log record permanently left out. Records over `maxRecordBytes` and lines
|
|
172
184
|
* that are not valid JSON are never parsed, so any prompt inside is lost, and
|
|
173
|
-
* the ordinals of later prompts in that file do not count it.
|
|
185
|
+
* the ordinals of later prompts in that file do not count it. Because of
|
|
186
|
+
* that, ordinals are only stable across runs and consumers that all use the
|
|
187
|
+
* default `maxRecordBytes` (`PROMPT_HISTORY_ORDINAL_MAX_RECORD_BYTES`).
|
|
174
188
|
*/
|
|
175
189
|
export interface PromptHistoryExclusion {
|
|
176
190
|
provider: PromptHistoryProvider;
|
|
@@ -213,6 +227,14 @@ export interface CollectPromptHistoryOptions {
|
|
|
213
227
|
cursor?: PromptHistoryCursor;
|
|
214
228
|
bounds?: PromptHistoryBounds;
|
|
215
229
|
signal?: AbortSignal;
|
|
230
|
+
/**
|
|
231
|
+
* Return prompt text. Default true. With `false`, entries keep their
|
|
232
|
+
* ordinal, timestamp, cwd, branch, and answer metadata but carry `text: ''`
|
|
233
|
+
* and `textOmitted: true`, and no prompt text is kept past classifying its
|
|
234
|
+
* line. Ordinals, timestamps, exclusions, and the cursor are identical in
|
|
235
|
+
* both modes, so a cursor written in one mode resumes in the other.
|
|
236
|
+
*/
|
|
237
|
+
includeText?: boolean;
|
|
216
238
|
}
|
|
217
239
|
export interface PromptHistoryResult {
|
|
218
240
|
/** Ordered by provider, session, then ordinal. */
|
|
@@ -235,6 +257,13 @@ export interface PromptHistoryResult {
|
|
|
235
257
|
/** Realpaths of the roots scanned, after worktree expansion. */
|
|
236
258
|
roots?: string[];
|
|
237
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* The default `maxRecordBytes` (16 MiB). Prompt ordinals are only comparable
|
|
262
|
+
* between readers that skip the same records, so every reader that publishes
|
|
263
|
+
* or joins on ordinals (prompt history, session prompt history, usage events'
|
|
264
|
+
* `promptOrdinal`) uses this bound.
|
|
265
|
+
*/
|
|
266
|
+
export declare const PROMPT_HISTORY_ORDINAL_MAX_RECORD_BYTES: number;
|
|
238
267
|
/** @internal */
|
|
239
268
|
export declare const DEFAULT_BOUNDS: Required<PromptHistoryBounds>;
|
|
240
269
|
/** Test hooks; not part of the public entry points. */
|
|
@@ -257,6 +286,17 @@ export type RecordObserver = (record: Record<string, unknown>, info: {
|
|
|
257
286
|
ordinal: number;
|
|
258
287
|
lineOffset: number;
|
|
259
288
|
}) => void;
|
|
289
|
+
/**
|
|
290
|
+
* Sees every human prompt as it is given its ordinal, before the timestamp and
|
|
291
|
+
* scope checks, so prompts that are not returned are seen too. `timestamp` is
|
|
292
|
+
* undefined when the line has no usable one.
|
|
293
|
+
* @internal
|
|
294
|
+
*/
|
|
295
|
+
export type PromptObserver = (prompt: {
|
|
296
|
+
ordinal: number;
|
|
297
|
+
timestamp: string | undefined;
|
|
298
|
+
lineOffset: number;
|
|
299
|
+
}) => void;
|
|
260
300
|
/** @internal */
|
|
261
301
|
export interface ScanContext {
|
|
262
302
|
roots: string[];
|
|
@@ -269,6 +309,9 @@ export interface ScanContext {
|
|
|
269
309
|
entriesSoFar: () => number;
|
|
270
310
|
exclude: (exclusion: PromptHistoryExclusion) => void;
|
|
271
311
|
observe?: RecordObserver;
|
|
312
|
+
onPrompt?: PromptObserver;
|
|
313
|
+
/** Keep prompt text on entries. Default true. */
|
|
314
|
+
includeText?: boolean;
|
|
272
315
|
}
|
|
273
316
|
/** @internal */
|
|
274
317
|
export interface FileBudget {
|
|
@@ -293,6 +336,24 @@ export interface FileOutcome {
|
|
|
293
336
|
}
|
|
294
337
|
/** Collect human-typed prompts from Claude Code and Codex logs under the given roots. */
|
|
295
338
|
export declare function collectPromptHistory(options: CollectPromptHistoryOptions): Promise<PromptHistoryResult>;
|
|
339
|
+
/** One human prompt of a session file: its ordinal and parsed timestamp. @internal */
|
|
340
|
+
export interface PromptOrdinalMark {
|
|
341
|
+
ordinal: number;
|
|
342
|
+
/** Prompt line timestamp, ms since epoch. */
|
|
343
|
+
timestampMs: number;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Every human prompt of one session file with its ordinal and timestamp, read
|
|
347
|
+
* whole in one pass with the default record bound, so the ordinals are the
|
|
348
|
+
* ones `collectPromptHistory()` returns with default bounds. No scope check
|
|
349
|
+
* and no text: prompts are counted wherever their cwd points. Prompts whose
|
|
350
|
+
* line has no usable timestamp still take an ordinal but are left out. A
|
|
351
|
+
* non-interactive Codex session (subagent, fork, unknown source) has none.
|
|
352
|
+
* In file order. Rejects when the file cannot be opened or changes while its
|
|
353
|
+
* head is read.
|
|
354
|
+
* @internal
|
|
355
|
+
*/
|
|
356
|
+
export declare function readPromptOrdinalMarks(provider: PromptHistoryProvider, sessionId: string, filePath: string): Promise<PromptOrdinalMark[]>;
|
|
296
357
|
/** @internal */
|
|
297
358
|
export declare function emptyStats(): PromptHistoryStats;
|
|
298
359
|
/**
|