repl: sub-broker delegation via DELEGATE: marker (closes #6)

Cost and context-window control: a "heavy" preset's model can offload
work to a cheaper preset without spending its own tokens on the result.
Example: deep model is mid-conversation and asks fast to summarize a
20k-line build log; the summary comes back as exec-output for the
next turn, deep stays small.

Marker syntax: DELEGATE: <preset> "<prompt>"

(Single or double quotes; one DELEGATE per line; lines without the
quoted shape are dropped — let the user write about delegation in
prose without accidental dispatch.)

Dispatch flow (mirrors CMD: / CMD&: extraction):
  1. ask_ai's stream completes
  2. extract_delegate_lines walks the final response
  3. For each {preset, prompt}: broker.chat(config.models[preset], ...)
     synchronously; result is appended via ctx:append_exec_output as
     "[delegate <preset>]: <result>"
  4. The model sees the delegate result on its next turn

Implementation choice — marker over tool: option 1 from the issue
("inline delegate marker") works with any model regardless of
tool_calls support. Option 2 (aish_delegate as a tool dispatched in
the existing Phase 2 sub-loop) is the better UX for capable models
since it returns the result mid-turn — filed as follow-up if needed.

Meta surface:
  :delegate <preset> <prompt>   one-shot direct invocation (useful for
                                testing without depending on the model
                                emitting DELEGATE:, and as a manual
                                "ask <preset> something" verb)

Scope:
  - Plan mode: emits "PLAN: DELEGATE <preset> <prompt>" without dispatch
  - Norris: not extended; the planner's model anchor would conflict with
    mid-plan switching (R-C3-adjacent risk)
  - No self-delegation guard: each DELEGATE is a separate broker call,
    not recursive; a delegate result reaching the next turn could
    contain another DELEGATE but that's bounded by max_tool_depth-style
    iteration cap on the parent
  - No cost prompt: configuring a paid cloud preset already implies
    consent to spend on it
  - Unknown preset → error status + exec-output note "[delegate X failed:
    unknown preset]"

Extractor unit-tested with 8 cases (single-quote, double-quote, multi-
line prose, empty prompt, no-quotes, case-sensitive, wrong prefix).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 21:29:09 +00:00
parent f94d16fc89
commit cdf4e86679
2 changed files with 74 additions and 0 deletions
+19
View File
@@ -147,4 +147,23 @@ function M.extract_cmd_bg_lines(text)
return cmds
end
-- Issue #6: `DELEGATE: <preset> "<prompt>"` lines. Parses each into
-- (preset, prompt) — quotes around the prompt are required so the
-- parser can find the boundary unambiguously (the prompt may contain
-- arbitrary punctuation otherwise). Lines that don't match the
-- quoted shape are silently dropped (rendered as text to the user).
function M.extract_delegate_lines(text)
local out = {}
for line in (text or ""):gmatch("[^\n]+") do
local preset, prompt = line:match([[^DELEGATE: (%S+)%s+"(.+)"%s*$]])
if not preset then
preset, prompt = line:match([[^DELEGATE: (%S+)%s+'(.+)'%s*$]])
end
if preset and prompt and prompt:match("%S") then
out[#out + 1] = { preset = preset, prompt = prompt }
end
end
return out
end
return M