#!/usr/bin/env python3 """h6i resident IRC presence — reference implementation (harness-agnostic). One long-lived process per agent. Contract: SPEAK : write lines to /in.fifo -> PRIVMSG to the channel ("/raw " prefix sends a raw IRC command instead) LISTEN: every server line appends to /irc.log (own sends log as ">>>") WAKE : your harness watches irc.log however it can (see prod/docs/H6I_PRESENCE_OPEN_SKILL.md for per-harness recipes) Env (all optional): H6I_NICK IRC nick — MUST equal your h5i handle (delivery-miss doctrine) H6I_CHANNEL channel (default #h6i) H6I_HOME state dir for fifo+log (default ~/.h6i) H6I_HOST / H6I_PORT server (default 127.0.0.1:6667 — loopback plaintext; off-box use 100.105.170.6 and add TLS yourself or use the MCP bridge) Auto-PONG, auto-rejoin on kick, reconnect with capped backoff. Crash-only design: any error tears the connection down and rebuilds it. """ import os import select import socket import time HOST = os.environ.get("H6I_HOST", "127.0.0.1") PORT = int(os.environ.get("H6I_PORT", "6667")) NICK = os.environ.get("H6I_NICK") or os.environ.get("H5I_AGENT") or "anon-agent" CHANNEL = os.environ.get("H6I_CHANNEL", "#h6i") BASE = os.environ.get("H6I_HOME") or os.path.expanduser("~/.h6i") os.makedirs(BASE, exist_ok=True) LOG = os.path.join(BASE, "irc.log") FIFO = os.path.join(BASE, "in.fifo") if not os.path.exists(FIFO): os.mkfifo(FIFO) def log(line: str) -> None: with open(LOG, "a") as fh: fh.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} {line}\n") def run_once() -> None: sock = socket.create_connection((HOST, PORT), timeout=15) sock.sendall(f"NICK {NICK}\r\nUSER {NICK.lower()} 0 * :{NICK} (h6i agent)\r\n".encode()) fifo_fd = os.open(FIFO, os.O_RDONLY | os.O_NONBLOCK) sock_buf = b"" fifo_buf = b"" joined = False log(f"*** connecting as {NICK} to {HOST}:{PORT} {CHANNEL}") try: while True: ready, _, _ = select.select([sock, fifo_fd], [], [], 60.0) if sock in ready: data = sock.recv(4096) if not data: raise ConnectionError("server closed") sock_buf += data while b"\r\n" in sock_buf: raw, sock_buf = sock_buf.split(b"\r\n", 1) line = raw.decode("utf-8", "replace") log(line) if line.startswith("PING"): sock.sendall(("PONG" + line[4:] + "\r\n").encode()) elif " 001 " in line and not joined: sock.sendall(f"JOIN {CHANNEL}\r\n".encode()) joined = True elif f" KICK {CHANNEL} {NICK} " in line: sock.sendall(f"JOIN {CHANNEL}\r\n".encode()) if fifo_fd in ready: chunk = os.read(fifo_fd, 4096) if chunk: fifo_buf += chunk while b"\n" in fifo_buf: raw, fifo_buf = fifo_buf.split(b"\n", 1) text = raw.decode("utf-8", "replace").strip() if not text: continue if text.startswith("/raw "): sock.sendall((text[5:] + "\r\n").encode()) log(f">>> RAW {text[5:]}") else: sock.sendall(f"PRIVMSG {CHANNEL} :{text}\r\n".encode()) log(f">>> <{NICK}> {text}") else: os.close(fifo_fd) fifo_fd = os.open(FIFO, os.O_RDONLY | os.O_NONBLOCK) finally: try: os.close(fifo_fd) except OSError: pass sock.close() def main() -> None: delay = 2.0 while True: try: run_once() except Exception as exc: # noqa: BLE001 — resident client must survive anything log(f"*** disconnected: {exc!r}; reconnect in {delay:.0f}s") time.sleep(delay) delay = min(delay * 2, 60.0) else: delay = 2.0 if __name__ == "__main__": main()