nothumanallowed 15.1.35 → 15.1.36

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": "15.1.35",
3
+ "version": "15.1.36",
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 = '15.1.35';
8
+ export const VERSION = '15.1.36';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -530,6 +530,38 @@ class TelegramResponder {
530
530
  saveTelegramContext(this._lastContextByChatId);
531
531
  }
532
532
 
533
+ /**
534
+ * Route a fresh message to the best agent.
535
+ *
536
+ * Tier 1 — keyword table (fast, no LLM cost). Falls through to CONDUCTOR
537
+ * when nothing matches.
538
+ *
539
+ * Tier 2 — if Tier 1 returned the fallback CONDUCTOR AND the user has an
540
+ * LLM key configured, ask the LLM to pick the right agent given the
541
+ * conversation history. This rescues paraphrased queries like
542
+ * "mostrami quanto vale ora il metallo prezioso giallo" (no keyword match)
543
+ * which v13 used to route correctly because of a similar LLM router.
544
+ */
545
+ async _routeFreshMessage(text, lastCtx) {
546
+ const keywordAgent = routeMessage(text, this.autoRoute);
547
+ if (keywordAgent !== 'conductor') return keywordAgent;
548
+ // Tier 2 fallback
549
+ const hasKey = !!(this.config.llm?.apiKey || this.config.llm?.openaiKey || this.config.llm?.geminiKey || this.config.llm?.deepseekKey || (this.config.llm?.provider === 'nha'));
550
+ if (!hasKey) return keywordAgent;
551
+ try {
552
+ const AGENTS = ['herald', 'mercury', 'athena', 'oracle', 'forge', 'scheherazade', 'saber', 'sauron', 'conductor'];
553
+ const tail = (lastCtx?.conversationLog || []).slice(-6)
554
+ .map(t => `${t.role === 'user' ? 'User' : 'Bot'}: ${String(t.content).slice(0, 200)}`)
555
+ .join('\n');
556
+ const sys = 'You are a routing classifier. Reply with EXACTLY one lowercase agent name from this list and nothing else: ' + AGENTS.join(', ') + '. herald=calendar/email/weather/news, mercury=finance/markets/crypto, athena=audit/compliance, oracle=data/analytics, forge=code/architecture, scheherazade=writing/content, saber=security, sauron=monitoring, conductor=anything else.';
557
+ const usr = `Recent conversation (most recent last):\n${tail || '(none)'}\n\nUser message: "${text}"\n\nWhich agent? Reply with ONLY the lowercase agent name.`;
558
+ const ans = await callLLM(this.config, sys, usr, { max_tokens: 16 });
559
+ const picked = String(ans || '').trim().toLowerCase().split(/\s+/)[0];
560
+ if (AGENTS.includes(picked)) return picked;
561
+ } catch { /* LLM unavailable, keep keyword fallback */ }
562
+ return keywordAgent;
563
+ }
564
+
533
565
  get enabled() {
534
566
  return !!this.token;
535
567
  }
@@ -802,10 +834,25 @@ class TelegramResponder {
802
834
  let enrichedMessage = cleanText;
803
835
  let preHistory = null;
804
836
 
837
+ // ── Always inject multi-turn rolling history (15.1.36) ─────────────
838
+ // Even when the new message isn't a "continuation" of the very last
839
+ // turn, the user may still be referring to something earlier in the
840
+ // conversation ("come ti avevo detto ieri", "ricorda che..."). Pass
841
+ // the full conversation log to the agent as preHistory.
842
+ const rollingLog = (lastCtx && Array.isArray(lastCtx.conversationLog) && lastCtx.conversationLog.length > 0)
843
+ ? lastCtx.conversationLog
844
+ .filter(t => t && (t.role === 'user' || t.role === 'assistant') && typeof t.content === 'string')
845
+ .map(t => ({ role: t.role, content: t.content }))
846
+ : null;
847
+
805
848
  if (isContinuation && !lastWasCompleted) {
806
849
  // Continue with same agent and inject full history for context
807
850
  agent = lastCtx.agent;
808
- if (lastCtx.history && lastCtx.history.length > 0) {
851
+ // Prefer the rolling multi-turn log if available; fall back to the
852
+ // legacy single-turn `history` for older saved contexts.
853
+ if (rollingLog && rollingLog.length > 0) {
854
+ preHistory = rollingLog;
855
+ } else if (lastCtx.history && lastCtx.history.length > 0) {
809
856
  preHistory = lastCtx.history;
810
857
  }
811
858
 
@@ -870,9 +917,13 @@ class TelegramResponder {
870
917
 
871
918
  this.log(`[Telegram] ${fromUser}: continuation → ${agent.toUpperCase()} (ctx ${Math.round(stickyAge/1000)}s ago, history=${preHistory ? preHistory.length : 0})`);
872
919
  } else {
873
- // Fresh request — route normally
874
- agent = routeMessage(cleanText, this.autoRoute);
875
- this.log(`[Telegram] ${fromUser}: new request ${agent.toUpperCase()}${isVoice ? ' [voice]' : ''}${lastWasCompleted ? ' [prev completed]' : ''}`);
920
+ // Fresh request — route normally, but STILL pass the rolling log so
921
+ // the model has the conversation context even when the new message
922
+ // is on a fresh topic (e.g. user starts a new task but the previous
923
+ // session is still relevant for memory: names, preferences, etc.).
924
+ agent = await this._routeFreshMessage(cleanText, lastCtx);
925
+ if (rollingLog && rollingLog.length > 0) preHistory = rollingLog;
926
+ this.log(`[Telegram] ${fromUser}: new request → ${agent.toUpperCase()}${isVoice ? ' [voice]' : ''}${lastWasCompleted ? ' [prev completed]' : ''} (rolling history=${preHistory ? preHistory.length : 0})`);
876
927
  }
877
928
 
878
929
  // Broadcast event
@@ -906,12 +957,25 @@ class TelegramResponder {
906
957
  ? responseText.slice(0, 3950) + '\n\n... [truncated]'
907
958
  : responseText;
908
959
 
909
- // Save context if action was completed, mark it so next turn starts fresh
960
+ // ── Multi-turn rolling memory (15.1.36) ────────────────────────────
961
+ // v13 worked well partially because every chat retained a real
962
+ // conversation history. The v14 refactor reduced this to 1 turn —
963
+ // hence "Telegram doesn't understand me anymore". We now keep the
964
+ // last MAX_CONVERSATION_TURNS turns per chat, persisted to disk.
965
+ const MAX_CONVERSATION_TURNS = 20; // user+assistant pairs
966
+ const prevLog = (lastCtx && Array.isArray(lastCtx.conversationLog)) ? lastCtx.conversationLog : [];
967
+ const conversationLog = [
968
+ ...prevLog,
969
+ { role: 'user', content: cleanText, ts: Date.now() },
970
+ { role: 'assistant', content: responseText, ts: Date.now() },
971
+ ].slice(-MAX_CONVERSATION_TURNS * 2);
972
+
910
973
  this._lastContextByChatId[chatId] = {
911
974
  agent,
912
975
  userMsg: cleanText,
913
976
  agentReply: responseText,
914
- history: isCompletedAction(responseText) ? null : responseHistory, // clear history after success
977
+ history: isCompletedAction(responseText) ? null : responseHistory, // single-turn (legacy)
978
+ conversationLog, // multi-turn (new)
915
979
  ts: Date.now(),
916
980
  };
917
981
  this._lastAgentByChatId[chatId] = agent;