diff --git a/agent-node/src/agent_node/skills/shell_bridge.py b/agent-node/src/agent_node/skills/shell_bridge.py
index cdf72e0..0879cde 100644
--- a/agent-node/src/agent_node/skills/shell_bridge.py
+++ b/agent-node/src/agent_node/skills/shell_bridge.py
@@ -166,6 +166,13 @@
print(f" [🐚⚠️] Protocol parsing failed: {e}")
if sess.get("event"): sess["event"].set()
+ # Matches Windows ConHost title-set sequences: "0;...
..." lines
+ _WIN_TITLE_RE = re.compile(r'^\s*0;.*$', re.MULTILINE)
+ # Matches the 'call "...bat"' invocation line echoed by cmd
+ _BAT_CALL_RE = re.compile(r'^\s*call\s+".+\.bat".*$', re.MULTILINE | re.IGNORECASE)
+ # Matches the 'del /Q ...' cleanup echo
+ _DEL_RE = re.compile(r'^\s*del\s+/Q\s+".+\.bat".*$', re.MULTILINE | re.IGNORECASE)
+
def _handle_ui_streaming(self, sess, session_id, active_tid, decoded, on_event):
"""Internal helper to filter plumbing and stream terminal output to the client."""
# Clean framing echoes from the live stream
@@ -174,9 +181,15 @@
decoded = STRIP_START_FENCE.sub('', decoded)
decoded = STRIP_BRACKET_FENCE.sub('', decoded)
- # Line-Aware Stealthing for extra safety
- lines = decoded.splitlines(keepends=True)
- clean_lines = [line for line in lines if not PROTOCOL_HINT_PATTERN.search(ANSI_ESCAPE.sub('', line))]
+ # Strip Windows internals: title-set escape sequences, bat call/del lines
+ clean_no_ansi = ANSI_ESCAPE.sub('', decoded)
+ clean_no_ansi = self._WIN_TITLE_RE.sub('', clean_no_ansi)
+ clean_no_ansi = self._BAT_CALL_RE.sub('', clean_no_ansi)
+ clean_no_ansi = self._DEL_RE.sub('', clean_no_ansi)
+
+ # Line-Aware Stealthing for extra safety (protocol fence markers)
+ lines = clean_no_ansi.splitlines(keepends=True)
+ clean_lines = [line for line in lines if not PROTOCOL_HINT_PATTERN.search(line)]
stealth_out = "".join(clean_lines)
if stealth_out.strip():
@@ -270,7 +283,9 @@
with self.lock:
sess["active_task"] = tid
sess["event"] = event
- sess["buffer_file"] = tempfile.NamedTemporaryFile("w+", encoding="utf-8", prefix=f"cortex_task_{tid}_", delete=False)
+ _tmp = tempfile.NamedTemporaryFile("w+", encoding="utf-8", prefix=f"cortex_task_{tid}_", delete=False, suffix=".tmp")
+ sess["buffer_file"] = _tmp
+ sess["buffer_file_path"] = _tmp.name
sess["tail_buffer"] = ""
sess["result"] = result_container
sess["cancel_event"] = cancel_event
@@ -304,16 +319,16 @@
os.makedirs(spool_dir, exist_ok=True)
task_path = os.path.join(spool_dir, f"{tid}.bat")
- # Use a robust wrapper that ensures the TaskEnd fence is ALWAYS printed
+ # Write bat with @echo off to suppress internal echo noise
with open(task_path, "w", encoding="utf-8") as f:
f.write(f"@echo off\r\n"
f"echo [[1337;TaskStart;id={tid}]]\r\n"
- f"rem Execute command and capture exit code\r\n"
- f"cmd /c \"{cmd}\"\r\n"
+ f"{cmd}\r\n"
f"set __ctx_err=%errorlevel%\r\n"
f"echo [[1337;TaskEnd;id={tid};exit=%__ctx_err%]]\r\n"
f"exit /b %__ctx_err%\r\n")
- return f"call \"{task_path}\"\r\n"
+ # Store bat path on sess so we can delete it after task
+ return f"call \"{task_path}\" & del /Q \"{task_path}\"\r\n"
else:
return f"echo -e -n \"\\033]1337;TaskStart;id={tid}\\007\"; {cmd}; __ctx_exit=$?; echo -e -n \"\\033]1337;TaskEnd;id={tid};exit=$__ctx_exit\\007\"\n"
@@ -338,9 +353,16 @@
with self.lock:
if sess.get("active_task") == tid:
if sess.get("buffer_file"):
- try: sess["buffer_file"].close()
+ try:
+ sess["buffer_file"].close()
except: pass
+ # Delete the temp buffer file
+ tmp_path = sess.get("buffer_file_path")
+ if tmp_path:
+ try: os.remove(tmp_path)
+ except: pass
sess["buffer_file"] = None
+ sess["buffer_file_path"] = None
sess["active_task"] = None
if sess.get("event") == event: sess["event"] = None
if sess.get("cancel_event") == cancel_event: sess["cancel_event"] = None
diff --git a/scripts/start_agent.bat b/scripts/start_agent.bat
new file mode 100644
index 0000000..05d84ad
--- /dev/null
+++ b/scripts/start_agent.bat
@@ -0,0 +1,4 @@
+@echo off
+cd /d C:\Users\axiey\.cortex\agent-node
+set PYTHONPATH=src
+python src\agent_node\main.py > out.log 2>&1