From fa1b27bcc8e0f076076b766273b305407b593f60 Mon Sep 17 00:00:00 2001 From: TolgaCinisli Date: Tue, 8 Sep 2026 17:05:11 +0300 Subject: [PATCH 01/26] fix(mobile): style inline code with the app mono face (#6631) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Inline code on mobile renders as **bold body text on a faint background wash** — no monospace face, no chip, and it cannot wrap. #5257 diagnosed this as a missing `highlightBuilder`. That is no longer the right fix. `gpt_markdown` 1.2.0 deprecates `highlightBuilder` (removal in 2.0.0), renders inline code as a real chip, and adds `InlineCodeStyle` for restyling it. The package author confirmed this on the issue. So this PR is an upgrade — 1.1.6 → 1.2.1 — plus one theme declaration, rather than the builder the issue originally asked for. **Where the style is declared.** `GptMarkdownThemeData` goes in `AppTheme._buildTheme`, which both `light()` and `dark()` call. That reaches all four `GptMarkdown` call sites — `message_content`, `transcript_item_widget`, `token_pill`, `custom_emoji_render` — so the style is stated once instead of per widget. A widget-level `inlineCodeStyle` would have covered channel messages only, leaving the other three on the package's defaults. **What is declared.** Face, size, ink, chip fill and outline — not the face alone. A face name on its own leaves the rest on the package's defaults, which put inline code at 14.1sp beside a fenced block's 13, on a neutral `onSurface` tint rather than the app's code surface. In dark that tint is *lighter* than the surface, while every other code surface in the app is recessed, so the chip read as a different kind of object. All of it now comes from one `CodeStyle` declaration that the fenced block reads from too, so the two cannot be edited apart. **Three adaptations the upgrade requires.** Each was found by running the gate, not by reading the changelog: 1. **`imageBuilder` widened** to `(context, url, width, height)`. This is a hard compile error, and it is **not listed in the package's migration guide**, which states "nothing here stops code compiling". Worth reporting upstream. 2. **`autolink` now defaults to `true`.** `normalizeBareLinks()` already rewrites bare URLs into Markdown links before rendering, so both would run. `message_content` opts out with `autolink: false` to keep current behaviour exactly. The migration guide argues for dropping the pre-processor instead — a better fix, but a behavioural change that belongs in its own PR. 3. **`gpt_markdown.dart` now re-exports `markdown_config.dart`**, making two direct imports redundant. `flutter analyze` reports `No issues found!` on 1.1.6 and flags both on 1.2.1, so these warnings are new, not pre-existing. **Deliberately out of scope.** The three non-message call sites now autolink bare URLs, since only `message_content` has a pre-processor to collide with. Custom inline components (`_MentionMd`, `CustomEmojiMd`, `_ChannelLinkMd`) could additionally declare `allScopesExceptLinkLabel` — 1.2.0 offers it as the fix for a `WidgetSpan` chip going blank inside a link label on iOS — but current behaviour is unchanged without it, so that stays a separate change. ### Related issue Fixes #5257 Duplicate scan: searched `gpt_markdown`, `inline code mobile`, `highlightBuilder` and `InlineCodeStyle` across both PRs and issues. No open PR touches inline code styling. #6135 (link labels) and #6166 (text selection) also touch mobile Markdown but address different defects. ### Testing Full gate, `just ci` — exit 0: | Stage | Result | |---|---| | Rust (33 suites) | 4768 passed, 0 failed | | Desktop | 5799 passed, 0 failed | | Mobile | **2011 passed**, 0 failed | | `flutter analyze` | `No issues found!` | | Desktop + web build | ok | Run on the branch with `main` merged in, so these numbers match what CI builds. **New regression test** — `renders inline code in the app code style`. It resolves the `CodeTextSpan` the package tags inline code with, which carries both the resolved `TextStyle` and the colours the chip behind it is painted with, so face, size, ink, fill and outline are all asserted rather than a widget's presence. It is negative-controlled: reverting only the theme declaration fails it with ```text Expected: a numeric value within <0.001> of <13.0> Actual: <14.1> ``` and dropping the declaration entirely falls back to `packages/gpt_markdown/JetBrainsMono` — so the test measures the real thing, and it would catch a future regression that silently drops the theme extension. The test passes `baseStyle: messageBodyTextStyle`, the style the message surfaces actually use; the widget's own fallback is the smaller `bodyMedium`, which would move the expected size. The test finds paragraphs with `find.byWidgetPredicate((widget) => widget is RichText)`, not `find.byType(RichText)`: inline code renders through `BidiRichText`, a `RichText` subclass, and `byType` matches exact runtime types. That is a hazard for any test that reads text back out of a paragraph, and one landed after this branch was cut: `message_content_custom_emoji_test.dart` arrived with #6996 and its `code keeps literal emoji while adjacent known tokens render` case reads a code span through `find.byType(RichText)`. It passes on `main` and fails on the merge result, which is what CI builds, so it went red only once CI was authorized. It now uses the same predicate. The two other `byType(RichText)` call sites — the rest of that file and `message_author_meta_test.dart` — were re-run and pass: their content carries no code span, so the exact type still matches. They were left alone. ### Screenshots Rendered through the real `MessageContent` widget with the app's own fonts loaded, at 390pt wide, 3x DPR. Sample text: ``Set `BUZZ_RELAY_URL` before launch, then run `just mobile-test` to verify.`` | | Before (1.1.6) | After (1.2.1) | |---|---|---| | Light | ![before-inline-code-light](https://raw.githubusercontent.com/TolgaCinisli/buzz/2d2d846291416d9b32d3fb9cfead950bcc4fe123/pr-6631--before-inline-code-light.png) | ![after-inline-code-light](https://raw.githubusercontent.com/TolgaCinisli/buzz/f230b95c7260a32bd5d76b1ac42130720a168521/pr-6631--after-inline-code-light.png) | | Dark | ![before-inline-code-dark](https://raw.githubusercontent.com/TolgaCinisli/buzz/2d2d846291416d9b32d3fb9cfead950bcc4fe123/pr-6631--before-inline-code-dark.png) | ![after-inline-code-dark](https://raw.githubusercontent.com/TolgaCinisli/buzz/f230b95c7260a32bd5d76b1ac42130720a168521/pr-6631--after-inline-code-dark.png) | Before: bold Inter on a flat wash, no chip edge, and `just mobile-test` breaks across the line with the wash simply ending. After: Geist Mono in a bordered, rounded chip, and the wrapped fragment gets its own chip on each line. --------- Signed-off-by: Tolga Cinisli Co-authored-by: Tolga Cinisli --- mobile/lib/app.dart | 8 +- .../transcript_item_widget.dart | 6 ++ .../features/channels/message_content.dart | 18 ++--- .../custom_emoji/custom_emoji_render.dart | 1 - .../lib/shared/theme/app_markdown_theme.dart | 46 +++++++++++ mobile/lib/shared/theme/code_style.dart | 34 +++++++++ mobile/lib/shared/theme/theme.dart | 2 + mobile/pubspec.lock | 4 +- mobile/pubspec.yaml | 2 +- .../transcript_item_widget_test.dart | 52 +++++++++++++ .../message_content_custom_emoji_test.dart | 6 +- .../channels/message_content_test.dart | 47 +++++++++++- .../shared/theme/app_markdown_theme_test.dart | 76 +++++++++++++++++++ 13 files changed, 284 insertions(+), 18 deletions(-) create mode 100644 mobile/lib/shared/theme/app_markdown_theme.dart create mode 100644 mobile/lib/shared/theme/code_style.dart create mode 100644 mobile/test/features/channels/agent_activity/transcript_item_widget_test.dart create mode 100644 mobile/test/shared/theme/app_markdown_theme_test.dart diff --git a/mobile/lib/app.dart b/mobile/lib/app.dart index d87982bac23..9f3724fd2d1 100644 --- a/mobile/lib/app.dart +++ b/mobile/lib/app.dart @@ -374,9 +374,11 @@ class App extends HookConsumerWidget { // Above the navigator, so a burst keeps playing over a pushed thread page // or a modal sheet — the same reason desktop pins its canvas to the // viewport rather than to the message row. - builder: (context, child) => MobileHuddleShell( - navigatorKey: _mobileRootNavigatorKey, - child: EmojiBurstOverlay(child: child ?? const SizedBox.shrink()), + builder: (context, child) => AppMarkdownTheme( + child: MobileHuddleShell( + navigatorKey: _mobileRootNavigatorKey, + child: EmojiBurstOverlay(child: child ?? const SizedBox.shrink()), + ), ), home: authState.when( loading: () => const _SplashScreen(), diff --git a/mobile/lib/features/channels/agent_activity/transcript_item_widget.dart b/mobile/lib/features/channels/agent_activity/transcript_item_widget.dart index 74b2322e34a..b2f515a888a 100644 --- a/mobile/lib/features/channels/agent_activity/transcript_item_widget.dart +++ b/mobile/lib/features/channels/agent_activity/transcript_item_widget.dart @@ -72,6 +72,10 @@ class _MessageItemWidget extends StatelessWidget { style: context.textTheme.bodyMedium?.copyWith( color: context.colors.onSurface, ), + // No link handler here, so an autolinked URL would draw as a + // link and do nothing when tapped. Transcript URLs stay text, + // as they were before gpt_markdown started autolinking. + autolink: false, ), ], ), @@ -143,6 +147,8 @@ class _ThoughtItemWidget extends HookWidget { style: context.textTheme.bodySmall?.copyWith( color: context.colors.onSurfaceVariant, ), + // As above: no link handler on this surface either. + autolink: false, ), ], ], diff --git a/mobile/lib/features/channels/message_content.dart b/mobile/lib/features/channels/message_content.dart index c9aa72599fb..b94954c00cf 100644 --- a/mobile/lib/features/channels/message_content.dart +++ b/mobile/lib/features/channels/message_content.dart @@ -7,7 +7,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:gpt_markdown/gpt_markdown.dart'; -import 'package:gpt_markdown/custom_widgets/markdown_config.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:open_filex/open_filex.dart'; import 'package:path_provider/path_provider.dart'; @@ -266,6 +265,9 @@ class MessageContent extends HookConsumerWidget { finalContent, style: style, followLinkColor: false, + // normalizeBareLinks() already turns bare URLs into Markdown links; + // gpt_markdown 1.2.0 autolinks by default, so both would run. + autolink: false, codeBuilder: (context, name, code, closed) => _MessageCodeBlock(name: name, code: code), linkBuilder: (context, linkText, url, linkStyle) => _buildLink( @@ -279,7 +281,7 @@ class MessageContent extends HookConsumerWidget { resolvedChannelTap, resolvedChannelNames, ), - imageBuilder: (context, imageUrl) => + imageBuilder: (context, imageUrl, _, _) => _buildMedia(context, imageUrl, imetaByUrl[imageUrl]), textAlign: textAlign, maxLines: maxLines, @@ -721,9 +723,9 @@ class _MessageCodeBlock extends HookWidget { } final codeBaseStyle = TextStyle( - fontFamily: 'GeistMono', - fontSize: 13, - height: 1.5, + fontFamily: CodeStyle.fontFamily, + fontSize: CodeStyle.fontSize, + height: CodeStyle.lineHeight, color: context.colors.onSurface, ); final isDark = context.theme.brightness == Brightness.dark; @@ -735,11 +737,9 @@ class _MessageCodeBlock extends HookWidget { return Container( margin: const EdgeInsets.only(top: Grid.half), decoration: BoxDecoration( - color: context.colors.surfaceContainerHighest.withValues(alpha: 0.6), + color: CodeStyle.background(context.colors), borderRadius: BorderRadius.circular(12), - border: Border.all( - color: context.colors.outline.withValues(alpha: 0.7), - ), + border: Border.all(color: CodeStyle.border(context.colors)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, diff --git a/mobile/lib/shared/custom_emoji/custom_emoji_render.dart b/mobile/lib/shared/custom_emoji/custom_emoji_render.dart index feede9aec81..bbfdd25603e 100644 --- a/mobile/lib/shared/custom_emoji/custom_emoji_render.dart +++ b/mobile/lib/shared/custom_emoji/custom_emoji_render.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:gpt_markdown/gpt_markdown.dart'; -import 'package:gpt_markdown/custom_widgets/markdown_config.dart'; import '../relay/relay.dart'; diff --git a/mobile/lib/shared/theme/app_markdown_theme.dart b/mobile/lib/shared/theme/app_markdown_theme.dart new file mode 100644 index 00000000000..27e5060901d --- /dev/null +++ b/mobile/lib/shared/theme/app_markdown_theme.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:gpt_markdown/gpt_markdown.dart'; + +import 'app_theme.dart'; +import 'code_style.dart'; + +/// States the app's code style on every `GptMarkdown` below it and leaves the +/// rest of the Markdown theme as it already was. +/// +/// Registering a [GptMarkdownThemeData] as a `ThemeExtension` would be the +/// shorter route, but its factory is not a partial override: it builds a fresh +/// stock Material theme and fills every heading, rule and highlight field from +/// it, so registering one stops `GptMarkdownTheme.of` deriving those from the +/// app. Restating them in the extension does not close the gap either — the +/// ambient headings carry the localized text geometry, which exists only once +/// `Theme.of` has run and a `ThemeData` factory cannot see. Overriding the one +/// field on the ambient theme is what leaves non-code Markdown untouched. +class AppMarkdownTheme extends StatelessWidget { + const AppMarkdownTheme({super.key, required this.child}); + + final Widget child; + + @override + Widget build(BuildContext context) { + final scheme = Theme.of(context).colorScheme; + return GptMarkdownTheme( + gptThemeData: GptMarkdownTheme.of(context).copyWith( + // Inline code is otherwise drawn in gpt_markdown's own bundled face, at + // its own size, tinted from `onSurface`. Stating the app's code style + // gives every Markdown surface the face, size and chip colours a fenced + // code block already uses. + inlineCode: InlineCodeStyle( + fontFamily: CodeStyle.fontFamily, + fontSizeFactor: CodeStyle.fontSizeFactor, + color: scheme.onSurface, + backgroundColor: CodeStyle.background(scheme), + borderColor: CodeStyle.border(scheme), + // A chip is small, so it takes the smallest step of the app's radius + // scale rather than the `Radii.card` a block uses. + borderRadius: const Radius.circular(Radii.xs), + ).resolve(scheme), + ), + child: child, + ); + } +} diff --git a/mobile/lib/shared/theme/code_style.dart b/mobile/lib/shared/theme/code_style.dart new file mode 100644 index 00000000000..3ed667f44c6 --- /dev/null +++ b/mobile/lib/shared/theme/code_style.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +import 'message_typography.dart'; + +/// The app's own code style — one place stating the mono face, its size and +/// the colours a code surface is drawn with. +/// +/// Two surfaces render code and they have to read as one family: the fenced +/// block in `MessageContent`, and the inline chip `gpt_markdown` paints from +/// the `InlineCodeStyle` declared in `AppTheme`. Both take their values from +/// here, so neither can drift from the other by editing one copy. +abstract final class CodeStyle { + /// The mono face, already used by the composer and by code blocks. + static const fontFamily = 'GeistMono'; + + /// Code size at message body scale. + static const fontSize = 13.0; + + /// Line height of code text. + static const lineHeight = 1.5; + + /// [fontSize] stated against the message body size, for APIs that scale code + /// relative to the text around it rather than fixing a point size. Keeping + /// it a ratio means code follows the body if that size ever changes. + static final fontSizeFactor = fontSize / messageBodyTextStyle.fontSize!; + + /// Fill behind a code surface. + static Color background(ColorScheme scheme) => + scheme.surfaceContainerHighest.withValues(alpha: 0.6); + + /// Outline around a code surface. + static Color border(ColorScheme scheme) => + scheme.outline.withValues(alpha: 0.7); +} diff --git a/mobile/lib/shared/theme/theme.dart b/mobile/lib/shared/theme/theme.dart index eacd1ea7807..55615b5b734 100644 --- a/mobile/lib/shared/theme/theme.dart +++ b/mobile/lib/shared/theme/theme.dart @@ -1,8 +1,10 @@ export 'accent_colors.dart'; export 'adaptive_theme.dart'; export 'app_colors.dart'; +export 'app_markdown_theme.dart'; export 'app_theme.dart'; export 'buzz_theme.dart'; +export 'code_style.dart'; export 'color_scheme.dart'; export 'community_theme_preference.dart'; export 'community_theme_provider.dart'; diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 8b190dcb03c..0632d4a8df7 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -620,10 +620,10 @@ packages: dependency: "direct main" description: name: gpt_markdown - sha256: "5c565a438e569b3b546023d0d4eccccf87e260a3289a17c4e241c41d5e7545df" + sha256: "42887afaf5c7a71348221048a6c1e34c9836703ee0e6b4865e789253fcaa204a" url: "https://pub.dev" source: hosted - version: "1.1.6" + version: "1.2.1" gtk: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index a5166bfa075..e6806b29d19 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -27,7 +27,7 @@ dependencies: nostr: ^2.0.0 pointycastle: ^4.0.0 url_launcher: ^6.3.2 - gpt_markdown: ^1.1.6 + gpt_markdown: ^1.2.1 highlight: ^0.7.0 intl: ^0.20.2 uuid: ^4.5.1 diff --git a/mobile/test/features/channels/agent_activity/transcript_item_widget_test.dart b/mobile/test/features/channels/agent_activity/transcript_item_widget_test.dart new file mode 100644 index 00000000000..20524e9a2db --- /dev/null +++ b/mobile/test/features/channels/agent_activity/transcript_item_widget_test.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gpt_markdown/custom_widgets/link_button.dart'; +import 'package:buzz/features/channels/agent_activity/observer_models.dart'; +import 'package:buzz/features/channels/agent_activity/transcript_item_widget.dart'; +import 'package:buzz/shared/theme/theme.dart'; + +Widget _testable(Widget child) { + return MaterialApp( + theme: AppTheme.light(), + home: Scaffold(body: SingleChildScrollView(child: child)), + ); +} + +void main() { + // Neither transcript surface passes a link handler, so an autolinked bare + // URL would draw as a link and then do nothing when tapped. They stay text. + testWidgets('leaves a bare URL in a message as plain text', (tester) async { + await tester.pumpWidget( + _testable( + TranscriptItemWidget( + item: MessageItem( + id: 'm1', + role: 'assistant', + title: 'Assistant', + text: 'Report is at https://example.com/report today.', + timestamp: '2026-08-29T09:00:00Z', + ), + ), + ), + ); + + expect(find.byType(LinkButton), findsNothing); + }); + + testWidgets('leaves a bare URL in a thought as plain text', (tester) async { + await tester.pumpWidget( + _testable( + TranscriptItemWidget( + item: ThoughtItem( + id: 't1', + title: 'Thinking', + text: 'Checking https://example.com/report first.', + timestamp: '2026-08-29T09:00:00Z', + ), + ), + ), + ); + + expect(find.byType(LinkButton), findsNothing); + }); +} diff --git a/mobile/test/features/channels/message_content_custom_emoji_test.dart b/mobile/test/features/channels/message_content_custom_emoji_test.dart index 3059af9a93e..c43ed614c68 100644 --- a/mobile/test/features/channels/message_content_custom_emoji_test.dart +++ b/mobile/test/features/channels/message_content_custom_emoji_test.dart @@ -111,8 +111,12 @@ void main() { ) async { await tester.pumpWidget(_testable('`:wave:` :unknown:wave:')); expect(find.byType(CustomEmojiImage), findsOneWidget); + // A paragraph holding inline code renders through BidiRichText, a RichText + // subclass, and `find.byType` matches the exact runtime type only. final text = tester - .widgetList(find.byType(RichText)) + .widgetList( + find.byWidgetPredicate((widget) => widget is RichText), + ) .map((widget) => widget.text.toPlainText()) .join(); expect(text, contains(':wave:')); diff --git a/mobile/test/features/channels/message_content_test.dart b/mobile/test/features/channels/message_content_test.dart index aaf6a5973b1..8fe47e88030 100644 --- a/mobile/test/features/channels/message_content_test.dart +++ b/mobile/test/features/channels/message_content_test.dart @@ -45,7 +45,8 @@ Widget _testable( data: MediaQuery.of( context, ).copyWith(disableAnimations: disableAnimations), - child: Scaffold(body: child), + // The app states its code style here, above the navigator. + child: AppMarkdownTheme(child: Scaffold(body: child)), ), ), ), @@ -2813,6 +2814,50 @@ Photos expect(_allRichText(tester), isNot(contains('**'))); }); + testWidgets('renders inline code in the app code style', (tester) async { + // The message surfaces pass this style in; the widget's own fallback + // is the smaller `bodyMedium`, which would move the expected size. + await tester.pumpWidget( + _testable( + const MessageContent( + content: 'Run `just test` now', + baseStyle: messageBodyTextStyle, + ), + ), + ); + + // gpt_markdown tags inline code with a CodeTextSpan carrying both the + // resolved text style and the colours the chip behind it is painted + // with. It renders through BidiRichText, a RichText subclass, so + // find.byType(RichText) would miss it. + final codeSpans = []; + for (final rich in tester.widgetList( + find.byWidgetPredicate((widget) => widget is RichText), + )) { + rich.text.visitChildren((span) { + if (span is CodeTextSpan && span.text == 'just test') { + codeSpans.add(span); + } + return true; + }); + } + + expect(codeSpans, hasLength(1)); + final code = codeSpans.single; + const scheme = lightColorScheme; + + // Face, size and ink — the values a fenced code block is drawn with, + // not the package's bundled mono at its own 0.94 of the body size. + expect(code.style?.fontFamily, CodeStyle.fontFamily); + expect(code.style?.fontSize, closeTo(CodeStyle.fontSize, 0.001)); + expect(code.style?.color, scheme.onSurface); + + // Chip fill and outline come from the app's code surface rather than + // the package's `onSurface` tints. + expect(code.codeStyle.backgroundColor, CodeStyle.background(scheme)); + expect(code.codeStyle.borderColor, CodeStyle.border(scheme)); + }); + testWidgets('renders code block between paragraphs', (tester) async { await tester.pumpWidget( _testable( diff --git a/mobile/test/shared/theme/app_markdown_theme_test.dart b/mobile/test/shared/theme/app_markdown_theme_test.dart new file mode 100644 index 00000000000..26ae369821a --- /dev/null +++ b/mobile/test/shared/theme/app_markdown_theme_test.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gpt_markdown/gpt_markdown.dart'; +import 'package:buzz/shared/theme/app_markdown_theme.dart'; +import 'package:buzz/shared/theme/app_theme.dart'; +import 'package:buzz/shared/theme/code_style.dart'; + +/// The Markdown theme a widget reads under [theme], with [wrapped] deciding +/// whether the app states its code style above it. +Future _markdownTheme( + WidgetTester tester, + ThemeData theme, { + required bool wrapped, +}) async { + late GptMarkdownThemeData read; + final reader = Builder( + builder: (context) { + read = GptMarkdownTheme.of(context); + return const SizedBox.shrink(); + }, + ); + + await tester.pumpWidget( + MaterialApp( + theme: theme, + home: wrapped ? AppMarkdownTheme(child: reader) : reader, + ), + ); + // MaterialApp animates a theme change, so the first frame after a pump can + // still carry the previous one. + await tester.pumpAndSettle(); + return read; +} + +void main() { + testWidgets('states inline code and leaves the rest of Markdown alone', ( + tester, + ) async { + for (final theme in [AppTheme.light(), AppTheme.dark()]) { + final ambient = await _markdownTheme(tester, theme, wrapped: false); + final stated = await _markdownTheme(tester, theme, wrapped: true); + + // Headings, rules and the rest keep the values gpt_markdown derives from + // the app theme: a code-only change must not restyle anything else. + expect(stated.h1, ambient.h1); + expect(stated.h2, ambient.h2); + expect(stated.h3, ambient.h3); + expect(stated.h4, ambient.h4); + expect(stated.h5, ambient.h5); + expect(stated.h6, ambient.h6); + expect(stated.hrLineColor, ambient.hrLineColor); + expect(stated.hrLineThickness, ambient.hrLineThickness); + expect(stated.hrLinePadding, ambient.hrLinePadding); + expect(stated.highlightColor, ambient.highlightColor); + expect(stated.linkColor, ambient.linkColor); + expect(stated.linkHoverColor, ambient.linkHoverColor); + expect( + stated.autoAddDividerLineAfterH1, + ambient.autoAddDividerLineAfterH1, + ); + expect(stated.styleSheet, ambient.styleSheet); + + // Inline code is the whole of the difference. + final scheme = theme.colorScheme; + expect(stated.inlineCode.fontFamily, CodeStyle.fontFamily); + expect(stated.inlineCode.color, scheme.onSurface); + expect(stated.inlineCode.backgroundColor, CodeStyle.background(scheme)); + expect(stated.inlineCode.borderColor, CodeStyle.border(scheme)); + expect( + ambient.inlineCode.fontFamily, + isNot(CodeStyle.fontFamily), + reason: 'the package default is what this widget exists to replace', + ); + } + }); +} From 86c189e8571d4254726f4d0519a500df9b9d947e Mon Sep 17 00:00:00 2001 From: Salman Mohammed Date: Tue, 8 Sep 2026 10:10:38 -0400 Subject: [PATCH 02/26] fix(buzz-acp): wake held ACP threads and fence forked sessions (#7340) ## Summary Adds an independent deadline wakeup so held thread work dispatches after its 10-second bound even when the relay loop is otherwise quiet. Fences session ownership by generation so a worker returning after a fork cannot make an older provider session claimable again. This follows up on the two post-merge findings from [#7337](https://github.com/block/buzz/pull/7337#pullrequestreview-5116329341). ### Related issue Follow-up to #7337. ### Testing - `cargo test -p buzz-acp` - `cargo clippy -p buzz-acp --all-targets -- -D warnings` - Pre-push file-size, differential Rust test, and desktop Tauri gates No UI changes. --- **Update Sep 4, 15:35:** Addressed both Codex review findings. - Queue-cap eviction now prunes orphaned hold deadlines. - An expired hold stays expired until a worker is successfully claimed. - Hold timers remain disabled while every worker is busy; worker return wakes dispatch directly. - Added regressions for queue eviction and pool exhaustion. Generated with Codex --------- Signed-off-by: Salman Mohammed --- crates/buzz-acp/src/lib.rs | 167 +++++++++++++---- crates/buzz-acp/src/pool.rs | 344 ++++++++++++++++++++++++++++++++--- crates/buzz-acp/src/queue.rs | 16 ++ 3 files changed, 466 insertions(+), 61 deletions(-) diff --git a/crates/buzz-acp/src/lib.rs b/crates/buzz-acp/src/lib.rs index ddd594b142d..cc2952a2f8d 100644 --- a/crates/buzz-acp/src/lib.rs +++ b/crates/buzz-acp/src/lib.rs @@ -3005,6 +3005,7 @@ async fn tokio_main() -> Result<()> { Panic(tokio::task::JoinError), SteerAck(SteerAckEvent), Wake(u32, Result), + HoldDeadline, } loop { @@ -3146,6 +3147,8 @@ async fn tokio_main() -> Result<()> { } // Borrow result_rx and join_set simultaneously via split-borrow helper. + pool.retain_held_scopes(|scope| queue.has_pending_scope(scope)); + let hold_deadline = pool.next_hold_deadline(pool::HOLD_BUSY_OWNER_TIMEOUT); let pool_event: Option = { let (result_rx, join_set) = pool.rx_and_join_set(); tokio::select! { @@ -3188,6 +3191,9 @@ async fn tokio_main() -> Result<()> { _ => std::future::pending().await, } } => None, + _ = pool::AgentPool::wait_for_hold_deadline(hold_deadline), if pool_ready => { + Some(PoolEvent::HoldDeadline) + }, Some(Err(error)) = wake_tasks.join_next(), if !wake_tasks.is_empty() => { if let Some(attempt) = pool_lifecycle.waking_attempt() { let message = format!("pool wake task failed: {error}"); @@ -4021,6 +4027,21 @@ async fn tokio_main() -> Result<()> { } } } + Some(PoolEvent::HoldDeadline) => { + // A held thread must make progress even when every unrelated + // relay/timer source is quiet. The deadline is derived from + // the pool's first-held stamp, so this dispatch observes + // `ForkAfterHold` and claims an idle worker immediately. + for (scope, thread_tags) in dispatch_pending( + &mut pool, + &mut queue, + &ctx, + &mut last_activity, + observer.as_ref(), + ) { + typing_channels.insert(scope, thread_tags); + } + } None => {} // relay/heartbeat/shutdown branches handled inline above } } @@ -4412,7 +4433,7 @@ fn dispatch_pending( let mut held: Vec = Vec::new(); // One clock read for the whole cycle so every batch's bounded-hold window is // measured against the same instant. - let now = std::time::Instant::now(); + let now = tokio::time::Instant::now(); loop { let batch = match queue.flush_next() { Some(b) => b, @@ -4427,7 +4448,8 @@ fn dispatch_pending( // so an active channel cannot starve a sibling channel on a shared // worker. A held thread that outwaits the window forks a fresh session // rather than starve behind an unbounded turn. - match pool.hold_decision(&scope, now, pool::HOLD_BUSY_OWNER_TIMEOUT) { + let forked_after_hold = match pool.hold_decision(&scope, now, pool::HOLD_BUSY_OWNER_TIMEOUT) + { pool::HoldDecision::Hold { held_for, owner_index, @@ -4458,31 +4480,9 @@ fn dispatch_pending( pool::HoldDecision::ForkAfterHold { held_for, owner_index, - } => { - tracing::warn!( - channel = %channel_id, - scope = %scope.telemetry_label(), - owner_index, - held_for_secs = held_for.as_secs_f64(), - "busy-owner hold expired — forking fresh session on an idle worker" - ); - if let Some(observer) = observer { - observer.emit( - "busy_owner_hold_forked", - None, - &observer::context_for(Some(channel_id), None, None), - serde_json::json!({ - "scope": scope.telemetry_label(), - "ownerIndex": owner_index, - "heldForSecs": held_for.as_secs_f64(), - }), - ); - } - // Fall through to try_claim below (fork); record_scope_owner - // reassigns ownership to the new worker automatically. - } - pool::HoldDecision::Dispatch => {} - } + } => Some((held_for, owner_index)), + pool::HoldDecision::Dispatch => None, + }; let typing_scope = batch .events .last() @@ -4502,6 +4502,32 @@ fn dispatch_pending( break; } }; + // Consume a bounded hold only after a worker was actually claimed. + // If every slot is checked out, the expired stamp remains sticky and + // the worker-return event retries immediately instead of waiting for a + // fresh timeout window. + pool.clear_hold(&scope); + if let Some((held_for, owner_index)) = forked_after_hold { + tracing::warn!( + channel = %channel_id, + scope = %scope.telemetry_label(), + owner_index, + held_for_secs = held_for.as_secs_f64(), + "busy-owner hold expired — forking fresh session on an idle worker" + ); + if let Some(observer) = observer { + observer.emit( + "busy_owner_hold_forked", + None, + &observer::context_for(Some(channel_id), None, None), + serde_json::json!({ + "scope": scope.telemetry_label(), + "ownerIndex": owner_index, + "heldForSecs": held_for.as_secs_f64(), + }), + ); + } + } tracing::debug!(agent = agent.index, channel = %channel_id, scope = %scope.telemetry_label(), affinity_hit, "agent_claimed"); let recoverable_batch = match ctx.dedup_mode { @@ -4532,6 +4558,14 @@ fn dispatch_pending( let turn_id = Uuid::new_v4().to_string(); let task_turn_id = turn_id.clone(); + // Assign ownership before moving the worker into the task. If this is + // a bounded-hold fork, the new generation immediately invalidates the + // prior busy worker's copy when that worker eventually returns. + let owner_generation = pool.record_scope_owner(scope.clone(), agent.index); + agent + .state + .set_scope_owner_generation(scope.clone(), owner_generation); + let abort_handle = pool.join_set.spawn(async move { pool::run_prompt_task( agent, @@ -4558,9 +4592,6 @@ fn dispatch_pending( successful_steer_deliveries: HashSet::new(), }, ); - // Record this worker as the scope's session owner so a later dispatch - // while it is busy holds instead of forking a duplicate session. - pool.record_scope_owner(scope.clone(), agent_index); dispatched_channels.push((scope, typing_scope)); *last_activity = tokio::time::Instant::now(); } @@ -6235,7 +6266,7 @@ mod owner_control_command_tests { // The bounded hold decision stamps A's first-held time, then forks once // the window elapses rather than starving behind the busy owner. - let now = std::time::Instant::now(); + let now = tokio::time::Instant::now(); assert!( matches!( pool.hold_decision(&ta, now, pool::HOLD_BUSY_OWNER_TIMEOUT), @@ -6256,9 +6287,10 @@ mod owner_control_command_tests { "elapsed window => fork on an idle worker" ); assert!( - !pool.held_since_contains(&ta), - "fork clears the first-held stamp" + pool.held_since_contains(&ta), + "expired hold stays sticky until an idle worker is claimed" ); + pool.clear_hold(&ta); // A conversation scope never holds even with a busy recorded owner — // this is the cross-channel head-of-line-blocking regression guard. @@ -6290,6 +6322,55 @@ mod owner_control_command_tests { ); } + #[tokio::test] + async fn queue_cap_eviction_prunes_orphaned_hold_deadline() { + let mut pool = AgentPool::from_slots(vec![]); + let mut queue = EventQueue::new(DedupMode::Queue); + let channel_id = Uuid::new_v4(); + let held_scope = thread_scope(channel_id, &"a".repeat(64)); + let surviving_scope = thread_scope(channel_id, &"b".repeat(64)); + + pool.record_scope_owner(held_scope.clone(), 0); + let (tx, _rx) = tokio::sync::oneshot::channel(); + insert_task_meta(&mut pool, 0, surviving_scope.clone(), tx); + assert!(matches!( + pool.hold_decision( + &held_scope, + tokio::time::Instant::now(), + pool::HOLD_BUSY_OWNER_TIMEOUT + ), + pool::HoldDecision::Hold { .. } + )); + + let oldest = std::time::Instant::now() - Duration::from_secs(1); + queue.push(queue::QueuedEvent { + channel_id, + scope: held_scope.clone(), + event: make_event(KIND_STREAM_MESSAGE, "held", None), + received_at: oldest, + prompt_tag: "test".into(), + }); + for i in 0..500 { + queue.push(queue::QueuedEvent { + channel_id, + scope: surviving_scope.clone(), + event: make_event(KIND_STREAM_MESSAGE, &format!("new-{i}"), None), + received_at: std::time::Instant::now(), + prompt_tag: "test".into(), + }); + } + + assert!( + !queue.has_pending_scope(&held_scope), + "aggregate cap evicts the globally oldest scope" + ); + pool.retain_held_scopes(|scope| queue.has_pending_scope(scope)); + assert!( + !pool.held_since_contains(&held_scope), + "evicted scope cannot leave an immediately-ready deadline behind" + ); + } + #[test] fn project_owner_control_signs_only_addressable_project_events() { let keys = Keys::generate(); @@ -9370,6 +9451,15 @@ mod error_outcome_emission_tests { } } + fn bind_agent_scope_owner( + pool: &mut AgentPool, + agent: &mut OwnedAgent, + scope: scope::SessionScope, + ) { + let generation = pool.record_scope_owner(scope.clone(), agent.index); + agent.state.set_scope_owner_generation(scope, generation); + } + #[tokio::test] async fn successful_native_steer_is_transferred_to_live_session_delivery_state() { let channel_id = Uuid::new_v4(); @@ -9385,6 +9475,11 @@ mod error_outcome_emission_tests { ); let mut pool = AgentPool::from_slots(vec![None]); + bind_agent_scope_owner( + &mut pool, + &mut agent, + scope::SessionScope::Conversation { channel_id }, + ); let task_id = pool.join_set.spawn(async {}).id(); pool.task_map_mut().insert( task_id, @@ -9460,6 +9555,11 @@ mod error_outcome_emission_tests { ); let mut pool = AgentPool::from_slots(vec![None]); + bind_agent_scope_owner( + &mut pool, + &mut agent, + scope::SessionScope::Conversation { channel_id }, + ); let task_id = pool.join_set.spawn(async {}).id(); pool.task_map_mut().insert( task_id, @@ -10697,6 +10797,7 @@ mod error_outcome_emission_tests { .sessions .insert(session_scope.clone(), "healthy-session".into()); let mut pool = AgentPool::from_slots(vec![None]); + bind_agent_scope_owner(&mut pool, &mut agent, session_scope.clone()); let task_id = pool.join_set.spawn(async {}).id(); pool.task_map_mut().insert( task_id, diff --git a/crates/buzz-acp/src/pool.rs b/crates/buzz-acp/src/pool.rs index 06383d456d3..dbeafedda70 100644 --- a/crates/buzz-acp/src/pool.rs +++ b/crates/buzz-acp/src/pool.rs @@ -141,9 +141,18 @@ pub struct SessionState { /// Per-scope successful-delivery state. Created with the ACP session and /// cleared atomically with every invalidation path. pub deliveries: HashMap, + /// Pool-assigned ownership generation for each scope. A worker returning + /// after another worker forked the scope carries an older generation; the + /// pool uses this fence to discard that stale provider session before the + /// worker becomes claimable again. + scope_owner_generations: HashMap, } impl SessionState { + pub(crate) fn set_scope_owner_generation(&mut self, scope: SessionScope, generation: u64) { + self.scope_owner_generations.insert(scope, generation); + } + /// Invalidate the session (and turn counter) for a specific prompt source. pub fn invalidate(&mut self, source: &PromptSource) { match source { @@ -165,6 +174,7 @@ impl SessionState { self.core_sections.remove(scope); self.canvas_sections.remove(scope); self.deliveries.remove(scope); + self.scope_owner_generations.remove(scope); self.sessions.remove(scope).is_some() } @@ -179,6 +189,7 @@ impl SessionState { .chain(self.core_sections.keys()) .chain(self.canvas_sections.keys()) .chain(self.deliveries.keys()) + .chain(self.scope_owner_generations.keys()) .filter(|s| s.channel_id() == *channel_id) .cloned() .collect::>() @@ -203,6 +214,7 @@ impl SessionState { self.core_sections.clear(); self.canvas_sections.clear(); self.deliveries.clear(); + self.scope_owner_generations.clear(); } pub(crate) fn mark_scope_delivery_success( @@ -336,13 +348,23 @@ pub struct AgentPool { /// cause another worker to open a duplicate session for the same thread. /// Best-effort: stale entries (rotation, crash/respawn) self-heal on the /// next dispatch and are pruned on channel-wide session invalidation. - session_owners: HashMap, + session_owners: HashMap, + /// Monotonic validity fence assigned whenever a scope is dispatched. The + /// generation distinguishes a newly forked owner from every older copy of + /// that scope's provider session. + next_scope_owner_generation: u64, /// First time each scope was held for a busy owner, so the bounded hold can /// expire and fork rather than starve behind an unbounded turn. Derived /// state: cleared on every dispatch/invalidation path, and only ever holds /// `Thread` scopes (the sole variant [`hold_decision`](Self::hold_decision) /// stamps). - held_since: HashMap, + held_since: HashMap, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct SessionOwner { + agent_index: usize, + generation: u64, } /// Result returned by a completed prompt task. @@ -830,14 +852,33 @@ impl AgentPool { join_set: JoinSet::new(), task_map: HashMap::new(), session_owners: HashMap::new(), + next_scope_owner_generation: 1, held_since: HashMap::new(), } } - /// Record which worker is handling `scope` so a later dispatch can detect a - /// busy owner and avoid opening a duplicate session on another worker. - pub fn record_scope_owner(&mut self, scope: SessionScope, agent_index: usize) { - self.session_owners.insert(scope, agent_index); + /// Record `agent_index` as the newest owner of `scope`, returning the + /// generation that the caller must install on the checked-out worker. + /// Returning workers are accepted only while this exact + /// `(worker, generation)` pair remains authoritative. + pub fn record_scope_owner(&mut self, scope: SessionScope, agent_index: usize) -> u64 { + let generation = self.next_scope_owner_generation; + self.next_scope_owner_generation = self.next_scope_owner_generation.wrapping_add(1); + if self.next_scope_owner_generation == 0 { + // Preserve zero as an unassigned sentinel. Reaching this requires + // 2^64 dispatches in one process, but resetting safely is cheap: + // every previously tagged session becomes stale on return/claim. + self.next_scope_owner_generation = 1; + self.session_owners.clear(); + } + self.session_owners.insert( + scope, + SessionOwner { + agent_index, + generation, + }, + ); + generation } /// True when this scope should be **held** (left queued) rather than @@ -854,16 +895,21 @@ impl AgentPool { return false; } match self.session_owners.get(scope) { - Some(&owner_idx) => self.task_map.values().any(|m| m.agent_index == owner_idx), + Some(owner) => self + .task_map + .values() + .any(|m| m.agent_index == owner.agent_index), None => false, } } /// Decide whether to hold `scope`'s batch for its busy session owner, fork it - /// after a bounded hold, or dispatch immediately. Stamps and clears the - /// first-held time internally so the bounded window survives across dispatch - /// cycles without a dedicated timer; `now` and `timeout` are injected for - /// testability. + /// after a bounded hold, or dispatch immediately. Stamps the first-held time + /// so the bounded window survives across dispatch cycles; `now` and + /// `timeout` are injected for testability. An expired + /// stamp remains sticky until [`clear_hold`](Self::clear_hold) confirms a + /// worker was successfully claimed, so pool exhaustion cannot restart the + /// bounded window. /// /// Gated on the scope variant, not the session policy: `Conversation` scopes /// (channel-policy channels and all DMs) never hold — a busy owner there means @@ -873,18 +919,21 @@ impl AgentPool { pub fn hold_decision( &mut self, scope: &SessionScope, - now: std::time::Instant, + now: tokio::time::Instant, timeout: Duration, ) -> HoldDecision { if !scope.is_thread() || !self.should_hold_for_busy_owner(scope) { self.held_since.remove(scope); return HoldDecision::Dispatch; } - let owner_index = self.session_owners.get(scope).copied().unwrap_or_default(); + let owner_index = self + .session_owners + .get(scope) + .map(|owner| owner.agent_index) + .unwrap_or_default(); let first = *self.held_since.entry(scope.clone()).or_insert(now); let held_for = now.saturating_duration_since(first); if held_for >= timeout { - self.held_since.remove(scope); HoldDecision::ForkAfterHold { held_for, owner_index, @@ -911,7 +960,7 @@ impl AgentPool { if let Some(scope) = scope { let idx = self.agents.iter().position(|slot| { slot.as_ref() - .map(|a| a.state.sessions.contains_key(scope)) + .map(|a| self.agent_owns_scope(a, scope)) .unwrap_or(false) }); if let Some(i) = idx { @@ -925,7 +974,27 @@ impl AgentPool { } /// Return an agent to its slot after a task completes. - pub fn return_agent(&mut self, agent: OwnedAgent) { + pub fn return_agent(&mut self, mut agent: OwnedAgent) { + let stale_scopes: Vec = agent + .state + .sessions + .keys() + .filter(|scope| !self.agent_owns_scope(&agent, scope)) + .cloned() + .collect(); + for scope in stale_scopes { + tracing::info!( + agent = agent.index, + scope = %scope.telemetry_label(), + "discarding stale session after ownership changed" + ); + agent.state.invalidate_scope(&scope); + } + let live_scopes: HashSet = agent.state.sessions.keys().cloned().collect(); + agent + .state + .scope_owner_generations + .retain(|scope, _| live_scopes.contains(scope)); let idx = agent.index; if self.agents[idx].is_some() { // This is a bug: two tasks returned the same agent index. Log it @@ -945,16 +1014,64 @@ impl AgentPool { self.agents.iter().any(|slot| slot.is_some()) } + /// Confirm that pending work for `scope` successfully claimed a worker. + /// + /// In particular, an expired busy-owner hold must not be consumed until + /// this point: `try_claim` can fail while every worker remains checked out. + pub(crate) fn clear_hold(&mut self, scope: &SessionScope) { + self.held_since.remove(scope); + } + + /// Remove derived hold stamps for scopes that no longer have pending work. + pub(crate) fn retain_held_scopes( + &mut self, + mut has_pending_work: impl FnMut(&SessionScope) -> bool, + ) { + self.held_since.retain(|scope, _| has_pending_work(scope)); + } + /// Whether any idle agent already has a session for `scope`. /// Used to compute `affinity_hit` before calling `try_claim`. pub fn has_session_for(&self, scope: &SessionScope) -> bool { self.agents.iter().any(|slot| { slot.as_ref() - .map(|a| a.state.sessions.contains_key(scope)) + .map(|a| self.agent_owns_scope(a, scope)) .unwrap_or(false) }) } + fn agent_owns_scope(&self, agent: &OwnedAgent, scope: &SessionScope) -> bool { + let Some(owner) = self.session_owners.get(scope) else { + return false; + }; + owner.agent_index == agent.index + && agent.state.scope_owner_generations.get(scope) == Some(&owner.generation) + && agent.state.sessions.contains_key(scope) + } + + /// Earliest scheduled wake for a currently held scope that can claim a + /// worker. A worker return wakes the main loop independently, so arming an + /// already-expired timer while every slot is checked out would only spin. + pub(crate) fn next_hold_deadline(&self, timeout: Duration) -> Option { + if !self.any_idle() { + return None; + } + self.held_since + .values() + .map(|held_since| *held_since + timeout) + .min() + } + + /// Sleep until a held scope's scheduled wake, or remain pending when no + /// scope is held. This is the future polled directly by the main + /// `select!`, kept here so paused-time tests exercise the production seam. + pub(crate) async fn wait_for_hold_deadline(deadline: Option) { + match deadline { + Some(deadline) => tokio::time::sleep_until(deadline).await, + None => std::future::pending().await, + } + } + /// Count of agents that are alive: idle OR checked out (have a task_map entry). /// /// Used to detect when all agents have exited so the caller can respawn. @@ -7461,9 +7578,16 @@ printf '%s\n' '{{"jsonrpc":"2.0","id":0,"result":{{"stopReason":"end_turn"}}}}'" agent.state.sessions.insert(ta.clone(), "sess-a".into()); agent.state.sessions.insert(tb.clone(), "sess-b".into()); let mut pool = AgentPool::from_slots(vec![Some(agent)]); - pool.record_scope_owner(ta.clone(), 0); - pool.record_scope_owner(tb.clone(), 0); - let now = std::time::Instant::now(); + let ta_generation = pool.record_scope_owner(ta.clone(), 0); + let tb_generation = pool.record_scope_owner(tb.clone(), 0); + let agent = pool.agents[0].as_mut().expect("idle test agent"); + agent + .state + .set_scope_owner_generation(ta.clone(), ta_generation); + agent + .state + .set_scope_owner_generation(tb.clone(), tb_generation); + let now = tokio::time::Instant::now(); pool.held_since.insert(ta.clone(), now); pool.held_since.insert(tb.clone(), now); @@ -7515,12 +7639,12 @@ printf '%s\n' '{{"jsonrpc":"2.0","id":0,"result":{{"stopReason":"end_turn"}}}}'" /// An idle agent (slot 0) holding a provider session for `scope`, so /// `has_session_for(scope)` is true. - async fn idle_agent_with_session(scope: SessionScope) -> OwnedAgent { + async fn idle_agent_with_session(index: usize, scope: SessionScope) -> OwnedAgent { let acp = AcpClient::spawn("bash", &["-c".into(), "sleep 10".into()], &[], false) .await .expect("spawn dummy ACP"); let mut agent = OwnedAgent { - index: 0, + index, acp, state: SessionState::default(), model_capabilities: None, @@ -7608,7 +7732,7 @@ printf '%s\n' '{{"jsonrpc":"2.0","id":0,"result":{{"stopReason":"end_turn"}}}}'" }, ]; - let base = std::time::Instant::now(); + let base = tokio::time::Instant::now(); for row in rows { let ch = Uuid::new_v4(); let scope = if row.is_thread { @@ -7617,12 +7741,19 @@ printf '%s\n' '{{"jsonrpc":"2.0","id":0,"result":{{"stopReason":"end_turn"}}}}'" conv(ch) }; let slots = if row.has_session { - vec![Some(idle_agent_with_session(scope.clone()).await)] + vec![Some(idle_agent_with_session(0, scope.clone()).await)] } else { vec![] }; let mut pool = AgentPool::from_slots(slots); - if row.owner_busy { + if row.has_session { + let generation = pool.record_scope_owner(scope.clone(), 0); + pool.agents[0] + .as_mut() + .expect("idle test agent") + .state + .set_scope_owner_generation(scope.clone(), generation); + } else if row.owner_busy { pool.record_scope_owner(scope.clone(), 1); mark_agent_busy(&mut pool, 1, thread_scope(ch, &"b".repeat(64))); } @@ -7648,8 +7779,12 @@ printf '%s\n' '{{"jsonrpc":"2.0","id":0,"result":{{"stopReason":"end_turn"}}}}'" _ => panic!("{}: expected {:?}, got {decision:?}", row.name, row.expect), } - // held_since holds the scope only while a Hold is outstanding. - if matches!(decision, HoldDecision::Hold { .. }) { + // An expired hold remains sticky until a worker is successfully + // claimed; only immediate dispatch clears it here. + if matches!( + decision, + HoldDecision::Hold { .. } | HoldDecision::ForkAfterHold { .. } + ) { assert!( pool.held_since.contains_key(&scope), "{}: hold stamps held_since", @@ -7665,6 +7800,159 @@ printf '%s\n' '{{"jsonrpc":"2.0","id":0,"result":{{"stopReason":"end_turn"}}}}'" } } + #[tokio::test(start_paused = true)] + async fn held_scope_deadline_wakes_a_quiet_dispatch_loop() { + let channel_id = Uuid::new_v4(); + let scope = thread_scope(channel_id, &"a".repeat(64)); + let idle_scope = thread_scope(channel_id, &"c".repeat(64)); + let idle_agent = idle_agent_with_session(0, idle_scope).await; + let mut pool = AgentPool::from_slots(vec![Some(idle_agent)]); + pool.record_scope_owner(scope.clone(), 1); + mark_agent_busy(&mut pool, 1, thread_scope(channel_id, &"b".repeat(64))); + + let started = tokio::time::Instant::now(); + assert!(matches!( + pool.hold_decision(&scope, started, HOLD_BUSY_OWNER_TIMEOUT), + HoldDecision::Hold { .. } + )); + let deadline = pool + .next_hold_deadline(HOLD_BUSY_OWNER_TIMEOUT) + .expect("held scope schedules an independent wake"); + let wake = AgentPool::wait_for_hold_deadline(Some(deadline)); + tokio::pin!(wake); + + tokio::time::advance(HOLD_BUSY_OWNER_TIMEOUT - Duration::from_millis(1)).await; + assert!( + tokio::time::timeout(Duration::ZERO, &mut wake) + .await + .is_err(), + "quiet loop stays asleep before deadline" + ); + tokio::time::advance(Duration::from_millis(1)).await; + wake.await; + + assert!(matches!( + pool.hold_decision(&scope, tokio::time::Instant::now(), HOLD_BUSY_OWNER_TIMEOUT), + HoldDecision::ForkAfterHold { .. } + )); + } + + #[tokio::test] + async fn expired_hold_survives_pool_exhaustion_until_a_worker_is_claimable() { + let channel_id = Uuid::new_v4(); + let scope = thread_scope(channel_id, &"a".repeat(64)); + let mut pool = AgentPool::from_slots(vec![None]); + pool.record_scope_owner(scope.clone(), 1); + mark_agent_busy(&mut pool, 1, thread_scope(channel_id, &"b".repeat(64))); + + let started = tokio::time::Instant::now(); + assert!(matches!( + pool.hold_decision(&scope, started, HOLD_BUSY_OWNER_TIMEOUT), + HoldDecision::Hold { .. } + )); + assert!(matches!( + pool.hold_decision( + &scope, + started + HOLD_BUSY_OWNER_TIMEOUT, + HOLD_BUSY_OWNER_TIMEOUT + ), + HoldDecision::ForkAfterHold { .. } + )); + assert!( + pool.held_since.contains_key(&scope), + "failed claim must not restart the timeout" + ); + assert_eq!( + pool.next_hold_deadline(HOLD_BUSY_OWNER_TIMEOUT), + None, + "an expired hold cannot spin while all workers are checked out" + ); + + let idle_scope = thread_scope(channel_id, &"c".repeat(64)); + pool.agents[0] = Some(idle_agent_with_session(0, idle_scope).await); + assert_eq!( + pool.next_hold_deadline(HOLD_BUSY_OWNER_TIMEOUT), + Some(started + HOLD_BUSY_OWNER_TIMEOUT), + "worker availability immediately re-arms the expired deadline" + ); + assert!(matches!( + pool.hold_decision( + &scope, + started + HOLD_BUSY_OWNER_TIMEOUT + Duration::from_secs(1), + HOLD_BUSY_OWNER_TIMEOUT + ), + HoldDecision::ForkAfterHold { .. } + )); + pool.clear_hold(&scope); + assert!(!pool.held_since.contains_key(&scope)); + } + + #[tokio::test] + async fn forked_scope_discards_stale_session_when_busy_owner_returns() { + let channel_id = Uuid::new_v4(); + let scope = thread_scope(channel_id, &"a".repeat(64)); + let busy_scope = thread_scope(channel_id, &"b".repeat(64)); + let old_owner = idle_agent_with_session(0, scope.clone()).await; + let replacement = idle_agent_with_session(1, busy_scope.clone()).await; + let mut pool = AgentPool::from_slots(vec![Some(old_owner), Some(replacement)]); + + let mut old_owner = pool.try_claim(None).expect("claim worker 0"); + let old_generation = pool.record_scope_owner(scope.clone(), old_owner.index); + old_owner + .state + .set_scope_owner_generation(scope.clone(), old_generation); + mark_agent_busy(&mut pool, old_owner.index, busy_scope); + + let started = tokio::time::Instant::now(); + assert!(matches!( + pool.hold_decision(&scope, started, HOLD_BUSY_OWNER_TIMEOUT), + HoldDecision::Hold { .. } + )); + assert!(matches!( + pool.hold_decision( + &scope, + started + HOLD_BUSY_OWNER_TIMEOUT, + HOLD_BUSY_OWNER_TIMEOUT + ), + HoldDecision::ForkAfterHold { .. } + )); + + let mut replacement = pool + .try_claim(Some(&scope)) + .expect("idle worker receives forked scope"); + pool.clear_hold(&scope); + assert_eq!(replacement.index, 1); + replacement + .state + .sessions + .insert(scope.clone(), "fresh-session".into()); + let fresh_generation = pool.record_scope_owner(scope.clone(), replacement.index); + replacement + .state + .set_scope_owner_generation(scope.clone(), fresh_generation); + + // Both turns return. Slot order must not make worker 0's old provider + // context claimable after worker 1 became the authoritative owner. + pool.return_agent(replacement); + pool.task_map + .retain(|_, meta| meta.agent_index != old_owner.index); + pool.return_agent(old_owner); + assert!( + !pool.agents[0] + .as_ref() + .expect("worker 0 returned") + .state + .sessions + .contains_key(&scope), + "return cleanup removes the old provider session" + ); + + let claimed = pool + .try_claim(Some(&scope)) + .expect("authoritative owner remains claimable"); + assert_eq!(claimed.index, 1, "next turn resumes the forked session"); + } + #[test] fn test_rotate_after_natural_completion_invalidates_channel_state() { let (mut s, ch_a, ch_b) = make_state(); @@ -10440,7 +10728,7 @@ done"# pool.invalidate_scope_session(&scopes[1]); pool.record_scope_owner(scopes[0].clone(), 0); pool.held_since - .insert(scopes[0].clone(), std::time::Instant::now()); + .insert(scopes[0].clone(), tokio::time::Instant::now()); assert_eq!( pool.switch_idle_agent_model(channel_id, "model-b", Some("pick".into())), IdleSwitchResult::Switched, diff --git a/crates/buzz-acp/src/queue.rs b/crates/buzz-acp/src/queue.rs index b2203f62f9d..a419b78178d 100644 --- a/crates/buzz-acp/src/queue.rs +++ b/crates/buzz-acp/src/queue.rs @@ -782,6 +782,22 @@ impl EventQueue { self.queues.len() } + /// Whether `scope` still has work that can be reconstructed into a batch. + /// + /// Busy-owner hold timestamps are derived from pending queue state. Queue + /// cap eviction can retire a scope without going through a pool cleanup + /// path, so the dispatch loop uses this seam to prune orphaned holds before + /// scheduling their deadline wakeups. + pub(crate) fn has_pending_scope(&self, scope: &SessionScope) -> bool { + self.queues + .get(scope) + .is_some_and(|queue| !queue.is_empty()) + || self + .cancelled_batches + .get(scope) + .is_some_and(|events| !events.is_empty()) + } + /// Number of queued events for a specific scope (or channel, treated as its /// conversation scope). Test-only. #[cfg(test)] From cd54e26823117259e0bde16bd71a9531e729046b Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Tue, 8 Sep 2026 07:23:48 -0700 Subject: [PATCH 03/26] fix(agent): route GPT-5+ model-service FQNs to Responses (#7358) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Route Databricks Unity Catalog model services to OpenAI Responses when their service name matches GPT-5 or newer. These models can reject tools plus reasoning on Chat Completions. Match only the service component, using the existing family-token boundaries and a numeric major version. Catalog and schema names cannot select the protocol. Keep neutral effort capabilities and the full model ID unchanged; other services still use MLflow Chat Completions. Keep the Rust and desktop resolvers in sync, add shared boundary cases and a captured-HTTP regression for completion and summarization, and update the documented FQN rule. ### Related issue No duplicate found in searches for “FQN responses” PRs or “astra” issues. Related: #6918 introduced Unity Catalog discovery. Originating conversation: buzz://message?channel=0b881928-a3a6-4c01-b981-8e64268f01ce&id=770949343bc96a9ed88acd90a1b37d358a0efc52c79237d0fdb491ce02b8d4ed ### Testing No live Databricks inference test. The gateway must accept the full model-service ID on its OpenAI Responses route; this remains the integration risk. The local `just ci` attempt exceeded its five-minute deadline during `mobile-check`, so the full repository gate was not completed. All push-hook checks passed. Generated with Codex Signed-off-by: Fizz <400e8babadcee6a7f420103f10a2849d84c4a9c71d5bd04f3948c814216648a3@buzz.block.builderlab.xyz> Co-authored-by: Fizz <400e8babadcee6a7f420103f10a2849d84c4a9c71d5bd04f3948c814216648a3@buzz.block.builderlab.xyz> --- crates/buzz-agent/README.md | 2 +- crates/buzz-agent/src/llm.rs | 1 + crates/buzz-agent/src/llm_fqn_tests.rs | 56 +++ crates/buzz-agent/src/model_capabilities.rs | 60 ++- crates/buzz-agent/tests/fqn_capabilities.rs | 63 +++ desktop/src/features/agents/AGENTS.md | 2 +- .../features/agents/ui/modelCapabilities.ts | 23 +- .../ui/modelCapabilitiesCorpus.test.mjs | 4 +- scripts/normative-corpus.json | 364 ++++++++++++++++++ 9 files changed, 559 insertions(+), 16 deletions(-) create mode 100644 crates/buzz-agent/src/llm_fqn_tests.rs create mode 100644 crates/buzz-agent/tests/fqn_capabilities.rs diff --git a/crates/buzz-agent/README.md b/crates/buzz-agent/README.md index f2d68d4d8cd..6032dd11392 100644 --- a/crates/buzz-agent/README.md +++ b/crates/buzz-agent/README.md @@ -242,7 +242,7 @@ lifecycle hook — see [MCP_DRIVEN_HOOKS.md](../../docs/MCP_DRIVEN_HOOKS.md). | Block Gateway | `openai` | `POST {base}/chat/completions` | gpt-5, claude | | OpenRouter | `openrouter` | `POST {base}/chat/completions` | anything they route (extended-thinking replay, provider-agnostic tool calling) | | Databricks | `databricks` | `POST {host}/serving-endpoints/{model}/invocations` | goose-claude-4-6-sonnet | -| Databricks AI Gateway v2 | `databricks_v2` | `POST {host}/ai-gateway/{provider}/v1/...` | workspace endpoints and Unity Catalog model-service FQNs; UC FQNs use MLflow Chat Completions | +| Databricks AI Gateway v2 | `databricks_v2` | `POST {host}/ai-gateway/{provider}/v1/...` | workspace endpoints and Unity Catalog model-service FQNs; UC GPT-5+ services use OpenAI Responses; other UC FQNs use MLflow Chat Completions | The optional `DATABRICKS_MODEL_FILTER` applies only to model discovery. Each comma-separated entry is trimmed and matched against the complete raw ID with case-sensitive `*` (zero or more characters) and `?` (one Unicode character) semantics; patterns are OR-ed. Unset or blank preserves the full authenticated catalog. A nonblank value containing no usable patterns is rejected. This controls picker visibility only; Databricks and Unity Catalog permissions remain the authorization boundary. A filtered-empty result is authoritative and does not restore the built-in fallback models. diff --git a/crates/buzz-agent/src/llm.rs b/crates/buzz-agent/src/llm.rs index 1bac5147743..c0eae9a4d27 100644 --- a/crates/buzz-agent/src/llm.rs +++ b/crates/buzz-agent/src/llm.rs @@ -2570,6 +2570,7 @@ fn apply_anthropic_cache_control(body: &mut serde_json::Map) { #[cfg(test)] mod tests { + include!("llm_fqn_tests.rs"); use super::*; use crate::config::{Config, HookServers, OpenAiApi, Provider, ThinkingSummary}; use crate::types::{HistoryItem, ToolCall, ToolResult, ToolResultContent}; diff --git a/crates/buzz-agent/src/llm_fqn_tests.rs b/crates/buzz-agent/src/llm_fqn_tests.rs new file mode 100644 index 00000000000..cf79358b862 --- /dev/null +++ b/crates/buzz-agent/src/llm_fqn_tests.rs @@ -0,0 +1,56 @@ +// Included in llm::tests to reuse the production-path HTTP capture fixture. +#[tokio::test] +async fn gpt_fqn_completion_and_summary_use_responses() { + let response = json!({"status":"completed", "output":[{ + "type":"message", "content":[{"type":"output_text", "text":"ok"}] + }]}); + let (base_url, captured) = spawn_sequence_stub(vec![ + StubHttpResponse::ok(response.clone()), + StubHttpResponse::ok(response), + ]) + .await; + let mut config = cfg(Provider::DatabricksV2); + config.base_url = base_url; + config.thinking_effort = Some(ThinkingEffort::High); + let model = "catalog.schema.goose-gpt-6-astra"; + let llm = Llm::new(&config).unwrap(); + let tools = vec![ToolDef { + name: "test_tool".into(), + description: "Test".into(), + input_schema: json!({"type":"object", "properties":{}}), + }]; + let result = llm + .complete( + &config, + "system", + &[HistoryItem::User("hello".into())], + &tools, + model, + ) + .await + .unwrap(); + assert_eq!(result.text, "ok"); + assert_eq!( + llm.summarize(&config, "system", "history", 128, model) + .await + .unwrap(), + "ok" + ); + let requests = captured.lock().await; + let posts: Vec<_> = requests.iter().filter(|r| r.method == "POST").collect(); + assert_eq!(posts.len(), 2); + for request in &posts { + assert_eq!(request.path, "/v1/ai-gateway/openai/v1/responses"); + let body = request.body.as_ref().unwrap(); + assert_eq!(body["model"], model); + assert!(body.get("input").is_some()); + assert!(body.get("messages").is_none()); + assert!(body.get("reasoning_effort").is_none()); + } + let completion = posts[0].body.as_ref().unwrap(); + assert!(completion["input"].is_array()); + assert_eq!(completion["reasoning"]["effort"], "high"); + assert_eq!(completion["tools"][0]["type"], "function"); + assert_eq!(completion["tools"][0]["name"], "test_tool"); + assert_eq!(posts[1].body.as_ref().unwrap()["max_output_tokens"], 128); +} diff --git a/crates/buzz-agent/src/model_capabilities.rs b/crates/buzz-agent/src/model_capabilities.rs index 53f2d290ff6..18a4726012b 100644 --- a/crates/buzz-agent/src/model_capabilities.rs +++ b/crates/buzz-agent/src/model_capabilities.rs @@ -292,8 +292,8 @@ fn prefix_matches(token: &str, s: &str) -> bool { /// /// Databricks Unity Catalog model-service names are catalog data, not model /// family hints. Both capability interpreters use this shape check before -/// family matching so suffixes such as `kimi-k3` cannot inherit endpoint -/// capabilities accidentally. +/// family matching so services cannot inherit endpoint capabilities accidentally. +/// GPT-5+ services have a route-only Responses exception. pub(crate) fn is_databricks_model_service_fqn(model: &str) -> bool { let mut components = model.split('.'); let (Some(catalog), Some(schema), Some(service)) = @@ -308,16 +308,35 @@ pub(crate) fn is_databricks_model_service_fqn(model: &str) -> bool { }) && components.next().is_none() } +/// Route GPT-5+ UC services to Responses without borrowing endpoint effort facts. +/// Match the first family token in the service only, preserving the existing +/// boundary semantics (e.g. `claude-gpt-5` is not a GPT service). +fn fqn_requires_responses(model: &str) -> bool { + let Some(service) = model.rsplit('.').next() else { + return false; + }; + let lower = service.to_ascii_lowercase(); + let stripped = strip_catalog_prefix(&lower, &manifest().family_tokens); + let Some(version) = stripped.strip_prefix("gpt-") else { + return false; + }; + let digits = version.bytes().take_while(u8::is_ascii_digit).count(); + let (major, suffix) = version.split_at(digits); + if suffix.starts_with(|c: char| c.is_ascii_alphanumeric()) { + return false; + } + major.parse::().is_ok_and(|major| major >= 5) +} + /// Resolve the capability profile for a `(provider, raw_model_id)` pair. pub fn resolve(provider: &str, raw_model_id: &str) -> CapabilityResult { let m = manifest(); let canon = canonical_provider(provider); let blank = raw_model_id.trim().is_empty(); - // Unity Catalog FQNs are neutral model-service identities. Resolve them - // through the concrete-unknown fallback before any suffix can match a - // provider family rule. Routing and effort normalization then share this - // one answer in Rust and TypeScript. + // FQNs keep neutral effort capabilities, but GPT-5+ service names need + // Responses for tools with reasoning. Only inspect the service component: + // catalog/schema names must never choose a model protocol. let model_service_fqn = canon == "databricks_v2" && is_databricks_model_service_fqn(raw_model_id); @@ -394,7 +413,11 @@ pub fn resolve(provider: &str, raw_model_id: &str) -> CapabilityResult { thinking_mode: state.thinking_mode, supported_efforts: &state.supported_efforts, default_effort: state.default_effort, - databricks_v2_wire_route: state.databricks_v2_wire_route, + databricks_v2_wire_route: if model_service_fqn && fqn_requires_responses(raw_model_id) { + DatabricksV2Route::OpenaiResponses + } else { + state.databricks_v2_wire_route + }, normalization_policy: state.normalization_policy, registry_label: None, } @@ -724,6 +747,25 @@ mod tests { Q::Vector { id: "boundary-claude-3-digit-run-anthropic-probe", provider: "anthropic", raw_model_id: "claude-35", note: Some("Probes whether the claude-3 prefix binds a longer digit run ('35').") }, Q::Vector { id: "boundary-claude-opus-4-70-anthropic-probe", provider: "anthropic", raw_model_id: "claude-opus-4-70", note: Some("Probes whether the claude-opus-4-7 prefix binds a longer digit run ('70').") }, Q::Vector { id: "boundary-gpt-5-1234-openai-probe", provider: "openai", raw_model_id: "gpt-5-1234", note: Some("Probes a 4-digit run after the gpt-5 stem.") }, + Q::Section { group: "Databricks FQN GPT-5+ Responses routing", note: Some("Only the service component selects Responses; effort capabilities remain neutral.") }, + Q::Vector { id: "dbv2-fqn-responses-0", provider: "databricks_v2", raw_model_id: "catalog.schema.goose-gpt-6-astra", note: None }, + Q::Vector { id: "dbv2-fqn-responses-1", provider: "databricks_v2", raw_model_id: "catalog.schema.goose-gpt-5", note: None }, + Q::Vector { id: "dbv2-fqn-responses-2", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-5-5", note: None }, + Q::Vector { id: "dbv2-fqn-responses-3", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-10", note: None }, + Q::Vector { id: "dbv2-fqn-responses-4", provider: "databricks_v2", raw_model_id: "catalog.schema.GOOSE-GPT-6-ASTRA", note: None }, + Q::Vector { id: "dbv2-fqn-responses-5", provider: "databricks_v2", raw_model_id: "gpt-6.schema.other", note: None }, + Q::Vector { id: "dbv2-fqn-responses-6", provider: "databricks_v2", raw_model_id: "catalog.gpt-5.other", note: None }, + Q::Vector { id: "dbv2-fqn-responses-7", provider: "databricks_v2", raw_model_id: "catalog.schema.claude-gpt-6", note: None }, + Q::Vector { id: "dbv2-fqn-responses-8", provider: "databricks_v2", raw_model_id: "catalog.schema.my-gpt-6-astra", note: None }, + Q::Vector { id: "dbv2-fqn-responses-9", provider: "databricks_v2", raw_model_id: "catalog.schema.mygpt-6-astra", note: None }, + Q::Vector { id: "dbv2-fqn-responses-10", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-4", note: None }, + Q::Vector { id: "dbv2-fqn-responses-11", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-4o", note: None }, + Q::Vector { id: "dbv2-fqn-responses-12", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-6x", note: None }, + Q::Vector { id: "dbv2-fqn-responses-13", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-oss-120b", note: None }, + Q::Vector { id: "dbv2-fqn-responses-14", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-", note: None }, + Q::Vector { id: "dbv2-fqn-responses-15", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-4294967296", note: None }, + Q::Vector { id: "dbv2-fqn-responses-16", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-6", note: None }, + Q::Vector { id: "dbv2-fqn-responses-17", provider: "databricks_v2", raw_model_id: "catalog.schema.kimi-k3", note: None }, Q::Section { group: "Databricks UC model-family humanization probes (#6918 follow-up)", note: Some("Exact-record and UC-FQN strip probes for the Gemini/DeepSeek/GLM/Grok/Llama/Qwen/Gemma/Inkling families surfaced by UC discovery.") }, Q::Vector { id: "dbv2-gemini-3-1-flash-image-exact-record-probe", provider: "databricks_v2", raw_model_id: "databricks-gemini-3-1-flash-image", note: Some("Probes the Gemini 3.1 Flash Image endpoint record and label.") }, Q::Vector { id: "dbv2-gemini-3-5-flash-exact-record-probe", provider: "databricks_v2", raw_model_id: "databricks-gemini-3-5-flash", note: Some("Probes the Gemini 3.5 Flash endpoint record and label.") }, @@ -844,7 +886,7 @@ mod tests { } #[test] - fn corpus_has_exactly_140_executable_vectors() { + fn corpus_has_exactly_158_executable_vectors() { // Locks the vector count so a silent INPUTS edit can't quietly drop // coverage; must equal the gate in the TS harness // (modelCapabilitiesCorpus.test.mjs). @@ -853,7 +895,7 @@ mod tests { .filter(|q| matches!(q, Q::Vector { .. })) .count(); assert_eq!( - vectors, 140, + vectors, 158, "corpus executable-vector count changed; update this gate deliberately" ); } diff --git a/crates/buzz-agent/tests/fqn_capabilities.rs b/crates/buzz-agent/tests/fqn_capabilities.rs new file mode 100644 index 00000000000..1c513dc0118 --- /dev/null +++ b/crates/buzz-agent/tests/fqn_capabilities.rs @@ -0,0 +1,63 @@ +//! UC route selection must change only the route, never effort or model identity. +use buzz_agent::model_capabilities::{resolve, DatabricksV2Route}; + +#[test] +fn gpt_fqn_route_preserves_neutral_capabilities() { + let fallback = resolve("databricks_v2", "catalog.schema.unknown"); + for model in [ + "catalog.schema.goose-gpt-6-astra", + "catalog.schema.gpt-5", + "catalog.schema.goose-gpt-5-5", + "catalog.schema.gpt-10", + "catalog.schema.GOOSE-GPT-6-ASTRA", + "catalog.schema.team-gpt-6-astra", + ] { + let got = resolve(" DATABRICKS-V2 ", model); + assert_eq!( + got.databricks_v2_wire_route, + DatabricksV2Route::OpenaiResponses, + "{model}" + ); + assert_eq!(got.thinking_mode, fallback.thinking_mode, "{model}"); + assert_eq!(got.supported_efforts, fallback.supported_efforts, "{model}"); + assert_eq!(got.default_effort, fallback.default_effort, "{model}"); + assert_eq!( + got.normalization_policy, fallback.normalization_policy, + "{model}" + ); + assert_eq!(got.registry_label, None, "{model}"); + } +} + +#[test] +fn unrelated_fqns_do_not_select_responses() { + for model in [ + "gpt-6.schema.other", + "catalog.gpt-5.other", + "catalog.schema.claude-gpt-6", + "catalog.schema.mygpt-6-astra", + "catalog.schema.gpt-4", + "catalog.schema.gpt-4o", + "catalog.schema.gpt-6x", + "catalog.schema.gpt-oss-120b", + "catalog.schema.gpt-", + "catalog.schema.gpt-6", + "catalog.schema.gpt-4294967296", + "catalog.schema.kimi-k3", + ] { + assert_eq!( + resolve("databricks_v2", model).databricks_v2_wire_route, + DatabricksV2Route::MlflowChat, + "{model}" + ); + } + // The new rule is not an endpoint-family or cross-provider capability change. + assert_eq!( + resolve("databricks_v2", "goose-gpt-6-astra").databricks_v2_wire_route, + DatabricksV2Route::MlflowChat + ); + assert_eq!( + resolve("openai", "catalog.schema.goose-gpt-6-astra").databricks_v2_wire_route, + DatabricksV2Route::NotApplicable + ); +} diff --git a/desktop/src/features/agents/AGENTS.md b/desktop/src/features/agents/AGENTS.md index dfb9c0ed494..7f1f73b1752 100644 --- a/desktop/src/features/agents/AGENTS.md +++ b/desktop/src/features/agents/AGENTS.md @@ -306,7 +306,7 @@ with a TypeScript lookup table or an id comparison in a component. refresh only local persona/team/managed-agent caches; they must never invalidate the remote relay directory. -17. **Databricks model discovery has one shared catalog authority.** Desktop and ACP call the shared `buzz-agent` discovery library; Desktop passes the effective merged `DATABRICKS_MODEL_FILTER` explicitly, and the library applies it to raw workspace endpoint IDs and Unity Catalog model-service FQNs after the additive union. A successful filtered-empty catalog is authoritative: it stays empty, disables switching, and never falls through to configured or known-model fallback. UC FQNs are catalog data and always use the MLflow Chat Completions route, regardless of family-looking text in their components. Global Defaults preserves the discovered model ID as the selected value while its closed trigger renders the provider-scoped display label; do not force the raw persisted ID over that label. +17. **Databricks model discovery has one shared catalog authority.** Desktop and ACP call the shared `buzz-agent` discovery library; Desktop passes the effective merged `DATABRICKS_MODEL_FILTER` explicitly, and the library applies it to raw workspace endpoint IDs and Unity Catalog model-service FQNs after the additive union. A successful filtered-empty catalog is authoritative: it stays empty, disables switching, and never falls through to configured or known-model fallback. UC FQNs retain neutral effort capabilities. A boundary-matched GPT-5-or-newer family in the service-name component selects OpenAI Responses so tools can coexist with reasoning; other FQNs use MLflow Chat Completions. Catalog/schema components never influence routing. Keep this route-only rule identical in the Rust and TypeScript capability interpreters. Global Defaults preserves the discovered model ID as the selected value while its closed trigger renders the provider-scoped display label; do not force the raw persisted ID over that label. ## Channel-only runtime controls diff --git a/desktop/src/features/agents/ui/modelCapabilities.ts b/desktop/src/features/agents/ui/modelCapabilities.ts index f2c8de6035a..d6866c5c7e9 100644 --- a/desktop/src/features/agents/ui/modelCapabilities.ts +++ b/desktop/src/features/agents/ui/modelCapabilities.ts @@ -302,6 +302,19 @@ function isDatabricksModelServiceFqn(model: string): boolean { ); } +// Mirror fqn_requires_responses: routing is the only inferred FQN capability. +function fqnRequiresResponses(model: string): boolean { + const service = model.split(".").at(-1) ?? ""; + const stripped = stripCatalogPrefix( + service.toLowerCase(), + MANIFEST.family_tokens, + ); + const match = /^gpt-([0-9]+)(?:$|[^a-z0-9])/.exec(stripped); + if (!match) return false; + const major = Number(match[1]); + return major >= 5 && major <= 0xffffffff; +} + /** * Resolve the capability profile for a `(provider, rawModelId)` pair. * @@ -315,8 +328,8 @@ export function resolveModelCapabilities( ): CapabilityResult { const canon = canonicalizeProvider(provider); const blank = rawModelId.trim().length === 0; - // Unity Catalog FQNs are neutral model-service identities. Resolve them - // through the concrete-unknown fallback before suffix family matching. + // FQNs keep neutral effort capabilities; only GPT-5+ service names + // select Responses. Catalog/schema names never choose the protocol. const modelServiceFqn = canon === "databricks_v2" && isDatabricksModelServiceFqn(rawModelId); @@ -370,7 +383,11 @@ export function resolveModelCapabilities( // 3. Provider fallback (blank vs. concrete-unknown); never carries a label. const pair = fallbackPair(canon); const state = blank ? pair.blank : pair.concrete_unknown; - return toResult(state, state.databricks_v2_wire_route, null); + const route = + modelServiceFqn && fqnRequiresResponses(rawModelId) + ? "openai-responses" + : state.databricks_v2_wire_route; + return toResult(state, route, null); } /** Authoritative list of known Databricks v2 model ids, sourced from the manifest. */ diff --git a/desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs b/desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs index e0d150cc4f4..21baf6b64b5 100644 --- a/desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs +++ b/desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs @@ -25,10 +25,10 @@ const corpus = JSON.parse(readFileSync(fileURLToPath(corpusUrl), "utf8")); // (`_group`) are skipped. Mirrors the Rust corpus filter. const executable = corpus.filter((entry) => entry.expect != null); -test("corpus has exactly 140 executable vectors", () => { +test("corpus has exactly 158 executable vectors", () => { // Locks the vector count so a silent corpus edit can't quietly drop coverage; // must equal the gate in the Rust suite (model_capabilities.rs). - assert.equal(executable.length, 140); + assert.equal(executable.length, 158); }); test("registry label aliases refuse an unprefixed query", () => { diff --git a/scripts/normative-corpus.json b/scripts/normative-corpus.json index b75e4eb0d50..cb0ff4ae278 100644 --- a/scripts/normative-corpus.json +++ b/scripts/normative-corpus.json @@ -2347,6 +2347,370 @@ "registry_label": null } }, + { + "_group": "Databricks FQN GPT-5+ Responses routing", + "_note": "Only the service component selects Responses; effort capabilities remain neutral." + }, + { + "id": "dbv2-fqn-responses-0", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.goose-gpt-6-astra", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-1", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.goose-gpt-5", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-2", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-5-5", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-3", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-10", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-4", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.GOOSE-GPT-6-ASTRA", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-5", + "provider": "databricks_v2", + "raw_model_id": "gpt-6.schema.other", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-6", + "provider": "databricks_v2", + "raw_model_id": "catalog.gpt-5.other", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-7", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.claude-gpt-6", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-8", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.my-gpt-6-astra", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-9", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.mygpt-6-astra", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-10", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-4", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-11", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-4o", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-12", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-6x", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-13", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-oss-120b", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-14", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-15", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-4294967296", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-16", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-6", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-17", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.kimi-k3", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, { "_group": "Databricks UC model-family humanization probes (#6918 follow-up)", "_note": "Exact-record and UC-FQN strip probes for the Gemini/DeepSeek/GLM/Grok/Llama/Qwen/Gemma/Inkling families surfaced by UC discovery." From 44316ff72f5f7de014c66b01cbf534298a70c249 Mon Sep 17 00:00:00 2001 From: Mic Neale Date: Wed, 9 Sep 2026 00:30:45 +1000 Subject: [PATCH 04/26] feat(mesh): upgrade to mesh-llm 0.76.0-rc8 and recommend Qwen3.8 27B (#6189) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upgrades Buzz's mesh-llm dependencies through the released `v0.76.0-rc8` tag (`2040765d`), including the Qwen3.8 curated recommendation and rc8's scheduler/runtime improvements. **Scope note:** the earlier open-relay/unenforced-admission mode has been removed from this PR at Mic's direction — it is not a product mode we want. Mesh admission remains roster/allowlist driven, exactly as on `main`: on a relay with no NIP-43 membership snapshot the mesh runs self-only. No NIP-11 mode probing, no mode-transition restarts. A future perimeter/admission strategy for open relays will be designed separately. This PR also: - seeds `BUZZ_AGENT_LLM_TIMEOUT_SECS=660` for mesh agents, above MeshLLM's 600-second backend timeout; - makes `desktop-tauri-clippy` lint both default and `mesh-llm` cfg graphs; - runs the feature-enabled desktop test suite in CI; - recommends Qwen3.8 27B Q4_K_M for 64 GB-and-larger machines, then ladders down through Gemma 4 E4B and Qwen 9B for smaller machines; - keeps stored shared-compute `auto` translated to MeshLLM's supported wire model `mesh`. RC8 verification: - `just ci` passed locally at `92ecc7ec933bdd4df804cc9f28a2b51efa5313c5`. - Pre-push differential gates passed, including both desktop Tauri cfg graphs and package tests. - A prior isolated runtime smoke used the RC8 binary's OpenAI endpoint for a Buzz-shaped system/user/tool/tool-result/final-response loop; all assertions passed and the isolated process was shut down. Perf previously measured on M5 Metal, Qwen3.8-27B-Q4_K_M: TTFT 0.22–0.32 s, ~25 tok/s streaming; agent-shaped turns ~1 s to first token after the first (prefix cache). --------- Signed-off-by: Michael Neale Signed-off-by: Jimmy <1fe240cd1a8cf775f6f3060f115e5a303181f3abf28ad4cb0c2515f4a02b36a8@meshllm.communities.buzz.xyz> Signed-off-by: Alessandro Joabar Co-authored-by: Michael Neale Co-authored-by: Jimmy <1fe240cd1a8cf775f6f3060f115e5a303181f3abf28ad4cb0c2515f4a02b36a8@meshllm.communities.buzz.xyz> Co-authored-by: Mic Neale <305999590+micspiral@users.noreply.github.com> Co-authored-by: Alessandro Joabar --- .github/workflows/_ci-desktop.yml | 4 + Cargo.lock | 477 +++++++++--------- Justfile | 11 +- crates/buzz-relay/Cargo.toml | 4 +- desktop/src-tauri/Cargo.lock | 423 ++++++++-------- desktop/src-tauri/Cargo.toml | 14 +- desktop/src-tauri/src/commands/workspace.rs | 10 +- .../src/managed_agents/global_config/tests.rs | 2 +- .../src/managed_agents/relay_mesh.rs | 87 ++++ .../src-tauri/src/managed_agents/restore.rs | 32 +- desktop/src-tauri/src/mesh_llm/catalog.rs | 114 +++-- desktop/src-tauri/src/mesh_llm/mod.rs | 26 +- desktop/src-tauri/src/mesh_llm/recovery.rs | 1 + .../src/mesh_llm/transport_policy.rs | 41 +- 14 files changed, 689 insertions(+), 557 deletions(-) diff --git a/.github/workflows/_ci-desktop.yml b/.github/workflows/_ci-desktop.yml index 667b4841418..108e18a2152 100644 --- a/.github/workflows/_ci-desktop.yml +++ b/.github/workflows/_ci-desktop.yml @@ -92,6 +92,10 @@ jobs: run: just desktop-tauri-test env: CMAKE_POLICY_VERSION_MINIMUM: "3.5" + - name: Desktop Mesh feature tests + run: cargo test --manifest-path desktop/src-tauri/Cargo.toml --features mesh-llm --lib + env: + CMAKE_POLICY_VERSION_MINIMUM: "3.5" - name: Desktop Tauri compiled-flag verification run: just desktop-tauri-test-compiled-flags env: diff --git a/Cargo.lock b/Cargo.lock index d5f80a5fc82..e57698003fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,7 +799,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.23.0", + "darling 0.20.11", "ident_case", "prettyplease", "proc-macro2", @@ -1045,8 +1045,8 @@ version = "0.1.0" dependencies = [ "metrics", "metrics-util", - "opentelemetry 0.32.0", - "opentelemetry_sdk 0.32.1", + "opentelemetry", + "opentelemetry_sdk", "proc-macro2", "quote", "syn 2.0.117", @@ -1299,9 +1299,9 @@ dependencies = [ "metrics-util", "moka", "nostr 0.44.7", - "opentelemetry 0.32.0", - "opentelemetry-otlp 0.32.0", - "opentelemetry_sdk 0.32.1", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry_sdk", "postcard", "pulldown-cmark", "rand 0.10.1", @@ -1577,9 +1577,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -2789,6 +2789,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fancy-regex" version = "0.11.0" @@ -3346,6 +3358,15 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "hashlink" version = "0.11.0" @@ -4027,9 +4048,9 @@ dependencies = [ [[package]] name = "iroh" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fca9b4b462c343ff88fc0af4096c186f939b602a0bc08723536ef2c31c93971" +checksum = "460de6bc52163b41b1646931f2897e5ab986f0966ade444467fec25024751a72" dependencies = [ "backon", "blake3", @@ -4078,9 +4099,9 @@ dependencies = [ [[package]] name = "iroh-base" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830a582cd54410dc1aa71d4786a82c3297d7b0165accd8b6dbbb3b240b48140d" +checksum = "6be73e16ee21c923aca9b3121aaa0db936f7c7ecc156ff47b8dac944c68d59a8" dependencies = [ "curve25519-dalek 5.0.0-rc.0", "data-encoding", @@ -4097,9 +4118,9 @@ dependencies = [ [[package]] name = "iroh-dns" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516e4eedc38e33ab69a6bd325520332dc3d67b25454e2d590ebb84a25240dd9a" +checksum = "46f6a9b39d18e6345f5c151afd299f2488e2cb5c520fe41b107b6bd3dc4c3349" dependencies = [ "arc-swap", "cfg_aliases", @@ -4148,9 +4169,9 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8149bb6a57126225a07d6928846d82dcedfd24ea0f863ef7b2eb475e1d726354" +checksum = "24bd586cf927f7b700f56ec3639b53cb5fa901ce284784051ff71092bfbf8193" dependencies = [ "blake3", "bytes", @@ -4187,7 +4208,6 @@ dependencies = [ "tokio-websockets", "tracing", "url", - "vergen-gitcl", "webpki-roots 1.0.7", "ws_stream_wasm", ] @@ -4309,16 +4329,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733a844dbd6fef128e98cb4487b887cb55454d92cd9994b1bafe004fabbe670c" -dependencies = [ - "serde", - "ucd-trie", -] - [[package]] name = "jsonpath-rust" version = "0.7.5" @@ -4537,10 +4547,11 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.30.1" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ + "cc", "pkg-config", "vcpkg", ] @@ -4797,8 +4808,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "mesh-llm-client", @@ -4807,8 +4818,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -4818,21 +4829,18 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-llm-client" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "crypto_box", - "ed25519-dalek", - "hex", "httparse", "iroh", "mesh-llm-identity", @@ -4842,11 +4850,9 @@ dependencies = [ "model-artifact", "nostr-sdk", "prost 0.14.3", - "rand 0.10.1", "rustls", "serde", "serde_json", - "sha2 0.10.9", "thiserror 2.0.18", "tokio", "tracing", @@ -4855,8 +4861,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -4871,8 +4877,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -4881,20 +4887,25 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", + "chrono", "clap", "crossterm 0.28.1", + "libc", "ratatui", + "serde", "serde_json", + "tracing", + "uuid", ] [[package]] name = "mesh-llm-gpu-bench" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -4903,8 +4914,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -4912,8 +4923,8 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "mesh-llm-native-runtime", ] @@ -4949,8 +4960,8 @@ dependencies = [ [[package]] name = "mesh-llm-host-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "argon2", @@ -4972,7 +4983,6 @@ dependencies = [ "http-body-util", "httparse", "iroh", - "json5", "keyring", "libc", "mdns-sd", @@ -4984,6 +4994,7 @@ dependencies = [ "mesh-llm-guardrails", "mesh-llm-hf-hub", "mesh-llm-identity", + "mesh-llm-log-store", "mesh-llm-native-runtime", "mesh-llm-node", "mesh-llm-plugin", @@ -5003,9 +5014,9 @@ dependencies = [ "model-resolver", "nostr-sdk", "openai-frontend", - "opentelemetry 0.31.0", - "opentelemetry-otlp 0.31.1", - "opentelemetry_sdk 0.31.0", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry_sdk", "prost 0.14.3", "rand 0.10.1", "regex-lite", @@ -5017,7 +5028,6 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_yaml", "sha2 0.10.9", "skippy-coordinator", "skippy-ffi", @@ -5037,14 +5047,16 @@ dependencies = [ "tracing-subscriber", "url", "urlencoding", + "uuid", + "windows-sys 0.61.2", "zeroize", "zip", ] [[package]] name = "mesh-llm-identity" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "argon2", "base64 0.22.1", @@ -5063,10 +5075,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mesh-llm-log-store" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "chrono", + "data-encoding", + "hex", + "mesh-llm-events", + "rusqlite", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror 2.0.18", + "uuid", + "windows-sys 0.61.2", +] + [[package]] name = "mesh-llm-native-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "serde", @@ -5076,8 +5106,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-types", @@ -5090,8 +5120,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", @@ -5107,8 +5137,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5126,8 +5156,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "hex", @@ -5139,8 +5169,8 @@ dependencies = [ [[package]] name = "mesh-llm-release-footer" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "sha2 0.10.9", @@ -5148,16 +5178,19 @@ dependencies = [ [[package]] name = "mesh-llm-routing" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ + "blake3", "iroh", + "serde", + "serde_json", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5179,8 +5212,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5194,8 +5227,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5205,8 +5238,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "chrono", @@ -5232,8 +5265,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "serde", @@ -5243,13 +5276,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-mixture-of-agents" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5262,18 +5295,19 @@ dependencies = [ [[package]] name = "mesh-native-serving-plugin-api" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-native-serving-plugin-host" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "libloading", "mesh-native-serving-plugin-api", "skippy-server", + "skippy-tokenizer", ] [[package]] @@ -5413,8 +5447,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", @@ -5424,26 +5458,29 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", "chrono", "dirs", + "libc", "mesh-llm-hf-hub", "model-artifact", "model-ref", + "rustls", "serde", "serde_json", "sha2 0.10.9", "tokio", + "tracing", ] [[package]] name = "model-package" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "bytes", @@ -5462,16 +5499,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "model-artifact", @@ -5822,9 +5859,9 @@ dependencies = [ [[package]] name = "noq" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf95190af1bd4a00a10e8255ca0c8ddd9e9a9f5e79151d7a7eb6d56aff5dc89" +checksum = "09e4bb6601fa543c110d8957813267d5a8d775a0f8fbaccf1f615d06ba9b10da" dependencies = [ "bytes", "cfg_aliases", @@ -5844,9 +5881,9 @@ dependencies = [ [[package]] name = "noq-proto" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" +checksum = "baa7b5ccd819a9c68a0d955e67a881032d09b1a17219b1f90b0997a0888e1a15" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -5872,9 +5909,9 @@ dependencies = [ [[package]] name = "noq-udp" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3137a52df66c20090a889828d1c655f21f52294cba64e5c4fbb04fc83eee7c8e" +checksum = "02bba20e097a5a16cd0ad14ec882fae1e80a092a124e9422fc4dddd92e96a647" dependencies = [ "cfg_aliases", "libc", @@ -6272,19 +6309,20 @@ checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" [[package]] name = "openai-frontend" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "async-trait", "axum", "futures-core", "futures-util", + "mesh-llm-events", "mesh-llm-guardrails", "serde", "serde_json", "tokio", - "tokio-stream", "tracing", + "uuid", ] [[package]] @@ -6340,20 +6378,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror 2.0.18", - "tracing", -] - [[package]] name = "opentelemetry" version = "0.32.0" @@ -6370,31 +6394,15 @@ dependencies = [ [[package]] name = "opentelemetry-http" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331" dependencies = [ "async-trait", "bytes", "http", - "opentelemetry 0.31.0", - "reqwest 0.12.28", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" -dependencies = [ - "http", - "opentelemetry 0.31.0", - "opentelemetry-http", - "opentelemetry-proto 0.31.0", - "opentelemetry_sdk 0.31.0", - "prost 0.14.3", - "reqwest 0.12.28", - "thiserror 2.0.18", + "opentelemetry", + "reqwest 0.13.4", ] [[package]] @@ -6404,10 +6412,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35" dependencies = [ "http", - "opentelemetry 0.32.0", - "opentelemetry-proto 0.32.0", - "opentelemetry_sdk 0.32.1", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", "prost 0.14.3", + "reqwest 0.13.4", "thiserror 2.0.18", "tokio", "tonic", @@ -6416,49 +6426,20 @@ dependencies = [ [[package]] name = "opentelemetry-proto" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" dependencies = [ "base64 0.22.1", "const-hex", - "opentelemetry 0.31.0", - "opentelemetry_sdk 0.31.0", + "opentelemetry", + "opentelemetry_sdk", "prost 0.14.3", "serde", - "serde_json", "tonic", "tonic-prost", ] -[[package]] -name = "opentelemetry-proto" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" -dependencies = [ - "opentelemetry 0.32.0", - "opentelemetry_sdk 0.32.1", - "prost 0.14.3", - "tonic", - "tonic-prost", -] - -[[package]] -name = "opentelemetry_sdk" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" -dependencies = [ - "futures-channel", - "futures-executor", - "futures-util", - "opentelemetry 0.31.0", - "percent-encoding", - "rand 0.9.4", - "thiserror 2.0.18", -] - [[package]] name = "opentelemetry_sdk" version = "0.32.1" @@ -6468,7 +6449,7 @@ dependencies = [ "futures-channel", "futures-executor", "futures-util", - "opentelemetry 0.32.0", + "opentelemetry", "percent-encoding", "portable-atomic", "rand 0.9.4", @@ -7840,7 +7821,6 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", - "futures-channel", "futures-core", "futures-util", "h2", @@ -7888,6 +7868,7 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", @@ -8006,12 +7987,37 @@ dependencies = [ [[package]] name = "rpassword" -version = "5.0.1" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +checksum = "2da316a15f47e3d053de9cb2c439650bd8fa4aaeb9365f2e5f27f492ff73c196" dependencies = [ "libc", - "winapi", + "rtoolbox", + "windows-sys 0.61.2", +] + +[[package]] +name = "rtoolbox" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1efe12a1469752d0e6ff5ebec0b6ef4924cc5c4c71046b0ec730040535819d" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "rusqlite" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +dependencies = [ + "bitflags 2.13.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink 0.10.0", + "libsqlite3-sys", + "smallvec", ] [[package]] @@ -8802,8 +8808,8 @@ checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" [[package]] name = "skippy-cache" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "blake3", @@ -8812,29 +8818,29 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "libloading", ] [[package]] name = "skippy-metrics" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "skippy-protocol" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "prost 0.14.3", "prost-build 0.14.3", @@ -8845,8 +8851,8 @@ dependencies = [ [[package]] name = "skippy-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "libc", @@ -8857,10 +8863,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "skippy-scheduler" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "skippy-runtime", + "thiserror 2.0.18", +] + [[package]] name = "skippy-server" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "ahash", "anyhow", @@ -8871,10 +8886,11 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-llm-events", "mesh-native-serving-plugin-api", "model-artifact", "openai-frontend", - "opentelemetry-proto 0.31.0", + "opentelemetry-proto", "serde", "serde_json", "sha2 0.10.9", @@ -8882,25 +8898,27 @@ dependencies = [ "skippy-metrics", "skippy-protocol", "skippy-runtime", + "skippy-scheduler", "skippy-tokenizer", + "skippy-topology", "socket2", "tokio", - "tokio-stream", "tonic", + "uuid", ] [[package]] name = "skippy-tokenizer" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", ] [[package]] name = "skippy-topology" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -9037,7 +9055,7 @@ dependencies = [ "futures-io", "futures-util", "hashbrown 0.16.1", - "hashlink", + "hashlink 0.11.0", "indexmap", "log", "memchr", @@ -10235,7 +10253,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" dependencies = [ "js-sys", - "opentelemetry 0.32.0", + "opentelemetry", "smallvec", "tracing", "tracing-core", @@ -10564,43 +10582,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vergen" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "vergen-lib", -] - -[[package]] -name = "vergen-gitcl" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ff3b5300a085d6bcd8fc96a507f706a28ae3814693236c9b409db71a1d15b9" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "time", - "vergen", - "vergen-lib", -] - -[[package]] -name = "vergen-lib" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", -] - [[package]] name = "version_check" version = "0.9.5" diff --git a/Justfile b/Justfile index e44d3158777..00c4fecc2f9 100644 --- a/Justfile +++ b/Justfile @@ -211,8 +211,13 @@ _ensure-migrations: _ensure-services ./scripts/seed-local-community.sh # Run clippy on the desktop Tauri Rust crate +# Features are additive, so a single invocation lints only one cfg graph. +# Both graphs ship (release-windows builds without mesh-llm), so lint both: +# the default graph covers the `#[cfg(not(feature = "mesh-llm"))]` arms and +# the feature-enabled graph covers the mesh code. desktop-tauri-clippy: _ensure-sidecar-stubs cargo clippy --manifest-path {{desktop_tauri_manifest}} --workspace --all-targets -- -D warnings + cargo clippy --manifest-path {{desktop_tauri_manifest}} --workspace --all-targets --features mesh-llm -- -D warnings # Check the desktop Tauri Rust crate compiles desktop-tauri-check: _ensure-sidecar-stubs @@ -682,7 +687,11 @@ desktop-standalone *ARGS: _ensure-sidecar-stubs fi trap '../scripts/cleanup-instance-agents.sh "$INSTANCE_ID" || true' EXIT echo "Starting standalone desktop on Vite port ${BUZZ_VITE_PORT}; no relay services were started" - pnpm exec tauri dev --config "$BUZZ_TAURI_CONFIG" {{ARGS}} + FEATURES=() + if [[ -n "{{mesh}}" ]]; then + FEATURES=(--features mesh-llm) + fi + pnpm exec tauri dev ${FEATURES[@]+"${FEATURES[@]}"} --config "$BUZZ_TAURI_CONFIG" {{ARGS}} # Run the desktop app against the internal staging relay (installs deps + builds agent tools automatically) staging *ARGS: bootstrap _ensure-sidecar-stubs diff --git a/crates/buzz-relay/Cargo.toml b/crates/buzz-relay/Cargo.toml index deb2e7e16a5..d022bcab01c 100644 --- a/crates/buzz-relay/Cargo.toml +++ b/crates/buzz-relay/Cargo.toml @@ -86,8 +86,8 @@ async-compression = { version = "0.4.42", features = ["tokio", "gzip"] } dev = ["buzz-auth/dev"] [dev-dependencies] -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } # Relay-driven mesh lifecycle smoke (examples/mesh_relay_lifecycle_smoke.rs): # the relay client for discovery notes and the exact ed25519 the mesh owner # keys use for binding verification. diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index 10e2e7491ac..6af27ee2438 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -210,7 +210,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -221,7 +221,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1476,9 +1476,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -1641,7 +1641,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -2308,7 +2308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.118", ] [[package]] @@ -2486,7 +2486,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2767,7 +2767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3295,7 +3295,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.1.3", + "windows-link 0.2.1", "windows-result 0.4.1", ] @@ -4369,14 +4369,14 @@ dependencies = [ [[package]] name = "iroh" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fca9b4b462c343ff88fc0af4096c186f939b602a0bc08723536ef2c31c93971" +checksum = "460de6bc52163b41b1646931f2897e5ab986f0966ade444467fec25024751a72" dependencies = [ "backon", "blake3", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "ctutils", "data-encoding", "derive_more", @@ -4420,9 +4420,9 @@ dependencies = [ [[package]] name = "iroh-base" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830a582cd54410dc1aa71d4786a82c3297d7b0165accd8b6dbbb3b240b48140d" +checksum = "6be73e16ee21c923aca9b3121aaa0db936f7c7ecc156ff47b8dac944c68d59a8" dependencies = [ "curve25519-dalek 5.0.0-rc.0", "data-encoding", @@ -4439,12 +4439,12 @@ dependencies = [ [[package]] name = "iroh-dns" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516e4eedc38e33ab69a6bd325520332dc3d67b25454e2d590ebb84a25240dd9a" +checksum = "46f6a9b39d18e6345f5c151afd299f2488e2cb5c520fe41b107b6bd3dc4c3349" dependencies = [ "arc-swap", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "hickory-resolver", "iroh-base", @@ -4490,13 +4490,13 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8149bb6a57126225a07d6928846d82dcedfd24ea0f863ef7b2eb475e1d726354" +checksum = "24bd586cf927f7b700f56ec3639b53cb5fa901ce284784051ff71092bfbf8193" dependencies = [ "blake3", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "data-encoding", "derive_more", "getrandom 0.4.3", @@ -4529,7 +4529,6 @@ dependencies = [ "tokio-websockets", "tracing", "url", - "vergen-gitcl", "webpki-roots 1.0.8", "ws_stream_wasm", ] @@ -4704,16 +4703,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "json5" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733a844dbd6fef128e98cb4487b887cb55454d92cd9994b1bafe004fabbe670c" -dependencies = [ - "serde", - "ucd-trie", -] - [[package]] name = "jsonptr" version = "0.6.3" @@ -5187,8 +5176,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "mesh-llm-client", @@ -5197,8 +5186,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5208,21 +5197,18 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-llm-client" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "crypto_box", - "ed25519-dalek", - "hex", "httparse", "iroh", "mesh-llm-identity", @@ -5232,11 +5218,9 @@ dependencies = [ "model-artifact", "nostr-sdk", "prost 0.14.4", - "rand 0.10.2", "rustls", "serde", "serde_json", - "sha2 0.10.9", "thiserror 2.0.18", "tokio", "tracing", @@ -5245,8 +5229,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5261,8 +5245,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -5271,20 +5255,25 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", + "chrono", "clap", "crossterm 0.28.1", + "libc", "ratatui", + "serde", "serde_json", + "tracing", + "uuid", ] [[package]] name = "mesh-llm-gpu-bench" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -5293,8 +5282,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -5302,8 +5291,8 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "mesh-llm-native-runtime", ] @@ -5339,8 +5328,8 @@ dependencies = [ [[package]] name = "mesh-llm-host-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "argon2", @@ -5362,7 +5351,6 @@ dependencies = [ "http-body-util", "httparse", "iroh", - "json5", "keyring", "libc", "mdns-sd", @@ -5374,6 +5362,7 @@ dependencies = [ "mesh-llm-guardrails", "mesh-llm-hf-hub", "mesh-llm-identity", + "mesh-llm-log-store", "mesh-llm-native-runtime", "mesh-llm-node", "mesh-llm-plugin", @@ -5407,7 +5396,6 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_yaml", "sha2 0.10.9", "skippy-coordinator", "skippy-ffi", @@ -5427,14 +5415,16 @@ dependencies = [ "tracing-subscriber", "url", "urlencoding", + "uuid", + "windows-sys 0.61.2", "zeroize", "zip 2.4.2", ] [[package]] name = "mesh-llm-identity" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "argon2", "base64 0.22.1", @@ -5453,10 +5443,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mesh-llm-log-store" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "chrono", + "data-encoding", + "hex", + "mesh-llm-events", + "rusqlite", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror 2.0.18", + "uuid", + "windows-sys 0.61.2", +] + [[package]] name = "mesh-llm-native-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "serde", @@ -5466,8 +5474,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-types", @@ -5480,8 +5488,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", @@ -5497,8 +5505,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5516,8 +5524,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "hex", @@ -5529,8 +5537,8 @@ dependencies = [ [[package]] name = "mesh-llm-release-footer" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "sha2 0.10.9", @@ -5538,16 +5546,19 @@ dependencies = [ [[package]] name = "mesh-llm-routing" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ + "blake3", "iroh", + "serde", + "serde_json", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5569,8 +5580,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5584,8 +5595,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5595,8 +5606,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "chrono", @@ -5622,8 +5633,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "serde", @@ -5633,13 +5644,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-mixture-of-agents" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5652,18 +5663,19 @@ dependencies = [ [[package]] name = "mesh-native-serving-plugin-api" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-native-serving-plugin-host" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "libloading 0.8.9", "mesh-native-serving-plugin-api", "skippy-server", + "skippy-tokenizer", ] [[package]] @@ -5753,13 +5765,13 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "model-artifact" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", @@ -5769,26 +5781,29 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", "chrono", "dirs", + "libc", "mesh-llm-hf-hub", "model-artifact", "model-ref", + "rustls", "serde", "serde_json", "sha2 0.10.9", "tokio", + "tracing", ] [[package]] name = "model-package" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "bytes", @@ -5807,16 +5822,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "model-artifact", @@ -5912,7 +5927,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5948,7 +5963,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2ab99dfb861450e68853d34ae665243a88b8c493d01ba957321a1e9b2312bbe" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "futures-buffered", "futures-lite", @@ -6138,7 +6153,7 @@ checksum = "4d9cbe01741347ef750d743d6690603f5eed8341e679fb51c8e629337aa11976" dependencies = [ "atomic-waker", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "ipnet", "js-sys", @@ -6193,7 +6208,7 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "memoffset", ] @@ -6206,7 +6221,7 @@ checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", ] @@ -6218,7 +6233,7 @@ checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", ] @@ -6243,12 +6258,12 @@ dependencies = [ [[package]] name = "noq" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf95190af1bd4a00a10e8255ca0c8ddd9e9a9f5e79151d7a7eb6d56aff5dc89" +checksum = "09e4bb6601fa543c110d8957813267d5a8d775a0f8fbaccf1f615d06ba9b10da" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "noq-proto", "noq-udp", @@ -6265,9 +6280,9 @@ dependencies = [ [[package]] name = "noq-proto" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" +checksum = "baa7b5ccd819a9c68a0d955e67a881032d09b1a17219b1f90b0997a0888e1a15" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -6293,11 +6308,11 @@ dependencies = [ [[package]] name = "noq-udp" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3137a52df66c20090a889828d1c655f21f52294cba64e5c4fbb04fc83eee7c8e" +checksum = "02bba20e097a5a16cd0ad14ec882fae1e80a092a124e9422fc4dddd92e96a647" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "socket2", "tracing", @@ -6425,7 +6440,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6535,7 +6550,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate 2.0.2", "proc-macro2", "quote", "syn 2.0.118", @@ -6917,19 +6932,20 @@ dependencies = [ [[package]] name = "openai-frontend" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "async-trait", "axum", "futures-core", "futures-util", + "mesh-llm-events", "mesh-llm-guardrails", "serde", "serde_json", "tokio", - "tokio-stream", "tracing", + "uuid", ] [[package]] @@ -6987,9 +7003,9 @@ dependencies = [ [[package]] name = "opentelemetry" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" +checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" dependencies = [ "futures-core", "futures-sink", @@ -7001,22 +7017,22 @@ dependencies = [ [[package]] name = "opentelemetry-http" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331" dependencies = [ "async-trait", "bytes", "http", "opentelemetry", - "reqwest 0.12.28", + "reqwest 0.13.4", ] [[package]] name = "opentelemetry-otlp" -version = "0.31.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" +checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35" dependencies = [ "http", "opentelemetry", @@ -7024,15 +7040,15 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost 0.14.4", - "reqwest 0.12.28", + "reqwest 0.13.4", "thiserror 2.0.18", ] [[package]] name = "opentelemetry-proto" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" dependencies = [ "base64 0.22.1", "const-hex", @@ -7040,22 +7056,22 @@ dependencies = [ "opentelemetry_sdk", "prost 0.14.4", "serde", - "serde_json", "tonic", "tonic-prost", ] [[package]] name = "opentelemetry_sdk" -version = "0.31.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" +checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9" dependencies = [ "futures-channel", "futures-executor", "futures-util", "opentelemetry", "percent-encoding", + "portable-atomic", "rand 0.9.4", "thiserror 2.0.18", ] @@ -7129,7 +7145,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] @@ -8108,7 +8124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "pin-project-lite", "quinn-proto", "quinn-udp", @@ -8150,12 +8166,12 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8529,7 +8545,6 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", - "futures-channel", "futures-core", "futures-util", "h2", @@ -8745,12 +8760,23 @@ dependencies = [ [[package]] name = "rpassword" -version = "5.0.1" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +checksum = "2da316a15f47e3d053de9cb2c439650bd8fa4aaeb9365f2e5f27f492ff73c196" dependencies = [ "libc", - "winapi", + "rtoolbox", + "windows-sys 0.61.2", +] + +[[package]] +name = "rtoolbox" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1efe12a1469752d0e6ff5ebec0b6ef4924cc5c4c71046b0ec730040535819d" +dependencies = [ + "libc", + "windows-sys 0.61.2", ] [[package]] @@ -8886,7 +8912,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8956,7 +8982,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -9223,7 +9249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -9708,8 +9734,8 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "skippy-cache" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "blake3", @@ -9718,29 +9744,29 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "libloading 0.8.9", ] [[package]] name = "skippy-metrics" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "skippy-protocol" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "prost 0.14.4", "prost-build 0.14.4", @@ -9751,8 +9777,8 @@ dependencies = [ [[package]] name = "skippy-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "libc", @@ -9763,10 +9789,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "skippy-scheduler" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "skippy-runtime", + "thiserror 2.0.18", +] + [[package]] name = "skippy-server" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "ahash", "anyhow", @@ -9777,6 +9812,7 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-llm-events", "mesh-native-serving-plugin-api", "model-artifact", "openai-frontend", @@ -9788,25 +9824,27 @@ dependencies = [ "skippy-metrics", "skippy-protocol", "skippy-runtime", + "skippy-scheduler", "skippy-tokenizer", + "skippy-topology", "socket2", "tokio", - "tokio-stream", "tonic", + "uuid", ] [[package]] name = "skippy-tokenizer" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", ] [[package]] name = "skippy-topology" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -9842,7 +9880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10867,10 +10905,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -10892,7 +10930,7 @@ dependencies = [ "parking_lot", "rustix 1.1.4", "signal-hook 0.3.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11634,7 +11672,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11735,7 +11773,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11998,43 +12036,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vergen" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "vergen-lib", -] - -[[package]] -name = "vergen-gitcl" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ff3b5300a085d6bcd8fc96a507f706a28ae3814693236c9b409db71a1d15b9" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "time", - "vergen", - "vergen-lib", -] - -[[package]] -name = "vergen-lib" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", -] - [[package]] name = "version-compare" version = "0.2.1" @@ -12586,7 +12587,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 36842d1677a..a7ae5229d1a 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -110,15 +110,15 @@ buzz_voice_pkg = { package = "buzz-voice", path = "../../crates/buzz-voice" } buzz_terminal = { package = "buzz-terminal", path = "crates/buzz-terminal" } buzz_ws_client_pkg = { package = "buzz-ws-client", path = "../../crates/buzz-ws-client" } portable-pty = "0.9" -iroh = { version = "1.0.2", optional = true } -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } +iroh = { version = "1.0.3", optional = true } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } # Model catalog + hardware survey for the Share-compute model picker (same # diagnose pattern as mesh-console). Lib name of mesh-llm-client is mesh_client. -mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-client", optional = true } -mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-node", optional = true } -mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-system", optional = true } -mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-events", optional = true } +mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-client", optional = true } +mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-node", optional = true } +mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-system", optional = true } +mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-events", optional = true } base64 = "0.22" sha2 = "0.11" tar = "0.4" diff --git a/desktop/src-tauri/src/commands/workspace.rs b/desktop/src-tauri/src/commands/workspace.rs index 418f994fb3e..c01c8473d91 100644 --- a/desktop/src-tauri/src/commands/workspace.rs +++ b/desktop/src-tauri/src/commands/workspace.rs @@ -353,7 +353,7 @@ pub async fn apply_workspace( } } }); - return Ok(()); + Ok(()) } #[cfg(not(feature = "mesh-llm"))] @@ -372,9 +372,11 @@ pub async fn apply_workspace( return Ok(()); } - assert_current_apply_generation(&state.workspace_apply_generation, apply_generation)?; - - Ok(()) + #[cfg(not(feature = "mesh-llm"))] + { + assert_current_apply_generation(&state.workspace_apply_generation, apply_generation)?; + Ok(()) + } } #[cfg(test)] diff --git a/desktop/src-tauri/src/managed_agents/global_config/tests.rs b/desktop/src-tauri/src/managed_agents/global_config/tests.rs index 5f39b7b75f2..5331b77e27a 100644 --- a/desktop/src-tauri/src/managed_agents/global_config/tests.rs +++ b/desktop/src-tauri/src/managed_agents/global_config/tests.rs @@ -509,7 +509,7 @@ fn inherited_shared_compute_translates_to_supported_agent_transport() { ); assert_eq!( effective.env.get("BUZZ_AGENT_MODEL").map(String::as_str), - Some("auto") + Some(super::super::RELAY_MESH_VIRTUAL_MODEL_ID) ); assert_eq!( effective diff --git a/desktop/src-tauri/src/managed_agents/relay_mesh.rs b/desktop/src-tauri/src/managed_agents/relay_mesh.rs index 3858212bbba..b6d5e33f309 100644 --- a/desktop/src-tauri/src/managed_agents/relay_mesh.rs +++ b/desktop/src-tauri/src/managed_agents/relay_mesh.rs @@ -66,6 +66,21 @@ pub fn apply_relay_mesh_env( // survives (see `insert_default_if_unset`, and the copy-forward list in // `relay_mesh_process_env` that preserves it through the spawn path). insert_default_if_unset(env, "BUZZ_AGENT_REQUIRE_REPLY", "1"); + // A local mesh prefills a cold multi-ten-thousand-token prompt at a few + // hundred tokens/second, so the first turn of a large session legitimately + // runs for minutes with no bytes on the wire (`stream: false`). MeshLLM's + // own frontend gives such a request 600 s before it aborts + // (`OpenAiFrontendConfig::DEFAULT_BACKEND_TIMEOUT`); buzz-agent's 240 s + // default abandons it at four minutes, so the client gives up on work the + // server is still doing and retries it — adding load to a box that is + // already prefilling. Seat the client just above the server's budget so the + // mesh's own error is what surfaces, rather than a client-side abort racing + // it. Measured: an 88,318-token cold prompt on an M5 Max returns 200 at + // 503 s. Remote providers keep the 240 s default; only mesh moves. + // A default, not policy — an explicit user value survives via + // `insert_default_if_unset` and the copy-forward list in + // `relay_mesh_process_env`. + insert_default_if_unset(env, "BUZZ_AGENT_LLM_TIMEOUT_SECS", "660"); // Deliberately no BUZZ_AGENT_THINKING_EFFORT default: mesh translates // `reasoning_effort` into the chat template's `enable_thinking` flag, so any // value we pick overrides each model's own template default — and the right @@ -102,6 +117,10 @@ pub fn relay_mesh_process_env( for key in [ "BUZZ_AGENT_MAX_OUTPUT_TOKENS", "BUZZ_AGENT_THINKING_EFFORT", + // Same reason as `BUZZ_AGENT_REQUIRE_REPLY` below: without the + // copy-forward, an explicit user timeout is re-defaulted to 660 by + // `apply_relay_mesh_env`. + "BUZZ_AGENT_LLM_TIMEOUT_SECS", // Must be copied forward for the user's value to survive: this map is // written onto the command *after* the layered user env, so a key absent // here is re-defaulted by `apply_relay_mesh_env` below and an explicit @@ -270,6 +289,74 @@ mod tests { ); } + /// MeshLLM's frontend allows a backend call 600 s + /// (`OpenAiFrontendConfig::DEFAULT_BACKEND_TIMEOUT`). buzz-agent's 240 s + /// default abandons a healthy cold prefill long before the server does and + /// retries it, so the client budget must sit above the server's. + #[test] + fn native_provider_outlasts_the_mesh_backend_timeout() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some(RELAY_MESH_AUTO_MODEL_ID), + ); + + let seconds: u64 = env + .get("BUZZ_AGENT_LLM_TIMEOUT_SECS") + .expect("mesh seeds a client timeout") + .parse() + .expect("timeout is whole seconds"); + assert!( + seconds > 600, + "client budget ({seconds}s) must outlast the mesh frontend's 600s backend timeout" + ); + } + + #[test] + fn native_provider_preserves_explicit_llm_timeout() { + let mut env = + BTreeMap::from([("BUZZ_AGENT_LLM_TIMEOUT_SECS".to_string(), "90".to_string())]); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some(RELAY_MESH_AUTO_MODEL_ID), + ); + + assert_eq!( + env.get("BUZZ_AGENT_LLM_TIMEOUT_SECS").map(String::as_str), + Some("90"), + "an explicit timeout is a user decision, not a value to re-default" + ); + } + + /// Same spawn-ordering hazard as the reply guard: without the copy-forward + /// in `relay_mesh_process_env`, the user's value is re-defaulted here. + #[test] + fn process_env_preserves_explicit_llm_timeout() { + let effective_env = + BTreeMap::from([("BUZZ_AGENT_LLM_TIMEOUT_SECS".to_string(), "90".to_string())]); + + let env = relay_mesh_process_env(&effective_env, "Gemma-4"); + + assert_eq!( + env.get("BUZZ_AGENT_LLM_TIMEOUT_SECS").map(String::as_str), + Some("90") + ); + } + + #[test] + fn non_mesh_provider_leaves_llm_timeout_unset() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env(&mut env, Some("anthropic"), Some("claude-haiku-4.5")); + + assert_eq!( + env.get("BUZZ_AGENT_LLM_TIMEOUT_SECS"), + None, + "remote providers keep buzz-agent's own default" + ); + } + #[test] fn non_mesh_provider_leaves_reply_guard_unset() { let mut env = BTreeMap::new(); diff --git a/desktop/src-tauri/src/managed_agents/restore.rs b/desktop/src-tauri/src/managed_agents/restore.rs index 5b79ccac27f..d29bec4a784 100644 --- a/desktop/src-tauri/src/managed_agents/restore.rs +++ b/desktop/src-tauri/src/managed_agents/restore.rs @@ -545,22 +545,6 @@ pub(crate) fn spawn_pending_profile_reconciliations(app: &tauri::AppHandle, work } } -#[cfg(test)] -mod profile_reconcile_tests { - use super::profile_reconcile_completed; - use crate::commands::ProfileReconcileOutcome; - - #[test] - fn skipped_reconciliation_never_retires_pending_work() { - assert!(profile_reconcile_completed( - ProfileReconcileOutcome::Reconciled - )); - assert!(!profile_reconcile_completed( - ProfileReconcileOutcome::SkippedDisabled - )); - } -} - #[cfg(feature = "mesh-llm")] fn persist_restore_error( app: &tauri::AppHandle, @@ -578,3 +562,19 @@ fn persist_restore_error( record.last_error = Some(error); save_managed_agents(app, &records) } + +#[cfg(test)] +mod profile_reconcile_tests { + use super::profile_reconcile_completed; + use crate::commands::ProfileReconcileOutcome; + + #[test] + fn skipped_reconciliation_never_retires_pending_work() { + assert!(profile_reconcile_completed( + ProfileReconcileOutcome::Reconciled + )); + assert!(!profile_reconcile_completed( + ProfileReconcileOutcome::SkippedDisabled + )); + } +} diff --git a/desktop/src-tauri/src/mesh_llm/catalog.rs b/desktop/src-tauri/src/mesh_llm/catalog.rs index 1a11fcfcd13..eb5d0c1cf16 100644 --- a/desktop/src-tauri/src/mesh_llm/catalog.rs +++ b/desktop/src-tauri/src/mesh_llm/catalog.rs @@ -13,38 +13,50 @@ use mesh_llm_system::hardware; use mesh_llm_system::vram::{format_rated_capacity, rated_capacity_gb}; /// Buzz-curated tier picks. These are the models we know survive the agent -/// harness on shared compute — deliberately non-reasoning instruction models, -/// so agents stay snappy instead of burning hidden reasoning tokens. +/// harness on shared compute. /// -/// The large pick is resolved through mesh-llm's remote catalog -/// (huggingface.co/datasets/meshllm/catalog), so it does not need to exist in -/// the compiled `MODEL_CATALOG`; the entry is synthesized below. -const CURATED_LARGE: &str = "unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M"; -const CURATED_LARGE_ALIAS: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M"; -const CURATED_LARGE_SIZE: &str = "17GB"; -const CURATED_LARGE_FILE: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M.gguf"; -const CURATED_LARGE_DESCRIPTION: &str = - "Gemma 4 26B MoE (4B active) — Buzz default for 64GB+ machines"; +/// The recommended ladder follows rated unified memory: +/// - below 32 GB: Gemma 4 E4B; +/// - 32 GB through the rated classes below 64 GB: Qwen3.5 9B; +/// - 64 GB and above: Qwen3.8 27B. +/// +/// The Qwen entries are canonicalized from mesh-llm's compiled +/// `MODEL_CATALOG` rather than synthesized. +const CURATED_LARGE: &str = "unsloth/Qwen3.8-27B-GGUF:Q4_K_M"; +const CURATED_LARGE_ALIAS: &str = "Qwen3.8-27B-Q4_K_M"; +const CURATED_MEDIUM: &str = "unsloth/Qwen3.5-9B-GGUF:Q4_K_M"; +const CURATED_MEDIUM_ALIAS: &str = "Qwen3.5-9B-Vision-Q4_K_M"; const CURATED_SMALL: &str = "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M"; const CURATED_SMALL_ALIAS: &str = "Gemma-4-E4B-it-Q4_K_M"; -/// Rated-capacity boundary between the two curated tiers, in GB (marketing -/// capacity — a "64GB" Mac rates as 64 even though usable AI memory is less). +/// Superseded large alias retained only to preserve the historical string +/// canonicalization contract. Availability in a particular Mesh runtime is +/// determined by that runtime's compiled catalog. +const LEGACY_LARGE: &str = "unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M"; +const LEGACY_LARGE_ALIAS: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M"; +/// Rated-capacity boundary for the balanced Qwen3.5 9B tier. +const CURATED_MEDIUM_MIN_RATED_GB: u64 = 32; +/// Qwen3.8 27B is recommended for 64 GB-and-larger rated capacity classes, +/// leaving substantial headroom beyond its observed working footprint for KV +/// cache and runtime use. const CURATED_LARGE_MIN_RATED_GB: u64 = 64; /// The Buzz-curated recommendation for a machine's rated memory capacity. fn buzz_recommended_model(rated_gb: Option) -> &'static str { match rated_gb { Some(gb) if gb >= CURATED_LARGE_MIN_RATED_GB => CURATED_LARGE, - _ => CURATED_SMALL, + Some(gb) if gb >= CURATED_MEDIUM_MIN_RATED_GB => CURATED_MEDIUM, + Some(_) | None => CURATED_SMALL, } } -/// Convert Buzz's pre-0.74 curated package aliases into the canonical model +/// Convert Buzz's historical curated package aliases into the canonical model /// ids advertised and accepted by Mesh's OpenAI ingress. pub(crate) fn canonical_curated_model_id(model_id: &str) -> &str { match model_id.trim() { CURATED_SMALL_ALIAS => CURATED_SMALL, + CURATED_MEDIUM_ALIAS => CURATED_MEDIUM, CURATED_LARGE_ALIAS => CURATED_LARGE, + LEGACY_LARGE_ALIAS => LEGACY_LARGE, other => other, } } @@ -172,31 +184,14 @@ fn build_catalog( }) .collect(); - // The compiled MODEL_CATALOG does not know the Buzz large pick; it - // resolves through mesh-llm's remote catalog at download time. Synthesize - // its entry so the picker can offer it. - if !entries.iter().any(|e| e.name == CURATED_LARGE) { - let size_gb = parse_size_gb(CURATED_LARGE_SIZE); - entries.push(MeshCatalogEntry { - fit: fit_code(size_gb, vram_gb), - installed: is_installed(CURATED_LARGE_FILE, CURATED_LARGE) - || is_installed(CURATED_LARGE_FILE, CURATED_LARGE_ALIAS), - recommended: false, - curated: false, - name: CURATED_LARGE.to_string(), - size: CURATED_LARGE_SIZE.to_string(), - size_gb, - description: CURATED_LARGE_DESCRIPTION.to_string(), - }); - } - let recommended = Some(buzz_recommended_model(rated_capacity_gb(vram_bytes)).to_string()); for entry in &mut entries { entry.recommended = recommended.as_deref() == Some(entry.name.as_str()); - // Both curated tiers are always offered: the recommended one for this - // machine plus the other pick (e.g. the small one as an explicit - // lighter choice on big machines). - entry.curated = entry.name == CURATED_LARGE || entry.name == CURATED_SMALL; + // All curated tiers are always offered: the recommendation plus + // lighter and heavier alternatives appropriate to other machine tiers. + entry.curated = entry.name == CURATED_LARGE + || entry.name == CURATED_MEDIUM + || entry.name == CURATED_SMALL; } entries.sort_by(|a, b| { @@ -269,20 +264,31 @@ mod tests { } #[test] - fn recommendation_follows_buzz_curated_tiers() { + fn recommendation_follows_buzz_curated_ladder() { assert_eq!(CURATED_SMALL, "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M"); - assert_eq!(CURATED_LARGE, "unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M"); - // 64GB+ rated machines get the large curated pick. + assert_eq!(CURATED_MEDIUM, "unsloth/Qwen3.5-9B-GGUF:Q4_K_M"); + assert_eq!(CURATED_LARGE, "unsloth/Qwen3.8-27B-GGUF:Q4_K_M"); + + // Qwen3.8 is recommended for 64 GB-and-larger rated capacity classes. let large = build_catalog(None, 64_000_000_000, 64.0, &[]); assert_eq!(large.recommended.as_deref(), Some(CURATED_LARGE)); + let large_80 = build_catalog(None, 80_000_000_000, 80.0, &[]); + assert_eq!(large_80.recommended.as_deref(), Some(CURATED_LARGE)); let big = build_catalog(None, 128_000_000_000, 128.0, &[]); assert_eq!(big.recommended.as_deref(), Some(CURATED_LARGE)); - // Below the boundary: the small curated pick — never a reasoning - // model, never sub-4B guesswork. - let small = build_catalog(None, 32_000_000_000, 32.0, &[]); + + // The balanced Qwen3.5 tier covers every rated class from 32 GB up + // to (but not including) 64 GB. + let medium = build_catalog(None, 32_000_000_000, 32.0, &[]); + assert_eq!(medium.recommended.as_deref(), Some(CURATED_MEDIUM)); + let medium_max = build_catalog(None, 48_000_000_000, 48.0, &[]); + assert_eq!(medium_max.recommended.as_deref(), Some(CURATED_MEDIUM)); + + // Smaller and unknown machines use the light Gemma tier. + let small = build_catalog(None, 24_000_000_000, 24.0, &[]); assert_eq!(small.recommended.as_deref(), Some(CURATED_SMALL)); - let tiny = build_catalog(None, 16_000_000_000, 16.0, &[]); - assert_eq!(tiny.recommended.as_deref(), Some(CURATED_SMALL)); + let unknown = build_catalog(None, 0, 0.0, &[]); + assert_eq!(unknown.recommended.as_deref(), Some(CURATED_SMALL)); } #[test] @@ -291,10 +297,15 @@ mod tests { canonical_curated_model_id(CURATED_SMALL_ALIAS), CURATED_SMALL ); + assert_eq!( + canonical_curated_model_id(CURATED_MEDIUM_ALIAS), + CURATED_MEDIUM + ); assert_eq!( canonical_curated_model_id(CURATED_LARGE_ALIAS), CURATED_LARGE ); + assert_eq!(canonical_curated_model_id(LEGACY_LARGE_ALIAS), LEGACY_LARGE); assert_eq!( canonical_curated_model_id("other/model:Q4"), "other/model:Q4" @@ -304,14 +315,17 @@ mod tests { #[test] fn curated_picks_lead_the_catalog() { let catalog = build_catalog(None, 96_000_000_000, 96.0, &[]); - // Recommended curated entry first, the other curated pick second, - // advanced entries after. + // Recommended curated entry first, then the other two curated tiers, + // with advanced entries after them. assert_eq!(catalog.entries[0].name, CURATED_LARGE); assert!(catalog.entries[0].recommended && catalog.entries[0].curated); - assert_eq!(catalog.entries[1].name, CURATED_SMALL); + assert_eq!(catalog.entries[1].name, CURATED_MEDIUM); assert!(catalog.entries[1].curated && !catalog.entries[1].recommended); - assert!(catalog.entries[2..].iter().all(|e| !e.curated)); - // The synthesized large pick carries a real size for fit ranking. + assert_eq!(catalog.entries[2].name, CURATED_SMALL); + assert!(catalog.entries[2].curated && !catalog.entries[2].recommended); + assert!(catalog.entries[3..].iter().all(|e| !e.curated)); + // The large pick comes from the compiled catalog with a real size, so + // fit ranking is meaningful rather than a placeholder. assert!(catalog.entries[0].size_gb > 10.0); } diff --git a/desktop/src-tauri/src/mesh_llm/mod.rs b/desktop/src-tauri/src/mesh_llm/mod.rs index e206c53886a..daab3c4b1c9 100644 --- a/desktop/src-tauri/src/mesh_llm/mod.rs +++ b/desktop/src-tauri/src/mesh_llm/mod.rs @@ -34,9 +34,9 @@ mod usage; pub use usage::{serving_usage_from_payload, MeshServingUsage}; mod transport_policy; +use transport_policy::{iroh_relay_mode, sdk_iroh_relay_config, validate_advertised_endpoint}; #[cfg(test)] -use transport_policy::iroh_relay_mode_from; -use transport_policy::{iroh_relay_mode, validate_advertised_endpoint, IrohRelayMode}; +use transport_policy::{iroh_relay_mode_from, IrohRelayMode}; use mesh_llm_sdk::{client, serve, EmbeddedNodeHandle, MeshDiscoveryMode, TrustPolicy}; use serde::{Deserialize, Serialize}; @@ -376,13 +376,10 @@ impl DesktopMeshRuntime { if let Some(mesh_name) = request.mesh_name.as_deref() { builder = builder.mesh_name(mesh_name); } - builder = match iroh_relay_mode()? { - IrohRelayMode::Disabled => builder.disable_iroh_relays(true), - IrohRelayMode::Default => builder.disable_iroh_relays(false), - IrohRelayMode::Custom(urls) => builder - .disable_iroh_relays(false) - .iroh_relays(urls.into_iter().map(|url| url.to_string())), - }; + let (disable_iroh_relays, iroh_relays) = sdk_iroh_relay_config(iroh_relay_mode()?); + builder = builder + .disable_iroh_relays(disable_iroh_relays) + .iroh_relays(iroh_relays); if let Some(max_vram_gb) = request.max_vram_gb { builder = builder.max_vram_gb(max_vram_gb as f64); } @@ -416,13 +413,10 @@ impl DesktopMeshRuntime { if let Some(mesh_name) = request.mesh_name.as_deref() { builder = builder.mesh_name(mesh_name); } - builder = match iroh_relay_mode()? { - IrohRelayMode::Disabled => builder.disable_iroh_relays(true), - IrohRelayMode::Default => builder.disable_iroh_relays(false), - IrohRelayMode::Custom(urls) => builder - .disable_iroh_relays(false) - .iroh_relays(urls.into_iter().map(|url| url.to_string())), - }; + let (disable_iroh_relays, iroh_relays) = sdk_iroh_relay_config(iroh_relay_mode()?); + builder = builder + .disable_iroh_relays(disable_iroh_relays) + .iroh_relays(iroh_relays); if let Some(join_token) = request.join_token.as_deref() { builder = builder.join_token(join_token); } diff --git a/desktop/src-tauri/src/mesh_llm/recovery.rs b/desktop/src-tauri/src/mesh_llm/recovery.rs index 6398f472505..a8b5584fd55 100644 --- a/desktop/src-tauri/src/mesh_llm/recovery.rs +++ b/desktop/src-tauri/src/mesh_llm/recovery.rs @@ -443,6 +443,7 @@ mod tests { id: pubkey.to_string(), display_name: pubkey.to_string(), avatar_url: None, + description: None, system_prompt: String::new(), runtime: None, model: None, diff --git a/desktop/src-tauri/src/mesh_llm/transport_policy.rs b/desktop/src-tauri/src/mesh_llm/transport_policy.rs index 122bffd60e4..24e5156295a 100644 --- a/desktop/src-tauri/src/mesh_llm/transport_policy.rs +++ b/desktop/src-tauri/src/mesh_llm/transport_policy.rs @@ -43,6 +43,22 @@ pub(super) fn iroh_relay_mode_from(raw: Option<&str>) -> anyhow::Result (bool, Vec) { + match mode { + IrohRelayMode::Disabled => (true, Vec::new()), + IrohRelayMode::Default => ( + false, + MESH_LLM_DEFAULT_RELAYS + .iter() + .map(|url| (*url).to_string()) + .collect(), + ), + IrohRelayMode::Custom(urls) => { + (false, urls.into_iter().map(|url| url.to_string()).collect()) + } + } +} + fn parse_configured_relay_url(raw: &str) -> anyhow::Result { let parsed = url::Url::parse(raw) .map_err(|error| anyhow::anyhow!("invalid relay URL {raw:?}: {error}"))?; @@ -205,9 +221,11 @@ fn validate_transport(transport: &TransportAddr, mode: &IrohRelayMode) -> anyhow /// mesh-llm serving node (they are not in iroh's own prod relay map). /// /// Kept in sync with `effective_relay_urls(RelayPolicy::DefaultPublic, &[])`. -const MESH_LLM_DEFAULT_RELAYS: &[&str] = &[ +pub(super) const MESH_LLM_DEFAULT_RELAYS: &[&str] = &[ "https://usw1-2.relay.michaelneale.mesh-llm.iroh.link./", "https://aps1-1.relay.michaelneale.mesh-llm.iroh.link./", + "https://euc1-1.relay.michaelneale.mesh-llm.iroh.link./", + "https://use1-1.relay.michaelneale.mesh-llm.iroh.link./", ]; /// Whether `relay` is one of mesh-llm's baked-in default public relays. @@ -327,6 +345,27 @@ mod tests { } } + #[test] + fn sdk_default_relays_are_exactly_the_trusted_default_relays() { + let (disabled, configured) = sdk_iroh_relay_config(IrohRelayMode::Default); + assert!(!disabled); + let configured = configured + .into_iter() + .map(|url| url.parse::().expect("SDK relay URL must parse")) + .collect::>(); + let trusted = MESH_LLM_DEFAULT_RELAYS + .iter() + .map(|url| { + url.parse::() + .expect("trusted relay URL must parse") + }) + .collect::>(); + assert_eq!(configured, trusted); + assert!(configured + .iter() + .all(|relay| relay_allowed(relay, &IrohRelayMode::Default))); + } + #[test] fn endpoint_with_one_good_and_one_junk_candidate_is_sanitized() { // A mesh-llm endpoint can advertise a usable relay alongside an From 218633b8fd6ee41aee8eb18ba9806e8d90694751 Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Tue, 8 Sep 2026 12:59:03 -0700 Subject: [PATCH 05/26] fix(link-preview): keep composer fetches user-paced (#7211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Category:** fix **User Impact:** Link previews can keep loading while a message is being composed, while sending still has a finite escape hatch and stalled network transports cannot occupy preview slots forever. **Problem:** Native metadata and image deadlines could collapse slow previews into fallback cards while the user was still composing, and a shared image-host cooldown made pasted batches fail inconsistently after one rate limit. **Solution:** Keep preview resolution user-paced with no aggregate request deadline, bound transport inactivity (15s DNS/connect, 30s idle read), serialize image requests by host, and allow at most one server-directed cooldown wait of up to 30s across an image fetch and its redirects. The existing bounded post-Send preparation and immediate Skip paths remain unchanged.
File changes **desktop/src-tauri/src/commands/link_preview.rs** Removes aggregate native deadlines so composer metadata work can complete at the user's pace, while retaining DNS/connect/idle-read liveness bounds. Adds bounded host-paced image request coordination that releases its gate during cooldown, waits inline at most once for at most 30 seconds, and cannot renew that wait through redirects or the outer transient retry. Same-host image and favicon requests remain deliberately serialized to align with host rate limits. **desktop/src-tauri/src/commands/link_preview_rate_limit.rs** Adds a fixed-size striped host gate so concurrent image requests are serialized without retaining an unbounded attacker-controlled hostname map. **desktop/src-tauri/src/commands/link_preview_tests.rs** Moves native link-preview tests into a dedicated module and covers the user-paced metadata contract, bounded one-shot cooldown behavior, and gate release while a rate-limited request sleeps—including a different host sharing the same bounded gate stripe. **desktop/src-tauri/src/commands/link_preview_youtube.rs** Removes the thumbnail fetch deadline so YouTube previews follow the same composer lifecycle contract while using the shared bounded transport. **desktop/src/shared/lib/useResolvedLinkPreviews.ts** Adds development-only metadata outcome diagnostics with elapsed time and image/fallback state, without logging encoded image payloads.
### Reproduction steps 1. Open the desktop composer and paste several GitHub pull request links whose OpenGraph images share a host. 2. Observe that image requests are paced by host instead of racing, and slow-but-progressing preview work remains pending rather than immediately becoming a completed favicon fallback. 3. Send while preview work is still pending and confirm **Preparing link preview** remains bounded by the existing post-Send budget. 4. Use **Skip** during preparation and confirm the message proceeds immediately. 5. In a development build, inspect the console for `[link-preview] metadata fetch completed` diagnostics containing elapsed time and image state without base64 payloads. ### Related issue N/A — scoped from the linked Buzz implementation room. ### Testing At current head `dfb394aafbee537e9ffb04ad3732d08f65f30b8e`: - Production-bound paused-time metadata regression passed through `fetch_link_preview_metadata`; restoring the former 10-second aggregate wrapper makes it fail at the pending assertion. - Native link-preview module: 19/19 passed. - `cargo check --manifest-path desktop/src-tauri/Cargo.toml` passed. - Rust formatting and `git diff --check` passed. - Pre-push `push-head-scope`, org safety, differential file-size, branch-skew, and `desktop-tauri-checks` hooks passed. At prior head `59e2dcf167b15c7a3e637ad2608008b7f9cef5f3`: - Full Tauri Rust suite: 3,056 passed, 19 ignored; integration crates 7 + 3 passed. - Focused native link-preview suite: 26/26 passed. - The pasted multi-preview workflow was exercised in the desktop app and confirmed improved before draft publication. --------- Signed-off-by: Taylor Ho Co-authored-by: Carl Co-authored-by: Carl --- .../src-tauri/src/commands/link_preview.rs | 530 +++++---------- .../src/commands/link_preview_cancellation.rs | 89 +++ .../src/commands/link_preview_rate_limit.rs | 21 +- .../src/commands/link_preview_tests.rs | 604 ++++++++++++++++++ .../src/commands/link_preview_youtube.rs | 16 +- desktop/src-tauri/src/lib.rs | 2 + .../lib/linkPreviewPreparationStore.test.mjs | 97 ++- .../lib/linkPreviewPreparationStore.ts | 58 +- .../lib/useResolvedLinkPreviews.test.mjs | 262 +++++++- .../src/shared/lib/useResolvedLinkPreviews.ts | 278 ++++++-- 10 files changed, 1510 insertions(+), 447 deletions(-) create mode 100644 desktop/src-tauri/src/commands/link_preview_cancellation.rs create mode 100644 desktop/src-tauri/src/commands/link_preview_tests.rs diff --git a/desktop/src-tauri/src/commands/link_preview.rs b/desktop/src-tauri/src/commands/link_preview.rs index b781f8d9e68..a746cc80598 100644 --- a/desktop/src-tauri/src/commands/link_preview.rs +++ b/desktop/src-tauri/src/commands/link_preview.rs @@ -10,6 +10,8 @@ use reqwest::{ use serde::Serialize; use url::Url; +#[path = "link_preview_cancellation.rs"] +mod cancellation; #[path = "link_preview_image_retry.rs"] mod image_retry; #[path = "link_preview_rate_limit.rs"] @@ -17,15 +19,19 @@ mod rate_limit; #[path = "link_preview_youtube.rs"] mod youtube; -use rate_limit::{image_host_cooldown_remaining, retry_after_duration, set_image_host_cooldown}; +use rate_limit::{ + image_host_cooldown_remaining, image_host_gate, retry_after_duration, set_image_host_cooldown, +}; const MAX_PREVIEW_FETCH_BYTES: usize = 256 * 1024; const MAX_IMAGE_FETCH_BYTES: usize = 2 * 1024 * 1024; const MAX_IMAGE_DIMENSION: u32 = 4096; const MAX_IMAGE_PIXELS: u64 = 16_000_000; const MAX_SANITIZED_DIMENSION: u32 = 1200; -const PREVIEW_FETCH_TIMEOUT: Duration = Duration::from_secs(4); -const PREVIEW_TOTAL_TIMEOUT: Duration = Duration::from_secs(10); +const TRANSPORT_CONNECT_TIMEOUT: Duration = Duration::from_secs(15); +const TRANSPORT_IDLE_TIMEOUT: Duration = Duration::from_secs(30); +const DNS_RESOLUTION_TIMEOUT: Duration = Duration::from_secs(15); +const MAX_INLINE_IMAGE_COOLDOWN: Duration = Duration::from_secs(30); const MAX_REDIRECTS: usize = 3; const MAX_METADATA_CHARS: usize = 180; const MAX_METADATA_DESCRIPTION_CHARS: usize = 280; @@ -55,27 +61,46 @@ pub struct LinkPreviewMetadata { #[tauri::command] pub async fn fetch_link_preview_metadata( href: String, + request_id: Option, ) -> Result, String> { - tokio::time::timeout( - PREVIEW_TOTAL_TIMEOUT, - fetch_link_preview_metadata_inner(href), - ) - .await - .map_err(|_| "link preview request timed out".to_string())? + let cancellation = cancellation::begin(request_id.as_deref()); + let result = match cancellation { + Some(cancellation) => { + tokio::select! { + result = fetch_link_preview_metadata_for_url(href) => result, + () = cancellation.cancelled() => Err("link preview request cancelled".to_string()), + } + } + None => fetch_link_preview_metadata_for_url(href).await, + }; + cancellation::finish(request_id.as_deref()); + result } -async fn fetch_link_preview_metadata_inner( +/// Cancel renderer-owned metadata work, including an in-flight response body. +#[tauri::command] +pub fn cancel_link_preview_metadata(request_id: String) { + cancellation::cancel(&request_id); +} + +/// Release a renderer's cancellation record after its invocation settles. +#[tauri::command] +pub fn release_link_preview_metadata(request_id: String) { + cancellation::finish(Some(&request_id)); +} + +async fn fetch_link_preview_metadata_for_url( href: String, ) -> Result, String> { let mut url = Url::parse(href.trim()).map_err(|error| format!("invalid URL: {error}"))?; - validate_public_https_url(&url).await?; + validate_metadata_url(&url).await?; if youtube::is_video_url(&url) { return youtube::fetch_oembed_metadata(&url).await; } for redirect_count in 0..=MAX_REDIRECTS { - let response = send_pinned_request(&url, "text/html,application/xhtml+xml;q=0.9").await?; + let response = send_metadata_request(&url, "text/html,application/xhtml+xml;q=0.9").await?; if response.status().is_redirection() { if redirect_count == MAX_REDIRECTS { @@ -90,7 +115,7 @@ async fn fetch_link_preview_metadata_inner( url = url .join(location) .map_err(|error| format!("invalid link preview redirect: {error}"))?; - validate_public_https_url(&url).await?; + validate_metadata_url(&url).await?; continue; } @@ -107,28 +132,15 @@ async fn fetch_link_preview_metadata_inner( let (image_result, favicon_result) = tokio::join!( async { match image_url { - Some(image_url) => Some( - tokio::time::timeout( - PREVIEW_FETCH_TIMEOUT, - fetch_sanitized_image_with_retry(image_url, false), - ) - .await - .unwrap_or(Err(ImageFetchError::Transient { - retry_after: None, - retry_inline: false, - })), - ), + Some(image_url) => { + Some(fetch_sanitized_image_with_retry(image_url, false).await) + } None => None, } }, async { match favicon_url { - Some(favicon_url) => tokio::time::timeout( - PREVIEW_FETCH_TIMEOUT, - fetch_sanitized_image(favicon_url, true), - ) - .await - .ok(), + Some(favicon_url) => Some(fetch_sanitized_image(favicon_url, true).await), None => None, } } @@ -166,6 +178,15 @@ fn apply_image_result( } } +async fn validate_metadata_url(url: &Url) -> Result<(), String> { + #[cfg(test)] + if METADATA_TEST_SERVER.try_with(|_| ()).is_ok() { + return Ok(()); + } + + validate_public_https_url(url).await +} + async fn validate_public_https_url(url: &Url) -> Result<(), String> { if url.scheme() != "https" || url.username() != "" || url.password().is_some() { return Err("link previews require an HTTPS URL without credentials".to_string()); @@ -182,11 +203,15 @@ async fn validate_public_https_url(url: &Url) -> Result<(), String> { async fn resolve_public_addresses(host: &str) -> Result, String> { let host = host.to_string(); - let addresses = tokio::net::lookup_host((host.as_str(), 443)) - .await - .map_err(|error| format!("link preview DNS resolution failed: {error}"))? - .map(|address| address.ip()) - .collect::>(); + let addresses = tokio::time::timeout( + DNS_RESOLUTION_TIMEOUT, + tokio::net::lookup_host((host.as_str(), 443)), + ) + .await + .map_err(|_| "link preview DNS resolution timed out".to_string())? + .map_err(|error| format!("link preview DNS resolution failed: {error}"))? + .map(|address| address.ip()) + .collect::>(); if addresses.is_empty() { return Err("link preview DNS resolution returned no addresses".to_string()); @@ -198,6 +223,25 @@ async fn resolve_public_addresses(host: &str) -> Result, String> { Ok(addresses) } +#[cfg(test)] +tokio::task_local! { + static METADATA_TEST_SERVER: std::net::SocketAddr; +} + +async fn send_metadata_request(url: &Url, accept: &str) -> Result { + #[cfg(test)] + if let Ok(address) = METADATA_TEST_SERVER.try_with(|address| *address) { + return reqwest::Client::new() + .get(format!("http://{address}{}", url.path())) + .header(ACCEPT, accept) + .send() + .await + .map_err(|error| format!("link preview test request failed: {error}")); + } + + send_pinned_request(url, accept).await +} + async fn send_pinned_request(url: &Url, accept: &str) -> Result { let host = url .host_str() @@ -211,6 +255,8 @@ async fn send_pinned_request(url: &Url, accept: &str) -> Result Result bool { + if *waited_for_cooldown { + return false; + } + *waited_for_cooldown = true; + tokio::time::sleep(retry_after).await; + true +} + +fn retryable_image_cooldown( + url: &Url, + retry_after: Option, + waited_for_cooldown: &mut bool, +) -> Option { + let retry_after = retry_after?; + if *waited_for_cooldown { + return None; + } + set_image_host_cooldown(url, retry_after); + if retry_after > MAX_INLINE_IMAGE_COOLDOWN { + return None; + } + *waited_for_cooldown = true; + Some(retry_after) +} + async fn fetch_sanitized_image( - mut url: Url, + url: Url, preserve_transparency: bool, ) -> Result<(String, String), ImageFetchError> { - validate_public_https_url(&url) + fetch_sanitized_image_using( + url, + preserve_transparency, + |url| async move { validate_public_https_url(&url).await }, + |url, accept| async move { send_pinned_request(&url, accept).await }, + ) + .await +} + +async fn fetch_sanitized_image_using( + mut url: Url, + preserve_transparency: bool, + mut validate_url: V, + mut send_request: F, +) -> Result<(String, String), ImageFetchError> +where + V: FnMut(Url) -> VFut, + VFut: std::future::Future>, + F: FnMut(Url, &'static str) -> Fut, + Fut: std::future::Future>, +{ + validate_url(url.clone()) .await .map_err(|_| ImageFetchError::Rejected)?; - for redirect_count in 0..=MAX_REDIRECTS { + let mut redirect_count = 0; + let mut waited_for_cooldown = false; + while redirect_count <= MAX_REDIRECTS { if let Some(retry_after) = image_host_cooldown_remaining(&url) { - return Err(ImageFetchError::Transient { - retry_after: Some(retry_after), - retry_inline: false, - }); + if retry_after > MAX_INLINE_IMAGE_COOLDOWN + || !wait_for_image_host_cooldown(&mut waited_for_cooldown, retry_after).await + { + return Err(ImageFetchError::Transient { + retry_after: Some(retry_after), + retry_inline: false, + }); + } + continue; } - let response = send_pinned_request(&url, "image/jpeg,image/png,image/webp") + + let host_gate = image_host_gate(&url); + let host_guard = host_gate.lock().await; + if image_host_cooldown_remaining(&url).is_some() { + continue; + } + let response = send_request(url.clone(), "image/jpeg,image/png,image/webp") .await .map_err(|_| ImageFetchError::Transient { retry_after: None, - retry_inline: true, + retry_inline: !waited_for_cooldown, })?; if response.status().is_redirection() { if redirect_count == MAX_REDIRECTS { @@ -370,9 +479,10 @@ async fn fetch_sanitized_image( .and_then(|value| value.to_str().ok()) .ok_or(ImageFetchError::Rejected)?; url = url.join(location).map_err(|_| ImageFetchError::Rejected)?; - validate_public_https_url(&url) + validate_url(url.clone()) .await .map_err(|_| ImageFetchError::Rejected)?; + redirect_count += 1; continue; } if !response.status().is_success() { @@ -383,12 +493,18 @@ async fn fetch_sanitized_image( || status.is_server_error() { let retry_after = retry_after_duration(&response); - if let Some(retry_after) = retry_after { - set_image_host_cooldown(&url, retry_after); + if let Some(retry_after) = + retryable_image_cooldown(&url, retry_after, &mut waited_for_cooldown) + { + drop(host_guard); + tokio::time::sleep(retry_after).await; + continue; } return Err(ImageFetchError::Transient { retry_after, - retry_inline: status != reqwest::StatusCode::TOO_MANY_REQUESTS, + retry_inline: retry_after.is_none() + && status != reqwest::StatusCode::TOO_MANY_REQUESTS + && !waited_for_cooldown, }); } return Err(ImageFetchError::Rejected); @@ -677,315 +793,5 @@ fn decode_html_entities(value: &str) -> String { } #[cfg(test)] -mod tests { - use super::rate_limit::MAX_IMAGE_RETRY_AFTER; - use super::{ - apply_image_result, declares_animation, extract_favicon_url, extract_image_url, - extract_link_preview_metadata, is_html_response, read_bytes_prefix, retry_after_duration, - sanitize_image, ImageFetchError, LinkPreviewImageFetchState, LinkPreviewMetadata, - MAX_METADATA_DESCRIPTION_CHARS, - }; - use axum::{body::Body, http::Response, routing::get, Router}; - use base64::Engine as _; - use bytes::Bytes; - use futures_util::stream; - use image::{DynamicImage, ImageFormat, Rgb, RgbImage, Rgba, RgbaImage}; - use std::{convert::Infallible, io::Cursor}; - use url::Url; - - async fn test_response(router: Router, path: &str) -> reqwest::Response { - let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); - let address = listener.local_addr().unwrap(); - tokio::spawn(async move { - axum::serve(listener, router).await.unwrap(); - }); - reqwest::get(format!("http://{address}{path}")) - .await - .unwrap() - } - - #[test] - fn metadata_prefers_open_graph_and_reads_site_name() { - let html = r#" - - - Fallback"#; - assert_eq!( - extract_link_preview_metadata(html), - Some(LinkPreviewMetadata { - title: "Rich previews & cards".to_string(), - site_name: Some("Buzz".to_string()), - description: Some("Safe & useful previews".to_string()), - image_data_url: None, - image_domain: None, - image_fetch_state: LinkPreviewImageFetchState::None, - image_retry_after_ms: None, - favicon_data_url: None, - }) - ); - } - - #[test] - fn image_results_preserve_absence_and_classify_recovery() { - let mut metadata = extract_link_preview_metadata("Preview result").unwrap(); - apply_image_result(&mut metadata, None); - assert_eq!(metadata.image_fetch_state, LinkPreviewImageFetchState::None); - - apply_image_result( - &mut metadata, - Some(Err(ImageFetchError::Transient { - retry_after: Some(std::time::Duration::from_secs(15)), - retry_inline: false, - })), - ); - assert_eq!( - metadata.image_fetch_state, - LinkPreviewImageFetchState::TransientFailure - ); - assert_eq!(metadata.image_retry_after_ms, Some(15_000)); - - apply_image_result( - &mut metadata, - Some(Ok(( - "data:image/jpeg;base64,abc".to_string(), - "images.example.com".to_string(), - ))), - ); - assert_eq!( - metadata.image_fetch_state, - LinkPreviewImageFetchState::Image - ); - assert_eq!(metadata.image_domain.as_deref(), Some("images.example.com")); - } - - #[test] - fn metadata_falls_back_to_twitter_then_title() { - assert_eq!( - extract_link_preview_metadata("") - .map(|metadata| metadata.title), - Some("Tweet title".to_string()) - ); - assert_eq!( - extract_link_preview_metadata(" Plain title ") - .map(|metadata| metadata.title), - Some("Plain title".to_string()) - ); - } - - #[test] - fn metadata_preserves_description_line_breaks() { - let html = r#" - "#; - assert_eq!( - extract_link_preview_metadata(html).and_then(|metadata| metadata.description), - Some("First paragraph.\n\nAgents:\n- One\n- Two".to_string()) - ); - } - - #[test] - fn metadata_description_supports_standard_x_posts() { - let description = "x".repeat(MAX_METADATA_DESCRIPTION_CHARS + 1); - let html = format!( - r#""# - ); - let extracted = extract_link_preview_metadata(&html) - .and_then(|metadata| metadata.description) - .unwrap(); - assert_eq!(extracted.chars().count(), MAX_METADATA_DESCRIPTION_CHARS); - } - - #[test] - fn favicon_metadata_resolves_relative_icon_links() { - let page = Url::parse("https://example.com/articles/one").unwrap(); - let html = r#" - "#; - assert_eq!( - extract_favicon_url(html, &page).unwrap().as_str(), - "https://example.com/favicon.png" - ); - } - - #[test] - fn favicon_metadata_prefers_a_supported_raster_candidate() { - let page = Url::parse("https://github.com/block/buzz").unwrap(); - let html = r#" - - "#; - assert_eq!( - extract_favicon_url(html, &page).unwrap().as_str(), - "https://assets.example/favicon.png" - ); - } - - #[test] - fn favicon_metadata_uses_touch_icon_before_unsupported_ico() { - let page = Url::parse("https://twitter.com/tellaho").unwrap(); - let html = r#" - "#; - assert_eq!( - extract_favicon_url(html, &page).unwrap().as_str(), - "https://twitter.com/apple-touch-icon.png" - ); - } - - #[test] - fn image_metadata_resolves_relative_urls_and_prefers_open_graph() { - let page = Url::parse("https://example.com/articles/one").unwrap(); - let html = r#" - "#; - assert_eq!( - extract_image_url(html, &page).unwrap().as_str(), - "https://example.com/preview.png" - ); - } - - #[tokio::test] - async fn oversized_html_uses_metadata_within_the_bounded_prefix() { - const LIMIT: usize = 256; - let metadata = r#""#; - let body = format!("{metadata}{}", "x".repeat(LIMIT)); - let response = test_response( - Router::new().route( - "/declared", - get(move || { - let body = body.clone(); - async move { - Response::builder() - .header("content-type", "text/html") - .body(Body::from(body)) - .unwrap() - } - }), - ), - "/declared", - ) - .await; - assert!(response - .content_length() - .is_some_and(|size| size > LIMIT as u64)); - assert!(is_html_response(&response)); - - let prefix = read_bytes_prefix(response, LIMIT).await.unwrap(); - assert_eq!(prefix.len(), LIMIT); - let html = String::from_utf8_lossy(&prefix); - assert_eq!( - extract_link_preview_metadata(&html).map(|metadata| metadata.title), - Some("Prefix title".to_string()) - ); - assert!(extract_image_url(&html, &Url::parse("https://example.com").unwrap()).is_some()); - } - - #[tokio::test] - async fn image_retry_after_uses_bounded_delta_seconds() { - let response = test_response( - Router::new().route( - "/rate-limited", - get(|| async { - Response::builder() - .status(429) - .header("retry-after", "900") - .body(Body::empty()) - .unwrap() - }), - ), - "/rate-limited", - ) - .await; - assert_eq!( - retry_after_duration(&response), - Some(std::time::Duration::from_secs(900)) - ); - - let response = test_response( - Router::new().route( - "/excessive", - get(|| async { - Response::builder() - .status(429) - .header("retry-after", "7200") - .body(Body::empty()) - .unwrap() - }), - ), - "/excessive", - ) - .await; - assert_eq!(retry_after_duration(&response), Some(MAX_IMAGE_RETRY_AFTER)); - } - - #[tokio::test] - async fn oversized_chunked_html_ignores_metadata_beyond_the_bounded_prefix() { - const LIMIT: usize = 256; - let response = test_response( - Router::new().route( - "/chunked", - get(|| async { - let chunks = stream::iter([ - Ok::<_, Infallible>(Bytes::from(vec![b'x'; LIMIT])), - Ok(Bytes::from_static( - br#""#, - )), - ]); - Response::builder() - .header("content-type", "text/html") - .body(Body::from_stream(chunks)) - .unwrap() - }), - ), - "/chunked", - ) - .await; - assert_eq!(response.content_length(), None); - - let prefix = read_bytes_prefix(response, LIMIT).await.unwrap(); - assert_eq!(prefix.len(), LIMIT); - let html = String::from_utf8_lossy(&prefix); - assert_eq!(extract_link_preview_metadata(&html), None); - assert_eq!( - extract_image_url(&html, &Url::parse("https://example.com").unwrap()), - None - ); - } - - #[test] - fn sanitizer_rejects_mime_mismatch_and_outputs_static_jpeg() { - let source = DynamicImage::ImageRgb8(RgbImage::from_pixel(2, 2, Rgb([10, 20, 30]))); - let mut png = Cursor::new(Vec::new()); - source.write_to(&mut png, ImageFormat::Png).unwrap(); - assert!(sanitize_image(png.get_ref(), "image/jpeg", false).is_err()); - let sanitized = sanitize_image(png.get_ref(), "image/png", false).unwrap(); - assert!(sanitized.starts_with("data:image/jpeg;base64,")); - } - - #[test] - fn favicon_sanitizer_preserves_png_transparency() { - let source = DynamicImage::ImageRgba8(RgbaImage::from_pixel(2, 2, Rgba([36, 41, 47, 0]))); - let mut png = Cursor::new(Vec::new()); - source.write_to(&mut png, ImageFormat::Png).unwrap(); - - let sanitized = sanitize_image(png.get_ref(), "image/png", true).unwrap(); - assert!(sanitized.starts_with("data:image/png;base64,")); - let encoded = sanitized.split_once(',').unwrap().1; - let bytes = base64::engine::general_purpose::STANDARD - .decode(encoded) - .unwrap(); - assert!(image::load_from_memory(&bytes).unwrap().color().has_alpha()); - } - - #[test] - fn animation_markers_are_rejected_before_decode() { - let mut apng = b"\x89PNG\r\n\x1a\n".to_vec(); - apng.extend_from_slice(b"junkacTLjunk"); - assert!(declares_animation(&apng, ImageFormat::Png)); - - let mut webp = b"RIFF\x00\x00\x00\x00WEBPVP8X\x0a\x00\x00\x00".to_vec(); - webp.push(0x02); - assert!(declares_animation(&webp, ImageFormat::WebP)); - } - - #[test] - fn metadata_requires_a_non_empty_title() { - assert_eq!(extract_link_preview_metadata(" "), None); - assert_eq!(extract_link_preview_metadata(""), None); - } -} +#[path = "link_preview_tests.rs"] +mod tests; diff --git a/desktop/src-tauri/src/commands/link_preview_cancellation.rs b/desktop/src-tauri/src/commands/link_preview_cancellation.rs new file mode 100644 index 00000000000..38be2b3a386 --- /dev/null +++ b/desktop/src-tauri/src/commands/link_preview_cancellation.rs @@ -0,0 +1,89 @@ +use std::{ + collections::HashMap, + sync::{LazyLock, Mutex}, +}; + +use tokio_util::sync::CancellationToken; + +#[derive(Default)] +struct LinkPreviewCancellations { + tokens: HashMap, +} + +impl LinkPreviewCancellations { + fn begin(&mut self, request_id: &str) -> CancellationToken { + if let Some(cancellation) = self.tokens.get(request_id).cloned() { + return cancellation; + } + let cancellation = CancellationToken::new(); + self.tokens + .insert(request_id.to_string(), cancellation.clone()); + cancellation + } + + fn cancel(&mut self, request_id: &str) { + self.tokens + .entry(request_id.to_string()) + .or_default() + .cancel(); + } + + fn finish(&mut self, request_id: &str) { + self.tokens.remove(request_id); + } +} + +static LINK_PREVIEW_CANCELLATIONS: LazyLock> = + LazyLock::new(|| Mutex::new(LinkPreviewCancellations::default())); + +pub(super) fn begin(request_id: Option<&str>) -> Option { + let request_id = request_id?; + LINK_PREVIEW_CANCELLATIONS + .lock() + .ok() + .map(|mut fetches| fetches.begin(request_id)) +} + +pub(super) fn cancel(request_id: &str) { + if let Ok(mut fetches) = LINK_PREVIEW_CANCELLATIONS.lock() { + fetches.cancel(request_id); + } +} + +pub(super) fn finish(request_id: Option<&str>) { + let Some(request_id) = request_id else { + return; + }; + if let Ok(mut fetches) = LINK_PREVIEW_CANCELLATIONS.lock() { + fetches.finish(request_id); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cancellation_before_begin_is_retained() { + let mut fetches = LinkPreviewCancellations::default(); + fetches.cancel("cancel-before-begin"); + + let cancellation = fetches.begin("cancel-before-begin"); + + assert!(cancellation.is_cancelled()); + fetches.finish("cancel-before-begin"); + assert!(fetches.tokens.is_empty()); + } + + #[test] + fn cancellation_reaches_active_owner() { + let mut fetches = LinkPreviewCancellations::default(); + let cancellation = fetches.begin("active-fetch"); + + fetches.cancel("active-fetch"); + + assert!(cancellation.is_cancelled()); + fetches.finish("active-fetch"); + assert!(fetches.tokens.is_empty()); + } +} diff --git a/desktop/src-tauri/src/commands/link_preview_rate_limit.rs b/desktop/src-tauri/src/commands/link_preview_rate_limit.rs index c3f7ed7188c..46032fc5f01 100644 --- a/desktop/src-tauri/src/commands/link_preview_rate_limit.rs +++ b/desktop/src-tauri/src/commands/link_preview_rate_limit.rs @@ -1,16 +1,31 @@ use std::{ - collections::HashMap, - sync::{Mutex, OnceLock}, - time::{Duration, Instant}, + collections::{hash_map::DefaultHasher, HashMap}, + hash::{Hash, Hasher}, + sync::{LazyLock, Mutex, OnceLock}, + time::Duration, }; use reqwest::header::RETRY_AFTER; +use tokio::{sync::Mutex as AsyncMutex, time::Instant}; use url::Url; pub(super) const MAX_IMAGE_RETRY_AFTER: Duration = Duration::from_secs(60 * 60); const MAX_IMAGE_HOST_COOLDOWNS: usize = 128; +const IMAGE_HOST_GATE_COUNT: usize = 64; static IMAGE_HOST_COOLDOWNS: OnceLock>> = OnceLock::new(); +// A bounded stripe table serializes image requests by host without retaining an +// unbounded attacker-controlled hostname map. Hash collisions only make two +// unrelated hosts wait for one another; they never weaken the host boundary. +static IMAGE_HOST_GATES: LazyLock<[AsyncMutex<()>; IMAGE_HOST_GATE_COUNT]> = + LazyLock::new(|| std::array::from_fn(|_| AsyncMutex::new(()))); + +pub(super) fn image_host_gate(url: &Url) -> &'static AsyncMutex<()> { + let mut hasher = DefaultHasher::new(); + url.host_str().unwrap_or_default().hash(&mut hasher); + let index = (hasher.finish() as usize) % IMAGE_HOST_GATE_COUNT; + &IMAGE_HOST_GATES[index] +} pub(super) fn retry_after_duration(response: &reqwest::Response) -> Option { response diff --git a/desktop/src-tauri/src/commands/link_preview_tests.rs b/desktop/src-tauri/src/commands/link_preview_tests.rs new file mode 100644 index 00000000000..05ab0eec2ce --- /dev/null +++ b/desktop/src-tauri/src/commands/link_preview_tests.rs @@ -0,0 +1,604 @@ +use super::rate_limit::MAX_IMAGE_RETRY_AFTER; +use super::{ + apply_image_result, cancel_link_preview_metadata, declares_animation, extract_favicon_url, + extract_image_url, extract_link_preview_metadata, fetch_link_preview_metadata, + fetch_sanitized_image_using, is_html_response, read_bytes_prefix, retry_after_duration, + retryable_image_cooldown, sanitize_image, ImageFetchError, LinkPreviewImageFetchState, + LinkPreviewMetadata, MAX_INLINE_IMAGE_COOLDOWN, MAX_METADATA_DESCRIPTION_CHARS, +}; +use axum::{body::Body, http::Response, routing::get, Router}; +use base64::Engine as _; +use bytes::Bytes; +use futures_util::stream; +use image::{DynamicImage, ImageFormat, Rgb, RgbImage, Rgba, RgbaImage}; +use std::{ + convert::Infallible, + io::Cursor, + sync::{Arc, Mutex}, +}; +use tokio::sync::oneshot; +use url::Url; + +async fn start_test_server(router: Router) -> std::net::SocketAddr { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let address = listener.local_addr().unwrap(); + tokio::spawn(async move { + axum::serve(listener, router).await.unwrap(); + }); + address +} + +async fn test_response(router: Router, path: &str) -> reqwest::Response { + let address = start_test_server(router).await; + reqwest::get(format!("http://{address}{path}")) + .await + .unwrap() +} + +#[tokio::test(start_paused = true)] +async fn metadata_pipeline_remains_pending_beyond_former_aggregate_deadline() { + let (request_started_tx, request_started_rx) = oneshot::channel::<()>(); + let request_started_tx = Arc::new(Mutex::new(Some(request_started_tx))); + let (release_response_tx, release_response_rx) = oneshot::channel::<()>(); + let release_response_rx = Arc::new(Mutex::new(Some(release_response_rx))); + let address = start_test_server(Router::new().route( + "/preview", + get(move || { + let request_started_tx = Arc::clone(&request_started_tx); + let release_response_rx = Arc::clone(&release_response_rx); + async move { + request_started_tx + .lock() + .unwrap() + .take() + .unwrap() + .send(()) + .unwrap(); + let release_response_rx = release_response_rx.lock().unwrap().take().unwrap(); + release_response_rx.await.unwrap(); + Response::builder() + .header("content-type", "text/html") + .body(Body::from("User-paced metadata")) + .unwrap() + } + }), + )) + .await; + let fetch = tokio::spawn(super::METADATA_TEST_SERVER.scope( + address, + fetch_link_preview_metadata("https://user-paced.example/preview".to_string(), None), + )); + + request_started_rx.await.unwrap(); + tokio::time::advance(std::time::Duration::from_secs(11)).await; + assert!(!fetch.is_finished()); + + release_response_tx.send(()).unwrap(); + let metadata = fetch.await.unwrap().unwrap().unwrap(); + assert_eq!(metadata.title, "User-paced metadata"); +} + +#[tokio::test] +async fn metadata_command_cancellation_drops_an_in_flight_response() { + let (request_started_tx, request_started_rx) = oneshot::channel::<()>(); + let request_started_tx = Arc::new(Mutex::new(Some(request_started_tx))); + let (_release_response_tx, release_response_rx) = oneshot::channel::<()>(); + let release_response_rx = Arc::new(Mutex::new(Some(release_response_rx))); + let address = start_test_server(Router::new().route( + "/preview", + get(move || { + let request_started_tx = Arc::clone(&request_started_tx); + let release_response_rx = Arc::clone(&release_response_rx); + async move { + request_started_tx + .lock() + .unwrap() + .take() + .unwrap() + .send(()) + .unwrap(); + let release_response_rx = release_response_rx.lock().unwrap().take().unwrap(); + let _ = release_response_rx.await; + Response::builder() + .header("content-type", "text/html") + .body(Body::from("Too late")) + .unwrap() + } + }), + )) + .await; + let request_id = "cancel-in-flight".to_string(); + let fetch = tokio::spawn(super::METADATA_TEST_SERVER.scope( + address, + fetch_link_preview_metadata( + "https://cancel.example/preview".to_string(), + Some(request_id.clone()), + ), + )); + + request_started_rx.await.unwrap(); + cancel_link_preview_metadata(request_id); + + assert_eq!( + fetch.await.unwrap(), + Err("link preview request cancelled".to_string()) + ); +} + +#[tokio::test(start_paused = true)] +async fn first_rate_limit_and_queued_host_request_share_one_cooldown_boundary() { + let cooldown = std::time::Duration::from_secs(20); + let rate_limited_path = "/rate-limited.png"; + let success_path = "/success.png"; + let url = Url::parse(&format!( + "https://rate-limit-regression.example{rate_limited_path}" + )) + .unwrap(); + let attempts = Arc::new(Mutex::new(0)); + let collision_attempts = Arc::new(Mutex::new(0)); + let image = DynamicImage::ImageRgb8(RgbImage::from_pixel(2, 2, Rgb([10, 20, 30]))); + let mut png = Cursor::new(Vec::new()); + image.write_to(&mut png, ImageFormat::Png).unwrap(); + let image_bytes = png.into_inner(); + let server_attempts = Arc::clone(&attempts); + let address = start_test_server(Router::new().route( + "/{image}", + get( + move |axum::extract::Path(image): axum::extract::Path| { + let image_bytes = image_bytes.clone(); + let server_attempts = Arc::clone(&server_attempts); + async move { + if image == "rate-limited.png" { + let attempt = { + let mut attempts = server_attempts.lock().unwrap(); + *attempts += 1; + *attempts + }; + if attempt == 1 { + return Response::builder() + .status(429) + .header("retry-after", cooldown.as_secs()) + .body(Body::empty()) + .unwrap(); + } + } + Response::builder() + .header("content-type", "image/png") + .body(Body::from(image_bytes)) + .unwrap() + } + }, + ), + )) + .await; + let test_client = reqwest::Client::new(); + let request = move |url: Url, _accept: &'static str| { + let test_client = test_client.clone(); + async move { + test_client + .get(format!("http://{address}{}", url.path())) + .send() + .await + .map_err(|error| error.to_string()) + } + }; + let collision_request = { + let collision_attempts = Arc::clone(&collision_attempts); + let test_client = reqwest::Client::new(); + move |url: Url, _accept: &'static str| { + let collision_attempts = Arc::clone(&collision_attempts); + let test_client = test_client.clone(); + async move { + *collision_attempts.lock().unwrap() += 1; + test_client + .get(format!("http://{address}{}", url.path())) + .send() + .await + .map_err(|error| error.to_string()) + } + } + }; + let validate = |_url: Url| async { Ok(()) }; + let first = tokio::spawn(fetch_sanitized_image_using( + url.clone(), + false, + validate, + request.clone(), + )); + while super::image_host_cooldown_remaining(&url).is_none() { + tokio::task::yield_now().await; + } + assert!(!first.is_finished()); + assert_eq!(*attempts.lock().unwrap(), 1); + assert_eq!(super::image_host_cooldown_remaining(&url), Some(cooldown)); + + let colliding_url = (0..10_000) + .map(|index| { + Url::parse(&format!("https://collision-{index}.example{success_path}")).unwrap() + }) + .find(|candidate| { + std::ptr::eq( + super::image_host_gate(candidate), + super::image_host_gate(&url), + ) + }) + .expect("a different host sharing the bounded gate stripe"); + + let (collision_started_tx, collision_started_rx) = oneshot::channel(); + tokio::spawn(async move { + let collision = + fetch_sanitized_image_using(colliding_url, false, validate, collision_request); + tokio::pin!(collision); + assert!(futures_util::poll!(&mut collision).is_pending()); + collision_started_tx.send(()).ok(); + assert!(collision.await.is_ok()); + }); + collision_started_rx.await.unwrap(); + assert_eq!(*collision_attempts.lock().unwrap(), 1); + assert_eq!(*attempts.lock().unwrap(), 1); + + let queued = tokio::spawn(fetch_sanitized_image_using(url, false, validate, request)); + tokio::task::yield_now().await; + assert!(!queued.is_finished()); + assert_eq!(*attempts.lock().unwrap(), 1); + + tokio::time::advance(cooldown - std::time::Duration::from_millis(1)).await; + assert!(!first.is_finished()); + assert!(!queued.is_finished()); + assert_eq!(*attempts.lock().unwrap(), 1); + + tokio::time::advance(std::time::Duration::from_millis(1)).await; + let (first, queued) = tokio::join!(first, queued); + assert!(first.unwrap().is_ok()); + assert!(queued.unwrap().is_ok()); + assert_eq!(*attempts.lock().unwrap(), 3); +} + +#[tokio::test(start_paused = true)] +async fn transport_failure_after_cooldown_does_not_renew_wait_on_outer_retry() { + let cooldown = std::time::Duration::from_secs(20); + let url = Url::parse("https://transport-after-cooldown.example/image.png").unwrap(); + super::set_image_host_cooldown(&url, cooldown); + let attempts = Arc::new(Mutex::new(0)); + + let result = super::image_retry::retry_transient_image_fetch(|| { + let url = url.clone(); + let attempts = Arc::clone(&attempts); + async move { + fetch_sanitized_image_using( + url, + false, + |_url| async { Ok(()) }, + move |_url, _accept| { + let attempts = Arc::clone(&attempts); + async move { + *attempts.lock().unwrap() += 1; + Err("connection failed".to_string()) + } + }, + ) + .await + } + }) + .await; + + assert_eq!( + result, + Err(ImageFetchError::Transient { + retry_after: None, + retry_inline: false, + }) + ); + assert_eq!(*attempts.lock().unwrap(), 1); +} + +#[test] +fn image_cooldown_wait_is_short_and_one_shot() { + let url = Url::parse("https://bounded-cooldown.example/image.png").unwrap(); + let mut waited = false; + assert_eq!( + retryable_image_cooldown(&url, Some(MAX_INLINE_IMAGE_COOLDOWN), &mut waited,), + Some(MAX_INLINE_IMAGE_COOLDOWN) + ); + assert!(waited); + assert_eq!( + retryable_image_cooldown(&url, Some(MAX_INLINE_IMAGE_COOLDOWN), &mut waited,), + None + ); + let excessive_url = Url::parse("https://excessive-cooldown.example/image.png").unwrap(); + let mut excessive_waited = false; + assert_eq!( + retryable_image_cooldown( + &excessive_url, + Some(MAX_INLINE_IMAGE_COOLDOWN + std::time::Duration::from_secs(1)), + &mut excessive_waited, + ), + None + ); + assert!(!excessive_waited); +} + +#[test] +fn metadata_prefers_open_graph_and_reads_site_name() { + let html = r#" + + + Fallback"#; + assert_eq!( + extract_link_preview_metadata(html), + Some(LinkPreviewMetadata { + title: "Rich previews & cards".to_string(), + site_name: Some("Buzz".to_string()), + description: Some("Safe & useful previews".to_string()), + image_data_url: None, + image_domain: None, + image_fetch_state: LinkPreviewImageFetchState::None, + image_retry_after_ms: None, + favicon_data_url: None, + }) + ); +} + +#[test] +fn image_results_preserve_absence_and_classify_recovery() { + let mut metadata = extract_link_preview_metadata("Preview result").unwrap(); + apply_image_result(&mut metadata, None); + assert_eq!(metadata.image_fetch_state, LinkPreviewImageFetchState::None); + + apply_image_result( + &mut metadata, + Some(Err(ImageFetchError::Transient { + retry_after: Some(std::time::Duration::from_secs(15)), + retry_inline: false, + })), + ); + assert_eq!( + metadata.image_fetch_state, + LinkPreviewImageFetchState::TransientFailure + ); + assert_eq!(metadata.image_retry_after_ms, Some(15_000)); + + apply_image_result( + &mut metadata, + Some(Ok(( + "data:image/jpeg;base64,abc".to_string(), + "images.example.com".to_string(), + ))), + ); + assert_eq!( + metadata.image_fetch_state, + LinkPreviewImageFetchState::Image + ); + assert_eq!(metadata.image_domain.as_deref(), Some("images.example.com")); +} + +#[test] +fn metadata_falls_back_to_twitter_then_title() { + assert_eq!( + extract_link_preview_metadata("") + .map(|metadata| metadata.title), + Some("Tweet title".to_string()) + ); + assert_eq!( + extract_link_preview_metadata(" Plain title ") + .map(|metadata| metadata.title), + Some("Plain title".to_string()) + ); +} + +#[test] +fn metadata_preserves_description_line_breaks() { + let html = r#" + "#; + assert_eq!( + extract_link_preview_metadata(html).and_then(|metadata| metadata.description), + Some("First paragraph.\n\nAgents:\n- One\n- Two".to_string()) + ); +} + +#[test] +fn metadata_description_supports_standard_x_posts() { + let description = "x".repeat(MAX_METADATA_DESCRIPTION_CHARS + 1); + let html = format!( + r#""# + ); + let extracted = extract_link_preview_metadata(&html) + .and_then(|metadata| metadata.description) + .unwrap(); + assert_eq!(extracted.chars().count(), MAX_METADATA_DESCRIPTION_CHARS); +} + +#[test] +fn favicon_metadata_resolves_relative_icon_links() { + let page = Url::parse("https://example.com/articles/one").unwrap(); + let html = r#" + "#; + assert_eq!( + extract_favicon_url(html, &page).unwrap().as_str(), + "https://example.com/favicon.png" + ); +} + +#[test] +fn favicon_metadata_prefers_a_supported_raster_candidate() { + let page = Url::parse("https://github.com/block/buzz").unwrap(); + let html = r#" + + "#; + assert_eq!( + extract_favicon_url(html, &page).unwrap().as_str(), + "https://assets.example/favicon.png" + ); +} + +#[test] +fn favicon_metadata_uses_touch_icon_before_unsupported_ico() { + let page = Url::parse("https://twitter.com/tellaho").unwrap(); + let html = r#" + "#; + assert_eq!( + extract_favicon_url(html, &page).unwrap().as_str(), + "https://twitter.com/apple-touch-icon.png" + ); +} + +#[test] +fn image_metadata_resolves_relative_urls_and_prefers_open_graph() { + let page = Url::parse("https://example.com/articles/one").unwrap(); + let html = r#" + "#; + assert_eq!( + extract_image_url(html, &page).unwrap().as_str(), + "https://example.com/preview.png" + ); +} + +#[tokio::test] +async fn oversized_html_uses_metadata_within_the_bounded_prefix() { + const LIMIT: usize = 256; + let metadata = r#""#; + let body = format!("{metadata}{}", "x".repeat(LIMIT)); + let response = test_response( + Router::new().route( + "/declared", + get(move || { + let body = body.clone(); + async move { + Response::builder() + .header("content-type", "text/html") + .body(Body::from(body)) + .unwrap() + } + }), + ), + "/declared", + ) + .await; + assert!(response + .content_length() + .is_some_and(|size| size > LIMIT as u64)); + assert!(is_html_response(&response)); + + let prefix = read_bytes_prefix(response, LIMIT).await.unwrap(); + assert_eq!(prefix.len(), LIMIT); + let html = String::from_utf8_lossy(&prefix); + assert_eq!( + extract_link_preview_metadata(&html).map(|metadata| metadata.title), + Some("Prefix title".to_string()) + ); + assert!(extract_image_url(&html, &Url::parse("https://example.com").unwrap()).is_some()); +} + +#[tokio::test] +async fn image_retry_after_uses_bounded_delta_seconds() { + let response = test_response( + Router::new().route( + "/rate-limited", + get(|| async { + Response::builder() + .status(429) + .header("retry-after", "900") + .body(Body::empty()) + .unwrap() + }), + ), + "/rate-limited", + ) + .await; + assert_eq!( + retry_after_duration(&response), + Some(std::time::Duration::from_secs(900)) + ); + + let response = test_response( + Router::new().route( + "/excessive", + get(|| async { + Response::builder() + .status(429) + .header("retry-after", "7200") + .body(Body::empty()) + .unwrap() + }), + ), + "/excessive", + ) + .await; + assert_eq!(retry_after_duration(&response), Some(MAX_IMAGE_RETRY_AFTER)); +} + +#[tokio::test] +async fn oversized_chunked_html_ignores_metadata_beyond_the_bounded_prefix() { + const LIMIT: usize = 256; + let response = test_response( + Router::new().route( + "/chunked", + get(|| async { + let chunks = stream::iter([ + Ok::<_, Infallible>(Bytes::from(vec![b'x'; LIMIT])), + Ok(Bytes::from_static( + br#""#, + )), + ]); + Response::builder() + .header("content-type", "text/html") + .body(Body::from_stream(chunks)) + .unwrap() + }), + ), + "/chunked", + ) + .await; + assert_eq!(response.content_length(), None); + + let prefix = read_bytes_prefix(response, LIMIT).await.unwrap(); + assert_eq!(prefix.len(), LIMIT); + let html = String::from_utf8_lossy(&prefix); + assert_eq!(extract_link_preview_metadata(&html), None); + assert_eq!( + extract_image_url(&html, &Url::parse("https://example.com").unwrap()), + None + ); +} + +#[test] +fn sanitizer_rejects_mime_mismatch_and_outputs_static_jpeg() { + let source = DynamicImage::ImageRgb8(RgbImage::from_pixel(2, 2, Rgb([10, 20, 30]))); + let mut png = Cursor::new(Vec::new()); + source.write_to(&mut png, ImageFormat::Png).unwrap(); + assert!(sanitize_image(png.get_ref(), "image/jpeg", false).is_err()); + let sanitized = sanitize_image(png.get_ref(), "image/png", false).unwrap(); + assert!(sanitized.starts_with("data:image/jpeg;base64,")); +} + +#[test] +fn favicon_sanitizer_preserves_png_transparency() { + let source = DynamicImage::ImageRgba8(RgbaImage::from_pixel(2, 2, Rgba([36, 41, 47, 0]))); + let mut png = Cursor::new(Vec::new()); + source.write_to(&mut png, ImageFormat::Png).unwrap(); + + let sanitized = sanitize_image(png.get_ref(), "image/png", true).unwrap(); + assert!(sanitized.starts_with("data:image/png;base64,")); + let encoded = sanitized.split_once(',').unwrap().1; + let bytes = base64::engine::general_purpose::STANDARD + .decode(encoded) + .unwrap(); + assert!(image::load_from_memory(&bytes).unwrap().color().has_alpha()); +} + +#[test] +fn animation_markers_are_rejected_before_decode() { + let mut apng = b"\x89PNG\r\n\x1a\n".to_vec(); + apng.extend_from_slice(b"junkacTLjunk"); + assert!(declares_animation(&apng, ImageFormat::Png)); + + let mut webp = b"RIFF\x00\x00\x00\x00WEBPVP8X\x0a\x00\x00\x00".to_vec(); + webp.push(0x02); + assert!(declares_animation(&webp, ImageFormat::WebP)); +} + +#[test] +fn metadata_requires_a_non_empty_title() { + assert_eq!(extract_link_preview_metadata(" "), None); + assert_eq!(extract_link_preview_metadata(""), None); +} diff --git a/desktop/src-tauri/src/commands/link_preview_youtube.rs b/desktop/src-tauri/src/commands/link_preview_youtube.rs index a0a5a753dcd..b759046d8f0 100644 --- a/desktop/src-tauri/src/commands/link_preview_youtube.rs +++ b/desktop/src-tauri/src/commands/link_preview_youtube.rs @@ -5,8 +5,8 @@ use url::Url; use super::{ apply_image_result, fetch_sanitized_image, normalize_metadata_description, - normalize_metadata_text, read_limited_bytes, send_pinned_request, ImageFetchError, - LinkPreviewImageFetchState, LinkPreviewMetadata, PREVIEW_FETCH_TIMEOUT, + normalize_metadata_text, read_limited_bytes, send_pinned_request, LinkPreviewImageFetchState, + LinkPreviewMetadata, }; const MAX_OEMBED_FETCH_BYTES: usize = 64 * 1024; @@ -54,17 +54,7 @@ pub(super) async fn fetch_oembed_metadata( return Ok(None); }; let image_result = match thumbnail_url { - Some(thumbnail_url) => Some( - tokio::time::timeout( - PREVIEW_FETCH_TIMEOUT, - fetch_sanitized_image(thumbnail_url, false), - ) - .await - .unwrap_or(Err(ImageFetchError::Transient { - retry_after: None, - retry_inline: false, - })), - ), + Some(thumbnail_url) => Some(fetch_sanitized_image(thumbnail_url, false).await), None => None, }; apply_image_result(&mut metadata, image_result); diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 12082a2a82e..e3ac45953dc 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -600,6 +600,8 @@ pub fn run() { get_relay_http_url, get_media_proxy_port, fetch_link_preview_metadata, + cancel_link_preview_metadata, + release_link_preview_metadata, discover_acp_auth_methods, discover_acp_providers, discover_git_bash_prerequisite, diff --git a/desktop/src/features/messages/lib/linkPreviewPreparationStore.test.mjs b/desktop/src/features/messages/lib/linkPreviewPreparationStore.test.mjs index 293fb63e154..7153ff38518 100644 --- a/desktop/src/features/messages/lib/linkPreviewPreparationStore.test.mjs +++ b/desktop/src/features/messages/lib/linkPreviewPreparationStore.test.mjs @@ -1,5 +1,7 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { after, before, test } from "node:test"; + +import { JSDOM } from "jsdom"; import { __linkPreviewPreparationTest, @@ -12,6 +14,29 @@ import { const first = { href: "https://example.com/first" }; const second = { href: "https://example.com/second" }; const firstTag = ["link-preview", "snapshot", first.href]; +const dom = new JSDOM("", { + url: "http://localhost", +}); +const ipcHandlers = new Map(); + +before(() => { + Object.assign(globalThis, { + document: dom.window.document, + HTMLElement: dom.window.HTMLElement, + window: dom.window, + }); + dom.window.__TAURI_INTERNALS__ = { + invoke: (cmd, args) => { + const handler = ipcHandlers.get(cmd); + return handler + ? handler(args) + : Promise.reject(new Error(`unmocked Tauri command: ${cmd}`)); + }, + transformCallback: () => Math.random(), + }; +}); + +after(() => dom.window.close()); function deferred() { let resolve; @@ -41,6 +66,7 @@ function seed( test.afterEach(() => { __linkPreviewPreparationTest.reset(); + ipcHandlers.clear(); }); test("adopts one in-flight job for the same canonical URL", () => { @@ -223,6 +249,60 @@ test("Skip after completion cannot replace finalized tags", async () => { }); }); +test("expired settled work replaced by a pending retry keeps deadline and Skip", async () => { + const expiredTag = ["link-preview", "snapshot", first.href, "expired"]; + const pendingRetry = deferred(); + let fetchCalls = 0; + ipcHandlers.set("fetch_link_preview_metadata", () => { + fetchCalls += 1; + return pendingRetry.promise; + }); + ipcHandlers.set("cancel_link_preview_metadata", () => Promise.resolve()); + ipcHandlers.set("release_link_preview_metadata", () => Promise.resolve()); + + const seedExpiredFallback = () => + seed( + first, + Promise.resolve(expiredTag), + true, + Date.now() - 5 * 60_000, + expiredTag, + expiredTag, + ); + + seedExpiredFallback(); + const timeoutPreparation = prepareBackgroundLinkPreviews([first], 0); + assert.ok(timeoutPreparation); + assert.equal( + __linkPreviewPreparationTest.jobs.get(first.href)?.settled, + false, + ); + assert.equal( + fetchCalls, + 1, + "the expired job was replaced through production I/O", + ); + assert.deepEqual(await timeoutPreparation.promise, { + status: "ready", + tags: [], + }); + + seedExpiredFallback(); + const skipPreparation = prepareBackgroundLinkPreviews([first], 1_000); + assert.ok(skipPreparation); + assert.equal( + __linkPreviewPreparationTest.jobs.get(first.href)?.settled, + false, + ); + skipPreparation.skip(); + assert.deepEqual(await skipPreparation.promise, { + status: "ready", + tags: [], + }); + + pendingRetry.resolve(null); +}); + test("already-settled partial results contain only successful tags", async () => { seed(first, Promise.resolve(firstTag), true); seed(second, Promise.resolve(null), true); @@ -272,3 +352,18 @@ test("reset cancels pending preparations instead of authorizing send", async () assert.deepEqual(await preparation.promise, { status: "cancelled" }); }); + +test("Skip aborts an abandoned in-flight preview job", async () => { + const pending = deferred(); + seed(first, pending.promise); + const job = __linkPreviewPreparationTest.jobs.get(first.href); + + const preparation = prepareBackgroundLinkPreviews([first], 1_000); + assert.ok(preparation); + preparation.skip(); + + assert.deepEqual(await preparation.promise, { status: "ready", tags: [] }); + assert.equal(job.controller.signal.aborted, true); + assert.equal(__linkPreviewPreparationTest.jobs.has(first.href), false); + pending.resolve(null); +}); diff --git a/desktop/src/features/messages/lib/linkPreviewPreparationStore.ts b/desktop/src/features/messages/lib/linkPreviewPreparationStore.ts index d61750fcd86..a4f1273ecf2 100644 --- a/desktop/src/features/messages/lib/linkPreviewPreparationStore.ts +++ b/desktop/src/features/messages/lib/linkPreviewPreparationStore.ts @@ -9,6 +9,7 @@ import { } from "@/shared/lib/linkPreviewSnapshot"; import { loadLinkPreviewMetadata, + type LinkPreviewMetadata, resolveLinkPreview, } from "@/shared/lib/useResolvedLinkPreviews"; @@ -27,6 +28,7 @@ type PreviewJob = { type BackgroundPreviewTask = { cancel: () => void; + hrefs: Set; id: number; skip: () => void; }; @@ -113,7 +115,26 @@ async function buildSnapshot( signal: AbortSignal, onMetadataReady: (tag: string[]) => void, ): Promise { - const metadata = await loadLinkPreviewMetadata(candidate.href); + const metadataLoad = loadLinkPreviewMetadata(candidate.href); + let settleAbortedMetadata: (() => void) | null = null; + const abortedMetadata = new Promise((resolve) => { + settleAbortedMetadata = () => resolve(null); + }); + const cancelMetadata = () => { + metadataLoad.cancel(); + settleAbortedMetadata?.(); + }; + signal.addEventListener("abort", cancelMetadata, { once: true }); + if (signal.aborted) cancelMetadata(); + let metadata: LinkPreviewMetadata | null; + try { + metadata = await Promise.race([metadataLoad.promise, abortedMetadata]); + } catch { + return null; + } finally { + signal.removeEventListener("abort", cancelMetadata); + metadataLoad.cancel(); + } if (signal.aborted || !metadata) return null; const preview = resolveLinkPreview(candidate, metadata); if (!preview.snapshotReady) return null; @@ -265,12 +286,15 @@ export function prepareBackgroundLinkPreviews( skip, }); + const preparations = external.map((candidate) => + prepareLinkPreview(candidate), + ); const pending = external.some( (candidate) => !jobs.get(candidate.href)?.settled, ); if (!pending) { return preparedSend( - Promise.all(external.map(prepareLinkPreview)).then((tags) => ({ + Promise.all(preparations).then((tags) => ({ status: "ready" as const, tags: tags.filter((tag): tag is string[] => tag !== null), })), @@ -288,27 +312,45 @@ export function prepareBackgroundLinkPreviews( let finish: ((result: BackgroundLinkPreviewResult) => void) | null = null; let terminal = false; let timer: ReturnType | null = null; - const complete = (result: BackgroundLinkPreviewResult) => { + const complete = ( + result: BackgroundLinkPreviewResult, + abortUnobservedJobs = false, + ) => { if (terminal) return; terminal = true; if (timer !== null) clearTimeout(timer); tasks.delete(taskId); + if (abortUnobservedJobs) { + for (const candidate of external) { + const observedByAnotherSend = [...tasks.values()].some((task) => + task.hrefs.has(candidate.href), + ); + if (!observedByAnotherSend) { + invalidateLinkPreviewPreparation(candidate.href); + } + } + } publishSnapshot(); finish?.(result); }; const promise = new Promise((resolve) => { finish = resolve; }); - const cancel = () => complete({ status: "cancelled" }); - const skip = () => complete({ status: "ready", tags: [] }); - tasks.set(taskId, { cancel, id: taskId, skip }); + const cancel = () => complete({ status: "cancelled" }, true); + const skip = () => complete({ status: "ready", tags: [] }, true); + tasks.set(taskId, { + cancel, + hrefs: new Set(external.map((candidate) => candidate.href)), + id: taskId, + skip, + }); publishSnapshot(); timer = setTimeout( - () => complete({ status: "ready", tags: availableTags() }), + () => complete({ status: "ready", tags: availableTags() }, true), timeoutMs, ); - void Promise.all(external.map(prepareLinkPreview)).then((tags) => { + void Promise.all(preparations).then((tags) => { complete({ status: "ready", tags: tags.filter((tag): tag is string[] => tag !== null), diff --git a/desktop/src/shared/lib/useResolvedLinkPreviews.test.mjs b/desktop/src/shared/lib/useResolvedLinkPreviews.test.mjs index e413f9c7c20..26811489037 100644 --- a/desktop/src/shared/lib/useResolvedLinkPreviews.test.mjs +++ b/desktop/src/shared/lib/useResolvedLinkPreviews.test.mjs @@ -6,6 +6,7 @@ import { JSDOM } from "jsdom"; import { __linkPreviewMetadataTest, fetchBuzzEntityMetadata, + loadLinkPreviewMetadata, isBuzzEntityPreview, resetLinkPreviewMetadataCache, resolveLinkPreview, @@ -33,6 +34,16 @@ function metadata(overrides = {}) { }; } +function deferred() { + let reject; + let resolve; + const promise = new Promise((resolvePromise, rejectPromise) => { + reject = rejectPromise; + resolve = resolvePromise; + }); + return { promise, reject, resolve }; +} + test("pending external metadata reserves the image treatment", () => { assert.deepEqual(resolveLinkPreview(preview, undefined), { ...preview, @@ -140,14 +151,14 @@ test("metadata loader retries transient images after the server cooldown", async }); assert.equal( - (await loader.load(preview.href)).metadata?.imageFetchState, + (await loader.load(preview.href).promise).metadata?.imageFetchState, "transient_failure", ); assert.equal(calls, 1); now += 10_000; assert.equal( - (await loader.load(preview.href)).metadata?.imageFetchState, + (await loader.load(preview.href).promise).metadata?.imageFetchState, "image", ); assert.equal(calls, 2); @@ -165,12 +176,15 @@ test("metadata loader retries rejected requests after the negative-cache TTL", a now: () => now, }); - assert.equal((await loader.load(preview.href)).metadata, null); - assert.equal((await loader.load(preview.href)).metadata, null); + assert.equal((await loader.load(preview.href).promise).metadata, null); + assert.equal((await loader.load(preview.href).promise).metadata, null); assert.equal(calls, 1); now += 5 * 60_000; - assert.deepEqual((await loader.load(preview.href)).metadata, metadata()); + assert.deepEqual( + (await loader.load(preview.href).promise).metadata, + metadata(), + ); assert.equal(calls, 2); }); @@ -191,10 +205,10 @@ test("metadata loader coalesces fragment variants and bounds concurrency", async }); await Promise.all([ - loader.load("https://example.com/one#first"), - loader.load("https://example.com/one#second"), - loader.load("https://example.com/two"), - loader.load("https://example.com/three"), + loader.load("https://example.com/one#first").promise, + loader.load("https://example.com/one#second").promise, + loader.load("https://example.com/two").promise, + loader.load("https://example.com/three").promise, ]); assert.equal(calls, 3); @@ -504,12 +518,15 @@ test("invalidateNegative drops a cached null miss so the next load refetches", a now: () => now, }); - assert.equal((await loader.load(preview.href)).metadata, null); + assert.equal((await loader.load(preview.href).promise).metadata, null); assert.equal(calls, 1); loader.invalidateNegative(preview.href); nextResult = metadata(); - assert.deepEqual((await loader.load(preview.href)).metadata, metadata()); + assert.deepEqual( + (await loader.load(preview.href).promise).metadata, + metadata(), + ); assert.equal(calls, 2); }); @@ -538,7 +555,7 @@ test("invalidateNegative drops a cached transient failure so the next load refet }); assert.equal( - (await loader.load(preview.href)).metadata?.imageFetchState, + (await loader.load(preview.href).promise).metadata?.imageFetchState, "transient_failure", ); assert.equal(calls, 1); @@ -547,7 +564,7 @@ test("invalidateNegative drops a cached transient failure so the next load refet // the cooldown — is what forces the refetch. loader.invalidateNegative(preview.href); assert.equal( - (await loader.load(preview.href)).metadata?.imageFetchState, + (await loader.load(preview.href).promise).metadata?.imageFetchState, "image", ); assert.equal(calls, 2); @@ -566,11 +583,17 @@ test("invalidateNegative leaves a healthy cached hit untouched", async () => { now: () => now, }); - assert.deepEqual((await loader.load(preview.href)).metadata, metadata()); + assert.deepEqual( + (await loader.load(preview.href).promise).metadata, + metadata(), + ); assert.equal(calls, 1); loader.invalidateNegative(preview.href); - assert.deepEqual((await loader.load(preview.href)).metadata, metadata()); + assert.deepEqual( + (await loader.load(preview.href).promise).metadata, + metadata(), + ); assert.equal(calls, 1); }); @@ -599,10 +622,74 @@ test("invalidateNegative leaves an in-flight fetch untouched", async () => { assert.equal(calls, 1, "no redundant fetch started by the bust"); releaseFetch(); - assert.deepEqual((await pending).metadata, metadata()); + assert.deepEqual((await pending.promise).metadata, metadata()); assert.equal(calls, 1, "the original in-flight fetch resolved, not a retry"); }); +test("an orphaned metadata load is cancelled after its re-entry grace period", async () => { + let cancelled = false; + const loader = __linkPreviewMetadataTest.createMetadataLoader({ + fetcher: (_href, signal) => + new Promise((_resolve, reject) => { + signal.addEventListener( + "abort", + () => { + cancelled = true; + reject(new Error("cancelled")); + }, + { once: true }, + ); + }), + }); + + const load = loader.load(preview.href); + const rejection = assert.rejects(load.promise, /cancelled/); + load.cancel(); + await new Promise((resolve) => setTimeout(resolve, 1_050)); + assert.equal(cancelled, true); + await rejection; +}); + +test("new metadata demand immediately reclaims slots from orphaned loads", async () => { + const started = []; + const cancelled = []; + const releases = new Map(); + const loader = __linkPreviewMetadataTest.createMetadataLoader({ + concurrency: 2, + fetcher: (href, signal) => + new Promise((resolve, reject) => { + started.push(href); + releases.set(href, () => resolve(metadata())); + signal.addEventListener( + "abort", + () => { + cancelled.push(href); + reject(new Error("cancelled")); + }, + { once: true }, + ); + }), + }); + + const firstHref = "https://example.com/stalled-one"; + const secondHref = "https://example.com/stalled-two"; + const thirdHref = "https://example.com/ordinary"; + const first = loader.load(firstHref); + const second = loader.load(secondHref); + const firstSettled = first.promise.catch(() => undefined); + const secondSettled = second.promise.catch(() => undefined); + first.cancel(); + second.cancel(); + + const third = loader.load(thirdHref); + await new Promise((resolve) => setImmediate(resolve)); + assert.deepEqual(started, [firstHref, secondHref, thirdHref]); + assert.deepEqual(new Set(cancelled), new Set([firstHref, secondHref])); + releases.get(thirdHref)(); + assert.deepEqual((await third.promise).metadata, metadata()); + await Promise.all([firstSettled, secondSettled]); +}); + // ── Hook-level regression: retained resolved-state invalidation on re-entry ─── // // The loader tests above prove `invalidateNegative` drops the SHARED loader @@ -899,3 +986,146 @@ test("hook clears a re-entered negative even when the shared cache holds an in-f ipcHandlers.clear(); } }); + +test("preparation abort releases only its own shared metadata lease", async () => { + const { prepareLinkPreview, resetLinkPreviewPreparations } = await import( + "../../features/messages/lib/linkPreviewPreparationStore.ts" + ); + + resetLinkPreviewMetadataCache(); + resetLinkPreviewPreparations(); + ipcHandlers.clear(); + + const href = "https://example.com/shared-pending"; + let fetchRequest; + const cancelledRequestIds = []; + ipcHandlers.set("fetch_link_preview_metadata", (args) => { + const request = deferred(); + fetchRequest = { ...args, ...request }; + return request.promise; + }); + ipcHandlers.set("cancel_link_preview_metadata", ({ requestId }) => { + cancelledRequestIds.push(requestId); + return Promise.resolve(); + }); + ipcHandlers.set("release_link_preview_metadata", () => Promise.resolve()); + + try { + const composerLoad = loadLinkPreviewMetadata(href); + const preparation = prepareLinkPreview({ ...hookPreview, href }); + await new Promise((resolve) => setImmediate(resolve)); + assert.ok(fetchRequest, "shared metadata reached the native fetch seam"); + + resetLinkPreviewPreparations(); + assert.equal(await preparation, null); + await new Promise((resolve) => setTimeout(resolve, 1_050)); + assert.deepEqual( + cancelledRequestIds, + [], + "preparation listener plus finally must not release the composer lease", + ); + + fetchRequest.resolve(metadata()); + assert.deepEqual(await composerLoad.promise, metadata()); + + const finalHref = `${href}/final`; + const finalLoad = loadLinkPreviewMetadata(finalHref); + await new Promise((resolve) => setImmediate(resolve)); + const finalRequest = fetchRequest; + const finalRejection = assert.rejects(finalLoad.promise); + finalLoad.cancel(); + finalLoad.cancel(); + await new Promise((resolve) => setTimeout(resolve, 1_050)); + assert.deepEqual( + cancelledRequestIds, + [finalRequest.requestId], + "one final consumer release permits native abort", + ); + finalRequest.reject(new Error("cancelled by test native seam")); + await finalRejection; + } finally { + resetLinkPreviewPreparations(); + resetLinkPreviewMetadataCache(); + ipcHandlers.clear(); + } +}); + +test("removing two stalled previews cancels native work and admits a third URL", async () => { + const { act, cleanup, renderHook } = await import("@testing-library/react"); + const { useResolvedLinkPreviews } = await import( + "./useResolvedLinkPreviews.ts" + ); + + resetLinkPreviewMetadataCache(); + ipcHandlers.clear(); + + const firstPreview = { + ...hookPreview, + href: "https://example.com/stalled-one", + }; + const secondPreview = { + ...hookPreview, + href: "https://example.com/stalled-two", + }; + const thirdPreview = { ...hookPreview, href: "https://example.com/ordinary" }; + const pending = new Map(); + const started = []; + const cancelled = []; + + ipcHandlers.set("fetch_link_preview_metadata", ({ href, requestId }) => { + started.push(href); + return new Promise((resolve, reject) => { + pending.set(requestId, { href, reject, resolve }); + }); + }); + ipcHandlers.set("cancel_link_preview_metadata", ({ requestId }) => { + const request = pending.get(requestId); + if (!request) return Promise.resolve(); + cancelled.push(request.href); + pending.delete(requestId); + request.reject(new Error("cancelled by test native seam")); + return Promise.resolve(); + }); + ipcHandlers.set("release_link_preview_metadata", () => Promise.resolve()); + + const flushScheduledLoads = async () => { + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 0)); + }); + }; + + const hook = renderHook(({ previews }) => useResolvedLinkPreviews(previews), { + initialProps: { previews: [firstPreview, secondPreview] }, + }); + + try { + await flushScheduledLoads(); + assert.deepEqual(started, [firstPreview.href, secondPreview.href]); + + hook.rerender({ previews: [thirdPreview] }); + await flushScheduledLoads(); + assert.deepEqual( + new Set(cancelled), + new Set([firstPreview.href, secondPreview.href]), + ); + assert.deepEqual(started, [ + firstPreview.href, + secondPreview.href, + thirdPreview.href, + ]); + + const third = [...pending.values()].find( + (request) => request.href === thirdPreview.href, + ); + assert.ok(third, "third URL reached the native fetch seam"); + await act(async () => { + third.resolve(metadata()); + await new Promise((resolve) => setTimeout(resolve, 0)); + }); + assert.equal(hook.result.current[0].title, "A story"); + } finally { + hook.unmount(); + cleanup(); + ipcHandlers.clear(); + } +}); diff --git a/desktop/src/shared/lib/useResolvedLinkPreviews.ts b/desktop/src/shared/lib/useResolvedLinkPreviews.ts index 4591d54e7dd..5111929b3e3 100644 --- a/desktop/src/shared/lib/useResolvedLinkPreviews.ts +++ b/desktop/src/shared/lib/useResolvedLinkPreviews.ts @@ -51,6 +51,10 @@ type MetadataLoadResult = MetadataCacheEntry & { const DEFAULT_TRANSIENT_RETRY_MS = 30_000; const NULL_METADATA_RETRY_MS = 5 * 60_000; const MAX_CONCURRENT_METADATA_FETCHES = 2; +// Keep a just-removed request cacheable through a brief edit/re-entry gap, but +// never let unobserved native work linger indefinitely. New queued demand +// reclaims these loads immediately rather than waiting for this grace period. +const ORPHANED_METADATA_LOAD_GRACE_MS = 1_000; /** * React may flush an interaction-triggered effect before the browser paints. @@ -107,29 +111,70 @@ function metadataExpiry( return now + retryAfterMs; } +type PendingMetadataLoad = { + controller: AbortController; + consumers: number; + orphanTimer: ReturnType | null; + promise: Promise; +}; + +type MetadataCacheValue = MetadataCacheEntry | PendingMetadataLoad; + +function isPendingMetadataLoad( + value: MetadataCacheValue, +): value is PendingMetadataLoad { + return "promise" in value; +} + +function abortError(): DOMException { + return new DOMException("Link preview fetch cancelled", "AbortError"); +} + function createTaskScheduler(concurrency: number) { - const pending: Array<() => void> = []; + const pending: Array<{ + reject: (reason?: unknown) => void; + run: () => void; + signal: AbortSignal; + }> = []; let active = 0; const drain = () => { while (active < concurrency) { - const run = pending.shift(); - if (!run) return; + const next = pending.shift(); + if (!next) return; + if (next.signal.aborted) { + next.reject(abortError()); + continue; + } active += 1; - run(); + next.run(); } }; - return (task: () => Promise): Promise => + return (task: () => Promise, signal: AbortSignal): Promise => new Promise((resolve, reject) => { - pending.push(() => { - void task() - .then(resolve, reject) - .finally(() => { - active -= 1; - drain(); - }); - }); + const entry = { + reject, + signal, + run: () => { + signal.removeEventListener("abort", cancelPending); + void task() + .then(resolve, reject) + .finally(() => { + active -= 1; + drain(); + }); + }, + }; + const cancelPending = () => { + const index = pending.indexOf(entry); + if (index < 0) return; + pending.splice(index, 1); + reject(abortError()); + drain(); + }; + signal.addEventListener("abort", cancelPending, { once: true }); + pending.push(entry); drain(); }); } @@ -140,20 +185,20 @@ function createMetadataLoader({ now = Date.now, }: { concurrency?: number; - fetcher: (href: string) => Promise; + fetcher: ( + href: string, + signal: AbortSignal, + ) => Promise; now?: () => number; }) { - const cache = new Map< - string, - MetadataCacheEntry | Promise - >(); + const cache = new Map(); const schedule = createTaskScheduler(Math.max(1, concurrency)); let generation = 0; const peek = (href: string): MetadataLoadResult | undefined => { const key = metadataCacheKey(href); const cached = cache.get(key); - if (!cached || cached instanceof Promise) return undefined; + if (!cached || isPendingMetadataLoad(cached)) return undefined; if (cached.expiresAt !== null && cached.expiresAt <= now()) { cache.delete(key); return undefined; @@ -161,32 +206,116 @@ function createMetadataLoader({ return { key, ...cached }; }; - const load = (href: string): Promise => { + const abortPending = (key: string, pending: PendingMetadataLoad) => { + if (pending.orphanTimer !== null) clearTimeout(pending.orphanTimer); + pending.orphanTimer = null; + if (cache.get(key) === pending) cache.delete(key); + pending.controller.abort(); + }; + + const release = (key: string, pending: PendingMetadataLoad) => { + if (pending.consumers <= 0) return; + pending.consumers -= 1; + if ( + pending.consumers > 0 || + pending.orphanTimer !== null || + cache.get(key) !== pending + ) { + return; + } + pending.orphanTimer = setTimeout( + () => abortPending(key, pending), + ORPHANED_METADATA_LOAD_GRACE_MS, + ); + }; + + const createRelease = (key: string, pending: PendingMetadataLoad) => { + let released = false; + return () => { + if (released) return; + released = true; + release(key, pending); + }; + }; + + const abortOrphanedLoads = (exceptKey: string) => { + for (const [key, cached] of cache) { + if ( + key !== exceptKey && + isPendingMetadataLoad(cached) && + cached.consumers === 0 + ) { + abortPending(key, cached); + } + } + }; + + const load = ( + href: string, + ): { cancel: () => void; promise: Promise } => { const key = metadataCacheKey(href); const cached = cache.get(key); - if (cached instanceof Promise) return cached; + if (cached && isPendingMetadataLoad(cached)) { + if (cached.orphanTimer !== null) clearTimeout(cached.orphanTimer); + cached.orphanTimer = null; + cached.consumers += 1; + return { + cancel: createRelease(key, cached), + promise: cached.promise, + }; + } if (cached) { if (cached.expiresAt === null || cached.expiresAt > now()) { - return Promise.resolve({ key, ...cached }); + return { + cancel: () => undefined, + promise: Promise.resolve({ key, ...cached }), + }; } cache.delete(key); } + abortOrphanedLoads(key); const requestGeneration = generation; - const promise = schedule(() => fetcher(href)) - .catch(() => null) + const controller = new AbortController(); + const pending: PendingMetadataLoad = { + controller, + consumers: 1, + orphanTimer: null, + promise: Promise.resolve({ + expiresAt: null, + key, + metadata: null, + }), + }; + pending.promise = schedule( + () => fetcher(href, controller.signal), + controller.signal, + ) + .catch((error) => { + if (controller.signal.aborted) throw error; + return null; + }) .then((metadata) => { + if (pending.orphanTimer !== null) clearTimeout(pending.orphanTimer); + pending.orphanTimer = null; const entry = { expiresAt: metadataExpiry(metadata, now()), metadata, }; - if (requestGeneration === generation) { + if ( + requestGeneration === generation && + cache.get(key) === pending && + !controller.signal.aborted + ) { cache.set(key, entry); } return { key, ...entry }; }); - cache.set(key, promise); - return promise; + cache.set(key, pending); + return { + cancel: createRelease(key, pending), + promise: pending.promise, + }; }; return { @@ -205,7 +334,7 @@ function createMetadataLoader({ invalidateNegative(href: string): boolean { const key = metadataCacheKey(href); const cached = cache.get(key); - if (!cached || cached instanceof Promise) return false; + if (!cached || isPendingMetadataLoad(cached)) return false; if (!isNegativeMetadata(cached.metadata)) return false; cache.delete(key); return true; @@ -214,6 +343,9 @@ function createMetadataLoader({ peek, reset() { generation += 1; + for (const [key, cached] of cache) { + if (isPendingMetadataLoad(cached)) abortPending(key, cached); + } cache.clear(); }, }; @@ -221,13 +353,57 @@ function createMetadataLoader({ function fetchLinkPreviewMetadata( href: string, + signal: AbortSignal, ): Promise { - return invokeTauri( + const requestId = crypto.randomUUID(); + const startedAt = performance.now(); + const logResult = ( + result: LinkPreviewMetadata | null, + ): LinkPreviewMetadata | null => { + if (import.meta.env?.DEV) { + console.info("[link-preview] metadata fetch completed", { + href, + elapsedMs: Math.round(performance.now() - startedAt), + result: result === null ? "miss" : "hit", + imageFetchState: result?.imageFetchState ?? "none", + imageRetryAfterMs: result?.imageRetryAfterMs ?? null, + hasImage: Boolean(result?.imageDataUrl && result.imageDomain), + hasFavicon: Boolean(result?.faviconDataUrl), + }); + } + return result; + }; + const logFailure = (error: unknown): never => { + if (import.meta.env?.DEV) { + console.warn("[link-preview] metadata fetch failed", { + href, + elapsedMs: Math.round(performance.now() - startedAt), + error, + }); + } + throw error; + }; + + const request = invokeTauri( "fetch_link_preview_metadata", { href, + requestId, }, ); + const onAbort = () => { + void invokeTauri("cancel_link_preview_metadata", { requestId }).catch( + () => undefined, + ); + }; + signal.addEventListener("abort", onAbort, { once: true }); + if (signal.aborted) onAbort(); + return request.then(logResult, logFailure).finally(() => { + signal.removeEventListener("abort", onAbort); + void invokeTauri("release_link_preview_metadata", { requestId }).catch( + () => undefined, + ); + }); } const metadataLoader = createMetadataLoader({ @@ -235,10 +411,15 @@ const metadataLoader = createMetadataLoader({ }); /** Share the same deduplicated metadata job between composer rendering and send preparation. */ -export async function loadLinkPreviewMetadata( - href: string, -): Promise { - return (await metadataLoader.load(href)).metadata; +export function loadLinkPreviewMetadata(href: string): { + cancel: () => void; + promise: Promise; +} { + const load = metadataLoader.load(href); + return { + cancel: load.cancel, + promise: load.promise.then((result) => result.metadata), + }; } type EntityEventFetcher = ( filter: Parameters[0], @@ -348,14 +529,19 @@ export async function fetchBuzzEntityMetadata( } const entityMetadataLoader = createMetadataLoader({ - fetcher: fetchBuzzEntityMetadata, + fetcher: (href) => fetchBuzzEntityMetadata(href), }); /** Share deduplicated relay-native entity metadata across cards and inline tooltips. */ export async function loadBuzzEntityMetadata( href: string, ): Promise { - return (await entityMetadataLoader.load(href)).metadata; + const load = entityMetadataLoader.load(href); + try { + return (await load.promise).metadata; + } finally { + load.cancel(); + } } /** Clear ephemeral metadata when the active relay/community changes. */ @@ -622,15 +808,19 @@ export function useResolvedLinkPreviews( cancelScheduledLoads.push( scheduleAfterPaint(() => { - void loader.load(preview.href).then((result) => { - if (cancelled) return; - setResolvedMetadata((current) => - current[result.key] === result.metadata - ? current - : { ...current, [result.key]: result.metadata }, - ); - scheduleRetry(result, loader); - }); + const load = loader.load(preview.href); + cancelScheduledLoads.push(load.cancel); + void load.promise + .then((result) => { + if (cancelled) return; + setResolvedMetadata((current) => + current[result.key] === result.metadata + ? current + : { ...current, [result.key]: result.metadata }, + ); + scheduleRetry(result, loader); + }) + .catch(() => undefined); }), ); } From c045321a7fb3ca8939f28519ce7a555a6f597728 Mon Sep 17 00:00:00 2001 From: Logan Johnson Date: Tue, 8 Sep 2026 17:51:47 -0400 Subject: [PATCH 06/26] fix(acp): pace targeted overflow recovery on consumer capacity (#7325) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 ## Summary When a Buzz agent falls behind on incoming messages, its connection can make the backlog worse while trying to recover. The connection buffers messages from the relay server until the agent is ready to process them; if that buffer overflows, recovery previously requested history for **every subscribed channel** and paused socket reads while sending those requests. That adds traffic to an already overloaded connection. This change requests history only for affected subscriptions, once the code consuming those messages has room, with at least five seconds between attempts. The recovery path now: - Combines repeated losses into one pending recovery per affected subscription, keeping the oldest dropped timestamp so replay starts early enough. - Waits until at least half the consumer queue is free and the relay's existing rate-limit delay has expired. The queue wakes recovery when space becomes available; recovery does not periodically sample capacity or hold queue space away from live messages. - Attempts one subscription at a time, choosing the least recently attempted so a busy channel cannot crowd out other channels or membership notifications. The five-second delay starts when an attempt finishes, including a failed write; failed writes leave recovery pending. Recovery is paced by available capacity, not by how often messages are lost. This is not a larger buffer or a cutoff that abandons recovery. Subscription identifiers, message filters, replay timestamp overlap and duplicate filtering are unchanged; no downstream agent changes are required. This targets a reproducible overload **amplifier**, not every cause of overload or every catch-up limitation. The initial live overload's cause has not been established. Recovery remains best effort: a successful request write is not proof of delivery, and existing history/retention limits, bounded duplicate tracking and replay limitations still apply. There is no exactly-once or complete catch-up guarantee. A stalled write can still pause socket reads for the existing ten-second timeout; the pacing bound does not cover initial subscriptions, reconnects or other retry paths. ### Related issue Closest related: #5014 (channel re-subscription); also #6661 (membership reconciliation) and #6090 (relay backpressure gap signaling). This addresses local overflow recovery scheduling, not those separate mechanisms. ### Testing Recorded offline comparisons against the previous behavior, with the final implementation at `8000636f3073167c5a5107bb179c7d91160f1729`: | Same fixture: 18 subscriptions, three overload rounds | Before | After | | --- | --- | --- | | Recovery history requests | 108 | 3 | | Ping-response delay | About 4.6 seconds | Below the measurement's 1 ms resolution | A separate bounded-history fixture delivered all 320 events plus subsequent live traffic in **both** versions. Regression coverage exercises the real socket-handling task, including intermittent consumer capacity, fairness, failed writes and cancellation of capacity waits before live delivery. These are synthetic results, not production throughput measurements or evidence of a deployed cure. The full local `RUST_TEST_THREADS=4 just ci` run passed on September 4, 2026. Earlier unsuccessful local runs remain part of the validation history. The [recorded validation evidence and separate desktop follow-up](https://github.com/block/buzz/pull/7325#issuecomment-5540592398) preserve the original desktop mock-history scroll failure, its passing rerun and the remaining investigation. That desktop path does not run the agent connection code; neither this repair nor the passing rerun fixes the observed scroll problem. --------- Signed-off-by: Logan Johnson --- crates/buzz-acp/README.md | 4 + crates/buzz-acp/src/relay.rs | 112 +--- crates/buzz-acp/src/relay/recovery.rs | 132 +++++ crates/buzz-acp/src/relay/recovery_tests.rs | 388 ++++++++++++++ .../buzz-acp/src/relay/recovery_wake_tests.rs | 507 ++++++++++++++++++ 5 files changed, 1052 insertions(+), 91 deletions(-) create mode 100644 crates/buzz-acp/src/relay/recovery.rs create mode 100644 crates/buzz-acp/src/relay/recovery_tests.rs create mode 100644 crates/buzz-acp/src/relay/recovery_wake_tests.rs diff --git a/crates/buzz-acp/README.md b/crates/buzz-acp/README.md index 41d9a214bdd..3d011eb8ebb 100644 --- a/crates/buzz-acp/README.md +++ b/crates/buzz-acp/README.md @@ -270,6 +270,10 @@ Forum event kinds: 4. **Prompting** — When events are pending and no prompt is in flight for that channel, drains all queued events for the oldest channel into a single batched prompt via ACP `session/prompt`. 5. **Agent response** — The agent processes the prompt and uses the Buzz CLI (`send_message`, `get_messages`, etc.) to interact with Buzz. 6. **Recovery** — If the agent crashes, the harness respawns it. If the relay disconnects, the harness reconnects with a `since` filter to avoid missing events. + If the inbound queue overflows, the harness attempts replay for affected + subscriptions when capacity and relay quota permit, with at least five seconds + between attempts. Recovery depends on available relay history and the consumer + making progress; complete delivery is not guaranteed. Each channel has at most one prompt in flight. Multiple channels can be processed concurrently when agents > 1. diff --git a/crates/buzz-acp/src/relay.rs b/crates/buzz-acp/src/relay.rs index 23ed454fa2e..a019e758341 100644 --- a/crates/buzz-acp/src/relay.rs +++ b/crates/buzz-acp/src/relay.rs @@ -1160,10 +1160,8 @@ struct BgState { /// On reconnect resubscribe, `since` = min(last_seen, channel_dropped_since). /// Cleared per-channel after a successful resubscribe. channel_dropped_since: HashMap, - /// Set by the backpressure handler when the event channel is full. - /// The main loop checks this flag and triggers a proactive resubscribe - /// (without waiting for a disconnect) so dropped events are replayed. - proactive_resubscribe_needed: bool, + /// Rate/fairness bookkeeping only; replay cursors retain baseline semantics. + recovery: recovery::RecoverySchedule, /// Unix timestamp captured just before the relay connection was established. /// Used as the floor `since` for membership notification replay so events /// predating this session are never re-delivered. @@ -1237,7 +1235,7 @@ impl BgState { membership_sub_active: false, observer_control_sub_active: false, channel_dropped_since: HashMap::new(), - proactive_resubscribe_needed: false, + recovery: recovery::RecoverySchedule::default(), startup_watermark: None, subscribe_since: HashMap::new(), rate_limit_gate: None, @@ -1298,6 +1296,9 @@ impl BgState { /// Prevents stale replay on re-subscribe and avoids unbounded state growth /// for channels that are removed and never re-added. fn clear_channel_state(&mut self, channel_id: &Uuid) { + self.recovery + .last_attempt + .remove(&channel_sub_id(*channel_id)); self.last_seen.remove(channel_id); self.subscribe_since.remove(channel_id); self.channel_dropped_since.remove(channel_id); @@ -1826,82 +1827,6 @@ async fn run_background_task( let mut drain_pacing_next: Option = None; loop { - if state.proactive_resubscribe_needed { - state.proactive_resubscribe_needed = false; - info!("proactive resubscribe triggered by backpressure event loss"); - // Proactive resubscribe runs on the EXISTING socket — do NOT clear the - // rate-limit gate or pending queues. - match resubscribe_after_reconnect( - &mut ws, - &mut cmd_rx, - &mut state, - &agent_pubkey_hex, - false, // existing socket — preserve gate state - ) - .await - { - ResubscribeResult::Ok => {} - ResubscribeResult::Shutdown => return, - ResubscribeResult::RetryConnection => { - warn!("proactive resubscribe had failures — triggering reconnect"); - let _ = event_tx.try_send(None); - match try_autonomous_reconnect( - &mut ws, - &mut cmd_rx, - &mut state, - &keys, - &relay_url, - &agent_pubkey_hex, - &event_tx, - &observer_control_tx, - auth_tag.as_ref(), - ) - .await - { - ReconnectOutcome::Ok => { - if matches!( - drain_post_reconnect( - &mut ws, - &mut cmd_rx, - &mut state, - &agent_pubkey_hex - ) - .await, - ReconnectOutcome::Shutdown - ) { - return; - } - } - ReconnectOutcome::Shutdown => return, - ReconnectOutcome::Failed => { - if matches!( - wait_for_reconnect( - &mut ws, - &mut cmd_rx, - &mut state, - &keys, - &relay_url, - &agent_pubkey_hex, - &event_tx, - &observer_control_tx, - true, - auth_tag.as_ref(), - ) - .await, - ReconnectOutcome::Shutdown - ) { - return; - } - } - } - ping_sent = false; - last_pong = Instant::now(); - connected_since = Instant::now(); - stable_logged = false; - } - } - } - // Drain pending subs, one REQ per pacing tick within the relay's // admission window. let drain_window_open = drain_pacing_next.is_none_or(|t| tokio::time::Instant::now() >= t); @@ -1982,7 +1907,11 @@ async fn run_background_task( } } + let recovery_at = recovery::ready_at(&mut state); tokio::select! { + _ = recovery::ready(&event_tx, recovery_at) => { + recovery::recover_one(&mut ws, &mut state, &event_tx, &agent_pubkey_hex).await; + } raw = ws.next() => { // Determine if the socket is lost. let socket_lost = match raw { @@ -2358,12 +2287,10 @@ async fn handle_ws_message( // replay starts early enough to re-deliver it. state.membership_dropped_since = Some(state.membership_dropped_since.map_or(ts, |d| d.min(ts))); - // Proactively trigger resubscribe without waiting for a disconnect. - state.proactive_resubscribe_needed = true; warn!( channel_id = %channel_uuid, ts, - "membership notification dropped (backpressure) — proactive resubscribe queued" + "membership notification dropped (backpressure) — targeted recovery pending" ); } Err(mpsc::error::TrySendError::Closed(_)) => return false, @@ -2400,12 +2327,10 @@ async fn handle_ws_message( .entry(channel_id) .and_modify(|d| *d = (*d).min(ts)) .or_insert(ts); - // Proactively trigger resubscribe without waiting for a disconnect. - state.proactive_resubscribe_needed = true; warn!( channel_id = %channel_id, ts, - "event channel full — dropping event for channel {channel_id} — proactive resubscribe queued" + "event channel full — dropping event for channel {channel_id} — targeted recovery pending" ); } Err(mpsc::error::TrySendError::Closed(_)) => { @@ -4248,6 +4173,11 @@ async fn wait_for_any_ok( } } +mod recovery; + +#[cfg(test)] +mod recovery_tests; + #[cfg(test)] mod tests { use super::*; @@ -4775,7 +4705,7 @@ mod tests { .expect("signing should succeed") } - async fn test_ws_pair() -> (WsStream, WebSocketStream) { + pub(super) async fn test_ws_pair() -> (WsStream, WebSocketStream) { let listener = tokio::net::TcpListener::bind("127.0.0.1:0") .await .expect("bind test websocket"); @@ -4792,7 +4722,7 @@ mod tests { (client, server.await.expect("join test websocket server")) } - async fn next_test_frame( + pub(super) async fn next_test_frame( server: &mut WebSocketStream, ) -> serde_json::Value { let message = timeout(Duration::from_secs(1), server.next()) @@ -5035,14 +4965,14 @@ mod tests { )); } - fn test_channel_filter() -> ChannelFilter { + pub(super) fn test_channel_filter() -> ChannelFilter { ChannelFilter { kinds: Some(vec![9]), require_mention: false, } } - fn seed_test_subscription(state: &mut BgState, channel_id: Uuid) { + pub(super) fn seed_test_subscription(state: &mut BgState, channel_id: Uuid) { apply_command_to_state( state, RelayCommand::Subscribe { diff --git a/crates/buzz-acp/src/relay/recovery.rs b/crates/buzz-acp/src/relay/recovery.rs new file mode 100644 index 00000000000..62da119a4be --- /dev/null +++ b/crates/buzz-acp/src/relay/recovery.rs @@ -0,0 +1,132 @@ +//! Overflow recovery is an attempted replay, not an EOSE/consumer receipt. +//! Keep the existing IDs and cursor retirement rules; bound when work is sent. +use super::*; + +pub(super) const RECOVERY_INTERVAL: Duration = Duration::from_secs(5); + +#[derive(Default)] +pub(super) struct RecoverySchedule { + next_attempt: Option, + pub(super) last_attempt: HashMap, +} + +/// Attempt at most one affected subscription, with space for replay to arrive. +/// Failed writes retain the loss cursor and are paced too. No EOSE is interpreted +/// as completion: overlapping requests keep their existing stable wire IDs. +pub(super) async fn recover_one( + ws: &mut WsStream, + state: &mut BgState, + event_tx: &mpsc::Sender>, + agent_pubkey_hex: &str, +) { + let now = tokio::time::Instant::now(); + if event_tx.is_closed() + || event_tx.capacity() < event_tx.max_capacity().div_ceil(2) + || state.recovery.next_attempt.is_some_and(|next| now < next) + || state.check_rate_gate().is_some() + { + return; + } + + let channel = next_channel(state); + let Some(channel) = channel else { return }; + let sub = channel.map_or_else(|| MEMBERSHIP_NOTIF_SUB_ID.to_owned(), channel_sub_id); + state.recovery.last_attempt.insert(sub.clone(), now); + info!(subscription = sub, "attempting targeted overflow replay"); + + if let Some(ch) = channel { + if let Some(filter) = state.active_filters.get(&ch).cloned() { + let since = state.channel_since(&ch); + if send_subscribe(ws, state, ch, agent_pubkey_hex, since, &filter).await { + // Baseline retirement point: REQ write, NOT proven delivery. + // New overflow after this attempt creates another pending cursor. + state.channel_dropped_since.remove(&ch); + } + } + } else { + let since = match (state.membership_dropped_since, state.membership_last_seen) { + (Some(d), Some(l)) => Some(d.min(l)), + (Some(d), None) => Some(d), + (None, Some(l)) => Some(l), + (None, None) => state.startup_watermark, + }; + if send_membership_subscribe(ws, agent_pubkey_hex, since).await { + state.membership_dropped_since = None; + } + } + // Pace from the end of a potentially backpressured write. No catch-up burst. + // The existing bounded write timeout and read/ping owner detect socket loss. + state.recovery.next_attempt = Some(tokio::time::Instant::now() + RECOVERY_INTERVAL); +} + +/// No timer or capacity waiter when another authority owns all pending loss. +/// Only actual attempts advance the cooldown; closed gates wake at expiry. +pub(super) fn ready_at(state: &mut BgState) -> Option { + next_channel(state)?; + Some( + state + .recovery + .next_attempt + .unwrap_or_else(tokio::time::Instant::now) + .max( + state + .check_rate_gate() + .unwrap_or_else(tokio::time::Instant::now), + ), + ) +} + +/// Select-local readiness, not a send or a reservation carried across reads. +/// The socket task is the sole producer. `select!` drops this future (including +/// partial permits) BEFORE handling another frame/command, so live try_send +/// never competes with a recovery reservation. Receives only add capacity. +/// Keep this future inside select!: awaiting it alone would block the reader; +/// persisting it across iterations would steal capacity from live delivery. +pub(super) async fn ready( + event_tx: &mpsc::Sender>, + at: Option, +) { + if let Some(at) = at { + if tokio::time::Instant::now() < at { + tokio::time::sleep_until(at).await; + } + // Use the channel's own race-free capacity wake, not periodic samples. + // Return ALL permits before recover_one rechecks capacity and intent. + if let Ok(permits) = event_tx + .reserve_many(event_tx.max_capacity().div_ceil(2)) + .await + { + drop(permits); + return; + } + } + // No loss or a closed receiver: no immediate-ready/error wake loop. + std::future::pending::<()>().await; +} + +fn next_channel(state: &BgState) -> Option> { + // One record per active intent, not per loss or per request generation. + // Least recently attempted prevents a repeatedly overflowing channel from + // starving other channels or membership. Missing filters fail closed. + state + .channel_dropped_since + .keys() + .filter(|ch| { + state.active_subscriptions.contains_key(ch) + && state.active_filters.contains_key(ch) + && !state.rate_limited_pending.contains_key(ch) + && !state.resubscribe_retry.contains(ch) + }) + .copied() + .map(Some) + .chain( + (state.membership_sub_active + && state.membership_dropped_since.is_some() + && !state.membership_resub_needed) + .then_some(None), + ) + .min_by_key(|ch| { + let sub = ch.map_or_else(|| MEMBERSHIP_NOTIF_SUB_ID.to_owned(), channel_sub_id); + (state.recovery.last_attempt.get(&sub).copied(), sub) + }) +} diff --git a/crates/buzz-acp/src/relay/recovery_tests.rs b/crates/buzz-acp/src/relay/recovery_tests.rs new file mode 100644 index 00000000000..4e51a0c6147 --- /dev/null +++ b/crates/buzz-acp/src/relay/recovery_tests.rs @@ -0,0 +1,388 @@ +//! Bounded synthetic WebSocket fixtures; no relay service, proxy or real agent. +use super::tests::{next_test_frame, seed_test_subscription, test_channel_filter, test_ws_pair}; +use super::*; + +fn fixture_event(channel: Uuid, n: u64, kind: u16) -> Event { + let keys = + Keys::parse("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); + EventBuilder::new(Kind::Custom(kind), format!("synthetic-{n}")) + .tags([Tag::parse(["h", &channel.to_string()]).unwrap()]) + .custom_created_at(nostr::Timestamp::from(1_000 + n)) + .sign_with_keys(&keys) + .unwrap() +} + +async fn dispatch( + client: &mut WsStream, + state: &mut BgState, + tx: &mpsc::Sender>, + frame: Value, +) { + let (control_tx, _control_rx) = mpsc::channel(1); + assert!( + handle_ws_message( + Message::Text(frame.to_string().into()), + client, + tx, + &control_tx, + state, + &Keys::generate(), + "ws://127.0.0.1:1", + "synthetic-agent", + None, + ) + .await + ); +} + +#[tokio::test] +async fn repeated_overflow_recovers_only_affected_channel_after_capacity() { + let (mut client, mut server) = test_ws_pair().await; + let mut state = BgState::new(); + let channels: Vec<_> = (0..18).map(|_| Uuid::new_v4()).collect(); + for ch in &channels { + seed_test_subscription(&mut state, *ch); + } + let ch = channels[0]; + let sub = channel_sub_id(ch); + let (tx, mut rx) = mpsc::channel(256); + // Relay history is newest-first; the oldest dropped event must survive + // a watermark already advanced by much newer successfully-enqueued events. + let events: Vec<_> = (0..320).rev().map(|n| fixture_event(ch, n, 9)).collect(); + for event in &events { + dispatch(&mut client, &mut state, &tx, json!(["EVENT", sub, event])).await; + recovery::recover_one(&mut client, &mut state, &tx, "synthetic-agent").await; + } + assert_eq!(rx.len(), 256); + assert_eq!(state.channel_dropped_since[&ch], 1_000); + assert!(timeout(Duration::from_millis(30), server.next()) + .await + .is_err()); + for event in &events[..256] { + assert_eq!(rx.recv().await.unwrap().unwrap().event.id, event.id); + } + recovery::recover_one(&mut client, &mut state, &tx, "synthetic-agent").await; + let req = next_test_frame(&mut server).await; + assert_eq!(req[0], "REQ"); + assert_eq!(req[1], sub); + assert_eq!(req[2]["#h"], json!([ch.to_string()])); + assert_eq!(req[2]["kinds"], json!([9])); + assert_eq!(req[2]["since"], 995); + // Concurrent timer ticks / duplicate arrivals cannot replace the replay. + for _ in 0..40 { + recovery::recover_one(&mut client, &mut state, &tx, "synthetic-agent").await; + } + assert!(timeout(Duration::from_millis(30), server.next()) + .await + .is_err()); + for event in &events { + dispatch(&mut client, &mut state, &tx, json!(["EVENT", sub, event])).await; + } + dispatch(&mut client, &mut state, &tx, json!(["EOSE", sub])).await; + assert_eq!(rx.len(), 64, "delivered IDs must remain deduplicated"); + for event in &events[256..] { + assert_eq!(rx.recv().await.unwrap().unwrap().event.id, event.id); + } + assert_eq!(state.channel_since(&ch), Some(1_319)); + recovery::recover_one(&mut client, &mut state, &tx, "synthetic-agent").await; + assert!(timeout(Duration::from_millis(30), server.next()) + .await + .is_err()); + let live = fixture_event(ch, 400, 9); + dispatch(&mut client, &mut state, &tx, json!(["EVENT", sub, live])).await; + assert_eq!(rx.recv().await.unwrap().unwrap().event.id, live.id); + println!("18 subscriptions; 320 newest-first arrivals; 64 losses coalesced; 0 REQ while full; 1 targeted REQ; 320 unique deliveries + live"); +} + +#[tokio::test] +async fn socket_owner_services_ping_shutdown_and_coalesces_overflow_ticks() { + let (client, mut server) = test_ws_pair().await; + let (tx, mut rx) = mpsc::channel(1); + let (control_tx, _control_rx) = mpsc::channel(1); + let (cmd_tx, cmd_rx) = mpsc::channel(64); + let task = tokio::spawn(run_background_task( + client, + VecDeque::new(), + tx, + control_tx, + cmd_rx, + Keys::generate(), + "ws://127.0.0.1:1".into(), + "synthetic-agent".into(), + None, + )); + let channels: Vec<_> = (0..18).map(|_| Uuid::new_v4()).collect(); + for ch in &channels { + cmd_tx + .send(RelayCommand::Subscribe { + channel_id: *ch, + filter: test_channel_filter(), + replay_since: Some(1_000), + }) + .await + .unwrap(); + } + let mut subscriptions = 0; + while subscriptions < 18 { + let frame = timeout(Duration::from_secs(2), server.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + match frame { + Message::Text(text) => { + let req: Value = serde_json::from_str(&text).unwrap(); + assert_eq!(req[0], "REQ"); + server + .send(Message::Text(json!(["EOSE", req[1]]).to_string().into())) + .await + .unwrap(); + subscriptions += 1; + } + Message::Ping(payload) => server.send(Message::Pong(payload)).await.unwrap(), + other => panic!("unexpected {other:?}"), + } + } + let sub = channel_sub_id(channels[0]); + for n in 0..40 { + server + .send(Message::Text( + json!(["EVENT", sub, fixture_event(channels[0], n, 9)]) + .to_string() + .into(), + )) + .await + .unwrap(); + } + server.send(Message::Ping(vec![42].into())).await.unwrap(); + timeout(Duration::from_secs(2), async { + loop { + match server.next().await.unwrap().unwrap() { + Message::Ping(payload) => server.send(Message::Pong(payload)).await.unwrap(), + Message::Pong(payload) => { + assert_eq!(payload.as_ref(), &[42]); + break; + } + other => panic!("no immediate all-channel recovery before ping: {other:?}"), + } + } + }) + .await + .unwrap(); + // Wait across a recovery tick with the consumer still full. + assert!(socket_frame(&mut server, Duration::from_millis(5_100)) + .await + .is_none()); + rx.recv().await.unwrap().unwrap(); + let frame = timeout(Duration::from_secs(6), server.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + let req: Value = serde_json::from_str(frame.to_text().unwrap()).unwrap(); + assert_eq!(req[1], sub); + assert_eq!(req[2]["since"], 996); + assert!(timeout(Duration::from_millis(100), server.next()) + .await + .is_err()); + // Sustained lag: each replay is followed by another burst, without EOSE. + // Recovery must stay paced, not permanently stall or sweep healthy channels. + for round in 1..=3 { + let started = tokio::time::Instant::now(); + for n in round * 40..(round + 1) * 40 { + server + .send(Message::Text( + json!(["EVENT", sub, fixture_event(channels[0], n, 9)]) + .to_string() + .into(), + )) + .await + .unwrap(); + } + server.send(Message::Ping(vec![43].into())).await.unwrap(); + let pong = timeout(Duration::from_secs(1), server.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + assert!( + matches!(pong, Message::Pong(_)), + "recovery preempted ping: {pong:?}" + ); + rx.recv().await.unwrap().unwrap(); + let frame = timeout(Duration::from_secs(6), server.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + let req: Value = serde_json::from_str(frame.to_text().unwrap()).unwrap(); + assert_eq!(req[1], sub, "healthy channels must not be swept"); + assert!( + started.elapsed() >= Duration::from_secs(4), + "unpaced repeat" + ); + } + cmd_tx.send(RelayCommand::Shutdown).await.unwrap(); + timeout(Duration::from_secs(1), task) + .await + .unwrap() + .unwrap(); + println!("socket-owner seam: 18 live REQs, 39 coalesced losses, PONG while full, zero recovery across full-capacity timer tick, four paced targeted REQs over sustained lag without EOSE, responsive shutdown"); +} + +#[tokio::test] +async fn recovery_is_fair_and_paced_even_with_new_loss_and_stale_eose() { + let (mut client, mut server) = test_ws_pair().await; + let mut state = BgState::new(); + let channels = [Uuid::new_v4(), Uuid::new_v4()]; + for ch in channels { + seed_test_subscription(&mut state, ch); + state.channel_dropped_since.insert(ch, 600); + } + state.membership_sub_active = true; + state.membership_dropped_since = Some(500); + let (tx, _rx) = mpsc::channel(1); + let mut visited = HashSet::new(); + for round in 0..9 { + timeout( + Duration::from_millis(100), + recovery::ready(&tx, recovery::ready_at(&mut state)), + ) + .await + .unwrap(); + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + let req = next_test_frame(&mut server).await; + let sub = req[1].as_str().unwrap(); + if round < 3 { + assert!(visited.insert(sub.to_owned()), "starved intent"); + } + if sub == MEMBERSHIP_NOTIF_SUB_ID { + assert_eq!(req[2]["since"], 495); + state.membership_dropped_since = Some(500); + } else { + let ch = channel_id_from_sub_id(sub).unwrap(); + assert_eq!(req[2]["since"], 595); + state.channel_dropped_since.insert(ch, 600); + } + // Neither stale nor current EOSE creates completion state or erases loss. + dispatch(&mut client, &mut state, &tx, json!(["EOSE", sub])).await; + for _ in 0..30 { + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + } + assert!(timeout(Duration::from_millis(1), server.next()) + .await + .is_err()); + advance_clock(recovery::RECOVERY_INTERVAL).await; + } + for ch in channels { + state.active_subscriptions.remove(&ch); + state.clear_channel_state(&ch); + assert!(!state + .recovery + .last_attempt + .contains_key(&channel_sub_id(ch))); + } + assert_eq!(state.recovery.last_attempt.len(), 1); +} + +#[tokio::test] +async fn gate_headroom_failed_writes_and_reconnect_preserve_pending_attempts() { + let (mut client, mut server) = test_ws_pair().await; + let mut state = BgState::new(); + let ch = Uuid::new_v4(); + seed_test_subscription(&mut state, ch); + state.channel_dropped_since.insert(ch, 700); + let (tx, mut rx) = mpsc::channel(4); + for _ in 0..3 { + tx.try_send(None).unwrap(); + } + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert!(state.recovery.last_attempt.is_empty()); + rx.recv().await; + state.rate_limit_gate = Some(tokio::time::Instant::now() + Duration::from_secs(10)); + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert!(state.recovery.last_attempt.is_empty()); + advance_clock(Duration::from_secs(10)).await; + // Close locally, so the actual production writer fails deterministically. + client.close(None).await.unwrap(); + server.next().await; + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert_eq!(state.channel_dropped_since[&ch], 700); + let attempted = state.recovery.last_attempt.clone(); + for _ in 0..30 { + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + } + assert_eq!(state.recovery.last_attempt, attempted); + advance_clock(recovery::RECOVERY_INTERVAL).await; + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert_ne!( + state.recovery.last_attempt, attempted, + "failed write must be retried" + ); + assert_eq!(state.channel_dropped_since[&ch], 700); + + let (mut client, mut server) = test_ws_pair().await; + let (_cmd_tx, mut cmd_rx) = mpsc::channel(1); + assert!(matches!( + resubscribe_after_reconnect(&mut client, &mut cmd_rx, &mut state, "agent", true,).await, + ResubscribeResult::Ok + )); + let req = next_test_frame(&mut server).await; + assert_eq!(req[1], channel_sub_id(ch)); + assert_eq!(req[2]["since"], 695); + assert!(!state.channel_dropped_since.contains_key(&ch)); + // This is deliberately the baseline write-retirement contract, not a receipt. +} + +async fn advance_clock(duration: Duration) { + tokio::time::pause(); + tokio::time::advance(duration).await; + tokio::time::resume(); +} + +#[tokio::test] +async fn blocked_recovery_write_is_bounded_and_retains_loss() { + let (mut client, _stalled_server) = test_ws_pair().await; + let mut state = BgState::new(); + let ch = Uuid::new_v4(); + seed_test_subscription(&mut state, ch); + // Bounded 16MB JSON request exceeds loopback TCP buffering. The server does + // not read it. This tests the real production write/timeout, not a mock sink. + state.active_filters.get_mut(&ch).unwrap().kinds = Some(vec![9; 8_000_000]); + state.channel_dropped_since.insert(ch, 700); + let (tx, _rx) = mpsc::channel(1); + let started = tokio::time::Instant::now(); + timeout( + Duration::from_secs(15), + recovery::recover_one(&mut client, &mut state, &tx, "agent"), + ) + .await + .unwrap(); + assert!(started.elapsed() >= Duration::from_secs(WS_SEND_TIMEOUT_SECS)); + assert_eq!(state.channel_dropped_since[&ch], 700); + let attempted = state.recovery.last_attempt.clone(); + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert_eq!(state.recovery.last_attempt, attempted); +} + +// Keep the fixture responsive to independent client keepalives while checking +// recovery traffic. Wall-clock scheduling may deliver the initial ping late. +async fn socket_frame( + server: &mut WebSocketStream, + duration: Duration, +) -> Option { + let deadline = tokio::time::Instant::now() + duration; + loop { + match tokio::time::timeout_at(deadline, server.next()).await { + Err(_) => return None, + Ok(Some(Ok(Message::Ping(payload)))) => { + server.send(Message::Pong(payload)).await.unwrap(); + } + Ok(Some(Ok(frame))) => return Some(frame), + other => panic!("unexpected socket state: {other:?}"), + } + } +} + +#[path = "recovery_wake_tests.rs"] +mod wake; diff --git a/crates/buzz-acp/src/relay/recovery_wake_tests.rs b/crates/buzz-acp/src/relay/recovery_wake_tests.rs new file mode 100644 index 00000000000..de27c7d06c9 --- /dev/null +++ b/crates/buzz-acp/src/relay/recovery_wake_tests.rs @@ -0,0 +1,507 @@ +//! Capacity-wake boundaries and the independently reproduced R1 schedule. +use super::*; + +// Reviewer-authored, exact-source comparison of recurring headroom at the socket owner. +// A small periodically refilled queue is empty for most of each five-second period. +async fn review_count_until( + server: &mut WebSocketStream, + deadline: tokio::time::Instant, +) -> usize { + let mut count = 0; + while tokio::time::Instant::now() < deadline { + let remaining = deadline.saturating_duration_since(tokio::time::Instant::now()); + match socket_frame(server, remaining).await { + None => break, + Some(Message::Text(text)) => { + let frame: Value = serde_json::from_str(&text).unwrap(); + assert_eq!(frame[0], "REQ"); + count += 1; + } + other => panic!("unexpected {other:?}"), + } + } + count +} + +async fn review_barrier(server: &mut WebSocketStream) -> usize { + server.send(Message::Ping(vec![77].into())).await.unwrap(); + let mut count = 0; + loop { + match socket_frame(server, Duration::from_secs(2)).await.unwrap() { + Message::Pong(payload) => { + assert_eq!(payload.as_ref(), &[77]); + return count; + } + Message::Text(text) => { + let frame: Value = serde_json::from_str(&text).unwrap(); + assert_eq!(frame[0], "REQ"); + count += 1; + } + other => panic!("unexpected {other:?}"), + } + } +} + +#[tokio::test] +async fn review_recurring_headroom_between_ticks_gets_an_attempt() { + let (client, mut server) = test_ws_pair().await; + let (tx, mut rx) = mpsc::channel(1); + let (control_tx, _control_rx) = mpsc::channel(1); + let (cmd_tx, cmd_rx) = mpsc::channel(8); + let start = tokio::time::Instant::now(); + let task = tokio::spawn(run_background_task( + client, + VecDeque::new(), + tx, + control_tx, + cmd_rx, + Keys::generate(), + "ws://127.0.0.1:1".into(), + "agent".into(), + None, + )); + let ch = Uuid::new_v4(); + let sub = channel_sub_id(ch); + cmd_tx + .send(RelayCommand::Subscribe { + channel_id: ch, + filter: test_channel_filter(), + replay_since: Some(1000), + }) + .await + .unwrap(); + let frame = socket_frame(&mut server, Duration::from_secs(2)) + .await + .unwrap(); + let req: Value = serde_json::from_str(frame.to_text().unwrap()).unwrap(); + assert_eq!(req[1], sub); + let lost = fixture_event(ch, 1, 9); + for event in [fixture_event(ch, 0, 9), lost.clone()] { + server + .send(Message::Text( + json!(["EVENT", sub, event]).to_string().into(), + )) + .await + .unwrap(); + } + let mut attempts = review_barrier(&mut server).await; + assert_eq!(rx.len(), 1); + rx.recv().await.unwrap().unwrap(); + for round in 1..=4 { + // Headroom until 1s before the tick; then only one live arrival, no new + // overflow. The queue stays full across the tick and drains 0.5s later. + attempts += review_count_until( + &mut server, + start + Duration::from_millis(round * 5000 - 1000), + ) + .await; + assert_eq!(rx.len(), 0); + server + .send(Message::Text( + json!(["EVENT", sub, fixture_event(ch, 10 + round, 9)]) + .to_string() + .into(), + )) + .await + .unwrap(); + attempts += review_barrier(&mut server).await; + assert_eq!(rx.len(), 1); + attempts += review_count_until( + &mut server, + start + Duration::from_millis(round * 5000 + 500), + ) + .await; + rx.recv().await.unwrap().unwrap(); + } + println!("REVIEW periodic consumer: 4 full-at-tick windows, empty >=3.5s each period, recovery_requests={attempts}"); + assert_eq!( + attempts, 1, + "recurring headroom must not strand the first attempt" + ); + // Return the missing event using the actual requested stable subscription. + server + .send(Message::Text( + json!(["EVENT", sub, lost]).to_string().into(), + )) + .await + .unwrap(); + assert_eq!(review_barrier(&mut server).await, 0); + assert_eq!(rx.recv().await.unwrap().unwrap().event.id, lost.id); + let after = review_count_until(&mut server, start + Duration::from_millis(25_500)).await; + assert_eq!( + after, 0, + "no timer churn or extra requests after successful write" + ); + println!( + "REVIEW continuous-headroom control: additional_requests={after}; missing event delivered" + ); + cmd_tx.send(RelayCommand::Shutdown).await.unwrap(); + timeout(Duration::from_secs(2), task) + .await + .unwrap() + .unwrap(); +} + +/// Same real socket owner as production, with no control of its internal state. +struct Owner { + server: WebSocketStream, + rx: mpsc::Receiver>, + cmd: mpsc::Sender, + task: tokio::task::JoinHandle<()>, + ch: Uuid, +} + +impl Owner { + async fn new(capacity: usize) -> Self { + let (client, server) = test_ws_pair().await; + let (tx, rx) = mpsc::channel(capacity); + let (control_tx, _control_rx) = mpsc::channel(1); + let (cmd, cmd_rx) = mpsc::channel(8); + let task = tokio::spawn(run_background_task( + client, + VecDeque::new(), + tx, + control_tx, + cmd_rx, + Keys::generate(), + "ws://127.0.0.1:1".into(), + "agent".into(), + None, + )); + let mut owner = Self { + server, + rx, + cmd, + task, + ch: Uuid::new_v4(), + }; + owner.subscribe().await; + owner + } + + async fn subscribe(&mut self) { + self.cmd + .send(RelayCommand::Subscribe { + channel_id: self.ch, + filter: test_channel_filter(), + replay_since: Some(1000), + }) + .await + .unwrap(); + let req = self.request(Duration::from_secs(2)).await; + assert_eq!(req[1], channel_sub_id(self.ch)); + } + + async fn event(&mut self, n: u64) { + self.server + .send(Message::Text( + json!([ + "EVENT", + channel_sub_id(self.ch), + fixture_event(self.ch, n, 9) + ]) + .to_string() + .into(), + )) + .await + .unwrap(); + } + + async fn request(&mut self, duration: Duration) -> Value { + let frame = socket_frame(&mut self.server, duration) + .await + .expect("recovery not woken"); + let req: Value = serde_json::from_str(frame.to_text().unwrap()).unwrap(); + assert_eq!(req[0], "REQ"); + req + } + + async fn shutdown(self) { + self.cmd.send(RelayCommand::Shutdown).await.unwrap(); + timeout(Duration::from_secs(1), self.task) + .await + .unwrap() + .unwrap(); + } +} + +#[tokio::test] +async fn capacity_flapping_cannot_storm_or_delay_an_allowed_attempt() { + let mut owner = Owner::new(1).await; + owner.event(0).await; + owner.event(1).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.rx.recv().await.unwrap(); + let first = owner.request(Duration::from_millis(500)).await; + let first_at = tokio::time::Instant::now(); + assert_eq!(first[2]["since"], 996); + // New loss, then rapid full/empty transitions during the attempt cooldown. + // No socket activity or capacity transition may reset or bypass that bound. + for n in 1..=20 { + owner.event(n * 2).await; + owner.event(n * 2 + 1).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.rx.recv().await.unwrap(); + assert!(socket_frame(&mut owner.server, Duration::from_millis(10)) + .await + .is_none()); + } + let req = owner.request(Duration::from_secs(6)).await; + assert_eq!(req[1], channel_sub_id(owner.ch)); + // Arrival timestamps approximate send completion; leave tolerance for TCP. + assert!(first_at.elapsed() >= Duration::from_millis(4_900)); + assert!(first_at.elapsed() < Duration::from_secs(6)); + assert_eq!(req[2]["since"], 998); + assert!(socket_frame(&mut owner.server, Duration::from_millis(100)) + .await + .is_none()); + owner.shutdown().await; +} + +#[tokio::test] +async fn partial_capacity_wait_does_not_steal_live_slots_and_cancels_on_unsubscribe() { + let mut owner = Owner::new(5).await; // odd capacity: threshold rounds UP to 3 + for n in 0..6 { + owner.event(n).await; + } + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.rx.recv().await.unwrap(); // partial reservation, insufficient for replay + assert!(socket_frame(&mut owner.server, Duration::from_millis(30)) + .await + .is_none()); + owner.event(6).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + assert_eq!( + owner.rx.len(), + 5, + "select must release partial permits BEFORE try_send" + ); + for _ in 0..2 { + owner.rx.recv().await.unwrap(); + } + assert!(socket_frame(&mut owner.server, Duration::from_millis(30)) + .await + .is_none()); + owner.rx.recv().await.unwrap(); // exactly three slots free, prompt capacity wake + let req = owner.request(Duration::from_millis(500)).await; + assert_eq!(req[2]["since"], 1000); + assert_eq!( + owner + .rx + .recv() + .await + .unwrap() + .unwrap() + .event + .created_at + .as_secs(), + 1004 + ); + assert_eq!( + owner + .rx + .recv() + .await + .unwrap() + .unwrap() + .event + .created_at + .as_secs(), + 1006 + ); + + // Wait out cooldown then create another pending loss and partial reservation. + tokio::time::sleep(recovery::RECOVERY_INTERVAL).await; + for n in 7..13 { + owner.event(n).await; + } + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.rx.recv().await.unwrap(); + assert!(socket_frame(&mut owner.server, Duration::from_millis(30)) + .await + .is_none()); + owner + .cmd + .send(RelayCommand::Unsubscribe { + channel_id: owner.ch, + }) + .await + .unwrap(); + let close = socket_frame(&mut owner.server, Duration::from_secs(1)) + .await + .unwrap(); + let close: Value = serde_json::from_str(close.to_text().unwrap()).unwrap(); + assert_eq!(close[0], "CLOSE"); + while owner.rx.try_recv().is_ok() {} + assert!(socket_frame(&mut owner.server, Duration::from_millis(100)) + .await + .is_none()); + owner.subscribe().await; + assert!(socket_frame(&mut owner.server, Duration::from_millis(100)) + .await + .is_none()); + // A re-added intent can record and recover fresh loss immediately. + for n in 20..26 { + owner.event(n).await; + } + assert_eq!(review_barrier(&mut owner.server).await, 0); + while owner.rx.try_recv().is_ok() {} + let req = owner.request(Duration::from_millis(500)).await; + assert_eq!(req[2]["since"], 1020); + owner.shutdown().await; +} + +#[tokio::test] +async fn shutdown_and_transport_loss_cancel_a_capacity_wait() { + let mut owner = Owner::new(1).await; + owner.event(0).await; + owner.event(1).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + // Full queue cannot block commands or processing an actual socket close. + owner.server.close(None).await.unwrap(); + owner.shutdown().await; + let mut owner = Owner::new(1).await; + owner.event(0).await; + owner.event(1).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.shutdown().await; +} + +#[tokio::test] +async fn readiness_gate_ownership_and_attempt_deadlines_are_not_polling_ticks() { + let (mut client, mut server) = test_ws_pair().await; + let mut state = BgState::new(); + let ch = Uuid::new_v4(); + seed_test_subscription(&mut state, ch); + let (tx, mut rx) = mpsc::channel(4); + assert!(recovery::ready_at(&mut state).is_none()); + state.channel_dropped_since.insert(ch, 700); + state + .rate_limited_pending + .insert(ch, tokio::time::Instant::now()); + assert!(recovery::ready_at(&mut state).is_none()); + state.rate_limited_pending.clear(); + state.resubscribe_retry.insert(ch); + assert!(recovery::ready_at(&mut state).is_none()); + state.resubscribe_retry.clear(); + state.rate_limit_gate = Some(tokio::time::Instant::now() + Duration::from_millis(80)); + let at = recovery::ready_at(&mut state); + assert_eq!(at, state.rate_limit_gate); + assert!(timeout(Duration::from_millis(20), recovery::ready(&tx, at)) + .await + .is_err()); + timeout(Duration::from_millis(200), recovery::ready(&tx, at)) + .await + .unwrap(); + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + next_test_frame(&mut server).await; + state.channel_dropped_since.insert(ch, 800); + let at = recovery::ready_at(&mut state).unwrap(); + let remaining = at.saturating_duration_since(tokio::time::Instant::now()); + assert!(remaining > Duration::from_millis(4900)); + // Neither new loss nor an intervening gate shorter than cooldown delays it. + state.rate_limit_gate = Some(tokio::time::Instant::now() + Duration::from_millis(10)); + assert_eq!(recovery::ready_at(&mut state), Some(at)); + state.rate_limit_gate = Some(at + Duration::from_secs(1)); + assert_eq!(recovery::ready_at(&mut state), state.rate_limit_gate); + advance_clock(Duration::from_secs(6)).await; + // Cancellation releases partial permits. No hidden reservation survives it. + for _ in 0..3 { + tx.try_send(None).unwrap(); + } + assert!(timeout( + Duration::from_millis(20), + recovery::ready(&tx, recovery::ready_at(&mut state)) + ) + .await + .is_err()); + assert_eq!(tx.capacity(), 1); + rx.recv().await.unwrap(); + timeout( + Duration::from_millis(100), + recovery::ready(&tx, recovery::ready_at(&mut state)), + ) + .await + .unwrap(); + assert_eq!(tx.capacity(), 2); + drop(rx); + assert!(timeout( + Duration::from_millis(20), + recovery::ready(&tx, recovery::ready_at(&mut state)) + ) + .await + .is_err()); +} + +#[tokio::test] +async fn readiness_uses_channel_wakes_without_idle_churn_or_lost_capacity() { + use std::future::Future; + use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }; + use std::task::{Context, Wake, Waker}; + #[derive(Default)] + struct Wakes(AtomicUsize); + impl Wake for Wakes { + fn wake(self: Arc) { + self.0.fetch_add(1, Ordering::SeqCst); + } + fn wake_by_ref(self: &Arc) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let wakes = Arc::new(Wakes::default()); + let waker = Waker::from(wakes.clone()); + let mut cx = Context::from_waker(&waker); + let (tx, mut rx) = mpsc::channel(4); + for _ in 0..4 { + tx.try_send(None).unwrap(); + } + let mut idle = Box::pin(recovery::ready(&tx, None)); + assert!(idle.as_mut().poll(&mut cx).is_pending()); + rx.recv().await.unwrap(); + rx.recv().await.unwrap(); + assert_eq!( + wakes.0.load(Ordering::SeqCst), + 0, + "no loss: no capacity subscription" + ); + drop(idle); + // Capacity freed before registration cannot be lost; ready on first poll. + let now = Some(tokio::time::Instant::now()); + let mut ready = Box::pin(recovery::ready(&tx, now)); + assert!(ready.as_mut().poll(&mut cx).is_ready()); + assert_eq!(tx.capacity(), 2, "successful readiness returns all permits"); + drop(ready); + for _ in 0..2 { + tx.try_send(None).unwrap(); + } + let mut ready = Box::pin(recovery::ready(&tx, now)); + assert!(ready.as_mut().poll(&mut cx).is_pending()); + assert_eq!(wakes.0.load(Ordering::SeqCst), 0); + rx.recv().await.unwrap(); + assert_eq!( + wakes.0.load(Ordering::SeqCst), + 0, + "below threshold: do not wake" + ); + rx.recv().await.unwrap(); + assert_eq!( + wakes.0.load(Ordering::SeqCst), + 1, + "threshold: channel wakes its waiter" + ); + assert!(ready.as_mut().poll(&mut cx).is_ready()); + assert_eq!(tx.capacity(), 2); + drop(ready); + drop(rx); + let mut closed = Box::pin(recovery::ready(&tx, now)); + let before = wakes.0.load(Ordering::SeqCst); + assert!(closed.as_mut().poll(&mut cx).is_pending()); + assert_eq!( + wakes.0.load(Ordering::SeqCst), + before, + "closed: no self-wake loop" + ); +} From 93761e411ced557d859c00df1933ab053d8d854b Mon Sep 17 00:00:00 2001 From: Logan Johnson Date: Wed, 9 Sep 2026 10:31:42 -0400 Subject: [PATCH 07/26] fix(mobile): render push notification sender identity as npub (#7494) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 ## Summary When an iOS push notification comes from someone the app has no cached name for, the notification title showed the first characters of the sender's raw public key — for example `aa4fc866…`. That fragment is unreadable and doesn't match how the same person appears anywhere else in Buzz. This PR changes that title to the compact form of the sender's npub (npub is the human-readable encoding of a Nostr public key): first 8 and last 4 characters — for example `npub14f8…9nsy`, the same identity shape used across the desktop and mobile apps. - Unnamed senders: raw hex fragment → compact npub. - Named senders: unchanged — a sender the app has a display name for still titles the notification with that name. - Unverifiable sender identities (malformed keys, or lookalike strings that are not literal 64-hex-digit keys) now render a neutral "Someone" instead of partial raw key material. - Everything else about the notification is unchanged: body text, subtitle, thread matching and grouping, deep-link navigation, thread identifiers, and the internal hex public key the resolver matches on. The native iOS notification-service package (`BuzzPushKit`) gains a minimal in-house bech32 codec (bech32 is the checksummed string encoding npubs use) — checksum-validated, 32-byte keys only, and no new external dependency. The hex input branch accepts exactly a 64 ASCII hex digit key before any parsing, so strings that merely parse like hex (for example a run of `+a` pairs) cannot become a displayed identity; this is input validation for presentation. Event signature verification is untouched. ### Related issue Fixes: N/A. Searched existing issues/PRs for push-notification npub identity — closest related: none found. ### Testing At head `3e3f2813b8864b76257ccb50dea3a4b31fa4de0d` (base `44316ff72f5f7de014c66b01cbf534298a70c249`; 4 files, +321/−4): - CI `Mobile Swift` lane, at this exact head — all passed: `swift test` (73 tests, 0 failures), the SwiftPM debug and release builds of `mobile/ios/BuzzPushKit`, and the unsigned iOS release build. - Test coverage: npub encoding cross-checked against independent nostr-rs/NIP-19 vectors; rejection of bad checksums, mixed case, wrong lengths, invalid alphabet, padding, and non-32-byte payloads; resolver boundary matrix — hex/npub/invalid sender keys render compact npub or "Someone" while body, subtitle, sender key, and thread identifier pass through; named senders keep cached display names. ### Task provenance Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9` Task: buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6 --------- Signed-off-by: Logan Johnson Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> --- .../Sources/BuzzPushKit/Bech32.swift | 179 ++++++++++++++++++ .../BuzzPushNotificationResolver.swift | 9 +- .../Tests/BuzzPushKitTests/Bech32Tests.swift | 98 ++++++++++ .../BuzzPushNotificationResolverTests.swift | 39 +++- 4 files changed, 321 insertions(+), 4 deletions(-) create mode 100644 mobile/ios/BuzzPushKit/Sources/BuzzPushKit/Bech32.swift create mode 100644 mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/Bech32Tests.swift diff --git a/mobile/ios/BuzzPushKit/Sources/BuzzPushKit/Bech32.swift b/mobile/ios/BuzzPushKit/Sources/BuzzPushKit/Bech32.swift new file mode 100644 index 00000000000..e6f31bf8e3e --- /dev/null +++ b/mobile/ios/BuzzPushKit/Sources/BuzzPushKit/Bech32.swift @@ -0,0 +1,179 @@ +import Foundation + +/// Minimal bech32 (BIP-173) codec backing NIP-19 npub sender labels. +/// +/// Only what push notification presentation needs is implemented: encoding +/// 32-byte public keys as npub and validating npub inputs well enough to +/// canonicalize them. Segwit addresses and bech32m are out of scope; NIP-19 +/// uses the original bech32 checksum. +enum Bech32 { + /// Bech32 data charset from BIP-173, indexed by 5-bit value. + private static let charset: [Character] = Array("qpzry9x8gf2tvdw0s3jn54khce6mua7l") + /// Bech32 charset lookup, built once for decoding. + private static let valueByCharacter: [Character: UInt8] = Dictionary( + uniqueKeysWithValues: charset.enumerated().map { ($1, UInt8($0)) }) + /// Generator polynomial coefficients from BIP-173. + private static let generator: [UInt32] = [ + 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3, + ] + /// Checksum length in 5-bit values, from BIP-173. + private static let checksumLength = 6 + /// Maximum bech32 string length, from BIP-173. + private static let maximumLength = 90 + + // MARK: Codec + + /// Encodes 5-bit values under a human-readable part (hrp), appending the + /// bech32 checksum. Returns nil for an invalid hrp or out-of-range values. + static func encode(hrp: String, values: [UInt8]) -> String? { + let valuesInRange = values.allSatisfy { $0 < 32 } + guard isPrintableASCII(hrp), valuesInRange, + hrp.count + 1 + values.count + checksumLength <= maximumLength + else { return nil } + let checksum = checksum(hrp: hrp, values: values) + return hrp + "1" + String((values + checksum).map { charset[Int($0)] }) + } + + /// Decodes a bech32 string into its hrp and 5-bit values, checksum-verified + /// and stripped. Follows BIP-173: characters must be printable ASCII, the + /// string must be entirely lowercase or entirely uppercase and at most 90 + /// characters long, and it must end in a valid checksum. + static func decode(_ string: String) -> (hrp: String, values: [UInt8])? { + guard !string.isEmpty, string.count <= maximumLength, isPrintableASCII(string) + else { return nil } + let lowercased = string.lowercased() + guard lowercased == string || lowercased.uppercased() == string, + let separator = lowercased.lastIndex(of: "1"), + separator != lowercased.startIndex + else { return nil } + let hrp = String(lowercased[..= checksumLength, + polymod(hrpExpanded(hrp) + allValues.map(UInt32.init)) == 1 + else { return nil } + return (hrp, Array(allValues.dropLast(checksumLength))) + } + + // MARK: NIP-19 + + /// The npub of a 32-byte public key, or nil for any other length. + static func npub(from bytes: [UInt8]) -> String? { + guard bytes.count == 32, + let values = convertBits(bytes, fromBits: 8, toBits: 5, padding: true) + else { return nil } + return encode(hrp: "npub", values: values) + } + + /// The 32-byte public key encoded by a valid npub, or nil for anything + /// else — an `npub1…` prefix alone is never trusted: the bech32 checksum + /// must validate and the payload must decode to exactly 32 bytes. + static func npubBytes(from string: String) -> [UInt8]? { + guard let (hrp, values) = decode(string), hrp == "npub", + let bytes = convertBits(values, fromBits: 5, toBits: 8, padding: false), + bytes.count == 32 + else { return nil } + return bytes + } + + /// The canonical npub for a public key supplied as exactly 64 ASCII hex + /// digits or as an existing npub (including the uppercase bech32 form, + /// which BIP-173 decoders must accept). Returns nil for anything else; + /// callers must fall back to a neutral label rather than raw key material. + static func canonicalNpub(from identifier: String) -> String? { + if isHexKey(identifier), + let bytes = VerifiedNostrEvent.hexBytes(identifier.lowercased()), bytes.count == 32 + { + return npub(from: bytes) + } + return npubBytes(from: identifier).flatMap { npub(from: $0) } + } + + // MARK: Internals + + /// Whether `value` is exactly 64 ASCII hex digits (0-9, A-F, a-f). + /// + /// `VerifiedNostrEvent.hexBytes` parses pairs with `UInt8(_:radix: 16)`, + /// which also accepts a leading sign — "+a" parses as 10 and "-0" as 0 — + /// so strings like "+a"×32 would otherwise decode to 32 bytes. The hex + /// branch of `canonicalNpub` gates on this literal key shape before any + /// hex parsing or allocation; every other input must arrive as a strictly + /// checksummed npub. + private static func isHexKey(_ value: String) -> Bool { + guard value.count == 64 else { return false } + return value.allSatisfy { character in + guard let ascii = character.asciiValue else { return false } + return (48...57).contains(ascii) // 0-9 + || (65...70).contains(ascii) // A-F + || (97...102).contains(ascii) // a-f + } + } + + private static func isPrintableASCII(_ string: String) -> Bool { + !string.isEmpty && string.allSatisfy { (33...126).contains($0.asciiValue ?? 0) } + } + + private static func checksum(hrp: String, values: [UInt8]) -> [UInt8] { + let expanded = + hrpExpanded(hrp) + values.map(UInt32.init) + + [UInt32](repeating: 0, count: checksumLength) + let polymod = polymod(expanded) ^ 1 + return (0..> shift) & 31) + } + } + + private static func polymod(_ values: [UInt32]) -> UInt32 { + var accumulator: UInt32 = 1 + for value in values { + let top = accumulator >> 25 + accumulator = ((accumulator & 0x1ffffff) << 5) ^ value + for (index, coefficient) in generator.enumerated() where (top >> index) & 1 == 1 { + accumulator ^= coefficient + } + } + return accumulator + } + + private static func hrpExpanded(_ hrp: String) -> [UInt32] { + let scalars = hrp.unicodeScalars.map { $0.value } + return scalars.map { $0 >> 5 } + [0] + scalars.map { $0 & 31 } + } + + /// Regroups a byte string between bit widths, as in BIP-173's `convertbits`. + /// With padding disabled, a nonzero remainder is rejected instead of padded. + private static func convertBits( + _ bytes: [UInt8], fromBits: Int, toBits: Int, padding: Bool + ) -> [UInt8]? { + var accumulator: UInt32 = 0 + var accumulated = 0 + var result: [UInt8] = [] + result.reserveCapacity((bytes.count * fromBits + toBits - 1) / toBits) + let maxValue: UInt32 = (1 << toBits) - 1 + let maxAccumulator: UInt32 = (1 << (fromBits + toBits - 1)) - 1 + let maxInput: Int = 1 << fromBits + for byte in bytes { + guard Int(byte) < maxInput else { return nil } + accumulator = ((accumulator << fromBits) | UInt32(byte)) & maxAccumulator + accumulated += fromBits + while accumulated >= toBits { + accumulated -= toBits + result.append(UInt8((accumulator >> accumulated) & maxValue)) + } + } + if padding { + if accumulated > 0 { + result.append(UInt8((accumulator << (toBits - accumulated)) & maxValue)) + } + } else if accumulated >= fromBits + || ((accumulator << (toBits - accumulated)) & maxValue) != 0 + { + return nil + } + return result + } +} diff --git a/mobile/ios/BuzzPushKit/Sources/BuzzPushKit/BuzzPushNotificationResolver.swift b/mobile/ios/BuzzPushKit/Sources/BuzzPushKit/BuzzPushNotificationResolver.swift index 47661e81300..7836385a773 100644 --- a/mobile/ios/BuzzPushKit/Sources/BuzzPushKit/BuzzPushNotificationResolver.swift +++ b/mobile/ios/BuzzPushKit/Sources/BuzzPushKit/BuzzPushNotificationResolver.swift @@ -611,8 +611,15 @@ public final class BuzzPushNotificationResolver: BuzzPushNotificationResolving { ? String(result.prefix(177)).trimmingCharacters(in: .whitespacesAndNewlines) + "…" : result } + /// Compact sender label for unnamed senders: the first 8 and last 4 + /// characters of the sender's full npub, matching the compact npub shape + /// used across Buzz. A key that cannot be encoded as an npub falls back to + /// the neutral "Someone" identity so malformed payloads leak no key + /// material while the notification keeps a useful title. static func shortPubkey(_ pubkey: String) -> String { - pubkey.count > 8 ? String(pubkey.prefix(8)) + "…" : pubkey + guard let npub = Bech32.canonicalNpub(from: pubkey) else { return "Someone" } + return npub.count > 12 + ? String(npub.prefix(8)) + "…" + String(npub.suffix(4)) : npub } private func loadCommunities() -> [PushLeaseCommunity] { diff --git a/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/Bech32Tests.swift b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/Bech32Tests.swift new file mode 100644 index 00000000000..44ad58e0f2b --- /dev/null +++ b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/Bech32Tests.swift @@ -0,0 +1,98 @@ +import XCTest +@testable import BuzzPushKit + +/// Bech32 codec tests against independently published npub vectors — the +/// NIP-19 spec example key and the nostr crate 0.44 test suite — plus +/// degenerate key material for the padding and payload-length boundaries. +/// Generic BIP-173 conformance is not asserted here: nothing outside the +/// codec calls raw decode, so alphabet, checksum, case, and length +/// rejection are pinned at canonicalNpub/npubBytes, the npub seam Buzz +/// actually uses. +final class Bech32Tests: XCTestCase { + /// Hex public keys with their published npub equivalents. + static let npubVectors: [(hex: String, npub: String)] = [ + // nostr-rs 0.44 key test: aa4fc866… ↔ npub14f8usejl…qqh9nsy. + ( + "aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4", + "npub14f8usejl26twx0dhuxjh9cas7keav9vr0v8nvtwtrjqx3vycc76qqh9nsy" + ), + // The NIP-19 spec's example profile key. + ( + "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", + "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6" + ), + // Degenerate key material still encodes (and exercises 5-bit padding). + (String(repeating: "00", count: 32), "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzqujme"), + ] + + func testNpubEncodingMatchesKnownVectors() throws { + for vector in Self.npubVectors { + let bytes = try XCTUnwrap(VerifiedNostrEvent.hexBytes(vector.hex)) + XCTAssertEqual(bytes.count, 32) + XCTAssertEqual(Bech32.npub(from: bytes), vector.npub, "hex: \(vector.hex)") + XCTAssertEqual(Bech32.canonicalNpub(from: vector.hex), vector.npub, "hex: \(vector.hex)") + } + } + + func testCanonicalNpubValidatesAndCanonicalizesNpubInputs() throws { + for vector in Self.npubVectors { + let npub = try XCTUnwrap(Bech32.canonicalNpub(from: vector.npub)) + XCTAssertEqual(npub, vector.npub, "npub: \(vector.npub)") + XCTAssertEqual( + Bech32.npubBytes(from: npub).map(VerifiedNostrEvent.hex), vector.hex, + "npub: \(vector.npub)") + } + // Uppercase bech32 is valid per BIP-173; canonical output is lowercase. + XCTAssertEqual( + Bech32.canonicalNpub( + from: "NPUB14F8USEJL26TWX0DHUXJH9CAS7KEAV9VR0V8NVTWTRJQX3VYCC76QQH9NSY"), + "npub14f8usejl26twx0dhuxjh9cas7keav9vr0v8nvtwtrjqx3vycc76qqh9nsy") + // Letter case within a hex key stays valid input; digits are caseless, + // so mixed-case letters still canonicalize to the lowercase npub. + let mixedCaseHex = String( + Self.npubVectors[0].hex.enumerated().map { + $0.offset.isMultiple(of: 2) ? $0.element : Character($0.element.uppercased()) + }) + XCTAssertEqual(Bech32.canonicalNpub(from: mixedCaseHex), Self.npubVectors[0].npub) + } + + func testCanonicalNpubRejectsInvalidAndLookalikeKeys() { + let rejected = [ + // Junk and empty payloads. + "", + "author-pubkey", + String(repeating: "a", count: 63), + "0", + // Hex that is not a 32-byte key. + String(repeating: "ab", count: 31), + String(repeating: "ab", count: 33), + // Signed radix-16 chunks ("+a"→10, "-0"→0) parse via UInt8(_:radix:) + // but are not literal ASCII hex digits, so never 32-byte keys. + String(repeating: "+a", count: 32), + String(repeating: "+A", count: 32), + String(repeating: "-0", count: 32), + // Npub-shaped payload with a character outside the bech32 alphabet + // ("b" never appears in the charset). + "npub1bqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzqujme", + // Valid npub with the checksum's final character mutated. + "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzqujma", + // Mixed case is never valid bech32. + "Npub14f8usejl26twx0dhuxjh9cas7keav9vr0v8nvtwtrjqx3vycc76qqh9nsy", + // Valid checksum but the wrong payload length for a key. + "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqk7h3rf", + "npub1llllllllllllllllllllllllllllllllllllllllllllllllllll7w6tc2n", + // Valid bech32 under a different human-readable part. + "nsec1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqwkhnav", + ] + for string in rejected { + XCTAssertNil(Bech32.canonicalNpub(from: string), "expected rejection: \(string)") + XCTAssertNil(Bech32.npubBytes(from: string), "expected rejection: \(string)") + } + } + + func testNpubRejectsNon32ByteKeys() { + XCTAssertNil(Bech32.npub(from: [UInt8](repeating: 0, count: 16))) + XCTAssertNil(Bech32.npub(from: [UInt8](repeating: 0xff, count: 33))) + XCTAssertNil(Bech32.npub(from: [])) + } +} diff --git a/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzPushNotificationResolverTests.swift b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzPushNotificationResolverTests.swift index b25e23595f6..6620f193699 100644 --- a/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzPushNotificationResolverTests.swift +++ b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzPushNotificationResolverTests.swift @@ -18,6 +18,10 @@ final class BuzzPushNotificationResolverTests: XCTestCase { static let relayPrivateKey = String(repeating: "0", count: 63) + "3" static let gatewayBody = "Reconnect to your relay now" static let channelID = "123e4567-e89b-42d3-a456-426614174000" + static let unnamedSenderHex = + "aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4" + static let unnamedSenderNpub = + "npub14f8usejl26twx0dhuxjh9cas7keav9vr0v8nvtwtrjqx3vycc76qqh9nsy" override func setUp() { super.setUp() @@ -129,6 +133,35 @@ final class BuzzPushNotificationResolverTests: XCTestCase { XCTAssertEqual(result?.0.body, "lower ID") } + func testDecodeResolutionSenderTitlesCanonicalizeKeysOrFallBackToNeutral() { + // The sender-identity presentation boundary: a verifiable key renders + // as the same compact npub whether it arrives as hex or as an npub, and + // an unverifiable key — including radix impostors like "+a"×32 that + // parse as hex pairs but are not literal hex keys — gets the neutral + // identity, never raw key material, while body, subtitle, and internal + // payload fields pass through untouched. Malformed-input classification + // itself is pinned at the codec (Bech32Tests); named senders winning + // over these labels is covered by the cached-profile resolve tests. + let senders: [(pubkey: String, title: String)] = [ + (Self.unnamedSenderHex, "npub14f8…9nsy"), + (Self.unnamedSenderNpub, "npub14f8…9nsy"), + ("author-pubkey", "Someone"), + (String(repeating: "+a", count: 32), "Someone"), + ] + for sender in senders { + let result = BuzzPushNotificationResolver.decodeResolution( + events: [event(pubkey: sender.pubkey, content: "Preview")], + community: community() + ) + + XCTAssertEqual(result?.0.title, sender.title, "pubkey: \(sender.pubkey)") + XCTAssertEqual(result?.0.body, "Preview", "pubkey: \(sender.pubkey)") + XCTAssertEqual(result?.0.subtitle, "Community", "pubkey: \(sender.pubkey)") + XCTAssertEqual(result?.0.senderPubkey, sender.pubkey, "pubkey: \(sender.pubkey)") + XCTAssertEqual(result?.0.threadIdentifier, "community-id", "pubkey: \(sender.pubkey)") + } + } + func testResolveSucceedsAndMutatesGatewayContent() throws { let event = try JSONDecoder().decode( VerifiedNostrEvent.self, @@ -146,7 +179,7 @@ final class BuzzPushNotificationResolverTests: XCTestCase { XCTAssertNotEqual(result.title, Self.gatewayBody) XCTAssertNotEqual(result.body, Self.gatewayBody) - XCTAssertEqual(result.title, String(event.pubkey.prefix(8)) + "…") + XCTAssertEqual(result.title, "npub1ccz…mnyd") XCTAssertEqual(result.body, "Hello Buzz") XCTAssertEqual(result.subtitle, "Community") XCTAssertEqual( @@ -559,7 +592,7 @@ final class BuzzPushNotificationResolverTests: XCTestCase { ) ) - XCTAssertEqual(result.title, String(message.pubkey.prefix(8)) + "…") + XCTAssertEqual(result.title, "npub1ccz…mnyd") XCTAssertNil(result.conversationDisplayName) XCTAssertEqual(result.subtitle, "Community") XCTAssertEqual(result.body, "Fallback content") @@ -602,7 +635,7 @@ final class BuzzPushNotificationResolverTests: XCTestCase { ) ) - XCTAssertEqual(result.title, String(message.pubkey.prefix(8)) + "…") + XCTAssertEqual(result.title, "npub1ccz…mnyd") XCTAssertNil(result.conversationDisplayName) XCTAssertEqual(result.body, "Bounded fallback") XCTAssertEqual(URLProtocolStub.requests.count, 2) From bfc384855889432df4a333a0edf3080f332ee169 Mon Sep 17 00:00:00 2001 From: Logan Johnson Date: Wed, 9 Sep 2026 10:32:26 -0400 Subject: [PATCH 08/26] fix(desktop): shared npub identity foundation (canonicalNpub, PubKey gate, strict parser) (#7488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 ## Summary Identity keys in the desktop app are displayed as raw 64-character hex. A person's key shows up as something like `953d3363…` — unreadable, impossible to recognize as the same identity on another screen, and a hazard when copied by hand. Nostr (the protocol Buzz runs on) has a human-readable spelling for identity keys — the `npub1…` form — but the desktop app did not use it consistently. This is the foundation of the desktop npub changes: it adds the shared pieces every identity surface builds on, and two follow-up slices stack directly on this branch — #7489 converts the identity controls (profile, settings, allowlist, workflow key fields) and #7495 converts the everyday display surfaces (mentions, member lists, sidebar, and other name fallbacks). After this change: - The shared identity widget shows the compact npub form — `npub1j57...fjmv` — instead of a hex prefix, everywhere it renders (for example the owned-agent public-key row on a profile). Copying it puts the full npub on the clipboard. - Copy is a real interaction, verified end-to-end: both popover variants put the exact canonical npub on the actual clipboard — never the raw hex the popover also lists, never a truncation — and a portaled popover's clicks no longer steal focus from the new-DM To-field mid-copy. Pointer copy, a natural Space-then-Enter path, and inner/outer Escape are covered. - Anything that isn't a valid identity key fails neutrally: short or corrupt values — including degenerate values that technically encode to a checksum-valid npub but aren't real identity keys — show "Unavailable" with no copy button, instead of a misleading value. - Both valid npub spellings display: all-lowercase `npub1…` and all-uppercase `NPUB1…` (Bech32, npub's encoding, permits either casing) both render the same canonical lowercase npub. Mixed case is rejected by the display path as written — `canonicalNpub` and the widget don't case-normalize input — while input parsing (`parsePubkeyInput`) keeps its trim-and-lowercase normalization and accepts mixed-case npubs; both paths require the decoded payload to be exactly a 64-character identity key. - Identity-key input is strict on payload: an npub whose decoded payload isn't exactly a 64-character identity key is rejected, matching the validation the app's Rust side already applies to agent allowlists. Intentional scope boundary: only surfaces that render through the shared widget change here. Outer profile copy, settings identity cards, the respond-to allowlist, and workflow key fields still show hex — they move to npub in the controls follow-up (#7489). Nothing else changes identity representation: display names, private keys, event IDs, and the hex the app stores, sends, and matches internally are untouched; only the user-facing spelling of an identity key changes. ## Details - `desktop/src/shared/lib/pubkey.ts` — `canonicalNpub()`: strict canonical full-npub helper (64-char hex in any case, or a checksum-validated npub, returns the canonical npub; anything else returns `null`); `truncateNpub()`: the compact display form; existing exports unchanged. - `desktop/src/shared/ui/PubKey.tsx` — the shared widget's identity gate validates through `canonicalNpub`; the popover copies the npub only. - `desktop/src/shared/lib/nostrUtils.ts` — `parsePubkeyInput` rejects npubs whose payload is not exactly a 64-character identity key. - `desktop/src/features/messages/ui/NewMessageScreen.tsx` — the To-field focuses its search input only for clicks that land inside the field itself, so portaled recipient popovers keep their focus while open (a popover click previously dismissed it mid-copy). - Unit suites cover the helper, widget, and parser (including the degenerate-encode and uppercase regressions); the e2e specs that render these rows assert the npub display. ### Related issue - Fixes: N/A. Searched existing issues/PRs for npub identity display — no existing match. - Stack: #7489 is based on this branch and builds on these primitives; it does not stand alone on main. ### Testing At head `b3310c248` (base: main `44316ff72`; 12 files, +440/−39): - Focused unit suites (pubkey, PubKey, parsePubkeyInput): 20/20 green; mutation-checked — removing the decoded-length predicate fails the short/empty checksum-valid-npub assertions in `canonicalNpub` and the widget, and a wrong-identity clipboard value fails the new copy assertions. - `pnpm typecheck` and `pnpm check`: pass; full desktop unit suite 6459/6459 at this exact head. - Targeted e2e at this exact head: 8/8 across the two specs that own the clipboard flows — `agent-access-warning.spec.ts` (compact variant, agent-access owner hint) and `pubkey-display-screenshots.spec.ts` (full variant, new-DM recipient verification: pointer copy, popover surviving the copy, inner/outer Escape, Space-then-Enter). - No Rust-side or build files change in this PR, so those results are unaffected. ### Task provenance Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9` Task: buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6 --------- Signed-off-by: Logan Johnson Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> --- .../features/messages/ui/NewMessageScreen.tsx | 17 ++- desktop/src/shared/lib/nostrUtils.ts | 10 +- .../src/shared/lib/parsePubkeyInput.test.mjs | 17 +++ desktop/src/shared/lib/pubkey.test.mjs | 69 +++++++++- desktop/src/shared/lib/pubkey.ts | 62 ++++++++- desktop/src/shared/ui/PubKey.test.mjs | 118 ++++++++++++++++++ desktop/src/shared/ui/PubKey.tsx | 81 +++++++----- .../tests/e2e/agent-access-warning.spec.ts | 26 ++++ desktop/tests/e2e/identity-archive.spec.ts | 5 +- desktop/tests/e2e/mentions.spec.ts | 5 +- desktop/tests/e2e/profile.spec.ts | 3 +- .../e2e/pubkey-display-screenshots.spec.ts | 66 ++++++++++ 12 files changed, 440 insertions(+), 39 deletions(-) create mode 100644 desktop/src/shared/ui/PubKey.test.mjs diff --git a/desktop/src/features/messages/ui/NewMessageScreen.tsx b/desktop/src/features/messages/ui/NewMessageScreen.tsx index f7192f6e45c..5c5c527df7c 100644 --- a/desktop/src/features/messages/ui/NewMessageScreen.tsx +++ b/desktop/src/features/messages/ui/NewMessageScreen.tsx @@ -328,7 +328,22 @@ export function NewMessageScreen() {
{ + onClick={(event) => { + // Portaled popovers (recipient inspection and its nested key + // copy) still bubble through React's tree to this handler, + // but their event targets are not DOM descendants of the + // field. Those clicks belong to the popover's own controls + // — they must not steal focus into the search input (which + // dismisses the popover via focus-outside) or reopen the + // picker. Only clicks physically within the recipient field + // focus its input. + const { currentTarget, target } = event; + if ( + !(target instanceof Node) || + !currentTarget.contains(target) + ) { + return; + } setIsRecipientPickerOpen(true); searchInputRef.current?.focus({ preventScroll: true }); }} diff --git a/desktop/src/shared/lib/nostrUtils.ts b/desktop/src/shared/lib/nostrUtils.ts index d98c6ee8cfb..9b6fe0fb23d 100644 --- a/desktop/src/shared/lib/nostrUtils.ts +++ b/desktop/src/shared/lib/nostrUtils.ts @@ -31,7 +31,13 @@ const HEX_PUBKEY_REGEX = /^[0-9a-f]{64}$/; * anything else (does NOT throw — intended for live form validation). * * The input is trimmed first; surrounding whitespace from copy-paste is - * tolerated. + * tolerated. It is also case-normalized before matching and decoding — + * preexisting behavior — so a hex key in any casing resolves, and a + * mixed-case npub (invalid Bech32 as written) is accepted via its + * lowercased form. The identity payload itself stays strict: it must + * decode to exactly a 64-char hex identity key, because `npubEncode` also + * encodes degenerate short payloads (even `""`), which are never valid + * identities. */ export function parsePubkeyInput(input: string): string | null { const trimmed = input.trim().toLowerCase(); @@ -41,7 +47,7 @@ export function parsePubkeyInput(input: string): string | null { if (trimmed.startsWith("npub1")) { try { const decoded = decode(trimmed); - if (decoded.type === "npub") { + if (decoded.type === "npub" && HEX_PUBKEY_REGEX.test(decoded.data)) { return decoded.data; } } catch { diff --git a/desktop/src/shared/lib/parsePubkeyInput.test.mjs b/desktop/src/shared/lib/parsePubkeyInput.test.mjs index f9aa57d87f1..b74331cd984 100644 --- a/desktop/src/shared/lib/parsePubkeyInput.test.mjs +++ b/desktop/src/shared/lib/parsePubkeyInput.test.mjs @@ -19,6 +19,16 @@ describe("parsePubkeyInput", () => { assert.equal(parsePubkeyInput(NPUB), HEX); }); + it("normalizes a mixed-case npub to its canonical hex", () => { + // Preexisting behavior: user input is lowercased before decoding, so a + // mixed-case npub — invalid Bech32 as written — still resolves to the + // identity. canonicalNpub is the strict counterpart (see ../lib/pubkey.ts). + assert.equal( + parsePubkeyInput(`${NPUB.slice(0, 10)}${NPUB.slice(10).toUpperCase()}`), + HEX, + ); + }); + it("tolerates surrounding whitespace from copy-paste", () => { assert.equal(parsePubkeyInput(` ${NPUB}\n`), HEX); assert.equal(parsePubkeyInput(` ${HEX} `), HEX); @@ -42,6 +52,13 @@ describe("parsePubkeyInput", () => { assert.equal(parsePubkeyInput(`${HEX}0`), null); }); + it("rejects degenerate npubs whose payload is not a 64-char identity", () => { + // `npubEncode` happily encodes short payloads with valid checksums — + // those are not identity keys and must never bind as one. + assert.equal(parsePubkeyInput("npub1m6kmamcvty5gd"), null); + assert.equal(parsePubkeyInput("npub106246s"), null); + }); + it("rejects non-hex non-npub input", () => { assert.equal(parsePubkeyInput(""), null); assert.equal(parsePubkeyInput("alice"), null); diff --git a/desktop/src/shared/lib/pubkey.test.mjs b/desktop/src/shared/lib/pubkey.test.mjs index 76d0a29b231..bce5834162d 100644 --- a/desktop/src/shared/lib/pubkey.test.mjs +++ b/desktop/src/shared/lib/pubkey.test.mjs @@ -1,10 +1,20 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { normalizePubkey, truncatePubkey } from "./pubkey.ts"; +import { + canonicalNpub, + normalizePubkey, + truncateNpub, + truncatePubkey, +} from "./pubkey.ts"; const PUBKEY = "44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435"; +const PUBKEY_NPUB = + "npub1gjuws2a2dc8z2nszprtg7v6u9q7ffeah3hgl5yx45jwn7y7aqs6s5e9xj6"; +const HEX = "ea9b4d7a7a78a3e3729e5568b14d764d4962be0e1f20f749bcf8d9dbbf9a9328"; +const HEX_NPUB = + "npub1a2d567n60z37xu57245tzntkf4yk90swrus0wjdulrvah0u6jv5qusyp60"; test("truncates to the canonical 8+4 form with unicode ellipsis", () => { assert.equal(truncatePubkey(PUBKEY), "44b8e82b…0435"); @@ -18,3 +28,60 @@ test("returns short strings unchanged", () => { test("normalizePubkey trims and lowercases", () => { assert.equal(normalizePubkey(" ABCDEF "), "abcdef"); }); + +test("truncateNpub compacts the hex pubkey's npub, not its hex form", () => { + assert.equal(truncateNpub(PUBKEY), "npub1gju…9xj6"); + assert.equal(truncateNpub(HEX), "npub1a2d…yp60"); + assert.equal(truncateNpub(HEX.toUpperCase()), "npub1a2d…yp60"); +}); + +test("truncateNpub accepts already-npub strings", () => { + assert.equal(truncateNpub(PUBKEY_NPUB), "npub1gju…9xj6"); + assert.equal(truncateNpub(` ${HEX_NPUB} `), "npub1a2d…yp60"); + // All-uppercase Bech32 is a valid identity per the parser; render the + // canonical form, never the neutral label. + assert.equal(truncateNpub(HEX_NPUB.toUpperCase()), "npub1a2d…yp60"); +}); + +test("truncateNpub renders the neutral label for invalid identities", () => { + // Never the raw hex/input fallback: a wrong-length or non-hex string is not + // a displayable identity. + assert.equal(truncateNpub(""), "Unavailable"); + assert.equal(truncateNpub("not a pubkey"), "Unavailable"); + assert.equal(truncateNpub(`${HEX.slice(0, 63)}`), "Unavailable"); + assert.equal(truncateNpub(`z${HEX.slice(1)}`), "Unavailable"); + // Corrupted npub checksum is not a valid identity either. + assert.equal(truncateNpub(`${HEX_NPUB.slice(0, -1)}q`), "Unavailable"); + // Other bech32 entities are not pubkeys. + assert.equal( + truncateNpub( + "nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5", + ), + "Unavailable", + ); +}); + +test("canonicalNpub returns the full npub for valid identities only", () => { + assert.equal(canonicalNpub(HEX), HEX_NPUB); + assert.equal(canonicalNpub(HEX.toUpperCase()), HEX_NPUB); + assert.equal(canonicalNpub(HEX_NPUB), HEX_NPUB); + // All-uppercase Bech32 is valid and returns the canonical lowercase npub + // (parser agreement); a mixed-case npub is invalid Bech32. + assert.equal(canonicalNpub(HEX_NPUB.toUpperCase()), HEX_NPUB); + assert.equal( + canonicalNpub( + `${HEX_NPUB.slice(0, 10)}${HEX_NPUB.slice(10).toUpperCase()}`, + ), + null, + ); + // Strict identity keys only — short/degenerate payloads never encode. + assert.equal(canonicalNpub(""), null); + assert.equal(canonicalNpub("deadbeef"), null); + assert.equal(canonicalNpub(`${HEX.slice(0, 63)}`), null); + // Checksum-valid short npubs are degenerate payloads too (8-char and + // empty) — `npubEncode` would happily re-encode them, so never bind them. + assert.equal(canonicalNpub("npub1m6kmamcvty5gd"), null); + assert.equal(canonicalNpub("npub106246s"), null); + // Corrupted checksum never binds as the identity it resembles. + assert.equal(canonicalNpub(`${HEX_NPUB.slice(0, -2)}qq`), null); +}); diff --git a/desktop/src/shared/lib/pubkey.ts b/desktop/src/shared/lib/pubkey.ts index 6dcc48749a3..199ad52d3c1 100644 --- a/desktop/src/shared/lib/pubkey.ts +++ b/desktop/src/shared/lib/pubkey.ts @@ -1,3 +1,7 @@ +import { decode, npubEncode } from "nostr-tools/nip19"; + +import { safeNpub } from "./nostrUtils"; + /** * Canonical pubkey normalisation. * @@ -8,14 +12,22 @@ export function normalizePubkey(pubkey: string): string { return pubkey.trim().toLowerCase(); } +/** Neutral identity label for keys that cannot be encoded for display. */ +export const UNAVAILABLE_KEY_LABEL = "Unavailable"; + +const HEX_64_REGEX = /^[0-9a-f]{64}$/; + /** - * The ONE canonical compact display form for a pubkey: `abcd1234…wxyz`. + * The ONE canonical compact display form for a hex string: `abcd1234…wxyz`. * * A truncated pubkey is a recognition aid, never an identity proof — vanity * grinders forge short prefixes cheaply. Surfaces where the user makes a * trust decision must show the full npub (see ``). * Do not hand-roll `pubkey.slice(…)` display forms; `check-pubkey-truncation` * fails the build if one sneaks in outside this module. + * + * Identity (pubkey) surfaces should use `truncateNpub` instead; this hex form + * remains canonical for non-identity identifiers — event and blob IDs. */ export function truncatePubkey(pubkey: string): string { if (pubkey.length <= 12) { @@ -23,3 +35,51 @@ export function truncatePubkey(pubkey: string): string { } return `${pubkey.slice(0, 8)}…${pubkey.slice(-4)}`; } + +/** + * Canonical full npub for an identity key: a 64-char hex pubkey (any + * case) or an already-npub string (checksum-validated) returns the + * canonical npub; anything else returns null. Strict 64-char identity keys + * only — `npubEncode` happily encodes short/degenerate payloads (even `""`), + * which are not displayable identities. + * + * Bech32 casing is strict on the input as written: a lowercase `npub1…` + * or an all-uppercase `NPUB1…` (both valid Bech32) returns the canonical + * lowercase npub, while a mixed-case npub is invalid Bech32 and returns + * null — `decode` enforces the all-lower/all-upper rule. This is + * intentionally stricter than the parser (`parsePubkeyInput`), which + * normalizes user input before decoding and so also accepts mixed-case + * npubs; the two agree that the payload must be a 64-hex identity key and + * that both valid casings above are acceptable input. + */ +export function canonicalNpub(pubkey: string): string | null { + const trimmed = pubkey.trim(); + if (trimmed.startsWith("npub1") || trimmed.startsWith("NPUB1")) { + try { + const decoded = decode(trimmed); + if (decoded.type !== "npub" || !HEX_64_REGEX.test(decoded.data)) { + return null; + } + return npubEncode(decoded.data); + } catch { + return null; + } + } + const normalized = normalizePubkey(trimmed); + return HEX_64_REGEX.test(normalized) ? safeNpub(normalized) : null; +} + +/** + * The ONE canonical compact identity display for a pubkey: `npub1abcd…wxyz` + * (first 8 + last 4 of the FULL npub). + * + * Identity surfaces render this form so a displayed prefix is always npub- + * shaped; the underlying hex never leaks as the identity display. A + * truncated key is a recognition aid, never an identity proof — trust + * decisions use `` or the full npub directly. Invalid + * keys render `UNAVAILABLE_KEY_LABEL`, never raw hex or raw input. + */ +export function truncateNpub(pubkey: string): string { + const npub = canonicalNpub(pubkey); + return npub === null ? UNAVAILABLE_KEY_LABEL : truncatePubkey(npub); +} diff --git a/desktop/src/shared/ui/PubKey.test.mjs b/desktop/src/shared/ui/PubKey.test.mjs new file mode 100644 index 00000000000..08167ae1c47 --- /dev/null +++ b/desktop/src/shared/ui/PubKey.test.mjs @@ -0,0 +1,118 @@ +/** + * Widget-boundary coverage for the shared identity gate. + * + * Codec vectors and the exact compact/neutral strings live in + * ../lib/pubkey.test.mjs. This suite pins what static rendering shows: the + * rendered text per variant, and that an unencodable identity — including + * degenerate-length hex and short-payload npubs whose npubEncode outputs + * carry valid checksums — renders the neutral label with no copy affordance, + * never a fake npub. The clipboard write behind the copy affordance and the + * popover the widget opens are real-bridge interactions owned by the E2E + * regressions: the full variant's copy is pinned by the new-DM recipient + * verification flow (tests/e2e/pubkey-display-screenshots.spec.ts) and the + * compact variant's by the agent-access owner hint + * (tests/e2e/agent-access-warning.spec.ts); both drive CopyRow through the + * mock bridge into the actual browser clipboard. + */ +import assert from "node:assert/strict"; +import { after, afterEach, before, test } from "node:test"; + +import { JSDOM } from "jsdom"; + +const dom = new JSDOM("", { + url: "http://localhost", +}); + +before(() => { + Object.assign(globalThis, { + document: dom.window.document, + getComputedStyle: dom.window.getComputedStyle.bind(dom.window), + HTMLElement: dom.window.HTMLElement, + IS_REACT_ACT_ENVIRONMENT: true, + Node: dom.window.Node, + ResizeObserver: class { + disconnect() {} + observe() {} + unobserve() {} + }, + window: dom.window, + }); +}); + +afterEach(async () => { + const { cleanup } = await import("@testing-library/react"); + cleanup(); +}); + +after(() => dom.window.close()); + +const HEX = "ea9b4d7a7a78a3e3729e5568b14d764d4962be0e1f20f749bcf8d9dbbf9a9328"; +const NPUB = "npub1a2d567n60z37xu57245tzntkf4yk90swrus0wjdulrvah0u6jv5qusyp60"; +const COMPACT_NPUB = "npub1a2d…yp60"; + +async function renderPubKey(props) { + const React = await import("react"); + const { render, within } = await import("@testing-library/react"); + const { PubKey } = await import("./PubKey.tsx"); + const view = render(React.createElement(PubKey, props)); + // render()'s bound queries search the whole body; scope to this render so + // earlier mounts (cleaned up only per test) stay invisible. + return { ...within(view.container), container: view.container }; +} + +test("compact PubKey renders the truncated npub, never the hex", async () => { + const trigger = await renderPubKey({ pubkey: HEX }); + assert.equal( + trigger.getByRole("button", { name: "Show full public key" }).textContent, + COMPACT_NPUB, + ); + assert.equal(trigger.queryByText(HEX), null); + + // A parent row that owns the interaction gets the same text, not a button. + const text = await renderPubKey({ interactive: false, pubkey: HEX }); + assert.equal(text.getByText(COMPACT_NPUB).tagName, "SPAN"); + assert.equal(text.queryByRole("button"), null); + assert.equal(text.queryByText(HEX), null); + + // An all-uppercase Bech32 npub is a valid identity (parsePubkeyInput + // accepts it); the gate must render its canonical compact form, not the + // neutral label. + const upper = await renderPubKey({ pubkey: NPUB.toUpperCase() }); + assert.equal( + upper.getByRole("button", { name: "Show full public key" }).textContent, + COMPACT_NPUB, + ); + assert.equal(upper.queryByText("Unavailable"), null); +}); + +test("full PubKey renders the complete npub with a copy affordance", async () => { + const view = await renderPubKey({ pubkey: HEX, variant: "full" }); + assert.equal(view.getByText(NPUB).textContent, NPUB); + assert.equal( + view.getByRole("button", { name: "Copy public key" }).tagName, + "BUTTON", + ); + assert.equal(view.queryByText(HEX), null); +}); + +test("unencodable keys render Unavailable with no copy affordance", async () => { + // "zz" cannot decode; "deadbeef" is a degenerate-length hex that npubEncode + // would happily turn into a checksum-valid fake npub; npub1m6kmamcvty5gd + // and npub106246s decode fine but are checksum-valid short-payload npubs + // (8-char and empty identity payloads). All four would masquerade as + // displayable identities — the gate refuses every one. + for (const pubkey of [ + "zz", + "deadbeef", + "npub1m6kmamcvty5gd", + "npub106246s", + ]) { + for (const variant of [undefined, "full"]) { + const view = await renderPubKey({ pubkey, variant }); + const label = `${pubkey} ${variant ?? "compact"}`; + assert.equal(view.getByText("Unavailable").tagName, "SPAN", label); + assert.equal(view.queryByRole("button"), null, label); + assert.equal(view.container.textContent?.includes("npub1"), false, label); + } + } +}); diff --git a/desktop/src/shared/ui/PubKey.tsx b/desktop/src/shared/ui/PubKey.tsx index fb5ac15a441..e8fe0bdead8 100644 --- a/desktop/src/shared/ui/PubKey.tsx +++ b/desktop/src/shared/ui/PubKey.tsx @@ -3,8 +3,11 @@ import * as React from "react"; import { copyTextToClipboard } from "@/shared/lib/clipboard"; import { cn } from "@/shared/lib/cn"; -import { safeNpub } from "@/shared/lib/nostrUtils"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { + canonicalNpub, + truncateNpub, + UNAVAILABLE_KEY_LABEL, +} from "@/shared/lib/pubkey"; import { Button } from "@/shared/ui/button"; import { DEFAULT_POPOVER_HOVER_OPEN_DELAY_MS, @@ -19,12 +22,12 @@ type PubKeyProps = { /** 64-char hex pubkey. */ pubkey: string; /** - * `compact` — truncated hex, click/tap opens a popover with the full npub, - * full hex, and copy buttons. The default for identity display in lists, - * cards, and metadata rows. + * `compact` — truncated npub (`npub1abcd…wxyz`), click/tap opens a popover + * with the full npub and its copy button. The default for identity display + * in lists, cards, and metadata rows. * - * `full` — the complete npub rendered inline with copy buttons. Required on - * security-decision surfaces (invite/approve, removal, trust/pairing, new + * `full` — the complete npub rendered inline with a copy button. Required + * on security-decision surfaces (invite/approve, removal, trust/pairing, new * DM, key import): a truncated key is forgeable by vanity grinding, so * decisions must be made against the whole key. */ @@ -66,12 +69,10 @@ function CopyRow({ label, value }: { label: string; value: string }) { ); } -function PubKeyDetails({ pubkey }: { pubkey: string }) { - const npub = safeNpub(pubkey); +function PubKeyDetails({ npub }: { npub: string }) { return (
- {npub ? : null} - +
); } @@ -119,29 +120,47 @@ export function PubKey({ React.useEffect(() => clearHoverTimer, [clearHoverTimer]); + // Strict identity gate: `safeNpub` would happily encode degenerate short + // payloads (e.g. an 8-char hex) as a fake npub, so the widget validates + // through `canonicalNpub` and renders Unavailable for anything else. + const npub = canonicalNpub(pubkey); + if (variant === "full") { - const npub = safeNpub(pubkey); return ( - {npub ?? pubkey} - - - - - - - - + + {npub ?? UNAVAILABLE_KEY_LABEL} + + {npub ? ( + + + + + + + + + ) : null} + + ); + } + + // An unencodable key has no key display to expand: render the neutral + // label without a popover or copy affordance. + if (npub === null) { + return ( + + {UNAVAILABLE_KEY_LABEL} ); } @@ -149,7 +168,7 @@ export function PubKey({ if (!interactive) { return ( - {truncatePubkey(pubkey)} + {truncateNpub(pubkey)} ); } @@ -168,7 +187,7 @@ export function PubKey({ onMouseLeave={handleMouseLeave} type="button" > - {truncatePubkey(pubkey)} + {truncateNpub(pubkey)} event.preventDefault()} > - + ); diff --git a/desktop/tests/e2e/agent-access-warning.spec.ts b/desktop/tests/e2e/agent-access-warning.spec.ts index adb9c6d58ab..55d5c4a0075 100644 --- a/desktop/tests/e2e/agent-access-warning.spec.ts +++ b/desktop/tests/e2e/agent-access-warning.spec.ts @@ -1,4 +1,5 @@ import { expect, test } from "@playwright/test"; +import { npubEncode } from "nostr-tools/nip19"; import { waitForAnimations } from "../helpers/animations"; import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge"; @@ -123,6 +124,31 @@ test("open agent access explains the available access before save", async ({ .getByRole("dialog", { name: "Manage agent access" }) .screenshot({ path: `${SHOTS}/selected-people-warning.png` }); + // Compact-variant clipboard regression (D1a): the owner hint's compact + // PubKey must expand to and copy the viewer's complete canonical npub — + // the truncated trigger is only a recognition aid. The real bridge writes + // the browser clipboard and the poll reads it back. + await page.context().grantPermissions(["clipboard-read", "clipboard-write"]); + await page + .getByTestId("agent-respond-to") + .getByRole("button", { name: "Show full public key" }) + .click(); + const copyNpubButton = page.getByRole("button", { name: "Copy npub" }); + await copyNpubButton.click(); + await expect + .poll(() => page.evaluate(() => navigator.clipboard.readText())) + .toBe(npubEncode("deadbeef".repeat(8))); + await expect( + page.locator("[data-sonner-toast]").filter({ hasText: "npub copied" }), + ).toBeVisible(); + // Dismiss just the key popover: the access dialog stays open for the + // remaining mode assertions below. + await page.keyboard.press("Escape"); + await expect(copyNpubButton).toHaveCount(0); + await expect( + page.getByRole("dialog", { name: "Manage agent access" }), + ).toBeVisible(); + // Only me shares nothing, so the warning goes away entirely. await accessSelect.selectOption("owner-only"); await expect(warning).toHaveCount(0); diff --git a/desktop/tests/e2e/identity-archive.spec.ts b/desktop/tests/e2e/identity-archive.spec.ts index dfbe2ff2690..cb6e83def1b 100644 --- a/desktop/tests/e2e/identity-archive.spec.ts +++ b/desktop/tests/e2e/identity-archive.spec.ts @@ -1,4 +1,5 @@ import { expect, test } from "@playwright/test"; +import { npubEncode } from "nostr-tools/nip19"; import { installMockBridge } from "../helpers/bridge"; @@ -33,7 +34,9 @@ async function openAliceProfile(page: import("@playwright/test").Page) { await aliceMessage.locator("button", { hasText: "alice" }).first().click(); const panel = page.getByTestId("user-profile-panel"); await expect(panel).toBeVisible(); - await expect(panel).toContainText(ALICE_PUBKEY.slice(0, 8)); + // The panel's public key row renders through the shared widget, + // which displays the canonical npub form — assert the npub prefix. + await expect(panel).toContainText(npubEncode(ALICE_PUBKEY).slice(0, 8)); } async function openProfileSettingsMenu(page: import("@playwright/test").Page) { diff --git a/desktop/tests/e2e/mentions.spec.ts b/desktop/tests/e2e/mentions.spec.ts index 6d9baa1e23b..e9248509fd1 100644 --- a/desktop/tests/e2e/mentions.spec.ts +++ b/desktop/tests/e2e/mentions.spec.ts @@ -1,4 +1,5 @@ import { expect, test } from "@playwright/test"; +import { npubEncode } from "nostr-tools/nip19"; import { waitForAnimations } from "../helpers/animations"; @@ -4534,7 +4535,9 @@ test("clicking author name opens user profile panel", async ({ page }) => { // Click now opens the full profile panel instead of the popover const panel = page.getByTestId("user-profile-panel"); await expect(panel).toBeVisible(); - await expect(panel).toContainText("deadbeef"); + // The panel's public key row renders through the shared widget, + // which displays the canonical npub form — assert the npub prefix. + await expect(panel).toContainText(npubEncode(MOCK_VIEWER_PUBKEY).slice(0, 8)); }); test("hovering avatar opens popover, clicking opens profile panel", async ({ diff --git a/desktop/tests/e2e/profile.spec.ts b/desktop/tests/e2e/profile.spec.ts index 095f0fe401e..e47d75706cb 100644 --- a/desktop/tests/e2e/profile.spec.ts +++ b/desktop/tests/e2e/profile.spec.ts @@ -1,4 +1,5 @@ import { expect, test, type Page } from "@playwright/test"; +import { npubEncode } from "nostr-tools/nip19"; import { createMockAgentMemoryListing, @@ -420,7 +421,7 @@ test("owned agent profile stays in parity between Agents and its DM", async ({ .getByRole("button", { name: `Open profile for ${agentName}` }) .click(); await expect(page.getByTestId("user-profile-public-key")).toContainText( - agentPubkey.slice(0, 8), + npubEncode(agentPubkey).slice(0, 8), ); const dmSurface = await readOwnedAgentProfileContract(page); diff --git a/desktop/tests/e2e/pubkey-display-screenshots.spec.ts b/desktop/tests/e2e/pubkey-display-screenshots.spec.ts index 77630467da1..81ff86c3299 100644 --- a/desktop/tests/e2e/pubkey-display-screenshots.spec.ts +++ b/desktop/tests/e2e/pubkey-display-screenshots.spec.ts @@ -1,4 +1,5 @@ import { expect, test } from "@playwright/test"; +import { npubEncode } from "nostr-tools/nip19"; import { installMockBridge, @@ -189,13 +190,68 @@ test("selected new-DM recipient can be verified again through search", async ({ await charlieNameTrigger.click(); await expect(charlieKeyPopover).toBeVisible(); await expect(charliePubkey).toContainText("npub1"); + // The shared PubKey widget is npub-only — no hex text or hex copy row in + // the widget itself. (D1a boundary: the chip's legacy raw-hex popover line + // is removed with the chip change in the descendant slice.) + await expect(charliePubkey).not.toContainText(TEST_IDENTITIES.charlie.pubkey); await expect(charlieKeyPopover).toContainText(TEST_IDENTITIES.charlie.pubkey); await waitForAnimations(page); await page.getByTestId("new-message-page").screenshot({ path: `${SHOTS}/new-dm-selected-recipient-key.png`, }); + + // Full-variant clipboard regression (D1a): the nested copy affordances + // must write the recipient's complete canonical npub through the real + // bridge — never the legacy raw hex the popover also shows, and never a + // truncation. The evidence screenshot above is captured first, so this + // interaction leaves it untouched. + await page.context().grantPermissions(["clipboard-read", "clipboard-write"]); + const copyPublicKeyTrigger = charliePubkey.getByRole("button", { + name: "Copy public key", + }); + await copyPublicKeyTrigger.click(); + const copyNpubButton = page.getByRole("button", { name: "Copy npub" }); + await copyNpubButton.click(); + await expect + .poll(() => page.evaluate(() => navigator.clipboard.readText())) + .toBe(npubEncode(TEST_IDENTITIES.charlie.pubkey)); + await expect( + page.locator("[data-sonner-toast]").filter({ hasText: "npub copied" }), + ).toBeVisible(); + // Copying is not a dismissal: the nested affordance and the inspection + // popover it lives in both survive the copy. + await expect(copyNpubButton).toBeVisible(); + await expect(charlieKeyPopover).toBeVisible(); + + // The inner Escape closes only the nested key popover — the inspection + // stays open. + await page.keyboard.press("Escape"); + await expect(copyNpubButton).toHaveCount(0); + await expect(charlieKeyPopover).toBeVisible(); + + // Keyboard path: after the inner Escape focus returns naturally to the + // full-key trigger; Space reopens the popover, whose auto-focus lands on + // Copy npub, and Enter activates it. The sentinel proves this keyboard + // copy rewrites the clipboard rather than inheriting the value above. + await expect(copyPublicKeyTrigger).toBeFocused(); + await page.evaluate(() => + navigator.clipboard.writeText("keyboard-copy-sentinel"), + ); + await page.keyboard.press("Space"); + await expect(copyNpubButton).toBeVisible(); + await expect(copyNpubButton).toBeFocused(); + await page.keyboard.press("Enter"); + await expect + .poll(() => page.evaluate(() => navigator.clipboard.readText())) + .toBe(npubEncode(TEST_IDENTITIES.charlie.pubkey)); + + // Close the reopened nested popover so the inspection popover owns the + // final Escape; the recipient itself survives both dismissals. + await page.keyboard.press("Escape"); + await expect(copyNpubButton).toHaveCount(0); await page.keyboard.press("Escape"); await expect(charlieKeyPopover).toHaveCount(0); + await expect(charlieChip).toBeVisible(); await search.fill("charlie"); await expect(charlieResult).toBeVisible(); @@ -226,6 +282,16 @@ test("selected new-DM recipient can be verified again through search", async ({ await page.getByTestId("new-message-page").screenshot({ path: `${SHOTS}/new-dm-selected-recipient.png`, }); + + // The To-field guard ignores popover clicks that bubble into the field; + // a click on the label itself — a physical descendant of the field — must + // still focus the input and open the recipient picker. + await page + .getByTestId("new-message-to-field") + .getByText("To:", { exact: true }) + .click(); + await expect(search).toBeFocused(); + await expect(page.getByTestId("new-message-recipient-popover")).toBeVisible(); }); test("member removal confirm shows the full npub inline", async ({ page }) => { From ad2a84131ee99d087b8dba95cff0f319785209df Mon Sep 17 00:00:00 2001 From: Logan Johnson Date: Wed, 9 Sep 2026 14:07:24 -0400 Subject: [PATCH 09/26] fix(desktop): npub identity displays for mention, member, and workflow surfaces (#7495) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 ## Summary Every Buzz account is identified by a long public key. Before this change, when someone had no display name, surfaces fell back to inconsistent labels — mostly raw hex fragments like `abcd1234…wxyz`, sometimes a generic role label with no key — so the same person looked different from surface to surface, and nothing looked like an npub address. This PR applies the npub identity foundation from #7488 to the everyday surfaces: a person without a display name now falls back to the same compact npub everywhere — `npub1xxxx…yyyy`, the human-readable spelling of their public key (first 8 + last 4 characters of the full npub) — across messages and mentions, reactions, huddles, member and participant lists, the sidebar and channel activity, search, projects, tray, notifications, and workflow surfaces. - **Mentions and messages**: key-only mention chips render the compact npub. Pasting a copied mention back still re-binds it byte-exactly to the identity it declares, for both the new npub chips and legacy hex-truncated chips copied by older clients — wrong, missing, or tampered key qualification is rejected instead of silently degrading to plain text. - **Reactions and huddles**: huddle reaction events and the huddle roster/participants render the compact npub for unnamed participants; workflow reaction triggers describe authors with the same form. - **Members and sidebar**: channel and community member lists, add-member results and invites, the members sidebar, the channel-activity popover, search, projects (assignees/reviewers/PR panels), the tray menu, and desktop notifications all fall back to the compact npub; titles and aria labels keep the machine-readable full labels. - **Profile labels**: panel/popover display names and owner handles fall back to the compact npub (never raw hex) when there is no name; linked-event (nevent) message metadata shows the npub-shaped author fallback while the event lookup and event IDs are unchanged. - **Workflows**: author-picker secondary labels, step destination keys, and trigger-author references render compact npubs; event and blob IDs keep their existing hex compacts (they are not identities). - **Avatars stay distinct**: fallback avatars for key-only identities derive initials from the key's tail, so prefixed role labels like "Participant npub1…" no longer collapse every unnamed participant onto the same initials; people with names keep their name initials. Preserved exactly: display names and distinct avatars, internal hex keys (storage/API forms unchanged), clipboard identity roundtrips, event/blob ID compaction, private keys (no nsec path is touched), and nevent link handling. Scope: this PR changes what identity labels **display**, not identity controls — profile/settings copy controls, the respond-to allowlist, workflow key fields, and agent dialogs are the sibling slice #7489, and the shared primitives (`canonicalNpub`, `truncateNpub`, the `` gate, strict input parsing) come from the foundation #7488. ### Related issue - Fixes: N/A. Searched existing issues/PRs for duplicates — none found; the related work is the npub identity stack this slice belongs to. - Base/dependency: stacks on #7488 (foundation) — this PR does not stand alone on main. - #7489 is a sibling slice on the same #7488 base (profile/agent/workflow controls), not a dependency: this PR does not require #7489, and #7489 does not require this PR — both only require #7488. ### Testing At exact head `4763cbeae1dd521309755e6d61f657324cb98667` (base: `fix/desktop-npub-identity-d1a` @ `5f3a4a8111998c8aa41ad77cf66992bd1c85343c`; 71 files, +656/−189 — production +277/−136, test support +379/−53): - At this head: targeted `mentions.spec.ts` (1/1), the e2e build, typecheck, and biome — green. - 9 changed/related unit files: 100/100 green; typecheck, e2e build, biome, and px text/truncation checks clean; huddle-roster focused run green; channel-activity e2e 11/11; mutation checks confirm the fallback wiring (removing it collapses shared initials and drops fallback rows). - Known pre-existing local e2e failures, unchanged by this PR and reproduced identically at the upstream merge-base: huddle-transcription voice-menu attribution (25 pass / 1 fail) and the `workflow-local-controls` 438px caret drift. Not claimed green locally. - Update at head `236af9e6137386737e84d3a474d6bc808a704c50` (test-only follow-ups `1143af345` + `236af9e6`): the `workflow-local-controls` races were fixed in the test drivers, and the 438px diff was shown to be a stale Darwin snapshot baseline (name-row enable switch already absent and `message_posted` already MessageSquare at recording commit `9390e11c9`) and refreshed — the focused screenshot test, including keyboard/caret assertions, now passes locally (twice). The full spec was not rerun after the snapshot refresh; the huddle-transcription item above is unchanged. Label/copy text changes are asserted by the e2e specs (`mentions`, `mention-recipients`, `pubkey-display-screenshots`, `huddle-transcription`, `channel-activity-popover`, `workflow-local-controls`) rather than new screenshots; the screenshot spec pins the compact npub text forms. ### Task provenance Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9` Task: buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6 --------- Signed-off-by: Logan Johnson Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> --- desktop/src/app/useTrayMenu.ts | 4 +- .../agents/ui/PersonaShareRecipients.tsx | 4 +- .../src/features/channels/lib/memberUtils.ts | 4 +- .../channels/ui/AddMemberSearchResultRow.tsx | 6 +- .../channels/ui/ChannelMemberInviteCard.tsx | 10 ++-- .../features/channels/ui/MembersSidebar.tsx | 6 +- .../channels/ui/MembersSidebarMemberCard.tsx | 4 +- .../channels/ui/useChannelAgentSessions.ts | 4 +- .../community-members/ui/AddMemberDialog.tsx | 4 +- .../ui/CommunityMembersCard.tsx | 4 +- .../ui/CommunityMembersSettingsCard.tsx | 10 +++- .../ui/ConfirmRemoveDialog.tsx | 4 +- .../useCommunityJoinAlerts.test.mjs | 7 ++- .../forum/ui/ForumComposer.lifecycle.test.mjs | 3 + .../forum/ui/useForumMentionPreparation.ts | 4 +- .../features/home/ui/RecentNotesSection.tsx | 5 +- .../features/huddle/components/HuddleBar.tsx | 4 +- .../huddle/components/ParticipantList.tsx | 17 ++++-- .../features/huddle/lib/huddleChannelName.ts | 4 +- .../messages/lib/formatTimelineMessages.ts | 4 +- .../messages/lib/mentionCandidates.ts | 4 +- .../messages/lib/mentionClipboard.test.mjs | 34 ++++++++--- .../features/messages/lib/mentionClipboard.ts | 16 ++++- .../features/messages/lib/mentionRanking.ts | 4 +- .../lib/normalizeMentionClipboard.test.mjs | 12 ++++ .../messages/lib/threadPanel.test.mjs | 13 +++- .../src/features/messages/lib/threadPanel.ts | 9 ++- .../messages/lib/timelineMentionCopy.test.mjs | 27 +++++---- .../messages/ui/MentionAutocomplete.tsx | 4 +- .../messages/ui/MessageAgentAddressPrefix.tsx | 4 +- .../messages/ui/NewMessageResultRow.tsx | 4 +- .../messages/ui/TypingIndicatorRow.tsx | 4 +- .../messages/ui/useAgentAddressLockPicker.ts | 6 +- .../ui/useMentionSendFlow.test-support.mjs | 3 + .../messages/ui/useMentionSendFlow.ts | 4 +- .../messages/ui/useNewMessageRecipients.ts | 4 +- .../use-feed-desktop-notifications.ts | 4 +- .../features/profile/lib/identity.test.mjs | 52 +++++++++++++++- desktop/src/features/profile/lib/identity.ts | 8 +-- .../src/features/profile/ui/ProfileAvatar.tsx | 14 ++++- .../features/profile/ui/UserProfilePanel.tsx | 6 +- .../profile/ui/UserProfilePanelUtils.ts | 8 +-- .../profile/ui/UserProfilePopover.tsx | 6 +- .../ui/useProfileInteractionActions.ts | 4 +- .../projects/ui/IssueAssigneesRow.tsx | 8 +-- .../projects/ui/ProjectDetailFeedPanels.tsx | 4 +- .../ui/ProjectPullRequestInlineComments.tsx | 4 +- .../projects/ui/ProjectPullRequestsPanel.tsx | 4 +- .../projects/ui/PullRequestReviewersRow.tsx | 8 +-- .../features/pulse/ui/AgentActivityCard.tsx | 4 +- desktop/src/features/pulse/ui/NoteCard.tsx | 6 +- desktop/src/features/pulse/ui/PulseView.tsx | 4 +- .../src/features/search/ui/TopbarSearch.tsx | 4 +- .../settings/ui/ModerationQueueCard.tsx | 19 +++--- .../sidebar/ui/ChannelActivityPopover.tsx | 22 +++++-- .../workflows/ui/WorkflowAuthorPicker.tsx | 4 +- .../ui/workflowStepDescription.test.mjs | 33 +++++++++++ .../workflows/ui/workflowStepDescription.ts | 12 +++- .../ui/workflowTriggerDescription.test.mjs | 33 +++++++++++ .../ui/workflowTriggerDescription.ts | 8 ++- desktop/src/shared/lib/initials.test.mjs | 43 ++++++++++++++ desktop/src/shared/lib/initials.ts | 45 ++++++++++++++ .../src/shared/lib/mentionDisplay.test.mjs | 31 ++++++++-- desktop/src/shared/lib/mentionDisplay.ts | 33 +++++++++-- desktop/src/shared/ui/UserAvatar.tsx | 14 ++++- .../ui/markdown/useMessageLinkMetadata.ts | 4 +- .../shared/ui/markdownMentionDisplay.test.mjs | 4 +- .../e2e/channel-activity-popover.spec.ts | 56 ++++++++++++++++++ desktop/tests/e2e/channels.spec.ts | 40 ++++++++++++- .../tests/e2e/huddle-transcription.spec.ts | 55 ++++++++++++++++- desktop/tests/e2e/mention-recipients.spec.ts | 8 +-- desktop/tests/e2e/mentions.spec.ts | 9 ++- .../e2e/pubkey-display-screenshots.spec.ts | 9 +-- .../tests/e2e/workflow-local-controls.spec.ts | 20 ++++--- ...ate-variable-autocomplete-smoke-darwin.png | Bin 104623 -> 103906 bytes 75 files changed, 716 insertions(+), 198 deletions(-) diff --git a/desktop/src/app/useTrayMenu.ts b/desktop/src/app/useTrayMenu.ts index 355c8e5d4f2..04ce9256a30 100644 --- a/desktop/src/app/useTrayMenu.ts +++ b/desktop/src/app/useTrayMenu.ts @@ -10,7 +10,7 @@ import { useManagedAgentsQuery, useRelayAgentsQuery, } from "@/features/agents/hooks"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { useNow } from "@/shared/lib/useNow"; import { formatElapsed } from "@/features/agents/ui/agentSessionUtils"; import type { Channel } from "@/shared/api/types"; @@ -72,7 +72,7 @@ export function useTrayMenu({ activityId: `${channelTurn.channelId}:${normalizePubkey(pubkey)}`, agentName: agentNames.get(normalizePubkey(pubkey)) ?? - `Agent ${truncatePubkey(pubkey)}`, + `Agent ${truncateNpub(pubkey)}`, channelId: channelTurn.channelId, channelName: channelNames.get(channelTurn.channelId) ?? "Unknown channel", diff --git a/desktop/src/features/agents/ui/PersonaShareRecipients.tsx b/desktop/src/features/agents/ui/PersonaShareRecipients.tsx index 7db7c32bfa5..89588b57567 100644 --- a/desktop/src/features/agents/ui/PersonaShareRecipients.tsx +++ b/desktop/src/features/agents/ui/PersonaShareRecipients.tsx @@ -15,7 +15,7 @@ import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar"; import { SelectedRecipientChip } from "@/features/profile/ui/SelectedRecipientChip"; import { useIdentityQuery } from "@/shared/api/hooks"; import type { UserSearchResult } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { Popover, PopoverAnchor, PopoverContent } from "@/shared/ui/popover"; import { Skeleton } from "@/shared/ui/skeleton"; @@ -25,7 +25,7 @@ export function formatShareRecipientName(user: UserSearchResult) { return ( user.displayName?.trim() || user.nip05Handle?.trim() || - truncatePubkey(user.pubkey) + truncateNpub(user.pubkey) ); } diff --git a/desktop/src/features/channels/lib/memberUtils.ts b/desktop/src/features/channels/lib/memberUtils.ts index d921457e000..10ad4b7ff0a 100644 --- a/desktop/src/features/channels/lib/memberUtils.ts +++ b/desktop/src/features/channels/lib/memberUtils.ts @@ -1,5 +1,5 @@ import type { ChannelMember } from "@/shared/api/types"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; export const roleOrder: Record = { owner: 0, @@ -17,7 +17,7 @@ export function formatMemberName( return "You"; } - return member.displayName ?? truncatePubkey(member.pubkey); + return member.displayName ?? truncateNpub(member.pubkey); } export function compareMembersByRole( diff --git a/desktop/src/features/channels/ui/AddMemberSearchResultRow.tsx b/desktop/src/features/channels/ui/AddMemberSearchResultRow.tsx index 7edeb842c8b..9e7dda9c7fe 100644 --- a/desktop/src/features/channels/ui/AddMemberSearchResultRow.tsx +++ b/desktop/src/features/channels/ui/AddMemberSearchResultRow.tsx @@ -3,7 +3,7 @@ import { Bot } from "lucide-react"; import type { UserSearchResult } from "@/shared/api/types"; import { Button } from "@/shared/ui/button"; import { cn } from "@/shared/lib/cn"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { UserAvatar } from "@/shared/ui/UserAvatar"; const MEMBER_ROW_INSET_DIVIDER_CLASS = @@ -13,7 +13,7 @@ export function formatAddCandidateName(user: UserSearchResult) { return ( user.displayName?.trim() || user.nip05Handle?.trim() || - truncatePubkey(user.pubkey) + truncateNpub(user.pubkey) ); } @@ -67,7 +67,7 @@ export function AddMemberSearchResultRow({ />
- {truncatePubkey(user.pubkey)} + {truncateNpub(user.pubkey)} {ownerLabel ? ( diff --git a/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx b/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx index 90fb5bfaa9b..335e379ce8e 100644 --- a/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx +++ b/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx @@ -2,7 +2,7 @@ import { Search, UserPlus, X } from "lucide-react"; import * as React from "react"; import { parsePubkeyInput } from "@/shared/lib/nostrUtils"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { PubKey } from "@/shared/ui/PubKey"; import { useIsArchivedPredicate } from "@/features/identity-archive/hooks"; import { useUserSearchQuery } from "@/features/profile/hooks"; @@ -19,7 +19,7 @@ function formatSearchUserName(user: UserSearchResult) { return ( user.displayName?.trim() || user.nip05Handle?.trim() || - truncatePubkey(user.pubkey) + truncateNpub(user.pubkey) ); } @@ -270,11 +270,11 @@ export function ChannelMemberInviteCard({

- {truncatePubkey(directInvitee.pubkey)} + {truncateNpub(directInvitee.pubkey)}

by public key @@ -370,7 +370,7 @@ export function ChannelMemberInviteCard({
{submissionErrors.map((error) => (

- {truncatePubkey(error.pubkey)}: {error.error} + {truncateNpub(error.pubkey)}: {error.error}

))}
diff --git a/desktop/src/features/channels/ui/MembersSidebar.tsx b/desktop/src/features/channels/ui/MembersSidebar.tsx index 1b0b7687c1b..3c9afbfaeca 100644 --- a/desktop/src/features/channels/ui/MembersSidebar.tsx +++ b/desktop/src/features/channels/ui/MembersSidebar.tsx @@ -49,7 +49,7 @@ import { } from "@/shared/ui/dialog"; import { useProfilePanel } from "@/shared/context/ProfilePanelContext"; import { useFeedbackToasts } from "@/shared/hooks/useToastEffect"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { MODAL_SEARCH_INPUT_CLASS, MODAL_SEARCH_SHELL_CLASS, @@ -641,7 +641,7 @@ export function MembersSidebar({ managedAgentRuntime={managedAgentRuntime} member={member} memberIsBot={memberIsBot} - memberAvatarLabel={member.displayName ?? truncatePubkey(member.pubkey)} + memberAvatarLabel={member.displayName ?? truncateNpub(member.pubkey)} memberLabel={formatMemberName(member, currentPubkey)} moderationState={moderationStateByPubkey.get( normalizePubkey(member.pubkey), @@ -886,7 +886,7 @@ export function MembersSidebar({
{inviteSubmissionErrors.map((error) => (

- {truncatePubkey(error.pubkey)}: {error.error} + {truncateNpub(error.pubkey)}: {error.error}

))}
diff --git a/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx b/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx index 9f187388645..6cc3da1d9c2 100644 --- a/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx +++ b/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx @@ -27,7 +27,7 @@ import { MANAGED_AGENT_PAIR_ACTION_LABELS, type ManagedAgentPairAction, } from "@/features/agents/managedAgentRuntimeStatus"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import type { ChannelMember, ManagedAgent, @@ -195,7 +195,7 @@ export function MembersSidebarMemberCard({
- {truncatePubkey(member.pubkey)} + {truncateNpub(member.pubkey)} diff --git a/desktop/src/features/channels/ui/useChannelAgentSessions.ts b/desktop/src/features/channels/ui/useChannelAgentSessions.ts index e12c1bc7768..d5cd1824e0d 100644 --- a/desktop/src/features/channels/ui/useChannelAgentSessions.ts +++ b/desktop/src/features/channels/ui/useChannelAgentSessions.ts @@ -8,7 +8,7 @@ import type { RelayAgent, } from "@/shared/api/types"; import { usePanelReturnTarget } from "@/shared/hooks/usePanelReturnTarget"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { channelBotMemberPubkeySet, channelMemberPubkeySet, @@ -100,7 +100,7 @@ export function buildChannelAgentSessionCandidates({ byPubkey.set(key, { pubkey: member.pubkey, - name: member.displayName ?? truncatePubkey(member.pubkey), + name: member.displayName ?? truncateNpub(member.pubkey), status: "deployed", agentSource: "member-bot", canInterruptTurn: false, diff --git a/desktop/src/features/community-members/ui/AddMemberDialog.tsx b/desktop/src/features/community-members/ui/AddMemberDialog.tsx index 5464111752f..c2a26b5df3b 100644 --- a/desktop/src/features/community-members/ui/AddMemberDialog.tsx +++ b/desktop/src/features/community-members/ui/AddMemberDialog.tsx @@ -13,7 +13,7 @@ import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar"; import { SelectedRecipientChip } from "@/features/profile/ui/SelectedRecipientChip"; import type { RelayMemberRole, UserSearchResult } from "@/shared/api/types"; import { parsePubkeyInput } from "@/shared/lib/nostrUtils"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { Button } from "@/shared/ui/button"; import { Dialog, @@ -50,7 +50,7 @@ function formatSearchUserName(user: UserSearchResult) { return ( user.displayName?.trim() || user.nip05Handle?.trim() || - truncatePubkey(user.pubkey) + truncateNpub(user.pubkey) ); } diff --git a/desktop/src/features/community-members/ui/CommunityMembersCard.tsx b/desktop/src/features/community-members/ui/CommunityMembersCard.tsx index fdc75f3d183..7693212711b 100644 --- a/desktop/src/features/community-members/ui/CommunityMembersCard.tsx +++ b/desktop/src/features/community-members/ui/CommunityMembersCard.tsx @@ -3,7 +3,7 @@ import { MoreHorizontal, Plus, Shield, ShieldCheck, User } from "lucide-react"; import { toast } from "sonner"; import { useUsersBatchQuery } from "@/features/profile/hooks"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { PubKey } from "@/shared/ui/PubKey"; import { useChangeRelayMemberRoleMutation, @@ -101,7 +101,7 @@ function MemberRow({
- {displayName || truncatePubkey(member.pubkey)} + {displayName || truncateNpub(member.pubkey)} {isSelf ? ( diff --git a/desktop/src/features/community-members/ui/CommunityMembersSettingsCard.tsx b/desktop/src/features/community-members/ui/CommunityMembersSettingsCard.tsx index 29338ff5672..dad1f7ff6bc 100644 --- a/desktop/src/features/community-members/ui/CommunityMembersSettingsCard.tsx +++ b/desktop/src/features/community-members/ui/CommunityMembersSettingsCard.tsx @@ -19,7 +19,11 @@ import type { RelayMemberRole, UserProfileSummary, } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { + normalizePubkey, + truncateNpub, + UNAVAILABLE_KEY_LABEL, +} from "@/shared/lib/pubkey"; import { Button } from "@/shared/ui/button"; import { DropdownMenu, @@ -65,7 +69,7 @@ function HoverMemberIdentity({ displayName: string; pubkey: string; }) { - const npub = npubFromPubkey(pubkey) ?? pubkey; + const npub = npubFromPubkey(pubkey) ?? UNAVAILABLE_KEY_LABEL; return ( - {truncatePubkey(npub)} + {truncateNpub(npub)} ); diff --git a/desktop/src/features/community-members/ui/ConfirmRemoveDialog.tsx b/desktop/src/features/community-members/ui/ConfirmRemoveDialog.tsx index 64735a5d567..087ef952fff 100644 --- a/desktop/src/features/community-members/ui/ConfirmRemoveDialog.tsx +++ b/desktop/src/features/community-members/ui/ConfirmRemoveDialog.tsx @@ -1,6 +1,6 @@ import { toast } from "sonner"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { PubKey } from "@/shared/ui/PubKey"; import { useRemoveRelayMemberMutation } from "@/features/community-members/hooks"; import type { RelayMember } from "@/shared/api/types"; @@ -25,7 +25,7 @@ export function ConfirmRemoveDialog({ onOpenChange: (open: boolean) => void; }) { const removeMutation = useRemoveRelayMemberMutation(); - const label = displayName || (member ? truncatePubkey(member.pubkey) : ""); + const label = displayName || (member ? truncateNpub(member.pubkey) : ""); function handleOpenChange(next: boolean) { if (!next) { diff --git a/desktop/src/features/community-members/useCommunityJoinAlerts.test.mjs b/desktop/src/features/community-members/useCommunityJoinAlerts.test.mjs index 7b34b901619..52d7a0a114d 100644 --- a/desktop/src/features/community-members/useCommunityJoinAlerts.test.mjs +++ b/desktop/src/features/community-members/useCommunityJoinAlerts.test.mjs @@ -250,6 +250,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { useCommunityJoinAlerts } from "@/features/community-members/useCommunityJoinAlerts.ts"; import { joinAlertStorageKey } from "@/features/community-members/lib/joinAlerts.ts"; +import { truncateNpub } from "@/shared/lib/pubkey.ts"; import { relayClient } from "@/shared/api/relayClient.ts"; import { CommunitiesProvider } from "@/features/communities/useCommunities.tsx"; import { useCommunities } from "@/features/communities/useCommunities.tsx"; @@ -1715,7 +1716,7 @@ describe("useCommunityJoinAlerts — mounted subscription behaviour", () => { "a frame that predates the demotion must not re-open disclosure", ); assert.ok( - !notifications.some((entry) => entry.body?.includes(BOB.slice(0, 8))), + !notifications.some((entry) => entry.body?.includes(truncateNpub(BOB))), "the demoted viewer must never learn the new member's identity", ); assert.equal( @@ -1982,11 +1983,11 @@ describe("useCommunityJoinAlerts — mounted subscription behaviour", () => { "a community switch clears the latch: the feature recovers without a reload", ); assert.ok( - notifications.some((entry) => entry.body?.includes(BOB.slice(0, 8))), + notifications.some((entry) => entry.body?.includes(truncateNpub(BOB))), "the join suppressed by the latch is re-announced, not lost", ); assert.ok( - notifications.some((entry) => entry.body?.includes(CAROL.slice(0, 8))), + notifications.some((entry) => entry.body?.includes(truncateNpub(CAROL))), "and the new join lands too", ); diff --git a/desktop/src/features/forum/ui/ForumComposer.lifecycle.test.mjs b/desktop/src/features/forum/ui/ForumComposer.lifecycle.test.mjs index 65e2912ce70..e827635d145 100644 --- a/desktop/src/features/forum/ui/ForumComposer.lifecycle.test.mjs +++ b/desktop/src/features/forum/ui/ForumComposer.lifecycle.test.mjs @@ -86,6 +86,9 @@ async function setup(options = {}) { "@/shared/lib/pubkey": { normalizePubkey: (s) => s.toLowerCase(), truncatePubkey: (s) => s, + // Compact-identity seam stubbed alongside its sibling: this suite + // renders display names, never key-form labels. + truncateNpub: (s) => s, }, "@/features/channels/hooks": { useAddChannelMembersMutation: () => ({ diff --git a/desktop/src/features/forum/ui/useForumMentionPreparation.ts b/desktop/src/features/forum/ui/useForumMentionPreparation.ts index 09a7392a829..3289a5e8068 100644 --- a/desktop/src/features/forum/ui/useForumMentionPreparation.ts +++ b/desktop/src/features/forum/ui/useForumMentionPreparation.ts @@ -4,7 +4,7 @@ import { PRIVATE_CHANNEL_ADD_DENIED_MESSAGE } from "@/features/channels/lib/chan import { useCanAddChannelMembers } from "@/features/channels/useCanAddChannelMembers"; import type { UseMentionsResult } from "@/features/messages/lib/useMentions"; import type { ChannelType } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; type PendingInvite = { channelId: string; @@ -176,7 +176,7 @@ export function useForumMentionPreparation( isInvitePending: isInviting, names: (pending?.nonMemberPubkeys ?? []).map( (pubkey) => - mentions.getMentionDisplayName(pubkey) ?? truncatePubkey(pubkey), + mentions.getMentionDisplayName(pubkey) ?? truncateNpub(pubkey), ), onDismiss: dismiss, onInvite: () => void invite(), diff --git a/desktop/src/features/home/ui/RecentNotesSection.tsx b/desktop/src/features/home/ui/RecentNotesSection.tsx index e0837b219df..3bff5c015a7 100644 --- a/desktop/src/features/home/ui/RecentNotesSection.tsx +++ b/desktop/src/features/home/ui/RecentNotesSection.tsx @@ -4,7 +4,7 @@ import type { UserNote } from "@/shared/api/socialTypes"; import type { UserProfileSummary } from "@/shared/api/types"; import { Markdown } from "@/shared/ui/markdown"; import { UserAvatar } from "@/shared/ui/UserAvatar"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; type RecentNotesSectionProps = { notes: UserNote[]; @@ -52,8 +52,7 @@ export function RecentNotesSection({
{notes.slice(0, 5).map((note) => { const profile = profiles[note.pubkey.toLowerCase()]; - const displayName = - profile?.displayName ?? truncatePubkey(note.pubkey); + const displayName = profile?.displayName ?? truncateNpub(note.pubkey); const isAgent = agentPubkeys.has(note.pubkey); return ( diff --git a/desktop/src/features/huddle/components/HuddleBar.tsx b/desktop/src/features/huddle/components/HuddleBar.tsx index d5a0423cf7c..4c01ca68a15 100644 --- a/desktop/src/features/huddle/components/HuddleBar.tsx +++ b/desktop/src/features/huddle/components/HuddleBar.tsx @@ -33,7 +33,7 @@ import { AddAgentDialog, type AgentAddResult } from "./AddAgentDialog"; import type { HuddleAgentVoiceSettings } from "./AgentVoiceMenu"; import { MicControls, SpeakerControls } from "./MicControls"; import { HuddleParticipantsControl } from "./ParticipantList"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; // Mirrors HuddleState in src-tauri/src/huddle/mod.rs. type HuddleState = { @@ -98,7 +98,7 @@ function clampReactionName(name: string): string { } function fallbackNameForPubkey(pubkey?: string | null): string { - return pubkey ? `Participant ${truncatePubkey(pubkey)}` : "Someone"; + return pubkey ? `Participant ${truncateNpub(pubkey)}` : "Someone"; } function parseHuddleReactionEvent(event: RelayEvent) { diff --git a/desktop/src/features/huddle/components/ParticipantList.tsx b/desktop/src/features/huddle/components/ParticipantList.tsx index 04b6a7453bf..b7d8d5888e9 100644 --- a/desktop/src/features/huddle/components/ParticipantList.tsx +++ b/desktop/src/features/huddle/components/ParticipantList.tsx @@ -11,7 +11,7 @@ import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover"; import type { VoiceRegistryEntry } from "@/features/settings/ui/voiceSettingsLogic"; import { invokeTauri } from "@/shared/api/tauri"; import { cn } from "@/shared/lib/cn"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { Button } from "@/shared/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/shared/ui/popover"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip"; @@ -49,6 +49,13 @@ const MAX_VISIBLE_PARTICIPANTS = 9; type ParticipantIdentity = { avatarUrl: string | null; displayName: string; + /** + * Label the avatar derives initials from: the authored name when one + * exists, otherwise the unprefixed compact key. `displayName` is the + * visible "Participant npub1…" fallback, whose word initials would + * collapse every unnamed participant onto "PN"/"AN". + */ + initialsLabel: string; isActive: boolean; isAgent: boolean; pubkey: string; @@ -110,10 +117,10 @@ function buildParticipantIdentities({ const profile = profiles[normalizedPubkey]; const isAgent = agentSet.has(normalizedPubkey); const agent = agentNames.get(normalizedPubkey); + const authoredName = profile?.displayName?.trim() || agent?.name?.trim(); + const keyLabel = truncateNpub(pubkey); const displayName = - profile?.displayName?.trim() || - agent?.name?.trim() || - `${isAgent ? "Agent" : "Participant"} ${truncatePubkey(pubkey)}`; + authoredName || `${isAgent ? "Agent" : "Participant"} ${keyLabel}`; const speakerLevel = normalizedSpeakerLevels.get(normalizedPubkey) ?? (activeSpeakerSet.has(normalizedPubkey) ? 0.55 : 0); @@ -121,6 +128,7 @@ function buildParticipantIdentities({ return { avatarUrl: profile?.avatarUrl ?? agent?.avatarUrl ?? null, displayName, + initialsLabel: authoredName || keyLabel, isActive: activeSpeakerSet.has(normalizedPubkey) || speakerLevel > 0.04, isAgent, pubkey, @@ -453,6 +461,7 @@ function ParticipantAvatar({ diff --git a/desktop/src/features/huddle/lib/huddleChannelName.ts b/desktop/src/features/huddle/lib/huddleChannelName.ts index c9f6dff2835..bd01c0d3e8a 100644 --- a/desktop/src/features/huddle/lib/huddleChannelName.ts +++ b/desktop/src/features/huddle/lib/huddleChannelName.ts @@ -1,5 +1,5 @@ import type { Channel, ChannelMember } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; type BuildHuddleChannelNameInput = { channel: Channel; @@ -33,7 +33,7 @@ function channelParticipantLabel( return firstName(fallbackName); } - return truncatePubkey(pubkey); + return truncateNpub(pubkey); } export function buildHuddleChannelName({ diff --git a/desktop/src/features/messages/lib/formatTimelineMessages.ts b/desktop/src/features/messages/lib/formatTimelineMessages.ts index a24da67f700..b35e8140481 100644 --- a/desktop/src/features/messages/lib/formatTimelineMessages.ts +++ b/desktop/src/features/messages/lib/formatTimelineMessages.ts @@ -45,7 +45,7 @@ import { formatTime } from "@/features/messages/lib/dateFormatters"; // Pure overlay helper lives in a sibling .mjs so node:test (no TS loader) // can exercise the exact same source the renderer uses. import { applyEditTagOverlay } from "@/features/messages/lib/applyEditTagOverlay.mjs"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; const HEX_RE = /^[0-9a-f]+$/i; @@ -387,7 +387,7 @@ export function formatTimelineMessages( ? "You" : profile?.displayName?.trim() || profile?.nip05Handle?.trim() || - truncatePubkey(actorPubkey); + truncateNpub(actorPubkey); existing.users.push({ pubkey: actorPubkey, displayName, diff --git a/desktop/src/features/messages/lib/mentionCandidates.ts b/desktop/src/features/messages/lib/mentionCandidates.ts index 2ee46b01415..9c714015b24 100644 --- a/desktop/src/features/messages/lib/mentionCandidates.ts +++ b/desktop/src/features/messages/lib/mentionCandidates.ts @@ -5,7 +5,7 @@ import type { ChannelRole, UserSearchResult, } from "@/shared/api/types"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; export function formatSearchUserDisplayName(user: UserSearchResult) { return user.displayName?.trim() || user.nip05Handle?.trim() || null; @@ -54,7 +54,7 @@ export type MentionCandidate = { export function mentionCandidateLabel(candidate: MentionCandidate) { return ( candidate.displayName ?? - (candidate.pubkey ? truncatePubkey(candidate.pubkey) : "agent") + (candidate.pubkey ? truncateNpub(candidate.pubkey) : "agent") ); } diff --git a/desktop/src/features/messages/lib/mentionClipboard.test.mjs b/desktop/src/features/messages/lib/mentionClipboard.test.mjs index 39f16c96791..39db21be0c3 100644 --- a/desktop/src/features/messages/lib/mentionClipboard.test.mjs +++ b/desktop/src/features/messages/lib/mentionClipboard.test.mjs @@ -444,20 +444,38 @@ test("does not treat an uncapped label's ellipsis as a truncation", () => { test("whole compact mention labels are restored only for their declared exact key", () => { const key = `150b20bd${"a".repeat(52)}15dc`; const label = `Scout (${key}) 2`; - const compact = "Scout (150b20bd…15dc) 2"; - assert.equal(matchChipTextToLabel(compact, label, "@", key), "truncated"); + // The npub compact a chip renders today, and the hex compact a chip copied + // before keys displayed as npub still carries: a whole chip in either + // form re-binds; nothing else does. + const npubCompact = "Scout (npub1z59…zwkg) 2"; + const legacyCompact = "Scout (150b20bd…15dc) 2"; + + for (const compact of [npubCompact, legacyCompact]) { + assert.equal(matchChipTextToLabel(compact, label, "@", key), "truncated"); + assert.equal( + matchChipTextToLabel(`@${compact}`, label, "@", key), + "truncated", + ); + } + assert.equal(matchChipTextToLabel(npubCompact, label, "@"), "fragment"); + assert.equal( + matchChipTextToLabel(npubCompact, label, "@", "b".repeat(64)), + "fragment", + ); + assert.equal(matchChipTextToLabel(npubCompact, label, "#", key), "fragment"); + // A different key's compact — npub or legacy hex — is text this record + // never declared: tampered text never gains the identity's binding. assert.equal( - matchChipTextToLabel(`@${compact}`, label, "@", key), - "truncated", + matchChipTextToLabel("Scout (npub1m6k…zuz0) 2", label, "@", key), + "fragment", ); - assert.equal(matchChipTextToLabel(compact, label, "@"), "fragment"); assert.equal( - matchChipTextToLabel(compact, label, "@", "b".repeat(64)), + matchChipTextToLabel("Scout (deadbeef…beef) 2", label, "@", key), "fragment", ); - assert.equal(matchChipTextToLabel(compact, label, "#", key), "fragment"); + // A dropped collision suffix is a partial chip, not a tolerated form. assert.equal( - matchChipTextToLabel("Scout (150b20bd…15dc)", label, "@", key), + matchChipTextToLabel("Scout (npub1z59…zwkg)", label, "@", key), "fragment", ); assert.equal(matchChipTextToLabel("Scout", label, "@", key), "fragment"); diff --git a/desktop/src/features/messages/lib/mentionClipboard.ts b/desktop/src/features/messages/lib/mentionClipboard.ts index 8886bf8c29d..8b093610a49 100644 --- a/desktop/src/features/messages/lib/mentionClipboard.ts +++ b/desktop/src/features/messages/lib/mentionClipboard.ts @@ -1,4 +1,7 @@ -import { formatMentionDisplayLabel } from "@/shared/lib/mentionDisplay"; +import { + formatLegacyMentionDisplayLabel, + formatMentionDisplayLabel, +} from "@/shared/lib/mentionDisplay"; import { truncateInlineChipLabel } from "@/shared/ui/mentionChip"; import { getMentionOffsets } from "./hasMention"; @@ -96,6 +99,17 @@ export function matchChipTextToLabel( if (compact !== label && matches(canonicalMentionLabel(compact))) { return "truncated"; } + // A chip copied before keys displayed as npub carries the hex-truncated + // key in its text. Accept that prior *display* form — derived from the + // same label and pubkey this record declares, never from the pasted text + // itself — so an old whole-chip copy still re-binds instead of losing its + // identity. The two forms are disjoint (a hex truncation cannot contain + // the `n` an npub starts with), so this cannot reclassify any new chip. + const legacy = + sigil === "@" ? formatLegacyMentionDisplayLabel(label, pubkey) : label; + if (legacy !== label && matches(canonicalMentionLabel(legacy))) { + return "truncated"; + } return "fragment"; } diff --git a/desktop/src/features/messages/lib/mentionRanking.ts b/desktop/src/features/messages/lib/mentionRanking.ts index 3df5bba0b0a..deef2d3ebff 100644 --- a/desktop/src/features/messages/lib/mentionRanking.ts +++ b/desktop/src/features/messages/lib/mentionRanking.ts @@ -1,4 +1,4 @@ -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; export type MentionCandidateForRanking = { displayName: string | null; @@ -118,7 +118,7 @@ export function rankMentionCandidates( : ""; const label = candidate.displayName ?? - (candidate.pubkey ? truncatePubkey(candidate.pubkey) : "agent"); + (candidate.pubkey ? truncateNpub(candidate.pubkey) : "agent"); const groupRank = getMentionCandidateGroupRank( candidate, activePersonaIds, diff --git a/desktop/src/features/messages/lib/normalizeMentionClipboard.test.mjs b/desktop/src/features/messages/lib/normalizeMentionClipboard.test.mjs index 3e0563ab96c..a42a3a9aecb 100644 --- a/desktop/src/features/messages/lib/normalizeMentionClipboard.test.mjs +++ b/desktop/src/features/messages/lib/normalizeMentionClipboard.test.mjs @@ -272,10 +272,22 @@ test("compact mention paste expands only complete bound labels", () => { const html = (text) => `${text}`; + // Today's chip text renders the key as npub… + assert.equal( + normalizeMentionClipboardContent(html("Scout (npub1z59…zwkg) 2")).text, + `@${label}`, + ); + // …and a whole chip copied before that switch still carries the hex + // compact, which must keep expanding to the exact declared identity. assert.equal( normalizeMentionClipboardContent(html("Scout (150b20bd…15dc) 2")).text, `@${label}`, ); + // A dropped collision suffix — in either form — is a partial chip. + assert.equal( + normalizeMentionClipboardContent(html("Scout (npub1z59…zwkg)")).text, + "Scout (npub1z59…zwkg)", + ); assert.equal( normalizeMentionClipboardContent(html("Scout (150b20bd…15dc)")).text, "Scout (150b20bd…15dc)", diff --git a/desktop/src/features/messages/lib/threadPanel.test.mjs b/desktop/src/features/messages/lib/threadPanel.test.mjs index 8981d363803..630ebbc6fea 100644 --- a/desktop/src/features/messages/lib/threadPanel.test.mjs +++ b/desktop/src/features/messages/lib/threadPanel.test.mjs @@ -607,6 +607,13 @@ test("buildThreadPanelDataFromIndex matches direct panel data", () => { test("buildMainTimelineEntries renders a relay-only thread summary", () => { const root = message({ id: "root", createdAt: 1 }); + // Realistic 64-hex relay participant keys: a cold/relay-only summary has no + // client messages to derive labels from, so an unnamed participant must + // fall back to the compact npub (`truncateNpub`, the same form the + // client-assembled path derives via `resolveUserLabel`) — never the raw + // hex identity. `participant.author` is what `MessageThreadSummaryRow` + // binds to `UserAvatar`'s visible/accessible `displayName` label. + const bob = "deadbeef".repeat(8); // → npub1m6k…zuz0 const summaries = new Map([ [ "root", @@ -614,7 +621,7 @@ test("buildMainTimelineEntries renders a relay-only thread summary", () => { replyCount: 2, descendantCount: 4, lastReplyAt: 9, - participantPubkeys: ["alice", "bob"], + participantPubkeys: ["alice", bob], }, ], ]); @@ -631,10 +638,10 @@ test("buildMainTimelineEntries renders a relay-only thread summary", () => { threadHeadId: "root", replyCount: 4, lastReplyAt: 9, - // Relay returns participants most-recent-first (["alice", "bob"]); the + // Relay returns participants most-recent-first (["alice", bob]); the // facepile renders them oldest-first so the last replier lands rightmost. participants: [ - { id: "bob", author: "bob", avatarUrl: null }, + { id: bob, author: "npub1m6k…zuz0", avatarUrl: null }, { id: "alice", author: "Alice", avatarUrl: "alice.png" }, ], }); diff --git a/desktop/src/features/messages/lib/threadPanel.ts b/desktop/src/features/messages/lib/threadPanel.ts index 3bc5ee39125..b85b57d1ac9 100644 --- a/desktop/src/features/messages/lib/threadPanel.ts +++ b/desktop/src/features/messages/lib/threadPanel.ts @@ -2,6 +2,7 @@ import type { TimelineMessage } from "@/features/messages/types"; import type { ChannelWindowThreadSummary } from "@/features/messages/lib/channelWindowStore"; import type { UserProfileLookup } from "@/features/profile/lib/identity"; import { isBroadcastReply } from "@/features/messages/lib/threading"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { KIND_HUDDLE_STARTED } from "@/shared/constants/kinds"; type ThreadPanelData = { @@ -408,7 +409,13 @@ function buildRelayThreadSummary( .reverse() .map((pubkey) => ({ id: pubkey, - author: profiles?.[pubkey.toLowerCase()]?.displayName ?? pubkey, + // Unnamed participants fall back to the compact npub — the same label + // the client-assembled path derives via `resolveUserLabel` — so a + // cold/relay-only facepile never surfaces raw hex. This `author` is + // what `MessageThreadSummaryRow` binds to `UserAvatar`'s + // `displayName` (the visible/accessible avatar label). + author: + profiles?.[pubkey.toLowerCase()]?.displayName ?? truncateNpub(pubkey), avatarUrl: profiles?.[pubkey.toLowerCase()]?.avatarUrl ?? null, ...(profiles?.[pubkey.toLowerCase()]?.isAgent === true ? { isAgent: true } diff --git a/desktop/src/features/messages/lib/timelineMentionCopy.test.mjs b/desktop/src/features/messages/lib/timelineMentionCopy.test.mjs index 4f8c05842aa..ceada037c0d 100644 --- a/desktop/src/features/messages/lib/timelineMentionCopy.test.mjs +++ b/desktop/src/features/messages/lib/timelineMentionCopy.test.mjs @@ -190,15 +190,20 @@ test("copy inlines a blockified chip but preserves its block ancestor", () => { test("copy expands compact key text but preserves the exact label and identity", () => { const label = `Scout (${JOHN_SMITH_PUBKEY}) 2`; - const flavors = copyRenderedBody( - `` + - 'Scout (7c7c7c7c…7c7c) 2', - ); - assert.ok(flavors); - assert.ok(flavors.html.includes(`@${label}`)); - assert.ok( - flavors.html.includes(`data-mention-pubkey="${JOHN_SMITH_PUBKEY}"`), - ); - assert.ok(!flavors.html.includes("…")); + // Chips render the npub compact today; a chip copied before that switch + // still carries the hex compact. A whole chip in either form expands to + // the declared identity. + for (const compact of ["npub1037…08vj", "7c7c7c7c…7c7c"]) { + const flavors = copyRenderedBody( + `` + + `Scout (${compact}) 2`, + ); + assert.ok(flavors); + assert.ok(flavors.html.includes(`@${label}`)); + assert.ok( + flavors.html.includes(`data-mention-pubkey="${JOHN_SMITH_PUBKEY}"`), + ); + assert.ok(!flavors.html.includes("…")); + } }); diff --git a/desktop/src/features/messages/ui/MentionAutocomplete.tsx b/desktop/src/features/messages/ui/MentionAutocomplete.tsx index a380ac83a09..65ae331d594 100644 --- a/desktop/src/features/messages/ui/MentionAutocomplete.tsx +++ b/desktop/src/features/messages/ui/MentionAutocomplete.tsx @@ -15,7 +15,7 @@ import { Switch } from "@/shared/ui/switch"; import { Toggle } from "@/shared/ui/toggle"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip"; import { safeNpub } from "@/shared/lib/nostrUtils"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { getPlatformKeysById } from "@/shared/lib/keyboard-shortcuts"; export type MentionSuggestion = { @@ -387,7 +387,7 @@ export const MentionAutocomplete = React.memo(function MentionAutocomplete({ data-testid="mention-collision-npub" title={collisionNpub} > - {truncatePubkey(collisionNpub)} + {truncateNpub(collisionNpub)} ) : null} diff --git a/desktop/src/features/messages/ui/MessageAgentAddressPrefix.tsx b/desktop/src/features/messages/ui/MessageAgentAddressPrefix.tsx index e3ab0c6076a..644a8dc80d3 100644 --- a/desktop/src/features/messages/ui/MessageAgentAddressPrefix.tsx +++ b/desktop/src/features/messages/ui/MessageAgentAddressPrefix.tsx @@ -5,7 +5,7 @@ import { getVisibleAgentAddressPubkeys } from "../lib/getVisibleAgentAddressPubk import type { UserProfileLookup } from "@/features/profile/lib/identity"; import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { InlineChip } from "@/shared/ui/InlineChip"; /** Resolve all literal competitors before hiding tag-backed address chips. */ @@ -54,7 +54,7 @@ export function MessageAgentAddressPrefix({ const label = profile?.displayName?.trim() || profile?.name?.trim() || - truncatePubkey(pubkey); + truncateNpub(pubkey); return ( {/* biome-ignore lint/a11y/useValidAriaRole: UserProfilePopover uses role for agent classification, not as an ARIA attribute. */} diff --git a/desktop/src/features/messages/ui/NewMessageResultRow.tsx b/desktop/src/features/messages/ui/NewMessageResultRow.tsx index 9625fb7882e..ea07a0607a1 100644 --- a/desktop/src/features/messages/ui/NewMessageResultRow.tsx +++ b/desktop/src/features/messages/ui/NewMessageResultRow.tsx @@ -6,7 +6,7 @@ import type { UserProfileLookup } from "@/features/profile/lib/identity"; import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar"; import type { UserSearchResult } from "@/shared/api/types"; import { cn } from "@/shared/lib/cn"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { formatRecipientName } from "./useNewMessageRecipients"; @@ -28,7 +28,7 @@ function HoverRecipientIdentity({ displayName: string; pubkey: string; }) { - const identityLabel = truncatePubkey(pubkey); + const identityLabel = truncateNpub(pubkey); return ( {typingPubkeys.map((pubkey, index) => { const profile = profiles?.[pubkey.toLowerCase()]; - const label = labels[index] ?? truncatePubkey(pubkey); + const label = labels[index] ?? truncateNpub(pubkey); return (
key.toLowerCase(), truncatePubkey: (key) => key, + // Compact-identity seam stubbed alongside its sibling: these suites + // render display names, never key-form labels. + truncateNpub: (key) => key, }, "@/shared/lib/customEmojiTags": { buildCustomEmojiTags: () => [] }, "./useMentionSendFlow.helpers": helpers, diff --git a/desktop/src/features/messages/ui/useMentionSendFlow.ts b/desktop/src/features/messages/ui/useMentionSendFlow.ts index 9847193101b..299c0144ab4 100644 --- a/desktop/src/features/messages/ui/useMentionSendFlow.ts +++ b/desktop/src/features/messages/ui/useMentionSendFlow.ts @@ -28,7 +28,7 @@ import { useDetachedAgentStart } from "./useDetachedAgentStart"; import { useEnsureAgentMentionsReady } from "./useEnsureAgentMentionsReady"; import { invokeTauri } from "@/shared/api/tauri"; import type { AcpRuntime, ManagedAgent } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { buildCustomEmojiTags } from "@/shared/lib/customEmojiTags"; import { dedupeQueuedAgentWakes, @@ -941,7 +941,7 @@ export function useMentionSendFlow({ if (!pendingNonMemberSend) return []; return pendingNonMemberSend.nonMemberPubkeys.map( (pubkey) => - mentions.getMentionDisplayName(pubkey) ?? truncatePubkey(pubkey), + mentions.getMentionDisplayName(pubkey) ?? truncateNpub(pubkey), ); }, [mentions.getMentionDisplayName, pendingNonMemberSend]); const invitation = useNonMemberInvite({ diff --git a/desktop/src/features/messages/ui/useNewMessageRecipients.ts b/desktop/src/features/messages/ui/useNewMessageRecipients.ts index ffc45c6c9e2..c1b821da62b 100644 --- a/desktop/src/features/messages/ui/useNewMessageRecipients.ts +++ b/desktop/src/features/messages/ui/useNewMessageRecipients.ts @@ -20,7 +20,7 @@ import { import { rankUserCandidatesBySearch } from "@/features/profile/lib/userCandidateSearch"; import { useIdentityQuery } from "@/shared/api/hooks"; import type { ManagedAgent, UserSearchResult } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; /** Maximum recipients (excluding the current user) a DM can address. */ export const NEW_MESSAGE_RECIPIENT_LIMIT = 8; @@ -37,7 +37,7 @@ export function formatRecipientName(user: UserSearchResult) { return ( user.displayName?.trim() || user.nip05Handle?.trim() || - truncatePubkey(user.pubkey) + truncateNpub(user.pubkey) ); } diff --git a/desktop/src/features/notifications/use-feed-desktop-notifications.ts b/desktop/src/features/notifications/use-feed-desktop-notifications.ts index 4a0865437ed..e4594547eed 100644 --- a/desktop/src/features/notifications/use-feed-desktop-notifications.ts +++ b/desktop/src/features/notifications/use-feed-desktop-notifications.ts @@ -1,6 +1,6 @@ import * as React from "react"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { resolveUserLabel, type UserProfileLookup, @@ -204,7 +204,7 @@ export function useFeedDesktopNotifications( : undefined; // Only use real display names, not truncated pubkey fallbacks. const senderName = - resolvedLabel && resolvedLabel !== truncatePubkey(item.pubkey) + resolvedLabel && resolvedLabel !== truncateNpub(item.pubkey) ? resolvedLabel : undefined; void deliverFeedNotification(item, senderName); diff --git a/desktop/src/features/profile/lib/identity.test.mjs b/desktop/src/features/profile/lib/identity.test.mjs index da0259a66fc..089195213b3 100644 --- a/desktop/src/features/profile/lib/identity.test.mjs +++ b/desktop/src/features/profile/lib/identity.test.mjs @@ -1,11 +1,19 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { formatOwnerLabel, profileLookupsEqual } from "./identity.ts"; +import { + formatOwnerLabel, + profileLookupsEqual, + resolveUserLabel, +} from "./identity.ts"; const OWNER_PUBKEY = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; +// npubEncode(OWNER_PUBKEY), pinned so a fallback regression cannot pass by +// re-deriving the expectation from the code under test. +const OWNER_NPUB_COMPACT = "npub1hwa…04hu"; + const summary = (over = {}) => ({ displayName: "Ada", avatarUrl: "https://x/a.png", @@ -15,13 +23,28 @@ const summary = (over = {}) => ({ ...over, }); -test("formatOwnerLabel resolves a known owner's display name", () => { +test("formatOwnerLabel prefers the owner’s authored labels over the compact npub", () => { + // The label ladder: display name first, then a NIP-05 handle, then the + // key’s compact npub — never raw hex. assert.equal( formatOwnerLabel(OWNER_PUBKEY, null, { [OWNER_PUBKEY]: summary({ displayName: "baxen" }), }), "baxen", ); + assert.equal( + formatOwnerLabel(OWNER_PUBKEY, "c".repeat(64), { + [OWNER_PUBKEY]: summary({ + nip05Handle: "baxen@relay", + displayName: null, + }), + }), + "baxen@relay", + ); + assert.equal( + formatOwnerLabel(OWNER_PUBKEY, "c".repeat(64), {}), + OWNER_NPUB_COMPACT, + ); }); test("formatOwnerLabel calls the viewer-owned agent's owner you", () => { @@ -32,6 +55,31 @@ test("formatOwnerLabel returns null when verified ownership is absent", () => { assert.equal(formatOwnerLabel(null, OWNER_PUBKEY, {}), null); }); +test("resolveUserLabel falls back to the key’s compact npub, never raw hex", () => { + // No profile, no fallback name: the last resort is the npub compact. + assert.equal( + resolveUserLabel({ pubkey: OWNER_PUBKEY, profiles: {} }), + OWNER_NPUB_COMPACT, + ); + // A provided fallback name still wins over the key form. + assert.equal( + resolveUserLabel({ + pubkey: OWNER_PUBKEY, + profiles: {}, + fallbackName: "legacy relay agent", + }), + "legacy relay agent", + ); + // A resolved display name wins over everything. + assert.equal( + resolveUserLabel({ + pubkey: OWNER_PUBKEY, + profiles: { [OWNER_PUBKEY]: summary({ displayName: "baxen" }) }, + }), + "baxen", + ); +}); + test("profileLookupsEqual: same reference is equal", () => { const a = { p1: summary() }; assert.equal(profileLookupsEqual(a, a), true); diff --git a/desktop/src/features/profile/lib/identity.ts b/desktop/src/features/profile/lib/identity.ts index d2e0a4fdd38..a0d32faaaa4 100644 --- a/desktop/src/features/profile/lib/identity.ts +++ b/desktop/src/features/profile/lib/identity.ts @@ -1,9 +1,9 @@ import type { Profile, UserProfileSummary } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; export type UserProfileLookup = Record; -export { truncatePubkey }; +export { truncateNpub }; /** * Deep-equal two profile lookups by value. Used to stabilise the merged @@ -128,7 +128,7 @@ export function resolveUserLabel(input: { return safeFallback; } - return truncatePubkey(pubkey); + return truncateNpub(pubkey); } /** @@ -188,6 +188,6 @@ export function formatOwnerLabel( return ( owner?.displayName?.trim() || owner?.nip05Handle?.trim() || - truncatePubkey(ownerPubkey) + truncateNpub(ownerPubkey) ); } diff --git a/desktop/src/features/profile/ui/ProfileAvatar.tsx b/desktop/src/features/profile/ui/ProfileAvatar.tsx index debbc42803a..d8d22eaa76a 100644 --- a/desktop/src/features/profile/ui/ProfileAvatar.tsx +++ b/desktop/src/features/profile/ui/ProfileAvatar.tsx @@ -23,6 +23,17 @@ type ProfileAvatarProps = { avatarUrl: string | null; avatarDataUrl?: string | null; label: string; + /** + * Label used to derive fallback initials; defaults to `label`. + * + * `label` stays the full visible/alt identity, but some callers build it + * as a generated role-prefixed key fallback ("Agent npub1abcd…wxyz"), + * which `getInitials` reads as ordinary words — collapsing every unnamed + * identity onto the same "AN"/"PN" initials. Identity-aware callers pass + * the unprefixed compact key here so key-fallback avatars keep distinct + * key-tail initials; authored display names keep their name initials. + */ + initialsLabel?: string; className?: string; iconClassName?: string; imageClassName?: string; @@ -46,6 +57,7 @@ export function ProfileAvatar({ avatarUrl, avatarDataUrl, label, + initialsLabel, className, iconClassName, imageClassName, @@ -54,7 +66,7 @@ export function ProfileAvatar({ testId, untrusted = false, }: ProfileAvatarProps) { - const initials = getInitials(label); + const initials = getInitials(initialsLabel ?? label); const presentation = useAvatarPresentation(avatarUrl); const presentedAvatarUrl = presentation?.displayUrl ?? avatarUrl; diff --git a/desktop/src/features/profile/ui/UserProfilePanel.tsx b/desktop/src/features/profile/ui/UserProfilePanel.tsx index d060deb5323..bad18369fe3 100644 --- a/desktop/src/features/profile/ui/UserProfilePanel.tsx +++ b/desktop/src/features/profile/ui/UserProfilePanel.tsx @@ -77,7 +77,7 @@ import { resolveAgentInstruction, resolvePanelProfile, resolveProfileDisplayName, - truncatePubkey, + truncateNpub, type UserProfilePanelProps, useRetainedPersona, } from "@/features/profile/ui/UserProfilePanelUtils"; @@ -675,7 +675,7 @@ export function UserProfilePanel({ return ( ownerProfile?.nip05Handle?.trim() || ownerProfile?.displayName?.trim() || - truncatePubkey(ownerPubkey) + truncateNpub(ownerPubkey) ); } @@ -687,7 +687,7 @@ export function UserProfilePanel({ return ( currentProfile?.nip05Handle?.trim() || currentProfile?.displayName?.trim() || - truncatePubkey(currentPubkey) + truncateNpub(currentPubkey) ); }, [ currentProfileQuery.data, diff --git a/desktop/src/features/profile/ui/UserProfilePanelUtils.ts b/desktop/src/features/profile/ui/UserProfilePanelUtils.ts index be1a57c112e..e7d5a4d4ef8 100644 --- a/desktop/src/features/profile/ui/UserProfilePanelUtils.ts +++ b/desktop/src/features/profile/ui/UserProfilePanelUtils.ts @@ -8,9 +8,9 @@ import type { RelayAgent, UpdateManagedAgentInput, } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; -export { truncatePubkey }; +export { truncateNpub }; export type ProfileChannelLink = { id: string; @@ -237,7 +237,7 @@ export function resolveProfileDisplayName({ return ( profile?.displayName ?? persona?.displayName ?? - (pubkey ? truncatePubkey(pubkey) : "Agent") + (pubkey ? truncateNpub(pubkey) : "Agent") ); } @@ -252,7 +252,7 @@ export function resolveOwnerHandle( return ( profile?.nip05Handle?.trim() || profile?.displayName?.trim() || - truncatePubkey(currentPubkey) + truncateNpub(currentPubkey) ); } diff --git a/desktop/src/features/profile/ui/UserProfilePopover.tsx b/desktop/src/features/profile/ui/UserProfilePopover.tsx index 3fab7b8102e..c36b59e3af6 100644 --- a/desktop/src/features/profile/ui/UserProfilePopover.tsx +++ b/desktop/src/features/profile/ui/UserProfilePopover.tsx @@ -30,7 +30,7 @@ import { ProfileAvatarWithStatus } from "@/features/profile/ui/ProfileAvatarWith import { useOpenAgentActivity } from "@/features/agents/useOpenAgentActivity"; import { useProfilePanel } from "@/shared/context/ProfilePanelContext"; import { cn } from "@/shared/lib/cn"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { useProfileInteractionActions } from "@/features/profile/ui/useProfileInteractionActions"; import { @@ -108,7 +108,7 @@ function HoverPubkeyName({ - {truncatePubkey(pubkey)} + {truncateNpub(pubkey)} ); @@ -297,7 +297,7 @@ function UserProfilePopoverBody({ relayAgentsQuery.isPending || managedAgentsQuery.isPending || usersBatchQuery.isPending); - const displayName = profile?.displayName ?? truncatePubkey(pubkey); + const displayName = profile?.displayName ?? truncateNpub(pubkey); // Owner signal mirrors UserProfilePanel: a declared NIP-OA owner whose agent // runs elsewhere holds no local seckey, so key custody (`isOwner`) alone // wrongly hides the affordance from them — and gating on bot-ness alone shows diff --git a/desktop/src/features/profile/ui/useProfileInteractionActions.ts b/desktop/src/features/profile/ui/useProfileInteractionActions.ts index f5ee47a7702..1993188f319 100644 --- a/desktop/src/features/profile/ui/useProfileInteractionActions.ts +++ b/desktop/src/features/profile/ui/useProfileInteractionActions.ts @@ -21,7 +21,7 @@ import { useIdentityQuery } from "@/shared/api/hooks"; import { sendChannelMessage } from "@/shared/api/tauri"; import type { Channel, RelayEvent } from "@/shared/api/types"; import { KIND_STREAM_MESSAGE } from "@/shared/constants/kinds"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; export type ProfileInteractionAction = "huddle" | "message" | "wave"; @@ -207,7 +207,7 @@ export function useProfileInteractionActions({ const senderName = selfProfileQuery.data?.displayName?.trim() || identity.displayName.trim() || - truncatePubkey(identity.pubkey); + truncateNpub(identity.pubkey); const content = buildWaveMessageContent(senderName); const queryKey = channelMessagesKey(dm.id); diff --git a/desktop/src/features/projects/ui/IssueAssigneesRow.tsx b/desktop/src/features/projects/ui/IssueAssigneesRow.tsx index 54073bac5dd..71855770f27 100644 --- a/desktop/src/features/projects/ui/IssueAssigneesRow.tsx +++ b/desktop/src/features/projects/ui/IssueAssigneesRow.tsx @@ -13,7 +13,7 @@ import { useUserSearchQuery } from "@/features/profile/hooks"; import type { UserProfileLookup } from "@/features/profile/lib/identity"; import type { UserSearchResult } from "@/shared/api/types"; import { cn } from "@/shared/lib/cn"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { Button } from "@/shared/ui/button"; import { Dialog, @@ -37,7 +37,7 @@ function labelForPubkey(pubkey: string, profiles?: UserProfileLookup) { return ( profile?.displayName?.trim() || profile?.nip05Handle?.trim() || - truncatePubkey(pubkey) + truncateNpub(pubkey) ); } @@ -45,7 +45,7 @@ function assigneeSearchLabel(user: UserSearchResult) { return ( user.displayName?.trim() || user.nip05Handle?.trim() || - truncatePubkey(user.pubkey) + truncateNpub(user.pubkey) ); } @@ -383,7 +383,7 @@ export function IssueAssigneesRow({ {candidate.isAgent ? "Agent · " : ""} - {truncatePubkey(candidate.pubkey)} + {truncateNpub(candidate.pubkey)} diff --git a/desktop/src/features/projects/ui/ProjectDetailFeedPanels.tsx b/desktop/src/features/projects/ui/ProjectDetailFeedPanels.tsx index d960d3f09fc..bdb81011b3a 100644 --- a/desktop/src/features/projects/ui/ProjectDetailFeedPanels.tsx +++ b/desktop/src/features/projects/ui/ProjectDetailFeedPanels.tsx @@ -16,7 +16,7 @@ import { selectionItemFromCommit } from "@/features/projects/lib/projectSelectio import { commitShareLink } from "@/features/projects/lib/projectShareLinks"; import { relativeTime } from "@/features/projects/lib/projectsViewHelpers"; import type { ProjectRepoCommit } from "@/shared/api/types"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { BuzzLoadingState } from "@/shared/ui/BuzzLoadingState"; import { resolveUserLabel, @@ -129,7 +129,7 @@ export function ContributorsPanel({ isAgent, label: profile ? resolveUserLabel({ profiles, pubkey }) - : truncatePubkey(pubkey), + : truncateNpub(pubkey), profileLinked: true, pubkey, reviewCount: signedCounts.reviews, diff --git a/desktop/src/features/projects/ui/ProjectPullRequestInlineComments.tsx b/desktop/src/features/projects/ui/ProjectPullRequestInlineComments.tsx index 281aefe71be..fc64ffc6324 100644 --- a/desktop/src/features/projects/ui/ProjectPullRequestInlineComments.tsx +++ b/desktop/src/features/projects/ui/ProjectPullRequestInlineComments.tsx @@ -7,7 +7,7 @@ import type { } from "@/features/projects/projectPullRequests.mjs"; import { relativeTime } from "@/features/projects/lib/projectsViewHelpers"; import type { UserProfileLookup } from "@/features/profile/lib/identity"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { ProjectRichContent } from "./ProjectRichContent"; function commentAuthor( @@ -18,7 +18,7 @@ function commentAuthor( return ( profile?.displayName?.trim() || profile?.nip05Handle?.trim() || - truncatePubkey(pubkey) + truncateNpub(pubkey) ); } diff --git a/desktop/src/features/projects/ui/ProjectPullRequestsPanel.tsx b/desktop/src/features/projects/ui/ProjectPullRequestsPanel.tsx index b130bf552c5..9fe09f50cd4 100644 --- a/desktop/src/features/projects/ui/ProjectPullRequestsPanel.tsx +++ b/desktop/src/features/projects/ui/ProjectPullRequestsPanel.tsx @@ -33,7 +33,7 @@ import { canReviewProjectPullRequest } from "@/features/projects/pullRequestRevi import type { UserProfileLookup } from "@/features/profile/lib/identity"; import { useIdentityQuery } from "@/shared/api/hooks"; import type { ChannelMember } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { ProjectFeedRow, ProjectFeedRowCluster, @@ -70,7 +70,7 @@ function labelForPubkey(pubkey: string, profiles?: UserProfileLookup) { return ( profile?.displayName?.trim() || profile?.nip05Handle?.trim() || - truncatePubkey(pubkey) + truncateNpub(pubkey) ); } diff --git a/desktop/src/features/projects/ui/PullRequestReviewersRow.tsx b/desktop/src/features/projects/ui/PullRequestReviewersRow.tsx index fa5f205fabb..8b824f917d9 100644 --- a/desktop/src/features/projects/ui/PullRequestReviewersRow.tsx +++ b/desktop/src/features/projects/ui/PullRequestReviewersRow.tsx @@ -19,7 +19,7 @@ import { useUserSearchQuery } from "@/features/profile/hooks"; import type { UserProfileLookup } from "@/features/profile/lib/identity"; import type { UserSearchResult } from "@/shared/api/types"; import { cn } from "@/shared/lib/cn"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { Button } from "@/shared/ui/button"; import { Dialog, @@ -44,7 +44,7 @@ function labelForPubkey(pubkey: string, profiles?: UserProfileLookup) { return ( profile?.displayName?.trim() || profile?.nip05Handle?.trim() || - truncatePubkey(pubkey) + truncateNpub(pubkey) ); } @@ -52,7 +52,7 @@ function reviewerSearchLabel(user: UserSearchResult) { return ( user.displayName?.trim() || user.nip05Handle?.trim() || - truncatePubkey(user.pubkey) + truncateNpub(user.pubkey) ); } @@ -270,7 +270,7 @@ export function PullRequestReviewersRow({ {candidate.isAgent ? "Agent · " : ""} - {truncatePubkey(candidate.pubkey)} + {truncateNpub(candidate.pubkey)} diff --git a/desktop/src/features/pulse/ui/AgentActivityCard.tsx b/desktop/src/features/pulse/ui/AgentActivityCard.tsx index 30e8601912a..310820363e2 100644 --- a/desktop/src/features/pulse/ui/AgentActivityCard.tsx +++ b/desktop/src/features/pulse/ui/AgentActivityCard.tsx @@ -6,7 +6,7 @@ import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover"; import type { UserProfileSummary } from "@/shared/api/types"; import { Markdown } from "@/shared/ui/markdown"; import { UserAvatar } from "@/shared/ui/UserAvatar"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; type AgentActivityCardProps = { group: AgentNoteGroup; @@ -51,7 +51,7 @@ export function AgentActivityCard({ agentStatus, }: AgentActivityCardProps) { const [expanded, setExpanded] = React.useState(false); - const displayName = profile?.displayName ?? truncatePubkey(group.pubkey); + const displayName = profile?.displayName ?? truncateNpub(group.pubkey); const avatarUrl = profile?.avatarUrl ?? null; const isSingleNote = group.notes.length === 1; diff --git a/desktop/src/features/pulse/ui/NoteCard.tsx b/desktop/src/features/pulse/ui/NoteCard.tsx index 96f25a6ede4..7ce04ce3dba 100644 --- a/desktop/src/features/pulse/ui/NoteCard.tsx +++ b/desktop/src/features/pulse/ui/NoteCard.tsx @@ -18,7 +18,7 @@ import { AnimatedCount } from "@/shared/ui/AnimatedCount"; import { Markdown } from "@/shared/ui/markdown"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip"; import { UserAvatar } from "@/shared/ui/UserAvatar"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; export type NoteCardActions = { reply?: ( @@ -67,7 +67,7 @@ function ReplyParentContext({ const parentDisplayName = parentNote ? (cachedProfile?.displayName ?? fetchedProfile?.displayName ?? - truncatePubkey(parentNote.pubkey)) + truncateNpub(parentNote.pubkey)) : null; const parentAvatarUrl = cachedProfile?.avatarUrl ?? fetchedProfile?.avatarUrl ?? null; @@ -149,7 +149,7 @@ export function NoteCard({ members = [], actions, }: NoteCardProps) { - const displayName = profile?.displayName ?? truncatePubkey(note.pubkey); + const displayName = profile?.displayName ?? truncateNpub(note.pubkey); const avatarUrl = profile?.avatarUrl ?? null; const [isReplyComposerOpen, setIsReplyComposerOpen] = React.useState(false); const actionButtonClass = diff --git a/desktop/src/features/pulse/ui/PulseView.tsx b/desktop/src/features/pulse/ui/PulseView.tsx index 2b595ed2ccb..01cd20d6225 100644 --- a/desktop/src/features/pulse/ui/PulseView.tsx +++ b/desktop/src/features/pulse/ui/PulseView.tsx @@ -30,7 +30,7 @@ import { Input } from "@/shared/ui/input"; import { Skeleton } from "@/shared/ui/skeleton"; import { UserAvatar } from "@/shared/ui/UserAvatar"; import { VirtualizedList } from "@/shared/ui/VirtualizedList"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; export type PulseTab = | "search" @@ -225,7 +225,7 @@ export function PulseView({ currentPubkey }: PulseViewProps) { : null; const currentDisplayName = currentProfile?.displayName ?? - (currentPubkey ? truncatePubkey(currentPubkey) : "You"); + (currentPubkey ? truncateNpub(currentPubkey) : "You"); const pulseMentionMembers = React.useMemo(() => { const members: ChannelMember[] = []; diff --git a/desktop/src/features/search/ui/TopbarSearch.tsx b/desktop/src/features/search/ui/TopbarSearch.tsx index 68e879a3033..5a032c780a4 100644 --- a/desktop/src/features/search/ui/TopbarSearch.tsx +++ b/desktop/src/features/search/ui/TopbarSearch.tsx @@ -20,7 +20,7 @@ import { HighlightedSearchText } from "@/features/search/ui/HighlightedSearchTex import { useSearchMenuKeyboardNavigation } from "@/features/search/ui/useSearchMenuKeyboardNavigation"; import type { Channel, SearchHit, UserSearchResult } from "@/shared/api/types"; import { cn } from "@/shared/lib/cn"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { Dialog, DialogContent, DialogTitle } from "@/shared/ui/dialog"; import { useDeferredModalOpen } from "@/shared/ui/deferredModalOpen"; import { @@ -134,7 +134,7 @@ function getUserDisplayName(user: UserSearchResult) { return ( user.displayName?.trim() || user.nip05Handle?.trim() || - truncatePubkey(user.pubkey) + truncateNpub(user.pubkey) ); } diff --git a/desktop/src/features/settings/ui/ModerationQueueCard.tsx b/desktop/src/features/settings/ui/ModerationQueueCard.tsx index 58dbe87ab79..9bb5962585a 100644 --- a/desktop/src/features/settings/ui/ModerationQueueCard.tsx +++ b/desktop/src/features/settings/ui/ModerationQueueCard.tsx @@ -34,7 +34,7 @@ import { type SeverityTier, } from "@/features/settings/lib/moderationQueue"; import { cn } from "@/shared/lib/cn"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub, truncatePubkey } from "@/shared/lib/pubkey"; import { Button } from "@/shared/ui/button"; import { DropdownMenu, @@ -195,14 +195,15 @@ const SEVERITY_BADGE: Record = { }; function targetLabel(group: ModerationQueueGroup): string { - const short = truncatePubkey(group.target); switch (group.targetKind) { case "event": - return `Message ${short}`; + // An event id is not a pubkey identity: keep the generic hex form. + return `Message ${truncatePubkey(group.target)}`; case "pubkey": - return `Member ${short}`; + return `Member ${truncateNpub(group.target)}`; case "blob": - return `Attachment ${short}`; + // Blob ids are not pubkey identities either. + return `Attachment ${truncatePubkey(group.target)}`; } } @@ -213,7 +214,7 @@ function ReporterLine({ report: ModerationReport; displayName?: string | null; }) { - const who = displayName?.trim() || truncatePubkey(report.reporterPubkey); + const who = displayName?.trim() || truncateNpub(report.reporterPubkey); return (
@@ -476,9 +477,11 @@ function AuditRow({ action: ModerationAction; actorName?: string | null; }) { - const who = actorName?.trim() || truncatePubkey(action.actorPubkey); + const who = actorName?.trim() || truncateNpub(action.actorPubkey); + // Actor and member targets are pubkey identities; a targeted event keeps + // the generic hex truncation for its event id. const targetShort = action.targetPubkey - ? truncatePubkey(action.targetPubkey) + ? truncateNpub(action.targetPubkey) : action.targetEventId ? truncatePubkey(action.targetEventId) : null; diff --git a/desktop/src/features/sidebar/ui/ChannelActivityPopover.tsx b/desktop/src/features/sidebar/ui/ChannelActivityPopover.tsx index 34fdba0bdfe..1eea8e21789 100644 --- a/desktop/src/features/sidebar/ui/ChannelActivityPopover.tsx +++ b/desktop/src/features/sidebar/ui/ChannelActivityPopover.tsx @@ -13,7 +13,7 @@ import type { UserProfileLookup } from "@/features/profile/lib/identity"; import { useRemindLater } from "@/features/reminders/ui/RemindMeLaterProvider"; import { useIdentityQuery } from "@/shared/api/hooks"; import type { Channel, FeedItem, HomeFeedResponse } from "@/shared/api/types"; -import { normalizePubkey, truncatePubkey } from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { useNow } from "@/shared/lib/useNow"; import { Markdown } from "@/shared/ui/markdown"; import { @@ -150,12 +150,15 @@ function ThreadPreviewRow({ function WorkingAgentRow({ avatarUrl, elapsed, + initialsLabel, name, onOpen, pubkey, }: { avatarUrl: string | null; elapsed: string; + /** Unprefixed key (or authored name) the avatar derives initials from. */ + initialsLabel: string; name: string; onOpen: () => void; pubkey: string; @@ -171,6 +174,7 @@ function WorkingAgentRow({ avatarUrl={avatarUrl} className="h-9 w-9 shrink-0" displayName={name} + initialsLabel={initialsLabel} shape="squircle" size="md" /> @@ -192,7 +196,12 @@ function WorkingAgentRow({ ); } -function WorkingAgentRows({ +/** + * Working-agent rows for the channel activity popover. Exported for consumer + * tests: it owns the generated `Agent npub1…` fallback label that flows into + * `UserAvatar` initials. + */ +export function WorkingAgentRows({ activeWorking, channelId, onOpen, @@ -212,14 +221,15 @@ function WorkingAgentRows({ return activeWorking.agentPubkeys.map((pubkey, index) => { const profile = profiles?.[normalizePubkey(pubkey)]; - const name = - profile?.displayName?.trim() || - alignedAgentNames?.[index] || - `Agent ${truncatePubkey(pubkey)}`; + const authoredName = + profile?.displayName?.trim() || alignedAgentNames?.[index]; + const keyLabel = truncateNpub(pubkey); + const name = authoredName || `Agent ${keyLabel}`; return ( onOpen(pubkey, channelId)} diff --git a/desktop/src/features/workflows/ui/WorkflowAuthorPicker.tsx b/desktop/src/features/workflows/ui/WorkflowAuthorPicker.tsx index 9d6bdddc005..6550845295d 100644 --- a/desktop/src/features/workflows/ui/WorkflowAuthorPicker.tsx +++ b/desktop/src/features/workflows/ui/WorkflowAuthorPicker.tsx @@ -10,7 +10,7 @@ import { } from "@/features/profile/hooks"; import { resolveUserLabel } from "@/features/profile/lib/identity"; import { cn } from "@/shared/lib/cn"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { Input } from "@/shared/ui/input"; import { UserAvatar } from "@/shared/ui/UserAvatar"; import { @@ -348,7 +348,7 @@ function AuthorOption({ {label} - {truncatePubkey(candidate.pubkey)} + {truncateNpub(candidate.pubkey)} { }), "“Build finished” to team-on-call", ); + // A key destination renders its compact npub (npubEncode of the pinned + // hex key); a checksum-broken npub renders the neutral label, never the + // raw text as if it were a role. + const hexKey = "deadbeef".repeat(8); + const brokenNpub = + "npub1m6kmam774klwlh4dhmhaatd7al02m0h0m6kmam774klwlh4dhmhslezuzy"; + assert.equal( + workflowStepDescription({ + id: "dm", + action: "send_dm", + text: "Build finished", + to: hexKey, + }), + "“Build finished” to npub1m6k…zuz0", + ); + assert.equal( + workflowStepDescription({ + id: "dm", + action: "send_dm", + text: "Build finished", + to: brokenNpub, + }), + "“Build finished” to Unavailable", + ); assert.equal( workflowStepDescription({ id: "approval", @@ -68,6 +92,15 @@ test("describes configured workflow steps on the canvas", () => { }), "“Ship the release?” from release-managers", ); + assert.equal( + workflowStepDescription({ + id: "approval", + action: "request_approval", + message: "Ship the release?", + from: hexKey, + }), + "“Ship the release?” from npub1m6k…zuz0", + ); assert.equal( workflowStepDescription({ id: "reaction", diff --git a/desktop/src/features/workflows/ui/workflowStepDescription.ts b/desktop/src/features/workflows/ui/workflowStepDescription.ts index 136ce026111..40697fe352f 100644 --- a/desktop/src/features/workflows/ui/workflowStepDescription.ts +++ b/desktop/src/features/workflows/ui/workflowStepDescription.ts @@ -1,4 +1,4 @@ -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { formatDurationSecondsVerbose, parseDurationSeconds, @@ -20,11 +20,17 @@ function quoted(value: string | undefined): string | null { return normalized ? `“${normalized}”` : null; } +// DM/approver references are pubkeys — 64-char hex in any case, or a +// canonical lowercase npub — or freeform role/template text; only a key +// identity renders as a key (an npub-shaped string that fails the shared +// helper's checksum renders the neutral Unavailable, never raw text). +const KEY_REFERENCE = /^(?:[0-9a-f]{64}|npub1[0-9a-z]+)$/i; + function destination(value: string | undefined): string | null { const normalized = value?.trim(); if (!normalized) return null; - return /^[0-9a-f]{64}$/i.test(normalized) - ? truncatePubkey(normalized) + return KEY_REFERENCE.test(normalized) + ? truncateNpub(normalized) : compact(normalized); } diff --git a/desktop/src/features/workflows/ui/workflowTriggerDescription.test.mjs b/desktop/src/features/workflows/ui/workflowTriggerDescription.test.mjs index 1f2d03b2382..9c96abd3539 100644 --- a/desktop/src/features/workflows/ui/workflowTriggerDescription.test.mjs +++ b/desktop/src/features/workflows/ui/workflowTriggerDescription.test.mjs @@ -170,6 +170,39 @@ test("describes selected trigger conditions on the workflow canvas", () => { ), "👾 reaction added by Carl to “hey yourself”", ); + + // Without a resolved label, an author renders its compact npub while the + // referenced message keeps the generic hex truncation — an event id, not + // a pubkey identity. + const unresolvedAuthor = "deadbeef".repeat(8); + assert.equal( + workflowTriggerDescription({ + on: "message_posted", + filter: `trigger_author == "${unresolvedAuthor}"`, + }), + "Message posted by npub1m6k…zuz0", + ); + assert.equal( + workflowTriggerDescription({ + on: "message_posted", + filter: `trigger_author != "${unresolvedAuthor}"`, + }), + "Message posted by anyone except npub1m6k…zuz0", + ); + assert.equal( + workflowTriggerDescription({ + on: "reaction_added", + filter: `trigger_message_id == "${"b".repeat(64)}"`, + }), + "Reaction added to bbbbbbbb…bbbb", + ); + assert.equal( + workflowTriggerDescription({ + on: "reaction_added", + filter: `trigger_emoji == "👾" && trigger_author == "${unresolvedAuthor}" && trigger_message_id == "${"b".repeat(64)}"`, + }), + "👾 reaction added by npub1m6k…zuz0 to bbbbbbbb…bbbb", + ); }); test("compacts only an included emoji already rendered as the node icon", () => { diff --git a/desktop/src/features/workflows/ui/workflowTriggerDescription.ts b/desktop/src/features/workflows/ui/workflowTriggerDescription.ts index 51bba6c0102..a4927444974 100644 --- a/desktop/src/features/workflows/ui/workflowTriggerDescription.ts +++ b/desktop/src/features/workflows/ui/workflowTriggerDescription.ts @@ -1,4 +1,4 @@ -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub, truncatePubkey } from "@/shared/lib/pubkey"; import { parseConditionExpressions } from "./workflowConditionExpression"; import { TRIGGER_LABELS } from "./workflowFormTypes"; import type { ParsedConditionExpression } from "./workflowConditionExpression"; @@ -19,7 +19,9 @@ function authorReference( authorLoading?: boolean, ): string { if (authorLoading) return TRIGGER_AUTHOR_LOADING_LABEL; - return authorLabel ?? truncatePubkey(condition.value); + // The trigger author is a pubkey identity; the unresolved fallback renders + // the npub compact, never raw hex. + return authorLabel ?? truncateNpub(condition.value); } function quotedValue(value: string): string { @@ -35,6 +37,8 @@ function messageReference( messageLoading?: boolean, ): string { if (messageLoading) return TRIGGER_MESSAGE_LOADING_LABEL; + // The referenced message is an event id, not a pubkey identity: it keeps + // the generic hex truncation. return messageLabel ? quotedValue(messageLabel) : truncatePubkey(condition.value); diff --git a/desktop/src/shared/lib/initials.test.mjs b/desktop/src/shared/lib/initials.test.mjs index 19e9f8dbcc8..634489b9245 100644 --- a/desktop/src/shared/lib/initials.test.mjs +++ b/desktop/src/shared/lib/initials.test.mjs @@ -19,4 +19,47 @@ describe("getInitials", () => { it("returns empty for a symbol-only name", () => { assert.equal(getInitials("()"), ""); }); + + it("derives key-label initials from the compact key’s visible tail, not the npub prefix", () => { + // Compact npub labels every start with npub1: the tail is the only + // fragment that distinguishes one key-identified identity from another. + assert.equal(getInitials("npub1z59…zwkg"), "ZW"); + assert.equal(getInitials("npub1m6k…zuz0"), "ZU"); + }); + + it("derives full-npub initials from the same tail fragment", () => { + assert.equal( + getInitials( + "npub1z59jp0d24242424242424242424242424242424242424242zhwqnlzwkg", + ), + "ZW", + ); + }); + + it("leaves authored names that merely resemble npubs on the name path", () => { + // Not a key-shaped label: wrong lengths, separators, or alphabet must + // keep the ordinary name derivation so an authored name is never + // re-derived as a key just for resembling one. + for (const [label, expected] of [ + ["Npub1 Person", "NP"], + ["npub1cool handle", "NH"], + // Compact-label shape with a missing data character. + ["npub1ab…wxy", "NW"], + // Compact-label shape over letters outside the bech32 alphabet. + ["npub1bio…biob", "NB"], + ]) { + assert.equal(getInitials(label), expected); + } + }); + + it("requires a checksum-valid npub before deriving key-tail initials", () => { + // Same length and alphabet as a real npub, but the checksum does not + // decode: an authored lookalike must stay on the name path. + assert.equal( + getInitials( + "npub1z59jp0d24242424242424242424242424242424242424242zhwqnlzwkq", + ), + "N", + ); + }); }); diff --git a/desktop/src/shared/lib/initials.ts b/desktop/src/shared/lib/initials.ts index 23dbf2ff93b..da5833086c0 100644 --- a/desktop/src/shared/lib/initials.ts +++ b/desktop/src/shared/lib/initials.ts @@ -1,5 +1,50 @@ +import { decode } from "nostr-tools/nip19"; + +/** + * Key-form labels carry no name to abbreviate: their head is the constant + * `npub1` prefix, so name-derived initials would collapse every key-identified + * identity onto the same leading letter. A key's distinguishing fragment is + * its tail — the part a compact key actually shows — so derive initials from + * there and keep key-fallback avatars visually distinct. + * + * Detection is deliberately narrow so authored names never lose their name + * initials for merely resembling a key: a full label counts as a key only + * when it decodes as a checksum-valid npub of an identity-length key, and a + * compact label must match the exact `npub` + 4 + `…` + 4 truncation shape + * over the bech32 alphabet (the form `truncateNpub` emits). A display name + * that is a checksum-valid npub is indistinguishable from a real key by + * shape alone, and taking key-tail initials there is the safe side of that + * boundary. + */ +const BECH32_ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; +const COMPACT_NPUB_KEY_LABEL = new RegExp( + `^npub1[${BECH32_ALPHABET}]{3}…[${BECH32_ALPHABET}]{4}$`, +); +const HEX_64_REGEX = /^[0-9a-f]{64}$/; + +function isFullNpubLabel(label: string): boolean { + try { + const decoded = decode(label); + return decoded.type === "npub" && HEX_64_REGEX.test(decoded.data); + } catch { + return false; + } +} + +function keyTailInitials(name: string): string | null { + const trimmed = name.trim(); + if (!COMPACT_NPUB_KEY_LABEL.test(trimmed) && !isFullNpubLabel(trimmed)) { + return null; + } + return trimmed.slice(-4, -2).toUpperCase(); +} + /** Derive up to two uppercase initials from a display name. */ export function getInitials(name: string): string { + const keyInitials = keyTailInitials(name); + if (keyInitials !== null) { + return keyInitials; + } return name .replace(/[^\p{L}\p{N}\s]/gu, " ") .trim() diff --git a/desktop/src/shared/lib/mentionDisplay.test.mjs b/desktop/src/shared/lib/mentionDisplay.test.mjs index 20e0f17eb0b..0f44d5131a2 100644 --- a/desktop/src/shared/lib/mentionDisplay.test.mjs +++ b/desktop/src/shared/lib/mentionDisplay.test.mjs @@ -1,21 +1,27 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { formatMentionDisplayLabel } from "./mentionDisplay.ts"; -import { truncatePubkey } from "./pubkey.ts"; +import { + formatLegacyMentionDisplayLabel, + formatMentionDisplayLabel, +} from "./mentionDisplay.ts"; +import { truncateNpub, truncatePubkey } from "./pubkey.ts"; const KEY = `150b20bd${"a".repeat(52)}15dc`; +// npubEncode(KEY), pinned so a formatter regression cannot pass by +// re-deriving the expectation from the code under test. +const KEY_NPUB_COMPACT = "npub1z59…zwkg"; test("compact mention display uses the member-list formatter and keeps collision suffixes", () => { for (const suffix of ["", " 2", " 10"]) { assert.equal( formatMentionDisplayLabel(`Bad Janet (${KEY})${suffix}`, KEY), - `Bad Janet (${truncatePubkey(KEY)})${suffix}`, + `Bad Janet (${truncateNpub(KEY)})${suffix}`, ); } - assert.equal(formatMentionDisplayLabel(KEY, KEY), truncatePubkey(KEY)); + assert.equal(formatMentionDisplayLabel(KEY, KEY), KEY_NPUB_COMPACT); assert.equal( formatMentionDisplayLabel(KEY.toUpperCase(), KEY), - truncatePubkey(KEY.toUpperCase()), + KEY_NPUB_COMPACT, ); }); @@ -32,10 +38,23 @@ test("display leaves unbound, mismatched, malformed and ordinary labels literal" assert.equal(formatMentionDisplayLabel(label, key), label); }); +test("legacy display keeps the retired hex compaction byte-exact for clipboard validation", () => { + for (const suffix of ["", " 2", " 10"]) { + assert.equal( + formatLegacyMentionDisplayLabel(`Bad Janet (${KEY})${suffix}`, KEY), + `Bad Janet (${truncatePubkey(KEY)})${suffix}`, + ); + } + assert.equal(formatLegacyMentionDisplayLabel(KEY, KEY), truncatePubkey(KEY)); +}); + test("matching compact keys do not become identity keys", () => { const other = KEY.replace("aaaa", "bbbb"); assert.notEqual(KEY, other); - assert.equal( + // The two keys collide under the old hex first8…last4 truncation yet + // compact to distinguishable npubs: npub display keeps them apart. + assert.equal(truncatePubkey(KEY), truncatePubkey(other)); + assert.notEqual( formatMentionDisplayLabel(`Scout (${KEY})`, KEY), formatMentionDisplayLabel(`Scout (${other})`, other), ); diff --git a/desktop/src/shared/lib/mentionDisplay.ts b/desktop/src/shared/lib/mentionDisplay.ts index 10d15643ad4..ec8a199efad 100644 --- a/desktop/src/shared/lib/mentionDisplay.ts +++ b/desktop/src/shared/lib/mentionDisplay.ts @@ -1,17 +1,40 @@ -import { truncatePubkey } from "./pubkey"; +import { truncateNpub, truncatePubkey } from "./pubkey"; -/** Compact only a bound mention's key; its literal label remains authoritative. */ -export function formatMentionDisplayLabel( +type KeyCompaction = (key: string) => string; + +function compactMentionDisplayLabel( label: string, pubkey: string | undefined, + compactKey: KeyCompaction, ): string { if (!pubkey || !/^[0-9a-f]{64}$/i.test(pubkey)) return label; if (label.toLowerCase() === pubkey.toLowerCase()) { - return truncatePubkey(label); + return compactKey(label); } const qualified = label.match( /^(.*) \(([0-9a-f]{64})\)((?: (?:[2-9]|[1-9][0-9]+))?)$/i, ); if (qualified?.[2].toLowerCase() !== pubkey.toLowerCase()) return label; - return `${qualified[1]} (${truncatePubkey(qualified[2])})${qualified[3]}`; + return `${qualified[1]} (${compactKey(qualified[2])})${qualified[3]}`; +} + +/** Compact only a bound mention's key; its literal label remains authoritative. */ +export function formatMentionDisplayLabel( + label: string, + pubkey: string | undefined, +): string { + return compactMentionDisplayLabel(label, pubkey, truncateNpub); +} + +/** + * The pre-npub key compaction a chip rendered before keys displayed as npub. + * Retired from rendering; kept byte-exact so clipboard validation can still + * recognize whole chips copied by an older Buzz, re-binding them to the exact + * identity their record declares instead of degrading them to plain text. + */ +export function formatLegacyMentionDisplayLabel( + label: string, + pubkey: string | undefined, +): string { + return compactMentionDisplayLabel(label, pubkey, truncatePubkey); } diff --git a/desktop/src/shared/ui/UserAvatar.tsx b/desktop/src/shared/ui/UserAvatar.tsx index 1b47bc4c92c..0398ea2ccd9 100644 --- a/desktop/src/shared/ui/UserAvatar.tsx +++ b/desktop/src/shared/ui/UserAvatar.tsx @@ -35,6 +35,17 @@ function fallbackColorClass(displayName: string) { type UserAvatarProps = { avatarUrl: string | null; displayName: string; + /** + * Label used to derive fallback initials; defaults to `displayName`. + * + * Callers whose `displayName` is a generated role-prefixed key fallback + * ("Agent npub1abcd…wxyz") pass the unprefixed compact key here: + * word-initials would collapse every unnamed identity onto "AN"/"PN", + * while the compact key keeps distinct key-tail initials. Authored + * display names keep their name initials. The fallback color keeps + * hashing `displayName`, which still contains the key. + */ + initialsLabel?: string; size?: UserAvatarSize; accent?: boolean; shape?: "circle" | "squircle"; @@ -47,6 +58,7 @@ type UserAvatarProps = { export function UserAvatar({ avatarUrl, displayName, + initialsLabel, size = "md", accent = false, shape, @@ -55,7 +67,7 @@ export function UserAvatar({ imageDraggable, testId, }: UserAvatarProps) { - const initials = getInitials(displayName); + const initials = getInitials(initialsLabel ?? displayName); // Animated avatars show their static poster frame until hovered, then play // the animation. const animated = parseAnimatedAvatarUrl(avatarUrl); diff --git a/desktop/src/shared/ui/markdown/useMessageLinkMetadata.ts b/desktop/src/shared/ui/markdown/useMessageLinkMetadata.ts index 7a051743344..8977c1bc4c6 100644 --- a/desktop/src/shared/ui/markdown/useMessageLinkMetadata.ts +++ b/desktop/src/shared/ui/markdown/useMessageLinkMetadata.ts @@ -4,7 +4,7 @@ import type { ParsedMessageLink } from "@/features/messages/lib/messageLink"; import { summarizeMessageLinkContent } from "@/features/messages/lib/messageLinkMetadata"; import { getEventById } from "@/shared/api/tauri"; import { getUserProfile } from "@/shared/api/tauriProfiles"; -import { truncatePubkey } from "@/shared/lib/pubkey"; +import { truncateNpub } from "@/shared/lib/pubkey"; import { isDefinitiveEventNotFound } from "@/shared/lib/eventLookupError"; const MESSAGE_METADATA_RETRY_DELAY_MS = 750; @@ -70,7 +70,7 @@ function fetchMetadata( author: profile?.displayName?.trim() || profile?.nip05Handle?.trim() || - truncatePubkey(event.pubkey), + truncateNpub(event.pubkey), createdAt: event.created_at, snippet: summarizeMessageLinkContent(event.content), }; diff --git a/desktop/src/shared/ui/markdownMentionDisplay.test.mjs b/desktop/src/shared/ui/markdownMentionDisplay.test.mjs index bf96caeee00..5f9fe6b1126 100644 --- a/desktop/src/shared/ui/markdownMentionDisplay.test.mjs +++ b/desktop/src/shared/ui/markdownMentionDisplay.test.mjs @@ -2,7 +2,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import React from "react"; import { renderToStaticMarkup } from "react-dom/server"; -import { truncatePubkey } from "../lib/pubkey.ts"; +import { truncateNpub } from "../lib/pubkey.ts"; import { createMarkdownComponents } from "./markdown.tsx"; import { renderCachedMarkdown } from "./markdown/nodeCache.ts"; import { MarkdownRuntimeContext } from "./markdown/runtimeContext.ts"; @@ -33,7 +33,7 @@ for (const agent of [false, true]) { ); assert.equal( html.replace(/<[^>]+>/g, ""), - `Ask Scout (${truncatePubkey(KEY)}) 2`, + `Ask Scout (${truncateNpub(KEY)}) 2`, ); assert.ok(html.includes(`data-mention-label="${label}"`)); assert.ok(html.includes(`data-mention-pubkey="${KEY}"`)); diff --git a/desktop/tests/e2e/channel-activity-popover.spec.ts b/desktop/tests/e2e/channel-activity-popover.spec.ts index 0e6caac7762..59d46804615 100644 --- a/desktop/tests/e2e/channel-activity-popover.spec.ts +++ b/desktop/tests/e2e/channel-activity-popover.spec.ts @@ -344,6 +344,12 @@ test.describe("channel activity hover preview", () => { await expect( popover.getByTestId(`channel-activity-agent-${AGENT_PUBKEY}`), ).toContainText("Charlie"); + // The authored name also supplies the row avatar's initials. + await expect( + popover + .getByTestId(`channel-activity-agent-${AGENT_PUBKEY}`) + .getByText("C", { exact: true }), + ).toBeVisible(); await expect( popover.getByTestId(`channel-activity-agent-${AGENT_PUBKEY}`), ).toContainText("Working"); @@ -372,6 +378,56 @@ test.describe("channel activity hover preview", () => { await expect(threadPanel).toContainText("direct thread link"); }); + test("unnamed working agents keep distinct key-tail initials, not AN", async ({ + page, + }) => { + // npubEncode of the fixture keys: "a"×64 → npub1424…rcaj (tail RC), + // "b"×64 → npub1hwa…04hu (tail 04). Neither has a seeded profile, so + // the rows show the generated "Agent npub1…" fallback label. + const unnamedAgents = [ + { initials: "RC", label: "Agent npub1424…rcaj", pubkey: "a".repeat(64) }, + { initials: "04", label: "Agent npub1hwa…04hu", pubkey: "b".repeat(64) }, + ]; + + await page.goto("/"); + await page.waitForFunction( + () => + typeof (window as Window & { __BUZZ_E2E_SEED_ACTIVE_TURNS__?: unknown }) + .__BUZZ_E2E_SEED_ACTIVE_TURNS__ === "function", + ); + for (const { pubkey } of unnamedAgents) { + await page.evaluate( + ({ agentPubkey, channelId }) => { + ( + window as Window & { + __BUZZ_E2E_SEED_ACTIVE_TURNS__?: (input: { + agentPubkey: string; + channelId: string; + turnId: string; + }) => void; + } + ).__BUZZ_E2E_SEED_ACTIVE_TURNS__?.({ + agentPubkey, + channelId, + turnId: "unnamed-agent-initials", + }); + }, + { agentPubkey: pubkey, channelId: CHANNEL_GENERAL }, + ); + } + + const popover = await openActivityPopover(page); + for (const { initials, label, pubkey } of unnamedAgents) { + const row = popover.getByTestId(`channel-activity-agent-${pubkey}`); + await expect(row).toContainText(label); + // The avatar abbreviates its key's visible tail (UserAvatar's fallback + // settles after its 200ms delay; expect retries past it), never the + // "Agent" word initials of the prefixed label. + await expect(row.getByText(initials, { exact: true })).toBeVisible(); + await expect(row.getByText("AN", { exact: true })).toHaveCount(0); + } + }); + test("removes the dot and preview after the final activity is read", async ({ page, }) => { diff --git a/desktop/tests/e2e/channels.spec.ts b/desktop/tests/e2e/channels.spec.ts index f9fd6dca58b..371cffdfb0b 100644 --- a/desktop/tests/e2e/channels.spec.ts +++ b/desktop/tests/e2e/channels.spec.ts @@ -4176,8 +4176,26 @@ test("members sidebar virtualizes large channel rosters", async ({ page }) => { await expect(memberRows.first()).toBeVisible(); expect(await memberRows.count()).toBeLessThan(50); + // Generated members have no display name, so the roster sorts them by + // their npub fallback label: these sequential pubkeys share a `npub1qqq…` + // prefix and order by the checksum tail, not their numeric value. Resolve + // a generated member from the rows the initial window actually rendered + // instead of assuming `pubkeys[0]` sorts into that window. + const generatedPubkeySet = new Set(pubkeys); + const renderedPubkeys = await memberRows.evaluateAll((rows) => + rows.map( + (row) => + (row as HTMLElement).dataset.testid?.slice("sidebar-member-".length) ?? + "", + ), + ); + const firstRenderedGeneratedPubkey = renderedPubkeys.find((pubkey) => + generatedPubkeySet.has(pubkey), + ); + expect(firstRenderedGeneratedPubkey).toBeTruthy(); + const firstGeneratedRow = memberList.getByTestId( - `sidebar-member-${pubkeys[0]}`, + `sidebar-member-${firstRenderedGeneratedPubkey}`, ); await expect .poll(() => @@ -4214,8 +4232,24 @@ test("members sidebar virtualizes large channel rosters", async ({ page }) => { element.scrollTop = element.scrollHeight; element.dispatchEvent(new Event("scroll")); }); - await expect( - memberList.getByTestId(`sidebar-member-${pubkeys.at(-1)}`), + // Fully scrolling must render the roster's true tail, and the tail + // endpoint must be known independently of whatever the virtual window + // happens to render. The sidebar's own roster accounting — the + // "Members · N" header — must read exactly the fixture-known total + // ("random" seeds alice, the mock identity, and bob; this test adds the + // 500 generated pubkeys on top), so fixture or classification drift + // fails loudly here instead of silently weakening the tail check. + const rosterCount = 3 + pubkeys.length; + await expect(memberList.getByText(/^Members · \d+$/)).toHaveText( + `Members · ${rosterCount}`, + ); + // VirtualizedList stamps each rendered row with its item index, so the + // final item's row is a fixed target that no window sample can pick in + // its place: a virtualizer clamped mid-roster never renders it. + await expect( + memberList.locator( + `[data-index="${rosterCount - 1}"] > [data-testid^="sidebar-member-"]`, + ), ).toBeVisible(); }); diff --git a/desktop/tests/e2e/huddle-transcription.spec.ts b/desktop/tests/e2e/huddle-transcription.spec.ts index 7c56648654a..32b4bcde56a 100644 --- a/desktop/tests/e2e/huddle-transcription.spec.ts +++ b/desktop/tests/e2e/huddle-transcription.spec.ts @@ -4,6 +4,7 @@ import { KIND_HUDDLE_ENDED, KIND_HUDDLE_STARTED, } from "../../src/shared/constants/kinds"; +import { truncateNpub } from "../../src/shared/lib/pubkey"; import { waitForAnimations } from "../helpers/animations"; import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge"; @@ -11,6 +12,15 @@ const HUDDLE_CHANNEL_ID = "11111111-1111-4111-8111-111111111111"; const HUDDLE_PARENT_ID = "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50"; const HUDDLE_THREAD_ROOT_ID = "mock-general-welcome"; +/** + * Initials a key-fallback avatar shows: the two characters just before the + * compact npub label's visible tail — the fragment `getInitials` reads from + * a key label, whose unit suite pins the derivation itself. + */ +function keyTailInitials(pubkey: string) { + return truncateNpub(pubkey).slice(-4, -2).toUpperCase(); +} + async function waitForMockLiveSubscription( page: import("@playwright/test").Page, channelName: string, @@ -1075,10 +1085,19 @@ test("returns to the parent channel when leaving a huddle channel in view", asyn test("keeps the huddle avatar strip compact and exposes the full roster", async ({ page, }) => { - const members = Array.from({ length: 11 }, (_, index) => ({ + // One named member plus ten key-only members (hex 01..0a + "a"×62), with + // the last as the huddle bot. The generated keys' npub tails are what + // key-fallback avatars abbreviate: 01… → npub1qx4…x2tc → X2, + // 02… → npub1q24…levs → LE, 0a… → npub1p24…sqjt → SQ. + const namedMember = "77".repeat(32); + const unnamedMembers = Array.from({ length: 10 }, (_, index) => ({ pubkey: `${(index + 1).toString(16).padStart(2, "0")}${"a".repeat(62)}`, - role: index === 10 ? ("bot" as const) : ("member" as const), + role: index === 9 ? ("bot" as const) : ("member" as const), })); + const members = [ + { pubkey: namedMember, role: "member" as const }, + ...unnamedMembers, + ]; await installMockBridge(page, { huddle: { @@ -1086,6 +1105,7 @@ test("keeps the huddle avatar strip compact and exposes the full roster", async ephemeralChannelId: HUDDLE_CHANNEL_ID, members, }, + searchProfiles: [{ pubkey: namedMember, displayName: "Ada Lovelace" }], }); await page.goto("/"); @@ -1100,13 +1120,44 @@ test("keeps the huddle avatar strip compact and exposes the full roster", async await expect(participantTrigger).toContainText("+2"); await expect(page.getByTestId("profile-huddle-control")).toHaveCount(0); + // Strip avatars stay distinct: the named member keeps name initials + // (AL), and a key-only participant shows its npub's tail (X2), never the + // word initials of the "Participant npub1…" label (PN). + const stripAvatars = participantStrip.getByTestId( + "huddle-participant-avatar", + ); + await expect(stripAvatars.first()).toHaveText("AL"); + await expect(stripAvatars.nth(1)).toHaveText( + keyTailInitials(unnamedMembers[0].pubkey), + ); + await participantTrigger.click(); + const roster = page + .getByRole("dialog") + .filter({ has: page.getByRole("heading", { name: "Participants" }) }); await expect( page.getByRole("heading", { name: "Participants" }), ).toBeVisible(); + const rosterRows = roster.getByRole("listitem"); + // Visible labels keep their generated forms alongside the avatar initials. + await expect(rosterRows.filter({ hasText: "Ada Lovelace" })).toHaveCount(1); + await expect( + rosterRows.filter({ + hasText: `Participant ${truncateNpub(unnamedMembers[0].pubkey)}`, + }), + ).toHaveCount(1); + const agentRow = rosterRows.filter({ + hasText: `Agent ${truncateNpub(unnamedMembers[9].pubkey)}`, + }); + await expect(agentRow).toHaveCount(1); await expect( page.getByRole("button", { name: /Remove Agent .* from huddle/ }), ).toHaveCount(1); + // The roster's agent avatar abbreviates its key tail (SQ), never the + // "Agent" word initials of the prefixed label (AN). + await expect(agentRow.getByTestId("huddle-participant-avatar")).toHaveText( + keyTailInitials(unnamedMembers[9].pubkey), + ); }); test("removes an agent from its menu without showing an extra participant control", async ({ diff --git a/desktop/tests/e2e/mention-recipients.spec.ts b/desktop/tests/e2e/mention-recipients.spec.ts index 0e29ed49327..0b81596780f 100644 --- a/desktop/tests/e2e/mention-recipients.spec.ts +++ b/desktop/tests/e2e/mention-recipients.spec.ts @@ -1,5 +1,5 @@ import { expect, test, type Page } from "@playwright/test"; -import { truncatePubkey } from "../../src/shared/lib/pubkey"; +import { truncateNpub } from "../../src/shared/lib/pubkey"; import { waitForAnimations } from "../helpers/animations"; import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge"; @@ -566,7 +566,7 @@ for (const { kind, scale } of [ .last(); await assertFits(markdown, "sent"); const qualifiedChip = row.locator("[data-mention]").last(); - await expect(qualifiedChip).toHaveText(`Scout (${truncatePubkey(SECOND)})`); + await expect(qualifiedChip).toHaveText(`Scout (${truncateNpub(SECOND)})`); await expect(qualifiedChip).toHaveAttribute( "data-mention-label", `Scout (${SECOND})`, @@ -873,7 +873,7 @@ for (const mismatchedKey of [false, true]) { .getByTestId("message-row") .filter({ hasText: "qualified clipboard roundtrip" }) .locator(`[data-mention-pubkey="${SECOND}"]`); - await expect(chip).toHaveText(`Scout (${truncatePubkey(SECOND)})`); + await expect(chip).toHaveText(`Scout (${truncateNpub(SECOND)})`); const flavors = await chip.evaluate((element) => { const range = document.createRange(); range.selectNode(element); @@ -956,7 +956,7 @@ for (const partial of [false, true]) { .filter({ hasText: "compact collision" }); for (const key of keys) { const chip = row.locator(`[data-mention-pubkey="${key}"]`); - await expect(chip).toHaveText(`Scout (${truncatePubkey(key)})`); + await expect(chip).toHaveText(`Scout (${truncateNpub(key)})`); await expect(chip).toHaveAttribute("title", `Scout (${key})`); const flavors = await chip.evaluate((element, partial) => { const range = document.createRange(); diff --git a/desktop/tests/e2e/mentions.spec.ts b/desktop/tests/e2e/mentions.spec.ts index e9248509fd1..ee3731d16d7 100644 --- a/desktop/tests/e2e/mentions.spec.ts +++ b/desktop/tests/e2e/mentions.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "@playwright/test"; -import { npubEncode } from "nostr-tools/nip19"; +import { truncateNpub } from "../../src/shared/lib/pubkey"; import { waitForAnimations } from "../helpers/animations"; import { @@ -4535,9 +4535,8 @@ test("clicking author name opens user profile panel", async ({ page }) => { // Click now opens the full profile panel instead of the popover const panel = page.getByTestId("user-profile-panel"); await expect(panel).toBeVisible(); - // The panel's public key row renders through the shared widget, - // which displays the canonical npub form — assert the npub prefix. - await expect(panel).toContainText(npubEncode(MOCK_VIEWER_PUBKEY).slice(0, 8)); + await expect(panel).toContainText(truncateNpub(MOCK_VIEWER_PUBKEY)); + await expect(panel).not.toContainText("deadbeefdeadbeef"); }); test("hovering avatar opens popover, clicking opens profile panel", async ({ @@ -4760,7 +4759,7 @@ test("agent profile popover falls back to the owner's pubkey", async ({ profilePopover.getByTestId( `user-profile-popover-owner-${OWNED_AGENT_PROFILE_PUBKEY}`, ), - ).toHaveText("managed by 11111111…1111"); + ).toHaveText(`managed by ${truncateNpub(CASEY_PROFILE_PUBKEY)}`); }); test("human profile popover does not show an owner", async ({ page }) => { diff --git a/desktop/tests/e2e/pubkey-display-screenshots.spec.ts b/desktop/tests/e2e/pubkey-display-screenshots.spec.ts index 81ff86c3299..8dbdbad005f 100644 --- a/desktop/tests/e2e/pubkey-display-screenshots.spec.ts +++ b/desktop/tests/e2e/pubkey-display-screenshots.spec.ts @@ -1,12 +1,13 @@ import { expect, test } from "@playwright/test"; import { npubEncode } from "nostr-tools/nip19"; +import { truncateNpub } from "../../src/shared/lib/pubkey"; +import { waitForAnimations } from "../helpers/animations"; import { installMockBridge, openNewMessagePage, TEST_IDENTITIES, } from "../helpers/bridge"; -import { waitForAnimations } from "../helpers/animations"; const SHOTS = "test-results/pubkey-display"; @@ -121,7 +122,7 @@ test("new-DM agent name swaps to its public key on name hover", async ({ ) .toBe(true); await expect(settledAgentNpub).not.toHaveCSS("opacity", "0"); - await expect(settledAgentNpub).toHaveText("cafef00d…f00d"); + await expect(settledAgentNpub).toHaveText(truncateNpub(AGENT_PUBKEY)); await expect( settledAgentName.getByText("Pinky", { exact: true }), ).not.toHaveCSS("opacity", "1"); @@ -159,7 +160,7 @@ test("selected new-DM recipient can be verified again through search", async ({ await charlieName.hover(); await expect(charlieNpub).toHaveCSS("opacity", "1"); await expect(charlieNpub).toHaveText( - `${TEST_IDENTITIES.charlie.pubkey.slice(0, 8)}…${TEST_IDENTITIES.charlie.pubkey.slice(-4)}`, + truncateNpub(TEST_IDENTITIES.charlie.pubkey), ); await page.mouse.move(1_100, 500); await expect(charlieNpub).toHaveCSS("opacity", "0"); @@ -262,7 +263,7 @@ test("selected new-DM recipient can be verified again through search", async ({ await charlieName.hover(); await expect(charlieNpub).toHaveCSS("opacity", "1"); await expect(charlieNpub).toHaveText( - `${TEST_IDENTITIES.charlie.pubkey.slice(0, 8)}…${TEST_IDENTITIES.charlie.pubkey.slice(-4)}`, + truncateNpub(TEST_IDENTITIES.charlie.pubkey), ); await expect(charlieName.getByText("charlie", { exact: true })).toHaveCSS( "opacity", diff --git a/desktop/tests/e2e/workflow-local-controls.spec.ts b/desktop/tests/e2e/workflow-local-controls.spec.ts index 6a8319082c8..bf3aca2e2d2 100644 --- a/desktop/tests/e2e/workflow-local-controls.spec.ts +++ b/desktop/tests/e2e/workflow-local-controls.spec.ts @@ -1,6 +1,7 @@ import { expect, test } from "@playwright/test"; import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; +import { truncateNpub } from "../../src/shared/lib/pubkey"; import { waitForAnimations } from "../helpers/animations"; import { installMockBridge } from "../helpers/bridge"; @@ -63,7 +64,9 @@ async function addMessageStep( ) { await dialog.getByRole("button", { name: "Add step", exact: true }).click(); await page.getByRole("menuitem", { name: "Send Message" }).click(); - await dialog.getByLabel("Message text").fill("Workflow notification"); + await dialog + .locator('textarea[id^="wf-step-"][id$="-text"]') + .fill("Workflow notification"); } async function createEnabled( @@ -152,7 +155,7 @@ test("inserts template variables with keyboard control and restores the caret", await dialog.getByRole("button", { name: "Add step", exact: true }).click(); await page.getByRole("menuitem", { name: "Send Message" }).click(); - const textarea = dialog.getByLabel("Message text"); + const textarea = dialog.locator('textarea[id^="wf-step-"][id$="-text"]'); const listbox = page.getByRole("listbox"); await textarea.fill("Hello {{trig"); await expect(listbox).toBeVisible(); @@ -286,6 +289,7 @@ test("round-trips and reopens structured message-text conditions", async ({ await dialog.getByRole("tab", { name: "Form" }).click(); await openTriggerInspector(dialog); + await waitForAnimations(page); const matchControls = dialog.getByRole("group", { name: "Match" }); const operatorButtons = matchControls.getByRole("button"); const firstOperatorBox = await operatorButtons.nth(0).boundingBox(); @@ -640,11 +644,11 @@ test("round-trips manual author and reaction message IDs through save and reopen }); await authorSearch.fill(author); await expect( - dialog.getByRole("option", { name: new RegExp(author.slice(0, 8)) }), + dialog.getByRole("option", { name: truncateNpub(author) }), ).toBeVisible(); await authorSearch.press("Enter"); await expect( - dialog.getByRole("option", { name: new RegExp(author.slice(0, 8)) }), + dialog.getByRole("option", { name: truncateNpub(author) }), ).toHaveAttribute("aria-selected", "true"); await expect(dialog.getByRole("button", { name: "Create" })).toBeEnabled(); await dialog.getByRole("tab", { name: "YAML" }).click(); @@ -663,7 +667,7 @@ test("round-trips manual author and reaction message IDs through save and reopen }); await correctedAuthorSearch.fill(author); await expect( - dialog.getByRole("option", { name: new RegExp(author.slice(0, 8)) }), + dialog.getByRole("option", { name: truncateNpub(author) }), ).toBeVisible(); await correctedAuthorSearch.press("Enter"); await dialog @@ -697,7 +701,7 @@ test("round-trips manual author and reaction message IDs through save and reopen await openTriggerInspector(reopened); await reopened.getByText("Author", { exact: true }).locator("..").click(); await expect( - reopened.getByRole("option", { name: new RegExp(author.slice(0, 8)) }), + reopened.getByRole("option", { name: truncateNpub(author) }), ).toHaveAttribute("aria-selected", "true"); await reopened.getByText("Message", { exact: true }).locator("..").click(); await expect( @@ -738,7 +742,7 @@ test("toggles selected author and message filters while preserving sibling condi .locator(".."); await authorField.getByText("Author", { exact: true }).locator("..").click(); const authorOption = dialog.getByRole("option", { - name: new RegExp(author.slice(0, 8)), + name: truncateNpub(author), }); await expect(authorOption).toHaveAttribute("aria-selected", "true"); await expect( @@ -751,7 +755,7 @@ test("toggles selected author and message filters while preserving sibling condi }); await authorSearch.fill(replacementAuthor); const replacementAuthorOption = dialog.getByRole("option", { - name: new RegExp(replacementAuthor.slice(0, 8)), + name: truncateNpub(replacementAuthor), }); await expect(replacementAuthorOption).toBeVisible(); await authorSearch.press("Enter"); diff --git a/desktop/tests/e2e/workflow-local-controls.spec.ts-snapshots/workflow-template-variable-autocomplete-smoke-darwin.png b/desktop/tests/e2e/workflow-local-controls.spec.ts-snapshots/workflow-template-variable-autocomplete-smoke-darwin.png index 54cc63d5b2a520a9fe9aff54caff40b69d25909f..f0826431ce64832ad04ab2ecdc829a2eacba214d 100644 GIT binary patch literal 103906 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�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 Yco8uZ5-Mox|5L>9k6GbY1iCMpC2$VSMA8h} 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 🤖 ## Summary If two people in a channel share a display name, mobile couldn't tell them apart in mentions: picking the second could overwrite the first's selection, a rendered mention linked whichever same-name person matched first, and a later rename or a shorter name could re-bind the text to the wrong recipient. This PR binds every mention to the exact selected identity: - Each same-name selection keeps its own recipient instead of overwriting by name; conflicting picks get a qualified label like `Name (key…)`. - The longest matching label wins, so a shorter or interior name can never claim part of a longer one and steal its identity. - Rendering resolves recipients by the signed identity key from the event's tags — never from message text alone — so qualified labels stay correct regardless of tag order and survive later renames; an untagged ambiguous label blocks shorter mentions instead of silently re-binding. Ports the landed Desktop exact-recipient behavior (see `docs/mention-editor.md`). ### Related issue - Fixes: N/A. No mobile issue; Desktop's landed exact-recipient fixes are the reference this ports. - Independent base (`main`). #7387 (child) persists these exact selections in saved drafts. - Landing note: branches in this series overlap in the composer — when rebasing, keep exact/durable mention bindings, the explicit invite/reference-only choice, the account/visit/revision fences, and authorization before membership preparation and publication; don't resolve conflicts by taking either side wholesale. - Draft — not requesting merge yet; the security advisory run for this range timed out without results (no verdict). ### Testing - Regressions cover same-name collisions, prefix/overlap, removal, tag order, and renames. - At `acd841354a692243f1cb4c04059baad42f1d2abb`: `just mobile-check` and full `just mobile-test` pass (2,082 tests). The earlier full `just ci` receipt linked below is reused only for unchanged non-mobile code/tooling, not claimed as a rerun at this head. - Previous-head evidence: `just mobile-check`, the full mobile test suite, and full local `just ci` all pass — receipts in the [exact-head evidence comment](https://github.com/block/buzz/pull/7385#issuecomment-5574635836). - Verification is widget-test level; no native device or simulator run is claimed. To see it: mention two teammates with the same display name — both stay distinct, the second shows a qualified label, and the rendered message keeps both correct even after either renames. ### Screenshots Flutter production-widget test renders — not native-device screenshots or acceptance captures. | Scenario | Before | After | |---|---|---| | Qualified mention chip for same-name recipients, at 200% text scale (deliberate stress fixture) | ![Before: the short chip is followed by the distinguishing key spilling out as raw text](https://github.com/user-attachments/assets/f1e40acc-24f1-47f7-b2f7-e3ba2348c169) | ![After: the full qualified label wraps inside the bounded chip, keeping the @ and robot glyph visible](https://github.com/user-attachments/assets/b949e44f-e3f6-4c72-98ff-68139b2e1ab3) |
Capture provenance Rendered by the Flutter widget engine in a `flutter test` run (production widgets, production theme; no device or simulator). Before: this PR's declared base `3c7f288c60d67df78577b237e27c3dfc8831aaa1`. After: its head `39afd73b0adfde14164f4b10dbd089cb498312b6`.
Signed-off-by: Logan Johnson --- .../activity/compose_drafts_provider.dart | 44 ++- mobile/lib/features/channels/compose_bar.dart | 1 + .../compose_bar/agent_mention_labels.dart | 54 ++- .../compose_bar/compose_bar_widget.dart | 62 +++- .../channels/compose_bar/draft_lifecycle.dart | 55 ++- .../markdown_editing_controller.dart | 10 + .../channels/mentions/mention_ranking.dart | 4 + .../features/channels/message_content.dart | 85 +++-- .../lib/shared/mentions/mention_bindings.dart | 88 +++++ .../channels/channel_detail_page_test.dart | 70 ++++ .../features/channels/compose_bar_test.dart | 6 + .../compose_bar_test/exact_mention_tests.dart | 312 ++++++++++++++++++ .../message_content_exact_mentions_test.dart | 51 +++ .../mentions/mention_bindings_test.dart | 56 ++++ 14 files changed, 837 insertions(+), 61 deletions(-) create mode 100644 mobile/lib/shared/mentions/mention_bindings.dart create mode 100644 mobile/test/features/channels/compose_bar_test/exact_mention_tests.dart create mode 100644 mobile/test/features/channels/message_content_exact_mentions_test.dart create mode 100644 mobile/test/shared/mentions/mention_bindings_test.dart diff --git a/mobile/lib/features/activity/compose_drafts_provider.dart b/mobile/lib/features/activity/compose_drafts_provider.dart index b5755f19c8d..cdeb7030923 100644 --- a/mobile/lib/features/activity/compose_drafts_provider.dart +++ b/mobile/lib/features/activity/compose_drafts_provider.dart @@ -22,6 +22,10 @@ class ComposeDraft { final String channelId; final String? threadHeadId; final String text; + + /// Literal picker labels bound to exact keys, never authorization metadata. + /// An empty key/value is a malformed-record tombstone; send must refuse it. + final Map mentionKeys; final int updatedAt; // unix seconds const ComposeDraft({ @@ -30,6 +34,7 @@ class ComposeDraft { required this.threadHeadId, required this.text, required this.updatedAt, + this.mentionKeys = const {}, }); Map toJson() => { @@ -37,6 +42,7 @@ class ComposeDraft { 'channel_id': channelId, if (threadHeadId != null) 'thread_head_id': threadHeadId, 'text': text, + 'mention_keys': mentionKeys, 'updated_at': updatedAt, }; @@ -48,10 +54,32 @@ class ComposeDraft { final updatedAt = raw['updated_at']; if (key is! String || channelId is! String || text is! String) return null; if (text.trim().isEmpty) return null; + final bindings = raw['mention_keys']; + final mentionKeys = {}; + if (raw.containsKey('mention_keys')) { + if (bindings is! Map) { + mentionKeys[''] = ''; + } else { + final labels = {}; + for (final entry in bindings.entries) { + if (!labels.add(entry.key.toLowerCase())) mentionKeys[''] = ''; + final value = entry.value; + mentionKeys[entry.key] = + entry.key.isNotEmpty && + value is String && + RegExp(r'^[0-9a-fA-F]{64}$').hasMatch(value) + ? value.toLowerCase() + : ''; + } + } + } return ComposeDraft( key: key, channelId: channelId, - threadHeadId: raw['thread_head_id'] as String?, + threadHeadId: raw['thread_head_id'] is String + ? raw['thread_head_id'] as String + : null, + mentionKeys: Map.unmodifiable(mentionKeys), text: text, updatedAt: updatedAt is int ? updatedAt : 0, ); @@ -108,18 +136,23 @@ class ComposeDraftsNotifier extends Notifier> { required String channelId, String? threadHeadId, required String text, + Map mentionKeys = const {}, }) { if (text.trim().isEmpty) { remove(key); return; } final existing = state.where((d) => d.key == key).firstOrNull; - if (existing?.text == text) return; + if (existing?.text == text && + mapEquals(existing?.mentionKeys, mentionKeys)) { + return; + } final draft = ComposeDraft( key: key, channelId: channelId, threadHeadId: threadHeadId, text: text, + mentionKeys: Map.unmodifiable(mentionKeys), updatedAt: DateTime.now().millisecondsSinceEpoch ~/ 1000, ); final next = [draft, ...state.where((d) => d.key != key)]; @@ -131,8 +164,11 @@ class ComposeDraftsNotifier extends Notifier> { _persist([...state.where((d) => d.key != key)]); } - String? textFor(String key) => - state.where((d) => d.key == key).firstOrNull?.text; + /// Read text and selected identities from the same persisted snapshot. + ComposeDraft? draftFor(String key) => + state.where((d) => d.key == key).firstOrNull; + + String? textFor(String key) => draftFor(key)?.text; void _persist(List drafts) { state = List.unmodifiable(drafts); diff --git a/mobile/lib/features/channels/compose_bar.dart b/mobile/lib/features/channels/compose_bar.dart index 726528a23e6..71ba39d14ec 100644 --- a/mobile/lib/features/channels/compose_bar.dart +++ b/mobile/lib/features/channels/compose_bar.dart @@ -18,6 +18,7 @@ import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:nostr/nostr.dart' as nostr; import '../../shared/mentions/agent_identity_provider.dart'; +import '../../shared/mentions/mention_bindings.dart'; import '../../shared/huddle/huddle_session.dart'; import '../../shared/relay/relay.dart'; import '../../shared/theme/theme.dart'; diff --git a/mobile/lib/features/channels/compose_bar/agent_mention_labels.dart b/mobile/lib/features/channels/compose_bar/agent_mention_labels.dart index bc29d5bb468..b988c3040ec 100644 --- a/mobile/lib/features/channels/compose_bar/agent_mention_labels.dart +++ b/mobile/lib/features/channels/compose_bar/agent_mention_labels.dart @@ -1,10 +1,54 @@ part of '../compose_bar.dart'; Set _agentMentionLabels({ - required Iterable candidates, -}) { - return { - for (final candidate in candidates) - if (candidate.isAgent) candidate.label, + required Map bindings, +}) => { + for (final entry in bindings.entries) + if (entry.value.isAgent) entry.key, +}; + +List _resolveComposerMentions( + String text, + Map selected, + List members, + List restoredCandidates, +) { + // A broken record cannot fall back to a same-name roster entry. + if (selected.values.any((c) => c.pubkey.isEmpty)) { + throw const FormatException( + 'Saved mention identity is invalid. Clear the draft and select again.', + ); + } + final candidates = >{ + for (final e in selected.entries) e.key.toLowerCase(): [e.value], }; + final selectedNames = candidates.keys.toSet(); + for (final member in members) { + final label = member.label.toLowerCase(); + if (!selectedNames.contains(label)) (candidates[label] ??= []).add(member); + } + final winners = {}; + for (final range in mentionOccurrences(text, candidates.keys)) { + final identities = { + for (final c in candidates[range.label]!) c.pubkey.toLowerCase(): c, + }; + if (identities.length > 1) { + throw FormatException( + 'The mention @${range.label} is ambiguous. Choose a recipient from the mention picker.', + ); + } + for (final entry in identities.entries) { + final selected = entry.value; + final current = restoredCandidates + .where((c) => c.pubkey == entry.key) + .firstOrNull; + if (selected.requiresRevalidation && current == null) { + throw const FormatException( + 'Saved mention is no longer available. Select it again from the picker.', + ); + } + winners[entry.key] = selected.requiresRevalidation ? current! : selected; + } + } + return winners.values.toList(); } diff --git a/mobile/lib/features/channels/compose_bar/compose_bar_widget.dart b/mobile/lib/features/channels/compose_bar/compose_bar_widget.dart index ca34ffd2a1b..dbf8b8f0fbe 100644 --- a/mobile/lib/features/channels/compose_bar/compose_bar_widget.dart +++ b/mobile/lib/features/channels/compose_bar/compose_bar_widget.dart @@ -86,7 +86,9 @@ class ComposeBar extends HookConsumerWidget { attachments: attachments, ); final voiceNoteRef = useRef(voiceNote)..value = voiceNote; + final mentionMap = useRef({}); _useComposeDraftLifecycle( + mentionMap: mentionMap, ref: ref, controller: controller, draftKey: draftKey, @@ -219,7 +221,6 @@ class ComposeBar extends HookConsumerWidget { // Map of displayName → selected mention candidate built as the user selects // mentions. Used to pass resolved pubkeys directly to onSend and to attach // selected non-member agents before the message is published. - final mentionMap = useRef({}); // Channel autocomplete state ---------------------------------------------- final channelQuery = useState(null); @@ -244,9 +245,7 @@ class ComposeBar extends HookConsumerWidget { // owners so @mention suggestions show names ("managed by …" included). final relayAgents = ref.watch(agentDirectoryProvider).asData?.value; final agentOwners = ref.watch(agentOwnersProvider).asData?.value; - final agentMentionLabels = _agentMentionLabels( - candidates: mentionMap.value.values, - ); + final agentMentionLabels = _agentMentionLabels(bindings: mentionMap.value); final agentMentionLabelsKey = (agentMentionLabels.toList()..sort()).join( '\u0000', ); @@ -263,6 +262,9 @@ class ComposeBar extends HookConsumerWidget { ); final pubkeys = [ ...memberList.map((m) => m.pubkey), + ...mentionMap.value.values + .where((c) => c.requiresRevalidation && c.pubkey.isNotEmpty) + .map((c) => c.pubkey), ...?relayAgents?.map((a) => a.pubkey), ...?agentOwners?.values, ]; @@ -272,6 +274,8 @@ class ComposeBar extends HookConsumerWidget { return null; }, [ + draftIdentity, + draftKey, membersAsync.asData?.value.length, cachedMembers.length, relayAgents?.length, @@ -382,7 +386,10 @@ class ComposeBar extends HookConsumerWidget { // Insert a selected mention into the text field. void insertMention(MentionCandidate candidate) { - final name = candidate.label; + final name = selectedMentionLabel(candidate.label, candidate.pubkey, { + for (final entry in mentionMap.value.entries) + entry.key: entry.value.pubkey, + }); // Track the resolved candidate so we can pass its pubkey and prepare // selected non-member agents at send time. mentionMap.value[name] = candidate; @@ -470,10 +477,45 @@ class ComposeBar extends HookConsumerWidget { final messenger = ScaffoldMessenger.maybeOf(context); // Extract pubkeys for mentions present in the final text. - final selectedMentions = [ - for (final entry in mentionMap.value.entries) - if (hasMention(text, entry.key)) entry.value, - ]; + List selectedMentions; + try { + selectedMentions = _resolveComposerMentions( + text, + mentionMap.value, + buildMentionCandidates( + members: channelMembersForAutocomplete( + membersAsync: membersAsync, + sessionStatus: sessionStatus, + cachedMembers: cachedMembers, + ), + relayAgents: const [], + sharedChannelIds: const {}, + userCache: userCache, + ownerByAgentPubkey: agentOwners ?? const {}, + ), + buildMentionCandidates( + members: membersAsync.asData?.value ?? const [], + relayAgents: relayAgents ?? const [], + sharedChannelIds: { + for (final c in channels) + if (c.isMember && !c.isArchived) c.id, + }, + userCache: userCache, + ownerByAgentPubkey: agentOwners ?? const {}, + currentPubkey: currentPubkey, + // Reuse ordinary search-result classification, not membership as + // permission. Persisted keys/flags themselves prove no role. + searchResults: [ + for (final c in mentionMap.value.values) + if (c.requiresRevalidation && userCache[c.pubkey] != null) + userCache[c.pubkey]!, + ], + ), + ); + } on FormatException catch (error) { + messenger?.showSnackBar(SnackBar(content: Text(error.message))); + return; + } final outgoing = _OutgoingMentions(selectedMentions); final scan = await _scanNonMemberMentions( ref, @@ -589,12 +631,12 @@ class ComposeBar extends HookConsumerWidget { if (context.mounted && queueGeneration == uploadGeneration.value && draftRevision.value == clearedDraftRevision) { - controller.value = draftText; attachments.value = draftAttachments; retainedForRetry = true; mentionMap.value ..clear() ..addAll(draftMentions); + controller.value = draftText; focusNode.requestFocus(); } } finally { diff --git a/mobile/lib/features/channels/compose_bar/draft_lifecycle.dart b/mobile/lib/features/channels/compose_bar/draft_lifecycle.dart index b712091b716..5ef69eaa59b 100644 --- a/mobile/lib/features/channels/compose_bar/draft_lifecycle.dart +++ b/mobile/lib/features/channels/compose_bar/draft_lifecycle.dart @@ -26,10 +26,10 @@ Future _sendTextOnlyDraft({ draftRevision.value != clearedDraftRevision) { return; } - controller.value = clearedDraftText; mentionMap.value ..clear() ..addAll(clearedDraftMentions); + controller.value = clearedDraftText; focusNode.requestFocus(); } @@ -63,6 +63,7 @@ Future _sendTextOnlyDraft({ } void _useComposeDraftLifecycle({ + required ObjectRef> mentionMap, required WidgetRef ref, required _MarkdownEditingController controller, required String draftKey, @@ -82,11 +83,25 @@ void _useComposeDraftLifecycle({ }) { final lastDraftIdentity = useRef(null); useEffect(() { + final identity = '$draftIdentity\u0000$draftKey'; + final shouldHydrate = lastDraftIdentity.value != identity; final identityChanged = - lastDraftIdentity.value != null && - lastDraftIdentity.value != draftIdentity; - lastDraftIdentity.value = draftIdentity; - final saved = ref.read(composeDraftsProvider.notifier).textFor(draftKey); + lastDraftIdentity.value != null && lastDraftIdentity.value != identity; + lastDraftIdentity.value = identity; + final saved = ref.read(composeDraftsProvider.notifier).draftFor(draftKey); + if (shouldHydrate) { + mentionMap.value + ..clear() + ..addAll({ + for (final entry + in (saved?.mentionKeys ?? {}).entries) + entry.key: MentionCandidate( + pubkey: entry.value, + displayName: entry.key, + requiresRevalidation: true, + ), + }); + } if (identityChanged) { draftRevision.value += 1; onDraftIdentityChanged(); @@ -101,15 +116,38 @@ void _useComposeDraftLifecycle({ final staleAttachments = attachments.value; attachments.value = const []; unawaited(_deleteOwnedAttachments(staleAttachments)); - controller.text = saved ?? ''; + controller.text = saved?.text ?? ''; } else if (saved != null && controller.text.isEmpty) { - controller.text = saved; + controller.text = saved.text; } var lastPersistedText = controller.text; + var lastBindings = { + for (final e in mentionMap.value.entries) e.key: e.value.pubkey, + }; void persistDraft() { final text = controller.text; - if (text == lastPersistedText) return; + if (text != lastPersistedText) { + // Prune before the atomic snapshot, not in a later editor listener. + // Code formatting hides a binding without deleting its literal. + final retained = mentionOccurrences( + text.replaceAll('`', ' '), + mentionMap.value.keys, + ).map((range) => range.label).toSet(); + // Whole-record taint has no literal to prune. Removing every @ is a + // safe reset; unrelated edits must not turn lost keys into name lookup. + mentionMap.value.removeWhere( + (label, _) => + label.isEmpty ? !text.contains('@') : !retained.contains(label), + ); + } + final bindings = { + for (final e in mentionMap.value.entries) e.key: e.value.pubkey, + }; + if (text == lastPersistedText && mapEquals(bindings, lastBindings)) { + return; + } + lastBindings = bindings; lastPersistedText = text; draftRevision.value += 1; ref @@ -119,6 +157,7 @@ void _useComposeDraftLifecycle({ channelId: channelId, threadHeadId: threadHeadId, text: text, + mentionKeys: bindings, ); } diff --git a/mobile/lib/features/channels/compose_bar/markdown_editing_controller.dart b/mobile/lib/features/channels/compose_bar/markdown_editing_controller.dart index 2ae71ac110b..2862aad1f37 100644 --- a/mobile/lib/features/channels/compose_bar/markdown_editing_controller.dart +++ b/mobile/lib/features/channels/compose_bar/markdown_editing_controller.dart @@ -282,6 +282,16 @@ class _MarkdownEditingController extends TextEditingController { if (prefix.isNotEmpty) spans.add(TextSpan(text: prefix, style: style)); final label = match.group(2)!; + if (RegExp(r'\([0-9a-f]{64}\)').hasMatch(label)) { + spans.add( + TextSpan( + text: '@$label', + style: style.copyWith(color: context.colors.primary), + ), + ); + offset = match.end; + continue; + } spans.add( WidgetSpan( alignment: PlaceholderAlignment.baseline, diff --git a/mobile/lib/features/channels/mentions/mention_ranking.dart b/mobile/lib/features/channels/mentions/mention_ranking.dart index 7706df4bdfc..fec710942db 100644 --- a/mobile/lib/features/channels/mentions/mention_ranking.dart +++ b/mobile/lib/features/channels/mentions/mention_ranking.dart @@ -7,6 +7,9 @@ import '../../../shared/utils/string_utils.dart'; @immutable class MentionCandidate { final String pubkey; + + /// Restored identity only: eligibility must come from current community state. + final bool requiresRevalidation; final String? displayName; final String? secondaryLabel; final String? avatarUrl; @@ -17,6 +20,7 @@ class MentionCandidate { const MentionCandidate({ required this.pubkey, + this.requiresRevalidation = false, this.displayName, this.secondaryLabel, this.avatarUrl, diff --git a/mobile/lib/features/channels/message_content.dart b/mobile/lib/features/channels/message_content.dart index b94954c00cf..f2cccbaa237 100644 --- a/mobile/lib/features/channels/message_content.dart +++ b/mobile/lib/features/channels/message_content.dart @@ -14,6 +14,8 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:video_player/video_player.dart'; import '../../shared/clipboard_utils.dart'; +import '../../shared/mentions/mention_bindings.dart'; +import '../../shared/mentions/mention_tags.dart'; import '../../shared/deeplink/deep_link.dart'; import '../../shared/deeplink/pending_deep_link_provider.dart'; import '../../shared/relay/relay.dart'; @@ -158,6 +160,12 @@ class MessageContent extends HookConsumerWidget { baseStyle ?? context.textTheme.bodyMedium?.copyWith(color: context.colors.onSurface); final resolvedMentionNames = mentionNames; + final signedMentionPubkeys = mentionedPubkeysFromTags(tags); + final mentionBindings = renderedMentionBindings( + content, + mentionNames, + signedMentionPubkeys, + ); final resolvedAgentMentionPubkeys = { ...agentMentionPubkeys.map((pubkey) => pubkey.toLowerCase()), }; @@ -196,6 +204,7 @@ class MessageContent extends HookConsumerWidget { ..sort((a, b) => a.key.compareTo(b.key)))) '${entry.key}\u0000${entry.value}', ...(resolvedAgentMentionPubkeys.toList()..sort()), + ...(signedMentionPubkeys.toList()..sort()), ].join('\u0001'); // Decided here rather than by the caller: this is where the event's own @@ -235,14 +244,15 @@ class MessageContent extends HookConsumerWidget { mentionBuf.write('`${mentionParts[i]}`'); } else { var segment = mentionParts[i]; - for (final name in resolvedMentionNames.values) { - if (name.contains(' ')) { - final normalizedName = _markdownMentionName(name); - segment = segment.replaceAllMapped( - RegExp('@${RegExp.escape(name)}', caseSensitive: false), - (m) => '@$normalizedName', - ); - } + for (final range in mentionOccurrences( + segment, + mentionBindings.keys, + ).reversed) { + segment = segment.replaceRange( + range.start, + range.end, + '@${_markdownMentionName(range.label)}', + ); } mentionBuf.write(segment); } @@ -288,6 +298,14 @@ class MessageContent extends HookConsumerWidget { inlineComponents: [ _MentionMd( mentionNames: resolvedMentionNames, + bindings: mentionBindings, + displayLabels: { + for (final range in mentionOccurrences( + content, + mentionBindings.keys, + )) + range.label: content.substring(range.start + 1, range.end), + }, agentMentionPubkeys: resolvedAgentMentionPubkeys, onMentionTap: onMentionTap, ), @@ -807,16 +825,20 @@ class _MessageCodeBlock extends HookWidget { } class _MentionMd extends InlineMd { + final Map> bindings; + final Map displayLabels; final Map mentionNames; final Set agentMentionPubkeys; final void Function(String pubkey)? onMentionTap; late final RegExp _exp = _buildPrefixPattern( prefix: '@', - knownNames: _mentionAliases(mentionNames.values), + knownNames: bindings.keys.map(_markdownMentionName), genericTokenPattern: r'[A-Za-z0-9_][A-Za-z0-9_\u00A0-]*', ); _MentionMd({ + required this.bindings, + required this.displayLabels, required this.mentionNames, required this.agentMentionPubkeys, this.onMentionTap, @@ -837,22 +859,25 @@ class _MentionMd extends InlineMd { } final name = raw.substring(1).replaceAll('\u00A0', ' ').toLowerCase(); - String? displayName; - String? pubkey; - for (final entry in mentionNames.entries) { - final entryName = entry.value.toLowerCase(); - final firstName = entryName.split(RegExp(r'\s+')).first; - if (entryName == name || firstName == name) { - displayName = entry.value; - pubkey = entry.key; - break; - } + final matches = bindings[name] ?? const {}; + final pubkey = matches.length == 1 ? matches.single : null; + if (bindings.containsKey(name) && matches.length != 1) { + return TextSpan(text: text, style: config.style); } + final displayName = name.contains(RegExp(r'\([0-9a-f]{64}\)')) + ? displayLabels[name] + : mentionNames[pubkey]; final isAgent = pubkey != null && agentMentionPubkeys.contains(pubkey.toLowerCase()); + final fullLabel = displayName ?? raw.substring(1); + final visibleLabel = fullLabel.replaceAllMapped( + RegExp(r'\(([0-9a-f]{64})\)'), + (m) => '(${m[1]!.substring(0, 8)}…${m[1]!.substring(60)})', + ); final pill = _MentionPill( - label: displayName ?? raw.substring(1), + label: visibleLabel, + semanticsLabel: fullLabel, isAgent: isAgent, textStyle: config.style, ); @@ -861,7 +886,7 @@ class _MentionMd extends InlineMd { alignment: PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic, child: pubkey != null && onMentionTap != null - ? GestureDetector(onTap: () => onMentionTap!(pubkey!), child: pill) + ? GestureDetector(onTap: () => onMentionTap!(pubkey), child: pill) : pill, ); } @@ -869,11 +894,13 @@ class _MentionMd extends InlineMd { class _MentionPill extends StatelessWidget { final String label; + final String? semanticsLabel; final bool isAgent; final TextStyle? textStyle; const _MentionPill({ required this.label, + this.semanticsLabel, required this.isAgent, this.textStyle, }); @@ -920,7 +947,9 @@ class _MentionPill extends StatelessWidget { offset: const Offset(0, -Grid.quarter), child: Text('@', style: style), ), - Text(label, style: style), + Flexible( + child: Text(label, style: style, semanticsLabel: semanticsLabel), + ), ], ), ); @@ -928,15 +957,3 @@ class _MentionPill extends StatelessWidget { } String _markdownMentionName(String name) => name.replaceAll(' ', '\u00A0'); - -Iterable _mentionAliases(Iterable mentionNames) sync* { - for (final name in mentionNames) { - final trimmed = name.trim(); - if (trimmed.isEmpty) continue; - yield _markdownMentionName(trimmed); - final firstName = trimmed.split(RegExp(r'\s+')).first; - if (firstName.isNotEmpty) { - yield firstName; - } - } -} diff --git a/mobile/lib/shared/mentions/mention_bindings.dart b/mobile/lib/shared/mentions/mention_bindings.dart new file mode 100644 index 00000000000..8b2305c1328 --- /dev/null +++ b/mobile/lib/shared/mentions/mention_bindings.dart @@ -0,0 +1,88 @@ +/// Reserve an exact label without retargeting an earlier selection. +String selectedMentionLabel( + String name, + String pubkey, + Map bindings, +) { + final normalized = { + for (final e in bindings.entries) + e.key.toLowerCase(): e.value.toLowerCase(), + }; + bool conflicts(String label) => + normalized.containsKey(label.toLowerCase()) && + normalized[label.toLowerCase()] != pubkey.toLowerCase(); + if (!conflicts(name)) return name; + final qualified = '$name (${pubkey.toLowerCase()})'; + var label = qualified; + for (var suffix = 2; conflicts(label); suffix++) { + label = '$qualified $suffix'; + } + return label; +} + +/// Longest literal ranges win, including labels containing another @ sign. +/// Recognition precedes eligibility: ambiguous labels still block shorter ones. +List<({int start, int end, String label})> mentionOccurrences( + String text, + Iterable labels, +) { + final matches = {}; + for (final label in labels) { + if (label.isEmpty) continue; + // A qualifier and reservation suffix belong to the literal even when no + // candidate binds it. Never fall back to a shorter, different recipient. + final suffix = + RegExp(r' \([0-9a-f]{64}\)$', caseSensitive: false).hasMatch(label) + ? r'(?! (?:[2-9]|[1-9][0-9]+)(?=[\s,;.!?:)\]}*_]|$))' + : ''; + final pattern = RegExp( + '(?:^|\\s|[*_]{1,3}|\\|\\|)(@${RegExp.escape(label)})(?! \\([0-9a-f]{64}\\))$suffix(?=\\|\\||[\\s,;.!?:)\\]}*_]|\$)', + caseSensitive: false, + ); + for (final match in pattern.allMatches(text)) { + final start = match.end - match.group(1)!.length; + if (matches[start] == null || matches[start]!.end < match.end) { + matches[start] = (start: start, end: match.end, label: label); + } + } + } + final result = <({int start, int end, String label})>[]; + for (final match + in matches.values.toList()..sort((a, b) => a.start.compareTo(b.start))) { + if (result.isEmpty || match.start >= result.last.end) result.add(match); + } + return result; +} + +/// Bind tagged qualifiers and ordinary aliases, never historical namesakes. +Map> renderedMentionBindings( + String content, + Map names, [ + Iterable signedPubkeys = const [], +]) { + final bindings = >{}; + void add(String label, String key) => + (bindings[label.toLowerCase()] ??= {}).add(key.toLowerCase()); + for (final entry in names.entries) { + add(entry.value, entry.key); + add(entry.value.split(RegExp(r'\s+')).first, entry.key); + } + final keys = signedPubkeys.map((key) => key.toLowerCase()).toSet(); + for (final match in RegExp( + r'@([^@\r\n]+) \(([0-9a-f]{64})\)(?: ((?:[1-9][0-9]+|[2-9])))?', + caseSensitive: false, + ).allMatches(content)) { + final key = match.group(2)!.toLowerCase(); + final label = match.group(0)!.substring(1).toLowerCase(); + if (!mentionOccurrences(content, [ + label, + ]).any((range) => range.start == match.start)) { + continue; + } + // Text can deny historical aliases without a profile, never authorize keys. + bindings[label] = keys.contains(key) ? {key} : {}; + // Recipient tags/current names cannot establish the historical plain owner. + bindings[match.group(1)!.toLowerCase()] = {}; + } + return bindings; +} diff --git a/mobile/test/features/channels/channel_detail_page_test.dart b/mobile/test/features/channels/channel_detail_page_test.dart index 23f547dbad0..7159fb2fd1e 100644 --- a/mobile/test/features/channels/channel_detail_page_test.dart +++ b/mobile/test/features/channels/channel_detail_page_test.dart @@ -476,6 +476,76 @@ void main() { _testPrefs = await SharedPreferences.getInstance(); }); + for (final thread in [false, true]) { + for (final reverse in [false, true]) { + testWidgets('signed qualified caller thread=$thread reverse=$reverse', ( + tester, + ) async { + final first = 'a' * 64, second = 'b' * 64, sibling = 'c' * 64; + Future tapProfile(String label, String key) async { + await tester.tap(find.text(label)); + await tester.pumpAndSettle(); + expect( + tester + .widget(find.byType(UserProfileSheet)) + .pubkey, + key, + ); + await tester.tap(find.byTooltip('Close sheet')); + await tester.pumpAndSettle(); + } + + for (final firstName in ['Scout', 'Renamed Scout', first, null]) { + for (final secondName in [null, 'Scout', 'Renamed Scout', second]) { + for (final bystander in [null, 'Bob', 'Scout']) { + final names = { + first: ?firstName, + second: ?secondName, + sibling: 'Alice', + 'd' * 64: ?bystander, + }; + final keys = [ + first, + second.toUpperCase(), + sibling, + if (bystander != null) 'd' * 64, + ]; + final event = _textMsg( + id: 'qualified', + pubkey: 'author', + content: '@Scout @Scout ($second) @Alice @Other (${'e' * 64})', + extraTags: [ + for (final key in reverse ? keys.reversed : keys) ['p', key], + ], + ); + await tester.pumpWidget( + _buildTestable( + messages: [event], + users: { + for (final e in names.entries) + e.key: UserProfile(pubkey: e.key, displayName: e.value), + }, + threadReplies: const {'qualified': []}, + initialThreadRootId: thread ? 'qualified' : null, + ), + ); + await tester.pumpAndSettle(); + await tapProfile('Scout (bbbbbbbb…bbbb)', second); + expect(find.text('Scout'), findsNothing); + expect(find.text('Bob'), findsNothing); + if (firstName != null) expect(find.text(firstName), findsNothing); + expect(find.text('Other (eeeeeeee…eeee)'), findsNothing); + await tapProfile('Alice', sibling); + expect(tester.takeException(), isNull); + await tester.pumpWidget(const SizedBox.shrink()); + await tester.pumpAndSettle(); + } + } + } + }); + } + } + group('ChannelDetailPage', () { testWidgets( 'bot-role author avatars stay squircles in channel and thread', diff --git a/mobile/test/features/channels/compose_bar_test.dart b/mobile/test/features/channels/compose_bar_test.dart index b8025112a25..34a91e7ea3b 100644 --- a/mobile/test/features/channels/compose_bar_test.dart +++ b/mobile/test/features/channels/compose_bar_test.dart @@ -16,6 +16,7 @@ import 'package:nostr/nostr.dart' as nostr; import 'package:buzz/features/channels/channel.dart'; import 'package:buzz/features/channels/channel_management_provider.dart'; import 'package:buzz/features/channels/compose_bar.dart'; +import 'package:buzz/features/channels/send_message_provider.dart'; import 'package:buzz/features/channels/channels_provider.dart'; import 'package:buzz/features/channels/photo_library.dart'; import 'package:buzz/features/channels/voice_note_play_pause_icon.dart'; @@ -25,6 +26,8 @@ import 'package:buzz/shared/custom_emoji/custom_emoji.dart'; import 'package:buzz/shared/custom_emoji/custom_emoji_provider.dart'; import 'package:buzz/shared/mentions/agent_identity_provider.dart'; import 'package:buzz/shared/relay/relay.dart'; +import 'package:buzz/shared/profile/user_cache_provider.dart'; +import 'package:buzz/shared/profile/user_profile.dart'; import 'package:buzz/shared/theme/theme.dart'; import 'package:buzz/shared/utils/string_utils.dart'; import 'package:buzz/shared/widgets/anchored_popover_menu.dart'; @@ -32,6 +35,8 @@ import 'package:buzz/shared/widgets/avatar_image.dart'; import 'package:buzz/shared/widgets/mobile_tab_footer_backdrop.dart'; import 'package:shared_preferences/shared_preferences.dart'; +part 'compose_bar_test/exact_mention_tests.dart'; + final _pngBytes = Uint8List.fromList([ 0x89, 0x50, @@ -643,6 +648,7 @@ class _FakeChannelsNotifier extends ChannelsNotifier { } void main() { + exactMentionTests(); TestWidgetsFlutterBinding.ensureInitialized(); setUp(() async { diff --git a/mobile/test/features/channels/compose_bar_test/exact_mention_tests.dart b/mobile/test/features/channels/compose_bar_test/exact_mention_tests.dart new file mode 100644 index 00000000000..e7b04ddfdb2 --- /dev/null +++ b/mobile/test/features/channels/compose_bar_test/exact_mention_tests.dart @@ -0,0 +1,312 @@ +part of '../compose_bar_test.dart'; + +void exactMentionTests() { + final first = 'a' * 64; + final second = 'b' * 64; + List members() => [ + for (final key in [first, second]) + ChannelMember( + pubkey: key, + displayName: 'Scout', + role: 'member', + joinedAt: DateTime(2025), + ), + ]; + Future mountComposer( + WidgetTester tester, + List roster, + ComposeBarOnSend onSend, { + nostr.Keys? signer, + }) async { + await tester.pumpWidget( + _buildComposeBar( + uploadService: _testUploadService( + (signer ?? nostr.Keys.generate()).nsec, + ), + members: roster, + channels: [_makeCurrentChannel()], + currentPubkey: signer?.public, + relayConfig: signer == null + ? null + : () => _SwitchableRelayConfigNotifier( + RelayConfig( + baseUrl: 'https://relay.example', + nsec: signer.nsec, + ), + ), + onSend: onSend, + ), + ); + if (find.byType(TextField).evaluate().isEmpty && + find.textContaining('@Scout').evaluate().isNotEmpty) { + // A restored draft displays its text, not the empty composer hint. + await tester.tap(find.textContaining('@Scout')); + await tester.pumpAndSettle(); + } else { + await _expandComposer(tester); + } + } + + Future pick( + WidgetTester tester, + String text, { + bool last = false, + }) async { + await tester.enterText(find.byType(TextField), text); + await tester.pumpAndSettle(); + await tester.tap(last ? find.text('Scout').last : find.text('Scout').first); + await tester.pumpAndSettle(); + } + + for (final scenario in [ + 'rename', + 'map edited', + 'map reset', + 'map plain', + 'map reselected', + 'removed', + 'legacy', + 'malformed', + 'tainted', + 'non-member human', + 'non-member denied agent', + ]) { + testWidgets('persisted selection restore to signed send: $scenario', ( + tester, + ) async { + final signer = nostr.Keys.generate(); + final events = >[]; + late SendMessage sendMessage; + Future mount(List roster) async { + await mountComposer( + tester, + roster, + (text, keys, {mediaTags = const []}) => sendMessage( + channelId: 'channel-1', + content: text, + mentionPubkeys: keys, + mediaTags: mediaTags, + ), + signer: signer, + ); + final container = ProviderScope.containerOf( + tester.element(find.byType(ComposeBar)), + ); + final session = container.read(relaySessionProvider.notifier); + session.debugAttachSocketForTest( + _RecordingRelaySocket( + events, + session.debugHandleSocketMessageForTest, + ), + ); + sendMessage = SendMessage( + signedEventRelay: SignedEventRelay( + session: session, + nsec: signer.nsec, + ), + fetchMembers: (_) async => roster, + readUserCache: () => const {}, + addLocalMessage: (_, _) {}, + completeLocalMessage: (_, _) {}, + removeLocalMessage: (_, _) {}, + ); + } + + await mount(members()); + await pick(tester, '@'); + final prefsKey = + 'compose_drafts_v1:https://relay.example:${signer.public}'; + final saved = jsonDecode(_testPrefs.getString(prefsKey)!) as List; + expect(saved.single['text'], '@Scout '); + expect(saved.single['mention_keys'], {'Scout': first}); + // Dispose the actual provider state, not just the text controller. + await tester.pumpWidget(const SizedBox.shrink()); + await tester.pumpAndSettle(); + if (scenario.startsWith('map ')) saved.single['mention_keys'] = null; + if (scenario == 'legacy') saved.single.remove('mention_keys'); + if (scenario == 'malformed') { + saved.single['mention_keys'] = {'Scout': 'not-a-key'}; + } + if (scenario == 'tainted') { + saved.single['mention_keys'] = { + 'Scout': {'pubkey': second, 'is_agent': true}, + }; + } + await _testPrefs.setString(prefsKey, jsonEncode(saved)); + await mount([ + if (![ + 'removed', + 'non-member human', + 'non-member denied agent', + ].contains(scenario)) + ChannelMember( + pubkey: first, + displayName: 'Renamed', + role: 'member', + joinedAt: DateTime(2025), + ), + members().last, + ]); + if (scenario.startsWith('non-member')) { + ProviderScope.containerOf(tester.element(find.byType(ComposeBar))) + .read(userCacheProvider.notifier) + .put( + UserProfile( + pubkey: first, + displayName: 'Renamed', + ownerPubkey: scenario == 'non-member denied agent' + ? second + : null, + ), + ); + await tester.pumpAndSettle(); + } + expect( + tester.widget(find.byType(TextField)).controller!.text, + '@Scout ', + ); + var expectedText = '@Scout '; + if (scenario.startsWith('map ')) { + expectedText = '@Scout hello'; + await tester.enterText(find.byType(TextField), expectedText); + await tester.pumpAndSettle(); + expect( + jsonDecode(_testPrefs.getString(prefsKey)!).single['mention_keys'], + {'': ''}, + ); + await tester.pumpWidget(const SizedBox.shrink()); + await tester.pumpAndSettle(); + await mount(members()); + if (scenario != 'map edited') { + expectedText = scenario == 'map plain' ? 'hello' : ''; + await tester.enterText(find.byType(TextField), expectedText); + await tester.pumpAndSettle(); + if (scenario != 'map plain') { + await pick(tester, '@', last: scenario == 'map reselected'); + expectedText = '@Scout '; + } + } + } + await tester.tap(find.byIcon(LucideIcons.arrowUp)); + await tester.pumpAndSettle(); + if (scenario == 'non-member human') { + expect( + find.textContaining('Renamed is not in this channel'), + findsOneWidget, + ); + await tester.tap(find.text('Do nothing')); + await tester.pumpAndSettle(); + } + final messages = events.where( + (e) => e['kind'] == EventKind.streamMessage, + ); + if ([ + 'rename', + 'legacy', + 'non-member human', + 'map reset', + 'map plain', + 'map reselected', + ].contains(scenario)) { + final event = messages.single; + expect(event['content'], expectedText.trim()); + expect((event['tags'] as List).where((t) => t[0] == 'p').toList(), [ + if (scenario != 'non-member human' && scenario != 'map plain') + [ + 'p', + ['legacy', 'map reselected'].contains(scenario) ? second : first, + ], + ]); + if (scenario == 'non-member human') { + expect( + (event['tags'] as List).where((t) => t[0] == 'mention').toList(), + [ + ['mention', first], + ], + ); + expect(events.where((e) => e['kind'] == 9000), isEmpty); + } + expect(event['pubkey'], signer.public); + expect(event['sig'], matches(RegExp(r'^[0-9a-f]{128}$'))); + expect(nostr.Event.fromMap(event).id, event['id']); + } else { + expect(messages, isEmpty); + expect(events.where((e) => e['kind'] == 9000), isEmpty); + expect(find.textContaining('Saved mention'), findsOneWidget); + expect( + tester.widget(find.byType(TextField)).controller!.text, + expectedText, + ); + } + // Refusal leaves autocomplete open. Dispose before draining its existing + // 250ms search debounce; no send assertion depends on this cleanup pump. + await tester.pumpWidget(const SizedBox.shrink()); + await tester.pump(const Duration(milliseconds: 250)); + }); + } + testWidgets( + 'same-name picker selections retain exact recipients through removal', + (tester) async { + List? sent; + await mountComposer( + tester, + members(), + (_, keys, {mediaTags = const []}) async => sent = keys, + ); + await pick(tester, '@'); + final controller = tester + .widget(find.byType(TextField)) + .controller!; + expect(controller.text, '@Scout '); + await pick(tester, '@Scout @', last: true); + expect(controller.text, '@Scout @Scout ($second) '); + await tester.tap(find.byIcon(LucideIcons.arrowUp)); + await tester.pumpAndSettle(); + expect(sent, [first, second]); + await pick(tester, '@'); + await pick(tester, '@Scout @', last: true); + await tester.enterText(find.byType(TextField), '@Scout ($second) '); + await tester.pumpAndSettle(); + await tester.tap(find.byIcon(LucideIcons.arrowUp)); + await tester.pumpAndSettle(); + expect(sent, [second]); + }, + ); + + testWidgets('unbound qualified text cannot notify a shorter member alias', ( + tester, + ) async { + List? sent; + await mountComposer(tester, [ + members().first, + ], (_, keys, {mediaTags = const []}) async => sent = keys); + await tester.enterText(find.byType(TextField), '@Scout ($second)'); + await tester.pumpAndSettle(); + await tester.tap(find.byIcon(LucideIcons.arrowUp)); + await tester.pumpAndSettle(); + expect(sent, isEmpty); + }); + + testWidgets('ambiguous typed names fail visibly without clearing the draft', ( + tester, + ) async { + var sent = false; + await mountComposer(tester, members(), ( + _, + _, { + mediaTags = const [], + }) async { + sent = true; + }); + await tester.enterText(find.byType(TextField), '@Scout hello'); + await tester.pumpAndSettle(); + await tester.tap(find.byIcon(LucideIcons.arrowUp)); + await tester.pumpAndSettle(); + expect(sent, isFalse); + expect( + tester.widget(find.byType(TextField)).controller!.text, + '@Scout hello', + ); + expect(find.textContaining('is ambiguous'), findsOneWidget); + }); +} diff --git a/mobile/test/features/channels/message_content_exact_mentions_test.dart b/mobile/test/features/channels/message_content_exact_mentions_test.dart new file mode 100644 index 00000000000..7f18431de67 --- /dev/null +++ b/mobile/test/features/channels/message_content_exact_mentions_test.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:buzz/features/channels/message_content.dart'; +import '../../helpers/widget_helpers.dart'; + +void main() { + for (final tagged in [false, true]) { + testWidgets( + 'qualified chips preserve authority and narrow layout: $tagged', + (tester) async { + tester.view.physicalSize = const Size(320, 640); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.reset); + final semantics = tester.ensureSemantics(); + final first = 'a' * 64, second = 'b' * 64; + String? tapped; + await tester.pumpWidget( + WidgetHelpers.testable( + child: MediaQuery( + data: const MediaQueryData(textScaler: TextScaler.linear(2)), + child: MessageContent( + content: '@Scout @Scout ($second)', + mentionNames: {second: 'Scout', first: 'Scout'}, + tags: [ + for (final key in [first, if (tagged) second]) ['p', key], + ], + onMentionTap: (key) => tapped = key, + ), + ), + ), + ); + await tester.pumpAndSettle(); + expect(find.text('Scout'), findsNothing); + final qualified = find.text('Scout (bbbbbbbb…bbbb)'); + if (tagged) { + await tester.tap(qualified); + expect(tapped, second); + expect( + find.bySemanticsLabel(RegExp('Scout.*$second')), + findsOneWidget, + ); + } else { + expect(qualified, findsNothing); + expect(tapped, isNull); + } + expect(tester.takeException(), isNull); + semantics.dispose(); + }, + ); + } +} diff --git a/mobile/test/shared/mentions/mention_bindings_test.dart b/mobile/test/shared/mentions/mention_bindings_test.dart new file mode 100644 index 00000000000..b4091d3b6e6 --- /dev/null +++ b/mobile/test/shared/mentions/mention_bindings_test.dart @@ -0,0 +1,56 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:buzz/shared/mentions/mention_bindings.dart'; + +void main() { + final first = 'a' * 64; + final second = 'b' * 64; + test('qualification is case-insensitive and collision-safe', () { + final bindings = {'Scout': first, 'Scout ($second)': first}; + expect(selectedMentionLabel('scout', first, bindings), 'scout'); + expect( + selectedMentionLabel('Scout', second, bindings), + 'Scout ($second) 2', + ); + }); + test('longest occurrences block shorter and interior recipients', () { + expect( + mentionOccurrences('@Scout ($second) 2', [ + 'Scout', + 'Scout ($second)', + 'Scout ($second) 2', + ]).single.label, + 'Scout ($second) 2', + ); + expect(mentionOccurrences('@A @B', ['A @B', 'B']).single.label, 'A @B'); + expect(mentionOccurrences('mail@Scout', ['Scout']), isEmpty); + expect(mentionOccurrences('@Scout ($second)', ['Scout']), isEmpty); + expect( + mentionOccurrences('@Scout ($second) 2', ['Scout ($second)']), + isEmpty, + ); + }); + // Historical plain denial and both tag orders run through channel/thread UI. + test('ordinary ambiguity and qualification require signed authority', () { + expect( + renderedMentionBindings('@Scout', { + first: 'Scout', + second: 'Scout', + })['scout'], + {first, second}, + ); + expect( + renderedMentionBindings('@Scout ($second)', { + first: 'Scout', + })['scout ($second)'], + isEmpty, + ); + expect( + renderedMentionBindings( + '@Old ($second)', + {second: 'New'}, + [second], + )['old ($second)'], + {second}, + ); + }); +} From ec11f8e2f30d921a6148d79c688414833af8b1c5 Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Fri, 11 Sep 2026 09:08:44 -0700 Subject: [PATCH 23/26] fix(avatars): scale agent squircles from normalized paths (#7307) ## Overview **Category:** fix **User Impact:** Agent avatars and empty agent-team placeholders now keep the same intentional squircle silhouette at every size on desktop, while human avatars remain circular. **Problem:** Desktop agent avatars used percentage-rounded rectangles, which made the silhouette vary by surface and required each avatar implementation to recreate the shape. Empty team placeholders also drew a regular CSS border inside the clip, leaving straight inset edges that looked like a cropped square. **Solution:** Reuse one normalized SVG clip path across desktop artwork. Empty team placeholders use a separately clipped squircle outline behind the clipped surface, so both the fill and outline follow the canonical silhouette. > **Scope correction:** Mobile is deferred from this PR because a real Flutter capture could not be obtained reliably in the current build environment. Previously attached mobile images were hand-composed synthetic layouts, not captures from the running app; they have been removed and must not be treated as validation. The mobile implementation is being reverted in one follow-up commit so it can be reapplied and verified separately. ### Related issue Follow-up to #7106. ## Changes
File changes **desktop/src/features/agents/ui/AgentCreationPreview.tsx** Applies the shared squircle clip to agent creation preview images and fallbacks. **desktop/src/features/agents/ui/AgentSessionToolItem/CompactMessageSummary.tsx** Uses the shared agent-avatar silhouette in compact session summaries. **desktop/src/features/agents/ui/IdentityInitialsAvatar.tsx** Clips agent initials with the normalized squircle instead of a percentage radius. **desktop/src/features/agents/ui/TeamIdentityCard.tsx** Uses the shared squircle for agent identities and gives empty teams a separately clipped squircle outline instead of a regular inset border. **desktop/src/features/agents/ui/activityRenderClasses/UserMessageBubble.tsx** Keeps agent avatars in activity message bubbles on the canonical silhouette. **desktop/src/features/home/ui/InboxListPane.tsx** Applies the shared clip to agent avatars in the inbox list. **desktop/src/features/home/ui/InboxMessageRow.tsx** Applies the shared clip to agent avatars in inbox message rows. **desktop/src/features/huddle/components/ParticipantList.tsx** Uses the normalized squircle for agent participants while preserving circular human avatars. **desktop/src/features/messages/ui/MessageRow.tsx** Clips agent message avatars through the shared definition. **desktop/src/features/messages/ui/SystemMessageAvatars.tsx** Uses the canonical agent shape in system-message avatar groups. **desktop/src/features/messages/ui/TypingIndicatorRow.tsx** Uses the canonical agent shape in typing indicators. **desktop/src/features/profile/ui/ProfileAvatar.tsx** Scales the same squircle to the larger agent profile avatar. **desktop/src/features/profile/ui/SelectedRecipientChip.tsx** Uses the shared shape for selected agent recipients. **desktop/src/features/projects/ui/IssueAssigneesRow.tsx** Applies the agent squircle to issue assignees without changing human avatar geometry. **desktop/src/features/projects/ui/ProjectCards.tsx** Uses the canonical shape for agent avatars on project cards. **desktop/src/features/projects/ui/ProjectEntityListRow.tsx** Uses the canonical shape for agent identities in project entity lists. **desktop/src/features/projects/ui/ProjectsActivityFeed.tsx** Uses the shared clip for agent avatars in project activity. **desktop/src/features/projects/ui/ProjectsOverviewRail.tsx** Uses the shared clip for agent avatars in the project overview rail. **desktop/src/main.tsx** Mounts the single shared SVG clip-path definition once for the desktop application. **desktop/src/shared/lib/cn.ts** Registers `rounded-squircle` in the border-radius class group so shadcn-style `cn(...)` composition resolves it against `rounded-full` by normal last-class precedence. **desktop/src/shared/styles/globals/utilities.css** Registers the reusable Tailwind `rounded-squircle` utility and references the normalized SVG clip path without agent-specific CSS selectors. **desktop/src/shared/ui/AvatarClipPaths.tsx** Owns the reusable normalized object-bounding-box `rounded-squircle` path so all desktop sizes reuse one definition. **desktop/src/shared/ui/UserAvatar.tsx** Routes the existing squircle shape option through the shared SVG clip. **desktop/tests/e2e/agents.spec.ts** Verifies team facepile and empty-team placeholder surfaces/outlines use the canonical clip without a rectangular border. **desktop/tests/e2e/messaging.spec.ts** Verifies agent message and profile avatars reuse one clip definition and scale across rendered sizes.
## Reproduction steps 1. Run the desktop app and open a channel containing both agent and human messages. 2. Compare agent avatars in the message list, inbox, huddle participants, projects, and profile panel. Agent images and fallbacks should share one smooth squircle silhouette at each size; human avatars should remain circular. 3. Open **Agents** and inspect an empty agent-team card. Its placeholder fill and outline should both be smooth squircles, with no straight inset cropped-square edges. 4. Open an agent profile from a message and confirm the larger profile avatar keeps the same proportions as the smaller message avatar. ## Testing - Desktop Biome check passed for the changed empty-team component and Agents E2E spec. - Desktop typecheck and differential file-size check passed. - Full desktop unit suite passed: 6,453 tests. - Full Agents integration spec passed: 37/37, including the empty-team outline regression. - Post-rebase pre-push gates passed at `304b159a2`: desktop checks, TypeScript typecheck, differential file-size gate, and all 6,453 desktop unit tests. - The shadcn-aligned `rounded-squircle` registration passed changed-file Biome, desktop TypeScript/build, and pre-push desktop checks at `27e0c12d3`; the full desktop unit suite passed 6,459/6,459. ## Screenshots Screenshots are temporarily omitted. The previous captures used the desktop E2E mock bridge and were not evidence from a live running app, so they were removed. --------- Signed-off-by: Taylor Ho Co-authored-by: Carl Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> --- .../src-tauri/src/managed_agents/personas.rs | 6 +- desktop/src-tauri/src/migration.rs | 40 +++- .../src-tauri/src/migration_avatar_tests.rs | 34 ++++ .../agents/ui/AgentCreationPreview.tsx | 57 ++++-- .../agents/ui/AgentRuntimeAvatarControl.tsx | 4 +- .../agents/ui/IdentityInitialsAvatar.tsx | 2 +- .../features/agents/ui/TeamIdentityCard.tsx | 13 +- .../agents/ui/managedAgentAvatar.test.mjs | 18 +- .../features/agents/ui/managedAgentAvatar.ts | 9 +- .../agents/ui/snapshotAvatarPng.test.mjs | 2 +- .../features/agents/ui/snapshotAvatarPng.ts | 33 +--- .../features/channels/ui/BotActivityBar.tsx | 1 + .../src/features/home/ui/InboxListPane.tsx | 13 +- .../src/features/home/ui/InboxMessageRow.tsx | 11 +- .../huddle/components/ParticipantList.tsx | 22 ++- .../messages/ui/MessageThreadSummaryRow.tsx | 50 +++-- .../messages/ui/TypingIndicatorRow.tsx | 2 +- .../profile/ui/MaskedAvatarBadgeFrame.tsx | 172 +++++++++++++--- .../src/features/profile/ui/ProfileAvatar.tsx | 8 +- .../ui/ProfileAvatarEditor.utils.test.mjs | 70 +++++++ .../profile/ui/ProfileAvatarEditor.utils.ts | 38 ++-- .../profile/ui/ProfileAvatarWithStatus.tsx | 3 +- .../profile/ui/SelectedRecipientChip.tsx | 6 +- .../profile/ui/UserProfilePanelSections.tsx | 2 +- .../profile/ui/UserProfilePopover.tsx | 12 +- .../projects/ui/IssueAssigneesRow.tsx | 7 +- .../projects/ui/ProjectEntityListRow.tsx | 48 ++--- .../projects/ui/ProjectsOverviewRail.tsx | 2 +- .../features/sidebar/ui/SidebarSection.tsx | 1 + desktop/src/main.tsx | 2 + desktop/src/shared/lib/cn.ts | 1 + .../src/shared/styles/globals/components.css | 15 +- .../src/shared/styles/globals/utilities.css | 6 + desktop/src/shared/ui/AvatarClipPaths.tsx | 99 ++++++++++ desktop/src/shared/ui/UserAvatar.tsx | 13 +- desktop/tests/e2e/agents.spec.ts | 101 +++++++++- desktop/tests/e2e/channels.spec.ts | 64 +++++- .../tests/e2e/huddle-transcription.spec.ts | 29 +++ desktop/tests/e2e/mentions.spec.ts | 47 +++++ desktop/tests/e2e/messaging.spec.ts | 141 ++++++++++++- .../tests/e2e/project-issue-comments.spec.ts | 12 ++ desktop/tests/e2e/project-pr-review.spec.ts | 185 ++++++++++++++++-- 42 files changed, 1175 insertions(+), 226 deletions(-) create mode 100644 desktop/src/features/profile/ui/ProfileAvatarEditor.utils.test.mjs create mode 100644 desktop/src/shared/ui/AvatarClipPaths.tsx diff --git a/desktop/src-tauri/src/managed_agents/personas.rs b/desktop/src-tauri/src/managed_agents/personas.rs index 094d0a1a478..e4c71483d8c 100644 --- a/desktop/src-tauri/src/managed_agents/personas.rs +++ b/desktop/src-tauri/src/managed_agents/personas.rs @@ -15,9 +15,9 @@ struct BuiltInPersona { default_active: bool, } -const FIZZ_AVATAR: &str = "data:image/png;base64,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"; -const HONEY_AVATAR: &str = "data:image/png;base64,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"; -const BUMBLE_AVATAR: &str = "data:image/png;base64,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"; +const FIZZ_AVATAR: &str = "data:image/png;base64,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"; +const HONEY_AVATAR: &str = "data:image/png;base64,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"; +const BUMBLE_AVATAR: &str = "data:image/png;base64,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"; const FIZZ_SYSTEM_PROMPT: &str = "You are Fizz, an energetic maker who turns ideas into action. Be upbeat, practical, and decisive. Help users plan, create, solve problems, and finish work. Add occasional bee wordplay or 🐝✨—keep it charming, never distracting."; diff --git a/desktop/src-tauri/src/migration.rs b/desktop/src-tauri/src/migration.rs index 9b105e94d4f..02efd3d91b9 100644 --- a/desktop/src-tauri/src/migration.rs +++ b/desktop/src-tauri/src/migration.rs @@ -525,6 +525,8 @@ struct LegacyBuiltInAvatar<'a> { } const LEGACY_BUILTIN_AVATARS: &[LegacyBuiltInAvatar<'static>] = &[ + // Original circular portraits replaced in #2215. Keep these entries so an + // install that skips directly over that release still migrates cleanly. LegacyBuiltInAvatar { persona_id: "builtin:fizz", data_url_sha256: "2771b8c9c46aa3c8ac1c4d2acfa23fa9ba35b79c4b1694554e923081e3b8b4d0", @@ -543,6 +545,25 @@ const LEGACY_BUILTIN_AVATARS: &[LegacyBuiltInAvatar<'static>] = &[ sanitized_media_sha256: "9f798c61f8965b80beb808f505feb0a5b33726545188ea8212cc9ab22d05f0b6", persona_content_hash: "544a73f9106a3c8848b0f308b7a8b6f95077ac8deccdb9ed5552caa833d66c95", }, + // Circular 384px portraits replaced by the square 128px agent artwork. + LegacyBuiltInAvatar { + persona_id: "builtin:fizz", + data_url_sha256: "6cdf8c338d27ef3c9dd68cf4839f3d62dec2298e2d4e07b851f26f8ba289b377", + sanitized_media_sha256: "e6025a21c864750f4b3828c9035bafdbe68ec73bd247745c78309b82fedcbed5", + persona_content_hash: "d30d9c7105be0765a2c5ebe89fdb67842201409e79a4b7937f46e2772411d925", + }, + LegacyBuiltInAvatar { + persona_id: "builtin:honey", + data_url_sha256: "7b51bb3e353aed13b5d6a01d0ad9aa58d63b6bb347f2ed6ee891e7af6149febe", + sanitized_media_sha256: "4a64f32d2375ccfe44c0880416bc72b809b055f0fb7d9dddeb8bb3dd6b80c297", + persona_content_hash: "e0f88abf79d6826973cc99373ddd1f8350bed1df51064f7b1a0d7e3d7984c49e", + }, + LegacyBuiltInAvatar { + persona_id: "builtin:bumble", + data_url_sha256: "9e1e841a76069202474393b786ada0628d90711703f8ba96c9ecee84a1c6a593", + sanitized_media_sha256: "99d32de5791ec07db70c761db30fd145422e6c8200ba8d189363c3ce53273758", + persona_content_hash: "783ee80629046d28f4f07095b1ceedab129661d40a2d5560806bc487a1a65a11", + }, ]; struct LegacyAvatarMatch<'a> { @@ -674,20 +695,21 @@ fn legacy_avatar_match<'a>( .get("persona_id") .and_then(serde_json::Value::as_str) .or_else(|| record.get("slug").and_then(serde_json::Value::as_str))?; - let metadata = legacy_avatars - .iter() - .find(|legacy| legacy.persona_id == persona_id)?; let current_avatar = record .get("avatar_url") .and_then(serde_json::Value::as_str)?; - let matches_data_url = - hex::encode(Sha256::digest(current_avatar.as_bytes())) == metadata.data_url_sha256; - let matches_uploaded_media = uploaded_media_sha256(current_avatar) - .is_some_and(|sha256| sha256 == metadata.sanitized_media_sha256); - (matches_data_url || matches_uploaded_media).then(|| LegacyAvatarMatch { + let data_url_sha256 = hex::encode(Sha256::digest(current_avatar.as_bytes())); + let uploaded_media_sha256 = uploaded_media_sha256(current_avatar); + let metadata = legacy_avatars.iter().find(|legacy| { + legacy.persona_id == persona_id + && (legacy.data_url_sha256 == data_url_sha256 + || uploaded_media_sha256.as_deref() == Some(legacy.sanitized_media_sha256)) + })?; + let was_uploaded = uploaded_media_sha256.as_deref() == Some(metadata.sanitized_media_sha256); + Some(LegacyAvatarMatch { persona_id: persona_id.to_string(), metadata, - was_uploaded: matches_uploaded_media, + was_uploaded, }) } diff --git a/desktop/src-tauri/src/migration_avatar_tests.rs b/desktop/src-tauri/src/migration_avatar_tests.rs index 2573ce2d566..612afc5d942 100644 --- a/desktop/src-tauri/src/migration_avatar_tests.rs +++ b/desktop/src-tauri/src/migration_avatar_tests.rs @@ -161,6 +161,40 @@ fn current_builtin_agent_avatars_do_not_match_legacy_hashes() { } } +#[test] +fn legacy_avatar_match_checks_every_known_generation_for_a_persona() { + use sha2::{Digest as _, Sha256}; + + let prior_avatar = "data:image/png;base64,prior-fizz"; + let prior_hash = hex::encode(Sha256::digest(prior_avatar.as_bytes())); + let legacy_avatars = [ + LegacyBuiltInAvatar { + persona_id: "builtin:fizz", + data_url_sha256: "older-fizz-hash", + sanitized_media_sha256: "older-upload-hash", + persona_content_hash: "older-persona-version", + }, + LegacyBuiltInAvatar { + persona_id: "builtin:fizz", + data_url_sha256: prior_hash.as_str(), + sanitized_media_sha256: "prior-upload-hash", + persona_content_hash: "prior-persona-version", + }, + ]; + let record = serde_json::json!({ + "slug": "builtin:fizz", + "avatar_url": prior_avatar, + }); + + let matched = legacy_avatar_match(&record, &legacy_avatars).unwrap(); + + assert_eq!( + matched.metadata.persona_content_hash, + "prior-persona-version" + ); + assert!(!matched.was_uploaded); +} + #[test] fn refresh_builtin_agent_avatars_updates_versions_without_stored_definitions() { use sha2::{Digest as _, Sha256}; diff --git a/desktop/src/features/agents/ui/AgentCreationPreview.tsx b/desktop/src/features/agents/ui/AgentCreationPreview.tsx index 2856f56e789..a213c9d46a8 100644 --- a/desktop/src/features/agents/ui/AgentCreationPreview.tsx +++ b/desktop/src/features/agents/ui/AgentCreationPreview.tsx @@ -28,6 +28,7 @@ import { AvatarCustomColorPanel } from "@/features/profile/ui/AvatarCustomColorP import { useAvatarUpload } from "@/features/profile/useAvatarUpload"; import { cn } from "@/shared/lib/cn"; import { Button } from "@/shared/ui/button"; +import { ROUNDED_SQUIRCLE_PATH } from "@/shared/ui/AvatarClipPaths"; import { useEmojiBurst } from "@/shared/ui/EmojiBurstProvider"; import { Popover, @@ -107,7 +108,6 @@ export function AgentCreationPreview({ assetLabel.charAt(0).toUpperCase() + assetLabel.slice(1); const isRoundedSquare = shape === "rounded-square"; const isCompact = variant === "compact"; - const emojiShape = isRoundedSquare ? "rounded-square" : "circle"; const { inputRef: avatarUploadInputRef, isUploading, @@ -197,11 +197,7 @@ export function AgentCreationPreview({ if (!isCustomColorPickerOpen || !selectedEmoji) { return; } - const nextAvatarUrl = emojiAvatarDataUrl( - selectedEmoji, - customColorDraft, - emojiShape, - ); + const nextAvatarUrl = emojiAvatarDataUrl(selectedEmoji, customColorDraft); if (avatarUrl === nextAvatarUrl) { return; } @@ -212,7 +208,6 @@ export function AgentCreationPreview({ isCustomColorPickerOpen, onSelectAvatar, selectedEmoji, - emojiShape, ]); function applyAvatarUrl() { @@ -227,7 +222,7 @@ export function AgentCreationPreview({ } function applyEmojiAvatar(emoji: string, color = selectedColor) { - const nextAvatarUrl = emojiAvatarDataUrl(emoji, color, emojiShape); + const nextAvatarUrl = emojiAvatarDataUrl(emoji, color); onSelectAvatar(nextAvatarUrl); onCommitAvatar?.(nextAvatarUrl); setSquishKey((key) => key + 1); @@ -724,7 +719,7 @@ export function AgentCreationPreview({ ? isCompact ? "rounded-2xl" : "rounded-[2rem]" - : "rounded-[30%]", + : "rounded-squircle", )} role="img" style={{ backgroundColor: emojiAvatarPreview.color }} @@ -924,19 +919,19 @@ export function AgentCreationPreview({ } className={isCompact ? "h-16 w-16" : "h-36 w-36"} clipTestId={`${testIdPrefix}-mask`} - cornerRadius={(isCompact ? 64 : 144) * 0.3} cutout={ isCompact ? { cx: 58, cy: 58, r: 16.5 } : { cx: 123, cy: 123, r: 24 } } maskMode={isCompact ? "radial" : "clip-path"} + shape="squircle" size={isCompact ? 64 : 144} > {emojiAvatarPreview ? (
+ {isRoundedSquare ? null : ( + + )} {isUploading ? ( ) : (
@@ -163,7 +168,7 @@ function TeamAvatarItem({ return (
0 ? "-ml-5" : ""}`} + className={`relative h-14 w-14 before:rounded-squircle before:absolute before:-inset-0.5 before:bg-card before:content-[''] ${index > 0 ? "-ml-5" : ""}`} data-team-member-avatar="avatar" style={{ zIndex: index + 1, diff --git a/desktop/src/features/agents/ui/managedAgentAvatar.test.mjs b/desktop/src/features/agents/ui/managedAgentAvatar.test.mjs index 4495872d072..c8f5b28d181 100644 --- a/desktop/src/features/agents/ui/managedAgentAvatar.test.mjs +++ b/desktop/src/features/agents/ui/managedAgentAvatar.test.mjs @@ -21,14 +21,22 @@ test("resolveManagedAgentAvatarUrl uploads data image URIs", async () => { assert.equal(uploaded, "https://relay.example/avatar.png"); }); -test("resolveManagedAgentAvatarUrl passes emoji svg data URLs through", async () => { - const emojiUrl = - "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3C%2Fsvg%3E"; - const uploaded = await resolveManagedAgentAvatarUrl(emojiUrl, async () => { +test("resolveManagedAgentAvatarUrl squares legacy emoji svg data URLs", async () => { + const legacySvg = + '🐝'; + const emojiUrl = `data:image/svg+xml,${encodeURIComponent(legacySvg)}`; + const resolved = await resolveManagedAgentAvatarUrl(emojiUrl, async () => { throw new Error("should not upload inline emoji svg data URLs"); }); - assert.equal(uploaded, emojiUrl); + assert.ok(resolved); + const normalizedSvg = decodeURIComponent(resolved.split(",", 2)[1]); + assert.match( + normalizedSvg, + //u, + ); + assert.doesNotMatch(normalizedSvg, /\brx=/u); + assert.match(normalizedSvg, />🐝<\/text>/u); }); test("resolveManagedAgentAvatarUrl passes non-data URLs through", async () => { diff --git a/desktop/src/features/agents/ui/managedAgentAvatar.ts b/desktop/src/features/agents/ui/managedAgentAvatar.ts index e700525fedb..e1f1af41728 100644 --- a/desktop/src/features/agents/ui/managedAgentAvatar.ts +++ b/desktop/src/features/agents/ui/managedAgentAvatar.ts @@ -1,3 +1,5 @@ +import { squareEmojiAvatarDataUrl } from "@/features/profile/ui/ProfileAvatarEditor.utils"; + type BlobDescriptor = { url: string; sha256: string; @@ -23,10 +25,11 @@ export async function resolveManagedAgentAvatarUrl( // Emoji avatars are stored as inline, percent-encoded SVG data URLs // (`data:image/svg+xml,%3C...`) — the same self-contained form profile - // persists. They are not base64 and must not be run through `atob`/upload; - // pass them through unchanged so the emoji survives agent creation. + // persists. They are not base64 and must not be run through `atob`/upload. + // Normalize legacy rounded source artwork to a square while creating or + // editing an agent so the consuming squircle owns the complete silhouette. if (!isBase64DataUri(resolvedAvatarUrl)) { - return resolvedAvatarUrl; + return squareEmojiAvatarDataUrl(resolvedAvatarUrl); } try { diff --git a/desktop/src/features/agents/ui/snapshotAvatarPng.test.mjs b/desktop/src/features/agents/ui/snapshotAvatarPng.test.mjs index a2b58b74068..70d066d4765 100644 --- a/desktop/src/features/agents/ui/snapshotAvatarPng.test.mjs +++ b/desktop/src/features/agents/ui/snapshotAvatarPng.test.mjs @@ -38,7 +38,7 @@ test("resolveSnapshotAvatarPng: emoji SVG is rasterized onto a canvas", async () }; const emojiSvg = - ''; + ''; const result = await resolveSnapshotAvatarPng( `data:image/svg+xml,${encodeURIComponent(emojiSvg)}`, { diff --git a/desktop/src/features/agents/ui/snapshotAvatarPng.ts b/desktop/src/features/agents/ui/snapshotAvatarPng.ts index dfeac347586..7d4a8922200 100644 --- a/desktop/src/features/agents/ui/snapshotAvatarPng.ts +++ b/desktop/src/features/agents/ui/snapshotAvatarPng.ts @@ -1,3 +1,4 @@ +import { squareEmojiAvatarDataUrl } from "@/features/profile/ui/ProfileAvatarEditor.utils"; import { fetchMediaBytes } from "@/shared/api/tauriMedia"; type SnapshotAvatarPngDependencies = { @@ -54,7 +55,7 @@ async function rasterizeSvg( ): Promise { try { const image = (dependencies.createImage ?? (() => new Image()))(); - image.src = squareEmojiAvatarBackground(svgDataUrl); + image.src = squareEmojiAvatarDataUrl(svgDataUrl); await image.decode(); const canvas = ( @@ -71,36 +72,6 @@ async function rasterizeSvg( } } -/** - * Emoji avatars use a circular SVG background in profile surfaces. Snapshot - * attachments already clip artwork to a rounded-square media slot, so remove - * that source-level circle before rasterizing to let the artwork fill the slot. - */ -function squareEmojiAvatarBackground(svgDataUrl: string) { - const commaIndex = svgDataUrl.indexOf(","); - if ( - commaIndex === -1 || - svgDataUrl.slice(0, commaIndex).includes(";base64") - ) { - return svgDataUrl; - } - - try { - const prefix = svgDataUrl.slice(0, commaIndex + 1); - const svg = decodeURIComponent(svgDataUrl.slice(commaIndex + 1)); - const squaredSvg = svg.replace( - /(]*\bwidth="512"[^>]*\bheight="512"[^>]*?)\s+rx="256"/u, - "$1", - ); - - return squaredSvg === svg - ? svgDataUrl - : `${prefix}${encodeURIComponent(squaredSvg)}`; - } catch { - return svgDataUrl; - } -} - function bytesToBase64(bytes: Uint8Array) { let binary = ""; const chunkSize = 0x8000; diff --git a/desktop/src/features/channels/ui/BotActivityBar.tsx b/desktop/src/features/channels/ui/BotActivityBar.tsx index d43b621c24b..dacdc7e89ed 100644 --- a/desktop/src/features/channels/ui/BotActivityBar.tsx +++ b/desktop/src/features/channels/ui/BotActivityBar.tsx @@ -195,6 +195,7 @@ export function BotActivityComposerAction({ fallbackDelayMs={isInline ? 0 : undefined} key={agent.pubkey} size="xs" + testId={`bot-activity-composer-avatar-${agent.pubkey}`} /> ))} diff --git a/desktop/src/features/home/ui/InboxListPane.tsx b/desktop/src/features/home/ui/InboxListPane.tsx index f0ed262a41c..d40ba26b29c 100644 --- a/desktop/src/features/home/ui/InboxListPane.tsx +++ b/desktop/src/features/home/ui/InboxListPane.tsx @@ -383,15 +383,14 @@ export function InboxListPane({ botIdenticonValue={item.senderLabel} pubkey={item.item.pubkey} role={profileRole} + triggerClassName={cn( + "shrink-0 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring", + isSenderAgent ? "rounded-[30%]" : "rounded-full", + )} triggerElement="span" + triggerTestId={`home-inbox-avatar-${item.id}`} > - + - + + {participant.isAgent ? ( + + ) : null} + ); + return (
0 && "-ml-1")} @@ -39,16 +61,19 @@ function ParticipantAvatar({ zIndex: index + 1, }} > - + {hasForegroundAvatar ? ( + + {avatar} + + ) : ( + avatar + )}
); } @@ -232,6 +257,9 @@ export function MessageThreadSummaryRow({
{summary.participants.map((participant, index) => ( 0 && "-ml-1.5", )} diff --git a/desktop/src/features/profile/ui/MaskedAvatarBadgeFrame.tsx b/desktop/src/features/profile/ui/MaskedAvatarBadgeFrame.tsx index ea6e2344932..ebef8c55199 100644 --- a/desktop/src/features/profile/ui/MaskedAvatarBadgeFrame.tsx +++ b/desktop/src/features/profile/ui/MaskedAvatarBadgeFrame.tsx @@ -3,6 +3,10 @@ import type * as React from "react"; import { motion } from "motion/react"; import { cn } from "@/shared/lib/cn"; +import { + ROUNDED_SQUIRCLE_PATH, + sampleRoundedSquircle, +} from "@/shared/ui/AvatarClipPaths"; export type AvatarBadgeCircle = { cx: number; @@ -51,8 +55,10 @@ type MaskedAvatarBadgeFrameProps = { curve?: AvatarBadgeCurve; cutout?: AvatarBadgeCircle; cutoutWidth?: number; - maskMode?: "clip-path" | "radial"; + maskMode?: "clip-path" | "none" | "radial" | "shape"; maskTransition?: React.ComponentProps["transition"]; + shape?: "circle" | "squircle"; + cutoutShape?: "circle" | "squircle"; size: number; }; @@ -560,6 +566,92 @@ function getRoundedAvatarCapsuleMaskPolygon( return `polygon(${alignedPoints.map((point) => toPolygonPoint(point, size)).join(", ")})`; } +function getShapeCutoutMask( + size: number, + cutout: AvatarBadgeCircle, + cutoutShape: "circle" | "squircle", +) { + const path = + cutoutShape === "squircle" + ? ROUNDED_SQUIRCLE_PATH + : "M .5 0 A .5 .5 0 1 1 .5 1 A .5 .5 0 1 1 .5 0 Z"; + const cutoutSize = cutout.r * 2; + const svg = ``; + return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`; +} + +function getSquircleMaskPolygon(size: number, cutout: AvatarBadgeCircle) { + const points = sampleRoundedSquircle(size, 32); + const distanceFromCutout = (point: Point) => + Math.hypot(point.x - cutout.cx, point.y - cutout.cy); + const outsideCutout = (point: Point) => distanceFromCutout(point) >= cutout.r; + const crossings: Array<{ + index: number; + outside: Point; + inside: Point; + entersCutout: boolean; + }> = []; + + for (let index = 0; index < points.length; index += 1) { + const current = points[index]; + const next = points[(index + 1) % points.length]; + if (outsideCutout(current) === outsideCutout(next)) continue; + crossings.push({ + index, + outside: outsideCutout(current) ? current : next, + inside: outsideCutout(current) ? next : current, + entersCutout: outsideCutout(current), + }); + } + + if (crossings.length !== 2) { + return `polygon(${points.map((point) => toPolygonPoint(point, size)).join(", ")})`; + } + + const intersectionPoint = ({ + outside, + inside, + }: (typeof crossings)[number]) => { + let low = outside; + let high = inside; + for (let iteration = 0; iteration < 12; iteration += 1) { + const midpoint = { + x: (low.x + high.x) / 2, + y: (low.y + high.y) / 2, + }; + if (outsideCutout(midpoint)) low = midpoint; + else high = midpoint; + } + return { + x: (low.x + high.x) / 2, + y: (low.y + high.y) / 2, + }; + }; + const enter = crossings.find((crossing) => crossing.entersCutout); + const exit = crossings.find((crossing) => !crossing.entersCutout); + if (!enter || !exit) { + return `polygon(${points.map((point) => toPolygonPoint(point, size)).join(", ")})`; + } + + const enterPoint = intersectionPoint(enter); + const exitPoint = intersectionPoint(exit); + const enterAngle = getAngle(cutout, enterPoint); + const exitAngle = getAngle(cutout, exitPoint); + let cutoutSweep = exitAngle - enterAngle; + while (cutoutSweep >= 0) cutoutSweep -= Math.PI * 2; + const cutoutPoints = Array.from({ length: 32 }, (_, index) => + getPointOnCircle(cutout, enterAngle + cutoutSweep * ((index + 1) / 32)), + ); + const maskPoints = [ + ...points.slice(0, enter.index + 1), + enterPoint, + ...cutoutPoints, + ...points.slice(exit.index + 1), + ]; + + return `polygon(${maskPoints.map((point) => toPolygonPoint(point, size)).join(", ")})`; +} + function getRoundedSquareMaskPolygon( size: number, cornerRadius: number, @@ -666,31 +758,40 @@ export function MaskedAvatarBadgeFrame({ cornerRadius, curve, cutout, + cutoutShape = "circle", cutoutWidth, maskMode = "clip-path", maskTransition, + shape = "circle", size, }: MaskedAvatarBadgeFrameProps) { - const shouldMask = Boolean(badge && badgeBox && cutout); + const shouldRenderFrame = Boolean( + cutout && + (maskMode === "radial" || maskMode === "shape" || (badge && badgeBox)), + ); + const shouldMask = maskMode !== "none" && shouldRenderFrame; const stabilizeOuterBoundary = Boolean(maskTransition); - const maskPolygon = cutout - ? cornerRadius === undefined - ? cutoutWidth && cutoutWidth > cutout.r * 2 - ? getRoundedAvatarCapsuleMaskPolygon( - size, - cutout, - cutoutWidth, - curve, - stabilizeOuterBoundary, - ) - : getRoundedAvatarMaskPolygon( - size, - cutout, - curve, - stabilizeOuterBoundary, - ) - : getRoundedSquareMaskPolygon(size, cornerRadius, cutout, curve) - : undefined; + const maskPolygon = + shouldMask && cutout + ? shape === "squircle" + ? getSquircleMaskPolygon(size, cutout) + : cornerRadius === undefined + ? cutoutWidth && cutoutWidth > cutout.r * 2 + ? getRoundedAvatarCapsuleMaskPolygon( + size, + cutout, + cutoutWidth, + curve, + stabilizeOuterBoundary, + ) + : getRoundedAvatarMaskPolygon( + size, + cutout, + curve, + stabilizeOuterBoundary, + ) + : getRoundedSquareMaskPolygon(size, cornerRadius, cutout, curve) + : undefined; const radialMask = maskMode === "radial" && cutout ? `radial-gradient(circle ${toRem(cutout.r)} at ${toRem( @@ -699,6 +800,11 @@ export function MaskedAvatarBadgeFrame({ cutout.cy, )}, transparent calc(100% - 0.03125rem), black 100%)` : undefined; + const shapeMask = + maskMode === "shape" && cutout + ? getShapeCutoutMask(size, cutout, cutoutShape) + : undefined; + const imageMask = radialMask ?? shapeMask; const sizeStyle = { height: toRem(size), width: toRem(size) }; const badgeMotionTarget = badgeBox && cutout @@ -708,7 +814,7 @@ export function MaskedAvatarBadgeFrame({ ? getBadgeStyle(badgeMotionTarget) : undefined; - if (!shouldMask) { + if (!shouldRenderFrame) { return (
{children} @@ -720,7 +826,7 @@ export function MaskedAvatarBadgeFrame({
diff --git a/desktop/src/features/profile/ui/ProfileAvatar.tsx b/desktop/src/features/profile/ui/ProfileAvatar.tsx index d8d22eaa76a..9a65a4e72a3 100644 --- a/desktop/src/features/profile/ui/ProfileAvatar.tsx +++ b/desktop/src/features/profile/ui/ProfileAvatar.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import { UserRound } from "lucide-react"; import { useAvatarPresentation } from "@/features/profile/avatarPresentationStore"; +import { avatarSourceUrlForShape } from "@/features/profile/ui/ProfileAvatarEditor.utils"; import { parseAnimatedAvatarUrl } from "@/shared/lib/animatedAvatar"; import { cn } from "@/shared/lib/cn"; import { getInitials } from "@/shared/lib/initials"; @@ -69,16 +70,17 @@ export function ProfileAvatar({ const initials = getInitials(initialsLabel ?? label); const presentation = useAvatarPresentation(avatarUrl); const presentedAvatarUrl = presentation?.displayUrl ?? avatarUrl; + const shapedAvatarUrl = avatarSourceUrlForShape(presentedAvatarUrl, shape); // Animated avatars show their static poster frame until hovered, then play // the animation. - const animated = parseAnimatedAvatarUrl(presentedAvatarUrl); + const animated = parseAnimatedAvatarUrl(shapedAvatarUrl); const [isHovered, setIsHovered] = React.useState(false); const baseUrl = animated ? isHovered ? animated.animationUrl : animated.posterUrl - : presentedAvatarUrl; + : shapedAvatarUrl; // Compute the live (proxied) source. Failures are tracked per resolved URL so // the poster and hover animation can recover independently. Under `untrusted` @@ -108,7 +110,7 @@ export function ProfileAvatar({ { + const avatarUrl = emojiAvatarDataUrl("✨", "#7657FF"); + const svg = decodeSvgDataUrl(avatarUrl); + + assert.match(svg, //u); + assert.doesNotMatch(svg, /\brx=/u); + assert.deepEqual(parseEmojiAvatarDataUrl(avatarUrl), { + color: "#7657FF", + emoji: "✨", + }); +}); + +test("squareEmojiAvatarDataUrl upgrades known legacy rounded artwork", () => { + for (const radius of [112, 256]) { + const legacySvg = `🐝`; + const upgraded = squareEmojiAvatarDataUrl( + `data:image/svg+xml,${encodeURIComponent(legacySvg)}`, + ); + const svg = decodeSvgDataUrl(upgraded); + + assert.match(svg, //u); + assert.doesNotMatch(svg, /\brx=/u); + assert.match(svg, />🐝<\/text>/u); + } +}); + +test("squircle consumers normalize known legacy emoji artwork", () => { + const legacySvg = + '🐝'; + const legacyUrl = `data:image/svg+xml,${encodeURIComponent(legacySvg)}`; + const resolved = avatarSourceUrlForShape(legacyUrl, "squircle"); + const svg = decodeSvgDataUrl(resolved); + + assert.doesNotMatch(svg, /\brx=/u); + assert.match(svg, />🐝<\/text>/u); + assert.equal(avatarSourceUrlForShape(legacyUrl, "circle"), legacyUrl); +}); + +test("squareEmojiAvatarDataUrl preserves custom inline SVG artwork", () => { + const customSvgs = [ + 'Buzz', + 'ACME', + ]; + + for (const customSvg of customSvgs) { + const avatarUrl = `data:image/svg+xml,${encodeURIComponent(customSvg)}`; + + assert.equal(parseEmojiAvatarDataUrl(avatarUrl), null); + assert.equal(squareEmojiAvatarDataUrl(avatarUrl), avatarUrl); + } +}); + +test("squareEmojiAvatarDataUrl leaves non-emoji images unchanged", () => { + const avatarUrl = "https://relay.example/media/avatar.png"; + assert.equal(squareEmojiAvatarDataUrl(avatarUrl), avatarUrl); +}); diff --git a/desktop/src/features/profile/ui/ProfileAvatarEditor.utils.ts b/desktop/src/features/profile/ui/ProfileAvatarEditor.utils.ts index 03b5e38026d..8c6705947a7 100644 --- a/desktop/src/features/profile/ui/ProfileAvatarEditor.utils.ts +++ b/desktop/src/features/profile/ui/ProfileAvatarEditor.utils.ts @@ -253,16 +253,31 @@ function unescapeSvgText(text: string) { .replace(/&/gu, "&"); } -export function emojiAvatarDataUrl( - emoji: string, - color: string, - shape: "circle" | "rounded-square" = "circle", -) { - const cornerRadius = shape === "rounded-square" ? 112 : 256; - const svg = `${escapeSvgText(emoji)}`; +export function emojiAvatarDataUrl(emoji: string, color: string) { + // Persist uncropped artwork. The consuming surface owns the silhouette: + // human profiles clip this square to a circle, while agents clip it to the + // shared squircle. Source-level rounding cannot be recovered by an outer + // clip and made legacy emoji agents look circular inside their squircles. + const svg = `${escapeSvgText(emoji)}`; return `${EMOJI_AVATAR_DATA_URL_PREFIX}${encodeURIComponent(svg)}`; } +export function squareEmojiAvatarDataUrl(avatarUrl: string) { + const descriptor = parseEmojiAvatarDataUrl(avatarUrl); + return descriptor + ? emojiAvatarDataUrl(descriptor.emoji, descriptor.color) + : avatarUrl; +} + +export function avatarSourceUrlForShape( + avatarUrl: string | null, + shape: "circle" | "squircle", +) { + return shape === "squircle" && avatarUrl + ? squareEmojiAvatarDataUrl(avatarUrl) + : avatarUrl; +} + export function parseEmojiAvatarDataUrl( avatarUrl: string, ): EmojiAvatarDescriptor | null { @@ -274,14 +289,15 @@ export function parseEmojiAvatarDataUrl( const svg = decodeURIComponent( avatarUrl.slice(EMOJI_AVATAR_DATA_URL_PREFIX.length), ); - const color = svg.match(/]*\sfill="([^"]+)"/u)?.[1]; - const emoji = svg.match(/]*>(.*?)<\/text>/u)?.[1]; + const match = svg.match( + /^([^<>]*)<\/text><\/svg>$/u, + ); - if (!color || !emoji) { + if (!match) { return null; } - return { color, emoji: unescapeSvgText(emoji) }; + return { color: match[1], emoji: unescapeSvgText(match[2]) }; } catch { return null; } diff --git a/desktop/src/features/profile/ui/ProfileAvatarWithStatus.tsx b/desktop/src/features/profile/ui/ProfileAvatarWithStatus.tsx index 9b9610fb8b1..b17128e771a 100644 --- a/desktop/src/features/profile/ui/ProfileAvatarWithStatus.tsx +++ b/desktop/src/features/profile/ui/ProfileAvatarWithStatus.tsx @@ -99,9 +99,10 @@ export function ProfileAvatarWithStatus({ } badgeBox={badgeBox} className={cn("inline-flex", className)} - cornerRadius={shape === "squircle" ? size * 0.3 : undefined} + clipTestId={testId ? `${testId}-mask` : undefined} curve={STATUS_DOT_MASK_CURVE} cutout={cutout} + shape={shape} size={size} > diff --git a/desktop/src/features/profile/ui/UserProfilePanelSections.tsx b/desktop/src/features/profile/ui/UserProfilePanelSections.tsx index 5ec8404bbd1..1ba07f24b79 100644 --- a/desktop/src/features/profile/ui/UserProfilePanelSections.tsx +++ b/desktop/src/features/profile/ui/UserProfilePanelSections.tsx @@ -650,9 +650,9 @@ function ProfileHero({ } badgeBox={PROFILE_HERO_PRESENCE_BADGE.shell} className="h-20 w-20" - cornerRadius={isBot ? 24 : undefined} curve={STATUS_DOT_MASK_CURVE} cutout={PROFILE_HERO_PRESENCE_BADGE.cutout} + shape={isBot ? "squircle" : "circle"} size={80} > diff --git a/desktop/src/features/projects/ui/ProjectEntityListRow.tsx b/desktop/src/features/projects/ui/ProjectEntityListRow.tsx index 46610e6d6ef..046ef899299 100644 --- a/desktop/src/features/projects/ui/ProjectEntityListRow.tsx +++ b/desktop/src/features/projects/ui/ProjectEntityListRow.tsx @@ -34,20 +34,27 @@ export function ProjectEntityFacepile({ {shown.map((pubkey, index) => { const label = resolveUserLabel({ profiles, pubkey }); + const isAgent = profiles?.[pubkey]?.isAgent === true; + const separatorClassName = cn( + "isolate relative inline-flex before:pointer-events-none before:absolute before:-inset-0.5 before:bg-background before:content-['']", + isAgent ? "before:rounded-squircle" : "before:rounded-full", + ); + const avatar = ( + + ); if (!interactive) { return ( 0 && "-ml-1.5")} + className={cn(separatorClassName, index > 0 && "-ml-1.5")} key={pubkey} title={label} > - + {avatar} ); } @@ -55,24 +62,17 @@ export function ProjectEntityFacepile({ 0 && "-ml-1.5", + )} triggerElement="span" + triggerAriaLabel={`View ${label}'s profile`} > - + + {avatar} + ); })} diff --git a/desktop/src/features/projects/ui/ProjectsOverviewRail.tsx b/desktop/src/features/projects/ui/ProjectsOverviewRail.tsx index 5ba34da99b8..e14d38d1509 100644 --- a/desktop/src/features/projects/ui/ProjectsOverviewRail.tsx +++ b/desktop/src/features/projects/ui/ProjectsOverviewRail.tsx @@ -60,7 +60,7 @@ function OverviewPerson({ ); diff --git a/desktop/src/main.tsx b/desktop/src/main.tsx index 634a9b171a1..0866ee051ed 100644 --- a/desktop/src/main.tsx +++ b/desktop/src/main.tsx @@ -14,6 +14,7 @@ import { CommunitiesProvider } from "@/features/communities/useCommunities"; import { huddleWindowChannelId } from "@/features/huddle/lib/huddleWindow"; import { CommunityOnboardingProvider } from "@/features/onboarding/communityOnboarding"; import { ThemeProvider } from "@/shared/theme/ThemeProvider"; +import { AvatarClipPaths } from "@/shared/ui/AvatarClipPaths"; import { EmojiBurstProvider } from "@/shared/ui/EmojiBurstProvider"; import { PoofBurstProvider } from "@/shared/ui/PoofBurstProvider"; import { Toaster } from "@/shared/ui/sonner"; @@ -84,6 +85,7 @@ function renderApp() { {/* block/buzz#5078 — catch any uncaught render error so a WebKit SecurityError from localStorage can't blank the whole window. */} + { + const points: Array<{ x: number; y: number }> = []; + const segments = Math.max(1, Math.floor(segmentsPerCubic)); + + for (const [ + start, + firstControl, + secondControl, + end, + ] of ROUNDED_SQUIRCLE_CUBICS) { + for (let index = 0; index < segments; index += 1) { + const progress = index / segments; + const remaining = 1 - progress; + points.push({ + x: + (remaining ** 3 * start[0] + + 3 * remaining ** 2 * progress * firstControl[0] + + 3 * remaining * progress ** 2 * secondControl[0] + + progress ** 3 * end[0]) * + size, + y: + (remaining ** 3 * start[1] + + 3 * remaining ** 2 * progress * firstControl[1] + + 3 * remaining * progress ** 2 * secondControl[1] + + progress ** 3 * end[1]) * + size, + }); + } + } + + return points; +} + +export function AvatarClipPaths() { + return ( + + ); +} diff --git a/desktop/src/shared/ui/UserAvatar.tsx b/desktop/src/shared/ui/UserAvatar.tsx index 0398ea2ccd9..80059206acc 100644 --- a/desktop/src/shared/ui/UserAvatar.tsx +++ b/desktop/src/shared/ui/UserAvatar.tsx @@ -1,5 +1,6 @@ import * as React from "react"; +import { avatarSourceUrlForShape } from "@/features/profile/ui/ProfileAvatarEditor.utils"; import { parseAnimatedAvatarUrl } from "@/shared/lib/animatedAvatar"; import { cn } from "@/shared/lib/cn"; import { getInitials } from "@/shared/lib/initials"; @@ -68,18 +69,19 @@ export function UserAvatar({ testId, }: UserAvatarProps) { const initials = getInitials(initialsLabel ?? displayName); + const resolvedShape = shape ?? "circle"; + const shapedAvatarUrl = avatarSourceUrlForShape(avatarUrl, resolvedShape); // Animated avatars show their static poster frame until hovered, then play // the animation. - const animated = parseAnimatedAvatarUrl(avatarUrl); + const animated = parseAnimatedAvatarUrl(shapedAvatarUrl); const [isHovered, setIsHovered] = React.useState(false); const src = animated ? rewriteRelayUrl(isHovered ? animated.animationUrl : animated.posterUrl) - : avatarUrl - ? rewriteRelayUrl(avatarUrl) + : shapedAvatarUrl + ? rewriteRelayUrl(shapedAvatarUrl) : null; - const resolvedShape = shape ?? "circle"; const radiusClass = - resolvedShape === "squircle" ? "rounded-[30%]" : "rounded-full"; + resolvedShape === "squircle" ? "rounded-squircle" : "rounded-full"; return ( setIsHovered(true) : undefined} onMouseLeave={animated ? () => setIsHovered(false) : undefined} diff --git a/desktop/tests/e2e/agents.spec.ts b/desktop/tests/e2e/agents.spec.ts index 758905f2cb1..cce9032b61f 100644 --- a/desktop/tests/e2e/agents.spec.ts +++ b/desktop/tests/e2e/agents.spec.ts @@ -340,12 +340,64 @@ test("built-in persona edits persist", async ({ page }) => { }); test("searches agent avatar emoji with focus on open", async ({ page }) => { + await page.addInitScript(() => { + window.localStorage.setItem("buzz-theme", "buzz-dark"); + window.localStorage.setItem("buzz-accent-color", "#c0a2f1"); + }); await gotoApp(page); await page.getByTestId("open-agents-view").click(); await page.getByTestId("new-agent-card").click(); await expect(page.getByTestId("persona-dialog")).toBeVisible(); - await page.getByLabel("Add avatar").click(); + const addAvatarButton = page.getByLabel("Add avatar"); + await expect(addAvatarButton).toHaveCSS("border-top-width", "0px"); + const idleShadow = await addAvatarButton.evaluate( + (button) => getComputedStyle(button).boxShadow, + ); + const emptyOutline = page.getByTestId("agent-avatar-empty-outline"); + const idlePlus = addAvatarButton.locator("svg.lucide-plus"); + await expect(idlePlus).toBeVisible(); + await expect(emptyOutline).toHaveCSS("z-index", "0"); + await expect(idlePlus).toHaveCSS("z-index", "10"); + const centerElement = await addAvatarButton.evaluate((button) => { + const bounds = button.getBoundingClientRect(); + const center = document.elementFromPoint( + bounds.left + bounds.width / 2, + bounds.top + bounds.height / 2, + ); + return center?.closest("svg.lucide-plus") !== null; + }); + expect(centerElement).toBe(true); + await waitForAnimations(page); + await page.screenshot({ + caret: "hide", + path: "test-results/agents/agent-avatar-idle-layering.png", + }); + await addAvatarButton.focus(); + await page.keyboard.press("Tab"); + await page.keyboard.press("Shift+Tab"); + await expect(addAvatarButton).toBeFocused(); + await expect(addAvatarButton).toHaveCSS("clip-path", "none"); + expect( + await addAvatarButton.evaluate( + (button) => getComputedStyle(button).boxShadow, + ), + ).not.toBe(idleShadow); + await waitForAnimations(page); + await page.screenshot({ + caret: "hide", + path: "test-results/agents/agent-avatar-keyboard-focus.png", + }); + await expect(emptyOutline).toHaveCSS( + "clip-path", + /url\(["']?#rounded-squircle-clip["']?\)/, + ); + await expect(emptyOutline).toBeVisible(); + await expect(emptyOutline.locator("path")).toHaveAttribute( + "d", + "M .5 0 C .93 0 1 .07 1 .5 C 1 .93 .93 1 .5 1 C .07 1 0 .93 0 .5 C 0 .07 .07 0 .5 0 Z", + ); + await addAvatarButton.click(); await page.getByRole("tab", { name: "Emoji" }).click(); const picker = page.locator("em-emoji-picker"); @@ -643,7 +695,7 @@ test("team cards use the thread-style overlapping avatar stack", async ({ return { maskImage: styles.maskImage, outlineBackground: outline.backgroundColor, - outlineBorderRadius: outline.borderRadius, + outlineClipPath: outline.clipPath, outlineInset: outline.inset, }; }), @@ -652,19 +704,19 @@ test("team cards use the thread-style overlapping avatar stack", async ({ { maskImage: "none", outlineBackground: "rgb(255, 255, 255)", - outlineBorderRadius: "calc(30% + 2px)", + outlineClipPath: 'url("#rounded-squircle-clip")', outlineInset: "-2px", }, { maskImage: "none", outlineBackground: "rgb(255, 255, 255)", - outlineBorderRadius: "calc(30% + 2px)", + outlineClipPath: 'url("#rounded-squircle-clip")', outlineInset: "-2px", }, { maskImage: "none", outlineBackground: "rgb(255, 255, 255)", - outlineBorderRadius: "calc(30% + 2px)", + outlineClipPath: 'url("#rounded-squircle-clip")', outlineInset: "-2px", }, ]); @@ -693,6 +745,42 @@ test("team cards use the thread-style overlapping avatar stack", async ({ ]); }); +test("empty team cards draw a squircle-shaped placeholder outline", async ({ + page, +}) => { + await installMockBridge(page, { + teams: [ + { + name: "Empty crew", + personaIds: [], + }, + ], + }); + await gotoApp(page); + await page.getByTestId("open-agents-view").click(); + + const placeholder = page.locator('[data-team-empty-avatar="avatar"]').first(); + const outline = placeholder.locator("xpath=.."); + await expect(placeholder).toHaveCSS( + "clip-path", + 'url("#rounded-squircle-clip")', + ); + const styles = await outline.evaluate((element) => { + const frame = getComputedStyle(element); + const border = getComputedStyle(element, "::before"); + return { + borderClipPath: border.clipPath, + borderWidth: frame.borderWidth, + frameClipPath: frame.clipPath, + }; + }); + expect(styles).toEqual({ + borderClipPath: 'url("#rounded-squircle-clip")', + borderWidth: "0px", + frameClipPath: "none", + }); +}); + test("agent defaults stays in the header without an actions menu", async ({ page, }) => { @@ -2652,6 +2740,8 @@ test("start pill morphs into the running dot without remounting the avatar", asy const card = page.getByTestId(`persona-agent-row-${personaId}`); const startButton = page.getByTestId(`agent-runtime-start-${pubkey}`); + const avatarMask = card.getByTestId("agent-runtime-avatar-mask"); + await expect(avatarMask).toHaveCSS("clip-path", "none"); const badge = startButton.locator("xpath=../.."); const initialAvatar = await card .getByAltText("Motion Auditor avatar") @@ -2686,6 +2776,7 @@ test("start pill morphs into the running dot without remounting the avatar", asy await expect( page.getByTestId(`agent-runtime-active-${pubkey}`), ).toBeVisible(); + await expect(avatarMask).toHaveCSS("clip-path", /polygon\(/); const samples = await samplesPromise; const finalAvatar = await card .getByAltText("Motion Auditor avatar") diff --git a/desktop/tests/e2e/channels.spec.ts b/desktop/tests/e2e/channels.spec.ts index b7cf2c23cbf..1797def2622 100644 --- a/desktop/tests/e2e/channels.spec.ts +++ b/desktop/tests/e2e/channels.spec.ts @@ -36,6 +36,7 @@ const UNNAMED_MEMBER_V5_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000005"; const UNNAMED_MEMBER_V24_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000018"; +const NEW_DM_AGENT_PUBKEY = "f".repeat(64); type MockFeedWindow = Window & { __BUZZ_E2E_EMIT_MOCK_MESSAGE__?: (input: { @@ -580,6 +581,16 @@ test("shows presence in sidebar, DM header, and member list", async ({ "Online", ); await expect(page.getByTestId("channel-presence-alice-tyler")).toBeVisible(); + const dmAvatarMask = page.getByTestId("channel-avatar-alice-tyler-mask"); + await expect(dmAvatarMask).toHaveCSS("border-radius", "0px"); + await expect(dmAvatarMask).toHaveCSS("clip-path", /polygon\(/); + await expect + .poll(() => + dmAvatarMask.evaluate( + (element) => getComputedStyle(element).clipPath.split(",").length, + ), + ) + .toBeGreaterThan(100); await page.getByTestId("channel-alice-tyler").click(); await expect(page.getByTestId("chat-title")).toHaveText("alice-tyler"); @@ -597,6 +608,22 @@ test("shows presence in sidebar, DM header, and member list", async ({ }); test("start a new direct message from the sidebar", async ({ page }) => { + await installMockBridge(page, { + managedAgents: [ + { + pubkey: NEW_DM_AGENT_PUBKEY, + name: "Squircle Agent", + status: "stopped", + }, + ], + searchProfiles: [ + { + pubkey: NEW_DM_AGENT_PUBKEY, + displayName: "Squircle Agent", + isAgent: true, + }, + ], + }); await page.goto("/"); await openNewMessagePage(page); @@ -641,11 +668,27 @@ test("start a new direct message from the sidebar", async ({ page }) => { page.getByTestId(`new-dm-selected-${TEST_IDENTITIES.charlie.pubkey}`), ).toBeVisible(); + await page.getByTestId("new-dm-search").fill("squircle agent"); + await page.getByTestId(`new-dm-result-${NEW_DM_AGENT_PUBKEY}`).click(); + const selectedAgent = page.getByTestId( + `new-dm-selected-${NEW_DM_AGENT_PUBKEY}`, + ); + await expect(selectedAgent).toBeVisible(); + await selectedAgent.focus(); + await expect(selectedAgent).toBeFocused(); + await expect(selectedAgent).toHaveCSS("clip-path", "none"); + await expect(selectedAgent).not.toHaveClass(/rounded-squircle/); + await expect( + selectedAgent.locator("span.absolute[data-avatar-shape='squircle']"), + ).toHaveCSS("clip-path", /url\(["']?#rounded-squircle-clip["']?\)/); + await page.getByTestId("message-input").fill("Hello charlie"); await page.getByTestId("send-message").click(); await expect(page.getByTestId("dm-list")).toContainText("charlie"); - await expect(page.getByTestId("chat-title")).toHaveText("charlie"); + await expect(page.getByTestId("chat-title")).toHaveText( + "charlie, Squircle Agent", + ); await expect(page.getByTestId("section-actions-dms")).not.toBeFocused(); }); @@ -2160,6 +2203,14 @@ test("shows and clears activity indicators for active channel agents", async ({ }, TEST_IDENTITIES.alice.pubkey); await expect(page.getByTestId("bot-activity-composer-trigger")).toBeVisible(); + const activityAvatar = page.getByTestId( + `bot-activity-composer-avatar-${TEST_IDENTITIES.alice.pubkey}`, + ); + await expect(activityAvatar).toHaveCSS("border-radius", "0px"); + await expect(activityAvatar).toHaveCSS( + "clip-path", + 'url("#rounded-squircle-clip")', + ); await expect( page.getByTestId("bot-activity-composer-trigger"), ).not.toContainText("View activity"); @@ -3966,6 +4017,17 @@ test("Inbox keeps the unread boundary for replies from multiple agents", async ( const firstUnreadRow = page.getByTestId(`home-inbox-item-${replyIds[0]}`); await expect(firstUnreadRow).toBeVisible(); + + const inboxAvatar = page.getByTestId(`home-inbox-avatar-${replyIds[0]}`); + await expect(inboxAvatar).toBeVisible(); + await inboxAvatar.focus(); + await expect(inboxAvatar).toBeFocused(); + await expect(inboxAvatar).toHaveCSS("clip-path", "none"); + await expect(inboxAvatar).not.toHaveClass(/rounded-squircle/); + await expect(inboxAvatar.locator("[data-avatar-shape='squircle']")).toHaveCSS( + "clip-path", + /url\(["']?#rounded-squircle-clip["']?\)/, + ); await firstUnreadRow.click(); const detail = page.getByTestId("home-inbox-detail"); diff --git a/desktop/tests/e2e/huddle-transcription.spec.ts b/desktop/tests/e2e/huddle-transcription.spec.ts index 32b4bcde56a..254fd5ef5bf 100644 --- a/desktop/tests/e2e/huddle-transcription.spec.ts +++ b/desktop/tests/e2e/huddle-transcription.spec.ts @@ -5,6 +5,7 @@ import { KIND_HUDDLE_STARTED, } from "../../src/shared/constants/kinds"; import { truncateNpub } from "../../src/shared/lib/pubkey"; +import { ROUNDED_SQUIRCLE_PATH } from "../../src/shared/ui/AvatarClipPaths"; import { waitForAnimations } from "../helpers/animations"; import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge"; @@ -667,6 +668,34 @@ test("animates the responding agent with the shared speaker ring", async ({ ), ) .toBe("0.890"); + await expect(agentAvatar).toHaveCSS("clip-path", "none"); + await expect(agentAvatar).not.toHaveClass(/rounded-squircle/); + await expect(agentAvatar).toHaveClass(/buzz-huddle-speaking-avatar-agent/); + const speakingRing = agentAvatar.getByTestId("huddle-agent-speaking-ring"); + await expect(speakingRing).toBeVisible(); + await expect(speakingRing.locator("path")).toHaveAttribute( + "d", + ROUNDED_SQUIRCLE_PATH, + ); + await expect(speakingRing.locator("path")).toHaveAttribute("fill", "none"); + await expect(speakingRing.locator("path")).toHaveAttribute( + "stroke-width", + "2", + ); + await expect(speakingRing.locator("path")).toHaveAttribute( + "vector-effect", + "non-scaling-stroke", + ); + await expect(speakingRing).toHaveCSS("opacity", "0.89"); + await expect(agentAvatar.locator(".rounded-squircle")).toHaveCSS( + "clip-path", + /rounded-squircle-clip/, + ); + + const voiceMenuTrigger = page.getByTestId("huddle-agent-voice-menu-trigger"); + await voiceMenuTrigger.focus(); + await expect(voiceMenuTrigger).toBeFocused(); + await expect(voiceMenuTrigger).toHaveCSS("clip-path", "none"); await page.evaluate(async () => { await window.__BUZZ_E2E_EMIT_MOCK_HUDDLE_TTS_SPEAKER__?.({ diff --git a/desktop/tests/e2e/mentions.spec.ts b/desktop/tests/e2e/mentions.spec.ts index 40a132e6df9..d8f4242c06d 100644 --- a/desktop/tests/e2e/mentions.spec.ts +++ b/desktop/tests/e2e/mentions.spec.ts @@ -4084,6 +4084,53 @@ test("collapses contiguous mixed join arrivals into one actor-neutral cohort", a await expect(rows.first()).toContainText(JOIN_COLLAPSE_GROUPED_TEXT); }); +test("system agent avatar keeps keyboard focus decoration outside artwork", async ({ + page, +}) => { + await installMockBridge(page, { + searchProfiles: [ + { + pubkey: PROFILE_ONLY_AGENT_PUBKEY, + displayName: "mira", + isAgent: true, + }, + ], + }); + await page.goto("/"); + await page.getByTestId("channel-random").click(); + await expect(page.getByTestId("chat-title")).toHaveText("random"); + await waitForMockLiveSubscription(page, "random", SYSTEM_MESSAGE_KIND); + + await page.evaluate( + ({ actorPubkey, kind }) => { + window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({ + channelName: "random", + content: JSON.stringify({ + type: "channel_created", + actor: actorPubkey, + }), + kind, + }); + }, + { + actorPubkey: PROFILE_ONLY_AGENT_PUBKEY, + kind: SYSTEM_MESSAGE_KIND, + }, + ); + await waitForTimelineSettled(page); + + const row = page + .getByTestId("system-message-row") + .filter({ hasText: "created this channel" }); + const control = row.locator('button[data-testid="system-message-avatar"]'); + const artwork = control.getByTestId("system-message-avatar"); + await control.focus(); + + await expect(control).toBeFocused(); + await expect(control).toHaveCSS("clip-path", "none"); + await expect(artwork).toHaveCSS("clip-path", /rounded-squircle-clip/); +}); + test("system agent profile exposes owned agent actions", async ({ page }) => { await page.goto("/"); await page.getByTestId("channel-random").click(); diff --git a/desktop/tests/e2e/messaging.spec.ts b/desktop/tests/e2e/messaging.spec.ts index 6f312cfa14e..c82c8b960e3 100644 --- a/desktop/tests/e2e/messaging.spec.ts +++ b/desktop/tests/e2e/messaging.spec.ts @@ -76,7 +76,9 @@ async function measureThreadSummaryGeometry(summaryRow: Locator) { const summarySurface = summaryButton.querySelector( '[data-testid="message-thread-summary-surface"]', ); - const firstAvatar = firstParticipant?.firstElementChild; + const firstAvatar = firstParticipant?.querySelector( + '[data-testid^="message-thread-summary-avatar-"]', + ); if ( !summaryWrapper || @@ -387,6 +389,56 @@ test.beforeEach(async ({ page }, testInfo) => { await installMockBridge(page, mock); }); +test("agent avatars use the one normalized SVG clip path", async ({ page }) => { + await page.goto("/"); + await page.getByTestId("channel-general").click(); + + const agentMessage = page + .getByTestId("message-row") + .filter({ hasText: "Hey team — checking in." }); + const avatar = agentMessage.getByTestId("message-avatar"); + await expect(avatar).toHaveClass(/rounded-squircle/); + await expect(avatar).toHaveCSS("border-radius", "0px"); + await expect(avatar).toHaveCSS("clip-path", /rounded-squircle-clip/); + await expect(page.locator("#rounded-squircle-clip")).toHaveCount(1); + + const avatarButton = avatar.locator("xpath=ancestor::button[1]"); + await page.keyboard.press("Tab"); + await avatarButton.focus(); + await expect(avatarButton).toBeFocused(); + await expect(avatarButton).toHaveCSS("clip-path", "none"); + await expect(avatarButton).not.toHaveCSS("box-shadow", "none"); + + const avatarBox = await avatar.boundingBox(); + expect(avatarBox).toMatchObject({ + width: expect.any(Number), + height: expect.any(Number), + }); + if (avatarBox === null) { + throw new Error("agent message avatar has no rendered bounds"); + } + expect(avatarBox.width).toBeGreaterThanOrEqual(24); + expect(avatarBox.height).toBeGreaterThanOrEqual(24); + expect(Math.abs(avatarBox.width - avatarBox.height)).toBeLessThanOrEqual(1); + + await agentMessage.getByRole("button", { name: "A" }).first().click(); + const profileAvatar = page + .getByTestId("user-profile-panel") + .locator(".rounded-squircle") + .first(); + await expect(profileAvatar).toBeVisible(); + await expect(profileAvatar).toHaveCSS("border-radius", "0px"); + await expect(profileAvatar).toHaveCSS("clip-path", /rounded-squircle-clip/); + await expect + .poll(() => + profileAvatar.evaluate((element) => { + const rect = element.getBoundingClientRect(); + return rect.width >= 80 && rect.height >= 80; + }), + ) + .toBe(true); +}); + test("agent owner label identifies the agent and owner", async ({ page }) => { await page.goto("/"); await page.getByTestId("channel-general").click(); @@ -2760,6 +2812,7 @@ test("opens a single-level thread panel with inline expansion", async ({ const firstReply = `First threaded reply ${timestamp}`; const siblingReply = `Sibling threaded reply ${timestamp}`; const nestedReply = `Nested threaded reply ${timestamp}`; + const nestedReplyFromAgent = `Nested reply from agent ${timestamp}`; const nestedReplyFromBob = `Nested reply from Bob ${timestamp}`; const fillerReplies = Array.from( { length: 14 }, @@ -2838,8 +2891,10 @@ test("opens a single-level thread panel with inline expansion", async ({ .getByTestId("message-thread-summary-participant") .first() .evaluate((wrapper) => { - const avatar = wrapper.firstElementChild; - if (!(avatar instanceof HTMLElement)) return "missing"; + const avatar = wrapper.querySelector( + '[data-testid^="message-thread-summary-avatar-"]', + ); + if (!avatar) return "missing"; const rect = avatar.getBoundingClientRect(); return `${Math.round(rect.width)}x${Math.round(rect.height)}`; }), @@ -2877,7 +2932,7 @@ test("opens a single-level thread panel with inline expansion", async ({ ); expect( Math.abs( - summaryGeometry.avatarLeft - summaryGeometry.summarySurfaceLeft - 4, + summaryGeometry.avatarLeft - summaryGeometry.summarySurfaceLeft - 8, ), ).toBeLessThanOrEqual(1); expect( @@ -2997,6 +3052,27 @@ test("opens a single-level thread panel with inline expansion", async ({ .first(); await expect(nestedReplyFromBobRow).toBeVisible(); + await page.evaluate( + ({ content, parentEventId, pubkey }) => { + window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({ + channelName: "general", + content, + parentEventId, + pubkey, + }); + }, + { + content: nestedReplyFromAgent, + parentEventId: firstReplyId, + pubkey: TEST_IDENTITIES.alice.pubkey, + }, + ); + const nestedReplyFromAgentRow = threadReplies + .getByTestId("message-row") + .filter({ hasText: nestedReplyFromAgent }) + .first(); + await expect(nestedReplyFromAgentRow).toBeVisible(); + const firstReplySummaryRow = threadReplies.locator( `[data-testid="message-thread-summary"][data-thread-head-id="${firstReplyId}"]`, ); @@ -3006,10 +3082,10 @@ test("opens a single-level thread panel with inline expansion", async ({ ); await expect(firstReplyBranchGuide).not.toHaveCount(0); - await expect(rootSummaryRow).toContainText("18 replies"); + await expect(rootSummaryRow).toContainText("19 replies"); await expect( rootSummaryRow.getByTestId("message-thread-summary-participant"), - ).toHaveCount(2); + ).toHaveCount(3); await expect .poll(() => rootSummaryRow @@ -3020,13 +3096,57 @@ test("opens a single-level thread panel with inline expansion", async ({ .join(","), ), ) - .toBe("1,2"); + .toBe("1,2,3"); + const stackedParticipants = rootSummaryRow.getByTestId( + "message-thread-summary-participant", + ); + const stackGeometry = await stackedParticipants.evaluateAll((participants) => + participants.map((participant) => { + const avatar = participant.querySelector( + '[data-testid^="message-thread-summary-avatar-"]', + ); + if (!avatar) throw new Error("Expected a stacked thread avatar."); + const rect = avatar.getBoundingClientRect(); + return { left: rect.left, width: rect.width }; + }), + ); + expect(stackGeometry[1].left - stackGeometry[0].left).toBeCloseTo( + stackGeometry[0].width - 4, + 1, + ); + expect(stackGeometry[2].left - stackGeometry[1].left).toBeCloseTo( + stackGeometry[1].width - 4, + 1, + ); + let foregroundAgentIndex = -1; + await expect + .poll(async () => { + foregroundAgentIndex = await stackedParticipants.evaluateAll( + (participants) => + participants.findIndex( + (participant, index) => + index > 0 && participant.querySelector(".rounded-squircle"), + ), + ); + return foregroundAgentIndex; + }) + .toBeGreaterThan(0); + const maskBehindAgent = rootSummaryRow.getByTestId( + `message-thread-summary-stack-mask-${foregroundAgentIndex - 1}`, + ); + const stackMaskImage = await maskBehindAgent.evaluate( + (element) => getComputedStyle(element).maskImage, + ); + expect(stackMaskImage).toContain("data:image/svg+xml"); + expect(decodeURIComponent(stackMaskImage)).toContain( + 'd="M .5 0 C .93 0 1 .07 1 .5', + ); await expectThreadReplyUnobscured(nestedReplyRow); await firstReplyBranchGuide.first().click(); await expect(firstReplySummaryRow).toHaveCount(1); - await expect(firstReplySummaryRow).toContainText("2 replies"); + await expect(firstReplySummaryRow).toContainText("3 replies"); await expect( threadReplies.getByTestId("message-row").filter({ hasText: nestedReply }), ).toHaveCount(0); @@ -3035,6 +3155,11 @@ test("opens a single-level thread panel with inline expansion", async ({ .getByTestId("message-row") .filter({ hasText: nestedReplyFromBob }), ).toHaveCount(0); + await expect( + threadReplies + .getByTestId("message-row") + .filter({ hasText: nestedReplyFromAgent }), + ).toHaveCount(0); }); test("thread panel width uses session storage and reset handle", async ({ diff --git a/desktop/tests/e2e/project-issue-comments.spec.ts b/desktop/tests/e2e/project-issue-comments.spec.ts index 341e444551f..3214f7b6070 100644 --- a/desktop/tests/e2e/project-issue-comments.spec.ts +++ b/desktop/tests/e2e/project-issue-comments.spec.ts @@ -244,6 +244,18 @@ test("issue assignees can be assigned and unassigned", async ({ page }) => { const unassign = page.getByTestId(`project-issue-unassign-${assignee}`); await expect(unassign).toBeVisible({ timeout: 10_000 }); + const assigneeAvatar = unassign.locator("[data-avatar-shape]"); + const expectedShape = await assigneeAvatar.getAttribute("data-avatar-shape"); + await unassign.focus(); + await expect(unassign).toBeFocused(); + await expect(unassign).toHaveCSS("clip-path", "none"); + await expect(unassign).not.toHaveClass(/rounded-squircle/); + await expect(assigneeAvatar).toHaveCSS( + "clip-path", + expectedShape === "squircle" + ? /url\(["']?#rounded-squircle-clip["']?\)/ + : "none", + ); await unassign.click(); await expect(page.getByText("Task unassigned.")).toBeVisible(); await expect(unassign).toHaveCount(0, { timeout: 10_000 }); diff --git a/desktop/tests/e2e/project-pr-review.spec.ts b/desktop/tests/e2e/project-pr-review.spec.ts index 16d249a6dbb..2524a2b30ed 100644 --- a/desktop/tests/e2e/project-pr-review.spec.ts +++ b/desktop/tests/e2e/project-pr-review.spec.ts @@ -6,6 +6,7 @@ import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge"; const SHOTS = "test-results/project-pr-review"; const RECOVERY_SHOTS = "test-results/project-pr-conflict-recovery"; const REVIEWER_AGENT_PUBKEY = "a".repeat(64); +const REVIEWER_HUMAN_PUBKEY = "b".repeat(64); const DEFAULT_MOCK_PUBKEY = "deadbeef".repeat(8); async function expectSinglePrimaryTextColumn(row: Locator) { @@ -77,19 +78,6 @@ async function addProjectToSidebar( await expect(page.getByTestId(`sidebar-project-${dtag}`)).toBeVisible(); } -async function openProjectRepository( - page: import("@playwright/test").Page, - repositoryId: string, -) { - await expect(page).toHaveURL(/\/projects\//); - const target = await page.evaluate((id) => { - const url = new URL(window.location.href); - url.searchParams.set("repositoryId", id); - return `${url.pathname}${url.search}`; - }, repositoryId); - await page.goto(target, { waitUntil: "domcontentloaded" }); -} - function pullRequestRowByAuthor( page: import("@playwright/test").Page, author: string, @@ -1494,7 +1482,29 @@ test("channels tab opens the latest matching conversation without leaving the pr page, }) => { await enableProjectsFeature(page); - await installMockBridge(page); + await page.addInitScript(() => { + window.localStorage.setItem("buzz-theme", "buzz-dark"); + window.localStorage.setItem("buzz-accent-color", "#c0a2f1"); + window.localStorage.setItem("buzz:text-scale", "1.5"); + }); + await installMockBridge(page, { + searchProfiles: [ + { + avatarUrl: + 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"%3E%3Crect width="16" height="16" fill="%23F4B942"/%3E%3C/svg%3E', + displayName: "reviewer human", + isAgent: false, + pubkey: REVIEWER_HUMAN_PUBKEY, + }, + { + avatarUrl: + 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"%3E%3Crect width="16" height="16" fill="%237657FF"/%3E%3C/svg%3E', + displayName: "reviewer agent", + isAgent: true, + pubkey: REVIEWER_AGENT_PUBKEY, + }, + ], + }); await page.goto("/", { waitUntil: "domcontentloaded" }); await page.getByTestId("channel-general").click(); await waitForMockLiveSubscription(page, "general"); @@ -1502,26 +1512,27 @@ test("channels tab opens the latest matching conversation without leaving the pr const olderContent = "Older matching conversation for channels tab"; const latestContent = "Latest matching conversation for channels tab"; await page.evaluate( - ({ author, latestContent, olderContent, repoToken }) => { + ({ latestAuthor, latestContent, olderAuthor, olderContent, repoToken }) => { const now = Math.floor(Date.now() / 1_000); window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({ channelName: "general", content: `${olderContent} ${repoToken}`, createdAt: now - 1, kind: 9, - pubkey: author, + pubkey: olderAuthor, }); window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({ channelName: "general", content: `${latestContent} ${repoToken}`, createdAt: now, kind: 9, - pubkey: author, + pubkey: latestAuthor, }); }, { - author: TEST_IDENTITIES.alice.pubkey, + latestAuthor: REVIEWER_HUMAN_PUBKEY, latestContent, + olderAuthor: REVIEWER_AGENT_PUBKEY, olderContent, repoToken: `${DEFAULT_MOCK_PUBKEY} buzz`, }, @@ -1540,6 +1551,117 @@ test("channels tab opens the latest matching conversation without leaving the pr await page.getByRole("tab", { name: "Channels", exact: true }).click(); const channelRow = page.getByTestId("project-channel-row").first(); await expect(channelRow).toBeVisible({ timeout: 10_000 }); + + const discussedAgent = channelRow.getByRole("button", { + name: "View reviewer agent's profile", + }); + const discussedHuman = channelRow.getByRole("button", { + name: "View reviewer human's profile", + }); + await expect(discussedAgent).toBeVisible(); + await expect(discussedHuman).toBeVisible(); + await expect( + channelRow.getByRole("button", { name: /^View .*'s profile$/ }), + ).toHaveCount(2); + expect( + await channelRow + .getByRole("button", { name: /^View .*'s profile$/ }) + .evaluateAll((elements) => + elements.map((element) => element.getAttribute("aria-label")), + ), + ).toEqual(["View reviewer human's profile", "View reviewer agent's profile"]); + await page.locator("body").click({ position: { x: 0, y: 0 } }); + await expect(discussedAgent).not.toBeFocused(); + await expect(discussedHuman).not.toBeFocused(); + await waitForAnimations(page); + await expect(discussedAgent).toHaveCSS("clip-path", "none"); + await expect(discussedAgent).toHaveCSS("isolation", "isolate"); + await expect(discussedHuman).toHaveCSS("isolation", "isolate"); + await expect(discussedAgent).not.toHaveClass( + /(?:^|\s)rounded-squircle(?:\s|$)/, + ); + await expect(discussedAgent).toHaveCSS( + "background-color", + "rgba(0, 0, 0, 0)", + ); + await expect(discussedAgent).toHaveCSS("box-shadow", "none"); + await expect(discussedAgent).toHaveCSS("overflow", "visible"); + const agentSeparator = await discussedAgent.evaluate((element) => { + const style = getComputedStyle(element, "::before"); + return { + backgroundColor: style.backgroundColor, + clipPath: style.clipPath, + content: style.content, + insetBottom: style.bottom, + insetLeft: style.left, + insetRight: style.right, + insetTop: style.top, + opacity: style.opacity, + position: style.position, + }; + }); + expect(agentSeparator).toMatchObject({ + clipPath: /url\(["']?#rounded-squircle-clip["']?\)/, + content: /(?:""|none)/, + opacity: "1", + position: "absolute", + }); + expect(agentSeparator.backgroundColor).not.toBe("rgba(0, 0, 0, 0)"); + expect(Number.parseFloat(agentSeparator.insetTop)).toBeLessThan(0); + expect(Number.parseFloat(agentSeparator.insetRight)).toBeLessThan(0); + expect(Number.parseFloat(agentSeparator.insetBottom)).toBeLessThan(0); + expect(Number.parseFloat(agentSeparator.insetLeft)).toBeLessThan(0); + await expect( + discussedAgent.locator("[data-avatar-shape='squircle']"), + ).toHaveCSS("clip-path", /url\(["']?#rounded-squircle-clip["']?\)/); + const [agentBox, humanBox] = await Promise.all([ + discussedAgent.boundingBox(), + discussedHuman.boundingBox(), + ]); + expect(agentBox).not.toBeNull(); + expect(humanBox).not.toBeNull(); + expect((agentBox?.x ?? 0) - (humanBox?.x ?? 0)).toBeLessThan( + humanBox?.width ?? 0, + ); + const separatorScreenshot = await page.screenshot({ caret: "hide" }); + const separatorPixel = await page.evaluate( + async ({ screenshot, x, y }) => { + const response = await fetch(`data:image/png;base64,${screenshot}`); + const image = await createImageBitmap(await response.blob()); + const canvas = document.createElement("canvas"); + canvas.width = image.width; + canvas.height = image.height; + const context = canvas.getContext("2d"); + if (!context) throw new Error("2D canvas context unavailable"); + context.drawImage(image, 0, 0); + const scaleX = image.width / window.innerWidth; + const scaleY = image.height / window.innerHeight; + return Array.from( + context.getImageData( + Math.round(x * scaleX), + Math.round(y * scaleY), + 1, + 1, + ).data, + ); + }, + { + screenshot: separatorScreenshot.toString("base64"), + x: (agentBox?.x ?? 0) - 1, + y: (agentBox?.y ?? 0) + (agentBox?.height ?? 0) / 2, + }, + ); + const separatorRgb = agentSeparator.backgroundColor + .match(/\d+(?:\.\d+)?/g) + ?.slice(0, 3) + .map(Number); + expect(separatorRgb).toHaveLength(3); + expect(separatorPixel.slice(0, 3)).toEqual(separatorRgb); + + await discussedAgent.focus(); + await expect(discussedAgent).toBeFocused(); + await expect(discussedAgent).toHaveCSS("clip-path", "none"); + await channelRow.click(); const panel = page.getByTestId("project-conversation-panel"); @@ -1677,7 +1799,15 @@ test("project overview presents collapsible context beside grouped activity", as page, }) => { await enableProjectsFeature(page); - await installMockBridge(page); + await installMockBridge(page, { + searchProfiles: [ + { + displayName: "tyler", + isAgent: true, + pubkey: DEFAULT_MOCK_PUBKEY, + }, + ], + }); await page.goto("/", { waitUntil: "domcontentloaded" }); await page.getByTestId("open-projects-view").click(); @@ -1744,6 +1874,23 @@ test("project overview presents collapsible context beside grouped activity", as .getByTestId("projects-overview-stats-pod") .getByTestId("projects-overview-people"), ).toHaveCount(0); + await page.getByTestId("projects-section-projects").click(); + await page.getByRole("button", { name: "Grid layout" }).click(); + const projectPeopleButton = page.locator( + 'button[aria-label="View tyler\'s profile"]', + ); + await expect(projectPeopleButton).toBeVisible(); + await projectPeopleButton.focus(); + await page.keyboard.press("Tab"); + await page.keyboard.press("Shift+Tab"); + await expect(projectPeopleButton).toBeFocused(); + await expect(projectPeopleButton).toHaveCSS("clip-path", "none"); + await expect(projectPeopleButton).not.toHaveCSS("box-shadow", "none"); + await expect(projectPeopleButton.locator(":scope > span")).toHaveCSS( + "clip-path", + /url\(["']?#rounded-squircle-clip["']?\)/, + ); + await page.getByTestId("projects-section-all").click(); await expect( page .getByTestId("projects-overview-context-panel") From 44c1cc7df8e0067c9d578a93ad8e4393304422b3 Mon Sep 17 00:00:00 2001 From: Salman Mohammed Date: Fri, 11 Sep 2026 12:20:31 -0400 Subject: [PATCH 24/26] feat(buzz-cli): render an agent-friendly command tree in --help (#7584) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `crates/buzz-acp/src/base_prompt.md` carries a hand-maintained `Group | Key commands` table of the `buzz` CLI. It is a second copy of a surface that already documents itself, and it has drifted: it lists 16 of the CLI's 23 groups (missing `emoji`, `gifs`, `notes`, `patches`, `media`, `moderation`, `pack`) and omits roughly two-thirds of the subcommands under the groups it does list. its hard to keep it in sync. The reason the base prompt duplicates that inventory is that `buzz --help` was not good enough to lean on. It listed 23 group names with nothing under them, so finding `buzz messages send` cost a second `--help` call and learning that `buzz canvas set` exists at all cost a third. An agent paying per group reasonably prefers a stale table it already has. This PR fixes the help output so the prompt can point at it instead. `buzz --help` now prints the whole tree — every group, its subcommands, and their descriptions — in one invocation: **Before** ``` Commands: messages Send, read, search, and manage messages channels Create, configure, and manage channels canvas Get and set channel canvas documents ``` **After** ``` Commands: messages Send, read, search, and manage messages send Send a message to a channel send-diff Send a code diff / patch to a channel edit Edit a previously sent message ... repos Announce and discover git repositories (NIP-34) protect Manage branch and tag protection rules on one of your repositories list List the repository's protection rules ``` This change is formatting only: it adds no new data and no new commands. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Salman Mohammed --- crates/buzz-cli/README.md | 6 + crates/buzz-cli/src/help_tree.rs | 214 +++++++++++++++++++++++++++++++ crates/buzz-cli/src/lib.rs | 40 +++++- 3 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 crates/buzz-cli/src/help_tree.rs diff --git a/crates/buzz-cli/README.md b/crates/buzz-cli/README.md index 85a04b47120..29ec19a04c2 100644 --- a/crates/buzz-cli/README.md +++ b/crates/buzz-cli/README.md @@ -112,6 +112,12 @@ stored rules in `validation_error` so an owner can remove and repair them. ## Commands +`buzz --help` prints this whole surface as an indented tree — every group with +its subcommands and their descriptions — so there is no need to run `--help` +once per group to find a command. `buzz -h` keeps the short group-level +summary, and `buzz --help` has the flags and examples. +The table below mirrors that tree for readers who are not at a terminal. + | Group | Subcommand | Description | |-------|-----------|-------------| | `messages` | `send` | Send a message to a channel | diff --git a/crates/buzz-cli/src/help_tree.rs b/crates/buzz-cli/src/help_tree.rs new file mode 100644 index 00000000000..754f012da5d --- /dev/null +++ b/crates/buzz-cli/src/help_tree.rs @@ -0,0 +1,214 @@ +//! Agent-friendly rendering of the `buzz` command tree. +//! +//! `buzz --help` lists every group *and* the subcommands under it, so a caller +//! learns the whole surface in one invocation instead of one `--help` call per +//! group. The tree is walked from clap's own command definition, so it cannot +//! drift from the commands that actually exist. + +use clap::Command; + +/// Column (0-indexed, in characters) where descriptions begin. +const DESC_COL: usize = 32; + +/// Total line width. This matches clap's own default when its `wrap_help` +/// feature is off, which is how this crate builds clap. +const LINE_WIDTH: usize = 100; + +/// Render the subcommand tree under `cmd`, descending at most `max_depth` +/// levels. `max_depth == 1` lists only the top-level groups. +/// +/// The returned string has one entry per line and no trailing newline. +pub(crate) fn render(cmd: &Command, max_depth: usize) -> String { + let mut out = String::new(); + write_level(&mut out, cmd, 1, max_depth); + out.truncate(out.trim_end().len()); + out +} + +fn write_level(out: &mut String, parent: &Command, depth: usize, max_depth: usize) { + if depth > max_depth { + return; + } + let mut children: Vec<&Command> = parent + .get_subcommands() + // clap's auto-generated `help` subcommand tells a reader nothing about + // what the CLI can do, and it repeats at every level of the tree. + .filter(|child| !child.is_hide_set() && child.get_name() != "help") + .collect(); + // Match clap's own ordering: the derive assigns a display order per + // variant, so this preserves declaration order, with the name as tiebreak. + children.sort_by_key(|child| (child.get_display_order(), child.get_name())); + + for child in children { + write_entry(out, child, depth); + write_level(out, child, depth + 1, max_depth); + } +} + +fn write_entry(out: &mut String, cmd: &Command, depth: usize) { + let left = format!("{}{}", " ".repeat(depth), cmd.get_name()); + let description = description_of(cmd); + + if description.is_empty() { + out.push_str(&left); + out.push('\n'); + return; + } + + let pad = DESC_COL.saturating_sub(left.chars().count()).max(1); + let mut lines = wrap(&description, LINE_WIDTH.saturating_sub(DESC_COL)).into_iter(); + + // `wrap` returns at least one line for non-empty input, but fall back to + // the bare name rather than dropping the entry if that ever changes. + let Some(first) = lines.next() else { + out.push_str(&left); + out.push('\n'); + return; + }; + out.push_str(&left); + out.push_str(&" ".repeat(pad)); + out.push_str(&first); + out.push('\n'); + + for line in lines { + out.push_str(&" ".repeat(DESC_COL)); + out.push_str(&line); + out.push('\n'); + } +} + +/// A command's one-line description, prefixed with its visible aliases. +fn description_of(cmd: &Command) -> String { + let mut description = String::new(); + let aliases: Vec<&str> = cmd.get_visible_aliases().collect(); + if !aliases.is_empty() { + description.push_str(&format!("[alias: {}] ", aliases.join(", "))); + } + if let Some(about) = cmd.get_about() { + description.push_str(&about.to_string()); + } + description.trim_end().to_string() +} + +/// Greedy word wrap. Words longer than `width` overflow rather than break. +fn wrap(text: &str, width: usize) -> Vec { + let mut lines = Vec::new(); + let mut current = String::new(); + for word in text.split_whitespace() { + if current.is_empty() { + current.push_str(word); + } else if current.chars().count() + 1 + word.chars().count() <= width { + current.push(' '); + current.push_str(word); + } else { + lines.push(std::mem::take(&mut current)); + current.push_str(word); + } + } + if !current.is_empty() { + lines.push(current); + } + lines +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::build_command; + + // These assertions are deliberately column-exact. clap runs + // `StyledStr::wrap` over `after_help`, which is a no-op only because this + // crate builds clap without the `wrap_help` feature; if workspace feature + // unification ever turns that on, clap reflows the tree and destroys the + // alignment. A looser `contains("send")` assertion would not notice. + #[test] + fn long_help_lists_each_group_with_its_subcommands() { + let help = build_command().render_long_help().to_string(); + + assert!( + help.contains( + "\n messages Send, read, search, and manage messages\n" + ), + "group line missing or misaligned:\n{help}" + ); + assert!( + help.contains("\n send Send a message to a channel\n"), + "subcommand line missing or misaligned:\n{help}" + ); + assert!( + help.contains("\n draft-create Open a prefilled create-agent form in the owner's Buzz Desktop\n"), + "subcommand line missing or misaligned:\n{help}" + ); + } + + #[test] + fn long_help_descends_past_the_second_level() { + let help = build_command().render_long_help().to_string(); + + assert!( + help.contains("\n protect Manage branch and tag protection rules on one of your repositories\n"), + "nested group line missing or misaligned:\n{help}" + ); + assert!( + help.contains( + "\n list List the repository's protection rules\n" + ), + "third-level line missing or misaligned:\n{help}" + ); + } + + #[test] + fn long_help_wraps_long_descriptions_into_the_description_column() { + let help = build_command().render_long_help().to_string(); + + assert!( + help.contains( + "\n emoji Manage your custom emoji set (workspace palette is the union of all\n members' sets)\n" + ), + "wrapped description missing or misaligned:\n{help}" + ); + } + + #[test] + fn short_help_stays_at_the_group_level() { + let help = build_command().render_help().to_string(); + + assert!( + help.contains( + "\n messages Send, read, search, and manage messages\n" + ), + "group line missing from short help:\n{help}" + ); + assert!( + !help.contains("\n send "), + "short help should not descend into subcommands:\n{help}" + ); + } + + #[test] + fn tree_omits_claps_generated_help_subcommand() { + let tree = render(&build_command(), usize::MAX); + + assert!(!tree.lines().any(|line| line.trim() == "help")); + assert!(!tree.contains("Print this message or the help of the given subcommand")); + } + + #[test] + fn depth_one_lists_groups_only() { + let cmd = build_command(); + let groups = render(&cmd, 1); + + assert!(groups.contains("\n messages ")); + assert!(!groups.contains("\n send ")); + } + + #[test] + fn wrap_keeps_words_intact_and_respects_width() { + assert_eq!(wrap("", 10), Vec::::new()); + assert_eq!(wrap("one two three", 7), vec!["one two", "three"]); + assert_eq!( + wrap("supercalifragilistic x", 5), + vec!["supercalifragilistic", "x"] + ); + } +} diff --git a/crates/buzz-cli/src/lib.rs b/crates/buzz-cli/src/lib.rs index dfb75e487f0..b38486417a8 100644 --- a/crates/buzz-cli/src/lib.rs +++ b/crates/buzz-cli/src/lib.rs @@ -2,10 +2,11 @@ pub mod agent_management; mod client; mod commands; mod error; +mod help_tree; mod links; mod validate; -use clap::{Parser, Subcommand}; +use clap::{CommandFactory, FromArgMatches, Parser, Subcommand}; use client::BuzzClient; use error::CliError; use nostr::Keys; @@ -38,7 +39,7 @@ where // double-install returns Err and is harmless. let _ = rustls::crypto::ring::default_provider().install_default(); - let cli = match Cli::try_parse_from(args) { + let cli = match parse_args(args) { Ok(cli) => cli, Err(e) => { if e.use_stderr() { @@ -60,6 +61,41 @@ where } } +/// Root help layout. Identical to clap's default except that `{subcommands}` +/// is dropped and the command tree arrives through `{after-help}` instead, so +/// the group list is not printed twice. `{options}` and `{subcommands}` emit no +/// heading of their own — clap writes those from `write_all_args`, which this +/// template bypasses — so the headings are spelled out here. +const ROOT_HELP_TEMPLATE: &str = "\ +{before-help}{about-with-newline} +{usage-heading} {usage}{after-help} + +Options: +{options}"; + +/// The root command with the agent-friendly command tree installed. +/// +/// clap picks `after_long_help` for `--help` and falls back to `after_help` +/// for `-h`, which is what gives the two depths: `-h` keeps the group-level +/// summary, `--help` shows every subcommand under every group. +fn build_command() -> clap::Command { + let cmd = Cli::command(); + let groups = help_tree::render(&cmd, 1); + let full = help_tree::render(&cmd, usize::MAX); + cmd.help_template(ROOT_HELP_TEMPLATE) + .after_help(format!("Commands:\n{groups}")) + .after_long_help(format!("Commands:\n{full}")) +} + +fn parse_args(args: I) -> Result +where + I: IntoIterator, + S: Into + Clone, +{ + let matches = build_command().try_get_matches_from(args)?; + Cli::from_arg_matches(&matches) +} + #[derive(Parser)] #[command( name = "buzz", From 0fe7c28f368504b827bf46688a229df75e195f9e Mon Sep 17 00:00:00 2001 From: lenny Date: Fri, 11 Sep 2026 13:06:35 -0400 Subject: [PATCH 25/26] [lenny] fix: drop obsolete pi_launcher block (upstream deleted the module) --- crates/buzz-acp/src/lib.rs | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/crates/buzz-acp/src/lib.rs b/crates/buzz-acp/src/lib.rs index a88d8cd6072..7bb5890fee5 100644 --- a/crates/buzz-acp/src/lib.rs +++ b/crates/buzz-acp/src/lib.rs @@ -2704,9 +2704,8 @@ async fn tokio_main() -> Result<()> { None } else { // Build standing context once under the configured policy, before any - // agent process starts. Pi consumes this through its native - // `--system-prompt`; other ACP agents consume the same bytes through - // session/new or legacy first-turn framing. + // session/new. The Pi adapter fork now consumes it through the normal + // ACP path (upstream #7552), as do all other ACP agents. Some( config.session_policy.append_session_model( base_prompt_content @@ -2715,31 +2714,6 @@ async fn tokio_main() -> Result<()> { ), ) }; - // PI_ACP_PI_COMMAND is Buzz-owned. Strip stale/user-provided copies from - // every adapter before optionally installing Buzz's generated Pi launcher. - config - .persona_env_vars - .retain(|(key, _)| !key.eq_ignore_ascii_case(pi_launcher::PI_ACP_PI_COMMAND_ENV)); - let managed_skills_dir = std::path::Path::new(&cwd).join(".agents/skills"); - let inherited_pi_command_is_set = - std::env::var_os(pi_launcher::PI_ACP_PI_COMMAND_ENV).is_some(); - let (pi_launch_override, base_prompt) = pi_launcher::PiLaunchOverride::prepare( - &config.agent_command, - base_prompt, - &managed_skills_dir, - inherited_pi_command_is_set, - ) - .context("failed to prepare Pi launch overrides")?; - if let Some(prepared) = pi_launch_override.as_ref() { - config.persona_env_vars.push(( - pi_launcher::PI_ACP_PI_COMMAND_ENV.to_string(), - prepared.launcher_path().to_string_lossy().into_owned(), - )); - tracing::info!( - skills_dir = %managed_skills_dir.display(), - "configured Pi to consume Buzz standing context and managed skills through native CLI flags" - ); - } let observer = config .relay_observer From 6573251fb59f0ff4375353b64a71a216ed4b1014 Mon Sep 17 00:00:00 2001 From: Michael Feth Date: Fri, 11 Sep 2026 13:27:19 -0400 Subject: [PATCH 26/26] [lenny] fix(desktop): drop unused truncatePubkey import left by the merge --- desktop/src/features/agents/ui/RespondToField.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/desktop/src/features/agents/ui/RespondToField.tsx b/desktop/src/features/agents/ui/RespondToField.tsx index e4e64799cf2..44c6fd146ed 100644 --- a/desktop/src/features/agents/ui/RespondToField.tsx +++ b/desktop/src/features/agents/ui/RespondToField.tsx @@ -5,11 +5,7 @@ import { parsePubkeyInput, } from "@/features/agents/lib/respondToAllowlist"; import { parsePubkeyInput as parseCanonicalPubkey } from "@/shared/lib/nostrUtils"; -import { - normalizePubkey, - truncateNpub, - truncatePubkey, -} from "@/shared/lib/pubkey"; +import { normalizePubkey, truncateNpub } from "@/shared/lib/pubkey"; import { PubKey } from "@/shared/ui/PubKey"; import { useIsArchivedPredicate } from "@/features/identity-archive/hooks"; import { useUserSearchQuery } from "@/features/profile/hooks";