agent-inspect 1.8.0 → 1.9.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 +8 -0
- package/README.md +137 -18
- package/docs/ADAPTERS.md +41 -6
- package/docs/API.md +98 -12
- package/docs/CLI.md +35 -1
- package/docs/GETTING-STARTED.md +71 -16
- package/docs/LOG-TO-TREE-QUICKSTART.md +1 -2
- package/docs/MIGRATION.md +67 -0
- package/package.json +2 -2
- package/packages/cli/dist/index.cjs +338 -48
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +338 -48
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/index.cjs +146 -0
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.d.cts +39 -2
- package/packages/core/dist/index.d.ts +39 -2
- package/packages/core/dist/index.mjs +148 -1
- package/packages/core/dist/index.mjs.map +1 -1
|
@@ -12,7 +12,7 @@ import process2, { stdin, stdout } from 'process';
|
|
|
12
12
|
import tty from 'tty';
|
|
13
13
|
|
|
14
14
|
// package.json
|
|
15
|
-
var version = "1.
|
|
15
|
+
var version = "1.9.0";
|
|
16
16
|
|
|
17
17
|
// packages/core/src/types.ts
|
|
18
18
|
var STEP_TYPES = [
|
|
@@ -4058,6 +4058,151 @@ ${attrsSection}
|
|
|
4058
4058
|
};
|
|
4059
4059
|
}
|
|
4060
4060
|
|
|
4061
|
+
// packages/core/src/explain.ts
|
|
4062
|
+
function flatten(nodes, out = []) {
|
|
4063
|
+
for (const node of nodes) {
|
|
4064
|
+
out.push({ node, index: out.length + 1 });
|
|
4065
|
+
flatten(node.children, out);
|
|
4066
|
+
}
|
|
4067
|
+
return out;
|
|
4068
|
+
}
|
|
4069
|
+
function redactValue(redactor, key, value) {
|
|
4070
|
+
return redactor.redactValue(key, value);
|
|
4071
|
+
}
|
|
4072
|
+
function fact(id, label, value, redactor) {
|
|
4073
|
+
return {
|
|
4074
|
+
id,
|
|
4075
|
+
label,
|
|
4076
|
+
value: redactValue(redactor, id.split(".").at(-1) ?? id, value),
|
|
4077
|
+
source: "trace",
|
|
4078
|
+
confidence: "observed"
|
|
4079
|
+
};
|
|
4080
|
+
}
|
|
4081
|
+
function topKinds(run) {
|
|
4082
|
+
return Object.entries(run.metadata.kinds).filter(([, count]) => count > 0).sort((a, b) => {
|
|
4083
|
+
if (b[1] !== a[1]) return b[1] - a[1];
|
|
4084
|
+
return a[0].localeCompare(b[0]);
|
|
4085
|
+
}).slice(0, 5).map(([kind, count]) => `${kind}:${count}`);
|
|
4086
|
+
}
|
|
4087
|
+
function countErrorNodes(nodes) {
|
|
4088
|
+
return nodes.filter((entry) => entry.node.event.status === "error").length;
|
|
4089
|
+
}
|
|
4090
|
+
function slowestNode(nodes) {
|
|
4091
|
+
return nodes.filter((entry) => entry.node.event.durationMs !== void 0).sort((a, b) => {
|
|
4092
|
+
const delta = (b.node.event.durationMs ?? 0) - (a.node.event.durationMs ?? 0);
|
|
4093
|
+
return delta !== 0 ? delta : a.index - b.index;
|
|
4094
|
+
})[0];
|
|
4095
|
+
}
|
|
4096
|
+
function attributeFacts(nodes, redactor) {
|
|
4097
|
+
const facts = [];
|
|
4098
|
+
for (const entry of nodes) {
|
|
4099
|
+
const attrs = entry.node.event.attributes;
|
|
4100
|
+
if (attrs === void 0) continue;
|
|
4101
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
4102
|
+
facts.push({
|
|
4103
|
+
id: `node.${entry.index}.attributes.${key}`,
|
|
4104
|
+
label: `${entry.node.event.name} attribute ${key}`,
|
|
4105
|
+
value: redactValue(redactor, key, attrs[key]),
|
|
4106
|
+
source: "trace",
|
|
4107
|
+
confidence: "observed"
|
|
4108
|
+
});
|
|
4109
|
+
if (facts.length >= 8) return facts;
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
return facts;
|
|
4113
|
+
}
|
|
4114
|
+
function buildFacts(run, redactor) {
|
|
4115
|
+
const nodes = flatten(run.children);
|
|
4116
|
+
const facts = [
|
|
4117
|
+
fact("run.id", "Run id", run.runId, redactor),
|
|
4118
|
+
fact("run.name", "Run name", run.name ?? run.runId, redactor),
|
|
4119
|
+
fact("run.status", "Run status", run.status ?? "unknown", redactor),
|
|
4120
|
+
fact("run.totalEvents", "Total events", run.metadata.totalEvents, redactor),
|
|
4121
|
+
fact("run.stepCount", "Top-level step count", run.children.length, redactor),
|
|
4122
|
+
fact("run.nodeCount", "Total node count", nodes.length, redactor),
|
|
4123
|
+
fact("run.errorNodeCount", "Error node count", countErrorNodes(nodes), redactor),
|
|
4124
|
+
fact("run.kinds", "Observed kind mix", topKinds(run), redactor)
|
|
4125
|
+
];
|
|
4126
|
+
if (run.durationMs !== void 0) {
|
|
4127
|
+
facts.push(fact("run.durationMs", "Run duration milliseconds", run.durationMs, redactor));
|
|
4128
|
+
}
|
|
4129
|
+
const slowest = slowestNode(nodes);
|
|
4130
|
+
if (slowest !== void 0) {
|
|
4131
|
+
facts.push(
|
|
4132
|
+
fact("run.slowestNode", "Slowest observed node", {
|
|
4133
|
+
name: slowest.node.event.name,
|
|
4134
|
+
kind: slowest.node.event.kind,
|
|
4135
|
+
durationMs: slowest.node.event.durationMs
|
|
4136
|
+
}, redactor)
|
|
4137
|
+
);
|
|
4138
|
+
}
|
|
4139
|
+
facts.push(...attributeFacts(nodes, redactor));
|
|
4140
|
+
return facts;
|
|
4141
|
+
}
|
|
4142
|
+
function buildInferences(run, facts) {
|
|
4143
|
+
const inferences = [];
|
|
4144
|
+
const errorFact = facts.find((item) => item.id === "run.errorNodeCount");
|
|
4145
|
+
const kindFact = facts.find((item) => item.id === "run.kinds");
|
|
4146
|
+
const durationFact = facts.find((item) => item.id === "run.durationMs");
|
|
4147
|
+
const errorNodeCount = typeof errorFact?.value === "number" ? errorFact.value : 0;
|
|
4148
|
+
if (run.status === "error" || errorNodeCount > 0) {
|
|
4149
|
+
inferences.push({
|
|
4150
|
+
id: "outcome.error",
|
|
4151
|
+
label: "Outcome",
|
|
4152
|
+
text: "The run recorded an error status or at least one error node.",
|
|
4153
|
+
evidence: ["run.status", "run.errorNodeCount"],
|
|
4154
|
+
confidence: "deterministic"
|
|
4155
|
+
});
|
|
4156
|
+
} else if (run.status === "ok") {
|
|
4157
|
+
inferences.push({
|
|
4158
|
+
id: "outcome.success",
|
|
4159
|
+
label: "Outcome",
|
|
4160
|
+
text: "The run completed without observed error nodes.",
|
|
4161
|
+
evidence: ["run.status", "run.errorNodeCount"],
|
|
4162
|
+
confidence: "deterministic"
|
|
4163
|
+
});
|
|
4164
|
+
}
|
|
4165
|
+
if (kindFact !== void 0) {
|
|
4166
|
+
inferences.push({
|
|
4167
|
+
id: "shape.kind-mix",
|
|
4168
|
+
label: "Trace shape",
|
|
4169
|
+
text: "The explanation is based on the observed event kind mix, not generated content.",
|
|
4170
|
+
evidence: [kindFact.id],
|
|
4171
|
+
confidence: "deterministic"
|
|
4172
|
+
});
|
|
4173
|
+
}
|
|
4174
|
+
if (durationFact !== void 0) {
|
|
4175
|
+
inferences.push({
|
|
4176
|
+
id: "timing.duration",
|
|
4177
|
+
label: "Timing",
|
|
4178
|
+
text: "Timing claims are limited to persisted duration fields in the trace.",
|
|
4179
|
+
evidence: [durationFact.id],
|
|
4180
|
+
confidence: "deterministic"
|
|
4181
|
+
});
|
|
4182
|
+
}
|
|
4183
|
+
return inferences;
|
|
4184
|
+
}
|
|
4185
|
+
function buildLocalExplanation(run, options = {}) {
|
|
4186
|
+
const redactionProfile = options.redactionProfile ?? "local";
|
|
4187
|
+
const resolved = resolveRedactionProfile(redactionProfile);
|
|
4188
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
4189
|
+
const mode = options.mode ?? "local";
|
|
4190
|
+
const facts = buildFacts(run, redactor);
|
|
4191
|
+
return {
|
|
4192
|
+
mode,
|
|
4193
|
+
runId: String(redactValue(redactor, "runId", run.runId)),
|
|
4194
|
+
...run.name !== void 0 ? { name: String(redactValue(redactor, "name", run.name)) } : {},
|
|
4195
|
+
...run.status !== void 0 ? { status: run.status } : {},
|
|
4196
|
+
redactionProfile,
|
|
4197
|
+
facts,
|
|
4198
|
+
inferences: mode === "dry-run" ? [] : buildInferences(run, facts),
|
|
4199
|
+
notes: [
|
|
4200
|
+
"Generated locally without provider or network calls.",
|
|
4201
|
+
"Facts are observed from normalized trace data; inferences are deterministic labels."
|
|
4202
|
+
]
|
|
4203
|
+
};
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4061
4206
|
// packages/core/src/stats.ts
|
|
4062
4207
|
function percentile(sorted, p) {
|
|
4063
4208
|
if (sorted.length === 0) return void 0;
|
|
@@ -8947,9 +9092,164 @@ async function readStdin(stdin) {
|
|
|
8947
9092
|
}
|
|
8948
9093
|
return content;
|
|
8949
9094
|
}
|
|
9095
|
+
function isMissingFileError2(error) {
|
|
9096
|
+
return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
9097
|
+
}
|
|
9098
|
+
async function inputFromTarget(target, options, stdin) {
|
|
9099
|
+
if (target === "-") {
|
|
9100
|
+
return { type: "string", content: await readStdin(stdin) };
|
|
9101
|
+
}
|
|
9102
|
+
try {
|
|
9103
|
+
const stats2 = await stat(target);
|
|
9104
|
+
if (stats2.isDirectory()) return { type: "directory", path: target };
|
|
9105
|
+
return { type: "file", path: target };
|
|
9106
|
+
} catch (error) {
|
|
9107
|
+
if (!isMissingFileError2(error)) throw error;
|
|
9108
|
+
}
|
|
9109
|
+
const runPath = getTraceFilePath(target, resolveTraceDir({ dir: options.dir }));
|
|
9110
|
+
const stats = await stat(runPath);
|
|
9111
|
+
if (stats.isDirectory()) return { type: "directory", path: runPath };
|
|
9112
|
+
return { type: "file", path: runPath };
|
|
9113
|
+
}
|
|
9114
|
+
|
|
9115
|
+
// packages/cli/src/explain.ts
|
|
9116
|
+
function parseRedactionProfile3(value) {
|
|
9117
|
+
const profile = (value ?? "local").trim().toLowerCase();
|
|
9118
|
+
if (profile === "local" || profile === "share" || profile === "strict") {
|
|
9119
|
+
return profile;
|
|
9120
|
+
}
|
|
9121
|
+
throw new Error(
|
|
9122
|
+
`Unsupported --redaction-profile "${value ?? ""}". Use local, share, or strict.`
|
|
9123
|
+
);
|
|
9124
|
+
}
|
|
9125
|
+
function selectRun(result, runId) {
|
|
9126
|
+
if (runId !== void 0) {
|
|
9127
|
+
return result.runs.find((run) => run.runId === runId);
|
|
9128
|
+
}
|
|
9129
|
+
return result.runs.length === 1 ? result.runs[0] : void 0;
|
|
9130
|
+
}
|
|
9131
|
+
function printMultipleRuns(result) {
|
|
9132
|
+
console.error(
|
|
9133
|
+
`Trace contains ${result.runs.length} runs. Re-run with --run <run-id>.`
|
|
9134
|
+
);
|
|
9135
|
+
for (const run of result.runs) {
|
|
9136
|
+
console.error(`- ${run.runId}${run.name !== void 0 ? ` name=${run.name}` : ""}`);
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9139
|
+
function renderHuman(result) {
|
|
9140
|
+
const lines = [
|
|
9141
|
+
`Explain: ${result.name ?? result.runId}`,
|
|
9142
|
+
`Mode: ${result.mode}`,
|
|
9143
|
+
`Status: ${result.status ?? "unknown"}`,
|
|
9144
|
+
`Redaction: ${result.redactionProfile}`,
|
|
9145
|
+
"",
|
|
9146
|
+
"Facts:"
|
|
9147
|
+
];
|
|
9148
|
+
for (const fact2 of result.facts) {
|
|
9149
|
+
lines.push(`- ${fact2.id}: ${JSON.stringify(fact2.value)}`);
|
|
9150
|
+
}
|
|
9151
|
+
lines.push("", "Inferences:");
|
|
9152
|
+
if (result.inferences.length === 0) {
|
|
9153
|
+
lines.push("- none");
|
|
9154
|
+
} else {
|
|
9155
|
+
for (const inference of result.inferences) {
|
|
9156
|
+
lines.push(`- ${inference.label}: ${inference.text}`);
|
|
9157
|
+
}
|
|
9158
|
+
}
|
|
9159
|
+
lines.push("", "Notes:");
|
|
9160
|
+
for (const note of result.notes) {
|
|
9161
|
+
lines.push(`- ${note}`);
|
|
9162
|
+
}
|
|
9163
|
+
return lines.join("\n");
|
|
9164
|
+
}
|
|
9165
|
+
function writeJson(result) {
|
|
9166
|
+
console.log(JSON.stringify(result, null, 2));
|
|
9167
|
+
}
|
|
9168
|
+
function rejectProvider(provider, json) {
|
|
9169
|
+
process.exitCode = 1;
|
|
9170
|
+
const message = `Provider explain is not implemented in this build: ${provider}. Use --dry-run to inspect the redacted local payload.`;
|
|
9171
|
+
if (json) {
|
|
9172
|
+
writeJson({
|
|
9173
|
+
ok: false,
|
|
9174
|
+
error: { code: "PROVIDER_NOT_IMPLEMENTED", message }
|
|
9175
|
+
});
|
|
9176
|
+
return;
|
|
9177
|
+
}
|
|
9178
|
+
console.error(message);
|
|
9179
|
+
}
|
|
9180
|
+
async function explainCommand(target, options = {}, stdin = process.stdin) {
|
|
9181
|
+
if (options.provider !== void 0) {
|
|
9182
|
+
rejectProvider(options.provider, options.json);
|
|
9183
|
+
return;
|
|
9184
|
+
}
|
|
9185
|
+
let redactionProfile;
|
|
9186
|
+
try {
|
|
9187
|
+
redactionProfile = parseRedactionProfile3(options.redactionProfile);
|
|
9188
|
+
} catch (error) {
|
|
9189
|
+
process.exitCode = 1;
|
|
9190
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
9191
|
+
return;
|
|
9192
|
+
}
|
|
9193
|
+
try {
|
|
9194
|
+
const input3 = await inputFromTarget(target, options, stdin);
|
|
9195
|
+
const read = await openTrace(input3, {
|
|
9196
|
+
...options.format !== void 0 ? { format: options.format } : {}
|
|
9197
|
+
});
|
|
9198
|
+
const selected = selectRun(read, options.run);
|
|
9199
|
+
if (selected === void 0) {
|
|
9200
|
+
process.exitCode = 1;
|
|
9201
|
+
const message = options.run !== void 0 ? `Run not found: ${options.run}` : `Trace contains ${read.runs.length} runs. Specify --run <run-id>.`;
|
|
9202
|
+
if (options.json) {
|
|
9203
|
+
writeJson({ ok: false, error: { message }, runs: read.runs });
|
|
9204
|
+
} else if (options.run !== void 0) {
|
|
9205
|
+
console.error(message);
|
|
9206
|
+
} else {
|
|
9207
|
+
printMultipleRuns(read);
|
|
9208
|
+
}
|
|
9209
|
+
return;
|
|
9210
|
+
}
|
|
9211
|
+
const mode = options.dryRun ? "dry-run" : "local";
|
|
9212
|
+
const explanation = buildLocalExplanation(selected, {
|
|
9213
|
+
mode,
|
|
9214
|
+
redactionProfile
|
|
9215
|
+
});
|
|
9216
|
+
if (options.json) {
|
|
9217
|
+
writeJson({
|
|
9218
|
+
ok: true,
|
|
9219
|
+
format: read.format,
|
|
9220
|
+
sourceFiles: read.sourceFiles,
|
|
9221
|
+
warnings: read.warnings,
|
|
9222
|
+
unsupportedFields: read.unsupportedFields,
|
|
9223
|
+
explanation
|
|
9224
|
+
});
|
|
9225
|
+
return;
|
|
9226
|
+
}
|
|
9227
|
+
console.log(renderHuman(explanation));
|
|
9228
|
+
} catch (error) {
|
|
9229
|
+
process.exitCode = 1;
|
|
9230
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9231
|
+
const code = error instanceof TraceReadError ? error.code : void 0;
|
|
9232
|
+
if (options.json) {
|
|
9233
|
+
writeJson({
|
|
9234
|
+
ok: false,
|
|
9235
|
+
error: { ...code !== void 0 ? { code } : {}, message }
|
|
9236
|
+
});
|
|
9237
|
+
return;
|
|
9238
|
+
}
|
|
9239
|
+
console.error(message);
|
|
9240
|
+
}
|
|
9241
|
+
}
|
|
9242
|
+
async function readStdin2(stdin) {
|
|
9243
|
+
stdin.setEncoding("utf8");
|
|
9244
|
+
let content = "";
|
|
9245
|
+
for await (const chunk of stdin) {
|
|
9246
|
+
content += typeof chunk === "string" ? chunk : String(chunk);
|
|
9247
|
+
}
|
|
9248
|
+
return content;
|
|
9249
|
+
}
|
|
8950
9250
|
async function inputFromPathOrStdin(input3, stdin) {
|
|
8951
9251
|
if (input3 === void 0 || input3 === "-") {
|
|
8952
|
-
return { type: "string", content: await
|
|
9252
|
+
return { type: "string", content: await readStdin2(stdin) };
|
|
8953
9253
|
}
|
|
8954
9254
|
const stats = await stat(input3);
|
|
8955
9255
|
if (stats.isDirectory()) return { type: "directory", path: input3 };
|
|
@@ -9000,13 +9300,13 @@ function printRun(result, run) {
|
|
|
9000
9300
|
printNode(node, 0);
|
|
9001
9301
|
}
|
|
9002
9302
|
}
|
|
9003
|
-
function
|
|
9303
|
+
function selectRun2(result, runId) {
|
|
9004
9304
|
if (runId !== void 0) {
|
|
9005
9305
|
return result.runs.find((run) => run.runId === runId);
|
|
9006
9306
|
}
|
|
9007
9307
|
return result.runs.length === 1 ? result.runs[0] : void 0;
|
|
9008
9308
|
}
|
|
9009
|
-
function
|
|
9309
|
+
function printMultipleRuns2(result) {
|
|
9010
9310
|
console.error(
|
|
9011
9311
|
`Trace contains ${result.runs.length} runs. Re-run with --run <run-id>.`
|
|
9012
9312
|
);
|
|
@@ -9020,7 +9320,7 @@ function printMultipleRuns(result) {
|
|
|
9020
9320
|
console.error(`- ${bits.join(" ")}`);
|
|
9021
9321
|
}
|
|
9022
9322
|
}
|
|
9023
|
-
function
|
|
9323
|
+
function writeJson2(output2) {
|
|
9024
9324
|
console.log(JSON.stringify(output2, null, 2));
|
|
9025
9325
|
}
|
|
9026
9326
|
async function openCommand(input3, options = {}, stdin = process.stdin) {
|
|
@@ -9029,12 +9329,12 @@ async function openCommand(input3, options = {}, stdin = process.stdin) {
|
|
|
9029
9329
|
const result = await openTrace(traceInput, {
|
|
9030
9330
|
...options.format !== void 0 ? { format: options.format } : {}
|
|
9031
9331
|
});
|
|
9032
|
-
const selected =
|
|
9332
|
+
const selected = selectRun2(result, options.run);
|
|
9033
9333
|
if (selected === void 0) {
|
|
9034
9334
|
process.exitCode = 1;
|
|
9035
9335
|
const message = options.run !== void 0 ? `Run not found: ${options.run}` : `Trace contains ${result.runs.length} runs. Specify --run <run-id>.`;
|
|
9036
9336
|
if (options.json) {
|
|
9037
|
-
|
|
9337
|
+
writeJson2({
|
|
9038
9338
|
format: result.format,
|
|
9039
9339
|
sourceFiles: result.sourceFiles,
|
|
9040
9340
|
warnings: result.warnings,
|
|
@@ -9045,12 +9345,12 @@ async function openCommand(input3, options = {}, stdin = process.stdin) {
|
|
|
9045
9345
|
} else if (options.run !== void 0) {
|
|
9046
9346
|
console.error(message);
|
|
9047
9347
|
} else {
|
|
9048
|
-
|
|
9348
|
+
printMultipleRuns2(result);
|
|
9049
9349
|
}
|
|
9050
9350
|
return;
|
|
9051
9351
|
}
|
|
9052
9352
|
if (options.json) {
|
|
9053
|
-
|
|
9353
|
+
writeJson2({
|
|
9054
9354
|
format: result.format,
|
|
9055
9355
|
sourceFiles: result.sourceFiles,
|
|
9056
9356
|
warnings: result.warnings,
|
|
@@ -9070,7 +9370,7 @@ async function openCommand(input3, options = {}, stdin = process.stdin) {
|
|
|
9070
9370
|
const code = error instanceof TraceReadError ? error.code : void 0;
|
|
9071
9371
|
const warnings = error instanceof TraceReadError ? error.warnings : [];
|
|
9072
9372
|
if (options.json) {
|
|
9073
|
-
|
|
9373
|
+
writeJson2({
|
|
9074
9374
|
warnings,
|
|
9075
9375
|
error: { ...code !== void 0 ? { code } : {}, message }
|
|
9076
9376
|
});
|
|
@@ -9174,7 +9474,7 @@ function errorResult(input3, diagnostics, selectedRun) {
|
|
|
9174
9474
|
function flattenNodes(nodes) {
|
|
9175
9475
|
return nodes.flatMap((node) => [node, ...flattenNodes(node.children)]);
|
|
9176
9476
|
}
|
|
9177
|
-
function
|
|
9477
|
+
function buildFacts2(input3, selectedRun) {
|
|
9178
9478
|
const scopedRuns = selectedRun ? [selectedRun] : input3.read.runs;
|
|
9179
9479
|
const scopedRunIds = new Set(scopedRuns.map((run) => run.runId));
|
|
9180
9480
|
const scopedEvents = selectedRun === void 0 ? input3.read.events : input3.read.events.filter((event) => scopedRunIds.has(event.runId));
|
|
@@ -10182,7 +10482,7 @@ function createBaselineRegressionRule(options) {
|
|
|
10182
10482
|
)
|
|
10183
10483
|
];
|
|
10184
10484
|
}
|
|
10185
|
-
const baselineFacts =
|
|
10485
|
+
const baselineFacts = buildFacts2(options.baseline, baselineSelection.run);
|
|
10186
10486
|
const baselineContext = {
|
|
10187
10487
|
...baselineFacts,
|
|
10188
10488
|
selectedRun: baselineSelection.run,
|
|
@@ -10301,7 +10601,7 @@ function runTraceChecks(input3, options = {}) {
|
|
|
10301
10601
|
if (rules.diagnostics.length > 0) {
|
|
10302
10602
|
return errorResult(input3, rules.diagnostics, selected.run);
|
|
10303
10603
|
}
|
|
10304
|
-
const facts =
|
|
10604
|
+
const facts = buildFacts2(input3, selected.run);
|
|
10305
10605
|
const context = {
|
|
10306
10606
|
...facts,
|
|
10307
10607
|
...selected.run ? { selectedRun: selected.run } : {},
|
|
@@ -10336,33 +10636,6 @@ function runTraceChecks(input3, options = {}) {
|
|
|
10336
10636
|
diagnostics
|
|
10337
10637
|
};
|
|
10338
10638
|
}
|
|
10339
|
-
async function readStdin2(stdin) {
|
|
10340
|
-
stdin.setEncoding("utf8");
|
|
10341
|
-
let content = "";
|
|
10342
|
-
for await (const chunk of stdin) {
|
|
10343
|
-
content += typeof chunk === "string" ? chunk : String(chunk);
|
|
10344
|
-
}
|
|
10345
|
-
return content;
|
|
10346
|
-
}
|
|
10347
|
-
function isMissingFileError2(error) {
|
|
10348
|
-
return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
10349
|
-
}
|
|
10350
|
-
async function inputFromTarget(target, options, stdin) {
|
|
10351
|
-
if (target === "-") {
|
|
10352
|
-
return { type: "string", content: await readStdin2(stdin) };
|
|
10353
|
-
}
|
|
10354
|
-
try {
|
|
10355
|
-
const stats2 = await stat(target);
|
|
10356
|
-
if (stats2.isDirectory()) return { type: "directory", path: target };
|
|
10357
|
-
return { type: "file", path: target };
|
|
10358
|
-
} catch (error) {
|
|
10359
|
-
if (!isMissingFileError2(error)) throw error;
|
|
10360
|
-
}
|
|
10361
|
-
const runPath = getTraceFilePath(target, resolveTraceDir({ dir: options.dir }));
|
|
10362
|
-
const stats = await stat(runPath);
|
|
10363
|
-
if (stats.isDirectory()) return { type: "directory", path: runPath };
|
|
10364
|
-
return { type: "file", path: runPath };
|
|
10365
|
-
}
|
|
10366
10639
|
|
|
10367
10640
|
// packages/cli/src/check.ts
|
|
10368
10641
|
var DEFAULT_SELECT = ["run.status"];
|
|
@@ -10809,7 +11082,7 @@ function stable3(value) {
|
|
|
10809
11082
|
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable3(record[key])])
|
|
10810
11083
|
);
|
|
10811
11084
|
}
|
|
10812
|
-
function
|
|
11085
|
+
function writeJson3(value) {
|
|
10813
11086
|
return `${JSON.stringify(stable3(value), null, 2)}
|
|
10814
11087
|
`;
|
|
10815
11088
|
}
|
|
@@ -10827,7 +11100,7 @@ function increment(record, key) {
|
|
|
10827
11100
|
const label = key && key.trim() !== "" ? key : "unknown";
|
|
10828
11101
|
record[label] = (record[label] ?? 0) + 1;
|
|
10829
11102
|
}
|
|
10830
|
-
function
|
|
11103
|
+
function selectRun3(read, runId) {
|
|
10831
11104
|
if (runId !== void 0) {
|
|
10832
11105
|
return read.runs.find((run) => run.runId === runId);
|
|
10833
11106
|
}
|
|
@@ -10975,7 +11248,7 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
|
10975
11248
|
process.exitCode = 1;
|
|
10976
11249
|
return;
|
|
10977
11250
|
}
|
|
10978
|
-
const selectedRun =
|
|
11251
|
+
const selectedRun = selectRun3(read, options.run);
|
|
10979
11252
|
const check = runTraceChecks(
|
|
10980
11253
|
{ read },
|
|
10981
11254
|
{
|
|
@@ -11012,12 +11285,12 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
|
11012
11285
|
}
|
|
11013
11286
|
const files = [];
|
|
11014
11287
|
await mkdir(outputDir, { recursive: true });
|
|
11015
|
-
await writeArtifact(outputDir, "trace.json",
|
|
11016
|
-
await writeArtifact(outputDir, "check.json",
|
|
11288
|
+
await writeArtifact(outputDir, "trace.json", writeJson3(trace), files);
|
|
11289
|
+
await writeArtifact(outputDir, "check.json", writeJson3(check), files);
|
|
11017
11290
|
await writeArtifact(
|
|
11018
11291
|
outputDir,
|
|
11019
11292
|
"diff.json",
|
|
11020
|
-
|
|
11293
|
+
writeJson3(diff ?? { status: "not_requested", findings: [], diagnostics: [] }),
|
|
11021
11294
|
files
|
|
11022
11295
|
);
|
|
11023
11296
|
await writeArtifact(outputDir, "summary.md", renderMarkdown(trace, check, diff), files);
|
|
@@ -11047,9 +11320,9 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
11047
11320
|
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path10.resolve(summaryTarget) } : {},
|
|
11048
11321
|
note: NOTE
|
|
11049
11322
|
};
|
|
11050
|
-
await writeFile(path10.join(outputDir, "manifest.json"),
|
|
11323
|
+
await writeFile(path10.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
|
|
11051
11324
|
if (options.json === true) {
|
|
11052
|
-
console.log(
|
|
11325
|
+
console.log(writeJson3(manifest).trimEnd());
|
|
11053
11326
|
} else {
|
|
11054
11327
|
console.log(`Wrote AgentInspect artifacts to ${outputDir}`);
|
|
11055
11328
|
console.log(`Status: ${manifest.status}`);
|
|
@@ -11268,6 +11541,23 @@ function createCliProgram() {
|
|
|
11268
11541
|
).action((runId, opts) => {
|
|
11269
11542
|
runCommand(() => reportCommand(runId, opts));
|
|
11270
11543
|
});
|
|
11544
|
+
program.command("explain").description("Explain a local trace with deterministic facts (no provider calls)").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
|
|
11545
|
+
new Option("--format <format>", "trace input format").choices([
|
|
11546
|
+
"agent-inspect-jsonl",
|
|
11547
|
+
"openinference-json",
|
|
11548
|
+
"otlp-json"
|
|
11549
|
+
])
|
|
11550
|
+
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--dry-run", "emit only the local facts payload that a provider could receive").option(
|
|
11551
|
+
"--provider <provider>",
|
|
11552
|
+
"reserved for explicit provider explain; currently rejected without network calls"
|
|
11553
|
+
).option("--json", "print deterministic JSON explanation result").addOption(
|
|
11554
|
+
new Option(
|
|
11555
|
+
"--redaction-profile <profile>",
|
|
11556
|
+
"redaction profile for explanation payload: local, share, strict (default: local)"
|
|
11557
|
+
).choices(["local", "share", "strict"])
|
|
11558
|
+
).action((target, opts) => {
|
|
11559
|
+
runCommand(() => explainCommand(target, opts));
|
|
11560
|
+
});
|
|
11271
11561
|
return program;
|
|
11272
11562
|
}
|
|
11273
11563
|
function isPrimaryModule() {
|