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
|
@@ -432,10 +432,12 @@ async function searchTraces(metas, options) {
|
|
|
432
432
|
durationFilter = parseDurationFilter(options.duration);
|
|
433
433
|
}
|
|
434
434
|
const limit = options.limit ?? 50;
|
|
435
|
+
const sessionId = options.session?.trim();
|
|
435
436
|
const hasContentFilter = Boolean(
|
|
436
437
|
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
|
|
437
438
|
);
|
|
438
439
|
const results = [];
|
|
440
|
+
const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
|
|
439
441
|
if (!hasContentFilter) {
|
|
440
442
|
for (const m of filtered) {
|
|
441
443
|
results.push({
|
|
@@ -444,9 +446,10 @@ async function searchTraces(metas, options) {
|
|
|
444
446
|
runStatus: m.status,
|
|
445
447
|
timestamp: m.startedAt,
|
|
446
448
|
durationMs: m.durationMs,
|
|
447
|
-
matchReason: "trace in directory",
|
|
448
|
-
matchedFields: ["run"],
|
|
449
|
-
filePath: m.filePath
|
|
449
|
+
matchReason: sessionLabel ? `trace in session ${sessionLabel}` : "trace in directory",
|
|
450
|
+
matchedFields: sessionLabel ? ["run", "session"] : ["run"],
|
|
451
|
+
filePath: m.filePath,
|
|
452
|
+
...sessionLabel ? { sessionId: sessionLabel } : {}
|
|
450
453
|
});
|
|
451
454
|
}
|
|
452
455
|
return results.slice(0, limit);
|
|
@@ -585,6 +588,571 @@ async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
|
585
588
|
}
|
|
586
589
|
return metas;
|
|
587
590
|
}
|
|
591
|
+
|
|
592
|
+
// packages/core/src/sessions/metadata.ts
|
|
593
|
+
function isNonEmptyString(value) {
|
|
594
|
+
return typeof value === "string" && value.trim() !== "";
|
|
595
|
+
}
|
|
596
|
+
function finitePositiveInt(value) {
|
|
597
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
|
|
598
|
+
return void 0;
|
|
599
|
+
}
|
|
600
|
+
return Math.trunc(value);
|
|
601
|
+
}
|
|
602
|
+
function extractSessionWorkflowMetadata(record) {
|
|
603
|
+
if (!record) return void 0;
|
|
604
|
+
const out = {};
|
|
605
|
+
let found = false;
|
|
606
|
+
const assignString = (key, value) => {
|
|
607
|
+
if (isNonEmptyString(value)) {
|
|
608
|
+
out[key] = value.trim();
|
|
609
|
+
found = true;
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
assignString("sessionId", record.sessionId);
|
|
613
|
+
assignString("conversationId", record.conversationId);
|
|
614
|
+
assignString("groupId", record.groupId);
|
|
615
|
+
assignString("parentGroupId", record.parentGroupId);
|
|
616
|
+
assignString("retryOf", record.retryOf);
|
|
617
|
+
assignString("retryReason", record.retryReason);
|
|
618
|
+
assignString("handoffFrom", record.handoffFrom);
|
|
619
|
+
assignString("handoffTo", record.handoffTo);
|
|
620
|
+
assignString("subAgentId", record.subAgentId);
|
|
621
|
+
assignString("subAgentName", record.subAgentName);
|
|
622
|
+
assignString("jobId", record.jobId);
|
|
623
|
+
assignString("queueName", record.queueName);
|
|
624
|
+
assignString("workflowName", record.workflowName);
|
|
625
|
+
assignString("workflowStep", record.workflowStep);
|
|
626
|
+
assignString("toolCallId", record.toolCallId);
|
|
627
|
+
assignString("mcpToolCallId", record.mcpToolCallId);
|
|
628
|
+
assignString("linkedStepId", record.linkedStepId);
|
|
629
|
+
assignString("correlationId", record.correlationId);
|
|
630
|
+
assignString("requestId", record.requestId);
|
|
631
|
+
assignString("decisionId", record.decisionId);
|
|
632
|
+
const attempt = finitePositiveInt(record.attempt);
|
|
633
|
+
if (attempt !== void 0) {
|
|
634
|
+
out.attempt = attempt;
|
|
635
|
+
found = true;
|
|
636
|
+
}
|
|
637
|
+
return found ? out : void 0;
|
|
638
|
+
}
|
|
639
|
+
function sessionKeyForRun(meta, options) {
|
|
640
|
+
if (meta?.sessionId) return meta.sessionId;
|
|
641
|
+
if (options?.correlateByGroupId && meta?.groupId) {
|
|
642
|
+
return `group:${meta.groupId}`;
|
|
643
|
+
}
|
|
644
|
+
return void 0;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// packages/core/src/sessions/types.ts
|
|
648
|
+
var SESSION_WORKFLOW_KEYS = [
|
|
649
|
+
"sessionId",
|
|
650
|
+
"conversationId",
|
|
651
|
+
"groupId",
|
|
652
|
+
"parentGroupId",
|
|
653
|
+
"attempt",
|
|
654
|
+
"retryOf",
|
|
655
|
+
"retryReason",
|
|
656
|
+
"handoffFrom",
|
|
657
|
+
"handoffTo",
|
|
658
|
+
"subAgentId",
|
|
659
|
+
"subAgentName",
|
|
660
|
+
"jobId",
|
|
661
|
+
"queueName",
|
|
662
|
+
"workflowName",
|
|
663
|
+
"workflowStep",
|
|
664
|
+
"toolCallId",
|
|
665
|
+
"mcpToolCallId",
|
|
666
|
+
"linkedStepId",
|
|
667
|
+
"correlationId",
|
|
668
|
+
"requestId",
|
|
669
|
+
"decisionId"
|
|
670
|
+
];
|
|
671
|
+
|
|
672
|
+
// packages/core/src/sessions/load.ts
|
|
673
|
+
async function enrichSessionRunRecord(meta) {
|
|
674
|
+
let metadata;
|
|
675
|
+
try {
|
|
676
|
+
const events = await readTraceEventsFromFile(meta.filePath);
|
|
677
|
+
for (const event of events) {
|
|
678
|
+
if (event.event !== "run_started") continue;
|
|
679
|
+
if (event.metadata && typeof event.metadata === "object") {
|
|
680
|
+
metadata = event.metadata;
|
|
681
|
+
}
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
686
|
+
return {
|
|
687
|
+
runId: meta.runId,
|
|
688
|
+
name: meta.name,
|
|
689
|
+
status: meta.status,
|
|
690
|
+
startedAt: meta.startedAt,
|
|
691
|
+
endedAt: meta.endedAt,
|
|
692
|
+
durationMs: meta.durationMs,
|
|
693
|
+
filePath: meta.filePath,
|
|
694
|
+
metadata
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
async function loadSessionRunRecords(metas) {
|
|
698
|
+
const out = [];
|
|
699
|
+
for (const meta of metas) {
|
|
700
|
+
out.push(await enrichSessionRunRecord(meta));
|
|
701
|
+
}
|
|
702
|
+
return out;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// packages/core/src/sessions/scope.ts
|
|
706
|
+
function metaToSessionRunRecord(meta) {
|
|
707
|
+
return {
|
|
708
|
+
runId: meta.runId,
|
|
709
|
+
name: meta.name,
|
|
710
|
+
status: meta.status,
|
|
711
|
+
startedAt: meta.startedAt,
|
|
712
|
+
endedAt: meta.endedAt,
|
|
713
|
+
durationMs: meta.durationMs,
|
|
714
|
+
filePath: meta.filePath,
|
|
715
|
+
metadata: void 0
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
function filterMetasBySessionScope(metas, records, options) {
|
|
719
|
+
const sessionId = options.sessionId?.trim();
|
|
720
|
+
const groupId = options.groupId?.trim();
|
|
721
|
+
const warnings = [];
|
|
722
|
+
if (sessionId) {
|
|
723
|
+
const index = buildSessionIndex(records, {
|
|
724
|
+
correlateByGroupId: options.correlateByGroupId === true
|
|
725
|
+
});
|
|
726
|
+
warnings.push(...index.warnings);
|
|
727
|
+
const session = index.sessions.find((item) => item.sessionId === sessionId);
|
|
728
|
+
if (!session) {
|
|
729
|
+
return {
|
|
730
|
+
metas: [],
|
|
731
|
+
scopeLabel: sessionId,
|
|
732
|
+
scopeKind: "session",
|
|
733
|
+
runIds: [],
|
|
734
|
+
warnings,
|
|
735
|
+
notFound: true
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
const runIdSet = new Set(session.runIds);
|
|
739
|
+
const filtered = metas.filter((meta) => runIdSet.has(meta.runId));
|
|
740
|
+
return {
|
|
741
|
+
metas: filtered,
|
|
742
|
+
scopeLabel: sessionId,
|
|
743
|
+
scopeKind: "session",
|
|
744
|
+
runIds: session.runIds,
|
|
745
|
+
warnings,
|
|
746
|
+
notFound: false
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
if (groupId) {
|
|
750
|
+
const runIds = records.filter((run) => extractSessionWorkflowMetadata(run.metadata)?.groupId === groupId).map((run) => run.runId).sort();
|
|
751
|
+
if (runIds.length === 0) {
|
|
752
|
+
return {
|
|
753
|
+
metas: [],
|
|
754
|
+
scopeLabel: groupId,
|
|
755
|
+
scopeKind: "group",
|
|
756
|
+
runIds: [],
|
|
757
|
+
warnings,
|
|
758
|
+
notFound: true
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
const runIdSet = new Set(runIds);
|
|
762
|
+
return {
|
|
763
|
+
metas: metas.filter((meta) => runIdSet.has(meta.runId)),
|
|
764
|
+
scopeLabel: groupId,
|
|
765
|
+
scopeKind: "group",
|
|
766
|
+
runIds,
|
|
767
|
+
warnings,
|
|
768
|
+
notFound: false
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
return {
|
|
772
|
+
metas: [...metas],
|
|
773
|
+
scopeLabel: "",
|
|
774
|
+
scopeKind: "session",
|
|
775
|
+
runIds: [],
|
|
776
|
+
warnings,
|
|
777
|
+
notFound: false
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
function traceMetasToSessionRunRecords(metas) {
|
|
781
|
+
return metas.map(metaToSessionRunRecord);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// packages/core/src/sessions/cohort.ts
|
|
785
|
+
function groupSessionCohorts(runs, options = {}) {
|
|
786
|
+
const groupBy = options.groupBy ?? "session";
|
|
787
|
+
const cohorts = [];
|
|
788
|
+
if (groupBy === "session") {
|
|
789
|
+
const index = buildSessionIndex(runs, options);
|
|
790
|
+
for (const session of index.sessions) {
|
|
791
|
+
cohorts.push({
|
|
792
|
+
key: session.sessionId,
|
|
793
|
+
kind: "session",
|
|
794
|
+
runIds: [...session.runIds]
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
if (index.unscopedRunIds.length > 0) {
|
|
798
|
+
cohorts.push({
|
|
799
|
+
key: "__unscoped__",
|
|
800
|
+
kind: "session",
|
|
801
|
+
runIds: [...index.unscopedRunIds]
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
return cohorts.sort((a, b) => a.key.localeCompare(b.key));
|
|
805
|
+
}
|
|
806
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
807
|
+
const unscoped = [];
|
|
808
|
+
for (const run of runs) {
|
|
809
|
+
const groupId = extractSessionWorkflowMetadata(run.metadata)?.groupId;
|
|
810
|
+
if (!groupId) {
|
|
811
|
+
unscoped.push(run.runId);
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
const bucket = byGroup.get(groupId) ?? [];
|
|
815
|
+
bucket.push(run.runId);
|
|
816
|
+
byGroup.set(groupId, bucket);
|
|
817
|
+
}
|
|
818
|
+
for (const [key, runIds] of [...byGroup.entries()].sort(
|
|
819
|
+
([a], [b]) => a.localeCompare(b)
|
|
820
|
+
)) {
|
|
821
|
+
cohorts.push({
|
|
822
|
+
key,
|
|
823
|
+
kind: "group",
|
|
824
|
+
runIds: runIds.sort()
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
if (unscoped.length > 0) {
|
|
828
|
+
cohorts.push({
|
|
829
|
+
key: "__unscoped__",
|
|
830
|
+
kind: "group",
|
|
831
|
+
runIds: unscoped.sort()
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
return cohorts;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// packages/core/src/sessions/checks.ts
|
|
838
|
+
function emptySummary() {
|
|
839
|
+
return { passed: 0, failed: 0, warnings: 0, errors: 0 };
|
|
840
|
+
}
|
|
841
|
+
function mergeSummary(target, source) {
|
|
842
|
+
return {
|
|
843
|
+
passed: target.passed + source.passed,
|
|
844
|
+
failed: target.failed + source.failed,
|
|
845
|
+
warnings: target.warnings + source.warnings,
|
|
846
|
+
errors: target.errors + source.errors
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
function sessionDiagnostic(code, message) {
|
|
850
|
+
return { code, message, severity: "error" };
|
|
851
|
+
}
|
|
852
|
+
function aggregateSessionCheckResults(perRun, scope) {
|
|
853
|
+
if (scope.notFound) {
|
|
854
|
+
return {
|
|
855
|
+
ok: false,
|
|
856
|
+
status: "error",
|
|
857
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
858
|
+
scopeKind: scope.scopeKind,
|
|
859
|
+
scopeLabel: scope.scopeLabel,
|
|
860
|
+
runIds: [],
|
|
861
|
+
runResults: [],
|
|
862
|
+
summary: { ...emptySummary(), errors: 1 },
|
|
863
|
+
findings: [],
|
|
864
|
+
diagnostics: [
|
|
865
|
+
sessionDiagnostic(
|
|
866
|
+
"AI_CHECK_INVALID_ARGUMENTS",
|
|
867
|
+
`${scope.scopeKind} not found: ${scope.scopeLabel}`
|
|
868
|
+
)
|
|
869
|
+
],
|
|
870
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
if (scope.empty || perRun.length === 0) {
|
|
874
|
+
return {
|
|
875
|
+
ok: false,
|
|
876
|
+
status: "error",
|
|
877
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
878
|
+
scopeKind: scope.scopeKind,
|
|
879
|
+
scopeLabel: scope.scopeLabel,
|
|
880
|
+
runIds: scope.runIds,
|
|
881
|
+
runResults: [],
|
|
882
|
+
summary: { ...emptySummary(), errors: 1 },
|
|
883
|
+
findings: [],
|
|
884
|
+
diagnostics: [
|
|
885
|
+
sessionDiagnostic(
|
|
886
|
+
"AI_CHECK_TRACE_UNREADABLE",
|
|
887
|
+
`No readable traces in ${scope.scopeKind}: ${scope.scopeLabel}`
|
|
888
|
+
)
|
|
889
|
+
],
|
|
890
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
let summary = emptySummary();
|
|
894
|
+
const findings = [];
|
|
895
|
+
const diagnostics = [];
|
|
896
|
+
const runResults = [];
|
|
897
|
+
for (const result of perRun) {
|
|
898
|
+
summary = mergeSummary(summary, result.summary);
|
|
899
|
+
findings.push(...result.findings);
|
|
900
|
+
diagnostics.push(...result.diagnostics);
|
|
901
|
+
if (result.runId) {
|
|
902
|
+
runResults.push({ runId: result.runId, status: result.status });
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
runResults.sort((a, b) => a.runId.localeCompare(b.runId));
|
|
906
|
+
findings.sort((a, b) => {
|
|
907
|
+
const runCmp = (a.evidence[0]?.runId ?? "").localeCompare(
|
|
908
|
+
b.evidence[0]?.runId ?? ""
|
|
909
|
+
);
|
|
910
|
+
if (runCmp !== 0) return runCmp;
|
|
911
|
+
return a.ruleId.localeCompare(b.ruleId);
|
|
912
|
+
});
|
|
913
|
+
const hasErrors = diagnostics.some((item) => item.severity === "error");
|
|
914
|
+
const status = hasErrors ? "error" : summary.failed > 0 ? "fail" : "pass";
|
|
915
|
+
return {
|
|
916
|
+
ok: status === "pass",
|
|
917
|
+
status,
|
|
918
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
919
|
+
scopeKind: scope.scopeKind,
|
|
920
|
+
scopeLabel: scope.scopeLabel,
|
|
921
|
+
runIds: scope.runIds,
|
|
922
|
+
runResults,
|
|
923
|
+
summary,
|
|
924
|
+
findings,
|
|
925
|
+
diagnostics,
|
|
926
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// packages/core/src/sessions/index.ts
|
|
931
|
+
function compareRuns(a, b) {
|
|
932
|
+
const aStart = a.startedAt ?? 0;
|
|
933
|
+
const bStart = b.startedAt ?? 0;
|
|
934
|
+
if (aStart !== bStart) return aStart - bStart;
|
|
935
|
+
return a.runId.localeCompare(b.runId);
|
|
936
|
+
}
|
|
937
|
+
function buildGroups(runIds, metaByRunId) {
|
|
938
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
939
|
+
for (const runId of runIds) {
|
|
940
|
+
const groupId = metaByRunId.get(runId)?.groupId;
|
|
941
|
+
if (!groupId) continue;
|
|
942
|
+
const existing = byGroup.get(groupId);
|
|
943
|
+
if (existing) {
|
|
944
|
+
existing.runIds.push(runId);
|
|
945
|
+
} else {
|
|
946
|
+
byGroup.set(groupId, {
|
|
947
|
+
groupId,
|
|
948
|
+
parentGroupId: metaByRunId.get(runId)?.parentGroupId,
|
|
949
|
+
runIds: [runId]
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return [...byGroup.values()].map((group) => ({
|
|
954
|
+
...group,
|
|
955
|
+
runIds: [...group.runIds].sort((a, b) => a.localeCompare(b))
|
|
956
|
+
}));
|
|
957
|
+
}
|
|
958
|
+
function buildHandoffs(runIds, metaByRunId, warnings, sessionId) {
|
|
959
|
+
const edges = [];
|
|
960
|
+
const seen = /* @__PURE__ */ new Set();
|
|
961
|
+
const pushEdge = (edge) => {
|
|
962
|
+
const key = `${edge.from}->${edge.to}:${edge.confidence}`;
|
|
963
|
+
if (seen.has(key)) return;
|
|
964
|
+
seen.add(key);
|
|
965
|
+
edges.push(edge);
|
|
966
|
+
};
|
|
967
|
+
for (const runId of runIds) {
|
|
968
|
+
const meta = metaByRunId.get(runId);
|
|
969
|
+
if (!meta) continue;
|
|
970
|
+
if (meta.handoffFrom && meta.handoffTo) {
|
|
971
|
+
pushEdge({
|
|
972
|
+
from: meta.handoffFrom,
|
|
973
|
+
to: meta.handoffTo,
|
|
974
|
+
source: "manual",
|
|
975
|
+
confidence: "explicit"
|
|
976
|
+
});
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (meta.handoffFrom) {
|
|
980
|
+
pushEdge({
|
|
981
|
+
from: meta.handoffFrom,
|
|
982
|
+
to: runId,
|
|
983
|
+
source: "manual",
|
|
984
|
+
confidence: "explicit"
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
if (meta.handoffTo) {
|
|
988
|
+
pushEdge({
|
|
989
|
+
from: runId,
|
|
990
|
+
to: meta.handoffTo,
|
|
991
|
+
source: "manual",
|
|
992
|
+
confidence: "explicit"
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
if (meta.subAgentId && meta.parentGroupId && !meta.handoffFrom && !meta.handoffTo) {
|
|
996
|
+
pushEdge({
|
|
997
|
+
from: meta.parentGroupId,
|
|
998
|
+
to: meta.subAgentId,
|
|
999
|
+
source: "inferred",
|
|
1000
|
+
confidence: "correlated"
|
|
1001
|
+
});
|
|
1002
|
+
warnings.push({
|
|
1003
|
+
code: "ambiguous-handoff-endpoints",
|
|
1004
|
+
message: "Handoff inferred from parentGroupId and subAgentId without explicit handoffFrom/handoffTo.",
|
|
1005
|
+
runId,
|
|
1006
|
+
sessionId
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
return edges.sort((a, b) => {
|
|
1011
|
+
const from = a.from.localeCompare(b.from);
|
|
1012
|
+
if (from !== 0) return from;
|
|
1013
|
+
return a.to.localeCompare(b.to);
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
function buildRetries(runIds, metaByRunId, warnings, sessionId) {
|
|
1017
|
+
const retries = [];
|
|
1018
|
+
for (const runId of runIds) {
|
|
1019
|
+
const meta = metaByRunId.get(runId);
|
|
1020
|
+
if (!meta) continue;
|
|
1021
|
+
if (meta.retryOf) {
|
|
1022
|
+
retries.push({
|
|
1023
|
+
runId,
|
|
1024
|
+
retryOf: meta.retryOf,
|
|
1025
|
+
attempt: meta.attempt,
|
|
1026
|
+
source: "manual",
|
|
1027
|
+
confidence: "explicit"
|
|
1028
|
+
});
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
if (meta.attempt !== void 0 && meta.attempt > 1) {
|
|
1032
|
+
retries.push({
|
|
1033
|
+
runId,
|
|
1034
|
+
attempt: meta.attempt,
|
|
1035
|
+
source: "inferred",
|
|
1036
|
+
confidence: "correlated"
|
|
1037
|
+
});
|
|
1038
|
+
warnings.push({
|
|
1039
|
+
code: "ambiguous-retry-link",
|
|
1040
|
+
message: "attempt > 1 without retryOf; retry link is correlated only.",
|
|
1041
|
+
runId,
|
|
1042
|
+
sessionId
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return retries.sort((a, b) => a.runId.localeCompare(b.runId));
|
|
1047
|
+
}
|
|
1048
|
+
function buildCriticalPath(runs, handoffs) {
|
|
1049
|
+
const runById = new Map(runs.map((run) => [run.runId, run]));
|
|
1050
|
+
const explicitTargets = new Set(
|
|
1051
|
+
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.to)
|
|
1052
|
+
);
|
|
1053
|
+
const explicitSources = new Set(
|
|
1054
|
+
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
1055
|
+
);
|
|
1056
|
+
const ordered = [...runs].sort(compareRuns);
|
|
1057
|
+
const path = [];
|
|
1058
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1059
|
+
const pushRun = (run, confidence, source) => {
|
|
1060
|
+
if (visited.has(run.runId)) return;
|
|
1061
|
+
visited.add(run.runId);
|
|
1062
|
+
path.push({
|
|
1063
|
+
runId: run.runId,
|
|
1064
|
+
name: run.name,
|
|
1065
|
+
startedAt: run.startedAt,
|
|
1066
|
+
durationMs: run.durationMs,
|
|
1067
|
+
confidence,
|
|
1068
|
+
source
|
|
1069
|
+
});
|
|
1070
|
+
};
|
|
1071
|
+
for (const edge of handoffs) {
|
|
1072
|
+
if (edge.confidence !== "explicit") continue;
|
|
1073
|
+
const fromRun = [...runById.values()].find(
|
|
1074
|
+
(run) => run.runId === edge.from || metaRunIdMatches(run, edge.from, runById)
|
|
1075
|
+
);
|
|
1076
|
+
const toRun = [...runById.values()].find(
|
|
1077
|
+
(run) => run.runId === edge.to || metaRunIdMatches(run, edge.to, runById)
|
|
1078
|
+
);
|
|
1079
|
+
if (fromRun) pushRun(fromRun, "explicit", "manual");
|
|
1080
|
+
if (toRun) pushRun(toRun, "explicit", "manual");
|
|
1081
|
+
}
|
|
1082
|
+
for (const run of ordered) {
|
|
1083
|
+
if (visited.has(run.runId)) continue;
|
|
1084
|
+
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
1085
|
+
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
1086
|
+
}
|
|
1087
|
+
return path;
|
|
1088
|
+
}
|
|
1089
|
+
function metaRunIdMatches(run, token, runById) {
|
|
1090
|
+
const meta = extractSessionWorkflowMetadata(run.metadata);
|
|
1091
|
+
return meta?.subAgentId === token || meta?.groupId === token || runById.has(token);
|
|
1092
|
+
}
|
|
1093
|
+
function buildSessionIndex(inputRuns, options = {}) {
|
|
1094
|
+
const warnings = [];
|
|
1095
|
+
const runs = [...inputRuns].sort(compareRuns);
|
|
1096
|
+
const metaByRunId = /* @__PURE__ */ new Map();
|
|
1097
|
+
for (const run of runs) {
|
|
1098
|
+
metaByRunId.set(run.runId, extractSessionWorkflowMetadata(run.metadata));
|
|
1099
|
+
}
|
|
1100
|
+
const sessionsByKey = /* @__PURE__ */ new Map();
|
|
1101
|
+
const unscopedRunIds = [];
|
|
1102
|
+
for (const run of runs) {
|
|
1103
|
+
const meta = metaByRunId.get(run.runId);
|
|
1104
|
+
const key = sessionKeyForRun(meta, {
|
|
1105
|
+
correlateByGroupId: options.correlateByGroupId === true
|
|
1106
|
+
});
|
|
1107
|
+
if (!key) {
|
|
1108
|
+
unscopedRunIds.push(run.runId);
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const bucket = sessionsByKey.get(key) ?? [];
|
|
1112
|
+
bucket.push(run);
|
|
1113
|
+
sessionsByKey.set(key, bucket);
|
|
1114
|
+
}
|
|
1115
|
+
const sessions = [...sessionsByKey.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([sessionId, sessionRuns]) => {
|
|
1116
|
+
const runIds = sessionRuns.map((run) => run.runId).sort();
|
|
1117
|
+
const handoffs = buildHandoffs(runIds, metaByRunId, warnings, sessionId);
|
|
1118
|
+
const retries = buildRetries(runIds, metaByRunId, warnings, sessionId);
|
|
1119
|
+
const groups = buildGroups(runIds, metaByRunId);
|
|
1120
|
+
const criticalPath = buildCriticalPath(sessionRuns, handoffs);
|
|
1121
|
+
const confidences = new Set(handoffs.map((edge) => edge.confidence));
|
|
1122
|
+
if (confidences.has("explicit") && confidences.has("correlated")) {
|
|
1123
|
+
warnings.push({
|
|
1124
|
+
code: "mixed-confidence-group",
|
|
1125
|
+
message: "Session aggregates explicit and correlated handoff edges.",
|
|
1126
|
+
sessionId
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
return {
|
|
1130
|
+
sessionId,
|
|
1131
|
+
runIds,
|
|
1132
|
+
groups,
|
|
1133
|
+
handoffs,
|
|
1134
|
+
retries,
|
|
1135
|
+
criticalPath
|
|
1136
|
+
};
|
|
1137
|
+
});
|
|
1138
|
+
if (sessions.length === 0 && runs.length > 0) {
|
|
1139
|
+
warnings.push({
|
|
1140
|
+
code: "missing-session-id",
|
|
1141
|
+
message: "No sessionId (or correlated groupId) found on input runs."
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
warnings.sort((a, b) => {
|
|
1145
|
+
const code = a.code.localeCompare(b.code);
|
|
1146
|
+
if (code !== 0) return code;
|
|
1147
|
+
return (a.runId ?? "").localeCompare(b.runId ?? "");
|
|
1148
|
+
});
|
|
1149
|
+
return {
|
|
1150
|
+
runs,
|
|
1151
|
+
sessions,
|
|
1152
|
+
unscopedRunIds: unscopedRunIds.sort(),
|
|
1153
|
+
warnings
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
588
1156
|
var KNOWN_EVENTS = /* @__PURE__ */ new Set([
|
|
589
1157
|
"run_started",
|
|
590
1158
|
"run_completed",
|
|
@@ -629,6 +1197,6 @@ async function isAgentInspectTrace(filePath) {
|
|
|
629
1197
|
}
|
|
630
1198
|
}
|
|
631
1199
|
|
|
632
|
-
export { buildLocalExplanation, buildTraceStats, filterTraces, isAgentInspectTrace, loadTraceMetadataList, parseDurationFilter, renderTraceStats, searchTraces };
|
|
1200
|
+
export { SESSION_WORKFLOW_KEYS, aggregateSessionCheckResults, buildLocalExplanation, buildSessionIndex, buildTraceStats, enrichSessionRunRecord, extractSessionWorkflowMetadata, filterMetasBySessionScope, filterTraces, groupSessionCohorts, isAgentInspectTrace, loadSessionRunRecords, loadTraceMetadataList, parseDurationFilter, renderTraceStats, searchTraces, sessionKeyForRun, traceMetasToSessionRunRecords };
|
|
633
1201
|
//# sourceMappingURL=advanced.mjs.map
|
|
634
1202
|
//# sourceMappingURL=advanced.mjs.map
|