- deadline: _result()/_abandon() replace 7 BoundedResult constructions and 2 cancel+callback sites; timers handled as a list; dead raise_if_timed_out removed. - file_safety: retired classify_cross_profile_target (0 refs; get_cross_profile_warning stub kept for external callers), _home_and_resolved/_mirror_warning shared by the sandbox/container mirror guards; _find_sandbox_mirror_segments inlined. - estop: _hermes_home/_canonical_root now the file_safety helpers; _reset_log_state_for_tests inlined into its only test. - subagent_lifecycle: _validate_request driven by _UNSUPPORTED_REQUEST_FIELDS table. - turn_liveness: dead start()/_abort_message removed, _emit_warning shared. - process_bootstrap: _enable_happy_eyeballs reused by the client variant. - Comment/docstring compaction across the remaining leaf modules.
47 lines
1.7 KiB
Python
47 lines
1.7 KiB
Python
"""Token-free detection of user *reactions* to the agent.
|
|
|
|
The only reaction today is ``vibe`` — affection/gratitude aimed at the agent
|
|
(``ily``, ``<3``, ``good bot``, a heart emoji). Detection is a curated regex:
|
|
no model call. Single source of truth for the CLI pet, TUI heart and desktop
|
|
hearts via ``AIAgent.reaction_callback``. :func:`detect_reaction` returns a
|
|
*kind* string so new kinds can be added without touching callers. Matches
|
|
affection specifically, not general positive sentiment ("this is great" does
|
|
NOT fire).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
#: The affection/gratitude reaction — the only kind today.
|
|
VIBE = "vibe"
|
|
|
|
# Narrow lexicon: gratitude + love aimed at the agent, hearts, ``<3`` (not ``</3``).
|
|
_VIBE_RE = re.compile(
|
|
"|".join(
|
|
(
|
|
r"\bgood\s*bot\b",
|
|
r"\bi\s*(?:love|luv)\s*(?:you|u|ya)\b",
|
|
r"\b(?:love|luv)\s*(?:you|u|ya)\b",
|
|
r"\bily(?:sm)?\b",
|
|
r"\bthank\s*(?:you|u)\b",
|
|
r"\b(?:thanks|thx|tysm|ty)\b",
|
|
r"<3+", # <3, <33 … but not </3
|
|
# Hearts + affection faces (❤ ♥ 🥰 😍 😘 💕 💖 💗 💞 💛 💜 💚 💙 💓 💘 💝 🩷).
|
|
r"[\u2764\u2665"
|
|
r"\U0001F970\U0001F60D\U0001F618"
|
|
r"\U0001F495\U0001F496\U0001F497\U0001F49E"
|
|
r"\U0001F49B\U0001F49C\U0001F49A\U0001F499"
|
|
r"\U0001F493\U0001F498\U0001F49D\U0001FA77]",
|
|
)
|
|
),
|
|
re.IGNORECASE,
|
|
)
|
|
|
|
|
|
def detect_reaction(text: str | None) -> str | None:
|
|
"""Return the reaction kind for *text* (currently :data:`VIBE`), or ``None``."""
|
|
if not text:
|
|
return None
|
|
return VIBE if _VIBE_RE.search(text) else None
|