@capxul/observability 4.20.0-beta.4 → 4.20.0-beta.40

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.
@@ -1,13 +1,24 @@
1
- import { _ as CLI_COMMAND_NAMES, an as redactSecrets, en as isCapxulOperation, g as CLI_CAPABILITIES, in as isCredentialField, nn as CAPXUL_ERROR_CODES, on as redactUrlSecrets, rn as FAILURE_MODES, v as CLI_INVOCATION_KINDS } from "./src-CyMef9yy.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-CyMiJfOK.mjs";
2
2
  import { Cause, Clock, Context, Deferred, Duration, Effect, Exit, FiberSet, Layer, LogLevel, Logger, Metric, Option, References, Schedule, Stream, Tracer } from "effect";
3
- import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientError, HttpClientResponse } from "effect/unstable/http";
4
- import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/unstable/observability";
5
- //#region src/engineering-delivery.ts
3
+ import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientError, HttpClientResponse } from "effect/http";
4
+ import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/observability";
5
+ //#region ../protocol/src/primitives/record.ts
6
+ /** A non-null object that is not an array: a value whose fields can be read by name. */
7
+ function isRecord(value) {
8
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9
+ }
10
+ /** The field `key` of `value` when `value` is a record, else undefined. */
11
+ function readField(value, key) {
12
+ return isRecord(value) ? value[key] : void 0;
13
+ }
14
+ //#endregion
15
+ //#region src/delivery/budget.ts
6
16
  /** Fixed admission limits for one CLI invocation and its SDK client. */
7
17
  const CLI_ENGINEERING_LIMITS = Object.freeze({
8
18
  records: 80,
9
19
  ordinaryRecords: 64,
10
20
  recordBytes: 16384,
21
+ recordFields: 48,
11
22
  requests: 5,
12
23
  ordinaryRequests: 2,
13
24
  requestBytes: 1572864,
@@ -52,9 +63,8 @@ var CliMetricRegistry = class extends Map {
52
63
  return this;
53
64
  }
54
65
  const labels = Object.entries(entry.attributes ?? {});
55
- const state = entry.hooks.get(Context.empty());
56
- const buckets = typeof state === "object" && state !== null && "buckets" in state ? state.buckets : void 0;
57
- if (!["Counter", "Histogram"].includes(entry.type) || (entry.description?.length ?? 0) > 128 || labels.length > 6 || labels.some(([name, value]) => !metricValues[name]?.includes(String(value))) || entry.type === "Histogram" && (!Array.isArray(buckets) || buckets.length > 32)) {
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)) {
58
68
  this.dropped();
59
69
  return this;
60
70
  }
@@ -118,8 +128,51 @@ function makeCliEngineeringBudget() {
118
128
  }
119
129
  });
120
130
  }
131
+ //#endregion
132
+ //#region src/delivery/admission.ts
133
+ function newDeliveryState(budget, ownsMetrics) {
134
+ return {
135
+ budget,
136
+ ownsMetrics,
137
+ closed: Deferred.makeUnsafe(),
138
+ isClosed: false,
139
+ metricsRunning: false,
140
+ logs: [],
141
+ spans: /* @__PURE__ */ new Map(),
142
+ terminalBodies: /* @__PURE__ */ new WeakSet(),
143
+ bodyLeases: /* @__PURE__ */ new WeakMap(),
144
+ pendingBodies: /* @__PURE__ */ new Set()
145
+ };
146
+ }
147
+ /** Hold the records a body carries until its request finishes or the body is discarded. */
148
+ function holdBody(state, body, releases) {
149
+ if (releases.length === 0) return;
150
+ const lease = {
151
+ started: false,
152
+ release: () => {
153
+ if (!state.pendingBodies.delete(lease)) return;
154
+ state.bodyLeases.delete(body);
155
+ for (const release of releases) release();
156
+ }
157
+ };
158
+ state.bodyLeases.set(body, lease);
159
+ state.pendingBodies.add(lease);
160
+ }
161
+ const discardBody = (state, body) => state.bodyLeases.get(body)?.release();
162
+ const drainLogs = (state) => {
163
+ for (const release of state.logs.splice(0)) release();
164
+ };
165
+ function closeDelivery(state) {
166
+ state.isClosed = true;
167
+ Deferred.doneUnsafe(state.closed, Effect.void);
168
+ for (const lease of state.pendingBodies) if (!lease.started) lease.release();
169
+ drainLogs(state);
170
+ for (const release of state.spans.values()) release();
171
+ state.spans.clear();
172
+ if (state.ownsMetrics) state.budget.metricRegistry.clear();
173
+ }
121
174
  const text = (value) => typeof value === "string" ? value.slice(0, 128) : "capxul.engineering";
122
- const attributes = (input, limit = 32) => {
175
+ const attributes = (input, limit = CLI_ENGINEERING_LIMITS.recordFields) => {
123
176
  const out = {};
124
177
  let count = 0;
125
178
  let bytes = 0;
@@ -137,293 +190,76 @@ const attributes = (input, limit = 32) => {
137
190
  } catch {}
138
191
  return out;
139
192
  };
140
- const terminalName = (name) => name === "cli.command.completed" || CLI_COMMAND_NAMES.some((command) => name === `cli.command.${command}`);
141
- /** Native exporters retain their serialization and scheduling; this owns admission only. */
142
- function cliEngineeringDelivery(budget, ownsMetrics) {
143
- const closed = Deferred.makeUnsafe();
144
- let isClosed = false;
145
- let metricsRunning = false;
146
- const logs = [];
147
- const spans = /* @__PURE__ */ new Map();
148
- const terminalBodies = /* @__PURE__ */ new WeakSet();
149
- const bodyLeases = /* @__PURE__ */ new WeakMap();
150
- const pendingBodies = /* @__PURE__ */ new Set();
151
- const holdBody = (body, releases) => {
152
- if (releases.length === 0) return;
153
- const lease = {
154
- started: false,
155
- release: () => {
156
- if (!pendingBodies.delete(lease)) return;
157
- bodyLeases.delete(body);
158
- for (const release of releases) release();
159
- }
160
- };
161
- bodyLeases.set(body, lease);
162
- pendingBodies.add(lease);
163
- };
164
- const discardBody = (body) => bodyLeases.get(body)?.release();
165
- const drainLogs = () => {
166
- for (const release of logs.splice(0)) release();
167
- };
168
- const close = () => {
169
- isClosed = true;
170
- Deferred.doneUnsafe(closed, Effect.void);
171
- for (const lease of pendingBodies) if (!lease.started) lease.release();
172
- drainLogs();
173
- for (const release of spans.values()) release();
174
- spans.clear();
175
- if (ownsMetrics) budget.metricRegistry.clear();
176
- };
177
- return {
178
- close,
179
- discardBody,
180
- metrics(run) {
181
- return Effect.suspend(() => {
182
- if (isClosed || metricsRunning) return Effect.void;
183
- metricsRunning = true;
184
- return run.pipe(Effect.ensuring(Effect.sync(() => {
185
- metricsRunning = false;
186
- })));
187
- });
188
- },
189
- logger(delegate) {
190
- return Logger.make((options) => {
191
- if (isClosed) return;
192
- let release;
193
- try {
194
- const message = text(Array.isArray(options.message) ? Object.getOwnPropertyDescriptor(options.message, "0")?.value : options.message);
195
- release = budget.admitRecord(terminalName(message));
196
- if (release === void 0) return;
197
- const context = Context.add(options.fiber.context, References.CurrentLogAnnotations, attributes(options.fiber.getRef(References.CurrentLogAnnotations)));
198
- const fiber = Object.create(options.fiber);
199
- Object.defineProperty(fiber, "getRef", { value: (ref) => Context.get(context, ref) });
200
- logs.push(release);
201
- delegate.log({
202
- ...options,
203
- message,
204
- cause: Cause.empty,
205
- fiber
206
- });
207
- } catch {
208
- if (release !== void 0) {
209
- const index = logs.indexOf(release);
210
- if (index !== -1) logs.splice(index, 1);
211
- release();
212
- }
213
- budget.drop();
214
- }
215
- });
216
- },
217
- tracer(delegate) {
218
- return Tracer.make({
219
- span(options) {
220
- const name = text(options.name);
221
- const release = isClosed ? void 0 : budget.admitRecord(terminalName(name));
222
- const spanOptions = {
223
- ...options,
224
- name,
225
- annotations: Context.empty(),
226
- links: []
227
- };
228
- const span = release === void 0 ? new Tracer.NativeSpan({
229
- ...spanOptions,
230
- sampled: false
231
- }) : delegate.span(spanOptions);
232
- if (release !== void 0) spans.set(span.spanId, release);
233
- const wrapped = Object.create(span);
234
- let fields = 0, events = 0, links = 0, attributeBytes = 0;
235
- let ended = false;
236
- Object.defineProperties(wrapped, {
237
- attribute: { value: (key, value) => {
238
- if (fields >= 32 && !span.attributes.has(key)) {
239
- budget.drop();
240
- return;
241
- }
242
- const safe = attributes({ [key]: value }, 1);
243
- if (!Object.hasOwn(safe, key)) return;
244
- const previous = span.attributes.has(key) ? new TextEncoder().encode(JSON.stringify([key, span.attributes.get(key)])).length : 0;
245
- const next = new TextEncoder().encode(JSON.stringify([key, safe[key]])).length;
246
- if (attributeBytes - previous + next > 2e3) {
247
- budget.drop();
248
- return;
249
- }
250
- attributeBytes += next - previous;
251
- if (!span.attributes.has(key)) fields += 1;
252
- span.attribute(key, safe[key]);
253
- } },
254
- event: { value: (eventName, time, attrs) => {
255
- if (events++ >= 2) {
256
- budget.drop();
257
- return;
258
- }
259
- span.event(text(eventName), time, attributes(attrs ?? {}, 4));
260
- } },
261
- addLinks: { value: (items) => {
262
- for (const item of items.slice(0, Math.max(0, 2 - links))) {
263
- if (!/^[a-f0-9]{32}$/u.test(item.span.traceId) || !/^[a-f0-9]{16}$/u.test(item.span.spanId)) {
264
- budget.drop();
265
- continue;
266
- }
267
- span.addLinks([{
268
- span: Tracer.externalSpan({
269
- traceId: item.span.traceId,
270
- spanId: item.span.spanId,
271
- sampled: item.span.sampled
272
- }),
273
- attributes: attributes(item.attributes, 4)
274
- }]);
275
- links += 1;
276
- }
277
- } },
278
- end: { value: (time, exit) => {
279
- if (ended) return;
280
- ended = true;
281
- span.end(time, Exit.isFailure(exit) ? Exit.fail("CLI operation failed") : Exit.void);
282
- } }
283
- });
284
- wrapped.addLinks(options.links ?? []);
285
- return wrapped;
286
- },
287
- ...delegate.context === void 0 ? {} : { context: delegate.context.bind(delegate) }
288
- });
289
- },
290
- serialization(delegate) {
291
- return {
292
- logs(data) {
293
- const releases = logs.splice(0);
294
- try {
295
- const body = delegate.logs(data);
296
- if (data.resourceLogs.some((r) => r.scopeLogs.some((s) => s.logRecords?.some((l) => l.body?.stringValue === "cli.command.completed")))) terminalBodies.add(body);
297
- holdBody(body, releases);
298
- return body;
299
- } catch (error) {
300
- for (const release of releases) release();
301
- throw error;
302
- }
303
- },
304
- traces(data) {
305
- const releases = [];
306
- for (const resource of data.resourceSpans) for (const scope of resource.scopeSpans) for (const span of scope.spans) {
307
- const release = spans.get(span.spanId);
308
- if (release !== void 0) releases.push(release);
309
- spans.delete(span.spanId);
310
- }
311
- try {
312
- const body = delegate.traces(data);
313
- if (data.resourceSpans.some((r) => r.scopeSpans.some((s) => s.spans.some((span) => terminalName(span.name))))) terminalBodies.add(body);
314
- holdBody(body, releases);
315
- return body;
316
- } catch (error) {
317
- for (const release of releases) release();
318
- throw error;
319
- }
320
- },
321
- metrics(data) {
322
- const body = delegate.metrics(data);
323
- terminalBodies.add(body);
324
- return body;
325
- }
326
- };
327
- },
328
- transport(client, enabled = () => true) {
329
- const retrying = HttpClient.transform(client, (response, request) => Effect.suspend(() => {
330
- if (enabled()) return response;
331
- budget.drop();
332
- return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
333
- })).pipe(HttpClient.transformResponse((response) => Effect.tap(response, (result) => {
334
- let received = 0;
335
- return Stream.runForEach(result.stream, (chunk) => Effect.sync(() => {
336
- received += chunk.byteLength;
337
- if (received > 65536) throw new Error("Engineering response limit exceeded");
338
- })).pipe(Effect.catchTag("HttpClientError", (error) => error.reason._tag === "EmptyBodyError" ? Effect.void : Effect.fail(error)));
339
- }))).pipe(HttpClient.filterStatusOk, HttpClient.retryTransient({
340
- times: 1,
341
- schedule: Schedule.spaced("100 millis").pipe(Schedule.addDelay(({ input }) => Effect.map(Clock.currentTimeMillis, (now) => {
342
- if (!HttpClientError.isHttpClientError(input) || input.reason._tag !== "StatusCodeError" || input.reason.response.status !== 429) return Duration.zero;
343
- const header = input.reason.response.headers["retry-after"];
344
- const numeric = Number(header);
345
- const delay = header === void 0 ? 2e3 : Number.isFinite(numeric) ? numeric * 1e3 : Date.parse(header) - now;
346
- return Duration.millis(Number.isFinite(delay) ? Math.min(2e3, Math.max(0, delay)) : 2e3);
347
- })))
348
- }));
349
- return HttpClient.transform(retrying, (response, request) => Effect.suspend(() => {
350
- if (isClosed) {
351
- discardBody(request.body);
352
- return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
353
- }
354
- const droppedResponse = () => HttpClientResponse.fromWeb(request, new Response(null, { status: 200 }));
355
- if (request.body._tag === "Empty") {
356
- discardBody(request.body);
357
- return Effect.succeed(droppedResponse());
358
- }
359
- const bytes = request.body.contentLength;
360
- const release = budget.admitRequest(bytes ?? Number.POSITIVE_INFINITY, terminalBodies.has(request.body));
361
- if (release === void 0) {
362
- discardBody(request.body);
363
- return Effect.succeed(droppedResponse());
364
- }
365
- const lease = bodyLeases.get(request.body);
366
- if (lease !== void 0) lease.started = true;
367
- return response.pipe(Effect.asVoid, Effect.timeoutOption(CLI_ENGINEERING_LIMITS.requestTimeoutMs), Effect.raceFirst(Deferred.await(closed).pipe(Effect.andThen(Effect.interrupt))), Effect.tap((result) => Effect.sync(() => {
368
- if (Option.isNone(result)) budget.drop();
369
- })), Effect.catchCause(() => Effect.sync(() => {
370
- budget.drop();
371
- })), Effect.as(droppedResponse()), Effect.ensuring(Effect.sync(() => {
372
- release();
373
- discardBody(request.body);
374
- })));
375
- }));
376
- }
377
- };
378
- }
193
+ const terminalName = (name) => name === "cli.command.completed" || name === "cli.wallet.step" || CLI_COMMAND_NAMES.some((command) => name === `cli.command.${command}`);
379
194
  //#endregion
380
- //#region src/engineering.ts
381
- const ENGINEERING_CAPXUL_ENVS = [
382
- "development",
383
- "staging",
384
- "production",
385
- "local"
386
- ];
387
- const traceHeaderFilter = (_name) => true;
388
- const credentialHeaderPattern = Object.assign(/./u, { test: (name) => isCredentialField(name, "header") });
389
- const postHogOtlpEndpoints = (host) => {
390
- const base = host.replace(/\/+$/, "");
391
- return {
392
- logs: `${base}/i/v1/logs`,
393
- traces: `${base}/i/v1/traces`,
394
- metrics: `${base}/i/v1/metrics`
395
- };
396
- };
397
- const ENGINEERING_CAPXUL_ENV_SET = new Set(ENGINEERING_CAPXUL_ENVS);
398
- const ENGINEERING_PRODUCERS = /* @__PURE__ */ new Set(["browser", "server"]);
399
- const PUBLIC_POSTHOG_AUTHORIZATION = /^Bearer phc_[A-Za-z0-9_-]{1,191}$/u;
400
- const SAFE_RESOURCE_VALUE = /^[A-Za-z0-9][A-Za-z0-9._+@/-]{0,127}$/u;
401
- function validateEngineeringPrivacy(config) {
402
- if (config.privacy !== void 0 && config.privacy !== "minimal") throw new TypeError("engineering telemetry privacy must be minimal when specified");
403
- if (config.privacy === "minimal" && (!/^[A-Za-z0-9][A-Za-z0-9._+@-]{0,127}$/u.test(config.sdkVersion) || config.serviceName !== void 0 && !/^[A-Za-z0-9][A-Za-z0-9._+@-]{0,127}$/u.test(config.serviceName))) throw new TypeError("minimal engineering resource values must not contain paths");
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)] ?? "");
404
234
  }
405
- const validateEngineeringTelemetryConfig = (config) => {
406
- let url;
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) {
407
249
  try {
408
- url = new URL(config.host);
250
+ return boundedText(maskKeepingIdentifiers(error.message, source));
409
251
  } catch {
410
- throw new TypeError("engineering telemetry host must be an absolute HTTPS URL");
252
+ return UNPRINTABLE;
411
253
  }
412
- 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");
413
- if (!ENGINEERING_CAPXUL_ENV_SET.has(config.capxulEnv)) throw new TypeError("engineering telemetry capxulEnv is not canonical");
414
- if (!ENGINEERING_PRODUCERS.has(config.producer)) throw new TypeError("engineering telemetry producer is not canonical");
415
- if (config.enabled !== void 0 && typeof config.enabled !== "boolean" && typeof config.enabled !== "function") throw new TypeError("engineering telemetry enabled must be a boolean or callback");
416
- validateEngineeringPrivacy(config);
417
- if (!SAFE_RESOURCE_VALUE.test(config.sdkVersion)) throw new TypeError("engineering telemetry sdkVersion must be a bounded safe value");
418
- if (config.serviceName !== void 0 && !SAFE_RESOURCE_VALUE.test(config.serviceName)) throw new TypeError("engineering telemetry serviceName must be a bounded safe value");
419
- const headerEntries = Object.entries(config.headers);
420
- if (headerEntries.length !== 1 || headerEntries[0]?.[0].toLowerCase() !== "authorization" || !PUBLIC_POSTHOG_AUTHORIZATION.test(headerEntries[0]?.[1] ?? "")) throw new TypeError("engineering telemetry headers must contain exactly one public PostHog authorization token");
421
- return {
422
- ...config,
423
- host: url.origin,
424
- headers: Object.freeze({ authorization: headerEntries[0][1] })
425
- };
426
- };
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
262
+ //#region src/engineering/tracer.ts
427
263
  const failureText = (failure, key, limit, source) => {
428
264
  if (failure === null || typeof failure !== "object") return void 0;
429
265
  try {
@@ -440,8 +276,20 @@ const failureText = (failure, key, limit, source) => {
440
276
  return;
441
277
  }
442
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
+ };
443
291
  const exportFailure = (failure, source) => {
444
- 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");
445
293
  const code = failureText(failure, "code", 128);
446
294
  const error = new Error((code === void 0 ? message : `${message} [code=${code}]`).slice(0, 2048));
447
295
  error.name = failureText(failure, "_tag", 128) ?? failureText(failure, "name", 128) ?? "Error";
@@ -457,7 +305,7 @@ const browserExportClient = (client) => HttpClient.transform(client, (response,
457
305
  if (request.body._tag === "Stream" || request.body._tag === "FormData" || bytes === void 0 || !Number.isSafeInteger(bytes) || bytes < 0 || unfinishedBrowserExportBytes + bytes > BROWSER_KEEPALIVE_BYTES) return;
458
306
  unfinishedBrowserExportBytes += bytes;
459
307
  return bytes;
460
- }), (bytes) => response.pipe(Effect.provideService(FetchHttpClient.RequestInit, { keepalive: bytes !== void 0 }), Effect.tap((result) => bytes === void 0 ? Effect.void : Stream.runDrain(result.stream).pipe(Effect.catch((error) => error.reason._tag === "EmptyBodyError" ? Effect.void : Effect.fail(error))))), (bytes) => Effect.sync(() => {
308
+ }), (bytes) => response.pipe(Effect.provideService(FetchHttpClient.RequestInit, { keepalive: bytes !== void 0 }), Effect.tap((result) => bytes === void 0 ? Effect.void : Stream.runDrain(result.stream).pipe(Effect.catchIf((error) => error.reason._tag === "EmptyBodyError", () => Effect.void)))), (bytes) => Effect.sync(() => {
461
309
  if (bytes !== void 0) unfinishedBrowserExportBytes -= bytes;
462
310
  })));
463
311
  const spanUpstream = (span) => {
@@ -477,11 +325,29 @@ function redactHttpPath(path, fullUrl, source) {
477
325
  const prefix = path.startsWith("/") ? origin : "";
478
326
  return redactUrlSecrets(`${prefix}${path}`, source).slice(prefix.length, prefix.length + 2048);
479
327
  }
328
+ /** A span whose prototype is `span`: it answers every Span member until an own property overrides it. */
329
+ function inheritSpan(span) {
330
+ const wrapped = Object.create(span);
331
+ Object.defineProperties(wrapped, {
332
+ traceId: { get: () => span.traceId },
333
+ spanId: { get: () => span.spanId },
334
+ attributes: { get: () => span.attributes },
335
+ event: {
336
+ value: span.event.bind(span),
337
+ configurable: true
338
+ },
339
+ addLinks: {
340
+ value: span.addLinks.bind(span),
341
+ configurable: true
342
+ }
343
+ });
344
+ return wrapped;
345
+ }
480
346
  /** Preserve domain exits while preventing the OTLP serializer from seeing raw causes. */
481
347
  const makeLeakSafeEngineeringTracer = (delegate, enabled = () => true) => Tracer.make({
482
348
  span(options) {
483
349
  const span = enabled() ? delegate.span(options) : new Tracer.NativeSpan(options);
484
- const wrapped = Object.create(span);
350
+ const wrapped = inheritSpan(span);
485
351
  Object.defineProperty(wrapped, "attribute", { value: (name, value) => {
486
352
  const source = spanUpstream(span);
487
353
  const header = /^http\.(?:request|response)\.header\.(.+)$/u.exec(name)?.[1];
@@ -521,20 +387,303 @@ const makeLeakSafeEngineeringTracer = (delegate, enabled = () => true) => Tracer
521
387
  },
522
388
  ...delegate.context === void 0 ? {} : { context: delegate.context.bind(delegate) }
523
389
  });
390
+ //#endregion
391
+ //#region src/delivery/records.ts
392
+ function admittedLogger(state, delegate) {
393
+ return Logger.make((options) => {
394
+ if (state.isClosed) return;
395
+ let release;
396
+ try {
397
+ const message = text(Array.isArray(options.message) ? Object.getOwnPropertyDescriptor(options.message, "0")?.value : options.message);
398
+ release = state.budget.admitRecord(terminalName(message));
399
+ if (release === void 0) return;
400
+ const context = Context.add(options.fiber.context, References.CurrentLogAnnotations, attributes(options.fiber.getRef(References.CurrentLogAnnotations)));
401
+ const fiber = Object.create(options.fiber);
402
+ Object.defineProperty(fiber, "getRef", { value: (ref) => Context.get(context, ref) });
403
+ state.logs.push(release);
404
+ delegate.log({
405
+ ...options,
406
+ message,
407
+ cause: Cause.empty,
408
+ fiber
409
+ });
410
+ } catch {
411
+ if (release !== void 0) {
412
+ const index = state.logs.indexOf(release);
413
+ if (index !== -1) state.logs.splice(index, 1);
414
+ release();
415
+ }
416
+ state.budget.drop();
417
+ }
418
+ });
419
+ }
420
+ function admittedTracer(state, delegate) {
421
+ return Tracer.make({
422
+ span(options) {
423
+ const name = text(options.name);
424
+ const release = state.isClosed ? void 0 : state.budget.admitRecord(terminalName(name));
425
+ const spanOptions = {
426
+ ...options,
427
+ name,
428
+ annotations: Context.empty(),
429
+ links: []
430
+ };
431
+ const span = release === void 0 ? new Tracer.NativeSpan({
432
+ ...spanOptions,
433
+ sampled: false
434
+ }) : delegate.span(spanOptions);
435
+ if (release !== void 0) state.spans.set(span.spanId, release);
436
+ const wrapped = boundedSpan(state, span);
437
+ wrapped.addLinks(options.links ?? []);
438
+ return wrapped;
439
+ },
440
+ ...delegate.context === void 0 ? {} : { context: delegate.context.bind(delegate) }
441
+ });
442
+ }
443
+ /**
444
+ * The span as a producer sees it: at most `recordFields` fields and 2000
445
+ * attribute bytes, 2 events, 2 links.
446
+ */
447
+ function boundedSpan(state, span) {
448
+ const { budget } = state;
449
+ const wrapped = inheritSpan(span);
450
+ let fields = 0, events = 0, links = 0, attributeBytes = 0;
451
+ let ended = false;
452
+ Object.defineProperties(wrapped, {
453
+ attribute: { value: (key, value) => {
454
+ if (fields >= CLI_ENGINEERING_LIMITS.recordFields && !span.attributes.has(key)) {
455
+ budget.drop();
456
+ return;
457
+ }
458
+ const safe = attributes({ [key]: value }, 1);
459
+ if (!Object.hasOwn(safe, key)) return;
460
+ const previous = span.attributes.has(key) ? new TextEncoder().encode(JSON.stringify([key, span.attributes.get(key)])).length : 0;
461
+ const next = new TextEncoder().encode(JSON.stringify([key, safe[key]])).length;
462
+ if (attributeBytes - previous + next > 2e3) {
463
+ budget.drop();
464
+ return;
465
+ }
466
+ attributeBytes += next - previous;
467
+ if (!span.attributes.has(key)) fields += 1;
468
+ span.attribute(key, safe[key]);
469
+ } },
470
+ event: { value: (eventName, time, attrs) => {
471
+ if (events++ >= 2) {
472
+ budget.drop();
473
+ return;
474
+ }
475
+ span.event(text(eventName), time, attributes(attrs ?? {}, 4));
476
+ } },
477
+ addLinks: { value: (items) => {
478
+ for (const item of items.slice(0, Math.max(0, 2 - links))) {
479
+ if (!/^[a-f0-9]{32}$/u.test(item.span.traceId) || !/^[a-f0-9]{16}$/u.test(item.span.spanId)) {
480
+ budget.drop();
481
+ continue;
482
+ }
483
+ span.addLinks([{
484
+ span: Tracer.externalSpan({
485
+ traceId: item.span.traceId,
486
+ spanId: item.span.spanId,
487
+ sampled: item.span.sampled
488
+ }),
489
+ attributes: attributes(item.attributes, 4)
490
+ }]);
491
+ links += 1;
492
+ }
493
+ } },
494
+ end: { value: (time, exit) => {
495
+ if (ended) return;
496
+ ended = true;
497
+ span.end(time, Exit.isFailure(exit) ? Exit.fail("CLI operation failed") : Exit.void);
498
+ } }
499
+ });
500
+ return wrapped;
501
+ }
502
+ //#endregion
503
+ //#region src/delivery/requests.ts
504
+ function admittedSerialization(state, delegate) {
505
+ return {
506
+ logs(data) {
507
+ const releases = state.logs.splice(0);
508
+ try {
509
+ const body = delegate.logs(data);
510
+ if (data.resourceLogs.some((r) => r.scopeLogs.some((s) => s.logRecords?.some((l) => terminalName(l.body?.stringValue ?? ""))))) state.terminalBodies.add(body);
511
+ holdBody(state, body, releases);
512
+ return body;
513
+ } catch (error) {
514
+ for (const release of releases) release();
515
+ throw error;
516
+ }
517
+ },
518
+ traces(data) {
519
+ const releases = [];
520
+ for (const resource of data.resourceSpans) for (const scope of resource.scopeSpans) for (const span of scope.spans) {
521
+ const release = state.spans.get(span.spanId);
522
+ if (release !== void 0) releases.push(release);
523
+ state.spans.delete(span.spanId);
524
+ }
525
+ try {
526
+ const body = delegate.traces(data);
527
+ if (data.resourceSpans.some((r) => r.scopeSpans.some((s) => s.spans.some((span) => terminalName(span.name))))) state.terminalBodies.add(body);
528
+ holdBody(state, body, releases);
529
+ return body;
530
+ } catch (error) {
531
+ for (const release of releases) release();
532
+ throw error;
533
+ }
534
+ },
535
+ metrics(data) {
536
+ const body = delegate.metrics(data);
537
+ state.terminalBodies.add(body);
538
+ return body;
539
+ }
540
+ };
541
+ }
542
+ /** A 429's Retry-After, bounded to two seconds; any other failure retries at once. */
543
+ const retryAfter = Schedule.spaced("100 millis").pipe(Schedule.addDelay(({ input }) => Effect.map(Clock.currentTimeMillis, (now) => {
544
+ if (!HttpClientError.isHttpClientError(input) || input.reason._tag !== "StatusCodeError" || input.reason.response.status !== 429) return Duration.zero;
545
+ const header = input.reason.response.headers["retry-after"];
546
+ const numeric = Number(header);
547
+ const delay = header === void 0 ? 2e3 : Number.isFinite(numeric) ? numeric * 1e3 : Date.parse(header) - now;
548
+ return Duration.millis(Number.isFinite(delay) ? Math.min(2e3, Math.max(0, delay)) : 2e3);
549
+ })));
550
+ function admittedTransport(state, client, enabled = () => true) {
551
+ const { budget } = state;
552
+ const retrying = HttpClient.transform(client, (response, request) => Effect.suspend(() => {
553
+ if (enabled()) return response;
554
+ budget.drop();
555
+ return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
556
+ })).pipe(HttpClient.transformResponse((response) => Effect.tap(response, (result) => {
557
+ let received = 0;
558
+ return Stream.runForEach(result.stream, (chunk) => Effect.sync(() => {
559
+ received += chunk.byteLength;
560
+ if (received > 65536) throw new Error("Engineering response limit exceeded");
561
+ })).pipe(Effect.catchReason("HttpClientError", "EmptyBodyError", () => Effect.void));
562
+ }))).pipe(HttpClient.filterStatusOk, HttpClient.retryTransient({
563
+ times: 1,
564
+ schedule: retryAfter
565
+ }));
566
+ return HttpClient.transform(retrying, (response, request) => Effect.suspend(() => {
567
+ if (state.isClosed) {
568
+ discardBody(state, request.body);
569
+ return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
570
+ }
571
+ const droppedResponse = () => HttpClientResponse.fromWeb(request, new Response(null, { status: 200 }));
572
+ if (request.body._tag === "Empty") {
573
+ discardBody(state, request.body);
574
+ return Effect.succeed(droppedResponse());
575
+ }
576
+ const bytes = request.body.contentLength;
577
+ const release = budget.admitRequest(bytes ?? Number.POSITIVE_INFINITY, state.terminalBodies.has(request.body));
578
+ if (release === void 0) {
579
+ discardBody(state, request.body);
580
+ return Effect.succeed(droppedResponse());
581
+ }
582
+ const lease = state.bodyLeases.get(request.body);
583
+ if (lease !== void 0) lease.started = true;
584
+ return response.pipe(Effect.asVoid, Effect.timeoutOption(CLI_ENGINEERING_LIMITS.requestTimeoutMs), Effect.raceFirst(Deferred.await(state.closed).pipe(Effect.andThen(Effect.interrupt))), Effect.tap((result) => Effect.sync(() => {
585
+ if (Option.isNone(result)) budget.drop();
586
+ })), Effect.catchCause(() => Effect.sync(() => {
587
+ budget.drop();
588
+ })), Effect.as(droppedResponse()), Effect.ensuring(Effect.sync(() => {
589
+ release();
590
+ discardBody(state, request.body);
591
+ })));
592
+ }));
593
+ }
594
+ //#endregion
595
+ //#region src/engineering-delivery.ts
596
+ /** Native exporters retain their serialization and scheduling; this owns admission only. */
597
+ function cliEngineeringDelivery(budget, ownsMetrics) {
598
+ const state = newDeliveryState(budget, ownsMetrics);
599
+ return {
600
+ close: () => closeDelivery(state),
601
+ discardBody: (body) => discardBody(state, body),
602
+ metrics(run) {
603
+ return Effect.suspend(() => {
604
+ if (state.isClosed || state.metricsRunning) return Effect.void;
605
+ state.metricsRunning = true;
606
+ return run.pipe(Effect.ensuring(Effect.sync(() => {
607
+ state.metricsRunning = false;
608
+ })));
609
+ });
610
+ },
611
+ logger: (delegate) => admittedLogger(state, delegate),
612
+ tracer: (delegate) => admittedTracer(state, delegate),
613
+ serialization: (delegate) => admittedSerialization(state, delegate),
614
+ transport: (client, enabled) => admittedTransport(state, client, enabled)
615
+ };
616
+ }
617
+ //#endregion
618
+ //#region src/engineering/config.ts
619
+ const ENGINEERING_CAPXUL_ENVS = [
620
+ "development",
621
+ "staging",
622
+ "production",
623
+ "local"
624
+ ];
625
+ const traceHeaderFilter = (_name) => true;
626
+ const credentialHeaderPattern = Object.assign(/./u, { exec: (name) => isCredentialField(name, "header") ? Object.assign([name], {
627
+ index: 0,
628
+ input: name
629
+ }) : null });
630
+ const postHogOtlpEndpoints = (host) => {
631
+ const base = host.replace(/\/+$/, "");
632
+ return {
633
+ logs: `${base}/i/v1/logs`,
634
+ traces: `${base}/i/v1/traces`,
635
+ metrics: `${base}/i/v1/metrics`
636
+ };
637
+ };
638
+ const ENGINEERING_CAPXUL_ENV_SET = new Set(ENGINEERING_CAPXUL_ENVS);
639
+ const ENGINEERING_PRODUCERS = /* @__PURE__ */ new Set(["browser", "server"]);
640
+ const PUBLIC_POSTHOG_AUTHORIZATION = /^Bearer phc_[A-Za-z0-9_-]{1,191}$/u;
641
+ const SAFE_RESOURCE_VALUE = /^[A-Za-z0-9][A-Za-z0-9._+@/-]{0,127}$/u;
642
+ function validateEngineeringPrivacy(config) {
643
+ if (config.privacy !== void 0 && config.privacy !== "minimal") throw new TypeError("engineering telemetry privacy must be minimal when specified");
644
+ if (config.privacy === "minimal" && (!/^[A-Za-z0-9][A-Za-z0-9._+@-]{0,127}$/u.test(config.sdkVersion) || config.serviceName !== void 0 && !/^[A-Za-z0-9][A-Za-z0-9._+@-]{0,127}$/u.test(config.serviceName))) throw new TypeError("minimal engineering resource values must not contain paths");
645
+ }
646
+ const validateEngineeringTelemetryConfig = (config) => {
647
+ let url;
648
+ try {
649
+ url = new URL(config.host);
650
+ } catch (cause) {
651
+ throw new TypeError("engineering telemetry host must be an absolute HTTPS URL", { cause });
652
+ }
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");
654
+ if (!ENGINEERING_CAPXUL_ENV_SET.has(config.capxulEnv)) throw new TypeError("engineering telemetry capxulEnv is not canonical");
655
+ if (!ENGINEERING_PRODUCERS.has(config.producer)) throw new TypeError("engineering telemetry producer is not canonical");
656
+ if (config.enabled !== void 0 && typeof config.enabled !== "boolean" && typeof config.enabled !== "function") throw new TypeError("engineering telemetry enabled must be a boolean or callback");
657
+ validateEngineeringPrivacy(config);
658
+ if (!SAFE_RESOURCE_VALUE.test(config.sdkVersion)) throw new TypeError("engineering telemetry sdkVersion must be a bounded safe value");
659
+ if (config.serviceName !== void 0 && !SAFE_RESOURCE_VALUE.test(config.serviceName)) throw new TypeError("engineering telemetry serviceName must be a bounded safe value");
660
+ const headerEntries = Object.entries(config.headers);
661
+ if (headerEntries.length !== 1 || headerEntries[0]?.[0].toLowerCase() !== "authorization" || !PUBLIC_POSTHOG_AUTHORIZATION.test(headerEntries[0]?.[1] ?? "")) throw new TypeError("engineering telemetry headers must contain exactly one public PostHog authorization token");
662
+ return {
663
+ ...config,
664
+ host: url.origin,
665
+ headers: Object.freeze({ authorization: headerEntries[0][1] })
666
+ };
667
+ };
668
+ //#endregion
669
+ //#region src/engineering/minimal.ts
524
670
  const MINIMAL_LOG_NAMES = /* @__PURE__ */ new Set([
525
671
  "capxul_sdk_operation",
526
672
  "identity.transition",
527
673
  "convex.connection.changed",
528
674
  "convex.action.connection_lost",
529
675
  "cli.command.completed",
530
- "cli.sdk.operation.failed"
676
+ "cli.sdk.operation.failed",
677
+ "cli.signer.wait",
678
+ "cli.wallet.step"
531
679
  ]);
532
680
  const MINIMAL_ERROR_CODES = /* @__PURE__ */ new Set([
533
681
  ...CAPXUL_ERROR_CODES,
534
682
  "CLI_USAGE",
535
683
  "CLI_CONFIG",
536
684
  "CLI_STORAGE",
537
- "CLI_DEFECT"
685
+ "CLI_DEFECT",
686
+ "CLI_DEADLINE"
538
687
  ]);
539
688
  const MINIMAL_NUMBERS = /* @__PURE__ */ new Set([
540
689
  "schema_version",
@@ -546,13 +695,21 @@ const MINIMAL_NUMBERS = /* @__PURE__ */ new Set([
546
695
  "http_status",
547
696
  "rpc.code",
548
697
  "connection_count",
549
- "connection_retries"
698
+ "connection_retries",
699
+ "elapsed_ms",
700
+ "total_ms",
701
+ "token_calls",
702
+ "call",
703
+ "attempt",
704
+ "bridge_protocol_version",
705
+ "httpStatus"
550
706
  ]);
551
707
  const MINIMAL_CORRELATIONS = /* @__PURE__ */ new Set([
552
708
  "correlation_id",
553
709
  "journey_id",
554
710
  "invocation_id",
555
- "posthogDistinctId"
711
+ "posthogDistinctId",
712
+ "failure_correlation_id"
556
713
  ]);
557
714
  const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
558
715
  "command",
@@ -565,6 +722,12 @@ const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
565
722
  const MINIMAL_METRIC_NAMES = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
566
723
  const traceId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{32}$/u.test(value);
567
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;
568
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";
569
732
  const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
570
733
  ["outcome", [
@@ -576,13 +739,46 @@ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
576
739
  ]],
577
740
  ["mode", FAILURE_MODES],
578
741
  ["failure_mode", FAILURE_MODES],
742
+ ["failure_class", FAILURE_CLASSES],
743
+ ["problem", PROVIDER_PROBLEMS],
579
744
  ["capxul_env", ENGINEERING_CAPXUL_ENVS],
580
745
  ["producer", ["browser", "server"]],
581
746
  ["command", CLI_COMMAND_NAMES],
582
747
  ["capability", CLI_CAPABILITIES],
583
748
  ["invocation_kind", CLI_INVOCATION_KINDS],
584
- ["cli_identity_kind", ["anonymous"]],
749
+ ["diagnostic_scope", [
750
+ "openfort.step",
751
+ "openfort.token",
752
+ "openfort.encryptionSession",
753
+ "openfort.embeddedState",
754
+ "openfort.storageCleared",
755
+ "openfort.configure",
756
+ "openfort.get",
757
+ "openfort.address",
758
+ "openfort.staleCacheHeal",
759
+ "openfort.signerRecovery",
760
+ "openfort.signerStatusListener"
761
+ ]],
762
+ ["step", [
763
+ "initialize",
764
+ "token",
765
+ "encryptionSession",
766
+ "embeddedState",
767
+ "configure",
768
+ "get",
769
+ "healStaleCache",
770
+ "walletReady"
771
+ ]],
772
+ ["provider", [...PROVIDERS, "oidc"]],
773
+ ["cli_identity_kind", ["anonymous", "identified"]],
585
774
  ["output_mode", ["human", "json"]],
775
+ ["setup_status", ORGANIZATION_SETUP_STATUSES],
776
+ ["setup_reason", ORGANIZATION_SETUP_REASONS],
777
+ ["signer_stage", [
778
+ "address",
779
+ "readiness",
780
+ "sign"
781
+ ]],
586
782
  ["unit", [
587
783
  "1",
588
784
  "s",
@@ -592,24 +788,36 @@ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
592
788
  const MINIMAL_CODE_KEYS = /* @__PURE__ */ new Set([
593
789
  "error.code",
594
790
  "error_code",
791
+ "error_kind",
595
792
  "failure_code",
596
- "refusal_code",
597
- "capxul_error_code"
793
+ "refusal_code"
598
794
  ]);
599
795
  const MINIMAL_VERSION_KEYS = /* @__PURE__ */ new Set([
600
796
  "sdk_version",
601
797
  "cli_version",
602
798
  "release"
603
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"]);
604
808
  function minimalStringValue(key, text) {
605
809
  if (key === "operation") return isCapxulOperation(text);
810
+ if (key === "organization_id") return /^org_[0-9A-Za-z]+$/u.test(text);
606
811
  if (MINIMAL_CODE_KEYS.has(key)) return MINIMAL_ERROR_CODES.has(text);
607
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);
608
816
  if (key === "trace_id") return traceId(text);
609
817
  if (key === "span_id" || key === "parent_span_id") return spanId(text);
610
818
  const values = MINIMAL_ENUM_VALUES.get(key);
611
819
  if (values !== void 0) return values.includes(text);
612
- 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);
613
821
  }
614
822
  /** A named privacy projection, separate from the default credential-only redaction policy. */
615
823
  function minimalAttributes(attributes, metric = false) {
@@ -628,6 +836,8 @@ function minimalAttributes(attributes, metric = false) {
628
836
  }] : [];
629
837
  });
630
838
  }
839
+ //#endregion
840
+ //#region src/engineering/metric-points.ts
631
841
  const finiteProtocolNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
632
842
  const protocolCount = (value, maximum = Number.MAX_SAFE_INTEGER) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= maximum ? value : void 0;
633
843
  const protocolFlags = (value) => protocolCount(Object.getOwnPropertyDescriptor(value, "flags")?.value, 4294967295);
@@ -677,6 +887,8 @@ function minimalHistogramPoints(points) {
677
887
  }];
678
888
  });
679
889
  }
890
+ //#endregion
891
+ //#region src/engineering/serialization.ts
680
892
  function minimalSerialization(config) {
681
893
  const service = config.serviceName ?? "capxul-sdk";
682
894
  const resource = { attributes: Object.entries({
@@ -767,7 +979,7 @@ function minimalSerialization(config) {
767
979
  const gauge = metric.gauge === void 0 ? void 0 : { dataPoints: minimalNumberPoints(metric.gauge.dataPoints) };
768
980
  const sum = metric.sum === void 0 ? void 0 : {
769
981
  aggregationTemporality: protocolCount(metric.sum.aggregationTemporality, 2) ?? 0,
770
- isMonotonic: metric.sum.isMonotonic === true,
982
+ isMonotonic: metric.sum.isMonotonic,
771
983
  dataPoints: minimalNumberPoints(metric.sum.dataPoints)
772
984
  };
773
985
  const histogram = metric.histogram === void 0 ? void 0 : {
@@ -797,14 +1009,18 @@ function minimalSerialization(config) {
797
1009
  }
798
1010
  };
799
1011
  }
1012
+ //#endregion
1013
+ //#region src/engineering/logs.ts
800
1014
  /** Attempt to drain all engineering signals; completion does not prove ingestion. */
801
1015
  const flushEngineeringTelemetry = Effect.flatMap(OtlpExporter.Flusher, (flusher) => flusher.flush);
802
1016
  var EngineeringLog = class extends Context.Service()("capxul/EngineeringLog") {};
803
1017
  /** Deliver an owned engineering record without changing the caller's console verbosity. */
804
1018
  const logEngineeringInfo = (message, attributes) => {
805
1019
  const record = Effect.logInfo(message).pipe(Effect.annotateLogs(attributes));
806
- return Effect.flatMap(LogLevel.isEnabled("Info"), (visible) => visible ? record : Effect.flatMap(EngineeringLog, (logger) => record.pipe(Effect.provideService(Logger.CurrentLoggers, /* @__PURE__ */ new Set([logger])), Effect.provideService(References.MinimumLogLevel, "All"))));
1020
+ return Effect.flatMap(LogLevel.isEnabled("Info"), (visible) => visible ? record : Effect.flatMap(Effect.serviceOption(EngineeringLog), (logger) => logger._tag === "None" ? Effect.void : record.pipe(Effect.provideService(Logger.CurrentLoggers, /* @__PURE__ */ new Set([logger.value])), Effect.provideService(References.MinimumLogLevel, "All"))));
807
1021
  };
1022
+ //#endregion
1023
+ //#region src/engineering.ts
808
1024
  /** Shared browser/server OTLP layer. OtlpLogger merges with incumbent loggers once. */
809
1025
  const makeEngineeringTelemetryLayer = (config) => {
810
1026
  const validated = validateEngineeringTelemetryConfig(config);
@@ -920,4 +1136,4 @@ const makeEngineeringTelemetryLayer = (config) => {
920
1136
  });
921
1137
  };
922
1138
  //#endregion
923
- 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 };