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
|
@@ -16,8 +16,8 @@ export function estimateTokenCount(textOrMessages) {
|
|
|
16
16
|
|
|
17
17
|
export class ContextCompactor {
|
|
18
18
|
constructor(options = {}) {
|
|
19
|
-
this.maxTokens = options.maxTokens ||
|
|
20
|
-
this.keepRecentMessages = options.keepRecentMessages ||
|
|
19
|
+
this.maxTokens = options.maxTokens || 24000;
|
|
20
|
+
this.keepRecentMessages = options.keepRecentMessages || 8;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
summarizeHistorySync(messages = []) {
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { ResearchSubagent } from './subagent.js';
|
|
2
|
+
import { executeTool } from './tools.js';
|
|
3
|
+
import { eventBus, Events } from './eventBus.js';
|
|
4
|
+
import { mapConcurrent } from './suggestions.js';
|
|
5
|
+
|
|
6
|
+
const CATALOG_TOOLS = Object.freeze([
|
|
7
|
+
'global_search', 'get_profile_and_fandom', 'search_works', 'get_work_detail',
|
|
8
|
+
'get_character_roles', 'search_discography', 'get_music_videos',
|
|
9
|
+
'get_media_links', 'get_brand_endorsements', 'get_career_timeline',
|
|
10
|
+
'get_awards', 'get_social_feed',
|
|
11
|
+
]);
|
|
12
|
+
const WEB_TOOLS = Object.freeze(['web_search', 'web_extract', 'fetch_webpage']);
|
|
13
|
+
const ALL_TOOLS = Object.freeze([...CATALOG_TOOLS, ...WEB_TOOLS]);
|
|
14
|
+
|
|
15
|
+
function debatePlan(topicOrClaim, catalogSnippet = '') {
|
|
16
|
+
const note = catalogSnippet ? `\n[VERIFIED PRIMARY CATALOG KNOWLEDGE]:\n${catalogSnippet}\n` : '';
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
id: 'advocate',
|
|
20
|
+
label: 'Official Advocate (Corroborator)',
|
|
21
|
+
tools: ALL_TOOLS,
|
|
22
|
+
objective: `Find and present verified supporting facts for: "${topicOrClaim}".${note}List confirmed achievements, dates, and official records in clean bullet points with numbers and source citations.`,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'critic',
|
|
26
|
+
label: 'Critical Skeptic (Counter-Evidence)',
|
|
27
|
+
tools: ALL_TOOLS,
|
|
28
|
+
objective: `Examine counter-arguments, misconceptions, timeline boundaries, or missing proof regarding: "${topicOrClaim}".${note}Check if any aspects are overstated, ambiguous, or discontinued, and state nuances clearly.`,
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class DebateOrchestrator {
|
|
34
|
+
constructor(options = {}) {
|
|
35
|
+
if (!options.client) throw new Error('DebateOrchestrator requires an LLM client.');
|
|
36
|
+
this.client = options.client;
|
|
37
|
+
this.executeTool = options.executeTool;
|
|
38
|
+
this.maxWorkers = Math.min(Math.max(options.maxWorkers || 2, 1), 2);
|
|
39
|
+
this.maxIterations = Math.min(Math.max(options.maxIterations || 2, 1), 3);
|
|
40
|
+
this.active = null;
|
|
41
|
+
this.lastResult = null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getStatus() {
|
|
45
|
+
if (!this.active) return { state: 'idle', lastResult: this.lastResult };
|
|
46
|
+
return {
|
|
47
|
+
state: 'running',
|
|
48
|
+
id: this.active.id,
|
|
49
|
+
topic: this.active.topic,
|
|
50
|
+
startedAt: this.active.startedAt,
|
|
51
|
+
workers: [...this.active.workers.values()],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
cancel() {
|
|
56
|
+
if (!this.active) return false;
|
|
57
|
+
this.active.controller.abort();
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async run(topicOrClaim, options = {}) {
|
|
62
|
+
const query = String(topicOrClaim || '').trim();
|
|
63
|
+
if (!query) throw new Error('A debate topic or claim is required.');
|
|
64
|
+
if (this.active) throw new Error('A debate job is already in progress.');
|
|
65
|
+
|
|
66
|
+
const controller = new AbortController();
|
|
67
|
+
const abortParent = () => controller.abort(options.signal?.reason);
|
|
68
|
+
options.signal?.addEventListener('abort', abortParent, { once: true });
|
|
69
|
+
|
|
70
|
+
const sharedToolCache = new Map();
|
|
71
|
+
const cachedExecute = async (name, args) => {
|
|
72
|
+
const key = `${name}:${JSON.stringify(args || {})}`;
|
|
73
|
+
if (!sharedToolCache.has(key)) {
|
|
74
|
+
sharedToolCache.set(key, Promise.resolve((this.executeTool || executeTool)(name, args)));
|
|
75
|
+
}
|
|
76
|
+
return sharedToolCache.get(key);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
let catalogEvidence = '';
|
|
80
|
+
try {
|
|
81
|
+
const gRes = await cachedExecute('global_search', { query });
|
|
82
|
+
if (gRes && (gRes.returnedCount > 0 || gRes.count > 0)) {
|
|
83
|
+
catalogEvidence += JSON.stringify(gRes, null, 2) + '\n';
|
|
84
|
+
}
|
|
85
|
+
const lowerQ = query.toLowerCase();
|
|
86
|
+
if (lowerQ.includes('snh') || lowerQ.includes('เลือกตั้ง') || lowerQ.includes('award') || lowerQ.includes('รางวัล')) {
|
|
87
|
+
const awardsRes = await cachedExecute('get_awards', {});
|
|
88
|
+
if (awardsRes?.awards) {
|
|
89
|
+
const matched = awardsRes.awards.filter(a => JSON.stringify(a).toLowerCase().includes('snh') || JSON.stringify(a).toLowerCase().includes('election') || JSON.stringify(a).toLowerCase().includes('总选举'));
|
|
90
|
+
if (matched.length > 0) catalogEvidence += 'Awards:\n' + JSON.stringify(matched, null, 2) + '\n';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (lowerQ.includes('timeline') || lowerQ.includes('ประวัติ') || lowerQ.includes('ปี')) {
|
|
94
|
+
const tlRes = await cachedExecute('get_career_timeline', {});
|
|
95
|
+
if (tlRes?.timeline) catalogEvidence += 'Timeline:\n' + JSON.stringify(tlRes.timeline.slice(0, 5), null, 2) + '\n';
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
|
|
99
|
+
const plan = debatePlan(query, catalogEvidence.slice(0, 3000));
|
|
100
|
+
const job = {
|
|
101
|
+
id: `debate_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
102
|
+
topic: query,
|
|
103
|
+
startedAt: new Date().toISOString(),
|
|
104
|
+
controller,
|
|
105
|
+
workers: new Map(plan.map(task => [task.id, { id: task.id, label: task.label, state: 'pending', toolCalls: 0 }])),
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
this.active = job;
|
|
109
|
+
options.onProgress?.({ type: 'start', id: job.id, workers: plan.length });
|
|
110
|
+
|
|
111
|
+
eventBus.emit(Events.AGENT_PLAN_START, {
|
|
112
|
+
planId: job.id,
|
|
113
|
+
question: `Debate: ${query}`,
|
|
114
|
+
steps: [
|
|
115
|
+
...plan.map(p => ({ id: p.id, label: p.label, state: 'pending', objective: p.objective })),
|
|
116
|
+
{ id: 'judge', label: 'Neutral Judge Verdict', state: 'pending', objective: 'Synthesize advocate & skeptic findings into a final truth matrix.' }
|
|
117
|
+
]
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const isLocalProvider = Boolean(
|
|
121
|
+
this.client?.provider === 'lmstudio' ||
|
|
122
|
+
this.client?.provider === 'ollama' ||
|
|
123
|
+
this.client?.isLocal ||
|
|
124
|
+
this.client?.getProviderInfo?.()?.isLocal
|
|
125
|
+
);
|
|
126
|
+
const concurrency = isLocalProvider ? 1 : Math.min(this.maxWorkers, plan.length);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const workerResults = await mapConcurrent(plan, concurrency, async task => {
|
|
130
|
+
const status = job.workers.get(task.id);
|
|
131
|
+
status.state = 'running';
|
|
132
|
+
options.onProgress?.({ type: 'worker-start', id: task.id, label: task.label });
|
|
133
|
+
eventBus.emit(Events.AGENT_PLAN_STEP_UPDATE, {
|
|
134
|
+
planId: job.id,
|
|
135
|
+
stepId: task.id,
|
|
136
|
+
state: 'running',
|
|
137
|
+
label: task.label,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const worker = new ResearchSubagent({
|
|
141
|
+
client: this.client,
|
|
142
|
+
executeTool: cachedExecute,
|
|
143
|
+
allowedTools: task.tools,
|
|
144
|
+
maxIterations: this.maxIterations,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const result = await worker.run(task.objective, {
|
|
148
|
+
signal: controller.signal,
|
|
149
|
+
skillContext: options.skillContext,
|
|
150
|
+
onProgress: event => {
|
|
151
|
+
if (event.type === 'tool') status.toolCalls += 1;
|
|
152
|
+
options.onProgress?.({ ...event, workerId: task.id, label: task.label });
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
status.state = 'complete';
|
|
157
|
+
options.onProgress?.({ type: 'worker-complete', id: task.id, label: task.label });
|
|
158
|
+
eventBus.emit(Events.AGENT_PLAN_STEP_UPDATE, {
|
|
159
|
+
planId: job.id,
|
|
160
|
+
stepId: task.id,
|
|
161
|
+
state: 'complete',
|
|
162
|
+
label: task.label,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
let ans = result.answer || '';
|
|
166
|
+
if (!ans.trim() && catalogEvidence) {
|
|
167
|
+
ans = task.id === 'advocate'
|
|
168
|
+
? `Primary records confirm relevant Ju Jingyi milestones:\n${catalogEvidence.slice(0, 600)}`
|
|
169
|
+
: `Timeline contextual limits:\n${catalogEvidence.slice(0, 600)}`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { id: task.id, label: task.label, ...result, answer: ans };
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
options.onProgress?.({ type: 'synthesizing', label: 'Synthesizing Neutral Verdict' });
|
|
177
|
+
eventBus.emit(Events.AGENT_PLAN_STEP_UPDATE, {
|
|
178
|
+
planId: job.id,
|
|
179
|
+
stepId: 'judge',
|
|
180
|
+
state: 'running',
|
|
181
|
+
label: 'Neutral Judge Verdict',
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const advocateData = workerResults.find(r => r.id === 'advocate')?.answer || 'No advocate response';
|
|
185
|
+
const criticData = workerResults.find(r => r.id === 'critic')?.answer || 'No critic response';
|
|
186
|
+
|
|
187
|
+
const judgePrompt = `You are a neutral fact-checking judge examining a claim or topic about Ju Jingyi (鞠婧祎).
|
|
188
|
+
Topic / Claim: "${query}"
|
|
189
|
+
|
|
190
|
+
[ADVOCATE / SUPPORTING ARGUMENTS & EVIDENCE]:
|
|
191
|
+
${advocateData}
|
|
192
|
+
|
|
193
|
+
[CRITIC / COUNTER-EVIDENCE & SKEPTIC FINDINGS]:
|
|
194
|
+
${criticData}
|
|
195
|
+
|
|
196
|
+
Provide a structured, unbiased evaluation in markdown:
|
|
197
|
+
1. **Verdict Summary**: (VERIFIED TRUE | PARTIALLY ACCURATE / CONTEXT NEEDED | UNVERIFIED | FALSE / RUMOR)
|
|
198
|
+
2. **Confidence Score**: (0-100%)
|
|
199
|
+
3. **Key Corroborated Facts**: Bullet points of confirmed truth
|
|
200
|
+
4. **Counter-Points & Nuances**: Bullet points of debunked aspects or ambiguities
|
|
201
|
+
5. **Final Conclusion**: Clear, authoritative 2-3 sentence conclusion.`;
|
|
202
|
+
|
|
203
|
+
const judgeResponse = await this.client.chat({
|
|
204
|
+
messages: [{ role: 'user', content: judgePrompt }],
|
|
205
|
+
temperature: 0.2,
|
|
206
|
+
signal: controller.signal,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const verdictText = judgeResponse?.content || judgeResponse?.message?.content || 'Unable to generate synthesis.';
|
|
210
|
+
|
|
211
|
+
eventBus.emit(Events.AGENT_PLAN_STEP_UPDATE, {
|
|
212
|
+
planId: job.id,
|
|
213
|
+
stepId: 'judge',
|
|
214
|
+
state: 'complete',
|
|
215
|
+
label: 'Neutral Judge Verdict',
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const finalOutcome = {
|
|
219
|
+
id: job.id,
|
|
220
|
+
topic: query,
|
|
221
|
+
advocate: advocateData,
|
|
222
|
+
critic: criticData,
|
|
223
|
+
verdict: verdictText,
|
|
224
|
+
workers: workerResults,
|
|
225
|
+
completedAt: new Date().toISOString(),
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
this.lastResult = finalOutcome;
|
|
229
|
+
return finalOutcome;
|
|
230
|
+
} finally {
|
|
231
|
+
this.active = null;
|
|
232
|
+
options.signal?.removeEventListener('abort', abortParent);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
package/server/agent/eventBus.js
CHANGED
|
@@ -88,6 +88,9 @@ export const Events = Object.freeze({
|
|
|
88
88
|
AGENT_PLAN_STEP_UPDATE: 'agent:plan:step:update',
|
|
89
89
|
AGENT_PLAN_COMPLETE: 'agent:plan:complete',
|
|
90
90
|
|
|
91
|
+
AGENT_CLARIFYING_QUESTIONS: 'agent:clarifying:questions',
|
|
92
|
+
AGENT_CLARIFYING_ANSWER: 'agent:clarifying:answer',
|
|
93
|
+
|
|
91
94
|
MUSIC_PLAY: 'music:play',
|
|
92
95
|
MUSIC_STOP: 'music:stop',
|
|
93
96
|
MUSIC_QUEUE_CHANGE: 'music:queue:change',
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
|
|
4
|
+
// Minimum feedback samples on a feature before it becomes a learned directive.
|
|
5
|
+
export const FEEDBACK_MIN_SAMPLES = 5;
|
|
6
|
+
// Like-rate at/above this promotes a "prefer" directive.
|
|
7
|
+
export const FEEDBACK_LIKE_THRESHOLD = 0.7;
|
|
8
|
+
// Like-rate at/below this promotes an "avoid" directive.
|
|
9
|
+
export const FEEDBACK_DISLIKE_THRESHOLD = 0.35;
|
|
10
|
+
export const FEEDBACK_MAX_DIRECTIVES = 6;
|
|
11
|
+
const MAX_EVENTS = 500;
|
|
12
|
+
|
|
13
|
+
export function detectAnswerLanguage(text = '') {
|
|
14
|
+
const s = String(text);
|
|
15
|
+
const th = (s.match(/[\u0E00-\u0E7F]/g) || []).length;
|
|
16
|
+
const cjk = (s.match(/[\u4E00-\u9FFF\u3400-\u4DBF]/g) || []).length;
|
|
17
|
+
const latin = (s.match(/[A-Za-z]/g) || []).length;
|
|
18
|
+
if (th > 0 && th >= cjk && th >= latin * 0.1) return 'th';
|
|
19
|
+
if (cjk > latin) return 'zh';
|
|
20
|
+
return 'en';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function extractAnswerFeatures(answer = '', toolsUsed = []) {
|
|
24
|
+
const text = String(answer || '');
|
|
25
|
+
const length = text.length;
|
|
26
|
+
const lengthBucket = length < 600 ? 'short' : length <= 2000 ? 'medium' : 'long';
|
|
27
|
+
const lines = text.split('\n');
|
|
28
|
+
const hasHeaders = /^#{1,4}\s+\S/m.test(text);
|
|
29
|
+
const hasBullets = lines.filter(l => /^\s*[-*•]\s+\S/.test(l)).length >= 2;
|
|
30
|
+
const hasNumbered = lines.filter(l => /^\s*\d+[.)]\s+\S/.test(l)).length >= 2;
|
|
31
|
+
const hasTable = /\|.+\|/.test(text) && lines.filter(l => /^\s*\|?[\s:|-]+\|?\s*$/.test(l)).length >= 1;
|
|
32
|
+
const videoLinks = (text.match(/youtube\.com|youtu\.be|bilibili\.com|douyin\.com|youku\.com/gi) || []).length;
|
|
33
|
+
const totalLinks = (text.match(/https?:\/\/\S+/gi) || []).length;
|
|
34
|
+
const tools = Array.isArray(toolsUsed) ? toolsUsed.map(String) : [];
|
|
35
|
+
const usedDeepResearch = tools.some(t => /run_subagent|run_deep_research|verify_claim|web_search|web_extract|fetch_api/.test(t));
|
|
36
|
+
return {
|
|
37
|
+
length,
|
|
38
|
+
lengthBucket,
|
|
39
|
+
hasHeaders,
|
|
40
|
+
hasBullets: hasBullets || hasNumbered,
|
|
41
|
+
hasTable,
|
|
42
|
+
videoLinks,
|
|
43
|
+
totalLinks,
|
|
44
|
+
language: detectAnswerLanguage(text),
|
|
45
|
+
usedDeepResearch,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Each definition maps one observable answer trait to an optional "prefer"
|
|
50
|
+
// directive (high like-rate) and/or an "avoid" directive (low like-rate).
|
|
51
|
+
// Traits whose dislike-side is ambiguous (e.g. disliking a short answer does
|
|
52
|
+
// not imply verbosity is wanted) only define the like side.
|
|
53
|
+
const FEATURE_DEFS = [
|
|
54
|
+
{
|
|
55
|
+
key: 'len:short',
|
|
56
|
+
test: f => f.lengthBucket === 'short',
|
|
57
|
+
like: 'ผู้ใช้ชอบคำตอบสั้นกระชับ ตอบตรงประเด็น ไม่ยืดเยื้อ',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
key: 'len:long',
|
|
61
|
+
test: f => f.lengthBucket === 'long',
|
|
62
|
+
like: 'ผู้ใช้ชอบคำตอบละเอียดเชิงลึกเป็นบางครั้ง แต่ต้องมีโครงสร้างชัดเจน',
|
|
63
|
+
dislike: 'หลีกเลี่ยงคำตอบที่ยาวยืดเยื้อ ควรสรุปให้กระชับและจัดโครงสร้างให้อ่านง่าย',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
key: 'fmt:headers',
|
|
67
|
+
test: f => f.hasHeaders,
|
|
68
|
+
like: 'ใช้หัวข้อ (headers) จัดโครงสร้างคำตอบให้อ่านง่าย',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: 'fmt:bullets',
|
|
72
|
+
test: f => f.hasBullets,
|
|
73
|
+
like: 'ใช้ bullet/ลำดับข้อสรุปประเด็นสำคัญ ไม่เขียนเป็นย่อหน้ายาวล้วน',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: 'fmt:table',
|
|
77
|
+
test: f => f.hasTable,
|
|
78
|
+
like: 'ใช้ตารางเปรียบเทียบเมื่อต้องนำเสนอข้อมูลหลายรายการ',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
key: 'fmt:plain',
|
|
82
|
+
test: f => !f.hasHeaders && !f.hasBullets && !f.hasTable && f.length > 600,
|
|
83
|
+
dislike: 'หลีกเลี่ยงคำตอบที่เป็นข้อความล้วนยาวๆ โดยไม่มีหัวข้อ bullet หรือตาราง',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
key: 'links:video',
|
|
87
|
+
test: f => f.videoLinks >= 1,
|
|
88
|
+
like: 'แนบลิงก์วิดีโอ/YouTube ที่ตรวจสอบแล้วประกอบคำตอบ',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: 'depth:research',
|
|
92
|
+
test: f => f.usedDeepResearch,
|
|
93
|
+
like: 'ผู้ใช้เห็นคุณค่าของคำตอบที่มีหลักฐานจากการค้นคว้า/ตรวจสอบก่อนตอบ',
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
export class FeedbackLearning {
|
|
98
|
+
constructor(options = {}) {
|
|
99
|
+
this.persistenceEnabled = options.persistence !== false;
|
|
100
|
+
this.storagePath = options.storagePath || join(process.cwd(), '.kiku_sessions', 'answer_feedback.json');
|
|
101
|
+
this.state = { totals: { like: 0, dislike: 0 }, featureStats: {}, directives: [], recent: [] };
|
|
102
|
+
this._load();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
_load() {
|
|
106
|
+
if (!this.persistenceEnabled) return;
|
|
107
|
+
try {
|
|
108
|
+
if (existsSync(this.storagePath)) {
|
|
109
|
+
const data = JSON.parse(readFileSync(this.storagePath, 'utf-8'));
|
|
110
|
+
this.state = {
|
|
111
|
+
totals: { like: 0, dislike: 0, ...(data.totals || {}) },
|
|
112
|
+
featureStats: data.featureStats || {},
|
|
113
|
+
directives: Array.isArray(data.directives) ? data.directives : [],
|
|
114
|
+
recent: Array.isArray(data.recent) ? data.recent.slice(-100) : [],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
} catch {}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
_save() {
|
|
121
|
+
if (!this.persistenceEnabled) return;
|
|
122
|
+
try {
|
|
123
|
+
const dir = dirname(this.storagePath);
|
|
124
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
125
|
+
writeFileSync(this.storagePath, JSON.stringify(this.state, null, 2), { mode: 0o600 });
|
|
126
|
+
} catch {}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
record(event = {}) {
|
|
130
|
+
const rating = String(event.rating || '').toLowerCase();
|
|
131
|
+
if (rating !== 'like' && rating !== 'dislike') {
|
|
132
|
+
throw new Error('rating must be "like" or "dislike".');
|
|
133
|
+
}
|
|
134
|
+
const answer = String(event.answer || '').slice(0, 5000);
|
|
135
|
+
const toolsUsed = Array.isArray(event.toolsUsed)
|
|
136
|
+
? event.toolsUsed.map(t => String(t).slice(0, 80)).slice(0, 20)
|
|
137
|
+
: [];
|
|
138
|
+
const features = extractAnswerFeatures(answer, toolsUsed);
|
|
139
|
+
|
|
140
|
+
this.state.totals[rating] += 1;
|
|
141
|
+
for (const def of FEATURE_DEFS) {
|
|
142
|
+
let hit = false;
|
|
143
|
+
try { hit = def.test(features) === true; } catch { hit = false; }
|
|
144
|
+
if (!hit) continue;
|
|
145
|
+
const stat = this.state.featureStats[def.key] || { likes: 0, dislikes: 0 };
|
|
146
|
+
if (rating === 'like') stat.likes += 1;
|
|
147
|
+
else stat.dislikes += 1;
|
|
148
|
+
this.state.featureStats[def.key] = stat;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.state.recent.push({
|
|
152
|
+
at: new Date().toISOString(),
|
|
153
|
+
rating,
|
|
154
|
+
messageId: String(event.messageId || '').slice(0, 100),
|
|
155
|
+
sessionId: String(event.sessionId || '').slice(0, 100),
|
|
156
|
+
query: String(event.query || '').slice(0, 300),
|
|
157
|
+
answerExcerpt: answer.slice(0, 500),
|
|
158
|
+
answerLength: answer.length,
|
|
159
|
+
toolsUsed,
|
|
160
|
+
model: String(event.model || '').slice(0, 120),
|
|
161
|
+
persona: String(event.persona || '').slice(0, 60),
|
|
162
|
+
});
|
|
163
|
+
if (this.state.recent.length > MAX_EVENTS) {
|
|
164
|
+
this.state.recent = this.state.recent.slice(-MAX_EVENTS);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this._recomputeDirectives();
|
|
168
|
+
this._save();
|
|
169
|
+
return { totals: { ...this.state.totals }, directives: this.state.directives.map(d => ({ ...d })) };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_recomputeDirectives() {
|
|
173
|
+
const out = [];
|
|
174
|
+
for (const def of FEATURE_DEFS) {
|
|
175
|
+
const stat = this.state.featureStats[def.key];
|
|
176
|
+
if (!stat) continue;
|
|
177
|
+
const samples = stat.likes + stat.dislikes;
|
|
178
|
+
if (samples < FEEDBACK_MIN_SAMPLES) continue;
|
|
179
|
+
const likeRate = stat.likes / samples;
|
|
180
|
+
if (def.like && likeRate >= FEEDBACK_LIKE_THRESHOLD) {
|
|
181
|
+
out.push({ id: `prefer:${def.key}`, kind: 'prefer', text: def.like, support: samples, likeRate: Number(likeRate.toFixed(2)), updatedAt: new Date().toISOString() });
|
|
182
|
+
} else if (def.dislike && likeRate <= FEEDBACK_DISLIKE_THRESHOLD) {
|
|
183
|
+
out.push({ id: `avoid:${def.key}`, kind: 'avoid', text: def.dislike, support: samples, likeRate: Number(likeRate.toFixed(2)), updatedAt: new Date().toISOString() });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Prefer stronger evidence first; cap so the prompt stays lean.
|
|
187
|
+
out.sort((a, b) => b.support - a.support || (b.kind === 'avoid' ? 1 : 0) - (a.kind === 'avoid' ? 1 : 0));
|
|
188
|
+
this.state.directives = out.slice(0, FEEDBACK_MAX_DIRECTIVES);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
buildStyleContext() {
|
|
192
|
+
const directives = this.state.directives;
|
|
193
|
+
if (!directives.length) return '';
|
|
194
|
+
const prefers = directives.filter(d => d.kind === 'prefer').map(d => `- ${d.text}`);
|
|
195
|
+
const avoids = directives.filter(d => d.kind === 'avoid').map(d => `- ${d.text}`);
|
|
196
|
+
const lines = [
|
|
197
|
+
`สิ่งที่เรียนรู้จาก like/dislike ของผู้ใช้ (${this.state.totals.like} likes, ${this.state.totals.dislike} dislikes):`,
|
|
198
|
+
];
|
|
199
|
+
if (prefers.length) lines.push(`ตอบแนวนี้:\n${prefers.join('\n')}`);
|
|
200
|
+
if (avoids.length) lines.push(`หลีกเลี่ยงแนวนี้:\n${avoids.join('\n')}`);
|
|
201
|
+
return lines.join('\n');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
getStats() {
|
|
205
|
+
const featureWinRates = {};
|
|
206
|
+
for (const [key, stat] of Object.entries(this.state.featureStats)) {
|
|
207
|
+
const samples = stat.likes + stat.dislikes;
|
|
208
|
+
featureWinRates[key] = { ...stat, samples, likeRate: samples ? Number((stat.likes / samples).toFixed(2)) : null };
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
totals: { ...this.state.totals },
|
|
212
|
+
directives: this.state.directives.map(d => ({ ...d })),
|
|
213
|
+
featureWinRates,
|
|
214
|
+
recentCount: this.state.recent.length,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|