nothumanallowed 13.2.96 → 13.2.97
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 +126 -102
- package/src/constants.mjs +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.2.
|
|
3
|
+
"version": "13.2.97",
|
|
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
|
@@ -3280,122 +3280,146 @@ ${context ? `## OUTPUT FROM PREVIOUS AGENTS (use only what is RELEVANT to the wo
|
|
|
3280
3280
|
}
|
|
3281
3281
|
|
|
3282
3282
|
// ── Stream LLM response ───────────────────────────────────────
|
|
3283
|
+
// Specialist (non-canvas, non-live-data) agents use Structured Generation:
|
|
3284
|
+
// Phase 1 — Outline: one call that returns ONLY section headings as a numbered list
|
|
3285
|
+
// Phase 2 — Section fill: one call per section, with full context of sections already written
|
|
3286
|
+
// Live-data + Canvas agents use the classic single-stream approach.
|
|
3283
3287
|
let fullOutput = '';
|
|
3284
|
-
let
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
const
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3288
|
+
let fullOutputClean = '';
|
|
3289
|
+
|
|
3290
|
+
// ── Helper: strip <think> tags from a string ─────────────────
|
|
3291
|
+
const stripThink = (s) => s.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
|
3292
|
+
|
|
3293
|
+
// ── Helper: stream a single LLM call, strip think tags, return full text ─
|
|
3294
|
+
const streamCall = async (sysPr, usrMsg, opts, timeout, onToken) => {
|
|
3295
|
+
let raw = '';
|
|
3296
|
+
let inThink = false;
|
|
3297
|
+
let tBuf = '';
|
|
3293
3298
|
await withTimeout(
|
|
3294
|
-
callLLMStream(config,
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
} else {
|
|
3310
|
-
buf = '';
|
|
3311
|
-
}
|
|
3312
|
-
} else {
|
|
3313
|
-
const openIdx = buf.indexOf('<think>');
|
|
3314
|
-
if (openIdx >= 0) {
|
|
3315
|
-
out += buf.slice(0, openIdx);
|
|
3316
|
-
buf = buf.slice(openIdx + 7);
|
|
3317
|
-
inThinkBlock = true;
|
|
3318
|
-
} else {
|
|
3319
|
-
out += buf;
|
|
3320
|
-
buf = '';
|
|
3321
|
-
}
|
|
3322
|
-
}
|
|
3299
|
+
callLLMStream(config, sysPr, usrMsg, (tok) => {
|
|
3300
|
+
raw += tok;
|
|
3301
|
+
if (onToken) {
|
|
3302
|
+
tBuf += tok;
|
|
3303
|
+
let out = '';
|
|
3304
|
+
let buf = tBuf;
|
|
3305
|
+
while (buf.length > 0) {
|
|
3306
|
+
if (inThink) {
|
|
3307
|
+
const ci = buf.indexOf('</think>');
|
|
3308
|
+
if (ci >= 0) { buf = buf.slice(ci + 8); inThink = false; }
|
|
3309
|
+
else { buf = ''; }
|
|
3310
|
+
} else {
|
|
3311
|
+
const oi = buf.indexOf('<think>');
|
|
3312
|
+
if (oi >= 0) { out += buf.slice(0, oi); buf = buf.slice(oi + 7); inThink = true; }
|
|
3313
|
+
else { out += buf; buf = ''; }
|
|
3323
3314
|
}
|
|
3324
|
-
thinkBuf = '';
|
|
3325
|
-
// Strip JSON tool calls from specialist agent output
|
|
3326
|
-
const stripped = out.replace(/\{[\s\S]*?"action"[\s\S]*?\}/g, '').trim();
|
|
3327
|
-
if (stripped) sendToken(stripped);
|
|
3328
3315
|
}
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3316
|
+
tBuf = '';
|
|
3317
|
+
const stripped = out.replace(/\{[\s\S]*?"action"[\s\S]*?\}/g, '').trim();
|
|
3318
|
+
if (stripped) onToken(stripped);
|
|
3319
|
+
}
|
|
3320
|
+
}, opts),
|
|
3321
|
+
timeout
|
|
3333
3322
|
);
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
}
|
|
3323
|
+
return stripThink(raw);
|
|
3324
|
+
};
|
|
3337
3325
|
|
|
3338
|
-
|
|
3339
|
-
let fullOutputClean = fullOutput.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
|
3326
|
+
const useStructuredGen = !isCanvasAgent && !isLiveDataAgent;
|
|
3340
3327
|
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3328
|
+
if (isCanvasAgent) {
|
|
3329
|
+
// ── Canvas: single stream, HTML output ────────────────────
|
|
3330
|
+
sendToken('Generating visual report...');
|
|
3331
|
+
try {
|
|
3332
|
+
fullOutput = await streamCall(sysPrompt, userMsg, { max_tokens: 12288, thinking: 'off' }, 180000, null);
|
|
3333
|
+
} catch (e) { /* canvas errors surfaced below */ }
|
|
3334
|
+
fullOutputClean = fullOutput;
|
|
3335
|
+
|
|
3336
|
+
} else if (useStructuredGen) {
|
|
3337
|
+
// ── Structured Generation ─────────────────────────────────
|
|
3338
|
+
// Phase 1: ask for ONLY the section outline (headings list), fast and cheap
|
|
3339
|
+
const structConfig = Object.assign({}, config, { thinking: 'off' });
|
|
3340
|
+
const outlineSys = `You are ${agent}, a specialist AI agent. Today is ${today}. Respond in ${language}.
|
|
3341
|
+
Your task: plan the sections of a complete structured report for this goal: ${task}
|
|
3342
|
+
Instruction: ${stepPrompt}
|
|
3343
|
+
|
|
3344
|
+
Output ONLY a numbered list of section headings — one per line, no body text, no explanation.
|
|
3345
|
+
Use ## prefix for each heading. Maximum 8 sections. Example:
|
|
3346
|
+
## 1. Executive Summary
|
|
3347
|
+
## 2. Key Findings
|
|
3348
|
+
## 3. Analysis`;
|
|
3349
|
+
let outlineRaw = '';
|
|
3350
|
+
try {
|
|
3351
|
+
outlineRaw = await withTimeout(
|
|
3352
|
+
new Promise((res) => {
|
|
3353
|
+
let acc = '';
|
|
3354
|
+
callLLMStream(structConfig, outlineSys, 'List the section headings only.', (tok) => { acc += tok; }, { max_tokens: 300, thinking: 'off' }).then(() => res(acc)).catch(() => res(acc));
|
|
3355
|
+
}),
|
|
3356
|
+
20000
|
|
3357
|
+
);
|
|
3358
|
+
} catch {}
|
|
3359
|
+
|
|
3360
|
+
// Parse headings: lines starting with ## (or plain numbered lines as fallback)
|
|
3361
|
+
const headingLines = stripThink(outlineRaw)
|
|
3362
|
+
.split('\n')
|
|
3363
|
+
.map(l => l.trim())
|
|
3364
|
+
.filter(l => l.match(/^#{1,4}\s+\S/) || l.match(/^\d+[\.\)]\s+\S/))
|
|
3365
|
+
.map(l => l.match(/^#{1,4}\s+/) ? l : '## ' + l.replace(/^\d+[\.\)]\s+/, ''))
|
|
3366
|
+
.slice(0, 8);
|
|
3367
|
+
|
|
3368
|
+
if (headingLines.length === 0) {
|
|
3369
|
+
// Outline failed — fall back to single stream
|
|
3370
|
+
sendToken('');
|
|
3371
|
+
try {
|
|
3372
|
+
fullOutput = await streamCall(sysPrompt, userMsg, { max_tokens: 8192, thinking_budget: 2048 }, 120000, sendToken);
|
|
3373
|
+
} catch (e) { sendToken(`[Error: ${e.message}]`); }
|
|
3374
|
+
fullOutputClean = stripThink(fullOutput);
|
|
3375
|
+
} else {
|
|
3376
|
+
// Phase 2: one call per section, each sees the sections already written
|
|
3377
|
+
const dataBlock = [
|
|
3378
|
+
attachmentText ? `## ATTACHED FILE:\n${attachmentText}` : '',
|
|
3379
|
+
toolData ? `## LIVE DATA:\n${toolData}` : '',
|
|
3380
|
+
context ? `## PREVIOUS AGENTS OUTPUT:\n${context}` : '',
|
|
3381
|
+
].filter(Boolean).join('\n\n');
|
|
3382
|
+
const sectionParts = [];
|
|
3383
|
+
for (let si = 0; si < headingLines.length; si++) {
|
|
3384
|
+
const heading = headingLines[si];
|
|
3385
|
+
const writtenSoFar = sectionParts.join('\n\n');
|
|
3386
|
+
const secSys = `You are ${agent}, a specialist AI agent inside NHA Studio. Today is ${today}. Respond entirely in ${language}.
|
|
3387
|
+
|
|
3388
|
+
## OVERALL WORKFLOW GOAL:
|
|
3389
|
+
${task}
|
|
3351
3390
|
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
sendToken('\n' + emptyHeading + '\n');
|
|
3364
|
-
let filledContent = '';
|
|
3365
|
-
const reportSoFar = fullOutputClean.slice(-3000);
|
|
3391
|
+
CRITICAL RULES:
|
|
3392
|
+
- Write ONLY the body content for the section heading given — do NOT repeat the heading
|
|
3393
|
+
- Do NOT open new headings or sub-sections — write flowing prose + bullet points
|
|
3394
|
+
- Do NOT invent data — use ONLY what is in the DATA block below
|
|
3395
|
+
- Be thorough, specific, and complete — this is one section of an executive report
|
|
3396
|
+
|
|
3397
|
+
${dataBlock}
|
|
3398
|
+
${writtenSoFar ? `## REPORT WRITTEN SO FAR (for consistency):\n${writtenSoFar.slice(-3000)}` : ''}`;
|
|
3399
|
+
const secUser = `Write the complete body content for this section (do NOT repeat the heading):\n${heading}`;
|
|
3400
|
+
sendToken('\n\n' + heading + '\n');
|
|
3401
|
+
let secContent = '';
|
|
3366
3402
|
try {
|
|
3367
|
-
await
|
|
3368
|
-
callLLMStream(
|
|
3369
|
-
fillConfig,
|
|
3370
|
-
`You are ${agent}. You wrote a report and accidentally left one section empty. Fill in ONLY the missing section — do NOT repeat the heading, do NOT add other sections. Be specific, consistent with the rest of the report, and thorough. Write in ${language}.\n\nSource data:\n${dataForFill}\n\nOverall task: ${task}\n\nReport written so far (for context and style consistency):\n${reportSoFar}`,
|
|
3371
|
-
`Write ONLY the body content for this section (heading already printed, do not repeat it):\n${emptyHeading}`,
|
|
3372
|
-
(tok) => { filledContent += tok; sendToken(tok); },
|
|
3373
|
-
{ max_tokens: 1500, thinking: 'off' }
|
|
3374
|
-
),
|
|
3375
|
-
45000
|
|
3376
|
-
);
|
|
3403
|
+
secContent = await streamCall(secSys, secUser, { max_tokens: 2000, thinking: 'off' }, 60000, sendToken);
|
|
3377
3404
|
} catch {}
|
|
3378
|
-
if (
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
fullOutputClean = fullOutputClean.replace(
|
|
3382
|
-
new RegExp('(' + escapedH + ')\\n+(#{1,4}\\s)', 'g'),
|
|
3383
|
-
'$1\n\n' + filledContent.trim() + '\n\n$2'
|
|
3384
|
-
);
|
|
3385
|
-
// Inject at end of document (trailing heading)
|
|
3386
|
-
fullOutputClean = fullOutputClean.replace(
|
|
3387
|
-
new RegExp('(' + escapedH + ')\\s*$', 'g'),
|
|
3388
|
-
'$1\n\n' + filledContent.trim()
|
|
3389
|
-
);
|
|
3405
|
+
if (secContent.trim()) {
|
|
3406
|
+
sectionParts.push(heading + '\n\n' + secContent.trim());
|
|
3407
|
+
fullOutput += '\n\n' + heading + '\n\n' + secContent.trim();
|
|
3390
3408
|
}
|
|
3391
3409
|
}
|
|
3392
|
-
|
|
3393
|
-
emptySections = getEmptySections(fullOutputClean);
|
|
3410
|
+
fullOutputClean = fullOutput.trim();
|
|
3394
3411
|
}
|
|
3395
|
-
|
|
3396
|
-
|
|
3412
|
+
|
|
3413
|
+
} else {
|
|
3414
|
+
// ── Live-data agents: single stream ───────────────────────
|
|
3415
|
+
sendToken('');
|
|
3416
|
+
try {
|
|
3417
|
+
fullOutput = await streamCall(sysPrompt, userMsg, { max_tokens: 8192, thinking_budget: 2048 }, 120000, sendToken);
|
|
3418
|
+
} catch (e) { sendToken(`[Error: ${e.message}]`); }
|
|
3419
|
+
fullOutputClean = stripThink(fullOutput);
|
|
3397
3420
|
}
|
|
3398
|
-
|
|
3421
|
+
|
|
3422
|
+
// Strip trailing lone headings at end of output (truly unfillable edge cases)
|
|
3399
3423
|
fullOutputClean = fullOutputClean.replace(/(#{1,4}\s+[^\n]+)\s*$/g, '').trim();
|
|
3400
3424
|
|
|
3401
3425
|
// Fallback: if LLM returned empty and we have tool data, send it directly
|
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.97';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|