@kortyx/usage-telemetry 0.3.0 → 0.5.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/dist/node.js CHANGED
@@ -23,8 +23,10 @@ __export(node_exports, {
23
23
  configureUsageTelemetry: () => configureUsageTelemetry,
24
24
  createNodeUsageTelemetry: () => createNodeUsageTelemetry,
25
25
  createUsageTelemetry: () => createUsageTelemetry,
26
+ flushUsageTelemetry: () => flushUsageTelemetry,
26
27
  getUsageTelemetry: () => getUsageTelemetry,
27
28
  publicUsageConfiguration: () => publicUsageConfiguration,
29
+ runtimeUsageContext: () => runtimeUsageContext,
28
30
  scale: () => scale,
29
31
  usageDisabled: () => usageDisabled
30
32
  });
@@ -62,6 +64,8 @@ var operations = [
62
64
  "sdk.checkpoint.rollback",
63
65
  "sdk.export",
64
66
  "studio.setup",
67
+ "studio.sample",
68
+ "studio.help",
65
69
  "studio.connection",
66
70
  "studio.read",
67
71
  "studio.render",
@@ -76,6 +80,11 @@ var operations = [
76
80
  "studio.settings",
77
81
  "studio.update",
78
82
  "studio.ingest",
83
+ "cli.command",
84
+ "studio.eval.execution",
85
+ "studio.diagnostic",
86
+ "studio.table.layout",
87
+ "studio.help",
79
88
  "cli.topology",
80
89
  "cli.eval"
81
90
  ];
@@ -90,9 +99,256 @@ var outcomes = [
90
99
  "ungraded"
91
100
  ];
92
101
  var publicCatalog = {
93
- products: ["sdk", "oss_studio"],
102
+ products: ["sdk", "oss_studio", "cli"],
94
103
  operations,
95
104
  properties: {
105
+ help_action: [
106
+ "open",
107
+ "documentation",
108
+ "start",
109
+ "step",
110
+ "complete",
111
+ "skip",
112
+ "exit",
113
+ "error",
114
+ "example_request",
115
+ "example_accept",
116
+ "example_cancel"
117
+ ],
118
+ help_page: [
119
+ "runs",
120
+ "run",
121
+ "sessions",
122
+ "session",
123
+ "workflows",
124
+ "interrupts",
125
+ "interrupt",
126
+ "prompts",
127
+ "prompt",
128
+ "prompt-groups",
129
+ "prompt-group",
130
+ "evals",
131
+ "eval-suites",
132
+ "eval-suite",
133
+ "evaluation",
134
+ "eval-run",
135
+ "eval-case",
136
+ "eval-comparison",
137
+ "eval-case-comparison",
138
+ "diagnostic",
139
+ "settings",
140
+ "onboarding",
141
+ "other"
142
+ ],
143
+ help_context: ["sample", "real"],
144
+ help_step: [
145
+ "overview",
146
+ "controls",
147
+ "records",
148
+ "tabs",
149
+ "content",
150
+ "navigation",
151
+ "state"
152
+ ],
153
+ sample_action: [
154
+ "select",
155
+ "view",
156
+ "hide",
157
+ "show",
158
+ "tour_start",
159
+ "tour_complete",
160
+ "tour_skip",
161
+ "tour_error",
162
+ "tour_step",
163
+ "tour_hub",
164
+ "tour_resume",
165
+ "tour_exit",
166
+ "tour_full_start",
167
+ "tour_full_complete",
168
+ "tour_full_partial",
169
+ "tour_reset"
170
+ ],
171
+ tour_chapter: [
172
+ "sample-execution",
173
+ "debug-execution",
174
+ "conversation",
175
+ "approvals",
176
+ "prompt-versions",
177
+ "evaluation-results",
178
+ "error-diagnostics",
179
+ "evaluation-progress",
180
+ "personalize"
181
+ ],
182
+ tour_step: [
183
+ "project-selector",
184
+ "sample-run",
185
+ "execution-details",
186
+ "find-failed-run",
187
+ "failure-summary",
188
+ "failure-trace",
189
+ "failure-model-io",
190
+ "session-history",
191
+ "session-runs",
192
+ "workflow-topology",
193
+ "workflow-system",
194
+ "approval-decision",
195
+ "approval-timeline",
196
+ "approval-result",
197
+ "prompt-original",
198
+ "prompt-updated",
199
+ "prompt-diff",
200
+ "suite-definition",
201
+ "evaluation-summary",
202
+ "case-evidence",
203
+ "saved-comparison",
204
+ "diagnostic-exception",
205
+ "diagnostic-capture",
206
+ "diagnostic-context",
207
+ "evaluation-progress-summary",
208
+ "evaluation-grading",
209
+ "evaluation-pending-evidence",
210
+ "appearance",
211
+ "sample-visibility"
212
+ ],
213
+ tour_failure: ["load", "target", "entry"],
214
+ tour_mode: ["chapter", "full"],
215
+ tour_version: ["1"],
216
+ help_placement: ["card", "button"],
217
+ measurement_version: ["2"],
218
+ measurement_kind: [
219
+ "invocation",
220
+ "stage",
221
+ "execution",
222
+ "attempt",
223
+ "chunk",
224
+ "output",
225
+ "update",
226
+ "poll",
227
+ "request",
228
+ "call",
229
+ "feature",
230
+ "observation"
231
+ ],
232
+ traffic_source: ["internal", "synthetic", "external", "unknown"],
233
+ layout_action: [
234
+ "visibility",
235
+ "show_all",
236
+ "reset",
237
+ "apply",
238
+ "reset_width",
239
+ "pin",
240
+ "unpin",
241
+ "resize",
242
+ "reorder"
243
+ ],
244
+ eval_phase: [
245
+ "params",
246
+ "setup",
247
+ "execute",
248
+ "responder",
249
+ "reference",
250
+ "grading",
251
+ "verification",
252
+ "cleanup",
253
+ "reporting"
254
+ ],
255
+ cli_resource: ["evals.runs", "evals.suites"],
256
+ retryable: "boolean",
257
+ retried: "boolean",
258
+ recovered: "boolean",
259
+ retry_attempt: ["1", "2-5", "6-20", "21-100", "101+"],
260
+ batch_size: ["1", "2-5", "6-20", "21-100", "101+"],
261
+ http_status_class: ["1xx", "2xx", "3xx", "4xx", "5xx"],
262
+ cause_category: [
263
+ "configuration",
264
+ "provider",
265
+ "validation",
266
+ "domain",
267
+ "workflow",
268
+ "persistence",
269
+ "request",
270
+ "transport",
271
+ "internal",
272
+ "unknown"
273
+ ],
274
+ cli_command: [
275
+ "studio.start",
276
+ "studio.stop",
277
+ "studio.restart",
278
+ "studio.status",
279
+ "studio.logs",
280
+ "studio.credentials",
281
+ "studio.reset",
282
+ "studio.prompts",
283
+ "studio.evals",
284
+ "studio.runs",
285
+ "studio.sessions",
286
+ "studio.interrupts",
287
+ "studio.workflows",
288
+ "studio.diagnostics",
289
+ "studio.inspect",
290
+ "studio.doctor",
291
+ "studio.catalogs",
292
+ "connections",
293
+ "evals.run",
294
+ "evals.list",
295
+ "topology.push",
296
+ "help",
297
+ "unknown"
298
+ ],
299
+ cli_action: [
300
+ "start",
301
+ "stop",
302
+ "restart",
303
+ "status",
304
+ "logs",
305
+ "credentials",
306
+ "reset",
307
+ "run",
308
+ "list",
309
+ "get",
310
+ "show",
311
+ "inspect",
312
+ "analyze",
313
+ "download",
314
+ "doctor",
315
+ "cancel",
316
+ "compare",
317
+ "create",
318
+ "update",
319
+ "delete",
320
+ "import",
321
+ "export",
322
+ "pull",
323
+ "push",
324
+ "add",
325
+ "remove",
326
+ "use",
327
+ "verify",
328
+ "versions",
329
+ "diff",
330
+ "validate",
331
+ "action",
332
+ "tag",
333
+ "untag",
334
+ "rollback",
335
+ "promote",
336
+ "archive",
337
+ "restore",
338
+ "test",
339
+ "copy",
340
+ "apply",
341
+ "wait",
342
+ "catalogs",
343
+ "help",
344
+ "unknown"
345
+ ],
346
+ dry_run: "boolean",
347
+ json_output: "boolean",
348
+ topology_strict: "boolean",
349
+ settings_override: "boolean",
350
+ case_timeout_override: "boolean",
351
+ run_timeout_override: "boolean",
96
352
  phase: [
97
353
  "admission",
98
354
  "execution",
@@ -237,6 +493,7 @@ var publicCatalog = {
237
493
  "pending",
238
494
  "expired"
239
495
  ],
496
+ match: ["all", "any"],
240
497
  filter: [
241
498
  "search",
242
499
  "status",
@@ -269,6 +526,7 @@ var publicCatalog = {
269
526
  "401",
270
527
  "403",
271
528
  "404",
529
+ "408",
272
530
  "409",
273
531
  "413",
274
532
  "422",
@@ -292,6 +550,48 @@ var publicCatalog = {
292
550
  },
293
551
  // Built-in contracts only; application codes and provider response text are excluded.
294
552
  errorCodes: [
553
+ "UNCLASSIFIED_FAILURE",
554
+ "HTTP_UNAUTHORIZED",
555
+ "HTTP_FORBIDDEN",
556
+ "HTTP_NOT_FOUND",
557
+ "HTTP_TIMEOUT",
558
+ "HTTP_CONFLICT",
559
+ "HTTP_TOO_LARGE",
560
+ "HTTP_RATE_LIMITED",
561
+ "HTTP_SERVER_ERROR",
562
+ "HTTP_INVALID_REQUEST",
563
+ "NETWORK_DNS_FAILED",
564
+ "NETWORK_CONNECTION_REFUSED",
565
+ "NETWORK_CONNECTION_RESET",
566
+ "NETWORK_TIMEOUT",
567
+ "NETWORK_TLS_FAILED",
568
+ "CLI_ARGUMENT_INVALID",
569
+ "CLI_EXIT_FAILED",
570
+ "KTX_TOPOLOGY_INCOMPLETE",
571
+ "STUDIO_NOT_CONFIGURED",
572
+ "STUDIO_RESPONSE_INVALID",
573
+ "EVAL_CONCURRENCY_CHANGED",
574
+ "EVAL_CONSUMER_FAILED",
575
+ "EVAL_CONTRACT_CHANGED",
576
+ "EVAL_DISCOVERY_REJECTED",
577
+ "EVAL_EXECUTION_CONFLICT",
578
+ "EVAL_EXECUTION_STORE_CHANGED",
579
+ "EVAL_EXECUTION_UNKNOWN",
580
+ "EVAL_EXECUTOR_BUSY",
581
+ "EVAL_EXECUTOR_FAILED",
582
+ "EVAL_EXECUTOR_REJECTED",
583
+ "EVAL_EXECUTOR_UNAUTHORIZED",
584
+ "EVAL_GRADING_FAILED",
585
+ "EVAL_GRADING_TIMEOUT",
586
+ "EVAL_JUDGE_UNAVAILABLE",
587
+ "EVAL_LEASE_LOST",
588
+ "EVAL_RETRY_DELAY_EXCEEDED",
589
+ "EVAL_SETUP_TIMEOUT",
590
+ "EVAL_TRANSPORT_UNAVAILABLE",
591
+ "EVAL_VERIFICATION_TIMEOUT",
592
+ "EVAL_RESULT_INVALID",
593
+ "EVAL_RUN_TIMEOUT",
594
+ "EVAL_SETTINGS_INVALID",
295
595
  "EXECUTION_FAILED",
296
596
  "INVALID_REQUEST",
297
597
  "INVALID_RESUME",
@@ -359,8 +659,8 @@ function scale(value) {
359
659
  return value <= 1 ? "1" : value <= 5 ? "2-5" : value <= 20 ? "6-20" : value <= 100 ? "21-100" : "101+";
360
660
  }
361
661
 
362
- // src/client.ts
363
- function property(value, key) {
662
+ // src/failure.ts
663
+ function usageErrorProperty(value, key) {
364
664
  try {
365
665
  if (!value || typeof value !== "object") return void 0;
366
666
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
@@ -369,40 +669,201 @@ function property(value, key) {
369
669
  return void 0;
370
670
  }
371
671
  }
372
- function classifyUsageError(error, catalog = publicCatalog) {
373
- const name = property(error, "name");
374
- const code = property(error, "code") ?? property(property(error, "failure"), "code");
375
- if (name === "AbortError" || code === "EXECUTION_CANCELLED")
672
+ var networkCodes = {
673
+ ENOTFOUND: "NETWORK_DNS_FAILED",
674
+ EAI_AGAIN: "NETWORK_DNS_FAILED",
675
+ ECONNREFUSED: "NETWORK_CONNECTION_REFUSED",
676
+ ECONNRESET: "NETWORK_CONNECTION_RESET",
677
+ EPIPE: "NETWORK_CONNECTION_RESET",
678
+ ETIMEDOUT: "NETWORK_TIMEOUT",
679
+ UND_ERR_CONNECT_TIMEOUT: "NETWORK_TIMEOUT",
680
+ UND_ERR_HEADERS_TIMEOUT: "NETWORK_TIMEOUT",
681
+ UND_ERR_BODY_TIMEOUT: "NETWORK_TIMEOUT",
682
+ CERT_HAS_EXPIRED: "NETWORK_TLS_FAILED",
683
+ DEPTH_ZERO_SELF_SIGNED_CERT: "NETWORK_TLS_FAILED",
684
+ UNABLE_TO_VERIFY_LEAF_SIGNATURE: "NETWORK_TLS_FAILED"
685
+ };
686
+ function classifyUsageHttpStatus(status) {
687
+ if (!Number.isInteger(status) || status < 100 || status > 599)
688
+ return {
689
+ outcome: "failed",
690
+ properties: {
691
+ error_code: "UNCLASSIFIED_FAILURE",
692
+ error_category: "unknown"
693
+ }
694
+ };
695
+ if (status < 400)
696
+ return {
697
+ outcome: "success",
698
+ properties: { http_status_class: `${Math.floor(status / 100)}xx` }
699
+ };
700
+ const code = status === 401 ? "HTTP_UNAUTHORIZED" : status === 403 ? "HTTP_FORBIDDEN" : status === 404 ? "HTTP_NOT_FOUND" : status === 408 || status === 504 ? "HTTP_TIMEOUT" : status === 409 ? "HTTP_CONFLICT" : status === 413 ? "HTTP_TOO_LARGE" : status === 429 ? "HTTP_RATE_LIMITED" : status >= 500 ? "HTTP_SERVER_ERROR" : "HTTP_INVALID_REQUEST";
701
+ return {
702
+ outcome: "failed",
703
+ properties: {
704
+ error_code: code,
705
+ error_category: status >= 500 || status === 408 ? "transport" : "request",
706
+ http_status: String(status),
707
+ http_status_class: `${Math.floor(status / 100)}xx`,
708
+ retryable: status === 408 || status === 429 || status >= 500
709
+ }
710
+ };
711
+ }
712
+ function nativeExceptionName(value) {
713
+ try {
714
+ if (typeof DOMException !== "undefined")
715
+ return Object.getOwnPropertyDescriptor(
716
+ DOMException.prototype,
717
+ "name"
718
+ )?.get?.call(value);
719
+ } catch {
720
+ }
721
+ return void 0;
722
+ }
723
+ function classifyOne(error, catalog) {
724
+ const read = (key) => usageErrorProperty(error, key);
725
+ let name = read("name") ?? nativeExceptionName(error);
726
+ try {
727
+ if (name === void 0 && error instanceof SyntaxError)
728
+ name = "SyntaxError";
729
+ } catch {
730
+ }
731
+ const code = read("code");
732
+ if (name === "AbortError" || code === "EXECUTION_CANCELLED" || code === "ABORT_ERR")
376
733
  return { outcome: "cancelled", properties: {} };
377
734
  if (code === "EXECUTION_LIMIT_REACHED" || name === "ExecutionLimitReached")
378
735
  return { outcome: "limit_reached", properties: {} };
379
- if (name === "GraphInterrupt" || name === "NodeInterrupt" || name === "Interrupt")
736
+ if (typeof name === "string" && [
737
+ "GraphInterrupt",
738
+ "NodeInterrupt",
739
+ "Interrupt",
740
+ "ParallelChildWaiting"
741
+ ].includes(name))
380
742
  return { outcome: "suspended", properties: {} };
381
- const known = typeof code === "string" && catalog.errorCodes.includes(code);
382
- const category = known && code !== "EXECUTION_FAILED" ? property(error, "category") ?? property(property(error, "failure"), "category") ?? "unknown" : "unknown";
383
- const status = property(error, "status") ?? property(property(error, "failure"), "status");
384
- return {
385
- outcome: "failed",
386
- properties: {
387
- error_code: known ? code : "other",
388
- error_category: category,
389
- ...typeof status === "number" ? { http_status: String(status) } : {}
743
+ if (name === "EvalConfigurationError")
744
+ return {
745
+ outcome: "failed",
746
+ properties: {
747
+ error_code: "EVAL_SETTINGS_INVALID",
748
+ error_category: "configuration",
749
+ retryable: false
750
+ }
751
+ };
752
+ if (name === "TimeoutError")
753
+ return {
754
+ outcome: "failed",
755
+ properties: {
756
+ error_code: "NETWORK_TIMEOUT",
757
+ error_category: "transport",
758
+ retryable: true
759
+ }
760
+ };
761
+ if (typeof code === "string" && Object.hasOwn(networkCodes, code))
762
+ return {
763
+ outcome: "failed",
764
+ properties: {
765
+ error_code: networkCodes[code] ?? "UNCLASSIFIED_FAILURE",
766
+ error_category: "transport",
767
+ retryable: true
768
+ }
769
+ };
770
+ if (typeof code === "string" && catalog.errorCodes.includes(code)) {
771
+ const failure = usageErrorProperty(error, "failure");
772
+ const category = read("category") ?? usageErrorProperty(failure, "category");
773
+ const retryable = read("retryable") ?? usageErrorProperty(failure, "retryable");
774
+ const status2 = read("status") ?? usageErrorProperty(failure, "status");
775
+ return {
776
+ outcome: "failed",
777
+ properties: {
778
+ ...typeof status2 === "number" ? classifyUsageHttpStatus(status2).properties : {},
779
+ // The framework code is more specific than its HTTP projection.
780
+ error_code: code,
781
+ error_category: code !== "EXECUTION_FAILED" && typeof category === "string" && Array.isArray(catalog.properties.error_category) && catalog.properties.error_category.includes(category) ? category : "unknown",
782
+ ...typeof retryable === "boolean" ? { retryable } : {}
783
+ }
784
+ };
785
+ }
786
+ const status = read("status") ?? read("statusCode");
787
+ if (typeof status === "number" && Number.isInteger(status) && status >= 400 && status <= 599)
788
+ return classifyUsageHttpStatus(status);
789
+ if (name === "ZodError" || name === "SyntaxError")
790
+ return {
791
+ outcome: "failed",
792
+ properties: {
793
+ error_code: "SCHEMA_VALIDATION",
794
+ error_category: "validation"
795
+ }
796
+ };
797
+ return void 0;
798
+ }
799
+ function classifyUsageError(error, catalog = publicCatalog) {
800
+ const seen = /* @__PURE__ */ new Set();
801
+ const pending = [{ value: error, depth: 0 }];
802
+ let result;
803
+ while (pending.length && seen.size < 8) {
804
+ const current = pending.shift();
805
+ if (!current || seen.has(current.value)) continue;
806
+ seen.add(current.value);
807
+ const found = classifyOne(current.value, catalog);
808
+ if (found) {
809
+ if (!result) result = found;
810
+ else if (found.properties.error_code && found.outcome === "failed" && found.properties.error_code !== result.properties.error_code) {
811
+ result.properties.cause_code = found.properties.error_code;
812
+ result.properties.cause_category = found.properties.error_category ?? "unknown";
813
+ if (result.properties.retryable === void 0 && typeof found.properties.retryable === "boolean")
814
+ result.properties.retryable = found.properties.retryable;
815
+ break;
816
+ }
817
+ if (result === found && found.outcome !== "failed") return found;
390
818
  }
819
+ if (current.depth < 3)
820
+ for (const key of ["failure", "cause", "error"]) {
821
+ const value = usageErrorProperty(current.value, key);
822
+ if (value && typeof value === "object")
823
+ pending.push({ value, depth: current.depth + 1 });
824
+ }
825
+ }
826
+ return result ?? {
827
+ outcome: "failed",
828
+ properties: { error_code: "other", error_category: "unknown" }
391
829
  };
392
830
  }
831
+
832
+ // src/measurement.ts
833
+ function usageMeasurement(event, operation, input) {
834
+ if (event === "usage.feature")
835
+ return usageErrorProperty(input, "origin") === "background" || usageErrorProperty(input, "origin") === "server" ? "observation" : "feature";
836
+ if (operation === "cli.command" || operation === "sdk.eval")
837
+ return "invocation";
838
+ if (operation === "studio.setup") return "stage";
839
+ if (operation === "sdk.execution" || operation === "studio.eval.execution")
840
+ return "execution";
841
+ if (operation === "sdk.export" || operation === "sdk.model") return "attempt";
842
+ if (operation === "sdk.structured") {
843
+ const format = usageErrorProperty(input, "format");
844
+ return format === "text-delta" ? "chunk" : format === "final" ? "output" : "update";
845
+ }
846
+ if (usageErrorProperty(input, "origin") === "background") return "poll";
847
+ if (operation.startsWith("studio.")) return "request";
848
+ return "call";
849
+ }
850
+
851
+ // src/client.ts
393
852
  function sanitizeProperties(input, catalog = publicCatalog) {
394
853
  const safe = {};
395
854
  for (const [key, rule] of Object.entries(catalog.properties)) {
396
- const value = property(input, key);
855
+ const value = usageErrorProperty(input, key);
397
856
  if (value === void 0) continue;
398
857
  if (rule === "boolean") {
399
858
  if (typeof value === "boolean") safe[key] = value;
400
859
  } else if (typeof value === "string")
401
860
  safe[key] = rule.includes(value) ? value : "other";
402
861
  }
403
- const code = property(input, "error_code");
404
- if (typeof code === "string")
405
- safe.error_code = catalog.errorCodes.includes(code) ? code : "other";
862
+ for (const key of ["error_code", "cause_code"]) {
863
+ const code = usageErrorProperty(input, key);
864
+ if (typeof code === "string")
865
+ safe[key] = catalog.errorCodes.includes(code) ? code : "other";
866
+ }
406
867
  return safe;
407
868
  }
408
869
  var durationBounds = [10, 100, 1e3, 1e4, 6e4];
@@ -423,6 +884,16 @@ function createUsageTelemetry(options) {
423
884
  let disposed = false;
424
885
  let dropped = 0;
425
886
  let delivered = 0;
887
+ let lostObservations = 0;
888
+ let failedBatches = 0;
889
+ let reportedLoss = 0;
890
+ let reportedFailures = 0;
891
+ const lose = (count) => {
892
+ lostObservations = Math.min(
893
+ Number.MAX_SAFE_INTEGER,
894
+ lostObservations + count
895
+ );
896
+ };
426
897
  let hour = Math.floor(now() / 36e5);
427
898
  let sent = 0;
428
899
  const active = () => {
@@ -458,6 +929,10 @@ function createUsageTelemetry(options) {
458
929
  pending.clear();
459
930
  if (!records.length) return;
460
931
  let attempted = records.length;
932
+ let attemptedObservations = records.reduce(
933
+ (total, record2) => total + record2.count,
934
+ 0
935
+ );
461
936
  flight = (async () => {
462
937
  try {
463
938
  if (options.debug?.()) {
@@ -475,7 +950,14 @@ function createUsageTelemetry(options) {
475
950
  );
476
951
  const batch = records.slice(0, allowance);
477
952
  dropped += records.length - batch.length;
953
+ lose(
954
+ records.slice(allowance).reduce((total, record2) => total + record2.count, 0)
955
+ );
478
956
  attempted = batch.length;
957
+ attemptedObservations = batch.reduce(
958
+ (total, record2) => total + record2.count,
959
+ 0
960
+ );
479
961
  if (!batch.length || !active()) return;
480
962
  const identified = await options.identity();
481
963
  const identity2 = typeof identified === "string" ? identified : identified.id;
@@ -486,13 +968,25 @@ function createUsageTelemetry(options) {
486
968
  identity2
487
969
  )) {
488
970
  dropped += batch.length;
971
+ lose(attemptedObservations);
489
972
  return;
490
973
  }
491
974
  sent += batch.length;
975
+ const lossSnapshot = lostObservations;
976
+ const failureSnapshot = failedBatches;
977
+ if (batch[0] && (lossSnapshot > reportedLoss || failureSnapshot > reportedFailures))
978
+ batch[0].delivery_health = {
979
+ dropped_observations: lossSnapshot - reportedLoss,
980
+ failed_batches: failureSnapshot - reportedFailures
981
+ };
492
982
  await options.transport(batch, identity2);
983
+ reportedLoss = lossSnapshot;
984
+ reportedFailures = failureSnapshot;
493
985
  delivered += batch.length;
494
986
  } catch {
495
987
  dropped += attempted;
988
+ lose(attemptedObservations);
989
+ failedBatches = Math.min(Number.MAX_SAFE_INTEGER, failedBatches + 1);
496
990
  }
497
991
  })().finally(() => {
498
992
  flight = void 0;
@@ -516,12 +1010,29 @@ function createUsageTelemetry(options) {
516
1010
  }
517
1011
  if (!configured || !catalog.operations.includes(operation) || !outcomes.includes(outcome))
518
1012
  return;
519
- const properties = sanitizeProperties(input, catalog);
1013
+ const failed = outcome === "failed";
1014
+ const status = usageErrorProperty(input, "http_status");
1015
+ const http = typeof status === "string" && /^\d{3}$/.test(status) ? classifyUsageHttpStatus(Number(status)).properties : {};
1016
+ const properties = sanitizeProperties(
1017
+ {
1018
+ measurement_version: "2",
1019
+ measurement_kind: usageMeasurement(event, operation, input),
1020
+ ...failed ? {
1021
+ error_code: "UNCLASSIFIED_FAILURE",
1022
+ error_category: "unknown",
1023
+ ...http
1024
+ } : {},
1025
+ ...sanitizeProperties(input, catalog),
1026
+ traffic_source: options.context.trafficSource ?? "unknown"
1027
+ },
1028
+ catalog
1029
+ );
520
1030
  const key = JSON.stringify([event, operation, outcome, properties]);
521
1031
  let existing = pending.get(key);
522
1032
  if (!existing) {
523
1033
  if (pending.size >= (options.maxGroups ?? 64)) {
524
1034
  dropped++;
1035
+ lose(1);
525
1036
  return;
526
1037
  }
527
1038
  existing = {
@@ -533,7 +1044,9 @@ function createUsageTelemetry(options) {
533
1044
  component: options.context.component,
534
1045
  version: options.context.version,
535
1046
  analytics_environment: options.context.environment ?? "production",
536
- runtime_mode: options.context.runtimeMode ?? "unknown",
1047
+ runtime_mode: ["development", "production", "ci"].includes(
1048
+ options.context.runtimeMode ?? ""
1049
+ ) ? options.context.runtimeMode ?? "unknown" : "unknown",
537
1050
  identity_scope: options.context.identityScope ?? "installation",
538
1051
  operation,
539
1052
  outcome,
@@ -578,8 +1091,11 @@ function createUsageTelemetry(options) {
578
1091
  if (ended) return;
579
1092
  ended = true;
580
1093
  try {
1094
+ const status = usageErrorProperty(extra, "http_status");
1095
+ const http = outcome === "failed" && typeof status === "string" && /^\d{3}$/.test(status) ? classifyUsageHttpStatus(Number(status)).properties : {};
581
1096
  record("usage.operation", operation, outcome, now() - began, {
582
1097
  ...initial,
1098
+ ...http,
583
1099
  ...sanitizeProperties(extra, catalog)
584
1100
  });
585
1101
  } catch {
@@ -666,6 +1182,10 @@ function createPostHogTransport(options) {
666
1182
  count: record.count,
667
1183
  duration_total_ms: record.duration_total_ms,
668
1184
  duration_buckets: record.duration_buckets,
1185
+ ...record.delivery_health ? {
1186
+ usage_dropped_observations: record.delivery_health.dropped_observations,
1187
+ usage_failed_batches: record.delivery_health.failed_batches
1188
+ } : {},
669
1189
  $process_person_profile: false,
670
1190
  $geoip_disable: true,
671
1191
  $ip: null
@@ -761,6 +1281,13 @@ async function installationIdentity() {
761
1281
  })();
762
1282
  return identity;
763
1283
  }
1284
+ function runtimeUsageContext() {
1285
+ const traffic = process.env.KORTYX_USAGE_TELEMETRY_TRAFFIC_SOURCE;
1286
+ return {
1287
+ runtimeMode: process.env.CI && !["0", "false"].includes(process.env.CI.toLowerCase()) ? "ci" : process.env.NODE_ENV === "production" ? "production" : process.env.NODE_ENV === "development" ? "development" : "unknown",
1288
+ trafficSource: traffic === "internal" || traffic === "synthetic" || traffic === "external" ? traffic : "unknown"
1289
+ };
1290
+ }
764
1291
  function usageDisabled() {
765
1292
  return ["1", "true"].includes(process.env.KORTYX_USAGE_TELEMETRY_DISABLED ?? "") || process.env.NODE_ENV === "test";
766
1293
  }
@@ -784,6 +1311,11 @@ function createNodeUsageTelemetry(options) {
784
1311
  }
785
1312
  return createUsageTelemetry({
786
1313
  ...options,
1314
+ context: {
1315
+ ...options.context,
1316
+ runtimeMode: options.context.runtimeMode ?? runtimeUsageContext().runtimeMode,
1317
+ trafficSource: options.context.trafficSource ?? runtimeUsageContext().trafficSource
1318
+ },
787
1319
  transport,
788
1320
  identity: options.identity ?? installationIdentity,
789
1321
  enabled: () => valid && !usageDisabled() && (options.enabled?.() ?? true),
@@ -792,6 +1324,9 @@ function createNodeUsageTelemetry(options) {
792
1324
  });
793
1325
  }
794
1326
  var clients = /* @__PURE__ */ new Map();
1327
+ async function flushUsageTelemetry() {
1328
+ await Promise.all([...clients.values()].map((client) => client.flush()));
1329
+ }
795
1330
  function configureUsageTelemetry(component, client) {
796
1331
  if (clients.get(component) === client) return;
797
1332
  clients.get(component)?.dispose();
@@ -806,7 +1341,7 @@ function getUsageTelemetry(component, version = "unknown", product = "sdk") {
806
1341
  component,
807
1342
  version,
808
1343
  environment: process.env.KORTYX_USAGE_TELEMETRY_ENVIRONMENT === "staging" ? "staging" : "production",
809
- runtimeMode: process.env.CI ? "ci" : process.env.NODE_ENV === "production" ? "production" : process.env.NODE_ENV === "development" ? "development" : "unknown"
1344
+ ...runtimeUsageContext()
810
1345
  }
811
1346
  });
812
1347
  clients.set(component, client);
@@ -832,6 +1367,7 @@ function publicUsageConfiguration() {
832
1367
  }
833
1368
  return {
834
1369
  enabled: valid && !usageDisabled(),
1370
+ ...runtimeUsageContext(),
835
1371
  debug: process.env.KORTYX_USAGE_TELEMETRY_DEBUG === "1",
836
1372
  environment: process.env.KORTYX_USAGE_TELEMETRY_ENVIRONMENT === "staging" ? "staging" : "production",
837
1373
  // A mistakenly supplied personal token or credential-bearing URL must never reach HTML.
@@ -845,8 +1381,10 @@ function publicUsageConfiguration() {
845
1381
  configureUsageTelemetry,
846
1382
  createNodeUsageTelemetry,
847
1383
  createUsageTelemetry,
1384
+ flushUsageTelemetry,
848
1385
  getUsageTelemetry,
849
1386
  publicUsageConfiguration,
1387
+ runtimeUsageContext,
850
1388
  scale,
851
1389
  usageDisabled
852
1390
  });