fix(cli): sync thread ID and render clickable link on /clear (#3305)

The `/clear` slash command was resetting the conversation thread but
leaving the internal thread ID stale and rendering the new thread ID as
plain text. Bring it in line with the resumed-thread flow by
synchronizing the local thread variable and promoting the confirmation
message to a clickable link.

## Changes
- `/clear` now assigns the new thread ID to `_lc_thread_id`, preventing
the CLI from tracking a stale ID after a thread reset
- Thread reset confirmation is now scheduled for link upgrade via
`_schedule_thread_message_link`, matching the clickable thread ID
pattern used on thread resume
- Added unit test coverage validating the `/clear` command's thread ID
synchronization and UI link scheduling behavior
This commit is contained in:
Mason Daugherty
2026-05-10 23:18:28 -04:00
committed by GitHub
parent c21dee2e9b
commit 0904974d52
2 changed files with 43 additions and 2 deletions
+7 -2
View File
@@ -4812,13 +4812,18 @@ class DeepAgentsApp(App):
# Reset thread to start fresh conversation
if self._session_state:
new_thread_id = self._session_state.reset_thread()
self._lc_thread_id = new_thread_id
try:
banner = self.query_one("#welcome-banner", WelcomeBanner)
banner.update_thread_id(new_thread_id)
except NoMatches:
pass
await self._mount_message(
AppMessage(f"Started new thread: {new_thread_id}")
thread_msg_widget = AppMessage(f"Started new thread: {new_thread_id}")
await self._mount_message(thread_msg_widget)
self._schedule_thread_message_link(
thread_msg_widget,
prefix="Started new thread",
thread_id=new_thread_id,
)
elif cmd == "/copy":
await self._mount_message(UserMessage(command))
+36
View File
@@ -2562,6 +2562,42 @@ class TestTraceCommand:
assert isinstance(app.screen, AuthManagerScreen)
class TestClearCommand:
"""Test /clear slash command."""
async def test_clear_syncs_thread_id_and_schedules_link_upgrade(self) -> None:
"""/clear should render the new ID like the resumed-thread footer."""
app = DeepAgentsApp(thread_id="old-thread")
async with app.run_test() as pilot:
await pilot.pause()
app._session_state = TextualSessionState(thread_id="old-thread")
app._lc_thread_id = "old-thread"
with (
patch("deepagents_cli.app._new_thread_id", return_value="new-thread"),
patch.object(app, "_schedule_thread_message_link") as schedule,
):
await app._handle_command("/clear")
await pilot.pause()
assert app._session_state.thread_id == "new-thread"
assert app._lc_thread_id == "new-thread"
app_msgs = list(app.query(AppMessage))
assert any(
str(widget._content) == "Started new thread: new-thread"
for widget in app_msgs
)
schedule.assert_called_once()
widget = schedule.call_args.args[0]
assert isinstance(widget, AppMessage)
assert widget in app_msgs
assert schedule.call_args.kwargs == {
"prefix": "Started new thread",
"thread_id": "new-thread",
}
class TestCopyCommand:
"""Tests for `/copy` command behavior."""