Previous to this change, `determine_action()` would
1. check if `program` is associated with a skill
2. if so, check if `program` is in `execve_session_approvals` to see
whether the user needs to be prompted
This PR flips the order of these checks to try to set us up so that
"session approvals" are always consulted first (which should soon extend
to include session approvals derived from `prefix_rule()`s, as well).
Though to make the new ordering work, we need to record any relevant
metadata to associate with the approval, which in the case of a
skill-based approval is the `SkillMetadata` so that we can derive the
`PermissionProfile` to include with the escalation. (Though as noted by
the `TODO`, this `PermissionProfile` is not honored yet.)
The new `ExecveSessionApproval` struct is used to retain the necessary
metadata.
## What Changed
- Replace the `execve_session_approvals` `HashSet` with a map that
stores an `ExecveSessionApproval` alongside each approved `program`.
- When a user chooses `ApprovedForSession` for a skill script, capture
the matched `SkillMetadata` in the session approval entry.
- Consult that cache before re-running `find_skill()`, and reuse the
originally approved skill metadata and permission profile when allowing
later execve callbacks in the same session.
## Summary
- allow `request_user_input` in Default collaboration mode as well as
Plan
- update the Default-mode instructions to prefer assumptions first and
use `request_user_input` only when a question is unavoidable
- update request_user_input and app-server tests to match the new
Default-mode behavior
- refactor collaboration-mode availability plumbing into
`CollaborationModesConfig` for future mode-related flags
## Codex author
`codex resume 019c9124-ed28-7c13-96c6-b916b1c97d49`
This reverts commit daf0f03ac8.
# External (non-OpenAI) Pull Request Requirements
Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md
If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.
Include a link to a bug report or enhancement request.
# External (non-OpenAI) Pull Request Requirements
Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md
If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.
Include a link to a bug report or enhancement request.
---------
Co-authored-by: Codex <noreply@openai.com>
Adds a new v2 app-server API for a client to be able to unsubscribe to a
thread:
- New RPC method: `thread/unsubscribe`
- New server notification: `thread/closed`
Today clients can start/resume/archive threads, but there wasn’t a way
to explicitly unload a live thread from memory without archiving it.
With `thread/unsubscribe`, a client can indicate it is no longer
actively working with a live Thread. If this is the only client
subscribed to that given thread, the thread will be automatically closed
by app-server, at which point the server will send `thread/closed` and
`thread/status/changed` with `status: notLoaded` notifications.
This gives clients a way to prevent long-running app-server processes
from accumulating too many thread (and related) objects in memory.
Closed threads will also be removed from `thread/loaded/list`.
## Why
The prior
`turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2`
assertion was brittle under Bazel: command approval payloads in the test
could include environment-dependent wrapper/command formatting
differences, which makes exact command-string matching flaky even when
behavior is correct.
(This regression was knowingly introduced in
https://github.com/openai/codex/pull/12800, but it was urgent to land
that PR.)
## What changed
- Hardened
`turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2`
in
[`turn_start_zsh_fork.rs`](https://github.com/openai/codex/blob/main/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs):
- Replaced strict `approval_command.starts_with("/bin/rm")` checks with
intent-based subcommand matching.
- Subcommand approvals are now recognized by file-target semantics
(`first.txt` or `second.txt`) plus `rm` intent.
- Parent approval recognition is now more tolerant of command-format
differences while still requiring a definitive parent command context.
- Uses a defensive loop that waits for all target subcommand decisions
and the parent approval request.
- Preserved the existing regression and unit test fixes from earlier
commits in `unix_escalation.rs` and `skill_approval.rs`.
## Verification
- Ran the zsh fork subcommand decline regression under this change:
-
`turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2`
- Confirmed the test is now robust against approval-command-string
variation instead of hardcoding one expected command shape.
Update realtime debug logs to include the actual text payloads in both
input and output paths.
- In `core/src/realtime_conversation.rs`:
- `handle_start`: add extracted assistant text output to the
`[realtime-text]` debug log.
- `handle_text`: add incoming text input (`params.text`) to the
`[realtime-text]` debug log.
No tests were run (per request).
Previously, clients would call `thread/start` with dynamic_tools set,
and when a model invokes a dynamic tool, it would just make the
server->client `item/tool/call` request and wait for the client's
response to complete the tool call. This works, but it doesn't have an
`item/started` or `item/completed` event.
Now we are doing this:
- [new] emit `item/started` with `DynamicToolCall` populated with the
call arguments
- send an `item/tool/call` server request
- [new] once the client responds, emit `item/completed` with
`DynamicToolCall` populated with the response.
Also, with `persistExtendedHistory: true`, dynamic tool calls are now
reconstructable in `thread/read` and `thread/resume` as
`ThreadItem::DynamicToolCall`.
We propagate the session ID when sending requests for inference but we
don't do the same for compaction requests. This makes it hard to link
compaction requests to their session for debugging purposes
## Why
Zsh fork execution was still able to bypass the `WorkspaceWrite` model
in edge cases because the fork path reconstructed command execution
without preserving sandbox wrappers, and command extraction only
accepted shell invocations in a narrow positional shape. This can allow
commands to run with broader filesystem access than expected, which
breaks the sandbox safety model.
## What changed
- Preserved the sandboxed `ExecRequest` produced by
`attempt.env_for(...)` when entering the zsh fork path in
[`unix_escalation.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs).
- Updated `CoreShellCommandExecutor` to execute the sandboxed command
and working directory captured from `attempt.env_for(...)`, instead of
re-running a freshly reconstructed shell command.
- Made zsh-fork script extraction robust to wrapped invocations by
scanning command arguments for `-c`/`-lc` rather than only matching the
first positional form.
- Added unit tests in `unix_escalation.rs` to lock in wrapper-tolerant
parsing behavior and keep unsupported shell forms rejected.
- Tightened the regression in
[`skill_approval.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/tests/suite/skill_approval.rs):
- `shell_zsh_fork_still_enforces_workspace_write_sandbox` now uses an
explicit `WorkspaceWrite` policy with `exclude_tmpdir_env_var: true` and
`exclude_slash_tmp: true`.
- The test attempts to write to `/tmp/...`, which is only reliably
outside writable roots with those explicit exclusions set.
## Verification
- Added and passed the new unit tests around `extract_shell_script`
parsing behavior with wrapped command shapes.
- `extract_shell_script_supports_wrapped_command_prefixes`
- `extract_shell_script_rejects_unsupported_shell_invocation`
- Verified the regression with the focused integration test:
`shell_zsh_fork_still_enforces_workspace_write_sandbox`.
## Manual Testing
Prior to this change, if I ran Codex via:
```
just codex --config zsh_path=/Users/mbolin/code/codex2/codex-rs/app-server/tests/suite/zsh --enable shell_zsh_fork
```
and asked:
```
what is the output of /bin/ps
```
it would run it, even though the default sandbox should prevent the
agent from running `/bin/ps` because it is setuid on MacOS.
But with this change, I now see the expected failure because it is
blocked by the sandbox:
```
/bin/ps exited with status 1 and produced no output in this environment.
```