Programmatic chat creation API for Codex Desktop: spawn a sidebar-visible chat with initial prompt from an external orchestrator
What variant of Codex are you using?
App
What feature would you like to see?
A way for an external tool to programmatically create a new chat in Codex Desktop's live "Chats" sidebar with a provided initial prompt, and receive back a chat/thread ID that the external tool can use for status checks and clean teardown.
The concrete shape is up to your team. Plausible options:
- URL scheme:
codex://new?prompt=<url-encoded-prompt>&title=<url-encoded-title>that activates Codex Desktop, creates the chat in the live sidebar, and returns the new thread ID (e.g., via stdout when invoked throughopenwith a callback URL, or by writing to a known file path). - Extension to
codex remote-control: anew-chatsubcommand that takes--prompt @fileand--title "..."and prints the new thread ID. The infrastructure for remote-control is already in place (v0.130+). - App-side MCP tool exposed by Codex Desktop that other agents can call (similar to how Codex.app already speaks to MCP servers in the other direction).
- App-server endpoint that an external process can POST to with the same payload.
Why I need this
I built a plugin (codex-fleet for Claude Code) that orchestrates parallel Codex chats: one operator command opens an orchestrator chat + N lane chats, each pre-loaded with its coordination prompt, so multiple Codex agents work in parallel on a shared cycle with a shared coord document. This is the workflow OpenAI's own multi-architect coordination docs describe (orchestrator + Lane A + Lane B), and it currently requires the operator to manually copy-paste each lane prompt into separate Codex Desktop windows.
I've tried two existing mechanisms and neither fully works:
- Terminal +
codexCLI: Spawning a Terminal.app window that runscodex "prompt"produces acodex-tuisession (source: cli) that does appear in Codex.app's Archived chats list — but only after the session ends and the app reloads its archive cache, and never in the live "Chats" sidebar. It also leaks Terminal windows that have to be cleaned up by AppleScript. codex exec "prompt": Produces sessions withoriginator: codex_exec/source: execthat the Codex Desktop sidebar does not index at all. We verified the-c originator="Codex Desktop"override is explicitly blocked forexecmode ("Failed to set codex exec originator override").
The result: there is no path today to land a chat in the live sidebar from an external orchestrator without driving Codex Desktop's UI with AppleScript keystrokes, which is brittle, requires Accessibility permission, can't capture the thread ID, and breaks if the operator is typing.
Additional information
Related issues I found that touch nearby territory but don't cover this:
- #24083 (sidebar metadata) — about display of existing chats, not creation
- #24060 (auto-updated chat titles) — title behavior of existing chats
- #21657 (custom interaction hooks for desktop pets) — closest in spirit; extends Codex Desktop with external hooks
- The various
remote-controlissues are mostly about mobile pairing and auth, not chat creation
If this lands, the codex-fleet plugin (and any similar orchestrator) becomes a one-command parallel-launch tool where each spawned Codex chat is operator-visible in the live sidebar with full session metadata, and cancel/cleanup is targeted by returned thread ID instead of relying on UI scripting.
Happy to provide the codex-fleet plugin source as a concrete consumer-side reference if useful.
4 Comments
Thanks for the feature request.
It's unfortunate that
codex execdoesn't work for this use case. That was going to be my initial suggestion, but you're correct that it produces sessions that are intentionally not enumerated by the app. We should think about ways to improve this.In the meantime, another approach you could explore is using the app server interface. This is the same interface that the desktop app is built upon. It's a more complex interface than
codex exec, but it's also way more flexible. You could ask Codex to write a small script that launchescodex app-serverand then uses its apis to create a new thread and inject a prompt.This lines up closely with a workflow I’ve been trying to make reliable.
The app-server approach suggested above seems like the right direction for the “create a sidebar-visible chat + inject initial prompt” half of the problem. In my own setup, the adjacent problem is what happens after those lane chats exist: an orchestrator needs to keep those visible top-level chats addressable as durable worker lanes, instead of spawning disposable child sessions that disappear from normal operator workflows.
One useful observation from my local workflow: if I manually spend a few seconds creating the live sidebar chats first, the rest of the orchestration shape fits naturally. Once a normal sidebar-visible thread exists, it already has the right lifecycle, visibility, project scoping, and operator affordances. The missing piece is a supported way to attach that existing thread to an orchestrator as a routable subagent/lane.
I’ve been able to make that style of workflow work locally, but only by directly mutating local thread metadata / parent-child edge state. And reversing that mutation before any version updates. That proves the state shape is viable, but it’s obviously not something users should have to do directly.
I opened a narrower companion issue for that second half here:
https://github.com/openai/codex/issues/24398
And I put together a small reference branch showing one App Server-shaped way to expose it:
https://github.com/johnastinnett/codex/tree/codex/thread-attachments-rpc
That branch does not solve chat creation by itself. The connection I see is:
So I think this issue and #24398 are complementary: this issue covers programmatic thread creation, while #24398 covers durable orchestration over those created or manually prepared threads once they exist.
Working solution for programmatic sidebar thread creation
We ran into this exact problem and solved it — here's the key and a working implementation.
---
The key:
ephemeral: falseinthread/startThe app-server JSON-RPC protocol already supports persistent threads. The
ephemeralfield inthread/startdefaults totrue(disposable, not written to disk). Setting it tofalsepersists the thread to~/.codex/state_5.sqlite— the shared database the desktop app polls — and threads appear in the sidebar within seconds.Implementation
We use a broker pattern: an
app-server-broker.mjsdaemon startscodex app-serveras a subprocess and proxies JSON-RPC over a Unix socket. The client connects, creates a thread, sets its name, and fires off the initial turn:\
\\`javascript// Create a persistent (non-ephemeral) thread
const { thread } = await client.request("thread/start", {
cwd,
approvalPolicy: "never",
sandbox: "workspace-write",
serviceName: "codex-fleet",
ephemeral: false, // ← persisted to state_5.sqlite → sidebar-visible
experimentalRawEvents: false,
});
// Set the sidebar display name (non-fatal on older CLI versions)
try {
await client.request("thread/name/set", { threadId: thread.id, name: "My Project Lane A" });
} catch {}
// Fire-and-forget the initial prompt — socket.end() flushes before FIN,
// so the broker receives turn/start even when the client closes immediately
client.request("turn/start", {
threadId: thread.id,
input: [{ type: "text", text: prompt, text_elements: [] }],
model: null,
effort: null,
outputSchema: null,
}).catch(() => {});
\
\\`Why fire-and-forget works
socket.write()is synchronous in the Node.js buffer.socket.end()flushes the buffer before sending FIN, so the broker subprocess receives theturn/startmessage even when the client closes immediately after. The agent runs independently in the broker subprocess; closing the client connection doesn't stop it.Verification
We verified with a smoke test: all 3 threads (orchestrator + lane-a + lane-b) appeared in
~/.codex/session_index.jsonlwithin 3 seconds and showedsidebarVisibility: true. Threads created this way are indistinguishable from ones created interactively in the desktop app.---
Hope this helps! This is the pattern we're using to auto-spawn multi-lane parallel Codex sessions directly into the sidebar without any manual copy-paste.
Clarification after deeper testing: the
ephemeral: falsemechanism above works for the VS Code extension context (threads written to~/.codex/state_5.sqliteappear in the VS Code Codex sidebar). The standalone Codex desktop app appears to be cloud-backed — it stores/fetches threads from OpenAI's API and caches in its own Electron LevelDB (~/Library/Application Support/Codex/), and does not read from~/.codex/state_5.sqlite. So programmatically-created threads via the broker approach show up in VS Code but not in the desktop app's sidebar.If there's a way to target the desktop app's thread store (either a public API or a documented IPC protocol), we'd love to know — that would complete the picture.