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:
@@ -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*.
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user