nothumanallowed 13.5.185 → 13.5.187

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "13.5.185",
3
+ "version": "13.5.187",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '13.5.185';
8
+ export const VERSION = '13.5.187';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -134,6 +134,44 @@ function routeMessage(text, useAutoRoute = true) {
134
134
  return bestAgent;
135
135
  }
136
136
 
137
+ // ── Language detection from message text ─────────────────────────────────────
138
+
139
+ const IT_WORDS = new Set(['il','lo','la','le','gli','un','una','che','di','da','in','con','su','per','tra','fra','non','ma','se','come','dove','quando','chi','cosa','ho','hai','ha','sono','sei','siamo','avere','essere','fare','dire','andare','mi','ti','ci','si','vi','li','le','gli','mio','tuo','suo','nostro','vostro','loro','questo','quello','questi','quelli','anche','già','ancora','sempre','mai','oggi','domani','ieri','adesso','ora','poi','dopo','prima','qui','qua','lì','là','più','meno','molto','poco','bene','male','sì','no','grazie','prego','ciao','buongiorno','buonasera','appuntamenti','calendario','riunione','meteo','temperatura','email','posta','notizie']);
140
+ const ES_WORDS = new Set(['el','la','los','las','un','una','que','de','en','con','por','para','pero','como','donde','cuando','quien','qué','tengo','tienes','tiene','somos','soy','eres','hacer','decir','ir','me','te','se','nos','este','ese','estos','esos','también','ya','todavía','siempre','nunca','hoy','mañana','ayer','aquí','allí','más','menos','muy','bien','mal','sí','no','gracias','hola','buenos']);
141
+ const FR_WORDS = new Set(['le','la','les','un','une','des','que','de','en','avec','pour','par','mais','comme','où','quand','qui','je','tu','il','elle','nous','vous','ils','elles','avoir','être','faire','dire','aller','me','te','se','ce','cet','cette','ces','aussi','déjà','toujours','jamais','aujourd','demain','hier','ici','là','plus','moins','très','bien','mal','oui','non','merci','bonjour','bonsoir']);
142
+ const DE_WORDS = new Set(['der','die','das','ein','eine','und','oder','aber','nicht','mit','für','von','zu','an','auf','ist','sind','hat','haben','sein','werden','ich','du','er','sie','es','wir','ihr','mich','dich','sich','uns','euch','diesem','diesen','dieser','dieses','auch','schon','noch','immer','nie','heute','morgen','gestern','hier','dort','mehr','weniger','sehr','gut','schlecht','ja','nein','danke','hallo']);
143
+ const PT_WORDS = new Set(['o','a','os','as','um','uma','que','de','em','com','por','para','mas','como','onde','quando','quem','eu','tu','ele','ela','nós','vós','eles','elas','ter','ser','fazer','dizer','ir','me','te','se','nos','este','esse','isso','aquele','também','já','ainda','sempre','nunca','hoje','amanhã','ontem','aqui','lá','mais','menos','muito','bem','mal','sim','não','obrigado','olá']);
144
+
145
+ function detectLanguage(text) {
146
+ if (!text || text.length < 6) return null;
147
+ const words = text.toLowerCase().replace(/[^a-zàáâãäèéêëìíîïòóôõöùúûüýñçàèìòù\s]/g, ' ').split(/\s+/).filter(w => w.length > 1);
148
+ if (words.length < 2) return null;
149
+
150
+ let it = 0, es = 0, fr = 0, de = 0, pt = 0, en = 0;
151
+ for (const w of words) {
152
+ if (IT_WORDS.has(w)) it++;
153
+ if (ES_WORDS.has(w)) es++;
154
+ if (FR_WORDS.has(w)) fr++;
155
+ if (DE_WORDS.has(w)) de++;
156
+ if (PT_WORDS.has(w)) pt++;
157
+ // Basic English common words
158
+ if (['the','a','an','is','are','was','were','have','has','do','does','i','you','he','she','we','they','and','or','but','not','with','for','from','to','in','on','at','this','that','these','those','can','will','would','could','should','what','where','when','who','how'].includes(w)) en++;
159
+ }
160
+
161
+ const max = Math.max(it, es, fr, de, pt, en);
162
+ if (max === 0) return null;
163
+ const threshold = Math.max(2, words.length * 0.15); // at least 15% of words or 2
164
+ if (max < threshold) return null;
165
+
166
+ if (it === max) return 'Italian';
167
+ if (es === max) return 'Spanish';
168
+ if (fr === max) return 'French';
169
+ if (de === max) return 'German';
170
+ if (pt === max) return 'Portuguese';
171
+ if (en === max) return 'English';
172
+ return null;
173
+ }
174
+
137
175
  // ── Tool-aware agent call (LLM + tool execution loop) ────────────────────────
138
176
 
139
177
  /**
@@ -141,12 +179,13 @@ function routeMessage(text, useAutoRoute = true) {
141
179
  * Like chat.mjs but headless — no confirmation prompts, all tools auto-executed.
142
180
  * Returns a human-readable summary of what was done.
143
181
  */
144
- async function callAgentWithTools(config, agentName, userMessage) {
182
+ async function callAgentWithTools(config, agentName, userMessage, languageOverride) {
145
183
  const today = new Date().toISOString().split('T')[0];
146
184
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
147
185
  const locale = Intl.DateTimeFormat().resolvedOptions().locale || 'en';
148
186
  const LANG_MAP = { en: 'English', it: 'Italian', es: 'Spanish', fr: 'French', de: 'German', pt: 'Portuguese', nl: 'Dutch', pl: 'Polish', ru: 'Russian', ja: 'Japanese', ko: 'Korean', zh: 'Chinese', ar: 'Arabic', hi: 'Hindi', tr: 'Turkish' };
149
- const language = config?.profile?.language || config?.language || LANG_MAP[locale.split('-')[0]] || 'English';
187
+ // Priority: explicit override (from message text detection) config setting → system locale
188
+ const language = languageOverride || config?.profile?.language || config?.language || LANG_MAP[locale.split('-')[0]] || 'English';
150
189
 
151
190
  const systemPrompt = TOOL_DEFINITIONS
152
191
  .replace('{{TODAY}}', today)
@@ -547,11 +586,20 @@ class TelegramResponder {
547
586
  // Send typing indicator
548
587
  await this._telegramCall('sendChatAction', { chat_id: chatId, action: 'typing' });
549
588
 
589
+ // Detect language from the message text — overrides system locale
590
+ const detectedLang = detectLanguage(cleanText);
591
+
550
592
  // Tool-capable agents use the full tool execution loop
551
593
  // Pure reasoning/analysis agents use the simple callAgent (no tools)
552
594
  const TOOL_AGENTS = new Set(['herald', 'hermes', 'edi', 'jarvis', 'flux', 'echo', 'mercury', 'pipe', 'navi', 'link', 'prometheus', 'tempest']);
553
- const callFn = TOOL_AGENTS.has(agent) ? callAgentWithTools : callAgent;
554
- const response = await callFn(this.config, agent, cleanText);
595
+ let response;
596
+ if (TOOL_AGENTS.has(agent)) {
597
+ response = await callAgentWithTools(this.config, agent, cleanText, detectedLang);
598
+ } else {
599
+ // For non-tool agents: inject language instruction into the message
600
+ const langInstruction = detectedLang ? `[Respond in ${detectedLang}] ` : '';
601
+ response = await callAgent(this.config, agent, langInstruction + cleanText);
602
+ }
555
603
 
556
604
  // Truncate if too long for Telegram (4096 char limit)
557
605
  const truncated = response.length > 4000