feat(hybrid-gateway): integrate muse-cli with Cloudflare netns isolation, symmetric sidechat routing, and 646-pip sync unblock
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Side-chat to main-chat work siphon — monitor loop.
|
||||
|
||||
Polls side chats for new messages, runs detection, siphons hits to main.
|
||||
|
||||
This is the integration point for bl. In production:
|
||||
- list_sidechats() calls muse-chat-api.py or the sidechat manager
|
||||
- get_messages() reads thread messages via CDP
|
||||
- post_to_main() sends via muse-chat-api.py send to main chat
|
||||
|
||||
For the prototype, all three are injectable (see tests).
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Callable, Dict, List
|
||||
|
||||
from detect import detect, is_opted_out
|
||||
from siphon import siphon, RateLimiter
|
||||
|
||||
|
||||
# Message shape: {"id": str, "text": str, "author": str, "ts": str}
|
||||
Message = Dict[str, str]
|
||||
|
||||
|
||||
def monitor_once(
|
||||
list_sidechats: Callable[[], List[Dict[str, str]]],
|
||||
get_messages: Callable[[str, str], List[Message]],
|
||||
post_to_main: Callable[[str], bool],
|
||||
watermarks: Dict[str, str],
|
||||
limiter: RateLimiter = None,
|
||||
min_confidence: float = 0.6,
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
One poll cycle. Returns updated watermarks.
|
||||
|
||||
list_sidechats: () -> [{"id": thread_id, "name": str, "agent": str}]
|
||||
get_messages: (thread_id, since_msg_id) -> [messages newer than watermark]
|
||||
post_to_main: (text) -> True on success
|
||||
watermarks: {thread_id: last_seen_message_id}
|
||||
"""
|
||||
lim = limiter or RateLimiter()
|
||||
new_marks = dict(watermarks)
|
||||
|
||||
for chat in list_sidechats():
|
||||
tid = chat["id"]
|
||||
agent = chat.get("agent", "unknown")
|
||||
|
||||
if is_opted_out(tid):
|
||||
continue
|
||||
|
||||
since = watermarks.get(tid, "")
|
||||
try:
|
||||
messages = get_messages(tid, since)
|
||||
except Exception:
|
||||
continue # don't let one bad chat kill the cycle
|
||||
|
||||
for msg in messages:
|
||||
mid = msg.get("id", "")
|
||||
text = msg.get("text", "")
|
||||
if not mid or not text:
|
||||
continue
|
||||
|
||||
# Update watermark to newest seen
|
||||
new_marks[tid] = mid
|
||||
|
||||
hit = detect(text, tid, mid, min_confidence)
|
||||
if hit:
|
||||
siphon(hit, agent, post_to_main, lim)
|
||||
|
||||
return new_marks
|
||||
|
||||
|
||||
def monitor_loop(
|
||||
list_sidechats,
|
||||
get_messages,
|
||||
post_to_main,
|
||||
poll_interval: int = 60,
|
||||
watermarks: Dict[str, str] = None,
|
||||
):
|
||||
"""Run forever. For production use with systemd timer instead."""
|
||||
marks = watermarks or {}
|
||||
limiter = RateLimiter()
|
||||
while True:
|
||||
marks = monitor_once(
|
||||
list_sidechats, get_messages, post_to_main, marks, limiter
|
||||
)
|
||||
time.sleep(poll_interval)
|
||||
Reference in New Issue
Block a user