Skip to content

v1.2.0

Choose a tag to compare

@edxeth edxeth released this 26 Jun 19:14

Install:

pi install git:github.com/edxeth/pi-ralph-loop@v1.2.0

Or latest:

pi install git:github.com/edxeth/pi-ralph-loop

✨ Features

New <promise>WAIT</promise> for iterations parked on async work

Refs: 36a56fd

Ralph gains a fourth control promise: WAIT. Emit <promise>WAIT</promise> when an iteration is intentionally waiting on an async result — a helper/subagent return, a background command, a review, a process alert, or a future tool result. Unlike NEXT (which starts a fresh session), WAIT keeps the current iteration in the same session and parks it.

WAIT arms a bounded 30-minute timeout. If no async result arrives before it fires, Ralph sends a structured re-check prompt asking the agent to re-check the iteration and choose WAIT, NEXT, or COMPLETE. The timeout nudge is token- and state-aware, so a stale prompt can't leak into a later turn or iteration. Resume adapts to WAIT too: from the same owning session, resuming after a WAIT keeps the iteration parked instead of advancing.

The control-tag table in the README now documents WAIT alongside NEXT, COMPLETE, and STOP. Missing-promise turns now receive an explicit control-tag prompt instead of a bare continue. STOP still works if the agent emits it, but Ralph no longer offers STOP in its default correction prompts or the ralph-plan-writer skill. The context-window limit reminders now reference WAIT, NEXT, and COMPLETE.

🐛 Bug Fixes

Stale provider-error banner no longer lingers over a recovering run

Refs: 54541e6

The "Provider error at Ralph iteration N; waiting for Pi's retry handling" banner was an aboveEditor widget with no auto-clear, and finalizeLoop only cleared the status line, not the widget — so it sat over a healthy recovering run until the next loop action or a Pi restart.

Ralph now clears problem notices (warnings and errors) at the moments a stale warning becomes inaccurate: after multi-turn provider recovery (supersedeProviderWait on turn_end), after single-turn recovery (agent_end), and when the next fresh iteration starts. Transient info confirmations ("Ralph loop started", "Starting iteration N") are left alone since they already auto-clear, and terminal notices stay because the loop is done.

Block configured human-input tools during unattended Ralph runs

Refs: 8812d3c

Some third-party tools open custom human-input UIs and then wait forever for a person to answer. Ralph can't reliably recover after such a tool has already started, because Pi exposes no generic "this tool is waiting for a human" signal and some custom UIs ignore abort.

Ralph now blocks a user-owned list of tool names while a loop is running:

export RALPH_BLOCKED_TOOLS=tool_name_1,tool_name_2
pi

The guard only fires while .ralph/loop.md says a loop is running in the current workspace; normal Pi sessions outside a running loop are unaffected. Ralph never hard-codes any tool name — you decide exactly which tools to block, matching by exact name only.

Plan-writer skill now designs Ralph loops for fully unattended runs

Refs: 8812d3c

The ralph-plan-writer skill now treats every Ralph session as unattended. Generated .ralph/prompt.md instructions tell the runtime agent never to ask the user, request approval, wait for human input, or use tools whose purpose is to ask the user. Ordinary ambiguity is now implementation work — make a conservative, reversible assumption, record it in .ralph/progress.md, and keep moving — while hard blockers (missing credentials, external accounts, paid services, hardware, admin permissions, destructive approval, or an unavailable verification dependency) are the only reason to leave an item's passes at false.

When an item depends on an unavailable external service, the skill directs the agent to look for a safe local substitute (a mock, fixture, fake service, local no-network adapter, or narrower item-specific check) before giving up, without ever weakening runtime_contract.verification_gates. Substitutes must be documented, and passes is set to true only if the substitute still satisfies the item's steps honestly. If no item can proceed without human input, the loop records each hard blocker once, leaves all passes values unchanged, and ends without emitting NEXT or COMPLETE rather than fabricating a pass. At planning time, if every acceptance path depends on unavailable external dependencies with no safe substitute, the skill refuses to write the bundle.

A helper process exiting no longer cancels a running loop

Refs: fafaf84

A running Ralph loop was falsely finalized as user_cancelled whenever any other Pi process that loaded the extension in the same workspace exited normally. The real-world trigger was a background helper (pi -p) that loads all extensions, including pi-ralph-loop: its print-mode dispose emitted session_shutdown with reason quit from a pid that wasn't the loop's owner, and the shutdown handler treated that as a cancel.

The shutdown handler is now guarded by the owner_pid identity recorded in .ralph/loop.md: the event is ignored unless the shutting-down process is actually the owner. The genuine cancel path is preserved — the owner's own quit still marks cancel_requested, and a quit during a committed handoff still finalizes as interrupted. A non-owner quitting mid-handoff is now a no-op rather than killing the owner's loop. The legacy owner_pid: null path keeps its prior behavior.

Ralph now actively recovers from abnormal agent endings instead of stopping

Refs: 57115e4

Previously, when a provider error or a malformed terminal stop survived Pi's retry window, Ralph finalized the loop as a fatal error immediately. Ralph now runs a multi-stage recovery before giving up.

After Pi's retry handling, Ralph shows a guarded countdown, then sends up to five same-iteration recovery nudges (one minute apart). If the model still cannot produce a normal turn, Ralph opens one fresh fallback session for that same iteration — without re-snapshotting the bundle, so item progress is preserved — and only stops as a resumable error if that fallback also exhausts recovery. The one-fallback-per-iteration cap is persisted in loop state, so recovery cannot loop forever opening fresh sessions.

User input cancels any pending recovery countdown, so a person stepping in always wins over automated recovery. This commit also fixes off-by-one budgets in the missing-promise nudge and bundle-rejection correction paths (the bundle-rejection counter now compares correctly and shows the right N/MAX denominator).

Stale cancellation notice is cleared on resume

Refs: 501e5ae

When an agent turn was interrupted, Ralph recorded a user-cancelled stop and showed an info notice. Resuming the same session correctly reset the durable loop state, but the "Ralph loop cancelled by user" banner could stay above the editor because the notice clearer only dismissed warning and error widgets. Cancellation notices now auto-clear, and an explicit same-session resume clears stale info notices too, so the UI can't keep showing a false cancellation banner while the loop runs again.

📝 Documentation

Add Acknowledgements section to the README

Refs: 7895881

The README now has a dedicated Acknowledgements section near the top, thanking @FasalZein and @isthatyousaf for their contributions, ideas, and for providing access to frontier models (GPT and Claude) that made experimenting with and building the extension possible. It's kept separate from the bottom Credits section, which covers the foundational Ralph technique and research.