From 997d80bc69a6363e8c65a987dace0e30d761522d Mon Sep 17 00:00:00 2001 From: t Date: Mon, 31 Aug 2026 10:17:11 -0600 Subject: Expose background workflow activity events Bracket each background workflow with lifecycle events and emit its primary wake before completion so integrations do not report a premature idle state. --- README.md | 6 ++++-- spec/test_luatool.lua | 4 +++- subagents/workflow.lua | 11 ++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e985c18..6841ed9 100644 --- a/README.md +++ b/README.md @@ -142,8 +142,10 @@ starts model-authored workflows in a restricted environment and returns a session-scoped workflow ID immediately. Work continues on Pantograph's event loop; completion wakes the primary, and later calls can inspect field-read-only records through `subagents.workflows[id]`, including named child status and -outputs. Every `ctx:agent` needs a workflow-unique `name`, and the callback must -return the workflow's string result. +outputs. While one is live the extension emits balanced `background_work_start` +and `background_work_end` events, allowing lifecycle integrations to remain +busy across the primary turn boundary. Every `ctx:agent` needs a workflow-unique +`name`, and the callback must return the workflow's string result. The same read-only workflow records are exposed to other extensions as `panto.ext.workflows`. Extensions can iterate this table or look up an ID; each diff --git a/spec/test_luatool.lua b/spec/test_luatool.lua index e7f2294..ee0a79e 100644 --- a/spec/test_luatool.lua +++ b/spec/test_luatool.lua @@ -112,7 +112,9 @@ return { assert(record.agents.review.status == "completed") assert(#handle.submissions == 1, "completion wakes the primary once") has(handle.submissions[1], id) - assert(handle.emitted[1] == "agent_submission", "the host pipeline is explicitly woken") + assert(table.concat(handle.emitted, ",") == + "background_work_start,agent_submission,background_work_end", + "background work brackets the primary wake") end) end }, diff --git a/subagents/workflow.lua b/subagents/workflow.lua index 671890e..ff534cb 100644 --- a/subagents/workflow.lua +++ b/subagents/workflow.lua @@ -748,6 +748,11 @@ local function make_workflow_record(id) return record end +local function emit(name) + local ext = host() + if type(ext.emit) == "function" then pcall(ext.emit, name) end +end + local function notify(record) if record.suppress_notification or record.status == "cancelled" then return end local primary = host().agent @@ -755,9 +760,7 @@ local function notify(record) local submitted = pcall(primary.submit, primary, string.format( "[subagents] Workflow %s %s. Inspect subagents.workflows[%q] with subagents.lua.", record.id, record.status, record.id)) - if submitted and type(host().emit) == "function" then - pcall(host().emit, "agent_submission") - end + if submitted then emit("agent_submission") end end local function finish_workflow(record, status, result, err) @@ -769,6 +772,7 @@ local function finish_workflow(record, status, result, err) active_workflows[record.id] = nil progress.workflow_finished(record.tool_call_id) notify(record) + emit("background_work_end") end -- Start a model-authored workflow on its own coroutine and return before the @@ -826,6 +830,7 @@ function M.start(wf, opts) end end) progress.workflow_started(record.tool_call_id) + emit("background_work_start") return id end -- cgit v1.3