Skip to content

Commit 1b6393c

Browse files
fix (Computer Use) Stabilize Codex Computer Use readiness (#103331)
* fix (Computer Use) Stabilize Codex Computer Use readiness Co-authored-by: Ben Badejo <[email protected]> * fix (Computer Use) Stabilize Codex Computer Use readiness * fix (Computer Use) Stabilize Codex Computer Use readiness * fix (Computer Use) Stabilize Codex Computer Use readiness * fix(clawsweeper): address review for automerge-openclaw-openclaw-103331 (1) * fix (Computer Use) Stabilize Codex Computer Use readiness --------- Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: Ben Badejo <[email protected]>
1 parent 72aa3f1 commit 1b6393c

23 files changed

Lines changed: 2432 additions & 152 deletions

docs/docs_map.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5404,6 +5404,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
54045404
- H2: Commands
54055405
- H2: Marketplace choices
54065406
- H2: Bundled macOS marketplace
5407+
- H3: Shared plugin cache
54075408
- H2: Remote catalog limit
54085409
- H2: Configuration reference
54095410
- H2: What OpenClaw checks

docs/plugins/codex-computer-use.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,17 @@ If you use a nonstandard Codex app path, run `/codex computer-use install
208208
local marketplace file path. Use `--marketplace-path` only when you have the
209209
marketplace JSON file path, not the bundled marketplace root.
210210

211+
### Shared plugin cache
212+
213+
The default `pluginCacheMode: "independent"` leaves each Codex home and its
214+
plugin cache unmanaged. Set `pluginCacheMode: "shared"` to copy the bundled
215+
Computer Use plugin into the active Codex home's discoverable plugin cache
216+
before app-server startup. Shared mode preserves older cached versions because
217+
running Codex clients can still reference their versioned plugin directories; a
218+
failed replacement copy also preserves the active cache. Explicit
219+
`marketplaceName` or `marketplacePath` configuration disables this
220+
reconciliation so OpenClaw does not override that selection.
221+
211222
## Remote catalog limit
212223

213224
Codex app-server can list and read remote-only catalog entries, but it does
@@ -231,6 +242,13 @@ remote install is unsupported, run install with a local source or path:
231242
| `enabled` | inferred | Require Computer Use. Defaults to true when another Computer Use field is set. |
232243
| `autoInstall` | false | Install or re-enable from already discovered marketplaces at turn start. |
233244
| `marketplaceDiscoveryTimeoutMs` | 60000 | How long install waits for Codex app-server marketplace discovery. |
245+
| `liveTestTimeoutMs` | 60000 | Timeout for the temporary readiness thread and its cleanup requests. |
246+
| `toolCallTimeoutMs` | 60000 | Timeout for the Computer Use `list_apps` readiness tool call. |
247+
| `healthCheckEnabled` | false | Run periodic readiness probes while the owning app-server client is active. |
248+
| `healthCheckIntervalMinutes` | 60 | Probe cadence; accepted values are 30, 60, 120, or 240 minutes. |
249+
| `pluginCacheMode` | `independent` | Use `shared` to refresh the Codex-home cache from the bundled desktop plugin. |
250+
| `strictReadiness` | false | Stop startup on a failed live probe instead of continuing with a warning. |
251+
| `autoRepair` | false | Kill stale scoped Computer Use MCP children and retry a failed probe once. |
234252
| `marketplaceSource` | unset | Source string passed to Codex app-server `marketplace/add`. |
235253
| `marketplacePath` | unset | Local Codex marketplace file path containing the plugin. |
236254
| `marketplaceName` | unset | Registered Codex marketplace name to select. |
@@ -252,6 +270,13 @@ matching config key is unset:
252270
| `enabled` | `OPENCLAW_CODEX_COMPUTER_USE` |
253271
| `autoInstall` | `OPENCLAW_CODEX_COMPUTER_USE_AUTO_INSTALL` |
254272
| `marketplaceDiscoveryTimeoutMs` | `OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS` |
273+
| `liveTestTimeoutMs` | `OPENCLAW_CODEX_COMPUTER_USE_LIVE_TEST_TIMEOUT_MS` |
274+
| `toolCallTimeoutMs` | `OPENCLAW_CODEX_COMPUTER_USE_TOOL_CALL_TIMEOUT_MS` |
275+
| `healthCheckEnabled` | `OPENCLAW_CODEX_COMPUTER_USE_HEALTH_CHECK_ENABLED` |
276+
| `healthCheckIntervalMinutes` | `OPENCLAW_CODEX_COMPUTER_USE_HEALTH_CHECK_INTERVAL_MINUTES` |
277+
| `pluginCacheMode` | `OPENCLAW_CODEX_COMPUTER_USE_PLUGIN_CACHE_MODE` |
278+
| `strictReadiness` | `OPENCLAW_CODEX_COMPUTER_USE_STRICT_READINESS` |
279+
| `autoRepair` | `OPENCLAW_CODEX_COMPUTER_USE_AUTO_REPAIR` |
255280
| `marketplaceSource` | `OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_SOURCE` |
256281
| `marketplacePath` | `OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_PATH` |
257282
| `marketplaceName` | `OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_NAME` |

extensions/codex/openclaw.plugin.json

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,38 @@
8383
"minimum": 1,
8484
"default": 60000
8585
},
86+
"liveTestTimeoutMs": {
87+
"type": "number",
88+
"minimum": 1,
89+
"default": 60000
90+
},
91+
"toolCallTimeoutMs": {
92+
"type": "number",
93+
"minimum": 1,
94+
"default": 60000
95+
},
96+
"healthCheckEnabled": {
97+
"type": "boolean",
98+
"default": false
99+
},
100+
"healthCheckIntervalMinutes": {
101+
"type": "number",
102+
"enum": [30, 60, 120, 240],
103+
"default": 60
104+
},
105+
"pluginCacheMode": {
106+
"type": "string",
107+
"enum": ["shared", "independent"],
108+
"default": "independent"
109+
},
110+
"strictReadiness": {
111+
"type": "boolean",
112+
"default": false
113+
},
114+
"autoRepair": {
115+
"type": "boolean",
116+
"default": false
117+
},
86118
"marketplaceSource": {
87119
"type": "string"
88120
},
@@ -365,6 +397,41 @@
365397
"help": "Maximum time to wait for Codex app-server to finish loading marketplaces during Computer Use install.",
366398
"advanced": true
367399
},
400+
"computerUse.liveTestTimeoutMs": {
401+
"label": "Live Test Timeout",
402+
"help": "Maximum time for the Computer Use list_apps readiness probe before status and startup treat the live test as failed.",
403+
"advanced": true
404+
},
405+
"computerUse.toolCallTimeoutMs": {
406+
"label": "Tool Call Timeout",
407+
"help": "Maximum expected time for real Computer Use tool calls such as list_apps before OpenClaw treats the child runtime as stale.",
408+
"advanced": true
409+
},
410+
"computerUse.healthCheckEnabled": {
411+
"label": "Periodic Health Checks",
412+
"help": "When true, run periodic Computer Use live probes on the configured cadence.",
413+
"advanced": true
414+
},
415+
"computerUse.healthCheckIntervalMinutes": {
416+
"label": "Health Check Interval",
417+
"help": "Cadence for periodic Computer Use health checks when health checks are enabled.",
418+
"advanced": true
419+
},
420+
"computerUse.pluginCacheMode": {
421+
"label": "Plugin Cache Mode",
422+
"help": "The default independent mode leaves each Codex home unmanaged. Choose shared to opt in to a refreshed Codex-discoverable cache copy.",
423+
"advanced": true
424+
},
425+
"computerUse.strictReadiness": {
426+
"label": "Strict Readiness",
427+
"help": "When true, a failed Computer Use live probe stops Codex-mode startup. The default false value preserves existing enabled setups by continuing with a warning.",
428+
"advanced": true
429+
},
430+
"computerUse.autoRepair": {
431+
"label": "Auto Repair",
432+
"help": "When true, failed Computer Use live tests repair stale scoped Computer Use MCP children before retrying once.",
433+
"advanced": true
434+
},
368435
"computerUse.marketplaceSource": {
369436
"label": "Marketplace Source",
370437
"help": "Optional Codex marketplace source to add before installing Computer Use.",

extensions/codex/src/app-server/attempt-startup.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import type {
1010
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
1111
import { startCodexAttemptThread } from "./attempt-startup.js";
1212
import { CodexAppServerClient } from "./client.js";
13-
import { type CodexPluginConfig, resolveCodexAppServerRuntimeOptions } from "./config.js";
13+
import {
14+
type CodexPluginConfig,
15+
resolveCodexAppServerRuntimeOptions,
16+
resolveCodexComputerUseConfig,
17+
} from "./config.js";
1418
import { testCodexAppServerBindingStore } from "./session-binding.test-helpers.js";
1519
import {
1620
clearSharedCodexAppServerClient,
@@ -107,7 +111,7 @@ function startThreadWithHarness(
107111
overrides?.attemptClientFactory?.(harness) ?? getLeasedSharedCodexAppServerClient,
108112
appServer: resolveCodexAppServerRuntimeOptions({ pluginConfig: effectivePluginConfig }),
109113
pluginConfig: effectivePluginConfig,
110-
computerUseConfig: effectivePluginConfig.computerUse ?? { enabled: false },
114+
computerUseConfig: resolveCodexComputerUseConfig({ pluginConfig: effectivePluginConfig }),
111115
startupAuthProfileId: undefined,
112116
startupAuthAccountCacheKey: undefined,
113117
startupEnvApiKeyCacheKey: undefined,

extensions/codex/src/app-server/attempt-startup.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ import { buildCodexPluginThreadConfigEligibilityLogData } from "./attempt-diagno
2121
import { withCodexStartupTimeout } from "./attempt-timeouts.js";
2222
import { ensureCodexAppServerClientRuntime } from "./client-runtime.js";
2323
import { isCodexAppServerConnectionClosedError, type CodexAppServerClient } from "./client.js";
24+
import { startCodexComputerUseHealthMonitor } from "./computer-use-health.js";
2425
import { ensureCodexComputerUse } from "./computer-use.js";
2526
import {
2627
resolveCodexPluginsPolicy,
2728
withMcpElicitationsApprovalPolicy,
2829
type CodexAppServerRuntimeOptions,
2930
type CodexPluginConfig,
30-
type CodexComputerUseConfig,
31+
type ResolvedCodexComputerUseConfig,
3132
} from "./config.js";
3233
import {
3334
disableCodexPluginThreadConfig,
@@ -104,7 +105,7 @@ export async function startCodexAttemptThread(params: {
104105
bindingStore: CodexAppServerBindingStore;
105106
appServer: CodexAppServerRuntimeOptions;
106107
pluginConfig: CodexPluginConfig;
107-
computerUseConfig: CodexComputerUseConfig;
108+
computerUseConfig: ResolvedCodexComputerUseConfig;
108109
startupAuthProfileId: string | null | undefined;
109110
startupAuthAccountCacheKey: string | undefined;
110111
startupEnvApiKeyCacheKey: string | undefined;
@@ -434,6 +435,10 @@ export async function startCodexAttemptThread(params: {
434435
throw new Error("codex app-server startup did not reserve its thread route");
435436
}
436437
startupSandboxEnvironmentAcquired = false;
438+
startCodexComputerUseHealthMonitor({
439+
client: activeStartupClient,
440+
config: params.computerUseConfig,
441+
});
437442
startupAttemptSucceeded = true;
438443
return {
439444
client: activeStartupClient,

extensions/codex/src/app-server/auth-bridge.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ import {
2121
} from "openclaw/plugin-sdk/agent-runtime";
2222
import { hasUsableOAuthCredential } from "openclaw/plugin-sdk/provider-auth";
2323
import type { CodexAppServerClient } from "./client.js";
24-
import { resolveCodexAppServerUserHomeDir, type CodexAppServerStartOptions } from "./config.js";
24+
import { ensureCodexComputerUseSharedPluginCache } from "./computer-use-cache.js";
25+
import {
26+
resolveCodexAppServerUserHomeDir,
27+
resolveCodexComputerUseConfig,
28+
type CodexAppServerStartOptions,
29+
} from "./config.js";
2530
import type {
2631
CodexChatgptAuthTokensRefreshResponse,
2732
CodexGetAccountResponse,
@@ -61,11 +66,16 @@ export async function bridgeCodexAppServerStartOptions(params: {
6166
authProfileId?: string | null;
6267
authProfileStore?: AuthProfileStore;
6368
config?: AuthProfileOrderConfig;
69+
pluginConfig?: unknown;
6470
}): Promise<CodexAppServerStartOptions> {
6571
if (params.startOptions.transport !== "stdio") {
6672
return params.startOptions;
6773
}
68-
const scopedStartOptions = await withCodexHomeEnvironment(params.startOptions, params.agentDir);
74+
const scopedStartOptions = await withCodexHomeEnvironment(
75+
params.startOptions,
76+
params.agentDir,
77+
params.pluginConfig,
78+
);
6979
if (params.authProfileId === null) {
7080
return scopedStartOptions;
7181
}
@@ -333,6 +343,7 @@ export function resolveCodexAppServerNativeHomeDir(agentDir: string): string {
333343
async function withCodexHomeEnvironment(
334344
startOptions: CodexAppServerStartOptions,
335345
agentDir: string,
346+
pluginConfig?: unknown,
336347
): Promise<CodexAppServerStartOptions> {
337348
const codexHome = startOptions.env?.[CODEX_HOME_ENV_VAR]?.trim()
338349
? startOptions.env[CODEX_HOME_ENV_VAR]
@@ -343,6 +354,10 @@ async function withCodexHomeEnvironment(
343354
? startOptions.env[HOME_ENV_VAR]
344355
: undefined;
345356
await fs.mkdir(codexHome, { recursive: true });
357+
await ensureCodexComputerUseSharedPluginCache({
358+
codexHome,
359+
config: resolveCodexComputerUseConfig({ pluginConfig }),
360+
});
346361
if (nativeHome) {
347362
await fs.mkdir(nativeHome, { recursive: true });
348363
}

extensions/codex/src/app-server/client.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ export class CodexAppServerClient {
229229
return this.runtimeIdentity ? { ...this.runtimeIdentity } : undefined;
230230
}
231231

232+
/** Returns the local transport PID for scoped child-process cleanup, when available. */
233+
getTransportPid(): number | undefined {
234+
return this.child.pid;
235+
}
236+
232237
request<M extends CodexAppServerRequestMethod>(
233238
method: M,
234239
params: CodexAppServerRequestParams<M>,

0 commit comments

Comments
 (0)