#!/usr/bin/env python3 """room_hook.py - re-inject a small agent's living state on every turn. Wire it as a per-prompt hook (e.g. a Claude Code UserPromptSubmit hook). Whatever it prints is added to the model's context, every single turn, so nothing important has to live only in the conversation. Fails open: if anything goes wrong it prints nothing and exits 0. A hook that crashes is a hook that silences the agent it serves. """ import os, sys, re ROOM = os.environ.get("ROOM_DIR", os.path.dirname(os.path.abspath(__file__))) BUDGET = int(os.environ.get("ROOM_BUDGET", "6000")) # chars. Past this, the least important sections are dropped - and the block says so. def read(rel): try: with open(os.path.join(ROOM, rel), encoding="utf-8") as f: return f.read() except OSError: return "" def main(): try: # A single un-encodable character (an arrow, an emoji) can kill the whole # block silently on some consoles. Mangle one character rather than lose all. sys.stdout.reconfigure(encoding="utf-8", errors="replace") except Exception: pass try: now = read("LIVE/NOW.md").strip() diary = read("LIVE/DIARY.md") entries = [e.strip() for e in re.split(r"(?m)^## ", diary) if e.strip()] last = ("## " + entries[-1]) if entries else "" todos = [l for l in read("LIVE/TODO.md").splitlines() if l.startswith("- [ ]")] # Number them the way `room.py done N` counts, so the number you see is the one you type. shown = [f"#{n} {l[6:]}" for n, l in enumerate(todos, 1)][-8:] dont = [l for l in read("LIVE/DONT_FORGET.md").splitlines() if l.startswith("- ")][-5:] # Most important first. Shedding happens from the bottom, and is announced. sections = [ ("WHERE I LEFT OFF", now), ("DON'T FORGET (newest 5)", "\n".join(dont)), (f"STILL OPEN (newest {len(shown)} of {len(todos)}; close with: room.py done N)", "\n".join(shown)), ("MY LAST DIARY ENTRY", last), ] out, shed = [], [] for name, body in sections: if not body: continue piece = f"{name}:\n{body}\n" if sum(map(len, out)) + len(piece) > BUDGET: shed.append(name) continue out.append(piece) if shed: out.append(f"[shed to fit budget: {', '.join(shed)} - still on disk, not gone]\n") print("\n" + "\n".join(out) + "") except Exception: pass # fail open sys.exit(0) if __name__ == "__main__": main()