feat(cli): show patch tasks in Codex desktop - #477
Conversation
mldangelo-oai
left a comment
There was a problem hiding this comment.
Non-blocking follow-up to my earlier comment on 43fd1b26. The installed package's normal patch and resume path worked. The additional protocol tests found three cases worth covering: a child task can finish the parent command, a server request for user input can go unanswered, and an early disconnect can return success.
The full local suite passed 1,288 tests with 11 skipped, and the current GitHub checks are green. I left the observed behavior and suggested regression tests inline. A small app-server helper shared with #471 would give these cases one place to live.
| if (turn.status !== "completed") { | ||
| error = turn.error?.message ?? "Codex did not complete the patch."; | ||
| } | ||
| appServer.input.end(); |
There was a problem hiding this comment.
Non-blocking: this notification can belong to a child task. In the installed CLI test, a child's item/completed and turn/completed produced exit 0 with the child's answer, then closed stdin before the parent finished. Could we retain the thread ID and turn ID returned by the start requests and accept messages and completion only for that pair? A child-finishes-first regression should still return the parent's final answer.
| if (appServer !== undefined && value["id"] !== undefined) { | ||
| const responseError = value["error"] as { message: string } | undefined; | ||
| if (responseError !== undefined) { | ||
| error = responseError.message; | ||
| appServer.input.end(); |
There was a problem hiding this comment.
Non-blocking: server-initiated JSON-RPC requests also have an id. A normal item/tool/requestUserInput request received no reply in QA, so the command waited until it was terminated. I'd route method-bearing requests separately from responses to our pending requests. Unsupported interactions should receive an explicit response or end with a clear saved-task handoff, while preserving the configured approval policy. An overlapping client/server request ID is worth covering too.
| let [status, events] = await Promise.all([invocationStatus, captured]); | ||
| if (status === 0 && output?.appServer !== undefined && events?.error) { | ||
| status = 1; | ||
| } |
There was a problem hiding this comment.
Non-blocking: a clean app-server exit does not tell us whether the patch turn finished. A commentary message followed by EOF returned exit 0 and printed the commentary as the result. Could the reader return an explicit completion state and the matching final-answer item, and have this path check both? The early-EOF case should report an incomplete result even if the process exits cleanly.
| appServer?: { | ||
| readonly directory: string; | ||
| readonly prompt: string; | ||
| readonly input: NodeJS.WritableStream; | ||
| }, | ||
| ): Promise<{ message?: string; error?: string; malformed: boolean }> { |
There was a problem hiding this comment.
Non-blocking simplification: I'd keep the legacy codex exec decoder separate and move the app-server lifecycle into a small typed helper shared with #471. It could own request IDs, routing, the active thread and turn, completion, redacted errors, and shutdown. That would remove the mixed protocol state machine from this output parser and give the cases above one place to be tested.
| const { thread } = value["result"] as { thread: { id: string } }; | ||
| send({ | ||
| id: 3, | ||
| method: "turn/start", | ||
| params: { | ||
| threadId: thread.id, |
There was a problem hiding this comment.
Non-blocking UX suggestion: could we return this task ID and give the task a useful name? The task was resumable in QA, but it had no name, its preview started with the implementation prompt, and the CLI did not identify which task to open. A short title and a supported open/resume instruction on stderr would make the handoff easier without changing final-response stdout. The common structured result could carry the ID too.
|
Non-blocking design summary for #475, #477, #471, and #461. I think the split is right: keep the pinned scan runtime isolated, share task visibility, and let the user continue a saved patch task. I'd keep the implementation small:
Across the four PR heads and a #475+#477 integration tree, five full local suite runs produced 6,336 passes and 55 skips. The real-model patch and saved-task resume paths worked with synthetic fixtures. The current heads do not yet provide one complete Linear issue to GitHub PR workflow; I did not use live Linear or create an external PR. My suggested follow-up order is the #477 lifecycle tests, the #471 naming and Windows cleanup, then the common patch result and publication recovery. After those pieces are joined, one explicitly scoped live acceptance test would close the remaining gap. |
zcrab-oai
left a comment
There was a problem hiding this comment.
Found a project-isolation regression in the transition from ephemeral patch execution to persistent desktop tasks.
| [ | ||
| "exec", | ||
| "--ignore-user-config", | ||
| ...(patch ? ["app-server"] : ["exec", "--ignore-user-config"]), |
There was a problem hiding this comment.
[P1] Preserve project isolation for desktop patch tasks
Could saved patch tasks retain the previous isolated-configuration behavior without silently changing persistent project trust? Starting a workspace-write app-server thread can load user/project configuration and activate standalone MCP servers even when plugins are disabled. Please ensure repository trust is not automatically promoted and project-configured MCP servers remain inactive before the task starts, with an integration test covering an initially untrusted project.
kmbroai
left a comment
There was a problem hiding this comment.
Preapproving - should fix some of the comments
Summary
Patch tasks did not appear in Codex desktop because the CLI created ephemeral
codex execsessions. The desktop app also excludes savedexecsessions from its task list.Changes
Testing
bun test tests-ts/cli-skills.test.ts tests-ts/cli.test.ts tests-ts/cli-authentication.test.ts tests-ts/cli-signals.test.ts --randomize --seed 12345 --timeout 30000: 183 passed.node scripts/generate-models.cjs --check.tsc --noEmit.tsc -p tsconfig.build.json.prettier --check --ignore-path .gitignore --ignore-path .prettierignore '**/*.{cjs,mjs,js,ts,json,md}'.Risk and rollout
Patch sessions are now saved in the configured Codex home and can contain finding details. The app server reads user configuration, but plugins remain disabled and the workspace-write sandbox and
neverapproval policy stay enforced. The CLI uses its bundled Codex executable; the desktop app is optional.Public disclosure review