feat: add truncation to Task tool output and auto-cleanup overflow files (#588)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
10
src/index.ts
10
src/index.ts
@@ -362,6 +362,16 @@ async function main(): Promise<void> {
|
||||
// Silently ignore update failures
|
||||
});
|
||||
|
||||
// Clean up old overflow files (non-blocking, 24h retention)
|
||||
const { cleanupOldOverflowFiles } = await import("./tools/impl/overflow");
|
||||
Promise.resolve().then(() => {
|
||||
try {
|
||||
cleanupOldOverflowFiles(process.cwd());
|
||||
} catch {
|
||||
// Silently ignore cleanup failures
|
||||
}
|
||||
});
|
||||
|
||||
// Parse command-line arguments (Bun-idiomatic approach using parseArgs)
|
||||
// Preprocess args to support --conv as alias for --conversation
|
||||
const processedArgs = process.argv.map((arg) =>
|
||||
|
||||
@@ -170,6 +170,33 @@ describe("overflow utilities", () => {
|
||||
|
||||
expect(deletedCount).toBe(0);
|
||||
});
|
||||
|
||||
test("skips subdirectories without crashing", () => {
|
||||
// Create a test file and a subdirectory
|
||||
const content = "Test content";
|
||||
const filePath = writeOverflowFile(content, testWorkingDir, "TestTool");
|
||||
|
||||
// Create a subdirectory in the overflow dir
|
||||
const subDir = path.join(path.dirname(filePath), "subdir");
|
||||
fs.mkdirSync(subDir, { recursive: true });
|
||||
|
||||
// Make the file old
|
||||
const oldTime = Date.now() - 48 * 60 * 60 * 1000;
|
||||
fs.utimesSync(filePath, new Date(oldTime), new Date(oldTime));
|
||||
|
||||
// Cleanup should skip the directory and only delete the file
|
||||
const deletedCount = cleanupOldOverflowFiles(
|
||||
testWorkingDir,
|
||||
24 * 60 * 60 * 1000,
|
||||
);
|
||||
|
||||
expect(deletedCount).toBe(1);
|
||||
expect(fs.existsSync(filePath)).toBe(false);
|
||||
expect(fs.existsSync(subDir)).toBe(true);
|
||||
|
||||
// Clean up the subdir
|
||||
fs.rmdirSync(subDir);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOverflowStats", () => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
generateSubagentId,
|
||||
registerSubagent,
|
||||
} from "../../cli/helpers/subagentState.js";
|
||||
import { LIMITS, truncateByChars } from "./truncation.js";
|
||||
import { validateRequiredParams } from "./validation";
|
||||
|
||||
interface TaskArgs {
|
||||
@@ -114,7 +115,18 @@ export async function task(args: TaskArgs): Promise<string> {
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
return `${header}\n\n${result.report}`;
|
||||
const fullOutput = `${header}\n\n${result.report}`;
|
||||
const userCwd = process.env.USER_CWD || process.cwd();
|
||||
|
||||
// Apply truncation to prevent excessive token usage (same pattern as Bash tool)
|
||||
const { content: truncatedOutput } = truncateByChars(
|
||||
fullOutput,
|
||||
LIMITS.TASK_OUTPUT_CHARS,
|
||||
"Task",
|
||||
{ workingDirectory: userCwd, toolName: "Task" },
|
||||
);
|
||||
|
||||
return truncatedOutput;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
completeSubagent(subagentId, { success: false, error: errorMessage });
|
||||
|
||||
@@ -113,17 +113,33 @@ export function cleanupOldOverflowFiles(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(overflowDir);
|
||||
let files: string[];
|
||||
try {
|
||||
files = fs.readdirSync(overflowDir);
|
||||
} catch {
|
||||
// Directory may have been deleted or become inaccessible
|
||||
return 0;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
let deletedCount = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(overflowDir, file);
|
||||
const stats = fs.statSync(filePath);
|
||||
try {
|
||||
const stats = fs.statSync(filePath);
|
||||
|
||||
if (now - stats.mtimeMs > maxAgeMs) {
|
||||
fs.unlinkSync(filePath);
|
||||
deletedCount++;
|
||||
// Skip directories (shouldn't exist, but be safe)
|
||||
if (stats.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (now - stats.mtimeMs > maxAgeMs) {
|
||||
fs.unlinkSync(filePath);
|
||||
deletedCount++;
|
||||
}
|
||||
} catch {
|
||||
// File may have been deleted, or permission error - skip it
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { OVERFLOW_CONFIG, writeOverflowFile } from "./overflow.js";
|
||||
export const LIMITS = {
|
||||
// Command output limits
|
||||
BASH_OUTPUT_CHARS: 30_000, // 30K characters for bash/shell output
|
||||
TASK_OUTPUT_CHARS: 30_000, // 30K characters for subagent task output
|
||||
|
||||
// File reading limits
|
||||
READ_MAX_LINES: 2_000, // Max lines per file read
|
||||
|
||||
Reference in New Issue
Block a user