Merge pull request #338 from ComposioHQ/session/ao-3

fix: suppress codex update prompt in non-interactive agent sessions
This commit is contained in:
prateek 2026-03-08 02:17:32 +05:30 committed by GitHub
commit 767495ff84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 70 additions and 5 deletions

View File

@ -0,0 +1,45 @@
import { describe, expect, it } from "vitest";
import type { AgentLaunchConfig } from "@composio/ao-core";
import codexPlugin from "@composio/ao-plugin-agent-codex";
function makeLaunchConfig(overrides: Partial<AgentLaunchConfig> = {}): AgentLaunchConfig {
return {
sessionId: "sess-int-1",
projectConfig: {
name: "my-project",
repo: "owner/repo",
path: "/workspace/repo",
defaultBranch: "main",
sessionPrefix: "my",
},
...overrides,
};
}
describe("agent-codex launch/env wiring (integration)", () => {
const agent = codexPlugin.create();
it("includes check_for_update_on_startup=false in launch command", () => {
const cmd = agent.getLaunchCommand(makeLaunchConfig());
expect(cmd).toContain("-c check_for_update_on_startup=false");
});
it("preserves update-check override alongside other flags", () => {
const cmd = agent.getLaunchCommand(
makeLaunchConfig({
permissions: "default",
model: "o3-mini",
prompt: "Do the thing",
}),
);
expect(cmd).toContain("-c check_for_update_on_startup=false");
expect(cmd).not.toContain("--ask-for-approval");
expect(cmd).toContain("--model 'o3-mini'");
expect(cmd).toContain("-c model_reasoning_effort=high");
});
it("sets CODEX_DISABLE_UPDATE_CHECK=1 in environment", () => {
const env = agent.getEnvironment(makeLaunchConfig());
expect(env["CODEX_DISABLE_UPDATE_CHECK"]).toBe("1");
});
});

View File

@ -210,7 +210,7 @@ describe("getLaunchCommand", () => {
const agent = create();
it("generates base command", () => {
expect(agent.getLaunchCommand(makeLaunchConfig())).toBe("'codex'");
expect(agent.getLaunchCommand(makeLaunchConfig())).toBe("'codex' -c check_for_update_on_startup=false");
});
it("includes bypass flag when permissions=permissionless", () => {
@ -258,7 +258,7 @@ describe("getLaunchCommand", () => {
const cmd = agent.getLaunchCommand(
makeLaunchConfig({ permissions: "permissionless", model: "o3", prompt: "Go" }),
);
expect(cmd).toBe("'codex' --dangerously-bypass-approvals-and-sandbox --model 'o3' -c model_reasoning_effort=high -- 'Go'");
expect(cmd).toBe("'codex' -c check_for_update_on_startup=false --dangerously-bypass-approvals-and-sandbox --model 'o3' -c model_reasoning_effort=high -- 'Go'");
});
it("escapes single quotes in prompt (POSIX shell escaping)", () => {
@ -297,10 +297,15 @@ describe("getLaunchCommand", () => {
expect(cmd).not.toContain("--dangerously-bypass-approvals-and-sandbox");
expect(cmd).not.toContain("--ask-for-approval");
expect(cmd).not.toContain("--model");
expect(cmd).not.toContain("-c");
expect(cmd).toContain("-c check_for_update_on_startup=false");
expect(cmd).not.toContain("model_reasoning_effort");
});
it("always includes -c check_for_update_on_startup=false", () => {
const cmd = agent.getLaunchCommand(makeLaunchConfig({ model: "gpt-4o", prompt: "Fix it" }));
expect(cmd).toContain("-c check_for_update_on_startup=false");
});
// -- Reasoning effort tests --
describe("reasoning effort", () => {
it("adds model_reasoning_effort=high for o3 model", () => {
@ -377,6 +382,11 @@ describe("getEnvironment", () => {
expect(env["PATH"]?.startsWith("/mock/home/.ao/bin:")).toBe(true);
});
it("sets CODEX_DISABLE_UPDATE_CHECK=1 to suppress interactive update prompts", () => {
const env = agent.getEnvironment(makeLaunchConfig());
expect(env["CODEX_DISABLE_UPDATE_CHECK"]).toBe("1");
});
it("falls back to /usr/bin:/bin when process.env.PATH is undefined", () => {
const originalPath = process.env["PATH"];
delete process.env["PATH"];
@ -965,6 +975,7 @@ describe("getRestoreCommand", () => {
expect(cmd).not.toBeNull();
expect(cmd).toContain("'codex' resume");
expect(cmd).toContain("-c check_for_update_on_startup=false");
expect(cmd).toContain("thread-abc-123");
});
@ -1236,13 +1247,13 @@ describe("postLaunchSetup", () => {
mockReadFile.mockRejectedValue(new Error("ENOENT"));
// Before postLaunchSetup, binary is "codex"
expect(agent.getLaunchCommand(makeLaunchConfig())).toBe("'codex'");
expect(agent.getLaunchCommand(makeLaunchConfig())).toBe("'codex' -c check_for_update_on_startup=false");
// After postLaunchSetup resolves the binary
await agent.postLaunchSetup!(makeSession({ workspacePath: "/workspace/test" }));
// Now getLaunchCommand should use the resolved binary
expect(agent.getLaunchCommand(makeLaunchConfig())).toBe("'/opt/bin/codex'");
expect(agent.getLaunchCommand(makeLaunchConfig())).toBe("'/opt/bin/codex' -c check_for_update_on_startup=false");
});
});

View File

@ -548,6 +548,11 @@ function appendModelFlags(parts: string[], model: string | undefined): void {
}
}
/** Disable Codex startup update checks/prompts in non-interactive sessions */
function appendNoUpdateCheckFlag(parts: string[]): void {
parts.push("-c", "check_for_update_on_startup=false");
}
/** TTL for session file path cache (ms). Prevents redundant filesystem scans
* when getActivityState and getSessionInfo are called in the same refresh cycle. */
const SESSION_FILE_CACHE_TTL_MS = 30_000;
@ -580,6 +585,7 @@ function createCodexAgent(): Agent {
getLaunchCommand(config: AgentLaunchConfig): string {
const binary = resolvedBinary ?? "codex";
const parts: string[] = [shellEscape(binary)];
appendNoUpdateCheckFlag(parts);
appendApprovalFlags(parts, config.permissions);
appendModelFlags(parts, config.model);
@ -613,6 +619,8 @@ function createCodexAgent(): Agent {
// The wrappers strip this directory from PATH before calling the real
// binary, so there's no infinite recursion.
env["PATH"] = `${AO_BIN_DIR}:${process.env["PATH"] ?? "/usr/bin:/bin"}`;
// Disable Codex's version check/update prompt for non-interactive AO sessions.
env["CODEX_DISABLE_UPDATE_CHECK"] = "1";
return env;
},
@ -769,6 +777,7 @@ function createCodexAgent(): Agent {
// Flags are placed before the positional threadId for CLI parser compatibility.
const binary = resolvedBinary ?? "codex";
const parts: string[] = [shellEscape(binary), "resume"];
appendNoUpdateCheckFlag(parts);
appendApprovalFlags(parts, project.agentConfig?.permissions);
const effectiveModel = (project.agentConfig?.model ?? data.model) as string | undefined;