Diversio Engineering
On this page
PI PACKAGE EXTENSION

Pi Monitor

Approved session-owned background scripts, bounded stdout events delivered mid-run or when idle, list/stop controls, and process-group cleanup. No model polling loop.

Overview

Approved session-owned background scripts, bounded stdout events delivered mid-run or when idle, list/stop controls, and process-group cleanup. No model polling loop.

Package docs also remain available on the package summary page at /docs/pi-monitor, but this page focuses on the Pi-native extension surface: commands, tools, environment variables, packaged skills, and extension files.

Extension Files

Primary extension path: pi-packages/pi-monitor/extensions/pi-monitor

  • index.ts

Installation

bash
pi install git:github.com/DiversioTeam/agent-skills-marketplace

Local test or verification snippet:

bash
cd pi-packages/pi-monitor
npm test
PI_TEST_BINARY="$(command -v pi)" npm run test:pi

Use

Ask Pi:

> Watch server.log for new errors for five minutes. Start at the end of the > existing file, report new errors, and keep working on the current task.

The model can call:

This is a monitor tool call. Approve the exact command, working directory, and timeout in Pi's confirmation dialog. The tool returns a monitor ID after a 0.5-second startup check: a command that fails within that window (bad syntax, a missing file) returns an error with its exit code and stderr instead of running. Output and terminal notices arrive automatically as one custom monitor-event message holding every queued event (details.events):

before its next model call. A model that keeps running tools still sees the event within the same run.

Events are not human replies. A long foreground tool call (for example a ten-minute Bash loop) still delays delivery until it returns.

Stopping a monitor discards its queued, undelivered events. monitor_stop returns the final state and sends no terminal event, since the model asked for the stop. /monitors stop comes from the user, so the model still receives the terminal event. Only the most recent 20 tasks are retained for listing, and listed commands are shortened to 120 characters. The tool ID is extension-owned, not a Pi core background-task ID; use the tools above, not another package's task-stop operation.

  • While the model is working, right after its current tool calls finish, before its next model call. A model that keeps running tools still sees the event within the same run.
  • When Pi is idle, as a follow-up that starts a new turn.
json
{
  "command": "tail -n 0 -F server.log | grep --line-buffered -E 'ERROR|Traceback|FAILED|OOM'",
  "description": "new failures in server.log",
  "timeout_ms": 300000
}

Approval and unattended runs

Commands are unsandboxed and inherit Pi's environment and OS permissions. They can read credentials, access the network, and change files. Approval does not make an arbitrary script safe. Other extensions may block the tool call, but direct child creation does not automatically use their Bash permission or sandbox implementation. No repository monitor file is automatically executed.

Every start requires confirmation unless the operator explicitly launches:

The flag grants this invocation's model permission to start any monitor command without individual confirmation. Use only for a trusted task, script, and model; it is not saved. Noninteractive runs without approval fail closed. RPC clients can instead approve individual extension_ui_request confirmations. Cancelling a pending tool also cancels its approval dialog; it cannot start later. An unanswered dialog expires after 60 seconds with a distinct "no approval answer" error, so the model can tell the user is away rather than that they declined. A command containing --monitor-allow-commands is rejected before any dialog: the flag is only for launching Pi, and a tool call cannot grant it.

Output is labeled untrusted external data. A monitor event does not authorize new actions. Avoid emitting secrets: output goes to the model and session transcript just like other tool output.

bash
pi --no-extensions -e ./pi-packages/pi-monitor --monitor-allow-commands

Script rules

awk with fflush(), python -u).

in the terminal event. Redirect 2>&1 before a filter if those failures should also generate live events.

interval (usually 30 seconds or longer), not a tight retry loop.

for a one-shot wait: even grep -m 1 may leave the producer stuck.

working directory. Do not depend on interactive shell aliases or input.

  • Emit changes you would act on, not raw logs or repeated unchanged status.
  • End each event with a newline. Flush every pipe stage (grep --line-buffered, awk with fflush(), python -u).
  • Only stdout generates output events. The last 2,000 stderr characters appear in the terminal event. Redirect 2>&1 before a filter if those failures should also generate live events.
  • Cover success and failure/cancellation/timeout states. Silence is not success.
  • Handle transient network failures explicitly. For remote APIs, use a sensible interval (usually 30 seconds or longer), not a tight retry loop.
  • For one completion, use a finite script that exits. Avoid an endless tail -f for a one-shot wait: even grep -m 1 may leave the producer stuck.
  • Bash runs with pipefail, no profile files, and closed stdin, in the session's working directory. Do not depend on interactive shell aliases or input.

Bounds and cleanup

or infinite watches in this version; re-arm only when still wanted.

retains 3,000, plus truncation markers. Blank lines are omitted. Large bursts are deliberately lossy; this is notifications, not full log archival.

safely. An unterminated final line is flushed on exit.

Excess batches are suppressed and counted; sustained flooding can stop the source after approximately 30 seconds. Each task stops after 100 output events.

drops an old output event first (otherwise the oldest event) and reports queueSuppressed on the next delivery. This queue, unlike native follow-ups, can be discarded on branch navigation without clearing human messages. Busy delivery steers from turn_end, which Pi drains before the next model call, so a delivered event never waits in Pi's own queue across an abort or a branch change. A cancellable 100 ms idle check also covers delayed lifecycle handlers; it does not call the model.

and stderr tail. Failure and timeout are not reported as success.

if needed. A shell exit also removes leftover members of its process group. Signal errors are reported as cleanup_failed, never thrown from a background callback. If close cannot be confirmed within two seconds of stopping, the task reports its process-group ID and cleanup error instead of hanging Pi. Shutdown-only failures go to stderr; inspect the reported group manually.

stop owned processes and discard pending events. Compaction displays a warning: re-arm any watch you still need afterward. Tasks also stop if another extension subsequently cancels navigation or compaction. Nothing restarts on resume.

Limits: A deliberately daemonized descendant that creates a new process group can escape group cleanup. Abrupt termination of Pi itself (SIGKILL, host crash) cannot run extension shutdown hooks. This is not an OS sandbox or a persistent process supervisor. Print/JSON mode exits after its run and shuts monitors down; use interactive or long-lived RPC mode for continuing watches.

  • Default lifetime: 5 minutes; explicit timeout: 1 second–30 minutes. No durable or infinite watches in this version; re-arm only when still wanted.
  • At most four running/pending-approval monitors.
  • Lines batch on a 200 ms timer. Each line retains 500 characters; each batch retains 3,000, plus truncation markers. Blank lines are omitted. Large bursts are deliberately lossy; this is notifications, not full log archival.
  • Newline-free pending output is capped at 1 MiB. UTF-8 pipe boundaries are decoded safely. An unterminated final line is flushed on exit.
  • A token bucket allows ten batches initially, then one every two seconds. Excess batches are suppressed and counted; sustained flooding can stop the source after approximately 30 seconds. Each task stops after 100 output events.
  • Until delivery, an extension-owned queue retains at most 40 events. Overflow drops an old output event first (otherwise the oldest event) and reports queueSuppressed on the next delivery. This queue, unlike native follow-ups, can be discarded on branch navigation without clearing human messages. Busy delivery steers from turn_end, which Pi drains before the next model call, so a delivered event never waits in Pi's own queue across an abort or a branch change. A cancellable 100 ms idle check also covers delayed lifecycle handlers; it does not call the model.
  • Exit events report exit code, signal, output-event count, suppressed count, and stderr tail. Failure and timeout are not reported as success.
  • Stop/timeout sends SIGTERM to the process group, then SIGKILL after one second if needed. A shell exit also removes leftover members of its process group. Signal errors are reported as cleanup_failed, never thrown from a background callback. If close cannot be confirmed within two seconds of stopping, the task reports its process-group ID and cleanup error instead of hanging Pi. Shutdown-only failures go to stderr; inspect the reported group manually.
  • Shutdown, reload, session replacement/fork, tree navigation, and compaction stop owned processes and discard pending events. Compaction displays a warning: re-arm any watch you still need afterward. Tasks also stop if another extension subsequently cancels navigation or compaction. Nothing restarts on resume.