Repository navigation
feat(tasks): add Codex async callbacks and restore BTW history - #58
muggle-stack wants to merge 6 commits into
Conversation
- 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.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@codex review |
There was a problem hiding this comment.
💡 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".
| 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, |
There was a problem hiding this comment.
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 👍 / 👎.
| current = store.get(task_id) | ||
| if current["cancel_requested"]: | ||
| reason = "cancelled" | ||
| store.update(task_id, state=reason or ("completed" if code == 0 else "failed"), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
💡 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".
| 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)) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
💡 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".
| return | ||
| command = asyncio.create_task(native.rpc("command/exec", { | ||
| "command": spec["argv"], "cwd": spec["cwd"], "processId": task_id, | ||
| "timeoutMs": timeout_ms, |
There was a problem hiding this comment.
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
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
task_start,task_status,task_resultandtask_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.timed_outwithout starting the command; submitted commands receive only the remaining budget.timed_outeven when its integer-millisecond deadline wins the local poll. Preserve early explicit exit 124 as failure and cancellation as the higher-priority outcome.cc-remote tasks refreshto reload an already-enabled MCP from the installed release and verify an existing thread's read-only tool call without restarting its daemon.nounset, with regression coverage for both shell versions.cc-remote updateupgrades in both READMEs and installation guides.Compatibility and scope
master; protocol v74 and product version v4.0.10 are unchanged.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.npm --prefix web run build,npm --prefix web run test:reliabilityandnpm --prefix web run lint: passed.bash -nandshellcheck -xchecks for deployment scripts: passed.git diff --check: passed.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_outputdrops 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.