kiku-agent 1.3.2 → 1.3.4
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 +69 -12
- package/package.json +2 -1
- package/server/agent/agentCore.js +319 -46
- package/server/agent/appPaths.js +31 -0
- package/server/agent/audioPlayer.js +339 -12
- package/server/agent/autoContext.js +193 -0
- package/server/agent/catalogEntityResolver.js +170 -28
- package/server/agent/clarifyingQuestions.js +137 -35
- package/server/agent/cli.js +177 -48
- package/server/agent/cloudAdapters.js +116 -0
- package/server/agent/commandRegistry.js +409 -80
- package/server/agent/commands.js +1056 -125
- package/server/agent/config.js +1088 -690
- package/server/agent/contextCompactor.js +213 -21
- package/server/agent/cpuMetrics.js +199 -0
- package/server/agent/engineService.js +591 -0
- package/server/agent/eval/modules/evaluation/runner.js +4 -14
- package/server/agent/eval/modules/evaluation/scoring.js +15 -3
- package/server/agent/eval/modules/server/evalApi.js +5 -5
- package/server/agent/eval/modules/ui/evalUi.js +7 -4
- package/server/agent/eventBus.js +1 -0
- package/server/agent/ink/main.jsx +196 -191
- package/server/agent/jobManager.js +11 -1
- package/server/agent/lanDiscovery.js +721 -23
- package/server/agent/llmClient.js +317 -71
- package/server/agent/lmStudioWakeup.js +517 -65
- package/server/agent/locationService.js +797 -0
- package/server/agent/mcpQuizData.js +509 -0
- package/server/agent/memory/contextAllocator.js +19 -7
- package/server/agent/memory.js +3 -1
- package/server/agent/modelCompatibility.js +352 -0
- package/server/agent/modelDefaults.js +1 -0
- package/server/agent/modelManager.js +46 -2
- package/server/agent/newsWatch.js +9 -3
- package/server/agent/processManager.js +229 -0
- package/server/agent/prompts.js +174 -8
- package/server/agent/routingInterceptor.js +9 -1
- package/server/agent/sanitizer.js +41 -6
- package/server/agent/searchQueryPlanner.js +202 -0
- package/server/agent/session.js +59 -4
- package/server/agent/tests/agentFollowUps.test.js +128 -2
- package/server/agent/tests/agentMusicControl.test.js +245 -0
- package/server/agent/tests/apiRouter.test.js +1 -1
- package/server/agent/tests/autoContext.test.js +176 -0
- package/server/agent/tests/bilibiliEmbed.test.js +45 -0
- package/server/agent/tests/chatHardening.test.js +121 -0
- package/server/agent/tests/chineseWebSearchEscalation.test.js +62 -0
- package/server/agent/tests/clarifyingQuestions.test.js +33 -0
- package/server/agent/tests/cliAutomation.test.js +38 -1
- package/server/agent/tests/cloudProviders.test.js +91 -3
- package/server/agent/tests/commands.test.js +92 -3
- package/server/agent/tests/contextIntelligence.test.js +50 -3
- package/server/agent/tests/cpuMetrics.test.js +101 -0
- package/server/agent/tests/decisionAndSearchEnhancements.test.js +32 -0
- package/server/agent/tests/edgeCasesScenarios.test.js +82 -0
- package/server/agent/tests/engineService.test.js +436 -0
- package/server/agent/tests/engineUnload.test.js +94 -0
- package/server/agent/tests/envCommand.test.js +69 -0
- package/server/agent/tests/evalMetrics.test.js +8 -0
- package/server/agent/tests/fetchApi.test.js +99 -0
- package/server/agent/tests/hybridWebSearch.test.js +2 -0
- package/server/agent/tests/lanDiscovery.test.js +5 -0
- package/server/agent/tests/lanInterfaces.test.js +510 -0
- package/server/agent/tests/lmStudioWakeup.test.js +278 -0
- package/server/agent/tests/locationTool.test.js +278 -0
- package/server/agent/tests/memoryRoute.test.js +173 -0
- package/server/agent/tests/memoryUiNormalization.test.js +75 -0
- package/server/agent/tests/modelCompatibility.test.js +148 -0
- package/server/agent/tests/modelManager.test.js +32 -0
- package/server/agent/tests/musicControlUiBridge.test.js +113 -0
- package/server/agent/tests/nextGenArchitecture.test.js +4 -2
- package/server/agent/tests/openDomainPrompt.test.js +197 -0
- package/server/agent/tests/openDomainSearch.test.js +121 -0
- package/server/agent/tests/processManager.test.js +144 -0
- package/server/agent/tests/providerPersistence.test.js +10 -5
- package/server/agent/tests/quickActionsAndModals.test.js +180 -0
- package/server/agent/tests/searchQueryPlanner.test.js +108 -0
- package/server/agent/tests/securityAuditRemediation.test.js +16 -9
- package/server/agent/tests/securityAuditRemediations2.test.js +154 -0
- package/server/agent/tests/securityHardening.test.js +4 -4
- package/server/agent/tests/sessionAndCopy.test.js +39 -4
- package/server/agent/tests/sidebarControls.test.js +34 -0
- package/server/agent/tests/statefulMemory.test.js +8 -1
- package/server/agent/tests/textProtocolLoop.test.js +71 -0
- package/server/agent/tests/textToolParsing.test.js +48 -0
- package/server/agent/tests/toolRoutingRegressions.test.js +41 -0
- package/server/agent/tests/unifiedEndpoints.test.js +13 -6
- package/server/agent/tests/v110Features.test.js +1 -1
- package/server/agent/tests/vramEstimator.test.js +75 -0
- package/server/agent/tests/webChatDocs.test.js +199 -0
- package/server/agent/tests/webChatInferenceControl.test.js +298 -0
- package/server/agent/tests/webChatInspection.test.js +256 -5
- package/server/agent/tests/webChatMcp.test.js +94 -0
- package/server/agent/tests/webExtract.test.js +23 -0
- package/server/agent/tests/webSources.test.js +46 -12
- package/server/agent/textToolProtocol.js +173 -46
- package/server/agent/toolRouter.js +318 -44
- package/server/agent/tools.js +877 -96
- package/server/agent/ui.js +39 -34
- package/server/agent/vramService.js +35 -54
- package/server/agent/webSearch.js +441 -162
- package/server/agent/websearch/orchestrator.js +15 -5
- package/server/agent/websearch/resilience/cache.js +9 -2
- package/server/agent/websearch/security/ssrf.js +41 -10
- package/server/agent/youtubeAuth.js +5 -20
- package/server/apiRouter.js +5558 -4422
- package/server/mcp/data/discography.js +2 -2
- package/server/mcp/examples/antigravity.example.json +17 -0
- package/server/mcp/handler.js +8 -4
- package/server/mcp/security.js +220 -21
- package/server/mcp/server.js +2 -1
- package/server/mcp/tests/connection.test.js +14 -8
- package/server/mcp/tests/mcpKeyAuth.test.js +156 -0
- package/server/mcp/tools.js +7 -1
- package/server/supabase_migration_prefix.sql +102 -0
- package/server/supabase_schema.sql +18 -18
- package/server/web-chat/core/connection.js +179 -218
- package/server/web-chat/core/conversationContext.js +9 -2
- package/server/web-chat/core/directCatalogEngine.js +52 -28
- package/server/web-chat/core/dramaKnowledge.js +33 -22
- package/server/web-chat/core/followUpEngine.js +735 -81
- package/server/web-chat/core/futureEventGuard.js +5 -1
- package/server/web-chat/core/musicControlAction.js +70 -0
- package/server/web-chat/core/security.js +32 -21
- package/server/web-chat/core/toolInspector.js +6 -6
- package/server/web-chat/core/urlGuard.js +45 -0
- package/server/web-chat/router.js +204 -157
- package/server/web-chat/routes/chatRoute.js +885 -488
- package/server/web-chat/routes/engineRoute.js +270 -0
- package/server/web-chat/routes/exportRoute.js +1 -1
- package/server/web-chat/routes/lanDiscoveryRoute.js +17 -2
- package/server/web-chat/routes/lmStudioLoadRoute.js +172 -0
- package/server/web-chat/routes/mcpRoute.js +386 -0
- package/server/web-chat/routes/memoryRoute.js +253 -0
- package/server/web-chat/routes/providerRoute.js +49 -16
- package/server/web-chat/routes/staticRoute.js +306 -17
- package/server/web-chat/routes/statusRoute.js +194 -73
- package/server/web-chat/routes/suggestRoute.js +11 -3
- package/server/web-chat/routes/verifyRoute.js +26 -4
- package/server/web-chat/server.js +7 -6
- package/server/web-chat/ui/docsBuilder.js +3558 -0
- package/server/web-chat/ui/htmlBuilder.js +12 -0
- package/server/web-chat/ui/scripts/client.js +3401 -1302
- package/server/web-chat/ui/scripts/webchatI18n.js +155 -153
- package/server/web-chat/ui/settingsBuilder.js +7870 -0
- package/server/web-chat/ui/styles/lan-card.css +165 -0
- package/server/web-chat/ui/styles/main.css +4289 -447
- package/server/web-chat/ui/sw.js +64 -0
- package/server/web-chat/ui/templates/body.html +407 -281
- package/src/data/awardsData.js +146 -146
- package/src/data/musicVideoData.js +6 -4
- package/src/lib/vram/estimator.js +195 -19
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
DO $$
|
|
2
|
+
BEGIN
|
|
3
|
+
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'api_keys')
|
|
4
|
+
AND NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jujingyi_kikuuu_api_keys') THEN
|
|
5
|
+
ALTER TABLE public.api_keys RENAME TO jujingyi_kikuuu_api_keys;
|
|
6
|
+
ELSIF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jujingyi_kikuuu_api_keys') THEN
|
|
7
|
+
CREATE TABLE public.jujingyi_kikuuu_api_keys (
|
|
8
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
9
|
+
key_prefix VARCHAR(32) NOT NULL,
|
|
10
|
+
key_hash VARCHAR(64) UNIQUE NOT NULL,
|
|
11
|
+
tier VARCHAR(32) DEFAULT 'developer_vip',
|
|
12
|
+
rate_limit_per_min INT DEFAULT 1000,
|
|
13
|
+
is_active BOOLEAN DEFAULT TRUE,
|
|
14
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
15
|
+
last_used_at TIMESTAMPTZ,
|
|
16
|
+
expires_at TIMESTAMPTZ,
|
|
17
|
+
revoked_reason TEXT
|
|
18
|
+
);
|
|
19
|
+
END IF;
|
|
20
|
+
END $$;
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
DO $$
|
|
24
|
+
BEGIN
|
|
25
|
+
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'quiz_sessions')
|
|
26
|
+
AND NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jujingyi_kikuuu_quiz_sessions') THEN
|
|
27
|
+
ALTER TABLE public.quiz_sessions RENAME TO jujingyi_kikuuu_quiz_sessions;
|
|
28
|
+
ELSIF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jujingyi_kikuuu_quiz_sessions') THEN
|
|
29
|
+
CREATE TABLE public.jujingyi_kikuuu_quiz_sessions (
|
|
30
|
+
session_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
31
|
+
challenge_token VARCHAR(255) UNIQUE NOT NULL,
|
|
32
|
+
is_completed BOOLEAN DEFAULT FALSE,
|
|
33
|
+
is_claimed BOOLEAN DEFAULT FALSE,
|
|
34
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
35
|
+
expires_at TIMESTAMPTZ DEFAULT (NOW() + INTERVAL '15 minutes')
|
|
36
|
+
);
|
|
37
|
+
END IF;
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
IF EXISTS (
|
|
41
|
+
SELECT 1 FROM information_schema.columns
|
|
42
|
+
WHERE table_schema = 'public' AND table_name = 'jujingyi_kikuuu_quiz_sessions'
|
|
43
|
+
AND column_name = 'challenge_token' AND character_maximum_length < 255
|
|
44
|
+
) THEN
|
|
45
|
+
ALTER TABLE public.jujingyi_kikuuu_quiz_sessions ALTER COLUMN challenge_token TYPE VARCHAR(255);
|
|
46
|
+
END IF;
|
|
47
|
+
END $$;
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
DO $$
|
|
51
|
+
BEGIN
|
|
52
|
+
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'api_request_logs')
|
|
53
|
+
AND NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jujingyi_kikuuu_api_request_logs') THEN
|
|
54
|
+
ALTER TABLE public.api_request_logs RENAME TO jujingyi_kikuuu_api_request_logs;
|
|
55
|
+
ELSIF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jujingyi_kikuuu_api_request_logs') THEN
|
|
56
|
+
CREATE TABLE public.jujingyi_kikuuu_api_request_logs (
|
|
57
|
+
id BIGSERIAL PRIMARY KEY,
|
|
58
|
+
ip_address VARCHAR(45) NOT NULL,
|
|
59
|
+
endpoint VARCHAR(255) NOT NULL,
|
|
60
|
+
method VARCHAR(10) NOT NULL,
|
|
61
|
+
status_code INT NOT NULL,
|
|
62
|
+
response_time_ms NUMERIC(8,2),
|
|
63
|
+
key_prefix VARCHAR(32),
|
|
64
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
65
|
+
);
|
|
66
|
+
END IF;
|
|
67
|
+
END $$;
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
ALTER TABLE public.jujingyi_kikuuu_api_keys ENABLE ROW LEVEL SECURITY;
|
|
71
|
+
ALTER TABLE public.jujingyi_kikuuu_quiz_sessions ENABLE ROW LEVEL SECURITY;
|
|
72
|
+
ALTER TABLE public.jujingyi_kikuuu_api_request_logs ENABLE ROW LEVEL SECURITY;
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
DO $$
|
|
76
|
+
BEGIN
|
|
77
|
+
|
|
78
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename = 'jujingyi_kikuuu_api_keys' AND policyname = 'Service Role Access on jujingyi_kikuuu_api_keys') THEN
|
|
79
|
+
CREATE POLICY "Service Role Access on jujingyi_kikuuu_api_keys"
|
|
80
|
+
ON public.jujingyi_kikuuu_api_keys FOR ALL USING (auth.role() = 'service_role');
|
|
81
|
+
END IF;
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename = 'jujingyi_kikuuu_quiz_sessions' AND policyname = 'Service Role Access on jujingyi_kikuuu_quiz_sessions') THEN
|
|
85
|
+
CREATE POLICY "Service Role Access on jujingyi_kikuuu_quiz_sessions"
|
|
86
|
+
ON public.jujingyi_kikuuu_quiz_sessions FOR ALL USING (auth.role() = 'service_role');
|
|
87
|
+
END IF;
|
|
88
|
+
|
|
89
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename = 'jujingyi_kikuuu_api_request_logs' AND policyname = 'Service Role Access on jujingyi_kikuuu_api_request_logs') THEN
|
|
90
|
+
CREATE POLICY "Service Role Access on jujingyi_kikuuu_api_request_logs"
|
|
91
|
+
ON public.jujingyi_kikuuu_api_request_logs FOR ALL USING (auth.role() = 'service_role');
|
|
92
|
+
END IF;
|
|
93
|
+
END $$;
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
ALTER INDEX IF EXISTS idx_api_keys_hash RENAME TO idx_jujingyi_kikuuu_api_keys_hash;
|
|
97
|
+
ALTER INDEX IF EXISTS idx_quiz_sessions_token RENAME TO idx_jujingyi_kikuuu_quiz_sessions_token;
|
|
98
|
+
ALTER INDEX IF EXISTS idx_api_logs_ip_time RENAME TO idx_jujingyi_kikuuu_api_logs_ip_time;
|
|
99
|
+
|
|
100
|
+
CREATE INDEX IF NOT EXISTS idx_jujingyi_kikuuu_api_keys_hash ON public.jujingyi_kikuuu_api_keys (key_hash) WHERE is_active = TRUE;
|
|
101
|
+
CREATE INDEX IF NOT EXISTS idx_jujingyi_kikuuu_quiz_sessions_token ON public.jujingyi_kikuuu_quiz_sessions (challenge_token) WHERE is_claimed = FALSE;
|
|
102
|
+
CREATE INDEX IF NOT EXISTS idx_jujingyi_kikuuu_api_logs_ip_time ON public.jujingyi_kikuuu_api_request_logs (ip_address, created_at DESC);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
CREATE TABLE IF NOT EXISTS public.
|
|
1
|
+
CREATE TABLE IF NOT EXISTS public.jujingyi_kikuuu_api_keys (
|
|
2
2
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
3
3
|
key_prefix VARCHAR(32) NOT NULL,
|
|
4
4
|
key_hash VARCHAR(64) UNIQUE NOT NULL,
|
|
@@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS public.api_keys (
|
|
|
12
12
|
);
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
CREATE TABLE IF NOT EXISTS public.
|
|
15
|
+
CREATE TABLE IF NOT EXISTS public.jujingyi_kikuuu_quiz_sessions (
|
|
16
16
|
session_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
17
17
|
challenge_token VARCHAR(64) UNIQUE NOT NULL,
|
|
18
18
|
is_completed BOOLEAN DEFAULT FALSE,
|
|
@@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS public.quiz_sessions (
|
|
|
22
22
|
);
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
CREATE TABLE IF NOT EXISTS public.
|
|
25
|
+
CREATE TABLE IF NOT EXISTS public.jujingyi_kikuuu_api_request_logs (
|
|
26
26
|
id BIGSERIAL PRIMARY KEY,
|
|
27
27
|
ip_address VARCHAR(45) NOT NULL,
|
|
28
28
|
endpoint VARCHAR(255) NOT NULL,
|
|
@@ -34,30 +34,30 @@ CREATE TABLE IF NOT EXISTS public.api_request_logs (
|
|
|
34
34
|
);
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
ALTER TABLE public.
|
|
38
|
-
ALTER TABLE public.
|
|
39
|
-
ALTER TABLE public.
|
|
37
|
+
ALTER TABLE public.jujingyi_kikuuu_api_keys ENABLE ROW LEVEL SECURITY;
|
|
38
|
+
ALTER TABLE public.jujingyi_kikuuu_quiz_sessions ENABLE ROW LEVEL SECURITY;
|
|
39
|
+
ALTER TABLE public.jujingyi_kikuuu_api_request_logs ENABLE ROW LEVEL SECURITY;
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
DO $$
|
|
43
43
|
BEGIN
|
|
44
|
-
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE policyname = 'Service Role Access on
|
|
45
|
-
CREATE POLICY "Service Role Access on
|
|
46
|
-
ON public.
|
|
44
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE policyname = 'Service Role Access on jujingyi_kikuuu_api_keys') THEN
|
|
45
|
+
CREATE POLICY "Service Role Access on jujingyi_kikuuu_api_keys"
|
|
46
|
+
ON public.jujingyi_kikuuu_api_keys FOR ALL USING (auth.role() = 'service_role');
|
|
47
47
|
END IF;
|
|
48
48
|
|
|
49
|
-
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE policyname = 'Service Role Access on
|
|
50
|
-
CREATE POLICY "Service Role Access on
|
|
51
|
-
ON public.
|
|
49
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE policyname = 'Service Role Access on jujingyi_kikuuu_quiz_sessions') THEN
|
|
50
|
+
CREATE POLICY "Service Role Access on jujingyi_kikuuu_quiz_sessions"
|
|
51
|
+
ON public.jujingyi_kikuuu_quiz_sessions FOR ALL USING (auth.role() = 'service_role');
|
|
52
52
|
END IF;
|
|
53
53
|
|
|
54
|
-
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE policyname = 'Service Role Access on
|
|
55
|
-
CREATE POLICY "Service Role Access on
|
|
56
|
-
ON public.
|
|
54
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE policyname = 'Service Role Access on jujingyi_kikuuu_api_request_logs') THEN
|
|
55
|
+
CREATE POLICY "Service Role Access on jujingyi_kikuuu_api_request_logs"
|
|
56
|
+
ON public.jujingyi_kikuuu_api_request_logs FOR ALL USING (auth.role() = 'service_role');
|
|
57
57
|
END IF;
|
|
58
58
|
END $$;
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
CREATE INDEX IF NOT EXISTS
|
|
62
|
-
CREATE INDEX IF NOT EXISTS
|
|
63
|
-
CREATE INDEX IF NOT EXISTS
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_jujingyi_kikuuu_api_keys_hash ON public.jujingyi_kikuuu_api_keys (key_hash) WHERE is_active = TRUE;
|
|
62
|
+
CREATE INDEX IF NOT EXISTS idx_jujingyi_kikuuu_quiz_sessions_token ON public.jujingyi_kikuuu_quiz_sessions (challenge_token) WHERE is_claimed = FALSE;
|
|
63
|
+
CREATE INDEX IF NOT EXISTS idx_jujingyi_kikuuu_api_logs_ip_time ON public.jujingyi_kikuuu_api_request_logs (ip_address, created_at DESC);
|
|
@@ -1,218 +1,179 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { PUBLIC_REMOTE_TOOL_NAMES } from '../../agent/publicTools.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export let
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export function setSharedWebMemory(mem) {
|
|
17
|
-
if (mem) sharedWebMemory = mem;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function setCurrentAgent(agent) {
|
|
21
|
-
if (agent) {
|
|
22
|
-
currentAgent = agent;
|
|
23
|
-
if (agent.memory) sharedWebMemory = agent.memory;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function normalizeWebSessionId(value) {
|
|
28
|
-
const normalized = String(value || '').trim().replace(/[^a-zA-Z0-9_-]+/g, '_').slice(0, 100);
|
|
29
|
-
return normalized || null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function credentialFingerprint(value) {
|
|
33
|
-
if (!value) return 'no-key';
|
|
34
|
-
return createHash('sha256').update(String(value)).digest('hex').slice(0, 12);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function computeSessionHash(messages) {
|
|
38
|
-
if (!Array.isArray(messages) || messages.length === 0) return '';
|
|
39
|
-
const payload = messages.map(m => (m.role || '') + ':' + (m.parts?.[0]?.content || m.text || '')).join('|');
|
|
40
|
-
return createHash('sha256').update(payload).digest('hex').slice(0, 16);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const wslHint = 'vLLM runs on WSL/Linux port 8000 (Windows cannot host it natively). Start it with: wsl -d Ubuntu -- vllm serve <model> --port 8000';
|
|
181
|
-
resolution.error = resolution.error ? `${resolution.error}\n Notice: ${wslHint}` : wslHint;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (resolution.connected && resolution.selectedModel) {
|
|
185
|
-
const agent = new KikuAgent({
|
|
186
|
-
client,
|
|
187
|
-
thinkingMode: customOptions.thinking || 'show',
|
|
188
|
-
memory: new AgentMemory({ persistence: false }),
|
|
189
|
-
sessionId,
|
|
190
|
-
sessionPersistence: false,
|
|
191
|
-
allowedToolNames: PUBLIC_REMOTE_TOOL_NAMES,
|
|
192
|
-
});
|
|
193
|
-
await prepareWebAgent(agent, { history: customOptions.history, sessionId });
|
|
194
|
-
const connection = {
|
|
195
|
-
ready: true,
|
|
196
|
-
agent,
|
|
197
|
-
client,
|
|
198
|
-
provider,
|
|
199
|
-
model: resolution.selectedModel,
|
|
200
|
-
models: resolution.models || [],
|
|
201
|
-
usedFallback: resolution.usedFallback || false,
|
|
202
|
-
resolution,
|
|
203
|
-
contentHash: computeSessionHash(agent.session?.messages || [])
|
|
204
|
-
};
|
|
205
|
-
return connection;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
ready: false,
|
|
210
|
-
client,
|
|
211
|
-
provider,
|
|
212
|
-
model: requestedModel,
|
|
213
|
-
models: resolution.models || [],
|
|
214
|
-
state: resolution.state,
|
|
215
|
-
error: resolution.error || 'Server offline',
|
|
216
|
-
resolution
|
|
217
|
-
};
|
|
218
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { LLMClient } from '../../agent/llmClient.js';
|
|
4
|
+
import { KikuAgent } from '../../agent/agentCore.js';
|
|
5
|
+
import { AgentMemory } from '../../agent/memory.js';
|
|
6
|
+
import { ModelManager } from '../../agent/modelManager.js';
|
|
7
|
+
import { config, getProviderBaseUrl, getPersistedActiveProvider } from '../../agent/config.js';
|
|
8
|
+
import { validateInferenceBaseUrl } from './urlGuard.js';
|
|
9
|
+
import { PUBLIC_REMOTE_TOOL_NAMES } from '../../agent/publicTools.js';
|
|
10
|
+
import { registerBackgroundProcess } from '../../agent/processManager.js';
|
|
11
|
+
import { wakeUpLmStudio } from '../../agent/lmStudioWakeup.js';
|
|
12
|
+
|
|
13
|
+
export let sharedWebMemory = new AgentMemory({ persistence: false });
|
|
14
|
+
export let currentAgent = null;
|
|
15
|
+
|
|
16
|
+
export function setSharedWebMemory(mem) {
|
|
17
|
+
if (mem) sharedWebMemory = mem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function setCurrentAgent(agent) {
|
|
21
|
+
if (agent) {
|
|
22
|
+
currentAgent = agent;
|
|
23
|
+
if (agent.memory) sharedWebMemory = agent.memory;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function normalizeWebSessionId(value) {
|
|
28
|
+
const normalized = String(value || '').trim().replace(/[^a-zA-Z0-9_-]+/g, '_').slice(0, 100);
|
|
29
|
+
return normalized || null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function credentialFingerprint(value) {
|
|
33
|
+
if (!value) return 'no-key';
|
|
34
|
+
return createHash('sha256').update(String(value)).digest('hex').slice(0, 12);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function computeSessionHash(messages) {
|
|
38
|
+
if (!Array.isArray(messages) || messages.length === 0) return '';
|
|
39
|
+
const payload = messages.map(m => (m.role || '') + ':' + (m.parts?.[0]?.content || m.text || '')).join('|');
|
|
40
|
+
return createHash('sha256').update(payload).digest('hex').slice(0, 16);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
export async function prepareWebAgent(agent, options = {}) {
|
|
45
|
+
await agent.ready();
|
|
46
|
+
if (Array.isArray(options.history) && options.history.length > 0) {
|
|
47
|
+
const agentMsgCount = agent.session?.messages?.length || 0;
|
|
48
|
+
const frontendMsgCount = options.history.length;
|
|
49
|
+
await agent.hydrateConversationHistory(options.history, {
|
|
50
|
+
sessionId: options.sessionId,
|
|
51
|
+
maxMessages: 24,
|
|
52
|
+
force: frontendMsgCount > agentMsgCount,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return agent;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
export async function waitForLMStudio(baseUrl, timeoutMs = 8000) {
|
|
60
|
+
const client = new LLMClient({ provider: 'lmstudio', baseUrl });
|
|
61
|
+
const start = Date.now();
|
|
62
|
+
while (Date.now() - start < timeoutMs) {
|
|
63
|
+
try {
|
|
64
|
+
const health = await client.checkConnection();
|
|
65
|
+
if (health.connected) return health;
|
|
66
|
+
} catch {}
|
|
67
|
+
await new Promise(r => setTimeout(r, 600));
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let lmStudioAutoStartPromise = null;
|
|
73
|
+
export function attemptAutoStartLMStudio(baseUrl, model) {
|
|
74
|
+
if (lmStudioAutoStartPromise) return lmStudioAutoStartPromise;
|
|
75
|
+
lmStudioAutoStartPromise = attemptAutoStartLMStudioInner(baseUrl, model).finally(() => {
|
|
76
|
+
lmStudioAutoStartPromise = null;
|
|
77
|
+
});
|
|
78
|
+
return lmStudioAutoStartPromise;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function attemptAutoStartLMStudioInner(baseUrl, model) {
|
|
82
|
+
try {
|
|
83
|
+
const res = await wakeUpLmStudio({
|
|
84
|
+
baseUrl,
|
|
85
|
+
model,
|
|
86
|
+
quiet: true,
|
|
87
|
+
maxWaitMs: 25000,
|
|
88
|
+
});
|
|
89
|
+
if (res.ok) {
|
|
90
|
+
return await waitForLMStudio(baseUrl, 5000);
|
|
91
|
+
}
|
|
92
|
+
} catch {}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function resolveAgentConnection(customOptions = {}) {
|
|
97
|
+
const isAuto = !customOptions.provider || customOptions.provider === 'auto';
|
|
98
|
+
const serverProvider = currentAgent?.client?.provider || config.activeProvider || 'lmstudio';
|
|
99
|
+
const provider = isAuto ? serverProvider : customOptions.provider;
|
|
100
|
+
|
|
101
|
+
const serverModel = currentAgent?.client?.model || config.providers?.[provider]?.model || 'google/gemma-4-e4b';
|
|
102
|
+
const requestedModel = (customOptions.model && !isAuto)
|
|
103
|
+
? customOptions.model
|
|
104
|
+
: (provider === serverProvider ? serverModel : (config.providers?.[provider]?.model || 'google/gemma-4-e4b'));
|
|
105
|
+
|
|
106
|
+
const isServerAgentMatch = Boolean(currentAgent?.client && provider === currentAgent.client.provider);
|
|
107
|
+
const baseUrl = (typeof customOptions.baseUrl === 'string' && customOptions.baseUrl.trim()
|
|
108
|
+
? customOptions.baseUrl.trim().replace(/\/+$/, '')
|
|
109
|
+
: null)
|
|
110
|
+
|| (isServerAgentMatch ? currentAgent.client.baseUrl : null)
|
|
111
|
+
|| getProviderBaseUrl(provider);
|
|
112
|
+
|
|
113
|
+
if (customOptions.callerIsTrusted !== undefined) {
|
|
114
|
+
const guard = await validateInferenceBaseUrl(baseUrl, { callerIsTrusted: customOptions.callerIsTrusted });
|
|
115
|
+
if (!guard.ok) { const error = new Error(guard.reason); error.statusCode = 400; throw error; }
|
|
116
|
+
}
|
|
117
|
+
const sessionId = normalizeWebSessionId(customOptions.sessionId);
|
|
118
|
+
|
|
119
|
+
const apiKey = customOptions.apiKey
|
|
120
|
+
|| (isServerAgentMatch ? currentAgent.client.apiKey : '')
|
|
121
|
+
|| '';
|
|
122
|
+
|
|
123
|
+
const client = new LLMClient({
|
|
124
|
+
provider,
|
|
125
|
+
baseUrl,
|
|
126
|
+
model: requestedModel,
|
|
127
|
+
apiKey
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const modelManager = new ModelManager(client, { requestedModel });
|
|
131
|
+
let resolution = await modelManager.resolve({ requestedModel });
|
|
132
|
+
|
|
133
|
+
if (customOptions.allowAutoStart !== false && provider === 'lmstudio' && resolution.state === 'server_offline' && process.env.NODE_ENV !== 'test' && !process.execArgv.includes('--test')) {
|
|
134
|
+
const started = await attemptAutoStartLMStudio(baseUrl, requestedModel);
|
|
135
|
+
if (started) {
|
|
136
|
+
resolution = await modelManager.resolve({ requestedModel });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (provider === 'vllm' && resolution.state === 'server_offline' && !resolution.error?.includes('WSL')) {
|
|
141
|
+
const wslHint = 'vLLM runs on WSL/Linux port 8000 (Windows cannot host it natively). Start it with: wsl -d Ubuntu -- vllm serve <model> --port 8000';
|
|
142
|
+
resolution.error = resolution.error ? `${resolution.error}\n Notice: ${wslHint}` : wslHint;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (resolution.connected && resolution.selectedModel) {
|
|
146
|
+
const agent = new KikuAgent({
|
|
147
|
+
client,
|
|
148
|
+
thinkingMode: customOptions.thinking || 'show',
|
|
149
|
+
memory: new AgentMemory({ persistence: false }),
|
|
150
|
+
sessionId,
|
|
151
|
+
sessionPersistence: false,
|
|
152
|
+
allowedToolNames: PUBLIC_REMOTE_TOOL_NAMES,
|
|
153
|
+
});
|
|
154
|
+
await prepareWebAgent(agent, { history: customOptions.history, sessionId });
|
|
155
|
+
const connection = {
|
|
156
|
+
ready: true,
|
|
157
|
+
agent,
|
|
158
|
+
client,
|
|
159
|
+
provider,
|
|
160
|
+
model: resolution.selectedModel,
|
|
161
|
+
models: resolution.models || [],
|
|
162
|
+
usedFallback: resolution.usedFallback || false,
|
|
163
|
+
resolution,
|
|
164
|
+
contentHash: computeSessionHash(agent.session?.messages || [])
|
|
165
|
+
};
|
|
166
|
+
return connection;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
ready: false,
|
|
171
|
+
client,
|
|
172
|
+
provider,
|
|
173
|
+
model: requestedModel,
|
|
174
|
+
models: resolution.models || [],
|
|
175
|
+
state: resolution.state,
|
|
176
|
+
error: resolution.error || 'Server offline',
|
|
177
|
+
resolution
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -38,10 +38,17 @@ function cleanHistoryText(value) {
|
|
|
38
38
|
|
|
39
39
|
export function detectConversationDomain(value) {
|
|
40
40
|
const text = String(value || '');
|
|
41
|
+
let bestDomain = 'general';
|
|
42
|
+
let maxScore = 0;
|
|
41
43
|
for (const [domain, pattern] of Object.entries(DOMAIN_PATTERNS)) {
|
|
42
|
-
|
|
44
|
+
const matches = text.match(new RegExp(pattern.source, 'gi'));
|
|
45
|
+
const score = matches ? matches.length : 0;
|
|
46
|
+
if (score > maxScore) {
|
|
47
|
+
maxScore = score;
|
|
48
|
+
bestDomain = domain;
|
|
49
|
+
}
|
|
43
50
|
}
|
|
44
|
-
return
|
|
51
|
+
return bestDomain;
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
export function isContextDependentQuery(query) {
|