nothumanallowed 12.6.4 → 12.6.5
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 +1 -1
- package/src/commands/ui.mjs +78 -17
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "12.6.
|
|
3
|
+
"version": "12.6.5",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools. 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/commands/ui.mjs
CHANGED
|
@@ -337,13 +337,19 @@ export async function cmdUI(args) {
|
|
|
337
337
|
if (method === 'POST' && pathname === '/api/google/auth') {
|
|
338
338
|
try {
|
|
339
339
|
const { runAuthFlow } = await import('../services/google-oauth.mjs');
|
|
340
|
-
// Run auth flow
|
|
341
|
-
runAuthFlow(config)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
340
|
+
// Run auth flow — opens browser, waits for callback
|
|
341
|
+
const success = await runAuthFlow(config);
|
|
342
|
+
if (success) {
|
|
343
|
+
config._googleConnected = true;
|
|
344
|
+
// Reload config to pick up new tokens
|
|
345
|
+
const freshConfig = await loadConfig();
|
|
346
|
+
Object.assign(config, freshConfig);
|
|
347
|
+
sendJSON(res, 200, { ok: true, message: 'Google connected successfully! You can now use email, calendar, contacts, and Drive.' });
|
|
348
|
+
} else {
|
|
349
|
+
sendJSON(res, 200, { ok: false, message: 'Google OAuth failed. Make sure you authorized the app in the browser window.' });
|
|
350
|
+
}
|
|
345
351
|
} catch (e) {
|
|
346
|
-
sendJSON(res, 500, { error: e.message });
|
|
352
|
+
sendJSON(res, 500, { error: `Google OAuth error: ${e.message}. Run "nha google" from terminal for manual setup.` });
|
|
347
353
|
}
|
|
348
354
|
logRequest(method, pathname, 200, Date.now() - start);
|
|
349
355
|
return;
|
|
@@ -1257,12 +1263,14 @@ export async function cmdUI(args) {
|
|
|
1257
1263
|
if (sLines.length > 0) parts.push(`[CONTEXT — ${sLines.length} earlier exchanges]\n${sLines.join('\n')}\n[END CONTEXT]`);
|
|
1258
1264
|
}
|
|
1259
1265
|
for (const turn of requestHistory.slice(-RECENT)) {
|
|
1260
|
-
|
|
1266
|
+
// Use llmContent (file context) when available, otherwise display content
|
|
1267
|
+
const turnContent = turn.llmContent || turn.content;
|
|
1268
|
+
parts.push(`${turn.role === 'user' ? '[User]' : '[Assistant]'} ${turnContent.slice(0, 4000)}`);
|
|
1261
1269
|
}
|
|
1262
1270
|
parts.push(`[User] ${body.message}`);
|
|
1263
1271
|
let userMessage = parts.join('\n\n');
|
|
1264
1272
|
|
|
1265
|
-
// Inject episodic memory
|
|
1273
|
+
// Inject episodic memory + cross-conversation memory into the system prompt
|
|
1266
1274
|
const basePrompt = effectiveSystemPrompt || chatSystemPrompt;
|
|
1267
1275
|
let enrichedSystemPrompt = basePrompt;
|
|
1268
1276
|
try {
|
|
@@ -1270,6 +1278,37 @@ export async function cmdUI(args) {
|
|
|
1270
1278
|
if (memCtx) enrichedSystemPrompt = basePrompt + memCtx;
|
|
1271
1279
|
} catch { /* memory unavailable */ }
|
|
1272
1280
|
|
|
1281
|
+
// Cross-conversation memory — summaries of recent conversations
|
|
1282
|
+
try {
|
|
1283
|
+
const allConvs = listConversations();
|
|
1284
|
+
if (allConvs.length > 1) {
|
|
1285
|
+
const summaries = [];
|
|
1286
|
+
let totalChars = 0;
|
|
1287
|
+
for (const c of allConvs) {
|
|
1288
|
+
if (c.id === (body.conversationId || activeConvId)) continue;
|
|
1289
|
+
if (summaries.length >= 8 || totalChars > 2000) break;
|
|
1290
|
+
if (!c.messages || c.messages.length === 0) continue;
|
|
1291
|
+
const firstUser = c.messages.find(m => m.role === 'user');
|
|
1292
|
+
const lastAssistant = [...c.messages].reverse().find(m => m.role === 'assistant');
|
|
1293
|
+
if (!firstUser) continue;
|
|
1294
|
+
const date = c.updatedAt?.split('T')[0] || '?';
|
|
1295
|
+
const title = c.title !== 'New Chat' ? c.title : firstUser.content.slice(0, 60);
|
|
1296
|
+
let s = `• [${date}] "${title}" (${c.messages.length} msgs)`;
|
|
1297
|
+
s += `\n User: ${firstUser.content.replace(/\s+/g, ' ').slice(0, 120)}`;
|
|
1298
|
+
if (lastAssistant) {
|
|
1299
|
+
const preview = lastAssistant.content.replace(/<think>[\s\S]*?<\/think>/g, '').replace(/\s+/g, ' ').slice(0, 150);
|
|
1300
|
+
s += `\n Result: ${preview}`;
|
|
1301
|
+
}
|
|
1302
|
+
if (totalChars + s.length > 2000) break;
|
|
1303
|
+
summaries.push(s);
|
|
1304
|
+
totalChars += s.length;
|
|
1305
|
+
}
|
|
1306
|
+
if (summaries.length > 0) {
|
|
1307
|
+
enrichedSystemPrompt += `\n\n--- CONVERSATION MEMORY ---\nYou remember these past conversations:\n\n${summaries.join('\n\n')}\n\nUse this to maintain continuity. Never say "I don't have access to previous conversations".\n--- END MEMORY ---`;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
} catch { /* non-critical */ }
|
|
1311
|
+
|
|
1273
1312
|
// Handle image attachment — vision API
|
|
1274
1313
|
if (body.imageBase64 && body.imageMimeType) {
|
|
1275
1314
|
try {
|
|
@@ -1362,13 +1401,33 @@ export async function cmdUI(args) {
|
|
|
1362
1401
|
const pdfPrompt = body.message || `Read and analyze this PDF document "${body.pdfName}". Extract all text content, summarize key information.`;
|
|
1363
1402
|
let pdfResponse = '';
|
|
1364
1403
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1404
|
+
// Step 1: Extract text — try server (pdftotext) first, then local fallback
|
|
1405
|
+
let pdfText = '';
|
|
1406
|
+
try {
|
|
1407
|
+
const extractRes = await fetch('https://nothumanallowed.com/api/v1/tools/extract-pdf', {
|
|
1408
|
+
method: 'POST',
|
|
1409
|
+
headers: { 'Content-Type': 'application/json', 'X-NHA-Client': 'desktop' },
|
|
1410
|
+
body: JSON.stringify({ base64: body.pdfBase64 }),
|
|
1411
|
+
signal: AbortSignal.timeout(30000),
|
|
1412
|
+
});
|
|
1413
|
+
if (extractRes.ok) {
|
|
1414
|
+
const d = await extractRes.json();
|
|
1415
|
+
pdfText = d.text || '';
|
|
1416
|
+
}
|
|
1417
|
+
} catch { /* server unreachable */ }
|
|
1418
|
+
// Local fallback if server extraction failed
|
|
1419
|
+
if (pdfText.length < 20) {
|
|
1368
1420
|
const pdfBuffer = Buffer.from(body.pdfBase64, 'base64');
|
|
1369
|
-
|
|
1421
|
+
pdfText = extractTextFromPdf(pdfBuffer);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// Save extracted text as llmContent so it persists across turns
|
|
1425
|
+
const pdfLlmContent = pdfText.length > 20
|
|
1426
|
+
? `[PDF: ${body.pdfName}]\n\n${pdfText.slice(0, 12000)}\n\n---\n\nUser question: ${pdfPrompt}`
|
|
1427
|
+
: '';
|
|
1428
|
+
|
|
1429
|
+
if (provider === 'nha') {
|
|
1370
1430
|
if (!pdfText || pdfText.length < 10) {
|
|
1371
|
-
// Fallback: send first page as image to vision model
|
|
1372
1431
|
const r = await fetch('https://nothumanallowed.com/api/v1/liara/vision', {
|
|
1373
1432
|
method: 'POST',
|
|
1374
1433
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -1381,7 +1440,6 @@ export async function cmdUI(args) {
|
|
|
1381
1440
|
pdfResponse = 'Could not read this PDF. Try a text-based PDF or use Claude/Gemini for scanned documents.';
|
|
1382
1441
|
}
|
|
1383
1442
|
} else {
|
|
1384
|
-
// Send extracted text to Liara chat
|
|
1385
1443
|
const truncatedText = pdfText.slice(0, 12000);
|
|
1386
1444
|
const r = await fetch('https://nothumanallowed.com/api/v1/liara/chat', {
|
|
1387
1445
|
method: 'POST',
|
|
@@ -1439,7 +1497,8 @@ export async function cmdUI(args) {
|
|
|
1439
1497
|
pdfResponse = `PDF reading requires Anthropic (Claude) or Gemini. Your provider "${provider}" does not support native PDF documents.`;
|
|
1440
1498
|
}
|
|
1441
1499
|
|
|
1442
|
-
|
|
1500
|
+
// Return llmContent so frontend can persist the PDF text across turns
|
|
1501
|
+
sendJSON(res, 200, { response: pdfResponse, llmContent: pdfLlmContent || undefined });
|
|
1443
1502
|
logRequest(method, pathname, 200, Date.now() - start);
|
|
1444
1503
|
return;
|
|
1445
1504
|
} catch (e) {
|
|
@@ -1449,12 +1508,14 @@ export async function cmdUI(args) {
|
|
|
1449
1508
|
}
|
|
1450
1509
|
}
|
|
1451
1510
|
|
|
1452
|
-
// Handle text file attachment
|
|
1511
|
+
// Handle text file attachment — include file content as llmContent for persistence
|
|
1512
|
+
let fileLlmContent = '';
|
|
1453
1513
|
if (body.fileContent && body.fileName) {
|
|
1454
1514
|
const filePrompt = body.message
|
|
1455
1515
|
? `User asks about file "${body.fileName}": ${body.message}\n\nFile content:\n${body.fileContent.slice(0, 8000)}`
|
|
1456
1516
|
: `Analyze this file "${body.fileName}":\n\n${body.fileContent.slice(0, 8000)}`;
|
|
1457
1517
|
userMessage = filePrompt;
|
|
1518
|
+
fileLlmContent = filePrompt;
|
|
1458
1519
|
}
|
|
1459
1520
|
|
|
1460
1521
|
try {
|
|
@@ -1551,7 +1612,7 @@ export async function cmdUI(args) {
|
|
|
1551
1612
|
} catch { /* non-critical */ }
|
|
1552
1613
|
try { extractMemory('chat', body.message, fullResponse); } catch { /* non-critical */ }
|
|
1553
1614
|
|
|
1554
|
-
sendJSON(res, 200, { response: fullResponse, toolResults, actions });
|
|
1615
|
+
sendJSON(res, 200, { response: fullResponse, toolResults, actions, ...(fileLlmContent ? { llmContent: fileLlmContent } : {}) });
|
|
1555
1616
|
} catch (e) {
|
|
1556
1617
|
sendJSON(res, 200, { response: null, error: e.message });
|
|
1557
1618
|
}
|
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 = '12.6.
|
|
8
|
+
export const VERSION = '12.6.5';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -925,6 +925,11 @@ function sendChat(){
|
|
|
925
925
|
clearChatAttach();
|
|
926
926
|
apiPost('/api/chat',payload).then(function(r){
|
|
927
927
|
chatHistory.pop();
|
|
928
|
+
// Save llmContent in the user message so file context persists across turns
|
|
929
|
+
if(r&&r.llmContent){
|
|
930
|
+
var lastUser=chatHistory[chatHistory.length-1];
|
|
931
|
+
if(lastUser&&lastUser.role==='user')lastUser.llmContent=r.llmContent;
|
|
932
|
+
}
|
|
928
933
|
if(r&&r.response){chatHistory.push({role:'assistant',content:r.response})}
|
|
929
934
|
else if(r&&r.error){chatHistory.push({role:'assistant',content:'Error: '+r.error})}
|
|
930
935
|
else{chatHistory.push({role:'assistant',content:'Error: no response'})}
|
|
@@ -943,7 +948,7 @@ function sendChat(){
|
|
|
943
948
|
chatHistory.push({role:'assistant',content:''});
|
|
944
949
|
renderMessages();
|
|
945
950
|
var streamIdx=chatHistory.length-1;
|
|
946
|
-
var allHistory=chatHistory.slice(0,-1).map(function(m){
|
|
951
|
+
var allHistory=chatHistory.slice(0,-1).map(function(m){var h={role:m.role,content:(m.content||'').replace(/!\\[Screenshot\\]\\(data:image\\/[^)]+\\)/g,'[Screenshot taken]')};if(m.llmContent)h.llmContent=m.llmContent;return h;});
|
|
947
952
|
var payload={message:msg,history:allHistory,conversationId:activeConvId,isRetry:isRetry};
|
|
948
953
|
|
|
949
954
|
fetch(API+'/api/chat/stream',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload),signal:chatAbortController.signal}).then(function(response){
|