1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
-- The `subagents.run` tool: start one child agent, or continue one, and wait.
--
-- One call handles all cases. `agent` starts a new child from that profile;
-- `system_prompt` starts one without a saved profile; `id` continues a child
-- this primary session started earlier. Exactly one is required, and `prompt`
-- is always required — a child cannot see the parent dialogue, so the prompt
-- is the only task context it gets.
--
-- The call blocks until the child settles. Parallelism is ordinary tool
-- batching: several subagents.run calls emitted in one batch run concurrently
-- under the session-wide bound in subagents/jobs.lua, and one failure does not
-- disturb its siblings.
--
-- Failures are values, not exceptions. Everything the model could plausibly
-- have caused — a missing prompt, both selectors at once, an unknown profile,
-- an unresumable id, a child that errored or was cancelled — comes back as
-- readable text. Validation that fails before a child is allocated has
-- no id to report, and a new child that dies before its first assistant
-- message has no durable file, so it reports `resumable: false` rather than
-- promising a continuation that would not resolve.
--
-- The result block is plain `key: value` lines rather than JSON: it is read by
-- a model, and the field names match the design's result shape (id, agent,
-- status, resumable, then the output or the error message).
local jobs = require("subagents.jobs")
local spawn = require("subagents.spawn")
local M = {}
-- A resumed child has no profile in hand; its identity comes back from the
-- manifest metadata stored on its first profile system message.
local function manifest_agent(result)
local manifest = result.manifest
if type(manifest) ~= "table" then
return nil
end
local mine = manifest.subagents
if type(mine) ~= "table" or type(mine.agent) ~= "string" then
return nil
end
return mine.agent
end
-- format_result(result, agent_name) -> the model-visible block.
function M.format_result(result, agent_name)
local body = result.output
if body == nil or body == "" then
body = result.error or ""
end
return table.concat({
"id: " .. (result.id or "(none)"),
"agent: " .. (agent_name or manifest_agent(result) or "?"),
"status: " .. (result.status or "unknown"),
"resumable: " .. tostring(result.resumable == true),
"--- output ---",
tostring(body),
}, "\n")
end
function M.handle(input, profiles)
local spec, err = spawn.build_spec(input, profiles)
if not spec then
return "Error: " .. tostring(err)
end
local handle, spawn_err = spawn.spawn(spec)
if not handle then
return "Error: " .. tostring(spawn_err)
end
local results = jobs.await({ handle }, "all")
local result = results and results[1]
if type(result) ~= "table" then
return "Error: the subagent produced no result."
end
return M.format_result(result, spec.label)
end
return M
|