agent-inspect 4.2.0 → 4.4.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/CHANGELOG.md +12 -0
- package/docs/API.md +3 -0
- package/docs/CLI.md +36 -0
- package/docs/SCHEMA.md +27 -1
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-5VSPJEZ7.mjs → chunk-ORHDQUT6.mjs} +385 -10
- package/packages/cli/dist/chunk-ORHDQUT6.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +2548 -1567
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +2028 -1495
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-VQT7QLGV.mjs → src-FJOHPC4X.mjs} +3 -3
- package/packages/cli/dist/{src-VQT7QLGV.mjs.map → src-FJOHPC4X.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +397 -12
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +105 -9
- package/packages/core/dist/advanced.d.ts +105 -9
- package/packages/core/dist/advanced.mjs +219 -17
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.cjs +117 -18
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.d.cts +11 -3
- package/packages/core/dist/checks.d.ts +11 -3
- package/packages/core/dist/checks.mjs +42 -1
- package/packages/core/dist/checks.mjs.map +1 -1
- package/packages/core/dist/{chunk-2ZXEMWGK.mjs → chunk-3USJVBLA.mjs} +3 -3
- package/packages/core/dist/{chunk-2ZXEMWGK.mjs.map → chunk-3USJVBLA.mjs.map} +1 -1
- package/packages/core/dist/{chunk-HY7H3CQM.mjs → chunk-CAWPF22J.mjs} +4 -3
- package/packages/core/dist/chunk-CAWPF22J.mjs.map +1 -0
- package/packages/core/dist/{chunk-XUNJMAPD.mjs → chunk-KNYL56KZ.mjs} +38 -3
- package/packages/core/dist/chunk-KNYL56KZ.mjs.map +1 -0
- package/packages/core/dist/{chunk-IZBJAZGF.mjs → chunk-P366CCL2.mjs} +34 -3
- package/packages/core/dist/chunk-P366CCL2.mjs.map +1 -0
- package/packages/core/dist/{chunk-4JVGFNQH.mjs → chunk-ULCGIRTC.mjs} +95 -8
- package/packages/core/dist/chunk-ULCGIRTC.mjs.map +1 -0
- package/packages/core/dist/chunk-VFO76UH3.mjs +228 -0
- package/packages/core/dist/chunk-VFO76UH3.mjs.map +1 -0
- package/packages/core/dist/{chunk-NQZPZ2FN.mjs → chunk-WRE4CNPF.mjs} +6 -3
- package/packages/core/dist/chunk-WRE4CNPF.mjs.map +1 -0
- package/packages/core/dist/{chunk-A4V2H3YU.mjs → chunk-ZWA5RWMV.mjs} +40 -4
- package/packages/core/dist/chunk-ZWA5RWMV.mjs.map +1 -0
- package/packages/core/dist/{context-DjY-jvQk.d.cts → context-BbfkMYjz.d.cts} +5 -3
- package/packages/core/dist/{context-BDZmi53V.d.ts → context-Cp8NRbBX.d.ts} +5 -3
- package/packages/core/dist/diff.cjs.map +1 -1
- package/packages/core/dist/diff.d.cts +3 -3
- package/packages/core/dist/diff.d.ts +3 -3
- package/packages/core/dist/diff.mjs +1 -1
- package/packages/core/dist/exporters.cjs +137 -1
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.d.cts +6 -3
- package/packages/core/dist/exporters.d.ts +6 -3
- package/packages/core/dist/exporters.mjs +71 -6
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/index.cjs +181 -6
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.d.cts +14 -7
- package/packages/core/dist/index.d.ts +14 -7
- package/packages/core/dist/index.mjs +51 -7
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/{inspect-event-CYAV7Hxm.d.cts → inspect-event-IqwOHbGo.d.cts} +1 -1
- package/packages/core/dist/{inspect-event-CYAV7Hxm.d.ts → inspect-event-IqwOHbGo.d.ts} +1 -1
- package/packages/core/dist/{log-config-BNQ9UTmP.d.cts → log-config-CW8w5YQ9.d.cts} +1 -1
- package/packages/core/dist/{log-config-D3Yp1SZN.d.ts → log-config-SRA4xTp8.d.ts} +1 -1
- package/packages/core/dist/logs.d.cts +3 -3
- package/packages/core/dist/logs.d.ts +3 -3
- package/packages/core/dist/{persisted-inspect-event-BLvb0jSX.d.ts → persisted-inspect-event-DruwqARZ.d.ts} +1 -1
- package/packages/core/dist/{persisted-inspect-event-D5SjBz9b.d.cts → persisted-inspect-event-Gd-W8vB7.d.cts} +1 -1
- package/packages/core/dist/persisted.cjs +67 -1
- package/packages/core/dist/persisted.cjs.map +1 -1
- package/packages/core/dist/persisted.d.cts +5 -5
- package/packages/core/dist/persisted.d.ts +5 -5
- package/packages/core/dist/persisted.mjs +3 -3
- package/packages/core/dist/readers.cjs +72 -3
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.d.cts +2 -2
- package/packages/core/dist/readers.d.ts +2 -2
- package/packages/core/dist/readers.mjs +5 -5
- package/packages/core/dist/readers.mjs.map +1 -1
- package/packages/core/dist/{types-CQun9LW6.d.ts → types-B1X87dUW.d.cts} +16 -2
- package/packages/core/dist/{types-C-llnPH0.d.cts → types-BVkjSd0q.d.ts} +16 -2
- package/packages/core/dist/types-CvsKGweN.d.cts +12 -0
- package/packages/core/dist/types-CvsKGweN.d.ts +12 -0
- package/packages/core/dist/writers.cjs +2 -1
- package/packages/core/dist/writers.cjs.map +1 -1
- package/packages/core/dist/writers.d.cts +2 -2
- package/packages/core/dist/writers.d.ts +2 -2
- package/packages/core/dist/writers.mjs +3 -4
- package/packages/core/dist/writers.mjs.map +1 -1
- package/packages/cli/dist/chunk-5VSPJEZ7.mjs.map +0 -1
- package/packages/core/dist/chunk-4JVGFNQH.mjs.map +0 -1
- package/packages/core/dist/chunk-A4V2H3YU.mjs.map +0 -1
- package/packages/core/dist/chunk-EDTQHZPM.mjs +0 -88
- package/packages/core/dist/chunk-EDTQHZPM.mjs.map +0 -1
- package/packages/core/dist/chunk-HY7H3CQM.mjs.map +0 -1
- package/packages/core/dist/chunk-IZBJAZGF.mjs.map +0 -1
- package/packages/core/dist/chunk-NQZPZ2FN.mjs.map +0 -1
- package/packages/core/dist/chunk-XUNJMAPD.mjs.map +0 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { resolveTraceDir, parseDuration, buildSessionIndex, enrichSessionRunRecord, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, extractMetadata, filterTraces, truncateName, getTraceFilePath, buildRunSummary, formatDuration, formatTimestamp, isAgentInspectTrace, filterMetasBySessionScope, aggregateSessionCheckResults, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, persistedInspectEventsToTraceEvents, renderStepLine, renderErrorLine, getIndent, Redactor, validateEvent, isPersistedInspectEvent, nanoid, resolveRedactionProfile, applyProfileMetadataCaps, extractCorrelationMetadata, truncateStringForProfile, parseTraceJsonl } from './chunk-
|
|
2
|
+
import { resolveTraceDir, parseDuration, buildSessionIndex, enrichSessionRunRecord, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, extractMetadata, filterTraces, truncateName, getTraceFilePath, buildRunSummary, formatDuration, formatTimestamp, isAgentInspectTrace, filterMetasBySessionScope, aggregateSessionCheckResults, resolveBundleRunIds, aggregateBundleSafeStatus, bundleFailsOnSafety, buildPlaceholderArtifact, buildBundleMetadata, buildBundleSummaryMarkdown, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, persistedInspectEventsToTraceEvents, renderStepLine, renderErrorLine, getIndent, Redactor, validateEvent, isPersistedInspectEvent, normalizeBundleOutputPath, defaultBundleOutputPath, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, nanoid, resolveRedactionProfile, applyProfileMetadataCaps, extractCorrelationMetadata, extractOutcomesFromPersistedEvents, outcomesMatchingStatus, truncateStringForProfile, parseTraceJsonl } from './chunk-ORHDQUT6.mjs';
|
|
3
3
|
import { realpathSync, constants as constants$1 } from 'fs';
|
|
4
|
-
import
|
|
4
|
+
import path13 from 'path';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
6
6
|
import { Command, Option } from 'commander';
|
|
7
|
-
import { unlink, stat, mkdir, writeFile, appendFile, rm, access,
|
|
7
|
+
import { unlink, stat, mkdir, writeFile, appendFile, readFile, rm, access, open, readdir, constants } from 'fs/promises';
|
|
8
8
|
import process2, { stdin, stdout } from 'process';
|
|
9
9
|
import crypto, { createHash } from 'crypto';
|
|
10
10
|
import { createServer } from 'http';
|
|
11
11
|
import { createRequire } from 'module';
|
|
12
12
|
|
|
13
13
|
// package.json
|
|
14
|
-
var version = "4.
|
|
14
|
+
var version = "4.4.0";
|
|
15
15
|
|
|
16
16
|
// packages/cli/src/trace-dir-scale.ts
|
|
17
17
|
var TRACE_COUNT_WARN = 1e3;
|
|
@@ -353,6 +353,8 @@ function nodeIdForEvent(event) {
|
|
|
353
353
|
case "step_started":
|
|
354
354
|
case "step_completed":
|
|
355
355
|
return event.stepId;
|
|
356
|
+
case "outcome_observed":
|
|
357
|
+
return event.outcomeId;
|
|
356
358
|
default:
|
|
357
359
|
return "unknown";
|
|
358
360
|
}
|
|
@@ -536,6 +538,39 @@ function traceEventToPersistedInspectEvent(event, options) {
|
|
|
536
538
|
error
|
|
537
539
|
};
|
|
538
540
|
}
|
|
541
|
+
case "outcome_observed": {
|
|
542
|
+
const tsObserved = toIsoTimestamp(event.observedAt);
|
|
543
|
+
const attributes = compactAttributes({
|
|
544
|
+
legacyEvent: "outcome_observed",
|
|
545
|
+
outcomeId: event.outcomeId,
|
|
546
|
+
outcomeStatus: event.status,
|
|
547
|
+
expectation: event.expectation,
|
|
548
|
+
method: event.method,
|
|
549
|
+
actual: event.actual,
|
|
550
|
+
evidence: event.evidence,
|
|
551
|
+
observedAt: tsObserved.iso,
|
|
552
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsObserved.invalidTimestamp ? true : void 0
|
|
553
|
+
});
|
|
554
|
+
const out = {
|
|
555
|
+
schemaVersion: "0.2",
|
|
556
|
+
eventId,
|
|
557
|
+
runId: event.runId,
|
|
558
|
+
kind: "OUTCOME",
|
|
559
|
+
name: event.name,
|
|
560
|
+
status: event.status === "failed" ? "error" : "ok",
|
|
561
|
+
timestamp: tsMain.iso,
|
|
562
|
+
confidence: "explicit",
|
|
563
|
+
source,
|
|
564
|
+
attributes
|
|
565
|
+
};
|
|
566
|
+
if (event.parentId !== void 0) {
|
|
567
|
+
out.parentId = event.parentId;
|
|
568
|
+
}
|
|
569
|
+
if (event.actual !== void 0) {
|
|
570
|
+
out.outputSummary = event.actual;
|
|
571
|
+
}
|
|
572
|
+
return out;
|
|
573
|
+
}
|
|
539
574
|
default: {
|
|
540
575
|
const _exhaustive = event;
|
|
541
576
|
throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
|
|
@@ -895,7 +930,7 @@ function findReaderByFormat(format, readers) {
|
|
|
895
930
|
}
|
|
896
931
|
async function jsonlFilesInDirectory(dirPath) {
|
|
897
932
|
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
898
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) =>
|
|
933
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path13.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
899
934
|
}
|
|
900
935
|
async function resolveInput(input3) {
|
|
901
936
|
const cached = resolvedInputCache.get(input3);
|
|
@@ -1265,7 +1300,7 @@ function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
|
|
|
1265
1300
|
function mapOpenInferenceKind(span, attributes, pathPrefix) {
|
|
1266
1301
|
const warnings = [];
|
|
1267
1302
|
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
1268
|
-
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
|
|
1303
|
+
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") {
|
|
1269
1304
|
return { kind: agentInspectKind, warnings };
|
|
1270
1305
|
}
|
|
1271
1306
|
const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
|
|
@@ -1786,7 +1821,7 @@ function mapOtlpStatus(status) {
|
|
|
1786
1821
|
function readOtlpKind(attributes, pathPrefix) {
|
|
1787
1822
|
const warnings = [];
|
|
1788
1823
|
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
1789
|
-
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
|
|
1824
|
+
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") {
|
|
1790
1825
|
return { kind: agentInspectKind, warnings };
|
|
1791
1826
|
}
|
|
1792
1827
|
const operation = attributes["gen_ai.operation.name"];
|
|
@@ -3914,6 +3949,40 @@ function redactTraceEventsForReport(events, options) {
|
|
|
3914
3949
|
)
|
|
3915
3950
|
} : {}
|
|
3916
3951
|
};
|
|
3952
|
+
case "outcome_observed": {
|
|
3953
|
+
const actualAttrs = event.actual !== void 0 ? redactEventAttributes(
|
|
3954
|
+
{ value: event.actual },
|
|
3955
|
+
redactor,
|
|
3956
|
+
maxMetadataValueLength,
|
|
3957
|
+
maxPreviewLength
|
|
3958
|
+
) : void 0;
|
|
3959
|
+
const redactedActual = actualAttrs !== void 0 && "value" in actualAttrs ? actualAttrs.value : void 0;
|
|
3960
|
+
const redactedEvidence = event.evidence !== void 0 ? redactEventAttributes(
|
|
3961
|
+
isRecord6(event.evidence) ? event.evidence : { value: event.evidence },
|
|
3962
|
+
redactor,
|
|
3963
|
+
maxMetadataValueLength,
|
|
3964
|
+
maxPreviewLength
|
|
3965
|
+
) : void 0;
|
|
3966
|
+
return {
|
|
3967
|
+
...event,
|
|
3968
|
+
name: truncateStringForProfile(
|
|
3969
|
+
event.name,
|
|
3970
|
+
"name",
|
|
3971
|
+
maxMetadataValueLength,
|
|
3972
|
+
maxPreviewLength
|
|
3973
|
+
),
|
|
3974
|
+
expectation: truncateStringForProfile(
|
|
3975
|
+
event.expectation,
|
|
3976
|
+
"expectation",
|
|
3977
|
+
maxMetadataValueLength,
|
|
3978
|
+
maxPreviewLength
|
|
3979
|
+
),
|
|
3980
|
+
...redactedActual !== void 0 ? { actual: redactedActual } : {},
|
|
3981
|
+
...redactedEvidence !== void 0 ? { evidence: redactedEvidence } : {}
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
default:
|
|
3985
|
+
return event;
|
|
3917
3986
|
}
|
|
3918
3987
|
});
|
|
3919
3988
|
}
|
|
@@ -4052,7 +4121,8 @@ function zeroKinds() {
|
|
|
4052
4121
|
RESULT: 0,
|
|
4053
4122
|
ERROR: 0,
|
|
4054
4123
|
LOGIC: 0,
|
|
4055
|
-
LOG: 0
|
|
4124
|
+
LOG: 0,
|
|
4125
|
+
OUTCOME: 0
|
|
4056
4126
|
};
|
|
4057
4127
|
}
|
|
4058
4128
|
|
|
@@ -4361,7 +4431,8 @@ function exportOpenInference(tree, options) {
|
|
|
4361
4431
|
];
|
|
4362
4432
|
const traceId = hexFrom(`trace:${tree.runId}`, 16);
|
|
4363
4433
|
const includeAttributes = options?.includeAttributes ?? false;
|
|
4364
|
-
const maxLen = options?.maxAttributeLength;
|
|
4434
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
4435
|
+
const pretty = options?.pretty ?? true;
|
|
4365
4436
|
const spans = [];
|
|
4366
4437
|
for (const n of flattenTree(tree)) {
|
|
4367
4438
|
const ev = n.event;
|
|
@@ -4435,7 +4506,7 @@ function exportOpenInference(tree, options) {
|
|
|
4435
4506
|
};
|
|
4436
4507
|
return {
|
|
4437
4508
|
format: "openinference",
|
|
4438
|
-
content: JSON.stringify(payload, null, 2 ),
|
|
4509
|
+
content: JSON.stringify(payload, null, pretty ? 2 : void 0),
|
|
4439
4510
|
contentType: "application/json",
|
|
4440
4511
|
fileExtension: ".openinference.json",
|
|
4441
4512
|
warnings
|
|
@@ -4469,7 +4540,8 @@ function exportOtlpJson(tree, options) {
|
|
|
4469
4540
|
];
|
|
4470
4541
|
const traceId = hexFrom2(`trace:${tree.runId}`, 16);
|
|
4471
4542
|
const includeAttributes = options?.includeAttributes ?? false;
|
|
4472
|
-
const maxLen = options?.maxAttributeLength;
|
|
4543
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
4544
|
+
const pretty = options?.pretty ?? true;
|
|
4473
4545
|
const flat = flattenTree(tree);
|
|
4474
4546
|
const spans = [];
|
|
4475
4547
|
for (const n of flat) {
|
|
@@ -4565,7 +4637,7 @@ function exportOtlpJson(tree, options) {
|
|
|
4565
4637
|
};
|
|
4566
4638
|
return {
|
|
4567
4639
|
format: "otlp-json",
|
|
4568
|
-
content: JSON.stringify(payload, null, 2 ),
|
|
4640
|
+
content: JSON.stringify(payload, null, pretty ? 2 : void 0),
|
|
4569
4641
|
contentType: "application/json",
|
|
4570
4642
|
fileExtension: ".otlp.json",
|
|
4571
4643
|
warnings
|
|
@@ -4788,9 +4860,9 @@ function mergeExportDefaults(options) {
|
|
|
4788
4860
|
includeMetadata: options.includeMetadata ?? true,
|
|
4789
4861
|
includeAttributes: options.includeAttributes ?? false,
|
|
4790
4862
|
includeErrors: options.includeErrors ?? true,
|
|
4791
|
-
pretty: options.pretty,
|
|
4792
|
-
redacted: options.redacted,
|
|
4793
|
-
maxAttributeLength: options.maxAttributeLength,
|
|
4863
|
+
pretty: options.pretty ?? true,
|
|
4864
|
+
redacted: options.redacted ?? true,
|
|
4865
|
+
maxAttributeLength: options.maxAttributeLength ?? 500,
|
|
4794
4866
|
redactionProfile: options.redactionProfile ?? "local"
|
|
4795
4867
|
};
|
|
4796
4868
|
}
|
|
@@ -4853,9 +4925,34 @@ footer{margin-top:2rem;font-size:0.85rem;color:#555}
|
|
|
4853
4925
|
`.trim();
|
|
4854
4926
|
function buildRunReport(events, options) {
|
|
4855
4927
|
const profile = options.redactionProfile ?? "local";
|
|
4928
|
+
const section = options.section ?? "all";
|
|
4856
4929
|
const safeEvents = redactTraceEventsForReport(events, {
|
|
4857
4930
|
redactionProfile: profile
|
|
4858
4931
|
});
|
|
4932
|
+
const outcomes = summarizeObservedOutcomes(
|
|
4933
|
+
extractOutcomesFromTraceEvents(safeEvents)
|
|
4934
|
+
);
|
|
4935
|
+
const observationsMarkdown = renderObservedOutcomesMarkdown(outcomes);
|
|
4936
|
+
const observationsHtml = renderObservedOutcomesHtml(outcomes);
|
|
4937
|
+
if (section === "observations") {
|
|
4938
|
+
if (options.format === "markdown") {
|
|
4939
|
+
return {
|
|
4940
|
+
format: "markdown",
|
|
4941
|
+
content: `## Observed outcomes
|
|
4942
|
+
|
|
4943
|
+
${observationsMarkdown}
|
|
4944
|
+
`,
|
|
4945
|
+
contentType: "text/markdown",
|
|
4946
|
+
fileExtension: ".md"
|
|
4947
|
+
};
|
|
4948
|
+
}
|
|
4949
|
+
return {
|
|
4950
|
+
format: "html",
|
|
4951
|
+
content: `<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>Observed outcomes</title><style>${REPORT_HTML_CSS}</style></head><body><h1>Observed outcomes</h1>${observationsHtml}</body></html>`,
|
|
4952
|
+
contentType: "text/html",
|
|
4953
|
+
fileExtension: ".html"
|
|
4954
|
+
};
|
|
4955
|
+
}
|
|
4859
4956
|
const whatSummary = buildRunWhatSummary(safeEvents);
|
|
4860
4957
|
const whatText = renderRunWhat(whatSummary, {
|
|
4861
4958
|
correlation: options.correlation !== false
|
|
@@ -4889,6 +4986,10 @@ function buildRunReport(events, options) {
|
|
|
4889
4986
|
"```text",
|
|
4890
4987
|
timelineText,
|
|
4891
4988
|
"```",
|
|
4989
|
+
"",
|
|
4990
|
+
"## Observed outcomes",
|
|
4991
|
+
"",
|
|
4992
|
+
observationsMarkdown,
|
|
4892
4993
|
""
|
|
4893
4994
|
];
|
|
4894
4995
|
if (tail2) {
|
|
@@ -4932,6 +5033,7 @@ function buildRunReport(events, options) {
|
|
|
4932
5033
|
<p class="note">Generated locally by AgentInspect. Review for sensitive data before sharing.</p>
|
|
4933
5034
|
<section class="what"><h2>What happened</h2><pre>${escapeHtml(whatText)}</pre></section>
|
|
4934
5035
|
<section class="timeline"><h2>Timeline</h2><pre>${escapeHtml(timelineText)}</pre></section>
|
|
5036
|
+
<section class="observations"><h2>Observed outcomes</h2>${observationsHtml}</section>
|
|
4935
5037
|
${treeSection || '<section class="tree"><h2>Execution tree</h2><p>No steps recorded.</p></section>'}
|
|
4936
5038
|
${errorsSection}
|
|
4937
5039
|
${attrsSection}
|
|
@@ -5024,9 +5126,9 @@ Trace directory: ${traceDir}`);
|
|
|
5024
5126
|
if (validation !== void 0 && !validation.ok) {
|
|
5025
5127
|
process.exitCode = 1;
|
|
5026
5128
|
}
|
|
5027
|
-
const outPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
5129
|
+
const outPath = options.output !== void 0 && options.output.trim() !== "" ? path13.resolve(options.output.trim()) : void 0;
|
|
5028
5130
|
if (outPath !== void 0) {
|
|
5029
|
-
await mkdir(
|
|
5131
|
+
await mkdir(path13.dirname(outPath), { recursive: true });
|
|
5030
5132
|
await writeFile(outPath, result.content, "utf-8");
|
|
5031
5133
|
const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
|
|
5032
5134
|
console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
|
|
@@ -5203,13 +5305,13 @@ function pairSteps(left, right) {
|
|
|
5203
5305
|
return pairs;
|
|
5204
5306
|
}
|
|
5205
5307
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
5206
|
-
const
|
|
5308
|
+
const path19 = buildPath(segments);
|
|
5207
5309
|
if (L.name !== R.name) {
|
|
5208
5310
|
out.push({
|
|
5209
5311
|
kind: "structure",
|
|
5210
5312
|
severity: "warning",
|
|
5211
5313
|
message: "Step name differs",
|
|
5212
|
-
path:
|
|
5314
|
+
path: path19,
|
|
5213
5315
|
left: L.name,
|
|
5214
5316
|
right: R.name
|
|
5215
5317
|
});
|
|
@@ -5219,7 +5321,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
5219
5321
|
kind: "step-type",
|
|
5220
5322
|
severity: "warning",
|
|
5221
5323
|
message: "Step type differs",
|
|
5222
|
-
path:
|
|
5324
|
+
path: path19,
|
|
5223
5325
|
left: L.type,
|
|
5224
5326
|
right: R.type
|
|
5225
5327
|
});
|
|
@@ -5229,7 +5331,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
5229
5331
|
kind: "step-status",
|
|
5230
5332
|
severity: "warning",
|
|
5231
5333
|
message: "Step status differs",
|
|
5232
|
-
path:
|
|
5334
|
+
path: path19,
|
|
5233
5335
|
left: L.status,
|
|
5234
5336
|
right: R.status
|
|
5235
5337
|
});
|
|
@@ -5241,7 +5343,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
5241
5343
|
kind: "error",
|
|
5242
5344
|
severity: "error",
|
|
5243
5345
|
message: "Step error message differs",
|
|
5244
|
-
path:
|
|
5346
|
+
path: path19,
|
|
5245
5347
|
left: le || void 0,
|
|
5246
5348
|
right: re || void 0
|
|
5247
5349
|
});
|
|
@@ -5259,7 +5361,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
5259
5361
|
kind: "duration",
|
|
5260
5362
|
severity: "info",
|
|
5261
5363
|
message: "Step duration differs",
|
|
5262
|
-
path:
|
|
5364
|
+
path: path19,
|
|
5263
5365
|
left: ld,
|
|
5264
5366
|
right: rd
|
|
5265
5367
|
});
|
|
@@ -5272,7 +5374,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
5272
5374
|
kind: "metadata",
|
|
5273
5375
|
severity: "info",
|
|
5274
5376
|
message: "Step metadata differs",
|
|
5275
|
-
path:
|
|
5377
|
+
path: path19,
|
|
5276
5378
|
left: L.metadata,
|
|
5277
5379
|
right: R.metadata
|
|
5278
5380
|
});
|
|
@@ -5284,7 +5386,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
5284
5386
|
kind: "output",
|
|
5285
5387
|
severity: "info",
|
|
5286
5388
|
message: "Output preview differs",
|
|
5287
|
-
path:
|
|
5389
|
+
path: path19,
|
|
5288
5390
|
left: L.outputPreview,
|
|
5289
5391
|
right: R.outputPreview
|
|
5290
5392
|
});
|
|
@@ -5444,11 +5546,11 @@ function diffRuns(left, right, options) {
|
|
|
5444
5546
|
}
|
|
5445
5547
|
|
|
5446
5548
|
// packages/core/src/diff/renderer.ts
|
|
5447
|
-
function formatPath(
|
|
5448
|
-
if (
|
|
5549
|
+
function formatPath(path19) {
|
|
5550
|
+
if (path19 === void 0 || path19.path.length === 0) {
|
|
5449
5551
|
return "(run)";
|
|
5450
5552
|
}
|
|
5451
|
-
return
|
|
5553
|
+
return path19.path.map((s) => s.name).join(" > ");
|
|
5452
5554
|
}
|
|
5453
5555
|
function formatValue(v, verbose) {
|
|
5454
5556
|
if (v === void 0) return "(undefined)";
|
|
@@ -5783,7 +5885,8 @@ async function searchCommand(options = {}) {
|
|
|
5783
5885
|
duration: options.duration,
|
|
5784
5886
|
limit: parseLimit2(options.limit),
|
|
5785
5887
|
...sessionId ? { session: sessionId } : {},
|
|
5786
|
-
...options.correlateGroup ? { correlateGroup: true } : {}
|
|
5888
|
+
...options.correlateGroup ? { correlateGroup: true } : {},
|
|
5889
|
+
...options.observation ? { observation: options.observation } : {}
|
|
5787
5890
|
});
|
|
5788
5891
|
if (options.json) {
|
|
5789
5892
|
console.log(JSON.stringify(results, null, 2));
|
|
@@ -5826,7 +5929,7 @@ function indexedToMetadata(row, traceDir) {
|
|
|
5826
5929
|
endedAt: row.endedAt ?? void 0,
|
|
5827
5930
|
durationMs: row.durationMs ?? void 0,
|
|
5828
5931
|
eventCount: 0,
|
|
5829
|
-
filePath:
|
|
5932
|
+
filePath: path13.join(traceDir, row.file),
|
|
5830
5933
|
fileSize: 0,
|
|
5831
5934
|
createdAt: new Date(row.mtimeMs)
|
|
5832
5935
|
};
|
|
@@ -5855,7 +5958,7 @@ async function newestTraceMtimeMs(traceDir) {
|
|
|
5855
5958
|
}
|
|
5856
5959
|
async function loadSessionRuns(traceDir) {
|
|
5857
5960
|
try {
|
|
5858
|
-
const mod = await import('./src-
|
|
5961
|
+
const mod = await import('./src-FJOHPC4X.mjs');
|
|
5859
5962
|
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
5860
5963
|
const status = mod.indexStatus(dbPath);
|
|
5861
5964
|
if (!status.healthy) {
|
|
@@ -6300,11 +6403,12 @@ async function reportCommand(runId, options = {}) {
|
|
|
6300
6403
|
includeAttributes: options.includeAttributes === true,
|
|
6301
6404
|
includeErrors: options.noErrors !== true,
|
|
6302
6405
|
redactionProfile,
|
|
6303
|
-
correlation: !options.noCorrelation
|
|
6406
|
+
correlation: !options.noCorrelation,
|
|
6407
|
+
...options.section ? { section: options.section } : {}
|
|
6304
6408
|
});
|
|
6305
|
-
const outPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
6409
|
+
const outPath = options.output !== void 0 && options.output.trim() !== "" ? path13.resolve(options.output.trim()) : void 0;
|
|
6306
6410
|
if (outPath !== void 0) {
|
|
6307
|
-
await mkdir(
|
|
6411
|
+
await mkdir(path13.dirname(outPath), { recursive: true });
|
|
6308
6412
|
await writeFile(outPath, result.content, "utf-8");
|
|
6309
6413
|
console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
|
|
6310
6414
|
}
|
|
@@ -6551,17 +6655,17 @@ function applyRule(rule, value, replacement) {
|
|
|
6551
6655
|
}
|
|
6552
6656
|
return value;
|
|
6553
6657
|
}
|
|
6554
|
-
function childPath(
|
|
6658
|
+
function childPath(path19, key) {
|
|
6555
6659
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
6556
|
-
return
|
|
6660
|
+
return path19 ? `${path19}.${key}` : key;
|
|
6557
6661
|
}
|
|
6558
|
-
return `${
|
|
6662
|
+
return `${path19 || "$"}[${JSON.stringify(key)}]`;
|
|
6559
6663
|
}
|
|
6560
|
-
function indexPath(
|
|
6561
|
-
return `${
|
|
6664
|
+
function indexPath(path19, index) {
|
|
6665
|
+
return `${path19 || "$"}[${index}]`;
|
|
6562
6666
|
}
|
|
6563
|
-
function makeFinding(
|
|
6564
|
-
return preview === void 0 ? { path:
|
|
6667
|
+
function makeFinding(path19, detector, action, matchKind, severity = "warning", preview) {
|
|
6668
|
+
return preview === void 0 ? { path: path19, detector, action, severity, matchKind } : { path: path19, detector, action, severity, matchKind, preview };
|
|
6565
6669
|
}
|
|
6566
6670
|
function createRedactionProfile(profile = "local") {
|
|
6567
6671
|
switch (profile) {
|
|
@@ -6630,11 +6734,11 @@ var Redactor2 = class {
|
|
|
6630
6734
|
#recordFinding(state, finding) {
|
|
6631
6735
|
if (this.#collectFindings) state.findings.push(finding);
|
|
6632
6736
|
}
|
|
6633
|
-
#redactValue(value, key,
|
|
6737
|
+
#redactValue(value, key, path19, depth, state) {
|
|
6634
6738
|
if (depth > this.#maxDepth) {
|
|
6635
6739
|
this.#recordFinding(
|
|
6636
6740
|
state,
|
|
6637
|
-
makeFinding(
|
|
6741
|
+
makeFinding(path19, "structure.maxDepth", "truncate", "value", "warning")
|
|
6638
6742
|
);
|
|
6639
6743
|
return "[Truncated]";
|
|
6640
6744
|
}
|
|
@@ -6643,19 +6747,19 @@ var Redactor2 = class {
|
|
|
6643
6747
|
if (rule) {
|
|
6644
6748
|
this.#recordFinding(
|
|
6645
6749
|
state,
|
|
6646
|
-
makeFinding(
|
|
6750
|
+
makeFinding(path19, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
6647
6751
|
);
|
|
6648
6752
|
return applyRule(rule, value, this.#replacement);
|
|
6649
6753
|
}
|
|
6650
6754
|
}
|
|
6651
6755
|
for (const detector of this.#detectors) {
|
|
6652
|
-
const detections = detector.detect({ path:
|
|
6756
|
+
const detections = detector.detect({ path: path19, key, value });
|
|
6653
6757
|
for (const detection of detections) {
|
|
6654
6758
|
const action = detection.action ?? "replace";
|
|
6655
6759
|
this.#recordFinding(
|
|
6656
6760
|
state,
|
|
6657
6761
|
makeFinding(
|
|
6658
|
-
|
|
6762
|
+
path19,
|
|
6659
6763
|
detector.id,
|
|
6660
6764
|
action,
|
|
6661
6765
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -6673,7 +6777,7 @@ var Redactor2 = class {
|
|
|
6673
6777
|
const out = [];
|
|
6674
6778
|
state.seen.set(value, out);
|
|
6675
6779
|
value.forEach((item, index) => {
|
|
6676
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
6780
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path19, index), depth + 1, state);
|
|
6677
6781
|
});
|
|
6678
6782
|
return out;
|
|
6679
6783
|
}
|
|
@@ -6685,7 +6789,7 @@ var Redactor2 = class {
|
|
|
6685
6789
|
out[entryKey] = this.#redactValue(
|
|
6686
6790
|
entryValue,
|
|
6687
6791
|
entryKey,
|
|
6688
|
-
childPath(
|
|
6792
|
+
childPath(path19 === "$" ? "" : path19, entryKey),
|
|
6689
6793
|
depth + 1,
|
|
6690
6794
|
state
|
|
6691
6795
|
);
|
|
@@ -6811,6 +6915,9 @@ function redactDocument(content, profile) {
|
|
|
6811
6915
|
return redactJsonlText(content, profile);
|
|
6812
6916
|
}
|
|
6813
6917
|
}
|
|
6918
|
+
function redactTraceContent(content, profile) {
|
|
6919
|
+
return redactDocument(content, profile);
|
|
6920
|
+
}
|
|
6814
6921
|
async function redactCommand(target, options = {}, stdin = process.stdin) {
|
|
6815
6922
|
const profile = parseRedactionProfile3(options.profile);
|
|
6816
6923
|
const source = await contentFromTarget(target, options, stdin);
|
|
@@ -7132,14 +7239,14 @@ function uniqueSorted(values) {
|
|
|
7132
7239
|
return [...new Set(values)].sort();
|
|
7133
7240
|
}
|
|
7134
7241
|
function isWithinDirectory(child, parent) {
|
|
7135
|
-
const relative =
|
|
7136
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
7242
|
+
const relative = path13.relative(parent, child);
|
|
7243
|
+
return relative === "" || !relative.startsWith("..") && !path13.isAbsolute(relative);
|
|
7137
7244
|
}
|
|
7138
7245
|
async function resolveOutputPath(inputPath, output2, force) {
|
|
7139
7246
|
if (output2 === void 0 || output2.trim() === "") return void 0;
|
|
7140
|
-
const inputAbs =
|
|
7141
|
-
const outputAbs =
|
|
7142
|
-
const inputDir =
|
|
7247
|
+
const inputAbs = path13.resolve(inputPath);
|
|
7248
|
+
const outputAbs = path13.resolve(output2.trim());
|
|
7249
|
+
const inputDir = path13.dirname(inputAbs);
|
|
7143
7250
|
if (!isWithinDirectory(outputAbs, inputDir)) {
|
|
7144
7251
|
throw new Error("Refusing to write migrated output outside the input directory.");
|
|
7145
7252
|
}
|
|
@@ -7260,7 +7367,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
7260
7367
|
process.exitCode = 1;
|
|
7261
7368
|
return;
|
|
7262
7369
|
}
|
|
7263
|
-
const inputPath =
|
|
7370
|
+
const inputPath = path13.resolve(input3.trim());
|
|
7264
7371
|
const dryRun = options.dryRun === true;
|
|
7265
7372
|
if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
|
|
7266
7373
|
console.error("migrate requires --dry-run or --output <path>.");
|
|
@@ -7279,7 +7386,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
7279
7386
|
);
|
|
7280
7387
|
const result = await buildMigration(inputPath, outputPath);
|
|
7281
7388
|
if (!dryRun && outputPath !== void 0) {
|
|
7282
|
-
await mkdir(
|
|
7389
|
+
await mkdir(path13.dirname(outputPath), { recursive: true });
|
|
7283
7390
|
await writeFile(outputPath, result.content, "utf-8");
|
|
7284
7391
|
}
|
|
7285
7392
|
printSummary2(result, dryRun);
|
|
@@ -7557,7 +7664,7 @@ function stripPrefix(name, prefixes) {
|
|
|
7557
7664
|
}
|
|
7558
7665
|
return name;
|
|
7559
7666
|
}
|
|
7560
|
-
function eventEvidence(event,
|
|
7667
|
+
function eventEvidence(event, path19) {
|
|
7561
7668
|
return {
|
|
7562
7669
|
runId: event.runId,
|
|
7563
7670
|
eventId: event.eventId,
|
|
@@ -7567,7 +7674,7 @@ function eventEvidence(event, path18) {
|
|
|
7567
7674
|
kind: event.kind,
|
|
7568
7675
|
name: event.name,
|
|
7569
7676
|
status: event.status,
|
|
7570
|
-
...
|
|
7677
|
+
...path19 ? { path: path19 } : {}
|
|
7571
7678
|
};
|
|
7572
7679
|
}
|
|
7573
7680
|
function runEvidence(run) {
|
|
@@ -7630,9 +7737,9 @@ function eventEndMs(event) {
|
|
|
7630
7737
|
function normalizedKey(value) {
|
|
7631
7738
|
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
7632
7739
|
}
|
|
7633
|
-
function lastPathSegment(
|
|
7634
|
-
const parts =
|
|
7635
|
-
return parts[parts.length - 1] ??
|
|
7740
|
+
function lastPathSegment(path19) {
|
|
7741
|
+
const parts = path19.split(".");
|
|
7742
|
+
return parts[parts.length - 1] ?? path19;
|
|
7636
7743
|
}
|
|
7637
7744
|
function valueType(value) {
|
|
7638
7745
|
if (Array.isArray(value)) return "array";
|
|
@@ -7646,12 +7753,12 @@ function serializedByteLength(value) {
|
|
|
7646
7753
|
return void 0;
|
|
7647
7754
|
}
|
|
7648
7755
|
}
|
|
7649
|
-
function pushValueEntries(entries, event, value,
|
|
7650
|
-
entries.push({ event, path:
|
|
7756
|
+
function pushValueEntries(entries, event, value, path19, key, depth = 0) {
|
|
7757
|
+
entries.push({ event, path: path19, key, value });
|
|
7651
7758
|
if (depth >= 8) return;
|
|
7652
7759
|
if (Array.isArray(value)) {
|
|
7653
7760
|
for (const [index, item] of value.entries()) {
|
|
7654
|
-
pushValueEntries(entries, event, item, `${
|
|
7761
|
+
pushValueEntries(entries, event, item, `${path19}.${index}`, String(index), depth + 1);
|
|
7655
7762
|
}
|
|
7656
7763
|
return;
|
|
7657
7764
|
}
|
|
@@ -7661,7 +7768,7 @@ function pushValueEntries(entries, event, value, path18, key, depth = 0) {
|
|
|
7661
7768
|
entries,
|
|
7662
7769
|
event,
|
|
7663
7770
|
value[nestedKey],
|
|
7664
|
-
`${
|
|
7771
|
+
`${path19}.${nestedKey}`,
|
|
7665
7772
|
nestedKey,
|
|
7666
7773
|
depth + 1
|
|
7667
7774
|
);
|
|
@@ -7742,9 +7849,9 @@ function eventDurationMs(event) {
|
|
|
7742
7849
|
}
|
|
7743
7850
|
function treeShape(nodes) {
|
|
7744
7851
|
const lines = [];
|
|
7745
|
-
const visit = (node,
|
|
7746
|
-
lines.push(`${
|
|
7747
|
-
node.children.forEach((child, index) => visit(child, `${
|
|
7852
|
+
const visit = (node, path19) => {
|
|
7853
|
+
lines.push(`${path19}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
|
|
7854
|
+
node.children.forEach((child, index) => visit(child, `${path19}.${index}`));
|
|
7748
7855
|
};
|
|
7749
7856
|
nodes.forEach((node, index) => visit(node, String(index)));
|
|
7750
7857
|
return lines;
|
|
@@ -7793,9 +7900,9 @@ function retrievalShape(context) {
|
|
|
7793
7900
|
function guardrailShape(context) {
|
|
7794
7901
|
return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
|
|
7795
7902
|
}
|
|
7796
|
-
function firstEvidenceForKind(context, kind,
|
|
7903
|
+
function firstEvidenceForKind(context, kind, path19) {
|
|
7797
7904
|
const event = context.events.find((candidate) => candidate.kind === kind);
|
|
7798
|
-
return event ? [eventEvidence(event,
|
|
7905
|
+
return event ? [eventEvidence(event, path19)] : runEvidence(context.selectedRun);
|
|
7799
7906
|
}
|
|
7800
7907
|
function baselineDiffFinding(message, evidence, expected, actual) {
|
|
7801
7908
|
return failFinding("baseline.regression", message, evidence, expected, actual);
|
|
@@ -8145,13 +8252,13 @@ function createStructureCycleRule() {
|
|
|
8145
8252
|
const seenCycles = /* @__PURE__ */ new Set();
|
|
8146
8253
|
const findings = [];
|
|
8147
8254
|
for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
|
|
8148
|
-
const
|
|
8255
|
+
const path19 = [];
|
|
8149
8256
|
const seenAt = /* @__PURE__ */ new Map();
|
|
8150
8257
|
let current = event;
|
|
8151
8258
|
while (current) {
|
|
8152
8259
|
const existing = seenAt.get(current.eventId);
|
|
8153
8260
|
if (existing !== void 0) {
|
|
8154
|
-
const cycle =
|
|
8261
|
+
const cycle = path19.slice(existing);
|
|
8155
8262
|
const key = cycle.map((item) => item.eventId).sort().join("\0");
|
|
8156
8263
|
if (!seenCycles.has(key)) {
|
|
8157
8264
|
seenCycles.add(key);
|
|
@@ -8167,8 +8274,8 @@ function createStructureCycleRule() {
|
|
|
8167
8274
|
}
|
|
8168
8275
|
break;
|
|
8169
8276
|
}
|
|
8170
|
-
seenAt.set(current.eventId,
|
|
8171
|
-
|
|
8277
|
+
seenAt.set(current.eventId, path19.length);
|
|
8278
|
+
path19.push(current);
|
|
8172
8279
|
current = current.parentId ? byId.get(current.parentId) : void 0;
|
|
8173
8280
|
}
|
|
8174
8281
|
}
|
|
@@ -8601,6 +8708,40 @@ function createBaselineRegressionRule(options) {
|
|
|
8601
8708
|
}
|
|
8602
8709
|
};
|
|
8603
8710
|
}
|
|
8711
|
+
function createObservedOutcomeRule(options = {}) {
|
|
8712
|
+
const failOn = options.failOn ?? ["failed"];
|
|
8713
|
+
return {
|
|
8714
|
+
id: "outcome.status",
|
|
8715
|
+
category: "run",
|
|
8716
|
+
defaultSeverity: "error",
|
|
8717
|
+
evaluate(context) {
|
|
8718
|
+
const outcomes = extractOutcomesFromPersistedEvents(context.events);
|
|
8719
|
+
const matching = outcomesMatchingStatus(outcomes, failOn);
|
|
8720
|
+
if (matching.length === 0) return [];
|
|
8721
|
+
return [
|
|
8722
|
+
failFinding(
|
|
8723
|
+
"outcome.status",
|
|
8724
|
+
`Observed outcome count ${matching.length} matched [${failOn.join(", ")}].`,
|
|
8725
|
+
matching.map((outcome) => ({
|
|
8726
|
+
runId: outcome.runId,
|
|
8727
|
+
eventId: outcome.outcomeId,
|
|
8728
|
+
...outcome.parentId !== void 0 ? { parentId: outcome.parentId } : {},
|
|
8729
|
+
kind: "OUTCOME",
|
|
8730
|
+
name: outcome.name,
|
|
8731
|
+
status: outcome.status,
|
|
8732
|
+
path: `outcome.${outcome.name}`
|
|
8733
|
+
})),
|
|
8734
|
+
{ failOn },
|
|
8735
|
+
matching.map((outcome) => ({
|
|
8736
|
+
name: outcome.name,
|
|
8737
|
+
status: outcome.status,
|
|
8738
|
+
expectation: outcome.expectation
|
|
8739
|
+
}))
|
|
8740
|
+
)
|
|
8741
|
+
];
|
|
8742
|
+
}
|
|
8743
|
+
};
|
|
8744
|
+
}
|
|
8604
8745
|
function runTraceChecks(input3, options = {}) {
|
|
8605
8746
|
const selected = resolveSelectedRun(input3, options.runId);
|
|
8606
8747
|
if (selected.diagnostics.length > 0) {
|
|
@@ -8965,23 +9106,23 @@ function evaluatePromptInjection(text, options = {}) {
|
|
|
8965
9106
|
}
|
|
8966
9107
|
return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
|
|
8967
9108
|
}
|
|
8968
|
-
function validateSchemaField(value, field,
|
|
9109
|
+
function validateSchemaField(value, field, path19, evidence) {
|
|
8969
9110
|
const ruleId = "guardrail.structured-output";
|
|
8970
9111
|
if (field.type) {
|
|
8971
9112
|
const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
8972
9113
|
if (actual !== field.type) {
|
|
8973
|
-
evidence.push({ ruleId, path:
|
|
9114
|
+
evidence.push({ ruleId, path: path19, preview: `expected ${field.type}, got ${actual}` });
|
|
8974
9115
|
return;
|
|
8975
9116
|
}
|
|
8976
9117
|
}
|
|
8977
9118
|
if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
|
|
8978
|
-
evidence.push({ ruleId, path:
|
|
9119
|
+
evidence.push({ ruleId, path: path19, preview: "value not in enum" });
|
|
8979
9120
|
}
|
|
8980
9121
|
if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
|
|
8981
9122
|
const record = value;
|
|
8982
9123
|
for (const key of field.required) {
|
|
8983
9124
|
if (!(key in record)) {
|
|
8984
|
-
evidence.push({ ruleId, path: `${
|
|
9125
|
+
evidence.push({ ruleId, path: `${path19}.${key}`, preview: "missing required key" });
|
|
8985
9126
|
}
|
|
8986
9127
|
}
|
|
8987
9128
|
}
|
|
@@ -9308,7 +9449,7 @@ function asConfig(value) {
|
|
|
9308
9449
|
}
|
|
9309
9450
|
async function loadConfig(configPath) {
|
|
9310
9451
|
if (configPath === void 0) return {};
|
|
9311
|
-
const extension =
|
|
9452
|
+
const extension = path13.extname(configPath);
|
|
9312
9453
|
if (TS_CONFIG_EXTENSIONS.has(extension)) {
|
|
9313
9454
|
throw new Error(
|
|
9314
9455
|
"TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -9317,7 +9458,7 @@ async function loadConfig(configPath) {
|
|
|
9317
9458
|
if (!CONFIG_EXTENSIONS.has(extension)) {
|
|
9318
9459
|
throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
|
|
9319
9460
|
}
|
|
9320
|
-
const absolute =
|
|
9461
|
+
const absolute = path13.resolve(configPath);
|
|
9321
9462
|
if (extension === ".json") {
|
|
9322
9463
|
const raw = await readFile(absolute, "utf-8");
|
|
9323
9464
|
return asConfig(JSON.parse(raw));
|
|
@@ -9372,6 +9513,22 @@ function buildRules(config, options) {
|
|
|
9372
9513
|
if (options.detectStalls) {
|
|
9373
9514
|
rules.push(createStallDetectionRule({ requireEndedAt: true }));
|
|
9374
9515
|
}
|
|
9516
|
+
if (options.failOnObservation !== void 0 && options.failOnObservation.trim() !== "") {
|
|
9517
|
+
try {
|
|
9518
|
+
const statuses = options.failOnObservation.split(",").map((value) => value.trim()).filter((value) => value !== "").map((value) => {
|
|
9519
|
+
if (value === "passed" || value === "failed" || value === "unknown" || value === "skipped") {
|
|
9520
|
+
return value;
|
|
9521
|
+
}
|
|
9522
|
+
throw new Error(`unsupported status "${value}"`);
|
|
9523
|
+
});
|
|
9524
|
+
rules.push(createObservedOutcomeRule({ failOn: statuses }));
|
|
9525
|
+
} catch (error) {
|
|
9526
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9527
|
+
diagnostics.push(
|
|
9528
|
+
diagnostic2("AI_CHECK_INVALID_ARGUMENTS", `--fail-on-observation: ${message}`)
|
|
9529
|
+
);
|
|
9530
|
+
}
|
|
9531
|
+
}
|
|
9375
9532
|
if (run.maxDepth !== void 0) {
|
|
9376
9533
|
rules.push(createRunDepthRule({ maxDepth: run.maxDepth }));
|
|
9377
9534
|
}
|
|
@@ -9466,10 +9623,10 @@ function printHuman(result) {
|
|
|
9466
9623
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
9467
9624
|
}
|
|
9468
9625
|
for (const finding of result.findings) {
|
|
9469
|
-
const
|
|
9626
|
+
const path19 = finding.evidence[0]?.path;
|
|
9470
9627
|
const run = finding.evidence[0]?.runId;
|
|
9471
9628
|
const runPrefix = run ? `[${run}] ` : "";
|
|
9472
|
-
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${
|
|
9629
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path19 ? ` (${path19})` : ""}`);
|
|
9473
9630
|
}
|
|
9474
9631
|
}
|
|
9475
9632
|
function readErrorResult(error) {
|
|
@@ -9669,7 +9826,7 @@ function createViewerServer(options = {}) {
|
|
|
9669
9826
|
return sendJson(res, 200, {
|
|
9670
9827
|
ok: true,
|
|
9671
9828
|
readOnly: true,
|
|
9672
|
-
traceDir:
|
|
9829
|
+
traceDir: path13.resolve(traceDir)
|
|
9673
9830
|
});
|
|
9674
9831
|
}
|
|
9675
9832
|
const td = new TraceDirectory({ dir: traceDir });
|
|
@@ -9687,7 +9844,7 @@ function createViewerServer(options = {}) {
|
|
|
9687
9844
|
runId: meta.runId,
|
|
9688
9845
|
name: meta.name,
|
|
9689
9846
|
status: meta.status,
|
|
9690
|
-
file:
|
|
9847
|
+
file: path13.basename(meta.filePath),
|
|
9691
9848
|
startedAt: meta.startedAt,
|
|
9692
9849
|
durationMs: meta.durationMs
|
|
9693
9850
|
}))
|
|
@@ -9802,7 +9959,7 @@ function startViewerServer(options = {}) {
|
|
|
9802
9959
|
resolve({
|
|
9803
9960
|
host,
|
|
9804
9961
|
port: resolvedPort,
|
|
9805
|
-
traceDir:
|
|
9962
|
+
traceDir: path13.resolve(traceDir),
|
|
9806
9963
|
url: `http://${host}:${resolvedPort}`
|
|
9807
9964
|
});
|
|
9808
9965
|
});
|
|
@@ -10007,10 +10164,10 @@ async function evalRun(input3, options = {}) {
|
|
|
10007
10164
|
diagnostics: []
|
|
10008
10165
|
};
|
|
10009
10166
|
}
|
|
10010
|
-
function evidenceForRun(run,
|
|
10011
|
-
return [{ runId: run.runId, ...
|
|
10167
|
+
function evidenceForRun(run, path19) {
|
|
10168
|
+
return [{ runId: run.runId, ...path19 !== void 0 ? { path: path19 } : {} }];
|
|
10012
10169
|
}
|
|
10013
|
-
function evidenceForEvent(event,
|
|
10170
|
+
function evidenceForEvent(event, path19) {
|
|
10014
10171
|
return [
|
|
10015
10172
|
{
|
|
10016
10173
|
runId: event.runId,
|
|
@@ -10018,7 +10175,7 @@ function evidenceForEvent(event, path18) {
|
|
|
10018
10175
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
10019
10176
|
kind: event.kind,
|
|
10020
10177
|
name: event.name,
|
|
10021
|
-
...
|
|
10178
|
+
...path19 !== void 0 ? { path: path19 } : {}
|
|
10022
10179
|
}
|
|
10023
10180
|
];
|
|
10024
10181
|
}
|
|
@@ -10176,9 +10333,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
10176
10333
|
function tokenize(text) {
|
|
10177
10334
|
return [...text.toLowerCase().matchAll(/[a-z0-9][a-z0-9'-]{2,}/g)].map((match) => match[0].replace(/^['-]+|['-]+$/g, "")).filter((token) => token.length > 2 && !STOP_WORDS.has(token));
|
|
10178
10335
|
}
|
|
10179
|
-
function firstEvidence(fields, run,
|
|
10336
|
+
function firstEvidence(fields, run, path19) {
|
|
10180
10337
|
const first = fields[0];
|
|
10181
|
-
return first === void 0 ? evidenceForRun(run,
|
|
10338
|
+
return first === void 0 ? evidenceForRun(run, path19) : evidenceForEvent(first.node.event, first.path);
|
|
10182
10339
|
}
|
|
10183
10340
|
function collectSourceIds(nodes, keys) {
|
|
10184
10341
|
const wanted = keySet(keys);
|
|
@@ -10555,8 +10712,8 @@ function renderEvalMarkdown(result) {
|
|
|
10555
10712
|
if (result.findings.length > 0) {
|
|
10556
10713
|
lines.push("", "## Findings");
|
|
10557
10714
|
for (const finding of result.findings) {
|
|
10558
|
-
const
|
|
10559
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
10715
|
+
const path19 = finding.evidence[0]?.path;
|
|
10716
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path19 ? ` (${path19})` : ""}`);
|
|
10560
10717
|
}
|
|
10561
10718
|
}
|
|
10562
10719
|
return `${lines.join("\n")}
|
|
@@ -10603,7 +10760,7 @@ function asConfig2(value) {
|
|
|
10603
10760
|
}
|
|
10604
10761
|
async function loadConfig2(configPath) {
|
|
10605
10762
|
if (configPath === void 0) return {};
|
|
10606
|
-
const extension =
|
|
10763
|
+
const extension = path13.extname(configPath);
|
|
10607
10764
|
if (TS_CONFIG_EXTENSIONS2.has(extension)) {
|
|
10608
10765
|
throw new Error(
|
|
10609
10766
|
"TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -10612,7 +10769,7 @@ async function loadConfig2(configPath) {
|
|
|
10612
10769
|
if (!CONFIG_EXTENSIONS2.has(extension)) {
|
|
10613
10770
|
throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
|
|
10614
10771
|
}
|
|
10615
|
-
const absolute =
|
|
10772
|
+
const absolute = path13.resolve(configPath);
|
|
10616
10773
|
if (extension === ".json") {
|
|
10617
10774
|
const raw = await readFile(absolute, "utf-8");
|
|
10618
10775
|
return asConfig2(JSON.parse(raw));
|
|
@@ -10749,8 +10906,8 @@ function printHuman2(result) {
|
|
|
10749
10906
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
10750
10907
|
}
|
|
10751
10908
|
for (const finding of result.findings) {
|
|
10752
|
-
const
|
|
10753
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
10909
|
+
const path19 = finding.evidence[0]?.path;
|
|
10910
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path19 ? ` (${path19})` : ""}`);
|
|
10754
10911
|
}
|
|
10755
10912
|
}
|
|
10756
10913
|
function readErrorResult2(error) {
|
|
@@ -10988,8 +11145,8 @@ function printHuman3(result) {
|
|
|
10988
11145
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
10989
11146
|
}
|
|
10990
11147
|
for (const finding of result.findings) {
|
|
10991
|
-
const
|
|
10992
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
11148
|
+
const path19 = finding.evidence[0]?.path;
|
|
11149
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path19 ? ` (${path19})` : ""}`);
|
|
10993
11150
|
}
|
|
10994
11151
|
console.log(`Note: ${result.note}`);
|
|
10995
11152
|
}
|
|
@@ -11035,6 +11192,38 @@ function scanCommand(target, options = {}, stdin = process.stdin) {
|
|
|
11035
11192
|
function verifySafeCommand(target, options = {}, stdin = process.stdin) {
|
|
11036
11193
|
return safetyCommand("verify-safe", target, options, stdin);
|
|
11037
11194
|
}
|
|
11195
|
+
function assessOpenedTrace(read, options = {}) {
|
|
11196
|
+
try {
|
|
11197
|
+
const rules = buildSafetyRules(options);
|
|
11198
|
+
const checkResult = runTraceChecks(
|
|
11199
|
+
{ read },
|
|
11200
|
+
{
|
|
11201
|
+
rules,
|
|
11202
|
+
...options.runId !== void 0 ? { runId: options.runId } : {},
|
|
11203
|
+
...options.run !== void 0 ? { runId: options.run } : {}
|
|
11204
|
+
}
|
|
11205
|
+
);
|
|
11206
|
+
const detectorFindings = checkResult.diagnostics.length === 0 ? redactionDetectorFindings(read, checkResult.runId) : [];
|
|
11207
|
+
return resultFromParts({
|
|
11208
|
+
command: "verify-safe",
|
|
11209
|
+
format: checkResult.format,
|
|
11210
|
+
runId: checkResult.runId,
|
|
11211
|
+
findings: [...checkResult.findings, ...detectorFindings],
|
|
11212
|
+
diagnostics: [
|
|
11213
|
+
...checkResult.diagnostics.map(diagnosticFromCheck),
|
|
11214
|
+
...warningDiagnostics(read.warnings, read.unsupportedFields)
|
|
11215
|
+
],
|
|
11216
|
+
warnings: read.warnings,
|
|
11217
|
+
unsupportedFields: read.unsupportedFields
|
|
11218
|
+
});
|
|
11219
|
+
} catch (error) {
|
|
11220
|
+
return messageStartsWithDash(error) ? invalidArgumentResult("verify-safe", error) : readErrorResult3("verify-safe", error);
|
|
11221
|
+
}
|
|
11222
|
+
}
|
|
11223
|
+
function messageStartsWithDash(error) {
|
|
11224
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11225
|
+
return message.startsWith("--");
|
|
11226
|
+
}
|
|
11038
11227
|
var NOTE = "Generated locally by AgentInspect. Artifacts are best-effort summaries, not compliance or security certification.";
|
|
11039
11228
|
var SAFETY_RULES = [
|
|
11040
11229
|
createSafetyRawContentRule(),
|
|
@@ -11108,8 +11297,8 @@ function renderCheckSection(result) {
|
|
|
11108
11297
|
`Diagnostics: ${result.diagnostics.length}`
|
|
11109
11298
|
];
|
|
11110
11299
|
for (const finding of result.findings.slice(0, 10)) {
|
|
11111
|
-
const
|
|
11112
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
11300
|
+
const path19 = finding.evidence[0]?.path ?? "(run)";
|
|
11301
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path19})`);
|
|
11113
11302
|
}
|
|
11114
11303
|
for (const diagnostic4 of result.diagnostics.slice(0, 10)) {
|
|
11115
11304
|
lines.push(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
@@ -11187,8 +11376,8 @@ function renderHtml(trace, check, diff) {
|
|
|
11187
11376
|
`;
|
|
11188
11377
|
}
|
|
11189
11378
|
async function writeArtifact(outputDir, relativePath, content, files) {
|
|
11190
|
-
const outPath =
|
|
11191
|
-
await mkdir(
|
|
11379
|
+
const outPath = path13.join(outputDir, relativePath);
|
|
11380
|
+
await mkdir(path13.dirname(outPath), { recursive: true });
|
|
11192
11381
|
await writeFile(outPath, content, "utf-8");
|
|
11193
11382
|
files.push(relativePath);
|
|
11194
11383
|
}
|
|
@@ -11204,7 +11393,7 @@ function manifestStatus(check, diff) {
|
|
|
11204
11393
|
return "ok";
|
|
11205
11394
|
}
|
|
11206
11395
|
async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
11207
|
-
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ?
|
|
11396
|
+
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path13.resolve(options.outputDir.trim()) : "";
|
|
11208
11397
|
if (outputDir === "") {
|
|
11209
11398
|
console.error("--output-dir is required.");
|
|
11210
11399
|
process.exitCode = 1;
|
|
@@ -11270,8 +11459,8 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
|
11270
11459
|
await writeArtifact(outputDir, "report.html", renderHtml(trace, check, diff), files);
|
|
11271
11460
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
11272
11461
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
11273
|
-
await mkdir(
|
|
11274
|
-
await appendFile(
|
|
11462
|
+
await mkdir(path13.dirname(path13.resolve(summaryTarget)), { recursive: true });
|
|
11463
|
+
await appendFile(path13.resolve(summaryTarget), `
|
|
11275
11464
|
${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
11276
11465
|
}
|
|
11277
11466
|
const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
|
|
@@ -11290,10 +11479,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
11290
11479
|
findings: diff?.findings.length ?? 0,
|
|
11291
11480
|
diagnostics: diff?.diagnostics.length ?? 0
|
|
11292
11481
|
},
|
|
11293
|
-
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary:
|
|
11482
|
+
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path13.resolve(summaryTarget) } : {},
|
|
11294
11483
|
note: NOTE
|
|
11295
11484
|
};
|
|
11296
|
-
await writeFile(
|
|
11485
|
+
await writeFile(path13.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
|
|
11297
11486
|
if (options.json === true) {
|
|
11298
11487
|
console.log(writeJson3(manifest).trimEnd());
|
|
11299
11488
|
} else {
|
|
@@ -11304,1568 +11493,1893 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
11304
11493
|
}
|
|
11305
11494
|
}
|
|
11306
11495
|
}
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
}
|
|
11335
|
-
|
|
11336
|
-
}
|
|
11337
|
-
const normalized = path11.posix.normalize(rawPath.replace(/\\/g, "/"));
|
|
11338
|
-
const segments = normalized.split("/");
|
|
11339
|
-
if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
|
|
11340
|
-
diagnostics.push({
|
|
11341
|
-
code: "artifact_path_escape",
|
|
11342
|
-
severity: "error",
|
|
11343
|
-
message: "Reporter artifact path must stay under the output directory.",
|
|
11344
|
-
target: rawPath
|
|
11345
|
-
});
|
|
11346
|
-
return { ok: false, outputDir, diagnostics };
|
|
11347
|
-
}
|
|
11348
|
-
const absolutePath = path11.resolve(outputDir, normalized);
|
|
11349
|
-
const relFromOutput = path11.relative(outputDir, absolutePath);
|
|
11350
|
-
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path11.isAbsolute(relFromOutput)) {
|
|
11351
|
-
diagnostics.push({
|
|
11352
|
-
code: "artifact_path_escape",
|
|
11353
|
-
severity: "error",
|
|
11354
|
-
message: "Reporter artifact path resolved outside the output directory.",
|
|
11355
|
-
target: rawPath
|
|
11356
|
-
});
|
|
11357
|
-
return { ok: false, outputDir, diagnostics };
|
|
11358
|
-
}
|
|
11496
|
+
|
|
11497
|
+
// packages/core/src/workspace/types.ts
|
|
11498
|
+
var WORKSPACE_SCHEMA_VERSION = "1.0";
|
|
11499
|
+
var WORKSPACE_DIR_NAME = ".agent-inspect";
|
|
11500
|
+
var WORKSPACE_MANIFEST_FILENAME = "workspace.json";
|
|
11501
|
+
|
|
11502
|
+
// packages/core/src/workspace/manifest.ts
|
|
11503
|
+
var DEFAULT_WORKSPACE_LAYOUT = {
|
|
11504
|
+
traceDirs: ["runs"],
|
|
11505
|
+
reportsDir: "reports",
|
|
11506
|
+
artifactsDir: "artifacts",
|
|
11507
|
+
bundlesDir: "bundles",
|
|
11508
|
+
notesDir: "notes"
|
|
11509
|
+
};
|
|
11510
|
+
var DEFAULT_REDACTION_PROFILE = "share";
|
|
11511
|
+
var REDACTION_PROFILES = [
|
|
11512
|
+
"local",
|
|
11513
|
+
"share",
|
|
11514
|
+
"strict"
|
|
11515
|
+
];
|
|
11516
|
+
var INDEX_TYPES = ["none", "sqlite", "custom"];
|
|
11517
|
+
var MAX_WORKSPACE_MANIFEST_BYTES = 64 * 1024;
|
|
11518
|
+
function createDefaultWorkspaceManifest(options) {
|
|
11519
|
+
const project = typeof options.project === "string" ? options.project.trim() : "";
|
|
11520
|
+
const index = {
|
|
11521
|
+
enabled: options.index?.enabled ?? false,
|
|
11522
|
+
type: options.index?.type ?? "none",
|
|
11523
|
+
...options.index?.path !== void 0 ? { path: options.index.path } : {}
|
|
11524
|
+
};
|
|
11359
11525
|
return {
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11526
|
+
schemaVersion: WORKSPACE_SCHEMA_VERSION,
|
|
11527
|
+
project,
|
|
11528
|
+
createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
11529
|
+
traceDirs: options.traceDirs ? [...options.traceDirs] : [...DEFAULT_WORKSPACE_LAYOUT.traceDirs],
|
|
11530
|
+
reportsDir: options.reportsDir ?? DEFAULT_WORKSPACE_LAYOUT.reportsDir,
|
|
11531
|
+
artifactsDir: options.artifactsDir ?? DEFAULT_WORKSPACE_LAYOUT.artifactsDir,
|
|
11532
|
+
bundlesDir: options.bundlesDir ?? DEFAULT_WORKSPACE_LAYOUT.bundlesDir,
|
|
11533
|
+
notesDir: options.notesDir ?? DEFAULT_WORKSPACE_LAYOUT.notesDir,
|
|
11534
|
+
redactionProfile: options.redactionProfile ?? DEFAULT_REDACTION_PROFILE,
|
|
11535
|
+
index
|
|
11365
11536
|
};
|
|
11366
11537
|
}
|
|
11367
|
-
|
|
11368
|
-
// packages/cli/src/ci-summary.ts
|
|
11369
|
-
var NOTE2 = "Generated locally by AgentInspect from reporter artifact manifests. Trace contents are not embedded.";
|
|
11370
|
-
var MAX_TEXT = 180;
|
|
11371
|
-
var FRAMEWORKS = /* @__PURE__ */ new Set(["vitest", "jest", "manual"]);
|
|
11372
|
-
var STATUSES = /* @__PURE__ */ new Set(["passed", "failed", "skipped", "todo"]);
|
|
11373
|
-
var ARTIFACT_KINDS = /* @__PURE__ */ new Set(["trace", "report", "eval", "redaction", "summary"]);
|
|
11374
|
-
var ARTIFACT_FORMATS = /* @__PURE__ */ new Set(["json", "jsonl", "md", "html"]);
|
|
11375
|
-
var REDACTION_PROFILES = /* @__PURE__ */ new Set(["local", "share", "strict"]);
|
|
11376
|
-
function stable6(value) {
|
|
11377
|
-
if (Array.isArray(value)) return value.map(stable6);
|
|
11378
|
-
if (value === null || typeof value !== "object") return value;
|
|
11379
|
-
const record = value;
|
|
11380
|
-
return Object.fromEntries(
|
|
11381
|
-
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable6(record[key])])
|
|
11382
|
-
);
|
|
11383
|
-
}
|
|
11384
|
-
function writeJson4(value) {
|
|
11385
|
-
return `${JSON.stringify(stable6(value), null, 2)}
|
|
11386
|
-
`;
|
|
11387
|
-
}
|
|
11388
|
-
function isObject(value) {
|
|
11538
|
+
function isPlainObject(value) {
|
|
11389
11539
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11390
11540
|
}
|
|
11391
|
-
function
|
|
11541
|
+
function isSafeRelativeWorkspacePath(p) {
|
|
11542
|
+
if (typeof p !== "string") return false;
|
|
11543
|
+
const trimmed = p.trim();
|
|
11544
|
+
if (trimmed === "") return false;
|
|
11545
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("\\")) return false;
|
|
11546
|
+
if (/^[a-zA-Z]:/.test(trimmed)) return false;
|
|
11547
|
+
const segments = trimmed.split(/[/\\]+/);
|
|
11548
|
+
return !segments.some((seg) => seg === "..");
|
|
11549
|
+
}
|
|
11550
|
+
function validateDirField(value, field, errors) {
|
|
11392
11551
|
if (typeof value !== "string" || value.trim() === "") {
|
|
11393
|
-
|
|
11552
|
+
errors.push(`${field} must be a non-empty string`);
|
|
11553
|
+
return;
|
|
11394
11554
|
}
|
|
11395
|
-
|
|
11396
|
-
|
|
11397
|
-
|
|
11398
|
-
|
|
11399
|
-
}
|
|
11400
|
-
function readFramework(value) {
|
|
11401
|
-
const framework = readString(value, "manifest.framework");
|
|
11402
|
-
if (!FRAMEWORKS.has(framework)) {
|
|
11403
|
-
throw new Error(`Unsupported reporter framework: ${framework}.`);
|
|
11555
|
+
if (!isSafeRelativeWorkspacePath(value)) {
|
|
11556
|
+
errors.push(
|
|
11557
|
+
`${field} must be a relative path inside the workspace (no absolute paths or ".." traversal)`
|
|
11558
|
+
);
|
|
11404
11559
|
}
|
|
11405
|
-
return framework;
|
|
11406
11560
|
}
|
|
11407
|
-
function
|
|
11408
|
-
|
|
11409
|
-
|
|
11410
|
-
|
|
11561
|
+
function validateIndex(value, errors) {
|
|
11562
|
+
if (!isPlainObject(value)) {
|
|
11563
|
+
errors.push("index must be an object");
|
|
11564
|
+
return void 0;
|
|
11411
11565
|
}
|
|
11412
|
-
|
|
11413
|
-
|
|
11414
|
-
function readArtifact(value, index) {
|
|
11415
|
-
if (!isObject(value)) throw new Error(`manifest.artifacts[${index}] must be an object.`);
|
|
11416
|
-
const kind = readString(value.kind, `manifest.artifacts[${index}].kind`);
|
|
11417
|
-
const format = readString(value.format, `manifest.artifacts[${index}].format`);
|
|
11418
|
-
const redactionProfile = readString(
|
|
11419
|
-
value.redactionProfile,
|
|
11420
|
-
`manifest.artifacts[${index}].redactionProfile`
|
|
11421
|
-
);
|
|
11422
|
-
if (!ARTIFACT_KINDS.has(kind)) throw new Error(`Unsupported artifact kind: ${kind}.`);
|
|
11423
|
-
if (!ARTIFACT_FORMATS.has(format)) throw new Error(`Unsupported artifact format: ${format}.`);
|
|
11424
|
-
if (!REDACTION_PROFILES.has(redactionProfile)) {
|
|
11425
|
-
throw new Error(`Unsupported artifact redaction profile: ${redactionProfile}.`);
|
|
11566
|
+
if (typeof value.enabled !== "boolean") {
|
|
11567
|
+
errors.push("index.enabled must be a boolean");
|
|
11426
11568
|
}
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11569
|
+
if (!INDEX_TYPES.includes(value.type)) {
|
|
11570
|
+
errors.push(`index.type must be one of: ${INDEX_TYPES.join(", ")}`);
|
|
11571
|
+
}
|
|
11572
|
+
if (value.path !== void 0 && !isSafeRelativeWorkspacePath(value.path)) {
|
|
11573
|
+
errors.push(
|
|
11574
|
+
'index.path must be a relative path inside the workspace (no absolute paths or ".." traversal)'
|
|
11575
|
+
);
|
|
11434
11576
|
}
|
|
11577
|
+
if (errors.length > 0) return void 0;
|
|
11435
11578
|
return {
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
redactionProfile
|
|
11579
|
+
enabled: value.enabled,
|
|
11580
|
+
type: value.type,
|
|
11581
|
+
...value.path !== void 0 ? { path: value.path } : {}
|
|
11440
11582
|
};
|
|
11441
11583
|
}
|
|
11442
|
-
function
|
|
11443
|
-
|
|
11444
|
-
|
|
11445
|
-
|
|
11584
|
+
function validateWorkspaceManifest(input3) {
|
|
11585
|
+
const errors = [];
|
|
11586
|
+
const warnings = [];
|
|
11587
|
+
if (!isPlainObject(input3)) {
|
|
11588
|
+
return { ok: false, errors: ["manifest must be an object"], warnings };
|
|
11589
|
+
}
|
|
11590
|
+
if (input3.schemaVersion !== WORKSPACE_SCHEMA_VERSION) {
|
|
11591
|
+
errors.push(
|
|
11592
|
+
`schemaVersion must be "${WORKSPACE_SCHEMA_VERSION}" (received ${JSON.stringify(
|
|
11593
|
+
input3.schemaVersion
|
|
11594
|
+
)})`
|
|
11595
|
+
);
|
|
11596
|
+
}
|
|
11597
|
+
if (typeof input3.project !== "string" || input3.project.trim() === "") {
|
|
11598
|
+
errors.push("project must be a non-empty string");
|
|
11599
|
+
}
|
|
11600
|
+
if (typeof input3.createdAt !== "string" || input3.createdAt.trim() === "") {
|
|
11601
|
+
errors.push("createdAt must be a non-empty ISO-8601 string");
|
|
11602
|
+
} else if (Number.isNaN(Date.parse(input3.createdAt))) {
|
|
11603
|
+
errors.push("createdAt must be a valid ISO-8601 date string");
|
|
11604
|
+
}
|
|
11605
|
+
if (!Array.isArray(input3.traceDirs) || input3.traceDirs.length === 0) {
|
|
11606
|
+
errors.push("traceDirs must be a non-empty array");
|
|
11607
|
+
} else {
|
|
11608
|
+
input3.traceDirs.forEach((dir, i) => {
|
|
11609
|
+
if (typeof dir !== "string" || dir.trim() === "") {
|
|
11610
|
+
errors.push(`traceDirs[${i}] must be a non-empty string`);
|
|
11611
|
+
} else if (!isSafeRelativeWorkspacePath(dir)) {
|
|
11612
|
+
errors.push(
|
|
11613
|
+
`traceDirs[${i}] must be a relative path inside the workspace (no absolute paths or ".." traversal)`
|
|
11614
|
+
);
|
|
11615
|
+
}
|
|
11616
|
+
});
|
|
11617
|
+
}
|
|
11618
|
+
validateDirField(input3.reportsDir, "reportsDir", errors);
|
|
11619
|
+
validateDirField(input3.artifactsDir, "artifactsDir", errors);
|
|
11620
|
+
validateDirField(input3.bundlesDir, "bundlesDir", errors);
|
|
11621
|
+
validateDirField(input3.notesDir, "notesDir", errors);
|
|
11622
|
+
if (!REDACTION_PROFILES.includes(input3.redactionProfile)) {
|
|
11623
|
+
errors.push(`redactionProfile must be one of: ${REDACTION_PROFILES.join(", ")}`);
|
|
11624
|
+
}
|
|
11625
|
+
const indexErrors = [];
|
|
11626
|
+
const index = validateIndex(input3.index, indexErrors);
|
|
11627
|
+
errors.push(...indexErrors);
|
|
11628
|
+
if (index && index.type !== "none" && !index.enabled) {
|
|
11629
|
+
warnings.push(`index.type is "${index.type}" but index.enabled is false`);
|
|
11630
|
+
}
|
|
11631
|
+
if (errors.length > 0 || index === void 0) {
|
|
11632
|
+
return { ok: false, errors, warnings };
|
|
11633
|
+
}
|
|
11634
|
+
const manifest = {
|
|
11635
|
+
schemaVersion: WORKSPACE_SCHEMA_VERSION,
|
|
11636
|
+
project: input3.project.trim(),
|
|
11637
|
+
createdAt: input3.createdAt,
|
|
11638
|
+
traceDirs: input3.traceDirs.map((d) => d.trim()),
|
|
11639
|
+
reportsDir: input3.reportsDir.trim(),
|
|
11640
|
+
artifactsDir: input3.artifactsDir.trim(),
|
|
11641
|
+
bundlesDir: input3.bundlesDir.trim(),
|
|
11642
|
+
notesDir: input3.notesDir.trim(),
|
|
11643
|
+
redactionProfile: input3.redactionProfile,
|
|
11644
|
+
index
|
|
11645
|
+
};
|
|
11646
|
+
return { ok: true, manifest, errors, warnings };
|
|
11446
11647
|
}
|
|
11447
|
-
function
|
|
11448
|
-
|
|
11449
|
-
if (
|
|
11450
|
-
|
|
11648
|
+
function parseWorkspaceManifest(json) {
|
|
11649
|
+
const warnings = [];
|
|
11650
|
+
if (typeof json !== "string") {
|
|
11651
|
+
return { ok: false, errors: ["manifest input must be a string"], warnings };
|
|
11652
|
+
}
|
|
11653
|
+
if (json.length > MAX_WORKSPACE_MANIFEST_BYTES) {
|
|
11654
|
+
return {
|
|
11655
|
+
ok: false,
|
|
11656
|
+
errors: [
|
|
11657
|
+
`manifest exceeds maximum size of ${MAX_WORKSPACE_MANIFEST_BYTES} bytes`
|
|
11658
|
+
],
|
|
11659
|
+
warnings
|
|
11660
|
+
};
|
|
11661
|
+
}
|
|
11662
|
+
let parsed;
|
|
11663
|
+
try {
|
|
11664
|
+
parsed = JSON.parse(json);
|
|
11665
|
+
} catch {
|
|
11666
|
+
return { ok: false, errors: ["manifest is not valid JSON"], warnings };
|
|
11667
|
+
}
|
|
11668
|
+
return validateWorkspaceManifest(parsed);
|
|
11451
11669
|
}
|
|
11452
|
-
function
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11670
|
+
function serializeWorkspaceManifest(manifest) {
|
|
11671
|
+
return `${JSON.stringify(manifest, null, 2)}
|
|
11672
|
+
`;
|
|
11673
|
+
}
|
|
11674
|
+
var INDEX_DIR_NAME = "index";
|
|
11675
|
+
function resolveWorkspaceLocation(cwd = process.cwd()) {
|
|
11676
|
+
const projectRoot = path13.resolve(cwd);
|
|
11677
|
+
const workspaceDir = path13.join(projectRoot, WORKSPACE_DIR_NAME);
|
|
11456
11678
|
return {
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
status: readStatus(value.status),
|
|
11461
|
-
...tracePath === void 0 ? {} : { tracePath },
|
|
11462
|
-
artifacts: readArtifacts(value.artifacts, `manifest.results[${index}].artifacts`),
|
|
11463
|
-
diagnostics: readDiagnosticsCount(
|
|
11464
|
-
value.diagnostics,
|
|
11465
|
-
`manifest.results[${index}].diagnostics`
|
|
11466
|
-
)
|
|
11679
|
+
projectRoot,
|
|
11680
|
+
workspaceDir,
|
|
11681
|
+
manifestPath: path13.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
|
|
11467
11682
|
};
|
|
11468
11683
|
}
|
|
11469
|
-
function
|
|
11470
|
-
|
|
11471
|
-
const
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
throw new Error(`Unsupported reporter manifest schemaVersion: ${schemaVersion}.`);
|
|
11684
|
+
function resolveInsideWorkspace(workspaceDir, relative) {
|
|
11685
|
+
const base = path13.resolve(workspaceDir);
|
|
11686
|
+
const resolved = path13.resolve(base, relative);
|
|
11687
|
+
const rel = path13.relative(base, resolved);
|
|
11688
|
+
if (rel === "" || rel === "." || !rel.startsWith("..") && !path13.isAbsolute(rel)) {
|
|
11689
|
+
return resolved;
|
|
11476
11690
|
}
|
|
11477
|
-
|
|
11478
|
-
|
|
11691
|
+
throw new Error(
|
|
11692
|
+
`Workspace path "${relative}" resolves outside the workspace directory`
|
|
11693
|
+
);
|
|
11694
|
+
}
|
|
11695
|
+
async function pathExists(p) {
|
|
11696
|
+
try {
|
|
11697
|
+
await access(p);
|
|
11698
|
+
return true;
|
|
11699
|
+
} catch {
|
|
11700
|
+
return false;
|
|
11479
11701
|
}
|
|
11480
|
-
const artifacts = readArtifacts(candidate.artifacts, "manifest.artifacts");
|
|
11481
|
-
const results = candidate.results.map((item, index) => readResult(item, index));
|
|
11482
|
-
const diagnostics = readDiagnosticsCount(candidate.diagnostics, "manifest.diagnostics");
|
|
11483
|
-
const manifest = {
|
|
11484
|
-
framework: readFramework(candidate.framework),
|
|
11485
|
-
generatedAt: readString(candidate.generatedAt, "manifest.generatedAt"),
|
|
11486
|
-
results,
|
|
11487
|
-
artifacts
|
|
11488
|
-
};
|
|
11489
|
-
return {
|
|
11490
|
-
packageName: readOptionalString(value.package),
|
|
11491
|
-
manifest,
|
|
11492
|
-
diagnostics
|
|
11493
|
-
};
|
|
11494
11702
|
}
|
|
11495
|
-
function
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
return
|
|
11703
|
+
async function isWritable(p) {
|
|
11704
|
+
try {
|
|
11705
|
+
await access(p, constants$1.W_OK);
|
|
11706
|
+
return true;
|
|
11707
|
+
} catch {
|
|
11708
|
+
return false;
|
|
11499
11709
|
}
|
|
11500
|
-
return relative;
|
|
11501
11710
|
}
|
|
11502
|
-
async function
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
name: safeText(result.name),
|
|
11510
|
-
...result.file === void 0 ? {} : { file: safeText(path11.basename(result.file)) },
|
|
11511
|
-
status: result.status,
|
|
11512
|
-
...result.tracePath === void 0 ? {} : { tracePath: safeText(path11.basename(result.tracePath)) },
|
|
11513
|
-
artifacts: result.artifacts,
|
|
11514
|
-
diagnostics: result.diagnostics
|
|
11515
|
-
}));
|
|
11516
|
-
return {
|
|
11517
|
-
...document.packageName === void 0 ? {} : { packageName: document.packageName },
|
|
11518
|
-
manifestFile: cwdRelative(absolute),
|
|
11519
|
-
framework: manifest.framework,
|
|
11520
|
-
generatedAt: manifest.generatedAt,
|
|
11521
|
-
results,
|
|
11522
|
-
artifacts: manifest.artifacts,
|
|
11523
|
-
diagnostics: document.diagnostics
|
|
11524
|
-
};
|
|
11711
|
+
async function listJsonl(dir) {
|
|
11712
|
+
try {
|
|
11713
|
+
const entries = await readdir(dir);
|
|
11714
|
+
return entries.filter((f) => f.endsWith(".jsonl"));
|
|
11715
|
+
} catch {
|
|
11716
|
+
return [];
|
|
11717
|
+
}
|
|
11525
11718
|
}
|
|
11526
|
-
function
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
for (const result of manifest.results) {
|
|
11541
|
-
summary.tests += 1;
|
|
11542
|
-
if (result.status === "failed") summary.failed += 1;
|
|
11543
|
-
else if (result.status === "passed") summary.passed += 1;
|
|
11544
|
-
else if (result.status === "skipped") summary.skipped += 1;
|
|
11545
|
-
else summary.todo += 1;
|
|
11546
|
-
summary.artifacts += result.artifacts.length;
|
|
11547
|
-
summary.diagnostics += result.diagnostics;
|
|
11548
|
-
}
|
|
11719
|
+
async function countFiles(dir) {
|
|
11720
|
+
try {
|
|
11721
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
11722
|
+
return entries.filter((e) => e.isFile()).length;
|
|
11723
|
+
} catch {
|
|
11724
|
+
return 0;
|
|
11725
|
+
}
|
|
11726
|
+
}
|
|
11727
|
+
async function readWorkspaceManifestFile(location) {
|
|
11728
|
+
let raw;
|
|
11729
|
+
try {
|
|
11730
|
+
raw = await readFile(location.manifestPath, "utf-8");
|
|
11731
|
+
} catch {
|
|
11732
|
+
return { exists: false, ok: false, errors: ["workspace.json not found"], warnings: [] };
|
|
11549
11733
|
}
|
|
11734
|
+
const parsed = parseWorkspaceManifest(raw);
|
|
11550
11735
|
return {
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11736
|
+
exists: true,
|
|
11737
|
+
ok: parsed.ok,
|
|
11738
|
+
...parsed.manifest ? { manifest: parsed.manifest } : {},
|
|
11739
|
+
errors: parsed.errors,
|
|
11740
|
+
warnings: parsed.warnings
|
|
11555
11741
|
};
|
|
11556
11742
|
}
|
|
11557
|
-
function
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
const
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
`| Tests | ${result.summary.tests} |`,
|
|
11571
|
-
`| Failed | ${result.summary.failed} |`,
|
|
11572
|
-
`| Passed | ${result.summary.passed} |`,
|
|
11573
|
-
`| Skipped | ${result.summary.skipped} |`,
|
|
11574
|
-
`| Todo | ${result.summary.todo} |`,
|
|
11575
|
-
`| Artifacts | ${result.summary.artifacts} |`,
|
|
11576
|
-
`| Diagnostics | ${result.summary.diagnostics} |`,
|
|
11577
|
-
"",
|
|
11578
|
-
"## Tests",
|
|
11579
|
-
"",
|
|
11580
|
-
"| Framework | Status | Test | File | Trace | Artifacts |",
|
|
11581
|
-
"| --- | --- | --- | --- | --- | --- |"
|
|
11582
|
-
];
|
|
11583
|
-
const rows = result.manifests.flatMap(
|
|
11584
|
-
(manifest) => manifest.results.map((test) => ({
|
|
11585
|
-
framework: manifest.framework,
|
|
11586
|
-
test
|
|
11587
|
-
}))
|
|
11588
|
-
);
|
|
11589
|
-
if (rows.length === 0) {
|
|
11590
|
-
lines.push("| unknown | unknown | No tests found | unknown | unknown | 0 |");
|
|
11743
|
+
async function createWorkspace(options = {}) {
|
|
11744
|
+
const location = resolveWorkspaceLocation(options.cwd);
|
|
11745
|
+
const dryRun = options.dryRun === true;
|
|
11746
|
+
const existing = await readWorkspaceManifestFile(location);
|
|
11747
|
+
const topLevelTraces = await listJsonl(location.workspaceDir);
|
|
11748
|
+
const detectedExistingTraces = topLevelTraces.length > 0;
|
|
11749
|
+
let manifest;
|
|
11750
|
+
let created;
|
|
11751
|
+
let adopted;
|
|
11752
|
+
if (existing.exists && existing.ok && existing.manifest) {
|
|
11753
|
+
manifest = existing.manifest;
|
|
11754
|
+
created = false;
|
|
11755
|
+
adopted = true;
|
|
11591
11756
|
} else {
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11757
|
+
const project = options.project?.trim() || path13.basename(location.projectRoot) || "workspace";
|
|
11758
|
+
const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
|
|
11759
|
+
manifest = createDefaultWorkspaceManifest({
|
|
11760
|
+
project,
|
|
11761
|
+
traceDirs,
|
|
11762
|
+
...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {}
|
|
11763
|
+
});
|
|
11764
|
+
created = true;
|
|
11765
|
+
adopted = detectedExistingTraces || existing.exists && !existing.ok;
|
|
11597
11766
|
}
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11767
|
+
const relDirs = uniqueDirs([
|
|
11768
|
+
...manifest.traceDirs.filter((d) => d !== "."),
|
|
11769
|
+
manifest.reportsDir,
|
|
11770
|
+
manifest.artifactsDir,
|
|
11771
|
+
manifest.bundlesDir,
|
|
11772
|
+
manifest.notesDir,
|
|
11773
|
+
INDEX_DIR_NAME
|
|
11774
|
+
]);
|
|
11775
|
+
const createdDirs = [];
|
|
11776
|
+
for (const rel of relDirs) {
|
|
11777
|
+
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
11778
|
+
if (await pathExists(abs)) continue;
|
|
11779
|
+
createdDirs.push(rel);
|
|
11780
|
+
if (!dryRun) await mkdir(abs, { recursive: true });
|
|
11603
11781
|
}
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
(
|
|
11607
|
-
);
|
|
11608
|
-
if (artifacts.length === 0) {
|
|
11609
|
-
lines.push("| unknown | unknown | No artifacts found | unknown | unknown |");
|
|
11610
|
-
} else {
|
|
11611
|
-
for (const row of artifacts) {
|
|
11612
|
-
lines.push(
|
|
11613
|
-
`| ${markdownCell(row.framework)} | ${markdownCell(row.artifact.kind)} | ${markdownCell(row.artifact.path)} | ${markdownCell(row.artifact.format)} | ${markdownCell(row.artifact.redactionProfile)} |`
|
|
11614
|
-
);
|
|
11615
|
-
}
|
|
11782
|
+
if (!dryRun && created) {
|
|
11783
|
+
await mkdir(location.workspaceDir, { recursive: true });
|
|
11784
|
+
await writeFile(location.manifestPath, serializeWorkspaceManifest(manifest), "utf-8");
|
|
11616
11785
|
}
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11786
|
+
return {
|
|
11787
|
+
location,
|
|
11788
|
+
manifest,
|
|
11789
|
+
created,
|
|
11790
|
+
adopted,
|
|
11791
|
+
createdDirs,
|
|
11792
|
+
detectedExistingTraces,
|
|
11793
|
+
dryRun
|
|
11794
|
+
};
|
|
11620
11795
|
}
|
|
11621
|
-
function
|
|
11622
|
-
const
|
|
11623
|
-
const
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11796
|
+
function uniqueDirs(dirs) {
|
|
11797
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11798
|
+
const out = [];
|
|
11799
|
+
for (const d of dirs) {
|
|
11800
|
+
const t = d.trim();
|
|
11801
|
+
if (t === "" || t === "." || seen.has(t)) continue;
|
|
11802
|
+
seen.add(t);
|
|
11803
|
+
out.push(t);
|
|
11804
|
+
}
|
|
11805
|
+
return out;
|
|
11629
11806
|
}
|
|
11630
|
-
async function
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11807
|
+
async function getWorkspaceStatus(location, manifest) {
|
|
11808
|
+
let traceFiles = 0;
|
|
11809
|
+
for (const rel of manifest.traceDirs) {
|
|
11810
|
+
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
11811
|
+
traceFiles += (await listJsonl(abs)).length;
|
|
11635
11812
|
}
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11813
|
+
const reports = await countFiles(
|
|
11814
|
+
resolveInsideWorkspace(location.workspaceDir, manifest.reportsDir)
|
|
11815
|
+
);
|
|
11816
|
+
const artifacts = await countFiles(
|
|
11817
|
+
resolveInsideWorkspace(location.workspaceDir, manifest.artifactsDir)
|
|
11818
|
+
);
|
|
11819
|
+
const bundles = await countFiles(
|
|
11820
|
+
resolveInsideWorkspace(location.workspaceDir, manifest.bundlesDir)
|
|
11821
|
+
);
|
|
11822
|
+
const notes = await countFiles(
|
|
11823
|
+
resolveInsideWorkspace(location.workspaceDir, manifest.notesDir)
|
|
11824
|
+
);
|
|
11825
|
+
const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
|
|
11826
|
+
return {
|
|
11827
|
+
project: manifest.project,
|
|
11828
|
+
traceFiles,
|
|
11829
|
+
reports,
|
|
11830
|
+
artifacts,
|
|
11831
|
+
bundles,
|
|
11832
|
+
notes,
|
|
11833
|
+
index: {
|
|
11834
|
+
enabled: manifest.index.enabled,
|
|
11835
|
+
type: manifest.index.type,
|
|
11836
|
+
exists: await pathExists(indexPath2)
|
|
11641
11837
|
}
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11838
|
+
};
|
|
11839
|
+
}
|
|
11840
|
+
async function doctorWorkspace(location) {
|
|
11841
|
+
const checks2 = [];
|
|
11842
|
+
const manifestResult = await readWorkspaceManifestFile(location);
|
|
11843
|
+
if (!manifestResult.exists) {
|
|
11844
|
+
checks2.push({
|
|
11845
|
+
id: "manifest",
|
|
11846
|
+
status: "fail",
|
|
11847
|
+
message: "workspace.json not found (run `agent-inspect workspace init`)"
|
|
11848
|
+
});
|
|
11849
|
+
return { ok: false, checks: checks2 };
|
|
11649
11850
|
}
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
|
|
11851
|
+
if (!manifestResult.ok || !manifestResult.manifest) {
|
|
11852
|
+
checks2.push({
|
|
11853
|
+
id: "manifest",
|
|
11854
|
+
status: "fail",
|
|
11855
|
+
message: `workspace.json is invalid: ${manifestResult.errors.join("; ")}`
|
|
11856
|
+
});
|
|
11857
|
+
return { ok: false, checks: checks2 };
|
|
11655
11858
|
}
|
|
11656
|
-
const
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
await appendFile(summaryPath, `
|
|
11661
|
-
${markdown}`, "utf-8");
|
|
11859
|
+
const manifest = manifestResult.manifest;
|
|
11860
|
+
checks2.push({ id: "manifest", status: "pass", message: "workspace.json is valid" });
|
|
11861
|
+
for (const warning of manifestResult.warnings) {
|
|
11862
|
+
checks2.push({ id: "manifest-warning", status: "warn", message: warning });
|
|
11662
11863
|
}
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
|
|
11864
|
+
const dirFields = [
|
|
11865
|
+
...manifest.traceDirs.filter((d) => d !== ".").map((d, i) => [`traceDir[${i}]`, d]),
|
|
11866
|
+
["reportsDir", manifest.reportsDir],
|
|
11867
|
+
["artifactsDir", manifest.artifactsDir],
|
|
11868
|
+
["bundlesDir", manifest.bundlesDir],
|
|
11869
|
+
["notesDir", manifest.notesDir]
|
|
11870
|
+
];
|
|
11871
|
+
for (const [id, rel] of dirFields) {
|
|
11872
|
+
let abs;
|
|
11873
|
+
try {
|
|
11874
|
+
abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
11875
|
+
} catch (error) {
|
|
11876
|
+
checks2.push({
|
|
11877
|
+
id,
|
|
11878
|
+
status: "fail",
|
|
11879
|
+
message: error instanceof Error ? error.message : String(error)
|
|
11880
|
+
});
|
|
11881
|
+
continue;
|
|
11882
|
+
}
|
|
11883
|
+
if (!await pathExists(abs)) {
|
|
11884
|
+
checks2.push({ id, status: "warn", message: `${rel}/ does not exist yet` });
|
|
11885
|
+
} else if (!await isWritable(abs)) {
|
|
11886
|
+
checks2.push({ id, status: "fail", message: `${rel}/ is not writable` });
|
|
11887
|
+
} else {
|
|
11888
|
+
checks2.push({ id, status: "pass", message: `${rel}/ is present and writable` });
|
|
11889
|
+
}
|
|
11670
11890
|
}
|
|
11671
|
-
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11891
|
+
let newestTraceMtime = 0;
|
|
11892
|
+
for (const rel of manifest.traceDirs) {
|
|
11893
|
+
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
11894
|
+
for (const file of await listJsonl(abs)) {
|
|
11895
|
+
try {
|
|
11896
|
+
const s = await stat(path13.join(abs, file));
|
|
11897
|
+
newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
|
|
11898
|
+
} catch {
|
|
11899
|
+
checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
|
|
11900
|
+
}
|
|
11901
|
+
}
|
|
11679
11902
|
}
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
}
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
};
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11903
|
+
if (manifest.index.enabled) {
|
|
11904
|
+
const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
|
|
11905
|
+
if (!await pathExists(indexPath2)) {
|
|
11906
|
+
checks2.push({ id: "index", status: "warn", message: "index enabled but not built" });
|
|
11907
|
+
} else {
|
|
11908
|
+
try {
|
|
11909
|
+
const s = await stat(indexPath2);
|
|
11910
|
+
if (newestTraceMtime > s.mtimeMs) {
|
|
11911
|
+
checks2.push({ id: "index", status: "warn", message: "index is stale (traces are newer)" });
|
|
11912
|
+
} else {
|
|
11913
|
+
checks2.push({ id: "index", status: "pass", message: "index is present" });
|
|
11914
|
+
}
|
|
11915
|
+
} catch {
|
|
11916
|
+
checks2.push({ id: "index", status: "warn", message: "cannot stat index" });
|
|
11917
|
+
}
|
|
11918
|
+
}
|
|
11919
|
+
}
|
|
11920
|
+
const ok = !checks2.some((c) => c.status === "fail");
|
|
11921
|
+
return { ok, checks: checks2 };
|
|
11699
11922
|
}
|
|
11700
|
-
function
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11923
|
+
async function cleanWorkspace(location, manifest, options = {}) {
|
|
11924
|
+
const dryRun = options.confirm !== true;
|
|
11925
|
+
const targets = uniqueDirs([
|
|
11926
|
+
manifest.reportsDir,
|
|
11927
|
+
manifest.artifactsDir,
|
|
11928
|
+
manifest.bundlesDir,
|
|
11929
|
+
manifest.index.path ?? INDEX_DIR_NAME
|
|
11930
|
+
]);
|
|
11931
|
+
const removed = [];
|
|
11932
|
+
for (const rel of targets) {
|
|
11933
|
+
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
11934
|
+
let entries;
|
|
11935
|
+
try {
|
|
11936
|
+
entries = await readdir(abs);
|
|
11937
|
+
} catch {
|
|
11938
|
+
continue;
|
|
11939
|
+
}
|
|
11940
|
+
for (const entry of entries) {
|
|
11941
|
+
const relPath = `${rel}/${entry}`;
|
|
11942
|
+
removed.push(relPath);
|
|
11943
|
+
if (!dryRun) {
|
|
11944
|
+
await rm(path13.join(abs, entry), { recursive: true, force: true });
|
|
11945
|
+
}
|
|
11946
|
+
}
|
|
11947
|
+
}
|
|
11948
|
+
return { dryRun, removed };
|
|
11714
11949
|
}
|
|
11715
11950
|
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
*/
|
|
11726
|
-
import { inspectRun, step } from "agent-inspect";
|
|
11727
|
-
|
|
11728
|
-
async function main() {
|
|
11729
|
-
await inspectRun("openai-agents-demo", async () => {
|
|
11730
|
-
await step.tool("mock-agent-run", async () => "ok");
|
|
11731
|
-
}, { traceDir: ".agent-inspect", silent: true });
|
|
11732
|
-
console.log("Trace written to .agent-inspect/");
|
|
11951
|
+
// packages/cli/src/bundle.ts
|
|
11952
|
+
var BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification.";
|
|
11953
|
+
function stable6(value) {
|
|
11954
|
+
if (Array.isArray(value)) return value.map(stable6);
|
|
11955
|
+
if (value === null || typeof value !== "object") return value;
|
|
11956
|
+
const record = value;
|
|
11957
|
+
return Object.fromEntries(
|
|
11958
|
+
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable6(record[key])])
|
|
11959
|
+
);
|
|
11733
11960
|
}
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
console.error(error);
|
|
11737
|
-
process.exitCode = 1;
|
|
11738
|
-
});
|
|
11961
|
+
function writeJson4(value) {
|
|
11962
|
+
return `${JSON.stringify(stable6(value), null, 2)}
|
|
11739
11963
|
`;
|
|
11740
|
-
case "langchain":
|
|
11741
|
-
return `/**
|
|
11742
|
-
* LangChain starter \u2014 wire @agent-inspect/langchain callbacks in your app.
|
|
11743
|
-
* This demo uses manual steps (no API keys).
|
|
11744
|
-
*/
|
|
11745
|
-
import { inspectRun, step } from "agent-inspect";
|
|
11746
|
-
|
|
11747
|
-
async function main() {
|
|
11748
|
-
await inspectRun("langchain-demo", async () => {
|
|
11749
|
-
await step.tool("mock-chain", async () => "ok");
|
|
11750
|
-
}, { traceDir: ".agent-inspect", silent: true });
|
|
11751
|
-
console.log("Trace written to .agent-inspect/");
|
|
11752
11964
|
}
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
process.exitCode = 1;
|
|
11757
|
-
});
|
|
11758
|
-
`;
|
|
11759
|
-
default:
|
|
11760
|
-
return `import { observe } from "agent-inspect";
|
|
11761
|
-
|
|
11762
|
-
class DemoAgent {
|
|
11763
|
-
async run(input: { question: string }) {
|
|
11764
|
-
return { answer: \`Echo: \${input.question}\` };
|
|
11965
|
+
function parseBundleProfile(value) {
|
|
11966
|
+
if (value === void 0 || value === "local" || value === "share" || value === "strict") {
|
|
11967
|
+
return value ?? "share";
|
|
11765
11968
|
}
|
|
11969
|
+
throw new Error(`Unsupported --profile "${value}". Use local, share, or strict.`);
|
|
11766
11970
|
}
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
traceDir: ".agent-inspect",
|
|
11770
|
-
silent: true,
|
|
11771
|
-
});
|
|
11772
|
-
|
|
11773
|
-
await agent.run({ question: "hello" });
|
|
11774
|
-
console.log("Trace written to .agent-inspect/");
|
|
11775
|
-
`;
|
|
11776
|
-
}
|
|
11971
|
+
function toReportProfile(profile) {
|
|
11972
|
+
return profile;
|
|
11777
11973
|
}
|
|
11778
|
-
function
|
|
11779
|
-
return
|
|
11780
|
-
|
|
11781
|
-
on:
|
|
11782
|
-
workflow_dispatch:
|
|
11783
|
-
push:
|
|
11784
|
-
branches: [main]
|
|
11785
|
-
|
|
11786
|
-
jobs:
|
|
11787
|
-
trace-artifacts:
|
|
11788
|
-
runs-on: ubuntu-latest
|
|
11789
|
-
steps:
|
|
11790
|
-
- uses: actions/checkout@v4
|
|
11791
|
-
- uses: actions/setup-node@v4
|
|
11792
|
-
with:
|
|
11793
|
-
node-version: "22"
|
|
11794
|
-
- run: npm ci
|
|
11795
|
-
- run: npm test
|
|
11796
|
-
- name: Upload AgentInspect traces
|
|
11797
|
-
if: always()
|
|
11798
|
-
uses: actions/upload-artifact@v4
|
|
11799
|
-
with:
|
|
11800
|
-
name: agent-inspect-traces
|
|
11801
|
-
path: .agent-inspect/**/*.jsonl
|
|
11802
|
-
if-no-files-found: ignore
|
|
11803
|
-
`;
|
|
11974
|
+
function escapeHtml3(value) {
|
|
11975
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
11804
11976
|
}
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
const demoPath = framework === "custom" ? path11.join("examples", "agent-inspect-demo.mjs") : path11.join("examples", `agent-inspect-${framework}-demo.mjs`);
|
|
11809
|
-
const candidates = [
|
|
11810
|
-
{ rel: CONFIG_FILE, content: configTemplate(framework) },
|
|
11811
|
-
{ rel: GITKEEP, content: "" },
|
|
11812
|
-
{ rel: demoPath, content: demoTemplate(framework) }
|
|
11813
|
-
];
|
|
11814
|
-
if (options.ci === "github") {
|
|
11815
|
-
candidates.push({
|
|
11816
|
-
rel: ".github/workflows/agent-inspect-artifacts.yml",
|
|
11817
|
-
content: githubWorkflowTemplate()
|
|
11818
|
-
});
|
|
11977
|
+
function safetyStatusFromAssess(status) {
|
|
11978
|
+
if (status === "SAFE" || status === "SAFE WITH WARNINGS" || status === "UNSAFE" || status === "UNKNOWN") {
|
|
11979
|
+
return status;
|
|
11819
11980
|
}
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11981
|
+
return "UNKNOWN";
|
|
11982
|
+
}
|
|
11983
|
+
async function resolveOutputDir(options, runIds, cwd) {
|
|
11984
|
+
if (options.out !== void 0 && options.out.trim() !== "") {
|
|
11985
|
+
const normalized = normalizeBundleOutputPath(options.out);
|
|
11823
11986
|
try {
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11987
|
+
const location = resolveWorkspaceLocation(cwd);
|
|
11988
|
+
const manifest = await readWorkspaceManifestFile(location);
|
|
11989
|
+
if (manifest.ok && manifest.manifest) {
|
|
11990
|
+
const rel = path13.relative(location.workspaceDir, normalized);
|
|
11991
|
+
if (!rel.startsWith("..") && !path13.isAbsolute(rel)) {
|
|
11992
|
+
return resolveInsideWorkspace(location.workspaceDir, rel);
|
|
11993
|
+
}
|
|
11994
|
+
}
|
|
11995
|
+
} catch {
|
|
11833
11996
|
}
|
|
11997
|
+
return normalized;
|
|
11834
11998
|
}
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
written.push(entry.path);
|
|
11846
|
-
continue;
|
|
11999
|
+
try {
|
|
12000
|
+
const location = resolveWorkspaceLocation(cwd);
|
|
12001
|
+
const manifest = await readWorkspaceManifestFile(location);
|
|
12002
|
+
if (manifest.ok && manifest.manifest) {
|
|
12003
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
12004
|
+
const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
|
|
12005
|
+
return resolveInsideWorkspace(
|
|
12006
|
+
location.workspaceDir,
|
|
12007
|
+
path13.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
|
|
12008
|
+
);
|
|
11847
12009
|
}
|
|
11848
|
-
|
|
11849
|
-
const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
|
|
11850
|
-
await writeFile(abs, content, "utf-8");
|
|
11851
|
-
written.push(entry.path);
|
|
12010
|
+
} catch {
|
|
11852
12011
|
}
|
|
11853
|
-
return
|
|
12012
|
+
return defaultBundleOutputPath(runIds);
|
|
11854
12013
|
}
|
|
11855
|
-
async function
|
|
11856
|
-
const
|
|
12014
|
+
async function writeBundleFile(outputDir, relativePath, content, files) {
|
|
12015
|
+
const outPath = path13.join(outputDir, relativePath);
|
|
12016
|
+
await mkdir(path13.dirname(outPath), { recursive: true });
|
|
12017
|
+
await writeFile(outPath, content, "utf-8");
|
|
12018
|
+
files.push(relativePath);
|
|
12019
|
+
}
|
|
12020
|
+
function renderBundleIndexHtml(parts) {
|
|
12021
|
+
const links = parts.runIds.map(
|
|
12022
|
+
(runId) => `<li><a href="assets/runs/${escapeHtml3(runId)}.html">${escapeHtml3(runId)}</a></li>`
|
|
12023
|
+
).join("");
|
|
12024
|
+
const sections = parts.runIds.map((runId) => {
|
|
12025
|
+
const html = parts.reports.get(runId) ?? "";
|
|
12026
|
+
return `<section id="run-${escapeHtml3(runId)}"><h2>${escapeHtml3(runId)}</h2>${html}</section>`;
|
|
12027
|
+
}).join("\n");
|
|
12028
|
+
return `<!doctype html>
|
|
12029
|
+
<html lang="en">
|
|
12030
|
+
<head>
|
|
12031
|
+
<meta charset="utf-8"/>
|
|
12032
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
12033
|
+
<title>AgentInspect Bundle</title>
|
|
12034
|
+
<style>
|
|
12035
|
+
body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}
|
|
12036
|
+
a{color:#0366d6}
|
|
12037
|
+
section{border-top:1px solid #ddd;margin-top:1.5rem;padding-top:1rem}
|
|
12038
|
+
</style>
|
|
12039
|
+
</head>
|
|
12040
|
+
<body>
|
|
12041
|
+
<h1>AgentInspect trace bundle</h1>
|
|
12042
|
+
<p>${escapeHtml3(BUNDLE_NOTE)}</p>
|
|
12043
|
+
<h2>Runs</h2>
|
|
12044
|
+
<ul>${links}</ul>
|
|
12045
|
+
${sections}
|
|
12046
|
+
</body>
|
|
12047
|
+
</html>
|
|
12048
|
+
`;
|
|
12049
|
+
}
|
|
12050
|
+
function extractHtmlBody(content) {
|
|
12051
|
+
const match = content.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
|
12052
|
+
return match?.[1]?.trim() ?? content;
|
|
12053
|
+
}
|
|
12054
|
+
async function bundleCommand(runIdArg, options = {}) {
|
|
12055
|
+
const profile = parseBundleProfile(options.profile);
|
|
12056
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
12057
|
+
const cwd = process.cwd();
|
|
12058
|
+
let resolveResult;
|
|
11857
12059
|
try {
|
|
11858
|
-
const
|
|
11859
|
-
const
|
|
11860
|
-
const
|
|
12060
|
+
const { runs } = await loadSessionRuns(traceDir);
|
|
12061
|
+
const staleThresholdMs = options.staleAfter && options.staleAfter.trim() !== "" ? parseDuration(options.staleAfter.trim()) : void 0;
|
|
12062
|
+
const index = buildSessionIndex(runs, {
|
|
12063
|
+
correlateByGroupId: options.correlateGroup === true,
|
|
12064
|
+
staleThresholdMs
|
|
12065
|
+
});
|
|
12066
|
+
resolveResult = resolveBundleRunIds(index, runs, {
|
|
12067
|
+
...runIdArg !== void 0 && runIdArg.trim() !== "" ? { runId: runIdArg.trim() } : {},
|
|
12068
|
+
...options.session ? { sessionId: options.session } : {},
|
|
12069
|
+
...options.since ? { since: options.since } : {}
|
|
12070
|
+
});
|
|
12071
|
+
} catch (error) {
|
|
12072
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11861
12073
|
if (options.json) {
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
framework: plan.framework,
|
|
11866
|
-
ci: plan.ci ?? null,
|
|
11867
|
-
dryRun: options.dryRun === true,
|
|
11868
|
-
planned: plan.files,
|
|
11869
|
-
wouldWrite: options.dryRun ? toWrite : void 0
|
|
11870
|
-
};
|
|
11871
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
11872
|
-
if (options.dryRun) return;
|
|
12074
|
+
console.log(writeJson4({ ok: false, error: message }).trimEnd());
|
|
12075
|
+
} else {
|
|
12076
|
+
console.error(`[AgentInspect] bundle failed: ${message}`);
|
|
11873
12077
|
}
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
12078
|
+
process.exitCode = 1;
|
|
12079
|
+
return;
|
|
12080
|
+
}
|
|
12081
|
+
const outputDir = await resolveOutputDir(options, resolveResult.runIds, cwd);
|
|
12082
|
+
const files = [];
|
|
12083
|
+
const checkRuns = [];
|
|
12084
|
+
const redactionRuns = [];
|
|
12085
|
+
const htmlByRun = /* @__PURE__ */ new Map();
|
|
12086
|
+
const redactedJsonlByRun = /* @__PURE__ */ new Map();
|
|
12087
|
+
let combinedJsonl = "";
|
|
12088
|
+
for (const runId of resolveResult.runIds) {
|
|
12089
|
+
const tracePath = getTraceFilePath(runId, traceDir);
|
|
12090
|
+
let rawContent;
|
|
12091
|
+
let sourceMtimeMs;
|
|
12092
|
+
try {
|
|
12093
|
+
rawContent = await readFile(tracePath, "utf-8");
|
|
12094
|
+
sourceMtimeMs = (await stat(tracePath)).mtimeMs;
|
|
12095
|
+
} catch (error) {
|
|
12096
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12097
|
+
if (options.json) {
|
|
12098
|
+
console.log(writeJson4({ ok: false, error: message, runId }).trimEnd());
|
|
12099
|
+
} else {
|
|
12100
|
+
console.error(`[AgentInspect] bundle failed: ${message}`);
|
|
11886
12101
|
}
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
12102
|
+
process.exitCode = 1;
|
|
12103
|
+
return;
|
|
12104
|
+
}
|
|
12105
|
+
let read;
|
|
12106
|
+
try {
|
|
12107
|
+
read = await openTrace(
|
|
12108
|
+
{ type: "file", path: tracePath },
|
|
12109
|
+
{ format: "agent-inspect-jsonl" }
|
|
12110
|
+
);
|
|
12111
|
+
} catch (error) {
|
|
12112
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12113
|
+
if (options.json) {
|
|
12114
|
+
console.log(writeJson4({ ok: false, error: message, runId }).trimEnd());
|
|
12115
|
+
} else {
|
|
12116
|
+
console.error(`[AgentInspect] bundle failed: ${message}`);
|
|
11891
12117
|
}
|
|
11892
|
-
|
|
11893
|
-
|
|
12118
|
+
process.exitCode = 1;
|
|
12119
|
+
return;
|
|
11894
12120
|
}
|
|
11895
|
-
|
|
11896
|
-
const
|
|
12121
|
+
const safety = assessOpenedTrace(read, { run: runId });
|
|
12122
|
+
const status = safetyStatusFromAssess(safety.status);
|
|
12123
|
+
checkRuns.push({
|
|
12124
|
+
runId,
|
|
12125
|
+
status,
|
|
12126
|
+
errors: safety.summary.errors,
|
|
12127
|
+
warnings: safety.summary.warnings,
|
|
12128
|
+
findings: safety.summary.findings
|
|
12129
|
+
});
|
|
12130
|
+
const redacted = redactTraceContent(rawContent, toReportProfile(profile));
|
|
12131
|
+
redactedJsonlByRun.set(runId, redacted.content);
|
|
12132
|
+
combinedJsonl += redacted.content.endsWith("\n") ? redacted.content : `${redacted.content}
|
|
12133
|
+
`;
|
|
12134
|
+
const detectors = [
|
|
12135
|
+
...new Set(redacted.findings.map((finding) => finding.detector))
|
|
12136
|
+
].sort((a, b) => a.localeCompare(b));
|
|
12137
|
+
redactionRuns.push({
|
|
12138
|
+
runId,
|
|
12139
|
+
findings: redacted.findings.length,
|
|
12140
|
+
detectors
|
|
12141
|
+
});
|
|
12142
|
+
const selected = read.runs.find((run) => run.runId === runId);
|
|
12143
|
+
const runReport = exportRunTree(selected ?? read.runs[0], {
|
|
12144
|
+
format: "html",
|
|
12145
|
+
redacted: true,
|
|
12146
|
+
redactionProfile: toReportProfile(profile)
|
|
12147
|
+
});
|
|
12148
|
+
htmlByRun.set(runId, extractHtmlBody(runReport.content));
|
|
12149
|
+
const afterMtime = (await stat(tracePath)).mtimeMs;
|
|
12150
|
+
if (afterMtime !== sourceMtimeMs) {
|
|
12151
|
+
const message = `Source trace "${runId}" was modified during bundle creation.`;
|
|
12152
|
+
if (options.json) {
|
|
12153
|
+
console.log(writeJson4({ ok: false, error: message }).trimEnd());
|
|
12154
|
+
} else {
|
|
12155
|
+
console.error(`[AgentInspect] bundle failed: ${message}`);
|
|
12156
|
+
}
|
|
12157
|
+
process.exitCode = 1;
|
|
12158
|
+
return;
|
|
12159
|
+
}
|
|
12160
|
+
}
|
|
12161
|
+
const checks2 = {
|
|
12162
|
+
aggregateStatus: aggregateBundleSafeStatus(checkRuns.map((run) => run.status)),
|
|
12163
|
+
runs: checkRuns
|
|
12164
|
+
};
|
|
12165
|
+
if (bundleFailsOnSafety(checks2.aggregateStatus, options.allowUnsafe === true)) {
|
|
11897
12166
|
if (options.json) {
|
|
11898
|
-
console.log(
|
|
12167
|
+
console.log(
|
|
12168
|
+
writeJson4({
|
|
12169
|
+
ok: false,
|
|
12170
|
+
error: `Bundle safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`,
|
|
12171
|
+
checks: checks2
|
|
12172
|
+
}).trimEnd()
|
|
12173
|
+
);
|
|
11899
12174
|
} else {
|
|
11900
|
-
console.error(
|
|
12175
|
+
console.error(
|
|
12176
|
+
`[AgentInspect] bundle refused: safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`
|
|
12177
|
+
);
|
|
11901
12178
|
}
|
|
11902
12179
|
process.exitCode = 1;
|
|
12180
|
+
return;
|
|
11903
12181
|
}
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
"openai-agents": ["@agent-inspect/openai-agents"],
|
|
11909
|
-
langchain: ["@agent-inspect/langchain"]
|
|
11910
|
-
};
|
|
11911
|
-
function nodeVersionCheck() {
|
|
11912
|
-
const major = Number(process2.versions.node.split(".")[0]);
|
|
11913
|
-
if (Number.isNaN(major) || major < 20) {
|
|
11914
|
-
return {
|
|
11915
|
-
id: "node-version",
|
|
11916
|
-
status: "fail",
|
|
11917
|
-
message: `Node ${process2.versions.node} is below the supported minimum (20).`,
|
|
11918
|
-
remediation: "Upgrade to Node 20 LTS or newer.",
|
|
11919
|
-
evidence: process2.versions.node
|
|
11920
|
-
};
|
|
11921
|
-
}
|
|
11922
|
-
return {
|
|
11923
|
-
id: "node-version",
|
|
11924
|
-
status: "pass",
|
|
11925
|
-
message: `Node ${process2.versions.node} meets the minimum (>=20).`,
|
|
11926
|
-
evidence: process2.versions.node
|
|
12182
|
+
const redactionReport = {
|
|
12183
|
+
profile,
|
|
12184
|
+
totalFindings: redactionRuns.reduce((sum, run) => sum + run.findings, 0),
|
|
12185
|
+
runs: redactionRuns
|
|
11927
12186
|
};
|
|
11928
|
-
}
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
12187
|
+
await mkdir(outputDir, { recursive: true });
|
|
12188
|
+
for (const runId of resolveResult.runIds) {
|
|
12189
|
+
const jsonl = redactedJsonlByRun.get(runId) ?? "";
|
|
12190
|
+
await writeBundleFile(outputDir, `assets/runs/${runId}.jsonl`, jsonl, files);
|
|
12191
|
+
const html = htmlByRun.get(runId) ?? "";
|
|
12192
|
+
await writeBundleFile(
|
|
12193
|
+
outputDir,
|
|
12194
|
+
`assets/runs/${runId}.html`,
|
|
12195
|
+
runReportWrap(html, runId),
|
|
12196
|
+
files
|
|
12197
|
+
);
|
|
11938
12198
|
}
|
|
11939
|
-
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
12199
|
+
const primaryRunId = resolveResult.runIds[0];
|
|
12200
|
+
const traceHtml = resolveResult.runIds.length === 1 ? runReportWrap(htmlByRun.get(primaryRunId) ?? "", primaryRunId) : renderBundleIndexHtml({ runIds: resolveResult.runIds, reports: htmlByRun });
|
|
12201
|
+
await writeBundleFile(outputDir, "trace.jsonl", combinedJsonl, files);
|
|
12202
|
+
await writeBundleFile(outputDir, "trace.html", traceHtml, files);
|
|
12203
|
+
await writeBundleFile(
|
|
12204
|
+
outputDir,
|
|
12205
|
+
"check-results.json",
|
|
12206
|
+
writeJson4(checks2),
|
|
12207
|
+
files
|
|
12208
|
+
);
|
|
12209
|
+
await writeBundleFile(
|
|
12210
|
+
outputDir,
|
|
12211
|
+
"eval-results.json",
|
|
12212
|
+
writeJson4(buildPlaceholderArtifact()),
|
|
12213
|
+
files
|
|
12214
|
+
);
|
|
12215
|
+
await writeBundleFile(
|
|
12216
|
+
outputDir,
|
|
12217
|
+
"redaction-report.json",
|
|
12218
|
+
writeJson4(redactionReport),
|
|
12219
|
+
files
|
|
12220
|
+
);
|
|
12221
|
+
await writeBundleFile(
|
|
12222
|
+
outputDir,
|
|
12223
|
+
"performance-summary.json",
|
|
12224
|
+
writeJson4(buildPlaceholderArtifact()),
|
|
12225
|
+
files
|
|
12226
|
+
);
|
|
12227
|
+
const metadata = buildBundleMetadata({
|
|
12228
|
+
agentInspectVersion: version,
|
|
12229
|
+
profile,
|
|
12230
|
+
resolve: resolveResult,
|
|
12231
|
+
checks: checks2,
|
|
12232
|
+
files: [...files]
|
|
12233
|
+
});
|
|
12234
|
+
await writeBundleFile(outputDir, "metadata.json", writeJson4(metadata), files);
|
|
12235
|
+
await writeBundleFile(
|
|
12236
|
+
outputDir,
|
|
12237
|
+
"summary.md",
|
|
12238
|
+
buildBundleSummaryMarkdown({ metadata, checks: checks2, redaction: redactionReport }),
|
|
12239
|
+
files
|
|
12240
|
+
);
|
|
12241
|
+
metadata.files = [...files].sort((a, b) => a.localeCompare(b));
|
|
12242
|
+
await writeFile(path13.join(outputDir, "metadata.json"), writeJson4(metadata), "utf-8");
|
|
12243
|
+
if (options.json) {
|
|
12244
|
+
console.log(
|
|
12245
|
+
writeJson4({
|
|
12246
|
+
ok: true,
|
|
12247
|
+
outputDir,
|
|
12248
|
+
metadata,
|
|
12249
|
+
checks: checks2,
|
|
12250
|
+
redaction: redactionReport
|
|
12251
|
+
}).trimEnd()
|
|
12252
|
+
);
|
|
12253
|
+
return;
|
|
11965
12254
|
}
|
|
12255
|
+
console.log(`Bundle written to ${outputDir}`);
|
|
12256
|
+
console.log(`Safe status: ${metadata.safeStatus}`);
|
|
12257
|
+
console.log(`Runs: ${resolveResult.runIds.join(", ")}`);
|
|
12258
|
+
console.log(`Files: ${metadata.files.length}`);
|
|
11966
12259
|
}
|
|
11967
|
-
function
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
12260
|
+
function runReportWrap(body, runId) {
|
|
12261
|
+
return `<!doctype html>
|
|
12262
|
+
<html lang="en">
|
|
12263
|
+
<head>
|
|
12264
|
+
<meta charset="utf-8"/>
|
|
12265
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
12266
|
+
<title>AgentInspect \u2014 ${escapeHtml3(runId)}</title>
|
|
12267
|
+
<style>body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}</style>
|
|
12268
|
+
</head>
|
|
12269
|
+
<body>
|
|
12270
|
+
${body}
|
|
12271
|
+
</body>
|
|
12272
|
+
</html>
|
|
12273
|
+
`;
|
|
11976
12274
|
}
|
|
11977
|
-
function
|
|
11978
|
-
const
|
|
11979
|
-
const
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
message: "
|
|
12275
|
+
function validateReporterArtifactPath(options) {
|
|
12276
|
+
const outputDir = path13.resolve(options.outputDir);
|
|
12277
|
+
const diagnostics = [];
|
|
12278
|
+
const rawPath = options.relativePath;
|
|
12279
|
+
if (rawPath.length === 0) {
|
|
12280
|
+
diagnostics.push({
|
|
12281
|
+
code: "artifact_path_empty",
|
|
12282
|
+
severity: "error",
|
|
12283
|
+
message: "Reporter artifact path must not be empty."
|
|
11986
12284
|
});
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
12285
|
+
return { ok: false, outputDir, diagnostics };
|
|
12286
|
+
}
|
|
12287
|
+
if (rawPath.includes("\0")) {
|
|
12288
|
+
diagnostics.push({
|
|
12289
|
+
code: "invalid_artifact_path",
|
|
12290
|
+
severity: "error",
|
|
12291
|
+
message: "Reporter artifact path must not contain null bytes.",
|
|
12292
|
+
target: rawPath
|
|
11993
12293
|
});
|
|
12294
|
+
return { ok: false, outputDir, diagnostics };
|
|
11994
12295
|
}
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12000
|
-
|
|
12296
|
+
if (path13.isAbsolute(rawPath) || path13.win32.isAbsolute(rawPath)) {
|
|
12297
|
+
diagnostics.push({
|
|
12298
|
+
code: "artifact_path_absolute",
|
|
12299
|
+
severity: "error",
|
|
12300
|
+
message: "Reporter artifact path must be relative.",
|
|
12301
|
+
target: rawPath
|
|
12001
12302
|
});
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12303
|
+
return { ok: false, outputDir, diagnostics };
|
|
12304
|
+
}
|
|
12305
|
+
const normalized = path13.posix.normalize(rawPath.replace(/\\/g, "/"));
|
|
12306
|
+
const segments = normalized.split("/");
|
|
12307
|
+
if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
|
|
12308
|
+
diagnostics.push({
|
|
12309
|
+
code: "artifact_path_escape",
|
|
12310
|
+
severity: "error",
|
|
12311
|
+
message: "Reporter artifact path must stay under the output directory.",
|
|
12312
|
+
target: rawPath
|
|
12007
12313
|
});
|
|
12314
|
+
return { ok: false, outputDir, diagnostics };
|
|
12008
12315
|
}
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
return packages.map((name) => {
|
|
12020
|
-
const resolved = resolvePackage(cwd, name);
|
|
12021
|
-
if (!resolved.ok) {
|
|
12022
|
-
return {
|
|
12023
|
-
id: `optional-package-${name}`,
|
|
12024
|
-
status: framework !== void 0 ? "warn" : "skipped",
|
|
12025
|
-
message: `${name} is not installed.`,
|
|
12026
|
-
remediation: framework !== void 0 ? `npm install ${name}` : void 0
|
|
12027
|
-
};
|
|
12028
|
-
}
|
|
12029
|
-
return {
|
|
12030
|
-
id: `optional-package-${name}`,
|
|
12031
|
-
status: "pass",
|
|
12032
|
-
message: `${name} is available.`,
|
|
12033
|
-
evidence: resolved.version
|
|
12034
|
-
};
|
|
12035
|
-
});
|
|
12036
|
-
}
|
|
12037
|
-
function versionMismatchCheck(cwd) {
|
|
12038
|
-
const root = resolvePackage(cwd, "agent-inspect");
|
|
12039
|
-
if (!root.ok || root.version === void 0) {
|
|
12040
|
-
return {
|
|
12041
|
-
id: "version-alignment",
|
|
12042
|
-
status: "skipped",
|
|
12043
|
-
message: "Skipped version alignment \u2014 agent-inspect not installed locally."
|
|
12044
|
-
};
|
|
12045
|
-
}
|
|
12046
|
-
if (root.version !== version) {
|
|
12047
|
-
return {
|
|
12048
|
-
id: "version-alignment",
|
|
12049
|
-
status: "warn",
|
|
12050
|
-
message: `CLI version ${version} differs from local agent-inspect@${root.version}.`,
|
|
12051
|
-
remediation: "Align versions with npm install agent-inspect@latest",
|
|
12052
|
-
evidence: `cli=${version};local=${root.version}`
|
|
12053
|
-
};
|
|
12316
|
+
const absolutePath = path13.resolve(outputDir, normalized);
|
|
12317
|
+
const relFromOutput = path13.relative(outputDir, absolutePath);
|
|
12318
|
+
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path13.isAbsolute(relFromOutput)) {
|
|
12319
|
+
diagnostics.push({
|
|
12320
|
+
code: "artifact_path_escape",
|
|
12321
|
+
severity: "error",
|
|
12322
|
+
message: "Reporter artifact path resolved outside the output directory.",
|
|
12323
|
+
target: rawPath
|
|
12324
|
+
});
|
|
12325
|
+
return { ok: false, outputDir, diagnostics };
|
|
12054
12326
|
}
|
|
12055
12327
|
return {
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12328
|
+
ok: true,
|
|
12329
|
+
outputDir,
|
|
12330
|
+
relativePath: normalized,
|
|
12331
|
+
absolutePath,
|
|
12332
|
+
diagnostics
|
|
12059
12333
|
};
|
|
12060
12334
|
}
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
if (options.checkImports !== false) {
|
|
12078
|
-
checks2.push(...importSmoke(cwd));
|
|
12079
|
-
}
|
|
12080
|
-
checks2.push(...optionalPackageChecks(cwd, options.framework));
|
|
12081
|
-
return checks2.sort((a, b) => a.id.localeCompare(b.id));
|
|
12335
|
+
|
|
12336
|
+
// packages/cli/src/ci-summary.ts
|
|
12337
|
+
var NOTE2 = "Generated locally by AgentInspect from reporter artifact manifests. Trace contents are not embedded.";
|
|
12338
|
+
var MAX_TEXT = 180;
|
|
12339
|
+
var FRAMEWORKS = /* @__PURE__ */ new Set(["vitest", "jest", "manual"]);
|
|
12340
|
+
var STATUSES = /* @__PURE__ */ new Set(["passed", "failed", "skipped", "todo"]);
|
|
12341
|
+
var ARTIFACT_KINDS = /* @__PURE__ */ new Set(["trace", "report", "eval", "redaction", "summary"]);
|
|
12342
|
+
var ARTIFACT_FORMATS = /* @__PURE__ */ new Set(["json", "jsonl", "md", "html"]);
|
|
12343
|
+
var REDACTION_PROFILES2 = /* @__PURE__ */ new Set(["local", "share", "strict"]);
|
|
12344
|
+
function stable7(value) {
|
|
12345
|
+
if (Array.isArray(value)) return value.map(stable7);
|
|
12346
|
+
if (value === null || typeof value !== "object") return value;
|
|
12347
|
+
const record = value;
|
|
12348
|
+
return Object.fromEntries(
|
|
12349
|
+
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable7(record[key])])
|
|
12350
|
+
);
|
|
12082
12351
|
}
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
summary: { pass: checks2.filter((c) => c.status === "pass").length, warn: warned, fail: failed },
|
|
12094
|
-
checks: checks2
|
|
12095
|
-
},
|
|
12096
|
-
null,
|
|
12097
|
-
2
|
|
12098
|
-
)
|
|
12099
|
-
);
|
|
12100
|
-
if (failed > 0) process2.exitCode = 1;
|
|
12101
|
-
return;
|
|
12102
|
-
}
|
|
12103
|
-
console.log("AgentInspect doctor");
|
|
12104
|
-
for (const check of checks2) {
|
|
12105
|
-
const tag = check.status.toUpperCase();
|
|
12106
|
-
console.log(`[${tag}] ${check.id}: ${check.message}`);
|
|
12107
|
-
if (check.remediation) console.log(` \u2192 ${check.remediation}`);
|
|
12352
|
+
function writeJson5(value) {
|
|
12353
|
+
return `${JSON.stringify(stable7(value), null, 2)}
|
|
12354
|
+
`;
|
|
12355
|
+
}
|
|
12356
|
+
function isObject(value) {
|
|
12357
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12358
|
+
}
|
|
12359
|
+
function readString(value, label) {
|
|
12360
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
12361
|
+
throw new Error(`${label} must be a non-empty string.`);
|
|
12108
12362
|
}
|
|
12109
|
-
|
|
12110
|
-
Summary: ${failed} failed, ${warned} warnings`);
|
|
12111
|
-
if (failed > 0) process2.exitCode = 1;
|
|
12363
|
+
return safeText(value);
|
|
12112
12364
|
}
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
function defineIndexer(indexer) {
|
|
12116
|
-
if (!indexer.id.trim()) throw new Error("indexer id is required");
|
|
12117
|
-
return indexer;
|
|
12365
|
+
function readOptionalString(value) {
|
|
12366
|
+
return typeof value === "string" && value.trim() !== "" ? safeText(value) : void 0;
|
|
12118
12367
|
}
|
|
12119
|
-
|
|
12120
|
-
const
|
|
12121
|
-
if (
|
|
12122
|
-
|
|
12123
|
-
const files = await td.list();
|
|
12124
|
-
for (const file of files) {
|
|
12125
|
-
const stats = await td.getFileStats(file);
|
|
12126
|
-
if (stats.mtimeMs > builtMs) return true;
|
|
12368
|
+
function readFramework(value) {
|
|
12369
|
+
const framework = readString(value, "manifest.framework");
|
|
12370
|
+
if (!FRAMEWORKS.has(framework)) {
|
|
12371
|
+
throw new Error(`Unsupported reporter framework: ${framework}.`);
|
|
12127
12372
|
}
|
|
12128
|
-
return
|
|
12373
|
+
return framework;
|
|
12129
12374
|
}
|
|
12130
|
-
function
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
status: meta.status
|
|
12155
|
-
})).sort((a, b) => a.runId.localeCompare(b.runId));
|
|
12156
|
-
if (entries.length < slice.length) {
|
|
12157
|
-
warnings.push(
|
|
12158
|
-
`indexer.partial: indexed ${entries.length} of ${slice.length} trace files`
|
|
12159
|
-
);
|
|
12160
|
-
}
|
|
12161
|
-
return {
|
|
12162
|
-
traceDir,
|
|
12163
|
-
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12164
|
-
entries,
|
|
12165
|
-
warnings
|
|
12166
|
-
};
|
|
12167
|
-
}
|
|
12375
|
+
function readStatus(value) {
|
|
12376
|
+
const status = readString(value, "result.status");
|
|
12377
|
+
if (!STATUSES.has(status)) {
|
|
12378
|
+
throw new Error(`Unsupported reporter test status: ${status}.`);
|
|
12379
|
+
}
|
|
12380
|
+
return status;
|
|
12381
|
+
}
|
|
12382
|
+
function readArtifact(value, index) {
|
|
12383
|
+
if (!isObject(value)) throw new Error(`manifest.artifacts[${index}] must be an object.`);
|
|
12384
|
+
const kind = readString(value.kind, `manifest.artifacts[${index}].kind`);
|
|
12385
|
+
const format = readString(value.format, `manifest.artifacts[${index}].format`);
|
|
12386
|
+
const redactionProfile = readString(
|
|
12387
|
+
value.redactionProfile,
|
|
12388
|
+
`manifest.artifacts[${index}].redactionProfile`
|
|
12389
|
+
);
|
|
12390
|
+
if (!ARTIFACT_KINDS.has(kind)) throw new Error(`Unsupported artifact kind: ${kind}.`);
|
|
12391
|
+
if (!ARTIFACT_FORMATS.has(format)) throw new Error(`Unsupported artifact format: ${format}.`);
|
|
12392
|
+
if (!REDACTION_PROFILES2.has(redactionProfile)) {
|
|
12393
|
+
throw new Error(`Unsupported artifact redaction profile: ${redactionProfile}.`);
|
|
12394
|
+
}
|
|
12395
|
+
const artifactPath = readString(value.path, `manifest.artifacts[${index}].path`);
|
|
12396
|
+
const pathCheck = validateReporterArtifactPath({
|
|
12397
|
+
outputDir: process.cwd(),
|
|
12398
|
+
relativePath: artifactPath
|
|
12168
12399
|
});
|
|
12400
|
+
if (!pathCheck.ok || pathCheck.relativePath === void 0) {
|
|
12401
|
+
throw new Error(`Unsafe reporter artifact path: ${artifactPath}.`);
|
|
12402
|
+
}
|
|
12403
|
+
return {
|
|
12404
|
+
kind,
|
|
12405
|
+
path: pathCheck.relativePath,
|
|
12406
|
+
format,
|
|
12407
|
+
redactionProfile
|
|
12408
|
+
};
|
|
12169
12409
|
}
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
return path11.join(traceDir, INDEX_FILENAME);
|
|
12410
|
+
function readArtifacts(value, label) {
|
|
12411
|
+
if (value === void 0) return [];
|
|
12412
|
+
if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
|
|
12413
|
+
return value.map((item, index) => readArtifact(item, index));
|
|
12175
12414
|
}
|
|
12176
|
-
function
|
|
12177
|
-
if (
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
|
|
12415
|
+
function readDiagnosticsCount(value, label) {
|
|
12416
|
+
if (value === void 0) return 0;
|
|
12417
|
+
if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
|
|
12418
|
+
return value.length;
|
|
12419
|
+
}
|
|
12420
|
+
function readResult(value, index) {
|
|
12421
|
+
if (!isObject(value)) throw new Error(`manifest.results[${index}] must be an object.`);
|
|
12422
|
+
const file = readOptionalString(value.file);
|
|
12423
|
+
const tracePath = readOptionalString(value.tracePath);
|
|
12424
|
+
return {
|
|
12425
|
+
testId: readString(value.testId, `manifest.results[${index}].testId`),
|
|
12426
|
+
name: readString(value.name, `manifest.results[${index}].name`),
|
|
12427
|
+
...file === void 0 ? {} : { file },
|
|
12428
|
+
status: readStatus(value.status),
|
|
12429
|
+
...tracePath === void 0 ? {} : { tracePath },
|
|
12430
|
+
artifacts: readArtifacts(value.artifacts, `manifest.results[${index}].artifacts`),
|
|
12431
|
+
diagnostics: readDiagnosticsCount(
|
|
12432
|
+
value.diagnostics,
|
|
12433
|
+
`manifest.results[${index}].diagnostics`
|
|
12434
|
+
)
|
|
12435
|
+
};
|
|
12436
|
+
}
|
|
12437
|
+
function readManifestDocument(value) {
|
|
12438
|
+
if (!isObject(value)) throw new Error("Reporter manifest file must contain a JSON object.");
|
|
12439
|
+
const candidate = isObject(value.manifest) ? value.manifest : value;
|
|
12440
|
+
if (!isObject(candidate)) throw new Error("Reporter manifest must be a JSON object.");
|
|
12441
|
+
const schemaVersion = readString(candidate.schemaVersion, "manifest.schemaVersion");
|
|
12442
|
+
if (schemaVersion !== "0.1") {
|
|
12443
|
+
throw new Error(`Unsupported reporter manifest schemaVersion: ${schemaVersion}.`);
|
|
12181
12444
|
}
|
|
12182
|
-
|
|
12445
|
+
if (!Array.isArray(candidate.results)) {
|
|
12446
|
+
throw new Error("manifest.results must be an array.");
|
|
12447
|
+
}
|
|
12448
|
+
const artifacts = readArtifacts(candidate.artifacts, "manifest.artifacts");
|
|
12449
|
+
const results = candidate.results.map((item, index) => readResult(item, index));
|
|
12450
|
+
const diagnostics = readDiagnosticsCount(candidate.diagnostics, "manifest.diagnostics");
|
|
12451
|
+
const manifest = {
|
|
12452
|
+
framework: readFramework(candidate.framework),
|
|
12453
|
+
generatedAt: readString(candidate.generatedAt, "manifest.generatedAt"),
|
|
12454
|
+
results,
|
|
12455
|
+
artifacts
|
|
12456
|
+
};
|
|
12457
|
+
return {
|
|
12458
|
+
packageName: readOptionalString(value.package),
|
|
12459
|
+
manifest,
|
|
12460
|
+
diagnostics
|
|
12461
|
+
};
|
|
12183
12462
|
}
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
return
|
|
12188
|
-
} catch {
|
|
12189
|
-
return void 0;
|
|
12463
|
+
function cwdRelative(filePath) {
|
|
12464
|
+
const relative = path13.relative(process.cwd(), path13.resolve(filePath)).replace(/\\/g, "/");
|
|
12465
|
+
if (relative === "" || relative.startsWith("../") || path13.isAbsolute(relative)) {
|
|
12466
|
+
return path13.basename(filePath);
|
|
12190
12467
|
}
|
|
12468
|
+
return relative;
|
|
12191
12469
|
}
|
|
12192
|
-
async function
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
-
|
|
12470
|
+
async function readReporterManifest(filePath) {
|
|
12471
|
+
const absolute = path13.resolve(filePath);
|
|
12472
|
+
const raw = await readFile(absolute, "utf-8");
|
|
12473
|
+
const document = readManifestDocument(JSON.parse(raw));
|
|
12474
|
+
const manifest = document.manifest;
|
|
12475
|
+
const results = manifest.results.map((result) => ({
|
|
12476
|
+
testId: safeText(result.testId),
|
|
12477
|
+
name: safeText(result.name),
|
|
12478
|
+
...result.file === void 0 ? {} : { file: safeText(path13.basename(result.file)) },
|
|
12479
|
+
status: result.status,
|
|
12480
|
+
...result.tracePath === void 0 ? {} : { tracePath: safeText(path13.basename(result.tracePath)) },
|
|
12481
|
+
artifacts: result.artifacts,
|
|
12482
|
+
diagnostics: result.diagnostics
|
|
12483
|
+
}));
|
|
12484
|
+
return {
|
|
12485
|
+
...document.packageName === void 0 ? {} : { packageName: document.packageName },
|
|
12486
|
+
manifestFile: cwdRelative(absolute),
|
|
12487
|
+
framework: manifest.framework,
|
|
12488
|
+
generatedAt: manifest.generatedAt,
|
|
12489
|
+
results,
|
|
12490
|
+
artifacts: manifest.artifacts,
|
|
12491
|
+
diagnostics: document.diagnostics
|
|
12492
|
+
};
|
|
12493
|
+
}
|
|
12494
|
+
function summarize3(manifests) {
|
|
12495
|
+
const summary = {
|
|
12496
|
+
manifests: manifests.length,
|
|
12497
|
+
tests: 0,
|
|
12498
|
+
failed: 0,
|
|
12499
|
+
passed: 0,
|
|
12500
|
+
skipped: 0,
|
|
12501
|
+
todo: 0,
|
|
12502
|
+
artifacts: 0,
|
|
12503
|
+
diagnostics: 0
|
|
12504
|
+
};
|
|
12505
|
+
for (const manifest of manifests) {
|
|
12506
|
+
summary.artifacts += manifest.artifacts.length;
|
|
12507
|
+
summary.diagnostics += manifest.diagnostics;
|
|
12508
|
+
for (const result of manifest.results) {
|
|
12509
|
+
summary.tests += 1;
|
|
12510
|
+
if (result.status === "failed") summary.failed += 1;
|
|
12511
|
+
else if (result.status === "passed") summary.passed += 1;
|
|
12512
|
+
else if (result.status === "skipped") summary.skipped += 1;
|
|
12513
|
+
else summary.todo += 1;
|
|
12514
|
+
summary.artifacts += result.artifacts.length;
|
|
12515
|
+
summary.diagnostics += result.diagnostics;
|
|
12213
12516
|
}
|
|
12214
|
-
} catch (e) {
|
|
12215
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
12216
|
-
console.error(`[AgentInspect] index build failed: ${msg}`);
|
|
12217
|
-
process.exitCode = 1;
|
|
12218
12517
|
}
|
|
12518
|
+
return {
|
|
12519
|
+
status: summary.failed > 0 ? "failed" : summary.diagnostics > 0 ? "warning" : "ok",
|
|
12520
|
+
manifests,
|
|
12521
|
+
summary,
|
|
12522
|
+
note: NOTE2
|
|
12523
|
+
};
|
|
12219
12524
|
}
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12228
|
-
|
|
12229
|
-
|
|
12230
|
-
|
|
12231
|
-
|
|
12232
|
-
|
|
12233
|
-
|
|
12525
|
+
function markdownCell(value) {
|
|
12526
|
+
return safeText(String(value ?? "unknown")).replaceAll("|", "\\|").replace(/\r?\n/g, " ");
|
|
12527
|
+
}
|
|
12528
|
+
function renderMarkdown2(result) {
|
|
12529
|
+
const lines = [
|
|
12530
|
+
"# AgentInspect CI Summary",
|
|
12531
|
+
"",
|
|
12532
|
+
NOTE2,
|
|
12533
|
+
"",
|
|
12534
|
+
"| Field | Value |",
|
|
12535
|
+
"| --- | --- |",
|
|
12536
|
+
`| Status | ${result.status} |`,
|
|
12537
|
+
`| Manifests | ${result.summary.manifests} |`,
|
|
12538
|
+
`| Tests | ${result.summary.tests} |`,
|
|
12539
|
+
`| Failed | ${result.summary.failed} |`,
|
|
12540
|
+
`| Passed | ${result.summary.passed} |`,
|
|
12541
|
+
`| Skipped | ${result.summary.skipped} |`,
|
|
12542
|
+
`| Todo | ${result.summary.todo} |`,
|
|
12543
|
+
`| Artifacts | ${result.summary.artifacts} |`,
|
|
12544
|
+
`| Diagnostics | ${result.summary.diagnostics} |`,
|
|
12545
|
+
"",
|
|
12546
|
+
"## Tests",
|
|
12547
|
+
"",
|
|
12548
|
+
"| Framework | Status | Test | File | Trace | Artifacts |",
|
|
12549
|
+
"| --- | --- | --- | --- | --- | --- |"
|
|
12550
|
+
];
|
|
12551
|
+
const rows = result.manifests.flatMap(
|
|
12552
|
+
(manifest) => manifest.results.map((test) => ({
|
|
12553
|
+
framework: manifest.framework,
|
|
12554
|
+
test
|
|
12555
|
+
}))
|
|
12556
|
+
);
|
|
12557
|
+
if (rows.length === 0) {
|
|
12558
|
+
lines.push("| unknown | unknown | No tests found | unknown | unknown | 0 |");
|
|
12559
|
+
} else {
|
|
12560
|
+
for (const row of rows) {
|
|
12561
|
+
lines.push(
|
|
12562
|
+
`| ${markdownCell(row.framework)} | ${markdownCell(row.test.status)} | ${markdownCell(row.test.name)} | ${markdownCell(row.test.file)} | ${markdownCell(row.test.tracePath)} | ${row.test.artifacts.length} |`
|
|
12563
|
+
);
|
|
12234
12564
|
}
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12565
|
+
}
|
|
12566
|
+
lines.push("", "## Manifests", "", "| Framework | File | Generated | Artifacts |", "| --- | --- | --- | --- |");
|
|
12567
|
+
for (const manifest of result.manifests) {
|
|
12568
|
+
lines.push(
|
|
12569
|
+
`| ${markdownCell(manifest.framework)} | ${markdownCell(manifest.manifestFile)} | ${markdownCell(manifest.generatedAt)} | ${manifest.artifacts.length} |`
|
|
12570
|
+
);
|
|
12571
|
+
}
|
|
12572
|
+
lines.push("", "## Artifacts", "", "| Framework | Kind | Path | Format | Profile |", "| --- | --- | --- | --- | --- |");
|
|
12573
|
+
const artifacts = result.manifests.flatMap(
|
|
12574
|
+
(manifest) => manifest.artifacts.map((artifact) => ({ framework: manifest.framework, artifact }))
|
|
12575
|
+
);
|
|
12576
|
+
if (artifacts.length === 0) {
|
|
12577
|
+
lines.push("| unknown | unknown | No artifacts found | unknown | unknown |");
|
|
12578
|
+
} else {
|
|
12579
|
+
for (const row of artifacts) {
|
|
12580
|
+
lines.push(
|
|
12581
|
+
`| ${markdownCell(row.framework)} | ${markdownCell(row.artifact.kind)} | ${markdownCell(row.artifact.path)} | ${markdownCell(row.artifact.format)} | ${markdownCell(row.artifact.redactionProfile)} |`
|
|
12582
|
+
);
|
|
12249
12583
|
}
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
|
|
12257
|
-
|
|
12584
|
+
}
|
|
12585
|
+
lines.push("");
|
|
12586
|
+
return `${lines.join("\n")}
|
|
12587
|
+
`;
|
|
12588
|
+
}
|
|
12589
|
+
function safeText(value) {
|
|
12590
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
12591
|
+
const redacted = compact.replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(
|
|
12592
|
+
/\b(?:api[_-]?key|authorization|token|secret|password)\s*[:=]\s*[^,\s]+/gi,
|
|
12593
|
+
"$1=[REDACTED]"
|
|
12594
|
+
);
|
|
12595
|
+
if (redacted.length <= MAX_TEXT) return redacted;
|
|
12596
|
+
return `${redacted.slice(0, MAX_TEXT - 12)}...[truncated]`;
|
|
12597
|
+
}
|
|
12598
|
+
async function ciSummaryCommand(manifestPaths, options = {}) {
|
|
12599
|
+
if (manifestPaths.length === 0) {
|
|
12600
|
+
console.error("At least one reporter manifest path is required.");
|
|
12601
|
+
process.exitCode = 1;
|
|
12602
|
+
return;
|
|
12603
|
+
}
|
|
12604
|
+
let result;
|
|
12605
|
+
try {
|
|
12606
|
+
const manifests = [];
|
|
12607
|
+
for (const manifestPath of manifestPaths) {
|
|
12608
|
+
manifests.push(await readReporterManifest(manifestPath));
|
|
12258
12609
|
}
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
|
|
12610
|
+
manifests.sort((a, b) => a.manifestFile.localeCompare(b.manifestFile));
|
|
12611
|
+
result = summarize3(manifests);
|
|
12612
|
+
} catch (error) {
|
|
12613
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12614
|
+
console.error(`[AgentInspect] ci-summary failed: ${message}`);
|
|
12262
12615
|
process.exitCode = 1;
|
|
12616
|
+
return;
|
|
12617
|
+
}
|
|
12618
|
+
const markdown = renderMarkdown2(result);
|
|
12619
|
+
const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path13.resolve(options.output.trim()) : void 0;
|
|
12620
|
+
if (outputPath !== void 0) {
|
|
12621
|
+
await mkdir(path13.dirname(outputPath), { recursive: true });
|
|
12622
|
+
await writeFile(outputPath, markdown, "utf-8");
|
|
12623
|
+
}
|
|
12624
|
+
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
12625
|
+
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
12626
|
+
const summaryPath = path13.resolve(summaryTarget);
|
|
12627
|
+
await mkdir(path13.dirname(summaryPath), { recursive: true });
|
|
12628
|
+
await appendFile(summaryPath, `
|
|
12629
|
+
${markdown}`, "utf-8");
|
|
12630
|
+
}
|
|
12631
|
+
if (options.json === true) {
|
|
12632
|
+
console.log(writeJson5(result).trimEnd());
|
|
12633
|
+
} else if (outputPath !== void 0) {
|
|
12634
|
+
console.log(`Wrote AgentInspect CI summary to ${outputPath}`);
|
|
12635
|
+
console.log(`Status: ${result.status}`);
|
|
12636
|
+
} else {
|
|
12637
|
+
console.log(markdown.trimEnd());
|
|
12263
12638
|
}
|
|
12264
12639
|
}
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12640
|
+
var CONFIG_FILE = "agent-inspect.config.ts";
|
|
12641
|
+
var TRACE_DIR = ".agent-inspect";
|
|
12642
|
+
var GITKEEP = ".agent-inspect/.gitkeep";
|
|
12643
|
+
function normalizeFramework(value) {
|
|
12644
|
+
const raw = (value ?? "custom").trim();
|
|
12645
|
+
if (raw === "ai-sdk" || raw === "openai-agents" || raw === "langchain" || raw === "custom") {
|
|
12646
|
+
return raw;
|
|
12647
|
+
}
|
|
12648
|
+
throw new Error(
|
|
12649
|
+
"Unsupported --framework value. Use ai-sdk, openai-agents, langchain, or custom."
|
|
12650
|
+
);
|
|
12651
|
+
}
|
|
12652
|
+
function configTemplate(framework) {
|
|
12653
|
+
const base = `/**
|
|
12654
|
+
* AgentInspect local config (metadata-only capture by default).
|
|
12655
|
+
* See https://github.com/rajudandigam/agent-inspect/blob/main/docs/SAFE-TRACE-SHARING.md
|
|
12656
|
+
*/
|
|
12657
|
+
export const agentInspectConfig = {
|
|
12658
|
+
traceDir: ".agent-inspect",
|
|
12659
|
+
enabled: process.env.AGENT_INSPECT !== "0",
|
|
12660
|
+
redactionProfile: "local" as const,
|
|
12661
|
+
};
|
|
12662
|
+
`;
|
|
12663
|
+
if (framework === "custom") return base;
|
|
12664
|
+
return `${base}
|
|
12665
|
+
export const framework = "${framework}" as const;
|
|
12666
|
+
`;
|
|
12667
|
+
}
|
|
12668
|
+
function demoTemplate(framework) {
|
|
12669
|
+
switch (framework) {
|
|
12670
|
+
case "ai-sdk":
|
|
12671
|
+
return `/**
|
|
12672
|
+
* AI SDK starter \u2014 metadata-only telemetry (no real model calls in this demo).
|
|
12673
|
+
* Install: npm install agent-inspect @agent-inspect/ai-sdk ai
|
|
12674
|
+
*/
|
|
12675
|
+
import { inspectRun, step } from "agent-inspect";
|
|
12676
|
+
|
|
12677
|
+
async function main() {
|
|
12678
|
+
await inspectRun("ai-sdk-demo", async () => {
|
|
12679
|
+
await step.tool("mock-generate", async () => ({ text: "ok" }));
|
|
12680
|
+
}, { traceDir: ".agent-inspect", silent: true });
|
|
12681
|
+
console.log("Trace written to .agent-inspect/");
|
|
12682
|
+
}
|
|
12683
|
+
|
|
12684
|
+
main().catch((error) => {
|
|
12685
|
+
console.error(error);
|
|
12686
|
+
process.exitCode = 1;
|
|
12687
|
+
});
|
|
12688
|
+
`;
|
|
12689
|
+
case "openai-agents":
|
|
12690
|
+
return `/**
|
|
12691
|
+
* OpenAI Agents starter \u2014 use @agent-inspect/openai-agents for local-only processors.
|
|
12692
|
+
* This demo uses manual steps (no API keys).
|
|
12693
|
+
*/
|
|
12694
|
+
import { inspectRun, step } from "agent-inspect";
|
|
12695
|
+
|
|
12696
|
+
async function main() {
|
|
12697
|
+
await inspectRun("openai-agents-demo", async () => {
|
|
12698
|
+
await step.tool("mock-agent-run", async () => "ok");
|
|
12699
|
+
}, { traceDir: ".agent-inspect", silent: true });
|
|
12700
|
+
console.log("Trace written to .agent-inspect/");
|
|
12701
|
+
}
|
|
12702
|
+
|
|
12703
|
+
main().catch((error) => {
|
|
12704
|
+
console.error(error);
|
|
12705
|
+
process.exitCode = 1;
|
|
12706
|
+
});
|
|
12707
|
+
`;
|
|
12708
|
+
case "langchain":
|
|
12709
|
+
return `/**
|
|
12710
|
+
* LangChain starter \u2014 wire @agent-inspect/langchain callbacks in your app.
|
|
12711
|
+
* This demo uses manual steps (no API keys).
|
|
12712
|
+
*/
|
|
12713
|
+
import { inspectRun, step } from "agent-inspect";
|
|
12714
|
+
|
|
12715
|
+
async function main() {
|
|
12716
|
+
await inspectRun("langchain-demo", async () => {
|
|
12717
|
+
await step.tool("mock-chain", async () => "ok");
|
|
12718
|
+
}, { traceDir: ".agent-inspect", silent: true });
|
|
12719
|
+
console.log("Trace written to .agent-inspect/");
|
|
12720
|
+
}
|
|
12721
|
+
|
|
12722
|
+
main().catch((error) => {
|
|
12723
|
+
console.error(error);
|
|
12724
|
+
process.exitCode = 1;
|
|
12725
|
+
});
|
|
12726
|
+
`;
|
|
12727
|
+
default:
|
|
12728
|
+
return `import { observe } from "agent-inspect";
|
|
12729
|
+
|
|
12730
|
+
class DemoAgent {
|
|
12731
|
+
async run(input: { question: string }) {
|
|
12732
|
+
return { answer: \`Echo: \${input.question}\` };
|
|
12733
|
+
}
|
|
12734
|
+
}
|
|
12735
|
+
|
|
12736
|
+
const agent = observe(new DemoAgent(), {
|
|
12737
|
+
traceDir: ".agent-inspect",
|
|
12738
|
+
silent: true,
|
|
12739
|
+
});
|
|
12740
|
+
|
|
12741
|
+
await agent.run({ question: "hello" });
|
|
12742
|
+
console.log("Trace written to .agent-inspect/");
|
|
12743
|
+
`;
|
|
12744
|
+
}
|
|
12745
|
+
}
|
|
12746
|
+
function githubWorkflowTemplate() {
|
|
12747
|
+
return `name: AgentInspect artifacts
|
|
12748
|
+
|
|
12749
|
+
on:
|
|
12750
|
+
workflow_dispatch:
|
|
12751
|
+
push:
|
|
12752
|
+
branches: [main]
|
|
12753
|
+
|
|
12754
|
+
jobs:
|
|
12755
|
+
trace-artifacts:
|
|
12756
|
+
runs-on: ubuntu-latest
|
|
12757
|
+
steps:
|
|
12758
|
+
- uses: actions/checkout@v4
|
|
12759
|
+
- uses: actions/setup-node@v4
|
|
12760
|
+
with:
|
|
12761
|
+
node-version: "22"
|
|
12762
|
+
- run: npm ci
|
|
12763
|
+
- run: npm test
|
|
12764
|
+
- name: Upload AgentInspect traces
|
|
12765
|
+
if: always()
|
|
12766
|
+
uses: actions/upload-artifact@v4
|
|
12767
|
+
with:
|
|
12768
|
+
name: agent-inspect-traces
|
|
12769
|
+
path: .agent-inspect/**/*.jsonl
|
|
12770
|
+
if-no-files-found: ignore
|
|
12771
|
+
`;
|
|
12772
|
+
}
|
|
12773
|
+
async function planInit(options = {}) {
|
|
12774
|
+
const framework = normalizeFramework(options.framework);
|
|
12775
|
+
const cwd = path13.resolve(options.cwd ?? process.cwd());
|
|
12776
|
+
const demoPath = framework === "custom" ? path13.join("examples", "agent-inspect-demo.mjs") : path13.join("examples", `agent-inspect-${framework}-demo.mjs`);
|
|
12777
|
+
const candidates = [
|
|
12778
|
+
{ rel: CONFIG_FILE, content: configTemplate(framework) },
|
|
12779
|
+
{ rel: GITKEEP, content: "" },
|
|
12780
|
+
{ rel: demoPath, content: demoTemplate(framework) }
|
|
12781
|
+
];
|
|
12782
|
+
if (options.ci === "github") {
|
|
12783
|
+
candidates.push({
|
|
12784
|
+
rel: ".github/workflows/agent-inspect-artifacts.yml",
|
|
12785
|
+
content: githubWorkflowTemplate()
|
|
12786
|
+
});
|
|
12787
|
+
}
|
|
12788
|
+
const files = [];
|
|
12789
|
+
for (const candidate of candidates) {
|
|
12790
|
+
const abs = path13.join(cwd, candidate.rel);
|
|
12791
|
+
try {
|
|
12792
|
+
await access(abs);
|
|
12793
|
+
files.push({
|
|
12794
|
+
path: candidate.rel,
|
|
12795
|
+
action: "skip",
|
|
12796
|
+
reason: "file already exists"
|
|
12797
|
+
});
|
|
12798
|
+
} catch (error) {
|
|
12799
|
+
if (error.code !== "ENOENT") throw error;
|
|
12800
|
+
files.push({ path: candidate.rel, action: "create" });
|
|
12273
12801
|
}
|
|
12274
|
-
console.log(`Removed index: ${indexPath2}`);
|
|
12275
|
-
} catch (e) {
|
|
12276
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
12277
|
-
console.error(`[AgentInspect] index clean failed: ${msg}`);
|
|
12278
|
-
process.exitCode = 1;
|
|
12279
12802
|
}
|
|
12803
|
+
return { framework, ...options.ci ? { ci: options.ci } : {}, files };
|
|
12280
12804
|
}
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
try {
|
|
12287
|
-
return await import('./src-VQT7QLGV.mjs');
|
|
12288
|
-
} catch (e) {
|
|
12289
|
-
if (isModuleNotFound3(e)) {
|
|
12290
|
-
console.error(
|
|
12291
|
-
`The optional SQLite index is not installed. Run: npm install ${PACKAGE}`
|
|
12292
|
-
);
|
|
12293
|
-
process.exitCode = 1;
|
|
12294
|
-
return null;
|
|
12805
|
+
async function writePlannedFiles(plan, cwd, options) {
|
|
12806
|
+
const written = [];
|
|
12807
|
+
for (const entry of plan.files) {
|
|
12808
|
+
if (entry.action === "skip") {
|
|
12809
|
+
continue;
|
|
12295
12810
|
}
|
|
12296
|
-
const
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
}
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
12306
|
-
throw new Error(`${flag} must be a positive integer.`);
|
|
12811
|
+
const abs = path13.join(cwd, entry.path);
|
|
12812
|
+
if (options.dryRun) {
|
|
12813
|
+
written.push(entry.path);
|
|
12814
|
+
continue;
|
|
12815
|
+
}
|
|
12816
|
+
await mkdir(path13.dirname(abs), { recursive: true });
|
|
12817
|
+
const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
|
|
12818
|
+
await writeFile(abs, content, "utf-8");
|
|
12819
|
+
written.push(entry.path);
|
|
12307
12820
|
}
|
|
12308
|
-
return
|
|
12821
|
+
return written;
|
|
12309
12822
|
}
|
|
12310
|
-
async function
|
|
12311
|
-
|
|
12823
|
+
async function initCommand(options = {}) {
|
|
12824
|
+
const cwd = path13.resolve(options.cwd ?? process.cwd());
|
|
12312
12825
|
try {
|
|
12313
|
-
const
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12826
|
+
const plan = await planInit({ ...options, cwd });
|
|
12827
|
+
const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
|
|
12828
|
+
const skipped = plan.files.filter((file) => file.action === "skip");
|
|
12829
|
+
if (options.json) {
|
|
12830
|
+
const payload = {
|
|
12831
|
+
ok: true,
|
|
12832
|
+
version,
|
|
12833
|
+
framework: plan.framework,
|
|
12834
|
+
ci: plan.ci ?? null,
|
|
12835
|
+
dryRun: options.dryRun === true,
|
|
12836
|
+
planned: plan.files,
|
|
12837
|
+
wouldWrite: options.dryRun ? toWrite : void 0
|
|
12838
|
+
};
|
|
12839
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
12840
|
+
if (options.dryRun) return;
|
|
12841
|
+
}
|
|
12842
|
+
const written = await writePlannedFiles(plan, cwd, options);
|
|
12843
|
+
if (!options.json) {
|
|
12844
|
+
console.log("AgentInspect init");
|
|
12845
|
+
console.log(`Framework: ${plan.framework}`);
|
|
12846
|
+
console.log(`Trace directory: ${TRACE_DIR}/`);
|
|
12847
|
+
if (options.dryRun) {
|
|
12848
|
+
console.log("Dry run \u2014 would create:");
|
|
12849
|
+
for (const file of toWrite) console.log(`- ${file}`);
|
|
12850
|
+
for (const file of skipped) {
|
|
12851
|
+
console.log(`- ${file.path} (skip: ${file.reason ?? "exists"})`);
|
|
12852
|
+
}
|
|
12853
|
+
return;
|
|
12854
|
+
}
|
|
12855
|
+
console.log("Created:");
|
|
12856
|
+
for (const file of written) console.log(`- ${file}`);
|
|
12857
|
+
for (const file of skipped) {
|
|
12858
|
+
console.log(`- ${file.path} (skipped: ${file.reason ?? "exists"})`);
|
|
12320
12859
|
}
|
|
12860
|
+
console.log("\nNext: run your demo, then `npx agent-inspect list --dir .agent-inspect`");
|
|
12861
|
+
console.log("No dependencies were installed. Add packages manually when ready.");
|
|
12321
12862
|
}
|
|
12322
|
-
} catch {
|
|
12323
|
-
|
|
12324
|
-
return newest;
|
|
12325
|
-
}
|
|
12326
|
-
async function indexSqliteBuildCommand(options = {}) {
|
|
12327
|
-
const mod = await loadIndexSqlite();
|
|
12328
|
-
if (!mod) return;
|
|
12329
|
-
const result = await mod.buildIndex({
|
|
12330
|
-
traceDir: options.dir,
|
|
12331
|
-
maxRuns: parsePositiveInt(options.maxRuns, "--max-runs")
|
|
12332
|
-
});
|
|
12333
|
-
if (options.json) {
|
|
12334
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
12335
|
-
return;
|
|
12336
|
-
}
|
|
12337
|
-
console.log(`Built SQLite index: ${result.dbPath}`);
|
|
12338
|
-
console.log(`Runs: ${result.runs} Steps: ${result.steps} Errors: ${result.errors}`);
|
|
12339
|
-
for (const warning of result.warnings) console.log(`warning: ${warning}`);
|
|
12340
|
-
}
|
|
12341
|
-
async function indexSqliteStatusCommand(options = {}) {
|
|
12342
|
-
const mod = await loadIndexSqlite();
|
|
12343
|
-
if (!mod) return;
|
|
12344
|
-
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
12345
|
-
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
12346
|
-
const status = mod.indexStatus(dbPath);
|
|
12347
|
-
const stale = mod.isIndexStale(dbPath, await newestTraceMtimeMs2(traceDir));
|
|
12348
|
-
if (options.json) {
|
|
12349
|
-
console.log(JSON.stringify({ ok: true, traceDir, stale, ...status }, null, 2));
|
|
12350
|
-
return;
|
|
12351
|
-
}
|
|
12352
|
-
if (!status.exists) {
|
|
12353
|
-
console.log(`No SQLite index at ${dbPath}`);
|
|
12354
|
-
console.log("Run: agent-inspect index sqlite build");
|
|
12355
|
-
return;
|
|
12356
|
-
}
|
|
12357
|
-
console.log(`Index: ${status.dbPath}`);
|
|
12358
|
-
console.log(`Healthy: ${status.healthy ? "yes" : "no"}`);
|
|
12359
|
-
console.log(`Built: ${status.builtAt ?? "unknown"}`);
|
|
12360
|
-
console.log(`Runs: ${status.runs} Steps: ${status.steps}`);
|
|
12361
|
-
console.log(`Stale: ${stale ? "yes" : "no"}`);
|
|
12362
|
-
}
|
|
12363
|
-
async function indexSqliteQueryCommand(options = {}) {
|
|
12364
|
-
const mod = await loadIndexSqlite();
|
|
12365
|
-
if (!mod) return;
|
|
12366
|
-
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
12367
|
-
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
12368
|
-
const status = mod.indexStatus(dbPath);
|
|
12369
|
-
if (!status.exists || !status.healthy) {
|
|
12863
|
+
} catch (error) {
|
|
12864
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
12370
12865
|
if (options.json) {
|
|
12371
|
-
console.log(JSON.stringify({ ok: false,
|
|
12866
|
+
console.log(JSON.stringify({ ok: false, error: msg }, null, 2));
|
|
12372
12867
|
} else {
|
|
12373
|
-
console.
|
|
12868
|
+
console.error(`[AgentInspect] init failed: ${msg}`);
|
|
12374
12869
|
}
|
|
12375
12870
|
process.exitCode = 1;
|
|
12376
|
-
return;
|
|
12377
|
-
}
|
|
12378
|
-
const rows = mod.queryRuns(dbPath, {
|
|
12379
|
-
status: options.status,
|
|
12380
|
-
sessionId: options.session,
|
|
12381
|
-
name: options.name,
|
|
12382
|
-
kind: options.kind,
|
|
12383
|
-
tool: options.tool,
|
|
12384
|
-
limit: parsePositiveInt(options.limit, "--limit")
|
|
12385
|
-
});
|
|
12386
|
-
if (options.json) {
|
|
12387
|
-
console.log(JSON.stringify({ ok: true, count: rows.length, runs: rows }, null, 2));
|
|
12388
|
-
return;
|
|
12389
|
-
}
|
|
12390
|
-
if (rows.length === 0) {
|
|
12391
|
-
console.log("No matching runs.");
|
|
12392
|
-
return;
|
|
12393
|
-
}
|
|
12394
|
-
for (const run of rows) {
|
|
12395
|
-
const parts = [
|
|
12396
|
-
run.runId,
|
|
12397
|
-
run.status ?? "unknown",
|
|
12398
|
-
run.name ?? "",
|
|
12399
|
-
run.durationMs != null ? `${run.durationMs}ms` : ""
|
|
12400
|
-
].filter((p) => p !== "");
|
|
12401
|
-
console.log(parts.join(" "));
|
|
12402
|
-
}
|
|
12403
|
-
}
|
|
12404
|
-
async function indexSqliteCleanCommand(options = {}) {
|
|
12405
|
-
const mod = await loadIndexSqlite();
|
|
12406
|
-
if (!mod) return;
|
|
12407
|
-
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
12408
|
-
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
12409
|
-
await mod.cleanIndex(dbPath);
|
|
12410
|
-
if (options.json) {
|
|
12411
|
-
console.log(JSON.stringify({ ok: true, removed: dbPath }, null, 2));
|
|
12412
|
-
return;
|
|
12413
12871
|
}
|
|
12414
|
-
console.log(`Removed SQLite index: ${dbPath}`);
|
|
12415
12872
|
}
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
// packages/core/src/workspace/manifest.ts
|
|
12423
|
-
var DEFAULT_WORKSPACE_LAYOUT = {
|
|
12424
|
-
traceDirs: ["runs"],
|
|
12425
|
-
reportsDir: "reports",
|
|
12426
|
-
artifactsDir: "artifacts",
|
|
12427
|
-
bundlesDir: "bundles",
|
|
12428
|
-
notesDir: "notes"
|
|
12873
|
+
var OPTIONAL_PACKAGES = {
|
|
12874
|
+
custom: [],
|
|
12875
|
+
"ai-sdk": ["@agent-inspect/ai-sdk"],
|
|
12876
|
+
"openai-agents": ["@agent-inspect/openai-agents"],
|
|
12877
|
+
langchain: ["@agent-inspect/langchain"]
|
|
12429
12878
|
};
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
|
|
12440
|
-
const index = {
|
|
12441
|
-
enabled: options.index?.enabled ?? false,
|
|
12442
|
-
type: options.index?.type ?? "none",
|
|
12443
|
-
...options.index?.path !== void 0 ? { path: options.index.path } : {}
|
|
12444
|
-
};
|
|
12445
|
-
return {
|
|
12446
|
-
schemaVersion: WORKSPACE_SCHEMA_VERSION,
|
|
12447
|
-
project,
|
|
12448
|
-
createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
12449
|
-
traceDirs: options.traceDirs ? [...options.traceDirs] : [...DEFAULT_WORKSPACE_LAYOUT.traceDirs],
|
|
12450
|
-
reportsDir: options.reportsDir ?? DEFAULT_WORKSPACE_LAYOUT.reportsDir,
|
|
12451
|
-
artifactsDir: options.artifactsDir ?? DEFAULT_WORKSPACE_LAYOUT.artifactsDir,
|
|
12452
|
-
bundlesDir: options.bundlesDir ?? DEFAULT_WORKSPACE_LAYOUT.bundlesDir,
|
|
12453
|
-
notesDir: options.notesDir ?? DEFAULT_WORKSPACE_LAYOUT.notesDir,
|
|
12454
|
-
redactionProfile: options.redactionProfile ?? DEFAULT_REDACTION_PROFILE,
|
|
12455
|
-
index
|
|
12456
|
-
};
|
|
12457
|
-
}
|
|
12458
|
-
function isPlainObject(value) {
|
|
12459
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12460
|
-
}
|
|
12461
|
-
function isSafeRelativeWorkspacePath(p) {
|
|
12462
|
-
if (typeof p !== "string") return false;
|
|
12463
|
-
const trimmed = p.trim();
|
|
12464
|
-
if (trimmed === "") return false;
|
|
12465
|
-
if (trimmed.startsWith("/") || trimmed.startsWith("\\")) return false;
|
|
12466
|
-
if (/^[a-zA-Z]:/.test(trimmed)) return false;
|
|
12467
|
-
const segments = trimmed.split(/[/\\]+/);
|
|
12468
|
-
return !segments.some((seg) => seg === "..");
|
|
12469
|
-
}
|
|
12470
|
-
function validateDirField(value, field, errors) {
|
|
12471
|
-
if (typeof value !== "string" || value.trim() === "") {
|
|
12472
|
-
errors.push(`${field} must be a non-empty string`);
|
|
12473
|
-
return;
|
|
12474
|
-
}
|
|
12475
|
-
if (!isSafeRelativeWorkspacePath(value)) {
|
|
12476
|
-
errors.push(
|
|
12477
|
-
`${field} must be a relative path inside the workspace (no absolute paths or ".." traversal)`
|
|
12478
|
-
);
|
|
12879
|
+
function nodeVersionCheck() {
|
|
12880
|
+
const major = Number(process2.versions.node.split(".")[0]);
|
|
12881
|
+
if (Number.isNaN(major) || major < 20) {
|
|
12882
|
+
return {
|
|
12883
|
+
id: "node-version",
|
|
12884
|
+
status: "fail",
|
|
12885
|
+
message: `Node ${process2.versions.node} is below the supported minimum (20).`,
|
|
12886
|
+
remediation: "Upgrade to Node 20 LTS or newer.",
|
|
12887
|
+
evidence: process2.versions.node
|
|
12888
|
+
};
|
|
12479
12889
|
}
|
|
12890
|
+
return {
|
|
12891
|
+
id: "node-version",
|
|
12892
|
+
status: "pass",
|
|
12893
|
+
message: `Node ${process2.versions.node} meets the minimum (>=20).`,
|
|
12894
|
+
evidence: process2.versions.node
|
|
12895
|
+
};
|
|
12480
12896
|
}
|
|
12481
|
-
function
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
return
|
|
12485
|
-
|
|
12486
|
-
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
errors.push(`index.type must be one of: ${INDEX_TYPES.join(", ")}`);
|
|
12491
|
-
}
|
|
12492
|
-
if (value.path !== void 0 && !isSafeRelativeWorkspacePath(value.path)) {
|
|
12493
|
-
errors.push(
|
|
12494
|
-
'index.path must be a relative path inside the workspace (no absolute paths or ".." traversal)'
|
|
12495
|
-
);
|
|
12897
|
+
function envCheck(name, optional = true) {
|
|
12898
|
+
const value = process2.env[name];
|
|
12899
|
+
if (value === void 0 || value.trim() === "") {
|
|
12900
|
+
return {
|
|
12901
|
+
id: `env-${name.toLowerCase()}`,
|
|
12902
|
+
status: optional ? "skipped" : "warn",
|
|
12903
|
+
message: `${name} is not set.`,
|
|
12904
|
+
remediation: optional ? void 0 : `Set ${name}=1 to enable tracing.`
|
|
12905
|
+
};
|
|
12496
12906
|
}
|
|
12497
|
-
if (errors.length > 0) return void 0;
|
|
12498
12907
|
return {
|
|
12499
|
-
|
|
12500
|
-
|
|
12501
|
-
|
|
12908
|
+
id: `env-${name.toLowerCase()}`,
|
|
12909
|
+
status: "pass",
|
|
12910
|
+
message: `${name} is set.`,
|
|
12911
|
+
evidence: value
|
|
12502
12912
|
};
|
|
12503
12913
|
}
|
|
12504
|
-
function
|
|
12505
|
-
const
|
|
12506
|
-
|
|
12507
|
-
|
|
12508
|
-
|
|
12509
|
-
|
|
12510
|
-
|
|
12511
|
-
|
|
12512
|
-
`
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
12914
|
+
async function traceDirWritable(traceDir) {
|
|
12915
|
+
const resolved = path13.resolve(traceDir);
|
|
12916
|
+
try {
|
|
12917
|
+
await mkdir(resolved, { recursive: true });
|
|
12918
|
+
await access(resolved, constants.W_OK);
|
|
12919
|
+
return {
|
|
12920
|
+
id: "trace-dir-writable",
|
|
12921
|
+
status: "pass",
|
|
12922
|
+
message: `Trace directory is writable: ${resolved}`,
|
|
12923
|
+
evidence: resolved
|
|
12924
|
+
};
|
|
12925
|
+
} catch (error) {
|
|
12926
|
+
return {
|
|
12927
|
+
id: "trace-dir-writable",
|
|
12928
|
+
status: "fail",
|
|
12929
|
+
message: `Trace directory is not writable: ${resolved}`,
|
|
12930
|
+
remediation: "Create the directory or set AGENT_INSPECT_TRACE_DIR to a writable path.",
|
|
12931
|
+
evidence: error instanceof Error ? error.message : String(error)
|
|
12932
|
+
};
|
|
12519
12933
|
}
|
|
12520
|
-
|
|
12521
|
-
|
|
12522
|
-
|
|
12523
|
-
|
|
12934
|
+
}
|
|
12935
|
+
function resolvePackage(cwd, name) {
|
|
12936
|
+
const require2 = createRequire(path13.join(cwd, "package.json"));
|
|
12937
|
+
try {
|
|
12938
|
+
const pkgPath = require2.resolve(`${name}/package.json`);
|
|
12939
|
+
const pkg = require2(pkgPath);
|
|
12940
|
+
return { ok: true, version: pkg.version };
|
|
12941
|
+
} catch {
|
|
12942
|
+
return { ok: false };
|
|
12524
12943
|
}
|
|
12525
|
-
|
|
12526
|
-
|
|
12527
|
-
|
|
12528
|
-
|
|
12529
|
-
|
|
12530
|
-
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12944
|
+
}
|
|
12945
|
+
function importSmoke(cwd) {
|
|
12946
|
+
const results = [];
|
|
12947
|
+
const require2 = createRequire(path13.join(cwd, "package.json"));
|
|
12948
|
+
try {
|
|
12949
|
+
require2.resolve("agent-inspect");
|
|
12950
|
+
results.push({
|
|
12951
|
+
id: "import-agent-inspect",
|
|
12952
|
+
status: "pass",
|
|
12953
|
+
message: "agent-inspect resolves from the current project."
|
|
12954
|
+
});
|
|
12955
|
+
} catch {
|
|
12956
|
+
results.push({
|
|
12957
|
+
id: "import-agent-inspect",
|
|
12958
|
+
status: "warn",
|
|
12959
|
+
message: "agent-inspect is not installed in the current project.",
|
|
12960
|
+
remediation: "Run npm install agent-inspect (or pnpm add agent-inspect)."
|
|
12536
12961
|
});
|
|
12537
12962
|
}
|
|
12538
|
-
|
|
12539
|
-
|
|
12540
|
-
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
|
|
12963
|
+
try {
|
|
12964
|
+
require2.resolve("agent-inspect/package.json");
|
|
12965
|
+
results.push({
|
|
12966
|
+
id: "import-agent-inspect-cjs",
|
|
12967
|
+
status: "pass",
|
|
12968
|
+
message: "CJS resolution for agent-inspect succeeded."
|
|
12969
|
+
});
|
|
12970
|
+
} catch {
|
|
12971
|
+
results.push({
|
|
12972
|
+
id: "import-agent-inspect-cjs",
|
|
12973
|
+
status: "skipped",
|
|
12974
|
+
message: "CJS resolution check skipped (package not installed locally)."
|
|
12975
|
+
});
|
|
12544
12976
|
}
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12977
|
+
return results;
|
|
12978
|
+
}
|
|
12979
|
+
function optionalPackageChecks(cwd, framework) {
|
|
12980
|
+
const packages = framework !== void 0 ? OPTIONAL_PACKAGES[framework] : [
|
|
12981
|
+
"@agent-inspect/ai-sdk",
|
|
12982
|
+
"@agent-inspect/openai-agents",
|
|
12983
|
+
"@agent-inspect/langchain",
|
|
12984
|
+
"@agent-inspect/redact",
|
|
12985
|
+
"@agent-inspect/eval"
|
|
12986
|
+
];
|
|
12987
|
+
return packages.map((name) => {
|
|
12988
|
+
const resolved = resolvePackage(cwd, name);
|
|
12989
|
+
if (!resolved.ok) {
|
|
12990
|
+
return {
|
|
12991
|
+
id: `optional-package-${name}`,
|
|
12992
|
+
status: framework !== void 0 ? "warn" : "skipped",
|
|
12993
|
+
message: `${name} is not installed.`,
|
|
12994
|
+
remediation: framework !== void 0 ? `npm install ${name}` : void 0
|
|
12995
|
+
};
|
|
12996
|
+
}
|
|
12997
|
+
return {
|
|
12998
|
+
id: `optional-package-${name}`,
|
|
12999
|
+
status: "pass",
|
|
13000
|
+
message: `${name} is available.`,
|
|
13001
|
+
evidence: resolved.version
|
|
13002
|
+
};
|
|
13003
|
+
});
|
|
13004
|
+
}
|
|
13005
|
+
function versionMismatchCheck(cwd) {
|
|
13006
|
+
const root = resolvePackage(cwd, "agent-inspect");
|
|
13007
|
+
if (!root.ok || root.version === void 0) {
|
|
13008
|
+
return {
|
|
13009
|
+
id: "version-alignment",
|
|
13010
|
+
status: "skipped",
|
|
13011
|
+
message: "Skipped version alignment \u2014 agent-inspect not installed locally."
|
|
13012
|
+
};
|
|
12550
13013
|
}
|
|
12551
|
-
if (
|
|
12552
|
-
return {
|
|
13014
|
+
if (root.version !== version) {
|
|
13015
|
+
return {
|
|
13016
|
+
id: "version-alignment",
|
|
13017
|
+
status: "warn",
|
|
13018
|
+
message: `CLI version ${version} differs from local agent-inspect@${root.version}.`,
|
|
13019
|
+
remediation: "Align versions with npm install agent-inspect@latest",
|
|
13020
|
+
evidence: `cli=${version};local=${root.version}`
|
|
13021
|
+
};
|
|
12553
13022
|
}
|
|
12554
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
traceDirs: input3.traceDirs.map((d) => d.trim()),
|
|
12559
|
-
reportsDir: input3.reportsDir.trim(),
|
|
12560
|
-
artifactsDir: input3.artifactsDir.trim(),
|
|
12561
|
-
bundlesDir: input3.bundlesDir.trim(),
|
|
12562
|
-
notesDir: input3.notesDir.trim(),
|
|
12563
|
-
redactionProfile: input3.redactionProfile,
|
|
12564
|
-
index
|
|
13023
|
+
return {
|
|
13024
|
+
id: "version-alignment",
|
|
13025
|
+
status: "pass",
|
|
13026
|
+
message: `CLI and local agent-inspect are aligned at ${version}.`
|
|
12565
13027
|
};
|
|
12566
|
-
return { ok: true, manifest, errors, warnings };
|
|
12567
13028
|
}
|
|
12568
|
-
function
|
|
12569
|
-
const
|
|
12570
|
-
|
|
12571
|
-
|
|
13029
|
+
async function runDoctorChecks(options = {}) {
|
|
13030
|
+
const cwd = path13.resolve(options.cwd ?? process2.cwd());
|
|
13031
|
+
const traceDir = options.traceDir?.trim() || process2.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
|
|
13032
|
+
const checks2 = [
|
|
13033
|
+
nodeVersionCheck(),
|
|
13034
|
+
{
|
|
13035
|
+
id: "cli-version",
|
|
13036
|
+
status: "pass",
|
|
13037
|
+
message: `agent-inspect CLI ${version}`,
|
|
13038
|
+
evidence: version
|
|
13039
|
+
},
|
|
13040
|
+
await traceDirWritable(traceDir),
|
|
13041
|
+
envCheck("AGENT_INSPECT"),
|
|
13042
|
+
envCheck("AGENT_INSPECT_TRACE_DIR"),
|
|
13043
|
+
versionMismatchCheck(cwd)
|
|
13044
|
+
];
|
|
13045
|
+
if (options.checkImports !== false) {
|
|
13046
|
+
checks2.push(...importSmoke(cwd));
|
|
12572
13047
|
}
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12580
|
-
|
|
13048
|
+
checks2.push(...optionalPackageChecks(cwd, options.framework));
|
|
13049
|
+
return checks2.sort((a, b) => a.id.localeCompare(b.id));
|
|
13050
|
+
}
|
|
13051
|
+
async function doctorCommand(options = {}) {
|
|
13052
|
+
const checks2 = await runDoctorChecks(options);
|
|
13053
|
+
const failed = checks2.filter((check) => check.status === "fail").length;
|
|
13054
|
+
const warned = checks2.filter((check) => check.status === "warn").length;
|
|
13055
|
+
if (options.json) {
|
|
13056
|
+
console.log(
|
|
13057
|
+
JSON.stringify(
|
|
13058
|
+
{
|
|
13059
|
+
ok: failed === 0,
|
|
13060
|
+
version,
|
|
13061
|
+
summary: { pass: checks2.filter((c) => c.status === "pass").length, warn: warned, fail: failed },
|
|
13062
|
+
checks: checks2
|
|
13063
|
+
},
|
|
13064
|
+
null,
|
|
13065
|
+
2
|
|
13066
|
+
)
|
|
13067
|
+
);
|
|
13068
|
+
if (failed > 0) process2.exitCode = 1;
|
|
13069
|
+
return;
|
|
12581
13070
|
}
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
13071
|
+
console.log("AgentInspect doctor");
|
|
13072
|
+
for (const check of checks2) {
|
|
13073
|
+
const tag = check.status.toUpperCase();
|
|
13074
|
+
console.log(`[${tag}] ${check.id}: ${check.message}`);
|
|
13075
|
+
if (check.remediation) console.log(` \u2192 ${check.remediation}`);
|
|
12587
13076
|
}
|
|
12588
|
-
|
|
12589
|
-
}
|
|
12590
|
-
|
|
12591
|
-
return `${JSON.stringify(manifest, null, 2)}
|
|
12592
|
-
`;
|
|
13077
|
+
console.log(`
|
|
13078
|
+
Summary: ${failed} failed, ${warned} warnings`);
|
|
13079
|
+
if (failed > 0) process2.exitCode = 1;
|
|
12593
13080
|
}
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
return
|
|
12599
|
-
projectRoot,
|
|
12600
|
-
workspaceDir,
|
|
12601
|
-
manifestPath: path11.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
|
|
12602
|
-
};
|
|
13081
|
+
|
|
13082
|
+
// packages/adapter-sdk/src/indexer.ts
|
|
13083
|
+
function defineIndexer(indexer) {
|
|
13084
|
+
if (!indexer.id.trim()) throw new Error("indexer id is required");
|
|
13085
|
+
return indexer;
|
|
12603
13086
|
}
|
|
12604
|
-
function
|
|
12605
|
-
const
|
|
12606
|
-
|
|
12607
|
-
const
|
|
12608
|
-
|
|
12609
|
-
|
|
13087
|
+
async function indexIsStale(snapshot, traceDir) {
|
|
13088
|
+
const builtMs = Date.parse(snapshot.builtAt);
|
|
13089
|
+
if (Number.isNaN(builtMs)) return true;
|
|
13090
|
+
const td = new TraceDirectory({ dir: traceDir });
|
|
13091
|
+
const files = await td.list();
|
|
13092
|
+
for (const file of files) {
|
|
13093
|
+
const stats = await td.getFileStats(file);
|
|
13094
|
+
if (stats.mtimeMs > builtMs) return true;
|
|
12610
13095
|
}
|
|
12611
|
-
|
|
12612
|
-
`Workspace path "${relative}" resolves outside the workspace directory`
|
|
12613
|
-
);
|
|
13096
|
+
return false;
|
|
12614
13097
|
}
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
13098
|
+
function createTraceDirectoryIndexer() {
|
|
13099
|
+
return defineIndexer({
|
|
13100
|
+
id: "trace-directory-metadata",
|
|
13101
|
+
async rebuild(traceDir, options = {}) {
|
|
13102
|
+
const warnings = [];
|
|
13103
|
+
const td = new TraceDirectory({ dir: traceDir });
|
|
13104
|
+
const files = await td.list();
|
|
13105
|
+
const maxEntries = options.maxEntries ?? 1e4;
|
|
13106
|
+
if (files.length > maxEntries) {
|
|
13107
|
+
warnings.push(
|
|
13108
|
+
`indexer.truncated: trace directory has ${files.length} files; indexing first ${maxEntries}`
|
|
13109
|
+
);
|
|
13110
|
+
}
|
|
13111
|
+
const slice = files.slice(0, maxEntries);
|
|
13112
|
+
const metas = await loadTraceMetadataList(
|
|
13113
|
+
traceDir,
|
|
13114
|
+
slice,
|
|
13115
|
+
(fileName) => td.getPath(fileName)
|
|
13116
|
+
);
|
|
13117
|
+
const entries = metas.map((meta) => ({
|
|
13118
|
+
runId: meta.runId,
|
|
13119
|
+
path: meta.filePath,
|
|
13120
|
+
name: meta.name,
|
|
13121
|
+
startedAt: meta.startedAt,
|
|
13122
|
+
status: meta.status
|
|
13123
|
+
})).sort((a, b) => a.runId.localeCompare(b.runId));
|
|
13124
|
+
if (entries.length < slice.length) {
|
|
13125
|
+
warnings.push(
|
|
13126
|
+
`indexer.partial: indexed ${entries.length} of ${slice.length} trace files`
|
|
13127
|
+
);
|
|
13128
|
+
}
|
|
13129
|
+
return {
|
|
13130
|
+
traceDir,
|
|
13131
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13132
|
+
entries,
|
|
13133
|
+
warnings
|
|
13134
|
+
};
|
|
13135
|
+
}
|
|
13136
|
+
});
|
|
13137
|
+
}
|
|
13138
|
+
|
|
13139
|
+
// packages/cli/src/index-cmd.ts
|
|
13140
|
+
var INDEX_FILENAME = ".agent-inspect-index.json";
|
|
13141
|
+
function traceIndexPath(traceDir) {
|
|
13142
|
+
return path13.join(traceDir, INDEX_FILENAME);
|
|
13143
|
+
}
|
|
13144
|
+
function parseMaxEntries(raw) {
|
|
13145
|
+
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
13146
|
+
const parsed = Number.parseInt(raw, 10);
|
|
13147
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
13148
|
+
throw new Error("--max-entries must be a positive integer.");
|
|
12621
13149
|
}
|
|
13150
|
+
return parsed;
|
|
12622
13151
|
}
|
|
12623
|
-
async function
|
|
13152
|
+
async function readSnapshot(indexPath2) {
|
|
12624
13153
|
try {
|
|
12625
|
-
await
|
|
12626
|
-
return
|
|
13154
|
+
const raw = await readFile(indexPath2, "utf8");
|
|
13155
|
+
return JSON.parse(raw);
|
|
12627
13156
|
} catch {
|
|
12628
|
-
return
|
|
13157
|
+
return void 0;
|
|
12629
13158
|
}
|
|
12630
13159
|
}
|
|
12631
|
-
async function
|
|
13160
|
+
async function indexBuildCommand(options = {}) {
|
|
12632
13161
|
try {
|
|
12633
|
-
const
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
13162
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
13163
|
+
await mkdir(traceDir, { recursive: true });
|
|
13164
|
+
const indexer = createTraceDirectoryIndexer();
|
|
13165
|
+
const snapshot = await indexer.rebuild(traceDir, {
|
|
13166
|
+
maxEntries: parseMaxEntries(options.maxEntries)
|
|
13167
|
+
});
|
|
13168
|
+
const indexPath2 = traceIndexPath(traceDir);
|
|
13169
|
+
await writeFile(indexPath2, `${JSON.stringify(snapshot, null, 2)}
|
|
13170
|
+
`, "utf8");
|
|
13171
|
+
if (options.json) {
|
|
13172
|
+
console.log(JSON.stringify({ ok: true, indexPath: indexPath2, ...snapshot }, null, 2));
|
|
13173
|
+
return;
|
|
13174
|
+
}
|
|
13175
|
+
console.log(`Built trace index: ${indexPath2}`);
|
|
13176
|
+
console.log(`Entries: ${snapshot.entries.length}`);
|
|
13177
|
+
if (snapshot.warnings.length > 0) {
|
|
13178
|
+
for (const warning of snapshot.warnings) {
|
|
13179
|
+
console.log(`warning: ${warning}`);
|
|
13180
|
+
}
|
|
13181
|
+
}
|
|
13182
|
+
} catch (e) {
|
|
13183
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
13184
|
+
console.error(`[AgentInspect] index build failed: ${msg}`);
|
|
13185
|
+
process.exitCode = 1;
|
|
12637
13186
|
}
|
|
12638
13187
|
}
|
|
12639
|
-
async function
|
|
13188
|
+
async function indexStatusCommand(options = {}) {
|
|
12640
13189
|
try {
|
|
12641
|
-
const
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
13190
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
13191
|
+
const indexPath2 = traceIndexPath(traceDir);
|
|
13192
|
+
const snapshot = await readSnapshot(indexPath2);
|
|
13193
|
+
if (!snapshot) {
|
|
13194
|
+
const payload2 = { ok: true, exists: false, indexPath: indexPath2, traceDir, stale: true };
|
|
13195
|
+
if (options.json) {
|
|
13196
|
+
console.log(JSON.stringify(payload2, null, 2));
|
|
13197
|
+
} else {
|
|
13198
|
+
console.log(`No index at ${indexPath2}`);
|
|
13199
|
+
console.log("Run: agent-inspect index build");
|
|
13200
|
+
}
|
|
13201
|
+
return;
|
|
13202
|
+
}
|
|
13203
|
+
const stale = await indexIsStale(snapshot, traceDir);
|
|
13204
|
+
const payload = {
|
|
13205
|
+
ok: true,
|
|
13206
|
+
exists: true,
|
|
13207
|
+
indexPath: indexPath2,
|
|
13208
|
+
traceDir,
|
|
13209
|
+
stale,
|
|
13210
|
+
builtAt: snapshot.builtAt,
|
|
13211
|
+
entries: snapshot.entries.length,
|
|
13212
|
+
warnings: snapshot.warnings
|
|
13213
|
+
};
|
|
13214
|
+
if (options.json) {
|
|
13215
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
13216
|
+
return;
|
|
13217
|
+
}
|
|
13218
|
+
console.log(`Index: ${indexPath2}`);
|
|
13219
|
+
console.log(`Built: ${snapshot.builtAt}`);
|
|
13220
|
+
console.log(`Entries: ${snapshot.entries.length}`);
|
|
13221
|
+
console.log(`Stale: ${stale ? "yes" : "no"}`);
|
|
13222
|
+
if (snapshot.warnings.length > 0) {
|
|
13223
|
+
for (const warning of snapshot.warnings) {
|
|
13224
|
+
console.log(`warning: ${warning}`);
|
|
13225
|
+
}
|
|
13226
|
+
}
|
|
13227
|
+
} catch (e) {
|
|
13228
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
13229
|
+
console.error(`[AgentInspect] index status failed: ${msg}`);
|
|
13230
|
+
process.exitCode = 1;
|
|
12645
13231
|
}
|
|
12646
13232
|
}
|
|
12647
|
-
async function
|
|
12648
|
-
let raw;
|
|
13233
|
+
async function indexCleanCommand(options = {}) {
|
|
12649
13234
|
try {
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
13235
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
13236
|
+
const indexPath2 = traceIndexPath(traceDir);
|
|
13237
|
+
await rm(indexPath2, { force: true });
|
|
13238
|
+
if (options.json) {
|
|
13239
|
+
console.log(JSON.stringify({ ok: true, removed: indexPath2 }, null, 2));
|
|
13240
|
+
return;
|
|
13241
|
+
}
|
|
13242
|
+
console.log(`Removed index: ${indexPath2}`);
|
|
13243
|
+
} catch (e) {
|
|
13244
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
13245
|
+
console.error(`[AgentInspect] index clean failed: ${msg}`);
|
|
13246
|
+
process.exitCode = 1;
|
|
12653
13247
|
}
|
|
12654
|
-
const parsed = parseWorkspaceManifest(raw);
|
|
12655
|
-
return {
|
|
12656
|
-
exists: true,
|
|
12657
|
-
ok: parsed.ok,
|
|
12658
|
-
...parsed.manifest ? { manifest: parsed.manifest } : {},
|
|
12659
|
-
errors: parsed.errors,
|
|
12660
|
-
warnings: parsed.warnings
|
|
12661
|
-
};
|
|
12662
13248
|
}
|
|
12663
|
-
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
const existing = await readWorkspaceManifestFile(location);
|
|
12667
|
-
const topLevelTraces = await listJsonl(location.workspaceDir);
|
|
12668
|
-
const detectedExistingTraces = topLevelTraces.length > 0;
|
|
12669
|
-
let manifest;
|
|
12670
|
-
let created;
|
|
12671
|
-
let adopted;
|
|
12672
|
-
if (existing.exists && existing.ok && existing.manifest) {
|
|
12673
|
-
manifest = existing.manifest;
|
|
12674
|
-
created = false;
|
|
12675
|
-
adopted = true;
|
|
12676
|
-
} else {
|
|
12677
|
-
const project = options.project?.trim() || path11.basename(location.projectRoot) || "workspace";
|
|
12678
|
-
const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
|
|
12679
|
-
manifest = createDefaultWorkspaceManifest({
|
|
12680
|
-
project,
|
|
12681
|
-
traceDirs,
|
|
12682
|
-
...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {}
|
|
12683
|
-
});
|
|
12684
|
-
created = true;
|
|
12685
|
-
adopted = detectedExistingTraces || existing.exists && !existing.ok;
|
|
12686
|
-
}
|
|
12687
|
-
const relDirs = uniqueDirs([
|
|
12688
|
-
...manifest.traceDirs.filter((d) => d !== "."),
|
|
12689
|
-
manifest.reportsDir,
|
|
12690
|
-
manifest.artifactsDir,
|
|
12691
|
-
manifest.bundlesDir,
|
|
12692
|
-
manifest.notesDir,
|
|
12693
|
-
INDEX_DIR_NAME
|
|
12694
|
-
]);
|
|
12695
|
-
const createdDirs = [];
|
|
12696
|
-
for (const rel of relDirs) {
|
|
12697
|
-
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
12698
|
-
if (await pathExists(abs)) continue;
|
|
12699
|
-
createdDirs.push(rel);
|
|
12700
|
-
if (!dryRun) await mkdir(abs, { recursive: true });
|
|
12701
|
-
}
|
|
12702
|
-
if (!dryRun && created) {
|
|
12703
|
-
await mkdir(location.workspaceDir, { recursive: true });
|
|
12704
|
-
await writeFile(location.manifestPath, serializeWorkspaceManifest(manifest), "utf-8");
|
|
12705
|
-
}
|
|
12706
|
-
return {
|
|
12707
|
-
location,
|
|
12708
|
-
manifest,
|
|
12709
|
-
created,
|
|
12710
|
-
adopted,
|
|
12711
|
-
createdDirs,
|
|
12712
|
-
detectedExistingTraces,
|
|
12713
|
-
dryRun
|
|
12714
|
-
};
|
|
13249
|
+
var PACKAGE = "@agent-inspect/index-sqlite";
|
|
13250
|
+
function isModuleNotFound3(e) {
|
|
13251
|
+
return e !== null && typeof e === "object" && "code" in e && (e.code === "ERR_MODULE_NOT_FOUND" || e.code === "MODULE_NOT_FOUND");
|
|
12715
13252
|
}
|
|
12716
|
-
function
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
|
|
13253
|
+
async function loadIndexSqlite() {
|
|
13254
|
+
try {
|
|
13255
|
+
return await import('./src-FJOHPC4X.mjs');
|
|
13256
|
+
} catch (e) {
|
|
13257
|
+
if (isModuleNotFound3(e)) {
|
|
13258
|
+
console.error(
|
|
13259
|
+
`The optional SQLite index is not installed. Run: npm install ${PACKAGE}`
|
|
13260
|
+
);
|
|
13261
|
+
process.exitCode = 1;
|
|
13262
|
+
return null;
|
|
13263
|
+
}
|
|
13264
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
13265
|
+
console.error(`[AgentInspect] failed to load ${PACKAGE}: ${msg}`);
|
|
13266
|
+
process.exitCode = 1;
|
|
13267
|
+
return null;
|
|
12724
13268
|
}
|
|
12725
|
-
return out;
|
|
12726
13269
|
}
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
13270
|
+
function parsePositiveInt(raw, flag) {
|
|
13271
|
+
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
13272
|
+
const parsed = Number.parseInt(raw, 10);
|
|
13273
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
13274
|
+
throw new Error(`${flag} must be a positive integer.`);
|
|
12732
13275
|
}
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
return {
|
|
12747
|
-
project: manifest.project,
|
|
12748
|
-
traceFiles,
|
|
12749
|
-
reports,
|
|
12750
|
-
artifacts,
|
|
12751
|
-
bundles,
|
|
12752
|
-
notes,
|
|
12753
|
-
index: {
|
|
12754
|
-
enabled: manifest.index.enabled,
|
|
12755
|
-
type: manifest.index.type,
|
|
12756
|
-
exists: await pathExists(indexPath2)
|
|
13276
|
+
return parsed;
|
|
13277
|
+
}
|
|
13278
|
+
async function newestTraceMtimeMs2(traceDir) {
|
|
13279
|
+
let newest = 0;
|
|
13280
|
+
try {
|
|
13281
|
+
const files = await readdir(traceDir);
|
|
13282
|
+
for (const file of files) {
|
|
13283
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
13284
|
+
try {
|
|
13285
|
+
const s = await stat(path13.join(traceDir, file));
|
|
13286
|
+
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
13287
|
+
} catch {
|
|
13288
|
+
}
|
|
12757
13289
|
}
|
|
12758
|
-
}
|
|
13290
|
+
} catch {
|
|
13291
|
+
}
|
|
13292
|
+
return newest;
|
|
12759
13293
|
}
|
|
12760
|
-
async function
|
|
12761
|
-
const
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
});
|
|
12769
|
-
return
|
|
13294
|
+
async function indexSqliteBuildCommand(options = {}) {
|
|
13295
|
+
const mod = await loadIndexSqlite();
|
|
13296
|
+
if (!mod) return;
|
|
13297
|
+
const result = await mod.buildIndex({
|
|
13298
|
+
traceDir: options.dir,
|
|
13299
|
+
maxRuns: parsePositiveInt(options.maxRuns, "--max-runs")
|
|
13300
|
+
});
|
|
13301
|
+
if (options.json) {
|
|
13302
|
+
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
13303
|
+
return;
|
|
12770
13304
|
}
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
13305
|
+
console.log(`Built SQLite index: ${result.dbPath}`);
|
|
13306
|
+
console.log(`Runs: ${result.runs} Steps: ${result.steps} Errors: ${result.errors}`);
|
|
13307
|
+
for (const warning of result.warnings) console.log(`warning: ${warning}`);
|
|
13308
|
+
}
|
|
13309
|
+
async function indexSqliteStatusCommand(options = {}) {
|
|
13310
|
+
const mod = await loadIndexSqlite();
|
|
13311
|
+
if (!mod) return;
|
|
13312
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
13313
|
+
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
13314
|
+
const status = mod.indexStatus(dbPath);
|
|
13315
|
+
const stale = mod.isIndexStale(dbPath, await newestTraceMtimeMs2(traceDir));
|
|
13316
|
+
if (options.json) {
|
|
13317
|
+
console.log(JSON.stringify({ ok: true, traceDir, stale, ...status }, null, 2));
|
|
13318
|
+
return;
|
|
12778
13319
|
}
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
13320
|
+
if (!status.exists) {
|
|
13321
|
+
console.log(`No SQLite index at ${dbPath}`);
|
|
13322
|
+
console.log("Run: agent-inspect index sqlite build");
|
|
13323
|
+
return;
|
|
12783
13324
|
}
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
message: error instanceof Error ? error.message : String(error)
|
|
12800
|
-
});
|
|
12801
|
-
continue;
|
|
12802
|
-
}
|
|
12803
|
-
if (!await pathExists(abs)) {
|
|
12804
|
-
checks2.push({ id, status: "warn", message: `${rel}/ does not exist yet` });
|
|
12805
|
-
} else if (!await isWritable(abs)) {
|
|
12806
|
-
checks2.push({ id, status: "fail", message: `${rel}/ is not writable` });
|
|
13325
|
+
console.log(`Index: ${status.dbPath}`);
|
|
13326
|
+
console.log(`Healthy: ${status.healthy ? "yes" : "no"}`);
|
|
13327
|
+
console.log(`Built: ${status.builtAt ?? "unknown"}`);
|
|
13328
|
+
console.log(`Runs: ${status.runs} Steps: ${status.steps}`);
|
|
13329
|
+
console.log(`Stale: ${stale ? "yes" : "no"}`);
|
|
13330
|
+
}
|
|
13331
|
+
async function indexSqliteQueryCommand(options = {}) {
|
|
13332
|
+
const mod = await loadIndexSqlite();
|
|
13333
|
+
if (!mod) return;
|
|
13334
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
13335
|
+
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
13336
|
+
const status = mod.indexStatus(dbPath);
|
|
13337
|
+
if (!status.exists || !status.healthy) {
|
|
13338
|
+
if (options.json) {
|
|
13339
|
+
console.log(JSON.stringify({ ok: false, reason: "index-missing", dbPath }, null, 2));
|
|
12807
13340
|
} else {
|
|
12808
|
-
|
|
13341
|
+
console.log("No usable SQLite index. Run: agent-inspect index sqlite build");
|
|
12809
13342
|
}
|
|
13343
|
+
process.exitCode = 1;
|
|
13344
|
+
return;
|
|
12810
13345
|
}
|
|
12811
|
-
|
|
12812
|
-
|
|
12813
|
-
|
|
12814
|
-
|
|
12815
|
-
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
13346
|
+
const rows = mod.queryRuns(dbPath, {
|
|
13347
|
+
status: options.status,
|
|
13348
|
+
sessionId: options.session,
|
|
13349
|
+
name: options.name,
|
|
13350
|
+
kind: options.kind,
|
|
13351
|
+
tool: options.tool,
|
|
13352
|
+
limit: parsePositiveInt(options.limit, "--limit")
|
|
13353
|
+
});
|
|
13354
|
+
if (options.json) {
|
|
13355
|
+
console.log(JSON.stringify({ ok: true, count: rows.length, runs: rows }, null, 2));
|
|
13356
|
+
return;
|
|
12822
13357
|
}
|
|
12823
|
-
if (
|
|
12824
|
-
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
} catch {
|
|
12836
|
-
checks2.push({ id: "index", status: "warn", message: "cannot stat index" });
|
|
12837
|
-
}
|
|
12838
|
-
}
|
|
13358
|
+
if (rows.length === 0) {
|
|
13359
|
+
console.log("No matching runs.");
|
|
13360
|
+
return;
|
|
13361
|
+
}
|
|
13362
|
+
for (const run of rows) {
|
|
13363
|
+
const parts = [
|
|
13364
|
+
run.runId,
|
|
13365
|
+
run.status ?? "unknown",
|
|
13366
|
+
run.name ?? "",
|
|
13367
|
+
run.durationMs != null ? `${run.durationMs}ms` : ""
|
|
13368
|
+
].filter((p) => p !== "");
|
|
13369
|
+
console.log(parts.join(" "));
|
|
12839
13370
|
}
|
|
12840
|
-
const ok = !checks2.some((c) => c.status === "fail");
|
|
12841
|
-
return { ok, checks: checks2 };
|
|
12842
13371
|
}
|
|
12843
|
-
async function
|
|
12844
|
-
const
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
for (const rel of targets) {
|
|
12853
|
-
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
12854
|
-
let entries;
|
|
12855
|
-
try {
|
|
12856
|
-
entries = await readdir(abs);
|
|
12857
|
-
} catch {
|
|
12858
|
-
continue;
|
|
12859
|
-
}
|
|
12860
|
-
for (const entry of entries) {
|
|
12861
|
-
const relPath = `${rel}/${entry}`;
|
|
12862
|
-
removed.push(relPath);
|
|
12863
|
-
if (!dryRun) {
|
|
12864
|
-
await rm(path11.join(abs, entry), { recursive: true, force: true });
|
|
12865
|
-
}
|
|
12866
|
-
}
|
|
13372
|
+
async function indexSqliteCleanCommand(options = {}) {
|
|
13373
|
+
const mod = await loadIndexSqlite();
|
|
13374
|
+
if (!mod) return;
|
|
13375
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
13376
|
+
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
13377
|
+
await mod.cleanIndex(dbPath);
|
|
13378
|
+
if (options.json) {
|
|
13379
|
+
console.log(JSON.stringify({ ok: true, removed: dbPath }, null, 2));
|
|
13380
|
+
return;
|
|
12867
13381
|
}
|
|
12868
|
-
|
|
13382
|
+
console.log(`Removed SQLite index: ${dbPath}`);
|
|
12869
13383
|
}
|
|
12870
13384
|
|
|
12871
13385
|
// packages/cli/src/workspace.ts
|
|
@@ -13143,7 +13657,10 @@ function createCliProgram() {
|
|
|
13143
13657
|
]).option("--max-total-tokens <number>", "add llm.usage with a max total-token budget").option(
|
|
13144
13658
|
"--max-step-duration <duration>",
|
|
13145
13659
|
"add run.maxStepDuration (e.g. 30s, 5m)"
|
|
13146
|
-
).option("--require-completed", "add run.requireCompleted").option("--detect-stalls", "add run.stall for running or incomplete events").option(
|
|
13660
|
+
).option("--require-completed", "add run.requireCompleted").option("--detect-stalls", "add run.stall for running or incomplete events").option(
|
|
13661
|
+
"--fail-on-observation <status>",
|
|
13662
|
+
"fail when observed outcomes match status (passed, failed, unknown, skipped; comma-separated)"
|
|
13663
|
+
).option("--session <id>", "check all runs in a workflow session (requires --dir)").option("--group <id>", "check all runs sharing a groupId (requires --dir)").option(
|
|
13147
13664
|
"--correlate-group",
|
|
13148
13665
|
"when using --session, also match synthetic group: session keys"
|
|
13149
13666
|
).option(
|
|
@@ -13217,6 +13734,11 @@ function createCliProgram() {
|
|
|
13217
13734
|
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--baseline <trace-path-or-run-id>", "optional baseline trace for diff artifacts").option("--baseline-run <run-id>", "select a run from the baseline trace").option("--github-summary <path>", "append a safe summary to this file, e.g. GITHUB_STEP_SUMMARY").option("--json", "print deterministic JSON manifest").action((target, opts) => {
|
|
13218
13735
|
runCommand(() => artifactsCommand(target, opts));
|
|
13219
13736
|
});
|
|
13737
|
+
program.command("bundle").description("Create a share-safe offline trace bundle (local folder)").argument("[run-id]", "run id to bundle (optional with --session or --since)").option("--dir <path>", "trace directory for run/session lookup").option("--session <session-id>", "bundle all runs in a session").option("--since <duration>", "bundle runs with activity since a duration (e.g. 24h)").addOption(
|
|
13738
|
+
new Option("--profile <profile>", "redaction profile for exported copies").choices(["local", "share", "strict"]).default("share")
|
|
13739
|
+
).option("--out <path>", "output directory (folder; .zip suffix is stripped)").option("--allow-unsafe", "write bundle even when verify-safe reports UNSAFE").option("--json", "print deterministic JSON manifest").action((runId, opts) => {
|
|
13740
|
+
runCommand(() => bundleCommand(runId, opts));
|
|
13741
|
+
});
|
|
13220
13742
|
program.command("ci-summary").description("Summarize local reporter artifact manifests for CI").argument("<manifest...>", "reporter artifact manifest JSON files").option("-o, --output <path>", "write Markdown summary to a local file").option("--github-summary <path>", "append Markdown summary to this local file, e.g. GITHUB_STEP_SUMMARY").option("--json", "print deterministic JSON summary").action((manifest, opts) => {
|
|
13221
13743
|
runCommand(() => ciSummaryCommand(manifest, opts));
|
|
13222
13744
|
});
|
|
@@ -13259,6 +13781,9 @@ function createCliProgram() {
|
|
|
13259
13781
|
"unknown"
|
|
13260
13782
|
])
|
|
13261
13783
|
).option("--kind <kind>", "filter by step kind/type (llm, tool, logic, \u2026)").option("--type <type>", "alias for --kind on manual trace step type").option("--name <query>", "substring match on run or step name").option("--tool <query>", "substring match on tool step name or metadata.toolName").option(
|
|
13784
|
+
"--observation <status>",
|
|
13785
|
+
"filter by observed outcome status (passed, failed, unknown, skipped)"
|
|
13786
|
+
).option(
|
|
13262
13787
|
"--duration <expr>",
|
|
13263
13788
|
"duration filter on run or step (e.g. >5s, >=500ms)"
|
|
13264
13789
|
).option("--limit <number>", "max results (default 50)").option("--session <id>", "limit search to one workflow session").option(
|
|
@@ -13305,6 +13830,14 @@ function createCliProgram() {
|
|
|
13305
13830
|
"html"
|
|
13306
13831
|
])
|
|
13307
13832
|
).option("-o, --output <path>", "write report to file (creates parent dirs)").option("--json", "emit JSON wrapper (includes content when writing to stdout)").option("--include-attributes", "include bounded step attributes in tree section").option("--no-errors", "omit error details from tree section").option("--no-correlation", "omit correlation metadata from what section").addOption(
|
|
13833
|
+
new Option("--section <section>", "report section (default: all)").choices([
|
|
13834
|
+
"all",
|
|
13835
|
+
"observations",
|
|
13836
|
+
"what",
|
|
13837
|
+
"timeline",
|
|
13838
|
+
"tree"
|
|
13839
|
+
])
|
|
13840
|
+
).addOption(
|
|
13308
13841
|
new Option(
|
|
13309
13842
|
"--redaction-profile <profile>",
|
|
13310
13843
|
"redaction profile for entire report: local, share, strict (default: local)"
|
|
@@ -13421,9 +13954,9 @@ function isPrimaryModule() {
|
|
|
13421
13954
|
if (!entry) return false;
|
|
13422
13955
|
const selfPath = fileURLToPath(import.meta.url);
|
|
13423
13956
|
try {
|
|
13424
|
-
return realpathSync(
|
|
13957
|
+
return realpathSync(path13.resolve(entry)) === realpathSync(path13.resolve(selfPath));
|
|
13425
13958
|
} catch {
|
|
13426
|
-
return
|
|
13959
|
+
return path13.resolve(entry) === path13.resolve(selfPath);
|
|
13427
13960
|
}
|
|
13428
13961
|
}
|
|
13429
13962
|
if (isPrimaryModule()) {
|