shell improvements

frdel committed Jul 4, 2025 at 22:33 UTC 07e3e59e6f969391c824c6202f0c0e33d36ae704
4 files changed +19 -87
python/helpers/files.py
-1
@@ -12,7 +12,6 @@ import zipfile
12 import importlib
13 import importlib.util
14 import inspect
15 -from python.helpers.print_style import PrintStyle
15
16
17 class VariablesPlugin(ABC):
python/helpers/shell_ssh.py
+4 -1
@@ -41,7 +41,7 @@ class SSHInteractiveSession:
41 allow_agent=False,
42 look_for_keys=False,
43 )
44 - self.shell = self.client.invoke_shell(width=80, height=40)
44 + self.shell = self.client.invoke_shell(width=100, height=50)
45 # self.shell.send(f'PS1="{SSHInteractiveSession.ps1_label}"'.encode())
46 # return
47 while True: # wait for end of initial output
@@ -193,6 +193,9 @@ class SSHInteractiveSession:
193 ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
194 cleaned = ansi_escape.sub("", input_string)
195
196 + # remove null bytes
197 + cleaned = cleaned.replace("\x00", "")
198 +
199 # Replace '\r\n' with '\n'
200 cleaned = cleaned.replace("\r\n", "\n")
201
python/tools/code_execution_tool.py
+15 -11
@@ -73,7 +73,9 @@ class CodeExecution(Tool):
73 if not text:
74 text = f"{self.name} - {self.args['runtime']}"
75 text = truncate_text_string(text, 60)
76 - return f"icon://terminal {text}"
76 + session = self.args.get("session", None)
77 + session_text = f"[{session}] " if session or session == 0 else ""
78 + return f"icon://terminal {session_text}{text}"
79
80 async def after_execution(self, response, **kwargs):
81 self.agent.hist_add_tool_result(self.name, response.message)
@@ -135,22 +137,24 @@ class CodeExecution(Tool):
137 async def execute_python_code(self, session: int, code: str, reset: bool = False):
138 escaped_code = shlex.quote(code)
139 command = f"ipython -c {escaped_code}"
138 - prefix = "python> "+self.format_command_for_output(code)+"\n\n"
140 + prefix = "python> " + self.format_command_for_output(code) + "\n\n"
141 return await self.terminal_session(session, command, reset, prefix)
142
143 async def execute_nodejs_code(self, session: int, code: str, reset: bool = False):
144 escaped_code = shlex.quote(code)
143 - command = f"node /exe/node_eval.js {escaped_code}"
144 - prefix = "node> "+self.format_command_for_output(code)+"\n\n"
145 + command = f"node /exe/node_eval.js {escaped_code}"
146 + prefix = "node> " + self.format_command_for_output(code) + "\n\n"
147 return await self.terminal_session(session, command, reset, prefix)
148
149 async def execute_terminal_command(
150 self, session: int, command: str, reset: bool = False
151 ):
150 - prefix = "bash> "+self.format_command_for_output(command)+"\n\n"
152 + prefix = "bash> " + self.format_command_for_output(command) + "\n\n"
153 return await self.terminal_session(session, command, reset, prefix)
154
153 - async def terminal_session(self, session: int, command: str, reset: bool = False, prefix: str = ""):
155 + async def terminal_session(
156 + self, session: int, command: str, reset: bool = False, prefix: str = ""
157 + ):
158
159 await self.agent.handle_intervention() # wait for intervention and handle it, if paused
160 # try again on lost connection
@@ -205,8 +209,6 @@ class CodeExecution(Tool):
209 # final length
210 short_cmd = truncate_text_string(short_cmd, 100)
211 return f"{short_cmd}"
208 -
209 -
212
213 async def get_terminal_output(
214 self,
@@ -217,7 +219,7 @@ class CodeExecution(Tool):
219 dialog_timeout=5, # potential dialog detection timeout
220 max_exec_timeout=180, # hard cap on total runtime
221 sleep_time=0.1,
220 - prefix=""
222 + prefix="",
223 ):
224 # Common shell prompt regex patterns (add more as needed)
225 prompt_patterns = [
@@ -343,7 +345,9 @@ class CodeExecution(Tool):
345 heading = self.get_heading_from_output(
346 truncated_output, 0
347 )
346 - self.log.update(content=prefix + response, heading=heading)
348 + self.log.update(
349 + content=prefix + response, heading=heading
350 + )
351 return response
352
353 async def reset_terminal(self, session=0, reason: str | None = None):
@@ -384,7 +388,7 @@ class CodeExecution(Tool):
388
389 def fix_full_output(self, output: str):
390 # remove any single byte \xXX escapes
387 - output = re.sub(r'(?<!\\)\\x[0-9A-Fa-f]{2}', '', output)
391 + output = re.sub(r"(?<!\\)\\x[0-9A-Fa-f]{2}", "", output)
392 # Strip every line of output before truncation
393 output = "\n".join(line.strip() for line in output.splitlines())
394 output = truncate_text_agent(agent=self.agent, output=output, threshold=10000)
test.py deleted
-74
@@ -1,74 +0,0 @@
1 -import asyncio
2 -from os import sep
3 -from langchain_core.messages import HumanMessage, SystemMessage
4 -from langchain_core.prompts import ChatPromptTemplate
5 -import models
6 -from python.helpers import dotenv
7 -
8 -
9 -async def test():
10 -
11 - dotenv.load_dotenv()
12 -
13 - # model_name = "moonshotai/kimi-dev-72b:free"
14 - # model_name = "qwen/qwq-32b"
15 - # model_name = "qwen/qwen3-32b"
16 - # model_name = "anthropic/claude-3.7-sonnet:thinking"
17 - model_name = "openai/gpt-4.1-nano"
18 - system = ""
19 - message = "hello"
20 -
21 - model = models.get_chat_model(models.ModelProvider.OPENROUTER, model_name)
22 -
23 - async def response_callback(chunk: str, full: str):
24 - if chunk == full:
25 - print("\n")
26 - print("Response:")
27 - print(chunk, end="", flush=True)
28 -
29 - async def reasoning_callback(chunk: str, full: str):
30 - if chunk == full:
31 - print("\n")
32 - print("Reasoning:")
33 - print(chunk, end="", flush=True)
34 -
35 - response, reasoning = await model.unified_call(
36 - system_message=system,
37 - user_message=message,
38 - response_callback=response_callback,
39 - reasoning_callback=reasoning_callback,
40 - )
41 -
42 - print("\n")
43 - print("Final:")
44 - print("Reasoning:", reasoning)
45 - print("Response:", response)
46 -
47 -
48 -async def test2():
49 -
50 - dotenv.load_dotenv()
51 -
52 - import initialize
53 - config = initialize.initialize_agent()
54 -
55 - model = models.get_browser_model(
56 - provider=config.browser_model.provider,
57 - name=config.browser_model.name,
58 - **config.browser_model.kwargs,
59 - )
60 -
61 - response, reasoning = await model.unified_call(
62 - system_message="",
63 - user_message="hi",
64 - )
65 -
66 - print("\n")
67 - print("Final:")
68 - print("Reasoning:", reasoning)
69 - print("Response:", response)
70 -
71 -
72 -if __name__ == "__main__":
73 - # asyncio.run(test())
74 - asyncio.run(test2())
\ No newline at end of file