Merge #4127 (run /yolo immediately on busy send path) onto master

This commit is contained in:
nesquena-hermes 2026-06-13 19:04:13 +00:00
commit 45b88b55b7
4 changed files with 46 additions and 8 deletions

View file

@ -910,13 +910,14 @@ async function send(){
if(!S.session){await newSession();await renderSessionList();}
// Busy-control slash commands must be intercepted HERE, before the
// busyMode routing block, so the user can always type /steer, /interrupt,
// or /queue while the agent is running and have them execute immediately.
// /queue, /terminal, /goal, or /yolo while the agent is running and have
// them execute immediately.
// Without this intercept they fall through to the queue and execute after
// the current turn ends — by which point there is no active stream and
// cmdSteer / cmdInterrupt say "No active task to stop."
if(text.startsWith('/')){
const _pc=typeof parseCommand==='function'&&parseCommand(text);
if(_pc&&['steer','interrupt','queue','terminal','goal'].includes(_pc.name)){
if(_pc&&['steer','interrupt','queue','terminal','goal','yolo'].includes(_pc.name)){
const _bc=COMMANDS.find(c=>c.name===_pc.name);
if(_bc){
$('msg').value='';autoResize();

View file

@ -284,7 +284,7 @@ class TestSendBusyBranchDispatch:
def test_slash_commands_intercepted_before_busymode_routing(self):
"""The three busy-control slash commands (/steer /interrupt /queue) must be
"""Busy-control slash commands (/steer /interrupt /queue /yolo) must be
intercepted at the TOP of the busy block — before the busyMode routing — so
they execute immediately while the agent is running.
@ -298,22 +298,26 @@ class TestSendBusyBranchDispatch:
busy_start = MESSAGES_JS.find("S.busy||compressionRunning", send_idx)
assert busy_start >= 0, "busy block not found"
# The intercept must appear BEFORE the busyMode assignment
intercept_idx = MESSAGES_JS.find("'steer','interrupt','queue'", busy_start)
intercept_idx = MESSAGES_JS.find("'steer','interrupt','queue','terminal','goal','yolo'", busy_start)
busymode_idx = MESSAGES_JS.find("_busyInputMode||'queue'", busy_start)
assert intercept_idx >= 0, (
"send() must intercept /steer /interrupt /queue before the busyMode "
"send() must intercept /steer /interrupt /queue /terminal /goal /yolo before the busyMode "
"routing block — otherwise they queue instead of executing immediately"
)
assert intercept_idx < busymode_idx, (
"The slash-command intercept must come BEFORE the busyMode routing "
"so /steer executes while the agent is running, not after the turn ends"
)
intercept_block = MESSAGES_JS[intercept_idx:busymode_idx]
assert "'yolo'" in intercept_block, (
"The busy-mode slash-command allowlist must include /yolo"
)
def test_steer_intercept_calls_handler_directly(self):
"""The busy-intercept must dispatch via _bc.fn(_pc.args), not queue the text."""
send_idx = MESSAGES_JS.find("async function send(")
busy_start = MESSAGES_JS.find("S.busy||compressionRunning", send_idx)
intercept_idx = MESSAGES_JS.find("'steer','interrupt','queue'", busy_start)
intercept_idx = MESSAGES_JS.find("'steer','interrupt','queue','terminal','goal','yolo'", busy_start)
assert intercept_idx >= 0
# Get the intercept block (up to the next busyMode assignment)
busymode_idx = MESSAGES_JS.find("_busyInputMode||'queue'", busy_start)
@ -335,7 +339,7 @@ class TestSendBusyBranchDispatch:
"""
send_idx = MESSAGES_JS.find("async function send(")
busy_start = MESSAGES_JS.find("S.busy||compressionRunning", send_idx)
intercept_idx = MESSAGES_JS.find("'steer','interrupt','queue'", busy_start)
intercept_idx = MESSAGES_JS.find("'steer','interrupt','queue','terminal','goal','yolo'", busy_start)
busymode_idx = MESSAGES_JS.find("_busyInputMode||'queue'", busy_start)
intercept_block = MESSAGES_JS[intercept_idx:busymode_idx]
clear_idx = intercept_block.find("$('msg').value=''")

View file

@ -408,7 +408,7 @@ def test_frontend_has_goal_slash_command_and_status_event_handler():
assert "goal'" in MESSAGES_JS
assert "source.addEventListener('goal'" in MESSAGES_JS
assert "source.addEventListener('goal_continue'" in MESSAGES_JS
assert "['steer','interrupt','queue','terminal','goal'].includes(_pc.name)" in MESSAGES_JS
assert "['steer','interrupt','queue','terminal','goal','yolo'].includes(_pc.name)" in MESSAGES_JS
assert "queueSessionMessage" in MESSAGES_JS

View file

@ -150,6 +150,39 @@ class TestYoloCommandRegistration:
assert "/api/session/yolo" in commands_js
class TestYoloBusySendPath:
"""/yolo should be recognized by the busy-send command intercept."""
@pytest.fixture(scope="class")
def messages_js(self):
with open("static/messages.js", "r") as f:
return f.read()
def test_yolo_in_busy_send_allowlist(self, messages_js):
send_idx = messages_js.find("async function send(")
assert send_idx >= 0, "send() not found in messages.js"
busy_start = messages_js.find("S.busy||compressionRunning", send_idx)
assert busy_start >= 0, "busy block not found in send()"
intercept_start = messages_js.find("if(text.startsWith('/')", busy_start)
assert intercept_start >= 0, "busy slash intercept block not found in send()"
intercept_idx = messages_js.find("'steer','interrupt','queue','terminal','goal','yolo'", intercept_start)
busymode_idx = messages_js.find("_busyInputMode||'queue'", busy_start)
assert intercept_idx >= 0, "Busy-path slash allowlist must include yolo in the mid-turn branch"
assert intercept_idx < busymode_idx, "Busy-path intercept must run before busyMode routing"
intercept_block = messages_js[intercept_idx:busymode_idx]
assert "_bc.fn(_pc.args)" in intercept_block, (
"Busy-path slash intercept should dispatch directly through the command handler"
)
assert "await _bc.fn" in intercept_block, (
"Busy-path slash intercept should await the command handler"
)
clear_idx = intercept_block.find("$('msg').value=''")
await_idx = intercept_block.find("await _bc.fn")
assert clear_idx >= 0, "Busy-path intercept should clear composer text before await"
assert clear_idx < await_idx, "Composer clear must happen before awaiting the handler"
class TestYoloPillHTML:
"""YOLO pill element should exist in index.html."""