From 448b1e44b0f24db4488ec8d39c33bc9bb151259c Mon Sep 17 00:00:00 2001 From: Yusheng Zou Date: Sun, 4 Oct 2026 01:20:06 +0800 Subject: [PATCH] fix(server): accept fractional PASEO_CLAUDE_REPLAY_BUDGET_MB values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parseInt read 0.5 as 0, so a fractional budget silently fell back to the 256 MB default — the opposite of what a small-host operator asked for. Parse the value as a float and pin the behavior with a regression test. --- .../claude/agent.sub-agent-replay.test.ts | 22 +++++++++++++++++++ .../server/agent/providers/claude/agent.ts | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/server/src/server/agent/providers/claude/agent.sub-agent-replay.test.ts b/packages/server/src/server/agent/providers/claude/agent.sub-agent-replay.test.ts index 65aefd294b..1516358ce4 100644 --- a/packages/server/src/server/agent/providers/claude/agent.sub-agent-replay.test.ts +++ b/packages/server/src/server/agent/providers/claude/agent.sub-agent-replay.test.ts @@ -489,6 +489,28 @@ describe("ClaudeAgentSession persisted subagent replay", () => { expect(descriptors.map((descriptor) => descriptor.id)).toContain(TOOL_USE_ID); }); + test("honors a fractional replay budget instead of rounding it up to the default", async () => { + // 0.5 MB is 524,288 bytes; parseInt would read it as 0 and silently fall back to 256 MB. + vi.stubEnv("PASEO_CLAUDE_REPLAY_BUDGET_MB", "0.5"); + const bulkEntry = JSON.stringify({ + type: "user", + isSidechain: true, + agentId: AGENT_ID, + sessionId: "replay-session", + timestamp: "2026-07-26T06:27:55.000Z", + message: { role: "user", content: "x".repeat(600 * 1024) }, + }); + writeSession({ + parentLines: [taskToolUse(), taskToolResult()], + meta: JSON.stringify({ toolUseId: TOOL_USE_ID }), + sidechainLines: [sidechainEntry(), bulkEntry], + }); + + const events = await replayEvents(); + + expect(events.filter((event) => event.type === "provider_subagent")).toEqual([]); + }); + test("replays the parent unchanged, matching the same session without subagent transcripts", async () => { // The #5820 reporter's own technique: moving the subagent transcripts away leaves the parent // loading fine. The over-budget skip has to be indistinguishable from that state. diff --git a/packages/server/src/server/agent/providers/claude/agent.ts b/packages/server/src/server/agent/providers/claude/agent.ts index 892c7acae7..1e998f98ea 100644 --- a/packages/server/src/server/agent/providers/claude/agent.ts +++ b/packages/server/src/server/agent/providers/claude/agent.ts @@ -5932,7 +5932,7 @@ const CLAUDE_SUBAGENT_META_FILE = /^agent-(.+)\.meta\.json$/; const DEFAULT_CLAUDE_REPLAY_BUDGET_BYTES = 256 * 1024 * 1024; function claudeReplayBudgetBytes(): number { - const megabytes = Number.parseInt(process.env.PASEO_CLAUDE_REPLAY_BUDGET_MB ?? "", 10); + const megabytes = Number.parseFloat(process.env.PASEO_CLAUDE_REPLAY_BUDGET_MB ?? ""); return Number.isFinite(megabytes) && megabytes > 0 ? megabytes * 1024 * 1024 : DEFAULT_CLAUDE_REPLAY_BUDGET_BYTES;