diff options
| -rw-r--r-- | README.md | 21 | ||||
| -rw-r--r-- | _selfcheck.lua | 28 | ||||
| -rw-r--r-- | panto-herdr/init.lua | 35 |
3 files changed, 63 insertions, 21 deletions
@@ -2,8 +2,8 @@ `panto-herdr` reports panto lifecycle state to [Herdr](https://herdr.dev/) when panto is running in a Herdr pane. It reports `panto` as the agent label and -uses `thinking`, `responding`, `compacting`, and `tool:<name>` as short status -labels. +uses `thinking`, `responding`, `compacting`, and `tool:<name>` as short report +messages. Install it through panto's rock loader: @@ -26,15 +26,20 @@ its JSON output, so reporting does not affect the panto terminal UI. | panto event | Herdr state | | --- | --- | -| `session_start` | `idle` | +| `session_start`, `session_ready` | `idle` | | `user_message`, `thinking`, `compaction`, `tool_details`, `assistant_text` | `working` | | `assistant_text_complete` | `idle` | +| `session_end` | agent released | -Current panto extensions do not receive a turn-complete or interrupt event. -`assistant_text_complete` is therefore the best available idle boundary; an -assistant text block followed by a tool call can show `idle` briefly before -the next lifecycle event restores `working`. Native session restore is also -out of scope until panto exposes a stable session reference to extensions. +`session_ready` restores `idle` after panto replays historical lifecycle events +while loading a resumed session. `session_end` removes panto's agent report when +the process shuts down, so Herdr also clears the pane/space activity indicator +once no reported agents remain. + +`assistant_text_complete` is the block-level idle boundary; an assistant text +block followed by a tool call can show `idle` briefly before the next lifecycle +event restores `working`. Native session restore is out of scope until panto +exposes a stable session reference to extensions. ## Check diff --git a/_selfcheck.lua b/_selfcheck.lua index 8b83fa5..08df12c 100644 --- a/_selfcheck.lua +++ b/_selfcheck.lua @@ -16,6 +16,7 @@ end assert(loadfile(assert(find_rockspec(), "no .rockspec in the repo root"))) +package.loaded.panto = nil package.preload.panto = function() return { ext = { @@ -24,6 +25,7 @@ package.preload.panto = function() } end +package.loaded.luv = nil package.preload.luv = function() return { spawn = function(command, options, _) @@ -34,12 +36,14 @@ package.preload.luv = function() end local getenv = os.getenv +local now = 100 +os.time = function() return now end os.getenv = function(name) if name == "HERDR_PANE_ID" then return "w1:p1" end return getenv(name) end -local entry = require("panto-herdr") +local entry = assert(loadfile("panto-herdr/init.lua"))() entry.activate() events.session_start() events.user_message() @@ -47,11 +51,25 @@ events.user_message() events.tool_details({ tool_name = "read" }) events.assistant_text() events.assistant_text_complete() +events.session_end() -assert(#reports == 5) +-- A new process in the same pane/source must outrank the old process's final +-- sequence number instead of being ignored as stale. +now = 101 +events = {} +entry.activate() +events.session_start() +events.assistant_text() +events.session_ready() + +assert(#reports == 9) assert(reports[1].command == "herdr") -assert(table.concat(reports[1].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state idle --seq 1") -assert(table.concat(reports[3].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state working --seq 3 --custom-status tool:read") -assert(table.concat(reports[5].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state idle --seq 5") +assert(table.concat(reports[1].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state idle --seq 100000001") +assert(table.concat(reports[3].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state working --seq 100000003 --message tool:read") +assert(table.concat(reports[5].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state idle --seq 100000005") +assert(table.concat(reports[6].args, " ") == "pane release-agent w1:p1 --source panto:extension --agent panto --seq 100000006") +assert(table.concat(reports[7].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state idle --seq 101000001") +assert(table.concat(reports[8].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state working --seq 101000002 --message responding") +assert(table.concat(reports[9].args, " ") == "pane report-agent w1:p1 --source panto:extension --agent panto --state idle --seq 101000003") print("panto-herdr self-check: ok") diff --git a/panto-herdr/init.lua b/panto-herdr/init.lua index dffef51..cc0b527 100644 --- a/panto-herdr/init.lua +++ b/panto-herdr/init.lua @@ -11,10 +11,22 @@ return { local panto = require("panto") local uv = require("luv") - local seq = 0 + -- Herdr orders reports by source and sequence. Seed each process from + -- wall time so a restarted panto cannot be shadowed by the prior + -- process's larger sequence numbers. + local seq = os.time() * 1000000 local last_state local last_status + local function spawn(args) + local handle + pcall(function() + handle = uv.spawn("herdr", { args = args, stdio = { nil, nil, nil } }, function() + if handle then handle:close() end + end) + end) + end + local function report(state, status) if state == last_state and status == last_status then return end last_state, last_status = state, status @@ -28,19 +40,25 @@ return { "--seq", tostring(seq), } if status then - args[#args + 1] = "--custom-status" + args[#args + 1] = "--message" args[#args + 1] = status end - local handle - pcall(function() - handle = uv.spawn("herdr", { args = args, stdio = { nil, nil, nil } }, function() - if handle then handle:close() end - end) - end) + spawn(args) + end + + local function release() + seq = seq + 1 + spawn({ + "pane", "release-agent", pane_id, + "--source", "panto:extension", + "--agent", "panto", + "--seq", tostring(seq), + }) end panto.ext.on("session_start", function() report("idle") end) + panto.ext.on("session_ready", function() report("idle") end) panto.ext.on("user_message", function() report("working", "thinking") end) panto.ext.on("thinking", function() report("working", "thinking") end) panto.ext.on("compaction", function() report("working", "compacting") end) @@ -49,5 +67,6 @@ return { end) panto.ext.on("assistant_text", function() report("working", "responding") end) panto.ext.on("assistant_text_complete", function() report("idle") end) + panto.ext.on("session_end", release) end, } |
