Skip to content

Commit 15f6c0d

Browse files
authored
Merge pull request #4 from axisrow/rebase-on-471
Rebase fork on upstream openai#471 (RPC override) + openai#506/openai#480/openai#490 + shutdown hardening
2 parents 0ddf717 + 040beef commit 15f6c0d

28 files changed

Lines changed: 1627 additions & 299 deletions

.claude-plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
},
66
"metadata": {
77
"description": "Codex plugins to use in Claude Code for delegation and code review.",
8-
"version": "1.0.6-fork.1"
8+
"version": "1.0.6-fork.2"
99
},
1010
"plugins": [
1111
{
1212
"name": "codex",
1313
"description": "Use Codex from Claude Code to review code or delegate tasks.",
14-
"version": "1.0.6-fork.1",
14+
"version": "1.0.6-fork.2",
1515
"author": {
1616
"name": "OpenAI"
1717
},

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,15 @@ Use it when you want:
8686
- a review of your current uncommitted changes
8787
- a review of your branch compared to a base branch like `main`
8888

89-
Use `--base <ref>` for branch review. It also supports `--wait`, `--background`, `--model <model|spark>`, and `--effort <none|minimal|low|medium|high|xhigh>`. It is not steerable and does not take custom focus text. Use [`/codex:adversarial-review`](#codexadversarial-review) when you want to challenge a specific decision or risk area.
89+
Use `--base <ref>` for branch review. It also supports `--wait`, `--background`, `--model`, and `--effort`. It is not steerable and does not take custom focus text. Use [`/codex:adversarial-review`](#codexadversarial-review) when you want to challenge a specific decision or risk area.
9090

9191
Examples:
9292

9393
```bash
9494
/codex:review
9595
/codex:review --base main
96-
/codex:review --model spark --effort medium
9796
/codex:review --background
97+
/codex:review --model gpt-5.6-sol --effort max
9898
```
9999

100100
This command is read-only and will not perform any changes. When run in the background you can use [`/codex:status`](#codexstatus) to check on the progress and [`/codex:cancel`](#codexcancel) to cancel the ongoing task.
@@ -106,7 +106,7 @@ Runs a **steerable** review that questions the chosen implementation and design.
106106
It can be used to pressure-test assumptions, tradeoffs, failure modes, and whether a different approach would have been safer or simpler.
107107

108108
It uses the same review target selection as `/codex:review`, including `--base <ref>` for branch review.
109-
It also supports `--wait`, `--background`, `--model <model|spark>`, and `--effort <none|minimal|low|medium|high|xhigh>`. Unlike `/codex:review`, it can take extra focus text after the flags.
109+
It also supports `--wait`, `--background`, `--model`, and `--effort`. Unlike `/codex:review`, it can take extra focus text after the flags.
110110

111111
Use it when you want:
112112

@@ -119,8 +119,8 @@ Examples:
119119
```bash
120120
/codex:adversarial-review
121121
/codex:adversarial-review --base main challenge whether this was the right caching and retry design
122-
/codex:adversarial-review --model gpt-5.4-mini --effort high look for race conditions and question the chosen approach
123122
/codex:adversarial-review --background look for race conditions and question the chosen approach
123+
/codex:adversarial-review --model gpt-5.6-terra --effort xhigh challenge the retry design
124124
```
125125

126126
This command is read-only. It does not fix code.
@@ -162,6 +162,8 @@ Ask Codex to redesign the database connection to be more resilient.
162162

163163
- if you do not pass `--model` or `--effort`, Codex chooses its own defaults.
164164
- if you say `spark`, the plugin maps that to `gpt-5.3-codex-spark`
165+
- reasoning efforts are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, and `ultra`; the current Codex model catalog is used to reject unsupported combinations such as `gpt-5.6-luna` with `ultra`
166+
- model names are otherwise passed through, so custom providers and newly released models are not blocked by a plugin allowlist
165167
- follow-up rescue requests can continue the latest Codex task in the repo
166168

167169
### `/codex:transfer`

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openai/codex-plugin-cc",
3-
"version": "1.0.6",
3+
"version": "1.0.7",
44
"private": true,
55
"type": "module",
66
"description": "Use Codex from Claude Code to review code or delegate tasks.",

plugins/codex/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codex",
3-
"version": "1.0.6-fork.1",
3+
"version": "1.0.6-fork.2",
44
"description": "Use Codex from Claude Code to review code or delegate tasks.",
55
"author": {
66
"name": "OpenAI"

plugins/codex/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Stop an orphaned shared Codex app-server broker after 15 minutes with no connected clients. Active foreground and background jobs keep their broker connection open, and the timeout can be configured with `CODEX_COMPANION_BROKER_IDLE_TIMEOUT_MS` (`0` disables the safety timer).
6+
- Close the broker listener before asynchronous child cleanup and safely reject reconnects already queued during shutdown.
7+
38
## 1.0.0
49

510
- Initial version of the Codex plugin for Claude Code

plugins/codex/commands/adversarial-review.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
description: Run a Codex review that challenges the implementation approach and design choices
3-
argument-hint: '[--wait|--background] [--base <ref>] [--scope auto|working-tree|branch] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [focus ...]'
3+
argument-hint: '[--wait|--background] [--base <ref>] [--scope auto|working-tree|branch] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh|max|ultra>] [focus ...]'
44
disable-model-invocation: true
55
allowed-tools: Read, Glob, Grep, Bash(node:*), Bash(git:*), AskUserQuestion
66
---
@@ -36,9 +36,9 @@ Execution mode rules:
3636

3737
Argument handling:
3838
- Preserve the user's arguments exactly.
39+
- `--model` and `--effort` select the Codex runtime and must not become part of the focus text.
3940
- Do not strip `--wait` or `--background` yourself.
4041
- Do not weaken the adversarial framing or rewrite the user's focus text.
41-
- `--model` and `--effort` are runtime-selection flags. Preserve them for the forwarded `adversarial-review` call, but do not treat them as part of the review instructions.
4242
- The companion script parses `--wait` and `--background`, but Claude Code's `Bash(..., run_in_background: true)` is what actually detaches the run.
4343
- `/codex:adversarial-review` uses the same review target selection as `/codex:review`.
4444
- It supports working-tree review, branch review, and `--base <ref>`.

plugins/codex/commands/rescue.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
description: Delegate investigation, an explicit fix request, or follow-up rescue work to the Codex rescue subagent
3-
argument-hint: "[--background|--wait] [--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [what Codex should investigate, solve, or continue]"
3+
argument-hint: "[--background|--wait] [--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh|max|ultra>] [what Codex should investigate, solve, or continue]"
44
allowed-tools: Bash(node:*), AskUserQuestion, Agent
55
---
66

plugins/codex/commands/review.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
description: Run a Codex code review against local git state
3-
argument-hint: '[--wait|--background] [--base <ref>] [--scope auto|working-tree|branch] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>]'
3+
argument-hint: '[--wait|--background] [--base <ref>] [--scope auto|working-tree|branch] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh|max|ultra>]'
44
disable-model-invocation: true
55
allowed-tools: Read, Glob, Grep, Bash(node:*), Bash(git:*), AskUserQuestion
66
---
@@ -33,9 +33,9 @@ Execution mode rules:
3333

3434
Argument handling:
3535
- Preserve the user's arguments exactly.
36+
- `--model` and `--effort` select the Codex runtime for the review and are not focus text.
3637
- Do not strip `--wait` or `--background` yourself.
3738
- Do not add extra review instructions or rewrite the user's intent.
38-
- `--model` and `--effort` are runtime-selection flags. Preserve them for the forwarded `review` call, but do not treat them as part of the review instructions.
3939
- The companion script parses `--wait` and `--background`, but Claude Code's `Bash(..., run_in_background: true)` is what actually detaches the run.
4040
- `/codex:review` is native-review only. It does not support staged-only review, unstaged-only review, or extra focus text.
4141
- If the user needs custom review instructions or more adversarial framing, they should use `/codex:adversarial-review`.

plugins/codex/scripts/app-server-broker.mjs

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ import { BROKER_BUSY_RPC_CODE, CodexAppServerClient } from "./lib/app-server.mjs
1010
import { parseBrokerEndpoint } from "./lib/broker-endpoint.mjs";
1111

1212
const STREAMING_METHODS = new Set(["turn/start", "review/start", "thread/compact/start"]);
13+
const BROKER_IDLE_TIMEOUT_ENV = "CODEX_COMPANION_BROKER_IDLE_TIMEOUT_MS";
14+
const DEFAULT_BROKER_IDLE_TIMEOUT_MS = 15 * 60 * 1000;
15+
16+
function resolveIdleTimeoutMs(env = process.env) {
17+
const rawValue = env[BROKER_IDLE_TIMEOUT_ENV];
18+
if (rawValue == null || rawValue === "") {
19+
return DEFAULT_BROKER_IDLE_TIMEOUT_MS;
20+
}
21+
22+
const parsed = Number(rawValue);
23+
if (!Number.isFinite(parsed) || parsed < 0) {
24+
return DEFAULT_BROKER_IDLE_TIMEOUT_MS;
25+
}
26+
return Math.floor(parsed);
27+
}
1328

1429
function buildStreamThreadIds(method, params, result) {
1530
const threadIds = new Set();
@@ -70,6 +85,16 @@ async function main() {
7085
let activeStreamSocket = null;
7186
let activeStreamThreadIds = null;
7287
const sockets = new Set();
88+
const idleTimeoutMs = resolveIdleTimeoutMs();
89+
let idleTimer = null;
90+
let shuttingDown = false;
91+
92+
function cancelIdleShutdown() {
93+
if (idleTimer) {
94+
clearTimeout(idleTimer);
95+
idleTimer = null;
96+
}
97+
}
7398

7499
function clearSocketOwnership(socket) {
75100
if (activeRequestSocket === socket) {
@@ -100,11 +125,20 @@ async function main() {
100125
}
101126

102127
async function shutdown(server) {
128+
if (shuttingDown) {
129+
return;
130+
}
131+
shuttingDown = true;
132+
cancelIdleShutdown();
133+
// Stop accepting connections before awaiting child cleanup. Otherwise a
134+
// reconnect can slip into the async shutdown window and inherit a closing
135+
// app-server client.
136+
const serverClosed = new Promise((resolve) => server.close(resolve));
103137
for (const socket of sockets) {
104138
socket.end();
105139
}
106140
await appClient.close().catch(() => {});
107-
await new Promise((resolve) => server.close(resolve));
141+
await serverClosed;
108142
if (listenTarget.kind === "unix" && fs.existsSync(listenTarget.path)) {
109143
fs.unlinkSync(listenTarget.path);
110144
}
@@ -113,9 +147,33 @@ async function main() {
113147
}
114148
}
115149

150+
function scheduleIdleShutdown(server) {
151+
cancelIdleShutdown();
152+
if (shuttingDown || idleTimeoutMs === 0 || sockets.size > 0 || activeRequestSocket || activeStreamSocket) {
153+
return;
154+
}
155+
156+
idleTimer = setTimeout(async () => {
157+
idleTimer = null;
158+
if (sockets.size > 0 || activeRequestSocket || activeStreamSocket) {
159+
return;
160+
}
161+
await shutdown(server);
162+
process.exit(0);
163+
}, idleTimeoutMs);
164+
}
165+
116166
appClient.setNotificationHandler(routeNotification);
117167

118168
const server = net.createServer((socket) => {
169+
if (shuttingDown) {
170+
// An already-accepted connection event can be delivered after
171+
// server.close(). Reject it decisively and absorb a simultaneous reset.
172+
socket.on("error", () => {});
173+
socket.destroy();
174+
return;
175+
}
176+
cancelIdleShutdown();
119177
sockets.add(socket);
120178
socket.setEncoding("utf8");
121179
let buffer = "";
@@ -158,6 +216,13 @@ async function main() {
158216
}
159217

160218
if (message.id !== undefined && message.method === "broker/shutdown") {
219+
if (activeRequestSocket || activeStreamSocket) {
220+
send(socket, {
221+
id: message.id,
222+
error: buildJsonRpcError(BROKER_BUSY_RPC_CODE, "Shared Codex broker is busy.")
223+
});
224+
continue;
225+
}
161226
send(socket, { id: message.id, result: {} });
162227
await shutdown(server);
163228
process.exit(0);
@@ -225,11 +290,13 @@ async function main() {
225290
socket.on("close", () => {
226291
sockets.delete(socket);
227292
clearSocketOwnership(socket);
293+
scheduleIdleShutdown(server);
228294
});
229295

230296
socket.on("error", () => {
231297
sockets.delete(socket);
232298
clearSocketOwnership(socket);
299+
scheduleIdleShutdown(server);
233300
});
234301
});
235302

@@ -243,7 +310,9 @@ async function main() {
243310
process.exit(0);
244311
});
245312

246-
server.listen(listenTarget.path);
313+
server.listen(listenTarget.path, () => {
314+
scheduleIdleShutdown(server);
315+
});
247316
}
248317

249318
main().catch((error) => {

0 commit comments

Comments
 (0)