fix(agents): handle resumed cli jsonl output

Signed-off-by: samzong <samzong.lu@gmail.com>
This commit is contained in:
samzong
2026-05-09 11:06:00 +08:00
committed by Peter Steinberger
parent 02ca572a26
commit ee6b29b715
4 changed files with 57 additions and 18 deletions

View File

@@ -357,6 +357,7 @@ Docs: https://docs.openclaw.ai
- Agents/failover: rotate auth profiles before deferred cooldown marking on rate-limit failures, so file-lock contention cannot stall profile failover. Fixes #57281. (#57283) Thanks @jeremyknows.
- Gateway/sessions: when `session.dmScope: "main"` is configured, route a bare webchat `/new` against the agent's main session (`sessions.create` with `emitCommandHooks=true`) to an in-place reset instead of creating a parallel `dashboard:` child, matching `/new` behavior on Telegram/Discord. Fixes #77434. (#71170) Thanks @statxc.
- Scripts/UI/Windows: launch `.cmd` and `.bat` UI runners through the shared cmd.exe escaping path with shell mode disabled, avoiding Node.js v24 DEP0190 warnings while preserving argument boundaries. (#62910) Thanks @nandanadileep.
- Agents/CLI runner: disable supervisor stdout/stderr capture for prepared CLI runs while keeping bounded diagnostics and incremental JSONL output parsing, preventing long CLI output from being retained in memory. (#79617) Thanks @samzong.
- Telegram: treat a DM binding that carries the chat id in both `conversationId` and `parentConversationId` as a direct conversation instead of a topic, so reverse delivery for Telegram DMs is not misrouted through a topic-shaped target. (#79700) Thanks @TSHOGX.
## 2026.5.7

View File

@@ -768,27 +768,19 @@ describe("runCliAgent spawn path", () => {
event: { type: "content_block_delta", delta: { type: "text_delta", text: " world" } },
}) + "\n",
);
input.onStdout?.(
JSON.stringify({
type: "result",
session_id: "session-123",
result: "Hello world",
}) + "\n",
);
return createManagedRun({
reason: "exit",
exitCode: 0,
exitSignal: null,
durationMs: 50,
stdout: [
JSON.stringify({ type: "init", session_id: "session-123" }),
JSON.stringify({
type: "stream_event",
event: { type: "content_block_delta", delta: { type: "text_delta", text: "Hello" } },
}),
JSON.stringify({
type: "stream_event",
event: { type: "content_block_delta", delta: { type: "text_delta", text: " world" } },
}),
JSON.stringify({
type: "result",
session_id: "session-123",
result: "Hello world",
}),
].join("\n"),
stdout: "",
stderr: "",
timedOut: false,
noOutputTimedOut: false,

View File

@@ -150,6 +150,52 @@ describe("executePreparedCliRun supervisor output capture", () => {
expect(result.sessionId).toBe("session-jsonl-large");
});
it("parses oversized resume JSONL output from the effective resume output mode", async () => {
const largeToolEvent = `${JSON.stringify({
type: "stream_event",
event: {
type: "content_block_delta",
delta: { type: "tool_delta", text: "x".repeat(2 * 1024 * 1024) },
},
})}\n`;
const resultEvent = `${JSON.stringify({
type: "result",
session_id: "resume-jsonl-session",
result: "resumed answer",
})}\n`;
const context = buildPreparedCliRunContext({
output: "text",
provider: "resume-jsonl-cli",
});
Object.assign(context.preparedBackend.backend, {
jsonlDialect: "claude-stream-json" as const,
resumeArgs: ["resume", "{sessionId}"],
resumeOutput: "jsonl" as const,
sessionMode: "existing" as const,
});
supervisorSpawnMock.mockImplementationOnce(async (...args: unknown[]) => {
const input = args[0] as SupervisorSpawnInput;
input.onStdout?.(largeToolEvent);
input.onStdout?.(resultEvent);
return createManagedRun({
reason: "exit",
exitCode: 0,
exitSignal: null,
durationMs: 50,
stdout: input.captureOutput === false ? "" : `${largeToolEvent}${resultEvent}`,
stderr: "",
timedOut: false,
noOutputTimedOut: false,
});
});
const result = await executePreparedCliRun(context, "resume-jsonl-session");
expect(result.text).toBe("resumed answer");
expect(result.sessionId).toBe("resume-jsonl-session");
});
it("classifies failed stdout from the retained parse buffer before the diagnostic tail", async () => {
const errorPrefix = `${JSON.stringify({
type: "result",

View File

@@ -442,7 +442,8 @@ export async function executePreparedCliRun(
useResume,
trigger: params.trigger,
});
const hasJsonlOutput = backend.output === "jsonl";
const outputMode = useResume ? (backend.resumeOutput ?? backend.output) : backend.output;
const hasJsonlOutput = outputMode === "jsonl";
if (shouldUseClaudeLiveSession(context)) {
if (!hasJsonlOutput) {
throw new Error("Claude live session requires JSONL streaming parser");
@@ -688,7 +689,6 @@ export async function executePreparedCliRun(
});
}
const outputMode = useResume ? (backend.resumeOutput ?? backend.output) : backend.output;
const streamedJsonlOutput =
outputMode === "jsonl" ? (streamingParser?.getOutput() ?? null) : null;