fix: pipe oversized claude prompts on windows

This commit is contained in:
lucaronin
2026-05-11 12:30:44 +02:00
parent 6c21aa1087
commit e727afdd81
4 changed files with 908 additions and 536 deletions

View File

@@ -1,7 +1,7 @@
use crate::ai_agents::{AiAgentPermissionMode, AiAgentStreamEvent};
use serde::Deserialize;
use std::ffi::OsString;
use std::io::BufRead;
use std::io::{BufRead, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus};
@@ -24,6 +24,27 @@ pub(crate) struct AgentCommandTarget {
pub first_arg: Option<PathBuf>,
}
pub(crate) struct JsonLineProcess<'a> {
command: Command,
process_name: &'static str,
stdin_input: Option<&'a str>,
}
impl<'a> JsonLineProcess<'a> {
pub(crate) fn new(command: Command, process_name: &'static str) -> Self {
Self {
command,
process_name,
stdin_input: None,
}
}
pub(crate) fn with_stdin(mut self, stdin_input: Option<&'a str>) -> Self {
self.stdin_input = stdin_input;
self
}
}
pub(crate) fn build_prompt(message: &str, system_prompt: Option<&str>) -> String {
match system_prompt
.map(str::trim)
@@ -266,19 +287,44 @@ where
}
pub(crate) fn run_json_line_process<Event, F, H>(
mut command: Command,
command: Command,
process_name: &'static str,
emit: &mut F,
error_event: impl Fn(String) -> Event,
handle_json: H,
) -> Result<JsonLineRun, String>
where
F: FnMut(Event),
H: FnMut(&serde_json::Value, &mut F, &mut String),
{
run_json_line_process_with_stdin(
JsonLineProcess::new(command, process_name),
emit,
error_event,
handle_json,
)
}
pub(crate) fn run_json_line_process_with_stdin<Event, F, H>(
mut process: JsonLineProcess<'_>,
emit: &mut F,
error_event: impl Fn(String) -> Event,
mut handle_json: H,
) -> Result<JsonLineRun, String>
where
F: FnMut(Event),
H: FnMut(&serde_json::Value, &mut F, &mut String),
{
let mut child = command
if process.stdin_input.is_some() {
process.command.stdin(std::process::Stdio::piped());
}
let mut child = process
.command
.spawn()
.map_err(|error| format_spawn_error(process_name, &error))?;
.map_err(|error| format_spawn_error(process.process_name, &error))?;
let stdin_write_error =
write_stdin_input(&mut child, process.process_name, process.stdin_input);
let stdout = child.stdout.take().ok_or("No stdout handle")?;
let reader = std::io::BufReader::new(stdout);
let mut session_id = String::new();
@@ -302,6 +348,7 @@ where
let status = child
.wait()
.map_err(|error| format!("Wait failed: {error}"))?;
let stderr_output = with_stdin_write_error(stderr_output, stdin_write_error);
Ok(JsonLineRun {
session_id,
@@ -310,6 +357,36 @@ where
})
}
fn write_stdin_input(
child: &mut std::process::Child,
process_name: &str,
stdin_input: Option<&str>,
) -> Option<String> {
let input = stdin_input?;
let Some(mut stdin) = child.stdin.take() else {
return Some(format!(
"Failed to write {process_name} stdin: no stdin handle"
));
};
stdin
.write_all(input.as_bytes())
.err()
.map(|error| format!("Failed to write {process_name} stdin: {error}"))
}
fn with_stdin_write_error(mut stderr_output: String, stdin_write_error: Option<String>) -> String {
let Some(error) = stdin_write_error else {
return stderr_output;
};
if !stderr_output.is_empty() {
stderr_output.push('\n');
}
stderr_output.push_str(&error);
stderr_output
}
fn format_spawn_error(process_name: &str, error: &std::io::Error) -> String {
if error.kind() == std::io::ErrorKind::NotFound {
return format!(