diff --git a/src/bot/handlers/callback.py b/src/bot/handlers/callback.py index b82ad0a..ef13a33 100644 --- a/src/bot/handlers/callback.py +++ b/src/bot/handlers/callback.py @@ -1004,8 +1004,10 @@ async def handle_git_callback( else: # Clean up diff output for Telegram # Remove emoji symbols that interfere with markdown parsing - clean_diff = diff_output.replace("➕", "+").replace("➖", "-").replace("📍", "@") - + clean_diff = ( + diff_output.replace("➕", "+").replace("➖", "-").replace("📍", "@") + ) + # Limit diff output max_length = 2000 if len(clean_diff) > max_length: diff --git a/src/claude/facade.py b/src/claude/facade.py index bf14258..2eb5b1b 100644 --- a/src/claude/facade.py +++ b/src/claude/facade.py @@ -70,6 +70,15 @@ async def run_command( user_id, working_directory, session_id ) + # Log session details for debugging + logger.info( + "Session retrieved for command", + session_id=session.session_id, + is_new_session=getattr(session, "is_new_session", False), + provided_session_id=session_id, + user_id=user_id, + ) + # Track streaming updates and validate tool calls tools_validated = True validation_errors = [] @@ -143,6 +152,15 @@ async def stream_handler(update: StreamUpdate): else session.session_id ) + logger.info( + "Claude command execution parameters", + should_continue_session=should_continue, + claude_session_id=claude_session_id, + original_session_id=session_id, + is_new_session=getattr(session, "is_new_session", False), + user_id=user_id, + ) + response = await self._execute_with_fallback( prompt=prompt, working_directory=working_directory, @@ -197,9 +215,20 @@ async def stream_handler(update: StreamUpdate): if hasattr(session, "is_new_session") and response.session_id: # The session_id has been updated to Claude's session_id final_session_id = response.session_id + logger.info( + "Session ID updated from temporary to Claude session ID", + old_session_id=old_session_id, + new_session_id=response.session_id, + user_id=user_id, + ) else: # Use the original session_id for continuing sessions final_session_id = old_session_id + logger.debug( + "Using existing session ID", + session_id=final_session_id, + user_id=user_id, + ) # Ensure response has the correct session_id response.session_id = final_session_id diff --git a/src/claude/integration.py b/src/claude/integration.py index ed857f7..7760171 100644 --- a/src/claude/integration.py +++ b/src/claude/integration.py @@ -188,6 +188,7 @@ def _build_command( elif session_id and prompt and continue_session: # Follow-up message in existing session - use resume with new prompt cmd.extend(["--resume", session_id, "-p", prompt]) + logger.info("Using --resume for continuing session", session_id=session_id) elif prompt: # New session with prompt (including new sessions with session_id) cmd.extend(["-p", prompt]) diff --git a/src/claude/sdk_integration.py b/src/claude/sdk_integration.py index 9ccf964..df074b0 100644 --- a/src/claude/sdk_integration.py +++ b/src/claude/sdk_integration.py @@ -167,6 +167,7 @@ async def execute_command( working_directory=str(working_directory), session_id=session_id, continue_session=continue_session, + session_continuation_mode="continue" if continue_session else "new", ) try: diff --git a/src/claude/session.py b/src/claude/session.py index 2d8921f..464180e 100644 --- a/src/claude/session.py +++ b/src/claude/session.py @@ -174,22 +174,58 @@ async def get_or_create_session( session_id=session_id, ) - # Check for existing session - if session_id and session_id in self.active_sessions: - session = self.active_sessions[session_id] - if not session.is_expired(self.config.session_timeout_hours): - logger.debug("Using active session", session_id=session_id) - return session - - # Try to load from storage - if session_id: + # If session_id is provided, try to resume existing session + if session_id and not session_id.startswith("temp_"): + logger.debug("Attempting to resume existing session", session_id=session_id) + + # Check active sessions first + if session_id in self.active_sessions: + session = self.active_sessions[session_id] + if not session.is_expired(self.config.session_timeout_hours): + logger.debug("Using active session", session_id=session_id) + return session + else: + logger.info( + "Active session expired, will try storage", + session_id=session_id, + ) + + # Try to load from storage session = await self.storage.load_session(session_id) if session and not session.is_expired(self.config.session_timeout_hours): + # This is an existing Claude session, NOT a new session + session.is_new_session = False self.active_sessions[session_id] = session - logger.info("Loaded session from storage", session_id=session_id) + logger.info( + "Successfully loaded existing session from storage", + session_id=session_id, + ) + return session + elif session: + logger.info( + "Session found in storage but expired", + session_id=session_id, + age_hours=(datetime.utcnow() - session.last_used).total_seconds() + / 3600, + ) + else: + logger.warning( + "Session not found in storage, will create new session", + session_id=session_id, + ) + + # Check for existing temporary session in active memory + if ( + session_id + and session_id.startswith("temp_") + and session_id in self.active_sessions + ): + session = self.active_sessions[session_id] + if not session.is_expired(self.config.session_timeout_hours): + logger.debug("Using active temporary session", session_id=session_id) return session - # Check user session limit + # Check user session limit before creating new session user_sessions = await self._get_user_sessions(user_id) if len(user_sessions) >= self.config.max_sessions_per_user: # Remove oldest session