feat(curator): hook into the gateway's cron-ticker thread
Long-running gateways need the curator to fire on cadence without restarts. Piggy-back on the existing cron ticker thread (which already runs image/document cache cleanup every hour on the same pattern) instead of spawning a dedicated timer thread. - New CURATOR_EVERY = 60 ticks (poll hourly at default 60s interval). The inner config.interval_hours gate controls the real cadence, so 60 of these 60 hourly pokes are cheap no-ops and one runs the review. - Removed the boot-time call added in the prior commit — the ticker covers boot + every hour thereafter. Avoids double-running. Handles the weekly-default-on-24/7-gateway gap flagged in review.
This commit is contained in:
@@ -2383,29 +2383,6 @@ class GatewayRunner:
|
||||
# Discover and load event hooks
|
||||
self.hooks.discover_and_load()
|
||||
|
||||
# Curator — kick off a background skill-maintenance pass on gateway
|
||||
# startup if the schedule says we're due. Runs in a daemon thread
|
||||
# so it never blocks gateway startup. Best-effort; any failure is
|
||||
# swallowed. The interval_hours gate prevents re-running on quick
|
||||
# restarts.
|
||||
try:
|
||||
from agent.curator import maybe_run_curator
|
||||
|
||||
def _curator_summary(msg: str) -> None:
|
||||
# Surface the one-line summary into gateway logs so operators
|
||||
# can see what the curator did. No per-platform push since
|
||||
# there's no user-facing session at gateway boot.
|
||||
logger.info("curator: %s", msg)
|
||||
|
||||
maybe_run_curator(
|
||||
idle_for_seconds=float("inf"), # gateway boot = no active agent
|
||||
on_summary=_curator_summary,
|
||||
)
|
||||
except Exception:
|
||||
logger.debug(
|
||||
"curator boot hook failed", exc_info=True,
|
||||
)
|
||||
|
||||
|
||||
# Recover background processes from checkpoint (crash recovery)
|
||||
try:
|
||||
@@ -11767,6 +11744,7 @@ def _start_cron_ticker(stop_event: threading.Event, adapters=None, loop=None, in
|
||||
IMAGE_CACHE_EVERY = 60 # ticks — once per hour at default 60s interval
|
||||
CHANNEL_DIR_EVERY = 5 # ticks — every 5 minutes
|
||||
PASTE_SWEEP_EVERY = 60 # ticks — once per hour
|
||||
CURATOR_EVERY = 60 # ticks — poll hourly (inner gate handles the real cadence)
|
||||
|
||||
logger.info("Cron ticker started (interval=%ds)", interval)
|
||||
tick_count = 0
|
||||
@@ -11818,6 +11796,21 @@ def _start_cron_ticker(stop_event: threading.Event, adapters=None, loop=None, in
|
||||
except Exception as e:
|
||||
logger.debug("Paste sweep error: %s", e)
|
||||
|
||||
# Curator — piggy-back on the existing cron ticker so long-running
|
||||
# gateways get weekly skill maintenance without needing restarts.
|
||||
# maybe_run_curator() is internally gated by config.interval_hours
|
||||
# (7 days by default), so CURATOR_EVERY is just the poll rate — the
|
||||
# real work only fires once per config interval.
|
||||
if tick_count % CURATOR_EVERY == 0:
|
||||
try:
|
||||
from agent.curator import maybe_run_curator
|
||||
maybe_run_curator(
|
||||
idle_for_seconds=float("inf"),
|
||||
on_summary=lambda msg: logger.info("curator: %s", msg),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("Curator tick error: %s", e)
|
||||
|
||||
stop_event.wait(timeout=interval)
|
||||
logger.info("Cron ticker stopped")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user