diff --git a/packages/adapters/codex-local/src/index.ts b/packages/adapters/codex-local/src/index.ts index 0b9b6103..3a499660 100644 --- a/packages/adapters/codex-local/src/index.ts +++ b/packages/adapters/codex-local/src/index.ts @@ -1,8 +1,10 @@ export const type = "codex_local"; export const label = "Codex (local)"; +export const DEFAULT_CODEX_LOCAL_MODEL = "gpt-5.3-codex"; +export const DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX = true; export const models = [ - { id: "gpt-5.3-codex", label: "gpt-5.3-codex" }, + { id: DEFAULT_CODEX_LOCAL_MODEL, label: DEFAULT_CODEX_LOCAL_MODEL }, { id: "gpt-5.3-codex-spark", label: "gpt-5.3-codex-spark" }, { id: "gpt-5", label: "gpt-5" }, { id: "o3", label: "o3" }, diff --git a/packages/adapters/codex-local/src/ui/build-config.ts b/packages/adapters/codex-local/src/ui/build-config.ts index 3c745c8e..4555dcbd 100644 --- a/packages/adapters/codex-local/src/ui/build-config.ts +++ b/packages/adapters/codex-local/src/ui/build-config.ts @@ -1,4 +1,8 @@ import type { CreateConfigValues } from "@paperclipai/adapter-utils"; +import { + DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX, + DEFAULT_CODEX_LOCAL_MODEL, +} from "../index.js"; function parseCommaArgs(value: string): string[] { return value @@ -55,7 +59,7 @@ export function buildCodexLocalConfig(v: CreateConfigValues): Record 0) ac.env = env; ac.search = v.search; - ac.dangerouslyBypassApprovalsAndSandbox = v.dangerouslyBypassSandbox; + ac.dangerouslyBypassApprovalsAndSandbox = + typeof v.dangerouslyBypassSandbox === "boolean" + ? v.dangerouslyBypassSandbox + : DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX; if (v.command) ac.command = v.command; if (v.extraArgs) ac.extraArgs = parseCommaArgs(v.extraArgs); return ac; diff --git a/server/src/routes/agents.ts b/server/src/routes/agents.ts index 94a2e4f4..9f4fdade 100644 --- a/server/src/routes/agents.ts +++ b/server/src/routes/agents.ts @@ -32,6 +32,10 @@ import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js"; import { findServerAdapter, listAdapterModels } from "../adapters/index.js"; import { redactEventPayload } from "../redaction.js"; import { runClaudeLogin } from "@paperclipai/adapter-claude-local/server"; +import { + DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX, + DEFAULT_CODEX_LOCAL_MODEL, +} from "@paperclipai/adapter-codex-local"; export function agentRoutes(db: Db) { const DEFAULT_INSTRUCTIONS_PATH_KEYS: Record = { @@ -170,6 +174,25 @@ export function agentRoutes(db: Db) { return trimmed.length > 0 ? trimmed : null; } + function applyCreateDefaultsByAdapterType( + adapterType: string | null | undefined, + adapterConfig: Record, + ): Record { + if (adapterType !== "codex_local") return adapterConfig; + + const next = { ...adapterConfig }; + if (!asNonEmptyString(next.model)) { + next.model = DEFAULT_CODEX_LOCAL_MODEL; + } + const hasBypassFlag = + typeof next.dangerouslyBypassApprovalsAndSandbox === "boolean" || + typeof next.dangerouslyBypassSandbox === "boolean"; + if (!hasBypassFlag) { + next.dangerouslyBypassApprovalsAndSandbox = DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX; + } + return next; + } + function resolveInstructionsFilePath(candidatePath: string, adapterConfig: Record) { const trimmed = candidatePath.trim(); if (path.isAbsolute(trimmed)) return trimmed; @@ -546,9 +569,13 @@ export function agentRoutes(db: Db) { await assertCanCreateAgentsForCompany(req, companyId); const sourceIssueIds = parseSourceIssueIds(req.body); const { sourceIssueId: _sourceIssueId, sourceIssueIds: _sourceIssueIds, ...hireInput } = req.body; + const requestedAdapterConfig = applyCreateDefaultsByAdapterType( + hireInput.adapterType, + ((hireInput.adapterConfig ?? {}) as Record), + ); const normalizedAdapterConfig = await secretsSvc.normalizeAdapterConfigForPersistence( companyId, - ((hireInput.adapterConfig ?? {}) as Record), + requestedAdapterConfig, { strictMode: strictSecretsMode }, ); const normalizedHireInput = { @@ -677,9 +704,13 @@ export function agentRoutes(db: Db) { assertBoard(req); } + const requestedAdapterConfig = applyCreateDefaultsByAdapterType( + req.body.adapterType, + ((req.body.adapterConfig ?? {}) as Record), + ); const normalizedAdapterConfig = await secretsSvc.normalizeAdapterConfigForPersistence( companyId, - ((req.body.adapterConfig ?? {}) as Record), + requestedAdapterConfig, { strictMode: strictSecretsMode }, ); diff --git a/ui/src/components/AgentConfigForm.tsx b/ui/src/components/AgentConfigForm.tsx index fedd300c..688e98cf 100644 --- a/ui/src/components/AgentConfigForm.tsx +++ b/ui/src/components/AgentConfigForm.tsx @@ -11,6 +11,10 @@ import type { AdapterModel } from "../api/agents"; import { agentsApi } from "../api/agents"; import { secretsApi } from "../api/secrets"; import { assetsApi } from "../api/assets"; +import { + DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX, + DEFAULT_CODEX_LOCAL_MODEL, +} from "@paperclipai/adapter-codex-local"; import { Popover, PopoverContent, @@ -433,7 +437,13 @@ export function AgentConfigForm(props: AgentConfigFormProps) { if (isCreate) { // Reset all adapter-specific fields to defaults when switching adapter type const { adapterType: _at, ...defaults } = defaultCreateValues; - set!({ ...defaults, adapterType: t }); + const nextValues: CreateConfigValues = { ...defaults, adapterType: t }; + if (t === "codex_local") { + nextValues.model = DEFAULT_CODEX_LOCAL_MODEL; + nextValues.dangerouslyBypassSandbox = + DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX; + } + set!(nextValues); } else { // Clear all adapter config and explicitly blank out model + both effort keys // so the old adapter's values don't bleed through via eff() @@ -441,9 +451,15 @@ export function AgentConfigForm(props: AgentConfigFormProps) { ...prev, adapterType: t, adapterConfig: { - model: "", + model: t === "codex_local" ? DEFAULT_CODEX_LOCAL_MODEL : "", effort: "", modelReasoningEffort: "", + ...(t === "codex_local" + ? { + dangerouslyBypassApprovalsAndSandbox: + DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX, + } + : {}), }, })); } diff --git a/ui/src/components/OnboardingWizard.tsx b/ui/src/components/OnboardingWizard.tsx index fbf7c524..69c81650 100644 --- a/ui/src/components/OnboardingWizard.tsx +++ b/ui/src/components/OnboardingWizard.tsx @@ -18,6 +18,10 @@ import { Button } from "@/components/ui/button"; import { cn } from "../lib/utils"; import { getUIAdapter } from "../adapters"; import { defaultCreateValues } from "./agent-config-defaults"; +import { + DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX, + DEFAULT_CODEX_LOCAL_MODEL, +} from "@paperclipai/adapter-codex-local"; import { AsciiArtAnimation } from "./AsciiArtAnimation"; import { ChoosePathButton } from "./PathInstructionsModal"; import { HintIcon } from "./agent-config-primitives"; @@ -156,11 +160,15 @@ export function OnboardingWizard() { ...defaultCreateValues, adapterType, cwd, - model, + model: adapterType === "codex_local" ? model || DEFAULT_CODEX_LOCAL_MODEL : model, command, args, url, dangerouslySkipPermissions: adapterType === "claude_local", + dangerouslyBypassSandbox: + adapterType === "codex_local" + ? DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX + : defaultCreateValues.dangerouslyBypassSandbox, }); } @@ -456,7 +464,12 @@ export function OnboardingWizard() { : "border-border hover:bg-accent/50" )} onClick={() => { - if (!opt.comingSoon) setAdapterType(opt.value as AdapterType); + if (opt.comingSoon) return; + const nextType = opt.value as AdapterType; + setAdapterType(nextType); + if (nextType === "codex_local" && !model) { + setModel(DEFAULT_CODEX_LOCAL_MODEL); + } }} >