./eiffel-monitor/monitor.log:12302:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12304:future: <Task finished name='Task-9951' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12321:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12323:future: <Task finished name='Task-9957' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12340:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12342:future: <Task finished name='Task-9963' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12359:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12361:future: <Task finished name='Task-9969' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12378:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12380:future: <Task finished name='Task-9975' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12397:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12399:future: <Task finished name='Task-9981' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12416:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12418:future: <Task finished name='Task-9987' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12435:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12473:future: <Task finished name='Task-9999' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12490:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12492:future: <Task finished name='Task-10005' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12509:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12511:future: <Task finished name='Task-10011' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12528:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12530:future: <Task finished name='Task-10017' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12547:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12549:future: <Task finished name='Task-10023' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12566:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12568:future: <Task finished name='Task-10029' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12585:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12587:future: <Task finished name='Task-10035' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12604:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12606:future: <Task finished name='Task-10041' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12623:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12625:future: <Task finished name='Task-10047' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12642:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12644:future: <Task finished name='Task-10053' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12661:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12663:future: <Task finished name='Task-10059' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12680:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12718:future: <Task finished name='Task-9417' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12735:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12737:future: <Task finished name='Task-9489' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12754:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12756:future: <Task finished name='Task-9561' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12773:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12775:future: <Task finished name='Task-9633' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12792:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12794:future: <Task finished name='Task-9705' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12811:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12813:future: <Task finished name='Task-9777' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12830:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12832:future: <Task finished name='Task-9849' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12849:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12851:future: <Task finished name='Task-9921' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12868:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12870:future: <Task finished name='Task-9993' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12887:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12889:future: <Task finished name='Task-10065' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12906:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12908:future: <Task finished name='Task-10071' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12925:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12927:future: <Task finished name='Task-10077' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12944:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12946:future: <Task finished name='Task-10083' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12963:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12965:future: <Task finished name='Task-10089' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:12982:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:12984:future: <Task finished name='Task-10095' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13001:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13003:future: <Task finished name='Task-10101' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13020:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13022:future: <Task finished name='Task-10107' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13039:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13041:future: <Task finished name='Task-10113' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13058:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13060:future: <Task finished name='Task-10119' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13077:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13079:future: <Task finished name='Task-10125' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13096:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13098:future: <Task finished name='Task-10131' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13115:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13153:future: <Task finished name='Task-10143' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13170:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13172:future: <Task finished name='Task-10149' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13189:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13191:future: <Task finished name='Task-10155' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13208:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13210:future: <Task finished name='Task-10161' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13227:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13229:future: <Task finished name='Task-10167' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13246:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13248:future: <Task finished name='Task-10173' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13265:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13267:future: <Task finished name='Task-10179' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13284:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13286:future: <Task finished name='Task-10185' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13303:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13305:future: <Task finished name='Task-10191' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13322:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13324:future: <Task finished name='Task-10197' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13341:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13343:future: <Task finished name='Task-10203' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13360:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13398:future: <Task finished name='Task-10215' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13415:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13417:future: <Task finished name='Task-10221' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13434:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13436:future: <Task finished name='Task-10227' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13453:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13455:future: <Task finished name='Task-10233' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13472:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13474:future: <Task finished name='Task-10239' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13491:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13493:future: <Task finished name='Task-10245' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13510:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13512:future: <Task finished name='Task-10251' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13529:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13531:future: <Task finished name='Task-10257' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13548:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13550:future: <Task finished name='Task-10263' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13567:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13569:future: <Task finished name='Task-10269' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13586:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13588:future: <Task finished name='Task-10275' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13605:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13643:future: <Task finished name='Task-10287' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13660:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13662:future: <Task finished name='Task-10293' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13679:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13681:future: <Task finished name='Task-10299' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13698:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13700:future: <Task finished name='Task-10305' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13717:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13719:future: <Task finished name='Task-10311' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13736:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13738:future: <Task finished name='Task-10317' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13755:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13757:future: <Task finished name='Task-10323' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13774:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13776:future: <Task finished name='Task-10329' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13793:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13795:future: <Task finished name='Task-10335' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13812:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13814:future: <Task finished name='Task-10341' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13831:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13833:future: <Task finished name='Task-10347' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13850:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13888:future: <Task finished name='Task-10359' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13905:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13907:future: <Task finished name='Task-10365' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13924:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13926:future: <Task finished name='Task-10371' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13943:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13945:future: <Task finished name='Task-10377' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13962:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13964:future: <Task finished name='Task-10383' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:13981:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:13983:future: <Task finished name='Task-10389' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14000:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14002:future: <Task finished name='Task-10395' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14019:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14021:future: <Task finished name='Task-10401' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14038:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14040:future: <Task finished name='Task-10407' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14057:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14059:future: <Task finished name='Task-10413' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14076:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14078:future: <Task finished name='Task-10419' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14095:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14133:future: <Task finished name='Task-10431' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14150:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14152:future: <Task finished name='Task-10437' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14169:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14171:future: <Task finished name='Task-10443' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14188:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14190:future: <Task finished name='Task-10449' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14207:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14209:future: <Task finished name='Task-10455' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14226:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14228:future: <Task finished name='Task-10461' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14245:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14247:future: <Task finished name='Task-10467' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14264:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14266:future: <Task finished name='Task-10473' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14283:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14285:future: <Task finished name='Task-10479' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14302:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14304:future: <Task finished name='Task-10485' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14321:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14323:future: <Task finished name='Task-10491' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14340:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14378:future: <Task finished name='Task-10503' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14395:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14397:future: <Task finished name='Task-10509' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14414:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14416:future: <Task finished name='Task-10515' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14433:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14435:future: <Task finished name='Task-10521' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14452:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14454:future: <Task finished name='Task-10527' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14471:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14473:future: <Task finished name='Task-10533' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14490:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14492:future: <Task finished name='Task-10539' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14509:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14511:future: <Task finished name='Task-10545' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14528:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14530:future: <Task finished name='Task-10551' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14547:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14549:future: <Task finished name='Task-10557' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14566:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14568:future: <Task finished name='Task-10563' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14585:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14623:future: <Task finished name='Task-10575' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14640:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14642:future: <Task finished name='Task-10581' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14659:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14661:future: <Task finished name='Task-10587' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14678:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14680:future: <Task finished name='Task-10593' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14697:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14699:future: <Task finished name='Task-10599' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14716:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14718:future: <Task finished name='Task-10605' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14735:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14737:future: <Task finished name='Task-10611' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14754:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14756:future: <Task finished name='Task-10617' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14773:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14775:future: <Task finished name='Task-10623' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14792:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14794:future: <Task finished name='Task-10629' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14811:Exception: Connection.init: Connection closed while reading from the driver
./eiffel-monitor/monitor.log:14813:future: <Task finished name='Task-10635' coro=<Connection.run.<locals>.init() done, defined at /home/sebas/eiffel-monitor/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:309> exception=Exception('Connection.init: Connection closed while reading from the driver')>
./eiffel-monitor/monitor.log:14830:Exception: Connection.init: Connection closed while reading from the driver
./telegram-bot-listener-backup-20260118/venv/lib/python3.13/site-packages/httpcore/_backends/mock.py:32:            raise ReadError("Connection closed")
./telegram-bot-listener-backup-20260118/venv/lib/python3.13/site-packages/httpcore/_backends/mock.py:93:            raise ReadError("Connection closed")
./telegram-bot-listener-backup-20260118/venv/lib/python3.13/site-packages/pip/_vendor/urllib3/response.py:471:                # everything, so if we have a hold of the connection close that
./telegram-bot-listener-backup-20260118/venv/lib/python3.13/site-packages/pip/_vendor/rich/console.py:976:            # ValueError: I/O operation on closed file
./telegram-bot-listener-backup-20260118/venv/lib/python3.13/site-packages/pip/_vendor/distlib/version.py:462:    """Suggest a normalized version close to the given version string.
./telegram-bot-listener-backup-20260118/venv/lib/python3.13/site-packages/anyio/_core/_tempfile.py:140:    :param delete_on_close: (Python 3.12+) Whether to delete the file on close.
./telegram-bot-listener-backup-20260118/venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/METADATA:379:- Don't raise exceptions on ungraceful connection closes. (Pull #310)
./telegram-bot-listener-backup-20260118/venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/METADATA:424:- Abort SSL connections on close rather than waiting for remote EOF when using `asyncio`.  (Pull #167)

[Showing lines 168-409 of 409 (50.0KB limit). Full output: /tmp/pi-bash-90b504501481150b.log]


/home/sebas/.agents/skills.bck/personal-research/SKILL.md:22:      "${AGENTS_DB_API:-http://100.116.176.16:8091}/api/ingest"`
/home/sebas/.agents/skills.bck/personal-research/SKILL.md:26:      "${AGENTS_DB_API:-http://100.116.176.16:8091}/api/ingest"`
/home/sebas/.agents/skills.bck/personal-research/SKILL.md:30:      "${AGENTS_DB_API:-http://100.116.176.16:8091}/api/ingest"`
/home/sebas/.agents/skills.bck/personal-research/SKILL.md:38:      "${AGENTS_DB_API:-http://100.116.176.16:8091}/api/ingest"`
/home/sebas/.agents/skills/memory-write/memory-write:20:    p = argparse.ArgumentParser(description='Write memory to agents-database')
/home/sebas/.agents/skills/memory-write/memory-write:75:        api.rstrip('/') + '/api/ingest',
/home/sebas/.agents/skills/memory-write/SKILL.md:2:name: memory-write
/home/sebas/.agents/skills/memory-write/SKILL.md:3:description: Save a durable high-signal memory into agents-database
/home/sebas/.agents/skills/memory-write/SKILL.md:6:# memory-write
/home/sebas/.agents/skills/memory-write/SKILL.md:33:   - `~/.agents/skills/memory-write/memory-write --type <type> --scope <scope> --title "<title>" --content "<content>"`
/home/sebas/.agents/skills/memory-write/SKILL.md:39:~/.agents/skills/memory-write/memory-write \
/home/sebas/.agents/skills/memory-write/SKILL.md:48:~/.agents/skills/memory-write/memory-write \
/home/sebas/.agents/skills/memory-write/SKILL.md:53:  --content "Treat agents-database as shared durable memory, not only search." \
/mnt/rpi/pi-config/SYSTEM.md:39:Use the `agents-database` HTTP API on `http://100.116.176.16:8091`, especially `GET /api/search`, `GET /api/context`, and `POST /api/ingest`.
/mnt/rpi/pi-config/SYSTEM.md:60:`agents-database` status: `http://100.116.176.16:8091/api/status`
/mnt/rpi/pi-config/AGENTS.md:45:- `agents-database`: `http://100.116.176.16:8091/api/status`
/mnt/rpi/pi-config/README.md:28:- Session close hook now prompts once before switch/fork/exit to encourage saving durable memory into agents-database.
/mnt/rpi/pi-config/README.md:30:- The canonical memory pipeline lives in `agents-database` (`MemoryService` / `MemoryEngine`). Repo-local helpers should only capture session context and hand off to that service.
/mnt/rpi/pi-config/scripts/session-memory-analyzer.py:48:        f"{AGENTS_DB_API}/api/ingest",
/mnt/rpi/pi-config/docs/agents-database-memory-contract.md:3:This repo uses `agents-database` as the canonical memory store.
/mnt/rpi/pi-config/docs/agents-database-memory-contract.md:9:- `capture_session()` and `capture_conversation()` create raw source material.


## Goal

Make this repo both the source of truth **and the live Pi config directory**.

## Notes

- Direct, compressed, low-noise communication is the default style.
- Session close hook now prompts once before switch/fork/exit to encourage saving durable memory into agents-database.
- Session memory now uses an external local dispatcher on `127.0.0.1:17882`; the hook only captures session identity/context, the dispatcher enqueues metadata, and a background analyzer decides whether anything durable is worth saving.
- The canonical memory pipeline lives in `agents-database` (`MemoryService` / `MemoryEngine`). Repo-local helpers should only capture session context and hand off to that service.
- The analyzer uses `pi` in background with a cheap model (`gpt-5.4-mini`) and thinking off when possible.
- Use `./scripts/install-session-memory-dispatcher.service` to keep the dispatcher alive under systemd user service if desired.
- The old `session_before_shutdown` runtime patch remains as fallback only if you choose to keep it enabled.
- Reapply the runtime patch after Pi upgrades only if you still use that fallback: `./scripts/apply-pi-shutdown-hook-patch.py`
- **Pending validation:** confirm the default communication mode auto-loads in every new Pi session.
- Top-level `/tmp` files older than 3 days are cleaned every 3 days via `bin/tmp-cleanup`.
- Install the cleanup cron with `scripts/install-tmp-cleanup-cron.sh`.
- Remote laptop workflow can be driven from this Pi using `bin/laptop-run`, `bin/laptop-read`, `bin/laptop-write`, `bin/laptop-rsync`, `bin/laptop-mount`, and `bin/laptop-umount` against host `sebastians-macbook-pro-2`.

## Local server routes on `ballbox-first`

Current nginx portal on `:80` exposes:
- `/` → portal home from `/var/www/portal`
- `/pi-job-status.json` → generated status file with active tmux `pi-job-` session count
- `/portal/` → proxied app on `127.0.0.1:18789`
- `/calistenia/` → static files from `/home/sebas/clawd/web/calistenia/`
- `/files/` → copyparty file server on `127.0.0.1:3923`
- `/mp3-share/` → static directory listing from `/home/sebas/downloads/mp3-share/`

Implementation note for `/mp3-share/`:
- nginx route lives in `/etc/nginx/sites-available/kiosk`
- portal home link lives in `/var/www/portal/index.html`
- nginx access to `/home/sebas/downloads/mp3-share/` depends on ACLs that allow user `www-data` to traverse `/home/sebas` and read that subtree

## Remote laptop helpers

Default host is `sebastians-macbook-pro-2`. Override with `LAPTOP_HOST` if needed.

Examples:
- `bin/laptop-run 'uname -a'`
- `bin/laptop-read /Users/sebas/.zshrc | sed -n '1,40p'`
- `bin/laptop-rsync sebastians-macbook-pro-2:/Users/sebas/Downloads/file ./`
- `bin/laptop-rsync ./localfile sebastians-macbook-pro-2:/Users/sebas/Downloads/`
- `printf 'new contents\n' | bin/laptop-write /Users/sebas/Downloads/test.txt`
- `bin/laptop-write --from ./localfile /Users/sebas/Downloads/test.txt`
- `bin/laptop-mount /Users/sebas/Downloads downloads` then work at `~/mnt/laptop/downloads`

## /tmp cleanup

Top-level `/tmp` files older than 3 days are cleaned every 3 days.

- `bin/tmp-cleanup [--dry-run]`
- `scripts/install-tmp-cleanup-cron.sh`


#!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
from typing import Any
from urllib import request

AGENTS_DB_API = os.environ.get("AGENTS_DB_API", "http://100.116.176.16:8091")
PI_MODEL = os.environ.get("PI_SESSION_MEMORY_MODEL", "gpt-5.4-mini")
PI_THINKING = os.environ.get("PI_SESSION_MEMORY_THINKING", "off")

def load_job(job_file: Path) -> dict[str, Any]:
    return json.loads(job_file.read_text(encoding="utf-8"))

def save_job(job_file: Path, payload: dict[str, Any]) -> None:
    job_file.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")

def call_pi(prompt: str) -> str:
    cmd = [
        "pi",
        "-p",
        "--provider",
        "openai-codex",
        "--model",
        PI_MODEL,
        "--thinking",
        PI_THINKING,
        "--no-tools",
        "--no-session",
        prompt,
    ]
    proc = subprocess.run(cmd, capture_output=True, text=True)
    if proc.returncode != 0:
        raise RuntimeError(proc.stderr.strip() or proc.stdout.strip() or f"pi failed ({proc.returncode})")
    return proc.stdout.strip()

def ingest(memory: dict[str, Any]) -> dict[str, Any]:
    req = request.Request(
        f"{AGENTS_DB_API}/api/ingest",
        method="POST",
        data=json.dumps(memory).encode(),
        headers={"Content-Type": "application/json"},
    )
    with request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read().decode("utf-8"))

def extract_json(text: str) -> dict[str, Any]:
    raw = text.strip()
    if raw.startswith("```"):
        raw = raw.strip("`")
        raw = raw.split("\n", 1)[-1] if "\n" in raw else raw
    start = raw.find("{")
    end = raw.rfind("}")
    if start >= 0 and end > start:
        raw = raw[start : end + 1]
    return json.loads(raw)

def session_meta(job: dict[str, Any]) -> dict[str, Any]:
    payload = job.get("payload")
    if isinstance(payload, dict):
        return payload
    return job

def build_prompt(job: dict[str, Any]) -> str:
    meta = session_meta(job)
    return f"""You are deciding whether a Pi session has durable value.

Rules:
- Prefer NOT saving anything unless it is durable and reusable.
- Session metadata alone is not enough.
- Do NOT save operational notes about this session-memory system, dispatch flow, or local implementation work unless they affect broader workflows beyond this repo.
- Save only if the session contains a stable user preference, enduring project fact, reusable external artifact, decision, procedure, or blocker that will matter in future unrelated sessions.
- If nothing durable exists, return save=false.
- If the memory is likely durable, assign the best type and include evidence.
- Output JSON only. No markdown. No extra text.

Return this schema:
{{
  "save": boolean,
  "type": "profile" | "decision" | "project" | "artifact" | "episode" | "procedural" | "semantic",
  "subtype": string | null,
  "scope": "global" | "project",
  "title": string,
  "content": string,
  "source_ref": string,
  "evidence_ref": string | null,
  "confidence": number,
  "freshness": number,
  "importance": number,
  "reason": string
}}

Scoring guidance:
- confidence: 0 to 1, how well supported the memory is.
- freshness: 0 to 1, how current it is.
- importance: 0 to 1, how much future value it has.
- evidence_ref: cite the strongest source in the session or branch excerpt when possible.

Session metadata:
- session_id: {meta.get('session_id')}
- session_file: {meta.get('session_file')}
- cwd: {meta.get('cwd')}
- scope: {meta.get('scope')}
- reason: {meta.get('reason')}
- user_messages: {meta.get('user_messages')}
- assistant_messages: {meta.get('assistant_messages')}
- last_user_text: {str(meta.get('last_user_text') or '')[:2000]}
- last_assistant_text: {str(meta.get('last_assistant_text') or '')[:2000]}
- trigger: {meta.get('trigger')}

Branch excerpt JSON:
{json.dumps(meta.get('branch') or [], ensure_ascii=False)[:30000]}
"""

def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--job-file", required=True)
    args = parser.parse_args()

    job_file = Path(args.job_file)
    job = load_job(job_file)
    meta = session_meta(job)
    job.setdefault("analysis", {})
    job["analysis"]["status"] = "running"
    save_job(job_file, job)

    try:
        raw = call_pi(build_prompt(job))
        analysis = extract_json(raw)
        job["analysis"]["raw"] = raw
        job["analysis"]["result"] = analysis
        job["analysis"]["status"] = "completed"

        if bool(analysis.get("save")):
            memory_type = analysis.get("type") or "episode"
            is_candidate = memory_type == "episode" or bool(float(analysis.get("importance") or 0.5) < 0.75)
            memory = {
                "type": memory_type,
                "subtype": analysis.get("subtype"),
                "scope": analysis.get("scope") or meta.get("scope") or "global",
                "status": "inbox" if is_candidate else "active",
                "source_kind": "manual",
                "title": analysis.get("title") or "Session insight",
                "content": analysis.get("content") or "",
                "confidence": float(analysis.get("confidence") or 0.75),
                "freshness": float(analysis.get("freshness") or 0.9),
                "importance": float(analysis.get("importance") or 0.5),
                "source_ref": analysis.get("source_ref") or f"pi-session:{meta.get('session_file') or meta.get('session_id') or 'unknown'}",
                "evidence_ref": analysis.get("evidence_ref"),
                "origin_agent": "pi-session-memory-analyzer",
                "metadata": {
                    "job_file": str(job_file),
                    "session_id": meta.get("session_id"),
                    "session_file": meta.get("session_file"),
                    "cwd": meta.get("cwd"),
                    "reason": meta.get("reason"),
                    "user_messages": meta.get("user_messages"),
                    "assistant_messages": meta.get("assistant_messages"),
                    "analysis_reason": analysis.get("reason"),
                    "trigger": meta.get("trigger"),
                    "promotion_candidate": not is_candidate,
                },
            }
            saved = ingest(memory)
            job["analysis"]["memory_id"] = saved.get("id")
            job["analysis"]["saved"] = True
        else:
            job["analysis"]["saved"] = False
            job["analysis"]["memory_id"] = None

        save_job(job_file, job)
        print(json.dumps(job["analysis"], ensure_ascii=False))
        return 0
    except Exception as exc:
        job["analysis"]["status"] = "failed"
        job["analysis"]["error"] = str(exc)
        save_job(job_file, job)
        print(json.dumps(job["analysis"], ensure_ascii=False), file=sys.stderr)
        return 1

if __name__ == "__main__":
    raise SystemExit(main())


# Agents Database memory contract

This repo uses `agents-database` as the canonical memory store.

## Canonical behavior

- `MemoryService` / `MemoryEngine` own the memory pipeline.
- `ingest()` writes memory records.
- `capture_session()` and `capture_conversation()` create raw source material.
- `process_pending()` extracts episode memories from pending ingestion events.
- `consolidate_candidates(config)` performs dedup, conflict detection, promotion, and optional synthesis.
- `maintenance_jobs` and `maintenance_runs` schedule and audit periodic consolidation.
- `episode` and `inbox` records are candidates, not truth, until consolidated or explicitly promoted.
- `memory_conflicts` and `memory_links` are the right place for contradiction and relation semantics.

## Pi-side rule

`pi-config` may:

- capture session context
- queue analyzer jobs
- ask for promotion candidates
- emit inspection reports

`pi-config` should not:

- reimplement the canonical consolidation pipeline
- invent a parallel truth store
- silently promote memories outside the shared DB contracts


bash

cd /mnt/rpi/pi-config && rg -n "17882|session-memory|dispatcher|before_shutdown|shutdown-hook|session close hook|branch excerpt|session_file|last_user_text|last_assistant_text|pi-session-memory-analyzer" -S .

ps -ef | grep -E '[s]ession-memory|17882|dispatcher'
ss -ltnp | grep 17882 || true
systemctl --user --no-pager --full status session-memory-dispatcher 2>/dev/null || true
journalctl --user -u session-memory-dispatcher -n 80 --no-pager 2>/dev/null || true


./extensions/session-memory-dispatcher.ts:4:const DISPATCH_URL = process.env.PI_SESSION_MEMORY_DISPATCH_URL || "http://127.0.0.1:17882/api/session-memory";
./extensions/session-memory-dispatcher.ts:66:		session_file: ctx.sessionManager.getSessionFile?.() || null,
./extensions/session-memory-dispatcher.ts:72:		last_user_text: truncate(stats.lastUserText, 2000),
./extensions/session-memory-dispatcher.ts:73:		last_assistant_text: truncate(stats.lastAssistantText, 2000),
./extensions/session-memory-dispatcher.ts:89:		throw new Error(`session memory dispatcher failed: ${response.status} ${response.statusText}`);
./extensions/session-memory-dispatcher.ts:125:				trigger: "session-memory-dispatcher",
./extensions/session-memory-dispatcher.ts:154:	pi.on("session_before_shutdown", async (event, ctx) => {
./extensions/session-memory-dispatcher.ts:155:		const result = await runPrompt(ctx, `before_shutdown:${event.reason}`, { allowCancel: true, title: "Analyze this session for memory?", timeoutMs: 12000 });
./scripts/session-memory-analyzer.py:83:- Do NOT save operational notes about this session-memory system, dispatch flow, or local implementation work unless they affect broader workflows beyond this repo.
./scripts/session-memory-analyzer.py:109:- evidence_ref: cite the strongest source in the session or branch excerpt when possible.
./scripts/session-memory-analyzer.py:113:- session_file: {meta.get('session_file')}
./scripts/session-memory-analyzer.py:119:- last_user_text: {str(meta.get('last_user_text') or '')[:2000]}
./scripts/session-memory-analyzer.py:120:- last_assistant_text: {str(meta.get('last_assistant_text') or '')[:2000]}
./scripts/session-memory-analyzer.py:123:Branch excerpt JSON:
./scripts/session-memory-analyzer.py:161:                "source_ref": analysis.get("source_ref") or f"pi-session:{meta.get('session_file') or meta.get('session_id') or 'unknown'}",
./scripts/session-memory-analyzer.py:163:                "origin_agent": "pi-session-memory-analyzer",
./scripts/session-memory-analyzer.py:167:                    "session_file": meta.get("session_file"),
./scripts/session-memory-dispatcher.py:13:JOB_DIR = Path(os.environ.get("PI_SESSION_MEMORY_JOB_DIR", BASE_DIR / "sessions" / "session-memory-jobs"))
./scripts/session-memory-dispatcher.py:14:ANALYZER = Path(os.environ.get("PI_SESSION_MEMORY_ANALYZER", BASE_DIR / "scripts" / "session-memory-analyzer.py"))
./scripts/session-memory-dispatcher.py:49:        if self.path != "/api/session-memory":
./scripts/session-memory-dispatcher.py:85:    parser.add_argument("--port", type=int, default=17882)
./scripts/session-memory-dispatcher.py:89:    print(f"session-memory-dispatcher listening on http://{args.host}:{args.port}/api/session-memory", flush=True)
./scripts/apply-pi-shutdown-hook-patch.py:48:                """/** Fired before forking a session (can be cancelled) */\nexport interface SessionBeforeForkEvent {\n    type: \"session_before_fork\";\n    entryId: string;\n}\n/** Fired before graceful shutdown paths such as Ctrl+C, Ctrl+D, /quit, or extension-triggered exit (can be cancelled). */\nexport interface SessionBeforeShutdownEvent {\n    type: \"session_before_shutdown\";\n    reason: \"ctrl_c\" | \"ctrl_d\" | \"command\" | \"extension\" | \"signal\" | \"rpc_eof\" | \"rpc_shutdown\" | \"unknown\";\n    signal?: \"SIGHUP\" | \"SIGTERM\";\n}\n/** Fired before context compaction (can be cancelled or customized) */\nexport interface SessionBeforeCompactEvent {\n""",
./scripts/apply-pi-shutdown-hook-patch.py:60:                """    on(event: \"session_before_switch\", handler: ExtensionHandler<SessionBeforeSwitchEvent, SessionBeforeSwitchResult>): void;\n    on(event: \"session_before_fork\", handler: ExtensionHandler<SessionBeforeForkEvent, SessionBeforeForkResult>): void;\n    on(event: \"session_before_shutdown\", handler: ExtensionHandler<SessionBeforeShutdownEvent, SessionBeforeShutdownResult>): void;\n    on(event: \"session_before_compact\", handler: ExtensionHandler<SessionBeforeCompactEvent, SessionBeforeCompactResult>): void;\n""",
./scripts/apply-pi-shutdown-hook-patch.py:76:                """} ? SessionBeforeSwitchResult | undefined : TEvent extends {\n    type: \"session_before_fork\";\n} ? SessionBeforeForkResult | undefined : TEvent extends {\n    type: \"session_before_shutdown\";\n} ? SessionBeforeShutdownResult | undefined : TEvent extends {\n    type: \"session_before_compact\";\n} ? SessionBeforeCompactResult | undefined : TEvent extends {\n""",
./scripts/apply-pi-shutdown-hook-patch.py:82:                """    isSessionBeforeEvent(event) {\n        return (event.type === \"session_before_switch\" ||\n            event.type === \"session_before_fork\" ||\n            event.type === \"session_before_shutdown\" ||\n            event.type === \"session_before_compact\" ||\n            event.type === \"session_before_tree\");\n    }\n""",
./scripts/apply-pi-shutdown-hook-patch.py:104:                """    async emitBeforeShutdown(reason = \"unknown\", signal) {\n        const runner = this.runtimeHost.session.extensionRunner;\n        if (!runner?.hasHandlers(\"session_before_shutdown\")) {\n            return { cancelled: false };\n        }\n        const result = await runner.emit({\n            type: \"session_before_shutdown\",\n            reason,\n            signal,\n        });\n        return { cancelled: result?.cancel === true };\n    }\n    /**\n     * Gracefully shutdown the agent.\n     * Emits shutdown event to extensions, then exits.\n     */\n    isShuttingDown = false;\n    async shutdown(reason = \"unknown\", signal) {\n        if (this.isShuttingDown)\n            return;\n        this.isShuttingDown = true;\n        const beforeResult = await this.emitBeforeShutdown(reason, signal);\n        if (beforeResult.cancelled) {\n            this.isShuttingDown = false;\n            this.shutdownRequested = false;\n            this.shutdownRequestReason = \"extension\";\n            this.shutdownRequestSignal = undefined;\n            return;\n        }\n        this.unregisterSignalHandlers();\n        await this.runtimeHost.dispose();\n""",
./scripts/apply-pi-shutdown-hook-patch.py:138:                """    let detachInput = () => { };\n    async function emitBeforeShutdown(reason = \"rpc_shutdown\", signal) {\n        const runner = runtimeHost.session.extensionRunner;\n        if (!runner?.hasHandlers(\"session_before_shutdown\")) {\n            return { cancelled: false };\n        }\n        const result = await runner.emit({\n            type: \"session_before_shutdown\",\n            reason,\n            signal,\n        });\n        return { cancelled: result?.cancel === true };\n    }\n    async function shutdown(exitCode = 0, reason = \"rpc_shutdown\", signal) {\n        if (shuttingDown) {\n            process.exit(exitCode);\n        }\n        shuttingDown = true;\n        const beforeResult = await emitBeforeShutdown(reason, signal);\n        if (beforeResult.cancelled) {\n            shuttingDown = false;\n            shutdownRequested = false;\n            shutdownRequestReason = \"extension\";\n            shutdownRequestSignal = undefined;\n            return;\n        }\n        for (const cleanup of signalCleanupHandlers) {\n            cleanup();\n        }\n        unsubscribe?.();\n        await runtimeHost.dispose();\n        detachInput();\n        process.stdin.pause();\n        process.exit(exitCode);\n    }\n    async function checkShutdownRequested() {\n        if (!shutdownRequested)\n            return;\n        await shutdown(0, shutdownRequestReason, shutdownRequestSignal);\n    }\n""",
./scripts/install-session-memory-dispatcher.service:2:Description=Pi session memory dispatcher
./scripts/install-session-memory-dispatcher.service:8:ExecStart=/usr/bin/env python3 /mnt/rpi/pi-config/scripts/session-memory-dispatcher.py --host 127.0.0.1 --port 17882
./README.md:11:- `extensions/session-memory-prompt.ts`
./README.md:12:- `scripts/apply-pi-shutdown-hook-patch.py`
./README.md:13:- `scripts/session-memory-analyzer.py`
./README.md:14:- `scripts/session-memory-dispatcher.py`
./README.md:15:- `scripts/install-session-memory-dispatcher.service`
./README.md:28:- Session close hook now prompts once before switch/fork/exit to encourage saving durable memory into agents-database.
./README.md:29:- Session memory now uses an external local dispatcher on `127.0.0.1:17882`; the hook only captures session identity/context, the dispatcher enqueues metadata, and a background analyzer decides whether anything durable is worth saving.
./README.md:32:- Use `./scripts/install-session-memory-dispatcher.service` to keep the dispatcher alive under systemd user service if desired.
./README.md:33:- The old `session_before_shutdown` runtime patch remains as fallback only if you choose to keep it enabled.
./README.md:34:- Reapply the runtime patch after Pi upgrades only if you still use that fallback: `./scripts/apply-pi-shutdown-hook-patch.py`


sebas       1378    1289  0 10:07 ?        00:00:00 python3 /mnt/rpi/pi-config/scripts/session-memory-dispatcher.py --host 127.0.0.1 --port 17882
sebas      32410   10916 99 11:48 ?        00:00:00 rg -n 17882|session-memory|dispatcher|before_shutdown|shutdown-hook|session close hook|branch excerpt|session_file|last_user_text|last_assistant_text|pi-session-memory-analyzer -S .
sebas      32411   10916  0 11:48 ?        00:00:00 /bin/bash -c ps -ef | grep -E '[s]ession-memory|17882|dispatcher' ss -ltnp | grep 17882 || true systemctl --user --no-pager --full status session-memory-dispatcher 2>/dev/null || true journalctl --user -u session-memory-dispatcher -n 80 --no-pager 2>/dev/null || true
sebas      32413   32411  0 11:48 ?        00:00:00 grep -E [s]ession-memory|17882|dispatcher
LISTEN 0      5                        127.0.0.1:17882      0.0.0.0:*    users:(("python3",pid=1378,fd=3))
-- No entries --


**Inspecting extensions and logs**

I need to take a closer look at the extension and possibly examine the dispatcher. Maybe checking the logs or the job directory could give me more insights into what's going on. It's important to make sure everything is functioning properly. There might be specific details in the logs that could help pinpoint any issues. I'll proceed with this inspection to ensure everything is in order and running smoothly.

python3 - <<'PY'
from pathlib import Path
base=Path('/mnt/rpi/pi-config/sessions/session-memory-jobs')
print('exists', base.exists())
if base.exists():
    files=sorted(base.glob('**/*'), key=lambda p: p.stat().st_mtime, reverse=True)
    for p in files[:30]:
        if p.is_file():
            print(p, p.stat().st_mtime)
PY


import path from "node:path";
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";

const DISPATCH_URL = process.env.PI_SESSION_MEMORY_DISPATCH_URL || "http://127.0.0.1:17882/api/session-memory";
const PROMPT_SAVE = "Yes";
const PROMPT_SKIP = "No";
const PROMPT_CANCEL = "Cancel";

function textFromMessage(message: any): string {
	if (!message || !Array.isArray(message.content)) return "";
	return message.content
		.filter((part: any) => part?.type === "text" && typeof part.text === "string")
		.map((part: any) => part.text)
		.join("\n")
		.trim();
}

function truncate(text: string | undefined, max = 220): string {
	const value = (text || "").replace(/\s+/g, " ").trim();
	if (!value) return "";
	return value.length > max ? value.slice(0, max - 1) + "…" : value;
}

function inferScope(cwd: string): "global" | "project" {
	const home = path.resolve(process.env.HOME || "/home/sebas");
	const resolved = path.resolve(cwd);
	if (resolved === home) return "global";
	if (resolved.startsWith(path.join(home, ".pi"))) return "global";
	return "project";
}

function getSessionStats(ctx: any) {
	const branch = ctx.sessionManager.getBranch();
	let userCount = 0;
	let assistantCount = 0;
	let lastUserText = "";
	let lastAssistantText = "";

	for (const entry of branch) {
		if (entry?.type !== "message") continue;
		const message = entry.message;
		if (!message || typeof message !== "object" || !("role" in message)) continue;
		if (message.role === "user") {
			userCount += 1;
			lastUserText = textFromMessage(message) || lastUserText;
		}
		if (message.role === "assistant") {
			assistantCount += 1;
			lastAssistantText = textFromMessage(message) || lastAssistantText;
		}
	}

	return {
		branch,
		userCount,
		assistantCount,
		lastUserText,
		lastAssistantText,
		shouldPrompt: userCount > 0 && assistantCount > 0,
	};
}

function buildHookPayload(ctx: any, stats: ReturnType<typeof getSessionStats>, reason: string) {
	return {
		session_id: ctx.sessionManager.getLeafId?.() || null,
		session_file: ctx.sessionManager.getSessionFile?.() || null,
		reason,
		cwd: ctx.cwd,
		scope: inferScope(ctx.cwd),
		user_messages: stats.userCount,
		assistant_messages: stats.assistantCount,
		last_user_text: truncate(stats.lastUserText, 2000),
		last_assistant_text: truncate(stats.lastAssistantText, 2000),
		branch: stats.branch,
		memory_hints: {
			observed_messages: stats.userCount + stats.assistantCount,
			has_latest_dialogue: Boolean(stats.lastUserText || stats.lastAssistantText),
		},
	};
}

async function dispatchMemory(payload: any) {
	const response = await fetch(DISPATCH_URL, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify(payload),
	});
	if (!response.ok) {
		throw new Error(`session memory dispatcher failed: ${response.status} ${response.statusText}`);
	}
	return response.json();
}

export default function (pi: ExtensionAPI) {
	let promptHandledForCurrentSession = false;

	async function runPrompt(ctx: any, reason: string, options?: { allowCancel?: boolean; title?: string; timeoutMs?: number }) {
		if (promptHandledForCurrentSession) return { cancel: false, skipped: true };
		if (!ctx.hasUI) return { cancel: false, skipped: true };

		const stats = getSessionStats(ctx);
		if (!stats.shouldPrompt) return { cancel: false, skipped: true };

		const allowCancel = options?.allowCancel ?? false;
		const timeoutMs = options?.timeoutMs;
		const title = options?.title || "Before closing this session";
		const choices = allowCancel ? [PROMPT_SAVE, PROMPT_SKIP, PROMPT_CANCEL] : [PROMPT_SAVE, PROMPT_SKIP];
		const dialogOptions = timeoutMs ? { timeout: timeoutMs } : undefined;
		const choice = await ctx.ui.select(title, choices, dialogOptions);

		if (choice === PROMPT_CANCEL || (allowCancel && choice === undefined)) {
			promptHandledForCurrentSession = false;
			return { cancel: true, skipped: false };
		}

		if (choice !== PROMPT_SAVE) {
			promptHandledForCurrentSession = true;
			return { cancel: false, skipped: true };
		}

		const payload = buildHookPayload(ctx, stats, reason);
		try {
			const result = await dispatchMemory({
				...payload,
				trigger: "session-memory-dispatcher",
			});
			promptHandledForCurrentSession = true;
			ctx.ui.notify(`Session analysis queued${result?.job_file ? `: ${result.job_file}` : ""}`, "info");
			return { cancel: false, skipped: false, queued: true, jobFile: result?.job_file || null };
		} catch (error) {
			promptHandledForCurrentSession = false;
			const message = error instanceof Error ? error.message : String(error);
			ctx.ui.notify(message, "error");
			if (!allowCancel) return { cancel: false, skipped: true, error: message };
			const ok = await ctx.ui.confirm("Session analysis failed", "Continue closing this session without queueing?", dialogOptions);
			if (!ok) return { cancel: true, skipped: false, error: message };
			promptHandledForCurrentSession = true;
			return { cancel: false, skipped: true, error: message };
		}
	}

	pi.on("session_before_switch", async (event, ctx) => {
		const result = await runPrompt(ctx, `before_switch:${event.reason}`, { allowCancel: true, title: "Analyze this session for memory?" });
		if (result.cancel) return { cancel: true };
		return undefined;
	});

	pi.on("session_before_fork", async (_event, ctx) => {
		const result = await runPrompt(ctx, "before_fork", { allowCancel: true, title: "Analyze this session for memory?" });
		if (result.cancel) return { cancel: true };
		return undefined;
	});

	pi.on("session_before_shutdown", async (event, ctx) => {
		const result = await runPrompt(ctx, `before_shutdown:${event.reason}`, { allowCancel: true, title: "Analyze this session for memory?", timeoutMs: 12000 });
		if (result.cancel) return { cancel: true };
		return undefined;
	});

	pi.on("session_shutdown", async (_event, ctx) => {
		await runPrompt(ctx, "shutdown_fallback", { allowCancel: false, title: "Analyze this session for memory?", timeoutMs: 12000 });
	});

	pi.registerCommand("remember-session", {
		description: "Queue a session analysis from the current session",
		handler: async (_args, ctx) => {
			promptHandledForCurrentSession = false;
			await runPrompt(ctx, "manual_command", { allowCancel: false, title: "Analyze this session for memory?" });
		},
	});
}


#!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
import os
import subprocess
from datetime import datetime, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent
JOB_DIR = Path(os.environ.get("PI_SESSION_MEMORY_JOB_DIR", BASE_DIR / "sessions" / "session-memory-jobs"))
ANALYZER = Path(os.environ.get("PI_SESSION_MEMORY_ANALYZER", BASE_DIR / "scripts" / "session-memory-analyzer.py"))

def write_job(payload: dict[str, object]) -> Path:
    JOB_DIR.mkdir(parents=True, exist_ok=True)
    job_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
    job_file = JOB_DIR / f"{job_id}.json"
    job = {
        "job_id": job_id,
        "queued_at": datetime.now(timezone.utc).isoformat(),
        "status": "queued",
        "job_type": "session_memory_analysis",
        "payload": payload,
        "analysis": {
            "status": "queued",
            "save": None,
            "memory_id": None,
        },
    }
    job_file.write_text(json.dumps(job, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
    return job_file

def launch_analyzer(job_file: Path) -> None:
    subprocess.Popen(
        ["python3", str(ANALYZER), "--job-file", str(job_file)],
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        start_new_session=True,
        close_fds=True,
    )

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        if self.path != "/api/session-memory":
            self.send_response(404)
            self.end_headers()
            return

        length = int(self.headers.get("Content-Length", "0") or "0")
        raw = self.rfile.read(length)
        try:
            payload = json.loads(raw.decode("utf-8"))
        except Exception as exc:
            self.send_response(400)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(json.dumps({"error": f"invalid json: {exc}"}).encode())
            return

        try:
            job_file = write_job(payload)
            launch_analyzer(job_file)
            self.send_response(202)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(json.dumps({"status": "queued", "job_file": str(job_file)}).encode())
        except Exception as exc:
            self.send_response(502)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(json.dumps({"error": str(exc)}).encode())

    def log_message(self, fmt, *args):
        return

def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--host", default="127.0.0.1")
    parser.add_argument("--port", type=int, default=17882)
    args = parser.parse_args()

    server = ThreadingHTTPServer((args.host, args.port), Handler)
    print(f"session-memory-dispatcher listening on http://{args.host}:{args.port}/api/session-memory", flush=True)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        return 0
    return 0

if __name__ == "__main__":
    raise SystemExit(main())


exists True
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260505T143201664755Z.json 1777991521.7944803
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260505T134438583726Z.json 1777988678.6973987
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260501T194747970340Z.json 1777664870.6168976
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260501T044724939741Z.json 1777610847.0987668
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260501T044435783880Z.json 1777610678.1176302
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260501T035247390123Z.json 1777607569.727583
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260501T033642290422Z.json 1777606605.0265558
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260423T191132397823Z.json 1776971497.2306113
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260423T133840452732Z.json 1776951524.2486367
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T164407751383Z.json 1776876251.8172593
/mnt/rpi/pi-config/sessions/session-memory-jobs/consolidation-20260422T164146241829Z.json 1776876106.2365904
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T152622743888Z.json 1776871596.8283596
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T143827064890Z.json 1776868712.5981774
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T143146583261Z.json 1776868309.9604785
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T142938948545Z.json 1776868186.0719972
/mnt/rpi/pi-config/sessions/session-memory-jobs/consolidation-20260422T142832064769Z.json 1776868112.0596604
/mnt/rpi/pi-config/sessions/session-memory-jobs/consolidation-20260422T142817021443Z.json 1776868097.0155876
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T142754083834Z.json 1776868078.319497
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T135731023970Z.json 1776866254.711166
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T134209878594Z.json 1776865339.3705792
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T130951197595Z.json 1776863394.399127
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T130336093713Z.json 1776863022.181612
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T124200568464Z.json 1776861727.5325553
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T123506691388Z.json 1776861311.2067528
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T032957877477Z.json 1776828601.8737555
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T032853519684Z.json 1776828539.3614755
/mnt/rpi/pi-config/sessions/session-memory-jobs/20260422T032521567405Z.json 1776828326.304507


**Inspecting job files**

I realize that jobs exist, but it seems our test might be a queued job that hasn't been saved yet. I'm thinking I need to take a closer look at the latest job files to see what’s going on. There’s a mix of uncertainty about whether this is working as intended or if I need to take further action. I'll definitely check that out to clarify everything!

python3 - <<'PY'
import json
from pathlib import Path
base=Path('/mnt/rpi/pi-config/sessions/session-memory-jobs')
for p in sorted(base.glob('*.json'), key=lambda x: x.stat().st_mtime, reverse=True)[:12]:
    data=json.loads(p.read_text())
    print(p.name, 'status=', data.get('analysis',{}).get('status'), 'saved=', data.get('analysis',{}).get('saved'), 'memory_id=', data.get('analysis',{}).get('memory_id'), 'trigger=', data.get('payload',{}).get('trigger'), 'reason=', data.get('payload',{}).get('reason'))
PY