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.
- package/package.json +10 -3
- package/src/cli.mjs +111 -3
- package/src/commands/autostart.mjs +342 -0
- package/src/commands/chat.mjs +12 -2
- package/src/commands/ops.mjs +37 -0
- package/src/commands/ui.mjs +22 -5
- package/src/config.mjs +38 -0
- package/src/constants.mjs +8 -1
- package/src/services/llm.mjs +22 -1
- package/src/services/memory.mjs +627 -0
- package/src/services/message-responder.mjs +778 -0
- package/src/services/ops-daemon.mjs +463 -8
- package/src/services/tool-executor.mjs +392 -0
package/src/commands/ui.mjs
CHANGED
|
@@ -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
|
|
549
|
+
// Build message with history (merge persisted + request history)
|
|
550
|
+
const requestHistory = body.history || [];
|
|
550
551
|
const parts = [];
|
|
551
|
-
for (const turn of
|
|
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
|
|
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,
|
|
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
|
-
|
|
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',
|
package/src/services/llm.mjs
CHANGED
|
@@ -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
|
-
|
|
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) ─────────────────────────────────────────────
|