@vainplex/openclaw-cortex 0.3.1 → 0.4.1
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/README.md +141 -46
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +3 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/hooks.d.ts.map +1 -1
- package/dist/src/hooks.js +13 -1
- package/dist/src/hooks.js.map +1 -1
- package/dist/src/trace-analyzer/analyzer.d.ts +59 -0
- package/dist/src/trace-analyzer/analyzer.d.ts.map +1 -0
- package/dist/src/trace-analyzer/analyzer.js +175 -0
- package/dist/src/trace-analyzer/analyzer.js.map +1 -0
- package/dist/src/trace-analyzer/chain-reconstructor.d.ts +52 -0
- package/dist/src/trace-analyzer/chain-reconstructor.d.ts.map +1 -0
- package/dist/src/trace-analyzer/chain-reconstructor.js +190 -0
- package/dist/src/trace-analyzer/chain-reconstructor.js.map +1 -0
- package/dist/src/trace-analyzer/classifier.d.ts +28 -0
- package/dist/src/trace-analyzer/classifier.d.ts.map +1 -0
- package/dist/src/trace-analyzer/classifier.js +271 -0
- package/dist/src/trace-analyzer/classifier.js.map +1 -0
- package/dist/src/trace-analyzer/config.d.ts +78 -0
- package/dist/src/trace-analyzer/config.d.ts.map +1 -0
- package/dist/src/trace-analyzer/config.js +161 -0
- package/dist/src/trace-analyzer/config.js.map +1 -0
- package/dist/src/trace-analyzer/events.d.ts +70 -0
- package/dist/src/trace-analyzer/events.d.ts.map +1 -0
- package/dist/src/trace-analyzer/events.js +228 -0
- package/dist/src/trace-analyzer/events.js.map +1 -0
- package/dist/src/trace-analyzer/hooks.d.ts +22 -0
- package/dist/src/trace-analyzer/hooks.d.ts.map +1 -0
- package/dist/src/trace-analyzer/hooks.js +151 -0
- package/dist/src/trace-analyzer/hooks.js.map +1 -0
- package/dist/src/trace-analyzer/index.d.ts +32 -0
- package/dist/src/trace-analyzer/index.d.ts.map +1 -0
- package/dist/src/trace-analyzer/index.js +30 -0
- package/dist/src/trace-analyzer/index.js.map +1 -0
- package/dist/src/trace-analyzer/nats-trace-source.d.ts +10 -0
- package/dist/src/trace-analyzer/nats-trace-source.d.ts.map +1 -0
- package/dist/src/trace-analyzer/nats-trace-source.js +151 -0
- package/dist/src/trace-analyzer/nats-trace-source.js.map +1 -0
- package/dist/src/trace-analyzer/output-generator.d.ts +29 -0
- package/dist/src/trace-analyzer/output-generator.d.ts.map +1 -0
- package/dist/src/trace-analyzer/output-generator.js +142 -0
- package/dist/src/trace-analyzer/output-generator.js.map +1 -0
- package/dist/src/trace-analyzer/redactor.d.ts +14 -0
- package/dist/src/trace-analyzer/redactor.d.ts.map +1 -0
- package/dist/src/trace-analyzer/redactor.js +137 -0
- package/dist/src/trace-analyzer/redactor.js.map +1 -0
- package/dist/src/trace-analyzer/report.d.ts +93 -0
- package/dist/src/trace-analyzer/report.d.ts.map +1 -0
- package/dist/src/trace-analyzer/report.js +108 -0
- package/dist/src/trace-analyzer/report.js.map +1 -0
- package/dist/src/trace-analyzer/signals/correction.d.ts +11 -0
- package/dist/src/trace-analyzer/signals/correction.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/correction.js +54 -0
- package/dist/src/trace-analyzer/signals/correction.js.map +1 -0
- package/dist/src/trace-analyzer/signals/dissatisfied.d.ts +11 -0
- package/dist/src/trace-analyzer/signals/dissatisfied.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/dissatisfied.js +60 -0
- package/dist/src/trace-analyzer/signals/dissatisfied.js.map +1 -0
- package/dist/src/trace-analyzer/signals/doom-loop.d.ts +13 -0
- package/dist/src/trace-analyzer/signals/doom-loop.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/doom-loop.js +156 -0
- package/dist/src/trace-analyzer/signals/doom-loop.js.map +1 -0
- package/dist/src/trace-analyzer/signals/hallucination.d.ts +11 -0
- package/dist/src/trace-analyzer/signals/hallucination.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/hallucination.js +67 -0
- package/dist/src/trace-analyzer/signals/hallucination.js.map +1 -0
- package/dist/src/trace-analyzer/signals/index.d.ts +16 -0
- package/dist/src/trace-analyzer/signals/index.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/index.js +92 -0
- package/dist/src/trace-analyzer/signals/index.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/index.d.ts +4 -0
- package/dist/src/trace-analyzer/signals/lang/index.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/index.js +2 -0
- package/dist/src/trace-analyzer/signals/lang/index.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/registry.d.ts +41 -0
- package/dist/src/trace-analyzer/signals/lang/registry.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/registry.js +111 -0
- package/dist/src/trace-analyzer/signals/lang/registry.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-de.d.ts +10 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-de.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-de.js +60 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-de.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-en.d.ts +4 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-en.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-en.js +55 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-en.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-es.d.ts +9 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-es.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-es.js +59 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-es.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-fr.d.ts +10 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-fr.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-fr.js +60 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-fr.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-it.d.ts +9 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-it.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-it.js +59 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-it.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ja.d.ts +9 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ja.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ja.js +59 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ja.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ko.d.ts +9 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ko.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ko.js +59 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ko.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-pt.d.ts +9 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-pt.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-pt.js +59 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-pt.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ru.d.ts +9 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ru.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ru.js +59 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-ru.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-zh.d.ts +8 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-zh.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-zh.js +58 -0
- package/dist/src/trace-analyzer/signals/lang/signal-lang-zh.js.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/types.d.ts +76 -0
- package/dist/src/trace-analyzer/signals/lang/types.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/lang/types.js +12 -0
- package/dist/src/trace-analyzer/signals/lang/types.js.map +1 -0
- package/dist/src/trace-analyzer/signals/repeat-fail.d.ts +24 -0
- package/dist/src/trace-analyzer/signals/repeat-fail.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/repeat-fail.js +112 -0
- package/dist/src/trace-analyzer/signals/repeat-fail.js.map +1 -0
- package/dist/src/trace-analyzer/signals/tool-fail.d.ts +10 -0
- package/dist/src/trace-analyzer/signals/tool-fail.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/tool-fail.js +99 -0
- package/dist/src/trace-analyzer/signals/tool-fail.js.map +1 -0
- package/dist/src/trace-analyzer/signals/types.d.ts +54 -0
- package/dist/src/trace-analyzer/signals/types.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/types.js +9 -0
- package/dist/src/trace-analyzer/signals/types.js.map +1 -0
- package/dist/src/trace-analyzer/signals/unverified-claim.d.ts +11 -0
- package/dist/src/trace-analyzer/signals/unverified-claim.d.ts.map +1 -0
- package/dist/src/trace-analyzer/signals/unverified-claim.js +84 -0
- package/dist/src/trace-analyzer/signals/unverified-claim.js.map +1 -0
- package/dist/src/trace-analyzer/trace-source.d.ts +31 -0
- package/dist/src/trace-analyzer/trace-source.d.ts.map +1 -0
- package/dist/src/trace-analyzer/trace-source.js +5 -0
- package/dist/src/trace-analyzer/trace-source.js.map +1 -0
- package/dist/src/trace-analyzer/util.d.ts +17 -0
- package/dist/src/trace-analyzer/util.d.ts.map +1 -0
- package/dist/src/trace-analyzer/util.js +26 -0
- package/dist/src/trace-analyzer/util.js.map +1 -0
- package/dist/src/types.d.ts +2 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/openclaw.plugin.json +184 -2
- package/package.json +7 -4
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Finding, SignalId, Severity } from "./signals/types.js";
|
|
2
|
+
import type { ConversationChain } from "./chain-reconstructor.js";
|
|
3
|
+
import type { GeneratedOutput } from "./output-generator.js";
|
|
4
|
+
/** Summary statistics for an analysis run. */
|
|
5
|
+
export type RunStats = {
|
|
6
|
+
/** Run start timestamp (ms). */
|
|
7
|
+
startedAt: number;
|
|
8
|
+
/** Run end timestamp (ms). */
|
|
9
|
+
completedAt: number;
|
|
10
|
+
/** Total events fetched from trace source. */
|
|
11
|
+
eventsProcessed: number;
|
|
12
|
+
/** Conversation chains reconstructed. */
|
|
13
|
+
chainsReconstructed: number;
|
|
14
|
+
/** Findings produced (stage 1). */
|
|
15
|
+
findingsDetected: number;
|
|
16
|
+
/** Findings classified by LLM (stage 2). */
|
|
17
|
+
findingsClassified: number;
|
|
18
|
+
/** Rules/policies/patterns generated (stage 3). */
|
|
19
|
+
outputsGenerated: number;
|
|
20
|
+
/** Time range of analyzed events. */
|
|
21
|
+
timeRange: {
|
|
22
|
+
startMs: number;
|
|
23
|
+
endMs: number;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
/** Per-signal breakdown. */
|
|
27
|
+
export type SignalStats = {
|
|
28
|
+
signal: SignalId;
|
|
29
|
+
count: number;
|
|
30
|
+
bySeverity: Partial<Record<Severity, number>>;
|
|
31
|
+
topAgents: Array<{
|
|
32
|
+
agent: string;
|
|
33
|
+
count: number;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
/** Rule effectiveness tracking (feedback loop). */
|
|
37
|
+
export type RuleEffectiveness = {
|
|
38
|
+
ruleId: string;
|
|
39
|
+
ruleText: string;
|
|
40
|
+
deployedAt: number;
|
|
41
|
+
failuresBefore: number;
|
|
42
|
+
failuresAfter: number;
|
|
43
|
+
effectivenessPercent: number;
|
|
44
|
+
status: "effective" | "marginal" | "ineffective" | "pending";
|
|
45
|
+
};
|
|
46
|
+
/** Processing state for incremental runs. */
|
|
47
|
+
export type ProcessingState = {
|
|
48
|
+
/** Last processed event timestamp (ms). */
|
|
49
|
+
lastProcessedTs: number;
|
|
50
|
+
/** Last processed NATS sequence (if applicable). */
|
|
51
|
+
lastProcessedSeq: number;
|
|
52
|
+
/** Total events processed across all runs. */
|
|
53
|
+
totalEventsProcessed: number;
|
|
54
|
+
/** Total findings across all runs. */
|
|
55
|
+
totalFindings: number;
|
|
56
|
+
/** ISO timestamp of this state update. */
|
|
57
|
+
updatedAt: string;
|
|
58
|
+
};
|
|
59
|
+
/** The complete analysis report. */
|
|
60
|
+
export type AnalysisReport = {
|
|
61
|
+
/** Schema version. */
|
|
62
|
+
version: 1;
|
|
63
|
+
/** ISO timestamp of report generation. */
|
|
64
|
+
generatedAt: string;
|
|
65
|
+
/** Run statistics. */
|
|
66
|
+
stats: RunStats;
|
|
67
|
+
/** Per-signal breakdown. */
|
|
68
|
+
signalStats: SignalStats[];
|
|
69
|
+
/** All findings (limited by config.traceAnalyzer.output.maxFindings). */
|
|
70
|
+
findings: Finding[];
|
|
71
|
+
/** Generated outputs (rules, policies, patterns). */
|
|
72
|
+
generatedOutputs: GeneratedOutput[];
|
|
73
|
+
/** Rule effectiveness (feedback loop). */
|
|
74
|
+
ruleEffectiveness: RuleEffectiveness[];
|
|
75
|
+
/** Processing state for incremental runs. */
|
|
76
|
+
processingState: ProcessingState;
|
|
77
|
+
};
|
|
78
|
+
/** Parameters for assembleReport(). */
|
|
79
|
+
export type AssembleReportParams = {
|
|
80
|
+
startedAt: number;
|
|
81
|
+
completedAt: number;
|
|
82
|
+
eventsProcessed: number;
|
|
83
|
+
chains: ConversationChain[];
|
|
84
|
+
findings: Finding[];
|
|
85
|
+
generatedOutputs: GeneratedOutput[];
|
|
86
|
+
previousState?: Partial<ProcessingState>;
|
|
87
|
+
ruleEffectiveness?: RuleEffectiveness[];
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Assemble the final AnalysisReport from all pipeline outputs.
|
|
91
|
+
*/
|
|
92
|
+
export declare function assembleReport(params: AssembleReportParams): AnalysisReport;
|
|
93
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/trace-analyzer/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Trace Analyzer — Report Assembly
|
|
3
|
+
// ============================================================
|
|
4
|
+
//
|
|
5
|
+
// Types for the analysis report and the assembleReport()
|
|
6
|
+
// function that combines all pipeline outputs into a final
|
|
7
|
+
// structured report. Implements R-019, R-020, R-040–R-042.
|
|
8
|
+
// ============================================================
|
|
9
|
+
/**
|
|
10
|
+
* Build the top-N agents list from a count map, sorted by count descending.
|
|
11
|
+
*/
|
|
12
|
+
function buildTopAgents(agentCounts, limit = 5) {
|
|
13
|
+
return [...agentCounts.entries()]
|
|
14
|
+
.sort((a, b) => b[1] - a[1])
|
|
15
|
+
.slice(0, limit)
|
|
16
|
+
.map(([agent, count]) => ({ agent, count }));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Compute per-signal statistics from findings.
|
|
20
|
+
*/
|
|
21
|
+
function computeSignalStats(findings) {
|
|
22
|
+
const statsMap = new Map();
|
|
23
|
+
for (const finding of findings) {
|
|
24
|
+
const sid = finding.signal.signal;
|
|
25
|
+
let entry = statsMap.get(sid);
|
|
26
|
+
if (!entry) {
|
|
27
|
+
entry = { count: 0, bySeverity: {}, agentCounts: new Map() };
|
|
28
|
+
statsMap.set(sid, entry);
|
|
29
|
+
}
|
|
30
|
+
entry.count++;
|
|
31
|
+
entry.bySeverity[finding.signal.severity] =
|
|
32
|
+
(entry.bySeverity[finding.signal.severity] ?? 0) + 1;
|
|
33
|
+
entry.agentCounts.set(finding.agent, (entry.agentCounts.get(finding.agent) ?? 0) + 1);
|
|
34
|
+
}
|
|
35
|
+
const result = [];
|
|
36
|
+
for (const [signal, entry] of statsMap) {
|
|
37
|
+
result.push({
|
|
38
|
+
signal,
|
|
39
|
+
count: entry.count,
|
|
40
|
+
bySeverity: entry.bySeverity,
|
|
41
|
+
topAgents: buildTopAgents(entry.agentCounts),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// Sort by count descending for readability
|
|
45
|
+
result.sort((a, b) => b.count - a.count);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Compute the time range from a set of chains.
|
|
50
|
+
*/
|
|
51
|
+
function computeTimeRange(chains) {
|
|
52
|
+
if (chains.length === 0)
|
|
53
|
+
return { startMs: 0, endMs: 0 };
|
|
54
|
+
let startMs = Infinity;
|
|
55
|
+
let endMs = -Infinity;
|
|
56
|
+
for (const chain of chains) {
|
|
57
|
+
if (chain.startTs < startMs)
|
|
58
|
+
startMs = chain.startTs;
|
|
59
|
+
if (chain.endTs > endMs)
|
|
60
|
+
endMs = chain.endTs;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
startMs: startMs === Infinity ? 0 : startMs,
|
|
64
|
+
endMs: endMs === -Infinity ? 0 : endMs,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build the processing state for incremental runs.
|
|
69
|
+
*/
|
|
70
|
+
function buildProcessingState(chains, eventsProcessed, findingsCount, previousState) {
|
|
71
|
+
const lastEvent = chains.reduce((max, c) => (c.endTs > max ? c.endTs : max), 0);
|
|
72
|
+
return {
|
|
73
|
+
lastProcessedTs: lastEvent || (previousState?.lastProcessedTs ?? 0),
|
|
74
|
+
lastProcessedSeq: previousState?.lastProcessedSeq ?? 0,
|
|
75
|
+
totalEventsProcessed: (previousState?.totalEventsProcessed ?? 0) + eventsProcessed,
|
|
76
|
+
totalFindings: (previousState?.totalFindings ?? 0) + findingsCount,
|
|
77
|
+
updatedAt: new Date().toISOString(),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Assemble the final AnalysisReport from all pipeline outputs.
|
|
82
|
+
*/
|
|
83
|
+
export function assembleReport(params) {
|
|
84
|
+
const { startedAt, completedAt, eventsProcessed, chains, findings, generatedOutputs, previousState, ruleEffectiveness, } = params;
|
|
85
|
+
const signalStats = computeSignalStats(findings);
|
|
86
|
+
const timeRange = computeTimeRange(chains);
|
|
87
|
+
const processingState = buildProcessingState(chains, eventsProcessed, findings.length, previousState);
|
|
88
|
+
return {
|
|
89
|
+
version: 1,
|
|
90
|
+
generatedAt: new Date().toISOString(),
|
|
91
|
+
stats: {
|
|
92
|
+
startedAt,
|
|
93
|
+
completedAt,
|
|
94
|
+
eventsProcessed,
|
|
95
|
+
chainsReconstructed: chains.length,
|
|
96
|
+
findingsDetected: findings.length,
|
|
97
|
+
findingsClassified: findings.filter(f => f.classification !== null).length,
|
|
98
|
+
outputsGenerated: generatedOutputs.length,
|
|
99
|
+
timeRange,
|
|
100
|
+
},
|
|
101
|
+
signalStats,
|
|
102
|
+
findings,
|
|
103
|
+
generatedOutputs,
|
|
104
|
+
ruleEffectiveness: ruleEffectiveness ?? [],
|
|
105
|
+
processingState,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/trace-analyzer/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ConversationChain } from "../chain-reconstructor.js";
|
|
2
|
+
import type { FailureSignal } from "./types.js";
|
|
3
|
+
import type { SignalPatternSet } from "./lang/registry.js";
|
|
4
|
+
/**
|
|
5
|
+
* Detect user corrections in a conversation chain.
|
|
6
|
+
*
|
|
7
|
+
* Pattern: msg.out (agent assertion) → msg.in (user correction)
|
|
8
|
+
* Exclusion: agent asked a question + user gave short negative → valid answer, not correction.
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectCorrections(chain: ConversationChain, patterns: SignalPatternSet): FailureSignal[];
|
|
11
|
+
//# sourceMappingURL=correction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correction.d.ts","sourceRoot":"","sources":["../../../../src/trace-analyzer/signals/correction.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAW3D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAmCvG"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SIG-CORRECTION — User Corrects Agent
|
|
3
|
+
// ============================================================
|
|
4
|
+
//
|
|
5
|
+
// Detects when a user corrects the agent after an agent response.
|
|
6
|
+
// Key: distinguishes corrections from valid "nein" answers
|
|
7
|
+
// (checks if preceding agent msg was a question).
|
|
8
|
+
// ============================================================
|
|
9
|
+
import { truncate, isQuestion } from "../util.js";
|
|
10
|
+
function isShortNegative(text, patterns) {
|
|
11
|
+
return patterns.correction.shortNegatives.some(p => p.test(text));
|
|
12
|
+
}
|
|
13
|
+
function matchesCorrection(text, patterns) {
|
|
14
|
+
return patterns.correction.indicators.some(p => p.test(text));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Detect user corrections in a conversation chain.
|
|
18
|
+
*
|
|
19
|
+
* Pattern: msg.out (agent assertion) → msg.in (user correction)
|
|
20
|
+
* Exclusion: agent asked a question + user gave short negative → valid answer, not correction.
|
|
21
|
+
*/
|
|
22
|
+
export function detectCorrections(chain, patterns) {
|
|
23
|
+
const signals = [];
|
|
24
|
+
const { events } = chain;
|
|
25
|
+
for (let i = 1; i < events.length; i++) {
|
|
26
|
+
const prev = events[i - 1];
|
|
27
|
+
const curr = events[i];
|
|
28
|
+
// Must be agent response followed by user message
|
|
29
|
+
if (prev.type !== "msg.out" || curr.type !== "msg.in")
|
|
30
|
+
continue;
|
|
31
|
+
const agentText = prev.payload.content ?? "";
|
|
32
|
+
const userText = curr.payload.content ?? "";
|
|
33
|
+
if (!userText)
|
|
34
|
+
continue;
|
|
35
|
+
if (!matchesCorrection(userText, patterns))
|
|
36
|
+
continue;
|
|
37
|
+
// Exclusion: if agent asked a question and user gave a short negative,
|
|
38
|
+
// it's a valid answer, not a correction.
|
|
39
|
+
if (isQuestion(agentText, patterns) && isShortNegative(userText, patterns))
|
|
40
|
+
continue;
|
|
41
|
+
signals.push({
|
|
42
|
+
signal: "SIG-CORRECTION",
|
|
43
|
+
severity: "medium",
|
|
44
|
+
eventRange: { start: i - 1, end: i },
|
|
45
|
+
summary: `User corrected agent after: '${truncate(agentText, 80)}'`,
|
|
46
|
+
evidence: {
|
|
47
|
+
agentMessage: truncate(agentText, 300),
|
|
48
|
+
userCorrection: truncate(userText, 300),
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return signals;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=correction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correction.js","sourceRoot":"","sources":["../../../../src/trace-analyzer/signals/correction.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,uCAAuC;AACvC,+DAA+D;AAC/D,EAAE;AACF,kEAAkE;AAClE,2DAA2D;AAC3D,kDAAkD;AAClD,+DAA+D;AAK/D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAElD,SAAS,eAAe,CAAC,IAAY,EAAE,QAA0B;IAC/D,OAAO,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY,EAAE,QAA0B;IACjE,OAAO,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAwB,EAAE,QAA0B;IACpF,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAEzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAEvB,kDAAkD;QAClD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QAEhE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAE5C,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAAE,SAAS;QAErD,uEAAuE;QACvE,yCAAyC;QACzC,IAAI,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAAE,SAAS;QAErF,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,gBAAgB;YACxB,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE;YACpC,OAAO,EAAE,gCAAgC,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,GAAG;YACnE,QAAQ,EAAE;gBACR,YAAY,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC;gBACtC,cAAc,EAAE,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC;aACxC;SACF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ConversationChain } from "../chain-reconstructor.js";
|
|
2
|
+
import type { FailureSignal } from "./types.js";
|
|
3
|
+
import type { SignalPatternSet } from "./lang/registry.js";
|
|
4
|
+
/**
|
|
5
|
+
* Detect dissatisfied session endings.
|
|
6
|
+
*
|
|
7
|
+
* Pattern: last user message matches dissatisfaction, no resolution follows,
|
|
8
|
+
* message is near the end of the chain (last 3 events).
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectDissatisfied(chain: ConversationChain, patterns: SignalPatternSet): FailureSignal[];
|
|
11
|
+
//# sourceMappingURL=dissatisfied.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dissatisfied.d.ts","sourceRoot":"","sources":["../../../../src/trace-analyzer/signals/dissatisfied.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAgC3D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAiBxG"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SIG-DISSATISFIED — Session Ends with User Frustration
|
|
3
|
+
// ============================================================
|
|
4
|
+
//
|
|
5
|
+
// Detects when the last user message in a chain expresses
|
|
6
|
+
// frustration or giving up, and the agent did not resolve it.
|
|
7
|
+
// ============================================================
|
|
8
|
+
import { truncate } from "../util.js";
|
|
9
|
+
function matchesDissatisfaction(text, patterns) {
|
|
10
|
+
// Satisfaction overrides dissatisfaction
|
|
11
|
+
if (patterns.dissatisfaction.satisfactionOverrides.some(p => p.test(text)))
|
|
12
|
+
return false;
|
|
13
|
+
return patterns.dissatisfaction.indicators.some(p => p.test(text));
|
|
14
|
+
}
|
|
15
|
+
/** Find the last msg.in index in a chain. Returns -1 if none. */
|
|
16
|
+
function findLastUserMessage(events) {
|
|
17
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
18
|
+
if (events[i].type === "msg.in")
|
|
19
|
+
return i;
|
|
20
|
+
}
|
|
21
|
+
return -1;
|
|
22
|
+
}
|
|
23
|
+
/** Check if the agent resolved dissatisfaction after the given index. */
|
|
24
|
+
function hasResolutionAfter(events, startIdx, patterns) {
|
|
25
|
+
for (let j = startIdx + 1; j < events.length; j++) {
|
|
26
|
+
if (events[j].type === "msg.out") {
|
|
27
|
+
const responseText = events[j].payload.content ?? "";
|
|
28
|
+
if (patterns.dissatisfaction.resolutionIndicators.some(p => p.test(responseText)))
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Detect dissatisfied session endings.
|
|
36
|
+
*
|
|
37
|
+
* Pattern: last user message matches dissatisfaction, no resolution follows,
|
|
38
|
+
* message is near the end of the chain (last 3 events).
|
|
39
|
+
*/
|
|
40
|
+
export function detectDissatisfied(chain, patterns) {
|
|
41
|
+
const { events } = chain;
|
|
42
|
+
const lastUserIdx = findLastUserMessage(events);
|
|
43
|
+
if (lastUserIdx < 0)
|
|
44
|
+
return [];
|
|
45
|
+
const userText = events[lastUserIdx].payload.content ?? "";
|
|
46
|
+
if (!userText || !matchesDissatisfaction(userText, patterns))
|
|
47
|
+
return [];
|
|
48
|
+
if (lastUserIdx < events.length - 3)
|
|
49
|
+
return [];
|
|
50
|
+
if (hasResolutionAfter(events, lastUserIdx, patterns))
|
|
51
|
+
return [];
|
|
52
|
+
return [{
|
|
53
|
+
signal: "SIG-DISSATISFIED",
|
|
54
|
+
severity: "high",
|
|
55
|
+
eventRange: { start: lastUserIdx, end: events.length - 1 },
|
|
56
|
+
summary: `Session ended with user dissatisfaction: '${truncate(userText, 80)}'`,
|
|
57
|
+
evidence: { userMessage: truncate(userText, 300) },
|
|
58
|
+
}];
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=dissatisfied.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dissatisfied.js","sourceRoot":"","sources":["../../../../src/trace-analyzer/signals/dissatisfied.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,wDAAwD;AACxD,+DAA+D;AAC/D,EAAE;AACF,0DAA0D;AAC1D,8DAA8D;AAC9D,+DAA+D;AAK/D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,SAAS,sBAAsB,CAAC,IAAY,EAAE,QAA0B;IACtE,yCAAyC;IACzC,IAAI,QAAQ,CAAC,eAAe,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACzF,OAAO,QAAQ,CAAC,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,iEAAiE;AACjE,SAAS,mBAAmB,CAAC,MAAmC;IAC9D,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,yEAAyE;AACzE,SAAS,kBAAkB,CACzB,MAAmC,EACnC,QAAgB,EAChB,QAA0B;IAE1B,KAAK,IAAI,CAAC,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClD,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;YACrD,IAAI,QAAQ,CAAC,eAAe,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;QACjG,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAwB,EAAE,QAA0B;IACrF,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,MAAM,WAAW,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,WAAW,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAE/B,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IAC3D,IAAI,CAAC,QAAQ,IAAI,CAAC,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IACxE,IAAI,WAAW,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/C,IAAI,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjE,OAAO,CAAC;YACN,MAAM,EAAE,kBAAkB;YAC1B,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;YAC1D,OAAO,EAAE,6CAA6C,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG;YAC/E,QAAQ,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE;SACnD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ConversationChain } from "../chain-reconstructor.js";
|
|
2
|
+
import type { FailureSignal } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Compute parameter similarity between two tool calls.
|
|
5
|
+
* Uses Levenshtein for exec commands, Jaccard for everything else.
|
|
6
|
+
*/
|
|
7
|
+
export declare function paramSimilarity(a: Record<string, unknown>, b: Record<string, unknown>): number;
|
|
8
|
+
/**
|
|
9
|
+
* Detect doom loops: 3+ consecutive similar tool calls with similar
|
|
10
|
+
* params all failing.
|
|
11
|
+
*/
|
|
12
|
+
export declare function detectDoomLoops(chain: ConversationChain): FailureSignal[];
|
|
13
|
+
//# sourceMappingURL=doom-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doom-loop.d.ts","sourceRoot":"","sources":["../../../../src/trace-analyzer/signals/doom-loop.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA6GhD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACzB,MAAM,CASR;AA0BD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,aAAa,EAAE,CAgCzE"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SIG-DOOM-LOOP — Repeated Failed Tool Calls
|
|
3
|
+
// ============================================================
|
|
4
|
+
//
|
|
5
|
+
// Detects 3+ consecutive similar tool calls with similar params
|
|
6
|
+
// all failing. Uses Jaccard similarity for generic params and
|
|
7
|
+
// Levenshtein for exec command strings.
|
|
8
|
+
// ============================================================
|
|
9
|
+
import { truncate } from "../util.js";
|
|
10
|
+
/**
|
|
11
|
+
* Extract all (tool.call, tool.result) pairs from a chain.
|
|
12
|
+
*/
|
|
13
|
+
function extractAttempts(chain) {
|
|
14
|
+
const attempts = [];
|
|
15
|
+
const { events } = chain;
|
|
16
|
+
for (let i = 0; i < events.length - 1; i++) {
|
|
17
|
+
if (events[i].type === "tool.call" && events[i + 1].type === "tool.result") {
|
|
18
|
+
const call = events[i];
|
|
19
|
+
const result = events[i + 1];
|
|
20
|
+
attempts.push({
|
|
21
|
+
callIdx: i,
|
|
22
|
+
resultIdx: i + 1,
|
|
23
|
+
toolName: call.payload.toolName ?? "",
|
|
24
|
+
params: (call.payload.toolParams ?? {}),
|
|
25
|
+
error: result.payload.toolError ?? "",
|
|
26
|
+
isError: Boolean(result.payload.toolError) || result.payload.toolIsError === true,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return attempts;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Jaccard similarity on stringified key-value pairs.
|
|
34
|
+
* Ignores volatile fields like `timeout`.
|
|
35
|
+
*/
|
|
36
|
+
function jaccardSimilarity(a, b) {
|
|
37
|
+
const volatileKeys = new Set(["timeout", "timestamp", "ts"]);
|
|
38
|
+
const aEntries = new Set(Object.entries(a)
|
|
39
|
+
.filter(([k]) => !volatileKeys.has(k))
|
|
40
|
+
.map(([k, v]) => `${k}=${JSON.stringify(v)}`));
|
|
41
|
+
const bEntries = new Set(Object.entries(b)
|
|
42
|
+
.filter(([k]) => !volatileKeys.has(k))
|
|
43
|
+
.map(([k, v]) => `${k}=${JSON.stringify(v)}`));
|
|
44
|
+
const intersection = [...aEntries].filter(x => bEntries.has(x)).length;
|
|
45
|
+
const union = new Set([...aEntries, ...bEntries]).size;
|
|
46
|
+
return union === 0 ? 1.0 : intersection / union;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Levenshtein distance between two strings.
|
|
50
|
+
* Capped at 500 chars to keep O(n²) bounded.
|
|
51
|
+
*/
|
|
52
|
+
function levenshteinDistance(a, b) {
|
|
53
|
+
const sa = a.slice(0, 500);
|
|
54
|
+
const sb = b.slice(0, 500);
|
|
55
|
+
if (sa === sb)
|
|
56
|
+
return 0;
|
|
57
|
+
if (sa.length === 0)
|
|
58
|
+
return sb.length;
|
|
59
|
+
if (sb.length === 0)
|
|
60
|
+
return sa.length;
|
|
61
|
+
const matrix = [];
|
|
62
|
+
for (let i = 0; i <= sb.length; i++) {
|
|
63
|
+
matrix[i] = [i];
|
|
64
|
+
}
|
|
65
|
+
for (let j = 0; j <= sa.length; j++) {
|
|
66
|
+
matrix[0][j] = j;
|
|
67
|
+
}
|
|
68
|
+
for (let i = 1; i <= sb.length; i++) {
|
|
69
|
+
for (let j = 1; j <= sa.length; j++) {
|
|
70
|
+
const cost = sb[i - 1] === sa[j - 1] ? 0 : 1;
|
|
71
|
+
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return matrix[sb.length][sa.length];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Levenshtein ratio: 1.0 = identical, 0.0 = completely different.
|
|
78
|
+
*/
|
|
79
|
+
function levenshteinRatio(a, b) {
|
|
80
|
+
const maxLen = Math.max(a.slice(0, 500).length, b.slice(0, 500).length);
|
|
81
|
+
if (maxLen === 0)
|
|
82
|
+
return 1.0;
|
|
83
|
+
return 1 - levenshteinDistance(a, b) / maxLen;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Compute parameter similarity between two tool calls.
|
|
87
|
+
* Uses Levenshtein for exec commands, Jaccard for everything else.
|
|
88
|
+
*/
|
|
89
|
+
export function paramSimilarity(a, b) {
|
|
90
|
+
// Special case for `exec` tool: compare command strings
|
|
91
|
+
const aCmd = typeof a.command === "string" ? a.command : "";
|
|
92
|
+
const bCmd = typeof b.command === "string" ? b.command : "";
|
|
93
|
+
if (aCmd && bCmd) {
|
|
94
|
+
return levenshteinRatio(aCmd, bCmd);
|
|
95
|
+
}
|
|
96
|
+
return jaccardSimilarity(a, b);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Count consecutive similar failing attempts starting from `anchor`.
|
|
100
|
+
* Returns the count and index of the last matching attempt.
|
|
101
|
+
*/
|
|
102
|
+
function countConsecutiveSimilarFails(attempts, startIdx) {
|
|
103
|
+
const anchor = attempts[startIdx];
|
|
104
|
+
let count = 1;
|
|
105
|
+
let lastIdx = startIdx;
|
|
106
|
+
for (let j = startIdx + 1; j < attempts.length; j++) {
|
|
107
|
+
const candidate = attempts[j];
|
|
108
|
+
if (candidate.toolName !== anchor.toolName)
|
|
109
|
+
break;
|
|
110
|
+
if (paramSimilarity(candidate.params, anchor.params) < 0.8)
|
|
111
|
+
break;
|
|
112
|
+
if (!candidate.isError)
|
|
113
|
+
break;
|
|
114
|
+
count++;
|
|
115
|
+
lastIdx = j;
|
|
116
|
+
}
|
|
117
|
+
return { count, lastIdx };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Detect doom loops: 3+ consecutive similar tool calls with similar
|
|
121
|
+
* params all failing.
|
|
122
|
+
*/
|
|
123
|
+
export function detectDoomLoops(chain) {
|
|
124
|
+
const signals = [];
|
|
125
|
+
const attempts = extractAttempts(chain);
|
|
126
|
+
let i = 0;
|
|
127
|
+
while (i < attempts.length) {
|
|
128
|
+
const anchor = attempts[i];
|
|
129
|
+
if (!anchor.isError) {
|
|
130
|
+
i++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const { count, lastIdx } = countConsecutiveSimilarFails(attempts, i);
|
|
134
|
+
if (count >= 3) {
|
|
135
|
+
const lastAttempt = attempts[lastIdx];
|
|
136
|
+
signals.push({
|
|
137
|
+
signal: "SIG-DOOM-LOOP",
|
|
138
|
+
severity: count >= 5 ? "critical" : "high",
|
|
139
|
+
eventRange: { start: anchor.callIdx, end: lastAttempt.resultIdx },
|
|
140
|
+
summary: `Doom loop: ${count}× ${anchor.toolName} with similar params, all failing`,
|
|
141
|
+
evidence: {
|
|
142
|
+
toolName: anchor.toolName,
|
|
143
|
+
loopSize: count,
|
|
144
|
+
firstError: truncate(anchor.error, 200),
|
|
145
|
+
params: anchor.params,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
i = lastIdx + 1;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
i++;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return signals;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=doom-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doom-loop.js","sourceRoot":"","sources":["../../../../src/trace-analyzer/signals/doom-loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ConversationChain } from "../chain-reconstructor.js";
|
|
2
|
+
import type { FailureSignal } from "./types.js";
|
|
3
|
+
import type { SignalPatternSet } from "./lang/registry.js";
|
|
4
|
+
/**
|
|
5
|
+
* Detect hallucinated completions.
|
|
6
|
+
*
|
|
7
|
+
* Pattern: agent claims completion in msg.out, but the last tool.result
|
|
8
|
+
* before that msg.out was an error.
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectHallucinations(chain: ConversationChain, patterns: SignalPatternSet): FailureSignal[];
|
|
11
|
+
//# sourceMappingURL=hallucination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hallucination.d.ts","sourceRoot":"","sources":["../../../../src/trace-analyzer/signals/hallucination.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AA+C3D;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAgB1G"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SIG-HALLUCINATION — Agent Claims Completion Despite Failure
|
|
3
|
+
// ============================================================
|
|
4
|
+
//
|
|
5
|
+
// Detects when the agent claims task completion ("done", "erledigt",
|
|
6
|
+
// "deployed", "✅") but the last tool result was an error or
|
|
7
|
+
// no tool was called at all.
|
|
8
|
+
// ============================================================
|
|
9
|
+
import { truncate, isToolError, isQuestion } from "../util.js";
|
|
10
|
+
function matchesCompletion(text, patterns) {
|
|
11
|
+
return patterns.completion.claims.some(p => p.test(text));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Find the last tool.result index before `msgOutIdx` within the same turn.
|
|
15
|
+
* Returns -1 if none found.
|
|
16
|
+
*/
|
|
17
|
+
function findLastToolResultInTurn(events, msgOutIdx) {
|
|
18
|
+
for (let j = msgOutIdx - 1; j >= 0; j--) {
|
|
19
|
+
if (events[j].type === "tool.result")
|
|
20
|
+
return j;
|
|
21
|
+
if (events[j].type === "msg.in")
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
return -1;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build a hallucination signal from the given event indices.
|
|
28
|
+
*/
|
|
29
|
+
function buildHallucinationSignal(events, toolResultIdx, msgOutIdx, content) {
|
|
30
|
+
const toolResult = events[toolResultIdx];
|
|
31
|
+
const toolCallIdx = toolResultIdx > 0 && events[toolResultIdx - 1].type === "tool.call"
|
|
32
|
+
? toolResultIdx - 1 : toolResultIdx;
|
|
33
|
+
return {
|
|
34
|
+
signal: "SIG-HALLUCINATION",
|
|
35
|
+
severity: "critical",
|
|
36
|
+
eventRange: { start: toolCallIdx, end: msgOutIdx },
|
|
37
|
+
summary: `Agent claimed completion despite tool failure: '${truncate(content, 100)}'`,
|
|
38
|
+
evidence: {
|
|
39
|
+
agentClaim: truncate(content, 300),
|
|
40
|
+
precedingError: truncate(toolResult.payload.toolError ?? "unknown", 200),
|
|
41
|
+
toolName: toolResult.payload.toolName ?? "unknown",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Detect hallucinated completions.
|
|
47
|
+
*
|
|
48
|
+
* Pattern: agent claims completion in msg.out, but the last tool.result
|
|
49
|
+
* before that msg.out was an error.
|
|
50
|
+
*/
|
|
51
|
+
export function detectHallucinations(chain, patterns) {
|
|
52
|
+
const signals = [];
|
|
53
|
+
const { events } = chain;
|
|
54
|
+
for (let i = 0; i < events.length; i++) {
|
|
55
|
+
if (events[i].type !== "msg.out")
|
|
56
|
+
continue;
|
|
57
|
+
const content = events[i].payload.content ?? "";
|
|
58
|
+
if (!content || !matchesCompletion(content, patterns) || isQuestion(content, patterns))
|
|
59
|
+
continue;
|
|
60
|
+
const lastToolResultIdx = findLastToolResultInTurn(events, i);
|
|
61
|
+
if (lastToolResultIdx >= 0 && isToolError(events[lastToolResultIdx].payload)) {
|
|
62
|
+
signals.push(buildHallucinationSignal(events, lastToolResultIdx, i, content));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return signals;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=hallucination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hallucination.js","sourceRoot":"","sources":["../../../../src/trace-analyzer/signals/hallucination.ts"],"names":[],"mappings":"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"}
|