Handle a TurnStart event (UserPromptSubmit). Begins file watching and transitions the session to Active.
(
&mut self,
event: TurnEvent,
)
| 35 | /// |
| 36 | /// Begins file watching and transitions the session to Active. |
| 37 | pub(super) async fn handle_turn_start( |
| 38 | &mut self, |
| 39 | event: TurnEvent, |
| 40 | ) -> AgentResult<DispatchResult> { |
| 41 | let session_id = &event.session_id; |
| 42 | |
| 43 | let mut session = self.load_or_create_session(session_id, &event)?; |
| 44 | |
| 45 | // Store the prompt |
| 46 | if let Some(ref prompt) = event.prompt { |
| 47 | session.set_first_prompt(prompt); |
| 48 | } |
| 49 | |
| 50 | // Update transcript path |
| 51 | if let Some(ref path) = event.transcript_path { |
| 52 | session.set_transcript_path(path); |
| 53 | } |
| 54 | |
| 55 | // Extract model/provider from raw_json if present. |
| 56 | // OpenCode sends: {"model": "claude-opus-4-5", "provider": "anthropic", ...} |
| 57 | // This is the most reliable source of model info — it comes from the |
| 58 | // chat.message hook which fires at the start of every turn. |
| 59 | if let Some(ref raw) = event.raw_json { |
| 60 | if let Some(model) = raw.get("model").and_then(|v| v.as_str()) { |
| 61 | if !model.is_empty() { |
| 62 | session.model = model.to_string(); |
| 63 | } |
| 64 | } |
| 65 | if let Some(provider) = raw.get("provider").and_then(|v| v.as_str()) { |
| 66 | if !provider.is_empty() { |
| 67 | session.agent_vendor = provider.to_string(); |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // Begin file watching for this turn |
| 73 | if let Err(e) = self.watcher.begin_turn(session_id).await { |
| 74 | log::warn!( |
| 75 | "Failed to begin file watching for session {}: {}", |
| 76 | session_id, |
| 77 | e |
| 78 | ); |
| 79 | // Continue anyway — we'll just miss file changes |
| 80 | } |
| 81 | |
| 82 | // Mark the turn as started in the session |
| 83 | session.begin_turn(); |
| 84 | |
| 85 | // State machine transition |
| 86 | let result = phase::transition( |
| 87 | session.phase, |
| 88 | Event::TurnStart, |
| 89 | TransitionContext::default(), |
| 90 | ); |
| 91 | let remaining = phase::apply_common_actions(&mut session, &result); |
| 92 | |
| 93 | // Handle strategy-specific actions |
| 94 | let mut dispatch = DispatchResult::new(session_id, session.phase); |
no test coverage detected