agent-inspect 2.2.0 → 2.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 +31 -4
- package/README.md +47 -4
- package/docs/ADAPTER-CONFORMANCE.md +25 -5
- package/docs/ADAPTERS.md +102 -11
- package/docs/CLI.md +54 -1
- package/docs/SCHEMA.md +1 -0
- package/package.json +2 -2
- package/packages/cli/dist/index.cjs +757 -14
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +757 -14
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/advanced.cjs +581 -3
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +172 -8
- package/packages/core/dist/advanced.d.ts +172 -8
- package/packages/core/dist/advanced.mjs +572 -4
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.d.cts +2 -2
- package/packages/core/dist/checks.d.ts +2 -2
- package/packages/core/dist/{context-yv2VSDQF.d.ts → context-BDZmi53V.d.ts} +3 -3
- package/packages/core/dist/{context-CSKnzpXR.d.cts → context-DjY-jvQk.d.cts} +3 -3
- package/packages/core/dist/diff.d.cts +3 -3
- package/packages/core/dist/diff.d.ts +3 -3
- package/packages/core/dist/exporters.d.cts +3 -3
- package/packages/core/dist/exporters.d.ts +3 -3
- package/packages/core/dist/index.d.cts +6 -6
- package/packages/core/dist/index.d.ts +6 -6
- package/packages/core/dist/{inspect-event-CevRYp58.d.cts → inspect-event-CYAV7Hxm.d.cts} +1 -1
- package/packages/core/dist/{inspect-event-CevRYp58.d.ts → inspect-event-CYAV7Hxm.d.ts} +1 -1
- package/packages/core/dist/{log-config-8aE5Kxtr.d.cts → log-config-BNQ9UTmP.d.cts} +1 -1
- package/packages/core/dist/{log-config-DVimQJho.d.ts → log-config-D3Yp1SZN.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-D-WpXeZX.d.ts → persisted-inspect-event-BLvb0jSX.d.ts} +1 -1
- package/packages/core/dist/{persisted-inspect-event-DHcHPUKv.d.cts → persisted-inspect-event-D5SjBz9b.d.cts} +1 -1
- package/packages/core/dist/persisted.d.cts +5 -5
- package/packages/core/dist/persisted.d.ts +5 -5
- package/packages/core/dist/readers.d.cts +2 -2
- package/packages/core/dist/readers.d.ts +2 -2
- package/packages/core/dist/{types-DgMN3qow.d.cts → types-C-llnPH0.d.cts} +1 -1
- package/packages/core/dist/{types-D-Y1kOU-.d.ts → types-CQun9LW6.d.ts} +1 -1
- package/packages/core/dist/writers.d.cts +2 -2
- package/packages/core/dist/writers.d.ts +2 -2
|
@@ -12,7 +12,7 @@ import tty from 'tty';
|
|
|
12
12
|
import { createInterface } from 'readline';
|
|
13
13
|
|
|
14
14
|
// package.json
|
|
15
|
-
var version = "2.
|
|
15
|
+
var version = "2.4.0";
|
|
16
16
|
|
|
17
17
|
// packages/core/src/correlation-metadata.ts
|
|
18
18
|
var TRACE_CORRELATION_KEYS = [
|
|
@@ -2529,10 +2529,12 @@ async function searchTraces(metas, options) {
|
|
|
2529
2529
|
durationFilter = parseDurationFilter(options.duration);
|
|
2530
2530
|
}
|
|
2531
2531
|
const limit = options.limit ?? 50;
|
|
2532
|
+
const sessionId = options.session?.trim();
|
|
2532
2533
|
const hasContentFilter = Boolean(
|
|
2533
2534
|
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
|
|
2534
2535
|
);
|
|
2535
2536
|
const results = [];
|
|
2537
|
+
const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
|
|
2536
2538
|
if (!hasContentFilter) {
|
|
2537
2539
|
for (const m of filtered) {
|
|
2538
2540
|
results.push({
|
|
@@ -2541,9 +2543,10 @@ async function searchTraces(metas, options) {
|
|
|
2541
2543
|
runStatus: m.status,
|
|
2542
2544
|
timestamp: m.startedAt,
|
|
2543
2545
|
durationMs: m.durationMs,
|
|
2544
|
-
matchReason: "trace in directory",
|
|
2545
|
-
matchedFields: ["run"],
|
|
2546
|
-
filePath: m.filePath
|
|
2546
|
+
matchReason: sessionLabel ? `trace in session ${sessionLabel}` : "trace in directory",
|
|
2547
|
+
matchedFields: sessionLabel ? ["run", "session"] : ["run"],
|
|
2548
|
+
filePath: m.filePath,
|
|
2549
|
+
...sessionLabel ? { sessionId: sessionLabel } : {}
|
|
2547
2550
|
});
|
|
2548
2551
|
}
|
|
2549
2552
|
return results.slice(0, limit);
|
|
@@ -2682,6 +2685,478 @@ async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
|
2682
2685
|
}
|
|
2683
2686
|
return metas;
|
|
2684
2687
|
}
|
|
2688
|
+
|
|
2689
|
+
// packages/core/src/sessions/metadata.ts
|
|
2690
|
+
function isNonEmptyString3(value) {
|
|
2691
|
+
return typeof value === "string" && value.trim() !== "";
|
|
2692
|
+
}
|
|
2693
|
+
function finitePositiveInt(value) {
|
|
2694
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
|
|
2695
|
+
return void 0;
|
|
2696
|
+
}
|
|
2697
|
+
return Math.trunc(value);
|
|
2698
|
+
}
|
|
2699
|
+
function extractSessionWorkflowMetadata(record) {
|
|
2700
|
+
if (!record) return void 0;
|
|
2701
|
+
const out = {};
|
|
2702
|
+
let found = false;
|
|
2703
|
+
const assignString = (key, value) => {
|
|
2704
|
+
if (isNonEmptyString3(value)) {
|
|
2705
|
+
out[key] = value.trim();
|
|
2706
|
+
found = true;
|
|
2707
|
+
}
|
|
2708
|
+
};
|
|
2709
|
+
assignString("sessionId", record.sessionId);
|
|
2710
|
+
assignString("conversationId", record.conversationId);
|
|
2711
|
+
assignString("groupId", record.groupId);
|
|
2712
|
+
assignString("parentGroupId", record.parentGroupId);
|
|
2713
|
+
assignString("retryOf", record.retryOf);
|
|
2714
|
+
assignString("retryReason", record.retryReason);
|
|
2715
|
+
assignString("handoffFrom", record.handoffFrom);
|
|
2716
|
+
assignString("handoffTo", record.handoffTo);
|
|
2717
|
+
assignString("subAgentId", record.subAgentId);
|
|
2718
|
+
assignString("subAgentName", record.subAgentName);
|
|
2719
|
+
assignString("jobId", record.jobId);
|
|
2720
|
+
assignString("queueName", record.queueName);
|
|
2721
|
+
assignString("workflowName", record.workflowName);
|
|
2722
|
+
assignString("workflowStep", record.workflowStep);
|
|
2723
|
+
assignString("toolCallId", record.toolCallId);
|
|
2724
|
+
assignString("mcpToolCallId", record.mcpToolCallId);
|
|
2725
|
+
assignString("linkedStepId", record.linkedStepId);
|
|
2726
|
+
assignString("correlationId", record.correlationId);
|
|
2727
|
+
assignString("requestId", record.requestId);
|
|
2728
|
+
assignString("decisionId", record.decisionId);
|
|
2729
|
+
const attempt = finitePositiveInt(record.attempt);
|
|
2730
|
+
if (attempt !== void 0) {
|
|
2731
|
+
out.attempt = attempt;
|
|
2732
|
+
found = true;
|
|
2733
|
+
}
|
|
2734
|
+
return found ? out : void 0;
|
|
2735
|
+
}
|
|
2736
|
+
function sessionKeyForRun(meta, options) {
|
|
2737
|
+
if (meta?.sessionId) return meta.sessionId;
|
|
2738
|
+
if (options?.correlateByGroupId && meta?.groupId) {
|
|
2739
|
+
return `group:${meta.groupId}`;
|
|
2740
|
+
}
|
|
2741
|
+
return void 0;
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
// packages/core/src/sessions/load.ts
|
|
2745
|
+
async function enrichSessionRunRecord(meta) {
|
|
2746
|
+
let metadata;
|
|
2747
|
+
try {
|
|
2748
|
+
const events = await readTraceEventsFromFile(meta.filePath);
|
|
2749
|
+
for (const event of events) {
|
|
2750
|
+
if (event.event !== "run_started") continue;
|
|
2751
|
+
if (event.metadata && typeof event.metadata === "object") {
|
|
2752
|
+
metadata = event.metadata;
|
|
2753
|
+
}
|
|
2754
|
+
break;
|
|
2755
|
+
}
|
|
2756
|
+
} catch {
|
|
2757
|
+
}
|
|
2758
|
+
return {
|
|
2759
|
+
runId: meta.runId,
|
|
2760
|
+
name: meta.name,
|
|
2761
|
+
status: meta.status,
|
|
2762
|
+
startedAt: meta.startedAt,
|
|
2763
|
+
endedAt: meta.endedAt,
|
|
2764
|
+
durationMs: meta.durationMs,
|
|
2765
|
+
filePath: meta.filePath,
|
|
2766
|
+
metadata
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
async function loadSessionRunRecords(metas) {
|
|
2770
|
+
const out = [];
|
|
2771
|
+
for (const meta of metas) {
|
|
2772
|
+
out.push(await enrichSessionRunRecord(meta));
|
|
2773
|
+
}
|
|
2774
|
+
return out;
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
// packages/core/src/sessions/scope.ts
|
|
2778
|
+
function filterMetasBySessionScope(metas, records, options) {
|
|
2779
|
+
const sessionId = options.sessionId?.trim();
|
|
2780
|
+
const groupId = options.groupId?.trim();
|
|
2781
|
+
const warnings = [];
|
|
2782
|
+
if (sessionId) {
|
|
2783
|
+
const index = buildSessionIndex(records, {
|
|
2784
|
+
correlateByGroupId: options.correlateByGroupId === true
|
|
2785
|
+
});
|
|
2786
|
+
warnings.push(...index.warnings);
|
|
2787
|
+
const session = index.sessions.find((item) => item.sessionId === sessionId);
|
|
2788
|
+
if (!session) {
|
|
2789
|
+
return {
|
|
2790
|
+
metas: [],
|
|
2791
|
+
scopeLabel: sessionId,
|
|
2792
|
+
scopeKind: "session",
|
|
2793
|
+
runIds: [],
|
|
2794
|
+
warnings,
|
|
2795
|
+
notFound: true
|
|
2796
|
+
};
|
|
2797
|
+
}
|
|
2798
|
+
const runIdSet = new Set(session.runIds);
|
|
2799
|
+
const filtered = metas.filter((meta) => runIdSet.has(meta.runId));
|
|
2800
|
+
return {
|
|
2801
|
+
metas: filtered,
|
|
2802
|
+
scopeLabel: sessionId,
|
|
2803
|
+
scopeKind: "session",
|
|
2804
|
+
runIds: session.runIds,
|
|
2805
|
+
warnings,
|
|
2806
|
+
notFound: false
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
if (groupId) {
|
|
2810
|
+
const runIds = records.filter((run) => extractSessionWorkflowMetadata(run.metadata)?.groupId === groupId).map((run) => run.runId).sort();
|
|
2811
|
+
if (runIds.length === 0) {
|
|
2812
|
+
return {
|
|
2813
|
+
metas: [],
|
|
2814
|
+
scopeLabel: groupId,
|
|
2815
|
+
scopeKind: "group",
|
|
2816
|
+
runIds: [],
|
|
2817
|
+
warnings,
|
|
2818
|
+
notFound: true
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
const runIdSet = new Set(runIds);
|
|
2822
|
+
return {
|
|
2823
|
+
metas: metas.filter((meta) => runIdSet.has(meta.runId)),
|
|
2824
|
+
scopeLabel: groupId,
|
|
2825
|
+
scopeKind: "group",
|
|
2826
|
+
runIds,
|
|
2827
|
+
warnings,
|
|
2828
|
+
notFound: false
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
return {
|
|
2832
|
+
metas: [...metas],
|
|
2833
|
+
scopeLabel: "",
|
|
2834
|
+
scopeKind: "session",
|
|
2835
|
+
runIds: [],
|
|
2836
|
+
warnings,
|
|
2837
|
+
notFound: false
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
// packages/core/src/sessions/checks.ts
|
|
2842
|
+
function emptySummary() {
|
|
2843
|
+
return { passed: 0, failed: 0, warnings: 0, errors: 0 };
|
|
2844
|
+
}
|
|
2845
|
+
function mergeSummary(target, source) {
|
|
2846
|
+
return {
|
|
2847
|
+
passed: target.passed + source.passed,
|
|
2848
|
+
failed: target.failed + source.failed,
|
|
2849
|
+
warnings: target.warnings + source.warnings,
|
|
2850
|
+
errors: target.errors + source.errors
|
|
2851
|
+
};
|
|
2852
|
+
}
|
|
2853
|
+
function sessionDiagnostic(code, message) {
|
|
2854
|
+
return { code, message, severity: "error" };
|
|
2855
|
+
}
|
|
2856
|
+
function aggregateSessionCheckResults(perRun, scope) {
|
|
2857
|
+
if (scope.notFound) {
|
|
2858
|
+
return {
|
|
2859
|
+
ok: false,
|
|
2860
|
+
status: "error",
|
|
2861
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
2862
|
+
scopeKind: scope.scopeKind,
|
|
2863
|
+
scopeLabel: scope.scopeLabel,
|
|
2864
|
+
runIds: [],
|
|
2865
|
+
runResults: [],
|
|
2866
|
+
summary: { ...emptySummary(), errors: 1 },
|
|
2867
|
+
findings: [],
|
|
2868
|
+
diagnostics: [
|
|
2869
|
+
sessionDiagnostic(
|
|
2870
|
+
"AI_CHECK_INVALID_ARGUMENTS",
|
|
2871
|
+
`${scope.scopeKind} not found: ${scope.scopeLabel}`
|
|
2872
|
+
)
|
|
2873
|
+
],
|
|
2874
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2877
|
+
if (scope.empty || perRun.length === 0) {
|
|
2878
|
+
return {
|
|
2879
|
+
ok: false,
|
|
2880
|
+
status: "error",
|
|
2881
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
2882
|
+
scopeKind: scope.scopeKind,
|
|
2883
|
+
scopeLabel: scope.scopeLabel,
|
|
2884
|
+
runIds: scope.runIds,
|
|
2885
|
+
runResults: [],
|
|
2886
|
+
summary: { ...emptySummary(), errors: 1 },
|
|
2887
|
+
findings: [],
|
|
2888
|
+
diagnostics: [
|
|
2889
|
+
sessionDiagnostic(
|
|
2890
|
+
"AI_CHECK_TRACE_UNREADABLE",
|
|
2891
|
+
`No readable traces in ${scope.scopeKind}: ${scope.scopeLabel}`
|
|
2892
|
+
)
|
|
2893
|
+
],
|
|
2894
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
let summary = emptySummary();
|
|
2898
|
+
const findings = [];
|
|
2899
|
+
const diagnostics = [];
|
|
2900
|
+
const runResults = [];
|
|
2901
|
+
for (const result of perRun) {
|
|
2902
|
+
summary = mergeSummary(summary, result.summary);
|
|
2903
|
+
findings.push(...result.findings);
|
|
2904
|
+
diagnostics.push(...result.diagnostics);
|
|
2905
|
+
if (result.runId) {
|
|
2906
|
+
runResults.push({ runId: result.runId, status: result.status });
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
runResults.sort((a, b) => a.runId.localeCompare(b.runId));
|
|
2910
|
+
findings.sort((a, b) => {
|
|
2911
|
+
const runCmp = (a.evidence[0]?.runId ?? "").localeCompare(
|
|
2912
|
+
b.evidence[0]?.runId ?? ""
|
|
2913
|
+
);
|
|
2914
|
+
if (runCmp !== 0) return runCmp;
|
|
2915
|
+
return a.ruleId.localeCompare(b.ruleId);
|
|
2916
|
+
});
|
|
2917
|
+
const hasErrors = diagnostics.some((item) => item.severity === "error");
|
|
2918
|
+
const status = hasErrors ? "error" : summary.failed > 0 ? "fail" : "pass";
|
|
2919
|
+
return {
|
|
2920
|
+
ok: status === "pass",
|
|
2921
|
+
status,
|
|
2922
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
2923
|
+
scopeKind: scope.scopeKind,
|
|
2924
|
+
scopeLabel: scope.scopeLabel,
|
|
2925
|
+
runIds: scope.runIds,
|
|
2926
|
+
runResults,
|
|
2927
|
+
summary,
|
|
2928
|
+
findings,
|
|
2929
|
+
diagnostics,
|
|
2930
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
2931
|
+
};
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
// packages/core/src/sessions/index.ts
|
|
2935
|
+
function compareRuns(a, b) {
|
|
2936
|
+
const aStart = a.startedAt ?? 0;
|
|
2937
|
+
const bStart = b.startedAt ?? 0;
|
|
2938
|
+
if (aStart !== bStart) return aStart - bStart;
|
|
2939
|
+
return a.runId.localeCompare(b.runId);
|
|
2940
|
+
}
|
|
2941
|
+
function buildGroups(runIds, metaByRunId) {
|
|
2942
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
2943
|
+
for (const runId of runIds) {
|
|
2944
|
+
const groupId = metaByRunId.get(runId)?.groupId;
|
|
2945
|
+
if (!groupId) continue;
|
|
2946
|
+
const existing = byGroup.get(groupId);
|
|
2947
|
+
if (existing) {
|
|
2948
|
+
existing.runIds.push(runId);
|
|
2949
|
+
} else {
|
|
2950
|
+
byGroup.set(groupId, {
|
|
2951
|
+
groupId,
|
|
2952
|
+
parentGroupId: metaByRunId.get(runId)?.parentGroupId,
|
|
2953
|
+
runIds: [runId]
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
return [...byGroup.values()].map((group) => ({
|
|
2958
|
+
...group,
|
|
2959
|
+
runIds: [...group.runIds].sort((a, b) => a.localeCompare(b))
|
|
2960
|
+
}));
|
|
2961
|
+
}
|
|
2962
|
+
function buildHandoffs(runIds, metaByRunId, warnings, sessionId) {
|
|
2963
|
+
const edges = [];
|
|
2964
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2965
|
+
const pushEdge = (edge) => {
|
|
2966
|
+
const key = `${edge.from}->${edge.to}:${edge.confidence}`;
|
|
2967
|
+
if (seen.has(key)) return;
|
|
2968
|
+
seen.add(key);
|
|
2969
|
+
edges.push(edge);
|
|
2970
|
+
};
|
|
2971
|
+
for (const runId of runIds) {
|
|
2972
|
+
const meta = metaByRunId.get(runId);
|
|
2973
|
+
if (!meta) continue;
|
|
2974
|
+
if (meta.handoffFrom && meta.handoffTo) {
|
|
2975
|
+
pushEdge({
|
|
2976
|
+
from: meta.handoffFrom,
|
|
2977
|
+
to: meta.handoffTo,
|
|
2978
|
+
source: "manual",
|
|
2979
|
+
confidence: "explicit"
|
|
2980
|
+
});
|
|
2981
|
+
continue;
|
|
2982
|
+
}
|
|
2983
|
+
if (meta.handoffFrom) {
|
|
2984
|
+
pushEdge({
|
|
2985
|
+
from: meta.handoffFrom,
|
|
2986
|
+
to: runId,
|
|
2987
|
+
source: "manual",
|
|
2988
|
+
confidence: "explicit"
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2991
|
+
if (meta.handoffTo) {
|
|
2992
|
+
pushEdge({
|
|
2993
|
+
from: runId,
|
|
2994
|
+
to: meta.handoffTo,
|
|
2995
|
+
source: "manual",
|
|
2996
|
+
confidence: "explicit"
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2999
|
+
if (meta.subAgentId && meta.parentGroupId && !meta.handoffFrom && !meta.handoffTo) {
|
|
3000
|
+
pushEdge({
|
|
3001
|
+
from: meta.parentGroupId,
|
|
3002
|
+
to: meta.subAgentId,
|
|
3003
|
+
source: "inferred",
|
|
3004
|
+
confidence: "correlated"
|
|
3005
|
+
});
|
|
3006
|
+
warnings.push({
|
|
3007
|
+
code: "ambiguous-handoff-endpoints",
|
|
3008
|
+
message: "Handoff inferred from parentGroupId and subAgentId without explicit handoffFrom/handoffTo.",
|
|
3009
|
+
runId,
|
|
3010
|
+
sessionId
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
return edges.sort((a, b) => {
|
|
3015
|
+
const from = a.from.localeCompare(b.from);
|
|
3016
|
+
if (from !== 0) return from;
|
|
3017
|
+
return a.to.localeCompare(b.to);
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
function buildRetries(runIds, metaByRunId, warnings, sessionId) {
|
|
3021
|
+
const retries = [];
|
|
3022
|
+
for (const runId of runIds) {
|
|
3023
|
+
const meta = metaByRunId.get(runId);
|
|
3024
|
+
if (!meta) continue;
|
|
3025
|
+
if (meta.retryOf) {
|
|
3026
|
+
retries.push({
|
|
3027
|
+
runId,
|
|
3028
|
+
retryOf: meta.retryOf,
|
|
3029
|
+
attempt: meta.attempt,
|
|
3030
|
+
source: "manual",
|
|
3031
|
+
confidence: "explicit"
|
|
3032
|
+
});
|
|
3033
|
+
continue;
|
|
3034
|
+
}
|
|
3035
|
+
if (meta.attempt !== void 0 && meta.attempt > 1) {
|
|
3036
|
+
retries.push({
|
|
3037
|
+
runId,
|
|
3038
|
+
attempt: meta.attempt,
|
|
3039
|
+
source: "inferred",
|
|
3040
|
+
confidence: "correlated"
|
|
3041
|
+
});
|
|
3042
|
+
warnings.push({
|
|
3043
|
+
code: "ambiguous-retry-link",
|
|
3044
|
+
message: "attempt > 1 without retryOf; retry link is correlated only.",
|
|
3045
|
+
runId,
|
|
3046
|
+
sessionId
|
|
3047
|
+
});
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
return retries.sort((a, b) => a.runId.localeCompare(b.runId));
|
|
3051
|
+
}
|
|
3052
|
+
function buildCriticalPath(runs, handoffs) {
|
|
3053
|
+
const runById = new Map(runs.map((run) => [run.runId, run]));
|
|
3054
|
+
const explicitTargets = new Set(
|
|
3055
|
+
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.to)
|
|
3056
|
+
);
|
|
3057
|
+
const explicitSources = new Set(
|
|
3058
|
+
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
3059
|
+
);
|
|
3060
|
+
const ordered = [...runs].sort(compareRuns);
|
|
3061
|
+
const path15 = [];
|
|
3062
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3063
|
+
const pushRun = (run, confidence, source) => {
|
|
3064
|
+
if (visited.has(run.runId)) return;
|
|
3065
|
+
visited.add(run.runId);
|
|
3066
|
+
path15.push({
|
|
3067
|
+
runId: run.runId,
|
|
3068
|
+
name: run.name,
|
|
3069
|
+
startedAt: run.startedAt,
|
|
3070
|
+
durationMs: run.durationMs,
|
|
3071
|
+
confidence,
|
|
3072
|
+
source
|
|
3073
|
+
});
|
|
3074
|
+
};
|
|
3075
|
+
for (const edge of handoffs) {
|
|
3076
|
+
if (edge.confidence !== "explicit") continue;
|
|
3077
|
+
const fromRun = [...runById.values()].find(
|
|
3078
|
+
(run) => run.runId === edge.from || metaRunIdMatches(run, edge.from, runById)
|
|
3079
|
+
);
|
|
3080
|
+
const toRun = [...runById.values()].find(
|
|
3081
|
+
(run) => run.runId === edge.to || metaRunIdMatches(run, edge.to, runById)
|
|
3082
|
+
);
|
|
3083
|
+
if (fromRun) pushRun(fromRun, "explicit", "manual");
|
|
3084
|
+
if (toRun) pushRun(toRun, "explicit", "manual");
|
|
3085
|
+
}
|
|
3086
|
+
for (const run of ordered) {
|
|
3087
|
+
if (visited.has(run.runId)) continue;
|
|
3088
|
+
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
3089
|
+
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
3090
|
+
}
|
|
3091
|
+
return path15;
|
|
3092
|
+
}
|
|
3093
|
+
function metaRunIdMatches(run, token, runById) {
|
|
3094
|
+
const meta = extractSessionWorkflowMetadata(run.metadata);
|
|
3095
|
+
return meta?.subAgentId === token || meta?.groupId === token || runById.has(token);
|
|
3096
|
+
}
|
|
3097
|
+
function buildSessionIndex(inputRuns, options = {}) {
|
|
3098
|
+
const warnings = [];
|
|
3099
|
+
const runs = [...inputRuns].sort(compareRuns);
|
|
3100
|
+
const metaByRunId = /* @__PURE__ */ new Map();
|
|
3101
|
+
for (const run of runs) {
|
|
3102
|
+
metaByRunId.set(run.runId, extractSessionWorkflowMetadata(run.metadata));
|
|
3103
|
+
}
|
|
3104
|
+
const sessionsByKey = /* @__PURE__ */ new Map();
|
|
3105
|
+
const unscopedRunIds = [];
|
|
3106
|
+
for (const run of runs) {
|
|
3107
|
+
const meta = metaByRunId.get(run.runId);
|
|
3108
|
+
const key = sessionKeyForRun(meta, {
|
|
3109
|
+
correlateByGroupId: options.correlateByGroupId === true
|
|
3110
|
+
});
|
|
3111
|
+
if (!key) {
|
|
3112
|
+
unscopedRunIds.push(run.runId);
|
|
3113
|
+
continue;
|
|
3114
|
+
}
|
|
3115
|
+
const bucket = sessionsByKey.get(key) ?? [];
|
|
3116
|
+
bucket.push(run);
|
|
3117
|
+
sessionsByKey.set(key, bucket);
|
|
3118
|
+
}
|
|
3119
|
+
const sessions = [...sessionsByKey.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([sessionId, sessionRuns]) => {
|
|
3120
|
+
const runIds = sessionRuns.map((run) => run.runId).sort();
|
|
3121
|
+
const handoffs = buildHandoffs(runIds, metaByRunId, warnings, sessionId);
|
|
3122
|
+
const retries = buildRetries(runIds, metaByRunId, warnings, sessionId);
|
|
3123
|
+
const groups = buildGroups(runIds, metaByRunId);
|
|
3124
|
+
const criticalPath = buildCriticalPath(sessionRuns, handoffs);
|
|
3125
|
+
const confidences = new Set(handoffs.map((edge) => edge.confidence));
|
|
3126
|
+
if (confidences.has("explicit") && confidences.has("correlated")) {
|
|
3127
|
+
warnings.push({
|
|
3128
|
+
code: "mixed-confidence-group",
|
|
3129
|
+
message: "Session aggregates explicit and correlated handoff edges.",
|
|
3130
|
+
sessionId
|
|
3131
|
+
});
|
|
3132
|
+
}
|
|
3133
|
+
return {
|
|
3134
|
+
sessionId,
|
|
3135
|
+
runIds,
|
|
3136
|
+
groups,
|
|
3137
|
+
handoffs,
|
|
3138
|
+
retries,
|
|
3139
|
+
criticalPath
|
|
3140
|
+
};
|
|
3141
|
+
});
|
|
3142
|
+
if (sessions.length === 0 && runs.length > 0) {
|
|
3143
|
+
warnings.push({
|
|
3144
|
+
code: "missing-session-id",
|
|
3145
|
+
message: "No sessionId (or correlated groupId) found on input runs."
|
|
3146
|
+
});
|
|
3147
|
+
}
|
|
3148
|
+
warnings.sort((a, b) => {
|
|
3149
|
+
const code = a.code.localeCompare(b.code);
|
|
3150
|
+
if (code !== 0) return code;
|
|
3151
|
+
return (a.runId ?? "").localeCompare(b.runId ?? "");
|
|
3152
|
+
});
|
|
3153
|
+
return {
|
|
3154
|
+
runs,
|
|
3155
|
+
sessions,
|
|
3156
|
+
unscopedRunIds: unscopedRunIds.sort(),
|
|
3157
|
+
warnings
|
|
3158
|
+
};
|
|
3159
|
+
}
|
|
2685
3160
|
var KNOWN_EVENTS = /* @__PURE__ */ new Set([
|
|
2686
3161
|
"run_started",
|
|
2687
3162
|
"run_completed",
|
|
@@ -3703,13 +4178,13 @@ function persistedEventsForParsedTrace(parsed) {
|
|
|
3703
4178
|
function isRecord8(value) {
|
|
3704
4179
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3705
4180
|
}
|
|
3706
|
-
function
|
|
4181
|
+
function isNonEmptyString4(value) {
|
|
3707
4182
|
return typeof value === "string" && value.trim() !== "";
|
|
3708
4183
|
}
|
|
3709
4184
|
function readStringField(record, keys) {
|
|
3710
4185
|
for (const key of keys) {
|
|
3711
4186
|
const value = record[key];
|
|
3712
|
-
if (
|
|
4187
|
+
if (isNonEmptyString4(value)) return value;
|
|
3713
4188
|
}
|
|
3714
4189
|
return void 0;
|
|
3715
4190
|
}
|
|
@@ -3843,7 +4318,7 @@ function parseUnixNanoToIso(value) {
|
|
|
3843
4318
|
return void 0;
|
|
3844
4319
|
}
|
|
3845
4320
|
function parseIsoTime(value) {
|
|
3846
|
-
if (!
|
|
4321
|
+
if (!isNonEmptyString4(value)) return void 0;
|
|
3847
4322
|
const ms = Date.parse(value);
|
|
3848
4323
|
if (!Number.isFinite(ms)) return void 0;
|
|
3849
4324
|
return new Date(ms).toISOString();
|
|
@@ -8397,11 +8872,29 @@ async function searchCommand(options = {}) {
|
|
|
8397
8872
|
parseDurationFilter(options.duration);
|
|
8398
8873
|
}
|
|
8399
8874
|
const files = await td.list();
|
|
8400
|
-
|
|
8875
|
+
let metas = await loadTraceMetadataList(
|
|
8401
8876
|
traceDir,
|
|
8402
8877
|
files,
|
|
8403
8878
|
(f) => td.getPath(f)
|
|
8404
8879
|
);
|
|
8880
|
+
const sessionId = options.session?.trim();
|
|
8881
|
+
if (sessionId) {
|
|
8882
|
+
const records = await loadSessionRunRecords(metas);
|
|
8883
|
+
const scoped = filterMetasBySessionScope(metas, records, {
|
|
8884
|
+
sessionId,
|
|
8885
|
+
correlateByGroupId: options.correlateGroup === true
|
|
8886
|
+
});
|
|
8887
|
+
if (scoped.notFound) {
|
|
8888
|
+
if (options.json) {
|
|
8889
|
+
console.log(JSON.stringify([], null, 2));
|
|
8890
|
+
} else {
|
|
8891
|
+
console.log(`Session not found: ${sessionId}`);
|
|
8892
|
+
console.log(`Trace directory: ${traceDir}`);
|
|
8893
|
+
}
|
|
8894
|
+
return;
|
|
8895
|
+
}
|
|
8896
|
+
metas = scoped.metas;
|
|
8897
|
+
}
|
|
8405
8898
|
const status = options.status === "success" || options.status === "error" || options.status === "running" || options.status === "unknown" ? options.status : void 0;
|
|
8406
8899
|
const results = await searchTraces(metas, {
|
|
8407
8900
|
traceDir,
|
|
@@ -8412,7 +8905,9 @@ async function searchCommand(options = {}) {
|
|
|
8412
8905
|
name: options.name,
|
|
8413
8906
|
tool: options.tool,
|
|
8414
8907
|
duration: options.duration,
|
|
8415
|
-
limit: parseLimit2(options.limit)
|
|
8908
|
+
limit: parseLimit2(options.limit),
|
|
8909
|
+
...sessionId ? { session: sessionId } : {},
|
|
8910
|
+
...options.correlateGroup ? { correlateGroup: true } : {}
|
|
8416
8911
|
});
|
|
8417
8912
|
if (options.json) {
|
|
8418
8913
|
console.log(JSON.stringify(results, null, 2));
|
|
@@ -8440,6 +8935,184 @@ async function searchCommand(options = {}) {
|
|
|
8440
8935
|
}
|
|
8441
8936
|
}
|
|
8442
8937
|
|
|
8938
|
+
// packages/cli/src/sessions.ts
|
|
8939
|
+
async function loadSessionIndex(traceDir, correlateGroup) {
|
|
8940
|
+
const td = new TraceDirectory({ dir: traceDir });
|
|
8941
|
+
const files = await td.list();
|
|
8942
|
+
const metas = await loadTraceMetadataList(
|
|
8943
|
+
traceDir,
|
|
8944
|
+
files,
|
|
8945
|
+
(fileName) => td.getPath(fileName)
|
|
8946
|
+
);
|
|
8947
|
+
const runs = await loadSessionRunRecords(metas);
|
|
8948
|
+
return buildSessionIndex(runs, { correlateByGroupId: correlateGroup === true });
|
|
8949
|
+
}
|
|
8950
|
+
function findSession(index, sessionId) {
|
|
8951
|
+
return index.sessions.find((session) => session.sessionId === sessionId);
|
|
8952
|
+
}
|
|
8953
|
+
function renderSessionsHuman(index, traceDir) {
|
|
8954
|
+
if (index.sessions.length === 0) {
|
|
8955
|
+
console.log("No sessions found");
|
|
8956
|
+
console.log(`Trace directory: ${traceDir}`);
|
|
8957
|
+
if (index.unscopedRunIds.length > 0) {
|
|
8958
|
+
console.log(
|
|
8959
|
+
`Unscoped runs (no sessionId): ${index.unscopedRunIds.join(", ")}`
|
|
8960
|
+
);
|
|
8961
|
+
}
|
|
8962
|
+
return;
|
|
8963
|
+
}
|
|
8964
|
+
console.log("Sessions:");
|
|
8965
|
+
for (const session of index.sessions) {
|
|
8966
|
+
const firstRun = index.runs.find(
|
|
8967
|
+
(run) => session.runIds.includes(run.runId)
|
|
8968
|
+
);
|
|
8969
|
+
const workflowName = firstRun?.metadata && typeof firstRun.metadata.workflowName === "string" ? firstRun.metadata.workflowName : void 0;
|
|
8970
|
+
const suffix = workflowName ? ` workflow=${workflowName}` : "";
|
|
8971
|
+
console.log(
|
|
8972
|
+
` ${session.sessionId} (${session.runIds.length} run${session.runIds.length === 1 ? "" : "s"})${suffix}`
|
|
8973
|
+
);
|
|
8974
|
+
}
|
|
8975
|
+
if (index.unscopedRunIds.length > 0) {
|
|
8976
|
+
console.log("");
|
|
8977
|
+
console.log(
|
|
8978
|
+
`Unscoped runs (no sessionId): ${index.unscopedRunIds.join(", ")}`
|
|
8979
|
+
);
|
|
8980
|
+
}
|
|
8981
|
+
}
|
|
8982
|
+
function renderSessionHuman(session, index, options) {
|
|
8983
|
+
console.log(`Session: ${session.sessionId}`);
|
|
8984
|
+
console.log(`Runs: ${session.runIds.join(", ")}`);
|
|
8985
|
+
if (session.handoffs.length > 0) {
|
|
8986
|
+
console.log("");
|
|
8987
|
+
console.log("Handoffs:");
|
|
8988
|
+
for (const edge of session.handoffs) {
|
|
8989
|
+
console.log(
|
|
8990
|
+
` ${edge.from} -> ${edge.to} (${edge.confidence}, ${edge.source})`
|
|
8991
|
+
);
|
|
8992
|
+
}
|
|
8993
|
+
}
|
|
8994
|
+
if (session.retries.length > 0) {
|
|
8995
|
+
console.log("");
|
|
8996
|
+
console.log("Retries:");
|
|
8997
|
+
for (const retry of session.retries) {
|
|
8998
|
+
const attempt = retry.attempt !== void 0 ? ` attempt=${retry.attempt}` : "";
|
|
8999
|
+
const of = retry.retryOf !== void 0 ? ` retryOf=${retry.retryOf}` : "";
|
|
9000
|
+
console.log(
|
|
9001
|
+
` ${retry.runId}${attempt}${of} (${retry.confidence})`
|
|
9002
|
+
);
|
|
9003
|
+
}
|
|
9004
|
+
}
|
|
9005
|
+
if (options.criticalPath && session.criticalPath.length > 0) {
|
|
9006
|
+
console.log("");
|
|
9007
|
+
console.log("Critical path:");
|
|
9008
|
+
for (const step of session.criticalPath) {
|
|
9009
|
+
const duration = step.durationMs !== void 0 ? ` ${step.durationMs}ms` : "";
|
|
9010
|
+
console.log(
|
|
9011
|
+
` ${step.runId}${step.name ? ` (${step.name})` : ""}${duration} [${step.confidence}]`
|
|
9012
|
+
);
|
|
9013
|
+
}
|
|
9014
|
+
}
|
|
9015
|
+
if (options.diagnostics && index.warnings.length > 0) {
|
|
9016
|
+
const scoped = index.warnings.filter(
|
|
9017
|
+
(warning) => warning.sessionId === session.sessionId || !warning.sessionId
|
|
9018
|
+
);
|
|
9019
|
+
if (scoped.length > 0) {
|
|
9020
|
+
console.log("");
|
|
9021
|
+
console.log("Diagnostics:");
|
|
9022
|
+
for (const warning of scoped) {
|
|
9023
|
+
const run = warning.runId ? ` run=${warning.runId}` : "";
|
|
9024
|
+
console.log(` [${warning.code}]${run} ${warning.message}`);
|
|
9025
|
+
}
|
|
9026
|
+
}
|
|
9027
|
+
}
|
|
9028
|
+
}
|
|
9029
|
+
async function sessionsCommand(options = {}) {
|
|
9030
|
+
try {
|
|
9031
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
9032
|
+
const index = await loadSessionIndex(traceDir, options.correlateGroup);
|
|
9033
|
+
if (options.json) {
|
|
9034
|
+
console.log(
|
|
9035
|
+
JSON.stringify(
|
|
9036
|
+
{
|
|
9037
|
+
traceDir,
|
|
9038
|
+
sessions: index.sessions,
|
|
9039
|
+
unscopedRunIds: index.unscopedRunIds,
|
|
9040
|
+
warnings: index.warnings
|
|
9041
|
+
},
|
|
9042
|
+
null,
|
|
9043
|
+
2
|
|
9044
|
+
)
|
|
9045
|
+
);
|
|
9046
|
+
return;
|
|
9047
|
+
}
|
|
9048
|
+
renderSessionsHuman(index, traceDir);
|
|
9049
|
+
} catch (e) {
|
|
9050
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
9051
|
+
console.error(`[AgentInspect] sessions failed: ${msg}`);
|
|
9052
|
+
process.exitCode = 1;
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
9055
|
+
async function sessionCommand(sessionId, options = {}) {
|
|
9056
|
+
const id = typeof sessionId === "string" && sessionId.trim() !== "" ? sessionId.trim() : "";
|
|
9057
|
+
if (id === "") {
|
|
9058
|
+
console.error("Session id is required");
|
|
9059
|
+
process.exitCode = 1;
|
|
9060
|
+
return;
|
|
9061
|
+
}
|
|
9062
|
+
try {
|
|
9063
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
9064
|
+
const index = await loadSessionIndex(traceDir);
|
|
9065
|
+
const session = findSession(index, id);
|
|
9066
|
+
if (!session) {
|
|
9067
|
+
console.log(`Session not found: ${id}`);
|
|
9068
|
+
console.log(`Trace directory: ${traceDir}`);
|
|
9069
|
+
process.exitCode = 1;
|
|
9070
|
+
return;
|
|
9071
|
+
}
|
|
9072
|
+
if (options.json) {
|
|
9073
|
+
const timelines = {};
|
|
9074
|
+
if (options.timeline) {
|
|
9075
|
+
for (const runId of session.runIds) {
|
|
9076
|
+
const result = await readRunTraceEvents(runId, traceDir);
|
|
9077
|
+
if (result && result.events.length > 0) {
|
|
9078
|
+
timelines[runId] = buildRunTimeline(result.events);
|
|
9079
|
+
}
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
console.log(
|
|
9083
|
+
JSON.stringify(
|
|
9084
|
+
{
|
|
9085
|
+
traceDir,
|
|
9086
|
+
session,
|
|
9087
|
+
warnings: index.warnings.filter(
|
|
9088
|
+
(warning) => warning.sessionId === id || warning.sessionId === void 0
|
|
9089
|
+
),
|
|
9090
|
+
...options.timeline ? { timelines } : {}
|
|
9091
|
+
},
|
|
9092
|
+
null,
|
|
9093
|
+
2
|
|
9094
|
+
)
|
|
9095
|
+
);
|
|
9096
|
+
return;
|
|
9097
|
+
}
|
|
9098
|
+
renderSessionHuman(session, index, options);
|
|
9099
|
+
if (options.timeline) {
|
|
9100
|
+
for (const runId of session.runIds) {
|
|
9101
|
+
const result = await readRunTraceEvents(runId, traceDir);
|
|
9102
|
+
if (!result || result.events.length === 0) continue;
|
|
9103
|
+
const timeline = buildRunTimeline(result.events);
|
|
9104
|
+
console.log("");
|
|
9105
|
+
console.log(`Timeline: ${runId}`);
|
|
9106
|
+
console.log(renderTimeline(timeline));
|
|
9107
|
+
}
|
|
9108
|
+
}
|
|
9109
|
+
} catch (e) {
|
|
9110
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
9111
|
+
console.error(`[AgentInspect] session failed: ${msg}`);
|
|
9112
|
+
process.exitCode = 1;
|
|
9113
|
+
}
|
|
9114
|
+
}
|
|
9115
|
+
|
|
8443
9116
|
// packages/cli/src/what.ts
|
|
8444
9117
|
async function whatCommand(runId, options = {}) {
|
|
8445
9118
|
const id = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "";
|
|
@@ -9586,7 +10259,7 @@ function diagnostic(code, message, ruleId) {
|
|
|
9586
10259
|
...ruleId ? { ruleId } : {}
|
|
9587
10260
|
};
|
|
9588
10261
|
}
|
|
9589
|
-
function
|
|
10262
|
+
function emptySummary2() {
|
|
9590
10263
|
return {
|
|
9591
10264
|
passed: 0,
|
|
9592
10265
|
failed: 0,
|
|
@@ -9601,7 +10274,7 @@ function errorResult(input3, diagnostics, selectedRun) {
|
|
|
9601
10274
|
format: input3.read.format,
|
|
9602
10275
|
...selectedRun ? { runId: selectedRun.runId } : {},
|
|
9603
10276
|
summary: {
|
|
9604
|
-
...
|
|
10277
|
+
...emptySummary2(),
|
|
9605
10278
|
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
9606
10279
|
},
|
|
9607
10280
|
findings: [],
|
|
@@ -10942,6 +11615,13 @@ function printJson(result) {
|
|
|
10942
11615
|
console.log(JSON.stringify(stable2(result), null, 2));
|
|
10943
11616
|
}
|
|
10944
11617
|
function printHuman(result) {
|
|
11618
|
+
const scoped = result;
|
|
11619
|
+
if (scoped.scopeLabel) {
|
|
11620
|
+
console.log(`Scope: ${scoped.scopeKind} ${scoped.scopeLabel}`);
|
|
11621
|
+
if (scoped.runIds?.length) {
|
|
11622
|
+
console.log(`Runs: ${scoped.runIds.join(", ")}`);
|
|
11623
|
+
}
|
|
11624
|
+
}
|
|
10945
11625
|
console.log(`Check status: ${result.status}`);
|
|
10946
11626
|
console.log(`Format: ${result.format}`);
|
|
10947
11627
|
if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
|
|
@@ -10953,7 +11633,9 @@ function printHuman(result) {
|
|
|
10953
11633
|
}
|
|
10954
11634
|
for (const finding of result.findings) {
|
|
10955
11635
|
const path15 = finding.evidence[0]?.path;
|
|
10956
|
-
|
|
11636
|
+
const run = finding.evidence[0]?.runId;
|
|
11637
|
+
const runPrefix = run ? `[${run}] ` : "";
|
|
11638
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
|
|
10957
11639
|
}
|
|
10958
11640
|
}
|
|
10959
11641
|
function readErrorResult(error) {
|
|
@@ -10969,12 +11651,58 @@ function readErrorResult(error) {
|
|
|
10969
11651
|
async function checkCommand(target, options = {}, stdin = process.stdin) {
|
|
10970
11652
|
let result;
|
|
10971
11653
|
let phase = "config";
|
|
11654
|
+
const sessionId = options.session?.trim();
|
|
11655
|
+
const groupId = options.group?.trim();
|
|
11656
|
+
const useSessionScope = Boolean(sessionId || groupId);
|
|
10972
11657
|
try {
|
|
10973
11658
|
const config = await loadConfig(options.config);
|
|
10974
11659
|
const built = buildRules(config, options);
|
|
10975
11660
|
if (built.diagnostics.some((item) => item.severity === "error")) {
|
|
10976
11661
|
result = errorResult2("AI_CHECK_INVALID_CONFIG", "Invalid check configuration.");
|
|
10977
11662
|
result.diagnostics = [...built.diagnostics];
|
|
11663
|
+
} else if (useSessionScope) {
|
|
11664
|
+
phase = "read";
|
|
11665
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
11666
|
+
const td = new TraceDirectory({ dir: traceDir });
|
|
11667
|
+
const files = await td.list();
|
|
11668
|
+
const metas = await loadTraceMetadataList(
|
|
11669
|
+
traceDir,
|
|
11670
|
+
files,
|
|
11671
|
+
(fileName) => td.getPath(fileName)
|
|
11672
|
+
);
|
|
11673
|
+
const records = await loadSessionRunRecords(metas);
|
|
11674
|
+
const scoped = filterMetasBySessionScope(metas, records, {
|
|
11675
|
+
...sessionId ? { sessionId } : {},
|
|
11676
|
+
...groupId ? { groupId } : {},
|
|
11677
|
+
correlateByGroupId: options.correlateGroup === true
|
|
11678
|
+
});
|
|
11679
|
+
const perRun = [];
|
|
11680
|
+
for (const meta of scoped.metas) {
|
|
11681
|
+
const read = await openTrace(
|
|
11682
|
+
{ type: "file", path: meta.filePath },
|
|
11683
|
+
{
|
|
11684
|
+
...options.format !== void 0 ? { format: options.format } : {}
|
|
11685
|
+
}
|
|
11686
|
+
);
|
|
11687
|
+
perRun.push(
|
|
11688
|
+
runTraceChecks(
|
|
11689
|
+
{ read },
|
|
11690
|
+
{
|
|
11691
|
+
rules: built.rules,
|
|
11692
|
+
select: built.select,
|
|
11693
|
+
runId: meta.runId
|
|
11694
|
+
}
|
|
11695
|
+
)
|
|
11696
|
+
);
|
|
11697
|
+
}
|
|
11698
|
+
result = aggregateSessionCheckResults(perRun, {
|
|
11699
|
+
scopeKind: scoped.scopeKind,
|
|
11700
|
+
scopeLabel: scoped.scopeLabel,
|
|
11701
|
+
runIds: scoped.runIds,
|
|
11702
|
+
sessionWarnings: scoped.warnings,
|
|
11703
|
+
notFound: scoped.notFound,
|
|
11704
|
+
empty: scoped.metas.length === 0
|
|
11705
|
+
});
|
|
10978
11706
|
} else {
|
|
10979
11707
|
phase = "read";
|
|
10980
11708
|
const input3 = await inputFromTarget(target, options, stdin);
|
|
@@ -12962,7 +13690,10 @@ function createCliProgram() {
|
|
|
12962
13690
|
]).option("--allowed-model <model>", "allow an LLM model (repeatable)", (value, previous = []) => [
|
|
12963
13691
|
...previous,
|
|
12964
13692
|
value
|
|
12965
|
-
]).option("--max-total-tokens <number>", "add llm.usage with a max total-token budget").
|
|
13693
|
+
]).option("--max-total-tokens <number>", "add llm.usage with a max total-token budget").option("--session <id>", "check all runs in a workflow session (requires --dir)").option("--group <id>", "check all runs sharing a groupId (requires --dir)").option(
|
|
13694
|
+
"--correlate-group",
|
|
13695
|
+
"when using --session, also match synthetic group: session keys"
|
|
13696
|
+
).action((target, opts) => {
|
|
12966
13697
|
runCommand(() => checkCommand(target, opts));
|
|
12967
13698
|
});
|
|
12968
13699
|
program.command("eval").description("Run deterministic local evals against a trace").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
|
|
@@ -13066,9 +13797,21 @@ function createCliProgram() {
|
|
|
13066
13797
|
).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(
|
|
13067
13798
|
"--duration <expr>",
|
|
13068
13799
|
"duration filter on run or step (e.g. >5s, >=500ms)"
|
|
13069
|
-
).option("--limit <number>", "max results (default 50)").option("--
|
|
13800
|
+
).option("--limit <number>", "max results (default 50)").option("--session <id>", "limit search to one workflow session").option(
|
|
13801
|
+
"--correlate-group",
|
|
13802
|
+
"when using --session, also match synthetic group: keys"
|
|
13803
|
+
).option("--json", "print results as JSON").action((opts) => {
|
|
13070
13804
|
runCommand(() => searchCommand(opts));
|
|
13071
13805
|
});
|
|
13806
|
+
program.command("sessions").description("List workflow sessions grouped from local trace metadata (read-only)").option("--dir <path>", "trace directory").option(
|
|
13807
|
+
"--correlate-group",
|
|
13808
|
+
"treat shared groupId as a synthetic session when sessionId is absent"
|
|
13809
|
+
).option("--json", "print sessions index as JSON").action((opts) => {
|
|
13810
|
+
runCommand(() => sessionsCommand(opts));
|
|
13811
|
+
});
|
|
13812
|
+
program.command("session").description("Inspect one workflow session: runs, handoffs, retries (read-only)").argument("<session-id>", "session id (from sessions output)").option("--dir <path>", "trace directory").option("--timeline", "include per-run timelines").option("--critical-path", "include critical path section").option("--diagnostics", "include ambiguity warnings").option("--json", "print session view as JSON").action((sessionId, opts) => {
|
|
13813
|
+
runCommand(() => sessionCommand(sessionId, opts));
|
|
13814
|
+
});
|
|
13072
13815
|
program.command("what").description("Concise human-readable summary of a single run (local JSONL)").argument("<run-id>", "run id (e.g. from list output)").option("--dir <path>", "trace directory").option("--json", "print structured summary as JSON").option("--no-correlation", "omit correlation metadata from human output").action((runId, opts) => {
|
|
13073
13816
|
runCommand(() => whatCommand(runId, opts));
|
|
13074
13817
|
});
|