workflows/default/systems/runtime/ProviderCLI/parsers/Parse-CodexStream.ps1
|
<# .SYNOPSIS Codex (OpenAI) stream parser for ProviderCLI. .DESCRIPTION Processes Codex CLI --json JSONL output. Codex emits events like: thread.started, turn.started, message.delta, message.completed, turn.completed, turn.failed, error Provides Process-StreamLine function for the ProviderCLI dispatcher. #> # Import helpers if (-not (Get-Command Write-ActivityLog -ErrorAction SilentlyContinue)) { Import-Module "$PSScriptRoot\..\..\ClaudeCLI\ClaudeCLI.psm1" -Force } function Process-StreamLine { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Line, [Parameter(Mandatory)] [hashtable]$State, [switch]$ShowDebugJson, [switch]$ShowVerbose ) $t = $State.theme # Skip non-JSON lines (stderr noise like rmcp errors) if (-not $Line -or $Line[0] -ne '{') { if ($ShowDebugJson) { [Console]::Error.WriteLine("$($t.Bezel)[SKIP] $Line$($t.Reset)") [Console]::Error.Flush() } return 'skip' } if ($ShowDebugJson) { [Console]::Error.WriteLine("$($t.Bezel)[JSON] $Line$($t.Reset)") [Console]::Error.Flush() } $evt = $null try { $evt = $Line | ConvertFrom-Json -ErrorAction Stop } catch { return 'skip' } if (-not $evt) { return 'skip' } switch ($evt.type) { 'thread.started' { $threadId = $evt.thread_id Write-ClaudeLog "init" "Codex thread: $threadId" "*" Write-ActivityLog -Type "init" -Message "Codex thread started: $threadId" return 'init' } 'turn.started' { Write-ClaudeLog "turn" "started" ">" return 'turn_started' } 'message.delta' { # Streaming text delta if ($evt.delta) { [void]$State.assistantText.Append($evt.delta) } return 'text' } 'message.completed' { # If content field present (full message without prior deltas), capture it if ($evt.content -and $State.assistantText.Length -eq 0) { [void]$State.assistantText.Append($evt.content) } # Full message completed — flush accumulated text if ($State.assistantText.Length -gt 0) { $text = $State.assistantText.ToString() [Console]::WriteLine("") [Console]::WriteLine($text) Write-ActivityLog -Type "text" -Message (Get-PreviewText $text 200) [Console]::Out.Flush() $State.assistantText.Length = 0 } # Track usage if present if ($evt.usage) { if ($evt.usage.input_tokens) { $State.totalInputTokens += $evt.usage.input_tokens } if ($evt.usage.output_tokens) { $State.totalOutputTokens += $evt.usage.output_tokens } } return 'message_completed' } 'function_call' { # Tool/function call $name = $evt.name $detail = "" if ($evt.arguments) { try { $args_ = $evt.arguments | ConvertFrom-Json -ErrorAction SilentlyContinue if ($args_.command) { $detail = Get-PreviewText $args_.command 140 } elseif ($args_.file_path) { $detail = $args_.file_path } } catch { $detail = Get-PreviewText $evt.arguments 140 } } Write-ClaudeLog $name $detail ">" Write-ActivityLog -Type $name -Message $detail return 'tool_use' } 'function_call_output' { # Tool result $icon = if ($evt.is_error) { "x" } else { "+" } $msg = "" if ($evt.duration_ms -and $evt.duration_ms -gt 100) { $msg = "$($evt.duration_ms)ms" } if ($msg) { Write-ClaudeLog "done" $msg $icon } return 'tool_result' } 'turn.completed' { # Flush any remaining text if ($State.assistantText.Length -gt 0) { $text = $State.assistantText.ToString() [Console]::WriteLine("") [Console]::WriteLine($text) Write-ActivityLog -Type "text" -Message (Get-PreviewText $text 200) [Console]::Out.Flush() $State.assistantText.Length = 0 } # Show summary if ($evt.usage) { $inp = if ($evt.usage.input_tokens) { $evt.usage.input_tokens } else { 0 } $out = if ($evt.usage.output_tokens) { $evt.usage.output_tokens } else { 0 } Write-ClaudeLog "done" "tokens: in=$inp out=$out" "+" } return 'result' } 'turn.failed' { $errorMsg = if ($evt.error?.message) { $evt.error.message } else { "Turn failed" } [Console]::Error.WriteLine("") [Console]::Error.WriteLine("$($t.Amber)Error: $errorMsg$($t.Reset)") [Console]::Error.Flush() Write-ActivityLog -Type "error" -Message $errorMsg return 'error' } 'error' { $errorMsg = if ($evt.message) { $evt.message } else { "Unknown error" } # Check for rate limit if ($errorMsg -match "rate.?limit|too many requests|429") { $State.rateLimitMessage = $errorMsg [Console]::Error.WriteLine("$($t.Amber)Rate limit: $errorMsg$($t.Reset)") [Console]::Error.Flush() Write-ActivityLog -Type "rate_limit" -Message $errorMsg return 'rate_limit' } [Console]::Error.WriteLine("") [Console]::Error.WriteLine("$($t.Amber)Error: $errorMsg$($t.Reset)") [Console]::Error.Flush() Write-ActivityLog -Type "error" -Message $errorMsg return 'error' } default { if ($ShowDebugJson) { [Console]::Error.WriteLine("$($t.Bezel)[UNKNOWN] type=$($evt.type)$($t.Reset)") [Console]::Error.Flush() } return 'unknown' } } } |