@hue-run/sdk 0.14.0 → 0.15.1

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.14.0`, `@opentelemetry/api@1.9.1` and
45
+ not a completed integration. TypeScript uses `@hue-run/sdk@0.15.1`, `@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
 
@@ -469,7 +469,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
469
469
  # Set project to an existing supported fixture; use the same directory on resume.
470
470
  project=/absolute/path/to/supported-fixture
471
471
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
472
- --archive .artifacts/typescript/hue-run-sdk-0.14.0.tgz \
472
+ --archive .artifacts/typescript/hue-run-sdk-0.15.1.tgz \
473
473
  --origin https://STAGING_ORIGIN \
474
474
  --project "$project" --command setup \
475
475
  --evidence .context/setup-staging-before-claim.json
@@ -480,7 +480,7 @@ the private local handoff and finish the real browser claim, then reconcile the
480
480
 
481
481
  ```sh
482
482
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
483
- --archive .artifacts/typescript/hue-run-sdk-0.14.0.tgz \
483
+ --archive .artifacts/typescript/hue-run-sdk-0.15.1.tgz \
484
484
  --origin https://STAGING_ORIGIN \
485
485
  --project "$project" --command claim \
486
486
  --evidence .context/setup-staging-after-claim.json
@@ -523,10 +523,10 @@ production agent lacks. Pin the SDK and bring its `zod` peer along, so the agent
523
523
  gains no dependency:
524
524
 
525
525
  ```sh
526
- npx --yes --package @hue-run/sdk@0.14.0 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" --command "node agent.js" --env-file .env.hue
527
- npx --yes --package @hue-run/sdk@0.14.0 --package "zod@^4.6.5" hue eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <experiment id>
528
- npx --yes --package @hue-run/sdk@0.14.0 --package "zod@^4.6.5" hue eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
529
- npx --yes --package @hue-run/sdk@0.14.0 --package "zod@^4.6.5" hue eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision> --env-file .env.hue
526
+ npx --yes --package @hue-run/sdk@0.15.1 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" --command "node agent.js" --env-file .env.hue
527
+ npx --yes --package @hue-run/sdk@0.15.1 --package "zod@^4.6.5" hue eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <experiment id>
528
+ npx --yes --package @hue-run/sdk@0.15.1 --package "zod@^4.6.5" hue eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
529
+ npx --yes --package @hue-run/sdk@0.15.1 --package "zod@^4.6.5" hue eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision> --env-file .env.hue
530
530
  ```
531
531
 
532
532
  Without the second `--package`, `hue eval` exits with `hue eval needs zod`, even for `--help`;
@@ -562,7 +562,7 @@ export default function runMyAgent(inputs: JsonValue, context: SimulationTargetC
562
562
  ```
563
563
 
564
564
  ```sh
565
- npx --yes --package @hue-run/sdk@0.14.0 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" ./hue-agent.ts --content --env-file .env.hue
565
+ npx --yes --package @hue-run/sdk@0.15.1 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" ./hue-agent.ts --content --env-file .env.hue
566
566
  ```
567
567
 
568
568
  `--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
@@ -581,11 +581,15 @@ Failing cases print the scorer explanation. An evaluator Hue records as not appl
581
581
  that case's columns and neither passes nor fails it, so in a mixed eval set a case is decided by
582
582
  the evaluators that apply to it; the pass line counts the not-applicable results. A case that no
583
583
  pinned evaluator applies to is an error that names what they need. A result of a pinned Hue judge
584
- (an evaluator of kind `world_judge`) that Hue marks advisory is
584
+ (an evaluator of kind `world_judge`) whose immutable version does not set `config.required: true`
585
+ and that Hue marks advisory is
585
586
  shown in its column, marked `(advisory)`, but never decides a case or the exit code; the pass line
586
587
  counts advisory failures as not counted, and a case only advisory results ran for is an error.
587
- The Answer outcome evaluator uses its required judge to decide answer-only cases; if that judge
588
- cannot run, the case is inconclusive. Any
588
+ Applicable outcome evaluators and judge versions pinned with `config.required: true` need a
589
+ decision before a case can pass. A required result skipped without a decision is inconclusive;
590
+ missing results remain pending and evaluator errors remain errors. A recorded failure takes
591
+ precedence over an unavailable required evaluator. The Answer outcome evaluator uses its
592
+ required judge to decide answer-only cases. Any
589
593
  other evaluator's result, an error, or a metric that carries `passed` is never advisory, whatever
590
594
  its evidence says. A metric name that
591
595
  several evaluators report, such as two judges' `verdict`, gets a column per evaluator, labelled
@@ -667,7 +671,7 @@ A Scenario or eval set whose dataset version is not saved cannot back an experim
667
671
  exits 1 and asks for **Save eval-set version** in Hue or `--save-version`, which freezes that
668
672
  version at its current revision. Connection settings are `HUE_API_KEY` and `HUE_BASE_URL`
669
673
  (default `https://app.hue.run`), loaded from `--env-file <path>` (or its alias `--env-path`) first when given; `--origin`
670
- overrides the origin. Starting with TypeScript 0.14.0, telemetry content capture defaults on; `--no-content` opts out.
674
+ overrides the origin. Since TypeScript 0.14.0, telemetry content capture defaults on; `--no-content` opts out.
671
675
  Earlier releases require `--content`; the examples include it for compatibility. Model and tool spans inside the agent come
672
676
  only from the agent's own instrumentation. Case outputs, error messages and explanations are
673
677
  stored in Hue independently of the telemetry capture setting, so a case's answer can be graded and read on
@@ -696,8 +700,11 @@ case is completed as failed (error `TelemetryNotAccepted`, evidence omitted as
696
700
  `telemetry_not_accepted`, no output or generated files attached) instead of being left started,
697
701
  and the run goes on. Each export of the CLI's own telemetry may take 30 seconds, retries
698
702
  included: a slow acknowledgement is waited for, and a 429, 502, 503, 504 or network error is
699
- retried within that time, honouring `Retry-After`; any other response, or rejected records, fails
700
- the case at once. Stderr names the case as it completes, with the export
703
+ retried within that time, honouring `Retry-After`. A 429 whose `Retry-After` is longer than that
704
+ holds the case's records and sends them again once it has passed, for at most 60 seconds of holds
705
+ per export, so such a case can take that much longer; a longer wait fails the case as
706
+ `telemetry_not_accepted`. Any other response, or rejected records, fails the case at once. Stderr
707
+ names the case as it completes, with the export
701
708
  issue counts, for example
702
709
  `[refund] telemetry not accepted, case failed: telemetry_not_accepted: traces failed 1 (HTTP 400)`;
703
710
  the case counts as an error in the table and JSON whatever its scores, and the command exits 1.
package/EVALUATIONS.md CHANGED
@@ -164,7 +164,7 @@ A callback receives `{inputs,hasOutput,output?,hasExpected,expected?,metadata,ex
164
164
  - `{state:"error",error:{type,message?}}`.
165
165
  - `{state:"skipped",explanation:"reason"}`.
166
166
 
167
- All declared metrics must appear exactly once and satisfy pinned types, bounds and categories. Each metric may include an optional `description` (1–500 characters) shown with its check in Hue's run view. The binding must match language, entrypoint, SHA-256 source digest and metric definitions. The digest is an authenticated caller declaration; it does not attest closures, dependency versions or actual execution. Callback source is never downloaded or evaluated. Callbacks are trusted local code; they have **no execution timeout or side-effect cancellation**. Concurrency limits active cases to 1–16 (default 1), with scorers evaluated sequentially within each case.
167
+ All declared metrics must appear exactly once and satisfy pinned types, bounds and categories. Each metric may include an optional `description` (1–500 characters) shown with its check in Hue's run view. The binding must match language, entrypoint, SHA-256 source digest and metric definitions. The digest is an authenticated caller declaration; it does not attest closures, dependency versions or actual execution. Callback source is never downloaded or evaluated. Callbacks are trusted local code; they have **no execution timeout or side-effect cancellation**. Concurrency limits active cases to 1–100 (default 1), with scorers evaluated sequentially within each case.
168
168
 
169
169
  `files` is present only when the runner handled files for that execution: every pinned input file
170
170
  (all roles, including the evaluator-only `org_template`) and every generated output, each with its
package/README.md CHANGED
@@ -324,11 +324,21 @@ there is no SDK retention timer or automatic content expiry. To redact strings
324
324
  before export, supply `redact(value, path)`; it applies to supported strings in
325
325
  attributes, resources, event/link attributes and log bodies. Return a string.
326
326
  Invalid helper content is omitted with an instrumentation failure; the span can still be delivered.
327
- A content value over Hue's 256 KiB value cap is not omitted: text is cut to a UTF-8 prefix and
327
+ A content value over Hue's 1 MiB value cap (or the value limit Hue's receiver advertises) is not
328
+ omitted. A span's own string attribute over the cap is uploaded to Hue whole and replaced by its
329
+ first 16 KiB (see [Values over the inline limit](#values-over-the-inline-limit)); `redact` sees
330
+ the whole value first, and its answer is what is uploaded. Any other value over the cap, and one
331
+ that could not be uploaded, is cut: text to a UTF-8 prefix, and
328
332
  bytes or a structured log body are replaced by the receiver's own marker
329
333
  (`{ "hue.truncated": true, "hue.truncated_bytes": <size> }`), and the value's key is listed in
330
334
  the record's `hue.truncated` attribute, where Hue's receiver lists the values it cuts itself,
331
335
  so the span still carries the call, the recorded part of its value and the rest of its evidence.
336
+ `redact` sees text past the cap, so it can recognize a secret that crosses it. A string longer
337
+ than the cap and 64 Ki code units that is not held whole for upload is cut to that length when
338
+ its record is queued, so a value of millions of characters cannot drop its record for the queue
339
+ budget; for such a text the last 64 Ki code units of the redactor's answer are never exported,
340
+ and tool definitions and recorded requests have their credentials removed from the whole value
341
+ before that cut.
332
342
  Export-time redactor failures reject the affected record and are reported by flush. Shared resources are redacted once per
333
343
  export batch. Do not put user content or secrets in span names or scope names.
334
344
 
@@ -361,18 +371,24 @@ rotates. Only definitions another integration recorded can be summarized: `hueTe
361
371
  inputs and exports through Hue's attached processors gets the summary.
362
372
  Recorded messages can inline files: GenAI `blob` parts in `gen_ai.input.messages` /
363
373
  `gen_ai.output.messages` (what the AI SDK 7 adapter records for a file part) and AI SDK 6 `file`
364
- parts in `ai.prompt.messages`. A span whose messages exceed 256 KiB would be cut, so when a
365
- record is queued Hue replaces the `content`/`data` of any such part longer than 64 KiB with the
366
- file's `sha256` and `size`, both of the file's own bytes whatever its media type (base64 content
367
- and `;base64` `data:` URLs decoded, other `data:` URLs percent-decoded, anything else as UTF-8 text), keeping the part's other fields such as `type`, `mime_type` and `mediaType`. Smaller inline
368
- files are exported as recorded. The digest matches `hue.recordFile`'s `hue.file.sha256` for the same
369
- bytes, so a file can be recognized wherever it appears. The replacement happens before the record is
370
- charged to the queue budget, so a large file does not drop its span. It is bounded: a message
371
- attribute longer than 8 MiB of text is left unchanged, and the default 8 MiB `maxQueueBytes` then
372
- drops the span. Base64 grows a file by a third, so that ceiling is an inline file of about 6 MiB.
373
- The same message text is hashed once per record, and a record inspects at most 16 MiB of message
374
- text in all; messages past that are charged as recorded. With `captureContent: false` the messages,
375
- which export removes, are neither hashed nor charged.
374
+ parts in `ai.prompt.messages`. A file is measured by its own bytes whatever its media type (base64
375
+ content and `;base64` `data:` URLs decoded, other `data:` URLs percent-decoded, anything else as
376
+ UTF-8 text). When a span's own message attribute inlines a file larger than 64 KiB, export uploads
377
+ the file's bytes to Hue under the part's media type (`mime_type` or `mediaType`, else its `data:`
378
+ URL's) and keeps the first 16 KiB of the part's `content`/`data` in place, listing the file in the
379
+ span's `hue.blobs` as `<attribute>#<JSON pointer>`, for example
380
+ `gen_ai.input.messages#/0/parts/1/content` (see
381
+ [Values over the inline limit](#values-over-the-inline-limit)). A file that is not uploaded, and a
382
+ large inline file in a log record or a span event, keeps the earlier behavior: the part's
383
+ `content`/`data` is replaced by the file's `sha256` and `size`, keeping its other fields such as
384
+ `type`, `mime_type` and `mediaType`, when the record is queued (or, for a span's own messages, at
385
+ export). Smaller inline files are exported as recorded. The digest matches `hue.recordFile`'s
386
+ `hue.file.sha256` for the same bytes, so a file can be recognized wherever it appears. It is bounded:
387
+ a message attribute longer than 8 MiB of text is not inspected for files, and is uploaded or cut
388
+ like any value over the cap. Base64 grows a file by a third, so that ceiling is an inline file of
389
+ about 6 MiB. The same message text is inspected once per record, and a record inspects at most
390
+ 16 MiB of message text in all. With `captureContent: false` the messages, which export removes,
391
+ are neither inspected nor charged.
376
392
 
377
393
  Manual helpers encode JSON values without converting null into absence. Unknown
378
394
  outputs and usage remain absent. This SDK does not estimate tokens or cost. A thrown
@@ -476,15 +492,92 @@ const hue = createHue({
476
492
  ## Delivery behavior
477
493
 
478
494
  Exports retry temporary HTTP/network failures (429, 502, 503, 504 and connection errors,
479
- honoring `Retry-After`) within the export timeout, by OpenTelemetry's OTLP/HTTP exporter rules. Each
480
- request is limited to 1 MiB before gzip (with space reserved for gzip overhead) and each content value to 256 KiB. Batches
481
- split at record boundaries. A record over the request limit on its own sheds its content values,
495
+ honoring `Retry-After`) within the export timeout, by OpenTelemetry's OTLP/HTTP exporter rules. A
496
+ 429 whose `Retry-After` outlasts the export timeout keeps its records queued, within the queue's
497
+ bounds, and sends them again once the wait has passed; an export holds its records this way for at
498
+ most 60 seconds in all, and a longer wait loses them, reported as a `failed` issue with status 429.
499
+ `flush()` and `shutdown()` wait for a held export, and a held export keeps the process running
500
+ until it is sent. When `shutdownSafe()`'s budget runs out first, a client that owns its providers
501
+ ends the hold: its records are reported lost to the rate limit and the process can exit. In attach
502
+ mode the application's providers drain with their own timeout (OpenTelemetry's default is 30
503
+ seconds), so a flush during a longer hold can report a provider flush failure while the held
504
+ export continues.
505
+
506
+ Requests are measured as Hue's receiver measures them: after gzip against its request limit and
507
+ after decompression against its decoded limit. Until a response advertises its own limits
508
+ (`Hue-Max-Request-Bytes`, `Hue-Max-Decoded-Bytes` and `Hue-Max-Value-Bytes`, on every
509
+ acknowledgement and refusal) these are 1 MiB on the wire, 4 MiB decoded and 1 MiB per value;
510
+ advertised limits are adopted, clamped to 1–64 MiB, 1–64 MiB and 256 KiB–16 MiB, and never
511
+ exceeded: a request that limits lowered while it was retried or held no longer fit is split and
512
+ shed again before it is sent. Batches are packed to 4 MiB before gzip (or the decoded limit, when lower) and split at
513
+ record boundaries; a batch that compresses worse than the limit allows is sent in halves. A record
514
+ over a limit on its own sheds its content values,
482
515
  largest first, each replaced by the receiver's marker and listed under `hue.truncated`, until it
483
516
  fits; only a record too large without any content value is lost, reported as an `invalid` issue,
484
517
  and counted on its trace's root span as `hue.sdk.dropped_records` when the root is exported, so
485
- Hue reads the trace as incomplete by that many records. Each signal queues at most 2,048 records, including
518
+ Hue reads the trace as incomplete by that many records; so are records lost to a rate limit
519
+ longer than the hold. Each signal queues at most 2,048 records, including
486
520
  exports in flight; overflow is reported through the callback, counters and next
487
- flush. This is an in-memory queue, not durable storage.
521
+ flush. This is an in-memory queue, not durable storage. A client's own providers keep 2,000
522
+ attributes, events and links per span and 2,000 attributes per log record (OpenTelemetry's default
523
+ is 128); providers an application attaches Hue to keep their own limits. A span whose events or
524
+ links do not all fit the queue's byte budget keeps its newest ones and counts the rest in its
525
+ dropped event and link counts, rather than being dropped whole.
526
+
527
+ ### Values over the inline limit
528
+
529
+ Hue keeps a value inline up to its value limit (1 MiB unless the receiver advertises another in
530
+ `Hue-Max-Value-Bytes`). A span's own string attribute larger than that (helper content such as
531
+ `input.value`, `output.value` and tool results, or another instrumentation's attribute), and an
532
+ inline file over 64 KiB in a span's own recorded messages, is uploaded to Hue apart from the span,
533
+ up to Hue's upload limit of 1,000,000,000 bytes:
534
+
535
+ 1. Export computes the SHA-256 of the value's bytes (a string as UTF-8, a file as its own bytes)
536
+ and reserves it with `POST /api/v1/otlp/blobs`, authenticated with the project key. Hue answers
537
+ that it already stores the value under the trace, and nothing is uploaded, or with a presigned
538
+ URL.
539
+ 2. The bytes go straight to the project's evidence store in one `PUT` to that URL, with exactly the
540
+ headers Hue signed and never the project key (over HTTPS, or plain HTTP to a loopback store or
541
+ with `allowInsecureHttp`), and
542
+ the upload is confirmed with `POST /api/v1/otlp/blobs/complete`. A PUT the store answers with
543
+ 412 (the same value uploaded at the same time by another span) counts as stored.
544
+ 3. The span keeps the value's first 16 KiB in the attribute it replaced and lists the value in its
545
+ `hue.blobs` attribute, one compact JSON object per value:
546
+ `{"key":"output.value","sha256":"<64 hex>","size":5242880,"content_type":"application/json"}`.
547
+ A whole value is listed under `hue.truncated` as well, since its inline text is a prefix.
548
+
549
+ Hue stores the value under the trace, so the trace's retention, deletion and erasure cover it.
550
+ Uploads run in the export, never on the application's thread: at most four at a time, each request
551
+ within the export timeout, and all of one export's uploads within six export timeouts; a
552
+ transient failure is retried once. A rate-limited reservation (429) waits out its `Retry-After`
553
+ within the export's 60-second hold. `flush()` and `shutdown()` wait for uploads like any export
554
+ work, and a `shutdownSafe()` whose budget runs out ends the uploads in flight.
555
+
556
+ A value that cannot be uploaded (Hue or the store refused it or failed, the network or a deadline
557
+ did, the receiver has no upload route, or the value is over 1 GB) is exported as before uploads
558
+ existed: cut to the limit and listed under `hue.truncated`, an inline file as its digest. Each
559
+ export reports such values in a `warning` issue per cause, whose `count` is the values and
560
+ `traceIds` their traces, and counts them in `report.uploadFallbacks`; uploaded values are counted
561
+ in `report.uploadedValues`. A warning never makes `flush()` throw. After a transient failure, or
562
+ a refusal that is not about the value alone, uploads pause for 30 seconds, and a receiver without
563
+ the upload route (a 404 that is not Hue's, a 405 or 501, or a 200 that is no reservation) is not
564
+ asked again for 10 minutes, so an unavailable store or another OTLP receiver meets no retry storm.
565
+
566
+ To upload a value whole, the queue holds it until export apart from `maxQueueBytes`, within a
567
+ 128 MiB budget of held values (a string charged two bytes a code unit); a recorded message holding
568
+ an inline file over 64 KiB is held the same way. `maxQueueBytes` is still charged what the value
569
+ would cost cut (a message, its copy with the files as their digest), so one that cannot be
570
+ uploaded is exported exactly as it was before. A value the budget cannot hold, or one queued while
571
+ the receiver is known to lack the upload route, is cut (its files digested) when it is queued, as
572
+ before, and reported as not uploaded, once per file; a single value larger than the whole budget
573
+ is held while nothing else is. While values can be uploaded, helpers encode content whole, up to the
574
+ upload limit and 1,048,576 values, rather than cutting it to the value limit as they record it.
575
+
576
+ Nothing is uploaded with `captureContent: false` or a setup credential (`hue_setup_…`), nor from
577
+ log records (Hue does not read a log record's uploaded values yet), span events, links, resources
578
+ or in-progress placeholders: those values are cut as before. `redact` and the credential scrub of
579
+ tool definitions and recorded requests run on the whole value before it is hashed, so a value they
580
+ rewrite is uploaded as rewritten.
488
581
 
489
582
  `flush()` waits for the current trace and log export work. A partial rejection,
490
583
  invalid acknowledgement, queue drop or failure throws `HueExportError`; its
@@ -702,6 +795,6 @@ existing-provider flush callbacks and recovery. Local/CI runners remain availabl
702
795
 
703
796
  Use `createHueSafe(options)` for best-effort startup. Invalid initialization returns a disabled client that keeps your `onExportIssue` hook and records the reason as an instrumentation failure. Pass `enabled: false` to disable Hue without a key or a `captureContent` choice; disabled helpers still execute the application callback, and `inject()` keeps propagating the application's own trace context. `flushSafe({ timeoutMillis: 1000 })` and `shutdownSafe({ timeoutMillis: 1000 })` return `{ ok, timedOut, report }` without rejecting. Strict initialization, connection checks and `flush()` remain available for diagnostics; do not gate application readiness or responses on them.
704
797
 
705
- Capture/serialization/redaction/provider failures omit unsafe telemetry, record failures, and preserve the original business result/error. Async diagnostic rejections are contained; diagnostics are rate-limited. The default `maxQueueBytes` is 8 MiB across traces/logs including in-flight work, alongside the existing record cap. `pendingBytes` is a current queue gauge; `droppedSpans`, `droppedLogs` and `instrumentationFailures` are cumulative failure counters. This is a telemetry budget, not a total process memory ceiling. A timeout bounds the caller and does not cancel a borrowed provider. Never retry the business operation to recover telemetry. See [production safety](https://docs.hue.run/guides/production-safety).
798
+ Capture/serialization/redaction/provider failures omit unsafe telemetry, record failures, and preserve the original business result/error. Async diagnostic rejections are contained; diagnostics are rate-limited. The default `maxQueueBytes` is 8 MiB across traces/logs including in-flight work, alongside the existing record cap. `pendingBytes` is a current queue gauge; `droppedSpans`, `droppedLogs` and `instrumentationFailures` are cumulative failure counters; `uploadedValues` and `uploadFallbacks` count the values over the inline limit uploaded apart from their spans and exported cut instead. This is a telemetry budget, not a total process memory ceiling. A timeout bounds the caller and does not cancel a borrowed provider. Never retry the business operation to recover telemetry. See [production safety](https://docs.hue.run/guides/production-safety).
706
799
 
707
800
  Queued records snapshot supported telemetry values when a span ends or a log is emitted; later caller mutations cannot change queued data. Resource attributes still awaiting detection are omitted with a sanitized warning. Later records include them after detection finishes; await resource detection before instrumentation when those attributes are required.
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Values larger than Hue's inline limit travel apart from their span: the SDK uploads the value's
3
+ * bytes straight to the project's evidence store through a presigned PUT that Hue answers at
4
+ * `POST /api/v1/otlp/blobs`, keeps the value's first 16 KiB in the attribute it replaced, and
5
+ * lists the value in the span attribute `hue.blobs`. A value that cannot be uploaded is exported
6
+ * as it was before uploads existed: cut to the inline limit and listed under `hue.truncated` (an
7
+ * inline file as its digest), reported as a warning and counted.
8
+ */
9
+ /** The span attribute listing the values the SDK uploaded apart from the span: one compact JSON
10
+ * object per value, `{"key", "sha256", "size", "content_type"}`, as Hue reads it. */
11
+ export declare const BLOBS_KEY = "hue.blobs";
12
+ /** The largest value one upload may hold (`Hue-Max-Blob-Bytes`): a larger value is cut inline. */
13
+ export declare const MAX_BLOB_BYTES = 1000000000;
14
+ /** What an uploaded value keeps inline: its first 16 KiB, the receiver's search excerpt. */
15
+ export declare const BLOB_PREFIX_BYTES: number;
16
+ /** The entries one record's `hue.blobs` may list; Hue reads no more. */
17
+ export declare const MAX_BLOB_ENTRIES = 64;
18
+ /**
19
+ * The values over the inline limit that queued spans may hold whole for upload at once, charged
20
+ * apart from `maxQueueBytes` (a string as two bytes a code unit, bytes as themselves). A value
21
+ * the budget cannot hold is cut when it is queued, as before uploads existed; a single value
22
+ * larger than the whole budget is held while nothing else is.
23
+ */
24
+ export declare const MAX_HELD_BLOB_BYTES: number;
25
+ /** How long one export may spend uploading its values, on top of its requests. */
26
+ export declare function uploadBudgetMillis(timeoutMillis: number): number;
27
+ /** Why a value over the inline limit was exported cut instead of uploaded. */
28
+ export type FallbackReason = "unsupported" | "failed" | "tooLarge" | "budget";
29
+ /** The fixed description of each fallback, as an export issue reports it. */
30
+ export declare const FALLBACK_MESSAGES: Record<FallbackReason, string>;
31
+ /** A span's own attribute value the redaction left whole for the upload step to place. */
32
+ export interface OffloadCandidate {
33
+ key: string;
34
+ value: string | Uint8Array;
35
+ }
36
+ /** What redaction leaves to the upload step: the candidates, and the keys of the values over the
37
+ * inline limit the queue cut when the span was admitted (they cannot be uploaded whole). */
38
+ export interface OffloadCollector {
39
+ candidates: OffloadCandidate[];
40
+ cut: string[];
41
+ }
42
+ /** The outcome of one export's uploads, reported once the export's records are placed. */
43
+ export interface OffloadTally {
44
+ uploaded: number;
45
+ fallbacks: Map<FallbackReason, {
46
+ count: number;
47
+ traceIds: Set<string>;
48
+ }>;
49
+ }
50
+ export declare function newTally(): OffloadTally;
51
+ export declare function isBlobContentType(value: unknown): value is string;
52
+ /** The media type an uploaded text is stored under: JSON when it reads as a JSON object or array
53
+ * (a structured value's text), plain UTF-8 text otherwise. Both are previewed as text. */
54
+ export declare function textContentType(text: string): string;
55
+ /** What an uploaded value keeps inline: its first 16 KiB (text cut back to a character), held
56
+ * as its own copy so the whole value is not kept alive by it. */
57
+ export declare function blobPrefix(value: string): string;
58
+ export declare function blobPrefix(value: Uint8Array): Uint8Array;
59
+ /** The SHA-256 (lowercase hex) and byte size of what an upload of `value` carries, yielding to
60
+ * the event loop between slices so a large value does not stall the application. */
61
+ export declare function digestOf(value: string | Uint8Array): Promise<{
62
+ sha256: string;
63
+ size: number;
64
+ }>;
65
+ /** What one export's uploads share: their deadline, and the export's hold for a receiver that
66
+ * limited the rate (`wait` resolves false when the hold may not take `millis` more). */
67
+ export interface UploadContext {
68
+ deadline: number;
69
+ wait(millis: number): Promise<boolean>;
70
+ }
71
+ type Outcome = {
72
+ ok: true;
73
+ contentType: string;
74
+ } | {
75
+ ok: false;
76
+ reason: FallbackReason;
77
+ };
78
+ /** The upload route's options, from the transport's validated options. */
79
+ export interface UploaderOptions {
80
+ baseUrl: string;
81
+ apiKey: string;
82
+ timeoutMillis: number;
83
+ allowInsecureHttp: boolean;
84
+ userAgent: string;
85
+ }
86
+ /**
87
+ * Uploads values to Hue: reserves each with `POST /api/v1/otlp/blobs`, PUTs its bytes to the
88
+ * presigned URL Hue answers (skipped when Hue already stores the value), and completes it. At
89
+ * most {@link UPLOAD_CONCURRENCY} uploads run at once; every request has a deadline and none
90
+ * follows a redirect. A receiver without the route (a 404 without Hue's JSON error, a 405 or
91
+ * 501, or a 200 that is no reservation) is remembered for {@link UNSUPPORTED_PAUSE_MILLIS}, and a
92
+ * transient failure or a refusal that is not about the value alone pauses uploads for
93
+ * {@link FAILURE_PAUSE_MILLIS}, so values fall back to the inline cut instead of retrying.
94
+ */
95
+ export declare class BlobUploader {
96
+ private readonly options;
97
+ private unsupportedUntil;
98
+ private pausedUntil;
99
+ private closed;
100
+ private running;
101
+ private waiting;
102
+ private requests;
103
+ constructor(options: UploaderOptions);
104
+ /** Whether a value over the inline limit is worth holding whole for upload now. */
105
+ available(): boolean;
106
+ /** Whether the receiver lacks the upload route, as it last answered. */
107
+ unsupported(): boolean;
108
+ /** Ends uploads in flight and refuses new ones: their values fall back to the inline cut. */
109
+ close(): void;
110
+ /** Runs `work` once fewer than {@link UPLOAD_CONCURRENCY} uploads are in flight. */
111
+ limit<R>(work: () => Promise<R>): Promise<R>;
112
+ /** Why a value cannot be uploaded right now without trying, if it cannot. */
113
+ refusal(): FallbackReason | undefined;
114
+ private pause;
115
+ /**
116
+ * Stores `value` under `key` of the trace: reserved, uploaded unless Hue already holds it, and
117
+ * completed. The caller holds a {@link limit} slot.
118
+ */
119
+ store(traceId: string, key: string, value: string | Uint8Array, contentType: string, digest: {
120
+ sha256: string;
121
+ size: number;
122
+ }, upload: UploadContext): Promise<Outcome>;
123
+ private jsonHeaders;
124
+ /** Reserves the value: Hue answers that it holds it already, or a presigned PUT. A rate limit
125
+ * is waited out within the export's hold, a transient refusal retried once. */
126
+ private reserve;
127
+ private delay;
128
+ /** A 200 answer read: `exists`, or an upload whose URL and headers are usable as given. */
129
+ private reservation;
130
+ /** The PUT of the value's bytes, with exactly the headers Hue signed. */
131
+ private put;
132
+ /**
133
+ * One HTTP request with a deadline and an idle timeout, never following a redirect, its
134
+ * response body bounded. Undefined for a network error, a timeout or a closed uploader. A text
135
+ * or byte body larger than one slice is written a slice at a time with backpressure; `length`
136
+ * is then its byte size.
137
+ */
138
+ private request;
139
+ }
140
+ /** Where one export's values are uploaded, and what it learned of them. */
141
+ export interface OffloadOptions {
142
+ uploader: BlobUploader;
143
+ traceId: string;
144
+ /** The receiver's inline value limit. */
145
+ valueBytes: number;
146
+ upload: UploadContext;
147
+ tally: OffloadTally;
148
+ }
149
+ /**
150
+ * A span's own attributes with each candidate placed: an inline file part over 64 KiB in a
151
+ * recorded message uploaded and replaced by its first 16 KiB (or by its digest, as before, when
152
+ * it could not be), and a value still over the inline limit uploaded and replaced by its first
153
+ * 16 KiB (or cut to the limit), each upload listed under `hue.blobs`, each whole value cut or
154
+ * uploaded listed under `hue.truncated`. Every fallback is counted in the tally.
155
+ */
156
+ export declare function offloadAttributes(attributes: Record<string, unknown>, collector: OffloadCollector, options: OffloadOptions): Promise<Record<string, unknown>>;
157
+ /**
158
+ * The span's own attributes with every candidate placed as it was before uploads existed, for an
159
+ * upload step that failed unexpectedly: a message's large inline files as their digest, a value
160
+ * over the inline limit cut to it (bytes replaced by the receiver's marker) and listed under
161
+ * `hue.truncated`.
162
+ */
163
+ export declare function fallbackAttributes(attributes: Record<string, unknown>, collector: OffloadCollector, valueBytes: number, tally: OffloadTally, traceId: string): Record<string, unknown>;
164
+ export {};