Skip to content

Commit c22f414

Browse files
steipeterubencu
andcommitted
fix(codex): keep app-server alive after turn activity
Co-authored-by: Rubén Cuevas <[email protected]>
1 parent ab8166b commit c22f414

8 files changed

Lines changed: 352 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
1313
- Docs/iMessage: deprecate BlueBubbles for new OpenClaw setups, document the upstream server-release rationale, and point new iMessage deployments toward the native `imsg` path while keeping BlueBubbles as a supported legacy fallback.
1414
- Discord/streaming: default Discord replies to progress draft previews so tool/work activity appears in one edited Discord message unless `channels.discord.streaming.mode` is set to `off`.
1515
- Plugins/install: add `npm-pack:<path.tgz>` installs so local npm pack artifacts run through the same managed npm-root install, lockfile verification, dependency scan, and install-record path as registry npm plugins.
16+
- Codex app-server: disarm the short post-tool completion watchdog after current-turn activity, expose `appServer.turnCompletionIdleTimeoutMs`, and include raw assistant item context in idle-timeout diagnostics so status-only post-tool stalls stop failing as idle. Fixes #77984. Thanks @roseware-dev and @rubencu.
1617
- Plugin skills/Windows: publish plugin-provided skill directories as junctions on Windows so standard users without Developer Mode can register plugin skills without symlink EPERM failures. Fixes #77958. (#77971) Thanks @hclsys and @jarro.
1718
- MS Teams: surface blocked Bot Framework egress by logging JWKS fetch network failures and adding a Bot Connector send hint for transport-level reply failures. Fixes #77674. (#78081) Thanks @Beandon13.
1819
- Gateway/sessions: fast-path already-qualified model refs while building session-list rows so `openclaw sessions` and Control UI session lists avoid heavyweight model resolution on large stores. (#77902) Thanks @ragesaq.

docs/plugins/codex-harness.md

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -628,21 +628,22 @@ Supported top-level Codex plugin fields:
628628

629629
Supported `appServer` fields:
630630

631-
| Field | Default | Meaning |
632-
| ------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
633-
| `transport` | `"stdio"` | `"stdio"` spawns Codex; `"websocket"` connects to `url`. |
634-
| `command` | managed Codex binary | Executable for stdio transport. Leave unset to use the managed binary; set it only for an explicit override. |
635-
| `args` | `["app-server", "--listen", "stdio://"]` | Arguments for stdio transport. |
636-
| `url` | unset | WebSocket app-server URL. |
637-
| `authToken` | unset | Bearer token for WebSocket transport. |
638-
| `headers` | `{}` | Extra WebSocket headers. |
639-
| `clearEnv` | `[]` | Extra environment variable names removed from the spawned stdio app-server process after OpenClaw builds its inherited environment. `CODEX_HOME` and `HOME` are reserved for OpenClaw's per-agent Codex isolation on local launches. |
640-
| `requestTimeoutMs` | `60000` | Timeout for app-server control-plane calls. |
641-
| `mode` | `"yolo"` | Preset for YOLO or guardian-reviewed execution. |
642-
| `approvalPolicy` | `"never"` | Native Codex approval policy sent to thread start/resume/turn. |
643-
| `sandbox` | `"danger-full-access"` | Native Codex sandbox mode sent to thread start/resume. |
644-
| `approvalsReviewer` | `"user"` | Use `"auto_review"` to let Codex review native approval prompts. `guardian_subagent` remains a legacy alias. |
645-
| `serviceTier` | unset | Optional Codex app-server service tier: `"fast"`, `"flex"`, or `null`. Invalid legacy values are ignored. |
631+
| Field | Default | Meaning |
632+
| ----------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
633+
| `transport` | `"stdio"` | `"stdio"` spawns Codex; `"websocket"` connects to `url`. |
634+
| `command` | managed Codex binary | Executable for stdio transport. Leave unset to use the managed binary; set it only for an explicit override. |
635+
| `args` | `["app-server", "--listen", "stdio://"]` | Arguments for stdio transport. |
636+
| `url` | unset | WebSocket app-server URL. |
637+
| `authToken` | unset | Bearer token for WebSocket transport. |
638+
| `headers` | `{}` | Extra WebSocket headers. |
639+
| `clearEnv` | `[]` | Extra environment variable names removed from the spawned stdio app-server process after OpenClaw builds its inherited environment. `CODEX_HOME` and `HOME` are reserved for OpenClaw's per-agent Codex isolation on local launches. |
640+
| `requestTimeoutMs` | `60000` | Timeout for app-server control-plane calls. |
641+
| `turnCompletionIdleTimeoutMs` | `60000` | Quiet window after a turn-scoped Codex app-server request while OpenClaw waits for `turn/completed`. Raise this for slow post-tool or status-only synthesis phases. |
642+
| `mode` | `"yolo"` | Preset for YOLO or guardian-reviewed execution. |
643+
| `approvalPolicy` | `"never"` | Native Codex approval policy sent to thread start/resume/turn. |
644+
| `sandbox` | `"danger-full-access"` | Native Codex sandbox mode sent to thread start/resume. |
645+
| `approvalsReviewer` | `"user"` | Use `"auto_review"` to let Codex review native approval prompts. `guardian_subagent` remains a legacy alias. |
646+
| `serviceTier` | unset | Optional Codex app-server service tier: `"fast"`, `"flex"`, or `null`. Invalid legacy values are ignored. |
646647

647648
OpenClaw-owned dynamic tool calls are bounded independently from
648649
`appServer.requestTimeoutMs`: each Codex `item/tool/call` request must receive
@@ -652,10 +653,15 @@ the turn can continue instead of leaving the session in `processing`.
652653

653654
After OpenClaw responds to a Codex turn-scoped app-server request, the harness
654655
also expects Codex to finish the native turn with `turn/completed`. If the
655-
app-server goes quiet for 60 seconds after that response, OpenClaw best-effort
656-
interrupts the Codex turn, records a diagnostic timeout, and releases the
657-
OpenClaw session lane so follow-up chat messages are not queued behind a stale
658-
native turn.
656+
app-server goes quiet for `appServer.turnCompletionIdleTimeoutMs` after that
657+
response, OpenClaw best-effort interrupts the Codex turn, records a diagnostic
658+
timeout, and releases the OpenClaw session lane so follow-up chat messages are
659+
not queued behind a stale native turn. Any non-terminal notification for the
660+
same turn, including `rawResponseItem/completed`, disarms that short watchdog
661+
because Codex has proven the turn is still alive; the longer terminal watchdog
662+
continues to protect genuinely stuck turns. Timeout diagnostics include the
663+
last app-server notification method and, for raw assistant response items, the
664+
item type, role, id, and a bounded assistant text preview.
659665

660666
Environment overrides remain available for local testing:
661667

extensions/codex/openclaw.plugin.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@
130130
"minimum": 1,
131131
"default": 60000
132132
},
133+
"turnCompletionIdleTimeoutMs": {
134+
"type": "number",
135+
"minimum": 1,
136+
"default": 60000
137+
},
133138
"approvalPolicy": {
134139
"type": "string",
135140
"enum": ["never", "on-request", "on-failure", "untrusted"]
@@ -270,6 +275,11 @@
270275
"help": "Maximum time to wait for Codex app-server control-plane requests.",
271276
"advanced": true
272277
},
278+
"appServer.turnCompletionIdleTimeoutMs": {
279+
"label": "Turn Completion Idle Timeout",
280+
"help": "Maximum quiet time after a turn-scoped Codex app-server request before OpenClaw interrupts the turn while waiting for turn/completed.",
281+
"advanced": true
282+
},
273283
"appServer.approvalPolicy": {
274284
"label": "Approval Policy",
275285
"help": "Codex native approval policy sent to thread start, resume, and turns.",

extensions/codex/src/app-server/config.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe("Codex app-server config", () => {
2222
sandbox: "danger-full-access",
2323
approvalsReviewer: "guardian_subagent",
2424
serviceTier: "flex",
25+
turnCompletionIdleTimeoutMs: 120_000,
2526
},
2627
},
2728
env: {
@@ -36,6 +37,7 @@ describe("Codex app-server config", () => {
3637
sandbox: "danger-full-access",
3738
approvalsReviewer: "guardian_subagent",
3839
serviceTier: "flex",
40+
turnCompletionIdleTimeoutMs: 120_000,
3941
start: expect.objectContaining({
4042
transport: "websocket",
4143
url: "ws://127.0.0.1:39175",

extensions/codex/src/app-server/config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export type CodexAppServerStartOptions = {
4949
export type CodexAppServerRuntimeOptions = {
5050
start: CodexAppServerStartOptions;
5151
requestTimeoutMs: number;
52+
turnCompletionIdleTimeoutMs: number;
5253
approvalPolicy: CodexAppServerApprovalPolicy;
5354
sandbox: CodexAppServerSandboxMode;
5455
approvalsReviewer: CodexAppServerApprovalsReviewer;
@@ -73,6 +74,7 @@ export type CodexPluginConfig = {
7374
headers?: Record<string, string>;
7475
clearEnv?: string[];
7576
requestTimeoutMs?: number;
77+
turnCompletionIdleTimeoutMs?: number;
7678
approvalPolicy?: CodexAppServerApprovalPolicy;
7779
sandbox?: CodexAppServerSandboxMode;
7880
approvalsReviewer?: CodexAppServerApprovalsReviewer;
@@ -91,6 +93,7 @@ export const CODEX_APP_SERVER_CONFIG_KEYS = [
9193
"headers",
9294
"clearEnv",
9395
"requestTimeoutMs",
96+
"turnCompletionIdleTimeoutMs",
9497
"approvalPolicy",
9598
"sandbox",
9699
"approvalsReviewer",
@@ -166,6 +169,7 @@ const codexPluginConfigSchema = z
166169
headers: z.record(z.string(), z.string()).optional(),
167170
clearEnv: z.array(z.string()).optional(),
168171
requestTimeoutMs: z.number().positive().optional(),
172+
turnCompletionIdleTimeoutMs: z.number().positive().optional(),
169173
approvalPolicy: codexAppServerApprovalPolicySchema.optional(),
170174
sandbox: codexAppServerSandboxSchema.optional(),
171175
approvalsReviewer: codexAppServerApprovalsReviewerSchema.optional(),
@@ -227,6 +231,10 @@ export function resolveCodexAppServerRuntimeOptions(
227231
...(transport === "stdio" && clearEnv.length > 0 ? { clearEnv } : {}),
228232
},
229233
requestTimeoutMs: normalizePositiveNumber(config.requestTimeoutMs, 60_000),
234+
turnCompletionIdleTimeoutMs: normalizePositiveNumber(
235+
config.turnCompletionIdleTimeoutMs,
236+
60_000,
237+
),
230238
approvalPolicy:
231239
resolveApprovalPolicy(config.approvalPolicy) ??
232240
resolveApprovalPolicy(env.OPENCLAW_CODEX_APP_SERVER_APPROVAL_POLICY) ??

0 commit comments

Comments
 (0)