Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
1dfcc4d
feat(sdk-native): add codex provider examples
ScriptedAlchemy Nov 5, 2025
6c4c6d5
feat(native): Complete CodexProvider implementation for OpenAI Agents…
ScriptedAlchemy Nov 5, 2025
936e560
fix(examples): Fix example imports and add mock server support
ScriptedAlchemy Nov 5, 2025
eb4f5d4
fix: Fix JSON schema validation and remove mock server
ScriptedAlchemy Nov 5, 2025
df3454b
fix(examples): Add proper multi-modal image handling to agents-with-t…
ScriptedAlchemy Nov 5, 2025
a2c5bbc
fix: Unskip test and fix example hanging issues
ScriptedAlchemy Nov 5, 2025
19d99c7
fix: Parallel subagent fixes for all examples
ScriptedAlchemy Nov 6, 2025
70beacd
fix: Remove timeout workarounds from automatic-tool-registration
ScriptedAlchemy Nov 6, 2025
ba64aa7
chore(ci): remove CLA workflow
ScriptedAlchemy Nov 6, 2025
65797a4
build: update pnpm lock
ScriptedAlchemy Nov 6, 2025
37e990d
build: include sdk optional deps in pnpm lock
ScriptedAlchemy Nov 6, 2025
d29f347
ci: skip optional deps during pnpm install
ScriptedAlchemy Nov 6, 2025
f10068f
ci: disable optional deps during pnpm install
ScriptedAlchemy Nov 6, 2025
9085544
Add renovate.json
renovate[bot] Nov 5, 2025
165aec4
ci: cancel superseded workflow runs
ScriptedAlchemy Nov 6, 2025
a76f07f
build: add sdk release helper scripts
ScriptedAlchemy Nov 6, 2025
de04e9e
fix: Comprehensive audit fixes for production readiness
ScriptedAlchemy Nov 6, 2025
7268aa3
chore: refresh pnpm lockfile
ScriptedAlchemy Nov 6, 2025
087bc3b
build: ensure sdk build script runs native + TS
ScriptedAlchemy Nov 6, 2025
8205995
feat(native): add fullAuto option threading
ScriptedAlchemy Nov 6, 2025
227a162
feat(native): support fullAuto threading option
ScriptedAlchemy Nov 6, 2025
bbfd003
fix: Improve process exit handling in example scripts
ScriptedAlchemy Nov 6, 2025
690b0b7
fix: Update example imports to use .ts extension
ScriptedAlchemy Nov 6, 2025
5299362
feat: Add fullAuto and review mode parameters to native binding
ScriptedAlchemy Nov 6, 2025
9537d87
feat: Add review mode support to CodexExec
ScriptedAlchemy Nov 6, 2025
40afe7b
feat: Add review options types and builder
ScriptedAlchemy Nov 6, 2025
f8b6541
feat: Add review methods to Codex class
ScriptedAlchemy Nov 6, 2025
c264598
feat: Export review types from package
ScriptedAlchemy Nov 6, 2025
638298c
feat(native): expose review workflow
ScriptedAlchemy Nov 6, 2025
a5dfbb2
feat: Add review method implementation and example
ScriptedAlchemy Nov 6, 2025
24be95c
feat(native): add review binding without exec CLI
ScriptedAlchemy Nov 6, 2025
bd8b35d
test(native): stub review stream
ScriptedAlchemy Nov 6, 2025
3b7a12a
fix: Replace broken agents-handoffs tests with mock-based implementation
ScriptedAlchemy Nov 6, 2025
746be10
test(native): remove all stubbed/mocked review tests
ScriptedAlchemy Nov 6, 2025
c1eb350
fix: Fix clippy warnings and update codespell config
ScriptedAlchemy Nov 6, 2025
9dc2913
Merge branch 'main' into feature/codex-provider-examples
ScriptedAlchemy Nov 6, 2025
255d746
chore: update native sdk examples
ScriptedAlchemy Nov 6, 2025
8eb5843
chore: refresh provider example sources
ScriptedAlchemy Nov 6, 2025
952d66d
Use nightly rustfmt for imports granularity
ScriptedAlchemy Nov 6, 2025
ca1b23d
fix(sdk/native): fix review flow hanging and improve build scripts
ScriptedAlchemy Nov 6, 2025
5df266f
chore: regenerate pnpm lockfile
ScriptedAlchemy Nov 6, 2025
5a888ff
feat(sdk/native): add comprehensive Rust tests
ScriptedAlchemy Nov 6, 2025
0a07e6c
chore: remove TESTS_README.md documentation file
ScriptedAlchemy Nov 6, 2025
7362982
build: allow pnpm optional deps
ScriptedAlchemy Nov 6, 2025
43a0fae
chore: format renovate config
ScriptedAlchemy Nov 6, 2025
70a2c22
ci: add concurrency groups to workflows to auto-cancel outdated runs
ScriptedAlchemy Nov 6, 2025
b7b25f1
ci: build native sdk in sdk workflow
ScriptedAlchemy Nov 6, 2025
dafa04a
feat(sdk/native): Add tool override, raw events, and schema optimization
ScriptedAlchemy Nov 6, 2025
857096a
fix(sdk/native): Fix compilation errors in Linux sandbox init
ScriptedAlchemy Nov 6, 2025
2f94ed3
feat(sdk/native): Organize examples into categorized subdirectories
ScriptedAlchemy Nov 6, 2025
5a9077c
fix(sdk/native): Update example import paths for new directory structure
ScriptedAlchemy Nov 6, 2025
edc95a7
fix(sdk/native): Fix remaining import paths in organized examples
ScriptedAlchemy Nov 6, 2025
76b1199
fix(codex-rs/exec): Collapse nested if statement per clippy lint
ScriptedAlchemy Nov 6, 2025
7aee080
fix(sdk/native): Remove unused io_to_napi and fix clippy warnings
ScriptedAlchemy Nov 6, 2025
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
1 change: 1 addition & 0 deletions .codespellignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
iTerm
trys
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = .git*,vendor,*-lock.yaml,*.lock,.codespellrc,*test.ts,*.jsonl,frame*.txt
skip = .git*,vendor,*-lock.yaml,*.lock,.codespellrc,.codespellignore,*test.ts,*.jsonl,frame*.txt,*/target/*,*/node_modules/*,*/.pnpm-store/*,*/dist/*,*/.yarn/*,package-lock.json,*.map
check-hidden = true
ignore-regex = ^\s*"image/\S+": ".*|\b(afterAll)\b
ignore-words-list = ratatui,ser
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ jobs:
node-version: 22

- name: Install dependencies
run: npm_config_optional=false pnpm install --frozen-lockfile
run: pnpm install --frozen-lockfile

# stage_npm_packages.py requires DotSlash when staging releases.
- uses: facebook/install-dotslash@v2

- name: Stage npm package
if: github.repository == 'openai/codex-native'
id: stage_npm_package
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -50,6 +51,7 @@ jobs:
echo "pack_output=$PACK_OUTPUT" >> "$GITHUB_OUTPUT"

- name: Upload staged npm package artifact
if: github.repository == 'openai/codex-native'
uses: actions/upload-artifact@v5
with:
name: codex-npm-staging
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ on:
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/issue-deduplicator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
- opened
- labeled

concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
cancel-in-progress: true

jobs:
gather-duplicates:
name: Identify potential duplicates
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/issue-labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
- opened
- labeled

concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
cancel-in-progress: true

jobs:
gather-labels:
name: Generate label suggestions
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
- main
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

# CI builds in debug (dev) for faster signal.

jobs:
Expand Down
19 changes: 15 additions & 4 deletions .github/workflows/sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,30 @@ jobs:
cache: pnpm

- uses: dtolnay/rust-toolchain@1.90
with:
components: rustfmt, clippy

- name: build codex
run: cargo build --bin codex
working-directory: codex-rs

- name: Install dependencies
run: npm_config_optional=false pnpm install --frozen-lockfile
run: pnpm install --frozen-lockfile

- name: Build SDK packages
- name: Build TypeScript SDK packages
run: pnpm -r --filter ./sdk/typescript run build

- name: Lint SDK packages
- name: Build native SDK package
run: pnpm -r --filter ./sdk/native run build

- name: Lint TypeScript SDK packages
run: pnpm -r --filter ./sdk/typescript run lint

- name: Test SDK packages
- name: Lint native SDK package
run: pnpm -r --filter ./sdk/native run lint

- name: Test TypeScript SDK packages
run: pnpm -r --filter ./sdk/typescript run test

- name: Test native SDK package
run: pnpm -r --filter ./sdk/native run test
59 changes: 49 additions & 10 deletions codex-rs/core/src/tools/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::time::Duration;

use async_trait::async_trait;
use codex_protocol::models::ResponseInputItem;
use tracing::error;
use tracing::warn;

use crate::client_common::tools::ToolSpec;
Expand Down Expand Up @@ -45,19 +46,34 @@ fn pending_external_tools() -> &'static Mutex<Vec<ExternalToolRegistration>> {
}

pub fn set_pending_external_tools(tools: Vec<ExternalToolRegistration>) {
let mut guard = pending_external_tools()
.lock()
.expect("pending external tools mutex poisoned");
*guard = tools;
match pending_external_tools().lock() {
Ok(mut guard) => {
*guard = tools;
}
Err(err) => {
error!(
error = ?err,
"failed to acquire pending external tools mutex; pending tools unchanged"
);
}
}
}

pub fn take_pending_external_tools() -> Vec<ExternalToolRegistration> {
let mut guard = pending_external_tools()
.lock()
.expect("pending external tools mutex poisoned");
let tools = guard.clone();
guard.clear();
tools
match pending_external_tools().lock() {
Ok(mut guard) => {
let tools = guard.clone();
guard.clear();
tools
}
Err(err) => {
error!(
error = ?err,
"failed to acquire pending external tools mutex; returning empty list"
);
Vec::new()
}
}
}

#[async_trait]
Expand Down Expand Up @@ -196,6 +212,12 @@ pub struct ToolRegistryBuilder {
specs: Vec<ConfiguredToolSpec>,
}

impl Default for ToolRegistryBuilder {
fn default() -> Self {
Self::new()
}
}

impl ToolRegistryBuilder {
pub fn new() -> Self {
Self {
Expand All @@ -217,6 +239,23 @@ impl ToolRegistryBuilder {
.push(ConfiguredToolSpec::new(spec, supports_parallel_tool_calls));
}

pub fn upsert_spec_with_parallel_support(
&mut self,
spec: ToolSpec,
supports_parallel_tool_calls: bool,
) {
let spec_name = spec.name().to_string();
if let Some(existing) = self
.specs
.iter_mut()
.find(|configured| configured.spec.name() == spec_name)
{
*existing = ConfiguredToolSpec::new(spec, supports_parallel_tool_calls);
} else {
self.push_spec_with_parallel_support(spec, supports_parallel_tool_calls);
}
}

pub fn register_handler(&mut self, name: impl Into<String>, handler: Arc<dyn ToolHandler>) {
let name = name.into();
if self
Expand Down
97 changes: 92 additions & 5 deletions codex-rs/core/src/tools/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ impl ToolRouter {
) -> Self {
let mut builder = build_specs(config, mcp_tools);
for tool in external_tools {
builder.push_spec_with_parallel_support(
tool.spec.clone(),
tool.supports_parallel_tool_calls,
);
builder.register_handler(tool.spec.name(), Arc::clone(&tool.handler));
let spec = tool.spec.clone();
let supports_parallel = tool.supports_parallel_tool_calls;
let spec_name = spec.name().to_string();
builder.upsert_spec_with_parallel_support(spec, supports_parallel);
builder.register_handler(spec_name, Arc::clone(&tool.handler));
}
let (specs, registry) = builder.build();

Expand Down Expand Up @@ -196,3 +196,90 @@ impl ToolRouter {
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::client_common::tools::ToolSpec;
use crate::features::Features;
use crate::model_family::find_family_for_model;
use crate::tools::context::ToolOutput;
use crate::tools::registry::ExternalToolRegistration;
use crate::tools::registry::ToolHandler;
use crate::tools::registry::ToolKind;
use crate::tools::spec::ToolsConfigParams;
use crate::tools::spec::create_function_tool_spec_from_schema;
use async_trait::async_trait;
use serde_json::json;
use std::sync::Arc;

struct DummyHandler;

#[async_trait]
impl ToolHandler for DummyHandler {
fn kind(&self) -> ToolKind {
ToolKind::Function
}

async fn handle(
&self,
_invocation: ToolInvocation,
) -> Result<ToolOutput, FunctionCallError> {
Err(FunctionCallError::Fatal("not implemented".to_string()))
}
}

#[test]
fn external_tool_can_override_builtin_spec() {
let model_family = find_family_for_model("codex-mini-latest")
.expect("valid model family for codex-mini-latest");
let features = Features::with_defaults();
let config = ToolsConfig::new(&ToolsConfigParams {
model_family: &model_family,
features: &features,
});

let custom_spec = create_function_tool_spec_from_schema(
"list_mcp_resources".to_string(),
Some("custom override".to_string()),
json!({
"type": "object",
"properties": {
"hint": { "type": "string" }
},
"required": ["hint"],
"additionalProperties": false
}),
true,
)
.expect("schema converts");

let external_tools = vec![ExternalToolRegistration {
spec: custom_spec,
handler: Arc::new(DummyHandler),
supports_parallel_tool_calls: false,
}];

let router = ToolRouter::from_config(&config, None, &external_tools);
let specs = router.specs();

let overrides: Vec<_> = specs
.iter()
.filter(|spec| spec.name() == "list_mcp_resources")
.collect();
assert_eq!(overrides.len(), 1, "override should replace built-in spec");

match overrides[0] {
ToolSpec::Function(tool) => {
assert_eq!(tool.description, "custom override");
assert!(tool.strict);
}
_ => panic!("expected function tool"),
}

assert!(
!router.tool_supports_parallel("list_mcp_resources"),
"override should control parallel flag"
);
}
}
11 changes: 10 additions & 1 deletion codex-rs/exec/src/event_processor_with_jsonl_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::exec_events::McpToolCallItemResult;
use crate::exec_events::McpToolCallStatus;
use crate::exec_events::PatchApplyStatus;
use crate::exec_events::PatchChangeKind;
use crate::exec_events::RawEvent;
use crate::exec_events::ReasoningItem;
use crate::exec_events::ThreadErrorEvent;
use crate::exec_events::ThreadEvent;
Expand Down Expand Up @@ -107,7 +108,7 @@ impl EventProcessorWithJsonOutput {
}

pub fn collect_thread_events(&mut self, event: &Event) -> Vec<ThreadEvent> {
match &event.msg {
let mut events = match &event.msg {
EventMsg::SessionConfigured(ev) => self.handle_session_configured(ev),
EventMsg::AgentMessage(ev) => self.handle_agent_message(ev),
EventMsg::AgentReasoning(ev) => self.handle_reasoning_event(ev),
Expand Down Expand Up @@ -148,7 +149,15 @@ impl EventProcessorWithJsonOutput {
})],
EventMsg::PlanUpdate(ev) => self.handle_plan_update(ev),
_ => Vec::new(),
};

if events.is_empty()
&& let Ok(raw) = serde_json::to_value(&event.msg)
{
events.push(ThreadEvent::Raw(RawEvent { raw }));
}

events
}

fn get_next_item_id(&self) -> String {
Expand Down
Loading
Loading