Herdr presence extension for Posoco: reports agent lifecycle state to the herdr terminal multiplexer via a single Observer port
Dependencies
Targets: native + js (bun); compiles inert elsewhere
| Struct | Port | Contribution |
|---|---|---|
| HerdrReporter | Observer | maps turn lifecycle onto herdr agent states by shelling herdr pane report-agent |
| HerdrReporter | Lifecycle | releases the pane's herdr authority on Agent shutdown by shelling herdr pane release-agent |
| HerdrReporter | Extension | contributes that observer plus that lifecycle and nothing else — no tools, no hooks, no prompt sections |
| HerdrDelegate | ToolProvider | contributes exactly one tool, herdr_delegate |
| HerdrDelegate | Lifecycle / Observer | captures the composed Tasks capability and tracks the parent session (background support) |
| HerdrDelegate | Extension | id posoco_ext_herdr_delegate; that tool and prompt section, requires Capability::Tasks |
moon add posoco/ext-herdr// moon.pkg: "posoco/ext-herdr" @herdr
// copied from cetas-js/lib/cetas_js.mbt — both detections are env-gated:
match @herdr.HerdrReporter::detect() {
Some(reporter) => exts.push(reporter as &@posoco.Extension)
None => ()
}
match @herdr.HerdrDelegate::detect(cwd=runtime.config.cwd) {
Some(delegate) => exts.push(delegate as &@posoco.Extension)
None => ()
}TurnStarted → herdr pane report-agent <pane> --source cetas --agent cetas --state working
UserRequestStarted → … --state blocked --message <bounded prompt>
UserRequestFinished → … --state working
TurnCompleted → … --state idle
TurnFailed → … --state idle
registration (once) → … --state idle
Agent shutdown → herdr pane release-agent <pane> --source cetas --agent cetasherdr pane split --pane <this pane> --direction <right|down> --cwd <host cwd> --no-focus
herdr pane run <new pane> "cetas-headless --permission readonly -- '<task>' ; echo '[cetas-headless] pane-id'le"
herdr pane wait-output --regex "\[cetas-headless\] (turn [0-9]+ done|turn failed:|pane-idle)" --source recent-unwrapped --timeout <ms> <new pane>
herdr pane read <new pane> --source recent-unwrapped
(re-run once, best-effort, after a timeout to capture child_session)
herdr pane close <new pane> (only after a successful read; skipped when close_pane=false)| argument | type | default | meaning |
|---|---|---|---|
| task | string, required | — | the delegated task; must be a single line — a newline is rejected before any CLI call, since it would escape the one pane run argv element into separate shell commands |
| permission | "readonly" / "write" | readonly | readonly launches cetas-headless --permission readonly (cannot write); write launches --yolo (unattended full permission) |
| model | string | — | forwarded to the child as --model; an empty string is treated as omitted |
| effort | string | — | forwarded to the child as --effort; an empty string is treated as omitted |
| session | string | — | a child_session id from an earlier result or error footer (returned on success, failure, and best-effort timeout); re-launches headless with --session to continue that child. Related tasks must reuse the same child session instead of spawning a new one. Omit it for a new conversation — never pass an empty string (an empty string is treated as omitted) |
| timeout_ms | integer | 1800000 (30 min) | wait bound for the completion marker |
| direction | "right" / "down" | right | direction of the split |
| close_pane | boolean | true | auto-close the child pane after a successful read; the pane is never closed on failure or timeout (a timed-out child may still be running and its pane stays for inspection); pass false to always keep the pane |
| background | boolean | false | return a receipt immediately and run the delegation through the Agent-owned task capability (core Tasks); the result arrives later as a follow-up turn. Background always starts a fresh child and cannot take session (see below) |
background herdr_delegate accepted
delegation_id: agent_task_…
a fresh pane and child session will start; the result arrives as a follow-up turn| variable | read by | meaning |
|---|---|---|
| HERDR_ENV + HERDR_PANE_ID | reporter and delegate | must be present and non-empty; absent → the extension is absent |
| HERDR_BIN_PATH | reporter and delegate | herdr CLI binary; defaults to herdr when absent or blank |
| CETAS_HEADLESS_BIN | delegate only | headless binary; defaults to cetas-headless when absent or blank |
| target | detection | CLI transport | notes |
|---|---|---|---|
| native | @env reads (synchronous, no subprocess) | @process.collect_output — stdout and stderr captured separately | herdr writes result JSON on stdout and error JSON on stderr, so merged capture would make a success read as an error |
| js (bun) | Bun.env (synchronous) | Bun.spawn with both streams piped; exit and drains awaited as one JSON envelope | the extern touches Bun only when invoked |
| wasm / wasm-gc | — | — | compiles as fully inert; both constructors return None |
pub struct HerdrDelegate {
pane : String
bin : String
headless_bin : String
cwd : String
active : Int
tasks : Tasks?
session : String?
parent_yolo : () -> Bool?
// private fields
}impl Extension for HerdrDelegateimpl Lifecycle for HerdrDelegateimpl Observer for HerdrDelegateimpl SystemPromptContributor for HerdrDelegateimpl ToolProvider for HerdrDelegateasync fn execute(self : HerdrDelegate, name : String, call : ToolCall) -> ToolOutcome raise RuntimeErrorfn HerdrDelegate::HerdrDelegate(pane~ : String, bin~ : String, headless_bin~ : String, cwd~ : String, background_run? : async (HerdrDelegate) -> ToolOutcome?, parent_yolo? : () -> Bool?) -> HerdrDelegateasync fn HerdrDelegate::execute(self : HerdrDelegate, name : String, call : ToolCall) -> ToolOutcome raise RuntimeErrorfn HerdrDelegate::on_compose(self : HerdrDelegate, view : CompositionView) -> Unit raise CompositionErrorfn HerdrDelegate::on_event_at(self : HerdrDelegate, scope : EventScope?, _event : TurnEvent) -> Unitimpl Extension for HerdrReporterimpl Lifecycle for HerdrReporterimpl Observer for HerdrReporterfn HerdrReporter::on_compose(_self : HerdrReporter, _ctx : CompositionView) -> Unit raise CompositionErrorfn HerdrReporter::on_event_at(self : HerdrReporter, _scope : EventScope?, event : TurnEvent) -> UnitInstall
Download zipHerdr presence extension for Posoco: reports agent lifecycle state to the herdr terminal multiplexer via a single Observer port
Dependencies