From 1eb9b4872e91f3e49ca69cea0ac4d1e1dd007015 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Sat, 11 Apr 2026 01:14:17 +0800 Subject: [PATCH] feat(settings): add Skills toggle for Codex CLI and improve config editor UX Add "Enable Skills" toggle below "Enable WebSocket" in Codex CLI settings, writing `skills = true` under [features] in config.toml. Also add max-height to auth.json and config.toml textareas, and fix TOML section key insertion to avoid stray blank lines between entries. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../settings/acp-agent-settings.tsx | 88 ++++++++++++++++++- src/i18n/messages/ar.json | 2 + src/i18n/messages/de.json | 2 + src/i18n/messages/en.json | 2 + src/i18n/messages/es.json | 2 + src/i18n/messages/fr.json | 2 + src/i18n/messages/ja.json | 2 + src/i18n/messages/ko.json | 2 + src/i18n/messages/pt.json | 2 + src/i18n/messages/zh-CN.json | 2 + src/i18n/messages/zh-TW.json | 2 + 11 files changed, 105 insertions(+), 3 deletions(-) diff --git a/src/components/settings/acp-agent-settings.tsx b/src/components/settings/acp-agent-settings.tsx index b8f26df..8a080bb 100644 --- a/src/components/settings/acp-agent-settings.tsx +++ b/src/components/settings/acp-agent-settings.tsx @@ -118,6 +118,7 @@ interface AgentDraft { codexProviderOptions: string[] codexReasoningEffort: CodexReasoningEffort codexSupportsWebsockets: boolean + codexSkills: boolean claudeMainModel: string claudeReasoningModel: string claudeDefaultHaikuModel: string @@ -1142,6 +1143,7 @@ interface CodexTomlImportantValues { providerBaseUrls: Record providerSupportsWebsockets: Record featureResponsesWebsocketsV2: boolean + featureSkills: boolean } interface CodexImportantValues { @@ -1152,6 +1154,7 @@ interface CodexImportantValues { reasoningEffort: CodexReasoningEffort providerOptions: string[] supportsWebsockets: boolean + skills: boolean } const CODEX_DEFAULT_MODEL_PROVIDER = "codeg" @@ -1312,6 +1315,7 @@ function extractCodexTomlImportantValues( let modelReasoningEffort: CodexReasoningEffort = CODEX_DEFAULT_REASONING_EFFORT let featureResponsesWebsocketsV2 = false + let featureSkills = false let currentProviderSection: string | null = null let inFeaturesSection = false @@ -1377,6 +1381,10 @@ function extractCodexTomlImportantValues( featureResponsesWebsocketsV2 = boolAssignment.value continue } + if (inFeaturesSection && boolAssignment.key === "skills") { + featureSkills = boolAssignment.value + continue + } const dottedProviderWebsocketMatch = boolAssignment.key.match( /^model_providers\.([A-Za-z0-9_-]+)\.supports_websockets$/ ) @@ -1390,6 +1398,10 @@ function extractCodexTomlImportantValues( featureResponsesWebsocketsV2 = boolAssignment.value continue } + if (boolAssignment.key === "features.skills") { + featureSkills = boolAssignment.value + continue + } } if (!assignment) continue @@ -1436,6 +1448,7 @@ function extractCodexTomlImportantValues( providerBaseUrls, providerSupportsWebsockets, featureResponsesWebsocketsV2, + featureSkills, } } @@ -1530,6 +1543,7 @@ function extractCodexImportantValues( toml.providerNames ), supportsWebsockets: providerSupportsWebsockets, + skills: toml.featureSkills, } } @@ -1701,7 +1715,14 @@ function upsertTomlSectionBooleanKey( if (assignmentIndex >= 0) { lines[assignmentIndex] = lineText } else { - lines.splice(section.end, 0, lineText) + let insertAt = section.end + for (let i = section.end - 1; i > section.start; i -= 1) { + if (lines[i].trim() !== "") { + insertAt = i + 1 + break + } + } + lines.splice(insertAt, 0, lineText) } return lines.join("\n").trim() } @@ -1927,6 +1948,7 @@ function patchCodexConfigTomlText( modelProvider?: string modelReasoningEffort?: string supportsWebsockets?: boolean + skills?: boolean } ): string { let nextTomlText = configTomlText @@ -2019,6 +2041,14 @@ function patchCodexConfigTomlText( "responses_websockets_v2", shouldEnableFeature ? true : null ) + if (typeof patch.skills === "boolean") { + nextTomlText = upsertTomlSectionBooleanKey( + nextTomlText, + "features", + "skills", + patch.skills ? true : null + ) + } nextTomlText = updateTomlRootBooleanKey( nextTomlText, "disable_response_storage", @@ -2252,6 +2282,7 @@ function buildAgentDraft(agent: AcpAgentInfo): AgentDraft { codexProviderOptions: codexImportant.providerOptions, codexReasoningEffort: codexImportant.reasoningEffort, codexSupportsWebsockets: codexImportant.supportsWebsockets, + codexSkills: codexImportant.skills, claudeMainModel: important.claudeMainModel, claudeReasoningModel: important.claudeReasoningModel, claudeDefaultHaikuModel: important.claudeDefaultHaikuModel, @@ -4432,6 +4463,7 @@ export function AcpAgentSettings() { codexProviderOptions: important.providerOptions, codexReasoningEffort: important.reasoningEffort, codexSupportsWebsockets: important.supportsWebsockets, + codexSkills: important.skills, })) }, [selectedAgent, selectedDraft, updateSelectedDraft] @@ -4454,6 +4486,7 @@ export function AcpAgentSettings() { codexProviderOptions: important.providerOptions, codexReasoningEffort: important.reasoningEffort, codexSupportsWebsockets: important.supportsWebsockets, + codexSkills: important.skills, })) }, [selectedAgent, selectedDraft, updateSelectedDraft] @@ -4505,6 +4538,7 @@ export function AcpAgentSettings() { codexProviderOptions: synced.providerOptions, codexReasoningEffort: synced.reasoningEffort, codexSupportsWebsockets: synced.supportsWebsockets, + codexSkills: synced.skills, })) return } @@ -4537,6 +4571,7 @@ export function AcpAgentSettings() { codexProviderOptions: synced.providerOptions, codexReasoningEffort: synced.reasoningEffort, codexSupportsWebsockets: synced.supportsWebsockets, + codexSkills: synced.skills, })) }, [selectedAgent, selectedDraft, updateSelectedDraft] @@ -4602,6 +4637,7 @@ export function AcpAgentSettings() { codexProviderOptions: synced.providerOptions, codexReasoningEffort: synced.reasoningEffort, codexSupportsWebsockets: synced.supportsWebsockets, + codexSkills: synced.skills, codexAuthJsonText: nextAuth.authJsonText, codexConfigTomlText: nextToml, })) @@ -4637,6 +4673,39 @@ export function AcpAgentSettings() { codexProviderOptions: synced.providerOptions, codexReasoningEffort: synced.reasoningEffort, codexSupportsWebsockets: synced.supportsWebsockets, + codexSkills: synced.skills, + codexConfigTomlText: nextToml, + })) + }, + [selectedAgent, selectedDraft, updateSelectedDraft] + ) + + const handleCodexSkillsChange = useCallback( + (enabled: boolean) => { + if ( + !selectedAgent || + !selectedDraft || + selectedAgent.agent_type !== "codex" + ) + return + const nextToml = patchCodexConfigTomlText( + selectedDraft.codexConfigTomlText, + { skills: enabled } + ) + const synced = extractCodexImportantValues( + selectedDraft.codexAuthJsonText, + nextToml + ) + updateSelectedDraft((current) => ({ + ...current, + apiBaseUrl: synced.apiBaseUrl, + apiKey: synced.apiKey ?? current.apiKey, + model: synced.model, + codexModelProvider: synced.modelProvider, + codexProviderOptions: synced.providerOptions, + codexReasoningEffort: synced.reasoningEffort, + codexSupportsWebsockets: synced.supportsWebsockets, + codexSkills: synced.skills, codexConfigTomlText: nextToml, })) }, @@ -5196,6 +5265,19 @@ export function AcpAgentSettings() { +
+
+ + +
+
+