kiku-agent 1.3.2 → 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 (152) hide show
  1. package/README.md +69 -12
  2. package/package.json +2 -1
  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/modules/evaluation/runner.js +4 -14
  18. package/server/agent/eval/modules/evaluation/scoring.js +15 -3
  19. package/server/agent/eval/modules/server/evalApi.js +5 -5
  20. package/server/agent/eval/modules/ui/evalUi.js +7 -4
  21. package/server/agent/eventBus.js +1 -0
  22. package/server/agent/ink/main.jsx +196 -191
  23. package/server/agent/jobManager.js +11 -1
  24. package/server/agent/lanDiscovery.js +721 -23
  25. package/server/agent/llmClient.js +317 -71
  26. package/server/agent/lmStudioWakeup.js +517 -65
  27. package/server/agent/locationService.js +797 -0
  28. package/server/agent/mcpQuizData.js +509 -0
  29. package/server/agent/memory/contextAllocator.js +19 -7
  30. package/server/agent/memory.js +3 -1
  31. package/server/agent/modelCompatibility.js +352 -0
  32. package/server/agent/modelDefaults.js +1 -0
  33. package/server/agent/modelManager.js +46 -2
  34. package/server/agent/newsWatch.js +9 -3
  35. package/server/agent/processManager.js +229 -0
  36. package/server/agent/prompts.js +174 -8
  37. package/server/agent/routingInterceptor.js +9 -1
  38. package/server/agent/sanitizer.js +41 -6
  39. package/server/agent/searchQueryPlanner.js +202 -0
  40. package/server/agent/session.js +59 -4
  41. package/server/agent/tests/agentFollowUps.test.js +128 -2
  42. package/server/agent/tests/agentMusicControl.test.js +245 -0
  43. package/server/agent/tests/apiRouter.test.js +1 -1
  44. package/server/agent/tests/autoContext.test.js +176 -0
  45. package/server/agent/tests/bilibiliEmbed.test.js +45 -0
  46. package/server/agent/tests/chatHardening.test.js +121 -0
  47. package/server/agent/tests/chineseWebSearchEscalation.test.js +62 -0
  48. package/server/agent/tests/clarifyingQuestions.test.js +33 -0
  49. package/server/agent/tests/cliAutomation.test.js +38 -1
  50. package/server/agent/tests/cloudProviders.test.js +91 -3
  51. package/server/agent/tests/commands.test.js +92 -3
  52. package/server/agent/tests/contextIntelligence.test.js +50 -3
  53. package/server/agent/tests/cpuMetrics.test.js +101 -0
  54. package/server/agent/tests/decisionAndSearchEnhancements.test.js +32 -0
  55. package/server/agent/tests/edgeCasesScenarios.test.js +82 -0
  56. package/server/agent/tests/engineService.test.js +436 -0
  57. package/server/agent/tests/engineUnload.test.js +94 -0
  58. package/server/agent/tests/envCommand.test.js +69 -0
  59. package/server/agent/tests/evalMetrics.test.js +8 -0
  60. package/server/agent/tests/fetchApi.test.js +99 -0
  61. package/server/agent/tests/hybridWebSearch.test.js +2 -0
  62. package/server/agent/tests/lanDiscovery.test.js +5 -0
  63. package/server/agent/tests/lanInterfaces.test.js +510 -0
  64. package/server/agent/tests/lmStudioWakeup.test.js +278 -0
  65. package/server/agent/tests/locationTool.test.js +278 -0
  66. package/server/agent/tests/memoryRoute.test.js +173 -0
  67. package/server/agent/tests/memoryUiNormalization.test.js +75 -0
  68. package/server/agent/tests/modelCompatibility.test.js +148 -0
  69. package/server/agent/tests/modelManager.test.js +32 -0
  70. package/server/agent/tests/musicControlUiBridge.test.js +113 -0
  71. package/server/agent/tests/nextGenArchitecture.test.js +4 -2
  72. package/server/agent/tests/openDomainPrompt.test.js +197 -0
  73. package/server/agent/tests/openDomainSearch.test.js +121 -0
  74. package/server/agent/tests/processManager.test.js +144 -0
  75. package/server/agent/tests/providerPersistence.test.js +10 -5
  76. package/server/agent/tests/quickActionsAndModals.test.js +180 -0
  77. package/server/agent/tests/searchQueryPlanner.test.js +108 -0
  78. package/server/agent/tests/securityAuditRemediation.test.js +16 -9
  79. package/server/agent/tests/securityAuditRemediations2.test.js +154 -0
  80. package/server/agent/tests/securityHardening.test.js +4 -4
  81. package/server/agent/tests/sessionAndCopy.test.js +39 -4
  82. package/server/agent/tests/sidebarControls.test.js +34 -0
  83. package/server/agent/tests/statefulMemory.test.js +8 -1
  84. package/server/agent/tests/textProtocolLoop.test.js +71 -0
  85. package/server/agent/tests/textToolParsing.test.js +48 -0
  86. package/server/agent/tests/toolRoutingRegressions.test.js +41 -0
  87. package/server/agent/tests/unifiedEndpoints.test.js +13 -6
  88. package/server/agent/tests/v110Features.test.js +1 -1
  89. package/server/agent/tests/vramEstimator.test.js +75 -0
  90. package/server/agent/tests/webChatDocs.test.js +199 -0
  91. package/server/agent/tests/webChatInferenceControl.test.js +298 -0
  92. package/server/agent/tests/webChatInspection.test.js +256 -5
  93. package/server/agent/tests/webChatMcp.test.js +94 -0
  94. package/server/agent/tests/webExtract.test.js +23 -0
  95. package/server/agent/tests/webSources.test.js +46 -12
  96. package/server/agent/textToolProtocol.js +173 -46
  97. package/server/agent/toolRouter.js +318 -44
  98. package/server/agent/tools.js +877 -96
  99. package/server/agent/ui.js +39 -34
  100. package/server/agent/vramService.js +35 -54
  101. package/server/agent/webSearch.js +441 -162
  102. package/server/agent/websearch/orchestrator.js +15 -5
  103. package/server/agent/websearch/resilience/cache.js +9 -2
  104. package/server/agent/websearch/security/ssrf.js +41 -10
  105. package/server/agent/youtubeAuth.js +5 -20
  106. package/server/apiRouter.js +5558 -4422
  107. package/server/mcp/data/discography.js +2 -2
  108. package/server/mcp/examples/antigravity.example.json +17 -0
  109. package/server/mcp/handler.js +8 -4
  110. package/server/mcp/security.js +220 -21
  111. package/server/mcp/server.js +2 -1
  112. package/server/mcp/tests/connection.test.js +14 -8
  113. package/server/mcp/tests/mcpKeyAuth.test.js +156 -0
  114. package/server/mcp/tools.js +7 -1
  115. package/server/supabase_migration_prefix.sql +102 -0
  116. package/server/supabase_schema.sql +18 -18
  117. package/server/web-chat/core/connection.js +179 -218
  118. package/server/web-chat/core/conversationContext.js +9 -2
  119. package/server/web-chat/core/directCatalogEngine.js +52 -28
  120. package/server/web-chat/core/dramaKnowledge.js +33 -22
  121. package/server/web-chat/core/followUpEngine.js +735 -81
  122. package/server/web-chat/core/futureEventGuard.js +5 -1
  123. package/server/web-chat/core/musicControlAction.js +70 -0
  124. package/server/web-chat/core/security.js +32 -21
  125. package/server/web-chat/core/toolInspector.js +6 -6
  126. package/server/web-chat/core/urlGuard.js +45 -0
  127. package/server/web-chat/router.js +204 -157
  128. package/server/web-chat/routes/chatRoute.js +885 -488
  129. package/server/web-chat/routes/engineRoute.js +270 -0
  130. package/server/web-chat/routes/exportRoute.js +1 -1
  131. package/server/web-chat/routes/lanDiscoveryRoute.js +17 -2
  132. package/server/web-chat/routes/lmStudioLoadRoute.js +172 -0
  133. package/server/web-chat/routes/mcpRoute.js +386 -0
  134. package/server/web-chat/routes/memoryRoute.js +253 -0
  135. package/server/web-chat/routes/providerRoute.js +49 -16
  136. package/server/web-chat/routes/staticRoute.js +306 -17
  137. package/server/web-chat/routes/statusRoute.js +194 -73
  138. package/server/web-chat/routes/suggestRoute.js +11 -3
  139. package/server/web-chat/routes/verifyRoute.js +26 -4
  140. package/server/web-chat/server.js +7 -6
  141. package/server/web-chat/ui/docsBuilder.js +3558 -0
  142. package/server/web-chat/ui/htmlBuilder.js +12 -0
  143. package/server/web-chat/ui/scripts/client.js +3401 -1302
  144. package/server/web-chat/ui/scripts/webchatI18n.js +155 -153
  145. package/server/web-chat/ui/settingsBuilder.js +7870 -0
  146. package/server/web-chat/ui/styles/lan-card.css +165 -0
  147. package/server/web-chat/ui/styles/main.css +4289 -447
  148. package/server/web-chat/ui/sw.js +64 -0
  149. package/server/web-chat/ui/templates/body.html +407 -281
  150. package/src/data/awardsData.js +146 -146
  151. package/src/data/musicVideoData.js +6 -4
  152. package/src/lib/vram/estimator.js +195 -19
@@ -1,73 +1,194 @@
1
- import { toolsSchema } from '../../agent/tools.js';
2
- import { resolveAgentConnection, currentAgent } from '../core/connection.js';
3
- import { config, CLOUD_PROVIDERS, getProviderBaseUrl, getUnifiedProviderCatalog } from '../../agent/config.js';
4
- import { getAgentMemorySnapshot } from '../core/memorySnapshot.js';
5
- import { sendJson, sendError } from '../core/errorHandler.js';
6
- import { extractApiKey } from '../core/security.js';
7
-
8
- export async function handleStatusRoute(req, res, urlObj, port) {
9
- try {
10
- const queryProvider = urlObj.searchParams.get('provider');
11
- const queryModel = urlObj.searchParams.get('model');
12
- const queryApiKey = extractApiKey(req);
13
- const querySessionId = urlObj.searchParams.get('sessionId');
14
- const queryBaseUrl = urlObj.searchParams.get('baseUrl');
15
-
16
- const conn = await resolveAgentConnection({
17
- provider: queryProvider,
18
- model: queryModel,
19
- apiKey: queryApiKey,
20
- sessionId: querySessionId,
21
- baseUrl: queryBaseUrl || undefined,
22
- });
23
-
24
- const totalTools = conn.agent?.getAllToolsSchema?.().length || toolsSchema.length;
25
- const serverDefaultProvider = currentAgent?.client?.provider || config.activeProvider || 'lmstudio';
26
- const serverDefaultModel = currentAgent?.client?.model || config.providers?.[serverDefaultProvider]?.model || 'google/gemma-4-e4b';
27
- const serverDefaultProviderName = currentAgent?.client?.providerName || (CLOUD_PROVIDERS?.[serverDefaultProvider]?.name) || serverDefaultProvider;
28
- const serverDefaultBaseUrl = currentAgent?.client?.baseUrl || getProviderBaseUrl(serverDefaultProvider);
29
- let localEndpoints = null;
30
- let providerCatalog = null;
31
- try {
32
- const catalog = getUnifiedProviderCatalog();
33
- localEndpoints = catalog.localEndpoints;
34
- providerCatalog = catalog.providers;
35
- } catch {}
36
-
37
- sendJson(res, 200, {
38
- status: 'ok',
39
- server: 'Kiku Web Chatbot',
40
- port,
41
- connected: conn.ready,
42
- state: conn.ready ? 'ready' : (conn.state || 'server_offline'),
43
- provider: conn.provider,
44
- model: conn.model,
45
- activeModel: conn.model,
46
- activeLoadedModel: conn.resolution?.activeLoadedModel || conn.model,
47
- loadedModels: conn.models || [],
48
- usedFallback: conn.usedFallback || false,
49
- serverDefaultProvider,
50
- serverDefaultModel,
51
- serverDefaultProviderName,
52
- serverDefaultBaseUrl,
53
- localEndpoints,
54
- providers: providerCatalog,
55
- hasServerAgent: Boolean(currentAgent),
56
- error: conn.error || null,
57
- capabilities: {
58
- reactAgent: Boolean(conn.agent),
59
- streaming: true,
60
- toolCalling: Boolean(conn.agent),
61
- localTools: toolsSchema.length,
62
- mcpTools: Math.max(0, totalTools - toolsSchema.length),
63
- totalTools,
64
- sessionMemory: Boolean(conn.agent),
65
- longTermMemory: Boolean(conn.agent?.memory),
66
- cachedAgent: conn.cached === true,
67
- },
68
- memory: getAgentMemorySnapshot(conn.agent, querySessionId)
69
- });
70
- } catch (err) {
71
- sendError(res, 500, err.message);
72
- }
73
- }
1
+ import { credentialFingerprint } from '../core/connection.js';
2
+ import { toolsSchema } from '../../agent/tools.js';
3
+ import { resolveAgentConnection, currentAgent } from '../core/connection.js';
4
+ import { config, CLOUD_PROVIDERS, getProviderBaseUrl, getUnifiedProviderCatalog, getPersistedInferenceSettings, getProvider } from '../../agent/config.js';
5
+ import { getAgentMemorySnapshot } from '../core/memorySnapshot.js';
6
+ import { sendJson, sendError } from '../core/errorHandler.js';
7
+ import { extractApiKey, isWebChatAdmin } from '../core/security.js';
8
+ import { validateInferenceBaseUrl, isTrustedRequest, sanitizeInferenceError } from '../core/urlGuard.js';
9
+
10
+ // Full /api/status builds an agent and probes the upstream server; dedupe
11
+ // bursts (chat-page poll + settings page) behind a tiny TTL cache.
12
+ const FULL_STATUS_CACHE_TTL_MS = 3_000;
13
+ let fullStatusCache = { key: null, at: 0, payload: null };
14
+
15
+ function fullStatusCacheKey({ provider, model, baseUrl, apiKey }) {
16
+ return [provider || 'auto', model || 'auto', baseUrl || '-', credentialFingerprint(apiKey)].join('|');
17
+ }
18
+
19
+ export async function handleStatusRoute(req, res, urlObj, port) {
20
+ try {
21
+ const queryProvider = urlObj.searchParams.get('provider');
22
+ const queryModel = urlObj.searchParams.get('model');
23
+ const queryApiKey = extractApiKey(req);
24
+ const querySessionId = urlObj.searchParams.get('sessionId');
25
+ const queryBaseUrl = urlObj.searchParams.get('baseUrl');
26
+ const light = urlObj.searchParams.get('light') === '1';
27
+
28
+ if (queryProvider && queryProvider !== 'auto' && !getProvider(queryProvider)) return sendError(res, 400, 'Unknown provider');
29
+ if (queryBaseUrl) {
30
+ const guard = await validateInferenceBaseUrl(queryBaseUrl, { trusted: isTrustedRequest(req) });
31
+ if (!guard.ok) {
32
+ return sendError(res, 400, `Invalid baseUrl: ${guard.reason}`);
33
+ }
34
+ }
35
+
36
+ if (light) {
37
+ return sendJson(res, 200, await buildLightStatus({ port, provider: queryProvider, model: queryModel, baseUrl: queryBaseUrl }));
38
+ }
39
+
40
+ const cacheKey = String(isTrustedRequest(req)) + '|' + querySessionId + '|' + fullStatusCacheKey({ provider: queryProvider, model: queryModel, baseUrl: queryBaseUrl, apiKey: queryApiKey });
41
+ const now = Date.now();
42
+ if (fullStatusCache.key === cacheKey && now - fullStatusCache.at < FULL_STATUS_CACHE_TTL_MS) {
43
+ return sendJson(res, 200, { ...fullStatusCache.payload, cached: true });
44
+ }
45
+
46
+ const conn = await resolveAgentConnection({
47
+ provider: queryProvider,
48
+ model: queryModel,
49
+ apiKey: isWebChatAdmin(req) ? undefined : queryApiKey,
50
+ callerIsTrusted: isTrustedRequest(req),
51
+ sessionId: querySessionId,
52
+ baseUrl: queryBaseUrl || undefined,
53
+ // Auto-starting LM Studio from a status GET is a side effect; only trusted
54
+ // (loopback/admin) callers may wake the local engine this way.
55
+ allowAutoStart: isTrustedRequest(req),
56
+ });
57
+
58
+ const payload = await buildFullStatus({ conn, queryProvider, queryModel, queryApiKey, querySessionId, port });
59
+ payload.error = payload.error ? sanitizeInferenceError(payload.error, isTrustedRequest(req)) : null;
60
+ fullStatusCache = { key: cacheKey, at: Date.now(), payload };
61
+ sendJson(res, 200, payload);
62
+ } catch (err) {
63
+ sendError(res, err.statusCode || 502, sanitizeInferenceError(err, isTrustedRequest(req)));
64
+ }
65
+ }
66
+
67
+ async function buildLightStatus({ port, provider, model, baseUrl }) {
68
+ const serverProvider = currentAgent?.client?.provider || config.activeProvider || 'lmstudio';
69
+ const activeBaseUrl = (typeof baseUrl === 'string' && baseUrl.trim())
70
+ ? baseUrl.trim().replace(/\/+$/, '')
71
+ : (currentAgent?.client?.baseUrl || getProviderBaseUrl(provider || serverProvider));
72
+
73
+ const connected = Boolean(currentAgent?.client && currentAgent.client.provider === (provider && provider !== 'auto' ? provider : serverProvider) && (!baseUrl || currentAgent.client.baseUrl === baseUrl));
74
+ let localEndpoints = null;
75
+ let providerCatalog = null;
76
+ try {
77
+ const catalog = getUnifiedProviderCatalog();
78
+ localEndpoints = catalog.localEndpoints;
79
+ providerCatalog = catalog.providers;
80
+ } catch {}
81
+
82
+ const serverDefaultModel = currentAgent?.client?.model || config.providers?.[serverProvider]?.model || 'google/gemma-4-e4b';
83
+ return {
84
+ status: 'ok',
85
+ server: 'Kiku Web Chatbot',
86
+ port,
87
+ light: true,
88
+ connected,
89
+ state: connected ? 'unprobed' : 'unknown',
90
+ healthVerified: false,
91
+ provider: serverProvider,
92
+ model: serverDefaultModel,
93
+ activeModel: serverDefaultModel,
94
+ loadedModels: [],
95
+ usedFallback: false,
96
+ serverDefaultProvider: serverProvider,
97
+ serverDefaultModel,
98
+ serverDefaultProviderName: currentAgent?.client?.providerName || CLOUD_PROVIDERS?.[serverProvider]?.name || serverProvider,
99
+ serverDefaultBaseUrl: currentAgent?.client?.baseUrl || getProviderBaseUrl(serverProvider),
100
+ contextLimit: getPersistedInferenceSettings().contextLimit,
101
+ inference: getPersistedInferenceSettings(),
102
+ localEndpoints,
103
+ providers: providerCatalog,
104
+ hasServerAgent: Boolean(currentAgent),
105
+ error: null,
106
+ capabilities: {
107
+ reactAgent: Boolean(currentAgent),
108
+ streaming: true,
109
+ toolCalling: Boolean(currentAgent),
110
+ localTools: toolsSchema.length,
111
+ mcpTools: 0,
112
+ totalTools: toolsSchema.length,
113
+ sessionMemory: Boolean(currentAgent),
114
+ longTermMemory: Boolean(currentAgent?.memory),
115
+ cachedAgent: false,
116
+ light: true,
117
+ },
118
+ mcp: {
119
+ endpoint: '/mcp',
120
+ protocolVersion: '2025-11-25',
121
+ transport: 'streamable-http',
122
+ online: true,
123
+ },
124
+ memory: null,
125
+ };
126
+ }
127
+
128
+ async function buildFullStatus({ conn, queryProvider, queryModel, queryApiKey, querySessionId, port }) {
129
+ const totalTools = conn.agent?.getAllToolsSchema?.().length || toolsSchema.length;
130
+ const serverDefaultProvider = currentAgent?.client?.provider || config.activeProvider || 'lmstudio';
131
+ const serverDefaultModel = currentAgent?.client?.model || config.providers?.[serverDefaultProvider]?.model || 'google/gemma-4-e4b';
132
+ const serverDefaultProviderName = currentAgent?.client?.providerName || (CLOUD_PROVIDERS?.[serverDefaultProvider]?.name) || serverDefaultProvider;
133
+ const serverDefaultBaseUrl = currentAgent?.client?.baseUrl || getProviderBaseUrl(serverDefaultProvider);
134
+ let localEndpoints = null;
135
+ let providerCatalog = null;
136
+ try {
137
+ const catalog = getUnifiedProviderCatalog();
138
+ localEndpoints = catalog.localEndpoints;
139
+ providerCatalog = catalog.providers;
140
+ } catch {}
141
+
142
+ const isLocal = conn.provider === 'lmstudio' || conn.provider === 'vllm';
143
+ const rawActive = conn.resolution?.activeLoadedModel || null;
144
+ const isModelLoaded = isLocal ? Boolean(rawActive) : Boolean(conn.ready);
145
+ const activeLoadedModel = rawActive || (!isLocal ? conn.model : null);
146
+ const loadedModels = conn.resolution?.loadedModels || (rawActive ? [rawActive] : (!isLocal ? conn.models || [] : []));
147
+ const effectiveState = !isModelLoaded && isLocal ? 'server_online_model_not_loaded' : (conn.ready ? 'ready' : (conn.state || 'server_offline'));
148
+
149
+ return {
150
+ status: 'ok',
151
+ server: 'Kiku Web Chatbot',
152
+ port,
153
+ light: false,
154
+ connected: conn.ready,
155
+ isModelLoaded,
156
+ state: effectiveState,
157
+ provider: conn.provider,
158
+ model: conn.model,
159
+ activeModel: conn.model,
160
+ activeLoadedModel,
161
+ loadedModels,
162
+ usedFallback: conn.usedFallback || false,
163
+ serverDefaultProvider,
164
+ serverDefaultModel,
165
+ serverDefaultProviderName,
166
+ serverDefaultBaseUrl,
167
+ // Effective engine context-budget (CLI KIKU_CONTEXT_LIMIT env; 0 = unset).
168
+ // The /settings Context Window slider syncs against this value.
169
+ contextLimit: getPersistedInferenceSettings().contextLimit,
170
+ inference: getPersistedInferenceSettings(),
171
+ localEndpoints,
172
+ providers: providerCatalog,
173
+ hasServerAgent: Boolean(currentAgent),
174
+ error: conn.error || null,
175
+ capabilities: {
176
+ reactAgent: Boolean(conn.agent),
177
+ streaming: true,
178
+ toolCalling: Boolean(conn.agent),
179
+ localTools: toolsSchema.length,
180
+ mcpTools: Math.max(0, totalTools - toolsSchema.length),
181
+ totalTools,
182
+ sessionMemory: Boolean(conn.agent),
183
+ longTermMemory: Boolean(conn.agent?.memory),
184
+ cachedAgent: conn.cached === true,
185
+ },
186
+ mcp: {
187
+ endpoint: '/mcp',
188
+ protocolVersion: '2025-11-25',
189
+ transport: 'streamable-http',
190
+ online: true,
191
+ },
192
+ memory: null
193
+ };
194
+ }
@@ -1,8 +1,10 @@
1
1
  import { executeTool } from '../../agent/tools.js';
2
2
  import { CATEGORIZED_SUGGESTIONS, DEFAULT_SUGGESTIONS } from '../ui/scripts/suggestionsData.js';
3
- import { resolveAgentConnection } from '../core/connection.js';
3
+ import { config, getProvider } from '../../agent/config.js';
4
+ import { isTrustedRequest } from '../core/urlGuard.js';
5
+ import { resolveAgentConnection, currentAgent } from '../core/connection.js';
4
6
  import { parseJsonBody, sendJson, sendError } from '../core/errorHandler.js';
5
- import { extractApiKey } from '../core/security.js';
7
+ import { extractApiKey, isWebChatAdmin } from '../core/security.js';
6
8
 
7
9
  export async function handleSuggestionsRoute(req, res) {
8
10
  sendJson(res, 200, {
@@ -30,6 +32,9 @@ export async function handleAiSuggestRoute(req, res) {
30
32
  body = await parseJsonBody(req);
31
33
  } catch {}
32
34
 
35
+ const provider = (!body.provider || body.provider === 'auto') ? (currentAgent?.client?.provider || config.activeProvider) : body.provider;
36
+ if (!getProvider(provider)) return sendError(res, 400, 'Unknown provider');
37
+ if (!['lmstudio', 'vllm', 'custom'].includes(provider) && !isTrustedRequest(req)) return sendError(res, 403, 'Cloud inference requires local or admin access');
33
38
  const { topic = '' } = body;
34
39
  const limit = Math.min(Math.max(Number(body.limit) || 5, 2), 8);
35
40
  const lang = AI_SUGGEST_LANGS.has(String(body.lang || '').toLowerCase())
@@ -43,7 +48,10 @@ export async function handleAiSuggestRoute(req, res) {
43
48
  const conn = await resolveAgentConnection({
44
49
  provider: body.provider,
45
50
  model: body.model,
46
- apiKey,
51
+ apiKey: isWebChatAdmin(req) ? undefined : apiKey,
52
+ baseUrl: body.baseUrl,
53
+ callerIsTrusted: isTrustedRequest(req),
54
+ allowAutoStart: isTrustedRequest(req),
47
55
  sessionId: body.sessionId,
48
56
  });
49
57
  const controller = new AbortController();
@@ -1,5 +1,6 @@
1
1
  import { executeTool } from '../../agent/tools.js';
2
2
  import { parseJsonBody, sendJson, sendError } from '../core/errorHandler.js';
3
+ import { extractWordTokens, buildTermFrequencyVector, computeCosineSimilarity } from '../../agent/memory/contextAllocator.js';
3
4
 
4
5
  export async function handleVerifyRoute(req, res) {
5
6
  let body;
@@ -23,10 +24,31 @@ export async function handleVerifyRoute(req, res) {
23
24
  ? rawResults
24
25
  : Object.values(rawResults || {}).flat().filter(item => typeof item === 'object');
25
26
 
26
- const hasDirectMatch = flatItems.some(r =>
27
- String(r.title || r.name || '').toLowerCase().includes(claim.toLowerCase()) ||
28
- String(r.description || r.synopsis || '').toLowerCase().includes(claim.toLowerCase())
29
- );
27
+ const claimLower = claim.toLowerCase().trim();
28
+ const claimTokens = extractWordTokens(claim);
29
+ const claimVec = buildTermFrequencyVector(claimTokens);
30
+
31
+ let bestScore = 0;
32
+ for (const r of flatItems) {
33
+ const title = String(r.title || r.name || r.titleTh || r.titleCn || '').toLowerCase();
34
+ if (title && (claimLower.includes(title) || title.includes(claimLower))) {
35
+ bestScore = Math.max(bestScore, 0.8);
36
+ }
37
+ const itemText = [
38
+ r.title || r.name || '',
39
+ r.titleCn || '',
40
+ r.titleTh || '',
41
+ r.role || '',
42
+ r.year || '',
43
+ r.description || r.synopsis || ''
44
+ ].join(' ');
45
+ const itemTokens = extractWordTokens(itemText);
46
+ const itemVec = buildTermFrequencyVector(itemTokens);
47
+ const sim = computeCosineSimilarity(claimVec, itemVec);
48
+ if (sim > bestScore) bestScore = sim;
49
+ }
50
+
51
+ const hasDirectMatch = bestScore >= 0.25;
30
52
 
31
53
  result = {
32
54
  verdict: hasDirectMatch ? 'PARTIALLY_VERIFIED' : 'UNVERIFIED',
@@ -1,7 +1,9 @@
1
+ import '../agent/envBootstrap.js';
1
2
  import http from 'node:http';
2
3
  import { handleWebChatRequest } from './router.js';
3
- import { currentAgent, setCurrentAgent, webAgentCache } from './core/connection.js';
4
+ import { currentAgent, setCurrentAgent } from './core/connection.js';
4
5
  import { config } from '../agent/config.js';
6
+ import { KIKU_DEFAULT_MODEL } from '../agent/modelDefaults.js';
5
7
 
6
8
  let activeServer = null;
7
9
  let activePort = Number(process.env.PORT_WEB_CHAT || 3002);
@@ -25,7 +27,7 @@ export function getWebChatServerStatus() {
25
27
  port: activePort,
26
28
  url: formatServerUrl(activeHost, activePort),
27
29
  provider: currentAgent?.client?.provider || config.activeProvider || 'lmstudio',
28
- model: currentAgent?.client?.model || 'google/gemma-4-e4b',
30
+ model: currentAgent?.client?.model || KIKU_DEFAULT_MODEL,
29
31
  };
30
32
  }
31
33
 
@@ -36,10 +38,9 @@ export function stopWebChatServer() {
36
38
  try { activeServer.closeAllConnections(); } catch {}
37
39
  }
38
40
  activeServer.close(async () => {
39
- await Promise.allSettled(
40
- Array.from(webAgentCache.values(), connection => connection.agent?.saveSession?.())
41
- );
42
- webAgentCache.clear();
41
+ if (currentAgent?.saveSession) {
42
+ try { await currentAgent.saveSession(); } catch {}
43
+ }
43
44
  activeServer = null;
44
45
  resolve(true);
45
46
  });