@agent-inspect/viewer 6.31.8 → 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",
@@ -5543,21 +5632,48 @@ function openTrace(input, options = {}) {
5543
5632
  }
5544
5633
 
5545
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
+ ]);
5546
5644
  function diagnostic4(code, message, severity = "error", caseId) {
5547
5645
  return { code, message, severity, ...caseId !== void 0 ? { caseId } : {} };
5548
5646
  }
5549
- function buildCaseRules(suiteCase, config) {
5647
+ function buildCaseAssertions(suiteCase, config) {
5550
5648
  const rules = [];
5551
5649
  const select = new Set(config.checks?.select ?? []);
5552
- 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) {
5553
5666
  rules.push(createRunStatusRule());
5667
+ select.add("run.status");
5554
5668
  }
5555
5669
  const requiredTools = [
5556
5670
  ...config.checks?.tool?.required ?? [],
5671
+ ...evalConfig?.requiredTools ?? [],
5557
5672
  ...suiteCase.requireTools ?? []
5558
5673
  ];
5559
5674
  const forbiddenTools = [
5560
5675
  ...config.checks?.tool?.forbidden ?? [],
5676
+ ...evalConfig?.forbiddenTools ?? [],
5561
5677
  ...suiteCase.forbidTools ?? []
5562
5678
  ];
5563
5679
  if (requiredTools.length > 0 || forbiddenTools.length > 0) {
@@ -5569,20 +5685,51 @@ function buildCaseRules(suiteCase, config) {
5569
5685
  );
5570
5686
  select.add("tool.usage");
5571
5687
  }
5572
- const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? config.eval?.maxDurationMs;
5688
+ const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? evalConfig?.maxDurationMs;
5573
5689
  if (maxDurationMs !== void 0) {
5574
5690
  rules.push(createRunDurationRule({ maxDurationMs }));
5575
5691
  select.add("run.duration");
5576
5692
  }
5577
- const llm = config.checks?.llm;
5578
- if (llm?.allowedModels !== void 0 || llm?.maxTotalTokens !== void 0) {
5579
- 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
+ );
5580
5711
  select.add("llm.usage");
5581
5712
  }
5582
5713
  if (select.has("outcome.status")) {
5583
5714
  rules.push(createObservedOutcomeRule({ failOn: ["failed"] }));
5584
5715
  }
5585
- 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
+ };
5586
5733
  }
5587
5734
  function outcomesFromRead(read) {
5588
5735
  const persistedOutcomes = extractOutcomesFromPersistedEvents(
@@ -5663,8 +5810,20 @@ async function runSuiteCase(suiteCase, config, options) {
5663
5810
  ]
5664
5811
  };
5665
5812
  }
5666
- const { rules, select } = buildCaseRules(suiteCase, config);
5667
- 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 }) : {
5668
5827
  ok: true,
5669
5828
  status: "pass",
5670
5829
  format: read.format,
@@ -5692,6 +5851,9 @@ async function runSuiteCase(suiteCase, config, options) {
5692
5851
  ];
5693
5852
  const checkOk = checkResult.ok;
5694
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;
5695
5857
  const ok = checkOk && observationsOk;
5696
5858
  const status = ok ? "pass" : checkResult.status === "error" ? "error" : "fail";
5697
5859
  return {
@@ -5700,6 +5862,7 @@ async function runSuiteCase(suiteCase, config, options) {
5700
5862
  tracePath: resolved.tracePath,
5701
5863
  ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5702
5864
  checkOk,
5865
+ ...evalOk !== void 0 ? { evalOk } : {},
5703
5866
  observationsOk,
5704
5867
  diagnostics,
5705
5868
  ...ok ? {} : {