mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
Bound code-mode output previews across result blocks (#46073)
## Why Multiple result blocks and wrapped text can exceed the compact preview's row budget. Truncation must also preserve trailing failure diagnostics. ## What changed Apply a shared rendered-row limit to code-mode output previews after wrapping, including overlong URLs. Keep the first and last rows around an `… more · ctrl+t` indicator, while retaining the full text in the expanded transcript. ## Testing Add coverage for multiple result blocks, trailing failure diagnostics, Unicode wrapping, long URLs, and error results at several terminal widths, including transcript preservation. GitOrigin-RevId: ad9cf0fafb909e4df9a132f83722d6cd4e669687
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
//! MCP tool-call, inventory, and output history cells.
|
||||
//! Code-mode output previews share a rendered-row budget across all result blocks;
|
||||
//! the expanded transcript retains the full text.
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -276,6 +278,22 @@ impl McpToolCallCell {
|
||||
}
|
||||
}
|
||||
|
||||
if compact {
|
||||
// Adaptive wrapping keeps URLs intact; split overlong preview rows before counting
|
||||
// them so a URL cannot exceed the budget. The transcript keeps the original URL.
|
||||
detail_lines = crate::wrapping::word_wrap_lines(detail_lines, detail_wrap_width);
|
||||
if detail_lines.len() > TOOL_CALL_MAX_LINES {
|
||||
// Retain the tail so stdout cannot crowd out a trailing failure diagnostic.
|
||||
let tail = detail_lines.split_off(detail_lines.len() - TOOL_CALL_MAX_LINES / 2);
|
||||
detail_lines.truncate((TOOL_CALL_MAX_LINES - 1) / 2);
|
||||
detail_lines.push(crate::line_truncation::truncate_line_to_width(
|
||||
"… more · ctrl+t".dim().into(),
|
||||
detail_wrap_width,
|
||||
));
|
||||
detail_lines.extend(tail);
|
||||
}
|
||||
}
|
||||
|
||||
if !detail_lines.is_empty() {
|
||||
let initial_prefix: Span<'static> = if inline_invocation {
|
||||
" └ ".dim()
|
||||
|
||||
@@ -133,6 +133,141 @@ fn result(content: Vec<Value>) -> CallToolResult {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_mode_output_shares_a_row_budget_across_blocks() {
|
||||
let mut cell = new_active_mcp_tool_call(
|
||||
"browser-call".to_string(),
|
||||
McpInvocation {
|
||||
server: "node_repl".to_string(),
|
||||
tool: "js".to_string(),
|
||||
arguments: Some(json!({"title": "Inspect page", "code": "await tab.snapshot()"})),
|
||||
},
|
||||
/*animations_enabled*/ false,
|
||||
);
|
||||
cell.complete(
|
||||
Duration::ZERO,
|
||||
Ok(result(vec![
|
||||
json!({"type": "text", "text": "Script completed\nOutput:\n"}),
|
||||
json!({"type": "text", "text": "Page title\nNavigation\nMain content"}),
|
||||
json!({"type": "text", "text": "Button\nLink\nFooter"}),
|
||||
])),
|
||||
);
|
||||
|
||||
let display = cell
|
||||
.display_lines(/*width*/ 40)
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
insta::assert_snapshot!(display, @"
|
||||
• Called Inspect page
|
||||
└ Page title
|
||||
Navigation
|
||||
… more · ctrl+t
|
||||
Link
|
||||
Footer
|
||||
");
|
||||
let transcript = cell
|
||||
.transcript_lines(/*width*/ 100)
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(transcript.contains("await tab.snapshot()"));
|
||||
assert!(transcript.ends_with(" Button\n Link\n Footer"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_mode_output_keeps_trailing_failure_diagnostics() {
|
||||
let mut cell = new_active_mcp_tool_call(
|
||||
"browser-error".to_string(),
|
||||
McpInvocation {
|
||||
server: "node_repl".to_string(),
|
||||
tool: "js".to_string(),
|
||||
arguments: Some(json!({"title": "Inspect page"})),
|
||||
},
|
||||
/*animations_enabled*/ false,
|
||||
);
|
||||
cell.complete(
|
||||
Duration::ZERO,
|
||||
Ok(CallToolResult {
|
||||
is_error: Some(true),
|
||||
..result(vec![
|
||||
json!({"type": "text", "text": "Script failed"}),
|
||||
json!({"type": "text", "text": "Page title\nNavigation\nMain content\nButton\nLink\nFooter"}),
|
||||
json!({"type": "text", "text": "Script error:\npermission denied"}),
|
||||
])
|
||||
}),
|
||||
);
|
||||
let display = cell
|
||||
.display_lines(/*width*/ 40)
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
insta::assert_snapshot!(display, @"
|
||||
• Called Inspect page
|
||||
└ Script failed
|
||||
Page title
|
||||
… more · ctrl+t
|
||||
Script error:
|
||||
permission denied
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_mode_output_row_budget_applies_after_wrapping_and_to_errors() {
|
||||
let output = format!("{}\ntranscript tail", "Browser 页面 👩💻\n".repeat(40));
|
||||
for server in ["node_repl", "cua_repl"] {
|
||||
for completion in [
|
||||
Ok(result(vec![json!({"type": "text", "text": output})])),
|
||||
Ok(result(vec![json!({
|
||||
"type": "text",
|
||||
"text": format!("https://example.com/{}\ntranscript tail", "页面".repeat(100)),
|
||||
})])),
|
||||
Ok(CallToolResult {
|
||||
is_error: Some(true),
|
||||
..result(vec![json!({"type": "text", "text": output})])
|
||||
}),
|
||||
Err(output.clone()),
|
||||
] {
|
||||
let mut cell = new_active_mcp_tool_call(
|
||||
"browser-call".to_string(),
|
||||
McpInvocation {
|
||||
server: server.to_string(),
|
||||
tool: "js".to_string(),
|
||||
arguments: Some(json!({"title": "Inspect"})),
|
||||
},
|
||||
/*animations_enabled*/ false,
|
||||
);
|
||||
cell.complete(Duration::ZERO, completion);
|
||||
for width in [20, 40, 80] {
|
||||
let display = cell.display_lines(width);
|
||||
assert_eq!(display.len(), 1 + TOOL_CALL_MAX_LINES);
|
||||
assert!(
|
||||
display
|
||||
.iter()
|
||||
.all(|line| line.width() <= usize::from(width))
|
||||
);
|
||||
assert_eq!(display[3].to_string(), " … more · ctrl+t");
|
||||
let transcript = cell
|
||||
.transcript_lines(width)
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(
|
||||
transcript
|
||||
.split_whitespace()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
.contains("transcript tail")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn projected_content_preserves_width_dependent_rendering() {
|
||||
let text = "{\"result\": [1, 2, 3], \"text\": \"long output 🦀\"}";
|
||||
|
||||
@@ -1444,8 +1444,7 @@ fn code_mode_tool_call_uses_title_and_preserves_full_transcript() {
|
||||
• Called Inspect Spotify workspace
|
||||
└ 012345678901234567890123456789012345
|
||||
67890123456789012345678901234567
|
||||
89012345678901234567890123456789
|
||||
01234567890123456789012345678901
|
||||
… more · ctrl+t
|
||||
23456789012345678901234567890123
|
||||
45678901...
|
||||
|
||||
|
||||
Reference in New Issue
Block a user