eb10803691 tightened the reply-run empty-turn gate to only count
baseBodyFinal (strict user body) and to always append the '[User sent
media without caption]' placeholder to any prefix. That broke the Control
UI webchat path: images arrive via opts.images and do not stamp
sessionCtx.MediaPath (by design — see chat.directive-tags.test.ts
assertion that ctx.MediaPath stays undefined on dispatch). For pure-image
webchat turns the gate therefore returned 'I didn't receive any text in
your message', and when a caption was present the placeholder text leaked
into the Control UI user bubble on top of the inbound-context prefix.
Revert the three get-reply-run.ts hunks from eb10803691 back to the stable
2026.4.5 behavior: check baseBodyForPrompt.trim() (which includes the
inbound-context prefix) for the empty-turn gate, and fall back to the
plain '[User sent media without caption]' placeholder only when the whole
prompt body is empty.
Drop the media-only test the same commit added for metadata-only-prefix
bail-out; it encoded the exact behavior this reverts.
Fixes#69358.
Refs #69427.
@clawdbot/lobster/core returns both resumeToken and approvalId when a
workflow step needs approval, but the lobster plugin was dropping
approvalId in three places: normalizeEnvelope, the tool schema, and the
embedded-runner resume branch.
Agents forced to round-trip the ~155-byte base64url resumeToken across
tool calls are one stray truncation away from "Invalid token". The
8-hex approvalId is a disk-indexed alias (~/.lobster/state/approval_*
.json) — stable and escape-safe.
Changes are additive: token-based resume keeps working unchanged,
callers just gain an approvalId path.
Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.
Closes#60665.
Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>