feat(work): add box work read command to extract live active chat text and side chats

This commit is contained in:
operator
2026-10-10 10:55:23 -04:00
parent 1b44d224a3
commit 073ef556ba
3 changed files with 132 additions and 0 deletions
+111
View File
@@ -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)