@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.cjs CHANGED
@@ -771,7 +771,18 @@ async function readTraceEventsFromFile(filePath) {
771
771
  }
772
772
 
773
773
  // packages/core/src/context.ts
774
- new async_hooks.AsyncLocalStorage();
774
+ var GLOBAL_CONTEXT_ALS_KEY = "agent-inspect:execution-context-als:v1";
775
+ function getSharedContextStorage() {
776
+ const host = globalThis;
777
+ const existing = host[GLOBAL_CONTEXT_ALS_KEY];
778
+ if (existing instanceof async_hooks.AsyncLocalStorage) {
779
+ return existing;
780
+ }
781
+ const created = new async_hooks.AsyncLocalStorage();
782
+ host[GLOBAL_CONTEXT_ALS_KEY] = created;
783
+ return created;
784
+ }
785
+ getSharedContextStorage();
775
786
 
776
787
  // packages/core/src/outcomes/types.ts
777
788
  var OBSERVED_OUTCOME_METHODS = [
@@ -1374,7 +1385,7 @@ function enrichSessionSummary(summary, runs, options = {}) {
1374
1385
  }
1375
1386
  const lastMs = latestActivityMs(sessionRuns);
1376
1387
  const lastActivity = lastMs > 0 ? new Date(lastMs).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString();
1377
- const retryCount = summary.retries.filter(
1388
+ const retryCount2 = summary.retries.filter(
1378
1389
  (retry) => retry.retryOf !== void 0 || (retry.attempt ?? 0) > 1
1379
1390
  ).length;
1380
1391
  return {
@@ -1388,7 +1399,7 @@ function enrichSessionSummary(summary, runs, options = {}) {
1388
1399
  workflowId,
1389
1400
  lastError: deriveLastError(sessionRuns),
1390
1401
  lastActivity,
1391
- retryCount,
1402
+ retryCount: retryCount2,
1392
1403
  observationSummary: deriveObservationSummary(sessionRuns),
1393
1404
  checkSummary: deriveCheckSummary(sessionRuns)
1394
1405
  };
@@ -2261,6 +2272,13 @@ function metadataStringAttr(event, keys) {
2261
2272
  }
2262
2273
  return void 0;
2263
2274
  }
2275
+ function numericAttr(event, keys) {
2276
+ for (const key of keys) {
2277
+ const value = event.attributes?.[key];
2278
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2279
+ }
2280
+ return void 0;
2281
+ }
2264
2282
  function stripPrefix(name, prefixes) {
2265
2283
  for (const prefix of prefixes) {
2266
2284
  if (name.startsWith(prefix)) return name.slice(prefix.length);
@@ -2316,9 +2334,12 @@ function llmFinishReason(event) {
2316
2334
  const keys = ["finishReason", "rawFinishReason", "finish_reason"];
2317
2335
  return stringAttr(event, keys) ?? metadataStringAttr(event, keys);
2318
2336
  }
2337
+ function retryCount(event) {
2338
+ return numericAttr(event, ["retryCount", "retryAttempt", "retry_attempt", "attempt"]);
2339
+ }
2319
2340
  function finishedEvents(context, kind) {
2320
2341
  return semanticEvents(context).filter(
2321
- (event) => (event.kind === kind) && event.status !== "running"
2342
+ (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
2322
2343
  );
2323
2344
  }
2324
2345
  function toolInvocationEvents(context) {
@@ -2383,6 +2404,35 @@ function createRunDurationRule(options) {
2383
2404
  }
2384
2405
  };
2385
2406
  }
2407
+ function createRunDepthRule(options) {
2408
+ return {
2409
+ id: "run.depth",
2410
+ category: "run",
2411
+ defaultSeverity: "error",
2412
+ evaluate(context) {
2413
+ const nodes = [...context.nodesByEventId.values()];
2414
+ const maxDepth = nodes.reduce((max, node) => Math.max(max, node.depth), 0);
2415
+ if (maxDepth <= options.maxDepth) return [];
2416
+ const deepest = nodes.filter((node) => node.depth === maxDepth);
2417
+ return [
2418
+ failFinding(
2419
+ "run.depth",
2420
+ `Run depth ${maxDepth} exceeded ${options.maxDepth}.`,
2421
+ deepest.map((node) => ({
2422
+ runId: node.event.runId,
2423
+ eventId: node.event.eventId,
2424
+ parentId: node.event.parentId,
2425
+ kind: node.event.kind,
2426
+ name: node.event.name,
2427
+ status: node.event.status
2428
+ })),
2429
+ { maxDepth: options.maxDepth },
2430
+ maxDepth
2431
+ )
2432
+ ];
2433
+ }
2434
+ };
2435
+ }
2386
2436
  function createToolUsageRule(options) {
2387
2437
  return {
2388
2438
  id: "tool.usage",
@@ -2447,6 +2497,45 @@ function createToolUsageRule(options) {
2447
2497
  }
2448
2498
  };
2449
2499
  }
2500
+ function createToolFailureRule(options) {
2501
+ return {
2502
+ id: "tool.failures",
2503
+ category: "tool",
2504
+ defaultSeverity: "error",
2505
+ evaluate(context) {
2506
+ const tools = finishedEvents(context, "TOOL");
2507
+ const failures = tools.filter((event) => event.status === "error");
2508
+ const retries = tools.map((event) => ({ event, count: retryCount(event) })).filter((item) => item.count !== void 0);
2509
+ const findings = [];
2510
+ if (options.maxFailures !== void 0 && failures.length > options.maxFailures) {
2511
+ findings.push(
2512
+ failFinding(
2513
+ "tool.failures",
2514
+ `Tool failure count ${failures.length} exceeded ${options.maxFailures}.`,
2515
+ failures.map((event) => eventEvidence(event)),
2516
+ { maxFailures: options.maxFailures },
2517
+ failures.length
2518
+ )
2519
+ );
2520
+ }
2521
+ if (options.maxRetries !== void 0) {
2522
+ const excessiveRetries = retries.filter((item) => item.count > options.maxRetries);
2523
+ if (excessiveRetries.length > 0) {
2524
+ findings.push(
2525
+ failFinding(
2526
+ "tool.failures",
2527
+ `Tool retry count exceeded ${options.maxRetries}.`,
2528
+ excessiveRetries.map((item) => eventEvidence(item.event, "attributes.retryCount")),
2529
+ { maxRetries: options.maxRetries },
2530
+ excessiveRetries.map((item) => ({ tool: toolName(item.event), retries: item.count }))
2531
+ )
2532
+ );
2533
+ }
2534
+ }
2535
+ return findings;
2536
+ }
2537
+ };
2538
+ }
2450
2539
  function createLlmUsageRule(options) {
2451
2540
  return {
2452
2541
  id: "llm.usage",
@@ -5550,21 +5639,48 @@ function openTrace(input, options = {}) {
5550
5639
  }
5551
5640
 
5552
5641
  // packages/core/src/suite/run.ts
5642
+ var KNOWN_SUITE_SELECT_IDS = /* @__PURE__ */ new Set([
5643
+ "run.status",
5644
+ "run.duration",
5645
+ "run.depth",
5646
+ "outcome.status",
5647
+ "tool.usage",
5648
+ "tool.failures",
5649
+ "llm.usage"
5650
+ ]);
5553
5651
  function diagnostic4(code, message, severity = "error", caseId) {
5554
5652
  return { code, message, severity, ...caseId !== void 0 ? { caseId } : {} };
5555
5653
  }
5556
- function buildCaseRules(suiteCase, config) {
5654
+ function buildCaseAssertions(suiteCase, config) {
5557
5655
  const rules = [];
5558
5656
  const select = new Set(config.checks?.select ?? []);
5559
- if (select.has("run.status")) {
5657
+ const configDiagnostics = [];
5658
+ const evalConfig = config.eval;
5659
+ const declaredSelect = [...select];
5660
+ for (const id of declaredSelect) {
5661
+ if (!KNOWN_SUITE_SELECT_IDS.has(id)) {
5662
+ configDiagnostics.push(
5663
+ diagnostic4(
5664
+ "AI_SUITE_UNKNOWN_SELECTOR",
5665
+ `Unknown or unsupported suite check selector "${id}".`,
5666
+ "error",
5667
+ suiteCase.id
5668
+ )
5669
+ );
5670
+ }
5671
+ }
5672
+ if (select.has("run.status") || evalConfig?.requireSuccess === true) {
5560
5673
  rules.push(createRunStatusRule());
5674
+ select.add("run.status");
5561
5675
  }
5562
5676
  const requiredTools = [
5563
5677
  ...config.checks?.tool?.required ?? [],
5678
+ ...evalConfig?.requiredTools ?? [],
5564
5679
  ...suiteCase.requireTools ?? []
5565
5680
  ];
5566
5681
  const forbiddenTools = [
5567
5682
  ...config.checks?.tool?.forbidden ?? [],
5683
+ ...evalConfig?.forbiddenTools ?? [],
5568
5684
  ...suiteCase.forbidTools ?? []
5569
5685
  ];
5570
5686
  if (requiredTools.length > 0 || forbiddenTools.length > 0) {
@@ -5576,20 +5692,51 @@ function buildCaseRules(suiteCase, config) {
5576
5692
  );
5577
5693
  select.add("tool.usage");
5578
5694
  }
5579
- const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? config.eval?.maxDurationMs;
5695
+ const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? evalConfig?.maxDurationMs;
5580
5696
  if (maxDurationMs !== void 0) {
5581
5697
  rules.push(createRunDurationRule({ maxDurationMs }));
5582
5698
  select.add("run.duration");
5583
5699
  }
5584
- const llm = config.checks?.llm;
5585
- if (llm?.allowedModels !== void 0 || llm?.maxTotalTokens !== void 0) {
5586
- rules.push(createLlmUsageRule(llm));
5700
+ const maxDepth = config.checks?.run?.maxDepth ?? evalConfig?.maxDepth;
5701
+ if (maxDepth !== void 0) {
5702
+ rules.push(createRunDepthRule({ maxDepth }));
5703
+ select.add("run.depth");
5704
+ }
5705
+ if (evalConfig?.maxRetries !== void 0) {
5706
+ rules.push(createToolFailureRule({ maxRetries: evalConfig.maxRetries }));
5707
+ select.add("tool.failures");
5708
+ }
5709
+ const allowedModels = config.checks?.llm?.allowedModels;
5710
+ const maxTotalTokens = config.checks?.llm?.maxTotalTokens ?? evalConfig?.maxTotalTokens;
5711
+ if (allowedModels !== void 0 || maxTotalTokens !== void 0) {
5712
+ rules.push(
5713
+ createLlmUsageRule({
5714
+ ...allowedModels !== void 0 ? { allowedModels } : {},
5715
+ ...maxTotalTokens !== void 0 ? { maxTotalTokens } : {}
5716
+ })
5717
+ );
5587
5718
  select.add("llm.usage");
5588
5719
  }
5589
5720
  if (select.has("outcome.status")) {
5590
5721
  rules.push(createObservedOutcomeRule({ failOn: ["failed"] }));
5591
5722
  }
5592
- return { rules, select: [...select] };
5723
+ const observationCount = suiteCase.expectedObservations?.length ?? 0;
5724
+ if (rules.length === 0 && observationCount === 0 && configDiagnostics.length === 0) {
5725
+ configDiagnostics.push(
5726
+ diagnostic4(
5727
+ "AI_SUITE_NO_ASSERTIONS",
5728
+ `Suite case "${suiteCase.id}" declares no effective checks, eval controls, or expected observations.`,
5729
+ "error",
5730
+ suiteCase.id
5731
+ )
5732
+ );
5733
+ }
5734
+ return {
5735
+ rules,
5736
+ select: [...select],
5737
+ observationCount,
5738
+ configDiagnostics
5739
+ };
5593
5740
  }
5594
5741
  function outcomesFromRead(read) {
5595
5742
  const persistedOutcomes = extractOutcomesFromPersistedEvents(
@@ -5670,8 +5817,20 @@ async function runSuiteCase(suiteCase, config, options) {
5670
5817
  ]
5671
5818
  };
5672
5819
  }
5673
- const { rules, select } = buildCaseRules(suiteCase, config);
5674
- const checkResult = rules.length > 0 ? runTraceChecks({ read }, { rules, select }) : {
5820
+ const compiled = buildCaseAssertions(suiteCase, config);
5821
+ if (compiled.configDiagnostics.length > 0) {
5822
+ return {
5823
+ id: suiteCase.id,
5824
+ status: "error",
5825
+ tracePath: resolved.tracePath,
5826
+ ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5827
+ checkOk: false,
5828
+ ...config.eval?.requireSuccess === true ? { evalOk: false } : {},
5829
+ message: compiled.configDiagnostics.map((item) => item.message).join("; "),
5830
+ diagnostics: compiled.configDiagnostics
5831
+ };
5832
+ }
5833
+ const checkResult = compiled.rules.length > 0 ? runTraceChecks({ read }, { rules: compiled.rules, select: compiled.select }) : {
5675
5834
  ok: true,
5676
5835
  status: "pass",
5677
5836
  format: read.format,
@@ -5699,6 +5858,9 @@ async function runSuiteCase(suiteCase, config, options) {
5699
5858
  ];
5700
5859
  const checkOk = checkResult.ok;
5701
5860
  const observationsOk = observationResult.ok;
5861
+ const evalOk = config.eval?.requireSuccess === true ? checkResult.findings.every(
5862
+ (finding) => finding.ruleId !== "run.status" || finding.status !== "fail"
5863
+ ) && checkResult.diagnostics.every((item) => item.severity !== "error") : void 0;
5702
5864
  const ok = checkOk && observationsOk;
5703
5865
  const status = ok ? "pass" : checkResult.status === "error" ? "error" : "fail";
5704
5866
  return {
@@ -5707,6 +5869,7 @@ async function runSuiteCase(suiteCase, config, options) {
5707
5869
  tracePath: resolved.tracePath,
5708
5870
  ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5709
5871
  checkOk,
5872
+ ...evalOk !== void 0 ? { evalOk } : {},
5710
5873
  observationsOk,
5711
5874
  diagnostics,
5712
5875
  ...ok ? {} : {