Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/bot/handlers/callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
29 changes: 29 additions & 0 deletions src/claude/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/claude/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
1 change: 1 addition & 0 deletions src/claude/sdk_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
58 changes: 47 additions & 11 deletions src/claude/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down