Skip to main content

call_coding_clis/output/
parse.rs

1use super::model::{Event, ToolCall, ToolResult, Transcript, Usage};
2use crate::invoke::RunnerKind;
3
4pub fn parse_transcript(raw: &str, schema: &str) -> Transcript {
5    transcript_from_parsed(crate::json_output::parse_json_output(raw, schema))
6}
7
8pub fn parse_transcript_for_runner(raw: &str, runner: RunnerKind) -> Option<Transcript> {
9    schema_name_for_runner(runner).map(|schema| parse_transcript(raw, schema))
10}
11
12pub fn schema_name_for_runner(runner: RunnerKind) -> Option<&'static str> {
13    match runner {
14        RunnerKind::OpenCode => Some("opencode"),
15        RunnerKind::Claude => Some("claude-code"),
16        RunnerKind::Codex => Some("codex"),
17        RunnerKind::Kimi => Some("kimi"),
18        RunnerKind::Cursor => Some("cursor-agent"),
19        RunnerKind::Gemini => Some("gemini"),
20        RunnerKind::Pi => Some("pi"),
21        RunnerKind::Grok => Some("grok"),
22        RunnerKind::RooCode | RunnerKind::Crush => None,
23    }
24}
25
26pub fn transcript_from_parsed(parsed: crate::json_output::ParsedJsonOutput) -> Transcript {
27    Transcript {
28        events: parsed.events.into_iter().map(convert_event).collect(),
29        final_text: parsed.final_text,
30        session_id: (!parsed.session_id.is_empty()).then_some(parsed.session_id),
31        usage: Usage {
32            counts: parsed.usage,
33            cost_usd: parsed.cost_usd,
34            duration_ms: parsed.duration_ms,
35        },
36        error: (!parsed.error.is_empty()).then_some(parsed.error),
37        unknown_json_lines: parsed.unknown_json_lines,
38    }
39}
40
41fn convert_event(event: crate::json_output::JsonEvent) -> Event {
42    if let Some(tool_call) = event.tool_call {
43        return Event::ToolCall(ToolCall {
44            id: tool_call.id,
45            name: tool_call.name,
46            arguments: tool_call.arguments,
47        });
48    }
49    if let Some(tool_result) = event.tool_result {
50        return Event::ToolResult(ToolResult {
51            tool_call_id: tool_result.tool_call_id,
52            content: tool_result.content,
53            is_error: tool_result.is_error,
54        });
55    }
56    if !event.thinking.is_empty() {
57        return Event::Thinking(event.thinking);
58    }
59    match event.event_type.as_str() {
60        "error" => Event::Error(event.text),
61        "raw_unknown_json" => Event::RawUnknownJson(event.text),
62        _ => Event::Text(event.text),
63    }
64}