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
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
enabled: true,
|
|
8
|
+
confidenceThreshold: 0.65,
|
|
9
|
+
maxRounds: 2,
|
|
10
|
+
timeoutMs: 30000,
|
|
11
|
+
preferLLMQuestions: false,
|
|
12
|
+
trackedCategories: [
|
|
13
|
+
'timeframe', 'scope', 'depth', 'comparison', 'media_type',
|
|
14
|
+
'language', 'specific_work', 'aspect_focus', 'date_range', 'content_format'
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const ANALYTICS_RETENTION_DAYS = 30;
|
|
19
|
+
|
|
20
|
+
export class ClarificationAnalytics {
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.storagePath = options.storagePath || join(process.cwd(), '.kiku_sessions', 'clarification_analytics.json');
|
|
23
|
+
this.stats = {
|
|
24
|
+
totalQueries: 0,
|
|
25
|
+
totalClarifications: 0,
|
|
26
|
+
totalSkipped: 0,
|
|
27
|
+
totalTimedOut: 0,
|
|
28
|
+
roundsHistogram: {},
|
|
29
|
+
categoryStats: {},
|
|
30
|
+
confidenceBuckets: {},
|
|
31
|
+
recentSessions: [],
|
|
32
|
+
dailyStats: {},
|
|
33
|
+
};
|
|
34
|
+
this._maxRecentSessions = options.maxRecentSessions || 100;
|
|
35
|
+
this._load();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_load() {
|
|
39
|
+
try {
|
|
40
|
+
if (existsSync(this.storagePath)) {
|
|
41
|
+
const data = JSON.parse(readFileSync(this.storagePath, 'utf-8'));
|
|
42
|
+
this.stats = { ...this.stats, ...data };
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_save() {
|
|
50
|
+
try {
|
|
51
|
+
const dir = dirname(this.storagePath);
|
|
52
|
+
if (!existsSync(dir)) {
|
|
53
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
54
|
+
}
|
|
55
|
+
writeFileSync(this.storagePath, JSON.stringify(this.stats, null, 2), { mode: 0o600 });
|
|
56
|
+
} catch {
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
recordClarificationStart(queryId, questions, confidenceScore) {
|
|
63
|
+
const today = new Date().toISOString().split('T')[0];
|
|
64
|
+
|
|
65
|
+
const session = {
|
|
66
|
+
queryId,
|
|
67
|
+
startedAt: Date.now(),
|
|
68
|
+
confidenceScore,
|
|
69
|
+
rounds: [{ round: 1, questions: questions.map(q => q.category) }],
|
|
70
|
+
totalQuestions: questions.length,
|
|
71
|
+
answered: false,
|
|
72
|
+
skipped: false,
|
|
73
|
+
timedOut: false,
|
|
74
|
+
finalRound: 1,
|
|
75
|
+
answerSummary: null,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
this.stats.totalClarifications++;
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if (!this.stats.dailyStats[today]) {
|
|
83
|
+
this.stats.dailyStats[today] = { asked: 0, answered: 0, skipped: 0 };
|
|
84
|
+
}
|
|
85
|
+
this.stats.dailyStats[today].asked++;
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
for (const q of questions) {
|
|
89
|
+
if (!this.stats.categoryStats[q.category]) {
|
|
90
|
+
this.stats.categoryStats[q.category] = { asked: 0, answered: 0, totalResponseTimeMs: 0 };
|
|
91
|
+
}
|
|
92
|
+
this.stats.categoryStats[q.category].asked++;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
const bucket = this._getConfidenceBucket(confidenceScore);
|
|
97
|
+
this.stats.confidenceBuckets[bucket] = (this.stats.confidenceBuckets[bucket] || 0) + 1;
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
this.stats.recentSessions.unshift(session);
|
|
101
|
+
if (this.stats.recentSessions.length > this._maxRecentSessions) {
|
|
102
|
+
this.stats.recentSessions = this.stats.recentSessions.slice(0, this._maxRecentSessions);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this._save();
|
|
106
|
+
return session;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
recordClarificationAnswer(queryId, answers, round, responseTimeMs) {
|
|
111
|
+
const session = this.stats.recentSessions.find(s => s.queryId === queryId);
|
|
112
|
+
if (!session) return;
|
|
113
|
+
|
|
114
|
+
session.answered = true;
|
|
115
|
+
session.answerSummary = answers;
|
|
116
|
+
session.finalRound = round;
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if (round > 1) {
|
|
120
|
+
session.rounds.push({ round, questions: Object.keys(answers) });
|
|
121
|
+
session.totalQuestions += Object.keys(answers).length;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
for (const [category] of Object.entries(answers)) {
|
|
126
|
+
if (this.stats.categoryStats[category]) {
|
|
127
|
+
this.stats.categoryStats[category].answered++;
|
|
128
|
+
this.stats.categoryStats[category].totalResponseTimeMs += responseTimeMs;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
const today = new Date().toISOString().split('T')[0];
|
|
134
|
+
if (this.stats.dailyStats[today]) {
|
|
135
|
+
this.stats.dailyStats[today].answered++;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
this.stats.roundsHistogram[round] = (this.stats.roundsHistogram[round] || 0) + 1;
|
|
140
|
+
|
|
141
|
+
this._save();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
recordClarificationSkipped(queryId) {
|
|
146
|
+
const session = this.stats.recentSessions.find(s => s.queryId === queryId);
|
|
147
|
+
if (session) {
|
|
148
|
+
session.skipped = true;
|
|
149
|
+
session.answered = false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.stats.totalSkipped++;
|
|
153
|
+
|
|
154
|
+
const today = new Date().toISOString().split('T')[0];
|
|
155
|
+
if (this.stats.dailyStats[today]) {
|
|
156
|
+
this.stats.dailyStats[today].skipped++;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this._save();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
recordClarificationTimeout(queryId) {
|
|
164
|
+
const session = this.stats.recentSessions.find(s => s.queryId === queryId);
|
|
165
|
+
if (session) {
|
|
166
|
+
session.timedOut = true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.stats.totalTimedOut++;
|
|
170
|
+
this._save();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
getAnalytics(options = {}) {
|
|
175
|
+
const { days = 7 } = options;
|
|
176
|
+
const cutoffDate = new Date();
|
|
177
|
+
cutoffDate.setDate(cutoffDate.getDate() - days);
|
|
178
|
+
const cutoffStr = cutoffDate.toISOString().split('T')[0];
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
const periodStats = {};
|
|
182
|
+
for (const [date, stats] of Object.entries(this.stats.dailyStats)) {
|
|
183
|
+
if (date >= cutoffStr) {
|
|
184
|
+
periodStats[date] = stats;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
let periodAsked = 0, periodAnswered = 0, periodSkipped = 0;
|
|
190
|
+
for (const stats of Object.values(periodStats)) {
|
|
191
|
+
periodAsked += stats.asked;
|
|
192
|
+
periodAnswered += stats.answered;
|
|
193
|
+
periodSkipped += stats.skipped;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
const categoryEffectiveness = {};
|
|
198
|
+
for (const [category, stats] of Object.entries(this.stats.categoryStats)) {
|
|
199
|
+
const avgResponseTime = stats.answered > 0
|
|
200
|
+
? Math.round(stats.totalResponseTimeMs / stats.answered)
|
|
201
|
+
: 0;
|
|
202
|
+
categoryEffectiveness[category] = {
|
|
203
|
+
asked: stats.asked,
|
|
204
|
+
answered: stats.answered,
|
|
205
|
+
skipRate: stats.asked > 0
|
|
206
|
+
? ((stats.asked - stats.answered) / stats.asked * 100).toFixed(1) + '%'
|
|
207
|
+
: '0%',
|
|
208
|
+
avgResponseTimeMs: avgResponseTime,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
const sessionsWithRounds = this.stats.recentSessions.filter(s => s.answered);
|
|
214
|
+
const avgRounds = sessionsWithRounds.length > 0
|
|
215
|
+
? (sessionsWithRounds.reduce((sum, s) => sum + s.finalRound, 0) / sessionsWithRounds.length).toFixed(2)
|
|
216
|
+
: 0;
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
summary: {
|
|
220
|
+
totalClarifications: this.stats.totalClarifications,
|
|
221
|
+
totalAnswered: this.stats.totalClarifications - this.stats.totalSkipped - this.stats.totalTimedOut,
|
|
222
|
+
totalSkipped: this.stats.totalSkipped,
|
|
223
|
+
totalTimedOut: this.stats.totalTimedOut,
|
|
224
|
+
overallSkipRate: this.stats.totalClarifications > 0
|
|
225
|
+
? ((this.stats.totalSkipped / this.stats.totalClarifications) * 100).toFixed(1) + '%'
|
|
226
|
+
: '0%',
|
|
227
|
+
avgRoundsPerSession: parseFloat(avgRounds),
|
|
228
|
+
},
|
|
229
|
+
period: {
|
|
230
|
+
days,
|
|
231
|
+
asked: periodAsked,
|
|
232
|
+
answered: periodAnswered,
|
|
233
|
+
skipped: periodSkipped,
|
|
234
|
+
dailyBreakdown: periodStats,
|
|
235
|
+
},
|
|
236
|
+
categories: categoryEffectiveness,
|
|
237
|
+
roundsDistribution: this.stats.roundsHistogram,
|
|
238
|
+
confidenceBuckets: this.stats.confidenceBuckets,
|
|
239
|
+
recentSessions: this.stats.recentSessions.slice(0, 10),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
getCategoryEffectiveness(category) {
|
|
245
|
+
const stats = this.stats.categoryStats[category];
|
|
246
|
+
if (!stats || stats.asked === 0) return 50;
|
|
247
|
+
|
|
248
|
+
const answerRate = stats.answered / stats.asked;
|
|
249
|
+
const avgResponseTime = stats.answered > 0
|
|
250
|
+
? stats.totalResponseTimeMs / stats.answered
|
|
251
|
+
: 5000;
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
const responseScore = Math.max(0, 1 - (avgResponseTime / 10000));
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
return Math.round((answerRate * 0.7 + responseScore * 0.3) * 100);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
getTopCategories(n = 5) {
|
|
262
|
+
const categories = Object.keys(this.stats.categoryStats);
|
|
263
|
+
const scored = categories.map(cat => ({
|
|
264
|
+
category: cat,
|
|
265
|
+
effectiveness: this.getCategoryEffectiveness(cat),
|
|
266
|
+
asked: this.stats.categoryStats[cat].asked,
|
|
267
|
+
}));
|
|
268
|
+
return scored.sort((a, b) => b.effectiveness - a.effectiveness).slice(0, n);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
cleanup(olderThanDays = ANALYTICS_RETENTION_DAYS) {
|
|
273
|
+
const cutoffDate = new Date();
|
|
274
|
+
cutoffDate.setDate(cutoffDate.getDate() - olderThanDays);
|
|
275
|
+
const cutoffStr = cutoffDate.toISOString().split('T')[0];
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
for (const date of Object.keys(this.stats.dailyStats)) {
|
|
279
|
+
if (date < cutoffStr) {
|
|
280
|
+
delete this.stats.dailyStats[date];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
this.stats.recentSessions = this.stats.recentSessions.filter(
|
|
286
|
+
s => s.startedAt > cutoffDate.getTime()
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
this._save();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
_getConfidenceBucket(score) {
|
|
293
|
+
if (score < 0.2) return '0-20';
|
|
294
|
+
if (score < 0.4) return '20-40';
|
|
295
|
+
if (score < 0.6) return '40-60';
|
|
296
|
+
if (score < 0.8) return '60-80';
|
|
297
|
+
return '80-100';
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export class ClarificationConfig {
|
|
302
|
+
constructor(options = {}) {
|
|
303
|
+
this.storagePath = options.storagePath || join(process.cwd(), '.kiku_sessions', 'clarification_config.json');
|
|
304
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
305
|
+
this._load();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
_load() {
|
|
309
|
+
try {
|
|
310
|
+
if (existsSync(this.storagePath)) {
|
|
311
|
+
const data = JSON.parse(readFileSync(this.storagePath, 'utf-8'));
|
|
312
|
+
this.config = { ...DEFAULT_CONFIG, ...data };
|
|
313
|
+
}
|
|
314
|
+
} catch {
|
|
315
|
+
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
_save() {
|
|
320
|
+
try {
|
|
321
|
+
const dir = dirname(this.storagePath);
|
|
322
|
+
if (!existsSync(dir)) {
|
|
323
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
324
|
+
}
|
|
325
|
+
writeFileSync(this.storagePath, JSON.stringify(this.config, null, 2), { mode: 0o600 });
|
|
326
|
+
} catch {
|
|
327
|
+
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
getConfig() {
|
|
333
|
+
return { ...this.config };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
updateConfig(updates) {
|
|
338
|
+
|
|
339
|
+
if (updates.confidenceThreshold !== undefined) {
|
|
340
|
+
const val = Number(updates.confidenceThreshold);
|
|
341
|
+
if (isNaN(val) || val < 0 || val > 1) {
|
|
342
|
+
throw new Error('confidenceThreshold must be between 0 and 1');
|
|
343
|
+
}
|
|
344
|
+
this.config.confidenceThreshold = val;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (updates.maxRounds !== undefined) {
|
|
348
|
+
const val = Number(updates.maxRounds);
|
|
349
|
+
if (isNaN(val) || val < 1 || val > 5) {
|
|
350
|
+
throw new Error('maxRounds must be between 1 and 5');
|
|
351
|
+
}
|
|
352
|
+
this.config.maxRounds = val;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (updates.timeoutMs !== undefined) {
|
|
356
|
+
const val = Number(updates.timeoutMs);
|
|
357
|
+
if (isNaN(val) || val < 5000 || val > 60000) {
|
|
358
|
+
throw new Error('timeoutMs must be between 5000 and 60000');
|
|
359
|
+
}
|
|
360
|
+
this.config.timeoutMs = val;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (updates.enabled !== undefined) {
|
|
364
|
+
this.config.enabled = Boolean(updates.enabled);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (updates.preferLLMQuestions !== undefined) {
|
|
368
|
+
this.config.preferLLMQuestions = Boolean(updates.preferLLMQuestions);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
this._save();
|
|
372
|
+
return this.getConfig();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
reset() {
|
|
377
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
378
|
+
this._save();
|
|
379
|
+
return this.getConfig();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
get(key) {
|
|
384
|
+
return this.config[key];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
set(key, value) {
|
|
389
|
+
this.config[key] = value;
|
|
390
|
+
this._save();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export function formatAnalyticsForCLI(analytics, colors = {}) {
|
|
395
|
+
const C = colors;
|
|
396
|
+
const lines = [];
|
|
397
|
+
|
|
398
|
+
lines.push(`${C.gold || ''}┌─ Clarification Analytics ──────────────────────────┐${C.reset || ''}`);
|
|
399
|
+
lines.push(`${C.gold || ''}│${C.reset || ''}`);
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
const s = analytics.summary;
|
|
403
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.bold || ''}Summary${C.reset || ''}`);
|
|
404
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} Total Clarifications: ${C.white || ''}${s.totalClarifications}${C.reset || ''}`);
|
|
405
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} Answered: ${C.green || ''}${s.totalAnswered}${C.reset || ''} Skipped: ${C.yellow || ''}${s.totalSkipped}${C.reset || ''} Timeout: ${C.red || ''}${s.totalTimedOut}${C.reset || ''}`);
|
|
406
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} Skip Rate: ${C.dim || ''}${s.overallSkipRate}${C.reset || ''} Avg Rounds: ${C.dim || ''}${s.avgRoundsPerSession}${C.reset || ''}`);
|
|
407
|
+
lines.push(`${C.gold || ''}│${C.reset || ''}`);
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
const topCats = Object.entries(analytics.categories)
|
|
411
|
+
.sort(([,a], [,b]) => b.asked - a.asked)
|
|
412
|
+
.slice(0, 5);
|
|
413
|
+
|
|
414
|
+
if (topCats.length > 0) {
|
|
415
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.bold || ''}Top Categories${C.reset || ''}`);
|
|
416
|
+
for (const [cat, stats] of topCats) {
|
|
417
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}${cat.padEnd(16)}${C.reset || ''} Asked: ${stats.asked} Answered: ${stats.answered} Skip: ${stats.skipRate}`);
|
|
418
|
+
}
|
|
419
|
+
lines.push(`${C.gold || ''}│${C.reset || ''}`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
const p = analytics.period;
|
|
424
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.bold || ''}Last ${p.days} Days${C.reset || ''}`);
|
|
425
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} Asked: ${p.asked} Answered: ${p.answered} Skipped: ${p.skipped}`);
|
|
426
|
+
lines.push(`${C.gold || ''}│${C.reset || ''}`);
|
|
427
|
+
|
|
428
|
+
lines.push(`${C.gold || ''}└────────────────────────────────────────────────────┘${C.reset || ''}`);
|
|
429
|
+
|
|
430
|
+
return lines.join('\n');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function formatConfigForCLI(config, colors = {}) {
|
|
434
|
+
const C = colors;
|
|
435
|
+
const lines = [];
|
|
436
|
+
|
|
437
|
+
lines.push(`${C.gold || ''}┌─ Clarification Settings ───────────────────────────┐${C.reset || ''}`);
|
|
438
|
+
lines.push(`${C.gold || ''}│${C.reset || ''}`);
|
|
439
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.bold || ''}Current Configuration${C.reset || ''}`);
|
|
440
|
+
lines.push(`${C.gold || ''}│${C.reset || ''}`);
|
|
441
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} Enabled: ${config.enabled ? `${C.green || ''}✓ ON${C.reset || ''}` : `${C.red || ''}✗ OFF${C.reset || ''}`}`);
|
|
442
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} Confidence Threshold: ${C.white || ''}${(config.confidenceThreshold * 100).toFixed(0)}%${C.reset || ''} ${C.dim || ''}(ask questions when confidence < this)${C.reset || ''}`);
|
|
443
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} Max Rounds: ${C.white || ''}${config.maxRounds}${C.reset || ''}`);
|
|
444
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} Timeout: ${C.white || ''}${(config.timeoutMs / 1000).toFixed(1)}s${C.reset || ''}`);
|
|
445
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} LLM Questions: ${config.preferLLMQuestions ? `${C.green || ''}✓ ON${C.reset || ''}` : `${C.yellow || ''}✗ OFF${C.reset || ''}`}`);
|
|
446
|
+
lines.push(`${C.gold || ''}│${C.reset || ''}`);
|
|
447
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}Commands:${C.reset || ''}`);
|
|
448
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}/clarify config threshold <0-100>${C.reset || ''}`);
|
|
449
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}/clarify config rounds <1-5>${C.reset || ''}`);
|
|
450
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}/clarify config timeout <5-60>${C.reset || ''}`);
|
|
451
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}/clarify config on|off${C.reset || ''}`);
|
|
452
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}/clarify config llm on|off${C.reset || ''}`);
|
|
453
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}/clarify config reset${C.reset || ''}`);
|
|
454
|
+
lines.push(`${C.gold || ''}│${C.reset || ''} ${C.dim || ''}/clarify analytics${C.reset || ''}`);
|
|
455
|
+
lines.push(`${C.gold || ''}│${C.reset || ''}`);
|
|
456
|
+
|
|
457
|
+
lines.push(`${C.gold || ''}└────────────────────────────────────────────────────┘${C.reset || ''}`);
|
|
458
|
+
|
|
459
|
+
return lines.join('\n');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export function formatAnalyticsForAPI(analytics) {
|
|
463
|
+
return {
|
|
464
|
+
type: 'clarification_analytics',
|
|
465
|
+
data: analytics,
|
|
466
|
+
timestamp: Date.now(),
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export default {
|
|
471
|
+
ClarificationAnalytics,
|
|
472
|
+
ClarificationConfig,
|
|
473
|
+
formatAnalyticsForCLI,
|
|
474
|
+
formatConfigForCLI,
|
|
475
|
+
formatAnalyticsForAPI,
|
|
476
|
+
DEFAULT_CONFIG,
|
|
477
|
+
};
|