cap to full

This commit is contained in:
Ahmed Ibrahim
2025-08-22 18:08:52 -07:00
parent a5c14eb8c0
commit 1a04fa0379
4 changed files with 23 additions and 68 deletions

View File

@@ -268,7 +268,7 @@ For casual greetings, acknowledgements, or other one-off conversational messages
When using the shell, you must adhere to the following guidelines:
- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)
- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.
- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output is not truncated by Codex; the full stdout/stderr streams are available to clients (TUI and MCP). The model receives only a formatted summary of command output.
## `update_plan`

View File

@@ -726,11 +726,9 @@ impl Session {
duration,
exit_code,
} = output;
// Because stdout and stderr could each be up to 100 KiB, we send
// truncated versions.
const MAX_STREAM_OUTPUT: usize = 5 * 1024; // 5KiB
let stdout = stdout.text.chars().take(MAX_STREAM_OUTPUT).collect();
let stderr = stderr.text.chars().take(MAX_STREAM_OUTPUT).collect();
// Send full stdout/stderr to clients; do not truncate.
let stdout = stdout.text.clone();
let stderr = stderr.text.clone();
let formatted_output = format_exec_output_str(output);
let aggregated_output: String = aggregated_output.text.clone();
@@ -2596,7 +2594,6 @@ fn format_exec_output(exec_output: &ExecToolCallOutput) -> String {
// round to 1 decimal place
let duration_seconds = ((duration.as_secs_f32()) * 10.0).round() / 10.0;
let formatted_output = format_exec_output_str(exec_output);
let formatted_output = format_exec_output_str(exec_output);
let payload = ExecOutput {

View File

@@ -31,8 +31,9 @@ use serde_bytes::ByteBuf;
// Maximum we send for each stream, which is either:
// - 10KiB OR
// - 256 lines
const MAX_STREAM_OUTPUT: usize = 10 * 1024;
const MAX_STREAM_OUTPUT_LINES: usize = 256;
// No caps: collect full stdout/stderr and aggregated output
const MAX_STREAM_OUTPUT: usize = usize::MAX;
const MAX_STREAM_OUTPUT_LINES: usize = usize::MAX;
const DEFAULT_TIMEOUT_MS: u64 = 10_000;
@@ -216,22 +217,8 @@ impl StreamOutput<Vec<u8>> {
}
#[inline]
fn copy_capped(
dst: &mut Vec<u8>,
src: &[u8],
remaining_bytes: &mut usize,
remaining_lines: &mut usize,
) {
for &b in src {
if *remaining_bytes == 0 || *remaining_lines == 0 {
break;
}
dst.push(b);
*remaining_bytes = remaining_bytes.saturating_sub(1);
if b == b'\n' {
*remaining_lines = remaining_lines.saturating_sub(1);
}
}
fn append_all(dst: &mut Vec<u8>, src: &[u8]) {
dst.extend_from_slice(src);
}
#[derive(Debug)]
@@ -338,28 +325,13 @@ pub(crate) async fn consume_truncated_output(
drop(agg_tx);
let mut combined_buf = Vec::with_capacity(MAX_STREAM_OUTPUT.min(8 * 1024));
let mut remaining_bytes = MAX_STREAM_OUTPUT;
let mut remaining_lines = MAX_STREAM_OUTPUT_LINES;
let mut combined_buf = Vec::with_capacity(8 * 1024);
while let Ok(chunk) = agg_rx.recv().await {
if remaining_bytes == 0 || remaining_lines == 0 {
continue;
}
copy_capped(
&mut combined_buf,
&chunk,
&mut remaining_bytes,
&mut remaining_lines,
);
append_all(&mut combined_buf, &chunk);
}
let truncated = remaining_lines == 0 || remaining_bytes == 0;
let aggregated_output = StreamOutput {
text: combined_buf,
truncated_after_lines: if truncated {
Some((MAX_STREAM_OUTPUT_LINES - remaining_lines) as u32)
} else {
None
},
truncated_after_lines: None,
};
Ok(RawExecToolCallOutput {
@@ -372,17 +344,16 @@ pub(crate) async fn consume_truncated_output(
async fn read_capped<R: AsyncRead + Unpin + Send + 'static>(
mut reader: R,
max_output: usize,
max_lines: usize,
_max_output: usize,
_max_lines: usize,
stream: Option<StdoutStream>,
is_stderr: bool,
aggregate_tx: Option<Sender<Vec<u8>>>,
) -> io::Result<StreamOutput<Vec<u8>>> {
let mut buf = Vec::with_capacity(max_output.min(8 * 1024));
let mut buf = Vec::with_capacity(8 * 1024);
let mut tmp = [0u8; 8192];
let mut remaining_bytes = max_output;
let mut remaining_lines = max_lines;
// No caps: append all bytes
loop {
let n = reader.read(&mut tmp).await?;
@@ -413,26 +384,13 @@ async fn read_capped<R: AsyncRead + Unpin + Send + 'static>(
let _ = tx.send(tmp[..n].to_vec()).await;
}
if remaining_bytes > 0 && remaining_lines > 0 {
copy_capped(
&mut buf,
&tmp[..n],
&mut remaining_bytes,
&mut remaining_lines,
);
}
// Continue reading to EOF to avoid back-pressure, but discard once caps are hit.
append_all(&mut buf, &tmp[..n]);
// Continue reading to EOF to avoid back-pressure
}
let truncated = remaining_lines == 0 || remaining_bytes == 0;
Ok(StreamOutput {
text: buf,
truncated_after_lines: if truncated {
Some((max_lines - remaining_lines) as u32)
} else {
None
},
truncated_after_lines: None,
})
}

View File

@@ -70,12 +70,12 @@ async fn truncates_output_lines() {
let output = run_test_cmd(tmp, cmd).await.unwrap();
let expected_output = (1..=256)
let expected_output = (1..=300)
.map(|i| format!("{i}\n"))
.collect::<Vec<_>>()
.join("");
assert_eq!(output.stdout.text, expected_output);
assert_eq!(output.stdout.truncated_after_lines, Some(256));
assert_eq!(output.stdout.truncated_after_lines, None);
}
/// Command succeeds with exit code 0 normally
@@ -91,8 +91,8 @@ async fn truncates_output_bytes() {
let output = run_test_cmd(tmp, cmd).await.unwrap();
assert_eq!(output.stdout.text.len(), 10240);
assert_eq!(output.stdout.truncated_after_lines, Some(10));
assert!(output.stdout.text.len() >= 15000);
assert_eq!(output.stdout.truncated_after_lines, None);
}
/// Command not found returns exit code 127, this is not considered a sandbox error