feat(coding-agent): enhanced skipped file messages to include file size information

- Enhanced skipped file messages to include file size information for better user visibility.
- Updated auto-read skip messages to display actual file size when files are too large to process.
- Inlined MAX_CLI_TEXT_BYTES calculation for improved code clarity.
This commit is contained in:
can1357
2026-02-10 03:36:14 +01:00
parent 0f3014a0f0
commit d668146ecf
4 changed files with 49 additions and 15 deletions
+39 -8
View File
@@ -38,7 +38,11 @@ use clap::Parser;
use napi::{
bindgen_prelude::*,
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
tokio::{self, sync::Mutex as TokioMutex, time},
tokio::{
self,
sync::{Mutex as TokioMutex, mpsc},
time,
},
};
use napi_derive::napi;
use tokio::io::AsyncReadExt as _;
@@ -550,10 +554,11 @@ async fn run_shell_command(
}
let reader_cancel = CancellationToken::new();
let (activity_tx, mut activity_rx) = mpsc::channel::<()>(1);
let mut reader_handle = tokio::spawn({
let reader_cancel = reader_cancel.clone();
async move {
read_output(reader_file, on_chunk, reader_cancel).await;
read_output(reader_file, on_chunk, reader_cancel, activity_tx).await;
Result::<()>::Ok(())
}
});
@@ -585,12 +590,34 @@ async fn run_shell_command(
drop(params);
// The foreground command can complete while background jobs keep the
// stdout/stderr pipe open. Don't hang forever waiting for EOF; drain briefly,
// then cancel.
if time::timeout(Duration::from_millis(200), &mut reader_handle)
.await
.is_err()
{
// stdout/stderr pipe open. Don't hang forever waiting for EOF; drain output
// for a short period, then cancel.
const POST_EXIT_IDLE: Duration = Duration::from_millis(250);
const POST_EXIT_MAX: Duration = Duration::from_secs(2);
let mut reader_finished = false;
let mut idle_timer = Box::pin(time::sleep(POST_EXIT_IDLE));
let mut max_timer = Box::pin(time::sleep(POST_EXIT_MAX));
loop {
tokio::select! {
res = &mut reader_handle => {
let _ = res;
reader_finished = true;
break;
}
msg = activity_rx.recv() => {
if msg.is_none() {
break;
}
idle_timer.as_mut().reset(time::Instant::now() + POST_EXIT_IDLE);
}
() = &mut idle_timer => break,
() = &mut max_timer => break,
}
}
if !reader_finished {
reader_cancel.cancel();
let _ = reader_handle.await;
}
@@ -730,6 +757,7 @@ async fn read_output(
reader: fs::File,
on_chunk: Option<ThreadsafeFunction<String>>,
cancel_token: CancellationToken,
activity: mpsc::Sender<()>,
) {
const REPLACEMENT: &str = "\u{FFFD}";
const BUF: usize = 4096;
@@ -751,6 +779,9 @@ async fn read_output(
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(_) => break,
};
if n > 0 {
let _ = activity.try_send(());
}
it += n;
// Consume as much of `pending` as is decodable *right now*.
+4 -1
View File
@@ -1,6 +1,7 @@
# Changelog
## [Unreleased]
### Added
- Added current date to system prompt context in YYYY-MM-DD format for date-aware agent reasoning
@@ -9,6 +10,7 @@
### Changed
- Changed skipped file messages to include file size information for better visibility into why files were excluded
- Changed file processing to skip reading files exceeding 5MB (text) or 25MB (images) and include them as path-only references instead
- Changed @mention auto-reading to skip files exceeding 5MB (text) or 25MB (images) to prevent out-of-memory issues with large files
- Clarified that subagents automatically inherit full system prompt including AGENTS.md, context files, and skills — do not repeat project rules or conventions in task context
@@ -19,8 +21,9 @@
### Fixed
- Fixed bash tool hanging when commands spawn background jobs by properly detecting foreground process completion
- Fixed crash when auto-reading @mentions for very large files by skipping content injection and referencing the path only
- Fixed bash tool occasionally hanging after command completion when background jobs keep stdout/stderr open
- Fixed crash when auto-reading @mentions for very large files by skipping content injection with an explicit "skipped" note
- Improved bash tool output draining after foreground completion to reduce tail output truncation
## [11.8.0] - 2026-02-10
### Added
@@ -7,13 +7,13 @@ import type { ImageContent } from "@oh-my-pi/pi-ai";
import { isEnoent } from "@oh-my-pi/pi-utils";
import chalk from "chalk";
import { resolveReadPath } from "../tools/path-utils";
import { DEFAULT_MAX_BYTES, formatSize } from "../tools/truncate";
import { formatSize } from "../tools/truncate";
import { formatDimensionNote, resizeImage } from "../utils/image-resize";
import { detectSupportedImageMimeTypeFromFile } from "../utils/mime";
// Keep CLI startup responsive and avoid OOM when users pass huge files.
// If a file exceeds these limits, we include it as a path-only <file/> block.
const MAX_CLI_TEXT_BYTES = DEFAULT_MAX_BYTES * 100; // 5MB
const MAX_CLI_TEXT_BYTES = 5 * 1024 * 1024; // 5MB
const MAX_CLI_IMAGE_BYTES = 25 * 1024 * 1024; // 25MB
export interface ProcessedFiles {
@@ -53,7 +53,7 @@ export async function processFileArguments(fileArgs: string[], options?: Process
console.error(
chalk.yellow(`Warning: Skipping file contents (too large: ${formatSize(stat.size)}): ${absolutePath}`),
);
text += `<file name="${absolutePath}"></file>\n`;
text += `<file name="${absolutePath}">(skipped: too large, ${formatSize(stat.size)})</file>\n`;
continue;
}
@@ -24,7 +24,7 @@ const DEFAULT_DIR_LIMIT = 500;
// Avoid OOM when users @mention very large files. Above these limits we skip
// auto-reading and only include the path in the message.
const MAX_AUTO_READ_TEXT_BYTES = DEFAULT_MAX_BYTES * 100; // 5MB
const MAX_AUTO_READ_TEXT_BYTES = 5 * 1024 * 1024; // 5MB
const MAX_AUTO_READ_IMAGE_BYTES = 25 * 1024 * 1024; // 25MB
function isMentionBoundary(text: string, index: number): boolean {
@@ -191,7 +191,7 @@ export async function generateFileMentionMessages(
if (stat.size > MAX_AUTO_READ_IMAGE_BYTES) {
files.push({
path: filePath,
content: "",
content: `(skipped auto-read: too large, ${formatSize(stat.size)})`,
byteSize: stat.size,
skippedReason: "tooLarge",
});
@@ -227,7 +227,7 @@ export async function generateFileMentionMessages(
if (stat.size > MAX_AUTO_READ_TEXT_BYTES) {
files.push({
path: filePath,
content: "",
content: `(skipped auto-read: too large, ${formatSize(stat.size)})`,
byteSize: stat.size,
skippedReason: "tooLarge",
});