nothumanallowed 13.3.1 → 13.3.2

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.3.1",
3
+ "version": "13.3.2",
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": {
@@ -2769,42 +2769,135 @@ export async function cmdUI(args) {
2769
2769
  return steps;
2770
2770
  };
2771
2771
 
2772
- // Use keyword plan directly only fall back to LLM for genuinely ambiguous tasks
2772
+ // ── Hybrid planning: keyword baseline + LLM refinement ──────────────────
2773
+ //
2774
+ // Strategy (3 tiers):
2775
+ //
2776
+ // TIER 1 — Keyword baseline (always runs, <1ms, zero LLM):
2777
+ // Builds a solid plan from regex matches on the task. Reliable for all
2778
+ // known patterns. Already contains `reason` for each step.
2779
+ //
2780
+ // TIER 2 — LLM refinement (runs when baseline ≥ 1 step OR task is non-trivial):
2781
+ // Receives the task + the keyword plan as context. Can ADD missing steps,
2782
+ // REMOVE wrong ones, REORDER, and ADJUST prompts. Does NOT build from scratch.
2783
+ // Falls back to keyword plan on any parse/timeout error.
2784
+ //
2785
+ // TIER 3 — LLM-only fallback (runs when keyword baseline is empty):
2786
+ // Task had zero keyword matches → pure LLM planning with full task text.
2787
+ // Same fallback: on error, returns a single WebSearchAgent step.
2788
+ //
2789
+ // Why this is safe now: SENTINEL's /api/studio/ is an intent-aware route.
2790
+ // Prompt injection detection is disabled for this path — the body IS the task.
2791
+ // Encoding attacks, rate limits, and toxicity checks remain fully active.
2792
+
2773
2793
  const keywordSteps = buildKeywordPlan();
2774
- const taskIsComplex = !hasPdf && !hasEmail && !hasCalendar && !hasSearch && !hasGitHub && !hasSlack && !hasBriefing && !hasStrategy && !hasReputation && !hasCode && !hasWriting && !hasData && keywordSteps.length <= 1;
2794
+ const hasKeywordPlan = keywordSteps.length > 0;
2795
+
2796
+ // Sanitize task for LLM: strip HTML tags and control chars (defensive, not SENTINEL).
2797
+ const sanitizedTask = task.replace(/<[^>]*>/g, ' ').replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, '').trim();
2798
+
2799
+ // Build a compact JSON representation of the keyword plan for the LLM to refine.
2800
+ const keywordPlanJson = hasKeywordPlan
2801
+ ? JSON.stringify(keywordSteps.map(s => ({ agent: s.agent, label: s.label, reason: s.reason || '' })))
2802
+ : '[]';
2803
+
2804
+ const planConfig = Object.assign({}, config, { thinking: 'off' });
2775
2805
 
2776
2806
  try {
2777
- let steps;
2778
- if (!taskIsComplex) {
2779
- // Use keyword plan directly no LLM, no SENTINEL risk
2780
- process.stderr.write('[STUDIO PLAN KEYWORD] steps=' + keywordSteps.length + '\n');
2781
- steps = keywordSteps;
2782
- } else {
2783
- // Task is ambiguous — use LLM planner with sanitized short description
2784
- const shortTask = task.slice(0, 200).replace(/[`'"]/g, ' ');
2785
- const plannerLangStr = plannerLang;
2786
- const planPrompt = `Workflow planner. Goal: ${shortTask}\nLanguage: ${plannerLangStr}.\nOutput ONLY JSON:\n{"steps":[{"icon":"EMOJI","agent":"AGENT_NAME","label":"LABEL","prompt":"INSTRUCTION"}]}\nAgents: WebSearchAgent, EmailAgent, CalendarAgent, HERALD, ORACLE, ATHENA, CASSANDRA, MERCURY, QUILL, CanvasAgent (last, only if visual needed). 2-5 steps.`;
2787
- const planConfig = Object.assign({}, config, { thinking: 'off' });
2788
- const planRaw = await callLLM(planConfig, 'Output ONLY valid JSON. No explanation.', planPrompt, { max_tokens: 800 });
2789
- process.stderr.write('[STUDIO PLAN LLM RAW] ' + planRaw.slice(0, 400) + '\n');
2807
+ let steps = keywordSteps;
2808
+
2809
+ // TIER 2 / 3: always attempt LLM if we have a working LLM config
2810
+ if (config && (config.provider || config.apiKey || config.baseUrl)) {
2790
2811
  try {
2812
+ let planPrompt;
2813
+ let planSys;
2814
+
2815
+ if (hasKeywordPlan) {
2816
+ // TIER 2: refine the keyword plan
2817
+ planSys = `You are a workflow planner for NHA Studio. Output ONLY valid JSON — no explanation, no markdown.`;
2818
+ planPrompt = `Task: ${sanitizedTask}
2819
+
2820
+ Keyword-detected plan (JSON):
2821
+ ${keywordPlanJson}
2822
+
2823
+ Language for labels: ${plannerLang}.
2824
+
2825
+ Review the plan above. You may:
2826
+ - ADD steps that are clearly needed but missing
2827
+ - REMOVE steps that are wrong for this task
2828
+ - REORDER steps to fix logical sequence (e.g. Notion before email)
2829
+ - ADJUST the "prompt" field of any step to better match the task
2830
+ - KEEP steps that are correct as-is
2831
+
2832
+ Available agents: WebSearchAgent, DocumentReaderAgent, EmailAgent, CalendarAgent, GitHubAgent, SlackAgent, NotionAgent, HERALD, ORACLE, ATHENA, CASSANDRA, MERCURY, QUILL, DataAnalystAgent, polyglot, CanvasAgent (last, only if visual output needed).
2833
+
2834
+ Output ONLY:
2835
+ {"steps":[{"icon":"EMOJI","agent":"AGENT_NAME","label":"LABEL","reason":"WHY THIS AGENT","prompt":"INSTRUCTION"}]}
2836
+
2837
+ Rules:
2838
+ - 2 to 6 steps maximum
2839
+ - CanvasAgent only as the final step and only for complex multi-agent analyses
2840
+ - Keep existing reasons where step is unchanged, write a new reason when you add/change a step`;
2841
+ } else {
2842
+ // TIER 3: pure LLM planning — zero keyword matches
2843
+ planSys = `You are a workflow planner for NHA Studio. Output ONLY valid JSON — no explanation, no markdown.`;
2844
+ planPrompt = `Task: ${sanitizedTask}
2845
+
2846
+ Language for labels: ${plannerLang}.
2847
+
2848
+ Build a workflow plan for this task.
2849
+
2850
+ Available agents: WebSearchAgent, DocumentReaderAgent, EmailAgent, CalendarAgent, GitHubAgent, SlackAgent, NotionAgent, HERALD, ORACLE, ATHENA, CASSANDRA, MERCURY, QUILL, DataAnalystAgent, polyglot, CanvasAgent.
2851
+
2852
+ Output ONLY:
2853
+ {"steps":[{"icon":"EMOJI","agent":"AGENT_NAME","label":"LABEL","reason":"WHY THIS AGENT","prompt":"INSTRUCTION"}]}
2854
+
2855
+ Rules:
2856
+ - 2 to 5 steps
2857
+ - HERALD = executive synthesis when no other specialist fits
2858
+ - CanvasAgent only as the final step for complex multi-agent workflows
2859
+ - reason = one sentence explaining why this agent was chosen`;
2860
+ }
2861
+
2862
+ const planRaw = await callLLM(planConfig, planSys, planPrompt, { max_tokens: 900 });
2863
+ process.stderr.write('[STUDIO PLAN LLM RAW] mode=' + (hasKeywordPlan ? 'refine' : 'pure') + ' len=' + planRaw.length + '\n');
2864
+
2865
+ // Parse LLM output — strip <think> blocks (Qwen3), markdown fences, extract JSON
2791
2866
  let clean = planRaw;
2792
2867
  let prev = '';
2793
2868
  while (prev !== clean) { prev = clean; clean = clean.replace(/<think>[\s\S]*?<\/think>/g, ''); }
2794
- clean = clean.trim().replace(/^```[\w]*\r?\n?/,'').replace(/\r?\n?```$/,'').trim();
2869
+ clean = clean.trim().replace(/^```[\w]*\r?\n?/, '').replace(/\r?\n?```$/, '').trim();
2795
2870
  const jsonMatch = clean.match(/\{[\s\S]*\}/);
2796
2871
  const parsed = JSON.parse(jsonMatch ? jsonMatch[0] : clean);
2797
- steps = parsed.steps;
2798
- } catch (parseErr) {
2799
- process.stderr.write('[STUDIO PLAN PARSE ERR] ' + parseErr.message + '\n');
2800
- steps = keywordSteps;
2872
+
2873
+ if (Array.isArray(parsed.steps) && parsed.steps.length > 0) {
2874
+ // Merge: LLM steps override keyword steps. Preserve `reason` from keyword where LLM kept same agent.
2875
+ const keywordReasonMap = {};
2876
+ keywordSteps.forEach(s => { keywordReasonMap[s.agent] = s.reason || ''; });
2877
+ steps = parsed.steps.map(s => ({
2878
+ icon: s.icon || '\u{1F916}',
2879
+ agent: s.agent,
2880
+ label: s.label,
2881
+ reason: s.reason || keywordReasonMap[s.agent] || '',
2882
+ prompt: s.prompt,
2883
+ }));
2884
+ process.stderr.write('[STUDIO PLAN LLM OK] steps=' + steps.length + '\n');
2885
+ } else {
2886
+ process.stderr.write('[STUDIO PLAN LLM EMPTY] falling back to keyword plan\n');
2887
+ }
2888
+ } catch (llmErr) {
2889
+ process.stderr.write('[STUDIO PLAN LLM ERR] ' + llmErr.message + ' — using keyword plan\n');
2890
+ // steps already = keywordSteps, no action needed
2801
2891
  }
2892
+ } else {
2893
+ process.stderr.write('[STUDIO PLAN KEYWORD ONLY] no LLM config, steps=' + keywordSteps.length + '\n');
2802
2894
  }
2895
+
2896
+ // Final safety net: if everything failed and we have nothing, single web search step
2803
2897
  if (!Array.isArray(steps) || !steps.length) {
2804
- sendJSON(res, 500, { error: 'Empty workflow plan' });
2805
- logRequest(method, pathname, 500, Date.now() - start);
2806
- return;
2898
+ steps = [{ icon: '\u{1F50D}', agent: 'WebSearchAgent', label: plannerLang === 'Italian' ? 'Ricerca web' : 'Web search', reason: plannerLang === 'Italian' ? 'Fallback: nessun piano costruito' : 'Fallback: no plan built', prompt: sanitizedTask }];
2807
2899
  }
2900
+
2808
2901
  sendJSON(res, 200, { steps });
2809
2902
  logRequest(method, pathname, 200, Date.now() - start);
2810
2903
  } catch (e) {
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.3.1';
8
+ export const VERSION = '13.3.2';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11