@agent-inspect/viewer 4.3.0 → 5.0.0

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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createServer } from 'http';
2
- import path7 from 'path';
2
+ import path11 from 'path';
3
3
  import { AsyncLocalStorage } from 'async_hooks';
4
4
  import 'crypto';
5
5
  import { readdir, stat, readFile } from 'fs/promises';
@@ -8,6 +8,7 @@ import 'nanoid';
8
8
  import 'chalk';
9
9
  import 'fs';
10
10
  import 'readline';
11
+ import 'url';
11
12
 
12
13
  // packages/viewer/src/server.ts
13
14
 
@@ -71,6 +72,9 @@ function isTraceEvent(value) {
71
72
  case "step_completed": {
72
73
  return typeof value.runId === "string" && typeof value.stepId === "string" && (value.status === "success" || value.status === "error") && typeof value.endTime === "number" && typeof value.durationMs === "number";
73
74
  }
75
+ case "outcome_observed": {
76
+ return typeof value.runId === "string" && typeof value.outcomeId === "string" && typeof value.name === "string" && typeof value.expectation === "string" && (value.status === "passed" || value.status === "failed" || value.status === "unknown" || value.status === "skipped") && typeof value.observedAt === "number";
77
+ }
74
78
  default:
75
79
  return false;
76
80
  }
@@ -88,7 +92,8 @@ var INSPECT_KINDS = [
88
92
  "RESULT",
89
93
  "ERROR",
90
94
  "LOGIC",
91
- "LOG"
95
+ "LOG",
96
+ "OUTCOME"
92
97
  ];
93
98
  var ATTRIBUTION_CONFIDENCES = [
94
99
  "explicit",
@@ -380,6 +385,32 @@ function fromLegacyStepCompleted(event) {
380
385
  if (error) out.error = error;
381
386
  return out;
382
387
  }
388
+ function fromLegacyOutcomeObserved(event) {
389
+ const attrs = event.attributes ?? {};
390
+ const observedAtRaw = attrs.observedAt;
391
+ const observedAt = typeof observedAtRaw === "string" ? Date.parse(observedAtRaw) : typeof observedAtRaw === "number" && Number.isFinite(observedAtRaw) ? observedAtRaw : resolveTimes(event).timestamp;
392
+ const status = attrs.outcomeStatus;
393
+ const out = {
394
+ schemaVersion: "0.1",
395
+ event: "outcome_observed",
396
+ timestamp: observedAt,
397
+ runId: event.runId,
398
+ outcomeId: typeof attrs.outcomeId === "string" ? attrs.outcomeId : event.eventId,
399
+ name: event.name,
400
+ expectation: typeof attrs.expectation === "string" ? attrs.expectation : event.name,
401
+ status: status === "passed" || status === "failed" || status === "unknown" || status === "skipped" ? status : "unknown",
402
+ observedAt
403
+ };
404
+ if (event.parentId !== void 0) out.parentId = event.parentId;
405
+ if (typeof attrs.method === "string") out.method = attrs.method;
406
+ if (attrs.actual !== void 0) out.actual = attrs.actual;
407
+ if (event.outputSummary !== void 0) out.actual = event.outputSummary;
408
+ if (attrs.evidence !== void 0) out.evidence = attrs.evidence;
409
+ return out;
410
+ }
411
+ function fromNativeOutcome(event) {
412
+ return [fromLegacyOutcomeObserved(event)];
413
+ }
383
414
  function fromNativeRun(event) {
384
415
  const { timestamp, startTime, endTime } = resolveTimes(event);
385
416
  const runStatus = mapPersistedStatusToRunStatus(event.status);
@@ -467,9 +498,13 @@ function persistedInspectEventToTraceEvents(event) {
467
498
  if (legacyEvent === "run_completed") return [fromLegacyRunCompleted(event)];
468
499
  if (legacyEvent === "step_started") return [fromLegacyStepStarted(event)];
469
500
  if (legacyEvent === "step_completed") return [fromLegacyStepCompleted(event)];
501
+ if (legacyEvent === "outcome_observed") return [fromLegacyOutcomeObserved(event)];
470
502
  if (event.kind === "RUN") {
471
503
  return fromNativeRun(event);
472
504
  }
505
+ if (event.kind === "OUTCOME") {
506
+ return fromNativeOutcome(event);
507
+ }
473
508
  return fromNativeStep(event);
474
509
  }
475
510
  function persistedInspectEventsToTraceEvents(events, options) {
@@ -483,7 +518,7 @@ function persistedInspectEventsToTraceEvents(events, options) {
483
518
  }
484
519
  var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
485
520
  var RUNS_DIR_NAME = "runs";
486
- var FALLBACK_TRACE_DIR = path7.join(
521
+ var FALLBACK_TRACE_DIR = path11.join(
487
522
  os.tmpdir(),
488
523
  "agent-inspect",
489
524
  RUNS_DIR_NAME
@@ -498,7 +533,7 @@ function getDefaultTraceDir() {
498
533
  if (typeof home !== "string" || home.trim() === "") {
499
534
  return FALLBACK_TRACE_DIR;
500
535
  }
501
- return path7.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
536
+ return path11.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
502
537
  } catch {
503
538
  return FALLBACK_TRACE_DIR;
504
539
  }
@@ -669,6 +704,9 @@ function validateEvent(event) {
669
704
  case "step_completed": {
670
705
  return nonEmptyString(event.runId) && nonEmptyString(event.stepId) && (event.status === "success" || event.status === "error") && finiteNumber(event.endTime) && finiteNumber(event.durationMs) && optionalErrorInfo(event.error);
671
706
  }
707
+ case "outcome_observed": {
708
+ return nonEmptyString(event.runId) && nonEmptyString(event.outcomeId) && nonEmptyString(event.name) && nonEmptyString(event.expectation) && (event.status === "passed" || event.status === "failed" || event.status === "unknown" || event.status === "skipped") && finiteNumber(event.observedAt);
709
+ }
672
710
  default:
673
711
  return false;
674
712
  }
@@ -704,7 +742,7 @@ var TraceDirectory = class {
704
742
  this.#dir = resolveTraceDir(options);
705
743
  }
706
744
  getPath(filename) {
707
- return filename ? path7.join(this.#dir, filename) : this.#dir;
745
+ return filename ? path11.join(this.#dir, filename) : this.#dir;
708
746
  }
709
747
  async list() {
710
748
  try {
@@ -731,7 +769,7 @@ function parseIsoToMs2(value) {
731
769
  }
732
770
  async function extractMetadata(filePath, _quickScan) {
733
771
  const stats = await stat(filePath);
734
- let runIdFromFile = path7.basename(filePath);
772
+ let runIdFromFile = path11.basename(filePath);
735
773
  if (runIdFromFile.endsWith(".jsonl")) {
736
774
  runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
737
775
  }
@@ -1341,12 +1379,12 @@ function buildCriticalPath(runs, handoffs) {
1341
1379
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
1342
1380
  );
1343
1381
  const ordered = [...runs].sort(compareRuns);
1344
- const path8 = [];
1382
+ const path12 = [];
1345
1383
  const visited = /* @__PURE__ */ new Set();
1346
1384
  const pushRun = (run, confidence, source) => {
1347
1385
  if (visited.has(run.runId)) return;
1348
1386
  visited.add(run.runId);
1349
- path8.push({
1387
+ path12.push({
1350
1388
  runId: run.runId,
1351
1389
  name: run.name,
1352
1390
  startedAt: run.startedAt,
@@ -1371,7 +1409,7 @@ function buildCriticalPath(runs, handoffs) {
1371
1409
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
1372
1410
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
1373
1411
  }
1374
- return path8;
1412
+ return path12;
1375
1413
  }
1376
1414
  function metaRunIdMatches(run, token, runById) {
1377
1415
  const meta = extractSessionWorkflowMetadata(run.metadata);
@@ -1644,7 +1682,7 @@ function summarize(findings, diagnostics) {
1644
1682
  errors: diagnostics.filter((item) => item.severity === "error").length
1645
1683
  };
1646
1684
  }
1647
- function eventEvidence(event, path8) {
1685
+ function eventEvidence(event, path12) {
1648
1686
  return {
1649
1687
  runId: event.runId,
1650
1688
  eventId: event.eventId,
@@ -1793,6 +1831,8 @@ function nodeIdForEvent(event) {
1793
1831
  case "step_started":
1794
1832
  case "step_completed":
1795
1833
  return event.stepId;
1834
+ case "outcome_observed":
1835
+ return event.outcomeId;
1796
1836
  default:
1797
1837
  return "unknown";
1798
1838
  }
@@ -1976,6 +2016,39 @@ function traceEventToPersistedInspectEvent(event, options) {
1976
2016
  error
1977
2017
  };
1978
2018
  }
2019
+ case "outcome_observed": {
2020
+ const tsObserved = toIsoTimestamp(event.observedAt);
2021
+ const attributes = compactAttributes({
2022
+ legacyEvent: "outcome_observed",
2023
+ outcomeId: event.outcomeId,
2024
+ outcomeStatus: event.status,
2025
+ expectation: event.expectation,
2026
+ method: event.method,
2027
+ actual: event.actual,
2028
+ evidence: event.evidence,
2029
+ observedAt: tsObserved.iso,
2030
+ invalidTimestamp: tsMain.invalidTimestamp || tsObserved.invalidTimestamp ? true : void 0
2031
+ });
2032
+ const out = {
2033
+ schemaVersion: "0.2",
2034
+ eventId,
2035
+ runId: event.runId,
2036
+ kind: "OUTCOME",
2037
+ name: event.name,
2038
+ status: event.status === "failed" ? "error" : "ok",
2039
+ timestamp: tsMain.iso,
2040
+ confidence: "explicit",
2041
+ source,
2042
+ attributes
2043
+ };
2044
+ if (event.parentId !== void 0) {
2045
+ out.parentId = event.parentId;
2046
+ }
2047
+ if (event.actual !== void 0) {
2048
+ out.outputSummary = event.actual;
2049
+ }
2050
+ return out;
2051
+ }
1979
2052
  default: {
1980
2053
  const _exhaustive = event;
1981
2054
  throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
@@ -1988,6 +2061,86 @@ function traceEventsToPersistedInspectEvents(events, options) {
1988
2061
  );
1989
2062
  }
1990
2063
 
2064
+ // packages/core/src/logs/tree-builder.ts
2065
+ function inc(map, key) {
2066
+ map[key] = (map[key] ?? 0) + 1;
2067
+ }
2068
+ function computeRunStatus(events) {
2069
+ let hasRunning = false;
2070
+ for (const e of events) {
2071
+ if (e.status === "error") return "error";
2072
+ if (e.status === "running") hasRunning = true;
2073
+ }
2074
+ if (hasRunning) return "running";
2075
+ return "ok";
2076
+ }
2077
+ var TreeBuilder = class {
2078
+ constructor(options) {
2079
+ void options?.config;
2080
+ }
2081
+ build(events) {
2082
+ const byRun = /* @__PURE__ */ new Map();
2083
+ for (const e of events) {
2084
+ if (!byRun.has(e.runId)) byRun.set(e.runId, []);
2085
+ byRun.get(e.runId).push(e);
2086
+ }
2087
+ const out = [];
2088
+ for (const [runId, runEvents] of byRun.entries()) {
2089
+ const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
2090
+ const nodes = /* @__PURE__ */ new Map();
2091
+ for (const e of sorted) {
2092
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
2093
+ }
2094
+ const roots = [];
2095
+ for (const node of nodes.values()) {
2096
+ const parentId = node.event.parentId;
2097
+ if (parentId && nodes.has(parentId)) {
2098
+ nodes.get(parentId).children.push(node);
2099
+ } else {
2100
+ roots.push(node);
2101
+ }
2102
+ }
2103
+ const assignDepth = (n, depth) => {
2104
+ n.depth = depth;
2105
+ for (const c of n.children) assignDepth(c, depth + 1);
2106
+ };
2107
+ for (const r of roots) assignDepth(r, 0);
2108
+ const confidenceBreakdown = {
2109
+ explicit: 0,
2110
+ correlated: 0,
2111
+ heuristic: 0,
2112
+ unknown: 0
2113
+ };
2114
+ const kinds = {};
2115
+ for (const e of sorted) {
2116
+ inc(confidenceBreakdown, e.confidence);
2117
+ kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
2118
+ }
2119
+ const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
2120
+ const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
2121
+ const status = computeRunStatus(sorted);
2122
+ const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
2123
+ const name = sorted.find((e) => e.kind === "RUN")?.name;
2124
+ out.push({
2125
+ runId,
2126
+ name,
2127
+ status,
2128
+ startedAt,
2129
+ endedAt: status === "running" ? void 0 : endedAt,
2130
+ durationMs,
2131
+ children: roots,
2132
+ metadata: {
2133
+ totalEvents: sorted.length,
2134
+ confidenceBreakdown,
2135
+ kinds
2136
+ }
2137
+ });
2138
+ }
2139
+ out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
2140
+ return out;
2141
+ }
2142
+ };
2143
+
1991
2144
  // packages/core/src/persisted/to-inspect-event.ts
1992
2145
  function compactAttributes2(entries) {
1993
2146
  const out = {};
@@ -2126,86 +2279,6 @@ function persistedInspectEventsToInspectEvents(events, options) {
2126
2279
  return out;
2127
2280
  }
2128
2281
 
2129
- // packages/core/src/logs/tree-builder.ts
2130
- function inc(map, key) {
2131
- map[key] = (map[key] ?? 0) + 1;
2132
- }
2133
- function computeRunStatus(events) {
2134
- let hasRunning = false;
2135
- for (const e of events) {
2136
- if (e.status === "error") return "error";
2137
- if (e.status === "running") hasRunning = true;
2138
- }
2139
- if (hasRunning) return "running";
2140
- return "ok";
2141
- }
2142
- var TreeBuilder = class {
2143
- constructor(options) {
2144
- void options?.config;
2145
- }
2146
- build(events) {
2147
- const byRun = /* @__PURE__ */ new Map();
2148
- for (const e of events) {
2149
- if (!byRun.has(e.runId)) byRun.set(e.runId, []);
2150
- byRun.get(e.runId).push(e);
2151
- }
2152
- const out = [];
2153
- for (const [runId, runEvents] of byRun.entries()) {
2154
- const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
2155
- const nodes = /* @__PURE__ */ new Map();
2156
- for (const e of sorted) {
2157
- nodes.set(e.eventId, { event: e, children: [], depth: 0 });
2158
- }
2159
- const roots = [];
2160
- for (const node of nodes.values()) {
2161
- const parentId = node.event.parentId;
2162
- if (parentId && nodes.has(parentId)) {
2163
- nodes.get(parentId).children.push(node);
2164
- } else {
2165
- roots.push(node);
2166
- }
2167
- }
2168
- const assignDepth = (n, depth) => {
2169
- n.depth = depth;
2170
- for (const c of n.children) assignDepth(c, depth + 1);
2171
- };
2172
- for (const r of roots) assignDepth(r, 0);
2173
- const confidenceBreakdown = {
2174
- explicit: 0,
2175
- correlated: 0,
2176
- heuristic: 0,
2177
- unknown: 0
2178
- };
2179
- const kinds = {};
2180
- for (const e of sorted) {
2181
- inc(confidenceBreakdown, e.confidence);
2182
- kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
2183
- }
2184
- const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
2185
- const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
2186
- const status = computeRunStatus(sorted);
2187
- const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
2188
- const name = sorted.find((e) => e.kind === "RUN")?.name;
2189
- out.push({
2190
- runId,
2191
- name,
2192
- status,
2193
- startedAt,
2194
- endedAt: status === "running" ? void 0 : endedAt,
2195
- durationMs,
2196
- children: roots,
2197
- metadata: {
2198
- totalEvents: sorted.length,
2199
- confidenceBreakdown,
2200
- kinds
2201
- }
2202
- });
2203
- }
2204
- out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
2205
- return out;
2206
- }
2207
- };
2208
-
2209
2282
  // packages/core/src/persisted/tree-bridge.ts
2210
2283
  function persistedInspectEventsToRunTrees(events, options) {
2211
2284
  const inspectEvents = persistedInspectEventsToInspectEvents(events, {
@@ -2213,6 +2286,8 @@ function persistedInspectEventsToRunTrees(events, options) {
2213
2286
  });
2214
2287
  return new TreeBuilder().build(inspectEvents);
2215
2288
  }
2289
+
2290
+ // packages/core/src/readers/index.ts
2216
2291
  var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
2217
2292
  var MIN_DETECTION_CONFIDENCE = 0.5;
2218
2293
  var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
@@ -2335,7 +2410,7 @@ function findReaderByFormat(format, readers) {
2335
2410
  }
2336
2411
  async function jsonlFilesInDirectory(dirPath) {
2337
2412
  const entries = await readdir(dirPath, { withFileTypes: true });
2338
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path7.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
2413
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path11.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
2339
2414
  }
2340
2415
  async function resolveInput(input) {
2341
2416
  const cached = resolvedInputCache.get(input);
@@ -2705,7 +2780,7 @@ function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
2705
2780
  function mapOpenInferenceKind(span, attributes, pathPrefix) {
2706
2781
  const warnings = [];
2707
2782
  const agentInspectKind = attributes["agent_inspect.kind"];
2708
- if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
2783
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG" || agentInspectKind === "OUTCOME") {
2709
2784
  return { kind: agentInspectKind, warnings };
2710
2785
  }
2711
2786
  const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
@@ -3226,7 +3301,7 @@ function mapOtlpStatus(status) {
3226
3301
  function readOtlpKind(attributes, pathPrefix) {
3227
3302
  const warnings = [];
3228
3303
  const agentInspectKind = attributes["agent_inspect.kind"];
3229
- if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
3304
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG" || agentInspectKind === "OUTCOME") {
3230
3305
  return { kind: agentInspectKind, warnings };
3231
3306
  }
3232
3307
  const operation = attributes["gen_ai.operation.name"];
@@ -3872,7 +3947,7 @@ function createViewerServer(options = {}) {
3872
3947
  return sendJson(res, 200, {
3873
3948
  ok: true,
3874
3949
  readOnly: true,
3875
- traceDir: path7.resolve(traceDir)
3950
+ traceDir: path11.resolve(traceDir)
3876
3951
  });
3877
3952
  }
3878
3953
  const td = new TraceDirectory({ dir: traceDir });
@@ -3890,7 +3965,7 @@ function createViewerServer(options = {}) {
3890
3965
  runId: meta.runId,
3891
3966
  name: meta.name,
3892
3967
  status: meta.status,
3893
- file: path7.basename(meta.filePath),
3968
+ file: path11.basename(meta.filePath),
3894
3969
  startedAt: meta.startedAt,
3895
3970
  durationMs: meta.durationMs
3896
3971
  }))
@@ -4005,7 +4080,7 @@ function startViewerServer(options = {}) {
4005
4080
  resolve({
4006
4081
  host,
4007
4082
  port: resolvedPort,
4008
- traceDir: path7.resolve(traceDir),
4083
+ traceDir: path11.resolve(traceDir),
4009
4084
  url: `http://${host}:${resolvedPort}`
4010
4085
  });
4011
4086
  });