Codex CLI 0.163: Steer Mid-Response, Parallel Read-Only Tools, and Three Proxy Fixes
💡 Tool Tip:JSON Formatter, Regex Tester, Timestamp Converter, Hash Generator
On October 15, 2026, OpenAI shipped Codex CLI 0.163.0. The change you notice first is an interaction: you can send a follow-up while the model is still responding and it redirects immediately instead of queueing until the turn ends. The second half of that sentence matters more. Interrupting a turn now also stops active Code Mode cells and their nested tool calls. Before this, pressing stop could leave nested work running. Now the interrupt propagates, and that changes what it costs you to press stop.
A searchable /config that tells you which settings need a restart saves more than the settings themselves.
Upgrade first, then read the migration notice
The headline feature is a searchable /config: search preferences across categories, apply the settings that can take effect immediately, and see which changes require a restart. That last clause is the useful half. Before this you inferred a restart requirement from behaviour that failed to change.
The release also stops bundling a patched zsh and moves to the standard shell executor. Configurations using retired shell flags now receive a migration notice. I would read that carefully, because receiving a notice and having a working config are different things, especially for the parts of your dotfiles nobody has touched in a year.
The leader key is configurable in /keymap and defaults to Ctrl+X, shortcut hints stay open until you choose or cancel, and voice mute moves to F9. /daemon restarts the managed background server from the command line, warns you about work the restart could interrupt, then reconnects automatically. Small things, all of them daily.
# Codex CLI 0.163.0, published 2026-10-15. Upgrade the way you always do, then read
# what actually changed before you trust a long unattended run.
npm install -g @openai/[email protected]
codex --version
# The new /config is searchable across categories, applies the settings it can apply
# immediately, and tells you which changes need a restart. That last part is the
# useful half: previously you found out by watching behaviour not change.
# > /config
# > /config sandbox # filter by category
#
# Retired shell flags now emit a migration notice instead of silently doing nothing.
# 0.163 uses the standard shell executor and no longer bundles a patched zsh, so any
# config that depended on the old shell flags needs a look.
Mid-response steering: what to steer on, what not to
In practice it looks like this. Turn one sends a migration off against staging and asks for a diff count. While it is still running, you send a follow-up: scope it to the orders table and skip everything else. Before 0.163 that message waited. Now it lands.
Ranked by payoff, there are only three things worth steering on: narrowing scope when you asked for too much and can see it drifting, correcting a wrong assumption such as the wrong schema or the wrong branch, and suppressing verbosity when the model is writing an essay instead of a patch.
What is not worth steering on is style. You will spend more attention describing what you want and verifying it than you would editing the file yourself. Cheap steering is not free steering. Attention is the budget.
# Instant steering: send a follow-up while Codex is responding and it redirects
# right away instead of queueing until the turn ends. Interrupting also stops active
# Code Mode cells and their nested tool calls — that second half is the important one.
#
# Before 0.163, "stop" could leave nested tool calls running. Now the interrupt
# propagates. Which means the cost of being wrong went down, so steer more freely.
# A steering pattern that works well on refactors:
# turn 1: "Run the migration on the staging dataset and report the diff count."
# (while it is running) "Scope it to the orders table only, skip anything else."
#
# What to steer on, in order of payoff:
# 1. Scope narrowing — you over-asked and can see it heading too wide
# 2. Wrong assumption — it picked the wrong schema or the wrong branch
# 3. Verbosity — it is writing an essay instead of a patch
#
# What NOT to steer on: styling. You will spend more attention than editing the file.
Parallel read-only tools: the win is cold-start latency
Independent read-only tools now run in parallel. Skill lookups, memory lookups and conversation-history lookups no longer queue behind one another. The gain concentrates at cold start, where an agent used to serialise several lookups before emitting its first token.
Read the constraint precisely: only independent and read-only work parallelises. Anything that writes is still ordered. That is the correct design, and it is also why you should not read this as the agent getting faster at editing.
Measure the number yourself. The function below compares serial and parallel totals for four cold-start lookups at 120, 90, 150 and 80 milliseconds: 440 milliseconds against 150. But lookup count and per-lookup latency depend entirely on how many skills you have installed and how much memory you have accumulated. Someone else's multiplier is not your multiplier.
# Independent read-only tools now run in parallel: skill lookups, memory lookups and
# conversation-history lookups no longer queue behind each other. The win is latency
# on cold starts, where an agent used to serialise four lookups before its first token.
#
# The constraint that matters: only INDEPENDENT and READ-ONLY work parallelises.
# Anything with a write is still ordered, which is correct and is also why you should
# not read this as "the agent got faster at editing".
def measure_lookup_phase(timings_ms: list[int], parallel: bool) -> dict:
total = max(timings_ms) if parallel else sum(timings_ms)
return {
"lookups": len(timings_ms),
"serial_baseline_ms": sum(timings_ms),
"observed_ms": total,
"ratio": round(sum(timings_ms) / max(1, total), 2),
}
# Four cold-start lookups at 120/90/150/80 ms
print(measure_lookup_phase([120, 90, 150, 80], parallel=False)) # 440 ms
print(measure_lookup_phase([120, 90, 150, 80], parallel=True)) # 150 ms, 2.93x
# Measure this on your own harness before quoting a number. Lookup counts and latencies
# depend entirely on how many skills and memories you actually have.
Stop should mean stop: interrupts now propagate into nested tool calls.
Three proxy fixes that are really three security fixes
This group sits under bug fixes and the content is security. First, the network proxy now checks permission before a DNS lookup. Previously a hostname could be resolved before the policy ran, which leaks the fact that you have that domain in your configuration to the resolver even when the request would have been denied.
Second, duplicate Host headers are rejected. That is header smuggling territory. Third, credential aliases can no longer bypass inspection hooks. Read together, all three tighten the same two properties: the order in which policy executes, and the set of paths that reach a hook.
Which makes pinning the version the practical action. An old binary does not acquire these fixes, and this class of change never appears on your upgrade checklist as a security advisory. The docs also clarified how the Linux sandbox works: bubblewrap for filesystem isolation and seccomp for syscall filtering. Two mechanisms, two failure modes. If your CI runs on Linux, that is the paragraph to read.
# Three proxy fixes in 0.163 that are security fixes wearing a bug-fix label.
#
# 1. Permission is now checked BEFORE the DNS lookup. Previously a hostname could be
# resolved before the policy ran, which leaks "this domain exists in my config"
# to the DNS resolver even when the request would be denied.
# 2. Duplicate Host headers are rejected. Header-smuggling territory.
# 3. Credential aliases can no longer bypass inspection hooks.
#
# Pin this in your config and pin the version: an old binary does not get these.
# codex config (per-project .codex/config.toml)
# [sandbox]
# mode = "workspace-write"
#
# [network]
# # Deny by default; allowlist exactly what the build needs.
# allowed_domains = ["registry.npmjs.org", "pypi.org"]
# The docs also clarified how the Linux sandbox works: bubblewrap for filesystem
# isolation and seccomp for syscall filtering. Two different mechanisms with two
# different failure modes. If your CI is Linux, that paragraph is the one to read.
def verify_policy(deny_all_by_default: bool, allowlist: list[str], attempted: str) -> str:
if attempted in allowlist and deny_all_by_default:
return "ALLOW (allowlisted)"
if deny_all_by_default:
return "DENY (before DNS)"
return "ALLOW (permissive config, fix this)"
print(verify_policy(True, ["registry.npmjs.org"], "registry.npmjs.org"))
print(verify_policy(True, ["registry.npmjs.org"], "attacker.example"))
print(verify_policy(False, ["registry.npmjs.org"], "attacker.example"))
Resumed session permissions, and two silent timeout generators
Resuming a saved thread now restores its permissions and network approvals while still respecting explicit overrides and current managed policy. The ordering is the point: a resumed thread must not inherit a policy looser than the machine has right now. If you run codex exec in CI, verify this once on purpose.
Two other fixes deserve naming because both were quiet timeout generators. codex exec no longer reprints the same file diff after an unrelated command, which used to inflate your parse of the output stream. And local commands now finish promptly even when a child keeps an output pipe open, while remote commands preserve late-arriving output so the final poll can still collect it.
If you have a CI step that occasionally hangs for exactly its full timeout and never errors, go back and check whether it was one of these. That is the signature: it never fails, it just waits.
# Resuming a saved thread now restores its permissions and network approvals, while
# still respecting explicit overrides and current managed policy. That ordering is the
# whole point: a resumed thread must not inherit a looser policy than the machine has
# right now.
import subprocess, json
def resume_and_inspect(thread_id: str) -> dict:
out = subprocess.run(
["codex", "exec", "--json", "--resume", thread_id, "print the effective policy"],
capture_output=True, text=True, timeout=120,
)
policy = None
for line in out.stdout.splitlines():
try:
evt = json.loads(line)
except json.JSONDecodeError:
continue
if evt.get("type") == "policy":
policy = evt
return {"rc": out.returncode, "policy": policy, "stderr": out.stderr[-400:]}
print(resume_and_inspect("thread_abc123"))
# Two more changes worth knowing for CI:
# - codex exec no longer reprints the same file diff after an unrelated command,
# which used to inflate your parse of the output stream.
# - local commands finish promptly even when a child keeps an output pipe open, and
# remote commands preserve late-arriving output so the final poll still collects it.
# Both of those were silent timeout generators. If your CI had a flaky step that
# sometimes hung for exactly its timeout, check whether it was this.
Checking permission before DNS closes a side channel on your configuration.
📌 Frequently Asked Questions
What exactly does mid-response steering change?
Previously a follow-up sent while the model was responding queued until the turn finished. From 0.163 it lands immediately and Codex redirects on the spot. The companion change is that interrupting a turn also stops active Code Mode cells and their nested tool calls, which could previously keep running in the background.
Do parallel read-only tools make editing faster?
Not directly. Only independent read-only tools parallelise, such as skill, memory and conversation-history lookups; anything that writes stays serial. The gain shows up at cold start, where it removes several serialised lookups before the first token.
Why single out the three proxy fixes?
Because they are security fixes filed as bug fixes: permission is checked before DNS resolution so configuration is not leaked to the resolver, duplicate Host headers are rejected to close header smuggling, and credential aliases can no longer bypass inspection hooks. Old binaries do not acquire these, so pin the version.
What happens to retired shell flags in old configs?
0.163 uses the standard shell executor and no longer bundles a patched zsh, so configurations using retired shell flags receive a migration notice. A notice is not proof your config still works, so check the relevant lines in dotfiles you have not touched in a long time.
Does a resumed session inherit its original permissions?
It restores that thread's permissions and network approvals while respecting explicit overrides and current managed policy, so a resumed thread cannot end up looser than the machine's present policy. If you use codex exec --resume in CI, verify the effective policy once explicitly.