Fix UTF-8 slicing panics: use floor_char_boundary for all truncation

Byte-position truncation (&s[..s.len().min(N)]) panics when position
N lands inside a multi-byte character. Fixed in parser debug logging,
API error messages, oneshot response logging, and CLI agent display.

Also fixed tool dispatch permissions (removed global fallback).

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-08 19:33:05 -04:00
parent 1776222b07
commit fba8fcc587
6 changed files with 12 additions and 10 deletions

View file

@ -245,7 +245,7 @@ pub(crate) async fn send_and_check(
status,
elapsed.as_secs_f64(),
url,
&body[..body.len().min(500)]
&body[..body.floor_char_boundary(body.len().min(500))]
);
if let Some(json) = request_json {
let log_dir = dirs::home_dir()
@ -260,7 +260,7 @@ pub(crate) async fn send_and_check(
);
}
}
anyhow::bail!("HTTP {} ({}): {}", status, url, &body[..body.len().min(1000)]);
anyhow::bail!("HTTP {} ({}): {}", status, url, &body[..body.floor_char_boundary(body.len().min(1000))]);
}
if debug {

View file

@ -507,7 +507,8 @@ impl ResponseParser {
if let Some(ref mut f) = log_file {
use std::io::Write;
for c in &calls {
let _ = writeln!(f, "tool_call: {} args={}", c.name, &c.arguments[..c.arguments.len().min(200)]);
let end = c.arguments.floor_char_boundary(c.arguments.len().min(200));
let _ = writeln!(f, "tool_call: {} args={}", c.name, &c.arguments[..end]);
}
}
}
@ -523,12 +524,13 @@ impl ResponseParser {
let _ = writeln!(f, "done: {} chars, {} <tool_call> tags, ctx: {} tokens",
full_text.len(), tc_count, ctx_tokens);
if tc_count == 0 && full_text.len() > 0 {
let _ = writeln!(f, "full text:\n{}", &full_text[..full_text.len().min(2000)]);
let end = full_text.floor_char_boundary(full_text.len().min(2000));
let _ = writeln!(f, "full text:\n{}", &full_text[..end]);
}
for (i, part) in full_text.split("<tool_call>").enumerate() {
if i > 0 {
let _ = writeln!(f, "tool_call body: {}...",
&part[..part.len().min(200)]);
let end = part.floor_char_boundary(part.len().min(200));
let _ = writeln!(f, "tool_call body: {}...", &part[..end]);
}
}
}

View file

@ -179,7 +179,7 @@ impl AutoAgent {
}
dbglog!("[auto] {} response: {}",
self.name, &text[..text.len().min(200)]);
self.name, &text[..text.floor_char_boundary(text.len().min(200))]);
if next_step < self.steps.len() {
if let Some(ref check) = bail_fn {