mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-01 00:46:42 +00:00
fix(agents): bound search tool stderr
This commit is contained in:
@@ -8,7 +8,7 @@ import { keyHint } from "../../modes/interactive/components/keybinding-hints.js"
|
||||
import type { AgentTool } from "../../runtime/index.js";
|
||||
import { ensureTool } from "../../utils/tools-manager.js";
|
||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
||||
import { normalizePositiveLimit } from "./limits.js";
|
||||
import { appendBoundedTextTail, normalizePositiveLimit } from "./limits.js";
|
||||
import { resolveToCwd } from "./path-utils.js";
|
||||
import { getTextOutput, invalidArgText, shortenPath, str } from "./render-utils.js";
|
||||
import type { FindToolDetails } from "./tool-contracts.js";
|
||||
@@ -277,7 +277,7 @@ export function createFindToolDefinition(
|
||||
};
|
||||
|
||||
child.stderr?.on("data", (chunk) => {
|
||||
stderr += chunk.toString();
|
||||
stderr = appendBoundedTextTail(stderr, chunk);
|
||||
});
|
||||
|
||||
rl.on("line", (line) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { keyHint } from "../../modes/interactive/components/keybinding-hints.js"
|
||||
import type { AgentTool } from "../../runtime/index.js";
|
||||
import { ensureTool } from "../../utils/tools-manager.js";
|
||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
||||
import { normalizePositiveLimit } from "./limits.js";
|
||||
import { appendBoundedTextTail, normalizePositiveLimit } from "./limits.js";
|
||||
import { resolveToCwd } from "./path-utils.js";
|
||||
import { getTextOutput, invalidArgText, shortenPath, str } from "./render-utils.js";
|
||||
import type { GrepToolDetails } from "./tool-contracts.js";
|
||||
@@ -270,7 +270,7 @@ export function createGrepToolDefinition(
|
||||
};
|
||||
signal?.addEventListener("abort", onAbort, { once: true });
|
||||
child.stderr?.on("data", (chunk) => {
|
||||
stderr += chunk.toString();
|
||||
stderr = appendBoundedTextTail(stderr, chunk);
|
||||
});
|
||||
|
||||
const formatBlock = async (filePath: string, lineNumber: number): Promise<string[]> => {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { normalizePositiveLimit } from "./limits.js";
|
||||
import {
|
||||
appendBoundedTextTail,
|
||||
normalizePositiveLimit,
|
||||
SESSION_TOOL_STDERR_TAIL_BYTES,
|
||||
} from "./limits.js";
|
||||
|
||||
describe("session tool limits", () => {
|
||||
it.each([
|
||||
@@ -13,4 +17,25 @@ describe("session tool limits", () => {
|
||||
])("normalizes %s to %s", (input, expected) => {
|
||||
expect(normalizePositiveLimit(input, 500)).toBe(expected);
|
||||
});
|
||||
|
||||
it("keeps a bounded tail of accumulated child output", () => {
|
||||
let output = appendBoundedTextTail("old-", "middle-", 12);
|
||||
output = appendBoundedTextTail(output, "recent", 12);
|
||||
|
||||
expect(output).toBe("iddle-recent");
|
||||
expect(Buffer.byteLength(output, "utf8")).toBeLessThanOrEqual(12);
|
||||
});
|
||||
|
||||
it("clips oversized chunks to the configured tail bytes", () => {
|
||||
const output = appendBoundedTextTail("ignored", Buffer.from("x".repeat(128)), 16);
|
||||
|
||||
expect(output).toBe("x".repeat(16));
|
||||
expect(Buffer.byteLength(output, "utf8")).toBe(16);
|
||||
});
|
||||
|
||||
it("uses the session stderr tail limit by default", () => {
|
||||
const output = appendBoundedTextTail("", "x".repeat(SESSION_TOOL_STDERR_TAIL_BYTES + 1));
|
||||
|
||||
expect(Buffer.byteLength(output, "utf8")).toBe(SESSION_TOOL_STDERR_TAIL_BYTES);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,3 +4,27 @@ export function normalizePositiveLimit(value: number | undefined, fallback: numb
|
||||
}
|
||||
return Math.max(1, Math.floor(value));
|
||||
}
|
||||
|
||||
export const SESSION_TOOL_STDERR_TAIL_BYTES = 64 * 1024;
|
||||
|
||||
export function appendBoundedTextTail(
|
||||
current: string,
|
||||
chunk: Buffer | string,
|
||||
maxBytes = SESSION_TOOL_STDERR_TAIL_BYTES,
|
||||
): string {
|
||||
const effectiveMaxBytes = normalizePositiveLimit(maxBytes, SESSION_TOOL_STDERR_TAIL_BYTES);
|
||||
const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
||||
if (chunkBuffer.byteLength >= effectiveMaxBytes) {
|
||||
return chunkBuffer.subarray(chunkBuffer.byteLength - effectiveMaxBytes).toString("utf8");
|
||||
}
|
||||
|
||||
const currentBuffer = Buffer.from(current);
|
||||
const nextBytes = currentBuffer.byteLength + chunkBuffer.byteLength;
|
||||
if (nextBytes <= effectiveMaxBytes) {
|
||||
return `${current}${chunkBuffer.toString("utf8")}`;
|
||||
}
|
||||
|
||||
const currentTailBytes = Math.max(0, effectiveMaxBytes - chunkBuffer.byteLength);
|
||||
const currentTail = currentBuffer.subarray(currentBuffer.byteLength - currentTailBytes);
|
||||
return Buffer.concat([currentTail, chunkBuffer], effectiveMaxBytes).toString("utf8");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user