nothumanallowed 13.2.92 → 13.2.93

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": "13.2.92",
3
+ "version": "13.2.93",
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": {
@@ -189,11 +189,11 @@ function sendHTML(res, html) {
189
189
  res.end(html);
190
190
  }
191
191
 
192
- function parseBody(req) {
192
+ function parseBody(req, maxBytes) {
193
193
  return new Promise((resolve, reject) => {
194
194
  const chunks = [];
195
195
  let size = 0;
196
- const MAX = 1_048_576; // 1 MB
196
+ const MAX = maxBytes || 1_048_576; // 1 MB default
197
197
  req.on('data', chunk => {
198
198
  size += chunk.length;
199
199
  if (size > MAX) { reject(new Error('Body too large')); req.destroy(); return; }
@@ -2814,7 +2814,7 @@ export async function cmdUI(args) {
2814
2814
 
2815
2815
  // ── Studio: run single step (SSE streaming) ──────────────────────
2816
2816
  if (pathname === '/api/studio/run' && method === 'POST') {
2817
- const body = await parseBody(req);
2817
+ const body = await parseBody(req, 4_194_304); // 4MB — context can be up to 120KB + task + PDF
2818
2818
  const { agent, task, context, stepDef } = body;
2819
2819
  const stepPdfBase64 = body.pdfBase64 || null;
2820
2820
  const stepPdfName = body.pdfName || null;
@@ -3284,7 +3284,11 @@ ${context ? `## OUTPUT FROM PREVIOUS AGENTS (use only what is RELEVANT to the wo
3284
3284
  let inThinkBlock = false;
3285
3285
  let thinkBuf = '';
3286
3286
  sendToken(isCanvasAgent ? 'Generating visual report...' : '');
3287
- const llmTimeout = isCanvasAgent ? 120000 : 90000;
3287
+ const llmTimeout = isCanvasAgent ? 180000 : 120000;
3288
+ // Canvas: no thinking (needs full tokens for HTML). Specialists: cap thinking to 2048 to leave room for answer.
3289
+ const stepLlmOpts = isCanvasAgent
3290
+ ? { max_tokens: 12288, thinking: 'off' }
3291
+ : { max_tokens: 8192, thinking_budget: 2048 };
3288
3292
  try {
3289
3293
  await withTimeout(
3290
3294
  callLLMStream(config, sysPrompt, userMsg,
@@ -3323,7 +3327,7 @@ ${context ? `## OUTPUT FROM PREVIOUS AGENTS (use only what is RELEVANT to the wo
3323
3327
  if (stripped) sendToken(stripped);
3324
3328
  }
3325
3329
  },
3326
- { max_tokens: 8192 },
3330
+ stepLlmOpts,
3327
3331
  ),
3328
3332
  llmTimeout
3329
3333
  );
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.92';
8
+ export const VERSION = '13.2.93';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -502,21 +502,33 @@ export async function callLLMStream(config, systemPrompt, userMessage, onToken,
502
502
  .replace(/\|\|\(/g, '||(')
503
503
  .replace(/\)\|\|/g, ')||');
504
504
 
505
+ // opts.thinking === 'off' forces thinking off regardless of config
506
+ // opts.max_tokens overrides the default token budget
507
+ const forceThinkingOff = opts.thinking === 'off' || opts.thinking === false;
508
+
505
509
  let thinkingEnabled = false;
506
- try {
507
- const fs2 = await import('fs');
508
- const path2 = await import('path');
509
- const os2 = await import('os');
510
- const cfgFile2 = path2.default.join(os2.default.homedir(), '.nha', 'config.json');
511
- if (fs2.default.existsSync(cfgFile2)) {
512
- const cfg2 = JSON.parse(fs2.default.readFileSync(cfgFile2, 'utf-8'));
513
- thinkingEnabled = cfg2.thinking === true || cfg2.thinking === 'on' || cfg2.thinking === 'true';
514
- }
515
- } catch {}
510
+ if (!forceThinkingOff) {
511
+ try {
512
+ const fs2 = await import('fs');
513
+ const path2 = await import('path');
514
+ const os2 = await import('os');
515
+ const cfgFile2 = path2.default.join(os2.default.homedir(), '.nha', 'config.json');
516
+ if (fs2.default.existsSync(cfgFile2)) {
517
+ const cfg2 = JSON.parse(fs2.default.readFileSync(cfgFile2, 'utf-8'));
518
+ thinkingEnabled = cfg2.thinking === true || cfg2.thinking === 'on' || cfg2.thinking === 'true';
519
+ }
520
+ } catch {}
521
+ }
522
+
523
+ // Determine effective max_tokens:
524
+ // 1. If opts.max_tokens explicitly set, use it
525
+ // 2. If thinking is on, default to 8192 (need room for think + answer)
526
+ // 3. Otherwise default to 8192 (full context for specialist agents)
527
+ const effectiveMaxTokens = opts.max_tokens || (thinkingEnabled ? 8192 : 8192);
516
528
 
517
529
  const nhaBody = {
518
530
  model: model || '/opt/models/qwen3-32b',
519
- max_tokens: thinkingEnabled ? 8192 : 4096,
531
+ max_tokens: effectiveMaxTokens,
520
532
  messages: [
521
533
  { role: 'system', content: sanitize(systemPrompt) },
522
534
  { role: 'user', content: sanitize(userMessage) },
@@ -3395,14 +3395,36 @@ function studioScrollToAgent(agentLabel) {
3395
3395
  if (!logEl) return;
3396
3396
  var entries = logEl.querySelectorAll('.studio-log-entry');
3397
3397
  var target = null;
3398
+ var labelLow = agentLabel.toLowerCase();
3399
+ // Pass 1: exact match
3398
3400
  for (var i2 = 0; i2 < entries.length; i2++) {
3399
3401
  var agentSpan = entries[i2].querySelector('.studio-log-entry__agent');
3400
3402
  if (agentSpan && agentSpan.textContent.trim() === agentLabel) { target = entries[i2]; break; }
3401
3403
  }
3404
+ // Pass 2: startsWith (handles Parliament label that changes to "ATHENA ⇄ ...")
3405
+ if (!target) {
3406
+ for (var i3 = 0; i3 < entries.length; i3++) {
3407
+ var sp = entries[i3].querySelector('.studio-log-entry__agent');
3408
+ if (sp && (sp.textContent.trim().toLowerCase().indexOf(labelLow) === 0 || labelLow.indexOf(sp.textContent.trim().toLowerCase()) === 0)) { target = entries[i3]; break; }
3409
+ }
3410
+ }
3411
+ // Pass 3: contains (for Parliament: "Parlamento" matches any log entry with that word)
3412
+ if (!target) {
3413
+ for (var i4 = 0; i4 < entries.length; i4++) {
3414
+ var sp2 = entries[i4].querySelector('.studio-log-entry__agent');
3415
+ if (sp2 && sp2.textContent.trim().toLowerCase().indexOf(labelLow.slice(0,8)) >= 0) { target = entries[i4]; break; }
3416
+ }
3417
+ }
3402
3418
  if (target) {
3403
- target.scrollIntoView({behavior:'smooth', block:'start'});
3419
+ var logContainer = document.querySelector('.studio-log');
3420
+ if (logContainer) {
3421
+ var entryTop = target.offsetTop - logContainer.offsetTop;
3422
+ logContainer.scrollTo({top: entryTop - 8, behavior: 'smooth'});
3423
+ } else {
3424
+ target.scrollIntoView({behavior:'smooth', block:'start'});
3425
+ }
3404
3426
  target.style.outline = '2px solid var(--green)';
3405
- setTimeout(function(){ target.style.outline = ''; }, 1500);
3427
+ setTimeout(function(){ target.style.outline = ''; }, 1800);
3406
3428
  }
3407
3429
  }
3408
3430
 
@@ -4152,7 +4174,7 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
4152
4174
  // Inject attachment into first step only — pass PDF/image as dedicated fields,
4153
4175
  // NOT as raw base64 in context (would cause 100k+ token overflow for any real PDF).
4154
4176
  // Cap accumulated context to ~40KB to avoid token overflow — keep the most recent content
4155
- var cappedContext = context && context.length > 40000 ? context.slice(-40000) : context;
4177
+ var cappedContext = context && context.length > 120000 ? context.slice(-120000) : context;
4156
4178
  var bodyObj = {stepIdx: idx, agent: node.agent, task: task, context: cappedContext, stepDef: stepDef};
4157
4179
  if (idx === 0 && studioState.attachmentContext) {
4158
4180
  var ac = studioState.attachmentContext;