Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions CodexSharpSDK.Tests/Unit/CodexExecTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ public async Task BuildCommandArgs_MapsExtendedCliFlags()
Color = ExecOutputColor.Never,
ProgressCursor = true,
OutputLastMessageFile = "/tmp/last-message.txt",
EnabledFeatures = ["multi_agent", "unified_exec"],
DisabledFeatures = ["steer"],
EnabledFeatures = [CodexFeatureFlags.MultiAgent, CodexFeatureFlags.UnifiedExec],
DisabledFeatures = [CodexFeatureFlags.Steer],
AdditionalCliArguments = ["--some-future-flag", "custom-value"],
});

Expand All @@ -144,13 +144,38 @@ public async Task BuildCommandArgs_MapsExtendedCliFlags()
await Assert.That(commandArgs.Contains("--ephemeral")).IsTrue();
await Assert.That(commandArgs.Contains("--progress-cursor")).IsTrue();

await Assert.That(CollectFlagValues(commandArgs, "--enable")).IsEquivalentTo(["multi_agent", "unified_exec"]);
await Assert.That(CollectFlagValues(commandArgs, "--disable")).IsEquivalentTo(["steer"]);
await Assert.That(CollectFlagValues(commandArgs, "--enable")).IsEquivalentTo([CodexFeatureFlags.MultiAgent, CodexFeatureFlags.UnifiedExec]);
await Assert.That(CollectFlagValues(commandArgs, "--disable")).IsEquivalentTo([CodexFeatureFlags.Steer]);

await Assert.That(commandArgs.Contains("--some-future-flag")).IsTrue();
await Assert.That(commandArgs.Contains("custom-value")).IsTrue();
}

[Test]
public async Task BuildCommandArgs_MapsNewUpstreamFeatureFlags()
{
var exec = new CodexExec("codex", null, null);

var commandArgs = exec.BuildCommandArgs(new CodexExecArgs
{
Input = "test",
EnabledFeatures =
[
CodexFeatureFlags.GuardianApproval,
CodexFeatureFlags.RequestPermissionsTool,
CodexFeatureFlags.ToolCallMcpElicitation,
],
});

await Assert.That(CollectFlagValues(commandArgs, "--enable"))
.IsEquivalentTo(
[
CodexFeatureFlags.GuardianApproval,
CodexFeatureFlags.RequestPermissionsTool,
CodexFeatureFlags.ToolCallMcpElicitation,
]);
}

[Test]
public async Task BuildCommandArgs_KeepsConfiguredWebSearchWhenThreadOverridesMissing()
{
Expand Down
94 changes: 94 additions & 0 deletions CodexSharpSDK/Models/CodexFeatureFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
namespace ManagedCode.CodexSharpSDK.Models;

/// <summary>
/// Known Codex CLI feature flag identifiers for use with
/// <see cref="ManagedCode.CodexSharpSDK.Client.ThreadOptions.EnabledFeatures"/> and
/// <see cref="ManagedCode.CodexSharpSDK.Client.ThreadOptions.DisabledFeatures"/>.
/// Feature flags are passed to the CLI via <c>--enable</c> and <c>--disable</c>.
/// </summary>
public static class CodexFeatureFlags
{
// --- Approval / sandbox ---
public const string GuardianApproval = "guardian_approval";
public const string RequestPermissions = "request_permissions";
public const string RequestPermissionsTool = "request_permissions_tool";

// --- Execution ---
public const string UnifiedExec = "unified_exec";
public const string ExperimentalUseUnifiedExecTool = "experimental_use_unified_exec_tool";
public const string ShellTool = "shell_tool";
public const string ShellSnapshot = "shell_snapshot";
public const string ShellZshFork = "shell_zsh_fork";
public const string UseLinuxSandboxBwrap = "use_linux_sandbox_bwrap";

// --- Apply patch ---
public const string ApplyPatchFreeform = "apply_patch_freeform";
public const string ExperimentalUseFreeformApplyPatch = "experimental_use_freeform_apply_patch";
public const string IncludeApplyPatchTool = "include_apply_patch_tool";

// --- MCP / tool calling ---
public const string ToolCallMcpElicitation = "tool_call_mcp_elicitation";

// --- Multi-agent / collaboration ---
public const string MultiAgent = "multi_agent";
public const string Collab = "collab";
public const string CollaborationModes = "collaboration_modes";
public const string ChildAgentsMd = "child_agents_md";
public const string Steer = "steer";

// --- Search ---
public const string SearchTool = "search_tool";
public const string WebSearch = "web_search";
public const string WebSearchCached = "web_search_cached";
public const string WebSearchRequest = "web_search_request";

// --- Memory ---
public const string Memories = "memories";
public const string MemoryTool = "memory_tool";

// --- Image ---
public const string ImageGeneration = "image_generation";
public const string ImageDetailOriginal = "image_detail_original";

// --- Plugins / apps ---
public const string Plugins = "plugins";
public const string Apps = "apps";
public const string AppsMcpGateway = "apps_mcp_gateway";
public const string Connectors = "connectors";

// --- JS REPL ---
public const string JsRepl = "js_repl";
public const string JsReplToolsOnly = "js_repl_tools_only";

// --- Realtime ---
public const string RealtimeConversation = "realtime_conversation";
public const string VoiceTranscription = "voice_transcription";

// --- Windows sandbox ---
public const string ExperimentalWindowsSandbox = "experimental_windows_sandbox";
public const string EnableExperimentalWindowsSandbox = "enable_experimental_windows_sandbox";
public const string ElevatedWindowsSandbox = "elevated_windows_sandbox";
public const string PowershellUtf8 = "powershell_utf8";

// --- Storage / DB ---
public const string Sqlite = "sqlite";
public const string RemoteModels = "remote_models";

// --- Networking / transport ---
public const string ResponsesWebsockets = "responses_websockets";
public const string ResponsesWebsocketsV2 = "responses_websockets_v2";
public const string EnableRequestCompression = "enable_request_compression";

// --- Misc ---
public const string FastMode = "fast_mode";
public const string Artifact = "artifact";
public const string RequestRule = "request_rule";
public const string RuntimeMetrics = "runtime_metrics";
public const string Undo = "undo";
public const string Personality = "personality";
public const string SkillEnvVarDependencyPrompt = "skill_env_var_dependency_prompt";
public const string SkillMcpDependencyInstall = "skill_mcp_dependency_install";
public const string CodexGitCommit = "codex_git_commit";
public const string DefaultModeRequestUserInput = "default_mode_request_user_input";
public const string PreventIdleSleep = "prevent_idle_sleep";
}
224 changes: 224 additions & 0 deletions docs/Features/feature-flags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# Feature: Feature Flags

Links:
Architecture: [docs/Architecture/Overview.md](../Architecture/Overview.md)
Modules: [CodexSharpSDK/Models/CodexFeatureFlags.cs](../../CodexSharpSDK/Models/CodexFeatureFlags.cs), [CodexSharpSDK/Client/ThreadOptions.cs](../../CodexSharpSDK/Client/ThreadOptions.cs)
ADRs: [001-codex-cli-wrapper.md](../ADR/001-codex-cli-wrapper.md)

---

## Purpose

Expose Codex CLI feature flags through the SDK so consumers can enable or disable experimental and optional capabilities without relying on magic string literals.

---

## Scope

### In scope

- `CodexFeatureFlags` constants class mirroring the `features` block from `codex-rs/core/config.schema.json`
- `ThreadOptions.EnabledFeatures` / `ThreadOptions.DisabledFeatures` accepting these constants
- `CodexExecArgs.EnabledFeatures` / `CodexExecArgs.DisabledFeatures` for low-level exec control

### Out of scope

- Deciding which flags to enable at runtime (consumer responsibility)
- Feature flag semantics (controlled entirely by the Codex CLI)

---

## Business Rules

- Feature flag identifiers must match the Codex CLI's `config.schema.json` exactly; never invent new names.
- SDK ships constants for every known flag at the pinned submodule commit so consumers have compile-time safety.
- New flags added by upstream syncs must be added to `CodexFeatureFlags` in the same PR that updates the submodule.
- Raw string literals for feature flags are not permitted in SDK production code or tests; always use `CodexFeatureFlags` constants.

---

## Usage

```csharp
using ManagedCode.CodexSharpSDK.Models;

var thread = client.StartThread(new ThreadOptions
{
// Enable guardian approval and request-permissions tool (added in upstream sync 06f82c1)
EnabledFeatures = [
CodexFeatureFlags.GuardianApproval,
CodexFeatureFlags.RequestPermissionsTool,
],
// Disable MCP elicitation
DisabledFeatures = [
CodexFeatureFlags.ToolCallMcpElicitation,
],
});
```

---

## Known Feature Flags

Sourced from `submodules/openai-codex/codex-rs/core/config.schema.json` at pinned commit.

### Approval / sandbox

| Constant | CLI string | Notes |
|---|---|---|
| `GuardianApproval` | `guardian_approval` | Guardian-based approval gate |
| `RequestPermissions` | `request_permissions` | Permission request flow |
| `RequestPermissionsTool` | `request_permissions_tool` | Request-permissions as a tool call |

### Execution

| Constant | CLI string |
|---|---|
| `UnifiedExec` | `unified_exec` |
| `ExperimentalUseUnifiedExecTool` | `experimental_use_unified_exec_tool` |
| `ShellTool` | `shell_tool` |
| `ShellSnapshot` | `shell_snapshot` |
| `ShellZshFork` | `shell_zsh_fork` |
| `UseLinuxSandboxBwrap` | `use_linux_sandbox_bwrap` |

### Apply patch

| Constant | CLI string |
|---|---|
| `ApplyPatchFreeform` | `apply_patch_freeform` |
| `ExperimentalUseFreeformApplyPatch` | `experimental_use_freeform_apply_patch` |
| `IncludeApplyPatchTool` | `include_apply_patch_tool` |

### MCP / tool calling

| Constant | CLI string | Notes |
|---|---|---|
| `ToolCallMcpElicitation` | `tool_call_mcp_elicitation` | MCP-style elicitation for tool approvals |

### Multi-agent / collaboration

| Constant | CLI string |
|---|---|
| `MultiAgent` | `multi_agent` |
| `Collab` | `collab` |
| `CollaborationModes` | `collaboration_modes` |
| `ChildAgentsMd` | `child_agents_md` |
| `Steer` | `steer` |

### Search

| Constant | CLI string |
|---|---|
| `SearchTool` | `search_tool` |
| `WebSearch` | `web_search` |
| `WebSearchCached` | `web_search_cached` |
| `WebSearchRequest` | `web_search_request` |

### Memory

| Constant | CLI string |
|---|---|
| `Memories` | `memories` |
| `MemoryTool` | `memory_tool` |

### Image

| Constant | CLI string |
|---|---|
| `ImageGeneration` | `image_generation` |
| `ImageDetailOriginal` | `image_detail_original` |

### Plugins / apps

| Constant | CLI string |
|---|---|
| `Plugins` | `plugins` |
| `Apps` | `apps` |
| `AppsMcpGateway` | `apps_mcp_gateway` |
| `Connectors` | `connectors` |

### JS REPL

| Constant | CLI string |
|---|---|
| `JsRepl` | `js_repl` |
| `JsReplToolsOnly` | `js_repl_tools_only` |

### Realtime

| Constant | CLI string |
|---|---|
| `RealtimeConversation` | `realtime_conversation` |
| `VoiceTranscription` | `voice_transcription` |

### Windows sandbox

| Constant | CLI string |
|---|---|
| `ExperimentalWindowsSandbox` | `experimental_windows_sandbox` |
| `EnableExperimentalWindowsSandbox` | `enable_experimental_windows_sandbox` |
| `ElevatedWindowsSandbox` | `elevated_windows_sandbox` |
| `PowershellUtf8` | `powershell_utf8` |

### Storage / DB

| Constant | CLI string |
|---|---|
| `Sqlite` | `sqlite` |
| `RemoteModels` | `remote_models` |

### Networking / transport

| Constant | CLI string |
|---|---|
| `ResponsesWebsockets` | `responses_websockets` |
| `ResponsesWebsocketsV2` | `responses_websockets_v2` |
| `EnableRequestCompression` | `enable_request_compression` |

### Miscellaneous

| Constant | CLI string |
|---|---|
| `FastMode` | `fast_mode` |
| `Artifact` | `artifact` |
| `RequestRule` | `request_rule` |
| `RuntimeMetrics` | `runtime_metrics` |
| `Undo` | `undo` |
| `Personality` | `personality` |
| `SkillEnvVarDependencyPrompt` | `skill_env_var_dependency_prompt` |
| `SkillMcpDependencyInstall` | `skill_mcp_dependency_install` |
| `CodexGitCommit` | `codex_git_commit` |
| `DefaultModeRequestUserInput` | `default_mode_request_user_input` |
| `PreventIdleSleep` | `prevent_idle_sleep` |

---

## Diagrams

```mermaid
flowchart LR
Consumer["Consumer code"] -->|"EnabledFeatures / DisabledFeatures"| ThreadOptions
ThreadOptions --> CodexExec["CodexExec.BuildCommandArgs()"]
CodexExec -->|"--enable guardian_approval\n--disable steer"| CLI["codex exec CLI"]
CodexFeatureFlags["CodexFeatureFlags constants"] -.->|"type-safe identifiers"| Consumer
```

---

## Upstream sync

Feature flags are kept in sync with the pinned `submodules/openai-codex` submodule.
When the submodule is updated, compare `codex-rs/core/config.schema.json` `.properties.features.properties` against `CodexFeatureFlags` and add any new entries.

Flags added by upstream sync `6638558b88 → 06f82c123c` (2026-03-08):
- `guardian_approval` — guardian approval MVP
- `request_permissions_tool` — request permissions as a tool call
- `tool_call_mcp_elicitation` — MCP-style elicitation for tool-call approvals

---

## Definition of Done

- `CodexFeatureFlags` constants match all `features` entries in pinned `config.schema.json`.
- Tests in `CodexExecTests` use `CodexFeatureFlags` constants (no raw string literals).
- Documentation table is complete and up to date.
2 changes: 1 addition & 1 deletion submodules/openai-codex
Submodule openai-codex updated 390 files