fix(code): consistent styling and links for thread status messages - #5118
Merged
Mason Daugherty (mdrxy) merged 6 commits intoJul 29, 2026
Merged
Conversation
`AppMessage` dims plain-string messages but leaves pre-built `Content` untouched, so thread notes brightened as soon as their LangSmith link resolved. `_build_thread_message` now carries dim italic spans, the "Already on thread" note resolves a link like its siblings, and hovering an embedded link in any app message switches the mouse pointer. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Re-selecting the thread the session is already on is transient feedback, not part of the conversation — the same reasoning that already makes the same-model no-op a toast. Hoists that path's dedup guard into `_notify_unchanged_once` so both share one implementation and neither can stack duplicate toasts while the previous one is still on-screen. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
…sage-link-styling # Conflicts: # libs/code/deepagents_code/app.py
Correct `_last_thread_unchanged`'s docstring, which called the field the same-model counterpart of `_last_model_unchanged` while its own summary line calls it the same-thread toast. Log the same-thread no-op resume. The `Already on thread` notice is a toast, which is transient and suppressible, and `/threads -r` mounts the user's command echo before dispatching -- so a no-op left the transcript showing the command and nothing else, indistinguishable from a dropped command. The same-model path already logs unconditionally; this mirrors it, outside the dedup guard. Document what `_notify_unchanged_once` actually guarantees: suppression is time-based and never inspects live toast state, so "on-screen" is a presumption -- a toast clicked away still suppresses. Its window matches the toast lifetime only because `notify` is called without a `timeout` override, which nothing else records. Note that callers own the record, and give `markup=False` the reason it is load-bearing. The return type drops `| None`: the suppression branch is guarded by `last is not None`, so only the explicit re-arm sites clear a caller's field. Replace two tests that could not fail: - `test_hovering_text_keeps_text_pointer` asserted `pointer == "text"`, already the CSS default on a fresh widget. Deleting the handler's entire `else "text"` arm left it green. The new test covers what that arm exists for -- moving off a link onto plain text without leaving the widget, where `on_leave` cannot help and the hand cursor would otherwise stick. - `test_linked_content_matches_plain_app_message_styling` hardcoded the same `dim`/`italic` literals as the implementation, so a change to `AppMessage` could break the parity it names while staying green. It now reads the expected style off a real `AppMessage`. Make the re-arm test behavioral. It asserted private state directly and passed even with the helper broken; it now pins the clock and asserts two toasts across an A -> B -> A round trip, mirroring the same-model counterpart.
Mason Daugherty (mdrxy)
marked this pull request as ready for review
July 29, 2026 20:59
…nter Three fixes in the code paths this branch already touches, each of which was invisible rather than wrong. `_build_thread_message` swallowed every LangSmith URL failure without a trace. Unlinked thread IDs are the only symptom, and they look identical to tracing simply not being configured -- so a rejected `LANGSMITH_API_KEY`, a changed payload shape, or a network failure left the user with no diagnostic and no reason to suspect misconfiguration. Log the cause at debug with `exc_info`. The `except (TimeoutError, Exception)` tuple collapses to `except Exception`, which is all it ever meant (`TimeoutError` inherits from `OSError`); its `noqa: BLE001` is dropped because the rule was flagging the missing log, not the breadth of the catch. `_resume_thread` set `previous_thread_id` once the switch was materially complete, but `_run_session_start_hook` runs after that and can raise -- it awaits `on_session_start` and `_mount_message` with no guard. Rollback restored `thread_id` and `_lc_thread_id` but not the back-pointer, so a raise there left `previous == current`: a later bare `/threads -r` resolved to the thread already active and reported the no-op, with nowhere to step back to. Capture and restore it alongside the other rollback state. The comment claiming the assignment sits after "the last statement that can raise" is corrected -- it never did. Add the one test that verifies Textual actually delivers hover events to these widgets. There are ten `on_mouse_move` handlers across seven widget files and every test for them calls the handler directly with a stand-in event, so nothing anywhere proved `MouseMove` reaches the widget or that `event.style` is populated at the hovered offset. One `pilot.hover` test over a real OSC 8 span covers the assumption the whole family shares.
Mason Daugherty (mdrxy)
deleted the
mdrxy/code/thread-message-link-styling
branch
July 29, 2026 21:15
Marcelo5444
pushed a commit
to Marcelo5444/deepagents
that referenced
this pull request
Jul 30, 2026
…angchain-ai#5118) Thread status notes in the transcript (`Resumed thread:`, `Started new thread:`, `Previous thread:`) now all render with the same dim italic styling, and the mouse pointer changes to a hand when hovering a link inside any app message. Re-selecting the thread the session is already on now shows a transient toast instead of an inline chat message, matching the existing same-model behavior. --- Several related inconsistencies, all visible in a single resume: - `AppMessage` applies `dim italic` only when it is handed a plain string; a pre-built `Content` is rendered as-is. Thread notes start life as plain strings and are upgraded to linked `Content` once the LangSmith URL resolves, so they visibly brightened relative to every other app message. `_build_thread_message` now styles each span `dim italic`, matching what `AppMessage` does for strings (and what other linked app messages such as `/trace` output already do explicitly). - `AppMessage` carries a `text` pointer in CSS with no hover handling, so an embedded link gave no cursor affordance. It now toggles the pointer on `MouseMove`/`Leave` using the existing `event_targets_link` helper, mirroring `AssistantMessage`. Link clicks still route through `open_style_link`, so URL safety checks are unchanged. - The `Already on thread` note was the odd one out: an inline transcript message for something that changed no state, where the equivalent `Already using <model>` no-op is a toast. It is now a toast too. Its dedup guard — don't stack an identical toast while the previous one is presumed still on-screen, but do re-announce once that has expired — is hoisted into `_notify_unchanged_once` and shared by both call sites. A successful thread switch re-arms the toast. The sibling `Switched to thread directory` message stays inline because it records a real state change. Made by [Open SWE](https://openswe.vercel.app/agents/4b51fe78-171c-1374-8355-489bab7e616d) --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Mason Daugherty (mdrxy)
pushed a commit
that referenced
this pull request
Jul 30, 2026
> [!CAUTION] > Merging this PR will automatically publish to **PyPI** and create a **GitHub release**. For the full release process, see [`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md). --- _Release notes preview: keep this section in sync with the package `CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`, not this PR description — keep them aligned anyway so the PR stays an accurate historical record for reviewers and anyone returning later._ --- ## [0.1.50](deepagents-code==0.1.49...deepagents-code==0.1.50) (2026-07-30) ### Highlights - Added project hooks workspace trust and expanded Hooks v2 support with client and server lifecycle events plus runtime feedback ([#5105](#5105), [#5104](#5104), [#4997](#4997), [#5045](#5045)). - Added an option to mute the “YOLO is active” toast ([#5103](#5103)). - Made the splash screen `thread` ID clickable to copy it ([#5173](#5173)). - Show `ask_user` answers directly on the answered tool row ([#5100](#5100)). - Show a toast when submitting an empty required `ask_user` answer ([#5095](#5095)). - Added thread message counts to the Debug Console ([#5117](#5117)). ### Fixes and improvements - Gated Hooks v2 behind `DEEPAGENTS_CODE_EXPERIMENTAL` and improved hook resume stability across identity and Command tool results ([#5146](#5146), [#5176](#5176)). - Kept server hook state out of task results ([#5164](#5164)). - Stopped duplicate Auto transcript events during interrupt replay ([#5157](#5157)). - Kept `/update` and `/install --package` prompts responsive ([#5127](#5127)). - Refreshed the `/threads` cache after each turn ([#5174](#5174)). - Anchored toasts above the chat input and added a toast when media is dropped into a free-text question ([#5101](#5101), [#5099](#5099)). - Improved thread status message styling and links ([#5118](#5118)). - Made resume hints echo the launched command name ([#5119](#5119)). - Scoped selection copy to the clicked screen ([#5140](#5140)). - Ignored mouse hits on detached widgets ([#5114](#5114)). _End release notes preview._ --- > [!NOTE] > A **New Contributors** section is appended to the GitHub release notes automatically at publish time (see [Release Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline), step 2). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com> Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
Mason Daugherty (mdrxy)
added a commit
that referenced
this pull request
Jul 30, 2026
…5118) Thread status notes in the transcript (`Resumed thread:`, `Started new thread:`, `Previous thread:`) now all render with the same dim italic styling, and the mouse pointer changes to a hand when hovering a link inside any app message. Re-selecting the thread the session is already on now shows a transient toast instead of an inline chat message, matching the existing same-model behavior. --- Several related inconsistencies, all visible in a single resume: - `AppMessage` applies `dim italic` only when it is handed a plain string; a pre-built `Content` is rendered as-is. Thread notes start life as plain strings and are upgraded to linked `Content` once the LangSmith URL resolves, so they visibly brightened relative to every other app message. `_build_thread_message` now styles each span `dim italic`, matching what `AppMessage` does for strings (and what other linked app messages such as `/trace` output already do explicitly). - `AppMessage` carries a `text` pointer in CSS with no hover handling, so an embedded link gave no cursor affordance. It now toggles the pointer on `MouseMove`/`Leave` using the existing `event_targets_link` helper, mirroring `AssistantMessage`. Link clicks still route through `open_style_link`, so URL safety checks are unchanged. - The `Already on thread` note was the odd one out: an inline transcript message for something that changed no state, where the equivalent `Already using <model>` no-op is a toast. It is now a toast too. Its dedup guard — don't stack an identical toast while the previous one is presumed still on-screen, but do re-announce once that has expired — is hoisted into `_notify_unchanged_once` and shared by both call sites. A successful thread switch re-arms the toast. The sibling `Switched to thread directory` message stays inline because it records a real state change. Made by [Open SWE](https://openswe.vercel.app/agents/4b51fe78-171c-1374-8355-489bab7e616d) --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Mason Daugherty (mdrxy)
pushed a commit
that referenced
this pull request
Jul 30, 2026
> [!CAUTION] > Merging this PR will automatically publish to **PyPI** and create a **GitHub release**. For the full release process, see [`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md). --- _Release notes preview: keep this section in sync with the package `CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`, not this PR description — keep them aligned anyway so the PR stays an accurate historical record for reviewers and anyone returning later._ --- ## [0.1.50](deepagents-code==0.1.49...deepagents-code==0.1.50) (2026-07-30) ### Highlights - Added project hooks workspace trust and expanded Hooks v2 support with client and server lifecycle events plus runtime feedback ([#5105](#5105), [#5104](#5104), [#4997](#4997), [#5045](#5045)). - Added an option to mute the “YOLO is active” toast ([#5103](#5103)). - Made the splash screen `thread` ID clickable to copy it ([#5173](#5173)). - Show `ask_user` answers directly on the answered tool row ([#5100](#5100)). - Show a toast when submitting an empty required `ask_user` answer ([#5095](#5095)). - Added thread message counts to the Debug Console ([#5117](#5117)). ### Fixes and improvements - Gated Hooks v2 behind `DEEPAGENTS_CODE_EXPERIMENTAL` and improved hook resume stability across identity and Command tool results ([#5146](#5146), [#5176](#5176)). - Kept server hook state out of task results ([#5164](#5164)). - Stopped duplicate Auto transcript events during interrupt replay ([#5157](#5157)). - Kept `/update` and `/install --package` prompts responsive ([#5127](#5127)). - Refreshed the `/threads` cache after each turn ([#5174](#5174)). - Anchored toasts above the chat input and added a toast when media is dropped into a free-text question ([#5101](#5101), [#5099](#5099)). - Improved thread status message styling and links ([#5118](#5118)). - Made resume hints echo the launched command name ([#5119](#5119)). - Scoped selection copy to the clicked screen ([#5140](#5140)). - Ignored mouse hits on detached widgets ([#5114](#5114)). _End release notes preview._ --- > [!NOTE] > A **New Contributors** section is appended to the GitHub release notes automatically at publish time (see [Release Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline), step 2). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com> Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thread status notes in the transcript (
Resumed thread:,Started new thread:,Previous thread:) now all render with the same dim italic styling, and the mouse pointer changes to a hand when hovering a link inside any app message. Re-selecting the thread the session is already on now shows a transient toast instead of an inline chat message, matching the existing same-model behavior.Several related inconsistencies, all visible in a single resume:
AppMessageappliesdim italiconly when it is handed a plain string; a pre-builtContentis rendered as-is. Thread notes start life as plain strings and are upgraded to linkedContentonce the LangSmith URL resolves, so they visibly brightened relative to every other app message._build_thread_messagenow styles each spandim italic, matching whatAppMessagedoes for strings (and what other linked app messages such as/traceoutput already do explicitly).AppMessagecarries atextpointer in CSS with no hover handling, so an embedded link gave no cursor affordance. It now toggles the pointer onMouseMove/Leaveusing the existingevent_targets_linkhelper, mirroringAssistantMessage. Link clicks still route throughopen_style_link, so URL safety checks are unchanged.Already on threadnote was the odd one out: an inline transcript message for something that changed no state, where the equivalentAlready using <model>no-op is a toast. It is now a toast too. Its dedup guard — don't stack an identical toast while the previous one is presumed still on-screen, but do re-announce once that has expired — is hoisted into_notify_unchanged_onceand shared by both call sites. A successful thread switch re-arms the toast. The siblingSwitched to thread directorymessage stays inline because it records a real state change.Made by Open SWE