From 49a6001dde72622df9410b738322e9a23e68ddee Mon Sep 17 00:00:00 2001 From: t Date: Fri, 21 Aug 2026 11:26:48 -0600 Subject: Add workflow cancellation controls --- subagents/workflow.lua | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'subagents/workflow.lua') diff --git a/subagents/workflow.lua b/subagents/workflow.lua index 375285f..97dd858 100644 --- a/subagents/workflow.lua +++ b/subagents/workflow.lua @@ -58,8 +58,8 @@ local ok_uv, uv = pcall(require, "luv") local M = {} --- Session-scoped background workflows. The model sees only immutable proxies; --- mutable state and live handles stay private in this module. +-- Session-scoped background workflows. The model sees field-read-only proxies +-- with cancellation closures; mutable state and live handles stay private. local workflow_sequence = 0 local workflow_records = {} local active_workflows = {} @@ -389,6 +389,13 @@ ctx_mt.__name = "subagents.ctx" -- exposing only `agent` and `await`. Weak keys so a finished run is collectable. local state = setmetatable({}, { __mode = "k" }) +local function cancel_agent_record(record) + if record.status ~= "running" or record.job == nil then + return false + end + return jobs().cancel(record.job) +end + local function make_agent_record(workflow_record, name) local record = { name = name, @@ -400,6 +407,8 @@ local function make_agent_record(workflow_record, name) record.proxy = readonly(function(_, key) if key == "name" or key == "status" or key == "output" or key == "error" or key == "id" then return record[key] + elseif key == "cancel" then + return function() return cancel_agent_record(record) end end end) workflow_record.agent_order[#workflow_record.agent_order + 1] = record @@ -412,6 +421,7 @@ local function settle_agent_record(record, result) record.status = result.status or "failed" record.id = result.id record.error = result.error + record.job = nil if result.output ~= nil then record.output = M.output_text(result) end @@ -493,6 +503,7 @@ function ctx_mt:agent(input) }) else job_of[handle] = job + if agent_record then agent_record.job = job end end s.job_count = s.job_count + 1 @@ -689,6 +700,17 @@ function M.output_text(result) return tostring(output) end +local function cancel_workflow_record(record) + if record.status ~= "running" then + return false + end + record.cancel_requested = true + for _, agent in ipairs(record.agent_order) do + cancel_agent_record(agent) + end + return true +end + local function make_workflow_record(id) local record = { id = id, @@ -719,6 +741,8 @@ local function make_workflow_record(id) return record[key] elseif key == "agents" then return record.agents_proxy + elseif key == "cancel" then + return function() return cancel_workflow_record(record) end end end) return record @@ -804,8 +828,8 @@ end function M.cancel_all(suppress_notification) for _, record in pairs(active_workflows) do - record.cancel_requested = true if suppress_notification then record.suppress_notification = true end + cancel_workflow_record(record) end end -- cgit v1.3