@adecore/agents 0.41.0 → 0.42.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.
Files changed (45) hide show
  1. package/dist/chat/chat-core.d.ts +1 -0
  2. package/dist/chat/chat-core.js +21 -3
  3. package/dist/chat/chat-store.d.ts +7 -1
  4. package/dist/chat/chat-store.js +68 -7
  5. package/dist/chat/errors.d.ts +1 -1
  6. package/dist/chat/payload-store.d.ts +28 -0
  7. package/dist/chat/payload-store.js +350 -0
  8. package/dist/chat/projector.js +9 -1
  9. package/dist/host/handlers.js +1 -0
  10. package/dist/providers/antigravity-models.json +1 -1
  11. package/dist/providers/claude-models.json +1 -1
  12. package/dist/providers/codex-models.json +1 -1
  13. package/dist/providers/copilot-models.json +1 -1
  14. package/dist/providers/grok-models.json +3 -3
  15. package/dist/usage/history-file.d.ts +25 -0
  16. package/dist/usage/history-file.js +72 -0
  17. package/dist/usage/index-file.d.ts +1 -1
  18. package/dist/usage/index-file.js +33 -114
  19. package/dist/usage/rows.d.ts +23 -0
  20. package/dist/usage/rows.js +94 -0
  21. package/dist/usage/scan-state.d.ts +21 -0
  22. package/dist/usage/scan-state.js +53 -0
  23. package/dist/usage/scanner.d.ts +1 -4
  24. package/dist/usage/scanner.js +37 -50
  25. package/dist/usage/usage-store.d.ts +26 -0
  26. package/dist/usage/usage-store.js +197 -0
  27. package/package.json +3 -3
  28. package/src/chat/chat-core.ts +27 -3
  29. package/src/chat/chat-store.ts +83 -8
  30. package/src/chat/errors.ts +2 -0
  31. package/src/chat/payload-store.ts +459 -0
  32. package/src/chat/projector.ts +11 -1
  33. package/src/host/handlers.ts +1 -0
  34. package/src/host/wiring.ts +1 -1
  35. package/src/providers/antigravity-models.json +1 -1
  36. package/src/providers/claude-models.json +1 -1
  37. package/src/providers/codex-models.json +1 -1
  38. package/src/providers/copilot-models.json +1 -1
  39. package/src/providers/grok-models.json +3 -3
  40. package/src/usage/history-file.ts +140 -0
  41. package/src/usage/index-file.ts +35 -143
  42. package/src/usage/rows.ts +136 -0
  43. package/src/usage/scan-state.ts +105 -0
  44. package/src/usage/scanner.ts +39 -54
  45. package/src/usage/usage-store.ts +237 -0
@@ -1,5 +1,5 @@
1
1
  {
2
- "updatedAt": "2026-10-27T12:00:00Z",
2
+ "updatedAt": "2026-10-27T14:38:15.597Z",
3
3
  "defaultModel": "default",
4
4
  "profiles": {
5
5
  "agent": { "options": [], "contextWindowTokens": {} },
@@ -9,7 +9,7 @@
9
9
  { "slug": "default", "name": "Default", "profile": "agent" },
10
10
  { "slug": "grok-4.7", "name": "Grok 4.7", "profile": "agent" },
11
11
  { "slug": "grok-4.7-build-fast", "name": "Grok 4.7 Build Fast", "profile": "agent" },
12
- { "slug": "grok-4.6", "name": "Grok 4.6", "profile": "agent-256k" },
13
- { "slug": "grok-4.5", "name": "Grok 4.5", "profile": "agent-256k" }
12
+ { "slug": "grok-4.6", "name": "Grok 4.6", "profile": "agent-256k", "legacy": true },
13
+ { "slug": "grok-4.5", "name": "Grok 4.5", "profile": "agent-256k", "legacy": true }
14
14
  ]
15
15
  }
@@ -0,0 +1,25 @@
1
+ import { type UsageProvider } from '@adecore/agent-contracts';
2
+ import { type UsageRecord } from './record.ts';
3
+ export interface FileUsage {
4
+ provider: UsageProvider;
5
+ records: UsageRecord[];
6
+ tail: UsageRecord[];
7
+ }
8
+ export interface HistoryEntry {
9
+ path: string;
10
+ op: 'set' | 'add';
11
+ usage: FileUsage;
12
+ }
13
+ export interface HistoryHeader {
14
+ gen: string;
15
+ base: number;
16
+ }
17
+ export declare function encodeHeader(header: HistoryHeader): string;
18
+ export declare function encodeEntry(entry: HistoryEntry): string;
19
+ export declare function applyEntry(usage: Map<string, FileUsage>, entry: HistoryEntry): void;
20
+ export interface DecodedHistory {
21
+ header: HistoryHeader | null;
22
+ usage: Map<string, FileUsage>;
23
+ complete: number;
24
+ }
25
+ export declare function decodeHistory(bytes: Uint8Array): DecodedHistory;
@@ -0,0 +1,72 @@
1
+ import { UsageProviderSchema } from '@adecore/agent-contracts';
2
+ import { foldByKey } from './record.js';
3
+ import { CURRENT_ROW, decodeRows, RowWriter } from './rows.js';
4
+ const HISTORY_VERSION = 1;
5
+ const NEWLINE = 0x0a;
6
+ export function encodeHeader(header) {
7
+ return `${JSON.stringify({ v: HISTORY_VERSION, gen: header.gen, base: header.base })}\n`;
8
+ }
9
+ export function encodeEntry(entry) {
10
+ const writer = new RowWriter();
11
+ const rows = entry.usage.records.map((record) => writer.row(record));
12
+ const tail = entry.usage.tail.map((record) => writer.row(record));
13
+ const { models, sessions, folders, accounts } = writer.tables;
14
+ const line = { p: entry.path, k: entry.usage.provider, op: entry.op, m: models, s: sessions, f: folders, a: accounts, r: rows, t: tail };
15
+ return `${JSON.stringify(line)}\n`;
16
+ }
17
+ function parseLine(text) {
18
+ try {
19
+ return JSON.parse(text);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ function decodeHeader(text) {
26
+ const header = parseLine(text);
27
+ if (header?.v !== HISTORY_VERSION || typeof header.gen !== 'string' || typeof header.base !== 'number') {
28
+ return null;
29
+ }
30
+ return { gen: header.gen, base: header.base };
31
+ }
32
+ function decodeEntry(text) {
33
+ const line = parseLine(text);
34
+ if (typeof line?.p !== 'string' || (line.op !== 'set' && line.op !== 'add') || !UsageProviderSchema.safeParse(line.k).success) {
35
+ return null;
36
+ }
37
+ const provider = line.k;
38
+ const tables = { models: line.m, sessions: line.s, folders: line.f, accounts: line.a };
39
+ const records = decodeRows(line.r, tables, provider, CURRENT_ROW);
40
+ const tail = decodeRows(line.t, tables, provider, CURRENT_ROW);
41
+ if (records === null || tail === null) {
42
+ return null;
43
+ }
44
+ return { path: line.p, op: line.op, usage: { provider, records, tail } };
45
+ }
46
+ /* Folds one line into what every path counted. */
47
+ export function applyEntry(usage, entry) {
48
+ const known = usage.get(entry.path);
49
+ if (entry.op === 'set' || known === undefined || known.provider !== entry.usage.provider) {
50
+ usage.set(entry.path, entry.usage);
51
+ return;
52
+ }
53
+ usage.set(entry.path, { provider: known.provider, records: foldByKey([...known.records, ...entry.usage.records]), tail: entry.usage.tail });
54
+ }
55
+ /*
56
+ * Every line stands alone, so a line that does not read back costs only that line. Only the end can
57
+ * be half-written, since lines are only ever appended.
58
+ */
59
+ export function decodeHistory(bytes) {
60
+ const buffer = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
61
+ const complete = buffer.lastIndexOf(NEWLINE) + 1;
62
+ const lines = buffer.toString('utf8', 0, complete).split('\n');
63
+ const usage = new Map();
64
+ const header = complete === 0 ? null : decodeHeader(lines[0]);
65
+ for (let i = header === null ? 0 : 1; i < lines.length; i++) {
66
+ const entry = lines[i] === '' ? null : decodeEntry(lines[i]);
67
+ if (entry !== null) {
68
+ applyEntry(usage, entry);
69
+ }
70
+ }
71
+ return { header, usage, complete };
72
+ }
@@ -15,4 +15,4 @@ export interface IndexedFile {
15
15
  }
16
16
  export type UsageIndex = Map<string, IndexedFile>;
17
17
  export declare function encodeIndex(index: UsageIndex): string;
18
- export declare function decodeIndex(text: string): UsageIndex;
18
+ export declare function decodeIndex(text: string): UsageIndex | null;
@@ -1,52 +1,15 @@
1
1
  import { UsageProviderSchema } from '@adecore/agent-contracts';
2
- /* Old records survive deleted transcripts; the scanner reparses existing Codex files with its current reader. */
2
+ import { CURRENT_ROW, decodeRows, RowWriter } from './rows.js';
3
+ /*
4
+ * The single index usage was kept in before history and scan state went apart. The store reads it
5
+ * once to migrate it; nothing writes it any more but the tests that need one to migrate.
6
+ */
3
7
  const INDEX_VERSION = 3;
4
8
  /* Before accounts: every record was the default account's, so it reads as that without a scan. */
5
9
  const ACCOUNTLESS_VERSION = 1;
6
- class Interner {
7
- values = [];
8
- at = new Map();
9
- index(value) {
10
- const seen = this.at.get(value);
11
- if (seen !== undefined) {
12
- return seen;
13
- }
14
- const next = this.values.length;
15
- this.values.push(value);
16
- this.at.set(value, next);
17
- return next;
18
- }
19
- }
20
10
  export function encodeIndex(index) {
21
- const models = new Interner();
22
- const sessions = new Interner();
23
- const folders = new Interner();
24
- const accounts = new Interner();
11
+ const writer = new RowWriter();
25
12
  const files = {};
26
- const row = (record) => {
27
- const counted = [
28
- record.timestampMs,
29
- models.index(record.model),
30
- sessions.index(record.sessionId),
31
- folders.index(record.cwd),
32
- record.totals.calls,
33
- record.totals.input,
34
- record.totals.cacheRead,
35
- record.totals.cacheWrite,
36
- record.totals.cacheWrite1h,
37
- record.totals.output,
38
- record.totals.reasoning,
39
- record.dedupeKey ?? 0,
40
- record.account === undefined ? 0 : accounts.index(record.account) + 1
41
- ];
42
- if (record.costUsd !== undefined || record.serviceTier !== undefined) {
43
- counted[13] = record.costUsd;
44
- }
45
- if (record.serviceTier !== undefined) {
46
- counted[14] = record.serviceTier;
47
- }
48
- return counted;
49
- };
50
13
  for (const [path, file] of index) {
51
14
  files[path] = {
52
15
  p: file.provider,
@@ -54,107 +17,63 @@ export function encodeIndex(index) {
54
17
  m: file.mtimeMs,
55
18
  o: file.offset,
56
19
  c: file.codex,
57
- r: file.records.map(row),
58
- t: file.tail.map(row),
20
+ r: file.records.map((record) => writer.row(record)),
21
+ t: file.tail.map((record) => writer.row(record)),
59
22
  ...(file.head === null ? {} : { h: file.head }),
60
23
  ...(file.parserVersion === undefined ? {} : { v: file.parserVersion }),
61
24
  ...(file.compressed === undefined ? {} : { z: file.compressed })
62
25
  };
63
26
  }
64
- const document = {
65
- version: INDEX_VERSION,
66
- models: models.values,
67
- sessions: sessions.values,
68
- folders: folders.values,
69
- accounts: accounts.values,
70
- files
71
- };
27
+ const document = { version: INDEX_VERSION, ...writer.tables, files };
72
28
  return JSON.stringify(document);
73
29
  }
74
- function isRow(row, version) {
75
- return (Array.isArray(row) &&
76
- (version === ACCOUNTLESS_VERSION ? row.length === 12 : row.length >= 13 && row.length <= (version === INDEX_VERSION ? 15 : 14)) &&
77
- row.slice(0, 11).every((value) => typeof value === 'number') &&
78
- (typeof row[11] === 'string' || row[11] === 0) &&
79
- (version === ACCOUNTLESS_VERSION || typeof row[12] === 'number') &&
80
- (row.length < 14 || row[13] === null || row[13] === undefined || typeof row[13] === 'number') &&
81
- (row.length < 15 || typeof row[14] === 'string'));
82
- }
83
30
  /*
84
- * A row that does not read back costs a cold parse of the file it belonged to, never a wrong total,
85
- * so anything unexpected drops that file's entry rather than being repaired.
31
+ * Null for text that is no index of a version this reads. A file whose rows do not read back is left
32
+ * out, so only that file's usage is missing from what migrates, never a wrong total.
86
33
  */
87
34
  export function decodeIndex(text) {
88
- const index = new Map();
89
35
  let document;
90
36
  try {
91
37
  document = JSON.parse(text);
92
38
  }
93
39
  catch {
94
- return index;
40
+ return null;
95
41
  }
96
42
  const version = document?.version;
97
43
  if ((version !== INDEX_VERSION && version !== 2 && version !== ACCOUNTLESS_VERSION) ||
98
44
  !Array.isArray(document.models) ||
99
45
  !Array.isArray(document.sessions) ||
100
46
  !Array.isArray(document.folders)) {
101
- return index;
47
+ return null;
102
48
  }
103
- const accounts = Array.isArray(document.accounts) ? document.accounts : [];
104
- const name = (table, at) => (typeof table[at] === 'string' ? table[at] : null);
49
+ const index = new Map();
50
+ const tables = { ...document, accounts: Array.isArray(document.accounts) ? document.accounts : [] };
51
+ const shape = { accountless: version === ACCOUNTLESS_VERSION, maxLength: version === INDEX_VERSION ? CURRENT_ROW.maxLength : 14 };
105
52
  for (const [path, file] of Object.entries(document.files ?? {})) {
106
53
  // Every provider the schema knows, so a new one is never dropped from the index it was saved in.
107
54
  if (!UsageProviderSchema.safeParse(file.p).success) {
108
55
  continue;
109
56
  }
110
- if (typeof file.s !== 'number' || typeof file.m !== 'number' || typeof file.o !== 'number' || file.o < 0 || !Array.isArray(file.r)) {
57
+ if (typeof file.s !== 'number' || typeof file.m !== 'number' || typeof file.o !== 'number' || file.o < 0) {
111
58
  continue;
112
59
  }
113
- let broken = false;
114
- const decode = (rows) => {
115
- const records = [];
116
- for (const row of rows) {
117
- const valid = isRow(row, version);
118
- const model = valid ? name(document.models, row[1]) : null;
119
- const sessionId = valid ? name(document.sessions, row[2]) : null;
120
- const cwd = valid ? name(document.folders, row[3]) : null;
121
- const accountAt = valid ? (row[12] ?? 0) : 0;
122
- const account = accountAt === 0 ? undefined : name(accounts, accountAt - 1);
123
- if (!valid || model === null || sessionId === null || cwd === null || account === null) {
124
- broken = true;
125
- return records;
126
- }
127
- records.push({
128
- provider: file.p,
129
- timestampMs: row[0],
130
- model,
131
- sessionId,
132
- cwd,
133
- ...(account === undefined ? {} : { account }),
134
- totals: { calls: row[4], input: row[5], cacheRead: row[6], cacheWrite: row[7], cacheWrite1h: row[8], output: row[9], reasoning: row[10] },
135
- ...(typeof row[13] === 'number' ? { costUsd: row[13] } : {}),
136
- ...(typeof row[14] === 'string' ? { serviceTier: row[14] } : {}),
137
- dedupeKey: row[11] === 0 ? null : row[11]
138
- });
139
- }
140
- return records;
141
- };
142
- const records = decode(file.r);
143
- const tail = decode(Array.isArray(file.t) ? file.t : []);
144
- if (!broken) {
145
- index.set(path, {
146
- provider: file.p,
147
- size: file.s,
148
- mtimeMs: file.m,
149
- offset: file.o,
150
- records,
151
- tail,
152
- codex: file.c ?? null,
153
- head: typeof file.h === 'string' ? file.h : null,
154
- ...(typeof file.v === 'number' ? { parserVersion: file.v } : {}),
155
- ...(typeof file.z === 'boolean' ? { compressed: file.z } : {})
156
- });
60
+ const records = decodeRows(file.r, tables, file.p, shape);
61
+ const tail = decodeRows(Array.isArray(file.t) ? file.t : [], tables, file.p, shape);
62
+ if (records === null || tail === null) {
63
+ continue;
157
64
  }
65
+ index.set(path, {
66
+ provider: file.p,
67
+ size: file.s,
68
+ mtimeMs: file.m,
69
+ offset: file.o,
70
+ records,
71
+ tail,
72
+ codex: file.c ?? null,
73
+ head: typeof file.h === 'string' ? file.h : null,
74
+ ...(typeof file.v === 'number' ? { parserVersion: file.v } : {}),
75
+ ...(typeof file.z === 'boolean' ? { compressed: file.z } : {})
76
+ });
158
77
  }
159
78
  return index;
160
79
  }
@@ -0,0 +1,23 @@
1
+ import type { UsageProvider } from '@adecore/agent-contracts';
2
+ import type { UsageRecord } from './record.ts';
3
+ export type RecordRow = [number, number, number, number, number, number, number, number, number, number, number, string | 0, number?, number?, string?];
4
+ export interface RowTables {
5
+ models: string[];
6
+ sessions: string[];
7
+ folders: string[];
8
+ accounts: string[];
9
+ }
10
+ export declare class RowWriter {
11
+ private readonly models;
12
+ private readonly sessions;
13
+ private readonly folders;
14
+ private readonly accounts;
15
+ get tables(): RowTables;
16
+ row(record: UsageRecord): RecordRow;
17
+ }
18
+ export interface RowShape {
19
+ accountless: boolean;
20
+ maxLength: number;
21
+ }
22
+ export declare const CURRENT_ROW: RowShape;
23
+ export declare function decodeRows(rows: unknown, tables: Partial<Record<keyof RowTables, unknown>>, provider: UsageProvider, shape: RowShape): UsageRecord[] | null;
@@ -0,0 +1,94 @@
1
+ class Interner {
2
+ values = [];
3
+ at = new Map();
4
+ index(value) {
5
+ const seen = this.at.get(value);
6
+ if (seen !== undefined) {
7
+ return seen;
8
+ }
9
+ const next = this.values.length;
10
+ this.values.push(value);
11
+ this.at.set(value, next);
12
+ return next;
13
+ }
14
+ }
15
+ /* Turns records into rows, collecting the names they share into one set of tables. */
16
+ export class RowWriter {
17
+ models = new Interner();
18
+ sessions = new Interner();
19
+ folders = new Interner();
20
+ accounts = new Interner();
21
+ get tables() {
22
+ return { models: this.models.values, sessions: this.sessions.values, folders: this.folders.values, accounts: this.accounts.values };
23
+ }
24
+ row(record) {
25
+ const counted = [
26
+ record.timestampMs,
27
+ this.models.index(record.model),
28
+ this.sessions.index(record.sessionId),
29
+ this.folders.index(record.cwd),
30
+ record.totals.calls,
31
+ record.totals.input,
32
+ record.totals.cacheRead,
33
+ record.totals.cacheWrite,
34
+ record.totals.cacheWrite1h,
35
+ record.totals.output,
36
+ record.totals.reasoning,
37
+ record.dedupeKey ?? 0,
38
+ record.account === undefined ? 0 : this.accounts.index(record.account) + 1
39
+ ];
40
+ if (record.costUsd !== undefined || record.serviceTier !== undefined) {
41
+ counted[13] = record.costUsd;
42
+ }
43
+ if (record.serviceTier !== undefined) {
44
+ counted[14] = record.serviceTier;
45
+ }
46
+ return counted;
47
+ }
48
+ }
49
+ export const CURRENT_ROW = { accountless: false, maxLength: 15 };
50
+ function isRow(row, shape) {
51
+ return (Array.isArray(row) &&
52
+ (shape.accountless ? row.length === 12 : row.length >= 13 && row.length <= shape.maxLength) &&
53
+ row.slice(0, 11).every((value) => typeof value === 'number') &&
54
+ (typeof row[11] === 'string' || row[11] === 0) &&
55
+ (shape.accountless || typeof row[12] === 'number') &&
56
+ (row.length < 14 || row[13] === null || row[13] === undefined || typeof row[13] === 'number') &&
57
+ (row.length < 15 || typeof row[14] === 'string'));
58
+ }
59
+ function nameAt(table, at) {
60
+ return Array.isArray(table) && typeof table[at] === 'string' ? table[at] : null;
61
+ }
62
+ /* The records of `rows`, or null when any row does not read back against its tables. */
63
+ export function decodeRows(rows, tables, provider, shape) {
64
+ if (!Array.isArray(rows)) {
65
+ return null;
66
+ }
67
+ const records = [];
68
+ for (const row of rows) {
69
+ if (!isRow(row, shape)) {
70
+ return null;
71
+ }
72
+ const model = nameAt(tables.models, row[1]);
73
+ const sessionId = nameAt(tables.sessions, row[2]);
74
+ const cwd = nameAt(tables.folders, row[3]);
75
+ const accountAt = row[12] ?? 0;
76
+ const account = accountAt === 0 ? undefined : nameAt(tables.accounts, accountAt - 1);
77
+ if (model === null || sessionId === null || cwd === null || account === null) {
78
+ return null;
79
+ }
80
+ records.push({
81
+ provider,
82
+ timestampMs: row[0],
83
+ model,
84
+ sessionId,
85
+ cwd,
86
+ ...(account === undefined ? {} : { account }),
87
+ totals: { calls: row[4], input: row[5], cacheRead: row[6], cacheWrite: row[7], cacheWrite1h: row[8], output: row[9], reasoning: row[10] },
88
+ ...(typeof row[13] === 'number' ? { costUsd: row[13] } : {}),
89
+ ...(typeof row[14] === 'string' ? { serviceTier: row[14] } : {}),
90
+ dedupeKey: row[11] === 0 ? null : row[11]
91
+ });
92
+ }
93
+ return records;
94
+ }
@@ -0,0 +1,21 @@
1
+ import { type UsageProvider } from '@adecore/agent-contracts';
2
+ import type { CodexParserState } from './readers/codex.ts';
3
+ export interface ScanState {
4
+ provider: UsageProvider;
5
+ size: number;
6
+ mtimeMs: number;
7
+ offset: number;
8
+ codex: CodexParserState | null;
9
+ head: string | null;
10
+ parserVersion?: number;
11
+ compressed?: boolean;
12
+ }
13
+ export interface HistoryMark {
14
+ gen: string;
15
+ bytes: number;
16
+ }
17
+ export declare function encodeState(state: ReadonlyMap<string, ScanState>, history: HistoryMark): string;
18
+ export declare function decodeState(text: string): {
19
+ history: HistoryMark;
20
+ files: Map<string, ScanState>;
21
+ } | null;
@@ -0,0 +1,53 @@
1
+ import { UsageProviderSchema } from '@adecore/agent-contracts';
2
+ const STATE_VERSION = 1;
3
+ export function encodeState(state, history) {
4
+ const files = {};
5
+ for (const [path, file] of state) {
6
+ files[path] = {
7
+ p: file.provider,
8
+ s: file.size,
9
+ m: file.mtimeMs,
10
+ o: file.offset,
11
+ c: file.codex,
12
+ ...(file.head === null ? {} : { h: file.head }),
13
+ ...(file.parserVersion === undefined ? {} : { v: file.parserVersion }),
14
+ ...(file.compressed === undefined ? {} : { z: file.compressed })
15
+ };
16
+ }
17
+ const document = { version: STATE_VERSION, history, files };
18
+ return JSON.stringify(document);
19
+ }
20
+ /* Null for text that is no state of this version; an entry that does not read only costs a read of its transcript. */
21
+ export function decodeState(text) {
22
+ let document;
23
+ try {
24
+ document = JSON.parse(text);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ const history = document?.history;
30
+ if (document?.version !== STATE_VERSION || typeof history?.gen !== 'string' || typeof history.bytes !== 'number') {
31
+ return null;
32
+ }
33
+ const files = new Map();
34
+ for (const [path, file] of Object.entries(document.files ?? {})) {
35
+ if (!UsageProviderSchema.safeParse(file.p).success) {
36
+ continue;
37
+ }
38
+ if (typeof file.s !== 'number' || typeof file.m !== 'number' || typeof file.o !== 'number' || file.o < 0) {
39
+ continue;
40
+ }
41
+ files.set(path, {
42
+ provider: file.p,
43
+ size: file.s,
44
+ mtimeMs: file.m,
45
+ offset: file.o,
46
+ codex: file.c ?? null,
47
+ head: typeof file.h === 'string' ? file.h : null,
48
+ ...(typeof file.v === 'number' ? { parserVersion: file.v } : {}),
49
+ ...(typeof file.z === 'boolean' ? { compressed: file.z } : {})
50
+ });
51
+ }
52
+ return { history: { gen: history.gen, bytes: history.bytes }, files };
53
+ }
@@ -20,14 +20,11 @@ export interface UsageScannerOptions {
20
20
  type AccountOf = (record: UsageRecord) => string | undefined;
21
21
  export declare function accountResolver(root: UsageRootPath, sessionAccounts: () => ReadonlyMap<string, string>): AccountOf;
22
22
  export declare class UsageScanner {
23
- private readonly home;
24
23
  private readonly roots;
25
24
  private readonly sessionAccounts;
26
- private index;
25
+ private readonly store;
27
26
  private loaded;
28
- private dirty;
29
27
  constructor(home: string, roots?: UsageRootPath[] | (() => UsageRootPath[]), options?: UsageScannerOptions);
30
- private get file();
31
28
  load(): Promise<void>;
32
29
  scan(): Promise<ScanReport>;
33
30
  records(): UsageRecord[];