Skip to content

fix(chat): broadcast SSE HITL resume to thread subscribers - #1291

Open
huangcheng wants to merge 1 commit into
TencentCloud:developfrom
huangcheng:fix/hitl-sse-resume-notify
Open

huangcheng wants to merge 1 commit into
TencentCloud:developfrom
huangcheng:fix/hitl-sse-resume-notify

Conversation

@huangcheng

@huangcheng huangcheng commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A HITL pause resolved outside a dashboard page (SSE resume from another client, IM, or API) left the page's approval/ask card visible until a manual reload.

Root cause: turns initiated over WebSocket broadcast their chunks to the thread's subscribers (ws_channel → hub.push_to_thread), but the SSE resume path (POST /agents/{agent_id}/chat/hitl/resume) only streamed to the HTTP caller — open pages never learned the pause was resolved elsewhere.

Fix

Mirror the ws_channel contract in iter_dashboard_hitl_resume_sse (src/octop/api/routers/chat/routes.py):

  • While streaming the resumed turn, push every chunk (plus done, and error+done on failure) to the thread's WebSocket subscribers.
  • Keep the hub's turn flag in sync (mark_turn_active / mark_turn_idle) so history turn_active stays accurate for SSE resumes too.
  • Delivery failures are logged and never abort the harness turn (same contract as ws_channel).
  • Subscribers keep receiving frames even after the SSE client disconnects (the turn continues server-side by design).

No frontend change needed: subscribed pages are already driven by WS frames, so the pending card dismisses in place.

Verification

  • New unit tests in tests/unit/api/test_chat_hitl_resume.py (real WebSocketHub + fake subscriber):
    • chunks + done are broadcast in order and the turn flag returns to idle;
    • broadcast continues after the SSE client disconnects.
  • Gates run manually: ruff check/format, mypy --strict src/octop, full pytest -m "not live" — green (committed with SKIP_PRECOMMIT=1 because make is unavailable in this Windows shell; the equivalent checks were run manually).
  • Live verification against a real model turn (MiniMax): pending ask_user_question card on an open page dismissed without reload after the pause was answered out-of-band via the API; the page continued streaming the resumed turn.

@jubaoliang

Copy link
Copy Markdown
Collaborator

分诊:问题真实(SSE HITL resume 不 push_to_thread,页外解决后面卡片卡住)。

请 rebase,CHANGELOG 改成中文并放到当前 Unreleased。CI 已绿。

A HITL pause resolved outside a dashboard page (SSE resume from another
client, IM, or API) left the page's approval/ask card visible until a
manual reload: turns initiated over WebSocket broadcast their chunks to
the thread's subscribers, but the SSE resume path only streamed to the
HTTP caller.

Mirror the ws_channel contract in iter_dashboard_hitl_resume_sse: while
streaming the resumed turn, also push every chunk (plus done/error) to
the thread's WebSocket subscribers and keep the hub's turn flag in sync.
Delivery failures are logged and never abort the turn, and subscribers
keep receiving frames even after the SSE client disconnects. Open pages
now see the remote resolution live and dismiss the card without reload.

Verified live against a real MiniMax turn: pending ask card on an open
page dismissed in place after the pause was answered via the API.

Note: committed with SKIP_PRECOMMIT=1 because make is unavailable in
this Windows shell; the equivalent gates (ruff check/format, mypy
--strict, full pytest) were run manually and are green.

Co-authored-by: HUANG Cheng <cheng@duck.com>
@cursor
cursor Bot force-pushed the fix/hitl-sse-resume-notify branch from 58b851a to e59641b Compare October 4, 2026 08:34

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants