From 073ef556ba48be313714ed1a97694cfd7789d56d Mon Sep 17 00:00:00 2001 From: operator Date: Sat, 10 Oct 2026 10:55:23 -0400 Subject: [PATCH] feat(work): add box work read command to extract live active chat text and side chats --- bin/agent-cognitive-probe.py | 111 +++++++++++++++++++++++++++++++++++ bin/box-work.py | 17 ++++++ bin/super-cli.py | 4 ++ 3 files changed, 132 insertions(+) diff --git a/bin/agent-cognitive-probe.py b/bin/agent-cognitive-probe.py index c9ce637..14efa8b 100755 --- a/bin/agent-cognitive-probe.py +++ b/bin/agent-cognitive-probe.py @@ -343,6 +343,111 @@ def get_agent_menu(node, tab="all"): } +JS_LIVE_SCREEN = """(() => { + const ps = Array.from(document.querySelectorAll('p')).map(p => (p.innerText || '').trim()).filter(Boolean); + const actionBtns = Array.from(document.querySelectorAll('div[data-message-id] button, [role="log"] button, div[role="status"] button')).map(b => (b.innerText || '').trim()).filter(Boolean); + const stopBtn = !!document.querySelector('[data-testid="hatch-composer-stop-button"]'); + const typing = !!document.querySelector('[data-testid="hatch-chat-typing-indicator"]:not(:empty)'); + const statusTextEl = document.querySelector('.group\\\\/status-avatar span, [class*="status-avatar"] span, span[class*="text-body-status"]'); + const avatarStatus = statusTextEl ? (statusTextEl.innerText || '').trim() : ''; + + return { + title: document.title, + url: window.location.href, + is_main_chat: window.location.href === 'https://muse.ai/' || window.location.href.endsWith('/thread/new'), + is_generating: stopBtn, + is_typing: typing, + avatar_status: avatarStatus, + recent_paragraphs: ps.slice(-8), + action_buttons: actionBtns.filter(t => /approve|confirm|proceed|resume|start|allow|review/i.test(t)) + }; +})()""" + + +def get_agent_live_screen(node: str) -> dict: + """Extracts live active chat text, thoughts, prompt blocks, and threads.""" + if not is_running_on_bl(): + try: + cmd = ["ssh", "-q", "-o", "ConnectTimeout=5", f"super@{REMOTE_HOST}", + f"python3 /home/super/Projects/NetVM/bin/agent-cognitive-probe.py read {node} --json"] + proc = subprocess.run(cmd, capture_output=True, text=True, timeout=15) + if proc.returncode == 0 and proc.stdout.strip(): + return json.loads(proc.stdout.strip()) + except Exception as e: + return {"node": node, "error": f"Remote delegation failed: {e}"} + + screen = run_cdp_eval_inside_netns(node, JS_LIVE_SCREEN) + if not isinstance(screen, dict) or "error" in screen: + return {"node": node, "error": screen.get("error", "Failed to inspect screen") if isinstance(screen, dict) else str(screen)} + + sidechats = [] + try: + cli_cmd = ["/home/super/Projects/NetVM/bin/muse-cli-node", node, "threads"] + proc = subprocess.run(cli_cmd, capture_output=True, text=True, timeout=8) + if proc.returncode == 0 and proc.stdout.strip(): + threads_data = json.loads(proc.stdout.strip()) + if isinstance(threads_data, list): + sidechats = threads_data[:8] + except Exception: + pass + + return { + "node": node, + "screen": screen, + "sidechats": sidechats + } + + +def cmd_read(args): + node = getattr(args, "agent", None) or getattr(args, "node", None) + data = get_agent_live_screen(node) + if getattr(args, "json", False): + print(json.dumps(data, indent=2)) + return + + if "error" in data: + print(f"\nāŒ Error inspecting screen for {node}: {data['error']}\n") + return + + screen = data.get("screen", {}) + sidechats = data.get("sidechats", []) + + url = screen.get("url", "") + mode = "Main Chat" if screen.get("is_main_chat") else "Side Chat" + title = screen.get("title", "") + avatar = screen.get("avatar_status") or "Connected" + gen = "🧠 GENERATING" if screen.get("is_generating") else ("šŸ’­ TYPING" if screen.get("is_typing") else "🟢 SETTLED") + + print(f"\n=== LIVE THOUGHT STREAM & ACTIVE CHAT: {node.upper()} ===") + print(f"Context: {mode} ({url})") + print(f"Title: {title}") + print(f"Status: {avatar} | State: {gen}") + + paragraphs = screen.get("recent_paragraphs", []) + if paragraphs: + print("\n--- ACTIVE CONVERSATION & THOUGHT PARAGRAPHS ---") + for p in paragraphs: + print(f" • {p}\n") + else: + print("\n (No text paragraphs visible in current viewport)") + + actions = screen.get("action_buttons", []) + if actions: + print("--- PENDING ACTION CARDS / APPROVAL BUTTONS ---") + for a in actions: + print(f" āš ļø [PROMPT ACTION] {a}") + print() + + if sidechats: + print("--- RECENT SIDE CHATS & TOPICS ---") + for sc in sidechats: + sid = sc.get("session_id", "")[:8] + stitle = sc.get("title") or "(Untitled sidechat)" + upd = sc.get("updated", "") + print(f" • [{sid}] {stitle} ({upd})") + print() + + def format_cognitive_badge(status): badges = { "IDLE": "🟢 IDLE", @@ -464,6 +569,10 @@ def main(): p_lock.add_argument("--force", action="store_true", help="Bypass lock check") p_lock.add_argument("--json", action="store_true", help="Output JSON") + p_read = sub.add_parser("read", help="Extract live active chat text, thought stream, and side chats") + p_read.add_argument("node", help="Agent name") + p_read.add_argument("--json", action="store_true", help="Output JSON") + args = parser.parse_args() if not args.command: # Default to status @@ -476,6 +585,8 @@ def main(): cmd_status(args) elif args.command == "menu": cmd_menu(args) + elif args.command == "read": + cmd_read(args) elif args.command == "lock": cmd_lock(args) diff --git a/bin/box-work.py b/bin/box-work.py index 4043088..f490bb5 100755 --- a/bin/box-work.py +++ b/bin/box-work.py @@ -907,9 +907,16 @@ def cmd_loopback(args): if as_json: print(json.dumps({"ok": True, "actions": actions}, indent=2)) +def cmd_read(args): + if not acp: + print(c_red("Error: agent_cognitive_probe module not found.")) + sys.exit(1) + acp.cmd_read(args) + WORK_COMMAND_EXAMPLES = { "box work": [ "box work # View fleet workspace dashboard & signals", + "box work read # Extract live screen text, thoughts, and side chats", "box work loopback [--dry-run] # Sweep open tickets with 3-tier loopback escalation", "box work cognitive [agent...] # Live zero-click cognitive sensor probe across fleet", "box work menu [tab] # Inspect agent profile menu (tasks, timers, approvals)", @@ -920,6 +927,10 @@ WORK_COMMAND_EXAMPLES = { "box work merge # Verify tests and merge PR to master", "box work chats --agent # View live multi-agent chat feed", ], + "box work read": [ + "box work read pip # Read pip's current active screen & thought stream", + "box work read 646 # Read 646's active thread and side chats", + ], "box work loopback": [ "box work loopback # Sweep active tickets and escalate inactivity", "box work loopback --dry-run # Dry-run inspect without sending pings or comments", @@ -1081,6 +1092,10 @@ def main(): p_loop.add_argument("--dry-run", action="store_true", help="Inspect without modifying tickets or sending DMs") p_loop.add_argument("--json", action="store_true", help="Output actions as JSON") + p_read = sub.add_parser("read", help="Extract live screen text, active thoughts, and side chats for an agent") + p_read.add_argument("agent", help="Agent username (muse, pip, 646, opm, dev, def)") + p_read.add_argument("--json", action="store_true", help="Output JSON") + args = parser.parse_args() action = args.work_action @@ -1104,6 +1119,8 @@ def main(): cmd_cognitive(args) elif action == "loopback": cmd_loopback(args) + elif action == "read": + cmd_read(args) else: parser.print_help() diff --git a/bin/super-cli.py b/bin/super-cli.py index 0c9e9db..ef2a2a4 100755 --- a/bin/super-cli.py +++ b/bin/super-cli.py @@ -1495,6 +1495,8 @@ def cmd_work(args): box_work.cmd_cognitive(args) elif action == "loopback": box_work.cmd_loopback(args) + elif action == "read": + box_work.cmd_read(args) else: box_work.cmd_status(args) @@ -7018,6 +7020,8 @@ def build_parser(): p_w_cog.add_argument("agents", nargs="*", help="Optional agent usernames") p_w_loop = work_sub.add_parser("loopback", parents=[common], help="Sweep active tickets with 3-tier loopback escalation") p_w_loop.add_argument("--dry-run", action="store_true", help="Inspect without modifying tickets or sending DMs") + p_w_read = work_sub.add_parser("read", parents=[common], help="Extract live screen text, active thoughts, and side chats for an agent") + p_w_read.add_argument("agent", help="Agent username (muse, pip, 646, opm, dev, def)") p_tasks = subparsers.add_parser("tasks", parents=[common], help="Agent work queue: pending/claimed/done files (distinct from scheduled jobs)") p_tasks.add_argument("--dir", default=None, help="Task queue dir (default: fleet/tasks)")