@hue-run/sdk 0.20.1 → 0.20.2

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/CLI.md CHANGED
@@ -42,7 +42,7 @@ project manifest are refused because managers can update ancestor locks; Python
42
42
  The generated `hue.setup.mjs` or `hue_setup.py` always selects `captureContent: false` /
43
43
  `capture_content=False`. For a supported application, setup installs the dependency and adds the
44
44
  managed import and middleware registration to the existing entrypoint; an unreferenced helper is
45
- not a completed integration. TypeScript uses `@hue-run/sdk@0.20.1`, `@opentelemetry/api@1.9.1` and
45
+ not a completed integration. TypeScript uses `@hue-run/sdk@0.20.2`, `@opentelemetry/api@1.9.1` and
46
46
  `@opentelemetry/context-async-hooks@2.11.0`; Python setup uses its separately tested package pin.
47
47
  Content capture requires an ordinary account-managed key and a later explicit application decision.
48
48
 
@@ -115,6 +115,8 @@ hue login # validate keys created in Hue and store them in ./.en
115
115
  hue mcp install --client claude-code # write the Hue MCP configuration for a coding agent
116
116
  hue listen --subscription <id> --forward-to http://localhost:3000/slack/events # deliver events to a local bot
117
117
  hue import langfuse --from 2026-01-01 --dry-run # count a Langfuse project's traces, then import them
118
+ hue traces import prepare ./exports --out ./exports.hue-import --json # convert exports into a bundle
119
+ hue traces import upload ./exports.hue-import --authorization-file ./authorization.json --json # deliver it
118
120
  ```
119
121
 
120
122
  Hosted setup accepts HTTPS origins only. HTTP is accepted solely for `localhost`, `127.0.0.1` and
@@ -379,8 +381,8 @@ and Hue never receives or stores them. The command is part of the `hue` executab
379
381
  # LANGFUSE_PUBLIC_KEY=pk-lf-...
380
382
  # LANGFUSE_SECRET_KEY=sk-lf-...
381
383
  # LANGFUSE_BASE_URL=https://cloud.langfuse.com
382
- npx -y @hue-run/sdk@0.20.1 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
383
- npx -y @hue-run/sdk@0.20.1 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
384
+ npx -y @hue-run/sdk@0.20.2 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
385
+ npx -y @hue-run/sdk@0.20.2 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
384
386
  ```
385
387
 
386
388
  `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` come from the environment or `--env-path`
@@ -491,6 +493,96 @@ once, abandoning exports still in flight for the next run to send. Exit codes: `
491
493
  trace was imported, `1` a trace, span or file was not (or a key was refused), `2` usage error, `130`
492
494
  stopped.
493
495
 
496
+ ## Import a bundle of existing traces
497
+
498
+ `hue traces import` brings historical traces into Hue from exports you already hold: OTLP/JSON
499
+ trace exports (a `{"resourceSpans": […]}` document, a list of them, or one per line), complete
500
+ Langfuse observation exports (the `data` or native `observations` envelope) and text ATIF v1.7 and
501
+ v1.8 exports, embedded subagents included. It needs no project key. `prepare` runs on the machine
502
+ that holds the exports and sends nothing; a Hue MCP connection then authorizes the delivery
503
+ (`prepare_trace_import`); `upload` delivers the bundle with that authorization, and
504
+ `get_trace_import` reports what arrived.
505
+
506
+ ```sh
507
+ npx -y @hue-run/sdk@latest traces import prepare ./exports --out ./exports.hue-import --json
508
+ npx -y @hue-run/sdk@latest traces import upload ./exports.hue-import --authorization-file ./authorization.json --json
509
+ ```
510
+
511
+ **Prepare.** `prepare <path>` reads a file, a directory tree or a ZIP archive (members are inflated
512
+ in memory; encrypted members, unsafe paths and nested archives are listed, not read) and
513
+ recognizes each file by its content, never by its name. Every trace becomes gzip OTLP/JSON batches
514
+ of at most 3 MiB decoded and 900 KiB on the wire, written to `--out` (default `<path>.hue-import`,
515
+ created with mode 0700) beside `manifest.json`, which declares each trace's id, span count, batches
516
+ (by the SHA-256 of each gzip body), large values, history owner and origin. A text value over 1 MiB
517
+ is stored as a value of its own and listed in `hue.blobs`, as the exporter lists the values it
518
+ uploads. OTLP spans are kept as recorded; a trace or span id that is not an OpenTelemetry id (an
519
+ exporter's UUID, for example) is mapped to one derived from it by SHA-256, with the original kept
520
+ as `hue.import.original_trace_id` or `hue.import.original_span_id`. A Langfuse observation becomes
521
+ one span keeping its whole record (`hue.import.source_record`) beside the `langfuse.*` and GenAI
522
+ attributes Hue reads; an id that is no OpenTelemetry id is mapped as `hue import langfuse` maps it,
523
+ so an export and a live import of the same trace agree. An ATIF export becomes one trace: an agent
524
+ span for the run, a model span per agent step with the conversation so far, a tool span per call,
525
+ each embedded subagent as its own agent span under the call that spawned it, and the export's
526
+ `extra.user_id` as the trace's `user.id`. ATIF records instants, not durations, so model and tool
527
+ spans are points in time, and a tool's result is kept as text with no error status of the
528
+ importer's making. The same trace in two files is one trace when the copies agree and a refusal
529
+ when they differ; a trace with more than 100,000 spans, 64 batches or 1,000 values is refused and
530
+ named. Nothing about where a file sat or what it was called reaches a span: a renamed, reordered
531
+ or re-zipped copy of the bundle prepares to byte-identical batches and the same manifest digest.
532
+
533
+ The report (`--json` prints it as one object) names `manifest_digest`, `totals` (traces, batches,
534
+ spans, blobs, bytes) and `source`, which `prepare_trace_import` takes as given; the formats
535
+ recognized and the traces each file held; the files it could not read, with the reason; the traces
536
+ and spans it refused; the traces without a history owner (a root `user.id`, a Langfuse `userId`, an
537
+ ATIF `extra.user_id`, or an explicit `hue.world.entity.namespace` and `hue.world.entity.id`), which
538
+ import like any other trace but leave a gap in continuous history; the origin each source recorded
539
+ (`production`, `evaluation`, `simulation` or `unknown`; evaluation and simulation traffic is
540
+ imported and kept out of production history); and the traces whose vendor ids were mapped. An
541
+ existing `--out` directory is accepted only while it holds nothing but an earlier preparation and
542
+ no upload has started in it.
543
+
544
+ **Authorize.** Call `prepare_trace_import` over the Hue MCP with the report's `manifest_digest`,
545
+ `totals` and `source`. It answers the import id, `upload_url` and an `authorization` (`hue_it_` and
546
+ 43 characters) valid for an hour. Save its answer, or the authorization alone, in a private file
547
+ (mode 0600; never in a transcript, a message or a shell history). The same digest in the same
548
+ project is the same import: calling again renews the authorization for an interrupted upload and
549
+ revokes the earlier one.
550
+
551
+ **Upload.** `upload <directory> --authorization-file <file>` reads the authorization from the file
552
+ (its JSON answer, or the authorization on the first line and the `upload_url` on the second;
553
+ `--import-url` names the URL otherwise, and `--origin` refuses a URL on any other Hue origin),
554
+ never from the command line or the environment, and never prints it. It registers the manifest's
555
+ exact bytes, then, trace by trace in declared order and two requests at a time, reserves each large
556
+ value, sends it to the URL Hue answers and completes it, posts each batch with its SHA-256 in
557
+ `Hue-Import-Batch`, and reads the trace's receipt for every span id: a trace counts as verified only
558
+ when Hue confirms every span. It then reconciles and completes the import. Progress is written to
559
+ `progress.json` in the bundle as it goes; running the same command again resumes there, and what
560
+ Hue already holds is not sent twice (a batch sent again after a lost acknowledgement is taken as
561
+ duplicates). Connection failures, timeouts, `408`, `429` and `5xx` answers are retried after 1, 2,
562
+ 4, 8, 16 and 30 seconds, waiting for `Retry-After`; after eight retries the command stops and the
563
+ next run resumes. Ctrl+C or `SIGTERM` finishes the requests in flight, saves progress and exits
564
+ `130`; a second signal stops at once.
565
+
566
+ **What Hue refuses.** Every refusal carries a fixed code, which the report names in `error.code`.
567
+ An expired or replaced authorization (`401`), a lapsed connection (`403` `authority_lapsed`) and an
568
+ expired import (`410`) stop the upload: call `prepare_trace_import` again with the same
569
+ `manifest_digest` and `totals`, write the new authorization to the file and run the same command.
570
+ A manifest whose digest or totals are not the import's (`409`), an authorization of another import
571
+ (`403`), an unknown import (`404`) and a canceled import (`410`) are final. A batch or value Hue
572
+ refuses for the trace's own sake (undeclared, another digest, another trace's records, fewer
573
+ finished spans than declared, a size or type the manifest did not declare), or a record Hue already
574
+ holds under the same id with other content, is a conflict: it is recorded, the trace is reported,
575
+ none of its later records are sent, and the same bytes are never sent again. An import Hue reports
576
+ already completed is done: the command exits `0` and points to `get_trace_import`.
577
+
578
+ **Output and exit codes.** Without `--json`, the command prints the registration, every hundredth
579
+ verified trace, each conflict and a summary. With `--json`, one progress object per line goes to
580
+ stderr and the report to stdout: the state (`completed`, `incomplete`, `stopped` or `failed`), the
581
+ traces verified and conflicted, the batches landed and values stored, Hue's own note and the traces
582
+ it still lists when completion was refused, the conflicts with their codes, and `next_action`. Exit
583
+ codes: `0` prepared or completed, `1` not completed (the report says what to do next), `2` usage
584
+ error, `130` stopped.
585
+
494
586
  ## Local state and conflicts
495
587
 
496
588
  Before its first network write, setup creates a lowercase UUIDv4 and a 32-byte random installation
@@ -595,7 +687,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
595
687
  # Set project to an existing supported fixture; use the same directory on resume.
596
688
  project=/absolute/path/to/supported-fixture
597
689
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
598
- --archive .artifacts/typescript/hue-run-sdk-0.20.1.tgz \
690
+ --archive .artifacts/typescript/hue-run-sdk-0.20.2.tgz \
599
691
  --origin https://STAGING_ORIGIN \
600
692
  --project "$project" --command setup \
601
693
  --evidence .context/setup-staging-before-claim.json
@@ -606,7 +698,7 @@ the private local handoff and finish the real browser claim, then reconcile the
606
698
 
607
699
  ```sh
608
700
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
609
- --archive .artifacts/typescript/hue-run-sdk-0.20.1.tgz \
701
+ --archive .artifacts/typescript/hue-run-sdk-0.20.2.tgz \
610
702
  --origin https://STAGING_ORIGIN \
611
703
  --project "$project" --command claim \
612
704
  --evidence .context/setup-staging-after-claim.json
@@ -648,10 +740,10 @@ otherwise. Never hand the agent Hue-native tools (`context.tools`, `context.mcp`
648
740
  production agent lacks. Pin the SDK, so the agent's repository gains no dependency:
649
741
 
650
742
  ```sh
651
- npx -y @hue-run/sdk@0.20.1 eval --case "Refund an eligible charge" --command "node agent.js"
652
- npx -y @hue-run/sdk@0.20.1 eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <run id>
653
- npx -y @hue-run/sdk@0.20.1 eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
654
- npx -y @hue-run/sdk@0.20.1 eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision>
743
+ npx -y @hue-run/sdk@0.20.2 eval --case "Refund an eligible charge" --command "node agent.js"
744
+ npx -y @hue-run/sdk@0.20.2 eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <run id>
745
+ npx -y @hue-run/sdk@0.20.2 eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
746
+ npx -y @hue-run/sdk@0.20.2 eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision>
655
747
  ```
656
748
 
657
749
  One invocation creates one run holding every selected case; it prints the run URL
@@ -689,7 +781,7 @@ export default function runMyAgent(inputs: JsonValue, context: SimulationTargetC
689
781
  ```
690
782
 
691
783
  ```sh
692
- npx -y @hue-run/sdk@0.20.1 eval --case "Refund an eligible charge" ./hue-agent.ts --content
784
+ npx -y @hue-run/sdk@0.20.2 eval --case "Refund an eligible charge" ./hue-agent.ts --content
693
785
  ```
694
786
 
695
787
  `--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
package/ENVIRONMENTS.md CHANGED
@@ -332,7 +332,7 @@ A refusal such as 409 `simulation_gateway_required` exposes its validated server
332
332
 
333
333
  `agentEnvironment` removes Hue control-plane credentials from the child by default: `HUE_API_KEY`,
334
334
  `HUE_MCP_KEY`, and any value carrying a Hue control-plane credential (`hue_sk_`, `hue_setup_`,
335
- `hue_attempt_`, `hue_mcp_`, `hue_inv_`, `hue_install_`, `hue_at_`, `hue_rt_`, `hue_oauth_`)
335
+ `hue_attempt_`, `hue_mcp_`, `hue_inv_`, `hue_install_`, `hue_at_`, `hue_rt_`, `hue_it_`, `hue_oauth_`)
336
336
  anywhere in it, including wrapped, percent-encoded or JSON-Unicode-escaped values. World tokens and
337
337
  `hue_sim_` capabilities pass. Pass `includeHueCredentials: true` only for an agent that must call
338
338
  Hue's own API.
package/README.md CHANGED
@@ -536,8 +536,8 @@ the Langfuse project's own keys (`LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, a
536
536
  `HUE_API_KEY`:
537
537
 
538
538
  ```sh
539
- npx -y @hue-run/sdk@0.20.1 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
540
- npx -y @hue-run/sdk@0.20.1 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
539
+ npx -y @hue-run/sdk@0.20.2 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
540
+ npx -y @hue-run/sdk@0.20.2 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
541
541
  ```
542
542
 
543
543
  - It reads Langfuse Cloud and self-hosted v4 through the Observations API v2, and self-hosted v3
@@ -558,6 +558,34 @@ The command has no Python counterpart; it runs with `npx` for an application in
558
558
  [Import traces from Langfuse](CLI.md#import-traces-from-langfuse) for every option, the attribute
559
559
  mapping, media handling, limits and exit codes.
560
560
 
561
+ ## Import a bundle of existing traces
562
+
563
+ `hue traces import` brings exports you already hold into Hue in two steps, with no project key.
564
+ `prepare` converts the bundle on your machine and sends nothing; a Hue MCP connection authorizes
565
+ the delivery with `prepare_trace_import`; `upload` delivers it with that authorization. Sources
566
+ are files, directories or ZIP archives of OTLP/JSON trace exports, complete Langfuse observation
567
+ exports and text ATIF v1.7 and v1.8 exports, recognized by content:
568
+
569
+ ```sh
570
+ npx -y @hue-run/sdk@latest traces import prepare ./exports --out ./exports.hue-import --json
571
+ # Call prepare_trace_import with the report's manifest_digest, totals and source, and save its
572
+ # answer (or the authorization alone) in a private file.
573
+ npx -y @hue-run/sdk@latest traces import upload ./exports.hue-import --authorization-file ./authorization.json --json
574
+ ```
575
+
576
+ - `prepare` writes each trace as gzip OTLP/JSON batches beside a manifest and reports the
577
+ manifest's digest and totals, the formats recognized, the files it could not read, invalid vendor
578
+ ids it mapped to OpenTelemetry ids, and the traces without a history owner (a root `user.id` or an
579
+ explicit entity). Nothing about a file's name or place reaches a span.
580
+ - `upload` registers the manifest, sends each trace's large values and batches in order, two
581
+ requests at a time, checks every span's receipt, reconciles and completes the import. Interrupted,
582
+ run it again: it resumes from `progress.json` in the bundle. An expired authorization is renewed
583
+ by calling `prepare_trace_import` again with the same digest and totals.
584
+ - The authorization (`hue_it_…`) is read only from the file and never printed.
585
+
586
+ See [Import a bundle of existing traces](CLI.md#import-a-bundle-of-existing-traces) for the
587
+ formats, the report, what Hue refuses and the exit codes.
588
+
561
589
  ## Vercel AI SDK 6
562
590
 
563
591
  AI SDK 6 accepts a per-call tracer through `experimental_telemetry`. Pass
@@ -0,0 +1,262 @@
1
+ /**
2
+ * The conversions behind `hue traces import prepare`: a source file is recognized by its content
3
+ * (an OTLP/JSON trace export, a complete Langfuse observation export or a text ATIF export),
4
+ * each trace becomes OTLP/JSON entries, one span each, that keep the recorded ids, hierarchy,
5
+ * timestamps, content, errors and usage, and the entries are packed into deterministic gzip
6
+ * batches. Nothing here depends on where a file sat or what it was called: a renamed or repacked
7
+ * bundle converts to byte-identical batches, so the manifest's digests name the same import.
8
+ */
9
+ export declare const traceImportFormats: readonly ["otlp_json", "langfuse_observations", "atif"];
10
+ export type TraceImportFormat = (typeof traceImportFormats)[number];
11
+ export declare const traceImportOrigins: readonly ["production", "evaluation", "simulation", "unknown"];
12
+ export type TraceImportOrigin = (typeof traceImportOrigins)[number];
13
+ /** The bounds the import routes hold a manifest to. */
14
+ export declare const traceImportLimits: {
15
+ readonly traces: 10000;
16
+ readonly batchesPerTrace: 64;
17
+ readonly blobsPerTrace: 1000;
18
+ readonly spansPerTrace: 100000;
19
+ readonly manifestBytes: number;
20
+ readonly batchBytes: number;
21
+ readonly blobBytes: 1000000000;
22
+ };
23
+ /** One batch, decoded, and on the wire: well inside the receiver's request bounds. */
24
+ export declare const MAX_BATCH_DECODED_BYTES: number;
25
+ export declare const MAX_BATCH_WIRE_BYTES: number;
26
+ /** The largest value a record keeps inline; a longer text is stored apart from its span. */
27
+ export declare const MAX_INLINE_VALUE_BYTES: number;
28
+ export declare const SOURCE_RECORD = "hue.import.source_record";
29
+ export declare const SOURCE_ENVELOPE = "hue.import.source_envelope";
30
+ export type JsonValue = null | boolean | number | string | JsonValue[] | {
31
+ [key: string]: JsonValue;
32
+ };
33
+ export interface OtlpValue {
34
+ stringValue?: string;
35
+ boolValue?: boolean;
36
+ intValue?: string;
37
+ doubleValue?: number;
38
+ arrayValue?: {
39
+ values: OtlpValue[];
40
+ };
41
+ kvlistValue?: {
42
+ values: OtlpAttribute[];
43
+ };
44
+ bytesValue?: string;
45
+ }
46
+ export interface OtlpAttribute {
47
+ key: string;
48
+ value: OtlpValue;
49
+ }
50
+ export type ImportSpan = {
51
+ traceId: string;
52
+ spanId: string;
53
+ parentSpanId?: string;
54
+ name: string;
55
+ kind: number;
56
+ startTimeUnixNano: string;
57
+ endTimeUnixNano: string;
58
+ attributes: OtlpAttribute[];
59
+ status: {
60
+ code: number;
61
+ message?: string;
62
+ };
63
+ };
64
+ /** One resource group holding one scope group holding one span: the unit the packer serializes.
65
+ * A converted span is an {@link ImportSpan}; an OTLP/JSON span is kept as recorded. */
66
+ export interface ImportEntry {
67
+ [key: string]: unknown;
68
+ scopeSpans: {
69
+ [key: string]: unknown;
70
+ spans: Record<string, unknown>[];
71
+ }[];
72
+ }
73
+ export interface TraceIdentity {
74
+ user?: string;
75
+ entity?: {
76
+ namespace: string;
77
+ id: string;
78
+ };
79
+ session?: string;
80
+ }
81
+ export interface PreparedBlob {
82
+ sha256: string;
83
+ byteSize: number;
84
+ contentType: string;
85
+ bytes: Uint8Array;
86
+ }
87
+ export interface ConvertedTrace {
88
+ traceId: string;
89
+ format: TraceImportFormat;
90
+ entries: ImportEntry[];
91
+ blobs: PreparedBlob[];
92
+ identity?: TraceIdentity;
93
+ origin: TraceImportOrigin;
94
+ /** The SHA-256 of every source file that held part of the trace. */
95
+ sourceDigests: Set<string>;
96
+ remappedTraceId: boolean;
97
+ remappedSpans: number;
98
+ }
99
+ /** A trace, span or file that was not converted, and why. */
100
+ export interface Refusal {
101
+ traceId?: string;
102
+ reason: string;
103
+ }
104
+ export declare class ConversionError extends Error {
105
+ }
106
+ export declare const sha256Hex: (value: string | Uint8Array) => string;
107
+ /**
108
+ * JSON text parsed with every integer too large for a JavaScript number kept as its decimal
109
+ * text, so an OTLP timestamp written as a number keeps its nanoseconds. `unsafeIntegers` says
110
+ * whether any was found; the Langfuse and ATIF readers refuse such a file, as Hue's do.
111
+ */
112
+ export declare function parseJsonText(text: string): {
113
+ value: unknown;
114
+ unsafeIntegers: boolean;
115
+ };
116
+ /** Refuses a source value that JavaScript or OTLP cannot retain exactly; never omits it. */
117
+ export declare function validateSource(value: JsonValue, depth?: number): void;
118
+ /** JSON.parse otherwise loses duplicate members and can round decimal argument values. */
119
+ export declare function validateJsonText(raw: string): void;
120
+ export type DetectedSource = {
121
+ format: "otlp_json";
122
+ envelopes: Record<string, unknown>[];
123
+ } | {
124
+ format: "langfuse_observations";
125
+ observations: Record<string, JsonValue>[];
126
+ envelope: Record<string, JsonValue>;
127
+ unsafeIntegers: boolean;
128
+ } | {
129
+ format: "atif";
130
+ text: string;
131
+ document: unknown;
132
+ unsafeIntegers: boolean;
133
+ } | {
134
+ format: "unsupported";
135
+ reason: string;
136
+ };
137
+ /** What a file holds, read from its content alone; never from its name. */
138
+ export declare function detectFormat(bytes: Uint8Array): DetectedSource;
139
+ export declare function anyValue(value: JsonValue): OtlpValue;
140
+ /** The large values one trace stores apart from its spans, each once by digest. */
141
+ export declare class BlobSink {
142
+ readonly blobs: Map<string, PreparedBlob>;
143
+ /** Stores a text whole and returns what its record keeps: the first 16 KiB and the `hue.blobs`
144
+ * entry; undefined when the text is over the limit of one stored value. */
145
+ place(key: string, text: string): {
146
+ prefix: string;
147
+ entry: string;
148
+ } | undefined;
149
+ /** The values in digest order: the manifest lists a trace's values the same whatever order its
150
+ * sources were read in, so renaming or repacking them keeps the manifest digest. */
151
+ sorted(): PreparedBlob[];
152
+ }
153
+ /** A converted span's attributes: a value over the inline limit is stored apart from the span
154
+ * through the trace's {@link BlobSink} and listed in `hue.blobs`, as the SDK's exporter lists
155
+ * the values it uploads, with the key in `hue.truncated`. */
156
+ export declare class AttributeList {
157
+ private readonly blobs;
158
+ readonly values: OtlpAttribute[];
159
+ private readonly listed;
160
+ private readonly truncated;
161
+ constructor(blobs: BlobSink);
162
+ add(key: string, value: JsonValue): void;
163
+ json(key: string, value: unknown): void;
164
+ finish(): OtlpAttribute[];
165
+ }
166
+ /** Gathers the spans of OTLP/JSON exports by trace, across files: an export writes a trace's
167
+ * spans in the requests that carried them, which need not be one file. */
168
+ export declare class OtlpTraceCollector {
169
+ private readonly traces;
170
+ /** Adds every span of the envelopes; the spans refused, with their reasons. */
171
+ add(envelopes: readonly Record<string, unknown>[], sourceDigest: string): Refusal[];
172
+ private addSpan;
173
+ /** Every trace gathered so far, spans in time order, and the traces refused whole. */
174
+ finish(): {
175
+ traces: ConvertedTrace[];
176
+ refused: Refusal[];
177
+ };
178
+ }
179
+ export declare function unixNano(value: JsonValue | undefined): string;
180
+ /** Gathers Langfuse observations by trace across files and converts each trace as Hue's reader
181
+ * converts an observation export: one span per observation, the record kept whole. */
182
+ export declare class LangfuseTraceCollector {
183
+ private readonly traces;
184
+ add(observations: readonly Record<string, JsonValue>[], envelope: Record<string, JsonValue>, sourceDigest: string): void;
185
+ finish(): {
186
+ traces: ConvertedTrace[];
187
+ refused: Refusal[];
188
+ };
189
+ }
190
+ /** The ATIF reader's bounds: a text export of at most 4 MiB and 2,000 spans. */
191
+ export declare const MAX_ATIF_FILE_BYTES: number;
192
+ export declare const MAX_ATIF_SPANS = 2000;
193
+ /** Retains nanoseconds and requires an explicit timezone, valid calendar date and OTLP uint64. */
194
+ export declare function atifUnixNano(value: string): string;
195
+ /**
196
+ * A text ATIF v1.7 or v1.8 export as one trace: an agent span for the run, a model span per
197
+ * agent step with the conversation so far, a tool span per call under it, and an embedded
198
+ * subagent trajectory as its own agent span under the tool span of the call that spawned it.
199
+ * Identity is run scoped (`session_id` and `trajectory_id`), never a path. The complete original
200
+ * document stays on the root; each model and tool span keeps its own record. ATIF supplies step
201
+ * instants, not call durations: model and tool spans are zero-duration point spans.
202
+ */
203
+ export declare function convertAtifExport(source: Extract<DetectedSource, {
204
+ format: "atif";
205
+ }>, rawBytes: number): ConvertedTrace;
206
+ export interface PreparedBatch {
207
+ sha256: string;
208
+ body: Uint8Array;
209
+ decodedBytes: number;
210
+ spanIds: string[];
211
+ }
212
+ /** `Math.floor(numerator / divisor)` for safe nonnegative integers, without a division: the
213
+ * published package's import-boundary check reads a `/` after a name as a regular expression, so
214
+ * the quotient is estimated from the reciprocal and corrected by whole steps, which makes it
215
+ * exact on every engine. */
216
+ export declare function integerQuotient(numerator: number, divisor: number): number;
217
+ /**
218
+ * The trace's entries packed into gzip OTLP/JSON batches in order, each at most 3 MiB decoded
219
+ * and 900 KiB on the wire. Packing is linear and the output depends on the entries alone, so the
220
+ * same trace packs to the same batches whatever bundle it came from.
221
+ */
222
+ export declare function batchEntries(entries: readonly ImportEntry[]): PreparedBatch[];
223
+ export interface ManifestTrace {
224
+ trace_id: string;
225
+ span_count: number;
226
+ batches: {
227
+ sha256: string;
228
+ bytes: number;
229
+ span_count: number;
230
+ }[];
231
+ blobs: {
232
+ sha256: string;
233
+ byte_size: number;
234
+ content_type: string;
235
+ }[];
236
+ identity?: TraceIdentity;
237
+ origin: TraceImportOrigin;
238
+ source_digest?: string;
239
+ }
240
+ export interface TraceImportManifest {
241
+ version: 1;
242
+ source: {
243
+ formats: TraceImportFormat[];
244
+ helper: string;
245
+ helper_version?: string;
246
+ };
247
+ traces: ManifestTrace[];
248
+ }
249
+ export interface TraceImportTotals {
250
+ traces: number;
251
+ batches: number;
252
+ spans: number;
253
+ blobs: number;
254
+ bytes: number;
255
+ }
256
+ /** The totals a manifest adds up to, as Hue computes them. */
257
+ export declare function manifestTotals(manifest: TraceImportManifest): TraceImportTotals;
258
+ export declare function manifestTraceHasIdentity(trace: Pick<ManifestTrace, "identity">): boolean;
259
+ /** A manifest's problem, or undefined: the shape and cross-checks Hue's registration applies
260
+ * (every trace id once, every batch digest once, each trace's batches adding up to its span
261
+ * count, each trace's values distinct) so a bundle is refused here, before any upload. */
262
+ export declare function validateManifest(value: unknown): string | undefined;