diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b49d63a921..bcd38712d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,7 @@ Docs: https://docs.openclaw.ai - Agents/replay: re-run tool/result pairing after strict replay tool-call ID sanitization on outbound requests so Anthropic-compatible providers like MiniMax no longer receive malformed orphan tool-result IDs such as `...toolresult1` during compaction and retry flows. (#67620) Thanks @stainlu. - Gateway/startup: fix spurious SIGUSR1 restart loop on Linux/systemd when plugin auto-enable is the only startup config write; the config hash guard was not captured for that write path, causing chokidar to treat each boot write as an external change and trigger a reload → restart cycle that corrupts manifest.db after repeated cycles. Fixes #67436. (#67557) thanks @openperf - Codex/harness: auto-enable the Codex plugin when `codex` is selected as an embedded agent harness runtime, including forced default, per-agent, and `OPENCLAW_AGENT_RUNTIME` paths. (#67474) Thanks @duqaXxX. -- OpenAI Codex/CLI: keep resumed `codex exec resume` runs on the safe non-interactive path without reintroducing the removed dangerous bypass flag by passing the supported `--skip-git-repo-check` resume arg that real Codex CLI requires outside trusted git directories. (#67666) Thanks @plgonzalezrx8. +- OpenAI Codex/CLI: keep resumed `codex exec resume` runs on the safe non-interactive path without reintroducing the removed dangerous bypass flag by passing the supported `--skip-git-repo-check` resume arg plus Codex's native `sandbox_mode="workspace-write"` config override. (#67666) Thanks @plgonzalezrx8. - Codex/app-server: parse Desktop-originated app-server user agents such as `Codex Desktop/0.118.0`, keeping the version gate working when the Codex CLI inherits a multi-word originator. (#64666) Thanks @cyrusaf. - Cron/announce delivery: keep isolated announce `NO_REPLY` stripping case-insensitive across direct and text delivery, preserve structured media-only sends when a caption strips silent, and derive main-session awareness from the cleaned payloads so silent captions no longer leak stale `NO_REPLY` text. (#65016) Thanks @BKF-Gitty. - Sessions/Codex: skip redundant `delivery-mirror` transcript appends only when the latest assistant message has the same visible text, preventing duplicate visible replies on Codex-backed turns without suppressing repeated answers across turns. (#67185) Thanks @andyylin. diff --git a/docs/gateway/cli-backends.md b/docs/gateway/cli-backends.md index 1b18979435e..b0587467662 100644 --- a/docs/gateway/cli-backends.md +++ b/docs/gateway/cli-backends.md @@ -221,7 +221,7 @@ The bundled OpenAI plugin also registers a default for `codex-cli`: - `command: "codex"` - `args: ["exec","--json","--color","never","--sandbox","workspace-write","--skip-git-repo-check"]` -- `resumeArgs: ["exec","resume","{sessionId}","--skip-git-repo-check"]` +- `resumeArgs: ["exec","resume","{sessionId}","-c","sandbox_mode=\"workspace-write\"","--skip-git-repo-check"]` - `output: "jsonl"` - `resumeOutput: "text"` - `modelArg: "--model"` diff --git a/extensions/openai/cli-backend.ts b/extensions/openai/cli-backend.ts index 0afd392afee..7a5125240d6 100644 --- a/extensions/openai/cli-backend.ts +++ b/extensions/openai/cli-backend.ts @@ -31,7 +31,14 @@ export function buildOpenAICodexCliBackend(): CliBackendPlugin { "workspace-write", "--skip-git-repo-check", ], - resumeArgs: ["exec", "resume", "{sessionId}", "--skip-git-repo-check"], + resumeArgs: [ + "exec", + "resume", + "{sessionId}", + "-c", + 'sandbox_mode="workspace-write"', + "--skip-git-repo-check", + ], output: "jsonl", resumeOutput: "text", input: "arg", diff --git a/src/agents/cli-backends.test.ts b/src/agents/cli-backends.test.ts index 78b58c6d43e..895cde3344d 100644 --- a/src/agents/cli-backends.test.ts +++ b/src/agents/cli-backends.test.ts @@ -244,7 +244,14 @@ beforeEach(() => { "workspace-write", "--skip-git-repo-check", ], - resumeArgs: ["exec", "resume", "{sessionId}", "--skip-git-repo-check"], + resumeArgs: [ + "exec", + "resume", + "{sessionId}", + "-c", + 'sandbox_mode="workspace-write"', + "--skip-git-repo-check", + ], systemPromptFileConfigArg: "-c", systemPromptFileConfigKey: "model_instructions_file", systemPromptWhen: "first", @@ -309,7 +316,7 @@ beforeEach(() => { }); describe("resolveCliBackendConfig reliability merge", () => { - it("defaults codex-cli fresh sandboxing and resume trust bypass for non-git runs", () => { + it("defaults codex-cli fresh sandboxing and config-pinned resume sandboxing", () => { const resolved = resolveCliBackendConfig("codex-cli"); expect(resolved).not.toBeNull(); @@ -326,6 +333,8 @@ describe("resolveCliBackendConfig reliability merge", () => { "exec", "resume", "{sessionId}", + "-c", + 'sandbox_mode="workspace-write"', "--skip-git-repo-check", ]); }); diff --git a/src/agents/cli-runner.test-support.ts b/src/agents/cli-runner.test-support.ts index ab851c41634..301422f0019 100644 --- a/src/agents/cli-runner.test-support.ts +++ b/src/agents/cli-runner.test-support.ts @@ -120,7 +120,14 @@ function buildOpenAICodexCliBackendFixture(): CliBackendPlugin { "workspace-write", "--skip-git-repo-check", ], - resumeArgs: ["exec", "resume", "{sessionId}", "--skip-git-repo-check"], + resumeArgs: [ + "exec", + "resume", + "{sessionId}", + "-c", + 'sandbox_mode="workspace-write"', + "--skip-git-repo-check", + ], output: "jsonl", resumeOutput: "text", input: "arg", diff --git a/src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test-support.ts b/src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test-support.ts index 04c052af76b..3ca39804034 100644 --- a/src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test-support.ts +++ b/src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test-support.ts @@ -33,6 +33,12 @@ type BootstrapContext = { bootstrapFiles: WorkspaceBootstrapFile[]; contextFiles: EmbeddedContextFile[]; }; + +function normalizeMockProviderId(providerId?: string): string { + const normalized = normalizeLowercaseStringOrEmpty(providerId); + return normalized === "z.ai" || normalized === "z-ai" ? "zai" : normalized; +} + type SessionManagerMocks = { getLeafEntry: UnknownMock; branch: UnknownMock; @@ -411,7 +417,19 @@ vi.mock("../../../image-generation/runtime.js", () => ({ })); vi.mock("../../model-selection.js", () => ({ - normalizeProviderId: (providerId?: string) => normalizeLowercaseStringOrEmpty(providerId), + findNormalizedProviderValue: (entries: Record | undefined, provider: string) => { + if (!entries) { + return undefined; + } + const providerKey = normalizeMockProviderId(provider); + for (const [key, value] of Object.entries(entries)) { + if (normalizeMockProviderId(key) === providerKey) { + return value; + } + } + return undefined; + }, + normalizeProviderId: normalizeMockProviderId, resolveDefaultModelForAgent: () => ({ provider: "openai", model: "gpt-test" }), }));