kiku-agent 1.0.4 → 1.0.6
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 +25 -2
- package/package.json +12 -3
- package/server/agent/agentCore.js +164 -55
- package/server/agent/cli.js +1300 -695
- package/server/agent/commandRegistry.js +436 -74
- package/server/agent/commands.js +629 -63
- package/server/agent/doctor.js +7 -6
- package/server/agent/mcpClient.js +436 -364
- package/server/agent/queryDetails.js +182 -0
- package/server/agent/sanitizer.js +4 -4
- package/server/agent/subagent.js +1 -0
- package/server/agent/suggestions.js +346 -0
- package/server/agent/theme.js +12 -12
- package/server/agent/tools.js +118 -7
- package/server/agent/ui.js +433 -31
- package/server/agent/updateManager.js +438 -0
- package/server/agent/version.js +10 -0
- package/server/mcp/data/brands.js +9 -4
- package/server/mcp/server.js +2 -1
- package/src/data/brandsData.js +65 -0
package/server/agent/tools.js
CHANGED
|
@@ -34,7 +34,66 @@ export function buildYoukuSearchUrl(query) {
|
|
|
34
34
|
return `https://so.youku.com/search_video/q_${encodeURIComponent(query)}`;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
export class ToolCache {
|
|
38
|
+
constructor(maxSize = 300, defaultTtlMs = 600_000) {
|
|
39
|
+
this.maxSize = maxSize;
|
|
40
|
+
this.defaultTtlMs = defaultTtlMs;
|
|
41
|
+
this.cache = new Map();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get(key) {
|
|
45
|
+
const entry = this.cache.get(key);
|
|
46
|
+
if (!entry) return null;
|
|
47
|
+
if (Date.now() > entry.expiresAt) {
|
|
48
|
+
this.cache.delete(key);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
this.cache.delete(key);
|
|
52
|
+
this.cache.set(key, entry);
|
|
53
|
+
return entry.value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
set(key, value, ttlMs = this.defaultTtlMs) {
|
|
57
|
+
if (this.cache.size >= this.maxSize) {
|
|
58
|
+
const oldestKey = this.cache.keys().next().value;
|
|
59
|
+
if (oldestKey) this.cache.delete(oldestKey);
|
|
60
|
+
}
|
|
61
|
+
this.cache.set(key, {
|
|
62
|
+
value,
|
|
63
|
+
expiresAt: Date.now() + ttlMs,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
clear() {
|
|
68
|
+
this.cache.clear();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const globalToolCache = new ToolCache();
|
|
73
|
+
|
|
74
|
+
const STATIC_CATALOG_TOOLS = new Set([
|
|
75
|
+
'get_profile_and_fandom',
|
|
76
|
+
'get_quotes',
|
|
77
|
+
'get_awards',
|
|
78
|
+
'get_timeline',
|
|
79
|
+
'get_brand_endorsements',
|
|
80
|
+
'get_gallery',
|
|
81
|
+
'get_music_videos',
|
|
82
|
+
'search_works',
|
|
83
|
+
'search_discography',
|
|
84
|
+
'get_work_detail',
|
|
85
|
+
'get_social_updates',
|
|
86
|
+
'get_media_links',
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
const WEB_CACHE_TOOLS = new Set([
|
|
90
|
+
'web_search',
|
|
91
|
+
'web_extract',
|
|
92
|
+
'fetch_webpage',
|
|
93
|
+
'fetch_json',
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
const CACHED_DISCOGRAPHY = (() => {
|
|
38
97
|
const map = new Map();
|
|
39
98
|
const allTracks = [
|
|
40
99
|
...(discographyData.latestReleases || []),
|
|
@@ -65,17 +124,25 @@ function getCleanDiscography() {
|
|
|
65
124
|
}
|
|
66
125
|
}
|
|
67
126
|
|
|
68
|
-
return Array.from(map.values()).sort((a, b) => (b.year || 0) - (a.year || 0));
|
|
127
|
+
return Object.freeze(Array.from(map.values()).sort((a, b) => (b.year || 0) - (a.year || 0)));
|
|
128
|
+
})();
|
|
129
|
+
|
|
130
|
+
function getCleanDiscography() {
|
|
131
|
+
return CACHED_DISCOGRAPHY;
|
|
69
132
|
}
|
|
70
133
|
|
|
71
|
-
|
|
134
|
+
const CACHED_MUSIC_VIDEOS = (() => {
|
|
72
135
|
if (Array.isArray(musicVideoData)) {
|
|
73
|
-
return musicVideoData;
|
|
136
|
+
return Object.freeze(musicVideoData);
|
|
74
137
|
}
|
|
75
|
-
return [
|
|
138
|
+
return Object.freeze([
|
|
76
139
|
...(musicVideoData.officialMvs || []).map(m => ({ ...m, category: 'Official MV' })),
|
|
77
140
|
...(musicVideoData.stagePerformances || []).map(m => ({ ...m, category: 'Stage Performance' }))
|
|
78
|
-
];
|
|
141
|
+
]);
|
|
142
|
+
})();
|
|
143
|
+
|
|
144
|
+
function getAllMusicVideos() {
|
|
145
|
+
return CACHED_MUSIC_VIDEOS;
|
|
79
146
|
}
|
|
80
147
|
|
|
81
148
|
export const toolsSchema = [
|
|
@@ -620,6 +687,20 @@ export const toolsSchema = [
|
|
|
620
687
|
}
|
|
621
688
|
}
|
|
622
689
|
},
|
|
690
|
+
{
|
|
691
|
+
type: "function",
|
|
692
|
+
function: {
|
|
693
|
+
name: "suggest_topics",
|
|
694
|
+
description: "Generate dynamic, intriguing research questions and exploration topics about Ju Jingyi across dramas, music, fashion, milestones, and trivia.",
|
|
695
|
+
parameters: {
|
|
696
|
+
type: "object",
|
|
697
|
+
properties: {
|
|
698
|
+
topic: { type: "string", description: "Optional topic or category focus (e.g. 'drama', 'music', 'fashion', '2026')." },
|
|
699
|
+
limit: { type: "number", description: "Number of suggestions to generate (default: 5)." }
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
},
|
|
623
704
|
{
|
|
624
705
|
type: "function",
|
|
625
706
|
function: {
|
|
@@ -1483,6 +1564,19 @@ export const toolHandlers = {
|
|
|
1483
1564
|
return { status: "success", claim, answer: result.answer, sources: result.sources, reports: result.reports };
|
|
1484
1565
|
},
|
|
1485
1566
|
|
|
1567
|
+
suggest_topics: async ({ topic, limit }, options = {}) => {
|
|
1568
|
+
if (options?.agent?.generateSuggestions) {
|
|
1569
|
+
const suggestions = await options.agent.generateSuggestions({ topic, limit, signal: options.signal });
|
|
1570
|
+
return { status: "success", topic, suggestions };
|
|
1571
|
+
}
|
|
1572
|
+
const { SuggestionEngine } = await import('./suggestions.js');
|
|
1573
|
+
const { LMStudioClient } = await import('./llmClient.js');
|
|
1574
|
+
const client = options?.client || new LMStudioClient();
|
|
1575
|
+
const engine = new SuggestionEngine({ client });
|
|
1576
|
+
const suggestions = await engine.generateSuggestions({ topic, limit, signal: options?.signal });
|
|
1577
|
+
return { status: "success", topic, suggestions };
|
|
1578
|
+
},
|
|
1579
|
+
|
|
1486
1580
|
manage_skill: async ({ action, instruction, skillId }, options = {}) => {
|
|
1487
1581
|
let memory = options?.agent?.memory || options?.memory;
|
|
1488
1582
|
if (!memory) {
|
|
@@ -1628,7 +1722,9 @@ export function normalizeToolResult(toolName, result) {
|
|
|
1628
1722
|
if (normalized.error && !normalized.message) normalized.message = String(normalized.error);
|
|
1629
1723
|
if (normalized.status === 'success' && (normalized.count === 0 || normalized.returnedCount === 0)) {
|
|
1630
1724
|
normalized.status = 'not_found';
|
|
1631
|
-
if (!normalized.message)
|
|
1725
|
+
if (!normalized.message) {
|
|
1726
|
+
normalized.message = 'No matching data found in local catalog. Hint: You can use web_search or web_extract to look up online information.';
|
|
1727
|
+
}
|
|
1632
1728
|
}
|
|
1633
1729
|
return normalized;
|
|
1634
1730
|
}
|
|
@@ -1652,6 +1748,15 @@ export async function executeTool(toolName, args = {}, options = {}) {
|
|
|
1652
1748
|
};
|
|
1653
1749
|
}
|
|
1654
1750
|
|
|
1751
|
+
const isStatic = STATIC_CATALOG_TOOLS.has(toolName);
|
|
1752
|
+
const isWeb = WEB_CACHE_TOOLS.has(toolName);
|
|
1753
|
+
const cacheKey = (isStatic || isWeb) ? `${toolName}:${JSON.stringify(prepared.args)}` : null;
|
|
1754
|
+
|
|
1755
|
+
if (cacheKey && !options.noCache) {
|
|
1756
|
+
const cached = globalToolCache.get(cacheKey);
|
|
1757
|
+
if (cached) return cached;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1655
1760
|
const configuredTimeout = Number(options.timeoutMs ?? process.env.KIKU_TOOL_TIMEOUT_MS ?? 45_000);
|
|
1656
1761
|
const timeoutMs = Number.isFinite(configuredTimeout) && configuredTimeout > 0 ? configuredTimeout : 45_000;
|
|
1657
1762
|
let timeoutId;
|
|
@@ -1669,6 +1774,12 @@ export async function executeTool(toolName, args = {}, options = {}) {
|
|
|
1669
1774
|
if (prepared.warnings.length > 0) {
|
|
1670
1775
|
normalized.validationWarnings = prepared.warnings;
|
|
1671
1776
|
}
|
|
1777
|
+
|
|
1778
|
+
if (cacheKey && normalized.status !== 'error') {
|
|
1779
|
+
const ttl = isStatic ? 3600_000 : 900_000;
|
|
1780
|
+
globalToolCache.set(cacheKey, normalized, ttl);
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1672
1783
|
return normalized;
|
|
1673
1784
|
} catch (err) {
|
|
1674
1785
|
return {
|