nothumanallowed 13.2.74 → 13.2.76
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 +33 -92
- 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.76",
|
|
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
|
@@ -2883,14 +2883,20 @@ export async function cmdUI(args) {
|
|
|
2883
2883
|
sendToken('[Structuring document content...] ');
|
|
2884
2884
|
const LANG_MAP_DOC = {en:'English',it:'Italian',es:'Spanish',fr:'French',de:'German',pt:'Portuguese',zh:'Chinese',ja:'Japanese',ar:'Arabic',hi:'Hindi',ru:'Russian',nl:'Dutch',pl:'Polish',tr:'Turkish',ko:'Korean',sv:'Swedish',da:'Danish',fi:'Finnish',no:'Norwegian',cs:'Czech'};
|
|
2885
2885
|
const docLang = LANG_MAP_DOC[(config?.language||'it').toLowerCase().slice(0,2)] || 'Italian';
|
|
2886
|
-
|
|
2886
|
+
// Put the raw PDF text in the SYSTEM prompt — SENTINEL only scans the user message.
|
|
2887
|
+
// The user message is a short, safe instruction that won't trigger false positives.
|
|
2888
|
+
const docSys = `You are a technical document analyst. The following is the raw text extracted from the document "${stepPdfName || 'document.pdf'}". Your job is to structure it into clear, readable markdown. Respond in ${docLang}.
|
|
2889
|
+
|
|
2887
2890
|
Rules:
|
|
2888
2891
|
- List ALL technical specifications with their exact values (codes, voltages, pressures, temperatures, dimensions, flow rates, etc.)
|
|
2889
2892
|
- Use markdown headers (##), bullet points (-), and tables where appropriate
|
|
2890
2893
|
- Do NOT invent, interpret, or add anything not present in the raw text
|
|
2891
2894
|
- Include all product/part codes exactly as written
|
|
2892
|
-
- Keep all numeric values with their units
|
|
2893
|
-
|
|
2895
|
+
- Keep all numeric values with their units
|
|
2896
|
+
|
|
2897
|
+
RAW DOCUMENT TEXT:
|
|
2898
|
+
${rawText.slice(0, 18000)}`;
|
|
2899
|
+
const docUser = `Structure the document content above into clean, readable markdown with all technical specifications.`;
|
|
2894
2900
|
let structuredOutput = '';
|
|
2895
2901
|
let inThink = false;
|
|
2896
2902
|
try {
|
|
@@ -2944,101 +2950,36 @@ Rules:
|
|
|
2944
2950
|
} else if (agent === 'WebSearchAgent' || agent === 'ResearchAgent') {
|
|
2945
2951
|
sendToken('[Searching the web and reading pages...] ');
|
|
2946
2952
|
try {
|
|
2947
|
-
//
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
// If context contains extracted PDF/document data, build a smart search query.
|
|
2951
|
-
// Detect whether the task asks for "similar" products vs. exact product lookup.
|
|
2953
|
+
// If there is document context from a previous step, ask the LLM to derive
|
|
2954
|
+
// the optimal search queries. This is generic and works for any document/task.
|
|
2955
|
+
let searchQueries = [stepPrompt.slice(0, 120)];
|
|
2952
2956
|
if (context && context.length > 50) {
|
|
2953
|
-
|
|
2954
|
-
const wantsSimilar = /simil|equivalen|alternativ|sostitut|find similar|alternative|replacement/i.test(taskLow2 + ' ' + stepPrompt);
|
|
2955
|
-
|
|
2956
|
-
// Extract manufacturer name from structured context
|
|
2957
|
-
const mfrMatch = context.match(/(?:Marca|Marchio|Manufacturer|Brand|Produttore|Costruttore|Parker|Bosch|Rexroth|SMC|Festo|Burkert|Norgren|Sirai|Asco)[:\s]*([A-Za-z0-9 &]{2,40})/i);
|
|
2958
|
-
const mfr = mfrMatch ? mfrMatch[1].trim().split(/\s+/).slice(0,2).join(' ') : '';
|
|
2959
|
-
|
|
2960
|
-
// Extract model/part code: 3-8 char alphanumeric, NOT common words/units
|
|
2961
|
-
const IGNORE = new Set(['NBR','FKM','RUBY','BSP','PDF','URL','HTTP','THE','AND','FOR','MIN','MAX','BAR','VAC','KV','AC','DC','CE','UK','EU','ISO','DIN']);
|
|
2962
|
-
const codeRx = /\b([A-Z][A-Z0-9]{2,7}(?:[-\/][A-Z0-9]{1,6})*)\b/g;
|
|
2963
|
-
const allCodes = [];
|
|
2964
|
-
let cm;
|
|
2965
|
-
while ((cm = codeRx.exec(context)) !== null) {
|
|
2966
|
-
const c = cm[1];
|
|
2967
|
-
if (!IGNORE.has(c) && c.length >= 4 && /\d/.test(c)) allCodes.push(c);
|
|
2968
|
-
}
|
|
2969
|
-
const productCode = [...new Set(allCodes)][0] || '';
|
|
2970
|
-
|
|
2971
|
-
// Extract key technical specs for similarity search
|
|
2972
|
-
const pressMatch = context.match(/(?:pressione|pressure|MOPD)[^\d]*(\d+)\s*bar/i);
|
|
2973
|
-
const pressure = pressMatch ? pressMatch[1] + ' bar' : '';
|
|
2974
|
-
const portMatch = context.match(/(?:porta|port|attacco|fitting)[^\d]*(\d+\/\d+[''"]?)/i);
|
|
2975
|
-
const port = portMatch ? portMatch[1] : '';
|
|
2976
|
-
const typeMatch = context.match(/(?:valvola|valve|solenoid)[^,\n]{0,30}?(2\/2|3\/2|pilot.operated|normally.closed|normally.open)/i);
|
|
2977
|
-
const valveType = typeMatch ? typeMatch[1] : '';
|
|
2978
|
-
|
|
2979
|
-
if (wantsSimilar) {
|
|
2980
|
-
// Task wants equivalent/similar product — search by specs, not by exact code
|
|
2981
|
-
const specParts = [
|
|
2982
|
-
valveType || 'solenoid valve',
|
|
2983
|
-
pressure,
|
|
2984
|
-
port,
|
|
2985
|
-
'hydraulic oil',
|
|
2986
|
-
'acquista distributore Italy'
|
|
2987
|
-
].filter(Boolean);
|
|
2988
|
-
searchQuery = specParts.join(' ');
|
|
2989
|
-
if (mfr && mfr.toLowerCase() !== productCode.toLowerCase()) searchQuery = mfr + ' ' + searchQuery;
|
|
2990
|
-
sendToken(`[Search query (similar product): "${searchQuery}"] `);
|
|
2991
|
-
} else if (productCode) {
|
|
2992
|
-
// Task wants the exact product — search by code + manufacturer
|
|
2993
|
-
searchQuery = (mfr ? mfr + ' ' : '') + productCode + ' acquista distributore';
|
|
2994
|
-
sendToken(`[Search query (exact product): "${searchQuery}"] `);
|
|
2995
|
-
}
|
|
2996
|
-
}
|
|
2997
|
-
|
|
2998
|
-
if (!context && searchQuery.length > 120) {
|
|
2999
|
-
const keywordMatch = searchQuery.match(/(?:cerca|search|find|ricerca|notizie su|news about|latest on|aggiornamenti su)\s+(.{5,80}?)(?:\s+(?:e|and|per|for|poi|then)|$)/i);
|
|
3000
|
-
if (keywordMatch) {
|
|
3001
|
-
searchQuery = keywordMatch[1].trim();
|
|
3002
|
-
} else {
|
|
3003
|
-
searchQuery = searchQuery.split(/[,\.\n]/)[0].slice(0, 100).trim();
|
|
3004
|
-
}
|
|
3005
|
-
}
|
|
3006
|
-
// If step prompt / task contains a specific domain, fetch that page directly first
|
|
3007
|
-
const domainMatch = (stepPrompt + ' ' + task).match(/(?:https?:\/\/)?(?:www\.)?([a-z0-9-]+\.[a-z]{2,}(?:\/[^\s,]*)?)/i);
|
|
3008
|
-
if (domainMatch) {
|
|
3009
|
-
let targetUrl = domainMatch[0];
|
|
3010
|
-
if (!targetUrl.startsWith('http')) targetUrl = 'https://' + targetUrl;
|
|
3011
|
-
sendToken(`[Fetching ${targetUrl}...] `);
|
|
2957
|
+
sendToken('[Building search queries from document...] ');
|
|
3012
2958
|
try {
|
|
3013
|
-
|
|
3014
|
-
const
|
|
3015
|
-
|
|
3016
|
-
|
|
2959
|
+
// Document context goes in system prompt — SENTINEL only scans user message
|
|
2960
|
+
const queryPlanSys = `You are a search query generator. Given a document summary and a user task, output a JSON array of 1-3 concise web search queries (strings, max 80 chars each) that will find the best results. Output ONLY the JSON array, no explanation.\n\nDocument content:\n${context.slice(0, 3000)}`;
|
|
2961
|
+
const queryPlanUser = `User task: "${task.slice(0, 200)}". Generate search queries. If task asks for similar/alternative products use technical specs. If it asks where to buy include vendor queries. Output: ["query1","query2",...]`;
|
|
2962
|
+
const planConfig2 = Object.assign({}, config, { thinking: 'off' });
|
|
2963
|
+
const queryRaw = await withTimeout(callLLM(planConfig2, queryPlanSys, queryPlanUser, { max_tokens: 200 }), 15000);
|
|
2964
|
+
const jsonMatch = queryRaw.match(/\[[\s\S]*?\]/);
|
|
2965
|
+
if (jsonMatch) {
|
|
2966
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
2967
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
2968
|
+
searchQueries = parsed.filter(q => typeof q === 'string' && q.length > 2).slice(0, 3);
|
|
2969
|
+
}
|
|
3017
2970
|
}
|
|
3018
2971
|
} catch {}
|
|
2972
|
+
sendToken(`[Queries: ${searchQueries.map(q => '"' + q + '"').join(', ')}] `);
|
|
3019
2973
|
}
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
// Secondary search: if task mentions "similar" or "where to buy", run a second query for vendors
|
|
3026
|
-
const taskLow3 = task.toLowerCase();
|
|
3027
|
-
if (/simil|equivalen|alternativ|sostitut|rivendit|distributore|vend|acquist|compra|dove\s+trovare|where\s+to\s+buy/i.test(taskLow3) && context && context.length > 50) {
|
|
3028
|
-
// Build vendor search query from manufacturer + product type
|
|
3029
|
-
const mfrVendor = context.match(/(?:Parker|Bosch|Rexroth|SMC|Festo|Burkert|Norgren|Sirai|Asco)\s+[A-Za-z]*/i);
|
|
3030
|
-
const mfrName = mfrVendor ? mfrVendor[0].trim() : '';
|
|
3031
|
-
const typeVendor = context.match(/(?:valvola|valve|solenoid|elettrovalvola)[^,\n]{0,30}?(2\/2|pilot.operated|normally.closed)/i);
|
|
3032
|
-
const typeStr = typeVendor ? typeVendor[1] : 'solenoid valve';
|
|
3033
|
-
const pressVendor = context.match(/(\d+)\s*bar/i);
|
|
3034
|
-
const pressStr = pressVendor ? pressVendor[1] + ' bar' : '';
|
|
3035
|
-
const vendorQuery = [mfrName, typeStr, pressStr, 'rivenditore distributore acquisto'].filter(Boolean).join(' ');
|
|
3036
|
-
sendToken(`[Search query (vendor/similar): "${vendorQuery}"] `);
|
|
2974
|
+
|
|
2975
|
+
// Run all queries sequentially, accumulate results
|
|
2976
|
+
for (let qi = 0; qi < searchQueries.length; qi++) {
|
|
2977
|
+
const q = searchQueries[qi];
|
|
3037
2978
|
try {
|
|
3038
|
-
const
|
|
3039
|
-
const
|
|
3040
|
-
toolData +=
|
|
3041
|
-
} catch {}
|
|
2979
|
+
const searchResult = await withTimeout(executeTool('web_search', { query: q, deep: qi === 0 }, config), 25000);
|
|
2980
|
+
const searchStr = typeof searchResult === 'string' ? searchResult : JSON.stringify(searchResult);
|
|
2981
|
+
toolData += (toolData ? '\n\n' : '') + `## Web search: "${q}":\n${searchStr}`;
|
|
2982
|
+
} catch (e) { toolData += (toolData ? '\n\n' : '') + `## Search "${q}" failed: ${e.message}`; }
|
|
3042
2983
|
}
|
|
3043
2984
|
} catch (e) { toolData = toolData || `Web search failed: ${e.message}`; }
|
|
3044
2985
|
|
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.76';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|