From fdba408bce35e9008573bd8046957e8af7db2e87 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 29 Apr 2026 20:35:09 +0100 Subject: [PATCH] fix: restore codex verbose full output --- CHANGELOG.md | 1 + .../codex/src/app-server/run-attempt.test.ts | 59 +++++++++++++++++++ src/gateway/gateway.test.ts | 14 ++++- src/gateway/sessions-patch.test.ts | 16 +++++ src/sessions/level-overrides.ts | 6 +- 5 files changed, 92 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d66bae11c..be67829f8cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/extensions/codex/src/app-server/run-attempt.test.ts b/extensions/codex/src/app-server/run-attempt.test.ts index ee6b880a8f4..3f99e5f7931 100644 --- a/extensions/codex/src/app-server/run-attempt.test.ts +++ b/extensions/codex/src/app-server/run-attempt.test.ts @@ -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"); diff --git a/src/gateway/gateway.test.ts b/src/gateway/gateway.test.ts index 6040dd8d3b4..87a506e873d 100644 --- a/src/gateway/gateway.test.ts +++ b/src/gateway/gateway.test.ts @@ -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(); } }, diff --git a/src/gateway/sessions-patch.test.ts b/src/gateway/sessions-patch.test.ts index 498b813f41a..53ab366be5e 100644 --- a/src/gateway/sessions-patch.test.ts +++ b/src/gateway/sessions-patch.test.ts @@ -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({ diff --git a/src/sessions/level-overrides.ts b/src/sessions/level-overrides.ts index e3cd3346696..fb05f172879 100644 --- a/src/sessions/level-overrides.ts +++ b/src/sessions/level-overrides.ts @@ -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 }; }