nothumanallowed 15.1.52 → 15.1.53

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.52",
3
+ "version": "15.1.53",
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.52';
8
+ export const VERSION = '15.1.53';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -210,8 +210,13 @@ export function register(router) {
210
210
  const settingLang = LANG_MAP[(config?.language || config?.lang || 'en').slice(0,2)] || 'English';
211
211
  const detectedFromMsg = detectLanguage(effectiveMsg);
212
212
  const userLang = detectedFromMsg || settingLang;
213
- if (!enrichedPrompt.toLowerCase().includes('respond in') && !enrichedPrompt.toLowerCase().includes('rispondi in')) {
214
- enrichedPrompt += `\n\nIMPORTANT: The user just wrote their message in ${userLang}. Respond in ${userLang} — match the user's language exactly, do not switch to a different language mid-response.`;
213
+ // Prepend the language directive at the TOP of the system prompt — many
214
+ // models give the highest weight to early instructions. Also append a
215
+ // reinforcement at the end. Belt and suspenders for language stickiness.
216
+ const langHeader = `[LANGUAGE — HIGHEST PRIORITY]\nRespond ENTIRELY in ${userLang}. From the very first word to the last word, every sentence must be in ${userLang}. Never start in English then switch — that includes intro fillers like "Let me…", "I'll…", "Sure,…". If you find yourself about to write in English, stop and write the same sentence in ${userLang} instead.\n\n`;
217
+ enrichedPrompt = langHeader + enrichedPrompt;
218
+ if (!enrichedPrompt.toLowerCase().endsWith('respond in ' + userLang.toLowerCase() + '.')) {
219
+ enrichedPrompt += `\n\nIMPORTANT: Output language is ${userLang}. Do NOT switch languages mid-response.`;
215
220
  }
216
221
 
217
222
  // Rolling context window
@@ -234,7 +239,11 @@ export function register(router) {
234
239
  for (const t of rawHistory.slice(-RECENT)) {
235
240
  parts.push(`${t.role === 'user' ? '[User]' : '[Assistant]'} ${t.content.slice(0, 2000)}`);
236
241
  }
237
- parts.push(`[User] ${effectiveMsg}`);
242
+ // Prefix the last user turn with an explicit per-message language tag.
243
+ // System prompts can lose effectiveness over long conversations; the
244
+ // per-turn tag is the closest hint to the model's first generated token
245
+ // and is the most reliable trigger for the right language.
246
+ parts.push(`[User · respond in ${userLang}] ${effectiveMsg}`);
238
247
  const userMessage = parts.join('\n\n');
239
248
 
240
249
  // Attachments — handle non-streaming
@@ -529,7 +538,7 @@ export function register(router) {
529
538
  };
530
539
  const toolContext = toolResults.map(t => `[${t.action} result]:\n${cleanResult(t.action, t.result)}`).join('\n\n---\n\n');
531
540
  const synthesisPrompt = `${enrichedPrompt}\n\n## DATA FROM TOOLS (already executed — do NOT plan to call them again):\n${toolContext}\n\n## STRICT OUTPUT RULES:\n- LANGUAGE: respond ENTIRELY in ${userLang}. Do not switch languages mid-answer, do not mix English and ${userLang}.\n- The tools above HAVE ALREADY RUN. Their output is the ground truth.\n- NEVER say "Let me read…", "I'll search…", "I will fetch…", "leggerò", "cercherò", "ti dirò" — those tools are already done.\n- Answer the user's question DIRECTLY using the data above. If a specific detail (delivery date, item, total, etc.) is in the data, quote it verbatim.\n- If the data does not contain the answer, state plainly what is missing — do not announce intent to look further.\n- Write ONLY plain prose or markdown (headers, bullets, bold). NEVER use \`\`\`json or any fenced code block.\n- Format numbers/prices as plain text. Be concise and human-readable.`;
532
- const synthesisMsg = `${effectiveMsg}\n\nThe tools have already been executed and their results are in the system prompt. Answer the question DIRECTLY using that data. Do NOT announce further actions ("Let me…", "I'll…", "leggerò", "cercherò"). Plain prose/markdown — zero JSON, zero code blocks.`;
541
+ const synthesisMsg = `[LANGUAGE: respond entirely in ${userLang}, every sentence] ${effectiveMsg}\n\nThe tools have already been executed and their results are in the system prompt. Answer the question DIRECTLY using that data. Do NOT announce further actions ("Let me…", "I'll…", "leggerò", "cercherò"). Plain prose/markdown — zero JSON, zero code blocks. EVERY sentence must be in ${userLang}.`;
533
542
  sse('tool_synthesis', {});
534
543
  // Keep the pre-synthesis prose around. If the synthesis call returns
535
544
  // empty (provider error, content filter, model bailed), we fall back