fix(codex): candidate checks reject a mismatched app-server (#124137)

* fix(codex): validate managed app-server candidates

* docs(codex): document managed binary preflight

* test(codex): review managed preflight execution

* docs(codex): document managed app-server check

* fix(pr): support macOS system Bash operation locks

* fix(codex): scope managed preflight to Codex agents
This commit is contained in:
Jason (Json)
2026-08-16 09:27:12 -06:00
committed by GitHub
parent b864da4d1f
commit eb174bcaff
10 changed files with 699 additions and 13 deletions
+15
View File
@@ -149,6 +149,21 @@ desktop-first rule applies when an isolated agent home's effective Codex config
enables native Computer Use. If no desktop app bundle is installed, OpenClaw
falls back to the pinned package binary.
Before cutting over a staged OpenClaw package, run the opt-in managed-binary
check against the candidate installation:
```bash
openclaw doctor --lint --only codex/managed-app-server --json
```
The check is read-only. For every configured Codex agent it applies the same
final command selection as a live harness turn, then verifies that a selected
package-owned native binary exists and reports the plugin's exact pinned
version. A selected Codex Desktop binary, an explicit custom command, and a
remote app-server are outside this package check. The command exits nonzero on
an error-level finding, so a deployer can reject the candidate before cutover
without changing Codex state or app-server settings.
Executable handoff and native-config fencing coordinate clients inside one
running Gateway process. Restart the Gateway after another process changes the
native Codex plugin config.
+12
View File
@@ -462,6 +462,18 @@ Then check Codex app-server state:
/codex binding
```
After installing or updating OpenClaw, explicitly verify the managed package
binary before cutover:
```bash
openclaw doctor --lint --only codex/managed-app-server --json
```
For an effective Codex route using the managed stdio app-server, this
default-disabled check resolves the platform-native executable and requires the
exact Codex version pinned by OpenClaw. It does not execute custom, remote, or
macOS desktop-owned app-servers.
`/status` reports the resolved OpenClaw Fast policy (`on`, `off`, or `auto`)
and the selected runtime. It does not report the upstream service tier actually
honored or returned for a completed request. `/codex binding` reports the
+17
View File
@@ -0,0 +1,17 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import type { HealthCheck } from "openclaw/plugin-sdk/health";
import {
CODEX_MANAGED_APP_SERVER_CHECK_ID,
registerCodexManagedAppServerDoctorChecks as registerChecks,
} from "./src/doctor.js";
const CODEX_PLUGIN_ROOT = path.dirname(fileURLToPath(import.meta.url));
export { CODEX_MANAGED_APP_SERVER_CHECK_ID };
export function registerCodexManagedAppServerDoctorChecks(host: {
registerHealthCheck(check: HealthCheck): void;
}): void {
registerChecks({ ...host, pluginRoot: CODEX_PLUGIN_ROOT });
}
@@ -76,10 +76,7 @@ export function resolveManagedCodexNativeCommand(
options: ResolveManagedCodexNativeCommandOptions = {},
): string | undefined {
const platform = options.platform ?? process.platform;
if (
platform === "darwin" &&
MACOS_DESKTOP_CODEX_APP_SERVER_COMMANDS.some((candidate) => candidate === command)
) {
if (isManagedCodexDesktopCommand(command, platform)) {
return command;
}
const target = resolveCodexNativeTarget(platform, options.arch ?? process.arch);
@@ -111,6 +108,17 @@ export function resolveManagedCodexNativeCommand(
return undefined;
}
/** Returns whether a resolved managed command is owned by the macOS desktop app. */
export function isManagedCodexDesktopCommand(
command: string,
platform: NodeJS.Platform = process.platform,
): boolean {
return (
platform === "darwin" &&
MACOS_DESKTOP_CODEX_APP_SERVER_COMMANDS.some((candidate) => candidate === command)
);
}
function resolveManagedCodexPackageRootForCommand(
command: string,
platform: NodeJS.Platform,
+319
View File
@@ -0,0 +1,319 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import type { HealthCheck, OpenClawConfig } from "openclaw/plugin-sdk/health";
import { describe, expect, it, vi } from "vitest";
import { CODEX_APP_SERVER_VERSION } from "./app-server/version.js";
import {
CODEX_MANAGED_APP_SERVER_CHECK_ID,
registerCodexManagedAppServerDoctorChecks,
} from "./doctor.js";
function config(appServer: Record<string, unknown> = {}): OpenClawConfig {
return {
agents: {
defaults: {
model: { primary: "openai/gpt-5.6-sol" },
models: {
"openai/gpt-5.6-sol": { agentRuntime: { id: "codex" } },
},
},
},
plugins: {
entries: {
codex: {
enabled: true,
config: {
appServer: {
args: [
"app-server",
"--listen",
"stdio://",
"-c",
"model_context_window=1000000",
"-c",
"model_auto_compact_token_limit=700000",
"-c",
"model_auto_compact_token_limit_scope=total",
],
...appServer,
},
},
},
},
},
};
}
function context(cfg: OpenClawConfig) {
return {
mode: "lint" as const,
runtime: {} as never,
cfg,
env: {} as NodeJS.ProcessEnv,
};
}
function managedDeps(version = CODEX_APP_SERVER_VERSION) {
const resolveNativeCommand = vi.fn(
(_command: string): string | undefined => "/candidate/plugin/codex-native",
);
return {
resolveStartOptions: vi.fn(async (start) => ({
...start,
command:
start.managedCommandOrder === "desktop-first"
? "/Applications/ChatGPT.app/Contents/Resources/codex"
: "/candidate/plugin/codex",
commandSource: "resolved-managed" as const,
})),
isDesktopCommand: vi.fn((command: string) => command.startsWith("/Applications/")),
resolveNativeCommand,
runVersionCommand: vi.fn(async () => ({ stdout: `codex-cli ${version}\n`, stderr: "" })),
};
}
function createCheck(deps: ReturnType<typeof managedDeps>) {
let check: HealthCheck | undefined;
registerCodexManagedAppServerDoctorChecks(
{
pluginRoot: "/candidate/plugin",
registerHealthCheck(value) {
check = value;
},
},
deps,
);
if (!check) {
throw new Error("Codex managed health check was not registered");
}
return check;
}
describe("managed Codex doctor check", () => {
it("accepts the exact pinned native binary without changing explicit long-context config", async () => {
const cfg = config();
const before = structuredClone(cfg);
const deps = managedDeps();
const check = createCheck(deps);
await expect(check.detect(context(cfg))).resolves.toEqual([]);
expect(deps.runVersionCommand).toHaveBeenCalledWith("/candidate/plugin/codex-native");
expect(cfg).toEqual(before);
});
it("reports the exact expected and detected versions", async () => {
const deps = managedDeps("0.146.0");
const check = createCheck(deps);
await expect(check.detect(context(config()))).resolves.toEqual([
expect.objectContaining({
checkId: CODEX_MANAGED_APP_SERVER_CHECK_ID,
severity: "error",
path: "/candidate/plugin/codex-native",
message: `Managed Codex app-server version mismatch: expected ${CODEX_APP_SERVER_VERSION}, detected 0.146.0.`,
}),
]);
});
it("reports a missing managed launcher before execution", async () => {
const deps = managedDeps();
deps.resolveStartOptions.mockRejectedValueOnce(new Error("managed launcher missing"));
const check = createCheck(deps);
await expect(check.detect(context(config()))).resolves.toEqual([
expect.objectContaining({
checkId: CODEX_MANAGED_APP_SERVER_CHECK_ID,
message: "Managed Codex app-server could not be resolved: managed launcher missing",
}),
]);
expect(deps.runVersionCommand).not.toHaveBeenCalled();
});
it("reports a launcher whose platform-native artifact is absent", async () => {
const deps = managedDeps();
deps.resolveNativeCommand.mockReturnValueOnce(undefined);
const check = createCheck(deps);
await expect(check.detect(context(config()))).resolves.toEqual([
expect.objectContaining({
checkId: CODEX_MANAGED_APP_SERVER_CHECK_ID,
path: "/candidate/plugin/codex",
message: "Managed Codex app-server resolved a launcher without a native artifact.",
}),
]);
expect(deps.runVersionCommand).not.toHaveBeenCalled();
});
it("reports a bounded version command failure", async () => {
const deps = managedDeps();
deps.runVersionCommand.mockRejectedValueOnce(new Error("timed out after 5000 ms"));
const check = createCheck(deps);
await expect(check.detect(context(config()))).resolves.toEqual([
expect.objectContaining({
checkId: CODEX_MANAGED_APP_SERVER_CHECK_ID,
path: "/candidate/plugin/codex-native",
message: "Managed Codex app-server version check failed: timed out after 5000 ms",
requirement: `Codex ${CODEX_APP_SERVER_VERSION} must report its version within 5000 ms`,
}),
]);
});
it.each([
["custom command", { command: "/operator/codex" }],
["websocket transport", { transport: "websocket", url: "ws://127.0.0.1:4500" }],
["unix transport", { transport: "unix", url: "unix:///tmp/codex.sock", homeScope: "user" }],
])("does not probe a %s", async (_label, appServer) => {
const deps = managedDeps();
const check = createCheck(deps);
await expect(check.detect(context(config(appServer)))).resolves.toEqual([]);
expect(deps.resolveStartOptions).not.toHaveBeenCalled();
expect(deps.runVersionCommand).not.toHaveBeenCalled();
});
it("does not enforce the package pin on a selected desktop-owned command", async () => {
const deps = managedDeps("0.146.0");
const check = createCheck(deps);
await expect(check.detect(context(config({ homeScope: "user" })))).resolves.toEqual([]);
expect(deps.resolveStartOptions).toHaveBeenCalledWith(
expect.objectContaining({ managedCommandOrder: "desktop-first" }),
{ pluginRoot: "/candidate/plugin" },
);
expect(deps.resolveNativeCommand).not.toHaveBeenCalled();
expect(deps.runVersionCommand).not.toHaveBeenCalled();
});
it("uses persisted per-agent Computer Use state before selecting the managed command", async () => {
const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-doctor-agent-"));
try {
await fs.mkdir(path.join(agentDir, "codex-home"));
await fs.writeFile(
path.join(agentDir, "codex-home", "config.toml"),
'[plugins."computer-use@openai-bundled"]\nenabled = true\n',
);
const cfg = config();
cfg.agents = {
...cfg.agents,
list: [{ id: "main", agentDir }],
};
const deps = managedDeps("0.146.0");
const check = createCheck(deps);
await expect(check.detect(context(cfg))).resolves.toEqual([]);
expect(deps.resolveStartOptions).toHaveBeenCalledWith(
expect.objectContaining({ managedCommandOrder: "desktop-first" }),
{ pluginRoot: "/candidate/plugin" },
);
expect(deps.resolveNativeCommand).not.toHaveBeenCalled();
expect(deps.runVersionCommand).not.toHaveBeenCalled();
} finally {
await fs.rm(agentDir, { recursive: true, force: true });
}
});
it("still validates the package when any configured agent can select it", async () => {
const desktopAgentDir = await fs.mkdtemp(
path.join(os.tmpdir(), "openclaw-codex-doctor-desktop-agent-"),
);
const packageAgentDir = await fs.mkdtemp(
path.join(os.tmpdir(), "openclaw-codex-doctor-package-agent-"),
);
try {
await fs.mkdir(path.join(desktopAgentDir, "codex-home"));
await fs.writeFile(
path.join(desktopAgentDir, "codex-home", "config.toml"),
'[plugins."computer-use@openai-bundled"]\nenabled = true\n',
);
const cfg = config();
cfg.agents = {
...cfg.agents,
list: [
{ id: "desktop", agentDir: desktopAgentDir },
{ id: "package", agentDir: packageAgentDir },
],
};
const deps = managedDeps("0.146.0");
const check = createCheck(deps);
await expect(check.detect(context(cfg))).resolves.toEqual([
expect.objectContaining({
checkId: CODEX_MANAGED_APP_SERVER_CHECK_ID,
message: `Managed Codex app-server version mismatch: expected ${CODEX_APP_SERVER_VERSION}, detected 0.146.0.`,
}),
]);
expect(deps.resolveStartOptions).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ managedCommandOrder: "desktop-first" }),
{ pluginRoot: "/candidate/plugin" },
);
expect(deps.resolveStartOptions).toHaveBeenNthCalledWith(
2,
expect.not.objectContaining({ managedCommandOrder: expect.anything() }),
{ pluginRoot: "/candidate/plugin" },
);
expect(deps.runVersionCommand).toHaveBeenCalledWith("/candidate/plugin/codex-native");
} finally {
await Promise.all(
[desktopAgentDir, packageAgentDir].map((agentDir) =>
fs.rm(agentDir, { recursive: true, force: true }),
),
);
}
});
it("ignores managed commands for agents whose effective runtime is not Codex", async () => {
const desktopAgentDir = await fs.mkdtemp(
path.join(os.tmpdir(), "openclaw-codex-doctor-desktop-agent-"),
);
try {
await fs.mkdir(path.join(desktopAgentDir, "codex-home"));
await fs.writeFile(
path.join(desktopAgentDir, "codex-home", "config.toml"),
'[plugins."computer-use@openai-bundled"]\nenabled = true\n',
);
const cfg = config();
cfg.agents = {
...cfg.agents,
list: [
{ id: "desktop", agentDir: desktopAgentDir },
{
id: "openclaw",
model: "anthropic/claude-opus-4-7",
models: {
"anthropic/claude-opus-4-7": { agentRuntime: { id: "openclaw" } },
},
},
],
};
const deps = managedDeps("0.146.0");
const check = createCheck(deps);
await expect(check.detect(context(cfg))).resolves.toEqual([]);
expect(deps.resolveStartOptions).toHaveBeenCalledTimes(1);
expect(deps.runVersionCommand).not.toHaveBeenCalled();
} finally {
await fs.rm(desktopAgentDir, { recursive: true, force: true });
}
});
it("still validates a package fallback selected after desktop-first resolution", async () => {
const deps = managedDeps("0.146.0");
deps.resolveStartOptions.mockImplementationOnce(async (start) => ({
...start,
command: "/candidate/plugin/codex",
commandSource: "resolved-managed" as const,
}));
const check = createCheck(deps);
await expect(check.detect(context(config({ homeScope: "user" })))).resolves.toEqual([
expect.objectContaining({
checkId: CODEX_MANAGED_APP_SERVER_CHECK_ID,
message: `Managed Codex app-server version mismatch: expected ${CODEX_APP_SERVER_VERSION}, detected 0.146.0.`,
}),
]);
expect(deps.runVersionCommand).toHaveBeenCalledWith("/candidate/plugin/codex-native");
});
});
+216
View File
@@ -0,0 +1,216 @@
import { execFile } from "node:child_process";
import { resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
import { listAgentIds, resolveAgentDir } from "openclaw/plugin-sdk/agent-scope-runtime";
import { resolveEffectiveAgentRuntime } from "openclaw/plugin-sdk/command-auth-native";
import { getHealthCheck, type HealthCheck, type HealthFinding } from "openclaw/plugin-sdk/health";
import {
resolveCodexAppServerRuntimeOptions,
resolveCodexAppServerStartOptionsForAgent,
} from "./app-server/config.js";
import {
isManagedCodexDesktopCommand,
resolveManagedCodexAppServerStartOptions,
resolveManagedCodexNativeCommand,
} from "./app-server/managed-binary.js";
import { CODEX_APP_SERVER_VERSION } from "./app-server/version.js";
export const CODEX_MANAGED_APP_SERVER_CHECK_ID = "codex/managed-app-server";
const CODEX_VERSION_TIMEOUT_MS = 5_000;
const CODEX_VERSION_MAX_BUFFER_BYTES = 64 * 1024;
type VersionCommandResult = {
stdout: string;
stderr: string;
};
type CodexManagedDoctorDependencies = {
resolveAgentStartOptions?: typeof resolveCodexAppServerStartOptionsForAgent;
resolveStartOptions?: typeof resolveManagedCodexAppServerStartOptions;
isDesktopCommand?: typeof isManagedCodexDesktopCommand;
resolveNativeCommand?: typeof resolveManagedCodexNativeCommand;
runVersionCommand?: (command: string) => Promise<VersionCommandResult>;
};
type CodexManagedDoctorRegistrationHost = {
readonly registerHealthCheck: (check: HealthCheck) => void;
readonly pluginRoot: string;
};
function managedCodexFinding(params: {
message: string;
path?: string;
requirement?: string;
fixHint?: string;
}): HealthFinding {
return {
checkId: CODEX_MANAGED_APP_SERVER_CHECK_ID,
severity: "error",
source: "codex",
message: params.message,
...(params.path ? { path: params.path } : {}),
...(params.requirement ? { requirement: params.requirement } : {}),
...(params.fixHint ? { fixHint: params.fixHint } : {}),
};
}
function readErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
function parseCodexVersion(output: string): string | undefined {
return /(?:^|\s)(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)(?:\s|$)/u.exec(
output,
)?.[1];
}
function runVersionCommand(command: string): Promise<VersionCommandResult> {
return new Promise((resolve, reject) => {
execFile(
command,
["--version"],
{
encoding: "utf8",
maxBuffer: CODEX_VERSION_MAX_BUFFER_BYTES,
timeout: CODEX_VERSION_TIMEOUT_MS,
windowsHide: true,
},
(error, stdout, stderr) => {
if (error) {
reject(new Error(readErrorMessage(error), { cause: error }));
return;
}
resolve({ stdout, stderr });
},
);
});
}
function createCodexManagedAppServerHealthCheck(params: {
pluginRoot: string;
deps?: CodexManagedDoctorDependencies;
}): HealthCheck & { readonly defaultEnabled: false } {
const resolveStartOptions =
params.deps?.resolveStartOptions ?? resolveManagedCodexAppServerStartOptions;
const resolveAgentStartOptions =
params.deps?.resolveAgentStartOptions ?? resolveCodexAppServerStartOptionsForAgent;
const isDesktopCommand = params.deps?.isDesktopCommand ?? isManagedCodexDesktopCommand;
const resolveNativeCommand =
params.deps?.resolveNativeCommand ?? resolveManagedCodexNativeCommand;
const executeVersion = params.deps?.runVersionCommand ?? runVersionCommand;
return {
id: CODEX_MANAGED_APP_SERVER_CHECK_ID,
kind: "plugin",
description: "Verify the selected managed Codex app-server binary and pinned version.",
source: "codex",
defaultEnabled: false,
async detect(ctx) {
const pluginConfig = ctx.cfg.plugins?.entries?.codex?.config;
const start = resolveCodexAppServerRuntimeOptions({
pluginConfig,
env: ctx.env ?? process.env,
}).start;
if (start.transport !== "stdio" || start.commandSource !== "managed") {
return [];
}
const env = ctx.env ?? process.env;
let resolved;
for (const agentId of listAgentIds(ctx.cfg)) {
const model = resolveDefaultModelForAgent({ cfg: ctx.cfg, agentId });
if (
resolveEffectiveAgentRuntime({
cfg: ctx.cfg,
provider: model.provider,
modelId: model.model,
agentId,
}) !== "codex"
) {
continue;
}
const agentStart = resolveAgentStartOptions({
startOptions: start,
agentDir: resolveAgentDir(ctx.cfg, agentId, env),
env,
});
try {
resolved = await resolveStartOptions(agentStart, { pluginRoot: params.pluginRoot });
} catch (error) {
return [
managedCodexFinding({
message: `Managed Codex app-server could not be resolved: ${readErrorMessage(error)}`,
path: params.pluginRoot,
requirement: `an executable Codex ${CODEX_APP_SERVER_VERSION} managed artifact`,
fixHint:
"Reinstall the staged OpenClaw package with its @openai/codex platform dependency, then rerun the candidate check.",
}),
];
}
if (!isDesktopCommand(resolved.command)) {
break;
}
resolved = undefined;
}
if (!resolved) {
return [];
}
const nativeCommand = resolveNativeCommand(resolved.command);
if (!nativeCommand) {
return [
managedCodexFinding({
message: "Managed Codex app-server resolved a launcher without a native artifact.",
path: resolved.command,
requirement: `the platform-native Codex ${CODEX_APP_SERVER_VERSION} executable`,
fixHint:
"Reinstall the staged OpenClaw package with the matching @openai/codex platform package, then rerun the candidate check.",
}),
];
}
let output: VersionCommandResult;
try {
output = await executeVersion(nativeCommand);
} catch (error) {
return [
managedCodexFinding({
message: `Managed Codex app-server version check failed: ${readErrorMessage(error)}`,
path: nativeCommand,
requirement: `Codex ${CODEX_APP_SERVER_VERSION} must report its version within ${CODEX_VERSION_TIMEOUT_MS} ms`,
fixHint:
"Repair or reinstall the staged OpenClaw package, then rerun the candidate check before cutover.",
}),
];
}
const detectedVersion = parseCodexVersion(`${output.stdout}\n${output.stderr}`);
if (detectedVersion !== CODEX_APP_SERVER_VERSION) {
return [
managedCodexFinding({
message: detectedVersion
? `Managed Codex app-server version mismatch: expected ${CODEX_APP_SERVER_VERSION}, detected ${detectedVersion}.`
: `Managed Codex app-server did not report a parseable version; expected ${CODEX_APP_SERVER_VERSION}.`,
path: nativeCommand,
requirement: `the exact OpenClaw-pinned Codex version ${CODEX_APP_SERVER_VERSION}`,
fixHint:
"Reinstall the staged OpenClaw package so its managed @openai/codex dependency matches the pinned version, then rerun the candidate check.",
}),
];
}
return [];
},
};
}
export function registerCodexManagedAppServerDoctorChecks(
host: CodexManagedDoctorRegistrationHost,
deps?: CodexManagedDoctorDependencies,
): void {
if (getHealthCheck(CODEX_MANAGED_APP_SERVER_CHECK_ID)) {
return;
}
host.registerHealthCheck(
createCodexManagedAppServerHealthCheck({ pluginRoot: host.pluginRoot, deps }),
);
}
+9 -5
View File
@@ -141,7 +141,11 @@ finish_pr_operation_completion() {
local operation_status="$1"
trap - EXIT
trap '' PIPE
if [ "$BASHPID" = "$PR_OPERATION_COMPLETION_LEADER_PID" ]; then
# macOS system Bash 3.2 has no BASHPID. $$ identifies the top-level shell,
# while BASH_SUBSHELL fences forked subshells that retain the same $$.
if [ "${BASH_SUBSHELL:-0}" -eq 0 ] &&
[ "$$" = "$PR_OPERATION_COMPLETION_LEADER_PID" ]
then
notify_pr_operation_phase operation-complete 2>/dev/null || :
fi
exit "$operation_status"
@@ -149,7 +153,7 @@ finish_pr_operation_completion() {
install_pr_operation_completion_trap() {
[ -z "$PR_OPERATION_COMPLETION_LEADER_PID" ] || return 0
PR_OPERATION_COMPLETION_LEADER_PID="$BASHPID"
PR_OPERATION_COMPLETION_LEADER_PID="$$"
trap 'finish_pr_operation_completion "$?"' EXIT
}
@@ -158,11 +162,11 @@ install_pr_operation_completion_trap() {
if [ "${OPENCLAW_PR_DEDICATED_PROCESS_GROUP:-}" = "1" ]; then
if [ "${OPENCLAW_PR_LOCK_NOTIFY_FD:-}" = "3" ] &&
[ "${OPENCLAW_PR_LOCK_SUPERVISOR_PID:-}" = "$PPID" ] &&
[ "$BASHPID" = "$$" ]
[ "${BASH_SUBSHELL:-0}" -eq 0 ]
then
pr_operation_entry_pgid=$(ps -o pgid= -p "$BASHPID" 2>/dev/null || true)
pr_operation_entry_pgid=$(ps -o pgid= -p "$$" 2>/dev/null || true)
pr_operation_entry_pgid="${pr_operation_entry_pgid//[[:space:]]/}"
if [ "$pr_operation_entry_pgid" = "$BASHPID" ]; then
if [ "$pr_operation_entry_pgid" = "$$" ]; then
install_pr_operation_completion_trap
fi
unset pr_operation_entry_pgid
+64 -1
View File
@@ -12,6 +12,7 @@ import {
const STATE_DEFERRED_CHECK_ID = "memory-core/managed-local-embedding-setup";
const mocks = vi.hoisted(() => ({
registerCodexManagedAppServerDoctorChecks: vi.fn(),
inspectEmbeddingProviderSetup: vi.fn(),
loadPluginManifestRegistryForPluginRegistry: vi.fn(() => ({
plugins: [],
@@ -28,7 +29,12 @@ const mocks = vi.hoisted(() => ({
}
: dirName === "cua-computer"
? { registerCuaDriverDoctorChecks: mocks.registerCuaDriverDoctorChecks }
: { registerPolicyDoctorChecks: mocks.registerPolicyDoctorChecks },
: dirName === "codex"
? {
registerCodexManagedAppServerDoctorChecks:
mocks.registerCodexManagedAppServerDoctorChecks,
}
: { registerPolicyDoctorChecks: mocks.registerPolicyDoctorChecks },
),
resolveProviderPolicySurface: vi.fn((): ProviderPolicySurface | null => ({
inspectEmbeddingProviderSetup: mocks.inspectEmbeddingProviderSetup,
@@ -282,6 +288,63 @@ describe("registerBundledHealthChecks", () => {
});
});
it("loads managed Codex health when an effective model route selects Codex", () => {
registerBundledHealthChecks({
cfg: {
agents: {
defaults: {
model: { primary: "openai/gpt-5.6-sol" },
models: {
"openai/gpt-5.6-sol": { agentRuntime: { id: "codex" } },
},
},
},
},
cwd: workspaceDir,
});
expect(mocks.loadBundledPluginPublicArtifactModuleSync).toHaveBeenCalledWith({
dirName: "codex",
artifactBasename: "api.js",
});
expect(mocks.registerCodexManagedAppServerDoctorChecks).toHaveBeenCalledWith({
registerHealthCheck: expect.any(Function),
});
});
it("does not load managed Codex health for OpenClaw routes or disabled Codex", () => {
for (const cfg of [
{
agents: {
defaults: {
model: { primary: "openai/gpt-5.6-sol" },
models: {
"openai/gpt-5.6-sol": { agentRuntime: { id: "openclaw" } },
},
},
},
},
{
agents: {
defaults: {
model: { primary: "openai/gpt-5.6-sol" },
models: {
"openai/gpt-5.6-sol": { agentRuntime: { id: "codex" } },
},
},
},
plugins: { entries: { codex: { enabled: false } } },
},
]) {
vi.clearAllMocks();
registerBundledHealthChecks({ cfg, cwd: workspaceDir });
expect(mocks.loadBundledPluginPublicArtifactModuleSync).not.toHaveBeenCalledWith({
dirName: "codex",
artifactBasename: "api.js",
});
}
});
it("does not use policy.jsonc existence as extension activation", () => {
writeFileSync(join(workspaceDir, "policy.jsonc"), "{}\n", "utf-8");
+23
View File
@@ -1,5 +1,6 @@
// Bundled health checks define built-in doctor checks for runtime readiness.
import { asOptionalObjectRecord as readRecord } from "@openclaw/normalization-core/record-coerce";
import { collectConfiguredAgentHarnessRuntimes } from "../agents/harness-runtimes.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { normalizePluginId, normalizePluginsConfig } from "../plugins/config-state.js";
import { passesManifestOwnerBasePolicy } from "../plugins/manifest-owner-policy.js";
@@ -16,6 +17,9 @@ type EmbeddingProviderSetupInspectionResult =
// Bridges bundled plugin doctor checks into the core health registry.
type BundledHealthApi = {
registerCodexManagedAppServerDoctorChecks?: (host: {
registerHealthCheck: typeof registerHealthCheck;
}) => void;
pluginStateIsolatedDoctorCheckIds?: readonly string[];
registerCuaDriverDoctorChecks?: (host: {
registerHealthCheck: typeof registerHealthCheck;
@@ -105,6 +109,12 @@ export function registerBundledHealthChecks(params: {
},
memoryCoreActive: isMemoryCoreActive(params.cfg),
});
if (shouldRegisterCodexManagedHealth(params.cfg)) {
loadBundledPluginPublicArtifactModuleSync<BundledHealthApi>({
dirName: "codex",
artifactBasename: "api.js",
}).registerCodexManagedAppServerDoctorChecks?.({ registerHealthCheck });
}
if (shouldRegisterPolicyHealth(params)) {
loadBundledPluginPublicArtifactModuleSync<BundledHealthApi>({
dirName: "policy",
@@ -119,6 +129,19 @@ export function registerBundledHealthChecks(params: {
}
}
function shouldRegisterCodexManagedHealth(cfg: OpenClawConfig): boolean {
if (!collectConfiguredAgentHarnessRuntimes(cfg).includes("codex")) {
return false;
}
if (cfg.plugins?.entries?.codex?.enabled === false) {
return false;
}
return passesManifestOwnerBasePolicy({
plugin: { id: "codex" },
normalizedConfig: normalizePluginsConfig(cfg.plugins),
});
}
function isMemoryCoreActive(cfg: OpenClawConfig): boolean {
const plugins = normalizePluginsConfig(cfg.plugins);
const selectedMemoryPluginId =
@@ -30,6 +30,7 @@ const REQUIRED_REVIEWED_PUBLISHABLE_CRITICAL_FINDING_COUNTS = new Map<string, nu
["@openclaw/codex:dangerous-exec:src/app-server/sandbox-exec-server/http.ts", 1],
["@openclaw/codex:dangerous-exec:src/app-server/sandbox-exec-server/processes.ts", 1],
["@openclaw/codex:dangerous-exec:src/app-server/transport-stdio.ts", 1],
["@openclaw/codex:dangerous-exec:src/doctor.ts", 1],
["@openclaw/codex:dangerous-exec:src/node-cli-sessions.ts", 1],
["@openclaw/discord:dangerous-exec:src/voice/audio.ts", 1],
["@openclaw/imessage:dangerous-exec:src/client.ts", 1],
@@ -46,7 +47,8 @@ const REQUIRED_REVIEWED_PUBLISHABLE_CRITICAL_FINDING_COUNTS = new Map<string, nu
const OPTIONAL_REVIEWED_PUBLISHABLE_DIST_CRITICAL_FINDING_COUNTS = new Map<string, number>([
["@openclaw/acpx:dangerous-exec:dist/mcp-proxy.mjs", 1],
["@openclaw/acpx:dangerous-exec:dist/service-<hash>.js", 1],
["@openclaw/codex:dangerous-exec:dist/run-attempt-<hash>.js", 2],
["@openclaw/codex:dangerous-exec:dist/api.js", 1],
["@openclaw/codex:dangerous-exec:dist/dynamic-tools-<hash>.js", 2],
["@openclaw/codex:dangerous-exec:dist/session-catalog-<hash>.js", 1],
["@openclaw/codex:dangerous-exec:dist/transport-stdio-<hash>.js", 1],
["@openclaw/llama-cpp-provider:dangerous-exec:dist/index.js", 1],
@@ -96,6 +98,7 @@ function isScannerWalkedPackedPath(packedPath: string): boolean {
function normalizePackedFindingPath(packedPath: string): string {
for (const prefix of [
"dynamic-tools",
"outbound-payload.test-harness",
"run-attempt",
"runtime-entry",
@@ -330,14 +333,20 @@ describe("publishable plugin npm package install security scan", () => {
});
it("requires exact occurrence counts for reviewed Codex dist chunks", () => {
const runAttemptKey = "@openclaw/codex:dangerous-exec:dist/run-attempt-<hash>.js";
const dynamicToolsKey = "@openclaw/codex:dangerous-exec:dist/dynamic-tools-<hash>.js";
expect(
expectedOptionalReviewedFindingsForPackedPath(
"@openclaw/codex",
"dist/dynamic-tools-current.js",
),
).toEqual([dynamicToolsKey, dynamicToolsKey]);
expect(
expectedOptionalReviewedFindingsForPackedPath(
"@openclaw/codex",
"dist/run-attempt-current.js",
),
).toEqual([runAttemptKey, runAttemptKey]);
).toEqual([]);
expect(
expectedOptionalReviewedFindingsForPackedPath(
"@openclaw/codex",