@capxul/observability 4.20.0-beta.36 → 4.20.0-beta.37

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/README.md CHANGED
@@ -201,3 +201,6 @@ Set `privacy: "minimal"` for hosts that must export only closed operation/error
201
201
  codes, timings, environment, and validated correlation. This opt-in removes
202
202
  free-form logs, provider messages, URLs, bodies, and source paths, including
203
203
  span exception events. Default consumers retain the existing redaction policy.
204
+ `minimalCorrelationId` is that projection's correlation rule: a host that shows
205
+ a correlation id to a person checks it first, so the id it shows is one the
206
+ export carries.
@@ -7,6 +7,7 @@ export declare const CLI_ENGINEERING_LIMITS: Readonly<{
7
7
  records: 80;
8
8
  ordinaryRecords: 64;
9
9
  recordBytes: 16384;
10
+ recordFields: 48;
10
11
  requests: 5;
11
12
  ordinaryRequests: 2;
12
13
  requestBytes: 1572864;
@@ -77,6 +78,14 @@ export declare const logEngineeringInfo: (message: string, attributes: Readonly<
77
78
  /** Preserve domain exits while preventing the OTLP serializer from seeing raw causes. */
78
79
  export declare const makeLeakSafeEngineeringTracer: (delegate: Tracer.Tracer, enabled?: () => boolean) => Tracer.Tracer;
79
80
  //#endregion
81
+ //#region src/engineering/minimal.d.ts
82
+ /**
83
+ * A correlation id the minimal export carries: an identifier with no `/`, not
84
+ * `0x` hex, and unchanged by the secret mask. The CLI quotes a backend
85
+ * correlation id as its `ref` only when it passes, so the ref finds a row.
86
+ */
87
+ export declare const minimalCorrelationId: (value: unknown) => value is string;
88
+ //#endregion
80
89
  //#region src/engineering.d.ts
81
90
  /** Shared browser/server OTLP layer. OtlpLogger merges with incumbent loggers once. */
82
91
  export declare const makeEngineeringTelemetryLayer: (config: EngineeringTelemetryConfig) => Layer.Layer<EngineeringLog | OtlpExporter.Flusher, never, never>;
@@ -1,4 +1,4 @@
1
- import { G as CLI_COMMAND_NAMES, K as CLI_INVOCATION_KINDS, W as CLI_CAPABILITIES, _n as redactSecrets, bn as CAPXUL_ERROR_CODES, gn as ORGANIZATION_SETUP_STATUSES, hn as ORGANIZATION_SETUP_REASONS, pn as isCapxulOperation, vn as redactUrlSecrets, xn as FAILURE_MODES, yn as isCredentialField } from "./src-B2XswhQh.mjs";
1
+ import { Cn as PROVIDERS, En as FAILURE_MODES, G as CLI_COMMAND_NAMES, K as CLI_INVOCATION_KINDS, Sn as CAPXUL_ERROR_CODES, Tn as isCapxulError, W as CLI_CAPABILITIES, _n as ORGANIZATION_SETUP_REASONS, bn as redactUrlSecrets, hn as isCapxulOperation, q as FAILURE_CLASSES, vn as ORGANIZATION_SETUP_STATUSES, wn as PROVIDER_PROBLEMS, xn as isCredentialField, yn as redactSecrets } from "./src-CkM01SMk.mjs";
2
2
  import { Cause, Clock, Context, Deferred, Duration, Effect, Exit, FiberSet, Layer, LogLevel, Logger, Metric, Option, References, Schedule, Stream, Tracer } from "effect";
3
3
  import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientError, HttpClientResponse } from "effect/http";
4
4
  import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/observability";
@@ -12,6 +12,123 @@ function readField(value, key) {
12
12
  return isRecord(value) ? value[key] : void 0;
13
13
  }
14
14
  //#endregion
15
+ //#region src/delivery/budget.ts
16
+ /** Fixed admission limits for one CLI invocation and its SDK client. */
17
+ const CLI_ENGINEERING_LIMITS = Object.freeze({
18
+ records: 80,
19
+ ordinaryRecords: 64,
20
+ recordBytes: 16384,
21
+ recordFields: 48,
22
+ requests: 5,
23
+ ordinaryRequests: 2,
24
+ requestBytes: 1572864,
25
+ metricSeries: 16,
26
+ requestTimeoutMs: 1e3
27
+ });
28
+ const metricNames = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
29
+ const metricValues = {
30
+ command: CLI_COMMAND_NAMES,
31
+ capability: CLI_CAPABILITIES,
32
+ invocation_kind: CLI_INVOCATION_KINDS,
33
+ outcome: [
34
+ "succeeded",
35
+ "refused",
36
+ "failed",
37
+ "cancelled"
38
+ ],
39
+ capxul_env: [
40
+ "development",
41
+ "staging",
42
+ "production",
43
+ "local"
44
+ ],
45
+ unit: [
46
+ "1",
47
+ "s",
48
+ "ms"
49
+ ]
50
+ };
51
+ /** Only this bounded registry is supplied to the CLI's metric producers. */
52
+ var CliMetricRegistry = class extends Map {
53
+ dropped;
54
+ enabled;
55
+ constructor(dropped, enabled = true) {
56
+ super();
57
+ this.dropped = dropped;
58
+ this.enabled = enabled;
59
+ }
60
+ set(key, entry) {
61
+ if (!this.enabled || !metricNames.has(entry.id) || !this.has(key) && this.size >= CLI_ENGINEERING_LIMITS.metricSeries) {
62
+ this.dropped();
63
+ return this;
64
+ }
65
+ const labels = Object.entries(entry.attributes ?? {});
66
+ const buckets = readField(entry.hooks.get(Context.empty()), "buckets");
67
+ if (!["Counter", "Histogram"].includes(entry.type) || (entry.description?.length ?? 0) > 128 || labels.length > 6 || labels.some(([name, value]) => !metricValues[name]?.includes(value)) || entry.type === "Histogram" && (!Array.isArray(buckets) || buckets.length > 32)) {
68
+ this.dropped();
69
+ return this;
70
+ }
71
+ return super.set(key, entry);
72
+ }
73
+ };
74
+ /** Shared, closure-owned admission state; no queue or mutable counters are exposed. */
75
+ function makeCliEngineeringBudget() {
76
+ let records = 0, peakRecords = 0, activeRequests = 0, requestBytes = 0;
77
+ let peakRequests = 0, peakRequestBytes = 0, dropped = 0;
78
+ const drop = () => {
79
+ dropped += 1;
80
+ };
81
+ const metricRegistry = new CliMetricRegistry(drop);
82
+ return Object.freeze({
83
+ metricRegistry,
84
+ emptyMetricRegistry: () => new CliMetricRegistry(drop, false),
85
+ drop,
86
+ admitRecord(terminal) {
87
+ if (records >= (terminal ? CLI_ENGINEERING_LIMITS.records : CLI_ENGINEERING_LIMITS.ordinaryRecords)) {
88
+ drop();
89
+ return;
90
+ }
91
+ records += 1;
92
+ peakRecords = Math.max(peakRecords, records);
93
+ let released = false;
94
+ return () => {
95
+ if (!released) {
96
+ released = true;
97
+ records -= 1;
98
+ }
99
+ };
100
+ },
101
+ admitRequest(bytes, terminal) {
102
+ if (bytes > CLI_ENGINEERING_LIMITS.requestBytes || activeRequests >= (terminal ? CLI_ENGINEERING_LIMITS.requests : CLI_ENGINEERING_LIMITS.ordinaryRequests)) {
103
+ drop();
104
+ return;
105
+ }
106
+ activeRequests += 1;
107
+ requestBytes += bytes;
108
+ peakRequests = Math.max(peakRequests, activeRequests);
109
+ peakRequestBytes = Math.max(peakRequestBytes, requestBytes);
110
+ return () => {
111
+ activeRequests -= 1;
112
+ requestBytes -= bytes;
113
+ };
114
+ },
115
+ snapshot() {
116
+ return {
117
+ records,
118
+ reservedRecordBytes: records * CLI_ENGINEERING_LIMITS.recordBytes,
119
+ peakRecords,
120
+ peakReservedRecordBytes: peakRecords * CLI_ENGINEERING_LIMITS.recordBytes,
121
+ activeRequests,
122
+ requestBytes,
123
+ peakRequests,
124
+ peakRequestBytes,
125
+ dropped,
126
+ metricSeries: metricRegistry.size
127
+ };
128
+ }
129
+ });
130
+ }
131
+ //#endregion
15
132
  //#region src/delivery/admission.ts
16
133
  function newDeliveryState(budget, ownsMetrics) {
17
134
  return {
@@ -55,7 +172,7 @@ function closeDelivery(state) {
55
172
  if (state.ownsMetrics) state.budget.metricRegistry.clear();
56
173
  }
57
174
  const text = (value) => typeof value === "string" ? value.slice(0, 128) : "capxul.engineering";
58
- const attributes = (input, limit = 32) => {
175
+ const attributes = (input, limit = CLI_ENGINEERING_LIMITS.recordFields) => {
59
176
  const out = {};
60
177
  let count = 0;
61
178
  let bytes = 0;
@@ -75,6 +192,73 @@ const attributes = (input, limit = 32) => {
75
192
  };
76
193
  const terminalName = (name) => name === "cli.command.completed" || name === "cli.wallet.step" || CLI_COMMAND_NAMES.some((command) => name === `cli.command.${command}`);
77
194
  //#endregion
195
+ //#region ../protocol/src/errors/identifier-mask.ts
196
+ /**
197
+ * A typed prefix over a 64-hex digest, such as `money_execution_<digest>`. A
198
+ * private key has the same 64 hex characters, so only Capxul's own id
199
+ * prefixes make the digest public (docs/rules/redaction.md); any other prefix
200
+ * keeps it masked.
201
+ */
202
+ const PREFIXED_DIGEST = /\b[a-z]+(?:_[a-z]+)*_[0-9a-f]{64}\b/gu;
203
+ /**
204
+ * The prefixes of Capxul's ids over a 64-hex digest. A client needs the id to
205
+ * recover. A new id of this shape is added here, or its digest is masked at
206
+ * the wire.
207
+ */
208
+ const CAPXUL_ID_PREFIXES = /* @__PURE__ */ new Set([
209
+ "invitation",
210
+ "money_execution",
211
+ "payment_command",
212
+ "payroll_run",
213
+ "permission_assignment",
214
+ "permission_command",
215
+ "permission_command_invitation",
216
+ "permission_invite"
217
+ ]);
218
+ /** Marks an id while the text is masked: private-use characters no mask matches. */
219
+ const ID_MARK = /\uE000(\d+)\uE001/gu;
220
+ /**
221
+ * `redactSecrets` with Capxul identifiers kept whole. Each id is set aside
222
+ * first, so a credential label still masks whatever value it names, id-shaped
223
+ * or not; only an unlabelled id comes back.
224
+ */
225
+ function maskKeepingIdentifiers(text, source) {
226
+ const ids = [];
227
+ const marked = text.replace(PREFIXED_DIGEST, (id) => {
228
+ if (!CAPXUL_ID_PREFIXES.has(id.slice(0, -65))) return id;
229
+ ids.push(id);
230
+ return `\uE000${String(ids.length - 1)}\uE001`;
231
+ });
232
+ const masked = redactSecrets(marked, source);
233
+ return ids.length === 0 ? masked : masked.replace(ID_MARK, (_, index) => ids[Number(index)] ?? "");
234
+ }
235
+ //#endregion
236
+ //#region ../protocol/src/errors/serialize.ts
237
+ /** Evidence strings are bounded: a provider body cannot inflate a failure. */
238
+ const EVIDENCE_TEXT_MAX = 2e3;
239
+ const UNPRINTABLE = "[unprintable]";
240
+ /**
241
+ * The masked, bounded message of a failure. A factory puts the field name
242
+ * after the words that describe it (`signature is invalid: …`), never before a
243
+ * colon, so the prose mask cannot read the field as a credential label and cut
244
+ * the next word (CAP-441). The message is masked whole, so a second mask on
245
+ * any surface leaves it as it is. A trace span passes its RPC upstream as
246
+ * `source`, so a key in that upstream's URL is masked too.
247
+ */
248
+ function maskedMessage(error, source) {
249
+ try {
250
+ return boundedText(maskKeepingIdentifiers(error.message, source));
251
+ } catch {
252
+ return UNPRINTABLE;
253
+ }
254
+ }
255
+ /** Text `boundedText` already cut: a second serialize (MCP after the SDK) keeps it as it is. */
256
+ const BOUNDED = new RegExp(`^[\\s\\S]{0,${String(EVIDENCE_TEXT_MAX)}}…\\(\\d{1,7} chars\\)$`);
257
+ function boundedText(masked) {
258
+ if (masked.length <= EVIDENCE_TEXT_MAX || BOUNDED.test(masked)) return masked;
259
+ return `${masked.slice(0, EVIDENCE_TEXT_MAX)}…(${String(masked.length)} chars)`;
260
+ }
261
+ //#endregion
78
262
  //#region src/engineering/tracer.ts
79
263
  const failureText = (failure, key, limit, source) => {
80
264
  if (failure === null || typeof failure !== "object") return void 0;
@@ -92,8 +276,20 @@ const failureText = (failure, key, limit, source) => {
92
276
  return;
93
277
  }
94
278
  };
279
+ /**
280
+ * A `CapxulError` reads as its failure row reads (`maskedMessage`), so a
281
+ * factory message keeps the word after its field name (CAP-427). The row
282
+ * reads the same error, so the exporter reads nothing more than the row does.
283
+ */
284
+ const capxulMessage = (failure, source) => {
285
+ try {
286
+ return isCapxulError(failure) ? maskedMessage(failure, source).slice(0, 2048) : void 0;
287
+ } catch {
288
+ return;
289
+ }
290
+ };
95
291
  const exportFailure = (failure, source) => {
96
- const message = failureText(failure, "message", 2048, source) ?? (typeof failure === "string" ? redactSecrets(failure, source).slice(0, 2048) : "Engineering operation failed");
292
+ const message = capxulMessage(failure, source) ?? failureText(failure, "message", 2048, source) ?? (typeof failure === "string" ? redactSecrets(failure, source).slice(0, 2048) : "Engineering operation failed");
97
293
  const code = failureText(failure, "code", 128);
98
294
  const error = new Error((code === void 0 ? message : `${message} [code=${code}]`).slice(0, 2048));
99
295
  error.name = failureText(failure, "_tag", 128) ?? failureText(failure, "name", 128) ?? "Error";
@@ -244,7 +440,10 @@ function admittedTracer(state, delegate) {
244
440
  ...delegate.context === void 0 ? {} : { context: delegate.context.bind(delegate) }
245
441
  });
246
442
  }
247
- /** The span as a producer sees it: at most 32 fields and 2000 attribute bytes, 2 events, 2 links. */
443
+ /**
444
+ * The span as a producer sees it: at most `recordFields` fields and 2000
445
+ * attribute bytes, 2 events, 2 links.
446
+ */
248
447
  function boundedSpan(state, span) {
249
448
  const { budget } = state;
250
449
  const wrapped = inheritSpan(span);
@@ -252,7 +451,7 @@ function boundedSpan(state, span) {
252
451
  let ended = false;
253
452
  Object.defineProperties(wrapped, {
254
453
  attribute: { value: (key, value) => {
255
- if (fields >= 32 && !span.attributes.has(key)) {
454
+ if (fields >= CLI_ENGINEERING_LIMITS.recordFields && !span.attributes.has(key)) {
256
455
  budget.drop();
257
456
  return;
258
457
  }
@@ -301,122 +500,6 @@ function boundedSpan(state, span) {
301
500
  return wrapped;
302
501
  }
303
502
  //#endregion
304
- //#region src/delivery/budget.ts
305
- /** Fixed admission limits for one CLI invocation and its SDK client. */
306
- const CLI_ENGINEERING_LIMITS = Object.freeze({
307
- records: 80,
308
- ordinaryRecords: 64,
309
- recordBytes: 16384,
310
- requests: 5,
311
- ordinaryRequests: 2,
312
- requestBytes: 1572864,
313
- metricSeries: 16,
314
- requestTimeoutMs: 1e3
315
- });
316
- const metricNames = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
317
- const metricValues = {
318
- command: CLI_COMMAND_NAMES,
319
- capability: CLI_CAPABILITIES,
320
- invocation_kind: CLI_INVOCATION_KINDS,
321
- outcome: [
322
- "succeeded",
323
- "refused",
324
- "failed",
325
- "cancelled"
326
- ],
327
- capxul_env: [
328
- "development",
329
- "staging",
330
- "production",
331
- "local"
332
- ],
333
- unit: [
334
- "1",
335
- "s",
336
- "ms"
337
- ]
338
- };
339
- /** Only this bounded registry is supplied to the CLI's metric producers. */
340
- var CliMetricRegistry = class extends Map {
341
- dropped;
342
- enabled;
343
- constructor(dropped, enabled = true) {
344
- super();
345
- this.dropped = dropped;
346
- this.enabled = enabled;
347
- }
348
- set(key, entry) {
349
- if (!this.enabled || !metricNames.has(entry.id) || !this.has(key) && this.size >= CLI_ENGINEERING_LIMITS.metricSeries) {
350
- this.dropped();
351
- return this;
352
- }
353
- const labels = Object.entries(entry.attributes ?? {});
354
- const buckets = readField(entry.hooks.get(Context.empty()), "buckets");
355
- if (!["Counter", "Histogram"].includes(entry.type) || (entry.description?.length ?? 0) > 128 || labels.length > 6 || labels.some(([name, value]) => !metricValues[name]?.includes(value)) || entry.type === "Histogram" && (!Array.isArray(buckets) || buckets.length > 32)) {
356
- this.dropped();
357
- return this;
358
- }
359
- return super.set(key, entry);
360
- }
361
- };
362
- /** Shared, closure-owned admission state; no queue or mutable counters are exposed. */
363
- function makeCliEngineeringBudget() {
364
- let records = 0, peakRecords = 0, activeRequests = 0, requestBytes = 0;
365
- let peakRequests = 0, peakRequestBytes = 0, dropped = 0;
366
- const drop = () => {
367
- dropped += 1;
368
- };
369
- const metricRegistry = new CliMetricRegistry(drop);
370
- return Object.freeze({
371
- metricRegistry,
372
- emptyMetricRegistry: () => new CliMetricRegistry(drop, false),
373
- drop,
374
- admitRecord(terminal) {
375
- if (records >= (terminal ? CLI_ENGINEERING_LIMITS.records : CLI_ENGINEERING_LIMITS.ordinaryRecords)) {
376
- drop();
377
- return;
378
- }
379
- records += 1;
380
- peakRecords = Math.max(peakRecords, records);
381
- let released = false;
382
- return () => {
383
- if (!released) {
384
- released = true;
385
- records -= 1;
386
- }
387
- };
388
- },
389
- admitRequest(bytes, terminal) {
390
- if (bytes > CLI_ENGINEERING_LIMITS.requestBytes || activeRequests >= (terminal ? CLI_ENGINEERING_LIMITS.requests : CLI_ENGINEERING_LIMITS.ordinaryRequests)) {
391
- drop();
392
- return;
393
- }
394
- activeRequests += 1;
395
- requestBytes += bytes;
396
- peakRequests = Math.max(peakRequests, activeRequests);
397
- peakRequestBytes = Math.max(peakRequestBytes, requestBytes);
398
- return () => {
399
- activeRequests -= 1;
400
- requestBytes -= bytes;
401
- };
402
- },
403
- snapshot() {
404
- return {
405
- records,
406
- reservedRecordBytes: records * CLI_ENGINEERING_LIMITS.recordBytes,
407
- peakRecords,
408
- peakReservedRecordBytes: peakRecords * CLI_ENGINEERING_LIMITS.recordBytes,
409
- activeRequests,
410
- requestBytes,
411
- peakRequests,
412
- peakRequestBytes,
413
- dropped,
414
- metricSeries: metricRegistry.size
415
- };
416
- }
417
- });
418
- }
419
- //#endregion
420
503
  //#region src/delivery/requests.ts
421
504
  function admittedSerialization(state, delegate) {
422
505
  return {
@@ -564,8 +647,8 @@ const validateEngineeringTelemetryConfig = (config) => {
564
647
  let url;
565
648
  try {
566
649
  url = new URL(config.host);
567
- } catch {
568
- throw new TypeError("engineering telemetry host must be an absolute HTTPS URL");
650
+ } catch (cause) {
651
+ throw new TypeError("engineering telemetry host must be an absolute HTTPS URL", { cause });
569
652
  }
570
653
  if (url.protocol !== "https:" || url.username.length > 0 || url.password.length > 0 || url.pathname !== "/" || url.search.length > 0 || url.hash.length > 0 || !(url.hostname === "posthog.com" || url.hostname.endsWith(".posthog.com"))) throw new TypeError("engineering telemetry host must be a credential-free HTTPS origin");
571
654
  if (!ENGINEERING_CAPXUL_ENV_SET.has(config.capxulEnv)) throw new TypeError("engineering telemetry capxulEnv is not canonical");
@@ -625,7 +708,8 @@ const MINIMAL_CORRELATIONS = /* @__PURE__ */ new Set([
625
708
  "correlation_id",
626
709
  "journey_id",
627
710
  "invocation_id",
628
- "posthogDistinctId"
711
+ "posthogDistinctId",
712
+ "failure_correlation_id"
629
713
  ]);
630
714
  const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
631
715
  "command",
@@ -638,6 +722,12 @@ const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
638
722
  const MINIMAL_METRIC_NAMES = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
639
723
  const traceId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{32}$/u.test(value);
640
724
  const spanId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{16}$/u.test(value);
725
+ /**
726
+ * A correlation id the minimal export carries: an identifier with no `/`, not
727
+ * `0x` hex, and unchanged by the secret mask. The CLI quotes a backend
728
+ * correlation id as its `ref` only when it passes, so the ref finds a row.
729
+ */
730
+ const minimalCorrelationId = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value) && !/^0x[0-9a-f]+$/iu.test(value) && redactSecrets(value) === value;
641
731
  const minimalName = (value) => typeof value === "string" && (MINIMAL_LOG_NAMES.has(value) || isCapxulOperation(value) || CLI_COMMAND_NAMES.some((command) => value === `cli.command.${command}`)) ? value : "capxul.engineering";
642
732
  const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
643
733
  ["outcome", [
@@ -649,6 +739,8 @@ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
649
739
  ]],
650
740
  ["mode", FAILURE_MODES],
651
741
  ["failure_mode", FAILURE_MODES],
742
+ ["failure_class", FAILURE_CLASSES],
743
+ ["problem", PROVIDER_PROBLEMS],
652
744
  ["capxul_env", ENGINEERING_CAPXUL_ENVS],
653
745
  ["producer", ["browser", "server"]],
654
746
  ["command", CLI_COMMAND_NAMES],
@@ -677,7 +769,7 @@ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
677
769
  "healStaleCache",
678
770
  "walletReady"
679
771
  ]],
680
- ["provider", ["better-auth", "oidc"]],
772
+ ["provider", [...PROVIDERS, "oidc"]],
681
773
  ["cli_identity_kind", ["anonymous", "identified"]],
682
774
  ["output_mode", ["human", "json"]],
683
775
  ["setup_status", ORGANIZATION_SETUP_STATUSES],
@@ -696,25 +788,36 @@ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
696
788
  const MINIMAL_CODE_KEYS = /* @__PURE__ */ new Set([
697
789
  "error.code",
698
790
  "error_code",
791
+ "error_kind",
699
792
  "failure_code",
700
- "refusal_code",
701
- "capxul_error_code"
793
+ "refusal_code"
702
794
  ]);
703
795
  const MINIMAL_VERSION_KEYS = /* @__PURE__ */ new Set([
704
796
  "sdk_version",
705
797
  "cli_version",
706
798
  "release"
707
799
  ]);
800
+ /** The failure row's identifiers that the `none` text policy lets through (ADR-0039 D6). */
801
+ const MINIMAL_FAILURE_IDENTIFIERS = /* @__PURE__ */ new Set([
802
+ "provider_operation",
803
+ "provider_code",
804
+ "field"
805
+ ]);
806
+ /** What raised a failure: its layer (`convex`, `sdk`), and a CLI defect's error name. */
807
+ const MINIMAL_FAILURE_ORIGINS = /* @__PURE__ */ new Set(["failure_layer", "failure_error_name"]);
708
808
  function minimalStringValue(key, text) {
709
809
  if (key === "operation") return isCapxulOperation(text);
710
810
  if (key === "organization_id") return /^org_[0-9A-Za-z]+$/u.test(text);
711
811
  if (MINIMAL_CODE_KEYS.has(key)) return MINIMAL_ERROR_CODES.has(text);
712
812
  if (MINIMAL_VERSION_KEYS.has(key)) return /^[A-Za-z0-9][A-Za-z0-9._+@-]{0,127}$/u.test(text);
813
+ if (MINIMAL_FAILURE_IDENTIFIERS.has(key)) return /^[A-Za-z0-9_][A-Za-z0-9_.:/[\]-]{0,127}$/u.test(text) && redactSecrets(text) === text;
814
+ if (MINIMAL_FAILURE_ORIGINS.has(key)) return /^[A-Za-z_][A-Za-z0-9_.-]{0,63}$/u.test(text) && redactSecrets(text) === text;
815
+ if (key === "request_id") return /^[0-9a-f]{16}$/u.test(text);
713
816
  if (key === "trace_id") return traceId(text);
714
817
  if (key === "span_id" || key === "parent_span_id") return spanId(text);
715
818
  const values = MINIMAL_ENUM_VALUES.get(key);
716
819
  if (values !== void 0) return values.includes(text);
717
- return MINIMAL_CORRELATIONS.has(key) && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(text) && !/^0x[0-9a-f]+$/iu.test(text) && redactSecrets(text) === text;
820
+ return MINIMAL_CORRELATIONS.has(key) && minimalCorrelationId(text);
718
821
  }
719
822
  /** A named privacy projection, separate from the default credential-only redaction policy. */
720
823
  function minimalAttributes(attributes, metric = false) {
@@ -1033,4 +1136,4 @@ const makeEngineeringTelemetryLayer = (config) => {
1033
1136
  });
1034
1137
  };
1035
1138
  //#endregion
1036
- export { CLI_ENGINEERING_LIMITS, ENGINEERING_CAPXUL_ENVS, flushEngineeringTelemetry, logEngineeringInfo, makeCliEngineeringBudget, makeEngineeringTelemetryLayer, makeLeakSafeEngineeringTracer, postHogOtlpEndpoints, traceHeaderFilter };
1139
+ export { CLI_ENGINEERING_LIMITS, ENGINEERING_CAPXUL_ENVS, flushEngineeringTelemetry, logEngineeringInfo, makeCliEngineeringBudget, makeEngineeringTelemetryLayer, makeLeakSafeEngineeringTracer, minimalCorrelationId, postHogOtlpEndpoints, traceHeaderFilter };