@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.
Files changed (227) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +209 -0
  3. package/bin/mark.js +27 -0
  4. package/out/renderer/assets/icon-DHo2CYph.svg +1 -0
  5. package/out/renderer/assets/index-G2fADWO-.js +4768 -0
  6. package/out/renderer/assets/index-f3mVse3c.css +1 -0
  7. package/out/renderer/assets/music-cover-CG4vi3kZ.png +0 -0
  8. package/out/renderer/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
  9. package/out/renderer/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
  10. package/out/renderer/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
  11. package/out/renderer/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
  12. package/out/renderer/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
  13. package/out/renderer/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
  14. package/out/renderer/assets/poppins-devanagari-700-normal-O-jipLrW.woff2 +0 -0
  15. package/out/renderer/assets/poppins-devanagari-700-normal-fHs-vx92.woff +0 -0
  16. package/out/renderer/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
  17. package/out/renderer/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
  18. package/out/renderer/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
  19. package/out/renderer/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
  20. package/out/renderer/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
  21. package/out/renderer/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
  22. package/out/renderer/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
  23. package/out/renderer/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
  24. package/out/renderer/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
  25. package/out/renderer/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
  26. package/out/renderer/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
  27. package/out/renderer/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
  28. package/out/renderer/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
  29. package/out/renderer/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
  30. package/out/renderer/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff +0 -0
  31. package/out/renderer/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 +0 -0
  32. package/out/renderer/favicon.ico +0 -0
  33. package/out/renderer/icon-256.png +0 -0
  34. package/out/renderer/icon.svg +1 -0
  35. package/out/renderer/index.html +25 -0
  36. package/out/renderer/manifest.json +25 -0
  37. package/out/renderer/robot-simulator.html +1640 -0
  38. package/package.json +106 -0
  39. package/src/cli/index.js +332 -0
  40. package/src/cli/monitor.js +413 -0
  41. package/src/cli/theme.js +347 -0
  42. package/src/launcher/MarkTray.cs +227 -0
  43. package/src/main/browser-agent.js +1132 -0
  44. package/src/main/git-service.js +90 -0
  45. package/src/main/google/google-calendar.js +69 -0
  46. package/src/main/google/google-drive.js +263 -0
  47. package/src/main/google/google-gmail.js +131 -0
  48. package/src/main/google/google-service.js +241 -0
  49. package/src/main/node-tools.js +23 -0
  50. package/src/main/pc-agent-scripts/mouse-locker.ps1 +76 -0
  51. package/src/main/pc-agent-scripts/ocr-region.ps1 +185 -0
  52. package/src/main/pc-agent-scripts/pc-daemon.ps1 +726 -0
  53. package/src/main/pc-agent-scripts/pc-overlay.ps1 +253 -0
  54. package/src/main/pc-agent-scripts/pick-folder.ps1 +33 -0
  55. package/src/main/pc-agent-scripts/read-ui.ps1 +181 -0
  56. package/src/main/pc-agent-scripts/take-screenshot.ps1 +24 -0
  57. package/src/main/pc-agent-scripts/win-action.ps1 +380 -0
  58. package/src/main/plugins/plugin-loader.js +306 -0
  59. package/src/main/skills/skill-manager.js +265 -0
  60. package/src/main/syntax-validator.js +455 -0
  61. package/src/main/task-daemon.js +117 -0
  62. package/src/main/telegram/telegram-service.js +835 -0
  63. package/src/main/tools/browser-tools.js +516 -0
  64. package/src/main/tools/file-tools.js +1126 -0
  65. package/src/main/tools/git-tools.js +79 -0
  66. package/src/main/tools/google-tools.js +566 -0
  67. package/src/main/tools/index.js +36 -0
  68. package/src/main/tools/media-tools.js +42 -0
  69. package/src/main/tools/system-tools.js +295 -0
  70. package/src/main/tools/task-tools.js +73 -0
  71. package/src/main/tools/telegram-tools.js +101 -0
  72. package/src/renderer/index.html +24 -0
  73. package/src/renderer/public/favicon.ico +0 -0
  74. package/src/renderer/public/icon-256.png +0 -0
  75. package/src/renderer/public/icon.svg +1 -0
  76. package/src/renderer/public/manifest.json +25 -0
  77. package/src/renderer/public/robot-simulator.html +1640 -0
  78. package/src/renderer/src/App.jsx +304 -0
  79. package/src/renderer/src/api/ai/awareness.js +294 -0
  80. package/src/renderer/src/api/ai/chatSummarizer.js +79 -0
  81. package/src/renderer/src/api/ai/contextCompactor.js +129 -0
  82. package/src/renderer/src/api/ai/contextManager.js +1130 -0
  83. package/src/renderer/src/api/ai/core.js +167 -0
  84. package/src/renderer/src/api/ai/index.js +4 -0
  85. package/src/renderer/src/api/ai/memoryGroomer.js +101 -0
  86. package/src/renderer/src/api/ai/persona.js +115 -0
  87. package/src/renderer/src/api/ai/planning.js +317 -0
  88. package/src/renderer/src/api/ai/relationship.js +177 -0
  89. package/src/renderer/src/api/ai/skillSynthesizer.js +168 -0
  90. package/src/renderer/src/api/ai/tools.js +167 -0
  91. package/src/renderer/src/api/ai/utils.js +359 -0
  92. package/src/renderer/src/api/db.js +1013 -0
  93. package/src/renderer/src/api/oramaStore.js +592 -0
  94. package/src/renderer/src/api/ragPipeline.js +87 -0
  95. package/src/renderer/src/api/subagent/subagentExecutor.js +780 -0
  96. package/src/renderer/src/api/subagent/subagentPrompt.js +44 -0
  97. package/src/renderer/src/api/subagent/subagentStore.js +173 -0
  98. package/src/renderer/src/api/taskExecutor.js +18 -0
  99. package/src/renderer/src/api/taskStore.js +315 -0
  100. package/src/renderer/src/api/tools/core-tools.js +707 -0
  101. package/src/renderer/src/api/tools/index.js +151 -0
  102. package/src/renderer/src/api/turnPairMigrator.js +200 -0
  103. package/src/renderer/src/api/vectorMemory.js +136 -0
  104. package/src/renderer/src/api/wakeWord.js +101 -0
  105. package/src/renderer/src/api/web-bridge.js +885 -0
  106. package/src/renderer/src/api/webSpeech.js +178 -0
  107. package/src/renderer/src/api/workspaceRag.js +76 -0
  108. package/src/renderer/src/assets/electron.svg +10 -0
  109. package/src/renderer/src/assets/icon.svg +1 -0
  110. package/src/renderer/src/assets/main.css +589 -0
  111. package/src/renderer/src/assets/music-cover.png +0 -0
  112. package/src/renderer/src/assets/wavy-lines.svg +25 -0
  113. package/src/renderer/src/components/Chat/ApprovalBubble.jsx +127 -0
  114. package/src/renderer/src/components/Chat/CodeBlock.jsx +83 -0
  115. package/src/renderer/src/components/Chat/DurableTaskBubble.jsx +455 -0
  116. package/src/renderer/src/components/Chat/FileDiffModal.jsx +328 -0
  117. package/src/renderer/src/components/Chat/GeminiWidgets.jsx +82 -0
  118. package/src/renderer/src/components/Chat/MemoryFooterBubble.jsx +34 -0
  119. package/src/renderer/src/components/Chat/MessageBubble.jsx +459 -0
  120. package/src/renderer/src/components/Chat/PluginExecutionBubble.jsx +41 -0
  121. package/src/renderer/src/components/Chat/ThinkingBubble.jsx +316 -0
  122. package/src/renderer/src/components/Chat/YoutubeSearchBubble.jsx +43 -0
  123. package/src/renderer/src/components/Chat/YoutubeSummaryBubble.jsx +40 -0
  124. package/src/renderer/src/components/Chat/index.js +11 -0
  125. package/src/renderer/src/components/ChatList.jsx +434 -0
  126. package/src/renderer/src/components/GlobalCameraManager.jsx +67 -0
  127. package/src/renderer/src/components/Versions.jsx +15 -0
  128. package/src/renderer/src/components/YoutubeMusicPlayer.jsx +375 -0
  129. package/src/renderer/src/components/camera/CameraPreview.jsx +236 -0
  130. package/src/renderer/src/components/config/AiEngineSection.jsx +363 -0
  131. package/src/renderer/src/components/config/CameraSection.jsx +100 -0
  132. package/src/renderer/src/components/config/PermissionsSection.jsx +130 -0
  133. package/src/renderer/src/components/config/ShortcutSection.jsx +102 -0
  134. package/src/renderer/src/components/config/VoiceSection.jsx +110 -0
  135. package/src/renderer/src/components/config/index.js +5 -0
  136. package/src/renderer/src/components/core/Avatar.jsx +2197 -0
  137. package/src/renderer/src/components/core/ConfirmModal.jsx +80 -0
  138. package/src/renderer/src/components/core/DraggableHoloCard.jsx +125 -0
  139. package/src/renderer/src/components/core/FloatingMenu.jsx +272 -0
  140. package/src/renderer/src/components/core/HoloCard.jsx +64 -0
  141. package/src/renderer/src/components/core/InputBar.jsx +1346 -0
  142. package/src/renderer/src/components/core/ProcessPanel.jsx +216 -0
  143. package/src/renderer/src/components/core/ResponseArea.jsx +149 -0
  144. package/src/renderer/src/components/core/StatusIndicator.jsx +59 -0
  145. package/src/renderer/src/components/core/ThoughtNeuralFlow.jsx +168 -0
  146. package/src/renderer/src/components/core/ToolClustersDeck.jsx +210 -0
  147. package/src/renderer/src/components/core/native-skills.js +43 -0
  148. package/src/renderer/src/components/neural-core/EmotionalCortexLobe.jsx +359 -0
  149. package/src/renderer/src/components/neural-core/NodeDissectionModal.jsx +57 -0
  150. package/src/renderer/src/components/neural-core/ProceduralMatrixLobe.jsx +193 -0
  151. package/src/renderer/src/components/neural-core/ProceduralSkillModal.jsx +53 -0
  152. package/src/renderer/src/components/neural-core/ResetAiModal.jsx +198 -0
  153. package/src/renderer/src/components/neural-core/SynapticMemoryLobe.jsx +204 -0
  154. package/src/renderer/src/components/neural-core/traitMeta.js +102 -0
  155. package/src/renderer/src/components/subagent/AgentChatFeed.jsx +603 -0
  156. package/src/renderer/src/components/subagent/AgentInspector.jsx +224 -0
  157. package/src/renderer/src/components/subagent/AgentSidebar.jsx +216 -0
  158. package/src/renderer/src/components/subagent/SubagentIntercom.jsx +572 -0
  159. package/src/renderer/src/components/subagent/SubagentTopologyMap.jsx +423 -0
  160. package/src/renderer/src/contexts/ApprovalContext.jsx +425 -0
  161. package/src/renderer/src/contexts/ChatContext.jsx +61 -0
  162. package/src/renderer/src/contexts/YoutubeMusicContext.jsx +333 -0
  163. package/src/renderer/src/data/faqData.js +202 -0
  164. package/src/renderer/src/hooks/agent/executeAgentTool.js +895 -0
  165. package/src/renderer/src/hooks/agent/index.js +4 -0
  166. package/src/renderer/src/hooks/agent/useMarkMusic.js +75 -0
  167. package/src/renderer/src/hooks/agent/useMarkPlan.js +2739 -0
  168. package/src/renderer/src/hooks/agent/useMarkState.js +173 -0
  169. package/src/renderer/src/hooks/agent/useMarkYoutube.js +82 -0
  170. package/src/renderer/src/hooks/agent/useRelationalGrowth.js +201 -0
  171. package/src/renderer/src/hooks/telegram/useTelegramBot.js +63 -0
  172. package/src/renderer/src/hooks/useAwareness.js +289 -0
  173. package/src/renderer/src/hooks/useChatArchiver.js +52 -0
  174. package/src/renderer/src/hooks/useConfirm.jsx +68 -0
  175. package/src/renderer/src/hooks/useMarkAgent.js +454 -0
  176. package/src/renderer/src/hooks/useMemoryGroomer.js +152 -0
  177. package/src/renderer/src/hooks/useVAD.js +511 -0
  178. package/src/renderer/src/hooks/useWebSpeechWakeWord.js +122 -0
  179. package/src/renderer/src/main.jsx +12 -0
  180. package/src/renderer/src/pages/ChatStudio.jsx +922 -0
  181. package/src/renderer/src/pages/Configuration.jsx +360 -0
  182. package/src/renderer/src/pages/GoogleWorkspace.jsx +293 -0
  183. package/src/renderer/src/pages/Guidebook.jsx +917 -0
  184. package/src/renderer/src/pages/Knowledge.jsx +241 -0
  185. package/src/renderer/src/pages/MarkHome.jsx +491 -0
  186. package/src/renderer/src/pages/NeuralCore.jsx +786 -0
  187. package/src/renderer/src/pages/Plugins.jsx +630 -0
  188. package/src/renderer/src/pages/SkillEditor.jsx +380 -0
  189. package/src/renderer/src/pages/Skills.jsx +219 -0
  190. package/src/renderer/src/pages/Subagents.jsx +342 -0
  191. package/src/renderer/src/pages/TelegramBot.jsx +343 -0
  192. package/src/renderer/src/utils/chatContentParser.js +203 -0
  193. package/src/renderer/src/utils/toolClusters.js +201 -0
  194. package/src/server/bin/sha3_wasm_bg.wasm +0 -0
  195. package/src/server/config-manager.js +57 -0
  196. package/src/server/index.js +195 -0
  197. package/src/server/launcher.js +87 -0
  198. package/src/server/memory/db-store.js +758 -0
  199. package/src/server/memory/orama-store.js +177 -0
  200. package/src/server/memory/vector-engine.js +109 -0
  201. package/src/server/routes/ai.routes.js +280 -0
  202. package/src/server/routes/awareness.routes.js +62 -0
  203. package/src/server/routes/chat.routes.js +61 -0
  204. package/src/server/routes/config.routes.js +41 -0
  205. package/src/server/routes/index.js +35 -0
  206. package/src/server/routes/integrations.routes.js +116 -0
  207. package/src/server/routes/memory.routes.js +256 -0
  208. package/src/server/routes/plugins.routes.js +72 -0
  209. package/src/server/routes/skills.routes.js +139 -0
  210. package/src/server/routes/subagents.routes.js +70 -0
  211. package/src/server/routes/tasks.routes.js +175 -0
  212. package/src/server/routes/workspace.routes.js +127 -0
  213. package/src/server/services/ai/ai-utils.js +161 -0
  214. package/src/server/services/ai/openai-provider.js +757 -0
  215. package/src/server/services/ai/web-provider.js +729 -0
  216. package/src/server/services/ai-bridge.js +46 -0
  217. package/src/server/services/deepseek-web.js +956 -0
  218. package/src/server/services/gemini-web.js +170 -0
  219. package/src/server/services/notification-service.js +91 -0
  220. package/src/server/services/updater.js +125 -0
  221. package/src/server/tools/awareness-tracker.js +292 -0
  222. package/src/server/tools/group-tools.js +1164 -0
  223. package/src/server/tools/media-tools.js +244 -0
  224. package/src/server/tools/pc-agent.js +327 -0
  225. package/src/server/tools/screen-service.js +51 -0
  226. package/src/server/ws-hub.js +168 -0
  227. 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
+ }