kiku-agent 1.3.0 → 1.3.2
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 +7 -7
- package/package.json +4 -7
- package/server/agent/agentCore.js +2 -2
- package/server/agent/catalogEntityResolver.js +8 -0
- package/server/agent/cli.js +4 -0
- package/server/agent/eval/AGENTS.md +228 -0
- package/server/agent/eval/analyze.py +684 -0
- package/server/agent/eval/gates.json +15 -0
- package/server/agent/eval/modules/analysis/compare.js +198 -0
- package/server/agent/eval/modules/analysis/gates.js +72 -0
- package/server/agent/eval/modules/analysis/judge.js +70 -0
- package/server/agent/eval/modules/core/cli.js +453 -0
- package/server/agent/eval/modules/core/config.js +60 -0
- package/server/agent/eval/modules/core/store.js +236 -0
- package/server/agent/eval/modules/evaluation/cases.js +689 -0
- package/server/agent/eval/modules/evaluation/runner.js +298 -0
- package/server/agent/eval/modules/evaluation/scoring.js +351 -0
- package/server/agent/eval/modules/evaluation/worker.js +122 -0
- package/server/agent/eval/modules/reporting/benchmark.js +265 -0
- package/server/agent/eval/modules/reporting/benchmarkPng.js +487 -0
- package/server/agent/eval/modules/reporting/dashboard.js +1406 -0
- package/server/agent/eval/modules/server/evalApi.js +1099 -0
- package/server/agent/eval/modules/ui/evalUi.js +4796 -0
- package/server/agent/eval/modules/utils/smokes.js +119 -0
- package/server/agent/ink/App.jsx +348 -0
- package/server/agent/ink/Panel.jsx +54 -0
- package/server/agent/ink/PromptInput.jsx +321 -0
- package/server/agent/ink/TaskPlanPanel.jsx +88 -0
- package/server/agent/ink/ThinkingPanel.jsx +68 -0
- package/server/agent/ink/ToolResult.jsx +133 -0
- package/server/agent/ink/main.jsx +195 -0
- package/server/agent/ink/useEventBus.js +218 -0
- package/server/agent/memory/contextAllocator.js +214 -0
- package/server/agent/memory/hierarchicalStore.js +523 -0
- package/server/agent/memory/reflectionWorker.js +110 -0
- package/server/agent/prompts.js +1 -1
- package/server/agent/telemetry/hardwareTiers.js +332 -0
- package/server/agent/telemetry/runtimeWatchdog.js +66 -0
- package/server/agent/telemetry/techPowerUpClient.js +682 -0
- package/server/agent/telemetry/techpowerup-mcp.json +22 -0
- package/server/agent/tests/agentBubbleSystem.test.js +65 -0
- package/server/agent/tests/agentFollowUps.test.js +127 -0
- package/server/agent/tests/apiRouter.test.js +108 -0
- package/server/agent/tests/architectureFixes.test.js +81 -0
- package/server/agent/tests/architectureRefactoring.test.js +194 -0
- package/server/agent/tests/autoResearch.test.js +64 -0
- package/server/agent/tests/bilibiliEmbed.test.js +251 -0
- package/server/agent/tests/catalogSync.test.js +130 -0
- package/server/agent/tests/citations.test.js +180 -0
- package/server/agent/tests/clarificationAnalytics.test.js +219 -0
- package/server/agent/tests/clarifyingQuestions.test.js +594 -0
- package/server/agent/tests/cliAutomation.test.js +121 -0
- package/server/agent/tests/cloudProviders.test.js +234 -0
- package/server/agent/tests/commandInput.test.js +24 -0
- package/server/agent/tests/commands.test.js +417 -0
- package/server/agent/tests/contextIntelligence.test.js +162 -0
- package/server/agent/tests/conversationSafety.test.js +53 -0
- package/server/agent/tests/decisionAndSearchEnhancements.test.js +135 -0
- package/server/agent/tests/dramaAnswerQuality.test.js +73 -0
- package/server/agent/tests/dynamicCatalogIngestion.test.js +285 -0
- package/server/agent/tests/dynamicRoutingAndSearch.test.js +92 -0
- package/server/agent/tests/edgeCasesScenarios.test.js +49 -0
- package/server/agent/tests/entertainmentMetrics.test.js +126 -0
- package/server/agent/tests/envBootstrap.test.js +87 -0
- package/server/agent/tests/envCommand.test.js +215 -0
- package/server/agent/tests/errorHandling.test.js +80 -0
- package/server/agent/tests/errors.test.js +13 -0
- package/server/agent/tests/evalAnalyze.test.js +54 -0
- package/server/agent/tests/evalCommand.test.js +54 -0
- package/server/agent/tests/evalCorpus.test.js +126 -0
- package/server/agent/tests/evalHarness.test.js +62 -0
- package/server/agent/tests/evalJudgeContext.test.js +38 -0
- package/server/agent/tests/evalMetrics.test.js +312 -0
- package/server/agent/tests/evalStore.test.js +190 -0
- package/server/agent/tests/evalStudio.test.js +179 -0
- package/server/agent/tests/feedbackLearning.test.js +186 -0
- package/server/agent/tests/firstRun.test.js +44 -0
- package/server/agent/tests/gemma4eOptimization.test.js +95 -0
- package/server/agent/tests/hardwareTiers.test.js +206 -0
- package/server/agent/tests/hybridWebSearch.test.js +192 -0
- package/server/agent/tests/judgeService.test.js +251 -0
- package/server/agent/tests/lanDiscovery.test.js +319 -0
- package/server/agent/tests/mcpClient.test.js +201 -0
- package/server/agent/tests/modelManager.test.js +109 -0
- package/server/agent/tests/multilingualScenarios.test.js +69 -0
- package/server/agent/tests/newCommands.test.js +347 -0
- package/server/agent/tests/newCommandsTui.test.js +141 -0
- package/server/agent/tests/newTools.test.js +113 -0
- package/server/agent/tests/newsIntelligence.test.js +178 -0
- package/server/agent/tests/nextGenArchitecture.test.js +99 -0
- package/server/agent/tests/offlineInput.test.js +217 -0
- package/server/agent/tests/packagingGuard.test.js +57 -0
- package/server/agent/tests/presentation.test.js +61 -0
- package/server/agent/tests/providerPersistence.test.js +39 -0
- package/server/agent/tests/queryBenchmarks.test.js +83 -0
- package/server/agent/tests/queryDetails.test.js +126 -0
- package/server/agent/tests/realWorldValidation.test.js +391 -0
- package/server/agent/tests/resilienceAndConcurrency.test.js +117 -0
- package/server/agent/tests/runtimeWatchdog.test.js +32 -0
- package/server/agent/tests/sanitizer.test.js +37 -0
- package/server/agent/tests/scenariosData.js +378 -0
- package/server/agent/tests/schemaRouting.test.js +54 -0
- package/server/agent/tests/securityAuditRemediation.test.js +214 -0
- package/server/agent/tests/securityHardening.test.js +146 -0
- package/server/agent/tests/securitySanitizer.test.js +38 -0
- package/server/agent/tests/sessionAndCopy.test.js +232 -0
- package/server/agent/tests/sidebarControls.test.js +101 -0
- package/server/agent/tests/statefulMemory.test.js +270 -0
- package/server/agent/tests/subagent.test.js +216 -0
- package/server/agent/tests/subagentProgress.test.js +109 -0
- package/server/agent/tests/subagentTextFallback.test.js +115 -0
- package/server/agent/tests/suggestions.test.js +199 -0
- package/server/agent/tests/suggestionsTui.test.js +251 -0
- package/server/agent/tests/telemetryAndProfiles.test.js +179 -0
- package/server/agent/tests/terminalImage.test.js +80 -0
- package/server/agent/tests/terminalMarkdown.test.js +166 -0
- package/server/agent/tests/terminalText.test.js +133 -0
- package/server/agent/tests/textProtocolLoop.test.js +126 -0
- package/server/agent/tests/textToolParsing.test.js +139 -0
- package/server/agent/tests/theme.test.js +26 -0
- package/server/agent/tests/toolRegistryParity.test.js +53 -0
- package/server/agent/tests/toolRoutingRegressions.test.js +119 -0
- package/server/agent/tests/tools.test.js +259 -0
- package/server/agent/tests/unifiedEndpoints.test.js +152 -0
- package/server/agent/tests/v110Features.test.js +262 -0
- package/server/agent/tests/versionBump.test.js +69 -0
- package/server/agent/tests/vramEstimator.test.js +520 -0
- package/server/agent/tests/webChatInspection.test.js +300 -0
- package/server/agent/tests/webChatMemory.test.js +112 -0
- package/server/agent/tests/webChatSecurity.test.js +169 -0
- package/server/agent/tests/webExtract.test.js +261 -0
- package/server/agent/tests/webSources.test.js +247 -0
- package/server/agent/websearch/adapters/duckduckgo.js +97 -0
- package/server/agent/websearch/adapters/jina.js +77 -0
- package/server/agent/websearch/adapters/searxng.js +57 -0
- package/server/agent/websearch/core/canonical.js +89 -0
- package/server/agent/websearch/core/fusion.js +81 -0
- package/server/agent/websearch/core/types.js +29 -0
- package/server/agent/websearch/extract.js +605 -0
- package/server/agent/websearch/index.js +43 -0
- package/server/agent/websearch/orchestrator.js +266 -0
- package/server/agent/websearch/resilience/breaker.js +97 -0
- package/server/agent/websearch/resilience/cache.js +59 -0
- package/server/agent/websearch/resilience/limiter.js +93 -0
- package/server/agent/websearch/security/filter.js +36 -0
- package/server/agent/websearch/security/ssrf.js +289 -0
- package/server/agent/websearch/telemetry.js +71 -0
- package/server/apiRouter.js +7 -11
- package/server/eval/judgeRubrics.js +110 -0
- package/server/eval/judgeService.js +290 -0
- package/server/eval/runBenchmark.js +182 -0
- package/server/mcp/examples/librechat.example.yaml +16 -0
- package/server/mcp/tests/connection.test.js +156 -0
- package/server/routes/agent.js +55 -0
- package/server/supabase_schema.sql +63 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_LOCAL_ENDPOINTS,
|
|
5
|
+
getProviderBaseUrl,
|
|
6
|
+
savePersistedProviderBaseUrl,
|
|
7
|
+
setActiveProvider,
|
|
8
|
+
getUnifiedProviderCatalog,
|
|
9
|
+
config
|
|
10
|
+
} from '../config.js';
|
|
11
|
+
import { LLMClient } from '../llmClient.js';
|
|
12
|
+
import { routeCommand } from '../commands.js';
|
|
13
|
+
import { handleProviderGetRoute, handleProviderUpdateRoute } from '../../web-chat/routes/providerRoute.js';
|
|
14
|
+
|
|
15
|
+
test('DEFAULT_LOCAL_ENDPOINTS exposes canonical local endpoints', () => {
|
|
16
|
+
assert.equal(DEFAULT_LOCAL_ENDPOINTS.lmstudio, 'http://127.0.0.1:1234/v1');
|
|
17
|
+
assert.equal(DEFAULT_LOCAL_ENDPOINTS.vllm, 'http://127.0.0.1:8000/v1');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('getProviderBaseUrl resolves local engines and custom endpoints correctly', () => {
|
|
21
|
+
const lmUrl = getProviderBaseUrl('lmstudio');
|
|
22
|
+
assert.ok(lmUrl.includes(':1234') || lmUrl.startsWith('http://127.0.0.1:1234'));
|
|
23
|
+
|
|
24
|
+
const vllmUrl = getProviderBaseUrl('vllm');
|
|
25
|
+
assert.ok(vllmUrl.includes(':8000') || vllmUrl.startsWith('http://127.0.0.1:8000'));
|
|
26
|
+
|
|
27
|
+
const customTestUrl = 'http://127.0.0.1:8888/v1';
|
|
28
|
+
savePersistedProviderBaseUrl('vllm', customTestUrl);
|
|
29
|
+
assert.equal(getProviderBaseUrl('vllm'), customTestUrl);
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
savePersistedProviderBaseUrl('vllm', DEFAULT_LOCAL_ENDPOINTS.vllm);
|
|
33
|
+
assert.equal(getProviderBaseUrl('vllm'), DEFAULT_LOCAL_ENDPOINTS.vllm);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('setActiveProvider updates active provider and in-memory configuration', () => {
|
|
37
|
+
const infoVllm = setActiveProvider('vllm');
|
|
38
|
+
assert.equal(infoVllm.provider, 'vllm');
|
|
39
|
+
assert.equal(config.activeProvider, 'vllm');
|
|
40
|
+
assert.equal(config.lmStudio.baseUrl, getProviderBaseUrl('vllm'));
|
|
41
|
+
|
|
42
|
+
const catalog = getUnifiedProviderCatalog();
|
|
43
|
+
assert.equal(catalog.activeProvider, 'vllm');
|
|
44
|
+
assert.equal(catalog.localEndpoints.vllm.isActive, true);
|
|
45
|
+
assert.equal(catalog.localEndpoints.lmstudio.isActive, false);
|
|
46
|
+
|
|
47
|
+
const infoLm = setActiveProvider('lmstudio');
|
|
48
|
+
assert.equal(infoLm.provider, 'lmstudio');
|
|
49
|
+
assert.equal(config.activeProvider, 'lmstudio');
|
|
50
|
+
assert.equal(config.lmStudio.baseUrl, getProviderBaseUrl('lmstudio'));
|
|
51
|
+
|
|
52
|
+
const catalogLm = getUnifiedProviderCatalog();
|
|
53
|
+
assert.equal(catalogLm.activeProvider, 'lmstudio');
|
|
54
|
+
assert.equal(catalogLm.localEndpoints.lmstudio.isActive, true);
|
|
55
|
+
assert.equal(catalogLm.localEndpoints.vllm.isActive, false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('LLMClient respects unified baseUrl for vLLM and LM Studio', () => {
|
|
59
|
+
const vllmClient = new LLMClient({ provider: 'vllm' });
|
|
60
|
+
assert.equal(vllmClient.provider, 'vllm');
|
|
61
|
+
assert.equal(vllmClient.baseUrl, getProviderBaseUrl('vllm'));
|
|
62
|
+
assert.equal(vllmClient._buildHeaders()['Authorization'], 'Bearer token-kiku');
|
|
63
|
+
|
|
64
|
+
vllmClient.setBaseUrl('http://127.0.0.1:8008/v1');
|
|
65
|
+
assert.equal(vllmClient.baseUrl, 'http://127.0.0.1:8008/v1');
|
|
66
|
+
assert.equal(getProviderBaseUrl('vllm'), 'http://127.0.0.1:8008/v1');
|
|
67
|
+
|
|
68
|
+
vllmClient.setBaseUrl(DEFAULT_LOCAL_ENDPOINTS.vllm);
|
|
69
|
+
assert.equal(vllmClient.baseUrl, DEFAULT_LOCAL_ENDPOINTS.vllm);
|
|
70
|
+
|
|
71
|
+
const lmClient = new LLMClient({ provider: 'lmstudio' });
|
|
72
|
+
assert.equal(lmClient.provider, 'lmstudio');
|
|
73
|
+
assert.equal(lmClient.baseUrl, getProviderBaseUrl('lmstudio'));
|
|
74
|
+
assert.equal(lmClient._buildHeaders()['Authorization'], 'Bearer lm-studio');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('CLI routeCommand handles /provider url, /url, and /endpoint', async () => {
|
|
78
|
+
const fakeContext = {
|
|
79
|
+
client: new LLMClient({ provider: 'vllm' }),
|
|
80
|
+
agent: null,
|
|
81
|
+
parts: ['/provider', 'url'],
|
|
82
|
+
sub: 'url',
|
|
83
|
+
extra: '',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const res1 = await routeCommand('/provider url', fakeContext);
|
|
87
|
+
assert.equal(res1, 'HANDLED');
|
|
88
|
+
|
|
89
|
+
const res2 = await routeCommand('/url', fakeContext);
|
|
90
|
+
assert.equal(res2, 'HANDLED');
|
|
91
|
+
|
|
92
|
+
const res3 = await routeCommand('/endpoint', fakeContext);
|
|
93
|
+
assert.equal(res3, 'HANDLED');
|
|
94
|
+
|
|
95
|
+
const res4 = await routeCommand('/provider url vllm http://127.0.0.1:8000/v1', {
|
|
96
|
+
...fakeContext,
|
|
97
|
+
parts: ['/provider', 'url', 'vllm', 'http://127.0.0.1:8000/v1'],
|
|
98
|
+
sub: 'url'
|
|
99
|
+
});
|
|
100
|
+
assert.equal(res4, 'HANDLED');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
import { Readable } from 'node:stream';
|
|
104
|
+
|
|
105
|
+
test('Web Chat /api/provider GET and POST routes work as unified manager', async () => {
|
|
106
|
+
let resStatus = null;
|
|
107
|
+
let resPayload = null;
|
|
108
|
+
|
|
109
|
+
const mockRes = {
|
|
110
|
+
writeHead(status, headers) {
|
|
111
|
+
resStatus = status;
|
|
112
|
+
},
|
|
113
|
+
end(content) {
|
|
114
|
+
if (content) resPayload = JSON.parse(content);
|
|
115
|
+
},
|
|
116
|
+
setHeader() {}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
await handleProviderGetRoute({}, mockRes);
|
|
120
|
+
assert.equal(resStatus, 200);
|
|
121
|
+
assert.ok(resPayload);
|
|
122
|
+
assert.equal(resPayload.status, 'ok');
|
|
123
|
+
assert.ok(resPayload.localEndpoints.lmstudio);
|
|
124
|
+
assert.ok(resPayload.localEndpoints.vllm);
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
const updateReq = Readable.from([
|
|
128
|
+
Buffer.from(JSON.stringify({
|
|
129
|
+
provider: 'vllm',
|
|
130
|
+
baseUrl: 'http://127.0.0.1:8000/v1'
|
|
131
|
+
}))
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
let postStatus = null;
|
|
135
|
+
let postPayload = null;
|
|
136
|
+
const mockPostRes = {
|
|
137
|
+
writeHead(status, headers) {
|
|
138
|
+
postStatus = status;
|
|
139
|
+
},
|
|
140
|
+
end(content) {
|
|
141
|
+
if (content) postPayload = JSON.parse(content);
|
|
142
|
+
},
|
|
143
|
+
setHeader() {}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
await handleProviderUpdateRoute(updateReq, mockPostRes);
|
|
147
|
+
assert.equal(postStatus, 200);
|
|
148
|
+
assert.equal(postPayload.activeProvider, 'vllm');
|
|
149
|
+
assert.equal(postPayload.activeBaseUrl, 'http://127.0.0.1:8000/v1');
|
|
150
|
+
|
|
151
|
+
setActiveProvider('lmstudio');
|
|
152
|
+
});
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { DebateOrchestrator } from '../debate.js';
|
|
4
|
+
import { AgentMemory } from '../memory.js';
|
|
5
|
+
import { AgentSession } from '../session.js';
|
|
6
|
+
import { promptDefinitions, getPrompt } from '../../mcp/prompts.js';
|
|
7
|
+
import { routeCommand } from '../commands.js';
|
|
8
|
+
import { formatCareerTimelineGraphic, formatDebateMatrix } from '../ui.js';
|
|
9
|
+
import { timelineData } from '../../../src/data/timelineData.js';
|
|
10
|
+
|
|
11
|
+
test('DebateOrchestrator generates structured adversarial truth matrix', async () => {
|
|
12
|
+
const mockClient = {
|
|
13
|
+
chat: async ({ messages }) => {
|
|
14
|
+
const userText = messages[messages.length - 1]?.content || '';
|
|
15
|
+
if (userText.includes('ADVOCATE')) {
|
|
16
|
+
return { message: { content: 'Verified in SNH48 Senbatsu archives and official press.' } };
|
|
17
|
+
}
|
|
18
|
+
if (userText.includes('CRITIC')) {
|
|
19
|
+
return { message: { content: 'Debunked common misconceptions regarding timeline dates.' } };
|
|
20
|
+
}
|
|
21
|
+
return { message: { content: 'NEUTRAL VERDICT: Claim verified with primary catalog records.' } };
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const orchestrator = new DebateOrchestrator({
|
|
26
|
+
client: mockClient,
|
|
27
|
+
executeTool: async () => ({ status: 'ok' })
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const result = await orchestrator.run('Did Ju Jingyi win SNH48 General Elections?');
|
|
31
|
+
assert.ok(result.topic);
|
|
32
|
+
assert.ok(result.advocate);
|
|
33
|
+
assert.ok(result.critic);
|
|
34
|
+
assert.ok(result.verdict);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('Smart Memory 2.0 calculates relevance with TF-IDF keyword scoring and recency decay', async () => {
|
|
38
|
+
const memory = new AgentMemory();
|
|
39
|
+
const fact1 = {
|
|
40
|
+
id: 'f1',
|
|
41
|
+
content: 'Ju Jingyi starred as Yang Caiwei in In Blossom (2024)',
|
|
42
|
+
category: 'drama',
|
|
43
|
+
tags: ['in-blossom', 'yang-caiwei'],
|
|
44
|
+
confidence: 'high',
|
|
45
|
+
createdAt: new Date(),
|
|
46
|
+
accessCount: 2,
|
|
47
|
+
};
|
|
48
|
+
const fact2 = {
|
|
49
|
+
id: 'f2',
|
|
50
|
+
content: 'Ju Jingyi released Everyday solo single in Spain',
|
|
51
|
+
category: 'music',
|
|
52
|
+
tags: ['everyday', 'single'],
|
|
53
|
+
confidence: 'high',
|
|
54
|
+
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 10),
|
|
55
|
+
accessCount: 0,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const scoreDrama = memory.scoreFactRelevance(fact1, ['caiwei', 'blossom']);
|
|
59
|
+
const scoreMusic = memory.scoreFactRelevance(fact2, ['caiwei', 'blossom']);
|
|
60
|
+
|
|
61
|
+
assert.ok(scoreDrama > scoreMusic, 'Drama fact should score significantly higher for drama keywords');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('AgentSession exports beautiful single-file HTML transcript', async () => {
|
|
65
|
+
const session = new AgentSession();
|
|
66
|
+
session.createUserMessage('Tell me about Ju Jingyi.');
|
|
67
|
+
session.messages.push({
|
|
68
|
+
id: 'msg_a1',
|
|
69
|
+
role: 'assistant',
|
|
70
|
+
parts: [
|
|
71
|
+
{ type: 'reasoning', content: 'Searching catalog...', durationMs: 120 },
|
|
72
|
+
{ type: 'tool', name: 'search_works', args: { query: 'In Blossom' }, result: { count: 1 }, status: 'ok', durationMs: 45 },
|
|
73
|
+
{ type: 'text', content: 'Ju Jingyi is a celebrated Chinese singer and actress.' }
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const html = session.getTranscriptHtml();
|
|
78
|
+
assert.ok(html.includes('<!DOCTYPE html>'));
|
|
79
|
+
assert.ok(html.includes('Kiku AI Agent — Session Transcript'));
|
|
80
|
+
assert.ok(html.includes('search_works'));
|
|
81
|
+
assert.ok(html.includes('Tell me about Ju Jingyi.'));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('MCP prompts expose draft_fan_fiction, analyze_drama_rating, and compare_discography_era', async () => {
|
|
85
|
+
const promptNames = promptDefinitions.map(p => p.name);
|
|
86
|
+
assert.ok(promptNames.includes('draft_fan_fiction'));
|
|
87
|
+
assert.ok(promptNames.includes('analyze_drama_rating'));
|
|
88
|
+
assert.ok(promptNames.includes('compare_discography_era'));
|
|
89
|
+
|
|
90
|
+
const fiction = await getPrompt('draft_fan_fiction', { character: 'Yang Caiwei' });
|
|
91
|
+
assert.ok(fiction.messages[0].content.text.includes('Yang Caiwei'));
|
|
92
|
+
|
|
93
|
+
const dramaAnalysis = await getPrompt('analyze_drama_rating', { title: 'In Blossom' });
|
|
94
|
+
assert.ok(dramaAnalysis.messages[0].content.text.includes('Drama Critical Analysis'));
|
|
95
|
+
|
|
96
|
+
const compareEra = await getPrompt('compare_discography_era', {});
|
|
97
|
+
assert.ok(compareEra.messages[0].content.text.includes('Discography Era Comparison'));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('formatCareerTimelineGraphic renders clean ASCII branch timeline', () => {
|
|
101
|
+
const lines = formatCareerTimelineGraphic(timelineData.slice(0, 3), 80);
|
|
102
|
+
assert.ok(lines.length > 0);
|
|
103
|
+
assert.ok(lines[0].includes('CAREER MILESTONES TIMELINE'));
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('formatDebateMatrix renders structured truth matrix', () => {
|
|
107
|
+
const lines = formatDebateMatrix({
|
|
108
|
+
topic: 'Test topic',
|
|
109
|
+
advocate: 'Advocate arguments',
|
|
110
|
+
critic: 'Critic counterpoints',
|
|
111
|
+
verdict: 'Final judge synthesis',
|
|
112
|
+
}, 80);
|
|
113
|
+
assert.ok(lines.length > 0);
|
|
114
|
+
assert.ok(lines[0].includes('DEBATE MATRIX'));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('Standalone webChatServer starts, exposes endpoints, and stops cleanly on port 3001', async () => {
|
|
118
|
+
const { startWebChatServer, stopWebChatServer, getWebChatServerStatus } = await import('../../webChatServer.js');
|
|
119
|
+
|
|
120
|
+
const serverInfo = await startWebChatServer({ port: 3309 });
|
|
121
|
+
assert.equal(serverInfo.port, 3309);
|
|
122
|
+
assert.equal(serverInfo.url, 'http://localhost:3309');
|
|
123
|
+
|
|
124
|
+
const status = getWebChatServerStatus();
|
|
125
|
+
assert.equal(status.running, true);
|
|
126
|
+
assert.equal(status.port, 3309);
|
|
127
|
+
|
|
128
|
+
const res = await fetch('http://localhost:3309/api/status');
|
|
129
|
+
const data = await res.json();
|
|
130
|
+
assert.equal(data.status, 'ok');
|
|
131
|
+
assert.equal(data.server, 'Kiku Web Chatbot');
|
|
132
|
+
|
|
133
|
+
const timelineRes = await fetch('http://localhost:3309/api/v1/timeline');
|
|
134
|
+
assert.equal(timelineRes.status, 200);
|
|
135
|
+
const timelinePayload = await timelineRes.json();
|
|
136
|
+
assert.equal(timelinePayload.status, 'success');
|
|
137
|
+
assert.ok(Array.isArray(timelinePayload.data));
|
|
138
|
+
assert.ok(timelinePayload.data.length > 0);
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
const sampleHistory = [
|
|
142
|
+
{ role: 'user', text: 'เล่าประวัติจวีจิ้งอี' },
|
|
143
|
+
{ role: 'assistant', text: 'จวีจิ้งอี เกิดวันที่ 18 มิถุนายน 1994' }
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
const exportJsonRes = await fetch('http://localhost:3309/api/export', {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: { 'Content-Type': 'application/json' },
|
|
149
|
+
body: JSON.stringify({ format: 'json', history: sampleHistory, sessionId: 'test_sess' })
|
|
150
|
+
});
|
|
151
|
+
assert.equal(exportJsonRes.status, 200);
|
|
152
|
+
const exportJson = await exportJsonRes.json();
|
|
153
|
+
assert.equal(exportJson.sessionId, 'test_sess');
|
|
154
|
+
assert.equal(exportJson.messages.length, 2);
|
|
155
|
+
|
|
156
|
+
const exportMdRes = await fetch('http://localhost:3309/api/export', {
|
|
157
|
+
method: 'POST',
|
|
158
|
+
headers: { 'Content-Type': 'application/json' },
|
|
159
|
+
body: JSON.stringify({ format: 'markdown', history: sampleHistory, sessionId: 'test_sess' })
|
|
160
|
+
});
|
|
161
|
+
assert.equal(exportMdRes.status, 200);
|
|
162
|
+
const exportMd = await exportMdRes.text();
|
|
163
|
+
assert.ok(exportMd.includes('# 🌸 Kiku AI Chat Export'));
|
|
164
|
+
assert.ok(exportMd.includes('เล่าประวัติจวีจิ้งอี'));
|
|
165
|
+
|
|
166
|
+
const exportHtmlRes = await fetch('http://localhost:3309/api/export', {
|
|
167
|
+
method: 'POST',
|
|
168
|
+
headers: { 'Content-Type': 'application/json' },
|
|
169
|
+
body: JSON.stringify({ format: 'html', history: sampleHistory, sessionId: 'test_sess' })
|
|
170
|
+
});
|
|
171
|
+
assert.equal(exportHtmlRes.status, 200);
|
|
172
|
+
const exportHtml = await exportHtmlRes.text();
|
|
173
|
+
assert.ok(exportHtml.includes('<!DOCTYPE html>'));
|
|
174
|
+
assert.ok(exportHtml.includes('Kiku AI Chat Export'));
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
const suggestRes = await fetch('http://localhost:3309/api/ai-suggest', {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
headers: { 'Content-Type': 'application/json' },
|
|
180
|
+
body: JSON.stringify({ topic: 'drama' })
|
|
181
|
+
});
|
|
182
|
+
assert.equal(suggestRes.status, 200);
|
|
183
|
+
const suggestData = await suggestRes.json();
|
|
184
|
+
assert.equal(suggestData.status, 'ok');
|
|
185
|
+
assert.ok(Array.isArray(suggestData.suggestions));
|
|
186
|
+
assert.ok(suggestData.suggestions.length > 0);
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
const verifyRes = await fetch('http://localhost:3309/api/verify', {
|
|
190
|
+
method: 'POST',
|
|
191
|
+
headers: { 'Content-Type': 'application/json' },
|
|
192
|
+
body: JSON.stringify({ claim: 'จวีจิ้งอีเป็น Brand Ambassador ของ Versace' })
|
|
193
|
+
});
|
|
194
|
+
assert.equal(verifyRes.status, 200);
|
|
195
|
+
const verifyData = await verifyRes.json();
|
|
196
|
+
assert.equal(verifyData.status, 'ok');
|
|
197
|
+
assert.ok(verifyData.verdict);
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
const newsRes = await fetch('http://localhost:3309/api/news', {
|
|
201
|
+
method: 'POST',
|
|
202
|
+
headers: { 'Content-Type': 'application/json' },
|
|
203
|
+
body: JSON.stringify({ query: 'จวีจิ้งอี' })
|
|
204
|
+
});
|
|
205
|
+
assert.equal(newsRes.status, 200);
|
|
206
|
+
const newsData = await newsRes.json();
|
|
207
|
+
assert.equal(newsData.status, 'ok');
|
|
208
|
+
assert.ok(Array.isArray(newsData.items));
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
const compareRes = await fetch('http://localhost:3309/api/compare', {
|
|
212
|
+
method: 'POST',
|
|
213
|
+
headers: { 'Content-Type': 'application/json' },
|
|
214
|
+
body: JSON.stringify({ itemA: 'In Blossom', itemB: 'Legend of Yunxi' })
|
|
215
|
+
});
|
|
216
|
+
assert.equal(compareRes.status, 200);
|
|
217
|
+
const compareData = await compareRes.json();
|
|
218
|
+
assert.equal(compareData.status, 'ok');
|
|
219
|
+
assert.ok(compareData.comparison?.itemA);
|
|
220
|
+
assert.ok(compareData.comparison?.itemB);
|
|
221
|
+
|
|
222
|
+
const stopped = await stopWebChatServer();
|
|
223
|
+
assert.equal(stopped, true);
|
|
224
|
+
assert.equal(getWebChatServerStatus().running, false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('System Prompt encapsulates MULTI-ANGLE RETRIEVAL & SMART MULTI-TOOL PARALLELISM across broad domains and single-hop guard', async () => {
|
|
228
|
+
const { buildSystemPrompt } = await import('../prompts.js');
|
|
229
|
+
const { config } = await import('../config.js');
|
|
230
|
+
const { KikuAgent } = await import('../agentCore.js');
|
|
231
|
+
|
|
232
|
+
const prompt = buildSystemPrompt();
|
|
233
|
+
|
|
234
|
+
assert.ok(prompt.includes('2A. MULTI-ANGLE RETRIEVAL & SMART MULTI-TOOL PARALLELISM:'));
|
|
235
|
+
assert.ok(prompt.includes('Dramas / Series / Characters / Acting'));
|
|
236
|
+
assert.ok(prompt.includes('Music / Singles / OST / Discography'));
|
|
237
|
+
assert.ok(prompt.includes('News / Recent Activities / Current Events / Trends'));
|
|
238
|
+
assert.ok(prompt.includes('Commercial Endorsements / Brand Styling / Fashion'));
|
|
239
|
+
assert.ok(prompt.includes('Awards / Honors / Music Chart Recognition'));
|
|
240
|
+
assert.ok(prompt.includes('Comprehensive Overview / Introduction for New Fans'));
|
|
241
|
+
assert.ok(prompt.includes('Fact-Checking / Rumors / Verification / Controversies'));
|
|
242
|
+
assert.ok(prompt.includes('SINGLE-HOP PRECISION GUARD (ANTI-OVERFETCH)'));
|
|
243
|
+
assert.ok(prompt.includes('PARALLEL EXECUTION:'));
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
assert.ok(prompt.includes('Action: search_works({"query": "花间令"})'));
|
|
247
|
+
assert.ok(prompt.includes('Action: get_gallery_photos({"query": "花间令"})'));
|
|
248
|
+
assert.ok(prompt.includes('Action: search_discography({"query": "恋爱告急"})'));
|
|
249
|
+
assert.ok(prompt.includes('Action: get_music_videos({"query": "恋爱告急"})'));
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
assert.ok(config.agent.systemPersona.includes('MULTI-ANGLE RETRIEVAL & SMART MULTI-TOOL PARALLELISM:'));
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
const agent = Object.create(KikuAgent.prototype);
|
|
256
|
+
agent.getAllToolsSchema = () => [];
|
|
257
|
+
const agentPrompt = agent.buildSystemPrompt();
|
|
258
|
+
assert.ok(agentPrompt.includes('MULTI-ANGLE RETRIEVAL & SMART MULTI-TOOL PARALLELISM:'));
|
|
259
|
+
assert.ok(agentPrompt.includes('search_works({"query": "花间令"})'));
|
|
260
|
+
assert.ok(agentPrompt.includes('get_gallery_photos({"query": "花间令"})'));
|
|
261
|
+
});
|
|
262
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { writeFileSync, rmSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
import { bumpVersion, findStaleVersionReferences } from '../../../bump_version.js';
|
|
8
|
+
import { PACKAGE_VERSION } from '../version.js';
|
|
9
|
+
|
|
10
|
+
const REPO_ROOT = fileURLToPath(new URL('../../../', import.meta.url));
|
|
11
|
+
|
|
12
|
+
test('bumpVersion accurately calculates patch, minor, and major increments in dry-run mode', () => {
|
|
13
|
+
const patchRes = bumpVersion({ target: 'patch', dryRun: true });
|
|
14
|
+
assert.equal(patchRes.currentVersion, PACKAGE_VERSION);
|
|
15
|
+
assert.ok(patchRes.nextVersion);
|
|
16
|
+
assert.equal(patchRes.changes.length, 3);
|
|
17
|
+
assert.ok(Array.isArray(patchRes.staleReferences));
|
|
18
|
+
|
|
19
|
+
const minorRes = bumpVersion({ target: 'minor', dryRun: true });
|
|
20
|
+
assert.ok(minorRes.nextVersion);
|
|
21
|
+
|
|
22
|
+
const majorRes = bumpVersion({ target: 'major', dryRun: true });
|
|
23
|
+
assert.ok(majorRes.nextVersion);
|
|
24
|
+
|
|
25
|
+
const preRes = bumpVersion({ target: 'prerelease', preId: 'beta', dryRun: true });
|
|
26
|
+
assert.ok(preRes.nextVersion.includes('-beta.'));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('bumpVersion handles explicit semver strings and validates ordering', () => {
|
|
30
|
+
// Hardcoding the next version here would expire on every release; derive a
|
|
31
|
+
// valid future version from the current package version instead.
|
|
32
|
+
const [major, minor, patch] = PACKAGE_VERSION.split('.').map(Number);
|
|
33
|
+
const explicitNext = `${major}.${minor}.${patch + 1}`;
|
|
34
|
+
const explicitRes = bumpVersion({ target: explicitNext, channel: 'beta', dryRun: true });
|
|
35
|
+
assert.equal(explicitRes.nextVersion, explicitNext);
|
|
36
|
+
assert.equal(explicitRes.changes[0].file, 'package.json');
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
assert.throws(() => {
|
|
40
|
+
bumpVersion({ target: 'invalid_version', dryRun: true });
|
|
41
|
+
}, /Unknown bump type or invalid version/);
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
assert.throws(() => {
|
|
45
|
+
bumpVersion({ target: '1.0.0', dryRun: true });
|
|
46
|
+
}, /must be greater than current version/);
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const forceRes = bumpVersion({ target: '1.0.0', force: true, dryRun: true });
|
|
50
|
+
assert.equal(forceRes.nextVersion, '1.0.0');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('stale version audit surfaces hardcoded pins and stays clean for the release', () => {
|
|
54
|
+
const fixturePath = join(REPO_ROOT, 'audit-fixture.tmp.md');
|
|
55
|
+
try {
|
|
56
|
+
writeFileSync(fixturePath, `metadata pinned at ${PACKAGE_VERSION}\n`, 'utf8');
|
|
57
|
+
const hits = findStaleVersionReferences(PACKAGE_VERSION);
|
|
58
|
+
assert.equal(hits.length, 1);
|
|
59
|
+
assert.equal(hits[0].file, 'audit-fixture.tmp.md');
|
|
60
|
+
assert.match(hits[0].snippet, new RegExp(PACKAGE_VERSION.replace(/\./g, '\.')));
|
|
61
|
+
} finally {
|
|
62
|
+
rmSync(fixturePath, { force: true });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Once the fixture is gone, nothing outside package.json/package-lock.json/
|
|
66
|
+
// the README tarball line may pin the released version — this is the drift
|
|
67
|
+
// guard that keeps badges and export metadata on dynamic lookups.
|
|
68
|
+
assert.deepEqual(findStaleVersionReferences(PACKAGE_VERSION), []);
|
|
69
|
+
});
|