| 200 | } |
| 201 | |
| 202 | fn parse_event(&self, hook_type: HookType, input: &[u8]) -> AgentResult<TurnEvent> { |
| 203 | if input.is_empty() { |
| 204 | return Err(AgentError::HookInputEmpty { |
| 205 | agent: self.name().to_string(), |
| 206 | hook_type: hook_type.as_str().to_string(), |
| 207 | }); |
| 208 | } |
| 209 | |
| 210 | let (parsed, raw) = Self::parse_input(self.name(), hook_type, input)?; |
| 211 | |
| 212 | if parsed.session_id.is_empty() { |
| 213 | return Err(AgentError::HookParseFailed { |
| 214 | agent: self.name().to_string(), |
| 215 | hook_type: hook_type.as_str().to_string(), |
| 216 | reason: "session_id is empty".to_string(), |
| 217 | }); |
| 218 | } |
| 219 | |
| 220 | let mut event = TurnEvent::new(parsed.session_id.clone(), hook_type).with_raw_json(raw); |
| 221 | |
| 222 | // Same as Claude Code: whichever hook carries the transcript sets |
| 223 | // it, and the session keeps the first one it sees. |
| 224 | if let Some(path) = &parsed.transcript_path { |
| 225 | if !path.is_empty() { |
| 226 | event = event.with_transcript_path(path.clone()); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | // Stamp model and provider so the orchestrator can persist them on |
| 231 | // the AgentSession — same pattern as OpenCode. |
| 232 | if let Some(model) = &parsed.model { |
| 233 | if !model.is_empty() { |
| 234 | if let Some(ref mut raw_json) = event.raw_json { |
| 235 | if let Some(obj) = raw_json.as_object_mut() { |
| 236 | obj.insert( |
| 237 | "model".to_string(), |
| 238 | serde_json::Value::String(model.clone()), |
| 239 | ); |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | if let Some(provider) = &parsed.provider { |
| 245 | if !provider.is_empty() { |
| 246 | if let Some(ref mut raw_json) = event.raw_json { |
| 247 | if let Some(obj) = raw_json.as_object_mut() { |
| 248 | obj.insert( |
| 249 | "provider".to_string(), |
| 250 | serde_json::Value::String(provider.clone()), |
| 251 | ); |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | match hook_type { |
| 258 | HookType::TurnStart => { |
| 259 | // intent_title doubles as the turn prompt — used by the |