@relayburn/sdk 1.10.0 → 2.1.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.
@@ -1,8 +1,36 @@
1
- export interface LedgerOpenOptions { home?: string }
2
- export declare class Ledger { static open(opts?: LedgerOpenOptions): Promise<Ledger> }
1
+ // Type surface for `@relayburn/sdk@2.x`.
2
+ //
3
+ // Mirrors `packages/sdk/index.d.ts` (the TS 1.x SDK) byte-for-byte modulo:
4
+ // - `bigint` is allowed alongside `number` for u64-typed token counts (the
5
+ // napi-rs binding emits `BigInt` for `u64`; the TS shape is widened so
6
+ // existing callers that pass through `number` keep type-checking once
7
+ // bound to the Rust impl).
8
+ // - Async fns return `Promise<T>` — the napi-rs binding uses `async fn`
9
+ // where the Rust SDK does, which is everywhere except the `Ledger.open`
10
+ // constructor.
11
+ //
12
+ // Source-of-truth comment: track `packages/sdk/index.d.ts`. Whenever a verb
13
+ // shape changes in TS, mirror it here AND in the Rust napi-rs binding (#247-a).
14
+
15
+ export interface LedgerOpenOptions { home?: string; contentHome?: string }
16
+ /**
17
+ * Stateful ledger handle. The 1.x TS class only exposes the static
18
+ * `open()` constructor; instances are placeholders today, with `home`
19
+ * exposed for callers that want to confirm which ledger they attached
20
+ * to. Verb methods are a future PR.
21
+ */
22
+ export declare class Ledger {
23
+ readonly home: string;
24
+ static open(opts?: LedgerOpenOptions): Promise<Ledger>;
25
+ }
3
26
 
4
27
  export interface IngestOptions { sessionId?: string; harness?: 'claude-code'|'codex'|'opencode'; ledgerHome?: string }
5
- export declare function ingest(opts?: IngestOptions): Promise<unknown>
28
+ export interface IngestReport {
29
+ scannedSessions: number | bigint;
30
+ ingestedSessions: number | bigint;
31
+ appendedTurns: number | bigint;
32
+ }
33
+ export declare function ingest(opts?: IngestOptions): Promise<IngestReport>
6
34
 
7
35
  export interface SummaryOptions {
8
36
  session?: string;
@@ -14,11 +42,22 @@ export interface SummaryOptions {
14
42
  onLog?: (msg: string) => void;
15
43
  }
16
44
  export declare function summary(opts?: SummaryOptions): Promise<{
17
- totalTokens: number;
45
+ totalTokens: number | bigint;
18
46
  totalCost: number;
19
47
  turnCount: number;
20
- byTool: Array<{ tool: string; tokens: number; cost: number; count: number }>;
21
- byModel: Array<{ model: string; tokens: number; cost: number }>;
48
+ byTool: Array<{ tool: string; tokens: number | bigint; cost: number; count: number }>;
49
+ byModel: Array<{ model: string; tokens: number | bigint; cost: number }>;
50
+ replacementSavings?: {
51
+ calls: number | bigint;
52
+ collapsedCalls: number | bigint;
53
+ estimatedTokensSaved: number | bigint;
54
+ byTool: Array<{
55
+ tool: string;
56
+ calls: number | bigint;
57
+ collapsedCalls: number | bigint;
58
+ estimatedTokensSaved: number | bigint;
59
+ }>;
60
+ };
22
61
  }>
23
62
 
24
63
  export interface SessionCostOptions {
@@ -30,7 +69,7 @@ export interface SessionCostOptions {
30
69
  export interface SessionCostResult {
31
70
  sessionId: string | null;
32
71
  totalUSD: number;
33
- totalTokens: number;
72
+ totalTokens: number | bigint;
34
73
  turnCount: number;
35
74
  models: string[];
36
75
  note?: string;
@@ -42,11 +81,8 @@ export type OverheadFileKind = 'claude-md' | 'agents-md';
42
81
  export type OverheadHarness = 'claude-code' | 'codex' | 'opencode';
43
82
 
44
83
  export interface OverheadOptions {
45
- /** Project path to inspect; defaults to process.cwd(). */
46
84
  project?: string;
47
- /** ISO timestamp or relative range (`24h`, `7d`, `4w`, `2m`); the SDK normalizes both forms before querying. */
48
85
  since?: string;
49
- /** Narrow to a single overhead file kind. */
50
86
  kind?: OverheadFileKind;
51
87
  ledgerHome?: string;
52
88
  onLog?: (msg: string) => void;
@@ -56,7 +92,7 @@ export interface OverheadSection {
56
92
  heading: string;
57
93
  startLine: number;
58
94
  endLine: number;
59
- tokens: number;
95
+ tokens: number | bigint;
60
96
  }
61
97
 
62
98
  export interface OverheadSectionCost {
@@ -80,8 +116,8 @@ export interface OverheadFileSummary {
80
116
  path: string;
81
117
  appliesTo: OverheadHarness[];
82
118
  totalLines: number;
83
- bytes: number;
84
- tokens: number;
119
+ bytes: number | bigint;
120
+ tokens: number | bigint;
85
121
  sections: OverheadSection[];
86
122
  groupingLevel: number;
87
123
  }
@@ -104,9 +140,7 @@ export interface OverheadResult {
104
140
  export declare function overhead(opts?: OverheadOptions): Promise<OverheadResult>
105
141
 
106
142
  export interface OverheadTrimOptions extends OverheadOptions {
107
- /** Recommendations per file. Default 3. */
108
143
  top?: number;
109
- /** Include the unified-diff text per recommendation (requires a file read per recommended file). Default true; pass false to skip. */
110
144
  includeDiff?: boolean;
111
145
  }
112
146
 
@@ -114,11 +148,11 @@ export interface OverheadTrimRecommendation {
114
148
  file: string;
115
149
  kind: OverheadFileKind;
116
150
  appliesTo: OverheadHarness[];
117
- section: { heading: string; startLine: number; endLine: number; tokens: number };
151
+ section: { heading: string; startLine: number; endLine: number; tokens: number | bigint };
118
152
  projectedSavings: {
119
153
  perSessionUsd: number;
120
154
  acrossWindowUsd: number;
121
- tokens: number;
155
+ tokens: number | bigint;
122
156
  tokenShare: number;
123
157
  };
124
158
  diff?: string;
@@ -145,68 +179,47 @@ export type HotspotsGroupBy = 'attribution' | 'bash' | 'bash-verb' | 'file' | 's
145
179
  export interface HotspotsOptions {
146
180
  session?: string;
147
181
  project?: string;
148
- /** ISO timestamp (e.g. `2026-04-01T00:00:00Z`) or relative range (`24h`, `7d`, `4w`, `2m`). */
149
182
  since?: string;
150
- /**
151
- * Narrow the attribution result to a single aggregation axis. When omitted
152
- * (or `'attribution'`), the full attribution shape is returned. Ignored
153
- * when `patterns` is set — patterns always returns the `findings` shape.
154
- */
155
183
  groupBy?: HotspotsGroupBy;
156
- /**
157
- * Pattern kinds to detect. Supported kinds:
158
- * - core (via `detectPatterns`): `retry-loop`, `failure-run`,
159
- * `cancellation-run`, `compaction-loss`, `edit-revert`, `edit-heavy`,
160
- * `skill-recall-dup`, `skill-pruning-protection`, `system-prompt-tax`
161
- * - side-channel: `tool-output-bloat`, `ghost-surface`, `tool-call-pattern`
162
- *
163
- * When omitted or empty, returns the attribution result instead of the
164
- * findings shape.
165
- */
166
184
  patterns?: string[];
167
185
  ledgerHome?: string;
168
- /** Optional logger invoked when the SQLite archive read fails and the SDK falls back to a full ledger walk. */
169
186
  onLog?: (msg: string) => void;
170
187
  }
171
188
 
172
- /** Per-axis aggregation row (file). */
173
189
  export interface HotspotsFileRow {
174
190
  path: string;
175
191
  firstEmitTurnIndex: number;
176
- initialTokens: number;
177
- persistenceTokens: number;
192
+ initialTokens: number | bigint;
193
+ persistenceTokens: number | bigint;
178
194
  ridingTurns: number;
179
195
  totalCost: number;
180
196
  }
181
197
 
182
- /** Per-axis aggregation row (bash, exact command). */
183
198
  export interface HotspotsBashRow {
184
199
  command: string | undefined;
185
200
  argsHash: string;
186
201
  callCount: number;
187
- initialTokens: number;
188
- persistenceTokens: number;
202
+ initialTokens: number | bigint;
203
+ persistenceTokens: number | bigint;
189
204
  totalCost: number;
190
205
  }
191
206
 
192
- /** Per-axis aggregation row (bash, by leading verb). */
193
207
  export interface HotspotsBashVerbRow {
194
208
  verb: string;
195
209
  callCount: number;
196
210
  distinctCommands: number;
197
- initialTokens: number;
198
- persistenceTokens: number;
211
+ initialTokens: number | bigint;
212
+ persistenceTokens: number | bigint;
199
213
  avgPersistenceTurns: number;
200
214
  totalCost: number;
201
215
  topExamples: string[];
202
216
  }
203
217
 
204
- /** Per-axis aggregation row (subagent / Agent / Task). */
205
218
  export interface HotspotsSubagentRow {
206
219
  subagentType: string;
207
220
  callCount: number;
208
- initialTokens: number;
209
- persistenceTokens: number;
221
+ initialTokens: number | bigint;
222
+ persistenceTokens: number | bigint;
210
223
  totalCost: number;
211
224
  }
212
225
 
@@ -221,12 +234,10 @@ export interface HotspotsSessionTotal {
221
234
  export interface HotspotsFidelityBlock {
222
235
  analyzed: number;
223
236
  excluded: number;
224
- /** Aggregate fidelity summary for the matched-window turns (analyzed + excluded). */
225
237
  summary: unknown;
226
238
  refused: boolean;
227
239
  }
228
240
 
229
- /** Full attribution shape — mirrors the CLI's `burn hotspots --json`. */
230
241
  export interface HotspotsAttributionResult {
231
242
  kind: 'attribution';
232
243
  turnsAnalyzed: number;
@@ -240,12 +251,10 @@ export interface HotspotsAttributionResult {
240
251
  bash: HotspotsBashRow[];
241
252
  subagents: HotspotsSubagentRow[];
242
253
  fidelity: HotspotsFidelityBlock;
243
- /** Set when every matched turn lacked the coverage attribution needs. */
244
254
  refused?: boolean;
245
255
  refusalReason?: string;
246
256
  }
247
257
 
248
- /** Narrowed shapes — one aggregation axis only. */
249
258
  export interface HotspotsBashResult { kind: 'bash'; rows: HotspotsBashRow[]; refused?: boolean; refusalReason?: string }
250
259
  export interface HotspotsBashVerbResult { kind: 'bash-verb'; rows: HotspotsBashVerbRow[]; refused?: boolean; refusalReason?: string }
251
260
  export interface HotspotsFileResult { kind: 'file'; rows: HotspotsFileRow[]; refused?: boolean; refusalReason?: string }
@@ -263,7 +272,6 @@ export interface HotspotsFinding {
263
272
  export interface HotspotsFindingsResult {
264
273
  kind: 'findings';
265
274
  findings: HotspotsFinding[];
266
- /** Aggregate fidelity summary for the matched-window turns. */
267
275
  summary: unknown;
268
276
  }
269
277
 
@@ -315,19 +323,14 @@ export interface CompareCellResult {
315
323
  }
316
324
 
317
325
  export interface CompareOptions {
318
- /** Required: ≥2 model names to compare. */
319
326
  models: string[];
320
327
  session?: string;
321
328
  project?: string;
322
- /** ISO timestamp (e.g. `2026-04-01T00:00:00Z`) or relative range (`24h`, `7d`, `4w`, `2m`). */
323
329
  since?: string;
324
330
  workflow?: string;
325
331
  agent?: string;
326
- /** Resolved provider filter (e.g. `['anthropic', 'synthetic']`). */
327
332
  provider?: string[];
328
- /** Insufficient-sample threshold; cells below this get flagged. Default 5. */
329
333
  minSample?: number;
330
- /** Minimum fidelity class to include in the aggregate. Default `'usage-only'`. */
331
334
  minFidelity?: FidelityClass;
332
335
  ledgerHome?: string;
333
336
  onLog?: (msg: string) => void;
@@ -347,12 +350,84 @@ export interface CompareResult {
347
350
  };
348
351
  }
349
352
 
353
+ /** Per-(model, activity) comparison shape. Powers `burn compare`. */
354
+ export declare function compare(opts: CompareOptions): Promise<CompareResult>
355
+
356
+ // ---------------------------------------------------------------------------
357
+ // 2.x extensions — surfaces present in `relayburn-sdk` (the Rust crate)
358
+ // but not in the TS 1.x `packages/sdk/index.d.ts`. Pre-1.0 widening per
359
+ // the SDK shape rule; embedders that pinned to 1.x won't see these names
360
+ // (the missing `onLog` etc. plus the absence of these is the only TS-vs-
361
+ // napi gap the conformance gate measures).
362
+ // ---------------------------------------------------------------------------
363
+
364
+ export interface SearchQueryOptions {
365
+ /** FTS5 query string. Phrase, boolean, and prefix syntax supported. */
366
+ query: string;
367
+ /** Hit cap. Defaults to 25 when omitted. */
368
+ limit?: number | bigint;
369
+ /** Restrict to a single session_id. Omit to search all sessions. */
370
+ sessionId?: string;
371
+ ledgerHome?: string;
372
+ }
373
+
374
+ export interface SearchHit {
375
+ sessionId: string;
376
+ messageId: string;
377
+ source: string;
378
+ /** FTS5 BM25 rank (lower = better match). */
379
+ rank: number;
380
+ /** `<b>…</b>`-highlighted snippet around the matching tokens. */
381
+ snippet: string;
382
+ }
383
+
384
+ export interface SearchResult {
385
+ query: string;
386
+ hits: SearchHit[];
387
+ }
388
+
389
+ /** FTS5-backed message-content search. 2.x extension over the TS surface. */
390
+ export declare function search(opts: SearchQueryOptions): Promise<SearchResult>
391
+
392
+ export interface ExportLedgerOptions { ledgerHome?: string }
393
+ export interface ExportStampsOptions { ledgerHome?: string }
394
+
350
395
  /**
351
- * Per-(model, activity) comparison shape. Powers `burn compare` and the
352
- * future `burn__compare` MCP tool. Reads through the SQLite archive when
353
- * `minFidelity === 'partial'` and no provider filter is set; otherwise
354
- * walks the ledger so the fidelity gate / provider filter can be applied
355
- * per-turn. Falls back transparently to the ledger walk when the archive
356
- * read fails.
396
+ * Stream every event row as a JSONL-shaped JSON object. Each value has
397
+ * the form `{ v: 1, kind: '<kind>', record: <json> }`. 2.x extension.
357
398
  */
358
- export declare function compare(opts: CompareOptions): Promise<CompareResult>
399
+ export declare function exportLedger(opts?: ExportLedgerOptions): Promise<unknown[]>
400
+
401
+ /** Stream every stamp row as a JSONL-shaped JSON object. 2.x extension. */
402
+ export declare function exportStamps(opts?: ExportStampsOptions): Promise<unknown[]>
403
+
404
+ /**
405
+ * Tagged error code surfaced on the thrown JS `Error.code` property.
406
+ * Sync verbs reject with one of these; the async `ingest` verb's
407
+ * rejection currently sets `code: 'GenericFailure'` — see the binding
408
+ * crate's `ingest` doc comment for the napi-rs-2.x rationale.
409
+ *
410
+ * The values are the literal strings written to `e.code`, matching the
411
+ * Rust constants `SDK_ERROR_CODE` / `IO_ERROR_CODE` / `INVALID_ARGUMENT_ERROR_CODE`.
412
+ */
413
+ export declare const BurnErrorCode: {
414
+ readonly Sdk: 'BURN_SDK';
415
+ readonly Io: 'BURN_IO';
416
+ readonly InvalidArgument: 'BURN_INVALID_ARGUMENT';
417
+ };
418
+ export type BurnErrorCodeValue = (typeof BurnErrorCode)[keyof typeof BurnErrorCode];
419
+
420
+ /** Wire-value enum for `OverheadOptions.kind` and `OverheadTrimOptions.kind`. */
421
+ export declare const OverheadFileKind: {
422
+ readonly ClaudeMd: 'claude-md';
423
+ readonly AgentsMd: 'agents-md';
424
+ };
425
+
426
+ /** Wire-value enum for `HotspotsOptions.groupBy`. */
427
+ export declare const HotspotsGroupBy: {
428
+ readonly Attribution: 'attribution';
429
+ readonly Bash: 'bash';
430
+ readonly BashVerb: 'bash-verb';
431
+ readonly File: 'file';
432
+ readonly Subagent: 'subagent';
433
+ };
package/src/index.js ADDED
@@ -0,0 +1,144 @@
1
+ // Thin ESM facade over the napi-rs binding. The verbs here re-export the
2
+ // matching `#[napi]` exports from the platform package resolved by
3
+ // `./binding.cjs`, with two adjustments to match the TS 1.x contract at
4
+ // `packages/sdk/index.d.ts`:
5
+ //
6
+ // 1. The sync `#[napi]` verbs (`summary`, `sessionCost`, `overhead`,
7
+ // `overheadTrim`, `hotspots`, `compare`) are re-exported as `async`
8
+ // functions so callers receive `Promise<T>` (matching the 1.x
9
+ // `Promise<...>` return shape). Awaiting an `async` wrapper around a
10
+ // sync return is free and preserves the typed `e.code` thrown by the
11
+ // binding (`BurnErrorCode.Sdk` / `Io` / `InvalidArgument`).
12
+ // 2. `Ledger.open()` is a JS-side wrapper around the binding's
13
+ // `ledgerOpen()` smoke verb. The 1.x `Ledger` class only exposes a
14
+ // static `open(opts)` that returns a placeholder instance, so we
15
+ // keep the same shape here without adding a stateful `#[napi]` class
16
+ // (which would force `Mutex<LedgerHandle>` plumbing for no benefit).
17
+ //
18
+ // All query / compute logic lives in the Rust SDK (`crates/relayburn-sdk`);
19
+ // the binding crate (`crates/relayburn-sdk-node`) wraps it for napi-rs.
20
+
21
+ import { createRequire } from 'node:module';
22
+
23
+ const require = createRequire(import.meta.url);
24
+ const binding = require('./binding.cjs');
25
+
26
+ // napi-rs serializes Rust `u64` / `i64` as JS `BigInt`, but the TS 1.x
27
+ // `@relayburn/sdk` shape (mirrored in `src/index.d.ts`) emits plain
28
+ // `Number` for the same fields. To keep the conformance gate's
29
+ // `deepStrictEqual` checks honest — and to match the runtime shape that
30
+ // 1.x callers expect (e.g. `result.turnCount === 0`, not `=== 0n`) — we
31
+ // downcast every `BigInt` in a verb's return value to `Number` when it
32
+ // fits in `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]`. Values
33
+ // outside that range are left as `BigInt`: realistic burn ledgers won't
34
+ // hit 2^53 tokens, but if one ever does, leaking a `BigInt` that crashes
35
+ // a `===` check is strictly safer than silently rounding to the nearest
36
+ // 1024. The TS shape declares `number | bigint` everywhere this matters
37
+ // so the type stays sound either way.
38
+ const MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER);
39
+ const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
40
+
41
+ function coerceBigInts(value) {
42
+ if (typeof value === 'bigint') {
43
+ return value >= MIN_SAFE && value <= MAX_SAFE ? Number(value) : value;
44
+ }
45
+ if (Array.isArray(value)) {
46
+ for (let i = 0; i < value.length; i++) {
47
+ value[i] = coerceBigInts(value[i]);
48
+ }
49
+ return value;
50
+ }
51
+ if (value !== null && typeof value === 'object') {
52
+ // Skip class instances we don't own (Date, Map, Set, Buffer, …) —
53
+ // walking their guts would be both wasteful and risky. Plain objects
54
+ // produced by napi-rs serde have a null or Object prototype.
55
+ const proto = Object.getPrototypeOf(value);
56
+ if (proto === null || proto === Object.prototype) {
57
+ for (const key of Object.keys(value)) {
58
+ value[key] = coerceBigInts(value[key]);
59
+ }
60
+ }
61
+ return value;
62
+ }
63
+ return value;
64
+ }
65
+
66
+ /**
67
+ * Stateful ledger handle. Mirrors the TS 1.x `Ledger` class shape from
68
+ * `packages/sdk/index.d.ts`. The 1.x version only exposes the static
69
+ * `open(opts)` constructor — instance methods are reserved for a future
70
+ * PR — so we replicate that surface and stash the resolved home for
71
+ * introspection.
72
+ */
73
+ export class Ledger {
74
+ constructor(home) {
75
+ /** @type {string} */
76
+ this.home = home;
77
+ }
78
+ /**
79
+ * Open and validate a ledger at `opts.home` (or `RELAYBURN_HOME`).
80
+ * Returns a `Promise<Ledger>` to mirror the 1.x async signature even
81
+ * though the underlying `ledgerOpen` binding is synchronous.
82
+ *
83
+ * @param {{ home?: string, contentHome?: string }} [opts]
84
+ * @returns {Promise<Ledger>}
85
+ */
86
+ static async open(opts) {
87
+ const home = binding.ledgerOpen(opts);
88
+ return new Ledger(home);
89
+ }
90
+ }
91
+
92
+ export async function ingest(opts) {
93
+ return coerceBigInts(await binding.ingest(opts));
94
+ }
95
+
96
+ export async function summary(opts) {
97
+ return coerceBigInts(await binding.summary(opts));
98
+ }
99
+
100
+ export async function sessionCost(opts) {
101
+ return coerceBigInts(await binding.sessionCost(opts));
102
+ }
103
+
104
+ export async function overhead(opts) {
105
+ return coerceBigInts(await binding.overhead(opts));
106
+ }
107
+
108
+ export async function overheadTrim(opts) {
109
+ return coerceBigInts(await binding.overheadTrim(opts));
110
+ }
111
+
112
+ export async function hotspots(opts) {
113
+ return coerceBigInts(await binding.hotspots(opts));
114
+ }
115
+
116
+ export async function compare(opts) {
117
+ return coerceBigInts(await binding.compare(opts));
118
+ }
119
+
120
+ // 2.x extensions — exposed by the Rust SDK but not declared in
121
+ // `packages/sdk/index.d.ts` (the 1.x TS surface). Per the SDK shape rule,
122
+ // pre-1.0 widening is allowed; these are surfaced here so embedders can
123
+ // reach the FTS5 search index and the JSONL export iterators without
124
+ // dropping into the binding directly.
125
+ export async function search(opts) {
126
+ return coerceBigInts(await binding.search(opts));
127
+ }
128
+
129
+ export async function exportLedger(opts) {
130
+ return coerceBigInts(await binding.exportLedger(opts));
131
+ }
132
+
133
+ export async function exportStamps(opts) {
134
+ return coerceBigInts(await binding.exportStamps(opts));
135
+ }
136
+
137
+ // Re-exported enums from the Rust binding. These come across as plain
138
+ // string-valued objects (`{ Sdk: 'BURN_SDK', ... }`) and let JS callers
139
+ // branch on `e.code === BurnErrorCode.Sdk` without stringly-typed
140
+ // literals. `OverheadFileKind` and `HotspotsGroupBy` are likewise the
141
+ // canonical wire values for the matching option-struct fields.
142
+ export const BurnErrorCode = binding.BurnErrorCode;
143
+ export const OverheadFileKind = binding.OverheadFileKind;
144
+ export const HotspotsGroupBy = binding.HotspotsGroupBy;