pi-pattern-retry

Pi extension: keep agent sessions alive across provider rate-limits, quota exhaustion, and transient auth failures by re-injecting continuation messages on an escalating retry schedule.

Packages

Package details

extension

Install pi-pattern-retry from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-pattern-retry
Package
pi-pattern-retry
Version
0.1.0
Published
May 20, 2026
Downloads
not available
Author
buihongduc132
License
MIT
Types
extension
Size
60.1 KB
Dependencies
0 dependencies · 1 peer
Pi manifest JSON
{
  "extensions": [
    "./index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-pattern-retry

npm version License: MIT

Keep your Pi Agent session alive across provider rate-limits, quota exhaustion, and transient auth failures. pi-pattern-retry watches provider responses and agent errors, matches them against configurable patterns (regex / substring), and re-injects a continuation message at escalating intervals — 30s → 1m → 5m → 15m → 30m → 1h → 4h → 5h → 12h → 24h by default — so the agent picks up where it left off.

Designed alongside todo-enforcer: same retry-scheduler idea, separate package, ships independently.

Install

pi install npm:pi-pattern-retry

Then restart your Pi session. The plugin ships with sensible defaults — no config file is required to get started.

What it does

Two trigger sources feed one matcher:

Event What it inspects
after_provider_response HTTP status — catches 401 / 403 / 429 / 5xx
agent_end last assistant message's errorMessage — catches transport text

A match schedules one timer per (sessionId, patternName). When the timer fires:

  1. If the conversation has progressed (branch grew, excluding our own injections), reset attempt counter and stop.
  2. Otherwise inject pi.sendUserMessage(<your template>) so the agent retries.
  3. Advance to the next delay in the schedule. When the schedule is exhausted, the pattern stays inert until /pattern-retry-reset.

Duplicate triggers (e.g. both events firing for the same failure) are debounced — the already-live timer wins.

Default patterns

Shipped with three patterns pre-seeded. Override or replace via config file.

Name Trigger Schedule (seconds)
llm-429-quota HTTP 429 30, 60, 300, 900, 1800, 3600, 14400, 18000, 43200, 86400 (10 steps)
llm-401-403-auth HTTP 401 / 403 60, 300, 1800 (3 steps)
litellm-weekly-limit errorMessage contains "Weekly/Monthly Limit Exhausted" 3600, 14400, 43200, 86400

Configuration

Global file: ~/.pattern-retry.json. Project override: <cwd>/.pattern-retry.json. Project keys override global; arrays replace, they do not merge.

{
  "enabled": true,
  "patterns": [
    {
      "name": "llm-429-quota",
      "match": { "source": "providerStatus", "kind": "regex", "value": "^429$" },
      "scheduleSec": [30, 60, 300, 900, 1800, 3600, 14400, 18000, 43200, 86400],
      "message": "Provider returned {{status}} (attempt {{attempt}}/{{maxAttempts}}). Retrying after {{lastDelaySec}}s.",
      "stopOnProgress": true,
      "notify": ["webhook", "dashboard"]
    },
    {
      "name": "litellm-weekly-limit",
      "match": {
        "source": "errorMessage",
        "kind": "contains",
        "value": "Weekly/Monthly Limit Exhausted"
      },
      "scheduleSec": [3600, 14400, 43200, 86400],
      "message": "Hit weekly LLM quota. Resuming attempt {{attempt}}."
    }
  ],
  "notifyTargets": {
    "webhook":   { "enabled": false, "url": "", "headers": {}, "timeoutMs": 10000 },
    "dashboard": { "enabled": false, "endpoint": "" }
  }
}

Match semantics

  • kind: "contains" — case-sensitive substring match.
  • kind: "regex" — compiled with new RegExp(value); no implicit flags. For case-insensitive, encode flags in the pattern itself.
  • source: "providerStatus" — matched against the decimal status string (e.g. "429").
  • source: "errorMessage" — matched against the assistant message's errorMessage string. If absent/empty, the pattern does not match.

A malformed regex degrades to a logged warning — other patterns continue to work.

Template variables

Variable Value
{{status}} HTTP status from providerStatus, or 0
{{statusText}} Human label (e.g. "Too Many Requests")
{{reason}} Match-source snippet
{{patternName}} The matched pattern's name
{{attempt}} 1-indexed current attempt
{{maxAttempts}} scheduleSec.length
{{lastDelaySec}} Delay that elapsed before this fire
{{nextDelaySec}} Next delay if any, else 0

Missing variables interpolate to empty string.

Commands

  • /pattern-retry-status — show each pattern's attempt/max, exhausted, and scheduled flags.
  • /pattern-retry-reset [patternName|all] — cancel timer(s) and reset attempt counter.
  • /pattern-retry-toggle — flip the global enabled flag.
  • /pattern-retry-log [N] — tail the last N lines (default 50, max 500) of the central log file straight into the session.

Troubleshooting / Logs

Every meaningful decision is appended to a single central log file:

~/.pi/logs/extensions/pattern-retry.log

This is the shared convention across all pi extensions — written via the rotated, size-capped (25 MB, 80% trim) createPluginLogger. Each line is:

<iso-timestamp> [pattern-retry] <LEVEL> <event> <json-details>

Events you can grep for:

Event Means
session-start Session bound to pattern-retry; sessionId + cwd recorded
config-loaded / config-load failed Result of merging ~/.pattern-retry.json + project override
trigger:matched A provider/error trigger arrived; lists matched pattern names
trigger:skipped-disabled / trigger:skipped-no-session Trigger dropped — explains why
schedule:armed Timer set; attempt, delaySec, trigger metadata
schedule:skip-idempotent Duplicate trigger dropped because a live timer already exists
schedule:skip-exhausted / schedule:exhausted All attempts used up
timer:elapsed Scheduled delay elapsed; about to fire
fire:injected Continuation message sent via pi.sendUserMessage
fire:reset-on-progress Agent advanced before timer fired → attempt counter reset, no send
cancel:pattern / cancel:session /pattern-retry-reset or session shutdown
command:toggle / command:reset / command:reset-all Audit trail for user commands
notify:<event>:<target> A configured notify target fired (V1: log-only)

When something looks wrong:

tail -f ~/.pi/logs/extensions/pattern-retry.log
# or from inside a session:
/pattern-retry-log 200

The file is the source of truth — every fire, skip, reset, and exhaustion is there with the sessionId and pattern name attached, so you can reconstruct exactly what happened and why.

Notify hooks (V1: no-op)

Each pattern may carry notify?: ("webhook" | "dashboard")[]. V1 logs one line per enabled target — real HTTP/WebSocket senders ship in V2 (see flow/plans/pattern-retry-future.md upstream).

The notifyHooks() signature is the public contract — V2 swaps the implementation without changing callers.

How it differs from todo-enforcer

Plugin Trigger Use case
todo-enforcer Idle agent with pending todos Nudge the agent to keep working on its list
pi-pattern-retry Provider error / quota signal Resume after an external rate-limit or outage

They share the retry-scheduler pattern (copy-adapted), not code — install one without the other.

License

MIT