Add self_main_loop.py main-chat self-monitor + box main-loop action

Reads each fleet agent's muse.ai Main chat on a 5-min systemd timer
(self-main-loop.timer). On new messages since the per-agent watermark,
posts a concise digest to that agent's prompting sidechat (dm.py, opm
as neutral sender - mirrors box notify), prompting the operator to
check main chat via DM/box. Escapes the main-chat-goes-unread failure
mode. No backfill on first run; silent when nothing new; read/send
failures logged without killing the timer; flock overlap guard.

box-ctl.py: main-loop check | status (fleet section).

Session: sidechat/chromebox-ops
This commit is contained in:
operator-main
2026-10-04 18:39:46 +00:00
parent f679ced960
commit e93e9b6122
2 changed files with 318 additions and 0 deletions
+29
View File
@@ -650,6 +650,30 @@ def act_notify(agent, message, sidechat=None, allow_main_chat=False):
out(True, agent=agent, target=target, sent=True)
def act_main_loop(sub):
"""Run the main-chat self-monitor loop (self_main_loop.py).
check: one loop iteration -> JSON {ok, prompted, errors, new_total, agents}
status: watermark / last run / prompt-sidechat config, no chat reads.
"""
if sub not in ("check", "status"):
fail("BAD_NAME", "usage: main-loop check|status")
audit("main-loop-" + sub)
script = BIN / "self_main_loop.py"
try:
r = subprocess.run([sys.executable, str(script), sub],
capture_output=True, text=True, timeout=600)
except subprocess.TimeoutExpired:
fail("LOOP_TIMEOUT", "self_main_loop.py timed out")
try:
data = json.loads(r.stdout.strip())
except Exception:
fail("LOOP_ERROR", "self_main_loop.py returned non-JSON",
{"stdout": (r.stdout or "")[-300:], "stderr": (r.stderr or "")[-300:]})
ok = bool(data.pop("ok", False))
out(ok, **data)
def act_watchdog_alerts():
"""Check for new failed browser relaunches since the watermark.
@@ -1147,6 +1171,7 @@ fleet:
relay-health
cdp-latency
identity-audit VM identity audit drift check
main-loop check|status main-chat self-monitor: prompt sidechat on new main msgs
timer actions:
timer-list
@@ -1389,6 +1414,10 @@ def main(argv):
if rest:
fail("BAD_ARGS", "usage: chrome-errors")
act_chrome_errors()
elif action == "main-loop":
if len(rest) != 1 or rest[0] not in ("check", "status"):
fail("BAD_NAME", "usage: main-loop check|status")
act_main_loop(rest[0])
elif action == "dm-log":
limit = 50
if rest: