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/models.lua | 56 +++------------------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) (limited to 'subagents/models.lua') diff --git a/subagents/models.lua b/subagents/models.lua index 511aa51..7272298 100644 --- a/subagents/models.lua +++ b/subagents/models.lua @@ -1,17 +1,12 @@ -- The `subagents.models` tool: a bounded window onto the model catalog. -- -- The catalog is far too large to inline into `subagents.run`'s description or --- schema, so it is queried on demand instead. Four forms, chosen in this order: +-- schema, so it is queried on demand instead. Three forms: -- --- { agent = "reviewer" } what that profile will actually run on -- { model = "anthropic:sonnet"} exact lookup: wire name, reasoning levels -- { provider =, query =, limit=} bounded search -- { } inherited model/reasoning + provider counts -- --- The agent form is the join the primary actually wants before overriding a --- profile: it reports the profile's own model, or says the profile inherits --- the primary model and shows what that is. --- -- Results are short readable lines, not JSON. A truncated search says so -- explicitly so the primary refines the query instead of assuming it saw -- everything; `limit` is clamped to 1..50 (default 10) so no query can dump @@ -22,8 +17,6 @@ -- tool cannot confirm is still validated at spawn time — the catalog is -- advice, the runtime is authoritative. -local spawn = require("subagents.spawn") - local DEFAULT_LIMIT = 10 local MAX_LIMIT = 50 @@ -143,52 +136,13 @@ local function clamp_limit(value) return limit end --- The agent form: report the profile's effective model, or say it inherits. -local function describe_agent(name, profiles) - profiles = spawn.profiles(profiles) - local profile = (profiles.by_name or {})[name] - if not profile then - return string.format("Error: unknown agent '%s'; known: %s", name, spawn.agent_names(profiles)) - end - - local out = { "agent: " .. profile.name } - if profile.description ~= "" then - out[#out + 1] = "description: " .. profile.description - end - - if profile.model then - local result, err = ask({ model = profile.model }) - if not result then - return "Error: " .. err - end - out[#out + 1] = format_exact(result, profile.model) - else - out[#out + 1] = "model: inherits the primary model" - local result, err = ask({}) - if not result then - return "Error: " .. err - end - out[#out + 1] = format_overview(result) - end - - if profile.reasoning then - out[#out + 1] = "profile reasoning: " .. profile.reasoning - end - return table.concat(out, "\n") -end - -function M.handle(input, profiles) +function M.handle(input) input = input or {} if type(input) ~= "table" then return "Error: expected a table of arguments." end - local agent, err = optional_string(input.agent, "agent") - if err then - return err - end - local model - model, err = optional_string(input.model, "model") + local model, err = optional_string(input.model, "model") if err then return err end @@ -203,10 +157,6 @@ function M.handle(input, profiles) return err end - if agent then - return describe_agent(agent, profiles) - end - if model then local result, ask_err = ask({ model = model }) if not result then -- cgit v1.3