summaryrefslogtreecommitdiff
path: root/subagents/progress.lua
diff options
context:
space:
mode:
Diffstat (limited to 'subagents/progress.lua')
-rw-r--r--subagents/progress.lua60
1 files changed, 57 insertions, 3 deletions
diff --git a/subagents/progress.lua b/subagents/progress.lua
index aee198b..7cad2fa 100644
--- a/subagents/progress.lua
+++ b/subagents/progress.lua
@@ -51,6 +51,35 @@ local function display_tool_name(name)
return type(name) == "string" and name:gsub("__", ".") or "tool"
end
+-- A board replaces the whole tool block, so the Lua a `subagents.lua` call ran
+-- would otherwise never be visible — least of all for an inspection call that
+-- starts no children and therefore has no cards at all. `tool_call_complete`
+-- carries the raw input JSON, which a replayed entry gets too, so the source
+-- survives /resume without any handler-side bookkeeping.
+local function decode_input(text)
+ if type(text) ~= "string" or text == "" then return nil end
+ local ok, panto = pcall(require, "panto")
+ local json = ok and type(panto) == "table" and type(panto.ext) == "table" and panto.ext.json or nil
+ if type(json) ~= "table" or type(json.decode) ~= "function" then
+ local dk_ok, dkjson = pcall(require, "dkjson")
+ json = dk_ok and type(dkjson) == "table" and dkjson or nil
+ end
+ if type(json) ~= "table" or type(json.decode) ~= "function" then return nil end
+ local decoded_ok, value = pcall(json.decode, text)
+ if not decoded_ok or type(value) ~= "table" then return nil end
+ return value
+end
+
+local function source_of(tool_name, input)
+ if tool_name ~= "subagents.lua" then return nil end
+ local decoded = decode_input(input)
+ local source = decoded and decoded.source
+ if type(source) ~= "string" or source == "" then return nil end
+ -- Tabs are stripped to a single space by sanitization, which would flatten
+ -- indented code; widen them first so the listing keeps its shape.
+ return sanitize_multiline((source:gsub("\t", " "))):sub(1, MAX_PRESENTATION_BYTES)
+end
+
local function wrap(text, width, emit)
for _, row in ipairs(require("panto").text.wrap(sanitize(text), math.max(1, width))) do
emit(row)
@@ -237,11 +266,34 @@ local function body_lines(card, width, collapsed)
return out
end
+-- The executed source, verbatim: real newlines, one rendered row per wrapped
+-- source line. Collapsed shows the head of it, expanded the whole thing.
+local function source_lines(board, width, collapsed)
+ local limit = collapsed and COLLAPSED_LINES or MAX_PRESENTATION_LINES
+ local out, truncated = {}, false
+ local function add(row)
+ if #out < limit then out[#out + 1] = BODY_INDENT .. row else truncated = true end
+ end
+ for line in (board.source .. "\n"):gmatch("(.-)\n") do
+ if truncated then break end
+ wrap(line, width, add)
+ end
+ if truncated then out[#out + 1] = BODY_INDENT .. "…" end
+ return out
+end
+
local function render(board, width)
local out = {}
width = math.floor(tonumber(width) or 0)
- if #board.cards == 0 or width <= #BODY_INDENT then return out end
+ if width <= #BODY_INDENT then return out end
+ local source = board.source and source_lines(board, width - #BODY_INDENT, board.collapsed) or nil
+ if #board.cards == 0 and not source then return out end
out[#out + 1] = ""
+ if source then
+ out[#out + 1] = require("panto").text.truncate(
+ sanitize("⚙ " .. display_tool_name(board.tool_name)), width)
+ for _, line in ipairs(source) do out[#out + 1] = line end
+ end
for _, card in ipairs(board.cards) do
local sid = card.sid ~= "" and (" " .. card.sid:sub(1, sid_width) ..
(sid_width < #card.sid and "…" or "")) or ""
@@ -277,9 +329,11 @@ local function prune_boards()
end
end
-local function new_board(key)
+local function new_board(key, tool_name, source)
local board = {
key = key,
+ tool_name = tool_name,
+ source = source,
cards = {},
handle = nil,
collapsed = tools_collapsed,
@@ -298,7 +352,7 @@ function M.claim(event)
if type(event.set_component) ~= "function" then return end
if type(event.collapsed) == "boolean" then tools_collapsed = event.collapsed end
local key = event.id or event.tool_call_id or name
- local board = new_board(key)
+ local board = new_board(key, name, source_of(name, event.input))
local attached, handle = pcall(event.set_component, event, board.component)
if not attached then return end
board.handle = handle