| 4398 | /* Three classes of items: |
| 4399 | * 1. consumes_reasoning: assistant message / function_call / hosted-tool |
| 4400 | * call. Attaches pending reasoning to its own assistant message. |
| 4401 | * 2. is_bookkeeping: compaction / context_compaction etc. Semantically |
| 4402 | * transparent — passes through without touching pending_reasoning. |
| 4403 | * 3. everything else (user message, tool output): forces pending |
| 4404 | * reasoning to flush in-position as an empty assistant message so it |
| 4405 | * stays before this item in the rendered history. */ |
| 4406 | bool consumes_reasoning = |
| 4407 | (!strcmp(t, "message") && role && !strcmp(role, "assistant")) || |
| 4408 | !strcmp(t, "function_call") || !strcmp(t, "custom_tool_call") || |
| 4409 | !strcmp(t, "local_shell_call") || !strcmp(t, "web_search_call") || |
| 4410 | !strcmp(t, "tool_search_call") || !strcmp(t, "image_generation_call"); |
| 4411 | bool is_bookkeeping = |
| 4412 | !strcmp(t, "compaction") || !strcmp(t, "context_compaction"); |
| 4413 | if (!consumes_reasoning && !is_bookkeeping && pending_reasoning.len) { |
| 4414 | chat_msg flush_msg = {0}; |
| 4415 | flush_msg.role = xstrdup("assistant"); |
| 4416 | flush_msg.content = xstrdup(""); |
| 4417 | flush_msg.reasoning = buf_take(&pending_reasoning); |
| 4418 | chat_msgs_push(msgs, flush_msg); |
| 4419 | } |
| 4420 | if (!strcmp(t, "message")) { |
| 4421 | chat_msg msg = {0}; |
| 4422 | msg.role = xstrdup(role ? role : "user"); |
| 4423 | msg.content = content ? content : xstrdup(""); |
| 4424 | content = NULL; |
| 4425 | msg.images = content_images; |
| 4426 | memset(&content_images, 0, sizeof(content_images)); |
| 4427 | if (!strcmp(msg.role, "assistant") && pending_reasoning.len) { |
| 4428 | msg.reasoning = buf_take(&pending_reasoning); |
| 4429 | } |
| 4430 | chat_msgs_push(msgs, msg); |
| 4431 | } else if (!strcmp(t, "function_call") || !strcmp(t, "custom_tool_call")) { |
no test coverage detected