Skip to content

checkpoint: into wallentx/termux-target from release/0.140.0 @ f88907c1fa2c - #205

Merged
wallentx merged 32 commits into
wallentx/termux-targetfrom
checkpoint/wallentx_termux-target_from_release_0.140.0_f88907c1fa2c
Jun 10, 2026
Merged

checkpoint: into wallentx/termux-target from release/0.140.0 @ f88907c1fa2c#205
wallentx merged 32 commits into
wallentx/termux-targetfrom
checkpoint/wallentx_termux-target_from_release_0.140.0_f88907c1fa2c

Conversation

@unemployabot

@unemployabot unemployabot Bot commented Jun 10, 2026

Copy link
Copy Markdown

Termux release checkpoint

  • Source branch: release/0.140.0
  • Source hash: f88907c1fa2c5c4c4268c2ab8df12ede723692fc
  • Destination branch: wallentx/termux-target
  • Remaining first-parent commits on source: 0

This PR carries release-train conflict fixes and follow-up changes back into the reusable Termux patch branch.

Release-only workflow files and metadata under .github were restored to the destination branch versions before opening this PR.

tamird and others added 30 commits June 8, 2026 21:32
Codex forces `core.fsmonitor=false` on internal Git commands so a
repository cannot select an executable fsmonitor helper. This also
disables Git's built-in daemon for `status`, `diff`, and `ls-files`,
turning those worktree reads into full scans in large repositories.

Read the raw effective `core.fsmonitor` value and preserve it only when
Git interprets it as true and advertises built-in daemon support through
`git version --build-options`. Query uncommon boolean spellings back
through Git using the exact effective value. Unset, false, helper paths,
malformed values, probe failures, and unsupported Git builds continue to
force `core.fsmonitor=false`.

Centralize this policy in `git-utils` while keeping process execution in
the existing local and workspace-command adapters. Probe once per
worktree workflow and reuse the result for its Git commands, including
the TUI `/diff` path. Metadata-only commands and repository discovery
remain disabled without probing. Each probe and requested Git process
keeps its own existing timeout, and the decision is not cached because
layered and conditional Git configuration can change while Codex runs.

---------

Co-authored-by: Chris Bookholt <bookholt@openai.com>
)

## Summary
- request `includeAppsNeedingAuth=true` when installing remote plugins
- return backend-provided `app_ids_needing_auth` from the remote install
client
- use those app IDs to populate `appsNeedingAuth` without refetching
accessible apps, with fallback for older responses

## Testing
- `just fmt`
- `just test -p codex-app-server`
- `just test -p codex-core-plugins`
- real app-server install/uninstall check with Notion remote plugin
- subagent review found no blocking issues
## Summary

- return installed `workspace-directory` remote plugins by default in
`plugin/installed`
- keep shared-with-me installed plugins gated behind `plugin_sharing`
- filter remote installed plugin marketplaces by canonical marketplace
name instead of coarse workspace scope

## Validation

- `just fmt`
- `just test -p codex-core-plugins`
- `just test -p codex-app-server`
- `just fix -p codex-core-plugins`
- `just fix -p codex-app-server`
- `$xin-build` targeted verification:
- `just test -p codex-core-plugins
build_remote_installed_plugin_marketplaces_from_cache_filters_by_marketplace_name`
- `just test -p codex-app-server
plugin_installed_includes_workspace_directory_without_plugin_sharing`
- `just test -p codex-app-server
plugin_installed_includes_remote_shared_with_me_plugins`
- `just test -p codex-app-server
plugin_list_omits_shared_with_me_kind_when_plugin_sharing_disabled`
## Why

Multi-agent v2 identifies agents by canonical paths, but its tool
handlers still emitted the larger legacy collaboration begin/end events
built around nickname and role metadata. App-server, rollout-trace,
analytics, and TUI consumers therefore lacked one compact path-based
completion signal that behaved consistently across live events and
replay.

The TUI also needs a bounded `/agent` status surface for v2 agents. It
should use recent local activity for previews, refresh liveness without
loading full histories, and keep the legacy picker available when no
path-backed v2 agent is known.

## What changed

- Replace the v2 `spawn_agent`, `send_message`, `followup_task`, and
`interrupt_agent` legacy lifecycle emissions with a success-only
`SubAgentActivity` event. The event records the tool call ID, occurrence
time, affected thread, canonical agent path, and `started`,
`interacted`, or `interrupted` kind.
- Expose the activity as a completion-only app-server v2
`subAgentActivity` thread item in live notifications and reconstructed
history, regenerate the protocol schemas, and count it in sub-agent tool
analytics.
- Track canonical paths from live activity and loaded-thread metadata in
the TUI, and render the activity in live and replayed transcripts.
- Make `/agent` list running path-backed agents with summaries from
bounded local event buffers. Each summary is capped at 240 graphemes,
the scan is capped at six recent items, only the last three wrapped
lines are shown, and command output is omitted. Liveness falls back to
metadata-only `thread/read` when local turn state is unavailable.
- Persist the activity as a terminal rollout-trace runtime payload and
reduce it to the corresponding spawn, send, follow-up, or close
interaction edge. `interrupt_agent` is classified as a close-edge
operation.
- Preserve the legacy picker when no path-backed v2 agent is known.

## Compatibility

App-server v2 clients that consumed `collabAgentToolCall` begin/end
pairs for these tools must handle the new completion-only
`subAgentActivity` item. Legacy v1 collaboration behavior is unchanged.

## Screenshot

<img width="684" height="288" alt="Screenshot 2026-06-08 at 15 40 47"
src="https://github.com/user-attachments/assets/194b3cd0-619d-45fb-b587-cf3e2b1b8a1d"
/>

## Testing

- `just test -p codex-app-server-protocol`
- `just test -p codex-rollout-trace`
- Added focused coverage for activity analytics, terminal trace
serialization, spawn-edge reduction, `interrupt_agent` classification,
TUI status rendering without aggregated command output, and clearing
stale running state after a completed turn.
…penai#27166)

## Why

PR openai#27007 moved MultiAgentV2 interruption reporting from the legacy
collaboration close event to `SubAgentActivity::Interrupted`.
App-server's missing-thread cleanup still ran only for the legacy event,
so an interrupted child that had already been unloaded could remain
marked as loaded and running in `ThreadWatchManager`. That leaves thread
status and running-turn accounting stale, including the count used
during graceful shutdown.

## What changed

- Handle `SubAgentActivity::Interrupted` separately in app-server event
processing.
- Remove the child's thread watch when `ThreadManager` no longer has
that thread.
- Continue forwarding the same completed sub-agent activity notification
to clients.

## Testing

- Added a regression test that starts with a running watch for an
unloaded child, applies the interrupted activity event, and verifies the
watch is removed, the running count returns to zero, and the client
notification is still emitted.
## Why

The PR babysitter could surface inline comments from a GitHub review
that was still in the `PENDING` state. That allowed Codex to start
acting on feedback before the reviewer submitted it.

## What changed

- Correlate inline comments with their parent review and ignore pending
reviews and their comments.
- Remove pending review IDs from saved watcher state so the feedback
surfaces normally after publication.
- Update the skill instructions and add regression coverage for the
draft-to-published transition.

## Validation

- `python3 -m pytest
.codex/skills/babysit-pr/scripts/test_gh_pr_watch.py`
- Skill package validation with `quick_validate.py`
- Live verification on openai#26835: the draft comment stayed hidden and
surfaced after the review was submitted.
## Summary

- avoid acquiring SQLite's writer slot when the singleton backfill row
already exists
- preserve race-safe repair when the row is missing
- add regressions for writer contention and missing-row repair

## Why

State runtime initialization and backfill-state reads previously
executed
`INSERT ... ON CONFLICT DO NOTHING` even in the steady state. SQLite
still
enters the writer path for that statement, so TUI and app-server startup
could
wait behind another writer for up to the configured five-second busy
timeout.

## Validation

- `just test -p codex-state` (134 tests passed)
- `just fix -p codex-state`
- `just fmt`
## Summary

- reuse the history-bearing `StoredThread` loaded while probing for a
running thread
- avoid rereading and reparsing the rollout when that probe finds no
active process
- reload after shutting down a loaded thread because shutdown may flush
newer rollout items
- add a regression test that verifies cold resume performs one
history-bearing store read

## Problem

`thread/resume` first reads the persisted thread with history while
checking whether the thread is
already running. When no running process exists, cold resume currently
falls through to
`resume_thread_from_rollout`, which reads and parses the same history
again.

That duplicate work grows with rollout size and remains on the
synchronous resume path even when
the caller requests `excludeTurns`.

## Background

The duplicate read was introduced by openai#24528, which fixed resume
overrides for idle cached
threads. To support resumes specified by rollout path,
`resume_running_thread` began loading the
stored thread with history so it could resolve the canonical thread ID
and determine whether a
cached `CodexThread` was already loaded.

That history is needed when the loaded-thread path handles the request.
On a cold miss, however,
the function's boolean result could only report that no loaded thread
handled the request. It
discarded the history-bearing `StoredThread`, and the normal cold-resume
path immediately loaded
and parsed the same rollout again.

This change preserves the idle cached-thread behavior from openai#24528 while
allowing the cold-resume
path to reuse the probe result.

## Performance

I benchmarked real retained rollouts using isolated `CODEX_HOME`
directories, explicit rollout
paths, debug builds of the commit and its exact parent, and alternating
parent/patch order. The
table below uses `thread/resume` with `excludeTurns: true`; response
payload sizes were identical.

| Rollout size | Records | Parent median | Patch median | Median paired
saving |
| ---: | ---: | ---: | ---: | ---: |
| 6 MB | 3,574 | 541 ms | 441 ms | 132 ms |
| 30 MB | 15,220 | 1.505 s | 1.041 s | 701 ms |
| 60 MB | 31,453 | 2.644 s | 1.742 s | 970 ms |
| 149 MB | 100,874 | 10.506 s | 7.156 s | 3.350 s |
| 559 MB | 259,734 | 27.759 s | 16.725 s | 9.836 s |

The absolute saving increases with thread size, as expected when
removing one complete JSONL
history read and parse. Total resume time is also content-dependent, so
the relationship is not
perfectly linear.

I also tested full-history resume with `excludeTurns: false`. The
response payload was
byte-identical between variants, and the same size-dependent improvement
remained visible:

| Rollout size | Parent median | Patch median | Median paired saving |
| ---: | ---: | ---: | ---: |
| 6 MB | 1.052 s | 904 ms | 270 ms |
| 30 MB | 2.667 s | 1.762 s | 924 ms |
| 60 MB | 8.464 s | 6.272 s | 3.680 s |
| 149 MB | 26.719 s | 12.118 s | 14.601 s |
| 559 MB | 40.359 s | 25.475 s | 16.590 s |

## Validation

- `just test -p codex-app-server
cold_thread_resume_reuses_non_local_history_probe`
- `just fix -p codex-app-server -p codex-thread-store`
- `just fmt`
…#22879)

This changes the `/review` escape path so `Esc` no longer behaves like
the normal queued-follow-up interrupt flow while a review is running.
Steering is not currently supported in `/review` mode, without this
change users are able to attempt a steer but it leads to a crash (see
openai#22815). If the user has already tried to send additional guidance
during `/review`, the TUI now keeps the review running and shows a
warning that steer messages are not supported in that mode, while still
pointing users to `Ctrl+C` if they actually want to cancel. It also adds
regression coverage for the review-specific warning behavior. When users
do cancel with Ctrl+C during /review, the TUI now tolerates the
active-turn race that can happen during review handoff, and any queued
steer messages are restored to the composer instead of being discarded.

- Special-case `Esc` during an active `/review` when follow-up steer
input is pending or has already been deferred.
- Show a clear warning instead of interrupting the running review.
- Make the Ctrl+C cancel path during /review resilient to active-turn
races, while preserving any queued steer text by restoring it to the
composer.
- Add review-mode test coverage for the warning path.

## Testing

1. Start a `/review` with a diff large enough that the review stays
active for more than a few seconds.

2. While the review is still running, type a follow-up / steer message,
submit it, and then press `Esc`.
   Before: `Esc` causes the TUI to close abruptly.  
After: the review keeps running and the transcript shows a warning that
steer messages are not supported during `/review`, with guidance to use
`Ctrl+C` if you want to cancel.

3. Press `Ctrl+C` if you actually want to stop the review.  
Before: (after restarting the test since Pt. 2 crashed) this is the
intentional cancellation path.
After: this remains the intentional cancellation path, and any queued
follow-up steer text is restored to the composer instead of being lost.
   
## Note:
`/review` mode explicitly does not support steering at this time (as
noted in `turn_processer.rs`, if we want to explore that in the future
this code will need to be modified). This change keeps unsupported steer
attempts from crashing the TUI and preserves queued follow-up text if
the user cancels with Ctrl+C.
…27173)

## Why

Multi-agent v2 sub-agents are owned and coordinated by their parent
agent. Allowing an app-server client to start or steer turns on a
spawned child bypasses the multi-agent messaging path and creates a
second, conflicting source of work for that sub-agent.

## What changed

- Reject direct `turn/start` and `turn/steer` requests targeting
multi-agent v2 thread-spawn sub-agents.
- Identify these targets using both the thread's resolved multi-agent
version and its `SubAgentSource::ThreadSpawn` session source, leaving
root threads, v1 agents, and other sub-agent types unchanged.
- Return a consistent invalid-request error before validating or
applying the submitted input.

## Testing

- Added an app-server integration test that spawns a real multi-agent v2
child and verifies that direct `turn/start` and `turn/steer` requests
are rejected.
## Why

CCA is moving toward a split runtime where the orchestrator may not have
a filesystem, while executors can expose preinstalled plugins and
skills. A thread therefore needs to select capabilities without asking
app-server or core to interpret executor-owned paths through the
orchestrator's filesystem.

The longer-term model is broader than executor skills:

- A plugin is a bundle of skills, MCP servers, connectors/apps, and
hooks.
- A plugin root can be local, executor-owned, or hosted by a backend.
- Components inside one plugin can use different access and execution
mechanisms. A skill may be read from a filesystem or through backend
tools; an HTTP MCP server can run without an executor; a stdio MCP
server or hook needs an execution environment.
- Core should carry generic extension initialization data. The extension
that owns a component should discover it, expose it to the model, and
invoke it through the appropriate runtime.

This PR establishes that architecture through one complete vertical:
selecting a root on an executor, discovering the skills beneath it,
exposing those skills to the model, and reading an explicitly invoked
`SKILL.md` through the same executor.

## Contract

`thread/start` gains an experimental `selectedCapabilityRoots` field:

```json
{
  "selectedCapabilityRoots": [
    {
      "id": "deploy-plugin@1",
      "location": {
        "type": "environment",
        "environmentId": "workspace",
        "path": "/opt/codex/plugins/deploy"
      }
    }
  ]
}
```

The root is intentionally not classified as a "plugin" or "skill" in the
API. It can point at a standalone skill, a directory containing several
skills, or a plugin containing skills and other components. This PR only
teaches the skills extension how to consume it; later extensions can
resolve MCP, connector, and hook components from the same selection.

The platform-supplied `id` is stable selection identity. The location
says which runtime owns the root and gives that runtime an opaque path.
App-server does not inspect or canonicalize the path.

## What changed

### Generic thread extension initialization

App-server converts selected roots into `ExtensionDataInit`. Core
carries that generic initialization value until the final thread ID is
known, then creates thread-scoped `ExtensionData` before lifecycle
contributors run.

This keeps `Session` and core independent of the capability-selection
contract. The initialization value is consumed during construction; it
is not retained as another long-lived `Session` field.

### Executor-backed skills

The skills extension now owns an `ExecutorSkillProvider` that:

- resolves the selected environment through `EnvironmentManager`
- discovers, canonicalizes, and reads skills through that environment's
`ExecutorFileSystem`
- contributes the bounded selected-skill catalog as stable developer
context
- reads an explicitly invoked skill body through the authority that
listed it
- warns when an environment or root is unavailable
- never falls back to the orchestrator filesystem for an executor-owned
root

Skill catalog and instruction fragments have hard byte bounds, which
also bound them below the 10K-token per-item context limit. If a
selected executor skill has the same name as a legacy local skill, the
executor selection owns that invocation and the local body is not
injected a second time.

Existing local and bundled skill loading remains in place. Omitting
`selectedCapabilityRoots` therefore preserves current local-only
behavior.

## Current semantics

- Only environment-owned locations are represented in this first
contract.
- Roots are resolved by the destination extension, not by app-server or
core.
- An unavailable executor or invalid root produces a warning and no
capabilities from that root; it does not trigger a local-filesystem
fallback.
- Selection applies to a newly started active thread.
- MCP servers, connectors, and hooks beneath a selected plugin root are
not activated yet.
- Selection is not yet persisted or inherited across resume, fork, or
subagent creation. Existing local capabilities continue to behave as
they do today in those flows.

## Planned vertical follow-ups

1. **Hosted HTTP MCP:** add an extension-backed HTTP MCP source that
works without an executor, then replace the special-purpose MCP plugins
loader with that implementation.
2. **Executor MCP:** register and execute stdio MCP servers through the
environment that owns the selected plugin root.
3. **Backend skills:** add a hosted skill source whose catalog and
bodies are accessed through extension tools rather than a filesystem.
4. **Connectors and hooks:** activate those components through their
owning extensions, using the same selected-root boundary and
component-specific runtime.
5. **Durable selection:** define the desired-selection lifecycle,
persist it, and make resume, fork, and subagent inheritance explicit
rather than accidental.
6. **Local convergence:** incrementally route existing local plugin,
skill, and MCP loading through the same extension model while preserving
current local behavior.

Each follow-up remains reviewable as an end-to-end capability. The
platform selects roots, generic thread extension data carries the
selection, and the owning extension resolves and operates its component.

## Verification

Coverage added for:

- app-server end-to-end discovery and explicit invocation of a skill
inside an executor-selected plugin root
- exclusive invocation when a selected executor skill collides with a
local skill name
- executor filesystem authority for discovery, canonicalization, and
reads
- thread extension initialization before lifecycle contributors run
- stable executor catalog context, explicit invocation, context
rebuilding, hidden skills, and preserved host/remote catalog behavior

Targeted protocol, core-skills, skills-extension, core lifecycle, and
app-server executor-skill tests were run during development.
## Why

`codex-extension-api` defines contracts shared by extension crates and
their hosts, but it had no direct test suite. Host and feature tests
cover downstream behavior, while regressions in the API crate's own
typed state, registry ordering, and capability adapters could go
unnoticed.

## What

- Add public-surface integration tests for `ExtensionData`, including
concurrent initialization and poison recovery.
- Cover contributor registration order, approval short-circuiting, event
sink retention, no-op response injection, and closure-based agent
spawning.
- Add the test-only dependencies used by the suite.

## Validation

- `just test -p codex-extension-api`
- `just argument-comment-lint -p codex-extension-api`
- `just bazel-lock-check`
## Why
- `ThreadSource` currently defines a closed set of core-owned values
- Product features also create threads for background or scheduled work
- Adding every product-specific value to the core enum would require
repeated `codex-rs` protocol changes
- Feature-backed values let product callers provide precise attribution
while preserving the existing core classifications

## What Changed
- Adds `ThreadSource::Feature(String)` for app-owned thread source
values
- Represents all app-server v2 thread sources as scalar strings, so a
feature source is supplied as `"automation"`
- Persists and emits the feature's plain string label, so `"automation"`
produces `thread_source="automation"` in analytics
- Keeps `user`, `subagent`, and `memory_consolidation` as explicit
core-owned values and regenerates the app-server schemas and TypeScript
bindings

## Verification
- `just write-app-server-schema`
- `cargo check --workspace`
- `just test -p codex-protocol
feature_thread_source_serializes_as_its_app_owned_label`
- `just test -p codex-app-server-protocol
thread_sources_round_trip_as_scalar_labels`
- `cargo test -p codex-analytics
thread_initialized_event_serializes_expected_shape`
- `just fmt`
## Why

`just test` currently uses the CI-oriented nextest profile, which
serializes app-server integration tests even on developer machines that
can run several safely. Bounded local parallelism substantially shortens
this common iteration loop without changing CI behavior.

Eight-worker experiments were faster, but keeping them reliable required
relaxing several test deadlines. Four workers for integration tests is a
solid tradeoff that speeds up local testing without needing to change
test logic.

## What changed

- Add a `local` nextest profile that inherits the existing defaults.
- Allow up to four app-server integration tests to run concurrently
under that profile.
- Make `just test` select the local profile on Unix and Windows.
- Keep the default CI profile serialized and leave all test deadlines
unchanged.

The tests use separate processes, randomized temporary `CODEX_HOME`
directories, and ephemeral ports. The remaining shared constraints are
system resources; each app-server also uses a multi-thread Tokio
runtime, and fuzzy-search tests can create additional worker threads, so
the local cap remains intentionally conservative.

## Performance and validation

All measurements below are warm, execution-only app-server runs with
nextest retries disabled.

On the current rebased branch, an AMD EPYC 7763 machine with 16 logical
CPUs and 62 GiB RAM completed three consecutive runs:

| Run | Nextest time | Wall time | Result |
| --- | ---: | ---: | --- |
| 1 | 142.941s | 145.17s | 836/836 passed |
| 2 | 143.402s | 145.59s | 836/836 passed |
| 3 | 142.870s | 145.08s | 836/836 passed |

The mean wall time was 145.28s. The slow-inventory, approval replay, and
zsh-fork tests all passed with their original deadlines.

Earlier measurements on the same Linux machine, before the suite grew,
showed the scaling that motivated the change:

| App-server concurrency | Nextest time | Result |
| --- | ---: | --- |
| 1 | 369.5s | 572/572 passed |
| 2 | 194.5s | 572/572 passed |
| 4 | 111.0s mean over 3 runs | 3/3 clean |

Four workers reduced that execution time by about 70%, a roughly 3.3x
speedup over serialization.
## Summary
- reuse the MITM HTTPS serving path for raw SOCKS5 TCP streams
- route limited-mode and hooked SOCKS5 TCP requests through MITM before
dialing upstream
- keep SOCKS5 UDP limited-mode behavior unchanged

## Validation
- `just fmt`
- `just test -p codex-network-proxy`
- `just fix -p codex-network-proxy`
- `git diff --check`
## Why

Users have indicated that they want an agent to be able to create a new
goal for itself after completing the previous goal. Currently, that's
not possible because agents cannot overwrite an existing goal even if
it's complete. This PR removes this limitation and allows `create_goal`
to overwrite an existing goal if it is in the `complete` state.

## What changed

`create_goal` now replaces the existing goal only when its status is
`complete`. The replacement is performed atomically in the goal store,
creates a fresh active goal with reset usage, and continues to reject
creation while any unfinished goal exists. App server clients see a
single `thread/goal/updated` event when the previous goal is replaced
with the new one.

The tool description and error message now reflect these semantics.

## What didn't change

Agents are not allowed to create a new goal (overwrite their existing
goal) if an existing goal is still active, blocked, paused, or in any
other state other than "completed".
## Why

The TUI still reached through `app-server-client::legacy_core` for
thread-name normalization and project-instruction filename details. In
particular, checking the TUI's local filesystem for `/init` is incorrect
for remote app-server sessions, where the server owns the working
directory and instruction discovery.

## What changed

- use the instruction source paths supplied by the app server to decide
whether `/init` should avoid overwriting project instructions
- keep the small thread-name normalization helper local to the TUI
- remove the now-unused instruction filename constants, utility module,
and other unused `legacy_core` re-exports
- make status helper tests independent of concrete instruction filenames

## Verification

- `just test -p codex-app-server-client`
- `just test -p codex-tui
slash_init_skips_when_project_instructions_are_loaded`
- `just test -p codex-tui` ran 2,799 tests; 2,797 passed and two
unrelated guardian feature-flag tests failed reproducibly in untouched
code

### Manual test

Started an app server over WebSocket with a remote workspace containing
`AGENTS.md`, then connected the TUI using `--remote`. After confirming
`thread/start` returned the file in `instructionSources`, deleted
`AGENTS.md` and ran `/init` in the existing session.

The TUI still reported that project instructions already existed and
skipped `/init`. The trace contained no `turn/start` request, confirming
the decision came from app-server session state rather than a new
client-local filesystem check.
## Why

Goal continuation turns are emitted by the existing runtime as separate
physical turns. The Python SDK needs private thread-scoped routing
before it can present those notifications as one logical operation,
without changing ordinary turn routing or the app-server protocol.

## What

- add private goal operation state and thread-scoped notification
routing
- add internal wrappers for the existing `thread/goal/clear` and
`thread/goal/set` RPCs
- include existing goal notifications in the SDK notification union
- preserve ordinary turn-ID routing unchanged
- add focused routing coverage

This PR does not expose a public goal API. It is the first PR in the
Python goal operations stack.

## Test plan

- online CI, including the Python SDK suite
- focused typed-notification routing coverage
## Stack

- Base: openai#27184
- This PR is the second vertical and should be reviewed against
`jif/external-plugins-1`, not `main`.

## Why

CCA is moving toward a split runtime where the orchestrator may have no
filesystem or executor, but it still needs to activate remotely hosted
plugin components. HTTP MCP servers are the simplest complete example:
they need configuration and host authentication, but they do not need an
executor process.

The Apps MCP endpoint is currently synthesized by a special-purpose
loader inside the MCP runtime. That works locally, but it leaves hosted
MCP activation outside the extension model being established in openai#27184.
It also makes the Apps path a poor foundation for plugins whose skills,
MCP servers, connectors, and hooks may come from different sources or
execute in different places.

This PR moves that one behavior behind an extension-owned contribution
while preserving the existing local fallback. It deliberately does not
introduce a generic plugin activation framework.

## What changed

### MCP extension contribution

`codex-extension-api` gains an ordered `McpServerContributor` contract.
A contributor returns typed `Set` or `Remove` overlays for MCP server
configuration; later contributors win for the names they own.

The contract stays at the existing MCP configuration boundary.
Extensions do not create a second connection manager or transport
abstraction.

### Hosted Apps MCP extension

A new `codex-mcp-extension` contributes the reserved `codex_apps` server
from the existing Apps feature, ChatGPT base URL, path override, and
product SKU configuration.

When `apps_mcp_path_override` is enabled for `https://chatgpt.com`, the
resulting streamable HTTP endpoint is
`https://chatgpt.com/backend-api/ps/mcp`. The existing ChatGPT-auth gate
remains authoritative, so this server can run in an orchestrator-only
process without being exposed for API-key sessions.

### One resolved runtime view

`McpManager` now distinguishes three views:

- **configured:** config- and plugin-backed servers before extension
overlays;
- **runtime:** configured servers plus host-installed extension
contributions;
- **effective:** runtime servers after auth gating and compatibility
built-ins.

App-server installs the hosted MCP extension and uses the runtime view
for thread startup, refresh, status, threadless resource reads,
connector discovery, and MCP OAuth lookup. This keeps
`mcpServer/oauth/login` consistent with the servers exposed by the other
MCP APIs. The hosted Apps server itself continues to use existing
ChatGPT host authentication rather than MCP OAuth.

## Compatibility

Hosts that do not install the MCP extension retain the existing Apps MCP
synthesis path. This preserves current local-only, CLI, and
standalone-host behavior while app-server exercises the extension path.

Disabling Apps removes the reserved `codex_apps` entry, and losing
ChatGPT auth removes it from the effective runtime view. Executor
availability is not consulted for this HTTP transport.

## Follow-ups

The next vertical will resolve a manifest-declared stdio MCP server from
an executor-selected plugin root and execute it in the environment that
owns that root. Later verticals can add backend-owned skills, connector
metadata, hooks, durable selection semantics, and incremental local
convergence without changing the component-specific runtime boundaries
introduced here.

## Verification

Focused coverage was added for:

- contributing the hosted Apps MCP at `/backend-api/ps/mcp` without an
executor;
- requiring ChatGPT auth in the effective runtime view;
- removing a reserved configured Apps server when the Apps feature is
disabled.

`cargo check -p codex-app-server -p codex-mcp-extension -p
codex-extension-api -p codex-mcp` passed. Tests and Clippy were not run
locally under the current development instruction; CI provides the full
validation pass.
## Why

Global instruction behavior spans thread creation, resume, forks,
subagents, and compaction. Characterization coverage is needed before
changing those semantics so preserved history can be distinguished from
newly loaded configuration.

## What changed

- Extends the existing `agents_md` suite with fresh-thread, warning,
resume, fork, and subagent lifecycle coverage.
- Extends the existing `compact` suite with manual, mid-turn, and
remote-v2 compaction coverage.
- Asserts rendered instruction fragments, reported source paths, and
structured request history before and after instruction-file mutations.
…6713)

## Why

Persisted MCP OAuth credentials were reported as authenticated whenever
a credential record existed. An expired token without a usable refresh
token could therefore appear as `OAuth` even though startup could not
authenticate with it, leaving users with a misleading status instead of
a login prompt.

## What changed

- Classify stored OAuth credentials as missing, usable, or requiring
authorization.
- Reuse the existing refresh window so near-expiry credentials without a
refresh path are also treated as logged out.
- Validate required credential fields before reporting OAuth
authentication.
- Add unit coverage for credential usability and integration coverage
for expired, unexpired, and refreshable persisted credentials.

## Validation

- `just test -p codex-rmcp-client`
## Why

A long-running unified exec process started with `tty: false` could not
be interrupted via `write_stdin`: ordinary non-TTY stdin writes are
rejected once stdin is closed, but an exact U+0003 payload should still
map to a process interrupt. The interrupt should flow through the same
process lifecycle path as a real signal so Codex preserves
process-reported output and exit metadata instead of fabricating a
Ctrl-C exit code or tearing down the session early.

## What Changed

- Add `process/signal` to exec-server with `ProcessSignal::Interrupt`
and an empty response.
- Add a non-consuming `ProcessHandle::signal` path for spawned
processes; on Unix it sends SIGINT to the process group and leaves
terminate/hard-kill unchanged.
- Route non-TTY U+0003 `write_stdin` through `process.signal(...)`
instead of `terminate`, then let the normal post-write collection path
drain output and observe exit.
- Add exec-server coverage where a shell `trap INT` handler prints the
signal and exits with its own code.
- Add unified exec coverage where a `tty: false` process traps SIGINT,
emits output, and exits with its own code.

## Validation

- `just test -p codex-exec-server
exec_process_signal_interrupts_process`
- `just test -p codex-exec-server`
- `just test -p codex-core
write_stdin_ctrl_c_interrupts_non_tty_session`
## Why

The realtime frontend model and the backing Codex thread should present
one coherent assistant. Raw typed messages, steers, and worker reports
belong to the orchestrator; the frontend model should receive the
orchestrator's user-facing result rather than a second copy of those
inputs.

Today normal `turn/start` input is automatically inserted into the
realtime conversation, while `turn/steer` is not. Besides creating
inconsistent context, this can make the frontend model react
independently before Codex has produced the response it should speak.

## What changed

- Remove automatic accepted-user-input mirroring into realtime
- Remove the mirror-only echo-suppression flag and dead V2 prefix helper
- Preserve explicit app-to-realtime text injection and FEM-to-Codex
delegation
- Replace the positive mirror tests and obsolete snapshots with a
negative routing regression test

## Test plan

- `cargo test -p codex-core
conversation_user_text_turn_is_not_sent_to_realtime`
- `cargo test -p codex-core
conversation_startup_context_is_truncated_and_sent_once_per_start`
- `cargo test -p codex-core inbound_handoff_request_starts_turn`
## Why

The Python SDK must treat the runtime's initial goal turn and its
continuations as one logical operation. That requires a private
lifecycle engine before the public API can return the existing turn
handle and result types.

## What

- start goals by composing the existing clear/set goal RPCs
- enforce persisted, idle threads and a bounded startup handshake
- coalesce continuation notifications under a stable logical turn ID
- aggregate items, usage, timing, and terminal status
- support rollover-aware steering, interruption, cancellation, and
cleanup
- provide equivalent sync and async internals

This is the second PR in the stack and intentionally adds no public API.

## Test plan

- online CI, including the Python SDK suite
- behavioral coverage is added in the following two stack PRs
## Summary
- Retry transient streamable HTTP failures during RMCP startup when the
failure happens while sending the initialize request.
- Retry transient streamable HTTP failures for tools/list, which is
read-only and safe to replay.
- Cover both retryable HTTP statuses and request-layer failures where no
HTTP status is returned.
- Surface retryable HTTP statuses from the streamable HTTP adapter as
typed client errors.
- Add integration coverage for initialize retry, tools/list retry,
no-status request failure retry, and non-retryable initialize status.

## Root cause
The observed codex_apps failures can happen before normal tool
execution: RMCP startup fails while sending initialize, or the first
read-only tools/list fails after startup. Retrying hosted_apps_bridge
tools/call would not cover initialize and would risk replaying
side-effecting tool calls. This change retries the streamable HTTP
handshake itself, recreates the transport between initialize attempts,
and retries only tools/list among post-initialize service operations.

## Validation
- cargo fmt --package codex-rmcp-client
- cargo test -p codex-rmcp-client --test streamable_http_recovery
…ai#27257)

## Summary

- order `McpConnectionManager` methods by visibility, with the primary
constructor and public API first
- restrict `list_available_server_infos` to `codex-mcp`
- make `new_uninitialized` a private test-only helper

## Why

The manager exposed methods that are only used inside `codex-mcp` or its
unit tests. Tightening those methods keeps the exported API intentional,
while the new ordering makes the supported surface easier to scan.

## Validation

- `just fmt`
- `git diff --check`
- local tests not run; relying on CI
wallentx and others added 2 commits June 10, 2026 01:15
…nt/wallentx_termux-target_from_release_0.140.0_f88907c1fa2c
@unemployabot
unemployabot Bot requested a review from wallentx June 10, 2026 06:16
@unemployabot unemployabot Bot added checkpoint Checkpoint merge termux-release Termux release automation labels Jun 10, 2026
@wallentx
wallentx merged commit ef74b49 into wallentx/termux-target Jun 10, 2026
1 check passed
@wallentx
wallentx deleted the checkpoint/wallentx_termux-target_from_release_0.140.0_f88907c1fa2c branch June 10, 2026 06:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

checkpoint Checkpoint merge termux-release Termux release automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.