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
package/server/agent/session.js
CHANGED
|
@@ -4,18 +4,61 @@ import { EXPORT_DIR, SESSION_DIR as DATA_SESSION_DIR } from './appPaths.js';
|
|
|
4
4
|
|
|
5
5
|
const SESSION_DIR = DATA_SESSION_DIR;
|
|
6
6
|
const SESSION_FILE = path.join(SESSION_DIR, 'current_session.json');
|
|
7
|
+
const SENSITIVE_FIELD = /(?:authorization|api[-_]?key|access[-_]?token|refresh[-_]?token|secret|password|cookie|private[-_]?key)/i;
|
|
8
|
+
|
|
9
|
+
export function redactSensitiveValue(value, depth = 0) {
|
|
10
|
+
if (depth > 12) return '[REDACTED:DEPTH_LIMIT]';
|
|
11
|
+
if (typeof value === 'string') {
|
|
12
|
+
return value
|
|
13
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, 'Bearer [REDACTED]')
|
|
14
|
+
.replace(/\b(?:sk-(?:or-v1-)?|gsk_|AIza)[A-Za-z0-9._-]{12,}/g, '[REDACTED_API_KEY]')
|
|
15
|
+
.replace(/\b(kiku_(?:master|vip|dev)_\S{8,})/gi, '[REDACTED_KIKU_KEY]');
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(value)) return value.map(item => redactSensitiveValue(item, depth + 1));
|
|
18
|
+
if (!value || typeof value !== 'object') return value;
|
|
19
|
+
|
|
20
|
+
const out = {};
|
|
21
|
+
for (const [key, item] of Object.entries(value)) {
|
|
22
|
+
out[key] = SENSITIVE_FIELD.test(key) ? '[REDACTED]' : redactSensitiveValue(item, depth + 1);
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeStorageKey(value) {
|
|
28
|
+
return String(value || '')
|
|
29
|
+
.trim()
|
|
30
|
+
.replace(/[^a-zA-Z0-9_-]+/g, '_')
|
|
31
|
+
.slice(0, 120);
|
|
32
|
+
}
|
|
7
33
|
|
|
8
34
|
export class AgentSession {
|
|
9
35
|
constructor(options = {}) {
|
|
10
|
-
this.
|
|
36
|
+
this.persistenceEnabled = options.persistence !== false;
|
|
37
|
+
this.id = options.sessionId || `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
11
38
|
this.createdAt = new Date();
|
|
12
39
|
this.thinkingMode = options.thinkingMode || 'hide';
|
|
40
|
+
const storageKey = normalizeStorageKey(options.storageKey);
|
|
41
|
+
this.storageFile = options.storageFile
|
|
42
|
+
? path.resolve(String(options.storageFile))
|
|
43
|
+
: (storageKey ? path.join(SESSION_DIR, `${storageKey}.json`) : SESSION_FILE);
|
|
13
44
|
this.messages = [];
|
|
14
45
|
this.stats = {
|
|
15
46
|
totalQueries: 0,
|
|
16
47
|
totalToolCalls: 0,
|
|
17
48
|
totalDurationMs: 0,
|
|
49
|
+
totalPromptTokens: 0,
|
|
50
|
+
totalCompletionTokens: 0,
|
|
51
|
+
totalReasoningTokens: 0,
|
|
52
|
+
totalTokens: 0,
|
|
53
|
+
totalCostUsd: 0,
|
|
54
|
+
};
|
|
55
|
+
this.lastQueryTokens = {
|
|
56
|
+
prompt: 0,
|
|
57
|
+
completion: 0,
|
|
58
|
+
reasoning: 0,
|
|
59
|
+
total: 0
|
|
18
60
|
};
|
|
61
|
+
this.lastQueryCostUsd = 0;
|
|
19
62
|
this._storageWarningShown = false;
|
|
20
63
|
}
|
|
21
64
|
|
|
@@ -57,12 +100,49 @@ export class AgentSession {
|
|
|
57
100
|
model: meta.model || null,
|
|
58
101
|
createdAt: new Date(),
|
|
59
102
|
durationMs: 0,
|
|
103
|
+
tokens: {
|
|
104
|
+
prompt: 0,
|
|
105
|
+
completion: 0,
|
|
106
|
+
reasoning: 0,
|
|
107
|
+
total: 0
|
|
108
|
+
},
|
|
109
|
+
costUsd: 0,
|
|
60
110
|
parts: []
|
|
61
111
|
};
|
|
62
112
|
this.messages.push(msg);
|
|
63
113
|
return msg;
|
|
64
114
|
}
|
|
65
115
|
|
|
116
|
+
recordQueryTokens(tokens = {}, costUsd = 0) {
|
|
117
|
+
const prompt = Number(tokens.prompt || tokens.prompt_tokens || 0);
|
|
118
|
+
const completion = Number(tokens.completion || tokens.completion_tokens || 0);
|
|
119
|
+
const reasoning = Number(tokens.reasoning || tokens.reasoning_tokens || 0);
|
|
120
|
+
const total = Number(tokens.total || tokens.total_tokens || (prompt + completion));
|
|
121
|
+
const cost = Number(costUsd || 0);
|
|
122
|
+
|
|
123
|
+
this.stats.totalPromptTokens += prompt;
|
|
124
|
+
this.stats.totalCompletionTokens += completion;
|
|
125
|
+
this.stats.totalReasoningTokens += reasoning;
|
|
126
|
+
this.stats.totalTokens += total;
|
|
127
|
+
this.stats.totalCostUsd = Number((this.stats.totalCostUsd + cost).toFixed(6));
|
|
128
|
+
|
|
129
|
+
this.lastQueryTokens = {
|
|
130
|
+
prompt,
|
|
131
|
+
completion,
|
|
132
|
+
reasoning,
|
|
133
|
+
total
|
|
134
|
+
};
|
|
135
|
+
this.lastQueryCostUsd = cost;
|
|
136
|
+
|
|
137
|
+
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
138
|
+
if (this.messages[i].role === 'assistant') {
|
|
139
|
+
this.messages[i].tokens = { ...this.lastQueryTokens };
|
|
140
|
+
this.messages[i].costUsd = cost;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
66
146
|
addReasoningPart(message, reasoningText) {
|
|
67
147
|
if (!message || !Array.isArray(message.parts)) return null;
|
|
68
148
|
let part = message.parts.find(p => p.type === 'reasoning');
|
|
@@ -174,16 +254,17 @@ export class AgentSession {
|
|
|
174
254
|
}
|
|
175
255
|
|
|
176
256
|
async saveToDisk() {
|
|
257
|
+
if (!this.persistenceEnabled) return false;
|
|
177
258
|
try {
|
|
178
|
-
await fs.mkdir(SESSION_DIR, { recursive: true });
|
|
259
|
+
await fs.mkdir(SESSION_DIR, { recursive: true, mode: 0o700 });
|
|
179
260
|
const sessionData = {
|
|
180
261
|
id: this.id,
|
|
181
262
|
createdAt: this.createdAt,
|
|
182
263
|
thinkingMode: this.thinkingMode,
|
|
183
|
-
messages: this.messages,
|
|
264
|
+
messages: redactSensitiveValue(this.messages),
|
|
184
265
|
stats: this.stats,
|
|
185
266
|
};
|
|
186
|
-
await fs.writeFile(
|
|
267
|
+
await fs.writeFile(this.storageFile, JSON.stringify(sessionData, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
187
268
|
return true;
|
|
188
269
|
} catch (error) {
|
|
189
270
|
if (!this._storageWarningShown) {
|
|
@@ -217,10 +298,16 @@ export class AgentSession {
|
|
|
217
298
|
this.createdAt = sessionData.createdAt ? new Date(sessionData.createdAt) : new Date();
|
|
218
299
|
this.thinkingMode = sessionData.thinkingMode || 'hide';
|
|
219
300
|
this.messages = Array.isArray(sessionData.messages) ? sessionData.messages : [];
|
|
220
|
-
this.stats =
|
|
301
|
+
this.stats = {
|
|
221
302
|
totalQueries: 0,
|
|
222
303
|
totalToolCalls: 0,
|
|
223
|
-
totalDurationMs: 0
|
|
304
|
+
totalDurationMs: 0,
|
|
305
|
+
totalPromptTokens: 0,
|
|
306
|
+
totalCompletionTokens: 0,
|
|
307
|
+
totalReasoningTokens: 0,
|
|
308
|
+
totalTokens: 0,
|
|
309
|
+
totalCostUsd: 0,
|
|
310
|
+
...(sessionData.stats || {})
|
|
224
311
|
};
|
|
225
312
|
return true;
|
|
226
313
|
}
|
|
@@ -232,23 +319,37 @@ export class AgentSession {
|
|
|
232
319
|
this.stats = {
|
|
233
320
|
totalQueries: 0,
|
|
234
321
|
totalToolCalls: 0,
|
|
235
|
-
totalDurationMs: 0
|
|
322
|
+
totalDurationMs: 0,
|
|
323
|
+
totalPromptTokens: 0,
|
|
324
|
+
totalCompletionTokens: 0,
|
|
325
|
+
totalReasoningTokens: 0,
|
|
326
|
+
totalTokens: 0,
|
|
327
|
+
totalCostUsd: 0
|
|
236
328
|
};
|
|
329
|
+
this.lastQueryTokens = { prompt: 0, completion: 0, reasoning: 0, total: 0 };
|
|
330
|
+
this.lastQueryCostUsd = 0;
|
|
237
331
|
}
|
|
238
332
|
|
|
239
333
|
async loadFromDisk() {
|
|
334
|
+
if (!this.persistenceEnabled) return false;
|
|
240
335
|
try {
|
|
241
|
-
const data = await fs.readFile(
|
|
336
|
+
const data = await fs.readFile(this.storageFile, 'utf-8');
|
|
242
337
|
const sessionData = JSON.parse(data);
|
|
243
338
|
|
|
244
339
|
this.id = sessionData.id;
|
|
245
340
|
this.createdAt = new Date(sessionData.createdAt);
|
|
246
341
|
this.thinkingMode = sessionData.thinkingMode || 'hide';
|
|
247
342
|
this.messages = sessionData.messages || [];
|
|
248
|
-
this.stats =
|
|
343
|
+
this.stats = {
|
|
249
344
|
totalQueries: 0,
|
|
250
345
|
totalToolCalls: 0,
|
|
251
|
-
totalDurationMs: 0
|
|
346
|
+
totalDurationMs: 0,
|
|
347
|
+
totalPromptTokens: 0,
|
|
348
|
+
totalCompletionTokens: 0,
|
|
349
|
+
totalReasoningTokens: 0,
|
|
350
|
+
totalTokens: 0,
|
|
351
|
+
totalCostUsd: 0,
|
|
352
|
+
...(sessionData.stats || {})
|
|
252
353
|
};
|
|
253
354
|
return true;
|
|
254
355
|
} catch {
|
|
@@ -257,8 +358,9 @@ export class AgentSession {
|
|
|
257
358
|
}
|
|
258
359
|
|
|
259
360
|
async clearDisk() {
|
|
361
|
+
if (!this.persistenceEnabled) return false;
|
|
260
362
|
try {
|
|
261
|
-
await fs.unlink(
|
|
363
|
+
await fs.unlink(this.storageFile);
|
|
262
364
|
return true;
|
|
263
365
|
} catch {
|
|
264
366
|
return false;
|
|
@@ -366,6 +468,198 @@ export class AgentSession {
|
|
|
366
468
|
return out;
|
|
367
469
|
}
|
|
368
470
|
|
|
471
|
+
getTranscriptHtml() {
|
|
472
|
+
const now = new Date();
|
|
473
|
+
const createdStr = this.createdAt ? new Date(this.createdAt).toISOString().replace('T', ' ').slice(0, 19) : 'unknown';
|
|
474
|
+
const totalDurationSec = ((this.stats.totalDurationMs || 0) / 1000).toFixed(1);
|
|
475
|
+
const toolStats = this.getToolStats();
|
|
476
|
+
const toolEntries = Object.entries(toolStats);
|
|
477
|
+
|
|
478
|
+
const escapeHtml = str => String(str || '')
|
|
479
|
+
.replace(/&/g, '&')
|
|
480
|
+
.replace(/</g, '<')
|
|
481
|
+
.replace(/>/g, '>')
|
|
482
|
+
.replace(/"/g, '"');
|
|
483
|
+
|
|
484
|
+
let toolTableHtml = '';
|
|
485
|
+
if (toolEntries.length > 0) {
|
|
486
|
+
toolTableHtml = `
|
|
487
|
+
<div class="card" style="margin-bottom: 24px;">
|
|
488
|
+
<h3>[Tools] Tool Invocations Summary</h3>
|
|
489
|
+
<table class="data-table">
|
|
490
|
+
<thead>
|
|
491
|
+
<tr><th>Tool Name</th><th>Invocations</th><th>OK</th><th>Not Found</th><th>Errors</th><th>Total Time</th></tr>
|
|
492
|
+
</thead>
|
|
493
|
+
<tbody>
|
|
494
|
+
${toolEntries.map(([name, s]) => `
|
|
495
|
+
<tr>
|
|
496
|
+
<td><code>${escapeHtml(name)}</code></td>
|
|
497
|
+
<td>${s.count}</td>
|
|
498
|
+
<td><span class="badge ok">${s.successCount}</span></td>
|
|
499
|
+
<td><span class="badge warn">${s.notFoundCount}</span></td>
|
|
500
|
+
<td><span class="badge err">${s.errorCount}</span></td>
|
|
501
|
+
<td>${(s.totalDurationMs / 1000).toFixed(2)}s</td>
|
|
502
|
+
</tr>
|
|
503
|
+
`).join('')}
|
|
504
|
+
</tbody>
|
|
505
|
+
</table>
|
|
506
|
+
</div>`;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
let messagesHtml = '';
|
|
510
|
+
let qIdx = 1;
|
|
511
|
+
for (const msg of this.messages) {
|
|
512
|
+
if (msg.role === 'user') {
|
|
513
|
+
const text = msg.parts?.find(p => p.type === 'text')?.content || '';
|
|
514
|
+
const qNum = msg.queryNumber || qIdx++;
|
|
515
|
+
messagesHtml += `
|
|
516
|
+
<div class="message user-message">
|
|
517
|
+
<div class="message-header">
|
|
518
|
+
<span class="user-badge">Question ${qNum}</span>
|
|
519
|
+
<span class="timestamp">${msg.createdAt ? new Date(msg.createdAt).toLocaleTimeString() : ''}</span>
|
|
520
|
+
</div>
|
|
521
|
+
<div class="message-content user-text">${escapeHtml(text)}</div>
|
|
522
|
+
</div>`;
|
|
523
|
+
} else if (msg.role === 'assistant') {
|
|
524
|
+
let partsContent = '';
|
|
525
|
+
for (const part of msg.parts || []) {
|
|
526
|
+
if (part.type === 'reasoning' && part.content) {
|
|
527
|
+
const dur = part.durationMs ? `${(part.durationMs / 1000).toFixed(1)}s` : 'Completed';
|
|
528
|
+
partsContent += `
|
|
529
|
+
<details class="reasoning-box">
|
|
530
|
+
<summary>[Thinking] Reasoning Trace (${dur})</summary>
|
|
531
|
+
<pre><code>${escapeHtml(part.content.trim())}</code></pre>
|
|
532
|
+
</details>`;
|
|
533
|
+
} else if (part.type === 'tool') {
|
|
534
|
+
const dur = part.durationMs !== undefined ? `${part.durationMs}ms` : '';
|
|
535
|
+
const statusClass = part.status === 'ok' ? 'ok' : part.status === 'not_found' ? 'warn' : 'err';
|
|
536
|
+
partsContent += `
|
|
537
|
+
<details class="tool-box">
|
|
538
|
+
<summary>[Tool] <code>${escapeHtml(part.name)}</code> <span class="badge ${statusClass}">${(part.status || 'ok').toUpperCase()}</span> <span class="dim">(${dur})</span></summary>
|
|
539
|
+
<div class="tool-detail">
|
|
540
|
+
<strong>Arguments:</strong>
|
|
541
|
+
<pre><code>${escapeHtml(JSON.stringify(part.args, null, 2))}</code></pre>
|
|
542
|
+
${part.result ? `<strong>Result:</strong><pre><code>${escapeHtml(JSON.stringify(part.result, null, 2))}</code></pre>` : ''}
|
|
543
|
+
</div>
|
|
544
|
+
</details>`;
|
|
545
|
+
} else if (part.type === 'text' && part.content) {
|
|
546
|
+
const formatted = escapeHtml(part.content)
|
|
547
|
+
.replace(/\n\n/g, '<br><br>')
|
|
548
|
+
.replace(/\n/g, '<br>');
|
|
549
|
+
partsContent += `<div class="assistant-text">${formatted}</div>`;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
messagesHtml += `
|
|
554
|
+
<div class="message assistant-message">
|
|
555
|
+
<div class="message-header">
|
|
556
|
+
<span class="kiku-badge">Kiku AI</span>
|
|
557
|
+
${msg.model ? `<span class="model-tag">${escapeHtml(msg.model)}</span>` : ''}
|
|
558
|
+
</div>
|
|
559
|
+
<div class="message-content">${partsContent}</div>
|
|
560
|
+
</div>`;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return `<!DOCTYPE html>
|
|
565
|
+
<html lang="en">
|
|
566
|
+
<head>
|
|
567
|
+
<meta charset="UTF-8">
|
|
568
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
569
|
+
<title>Kiku AI Agent — Session Transcript (${escapeHtml(this.id)})</title>
|
|
570
|
+
<style>
|
|
571
|
+
:root {
|
|
572
|
+
--bg: #090d16;
|
|
573
|
+
--card-bg: rgba(15, 23, 42, 0.85);
|
|
574
|
+
--card-border: rgba(255, 183, 197, 0.2);
|
|
575
|
+
--primary: #ff7eb3;
|
|
576
|
+
--secondary: #ff758c;
|
|
577
|
+
--accent: #38bdf8;
|
|
578
|
+
--text: #f8fafc;
|
|
579
|
+
--text-dim: #94a3b8;
|
|
580
|
+
--ok: #10b981;
|
|
581
|
+
--warn: #f59e0b;
|
|
582
|
+
--err: #ef4444;
|
|
583
|
+
}
|
|
584
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
585
|
+
body {
|
|
586
|
+
background: var(--bg);
|
|
587
|
+
color: var(--text);
|
|
588
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
589
|
+
line-height: 1.6;
|
|
590
|
+
padding: 32px 16px;
|
|
591
|
+
}
|
|
592
|
+
.container { max-width: 900px; margin: 0 auto; }
|
|
593
|
+
.header {
|
|
594
|
+
background: linear-gradient(135deg, rgba(255, 126, 179, 0.15), rgba(56, 189, 248, 0.1));
|
|
595
|
+
border: 1px solid var(--card-border);
|
|
596
|
+
border-radius: 16px;
|
|
597
|
+
padding: 28px;
|
|
598
|
+
margin-bottom: 24px;
|
|
599
|
+
backdrop-filter: blur(12px);
|
|
600
|
+
}
|
|
601
|
+
.title { font-size: 24px; font-weight: 700; color: var(--primary); margin-bottom: 12px; }
|
|
602
|
+
.meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
|
|
603
|
+
.meta-item { background: rgba(0, 0, 0, 0.3); padding: 10px 14px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.05); }
|
|
604
|
+
.meta-label { font-size: 11px; text-transform: uppercase; color: var(--text-dim); }
|
|
605
|
+
.meta-value { font-size: 14px; font-weight: 600; color: var(--text); }
|
|
606
|
+
.card {
|
|
607
|
+
background: var(--card-bg);
|
|
608
|
+
border: 1px solid var(--card-border);
|
|
609
|
+
border-radius: 12px;
|
|
610
|
+
padding: 20px;
|
|
611
|
+
margin-bottom: 20px;
|
|
612
|
+
}
|
|
613
|
+
.card h3 { color: var(--accent); font-size: 16px; margin-bottom: 12px; }
|
|
614
|
+
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
615
|
+
.data-table th, .data-table td { padding: 8px 12px; text-align: left; border-bottom: 1px solid rgba(255,255,255,0.06); }
|
|
616
|
+
.data-table th { color: var(--text-dim); font-weight: 600; }
|
|
617
|
+
.badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 700; }
|
|
618
|
+
.badge.ok { background: rgba(16, 185, 129, 0.2); color: var(--ok); }
|
|
619
|
+
.badge.warn { background: rgba(245, 158, 11, 0.2); color: var(--warn); }
|
|
620
|
+
.badge.err { background: rgba(239, 68, 68, 0.2); color: var(--err); }
|
|
621
|
+
.message { margin-bottom: 24px; border-radius: 12px; border: 1px solid var(--card-border); overflow: hidden; }
|
|
622
|
+
.user-message { background: rgba(30, 41, 59, 0.6); }
|
|
623
|
+
.assistant-message { background: var(--card-bg); }
|
|
624
|
+
.message-header {
|
|
625
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
626
|
+
padding: 12px 16px; background: rgba(0, 0, 0, 0.2);
|
|
627
|
+
border-bottom: 1px solid rgba(255,255,255,0.05); font-size: 13px;
|
|
628
|
+
}
|
|
629
|
+
.user-badge { color: var(--accent); font-weight: 700; }
|
|
630
|
+
.kiku-badge { color: var(--primary); font-weight: 700; }
|
|
631
|
+
.model-tag { color: var(--text-dim); font-size: 11px; background: rgba(255,255,255,0.05); padding: 2px 6px; border-radius: 4px; }
|
|
632
|
+
.timestamp { color: var(--text-dim); font-size: 11px; }
|
|
633
|
+
.message-content { padding: 16px; font-size: 14px; }
|
|
634
|
+
.user-text { font-size: 15px; font-weight: 500; }
|
|
635
|
+
.assistant-text { margin-top: 8px; }
|
|
636
|
+
details { margin: 10px 0; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; overflow: hidden; }
|
|
637
|
+
summary { padding: 8px 12px; background: rgba(0,0,0,0.3); cursor: pointer; font-size: 12px; font-weight: 600; }
|
|
638
|
+
pre { padding: 12px; background: rgba(0,0,0,0.4); font-size: 12px; overflow-x: auto; color: #e2e8f0; }
|
|
639
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
|
|
640
|
+
.tool-detail { padding: 12px; font-size: 12px; }
|
|
641
|
+
.dim { color: var(--text-dim); }
|
|
642
|
+
</style>
|
|
643
|
+
</head>
|
|
644
|
+
<body>
|
|
645
|
+
<div class="container">
|
|
646
|
+
<div class="header">
|
|
647
|
+
<div class="title">Kiku AI Agent — Session Transcript</div>
|
|
648
|
+
<div class="meta-grid">
|
|
649
|
+
<div class="meta-item"><div class="meta-label">Session ID</div><div class="meta-value">${escapeHtml(this.id)}</div></div>
|
|
650
|
+
<div class="meta-item"><div class="meta-label">Total Queries</div><div class="meta-value">${this.stats.totalQueries}</div></div>
|
|
651
|
+
<div class="meta-item"><div class="meta-label">Tool Calls</div><div class="meta-value">${this.stats.totalToolCalls}</div></div>
|
|
652
|
+
<div class="meta-item"><div class="meta-label">Total Duration</div><div class="meta-value">${totalDurationSec}s</div></div>
|
|
653
|
+
<div class="meta-item"><div class="meta-label">Exported At</div><div class="meta-value">${now.toISOString().slice(0, 19)} UTC</div></div>
|
|
654
|
+
</div>
|
|
655
|
+
</div>
|
|
656
|
+
${toolTableHtml}
|
|
657
|
+
${messagesHtml}
|
|
658
|
+
</div>
|
|
659
|
+
</body>
|
|
660
|
+
</html>`;
|
|
661
|
+
}
|
|
662
|
+
|
|
369
663
|
async exportToMarkdown(options = {}) {
|
|
370
664
|
const exportDir = EXPORT_DIR;
|
|
371
665
|
await fs.mkdir(exportDir, { recursive: true }).catch(() => {});
|
|
@@ -380,6 +674,20 @@ export class AgentSession {
|
|
|
380
674
|
return { filePath, filename, format: 'markdown', size: md.length };
|
|
381
675
|
}
|
|
382
676
|
|
|
677
|
+
async exportToHtml() {
|
|
678
|
+
const exportDir = EXPORT_DIR;
|
|
679
|
+
await fs.mkdir(exportDir, { recursive: true }).catch(() => {});
|
|
680
|
+
|
|
681
|
+
const now = new Date();
|
|
682
|
+
const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
683
|
+
const filename = `kiku-session-${timestamp}.html`;
|
|
684
|
+
const filePath = path.join(exportDir, filename);
|
|
685
|
+
|
|
686
|
+
const html = this.getTranscriptHtml();
|
|
687
|
+
await fs.writeFile(filePath, html, 'utf-8');
|
|
688
|
+
return { filePath, filename, format: 'html', size: html.length };
|
|
689
|
+
}
|
|
690
|
+
|
|
383
691
|
async exportToJson() {
|
|
384
692
|
const exportDir = EXPORT_DIR;
|
|
385
693
|
await fs.mkdir(exportDir, { recursive: true }).catch(() => {});
|
package/server/agent/subagent.js
CHANGED
|
@@ -68,7 +68,8 @@ ${options.skillContext ? `\nUser response skill:\n${options.skillContext}` : ''}
|
|
|
68
68
|
|
|
69
69
|
const calls = (response.tool_calls || []).map(normalizeToolCall);
|
|
70
70
|
if (calls.length === 0) {
|
|
71
|
-
|
|
71
|
+
const text = response.content || response.reasoning_content || '';
|
|
72
|
+
return { task: objective, answer: text, toolCalls, iterations: iteration + 1 };
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
for (const call of calls) {
|
|
@@ -104,7 +105,7 @@ ${options.skillContext ? `\nUser response skill:\n${options.skillContext}` : ''}
|
|
|
104
105
|
|
|
105
106
|
messages.push({
|
|
106
107
|
role: 'user',
|
|
107
|
-
content: '
|
|
108
|
+
content: 'Synthesize your research findings and return the concise evidence report now in clear markdown. State key facts directly.',
|
|
108
109
|
});
|
|
109
110
|
const final = await this.client.chat({
|
|
110
111
|
messages,
|
|
@@ -112,6 +113,10 @@ ${options.skillContext ? `\nUser response skill:\n${options.skillContext}` : ''}
|
|
|
112
113
|
stream: false,
|
|
113
114
|
signal: options.signal,
|
|
114
115
|
});
|
|
115
|
-
|
|
116
|
+
let finalAnswer = final?.content || final?.reasoning_content || '';
|
|
117
|
+
if (!finalAnswer.trim() && toolCalls.length > 0) {
|
|
118
|
+
finalAnswer = `Verified primary evidence from ${toolCalls.length} catalog/web tools: ` + toolCalls.map(t => t.name).join(', ');
|
|
119
|
+
}
|
|
120
|
+
return { task: objective, answer: finalAnswer, toolCalls, iterations: this.maxIterations };
|
|
116
121
|
}
|
|
117
122
|
}
|