kiku-agent 1.0.0 → 1.0.4
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 +293 -122
- package/package.json +60 -61
- package/server/agent/agentCore.js +892 -782
- package/server/agent/appPaths.js +21 -0
- package/server/agent/audioPlayer.js +739 -673
- package/server/agent/cli.js +1414 -1111
- package/server/agent/commandRegistry.js +74 -58
- package/server/agent/commands.js +1694 -788
- package/server/agent/config.js +264 -90
- package/server/agent/contextCompactor.js +79 -0
- package/server/agent/doctor.js +69 -0
- package/server/agent/eventBus.js +107 -103
- package/server/agent/gpuMetrics.js +189 -0
- package/server/agent/llmClient.js +369 -200
- package/server/agent/mcpClient.js +364 -0
- package/server/agent/memory.js +670 -566
- package/server/agent/modelManager.js +77 -0
- package/server/agent/research.js +202 -0
- package/server/agent/session.js +244 -239
- package/server/agent/subagent.js +116 -0
- package/server/agent/theme.js +125 -124
- package/server/agent/toolRouter.js +201 -0
- package/server/agent/tools.js +1679 -1528
- package/server/agent/ui.js +760 -469
- package/server/agent/youtubeAuth.js +144 -65
- package/server/mcp/claude-desktop-config.example.json +1 -1
- package/server/mcp/data/discography.js +36 -36
- package/server/mcp/dataAdapter.js +102 -0
- package/server/mcp/examples/claude-code.mcp.example.json +19 -0
- package/server/mcp/examples/claude-desktop.example.json +11 -0
- package/server/mcp/examples/cline.example.json +13 -0
- package/server/mcp/examples/cursor.example.json +14 -0
- package/server/mcp/examples/gemini-cli.example.json +17 -0
- package/server/mcp/examples/opencode.example.json +21 -0
- package/server/mcp/examples/vscode.example.json +19 -0
- package/server/mcp/examples/windsurf.example.json +14 -0
- package/server/mcp/handler.js +85 -159
- package/server/mcp/security.js +140 -138
- package/server/mcp/server.js +129 -100
- package/server/mcp/stdio.js +4 -2
package/server/agent/eventBus.js
CHANGED
|
@@ -1,103 +1,107 @@
|
|
|
1
|
-
class EventBus {
|
|
2
|
-
constructor() {
|
|
3
|
-
this._listeners = new Map();
|
|
4
|
-
this._history = [];
|
|
5
|
-
this._maxHistory = 50;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
on(event, fn) {
|
|
9
|
-
if (!this._listeners.has(event)) this._listeners.set(event, new Set());
|
|
10
|
-
this._listeners.get(event).add(fn);
|
|
11
|
-
return () => this.off(event, fn);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
once(event, fn) {
|
|
15
|
-
const wrapper = (data, name) => {
|
|
16
|
-
this.off(event, wrapper);
|
|
17
|
-
fn(data, name);
|
|
18
|
-
};
|
|
19
|
-
return this.on(event, wrapper);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
off(event, fn) {
|
|
23
|
-
const set = this._listeners.get(event);
|
|
24
|
-
if (set) set.delete(fn);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
emit(event, data) {
|
|
28
|
-
this._history.push({ event, data, ts: Date.now() });
|
|
29
|
-
if (this._history.length > this._maxHistory) {
|
|
30
|
-
this._history.shift();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const exactSet = this._listeners.get(event);
|
|
34
|
-
if (exactSet) {
|
|
35
|
-
for (const fn of exactSet) {
|
|
36
|
-
try { fn(data, event); } catch { /* swallow */ }
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const ns = event.split(':')[0];
|
|
41
|
-
const wildcardEvent = ns + ':*';
|
|
42
|
-
const wildcardSet = this._listeners.get(wildcardEvent);
|
|
43
|
-
if (wildcardSet) {
|
|
44
|
-
for (const fn of wildcardSet) {
|
|
45
|
-
try { fn(data, event); } catch { /* swallow */ }
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const globalSet = this._listeners.get('*');
|
|
50
|
-
if (globalSet) {
|
|
51
|
-
for (const fn of globalSet) {
|
|
52
|
-
try { fn(data, event); } catch { /* swallow */ }
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
getHistory(limit = this._maxHistory) {
|
|
58
|
-
return this._history.slice(-limit);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
clear() {
|
|
62
|
-
this._listeners.clear();
|
|
63
|
-
this._history.length = 0;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export const eventBus = new EventBus();
|
|
68
|
-
|
|
69
|
-
export const Events = Object.freeze({
|
|
70
|
-
AGENT_QUERY_START: 'agent:query:start',
|
|
71
|
-
AGENT_QUERY_END: 'agent:query:end',
|
|
72
|
-
AGENT_QUERY_ERROR: 'agent:query:error',
|
|
73
|
-
AGENT_STATE_CHANGE: 'agent:state:change',
|
|
74
|
-
|
|
75
|
-
AGENT_REASONING_START: 'agent:reasoning:start',
|
|
76
|
-
AGENT_REASONING_DELTA: 'agent:reasoning:delta',
|
|
77
|
-
AGENT_REASONING_END: 'agent:reasoning:end',
|
|
78
|
-
AGENT_TOKEN_START: 'agent:token:start',
|
|
79
|
-
AGENT_TOKEN_DELTA: 'agent:token:delta',
|
|
80
|
-
AGENT_TOKEN_END: 'agent:token:end',
|
|
81
|
-
AGENT_FINAL_ANSWER: 'agent:final:answer',
|
|
82
|
-
|
|
83
|
-
AGENT_TOOL_START: 'agent:tool:start',
|
|
84
|
-
AGENT_TOOL_RESULT: 'agent:tool:result',
|
|
85
|
-
AGENT_TOOL_ERROR: 'agent:tool:error',
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
class EventBus {
|
|
2
|
+
constructor() {
|
|
3
|
+
this._listeners = new Map();
|
|
4
|
+
this._history = [];
|
|
5
|
+
this._maxHistory = 50;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
on(event, fn) {
|
|
9
|
+
if (!this._listeners.has(event)) this._listeners.set(event, new Set());
|
|
10
|
+
this._listeners.get(event).add(fn);
|
|
11
|
+
return () => this.off(event, fn);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
once(event, fn) {
|
|
15
|
+
const wrapper = (data, name) => {
|
|
16
|
+
this.off(event, wrapper);
|
|
17
|
+
fn(data, name);
|
|
18
|
+
};
|
|
19
|
+
return this.on(event, wrapper);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
off(event, fn) {
|
|
23
|
+
const set = this._listeners.get(event);
|
|
24
|
+
if (set) set.delete(fn);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
emit(event, data) {
|
|
28
|
+
this._history.push({ event, data, ts: Date.now() });
|
|
29
|
+
if (this._history.length > this._maxHistory) {
|
|
30
|
+
this._history.shift();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const exactSet = this._listeners.get(event);
|
|
34
|
+
if (exactSet) {
|
|
35
|
+
for (const fn of exactSet) {
|
|
36
|
+
try { fn(data, event); } catch { /* swallow */ }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ns = event.split(':')[0];
|
|
41
|
+
const wildcardEvent = ns + ':*';
|
|
42
|
+
const wildcardSet = this._listeners.get(wildcardEvent);
|
|
43
|
+
if (wildcardSet) {
|
|
44
|
+
for (const fn of wildcardSet) {
|
|
45
|
+
try { fn(data, event); } catch { /* swallow */ }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const globalSet = this._listeners.get('*');
|
|
50
|
+
if (globalSet) {
|
|
51
|
+
for (const fn of globalSet) {
|
|
52
|
+
try { fn(data, event); } catch { /* swallow */ }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getHistory(limit = this._maxHistory) {
|
|
58
|
+
return this._history.slice(-limit);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
clear() {
|
|
62
|
+
this._listeners.clear();
|
|
63
|
+
this._history.length = 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const eventBus = new EventBus();
|
|
68
|
+
|
|
69
|
+
export const Events = Object.freeze({
|
|
70
|
+
AGENT_QUERY_START: 'agent:query:start',
|
|
71
|
+
AGENT_QUERY_END: 'agent:query:end',
|
|
72
|
+
AGENT_QUERY_ERROR: 'agent:query:error',
|
|
73
|
+
AGENT_STATE_CHANGE: 'agent:state:change',
|
|
74
|
+
|
|
75
|
+
AGENT_REASONING_START: 'agent:reasoning:start',
|
|
76
|
+
AGENT_REASONING_DELTA: 'agent:reasoning:delta',
|
|
77
|
+
AGENT_REASONING_END: 'agent:reasoning:end',
|
|
78
|
+
AGENT_TOKEN_START: 'agent:token:start',
|
|
79
|
+
AGENT_TOKEN_DELTA: 'agent:token:delta',
|
|
80
|
+
AGENT_TOKEN_END: 'agent:token:end',
|
|
81
|
+
AGENT_FINAL_ANSWER: 'agent:final:answer',
|
|
82
|
+
|
|
83
|
+
AGENT_TOOL_START: 'agent:tool:start',
|
|
84
|
+
AGENT_TOOL_RESULT: 'agent:tool:result',
|
|
85
|
+
AGENT_TOOL_ERROR: 'agent:tool:error',
|
|
86
|
+
|
|
87
|
+
AGENT_PLAN_START: 'agent:plan:start',
|
|
88
|
+
AGENT_PLAN_STEP_UPDATE: 'agent:plan:step:update',
|
|
89
|
+
AGENT_PLAN_COMPLETE: 'agent:plan:complete',
|
|
90
|
+
|
|
91
|
+
MUSIC_PLAY: 'music:play',
|
|
92
|
+
MUSIC_STOP: 'music:stop',
|
|
93
|
+
MUSIC_QUEUE_CHANGE: 'music:queue:change',
|
|
94
|
+
MUSIC_STATE_CHANGE: 'music:state:change',
|
|
95
|
+
|
|
96
|
+
SESSION_SAVED: 'session:saved',
|
|
97
|
+
SESSION_ARCHIVED: 'session:archived',
|
|
98
|
+
MEMORY_FACT_ADDED: 'memory:fact:added',
|
|
99
|
+
|
|
100
|
+
UI_RENDER: 'ui:render',
|
|
101
|
+
UI_CLEAR: 'ui:clear',
|
|
102
|
+
UI_MESSAGE: 'ui:message',
|
|
103
|
+
|
|
104
|
+
QUEUE_ITEM_ADDED: 'queue:item:added',
|
|
105
|
+
QUEUE_ITEM_CONSUMED: 'queue:item:consumed',
|
|
106
|
+
QUEUE_CLEARED: 'queue:cleared',
|
|
107
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { C } from './ui.js';
|
|
3
|
+
|
|
4
|
+
function runProcessCapture(cmd, args, timeoutMs = 2500) {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
let stdout = '';
|
|
7
|
+
let stderr = '';
|
|
8
|
+
let settled = false;
|
|
9
|
+
|
|
10
|
+
const child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
11
|
+
|
|
12
|
+
const finish = (result) => {
|
|
13
|
+
if (settled) return;
|
|
14
|
+
settled = true;
|
|
15
|
+
clearTimeout(timer);
|
|
16
|
+
resolve(result);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const timer = setTimeout(() => {
|
|
20
|
+
try { child.kill('SIGKILL'); } catch {}
|
|
21
|
+
finish({ ok: false, stdout: '', stderr: 'timeout' });
|
|
22
|
+
}, timeoutMs);
|
|
23
|
+
|
|
24
|
+
child.stdout?.on('data', d => { stdout += d.toString(); });
|
|
25
|
+
child.stderr?.on('data', d => { stderr += d.toString(); });
|
|
26
|
+
|
|
27
|
+
child.on('error', (err) => finish({ ok: false, stdout: '', stderr: err.message }));
|
|
28
|
+
child.on('close', (code) => finish({ ok: code === 0, stdout: stdout.trim(), stderr: stderr.trim() }));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getGpuMetrics() {
|
|
33
|
+
|
|
34
|
+
const nv = await runProcessCapture('nvidia-smi', [
|
|
35
|
+
'--query-gpu=name,memory.total,memory.used,memory.free,utilization.gpu,temperature.gpu,driver_version',
|
|
36
|
+
'--format=csv,noheader,nounits'
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
if (nv.ok && nv.stdout) {
|
|
40
|
+
const lines = nv.stdout.split('\n').map(l => l.trim()).filter(Boolean);
|
|
41
|
+
const gpus = lines.map((line, idx) => {
|
|
42
|
+
const [name, total, used, free, util, temp, driver] = line.split(',').map(s => s?.trim());
|
|
43
|
+
const totalMb = parseFloat(total) || 0;
|
|
44
|
+
const usedMb = parseFloat(used) || 0;
|
|
45
|
+
const freeMb = parseFloat(free) || 0;
|
|
46
|
+
const utilPercent = parseFloat(util) || 0;
|
|
47
|
+
const tempC = parseFloat(temp) || 0;
|
|
48
|
+
const usedPct = totalMb > 0 ? Number(((usedMb / totalMb) * 100).toFixed(1)) : 0;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
index: idx,
|
|
52
|
+
name: name || 'NVIDIA GPU',
|
|
53
|
+
vendor: 'NVIDIA',
|
|
54
|
+
totalMb,
|
|
55
|
+
usedMb,
|
|
56
|
+
freeMb,
|
|
57
|
+
totalGb: (totalMb / 1024).toFixed(1),
|
|
58
|
+
usedGb: (usedMb / 1024).toFixed(1),
|
|
59
|
+
freeGb: (freeMb / 1024).toFixed(1),
|
|
60
|
+
usedPercent: usedPct,
|
|
61
|
+
utilizationGpu: utilPercent,
|
|
62
|
+
temperatureC: tempC,
|
|
63
|
+
driverVersion: driver || 'N/A',
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (gpus.length > 0) {
|
|
68
|
+
return {
|
|
69
|
+
available: true,
|
|
70
|
+
vendor: 'NVIDIA',
|
|
71
|
+
count: gpus.length,
|
|
72
|
+
primary: gpus[0],
|
|
73
|
+
gpus,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if (process.platform === 'win32') {
|
|
80
|
+
const ps = await runProcessCapture('powershell', [
|
|
81
|
+
'-NoProfile',
|
|
82
|
+
'-NonInteractive',
|
|
83
|
+
'-Command',
|
|
84
|
+
'Get-CimInstance Win32_VideoController | Select-Object Name, AdapterRAM | ConvertTo-Json'
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
if (ps.ok && ps.stdout) {
|
|
88
|
+
try {
|
|
89
|
+
let data = JSON.parse(ps.stdout);
|
|
90
|
+
if (!Array.isArray(data)) data = [data];
|
|
91
|
+
const gpus = data.filter(item => item?.Name).map((item, idx) => {
|
|
92
|
+
const ramBytes = item.AdapterRAM || 0;
|
|
93
|
+
const totalMb = Math.round(ramBytes / (1024 * 1024));
|
|
94
|
+
const name = String(item.Name);
|
|
95
|
+
const vendor = name.includes('NVIDIA')
|
|
96
|
+
? 'NVIDIA'
|
|
97
|
+
: name.includes('AMD') || name.includes('Radeon')
|
|
98
|
+
? 'AMD'
|
|
99
|
+
: name.includes('Intel')
|
|
100
|
+
? 'Intel'
|
|
101
|
+
: 'Generic';
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
index: idx,
|
|
105
|
+
name,
|
|
106
|
+
vendor,
|
|
107
|
+
totalMb,
|
|
108
|
+
usedMb: null,
|
|
109
|
+
freeMb: null,
|
|
110
|
+
totalGb: (totalMb / 1024).toFixed(1),
|
|
111
|
+
usedGb: null,
|
|
112
|
+
freeGb: null,
|
|
113
|
+
usedPercent: null,
|
|
114
|
+
utilizationGpu: null,
|
|
115
|
+
temperatureC: null,
|
|
116
|
+
driverVersion: 'N/A',
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (gpus.length > 0) {
|
|
121
|
+
return {
|
|
122
|
+
available: true,
|
|
123
|
+
vendor: gpus[0].vendor,
|
|
124
|
+
count: gpus.length,
|
|
125
|
+
primary: gpus[0],
|
|
126
|
+
gpus,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
} catch {}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
if (process.platform === 'darwin') {
|
|
135
|
+
const mac = await runProcessCapture('system_profiler', ['SPDisplaysDataType', '-json']);
|
|
136
|
+
if (mac.ok && mac.stdout) {
|
|
137
|
+
try {
|
|
138
|
+
const data = JSON.parse(mac.stdout);
|
|
139
|
+
const displays = data.SPDisplaysDataType || [];
|
|
140
|
+
if (displays.length > 0) {
|
|
141
|
+
const first = displays[0];
|
|
142
|
+
return {
|
|
143
|
+
available: true,
|
|
144
|
+
vendor: 'Apple',
|
|
145
|
+
count: displays.length,
|
|
146
|
+
primary: {
|
|
147
|
+
index: 0,
|
|
148
|
+
name: first.sppci_model || first._name || 'Apple GPU',
|
|
149
|
+
vendor: 'Apple',
|
|
150
|
+
totalMb: 0,
|
|
151
|
+
usedMb: null,
|
|
152
|
+
freeMb: null,
|
|
153
|
+
totalGb: 'Unified',
|
|
154
|
+
usedGb: null,
|
|
155
|
+
freeGb: null,
|
|
156
|
+
usedPercent: null,
|
|
157
|
+
utilizationGpu: null,
|
|
158
|
+
temperatureC: null,
|
|
159
|
+
driverVersion: 'Metal',
|
|
160
|
+
},
|
|
161
|
+
gpus: displays.map((d, i) => ({
|
|
162
|
+
index: i,
|
|
163
|
+
name: d.sppci_model || d._name || 'Apple GPU',
|
|
164
|
+
vendor: 'Apple',
|
|
165
|
+
})),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
} catch {}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
available: false,
|
|
174
|
+
message: 'No GPU hardware acceleration metrics available',
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function formatProgressBar(percent = 0, width = 16) {
|
|
179
|
+
const safePct = Math.max(0, Math.min(100, Number(percent) || 0));
|
|
180
|
+
const filled = Math.round((safePct / 100) * width);
|
|
181
|
+
const empty = Math.max(0, width - filled);
|
|
182
|
+
|
|
183
|
+
let barColor = C.emerald;
|
|
184
|
+
if (safePct >= 85) barColor = C.roseGlow;
|
|
185
|
+
else if (safePct >= 60) barColor = C.gold;
|
|
186
|
+
|
|
187
|
+
const bar = `${barColor}${'█'.repeat(filled)}${C.dim}${'░'.repeat(empty)}${C.reset}`;
|
|
188
|
+
return `[${bar}] ${barColor}${safePct.toFixed(1)}%${C.reset}`;
|
|
189
|
+
}
|