fix(tui): preserve draft scope until first message

This commit is contained in:
Xubin Ren
2026-08-24 10:40:16 +08:00
parent d50a2fab32
commit baa0233377
5 changed files with 79 additions and 8 deletions
+1 -1
View File
@@ -1217,7 +1217,6 @@ class WebSocketChannel(BaseChannel):
if session_mentions:
metadata["session_mentions"] = session_mentions
metadata[WORKSPACE_SCOPE_METADATA_KEY] = scope.metadata()
self._workspaces.persist_scope(cid, scope)
is_webui = metadata.get("webui") is True
queued_owner = None
if is_webui and not is_user_shell and builtin_command_starts_agent_turn(content):
@@ -1272,6 +1271,7 @@ class WebSocketChannel(BaseChannel):
else False
),
)
self._workspaces.persist_scope(cid, scope)
accepted = True
finally:
if not accepted and queued_owner is not None:
@@ -1563,6 +1563,49 @@ async def test_new_chat_without_message_does_not_create_session(
assert sessions.list_sessions() == []
@pytest.mark.asyncio
async def test_failed_first_message_does_not_persist_draft_session(
bus: MagicMock,
tmp_path,
) -> None:
sessions = SessionManager(tmp_path / "sessions")
channel = WebSocketChannel(
{"enabled": True, "allowFrom": ["*"], "host": "127.0.0.1"},
bus,
gateway=_basic_handler(bus, session_manager=sessions, workspace_path=tmp_path),
)
conn = AsyncMock()
conn.remote_address = ("127.0.0.1", 50123)
await channel._dispatch_envelope(
conn,
"tui-client",
{
"type": "new_chat",
"workspace_scope": {
"project_path": str(tmp_path),
"access_mode": "full",
},
},
)
chat_id = json.loads(conn.send.await_args_list[0].args[0])["chat_id"]
bus.publish_inbound.side_effect = RuntimeError("queue unavailable")
with pytest.raises(RuntimeError, match="queue unavailable"):
await channel._dispatch_envelope(
conn,
"tui-client",
{
"type": "message",
"chat_id": chat_id,
"content": "hello",
"webui": True,
},
)
assert sessions.list_sessions() == []
@pytest.mark.asyncio
async def test_workspace_scope_change_invalidates_other_attached_clients(
bus: MagicMock,
+1 -1
View File
@@ -336,12 +336,12 @@ class WebUIWorkspaceController:
def persist_scope(self, chat_id: str, scope: WorkspaceScope) -> None:
session_key = f"websocket:{chat_id}"
self._draft_scopes.pop(session_key, None)
if self._sessions is not None:
session = self._sessions.get_or_create(session_key)
session.metadata["webui"] = True
session.metadata[WORKSPACE_SCOPE_METADATA_KEY] = scope.metadata()
self._sessions.save(session)
self._draft_scopes.pop(session_key, None)
def stage_scope(self, chat_id: str, scope: WorkspaceScope) -> None:
"""Keep a new chat's scope transient until its first accepted message."""