原始内容
pi-hodor
pi-hodor is a pi extension that automatically sends a follow-up retry message when an assistant response stops in a retryable way.
It is useful when model output is interrupted by provider-side failures such as ECONNRESET, ETIMEDOUT, premature stream closure, or partial JSON responses. It can also continue when the assistant stops because it hit a length limit and enough context remains, when it ends with a normal stop but only emitted thinking content and never produced user-visible text or tool calls, when it ends with a silent stop immediately after a user message without producing any visible follow-up, when it ends with a silent stop immediately after a tool result without producing any visible follow-up, or when an automatic retry message such as continue is followed by another silent stop. Instead of stopping and waiting for manual intervention, the extension detects these cases and sends a configurable retry message such as continue.
Features
- Watches assistant messages that end with retryable stop reasons
- Automatically continues on
stopReason === "length"when enough context remains - Automatically continues on
stopReason === "stop"when the assistant emitted only thinking content - Automatically continues on silent
stopReason === "stop"immediately after a user message when the assistant emitted no visible output - Automatically continues on silent
stopReason === "stop"immediately after a tool result when the assistant emitted no visible output - Automatically continues again when its own retry message is followed by another silent
stopReason === "stop" - Matches
stopReason === "error"text against configurable substring patterns - Defers configured errors that should be left to pi's built-in auto-retry, such as
WebSocket error - Automatically sends a configurable retry message when an error matches
errorPatternsbut notdeferredErrorPatterns - Prevents runaway loops with a configurable retry limit
- Optionally shows UI notifications when an auto-retry happens
- Supports project-level and global config overrides without modifying the packaged files
Installation
Install from npm:
pi install npm:pi-hodor
Or from git:
pi install git:github.com/vurihuang/pi-hodor
Restart pi after installation so the extension is loaded.
Load it for a single run
pi -e npm:pi-hodor
Install from a local path
pi install /absolute/path/to/pi-hodor
Load from a local path for one session
pi -e /absolute/path/to/pi-hodor
Verify installation
After restarting pi, the extension is active automatically.
You can confirm it is loaded by triggering a transient stream error during normal use, or by checking that the extension has been installed from npm and is available in your pi package list.
Usage
Once the extension is loaded, there is nothing else to trigger manually.
When pi receives an assistant message that matches one of these conditions, pi-hodor automatically sends the configured retry message:
- it ends with
stopReason === "error", its text matcheserrorPatterns, and it does not matchdeferredErrorPatterns - it ends with
stopReason === "length",autoContinueOnLengthis enabled, and the remaining context is above the configured length-continue safety threshold - it ends with
stopReason === "stop", emitted thinking content, and emitted neither text nor tool calls, andautoContinueOnThinkingOnlyStopis enabled - it ends with
stopReason === "stop", emitted no visible output, the previous message was a user message, andautoContinueOnSilentStopAfterToolis enabled - it ends with
stopReason === "stop", emitted no visible output, the previous message was atoolResult, andautoContinueOnSilentStopAfterToolis enabled - it ends with
stopReason === "stop", emitted no visible output, the previous message was the extension's own automatic retry message, andautoContinueOnSilentStopAfterToolis enabled
The default retry message is:
"continue"
Setup command
If you want a reusable global config, run:
/pi-hodor:setup
What this command does:
- ensures the bundled default config exists inside the package
- creates the global config directory if needed
- copies the bundled default config to:
~/.pi/agent/extensions/pi-hodor/config.json - does not overwrite the file if it already exists
So /pi-hodor:setup is a one-time bootstrap command for creating your editable global config file.
If the global config already exists, the command shows a warning and leaves your existing file unchanged.
Configuration
Configuration is resolved in this order:
./.pi-hodor.json./.pi/pi-hodor.json~/.pi/agent/extensions/pi-hodor/config.json- the bundled
config.jsoninside this package
That means:
- project config overrides global config
- global config overrides the packaged defaults
- packaged defaults are only used when no override file exists
Use project config when you want repo-specific behavior. Use the global config when you want the same defaults across all projects.
Path details
1. Project config in repo root
./.pi-hodor.json
Best for a single repository.
2. Project config in .pi
./.pi/pi-hodor.json
Also project-scoped, useful if you prefer keeping pi-related files together.
3. Global config
~/.pi/agent/extensions/pi-hodor/config.json
Best for personal defaults shared across projects.
You can generate this file with /pi-hodor:setup.
4. Bundled fallback config
The package ships with its own config.json, which acts as the final fallback when no project or global override exists.
Example flow
- You install
pi-hodor - You run
/pi-hodor:setup - The extension creates
~/.pi/agent/extensions/pi-hodor/config.json - You edit that file to define your global defaults
- In one specific repo, you add
./.pi-hodor.json - That repo now uses its local config instead of your global one
Example config
{
"enabled": true,
"retryMessage": "continue",
"maxConsecutiveAutoRetries": 99,
"notifyOnAutoContinue": true,
"autoContinueOnLength": true,
"minRemainingTokensForLengthAutoContinue": 16384,
"autoContinueOnThinkingOnlyStop": true,
"autoContinueOnSilentStopAfterTool": true,
"deferredErrorPatterns": [
"WebSocket error",
"fetch failed",
"socket hang up"
],
"errorPatterns": [
"error decoding response body",
"stream disconnected before completion",
"ECONNRESET"
]
}
Config fields
| Field | Type | Description |
|---|---|---|
enabled |
boolean |
Enables or disables the extension logic. |
retryMessage |
string |
The exact user message sent back to pi after a matched error. |
maxConsecutiveAutoRetries |
number |
Maximum automatic retries before the extension stops retrying. |
notifyOnAutoContinue |
boolean |
Shows a UI notification when an automatic retry happens or when the retry limit is reached. |
autoContinueOnLength |
boolean |
Automatically retries when an assistant message ends with stopReason === "length". |
minRemainingTokensForLengthAutoContinue |
number |
Defers length auto-continue when known remaining context tokens are at or below this value, so pi can compact first. Set to 0 to disable this guard. |
autoContinueOnThinkingOnlyStop |
boolean |
Automatically retries when an assistant message ends with stopReason === "stop" after emitting only thinking content and no text or tool calls. |
autoContinueOnSilentStopAfterTool |
boolean |
Automatically retries when an assistant message ends with stopReason === "stop" after a user message, after a toolResult, or immediately after the extension's own retry message, and emits no visible output. |
deferredErrorPatterns |
string[] |
Case-insensitive substrings for errors hodor should skip because pi or another layer handles them. Set to [] to disable error deferral. |
errorPatterns |
string[] |
Case-insensitive substrings used to detect transient failures for stopReason === "error". |
Development
Install dependencies:
npm install
Run the type check:
npm run check
Preview the npm package contents:
npm run pack:check
Package structure
.
├── auto-continue.ts
├── config.json
├── index.ts
├── LICENSE
├── package.json
├── README.md
└── tsconfig.json
Updating
Reinstall the package from npm:
pi install npm:pi-hodor
Or update from git:
pi install git:github.com/vurihuang/pi-hodor
Restart pi after updating.
Install as a pi package
This project is already structured as a pi package via the pi field in package.json:
{
"pi": {
"extensions": ["./index.ts"]
}
}
That means pi can install it from a local path, npm, or git using the standard pi package flow.