English · 繁體中文 · 日本語 · Français · Español
A Pi extension that keeps a run alive across provider usage limits, and schedules one-shot follow-up messages when you ask for them.
You do not have to run any command. Auto-resume is on by default
(autoResume.enabled: true), so once the extension is installed it watches
every turn on its own:
- It caches any
429response it sees from the provider. - When a turn ends on a usage-limit error, it classifies the error and resolves the reset time (headers → error body → provider usage API).
- It schedules the continuation message (
continue) forreset + 90sand tells you when that is:Usage limit reached (anthropic) — auto-resuming at 14:05.
When the window reopens the message is sent and the agent picks up where it
stopped. The /kg command exists for the times you want to schedule something
yourself — it is never required for the automatic path.
pi install npm:pi-keep-goingPi prompts you to run pi update --extensions only when a new version is
published: for an npm source it compares the installed package.json version
against the registry. Leave the spec unversioned — npm:pi-keep-going@1.0.0
counts as pinned, and Pi skips update checks for pinned sources entirely.
To hack on it, install a clone by path instead. A local-path install is
referenced from ~/.pi/agent/settings.json, not copied, so your edits take
effect on the next Pi start:
git clone https://github.com/ohlulu/pi-keep-going
pi install ./pi-keep-going| Command | Effect |
|---|---|
/kg 40m keep going |
Send keep going after 40 minutes. |
/kg 2h30m |
Send the default message (keep going) after 2h30m. |
/kg 90s ship it |
Duration is largest-unit-first: d h m s, each unit at most once. |
/kg auto [message] |
Query the current provider's usage API and schedule at the reset time + buffer. |
/kg list |
List pending scheduled messages. |
/kg cancel |
Cancel a scheduled message (prompts when several are pending). |
/kg help |
Show usage. A bare /kg does the same. |
Scheduled jobs are persisted per branch, so they survive /tree, /fork, and
reload. Timers use an absolute fire timestamp checked on a 30s tick, so a job
still fires correctly after the machine sleeps. Nothing enters the LLM context
except the final message that is actually sent.
While anything is pending, a countdown sits above the editor with a small animated companion — a dog or a cat, chosen at random each time a countdown starts:
⏱ keep going in 7m 58s (14:23)
The art is original pixel art, drawn as truecolor half-blocks so one terminal cell carries two pixels. Colour degrades on its own: 24-bit where the terminal advertises it, 256 colours otherwise, and flat ASCII art when there is no colour at all. Frames advance every 900ms, and the timer only exists while a job is pending — an idle session is left completely alone.
When a turn ends on a usage-limit error, the extension:
- Classifies the error per provider (from the assistant error message plus any
cached
429response headers). - Resolves the reset time (headers → embedded time → provider usage API). The usage-API step is load-bearing for Anthropic: the SDK throws on 429 before pi can observe the response, so the unified-reset headers are never cached and the error body carries no reset time.
- Schedules a continuation message at
reset + bufferSeconds, guarded by the settings below.
Auto-resume is skipped silently inside a 5-minute window after a previous
resume (loop protection), and turns into a notification (rather than a schedule)
when the per-session cap is reached or the reset is further away than
maxWaitHours.
| Provider | Detection | auto usage API |
|---|---|---|
OpenAI Codex (openai-codex) |
hit your ChatGPT usage limit, usage_limit_reached, 429 |
GET /backend-api/wham/usage → rate_limit.primary_window.reset_at |
Anthropic (anthropic) |
rate-limit errors, 429, unified-reset headers | GET /api/oauth/usage → five_hour.resets_at (needs an OAuth login, not an API key) |
Google Gemini (google-gemini-cli, google) |
RESOURCE_EXHAUSTED, quota errors |
POST v1internal:retrieveUserQuota → earliest buckets[].resetTime (needs the CLI login's project id) |
Tokens are resolved through ctx.modelRegistry.getApiKeyForProvider() (Pi
handles OAuth refresh); the extension never reads auth.json or refreshes tokens
itself. If a usage API is unreachable or unsupported, auto degrades to a
notification suggesting a manual /kg <duration>.
Everything below already has a working default — you only need a config file to change behavior, e.g. to turn auto-resume off or send a different message.
Global config lives at <pi agent dir>/keep-going.json. A project-local
override at <cwd>/<pi config dir>/keep-going.json is applied only when the
project is trusted. Later layers win; unknown or invalid fields are ignored.
- Generation guard — every session gets an
AbortController+ generation id.autousage-API fetches run with a 10s timeout composed with the session signal, and the result is discarded if the session was replaced while the request was in flight. - Single-firer lease — if two Pi processes attach to the same session, an advisory lock elects one firer; the other runs read-only so a job is sent exactly once.
npm install
npm run typecheck
npm test
pi -e ./src/index.ts # load locally@earendil-works/pi-coding-agent is a peer dependency — it is provided by
the Pi runtime that loads the extension, so it must not be bundled. It is also a
dev dependency here so tsc and vitest resolve it locally.

{ "defaultMessage": "keep going", "autoResume": { "enabled": true, // master switch for usage-limit auto-resume "message": "continue", // message sent when a window reopens "bufferSeconds": 90, // wait past the reset before sending "maxPerSession": 5, // cap auto-resumes per session "maxWaitHours": 24 // beyond this, notify instead of scheduling } }