kiku-agent 1.2.0 → 1.3.1
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/README.md +459 -407
- package/package.json +32 -3
- package/server/agent/agentCore.js +635 -267
- package/server/agent/appPaths.js +2 -0
- package/server/agent/catalogEntityResolver.js +637 -0
- package/server/agent/citations.js +0 -21
- package/server/agent/clarificationAnalytics.js +1 -1
- package/server/agent/clarifyingQuestions.js +286 -216
- package/server/agent/cli.js +532 -73
- package/server/agent/commandFeatures.js +123 -24
- package/server/agent/commandRegistry.js +128 -62
- package/server/agent/commands.js +1410 -95
- package/server/agent/config.js +253 -163
- package/server/agent/contextCompactor.js +239 -25
- package/server/agent/dynamicCatalog.js +192 -0
- package/server/agent/envBootstrap.js +46 -0
- package/server/agent/envManager.js +317 -0
- package/server/agent/eventBus.js +3 -3
- package/server/agent/feedbackLearning.js +0 -8
- package/server/agent/gpuMetrics.js +188 -55
- package/server/agent/ingestionWorker.js +339 -0
- package/server/agent/ink/App.jsx +348 -0
- package/server/agent/ink/Panel.jsx +54 -0
- package/server/agent/ink/PromptInput.jsx +321 -0
- package/server/agent/ink/TaskPlanPanel.jsx +88 -0
- package/server/agent/ink/ThinkingPanel.jsx +68 -0
- package/server/agent/ink/ToolResult.jsx +133 -0
- package/server/agent/ink/main.jsx +195 -0
- package/server/agent/ink/useEventBus.js +218 -0
- package/server/agent/lanDiscovery.js +369 -0
- package/server/agent/llmClient.js +111 -39
- package/server/agent/lmStudioWakeup.js +144 -0
- package/server/agent/mcpClient.js +2 -2
- package/server/agent/memory/contextAllocator.js +214 -0
- package/server/agent/memory/hierarchicalStore.js +523 -0
- package/server/agent/memory/reflectionWorker.js +110 -0
- package/server/agent/memory.js +635 -494
- package/server/agent/modelConfig.js +487 -0
- package/server/agent/newsIntelligence.js +219 -0
- package/server/agent/newsWatch.js +85 -0
- package/server/agent/prompts.js +146 -0
- package/server/agent/publicTools.js +7 -0
- package/server/agent/queryDetails.js +62 -1
- package/server/agent/research.js +314 -35
- package/server/agent/routingInterceptor.js +77 -0
- package/server/agent/rumoredProjects.js +15 -0
- package/server/agent/sanitizer.js +117 -17
- package/server/agent/subagent.js +52 -9
- package/server/agent/suggestionTui.js +33 -13
- package/server/agent/suggestions.js +29 -13
- package/server/agent/telemetry/hardwareTiers.js +332 -0
- package/server/agent/telemetry/runtimeWatchdog.js +66 -0
- package/server/agent/telemetry/techPowerUpClient.js +682 -0
- package/server/agent/telemetry/techpowerup-mcp.json +22 -0
- package/server/agent/telemetry.js +0 -11
- package/server/agent/terminalImage.js +0 -1
- package/server/agent/textToolProtocol.js +190 -0
- package/server/agent/tokenTracker.js +220 -0
- package/server/agent/toolRouter.js +362 -216
- package/server/agent/tools.js +1155 -194
- package/server/agent/ui.js +144 -41
- package/server/agent/vramService.js +572 -0
- package/server/agent/webSearch.js +2279 -273
- package/server/agent/websearch/adapters/duckduckgo.js +97 -0
- package/server/agent/websearch/adapters/jina.js +77 -0
- package/server/agent/websearch/adapters/searxng.js +57 -0
- package/server/agent/websearch/core/canonical.js +89 -0
- package/server/agent/websearch/core/fusion.js +81 -0
- package/server/agent/websearch/core/types.js +29 -0
- package/server/agent/websearch/extract.js +605 -0
- package/server/agent/websearch/index.js +43 -0
- package/server/agent/websearch/orchestrator.js +266 -0
- package/server/agent/websearch/resilience/breaker.js +97 -0
- package/server/agent/websearch/resilience/cache.js +59 -0
- package/server/agent/websearch/resilience/limiter.js +93 -0
- package/server/agent/websearch/security/filter.js +36 -0
- package/server/agent/websearch/security/ssrf.js +289 -0
- package/server/agent/websearch/telemetry.js +71 -0
- package/server/agent/youtubeAuth.js +82 -73
- package/server/agent/youtubeTranscript.js +209 -0
- package/server/apiRouter.js +390 -190
- package/server/mcp/data/brands.js +5 -5
- package/server/mcp/data/discography.js +56 -45
- package/server/mcp/data/gallery.js +9 -0
- package/server/mcp/data/index.js +1 -0
- package/server/mcp/data/industryIndex.js +251 -0
- package/server/mcp/data/profile.js +5 -5
- package/server/mcp/data/timeline.js +2 -2
- package/server/mcp/data/works.js +2 -2
- package/server/mcp/handler.js +0 -2
- package/server/mcp/prompts.js +42 -2
- package/server/mcp/resources.js +5 -1
- package/server/mcp/security.js +25 -14
- package/server/mcp/server.js +92 -3
- package/server/mcp/tools.js +20 -11
- package/server/routes/agent.js +55 -0
- package/server/web-chat/core/connection.js +218 -194
- package/server/web-chat/core/conversationContext.js +1 -1
- package/server/web-chat/core/directCatalogEngine.js +190 -184
- package/server/web-chat/core/followUpEngine.js +88 -11
- package/server/web-chat/core/security.js +7 -4
- package/server/web-chat/router.js +54 -17
- package/server/web-chat/routes/bilibiliRoute.js +34 -0
- package/server/web-chat/routes/chatRoute.js +488 -397
- package/server/web-chat/routes/clarifyConfigRoute.js +1 -2
- package/server/web-chat/routes/exportRoute.js +2 -1
- package/server/web-chat/routes/lanDiscoveryRoute.js +90 -0
- package/server/web-chat/routes/newsRoute.js +36 -6
- package/server/web-chat/routes/providerRoute.js +87 -0
- package/server/web-chat/routes/staticRoute.js +61 -13
- package/server/web-chat/routes/statusRoute.js +23 -2
- package/server/web-chat/routes/vramRoute.js +22 -0
- package/server/web-chat/server.js +135 -106
- package/server/web-chat/ui/htmlBuilder.js +8 -8
- package/server/web-chat/ui/previews/modelSettings.preview.html +41 -0
- package/server/web-chat/ui/scripts/agentBubbleSystem.js +0 -5
- package/server/web-chat/ui/scripts/client.js +10526 -8397
- package/server/web-chat/ui/scripts/webchatI18n.js +190 -138
- package/server/web-chat/ui/styles/main.css +7471 -4246
- package/server/web-chat/ui/templates/body.html +305 -179
- package/src/data/brandsData.js +29 -6
- package/src/data/discographyData.js +918 -900
- package/src/data/galleryData.js +66 -0
- package/src/data/industryIndexData.js +292 -0
- package/src/data/musicVideoData.js +48 -5
- package/src/data/profileData.js +15 -1
- package/src/data/timelineData.js +4 -3
- package/src/data/worksData.js +536 -535
- package/src/lib/vram/estimator.js +464 -0
- package/src/lib/vram/modelRegistry.js +387 -0
- package/tokens.css +33 -2
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
|
|
6
|
+
export async function isLmStudioResponding(baseUrl = 'http://127.0.0.1:1234/v1', timeoutMs = 2500) {
|
|
7
|
+
try {
|
|
8
|
+
const root = baseUrl.replace(/\/v1\/?$/, '').replace(/\/+$/, '');
|
|
9
|
+
const res = await fetch(`${root}/v1/models`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
10
|
+
return res.ok;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function getLoadedModels(baseUrl = 'http://127.0.0.1:1234/v1', timeoutMs = 2500) {
|
|
17
|
+
try {
|
|
18
|
+
const root = baseUrl.replace(/\/v1\/?$/, '').replace(/\/+$/, '');
|
|
19
|
+
const res = await fetch(`${root}/v1/models`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
20
|
+
if (!res.ok) return [];
|
|
21
|
+
const data = await res.json();
|
|
22
|
+
return Array.isArray(data.data) ? data.data.map(m => m.id) : [];
|
|
23
|
+
} catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resolveLmsCli() {
|
|
29
|
+
const userHome = os.homedir();
|
|
30
|
+
const isWin = process.platform === 'win32';
|
|
31
|
+
const candidates = [
|
|
32
|
+
path.join(userHome, '.lmstudio', 'bin', isWin ? 'lms.exe' : 'lms'),
|
|
33
|
+
isWin ? 'lms.exe' : 'lms'
|
|
34
|
+
];
|
|
35
|
+
return candidates.find(c => {
|
|
36
|
+
try { return fs.existsSync(c); } catch { return false; }
|
|
37
|
+
}) || (isWin ? 'lms.exe' : 'lms');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function loadLmStudioModel(model, options = {}) {
|
|
41
|
+
const lmsCmd = resolveLmsCli();
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const args = ['load', model, '-y', '--identifier', model];
|
|
44
|
+
const child = spawn(lmsCmd, args, {
|
|
45
|
+
windowsHide: true,
|
|
46
|
+
stdio: 'pipe',
|
|
47
|
+
shell: process.platform === 'win32'
|
|
48
|
+
});
|
|
49
|
+
let output = '';
|
|
50
|
+
child.stdout?.on('data', d => { output += d.toString(); });
|
|
51
|
+
child.stderr?.on('data', d => { output += d.toString(); });
|
|
52
|
+
child.on('close', code => {
|
|
53
|
+
resolve({ ok: code === 0, output });
|
|
54
|
+
});
|
|
55
|
+
child.on('error', err => {
|
|
56
|
+
resolve({ ok: false, error: err.message });
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function wakeUpLmStudio(options = {}) {
|
|
62
|
+
const {
|
|
63
|
+
baseUrl = 'http://127.0.0.1:1234/v1',
|
|
64
|
+
model = 'google/gemma-4-e4b',
|
|
65
|
+
maxWaitMs = 35000
|
|
66
|
+
} = options;
|
|
67
|
+
|
|
68
|
+
const isLocal = baseUrl.includes('127.0.0.1') || baseUrl.includes('localhost');
|
|
69
|
+
if (!isLocal) {
|
|
70
|
+
return { ok: false, reason: 'Non-local endpoint, automatic wakeup skipped' };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
let serverUp = await isLmStudioResponding(baseUrl, 2000);
|
|
75
|
+
if (!serverUp) {
|
|
76
|
+
const userHome = os.homedir();
|
|
77
|
+
const possiblePaths = [
|
|
78
|
+
path.join(userHome, 'AppData', 'Local', 'Programs', 'LM Studio', 'LM Studio.exe'),
|
|
79
|
+
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'LM Studio', 'LM Studio.exe'),
|
|
80
|
+
'C:\\Program Files\\LM Studio\\LM Studio.exe'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const appExe = possiblePaths.find(p => p && fs.existsSync(p));
|
|
84
|
+
if (appExe) {
|
|
85
|
+
try {
|
|
86
|
+
const child = spawn(appExe, [], {
|
|
87
|
+
detached: true,
|
|
88
|
+
stdio: 'ignore',
|
|
89
|
+
windowsHide: false
|
|
90
|
+
});
|
|
91
|
+
child.unref();
|
|
92
|
+
} catch {}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const lmsCmd = resolveLmsCli();
|
|
97
|
+
const lmsChild = spawn(lmsCmd, ['server', 'start'], {
|
|
98
|
+
detached: true,
|
|
99
|
+
stdio: 'ignore',
|
|
100
|
+
windowsHide: true,
|
|
101
|
+
shell: process.platform === 'win32'
|
|
102
|
+
});
|
|
103
|
+
lmsChild.unref();
|
|
104
|
+
} catch {}
|
|
105
|
+
|
|
106
|
+
const start = Date.now();
|
|
107
|
+
while (Date.now() - start < maxWaitMs) {
|
|
108
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
109
|
+
if (await isLmStudioResponding(baseUrl, 2000)) {
|
|
110
|
+
serverUp = true;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!serverUp) {
|
|
117
|
+
return { ok: false, reason: 'Timed out waiting for LM Studio server' };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if (model) {
|
|
122
|
+
const loaded = await getLoadedModels(baseUrl);
|
|
123
|
+
if (!loaded.includes(model)) {
|
|
124
|
+
try {
|
|
125
|
+
process.stdout.write(`\n🌸 [Kiku Auto-Wakeup] LM Studio server active. Auto-loading model "${model}" into VRAM...\n`);
|
|
126
|
+
} catch {}
|
|
127
|
+
await loadLmStudioModel(model);
|
|
128
|
+
|
|
129
|
+
const start = Date.now();
|
|
130
|
+
while (Date.now() - start < maxWaitMs) {
|
|
131
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
132
|
+
const currentModels = await getLoadedModels(baseUrl);
|
|
133
|
+
if (currentModels.includes(model)) {
|
|
134
|
+
try {
|
|
135
|
+
process.stdout.write(`🌸 [Kiku Auto-Wakeup] Model "${model}" loaded and ready!\n\n`);
|
|
136
|
+
} catch {}
|
|
137
|
+
return { ok: true, awakened: true, modelLoaded: true };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return { ok: true, alreadyRunning: true };
|
|
144
|
+
}
|
|
@@ -185,7 +185,6 @@ export class McpClientManager {
|
|
|
185
185
|
let resources = serverConfig.resources || [];
|
|
186
186
|
let prompts = serverConfig.prompts || [];
|
|
187
187
|
|
|
188
|
-
// If tools not pre-provided, fetch once upon connection
|
|
189
188
|
if (!tools || tools.length === 0) {
|
|
190
189
|
try {
|
|
191
190
|
if (!this._isRateLimited(name)) {
|
|
@@ -570,7 +569,6 @@ export class McpClientManager {
|
|
|
570
569
|
if (cfg.enabled === false || cfg.disabled === true) continue;
|
|
571
570
|
if (this.servers.has(name)) continue;
|
|
572
571
|
|
|
573
|
-
// If tools are cached or it's a known Kiku remote MCP server, register lazily without network call on startup
|
|
574
572
|
let tools = cfg.tools;
|
|
575
573
|
if (!tools || tools.length === 0) {
|
|
576
574
|
if (name === 'kiku-mcp' || /jujingyi-kikuuu|vercel\.app/i.test(cfg.url || '')) {
|
|
@@ -641,3 +639,5 @@ export class McpClientManager {
|
|
|
641
639
|
return results;
|
|
642
640
|
}
|
|
643
641
|
}
|
|
642
|
+
|
|
643
|
+
export const mcpClientManager = new McpClientManager();
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
export function extractWordTokens(text) {
|
|
2
|
+
if (!text || typeof text !== 'string') return [];
|
|
3
|
+
return text
|
|
4
|
+
.toLowerCase()
|
|
5
|
+
.replace(/[^\p{L}\p{N}\s]+/gu, ' ')
|
|
6
|
+
.split(/\s+/)
|
|
7
|
+
.filter(t => t.length >= 2);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function buildTermFrequencyVector(tokens) {
|
|
11
|
+
const vec = {};
|
|
12
|
+
for (const t of tokens) {
|
|
13
|
+
vec[t] = (vec[t] || 0) + 1;
|
|
14
|
+
}
|
|
15
|
+
return vec;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function computeCosineSimilarity(vecA, vecB) {
|
|
19
|
+
let dotProduct = 0;
|
|
20
|
+
let normA = 0;
|
|
21
|
+
let normB = 0;
|
|
22
|
+
|
|
23
|
+
for (const [key, val] of Object.entries(vecA)) {
|
|
24
|
+
normA += val * val;
|
|
25
|
+
if (vecB[key]) {
|
|
26
|
+
dotProduct += val * vecB[key];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const val of Object.values(vecB)) {
|
|
31
|
+
normB += val * val;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (normA === 0 || normB === 0) return 0;
|
|
35
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class ContextAllocator {
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.store = options.store;
|
|
41
|
+
this.defaultMaxTokens = options.maxTokens || 2000;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
estimateTokens(text) {
|
|
45
|
+
if (!text) return 0;
|
|
46
|
+
|
|
47
|
+
return Math.ceil(text.length / 3.5);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
truncateToTokenLimit(text, tokenLimit) {
|
|
51
|
+
if (!text) return '';
|
|
52
|
+
const charLimit = Math.floor(tokenLimit * 3.5);
|
|
53
|
+
if (text.length <= charLimit) return text;
|
|
54
|
+
return text.slice(0, charLimit - 15) + '\n...[truncated]';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async allocateContext({ currentQuery = '', activeRunId = null, memory = null, maxTokens = null } = {}) {
|
|
59
|
+
const budgetTokens = maxTokens || this.defaultMaxTokens;
|
|
60
|
+
const quota = {
|
|
61
|
+
user: Math.floor(budgetTokens * 0.20),
|
|
62
|
+
run: Math.floor(budgetTokens * 0.40),
|
|
63
|
+
reflections: Math.floor(budgetTokens * 0.20),
|
|
64
|
+
general: Math.floor(budgetTokens * 0.20),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const sections = [];
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
const userParts = [];
|
|
71
|
+
if (this.store) {
|
|
72
|
+
const guidelineNodes = await this.store.listNodes('kiku/guidelines/');
|
|
73
|
+
for (const node of guidelineNodes) {
|
|
74
|
+
const domain = node.domain || 'general';
|
|
75
|
+
const content = typeof node.content === 'string' ? node.content : JSON.stringify(node.content);
|
|
76
|
+
userParts.push(`[Guideline: ${domain}]\n${content}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (memory?.preferences?.language) {
|
|
81
|
+
userParts.push(`User preferred language: ${memory.preferences.language}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const userSkillContext = memory?.buildUserSkillContext ? memory.buildUserSkillContext() : '';
|
|
85
|
+
if (userSkillContext) {
|
|
86
|
+
userParts.push(`User-taught skills:\n${userSkillContext}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const feedbackStyleContext = memory?.buildFeedbackStyleContext ? memory.buildFeedbackStyleContext() : '';
|
|
90
|
+
if (feedbackStyleContext) {
|
|
91
|
+
userParts.push(`Learned answer-style preferences:\n${feedbackStyleContext}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (userParts.length > 0) {
|
|
95
|
+
const userText = this.truncateToTokenLimit(userParts.join('\n\n'), quota.user);
|
|
96
|
+
sections.push(`--- USER GUIDELINES & PREFERENCES (20% Quota) ---\n${userText}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if (activeRunId && this.store) {
|
|
101
|
+
const runData = await this.store.loadActiveRun(activeRunId);
|
|
102
|
+
if (runData.exists) {
|
|
103
|
+
const runParts = [];
|
|
104
|
+
runParts.push(`Active Run ID: ${activeRunId}`);
|
|
105
|
+
|
|
106
|
+
if (runData.state) {
|
|
107
|
+
runParts.push(`Status: ${runData.state.status || 'IN_PROGRESS'}`);
|
|
108
|
+
if (runData.state.currentStep) {
|
|
109
|
+
runParts.push(`Current Step: ${runData.state.currentStep}`);
|
|
110
|
+
}
|
|
111
|
+
if (runData.state.lastSummary) {
|
|
112
|
+
runParts.push(`Latest Step Summary: ${runData.state.lastSummary}`);
|
|
113
|
+
}
|
|
114
|
+
if (runData.state.lastFailure) {
|
|
115
|
+
runParts.push(`Recent Failure Alert: ${runData.state.lastFailure.errorTrace} -> Recovery: ${runData.state.lastFailure.recoveryAction}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (runData.plan && Array.isArray(runData.plan.subtasks) && runData.plan.subtasks.length > 0) {
|
|
120
|
+
const checklist = runData.plan.subtasks.map(t => {
|
|
121
|
+
const marker = t.status === 'completed' ? '[x]' : (t.status === 'in_progress' ? '[>]' : (t.status === 'failed' ? '[!]' : '[ ]'));
|
|
122
|
+
const summaryText = t.summary ? ` - ${t.summary}` : '';
|
|
123
|
+
return `${marker} ${t.name} (${t.status})${summaryText}`;
|
|
124
|
+
}).join('\n');
|
|
125
|
+
runParts.push(`Execution Plan Checklist:\n${checklist}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (runData.artifacts && Array.isArray(runData.artifacts.items) && runData.artifacts.items.length > 0) {
|
|
129
|
+
const artifactList = runData.artifacts.items.map(a => `- ${a.name}: ${a.uri}`).join('\n');
|
|
130
|
+
runParts.push(`Artifact Pointers:\n${artifactList}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const runText = this.truncateToTokenLimit(runParts.join('\n\n'), quota.run);
|
|
134
|
+
sections.push(`--- ACTIVE TASK RUN STATE (40% Quota) ---\n${runText}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if (this.store) {
|
|
140
|
+
const reflectionNodes = await this.store.listNodes('kiku/reflections/');
|
|
141
|
+
if (reflectionNodes.length > 0) {
|
|
142
|
+
const queryTokens = extractWordTokens(currentQuery);
|
|
143
|
+
const queryVector = buildTermFrequencyVector(queryTokens);
|
|
144
|
+
|
|
145
|
+
const scored = reflectionNodes.map(node => {
|
|
146
|
+
const content = node.content || {};
|
|
147
|
+
const textToMatch = [
|
|
148
|
+
node.domain || '',
|
|
149
|
+
content.issuePattern || '',
|
|
150
|
+
content.errorTrace || '',
|
|
151
|
+
content.recoveryAction || '',
|
|
152
|
+
content.lesson || '',
|
|
153
|
+
content.summary || ''
|
|
154
|
+
].join(' ');
|
|
155
|
+
|
|
156
|
+
const nodeTokens = extractWordTokens(textToMatch);
|
|
157
|
+
const nodeVector = buildTermFrequencyVector(nodeTokens);
|
|
158
|
+
const similarity = computeCosineSimilarity(queryVector, nodeVector);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
node,
|
|
162
|
+
similarity,
|
|
163
|
+
confidence: Number(content.confidence) || 1.0,
|
|
164
|
+
score: similarity * (Number(content.confidence) || 1.0)
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
scored.sort((a, b) => b.score - a.score);
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
const positiveScored = scored.filter(s => s.score > 0.05).slice(0, 4);
|
|
172
|
+
const topReflections = positiveScored.length > 0 ? positiveScored : scored.slice(0, 2);
|
|
173
|
+
|
|
174
|
+
const refLines = topReflections.map(item => {
|
|
175
|
+
const c = item.node.content || {};
|
|
176
|
+
const title = item.node.domain ? `[Reflection: ${item.node.domain}]` : '[Reflection]';
|
|
177
|
+
const issue = c.issuePattern ? `Problem: ${c.issuePattern}` : (c.errorTrace ? `Error: ${c.errorTrace}` : '');
|
|
178
|
+
const solution = c.recoveryAction ? `Solution: ${c.recoveryAction}` : (c.lesson ? `Lesson: ${c.lesson}` : (c.summary || ''));
|
|
179
|
+
return `${title}\n${issue}\n${solution}`.trim();
|
|
180
|
+
}).filter(Boolean);
|
|
181
|
+
|
|
182
|
+
if (refLines.length > 0) {
|
|
183
|
+
const refText = this.truncateToTokenLimit(refLines.join('\n\n'), quota.reflections);
|
|
184
|
+
sections.push(`--- EPISODIC REFLECTIONS & GOTCHAS (20% Quota) ---\n${refText}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
const generalParts = [];
|
|
191
|
+
if (memory?.getRelevantFacts) {
|
|
192
|
+
const facts = currentQuery ? memory.getRelevantFacts(currentQuery, 6) : (memory.facts || []).slice(-6);
|
|
193
|
+
if (facts.length > 0) {
|
|
194
|
+
generalParts.push('Known facts:\n' + facts.map(f => `- ${f.content}`).join('\n'));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (memory?.getTopTopics) {
|
|
199
|
+
const topTopics = memory.getTopTopics(3);
|
|
200
|
+
if (topTopics.length > 0) {
|
|
201
|
+
generalParts.push('Frequent topics: ' + topTopics.map(t => `${t.topic} (${t.count}x)`).join(', '));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (generalParts.length > 0) {
|
|
206
|
+
const genText = this.truncateToTokenLimit(generalParts.join('\n\n'), quota.general);
|
|
207
|
+
sections.push(`--- GENERAL KNOWLEDGE & CONTEXT (20% Quota) ---\n${genText}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (sections.length === 0) return '';
|
|
211
|
+
|
|
212
|
+
return `\n\n=== STATEFUL & SELF-REFLECTIVE MEMORY CONTEXT ===\n${sections.join('\n\n')}\n=== END MEMORY CONTEXT ===`;
|
|
213
|
+
}
|
|
214
|
+
}
|