kiku-agent 1.3.2 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -0,0 +1,509 @@
1
+ export const MCP_QUIZ_CORRECT_ANSWERS = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
2
+
3
+ export const MCP_QUIZ_I18N = {
4
+ th: {
5
+ title: "Ju Jingyi MCP Agent Challenge 🌸",
6
+ subtitle: "ตอบคำถามแฟนพันธุ์แท้ 12 ข้อใหม่เพื่อรับ MCP API Key (1,000 req/min)",
7
+ questionOf: (idx, total) => `คำถามที่ ${idx} จาก ${total}`,
8
+ score: (score, total) => `คะแนน: ${score} / ${total}`,
9
+ correct: "🌸 ถูกต้องสมบูรณ์!",
10
+ incorrect: "✗ ยังไม่ถูกต้องนะมี่จวี!",
11
+ tryAgain: "ลองตอบใหม่อีกครั้ง ↺",
12
+ hint: "คำใบ้",
13
+ showHint: "ดูคำใบ้",
14
+ generatingKey: "กำลังสร้าง MCP API Key ประจำตัวของคุณ...",
15
+ generatingDesc: "กำลังตรวจสอบคะแนน 12/12 และออกคีย์ความเร็วสูงสำหรับ Claude Desktop, Cursor, และ Antigravity...",
16
+ congratsTitle: "ยินดีด้วย! คุณคือมี่จวีระดับ Master 🌸",
17
+ congratsDesc: "ตอบถูกต้องครบ 12/12 ข้อ! ปลดล็อก MCP API Key (โควต้า 1,000 requests/min) สำเร็จ",
18
+ keyLabel: "MCP API Key (Streamable HTTP)",
19
+ copyKey: "Copy Key",
20
+ copyClaudeConfig: "✨ คัดลอก Claude / Cursor Config",
21
+ copiedClaudeConfig: "✓ คัดลอก Config สำเร็จ!",
22
+ finishBtn: "เสร็จสิ้น",
23
+ errorTitle: "เกิดข้อผิดพลาดในการออก Key",
24
+ retryQuiz: "ลองใหม่อีกครั้ง",
25
+ copyAlert: (key) => '✨ คัดลอก MCP API Key เรียบร้อยแล้ว!\n\n' + key,
26
+ },
27
+ en: {
28
+ title: "Ju Jingyi MCP Agent Challenge 🌸",
29
+ subtitle: "Answer all 12 questions correctly to claim your MCP API Key (1,000 req/min)",
30
+ questionOf: (idx, total) => `Question ${idx} of ${total}`,
31
+ score: (score, total) => `Score: ${score} / ${total}`,
32
+ correct: "🌸 Perfectly Correct!",
33
+ incorrect: "✗ Not quite right, Miju!",
34
+ tryAgain: "Try Again ↺",
35
+ hint: "Hint",
36
+ showHint: "Show Hint",
37
+ generatingKey: "Generating your dedicated MCP API Key...",
38
+ generatingDesc: "Verifying 12/12 score and issuing credentials for Claude Desktop, Cursor, and Antigravity...",
39
+ congratsTitle: "Congratulations! Ju Jingyi Master Fan 🌸",
40
+ congratsDesc: "12/12 Perfect Score! You have unlocked your MCP API Key (1,000 req/min quota).",
41
+ keyLabel: "MCP API Key (Streamable HTTP)",
42
+ copyKey: "Copy Key",
43
+ copyClaudeConfig: "✨ Copy Claude / Cursor Config",
44
+ copiedClaudeConfig: "✓ Config Copied!",
45
+ finishBtn: "Done",
46
+ errorTitle: "Key Issuing Failed",
47
+ retryQuiz: "Try Again",
48
+ copyAlert: (key) => '✨ Copied MCP API Key to clipboard!\n\n' + key,
49
+ },
50
+ zh: {
51
+ title: "鞠婧祎 MCP 智能体问答挑战 🌸",
52
+ subtitle: "正确回答全部 12 道全新题目即可获取专属 MCP API Key (1,000次/分钟)",
53
+ questionOf: (idx, total) => `第 ${idx} 题 / 共 ${total} 题`,
54
+ score: (score, total) => `得分: ${score} / ${total}`,
55
+ correct: "🌸 回答完全正确!",
56
+ incorrect: "✗ 答错啦,蜜橘继续加油!",
57
+ tryAgain: "重新作答 ↺",
58
+ hint: "提示",
59
+ showHint: "查看提示",
60
+ generatingKey: "正在为您生成专属 MCP API Key...",
61
+ generatingDesc: "系统正在验证满分答卷并为 Claude Desktop, Cursor, Antigravity 颁发权限...",
62
+ congratsTitle: "恭喜通关!鞠婧祎资深全能蜜橘 🌸",
63
+ congratsDesc: "答对全部 12 道题目!成功解锁专属 MCP API Key (1,000次/分钟高频配额)。",
64
+ keyLabel: "MCP API Key (Streamable HTTP)",
65
+ copyKey: "复制密钥",
66
+ copyClaudeConfig: "✨ 复制 Claude / Cursor 配置代码",
67
+ copiedClaudeConfig: "✓ 配置已复制!",
68
+ finishBtn: "完成",
69
+ errorTitle: "密钥生成失败",
70
+ retryQuiz: "重新尝试",
71
+ copyAlert: (key) => '✨ MCP API Key 已复制到剪贴板!\n\n' + key,
72
+ }
73
+ };
74
+
75
+ export const MCP_QUIZ_QUESTIONS = [
76
+ {
77
+ q: {
78
+ th: "1. ในซีรีส์แฟนตาซีย้อนยุคฟอร์มยักษ์ปี 2026 '月鳞绮纪 (Veil of Shadows)' จวีจิ้งอีรับบทเป็นตัวละครใด?",
79
+ en: "1. In the 2026 epic fantasy drama 'Veil of Shadows' (月鳞绮纪), which character does Ju Jingyi portray?",
80
+ zh: "1. 在2026年古装奇幻大剧《月鳞绮纪》中,鞠婧祎领衔主演的角色是?"
81
+ },
82
+ options: {
83
+ th: [
84
+ "ลู่หวู่อี (Lu Wuyi) — จิ้งจอกเก้าหางแห่งสำนักไร้จันทร์",
85
+ "เฟิ่งเฟยเฟย (Feng Feifei) — ธิดาหงส์หยก",
86
+ "หนานกงเยว่ (Nangong Yue) — เจ้าสำนักกระบี่จันทรา",
87
+ "ฉู่หลิงหลง (Chu Linglong) — มือปราบหญิงแห่งศาลต้าหลี่"
88
+ ],
89
+ en: [
90
+ "Lu Wuyi (露芜衣) — Nine-tailed fox of the Moonless Sect",
91
+ "Feng Feifei — Jade Phoenix maiden",
92
+ "Nangong Yue — Master of Moon Sword Sect",
93
+ "Chu Linglong — Female constable of Dali Court"
94
+ ],
95
+ zh: [
96
+ "露芜衣 (Lu Wuyi) — 无月门九尾狐",
97
+ "凤飞飞 — 玉凤神女",
98
+ "南宫月 — 望月剑派传人",
99
+ "楚玲珑 — 大理寺女神捕"
100
+ ]
101
+ },
102
+ answer: 0,
103
+ hint: {
104
+ th: "ตัวละครนี้เป็นจิ้งจอกเก้าหางสาวผู้ปราดเปรียว ได้รับภารกิจแฝงตัวเข้าจวนตระกูลเว่ยเพื่อตามหาพลังมังกร",
105
+ en: "This character is a nimble nine-tailed fox who infiltrates the Wei manor to track the dragon power.",
106
+ zh: "这个角色是无月门的九尾灵狐,潜入魏府追寻龙神之力。"
107
+ }
108
+ },
109
+ {
110
+ q: {
111
+ th: "2. ในซีรีส์เซียนเซี่ยปี 2026 'The Eternal Fragrance (千香 / พันกลิ่นบุปผา)' จวีจิ้งอีโคจรกลับมาร่วมงานกับพระเอกคู่ขวัญคนใด?",
112
+ en: "2. In the 2026 xianxia drama 'The Eternal Fragrance' (千香), with which frequent male lead co-star does Ju Jingyi reunite?",
113
+ zh: "2. 在2026年仙侠力作《千香》中,鞠婧祎与哪位默契搭档男主角再度合作?"
114
+ },
115
+ options: {
116
+ th: [
117
+ "ซ่งเวยหลง (Song Weilong)",
118
+ "หวังอี้ป๋อ (Wang Yibo)",
119
+ "หยางหยาง (Yang Yang)",
120
+ "อู๋เหล่ย (Wu Lei)"
121
+ ],
122
+ en: [
123
+ "Song Weilong (宋威龙)",
124
+ "Wang Yibo",
125
+ "Yang Yang",
126
+ "Wu Lei"
127
+ ],
128
+ zh: [
129
+ "宋威龙 (Song Weilong)",
130
+ "王一博",
131
+ "杨洋",
132
+ "吴磊"
133
+ ]
134
+ },
135
+ answer: 0,
136
+ hint: {
137
+ th: "ทั้งคู่เคยสร้างความประทับใจร่วมกันมาแล้วในซีรีส์บัณฑิตหน้าหวาน 'In a Class of Her Own' (漂亮书生)",
138
+ en: "They previously starred together as a beloved on-screen couple in 'In a Class of Her Own' (漂亮书生).",
139
+ zh: "两人此前在古装校园剧《漂亮书生》中就曾联袂主演,深受观众喜爱。"
140
+ }
141
+ },
142
+ {
143
+ q: {
144
+ th: "3. ในซีรีส์แฟนตาซียอดนิยมปี 2024 'เซียนกระบี่พิชิตมาร 4 (Sword and Fairy 4 / 仙剑四)' จวีจิ้งอีรับบทเป็นใคร?",
145
+ en: "3. In the 2024 hit fantasy series 'Sword and Fairy 4' (仙剑四), which iconic heroine does Ju Jingyi portray?",
146
+ zh: "3. 在2024年经典仙侠IP剧《仙剑四》中,鞠婧祎领衔饰演的传奇女主角是?"
147
+ },
148
+ options: {
149
+ th: [
150
+ "หานหลิงซา (Han Lingsha) — จอมยุทธ์สาวชุดแดงผู้คล่องแคล่ว",
151
+ "หลิวม่งหลี (Liu Mengli) — องค์หญิงเผ่าฝันเร้นลับ",
152
+ "จ้าวหลิงเอ๋อร์ (Zhao Ling'er) — ทายาทเทพมารดรหนี่วา",
153
+ "หลินเยว่หรู (Lin Yueru) — คุณหนูใหญ่ตระกูลหลิน"
154
+ ],
155
+ en: [
156
+ "Han Lingsha (韩菱纱) — The nimble red-clad heroine",
157
+ "Liu Mengli — Mystic dream princess",
158
+ "Zhao Ling'er — Nuwa clan descendant",
159
+ "Lin Yueru — Eldest daughter of Lin Manor"
160
+ ],
161
+ zh: [
162
+ "韩菱纱 (Han Lingsha) — 红衣灵动独行游侠",
163
+ "柳梦璃 — 幻瞑界少主",
164
+ "赵灵儿 — 女娲后裔",
165
+ "林月如 — 南武林盟主千金"
166
+ ]
167
+ },
168
+ answer: 0,
169
+ hint: {
170
+ th: "ตัวละครเอกหญิงชุดแดงผู้ร่าเริงและเป็นผู้เชี่ยวชาญการผจญภัยในสุสานโบราณจากตำนานเกมเซียนกระบี่",
171
+ en: "She portrays the energetic, red-clad tomb explorer who accompanies the protagonist throughout his journey.",
172
+ zh: "她饰演的是常穿一袭红衣、灵动洒脱而深具情义的经典女主角。"
173
+ }
174
+ },
175
+ {
176
+ q: {
177
+ th: "4. ในซีรีส์ย้อนยุคยอดนิยม 'กรุ่นรักกลิ่นบุปผา (The Blooms at Ruyi Pavilion / 如意芳霏)' จวีจิ้งอีรับบทนำเป็นตัวละครใด?",
178
+ en: "4. In the hit costume drama 'The Blooms at Ruyi Pavilion' (如意芳霏), which character does Ju Jingyi star as?",
179
+ zh: "4. 在热播古装爱情剧《如意芳霏》中,鞠婧祎领衔主演的女主角是?"
180
+ },
181
+ options: {
182
+ th: [
183
+ "ฟู่หรง (Fu Rong) — คุณหนูรองตระกูลฟู่ผู้มีนิมิตฝันเห็นอนาคต",
184
+ "ฟู่เซวียน (Fu Xuan) — คุณหนูใหญ่ตระกูลฟู่",
185
+ "ชุยหว่าน (Cui Wan) — องค์หญิงแห่งแคว้น",
186
+ "หลานเซียง (Lan Xiang) — สาวใช้ผู้ภักดี"
187
+ ],
188
+ en: [
189
+ "Fu Rong (傅容) — Second daughter of the Fu family who dreams of the future",
190
+ "Fu Xuan — Eldest daughter of the Fu family",
191
+ "Cui Wan — Imperial princess",
192
+ "Lan Xiang — Faithful attendant"
193
+ ],
194
+ zh: [
195
+ "傅容 (Fu Rong) — 坚强聪慧、能梦见未来的恒京令次女",
196
+ "傅宣 — 傅家知书达理的长女",
197
+ "崔绾 — 傲娇受宠的西河郡主",
198
+ "兰香 — 忠心耿耿的随身侍女"
199
+ ]
200
+ },
201
+ answer: 0,
202
+ hint: {
203
+ th: "ตัวละครนี้มีชื่อที่สื่อถึงดอกไม้หอม และมีความสามารถพิเศษในการฝันเห็นลางบอกเหตุล่วงหน้า",
204
+ en: "Her character name contains 'Rong' (容) and she possesses the unique ability to see prophetic dreams.",
205
+ zh: "剧名《如意芳霏》中的女主角,聪慧敏捷且能借由梦境预知命运轨迹。"
206
+ }
207
+ },
208
+ {
209
+ q: {
210
+ th: "5. ในซีรีส์ย้อนยุคปี 2021 'เจียหนานจ้วน (Rebirth for You / 嘉南传)' จวีจิ้งอีรับบทเป็นตัวละครใด?",
211
+ en: "5. In the 2021 historical romance 'Rebirth for You' (嘉南传), which royal role does Ju Jingyi lead?",
212
+ zh: "5. 在2021年古装爱情励志剧《嘉南传》中,鞠婧祎饰演的皇家贵胄是?"
213
+ },
214
+ options: {
215
+ th: [
216
+ "เจียงเป่าหนิง (Jiang Baoning) — ท่านหญิงเจียหนานผู้ฉลาดหลักแหลมและรักอิสระ",
217
+ "ไป๋ซู่ (Bai Su) — ธิดาแม่ทัพผู้เก่งกล้า",
218
+ "หานถง (Han Tong) — สาวชาววังผู้ลึกลับ",
219
+ "เกาเมี่ยวหรง (Gao Miaorong) — บุตรสาวตระกูลหมอหลวง"
220
+ ],
221
+ en: [
222
+ "Jiang Baoning (姜保宁) — Princess Jia Nan, brilliant inventor who treasures freedom",
223
+ "Bai Su — Courageous general's daughter",
224
+ "Han Tong — Mysterious court lady",
225
+ "Gao Miaorong — Imperial physician's daughter"
226
+ ],
227
+ zh: [
228
+ "姜保宁 (Jiang Baoning) — 聪慧机巧、精通机关巧术的嘉南郡主",
229
+ "白愫 — 忠厚善良的定北侯府小姐",
230
+ "韩同 — 深宫内廷侍卫",
231
+ "高妙容 — 机关算尽的医术世家之女"
232
+ ]
233
+ },
234
+ answer: 0,
235
+ hint: {
236
+ th: "เธอคือ 'ท่านหญิงเจียหนาน' ผู้มีความสามารถพิเศษด้านการประดิษฐ์กลไกเครื่องกลและชิงชะตาชีวิตของตนเอง",
237
+ en: "She portrays Princess Jia Nan, a gifted mechanical inventor determined to control her own destiny.",
238
+ zh: "她饰演的正是剧名所指的“嘉南郡主”,酷爱器械机关制作且性格自立果敢。"
239
+ }
240
+ },
241
+ {
242
+ q: {
243
+ th: "6. ในซีรีส์แฟนตาซีเทพเซียนปี 2023 '花戎 (Beauty of Resilience)' จวีจิ้งอีรับบทเป็นตัวละครใด?",
244
+ en: "6. In the 2023 fantasy romance 'Beauty of Resilience' (花戎), which mythological identity does Ju Jingyi depict?",
245
+ zh: "6. 在2023年仙侠玄幻剧《花戎》中,鞠婧祎饰演的核心角色身份是?"
246
+ },
247
+ options: {
248
+ th: [
249
+ "เว่ยจือ (Wei Zhi) — หงส์เพลิงตัวแรกแห่งปฐพี (The Phoenix)",
250
+ "มู่เหยียน (Mu Yan) — เทพธิดาแห่งสายธาร",
251
+ "หลิงหลง (Ling Long) — เซียนนกกระจอกสวรรค์",
252
+ "ไป๋หลิง (Bai Ling) — จิ้งจอกหิมะเก้าหาง"
253
+ ],
254
+ en: [
255
+ "Wei Zhi (魏枝) — The primordial Phoenix of the heavens and earth",
256
+ "Mu Yan — River goddess",
257
+ "Ling Long — Heavenly celestial sparrow",
258
+ "Bai Ling — Nine-tailed snow fox"
259
+ ],
260
+ zh: [
261
+ "魏枝 (Wei Zhi) — 天地间第一只凤凰转世",
262
+ "木烟 — 碧水仙子",
263
+ "玲珑 — 天界青雀仙侍",
264
+ "白灵 — 九尾雪狐"
265
+ ]
266
+ },
267
+ answer: 0,
268
+ hint: {
269
+ th: "เด็กสาวที่ทุกคนมองว่าเป็นตัวอับโชค แต่แท้จริงแล้วคือร่างเกิดใหม่ของหงส์เพลิงผู้ทรงอิทธิฤทธิ์",
270
+ en: "A humble washer girl revealed to be the reincarnation of the legendary heavenly Phoenix.",
271
+ zh: "外表看似平凡却背负三界宿命,真身为震动天地的凤凰本尊。"
272
+ }
273
+ },
274
+ {
275
+ q: {
276
+ th: "7. ในปี 2025 จวีจิ้งอีได้รับแต่งตั้งเป็น Brand Ambassador ของแบรนด์เครื่องประดับอัญมณีชั้นสูงจากฝรั่งเศส (LVMH Group) แบรนด์ใด?",
277
+ en: "7. In 2025, Ju Jingyi was appointed Brand Ambassador for which prestigious French High Jewelry maison under LVMH?",
278
+ zh: "7. 2025年,鞠婧祎官宣成为LVMH集团旗下哪家历史悠久的法国殿堂级高级珠宝品牌大使?"
279
+ },
280
+ options: {
281
+ th: [
282
+ "FRED Paris (斐登珠宝)",
283
+ "Cartier (คาร์เทียร์)",
284
+ "Bulgari (บุลการี)",
285
+ "Tiffany & Co. (ทิฟฟานี แอนด์ โค)"
286
+ ],
287
+ en: [
288
+ "FRED Paris (French High Jewelry, LVMH)",
289
+ "Cartier",
290
+ "Bulgari",
291
+ "Tiffany & Co."
292
+ ],
293
+ zh: [
294
+ "斐登珠宝 (FRED Paris - 法国现代珠宝商)",
295
+ "卡地亚 (Cartier)",
296
+ "宝格丽 (Bulgari)",
297
+ "蒂芙尼 (Tiffany & Co.)"
298
+ ]
299
+ },
300
+ answer: 0,
301
+ hint: {
302
+ th: "แบรนด์ไฮจิวเวลรีจากฝรั่งเศส ก่อตั้งขึ้นในปี 1936 โด่งดังจากคอลเลกชัน Force 10 สายเคเบิลเรือใบ",
303
+ en: "A French high jewelry house established in 1936, globally celebrated for its sailing cable 'Force 10' collection.",
304
+ zh: "源自法国巴黎的现代高级珠宝世家,以标志性的 Force 10 航海缆绳手链闻名世界。"
305
+ }
306
+ },
307
+ {
308
+ q: {
309
+ th: "8. ใน Milan Fashion Week 2026 จวีจิ้งอีสร้างลุคไวรัลระดับสากลในฐานะ Global Brand Ambassador ให้กับแบรนด์ใด?",
310
+ en: "8. At Milan Fashion Week 2026, for which brand did Ju Jingyi create a viral global sensation as Global Brand Ambassador?",
311
+ zh: "8. 在2026米兰时装周期间,鞠婧祎作为全球品牌代言人惊艳亮相并引爆全网讨论的是哪个品牌?"
312
+ },
313
+ options: {
314
+ th: [
315
+ "PRSR (Pasha / 帕莎眼镜) — แบรนด์แว่นตาสุดหรู",
316
+ "Ray-Ban (เรย์แบน)",
317
+ "Gentle Monster (เจนเทิล มอนสเตอร์)",
318
+ "Oakley (โอ๊คลีย์)"
319
+ ],
320
+ en: [
321
+ "PRSR (Pasha) — Luxury fashion eyewear",
322
+ "Ray-Ban",
323
+ "Gentle Monster",
324
+ "Oakley"
325
+ ],
326
+ zh: [
327
+ "帕莎眼镜 (PRSR / Pasha) — 时尚先锋眼镜品牌",
328
+ "雷朋 (Ray-Ban)",
329
+ "Gentle Monster",
330
+ "欧克利 (Oakley)"
331
+ ]
332
+ },
333
+ answer: 0,
334
+ hint: {
335
+ th: "แบรนด์แว่นตาสุดชิคที่เธอสวมแว่นกันแดดแมตช์กับชุดเดรสสั้นผ้าไหมสีชมพูประดับเลื่อมแวววาวและโค้ตดำสุดเท่",
336
+ en: "The luxury eyewear brand for which she styled sunglasses with a shimmering pink sequined dress and a black coat.",
337
+ zh: "她以一袭亮片粉色复古短裙搭配黑色大衣与该品牌的时尚墨镜漫步米兰街头。"
338
+ }
339
+ },
340
+ {
341
+ q: {
342
+ th: "9. ซิงเกิลเดี่ยวพิเศษที่จวีจิ้งอีปล่อยออกมาฉลองวันคล้ายวันเกิดครบรอบ 30 ปี (18 มิถุนายน 2024) มีชื่อว่าอะไร?",
343
+ en: "9. What is the title of Ju Jingyi's special milestone solo single released for her 30th birthday on June 18, 2024?",
344
+ zh: "9. 2024年6月18日鞠婧祎在30岁生日当天作为特别纪念发行的个人单曲是?"
345
+ },
346
+ options: {
347
+ th: [
348
+ "0.2s (0.2秒)",
349
+ "30th Sunrise",
350
+ "Time Capsule",
351
+ "Golden Horizon"
352
+ ],
353
+ en: [
354
+ "0.2s (0.2秒)",
355
+ "30th Sunrise",
356
+ "Time Capsule",
357
+ "Golden Horizon"
358
+ ],
359
+ zh: [
360
+ "《0.2s》(0.2秒)",
361
+ "《三十而立》",
362
+ "《时光胶囊》",
363
+ "《金色地平线》"
364
+ ]
365
+ },
366
+ answer: 0,
367
+ hint: {
368
+ th: "ชื่อเพลงเป็นตัวเลขทศนิยมและหน่วยวินาที สื่อถึงความรู้สึกที่ก่อตัวขึ้นในเสี้ยววินาที",
369
+ en: "The title is a decimal number followed by the letter 's', conveying feelings sparked in a mere fraction of a second.",
370
+ zh: "歌名由一个带小数点的数字与代表秒的字母组合而成,表达刹那间的永恒触动。"
371
+ }
372
+ },
373
+ {
374
+ q: {
375
+ th: "10. ซิงเกิลเพลงแดนซ์สุดฮิตที่มีท่าเต้นสุดน่ารักและเป็นไวรัลให้แฟนๆ ทั่วเอเชียเต้นคัฟเวอร์คือเพลงใด?",
376
+ en: "10. Which iconic upbeat dance single by Ju Jingyi became a massive viral dance craze covered across Asia?",
377
+ zh: "10. 鞠婧祎风靡亚洲、舞台元气满满且编舞引发全网翻跳狂潮的标志性舞曲单曲是?"
378
+ },
379
+ options: {
380
+ th: [
381
+ "Love Confidential (恋爱告急)",
382
+ "Silent Night",
383
+ "Dance in the Wind",
384
+ "Sweet Whisper"
385
+ ],
386
+ en: [
387
+ "Love Confidential (恋爱告急)",
388
+ "Silent Night",
389
+ "Dance in the Wind",
390
+ "Sweet Whisper"
391
+ ],
392
+ zh: [
393
+ "《恋爱告急》(Love Confidential)",
394
+ "《寂静之夜》",
395
+ "《风中起舞》",
396
+ "《甜蜜悄悄话》"
397
+ ]
398
+ },
399
+ answer: 0,
400
+ hint: {
401
+ th: "เพลงป๊อปจังหวะสดใสที่ชื่อเพลงภาษาจีนแปลตรงตัวว่า 'ความรักอยู่ในภาวะเร่งด่วน/ฉุกเฉิน'",
402
+ en: "A vibrant pop anthem whose Chinese title literally translates to 'Love Emergency / Confidential'.",
403
+ zh: "以红黑格纹贝雷帽等打歌舞台闻名,旋律极其洗脑、歌名带有“告急”二字。"
404
+ }
405
+ },
406
+ {
407
+ q: {
408
+ th: "11. จวีจิ้งอีขึ้นแสดงสเตจในโรงละคร SNH48 Theater อย่างเป็นทางการครั้งแรกในสเตจใด เมื่อวันที่ 2 พฤศจิกายน 2013?",
409
+ en: "11. In which inaugural theater production did Ju Jingyi make her official SNH48 stage debut on November 2, 2013?",
410
+ zh: "11. 2013年11月2日,鞠婧祎在SNH48星梦剧场首次正式登台公演的剧目是?"
411
+ },
412
+ options: {
413
+ th: [
414
+ "Theater Goddess (剧场女神 — สเตจเปิดตัว Team NII)",
415
+ "Give Me Power",
416
+ "Boku no Taiyou",
417
+ "Pajama Drive"
418
+ ],
419
+ en: [
420
+ "Theater Goddess (剧场女神 — Team NII debut stage)",
421
+ "Give Me Power",
422
+ "Boku no Taiyou",
423
+ "Pajama Drive"
424
+ ],
425
+ zh: [
426
+ "《剧场女神》(Theater Goddess — Team NII 创团公演)",
427
+ "《勇往直前》",
428
+ "《我的太阳》",
429
+ "《睡衣大乱斗》"
430
+ ]
431
+ },
432
+ answer: 0,
433
+ hint: {
434
+ th: "ชื่อสเตจการแสดงเปิดตัวตรงกับคำว่า 'เทพธิดาแห่งโรงละคร' ในภาษาไทย",
435
+ en: "The title translates into English as 'Theater Goddess', marking the birth of her stage journey.",
436
+ zh: "公演名称恰好象征着舞台上的璀璨女神,是二期生Team NII的启航之作。"
437
+ }
438
+ },
439
+ {
440
+ q: {
441
+ th: "12. หมุดหมายประวัติศาสตร์ในการก้าวสู่การเป็นศิลปินเดี่ยว คือการก่อตั้ง 'Ju Jingyi Individual Studio' ในเดือนและปีใด?",
442
+ en: "12. In which month and year was the historic 'Ju Jingyi Individual Studio' officially launched?",
443
+ zh: "12. 鞠婧祎正式升入明星殿堂并宣布成立“鞠婧祎个人工作室”的历史性时刻是在哪一年哪一月?"
444
+ },
445
+ options: {
446
+ th: [
447
+ "ธันวาคม 2017 (December 2017)",
448
+ "มิถุนายน 2015",
449
+ "มกราคม 2019",
450
+ "สิงหาคม 2021"
451
+ ],
452
+ en: [
453
+ "December 2017",
454
+ "June 2015",
455
+ "January 2019",
456
+ "August 2021"
457
+ ],
458
+ zh: [
459
+ "2017年12月 (December 2017)",
460
+ "2015年6月",
461
+ "2019年1月",
462
+ "2021年8月"
463
+ ]
464
+ },
465
+ answer: 0,
466
+ hint: {
467
+ th: "เกิดขึ้นช่วงปลายปี 2017 หลังจากที่เธอคว้าชัยชนะอันดับ 1 ในการเลือกตั้งทั่วไป 2 สมัยติดต่อกัน",
468
+ en: "Takes place in late 2017 shortly after her historic back-to-back 1st place general election victories.",
469
+ zh: "在蝉联两届总选举第一名并晋升明星殿堂后,于2017年岁末正式独立启航。"
470
+ }
471
+ }
472
+ ];
473
+
474
+ export const LETTER_LABELS = ['A', 'B', 'C', 'D'];
475
+
476
+ export function shuffleMcpQuizQuestions(questions = MCP_QUIZ_QUESTIONS, count = null) {
477
+ const indexed = questions.map((q, idx) => ({
478
+ ...q,
479
+ originalIndex: q.originalIndex !== undefined ? q.originalIndex : idx
480
+ }));
481
+
482
+ const shuffledQuestions = [...indexed];
483
+ for (let i = shuffledQuestions.length - 1; i > 0; i--) {
484
+ const j = Math.floor(Math.random() * (i + 1));
485
+ [shuffledQuestions[i], shuffledQuestions[j]] = [shuffledQuestions[j], shuffledQuestions[i]];
486
+ }
487
+
488
+ const selected = count ? shuffledQuestions.slice(0, count) : shuffledQuestions;
489
+
490
+ return selected.map(q => {
491
+ const order = [0, 1, 2, 3];
492
+ for (let i = order.length - 1; i > 0; i--) {
493
+ const j = Math.floor(Math.random() * (i + 1));
494
+ [order[i], order[j]] = [order[j], order[i]];
495
+ }
496
+ return {
497
+ originalIndex: q.originalIndex,
498
+ q: q.q,
499
+ hint: q.hint,
500
+ options: {
501
+ th: order.map(idx => q.options.th[idx]),
502
+ en: order.map(idx => q.options.en[idx]),
503
+ zh: order.map(idx => q.options.zh[idx])
504
+ },
505
+ optionOrder: [...order],
506
+ answer: order.indexOf(q.answer)
507
+ };
508
+ });
509
+ }
@@ -1,10 +1,21 @@
1
1
  export function extractWordTokens(text) {
2
2
  if (!text || typeof text !== 'string') return [];
3
- return text
3
+ const normalized = text
4
4
  .toLowerCase()
5
- .replace(/[^\p{L}\p{N}\s]+/gu, ' ')
6
- .split(/\s+/)
7
- .filter(t => t.length >= 2);
5
+ .replace(/[^\p{L}\p{N}\s]+/gu, ' ');
6
+ const hasCjkOrThai = /[\u0e00-\u0e7f\u3400-\u9fff]/u.test(normalized);
7
+ if (hasCjkOrThai && typeof Intl?.Segmenter === 'function') {
8
+ const segmenter = new Intl.Segmenter(undefined, { granularity: 'word' });
9
+ const segmented = [...segmenter.segment(normalized)]
10
+ .filter(item => item.isWordLike)
11
+ .map(item => item.segment)
12
+ .filter(token => token.length >= 1);
13
+ if (segmented.length > 0) return segmented;
14
+ }
15
+ const whitespaceTokens = normalized.split(/\s+/).filter(token => token.length >= 2);
16
+ if (!hasCjkOrThai || whitespaceTokens.length > 1) return whitespaceTokens;
17
+ const compact = whitespaceTokens[0] || '';
18
+ return compact.length >= 2 ? [...compact].flatMap((_, index) => index < compact.length - 1 ? [compact.slice(index, index + 2)] : []) : [];
8
19
  }
9
20
 
10
21
  export function buildTermFrequencyVector(tokens) {
@@ -167,9 +178,10 @@ export class ContextAllocator {
167
178
 
168
179
  scored.sort((a, b) => b.score - a.score);
169
180
 
170
-
171
- const positiveScored = scored.filter(s => s.score > 0.05).slice(0, 4);
172
- const topReflections = positiveScored.length > 0 ? positiveScored : scored.slice(0, 2);
181
+ const REFL_MIN = Number(process.env.KIKU_REFLECTION_MIN_SCORE || 0.05);
182
+ const topScore = scored[0]?.score ?? 0;
183
+ const dynamicFloor = Math.max(REFL_MIN, topScore * 0.3);
184
+ const topReflections = scored.filter(s => s.score >= dynamicFloor).slice(0, 4);
173
185
 
174
186
  const refLines = topReflections.map(item => {
175
187
  const c = item.node.content || {};
@@ -163,7 +163,9 @@ export class AgentMemory {
163
163
  stats: this.stats,
164
164
  lastSavedAt: new Date().toISOString()
165
165
  };
166
- await fs.writeFile(this.storageFile, JSON.stringify(data), { encoding: 'utf-8', mode: 0o600 });
166
+ const tmpFile = `${this.storageFile}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
167
+ await fs.writeFile(tmpFile, JSON.stringify(data), { encoding: 'utf-8', mode: 0o600 });
168
+ await fs.rename(tmpFile, this.storageFile);
167
169
  return true;
168
170
  } catch (error) {
169
171
  this._warnStorage(error);