| 183 | }; |
| 184 | |
| 185 | const recordTurn = async (sessionID: string) => { |
| 186 | const state = ensure(sessionID); |
| 187 | |
| 188 | if (!state.started) { |
| 189 | await hook(sessionID, "session-start", { source: "startup" }); |
| 190 | state.started = true; |
| 191 | } |
| 192 | |
| 193 | state.turnCount++; |
| 194 | |
| 195 | const turnMsg = state.prompt |
| 196 | ? state.prompt.substring(0, 200) |
| 197 | : `Turn ${state.turnCount}`; |
| 198 | |
| 199 | await hook(sessionID, "user-prompt", { |
| 200 | prompt: turnMsg, |
| 201 | ...(state.model ? { model: state.model } : {}), |
| 202 | ...(state.provider ? { provider: state.provider } : {}), |
| 203 | ...(state.agent ? { agent: state.agent } : {}), |
| 204 | }); |
| 205 | |
| 206 | for (const tc of state.completedTools) { |
| 207 | const extra: Record<string, unknown> = tc as any; |
| 208 | await hook(sessionID, "after-tool", { |
| 209 | tool_name: tc.tool, |
| 210 | tool_call_id: tc.callID, |
| 211 | status: tc.status, |
| 212 | duration: tc.startTime > 0 ? Date.now() - tc.startTime : undefined, |
| 213 | modified_files: [ |
| 214 | "edit", |
| 215 | "write", |
| 216 | "multiedit", |
| 217 | "patch", |
| 218 | "create", |
| 219 | ].includes(tc.tool), |
| 220 | tool_input: tc.input, |
| 221 | tool_output: tc.output?.substring(0, 500), |
| 222 | // Rich metadata from tool.execute.after hook |
| 223 | ...(extra.title ? { title: extra.title } : {}), |
| 224 | ...(extra.filePath ? { file_path: extra.filePath } : {}), |
| 225 | ...(extra.filediff ? { filediff: extra.filediff } : {}), |
| 226 | ...(extra.diagnostics ? { diagnostics: extra.diagnostics } : {}), |
| 227 | ...(extra.exitCode !== undefined ? { exit_code: extra.exitCode } : {}), |
| 228 | }); |
| 229 | } |
| 230 | state.completedTools = []; |
| 231 | |
| 232 | // Build concatenated reasoning text and extract last signature |
| 233 | const reasoningText = state.turnReasoningBlocks |
| 234 | .map((b) => b.text) |
| 235 | .filter((t) => t.length > 0) |
| 236 | .join("\n---\n"); |
| 237 | const lastSignature = [...state.turnReasoningBlocks] |
| 238 | .reverse() |
| 239 | .find((b) => b.signature)?.signature; |
| 240 | const totalReasoningDurationMs = state.turnReasoningBlocks.reduce( |
| 241 | (sum, b) => sum + b.durationMs, |
| 242 | 0, |