From d1306506aa7f504b0e91c9c6ed7314afbf99978e Mon Sep 17 00:00:00 2001 From: t Date: Wed, 19 Aug 2026 19:02:30 -0600 Subject: Add background Lua workflows, inline child prompts, and layered config subagents.lua now starts a workflow on its own coroutine and returns a session-scoped id immediately, so fan-out continues while the primary keeps working; completion wakes the primary, and later calls read immutable records from subagents.workflows. Every ctx:agent takes a workflow-unique name so those records are addressable. A session_start guidance message tells the primary when to reach for run vs lua. Children no longer inherit the primary's system context: a child starts from the fixed child-role instruction plus its profile, and subagents.run/ctx:agent accept an inline system_prompt instead of a profile. The now-redundant `agent` form of subagents.models is gone. Concurrency defaults to five and is configurable through [subagents] max_concurrent in any layered config.toml; turn boundaries reap only settled jobs so background workflows survive, while interrupt and session end cancel. Config roots come from panto.ext.dirs.layers rather than a hand-rolled XDG lookup, which picks up the base and git-ignored local layers for both agents/ and workflows/. TOML workflows tighten up: the subagents.workflow tool takes a discovered name only (inline `steps` duplicated subagents.lua at less power), an optional top-level `output` array chooses the reported steps and their order instead of the terminal set, a step with no workflow input gets no empty input heading, and a workflow naming an undiscovered agent is rejected at discovery rather than part-way through a run. --- subagents/progress.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'subagents/progress.lua') diff --git a/subagents/progress.lua b/subagents/progress.lua index 03fe62a..d88e239 100644 --- a/subagents/progress.lua +++ b/subagents/progress.lua @@ -326,8 +326,9 @@ end function M.settle(event) local key = type(event) == "table" and (event.id or event.tool_call_id) or nil local board = key and boards[key] - if not board then return end - set_board_pinned(board, false) + if board then set_board_pinned(board, false) end + local co = coroutine.running() + if co and bound[co] == key then bound[co] = nil end end function M.collapse(event) @@ -347,6 +348,12 @@ function M.bind(context) if co then bound[co] = key end end +function M.bind_coroutine(co, tool_call_id) + if type(co) ~= "thread" then return end + if type(tool_call_id) ~= "string" or tool_call_id == "" then tool_call_id = nil end + bound[co] = tool_call_id +end + -- The outer tool call is durable child-turn metadata, not model-visible output. -- Expose it to the spawn seam without making the board state global: workflow -- callbacks run on the same coroutine as their owning tool handler. @@ -723,7 +730,8 @@ function M.reset() set_board_pinned(board, false) end prune_boards() - bound = setmetatable({}, { __mode = "k" }) + -- `bound` has weak coroutine keys. Foreground handlers clear themselves in + -- settle(); background workflow bindings must survive turn boundaries. end return M -- cgit v1.3