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