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): def format_cognitive_badge(status):
badges = { badges = {
"IDLE": "🟢 IDLE", "IDLE": "🟢 IDLE",
@@ -464,6 +569,10 @@ def main():
p_lock.add_argument("--force", action="store_true", help="Bypass lock check") p_lock.add_argument("--force", action="store_true", help="Bypass lock check")
p_lock.add_argument("--json", action="store_true", help="Output JSON") 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() args = parser.parse_args()
if not args.command: if not args.command:
# Default to status # Default to status
@@ -476,6 +585,8 @@ def main():
cmd_status(args) cmd_status(args)
elif args.command == "menu": elif args.command == "menu":
cmd_menu(args) cmd_menu(args)
elif args.command == "read":
cmd_read(args)
elif args.command == "lock": elif args.command == "lock":
cmd_lock(args) cmd_lock(args)
+17
View File
@@ -907,9 +907,16 @@ def cmd_loopback(args):
if as_json: if as_json:
print(json.dumps({"ok": True, "actions": actions}, indent=2)) 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 = { WORK_COMMAND_EXAMPLES = {
"box work": [ "box work": [
"box work # View fleet workspace dashboard & signals", "box work # View fleet workspace dashboard & signals",
"box work read <agent> # Extract live screen text, thoughts, and side chats",
"box work loopback [--dry-run] # Sweep open tickets with 3-tier loopback escalation", "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 cognitive [agent...] # Live zero-click cognitive sensor probe across fleet",
"box work menu <agent> [tab] # Inspect agent profile menu (tasks, timers, approvals)", "box work menu <agent> [tab] # Inspect agent profile menu (tasks, timers, approvals)",
@@ -920,6 +927,10 @@ WORK_COMMAND_EXAMPLES = {
"box work merge <pr#> # Verify tests and merge PR to master", "box work merge <pr#> # Verify tests and merge PR to master",
"box work chats --agent <name> # View live multi-agent chat feed", "box work chats --agent <name> # 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": [
"box work loopback # Sweep active tickets and escalate inactivity", "box work loopback # Sweep active tickets and escalate inactivity",
"box work loopback --dry-run # Dry-run inspect without sending pings or comments", "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("--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_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() args = parser.parse_args()
action = args.work_action action = args.work_action
@@ -1104,6 +1119,8 @@ def main():
cmd_cognitive(args) cmd_cognitive(args)
elif action == "loopback": elif action == "loopback":
cmd_loopback(args) cmd_loopback(args)
elif action == "read":
cmd_read(args)
else: else:
parser.print_help() parser.print_help()
+4
View File
@@ -1495,6 +1495,8 @@ def cmd_work(args):
box_work.cmd_cognitive(args) box_work.cmd_cognitive(args)
elif action == "loopback": elif action == "loopback":
box_work.cmd_loopback(args) box_work.cmd_loopback(args)
elif action == "read":
box_work.cmd_read(args)
else: else:
box_work.cmd_status(args) 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_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 = 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_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 = 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)") p_tasks.add_argument("--dir", default=None, help="Task queue dir (default: fleet/tasks)")