fix: support Claude stream-json verbose mode

This commit is contained in:
liujing
2026-07-17 14:20:59 +08:00
parent b756a10c35
commit 90551414b1
2 changed files with 53 additions and 13 deletions
+51 -13
View File
@@ -154,7 +154,7 @@ describe("ClaudeAdapter", () => {
beforeEach(() => { tmpDir = makeTmpDir(); });
afterEach(() => { cleanupDir(tmpDir); });
it("start includes --session-id and -p flags", async () => {
it("start includes --session-id, -p, --verbose, and --output-format flags", async () => {
const argsFile = path.join(tmpDir, "args.txt");
const fakeBin = writeFakeCli(tmpDir, {
argsFile,
@@ -170,10 +170,17 @@ describe("ClaudeAdapter", () => {
config: { binary: fakeBin },
});
const capturedArgs = fs.existsSync(argsFile) ? fs.readFileSync(argsFile, "utf8") : "";
const capturedArgs = fs.existsSync(argsFile)
? fs.readFileSync(argsFile, "utf8").split("\0").filter(Boolean)
: [];
expect(capturedArgs).toContain("-p");
expect(capturedArgs).toContain("--output-format");
expect(capturedArgs).toContain("--session-id");
expect(capturedArgs).toContain("--verbose");
const outputFormatIdx = capturedArgs.indexOf("--output-format");
expect(outputFormatIdx).toBeGreaterThanOrEqual(0);
expect(capturedArgs[outputFormatIdx + 1]).toBe("stream-json");
const sessionIdIdx = capturedArgs.indexOf("--session-id");
expect(sessionIdIdx).toBeGreaterThanOrEqual(0);
expect(capturedArgs[sessionIdIdx + 1]).toMatch(/^[0-9a-f-]{36}$/i);
expect(result.sessionHandle?.kind).toBe("claude");
});
@@ -192,9 +199,17 @@ describe("ClaudeAdapter", () => {
config: { binary: fakeBin },
});
const capturedArgs = fs.existsSync(argsFile) ? fs.readFileSync(argsFile, "utf8") : "";
expect(capturedArgs).toContain("--resume");
expect(capturedArgs).toContain("captured-session-id");
const capturedArgs = fs.existsSync(argsFile)
? fs.readFileSync(argsFile, "utf8").split("\0").filter(Boolean)
: [];
expect(capturedArgs).toContain("-p");
expect(capturedArgs).toContain("--verbose");
const outputFormatIdx = capturedArgs.indexOf("--output-format");
expect(outputFormatIdx).toBeGreaterThanOrEqual(0);
expect(capturedArgs[outputFormatIdx + 1]).toBe("stream-json");
const resumeIdx = capturedArgs.indexOf("--resume");
expect(resumeIdx).toBeGreaterThanOrEqual(0);
expect(capturedArgs[resumeIdx + 1]).toBe("captured-session-id");
expect(result.sessionHandle?.kind).toBe("claude");
});
@@ -338,10 +353,15 @@ printf '%s\\0' "$@" >> "${argsFile}"
config: { binary: trackingBin },
});
// 1) --output-format stream-json was passed
const capturedArgs = fs.existsSync(argsFile) ? fs.readFileSync(argsFile, "utf8") : "";
expect(capturedArgs).toContain("--output-format");
expect(capturedArgs).toContain("stream-json");
// 1) --output-format stream-json, -p, --verbose were passed
const capturedArgs = fs.existsSync(argsFile)
? fs.readFileSync(argsFile, "utf8").split("\0").filter(Boolean)
: [];
expect(capturedArgs).toContain("-p");
expect(capturedArgs).toContain("--verbose");
const outputFormatIdx = capturedArgs.indexOf("--output-format");
expect(outputFormatIdx).toBeGreaterThanOrEqual(0);
expect(capturedArgs[outputFormatIdx + 1]).toBe("stream-json");
// 2) Exact session preservation
if (result.sessionHandle?.kind === "claude") {
@@ -384,11 +404,17 @@ printf '%s\\0' "$@" >> "${argsFile}"
expect(sidecar!.phase).toBe("failed");
});
it("resume: preserves session ID from the handle", async () => {
it("resume: preserves session ID from the handle and passes expected args", async () => {
const argsFile = path.join(tmpDir, "args.txt");
const fakeBin = makeFakeClaude([
'{"type":"system","subtype":"init","session_id":"resumed-session-999"}',
'{"type":"result","result":"completed"}',
]);
const trackingBin = path.join(tmpDir, "tracking-claude.sh");
fs.writeFileSync(trackingBin, `#!/bin/bash
printf '%s\\0' "$@" >> "${argsFile}"
"${fakeBin}" "$@"
`, { mode: 0o755 });
const adapter = createExecutorAdapter("claude");
const logFilePath = path.join(tmpDir, "claude-resume.log");
@@ -400,9 +426,21 @@ printf '%s\\0' "$@" >> "${argsFile}"
runDir: tmpDir,
cycleIndex: 2,
logFilePath,
config: { binary: fakeBin },
config: { binary: trackingBin },
});
const capturedArgs = fs.existsSync(argsFile)
? fs.readFileSync(argsFile, "utf8").split("\0").filter(Boolean)
: [];
expect(capturedArgs).toContain("-p");
expect(capturedArgs).toContain("--verbose");
const outputFormatIdx = capturedArgs.indexOf("--output-format");
expect(outputFormatIdx).toBeGreaterThanOrEqual(0);
expect(capturedArgs[outputFormatIdx + 1]).toBe("stream-json");
const resumeIdx = capturedArgs.indexOf("--resume");
expect(resumeIdx).toBeGreaterThanOrEqual(0);
expect(capturedArgs[resumeIdx + 1]).toBe("resumed-session-999");
if (result.sessionHandle?.kind === "claude") {
expect(result.sessionHandle.sessionId).toBe("resumed-session-999");
}