(entries: readonly WireEntry[])
| 209 | } |
| 210 | |
| 211 | export function analyzeWire(entries: readonly WireEntry[]): Analysis { |
| 212 | const turns: TurnNode[] = []; |
| 213 | const contextSeries: ContextPoint[] = []; |
| 214 | const toolStatMap = new Map<string, ToolStat>(); |
| 215 | const idleGaps: IdleGap[] = []; |
| 216 | |
| 217 | const stepByUuid = new Map<string, StepNode>(); |
| 218 | const toolByCallId = new Map<string, ToolCallNode>(); |
| 219 | const cache: TokenUsage = { ...ZERO_USAGE }; |
| 220 | const configChanges: ConfigChange[] = []; |
| 221 | |
| 222 | let current: TurnNode | null = null; |
| 223 | let contextTokens = 0; |
| 224 | let peakContext = 0; |
| 225 | let firstTime: number | undefined; |
| 226 | let lastTime: number | undefined; |
| 227 | let prevTime: number | undefined; |
| 228 | let prevLineNo = 0; |
| 229 | |
| 230 | const startTurn = (trigger: 'prompt' | 'steer', lineNo: number, time: number | undefined, text: string, originKind: string | undefined): TurnNode => { |
| 231 | const node: TurnNode = { |
| 232 | index: turns.length, |
| 233 | trigger, |
| 234 | promptLineNo: lineNo, |
| 235 | promptTime: time, |
| 236 | promptText: text, |
| 237 | originKind, |
| 238 | steps: [], |
| 239 | tokens: { ...ZERO_USAGE }, |
| 240 | toolCallCount: 0, |
| 241 | toolErrorCount: 0, |
| 242 | cancelled: false, |
| 243 | }; |
| 244 | if (time !== undefined && current?.endTime !== undefined) { |
| 245 | node.waitBeforeMs = Math.max(0, time - current.endTime); |
| 246 | } |
| 247 | turns.push(node); |
| 248 | return node; |
| 249 | }; |
| 250 | |
| 251 | for (const entry of entries) { |
| 252 | const rec = entry.data; |
| 253 | const t = rec.time; |
| 254 | if (t !== undefined) { |
| 255 | firstTime ??= t; |
| 256 | lastTime = t; |
| 257 | if (prevTime !== undefined && t - prevTime >= IDLE_GAP_MS) { |
| 258 | idleGaps.push({ |
| 259 | afterLineNo: prevLineNo, |
| 260 | beforeLineNo: entry.lineNo, |
| 261 | gapMs: t - prevTime, |
| 262 | // A gap straddling a turn boundary is "waiting for the user"; a gap |
| 263 | // inside a turn is the agent/tool being slow. |
| 264 | kind: rec.type === 'turn.prompt' || rec.type === 'turn.steer' ? 'between_turns' : 'in_turn', |
| 265 | }); |
| 266 | } |
| 267 | prevTime = t; |
| 268 | prevLineNo = entry.lineNo; |
no test coverage detected