fix(agent): stabilize large tool results
Generic Container CI/CD / test-build-publish (push) Successful in 1m53s
Agent CI/CD v2 / build-test-publish-and-deploy (push) Successful in 1m53s

This commit is contained in:
2026-08-25 12:02:48 +08:00
parent 774f39cbbe
commit 004c9bb72d
17 changed files with 721 additions and 62 deletions
+28 -13
View File
@@ -21,7 +21,8 @@ const context: RuntimeSessionContext = {
const run = (
command: string,
options: {
maxOutputBytes?: number;
maxStderrBytes?: number;
maxStdoutBytes?: number;
terminationGraceMs?: number;
timeoutSec?: number;
} = {},
@@ -29,13 +30,14 @@ const run = (
executeCliCommand(context, command, options.timeoutSec ?? 1, {
apiBaseUrl: "http://127.0.0.1:8000",
cliPath,
maxOutputBytes: options.maxOutputBytes ?? 64,
maxStderrBytes: options.maxStderrBytes ?? 8,
maxStdoutBytes: options.maxStdoutBytes ?? 64,
terminationGraceMs: options.terminationGraceMs ?? 20,
});
describe("executeCliCommand", () => {
test("accepts output at the byte limit", async () => {
await expect(run("stdout 123456", { maxOutputBytes: 6 })).resolves.toMatchObject({
await expect(run("stdout 123456", { maxStdoutBytes: 6 })).resolves.toMatchObject({
outcome: "completed",
exitCode: 0,
status: 200,
@@ -43,8 +45,16 @@ describe("executeCliCommand", () => {
});
});
test("does not treat the OpenCode 12000-byte inline threshold as a CLI limit", async () => {
const stdout = "x".repeat(12_001);
const result = await run(`stdout ${stdout}`, { maxStdoutBytes: 128 * 1024 * 1024 });
expect(result.outcome).toBe("completed");
expect(result.stdout).toBe(stdout);
});
test("rejects multibyte output above the byte limit without returning a partial body", async () => {
await expect(run("stdout 水水", { maxOutputBytes: 5 })).resolves.toMatchObject({
await expect(run("stdout 水水", { maxStdoutBytes: 5 })).resolves.toMatchObject({
outcome: "output_limit",
exceededStream: "stdout",
status: 502,
@@ -53,13 +63,16 @@ describe("executeCliCommand", () => {
});
});
test("limits stderr independently", async () => {
await expect(run("stderr 1234567", { maxOutputBytes: 6 })).resolves.toMatchObject({
outcome: "output_limit",
exceededStream: "stderr",
status: 502,
stderr: "",
stdout: "",
test("truncates stderr independently without terminating a successful command", async () => {
await expect(
run("stderr-success 123456789", { maxStderrBytes: 6 }),
).resolves.toMatchObject({
outcome: "completed",
exitCode: 0,
status: 200,
stderr: "123456",
stderrTruncated: true,
stdout: '{"ok":true}',
});
});
@@ -94,7 +107,8 @@ describe("executeCliCommand", () => {
executeCliCommand(largeContext, "ignore-term", 0.25, {
apiBaseUrl: "http://127.0.0.1:8000",
cliPath,
maxOutputBytes: 64,
maxStderrBytes: 8,
maxStdoutBytes: 64,
terminationGraceMs: 20,
}),
).resolves.toMatchObject({
@@ -114,7 +128,8 @@ describe("executeCliCommand", () => {
executeCliCommand(largeContext, "closed-stdin", 1, {
apiBaseUrl: "http://127.0.0.1:8000",
cliPath,
maxOutputBytes: 64,
maxStderrBytes: 8,
maxStdoutBytes: 64,
terminationGraceMs: 20,
}),
).rejects.toBeInstanceOf(Error);