@hue-run/sdk 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.22.0`, `@opentelemetry/api@1.9.1` and
45
+ not a completed integration. TypeScript uses `@hue-run/sdk@0.23.0`, `@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
 
@@ -381,8 +381,8 @@ and Hue never receives or stores them. The command is part of the `hue` executab
381
381
  # LANGFUSE_PUBLIC_KEY=pk-lf-...
382
382
  # LANGFUSE_SECRET_KEY=sk-lf-...
383
383
  # LANGFUSE_BASE_URL=https://cloud.langfuse.com
384
- npx -y @hue-run/sdk@0.22.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
385
- npx -y @hue-run/sdk@0.22.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
384
+ npx -y @hue-run/sdk@0.23.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
385
+ npx -y @hue-run/sdk@0.23.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
386
386
  ```
387
387
 
388
388
  `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` come from the environment or `--env-path`
@@ -892,7 +892,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
892
892
  # Set project to an existing supported fixture; use the same directory on resume.
893
893
  project=/absolute/path/to/supported-fixture
894
894
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
895
- --archive .artifacts/typescript/hue-run-sdk-0.22.0.tgz \
895
+ --archive .artifacts/typescript/hue-run-sdk-0.23.0.tgz \
896
896
  --origin https://STAGING_ORIGIN \
897
897
  --project "$project" --command setup \
898
898
  --evidence .context/setup-staging-before-claim.json
@@ -903,7 +903,7 @@ the private local handoff and finish the real browser claim, then reconcile the
903
903
 
904
904
  ```sh
905
905
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
906
- --archive .artifacts/typescript/hue-run-sdk-0.22.0.tgz \
906
+ --archive .artifacts/typescript/hue-run-sdk-0.23.0.tgz \
907
907
  --origin https://STAGING_ORIGIN \
908
908
  --project "$project" --command claim \
909
909
  --evidence .context/setup-staging-after-claim.json
@@ -945,10 +945,10 @@ otherwise. Never hand the agent Hue-native tools (`context.tools`, `context.mcp`
945
945
  production agent lacks. Pin the SDK, so the agent's repository gains no dependency:
946
946
 
947
947
  ```sh
948
- npx -y @hue-run/sdk@0.22.0 eval --case "Refund an eligible charge" --command "node agent.js"
949
- npx -y @hue-run/sdk@0.22.0 eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <run id>
950
- npx -y @hue-run/sdk@0.22.0 eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
951
- npx -y @hue-run/sdk@0.22.0 eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision>
948
+ npx -y @hue-run/sdk@0.23.0 eval --case "Refund an eligible charge" --command "node agent.js"
949
+ npx -y @hue-run/sdk@0.23.0 eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <run id>
950
+ npx -y @hue-run/sdk@0.23.0 eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
951
+ npx -y @hue-run/sdk@0.23.0 eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision>
952
952
  ```
953
953
 
954
954
  One invocation creates one run holding every selected case; it prints the run URL
@@ -986,7 +986,7 @@ export default function runMyAgent(inputs: JsonValue, context: SimulationTargetC
986
986
  ```
987
987
 
988
988
  ```sh
989
- npx -y @hue-run/sdk@0.22.0 eval --case "Refund an eligible charge" ./hue-agent.ts --content
989
+ npx -y @hue-run/sdk@0.23.0 eval --case "Refund an eligible charge" ./hue-agent.ts --content
990
990
  ```
991
991
 
992
992
  `--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
package/README.md CHANGED
@@ -552,8 +552,8 @@ the Langfuse project's own keys (`LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, a
552
552
  `HUE_API_KEY`:
553
553
 
554
554
  ```sh
555
- npx -y @hue-run/sdk@0.22.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
556
- npx -y @hue-run/sdk@0.22.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
555
+ npx -y @hue-run/sdk@0.23.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
556
+ npx -y @hue-run/sdk@0.23.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
557
557
  ```
558
558
 
559
559
  - It reads Langfuse Cloud and self-hosted v4 through the Observations API v2, and self-hosted v3
@@ -1106,6 +1106,16 @@ own. Stop request production
1106
1106
  before shutdown so late spans cannot race it. A client does not own instrumented
1107
1107
  operations still running in the application.
1108
1108
 
1109
+ In Vercel Functions, the TypeScript SDK automatically registers admitted spans and logs with
1110
+ [`waitUntil`](https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package#waituntil).
1111
+ The response or Workflow step can return while the existing batches and their media uploads finish
1112
+ in the background, within the function's lifetime limit. You can remove a per-step
1113
+ `await hue.flushSafe()` when its purpose is to prevent the function freezing before delivery.
1114
+ Keep an explicit flush when you need its acknowledgement report before continuing. Background
1115
+ delivery preserves capture, redaction, batching and failure reports; it never flushes or shuts down
1116
+ application-owned providers. In other hosts, keep the host's lifetime hook or await a flush before
1117
+ a short-lived process exits.
1118
+
1109
1119
  ## Live spans
1110
1120
 
1111
1121
  OpenTelemetry exports a span only when it ends, so a long streamed turn would otherwise stay
@@ -1119,7 +1129,11 @@ arrives.
1119
1129
  The placeholder then waits for the next batch export like any queued span: up to 500 ms until the
1120
1130
  next tick, then the 1 s batch delay when nothing else is queued, so it usually reaches Hue within
1121
1131
  about 1.5 s of its span starting. It goes out sooner when a batch is already scheduled, for example
1122
- because another span has just ended, and at once for a full batch or `flush()`. It goes out later
1132
+ because another span has just ended, and at once for a full batch or a flush with no completed
1133
+ spans. When a flush has completed spans to deliver, it defers queued placeholders to the next
1134
+ normal announcement after the drain and its uploads finish. This avoids an extra acknowledgement
1135
+ for a still-open parent while preserving delivery of its completed children. A parent that ends
1136
+ before that announcement sends only its completed span. A placeholder goes out later
1123
1137
  while an earlier export is still in flight, because the next batch is scheduled only after that
1124
1138
  export finishes. A placeholder whose span has ended by the time it is exported is not sent, so a
1125
1139
  short span may send none and appear in Hue only when it finishes.
@@ -53,6 +53,9 @@ export declare class HueTransport {
53
53
  private failed;
54
54
  private spans;
55
55
  private logs;
56
+ private deliveryCohort?;
57
+ private deliveries;
58
+ private deliveryUploads;
56
59
  private pendingBytes;
57
60
  private dropped;
58
61
  /** Records of each trace the SDK never sent (dropped from the queue, refused as invalid or
@@ -81,6 +84,8 @@ export declare class HueTransport {
81
84
  private placeholdersRejected;
82
85
  private placeholders;
83
86
  private placeholderSources;
87
+ private forcedSpanDrains;
88
+ private completedSpanDrains;
84
89
  private batchSpans?;
85
90
  private limits;
86
91
  /** The rate-limit holds in progress, each ended early by calling it. */
@@ -120,11 +125,16 @@ export declare class HueTransport {
120
125
  * skipped silently.
121
126
  */
122
127
  private enqueue;
128
+ /** Register in the caller's request before it can return; outside Vercel this is a no-op. */
129
+ private protectDelivery;
130
+ private holdDelivery;
131
+ private finishDelivery;
123
132
  private track;
124
133
  /** Placeholders are built lazily, so input set right after a span starts is included. */
125
134
  private announceLiveSpans;
126
135
  private stopLiveTimer;
127
136
  private stopLiveSpans;
137
+ private beginSpanDrain;
128
138
  /** Holds the staged attachments of a log record being admitted with the queued record, within
129
139
  * the held-value budget; those the budget refuses are counted as not uploaded. */
130
140
  private admitLogParts;
@@ -144,6 +154,8 @@ export declare class HueTransport {
144
154
  private protectMedia;
145
155
  private releaseMediaIdentity;
146
156
  private sweepMedia;
157
+ /** Keep an export's warning aggregation within each cohort's upload ownership. */
158
+ private collectDeliveryUploads;
147
159
  /** Bounded: a trace whose root never arrives (an abandoned run) gives way to newer ones. */
148
160
  private countDropped;
149
161
  /** Cumulative counters and current queue gauges. */
package/dist/transport.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { gzip } from "node:zlib";
3
+ import { waitUntil } from "@vercel/functions";
3
4
  import { ExportResultCode } from "@opentelemetry/core";
4
5
  import { createOtlpNetworkExportDelegate, OTLPExporterBase, OTLPExporterError, } from "@opentelemetry/otlp-exporter-base";
5
6
  import { createOtlpHttpExporterMetrics } from "@opentelemetry/otlp-exporter-base/node-http";
@@ -182,6 +183,10 @@ export class HueTransport {
182
183
  failed = { traces: 0, logs: 0 };
183
184
  spans = new Map();
184
185
  logs = new Map();
186
+ // A cohort closes when export starts, so later requests cannot extend its lifetime.
187
+ deliveryCohort;
188
+ deliveries = new WeakMap();
189
+ deliveryUploads = new WeakMap();
185
190
  pendingBytes = 0;
186
191
  dropped = { traces: 0, logs: 0 };
187
192
  /** Records of each trace the SDK never sent (dropped from the queue, refused as invalid or
@@ -214,6 +219,8 @@ export class HueTransport {
214
219
  placeholders = new WeakMap();
215
220
  // The running span each admitted placeholder announces, so one it has outlived is not sent.
216
221
  placeholderSources = new WeakMap();
222
+ forcedSpanDrains = 0;
223
+ completedSpanDrains = 0;
217
224
  batchSpans;
218
225
  // What the receiver accepts, as it last advertised; the limits of this release until it does.
219
226
  limits = DEFAULT_RECEIVER_LIMITS;
@@ -340,6 +347,7 @@ export class HueTransport {
340
347
  this.protectMedia(admitted);
341
348
  this.countEnded(admitted);
342
349
  spans.onEnd(admitted);
350
+ this.protectDelivery(admitted);
343
351
  }
344
352
  catch {
345
353
  if (admitted)
@@ -352,12 +360,18 @@ export class HueTransport {
352
360
  // The spans are sent first, then the uploads queued so far are waited for, unless a
353
361
  // flush sends them: that flush waits for the uploads after its send.
354
362
  forceFlush: async () => {
363
+ const finish = this.beginSpanDrain();
355
364
  try {
356
365
  await spans.forceFlush();
357
366
  }
358
367
  finally {
359
- if (!this.sendingRecords.getStore())
360
- await this.drainUploads();
368
+ try {
369
+ if (!this.sendingRecords.getStore())
370
+ await this.drainUploads();
371
+ }
372
+ finally {
373
+ finish();
374
+ }
361
375
  }
362
376
  },
363
377
  shutdown: async () => {
@@ -391,6 +405,7 @@ export class HueTransport {
391
405
  admitted = queued;
392
406
  this.admitLogParts(log, queued);
393
407
  logs.onEmit(queued);
408
+ this.protectDelivery(admitted);
394
409
  }
395
410
  catch {
396
411
  if (admitted)
@@ -399,12 +414,18 @@ export class HueTransport {
399
414
  }
400
415
  },
401
416
  forceFlush: async () => {
417
+ const finish = this.beginSpanDrain();
402
418
  try {
403
419
  await logs.forceFlush();
404
420
  }
405
421
  finally {
406
- if (!this.sendingRecords.getStore())
407
- await this.drainUploads();
422
+ try {
423
+ if (!this.sendingRecords.getStore())
424
+ await this.drainUploads();
425
+ }
426
+ finally {
427
+ finish();
428
+ }
408
429
  }
409
430
  },
410
431
  shutdown: async () => {
@@ -472,6 +493,14 @@ export class HueTransport {
472
493
  this.logs.set(snapshot.record, snapshot.bytes);
473
494
  if (snapshot.unresolvedResource && !advisory)
474
495
  this.issue(signal, "warning", 0, "Unresolved resource attributes omitted from the telemetry snapshot");
496
+ if (!advisory) {
497
+ const cohort = (this.deliveryCohort ??= {
498
+ pending: 0,
499
+ ...Promise.withResolvers(),
500
+ });
501
+ cohort.pending++;
502
+ this.deliveries.set(snapshot.record, cohort);
503
+ }
475
504
  return snapshot.record;
476
505
  }
477
506
  catch {
@@ -481,11 +510,42 @@ export class HueTransport {
481
510
  return undefined;
482
511
  }
483
512
  }
513
+ /** Register in the caller's request before it can return; outside Vercel this is a no-op. */
514
+ protectDelivery(record) {
515
+ const cohort = this.deliveries.get(record);
516
+ if (!cohort)
517
+ return;
518
+ try {
519
+ waitUntil(cohort.promise);
520
+ }
521
+ catch {
522
+ // A host hook must never undo admission or change the application's result.
523
+ }
524
+ }
525
+ /** @internal Seal the batch's cohort before export can yield to another request. */
526
+ exporting(records) {
527
+ if (records.some((record) => this.deliveries.get(record) === this.deliveryCohort))
528
+ this.deliveryCohort = undefined;
529
+ }
530
+ holdDelivery(cohort, work) {
531
+ if (!cohort)
532
+ return;
533
+ cohort.pending++;
534
+ const finish = () => this.finishDelivery(cohort);
535
+ void work.then(finish, finish);
536
+ }
537
+ finishDelivery(cohort) {
538
+ if (--cohort.pending !== 0)
539
+ return;
540
+ if (this.deliveryCohort === cohort)
541
+ this.deliveryCohort = undefined;
542
+ cohort.resolve();
543
+ }
484
544
  track(span) {
485
545
  try {
486
546
  if (!this.liveSpans || this.live.size >= MAX_LIVE_SPANS)
487
547
  return;
488
- if (!(span.spanContext().traceFlags & 1) || !span.isRecording() || !announcesLiveSpan(span))
548
+ if (!(span.spanContext().traceFlags & 1) || span.ended || !announcesLiveSpan(span))
489
549
  return;
490
550
  this.live.add(span);
491
551
  if (!this.liveTimer) {
@@ -500,6 +560,8 @@ export class HueTransport {
500
560
  }
501
561
  /** Placeholders are built lazily, so input set right after a span starts is included. */
502
562
  announceLiveSpans() {
563
+ if (this.forcedSpanDrains)
564
+ return;
503
565
  for (const span of this.live) {
504
566
  this.live.delete(span);
505
567
  try {
@@ -547,6 +609,28 @@ export class HueTransport {
547
609
  placeholderSettled(record) {
548
610
  return this.placeholderSources.get(record)?.ended === true;
549
611
  }
612
+ beginSpanDrain() {
613
+ const completed = [...this.spans.keys()].some((span) => !this.placeholders.has(span));
614
+ this.forcedSpanDrains++;
615
+ if (completed)
616
+ this.completedSpanDrains++;
617
+ return () => {
618
+ this.forcedSpanDrains--;
619
+ if (completed)
620
+ this.completedSpanDrains--;
621
+ };
622
+ }
623
+ /** @internal A forced drain sends completed spans without another advisory round trip. */
624
+ deferPlaceholders(records) {
625
+ if (!this.completedSpanDrains)
626
+ return false;
627
+ for (const record of records) {
628
+ const source = this.placeholderSources.get(record);
629
+ if (!this.closed && source)
630
+ this.track(source);
631
+ }
632
+ return true;
633
+ }
550
634
  /** @internal Exporter callback: false once the receiver refused placeholders; queued ones are then dropped. */
551
635
  sendsPlaceholders() {
552
636
  return !this.placeholdersRejected;
@@ -570,6 +654,11 @@ export class HueTransport {
570
654
  /** @internal Exporter callback: releases queued records after an export attempt settles. */
571
655
  finish(signal, records) {
572
656
  for (const record of records) {
657
+ const cohort = this.deliveries.get(record);
658
+ if (cohort) {
659
+ this.deliveries.delete(record);
660
+ this.finishDelivery(cohort);
661
+ }
573
662
  this.releaseMedia(record);
574
663
  this.logMedia.delete(record);
575
664
  const pending = signal === "traces" ? this.spans : this.logs;
@@ -737,6 +826,7 @@ export class HueTransport {
737
826
  * them. */
738
827
  uploadQueued(record, settled, shared, released) {
739
828
  this.awaitUploads(settled);
829
+ this.holdDelivery(this.deliveries.get(record), settled);
740
830
  const identity = this.mediaIdentity(record);
741
831
  const staged = identity
742
832
  ? (this.media.get(identity) ?? []).reduce((total, file) => total + heldBytesOf(file.bytes), 0)
@@ -853,8 +943,8 @@ export class HueTransport {
853
943
  return this.offloadsValues() && this.uploader?.available() === true;
854
944
  }
855
945
  /** @internal Exporter callback: a redacted span with the values its redaction left to the
856
- * upload step placed: listed under `hue.blobs` with their uploads queued (each added to `late`
857
- * once it settles, and charged in place of the `queued` record it came from), or cut as before.
946
+ * upload step placed: listed under `hue.blobs` with their uploads queued (reported once settled,
947
+ * and charged in place of the `queued` record they came from), or cut as before.
858
948
  * Never rejects. */
859
949
  async offloadSpan(span, collector, upload, tally, late, queued) {
860
950
  // A record without a readable context cannot name its trace: its values are cut.
@@ -878,8 +968,7 @@ export class HueTransport {
878
968
  memo: this.mediaMemo,
879
969
  background: own,
880
970
  });
881
- if (own.settled.length)
882
- late.settled.push(mergeWhenSettled(late.tally, own.tally, own.settled));
971
+ this.collectDeliveryUploads(queued, own, late);
883
972
  return { ...span, attributes: attributes };
884
973
  }
885
974
  catch {
@@ -919,14 +1008,37 @@ export class HueTransport {
919
1008
  memo: this.mediaMemo,
920
1009
  background: own,
921
1010
  });
922
- if (own.settled.length)
923
- late.settled.push(mergeWhenSettled(late.tally, own.tally, own.settled));
1011
+ this.collectDeliveryUploads(queued, own, late);
924
1012
  return placed(result);
925
1013
  }
926
1014
  catch {
927
1015
  return placed(fallbackRecord(log.attributes, collector, valueBytes, tally, traceId, this.mediaMemo));
928
1016
  }
929
1017
  }
1018
+ /** Keep an export's warning aggregation within each cohort's upload ownership. */
1019
+ collectDeliveryUploads(record, uploads, late) {
1020
+ const { settled, tally } = uploads;
1021
+ if (!settled.length)
1022
+ return;
1023
+ let groups = this.deliveryUploads.get(late);
1024
+ if (!groups)
1025
+ this.deliveryUploads.set(late, (groups = new Map()));
1026
+ const cohort = this.deliveries.get(record);
1027
+ let group = groups.get(cohort);
1028
+ if (!group)
1029
+ groups.set(cohort, (group = { tally: newTally(), settled: [] }));
1030
+ group.settled.push(mergeWhenSettled(group.tally, tally, settled));
1031
+ }
1032
+ /** @internal Report only the uploads each cohort owns, without retaining their records. */
1033
+ reportUploads(late, signal) {
1034
+ const groups = this.deliveryUploads.get(late);
1035
+ this.deliveryUploads.delete(late);
1036
+ for (const [cohort, { settled, tally }] of groups ?? []) {
1037
+ const report = Promise.all(settled).then(() => this.reportOffload(tally, signal, true));
1038
+ this.awaitUploads(report);
1039
+ this.holdDelivery(cohort, report);
1040
+ }
1041
+ }
930
1042
  /** @internal Exporter callback: counts an export's uploads and reports each kind of fallback
931
1043
  * once, as a warning naming its traces: the values were exported cut as before, or, `late`,
932
1044
  * their uploads failed after their records listed them. */
@@ -1147,13 +1259,19 @@ export class HueTransport {
1147
1259
  */
1148
1260
  flushRecords(providers) {
1149
1261
  const from = this.observedSequence;
1150
- const join = () => this.sendRecords();
1262
+ let finish;
1263
+ const join = () => {
1264
+ finish = this.beginSpanDrain();
1265
+ return this.sendRecords();
1266
+ };
1151
1267
  const sent = providers ? this.sendingRecords.run(true, providers).then(join, join) : join();
1152
- const report = sent.then(async (processors) => {
1268
+ const report = sent
1269
+ .then(async (processors) => {
1153
1270
  // Exports the processors did not start (a timer's, still in flight) queued uploads too.
1154
1271
  await this.drainUploads();
1155
1272
  return this.flushReport(from, processors);
1156
- });
1273
+ })
1274
+ .finally(() => finish?.());
1157
1275
  return { sent, report };
1158
1276
  }
1159
1277
  /** Sends what the processors and exporters hold, once the previous flush's send ended. */
@@ -1387,6 +1505,7 @@ class ReportingExporter {
1387
1505
  this.redact = redact;
1388
1506
  }
1389
1507
  export(records, callback) {
1508
+ this.transport.exporting(records);
1390
1509
  const work = this.exportRecords(records)
1391
1510
  .then(() => callback({ code: ExportResultCode.SUCCESS }), () => callback({
1392
1511
  code: ExportResultCode.FAILED,
@@ -1423,23 +1542,16 @@ class ReportingExporter {
1423
1542
  real.push(placeholder);
1424
1543
  return real;
1425
1544
  }
1426
- /** Places and sends the records; the uploads their placement queued are reported once they
1427
- * settle, after the records were sent, never awaited here. */
1545
+ /** Queue final upload reports before releasing the records that own their cohorts. */
1428
1546
  async exportRecords(records) {
1429
1547
  const late = { tally: newTally(), settled: [] };
1430
1548
  try {
1431
1549
  await this.placeAndSend(records, late);
1432
1550
  }
1433
1551
  finally {
1434
- if (late.settled.length)
1435
- this.reportWhenSettled(late);
1552
+ this.transport.reportUploads(late, this.signal);
1436
1553
  }
1437
1554
  }
1438
- /** Reports an export's uploads once they all settle. Built apart from the export, so a pending
1439
- * upload never keeps the export's records alive. */
1440
- reportWhenSettled(late) {
1441
- this.transport.awaitUploads(Promise.all(late.settled).then(() => this.transport.reportOffload(late.tally, this.signal, true)));
1442
- }
1443
1555
  async placeAndSend(records, late) {
1444
1556
  // Completed records and placeholders travel in separate requests, completed records first,
1445
1557
  // so a rejection count is always one kind of record's. Placeholders are advisory: losing one
@@ -1850,7 +1962,10 @@ class ReportingExporter {
1850
1962
  // records' acknowledgements may have just shown, nor after this export's completed records were
1851
1963
  // refused or unanswered: a receiver that just refused or timed out gets no second request with
1852
1964
  // its own retries. A record this side could not send keeps the placeholders going out.
1853
- if (pending.length && !refused && this.transport.sendsPlaceholders())
1965
+ if (pending.length &&
1966
+ !refused &&
1967
+ this.transport.sendsPlaceholders() &&
1968
+ !this.transport.deferPlaceholders(records))
1854
1969
  await pack(pending, true);
1855
1970
  if (failed)
1856
1971
  throw new Error("Hue telemetry export failed");
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /** Package version shared by the instrumentation scope and the export User-Agent. */
2
- export declare const sdkVersion = "0.22.0";
2
+ export declare const sdkVersion = "0.23.0";
3
3
  /** OTLP dependency version sent in the export User-Agent. */
4
4
  export declare const otlpExporterVersion = "0.223.0";
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Generated by scripts/write-version.mjs from package.json; do not edit by hand.
2
2
  /** Package version shared by the instrumentation scope and the export User-Agent. */
3
- export const sdkVersion = "0.22.0";
3
+ export const sdkVersion = "0.23.0";
4
4
  /** OTLP dependency version sent in the export User-Agent. */
5
5
  export const otlpExporterVersion = "0.223.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hue-run/sdk",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -98,6 +98,7 @@
98
98
  "@opentelemetry/resources": "2.12.0",
99
99
  "@opentelemetry/sdk-logs": "0.223.0",
100
100
  "@opentelemetry/sdk-trace": "2.12.0",
101
+ "@vercel/functions": "3.9.7",
101
102
  "zod": "^3.25.76 || ^4.6.5"
102
103
  },
103
104
  "devDependencies": {