cc-os/plugins/memory/hooks/session_start.py

107 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""SessionStart hook — staleness check + detached graph rebuild. Fail-open.
Must return sub-second; heavy work is detached in the background. Context
injection is handled by session_context.py (UserPromptSubmit hook).
"""
import os
import sys
import time
import subprocess
from datetime import datetime, timezone
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import config as config_mod
import hook_io
HOOK_LOG = "/tmp/memory-hook.log"
def _utc():
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def _log(line):
try:
with open(HOOK_LOG, "a") as f:
f.write(line + "\n")
except Exception:
pass
def _needs_rebuild(stamp, stale_days):
if not os.path.exists(stamp):
return True
try:
age_days = (time.time() - os.path.getmtime(stamp)) / 86400
return age_days > stale_days
except Exception:
return True
def main():
inp = hook_io.read_input()
source = inp.source or "unknown"
_log(f"[{_utc()}] session-start fired: source={source} cwd={os.getcwd()}")
cfg = config_mod.load_config()
cache_dir = os.path.expanduser("~/.cache/graphify")
stamp = os.path.join(cache_dir, "vault-rebuild.stamp")
lock = os.path.join(cache_dir, "vault-rebuild.lock")
logfile = os.path.join(cache_dir, "vault-rebuild.log")
try:
os.makedirs(cache_dir, exist_ok=True)
except Exception:
pass
if _needs_rebuild(stamp, cfg.stale_days):
if os.path.exists(lock):
try:
with open(logfile, "a") as f:
f.write("[memory/session-start] rebuild already running (lock present), skipping\n")
except Exception:
pass
_log(f"[{_utc()}] session-start: rebuild skipped (lock present)")
else:
try:
open(lock, "a").close()
except Exception:
pass
env = dict(os.environ)
env.update(cfg.env)
env["VAULT_PATH"] = cfg.vault_path
env["MODEL"] = cfg.model
env["STAMP"] = stamp
env["LOCK"] = lock
env["LOGFILE"] = logfile
cmd = (
'graphify extract "$VAULT_PATH" --backend ollama --model "$MODEL" --force '
'>> "$LOGFILE" 2>&1 '
'&& date > "$STAMP" && rm -f "$LOCK" '
'|| (rm -f "$LOCK"; echo "rebuild failed" >> "$LOGFILE")'
)
try:
devnull = open(os.devnull, "wb")
subprocess.Popen(
["bash", "-c", cmd],
stdin=subprocess.DEVNULL,
stdout=devnull,
stderr=devnull,
env=env,
start_new_session=True,
)
except Exception:
pass
_log(f"[{_utc()}] session-start: rebuild triggered")
else:
_log(f"[{_utc()}] session-start: rebuild not needed")
if __name__ == "__main__":
try:
main()
except Exception as e:
print(f"[session-start] error: {e}", file=sys.stderr)
sys.exit(0)