@sentropic/agent-stats-core 0.1.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/README.md +4 -0
- package/dist/aggregations.d.ts +87 -0
- package/dist/aggregations.d.ts.map +1 -0
- package/dist/aggregations.js +242 -0
- package/dist/aggregations.js.map +1 -0
- package/dist/analyze.d.ts +93 -0
- package/dist/analyze.d.ts.map +1 -0
- package/dist/analyze.js +143 -0
- package/dist/analyze.js.map +1 -0
- package/dist/anomalies.d.ts +37 -0
- package/dist/anomalies.d.ts.map +1 -0
- package/dist/anomalies.js +188 -0
- package/dist/anomalies.js.map +1 -0
- package/dist/cleanser.d.ts +56 -0
- package/dist/cleanser.d.ts.map +1 -0
- package/dist/cleanser.js +132 -0
- package/dist/cleanser.js.map +1 -0
- package/dist/collect.d.ts +43 -0
- package/dist/collect.d.ts.map +1 -0
- package/dist/collect.js +140 -0
- package/dist/collect.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/claude.d.ts +33 -0
- package/dist/parsers/claude.d.ts.map +1 -0
- package/dist/parsers/claude.js +169 -0
- package/dist/parsers/claude.js.map +1 -0
- package/dist/parsers/codex.d.ts +69 -0
- package/dist/parsers/codex.d.ts.map +1 -0
- package/dist/parsers/codex.js +262 -0
- package/dist/parsers/codex.js.map +1 -0
- package/dist/rate-card.d.ts +50 -0
- package/dist/rate-card.d.ts.map +1 -0
- package/dist/rate-card.js +138 -0
- package/dist/rate-card.js.map +1 -0
- package/dist/schema.d.ts +138 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +37 -0
- package/dist/schema.js.map +1 -0
- package/dist/secrets.d.ts +24 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +45 -0
- package/dist/secrets.js.map +1 -0
- package/dist/storage.d.ts +33 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +112 -0
- package/dist/storage.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weekly aggregations.
|
|
3
|
+
*
|
|
4
|
+
* Two-pass algorithm:
|
|
5
|
+
* 1. Group events by `sessionId` and compute a `SessionMeta` per session
|
|
6
|
+
* (start ts, total usage, sub-agent depth, etc.).
|
|
7
|
+
* 2. Bucket sessions by (weekStart × projectCwd × tool × model) and sum
|
|
8
|
+
* the metrics into `WeeklyAggregation` rows.
|
|
9
|
+
*
|
|
10
|
+
* Cost is split by currency: Codex credits and Claude USD cents are
|
|
11
|
+
* tracked separately so they never get mixed.
|
|
12
|
+
*/
|
|
13
|
+
import { type SessionEvent, type Tool, type Usage } from './schema.js';
|
|
14
|
+
import { type ModelRates } from './rate-card.js';
|
|
15
|
+
export interface SessionAggregate {
|
|
16
|
+
sessionId: string;
|
|
17
|
+
tool: Tool;
|
|
18
|
+
projectCwd: string;
|
|
19
|
+
model: string;
|
|
20
|
+
startTs: string;
|
|
21
|
+
endTs: string;
|
|
22
|
+
durationMs: number;
|
|
23
|
+
turns: number;
|
|
24
|
+
totalUsage: Usage;
|
|
25
|
+
isSubagent: boolean;
|
|
26
|
+
forkedFromId?: string;
|
|
27
|
+
toolCalls: number;
|
|
28
|
+
toolCallsByCategory: Record<string, number>;
|
|
29
|
+
toolCallsByName: Record<string, number>;
|
|
30
|
+
skillInvocations: number;
|
|
31
|
+
skillsByName: Record<string, number>;
|
|
32
|
+
compactions: number;
|
|
33
|
+
estimatedCost: {
|
|
34
|
+
codexCredits: number;
|
|
35
|
+
claudeUsdCents: number;
|
|
36
|
+
unknown: number;
|
|
37
|
+
};
|
|
38
|
+
rateLimitMax?: {
|
|
39
|
+
primaryPercent: number;
|
|
40
|
+
secondaryPercent: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export interface WeeklyAggregation {
|
|
44
|
+
/** ISO date of the Monday opening the week (UTC). */
|
|
45
|
+
weekStart: string;
|
|
46
|
+
projectCwd: string;
|
|
47
|
+
tool: Tool;
|
|
48
|
+
model: string;
|
|
49
|
+
sessions: number;
|
|
50
|
+
subagentSessions: number;
|
|
51
|
+
uniqueParents: number;
|
|
52
|
+
totalDurationMs: number;
|
|
53
|
+
turns: number;
|
|
54
|
+
totalUsage: Usage;
|
|
55
|
+
toolCalls: number;
|
|
56
|
+
toolCallsByCategory: Record<string, number>;
|
|
57
|
+
toolCallsByName: Record<string, number>;
|
|
58
|
+
skillInvocations: number;
|
|
59
|
+
skillsByName: Record<string, number>;
|
|
60
|
+
compactions: number;
|
|
61
|
+
estimatedCost: {
|
|
62
|
+
codexCredits: number;
|
|
63
|
+
claudeUsdCents: number;
|
|
64
|
+
unknown: number;
|
|
65
|
+
};
|
|
66
|
+
rateLimitMax?: {
|
|
67
|
+
primaryPercent: number;
|
|
68
|
+
secondaryPercent: number;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export interface AggregateOptions {
|
|
72
|
+
/** Override the rate card; defaults to `DEFAULT_RATE_CARD`. */
|
|
73
|
+
rateCard?: Record<string, ModelRates>;
|
|
74
|
+
}
|
|
75
|
+
/** Monday 00:00 UTC of the ISO week containing `iso`. */
|
|
76
|
+
export declare function weekStartIso(iso: string): string;
|
|
77
|
+
export declare function aggregateSessions(events: AsyncIterable<SessionEvent> | Iterable<SessionEvent>, opts?: AggregateOptions): Promise<SessionAggregate[]>;
|
|
78
|
+
/** Roll SessionAggregates into weekly rows. */
|
|
79
|
+
export declare function bucketWeekly(sessions: SessionAggregate[]): WeeklyAggregation[];
|
|
80
|
+
/**
|
|
81
|
+
* High-level helper: events → SessionAggregate[] → WeeklyAggregation[].
|
|
82
|
+
* The caller does not need to know about the two passes.
|
|
83
|
+
*/
|
|
84
|
+
export declare function aggregateWeekly(events: AsyncIterable<SessionEvent> | Iterable<SessionEvent>, opts?: AggregateOptions): Promise<WeeklyAggregation[]>;
|
|
85
|
+
/** Cache-efficiency ratio (cached input tokens / total input tokens). */
|
|
86
|
+
export declare function cacheEfficiency(usage: Usage): number;
|
|
87
|
+
//# sourceMappingURL=aggregations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../src/aggregations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weekly aggregations.
|
|
3
|
+
*
|
|
4
|
+
* Two-pass algorithm:
|
|
5
|
+
* 1. Group events by `sessionId` and compute a `SessionMeta` per session
|
|
6
|
+
* (start ts, total usage, sub-agent depth, etc.).
|
|
7
|
+
* 2. Bucket sessions by (weekStart × projectCwd × tool × model) and sum
|
|
8
|
+
* the metrics into `WeeklyAggregation` rows.
|
|
9
|
+
*
|
|
10
|
+
* Cost is split by currency: Codex credits and Claude USD cents are
|
|
11
|
+
* tracked separately so they never get mixed.
|
|
12
|
+
*/
|
|
13
|
+
import { ZERO_USAGE, addUsage } from './schema.js';
|
|
14
|
+
import { DEFAULT_RATE_CARD, estimateCost, resolveRates } from './rate-card.js';
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Helpers
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
const ZERO_COST = { codexCredits: 0, claudeUsdCents: 0, unknown: 0 };
|
|
19
|
+
function addCost(a, b) {
|
|
20
|
+
return {
|
|
21
|
+
codexCredits: a.codexCredits + b.codexCredits,
|
|
22
|
+
claudeUsdCents: a.claudeUsdCents + b.claudeUsdCents,
|
|
23
|
+
unknown: a.unknown + b.unknown,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Monday 00:00 UTC of the ISO week containing `iso`. */
|
|
27
|
+
export function weekStartIso(iso) {
|
|
28
|
+
const d = new Date(iso);
|
|
29
|
+
if (Number.isNaN(d.getTime()))
|
|
30
|
+
return '';
|
|
31
|
+
const day = d.getUTCDay(); // 0 = Sun .. 6 = Sat
|
|
32
|
+
const diff = day === 0 ? -6 : 1 - day;
|
|
33
|
+
const monday = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() + diff));
|
|
34
|
+
return monday.toISOString().slice(0, 10);
|
|
35
|
+
}
|
|
36
|
+
function emptySessionAggregate() {
|
|
37
|
+
return {
|
|
38
|
+
sessionId: '',
|
|
39
|
+
tool: 'claude',
|
|
40
|
+
projectCwd: '',
|
|
41
|
+
model: 'unknown',
|
|
42
|
+
startTs: '',
|
|
43
|
+
endTs: '',
|
|
44
|
+
durationMs: 0,
|
|
45
|
+
turns: 0,
|
|
46
|
+
totalUsage: { ...ZERO_USAGE },
|
|
47
|
+
isSubagent: false,
|
|
48
|
+
toolCalls: 0,
|
|
49
|
+
toolCallsByCategory: {},
|
|
50
|
+
toolCallsByName: {},
|
|
51
|
+
skillInvocations: 0,
|
|
52
|
+
skillsByName: {},
|
|
53
|
+
compactions: 0,
|
|
54
|
+
estimatedCost: { ...ZERO_COST },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Pass 1: events → SessionAggregate[]
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
export async function aggregateSessions(events, opts = {}) {
|
|
61
|
+
const card = opts.rateCard ?? DEFAULT_RATE_CARD;
|
|
62
|
+
const map = new Map();
|
|
63
|
+
const ensure = (key, ev) => {
|
|
64
|
+
let s = map.get(key);
|
|
65
|
+
if (!s) {
|
|
66
|
+
s = emptySessionAggregate();
|
|
67
|
+
s.sessionId = ev.sessionId;
|
|
68
|
+
s.tool = ev.tool;
|
|
69
|
+
s.projectCwd = ev.projectCwd;
|
|
70
|
+
s.startTs = ev.ts;
|
|
71
|
+
s.endTs = ev.ts;
|
|
72
|
+
map.set(key, s);
|
|
73
|
+
}
|
|
74
|
+
return s;
|
|
75
|
+
};
|
|
76
|
+
for await (const ev of events) {
|
|
77
|
+
if (!ev.sessionId)
|
|
78
|
+
continue;
|
|
79
|
+
const s = ensure(ev.sessionId, ev);
|
|
80
|
+
if (ev.ts && (!s.startTs || ev.ts < s.startTs))
|
|
81
|
+
s.startTs = ev.ts;
|
|
82
|
+
if (ev.ts && (!s.endTs || ev.ts > s.endTs))
|
|
83
|
+
s.endTs = ev.ts;
|
|
84
|
+
switch (ev.kind) {
|
|
85
|
+
case 'session_start': {
|
|
86
|
+
if (ev.model)
|
|
87
|
+
s.model = ev.model;
|
|
88
|
+
s.isSubagent = ev.isSubagent;
|
|
89
|
+
if (ev.forkedFromId)
|
|
90
|
+
s.forkedFromId = ev.forkedFromId;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case 'session_end':
|
|
94
|
+
// ts already absorbed above.
|
|
95
|
+
break;
|
|
96
|
+
case 'turn': {
|
|
97
|
+
s.turns += 1;
|
|
98
|
+
if (ev.model && s.model === 'unknown')
|
|
99
|
+
s.model = ev.model;
|
|
100
|
+
s.totalUsage = addUsage(s.totalUsage, ev.usage);
|
|
101
|
+
const rates = resolveRates(ev.model, card);
|
|
102
|
+
const cost = estimateCost(ev.usage, rates);
|
|
103
|
+
if (cost.currency === 'codex_credits')
|
|
104
|
+
s.estimatedCost.codexCredits += cost.amount;
|
|
105
|
+
else if (cost.currency === 'claude_usd_cents')
|
|
106
|
+
s.estimatedCost.claudeUsdCents += cost.amount;
|
|
107
|
+
else
|
|
108
|
+
s.estimatedCost.unknown += cost.amount;
|
|
109
|
+
if (ev.rateLimit) {
|
|
110
|
+
const prev = s.rateLimitMax;
|
|
111
|
+
const next = {
|
|
112
|
+
primaryPercent: Math.max(prev?.primaryPercent ?? 0, ev.rateLimit.primaryPercent),
|
|
113
|
+
secondaryPercent: Math.max(prev?.secondaryPercent ?? 0, ev.rateLimit.secondaryPercent),
|
|
114
|
+
};
|
|
115
|
+
s.rateLimitMax = next;
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case 'tool_call': {
|
|
120
|
+
s.toolCalls += 1;
|
|
121
|
+
s.toolCallsByCategory[ev.category] = (s.toolCallsByCategory[ev.category] ?? 0) + 1;
|
|
122
|
+
s.toolCallsByName[ev.name] = (s.toolCallsByName[ev.name] ?? 0) + 1;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case 'skill_invoke': {
|
|
126
|
+
s.skillInvocations += 1;
|
|
127
|
+
s.skillsByName[ev.name] = (s.skillsByName[ev.name] ?? 0) + 1;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'compaction':
|
|
131
|
+
s.compactions += 1;
|
|
132
|
+
break;
|
|
133
|
+
case 'user_prompt':
|
|
134
|
+
// ts already absorbed; no other update for now.
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// duration
|
|
139
|
+
for (const s of map.values()) {
|
|
140
|
+
const start = Date.parse(s.startTs);
|
|
141
|
+
const end = Date.parse(s.endTs);
|
|
142
|
+
if (!Number.isNaN(start) && !Number.isNaN(end)) {
|
|
143
|
+
s.durationMs = Math.max(0, end - start);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return [...map.values()].sort((a, b) => a.startTs.localeCompare(b.startTs));
|
|
147
|
+
}
|
|
148
|
+
function weeklyKey(weekStart, projectCwd, tool, model) {
|
|
149
|
+
return `${weekStart}|${projectCwd}|${tool}|${model}`;
|
|
150
|
+
}
|
|
151
|
+
function emptyWeekly(weekStart, projectCwd, tool, model) {
|
|
152
|
+
return {
|
|
153
|
+
weekStart,
|
|
154
|
+
projectCwd,
|
|
155
|
+
tool,
|
|
156
|
+
model,
|
|
157
|
+
sessions: 0,
|
|
158
|
+
subagentSessions: 0,
|
|
159
|
+
uniqueParents: 0,
|
|
160
|
+
totalDurationMs: 0,
|
|
161
|
+
turns: 0,
|
|
162
|
+
totalUsage: { ...ZERO_USAGE },
|
|
163
|
+
toolCalls: 0,
|
|
164
|
+
toolCallsByCategory: {},
|
|
165
|
+
toolCallsByName: {},
|
|
166
|
+
skillInvocations: 0,
|
|
167
|
+
skillsByName: {},
|
|
168
|
+
compactions: 0,
|
|
169
|
+
estimatedCost: { ...ZERO_COST },
|
|
170
|
+
parentIds: new Set(),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function mergeCounts(target, src) {
|
|
174
|
+
for (const [k, v] of Object.entries(src)) {
|
|
175
|
+
target[k] = (target[k] ?? 0) + v;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Roll SessionAggregates into weekly rows. */
|
|
179
|
+
export function bucketWeekly(sessions) {
|
|
180
|
+
const buckets = new Map();
|
|
181
|
+
for (const s of sessions) {
|
|
182
|
+
const week = weekStartIso(s.startTs);
|
|
183
|
+
if (!week)
|
|
184
|
+
continue;
|
|
185
|
+
const key = weeklyKey(week, s.projectCwd, s.tool, s.model);
|
|
186
|
+
let acc = buckets.get(key);
|
|
187
|
+
if (!acc) {
|
|
188
|
+
acc = emptyWeekly(week, s.projectCwd, s.tool, s.model);
|
|
189
|
+
buckets.set(key, acc);
|
|
190
|
+
}
|
|
191
|
+
acc.sessions += 1;
|
|
192
|
+
if (s.isSubagent)
|
|
193
|
+
acc.subagentSessions += 1;
|
|
194
|
+
if (s.forkedFromId)
|
|
195
|
+
acc.parentIds.add(s.forkedFromId);
|
|
196
|
+
acc.totalDurationMs += s.durationMs;
|
|
197
|
+
acc.turns += s.turns;
|
|
198
|
+
acc.totalUsage = addUsage(acc.totalUsage, s.totalUsage);
|
|
199
|
+
mergeCounts(acc.toolCallsByCategory, s.toolCallsByCategory);
|
|
200
|
+
mergeCounts(acc.toolCallsByName, s.toolCallsByName);
|
|
201
|
+
acc.toolCalls += s.toolCalls;
|
|
202
|
+
mergeCounts(acc.skillsByName, s.skillsByName);
|
|
203
|
+
acc.skillInvocations += s.skillInvocations;
|
|
204
|
+
acc.compactions += s.compactions;
|
|
205
|
+
acc.estimatedCost = addCost(acc.estimatedCost, s.estimatedCost);
|
|
206
|
+
if (s.rateLimitMax) {
|
|
207
|
+
const prev = acc.rateLimitMax;
|
|
208
|
+
acc.rateLimitMax = {
|
|
209
|
+
primaryPercent: Math.max(prev?.primaryPercent ?? 0, s.rateLimitMax.primaryPercent),
|
|
210
|
+
secondaryPercent: Math.max(prev?.secondaryPercent ?? 0, s.rateLimitMax.secondaryPercent),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return [...buckets.values()]
|
|
215
|
+
.map((a) => {
|
|
216
|
+
a.uniqueParents = a.parentIds.size;
|
|
217
|
+
// strip the helper Set before returning.
|
|
218
|
+
const { parentIds, ...rest } = a;
|
|
219
|
+
void parentIds;
|
|
220
|
+
return rest;
|
|
221
|
+
})
|
|
222
|
+
.sort((a, b) => a.weekStart.localeCompare(b.weekStart) ||
|
|
223
|
+
a.projectCwd.localeCompare(b.projectCwd) ||
|
|
224
|
+
a.tool.localeCompare(b.tool) ||
|
|
225
|
+
a.model.localeCompare(b.model));
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* High-level helper: events → SessionAggregate[] → WeeklyAggregation[].
|
|
229
|
+
* The caller does not need to know about the two passes.
|
|
230
|
+
*/
|
|
231
|
+
export async function aggregateWeekly(events, opts = {}) {
|
|
232
|
+
const sessions = await aggregateSessions(events, opts);
|
|
233
|
+
return bucketWeekly(sessions);
|
|
234
|
+
}
|
|
235
|
+
/** Cache-efficiency ratio (cached input tokens / total input tokens). */
|
|
236
|
+
export function cacheEfficiency(usage) {
|
|
237
|
+
const total = usage.newInputTokens + usage.cachedInputTokens + usage.cacheWriteTokens;
|
|
238
|
+
if (total === 0)
|
|
239
|
+
return 0;
|
|
240
|
+
return usage.cachedInputTokens / total;
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=aggregations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregations.js","sourceRoot":"","sources":["../src/aggregations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `analyzeWithLlm(session, opts)` — qualitative analysis of a session.
|
|
3
|
+
*
|
|
4
|
+
* Phase 2 (LLM-driven) is intentionally pluggable: callers inject the
|
|
5
|
+
* `mesh` (an `LlmMesh` instance from `@sentropic/llm-mesh`) and the
|
|
6
|
+
* `model` id. The core lib does not import `@sentropic/llm-mesh`
|
|
7
|
+
* directly so that consumers without a mesh can still use the rest of
|
|
8
|
+
* the library; the type is duck-typed below.
|
|
9
|
+
*
|
|
10
|
+
* Outputs a JSON verdict (`AnalysisVerdict`) with `frustration_level`
|
|
11
|
+
* and `out_of_control_score` (0-10), a short summary, and an optional
|
|
12
|
+
* list of root causes.
|
|
13
|
+
*/
|
|
14
|
+
import type { SessionAggregate } from './aggregations.js';
|
|
15
|
+
export interface AnalysisVerdict {
|
|
16
|
+
sessionId: string;
|
|
17
|
+
model: string;
|
|
18
|
+
frustrationLevel: number;
|
|
19
|
+
outOfControlScore: number;
|
|
20
|
+
summary: string;
|
|
21
|
+
rootCauses: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Duck-typed subset of `LlmMesh` from `@sentropic/llm-mesh`. We don't
|
|
25
|
+
* import the real type so the core stays decoupled.
|
|
26
|
+
*/
|
|
27
|
+
export interface LlmMeshLike {
|
|
28
|
+
generate(request: {
|
|
29
|
+
model: string | {
|
|
30
|
+
providerId: string;
|
|
31
|
+
modelId: string;
|
|
32
|
+
};
|
|
33
|
+
messages?: Array<{
|
|
34
|
+
role: 'system' | 'user' | 'assistant';
|
|
35
|
+
content: string;
|
|
36
|
+
}>;
|
|
37
|
+
providerId?: string;
|
|
38
|
+
[k: string]: unknown;
|
|
39
|
+
}): Promise<{
|
|
40
|
+
message?: {
|
|
41
|
+
content?: string;
|
|
42
|
+
};
|
|
43
|
+
text?: string;
|
|
44
|
+
[k: string]: unknown;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export interface AnalyzeOptions {
|
|
48
|
+
mesh: LlmMeshLike;
|
|
49
|
+
/** Model id passed verbatim to the mesh `generate` call. */
|
|
50
|
+
model: string;
|
|
51
|
+
/** Optional storage adapter used for verdict cache. */
|
|
52
|
+
cache?: AnalyzeCache;
|
|
53
|
+
}
|
|
54
|
+
export interface AnalyzeCache {
|
|
55
|
+
read(key: string): Promise<AnalysisVerdict | null>;
|
|
56
|
+
write(key: string, verdict: AnalysisVerdict): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Run the LLM analysis for a single session. Returns the verdict.
|
|
60
|
+
* On cache hit, the mesh is not called.
|
|
61
|
+
*/
|
|
62
|
+
export declare function analyzeWithLlm(session: SessionAggregate, opts: AnalyzeOptions): Promise<AnalysisVerdict>;
|
|
63
|
+
/**
|
|
64
|
+
* Compare `n` LLM models on the same set of sessions and return per-model
|
|
65
|
+
* latency, predicted-vs-baseline alignment, and aggregate cost (best-effort).
|
|
66
|
+
*
|
|
67
|
+
* `baseline` is the heuristic verdict (or hand-labeled if available); it
|
|
68
|
+
* provides a reference frustration & out-of-control score per session.
|
|
69
|
+
*/
|
|
70
|
+
export interface BenchModelConfig {
|
|
71
|
+
model: string;
|
|
72
|
+
mesh: LlmMeshLike;
|
|
73
|
+
}
|
|
74
|
+
export interface BenchSampleBaseline {
|
|
75
|
+
sessionId: string;
|
|
76
|
+
frustrationLevel: number;
|
|
77
|
+
outOfControlScore: number;
|
|
78
|
+
}
|
|
79
|
+
export interface BenchModelResult {
|
|
80
|
+
model: string;
|
|
81
|
+
samples: number;
|
|
82
|
+
durationMs: number;
|
|
83
|
+
/** Mean absolute error vs baseline scores (combined, normalized 0..10). */
|
|
84
|
+
meanAbsoluteError: number;
|
|
85
|
+
errors: string[];
|
|
86
|
+
}
|
|
87
|
+
export interface BenchOptions {
|
|
88
|
+
models: BenchModelConfig[];
|
|
89
|
+
sessions: SessionAggregate[];
|
|
90
|
+
baselines: BenchSampleBaseline[];
|
|
91
|
+
}
|
|
92
|
+
export declare function benchModels(opts: BenchOptions): Promise<BenchModelResult[]>;
|
|
93
|
+
//# sourceMappingURL=analyze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE;QAChB,KAAK,EAAE,MAAM,GAAG;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QACxD,QAAQ,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;KACtB,GAAG,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACnD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAoED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,eAAe,CAAC,CA6B1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,SAAS,EAAE,mBAAmB,EAAE,CAAC;CAClC;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiCjF"}
|
package/dist/analyze.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `analyzeWithLlm(session, opts)` — qualitative analysis of a session.
|
|
3
|
+
*
|
|
4
|
+
* Phase 2 (LLM-driven) is intentionally pluggable: callers inject the
|
|
5
|
+
* `mesh` (an `LlmMesh` instance from `@sentropic/llm-mesh`) and the
|
|
6
|
+
* `model` id. The core lib does not import `@sentropic/llm-mesh`
|
|
7
|
+
* directly so that consumers without a mesh can still use the rest of
|
|
8
|
+
* the library; the type is duck-typed below.
|
|
9
|
+
*
|
|
10
|
+
* Outputs a JSON verdict (`AnalysisVerdict`) with `frustration_level`
|
|
11
|
+
* and `out_of_control_score` (0-10), a short summary, and an optional
|
|
12
|
+
* list of root causes.
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from 'node:crypto';
|
|
15
|
+
const PROMPT = `You are an analyst grading a coding-agent session.
|
|
16
|
+
Read the JSON aggregate below and return a JSON verdict with EXACTLY this shape:
|
|
17
|
+
{
|
|
18
|
+
"frustrationLevel": <integer 0..10>,
|
|
19
|
+
"outOfControlScore": <integer 0..10>,
|
|
20
|
+
"summary": "<one sentence>",
|
|
21
|
+
"rootCauses": ["<short cause>", ...]
|
|
22
|
+
}
|
|
23
|
+
Frustration: estimate user frustration from retries, compactions, error rates.
|
|
24
|
+
Out-of-control: estimate AI loops, runaway sessions, tool flailing.
|
|
25
|
+
Return ONLY the JSON, no prose, no markdown fence.`;
|
|
26
|
+
function sessionFingerprint(session) {
|
|
27
|
+
const payload = JSON.stringify({
|
|
28
|
+
id: session.sessionId,
|
|
29
|
+
turns: session.turns,
|
|
30
|
+
tools: session.toolCalls,
|
|
31
|
+
cat: session.toolCallsByCategory,
|
|
32
|
+
comp: session.compactions,
|
|
33
|
+
cost: session.estimatedCost,
|
|
34
|
+
dur: session.durationMs,
|
|
35
|
+
rl: session.rateLimitMax,
|
|
36
|
+
});
|
|
37
|
+
return createHash('sha256').update(payload).digest('hex').slice(0, 16);
|
|
38
|
+
}
|
|
39
|
+
function extractJson(text) {
|
|
40
|
+
// The mesh sometimes wraps output in ```json ... ```; strip it.
|
|
41
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]+?)\s*```/);
|
|
42
|
+
const raw = (fenced?.[1] ?? text).trim();
|
|
43
|
+
return JSON.parse(raw);
|
|
44
|
+
}
|
|
45
|
+
function clampScore(x) {
|
|
46
|
+
const n = typeof x === 'number' ? x : Number(x);
|
|
47
|
+
if (!Number.isFinite(n))
|
|
48
|
+
return 0;
|
|
49
|
+
return Math.max(0, Math.min(10, Math.round(n)));
|
|
50
|
+
}
|
|
51
|
+
function buildPayload(session) {
|
|
52
|
+
return JSON.stringify({
|
|
53
|
+
sessionId: session.sessionId,
|
|
54
|
+
tool: session.tool,
|
|
55
|
+
model: session.model,
|
|
56
|
+
durationMs: session.durationMs,
|
|
57
|
+
turns: session.turns,
|
|
58
|
+
isSubagent: session.isSubagent,
|
|
59
|
+
forkedFromId: session.forkedFromId ?? null,
|
|
60
|
+
toolCalls: session.toolCalls,
|
|
61
|
+
toolCallsByCategory: session.toolCallsByCategory,
|
|
62
|
+
compactions: session.compactions,
|
|
63
|
+
estimatedCost: session.estimatedCost,
|
|
64
|
+
rateLimitMax: session.rateLimitMax ?? null,
|
|
65
|
+
totalInputTokens: session.totalUsage.newInputTokens +
|
|
66
|
+
session.totalUsage.cachedInputTokens +
|
|
67
|
+
session.totalUsage.cacheWriteTokens,
|
|
68
|
+
outputTokens: session.totalUsage.outputTokens,
|
|
69
|
+
reasoningTokens: session.totalUsage.reasoningTokens,
|
|
70
|
+
}, null, 2);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run the LLM analysis for a single session. Returns the verdict.
|
|
74
|
+
* On cache hit, the mesh is not called.
|
|
75
|
+
*/
|
|
76
|
+
export async function analyzeWithLlm(session, opts) {
|
|
77
|
+
const key = `${session.sessionId}|${opts.model}|${sessionFingerprint(session)}`;
|
|
78
|
+
if (opts.cache) {
|
|
79
|
+
const cached = await opts.cache.read(key);
|
|
80
|
+
if (cached)
|
|
81
|
+
return cached;
|
|
82
|
+
}
|
|
83
|
+
const payload = buildPayload(session);
|
|
84
|
+
const result = await opts.mesh.generate({
|
|
85
|
+
model: opts.model,
|
|
86
|
+
messages: [
|
|
87
|
+
{ role: 'system', content: PROMPT },
|
|
88
|
+
{ role: 'user', content: payload },
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
const text = result.message?.content ?? result.text ?? '';
|
|
92
|
+
if (!text)
|
|
93
|
+
throw new Error('analyzeWithLlm: mesh returned empty response');
|
|
94
|
+
const parsed = extractJson(text);
|
|
95
|
+
const verdict = {
|
|
96
|
+
sessionId: session.sessionId,
|
|
97
|
+
model: opts.model,
|
|
98
|
+
frustrationLevel: clampScore(parsed['frustrationLevel']),
|
|
99
|
+
outOfControlScore: clampScore(parsed['outOfControlScore']),
|
|
100
|
+
summary: typeof parsed['summary'] === 'string' ? parsed['summary'] : '',
|
|
101
|
+
rootCauses: Array.isArray(parsed['rootCauses'])
|
|
102
|
+
? parsed['rootCauses'].filter((s) => typeof s === 'string')
|
|
103
|
+
: [],
|
|
104
|
+
};
|
|
105
|
+
if (opts.cache)
|
|
106
|
+
await opts.cache.write(key, verdict);
|
|
107
|
+
return verdict;
|
|
108
|
+
}
|
|
109
|
+
export async function benchModels(opts) {
|
|
110
|
+
const baselineById = new Map(opts.baselines.map((b) => [b.sessionId, b]));
|
|
111
|
+
const results = [];
|
|
112
|
+
for (const cfg of opts.models) {
|
|
113
|
+
const errors = [];
|
|
114
|
+
let mae = 0;
|
|
115
|
+
let counted = 0;
|
|
116
|
+
const t0 = Date.now();
|
|
117
|
+
for (const s of opts.sessions) {
|
|
118
|
+
const base = baselineById.get(s.sessionId);
|
|
119
|
+
try {
|
|
120
|
+
const v = await analyzeWithLlm(s, { mesh: cfg.mesh, model: cfg.model });
|
|
121
|
+
if (base) {
|
|
122
|
+
const e = (Math.abs(v.frustrationLevel - base.frustrationLevel) +
|
|
123
|
+
Math.abs(v.outOfControlScore - base.outOfControlScore)) /
|
|
124
|
+
2;
|
|
125
|
+
mae += e;
|
|
126
|
+
counted += 1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
errors.push(`${s.sessionId}: ${e instanceof Error ? e.message : String(e)}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
results.push({
|
|
134
|
+
model: cfg.model,
|
|
135
|
+
samples: counted,
|
|
136
|
+
durationMs: Date.now() - t0,
|
|
137
|
+
meanAbsoluteError: counted ? mae / counted : 0,
|
|
138
|
+
errors,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return results;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anomaly detection heuristics (no LLM).
|
|
3
|
+
*
|
|
4
|
+
* Operates on a stream of `SessionEvent`s and emits `Anomaly` records
|
|
5
|
+
* flagging sessions that exhibit known costly or stuck patterns:
|
|
6
|
+
* - runaway_compactions : auto-compaction triggered N+ times in one session.
|
|
7
|
+
* - high_error_rate : tool error rate > threshold (min 10 calls).
|
|
8
|
+
* - prompt_retry_loop : same user prompt textHash repeated N+ times.
|
|
9
|
+
* - tool_loop : same tool name called N+ times consecutively.
|
|
10
|
+
* - zombie_session : > N minutes between consecutive turns.
|
|
11
|
+
*
|
|
12
|
+
* Frustration via user-prompt content is intentionally out of scope for
|
|
13
|
+
* WP5; the parser stores only prompt hashes (privacy). That heuristic
|
|
14
|
+
* is deferred to WP7 (LLM phase 2).
|
|
15
|
+
*/
|
|
16
|
+
import type { SessionEvent } from './schema.js';
|
|
17
|
+
export type AnomalyType = 'runaway_compactions' | 'high_error_rate' | 'prompt_retry_loop' | 'tool_loop' | 'zombie_session';
|
|
18
|
+
export type AnomalySeverity = 'low' | 'medium' | 'high';
|
|
19
|
+
export interface Anomaly {
|
|
20
|
+
sessionId: string;
|
|
21
|
+
tool: 'claude' | 'codex';
|
|
22
|
+
projectCwd: string;
|
|
23
|
+
type: AnomalyType;
|
|
24
|
+
severity: AnomalySeverity;
|
|
25
|
+
/** Free-form, machine-readable evidence. */
|
|
26
|
+
evidence: Record<string, number | string>;
|
|
27
|
+
}
|
|
28
|
+
export interface DetectAnomaliesOptions {
|
|
29
|
+
runawayCompactions?: number;
|
|
30
|
+
errorRateThreshold?: number;
|
|
31
|
+
minToolCallsForErrorRate?: number;
|
|
32
|
+
promptRetryCount?: number;
|
|
33
|
+
toolLoopCount?: number;
|
|
34
|
+
zombieGapMinutes?: number;
|
|
35
|
+
}
|
|
36
|
+
export declare function detectAnomalies(events: AsyncIterable<SessionEvent> | Iterable<SessionEvent>, opts?: DetectAnomaliesOptions): Promise<Anomaly[]>;
|
|
37
|
+
//# sourceMappingURL=anomalies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anomalies.d.ts","sourceRoot":"","sources":["../src/anomalies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,WAAW,GACnB,qBAAqB,GACrB,iBAAiB,GACjB,mBAAmB,GACnB,WAAW,GACX,gBAAgB,CAAC;AAErB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,eAAe,CAAC;IAC1B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAsCD,wBAAsB,eAAe,CACnC,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,EAC5D,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,OAAO,EAAE,CAAC,CAqJpB"}
|