agentid-sdk 0.1.25 → 0.1.28

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.
@@ -1149,6 +1149,79 @@ var PIIManager = class {
1149
1149
  }
1150
1150
  };
1151
1151
 
1152
+ // src/context-intent.ts
1153
+ var EDUCATIONAL_MARKERS = [
1154
+ "explain",
1155
+ "analysis",
1156
+ "analyze",
1157
+ "analyse",
1158
+ "review",
1159
+ "summarize",
1160
+ "summarise",
1161
+ "summary",
1162
+ "plain-language explanation",
1163
+ "red flags",
1164
+ "security workshop",
1165
+ "training",
1166
+ "lesson",
1167
+ "quoted attack",
1168
+ "quoted example",
1169
+ "attack example",
1170
+ "security event",
1171
+ "security log",
1172
+ "audit log",
1173
+ "incident report",
1174
+ "trace timeline"
1175
+ ];
1176
+ var WARNING_MARKERS = [
1177
+ "dangerous",
1178
+ "risky",
1179
+ "risk",
1180
+ "warning",
1181
+ "warn",
1182
+ "malicious",
1183
+ "security indicator",
1184
+ "red flags"
1185
+ ];
1186
+ var NON_EXECUTION_MARKERS = [
1187
+ "do not execute",
1188
+ "do not run",
1189
+ "do not return commands",
1190
+ "do not output commands",
1191
+ "do not provide executable steps",
1192
+ "for explanation only",
1193
+ "non-executable"
1194
+ ];
1195
+ function normalizeIntentInput(input) {
1196
+ return input.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/\s+/g, " ").trim();
1197
+ }
1198
+ function hasAnyMarker(input, markers) {
1199
+ return markers.some((marker) => input.includes(marker));
1200
+ }
1201
+ function classifyInjectionContextIntent(input) {
1202
+ const normalized = normalizeIntentInput(input ?? "");
1203
+ if (!normalized) {
1204
+ return {
1205
+ educational: false,
1206
+ quotedExample: false,
1207
+ warningOnly: false,
1208
+ nonExecutableConstraint: false,
1209
+ promptInjectionExempt: false
1210
+ };
1211
+ }
1212
+ const educational = hasAnyMarker(normalized, EDUCATIONAL_MARKERS);
1213
+ const warningOnly = hasAnyMarker(normalized, WARNING_MARKERS);
1214
+ const nonExecutableConstraint = hasAnyMarker(normalized, NON_EXECUTION_MARKERS);
1215
+ const quotedExample = /["'`]/.test(input) || normalized.includes("quoted") || normalized.includes("quote") || normalized.includes("phrase") || normalized.includes("attack example");
1216
+ return {
1217
+ educational,
1218
+ quotedExample,
1219
+ warningOnly,
1220
+ nonExecutableConstraint,
1221
+ promptInjectionExempt: educational && (quotedExample || warningOnly || nonExecutableConstraint || normalized.includes("prompt injection indicator") || normalized.includes("jailbreak prompt"))
1222
+ };
1223
+ }
1224
+
1152
1225
  // src/security.ts
1153
1226
  var MAX_ANALYSIS_WINDOW = 8192;
1154
1227
  var WINDOW_SLICE_SIZE = 4e3;
@@ -1311,6 +1384,10 @@ function findRegexMatch(prompt) {
1311
1384
  if (!prompt) {
1312
1385
  return null;
1313
1386
  }
1387
+ const contextIntent = classifyInjectionContextIntent(prompt);
1388
+ if (contextIntent.promptInjectionExempt) {
1389
+ return null;
1390
+ }
1314
1391
  const normalizedPrompt = normalizeForHeuristics(prompt);
1315
1392
  for (const rule of HEURISTIC_RULES) {
1316
1393
  try {
@@ -1444,7 +1521,8 @@ async function reportSecurityEvent(options) {
1444
1521
  language: options.language,
1445
1522
  ai_scan_status: options.aiStatus ?? null,
1446
1523
  reason: options.reason ?? null,
1447
- client_event_id: eventId
1524
+ client_event_id: eventId,
1525
+ ...options.telemetryMetadata ?? {}
1448
1526
  };
1449
1527
  if (options.storePii) {
1450
1528
  metadata.snippet = snippet;
@@ -1492,7 +1570,8 @@ var InjectionScanner = class _InjectionScanner {
1492
1570
  source: options?.source,
1493
1571
  systemId: options?.systemId,
1494
1572
  eventId: options?.eventId,
1495
- clientEventId: options?.clientEventId
1573
+ clientEventId: options?.clientEventId,
1574
+ telemetryMetadata: options?.telemetryMetadata
1496
1575
  });
1497
1576
  }
1498
1577
  async scan(params) {
@@ -1504,6 +1583,11 @@ var InjectionScanner = class _InjectionScanner {
1504
1583
  const storePii = params.storePii === true;
1505
1584
  const aiScanEnabled = params.aiScanEnabled !== false;
1506
1585
  const language = detectLanguageTag(prompt);
1586
+ const scanStartedAt = Date.now();
1587
+ const buildTelemetryMetadata = () => ({
1588
+ ...params.telemetryMetadata ?? {},
1589
+ sdk_local_scan_ms: Math.max(0, Date.now() - scanStartedAt)
1590
+ });
1507
1591
  const regexMatch = findRegexMatch(prompt);
1508
1592
  if (regexMatch) {
1509
1593
  await reportSecurityEvent({
@@ -1518,7 +1602,8 @@ var InjectionScanner = class _InjectionScanner {
1518
1602
  language,
1519
1603
  systemId: params.systemId,
1520
1604
  eventId: params.eventId,
1521
- clientEventId: params.clientEventId
1605
+ clientEventId: params.clientEventId,
1606
+ telemetryMetadata: buildTelemetryMetadata()
1522
1607
  });
1523
1608
  throw new Error(`AgentID: Prompt injection blocked (${regexMatch.rule})`);
1524
1609
  }
@@ -1542,7 +1627,8 @@ var InjectionScanner = class _InjectionScanner {
1542
1627
  reason: "ai_scan_disabled_for_high_risk_language",
1543
1628
  systemId: params.systemId,
1544
1629
  eventId: params.eventId,
1545
- clientEventId: params.clientEventId
1630
+ clientEventId: params.clientEventId,
1631
+ telemetryMetadata: buildTelemetryMetadata()
1546
1632
  });
1547
1633
  return;
1548
1634
  }
@@ -1564,7 +1650,8 @@ var InjectionScanner = class _InjectionScanner {
1564
1650
  reason: aiResult.reason,
1565
1651
  systemId: params.systemId,
1566
1652
  eventId: params.eventId,
1567
- clientEventId: params.clientEventId
1653
+ clientEventId: params.clientEventId,
1654
+ telemetryMetadata: buildTelemetryMetadata()
1568
1655
  });
1569
1656
  return;
1570
1657
  }
@@ -1583,7 +1670,8 @@ var InjectionScanner = class _InjectionScanner {
1583
1670
  reason: aiResult.reason,
1584
1671
  systemId: params.systemId,
1585
1672
  eventId: params.eventId,
1586
- clientEventId: params.clientEventId
1673
+ clientEventId: params.clientEventId,
1674
+ telemetryMetadata: buildTelemetryMetadata()
1587
1675
  });
1588
1676
  throw new Error(`AgentID: Prompt injection blocked (${aiResult.reason})`);
1589
1677
  }
@@ -1595,7 +1683,7 @@ function getInjectionScanner() {
1595
1683
 
1596
1684
  // src/sdk-version.ts
1597
1685
  var FALLBACK_SDK_VERSION = "js-0.0.0-dev";
1598
- var AGENTID_SDK_VERSION_HEADER = "js-0.1.25".trim().length > 0 ? "js-0.1.25" : FALLBACK_SDK_VERSION;
1686
+ var AGENTID_SDK_VERSION_HEADER = "js-0.1.28".trim().length > 0 ? "js-0.1.28" : FALLBACK_SDK_VERSION;
1599
1687
 
1600
1688
  // src/local-security-enforcer.ts
1601
1689
  var DEFAULT_FAIL_OPEN_CONFIG = {
@@ -1708,8 +1796,8 @@ var LocalSecurityEnforcer = class {
1708
1796
 
1709
1797
  // src/capability-config.ts
1710
1798
  var CONFIG_TTL_MS = 5 * 60 * 1e3;
1711
- var CONFIG_TIMEOUT_MS = 8e3;
1712
- var CONFIG_RETRY_DELAY_MS = 1e3;
1799
+ var CONFIG_TIMEOUT_MS = 1500;
1800
+ var CONFIG_RETRY_DELAY_MS = 150;
1713
1801
  var MAX_CAPABILITY_CACHE_ENTRIES = 500;
1714
1802
  var CapabilityConfigFetchError = class extends Error {
1715
1803
  constructor(message, params) {
@@ -2050,6 +2138,19 @@ function normalizeIngestTimeoutMs(value) {
2050
2138
  }
2051
2139
  return rounded;
2052
2140
  }
2141
+ function setFiniteDurationMetadata(metadata, key, value) {
2142
+ if (typeof value === "number" && Number.isFinite(value)) {
2143
+ metadata[key] = Math.max(0, Math.trunc(value));
2144
+ }
2145
+ }
2146
+ function buildSdkTimingMetadata(params) {
2147
+ const metadata = {};
2148
+ setFiniteDurationMetadata(metadata, "sdk_config_fetch_ms", params.sdkConfigFetchMs);
2149
+ setFiniteDurationMetadata(metadata, "sdk_local_scan_ms", params.sdkLocalScanMs);
2150
+ setFiniteDurationMetadata(metadata, "sdk_guard_ms", params.sdkGuardMs);
2151
+ setFiniteDurationMetadata(metadata, "sdk_ingest_ms", params.sdkIngestMs);
2152
+ return metadata;
2153
+ }
2053
2154
  function resolveConfiguredApiKey(value) {
2054
2155
  const explicit = typeof value === "string" ? value.trim() : "";
2055
2156
  const fromEnv = globalThis.process?.env?.AGENTID_API_KEY ?? "";
@@ -2063,6 +2164,9 @@ function isInfrastructureGuardReason(reason) {
2063
2164
  if (!reason) return false;
2064
2165
  return reason === "system_failure" || reason === "system_failure_db_unavailable" || reason === "logging_failed" || reason === "server_error" || reason === "guard_unreachable" || reason === "api_key_pepper_missing" || reason === "encryption_key_missing";
2065
2166
  }
2167
+ function isGuardFailureEligibleForLocalFallback(reason) {
2168
+ return reason === "network_error_strict_mode" || reason === "server_error" || isInfrastructureGuardReason(reason);
2169
+ }
2066
2170
  function isFailCloseIngestReason(reason) {
2067
2171
  if (!reason) return false;
2068
2172
  return reason === "system_failure" || reason === "system_failure_db_unavailable" || reason === "server_error" || reason === "redis_rate_limit_unavailable" || reason === "redis_quota_unavailable" || reason === "redis_token_quota_unavailable";
@@ -2272,6 +2376,7 @@ var AgentID = class {
2272
2376
  this.baseUrl = normalizeBaseUrl3(config.baseUrl ?? "https://app.getagentid.com/api/v1");
2273
2377
  this.configuredPiiMasking = typeof config.piiMasking === "boolean" ? config.piiMasking : null;
2274
2378
  this.checkInjection = config.checkInjection !== false;
2379
+ this.clientFastFail = config.clientFastFail === true || config.client_fast_fail === true;
2275
2380
  this.aiScanEnabled = config.aiScanEnabled !== false;
2276
2381
  this.storePii = config.storePii === true;
2277
2382
  this.strictMode = config.strictMode === true;
@@ -2374,6 +2479,14 @@ var AgentID = class {
2374
2479
  force
2375
2480
  });
2376
2481
  }
2482
+ async getCapabilityConfigWithTelemetry(force = false, options) {
2483
+ const configStartedAt = Date.now();
2484
+ const capabilityConfig = await this.getCapabilityConfig(force, options);
2485
+ return {
2486
+ capabilityConfig,
2487
+ sdkConfigFetchMs: Math.max(0, Date.now() - configStartedAt)
2488
+ };
2489
+ }
2377
2490
  getCachedCapabilityConfig(options) {
2378
2491
  const effectiveApiKey = this.resolveApiKey(options?.apiKey);
2379
2492
  return getCachedCapabilityConfig({
@@ -2417,37 +2530,45 @@ var AgentID = class {
2417
2530
  }
2418
2531
  return config.block_on_heuristic;
2419
2532
  }
2420
- async prepareInputForDispatch(params, options) {
2421
- const effectiveApiKey = this.resolveApiKey(options?.apiKey);
2422
- const capabilityConfig = await this.getCapabilityConfig(false, options);
2423
- if (!params.skipInjectionScan && params.input && this.shouldRunLocalInjectionScan(capabilityConfig)) {
2533
+ async applyLocalPolicyChecks(params) {
2534
+ const localScanStartedAt = Date.now();
2535
+ if (params.runPromptInjectionCheck && params.input && this.shouldRunLocalInjectionScan(params.capabilityConfig)) {
2424
2536
  await this.injectionScanner.scan({
2425
2537
  prompt: params.input,
2426
- apiKey: effectiveApiKey,
2538
+ apiKey: params.apiKey,
2427
2539
  baseUrl: this.baseUrl,
2428
2540
  aiScanEnabled: this.aiScanEnabled,
2429
2541
  storePii: this.storePii,
2430
2542
  piiManager: this.pii,
2431
- source: "js_sdk"
2543
+ source: "js_sdk",
2544
+ systemId: params.systemId,
2545
+ eventId: params.clientEventId,
2546
+ clientEventId: params.clientEventId,
2547
+ telemetryMetadata: buildSdkTimingMetadata({
2548
+ sdkConfigFetchMs: params.sdkConfigFetchMs
2549
+ })
2432
2550
  });
2433
2551
  }
2434
2552
  try {
2435
2553
  const enforced = this.localEnforcer.enforce({
2436
2554
  input: params.input,
2437
2555
  stream: params.stream,
2438
- config: capabilityConfig
2556
+ config: params.capabilityConfig
2439
2557
  });
2558
+ const sdkLocalScanMs = Math.max(0, Date.now() - localScanStartedAt);
2440
2559
  for (const event of enforced.events) {
2441
2560
  this.logSecurityPolicyViolation({
2442
2561
  systemId: params.systemId,
2443
2562
  violationType: event.violationType,
2444
2563
  actionTaken: event.actionTaken,
2445
- apiKey: effectiveApiKey
2564
+ apiKey: params.apiKey,
2565
+ sdkConfigFetchMs: params.sdkConfigFetchMs,
2566
+ sdkLocalScanMs
2446
2567
  });
2447
2568
  }
2448
2569
  return {
2449
2570
  sanitizedInput: enforced.sanitizedInput,
2450
- capabilityConfig
2571
+ sdkLocalScanMs
2451
2572
  };
2452
2573
  } catch (error) {
2453
2574
  if (error instanceof SecurityPolicyViolationError) {
@@ -2455,17 +2576,76 @@ var AgentID = class {
2455
2576
  systemId: params.systemId,
2456
2577
  violationType: error.violationType,
2457
2578
  actionTaken: error.actionTaken,
2458
- apiKey: effectiveApiKey
2579
+ apiKey: params.apiKey,
2580
+ sdkConfigFetchMs: params.sdkConfigFetchMs,
2581
+ sdkLocalScanMs: Math.max(0, Date.now() - localScanStartedAt)
2459
2582
  });
2460
2583
  }
2461
2584
  throw error;
2462
2585
  }
2463
2586
  }
2587
+ async prepareInputForDispatch(params, options) {
2588
+ const effectiveApiKey = this.resolveApiKey(options?.apiKey);
2589
+ const { capabilityConfig, sdkConfigFetchMs } = await this.getCapabilityConfigWithTelemetry(
2590
+ false,
2591
+ options
2592
+ );
2593
+ if (!this.clientFastFail) {
2594
+ return {
2595
+ sanitizedInput: params.input,
2596
+ capabilityConfig,
2597
+ sdkConfigFetchMs,
2598
+ sdkLocalScanMs: 0
2599
+ };
2600
+ }
2601
+ const enforced = await this.applyLocalPolicyChecks({
2602
+ input: params.input,
2603
+ systemId: params.systemId,
2604
+ stream: params.stream,
2605
+ capabilityConfig,
2606
+ apiKey: effectiveApiKey,
2607
+ clientEventId: params.clientEventId,
2608
+ sdkConfigFetchMs,
2609
+ runPromptInjectionCheck: !params.skipInjectionScan
2610
+ });
2611
+ return {
2612
+ sanitizedInput: enforced.sanitizedInput,
2613
+ capabilityConfig,
2614
+ sdkConfigFetchMs,
2615
+ sdkLocalScanMs: enforced.sdkLocalScanMs
2616
+ };
2617
+ }
2618
+ async applyLocalFallbackForGuardFailure(params, options) {
2619
+ const effectiveApiKey = this.resolveApiKey(options?.apiKey);
2620
+ const resolvedConfig = params.capabilityConfig && typeof params.sdkConfigFetchMs === "number" ? {
2621
+ capabilityConfig: params.capabilityConfig,
2622
+ sdkConfigFetchMs: params.sdkConfigFetchMs
2623
+ } : await this.getCapabilityConfigWithTelemetry(false, options);
2624
+ const enforced = await this.applyLocalPolicyChecks({
2625
+ input: params.input,
2626
+ systemId: params.systemId,
2627
+ stream: params.stream,
2628
+ capabilityConfig: resolvedConfig.capabilityConfig,
2629
+ apiKey: effectiveApiKey,
2630
+ clientEventId: params.clientEventId,
2631
+ sdkConfigFetchMs: resolvedConfig.sdkConfigFetchMs,
2632
+ runPromptInjectionCheck: true
2633
+ });
2634
+ return {
2635
+ sanitizedInput: enforced.sanitizedInput,
2636
+ capabilityConfig: resolvedConfig.capabilityConfig,
2637
+ sdkConfigFetchMs: resolvedConfig.sdkConfigFetchMs,
2638
+ sdkLocalScanMs: enforced.sdkLocalScanMs
2639
+ };
2640
+ }
2464
2641
  async scanPromptInjection(input, options) {
2465
2642
  if (!input) {
2466
2643
  return;
2467
2644
  }
2468
- const capabilityConfig = await this.getCapabilityConfig(false, options);
2645
+ const { capabilityConfig, sdkConfigFetchMs } = await this.getCapabilityConfigWithTelemetry(
2646
+ false,
2647
+ options
2648
+ );
2469
2649
  if (!this.shouldRunLocalInjectionScan(capabilityConfig)) {
2470
2650
  return;
2471
2651
  }
@@ -2480,7 +2660,10 @@ var AgentID = class {
2480
2660
  source: "js_sdk",
2481
2661
  systemId: options?.systemId,
2482
2662
  eventId: options?.clientEventId,
2483
- clientEventId: options?.clientEventId
2663
+ clientEventId: options?.clientEventId,
2664
+ telemetryMetadata: buildSdkTimingMetadata({
2665
+ sdkConfigFetchMs
2666
+ })
2484
2667
  });
2485
2668
  }
2486
2669
  withMaskedOpenAIRequest(req, maskedText) {
@@ -2529,7 +2712,11 @@ var AgentID = class {
2529
2712
  system_id: params.systemId,
2530
2713
  violation_type: params.violationType,
2531
2714
  input_snippet: "[REDACTED_SAMPLE]",
2532
- action_taken: params.actionTaken
2715
+ action_taken: params.actionTaken,
2716
+ ...buildSdkTimingMetadata({
2717
+ sdkConfigFetchMs: params.sdkConfigFetchMs,
2718
+ sdkLocalScanMs: params.sdkLocalScanMs
2719
+ })
2533
2720
  }
2534
2721
  }, { apiKey: params.apiKey });
2535
2722
  }
@@ -2555,6 +2742,42 @@ var AgentID = class {
2555
2742
  { apiKey: params.apiKey }
2556
2743
  );
2557
2744
  }
2745
+ async finalizeIngestTelemetry(params) {
2746
+ const controller = new AbortController();
2747
+ const timeoutId = setTimeout(
2748
+ () => controller.abort(),
2749
+ Math.min(this.ingestTimeoutMs, 2e3)
2750
+ );
2751
+ try {
2752
+ const response = await fetch(`${this.baseUrl}/ingest/finalize`, {
2753
+ method: "POST",
2754
+ headers: {
2755
+ "Content-Type": "application/json",
2756
+ "x-agentid-api-key": params.apiKey,
2757
+ "x-correlation-id": params.clientEventId,
2758
+ "X-AgentID-SDK-Version": AGENTID_SDK_VERSION_HEADER
2759
+ },
2760
+ body: JSON.stringify({
2761
+ client_event_id: params.clientEventId,
2762
+ system_id: params.systemId,
2763
+ sdk_ingest_ms: params.sdkIngestMs
2764
+ }),
2765
+ signal: controller.signal
2766
+ });
2767
+ if (!response.ok) {
2768
+ console.warn(
2769
+ `[AgentID] Ingest telemetry finalize failed (status=${response.status}, client_event_id=${params.clientEventId}).`
2770
+ );
2771
+ }
2772
+ } catch (error) {
2773
+ const label = error && typeof error === "object" && error.name === "AbortError" ? "timeout" : "network";
2774
+ console.warn(
2775
+ `[AgentID] Ingest telemetry finalize failed (${label}, client_event_id=${params.clientEventId}).`
2776
+ );
2777
+ } finally {
2778
+ clearTimeout(timeoutId);
2779
+ }
2780
+ }
2558
2781
  /**
2559
2782
  * GUARD: Checks limits, PII, and security before execution.
2560
2783
  * strictMode=false (default): FAIL-OPEN on connectivity/timeouts.
@@ -2712,15 +2935,16 @@ var AgentID = class {
2712
2935
  }
2713
2936
  return withGuardLatency({ allowed: true, reason: "guard_unreachable" });
2714
2937
  }
2715
- async sendIngest(params, options) {
2938
+ async sendIngest(params, options, internal) {
2939
+ const ingestStartedAt = Date.now();
2716
2940
  const effectiveApiKey = this.resolveApiKey(options?.apiKey);
2717
- const eventId = createEventId2(params.event_id);
2941
+ const transportEventId = createEventId2(internal?.transportEventId ?? params.event_id);
2718
2942
  const timestamp = params.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
2719
2943
  const metadata = {
2720
2944
  ...params.metadata ?? {}
2721
2945
  };
2722
2946
  const metadataClientEventId = typeof metadata.client_event_id === "string" && isUuidLike2(metadata.client_event_id) ? metadata.client_event_id : null;
2723
- const canonicalClientEventId = metadataClientEventId ?? eventId;
2947
+ const canonicalClientEventId = metadataClientEventId ?? transportEventId;
2724
2948
  if (!metadataClientEventId) {
2725
2949
  metadata.client_event_id = canonicalClientEventId;
2726
2950
  }
@@ -2730,7 +2954,7 @@ var AgentID = class {
2730
2954
  void this.getCapabilityConfig(false, { apiKey: effectiveApiKey }).catch(() => void 0);
2731
2955
  const payload = {
2732
2956
  ...params,
2733
- event_id: canonicalClientEventId,
2957
+ event_id: internal?.transportEventId ? transportEventId : canonicalClientEventId,
2734
2958
  timestamp,
2735
2959
  input: sanitizeIngestText(params.input),
2736
2960
  output: sanitizeIngestText(params.output),
@@ -2755,6 +2979,14 @@ var AgentID = class {
2755
2979
  });
2756
2980
  const responseBody = await safeReadJson2(response);
2757
2981
  if (response.ok) {
2982
+ if (!internal?.disableFinalize) {
2983
+ void this.finalizeIngestTelemetry({
2984
+ apiKey: effectiveApiKey,
2985
+ clientEventId: canonicalClientEventId,
2986
+ systemId: params.system_id,
2987
+ sdkIngestMs: Math.max(0, Date.now() - ingestStartedAt)
2988
+ });
2989
+ }
2758
2990
  return { ok: true, status: response.status, reason: null };
2759
2991
  }
2760
2992
  const reason = responseBody && typeof responseBody === "object" && typeof responseBody.reason === "string" ? responseBody.reason ?? null : null;
@@ -2815,6 +3047,13 @@ var AgentID = class {
2815
3047
  }
2816
3048
  return text;
2817
3049
  }
3050
+ extractStreamChunkUsage(chunk) {
3051
+ if (!chunk || typeof chunk !== "object") {
3052
+ return void 0;
3053
+ }
3054
+ const usage = chunk.usage;
3055
+ return usage && typeof usage === "object" && !Array.isArray(usage) ? usage : void 0;
3056
+ }
2818
3057
  wrapCompletion(completion) {
2819
3058
  if (typeof completion === "string") {
2820
3059
  const masked = this.pii.anonymize(completion);
@@ -2838,7 +3077,9 @@ var AgentID = class {
2838
3077
  const source = completion;
2839
3078
  const collector = createCompletionChunkCollector();
2840
3079
  const extractStreamChunkText = this.extractStreamChunkText.bind(this);
3080
+ const extractStreamChunkUsage = this.extractStreamChunkUsage.bind(this);
2841
3081
  const piiManager = this.pii;
3082
+ let lastUsage;
2842
3083
  let resolveDone = null;
2843
3084
  let rejectDone = null;
2844
3085
  const done = new Promise((resolve, reject) => {
@@ -2853,6 +3094,10 @@ var AgentID = class {
2853
3094
  if (chunkText) {
2854
3095
  await collector.push(chunkText);
2855
3096
  }
3097
+ const chunkUsage = extractStreamChunkUsage(chunk);
3098
+ if (chunkUsage) {
3099
+ lastUsage = chunkUsage;
3100
+ }
2856
3101
  yield chunk;
2857
3102
  }
2858
3103
  await collector.close();
@@ -2862,7 +3107,8 @@ var AgentID = class {
2862
3107
  mode: "static",
2863
3108
  rawOutput,
2864
3109
  transformedOutput: masked.maskedText,
2865
- outputMasked: masked.maskedText !== rawOutput
3110
+ outputMasked: masked.maskedText !== rawOutput,
3111
+ usage: lastUsage
2866
3112
  });
2867
3113
  } catch (error) {
2868
3114
  await collector.abort(error);
@@ -2912,7 +3158,7 @@ var AgentID = class {
2912
3158
  * Wrap an OpenAI client once; AgentID will automatically:
2913
3159
  * - run guard() before chat.completions.create
2914
3160
  * - measure latency
2915
- * - fire-and-forget ingest logging
3161
+ * - persist ingest telemetry for the wrapped call
2916
3162
  */
2917
3163
  wrapOpenAI(openai, options) {
2918
3164
  const systemId = options.system_id;
@@ -2955,20 +3201,19 @@ var AgentID = class {
2955
3201
  let createArgs = normalizedCreateArgs;
2956
3202
  let mapping = {};
2957
3203
  let shouldDeanonymize = false;
3204
+ let sdkConfigFetchMs = 0;
3205
+ let sdkLocalScanMs = 0;
2958
3206
  if (userText) {
2959
- await this.scanPromptInjection(userText, {
2960
- ...requestOptions,
2961
- clientEventId,
2962
- systemId
2963
- });
2964
3207
  const prepared = await this.prepareInputForDispatch({
2965
3208
  input: userText,
2966
3209
  systemId,
2967
3210
  stream,
2968
- skipInjectionScan: true
3211
+ clientEventId
2969
3212
  }, requestOptions);
2970
3213
  capabilityConfig = prepared.capabilityConfig;
2971
3214
  maskedText = prepared.sanitizedInput;
3215
+ sdkConfigFetchMs = prepared.sdkConfigFetchMs ?? 0;
3216
+ sdkLocalScanMs = prepared.sdkLocalScanMs ?? 0;
2972
3217
  if (maskedText !== userText) {
2973
3218
  maskedReq = this.withMaskedOpenAIRequest(
2974
3219
  req,
@@ -3009,14 +3254,46 @@ var AgentID = class {
3009
3254
  user_id: options.user_id,
3010
3255
  client_event_id: clientEventId,
3011
3256
  expected_languages: expectedLanguages,
3257
+ request_identity: options.request_identity,
3012
3258
  client_capabilities: this.buildClientCapabilities(
3013
3259
  "openai",
3014
3260
  false,
3015
3261
  capabilityConfig
3016
3262
  )
3017
3263
  }, requestOptions);
3264
+ let localFallbackApplied = false;
3265
+ let localFallbackReason = null;
3018
3266
  if (!verdict.allowed) {
3019
- throw new SecurityBlockError(verdict.reason ?? "guard_denied");
3267
+ if (effectiveStrictMode && isGuardFailureEligibleForLocalFallback(verdict.reason)) {
3268
+ localFallbackApplied = true;
3269
+ localFallbackReason = verdict.reason ?? "guard_unreachable";
3270
+ if (sdkLocalScanMs === 0) {
3271
+ const fallback = await this.applyLocalPolicyChecks({
3272
+ input: maskedText,
3273
+ systemId,
3274
+ stream,
3275
+ capabilityConfig,
3276
+ apiKey: effectiveApiKey,
3277
+ clientEventId,
3278
+ sdkConfigFetchMs,
3279
+ runPromptInjectionCheck: true
3280
+ });
3281
+ maskedText = fallback.sanitizedInput;
3282
+ sdkLocalScanMs = fallback.sdkLocalScanMs;
3283
+ }
3284
+ } else {
3285
+ throw new SecurityBlockError(verdict.reason ?? "guard_denied");
3286
+ }
3287
+ }
3288
+ const currentRequestInput = adapter.extractInput(maskedReq);
3289
+ if (maskedText !== currentRequestInput) {
3290
+ maskedReq = this.withMaskedOpenAIRequest(
3291
+ req,
3292
+ maskedText
3293
+ );
3294
+ const nextCreateArgs = [...normalizedCreateArgs];
3295
+ nextCreateArgs[0] = maskedReq;
3296
+ createArgs = nextCreateArgs;
3020
3297
  }
3021
3298
  const canonicalClientEventId = typeof verdict.client_event_id === "string" && isUuidLike2(verdict.client_event_id) ? verdict.client_event_id : clientEventId;
3022
3299
  const guardEventId = typeof verdict.guard_event_id === "string" && verdict.guard_event_id.length > 0 ? verdict.guard_event_id : null;
@@ -3053,10 +3330,11 @@ var AgentID = class {
3053
3330
  event_id: canonicalClientEventId,
3054
3331
  system_id: systemId,
3055
3332
  user_id: options.user_id,
3333
+ request_identity: options.request_identity,
3056
3334
  input: maskedText,
3057
3335
  output: outputForLog,
3058
3336
  model: adapter.getModelName(maskedReq),
3059
- usage: void 0,
3337
+ usage: result.usage,
3060
3338
  latency: modelLatencyMs2,
3061
3339
  event_type: "complete",
3062
3340
  metadata: {
@@ -3067,12 +3345,19 @@ var AgentID = class {
3067
3345
  simulated_decision: verdict.simulated_decision ?? null,
3068
3346
  simulated_output_decision: isShadowMode && result.outputMasked ? "masked" : "allowed",
3069
3347
  response_streamed: true,
3348
+ sdk_local_fallback_applied: localFallbackApplied,
3349
+ sdk_local_fallback_reason: localFallbackReason,
3070
3350
  guard_latency_ms: guardLatencyMs,
3071
3351
  model_latency_ms: modelLatencyMs2,
3072
3352
  total_pipeline_latency_ms: totalPipelineLatencyMs2,
3073
3353
  guard_event_id: guardEventId,
3074
3354
  client_event_id: canonicalClientEventId,
3075
- transparency
3355
+ transparency,
3356
+ ...buildSdkTimingMetadata({
3357
+ sdkConfigFetchMs,
3358
+ sdkLocalScanMs,
3359
+ sdkGuardMs: guardLatencyMs
3360
+ })
3076
3361
  },
3077
3362
  client_capabilities: this.buildClientCapabilities(
3078
3363
  "openai",
@@ -3112,6 +3397,7 @@ var AgentID = class {
3112
3397
  event_id: canonicalClientEventId,
3113
3398
  system_id: systemId,
3114
3399
  user_id: options.user_id,
3400
+ request_identity: options.request_identity,
3115
3401
  input: maskedText,
3116
3402
  output: outputForLog,
3117
3403
  model,
@@ -3126,12 +3412,19 @@ var AgentID = class {
3126
3412
  simulated_decision: verdict.simulated_decision ?? null,
3127
3413
  simulated_output_decision: isShadowMode && wrappedCompletion.outputMasked ? "masked" : "allowed",
3128
3414
  response_streamed: false,
3415
+ sdk_local_fallback_applied: localFallbackApplied,
3416
+ sdk_local_fallback_reason: localFallbackReason,
3129
3417
  guard_latency_ms: guardLatencyMs,
3130
3418
  model_latency_ms: modelLatencyMs,
3131
3419
  total_pipeline_latency_ms: totalPipelineLatencyMs,
3132
3420
  guard_event_id: guardEventId,
3133
3421
  client_event_id: canonicalClientEventId,
3134
- transparency
3422
+ transparency,
3423
+ ...buildSdkTimingMetadata({
3424
+ sdkConfigFetchMs,
3425
+ sdkLocalScanMs,
3426
+ sdkGuardMs: guardLatencyMs
3427
+ })
3135
3428
  },
3136
3429
  client_capabilities: this.buildClientCapabilities(
3137
3430
  "openai",
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as TransparencyMetadata } from './agentid-B5Y1g2Ko.mjs';
2
- export { A as AgentID, D as DependencyError, G as GuardParams, a as GuardResponse, L as LogParams, P as PreparedInput, R as RequestOptions, S as SecurityBlockError } from './agentid-B5Y1g2Ko.mjs';
1
+ import { T as TransparencyMetadata } from './agentid-agvYW2vW.mjs';
2
+ export { A as AgentID, D as DependencyError, G as GuardParams, a as GuardResponse, L as LogParams, P as PreparedInput, R as RequestOptions, S as SecurityBlockError } from './agentid-agvYW2vW.mjs';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
 
5
5
  type PIIMapping = Record<string, string>;
@@ -16,7 +16,7 @@ declare class PIIManager {
16
16
  deanonymize(text: string, mapping: PIIMapping): string;
17
17
  }
18
18
 
19
- type TokenUsage = Record<string, number>;
19
+ type TokenUsage = Record<string, unknown>;
20
20
  interface LLMAdapter {
21
21
  extractInput(req: unknown): string | null;
22
22
  getModelName(req: unknown, res?: unknown): string;
@@ -43,6 +43,7 @@ type InjectionScanParams = {
43
43
  systemId?: string;
44
44
  eventId?: string;
45
45
  clientEventId?: string;
46
+ telemetryMetadata?: Record<string, unknown>;
46
47
  };
47
48
  declare function scanWithRegex(prompt: string): string | null;
48
49
  declare class InjectionScanner {
@@ -55,6 +56,7 @@ declare class InjectionScanner {
55
56
  systemId?: string;
56
57
  eventId?: string;
57
58
  clientEventId?: string;
59
+ telemetryMetadata?: Record<string, unknown>;
58
60
  }): Promise<void>;
59
61
  scan(params: InjectionScanParams): Promise<void>;
60
62
  }