diff --git a/prompts/default/fw.code.pause_dialog.md b/prompts/default/fw.code.pause_dialog.md new file mode 100644 index 000000000..07291dcf8 --- /dev/null +++ b/prompts/default/fw.code.pause_dialog.md @@ -0,0 +1 @@ +Potential dialog detected in output. Returning control to agent after {{timeout}} seconds since last output update. Decide whether dialog actually occurred and needs to be addressed, or if it was just a false positive and wait for more output. \ No newline at end of file diff --git a/python/tools/code_execution_tool.py b/python/tools/code_execution_tool.py index 33e297180..d89573550 100644 --- a/python/tools/code_execution_tool.py +++ b/python/tools/code_execution_tool.py @@ -191,7 +191,8 @@ class CodeExecution(Tool): reset_full_output=True, first_output_timeout=30, # Wait up to x seconds for first output between_output_timeout=15, # Wait up to x seconds between outputs - max_exec_timeout=180, #hard cap on total runtime + dialog_timeout=5, # potential dialog detection timeout + max_exec_timeout=180, # hard cap on total runtime sleep_time=0.1, ): # Common shell prompt regex patterns (add more as needed) @@ -201,6 +202,14 @@ class CodeExecution(Tool): re.compile(r"[a-zA-Z0-9_.-]+@[^:]+:[^$#]+[$#] ?$"), # user@host:~$ ] + # potential dialog detection + dialog_patterns = [ + re.compile(r"Y/N", re.IGNORECASE), # Y/N anywhere in line + re.compile(r"yes/no", re.IGNORECASE), # yes/no anywhere in line + re.compile(r":\s*$"), # line ending with colon + re.compile(r"\?\s*$"), # line ending with question mark + ] + start_time = time.time() last_output_time = start_time full_output = "" @@ -228,7 +237,9 @@ class CodeExecution(Tool): got_output = True # Check for shell prompt at the end of output - last_lines = truncated_output.splitlines()[-3:] if truncated_output else [] + last_lines = ( + truncated_output.splitlines()[-3:] if truncated_output else [] + ) for line in last_lines: for pat in prompt_patterns: if pat.search(line.strip()): @@ -272,6 +283,31 @@ class CodeExecution(Tool): self.log.update(content=response) return response + # potential dialog detection + if now - last_output_time > dialog_timeout: + # Check for dialog prompt at the end of output + last_lines = ( + truncated_output.splitlines()[-2:] if truncated_output else [] + ) + for line in last_lines: + for pat in dialog_patterns: + if pat.search(line.strip()): + PrintStyle.info( + "Detected dialog prompt, returning output early." + ) + + sysinfo = self.agent.read_prompt( + "fw.code.pause_dialog.md", timeout=dialog_timeout + ) + response = self.agent.read_prompt( + "fw.code.info.md", info=sysinfo + ) + if truncated_output: + response = truncated_output + "\n\n" + response + PrintStyle.warning(sysinfo) + self.log.update(content=response) + return response + async def reset_terminal(self, session=0, reason: str | None = None): # Print the reason for the reset to the console if provided if reason: diff --git a/python/tools/input.py b/python/tools/input.py index d7a634cbf..85b55f567 100644 --- a/python/tools/input.py +++ b/python/tools/input.py @@ -8,7 +8,7 @@ class Input(Tool): async def execute(self, keyboard="", **kwargs): # normalize keyboard input keyboard = keyboard.rstrip() - keyboard += "\n" + # keyboard += "\n" # no need to, code_exec does that # terminal session number session = int(self.args.get("session", 0))