@agent-inspect/viewer 6.31.8 → 6.31.10

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
  };
@@ -1706,6 +1717,17 @@ var PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
1706
1717
  summary: "Trace contains a parentId cycle.",
1707
1718
  remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
1708
1719
  relatedCodes: ["structure.cycle"]
1720
+ },
1721
+ AI_TRACE_CONTRACT_INPUT_INVALID: {
1722
+ code: "AI_TRACE_CONTRACT_INPUT_INVALID",
1723
+ summary: "evaluateTraceContract expects ({ read }, contract) or use evaluateTraceContractRead(read, contract).",
1724
+ remediation: "Pass TraceCheckInput as the first argument: evaluateTraceContract({ read }, contract).",
1725
+ relatedCodes: ["AI_CHECK_INVALID_ARGUMENTS"]
1726
+ },
1727
+ AI_INSPECT_RUN_UNSUPPORTED_OPTION: {
1728
+ code: "AI_INSPECT_RUN_UNSUPPORTED_OPTION",
1729
+ summary: 'inspectRun does not accept a "writer" option.',
1730
+ remediation: "Use createInspector({ writer }) then inspector.run(...). inspectRun persists via the global JSONL path (schema 0.1)."
1709
1731
  }
1710
1732
  });
1711
1733
  function formatProgrammaticDiagnostic(code, detail) {
@@ -2261,6 +2283,13 @@ function metadataStringAttr(event, keys) {
2261
2283
  }
2262
2284
  return void 0;
2263
2285
  }
2286
+ function numericAttr(event, keys) {
2287
+ for (const key of keys) {
2288
+ const value = event.attributes?.[key];
2289
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2290
+ }
2291
+ return void 0;
2292
+ }
2264
2293
  function stripPrefix(name, prefixes) {
2265
2294
  for (const prefix of prefixes) {
2266
2295
  if (name.startsWith(prefix)) return name.slice(prefix.length);
@@ -2316,9 +2345,12 @@ function llmFinishReason(event) {
2316
2345
  const keys = ["finishReason", "rawFinishReason", "finish_reason"];
2317
2346
  return stringAttr(event, keys) ?? metadataStringAttr(event, keys);
2318
2347
  }
2348
+ function retryCount(event) {
2349
+ return numericAttr(event, ["retryCount", "retryAttempt", "retry_attempt", "attempt"]);
2350
+ }
2319
2351
  function finishedEvents(context, kind) {
2320
2352
  return semanticEvents(context).filter(
2321
- (event) => (event.kind === kind) && event.status !== "running"
2353
+ (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
2322
2354
  );
2323
2355
  }
2324
2356
  function toolInvocationEvents(context) {
@@ -2383,6 +2415,35 @@ function createRunDurationRule(options) {
2383
2415
  }
2384
2416
  };
2385
2417
  }
2418
+ function createRunDepthRule(options) {
2419
+ return {
2420
+ id: "run.depth",
2421
+ category: "run",
2422
+ defaultSeverity: "error",
2423
+ evaluate(context) {
2424
+ const nodes = [...context.nodesByEventId.values()];
2425
+ const maxDepth = nodes.reduce((max, node) => Math.max(max, node.depth), 0);
2426
+ if (maxDepth <= options.maxDepth) return [];
2427
+ const deepest = nodes.filter((node) => node.depth === maxDepth);
2428
+ return [
2429
+ failFinding(
2430
+ "run.depth",
2431
+ `Run depth ${maxDepth} exceeded ${options.maxDepth}.`,
2432
+ deepest.map((node) => ({
2433
+ runId: node.event.runId,
2434
+ eventId: node.event.eventId,
2435
+ parentId: node.event.parentId,
2436
+ kind: node.event.kind,
2437
+ name: node.event.name,
2438
+ status: node.event.status
2439
+ })),
2440
+ { maxDepth: options.maxDepth },
2441
+ maxDepth
2442
+ )
2443
+ ];
2444
+ }
2445
+ };
2446
+ }
2386
2447
  function createToolUsageRule(options) {
2387
2448
  return {
2388
2449
  id: "tool.usage",
@@ -2447,6 +2508,45 @@ function createToolUsageRule(options) {
2447
2508
  }
2448
2509
  };
2449
2510
  }
2511
+ function createToolFailureRule(options) {
2512
+ return {
2513
+ id: "tool.failures",
2514
+ category: "tool",
2515
+ defaultSeverity: "error",
2516
+ evaluate(context) {
2517
+ const tools = finishedEvents(context, "TOOL");
2518
+ const failures = tools.filter((event) => event.status === "error");
2519
+ const retries = tools.map((event) => ({ event, count: retryCount(event) })).filter((item) => item.count !== void 0);
2520
+ const findings = [];
2521
+ if (options.maxFailures !== void 0 && failures.length > options.maxFailures) {
2522
+ findings.push(
2523
+ failFinding(
2524
+ "tool.failures",
2525
+ `Tool failure count ${failures.length} exceeded ${options.maxFailures}.`,
2526
+ failures.map((event) => eventEvidence(event)),
2527
+ { maxFailures: options.maxFailures },
2528
+ failures.length
2529
+ )
2530
+ );
2531
+ }
2532
+ if (options.maxRetries !== void 0) {
2533
+ const excessiveRetries = retries.filter((item) => item.count > options.maxRetries);
2534
+ if (excessiveRetries.length > 0) {
2535
+ findings.push(
2536
+ failFinding(
2537
+ "tool.failures",
2538
+ `Tool retry count exceeded ${options.maxRetries}.`,
2539
+ excessiveRetries.map((item) => eventEvidence(item.event, "attributes.retryCount")),
2540
+ { maxRetries: options.maxRetries },
2541
+ excessiveRetries.map((item) => ({ tool: toolName(item.event), retries: item.count }))
2542
+ )
2543
+ );
2544
+ }
2545
+ }
2546
+ return findings;
2547
+ }
2548
+ };
2549
+ }
2450
2550
  function createLlmUsageRule(options) {
2451
2551
  return {
2452
2552
  id: "llm.usage",
@@ -5550,21 +5650,48 @@ function openTrace(input, options = {}) {
5550
5650
  }
5551
5651
 
5552
5652
  // packages/core/src/suite/run.ts
5653
+ var KNOWN_SUITE_SELECT_IDS = /* @__PURE__ */ new Set([
5654
+ "run.status",
5655
+ "run.duration",
5656
+ "run.depth",
5657
+ "outcome.status",
5658
+ "tool.usage",
5659
+ "tool.failures",
5660
+ "llm.usage"
5661
+ ]);
5553
5662
  function diagnostic4(code, message, severity = "error", caseId) {
5554
5663
  return { code, message, severity, ...caseId !== void 0 ? { caseId } : {} };
5555
5664
  }
5556
- function buildCaseRules(suiteCase, config) {
5665
+ function buildCaseAssertions(suiteCase, config) {
5557
5666
  const rules = [];
5558
5667
  const select = new Set(config.checks?.select ?? []);
5559
- if (select.has("run.status")) {
5668
+ const configDiagnostics = [];
5669
+ const evalConfig = config.eval;
5670
+ const declaredSelect = [...select];
5671
+ for (const id of declaredSelect) {
5672
+ if (!KNOWN_SUITE_SELECT_IDS.has(id)) {
5673
+ configDiagnostics.push(
5674
+ diagnostic4(
5675
+ "AI_SUITE_UNKNOWN_SELECTOR",
5676
+ `Unknown or unsupported suite check selector "${id}".`,
5677
+ "error",
5678
+ suiteCase.id
5679
+ )
5680
+ );
5681
+ }
5682
+ }
5683
+ if (select.has("run.status") || evalConfig?.requireSuccess === true) {
5560
5684
  rules.push(createRunStatusRule());
5685
+ select.add("run.status");
5561
5686
  }
5562
5687
  const requiredTools = [
5563
5688
  ...config.checks?.tool?.required ?? [],
5689
+ ...evalConfig?.requiredTools ?? [],
5564
5690
  ...suiteCase.requireTools ?? []
5565
5691
  ];
5566
5692
  const forbiddenTools = [
5567
5693
  ...config.checks?.tool?.forbidden ?? [],
5694
+ ...evalConfig?.forbiddenTools ?? [],
5568
5695
  ...suiteCase.forbidTools ?? []
5569
5696
  ];
5570
5697
  if (requiredTools.length > 0 || forbiddenTools.length > 0) {
@@ -5576,20 +5703,51 @@ function buildCaseRules(suiteCase, config) {
5576
5703
  );
5577
5704
  select.add("tool.usage");
5578
5705
  }
5579
- const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? config.eval?.maxDurationMs;
5706
+ const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? evalConfig?.maxDurationMs;
5580
5707
  if (maxDurationMs !== void 0) {
5581
5708
  rules.push(createRunDurationRule({ maxDurationMs }));
5582
5709
  select.add("run.duration");
5583
5710
  }
5584
- const llm = config.checks?.llm;
5585
- if (llm?.allowedModels !== void 0 || llm?.maxTotalTokens !== void 0) {
5586
- rules.push(createLlmUsageRule(llm));
5711
+ const maxDepth = config.checks?.run?.maxDepth ?? evalConfig?.maxDepth;
5712
+ if (maxDepth !== void 0) {
5713
+ rules.push(createRunDepthRule({ maxDepth }));
5714
+ select.add("run.depth");
5715
+ }
5716
+ if (evalConfig?.maxRetries !== void 0) {
5717
+ rules.push(createToolFailureRule({ maxRetries: evalConfig.maxRetries }));
5718
+ select.add("tool.failures");
5719
+ }
5720
+ const allowedModels = config.checks?.llm?.allowedModels;
5721
+ const maxTotalTokens = config.checks?.llm?.maxTotalTokens ?? evalConfig?.maxTotalTokens;
5722
+ if (allowedModels !== void 0 || maxTotalTokens !== void 0) {
5723
+ rules.push(
5724
+ createLlmUsageRule({
5725
+ ...allowedModels !== void 0 ? { allowedModels } : {},
5726
+ ...maxTotalTokens !== void 0 ? { maxTotalTokens } : {}
5727
+ })
5728
+ );
5587
5729
  select.add("llm.usage");
5588
5730
  }
5589
5731
  if (select.has("outcome.status")) {
5590
5732
  rules.push(createObservedOutcomeRule({ failOn: ["failed"] }));
5591
5733
  }
5592
- return { rules, select: [...select] };
5734
+ const observationCount = suiteCase.expectedObservations?.length ?? 0;
5735
+ if (rules.length === 0 && observationCount === 0 && configDiagnostics.length === 0) {
5736
+ configDiagnostics.push(
5737
+ diagnostic4(
5738
+ "AI_SUITE_NO_ASSERTIONS",
5739
+ `Suite case "${suiteCase.id}" declares no effective checks, eval controls, or expected observations.`,
5740
+ "error",
5741
+ suiteCase.id
5742
+ )
5743
+ );
5744
+ }
5745
+ return {
5746
+ rules,
5747
+ select: [...select],
5748
+ observationCount,
5749
+ configDiagnostics
5750
+ };
5593
5751
  }
5594
5752
  function outcomesFromRead(read) {
5595
5753
  const persistedOutcomes = extractOutcomesFromPersistedEvents(
@@ -5670,8 +5828,20 @@ async function runSuiteCase(suiteCase, config, options) {
5670
5828
  ]
5671
5829
  };
5672
5830
  }
5673
- const { rules, select } = buildCaseRules(suiteCase, config);
5674
- const checkResult = rules.length > 0 ? runTraceChecks({ read }, { rules, select }) : {
5831
+ const compiled = buildCaseAssertions(suiteCase, config);
5832
+ if (compiled.configDiagnostics.length > 0) {
5833
+ return {
5834
+ id: suiteCase.id,
5835
+ status: "error",
5836
+ tracePath: resolved.tracePath,
5837
+ ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5838
+ checkOk: false,
5839
+ ...config.eval?.requireSuccess === true ? { evalOk: false } : {},
5840
+ message: compiled.configDiagnostics.map((item) => item.message).join("; "),
5841
+ diagnostics: compiled.configDiagnostics
5842
+ };
5843
+ }
5844
+ const checkResult = compiled.rules.length > 0 ? runTraceChecks({ read }, { rules: compiled.rules, select: compiled.select }) : {
5675
5845
  ok: true,
5676
5846
  status: "pass",
5677
5847
  format: read.format,
@@ -5699,6 +5869,9 @@ async function runSuiteCase(suiteCase, config, options) {
5699
5869
  ];
5700
5870
  const checkOk = checkResult.ok;
5701
5871
  const observationsOk = observationResult.ok;
5872
+ const evalOk = config.eval?.requireSuccess === true ? checkResult.findings.every(
5873
+ (finding) => finding.ruleId !== "run.status" || finding.status !== "fail"
5874
+ ) && checkResult.diagnostics.every((item) => item.severity !== "error") : void 0;
5702
5875
  const ok = checkOk && observationsOk;
5703
5876
  const status = ok ? "pass" : checkResult.status === "error" ? "error" : "fail";
5704
5877
  return {
@@ -5707,6 +5880,7 @@ async function runSuiteCase(suiteCase, config, options) {
5707
5880
  tracePath: resolved.tracePath,
5708
5881
  ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5709
5882
  checkOk,
5883
+ ...evalOk !== void 0 ? { evalOk } : {},
5710
5884
  observationsOk,
5711
5885
  diagnostics,
5712
5886
  ...ok ? {} : {