nothumanallowed 14.1.45 → 14.1.47

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": "14.1.45",
3
+ "version": "14.1.47",
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 = '14.1.45';
8
+ export const VERSION = '14.1.47';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -37,11 +37,12 @@ export function register(router) {
37
37
  const limit = parseInt(url.searchParams.get('pageSize') || url.searchParams.get('limit') || '200');
38
38
  const offset = parseInt(url.searchParams.get('page') || url.searchParams.get('offset') || '0') * (url.searchParams.get('page') ? limit : 1);
39
39
  try {
40
- // getUnreadImportant returns fully-parsed messages (subject, from, snippet, etc.)
41
- // For non-inbox folders fall back to raw id+threadId list
40
+ // getAllEmails returns ALL messages (read + unread) for local-first architecture
41
+ // This ensures email like Luca's are downloaded even if read in Gmail
42
42
  let emails;
43
43
  if (folder === 'inbox' || folder === 'INBOX') {
44
- emails = await getUnreadImportant(config, limit + offset);
44
+ const { getAllEmails } = await import('../../services/google-gmail.mjs');
45
+ emails = await getAllEmails(config, 'INBOX', limit + offset);
45
46
  } else {
46
47
  const gmailQuery = folder === 'sent' ? 'in:sent' : folder === 'spam' ? 'in:spam' : folder === 'trash' ? 'in:trash' : `in:${folder}`;
47
48
  const refs = await listMessages(config, gmailQuery, limit + offset);
@@ -19,7 +19,7 @@ import { createServer } from 'net';
19
19
  import { promisify } from 'util';
20
20
  import { sendJSON, sendError, parseBody, sendSSE } from '../index.mjs';
21
21
  import { loadConfig } from '../../config.mjs';
22
- import { callLLM, callLLMStream, fixQwen3BPE } from '../../services/llm.mjs';
22
+ import { callLLM, callLLMStream } from '../../services/llm.mjs';
23
23
  import { NHA_DIR } from '../../constants.mjs';
24
24
 
25
25
  const execAsync = promisify(exec);
@@ -487,10 +487,8 @@ RULES:
487
487
 
488
488
  await callLLMStream(config, systemPrompt, userContent, (token) => {
489
489
  fullResponse += token;
490
- // Apply BPE fix for better Italian text quality
491
- const fixedToken = fixQwen3BPE(token);
492
490
  // Suppress raw <tool> blocks from text stream — only emit visible text
493
- const visibleToken = fixedToken.replace(/<tool>[\s\S]*?<\/tool>/g, '');
491
+ const visibleToken = token.replace(/<tool>[\s\S]*?<\/tool>/g, '');
494
492
  if (visibleToken) emit({ type: 'text', token: visibleToken });
495
493
  }, { max_tokens: 8192 });
496
494
 
@@ -95,6 +95,38 @@ export async function getUnreadImportant(config, maxResults = 30) {
95
95
  return messages;
96
96
  }
97
97
 
98
+ /**
99
+ * Get ALL emails from a folder (read + unread) for local-first architecture.
100
+ * This replaces getUnreadImportant to ensure ALL emails are downloaded locally.
101
+ */
102
+ export async function getAllEmails(config, folder = 'INBOX', maxResults = 200) {
103
+ // Map folder names to Gmail queries
104
+ const folderQueries = {
105
+ 'INBOX': 'in:inbox',
106
+ 'SENT': 'in:sent',
107
+ 'DRAFTS': 'in:drafts',
108
+ 'SPAM': 'in:spam',
109
+ 'TRASH': 'in:trash',
110
+ 'STARRED': 'is:starred',
111
+ 'IMPORTANT': 'is:important'
112
+ };
113
+
114
+ const query = folderQueries[folder.toUpperCase()] || `in:${folder.toLowerCase()}`;
115
+ const messageRefs = await listMessages(config, query, maxResults);
116
+ const messages = [];
117
+
118
+ for (const ref of messageRefs.slice(0, maxResults)) {
119
+ try {
120
+ const msg = await getMessage(config, ref.id);
121
+ messages.push(msg);
122
+ } catch { /* skip failed messages */ }
123
+ }
124
+
125
+ // Cache messages locally - this ensures local-first access
126
+ await cacheMessages(messages);
127
+ return messages;
128
+ }
129
+
98
130
  /**
99
131
  * Get emails from today (read + unread).
100
132
  */
@@ -362,24 +394,35 @@ async function cacheMessages(messages) {
362
394
 
363
395
  // ENTERPRISE LOCAL-FIRST: Also save to IMAP database for Web UI offline access
364
396
  try {
397
+ if (!messages.length) return; // Nothing to cache
398
+
365
399
  const { ensureGoogleAccount, upsertFolder, getSystemLabel, saveMessage } = await import('./email-db.mjs');
366
400
 
401
+ // Extract email from first message for account setup
402
+ const firstMsg = messages[0];
403
+ const accountEmail = extractEmail(firstMsg.to) || 'gmail@account';
404
+
367
405
  // Ensure Google account exists in IMAP database
368
406
  const googleAccount = ensureGoogleAccount({
369
407
  id: 'google',
370
408
  display_name: 'Gmail',
371
- email_address: messages[0]?.to || 'gmail@account',
409
+ email_address: accountEmail,
372
410
  imap_host: 'gmail.googleapis.com',
373
411
  imap_port: 993,
374
412
  imap_secure: true,
375
413
  is_active: 1
376
414
  });
377
415
 
416
+ if (!googleAccount) {
417
+ throw new Error('Failed to create/get Google account');
418
+ }
419
+
378
420
  // Ensure INBOX folder exists for Gmail account
379
421
  const inboxFolderId = upsertFolder(googleAccount.id, 'INBOX', 'Inbox', 'inbox', 1, 0);
380
422
 
381
- // Get system labels (inbox, sent, etc.) - they are auto-created by ensureGoogleAccount
382
- const inboxLabel = getSystemLabel(googleAccount.id, 'inbox');
423
+ if (!inboxFolderId) {
424
+ throw new Error('Failed to create/get INBOX folder');
425
+ }
383
426
 
384
427
  // Save each message to IMAP database
385
428
  for (const msg of messages) {
@@ -371,7 +371,7 @@ function repairFragmentRuns(line) {
371
371
  return result.join(' ');
372
372
  }
373
373
 
374
- function fixQwen3BPE(text) {
374
+ export function fixQwen3BPE(text) {
375
375
  let inCode = false;
376
376
  return text.split('\n').map((line) => {
377
377
  if (line.trimStart().startsWith('```')) { inCode = !inCode; return line; }