@lucern/sdk 1.0.1 → 1.0.3

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.
Files changed (176) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/accessControl.d.ts +1 -0
  3. package/dist/accessControl.js +213 -26
  4. package/dist/accessControl.js.map +1 -1
  5. package/dist/adminClient.d.ts +1 -0
  6. package/dist/adminClient.js +213 -26
  7. package/dist/adminClient.js.map +1 -1
  8. package/dist/answersClient.d.ts +1 -0
  9. package/dist/answersClient.js +213 -26
  10. package/dist/answersClient.js.map +1 -1
  11. package/dist/audiencesClient.d.ts +1 -0
  12. package/dist/audiencesClient.js +213 -26
  13. package/dist/audiencesClient.js.map +1 -1
  14. package/dist/auditClient.d.ts +1 -0
  15. package/dist/auditClient.js +213 -26
  16. package/dist/auditClient.js.map +1 -1
  17. package/dist/authDeviceClient.d.ts +1 -0
  18. package/dist/beliefs/index.d.ts +1 -0
  19. package/dist/beliefs/index.js +214 -27
  20. package/dist/beliefs/index.js.map +1 -1
  21. package/dist/beliefsClient.d.ts +1 -0
  22. package/dist/beliefsClient.js +213 -26
  23. package/dist/beliefsClient.js.map +1 -1
  24. package/dist/client.d.ts +1 -0
  25. package/dist/client.js +214 -27
  26. package/dist/client.js.map +1 -1
  27. package/dist/clientConfig.d.ts +1 -0
  28. package/dist/clientEvidenceCompat.d.ts +1 -0
  29. package/dist/clientKnowledgeNamespaces.d.ts +1 -0
  30. package/dist/clientLocalHelpers.d.ts +1 -0
  31. package/dist/clientLocalHelpers.js +2 -0
  32. package/dist/clientLocalHelpers.js.map +1 -1
  33. package/dist/clientPlatformNamespaces.d.ts +1 -0
  34. package/dist/clientRuntime.d.ts +1 -0
  35. package/dist/clientWorkflowNamespaces.d.ts +1 -0
  36. package/dist/contextClient.d.ts +1 -0
  37. package/dist/contextClient.js +213 -26
  38. package/dist/contextClient.js.map +1 -1
  39. package/dist/contradictions/index.d.ts +1 -0
  40. package/dist/contradictions/index.js +214 -27
  41. package/dist/contradictions/index.js.map +1 -1
  42. package/dist/control-plane.d.ts +1 -0
  43. package/dist/control-plane.js +213 -26
  44. package/dist/control-plane.js.map +1 -1
  45. package/dist/coreClient.d.ts +25 -1
  46. package/dist/coreClient.js +217 -27
  47. package/dist/coreClient.js.map +1 -1
  48. package/dist/decisions/index.d.ts +1 -0
  49. package/dist/decisions/index.js +214 -27
  50. package/dist/decisions/index.js.map +1 -1
  51. package/dist/decisionsClient.d.ts +1 -0
  52. package/dist/decisionsClient.js +213 -26
  53. package/dist/decisionsClient.js.map +1 -1
  54. package/dist/edges/index.d.ts +1 -0
  55. package/dist/edges/index.js +214 -27
  56. package/dist/edges/index.js.map +1 -1
  57. package/dist/embeddingsClient.d.ts +1 -0
  58. package/dist/embeddingsClient.js +213 -26
  59. package/dist/embeddingsClient.js.map +1 -1
  60. package/dist/eventingClient.d.ts +1 -0
  61. package/dist/eventingClient.js +213 -26
  62. package/dist/eventingClient.js.map +1 -1
  63. package/dist/eventsCore.d.ts +1 -0
  64. package/dist/eventsCore.js +213 -26
  65. package/dist/eventsCore.js.map +1 -1
  66. package/dist/evidence/index.d.ts +1 -0
  67. package/dist/evidence/index.js +214 -27
  68. package/dist/evidence/index.js.map +1 -1
  69. package/dist/evidenceClient.d.ts +1 -0
  70. package/dist/evidenceClient.js +213 -26
  71. package/dist/evidenceClient.js.map +1 -1
  72. package/dist/functionSurface.d.ts +1 -0
  73. package/dist/functionSurface.js +213 -26
  74. package/dist/functionSurface.js.map +1 -1
  75. package/dist/functionSurfaceClient.d.ts +1 -0
  76. package/dist/functionSurfaceClient.js +213 -26
  77. package/dist/functionSurfaceClient.js.map +1 -1
  78. package/dist/gatewayFacades.d.ts +1 -0
  79. package/dist/gatewayFacades.factories.d.ts +1 -0
  80. package/dist/gatewayFacades.factories.js +213 -26
  81. package/dist/gatewayFacades.factories.js.map +1 -1
  82. package/dist/gatewayFacades.js +213 -26
  83. package/dist/gatewayFacades.js.map +1 -1
  84. package/dist/graphAnalysisClient.d.ts +1 -0
  85. package/dist/graphAnalysisClient.js +213 -26
  86. package/dist/graphAnalysisClient.js.map +1 -1
  87. package/dist/graphClient.d.ts +1 -0
  88. package/dist/graphClient.js +213 -26
  89. package/dist/graphClient.js.map +1 -1
  90. package/dist/graphRecommendationsClient.d.ts +1 -0
  91. package/dist/graphRecommendationsClient.js +213 -26
  92. package/dist/graphRecommendationsClient.js.map +1 -1
  93. package/dist/graphStateClassifierClient.d.ts +1 -0
  94. package/dist/graphStateClassifierClient.js +213 -26
  95. package/dist/graphStateClassifierClient.js.map +1 -1
  96. package/dist/harnessClient.d.ts +1 -0
  97. package/dist/harnessClient.js +213 -26
  98. package/dist/harnessClient.js.map +1 -1
  99. package/dist/identityClient.d.ts +1 -0
  100. package/dist/identityClient.js +213 -26
  101. package/dist/identityClient.js.map +1 -1
  102. package/dist/index.d.ts +2 -1
  103. package/dist/index.js +237 -28
  104. package/dist/index.js.map +1 -1
  105. package/dist/jobsClient.d.ts +1 -0
  106. package/dist/jobsClient.js +213 -26
  107. package/dist/jobsClient.js.map +1 -1
  108. package/dist/learningClient.d.ts +1 -0
  109. package/dist/learningClient.js +213 -26
  110. package/dist/learningClient.js.map +1 -1
  111. package/dist/lenses/index.d.ts +1 -0
  112. package/dist/lenses/index.js +214 -27
  113. package/dist/lenses/index.js.map +1 -1
  114. package/dist/mcpClient.d.ts +1 -0
  115. package/dist/mcpClient.js +213 -26
  116. package/dist/mcpClient.js.map +1 -1
  117. package/dist/modelRuntimeClient.d.ts +1 -0
  118. package/dist/modelRuntimeClient.js +213 -26
  119. package/dist/modelRuntimeClient.js.map +1 -1
  120. package/dist/nodes/index.d.ts +1 -0
  121. package/dist/nodes/index.js +214 -27
  122. package/dist/nodes/index.js.map +1 -1
  123. package/dist/ontologies/index.d.ts +1 -0
  124. package/dist/ontologies/index.js +214 -27
  125. package/dist/ontologies/index.js.map +1 -1
  126. package/dist/ontologyClient.d.ts +1 -0
  127. package/dist/ontologyClient.js +213 -26
  128. package/dist/ontologyClient.js.map +1 -1
  129. package/dist/ontologyLinksClient.d.ts +1 -0
  130. package/dist/ontologyLinksClient.js +213 -26
  131. package/dist/ontologyLinksClient.js.map +1 -1
  132. package/dist/orgGraphSearchClient.d.ts +1 -0
  133. package/dist/orgGraphSearchClient.js +213 -26
  134. package/dist/orgGraphSearchClient.js.map +1 -1
  135. package/dist/packsClient.d.ts +1 -0
  136. package/dist/packsClient.js +213 -26
  137. package/dist/packsClient.js.map +1 -1
  138. package/dist/policyClient.d.ts +1 -0
  139. package/dist/policyClient.js +213 -26
  140. package/dist/policyClient.js.map +1 -1
  141. package/dist/proof-attestation.json +45 -0
  142. package/dist/questions/index.d.ts +1 -0
  143. package/dist/questions/index.js +214 -27
  144. package/dist/questions/index.js.map +1 -1
  145. package/dist/reportsClient.d.ts +1 -0
  146. package/dist/reportsClient.js +213 -26
  147. package/dist/reportsClient.js.map +1 -1
  148. package/dist/schemaClient.d.ts +1 -0
  149. package/dist/schemaClient.js +213 -26
  150. package/dist/schemaClient.js.map +1 -1
  151. package/dist/sdkSurface.d.ts +1 -0
  152. package/dist/sourcesClient.d.ts +1 -0
  153. package/dist/sourcesClient.js +213 -26
  154. package/dist/sourcesClient.js.map +1 -1
  155. package/dist/telemetryClient.d.ts +1 -0
  156. package/dist/telemetryClient.js +213 -26
  157. package/dist/telemetryClient.js.map +1 -1
  158. package/dist/toolRegistryClient.d.ts +1 -0
  159. package/dist/toolRegistryClient.js +213 -26
  160. package/dist/toolRegistryClient.js.map +1 -1
  161. package/dist/topics/index.d.ts +1 -0
  162. package/dist/topics/index.js +214 -27
  163. package/dist/topics/index.js.map +1 -1
  164. package/dist/topicsClient.d.ts +1 -0
  165. package/dist/topicsClient.js +213 -26
  166. package/dist/topicsClient.js.map +1 -1
  167. package/dist/version.d.ts +1 -1
  168. package/dist/version.js +1 -1
  169. package/dist/version.js.map +1 -1
  170. package/dist/workflowClient.d.ts +1 -0
  171. package/dist/workflowClient.js +213 -26
  172. package/dist/workflowClient.js.map +1 -1
  173. package/dist/worktrees/index.d.ts +1 -0
  174. package/dist/worktrees/index.js +214 -27
  175. package/dist/worktrees/index.js.map +1 -1
  176. package/package.json +6 -5
@@ -1,5 +1,7 @@
1
+ import { createTelemetryExporterFromEnv, emitTelemetrySignal } from '@lucern/transport-core';
1
2
  import { redactDiagnosticValue } from '@lucern/transport-core/redaction';
2
3
  import { classifyRetry } from '@lucern/transport-core/transport';
4
+ import { Effect, Exit, Cause } from 'effect';
3
5
 
4
6
  // src/coreClient.ts
5
7
 
@@ -171,6 +173,33 @@ function createCanonicalAuthHeaders(authContext) {
171
173
  }
172
174
 
173
175
  // src/coreClient.ts
176
+ var DEFAULT_GATEWAY_TIMEOUT_MS = 15e3;
177
+ var DEFAULT_GATEWAY_MAX_RETRIES = 2;
178
+ var DEFAULT_ENV_TIMEOUT_MS = "LUCERN_REQUEST_TIMEOUT_MS";
179
+ var DEFAULT_ENV_MAX_RETRIES = "LUCERN_GATEWAY_MAX_RETRIES";
180
+ var ENV_TIMEOUT_BY_METHOD_PREFIX = "LUCERN_REQUEST_TIMEOUT_MS_";
181
+ var GatewayTimeoutError = class extends Error {
182
+ retryable = true;
183
+ timeoutMs;
184
+ constructor(timeoutMs) {
185
+ super(`Request timed out after ${timeoutMs}ms`);
186
+ this.name = "AbortError";
187
+ this.timeoutMs = timeoutMs;
188
+ }
189
+ };
190
+ var GatewayTransportError = class extends Error {
191
+ retryable;
192
+ cause;
193
+ constructor(message, options) {
194
+ super(message);
195
+ this.name = "GatewayTransportError";
196
+ this.retryable = options?.retryable ?? true;
197
+ this.cause = options?.cause;
198
+ }
199
+ };
200
+ function isGatewayRetryableError(error) {
201
+ return error instanceof GatewayTimeoutError && error.retryable || error instanceof GatewayTransportError && error.retryable || false;
202
+ }
174
203
  var LucernApiError = class extends Error {
175
204
  code;
176
205
  status;
@@ -237,6 +266,99 @@ function generatePortableRequestId() {
237
266
  8
238
267
  ).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
239
268
  }
269
+ function resolveEnvironment() {
270
+ const processEnv = typeof globalThis === "object" && globalThis !== null && "process" in globalThis ? globalThis.process : void 0;
271
+ const env = processEnv !== void 0 && typeof processEnv === "object" && processEnv !== null && typeof processEnv.env === "object" ? processEnv.env : void 0;
272
+ return {
273
+ get: (name) => {
274
+ const value = env?.[name];
275
+ return typeof value === "string" && value.length > 0 ? value : void 0;
276
+ }
277
+ };
278
+ }
279
+ function telemetryEnvironmentRecord(environment) {
280
+ const names = [
281
+ "LUCERN_TELEMETRY_ENABLED",
282
+ "AXIOM_TELEMETRY_ENABLED",
283
+ "LUCERN_AXIOM_TOKEN",
284
+ "AXIOM_TOKEN",
285
+ "LUCERN_AXIOM_EVENTS_DATASET",
286
+ "LUCERN_AXIOM_DATASET",
287
+ "AXIOM_EVENTS_DATASET",
288
+ "AXIOM_DATASET",
289
+ "LUCERN_AXIOM_API_URL",
290
+ "AXIOM_URL",
291
+ "LUCERN_ENVIRONMENT",
292
+ "NODE_ENV",
293
+ "LUCERN_RELEASE",
294
+ "SENTRY_RELEASE",
295
+ "VERCEL_GIT_COMMIT_SHA"
296
+ ];
297
+ return Object.fromEntries(
298
+ names.map((name) => [name, environment.get(name)])
299
+ );
300
+ }
301
+ function resolveRequestProfile(config, environment) {
302
+ const requestIdFactory = config.requestIdFactory ?? (() => generatePortableRequestId());
303
+ const parsedMaxRetries = parseIntegerFromString(
304
+ config.maxRetries,
305
+ environment.get(DEFAULT_ENV_MAX_RETRIES)
306
+ );
307
+ const parsedTimeoutMs = parseIntegerFromString(
308
+ config.timeoutMs,
309
+ environment.get(DEFAULT_ENV_TIMEOUT_MS)
310
+ );
311
+ const methodTimeouts = {
312
+ ...config.timeoutMsByMethod
313
+ };
314
+ for (const method of ["GET", "POST", "PUT", "PATCH", "DELETE"]) {
315
+ const envKey = `${ENV_TIMEOUT_BY_METHOD_PREFIX}${method}`;
316
+ const raw = environment.get(envKey);
317
+ if (!raw || methodTimeouts[method] !== void 0) {
318
+ continue;
319
+ }
320
+ const parsed = parseIntegerFromString(void 0, raw);
321
+ if (typeof parsed === "number") {
322
+ methodTimeouts[method] = parsed;
323
+ }
324
+ }
325
+ return {
326
+ maxRetries: parsedMaxRetries ?? DEFAULT_GATEWAY_MAX_RETRIES,
327
+ timeoutMs: parsedTimeoutMs ?? DEFAULT_GATEWAY_TIMEOUT_MS,
328
+ timeoutMsByMethod: methodTimeouts,
329
+ requestIdFactory
330
+ };
331
+ }
332
+ function createGatewayRuntime(config, environment) {
333
+ return {
334
+ fetch: config.fetchImpl ?? fetch,
335
+ now: () => Date.now(),
336
+ sleep: (ms) => delay(ms),
337
+ env: environment,
338
+ redaction: resolveRequestRedactionValue,
339
+ profile: resolveRequestProfile(config, environment)
340
+ };
341
+ }
342
+ function parseIntegerFromString(value, rawValue) {
343
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0) {
344
+ return value;
345
+ }
346
+ if (typeof rawValue !== "string" || !rawValue.trim()) {
347
+ return void 0;
348
+ }
349
+ const parsed = Number.parseInt(rawValue, 10);
350
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : void 0;
351
+ }
352
+ function resolveRequestRedactionValue(value) {
353
+ return redactDiagnosticValue(value);
354
+ }
355
+ function resolveGatewayBaseUrl(configBaseUrl, environment) {
356
+ const envBaseUrl = environment.get("LUCERN_API_URL") ?? environment.get("LUCERN_BASE_URL") ?? environment.get("LUCERN_GATEWAY_BASE_URL");
357
+ return (configBaseUrl ?? envBaseUrl ?? "").replace(/\/+$/, "");
358
+ }
359
+ function normalizeGatewayEnvironment(value) {
360
+ return value === "sandbox" || value === "production" ? value : void 0;
361
+ }
240
362
  function fallbackErrorCode(status) {
241
363
  if (status === 401) {
242
364
  return "AUTHENTICATION_REQUIRED";
@@ -275,10 +397,8 @@ function computeRetryDelayMs(args) {
275
397
  const jitterWindow = Math.max(250, Math.round(baseDelay * 0.25));
276
398
  return baseDelay + Math.round(Math.random() * jitterWindow);
277
399
  }
278
- function timeoutError(timeoutMs) {
279
- const error = new Error(`Request timed out after ${timeoutMs}ms`);
280
- error.name = "AbortError";
281
- return error;
400
+ function classifyGatewayErrorForRetry(error) {
401
+ return isGatewayRetryableError(error) || classifyRetry({ error }).retryable;
282
402
  }
283
403
  function isRecord(value) {
284
404
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -333,10 +453,18 @@ function cleanHeaderValue(value) {
333
453
  return normalized ? normalized : void 0;
334
454
  }
335
455
  function createGatewayRequestClient(config = {}) {
336
- const fetchImpl = config.fetchImpl ?? fetch;
337
- const baseUrl = config.baseUrl?.replace(/\/+$/, "") ?? "";
338
- const maxRetries = config.maxRetries ?? 2;
339
- const requestIdFactory = config.requestIdFactory ?? (() => generatePortableRequestId());
456
+ const env = resolveEnvironment();
457
+ const runtime = createGatewayRuntime(config, env);
458
+ const baseUrl = resolveGatewayBaseUrl(config.baseUrl, env);
459
+ const maxRetries = runtime.profile.maxRetries;
460
+ const requestIdFactory = runtime.profile.requestIdFactory;
461
+ const requestTimeoutByMethod = runtime.profile.timeoutMsByMethod;
462
+ const defaultRequestTimeoutMs = runtime.profile.timeoutMs;
463
+ const normalizedEnvironment = normalizeGatewayEnvironment(config.environment);
464
+ const telemetryExporter = config.telemetryEnabled === false ? null : config.telemetryExporter ?? createTelemetryExporterFromEnv(telemetryEnvironmentRecord(env), {
465
+ service: "lucern-sdk",
466
+ environment: normalizedEnvironment
467
+ });
340
468
  async function resolveAuthHeaders() {
341
469
  const provided = config.getAuthHeaders ? await config.getAuthHeaders() : {};
342
470
  const headers = new Headers(provided);
@@ -348,7 +476,7 @@ function createGatewayRequestClient(config = {}) {
348
476
  };
349
477
  setIfAbsent("x-lucern-key", config.apiKey);
350
478
  setIfAbsent("x-lucern-session-token", config.userToken);
351
- setIfAbsent("x-lucern-environment", config.environment);
479
+ setIfAbsent("x-lucern-environment", normalizedEnvironment);
352
480
  setIfAbsent("x-lucern-clerk-id", config.clerkId);
353
481
  setIfAbsent("x-lucern-user-id", config.userId ?? config.clerkId);
354
482
  setIfAbsent("x-lucern-deployment-host", config.deploymentHost);
@@ -363,19 +491,73 @@ function createGatewayRequestClient(config = {}) {
363
491
  return mergeHeaderRecord(base, createCanonicalAuthHeaders(authContext));
364
492
  }
365
493
  async function fetchWithTimeout(url, init, timeoutMs) {
494
+ const normalizeTransportError = (error, isTimeout) => {
495
+ if (isTimeout) {
496
+ return new GatewayTimeoutError(timeoutMs);
497
+ }
498
+ return error instanceof GatewayTimeoutError || error instanceof GatewayTransportError ? error : new GatewayTransportError(
499
+ error instanceof Error ? error.message : "Gateway transport error",
500
+ {
501
+ cause: error,
502
+ retryable: classifyGatewayErrorForRetry(error)
503
+ }
504
+ );
505
+ };
366
506
  const controller = new AbortController();
367
507
  const timer = setTimeout(() => controller.abort(), timeoutMs);
508
+ const requestEffect = Effect.tryPromise({
509
+ try: () => runtime.fetch(url, { ...init, signal: controller.signal }),
510
+ catch: (error) => normalizeTransportError(error, controller.signal.aborted)
511
+ });
368
512
  try {
369
- return await fetchImpl(url, { ...init, signal: controller.signal });
370
- } catch (error) {
371
- if (controller.signal.aborted) {
372
- throw timeoutError(timeoutMs);
513
+ const exit = await Effect.runPromiseExit(requestEffect);
514
+ if (Exit.isSuccess(exit)) {
515
+ return exit.value;
373
516
  }
374
- throw error;
517
+ const failure = Array.from(Cause.failures(exit.cause))[0];
518
+ if (failure !== void 0) {
519
+ throw failure;
520
+ }
521
+ throw Cause.squash(exit.cause);
375
522
  } finally {
376
523
  clearTimeout(timer);
377
524
  }
378
525
  }
526
+ async function emitSdkResponseTelemetry(context) {
527
+ const retry = classifyRetry({
528
+ status: context.status,
529
+ error: context.error,
530
+ retryAfter: context.retryAfterMs !== null && context.retryAfterMs !== void 0 ? String(context.retryAfterMs / 1e3) : void 0
531
+ });
532
+ await emitTelemetrySignal(telemetryExporter, {
533
+ signalType: "trace",
534
+ surface: "sdk-retry",
535
+ eventName: context.willRetry ? "sdk.retry" : context.error ? "sdk.request.error" : "sdk.request.complete",
536
+ severity: context.error ? context.willRetry ? "warn" : "error" : "info",
537
+ durationMs: context.durationMs,
538
+ metricName: "sdk.request.duration_ms",
539
+ metricValue: context.durationMs,
540
+ correlationId: context.correlationId ?? context.requestId,
541
+ policyTraceId: context.policyTraceId ?? null,
542
+ tenantId: context.headers.get("x-lucern-tenant-id") ?? context.headers.get("x-lucern-tenant") ?? void 0,
543
+ workspaceId: context.headers.get("x-lucern-workspace-id") ?? context.headers.get("x-lucern-workspace") ?? void 0,
544
+ attributes: {
545
+ service: "lucern-sdk",
546
+ operation: "gateway.request",
547
+ path: context.path,
548
+ httpMethod: context.method,
549
+ httpStatus: context.status,
550
+ attempt: context.attempt,
551
+ maxRetries: context.maxRetries,
552
+ retryReason: retry.reason,
553
+ retryAfterMs: context.retryAfterMs ?? retry.retryAfterMs,
554
+ willRetry: context.willRetry,
555
+ retryable: retry.retryable,
556
+ errorName: context.error instanceof Error ? context.error.name : void 0,
557
+ errorMessage: context.error instanceof Error ? context.error.message : void 0
558
+ }
559
+ });
560
+ }
379
561
  async function parsePayload(response) {
380
562
  const text = await response.text();
381
563
  if (!text) {
@@ -391,11 +573,11 @@ function createGatewayRequestClient(config = {}) {
391
573
  if (typeof requestTimeoutMs === "number") {
392
574
  return requestTimeoutMs;
393
575
  }
394
- const methodTimeoutMs = config.timeoutMsByMethod?.[method];
576
+ const methodTimeoutMs = requestTimeoutByMethod?.[method];
395
577
  if (typeof methodTimeoutMs === "number") {
396
578
  return methodTimeoutMs;
397
579
  }
398
- return config.timeoutMs ?? 15e3;
580
+ return defaultRequestTimeoutMs;
399
581
  }
400
582
  function tryParseGatewayEnvelopeJson(text) {
401
583
  const trimmed = text.trim();
@@ -416,8 +598,8 @@ function createGatewayRequestClient(config = {}) {
416
598
  const legacyError = failure && isRecord(failure.error) ? failure.error : failure?.legacyError;
417
599
  const correlationId = failure?.correlationId ?? args.response.headers.get("x-lucern-correlation-id")?.trim() ?? args.requestId;
418
600
  const policyTraceId = failure?.policyTraceId ?? args.response.headers.get("x-lucern-policy-trace-id")?.trim() ?? null;
419
- const details = redactJsonDiagnosticValue(
420
- failure?.details ?? legacyError?.details
601
+ const details = runtime.redaction(
602
+ redactJsonDiagnosticValue(failure?.details ?? legacyError?.details)
421
603
  );
422
604
  const policySummary = readPolicySummaryFromDetails(details);
423
605
  const failureMessage = typeof failure?.error === "string" ? failure.error : legacyError?.message;
@@ -487,7 +669,7 @@ function createGatewayRequestClient(config = {}) {
487
669
  failure
488
670
  });
489
671
  const willRetry = attempt < maxRetries && retry.retryable;
490
- await config.onResponse?.({
672
+ const responseContext2 = {
491
673
  ...hookRequestContext,
492
674
  durationMs: Date.now() - startedAt,
493
675
  status: response.status,
@@ -497,7 +679,9 @@ function createGatewayRequestClient(config = {}) {
497
679
  policyTraceId: apiError.policyTraceId ?? null,
498
680
  retryAfterMs,
499
681
  willRetry
500
- });
682
+ };
683
+ await config.onResponse?.(responseContext2);
684
+ await emitSdkResponseTelemetry(responseContext2);
501
685
  if (willRetry) {
502
686
  lastError = apiError;
503
687
  await delay(
@@ -512,7 +696,7 @@ function createGatewayRequestClient(config = {}) {
512
696
  throw apiError;
513
697
  }
514
698
  const successPayload = payload;
515
- await config.onResponse?.({
699
+ const responseContext = {
516
700
  ...hookRequestContext,
517
701
  durationMs: Date.now() - startedAt,
518
702
  status: response.status,
@@ -522,22 +706,25 @@ function createGatewayRequestClient(config = {}) {
522
706
  idempotentReplay: successPayload.idempotentReplay,
523
707
  retryAfterMs,
524
708
  willRetry: false
525
- });
709
+ };
710
+ await config.onResponse?.(responseContext);
711
+ await emitSdkResponseTelemetry(responseContext);
526
712
  return successPayload;
527
713
  } catch (fetchError) {
528
714
  if (fetchError instanceof LucernApiError) {
529
715
  throw fetchError;
530
716
  }
531
- const retry = classifyRetry({ error: fetchError });
532
- const willRetry = attempt < maxRetries && retry.retryable;
533
- await config.onResponse?.({
717
+ const willRetry = attempt < maxRetries && classifyGatewayErrorForRetry(fetchError);
718
+ const responseContext = {
534
719
  ...hookRequestContext,
535
720
  durationMs: Date.now() - startedAt,
536
721
  error: fetchError,
537
722
  correlationId: requestId,
538
723
  policyTraceId: null,
539
724
  willRetry
540
- });
725
+ };
726
+ await config.onResponse?.(responseContext);
727
+ await emitSdkResponseTelemetry(responseContext);
541
728
  lastError = fetchError;
542
729
  if (willRetry) {
543
730
  await delay(computeRetryDelayMs({ attempt }));