@mazees/mark 5.0.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/LICENSE +29 -0
- package/README.md +209 -0
- package/bin/mark.js +27 -0
- package/out/renderer/assets/icon-DHo2CYph.svg +1 -0
- package/out/renderer/assets/index-G2fADWO-.js +4768 -0
- package/out/renderer/assets/index-f3mVse3c.css +1 -0
- package/out/renderer/assets/music-cover-CG4vi3kZ.png +0 -0
- package/out/renderer/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-700-normal-O-jipLrW.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-700-normal-fHs-vx92.woff +0 -0
- package/out/renderer/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
- package/out/renderer/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
- package/out/renderer/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
- package/out/renderer/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
- package/out/renderer/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 +0 -0
- package/out/renderer/favicon.ico +0 -0
- package/out/renderer/icon-256.png +0 -0
- package/out/renderer/icon.svg +1 -0
- package/out/renderer/index.html +25 -0
- package/out/renderer/manifest.json +25 -0
- package/out/renderer/robot-simulator.html +1640 -0
- package/package.json +106 -0
- package/src/cli/index.js +332 -0
- package/src/cli/monitor.js +413 -0
- package/src/cli/theme.js +347 -0
- package/src/launcher/MarkTray.cs +227 -0
- package/src/main/browser-agent.js +1132 -0
- package/src/main/git-service.js +90 -0
- package/src/main/google/google-calendar.js +69 -0
- package/src/main/google/google-drive.js +263 -0
- package/src/main/google/google-gmail.js +131 -0
- package/src/main/google/google-service.js +241 -0
- package/src/main/node-tools.js +23 -0
- package/src/main/pc-agent-scripts/mouse-locker.ps1 +76 -0
- package/src/main/pc-agent-scripts/ocr-region.ps1 +185 -0
- package/src/main/pc-agent-scripts/pc-daemon.ps1 +726 -0
- package/src/main/pc-agent-scripts/pc-overlay.ps1 +253 -0
- package/src/main/pc-agent-scripts/pick-folder.ps1 +33 -0
- package/src/main/pc-agent-scripts/read-ui.ps1 +181 -0
- package/src/main/pc-agent-scripts/take-screenshot.ps1 +24 -0
- package/src/main/pc-agent-scripts/win-action.ps1 +380 -0
- package/src/main/plugins/plugin-loader.js +306 -0
- package/src/main/skills/skill-manager.js +265 -0
- package/src/main/syntax-validator.js +455 -0
- package/src/main/task-daemon.js +117 -0
- package/src/main/telegram/telegram-service.js +835 -0
- package/src/main/tools/browser-tools.js +516 -0
- package/src/main/tools/file-tools.js +1126 -0
- package/src/main/tools/git-tools.js +79 -0
- package/src/main/tools/google-tools.js +566 -0
- package/src/main/tools/index.js +36 -0
- package/src/main/tools/media-tools.js +42 -0
- package/src/main/tools/system-tools.js +295 -0
- package/src/main/tools/task-tools.js +73 -0
- package/src/main/tools/telegram-tools.js +101 -0
- package/src/renderer/index.html +24 -0
- package/src/renderer/public/favicon.ico +0 -0
- package/src/renderer/public/icon-256.png +0 -0
- package/src/renderer/public/icon.svg +1 -0
- package/src/renderer/public/manifest.json +25 -0
- package/src/renderer/public/robot-simulator.html +1640 -0
- package/src/renderer/src/App.jsx +304 -0
- package/src/renderer/src/api/ai/awareness.js +294 -0
- package/src/renderer/src/api/ai/chatSummarizer.js +79 -0
- package/src/renderer/src/api/ai/contextCompactor.js +129 -0
- package/src/renderer/src/api/ai/contextManager.js +1130 -0
- package/src/renderer/src/api/ai/core.js +167 -0
- package/src/renderer/src/api/ai/index.js +4 -0
- package/src/renderer/src/api/ai/memoryGroomer.js +101 -0
- package/src/renderer/src/api/ai/persona.js +115 -0
- package/src/renderer/src/api/ai/planning.js +317 -0
- package/src/renderer/src/api/ai/relationship.js +177 -0
- package/src/renderer/src/api/ai/skillSynthesizer.js +168 -0
- package/src/renderer/src/api/ai/tools.js +167 -0
- package/src/renderer/src/api/ai/utils.js +359 -0
- package/src/renderer/src/api/db.js +1013 -0
- package/src/renderer/src/api/oramaStore.js +592 -0
- package/src/renderer/src/api/ragPipeline.js +87 -0
- package/src/renderer/src/api/subagent/subagentExecutor.js +780 -0
- package/src/renderer/src/api/subagent/subagentPrompt.js +44 -0
- package/src/renderer/src/api/subagent/subagentStore.js +173 -0
- package/src/renderer/src/api/taskExecutor.js +18 -0
- package/src/renderer/src/api/taskStore.js +315 -0
- package/src/renderer/src/api/tools/core-tools.js +707 -0
- package/src/renderer/src/api/tools/index.js +151 -0
- package/src/renderer/src/api/turnPairMigrator.js +200 -0
- package/src/renderer/src/api/vectorMemory.js +136 -0
- package/src/renderer/src/api/wakeWord.js +101 -0
- package/src/renderer/src/api/web-bridge.js +885 -0
- package/src/renderer/src/api/webSpeech.js +178 -0
- package/src/renderer/src/api/workspaceRag.js +76 -0
- package/src/renderer/src/assets/electron.svg +10 -0
- package/src/renderer/src/assets/icon.svg +1 -0
- package/src/renderer/src/assets/main.css +589 -0
- package/src/renderer/src/assets/music-cover.png +0 -0
- package/src/renderer/src/assets/wavy-lines.svg +25 -0
- package/src/renderer/src/components/Chat/ApprovalBubble.jsx +127 -0
- package/src/renderer/src/components/Chat/CodeBlock.jsx +83 -0
- package/src/renderer/src/components/Chat/DurableTaskBubble.jsx +455 -0
- package/src/renderer/src/components/Chat/FileDiffModal.jsx +328 -0
- package/src/renderer/src/components/Chat/GeminiWidgets.jsx +82 -0
- package/src/renderer/src/components/Chat/MemoryFooterBubble.jsx +34 -0
- package/src/renderer/src/components/Chat/MessageBubble.jsx +459 -0
- package/src/renderer/src/components/Chat/PluginExecutionBubble.jsx +41 -0
- package/src/renderer/src/components/Chat/ThinkingBubble.jsx +316 -0
- package/src/renderer/src/components/Chat/YoutubeSearchBubble.jsx +43 -0
- package/src/renderer/src/components/Chat/YoutubeSummaryBubble.jsx +40 -0
- package/src/renderer/src/components/Chat/index.js +11 -0
- package/src/renderer/src/components/ChatList.jsx +434 -0
- package/src/renderer/src/components/GlobalCameraManager.jsx +67 -0
- package/src/renderer/src/components/Versions.jsx +15 -0
- package/src/renderer/src/components/YoutubeMusicPlayer.jsx +375 -0
- package/src/renderer/src/components/camera/CameraPreview.jsx +236 -0
- package/src/renderer/src/components/config/AiEngineSection.jsx +363 -0
- package/src/renderer/src/components/config/CameraSection.jsx +100 -0
- package/src/renderer/src/components/config/PermissionsSection.jsx +130 -0
- package/src/renderer/src/components/config/ShortcutSection.jsx +102 -0
- package/src/renderer/src/components/config/VoiceSection.jsx +110 -0
- package/src/renderer/src/components/config/index.js +5 -0
- package/src/renderer/src/components/core/Avatar.jsx +2197 -0
- package/src/renderer/src/components/core/ConfirmModal.jsx +80 -0
- package/src/renderer/src/components/core/DraggableHoloCard.jsx +125 -0
- package/src/renderer/src/components/core/FloatingMenu.jsx +272 -0
- package/src/renderer/src/components/core/HoloCard.jsx +64 -0
- package/src/renderer/src/components/core/InputBar.jsx +1346 -0
- package/src/renderer/src/components/core/ProcessPanel.jsx +216 -0
- package/src/renderer/src/components/core/ResponseArea.jsx +149 -0
- package/src/renderer/src/components/core/StatusIndicator.jsx +59 -0
- package/src/renderer/src/components/core/ThoughtNeuralFlow.jsx +168 -0
- package/src/renderer/src/components/core/ToolClustersDeck.jsx +210 -0
- package/src/renderer/src/components/core/native-skills.js +43 -0
- package/src/renderer/src/components/neural-core/EmotionalCortexLobe.jsx +359 -0
- package/src/renderer/src/components/neural-core/NodeDissectionModal.jsx +57 -0
- package/src/renderer/src/components/neural-core/ProceduralMatrixLobe.jsx +193 -0
- package/src/renderer/src/components/neural-core/ProceduralSkillModal.jsx +53 -0
- package/src/renderer/src/components/neural-core/ResetAiModal.jsx +198 -0
- package/src/renderer/src/components/neural-core/SynapticMemoryLobe.jsx +204 -0
- package/src/renderer/src/components/neural-core/traitMeta.js +102 -0
- package/src/renderer/src/components/subagent/AgentChatFeed.jsx +603 -0
- package/src/renderer/src/components/subagent/AgentInspector.jsx +224 -0
- package/src/renderer/src/components/subagent/AgentSidebar.jsx +216 -0
- package/src/renderer/src/components/subagent/SubagentIntercom.jsx +572 -0
- package/src/renderer/src/components/subagent/SubagentTopologyMap.jsx +423 -0
- package/src/renderer/src/contexts/ApprovalContext.jsx +425 -0
- package/src/renderer/src/contexts/ChatContext.jsx +61 -0
- package/src/renderer/src/contexts/YoutubeMusicContext.jsx +333 -0
- package/src/renderer/src/data/faqData.js +202 -0
- package/src/renderer/src/hooks/agent/executeAgentTool.js +895 -0
- package/src/renderer/src/hooks/agent/index.js +4 -0
- package/src/renderer/src/hooks/agent/useMarkMusic.js +75 -0
- package/src/renderer/src/hooks/agent/useMarkPlan.js +2739 -0
- package/src/renderer/src/hooks/agent/useMarkState.js +173 -0
- package/src/renderer/src/hooks/agent/useMarkYoutube.js +82 -0
- package/src/renderer/src/hooks/agent/useRelationalGrowth.js +201 -0
- package/src/renderer/src/hooks/telegram/useTelegramBot.js +63 -0
- package/src/renderer/src/hooks/useAwareness.js +289 -0
- package/src/renderer/src/hooks/useChatArchiver.js +52 -0
- package/src/renderer/src/hooks/useConfirm.jsx +68 -0
- package/src/renderer/src/hooks/useMarkAgent.js +454 -0
- package/src/renderer/src/hooks/useMemoryGroomer.js +152 -0
- package/src/renderer/src/hooks/useVAD.js +511 -0
- package/src/renderer/src/hooks/useWebSpeechWakeWord.js +122 -0
- package/src/renderer/src/main.jsx +12 -0
- package/src/renderer/src/pages/ChatStudio.jsx +922 -0
- package/src/renderer/src/pages/Configuration.jsx +360 -0
- package/src/renderer/src/pages/GoogleWorkspace.jsx +293 -0
- package/src/renderer/src/pages/Guidebook.jsx +917 -0
- package/src/renderer/src/pages/Knowledge.jsx +241 -0
- package/src/renderer/src/pages/MarkHome.jsx +491 -0
- package/src/renderer/src/pages/NeuralCore.jsx +786 -0
- package/src/renderer/src/pages/Plugins.jsx +630 -0
- package/src/renderer/src/pages/SkillEditor.jsx +380 -0
- package/src/renderer/src/pages/Skills.jsx +219 -0
- package/src/renderer/src/pages/Subagents.jsx +342 -0
- package/src/renderer/src/pages/TelegramBot.jsx +343 -0
- package/src/renderer/src/utils/chatContentParser.js +203 -0
- package/src/renderer/src/utils/toolClusters.js +201 -0
- package/src/server/bin/sha3_wasm_bg.wasm +0 -0
- package/src/server/config-manager.js +57 -0
- package/src/server/index.js +195 -0
- package/src/server/launcher.js +87 -0
- package/src/server/memory/db-store.js +758 -0
- package/src/server/memory/orama-store.js +177 -0
- package/src/server/memory/vector-engine.js +109 -0
- package/src/server/routes/ai.routes.js +280 -0
- package/src/server/routes/awareness.routes.js +62 -0
- package/src/server/routes/chat.routes.js +61 -0
- package/src/server/routes/config.routes.js +41 -0
- package/src/server/routes/index.js +35 -0
- package/src/server/routes/integrations.routes.js +116 -0
- package/src/server/routes/memory.routes.js +256 -0
- package/src/server/routes/plugins.routes.js +72 -0
- package/src/server/routes/skills.routes.js +139 -0
- package/src/server/routes/subagents.routes.js +70 -0
- package/src/server/routes/tasks.routes.js +175 -0
- package/src/server/routes/workspace.routes.js +127 -0
- package/src/server/services/ai/ai-utils.js +161 -0
- package/src/server/services/ai/openai-provider.js +757 -0
- package/src/server/services/ai/web-provider.js +729 -0
- package/src/server/services/ai-bridge.js +46 -0
- package/src/server/services/deepseek-web.js +956 -0
- package/src/server/services/gemini-web.js +170 -0
- package/src/server/services/notification-service.js +91 -0
- package/src/server/services/updater.js +125 -0
- package/src/server/tools/awareness-tracker.js +292 -0
- package/src/server/tools/group-tools.js +1164 -0
- package/src/server/tools/media-tools.js +244 -0
- package/src/server/tools/pc-agent.js +327 -0
- package/src/server/tools/screen-service.js +51 -0
- package/src/server/ws-hub.js +168 -0
- package/src/shared/parsers/mark-tag-parser.js +275 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { core_tools, core_tools_schema } from './core-tools'
|
|
2
|
+
import { webApi } from '../web-bridge.js'
|
|
3
|
+
|
|
4
|
+
let cachedGroupToolsFlat = {}
|
|
5
|
+
let cachedGroupToolsSchema = {}
|
|
6
|
+
let cachedGroupToolNames = []
|
|
7
|
+
|
|
8
|
+
// Inisialisasi cache secara background agar checkTools siap sedini mungkin
|
|
9
|
+
if (typeof window !== 'undefined') {
|
|
10
|
+
webApi
|
|
11
|
+
.getGroupTools()
|
|
12
|
+
.then((data) => {
|
|
13
|
+
if (data?.flat) cachedGroupToolsFlat = data.flat
|
|
14
|
+
if (data?.schema) cachedGroupToolsSchema = data.schema
|
|
15
|
+
if (data?.names) cachedGroupToolNames = data.names
|
|
16
|
+
})
|
|
17
|
+
.catch(() => {})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const checkTools = (toolName) => {
|
|
21
|
+
return (
|
|
22
|
+
!!core_tools[toolName] ||
|
|
23
|
+
!!cachedGroupToolsFlat[toolName] ||
|
|
24
|
+
toolName === 'read-tools' ||
|
|
25
|
+
(typeof toolName === 'string' && toolName.startsWith('plugin-'))
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Mengambil array skema tools OpenAPI lengkap untuk dikirimkan ke model.
|
|
31
|
+
* Menyaring tool groups yang relevan dengan query atau yang telah dimuat via 'read-tools' untuk menghemat context window.
|
|
32
|
+
* @param {string} _intentQuery Kata kunci / prompt user (disimpan untuk kompatibilitas filter)
|
|
33
|
+
* @param {Set<string>|Array<string>} loadedGroups Daftar nama group yang telah dimuat via read-tools
|
|
34
|
+
*/
|
|
35
|
+
export const getActiveToolsSchema = async (intentQuery = '', loadedGroups = []) => {
|
|
36
|
+
void intentQuery
|
|
37
|
+
const groupToolsData = await webApi.getGroupTools()
|
|
38
|
+
const groupToolsSchema = groupToolsData?.schema || cachedGroupToolsSchema || {}
|
|
39
|
+
const groupToolGroupNames = groupToolsData?.names || cachedGroupToolNames || []
|
|
40
|
+
if (groupToolsData?.flat) cachedGroupToolsFlat = groupToolsData.flat
|
|
41
|
+
|
|
42
|
+
const activeSchemas = [...core_tools_schema]
|
|
43
|
+
const seenToolNames = new Set(core_tools_schema.map((t) => t.function?.name))
|
|
44
|
+
const loadedSet = loadedGroups instanceof Set ? loadedGroups : new Set(loadedGroups || [])
|
|
45
|
+
|
|
46
|
+
for (const [groupKey, group] of Object.entries(groupToolsSchema)) {
|
|
47
|
+
// Hanya tambah schema group jika user explicit panggil read-tools("group_name")
|
|
48
|
+
if (loadedSet.has(groupKey) && Array.isArray(group.tools)) {
|
|
49
|
+
for (const t of group.tools) {
|
|
50
|
+
const name = t.function?.name
|
|
51
|
+
if (name && !seenToolNames.has(name)) {
|
|
52
|
+
seenToolNames.add(name)
|
|
53
|
+
activeSchemas.push(t)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Muat dynamic plugin actions jika group plugin aktif tersebut telah dimuat via read-tools
|
|
60
|
+
let activePluginNames = []
|
|
61
|
+
try {
|
|
62
|
+
if (typeof window !== 'undefined' && window.api && window.api.getPlugins) {
|
|
63
|
+
const plugins = await window.api.getPlugins()
|
|
64
|
+
if (Array.isArray(plugins)) {
|
|
65
|
+
const activePlugins = plugins.filter((p) => p.isEnabled !== false)
|
|
66
|
+
activePluginNames = activePlugins.map((p) => p.name)
|
|
67
|
+
|
|
68
|
+
for (const plugin of activePlugins) {
|
|
69
|
+
if (
|
|
70
|
+
(loadedSet.has(plugin.name) || loadedSet.has('custom_plugins')) &&
|
|
71
|
+
Array.isArray(plugin.actions)
|
|
72
|
+
) {
|
|
73
|
+
for (const act of plugin.actions) {
|
|
74
|
+
const pName = `plugin-${plugin.name}-${act.name}`
|
|
75
|
+
if (!seenToolNames.has(pName)) {
|
|
76
|
+
seenToolNames.add(pName)
|
|
77
|
+
activeSchemas.push({
|
|
78
|
+
type: 'function',
|
|
79
|
+
function: {
|
|
80
|
+
name: pName,
|
|
81
|
+
description: `[Plugin: ${plugin.name}] ${act.description || act.triggerHint || ''}`,
|
|
82
|
+
parameters: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
query: { type: 'string', description: 'Parameter aksi plugin' }
|
|
86
|
+
},
|
|
87
|
+
required: ['query'],
|
|
88
|
+
additionalProperties: false
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// ignore
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Perbarui parameter enum dan description pada read-tools agar mencakup nama plugin aktif
|
|
103
|
+
const readToolsIdx = activeSchemas.findIndex((s) => s.function?.name === 'read-tools')
|
|
104
|
+
if (readToolsIdx >= 0) {
|
|
105
|
+
const orig = activeSchemas[readToolsIdx]
|
|
106
|
+
const allGroups = [...groupToolGroupNames, ...activePluginNames]
|
|
107
|
+
activeSchemas[readToolsIdx] = {
|
|
108
|
+
...orig,
|
|
109
|
+
function: {
|
|
110
|
+
...orig.function,
|
|
111
|
+
parameters: {
|
|
112
|
+
...orig.function?.parameters,
|
|
113
|
+
properties: {
|
|
114
|
+
...orig.function?.parameters?.properties,
|
|
115
|
+
group_name: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
description: `Nama grup tool bawaan atau plugin aktif: ${allGroups.join(', ')}`,
|
|
118
|
+
enum: allGroups
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Pastikan seluruh schema tool memiliki parameter reason wajib
|
|
127
|
+
for (const t of activeSchemas) {
|
|
128
|
+
if (t.function?.parameters?.properties) {
|
|
129
|
+
if (!t.function.parameters.properties.reason) {
|
|
130
|
+
t.function.parameters.properties.reason = {
|
|
131
|
+
type: 'string',
|
|
132
|
+
description:
|
|
133
|
+
'Penjelasan ringkas dalam bahasa manusia mengenai alasan atau tujuan aksi ini (contoh: "Membuka tab Instagram di browser").'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (Array.isArray(t.function.parameters.required)) {
|
|
137
|
+
if (!t.function.parameters.required.includes('reason')) {
|
|
138
|
+
t.function.parameters.required.push('reason')
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
t.function.parameters.required = ['reason']
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return activeSchemas
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const getGroupTools = () => webApi.getGroupTools()
|
|
150
|
+
|
|
151
|
+
export { core_tools, core_tools_schema }
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { generateVector } from './vectorMemory'
|
|
2
|
+
import {
|
|
3
|
+
getAllSessions,
|
|
4
|
+
saveBatchChatTurns,
|
|
5
|
+
saveChatTurn
|
|
6
|
+
} from './db'
|
|
7
|
+
import { insertBatchTurnPairsToOrama, insertTurnPairToOrama } from './oramaStore'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Ekstraksi teks murni dari objek pesan (mendukung string biasa atau array multimodal)
|
|
11
|
+
*/
|
|
12
|
+
function cleanMessageContent(content) {
|
|
13
|
+
if (typeof content === 'string') return content.trim()
|
|
14
|
+
if (Array.isArray(content)) {
|
|
15
|
+
const textPart = content.find((c) => c.type === 'text')
|
|
16
|
+
return textPart?.text ? textPart.text.trim() : ''
|
|
17
|
+
}
|
|
18
|
+
if (content && typeof content === 'object' && content.text) {
|
|
19
|
+
return String(content.text).trim()
|
|
20
|
+
}
|
|
21
|
+
return ''
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeTimestamp(ts) {
|
|
25
|
+
if (typeof ts === 'number' && !isNaN(ts)) return ts
|
|
26
|
+
if (typeof ts === 'string') {
|
|
27
|
+
const parsed = Date.parse(ts)
|
|
28
|
+
if (!isNaN(parsed)) return parsed
|
|
29
|
+
const num = Number(ts)
|
|
30
|
+
if (!isNaN(num)) return num
|
|
31
|
+
}
|
|
32
|
+
return Date.now()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Memetakan riwayat pesan satu sesi menjadi array Turn Pairs (Tanya - Jawab)
|
|
37
|
+
*/
|
|
38
|
+
export function extractTurnPairsFromSession(sessionData, sessionId, sessionTitle) {
|
|
39
|
+
const pairs = []
|
|
40
|
+
if (!Array.isArray(sessionData) || sessionData.length === 0) return pairs
|
|
41
|
+
|
|
42
|
+
const cleanTitle = sessionTitle || `Session ${sessionId}`
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < sessionData.length; i++) {
|
|
45
|
+
const msg = sessionData[i]
|
|
46
|
+
if (!msg) continue
|
|
47
|
+
|
|
48
|
+
// Abaikan pesan intermediate / thinking / searching
|
|
49
|
+
if (msg.isThinking || msg.isSearching || msg.isSummarizing) continue
|
|
50
|
+
|
|
51
|
+
if (msg.role === 'user') {
|
|
52
|
+
const userText = cleanMessageContent(msg.content)
|
|
53
|
+
if (!userText || userText.length < 2) continue
|
|
54
|
+
|
|
55
|
+
// Cari balasan AI yang sesuai setelah pesan user ini
|
|
56
|
+
let aiText = ''
|
|
57
|
+
let aiMsgTimestamp = null
|
|
58
|
+
|
|
59
|
+
for (let j = i + 1; j < sessionData.length; j++) {
|
|
60
|
+
const nextMsg = sessionData[j]
|
|
61
|
+
if (!nextMsg) continue
|
|
62
|
+
if (nextMsg.role === 'user') break // Berhenti jika sudah bertemu pesan user berikutnya
|
|
63
|
+
if (nextMsg.role === 'ai' && !nextMsg.isThinking && !nextMsg.isSearching && !nextMsg.isSummarizing) {
|
|
64
|
+
aiText = cleanMessageContent(nextMsg.content)
|
|
65
|
+
aiMsgTimestamp = nextMsg.timestamp
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Pastikan ada konten yang bermakna
|
|
71
|
+
if (userText.length > 2 || aiText.length > 2) {
|
|
72
|
+
const rawTs = msg.timestamp || aiMsgTimestamp || Date.now()
|
|
73
|
+
const timestamp = normalizeTimestamp(rawTs)
|
|
74
|
+
const pairId = `turn-${sessionId}-${timestamp}-${i}`
|
|
75
|
+
const combinedText = `[User]: ${userText}\n[Mark]: ${aiText || '(Menjalankan instruksi)'}`
|
|
76
|
+
|
|
77
|
+
pairs.push({
|
|
78
|
+
pairId,
|
|
79
|
+
sessionId: Number(sessionId),
|
|
80
|
+
sessionTitle: cleanTitle,
|
|
81
|
+
userText,
|
|
82
|
+
aiText,
|
|
83
|
+
combinedText,
|
|
84
|
+
timestamp
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return pairs
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Migrasi bertahap data chat lama ke dalam format Turn-Pair Vektor (Smart Incremental Queue)
|
|
95
|
+
*/
|
|
96
|
+
export async function migrateOldSessionsToTurns(onProgress) {
|
|
97
|
+
try {
|
|
98
|
+
const sessions = await getAllSessions()
|
|
99
|
+
if (!Array.isArray(sessions) || sessions.length === 0) return 0
|
|
100
|
+
|
|
101
|
+
// Kumpulkan seluruh turn pair dari semua sesi
|
|
102
|
+
const allPairs = []
|
|
103
|
+
for (const session of sessions) {
|
|
104
|
+
const turns = extractTurnPairsFromSession(session.data, session.id, session.title)
|
|
105
|
+
allPairs.push(...turns)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const totalCount = allPairs.length
|
|
109
|
+
if (totalCount === 0) return 0
|
|
110
|
+
|
|
111
|
+
console.log(`[TurnMigrator] Memulai migrasi ${totalCount} turn pairs lama...`)
|
|
112
|
+
if (typeof onProgress === 'function') {
|
|
113
|
+
onProgress(0, totalCount)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const BATCH_SIZE = 5
|
|
117
|
+
let processedCount = 0
|
|
118
|
+
|
|
119
|
+
for (let i = 0; i < allPairs.length; i += BATCH_SIZE) {
|
|
120
|
+
const batch = allPairs.slice(i, i + BATCH_SIZE)
|
|
121
|
+
const validTurns = []
|
|
122
|
+
|
|
123
|
+
for (const turn of batch) {
|
|
124
|
+
try {
|
|
125
|
+
const vector = await generateVector(turn.combinedText)
|
|
126
|
+
if (vector && vector.length === 384) {
|
|
127
|
+
validTurns.push({
|
|
128
|
+
...turn,
|
|
129
|
+
vector
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.warn(`[TurnMigrator] Gagal generate vector untuk turn ${turn.pairId}:`, err)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (validTurns.length > 0) {
|
|
138
|
+
await saveBatchChatTurns(validTurns)
|
|
139
|
+
await insertBatchTurnPairsToOrama(validTurns)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
processedCount += batch.length
|
|
143
|
+
if (typeof onProgress === 'function') {
|
|
144
|
+
onProgress(Math.min(processedCount, totalCount), totalCount)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Jeda istirahat CPU (60ms) agar event loop tetap responsif
|
|
148
|
+
await new Promise((resolve) => setTimeout(resolve, 60))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log(`[TurnMigrator] Sukses menyelesaikan migrasi ${totalCount} turn pairs!`)
|
|
152
|
+
return totalCount
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error('[TurnMigrator] Error saat migrasi sessions to turns:', error)
|
|
155
|
+
return 0
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Pengindeksan instan 1 pasang percakapan secara real-time saat chat selesai
|
|
161
|
+
*/
|
|
162
|
+
export async function indexSingleTurn(sessionId, sessionTitle, userMsg, aiMsg) {
|
|
163
|
+
try {
|
|
164
|
+
const userText = cleanMessageContent(userMsg?.content || userMsg)
|
|
165
|
+
const aiText = cleanMessageContent(aiMsg?.content || aiMsg)
|
|
166
|
+
|
|
167
|
+
if (!userText && !aiText) return null
|
|
168
|
+
if (userText.length < 2 && aiText.length < 2) return null
|
|
169
|
+
|
|
170
|
+
const timestamp = Date.now()
|
|
171
|
+
const pairId = `turn-${sessionId}-${timestamp}`
|
|
172
|
+
const combinedText = `[User]: ${userText}\n[Mark]: ${aiText || '(Menjalankan instruksi)'}`
|
|
173
|
+
|
|
174
|
+
const vector = await generateVector(combinedText)
|
|
175
|
+
if (!vector || vector.length !== 384) {
|
|
176
|
+
console.warn('[TurnMigrator] Gagal generate vector untuk turn baru')
|
|
177
|
+
return null
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const turnData = {
|
|
181
|
+
pairId,
|
|
182
|
+
sessionId: Number(sessionId) || 1,
|
|
183
|
+
sessionTitle: sessionTitle || 'Main Thread',
|
|
184
|
+
userText,
|
|
185
|
+
aiText,
|
|
186
|
+
combinedText,
|
|
187
|
+
timestamp,
|
|
188
|
+
vector
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await saveChatTurn(turnData)
|
|
192
|
+
await insertTurnPairToOrama(turnData)
|
|
193
|
+
|
|
194
|
+
console.log(`[TurnMigrator] Realtime indexed turn pair: ${pairId}`)
|
|
195
|
+
return turnData
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.error('[TurnMigrator] Error indexSingleTurn:', err)
|
|
198
|
+
return null
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
searchArchives,
|
|
3
|
+
searchDocuments,
|
|
4
|
+
searchMemoriesInOrama,
|
|
5
|
+
searchTurnPairsInOrama
|
|
6
|
+
} from './oramaStore'
|
|
7
|
+
import { getAllMemory } from './db'
|
|
8
|
+
import { API_BASE } from './web-bridge'
|
|
9
|
+
|
|
10
|
+
export const getExtractor = async () => {
|
|
11
|
+
return true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const generateVector = async (text) => {
|
|
15
|
+
if (!text || typeof text !== 'string' || !text.trim()) {
|
|
16
|
+
return null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const res = await fetch(`${API_BASE}/api/vector`, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: { 'Content-Type': 'application/json' },
|
|
23
|
+
body: JSON.stringify({ text })
|
|
24
|
+
})
|
|
25
|
+
const json = await res.json()
|
|
26
|
+
return json.vector || null
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Gagal generate vector via server API:', error)
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// SEARCH: Rumus matematika buat ngukur kemiripan (0 sampai 1)
|
|
34
|
+
export const cosineSimilarity = (vecA, vecB) => {
|
|
35
|
+
if (!Array.isArray(vecA) || !Array.isArray(vecB) || vecA.length === 0 || vecB.length === 0) {
|
|
36
|
+
return 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return vecA.reduce((sum, a, i) => sum + a * vecB[i], 0)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const getRelevantMemory = async (userInput, memoryList) => {
|
|
43
|
+
let list = memoryList
|
|
44
|
+
if (!Array.isArray(list)) {
|
|
45
|
+
try {
|
|
46
|
+
list = await getAllMemory()
|
|
47
|
+
} catch (e) {
|
|
48
|
+
list = []
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!Array.isArray(list)) {
|
|
52
|
+
list = []
|
|
53
|
+
}
|
|
54
|
+
const coreMemories = list
|
|
55
|
+
.filter((m) => m && typeof m === 'object' && (m.type === 'profile' || m.type === 'preference'))
|
|
56
|
+
.map(({ vector, ...rest }) => rest)
|
|
57
|
+
|
|
58
|
+
return coreMemories
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const searchExtendedMemory = async (query, threshold = 0.5, limit = 5) => {
|
|
62
|
+
const queryVector = await generateVector(query)
|
|
63
|
+
if (!queryVector) return { memories: [], chatTurns: [] }
|
|
64
|
+
|
|
65
|
+
const memories = await searchMemoriesInOrama(
|
|
66
|
+
query,
|
|
67
|
+
queryVector,
|
|
68
|
+
limit,
|
|
69
|
+
['notes', 'learn'],
|
|
70
|
+
threshold
|
|
71
|
+
)
|
|
72
|
+
const chatTurns = await searchTurnPairsInOrama(query, queryVector, limit, threshold)
|
|
73
|
+
|
|
74
|
+
return { memories, chatTurns }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const executeMemorySearch = async (rawQuery) => {
|
|
78
|
+
const parts = (rawQuery || '').split('||')
|
|
79
|
+
const searchKeyword = parts[0]?.trim() || ''
|
|
80
|
+
const customThreshold =
|
|
81
|
+
parts[1] && !isNaN(parseFloat(parts[1])) ? parseFloat(parts[1].trim()) : 0.5
|
|
82
|
+
const customLimit = parts[2] && !isNaN(parseInt(parts[2], 10)) ? parseInt(parts[2].trim(), 10) : 5
|
|
83
|
+
|
|
84
|
+
const { memories = [], chatTurns = [] } = await searchExtendedMemory(
|
|
85
|
+
searchKeyword,
|
|
86
|
+
customThreshold,
|
|
87
|
+
customLimit
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
const formattedMemories =
|
|
91
|
+
memories.length > 0
|
|
92
|
+
? memories
|
|
93
|
+
.map(
|
|
94
|
+
(m) =>
|
|
95
|
+
`- [${m.type.toUpperCase()}] (ID:${m.id}, Score:${(m.score || 0).toFixed(2)}) ${m.memory}`
|
|
96
|
+
)
|
|
97
|
+
.join('\n')
|
|
98
|
+
: ''
|
|
99
|
+
|
|
100
|
+
const formattedTurns =
|
|
101
|
+
chatTurns.length > 0
|
|
102
|
+
? chatTurns
|
|
103
|
+
.map(
|
|
104
|
+
(t) =>
|
|
105
|
+
`--- [RIWAYAT CHAT: "${t.sessionTitle || 'Session'}" | Score:${(t.score || 0).toFixed(2)}] ---\n${t.combinedText}`
|
|
106
|
+
)
|
|
107
|
+
.join('\n\n')
|
|
108
|
+
: ''
|
|
109
|
+
|
|
110
|
+
let sections = []
|
|
111
|
+
if (formattedMemories) {
|
|
112
|
+
sections.push(`[CATATAN & MEMORI PENGGUNA]\n${formattedMemories}`)
|
|
113
|
+
}
|
|
114
|
+
if (formattedTurns) {
|
|
115
|
+
sections.push(`[RIWAYAT PERCAKAPAN ASLI (TURN PAIRS)]\n${formattedTurns}`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (sections.length > 0) {
|
|
119
|
+
return `[MEMORY SEARCH RESULTS (Threshold: ${customThreshold}, Limit: ${customLimit})]\n\n${sections.join('\n\n')}`
|
|
120
|
+
}
|
|
121
|
+
return `[MEMORY SEARCH RESULTS (Threshold: ${customThreshold}, Limit: ${customLimit})]\nTidak ditemukan memori atau percakapan yang relevan dengan kata kunci "${searchKeyword}".`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const getUnifiedContext = async (userInput, memoryList) => {
|
|
125
|
+
const memories = await getRelevantMemory(userInput, memoryList)
|
|
126
|
+
|
|
127
|
+
const output = await generateVector(userInput)
|
|
128
|
+
if (!output) return { memories, archives: [], documents: [], turnPairs: [] }
|
|
129
|
+
const userVector = Array.from(output)
|
|
130
|
+
|
|
131
|
+
const archives = await searchArchives(userVector, 3)
|
|
132
|
+
const documents = await searchDocuments(userInput, userVector, 5)
|
|
133
|
+
const turnPairs = await searchTurnPairsInOrama(userInput, userVector, 3, 0.3)
|
|
134
|
+
|
|
135
|
+
return { memories, archives, documents, turnPairs }
|
|
136
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Wake Word Engine Pattern Matcher & Command Extractor
|
|
2
|
+
// Mendukung pola default "Hey Mark", "Halo Mark", "Mark" serta kata pemicu kustom pengguna
|
|
3
|
+
|
|
4
|
+
// Pola kata Mark dan variasinya dalam pengenalan suara bahasa Indonesia Edge STT
|
|
5
|
+
const DEFAULT_MARK_NAMES = '(?:mark|marc|mak|mart|marg|makh|smart|marck)'
|
|
6
|
+
const GREETINGS = '(?:hey|hei|halo|hello|helo|hai|hi|woi|oi|bro)'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Membuat Regex matcher dinamis dari kata pemicu default + custom wake words
|
|
10
|
+
*/
|
|
11
|
+
export function getWakeWordRegex(customWakeWords = '') {
|
|
12
|
+
const parts = [DEFAULT_MARK_NAMES]
|
|
13
|
+
|
|
14
|
+
if (customWakeWords) {
|
|
15
|
+
const rawList = Array.isArray(customWakeWords)
|
|
16
|
+
? customWakeWords
|
|
17
|
+
: String(customWakeWords).split(/[,|\n]+/)
|
|
18
|
+
|
|
19
|
+
for (const item of rawList) {
|
|
20
|
+
const clean = item.trim()
|
|
21
|
+
if (clean) {
|
|
22
|
+
const escaped = clean.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
23
|
+
parts.push(escaped)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const combinedNames = `(?:${parts.join('|')})`
|
|
29
|
+
return new RegExp(`(?:\\b${GREETINGS}[,\\s]+)?\\b${combinedNames}\\b`, 'i')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Mendeteksi keberadaan wake word dalam teks transkrip dan memisahkan perintah
|
|
34
|
+
*/
|
|
35
|
+
export function detectWakeWord(transcript, customWakeWords = '') {
|
|
36
|
+
if (!transcript || typeof transcript !== 'string') {
|
|
37
|
+
return { detected: false, command: '', wakePhrase: '' }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const regex = getWakeWordRegex(customWakeWords)
|
|
41
|
+
const match = transcript.match(regex)
|
|
42
|
+
|
|
43
|
+
if (!match) {
|
|
44
|
+
return { detected: false, command: '', wakePhrase: '' }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const wakePhrase = match[0]
|
|
48
|
+
const matchIndex = match.index
|
|
49
|
+
const matchLength = match[0].length
|
|
50
|
+
|
|
51
|
+
// Ambil teks setelah kata pemicu (jika user langsung mengucapkan perintah dalam 1 tarikan napas)
|
|
52
|
+
const afterWakeText = transcript.substring(matchIndex + matchLength).trim()
|
|
53
|
+
|
|
54
|
+
// Bersihkan tanda baca awal seperti koma, titik dua, tanda hubung
|
|
55
|
+
const cleanCommand = cleanSpokenCommand(afterWakeText, customWakeWords)
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
detected: true,
|
|
59
|
+
wakePhrase,
|
|
60
|
+
command: cleanCommand
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Membersihkan awalan sapaan / nama pemicu dari perintah yang diucapkan
|
|
66
|
+
*/
|
|
67
|
+
export function cleanSpokenCommand(text, customWakeWords = '') {
|
|
68
|
+
if (!text || typeof text !== 'string') return ''
|
|
69
|
+
|
|
70
|
+
const parts = [DEFAULT_MARK_NAMES]
|
|
71
|
+
|
|
72
|
+
if (customWakeWords) {
|
|
73
|
+
const rawList = Array.isArray(customWakeWords)
|
|
74
|
+
? customWakeWords
|
|
75
|
+
: String(customWakeWords).split(/[,|\n]+/)
|
|
76
|
+
|
|
77
|
+
for (const item of rawList) {
|
|
78
|
+
const clean = item.trim()
|
|
79
|
+
if (clean) {
|
|
80
|
+
const escaped = clean.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
81
|
+
parts.push(escaped)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const combinedNames = `(?:${parts.join('|')})`
|
|
87
|
+
const prefixRegex = new RegExp(`^\\s*(?:${GREETINGS}[,\\s]+)?${combinedNames}\\b`, 'gi')
|
|
88
|
+
|
|
89
|
+
let stripped = text
|
|
90
|
+
.replace(/^[,.:;!\-–—\s]+/, '')
|
|
91
|
+
.replace(prefixRegex, '')
|
|
92
|
+
.replace(/^[,.:;!\-–—\s]+/, '')
|
|
93
|
+
.trim()
|
|
94
|
+
|
|
95
|
+
// Jika setelah dibersihkan hanya berisi tanda baca atau simbol tanpa huruf/angka, kosongkan
|
|
96
|
+
if (/^[^a-zA-Z0-9]+$/.test(stripped)) {
|
|
97
|
+
return ''
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return stripped
|
|
101
|
+
}
|