@agent-inspect/viewer 6.31.7 → 6.31.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -186,7 +186,7 @@ interface TimelineOptions {
186
186
  declare function buildRunTimeline(events: readonly TraceEvent[], options?: TimelineOptions): RunTimeline;
187
187
 
188
188
  type SuiteCaseStatus = "pass" | "fail" | "error" | "skipped";
189
- type SuiteDiagnosticCode = "AI_SUITE_CONFIG_INVALID" | "AI_SUITE_CONFIG_LOAD_FAILED" | "AI_SUITE_CASE_TRACE_MISSING" | "AI_SUITE_CASE_CHECK_FAILED" | "AI_SUITE_CASE_EVAL_FAILED" | "AI_SUITE_CASE_OBSERVATION_FAILED" | "AI_SUITE_TRACE_UNREADABLE";
189
+ type SuiteDiagnosticCode = "AI_SUITE_CONFIG_INVALID" | "AI_SUITE_CONFIG_LOAD_FAILED" | "AI_SUITE_CASE_TRACE_MISSING" | "AI_SUITE_CASE_CHECK_FAILED" | "AI_SUITE_CASE_EVAL_FAILED" | "AI_SUITE_CASE_OBSERVATION_FAILED" | "AI_SUITE_NO_ASSERTIONS" | "AI_SUITE_UNKNOWN_SELECTOR" | "AI_SUITE_TRACE_UNREADABLE";
190
190
  interface SuiteDiagnostic {
191
191
  code: SuiteDiagnosticCode;
192
192
  severity: "error" | "warning" | "info";
package/dist/index.d.ts CHANGED
@@ -186,7 +186,7 @@ interface TimelineOptions {
186
186
  declare function buildRunTimeline(events: readonly TraceEvent[], options?: TimelineOptions): RunTimeline;
187
187
 
188
188
  type SuiteCaseStatus = "pass" | "fail" | "error" | "skipped";
189
- type SuiteDiagnosticCode = "AI_SUITE_CONFIG_INVALID" | "AI_SUITE_CONFIG_LOAD_FAILED" | "AI_SUITE_CASE_TRACE_MISSING" | "AI_SUITE_CASE_CHECK_FAILED" | "AI_SUITE_CASE_EVAL_FAILED" | "AI_SUITE_CASE_OBSERVATION_FAILED" | "AI_SUITE_TRACE_UNREADABLE";
189
+ type SuiteDiagnosticCode = "AI_SUITE_CONFIG_INVALID" | "AI_SUITE_CONFIG_LOAD_FAILED" | "AI_SUITE_CASE_TRACE_MISSING" | "AI_SUITE_CASE_CHECK_FAILED" | "AI_SUITE_CASE_EVAL_FAILED" | "AI_SUITE_CASE_OBSERVATION_FAILED" | "AI_SUITE_NO_ASSERTIONS" | "AI_SUITE_UNKNOWN_SELECTOR" | "AI_SUITE_TRACE_UNREADABLE";
190
190
  interface SuiteDiagnostic {
191
191
  code: SuiteDiagnosticCode;
192
192
  severity: "error" | "warning" | "info";
package/dist/index.mjs CHANGED
@@ -764,7 +764,18 @@ async function readTraceEventsFromFile(filePath) {
764
764
  }
765
765
 
766
766
  // packages/core/src/context.ts
767
- new AsyncLocalStorage();
767
+ var GLOBAL_CONTEXT_ALS_KEY = "agent-inspect:execution-context-als:v1";
768
+ function getSharedContextStorage() {
769
+ const host = globalThis;
770
+ const existing = host[GLOBAL_CONTEXT_ALS_KEY];
771
+ if (existing instanceof AsyncLocalStorage) {
772
+ return existing;
773
+ }
774
+ const created = new AsyncLocalStorage();
775
+ host[GLOBAL_CONTEXT_ALS_KEY] = created;
776
+ return created;
777
+ }
778
+ getSharedContextStorage();
768
779
 
769
780
  // packages/core/src/outcomes/types.ts
770
781
  var OBSERVED_OUTCOME_METHODS = [
@@ -1367,7 +1378,7 @@ function enrichSessionSummary(summary, runs, options = {}) {
1367
1378
  }
1368
1379
  const lastMs = latestActivityMs(sessionRuns);
1369
1380
  const lastActivity = lastMs > 0 ? new Date(lastMs).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString();
1370
- const retryCount = summary.retries.filter(
1381
+ const retryCount2 = summary.retries.filter(
1371
1382
  (retry) => retry.retryOf !== void 0 || (retry.attempt ?? 0) > 1
1372
1383
  ).length;
1373
1384
  return {
@@ -1381,7 +1392,7 @@ function enrichSessionSummary(summary, runs, options = {}) {
1381
1392
  workflowId,
1382
1393
  lastError: deriveLastError(sessionRuns),
1383
1394
  lastActivity,
1384
- retryCount,
1395
+ retryCount: retryCount2,
1385
1396
  observationSummary: deriveObservationSummary(sessionRuns),
1386
1397
  checkSummary: deriveCheckSummary(sessionRuns)
1387
1398
  };
@@ -2254,6 +2265,13 @@ function metadataStringAttr(event, keys) {
2254
2265
  }
2255
2266
  return void 0;
2256
2267
  }
2268
+ function numericAttr(event, keys) {
2269
+ for (const key of keys) {
2270
+ const value = event.attributes?.[key];
2271
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2272
+ }
2273
+ return void 0;
2274
+ }
2257
2275
  function stripPrefix(name, prefixes) {
2258
2276
  for (const prefix of prefixes) {
2259
2277
  if (name.startsWith(prefix)) return name.slice(prefix.length);
@@ -2309,9 +2327,12 @@ function llmFinishReason(event) {
2309
2327
  const keys = ["finishReason", "rawFinishReason", "finish_reason"];
2310
2328
  return stringAttr(event, keys) ?? metadataStringAttr(event, keys);
2311
2329
  }
2330
+ function retryCount(event) {
2331
+ return numericAttr(event, ["retryCount", "retryAttempt", "retry_attempt", "attempt"]);
2332
+ }
2312
2333
  function finishedEvents(context, kind) {
2313
2334
  return semanticEvents(context).filter(
2314
- (event) => (event.kind === kind) && event.status !== "running"
2335
+ (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
2315
2336
  );
2316
2337
  }
2317
2338
  function toolInvocationEvents(context) {
@@ -2376,6 +2397,35 @@ function createRunDurationRule(options) {
2376
2397
  }
2377
2398
  };
2378
2399
  }
2400
+ function createRunDepthRule(options) {
2401
+ return {
2402
+ id: "run.depth",
2403
+ category: "run",
2404
+ defaultSeverity: "error",
2405
+ evaluate(context) {
2406
+ const nodes = [...context.nodesByEventId.values()];
2407
+ const maxDepth = nodes.reduce((max, node) => Math.max(max, node.depth), 0);
2408
+ if (maxDepth <= options.maxDepth) return [];
2409
+ const deepest = nodes.filter((node) => node.depth === maxDepth);
2410
+ return [
2411
+ failFinding(
2412
+ "run.depth",
2413
+ `Run depth ${maxDepth} exceeded ${options.maxDepth}.`,
2414
+ deepest.map((node) => ({
2415
+ runId: node.event.runId,
2416
+ eventId: node.event.eventId,
2417
+ parentId: node.event.parentId,
2418
+ kind: node.event.kind,
2419
+ name: node.event.name,
2420
+ status: node.event.status
2421
+ })),
2422
+ { maxDepth: options.maxDepth },
2423
+ maxDepth
2424
+ )
2425
+ ];
2426
+ }
2427
+ };
2428
+ }
2379
2429
  function createToolUsageRule(options) {
2380
2430
  return {
2381
2431
  id: "tool.usage",
@@ -2440,6 +2490,45 @@ function createToolUsageRule(options) {
2440
2490
  }
2441
2491
  };
2442
2492
  }
2493
+ function createToolFailureRule(options) {
2494
+ return {
2495
+ id: "tool.failures",
2496
+ category: "tool",
2497
+ defaultSeverity: "error",
2498
+ evaluate(context) {
2499
+ const tools = finishedEvents(context, "TOOL");
2500
+ const failures = tools.filter((event) => event.status === "error");
2501
+ const retries = tools.map((event) => ({ event, count: retryCount(event) })).filter((item) => item.count !== void 0);
2502
+ const findings = [];
2503
+ if (options.maxFailures !== void 0 && failures.length > options.maxFailures) {
2504
+ findings.push(
2505
+ failFinding(
2506
+ "tool.failures",
2507
+ `Tool failure count ${failures.length} exceeded ${options.maxFailures}.`,
2508
+ failures.map((event) => eventEvidence(event)),
2509
+ { maxFailures: options.maxFailures },
2510
+ failures.length
2511
+ )
2512
+ );
2513
+ }
2514
+ if (options.maxRetries !== void 0) {
2515
+ const excessiveRetries = retries.filter((item) => item.count > options.maxRetries);
2516
+ if (excessiveRetries.length > 0) {
2517
+ findings.push(
2518
+ failFinding(
2519
+ "tool.failures",
2520
+ `Tool retry count exceeded ${options.maxRetries}.`,
2521
+ excessiveRetries.map((item) => eventEvidence(item.event, "attributes.retryCount")),
2522
+ { maxRetries: options.maxRetries },
2523
+ excessiveRetries.map((item) => ({ tool: toolName(item.event), retries: item.count }))
2524
+ )
2525
+ );
2526
+ }
2527
+ }
2528
+ return findings;
2529
+ }
2530
+ };
2531
+ }
2443
2532
  function createLlmUsageRule(options) {
2444
2533
  return {
2445
2534
  id: "llm.usage",
@@ -3885,8 +3974,13 @@ function persistedInspectEventsToInspectEvents(events, options) {
3885
3974
  }
3886
3975
 
3887
3976
  // packages/core/src/persisted/tree-bridge.ts
3977
+ function logicalToPersisted(event) {
3978
+ const { sourceEventIds: _sourceEventIds, projection: _projection, ...rest } = event;
3979
+ return rest;
3980
+ }
3888
3981
  function persistedInspectEventsToRunTrees(events, options) {
3889
- const inspectEvents = persistedInspectEventsToInspectEvents(events, {
3982
+ const sourceEvents = options?.coalesceLifecycle === true ? projectLogicalEvents(events).logicalEvents.map(logicalToPersisted) : events;
3983
+ const inspectEvents = persistedInspectEventsToInspectEvents(sourceEvents, {
3890
3984
  skipInvalid: options?.skipInvalid
3891
3985
  });
3892
3986
  return new TreeBuilder().build(inspectEvents);
@@ -4580,7 +4674,7 @@ function mapOpenInferenceSpan(span, index, version) {
4580
4674
  const errorMessage = isRecord7(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
4581
4675
  const event = {
4582
4676
  schemaVersion: "0.2",
4583
- eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
4677
+ eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : `${traceId}:${spanId}`,
4584
4678
  runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
4585
4679
  kind,
4586
4680
  name,
@@ -4631,12 +4725,16 @@ function mapOpenInferenceEvents(document) {
4631
4725
  const mapped = document.spans.map(
4632
4726
  (span, index) => mapOpenInferenceSpan(span, index, document.version)
4633
4727
  );
4634
- const spanIdToEventId = new Map(
4635
- mapped.map((span) => [span.spanId, span.event.eventId])
4728
+ const spanKeyToEventId = new Map(
4729
+ mapped.map((span) => {
4730
+ const traceId = typeof span.event.trace?.traceId === "string" ? span.event.trace.traceId : "";
4731
+ return [`${traceId}:${span.spanId}`, span.event.eventId];
4732
+ })
4636
4733
  );
4637
4734
  for (const span of mapped) {
4638
4735
  if (span.parentSpanId === void 0) continue;
4639
- span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
4736
+ const traceId = typeof span.event.trace?.traceId === "string" ? span.event.trace.traceId : "";
4737
+ span.event.parentId = spanKeyToEventId.get(`${traceId}:${span.parentSpanId}`) ?? span.parentSpanId;
4640
4738
  }
4641
4739
  return {
4642
4740
  events: mapped.map((span) => span.event),
@@ -5185,7 +5283,7 @@ function mapOtlpSpan(context) {
5185
5283
  const errorMessage = isRecord7(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
5186
5284
  const event = {
5187
5285
  schemaVersion: "0.2",
5188
- eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
5286
+ eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : `${traceId}:${spanId}`,
5189
5287
  runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
5190
5288
  kind,
5191
5289
  name: readStringField(span, ["name"]) ?? spanId,
@@ -5234,12 +5332,16 @@ function mapOtlpSpan(context) {
5234
5332
  }
5235
5333
  function mapOtlpEventsToPersisted(document) {
5236
5334
  const mapped = document.spans.map((span) => mapOtlpSpan(span));
5237
- const spanIdToEventId = new Map(
5238
- mapped.map((span) => [span.spanId, span.event.eventId])
5335
+ const spanKeyToEventId = new Map(
5336
+ mapped.map((span) => {
5337
+ const traceId = typeof span.event.trace?.traceId === "string" ? span.event.trace.traceId : "";
5338
+ return [`${traceId}:${span.spanId}`, span.event.eventId];
5339
+ })
5239
5340
  );
5240
5341
  for (const span of mapped) {
5241
5342
  if (span.parentSpanId === void 0) continue;
5242
- span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
5343
+ const traceId = typeof span.event.trace?.traceId === "string" ? span.event.trace.traceId : "";
5344
+ span.event.parentId = spanKeyToEventId.get(`${traceId}:${span.parentSpanId}`) ?? span.parentSpanId;
5243
5345
  }
5244
5346
  return {
5245
5347
  events: mapped.map((span) => span.event),
@@ -5530,21 +5632,48 @@ function openTrace(input, options = {}) {
5530
5632
  }
5531
5633
 
5532
5634
  // packages/core/src/suite/run.ts
5635
+ var KNOWN_SUITE_SELECT_IDS = /* @__PURE__ */ new Set([
5636
+ "run.status",
5637
+ "run.duration",
5638
+ "run.depth",
5639
+ "outcome.status",
5640
+ "tool.usage",
5641
+ "tool.failures",
5642
+ "llm.usage"
5643
+ ]);
5533
5644
  function diagnostic4(code, message, severity = "error", caseId) {
5534
5645
  return { code, message, severity, ...caseId !== void 0 ? { caseId } : {} };
5535
5646
  }
5536
- function buildCaseRules(suiteCase, config) {
5647
+ function buildCaseAssertions(suiteCase, config) {
5537
5648
  const rules = [];
5538
5649
  const select = new Set(config.checks?.select ?? []);
5539
- if (select.has("run.status")) {
5650
+ const configDiagnostics = [];
5651
+ const evalConfig = config.eval;
5652
+ const declaredSelect = [...select];
5653
+ for (const id of declaredSelect) {
5654
+ if (!KNOWN_SUITE_SELECT_IDS.has(id)) {
5655
+ configDiagnostics.push(
5656
+ diagnostic4(
5657
+ "AI_SUITE_UNKNOWN_SELECTOR",
5658
+ `Unknown or unsupported suite check selector "${id}".`,
5659
+ "error",
5660
+ suiteCase.id
5661
+ )
5662
+ );
5663
+ }
5664
+ }
5665
+ if (select.has("run.status") || evalConfig?.requireSuccess === true) {
5540
5666
  rules.push(createRunStatusRule());
5667
+ select.add("run.status");
5541
5668
  }
5542
5669
  const requiredTools = [
5543
5670
  ...config.checks?.tool?.required ?? [],
5671
+ ...evalConfig?.requiredTools ?? [],
5544
5672
  ...suiteCase.requireTools ?? []
5545
5673
  ];
5546
5674
  const forbiddenTools = [
5547
5675
  ...config.checks?.tool?.forbidden ?? [],
5676
+ ...evalConfig?.forbiddenTools ?? [],
5548
5677
  ...suiteCase.forbidTools ?? []
5549
5678
  ];
5550
5679
  if (requiredTools.length > 0 || forbiddenTools.length > 0) {
@@ -5556,20 +5685,51 @@ function buildCaseRules(suiteCase, config) {
5556
5685
  );
5557
5686
  select.add("tool.usage");
5558
5687
  }
5559
- const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? config.eval?.maxDurationMs;
5688
+ const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? evalConfig?.maxDurationMs;
5560
5689
  if (maxDurationMs !== void 0) {
5561
5690
  rules.push(createRunDurationRule({ maxDurationMs }));
5562
5691
  select.add("run.duration");
5563
5692
  }
5564
- const llm = config.checks?.llm;
5565
- if (llm?.allowedModels !== void 0 || llm?.maxTotalTokens !== void 0) {
5566
- rules.push(createLlmUsageRule(llm));
5693
+ const maxDepth = config.checks?.run?.maxDepth ?? evalConfig?.maxDepth;
5694
+ if (maxDepth !== void 0) {
5695
+ rules.push(createRunDepthRule({ maxDepth }));
5696
+ select.add("run.depth");
5697
+ }
5698
+ if (evalConfig?.maxRetries !== void 0) {
5699
+ rules.push(createToolFailureRule({ maxRetries: evalConfig.maxRetries }));
5700
+ select.add("tool.failures");
5701
+ }
5702
+ const allowedModels = config.checks?.llm?.allowedModels;
5703
+ const maxTotalTokens = config.checks?.llm?.maxTotalTokens ?? evalConfig?.maxTotalTokens;
5704
+ if (allowedModels !== void 0 || maxTotalTokens !== void 0) {
5705
+ rules.push(
5706
+ createLlmUsageRule({
5707
+ ...allowedModels !== void 0 ? { allowedModels } : {},
5708
+ ...maxTotalTokens !== void 0 ? { maxTotalTokens } : {}
5709
+ })
5710
+ );
5567
5711
  select.add("llm.usage");
5568
5712
  }
5569
5713
  if (select.has("outcome.status")) {
5570
5714
  rules.push(createObservedOutcomeRule({ failOn: ["failed"] }));
5571
5715
  }
5572
- return { rules, select: [...select] };
5716
+ const observationCount = suiteCase.expectedObservations?.length ?? 0;
5717
+ if (rules.length === 0 && observationCount === 0 && configDiagnostics.length === 0) {
5718
+ configDiagnostics.push(
5719
+ diagnostic4(
5720
+ "AI_SUITE_NO_ASSERTIONS",
5721
+ `Suite case "${suiteCase.id}" declares no effective checks, eval controls, or expected observations.`,
5722
+ "error",
5723
+ suiteCase.id
5724
+ )
5725
+ );
5726
+ }
5727
+ return {
5728
+ rules,
5729
+ select: [...select],
5730
+ observationCount,
5731
+ configDiagnostics
5732
+ };
5573
5733
  }
5574
5734
  function outcomesFromRead(read) {
5575
5735
  const persistedOutcomes = extractOutcomesFromPersistedEvents(
@@ -5650,8 +5810,20 @@ async function runSuiteCase(suiteCase, config, options) {
5650
5810
  ]
5651
5811
  };
5652
5812
  }
5653
- const { rules, select } = buildCaseRules(suiteCase, config);
5654
- const checkResult = rules.length > 0 ? runTraceChecks({ read }, { rules, select }) : {
5813
+ const compiled = buildCaseAssertions(suiteCase, config);
5814
+ if (compiled.configDiagnostics.length > 0) {
5815
+ return {
5816
+ id: suiteCase.id,
5817
+ status: "error",
5818
+ tracePath: resolved.tracePath,
5819
+ ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5820
+ checkOk: false,
5821
+ ...config.eval?.requireSuccess === true ? { evalOk: false } : {},
5822
+ message: compiled.configDiagnostics.map((item) => item.message).join("; "),
5823
+ diagnostics: compiled.configDiagnostics
5824
+ };
5825
+ }
5826
+ const checkResult = compiled.rules.length > 0 ? runTraceChecks({ read }, { rules: compiled.rules, select: compiled.select }) : {
5655
5827
  ok: true,
5656
5828
  status: "pass",
5657
5829
  format: read.format,
@@ -5679,6 +5851,9 @@ async function runSuiteCase(suiteCase, config, options) {
5679
5851
  ];
5680
5852
  const checkOk = checkResult.ok;
5681
5853
  const observationsOk = observationResult.ok;
5854
+ const evalOk = config.eval?.requireSuccess === true ? checkResult.findings.every(
5855
+ (finding) => finding.ruleId !== "run.status" || finding.status !== "fail"
5856
+ ) && checkResult.diagnostics.every((item) => item.severity !== "error") : void 0;
5682
5857
  const ok = checkOk && observationsOk;
5683
5858
  const status = ok ? "pass" : checkResult.status === "error" ? "error" : "fail";
5684
5859
  return {
@@ -5687,6 +5862,7 @@ async function runSuiteCase(suiteCase, config, options) {
5687
5862
  tracePath: resolved.tracePath,
5688
5863
  ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5689
5864
  checkOk,
5865
+ ...evalOk !== void 0 ? { evalOk } : {},
5690
5866
  observationsOk,
5691
5867
  diagnostics,
5692
5868
  ...ok ? {} : {