Skip to content

No way to pause an agent or a pod — quota exhaustion is discovered independently by every agent sharing a credential pool #832

Description

@lilyshen0722

Problem

There is no way to make an agent, or every agent in a pod, stop consuming model
quota and resume later. The only available action is killing the wrapper
process, which is not a pause: it loses the process, survives nothing, and is
undone by the next commonly agent run.

Observed 2026-08-04. Four wrapper agents on one Claude account hit the session
limit. Each independently opened its client-side circuit breaker and backed off
alone. Because they share a credential pool, the other three kept probing the
same wall, so no single agent's backoff reduced total consumption. The account
was exhausted twice in one day.

What already exists, and why it is not enough

Mechanism Layer Why it does not solve this
spawn-retry circuit breaker (#794, cli 0.1.8) wrapper, in-memory Per-process. Resets on restart, cannot coordinate across agents, invisible to the kernel
AgentInstallation.config.heartbeat.everyMinutes kernel Changes cadence permanently; not a temporary state, and does not stop chat.mention delivery
Killing the wrapper operator Not durable, not scheduled, not per-pod, and loses local session state

The breaker was the right fix for storming. It is the wrong layer for
resting, because rest has to outlive the process and apply to a group.

Proposal — pausedUntil, kernel-side

Add a nullable timestamp to the runtime projection and honour it in two places:

  • schedulerService.dispatchAgentHeartbeats — skip an installation whose
    pausedUntil is in the future
  • agent event delivery — hold (do not drop) chat.mention and other events for
    a paused agent, so nothing is lost, it is only deferred

Kernel-side because it must survive a wrapper restart, apply to every driver
(openclaw, cloud-codex, BYO wrapper) rather than one, and be settable for a
group. A wrapper-side pause would be undone by the operator relaunching, which
is exactly what happens after a laptop reboot.

Three ways in

  1. Operator, explicitcommonly agent pause <name> [--until <t>|--for 4h]
    and commonly pod pause <podId>, plus the same in the inspector UI. Pod
    scope fans out over installations, the same shape as install scope.
  2. Self-pause on a quota class — when the wrapper classifies a failure as
    QUOTA, it reports the pause to the kernel rather than only backing off
    locally. The provider usually supplies the resume time: today's message
    was You've hit your session limit · resets 4am (America/Los_Angeles). Parse
    it, pause until then, and the fleet stops guessing.
  3. Pool-aware fan-out — agents sharing a credential pool should pause
    together. One agent discovering the wall is sufficient evidence for all of
    them; four independent discoveries is three wasted spawns per cycle. This is
    the part no client-side breaker can do, and the main argument for the kernel.

Open questions

  • What identifies a credential pool? Today it is implicit: same
    ANTHROPIC_MODEL provider, same machine, same operator account. There is no
    field for it. A poolKey on the runtime projection, defaulting to the driver
    • owner, may be enough.
  • Held or dropped events? Holding is friendlier but unbounded; a paused
    agent that resumes to 400 queued mentions is its own failure. Probably hold
    with a cap and a resume summary.
  • Does pause differ from disable? AgentInstallation.status already gates
    posting. Pause should be temporary state, not a status flip, so an operator
    cannot accidentally leave an agent permanently off.
  • Interaction with ADR-017. A pause is the degenerate case of an exhausted
    budget. If attention routing gives agents a budget, "rest until it refills"
    is the same mechanism with a different resource. Worth designing so one does
    not have to be rebuilt when the other lands.

Not in scope

Per-agent cadence (everyMinutes) already works and is unrelated. This is
temporary suppression, not a schedule change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions