Commit Graph

62 Commits

  • Show activity for standalone web search calls (#24693)
    ## Why
    
    Standalone `web.run` calls run in the extension, so they need normal
    web-search progress activity while a request is in flight and durable
    completed activity after a thread is reloaded.
    
    Follow-up to #23823; uses the extension turn-item emission path added in
    #24813.
    
    ## What changed
    
    - Emit standalone `web.run` start/completion items through the host
    turn-item emitter, preserving standard client delivery and rollout
    persistence.
    - Include useful completion detail for queries, image queries, and
    literal-URL `open`/`find` commands.
    - Render completed searches as `Searched the web` or `Searched the web
    for <detail>`, with snapshot coverage for the detail-free case.
    - Extend the app-server round-trip test to verify completed search
    activity is reconstructed by `thread/read` after a fresh-process reload.
    
    ## Testing
    
    - `just test -p codex-web-search-extension`
    - `just test -p codex-app-server -E
    "test(standalone_web_search_round_trips_encrypted_output)"`
  • Handle goal usage limits from turn errors (#25095)
    ## Summary
    - handle goal usage-limit turn errors in the goal extension
    - exercise the extension path in the goal backend test
    
    ## Tests
    - just fmt
    - just test -p codex-goal-extension
    - just fix -p codex-goal-extension
  • Use inject_if_running for active goal steering (#24924)
    ## Why
    
    This PR is stacked on #24918, which moves goal steering onto
    source-labeled internal model context fragments. Active-turn goal
    steering should use the same running-turn injection path as other
    runtime steering, so those fragments enter the pending input queue as
    `ResponseItem`s through the existing
    [`Session::inject_if_running`](https://github.com/openai/codex/blob/8d6f6cdf69b055c27682e7cdea9caf72a3e2ee7f/codex-rs/core/src/session/inject.rs#L12-L27)
    behavior instead of through a goal-specific conversion wrapper.
    
    ## What Changed
    
    - Exposes a narrow `CodexThread::inject_if_running` bridge for callers
    that only hold a thread handle.
    - Changes `ext/goal` active-turn steering to pass `ResponseItem`s
    directly.
    - Builds goal steering prompts as contextual internal model context
    `ResponseItem`s before injecting them into the running turn.
    
    ## Testing
    
    Not run locally; PR metadata update only.
  • Use internal model context fragments for goal steering (#24918)
    ## Why
    
    Goal steering is one form of runtime-owned model context, but the old
    `<goal_context>` wrapper made the contextual-fragment hiding path
    goal-specific. Using a source-labeled internal context fragment gives
    core and extensions a shared shape for hidden model steering while
    keeping those prompts out of visible turn history.
    
    The change also keeps legacy `<goal_context>` messages recognized as
    hidden contextual input so existing stored history does not start
    rendering old goal-steering prompts as user-visible turn items.
    
    ## What Changed
    
    - Replaces `GoalContext` with `InternalModelContextFragment` plus a
    validated `InternalContextSource`.
    - Renders goal steering as `<codex_internal_context
    source="goal">...</codex_internal_context>`.
    - Updates core goal steering and `ext/goal` steering to inject the new
    internal-context fragment.
    - Updates contextual-fragment, event-mapping, goal, and session tests
    for the new wrapper.
    
    ## Test Coverage
    
    - Adds coverage for detecting the new internal model context fragment.
    - Preserves coverage for hiding legacy `<goal_context>` fragments.
    - Verifies invalid internal context sources are rejected and arbitrary
    context tags are not hidden.
    - Updates goal steering/session assertions to expect the new
    `source="goal"` wrapper.
  • Add feature-gated standalone image generation extension (#24723)
    ## Why
    
    Add a standalone image generation path that can be exercised
    independently of hosted Responses image generation, while retaining the
    hosted tool as fallback unless the extension is actually available to
    the model.
    
    ## What changed
    
    - Added the `codex-image-generation-extension` crate with standalone
    generate/edit execution, prior-image selection for edits, model-visible
    image output, and local generated-image persistence.
    - Installed the extension in app-server behind the disabled-by-default
    `imagegenext` feature and backend eligibility checks.
    - Updated core tool planning so eligible `image_gen.imagegen` exposure
    replaces hosted `image_generation`, while unavailable configurations
    retain hosted fallback.
    - Added coverage for extension behavior, edit history reuse, feature
    gating, auth eligibility, and hosted-tool replacement.
    - The extension is installed through app-server only in this PR; other
    execution paths retain hosted image generation because hosted
    replacement occurs only when the standalone executor is actually
    registered and model-visible.
    - The initial extension contract intentionally fixes the image model to
    `gpt-image-2` and uses automatic image parameters.
    - Native generated-image history/card parity and rollout persistence
    cleanup are intentionally deferred follow-up work.
    
    ## Validation
    
    - `just test -p codex-image-generation-extension`
    - `just test -p codex-features`
    - `just test -p codex-core
    hosted_tools_follow_provider_auth_model_and_config_gates`
    - `just test -p codex-app-server`
    - `just fix -p codex-image-generation-extension -p codex-features -p
    codex-core -p codex-app-server`
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    
    ---------
    
    Co-authored-by: jif-oai <jif@openai.com>
  • 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>
  • Gate goal tools by thread eligibility (#24925)
    ## Why
    
    Goal tools create and update goal state for a persistent thread. The
    extension was only checking whether goals were enabled before
    advertising those tools, which meant they could be surfaced in contexts
    that should not receive thread goal controls: ephemeral threads without
    persistent thread state and review subagents.
    
    Those sessions can still run the goal extension lifecycle, but the
    thread tools should only be visible when the current thread can safely
    use them.
    
    ## What changed
    
    - Adds a `GoalRuntimeConfig` that separates goal enablement from whether
    goal tools are available for the current thread.
    - Computes tool eligibility on thread start from
    `persistent_thread_state_available` and `SessionSource`, hiding tools
    for review subagents.
    - Uses `GoalRuntimeHandle::tools_visible()` when contributing thread
    tools so enabled runtime state does not automatically imply tool
    exposure.
    - Adds backend coverage for hiding goal tools on ephemeral threads and
    review subagents.
    
    ## Testing
    
    - Added `goal_tools_hidden_for_ephemeral_threads`.
    - Added `goal_tools_hidden_for_review_subagents`.
  • Add turn error lifecycle contributor (#24916)
    Summary
    - Add TurnErrorInput and TurnLifecycleContributor::on_turn_error to the
    extension API.
    - Emit the turn-error lifecycle from core turn error paths, including
    usage limit failures.
    - Add direct lifecycle coverage for the emitted error facts and stores.
    
    Tests
    - just fmt
    - git diff --check
    - Not run: full tests or clippy (per instructions)
  • Add thread start contributor facts (#24915)
    Summary: add session source and persistent-state availability to
    ThreadStartInput; populate them from session init; update existing goal
    test harness constructors. Tests: just fmt; git diff --check. No full
    tests or clippy run per request.
  • 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: add thread idle lifecycle hook (#24744)
    ## Why
    
    Extensions can currently observe thread start, resume, and stop, but
    they do not have a lifecycle point for the host to say that immediately
    pending thread work has drained. That makes idle follow-up behavior
    harder to express as extension-owned logic instead of host-specific
    plumbing.
    
    This adds an explicit idle lifecycle hook so an extension can react when
    a thread becomes idle while the host keeps ownership of whether any
    submitted follow-up input starts a turn, is queued, or is ignored.
    
    ## What changed
    
    - Added `ThreadIdleInput` with access to the session-scoped and
    thread-scoped extension stores.
    - Added a default `on_thread_idle` method to
    `ThreadLifecycleContributor`.
    - Re-exported `ThreadIdleInput` from the extension API surface.
    
    ## Testing
    
    Not run; this only extends the extension API trait surface with a
    default hook and exported input type.
  • feat: handle goal usage limits in goal extension (#24628)
    ## Why
    
    The extracted goal runtime needs a host-callable path for turns that
    stop because the workspace usage limit is reached. In that case, any
    in-turn goal progress should be accounted before the goal becomes
    terminal, and active goal accounting must be cleared so later
    tool-finish or turn-stop handling does not keep charging usage to a
    stopped goal.
    
    ## What changed
    
    - Adds `GoalRuntimeHandle::usage_limit_active_goal_for_turn`, which
    accounts current active-goal progress, marks the active or
    budget-limited thread goal as `UsageLimited`, records terminal metrics
    when the status changes, clears active goal accounting, and emits the
    updated goal event.
    - Covers both active and budget-limited goals in
    `ext/goal/tests/goal_extension_backend.rs`, including the invariant that
    later token/tool events do not add usage after the goal has been
    usage-limited.
    
    ## Testing
    
    - Added
    `usage_limit_active_goal_accounts_progress_and_clears_accounting`.
    - Added `usage_limit_budget_limited_goal_accounts_remaining_progress`.
  • fix: dont compact standalone websearch schema (#24660)
    add new `parse_tool_input_schema_without_compaction` to bypass the
    existing compaction/trimming of client-provided tool schemas that are
    over 4k bytes.
    
    we want this for standalone web search to keep field guidance/metadata
    on certain fields; this keeps us closer to parity with existing hosted
    tool schema (which didnt go through this 4k byte filter).
  • make direct only allowed caller for standalone websearch (#24646)
    only allow `Direct` callers of the standalone websearch tool because its
    not supported in codemode
  • Add experimental turn additional context (#24154)
    ## Summary
    
    Adds experimental `additionalContext` support to `turn/start` and
    `turn/steer` so clients can provide ephemeral external context, such as
    browser or automation state, without turning that plumbing into a
    visible user prompt or triggering user-prompt lifecycle behavior.
    
    ## API Shape
    
    The parameter shape is:
    
    ```ts
    additionalContext?: Record<string, {
      value: string
      kind: "untrusted" | "application"
    }> | null
    ```
    
    Example:
    
    ```json
    {
      "additionalContext": {
        "browser_info": {
          "value": "Active tab is CI failures.",
          "kind": "untrusted"
        },
        "automation_info": {
          "value": "CI rerun is in progress.",
          "kind": "application"
        }
      }
    }
    ```
    
    The keys are opaque and caller-defined.
    
    ## Context Injection
    
    When provided, accepted entries are inserted into model context as
    hidden contextual message items, not as visible thread user-message
    items.
    
    `kind: "untrusted"` entries are inserted with role `user`:
    
    ```text
    <external_${key}>${value}</external_${key}>
    ```
    
    `kind: "application"` entries are inserted with role `developer`:
    
    ```text
    <${key}>${value}</${key}>
    ```
    
    Values are not escaped. Each value is truncated to 1k approximate tokens
    before wrapping.
    
    For `turn/start`, accepted additional context is inserted before normal
    user input. For `turn/steer`, additional context is merged only when the
    steer includes non-empty user input; context-only steers still reject as
    empty input.
    
    ## Dedupe Strategy
    
    `AdditionalContextStore` lives on session state and stores the latest
    complete additional-context map.
    
    Each `turn/start` or non-empty `turn/steer` treats its
    `additionalContext` as the current complete set of values. Entries are
    injected only when the key is new or the exact entry for that key
    changed, including `value` or `kind`. After merging, the store is
    replaced with the provided map, so omitted keys are removed from the
    retained set and can be injected again later if reintroduced.
    
    Omitting `additionalContext`, passing `null`, or passing an empty object
    resets the store to empty and injects nothing.
    
    ## What Changed
    
    - Threads experimental v2 `additionalContext` through app-server into
    core turn start and steer handling.
    - Adds separate contextual fragment types for untrusted user-role
    context and application developer-role context.
    - Uses pending response input items so additional context can be
    combined with normal user input without treating it as prompt text.
    - Adds integration coverage for start/steer flow, role routing,
    dedupe/reset behavior, deletion/re-add behavior, hook-blocked input
    behavior, empty context-only steer rejection, external-fragment marker
    matching, and truncation.
  • standalone websearch extension (#23823)
    ## Summary
    
    Add the extension-backed standalone `web.run` tool so Codex can call the
    standalone search endpoint through the `codex-api` search client and
    return its encrypted output to Responses.
    
    - gate the new tool behind `standalone_web_search`
    - install the extension in the app-server thread registry and hide
    hosted `web_search` when standalone search is enabled for OpenAI
    providers so the two paths stay mutually exclusive
    - build search context from persisted history using a small tail
    heuristic: previous user message, assistant text between the last two
    user turns capped at about 1k tokens, and current user message
    
    ## Test Plan
    
    - `cargo test -p codex-web-search-extension`
    - `cargo test -p codex-api`
    - `cargo test -p codex-core
    hosted_tools_follow_provider_auth_model_and_config_gates`
  • fix: restore goal accounting after thread resume (#24626)
    ## Why
    
    Goal idle accounting is supposed to survive a thread resume. Previously,
    the resume hook restored the active goal state inline from the extension
    lifecycle contributor, which left the runtime handle without a reusable
    restoration path and made the behavior hard to cover directly. When a
    thread with an active goal was resumed, goal accounting could lose track
    of the active idle goal instead of continuing to accrue elapsed time.
    
    ## What changed
    
    - Moved thread-resume restoration into
    `GoalRuntimeHandle::restore_after_resume()` so the runtime owns
    rehydrating active goal accounting from persisted thread goal state.
    - Kept disabled goal runtimes as a no-op and preserved the existing
    warning path when persisted goal state cannot be loaded.
    - Added a backend regression test that seeds an active goal, resumes the
    thread, waits briefly, and verifies elapsed idle time is reflected on
    the next external goal mutation.
    
    ## Testing
    
    - Not run locally; this metadata update only rewrote the PR title/body.
  • Add goal extension telemetry parity (#24615)
    ## Why
    
    `core/src/goals.rs` already emits OTEL metrics for goal creation,
    resume, terminal transitions, token counts, and duration. As `/goal`
    moves into `ext/goal`, the extension needs to preserve that telemetry
    contract instead of only emitting app-visible `ThreadGoalUpdated`
    events.
    
    This keeps the existing `codex.goal.*` metric surface intact while goal
    lifecycle ownership shifts toward the extension.
    
    ## What changed
    
    - Added an extension-local `GoalMetrics` helper that records the
    existing `codex.goal.*` counters and histograms through `codex-otel`.
    - Threaded an optional `MetricsClient` through `install_with_backend`,
    `GoalExtension`, `GoalRuntimeHandle`, and `GoalToolExecutor`.
    - Emitted created, resumed, and terminal goal metrics from the extension
    paths that create goals, restore active goals on thread resume, account
    budget limits, complete or block goals, and handle external goal
    mutations.
    - Updated existing goal extension test setup callsites to pass `None`
    for metrics when instrumentation is not under test.
    
    ## Verification
    
    Not run locally.
  • 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.
  • [codex] Steer budget-limited goal extension turns (#23718)
    ## What
    - Add a small extension capability for injecting model-visible response
    items into the active turn
    - Have the goal extension inject hidden goal-context steering when
    tool-finish accounting reaches `BudgetLimited`
    - Cover the extension backend path with an assertion on the injected
    steering item
    
    ## Why
    PR #23696 persists and emits the budget-limited goal update from
    tool-finish accounting, but it leaves the model unaware of that
    transition. The existing core runtime steers the model to wrap up in
    this case; the extension path should do the same through an explicit
    host capability.
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-goal-extension`
    - `cargo test -p codex-extension-api`
  • Fix thread settings clippy failure (#23724)
    ## Why
    
    `main` picked up two small Rust build failures after nearby merges:
    
    - #23507 added a real handler for
    `ServerNotification::ThreadSettingsUpdated`, but the same variant was
    still listed in the ignored-notification match arm. Full Clippy runs
    treat the resulting unreachable-pattern warning as an error.
    - #23666 added `turn_id` and `truncation_policy` to
    `codex_tools::ToolCall`, while the goal extension backend test fixtures
    from the goal-extension work still used the old shape. That left
    `codex-goal-extension` tests unable to compile once the branches met on
    `main`.
    
    ## What changed
    
    Removed the duplicate `ThreadSettingsUpdated` match pattern from
    `tui/src/chatwidget/protocol.rs`.
    
    Updated the goal extension test `tool_call` helper to populate the new
    `ToolCall` fields, and reused that helper for the one direct literal
    that still had the old field list.
    
    ## Verification
    
    - `just fix -p codex-tui`
    - `cargo test -p codex-goal-extension`
  • [codex] Preserve failed goal accounting flushes (#23717)
    ## What
    - Preserve database accounting failures from the goal extension instead
    of collapsing them into `None`
    - Warn with turn/tool context when a flush fails
    - Keep stop/abort accounting snapshots alive when the final flush did
    not persist
    
    ## Why
    PR #23696 can finish and discard a turn snapshot after
    `account_thread_goal_usage` fails. That loses the final accumulated
    accounting state silently. This follow-up keeps that failure explicit
    and avoids deleting the local snapshot in the failing path.
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-goal-extension`
  • 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`
  • feat: account active goal progress in the goal extension (#23696)
    ## Why
    
    The goal extension can create and surface goals, but the live
    turn-accounting path still stopped short of persisting active-goal
    progress. That leaves token and wall-clock usage, plus
    `ThreadGoalUpdated` events, out of sync with the extension boundary once
    work actually advances or a goal transitions out of active state.
    
    ## What changed
    
    - Teach `GoalAccountingState` to track the current turn, active goal,
    token deltas, and wall-clock progress snapshots against the persisted
    goal id.
    - Flush active-goal accounting from tool-finish, turn-stop, and
    turn-abort lifecycle hooks, and emit `ThreadGoalUpdated` events when
    persisted progress changes.
    - Route `create_goal` and `update_goal` through the same accounting
    state so new goals start from the right baseline, final progress is
    flushed before status changes, and `update_goal` can mark a goal
    `blocked` as well as `complete`.
    - Keep budget-limited goals accruing through the end of the turn while
    clearing local active-goal state once a turn or explicit update is
    finished.
    - Expand backend and lifecycle coverage around store ids, baseline
    reset, tool-finish accounting, budget-limited carry-through, and
    blocked-goal updates.
    
    ## Testing
    
    - Added focused backend coverage in
    `codex-rs/ext/goal/tests/goal_extension_backend.rs` for baseline reset,
    tool-finish accounting, budget-limited turns, and blocked-goal updates.
    - Extended `codex-rs/core/src/session/tests.rs` to assert that lifecycle
    inputs expose the expected session, thread, and turn store ids.
  • feat: expose turn-start metadata to extensions (#23688)
    ## Why
    
    The goal extension needs more context when a turn starts than
    `turn_store` alone provides.
    
    In particular, goal accounting needs the stable turn id, the effective
    collaboration mode, and the cumulative token-usage baseline captured at
    turn start so it can:
    
    - suppress goal accounting for plan-mode turns
    - compute exact per-turn deltas from cumulative `total_token_usage`
    snapshots instead of relying on the most recent usage event alone
    - keep the extension-owned accounting path aligned with the host turn
    lifecycle
    
    ## What
    
    - extend `codex_extension_api::TurnStartInput` to expose `turn_id`,
    `collaboration_mode`, and `token_usage_at_turn_start`
    - pass the full `TurnContext` plus the captured token-usage baseline
    through the turn-start lifecycle emission path
    - initialize goal turn accounting from the turn-start baseline and
    collaboration mode
    - switch goal token accounting to compute deltas from cumulative
    `total_token_usage` snapshots
    - add coverage for the new turn-start lifecycle fields and for
    goal-accounting baseline behavior
    
    ## Testing
    
    - added `turn_start_lifecycle_exposes_turn_metadata_and_token_baseline`
    in `codex-rs/core/src/session/tests.rs`
    - added `ext/goal/tests/accounting.rs` coverage for baseline-aware goal
    accounting and plan-mode suppression
  • feat: wire goal extension tools to the dedicated goal store (#23685)
    ## Why
    
    `ext/goal` already had the tool specs and contributor wiring for
    `/goal`, but the installed tools still depended on a placeholder backend
    that always errored. That meant the extension could not actually own
    goal persistence even though the dedicated `thread_goals` store already
    exists.
    
    This change wires the extension tools directly to the dedicated goal
    store so the extension can create, read, and complete goals against real
    state instead of falling back to host-side placeholders.
    
    ## What changed
    
    - make `install_with_backend(...)` require
    `Arc<codex_state::StateRuntime>` so goal storage is always available
    when the extension is installed
    - remove the unused no-backend/public backend abstraction from
    `ext/goal` and have the tool executors talk directly to `StateRuntime`
    - map `thread_goals` rows into the existing protocol response shape for
    `get_goal`, `create_goal`, and `update_goal`
    - preserve current thread-list behavior by filling an empty thread
    preview from the goal objective when a goal is created through the
    extension path
    - add integration coverage for the installed tool surface, including
    successful goal creation and duplicate-create rejection
    
    ## Testing
    
    - `cargo test -p codex-goal-extension`
  • Add tool lifecycle extension contributor (#23309)
    ## Why
    
    Extensions that need to track runtime progress currently have no typed
    host signal for tool execution. The goal extension in particular needs
    to observe tool attempts without inspecting tool payloads, owning tool
    implementations, or staying coupled to core-only runtime plumbing.
    
    This adds a narrow lifecycle contributor API for host-owned tool
    execution: extensions can observe when an accepted tool call starts and
    how it finishes, while policy hooks and tool handlers continue to own
    payload rewriting, blocking, and execution.
    
    Relevant code:
    
    -
    [`ToolLifecycleContributor`](https://github.com/openai/codex/blob/3ad2850ffc7d8a1da19c65a92425637a59098f1b/codex-rs/ext/extension-api/src/contributors.rs#L119)
    defines the extension-facing observer contract.
    -
    [`tool_lifecycle.rs`](https://github.com/openai/codex/blob/3ad2850ffc7d8a1da19c65a92425637a59098f1b/codex-rs/ext/extension-api/src/contributors/tool_lifecycle.rs)
    defines the typed start/finish inputs, source, and outcome enums.
    - [`notify_tool_start` /
    `notify_tool_finish`](https://github.com/openai/codex/blob/3ad2850ffc7d8a1da19c65a92425637a59098f1b/codex-rs/core/src/tools/lifecycle.rs)
    bridges core tool dispatch into the extension registry.
    
    ## What Changed
    
    - Added `ToolLifecycleContributor` to `codex-extension-api`, including:
      - `ToolStartInput`
      - `ToolFinishInput`
      - `ToolCallSource`
      - `ToolCallOutcome`
    - Added registration and lookup support on `ExtensionRegistryBuilder` /
    `ExtensionRegistry`.
    - Wired core tool dispatch to notify lifecycle contributors for:
      - accepted tool starts
      - completed tool calls, including the tool output success marker
      - pre-tool-use blocks
      - failures before or after the handler runs
      - cancellation/abort in the parallel tool path
    - Registered the goal extension as a lifecycle contributor and added the
    outcome filter it will use for goal progress accounting.
    
    ## Test Coverage
    
    - Added `dispatch_notifies_tool_lifecycle_contributors` to cover
    lifecycle notification ordering and outcomes for successful and
    handler-failed tool calls.
  • Emit goal update events from goal extension tools (#23306)
    ## Why
    
    Goal creation and completion are moving through the goal extension, but
    the rest of Codex still observes goal state through `ThreadGoalUpdated`
    events. Without an event from the extension-owned tool path, a
    model-initiated `create_goal` or `update_goal` can mutate the backend
    and return a tool result while app-server and TUI listeners miss the
    goal state transition.
    
    ## What changed
    
    - Added `GoalEventEmitter` as a small wrapper around the host
    `ExtensionEventSink` to build `EventMsg::ThreadGoalUpdated` events for
    goal updates.
    - Threaded the registry event sink into `GoalExtension` and the
    `GoalToolExecutor`s created by the extension. The public
    `GoalExtension::new` constructor keeps a `NoopExtensionEventSink`
    fallback for standalone use.
    - Emitted a goal update after successful `create_goal` and `update_goal`
    tool calls. Until `ToolCall` exposes the current turn submission id,
    these events use the tool call id as the event id and leave `turn_id`
    unset.
    
    Relevant code:
    
    -
    [`GoalEventEmitter::thread_goal_updated`](https://github.com/openai/codex/blob/1fe2d73890df9a50996f67f705d4da4cc3d4b866/codex-rs/ext/goal/src/events.rs#L19-L32)
    - [`GoalToolExecutor` emission
    points](https://github.com/openai/codex/blob/1fe2d73890df9a50996f67f705d4da4cc3d4b866/codex-rs/ext/goal/src/tool.rs#L161-L190)
    
    ## Testing
    
    - `cargo test -p codex-goal-extension`
  • chore: make token usage async (#23305)
    Make the `TokenUsageContributor` async. This will be required for future
    extension and it's basically free
  • feat: add extension event sink capability (#23293)
    ## Why
    
    Extensions can already expose typed contributions and receive host
    capabilities such as `AgentSpawner`, but they do not have a typed way to
    send protocol events back through the host. Extensions that need to
    surface progress or status should not have to own persistence, ordering,
    transport fanout, or logging decisions themselves.
    
    ## What
    
    - Add `ExtensionEventSink`, a host-provided fire-and-forget sink for
    `codex_protocol::protocol::Event`.
    - Add `NoopExtensionEventSink` so hosts that do not expose extension
    event emission keep the existing empty-registry behavior.
    - Store the sink on `ExtensionRegistryBuilder` / `ExtensionRegistry`,
    with `with_event_sink(...)` and `event_sink()` accessors, and re-export
    the new capability from `codex-extension-api`.
    
    ## Testing
    
    - Not run locally; PR metadata/body update only.
  • 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.
  • chore: goal ext skeleton (#23288)
    Skeleton of `/goal` in extension
    Lot's of follow-ups coming
  • 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.
  • chore(config) rm Feature::CodexGitCommit (#22412)
    ## Summary
    Removes the unused Feature::CodexGitCommit
    
    ## Testing
    - [x] tests pass
  • 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.
  • fix: main (#22503)
    Fix main due to conflicting merge
  • feat: memories ext (#22498)
    First memories extension implementation
    Based on memories-mcp tools
  • feat: add config-change extension contributor (#22488)
    ## Why
    
    Extensions can observe thread and turn lifecycle events today, but there
    was no single host-owned hook for changes to the effective thread
    configuration. That makes features that need to react to model,
    permission, or tool-suggest updates either depend on individual mutation
    paths or risk going stale after runtime config refreshes.
    
    This adds a typed config-change contributor so extension-owned state can
    stay synchronized with the effective thread config while the host
    remains responsible for deciding when config changed.
    
    ## What Changed
    
    - Added `ConfigContributor<C>` to `codex_extension_api`, with
    before/after immutable snapshots of the effective config plus
    session/thread extension stores.
    - Added registry builder/accessor support through `config_contributor`
    and `config_contributors`.
    - Emits config-change callbacks after committed updates from session
    settings, per-turn setting updates, and `refresh_runtime_config`.
    - Builds effective config snapshots only when config contributors are
    registered, and suppresses no-op callbacks when the before/after
    snapshots are equal.
    - Added a core session regression test that verifies contributors
    observe both model changes and user-layer runtime config changes,
    including access to session and thread extension stores.
    
    ## Validation
    
    Added `config_change_contributor_observes_effective_config_changes` in
    `codex-rs/core/src/session/tests.rs` to cover the new contributor path.