Skip to content

feat(tasks): add Codex async callbacks and restore BTW history - #58

Open
muggle-stack wants to merge 6 commits into
masterfrom
feat/codex-async-tasks
Open

muggle-stack wants to merge 6 commits into
masterfrom
feat/codex-async-tasks

Conversation

@muggle-stack

@muggle-stack muggle-stack commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Codex can start long-running commands or external-agent jobs without polling them throughout a conversation. This adds an opt-in MCP that returns completion output to the originating thread through the existing official daemon, plus visible background-task activity and continuation receipts in cc-remote.

Changes

  • Add task_start, task_status, task_result and task_cancel, with native caller binding, account/thread isolation, idempotent submission, bounded output and retained receipts. Commands are not automatically rerun after ambiguous execution; uncertain callback delivery is not resent.
  • Recheck the task deadline immediately before native command submission, after connection, thread validation, scheduling and store waits. Expired tasks are recorded as timed_out without starting the command; submitted commands receive only the remaining budget.
  • Classify native command expiration as timed_out even when its integer-millisecond deadline wins the local poll. Preserve early explicit exit 124 as failure and cancellation as the higher-priority outcome.
  • Track the exact callback turn after acceptance. Only normal turn completion marks delivery successful; provider failure/interruption remains visible with retained output. Recovery checks do not resend accepted callbacks, run commands again, or resume engines; unconfirmed outcomes stop tracking after 24 hours.
  • Show background-task activity independently of the main turn, and preserve “Codex 收到后台消息,继续处理” boundaries through live output, reconnects and restored history.
  • Add cc-remote tasks refresh to reload an already-enabled MCP from the installed release and verify an existing thread's read-only tool call without restarting its daemon.
  • Restore Claude BTW history before displaying it, retain the correct completion event, and preserve background continuation order.
  • Fix Wrapper installation under macOS Bash 3.2 and Supervisor preflight under nounset, with regression coverage for both shell versions.
  • Recommend stable Release installations and subsequent cc-remote update upgrades in both READMEs and installation guides.

Compatibility and scope

  • Based on current master; protocol v74 and product version v4.0.10 are unchanged.
  • The MCP is opt-in and requires Codex 0.159.2+ with an existing shared daemon. Execution uses the account's configured command permissions, not temporary thread permission overrides.
  • Strict Responses providers can still reject Codex 0.159.2 call-ID-less tool output. This change detects and reports that failure; it does not rewrite native model requests or add provider credentials. Old receipts without a callback turn ID keep their legacy acceptance-only meaning.
  • Task records are bounded per account; pending or uncertain receipts are preserved. Files created by the submitted command are outside task-record cleanup.
  • Native App/CLI presentation and combined native-task/MCP behavior have not received full cross-client live acceptance. A real 30-second automatic callback was verified on the deployed feature snapshot before this rebase.

Validation

  • .venv/bin/python -m pytest: 5694 passed, 4 skipped, 14 Claude SDK permission-mode warnings.
  • uvx --from ruff==0.15.13 ruff check cc_remote tests deploy: passed.
  • Node 24.20.0: npm --prefix web run build, npm --prefix web run test:reliability and npm --prefix web run lint: passed.
  • Required bash -n and shellcheck -x checks for deployment scripts: passed.
  • git diff --check: passed.
  • Bash 3.2/5 installer regressions cover fresh installation, upgrades, same-release activation, systemd and Supervisor paths. These and all other automated checks use no live model calls.

Review verification

  • The timeout-state finding is reproduced by a deterministic boundary regression; the fix compares native exit 124 with the submitted command deadline using a monotonic clock. Coverage includes early exit 124, ordinary failure, successful completion and concurrent cancellation.

  • The output-cap P1 is not supported by the full pinned native path. In Codex 0.159.2, spawn_process_output drops its receiver on return, while the lower-level pipe reader ignores closed-channel send errors and keeps reading to EOF. The existing native output caps are preserved.

  • An isolated Codex 0.159.2 app-server probe sent 4 MiB to stdout, then separately 4 MiB to stderr, with the existing 32 KiB per-stream cap: both completed with exit 0 and exactly 32 KiB of notifications. The uncapped controls also completed. A 100 ms native timeout returned exit 124. These probes created no thread/turn and called no model; they do not claim full cross-client acceptance.

  • Reproduced the strict-provider P2 with the real isolated Codex 0.159.2 app-server and a localhost-only mock Responses endpoint. The initial native receipt was accepted, the endpoint rejected the missing call ID, and the exact turn ended failed. The updated worker recorded notification failure, retained output and sent exactly one callback across recovery. No live provider or model credentials were used.

  • Callback regression tests cover delayed success/failure/interruption, exact-turn pagination past an unrelated successful turn, missing history and RPC/socket failures, tracking expiry, concurrent additive database migration, safe public snapshots, visible failure warnings and active-task priority when retained warnings reach the snapshot cap.

  • Eight isolated native-transport regressions cover connection, thread-verification, scheduling and database delays: a partial delay reduces the submitted timeout, while an exhausted deadline sends no command and creates no side-effect marker.

- Run account-scoped commands through the shared official daemon and deliver bounded results to their originating thread.
- Show independent background activity and preserve native callback boundaries across live output and restored history.
- Refresh enabled task MCP connections per release and verify existing-thread calls without restarting the daemon.
- Restore Claude side chats at the final history position and retain exact completion ownership and continuation order.
- Validate the full local gate, deploy the frozen source across all four targets, and verify a native 30-second callback before committing.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-10T12:35:21.282809Z 9a9eb3b New commits
🔒 Security Review ✅ Completed 2026-10-10T12:38:49.845874Z 9a9eb3b New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@muggle-stack

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9cc1da1555

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cc_remote/async_tasks.py
command = asyncio.create_task(native.rpc("command/exec", {
"command": spec["argv"], "cwd": spec["cwd"], "processId": task_id,
"timeoutMs": max(1, int(remaining * 1000)),
"outputBytesCap": OUTPUT_BYTES // 2, "streamStdoutStderr": True,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep command/exec draining after the output cap

For any task that writes more than 32 KiB to stdout or stderr, this per-stream cap can stall the command instead of merely truncating its receipt. The pinned Codex 0.159.2 implementation stops its output receiver when cap_reached becomes true, which can back-pressure a still-writing child and is the documented cause of command/exec hanging after its cap; the fake daemon test misses this because it continues draining all output. Use a drain-safe native configuration while continuing to enforce OUTPUT_BYTES locally, otherwise verbose test suites and external agents can run until the task deadline and fail.

Useful? React with 👍 / 👎.

Comment thread cc_remote/async_tasks.py
current = store.get(task_id)
if current["cancel_requested"]:
reason = "cancelled"
store.update(task_id, state=reason or ("completed" if code == 0 else "failed"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record native command timeouts as timed_out

When the app-server enforces the supplied timeoutMs, the command future can complete before the polling loop sets reason; this expression then records the task as failed. Codex 0.159.2 explicitly returns exit code 124 for ExecExpirationOutcome::TimedOut, and the millisecond truncation used when constructing timeoutMs makes this race especially plausible. Derive the terminal state from the native timeout outcome/deadline as well, so commands that exhaust timeout_seconds produce the promised timed_out receipt and UI status.

Useful? React with 👍 / 👎.

- Recognize native timeout exit 124 at the submitted monotonic deadline before the local poll expires.
- Preserve early explicit exit 124 as failure and cancellation as the higher-priority result.
- Add boundary regressions and document timeout receipt semantics.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 59a20176e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cc_remote/async_tasks.py
Comment on lines +176 to +180
result = await native.rpc("turn/start", {
"threadId": task["sid"], "input": [],
"toolOutput": {"namespace": "cc_remote_tasks", "name": "task_result",
"output": json.dumps(receipt, ensure_ascii=False)},
}, before_send=lambda: store.begin_delivery(task_id))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid call-id-less callbacks on strict Responses providers

When the selected Codex account uses a strict non-OpenAI Responses provider, this callback is accepted and marked delivered, but the resulting model turn fails before processing the task result. The pinned app-server converts turn/start.toolOutput into a FunctionCallOutput with call_id: None (0.159.2 implementation), which is the upstream-documented cause of strict-provider rejection; because this worker treats the initial turn/start receipt as final success, it neither reports nor retries that failure. Use a provider-compatible external-message mechanism or track the callback turn's terminal outcome before recording successful delivery.

AGENTS.md reference: AGENTS.md:L6-L11

Useful? React with 👍 / 👎.

- Persist native callback acceptance separately from successful turn completion.
- Track the exact turn through bounded read-only checks across worker recovery without resending results.
- Retain and expose failed or unconfirmed callbacks while keeping active tasks visible.
- Cover provider failure, recovery, pagination, schema migration and failure presentation regressions.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c60f3d560d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cc_remote/async_tasks.py Outdated
return
command = asyncio.create_task(native.rpc("command/exec", {
"command": spec["argv"], "cwd": spec["cwd"], "processId": task_id,
"timeoutMs": timeout_ms,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recheck the deadline before submitting the command

When the daemon connection, initialization, or native.thread() verification is slow, remaining and timeout_ms may already be stale by the time this request is sent. Those pre-submission operations can consume longer than a short timeout_seconds, yet the command is still started with the original timeout budget and can perform side effects after its absolute task deadline. Recompute the remaining time immediately before submission and record timed_out without sending when the deadline has passed.

Useful? React with 👍 / 👎.

- Include connection, validation, scheduling and store waits in the command budget
- Record expiry without sending command/exec or allowing late side effects
- Cover each pre-submission delay with an isolated native transport regression
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants