From bbe0209403709608e628074d7d3c0f6d6937889a Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 8 Mar 2026 21:17:27 -0400 Subject: [PATCH] feat(logs): Harden session restoration for legacy logs and offloaded data resolution --- src/app_controller.py | 56 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/src/app_controller.py b/src/app_controller.py index 025c287..d7324b1 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -821,13 +821,35 @@ class AppController: log_path = Path(path) if log_path.is_dir(): log_file = log_path / "comms.log" + session_dir = log_path else: log_file = log_path + session_dir = log_path.parent if not log_file.exists(): self._set_status(f"log file not found: {log_file}") return + def _resolve_log_ref(content: Any, session_dir: Path) -> str: + if not content or not isinstance(content, str) or "[REF:" not in content: + return str(content) if content is not None else "" + pattern = r'\[REF:([^\]]+)\]' + def replace_ref(match): + ref_file = match.group(1) + paths_to_check = [ + session_dir / "outputs" / ref_file, + session_dir / "scripts" / ref_file + ] + for p in paths_to_check: + if p.exists(): + try: + with open(p, "r", encoding="utf-8") as rf: + return rf.read() + except Exception: + return f"[ERROR READING REF: {ref_file}]" + return match.group(0) + return re.sub(pattern, replace_ref, content) + entries = [] disc_entries = [] try: @@ -838,35 +860,57 @@ class AppController: try: entry = json.loads(line) entries.append(entry) - kind = entry.get("kind") + kind = entry.get("kind", entry.get("type", "")) payload = entry.get("payload", {}) ts = entry.get("ts", "") if kind == "history_add": + content = payload.get("content", payload.get("text", payload.get("message", ""))) + content = _resolve_log_ref(content, session_dir) disc_entries.append({ "role": payload.get("role", "AI"), - "content": payload.get("content", ""), + "content": content, "collapsed": payload.get("collapsed", False), "ts": ts }) elif kind == "request": + content = payload.get("message", payload.get("content", payload.get("text", ""))) + content = _resolve_log_ref(content, session_dir) disc_entries.append({ "role": "User", - "content": payload.get("message", ""), + "content": content, "collapsed": False, "ts": ts }) elif kind == "response": + text = payload.get("text", payload.get("content", payload.get("message", ""))) + text = _resolve_log_ref(text, session_dir) + tool_calls = payload.get("tool_calls", []) + content = text + if tool_calls: + try: + tc_str = json.dumps(tool_calls, indent=1) + if content: + content += f"\n\n[TOOL CALLS]\n{tc_str}" + else: + content = f"[TOOL CALLS]\n{tc_str}" + except: + if content: + content += f"\n\n[TOOL CALLS PRESENT]" + else: + content = "[TOOL CALLS PRESENT]" disc_entries.append({ "role": "AI", - "content": payload.get("text", ""), + "content": content, "collapsed": False, "ts": ts }) elif kind == "tool_result": + output = payload.get("output", payload.get("content", "")) + output = _resolve_log_ref(output, session_dir) disc_entries.append({ "role": "Tool", - "content": f"[TOOL RESULT]\n{payload.get('output', '')}", + "content": f"[TOOL RESULT]\n{output}", "collapsed": True, "ts": ts }) @@ -879,7 +923,7 @@ class AppController: self.prior_session_entries = entries self.prior_disc_entries = disc_entries self.is_viewing_prior_session = True - self._set_status(f"viewing prior session: {log_path.name} ({len(entries)} entries)") + self._set_status(f"viewing prior session: {session_dir.name} ({len(entries)} entries)") def cb_prune_logs(self) -> None: """Manually triggers the log pruning process with aggressive thresholds."""