kiku-agent 1.3.1 → 1.3.3

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 (232) hide show
  1. package/README.md +69 -12
  2. package/package.json +5 -12
  3. package/server/agent/agentCore.js +319 -46
  4. package/server/agent/appPaths.js +31 -0
  5. package/server/agent/audioPlayer.js +339 -12
  6. package/server/agent/autoContext.js +193 -0
  7. package/server/agent/catalogEntityResolver.js +170 -28
  8. package/server/agent/clarifyingQuestions.js +137 -35
  9. package/server/agent/cli.js +177 -48
  10. package/server/agent/cloudAdapters.js +116 -0
  11. package/server/agent/commandRegistry.js +409 -80
  12. package/server/agent/commands.js +1056 -125
  13. package/server/agent/config.js +1088 -690
  14. package/server/agent/contextCompactor.js +213 -21
  15. package/server/agent/cpuMetrics.js +199 -0
  16. package/server/agent/engineService.js +591 -0
  17. package/server/agent/eval/AGENTS.md +228 -0
  18. package/server/agent/eval/analyze.py +684 -0
  19. package/server/agent/eval/gates.json +15 -0
  20. package/server/agent/eval/modules/analysis/compare.js +198 -0
  21. package/server/agent/eval/modules/analysis/gates.js +72 -0
  22. package/server/agent/eval/modules/analysis/judge.js +70 -0
  23. package/server/agent/eval/modules/core/cli.js +453 -0
  24. package/server/agent/eval/modules/core/config.js +60 -0
  25. package/server/agent/eval/modules/core/store.js +236 -0
  26. package/server/agent/eval/modules/evaluation/cases.js +689 -0
  27. package/server/agent/eval/modules/evaluation/runner.js +288 -0
  28. package/server/agent/eval/modules/evaluation/scoring.js +363 -0
  29. package/server/agent/eval/modules/evaluation/worker.js +122 -0
  30. package/server/agent/eval/modules/reporting/benchmark.js +265 -0
  31. package/server/agent/eval/modules/reporting/benchmarkPng.js +487 -0
  32. package/server/agent/eval/modules/reporting/dashboard.js +1406 -0
  33. package/server/agent/eval/modules/server/evalApi.js +1099 -0
  34. package/server/agent/eval/modules/ui/evalUi.js +4799 -0
  35. package/server/agent/eval/modules/utils/smokes.js +119 -0
  36. package/server/agent/eventBus.js +1 -0
  37. package/server/agent/ink/main.jsx +196 -191
  38. package/server/agent/jobManager.js +11 -1
  39. package/server/agent/lanDiscovery.js +721 -23
  40. package/server/agent/llmClient.js +317 -71
  41. package/server/agent/lmStudioWakeup.js +517 -65
  42. package/server/agent/locationService.js +797 -0
  43. package/server/agent/mcpQuizData.js +509 -0
  44. package/server/agent/memory/contextAllocator.js +19 -7
  45. package/server/agent/memory.js +3 -1
  46. package/server/agent/modelCompatibility.js +352 -0
  47. package/server/agent/modelDefaults.js +1 -0
  48. package/server/agent/modelManager.js +46 -2
  49. package/server/agent/newsWatch.js +9 -3
  50. package/server/agent/processManager.js +229 -0
  51. package/server/agent/prompts.js +174 -8
  52. package/server/agent/routingInterceptor.js +9 -1
  53. package/server/agent/sanitizer.js +41 -6
  54. package/server/agent/searchQueryPlanner.js +202 -0
  55. package/server/agent/session.js +59 -4
  56. package/server/agent/tests/agentBubbleSystem.test.js +65 -0
  57. package/server/agent/tests/agentFollowUps.test.js +253 -0
  58. package/server/agent/tests/agentMusicControl.test.js +245 -0
  59. package/server/agent/tests/apiRouter.test.js +108 -0
  60. package/server/agent/tests/architectureFixes.test.js +81 -0
  61. package/server/agent/tests/architectureRefactoring.test.js +194 -0
  62. package/server/agent/tests/autoContext.test.js +176 -0
  63. package/server/agent/tests/autoResearch.test.js +64 -0
  64. package/server/agent/tests/bilibiliEmbed.test.js +296 -0
  65. package/server/agent/tests/catalogSync.test.js +130 -0
  66. package/server/agent/tests/chatHardening.test.js +121 -0
  67. package/server/agent/tests/chineseWebSearchEscalation.test.js +62 -0
  68. package/server/agent/tests/citations.test.js +180 -0
  69. package/server/agent/tests/clarificationAnalytics.test.js +219 -0
  70. package/server/agent/tests/clarifyingQuestions.test.js +627 -0
  71. package/server/agent/tests/cliAutomation.test.js +158 -0
  72. package/server/agent/tests/cloudProviders.test.js +322 -0
  73. package/server/agent/tests/commandInput.test.js +24 -0
  74. package/server/agent/tests/commands.test.js +506 -0
  75. package/server/agent/tests/contextIntelligence.test.js +209 -0
  76. package/server/agent/tests/conversationSafety.test.js +53 -0
  77. package/server/agent/tests/cpuMetrics.test.js +101 -0
  78. package/server/agent/tests/decisionAndSearchEnhancements.test.js +167 -0
  79. package/server/agent/tests/dramaAnswerQuality.test.js +73 -0
  80. package/server/agent/tests/dynamicCatalogIngestion.test.js +285 -0
  81. package/server/agent/tests/dynamicRoutingAndSearch.test.js +92 -0
  82. package/server/agent/tests/edgeCasesScenarios.test.js +131 -0
  83. package/server/agent/tests/engineService.test.js +436 -0
  84. package/server/agent/tests/engineUnload.test.js +94 -0
  85. package/server/agent/tests/entertainmentMetrics.test.js +126 -0
  86. package/server/agent/tests/envBootstrap.test.js +87 -0
  87. package/server/agent/tests/envCommand.test.js +284 -0
  88. package/server/agent/tests/errorHandling.test.js +80 -0
  89. package/server/agent/tests/errors.test.js +13 -0
  90. package/server/agent/tests/evalAnalyze.test.js +54 -0
  91. package/server/agent/tests/evalCommand.test.js +54 -0
  92. package/server/agent/tests/evalCorpus.test.js +126 -0
  93. package/server/agent/tests/evalHarness.test.js +62 -0
  94. package/server/agent/tests/evalJudgeContext.test.js +38 -0
  95. package/server/agent/tests/evalMetrics.test.js +320 -0
  96. package/server/agent/tests/evalStore.test.js +190 -0
  97. package/server/agent/tests/evalStudio.test.js +179 -0
  98. package/server/agent/tests/feedbackLearning.test.js +186 -0
  99. package/server/agent/tests/fetchApi.test.js +99 -0
  100. package/server/agent/tests/firstRun.test.js +44 -0
  101. package/server/agent/tests/gemma4eOptimization.test.js +95 -0
  102. package/server/agent/tests/hardwareTiers.test.js +206 -0
  103. package/server/agent/tests/hybridWebSearch.test.js +194 -0
  104. package/server/agent/tests/judgeService.test.js +251 -0
  105. package/server/agent/tests/lanDiscovery.test.js +324 -0
  106. package/server/agent/tests/lanInterfaces.test.js +510 -0
  107. package/server/agent/tests/lmStudioWakeup.test.js +278 -0
  108. package/server/agent/tests/locationTool.test.js +278 -0
  109. package/server/agent/tests/mcpClient.test.js +201 -0
  110. package/server/agent/tests/memoryRoute.test.js +173 -0
  111. package/server/agent/tests/memoryUiNormalization.test.js +75 -0
  112. package/server/agent/tests/modelCompatibility.test.js +148 -0
  113. package/server/agent/tests/modelManager.test.js +141 -0
  114. package/server/agent/tests/multilingualScenarios.test.js +69 -0
  115. package/server/agent/tests/musicControlUiBridge.test.js +113 -0
  116. package/server/agent/tests/newCommands.test.js +347 -0
  117. package/server/agent/tests/newCommandsTui.test.js +141 -0
  118. package/server/agent/tests/newTools.test.js +113 -0
  119. package/server/agent/tests/newsIntelligence.test.js +178 -0
  120. package/server/agent/tests/nextGenArchitecture.test.js +101 -0
  121. package/server/agent/tests/offlineInput.test.js +217 -0
  122. package/server/agent/tests/openDomainPrompt.test.js +197 -0
  123. package/server/agent/tests/openDomainSearch.test.js +121 -0
  124. package/server/agent/tests/packagingGuard.test.js +57 -0
  125. package/server/agent/tests/presentation.test.js +61 -0
  126. package/server/agent/tests/processManager.test.js +144 -0
  127. package/server/agent/tests/providerPersistence.test.js +44 -0
  128. package/server/agent/tests/queryBenchmarks.test.js +83 -0
  129. package/server/agent/tests/queryDetails.test.js +126 -0
  130. package/server/agent/tests/quickActionsAndModals.test.js +180 -0
  131. package/server/agent/tests/realWorldValidation.test.js +391 -0
  132. package/server/agent/tests/resilienceAndConcurrency.test.js +117 -0
  133. package/server/agent/tests/runtimeWatchdog.test.js +32 -0
  134. package/server/agent/tests/sanitizer.test.js +37 -0
  135. package/server/agent/tests/scenariosData.js +378 -0
  136. package/server/agent/tests/schemaRouting.test.js +54 -0
  137. package/server/agent/tests/searchQueryPlanner.test.js +108 -0
  138. package/server/agent/tests/securityAuditRemediation.test.js +221 -0
  139. package/server/agent/tests/securityAuditRemediations2.test.js +154 -0
  140. package/server/agent/tests/securityHardening.test.js +146 -0
  141. package/server/agent/tests/securitySanitizer.test.js +38 -0
  142. package/server/agent/tests/sessionAndCopy.test.js +267 -0
  143. package/server/agent/tests/sidebarControls.test.js +135 -0
  144. package/server/agent/tests/statefulMemory.test.js +277 -0
  145. package/server/agent/tests/subagent.test.js +216 -0
  146. package/server/agent/tests/subagentProgress.test.js +109 -0
  147. package/server/agent/tests/subagentTextFallback.test.js +115 -0
  148. package/server/agent/tests/suggestions.test.js +199 -0
  149. package/server/agent/tests/suggestionsTui.test.js +251 -0
  150. package/server/agent/tests/telemetryAndProfiles.test.js +179 -0
  151. package/server/agent/tests/terminalImage.test.js +80 -0
  152. package/server/agent/tests/terminalMarkdown.test.js +166 -0
  153. package/server/agent/tests/terminalText.test.js +133 -0
  154. package/server/agent/tests/textProtocolLoop.test.js +197 -0
  155. package/server/agent/tests/textToolParsing.test.js +187 -0
  156. package/server/agent/tests/theme.test.js +26 -0
  157. package/server/agent/tests/toolRegistryParity.test.js +53 -0
  158. package/server/agent/tests/toolRoutingRegressions.test.js +160 -0
  159. package/server/agent/tests/tools.test.js +259 -0
  160. package/server/agent/tests/unifiedEndpoints.test.js +159 -0
  161. package/server/agent/tests/v110Features.test.js +262 -0
  162. package/server/agent/tests/versionBump.test.js +69 -0
  163. package/server/agent/tests/vramEstimator.test.js +595 -0
  164. package/server/agent/tests/webChatDocs.test.js +199 -0
  165. package/server/agent/tests/webChatInferenceControl.test.js +298 -0
  166. package/server/agent/tests/webChatInspection.test.js +551 -0
  167. package/server/agent/tests/webChatMcp.test.js +94 -0
  168. package/server/agent/tests/webChatMemory.test.js +112 -0
  169. package/server/agent/tests/webChatSecurity.test.js +169 -0
  170. package/server/agent/tests/webExtract.test.js +284 -0
  171. package/server/agent/tests/webSources.test.js +281 -0
  172. package/server/agent/textToolProtocol.js +173 -46
  173. package/server/agent/toolRouter.js +318 -44
  174. package/server/agent/tools.js +877 -96
  175. package/server/agent/ui.js +39 -34
  176. package/server/agent/vramService.js +35 -54
  177. package/server/agent/webSearch.js +441 -162
  178. package/server/agent/websearch/orchestrator.js +15 -5
  179. package/server/agent/websearch/resilience/cache.js +9 -2
  180. package/server/agent/websearch/security/ssrf.js +41 -10
  181. package/server/agent/youtubeAuth.js +5 -20
  182. package/server/apiRouter.js +5558 -4422
  183. package/server/eval/judgeRubrics.js +110 -0
  184. package/server/eval/judgeService.js +290 -0
  185. package/server/eval/runBenchmark.js +182 -0
  186. package/server/mcp/data/discography.js +2 -2
  187. package/server/mcp/examples/antigravity.example.json +17 -0
  188. package/server/mcp/examples/librechat.example.yaml +16 -0
  189. package/server/mcp/handler.js +8 -4
  190. package/server/mcp/security.js +220 -21
  191. package/server/mcp/server.js +2 -1
  192. package/server/mcp/tests/connection.test.js +162 -0
  193. package/server/mcp/tests/mcpKeyAuth.test.js +156 -0
  194. package/server/mcp/tools.js +7 -1
  195. package/server/supabase_migration_prefix.sql +102 -0
  196. package/server/supabase_schema.sql +63 -0
  197. package/server/web-chat/core/connection.js +179 -218
  198. package/server/web-chat/core/conversationContext.js +9 -2
  199. package/server/web-chat/core/directCatalogEngine.js +52 -28
  200. package/server/web-chat/core/dramaKnowledge.js +33 -22
  201. package/server/web-chat/core/followUpEngine.js +735 -81
  202. package/server/web-chat/core/futureEventGuard.js +5 -1
  203. package/server/web-chat/core/musicControlAction.js +70 -0
  204. package/server/web-chat/core/security.js +32 -21
  205. package/server/web-chat/core/toolInspector.js +6 -6
  206. package/server/web-chat/core/urlGuard.js +45 -0
  207. package/server/web-chat/router.js +204 -157
  208. package/server/web-chat/routes/chatRoute.js +885 -488
  209. package/server/web-chat/routes/engineRoute.js +270 -0
  210. package/server/web-chat/routes/exportRoute.js +1 -1
  211. package/server/web-chat/routes/lanDiscoveryRoute.js +17 -2
  212. package/server/web-chat/routes/lmStudioLoadRoute.js +172 -0
  213. package/server/web-chat/routes/mcpRoute.js +386 -0
  214. package/server/web-chat/routes/memoryRoute.js +253 -0
  215. package/server/web-chat/routes/providerRoute.js +49 -16
  216. package/server/web-chat/routes/staticRoute.js +306 -17
  217. package/server/web-chat/routes/statusRoute.js +194 -73
  218. package/server/web-chat/routes/suggestRoute.js +11 -3
  219. package/server/web-chat/routes/verifyRoute.js +26 -4
  220. package/server/web-chat/server.js +7 -6
  221. package/server/web-chat/ui/docsBuilder.js +3558 -0
  222. package/server/web-chat/ui/htmlBuilder.js +12 -0
  223. package/server/web-chat/ui/scripts/client.js +3401 -1302
  224. package/server/web-chat/ui/scripts/webchatI18n.js +155 -153
  225. package/server/web-chat/ui/settingsBuilder.js +7870 -0
  226. package/server/web-chat/ui/styles/lan-card.css +165 -0
  227. package/server/web-chat/ui/styles/main.css +4289 -447
  228. package/server/web-chat/ui/sw.js +64 -0
  229. package/server/web-chat/ui/templates/body.html +407 -281
  230. package/src/data/awardsData.js +146 -146
  231. package/src/data/musicVideoData.js +6 -4
  232. package/src/lib/vram/estimator.js +195 -19
@@ -0,0 +1,386 @@
1
+ import { toolsSchema } from '../../agent/tools.js';
2
+ import { toolDefinitions as mcpToolDefinitions } from '../../mcp/tools.js';
3
+ import { TOOL_CLUSTERS } from '../../agent/toolRouter.js';
4
+ import { currentAgent } from '../core/connection.js';
5
+ import { PACKAGE_VERSION } from '../../agent/version.js';
6
+ import { getMcpSessionStats } from '../../mcp/handler.js';
7
+ import { sendJson } from '../core/errorHandler.js';
8
+
9
+ export function buildMcpClientConfigs(baseUrl) {
10
+ const httpUrl = `${baseUrl}/mcp`;
11
+ const stdioCmd = 'npx';
12
+ const stdioArgs = ['-y', '-p', 'kiku-agent', 'kiku-mcp'];
13
+ const stdioEnv = { KIKU_MCP_ENABLE_WEB_TOOLS: 'true' };
14
+
15
+ return {
16
+ claudeDesktop: {
17
+ id: 'claudeDesktop',
18
+ name: 'Claude Desktop',
19
+ file: 'claude_desktop_config.json',
20
+ pathHint: 'macOS: ~/Library/Application Support/Claude/ • Win: %APPDATA%\\Claude\\',
21
+ desc: 'Anthropic Claude Desktop App (macOS & Windows)',
22
+ icon: 'bi-robot',
23
+ category: 'Desktop App',
24
+ http: {
25
+ mcpServers: {
26
+ kiku: { url: httpUrl }
27
+ }
28
+ },
29
+ stdio: {
30
+ mcpServers: {
31
+ kiku: {
32
+ command: stdioCmd,
33
+ args: stdioArgs,
34
+ env: stdioEnv
35
+ }
36
+ }
37
+ },
38
+ mcpServers: {
39
+ kiku: { url: httpUrl }
40
+ }
41
+ },
42
+ cursor: {
43
+ id: 'cursor',
44
+ name: 'Cursor',
45
+ file: '.cursor/mcp.json',
46
+ pathHint: '.cursor/mcp.json (ในโฟลเดอร์โปรเจกต์ หรือ ~/.cursor/mcp.json)',
47
+ desc: 'Cursor AI Code Editor',
48
+ icon: 'bi-cursor',
49
+ category: 'IDE',
50
+ http: {
51
+ mcpServers: {
52
+ kiku: { url: httpUrl }
53
+ }
54
+ },
55
+ stdio: {
56
+ mcpServers: {
57
+ kiku: {
58
+ command: stdioCmd,
59
+ args: stdioArgs,
60
+ env: stdioEnv
61
+ }
62
+ }
63
+ },
64
+ mcpServers: {
65
+ kiku: { url: httpUrl }
66
+ }
67
+ },
68
+ windsurf: {
69
+ id: 'windsurf',
70
+ name: 'Windsurf',
71
+ file: '~/.codeium/windsurf/mcp_config.json',
72
+ pathHint: '~/.codeium/windsurf/mcp_config.json',
73
+ desc: 'Codeium Windsurf Next-Gen IDE',
74
+ icon: 'bi-wind',
75
+ category: 'IDE',
76
+ http: {
77
+ mcpServers: {
78
+ kiku: { serverUrl: httpUrl }
79
+ }
80
+ },
81
+ stdio: {
82
+ mcpServers: {
83
+ kiku: {
84
+ command: stdioCmd,
85
+ args: stdioArgs,
86
+ env: stdioEnv
87
+ }
88
+ }
89
+ },
90
+ mcpServers: {
91
+ kiku: { serverUrl: httpUrl }
92
+ }
93
+ },
94
+ vscode: {
95
+ id: 'vscode',
96
+ name: 'VS Code & Copilot',
97
+ file: '.vscode/mcp.json',
98
+ pathHint: '.vscode/mcp.json (ในโปรเจกต์ หรือ User MCP Settings)',
99
+ desc: 'Visual Studio Code & GitHub Copilot Agent',
100
+ icon: 'bi-code-square',
101
+ category: 'IDE',
102
+ http: {
103
+ servers: {
104
+ kiku: {
105
+ type: 'http',
106
+ url: httpUrl
107
+ }
108
+ }
109
+ },
110
+ stdio: {
111
+ servers: {
112
+ kiku: {
113
+ type: 'stdio',
114
+ command: stdioCmd,
115
+ args: stdioArgs,
116
+ env: stdioEnv
117
+ }
118
+ }
119
+ },
120
+ servers: {
121
+ kiku: {
122
+ type: 'http',
123
+ url: httpUrl
124
+ }
125
+ }
126
+ },
127
+ antigravity: {
128
+ id: 'antigravity',
129
+ name: 'Google Antigravity (AGY)',
130
+ file: '.agents/mcp_config.json',
131
+ pathHint: '.agents/mcp_config.json หรือ ~/.gemini/config/mcp_config.json',
132
+ desc: 'Google Antigravity IDE & AGY Subagent Framework',
133
+ icon: 'bi-stars',
134
+ category: 'IDE / Agent',
135
+ http: {
136
+ mcpServers: {
137
+ kiku: { url: httpUrl }
138
+ }
139
+ },
140
+ stdio: {
141
+ mcpServers: {
142
+ kiku: {
143
+ command: stdioCmd,
144
+ args: stdioArgs,
145
+ env: stdioEnv
146
+ }
147
+ }
148
+ },
149
+ mcpServers: {
150
+ kiku: { url: httpUrl }
151
+ }
152
+ },
153
+ cline: {
154
+ id: 'cline',
155
+ name: 'Cline / Roo Code',
156
+ file: 'cline_mcp_settings.json',
157
+ pathHint: 'ส่วนขยาย Cline / Roo Code Settings',
158
+ desc: 'Cline & Roo Code Autonomous Agent Extension',
159
+ icon: 'bi-cpu',
160
+ category: 'Extension Agent',
161
+ http: {
162
+ mcpServers: {
163
+ kiku: {
164
+ url: httpUrl,
165
+ disabled: false,
166
+ alwaysAllow: []
167
+ }
168
+ }
169
+ },
170
+ stdio: {
171
+ mcpServers: {
172
+ kiku: {
173
+ command: stdioCmd,
174
+ args: stdioArgs,
175
+ env: stdioEnv,
176
+ disabled: false,
177
+ alwaysAllow: []
178
+ }
179
+ }
180
+ },
181
+ mcpServers: {
182
+ kiku: {
183
+ url: httpUrl,
184
+ disabled: false,
185
+ alwaysAllow: []
186
+ }
187
+ }
188
+ },
189
+ claudeCode: {
190
+ id: 'claudeCode',
191
+ name: 'Claude Code CLI',
192
+ file: '~/.claude/mcp.json',
193
+ pathHint: 'Terminal CLI: claude mcp add kiku ' + httpUrl,
194
+ desc: 'Anthropic Claude Code Terminal Agent',
195
+ icon: 'bi-terminal',
196
+ category: 'CLI Agent',
197
+ cliCommand: `claude mcp add kiku ${httpUrl}`,
198
+ http: {
199
+ mcpServers: {
200
+ kiku: {
201
+ type: 'http',
202
+ url: httpUrl
203
+ }
204
+ }
205
+ },
206
+ stdio: {
207
+ mcpServers: {
208
+ kiku: {
209
+ type: 'stdio',
210
+ command: stdioCmd,
211
+ args: stdioArgs,
212
+ env: stdioEnv
213
+ }
214
+ }
215
+ },
216
+ mcpServers: {
217
+ kiku: {
218
+ type: 'http',
219
+ url: httpUrl
220
+ }
221
+ }
222
+ },
223
+ geminiCli: {
224
+ id: 'geminiCli',
225
+ name: 'Gemini CLI',
226
+ file: '~/.gemini/mcp.json',
227
+ pathHint: '~/.gemini/mcp.json หรือ gemini.json',
228
+ desc: 'Google Gemini CLI & Developer Tools',
229
+ icon: 'bi-gem',
230
+ category: 'CLI Agent',
231
+ http: {
232
+ mcpServers: {
233
+ kiku: {
234
+ httpUrl: httpUrl
235
+ }
236
+ }
237
+ },
238
+ stdio: {
239
+ mcpServers: {
240
+ kiku: {
241
+ command: stdioCmd,
242
+ args: stdioArgs,
243
+ env: stdioEnv
244
+ }
245
+ }
246
+ },
247
+ mcpServers: {
248
+ kiku: {
249
+ httpUrl: httpUrl
250
+ }
251
+ }
252
+ },
253
+ librechat: {
254
+ id: 'librechat',
255
+ name: 'LibreChat',
256
+ file: 'librechat.yaml',
257
+ pathHint: 'librechat.yaml (YAML Format)',
258
+ desc: 'LibreChat Multi-User AI Web Interface',
259
+ icon: 'bi-chat-square-text',
260
+ category: 'Web UI',
261
+ format: 'yaml',
262
+ http: `mcpServers:\n kiku:\n type: streamable-http\n url: ${httpUrl}`,
263
+ stdio: `mcpServers:\n kiku:\n type: stdio\n command: npx\n args:\n - -y\n - -p\n - kiku-agent\n - kiku-mcp\n env:\n KIKU_MCP_ENABLE_WEB_TOOLS: "true"`,
264
+ httpYaml: `mcpServers:\n kiku:\n type: streamable-http\n url: ${httpUrl}`,
265
+ stdioYaml: `mcpServers:\n kiku:\n type: stdio\n command: npx\n args:\n - -y\n - -p\n - kiku-agent\n - kiku-mcp\n env:\n KIKU_MCP_ENABLE_WEB_TOOLS: "true"`,
266
+ raw: `mcpServers:\n kiku:\n type: streamable-http\n url: ${httpUrl}`
267
+ },
268
+ opencode: {
269
+ id: 'opencode',
270
+ name: 'OpenCode / Open WebUI',
271
+ file: 'opencode.json',
272
+ pathHint: 'opencode.json (OpenCode / Open WebUI)',
273
+ desc: 'OpenCode & Open WebUI AI Agent Framework',
274
+ icon: 'bi-globe',
275
+ category: 'Web UI / Agent',
276
+ http: {
277
+ $schema: 'https://opencode.ai/config.json',
278
+ mcp: {
279
+ kiku: {
280
+ type: 'remote',
281
+ url: httpUrl,
282
+ enabled: true
283
+ }
284
+ }
285
+ },
286
+ stdio: {
287
+ $schema: 'https://opencode.ai/config.json',
288
+ mcp: {
289
+ kiku: {
290
+ type: 'local',
291
+ command: ['npx', '-y', '-p', 'kiku-agent', 'kiku-mcp'],
292
+ environment: stdioEnv,
293
+ enabled: true
294
+ }
295
+ }
296
+ },
297
+ $schema: 'https://opencode.ai/config.json',
298
+ mcp: {
299
+ kiku: {
300
+ type: 'remote',
301
+ url: httpUrl,
302
+ enabled: true
303
+ }
304
+ }
305
+ }
306
+ };
307
+ }
308
+
309
+ export async function handleMcpStatusRoute(req, res, options = {}) {
310
+ const port = Number(options.port || 3002);
311
+ const host = req.headers.host || `localhost:${port}`;
312
+ const protocol = req.headers['x-forwarded-proto'] || 'http';
313
+ const baseUrl = `${protocol}://${host}`;
314
+
315
+ const mcpToolSet = new Set(mcpToolDefinitions.map(t => t.name));
316
+
317
+ const toolClusterMap = new Map();
318
+ for (const [cluster, tools] of Object.entries(TOOL_CLUSTERS)) {
319
+ for (const t of tools) {
320
+ toolClusterMap.set(t, cluster);
321
+ }
322
+ }
323
+
324
+ const agentTools = currentAgent?.getAllToolsSchema?.() || toolsSchema;
325
+ const tools = agentTools.map(t => {
326
+ const fn = t.function || t;
327
+ const name = fn.name;
328
+ const category = toolClusterMap.get(name) || (name.startsWith('mcp_') ? 'MCP' : 'CATALOG');
329
+ return {
330
+ name,
331
+ description: fn.description || '',
332
+ category,
333
+ isMcpExposed: mcpToolSet.has(name),
334
+ isExternalMcp: name.startsWith('mcp_'),
335
+ parameters: fn.parameters || fn.inputSchema || { type: 'object', properties: {} }
336
+ };
337
+ });
338
+
339
+ const categories = {
340
+ ALL: tools.length,
341
+ CATALOG: tools.filter(t => t.category === 'CATALOG').length,
342
+ MEDIA: tools.filter(t => t.category === 'MEDIA').length,
343
+ RESEARCH: tools.filter(t => t.category === 'RESEARCH').length,
344
+ SYSTEM: tools.filter(t => t.category === 'SYSTEM').length,
345
+ MCP: tools.filter(t => t.category === 'MCP' || t.isExternalMcp).length,
346
+ };
347
+
348
+ const connectedServers = currentAgent?.mcpManager?.servers
349
+ ? Array.from(currentAgent.mcpManager.servers.keys())
350
+ : [];
351
+ const importedTools = currentAgent?.mcpManager?.toolRegistry
352
+ ? Array.from(currentAgent.mcpManager.toolRegistry.keys())
353
+ : [];
354
+
355
+ const clientConfigs = buildMcpClientConfigs(baseUrl);
356
+
357
+ return sendJson(res, 200, {
358
+ status: 'ok',
359
+ timestamp: new Date().toISOString(),
360
+ mcpServer: {
361
+ online: true,
362
+ name: 'kiku',
363
+ title: 'Kiku Agent MCP Server',
364
+ version: PACKAGE_VERSION,
365
+ protocol: 'model-context-protocol',
366
+ protocolVersion: '2025-11-25',
367
+ supportedProtocolVersions: ['2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05', '2024-10-07'],
368
+ transport: 'streamable-http',
369
+ endpoint: '/mcp',
370
+ fullUrl: `${baseUrl}/mcp`,
371
+ toolsCount: mcpToolDefinitions.length,
372
+ stats: getMcpSessionStats(),
373
+ clientConfigs
374
+ },
375
+ mcpClient: {
376
+ supported: true,
377
+ connectedServersCount: connectedServers.length,
378
+ connectedServers,
379
+ importedToolsCount: importedTools.length,
380
+ importedTools
381
+ },
382
+ totalTools: tools.length,
383
+ categories,
384
+ tools
385
+ });
386
+ }
@@ -0,0 +1,253 @@
1
+ import { AgentMemory } from '../../agent/memory.js';
2
+ import { parseJsonBody, sendJson, sendError } from '../core/errorHandler.js';
3
+ import { isWebChatAdmin } from '../core/security.js';
4
+
5
+ const MAX_FACTS_RESPONSE = 200;
6
+ const MAX_CONTENT_LENGTH = 2000;
7
+
8
+ let store = null;
9
+ let loadPromise = null;
10
+
11
+ export function setMemoryStore(customStore) {
12
+ store = customStore;
13
+ loadPromise = null;
14
+ }
15
+
16
+ function getStore() {
17
+ if (!store) store = new AgentMemory();
18
+ return store;
19
+ }
20
+
21
+ async function ensureLoaded() {
22
+ const mem = getStore();
23
+ if (mem._loaded) return mem;
24
+ if (!loadPromise) {
25
+ loadPromise = Promise.resolve()
26
+ .then(() => mem.load())
27
+ .catch(() => false)
28
+ .finally(() => { loadPromise = null; });
29
+ }
30
+ await loadPromise;
31
+ return mem;
32
+ }
33
+
34
+ function toIsoDate(value) {
35
+ if (!value) return null;
36
+ try {
37
+ const date = value instanceof Date ? value : new Date(value);
38
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ function serializeFact(fact = {}) {
45
+ return {
46
+ id: String(fact.id || ''),
47
+ content: String(fact.content || ''),
48
+ category: String(fact.category || 'general'),
49
+ source: String(fact.source || 'conversation'),
50
+ confidence: fact.confidence ?? 'high',
51
+ tags: Array.isArray(fact.tags) ? fact.tags.map(t => String(t)) : [],
52
+ accessCount: Number(fact.accessCount) || 0,
53
+ createdAt: toIsoDate(fact.createdAt),
54
+ lastAccessedAt: toIsoDate(fact.lastAccessedAt),
55
+ };
56
+ }
57
+
58
+ function serializeSession(s = {}) {
59
+ const summary = s?.summary || null;
60
+ const firstQuestion = summary && Array.isArray(summary.keyExchanges)
61
+ ? summary.keyExchanges.find(x => x?.question)
62
+ : null;
63
+ return {
64
+ id: String(s?.id || ''),
65
+ messageCount: Number(s?.messageCount) || 0,
66
+ createdAt: toIsoDate(s?.createdAt),
67
+ endedAt: toIsoDate(s?.endedAt),
68
+ hasSummary: Boolean(summary),
69
+ topics: summary && Array.isArray(summary.topics) ? summary.topics.map(t => String(t)).slice(0, 8) : [],
70
+ preview: firstQuestion?.question ? String(firstQuestion.question).slice(0, 160) : null,
71
+ questionCount: summary?.questionCount ?? null,
72
+ durationMs: summary?.durationMs ?? null,
73
+ toolCalls: summary?.toolCalls ?? null,
74
+ };
75
+ }
76
+
77
+ function extractSessionExchanges(messages = [], limit = 5) {
78
+ const exchanges = [];
79
+ let pendingQuestion = null;
80
+ for (const m of Array.isArray(messages) ? messages : []) {
81
+ if (!m || typeof m !== 'object') continue;
82
+ const textPart = Array.isArray(m.parts) ? m.parts.find(p => p && p.type === 'text') : null;
83
+ const text = String(textPart?.content || m.content || m.text || '').trim();
84
+ if (!text) continue;
85
+ if (m.role === 'user') {
86
+ if (exchanges.length >= limit) break;
87
+ if (pendingQuestion) exchanges.push({ question: pendingQuestion, answerPreview: '' });
88
+ pendingQuestion = text.slice(0, 300);
89
+ } else if (m.role === 'assistant' && pendingQuestion) {
90
+ exchanges.push({ question: pendingQuestion, answerPreview: text.slice(0, 300) });
91
+ pendingQuestion = null;
92
+ }
93
+ }
94
+ if (pendingQuestion && exchanges.length < limit) {
95
+ exchanges.push({ question: pendingQuestion, answerPreview: '' });
96
+ }
97
+ return exchanges;
98
+ }
99
+
100
+ function sanitizeCategory(value) {
101
+ const cleaned = String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '').slice(0, 40);
102
+ return cleaned || 'general';
103
+ }
104
+
105
+ function sanitizeTags(value) {
106
+ if (!Array.isArray(value)) return [];
107
+ return value
108
+ .map(t => String(t || '').trim().slice(0, 30))
109
+ .filter(Boolean)
110
+ .slice(0, 10);
111
+ }
112
+
113
+ function sanitizeId(value) {
114
+ const id = String(value || '').trim();
115
+ if (!id || id.length > 120 || !/^[A-Za-z0-9_-]+$/.test(id)) return null;
116
+ return id;
117
+ }
118
+
119
+ export async function handleMemoryGetRoute(req, res) {
120
+ const mem = await ensureLoaded();
121
+ const facts = Array.isArray(mem.facts) ? mem.facts : [];
122
+ const status = typeof mem.getStatus === 'function' ? mem.getStatus() : {};
123
+ sendJson(res, 200, {
124
+ status: 'ok',
125
+ persistent: Boolean(mem.persistenceEnabled && !mem._storageError),
126
+ storageError: mem._storageError ? String(mem._storageError.code || mem._storageError.message || mem._storageError) : null,
127
+ factsCount: facts.length,
128
+ sessionsCount: Array.isArray(mem.sessions) ? mem.sessions.length : 0,
129
+ summariesCount: Array.isArray(mem.summaries) ? mem.summaries.length : 0,
130
+ factsByCategory: status.factsByCategory || {},
131
+ topTopics: Array.isArray(status.topTopics) ? status.topTopics : [],
132
+ totalFacts: facts.length,
133
+ facts: facts.slice(-MAX_FACTS_RESPONSE).map(serializeFact).reverse(),
134
+ sessions: (Array.isArray(mem.sessions) ? mem.sessions : []).slice(-50).map(serializeSession).reverse(),
135
+ });
136
+ }
137
+
138
+ export async function handleMemoryActionRoute(req, res) {
139
+ let body;
140
+ try {
141
+ body = await parseJsonBody(req, 64 * 1024);
142
+ } catch (err) {
143
+ return sendError(res, 400, `Invalid JSON body: ${err.message}`);
144
+ }
145
+
146
+ const action = String(body.action || '').toLowerCase();
147
+ const mem = await ensureLoaded();
148
+
149
+ if (action === 'add') {
150
+ const content = String(body.content || '').trim();
151
+ if (!content) return sendError(res, 400, 'content is required');
152
+ if (content.length > MAX_CONTENT_LENGTH) {
153
+ return sendError(res, 400, `content too long (max ${MAX_CONTENT_LENGTH} chars)`);
154
+ }
155
+ const entry = mem.addFact(content, {
156
+ category: sanitizeCategory(body.category),
157
+ tags: sanitizeTags(body.tags),
158
+ source: 'web-settings',
159
+ });
160
+ if (!entry) return sendError(res, 500, 'Failed to store fact');
161
+ return sendJson(res, 200, { status: 'ok', fact: serializeFact(entry), factsCount: mem.facts.length });
162
+ }
163
+
164
+ if (action === 'delete') {
165
+ const id = sanitizeId(body.id);
166
+ if (!id) return sendError(res, 400, 'valid id is required');
167
+ if (!mem.removeFact(id)) return sendError(res, 404, `Fact not found: ${id}`);
168
+ return sendJson(res, 200, { status: 'ok', id, factsCount: mem.facts.length });
169
+ }
170
+
171
+ if (action === 'search') {
172
+ const query = String(body.query || '').trim().slice(0, 500);
173
+ const limit = Math.min(Math.max(Number(body.limit) || 20, 1), 50);
174
+ const results = mem.searchFacts(query, { limit });
175
+ return sendJson(res, 200, {
176
+ status: 'ok',
177
+ query,
178
+ count: results.length,
179
+ facts: results.map(serializeFact),
180
+ });
181
+ }
182
+
183
+ if (action === 'session_summary') {
184
+ const sessionId = sanitizeId(body.sessionId);
185
+ if (!sessionId) return sendError(res, 400, 'valid sessionId is required');
186
+ let session = null;
187
+ try {
188
+ session = await mem.loadArchivedSession(sessionId);
189
+ } catch {
190
+ session = null;
191
+ }
192
+ if (!session || typeof session !== 'object') {
193
+ return sendError(res, 404, `Session not found or not archived: ${sessionId}`);
194
+ }
195
+ const summaryFromMemory = Array.isArray(mem.summaries)
196
+ ? mem.summaries.find(s => s?.sessionId === sessionId) || null
197
+ : null;
198
+ const summarySource = session.summary || summaryFromMemory;
199
+ const messages = Array.isArray(session.messages) ? session.messages : [];
200
+ const userMessageCount = messages.filter(m => m?.role === 'user').length;
201
+ const exchanges = extractSessionExchanges(messages, 5);
202
+ const fallbackExchanges = Array.isArray(summarySource?.keyExchanges)
203
+ ? summarySource.keyExchanges.slice(0, 5).map(x => ({
204
+ question: String(x?.question || '').slice(0, 300),
205
+ answerPreview: String(x?.answerPreview || '').slice(0, 300),
206
+ }))
207
+ : [];
208
+ const stats = session.stats && typeof session.stats === 'object' ? session.stats : {};
209
+ return sendJson(res, 200, {
210
+ status: 'ok',
211
+ sessionId,
212
+ messageCount: messages.length || Number(session.messageCount) || 0,
213
+ createdAt: toIsoDate(session.createdAt),
214
+ endedAt: toIsoDate(session.endedAt),
215
+ thinkingMode: session.thinkingMode ? String(session.thinkingMode) : null,
216
+ stats: (Number(stats.totalDurationMs) || Number(stats.totalToolCalls))
217
+ ? {
218
+ totalDurationMs: Number(stats.totalDurationMs) || 0,
219
+ totalToolCalls: Number(stats.totalToolCalls) || 0,
220
+ }
221
+ : null,
222
+ summary: {
223
+ topics: Array.isArray(summarySource?.topics) ? summarySource.topics.map(t => String(t)).slice(0, 10) : [],
224
+ questionCount: userMessageCount || Number(summarySource?.questionCount) || 0,
225
+ durationMs: Number(summarySource?.durationMs) || Number(stats.totalDurationMs) || 0,
226
+ toolCalls: Number(summarySource?.toolCalls) || Number(stats.totalToolCalls) || 0,
227
+ keyExchanges: exchanges.length ? exchanges : fallbackExchanges,
228
+ },
229
+ });
230
+ }
231
+
232
+ if (action === 'export') {
233
+ const facts = Array.isArray(mem.facts) ? mem.facts : [];
234
+ return sendJson(res, 200, {
235
+ status: 'ok',
236
+ exportedAt: new Date().toISOString(),
237
+ facts: facts.map(serializeFact),
238
+ preferences: mem.preferences || {},
239
+ sessionsCount: Array.isArray(mem.sessions) ? mem.sessions.length : 0,
240
+ summariesCount: Array.isArray(mem.summaries) ? mem.summaries.length : 0,
241
+ });
242
+ }
243
+
244
+ if (action === 'clear') {
245
+ if (!isWebChatAdmin(req)) {
246
+ return sendError(res, 401, 'Admin API key required to wipe memory');
247
+ }
248
+ await mem.clearAll();
249
+ return sendJson(res, 200, { status: 'ok', factsCount: 0 });
250
+ }
251
+
252
+ return sendError(res, 400, 'action must be one of: add, delete, search, session_summary, export, clear');
253
+ }