| 51 | * - turn.completed → extract token usage |
| 52 | */ |
| 53 | export function parseCodexJSONL(lines: string[]): ParsedCodexJSONL { |
| 54 | const outputParts: string[] = []; |
| 55 | const reasoning: string[] = []; |
| 56 | const toolCalls: string[] = []; |
| 57 | let tokens = 0; |
| 58 | let sessionId: string | null = null; |
| 59 | |
| 60 | for (const line of lines) { |
| 61 | if (!line.trim()) continue; |
| 62 | try { |
| 63 | const obj = JSON.parse(line); |
| 64 | const t = obj.type || ''; |
| 65 | |
| 66 | if (t === 'thread.started') { |
| 67 | const tid = obj.thread_id || ''; |
| 68 | if (tid) sessionId = tid; |
| 69 | } else if (t === 'item.completed' && obj.item) { |
| 70 | const item = obj.item; |
| 71 | const itype = item.type || ''; |
| 72 | const text = item.text || ''; |
| 73 | |
| 74 | if (itype === 'reasoning' && text) { |
| 75 | reasoning.push(text); |
| 76 | } else if (itype === 'agent_message' && text) { |
| 77 | outputParts.push(text); |
| 78 | } else if (itype === 'command_execution') { |
| 79 | const cmd = item.command || ''; |
| 80 | if (cmd) toolCalls.push(cmd); |
| 81 | } |
| 82 | } else if (t === 'turn.completed') { |
| 83 | const usage = obj.usage || {}; |
| 84 | const turnTokens = (usage.input_tokens || 0) + (usage.output_tokens || 0); |
| 85 | tokens += turnTokens; |
| 86 | } |
| 87 | } catch { /* skip malformed lines */ } |
| 88 | } |
| 89 | |
| 90 | return { |
| 91 | output: outputParts.join('\n'), |
| 92 | reasoning, |
| 93 | toolCalls, |
| 94 | tokens, |
| 95 | sessionId, |
| 96 | }; |
| 97 | } |
| 98 | |
| 99 | // --- Skill installation helper --- |
| 100 | |