@qcts33/pi-herdr-tasks0.3.0
Run one external command per Herdr pane as a Command task and deliver its Task outcome back to a pi conversation
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
@qcts33/pi-herdr-tasks
Run one external command per herdr pane as a Command
task, watch it in its own herdr tab, and get its outcome back in the
pi conversation when it ends. The
task tool returns as soon as the command is admitted, so a build, a test
suite, a batch job, or a training run is not tied to the turn that started it.
POSIX only in this version.
How it works
task starts a Command task: one external command on the raw terminal surface
of a herdr pane, with no Pi child runtime and no agent lifecycle. The tool
creates a Task run directory under the OS temporary directory, writes a
generated Task launcher there, and opens a Task pane in a new herdr tab
in the parent runtime's workspace, without stealing focus. The launch returns a
report once the launcher has written its start record — normally immediately —
and watches from the parent process from then on.
Inside the Task pane the launcher:
- runs the caller's command verbatim, so the command text is never subject to shell quoting or escaping;
- redirects the command's stdin from the null device, so a command that waits for input fails instead of hanging;
- pipes stdout and stderr through
tee, so the Task pane and the Task log carry the same bytes; - records the command's pid, traps
INTandTERM, and writes the Task completion record when the command ends, with its status, exit code or signal, and start and end times.
The extension reads that record; it never reads the pane for a result. A
human's Ctrl+C in the pane therefore still produces a terminal record rather
than missing evidence. The watch polls the record once a second, and when the
command has ended it reads the log tail, closes the Task pane, and delivers
exactly one Task outcome message. The outcome carries the Task status, exit
code or signal, elapsed time, the Task log path, a Task failure category
when the task failed, and the last 50 lines of the log (ANSI escapes stripped,
capped at about 8 KB). The message arrives through Pi's custom-message API as a
steer with a turn trigger; a long tail or log path stays readable with the
usual expand key.
The Task log is the complete output, not a terminal window: it stays in the
Task run directory after the task ends, so a result can be read the next day.
The reasoning behind the result channel is ADR-0001 in this package's
docs/adr/.
A line above the editor spins a count of running Command tasks — ⠹ 2 tasks — and disappears when the last outcome is claimed. Names, logs, and elapsed times stay in the task_inventory report and the Task outcome. The extension sends nothing to the model while a task runs and nothing to a phone when it ends.
Install
Install the package from npm:
pi install npm:@qcts33/pi-herdr-tasks
Start herdr, then run pi inside it:
herdr
pi
The extension requires HERDR_ENV=1, the herdr CLI on PATH, and the
workspace id herdr exposes to the session. A missing CLI refuses every task
tool with one line telling you to start pi inside herdr; a herdr below the
floor is refused with the runtime-window line in Requirements.
Requirements
- pi — the coding agent, 1.x (floor 1.0.0, tested on 1.2.0); update with
pi update self - herdr — the required terminal workspace, 0.8.2 or newer (tested on 0.9.5); update with
herdr update
Outside that window, a session shows one warning at session start, and every
herdr-backed tool refuses with the same line the warning carried. The check is
runtime-compat.ts, generated from shared/runtime-compat.template.ts and
probed once per process, so a herdr below the floor or a server the running
client cannot talk to fails a tool call with an instruction instead of a raw
herdr error. The decision is ADR-0009 in the repository root.
Tools
task
Start one Command task and return its Task launch report.
| Parameter | Type | Default | Description |
|---|---|---|---|
command |
string | required | The command to run. It is written verbatim into the Task launcher, so quotes and shell metacharacters behave as in a shell; do not quote it for the tool. |
name |
string | a label derived from the command | Display name used in the report, the outcome, and the inventory. |
cwd |
string | the parent runtime's directory | Working directory for the command. |
The tool always returns a launch report, never the outcome. Its text says the task is running in the background in its own herdr tab, that the outcome arrives as one message and starts the next turn, that the turn ends when nothing else is left to do, and where the Task log is. The tool also carries one prompt guideline, which the host renders into the system prompt's rules section: end the turn when the next step needs a Task outcome or nothing else is left to do, because the outcome starts the next turn.
Its details name the run id, name, command, cwd, tab id, pane id, Task run
directory, and running as the status. There is no foreground mode (ADR-0002),
and there is no env parameter, tool-level timeout, or concurrency cap.
A launch that fails before the command is admitted is reported synchronously in this tool result, its Task pane is reclaimed, and no Task outcome is delivered. That covers a tab that cannot be created, a launcher that cannot be started, and a launcher that never writes its start record inside the 30-second startup window. A refused platform, a missing herdr CLI, and a runtime outside the support window are reported the same way.
task_cancel
Cancel one running Command task, by exact id or exact name.
| Parameter | Type | Description |
|---|---|---|
id |
string | Exact run id; tried first. |
name |
string | Exact display name; used when no id is given. |
A missing target, an unknown id or name, and an ambiguous name are each reported precisely, with the matching candidates for an ambiguity. A task that is already cancelling reports that instead of receiving a second request, and a task that is already finalizing its outcome is left alone.
Cancellation is applied to the Task pane, never directly to the command process, and in two levels: the extension writes a cancellation request, interrupts the Task pane's foreground process group, and — only if the command's own pid is still alive after a 5-second grace period polled every 100 ms — closes the Task pane. The Task completion record still decides how the task ended. A command that detached into its own session survives both levels; the result says so instead of implying the machine is idle. The design and the measured reason both levels exist are ADR-0003.
A cancellation you request delivers a cancelled Task outcome. A cancellation
the extension performs internally is control flow and delivers nothing. A
manual Ctrl+C in the Task pane is a cancellation too: the launcher records it
and the outcome is delivered.
task_inventory
Report the Command tasks this session still owns. It takes no parameters, is
read-only, is derived at call time from the running task registry, and keeps no
history: a finished task is absent. Each entry names the task, its command, its
running status, elapsed time, tab id, pane id, and Task run directory. It
needs no herdr call and reports nothing about tasks started by another session
or by a parent process that has ended.
Statuses and failure categories
A Task status is running while the command executes, and otherwise how it
ended: completed, failed, or cancelled. A Task failure category is the
stable machine-readable reason on a failed outcome:
no-completion-record— the Task pane disappeared before a completion record existed; the log is delivered as it stood;invalid-completion-record— the record was there but unusable;supervision-error— the extension could not supervise the task.
completed outcomes and command-exit failed outcomes carry an exit code;
cancelled outcomes carry a signal name; a failed outcome with a failure
category has neither.
Lifecycle
A Command task's lifecycle belongs to herdr, not to the parent runtime (ADR-0002):
- Quitting or restarting Pi leaves running Command tasks running in their panes. Their outcomes are dropped; the Task log stays in the Task run directory.
/reloadcontinues the same parent runtime: the running registry and its watchers are preserved, and outcomes keep arriving.- No other shutdown is a cancellation. Every shutdown reason except a reload stops watching and drops the outcome without cancelling anything.
- A new parent process does not re-adopt, resume, or recover tasks. Use
task_inventory(orherdr tab list) to see what a previous session left running. - A Command task started by a Subagent keeps it alive: the child's Completion evidence waits for the Task outcome, so the outcome reaches the session that asked for it. A cancelled child ends its own tasks as control flow, and a Task still outlives a child runtime that a cancellation or a parent exit ends, with the Task log left in the Task run directory.
Task run directory
Each Command task gets one private directory under the OS temporary directory,
named by its run id (herdr-tasks/<run id>/). It holds the launcher, the Task
log, the start record, the Task completion record, the command pid, the
cancellation request, and an owner record naming the creating process.
The directory outlives the task, because the Task log is the delivered evidence. A bounded sweep keeps the storage from growing forever: every allocation sweeps the run root (at most 100 entries per pass) and deletes a run directory only when its owner process is gone and nothing has written to it for seven days. A directory whose owner is still alive, or that was written recently, is left alone; one whose owner record is unreadable is decided by age alone.
Platform limit
Command tasks are POSIX only in this version. Starting a task on Windows is refused with one message naming the limit, both in the tool result and once at session start when the session has a UI:
Command tasks are POSIX only and cannot start on Windows. Run the command with the built-in bash tool instead.
The limit exists because the Task launcher is a POSIX sh script run by path
inside the pane. Windows support, including its test matrix, is out of scope
for this version.
Development
Run the unit tests, lint, and typecheck locally:
npm test
npm run lint
npm run typecheck
The unit suite is node --test files named after the module they cover; no
build step is involved. The end-to-end suite (test/integration/, run from the
repo root as npm run test:integration:tasks) drives a real parent Pi session
inside herdr and uses the same PI_TEST_* environment contract as the
subagents suite, documented in packages/pi-herdr-subagents/README.md in the
repository. It skips itself unless HERDR_ENV=1 and herdr is on PATH.
License
MIT