@innvisor/conny-ai 9.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/.env.example +68 -0
  2. package/CHANGELOG.md +54 -0
  3. package/LICENSE +21 -0
  4. package/README.md +369 -0
  5. package/brand-assets/A_dark_luxury_web_background_202605210700.jpeg +0 -0
  6. package/brand-assets/Conny.web.logo.png +0 -0
  7. package/brand-assets/Logo_Conny_Petalo_Claro.png +0 -0
  8. package/brand-assets/cl-nica-de-las-am-ricas/manifest.json +22 -0
  9. package/brand-assets/cl-nica-de-las-am-ricas/processed/business-identity.txt +11 -0
  10. package/brand-assets/cl-nica-de-las-am-ricas/raw/business-identity.txt +11 -0
  11. package/brand-assets/cl-nica-las-am-ricas/manifest.json +22 -0
  12. package/brand-assets/cl-nica-las-am-ricas/processed/business-identity.txt +11 -0
  13. package/brand-assets/cl-nica-las-am-ricas/raw/business-identity.txt +11 -0
  14. package/brand-assets/conny-demo/manifest.json +22 -0
  15. package/brand-assets/conny-demo/processed/business-identity.txt +7 -0
  16. package/brand-assets/conny-demo/raw/business-identity.txt +7 -0
  17. package/brand-assets/conny-logo.png +0 -0
  18. package/brand-assets/web.background.png +0 -0
  19. package/brand_assets.py +323 -0
  20. package/conny +28 -0
  21. package/conny-chat.py +579 -0
  22. package/conny-omni.py +3843 -0
  23. package/conny.py +113 -0
  24. package/conny_agents/__init__.py +1 -0
  25. package/conny_agents/agenda.py +1 -0
  26. package/conny_agents/captacion.py +1 -0
  27. package/conny_agents/conocimiento.py +1 -0
  28. package/conny_agents/escalacion.py +1 -0
  29. package/conny_agents/objeciones.py +1 -0
  30. package/conny_agents/seguimiento.py +1 -0
  31. package/conny_app.py +287 -0
  32. package/conny_audio.py +350 -0
  33. package/conny_audio_learn.py +84 -0
  34. package/conny_brain_v10.py +804 -0
  35. package/conny_bridge.py +656 -0
  36. package/conny_calendar.py +169 -0
  37. package/conny_cli.py +11784 -0
  38. package/conny_cli_bb.py +437 -0
  39. package/conny_commands.py +243 -0
  40. package/conny_config.py +215 -0
  41. package/conny_core/__init__.py +3 -0
  42. package/conny_core/conversation_engine.py +446 -0
  43. package/conny_core/first_turn_ops.py +287 -0
  44. package/conny_core/persona_registry.py +157 -0
  45. package/conny_core/prompt_ops.py +561 -0
  46. package/conny_cron.py +72 -0
  47. package/conny_demo_v2.py +209 -0
  48. package/conny_demo_voice.py +134 -0
  49. package/conny_design.py +43 -0
  50. package/conny_doctor.py +319 -0
  51. package/conny_domino.py +696 -0
  52. package/conny_generator.py +447 -0
  53. package/conny_google_auth.py +159 -0
  54. package/conny_i18n.py +619 -0
  55. package/conny_init.py +509 -0
  56. package/conny_integrations/__init__.py +4 -0
  57. package/conny_integrations/llm.py +1 -0
  58. package/conny_integrations/vault.py +77 -0
  59. package/conny_integrations/whatsapp.py +1 -0
  60. package/conny_intelligence.py +65 -0
  61. package/conny_learning.py +154 -0
  62. package/conny_memory.py +243 -0
  63. package/conny_memory_engine.py +292 -0
  64. package/conny_nova_proxy.py +170 -0
  65. package/conny_nuke_robot_phrases.py +493 -0
  66. package/conny_pairing.py +253 -0
  67. package/conny_patch.py +291 -0
  68. package/conny_persona_cli.py +150 -0
  69. package/conny_router.py +308 -0
  70. package/conny_runtime_ops.py +271 -0
  71. package/conny_session.py +516 -0
  72. package/conny_skills/__init__.py +1 -0
  73. package/conny_skills/demo_mode.py +35 -0
  74. package/conny_skills/text_processing.py +1 -0
  75. package/conny_skills/tone_detection.py +1 -0
  76. package/conny_smart_features.py +333 -0
  77. package/conny_studio.py +161 -0
  78. package/conny_sync_fix.py +306 -0
  79. package/conny_tui.py +512 -0
  80. package/conny_tui_select.py +202 -0
  81. package/conny_ultra_config.py +411 -0
  82. package/conny_uncertainty.py +174 -0
  83. package/conny_utils.py +87 -0
  84. package/conny_voice.py +156 -0
  85. package/conny_voice_engine.py +124 -0
  86. package/conny_web_search.py +66 -0
  87. package/conny_weekly_report.py +85 -0
  88. package/conny_worm.py +88 -0
  89. package/core/__init__.py +25 -0
  90. package/ecosystem.config.js +24 -0
  91. package/fix_init.py +27 -0
  92. package/install.sh +78 -0
  93. package/knowledge_base.py +330 -0
  94. package/nova/rules/default.yaml +37 -0
  95. package/nova_bridge.py +509 -0
  96. package/npm/conny.js +471 -0
  97. package/package.json +102 -0
  98. package/personas/conny/base/default.yaml +35 -0
  99. package/personas/conny/base/estetica_whatsapp.yaml +36 -0
  100. package/requirements.txt +14 -0
  101. package/run.sh +47 -0
  102. package/search.py +465 -0
  103. package/smart_handoff.py +1150 -0
  104. package/src/__init__.py +0 -0
  105. package/src/conny/__init__.py +0 -0
  106. package/src/conny/admin/__init__.py +0 -0
  107. package/src/conny/admin/api.py +234 -0
  108. package/src/conny/admin/dashboard.py +772 -0
  109. package/src/conny/api/__init__.py +0 -0
  110. package/src/conny/api/routes.py +8851 -0
  111. package/src/conny/brain/__init__.py +15 -0
  112. package/src/conny/brain/engine.py +804 -0
  113. package/src/conny/brain/learning.py +154 -0
  114. package/src/conny/brain/memory.py +324 -0
  115. package/src/conny/brain/smart_features.py +333 -0
  116. package/src/conny/brain/uncertainty.py +167 -0
  117. package/src/conny/channels/__init__.py +0 -0
  118. package/src/conny/channels/audio.py +316 -0
  119. package/src/conny/channels/cli.py +11795 -0
  120. package/src/conny/channels/logo_art.py +11 -0
  121. package/src/conny/channels/voice.py +156 -0
  122. package/src/conny/core/__init__.py +0 -0
  123. package/src/conny/core/config.py +215 -0
  124. package/src/conny/core/cron.py +72 -0
  125. package/src/conny/core/messenger.py +563 -0
  126. package/src/conny/core/router.py +297 -0
  127. package/src/conny/core/session.py +312 -0
  128. package/src/conny/demo/__init__.py +0 -0
  129. package/src/conny/demo/handler.py +3110 -0
  130. package/src/conny/integrations/__init__.py +19 -0
  131. package/src/conny/integrations/calendar.py +169 -0
  132. package/src/conny/integrations/knowledge.py +312 -0
  133. package/src/conny/integrations/search.py +66 -0
  134. package/src/conny/personas/__init__.py +0 -0
  135. package/src/conny/personas/generator.py +447 -0
  136. package/src/conny/production/__init__.py +0 -0
  137. package/src/conny/production/domino.py +696 -0
  138. package/src/conny/production/guard.py +550 -0
  139. package/src/conny/production/handoff.py +1150 -0
  140. package/src/conny/production/monitor.py +353 -0
  141. package/src/conny/utils/__init__.py +2 -0
  142. package/src/conny/utils/helpers.py +75 -0
  143. package/src/conny/utils/i18n.py +619 -0
  144. package/src/core/admin_engines.py +772 -0
  145. package/src/core/globals.py +11845 -0
  146. package/src/core/orchestrator.py +273 -0
  147. package/src/core/production_monitor.py +353 -0
  148. package/src/core/runtime.py +5487 -0
  149. package/src/domain/onboarding_flow.py +230 -0
  150. package/src/domain/prompts/__init__.py +1 -0
  151. package/src/domain/prompts/prospect_pitch.py +282 -0
  152. package/src/domain/send_guard.py +636 -0
  153. package/src/domain/swarm/queen.py +96 -0
  154. package/src/infrastructure/llm_providers/engine.py +487 -0
  155. package/src/interfaces/mcp_server.py +73 -0
  156. package/src/interfaces/nova_bridge.py +58 -0
  157. package/src/interfaces/web/admin_api.py +1379 -0
  158. package/src/interfaces/web/app.py +9408 -0
  159. package/src/interfaces/web/demo_handler.py +3450 -0
  160. package/src/interfaces/web/static/generate_avatars.py +46 -0
  161. package/v7/__init__.py +46 -0
  162. package/v7/agents/__init__.py +46 -0
  163. package/v7/agents/agenda.py +77 -0
  164. package/v7/agents/base.py +216 -0
  165. package/v7/agents/captacion.py +60 -0
  166. package/v7/agents/conocimiento.py +69 -0
  167. package/v7/agents/escalacion.py +83 -0
  168. package/v7/agents/objeciones.py +109 -0
  169. package/v7/agents/seguimiento.py +71 -0
  170. package/v7/memory/__init__.py +46 -0
  171. package/v7/memory/patient_profile.py +200 -0
  172. package/v7/orchestrator.py +275 -0
  173. package/v7/postprocess.py +127 -0
  174. package/v7/router.py +239 -0
  175. package/verify_conversation_impl.py +48 -0
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env python3
2
+ """conny_demo_v2.py — Ultra-realistic demo with Colombian personas and memory."""
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import random
9
+ import sys
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Dict, List, Optional
13
+
14
+ import httpx
15
+
16
+ API_URL = "http://localhost:8001/test"
17
+ MASTER_KEY = os.getenv("MASTER_API_KEY", "conny_master_2026_santiago")
18
+
19
+ DEMO_PERSONAS = {
20
+ "dona_carmen": {
21
+ "profile": "Mujer 65 años, Medellín, no tecnológica, formal",
22
+ "typing_wpm": 20,
23
+ "typos": True,
24
+ "messages": [
25
+ "buenas tardes quiero saber si hay cita para el medico",
26
+ "es que me duele el pecho hace dias",
27
+ "cuanto vale la consulta",
28
+ "y el doctor atiende mañana",
29
+ "no pues gracias mija que Dios la bendiga",
30
+ ],
31
+ },
32
+ "juan_ejecutivo": {
33
+ "profile": "Hombre 35 años, ocupado, directo, WhatsApp",
34
+ "typing_wpm": 60,
35
+ "typos": False,
36
+ "messages": [
37
+ "Hola! Cita cardio para mañana si hay?",
38
+ "Cuánto tarda la espera normalmente",
39
+ "Ok perfecto, quedamos así entonces 👍",
40
+ ],
41
+ },
42
+ "mama_preocupada": {
43
+ "profile": "Mamá 40 años, hijo enfermo, ansiosa",
44
+ "typing_wpm": 45,
45
+ "typos": False,
46
+ "messages": [
47
+ "Hola urgente mi hijo de 8 años tiene fiebre alta",
48
+ "Me pueden atender hoy??",
49
+ "Tiene 39.5 grados y lleva 2 días así",
50
+ "A qué hora hay pediatra",
51
+ ],
52
+ },
53
+ "joven_curioso": {
54
+ "profile": "Hombre 22 años, primera vez, informal",
55
+ "typing_wpm": 70,
56
+ "typos": True,
57
+ "messages": [
58
+ "ey hola q tal",
59
+ "quiero saber si hacen limpieza facial",
60
+ "y eso duele? jaja",
61
+ "bueno listo agendame pa cuando haya",
62
+ "vale gracias bro",
63
+ ],
64
+ },
65
+ "abuela_tecnologica": {
66
+ "profile": "Mujer 72 años, nietos le enseñaron WhatsApp, muy educada",
67
+ "typing_wpm": 15,
68
+ "typos": True,
69
+ "messages": [
70
+ "Buenos dias señorita",
71
+ "Disculpe la molestia quisiera saber si puedo sacar una cita",
72
+ "Es para un control de tension arterial",
73
+ "Mi nombre es Rosa Elena Martinez",
74
+ "Muchas gracias señorita muy amable Dios la bendiga",
75
+ ],
76
+ },
77
+ }
78
+
79
+ TYPO_MAP = {
80
+ "que": "q", "para": "pa", "por favor": "porfa", "está": "esta",
81
+ "también": "tambien", "después": "despues", "cuánto": "cuanto",
82
+ }
83
+
84
+
85
+ def inject_typos(text: str, rate: float = 0.05) -> str:
86
+ """Inject realistic typos based on rate."""
87
+ words = text.split()
88
+ result = []
89
+ for w in words:
90
+ if random.random() < rate and len(w) > 3:
91
+ # Swap two adjacent chars
92
+ i = random.randint(0, len(w) - 2)
93
+ w = w[:i] + w[i + 1] + w[i] + w[i + 2:]
94
+ result.append(w)
95
+ return " ".join(result)
96
+
97
+
98
+ def simulate_typing_delay(text: str, wpm: int = 35) -> float:
99
+ """Calculate realistic typing delay."""
100
+ chars_per_second = (wpm * 5) / 60
101
+ delay = len(text) / chars_per_second
102
+ return min(delay, 6.0)
103
+
104
+
105
+ def color(text, code):
106
+ return f"\033[{code}m{text}\033[0m"
107
+
108
+
109
+ async def send_to_conny(message: str, chat_id: str) -> Dict:
110
+ """Send message to Conny API."""
111
+ async with httpx.AsyncClient(timeout=30.0) as client:
112
+ r = await client.post(
113
+ API_URL,
114
+ json={"message": message, "chat_id": chat_id},
115
+ headers={"X-Master-Key": MASTER_KEY, "Content-Type": "application/json"},
116
+ )
117
+ return r.json()
118
+
119
+
120
+ async def run_demo(persona_key: str = "dona_carmen", instance_id: str = "default"):
121
+ """Run a full multi-turn demo with a persona."""
122
+ persona = DEMO_PERSONAS.get(persona_key)
123
+ if not persona:
124
+ print(f"Persona '{persona_key}' not found. Available: {list(DEMO_PERSONAS.keys())}")
125
+ return
126
+
127
+ chat_id = f"demo_{persona_key}_{int(time.time())}"
128
+
129
+ print()
130
+ print(color("╔══════════════════════════════════════════════╗", "1;36"))
131
+ print(color("║ CONNY DEMO v2.0 — Simulación Realista ║", "1;36"))
132
+ print(color("╚══════════════════════════════════════════════╝", "1;36"))
133
+ print(f" Persona: {color(persona_key, '1')} — {persona['profile']}")
134
+ print(f" Instance: {instance_id}")
135
+ print(f" Velocidad: {persona['typing_wpm']} WPM | Typos: {'sí' if persona['typos'] else 'no'}")
136
+ print("─" * 50)
137
+ print()
138
+
139
+ for i, msg in enumerate(persona["messages"]):
140
+ # Simulate typing delay
141
+ delay = simulate_typing_delay(msg, persona["typing_wpm"])
142
+ if i > 0:
143
+ # Thinking time before next message
144
+ await asyncio.sleep(random.uniform(1.5, 4.0))
145
+
146
+ # Apply typos if persona has them
147
+ display_msg = msg
148
+ if persona["typos"] and random.random() < 0.3:
149
+ display_msg = inject_typos(msg, rate=0.08)
150
+
151
+ # Show user typing indicator
152
+ print(f" {color('...typing...', '90')}", end="\r")
153
+ await asyncio.sleep(min(delay, 3.0))
154
+ print(f" {color(f'[{persona_key.upper()}]', '1;33')} {display_msg}")
155
+
156
+ # Get Conny's response
157
+ try:
158
+ result = await send_to_conny(display_msg, chat_id)
159
+ bubbles = result.get("bubbles", [result.get("response", "error")])
160
+ # Simulate Conny "thinking"
161
+ await asyncio.sleep(random.uniform(1.0, 2.5))
162
+ for bubble in bubbles:
163
+ print(f" {color('[CONNY]', '1;35')} {bubble}")
164
+ if len(bubbles) > 1:
165
+ await asyncio.sleep(random.uniform(0.5, 1.5))
166
+ except Exception as e:
167
+ print(f" {color('[ERROR]', '31')} {e}")
168
+
169
+ print()
170
+
171
+ print("─" * 50)
172
+ print(color(" Demo completada.", "90"))
173
+ print(f" {len(persona['messages'])} turnos | Chat ID: {chat_id}")
174
+ print()
175
+
176
+
177
+ async def run_all_demos(instance_id: str = "default"):
178
+ """Run all demo personas sequentially."""
179
+ for persona_key in DEMO_PERSONAS:
180
+ await run_demo(persona_key, instance_id)
181
+ print("\n" + "═" * 50 + "\n")
182
+ await asyncio.sleep(2)
183
+
184
+
185
+ def main():
186
+ import argparse
187
+
188
+ parser = argparse.ArgumentParser(description="Conny Demo v2 — Realistic Colombian personas")
189
+ parser.add_argument("--persona", "-p", default="dona_carmen",
190
+ choices=list(DEMO_PERSONAS.keys()) + ["all"],
191
+ help="Which persona to simulate")
192
+ parser.add_argument("--instance", "-i", default="default", help="Instance ID")
193
+ parser.add_argument("--list", "-l", action="store_true", help="List available personas")
194
+ args = parser.parse_args()
195
+
196
+ if args.list:
197
+ print("Available demo personas:")
198
+ for key, p in DEMO_PERSONAS.items():
199
+ print(f" {key:20s} — {p['profile']}")
200
+ return
201
+
202
+ if args.persona == "all":
203
+ asyncio.run(run_all_demos(args.instance))
204
+ else:
205
+ asyncio.run(run_demo(args.persona, args.instance))
206
+
207
+
208
+ if __name__ == "__main__":
209
+ main()
@@ -0,0 +1,134 @@
1
+ """conny_demo_voice.py — Voice cascade with ElevenLabs for demo mode."""
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import logging
6
+ import os
7
+ import tempfile
8
+ from typing import List, Optional, Tuple
9
+
10
+ import httpx
11
+
12
+ log = logging.getLogger("conny.demo_voice")
13
+
14
+ # Cascade of API keys — if first fails, try second
15
+ API_KEYS = [
16
+ os.getenv("ELEVENLABS_API_KEY", "sk_e200c93fe4787b44dae55fdfe4e938d1f07ebbd2b0a67bb7"),
17
+ os.getenv("ELEVENLABS_API_KEY_2", "sk_199e6c73c2adbdeedd746de08fbb3ec7e9067259c7210c50"),
18
+ ]
19
+
20
+ # Female voices ranked by how natural/warm they sound in Spanish
21
+ # All free tier, tested for WhatsApp voice note quality
22
+ VOICE_CASCADE: List[Tuple[str, str]] = [
23
+ ("cgSgspJ2msm6clMCkdW9", "Jessica"), # Playful, Bright, Warm — young (best for Colombian)
24
+ ("EXAVITQu4vr4xnSDxMaL", "Sarah"), # Mature, Confident — young (backup)
25
+ ("FGY2WhTYpPnrIDTdsKH5", "Laura"), # Enthusiast, quirky — young (energetic)
26
+ ("hpp4J3VqNfWAUOO0d1Us", "Bella"), # Professional, Warm — middle_aged (formal fallback)
27
+ ]
28
+
29
+ # Default: Jessica (most natural for Colombian Spanish)
30
+ DEFAULT_VOICE = VOICE_CASCADE[0][0]
31
+
32
+
33
+ async def generate_demo_audio(text: str, voice_idx: int = 0) -> Optional[str]:
34
+ """Generate audio with cascade: try key1 → key2, voice1 → voice2 → voice3."""
35
+ if not any(API_KEYS):
36
+ return None
37
+
38
+ # Try each API key
39
+ for key in API_KEYS:
40
+ if not key:
41
+ continue
42
+ # Try voices in order
43
+ for vid, vname in VOICE_CASCADE[voice_idx:voice_idx + 2]:
44
+ result = await _try_generate(text, key, vid)
45
+ if result:
46
+ log.info(f"[demo_voice] generated with {vname} ({vid[:8]}...)")
47
+ return result
48
+ log.debug(f"[demo_voice] {vname} failed with key ...{key[-6:]}, trying next")
49
+
50
+ log.warning("[demo_voice] all keys/voices exhausted")
51
+ return None
52
+
53
+
54
+ async def _try_generate(text: str, api_key: str, voice_id: str) -> Optional[str]:
55
+ """Single attempt to generate audio."""
56
+ try:
57
+ async with httpx.AsyncClient(timeout=20.0) as client:
58
+ r = await client.post(
59
+ f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}",
60
+ headers={
61
+ "xi-api-key": api_key,
62
+ "Content-Type": "application/json",
63
+ },
64
+ json={
65
+ "text": text,
66
+ "model_id": "eleven_flash_v2_5",
67
+ "voice_settings": {
68
+ "stability": 0.35, # Lower = more natural variation
69
+ "similarity_boost": 0.70,
70
+ "style": 0.25, # Subtle expressiveness
71
+ "use_speaker_boost": True,
72
+ },
73
+ },
74
+ )
75
+ if r.status_code == 200 and len(r.content) > 1000:
76
+ tmp = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False)
77
+ tmp.write(r.content)
78
+ tmp.close()
79
+ return tmp.name
80
+ elif r.status_code == 401:
81
+ log.debug(f"[demo_voice] key unauthorized")
82
+ return None
83
+ elif r.status_code == 429:
84
+ log.debug(f"[demo_voice] rate limited")
85
+ return None
86
+ else:
87
+ log.debug(f"[demo_voice] HTTP {r.status_code}: {r.text[:100]}")
88
+ return None
89
+ except Exception as e:
90
+ log.debug(f"[demo_voice] error: {e}")
91
+ return None
92
+
93
+
94
+ def should_send_voice_in_demo(text: str, turn_number: int, has_business_name: bool) -> bool:
95
+ """Decide when to send voice in demo — strategic moments only."""
96
+ # First greeting: ALWAYS voice (immediate wow)
97
+ if turn_number <= 1:
98
+ return True
99
+
100
+ # After getting business name and entering character
101
+ if has_business_name and turn_number <= 4:
102
+ return True
103
+
104
+ # Patient simulation responses (showing the product)
105
+ if turn_number >= 3 and len(text) < 150:
106
+ return True
107
+
108
+ # Never spam voice — max every 3 turns
109
+ if turn_number > 5 and turn_number % 3 != 0:
110
+ return False
111
+
112
+ return False
113
+
114
+
115
+ # Conversion hooks for demo closing
116
+ DEMO_CLOSING_TRIGGERS = [
117
+ "como activo", "cómo activo", "como lo contrato", "cómo contrato",
118
+ "cuanto cuesta", "cuánto cuesta", "precio", "planes",
119
+ "me interesa", "lo quiero", "activar", "contratar",
120
+ ]
121
+
122
+
123
+ def detect_buying_intent(text: str) -> bool:
124
+ """Detect if prospect wants to buy."""
125
+ return any(t in text.lower() for t in DEMO_CLOSING_TRIGGERS)
126
+
127
+
128
+ def get_closing_response() -> str:
129
+ """When prospect wants to buy, close with Santiago's contact."""
130
+ return (
131
+ "me alegra que te haya gustado la demo! |||"
132
+ " para activarlo en tu negocio, Santiago te explica todo: 3124348669 |||"
133
+ " la activación es rápida, en menos de 5 minutos ya estoy respondiendo tu WhatsApp"
134
+ )
@@ -0,0 +1,43 @@
1
+ """conny_design.py — Single source of truth for all visual elements."""
2
+ from __future__ import annotations
3
+
4
+ COLORS = {
5
+ "primary": "#b48ead", # Lavender/Purple
6
+ "secondary": "#81a1c1", # Soft Blue
7
+ "success": "#a3be8c", # Sage Green
8
+ "warning": "#ebcb8b", # Muted Yellow
9
+ "error": "#bf616a", # Soft Red
10
+ "dim": "#4c566a", # Slate Grey
11
+ "text": "#d8dee9", # Snow Storm White
12
+ "accent": "#88c0d0", # Frost Cyan
13
+ }
14
+
15
+ LOGO_FULL = """\
16
+ [#b48ead] ██████╗ ██████╗ ███╗ ██╗███╗ ██╗██╗ ██╗[/#b48ead]
17
+ [#a690b8]██╔════╝ ██╔═══██╗████╗ ██║████╗ ██║╚██╗ ██╔╝[/#a690b8]
18
+ [#9992c3]██║ ██║ ██║██╔██╗ ██║██╔██╗ ██║ ╚████╔╝ [/#9992c3]
19
+ [#8b93ce]██║ ██║ ██║██║╚██╗██║██║╚██╗██║ ╚██╔╝ [/#8b93ce]
20
+ [#7d95d8]╚██████╗ ╚██████╔╝██║ ╚████║██║ ╚████║ ██║ [/#7d95d8]
21
+ [#6f97e3] ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝ ╚═╝ [/#6f97e3]"""
22
+
23
+ WORM_RESTING = """\
24
+ [#b48ead]◆[/#b48ead][#4c566a]█▓▒░[/#4c566a]╮
25
+ ╰─╯"""
26
+
27
+ WORM_INLINE = "[#b48ead]◆[/#b48ead][dim]▓▒░[/dim]"
28
+
29
+ SEP = "[dim]─────────────────────────────────────────────────────[/dim]"
30
+
31
+ # Status icons - using standard circles and marks for universal compatibility
32
+ ICON_ONLINE = "[#a3be8c]●[/#a3be8c]"
33
+ ICON_OFFLINE = "[#bf616a]○[/#bf616a]"
34
+ ICON_WARN = "[#ebcb8b]![/#ebcb8b]"
35
+ ICON_OK = "[#a3be8c]✓[/#a3be8c]"
36
+ ICON_ERR = "[#bf616a]✕[/#bf616a]"
37
+ ICON_BRAND = "[#b48ead]✦[/#b48ead]"
38
+
39
+ # Category icons - professional and clean
40
+ ICON_CORE = "■"
41
+ ICON_BOT = "⬢"
42
+ ICON_INT = "▲"
43
+ ICON_OPS = "◆"