use super::*;
pub(crate) const NARRATION_TAG: &str = "narration";
pub(crate) fn thinking_signature_tag(signature: Option<&str>) -> Option<String> {
let sig = signature?;
if sig.is_empty() {
return None;
}
let bytes = crate::image::decode_base64(sig)?;
let f2 = last_len_delimited(&bytes, 2)?;
let f1 = last_len_delimited(f2, 1)?;
let f8 = last_len_delimited(f1, 8)?;
std::str::from_utf8(f8).ok().map(str::to_string)
}
const NARRATION_B64_ALIGNMENTS: [&str; 3] = ["bmFycmF0aW9u", "5hcnJhdGlv", "uYXJyYXRp"];
pub(crate) fn thinking_block_class(signature: Option<&str>) -> Class {
let Some(sig) = signature else {
return Class::AgentThinking;
};
let gate_open = NARRATION_B64_ALIGNMENTS.iter().any(|n| sig.contains(n))
|| sig.bytes().any(|b| b.is_ascii_whitespace());
if !gate_open {
return Class::AgentThinking;
}
if thinking_signature_tag(Some(sig)).as_deref() == Some(NARRATION_TAG) {
Class::AgentThinkingNarration
} else {
Class::AgentThinking
}
}
fn last_len_delimited(bytes: &[u8], want: u64) -> Option<&[u8]> {
let mut pos = 0usize;
let mut found: Option<&[u8]> = None;
while pos < bytes.len() {
let (key, n) = read_varint(&bytes[pos..])?;
pos += n;
let (field, wire) = (key >> 3, key & 7);
match wire {
0 => {
let (_, n) = read_varint(&bytes[pos..])?;
pos += n;
}
1 => pos = advance(pos, 8, bytes.len())?,
5 => pos = advance(pos, 4, bytes.len())?,
2 => {
let (len, n) = read_varint(&bytes[pos..])?;
pos += n;
let end = advance(pos, usize::try_from(len).ok()?, bytes.len())?;
if field == want {
found = Some(&bytes[pos..end]);
}
pos = end;
}
_ => return None,
}
}
found
}
fn advance(pos: usize, step: usize, len: usize) -> Option<usize> {
let next = pos.checked_add(step)?;
(next <= len).then_some(next)
}
fn read_varint(bytes: &[u8]) -> Option<(u64, usize)> {
let mut value = 0u64;
for (i, b) in bytes.iter().take(10).enumerate() {
value |= u64::from(b & 0x7f) << (7 * i as u32);
if b & 0x80 == 0 {
return Some((value, i + 1));
}
}
None
}