pi-queue-steer-factory

Visible steering, follow-up, and session-control queues for Pi and Pi Fabric.

Packages

Package details

extension

Install pi-queue-steer-factory from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-queue-steer-factory
Package
pi-queue-steer-factory
Version
0.15.0
Published
Sep 5, 2026
Downloads
2,159/mo · 2,159/wk
Author
monotykamary
License
MIT
Types
extension
Size
1.1 MB
Dependencies
0 dependencies · 3 peers
Pi manifest JSON
{
  "extensions": [
    "index.ts"
  ],
  "image": "https://raw.githubusercontent.com/monotykamary/pi-queue-steer-factory/main/assets/pi-queue-steer-demo.gif"
}

Security note

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

README

pi-queue-steer-factory

CI License: MIT

A visible steering, follow-up, and session-control timeline for Pi, with acknowledged /fabric prewalk barriers.

Queue instructions while the agent works. Every row keeps its Pi delivery timing — blue steering at a turn boundary, yellow follow-up after a run — inside one first-in, first-out execution outline. Follow-ups are root rows that start runs; steering is indented beneath the run it will join.

Move into any row to edit it. The selected row becomes the live Pi editor, with its cursor, wrapping, paste handling, autocomplete and custom-editor behaviour intact.

Demo

Looping demonstration of steering and follow-up queues while Pi continues working

Install

Install the queue and Pi Fabric from npm:

pi install npm:pi-queue-steer-factory
pi install npm:pi-fabric

Pin the current releases when you want reproducible installs:

pi install npm:pi-queue-steer-factory@0.15.0
pi install npm:pi-fabric@0.64.0

The GitHub package is also installable directly:

pi install git:github.com/monotykamary/pi-queue-steer-factory@v0.15.0

Then start a new Pi session or run /reload.

Controls

The extension follows your configured Pi action bindings. These are the default keys on macOS terminals:

Context Key Action
Agent working Enter Append visible steering; it runs at its next safe turn boundary after earlier rows
Agent working Option+Enter Append a visible follow-up; it runs after earlier rows and the active run
Queue visible Option+Up Select the most recently queued row
Editing a row Option+Up Keep the current draft and move to the previous visual row
Editing a row Option+Down Keep the current draft and move to the next visual row; mirrors the configured Option+Up action
Editing a row Type normally Edit directly inside the selected row
Editing a row Option+X Mark the selected row for removal; save deletes it, a second press restores it
Editing a row Option+Right Indent the row into steering for the run above; its timeline position does not change
Editing a row Option+Left Outdent the row into a follow-up that starts the next run; its timeline position does not change
Editing a row Option+T Toggle the same depth as a fallback when a terminal cannot distinguish Option+Arrow
Editing a row Option+P Pause or resume the selected row where it sits; a paused row stops dispatch at its position until resumed
Editing a row Option+Shift+Up / Option+Shift+Down Reorder the selected row within its lane; positions apply immediately and roll back on Escape
Editing a row Enter or Option+Enter Save all row edits, including explicit indent/outdent drafts
Editing a row Escape Cancel the session and roll back all unsaved row edits
Empty composer, follow-up at the timeline head Enter Promote that next follow-up to steering now
Queue paused after an abort Enter Resume from the next timeline row
Queue paused after a run error Enter Resume manually; a recovered run (built-in retry, auto-compact, pi-retry) releases the queue first
Queue restored after resume Enter Send the next queued row; Option+Up edits it first
Agent stopped Option+Enter Queue a message, skill/template, or control command (/compact, /reload, /new, /model, /thinking, /fabric prewalk) visibly and paused
Agent working, queue visible Escape Abort the run and pause the entire delivery plan
Agent working /pause Pause the run once every in-flight tool call finishes; tool work is never killed mid-execution
Any state Option+W Toggle a peer settle gate row (pick a peer or all peers)
Peer gate waiting Escape Cancel the wait and pause the gate row

Option+Left/Right, Option+W, Option+X, Option+T, Option+P and Option+Shift+Up/Down are fixed shortcuts. The other controls use Pi’s configured action bindings, and the next-row key mirrors whatever app.message.dequeue is bound to — rebind that action to ctrl+up and next-row becomes ctrl+down. A dequeue binding with no up twin keeps Option+Down. The physical arrow sequences change depth only while editing queued rows; Option+B/F remain editor word navigation. A terminal that encodes Option+Arrow as those indistinguishable word-navigation bytes can use Option+T instead. Terminals outside macOS may label Option as Alt.

Execution outline

The indentation is deliberately one-way:

• current run
  ↳ steer current work
○ queued run A
  ↳ steer inside A
○ queued run B

A yellow follow-up is a root because it starts a run. Any blue steering rows immediately after it are children because they enter that run at turn boundaries. Leading steering rows sit under an implicit current run (or next run while idle). A follow-up after steering always outdents to start another run; queues are therefore never shown as children of steers. There are exactly two depths, so Option+Right on steering and Option+Left on a follow-up are no-ops. The keys preview real depth changes in place, and saving changes only delivery timing—not FIFO position, text, images, or row identity.

Pausing at a tool boundary

Interrupting a run mid-tool (Escape) kills the executing tool outright. /pause is the graceful alternative: with tool calls in flight it holds fire until every one of them finishes and then stops the run at that tool boundary, so long bash jobs and edits always complete and their results stay in the transcript. With no tool call executing it stops the LLM call immediately, and with the agent idle it simply parks the visible queues. A paused queue stays put until an explicit Enter on the empty composer, and /pause during compaction reports back instead of cutting summarization.

Pausing a single row

The whole-queue pause stops everything; sometimes the agent should keep working until it reaches one specific row. While editing the queue (Option+Up), press Option+P to pause or resume the selected row where it sits. A paused row is a dispatch barrier: earlier rows still send on their normal boundaries, and once the paused row reaches the timeline head delivery stops there — rows behind it never jump ahead, regardless of lane — until you select it again and press Option+P to resume. The pause belongs to the row like its lane does: it survives a save, persists across restart and resume, and an unsaved toggle rolls back with the rest of the editing session on Escape. A drain skips paused rows and leaves them parked.

Peer settle gates

When another Pi Fabric session is running in the same project, queue the edit work and hold it behind a gate until that peer settles. Press Option+W (or queue /fabric await [LABEL] by hand) to add a gate row: with one live peer it targets that peer directly, with several it offers an all-peers default plus per-peer cards (● PQS-1 · gpt-5.4 · running · started 6m ago, with Linear-style project labels that are never reused). Rows queued behind the gate dispatch as soon as every watched peer has settled and stayed quiet briefly; peers that leave the mesh count as settled. Escape cancels an active wait and pauses the gate row; Option+W again removes a queued gate. Requires pi-fabric 0.64.0 or newer.

Delivery semantics

The extension keeps Pi’s 2 delivery classes inside one ordered timeline:

  • steering at the timeline head reaches the current run at Pi’s next safe turn boundary
  • a follow-up at the head waits until the current run finishes
  • later rows never overtake the head because they use the other delivery class
  • yellow follow-ups stay at the root and blue steering rows indent beneath the run they will join
  • reordered rows keep their stable IDs, text drafts and attachments
  • reordering waits while an indent/outdent draft is pending; saving the depth change keeps the row in its existing timeline slot
  • Pi’s one-at-a-time and all settings still apply per lane, but an all batch stops at the next lane switch, command, or paused row

This makes both directions composable. Queueing follow-up A → steer A → follow-up B starts A after the current run, injects the steering into A at its next turn boundary, then starts B only after that run settles. While an agent is working, enter that sequence with Option+Enter, Enter, then Option+Enter; queueing steering before a follow-up keeps the inverse relationship just as strictly.

The extension hands the timeline head back to Pi only when that row's delivery boundary arrives. Rows remain visible and editable before that point, and Pi records delivered rows as normal user messages. Queue ownership is TUI-only; RPC, JSON and print-mode input pass through unchanged.

Run errors and retries

A run that ends in an error (including context overflow) pauses the queue instead of de-queueing the next row into the failed session. Recovery gets the queue first: Pi's built-in retry and overflow auto-compaction, or an external retry loop such as pi-retry that re-prompts once the agent goes idle. The pause lifts automatically at the first healthy assistant tail — or when an overflow compact-and-retry cycle concludes — and the parked rows then flow in order. A threshold compaction triggered merely by context size after a failed run is housekeeping, not recovery, and leaves the queue parked. If nothing recovers the run (no retry installed, retries exhausted, or compact-and-retry itself failed), the rows stay parked until Enter sends the next one. Aborting during recovery keeps the pause; an aborted tail never counts as recovery.

Queueing while stopped

With the agent stopped, Enter keeps Pi's normal immediate send. Option+Enter instead places the submission as a yellow follow-up root, paused — including skill and prompt-template invocations and the supported /compact [instructions], /reload, /new, /model [target], /thinking [level], exact /fabric prewalk, and /fabric await [label] controls. Press Enter on the empty composer to execute the next row, or Option+Up to edit it first.

A plain Enter still runs every command immediately. With Option+Enter, other Pi built-ins, other extension commands, unknown slash input and ! bash keep passing straight through.

Prompt templates and Agent Skills

Queued /do-less this code, /skill:bro and /bro rows stay short and editable, then expand when delivered — while the agent works they queue through steering or follow-up input, and while stopped Option+Enter parks them paused like any message. /bro is shorthand for /skill:bro unless a built-in, prompt or extension already uses that name. Template arguments and images are preserved; unknown slash input remains ordinary text.

Arbitrary commands are intentionally not replayed. The supported command rows have explicit completion signals; an unsupported queued extension command pauses until you edit or remove it.

Command rows

Text-only rows matching /compact [instructions], /reload, /new, /model [target], /thinking [level], exact /fabric prewalk, or /fabric await [label] are command rows. A row with image attachments remains a normal message even if its text matches a command, so attachments are never discarded. Command rows execute the control operation instead of becoming LLM messages:

  • Option+Enter while the agent works queues a command in normal follow-up order; while stopped it parks every command row paused, /compact and /reload included
  • lane timing is uniform: a steered command row executes at the next turn boundary — mid-run, exactly as if typed there — and a queued (follow-up) command row runs when the run settles; rows behind an executing command wait for it
  • /model provider/model resolves an exact available model; bare or non-exact /model opens a filtered picker, and cancellation or authentication failure restores and pauses the row
  • /thinking level sets Pi's thinking level through the clamped public API; bare /thinking opens the level picker, and an unknown level or a cancelled picker restores and pauses the row
  • exact /fabric prewalk waits for Pi Fabric to acknowledge that prewalk is armed before the next row can run; it requires Pi Fabric 0.62.7 or newer
  • /fabric await [label] holds the tail until every watched peer session on the project mesh settles (a quiet window after its last observed run) or leaves the mesh; it requires Pi Fabric 0.64.0 or newer. Controls and watch state show on the row (waiting for PQS-1 (running))
  • /new starts a fresh session and transfers its committed tail to that replacement runtime without adding rows to either transcript; the tail continues automatically, while reopening a persisted queue still starts paused. Pi resolves a fresh session's model from the shared saved default (the last model any session persisted) or the first scoped model, so the handoff pins the outgoing session's model back onto the replacement session before the tail runs — or warns and continues if that model is no longer available
  • /reload runs Pi’s built-in reload; committed trailing rows retain their IDs, lanes, attachments and pause state across the runtime swap
  • a queued /compact runs at settle via Pi’s public compaction API and trailing rows resume when compaction finishes; steered, it fires at the next turn boundary and aborts the in-flight run on purpose — a start failure restores and pauses the command row
  • /reload queued as a follow-up runs at settle, sidestepping Pi's built-in busy wait warning; steered, it fires at the next turn boundary and Pi's own busy handling applies
  • plain Enter on /compact or /new while the agent works parks it as a steer row instead of firing instantly: it runs at the next turn boundary — after the turn's in-flight tool results land — and the extension owns the abort tail; from idle, both still start immediately and a started compaction holds visible rows until it settles
  • ordinary messages submitted during compaction remain in Pi's native queue and can run before extension-owned command rows after compaction finishes
  • stopped Option+Enter parks /compact and /reload paused like every other control row; they run only on an explicit empty-composer Enter
  • unsupported command forms, including /fabric prewalk <task>, are not control rows; queue exact /fabric prewalk and the task as separate rows
  • command rows show a marker and keep the same pause, edit, reorder and snapshot semantics as messages

Factory pipelines

A linear Factory run is just an observed queue of controls followed by work:

/new
/model openai/gpt-5.4
/fabric prewalk
Implement the queued task

Queue each line with Option+Enter, then press Enter on the empty composer. The dispatcher waits for session replacement, model selection and prewalk arming before advancing; later controls wait for Pi's agent_settled idle boundary. A failed or cancelled control remains at the front and pauses the whole tail, so it can be edited or retried without reordering.

Fabric remains the execution plane inside the task: it can launch durable or recursive agents, steer them, and create isolated worktrees. This extension owns only the visible deterministic queue and its observation boundaries; it does not introduce another agent loop.

Draining the queue

/queue-drain empties both lanes into the run as a single combined message. Row texts join in exact global timeline order, expanding prompt templates and skills as they go, with every row's image attachments appended in the same order.

  • during a run, the combined message reaches Pi as one steering message
  • while stopped, the combined message starts a new run directly
  • a mid-turn drain lands inside the in-flight call's context when the turn has not responded yet, or as the next steering turn once it has — either way the transcript records the combined message exactly once
  • command rows are not messages: /compact, /reload, /new, /model, /thinking, and /fabric prewalk stay queued and execute at their lane's dispatch boundary
  • an active row-editing session refuses the drain, so rows are never pulled away mid-draft
  • a synchronous hand-off failure restores every row, in order, and pauses the queue

Editing semantics

  • Option+Up starts at the row you queued most recently
  • Option+Up and Option+Down then move through the visible timeline
  • saving never changes a row’s lane implicitly; Option+Right indents to steering and Option+Left outdents to follow-up
  • depth changes preview in place and commit without changing the row’s global timeline position; Option+T remains a toggle fallback
  • Option+X marks the selected row for removal; save deletes it, and Escape or a second Option+X restores it
  • a selected row becomes the real editor without a nested composer frame
  • one editing session can hold drafts for several rows
  • Escape restores every row from the session snapshot, including removal marks and indent/outdent drafts
  • saving an empty text-only row removes it
  • image-only rows survive text clearing; Option+X removes them
  • an unrelated composer draft is stashed and restored when editing ends

A touched head row is pinned until you save or cancel. In one-at-a-time mode, later rows do not block the head. In all mode, editing any row in the contiguous dispatchable head segment holds that batch at active-run delivery boundaries; rows beyond a lane switch, command, or paused row are not part of it.

Abort and recovery

Aborting a run pauses the entire visible delivery plan. This prevents a follow-up from starting immediately after the abort.

Press Enter on the empty composer to resume; the same keypress sends rows queued while stopped. A synchronous handoff or preflight failure returns the affected batch to the global timeline head.

Committed rows also survive quitting and resuming Pi. On shutdown the extension records the queue in the session file as an invisible custom entry that stays out of the transcript and out of the model context. Committed row saves and accepted deliveries immediately supersede that entry with the remaining queue—or an empty tombstone—so edited-away or already-sent rows cannot return after a later restart. Reopening that session restores the remaining rows paused: nothing sends until you press Enter on the empty composer. A /reload runtime swap still carries committed rows and pause state through a short in-process handoff. Edit drafts stay session-local and never persist; ordinary /new and forks start clean, while queued /new intentionally transfers its committed tail.

Public API limits

Pi’s public sendUserMessage API is fire-and-forget. The extension restores synchronous message-dispatch failures and preflight/expansion failures without reordering; once the call returns, it retires the accepted row and checkpoints the remaining queue. Pi does not expose later asynchronous input rejection. Inferring rejection from queue timing could duplicate a delayed successful handoff, so the extension does not do that.

Queued /model uses Pi's awaited model API. Queued /new runs through an internal extension-command adapter because newSession() is intentionally available only in command contexts. Queued /fabric prewalk uses Pi Fabric's versioned host-local request/ack protocol. /reload remains the one supported control exposed only through the TUI editor's void submit callback, so Pi cannot acknowledge or reject that submit back to the extension.

If an all-mode head segment stays pinned until the agent settles, saving from idle starts the new run with the timeline head, then delivers the remaining rows in that segment at the next native boundary. The public API has no atomic idle-to-native-queue batch operation, so this restart cannot be one native batch. Draining sidesteps that limit by composing its combined message client-side, so one send carries every row.

Resume persistence

Queuing a row does not send it. When Pi shuts down cleanly — /quit, Ctrl+C, Ctrl+D, or a session switch — the extension records the committed queue as a custom session entry (pi-queue-steer:queue), invisible in the transcript and excluded from the model context. Successful deliveries and committed row saves append a superseding snapshot immediately; if they empty the queue, they append a tombstone instead. When the same session is reopened (pi -c, pi -r, pi --session, /resume), only the rows in the newest snapshot come back, in FIFO order with their IDs, lanes, image attachments and command rows intact — and the queue is parked paused. Press Enter on the empty composer to send the next row, or Option+Up to edit it first.

Rows normally belong to the session they were queued in. /fork and an ordinary immediate /new start with an empty queue. A queued /new is the explicit exception: it retires the old session snapshot, transfers its tail in process, and continues that tail in the replacement session. Older snapshots superseded by later ones stay in session files but are never restored, and a session can only be resumed at all if Pi wrote it: sessions without an assistant response are not persisted by Pi, and a hard kill skips the shutdown hook.

Editor composition

pi-queue-steer-factory wraps the active Pi editor. It does not replace Pi’s input model.

For display, it extracts the live editor’s text and cursor from the editor frame. It then places that content inside the selected queue row. Autocomplete remains below the edited text.

The extension composes with custom editors including raw-paste and pi-session-hud.

Extension interop

The queue publishes its state for peer extensions on the shared pi.events bus:

  • Eventqueue-steer:state, emitted on every change to { pending, paused, blocked }. pending counts all rows still held by the queue (both lanes, including paused and edit-held rows), paused means dispatch is paused, blocked means a control row (/compact, /model, /thinking, /new, /reload, /fabric prewalk) is executing.
  • Mirror — the same snapshot lives on globalThis.__tmustierPiQueueSteerState for synchronous reads, immune to extension load order, and survives /reload runtime swaps.

Consumers: pi-ledger ≥ 0.6.0 holds back its no-credit engagement wizard while pending > 0, so a parked backlog no longer triggers the billing prompt, and re-offers it once the backlog drains without starting a run.

Development

npm install
npm run ci
./test/tui-evidence.sh /tmp/pi-queue-tui-evidence
pi -e ./index.ts

The automated suite covers delivery, editing, command rows, resource expansion, recovery, images, editor composition, repeated reloads, real retry ordering, real manual compaction success/failure and real automatic overflow compaction. The tmux harness exercises the same paths through Pi's real TUI, including actual runtime reloads and native post-compaction input.

The Pi package ranges are intentionally unpinned. The full suite and real-TUI harness are verified against the current resolved Pi release; see the validation record for exact commands and evidence.

Security

Pi extensions run with the same system permissions as Pi. Review extension source before installing a third-party package.

Licence

MIT. See LICENSE.

This project draws on Cursor’s queue interaction. It is not affiliated with Cursor or Anysphere.