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.
Files changed (40) hide show
  1. package/README.md +293 -122
  2. package/package.json +60 -61
  3. package/server/agent/agentCore.js +892 -782
  4. package/server/agent/appPaths.js +21 -0
  5. package/server/agent/audioPlayer.js +739 -673
  6. package/server/agent/cli.js +1414 -1111
  7. package/server/agent/commandRegistry.js +74 -58
  8. package/server/agent/commands.js +1694 -788
  9. package/server/agent/config.js +264 -90
  10. package/server/agent/contextCompactor.js +79 -0
  11. package/server/agent/doctor.js +69 -0
  12. package/server/agent/eventBus.js +107 -103
  13. package/server/agent/gpuMetrics.js +189 -0
  14. package/server/agent/llmClient.js +369 -200
  15. package/server/agent/mcpClient.js +364 -0
  16. package/server/agent/memory.js +670 -566
  17. package/server/agent/modelManager.js +77 -0
  18. package/server/agent/research.js +202 -0
  19. package/server/agent/session.js +244 -239
  20. package/server/agent/subagent.js +116 -0
  21. package/server/agent/theme.js +125 -124
  22. package/server/agent/toolRouter.js +201 -0
  23. package/server/agent/tools.js +1679 -1528
  24. package/server/agent/ui.js +760 -469
  25. package/server/agent/youtubeAuth.js +144 -65
  26. package/server/mcp/claude-desktop-config.example.json +1 -1
  27. package/server/mcp/data/discography.js +36 -36
  28. package/server/mcp/dataAdapter.js +102 -0
  29. package/server/mcp/examples/claude-code.mcp.example.json +19 -0
  30. package/server/mcp/examples/claude-desktop.example.json +11 -0
  31. package/server/mcp/examples/cline.example.json +13 -0
  32. package/server/mcp/examples/cursor.example.json +14 -0
  33. package/server/mcp/examples/gemini-cli.example.json +17 -0
  34. package/server/mcp/examples/opencode.example.json +21 -0
  35. package/server/mcp/examples/vscode.example.json +19 -0
  36. package/server/mcp/examples/windsurf.example.json +14 -0
  37. package/server/mcp/handler.js +85 -159
  38. package/server/mcp/security.js +140 -138
  39. package/server/mcp/server.js +129 -100
  40. package/server/mcp/stdio.js +4 -2
@@ -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
- MUSIC_PLAY: 'music:play',
88
- MUSIC_STOP: 'music:stop',
89
- MUSIC_QUEUE_CHANGE: 'music:queue:change',
90
- MUSIC_STATE_CHANGE: 'music:state:change',
91
-
92
- SESSION_SAVED: 'session:saved',
93
- SESSION_ARCHIVED: 'session:archived',
94
- MEMORY_FACT_ADDED: 'memory:fact:added',
95
-
96
- UI_RENDER: 'ui:render',
97
- UI_CLEAR: 'ui:clear',
98
- UI_MESSAGE: 'ui:message',
99
-
100
- QUEUE_ITEM_ADDED: 'queue:item:added',
101
- QUEUE_ITEM_CONSUMED: 'queue:item:consumed',
102
- QUEUE_CLEARED: 'queue:cleared',
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
+ }