@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.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
  };
@@ -1699,6 +1710,17 @@ var PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
1699
1710
  summary: "Trace contains a parentId cycle.",
1700
1711
  remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
1701
1712
  relatedCodes: ["structure.cycle"]
1713
+ },
1714
+ AI_TRACE_CONTRACT_INPUT_INVALID: {
1715
+ code: "AI_TRACE_CONTRACT_INPUT_INVALID",
1716
+ summary: "evaluateTraceContract expects ({ read }, contract) or use evaluateTraceContractRead(read, contract).",
1717
+ remediation: "Pass TraceCheckInput as the first argument: evaluateTraceContract({ read }, contract).",
1718
+ relatedCodes: ["AI_CHECK_INVALID_ARGUMENTS"]
1719
+ },
1720
+ AI_INSPECT_RUN_UNSUPPORTED_OPTION: {
1721
+ code: "AI_INSPECT_RUN_UNSUPPORTED_OPTION",
1722
+ summary: 'inspectRun does not accept a "writer" option.',
1723
+ remediation: "Use createInspector({ writer }) then inspector.run(...). inspectRun persists via the global JSONL path (schema 0.1)."
1702
1724
  }
1703
1725
  });
1704
1726
  function formatProgrammaticDiagnostic(code, detail) {
@@ -2254,6 +2276,13 @@ function metadataStringAttr(event, keys) {
2254
2276
  }
2255
2277
  return void 0;
2256
2278
  }
2279
+ function numericAttr(event, keys) {
2280
+ for (const key of keys) {
2281
+ const value = event.attributes?.[key];
2282
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2283
+ }
2284
+ return void 0;
2285
+ }
2257
2286
  function stripPrefix(name, prefixes) {
2258
2287
  for (const prefix of prefixes) {
2259
2288
  if (name.startsWith(prefix)) return name.slice(prefix.length);
@@ -2309,9 +2338,12 @@ function llmFinishReason(event) {
2309
2338
  const keys = ["finishReason", "rawFinishReason", "finish_reason"];
2310
2339
  return stringAttr(event, keys) ?? metadataStringAttr(event, keys);
2311
2340
  }
2341
+ function retryCount(event) {
2342
+ return numericAttr(event, ["retryCount", "retryAttempt", "retry_attempt", "attempt"]);
2343
+ }
2312
2344
  function finishedEvents(context, kind) {
2313
2345
  return semanticEvents(context).filter(
2314
- (event) => (event.kind === kind) && event.status !== "running"
2346
+ (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
2315
2347
  );
2316
2348
  }
2317
2349
  function toolInvocationEvents(context) {
@@ -2376,6 +2408,35 @@ function createRunDurationRule(options) {
2376
2408
  }
2377
2409
  };
2378
2410
  }
2411
+ function createRunDepthRule(options) {
2412
+ return {
2413
+ id: "run.depth",
2414
+ category: "run",
2415
+ defaultSeverity: "error",
2416
+ evaluate(context) {
2417
+ const nodes = [...context.nodesByEventId.values()];
2418
+ const maxDepth = nodes.reduce((max, node) => Math.max(max, node.depth), 0);
2419
+ if (maxDepth <= options.maxDepth) return [];
2420
+ const deepest = nodes.filter((node) => node.depth === maxDepth);
2421
+ return [
2422
+ failFinding(
2423
+ "run.depth",
2424
+ `Run depth ${maxDepth} exceeded ${options.maxDepth}.`,
2425
+ deepest.map((node) => ({
2426
+ runId: node.event.runId,
2427
+ eventId: node.event.eventId,
2428
+ parentId: node.event.parentId,
2429
+ kind: node.event.kind,
2430
+ name: node.event.name,
2431
+ status: node.event.status
2432
+ })),
2433
+ { maxDepth: options.maxDepth },
2434
+ maxDepth
2435
+ )
2436
+ ];
2437
+ }
2438
+ };
2439
+ }
2379
2440
  function createToolUsageRule(options) {
2380
2441
  return {
2381
2442
  id: "tool.usage",
@@ -2440,6 +2501,45 @@ function createToolUsageRule(options) {
2440
2501
  }
2441
2502
  };
2442
2503
  }
2504
+ function createToolFailureRule(options) {
2505
+ return {
2506
+ id: "tool.failures",
2507
+ category: "tool",
2508
+ defaultSeverity: "error",
2509
+ evaluate(context) {
2510
+ const tools = finishedEvents(context, "TOOL");
2511
+ const failures = tools.filter((event) => event.status === "error");
2512
+ const retries = tools.map((event) => ({ event, count: retryCount(event) })).filter((item) => item.count !== void 0);
2513
+ const findings = [];
2514
+ if (options.maxFailures !== void 0 && failures.length > options.maxFailures) {
2515
+ findings.push(
2516
+ failFinding(
2517
+ "tool.failures",
2518
+ `Tool failure count ${failures.length} exceeded ${options.maxFailures}.`,
2519
+ failures.map((event) => eventEvidence(event)),
2520
+ { maxFailures: options.maxFailures },
2521
+ failures.length
2522
+ )
2523
+ );
2524
+ }
2525
+ if (options.maxRetries !== void 0) {
2526
+ const excessiveRetries = retries.filter((item) => item.count > options.maxRetries);
2527
+ if (excessiveRetries.length > 0) {
2528
+ findings.push(
2529
+ failFinding(
2530
+ "tool.failures",
2531
+ `Tool retry count exceeded ${options.maxRetries}.`,
2532
+ excessiveRetries.map((item) => eventEvidence(item.event, "attributes.retryCount")),
2533
+ { maxRetries: options.maxRetries },
2534
+ excessiveRetries.map((item) => ({ tool: toolName(item.event), retries: item.count }))
2535
+ )
2536
+ );
2537
+ }
2538
+ }
2539
+ return findings;
2540
+ }
2541
+ };
2542
+ }
2443
2543
  function createLlmUsageRule(options) {
2444
2544
  return {
2445
2545
  id: "llm.usage",
@@ -5543,21 +5643,48 @@ function openTrace(input, options = {}) {
5543
5643
  }
5544
5644
 
5545
5645
  // packages/core/src/suite/run.ts
5646
+ var KNOWN_SUITE_SELECT_IDS = /* @__PURE__ */ new Set([
5647
+ "run.status",
5648
+ "run.duration",
5649
+ "run.depth",
5650
+ "outcome.status",
5651
+ "tool.usage",
5652
+ "tool.failures",
5653
+ "llm.usage"
5654
+ ]);
5546
5655
  function diagnostic4(code, message, severity = "error", caseId) {
5547
5656
  return { code, message, severity, ...caseId !== void 0 ? { caseId } : {} };
5548
5657
  }
5549
- function buildCaseRules(suiteCase, config) {
5658
+ function buildCaseAssertions(suiteCase, config) {
5550
5659
  const rules = [];
5551
5660
  const select = new Set(config.checks?.select ?? []);
5552
- if (select.has("run.status")) {
5661
+ const configDiagnostics = [];
5662
+ const evalConfig = config.eval;
5663
+ const declaredSelect = [...select];
5664
+ for (const id of declaredSelect) {
5665
+ if (!KNOWN_SUITE_SELECT_IDS.has(id)) {
5666
+ configDiagnostics.push(
5667
+ diagnostic4(
5668
+ "AI_SUITE_UNKNOWN_SELECTOR",
5669
+ `Unknown or unsupported suite check selector "${id}".`,
5670
+ "error",
5671
+ suiteCase.id
5672
+ )
5673
+ );
5674
+ }
5675
+ }
5676
+ if (select.has("run.status") || evalConfig?.requireSuccess === true) {
5553
5677
  rules.push(createRunStatusRule());
5678
+ select.add("run.status");
5554
5679
  }
5555
5680
  const requiredTools = [
5556
5681
  ...config.checks?.tool?.required ?? [],
5682
+ ...evalConfig?.requiredTools ?? [],
5557
5683
  ...suiteCase.requireTools ?? []
5558
5684
  ];
5559
5685
  const forbiddenTools = [
5560
5686
  ...config.checks?.tool?.forbidden ?? [],
5687
+ ...evalConfig?.forbiddenTools ?? [],
5561
5688
  ...suiteCase.forbidTools ?? []
5562
5689
  ];
5563
5690
  if (requiredTools.length > 0 || forbiddenTools.length > 0) {
@@ -5569,20 +5696,51 @@ function buildCaseRules(suiteCase, config) {
5569
5696
  );
5570
5697
  select.add("tool.usage");
5571
5698
  }
5572
- const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? config.eval?.maxDurationMs;
5699
+ const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? evalConfig?.maxDurationMs;
5573
5700
  if (maxDurationMs !== void 0) {
5574
5701
  rules.push(createRunDurationRule({ maxDurationMs }));
5575
5702
  select.add("run.duration");
5576
5703
  }
5577
- const llm = config.checks?.llm;
5578
- if (llm?.allowedModels !== void 0 || llm?.maxTotalTokens !== void 0) {
5579
- rules.push(createLlmUsageRule(llm));
5704
+ const maxDepth = config.checks?.run?.maxDepth ?? evalConfig?.maxDepth;
5705
+ if (maxDepth !== void 0) {
5706
+ rules.push(createRunDepthRule({ maxDepth }));
5707
+ select.add("run.depth");
5708
+ }
5709
+ if (evalConfig?.maxRetries !== void 0) {
5710
+ rules.push(createToolFailureRule({ maxRetries: evalConfig.maxRetries }));
5711
+ select.add("tool.failures");
5712
+ }
5713
+ const allowedModels = config.checks?.llm?.allowedModels;
5714
+ const maxTotalTokens = config.checks?.llm?.maxTotalTokens ?? evalConfig?.maxTotalTokens;
5715
+ if (allowedModels !== void 0 || maxTotalTokens !== void 0) {
5716
+ rules.push(
5717
+ createLlmUsageRule({
5718
+ ...allowedModels !== void 0 ? { allowedModels } : {},
5719
+ ...maxTotalTokens !== void 0 ? { maxTotalTokens } : {}
5720
+ })
5721
+ );
5580
5722
  select.add("llm.usage");
5581
5723
  }
5582
5724
  if (select.has("outcome.status")) {
5583
5725
  rules.push(createObservedOutcomeRule({ failOn: ["failed"] }));
5584
5726
  }
5585
- return { rules, select: [...select] };
5727
+ const observationCount = suiteCase.expectedObservations?.length ?? 0;
5728
+ if (rules.length === 0 && observationCount === 0 && configDiagnostics.length === 0) {
5729
+ configDiagnostics.push(
5730
+ diagnostic4(
5731
+ "AI_SUITE_NO_ASSERTIONS",
5732
+ `Suite case "${suiteCase.id}" declares no effective checks, eval controls, or expected observations.`,
5733
+ "error",
5734
+ suiteCase.id
5735
+ )
5736
+ );
5737
+ }
5738
+ return {
5739
+ rules,
5740
+ select: [...select],
5741
+ observationCount,
5742
+ configDiagnostics
5743
+ };
5586
5744
  }
5587
5745
  function outcomesFromRead(read) {
5588
5746
  const persistedOutcomes = extractOutcomesFromPersistedEvents(
@@ -5663,8 +5821,20 @@ async function runSuiteCase(suiteCase, config, options) {
5663
5821
  ]
5664
5822
  };
5665
5823
  }
5666
- const { rules, select } = buildCaseRules(suiteCase, config);
5667
- const checkResult = rules.length > 0 ? runTraceChecks({ read }, { rules, select }) : {
5824
+ const compiled = buildCaseAssertions(suiteCase, config);
5825
+ if (compiled.configDiagnostics.length > 0) {
5826
+ return {
5827
+ id: suiteCase.id,
5828
+ status: "error",
5829
+ tracePath: resolved.tracePath,
5830
+ ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5831
+ checkOk: false,
5832
+ ...config.eval?.requireSuccess === true ? { evalOk: false } : {},
5833
+ message: compiled.configDiagnostics.map((item) => item.message).join("; "),
5834
+ diagnostics: compiled.configDiagnostics
5835
+ };
5836
+ }
5837
+ const checkResult = compiled.rules.length > 0 ? runTraceChecks({ read }, { rules: compiled.rules, select: compiled.select }) : {
5668
5838
  ok: true,
5669
5839
  status: "pass",
5670
5840
  format: read.format,
@@ -5692,6 +5862,9 @@ async function runSuiteCase(suiteCase, config, options) {
5692
5862
  ];
5693
5863
  const checkOk = checkResult.ok;
5694
5864
  const observationsOk = observationResult.ok;
5865
+ const evalOk = config.eval?.requireSuccess === true ? checkResult.findings.every(
5866
+ (finding) => finding.ruleId !== "run.status" || finding.status !== "fail"
5867
+ ) && checkResult.diagnostics.every((item) => item.severity !== "error") : void 0;
5695
5868
  const ok = checkOk && observationsOk;
5696
5869
  const status = ok ? "pass" : checkResult.status === "error" ? "error" : "fail";
5697
5870
  return {
@@ -5700,6 +5873,7 @@ async function runSuiteCase(suiteCase, config, options) {
5700
5873
  tracePath: resolved.tracePath,
5701
5874
  ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
5702
5875
  checkOk,
5876
+ ...evalOk !== void 0 ? { evalOk } : {},
5703
5877
  observationsOk,
5704
5878
  diagnostics,
5705
5879
  ...ok ? {} : {