OpenAI Codex CLI Runner

ClawSkills 作者 sbrin v1.0.0

Launch OpenAI Codex CLI async in background with automatic delivery to Telegram/WhatsApp. Use for coding, refactoring, codebase research, file generation, and complex multi-step automations. NOT for quick one-off questions or real-time interactive tasks. Includes strict thread-safe routing + E2E operator validation workflow.

源码 ↗

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install clawskills:sbrin~codex-code-task
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Asbrin~codex-code-task/file -o codex-code-task.md
Git 仓库获取源码
git clone https://github.com/openclaw/skills/commit/cf3458e375bd82f9f7952abbd38ededd40e0da31
# Codex Code Task (Async)

Run OpenAI Codex CLI in background — zero OpenClaw tokens while it works. Results delivered to WhatsApp or Telegram automatically.

## Important: Codex = General AI Agent

Codex is NOT just a coding tool. In `codex exec` mode it is a general-purpose AI agent with file access, shell execution, optional web search, and deep reasoning.

Use it for:

- **Research** — web search, synthesis, competitive analysis, user experience reports
- **Coding** — create tools, scripts, APIs, refactor codebases
- **Analysis** — read and analyze files, data, logs, source code
- **Content** — write docs, reports, summaries
- **Automations** — complex multi-step workflows with filesystem access

Give it prompts the same way you'd talk to a smart human — natural language, focused on WHAT you need, not HOW to do it.

**NOT for:**

- Quick questions
- Tasks needing real-time back-and-forth

## Quick Start

## What "run tests" means for this skill (critical)

When user asks things like:

- "прогони все тесты"
- "run tests"
- "проверь что всё работает"

it means **run the full E2E operator validation flow** for `run-task.py` routing + notifications.

It does **NOT** mean plain `pytest`/`unittest` discovery by default.

Required behavior:

1. Run routing validation first (`--validate-only`).
2. Launch smoke/E2E scenario via `nohup` and file-based prompt.
3. Wait for completion through normal async flow, not same-turn blocking.
4. Report PASS/FAIL against E2E criteria: routing, heartbeat, mid-task update, completion delivery.

Use the canonical protocol: **[references/testing-protocol.md](references/testing-protocol.md)** and the section below **Full E2E Test (reference)**.

## Async Boundary Rule (mandatory)

`run-task.py` is asynchronous orchestration.

After a successful `nohup` launch, the correct behavior is:

1. Send a short launch acknowledgment (PID/log/session)
2. **Stop this turn immediately**
3. Continue only when wake/completion event arrives in the same session

Do **not** keep waiting in the same turn for Codex completion.
Do **not** poll and then summarize in the same turn unless user explicitly asked for active live monitoring.

Anti-pattern:

- ❌ Launch `run-task.py` and keep responding as if completion should appear in this turn

Correct pattern:

- ✅ Launch `run-task.py` → acknowledge launch → stop → wait for wake

## Launch Confirmation Gate (mandatory)

Never claim "launched" until you have **positive launch proof**.

Required proof checklist:

1. `nohup` command returned a PID
2. process is alive (`ps -p <PID>`)
3. run log contains `🔧 Starting OpenAI Codex...` or equivalent startup marker
4. routing was validated (`--validate-only`) for Telegram thread runs

If launch fails with `❌ Invalid routing`:

- resolve via `sessions_list`
- rerun with explicit routing/session arguments if needed
- re-check proof checklist
- only then send launch acknowledgment

## Pre-launch planning note (mandatory)

Before launching Codex, post a short plan in chat:

- how you plan to solve the task
- what result you expect from this run
- any clarifying assumptions
- whether you expect one iteration or staged follow-up

If staged: explicitly say this run is "phase 1" and what signal decides phase 2.

## Telegram Thread Safety (must-follow)

For Telegram thread runs, `run-task.py` is designed to either route correctly or fail immediately.

### Mandatory step before launch

Resolve the **current runtime session key** first, then launch with it.

- Get current key via `sessions_list` or runtime context
- If key is `agent:main:main:thread:<THREAD_ID>` → use it directly in `--session`
- Never derive `--session` from `chat_id` / sender heuristics

### Rules

- Use only `--session "agent:main:main:thread:<THREAD_ID>"` for thread tasks
- Never use `agent:main:telegram:user:<id>` for thread tasks
- If routing metadata is inconsistent, script exits with `❌ Invalid routing`
- Default mode is `--telegram-routing-mode auto`
- Force strict thread-only behavior with `--telegram-routing-mode thread-only`
- Force non-thread behavior with `--telegram-routing-mode allow-non-thread` or `--allow-main-telegram`

This is intentional: **abort fast > silent misroute**

⚠️ **ALWAYS launch via nohup** — exec timeout will kill the process otherwise.

⚠️ **NEVER put the task text directly in the shell command** — save the prompt to a file first, then use `$(cat file)`.

### WhatsApp

```bash
# Step 1: Save prompt to a temp file
write /tmp/codex-prompt.txt with your task text

# Step 2: Launch with $(cat ...)
nohup python3 {baseDir}/run-task.py \
  --task "$(cat /tmp/codex-prompt.txt)" \
  --project ~/projects/my-project \
  --session "agent:main:whatsapp:group:<JID>" \
  --timeout 900 \
  > /tmp/codex-run.log 2>&1 &
```

### Telegram (thread-safe default)

```bash
nohup python3 {baseDir}/run-task.py \
  --task "$(cat /tmp/codex-prompt.txt)" \
  --project ~/projects/my-project \
  --session "agent:main:main:thread:<THREAD_ID>" \
  --timeout 900 \
  > /tmp/codex-run.log 2>&1 &
```

> Do **NOT** use `agent:main:telegram:user:<id>` for thread tests/runs.

### Telegram Threaded Mode (1:1 DM with threads)

When OpenClaw is used in Telegram threaded mode, each thread has its own session key like `agent:main:main:thread:369520`.

**Fail-safe routing (NEW):** `run-task.py` now enforces strict thread routing.
- If `--session` contains `:thread:<id>`, the script **refuses to start** unless Telegram target + thread session UUID are resolved.
- It auto-resolves missing values from `sessions_list` when possible.
- If the session is inactive and not returned by API, it falls back to local session files: `~/.openclaw/agents/main/sessions/*-topic-<thread_id>.jsonl`.
- If provided `--notify-session-id` mismatches the session key, it exits with error.
- Result: misrouted launches/heartbeats to main chat are blocked before Codex starts.

Use `--notify-session-id` to wake the exact thread session:

```bash
nohup python3 {baseDir}/run-task.py \
  --task "$(cat /tmp/codex-prompt.txt)" \
  --project ~/projects/my-project \
  --session "agent:main:main:thread:369520" \
  --timeout 900 \
  > /tmp/codex-run.log 2>&1 &
```

All 5 notification types route to the DM thread when `--session` key contains `:thread:<id>` ✅

- `--notify-session-id` — optional override. Usually auto-resolved from session metadata/files.
- `--notify-thread-id` — optional override. Usually auto-extracted from `--session`.
- `--reply-to-message-id` — optional debug field; avoid for DM thread routing.
- `--validate-only` — resolve routing and exit (no Codex run). Use this to verify thread launch args safely.
- `--notify-channel` — optional channel hint (`telegram`/`whatsapp`); target is always auto-resolved from session metadata.
- `--timeout` — max runtime in seconds (default: 7200 = 2 hours)
- `--completion-mode` — optional legacy hint (`single` default, `iterate` if explicitly needed)
- `--max-iterations` — optional budget hint when using iterate mode
- `--trace-live` — emit live technical trace markers into the same chat/thread (debug mode)
- Always redirect stdout/stderr to a log file

### Why file-based prompts?

Research/complex prompts contain single quotes, double quotes, markdown, backticks — any of these break shell argument parsing. Saving to a file and reading with `$(cat ...)` avoids all quoting issues.

## Channel Detection

The `detect_channel()` function determines where to send notifications:

1. **Deterministic auto-resolve** — target is resolved from session metadata/session key (no manual target flag)
2. **WhatsApp auto-detect** — if the session key contains `@g.us` (WhatsApp group JID), WhatsApp is used
3. **Fail fast on unresolved Telegram target** — script exits with `❌ Invalid routing` instead of silent misroute

```python
def detect_channel(session_key):
    if NOTIFY_CHANNEL_OVERRIDE and NOTIFY_TARGET_OVERRIDE:
        return NOTIFY_CHANNEL_OVERRIDE, NOTIFY_TARGET_OVERRIDE
    jid = extract_group_jid(session