JKToolKit.CodexSDK.AgentFramework
0.0.177
dotnet add package JKToolKit.CodexSDK.AgentFramework --version 0.0.177
NuGet\Install-Package JKToolKit.CodexSDK.AgentFramework -Version 0.0.177
<PackageReference Include="JKToolKit.CodexSDK.AgentFramework" Version="0.0.177" />
<PackageVersion Include="JKToolKit.CodexSDK.AgentFramework" Version="0.0.177" />
<PackageReference Include="JKToolKit.CodexSDK.AgentFramework" />
paket add JKToolKit.CodexSDK.AgentFramework --version 0.0.177
#r "nuget: JKToolKit.CodexSDK.AgentFramework, 0.0.177"
#:package JKToolKit.CodexSDK.AgentFramework@0.0.177
#addin nuget:?package=JKToolKit.CodexSDK.AgentFramework&version=0.0.177
#tool nuget:?package=JKToolKit.CodexSDK.AgentFramework&version=0.0.177
Microsoft Agent Framework Adapter
JKToolKit.CodexSDK.AgentFramework makes Codex usable from Microsoft Agent Framework and adapts Agent Framework function tools to Codex app-server dynamic tools.
Use it when you want a Codex-backed AIAgent, or when you already have Agent Framework tools represented as Microsoft.Extensions.AI.AIFunction instances and want Codex app-server to call them through its item/tool/call flow.
NuGet package: JKToolKit.CodexSDK.AgentFramework
Microsoft docs: Agent Framework, function tools, and context providers.
Install
Install the adapter package into the project that owns your Agent Framework tools:
dotnet add package JKToolKit.CodexSDK.AgentFramework
The adapter package depends on JKToolKit.CodexSDK, Microsoft.Agents.AI, Microsoft.Extensions.AI, and Microsoft.Extensions.AI.Abstractions.
Usage
Create a Codex-backed Agent Framework agent:
using System.Text.Json;
using JKToolKit.CodexSDK;
using JKToolKit.CodexSDK.AgentFramework.Agents;
using JKToolKit.CodexSDK.Models;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
AIFunction getWeather = AIFunctionFactory.Create(
(Func<string, string>)(location => $"Weather in {location}: cloudy."),
name: "get_weather",
description: "Gets the weather for a location.");
await using var sdk = CodexSdk.Create(builder =>
{
builder.ConfigureAppServer(options =>
{
options.CodexHomeDirectory = Environment.GetEnvironmentVariable("CODEX_HOME");
});
});
AIAgent agent = sdk
.AsAIAgent(
model: "gpt-5.5",
instructions: "You are a helpful assistant.",
tools: [getWeather]);
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));
You can also use new CodexAgentClient(configureSdk).AsAIAgent(...) when you want the agent to create a fresh SDK facade for each run.
Remote App Servers
The Agent Framework adapter can run against the same remote app-server transports as the core SDK.
For process-bound remote stdio, configure the app-server launch on the SDK builder. This starts codex app-server over SSH or Docker for each agent run:
using JKToolKit.CodexSDK.AppServer;
using JKToolKit.CodexSDK.AgentFramework.Agents;
using Microsoft.Agents.AI;
AIAgent sshAgent = new CodexAgentClient(builder =>
{
builder.ConfigureAppServer(options =>
{
options.Launch = CodexLaunchRemote.SshAppServer(
host: "devbox",
remoteWorkingDirectory: "/home/me/project");
});
}).AsAIAgent(
model: "gpt-5.5",
instructions: "Work in the remote checkout.");
AIAgent dockerAgent = new CodexAgentClient(builder =>
{
builder.ConfigureAppServer(options =>
{
options.Launch = CodexLaunchRemote.DockerAppServer(
container: "codex-dev",
workingDirectory: "/workspace",
codexHome: "/home/codex/.codex");
});
}).AsAIAgent(
model: "gpt-5.5",
instructions: "Work inside the container.");
For detached WebSocket app-servers, start or load a managed remote entry with CodexRemoteAppServerManager, then attach the agent to that entry:
using JKToolKit.CodexSDK.AgentFramework.Agents;
using JKToolKit.CodexSDK.AgentFramework.Agents.Remote;
using JKToolKit.CodexSDK.AppServer.Remote;
using JKToolKit.CodexSDK.AppServer.Remote.Registry;
using JKToolKit.CodexSDK.Models;
using Microsoft.Agents.AI;
var registry = new JsonFileCodexRemoteAppServerRegistry("codexsdk-appservers.json");
var manager = new CodexRemoteAppServerManager(registry);
var entry = await manager.StartDockerContainerWebSocketAsync(new CodexDockerContainerWebSocketAppServerOptions
{
Image = "codex-dev",
WorkingDirectory = "/workspace",
CodexHome = "/home/codex/.codex",
AdditionalDockerRunArguments =
[
"-v", "/host/project:/workspace",
"-v", "/host/.codex:/home/codex/.codex"
]
});
AIAgent remoteAgent = new CodexAgentClient().AsAIAgent(new CodexAIAgentOptions
{
Model = "gpt-5.5",
Cwd = "/workspace",
ApprovalPolicy = CodexApprovalPolicy.Never,
Instructions = "Work in the managed remote app-server.",
RemoteAppServer = new CodexAgentRemoteAppServerOptions
{
Manager = manager,
EntryId = entry.Id
}
});
Console.WriteLine(await remoteAgent.RunAsync("Run pwd and summarize the result."));
await manager.StopAsync(entry.Id, new CodexRemoteStopOptions { RemoveFromRegistry = true });
Each agent run attaches to the registered app-server and disposes only that attachment when the run completes. For SSH entries this closes the local tunnel; for detached Docker and SSH WebSocket entries, the remote app-server keeps running until you stop it through the manager. Dynamic Agent Framework tools still work: the adapter applies the required app-server experimental capability and approval handler to the remote attachment.
Use Agent Framework ChatClientAgentOptions when you already configure agents that way:
AIAgent agent = sdk.AsAIAgent(
model: "gpt-5.5",
options: new ChatClientAgentOptions
{
Name = "CodexAgent",
Description = "Codex with Agent Framework tools.",
ChatOptions = new ChatOptions
{
Instructions = "You are a helpful assistant.",
Tools = [getWeather]
},
ChatHistoryProvider = new InMemoryChatHistoryProvider()
});
Layer Codex-specific defaults on top of Agent Framework options when needed:
var options = new ChatClientAgentOptions
{
Name = "CodexAgent",
ChatOptions = new ChatOptions
{
Instructions = "You are a helpful assistant.",
Tools = [getWeather]
}
}.ToCodexAIAgentOptions(model: "gpt-5.5");
options.Cwd = Environment.CurrentDirectory;
options.ApprovalPolicy = CodexApprovalPolicy.Never;
AIAgent agent = sdk.AsAIAgent(options);
Use sessions for multi-turn conversations:
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("My name is Alice.", session));
Console.WriteLine(await agent.RunAsync("What is my name?", session));
JsonElement serialized = await agent.SerializeSessionAsync(session);
AgentSession resumed = await agent.DeserializeSessionAsync(serialized);
Stream responses:
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Order a pizza."))
{
Console.Write(update.Text);
}
Register the agent with dependency injection when your host resolves AIAgent instances:
using JKToolKit.CodexSDK;
using JKToolKit.CodexSDK.AgentFramework.Agents;
using Microsoft.Agents.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.AI;
static string GetWeather(string location) => $"Weather in {location}: cloudy.";
var services = new ServiceCollection();
services.AddCodexSdk(appServer: options =>
{
options.CodexHomeDirectory = Environment.GetEnvironmentVariable("CODEX_HOME");
});
services.AddKeyedCodexAIAgent("pizza", agent =>
{
agent.Name = "PizzaAgent";
agent.Instructions = "Use the pizza tools for menu, cart, and checkout state.";
agent.Tools = [AIFunctionFactory.Create(GetWeather)];
});
await using var provider = services.BuildServiceProvider();
AIAgent agent = provider.GetRequiredKeyedService<AIAgent>("pizza");
DI registration also accepts native Agent Framework options:
services.AddCodexAIAgent(
new ChatClientAgentOptions
{
Name = "CodexAgent",
ChatOptions = new ChatOptions
{
Instructions = "Use the configured tools when helpful.",
Tools = [AIFunctionFactory.Create(GetWeather)]
}
},
model: "gpt-5.5");
Configure Codex-specific run options:
var runOptions = new CodexAgentRunOptions
{
Cwd = Environment.CurrentDirectory,
ApprovalPolicy = CodexApprovalPolicy.Never,
Sandbox = CodexSandboxMode.ReadOnly
};
Console.WriteLine(await agent.RunAsync("Inspect the current project.", options: runOptions));
Use Agent Framework ChatClientAgentRunOptions when you also want Agent Framework chat options, structured output, reasoning options, or function invocation middleware:
using JKToolKit.CodexSDK.AgentFramework.Agents;
using JKToolKit.CodexSDK.Models;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
var runOptions = new ChatClientAgentRunOptions(new ChatOptions
{
ModelId = "gpt-5.5",
ResponseFormat = ChatResponseFormat.ForJsonSchema<PizzaOrder>(),
Reasoning = new ReasoningOptions
{
Effort = ReasoningEffort.High,
Output = ReasoningOutput.Summary
},
Tools = [getWeather]
}).ConfigureCodex(codex =>
{
codex.Cwd = Environment.CurrentDirectory;
codex.ApprovalPolicy = CodexApprovalPolicy.Never;
});
AgentResponse<PizzaOrder> response =
await agent.RunAsync<PizzaOrder>("Return a small pizza order as JSON.", options: runOptions);
Attach Agent Framework function invocation middleware the same way you would for other AIAgent implementations:
AIAgent middlewareAgent = agent
.AsBuilder()
.Use(async (innerAgent, context, next, cancellationToken) =>
{
context.Arguments["requestSource"] = "codex";
return await next(context, cancellationToken);
})
.Build();
Console.WriteLine(await middlewareAgent.RunAsync(
"What is the weather like in Amsterdam?",
options: new ChatClientAgentRunOptions().ConfigureCodex(codex =>
{
codex.Cwd = Environment.CurrentDirectory;
})));
Provide services for AIFunctionFactory functions that take an IServiceProvider parameter:
AIAgent agent = new CodexAgentClient().AsAIAgent(new CodexAIAgentOptions
{
Instructions = "You are a helpful assistant.",
Tools = [AIFunctionFactory.Create(ReadFromServices)],
FunctionInvocationServices = serviceProvider
});
Handle ApprovalRequiredAIFunction calls with a Codex-side approval callback:
using JKToolKit.CodexSDK.AgentFramework.Tools;
AIFunction deleteFile = new ApprovalRequiredAIFunction(
AIFunctionFactory.Create(DeleteFile, name: "delete_file"));
AIAgent agent = new CodexAgentClient().AsAIAgent(new CodexAIAgentOptions
{
Tools = [deleteFile],
ToolApprovalHandler = (request, cancellationToken) =>
{
return ValueTask.FromResult(
request.ToolName == "delete_file"
? AgentFrameworkToolApprovalResponse.Reject("File deletion is disabled.")
: AgentFrameworkToolApprovalResponse.Approve());
}
});
Add adapter-level safety policy when host approval should not depend only on individual ApprovalRequiredAIFunction wrappers:
using JKToolKit.CodexSDK.AgentFramework.Agents;
using JKToolKit.CodexSDK.AgentFramework.Tools;
using JKToolKit.CodexSDK.Models;
AIAgent agent = new CodexAgentClient().AsAIAgent(new CodexAIAgentOptions
{
Tools = [deleteFile],
Sandbox = CodexSandboxMode.ReadOnly,
SafetyOptions = new CodexAgentSafetyOptions
{
RequireApprovalForAllAgentFrameworkTools = true,
DeniedToolNames = new HashSet<string>(StringComparer.Ordinal)
{
"delete_file"
}
},
ToolApprovalHandler = (request, cancellationToken) =>
{
return ValueTask.FromResult(AgentFrameworkToolApprovalResponse.Reject("Disabled by host policy."));
}
});
Agent Framework runtime context is available inside tools and function middleware:
static string RememberTopic(string topic)
{
var session = AIAgent.CurrentRunContext?.Session;
if (session is null)
{
return "No session is available.";
}
session.StateBag.SetValue("topic", topic);
return $"Remembered {topic}.";
}
AIAgent agent = new CodexAgentClient().AsAIAgent(
instructions: "Remember useful project context.",
tools: [AIFunctionFactory.Create(RememberTopic)]);
Use Agent Framework context providers when you want the normal memory/RAG pipeline to enrich Codex runs:
AIContextProvider projectContextProvider = new MyProjectContextProvider();
AIAgent agent = new CodexAgentClient().AsAIAgent(new CodexAIAgentOptions
{
Instructions = "Use project context when it is relevant.",
AIContextProviders = [projectContextProvider],
ChatHistoryProvider = new InMemoryChatHistoryProvider(),
Tools = [AIFunctionFactory.Create(GetWeather)]
});
Compose or host the Codex agent with Agent Framework packages that accept AIAgent:
// dotnet add package Microsoft.Agents.AI.Workflows --prerelease
using Microsoft.Agents.AI.Workflows;
var workflow = new WorkflowBuilder(codexAgent)
.AddEdge(codexAgent, reviewerAgent)
.Build();
// dotnet add package Microsoft.Agents.AI.Hosting.OpenAI --prerelease
using Microsoft.Agents.AI.Hosting;
app.MapOpenAIChatCompletions(codexAgent);
app.MapOpenAIResponses(codexAgent);
// dotnet add package Microsoft.Agents.AI.Hosting.A2A.AspNetCore --prerelease
using Microsoft.Agents.AI.Hosting;
app.MapA2A(codexAgent, path: "/a2a/codex");
The lower-level adapter remains available when you want to manually wire Codex app-server:
using JKToolKit.CodexSDK;
using JKToolKit.CodexSDK.AgentFramework.Tools;
using JKToolKit.CodexSDK.AppServer;
using JKToolKit.CodexSDK.Models;
using Microsoft.Extensions.AI;
AIFunction getWeather = AIFunctionFactory.Create(
(Func<string, string>)(location => $"Weather in {location}: cloudy."),
name: "get_weather",
description: "Gets the weather for a location.");
var agentFrameworkTools = AgentFrameworkCodexToolAdapter.Create([getWeather]);
await using var sdk = CodexSdk.Create(builder =>
{
builder.ConfigureAppServer(o =>
{
o.ExperimentalApi = true;
o.ApprovalHandler = agentFrameworkTools.ApprovalHandler;
});
});
await using var codex = await sdk.AppServer.StartAsync();
var thread = await codex.StartThreadAsync(new ThreadStartOptions
{
Cwd = Environment.CurrentDirectory,
Sandbox = CodexSandboxMode.ReadOnly,
ApprovalPolicy = CodexApprovalPolicy.Never,
DynamicTools = agentFrameworkTools.DynamicTools
});
await using var turn = await codex.StartTurnAsync(thread.Id, new TurnStartOptions
{
Input = [TurnInputItem.Text("Use the weather tool for Vienna.")]
});
Run the end-to-end pizza demo:
dotnet run --project src/JKToolKit.CodexSDK.Demo -- agent-framework-function-calling --repo "<repo-path>"
The demo inherits the default model from your Codex configuration. To force a model, pass --model <MODEL>, for example:
dotnet run --project src/JKToolKit.CodexSDK.Demo -- agent-framework-function-calling --model gpt-5.5
Notes
CodexAgentClient().AsAIAgent(...)returns a normal Agent FrameworkAIAgent, so Agent Framework middleware, workflows,RunAsync<T>,RunStreamingAsync, andAIAgent.AsAIFunction(...)can be used on top of it.CodexSdk.AsAIAgent(...)adapts an existing SDK facade and leaves its lifetime with the caller. This is the closest match to provider APIs such asAIProjectClient.AsAIAgent(...).- Workflow and hosting packages are intentionally not referenced by this adapter. Add
Microsoft.Agents.AI.Workflows,Microsoft.Agents.AI.Hosting.OpenAI,Microsoft.Agents.AI.Hosting.A2A.AspNetCore, or another Agent Framework host package in the application that composes or exposes the Codex agent. ChatClientAgentOptionsmaps its metadata, defaultChatOptions,ChatHistoryProvider, andAIContextProvidersinto the Codex agent.CodexAIAgentexposesChatOptions,Instructions,ChatHistoryProvider, andAIContextProvidersfor the same native inspection style. Chat-client pipeline flags such asUseProvidedChatClientAsIsare specific toIChatClientagents and are not used by Codex.ChatClientAgentOptions.ToCodexAIAgentOptions(...)keeps existing Agent Framework configuration reusable when you also need Codex-specific agent defaults.CodexAgentSessionstores the backing Codex thread id, Agent Framework session state bag, dynamic tool schema hash, model, working directory, approval policy, sandbox mode, and thread creation timestamp. Serialize and deserialize the session through the Agent Framework APIs to resume the same Codex thread later.- When a run does not pass a session, the Codex agent creates one and updates
AIAgent.CurrentRunContextso Agent Framework tools can still readCurrentRunContext.SessionandCurrentRunContext.RunOptions. AIContextProvidersrun before Codex starts the turn. Provider instructions and tools are merged intoChatOptions; provider messages are sent as turn input; providers are notified after success or failure.ChatHistoryProvideris opt-in because Codex threads already maintain conversation state. Add one only when you explicitly want Agent Framework-managed history or memory enrichment.- Codex dynamic tools are currently behind the app-server experimental API capability. The native
AIAgentsurface enables it automatically when tools are present. - Tool names are the
AIFunction.Namevalues supplied by Agent Framework. - Agent Framework tools must currently be
AIFunctioninstances. Hosted Agent Framework tools such as provider-native code interpreter, file search, or web search are not translated to Codex dynamic tools. - Per-run tools and context-provider tools can be used when creating a new Codex thread. When resuming a session, the adapter compares the current dynamic tool schema hash with the hash captured on the session and rejects changed tool sets because Codex app-server dynamic tools are thread-scoped.
- Function invocation middleware is supported for default tools and
ChatClientAgentRunOptionstools throughChatClientFactory. When combining it with Codex-specific settings, attach those settings withConfigureCodex(...); Agent Framework's function middleware only accepts no options, baseAgentRunOptions, orChatClientAgentRunOptions. ChatClientAgentRunOptions.ChatClientFactoryis used only to apply Agent Framework option/tool transformations before Codex runs. Codex is not anIChatClient, so chat-client middleware cannot observe the raw Codex model request or response.ApprovalRequiredAIFunctionis supported throughtoolApprovalHandler. Codex dynamic tool calls are synchronous, so the adapter does not use Agent FrameworkFunctionApprovalRequestContent/FunctionApprovalResponseContentround trips. Without an approval handler, approval-required functions are rejected before invocation.CodexAgentSafetyOptionscan require host approval for all tools, require approval through a predicate, allow or deny exact tool names, redact tool exception details, and provide a default sandbox when no run or agent sandbox is configured.- Failed dynamic tool calls return
success: falsewith a JSON error envelope in the text content item. The envelope includeserror_code,error_message,is_retryable,tool_name,call_id, and opt-in exception diagnostics whenRedactToolExceptionDetailsisfalse. - Codex dynamic tool output currently supports only
inputTextandinputImage. The adapter mapsTextContent, imageDataContent, imageUriContent, and multipleAIContentresults to those item types; JSON and other structured values are serialized as text because the current Codex app-server protocol has no JSON output content item.
Capability Matrix
| Agent Framework capability | Status | Notes |
|---|---|---|
AIAgent execution |
Supported | Codex runs behind a native AIAgent implementation. |
| Sessions | Supported | Session JSON includes Codex thread id plus diagnostic thread metadata. |
AIFunction tools |
Supported | Functions are exposed as Codex dynamic tools. |
| Approval-required functions | Supported, synchronous | Uses a Codex-side callback rather than Agent Framework approval request/response content. |
| Policy-level tool approval | Supported | CodexAgentSafetyOptions can require approval for all tools or by predicate. |
| Tool allow/deny policy | Supported | Exact Agent Framework tool-name allow and deny sets. |
| Tool result content | Approximated | Codex supports text and image output items; other structured content is serialized to text. |
| Structured tool errors | Approximated | Error metadata is returned as JSON text inside the Codex dynamic-tool response. |
| Per-run tools on resumed sessions | Partially supported | Allowed only when the dynamic tool schema hash matches the thread-created schema. |
| Agent/chat-client middleware fidelity | Approximated | Chat-client middleware is used for option/tool transformation, not raw Codex request/response observation. |
| Rich Codex event streaming | Limited | Assistant text deltas and errors are mapped; command/file/tool lifecycle events are not yet first-class Agent Framework content. |
| Workflow checkpointing | Limited | Store serialized CodexAgentSession in workflow checkpoint metadata when composing workflows. |
| MCP/A2A/hosting packages | Application-owned | The adapter does not reference hosting packages; compose them in the app that owns the agent. |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- JKToolKit.CodexSDK (>= 0.0.177)
- Microsoft.Agents.AI (>= 1.3.0)
- Microsoft.Extensions.AI (>= 10.5.1)
- Microsoft.Extensions.AI.Abstractions (>= 10.5.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.