kiku-agent 1.0.11 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +300 -495
- package/package.json +12 -11
- package/public/fonts/GoogleSans-Bold.ttf +0 -0
- package/public/fonts/GoogleSans-Bold.woff2 +0 -0
- package/public/fonts/GoogleSans-BoldItalic.ttf +0 -0
- package/public/fonts/GoogleSans-Italic.ttf +0 -0
- package/public/fonts/GoogleSans-Latin-400.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-500.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-600.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-700.woff2 +0 -0
- package/public/fonts/GoogleSans-Medium.ttf +0 -0
- package/public/fonts/GoogleSans-Medium.woff2 +0 -0
- package/public/fonts/GoogleSans-Regular.ttf +0 -0
- package/public/fonts/GoogleSans-Regular.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-400.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-500.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-600.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-700.woff2 +0 -0
- package/public/fonts/NotoSansThai-Bold.woff2 +0 -0
- package/public/fonts/NotoSansThai-Regular.woff2 +0 -0
- package/public/fonts/google-sans-official.css +900 -0
- package/public/images/0040jbadgy1id6am4yxqfj61s82dn1kx02.jpg +0 -0
- package/public/images/0040jbadgy1id6am708pbj62c0340qv602.jpg +0 -0
- package/public/images/0040jbadgy1id6amb81uqj62c0340x6q02.jpg +0 -0
- package/public/images/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
- package/public/images/0040jbadgy1id6amdg3xcj62b232wqv502.jpg +0 -0
- package/public/images/0040jbadgy1id6amexv23j620v2pau0x02.jpg +0 -0
- package/public/images/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
- package/public/images/008ET7Legy1ig9s89nri5j30i20n1gn4.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8a2lmlj30i20ny409.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8ay1hsj30i20nk3zv.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8bab6sj30i20o7go1.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8blqsxj30i20oiq4r.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8cbxkzj30i20mxmzh.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-399915.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-424041.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-588166.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-672198.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-700522.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-839722.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026825-460586.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/263202694-689407.jpg +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-399915.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-424041.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-588166.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-672198.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-700522.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-839722.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026825-460586.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/263202694-689407.mp4 +0 -0
- package/public/images/general/0040jbadgy1id6am4yxqfj61s82dn1kx02.jpg +0 -0
- package/public/images/general/0040jbadgy1id6am708pbj62c0340qv602.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amb81uqj62c0340x6q02.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amdg3xcj62b232wqv502.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amexv23j620v2pau0x02.jpg +0 -0
- package/public/images/general/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s89nri5j30i20n1gn4.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8a2lmlj30i20ny409.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8ay1hsj30i20nk3zv.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8bab6sj30i20o7go1.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8blqsxj30i20oiq4r.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8cbxkzj30i20mxmzh.jpg +0 -0
- package/public/images/general/jujingyi_portrait.jpg +0 -0
- package/public/images/jujingyi_portrait.jpg +0 -0
- package/public/images/series/a-millenniums-wait/poster.jpg +0 -0
- package/public/images/series/beauty-of-resilience/poster.jpg +0 -0
- package/public/images/series/detective-samoyeds/poster.jpg +0 -0
- package/public/images/series/everyday/poster.jpg +0 -0
- package/public/images/series/fu-rong-ancient-painting/poster.jpg +0 -0
- package/public/images/series/in-a-class-of-her-own/poster.jpg +0 -0
- package/public/images/series/in-blossom/poster.jpg +0 -0
- package/public/images/series/legend-of-yunxi/poster.jpg +0 -0
- package/public/images/series/lets-fight/poster-2.jpg +0 -0
- package/public/images/series/lets-fight/poster.jpg +0 -0
- package/public/images/series/love-emergency/poster.jpg +0 -0
- package/public/images/series/love-under-the-full-moon/poster.jpg +0 -0
- package/public/images/series/money-bastion/poster.jpg +0 -0
- package/public/images/series/mr-swimmer/poster.jpg +0 -0
- package/public/images/series/novoland-castle-in-the-sky/poster.jpg +0 -0
- package/public/images/series/please-give-me-a-pair-of-wings/poster.jpg +0 -0
- package/public/images/series/rebirth-for-you/poster.jpg +0 -0
- package/public/images/series/sword-and-fairy-4/poster.jpg +0 -0
- package/public/images/series/the-blooms-at-ruyi-pavilion/poster.jpg +0 -0
- package/public/images/series/the-eternal-fragrance/poster-2.jpg +0 -0
- package/public/images/series/the-eternal-fragrance/poster.jpg +0 -0
- package/public/images/series/the-fallen-flowers/poster.jpg +0 -0
- package/public/images/series/the-legend-of-white-snake/poster.jpg +0 -0
- package/public/images/series/veil-of-shadows/poster.jpg +0 -0
- package/public/images/series/wanhua-sin/poster-2.jpg +0 -0
- package/public/images/series/wanhua-sin/poster.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj10yogj62u03s0npe02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj2pdvyj61li25jkjl02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj71177j62672w9e8302.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj7twpfj62192197rw02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj8jfu8j62c03401kx02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj9dhdsj622k2l77wh02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkja31s4j62bw3407r802.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjcf331j61402ey1kx02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjdx8gqj62602pihdt02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjfvxnzj62432n57wh02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixniv2yj627y4fxb2b02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixsvbdaj625k2vfu1102.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixxxdaoj62c03501l202.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixzeef9j62c0340npd02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3iy0vs6tj626t4rbhdu02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3iy21wwuj62bt34hx6q02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly4wr85j62z629y7wh02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly67oilj623u2t5npd02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly9qtw8j62c0340u0x02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlyem4m4j63402c01kz02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlygkoq4j627s2yeavd02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlyjh0daj62bw340nm102.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlylejlhj62bw3401kx02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiaa8b7fj627j2y67wh02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiagw3ezj61s02ebu0x02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiaivng3j61rv2ebx6p02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiajuixvj62bw340qv502.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiapyartj62bw340hdt02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaibkwu4ij61vy2iq1kx02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaic1fvkpj61u92eo1ky02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaic3cruhj62822ywb2902.jpg +0 -0
- package/public/manifest.json +25 -0
- package/public/mcp-logo-128.png +0 -0
- package/public/mcp-logo-64.png +0 -0
- package/public/mcp-logo.svg +16 -0
- package/server/agent/agentCore.js +542 -74
- package/server/agent/appPaths.js +1 -0
- package/server/agent/audioPlayer.js +4 -2
- package/server/agent/citations.js +294 -0
- package/server/agent/clarificationAnalytics.js +477 -0
- package/server/agent/clarifyingQuestions.js +1089 -0
- package/server/agent/cli.js +549 -125
- package/server/agent/commandFeatures.js +1 -0
- package/server/agent/commandRegistry.js +946 -879
- package/server/agent/commands.js +546 -272
- package/server/agent/config.js +581 -412
- package/server/agent/contextCompactor.js +2 -2
- package/server/agent/debate.js +235 -0
- package/server/agent/eventBus.js +3 -0
- package/server/agent/feedbackLearning.js +217 -0
- package/server/agent/llmClient.js +512 -394
- package/server/agent/mcpClient.js +182 -32
- package/server/agent/memory.js +283 -224
- package/server/agent/modelManager.js +79 -39
- package/server/agent/profiles.js +178 -0
- package/server/agent/publicTools.js +22 -0
- package/server/agent/queryDetails.js +68 -8
- package/server/agent/quizData.js +398 -384
- package/server/agent/research.js +121 -79
- package/server/agent/sanitizer.js +104 -13
- package/server/agent/session.js +319 -11
- package/server/agent/subagent.js +8 -3
- package/server/agent/suggestionTui.js +294 -0
- package/server/agent/suggestions.js +69 -3
- package/server/agent/telemetry.js +119 -0
- package/server/agent/terminalImage.js +146 -27
- package/server/agent/theme.js +17 -0
- package/server/agent/toolRouter.js +16 -1
- package/server/agent/tools.js +424 -225
- package/server/agent/ui.js +486 -64
- package/server/agent/version.js +2 -4
- package/server/agent/webSearch.js +111 -11
- package/server/agent/youtubeAuth.js +24 -6
- package/server/apiRouter.js +4222 -0
- package/server/mcp/data/discography.js +25 -16
- package/server/mcp/data/gallery.js +308 -20
- package/server/mcp/data/profile.js +74 -44
- package/server/mcp/data/timeline.js +1 -1
- package/server/mcp/data/works.js +51 -17
- package/server/mcp/dataAdapter.js +5 -3
- package/server/mcp/handler.js +5 -11
- package/server/mcp/prompts.js +86 -0
- package/server/mcp/security.js +18 -15
- package/server/mcp/server.js +7 -2
- package/server/mcp/tools.js +66 -2
- package/server/web-chat/core/browserLauncher.js +18 -0
- package/server/web-chat/core/connection.js +194 -0
- package/server/web-chat/core/conversationContext.js +82 -0
- package/server/web-chat/core/directCatalogEngine.js +196 -0
- package/server/web-chat/core/dramaKnowledge.js +142 -0
- package/server/web-chat/core/errorHandler.js +51 -0
- package/server/web-chat/core/followUpEngine.js +182 -0
- package/server/web-chat/core/futureEventGuard.js +80 -0
- package/server/web-chat/core/memorySnapshot.js +12 -0
- package/server/web-chat/core/security.js +146 -0
- package/server/web-chat/core/toolInspector.js +52 -0
- package/server/web-chat/router.js +120 -0
- package/server/web-chat/routes/chatRoute.js +397 -0
- package/server/web-chat/routes/clarifyConfigRoute.js +98 -0
- package/server/web-chat/routes/clarifyRoute.js +83 -0
- package/server/web-chat/routes/compareRoute.js +79 -0
- package/server/web-chat/routes/exportRoute.js +59 -0
- package/server/web-chat/routes/feedbackRoute.js +57 -0
- package/server/web-chat/routes/newsRoute.js +33 -0
- package/server/web-chat/routes/quizRoute.js +11 -0
- package/server/web-chat/routes/staticRoute.js +103 -0
- package/server/web-chat/routes/statusRoute.js +52 -0
- package/server/web-chat/routes/suggestRoute.js +77 -0
- package/server/web-chat/routes/timelineRoute.js +34 -0
- package/server/web-chat/routes/verifyRoute.js +57 -0
- package/server/web-chat/server.js +116 -0
- package/server/web-chat/ui/htmlBuilder.js +91 -0
- package/server/web-chat/ui/previews/agentBubble.preview.html +134 -0
- package/server/web-chat/ui/scripts/agentBubbleSystem.js +320 -0
- package/server/web-chat/ui/scripts/client.js +8397 -0
- package/server/web-chat/ui/scripts/kikuDb.js +177 -0
- package/server/web-chat/ui/scripts/suggestionsData.js +118 -0
- package/server/web-chat/ui/scripts/webchatI18n.js +291 -0
- package/server/web-chat/ui/styles/main.css +8568 -0
- package/server/web-chat/ui/templates/body.html +796 -0
- package/server/webChatHtml.js +1 -0
- package/server/webChatServer.js +6 -0
- package/src/data/discographyData.js +892 -866
- package/src/data/galleryData.js +1805 -561
- package/src/data/profileData.js +79 -2
- package/src/data/socialUpdatesData.js +106 -47
- package/src/data/timelineData.js +1 -1
- package/src/data/worksData.js +25 -4
- package/tokens.css +97 -0
- package/server/agent/updateManager.js +0 -456
|
@@ -1,20 +1,27 @@
|
|
|
1
|
-
import { normalizeProviderModel } from './config.js';
|
|
2
|
-
|
|
3
|
-
const NON_CHAT_MODEL = /(embed|embedding|rerank|whisper|tts|clip)/i;
|
|
1
|
+
import { normalizeProviderModel, matchModelIdentifier } from './config.js';
|
|
2
|
+
|
|
3
|
+
const NON_CHAT_MODEL = /(embed|embedding|rerank|whisper|tts|clip)/i;
|
|
4
4
|
|
|
5
5
|
export class ModelManager {
|
|
6
|
-
constructor(client, options = {}) {
|
|
6
|
+
constructor(client, options = {}) {
|
|
7
7
|
if (!client) throw new Error('ModelManager requires an LLM client.');
|
|
8
8
|
this.client = client;
|
|
9
|
-
this.requestedModel = options.requestedModel || client.model;
|
|
10
|
-
this.fallbackModel = options.fallbackModel || null;
|
|
11
|
-
this.fallbackModels = Array.isArray(options.fallbackModels) ? options.fallbackModels : [];
|
|
9
|
+
this.requestedModel = options.requestedModel || client.model;
|
|
10
|
+
this.fallbackModel = options.fallbackModel || null;
|
|
11
|
+
this.fallbackModels = Array.isArray(options.fallbackModels) ? options.fallbackModels : [];
|
|
12
12
|
this.lastResolution = null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async inspect() {
|
|
16
16
|
const health = await this.client.checkConnection();
|
|
17
17
|
const models = health.models || [];
|
|
18
|
+
const isReady = health.connected && models.length > 0 && (
|
|
19
|
+
models.includes(this.client.model) ||
|
|
20
|
+
models.some(m => matchModelIdentifier(this.client.model, m)) ||
|
|
21
|
+
Boolean(health.activeLoadedModel && models.includes(health.activeLoadedModel)) ||
|
|
22
|
+
Boolean(health.activeModel && models.includes(health.activeModel))
|
|
23
|
+
);
|
|
24
|
+
|
|
18
25
|
return {
|
|
19
26
|
...health,
|
|
20
27
|
models,
|
|
@@ -23,28 +30,40 @@ export class ModelManager {
|
|
|
23
30
|
? 'server_offline'
|
|
24
31
|
: models.length === 0
|
|
25
32
|
? 'server_online_no_models'
|
|
26
|
-
:
|
|
33
|
+
: isReady
|
|
27
34
|
? 'ready'
|
|
28
35
|
: 'requested_model_missing',
|
|
29
36
|
};
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
select(model, availableModels = []) {
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
35
|
-
|
|
36
|
-
throw new Error(`Model is not available: ${requested}`);
|
|
37
|
-
}
|
|
39
|
+
select(model, availableModels = []) {
|
|
40
|
+
const rawInput = String(model || '').trim();
|
|
41
|
+
if (!rawInput) throw new Error('A model ID is required.');
|
|
42
|
+
const requested = normalizeProviderModel(this.client.provider, rawInput);
|
|
38
43
|
if (NON_CHAT_MODEL.test(requested)) {
|
|
39
44
|
throw new Error(`Model does not appear to support chat: ${requested}`);
|
|
40
45
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
|
|
47
|
+
let targetModel = requested;
|
|
48
|
+
if (availableModels.length > 0) {
|
|
49
|
+
const exact = availableModels.find(m => m === requested || m === rawInput);
|
|
50
|
+
const fuzzy = availableModels.find(m => matchModelIdentifier(requested, m) || matchModelIdentifier(rawInput, m));
|
|
51
|
+
if (exact) {
|
|
52
|
+
targetModel = exact;
|
|
53
|
+
} else if (fuzzy) {
|
|
54
|
+
targetModel = fuzzy;
|
|
55
|
+
} else {
|
|
56
|
+
throw new Error(`Model is not available: ${requested}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof this.client.setModel === 'function') {
|
|
61
|
+
this.client.setModel(targetModel);
|
|
62
|
+
} else {
|
|
63
|
+
this.client.model = targetModel;
|
|
64
|
+
}
|
|
65
|
+
this.requestedModel = targetModel;
|
|
66
|
+
return targetModel;
|
|
48
67
|
}
|
|
49
68
|
|
|
50
69
|
async resolve(options = {}) {
|
|
@@ -57,33 +76,54 @@ export class ModelManager {
|
|
|
57
76
|
return this._record({ state: 'server_online_no_models', connected: true, models, error: `${this.client.providerName || 'LLM server'} is online but no model is loaded.` });
|
|
58
77
|
}
|
|
59
78
|
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
const requestedRaw = options.requestedModel || this.requestedModel || this.client.model;
|
|
80
|
+
const requested = normalizeProviderModel(this.client.provider, requestedRaw);
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
const activeLoaded = health.activeLoadedModel;
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
const exact = models.find(id => id === requested || id === requestedRaw);
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
const fuzzyRequested = models.find(id => matchModelIdentifier(requested, id) || matchModelIdentifier(requestedRaw, id));
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
const configuredFallback = models.find(id => id === (options.fallbackModel || this.fallbackModel));
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
const providerRecommendations = this.client.getProviderInfo?.().recommendedModels || [];
|
|
96
|
+
const fallbackCandidates = [
|
|
97
|
+
...(Array.isArray(options.fallbackModels) ? options.fallbackModels : this.fallbackModels),
|
|
98
|
+
...providerRecommendations.map(model => model.id),
|
|
99
|
+
];
|
|
100
|
+
const recommendedFallback = fallbackCandidates
|
|
101
|
+
.map(candidate => models.find(id => id === candidate || matchModelIdentifier(candidate, id)))
|
|
102
|
+
.find(Boolean);
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
const gemma = this.client.provider === 'lmstudio'
|
|
106
|
+
? (activeLoaded && /gemma/i.test(activeLoaded) ? activeLoaded : models.find(id => /gemma/i.test(id) && !NON_CHAT_MODEL.test(id)))
|
|
107
|
+
: null;
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
const chatFallback = (activeLoaded && !NON_CHAT_MODEL.test(activeLoaded))
|
|
111
|
+
? activeLoaded
|
|
112
|
+
: models.find(id => !NON_CHAT_MODEL.test(id));
|
|
113
|
+
|
|
114
|
+
const selected = exact || fuzzyRequested || (activeLoaded && matchModelIdentifier(requested, activeLoaded) ? activeLoaded : null) || configuredFallback || recommendedFallback || activeLoaded || gemma || chatFallback;
|
|
76
115
|
if (!selected) {
|
|
77
116
|
return this._record({ state: 'no_chat_model', connected: true, models, error: 'No chat-capable model was found.' });
|
|
78
117
|
}
|
|
118
|
+
|
|
79
119
|
this.client.model = selected;
|
|
80
120
|
return this._record({
|
|
81
|
-
state: exact ? 'ready' : 'fallback_selected',
|
|
121
|
+
state: (exact || fuzzyRequested) ? 'ready' : 'fallback_selected',
|
|
82
122
|
connected: true,
|
|
83
123
|
models,
|
|
84
124
|
selectedModel: selected,
|
|
85
125
|
requestedModel: requested,
|
|
86
|
-
usedFallback: !exact,
|
|
126
|
+
usedFallback: !(exact || (fuzzyRequested && matchModelIdentifier(requested, selected))),
|
|
87
127
|
});
|
|
88
128
|
}
|
|
89
129
|
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { PROFILES_FILE, KIKU_DATA_DIR } from './appPaths.js';
|
|
4
|
+
|
|
5
|
+
export const BUILTIN_PROFILES = {
|
|
6
|
+
default: {
|
|
7
|
+
name: 'default',
|
|
8
|
+
provider: 'lmstudio',
|
|
9
|
+
model: 'google/gemma-4-e4b',
|
|
10
|
+
thinking: 'show',
|
|
11
|
+
temperature: 0.3,
|
|
12
|
+
description: 'Local Gemma 4 e4b via LM Studio (free, private, offline)',
|
|
13
|
+
isBuiltIn: true
|
|
14
|
+
},
|
|
15
|
+
fast: {
|
|
16
|
+
name: 'fast',
|
|
17
|
+
provider: 'groq',
|
|
18
|
+
model: 'openai/gpt-oss-20b',
|
|
19
|
+
thinking: 'hide',
|
|
20
|
+
temperature: 0.2,
|
|
21
|
+
description: 'Ultra-fast inference on Groq LPU (low latency)',
|
|
22
|
+
isBuiltIn: true
|
|
23
|
+
},
|
|
24
|
+
'deep-reasoning': {
|
|
25
|
+
name: 'deep-reasoning',
|
|
26
|
+
provider: 'deepseek',
|
|
27
|
+
model: 'deepseek-v4-flash',
|
|
28
|
+
thinking: 'show',
|
|
29
|
+
temperature: 0.3,
|
|
30
|
+
description: 'DeepSeek V4 with full reasoning traces and CoT',
|
|
31
|
+
isBuiltIn: true
|
|
32
|
+
},
|
|
33
|
+
cloud: {
|
|
34
|
+
name: 'cloud',
|
|
35
|
+
provider: 'openai',
|
|
36
|
+
model: 'gpt-5.6-luna',
|
|
37
|
+
thinking: 'show',
|
|
38
|
+
temperature: 0.3,
|
|
39
|
+
description: 'OpenAI Cloud flagship multilingual agent tier',
|
|
40
|
+
isBuiltIn: true
|
|
41
|
+
},
|
|
42
|
+
gemini: {
|
|
43
|
+
name: 'gemini',
|
|
44
|
+
provider: 'gemini',
|
|
45
|
+
model: 'gemini-3.7-flash',
|
|
46
|
+
thinking: 'show',
|
|
47
|
+
temperature: 0.3,
|
|
48
|
+
description: 'Google Gemini 3.7 Flash multilingual reasoning',
|
|
49
|
+
isBuiltIn: true
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export function loadProfiles() {
|
|
54
|
+
try {
|
|
55
|
+
if (fs.existsSync(PROFILES_FILE)) {
|
|
56
|
+
const raw = fs.readFileSync(PROFILES_FILE, 'utf-8');
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
if (parsed && typeof parsed === 'object') {
|
|
59
|
+
const userProfiles = parsed.profiles || {};
|
|
60
|
+
return {
|
|
61
|
+
activeProfile: parsed.activeProfile || 'default',
|
|
62
|
+
profiles: {
|
|
63
|
+
...BUILTIN_PROFILES,
|
|
64
|
+
...userProfiles
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch {}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
activeProfile: 'default',
|
|
73
|
+
profiles: { ...BUILTIN_PROFILES }
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function saveProfiles(data) {
|
|
78
|
+
try {
|
|
79
|
+
if (!fs.existsSync(KIKU_DATA_DIR)) {
|
|
80
|
+
fs.mkdirSync(KIKU_DATA_DIR, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
const customProfiles = {};
|
|
83
|
+
for (const [key, val] of Object.entries(data.profiles || {})) {
|
|
84
|
+
if (!BUILTIN_PROFILES[key] || val.customized) {
|
|
85
|
+
customProfiles[key] = val;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const toWrite = {
|
|
89
|
+
activeProfile: data.activeProfile || 'default',
|
|
90
|
+
profiles: customProfiles,
|
|
91
|
+
updatedAt: new Date().toISOString()
|
|
92
|
+
};
|
|
93
|
+
fs.writeFileSync(PROFILES_FILE, JSON.stringify(toWrite, null, 2), 'utf-8');
|
|
94
|
+
return true;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error('Failed to save profiles to disk:', err.message);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getProfile(name) {
|
|
102
|
+
if (!name || typeof name !== 'string') return null;
|
|
103
|
+
const normalized = name.toLowerCase().trim();
|
|
104
|
+
const data = loadProfiles();
|
|
105
|
+
return data.profiles[normalized] || null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function getActiveProfile() {
|
|
109
|
+
const data = loadProfiles();
|
|
110
|
+
const activeName = data.activeProfile || 'default';
|
|
111
|
+
const profile = data.profiles[activeName] || BUILTIN_PROFILES.default;
|
|
112
|
+
return {
|
|
113
|
+
...profile,
|
|
114
|
+
name: activeName
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function setActiveProfile(name) {
|
|
119
|
+
if (!name || typeof name !== 'string') return false;
|
|
120
|
+
const normalized = name.toLowerCase().trim();
|
|
121
|
+
const data = loadProfiles();
|
|
122
|
+
if (!data.profiles[normalized]) {
|
|
123
|
+
throw new Error(`Profile "${name}" does not exist. Run /profile list to see available profiles.`);
|
|
124
|
+
}
|
|
125
|
+
data.activeProfile = normalized;
|
|
126
|
+
saveProfiles(data);
|
|
127
|
+
return data.profiles[normalized];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function createOrUpdateProfile(name, config) {
|
|
131
|
+
if (!name || typeof name !== 'string') {
|
|
132
|
+
throw new Error('Profile name is required');
|
|
133
|
+
}
|
|
134
|
+
const normalized = name.toLowerCase().trim();
|
|
135
|
+
const data = loadProfiles();
|
|
136
|
+
|
|
137
|
+
const existing = data.profiles[normalized] || {};
|
|
138
|
+
data.profiles[normalized] = {
|
|
139
|
+
...existing,
|
|
140
|
+
name: normalized,
|
|
141
|
+
provider: config.provider || existing.provider || 'lmstudio',
|
|
142
|
+
model: config.model || existing.model || 'google/gemma-4-e4b',
|
|
143
|
+
thinking: config.thinking || existing.thinking || 'show',
|
|
144
|
+
temperature: config.temperature !== undefined ? Number(config.temperature) : (existing.temperature ?? 0.3),
|
|
145
|
+
description: config.description || existing.description || `Custom profile ${normalized}`,
|
|
146
|
+
customized: true,
|
|
147
|
+
updatedAt: new Date().toISOString()
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
saveProfiles(data);
|
|
151
|
+
return data.profiles[normalized];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function deleteProfile(name) {
|
|
155
|
+
if (!name || typeof name !== 'string') return false;
|
|
156
|
+
const normalized = name.toLowerCase().trim();
|
|
157
|
+
if (BUILTIN_PROFILES[normalized]) {
|
|
158
|
+
throw new Error(`Cannot delete built-in profile "${name}".`);
|
|
159
|
+
}
|
|
160
|
+
const data = loadProfiles();
|
|
161
|
+
if (!data.profiles[normalized]) return false;
|
|
162
|
+
|
|
163
|
+
delete data.profiles[normalized];
|
|
164
|
+
if (data.activeProfile === normalized) {
|
|
165
|
+
data.activeProfile = 'default';
|
|
166
|
+
}
|
|
167
|
+
saveProfiles(data);
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function listProfiles() {
|
|
172
|
+
const data = loadProfiles();
|
|
173
|
+
return Object.values(data.profiles).map(p => ({
|
|
174
|
+
id: p.name,
|
|
175
|
+
...p,
|
|
176
|
+
isActive: p.name === data.activeProfile
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
export const PUBLIC_REMOTE_TOOL_NAMES = Object.freeze(new Set([
|
|
3
|
+
'global_search',
|
|
4
|
+
'search_works',
|
|
5
|
+
'get_work_detail',
|
|
6
|
+
'get_character_roles',
|
|
7
|
+
'search_discography',
|
|
8
|
+
'get_profile_and_fandom',
|
|
9
|
+
'get_awards',
|
|
10
|
+
'get_brand_endorsements',
|
|
11
|
+
'get_career_timeline',
|
|
12
|
+
'get_social_feed',
|
|
13
|
+
'get_gallery_photos',
|
|
14
|
+
'get_quotes',
|
|
15
|
+
'get_music_videos',
|
|
16
|
+
'get_media_links',
|
|
17
|
+
'search_youtube',
|
|
18
|
+
'web_search',
|
|
19
|
+
'web_extract',
|
|
20
|
+
'fetch_webpage',
|
|
21
|
+
'fetch_api',
|
|
22
|
+
]));
|
|
@@ -5,13 +5,32 @@ const DETAIL_VIEWS = new Set([
|
|
|
5
5
|
'markdown', 'json', 'text', 'raw', 'session', 'transcript', 'history'
|
|
6
6
|
]);
|
|
7
7
|
|
|
8
|
+
const DETAIL_VIEW_ALIASES = Object.freeze({
|
|
9
|
+
think: 'thinking',
|
|
10
|
+
thought: 'thinking',
|
|
11
|
+
thoughts: 'thinking',
|
|
12
|
+
reasoning: 'thinking',
|
|
13
|
+
ans: 'answer',
|
|
14
|
+
response: 'answer',
|
|
15
|
+
result: 'answer',
|
|
16
|
+
toolcalls: 'tools',
|
|
17
|
+
tool_calls: 'tools',
|
|
18
|
+
calls: 'tools',
|
|
19
|
+
tool_call: 'tool',
|
|
20
|
+
md: 'markdown',
|
|
21
|
+
txt: 'text',
|
|
22
|
+
sess: 'session',
|
|
23
|
+
});
|
|
24
|
+
|
|
8
25
|
export function parseDetailsRequest(reference = '') {
|
|
9
26
|
const tokens = String(reference || '').trim().split(/\s+/).filter(Boolean);
|
|
10
27
|
let queryNumber = null;
|
|
11
28
|
let isSession = false;
|
|
12
29
|
|
|
13
30
|
const first = (tokens[0] || '').toLowerCase();
|
|
14
|
-
|
|
31
|
+
const normalizedFirst = DETAIL_VIEW_ALIASES[first] || first;
|
|
32
|
+
|
|
33
|
+
if (normalizedFirst === 'session' || normalizedFirst === 'transcript' || normalizedFirst === 'history') {
|
|
15
34
|
isSession = true;
|
|
16
35
|
tokens.shift();
|
|
17
36
|
} else if (/^q?\d+$/i.test(tokens[0])) {
|
|
@@ -20,10 +39,13 @@ export function parseDetailsRequest(reference = '') {
|
|
|
20
39
|
tokens.shift();
|
|
21
40
|
}
|
|
22
41
|
|
|
23
|
-
let
|
|
42
|
+
let rawView = (tokens.shift() || (isSession ? 'markdown' : 'answer')).toLowerCase();
|
|
43
|
+
let view = DETAIL_VIEW_ALIASES[rawView] || rawView;
|
|
44
|
+
|
|
24
45
|
if (view === 'session' || view === 'transcript' || view === 'history') {
|
|
25
46
|
isSession = true;
|
|
26
|
-
|
|
47
|
+
const nextRaw = (tokens.shift() || 'markdown').toLowerCase();
|
|
48
|
+
view = DETAIL_VIEW_ALIASES[nextRaw] || nextRaw;
|
|
27
49
|
}
|
|
28
50
|
|
|
29
51
|
if (!DETAIL_VIEWS.has(view)) {
|
|
@@ -95,14 +117,15 @@ export function copyToClipboard(text) {
|
|
|
95
117
|
|
|
96
118
|
try {
|
|
97
119
|
if (platform === 'win32') {
|
|
98
|
-
const
|
|
99
|
-
if (proc.status === 0) return true;
|
|
100
|
-
const ps = spawnSync('powershell.exe', ['-NoProfile', '-Command', '$input | Set-Clipboard'], {
|
|
120
|
+
const ps = spawnSync('powershell.exe', ['-NoProfile', '-Command', '[Console]::InputEncoding = [System.Text.Encoding]::UTF8; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Set-Clipboard -Value ([System.Console]::In.ReadToEnd())'], {
|
|
101
121
|
input: text,
|
|
102
122
|
encoding: 'utf-8',
|
|
103
123
|
windowsHide: true,
|
|
104
124
|
});
|
|
105
|
-
|
|
125
|
+
if (ps.status === 0) return true;
|
|
126
|
+
|
|
127
|
+
const proc = spawnSync('clip', [], { input: text, encoding: 'utf-8', windowsHide: true });
|
|
128
|
+
if (proc.status === 0) return true;
|
|
106
129
|
}
|
|
107
130
|
if (platform === 'darwin') {
|
|
108
131
|
const proc = spawnSync('pbcopy', [], { input: text, encoding: 'utf-8' });
|
|
@@ -123,8 +146,38 @@ export function copyToClipboard(text) {
|
|
|
123
146
|
return false;
|
|
124
147
|
}
|
|
125
148
|
|
|
149
|
+
export function readFromClipboard() {
|
|
150
|
+
const platform = process.platform;
|
|
151
|
+
try {
|
|
152
|
+
if (platform === 'win32') {
|
|
153
|
+
const ps = spawnSync('powershell.exe', ['-NoProfile', '-Command', '[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-Clipboard -Raw'], {
|
|
154
|
+
encoding: 'utf-8',
|
|
155
|
+
windowsHide: true,
|
|
156
|
+
timeout: 1500,
|
|
157
|
+
});
|
|
158
|
+
if (ps.status === 0 && typeof ps.stdout === 'string') return ps.stdout;
|
|
159
|
+
} else if (platform === 'darwin') {
|
|
160
|
+
const proc = spawnSync('pbpaste', [], { encoding: 'utf-8', timeout: 1500 });
|
|
161
|
+
if (proc.status === 0 && typeof proc.stdout === 'string') return proc.stdout;
|
|
162
|
+
} else {
|
|
163
|
+
const tools = [
|
|
164
|
+
{ cmd: 'wl-paste', args: ['--no-newline'] },
|
|
165
|
+
{ cmd: 'xclip', args: ['-selection', 'clipboard', '-o'] },
|
|
166
|
+
{ cmd: 'xsel', args: ['--clipboard', '--output'] },
|
|
167
|
+
];
|
|
168
|
+
for (const t of tools) {
|
|
169
|
+
try {
|
|
170
|
+
const proc = spawnSync(t.cmd, t.args, { encoding: 'utf-8', timeout: 1500 });
|
|
171
|
+
if (proc.status === 0 && typeof proc.stdout === 'string') return proc.stdout;
|
|
172
|
+
} catch {}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} catch {}
|
|
176
|
+
return '';
|
|
177
|
+
}
|
|
178
|
+
|
|
126
179
|
export function getCopyableContent(details, request = {}, session = null) {
|
|
127
|
-
const isSessionReq = request.isSession
|
|
180
|
+
const isSessionReq = Boolean(request.isSession);
|
|
128
181
|
const view = request.view || 'answer';
|
|
129
182
|
|
|
130
183
|
if (isSessionReq && session) {
|
|
@@ -135,6 +188,13 @@ export function getCopyableContent(details, request = {}, session = null) {
|
|
|
135
188
|
type: 'session_json',
|
|
136
189
|
};
|
|
137
190
|
}
|
|
191
|
+
if (view === 'html' || view === 'web') {
|
|
192
|
+
return {
|
|
193
|
+
title: `Full Session HTML • ${session.id}`,
|
|
194
|
+
text: typeof session.getTranscriptHtml === 'function' ? session.getTranscriptHtml() : '',
|
|
195
|
+
type: 'session_html',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
138
198
|
if (view === 'text' || view === 'raw') {
|
|
139
199
|
return {
|
|
140
200
|
title: `Full Session Text • ${session.id}`,
|