Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Codex workflows

Run an agent-written JavaScript workflow from a Codex desktop shell call. The Bash launcher uses the app's bundled Node and Codex executables. The single workflow.mjs runner uses only Node built-ins and launches the bundled Codex CLI directly. Completion uses the desktop conversation bridge verified in this project. There are no npm dependencies, generated bundles, or MCP server.

AGENTS.md is the short entry point for agents; this README is the complete reference. Codex automatically discovers AGENTS.md in its project instruction chain when a run starts. Keeping it beside the runner makes it available when this directory is the project or working directory. An agent working in another project must explicitly read it, or that project's existing AGENTS.md can reference these workflow instructions. The runtime's location alone does not add its instructions to other projects. See the official discovery rules.

Discover and share as a skill

The self-contained codex-workflows skill packages the runtime, launcher, examples, and a runtime reference. Installing it at user scope makes its name and description available across projects. Codex can choose it when a task matches, or you can invoke $codex-workflows explicitly. Its full instructions load only when used. See the official skills guide.

Share release/codex-workflows-skill.zip. Extract it into ~/.agents/skills (the current documented user-skill location), producing ~/.agents/skills/codex-workflows/SKILL.md. Existing Codex installations also use ${CODEX_HOME:-$HOME/.codex}/skills; the local installation location is verified with the bundled CLI during setup. Install into one user-skill location to avoid duplicate entries. An update should replace that skill folder as a unit. Codex detects skill changes automatically; restart it if the skill does not appear.

The package resolves its resources from its own directory and runs in the calling project's working directory. Write workflows and run state there. The installed skill folder holds reusable code and instructions. The desktop bridge requirements below also apply to the skill.

To refresh the packaged skill after runtime, example, or documentation changes:

"$CODEX_MCP_NODE_PATH" scripts/package-skill.mjs

This copies the canonical root runtime into the skill and creates the zip; there is no compilation or dependency installation. The skill's seven-file payload contains no account data, local run state, or machine-specific paths.

Run

bash /path/to/codex-workflows/workflow.sh /path/to/review-changes.mjs \
  --args '{"base":"origin/main"}' \
  --model gpt-6.1-sol --effort xhigh

Pass the originating conversation's actual model and effort rather than assuming the CLI default matches the app. Launch from Codex: it supplies CODEX_MCP_NODE_PATH, CODEX_CLI_PATH, CODEX_THREAD_ID, CODEX_PERMISSION_PROFILE, and CODEX_APP_TOOLS_PIPE_PATH. The launcher also discovers bundled Node relative to CODEX_CLI_PATH when the Node variable is absent.

The command checks module syntax without evaluating it, persists the source, starts a detached worker, and returns JSON containing runId, taskId, transcriptDir, scriptFile, and dashboardFile. The worker establishes its desktop bridge connection while its trusted parent is alive. It then continues after the launcher and orchestrator turn end.

The default state directory is .workflow-state in the project. Use --state-dir consistently when changing it. The completion worker waits for the originating turn to finish and the destination chat to become idle, then sends one tool-output notification that starts a new turn. An unloaded chat can be resumed by that same path. Other user turns continue normally while the workflow runs.

This integration currently relies on the desktop app's internal bridge and signed-process checks. It was verified on macOS with the installed bundled runtime. It is not a documented public callback API, and compatibility with other desktop versions/platforms is not assumed. A missing bridge fails startup; the runner never silently substitutes a new conversation. For standalone execution, explicitly pass --no-notify.

Progress dashboard

Every new run automatically writes dashboard.html in its transcript directory. Open the returned dashboardFile directly in a browser. The page embeds the current snapshot: it never fetches another local file and needs no server or dependencies.

The dashboard shows planned/current phases, agent states and cache hits, token counts, elapsed time, the latest 20 narrator logs, errors, and a final result preview. Counts describe agents invoked so far, since later phases can add more. Full logs and results remain in the linked journal.

The runner rewrites the page atomically on progress changes and every minute during quiet waits. The browser reloads the HTML every 60 seconds; a manual refresh link is also available. The final page removes automatic refresh on completion, failure, or cancellation and remains as the run's report. Completion callback delivery continues independently. A resumed run gets its own dashboard.

Workflow format

export const meta = {
  name: 'review',
  description: 'Review changes across three lenses',
  phases: [{ title: 'Review' }],
};

export default async function workflow() {
  phase('Review');
  const results = await parallel(['correctness', 'security', 'tests'].map(lens => () =>
    agent(`Review ${args.base}..HEAD for ${lens}.`, {
      label: lens,
      readOnly: true,
    })
  ));
  return { results: results.filter(Boolean) };
}

Workflows are ordinary ES modules exporting meta and a default async function. Put workflow execution inside that function. meta is JSON data with a lowercase name, description, and optional phases; trusted expressions are allowed. Node checks syntax before launch, then the bounded script worker loads the module once and validates its exports and metadata. Invalid exports fail the run with a journaled error.

Static imports, dynamic import(), and native import attributes are supported. The worker executes the saved .mjs snapshot and uses Node's synchronous module-resolution hook to resolve its imports beside the original workflow, including when resuming. Imported modules retain their own native resolution rules. The runner requires Node 24+ (the installed desktop runtime is Node 24). Relative imports and import.meta.resolve() use the original directory; import.meta.url identifies the saved snapshot. For files opened through fs, use an explicit input path in args or the configured working directory.

Scripts are trusted ordinary JavaScript, with access to Node and the host filesystem/network subject to their process permissions. This intentionally relaxes the original sandbox requirement. A worker thread separates script execution from agent scheduling and lets wall-clock cancellation stop a busy loop. It is not a security sandbox.

The workflow functions are available both in scope and on globalThis:

API Behavior
agent(prompt, opts) Fresh CLI session; final text, validated JSON object, or null on agent failure/skip
parallel(thunks) Concurrent execution and a barrier; thrown thunks become null; at most 4096 thunks
pipeline(items, ...stages) Stages receive (previous, originalItem, index); a thrown stage stops that item; no stage-wide barrier
phase(title) Progress group for subsequently invoked agents; prefer opts.phase inside concurrent branches
log(message) Append narrator/progress output to the journal
args The structured JSON value supplied by --args or --args-file

Options for agent() are label, phase, schema, model, effort, agentType, isolation: 'worktree', readOnly, and replay: 'never'.

readOnly: true or agentType: 'reviewer' narrows agent permissions to read-only filesystem access, denies network/web search, and disables inherited connector/plugin tools. Writable agents retain the configured parent permission profile and workspace roots. New approvals are denied in background execution (approval_policy = "never"); the workflow sees a failure rather than an interactive approval prompt. No agent resumes the orchestrator thread or receives its latest user message. Each agent gets only its task, worker instructions, its optional profile instructions, and the project instructions loaded by Codex.

Structured output uses the CLI’s native --output-schema file. A small local validator checks the supported JSON Schema subset without dependencies. Invalid JSON or schema violations are sent back to the same agent thread for up to two correction attempts. Read-only API failures may retry once in a fresh thread; writable agents are not automatically retried after failure because their side effects may already have occurred.

The supported schema subset includes object properties/required/additionalProperties; array items, lengths, and uniqueness; primitive types (including unions); enum/const; numeric bounds; string lengths/pattern; property counts; and title/description/default. The root must be an object with properties, and required must be a subset of properties. Unsupported composition/ref keywords and detected contradictions are rejected. This is not a general JSON Schema satisfiability solver.

Deterministic concurrent resume

Agents execute concurrently under a FIFO concurrency cap. Results are released to the workflow in invocation order, independent of completion latency. After each release, the coordinator waits for a worker acknowledgment that promise reactions have drained and any newly invoked agents have been registered. The next result cannot overtake these reactions, including when it came from cache.

This fixes the pipeline replay problem: a cached result returning immediately cannot reorder downstream agent() calls. Nested fan-out and Promise.race are stable too; races among agent calls intentionally select by invocation order. The tradeoff is head-of-line waiting when an earlier agent is slow. Pipelines still overlap stages; there is no global barrier between stages.

Resume reads the old journal and replays its longest unchanged prefix. Call keys contain canonical prompt/options JSON (excluding display label/phase) and the execution context fingerprint. A prompt change in call 3 replays calls 1–2 and runs call 3 onward live. Context, args, runtime implementation, Codex binary metadata, configuration/instruction files, and workspace input changes invalidate reuse.

Only enforced read-only agents are reusable. If a run contains any non-reusable call, the next resume runs that whole run live: a concurrent writer could have changed the inputs observed by an earlier reader. Cached final responses are never treated as restored edits or worktrees. replay: 'never' disables reuse conservatively as well.

Workspace fingerprints include Git-tracked and untracked non-ignored file bytes, or a bounded recursive snapshot outside Git. Runtime state and node_modules are excluded. Use --cache-key to declare an explicit input revision for large workspaces or inputs outside that snapshot; change it whenever those inputs change. Do not mutate workflow inputs during execution. Deterministic control flow should depend only on args and workflow agent results, not timers or independent external I/O. Date.now(), zero-argument Date, and Math.random() throw to catch accidental nondeterminism; trusted scripts can bypass these guards, so they are a contract rather than an isolation boundary.

All launched/result entries have invocation indexes. Agent result records are written and fsynced when real work finishes; a separate deterministic release schedule controls what the script observes. Each live/replayed agent has its own JSONL transcript. Truncated completion messages point to the full journal result.

bash workflow.sh inspect wf_<id>
bash workflow.sh resume wf_<id>
# Use --script to resume with an edited script; args/settings otherwise carry forward.
bash workflow.sh resume wf_<id> --script edited-review.mjs
bash workflow.sh cancel wf_<id>
bash workflow.sh skip wf_<id> a_<agent-id>

Skip accepts an unambiguous label or agent ID from inspect. Cancelling interrupts active CLI subprocesses, prevents queued agents from starting, and produces a cancelled completion notification. Returned worktrees are retained for inspection: there is no automatic merge, deletion, commit, or push.

Notifications maintain a persistent state: pending, sending, delivered, failed, or unknown. The desktop message API has no idempotency key, so an ambiguous timeout/disconnect after sending is recorded as unknown and never automatically retried. This avoids duplicate turns; check the destination chat before arranging another delivery. The idle wait has a finite deadline (default 24 hours). The bridge connection is not replaced with an unrelated server on disconnect.

Limits default to max(1, min(16, availableCPUs - 2)) running agents, 1000 lifetime calls, one hour per workflow, 20 minutes per agent, and 128 MiB for the script worker's old-generation heap. Calls returned without awaiting them fail the run. Optional nested workflows and a turn-wide token budget are not included. The dashboard provides progress inspection; CLI status and journals provide inspection/control.

Context and library use

For custom permissions/profiles, supply --context-file with JSON like:

{
  "model": "gpt-6.1-sol",
  "effort": "xhigh",
  "cwd": "/path/to/project",
  "permissionProfile": ":workspace",
  "sandboxMode": "workspace-write",
  "workspaceRoots": ["/path/to/project"],
  "profiles": {
    "skeptic": { "readOnly": true, "instructions": "Try to refute each finding using evidence." }
  }
}

The exported Workflow(input, context, options) function in workflow.mjs accepts exactly one of script or scriptPath, plus args and resumeFromRunId. Library scriptPath defaults to previously returned paths only. The CLI deliberately allows any explicitly supplied file path. Neither interface adds tools to Codex or installs a server.

Test and share

No install or build step is needed. Run tests with the app's bundled Node:

"$CODEX_MCP_NODE_PATH" --test test/*.test.mjs

Share workflow.mjs and the optional workflow.sh launcher. Recipients use their desktop app's bundled Node/CLI and existing Codex login. They can launch the runner directly from a Codex shell call:

"$CODEX_MCP_NODE_PATH" /path/to/workflow.mjs /path/to/review-changes.mjs \
  --args '{"base":"origin/main"}' --model gpt-6.1-sol --effort xhigh

The same runner file handles CLI commands, detached background hosting, and worker-thread execution. State files and workflow snapshots are created as runs execute. The optional full release archive contains the runner, launcher, examples, README, AGENTS.md, prepared skill, and packaging helper, with no runtime binary, dependencies, credentials, or journals. For skill installation, use the smaller codex-workflows-skill.zip described above.

To create that archive:

mkdir -p release
"$CODEX_MCP_NODE_PATH" scripts/package-skill.mjs
tar -czf release/codex-workflows.tar.gz workflow.mjs workflow.sh examples README.md AGENTS.md skills scripts

Version 0.2 uses the native module format. Old async-body workflow scripts need their execution wrapped in export default async function workflow() { ... } before use. Resume caches from the previous implementation are invalidated by the runtime fingerprint/version.

Tests cover schema constraints and correction sessions, CLI JSONL transport/cancellation, module imports and evaluation, concurrency, pipeline/nested/race replay, prefix invalidation, permissions, side-effect caching rules, cancellation/skip, clock guards, busy-loop limits, background process lifetime, and desktop notification framing/queueing. A real CLI smoke workflow is provided in examples/cli-smoke.mjs.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages