From 02b4c7a35ac0b714bc045d54fb4bb45d1ce4e490 Mon Sep 17 00:00:00 2001 From: t Date: Sat, 13 Jun 2026 23:45:59 -0600 Subject: Add Codex Responses support and session debugging Teach provider config and auth resolution about the Codex Responses dialect, including user-facing `style = "openai_responses"` with `dialect = "codex"`. Serialize and parse Responses traffic with provider-specific reasoning replay, assistant phase metadata, and robust function-call assembly keyed by `output_index` so streamed tool inputs survive proxy quirks and empty terminal payloads. Also persist thinking origins and message metadata across sessions, add the Anthropic interleaved-thinking header switch, write per-session debug logs, and improve the TUI and scripts for inspecting tool output and session costs. --- libpanto/src/provider_openai_chat.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'libpanto/src/provider_openai_chat.zig') diff --git a/libpanto/src/provider_openai_chat.zig b/libpanto/src/provider_openai_chat.zig index b236a60..3f01e7f 100644 --- a/libpanto/src/provider_openai_chat.zig +++ b/libpanto/src/provider_openai_chat.zig @@ -94,6 +94,7 @@ pub const OpenAIChatRequest = struct { // Build the request body. const body = try json_mod.serializeRequest(self.allocator, self.config, conv, tools); defer self.allocator.free(body); + std.log.debug("openai_chat => {s}", .{body}); // Auth header const auth_value = try std.fmt.allocPrint( @@ -165,11 +166,19 @@ pub const OpenAIChatRequest = struct { if (err_buf.items.len > 16 * 1024) break; } const status: u16 = @intFromEnum(rr.response.head.status); - std.log.err("openai_chat HTTP {d}: {s}", .{ status, err_buf.items }); // Classify the status into a retryable/terminal provider error. // HTTP 400 with a context marker becomes `ContextOverflow` so the // caller can compact and retry rather than hard-fail. const classified = provider_mod.classifyHttpStatus(status, err_buf.items); + // 401/403 is routinely recovered by the turn-runner's forced token + // refresh + reopen; demote it to `.debug` (still in the debug log) + // so a transparent refresh doesn't surface a scary error line. The + // retry layer raises a hard error only if recovery ultimately fails. + if (classified == error.ProviderAuthFailed) { + std.log.debug("openai_chat HTTP {d} (recoverable auth): {s}", .{ status, err_buf.items }); + } else { + std.log.err("openai_chat HTTP {d}: {s}", .{ status, err_buf.items }); + } if (self.diag) |d| { d.status_code = status; d.retry_after_ms = retry_after_ms; @@ -1032,7 +1041,6 @@ test "inbound wire tool name is decoded to dotted form (even split across __)" { try testing.expectEqualStrings("std.read", tu.name); } - test "parallel tool_calls emit one complete start/delta/complete cycle per block" { // Regression test: previously, the OpenAI provider deferred ALL // tool_use onBlockComplete callbacks to finalize, so a four-tool -- cgit v1.3