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.
Files changed (131) hide show
  1. package/README.md +459 -407
  2. package/package.json +32 -3
  3. package/server/agent/agentCore.js +635 -267
  4. package/server/agent/appPaths.js +2 -0
  5. package/server/agent/catalogEntityResolver.js +637 -0
  6. package/server/agent/citations.js +0 -21
  7. package/server/agent/clarificationAnalytics.js +1 -1
  8. package/server/agent/clarifyingQuestions.js +286 -216
  9. package/server/agent/cli.js +532 -73
  10. package/server/agent/commandFeatures.js +123 -24
  11. package/server/agent/commandRegistry.js +128 -62
  12. package/server/agent/commands.js +1410 -95
  13. package/server/agent/config.js +253 -163
  14. package/server/agent/contextCompactor.js +239 -25
  15. package/server/agent/dynamicCatalog.js +192 -0
  16. package/server/agent/envBootstrap.js +46 -0
  17. package/server/agent/envManager.js +317 -0
  18. package/server/agent/eventBus.js +3 -3
  19. package/server/agent/feedbackLearning.js +0 -8
  20. package/server/agent/gpuMetrics.js +188 -55
  21. package/server/agent/ingestionWorker.js +339 -0
  22. package/server/agent/ink/App.jsx +348 -0
  23. package/server/agent/ink/Panel.jsx +54 -0
  24. package/server/agent/ink/PromptInput.jsx +321 -0
  25. package/server/agent/ink/TaskPlanPanel.jsx +88 -0
  26. package/server/agent/ink/ThinkingPanel.jsx +68 -0
  27. package/server/agent/ink/ToolResult.jsx +133 -0
  28. package/server/agent/ink/main.jsx +195 -0
  29. package/server/agent/ink/useEventBus.js +218 -0
  30. package/server/agent/lanDiscovery.js +369 -0
  31. package/server/agent/llmClient.js +111 -39
  32. package/server/agent/lmStudioWakeup.js +144 -0
  33. package/server/agent/mcpClient.js +2 -2
  34. package/server/agent/memory/contextAllocator.js +214 -0
  35. package/server/agent/memory/hierarchicalStore.js +523 -0
  36. package/server/agent/memory/reflectionWorker.js +110 -0
  37. package/server/agent/memory.js +635 -494
  38. package/server/agent/modelConfig.js +487 -0
  39. package/server/agent/newsIntelligence.js +219 -0
  40. package/server/agent/newsWatch.js +85 -0
  41. package/server/agent/prompts.js +146 -0
  42. package/server/agent/publicTools.js +7 -0
  43. package/server/agent/queryDetails.js +62 -1
  44. package/server/agent/research.js +314 -35
  45. package/server/agent/routingInterceptor.js +77 -0
  46. package/server/agent/rumoredProjects.js +15 -0
  47. package/server/agent/sanitizer.js +117 -17
  48. package/server/agent/subagent.js +52 -9
  49. package/server/agent/suggestionTui.js +33 -13
  50. package/server/agent/suggestions.js +29 -13
  51. package/server/agent/telemetry/hardwareTiers.js +332 -0
  52. package/server/agent/telemetry/runtimeWatchdog.js +66 -0
  53. package/server/agent/telemetry/techPowerUpClient.js +682 -0
  54. package/server/agent/telemetry/techpowerup-mcp.json +22 -0
  55. package/server/agent/telemetry.js +0 -11
  56. package/server/agent/terminalImage.js +0 -1
  57. package/server/agent/textToolProtocol.js +190 -0
  58. package/server/agent/tokenTracker.js +220 -0
  59. package/server/agent/toolRouter.js +362 -216
  60. package/server/agent/tools.js +1155 -194
  61. package/server/agent/ui.js +144 -41
  62. package/server/agent/vramService.js +572 -0
  63. package/server/agent/webSearch.js +2279 -273
  64. package/server/agent/websearch/adapters/duckduckgo.js +97 -0
  65. package/server/agent/websearch/adapters/jina.js +77 -0
  66. package/server/agent/websearch/adapters/searxng.js +57 -0
  67. package/server/agent/websearch/core/canonical.js +89 -0
  68. package/server/agent/websearch/core/fusion.js +81 -0
  69. package/server/agent/websearch/core/types.js +29 -0
  70. package/server/agent/websearch/extract.js +605 -0
  71. package/server/agent/websearch/index.js +43 -0
  72. package/server/agent/websearch/orchestrator.js +266 -0
  73. package/server/agent/websearch/resilience/breaker.js +97 -0
  74. package/server/agent/websearch/resilience/cache.js +59 -0
  75. package/server/agent/websearch/resilience/limiter.js +93 -0
  76. package/server/agent/websearch/security/filter.js +36 -0
  77. package/server/agent/websearch/security/ssrf.js +289 -0
  78. package/server/agent/websearch/telemetry.js +71 -0
  79. package/server/agent/youtubeAuth.js +82 -73
  80. package/server/agent/youtubeTranscript.js +209 -0
  81. package/server/apiRouter.js +390 -190
  82. package/server/mcp/data/brands.js +5 -5
  83. package/server/mcp/data/discography.js +56 -45
  84. package/server/mcp/data/gallery.js +9 -0
  85. package/server/mcp/data/index.js +1 -0
  86. package/server/mcp/data/industryIndex.js +251 -0
  87. package/server/mcp/data/profile.js +5 -5
  88. package/server/mcp/data/timeline.js +2 -2
  89. package/server/mcp/data/works.js +2 -2
  90. package/server/mcp/handler.js +0 -2
  91. package/server/mcp/prompts.js +42 -2
  92. package/server/mcp/resources.js +5 -1
  93. package/server/mcp/security.js +25 -14
  94. package/server/mcp/server.js +92 -3
  95. package/server/mcp/tools.js +20 -11
  96. package/server/routes/agent.js +55 -0
  97. package/server/web-chat/core/connection.js +218 -194
  98. package/server/web-chat/core/conversationContext.js +1 -1
  99. package/server/web-chat/core/directCatalogEngine.js +190 -184
  100. package/server/web-chat/core/followUpEngine.js +88 -11
  101. package/server/web-chat/core/security.js +7 -4
  102. package/server/web-chat/router.js +54 -17
  103. package/server/web-chat/routes/bilibiliRoute.js +34 -0
  104. package/server/web-chat/routes/chatRoute.js +488 -397
  105. package/server/web-chat/routes/clarifyConfigRoute.js +1 -2
  106. package/server/web-chat/routes/exportRoute.js +2 -1
  107. package/server/web-chat/routes/lanDiscoveryRoute.js +90 -0
  108. package/server/web-chat/routes/newsRoute.js +36 -6
  109. package/server/web-chat/routes/providerRoute.js +87 -0
  110. package/server/web-chat/routes/staticRoute.js +61 -13
  111. package/server/web-chat/routes/statusRoute.js +23 -2
  112. package/server/web-chat/routes/vramRoute.js +22 -0
  113. package/server/web-chat/server.js +135 -106
  114. package/server/web-chat/ui/htmlBuilder.js +8 -8
  115. package/server/web-chat/ui/previews/modelSettings.preview.html +41 -0
  116. package/server/web-chat/ui/scripts/agentBubbleSystem.js +0 -5
  117. package/server/web-chat/ui/scripts/client.js +10526 -8397
  118. package/server/web-chat/ui/scripts/webchatI18n.js +190 -138
  119. package/server/web-chat/ui/styles/main.css +7471 -4246
  120. package/server/web-chat/ui/templates/body.html +305 -179
  121. package/src/data/brandsData.js +29 -6
  122. package/src/data/discographyData.js +918 -900
  123. package/src/data/galleryData.js +66 -0
  124. package/src/data/industryIndexData.js +292 -0
  125. package/src/data/musicVideoData.js +48 -5
  126. package/src/data/profileData.js +15 -1
  127. package/src/data/timelineData.js +4 -3
  128. package/src/data/worksData.js +536 -535
  129. package/src/lib/vram/estimator.js +464 -0
  130. package/src/lib/vram/modelRegistry.js +387 -0
  131. package/tokens.css +33 -2
@@ -0,0 +1,195 @@
1
+ import React from 'react';
2
+ import { render } from 'ink';
3
+ import '../youtubeAuth.js';
4
+ import { App } from './App.jsx';
5
+ import { KikuAgent } from '../agentCore.js';
6
+ import { LMStudioClient } from '../llmClient.js';
7
+ import { toolsSchema } from '../tools.js';
8
+ import { config } from '../config.js';
9
+ import { COMMAND_DEFINITIONS } from '../commandRegistry.js';
10
+ import { C, UI_ICONS } from '../ui.js';
11
+ import { existsSync } from 'node:fs';
12
+ import { spawn } from 'node:child_process';
13
+ import { PACKAGE_VERSION } from '../version.js';
14
+
15
+ function parseCliArgs() {
16
+ const args = process.argv.slice(2);
17
+ const options = {
18
+ query: null,
19
+ model: 'google/gemma-4-e4b',
20
+ url: null,
21
+ showTools: false,
22
+ showStats: false,
23
+ thinking: 'show',
24
+ verbose: false,
25
+ help: false,
26
+ version: false
27
+ };
28
+
29
+ for (let i = 0; i < args.length; i++) {
30
+ const arg = args[i];
31
+ if (arg === '--help' || arg === '-h') options.help = true;
32
+ else if (arg === '--version' || arg === '-V') options.version = true;
33
+ else if (arg === '--tools' || arg === '-t') options.showTools = true;
34
+ else if (arg === '--stats' || arg === '-s') options.showStats = true;
35
+ else if (arg === '--verbose' || arg === '-v') { options.verbose = true; options.thinking = 'show'; }
36
+ else if (arg === '--thinking') options.thinking = args[++i] || 'show';
37
+ else if (arg === '--model' || arg === '-m') options.model = args[++i] || options.model;
38
+ else if (arg === '--url' || arg === '-u') options.url = args[++i];
39
+ else if (arg === '--query' || arg === '-q') options.query = args[++i];
40
+ else if (!arg.startsWith('-') && !options.query) { options.query = args.slice(i).join(' '); break; }
41
+ }
42
+ return options;
43
+ }
44
+
45
+ async function waitForLMStudio(baseUrl, timeoutMs = 30000) {
46
+ const client = new LMStudioClient({ baseUrl });
47
+ const start = Date.now();
48
+ while (Date.now() - start < timeoutMs) {
49
+ const health = await client.checkConnection();
50
+ if (health.connected) return health;
51
+ await new Promise(r => setTimeout(r, 1000));
52
+ }
53
+ return null;
54
+ }
55
+
56
+ async function attemptAutoStartLMStudio(baseUrl, model) {
57
+ const isWin = process.platform === 'win32';
58
+ const isMac = process.platform === 'darwin';
59
+
60
+ const candidates = isWin
61
+ ? [
62
+ `${process.env.LOCALAPPDATA}\\LM Studio\\LM Studio.exe`,
63
+ `${process.env.LOCALAPPDATA}\\Programs\\LM Studio\\LM Studio.exe`,
64
+ 'C:\\Program Files\\LM Studio\\LM Studio.exe'
65
+ ]
66
+ : isMac
67
+ ? ['/Applications/LM Studio.app/Contents/MacOS/LM Studio']
68
+ : ['lmstudio', '/usr/local/bin/lmstudio', '/opt/lmstudio/bin/lmstudio'];
69
+
70
+ const exe = candidates.find(p => {
71
+ if (p.includes('/') || p.includes('\\')) {
72
+ try { return existsSync(p); } catch { return false; }
73
+ }
74
+ return true;
75
+ });
76
+
77
+ if (!exe) return null;
78
+
79
+ try {
80
+ let port = '1234';
81
+ try { const parsedUrl = new URL(baseUrl); if (parsedUrl.port) port = parsedUrl.port; } catch {}
82
+
83
+ const child = spawn(exe, isWin
84
+ ? ['--model', model, '--serve', '--port', port]
85
+ : ['serve', '--model', model, '--port', port],
86
+ { detached: true, stdio: 'ignore', windowsHide: true }
87
+ );
88
+ child.unref();
89
+
90
+ return await waitForLMStudio(baseUrl, 45000);
91
+ } catch { return null; }
92
+ }
93
+
94
+ function printHelpText() {
95
+ const commands = COMMAND_DEFINITIONS
96
+ .map(command => ` ${C.emerald}${command.usage.padEnd(30)}${C.reset} ${command.desc}`)
97
+ .join('\n');
98
+ console.log(`\n ${C.bold}${C.gold}${UI_ICONS.help} Kiku AI Agent — ${COMMAND_DEFINITIONS.length} Commands${C.reset}\n${commands}\n ${C.dim}Tip: Press [Tab] for auto-completion.${C.reset}\n`);
99
+ }
100
+
101
+ async function printStatsText() {
102
+ const { executeTool } = await import('../tools.js');
103
+ const result = await executeTool('get_system_stats', {});
104
+ const s = result.stats;
105
+ console.log(JSON.stringify(s, null, 2));
106
+ }
107
+
108
+ async function main() {
109
+ const opts = parseCliArgs();
110
+
111
+ if (opts.version) { console.log(PACKAGE_VERSION); process.exit(0); }
112
+ if (opts.help) { printHelpText(); process.exit(0); }
113
+ if (opts.showStats) { await printStatsText(); process.exit(0); }
114
+
115
+ if (opts.showTools) {
116
+ console.log(`\n ${C.bold}${C.sakura}Tools (${toolsSchema.length})${C.reset}\n`);
117
+ for (const [i, t] of toolsSchema.entries()) {
118
+ console.log(` ${C.gold}${String(i + 1).padStart(2)}${C.reset} ${C.cyberCyan}${t.function.name}${C.reset} — ${C.dim}${t.function.description.slice(0, 60)}${C.reset}`);
119
+ }
120
+ process.exit(0);
121
+ }
122
+
123
+ const baseUrl = opts.url || config.lmStudio.baseUrl;
124
+ const client = new LMStudioClient({ baseUrl, model: opts.model });
125
+ const health = await client.checkConnection();
126
+
127
+ if (!health.connected) {
128
+ console.log(` ${C.amber}!! LM Studio offline${C.reset}`);
129
+ console.log(` ${C.dim}• ${health.error}${C.reset}\n`);
130
+
131
+ const autoHealth = await attemptAutoStartLMStudio(baseUrl, opts.model || config.lmStudio.model);
132
+ if (autoHealth) {
133
+ console.log(` ${C.emerald}[OK] LM Studio started.${C.reset}\n`);
134
+ } else {
135
+ console.log(` ${C.sakura}Setup: Open LM Studio → load google/gemma-4-e4b → Start Server :1234${C.reset}\n`);
136
+ process.exit(1);
137
+ }
138
+ }
139
+
140
+ const agent = new KikuAgent({
141
+ baseUrl,
142
+ model: health.activeModel || opts.model,
143
+ thinkingMode: opts.thinking,
144
+ });
145
+
146
+ await agent._ready;
147
+
148
+ if (opts.query) {
149
+ const { executeTool: execTool } = await import('../tools.js');
150
+ const { sanitizeQuery } = await import('../sanitizer.js');
151
+ const { sanitized } = sanitizeQuery(opts.query);
152
+ const prompt = sanitized || opts.query;
153
+
154
+ console.log(` ${C.bold}${C.gold}QUERY${C.reset} ${prompt}\n`);
155
+
156
+ await agent.ask(prompt, {
157
+ onReasoningStart: () => process.stdout.write(' THINKING '),
158
+ onReasoningDelta: (chunk) => process.stdout.write(chunk),
159
+ onReasoningEnd: () => process.stdout.write('\n'),
160
+ onToolStart: (name) => process.stdout.write(` [RUNNING] ${name}... `),
161
+ onToolResult: () => process.stdout.write(`[OK]\n`),
162
+ onTokenDelta: (chunk) => process.stdout.write(chunk),
163
+ onFinalAnswer: () => {},
164
+ onError: (err) => console.error(`\n [ERROR] ${err.message}`),
165
+ });
166
+ process.exit(0);
167
+ }
168
+
169
+ const helpFn = (sub) => { printHelpText(); };
170
+ const adFn = () => { console.log(` ${UI_ICONS.sponsor} SPONSOR DashSecurity — https://dashsecurity.kpltgroup.com/`); };
171
+
172
+ const { waitUntilExit } = render(
173
+ <App
174
+ agent={agent}
175
+ client={client}
176
+ baseUrl={baseUrl}
177
+ printHelp={helpFn}
178
+ printAd={adFn}
179
+ />,
180
+ {
181
+ exitOnCtrlC: true,
182
+ patchConsole: false,
183
+ }
184
+ );
185
+
186
+ await waitUntilExit;
187
+
188
+ try { await agent.endSession(); } catch {}
189
+ console.log('\n Session archived. Goodbye. (鞠躬 ~ 谢谢!)\n');
190
+ }
191
+
192
+ main().catch(err => {
193
+ console.error(`\n ${C.red}Fatal: ${err.message}${C.reset}\n`);
194
+ process.exit(1);
195
+ });
@@ -0,0 +1,218 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { eventBus } from '../eventBus.js';
3
+
4
+ export function useEvent(eventName, initialValue = null) {
5
+ const [value, setValue] = useState(initialValue);
6
+
7
+ useEffect(() => {
8
+ const unsub = eventBus.on(eventName, (data) => {
9
+ setValue(data);
10
+ });
11
+ return unsub;
12
+ }, [eventName]);
13
+
14
+ return value;
15
+ }
16
+
17
+ export function useEvents(eventMap) {
18
+ const [state, setState] = useState(() => {
19
+ const init = {};
20
+ for (const [key] of Object.entries(eventMap)) {
21
+ init[key] = null;
22
+ }
23
+ return init;
24
+ });
25
+
26
+ useEffect(() => {
27
+ const unsubs = [];
28
+ for (const [key, eventName] of Object.entries(eventMap)) {
29
+ const unsub = eventBus.on(eventName, (data) => {
30
+ setState(prev => ({ ...prev, [key]: data }));
31
+ });
32
+ unsubs.push(unsub);
33
+ }
34
+ return () => unsubs.forEach(u => u());
35
+ }, []);
36
+
37
+ return state;
38
+ }
39
+
40
+ export function useEmit() {
41
+ return useCallback((eventName, data) => {
42
+ eventBus.emit(eventName, data);
43
+ }, []);
44
+ }
45
+
46
+ export function useAgentState() {
47
+ const [agentState, setAgentState] = useState({
48
+ state: 'IDLE',
49
+ label: 'Idle',
50
+ toolName: null,
51
+ elapsedMs: 0,
52
+ isProcessing: false,
53
+ });
54
+
55
+ useEffect(() => {
56
+ const unsubs = [
57
+ eventBus.on('agent:state:change', (data) => {
58
+ setAgentState({
59
+ state: data.state || 'IDLE',
60
+ label: data.label || 'Idle',
61
+ toolName: data.state === 'FETCHING_TOOL' ? data.label?.replace('Tool: ', '') : null,
62
+ elapsedMs: 0,
63
+ isProcessing: data.state !== 'IDLE' && data.state !== 'COMPLETE',
64
+ });
65
+ }),
66
+ ];
67
+ return () => unsubs.forEach(u => u());
68
+ }, []);
69
+
70
+ return agentState;
71
+ }
72
+
73
+ export function useStreamingText() {
74
+ const [text, setText] = useState('');
75
+ const [isStreaming, setIsStreaming] = useState(false);
76
+
77
+ useEffect(() => {
78
+ const unsubs = [
79
+ eventBus.on('agent:token:start', () => {
80
+ setIsStreaming(true);
81
+ setText('');
82
+ }),
83
+ eventBus.on('agent:token:delta', (data) => {
84
+ setText(prev => prev + (data?.chunk || ''));
85
+ }),
86
+ eventBus.on('agent:final:answer', () => {
87
+ setIsStreaming(false);
88
+ }),
89
+ ];
90
+ return () => unsubs.forEach(u => u());
91
+ }, []);
92
+
93
+ return { text, isStreaming, reset: () => setText('') };
94
+ }
95
+
96
+ export function useThinking() {
97
+ const [thinking, setThinking] = useState('');
98
+ const [duration, setDuration] = useState(0);
99
+ const [isActive, setIsActive] = useState(false);
100
+ const startTimeRef = useRef(null);
101
+
102
+ useEffect(() => {
103
+ const unsubs = [
104
+ eventBus.on('agent:reasoning:start', () => {
105
+ setIsActive(true);
106
+ setThinking('');
107
+ startTimeRef.current = Date.now();
108
+ }),
109
+ eventBus.on('agent:reasoning:delta', (data) => {
110
+ setThinking(prev => prev + (data?.chunk || ''));
111
+ }),
112
+ eventBus.on('agent:reasoning:end', () => {
113
+ setIsActive(false);
114
+ if (startTimeRef.current) {
115
+ setDuration(((Date.now() - startTimeRef.current) / 1000).toFixed(1));
116
+ }
117
+ }),
118
+ ];
119
+ return () => unsubs.forEach(u => u());
120
+ }, []);
121
+
122
+ return { thinking, duration, isActive };
123
+ }
124
+
125
+ export function useToolResults() {
126
+ const [tools, setTools] = useState([]);
127
+
128
+ useEffect(() => {
129
+ const onToolStart = (data) => {
130
+ const toolName = data && data.toolName ? data.toolName : '?';
131
+ const toolArgs = data && data.args ? data.args : {};
132
+ setTools((prev) => [...prev, {
133
+ name: toolName,
134
+ args: toolArgs,
135
+ result: null,
136
+ duration: 0,
137
+ status: 'running',
138
+ id: Date.now(),
139
+ }]);
140
+ };
141
+
142
+ const onToolResult = (data) => {
143
+ const toolName = data && data.toolName ? data.toolName : '';
144
+ const result = data && data.result ? data.result : null;
145
+ const durationMs = data && data.durationMs ? data.durationMs : 0;
146
+ const isError = result && result.status === 'error';
147
+ setTools((prev) => prev.map((t) => {
148
+ if (t.status === 'running' && t.name === toolName) {
149
+ return Object.assign({}, t, {
150
+ result: result,
151
+ duration: durationMs,
152
+ status: isError ? 'error' : 'done',
153
+ });
154
+ }
155
+ return t;
156
+ }));
157
+ };
158
+
159
+ const unsub1 = eventBus.on('agent:tool:start', onToolStart);
160
+ const unsub2 = eventBus.on('agent:tool:result', onToolResult);
161
+ return function cleanup() {
162
+ unsub1();
163
+ unsub2();
164
+ };
165
+ }, []);
166
+
167
+ const clearTools = useCallback(() => setTools([]), []);
168
+
169
+ return { tools: tools, clearTools: clearTools };
170
+ }
171
+
172
+ export function useTaskPlan() {
173
+ const [plan, setPlan] = useState(null);
174
+
175
+ useEffect(() => {
176
+ const onStart = (data) => {
177
+ if (data?.steps) {
178
+ setPlan({
179
+ planId: data.planId,
180
+ question: data.question,
181
+ steps: data.steps,
182
+ isActive: true,
183
+ startTime: Date.now(),
184
+ });
185
+ }
186
+ };
187
+
188
+ const onStepUpdate = (data) => {
189
+ setPlan(prev => {
190
+ if (!prev) return prev;
191
+ const updatedSteps = prev.steps.map(s => {
192
+ if (s.id === data.stepId) {
193
+ return { ...s, state: data.state, label: data.label || s.label };
194
+ }
195
+ return s;
196
+ });
197
+ return { ...prev, steps: updatedSteps };
198
+ });
199
+ };
200
+
201
+ const onComplete = () => {
202
+ setPlan(prev => prev ? { ...prev, isActive: false } : null);
203
+ };
204
+
205
+ const unsubs = [
206
+ eventBus.on('agent:plan:start', onStart),
207
+ eventBus.on('agent:plan:step:update', onStepUpdate),
208
+ eventBus.on('agent:plan:complete', onComplete),
209
+ eventBus.on('agent:final:answer', () => setPlan(null)),
210
+ ];
211
+
212
+ return () => unsubs.forEach(u => u());
213
+ }, []);
214
+
215
+ const clearPlan = useCallback(() => setPlan(null), []);
216
+
217
+ return { plan, clearPlan };
218
+ }