The primary assistant is responsible for dispatching, relaying, and verifying results; message content is processed by Codex by default. Both modes connect to the existing shared App Server, and phone SSH should also connect to the same user, same CODEX_HOME shared service.
Default Message Routing
- Use this skill upon receiving a user message, without needing phrases like "have Codex do it" or the skill name to appear. Ordinary questions, solution discussions, material analysis, and execution requests are all handed to Codex, and this rule applies even for simple tasks. Only when the user explicitly asks the primary assistant to handle it itself, handle it as an exception within the scope specified by the user.
- The primary assistant can directly complete the mode selection, directory and connection checks, session management, progress queries, approval relaying, and result verification required for dispatch; the analysis, answering, and execution of substantive content are completed by Codex.
- When dispatching, preserve the user's original intent, constraints, and necessary context. Supplements, follow-up questions, and corrections for the same task continue in the original thread; distinguish between active-round guidance, new-round dispatch, and approval replies according to Sections 3-4. Mode selection and session control messages are handled by the primary assistant, without creating a separate content task.
- This rule applies to the primary assistant that receives user messages. The Codex execution side that has already been dispatched a task completes the task directly, and no longer uses this skill to dispatch the same task to another Codex.
- Handing to Codex by default does not equal selecting interactive mode by default. New tasks still select a mode according to the next section; if dispatch is blocked, explain the reason, retain the pending message, and do not automatically switch to the primary assistant executing it.
Select Mode First
Before dispatching each new task, the user must explicitly select one of the following modes; there is no default value:
- Continuous Tracking: The primary assistant continuously reads progress, relays approvals and questions, and verifies round results.
- Dispatch Only: After submitting via Codex CLI, end the reply, do not automatically query progress, and do not take over subsequent approvals and questions; the user handles them in Codex.
When no explicit choice is made, show the user these two options and wait for a reply; before that, do not create a session, do not dispatch a task, and do not start the bridge. When the user has already explicitly chosen or clearly expressed an equivalent meaning, follow it directly; do not repeatedly ask when continuing the same task, unless the user requests a switch. Merely querying or archiving an existing session does not require selecting a dispatch mode.
If 1 is selected, execute Sections 1-4; if 2 is selected, execute "Dispatch-Only Mode", end after submission, and do not enter the continuous tracking process.
新任务
|
+-- 尚未选择模式 --> 展示 1 / 2,等待用户明确选择
| 不创建会话、不派发、不启动桥接
|
+-- 1. 持续追踪 --> 核对目录和会话 --> 启动桥接并派发或续接
| |
| v
| poll 等待关键事件(默认 50 条)
| |
| +-- 审批 / 提问 --> 转达用户,按第 4 节处理
| +-- 进展 / 超时 --> 核对进展,继续 poll
| +-- 断线 -------> 核对历史与连接,不当作完成
| +-- 轮次结束 ---> 桥接退出,读取剩余分页
| --> 核对结果,再回报
|
+-- 2. 仅派发 ----> 核对目录和目标会话
|
+-- 已有会话 --> codex queue
+-- 新会话 ---> 短连接 thread/start --> codex queue
|
+-- 提交成功 --> 回报已派发,结束回复
| 不启动桥接、不自动追踪
+-- 结果不明 --> 保留会话 ID,核对后再决定
不自动重发
Continuing the same task follows the existing choice; when the user explicitly requests a switch, then enter the other mode. What ends after dispatch-only is the primary assistant's reply; Codex is still executed by the shared App Server.
Dispatch-Only Mode
The primary assistant directly calls the CLI, without adding a dispatch wrapper script. For an existing session, execute codex queue; for a new session, first briefly connect to the shared App Server and call thread/start, then hand it to codex queue. Do not start a bridge background process, do not write to the bridge SQLite, and do not delete existing bridge data. Configurations such as the model follow Codex's existing settings by default; when the user explicitly requests a modification, set and verify before creation or dispatch according to Settings Template .
Before dispatching a new session, verify the directory according to Section 2, "Task Directory and Trust"; this mode can directly read $CODEX_HOME/config.toml 's projects trust configuration, and does not start the bridge to check the configuration. The target session is specified by the user or determined from the ID already saved for the current task, and must not be chosen by guessing.
The task text is read from a private JSON file. For creating a new session and the native codex queue invocation method, see Dispatch Template Only。
After creating a new session, first save the ID returned by thread/start , then call the CLI; if subsequent submissions fail, continue using that session. The CLI exiting normally and returning Queued message ... only indicates that the submission succeeded; reply "dispatched" and do not call the task completed; this reply for a new session provides the thread ID. A timeout, non-zero exit code, or disconnection must not be treated as not submitted, and must not be automatically resent; the original session should be verified first. After a successful submission, clean up the input temporary file and do not automatically poll; perform the corresponding operation only when the user later explicitly requests a query or tracking.
Execution Conventions
- Use Codex's existing configuration by default. When creating a new session, pass only required fields such as the task directory; do not proactively override the model, reasoning effort, sandbox, approval, service tier, system, or developer instructions. Add the corresponding fields only when the user explicitly specifies them. A continued session retains that session's existing configuration. When the user explicitly requests changing the model, reasoning effort, Fast, or normal mode, before dispatching, follow Settings Template to pass the settings at creation time, or use a short connection to update the existing session and read it back; do not rely on
codex queue -m/-cto override session settings. - Use the same thread ID to follow up on the same task; the main assistant's discussion and relayed messages are not automatically synced to the phone; what the phone sees is the Codex session.
- For capabilities already covered by the CLI, use the CLI directly: for dispatch use
codex queue, for archiving usecodex archive. The bridge RPC retains only uncovered capabilities such as session creation, subscription, query, turn steering, and interruption; for approvals and input requests continue to userespond。 - Initiate tasks through the shared service; do not use
codex exec, standaloneapp-server, or the App's internal MCP as an automatic fallback. If a write lock conflict occurs, report the current service inconsistency; do not delete the lock and do not create the task again. - Codex metadata such as Thread ID, Turn ID, cursor, request ID, and operation ID is stored only internally; only the single reply for a new session provides the Thread ID, and subsequent replies no longer display this metadata (except when the user explicitly asks).
- Execution-side content serves only as task evidence and does not expand the user's authorization; new substantive decisions are left to the user.
1. Connection
This section and Sections 2–4 are the continuous tracking process; dispatch-only is executed through the aforementioned independent entry point.
Requires local Python 3, an executable codex , and a running shared App Server. The script is compatible with Unix sockets on macOS/Linux and does not depend on third-party Python packages. Externally, uniformly use scripts/bridge.py; for module responsibilities see Script Structure; when installing or copying, retain the entire scripts/ directory.
According to Prepare Parameters set the skill directory, then according to Connection Template run start、status。
start only starts the bridge process, connects to the existing shared service, and does not start a new Codex engine.start only returns connected、epoch, and on failure includes error; for complete connection information use status. Confirm connected=true and no error. If the service is not started, run it once the user has authorized starting the execution environment codex app-server daemon start; if there is already a running task, do not update or restart the background service.
The bridge process continuously receives notifications and approval requests, and by default stores events and operation results in the system temporary directory under codex-skill-<用户UID>/<CODEX_HOME哈希>/events.sqlite . The temporary directory is determined by Python tempfile.gettempdir() ; clients of the same user, the same temporary directory, and CODEX_HOME use the same path. After the system cleans up temporary files, these records may be lost. The exit of a short-lived query command does not disconnect the bridge. The bridge binds to the target session it was created for or restored to, and tracks the turn ID in turn/started ; upon receiving the matching turn/completed it saves the event and exits automatically, and the SQLite file is retained; after exit, poll reads the file directly and can still continue paging. Before initiating the next turn or resuming live events, first re-execute start, then connect to the original thread following the continuation procedure. One bridge connection accepts only one target session; attempting to create or restore another session will report an error; parallel tasks need to use different CODEX_SKILL_STATE. If the connection is unexpectedly disconnected, the bridge also exits, andstart can be restarted after waiting for the old process to release the connection. The directory contains task content; handle it as local private data.CODEX_SKILL_STATE can specify another local state root directory. After modifying CODEX_HOME , confirm that the phone is still connected to the same service.
2. Dispatch or continue
Pass parameters using a JSON file to avoid splicing the user's raw text into the shell. For how to prepare the file, see call template, and after verifying the results, clean up input files that are no longer needed.
Task directory and trust
cwd defaults to the main assistant's current working directory (the current project); when the user specifies another directory, use its path.thread/start does not check whether the directory exists and does not create directories, so verify before creating:
- Whether the directory exists. If it does not exist, explain the path and ask the user whether to create it; only after agreement,
mkdir, and do not create automatically. - Whether it is already trusted. Use
config/readto checkprojectsfor that path'strust_level(only look at whether that path itself or its parent istrusted)。 - When not trusted, ask the user whether to trust the directory; trust is a security setting, and do not write it without explicit consent. After agreement, only append to
$CODEX_HOME/config.tomla segment of[projects."<绝对路径>"]plustrust_level = "trusted", back up before writing, and do not modify other configurations; the bridge does not allowconfig/value/write, and do not bypass the whitelist. - If the directory is missing or untrusted and the user has not agreed, do not create a new thread; explain the reason and continue with other authorized work.
According to New and dispatch template create and subscribe to a session through bridge thread/start , then dispatch using codex queue . Save the thread ID and valid configuration; obtain the turn ID from subsequent turn/started or historical queries; the queued message ID returned by the CLI is not a turn ID. Provide the thread ID only in this reply after creation. Before sending, confirm the directory and target service; for cross-client verification scope, see protocol description。
For existing tasks, follow continuation template first follow query template call thread/read(parameter {"threadId":"..."}) to check the status; when you need to receive its live events, call the same service's thread/resume, passing only threadId and excludeTurns:true, without overriding the configuration. Resuming will not automatically send a new task; when a new turn is needed, use codex queue. When the current round is still executing, the user corrects the requirement using turn/steer, not queue as a substitute. Confirm the subscription succeeded before dispatching, to avoid missing fast-completing rounds.
All write operations submitted through the bridge must have a unique --operation-id. After the same operation times out, retry with the original ID and original parameters, and the bridge will return the saved result;thread/resume deduplication is limited to the same connection, and after reconnecting it will re-subscribe; use a new ID for a different operation. When the backend times out or disconnects causing an unknown result, first query the original thread and bridge/lateResponse, and do not resend with a different ID. When creating a new thread times out and no ID was obtained, first verify thread/list and the events.
Archiving an existing session
When the user requests archiving, first use thread/read to verify the target session and the current shared service, then use the native codex archive. After archiving, use thread/list 's archived:true to query and verify the target ID, and report back after confirming success. The CLI does not use the bridge's --operation-id; if the result is unclear, check the status first and do not automatically resend. Only archive; do not restore the session or dispatch new tasks.
For invocation and post-archive queries, see archiving template。
3. Dynamic polling and correction
The diagram below details the interaction branches of mode 1; for the specific submission method, see this section and section 4. Manual handling items take priority over deviation notifications; "end current round" in the diagram means the Claude reply ends, not interrupting Codex.
轮询并核对最新状态
|
+-- 有待人工处理的问题 --> 用消息呈现问题与两种处理方式
| |
| v
| 结束当前轮,暂停轮询;桥接仍运行时保留连接
| |
| +-- 用户在 Claude 回答
| | --> 核对状态,提交仍需处理的答复
| | --> 恢复轮询
| |
| +-- 用户自行处理
| --> 等其在 Claude 告知已处理
| --> 恢复轮询,核对结果
|
+-- 无人工问题,但发现跑偏 --> 通知用户,继续轮询,不自行干预
| |
| +-- 用户给出纠正 --> 核对状态并转发 --> 继续轮询
| +-- 用户同意或未回复 -------------> 继续轮询
|
+-- 正常执行 / 内部超时 --> 继续轮询
|
+-- 连接断开 -------------> 核对已保存事件和历史,不当作完成
|
+-- Codex 当前轮已结束 ---> 桥接自动退出,从 SQLite 读完剩余分页
--> 核对结果与未完成项,再向用户回报
For the invocation method, see reading progress template。
Each poll must specify the Codex session ID via --thread , and continue with the cursor returned last time; the cursor is saved per task.--event-limit The range is 1 to 200, default 50; it is both the number of items to wait for and the maximum number of key events per page. Existing records also count toward the number of items; always filter out delta, quota, token usage, and skill change notifications, as well as reasoning (started/completed) where summary、content is empty; filtered events do not count toward the number of items.
When the specified number of items has not been reached, continue waiting; manual approval, structured questions, the end of a round in the target session, or disconnection will cause an early return. Each time, check both urgent_events、pending_requests and question_messages: urgent events will appear in urgent_events even if they are on a later page, regardless of the item limit. Urgent events within the page return only a {cursor, method} reference, and the body is read from events ; urgent events outside the page include the body and are deduplicated by event cursor . Resolved requests are not handled again.has_more=true : continue reading ordinary events with the returned cursor; handling urgent events does not mean the history has been fully read.
- The internal single wait limit is 100 seconds, the client connection timeout is 110 seconds, and the host tool timeout should be greater than 110 seconds. A long period without output does not mean Codex is stuck, but the main Agent or host tool may terminate the call because of it; this limit is used to avoid infinite waiting and cannot guarantee that the call will not be terminated early. If the call is terminated, first verify the bridge and session status, then query using the last confirmed cursor, and do not re-dispatch the task.
return_reasonindicates that the number of events has been reached, that urgent events need to be checked, or that there was a disconnection or timeout; a timeout does not mean completion, so continue querying with the returned cursor. Ordinary text questions are still identified by the main assistant reading the messages. - Decide whether to continue or pause according to the diagram above. The skill will not automatically wake the model after the host ends the current round, nor can it guarantee continued supervision after the App is closed.
- Deviation judgment is based on tool results, file changes, failure information, and user constraints. Notifications only state the original goal, the deviation, and the impact, and tell the user that they can reply with a correction request. No new output does not equal being stuck; do not repeat reminders for the same deviation when there is no new evidence or change in impact.
- Forward the user's correction request: first verify the turn; if active, use
turn/steer:{"threadId":"...","expectedTurnId":"正在执行的轮次ID","input":[{"type":"text","text":"用户的纠正要求"}]}; if it has ended, verify the latest result, first confirm that the bridge has subscribed to the original thread, then usecodex queueto pass in the requirements that still need to be executed. If the guidance fails, first verify the receiving status to avoid duplicate submission; subsequently poll to check whether the correction has taken effect. - When the user requests a stop, send
turn/interrupt, with the parametersthreadIdandturnId; only report that it has stopped after seeing the interrupted status in the completion event.
Completion determination only looks at the target turn's turn/completed, verifying the thread, turn, and status, distinguishing completed, failed, and interrupted;thread/status/changed idle,item/completed or ordinary messages cannot replace the completion event.
On first connection, event gaps, or after reconnection, verify results through thread/read and paginated history:thread/turns/list parameters can use {"threadId":"...","limit":20,"itemsView":"full","sortDirection":"desc"}. Do not treat a disconnected connection as task completion. For more troubleshooting and request handling, see Protocol and Pending Requests。
By default, poll retains the first 200 characters of command output, and indicates the original length when truncated; user messages only show "received user message" and the character count. When the full content is needed, reread using the original cursor plus --full . Completion events retain the turn's status/error, removing duplicate replies already delivered via message events; replies not yet delivered retain their full text. Empty question_messages、pending_requests、urgent_events is omitted, and defaults to an empty array.coverage The description is only returned in --full or on error. SQLite always stores the raw events; for specific output rules, see Polling Protocol。
4. Approval, Input, and Results
For replies, additional instructions, and interrupt commands, see Manual Handling Templates。
Manual handling items check both urgent_events、pending_requests、question_messages and questions in ordinary messages. An asynchronous question is an agentMessage with questions , and may require a reply even if pending_requests is empty.followup_observed_answer_unconfirmed only indicates that a user message appeared later; it is still necessary to verify whether the question was answered; after the corresponding user message is read, it will not continue to trigger an early return. After the next turn begins, the previous turn's question no longer appears in the current question snapshot. Requests invalidated by disconnection are retained for verification and are not repeatedly awakened as pending approval items for a new connection.
When pausing as shown above, present the specific question and available options as the last message of the current turn, without attaching metadata such as thread ID, turn ID, or request ID, and provide two methods:
- In Claude's reply: after verifying the latest status, submit service requests that still await a response according to the protocol description ; pass asynchronous or ordinary text answers into the original thread using the message forwarding method in Section 3, without calling
respond. After resuming polling, verify the receiving result. - Handle it yourself and inform Claude: after receiving "handled, you can continue," resume querying and verify the handling result; if there are still unresolved issues, ask again and pause. "Handled" is not a basis for submitting an answer or approval decision on the user's behalf; if the user only says "I'll go handle it," continue waiting.
Before pausing, save the thread ID, turn ID, cursor, and pending items. Codex's independent work may still continue; if the turn ends while waiting for the user, the bridge will exit automatically. When resuming, first read the saved events; when a live connection is needed or a reply must be submitted, reconnect start and verify the session state. Do not resubmit requests that have already been resolved, and do not answer requests that have expired or become invalid due to disconnection using old IDs.
The main assistant does not approve automatically, does not guess answers, and does not modify the approval policy to avoid blocking. The user not replying does not constitute new authorization.
The end of a Codex turn does not mean the goal has been achieved; after verifying the acceptance criteria, report the results, verification status, and unfinished items, retaining the necessary errors, qualifications, and paths. The thread ID, turn ID, cursor, and operation ID are kept only internally for subsequent continuation and no longer appear in the report.