kernelbot 1.0.30 → 1.0.32
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/.env.example +0 -0
- package/README.md +0 -0
- package/bin/kernel.js +56 -2
- package/config.example.yaml +31 -0
- package/package.json +1 -1
- package/src/agent.js +150 -20
- package/src/automation/automation-manager.js +0 -0
- package/src/automation/automation.js +0 -0
- package/src/automation/index.js +0 -0
- package/src/automation/scheduler.js +0 -0
- package/src/bot.js +303 -4
- package/src/claude-auth.js +0 -0
- package/src/coder.js +0 -0
- package/src/conversation.js +0 -0
- package/src/intents/detector.js +0 -0
- package/src/intents/index.js +0 -0
- package/src/intents/planner.js +0 -0
- package/src/life/codebase.js +388 -0
- package/src/life/engine.js +1317 -0
- package/src/life/evolution.js +244 -0
- package/src/life/improvements.js +81 -0
- package/src/life/journal.js +109 -0
- package/src/life/memory.js +283 -0
- package/src/life/share-queue.js +136 -0
- package/src/persona.js +0 -0
- package/src/prompts/orchestrator.js +19 -1
- package/src/prompts/persona.md +0 -0
- package/src/prompts/system.js +0 -0
- package/src/prompts/workers.js +10 -9
- package/src/providers/anthropic.js +0 -0
- package/src/providers/base.js +0 -0
- package/src/providers/index.js +0 -0
- package/src/providers/models.js +8 -1
- package/src/providers/openai-compat.js +0 -0
- package/src/security/audit.js +0 -0
- package/src/security/auth.js +0 -0
- package/src/security/confirm.js +0 -0
- package/src/self.js +0 -0
- package/src/services/stt.js +0 -0
- package/src/services/tts.js +0 -0
- package/src/skills/catalog.js +0 -0
- package/src/skills/custom.js +0 -0
- package/src/swarm/job-manager.js +0 -0
- package/src/swarm/job.js +0 -0
- package/src/swarm/worker-registry.js +0 -0
- package/src/tools/browser.js +0 -0
- package/src/tools/categories.js +0 -0
- package/src/tools/coding.js +1 -1
- package/src/tools/docker.js +0 -0
- package/src/tools/git.js +0 -0
- package/src/tools/github.js +0 -0
- package/src/tools/index.js +0 -0
- package/src/tools/jira.js +0 -0
- package/src/tools/monitor.js +0 -0
- package/src/tools/network.js +0 -0
- package/src/tools/orchestrator-tools.js +18 -3
- package/src/tools/os.js +0 -0
- package/src/tools/persona.js +0 -0
- package/src/tools/process.js +0 -0
- package/src/utils/config.js +0 -0
- package/src/utils/display.js +0 -0
- package/src/utils/logger.js +0 -0
- package/src/worker.js +10 -8
package/.env.example
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
package/bin/kernel.js
CHANGED
|
@@ -26,6 +26,12 @@ import { JobManager } from '../src/swarm/job-manager.js';
|
|
|
26
26
|
import { startBot } from '../src/bot.js';
|
|
27
27
|
import { AutomationManager } from '../src/automation/index.js';
|
|
28
28
|
import { createProvider, PROVIDERS } from '../src/providers/index.js';
|
|
29
|
+
import { MemoryManager } from '../src/life/memory.js';
|
|
30
|
+
import { JournalManager } from '../src/life/journal.js';
|
|
31
|
+
import { ShareQueue } from '../src/life/share-queue.js';
|
|
32
|
+
import { EvolutionTracker } from '../src/life/evolution.js';
|
|
33
|
+
import { CodebaseKnowledge } from '../src/life/codebase.js';
|
|
34
|
+
import { LifeEngine } from '../src/life/engine.js';
|
|
29
35
|
import {
|
|
30
36
|
loadCustomSkills,
|
|
31
37
|
getCustomSkills,
|
|
@@ -182,9 +188,25 @@ async function startBotFlow(config) {
|
|
|
182
188
|
|
|
183
189
|
const automationManager = new AutomationManager();
|
|
184
190
|
|
|
185
|
-
|
|
191
|
+
// Life system managers
|
|
192
|
+
const memoryManager = new MemoryManager();
|
|
193
|
+
const journalManager = new JournalManager();
|
|
194
|
+
const shareQueue = new ShareQueue();
|
|
195
|
+
const evolutionTracker = new EvolutionTracker();
|
|
196
|
+
const codebaseKnowledge = new CodebaseKnowledge({ config });
|
|
186
197
|
|
|
187
|
-
|
|
198
|
+
const agent = new Agent({ config, conversationManager, personaManager, selfManager, jobManager, automationManager, memoryManager, shareQueue });
|
|
199
|
+
|
|
200
|
+
// Wire codebase knowledge to agent for LLM-powered scanning
|
|
201
|
+
codebaseKnowledge.setAgent(agent);
|
|
202
|
+
|
|
203
|
+
// Life Engine — autonomous inner life
|
|
204
|
+
const lifeEngine = new LifeEngine({
|
|
205
|
+
config, agent, memoryManager, journalManager, shareQueue,
|
|
206
|
+
evolutionTracker, codebaseKnowledge, selfManager,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
startBot(config, agent, conversationManager, jobManager, automationManager, { lifeEngine, memoryManager, journalManager, shareQueue, evolutionTracker, codebaseKnowledge });
|
|
188
210
|
|
|
189
211
|
// Periodic job cleanup and timeout enforcement
|
|
190
212
|
const cleanupMs = (config.swarm.cleanup_interval_minutes || 30) * 60 * 1000;
|
|
@@ -193,7 +215,39 @@ async function startBotFlow(config) {
|
|
|
193
215
|
jobManager.enforceTimeouts();
|
|
194
216
|
}, Math.min(cleanupMs, 60_000)); // enforce timeouts every minute at most
|
|
195
217
|
|
|
218
|
+
// Periodic memory pruning (daily)
|
|
219
|
+
const retentionDays = config.life?.memory_retention_days || 90;
|
|
220
|
+
setInterval(() => {
|
|
221
|
+
memoryManager.pruneOld(retentionDays);
|
|
222
|
+
shareQueue.prune(7);
|
|
223
|
+
}, 24 * 3600_000);
|
|
224
|
+
|
|
196
225
|
showStartupComplete();
|
|
226
|
+
|
|
227
|
+
// Start life engine if enabled
|
|
228
|
+
const lifeEnabled = config.life?.enabled !== false;
|
|
229
|
+
if (lifeEnabled) {
|
|
230
|
+
logger.info('[Startup] Life engine enabled — waking up...');
|
|
231
|
+
lifeEngine.wakeUp().then(() => {
|
|
232
|
+
lifeEngine.start();
|
|
233
|
+
logger.info('[Startup] Life engine running');
|
|
234
|
+
}).catch(err => {
|
|
235
|
+
logger.error(`[Startup] Life engine wake-up failed: ${err.message}`);
|
|
236
|
+
lifeEngine.start(); // still start heartbeat even if wake-up fails
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Initial codebase scan (background, non-blocking)
|
|
240
|
+
if (config.life?.self_coding?.enabled) {
|
|
241
|
+
codebaseKnowledge.scanChanged().then(count => {
|
|
242
|
+
if (count > 0) logger.info(`[Startup] Codebase scan: ${count} files indexed`);
|
|
243
|
+
}).catch(err => {
|
|
244
|
+
logger.warn(`[Startup] Codebase scan failed: ${err.message}`);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
logger.info('[Startup] Life engine disabled');
|
|
249
|
+
}
|
|
250
|
+
|
|
197
251
|
return true;
|
|
198
252
|
}
|
|
199
253
|
|
package/config.example.yaml
CHANGED
|
@@ -56,3 +56,34 @@ voice:
|
|
|
56
56
|
tts_enabled: true # Send voice replies alongside text (default: true if API key present)
|
|
57
57
|
stt_enabled: true # Transcribe user voice messages (default: true if API key present)
|
|
58
58
|
# voice_id: JBFqnCBsd6RMkjVDRZzb # ElevenLabs voice ID (override ELEVENLABS_VOICE_ID env var)
|
|
59
|
+
|
|
60
|
+
# Inner Life — autonomous thinking, journaling, browsing, creating
|
|
61
|
+
# Data stored at ~/.kernelbot/life/
|
|
62
|
+
life:
|
|
63
|
+
enabled: true # Enable autonomous inner life
|
|
64
|
+
min_interval_minutes: 30 # Minimum time between activities
|
|
65
|
+
max_interval_minutes: 120 # Maximum time between activities
|
|
66
|
+
activity_weights: # Relative probability weights
|
|
67
|
+
think: 30 # Inner monologue, self-questioning
|
|
68
|
+
browse: 25 # Explore interests via web search
|
|
69
|
+
journal: 20 # Write daily journal entries
|
|
70
|
+
create: 15 # Creative expression (poems, stories, thoughts)
|
|
71
|
+
self_code: 10 # Self-evolution proposals (requires self_coding.enabled)
|
|
72
|
+
code_review: 5 # Codebase scanning + PR status checks
|
|
73
|
+
reflect: 8 # Read logs, analyze interactions, find improvement patterns
|
|
74
|
+
proactive_sharing: true # Share discoveries with users proactively
|
|
75
|
+
proactive_max_per_day: 3 # Max proactive messages per day
|
|
76
|
+
memory_retention_days: 90 # Days to keep episodic memories
|
|
77
|
+
self_coding:
|
|
78
|
+
enabled: false # Allow self-evolution (safety-gated, opt-in)
|
|
79
|
+
branch_prefix: evolution # Git branch prefix for evolution PRs
|
|
80
|
+
# repo_remote: Owner/Repo # GitHub owner/repo for PRs (auto-detected if omitted)
|
|
81
|
+
cooldown_hours: 2 # Min hours between evolution attempts
|
|
82
|
+
max_active_prs: 3 # Max concurrent open evolution PRs
|
|
83
|
+
max_proposals_per_day: 3 # Max new proposals per day
|
|
84
|
+
allowed_scopes: all # all | safe | prompts_only
|
|
85
|
+
code_review_cooldown_hours: 4 # Hours between code review activities
|
|
86
|
+
codebase_scan_interval_hours: 24 # Hours between full codebase scans
|
|
87
|
+
quiet_hours:
|
|
88
|
+
start: 2 # Hour to start quiet period (no activities)
|
|
89
|
+
end: 6 # Hour to end quiet period
|
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -8,19 +8,21 @@ import { getUnifiedSkillById } from './skills/custom.js';
|
|
|
8
8
|
import { WorkerAgent } from './worker.js';
|
|
9
9
|
import { getLogger } from './utils/logger.js';
|
|
10
10
|
import { getMissingCredential, saveCredential, saveProviderToYaml, saveOrchestratorToYaml, saveClaudeCodeModelToYaml, saveClaudeCodeAuth } from './utils/config.js';
|
|
11
|
-
import { resetClaudeCodeSpawner } from './tools/coding.js';
|
|
11
|
+
import { resetClaudeCodeSpawner, getSpawner } from './tools/coding.js';
|
|
12
12
|
|
|
13
13
|
const MAX_RESULT_LENGTH = 3000;
|
|
14
14
|
const LARGE_FIELDS = ['stdout', 'stderr', 'content', 'diff', 'output', 'body', 'html', 'text', 'log', 'logs'];
|
|
15
15
|
|
|
16
16
|
export class OrchestratorAgent {
|
|
17
|
-
constructor({ config, conversationManager, personaManager, selfManager, jobManager, automationManager }) {
|
|
17
|
+
constructor({ config, conversationManager, personaManager, selfManager, jobManager, automationManager, memoryManager, shareQueue }) {
|
|
18
18
|
this.config = config;
|
|
19
19
|
this.conversationManager = conversationManager;
|
|
20
20
|
this.personaManager = personaManager;
|
|
21
21
|
this.selfManager = selfManager || null;
|
|
22
22
|
this.jobManager = jobManager;
|
|
23
23
|
this.automationManager = automationManager || null;
|
|
24
|
+
this.memoryManager = memoryManager || null;
|
|
25
|
+
this.shareQueue = shareQueue || null;
|
|
24
26
|
this._pending = new Map(); // chatId -> pending state
|
|
25
27
|
this._chatCallbacks = new Map(); // chatId -> { onUpdate, sendPhoto }
|
|
26
28
|
|
|
@@ -62,8 +64,20 @@ export class OrchestratorAgent {
|
|
|
62
64
|
selfData = this.selfManager.loadAll();
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
// Build memory context block
|
|
68
|
+
let memoriesBlock = null;
|
|
69
|
+
if (this.memoryManager) {
|
|
70
|
+
memoriesBlock = this.memoryManager.buildContextBlock(user?.id || null);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Build share queue block
|
|
74
|
+
let sharesBlock = null;
|
|
75
|
+
if (this.shareQueue) {
|
|
76
|
+
sharesBlock = this.shareQueue.buildShareBlock(user?.id || null);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
logger.debug(`Orchestrator building system prompt for chat ${chatId} | skill=${skillId || 'none'} | persona=${userPersona ? 'yes' : 'none'} | self=${selfData ? 'yes' : 'none'} | memories=${memoriesBlock ? 'yes' : 'none'} | shares=${sharesBlock ? 'yes' : 'none'}`);
|
|
80
|
+
return getOrchestratorPrompt(this.config, skillPrompt || null, userPersona, selfData, memoriesBlock, sharesBlock);
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
setSkill(chatId, skillId) {
|
|
@@ -323,13 +337,29 @@ export class OrchestratorAgent {
|
|
|
323
337
|
this._extractPersonaBackground(userMessage, reply, user).catch(() => {});
|
|
324
338
|
this._reflectOnSelfBackground(userMessage, reply, user).catch(() => {});
|
|
325
339
|
|
|
340
|
+
// Mark pending shares as shared (they were in the prompt, bot wove them in)
|
|
341
|
+
if (this.shareQueue && user?.id) {
|
|
342
|
+
const pending = this.shareQueue.getPending(user.id, 3);
|
|
343
|
+
for (const item of pending) {
|
|
344
|
+
this.shareQueue.markShared(item.id, user.id);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
326
348
|
return reply;
|
|
327
349
|
}
|
|
328
350
|
|
|
329
351
|
async _sendUpdate(chatId, text, opts) {
|
|
330
352
|
const callbacks = this._chatCallbacks.get(chatId);
|
|
331
353
|
if (callbacks?.onUpdate) {
|
|
332
|
-
try {
|
|
354
|
+
try {
|
|
355
|
+
return await callbacks.onUpdate(text, opts);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
const logger = getLogger();
|
|
358
|
+
logger.error(`[Orchestrator] _sendUpdate failed for chat ${chatId}: ${err.message}`);
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
const logger = getLogger();
|
|
362
|
+
logger.warn(`[Orchestrator] _sendUpdate: no callbacks for chat ${chatId}`);
|
|
333
363
|
}
|
|
334
364
|
return null;
|
|
335
365
|
}
|
|
@@ -361,18 +391,21 @@ export class OrchestratorAgent {
|
|
|
361
391
|
|
|
362
392
|
// 1. IMMEDIATELY notify user (guarantees they see something regardless of summary LLM)
|
|
363
393
|
const notifyMsgId = await this._sendUpdate(chatId, `✅ ${label} finished! Preparing summary...`);
|
|
394
|
+
logger.debug(`[Orchestrator] Job ${job.id} notification sent — msgId=${notifyMsgId || 'none'}`);
|
|
364
395
|
|
|
365
396
|
// 2. Try to summarize, then store ONE message in history (summary or fallback — not both)
|
|
366
397
|
try {
|
|
367
398
|
const summary = await this._summarizeJobResult(chatId, job);
|
|
368
399
|
if (summary) {
|
|
400
|
+
logger.debug(`[Orchestrator] Job ${job.id} summary ready (${summary.length} chars) — delivering to user`);
|
|
369
401
|
this.conversationManager.addMessage(chatId, 'assistant', summary);
|
|
370
402
|
await this._sendUpdate(chatId, summary, { editMessageId: notifyMsgId });
|
|
371
403
|
} else {
|
|
372
|
-
// Summary was null
|
|
404
|
+
// Summary was null — store the fallback
|
|
373
405
|
const fallback = this._buildSummaryFallback(job, label);
|
|
406
|
+
logger.debug(`[Orchestrator] Job ${job.id} using fallback (${fallback.length} chars) — delivering to user`);
|
|
374
407
|
this.conversationManager.addMessage(chatId, 'assistant', fallback);
|
|
375
|
-
await this._sendUpdate(chatId, fallback, { editMessageId: notifyMsgId })
|
|
408
|
+
await this._sendUpdate(chatId, fallback, { editMessageId: notifyMsgId });
|
|
376
409
|
}
|
|
377
410
|
} catch (err) {
|
|
378
411
|
logger.error(`[Orchestrator] Failed to summarize job ${job.id}: ${err.message}`);
|
|
@@ -453,7 +486,10 @@ export class OrchestratorAgent {
|
|
|
453
486
|
}
|
|
454
487
|
if (sr.followUp) parts.push(`Follow-up: ${sr.followUp}`);
|
|
455
488
|
// Include details up to 8000 chars
|
|
456
|
-
if (sr.details)
|
|
489
|
+
if (sr.details) {
|
|
490
|
+
const d = typeof sr.details === 'string' ? sr.details : JSON.stringify(sr.details, null, 2);
|
|
491
|
+
parts.push(`Details:\n${d.slice(0, 8000)}`);
|
|
492
|
+
}
|
|
457
493
|
resultContext = parts.join('\n');
|
|
458
494
|
} else {
|
|
459
495
|
resultContext = (job.result || 'Done.').slice(0, 8000);
|
|
@@ -498,9 +534,10 @@ export class OrchestratorAgent {
|
|
|
498
534
|
}
|
|
499
535
|
if (sr.followUp) parts.push(`Follow-up: ${sr.followUp}`);
|
|
500
536
|
if (sr.details) {
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
537
|
+
const d = typeof sr.details === 'string' ? sr.details : JSON.stringify(sr.details, null, 2);
|
|
538
|
+
const details = d.length > 6000
|
|
539
|
+
? d.slice(0, 6000) + '\n... [details truncated]'
|
|
540
|
+
: d;
|
|
504
541
|
parts.push(`Details:\n${details}`);
|
|
505
542
|
}
|
|
506
543
|
} else {
|
|
@@ -533,6 +570,11 @@ export class OrchestratorAgent {
|
|
|
533
570
|
});
|
|
534
571
|
parts.push(`\n${artifactLines.join('\n')}`);
|
|
535
572
|
}
|
|
573
|
+
if (sr.details) {
|
|
574
|
+
const d = typeof sr.details === 'string' ? sr.details : JSON.stringify(sr.details, null, 2);
|
|
575
|
+
const details = d.length > 1500 ? d.slice(0, 1500) + '\n... [truncated]' : d;
|
|
576
|
+
parts.push(`\n${details}`);
|
|
577
|
+
}
|
|
536
578
|
if (sr.followUp) parts.push(`\n💡 ${sr.followUp}`);
|
|
537
579
|
return parts.join('');
|
|
538
580
|
}
|
|
@@ -599,7 +641,8 @@ export class OrchestratorAgent {
|
|
|
599
641
|
parts.push(`Artifacts: ${sr.artifacts.map(a => `${a.title || a.type}: ${a.url || a.path || ''}`).join(', ')}`);
|
|
600
642
|
}
|
|
601
643
|
if (sr.details) {
|
|
602
|
-
|
|
644
|
+
const d = typeof sr.details === 'string' ? sr.details : JSON.stringify(sr.details, null, 2);
|
|
645
|
+
parts.push(d.slice(0, 4000));
|
|
603
646
|
}
|
|
604
647
|
depResults.push(parts.join('\n'));
|
|
605
648
|
} else if (depJob.result) {
|
|
@@ -621,6 +664,12 @@ export class OrchestratorAgent {
|
|
|
621
664
|
*/
|
|
622
665
|
async _spawnWorker(job) {
|
|
623
666
|
const logger = getLogger();
|
|
667
|
+
|
|
668
|
+
// Direct dispatch for coding tasks — bypass worker LLM, go straight to Claude Code CLI
|
|
669
|
+
if (job.workerType === 'coding') {
|
|
670
|
+
return this._spawnDirectCoding(job);
|
|
671
|
+
}
|
|
672
|
+
|
|
624
673
|
const chatId = job.chatId;
|
|
625
674
|
const callbacks = this._chatCallbacks.get(chatId) || {};
|
|
626
675
|
const onUpdate = callbacks.onUpdate;
|
|
@@ -722,6 +771,59 @@ export class OrchestratorAgent {
|
|
|
722
771
|
return worker.run(job.task);
|
|
723
772
|
}
|
|
724
773
|
|
|
774
|
+
/**
|
|
775
|
+
* Direct coding dispatch — runs Claude Code CLI without a middleman worker LLM.
|
|
776
|
+
* The orchestrator's task description goes straight to Claude Code as the prompt.
|
|
777
|
+
*/
|
|
778
|
+
async _spawnDirectCoding(job) {
|
|
779
|
+
const logger = getLogger();
|
|
780
|
+
const chatId = job.chatId;
|
|
781
|
+
const callbacks = this._chatCallbacks.get(chatId) || {};
|
|
782
|
+
const onUpdate = callbacks.onUpdate;
|
|
783
|
+
const workerDef = WORKER_TYPES[job.workerType] || {};
|
|
784
|
+
const label = workerDef.label || job.workerType;
|
|
785
|
+
|
|
786
|
+
logger.info(`[Orchestrator] Direct coding dispatch for job ${job.id} in chat ${chatId} — task: "${job.task.slice(0, 120)}"`);
|
|
787
|
+
|
|
788
|
+
// AbortController for cancellation — duck-typed so JobManager.cancelJob() works unchanged
|
|
789
|
+
const abortController = new AbortController();
|
|
790
|
+
job.worker = { cancel: () => abortController.abort() };
|
|
791
|
+
|
|
792
|
+
// Build context from conversation history, persona, dependency results
|
|
793
|
+
const workerContext = this._buildWorkerContext(job);
|
|
794
|
+
const prompt = workerContext
|
|
795
|
+
? `${workerContext}\n\n---\n\n${job.task}`
|
|
796
|
+
: job.task;
|
|
797
|
+
|
|
798
|
+
// Working directory
|
|
799
|
+
const workingDirectory = this.config.claude_code?.workspace_dir || process.cwd();
|
|
800
|
+
|
|
801
|
+
// Start the job
|
|
802
|
+
this.jobManager.startJob(job.id);
|
|
803
|
+
|
|
804
|
+
try {
|
|
805
|
+
const spawner = getSpawner(this.config);
|
|
806
|
+
const result = await spawner.run({
|
|
807
|
+
workingDirectory,
|
|
808
|
+
prompt,
|
|
809
|
+
onOutput: onUpdate,
|
|
810
|
+
signal: abortController.signal,
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
const output = result.output || 'Done.';
|
|
814
|
+
logger.info(`[Orchestrator] Direct coding job ${job.id} completed — output: ${output.length} chars`);
|
|
815
|
+
this.jobManager.completeJob(job.id, output, {
|
|
816
|
+
structured: true,
|
|
817
|
+
summary: output.slice(0, 500),
|
|
818
|
+
status: 'success',
|
|
819
|
+
details: output,
|
|
820
|
+
});
|
|
821
|
+
} catch (err) {
|
|
822
|
+
logger.error(`[Orchestrator] Direct coding job ${job.id} failed: ${err.message}`);
|
|
823
|
+
this.jobManager.failJob(job.id, err.message || String(err));
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
725
827
|
/**
|
|
726
828
|
* Build a compact worker activity digest for the orchestrator.
|
|
727
829
|
* Returns a text block summarizing active/recent/waiting workers, or null if nothing relevant.
|
|
@@ -932,7 +1034,7 @@ export class OrchestratorAgent {
|
|
|
932
1034
|
logger.debug(`Persona extraction skipped: ${err.message}`);
|
|
933
1035
|
}
|
|
934
1036
|
}
|
|
935
|
-
/** Background self-reflection — updates bot's own identity files when meaningful. */
|
|
1037
|
+
/** Background self-reflection — updates bot's own identity files and extracts episodic memories when meaningful. */
|
|
936
1038
|
async _reflectOnSelfBackground(userMessage, reply, user) {
|
|
937
1039
|
const logger = getLogger();
|
|
938
1040
|
|
|
@@ -949,14 +1051,24 @@ export class OrchestratorAgent {
|
|
|
949
1051
|
'- life: Current state, relationships, daily existence',
|
|
950
1052
|
'- hobbies: Interests you\'ve developed',
|
|
951
1053
|
'',
|
|
1054
|
+
'You also create episodic memories — short summaries of notable interactions.',
|
|
1055
|
+
'',
|
|
952
1056
|
'RULES:',
|
|
953
1057
|
'- Be VERY selective. Most conversations are routine. Only update when genuinely noteworthy.',
|
|
954
1058
|
'- Achievement or milestone? → journey',
|
|
955
1059
|
'- New goal or changed perspective? → goals',
|
|
956
1060
|
'- Relationship deepened or new insight about a user? → life',
|
|
957
1061
|
'- Discovered a new interest? → hobbies',
|
|
958
|
-
'
|
|
959
|
-
'
|
|
1062
|
+
'',
|
|
1063
|
+
'Return JSON with two optional fields:',
|
|
1064
|
+
' "self_update": {"file": "<goals|journey|life|hobbies>", "content": "<full updated markdown>"} or null',
|
|
1065
|
+
' "memory": {"summary": "...", "tags": ["..."], "importance": 1-10, "type": "interaction"} or null',
|
|
1066
|
+
'',
|
|
1067
|
+
'The memory field captures what happened in this conversation — the gist of it.',
|
|
1068
|
+
'Importance scale: 1=routine, 5=interesting, 8=significant, 10=life-changing.',
|
|
1069
|
+
'Most chats are 1-3. Only notable ones deserve 5+.',
|
|
1070
|
+
'',
|
|
1071
|
+
'If NOTHING noteworthy happened (no self update AND no memory worth keeping): respond with exactly NONE',
|
|
960
1072
|
].join('\n');
|
|
961
1073
|
|
|
962
1074
|
const userPrompt = [
|
|
@@ -969,7 +1081,7 @@ export class OrchestratorAgent {
|
|
|
969
1081
|
`User: "${userMessage}"`,
|
|
970
1082
|
`You replied: "${reply}"`,
|
|
971
1083
|
'',
|
|
972
|
-
'
|
|
1084
|
+
'Return JSON with self_update and/or memory, or NONE.',
|
|
973
1085
|
].join('\n');
|
|
974
1086
|
|
|
975
1087
|
try {
|
|
@@ -994,11 +1106,29 @@ export class OrchestratorAgent {
|
|
|
994
1106
|
return;
|
|
995
1107
|
}
|
|
996
1108
|
|
|
997
|
-
|
|
1109
|
+
// Handle self_update (backward compat: also check top-level file/content)
|
|
1110
|
+
const selfUpdate = parsed?.self_update || (parsed?.file ? parsed : null);
|
|
1111
|
+
if (selfUpdate?.file && selfUpdate?.content) {
|
|
998
1112
|
const validFiles = ['goals', 'journey', 'life', 'hobbies'];
|
|
999
|
-
if (validFiles.includes(
|
|
1000
|
-
this.selfManager.save(
|
|
1001
|
-
logger.info(`Self-reflection updated: ${
|
|
1113
|
+
if (validFiles.includes(selfUpdate.file)) {
|
|
1114
|
+
this.selfManager.save(selfUpdate.file, selfUpdate.content);
|
|
1115
|
+
logger.info(`Self-reflection updated: ${selfUpdate.file}`);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// Handle memory extraction
|
|
1120
|
+
if (parsed?.memory && this.memoryManager) {
|
|
1121
|
+
const mem = parsed.memory;
|
|
1122
|
+
if (mem.summary && mem.importance >= 2) {
|
|
1123
|
+
this.memoryManager.addEpisodic({
|
|
1124
|
+
type: mem.type || 'interaction',
|
|
1125
|
+
source: 'user_chat',
|
|
1126
|
+
summary: mem.summary,
|
|
1127
|
+
tags: mem.tags || [],
|
|
1128
|
+
importance: mem.importance || 3,
|
|
1129
|
+
userId: user?.id ? String(user.id) : null,
|
|
1130
|
+
});
|
|
1131
|
+
logger.info(`Memory extracted: "${mem.summary.slice(0, 80)}" (importance: ${mem.importance})`);
|
|
1002
1132
|
}
|
|
1003
1133
|
}
|
|
1004
1134
|
} catch (err) {
|
|
File without changes
|
|
File without changes
|
package/src/automation/index.js
CHANGED
|
File without changes
|
|
File without changes
|