# pi-retry

[English](./README.md) | [中文](./README.zh-CN.md)

**Retry hints for [pi](https://pi.dev) provider errors and stalled streams.**

npm package: [`@geebos/pi-retry`](https://www.npmjs.com/package/@geebos/pi-retry)

This is a fork of `pi-retry` from
[narumiruna/pi-extensions](https://github.com/narumiruna/pi-extensions)
(`deprecated/pi-retry`). The upstream package was deprecated because current Pi releases
ship built-in provider retry, Codex websocket renewal, and configurable timeouts — but the
fork keeps the still-useful behaviors (error classification and the stall watchdog) and adds
**configurable retry keyword patterns** so you can extend what counts as retryable without
editing code.

## Features

- Detects assistant messages that end with `stopReason: "error"`.
- Matches built-in retryable provider errors:
  - `Unknown error (no error details in response)`
  - Codex `websocket_connection_limit_reached` (60-minute websocket limit)
  - Codex backend failures that explicitly include `You can retry your request`
- **Custom retry keyword patterns** — add your own RegExp sources via `/plugin:retry`;
  matches are treated exactly like the built-in ones.
- Appends Pi's retryable-provider-error hint so Pi's built-in retry path continues the turn.
- Watches provider requests and assistant stream events for stalls while Pi retries are enabled;
  aborts and rewrites watchdog-triggered aborts as retryable provider errors.
- Shows `receiving` / `retrying` in the statusline.
- Supports `--retry-stall-timeout-ms <ms>` and `PI_RETRY_STALL_TIMEOUT_MS=<ms>`.

## Install

```bash
pi install npm:@geebos/pi-retry
```

Try without installing permanently:

```bash
pi -e npm:@geebos/pi-retry
```

Try this package locally from the repository root:

```bash
pi -e .
```

Pi's agent-level retry policy must be enabled (the default):

```json
{
  "retry": {
    "enabled": true
  }
}
```

`pi-retry` warns when this policy is disabled, but it never changes the setting automatically.

## How it works

When an assistant message ends with `stopReason: "error"`, the extension matches the error
message against the built-in patterns first, then against your configured custom patterns.
On a match it appends Pi's retryable-provider-error hint, so Pi's built-in retry path can
continue the turn.

Pi owns retry attempts, the retry budget, and exponential backoff. `pi-retry` only adds
classifications and stall detection; it does not implement a separate retry loop. The extension
reads Pi's global and trusted-project settings at session start and before provider requests.

When Pi's retry policy is enabled, a stall watchdog starts after each provider request. Provider
responses and assistant stream events refresh the `receiving` statusline item. If no provider
response or assistant stream event arrives for 90s, the extension briefly shows `retrying`,
calls `ctx.abort()`, and rewrites the abort as a retryable provider error.

Configure the watchdog with:

```bash
pi -e npm:@geebos/pi-retry --retry-stall-timeout-ms 10000
PI_RETRY_STALL_TIMEOUT_MS=10000 pi -e npm:@geebos/pi-retry
```

Use `0`, `off`, or `false` to disable the watchdog.

## Configuration: `/plugin:retry`

Add, list, and remove custom retry keyword patterns (JavaScript RegExp sources). A pattern
matches case-insensitively against the error message, and a match is treated like a built-in
retryable error.

```text
/plugin:retry                       List configured patterns
/plugin:retry add <pattern>         Add a pattern, e.g. /plugin:retry add rate.?limit
/plugin:retry remove <pattern|n>    Remove a pattern by text or list index
/plugin:retry clear                 Remove all configured patterns
```

Patterns are validated when added (they must compile as a RegExp source) and persisted to
`~/.pi/agent/extensions/pi-retry/config.json`. The file is read on every error match, so changes
apply immediately.

```json
{
  "patterns": ["rate.?limit", "upstream.*timed out"]
}
```

## Use cases

- Reduce manual restarts after transient provider failures.
- Recover from explicitly retryable Codex backend failures or websocket connection limits.
- Add your own retry rules for errors only your providers emit.
- Improve reliability during long Pi coding agent sessions.

## Package layout

```txt
pi-retry/
├── .github/workflows/
│   └── publish-npm.yml
├── src/
│   ├── index.ts
│   ├── command.ts
│   ├── config.ts
│   └── retry.ts
├── test/
│   ├── support.ts
│   ├── retry.test.ts
│   ├── config.test.ts
│   └── command.test.ts
├── README.md
├── README.zh-CN.md
├── LICENSE
├── tsconfig.json
└── package.json
```

## Publishing

Pushing a version tag runs the npm publish workflow:

```bash
git tag v0.0.1
git push origin v0.0.1
```

Manual re-publish of an existing tag: Actions → **Publish to npm** → **Run workflow** → enter `v0.0.1`.

### npm Trusted Publishing (no token)

The workflow uses [Trusted Publishing](https://docs.npmjs.com/trusted-publishers) (OIDC). Do
**not** set `NPM_TOKEN`.

Configure once on npmjs.com → **@geebos/pi-retry** → **Settings** → **Trusted Publisher**:

| Field | Value |
| --- | --- |
| Provider | GitHub Actions |
| Organization or user | `geebos` |
| Repository | `pi-retry` |
| Workflow filename | `publish-npm.yml` |
| Allowed actions | `npm publish` |

Requirements: Node 24 / npm ≥ 11.5.1 (set in the workflow), `permissions.id-token: write`.

## Credits

Forked from [`narumiruna/pi-extensions`](https://github.com/narumiruna/pi-extensions)
(`deprecated/pi-retry`), extended with configurable retry keyword patterns.

## License

[MIT](./LICENSE)
