malvin 0.2.7

Non-interactive research and coding agent
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum SessionUpdateChunkKind {
    Message,
    Thought,
}

pub(crate) const ACP_VERBOSE_COALESCE_MAX: usize = 125;

pub(crate) fn coalesce_append_chunk(
    buf: &mut String,
    buf_chars: &mut usize,
    chunk: &str,
    emissions: &mut Vec<String>,
) {
    let mut pos = 0usize;
    let b = chunk.as_bytes();
    while pos < b.len() {
        if let Some(rel) = b[pos..].iter().position(|&c| c == b'\n') {
            let end = pos + rel;
            let piece = &chunk[pos..end];
            buf.push_str(piece);
            *buf_chars += piece.chars().count();
            coalesce_flush_cap(buf, buf_chars, emissions);
            coalesce_flush_nonempty(buf, buf_chars, emissions);
            pos = end;
            while pos < b.len() && b[pos] == b'\n' {
                pos += 1;
            }
        } else {
            let piece = &chunk[pos..];
            buf.push_str(piece);
            *buf_chars += piece.chars().count();
            coalesce_flush_cap(buf, buf_chars, emissions);
            break;
        }
    }
}

pub(crate) fn coalesce_char_boundary_at(s: &str, n_chars: usize) -> usize {
    s.char_indices().nth(n_chars).map_or(s.len(), |(i, _)| i)
}

pub(crate) fn coalesce_flush_cap(
    buf: &mut String,
    buf_chars: &mut usize,
    emissions: &mut Vec<String>,
) {
    while *buf_chars >= ACP_VERBOSE_COALESCE_MAX {
        let hard_end = coalesce_char_boundary_at(buf, ACP_VERBOSE_COALESCE_MAX);
        let (emit_end, drain_end, drained_chars) = coalesce_word_split_points(buf, hard_end);
        let emitted = buf[..emit_end].to_string();
        buf.drain(..drain_end);
        *buf_chars -= drained_chars;
        emissions.push(emitted);
    }
}

fn coalesce_word_split_points(buf: &str, hard_end: usize) -> (usize, usize, usize) {
    let region = &buf[..hard_end];
    let mut last_space_start: Option<usize> = None;
    for (i, ch) in region.char_indices() {
        if ch == ' ' {
            last_space_start = Some(i);
        }
    }
    if let Some(space_start) = last_space_start {
        let mut drain_end = space_start;
        for ch in buf[space_start..hard_end].chars() {
            if ch == ' ' {
                drain_end += ch.len_utf8();
            } else {
                break;
            }
        }
        let emit_end = space_start;
        let emit_chars = buf[..emit_end].chars().count();
        if emit_chars > 0 {
            let drained_chars = buf[..drain_end].chars().count();
            return (emit_end, drain_end, drained_chars);
        }
    }
    (hard_end, hard_end, ACP_VERBOSE_COALESCE_MAX)
}

pub(crate) fn coalesce_flush_nonempty(
    buf: &mut String,
    buf_chars: &mut usize,
    emissions: &mut Vec<String>,
) {
    if !buf.is_empty() {
        emissions.push(std::mem::take(buf));
        *buf_chars = 0;
    }
}

#[cfg(test)]
mod coalesce_tests {
    use super::{ACP_VERBOSE_COALESCE_MAX, coalesce_flush_cap};

    #[test]
    fn coalesce_flush_cap_preserves_tab_boundary_content() {
        let original = format!("{}\t{}", "a".repeat(50), "b".repeat(100));
        let mut buf = original.clone();
        let mut buf_chars = buf.chars().count();
        let mut emissions = Vec::new();
        coalesce_flush_cap(&mut buf, &mut buf_chars, &mut emissions);
        assert_eq!(emissions.len(), 1);
        assert_eq!(emissions[0].chars().count(), ACP_VERBOSE_COALESCE_MAX);
        let rebuilt = format!("{}{}", emissions.concat(), buf);
        assert_eq!(rebuilt, original);
        assert!(rebuilt.contains('\t'));
    }
}