From d087b76bba891ee2a40d1deafd776e70c2803bbe Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Sat, 20 Jun 2026 06:14:58 +0800 Subject: [PATCH] Fix delayed goal submit detection --- src/telegram_codex_bot/tmux_manager.py | 27 ++++++++----- tests/telegram_codex_bot/test_tmux_manager.py | 40 ++++++++++++++++++- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/telegram_codex_bot/tmux_manager.py b/src/telegram_codex_bot/tmux_manager.py index 4ec9e14..14dc792 100644 --- a/src/telegram_codex_bot/tmux_manager.py +++ b/src/telegram_codex_bot/tmux_manager.py @@ -48,6 +48,8 @@ r"^[•◦]\s*Waiting for background terminal\b", re.IGNORECASE ) _HOOK_TRUST_BYPASS_FLAG = "--dangerously-bypass-hook-trust" +_SUBMIT_SETTLE_CHECKS = 8 +_SUBMIT_SETTLE_INTERVAL_SECONDS = 0.5 def _resume_target_id(session_id: str) -> str: @@ -697,16 +699,21 @@ def _send_control_key(key: str) -> bool: window_id, text, ): - # Codex can briefly leave the submitted prompt rendered near the - # footer before the working status is painted. Give the TUI one - # final settle window so a successful submit is not reported as - # a send failure. - await asyncio.sleep(1.0) - if await asyncio.to_thread( - self._pane_still_has_pending_literal_input, - window_id, - text, - ): + submitted_after_settle = False + for _ in range(_SUBMIT_SETTLE_CHECKS): + # Codex can leave submitted /goal prompts rendered for a + # few seconds before the Pursuing goal status is painted. + # Poll briefly so a slow successful submit is not + # reported as a send failure. + await asyncio.sleep(_SUBMIT_SETTLE_INTERVAL_SECONDS) + if not await asyncio.to_thread( + self._pane_still_has_pending_literal_input, + window_id, + text, + ): + submitted_after_settle = True + break + if not submitted_after_settle: logger.error( "Codex prompt still appears pending in window %s after retry", window_id, diff --git a/tests/telegram_codex_bot/test_tmux_manager.py b/tests/telegram_codex_bot/test_tmux_manager.py index 7e637da..4a7e598 100644 --- a/tests/telegram_codex_bot/test_tmux_manager.py +++ b/tests/telegram_codex_bot/test_tmux_manager.py @@ -683,6 +683,39 @@ async def test_send_keys_allows_slow_submit_status_after_retry(self) -> None: self.assertTrue(ok) self.assertEqual(run_mock.call_count, 2) + async def test_send_keys_waits_for_goal_status_after_retry_settle(self) -> None: + pane = _SendKeysDummyPane() + window = _SendKeysDummyWindow(pane) + session = _SendKeysDummySession(window) + manager = tmux_manager_module.TmuxManager( + session_name="telegram-codex-bot-test" + ) + + with ( + patch.object(manager, "get_session", return_value=session), + patch( + "telegram_codex_bot.tmux_manager.subprocess.run", + return_value=subprocess.CompletedProcess( + args=["tmux", "send-keys"], returncode=0 + ), + ) as run_mock, + patch.object( + manager, + "_pane_still_has_pending_literal_input", + side_effect=[True, True, True, True, False], + ) as pending_mock, + patch.object(manager, "_pane_has_insert_overlay", return_value=False), + patch( + "telegram_codex_bot.tmux_manager.asyncio.sleep", + new_callable=AsyncMock, + ), + ): + ok = await manager.send_keys("@9", "/goal 按照你的建议全部完善优化") + + self.assertTrue(ok) + self.assertEqual(run_mock.call_count, 2) + self.assertEqual(pending_mock.call_count, 5) + async def test_send_keys_returns_false_when_retry_leaves_prompt_pending( self, ) -> None: @@ -704,9 +737,14 @@ async def test_send_keys_returns_false_when_retry_leaves_prompt_pending( patch.object( manager, "_pane_still_has_pending_literal_input", - side_effect=[True, True, True], + side_effect=[True, True] + + [True] * tmux_manager_module._SUBMIT_SETTLE_CHECKS, ), patch.object(manager, "_pane_has_insert_overlay", return_value=False), + patch( + "telegram_codex_bot.tmux_manager.asyncio.sleep", + new_callable=AsyncMock, + ), ): ok = await manager.send_keys("@9", "hello")