pi-taskwatcher

Progress chips for background tasks + auto-detached bash + hung-tool watchdog + one-shot wake-ups

Packages

Package details

extension

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

$ pi install npm:pi-taskwatcher
Package
pi-taskwatcher
Version
0.5.2
Published
Nov 2, 2026
Downloads
601/mo · 601/wk
Author
codelllc09
License
MIT
Types
extension
Size
160.1 KB
Dependencies
0 dependencies · 1 peer
Pi manifest JSON
{
  "extensions": [
    "./taskwatcher.ts",
    "./autobg.ts",
    "./wake.ts"
  ]
}

Security note

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

README

English · 简体中文

pi-taskwatcher

Live progress chips in the Pi footer for any background process — plus a watchdog that nudges the agent when a task stalls. Any language, any machine, no Pi-side wiring: the job just writes a JSON file.

✓ Convert 153/153 100%   ◆ Transcribe 16/153 10% · 3.4h left

Convention

~/.pi/agent/status/<id>.json, polled every 5 s. Atomic writes (tmp + rename) recommended; partial JSON is tolerated.

{"id":"asr","label":"Transcribe","done":16,"total":153,"note":"3.4h left","updated":1761234567}
echo '{"id":"x","label":"L","done":1,"total":10,"updated":'$(date +%s)'}' \
  > ~/.pi/agent/status/x.json

CLI

scripts/task_status.py wraps the convention:

task_status.py bump --id asr --label Transcribe --total 153   # +1 per unit
task_status.py set  --id asr --done 16 --total 153 --note "3.4h left"
task_status.py sync --id asr --total 153 --done-file out/_done.json
task_status.py done --id asr      # or: rm / ls

Rules

✓ done · ◆ running · stale after 180 s silent · hidden after 30 min · max 3 chips (newest first). /task-status lists all (incl. hidden and problem files); /task-status clear wipes.

Agent tool

The extension registers a task_progress tool (set/bump/done/remove), so the agent can create and drive tasks itself. The set result embeds the script-side wiring recipe, and a system-prompt guideline makes the agent reach for it before launching any long batch job — no user reminder needed.

Conversation isolation

STATUS_DIR is machine-global, but tasks belong to the conversation that created them: tool writes stamp owner=<sessionId> (creation only — never adopted implicitly), and the footer and watchdog consider only this conversation's tasks. Un-stamped files (legacy writers) are not shown or watched here, but never silent: each live one is announced once with the claim recipe (/task-status claim <id> or task_progress {action:"claim"}); finished or aged-out ones — ghosts — are not announced, just listed [ownerless] in /task-status (delete: task_progress {action:"remove"}). PI_TASK_SHOW_OWNERLESS=1 restores legacy display; PI_TASK_ISOLATION=0 disables isolation. Scripts keep ownership via --owner (CLI) or the owner JSON field.

Watchdog

A task owned by this conversation that stops updating for PI_TASK_WAKE_MIN minutes (default 5) is nudged exactly once per update-level: the extension injects a wake-up message into the session (followUp, queued behind a busy run, never interrupts) telling the agent what stalled and how to close it out. Progress re-arms the nudge. Stuck jobs surface themselves.

Every queued notice (stale nudge, wake, autobg) stamps its build time — local clock with an explicit UTC offset — because a queued message may reach the agent long after it was built; the stamp lets it judge how stale the "N min" figures in it really are.

Since v0.4.0 the nudge set is all own tasks, not only the rendered ones: PI_TASK_HIDE_MIN fades the chip but never removes a task from the watchdog — a task hung for 35 min is nudged just like one hung for 6 min (previously the longer it hung, the quieter it got). Tasks stamped kind:"bg" are skipped: autobg delivers their completion wake itself. Scripts that update status files directly must read-modify-write (jq, or the CLI's --owner): a partial overwrite silently drops owner and the task falls out of this conversation's watchdog.

Autobg (v0.4.0) — long-running tool calls never stall the agent

bash auto-detach. Commands run synchronously exactly like the built-in tool. Past PI_AUTOBG_DEMOTE_MS (default 120 s) the process is not killed: output switches to ~/.pi/agent/bg/<id>.log, the tool returns immediately with an explanation, and the model gets control back. On exit a followUp message wakes it with the exit code, duration and output tail. A kind:"bg" status file shows the footer chip. Esc still kills during the synchronous phase; a detached task is independent.

The demote notice asks the model for a one-shot sanity check (it knows best whether this command should have finished long ago), and a stuck watch guards detached tasks: still running with no log growth for PI_AUTOBG_BG_STUCK_S (default 30 min) → one check-up wake per silent period (expected quiet runs are told to ignore it; stuck ones get the pid to kill).

Generic stall watch (MCP tools, other extensions). Only bash exposes an execution seam, so a hung non-bash tool is detected, not fixed: silent for PI_AUTOBG_STALL_S (180 s) → the user is informed and the model gets a steered note — steer lands between turns of a running agent, before its next decision, because a busy agent inside a tool-call loop never drains followUps until it stops; silent for PI_AUTOBG_ABORT_S (600 s) → the run is aborted — the only channel that frees a model blocked on a hung tool — with an explanation so it can retry differently. Stall notes are budgeted per tool (PI_AUTOBG_MAX_STALL_NOTES, default 2): a tool that legitimately takes minutes per call (e.g. a single long VLM request) would otherwise re-educate the model on every invocation — the second note announces the mute, later ones are silent, and the abort fuse stays armed regardless. When a stalled call settles, only one outcome adds a message: completed gets a short "prefer stepwise/backgroundable" retrospective (followUp); an abort explains itself, and a failed call's result is already in context. Per-tool cap PI_AUTOBG_MAX_AUTO_ABORTS (2) prevents retry loops; PI_AUTOBG_NEVER_ABORT exempts legitimately slow tools. MCP servers configured without a timeout are announced at startup: with a timeout, a hung call returns an error result and the model continues on its own.

Wake (v0.5.0) — one-shot alarms, set by you

The watchdog family wakes the agent on conditions (stalled task, hung tool, exited bg command). /wake adds the missing axis: time, chosen by the user. One-shot by design — no loop tax; the "is this wake worth the tokens?" decision stays with the person who set it.

/wake 30m check the build result      # relative: 30s|30m|2h
/wake 14:00 review the test run       # absolute HH:MM; tomorrow if past
/wake list                            # pending wakes
/wake cancel w1                       # cancel

/wake is the name since v0.5.1 — the bare verb fits both spec forms ("at" only matched the absolute one). The old /wake-at name was removed outright: the package has no external users to carry.

Delivery uses the same follow-up channel as the stale nudge: an idle agent wakes immediately, a busy one gets it queued — never interrupted; the message carries a may-be-stale hint. An absolute time inside the PI_WAKE_MIN_S floor window rolls to tomorrow (an alarm can never sneak past the floor). Wakes are session-scoped and in-memory: they die with the session (announced, never silently), and nothing touches disk. Cross-session scheduling is cron's job — write a status file and let the footer/nudge pick it up. Delays are floored at PI_WAKE_MIN_S (60 s) and capped at 24 h; at most PI_WAKE_MAX (5) wakes pending per session.

Env

PI_TASK_STATUS_DIR · PI_TASK_MAX · PI_TASK_REFRESH_MS (floor 500 ms) · PI_TASK_HIDE_MIN · PI_TASK_STALE_SEC · PI_TASK_WAKE ("0" disables the watchdog) · PI_TASK_WAKE_MIN · PI_TASK_MAX_FILE_KB · PI_TASK_MAX_FILES · PI_TASK_ISOLATION · autobg: PI_AUTOBG_DEMOTE_MS · PI_AUTOBG_STALL_S · PI_AUTOBG_ABORT_S (0 disables) · PI_AUTOBG_MAX_AUTO_ABORTS · PI_AUTOBG_NEVER_ABORT (comma list) · PI_AUTOBG_MAX_STALL_NOTES (per-tool stall-note budget, default 2) · PI_AUTOBG_BG_STUCK_S (bg stuck watch, 0 disables) · PI_AUTOBG_DIR · wake: PI_WAKE_MAX (5) · PI_WAKE_MIN_S (60)

All values are sanitized: garbage/zero/negative fall back to the default and the ignored value is reported at session start.

Guarantees

  • Never blocks — all filesystem access is async; the TUI event loop stays responsive even on a slow or huge status dir (per-file size cap, per-tick file-count cap).
  • Never spins, never leaks — self-rescheduling unref'd timer; repeated failures back off to 30 s; a leaked timer can never keep the process alive.
  • Never fails silently — problem files (persistently corrupt, oversized, wrong schema) are counted, announced once, and listed by /task-status; refresh failures and watchdog delivery failures are announced; invalid env is reported; the CLI exits non-zero with a message on misuse (e.g. bumping an unknown task).

Install

pi install npm:pi-taskwatcher

Tests

npm test   # 199 assertions: tool roundtrip, watchdog lifecycle, anti-freeze,
           # env sanitization, zombie-proof exit, failure surfacing, wake

License

MIT — see LICENSE.