fix(conversation_store,admin,cli): keep session when file lags db, sort pty by recency

This commit is contained in:
hh
2026-07-26 00:25:07 +02:00
parent d2981116d4
commit 90fe92d014
7 changed files with 113 additions and 7 deletions
+26
View File
@@ -21,7 +21,9 @@ it just keeps the aggregator running for anyone who needs it.
from __future__ import annotations
import asyncio
import contextlib
import logging
import signal
from contextlib import AsyncExitStack
from typing import TYPE_CHECKING
@@ -60,9 +62,33 @@ def main() -> None:
logging.basicConfig(
level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s"
)
_install_sigterm_handler()
asyncio.run(_async_main(), loop_factory=uvloop.new_event_loop)
def _install_sigterm_handler() -> None:
"""Turn SIGTERM into a normal interpreter exit.
Python's default SIGTERM disposition kills the process outright, so
neither ``AsyncExitStack`` unwinding nor ``atexit`` hooks run. That
matters because every ``claude`` we spawn lives in its own session
(ptyprocess calls ``setsid``), which makes it immune to the signal
that took us down — a hard SIGTERM leaves one orphaned CLI per live
session, each holding hundreds of MB. Raising ``KeyboardInterrupt``
instead routes ``docker stop`` / ``systemctl stop`` through the same
shutdown path as Ctrl-C, which does reap them.
Only installed when we own the main thread's signal handlers; under
an embedding host that isn't ours to take.
"""
def _raise_interrupt(_signum: int, _frame: object) -> None:
raise KeyboardInterrupt
with contextlib.suppress(ValueError, OSError):
signal.signal(signal.SIGTERM, _raise_interrupt)
async def _async_main() -> None:
# Populate ``os.environ`` from ``.env`` before anything else so the
# user's ``config.py`` can read its own secrets via ``os.environ[...]``