LastNoob commited on
Commit
e057fff
·
1 Parent(s): 9d83aff

fix(messaging): reuse parent CLI session for Telegram reply continuation (#233)

Browse files

Pass parent_session_id into get_or_create_session so reply nodes align with
the fork/resume path instead of always allocating a fresh pending session.
Add unit coverage and update integration expectations.

messaging/handler.py CHANGED
@@ -370,7 +370,9 @@ class ClaudeMessageHandler:
370
  cli_session,
371
  session_or_temp_id,
372
  is_new,
373
- ) = await self.cli_manager.get_or_create_session(session_id=None)
 
 
374
  if is_new:
375
  temp_session_id = session_or_temp_id
376
  else:
 
370
  cli_session,
371
  session_or_temp_id,
372
  is_new,
373
+ ) = await self.cli_manager.get_or_create_session(
374
+ session_id=parent_session_id
375
+ )
376
  if is_new:
377
  temp_session_id = session_or_temp_id
378
  else:
tests/messaging/test_handler.py CHANGED
@@ -419,6 +419,54 @@ async def test_process_node_success_flow(handler, mock_cli_manager, mock_platfor
419
  assert "✅ *Complete*" in last_call[0][2]
420
  assert "Hello world" in last_call[0][2]
421
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
 
423
  @pytest.mark.asyncio
424
  async def test_process_node_error_flow(handler, mock_cli_manager, mock_platform):
 
419
  assert "✅ *Complete*" in last_call[0][2]
420
  assert "Hello world" in last_call[0][2]
421
 
422
+ mock_cli_manager.get_or_create_session.assert_awaited_once_with(session_id=None)
423
+ mock_session.start_task.assert_called_once()
424
+ st_kw = mock_session.start_task.call_args
425
+ assert st_kw.kwargs.get("session_id") is None
426
+ assert st_kw.kwargs.get("fork_session") is False
427
+
428
+
429
+ @pytest.mark.asyncio
430
+ async def test_process_node_reply_uses_parent_session_for_manager_and_fork(
431
+ handler, mock_cli_manager, mock_platform
432
+ ):
433
+ """Telegram follow-ups must reuse parent Claude session (issue #233)."""
434
+ node_id = "child_1"
435
+ mock_node = MagicMock()
436
+ mock_node.incoming.chat_id = "chat_1"
437
+ mock_node.incoming.text = "follow up"
438
+ mock_node.status_message_id = "status_child"
439
+ mock_node.parent_id = "root_msg"
440
+
441
+ parent_claude_session = "claude_sess_parent"
442
+ mock_session = MagicMock()
443
+ mock_session.start_task.return_value = mock_async_gen([{"type": "exit", "code": 0}])
444
+ mock_cli_manager.get_or_create_session.return_value = (
445
+ mock_session,
446
+ parent_claude_session,
447
+ False,
448
+ )
449
+
450
+ mock_tree = MagicMock()
451
+ mock_tree.update_state = AsyncMock()
452
+ mock_tree.root_id = "root_msg"
453
+ mock_tree.to_dict.return_value = {}
454
+ mock_tree.get_parent_session_id = MagicMock(return_value=parent_claude_session)
455
+
456
+ with patch.object(
457
+ handler.tree_queue, "get_tree_for_node", MagicMock(return_value=mock_tree)
458
+ ):
459
+ await handler._process_node(node_id, mock_node)
460
+
461
+ mock_tree.get_parent_session_id.assert_called_once_with(node_id)
462
+ mock_cli_manager.get_or_create_session.assert_awaited_once_with(
463
+ session_id=parent_claude_session
464
+ )
465
+ mock_session.start_task.assert_called_once()
466
+ st_kw = mock_session.start_task.call_args
467
+ assert st_kw.kwargs.get("session_id") == parent_claude_session
468
+ assert st_kw.kwargs.get("fork_session") is True
469
+
470
 
471
  @pytest.mark.asyncio
472
  async def test_process_node_error_flow(handler, mock_cli_manager, mock_platform):
tests/messaging/test_handler_integration.py CHANGED
@@ -95,7 +95,7 @@ async def test_full_conversation_flow_single_user(
95
 
96
  assert tree.get_node("m2").state.value == MessageState.COMPLETED.value
97
  assert tree.get_node("m2").parent_id == "m1"
98
- mock_cli_manager.get_or_create_session.assert_called_with(session_id=None)
99
  mock_session2.start_task.assert_called_with(
100
  "message 2", session_id="sess1", fork_session=True
101
  )
 
95
 
96
  assert tree.get_node("m2").state.value == MessageState.COMPLETED.value
97
  assert tree.get_node("m2").parent_id == "m1"
98
+ mock_cli_manager.get_or_create_session.assert_called_with(session_id="sess1")
99
  mock_session2.start_task.assert_called_with(
100
  "message 2", session_id="sess1", fork_session=True
101
  )