nothumanallowed 5.0.0 → 6.0.0

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.
@@ -26,6 +26,7 @@ import {
26
26
  import { runPlanningPipeline } from '../services/ops-pipeline.mjs';
27
27
  import { AGENTS, AGENTS_DIR, NHA_DIR, VERSION } from '../constants.mjs';
28
28
  import { getHTML } from '../services/web-ui.mjs';
29
+ import { loadChatHistory, saveChatHistory, extractMemory, buildMemoryContext } from '../services/memory.mjs';
29
30
  import { info, ok, fail, warn, C, G, D, NC, BOLD } from '../ui.mjs';
30
31
 
31
32
  // ── Constants ──────────────────────────────────────────────────────────────
@@ -545,18 +546,25 @@ export async function cmdUI(args) {
545
546
  return;
546
547
  }
547
548
 
548
- // Build message with history
549
- const history = body.history || [];
549
+ // Build message with history (merge persisted + request history)
550
+ const requestHistory = body.history || [];
550
551
  const parts = [];
551
- for (const turn of history) {
552
+ for (const turn of requestHistory) {
552
553
  const prefix = turn.role === 'user' ? '[User]' : '[Assistant]';
553
554
  parts.push(`${prefix} ${turn.content}`);
554
555
  }
555
556
  parts.push(`[User] ${body.message}`);
556
557
  const userMessage = parts.join('\n\n');
557
558
 
559
+ // Inject episodic memory context into the system prompt
560
+ let enrichedSystemPrompt = chatSystemPrompt;
558
561
  try {
559
- const response = await callLLM(config, chatSystemPrompt, userMessage);
562
+ const memCtx = buildMemoryContext('chat', body.message);
563
+ if (memCtx) enrichedSystemPrompt = chatSystemPrompt + memCtx;
564
+ } catch { /* memory unavailable */ }
565
+
566
+ try {
567
+ const response = await callLLM(config, enrichedSystemPrompt, userMessage);
560
568
  const { textParts, actions } = parseActions(response);
561
569
  const textResponse = textParts.join('\n\n');
562
570
 
@@ -577,7 +585,7 @@ export async function cmdUI(args) {
577
585
  const toolContext = toolResults.map(t => `[${t.action} result]: ${t.result}`).join('\n\n');
578
586
  const followUp = `The user asked: "${body.message}"\n\nI executed these tools and got REAL results:\n\n${toolContext}\n\nNow respond to the user based ONLY on the REAL data above. Do NOT invent or fabricate any information. Present the actual results clearly.`;
579
587
  try {
580
- fullResponse = await callLLM(config, chatSystemPrompt, followUp);
588
+ fullResponse = await callLLM(config, enrichedSystemPrompt, followUp);
581
589
  } catch {
582
590
  // Fallback: show raw results
583
591
  fullResponse = toolResults.map(t => `${t.action}: ${t.result}`).join('\n\n');
@@ -586,6 +594,15 @@ export async function cmdUI(args) {
586
594
  fullResponse = textResponse;
587
595
  }
588
596
 
597
+ // Persist chat history and extract episodic memory (fire-and-forget)
598
+ try {
599
+ const persistedHistory = loadChatHistory();
600
+ persistedHistory.push({ role: 'user', content: body.message });
601
+ persistedHistory.push({ role: 'assistant', content: fullResponse });
602
+ saveChatHistory(persistedHistory);
603
+ } catch { /* non-critical */ }
604
+ try { extractMemory('chat', body.message, fullResponse); } catch { /* non-critical */ }
605
+
589
606
  sendJSON(res, 200, { response: fullResponse, toolResults, actions });
590
607
  } catch (e) {
591
608
  sendJSON(res, 200, { response: null, error: e.message });
package/src/config.mjs CHANGED
@@ -72,6 +72,24 @@ const DEFAULT_CONFIG = {
72
72
  desktop: true,
73
73
  terminal: true,
74
74
  },
75
+ proactive: {
76
+ enabled: true,
77
+ emailFollowUp: true,
78
+ meetingPrep: true,
79
+ patterns: true,
80
+ deadlines: true,
81
+ },
82
+ },
83
+ responder: {
84
+ autoRoute: true,
85
+ telegram: {
86
+ token: '',
87
+ allowedChatIds: [],
88
+ },
89
+ discord: {
90
+ token: '',
91
+ allowedChannelIds: [],
92
+ },
75
93
  },
76
94
  plugins: {
77
95
  autoRun: true,
@@ -202,6 +220,14 @@ export function setConfigValue(key, value) {
202
220
  'voice-whisper': 'voice.preferWhisper',
203
221
  'voice-speech': 'voice.speechSynthesis',
204
222
  'voice-language': 'voice.language',
223
+ 'telegram-bot-token': 'responder.telegram.token',
224
+ 'discord-bot-token': 'responder.discord.token',
225
+ 'responder-auto-route': 'responder.autoRoute',
226
+ 'proactive': 'ops.proactive.enabled',
227
+ 'proactive-email': 'ops.proactive.emailFollowUp',
228
+ 'proactive-meeting': 'ops.proactive.meetingPrep',
229
+ 'proactive-patterns': 'ops.proactive.patterns',
230
+ 'proactive-deadlines': 'ops.proactive.deadlines',
205
231
  };
206
232
 
207
233
  const resolved = aliases[key] || key;
@@ -222,6 +248,18 @@ export function setConfigValue(key, value) {
222
248
  obj[lastKey] = value === 'true' || value === '1' || value === 'yes';
223
249
  } else if (typeof existing === 'number') {
224
250
  obj[lastKey] = Number(value);
251
+ } else if (Array.isArray(existing)) {
252
+ // Parse comma-separated values or JSON array
253
+ try {
254
+ const parsed = JSON.parse(value);
255
+ obj[lastKey] = Array.isArray(parsed) ? parsed : [parsed];
256
+ } catch {
257
+ obj[lastKey] = value.split(',').map(v => {
258
+ const trimmed = v.trim();
259
+ const num = Number(trimmed);
260
+ return !isNaN(num) && trimmed !== '' ? num : trimmed;
261
+ }).filter(Boolean);
262
+ }
225
263
  } else {
226
264
  obj[lastKey] = value;
227
265
  }
package/src/constants.mjs CHANGED
@@ -1,7 +1,11 @@
1
1
  import os from 'os';
2
2
  import path from 'path';
3
+ import { fileURLToPath } from 'url';
3
4
 
4
- export const VERSION = '5.0.0';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ export const VERSION = '6.0.0';
5
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
6
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
7
11
 
@@ -19,6 +23,9 @@ export const PIF_FILE = path.join(CORE_DIR, 'pif.mjs');
19
23
  export const VERSIONS_FILE = path.join(CORE_DIR, 'versions.json');
20
24
  export const LAST_UPDATE_CHECK = path.join(CORE_DIR, '.last-update-check');
21
25
 
26
+ /** Path to the daemon script within the installed npm package. */
27
+ export const DAEMON_SCRIPT = path.resolve(path.join(__dirname, 'services', 'ops-daemon.mjs'));
28
+
22
29
  export const AGENTS = [
23
30
  'ade', 'athena', 'atlas', 'babel', 'cartographer', 'cassandra', 'conductor',
24
31
  'cron', 'echo', 'edi', 'epicure', 'flux', 'forge', 'glitch', 'heimdall',
@@ -278,6 +278,9 @@ export async function callLLM(config, systemPrompt, userMessage, opts = {}) {
278
278
  /**
279
279
  * Call an agent by name — loads the agent file, calls LLM, returns response.
280
280
  * No streaming. Used by PAO pipeline for batch agent calls.
281
+ *
282
+ * Automatically injects relevant episodic memories into the prompt
283
+ * and extracts key facts from the response for future retrieval.
281
284
  */
282
285
  export async function callAgent(config, agentName, userMessage, opts = {}) {
283
286
  const { AGENTS_DIR } = await import('../constants.mjs');
@@ -291,7 +294,25 @@ export async function callAgent(config, agentName, userMessage, opts = {}) {
291
294
  const { systemPrompt } = parseAgentFile(source, agentName);
292
295
  if (!systemPrompt) throw new Error(`Agent ${agentName} has no SYSTEM_PROMPT`);
293
296
 
294
- return callLLM(config, systemPrompt, userMessage, opts);
297
+ // ── Episodic Memory: inject relevant past interactions ──────────────────
298
+ let enrichedSystemPrompt = systemPrompt;
299
+ try {
300
+ const { buildMemoryContext } = await import('./memory.mjs');
301
+ const memoryContext = buildMemoryContext(agentName, userMessage);
302
+ if (memoryContext) {
303
+ enrichedSystemPrompt = systemPrompt + memoryContext;
304
+ }
305
+ } catch { /* memory unavailable — proceed without it */ }
306
+
307
+ const response = await callLLM(config, enrichedSystemPrompt, userMessage, opts);
308
+
309
+ // ── Episodic Memory: extract key facts from the interaction ─────────────
310
+ try {
311
+ const { extractMemory } = await import('./memory.mjs');
312
+ extractMemory(agentName, userMessage, response);
313
+ } catch { /* memory extraction failed — non-critical */ }
314
+
315
+ return response;
295
316
  }
296
317
 
297
318
  // ── Agent File Parser (shared) ─────────────────────────────────────────────