fix: restore codex verbose full output

This commit is contained in:
Peter Steinberger
2026-04-29 20:35:09 +01:00
parent 8a3507e310
commit fdba408bce
5 changed files with 92 additions and 4 deletions

View File

@@ -27,6 +27,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Web fetch: add a documented `tools.web.fetch.ssrfPolicy.allowIpv6UniqueLocalRange` opt-in and thread it through cache keys and DNS/IP checks so trusted fake-IP proxy stacks using `fc00::/7` can work without broad private-network access. Fixes #74351. Thanks @jeffrey701.
- OpenAI Codex: restore `/verbose full` persistence and app-server tool-output forwarding, and retry Gateway E2E temp-home cleanup so debug runs do not regress on stale validation or cleanup flakes. Thanks @vincentkoc.
- Anthropic/Meridian: preserve text and thinking content seeded on `content_block_start` in anthropic-messages streams, so `[thinking, text]` replies no longer persist as empty turns or trigger empty-response fallbacks. Fixes #74410. Thanks @vyctorbrzezowski.
- Channels/Matrix: complete the cross-signing handshake on `openclaw matrix verify confirm-sas` so the operator's other Matrix device clears its `Verifying…` loop instead of staying stuck after the agent confirms. (#74542) Thanks @nklock.
- CLI/status: honor channel-specific model context-window overrides when reporting effective context, so channel-scoped sessions reflect the active window in `openclaw status`. Thanks @HemantSudarshan.

View File

@@ -636,6 +636,65 @@ describe("runCodexAppServerAttempt", () => {
);
});
it("forwards Codex app-server verbose tool summaries and completed output", async () => {
const onToolResult = vi.fn();
const sessionFile = path.join(tempDir, "session.jsonl");
const workspaceDir = path.join(tempDir, "workspace");
const harness = createStartedThreadHarness();
const params = createParams(sessionFile, workspaceDir);
params.verboseLevel = "full";
params.onToolResult = onToolResult;
const run = runCodexAppServerAttempt(params);
await harness.waitForMethod("turn/start");
await harness.notify({
method: "item/started",
params: {
threadId: "thread-1",
turnId: "turn-1",
item: {
type: "dynamicToolCall",
id: "tool-1",
namespace: null,
tool: "read",
arguments: { path: "README.md" },
status: "inProgress",
contentItems: null,
success: null,
durationMs: null,
},
},
});
await harness.notify({
method: "item/completed",
params: {
threadId: "thread-1",
turnId: "turn-1",
item: {
type: "dynamicToolCall",
id: "tool-1",
namespace: null,
tool: "read",
arguments: { path: "README.md" },
status: "completed",
contentItems: [{ type: "inputText", text: "file contents" }],
success: true,
durationMs: 12,
},
},
});
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
await run;
expect(onToolResult).toHaveBeenCalledTimes(2);
expect(onToolResult).toHaveBeenNthCalledWith(1, {
text: "📖 Read: `from README.md`",
});
expect(onToolResult).toHaveBeenNthCalledWith(2, {
text: "📖 Read: `from README.md`\n```txt\nfile contents\n```",
});
});
it("registers native hook relay config for an enabled Codex turn and cleans it up", async () => {
const sessionFile = path.join(tempDir, "session.jsonl");
const workspaceDir = path.join(tempDir, "workspace");

View File

@@ -298,7 +298,12 @@ module.exports = {
expect(afterCount).toBe(beforeCount);
} finally {
await server.close({ reason: "http tools workspace test complete" });
await fs.rm(tempHome, { recursive: true, force: true });
await fs.rm(tempHome, {
recursive: true,
force: true,
maxRetries: 10,
retryDelay: 50,
});
envSnapshot.restore();
}
},
@@ -505,7 +510,12 @@ module.exports = {
expect(parsed.plugins?.entries?.discord).toBeUndefined();
} finally {
await server.close({ reason: "minimal gateway auto-enable verify" });
await fs.rm(tempHome, { recursive: true, force: true });
await fs.rm(tempHome, {
recursive: true,
force: true,
maxRetries: 10,
retryDelay: 50,
});
envSnapshot.restore();
}
},

View File

@@ -186,6 +186,22 @@ describe("gateway sessions patch", () => {
expect(entry.fastMode).toBeUndefined();
});
test("persists verboseLevel=full", async () => {
const entry = expectPatchOk(
await runPatch({
patch: { key: MAIN_SESSION_KEY, verboseLevel: "full" },
}),
);
expect(entry.verboseLevel).toBe("full");
});
test("rejects invalid verboseLevel values with all valid choices in the error", async () => {
const result = await runPatch({
patch: { key: MAIN_SESSION_KEY, verboseLevel: "maybe" },
});
expectPatchError(result, 'invalid verboseLevel (use "on"|"off"|"full")');
});
test("persists elevatedLevel=off (does not clear)", async () => {
const entry = expectPatchOk(
await runPatch({

View File

@@ -6,6 +6,8 @@ import {
} from "../auto-reply/thinking.js";
import type { SessionEntry } from "../config/sessions.js";
const INVALID_VERBOSE_LEVEL_ERROR = 'invalid verboseLevel (use "on"|"off"|"full")';
export function parseVerboseOverride(
raw: unknown,
): { ok: true; value: VerboseLevel | null | undefined } | { ok: false; error: string } {
@@ -16,11 +18,11 @@ export function parseVerboseOverride(
return { ok: true, value: undefined };
}
if (typeof raw !== "string") {
return { ok: false, error: 'invalid verboseLevel (use "on"|"off")' };
return { ok: false, error: INVALID_VERBOSE_LEVEL_ERROR };
}
const normalized = normalizeVerboseLevel(raw);
if (!normalized) {
return { ok: false, error: 'invalid verboseLevel (use "on"|"off")' };
return { ok: false, error: INVALID_VERBOSE_LEVEL_ERROR };
}
return { ok: true, value: normalized };
}