@carrotciallo/pi-monitor
Nonblocking read-only background monitoring for Pi Coding Agent
Package details
Install @carrotciallo/pi-monitor from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@carrotciallo/pi-monitor- Package
@carrotciallo/pi-monitor- Version
1.3.0- Published
- Oct 13, 2026
- Downloads
- 394/mo · 8/wk
- Author
- carrotciallo
- License
- MIT
- Types
- extension
- Size
- 242.5 KB
- Dependencies
- 1 dependency · 2 peers
Pi manifest JSON
{
"image": "https://github.com/carrotciallo/pi-monitor/raw/main/docs/promo-poster.webp",
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-monitor
Nonblocking, bounded background monitoring for Pi Coding Agent.
Start a trusted read-only query, receive a monitor ID immediately, and resume the session when a condition is stably matched, a query fails, or the deadline expires. Say goodbye to idle token-burning polling loops in your agent workflows.
⚡ Why pi-monitor?
When managing long-running background tasks (e.g. multi-step builds, long CI pipelines, server provisioning, database restores), standard AI Coding Agents fall into active polling traps:
| Dimension | Legacy Agent Polling ❌ | With pi-monitor ✅ |
|---|---|---|
| Execution Model | Burns turn cycles with repetitive sleep and status tools |
Instantly yields control; nonblocking bounded background polling |
| Token Consumption | Repetitive status checks consume massive prompt context | Headless scheduler queries in background; 0 extra LLM tokens burned |
| Turn Lifecycle | Blocks user interaction or triggers dead-loop safeguards | Agent continues independent tasks or ends turn cleanly awaiting wakeup |
| Stability Validation | Susceptible to transient flickers or single false positives | Dual debounce: consecutiveMatches and stableForSeconds |
| Concurrency & Safety | Risk of overlapping queries and ReDoS vulnerabilities | Single in-flight lock, RE2JS ReDoS immunity, and scoped call leases |
Architecture Overview
Pi-Web is supported, but not required.
The extension relies on native Pi host extension APIs rather than a web-only backend. Compatible terminal hosts display a compact live status widget; background queries and session resume work equally well in headless environments.
Installation
pi install npm:@carrotciallo/pi-monitor
Or install straight from the repository:
pi install git:github.com/carrotciallo/pi-monitor
Run /reload in an existing session.
Requirements: Node.js >= 22.19.0. The host must supply ExtensionToolContext.executeTool, before_agent_start.systemPromptOptions.promptGuidelines, and standard extension messaging APIs.
Quick Start & Scenarios
Tools and Commands
| Tool / Command | Type | Purpose |
|---|---|---|
monitor_start |
Agent Tool | Start bounded polling of a trusted read-only tool; returns monitorId immediately |
monitor_status |
Agent Tool | Inspect current-session monitor details, health metrics, and retained output |
monitor_cancel |
Agent Tool | Stop a specific monitor or all active monitors in the current session |
/monitor_status [id] |
User Command | CLI / Web command to view live monitor status |
/monitor_cancel [id|all] |
User Command | CLI / Web command to terminate background monitors |
Scenario 1: Waiting for a Build / CI Job Completion
The agent triggers an asynchronous job, starts monitoring, and yields immediately:
{
"tool": "job_status",
"args": { "id": "build-release-1024" },
"condition": { "field": "status", "equals": "completed" },
"consecutiveMatches": 2,
"description": "Wait for build release completion"
}
Scenario 2: Service Drain Stability Hold
For state transitions that require prolonged stability (e.g. connection draining before shutdown):
{
"tool": "bash",
"args": { "command": "job-status --json" },
"condition": { "field": "state", "equals": "drained" },
"intervalSeconds": 30,
"timeoutSeconds": 21600,
"consecutiveMatches": 3,
"stableForSeconds": 120,
"description": "Wait for drain to hold stable for two minutes"
}
Note: For
bashandhypa_shell, when execution succeeds (exit_code: 0) and the output is valid JSON, the output is automatically unwrapped for field evaluation. Only exact JSON is adapted—no partial or fuzzy parsing.
Behavior & Robustness
When a task is pending and requires a later check, the agent calls monitor_start instead of executing a polling loop.
- Defaults & Limits:
- Polling interval: 5s (range: 2–300s)
- Timeout: 120s (range: 5–604,800s / up to 7 days)
- Max retries: 3 on transient errors (range: 0–10)
- Concurrency: Max 5 concurrent monitors per session.
- Single In-Flight Lock: Queries for a given monitor never overlap.
- Stability Engine:
consecutiveMatches(1–1000, default 1): Requires N continuous observed matches.stableForSeconds(0–604800, default 0): Requires condition to hold continuously for at least N seconds.- Any miss or query failure immediately resets stability counters to prevent premature completion.
- Smart Deduplication: Starting a monitor with the same tool, canonical-JSON arguments, condition,
displaydeclaration, and polling config reuses the existing active monitor and setsreused: true. - Matching Engine: Supports structured field checks (equality, numeric comparison, array inclusion), negation-aware text matching, RE2JS regular expressions, or SHA-256 hashes. RE2JS eliminates ReDoS risks.
- UI & Resumption:
Displays live status badge, task name, query count, elapsed time, health progress, and observed values.
Progress display (
display): Declare up to 4 result fields to project into the live widget, so a polled script can report progress — download percentage, item counts, transfer speed — without the user asking for a status check. Each entry names a field path plus an optionallabel,total, ortotalField:{ "tool": "read", "args": { "path": "/tmp/progress.json" }, "condition": { "field": "status", "equals": "done" }, "display": [ { "field": "downloaded", "label": "progress", "totalField": "totalBytes" }, { "field": "speed", "label": "speed" } ] }renders as
progress=530/1000 · speed=1.2MB/s.totalFieldreads a sibling field and wins over a fixedtotal. Values are bounded to 80 characters, sensitive field paths (containingkey,token,secret,password, …) render as[REDACTED], and composite values collapse to[object]or[array: N]. A failed or field-less sample clears the segment rather than showing a stale reading. Omittingdisplayleaves the widget exactly as it was.On condition match, query failure, or timeout, a concise notification wakes the session. Raw response payloads stay in
monitor_statusto prevent context pollution.
- Branch & Lifecycle Isolation: Branch switches, session changes, reloads, or shutdowns transition active monitors to
interruptedwithout firing turn-waking messages into improper sessions.
Safety & Permissions
- Default-Deny Read Policy: Non-shell tools must be provably read-only. A tool is admitted when it is a known read-only builtin, or when it declares
readOnlyHint: trueitself. Anything unproven is rejected. - Annotations Survive the Adapter: Some MCP adapters register a server's tools without forwarding its
annotationspayload, which would make an explicitly read-only tool look unproven. When that happens pi-monitor recovers the declaration from the adapter's on-disk cache. This is evidence only, never authority: it cannot lift a hard block below, and a cachedreadOnlyHint: trueon a mutating tool name is still rejected. - Hard Blocks Outrank Everything: Forbidden gateways (
mcp,mcpScript), MCP namespace proxies (mcp__*),destructiveHint: true, and mutating tool names are rejected before any annotation or trust rule is consulted. A mislabelledreadOnlyHinttherefore cannot admit a destructive tool, and trusting a proxy cannot hand the monitor every tool behind it. - Shell Query Constraints:
bashandhypa_shellaccept nonempty commands. System guidelines instruct the agent to execute only idempotent read-only status checks. Commands inherit the host's existing permission boundaries. - Call Leases: Scoped, one-use call leases allow seamless compatibility with external repeat guards and loop protection extensions.
- Memory-Only & Redaction: Execution state lives in memory only. Retained audit logs are capped at 10 items per session with automated redaction of sensitive arguments.
Trusting a tool your host cannot vouch for
Many MCP servers describe their tools loosely, or not at all, so the default deny keeps them out of reach. Rather than weakening the default for every tool, you can vouch for specific tools in a Markdown file, which keeps the justification next to the rule:
# pi-monitor trust
## Allow
- `mail_list_*` — read-only listings; the server omits annotations
- index_search
## Deny
- mail_get_emails_content
- User level:
~/.pi/agent/pi-monitor.md, always read. SetPI_MONITOR_TRUST_FILEto keep the rules elsewhere. - Project level:
PI-MONITOR.mdat the project root, read only when the project is trusted, so an untrusted repository cannot grant itself monitoring access. - Patterns are globs:
*matches any run of characters,?exactly one; matching is case-insensitive. - Only
-list items under anAllow/Denyheading are rules. Everything else is documentation and is ignored, so the file can explain itself. - An explicit
Denybeats an annotation. An explicitAllowrescues an unannotated tool. Neither can override a hard block above.
A commented starting point lives at docs/trust-example.md; copy it to ~/.pi/agent/pi-monitor.md and edit the two lists.
The cache fallback reads ~/.pi/agent/mcp-cache.json; set PI_MONITOR_MCP_CACHE to point elsewhere.
Development
# Install development dependencies
npm ci --include=dev
# Type checking
npm run check
# Verify packaging integrity
npm pack --dry-run
Contributing & License
Contributions, issues, and PRs are welcome!
See CONTRIBUTING.md, SECURITY.md, and MIT License.
