nothumanallowed 13.2.70 → 13.2.71
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 +44 -3
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +37 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.2.
|
|
3
|
+
"version": "13.2.71",
|
|
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/commands/ui.mjs
CHANGED
|
@@ -2805,6 +2805,10 @@ export async function cmdUI(args) {
|
|
|
2805
2805
|
if (pathname === '/api/studio/run' && method === 'POST') {
|
|
2806
2806
|
const body = await parseBody(req);
|
|
2807
2807
|
const { agent, task, context, stepDef } = body;
|
|
2808
|
+
const stepPdfBase64 = body.pdfBase64 || null;
|
|
2809
|
+
const stepPdfName = body.pdfName || null;
|
|
2810
|
+
const stepImageBase64 = body.imageBase64 || null;
|
|
2811
|
+
const stepImageMime = body.imageMimeType || 'image/jpeg';
|
|
2808
2812
|
if (!agent || !task) { sendJSON(res, 400, { error: 'agent and task required' }); logRequest(method, pathname, 400, Date.now() - start); return; }
|
|
2809
2813
|
|
|
2810
2814
|
res.writeHead(200, {
|
|
@@ -3009,11 +3013,48 @@ RULES:
|
|
|
3009
3013
|
- Use .priority-list for action items, .source-item for each email/news source, .bar-row for any percentage data
|
|
3010
3014
|
- Output must start with <div class="header"> and end with <div class="footer">`;
|
|
3011
3015
|
|
|
3016
|
+
// ── Handle PDF/image attachment on first step ─────────────────
|
|
3017
|
+
let attachmentText = '';
|
|
3018
|
+
if (stepPdfBase64 && !isLiveDataAgent) {
|
|
3019
|
+
sendToken('[Reading PDF...] ');
|
|
3020
|
+
try {
|
|
3021
|
+
// Extract base64 payload (strip data URL prefix if present)
|
|
3022
|
+
const b64 = stepPdfBase64.includes(',') ? stepPdfBase64.split(',')[1] : stepPdfBase64;
|
|
3023
|
+
const pdfBuffer = Buffer.from(b64, 'base64');
|
|
3024
|
+
const extracted = extractTextFromPdf(pdfBuffer);
|
|
3025
|
+
if (extracted && extracted.length > 20) {
|
|
3026
|
+
attachmentText = `## ATTACHED PDF: ${stepPdfName || 'document.pdf'}\n${extracted.slice(0, 15000)}`;
|
|
3027
|
+
} else {
|
|
3028
|
+
// PDF has no extractable text (scanned) — use vision
|
|
3029
|
+
try {
|
|
3030
|
+
const { callLLMVision } = await import('../services/llm.mjs');
|
|
3031
|
+
const visionResult = await withTimeout(
|
|
3032
|
+
callLLMVision(config, 'Extract all text and information from this PDF document.', task, { base64: b64, mimeType: 'application/pdf' }),
|
|
3033
|
+
60000
|
|
3034
|
+
);
|
|
3035
|
+
if (visionResult) attachmentText = `## ATTACHED PDF: ${stepPdfName || 'document.pdf'}\n${visionResult.slice(0, 15000)}`;
|
|
3036
|
+
} catch {}
|
|
3037
|
+
}
|
|
3038
|
+
} catch (e) { attachmentText = `[PDF read failed: ${e.message}]`; }
|
|
3039
|
+
} else if (stepImageBase64 && !isLiveDataAgent) {
|
|
3040
|
+
sendToken('[Reading image...] ');
|
|
3041
|
+
try {
|
|
3042
|
+
const { callLLMVision } = await import('../services/llm.mjs');
|
|
3043
|
+
const b64 = stepImageBase64.includes(',') ? stepImageBase64.split(',')[1] : stepImageBase64;
|
|
3044
|
+
const visionResult = await withTimeout(
|
|
3045
|
+
callLLMVision(config, 'Describe and extract all information from this image in detail.', task, { base64: b64, mimeType: stepImageMime }),
|
|
3046
|
+
45000
|
|
3047
|
+
);
|
|
3048
|
+
if (visionResult) attachmentText = `## ATTACHED IMAGE: ${body.imageName || 'image'}\n${visionResult.slice(0, 8000)}`;
|
|
3049
|
+
} catch (e) { attachmentText = `[Image read failed: ${e.message}]`; }
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3012
3052
|
let sysPrompt, userMsg;
|
|
3013
3053
|
|
|
3014
3054
|
if (isCanvasAgent) {
|
|
3015
3055
|
sysPrompt = canvasSystemPrompt;
|
|
3016
|
-
|
|
3056
|
+
const canvasData = [attachmentText, context].filter(Boolean).join('\n\n');
|
|
3057
|
+
userMsg = `Create a professional dashboard report for this data. Output ONLY the inner HTML body content (starting with <div class="header">):\n\n${canvasData}`;
|
|
3017
3058
|
} else if (isLiveDataAgent) {
|
|
3018
3059
|
// These agents fetched real data — use a focused prompt (no tool definitions to avoid JSON output)
|
|
3019
3060
|
const agentInstruction = `You are ${agent}, a specialist AI agent inside NHA Studio. Today is ${today}. Respond entirely in ${language}.
|
|
@@ -3025,7 +3066,7 @@ CRITICAL: Do NOT invent, hallucinate, or add any data not present in the DATA se
|
|
|
3025
3066
|
Do NOT output JSON, tool calls, or code blocks. Write in plain text with markdown headers.
|
|
3026
3067
|
Always apply your analysis specifically to the subject mentioned in the WORKFLOW GOAL.
|
|
3027
3068
|
|
|
3028
|
-
${toolData ? `## DATA FROM TOOLS:\n${toolData}\n` : '## DATA: No data was retrieved by this agent.\n'}
|
|
3069
|
+
${attachmentText ? `## ATTACHED FILE CONTENT:\n${attachmentText}\n` : ''}${toolData ? `## DATA FROM TOOLS:\n${toolData}\n` : '## DATA: No data was retrieved by this agent.\n'}
|
|
3029
3070
|
${context ? `## OUTPUT FROM PREVIOUS AGENTS:\n${context}\n` : ''}
|
|
3030
3071
|
|
|
3031
3072
|
Your task: ${stepPrompt}`;
|
|
@@ -3052,7 +3093,7 @@ CRITICAL RULES:
|
|
|
3052
3093
|
- Be thorough and specific — this is for an executive briefing based on REAL data only
|
|
3053
3094
|
- Always keep the OVERALL WORKFLOW GOAL in mind — apply your analysis specifically to the subject mentioned
|
|
3054
3095
|
|
|
3055
|
-
${toolData ? `## LIVE DATA FROM TOOLS:\n${toolData}\n` : '## LIVE DATA: No tool data was fetched for this step.\n'}
|
|
3096
|
+
${attachmentText ? `## ATTACHED FILE CONTENT:\n${attachmentText}\n` : ''}${toolData ? `## LIVE DATA FROM TOOLS:\n${toolData}\n` : '## LIVE DATA: No tool data was fetched for this step.\n'}
|
|
3056
3097
|
${context ? `## OUTPUT FROM PREVIOUS AGENTS:\n${context}\n` : ''}`;
|
|
3057
3098
|
userMsg = hasRealData
|
|
3058
3099
|
? `Based ONLY on the real data above, complete this task specifically for the subject in the WORKFLOW GOAL: ${stepPrompt}`
|
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.2.
|
|
8
|
+
export const VERSION = '13.2.71';
|
|
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
|
@@ -3371,6 +3371,18 @@ function downloadStudioPDF() {
|
|
|
3371
3371
|
var task = studioState.task || 'NHA Studio Report';
|
|
3372
3372
|
var today = new Date().toLocaleDateString('it-IT', {day:'2-digit',month:'2-digit',year:'numeric'});
|
|
3373
3373
|
var nodes = studioState.nodes || [];
|
|
3374
|
+
var fileName = (task).slice(0, 60).replace(/[^a-z0-9\s]/gi,'').trim().replace(/\s+/g,'-') || 'NHA-Studio';
|
|
3375
|
+
|
|
3376
|
+
// If canvas exists, download the canvas HTML directly (preserves colors and layout)
|
|
3377
|
+
if (studioState.canvas) {
|
|
3378
|
+
var blob = new Blob([studioState.canvas], {type: 'text/html'});
|
|
3379
|
+
var url = URL.createObjectURL(blob);
|
|
3380
|
+
var a = document.createElement('a');
|
|
3381
|
+
a.href = url; a.target = '_blank'; a.download = fileName + '.html';
|
|
3382
|
+
document.body.appendChild(a); a.click(); document.body.removeChild(a);
|
|
3383
|
+
setTimeout(function(){ URL.revokeObjectURL(url); }, 5000);
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3374
3386
|
|
|
3375
3387
|
// Build sections for each agent
|
|
3376
3388
|
function mdToPdfHtml(raw) {
|
|
@@ -3780,11 +3792,31 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
|
|
|
3780
3792
|
return new Promise(function(resolve) {
|
|
3781
3793
|
var output = '';
|
|
3782
3794
|
var canvasHtml = null;
|
|
3783
|
-
// Inject attachment
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3795
|
+
// Inject attachment into first step only — pass PDF/image as dedicated fields,
|
|
3796
|
+
// NOT as raw base64 in context (would cause 100k+ token overflow for any real PDF).
|
|
3797
|
+
var bodyObj = {stepIdx: idx, agent: node.agent, task: task, context: context, stepDef: stepDef};
|
|
3798
|
+
if (idx === 0 && studioState.attachmentContext) {
|
|
3799
|
+
var ac = studioState.attachmentContext;
|
|
3800
|
+
var isPdfAttach = ac.indexOf('[ATTACHED PDF:') === 0;
|
|
3801
|
+
var isImgAttach = ac.indexOf('[ATTACHED IMAGE:') === 0;
|
|
3802
|
+
// Extract base64 data URL from attachment context
|
|
3803
|
+
var b64Match = ac.indexOf('Base64: ');
|
|
3804
|
+
var dataUrl = b64Match >= 0 ? ac.slice(b64Match + 8).trim() : '';
|
|
3805
|
+
if (isPdfAttach && dataUrl) {
|
|
3806
|
+
// Pass PDF as dedicated field — agent/llm handles it natively
|
|
3807
|
+
bodyObj.pdfBase64 = dataUrl;
|
|
3808
|
+
bodyObj.pdfName = studioState.attachmentName;
|
|
3809
|
+
// Add a short note in context instead of the full base64
|
|
3810
|
+
bodyObj.context = '[User attached PDF: ' + studioState.attachmentName + ']' +
|
|
3811
|
+
(context ? String.fromCharCode(10) + String.fromCharCode(10) + context : '');
|
|
3812
|
+
} else if (isImgAttach && dataUrl) {
|
|
3813
|
+
bodyObj.imageBase64 = dataUrl;
|
|
3814
|
+
bodyObj.imageMimeType = dataUrl.indexOf('data:image/png') === 0 ? 'image/png' : 'image/jpeg';
|
|
3815
|
+
bodyObj.context = '[User attached image: ' + studioState.attachmentName + ']' +
|
|
3816
|
+
(context ? String.fromCharCode(10) + String.fromCharCode(10) + context : '');
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
var body = JSON.stringify(bodyObj);
|
|
3788
3820
|
var fetchOpts = {method: 'POST', headers: {'Content-Type': 'application/json'}, body: body};
|
|
3789
3821
|
if (signal) fetchOpts.signal = signal;
|
|
3790
3822
|
|