24 Commits

  • Add turn-scoped context contributions (#28911)
    ## Summary
    - keep context injection on a single ContextContributor trait
    - split context injection into thread-scoped and turn-scoped
    contribution methods
    - wire turn-scoped fragments into initial context assembly so extensions
    can contribute context from turn-local state
  • build: run buildifier from just fmt (#28125)
    ## Intent
    
    Keep Bazel and Starlark files consistently formatted without requiring
    contributors to install or version buildifier themselves.
    
    ## Implementation
    
    - Add a SHA-256-pinned, cross-platform DotSlash manifest for buildifier
    v8.5.1.
    - Run buildifier from the shared `just fmt` and `just fmt-check` driver,
    with Windows-safe explicit DotSlash invocation.
    - Provision DotSlash in formatting CI and contributor devcontainers, and
    document the source-build prerequisite.
    - Apply the initial mechanical buildifier formatting baseline.
  • Route image extension reads through turn environments v2 (#27498)
    ## Why
    
    Image generation used `std::fs::read` for referenced image paths, which
    did not support environment-backed filesystems or their sandbox context.
    
    ## What changed
    
    - Expose optional turn environments to extension tool calls.
    - Include each environment’s ID, working directory, filesystem, and
    sandbox context.
    - Read referenced images through the selected environment filesystem.
    - Keep sandbox usage at the extension call site so extensions can choose
    the appropriate access mode.
    - Consolidate image request construction into one async function.
    - Add coverage for successful environment reads and read failures.
    
    ## Validation
    
    - `cargo check -p codex-image-generation-extension --tests`
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    
    `just test -p codex-image-generation-extension` could not complete
    because the build exhausted available disk space.
  • [codex] Remove async_trait from ToolExecutor (#27304)
    ## Why
    
    We're now [discouraging use of
    `async_trait`](https://github.com/openai/codex/pull/20242).
    
    Removing use of `async_trait` from `ToolExecutor` yields a `codex_core`
    debug test build speedup of ~78% (from 227.5s to 50.3s) on my machine.
    
    Stacked on #27299, this PR applies the trait change after the handler
    bodies have been outlined.
    
    ## What
    
    Changed `ToolExecutor::handle` to return an explicit boxed
    `ToolExecutorFuture` instead of using `async_trait`.
    
    Updated ToolExecutor implementors to return `Box::pin(...)`, reexported
    the future alias through `codex-tools` and `codex-extension-api`, and
    removed `codex-tools` direct `async-trait` dependency.
  • [codex] Outline ToolExecutor handler bodies (#27299)
    ## Why
    
    We're now [discouraging use of
    `async_trait`](https://github.com/openai/codex/pull/20242).
    
    Removing use of `async_trait` from `ToolExecutor` yields a `codex_core`
    debug test build speedup of ~78% (from 227.5s to 50.3s) on my machine.
    
    For ease of reviewing, this is a prefactor to extract trait method
    implementations to inherent methods. This will prevent changing
    indentation from creating a huge diff.
    
    ## What
    
    Outlined existing `ToolExecutor::handle` bodies into inherent async
    `handle_call` methods across core and extension tool handlers.
    
    The trait methods still use `async_trait` and now delegate to
    `self.handle_call(...).await`; handler behavior is unchanged.
  • Remove async-trait from extension contributors (#27383)
    ## Why
    
    Extension contributors are registered behind `dyn Trait` objects, so
    native `async fn`/RPITIT methods would make these traits
    non-object-safe. Spell out the boxed, `Send` future contract directly so
    `extension-api` no longer needs `async-trait` while retaining the
    existing runtime model.
    
    ## What changed
    
    - add a shared `ExtensionFuture` alias and use it for asynchronous
    contributor methods
    - migrate production and test implementations to return `Box::pin(async
    move { ... })`
    - remove `async-trait` dependencies where they are no longer used,
    keeping it dev-only where unrelated test executors still require it
    
    ## Behavior
    
    No behavior change is intended. Contributor futures remain boxed,
    `Send`, dynamically dispatched, and lazily executed; cancellation and
    callback ordering stay unchanged.
    
    ## Testing
    
    - `just test -p codex-extension-api` (11 passed)
    - affected extension crates (64 passed)
    - targeted `codex-core` contributor tests (14 passed)
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    
    A broad local `codex-core` run compiled successfully but encountered
    unrelated sandbox and missing test-binary fixture failures; CI will run
    the full checks.
  • [codex] Require model for standalone web search (#25131)
    ## Why
    
    The standalone `/v1/alpha/search` request now requires a `model`, but
    the `web.run` extension currently omits it.
    
    Adds `model` to extension `ToolCall` invocation.
    
    Follow-up to #23823.
    
    ## What changed
    
    - Make `SearchRequest.model` required.
    - Expose the effective per-turn model on extension tool calls and pass
    it in standalone web-search requests.
    - Assert the model is forwarded in the app-server round-trip test.
    
    ## Testing
    
    - `just test -p codex-api -p codex-tools -p codex-web-search-extension
    -p codex-memories-extension -p codex-goal-extension`
    - `just test -p codex-core -E
    'test(passes_turn_fields_and_scoped_turn_item_emitter_to_extension_call)'`
    - `just test -p codex-app-server -E
    'test(standalone_web_search_round_trips_encrypted_output)'`
  • extension-api: add TurnItemEmitter to tool calls (#24813)
    ## Why
    Extension-contributed tools need to emit visible turn items through
    Codex's normal event and persistence pipeline.
    
    ## What
    - Add `TurnItemEmitter` to extension `ToolCall`s and route the core
    implementation through `Session::emit_turn_item_*`.
    - Hold weak session and turn references so retained tool calls cannot
    keep host state alive.
    - Provide a no-op emitter for extension test callers.
    
    ## Test Plan
    - `just test -p codex-core -E
    'test(passes_turn_fields_and_scoped_turn_item_emitter_to_extension_call)'`
    
    ---------
    
    Co-authored-by: jif-oai <jif@openai.com>
  • Fix memories namespace for Responses API tools (#24898)
    ## Why
    
    Dedicated memories tools are exposed through a Responses API namespace
    tool. The namespace itself has to be a valid tool identifier, so
    `memories/` can fail validation before the model ever gets a chance to
    call the memory tools.
    
    ## What changed
    
    - Changed `MEMORY_TOOLS_NAMESPACE` from `memories/` to `memories`.
    - Added `memory_tool_namespace_matches_responses_api_identifier` so the
    namespace stays non-empty and limited to Responses-safe identifier
    characters.
    
    ## Verification
    
    - Added unit coverage for the namespace identifier shape in
    `codex-rs/ext/memories/src/tests.rs`.
  • feat: gate dedicated memories tools in config (#24600)
    ## Why
    
    The memories extension already has dedicated `list`, `read`, `search`,
    and `add_ad_hoc_note` tools, but app-server registration was still
    disabled. The memories app collaborator needs an explicit config switch
    so those native extension tools can be exposed intentionally, without
    making ordinary memory prompt usage automatically register the dedicated
    tool surface.
    
    ## What changed
    
    - Added `[memories].dedicated_tools`, defaulting to `false`, to
    `MemoriesToml` / `MemoriesConfig`.
    - Regenerated `core/config.schema.json` for the new setting.
    - Registered the memories extension as a `ToolContributor`, while
    keeping tool contribution gated on both memories being enabled and
    `dedicated_tools = true`.
    - Added tests for the disabled default, the enabled dedicated-tools
    path, and installer registration.
    
    ## Verification
    
    - `just test -p codex-config -p codex-memories-extension`
  • Add memory tool call metrics to memories extension (#24583)
    ## Why
    
    The memories extension now receives a metrics exporter, but the useful
    extension-owned signal is the memory tool call itself: which operation
    ran, which memory area it touched, whether the backend call succeeded,
    and whether the result was truncated.
    
    ## What changed
    
    - Added the `codex.memories.tool.call` counter in
    `ext/memories/src/metrics.rs`.
    - Emit that counter from `memories/add_ad_hoc_note`, `memories/list`,
    `memories/read`, and `memories/search` after backend execution.
    - Tag each call with `tool`, `operation`, `scope`, `status`, and
    `truncated`.
    - Pass the existing `MetricsClient` through the memories extension into
    the tool executors; tests use `None`.
    
    ## Verification
    
    - `just test -p codex-memories-extension`
  • Wire metrics client into memories extension (#24567)
    ## Summary
    
    - let the memories extension capture the process-global OTEL metrics
    client at install time
    - keep app-server/TUI/exec extension construction APIs unchanged
    - store the metrics client for future memory metrics without emitting
    any metrics yet
    
    ## Test plan
    
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - Not run: tests/clippy per request; CI will cover them
  • Add ad-hoc memory note tool (#24562)
    ## Why
    
    Codex memory updates currently rely on instructions that tell agents to
    create ad-hoc note files directly in the memory workspace. The memories
    extension already has a `MemoriesBackend` abstraction for local storage
    and future non-filesystem backends, so the ad-hoc note writer should
    live behind that same interface instead of baking local filesystem
    assumptions into the tool shape.
    
    ## What
    
    - Adds a `memories/add_ad_hoc_note` tool to the existing memories tool
    bundle.
    - Extends `MemoriesBackend` with `add_ad_hoc_note` plus request/response
    types so remote memory stores can implement the same operation later.
    - Implements the local backend by creating append-only notes under
    `extensions/ad_hoc/notes`.
    - Validates the tool-provided filename contract
    (`YYYY-MM-DDTHH-MM-SS-<slug>.md`), rejects path-like filenames, rejects
    empty notes, and uses create-new semantics so existing notes are never
    overwritten.
    - Keeps memories tool contribution behind the existing commented-out
    registration path; this defines the tool surface without newly exposing
    it through app-server.
    
    ## Test Plan
    
    - `just test -p codex-memories-extension`
  • chore: move memory prompt builder into extension (#24558)
    ## Why
    
    The memories extension now owns the read-path developer instructions it
    injects at thread start. Keeping that prompt builder and template in
    `codex-memories-read` left the extension depending on a helper crate for
    extension-specific prompt assembly, and kept async template/truncation
    dependencies in the read crate after the remaining read surface no
    longer needed them.
    
    ## What changed
    
    - Moved `prompts.rs`, its tests, and `templates/memories/read_path.md`
    from `memories/read` into `ext/memories`.
    - Wired `MemoryExtension` to call the local prompt builder and added the
    moved templates to `ext/memories/BUILD.bazel` compile data.
    - Removed the now-unused prompt export and prompt-related dependencies
    from `codex-memories-read`.
    
    ## Testing
    
    - Not run locally.
  • Expose conversation history to extension tools (#23963)
    ## Why
    
    Extension tools that need conversation context should be able to read it
    from the live tool invocation instead of reaching into thread
    persistence themselves.
    
    ## What changed
    
    - Add a `ConversationHistory` snapshot to extension `ToolCall`s and
    populate it from the current raw in-memory response history.
    - Expose all history items at this boundary so each extension can filter
    and bound the subset it needs before consuming or forwarding it.
    - Cover the adapter and registry dispatch paths and update existing
    extension tests that construct `ToolCall` literals.
    
    ## Test plan
    
    - `cargo test -p codex-tools`
    - `cargo test -p codex-extension-api`
    - `cargo test -p codex-goal-extension`
    - `cargo test -p codex-memories-extension`
    - `cargo test -p codex-core passes_turn_fields_to_extension_call`
    - `cargo test -p codex-core
    extension_tool_executors_are_model_visible_and_dispatchable`
  • Make tool executor specs mandatory (#23870)
    ## Why
    
    `ToolExecutor` is the runtime contract that keeps a callable tool and
    its model-visible spec together. Leaving `spec()` optional lets a
    registered runtime silently omit that half of the contract, and it also
    overloads a missing spec as an exposure decision for tools that should
    stay dispatchable without being shown to the model.
    
    ## What
    
    - Make `ToolExecutor::spec()` required and update core, extension, and
    test tool executors to return a concrete `ToolSpec`.
    - Add `ToolExposure::Hidden` for dispatch-only tools. The legacy
    `shell_command` runtime in unified-exec sessions now uses that explicit
    exposure instead of hiding itself by omitting a spec.
    - Build MCP tool specs when `McpHandler` is constructed so invalid MCP
    specs are skipped before the handler is registered.
    - Keep tool planning aligned with the new contract for direct, deferred,
    hidden, code-mode, dynamic, and namespaced tool paths.
    
    ## Testing
    
    - Added tool-plan coverage that invalid MCP tool specs are not
    registered.
    - Updated shell-family coverage for the hidden legacy `shell_command`
    runtime and the affected tool executor test fixtures.
  • feat: add turn_id and truncation_policy to extension tool calls (#23666)
    ## Why
    
    Extension-owned tools currently receive a stripped `ToolCall` with only
    `call_id`, `tool_name`, and `payload`.
    That makes extension work that needs turn-local execution context
    awkward, especially web-search extension work that needs the active
    `truncation_policy` at tool invocation time.
    
    Reconstructing that value from config or `ExtensionData` would be
    indirect and could drift from the actual turn context, so the cleaner
    fix is to pass the needed turn metadata directly on the extension-facing
    invocation type.
    
    ## What changed
    
    - added `turn_id` and `truncation_policy` to `codex_tools::ToolCall`
    - populated those fields when core adapts `ToolInvocation` into an
    extension tool call
    - added a focused adapter test that verifies extension executors receive
    the forwarded turn metadata
    - updated the memories extension tests to construct the richer
    `ToolCall`
    - added the `codex-utils-output-truncation` dependency to `codex-tools`
    and refreshed lockfiles
    
    ## Testing
    
    - `cargo test -p codex-tools`
    - `cargo test -p codex-memories-extension`
    - `cargo test -p codex-core passes_turn_fields_to_extension_call`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
  • Make extension lifecycle hooks async (#23291)
    ## Why
    
    Extension lifecycle hooks sit on the host/extension boundary, but the
    current trait surface only allows synchronous callbacks. That forces
    extensions that need to seed, rehydrate, observe, or flush
    extension-owned state during thread and turn transitions to either block
    inside the callback or move async work into separate host plumbing.
    
    This PR makes those lifecycle callbacks awaitable so extension
    implementations can perform async work directly at the lifecycle point
    where the host already has the relevant session, thread, or turn stores
    available.
    
    ## What changed
    
    - Makes `ThreadLifecycleContributor` and `TurnLifecycleContributor`
    async in `codex-extension-api`.
    - Awaits thread start/resume/stop and turn start/stop/abort lifecycle
    callbacks from `codex-core`.
    - Updates the guardian and memories extensions to implement the async
    lifecycle trait surface.
    - Updates the existing lifecycle tests to use async contributor
    implementations.
    - Adds `async-trait` to the crates that now expose or implement these
    async object-safe lifecycle traits.
    
    ## Testing
    
    - Existing `codex-core` lifecycle tests were updated to cover async
    implementations for thread stop and turn abort ordering.
  • Move memory prompt injection to app-server extension (#22841)
    ## Why
    
    Memory prompt injection should be owned by the extension path that
    app-server composes at runtime, not by an inlined special case inside
    `codex-core`. This keeps `codex-core` focused on session orchestration
    while allowing the memories extension to own its app-server prompt
    behavior.
    
    ## What Changed
    
    - Registers `codex-memories-extension` in the app-server extension
    registry.
    - Moves the memory developer-instruction injection out of
    `core/src/session/mod.rs` and into the memories extension prompt
    contributor.
    - Adds config-change handling so the extension keeps its per-thread
    memory settings in sync after startup.
    - Leaves memories read/retrieval tools unregistered for now so this PR
    only changes prompt injection.
    - Removes the stale `cargo-shear` ignore now that app-server depends on
    the extension crate.
    
    ## Validation
    
    Not run locally; validation is left to CI.
  • Simplify tool executor and registry plumbing (#22636)
    ## Why
    
    The tool runtime path still had a typed output associated type on
    `ToolExecutor`, plus a core-only `RegisteredTool` adapter and
    extension-only executor aliases. That made every new shared tool runtime
    carry extra adapter plumbing before it could participate in core
    dispatch, extension tools, hook payloads, telemetry, and model-visible
    spec generation.
    
    This PR moves output erasure to the shared executor boundary so core and
    extension tools can use the same execution contract directly.
    
    ## What Changed
    
    - Changed `codex_tools::ToolExecutor` to return `Box<dyn ToolOutput>`
    instead of an associated `Output` type.
    - Removed the extension-specific `ExtensionToolExecutor` /
    `ExtensionToolOutput` aliases and exposed `ToolExecutor<ToolCall>` plus
    `ToolOutput` through `codex-extension-api`.
    - Reworked core tool registration around `CoreToolRuntime` and
    `ToolRegistry::from_tools`, removing the extra `RegisteredTool` /
    `ToolRegistryBuilder` layer.
    - Consolidated model-visible spec planning and registry construction in
    `core/src/tools/spec_plan.rs`, including deferred tool search and
    code-mode-only filtering.
    - Added `ToolOutput` helpers for post-tool-use hook ids and inputs so
    MCP, unified exec, extension, and other boxed outputs preserve the same
    hook payload behavior.
    - Updated core handlers, memories tools, and the related
    registry/spec/router tests to use the simplified contract.
    
    ## Test Coverage
    
    - Updated coverage for tool spec planning, registry lookup, deferred
    tool search registration, extension tool routing, post-tool-use hook
    payloads, dispatch tracing, guardian output extraction, and memories
    extension tool execution.
  • feat: make ToolExecutor an async trait (#22560)
    ## Why
    
    `codex_tools::ToolExecutor` keeps a tool spec attached to its runtime
    handler, but extension tools still carried a parallel
    `ExtensionToolFuture` / `ExtensionToolExecutor` shape. That made
    extension-owned tools look different from host tools even though
    routing, registration, and execution need the same abstraction.
    
    This PR makes the shared executor contract directly async and lets
    extension tools implement it too, so host tools and extension tools can
    move through the same registration path.
    
    ## What changed
    
    - Changed `ToolExecutor::handle` to an `async fn` using `async-trait`,
    and updated built-in tool handlers to implement the async trait
    directly.
    - Replaced the bespoke `ExtensionToolFuture` contract with a marker
    `ExtensionToolExecutor` over `ToolExecutor<ToolCall, Output =
    JsonToolOutput>`, re-exporting `ToolExecutor` from
    `codex-extension-api`.
    - Updated the memories extension tools to implement the shared executor
    trait.
    - Split tool-router construction into collected executors plus hosted
    model specs, keeping hosted tools like web search and image generation
    separate from executable handlers.
    - Updated spec/router tests and extension-tool stubs for the new
    executor shape.
    
    ## Verification
    
    - Not run locally.
  • refactor: split memories extension crate modules (#22500)
    ## Why
    
    The memories extension has several distinct responsibilities:
    registering its prompt and tool contributors, enforcing local-memory
    filesystem boundaries, implementing list/read/search behavior, and
    wrapping that backend as extension tools. Those responsibilities were
    concentrated in `lib.rs`, `local.rs`, and the tool modules, which made
    follow-up work harder to review and risked growing files through
    unrelated edits.
    
    This PR reorganizes the crate so each responsibility has a narrower
    owner while preserving the same extension entrypoint and memory tool
    behavior.
    
    ## What Changed
    
    - Moved extension lifecycle, prompt, and tool registration into
    `src/extension.rs`, leaving `src/lib.rs` as the small crate entrypoint.
    - Split `LocalMemoriesBackend` helpers into `local/list.rs`,
    `local/path.rs`, `local/read.rs`, and `local/search.rs`.
    - Centralized tool names and limits at the crate level, and kept the
    backend and extension implementation crate-private.
    - Made `memory_list`, `memory_read`, and `memory_search` tool executors
    generic over `MemoriesBackend`, so tests can exercise the full executor
    path without depending on tool internals.
    - Consolidated and expanded memory extension tests in `src/tests.rs`,
    including read/search tool output coverage, multi-query search, windowed
    `all_within_lines`, and legacy `query` rejection.
    
    ## Testing
    
    - Not run locally.
  • feat: memories ext (#22498)
    First memories extension implementation
    Based on memories-mcp tools