diff --git a/static/messages.js b/static/messages.js index f090968d..5d2812bf 100644 --- a/static/messages.js +++ b/static/messages.js @@ -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(); diff --git a/tests/test_1062_busy_input_modes.py b/tests/test_1062_busy_input_modes.py index 47ede9ab..0a91ebb7 100644 --- a/tests/test_1062_busy_input_modes.py +++ b/tests/test_1062_busy_input_modes.py @@ -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=''") diff --git a/tests/test_goal_command_webui.py b/tests/test_goal_command_webui.py index 1fb96d08..4a70cbde 100644 --- a/tests/test_goal_command_webui.py +++ b/tests/test_goal_command_webui.py @@ -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 diff --git a/tests/test_issue467_yolo_mode_toggle.py b/tests/test_issue467_yolo_mode_toggle.py index c4def5d0..b55f3f67 100644 --- a/tests/test_issue467_yolo_mode_toggle.py +++ b/tests/test_issue467_yolo_mode_toggle.py @@ -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."""