09 / 10Operator49 min lesson + lab

Automate, Embed, and Run Long Work Safely

Turn a trusted workflow into non-interactive, scheduled, SDK-driven, app-server, or long-running work with idempotence, checkpoints, bounded authority, and receipts for success and failure.

By the end

A repeatable `codex exec` job, a working SDK proof, an app-server protocol map, and an automation contract that avoids duplicate side effects and hands control back with evidence.

01

Earn the right to automate

Automation magnifies the quality of the underlying workflow. If the manual process has unclear inputs, unstable decisions, broad permissions, or inconsistent evidence, scheduling it will produce failures faster and with less visibility. Repeat the workflow under supervision until its boundaries and common exceptions are known.

Choose an automation candidate with a clear trigger, bounded data, deterministic prechecks, recoverable actions, and an operator who owns exceptions. Read-only monitoring, classification, and draft preparation are safer first candidates than publishing, deletion, payments, or external communication.

02

Use non-interactive mode deliberately

Non-interactive execution lets a script or workflow invoke Codex and receive output without an ongoing chat. That makes inputs, environment, exit behavior, and machine-readable results more important. Use the current CLI help and official non-interactive documentation for exact flags because command options can evolve.

Bound the task with an explicit working directory, authority profile, expected output, and timeout or stopping condition. Capture standard output, standard error, exit status, and produced artifacts. A paragraph that sounds successful is not enough for a machine-run workflow; require a result shape that downstream logic can validate.

03

Choose `exec`, the SDK, or app-server for the right integration

Use `codex exec` for shell scripts, CI steps, scheduled jobs, and pipelines. It streams progress to standard error and prints the final message to standard output. `--json` changes standard output to JSON Lines events; `--output-schema` validates the final response against a JSON Schema; `-o` writes the last message to a file. It starts read-only by default, and `--sandbox workspace-write` is the explicit step when a controlled job must edit.

Use `@openai/codex-sdk` in server-side Node.js when your application needs to start, continue, or resume Codex threads programmatically. Use `codex app-server` only for a rich client that needs the lower-level JSON-RPC interface for authentication, conversation history, approvals, and streamed events. App-server powers clients such as the IDE extension; ordinary CI automation should use the SDK or `codex exec` instead.

04

Understand app-server before building a client

The default app-server transport is newline-delimited JSON over standard input and output. A client starts the process, sends `initialize`, sends the `initialized` notification, then starts a thread and a turn while continuously reading notifications. Generate TypeScript or JSON Schema files from the installed CLI so the client matches that exact Codex version instead of copying protocol shapes from a blog post.

A local WebSocket listener exists for remote terminal and client experiments, but the current documentation marks that transport experimental and unsupported. Do not expose a non-loopback listener without the documented authentication and TLS protections. For this guide's lab, stay on the default stdio transport and generate schemas; that teaches the protocol boundary without creating a network service.

05

Schedule only a workflow that already has receipts

The desktop app can schedule a task after the prompt, project, cadence, and permission boundary are defined. In a Git project, scheduled tasks can use dedicated background worktrees so they do not collide with foreground changes; in a non-version-controlled project they run directly in the project directory. That difference should influence whether the task is safe to schedule.

A schedule is an operator-owned product. Define how to disable it, where results appear, what counts as unchanged, and which failures notify a human. Start with read-only drift checks. Do not schedule publishing, deletion, messaging, or production mutation until supervised runs demonstrate idempotence, recovery, and explicit authority for the side effect.

06

Design recurrence around state

A scheduled task runs again. That single fact changes the design. The workflow must know which inputs are new, which work is already complete, and what should happen after an interrupted run. Idempotence means repeating the same operation does not create an unintended duplicate effect.

Use stable identifiers, checkpoints, last-success markers, and explicit deduplication where side effects are possible. Do not mark work complete until the receipt is durably stored. If a run fails after partially acting, the next run should reconcile what happened rather than starting blindly from the beginning.

07

Manage long-running objectives

Some work is not a schedule but an objective that requires many steps, checkpoints, or external waits. Break it into milestones with acceptance criteria and store progress outside transient reasoning. The agent should be able to resume from the last proven state and explain what is complete, active, pending, or blocked.

Long duration does not broaden authority. A goal to finish a release still does not authorize publishing, buying services, changing permissions, or contacting people unless those actions were explicitly included. Reconfirm consequential steps when the external state may have changed since the goal began.

08

Operate with receipts and human handoffs

Every run should produce a compact receipt containing the trigger, input window, environment, actions, outputs, checks, cost or usage signal when available, and remaining exceptions. Unchanged state is a valid monitoring result, not a reason to invent work.

Define escalation before launch: which failure retries automatically, which pauses, which pages a human, and what evidence accompanies the alert. A human should be able to resume without reconstructing the agent's hidden context.

Working reference

Commands and patterns

Inspect non-interactive options

codex exec --help

Use the live CLI output for current flags, output formats, working-directory controls, and automation behavior.

Run a bounded one-off task

codex exec --ephemeral "Inspect this repository and report documentation links that point to missing local files. Do not edit anything."

A read-only documentation check is a safer first non-interactive workflow than a task with side effects. `--ephemeral` avoids persisting session rollout files for this one-off run.

Capture machine-readable events

codex exec --json --ephemeral "Summarize the repository structure and do not edit files." > codex-events.jsonl
jq -s 'map(.type) | unique' codex-events.jsonl

JSONL includes thread, turn, item, error, and usage events. Validate events rather than parsing prose progress; review the output file before using it downstream.

Install the TypeScript SDK package

npm install @openai/codex-sdk

Run this shell command in a disposable Node.js 18+ project. Confirm the installed package version in the lockfile before relying on an SDK behavior in production.

File contents: run-codex.mjs

import { writeFile } from "node:fs/promises";
import { Codex } from "@openai/codex-sdk";

const codex = new Codex();
const readOnlyOptions = {
  workingDirectory: process.cwd(),
  sandboxMode: "read-only",
  approvalPolicy: "never",
};

const thread = codex.startThread(readOnlyOptions);
const first = await thread.run(
  "List this repository's test commands and cite the files that define them."
);

const threadId = thread.id;
if (!threadId) throw new Error("Codex did not return a thread ID.");
await writeFile("codex-thread-id.txt", `${threadId}\n`, "utf8");

const resumed = codex.resumeThread(threadId, readOnlyOptions);
const second = await resumed.run(
  "Continue from the prior evidence and identify the smallest safe test to run first."
);

await writeFile(
  "codex-sdk-receipt.json",
  JSON.stringify(
    { threadId, first: first.finalResponse, second: second.finalResponse },
    null,
    2
  ) + "\n",
  "utf8"
);
console.log({ threadId, first: first.finalResponse, second: second.finalResponse });

Save this block exactly as `run-codex.mjs`. `sandboxMode` makes the spawned Codex agent read-only, while the supervising Node process intentionally writes the two local receipt files; host-process writes are outside the agent sandbox and should stay limited to named evidence artifacts. This example deliberately selects the older sandbox engine, so its loaded configs must contain no newer permission-profile settings.

Run the SDK resume proof

node run-codex.mjs

A successful run creates `codex-thread-id.txt` and `codex-sdk-receipt.json`, then prints the same thread ID and both final responses. The second turn must be created with `resumeThread(threadId, readOnlyOptions)` rather than a new thread.

Generate app-server schemas for the installed version

codex app-server generate-ts --out ./codex-app-server-schemas
codex app-server generate-json-schema --out ./codex-app-server-json-schema

These generated contracts match the Codex version that produced them. Use app-server for a rich client; use the SDK for job automation.

When the happy path breaks

Failure modes

01

Symptom

A scheduled run creates duplicate records, messages, issues, or deployments.

Likely cause

The workflow has no stable input identity, checkpoint, deduplication, or reconciliation after partial success.

Recovery

Add idempotency keys or stable identifiers, persist receipts only after verification, and make the next run reconcile prior effects before acting.

02

Symptom

Automation reports success because Codex returned fluent text, but no required artifact exists.

Likely cause

The runner validated narrative output rather than an explicit result contract and evidence.

Recovery

Require a machine-checkable output shape, validate files or external receipts directly, and treat missing evidence as failure.

03

Symptom

A long-running task continues acting after requirements or external state changed.

Likely cause

The objective has milestones but no freshness checks or renewed approval before consequential steps.

Recovery

Re-read current source and external state at milestone boundaries, invalidate stale assumptions, and reconfirm authority before irreversible actions.

04

Symptom

A failed run retries forever or leaves no useful handoff.

Likely cause

Retry count, blocker classification, stop conditions, and operator receipt were not designed.

Recovery

Bound retries, distinguish transient from structural failures, persist the last proven checkpoint, and alert the owner with exact evidence and safe next action.

Hands-on lab

Automate a read-only drift check

Turn a supervised repository check into a safe non-interactive workflow, then design its scheduled and long-running behavior.

  1. 01Choose a read-only check such as broken local documentation links. Run it interactively twice and document inputs, expected output, false positives, and completion evidence.
  2. 02Run the `codex exec --json --ephemeral` example inside the Git repository. Inspect standard error separately from the JSONL file and confirm the event stream reaches `turn.completed` or exposes a failure.
  3. 03Create a small JSON Schema for fields `status`, `findings`, and `checked_paths`; rerun with `--output-schema <schema-file>` and `-o <result-file>`, then validate that file with an ordinary JSON parser.
  4. 04For the SDK exercise, deliberately choose its typed older-sandbox lane: in the disposable setup, confirm that no loaded config contains `default_permissions` or `[permissions]`. Keep `sandboxMode: "read-only"` and `approvalPolicy: "never"` in `readOnlyOptions`; do not rely on prompt prose for authority and do not add newer permission-profile keys.
  5. 05Run the separate install command, save the dedicated file-content block as `run-codex.mjs`, and then run `node run-codex.mjs`. Prove that the first turn populated `thread.id`, that `codex-thread-id.txt` contains that exact value, that `resumeThread(threadId, readOnlyOptions)` completed the second turn, and that `codex-sdk-receipt.json` contains both final responses under one thread identity.
  6. 06Explain the authority boundary in the receipt: the Codex agent stayed read-only because both turns received `readOnlyOptions`; the supervising Node process—not the agent—wrote only the two named local receipt files.
  7. 07Generate TypeScript and JSON schemas from `codex app-server`. Open the generated thread and turn types and write the required sequence: start process, initialize, initialized, thread start, turn start, notifications.
  8. 08Run the same read-only input twice and prove no duplicate side effects. Then simulate a missing dependency and verify failure receipt and human handoff.
  9. 09Design, but do not enable, a desktop scheduled task for this check. Name its Git project, cadence, permission profile, dedicated-worktree behavior, unchanged receipt, notification rule, and disable path.
  10. 10Write what additional controls would be required before allowing edits or external actions.

Deliverable: A repeatable read-only `codex exec` job with structured output, a two-turn SDK proof, generated app-server schemas and protocol map, plus success, unchanged, failure, and recovery receipts.

Reusable artifact

Automation operating contract

A specification for recurring or long-running Codex work that can be operated by someone other than its author.

Objective: [bounded recurring outcome]
Trigger: [schedule, event, or manual start]
Input window and identity: [what is new and how it is keyed]
Environment and working directory: [exact target]
Authority: [read / draft / write scopes]
Prechecks: [conditions required before action]
Result contract: [machine-checkable fields or artifacts]
Idempotence and deduplication: [mechanism]
Checkpoint and resume: [durable state]
Retries and timeout: [bounded policy]
Escalate when: [structural blocker or consequential action]
Run receipt: [trigger, actions, checks, changes, exceptions]
Owner, pause, and disable path: [human operator]

Before you move on

Receipt checklist

  • The workflow was proven manually before it was automated.
  • Non-interactive execution has an explicit environment, authority boundary, output contract, and stop condition.
  • Repeating the same input produces no unintended duplicate side effect.
  • Success, unchanged, and failure paths each produce an operator-readable receipt.
  • A simulated blocker stops safely and hands off the last proven checkpoint and next action.

Primary sources

Continue with the source

Home
Book
Blog
Calugaru Labs
GitHub
LinkedIn
Email
Theme