Add wait/wake support for goals without spending tokens

Open 💬 3 comments Opened Jun 14, 2026 by jerryfane

What variant of Codex are you using?

CLI/TUI and app-server goal runtime. This also matters for SDK/app-server integrations that drive long-running goals.

What feature would you like to see?

Add first-class wait/wake support for goals so a goal can stop making model calls while it is waiting for time or an external event, then continue when the wait condition is satisfied.

Current behavior, as I understand it from the goal runtime:

  • Active /goal work continues automatically when the thread becomes idle.
  • /goal pause and /goal resume already let a user stop and restart continuation manually.
  • Blocked, usage-limited, budget-limited, and complete statuses stop automatic continuation.
  • There is not currently a durable "waiting" state that can be resumed automatically by a timer or external signal without spending tokens during the wait.

The missing capability is useful for goals like:

  • "Check CI until it finishes, then fix failures."
  • "Wait until the deployment completes, then verify it."
  • "Poll this external job occasionally, but do not burn tokens while nothing can be done."
  • "Resume this goal when another process/webhook tells Codex there is new information."

A good shape might be to separate "goal continuation" from "wait scheduling", similar to how other agent harnesses separate active goals from loop/scheduler primitives:

  • A goal can enter a waiting state with a bounded reason and optional wakeAt timestamp.
  • While waiting, the runtime must not start another model turn or consume tokens.
  • A waiting goal can be resumed by user action, an app-server API call, a timer, or a future external-event/channel mechanism.
  • Waiting state should be persisted so it survives app/server restart and thread resume.
  • Goal update notifications should expose the waiting state and wake metadata to clients.

A small first stage could avoid building every event source at once:

  1. Add a persisted waiting goal status with optional wakeAt/reason metadata.
  2. Teach the idle continuation path to treat waiting like paused for token usage.
  3. Add an app-server way for clients/supervisors to wake the goal by setting it active again.
  4. Optionally add a later scheduler that wakes goals at wakeAt.

Additional information

I searched existing issues/PRs and found adjacent work, but not an exact request for durable wait/wake semantics:

  • #23094 added blocked/usage-limited statuses so continuation loops pause on blockers and usage limits.
  • #23177 reported blocked goals continuing to consume tokens; this appears addressed by #23094.
  • #24531 discussed active goals continuing despite a chat-level "stop" message; maintainers clarified active goals are intended to keep going and users should pause/clear them.
  • #24016 / #24321 cover promptless exec resume/follow behavior for active goals, which is related to supervising goals but not the same as a no-token waiting state.
  • #22312 covered approval-wait time accounting, which is also adjacent but narrower.

Prior-art note: Claude Code appears to expose this kind of separation through /goal for autonomous continuation, /loop for scheduled prompts, and channels/webhooks for external events. OpenCode appears to support similar patterns mostly through plugins/community tooling rather than a core built-in. The Codex version does not have to copy those interfaces exactly, but the product need seems similar: let long-running goals wait without spending tokens, then wake from time or outside state.

View original on GitHub ↗

3 Comments

devansh-jain-18 · 1 month ago

I hit this exact gap in a delegated-work + human-review workflow.

Generic reproduction:

  1. Start an active goal whose plan delegates the current step to a separate background task/thread.
  2. Require the coordinator to wait for that worker’s final callback, then present the evidence for human review before starting the next step.
  3. While the worker is still actively making progress, the coordinator has no meaningful action to take.
  4. Because the goal remains active, the runtime repeatedly starts continuation turns that do little more than check whether the worker finished.

In one run this produced 15 automatic continuation turns and increased the goal accounting by roughly 101k tokens while the actual work was proceeding in the separate task.

Neither existing terminal state fits:

  • complete is false because later reviewed steps remain.
  • blocked is also false because the objective is still making progress and there is no genuine impasse.
  • The agent-visible tool cannot set paused, and manual pause loses the desired automatic callback behavior.

This suggests delegated task completion and HITL review are additional concrete wake sources for the proposed waiting state:

  • wakeOnThreadCompletion(threadId)
  • wakeOnUserReview / next user message
  • optionally a generic app-server event subscription

While waiting, the parent should make zero model calls. A child terminal event should enqueue one continuation turn, and a review gate should then remain dormant until the user responds.

Comparable event-driven patterns exist elsewhere: Claude Code agent teams deliver teammate completion/idle notifications and unblock task dependencies automatically; its async hooks support event-triggered rewake; GitHub Copilot CLI exposes agent_completed and agent_idle notifications. Those patterns avoid making the coordinator poll with model turns.

This is not a request to let the model abandon an active goal. It is a non-terminal scheduling state that preserves the objective while separating “work is still progressing elsewhere” from “start another model turn now.”

B143KC47 · 1 month ago

+1 — I'm seeing the same on CLI: an active goal keeps emitting placeholder tokens while waiting, instead of waiting silently. A durable waiting state that can be woken by a timer would solve this

vincenthcui · 3 days ago

I implemented a narrower version of the wait/wake separation proposed here:

https://github.com/vincenthcui/codex/pull/1

Instead of adding a new Goal status, it keeps the Goal active and persists a separate continuation trigger: onIdle or timer-owned. While timer-owned, the Goal runtime makes zero automatic idle model calls; the scheduler later delivers one explicit turn.

The design also adds cross-process CAS using goalVersion + automationId + generation. This prevents stale timer operations from affecting a replacement Goal or newer schedule generation.

@jerryfane, could you review whether this narrower continuation-trigger model preserves the semantics you wanted from a durable waiting state? The PR leaves external-event wake sources for later, but the persisted trigger model can be extended beyond timers.