@agent-inspect/viewer 5.1.0 → 5.3.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.d.ts CHANGED
@@ -1,21 +1,423 @@
1
1
  import { Server } from 'node:http';
2
2
 
3
+ type ViewerMode = "traces" | "suite" | "workspace";
3
4
  interface ViewerServerOptions {
4
5
  traceDir?: string;
5
6
  host?: string;
6
7
  port?: number;
7
8
  maxEvents?: number;
9
+ mode?: ViewerMode;
10
+ suiteConfigPath?: string;
11
+ cwd?: string;
8
12
  }
9
13
  interface ViewerServerInfo {
10
14
  host: string;
11
15
  port: number;
12
16
  traceDir: string;
13
17
  url: string;
18
+ mode: ViewerMode;
14
19
  }
15
20
 
16
21
  declare function createViewerServer(options?: ViewerServerOptions): Server;
17
22
  declare function startViewerServer(options?: ViewerServerOptions): Promise<ViewerServerInfo>;
18
23
 
19
- declare const viewerIndexHtml = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>AgentInspect Viewer</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 1.5rem; line-height: 1.4; }\n h1 { font-size: 1.25rem; }\n pre { background: #f4f4f5; padding: 1rem; overflow: auto; max-height: 70vh; }\n a { color: #0b57d0; }\n .muted { color: #666; }\n </style>\n</head>\n<body>\n <h1>AgentInspect local viewer</h1>\n <p class=\"muted\">Read-only. JSONL on disk remains canonical.</p>\n <p><a href=\"/api/health\">/api/health</a> \u00B7 <a href=\"/api/traces\">/api/traces</a> \u00B7 <a href=\"/api/sessions\">/api/sessions</a></p>\n <pre id=\"out\">Loading traces\u2026</pre>\n <script>\n fetch(\"/api/traces\").then((r) => r.json()).then((data) => {\n document.getElementById(\"out\").textContent = JSON.stringify(data, null, 2);\n }).catch((err) => {\n document.getElementById(\"out\").textContent = String(err);\n });\n </script>\n</body>\n</html>\n";
24
+ declare const viewerIndexHtml = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>AgentInspect Viewer</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 1.5rem; line-height: 1.4; }\n h1 { font-size: 1.25rem; }\n pre { background: #f4f4f5; padding: 1rem; overflow: auto; max-height: 50vh; }\n a { color: #0b57d0; }\n .muted { color: #666; }\n table { border-collapse: collapse; width: 100%; margin: 1rem 0; }\n th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; vertical-align: top; }\n th { background: #f6f6f6; }\n .fail { color: #b42318; font-weight: 600; }\n .pass { color: #027a48; font-weight: 600; }\n section { margin: 1.5rem 0; }\n </style>\n</head>\n<body>\n <h1>AgentInspect local viewer</h1>\n <p class=\"muted\">Read-only. JSONL on disk remains canonical.</p>\n <p id=\"nav\"></p>\n <div id=\"content\"><pre id=\"out\">Loading\u2026</pre></div>\n <script>\n const params = new URLSearchParams(location.search);\n const mode = params.get(\"mode\") || \"traces\";\n\n function renderSuite(data) {\n const rows = data.cases.map((c) =>\n '<tr><td>' + c.id + '</td><td class=\"' + c.status + '\">' + c.status +\n '</td><td>' + (c.message || '') + '</td><td>' + c.toolPath.join(' \u2192 ') +\n '</td><td>' + c.observations.map((o) => o.name + ':' + o.status).join(', ') + '</td></tr>'\n ).join('');\n const failed = data.cases.filter((c) => c.status !== 'pass');\n const detail = failed.map((c) => {\n let html = '<h3>Case ' + c.id + '</h3><ul>';\n if (c.failureDiff) html += '<li>Diff errors: ' + c.failureDiff.summary.errors + '</li>';\n if (c.timeline) html += '<li>Timeline steps: ' + c.timeline.entries.length + '</li>';\n html += '<li>Diagnostics: ' + c.diagnostics.map((d) => d.message).join('; ') + '</li></ul>';\n return html;\n }).join('');\n return '<section><h2>Suite: ' + data.suiteName + ' (' + data.status + ')</h2>' +\n '<p>Passed ' + data.summary.passed + ', failed ' + data.summary.failed + '</p>' +\n '<table><thead><tr><th>Case</th><th>Status</th><th>Message</th><th>Tool path</th><th>Observations</th></tr></thead><tbody>' +\n rows + '</tbody></table>' +\n '<section><h2>Failure detail</h2>' + (detail || '<p class=\"pass\">No failures</p>') + '</section>' +\n '<section><h2>CI artifacts</h2><p>' + (data.ciArtifactsDir || 'n/a') + '</p></section>' +\n '<section><h2>Bundle export</h2><p>' + data.bundleExportHint + '</p></section>';\n }\n\n async function load() {\n const nav = document.getElementById(\"nav\");\n const out = document.getElementById(\"out\");\n const content = document.getElementById(\"content\");\n if (mode === \"suite\") {\n nav.innerHTML = '<a href=\"/api/suite\">/api/suite</a>';\n const data = await fetch(\"/api/suite\").then((r) => r.json());\n content.innerHTML = renderSuite(data);\n return;\n }\n if (mode === \"workspace\") {\n nav.innerHTML = '<a href=\"/api/workspace\">/api/workspace</a>';\n const data = await fetch(\"/api/workspace\").then((r) => r.json());\n content.innerHTML = '<section><h2>Workspace: ' + (data.project || 'workspace') + '</h2>' +\n '<p>Runs: ' + data.runs.length + '</p><pre>' + JSON.stringify(data, null, 2) + '</pre></section>';\n return;\n }\n nav.innerHTML = '<a href=\"/api/traces\">/api/traces</a> \u00B7 <a href=\"/api/sessions\">/api/sessions</a>';\n const data = await fetch(\"/api/traces\").then((r) => r.json());\n out.textContent = JSON.stringify(data, null, 2);\n }\n load().catch((err) => {\n document.getElementById(\"out\").textContent = String(err);\n });\n </script>\n</body>\n</html>\n";
20
25
 
21
- export { type ViewerServerInfo, type ViewerServerOptions, createViewerServer, startViewerServer, viewerIndexHtml };
26
+ /**
27
+ * Discriminator for what kind of work a {@link Step} represents.
28
+ * `"decision"` captures agent branching/choices; other values cover runs, LLM calls, tools, and user-defined steps.
29
+ */
30
+ type StepType = "run" | "llm" | "tool" | "decision" | "logic" | "state" | "custom";
31
+ /** Lifecycle state of a single {@link Step}. */
32
+ type StepStatus = "running" | "success" | "error";
33
+ /** Structured error attached to a run or step when status is `"error"`. */
34
+ interface ErrorInfo {
35
+ message: string;
36
+ stack?: string;
37
+ }
38
+ /**
39
+ * Optional token counts for a step (e.g. LLM usage).
40
+ * Reserved for future roadmap; MVP does not compute or persist token usage.
41
+ */
42
+ interface TokenMetadata {
43
+ input?: number;
44
+ output?: number;
45
+ total?: number;
46
+ cached?: number;
47
+ }
48
+ /** Arbitrary structured fields for a step; safe extensions use string keys. */
49
+ interface StepMetadata {
50
+ model?: string;
51
+ toolName?: string;
52
+ tokens?: TokenMetadata;
53
+ retryCount?: number;
54
+ [key: string]: unknown;
55
+ }
56
+ /** Version of the JSONL trace line schema consumed by AgentInspect tooling. */
57
+ type TraceSchemaVersion = "0.1";
58
+ /**
59
+ * Status for lightweight trace metadata extraction.
60
+ * `"unknown"` means the file contained valid events but a run status could not be determined safely.
61
+ */
62
+ type TraceMetadataStatus = "success" | "error" | "running" | "unknown";
63
+ /** Fields shared by every persisted trace event line. */
64
+ interface TraceEventBase {
65
+ schemaVersion: TraceSchemaVersion;
66
+ event: string;
67
+ timestamp: number;
68
+ }
69
+ /** Emitted when a run begins. */
70
+ interface RunStartedEvent extends TraceEventBase {
71
+ event: "run_started";
72
+ runId: string;
73
+ name: string;
74
+ startTime: number;
75
+ metadata?: Record<string, unknown>;
76
+ }
77
+ /** Emitted when a run finishes successfully or with an error. */
78
+ interface RunCompletedEvent extends TraceEventBase {
79
+ event: "run_completed";
80
+ runId: string;
81
+ status: "success" | "error";
82
+ endTime: number;
83
+ durationMs: number;
84
+ error?: ErrorInfo;
85
+ }
86
+ /** Emitted when a step begins (including nested steps under `parentId`). */
87
+ interface StepStartedEvent extends TraceEventBase {
88
+ event: "step_started";
89
+ runId: string;
90
+ stepId: string;
91
+ parentId?: string;
92
+ name: string;
93
+ type: StepType;
94
+ startTime: number;
95
+ metadata?: StepMetadata;
96
+ }
97
+ /**
98
+ * Emitted when a step finishes (success or failure).
99
+ * Failures use `status: "error"` and optional {@link ErrorInfo}; there is no separate `step_failed` event in MVP.
100
+ */
101
+ interface StepCompletedEvent extends TraceEventBase {
102
+ event: "step_completed";
103
+ runId: string;
104
+ stepId: string;
105
+ status: "success" | "error";
106
+ endTime: number;
107
+ durationMs: number;
108
+ error?: ErrorInfo;
109
+ }
110
+ /** Emitted when an external-world outcome is observed (v4.4+). */
111
+ interface OutcomeObservedEvent extends TraceEventBase {
112
+ event: "outcome_observed";
113
+ runId: string;
114
+ outcomeId: string;
115
+ parentId?: string;
116
+ name: string;
117
+ expectation: string;
118
+ status: "passed" | "failed" | "unknown" | "skipped";
119
+ method?: "dom" | "accessibility" | "snapshot" | "network" | "storage" | "filesystem" | "database" | "queue" | "custom";
120
+ actual?: unknown;
121
+ evidence?: unknown;
122
+ observedAt: number;
123
+ }
124
+ /** Discriminated union of all MVP trace events written as JSONL lines. */
125
+ type TraceEvent = RunStartedEvent | RunCompletedEvent | StepStartedEvent | StepCompletedEvent | OutcomeObservedEvent;
126
+
127
+ type ObservedOutcomeStatus = "passed" | "failed" | "unknown" | "skipped";
128
+ type ObservedOutcomeMethod = "dom" | "accessibility" | "snapshot" | "network" | "storage" | "filesystem" | "database" | "queue" | "custom";
129
+ interface ObservedOutcome {
130
+ outcomeId: string;
131
+ runId: string;
132
+ parentId?: string;
133
+ name: string;
134
+ expectation: string;
135
+ status: ObservedOutcomeStatus;
136
+ method?: ObservedOutcomeMethod;
137
+ actual?: unknown;
138
+ evidence?: unknown;
139
+ observedAt: number;
140
+ }
141
+
142
+ declare function extractOutcomesFromTraceEvents(events: readonly TraceEvent[]): ObservedOutcome[];
143
+
144
+ type TimelineFocus = "all" | "slow";
145
+ interface TimelineEntry {
146
+ stepId: string;
147
+ name: string;
148
+ type: StepType;
149
+ status: StepStatus;
150
+ depth: number;
151
+ startedAt: number;
152
+ offsetMs: number;
153
+ durationMs?: number;
154
+ isError: boolean;
155
+ slow?: boolean;
156
+ streaming?: {
157
+ chunkCount?: number;
158
+ streamDurationMs?: number;
159
+ streamedCharCount?: number;
160
+ };
161
+ }
162
+ interface RunTimeline {
163
+ runId: string;
164
+ name?: string;
165
+ status: TraceMetadataStatus;
166
+ startedAt?: number;
167
+ endedAt?: number;
168
+ durationMs?: number;
169
+ correlation?: {
170
+ correlationId?: string;
171
+ requestId?: string;
172
+ decisionId?: string;
173
+ groupId?: string;
174
+ };
175
+ entries: TimelineEntry[];
176
+ }
177
+ interface TimelineOptions {
178
+ focus?: TimelineFocus;
179
+ slowTopN?: number;
180
+ }
181
+ declare function buildRunTimeline(events: TraceEvent[], options?: TimelineOptions): RunTimeline;
182
+
183
+ type SuiteCaseStatus = "pass" | "fail" | "error" | "skipped";
184
+ 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";
185
+ interface SuiteDiagnostic {
186
+ code: SuiteDiagnosticCode;
187
+ severity: "error" | "warning" | "info";
188
+ message: string;
189
+ caseId?: string;
190
+ }
191
+ interface SuiteCaseResult {
192
+ id: string;
193
+ status: SuiteCaseStatus;
194
+ tracePath?: string;
195
+ runId?: string;
196
+ checkOk?: boolean;
197
+ evalOk?: boolean;
198
+ observationsOk?: boolean;
199
+ message?: string;
200
+ diagnostics: SuiteDiagnostic[];
201
+ }
202
+ interface SuiteRunSummary {
203
+ passed: number;
204
+ failed: number;
205
+ errors: number;
206
+ skipped: number;
207
+ }
208
+ interface SuiteRunResult {
209
+ ok: boolean;
210
+ status: "pass" | "fail" | "error";
211
+ suiteName: string;
212
+ configPath: string;
213
+ tracesDir: string;
214
+ startedAt: string;
215
+ finishedAt: string;
216
+ summary: SuiteRunSummary;
217
+ cases: SuiteCaseResult[];
218
+ diagnostics: SuiteDiagnostic[];
219
+ }
220
+
221
+ type DiffSeverity = "info" | "warning" | "error";
222
+ type DiffKind = "run-status" | "structure" | "step-added" | "step-removed" | "step-status" | "step-type" | "duration" | "error" | "metadata" | "output" | "first-divergence";
223
+ interface DiffPathSegment {
224
+ index: number;
225
+ name: string;
226
+ stepId?: string;
227
+ }
228
+ interface DiffPath {
229
+ path: DiffPathSegment[];
230
+ }
231
+ interface RunDiffItem {
232
+ kind: DiffKind;
233
+ severity: DiffSeverity;
234
+ message: string;
235
+ path?: DiffPath;
236
+ left?: unknown;
237
+ right?: unknown;
238
+ }
239
+ interface StepComparable {
240
+ id: string;
241
+ name: string;
242
+ type?: string;
243
+ status?: string;
244
+ durationMs?: number;
245
+ error?: string;
246
+ metadata?: Record<string, unknown>;
247
+ outputPreview?: unknown;
248
+ children: StepComparable[];
249
+ }
250
+ interface RunComparable {
251
+ runId: string;
252
+ name?: string;
253
+ status?: string;
254
+ durationMs?: number;
255
+ steps: StepComparable[];
256
+ }
257
+ interface RunDiffSummary {
258
+ leftRunId: string;
259
+ rightRunId: string;
260
+ totalDifferences: number;
261
+ errors: number;
262
+ warnings: number;
263
+ info: number;
264
+ firstDivergence?: RunDiffItem;
265
+ }
266
+ interface RunDiffResult {
267
+ summary: RunDiffSummary;
268
+ differences: RunDiffItem[];
269
+ }
270
+ interface DiffOptions {
271
+ ignoreDuration?: boolean;
272
+ durationThresholdMs?: number;
273
+ focus?: "all" | "errors" | "structure" | "outputs";
274
+ check?: "all" | "structure" | "outputs" | "errors" | "timing";
275
+ }
276
+
277
+ declare function diffRuns(left: RunComparable, right: RunComparable, options?: DiffOptions): RunDiffResult;
278
+
279
+ interface SuiteCaseViewerDetail {
280
+ id: string;
281
+ status: SuiteCaseResult["status"];
282
+ tracePath?: string;
283
+ runId?: string;
284
+ message?: string;
285
+ diagnostics: SuiteCaseResult["diagnostics"];
286
+ timeline?: ReturnType<typeof buildRunTimeline>;
287
+ toolPath: string[];
288
+ observations: ReturnType<typeof extractOutcomesFromTraceEvents>;
289
+ failureDiff?: {
290
+ summary: ReturnType<typeof diffRuns>["summary"];
291
+ differences: Array<{
292
+ kind: string;
293
+ message: string;
294
+ }>;
295
+ };
296
+ }
297
+ interface SuiteViewerData {
298
+ suiteName: string;
299
+ configPath: string;
300
+ tracesDir: string;
301
+ ok: boolean;
302
+ status: SuiteRunResult["status"];
303
+ summary: SuiteRunResult["summary"];
304
+ cases: SuiteCaseViewerDetail[];
305
+ ciArtifactsDir?: string;
306
+ bundleExportHint: string;
307
+ }
308
+ declare function loadSuiteViewerData(options: {
309
+ suiteConfigPath?: string;
310
+ cwd?: string;
311
+ }): Promise<SuiteViewerData>;
312
+
313
+ /**
314
+ * Internal workspace manifest types (v4.0).
315
+ *
316
+ * @remarks
317
+ * Experimental and internal to `@agent-inspect/core`. This module is not part
318
+ * of any published entry point. Adding a public `agent-inspect/workspace`
319
+ * export is a separate, maintainer-gated step (see
320
+ * `docs/proposals/LOCAL-TRACE-WORKSPACE.md`).
321
+ */
322
+ /** Fixed manifest schema version for the v4.0 workspace model. */
323
+ declare const WORKSPACE_SCHEMA_VERSION: "1.0";
324
+ /** Default share-safety posture applied to a workspace. */
325
+ type WorkspaceRedactionProfile = "local" | "share" | "strict";
326
+ /** Optional local index kind (SQLite index arrives as an opt-in package in v4.1). */
327
+ type WorkspaceIndexType = "none" | "sqlite" | "custom";
328
+ /** Optional local index descriptor. */
329
+ interface WorkspaceIndexConfig {
330
+ enabled: boolean;
331
+ type: WorkspaceIndexType;
332
+ path?: string;
333
+ }
334
+ /**
335
+ * The `.agent-inspect/workspace.json` manifest.
336
+ *
337
+ * @remarks
338
+ * All directory fields are paths relative to the workspace root and must
339
+ * resolve inside it (no absolute paths, no `..` traversal).
340
+ */
341
+ interface AgentInspectWorkspaceManifest {
342
+ schemaVersion: typeof WORKSPACE_SCHEMA_VERSION;
343
+ project: string;
344
+ createdAt: string;
345
+ traceDirs: string[];
346
+ reportsDir: string;
347
+ artifactsDir: string;
348
+ bundlesDir: string;
349
+ notesDir: string;
350
+ redactionProfile: WorkspaceRedactionProfile;
351
+ index: WorkspaceIndexConfig;
352
+ }
353
+
354
+ /**
355
+ * Internal workspace filesystem helpers (v4.0).
356
+ *
357
+ * @remarks
358
+ * Local-only. Never deletes trace files. All manifest-derived paths are
359
+ * resolved and confirmed to stay within the workspace directory
360
+ * (path-traversal guarded). No network access.
361
+ */
362
+ /** Resolved on-disk location of a workspace. */
363
+ interface WorkspaceLocation {
364
+ /** Project directory that contains the `.agent-inspect` folder. */
365
+ projectRoot: string;
366
+ /** The `.agent-inspect` workspace directory (root for relative manifest paths). */
367
+ workspaceDir: string;
368
+ /** Absolute path to `workspace.json`. */
369
+ manifestPath: string;
370
+ }
371
+ /** Index presence/status for {@link getWorkspaceStatus}. */
372
+ interface WorkspaceIndexStatus {
373
+ enabled: boolean;
374
+ type: string;
375
+ exists: boolean;
376
+ }
377
+ /** Aggregate, read-only workspace status. */
378
+ interface WorkspaceStatus {
379
+ project: string;
380
+ traceFiles: number;
381
+ reports: number;
382
+ artifacts: number;
383
+ bundles: number;
384
+ notes: number;
385
+ index: WorkspaceIndexStatus;
386
+ }
387
+ /** Computes read-only counts for a workspace. Requires a valid manifest. */
388
+ declare function getWorkspaceStatus(location: WorkspaceLocation, manifest: AgentInspectWorkspaceManifest): Promise<WorkspaceStatus>;
389
+ /** A single workspace doctor check. */
390
+ interface WorkspaceDoctorCheck {
391
+ id: string;
392
+ status: "pass" | "warn" | "fail";
393
+ message: string;
394
+ }
395
+ /** Result of {@link doctorWorkspace}. */
396
+ interface WorkspaceDoctorResult {
397
+ ok: boolean;
398
+ checks: WorkspaceDoctorCheck[];
399
+ }
400
+ /**
401
+ * Validates a workspace: manifest presence/shape, folder permissions, trace
402
+ * readability, and index staleness. Read-only; never throws.
403
+ */
404
+ declare function doctorWorkspace(location: WorkspaceLocation): Promise<WorkspaceDoctorResult>;
405
+
406
+ interface WorkspaceViewerData {
407
+ workspaceDir: string;
408
+ project?: string;
409
+ status: Awaited<ReturnType<typeof getWorkspaceStatus>>;
410
+ doctor: Awaited<ReturnType<typeof doctorWorkspace>>;
411
+ runs: Array<{
412
+ runId: string;
413
+ name?: string;
414
+ status: string;
415
+ file: string;
416
+ }>;
417
+ bundleDirs: string[];
418
+ }
419
+ declare function loadWorkspaceViewerData(options: {
420
+ cwd?: string;
421
+ }): Promise<WorkspaceViewerData>;
422
+
423
+ export { type ViewerMode, type ViewerServerInfo, type ViewerServerOptions, createViewerServer, loadSuiteViewerData, loadWorkspaceViewerData, startViewerServer, viewerIndexHtml };