Load a KV file and optionally verify either its session identity or exact * rendered text. sysprompt.kv uses exact text because the file name is fixed; * saved sessions use their filename SHA: modern agent sessions hash the title * trailer plus created_at, while legacy sessions still hash rendered text. */
| 3769 | agent_stream_note_plain_dsml_byte(sr, sr->dsml_start_tail[i]); |
| 3770 | if (sr->parser->state == AGENT_DSML_ERROR) break; |
| 3771 | } |
| 3772 | if (sr->parser->state == AGENT_DSML_ERROR) { |
| 3773 | sr->dsml_start_len = 0; |
| 3774 | return; |
| 3775 | } |
| 3776 | sr->dsml_start_tail[0] = start[0]; |
| 3777 | sr->dsml_start_len = 1; |
| 3778 | return; |
| 3779 | } |
| 3780 | agent_stream_flush_start_tail(sr); |
| 3781 | return; |
| 3782 | } |
| 3783 | |
| 3784 | sr->post_think_gap = false; |
| 3785 | renderer_write_char(sr->renderer, c); |
| 3786 | agent_stream_note_plain_dsml_byte(sr, c); |
| 3787 | } |
| 3788 | |
| 3789 | /* This is the single streaming display state machine for assistant output. It |
| 3790 | * hides raw DSML as soon as the tool_calls marker is complete, lets the DSML |
| 3791 | * parser continue building executable calls, and paints semantic tool output |
| 3792 | * from parser state changes. The sampled transcript remains unchanged: only |
| 3793 | * the terminal projection is rewritten. */ |
| 3794 | static void agent_stream_text(agent_stream_renderer *sr, const char *text, size_t len, bool finish) { |
| 3795 | const char *think_open = "<think>"; |
| 3796 | const char *think_close = "</think>"; |
| 3797 | size_t total = sr->pending_len + len; |
| 3798 | char *buf = xmalloc(total ? total : 1); |
| 3799 | if (sr->pending_len) memcpy(buf, sr->pending, sr->pending_len); |
| 3800 | if (len) memcpy(buf + sr->pending_len, text, len); |
| 3801 | sr->pending_len = 0; |
| 3802 | |
| 3803 | /* The UI may reset terminal attributes while redrawing the editable prompt |
| 3804 | * between generated chunks. If a DSML parameter is still streaming, make |
| 3805 | * each new token fragment self-contained by restoring the active parameter |
| 3806 | * color before visible bytes are projected. This keeps the prompt normal |
| 3807 | * without sacrificing long write/edit content coloring. */ |
| 3808 | if (len) agent_tool_viz_restore_param_color(sr); |
| 3809 | if (len && !sr->dsml_active) renderer_restore_text_attrs(sr->renderer); |
| 3810 | |
| 3811 | size_t i = 0; |
| 3812 | while (i < total) { |
| 3813 | char *cur = buf + i; |
| 3814 | size_t rem = total - i; |
| 3815 | if (!sr->dsml_active && bytes_has_prefix(cur, rem, think_open)) { |
| 3816 | agent_stream_flush_start_tail(sr); |
| 3817 | sr->post_think_gap = false; |
| 3818 | sr->in_think = true; |
| 3819 | sr->renderer->in_think = true; |
| 3820 | i += strlen(think_open); |
| 3821 | continue; |
| 3822 | } |
| 3823 | if (!sr->dsml_active && bytes_has_prefix(cur, rem, think_close)) { |
| 3824 | agent_stream_flush_start_tail(sr); |
| 3825 | sr->in_think = false; |
| 3826 | sr->renderer->in_think = false; |
| 3827 | renderer_reset_color(sr->renderer); |
| 3828 | if (!sr->renderer->last_output_newline) |
no test coverage detected