Skip to content

smart_intents

smart_intents

Diapason-style smart browser/app intents (pattern matching, FR + EN).

Ports the fast heuristics from jarvis-ai-assistant SmartBrowserService / AICommandParser.tryFastParse — no cloud AI required for obvious commands.

Classes

SmartIntent dataclass

SmartIntent(
    kind: str,
    action: str = "",
    query: str = "",
    url: str = "",
    app: str = "",
    to: str = "",
    subject: str = "",
    body: str = "",
    confidence: float = 0.0,
    reasoning: str = "",
)

Resolved open/search/play intent.

Functions:

resolve_youtube_watch_url

resolve_youtube_watch_url(
    query: str,
    *,
    timeout: float = 4.0,
    fetch=None,
    mix: bool = False,
) -> str

Top YouTube search hit as a watch URL, or "" when resolution fails.

No API key: one GET on the public results page. Failure is a normal state (offline, layout change, consent wall) — the caller falls back to opening the results page, which is what happened before this existed.

Source code in src/diapason/desktop/smart_intents.py
def resolve_youtube_watch_url(
    query: str,
    *,
    # 4 s, not 1.5: this runs DURING a voice turn, while Whisper, the LLM and
    # the TTS are saturating the machine — measured 0.9 s idle, and the tight
    # budget silently downgraded every « joue X » to a results page under
    # load. Losing the feature to save 2.5 s of worst case is a bad trade.
    timeout: float = 4.0,
    fetch=None,
    mix: bool = False,
) -> str:
    """Top YouTube search hit as a watch URL, or "" when resolution fails.

    No API key: one GET on the public results page. Failure is a normal
    state (offline, layout change, consent wall) — the caller falls back to
    opening the results page, which is what happened before this existed.
    """
    url = "https://www.youtube.com/results?search_query=" + quote_plus(query)
    # Exempt from local_only, narrowly: this single-purpose lookup completes
    # a user-commanded browse of the SAME destination — the browser open that
    # follows discloses the same query either way. Boundary documented in
    # core/local_mode.py.
    try:
        if fetch is None:

            def fetch(u: str) -> str:
                import urllib.request

                req = urllib.request.Request(
                    u,
                    headers={
                        "User-Agent": "Mozilla/5.0",
                        "Accept-Language": "fr,en;q=0.8",
                    },
                )
                with urllib.request.urlopen(req, timeout=timeout) as r:
                    return r.read(2_000_000).decode("utf-8", "replace")

        m = _YT_TOP_RESULT.search(fetch(url))
        if m:
            video = m.group(1)
            if mix:
                # La radio de YouTube : la liste RD<id> enchaîne des titres
                # voisins sans fin — c'est « mets de la musique », pas
                # « mets UNE musique » (demandé le 23 août 2026).
                return "https://www.youtube.com/watch?v=" + video + "&list=RD" + video
            return "https://www.youtube.com/watch?v=" + video
    except Exception:  # noqa: BLE001 - resolution is best-effort by design
        # Best-effort, but never mute: a silent "" here downgrades every
        # « joue X » to a results page with nothing in the logs to say why.
        logging.getLogger(__name__).debug(
            "youtube top-result resolution failed for %r", query, exc_info=True
        )
    return ""

parse_smart_intent

parse_smart_intent(command: str) -> SmartIntent

Parse a spoken/typed command into a SmartIntent (best-effort).

Source code in src/diapason/desktop/smart_intents.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
def parse_smart_intent(command: str) -> SmartIntent:
    """Parse a spoken/typed command into a SmartIntent (best-effort)."""
    raw = (command or "").strip()
    if not raw:
        return SmartIntent(kind=KIND_NONE)

    low = raw.lower().strip()

    # A target that already IS a URL must pass through untouched. Before
    # this check, an LLM-built https://www.youtube.com/results?search_query=…
    # entered the YouTube block ("youtube" in low), matched none of the
    # phrase regexes, and was silently replaced by the YouTube home page.
    if re.match(r"^(?:https?://|www\.)\S+$", raw, re.IGNORECASE):
        return SmartIntent(
            kind=KIND_URL,
            url=raw if raw.lower().startswith("http") else f"https://{raw}",
            confidence=0.98,
            reasoning="Direct URL",
        )

    # --- YouTube ---
    if "youtube" in low:
        # Spoken phrasing is full of harmless padding that used to defeat
        # every pattern and fall through to the catch-all, which then
        # SEARCHED the padding itself: « Ouvre-moi YouTube sur mon
        # navigateur et joue-moi la chanson X » became a search for
        # "sur mon navigateur et joue-moi la chanson x". Strip the padding
        # first; the patterns then see the sentence the user meant.
        # « depuis mon navigateur » avait échappé à la liste : la requête
        # devenait littéralement « depuis mon navigateur » (23 août 2026).
        yt = re.sub(
            r"\b(?:dans|sur|depuis|via|avec)\s+(?:mon|ton|le|un)\s+navigateur\b",
            " ",
            low,
        )
        # « joue-moi » → « joue », mais seulement après un verbe de commande :
        # un titre comme « Laisse-moi » doit garder son -moi.
        yt = re.sub(
            r"\b(ouvre|joue|mets|lance|écoute|ecoute|regarde|montre|cherche)"
            r"[-\s]+(?:moi|nous)\b",
            r"\1",
            yt,
        )
        yt = re.sub(r"\bs[’']il\s+(?:te|vous)\s+pla[iî]t\b|\bstp\b", " ", yt)
        yt = re.sub(r"\s+", " ", yt).strip()
        for pat, action in (
            (
                r"(?:ouvre|open)\s+youtube\s+(?:et|and)\s+(?:cherche|search(?:\s+for)?)\s+(.+)",
                "search",
            ),
            (r"youtube\s+(?:cherche|search(?:\s+for)?)\s+(.+)", "search"),
            (r"(?:cherche|search(?:\s+for)?)\s+(.+?)\s+(?:sur|on)\s+youtube", "search"),
            (
                r"(?:joue|play|regarde|watch|écoute|ecoute|mets|lance)"
                r"\s+(.+?)\s+(?:sur|on)\s+youtube",
                "play",
            ),
            # Play verb AFTER the youtube mention: « ouvre youtube et joue X ».
            # The sentence names youtube, so the trailing request is for it.
            (
                r"youtube\b.*?\b(?:joue|play|regarde|watch|écoute|ecoute|mets|lance)\s+(.+)$",
                "play",
            ),
            (r"youtube\s+(.+)", "search"),
        ):
            m = re.search(pat, yt, re.IGNORECASE)
            if m:
                q = m.group(1).strip()
                q = _clean_query(
                    q,
                    "for",
                    "the",
                    "le",
                    "la",
                    "les",
                    "des",
                    "un",
                    "une",
                    "de",
                    "d",
                    "vidéo",
                    "video",
                    "clip",
                    "chanson",
                    "musique",
                    "music",
                    "song",
                )
                # « joue de la musique sur youtube » : le sujet ENTIER est
                # fait de mots vides (« de la musique ») et la requête
                # nettoyée est vide — mais l'intention, elle, est limpide :
                # QUE ÇA JOUE, n'importe quelle musique. L'action play_mix
                # se résout en radio YouTube (watch + list=RD…) qui démarre
                # et s'enchaîne ; l'url ici n'est que le repli si la
                # résolution échoue.
                if not q and action == "play" and re.search(r"\bmusi(?:que|c)\b", yt):
                    return SmartIntent(
                        kind=KIND_YOUTUBE,
                        action="play_mix",
                        query="musique",
                        url="https://music.youtube.com",
                        confidence=0.9,
                        reasoning="De la musique, sans titre : radio YouTube",
                    )
                # « mets la vidéo en pause sur youtube » must not PLAY a
                # video titled "pause" — control words are not queries.
                if q and (action != "play" or q not in _CONTROL_WORDS):
                    return SmartIntent(
                        kind=KIND_YOUTUBE,
                        action=action,
                        query=q,
                        url=(
                            "https://www.youtube.com/results?search_query="
                            + quote_plus(q)
                        ),
                        confidence=0.92,
                        reasoning=f"YouTube {action}",
                    )
        return SmartIntent(
            kind=KIND_URL,
            url="https://www.youtube.com",
            confidence=0.9,
            reasoning="YouTube home",
        )

    # --- Spotify ---
    if (
        "spotify" in low
        or re.search(
            r"\b(?:écoute|ecoute|listen(?:\s+to)?|joue|play|mets?|lance)\b.+"
            r"\b(?:musique|music|song|chanson)\b",
            low,
        )
    ) and not re.search(
        # « mets la musique en pause » est un ordre de contrôle, pas une
        # envie de musique : il ne doit ni chercher « en pause » ni ouvrir
        # l'application.
        r"\b(?:pause|arr[êe]te|arrete|stop|coupe|[ée]teins)\b",
        low,
    ):
        # Search patterns come FIRST and each pattern carries its own
        # action: sniffing play-verbs anywhere in the phrase turned
        # « cherche listen de beyoncé sur spotify » into an autoplay.
        for pat, action in (
            (
                r"(?:ouvre|open)\s+spotify\s+(?:et|and)\s+"
                r"(?:cherche|search(?:\s+for)?)\s+(.+)",
                "search",
            ),
            (r"(?:cherche|search(?:\s+for)?)\s+(.+?)\s+(?:sur|on)\s+spotify", "search"),
            (r"spotify\s+(?:cherche|search(?:\s+for)?)\s+(.+)", "search"),
            (
                r"(?:ouvre|open)\s+spotify\s+(?:et|and)\s+(?:joue|play)\s+(.+)",
                "play",
            ),
            (
                r"(?:joue|play|écoute|ecoute|listen(?:\s+to)?)\s+(.+?)\s+(?:sur|on)\s+spotify",
                "play",
            ),
            (r"spotify\s+(?:joue|play)\s+(.+)", "play"),
            (r"spotify\s+(.+)", "search"),
            (r"(?:joue|play|écoute|ecoute|mets?|lance)\s+(.+)", "play"),
        ):
            m = re.search(pat, low, re.IGNORECASE)
            if m:
                q = _clean_query(
                    m.group(1),
                    "for",
                    "the",
                    "le",
                    "la",
                    "de",
                    "du",
                    "music",
                    "musique",
                    "song",
                    "chanson",
                )
                if q and q not in {"spotify", "app"}:
                    return SmartIntent(
                        kind=KIND_SPOTIFY,
                        action=action,
                        query=q,
                        url=f"spotify:search:{quote(q)}",
                        confidence=0.9,
                        reasoning="Spotify search/play",
                    )
        if "spotify" in low:
            return SmartIntent(
                kind=KIND_APP,
                app="Spotify",
                confidence=0.88,
                reasoning="Open Spotify app",
            )
        # « Mets de la musique » tout court : pas de titre, pas de service —
        # la requête nettoyée est vide mais l'envie est claire. On ouvre le
        # juke-box plutôt que de répondre qu'on ne peut pas (23 août 2026,
        # le chat répondait un refus à cette phrase).
        return SmartIntent(
            kind=KIND_APP,
            app="Spotify",
            confidence=0.85,
            reasoning="De la musique, sans titre : on ouvre Spotify",
        )

    # --- Amazon ---
    if "amazon" in low:
        for pat in (
            r"(?:cherche|search(?:\s+for)?|achète|achete|buy|shop(?:\s+for)?)\s+(.+?)\s+(?:sur|on)\s+amazon",
            r"amazon\s+(?:cherche|search(?:\s+for)?)\s+(.+)",
            r"amazon\s+(.+)",
        ):
            m = re.search(pat, low, re.IGNORECASE)
            if m:
                q = _clean_query(m.group(1), "for", "the", "le", "la", "un", "une")
                if q and q != "amazon":
                    return SmartIntent(
                        kind=KIND_AMAZON,
                        query=q,
                        url=("https://www.amazon.com/s?k=" + quote_plus(q)),
                        confidence=0.9,
                        reasoning="Amazon search",
                    )
        return SmartIntent(
            kind=KIND_URL,
            url="https://www.amazon.com",
            confidence=0.85,
            reasoning="Amazon home",
        )

    # --- Netflix ---
    if "netflix" in low:
        m = re.search(
            r"(?:cherche|search(?:\s+for)?|joue|play|regarde|watch)\s+(.+?)\s+(?:sur|on)\s+netflix|"
            r"netflix\s+(.+)",
            low,
            re.IGNORECASE,
        )
        if m:
            q = _clean_query((m.group(1) or m.group(2) or ""), "for", "the")
            if q and q != "netflix":
                return SmartIntent(
                    kind=KIND_NETFLIX,
                    query=q,
                    url="https://www.netflix.com/search?q=" + quote_plus(q),
                    confidence=0.88,
                    reasoning="Netflix search",
                )
        return SmartIntent(
            kind=KIND_URL,
            url="https://www.netflix.com",
            confidence=0.9,
            reasoning="Netflix home",
        )

    # --- Mail / Messages compose (drafts only; before open-Gmail) ---
    mail_m = re.search(
        r"(?:écris|ecris|rédige|redige|compose|write|envoie|envoyer|send)\s+"
        r"(?:un\s+|an?\s+)?(?:mail|e-?mail|courriel)\s+"
        r"(?:à|a|to)\s+(?P<to>\S+)"
        r"(?:\s+(?:au sujet de|sujet|about|re|subject)\s+(?P<subject>.+?))?"
        r"(?:\s+(?:disant|saying|pour dire|body|:)\s+(?P<body>.+))?$",
        low,
        re.IGNORECASE,
    )
    if not mail_m:
        mail_m = re.search(
            r"(?:email|e-mail)\s+(?P<to>\S+)\s+(?:about|re|sujet)\s+(?P<subject>.+)$",
            low,
            re.IGNORECASE,
        )
    if mail_m:
        to = (mail_m.groupdict().get("to") or "").strip(" .,!?;:")
        subject = (mail_m.groupdict().get("subject") or "").strip(" .,!?;:")
        body = (mail_m.groupdict().get("body") or "").strip()
        if to:
            return SmartIntent(
                kind=KIND_MAIL_COMPOSE,
                to=to,
                subject=subject,
                body=body or subject,
                confidence=0.9,
                reasoning="Mail compose draft",
            )

    msg_m = re.search(
        r"(?:envoie|envoyer|send)\s+(?:un\s+|an?\s+)?(?:message|sms|imessage|texto)\s+"
        r"(?:à|a|to)\s+(?P<to>.+?)"
        r"(?:\s+(?:disant|saying|pour dire|:)\s+(?P<body>.+))?$",
        low,
        re.IGNORECASE,
    )
    if not msg_m:
        msg_m = re.search(
            r"(?:message|text|texte|imessage)\s+(?:à|a|to\s+)?(?P<to>\S+)"
            r"(?:\s+(?:disant|saying|pour dire|:)\s+(?P<body>.+))?$",
            low,
            re.IGNORECASE,
        )
    if msg_m:
        to = (msg_m.group("to") or "").strip(" .,!?;:")
        body = (msg_m.groupdict().get("body") or "").strip()
        # Don't steal bare "messages" / "message" app opens
        if to and to not in {"messages", "message", "sms"}:
            return SmartIntent(
                kind=KIND_MESSAGES_COMPOSE,
                to=to,
                body=body,
                confidence=0.88,
                reasoning="Messages compose draft",
            )

    # --- Gmail / email web ---
    # « gmail » doit être DIT pour aller au web. La forme française « ouvre
    # (mes) mails » déclenchait cette règle et envoyait mail.google.com dans
    # un navigateur alors que Mail.app est installée — pour l'utilisateur,
    # « Mail ne s'ouvre pas ». L'application installée gagne ; le site n'est
    # que le repli de qui le nomme.
    if "gmail" in low or re.search(
        r"\b(?:check(?:\s+my)?\s+email|check\s+mail)\b",
        low,
    ):
        if "apple mail" not in low:
            return SmartIntent(
                kind=KIND_GMAIL,
                url="https://mail.google.com",
                confidence=0.9,
                reasoning="Gmail",
            )

    # --- WhatsApp Web ---
    if "whatsapp" in low and any(
        w in low for w in ("web", "chrome", "browser", "navigateur")
    ):
        return SmartIntent(
            kind=KIND_URL,
            url="https://web.whatsapp.com",
            confidence=0.95,
            reasoning="WhatsApp Web",
        )

    # --- Social homes ---
    for name, url in (
        ("facebook", "https://www.facebook.com"),
        ("instagram", "https://www.instagram.com"),
        ("linkedin", "https://www.linkedin.com"),
        ("reddit", "https://www.reddit.com"),
        ("twitter", "https://x.com"),
    ):
        if re.search(
            rf"\b(?:ouvre|open|go to|va sur|check|vérifie)\s+{name}\b|\b{name}\b$",
            low,
        ):
            # Don't steal "open twitter and search …" if we add later
            if "search" in low or "cherche" in low:
                continue
            return SmartIntent(
                kind=KIND_URL, url=url, confidence=0.9, reasoning=f"{name} home"
            )

    # --- Explicit website map / open X.com ---
    m = re.search(
        r"(?:ouvre|open|go to|va sur)\s+"
        r"(?:le site |the (?:site|page) )?"
        r"(?P<site>[\w.-]+\.(?:com|org|net|io|ai|co|fr|dev))\b",
        low,
    )
    if m:
        host = m.group("site")
        return SmartIntent(
            kind=KIND_URL,
            url=f"https://{host}",
            confidence=0.95,
            reasoning="Navigate domain",
        )

    # --- Open native app / known website alias ---
    m = re.search(
        r"^\s*(?:please\s+)?(?:ouvre|ouvrir|open|lance|lancer|launch|start|démarre|"
        r"show|montre)\s+"
        r"(?:l['’]|le |la |les |the |app |application )?"
        r"(?P<target>.+?)\s*$",
        low,
        re.IGNORECASE,
    )
    if m:
        target = m.group("target").strip().strip(".!?")
        # "youtube and search for cats" already handled above if youtube in string
        target = re.sub(r"^(?:de\s+|d['’]\s*|du\s+)", "", target)
        if target in _NATIVE_APPS:
            return SmartIntent(
                kind=KIND_APP,
                app=_NATIVE_APPS[target],
                confidence=0.95,
                reasoning="Native app",
            )
        # L'application INSTALLÉE gagne sur le raccourci web : qui a
        # l'application ChatGPT ne veut pas chatgpt.com dans un onglet.
        # L'index couvre tout le disque — chaque application du Mac devient
        # ouvrable à la voix sans figurer dans une table écrite à la main.
        from diapason.desktop.app_index import APP_INDEX

        installee = APP_INDEX.lookup(target)
        if installee is not None:
            return SmartIntent(
                kind=KIND_APP,
                app=installee,
                confidence=0.94,
                reasoning="Installed app",
            )
        if target in _WEBSITES:
            return SmartIntent(
                kind=KIND_URL,
                url=_WEBSITES[target],
                confidence=0.92,
                reasoning="Website shortcut",
            )
        # "spotify app"
        if target.endswith(" app") and target[:-4] in _NATIVE_APPS:
            return SmartIntent(
                kind=KIND_APP,
                app=_NATIVE_APPS[target[:-4]],
                confidence=0.93,
                reasoning="Native app suffix",
            )

    # Bare website alias
    if low in _WEBSITES:
        return SmartIntent(
            kind=KIND_URL, url=_WEBSITES[low], confidence=0.9, reasoning="Bare site"
        )
    if low in _NATIVE_APPS:
        return SmartIntent(
            kind=KIND_APP, app=_NATIVE_APPS[low], confidence=0.9, reasoning="Bare app"
        )

    return SmartIntent(kind=KIND_NONE, reasoning="No smart match")

execute_smart_intent

execute_smart_intent(
    intent: SmartIntent, *, browser: str = ""
) -> Optional[ToolResult]

Execute a SmartIntent via desktop helpers. Returns None if KIND_NONE.

Source code in src/diapason/desktop/smart_intents.py
def execute_smart_intent(
    intent: SmartIntent, *, browser: str = ""
) -> Optional[ToolResult]:
    """Execute a SmartIntent via desktop helpers. Returns None if KIND_NONE."""
    if intent.kind == KIND_NONE:
        return None

    from diapason.tools.desktop_tools import (
        open_application,
        open_in_browser,
    )

    if intent.kind == KIND_MAIL_COMPOSE:
        from diapason.tools.voice_mac_tools import MailComposeTool

        return MailComposeTool().execute(
            to=intent.to,
            subject=intent.subject,
            body=intent.body,
        )

    if intent.kind == KIND_MESSAGES_COMPOSE:
        from diapason.tools.voice_mac_tools import MessagesComposeTool

        return MessagesComposeTool().execute(
            recipient=intent.to,
            body=intent.body,
        )

    if intent.kind == KIND_YOUTUBE and intent.action == "play" and intent.query:
        watch = resolve_youtube_watch_url(intent.query)
        if watch:
            res = open_in_browser(watch, browser=browser)
            if res.success:
                return ToolResult(
                    tool_name=res.tool_name,
                    content=(
                        f"Playing top YouTube result for '{intent.query}': {watch}"
                    ),
                    success=True,
                    metadata={"watch_url": watch, "query": intent.query},
                )
        res = open_in_browser(intent.url, browser=browser)
        return ToolResult(
            tool_name=res.tool_name,
            content=(
                f"Opened YouTube SEARCH RESULTS for '{intent.query}' (could not "
                "resolve the top video; the user must click one to play)."
            ),
            success=res.success,
            metadata={"query": intent.query},
        )

    if intent.kind == KIND_SPOTIFY and intent.query:
        # Prefer native Spotify URI (app), fall back to web search URL
        from diapason.tools.voice_mac_tools import SpotifyPlayTool

        result = SpotifyPlayTool().execute(
            query=intent.query, action=intent.action or "search"
        )
        if result.success or not (getattr(result, "metadata", None) or {}).get(
            "spotify_missing"
        ):
            return result
        # Spotify is not installed on this machine: the user still asked to
        # HEAR something, so play the top YouTube result instead of reading
        # an installation error out loud. Only for a PLAY intent — a mere
        # search must never turn into an unexpected autoplay.
        if intent.action != "play":
            return result
        watch = resolve_youtube_watch_url(intent.query)
        if watch:
            res = open_in_browser(watch, browser=browser)
            if res.success:
                return ToolResult(
                    tool_name="spotify_play",
                    content=(
                        "Spotify is not installed; playing the top YouTube "
                        f"result for '{intent.query}' instead: {watch}"
                    ),
                    success=True,
                    metadata={"fallback": "youtube", "watch_url": watch},
                )
        return result

    if intent.kind == KIND_APP and intent.app:
        return open_application(intent.app)

    if intent.url:
        return open_in_browser(intent.url, browser=browser)

    if intent.kind == KIND_WEB_SEARCH and intent.query:
        from diapason.tools.desktop_tools import web_search_url

        return open_in_browser(web_search_url(intent.query), browser=browser)

    return ToolResult(
        tool_name="smart_intent",
        content=f"Unhandled intent kind={intent.kind}",
        success=False,
    )

try_execute_smart_command

try_execute_smart_command(
    command: str, *, browser: str = ""
) -> Optional[ToolResult]

Parse + execute in one shot. None if no rich intent matched.

Source code in src/diapason/desktop/smart_intents.py
def try_execute_smart_command(
    command: str, *, browser: str = ""
) -> Optional[ToolResult]:
    """Parse + execute in one shot. None if no rich intent matched."""
    intent = parse_smart_intent(command)
    if intent.kind == KIND_NONE:
        return None
    result = execute_smart_intent(intent, browser=browser)
    if result is None:
        return None
    # Annotate metadata
    meta = dict(getattr(result, "metadata", None) or {})
    meta.update(
        {
            "smart_kind": intent.kind,
            "smart_reasoning": intent.reasoning,
            "smart_confidence": intent.confidence,
        }
    )
    return ToolResult(
        tool_name=result.tool_name or "smart_intent",
        content=result.content,
        success=result.success,
        metadata=meta,
    )