fix(gateway): unstick claude cli live e2e

This commit is contained in:
Peter Steinberger
2026-04-07 09:46:52 +01:00
parent 54a884865e
commit e331694df6
4 changed files with 90 additions and 3 deletions

View File

@@ -316,8 +316,6 @@ export function applyCliBackendLiveEnv(preservedEnv: ReadonlySet<string>): void
process.env.OPENCLAW_SKIP_CRON = "1";
process.env.OPENCLAW_SKIP_CANVAS_HOST = "1";
process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER = "1";
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR =
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR ?? "openclaw-live-test-no-bundled-extensions";
process.env.OPENCLAW_TEST_MINIMAL_GATEWAY = "1";
if (!preservedEnv.has("ANTHROPIC_API_KEY")) {
delete process.env.ANTHROPIC_API_KEY;

View File

@@ -148,6 +148,7 @@ describeLive("gateway live (cli backend)", () => {
const nextCfg = {
...cfg,
gateway: {
mode: "local",
...cfg.gateway,
port,
auth: { mode: "token", token },

View File

@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";
import { extractPayloadText } from "./test-helpers.agent-results.js";
describe("extractPayloadText", () => {
it("returns plain payload text unchanged", () => {
expect(
extractPayloadText({
payloads: [{ text: "hello world" }],
}),
).toBe("hello world");
});
it("extracts final text from Claude CLI stream-json payloads", () => {
const streamJson = [
JSON.stringify({
type: "system",
subtype: "init",
}),
JSON.stringify({
type: "assistant",
message: {
content: [{ type: "text", text: "CLI backend OK ABC123." }],
},
}),
JSON.stringify({
type: "result",
result: "CLI backend OK ABC123.",
}),
].join("\n");
expect(
extractPayloadText({
payloads: [{ text: streamJson }],
}),
).toBe("CLI backend OK ABC123.");
});
});

View File

@@ -4,13 +4,64 @@ type AgentDeltaEvent = {
data: { delta: string };
};
function extractCliStreamJsonText(text: string): string | null {
const lines = text
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.length > 0);
if (lines.length === 0) {
return null;
}
let assistantText: string | null = null;
let resultText: string | null = null;
for (const line of lines) {
let parsed: unknown;
try {
parsed = JSON.parse(line);
} catch {
continue;
}
if (!parsed || typeof parsed !== "object") {
continue;
}
const record = parsed as Record<string, unknown>;
if (record.type === "assistant") {
const message =
record.message && typeof record.message === "object"
? (record.message as Record<string, unknown>)
: null;
const content = Array.isArray(message?.content) ? message.content : [];
const textParts = content
.map((entry) =>
entry && typeof entry === "object" ? (entry as Record<string, unknown>).text : undefined,
)
.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0);
if (textParts.length > 0) {
assistantText = textParts.join("\n").trim();
}
continue;
}
if (record.type === "result" && typeof record.result === "string" && record.result.trim()) {
resultText = record.result.trim();
}
}
return resultText ?? assistantText;
}
export function extractPayloadText(result: unknown): string {
const record = result as Record<string, unknown>;
const payloads = Array.isArray(record.payloads) ? record.payloads : [];
const texts = payloads
.map((p) => (p && typeof p === "object" ? (p as Record<string, unknown>).text : undefined))
.filter((t): t is string => typeof t === "string" && t.trim().length > 0);
return texts.join("\n").trim();
const joined = texts.join("\n").trim();
if (!joined) {
return joined;
}
return extractCliStreamJsonText(joined) ?? joined;
}
export function buildAssistantDeltaResult(params: {