@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,173 @@
1
+ import { useState, useEffect, useRef } from 'react'
2
+ import { getAllConfig, saveMainThread, getMainThread } from '../../api/db'
3
+
4
+ export const useMarkState = () => {
5
+ const [chatData, setChatData] = useState([])
6
+ const [config, setConfig] = useState([])
7
+ const [message, setMessage] = useState('')
8
+ const [isLoading, setIsLoading] = useState(false)
9
+ const [isAgentBusy, setIsAgentBusy] = useState(false)
10
+ const [runningSessionIds, setRunningSessionIds] = useState([])
11
+ const runningSessionId = runningSessionIds[0] || null
12
+
13
+ const addRunningSessionId = (id) => {
14
+ const num = Number(id)
15
+ setRunningSessionIds((prev) => (prev.map(Number).includes(num) ? prev : [...prev, num]))
16
+ }
17
+
18
+ const removeRunningSessionId = (id) => {
19
+ const num = Number(id)
20
+ setRunningSessionIds((prev) => prev.filter((x) => Number(x) !== num))
21
+ }
22
+
23
+ const setRunningSessionId = (id) => {
24
+ if (id === null || id === undefined) {
25
+ setRunningSessionIds([])
26
+ } else {
27
+ addRunningSessionId(id)
28
+ }
29
+ }
30
+ const [isSpeak, setIsSpeak] = useState(false)
31
+ const [orbStatus, setOrbStatus] = useState('idle')
32
+ const [currentResponse, setCurrentResponse] = useState(null)
33
+ const [notifications, setNotifications] = useState([])
34
+ const [activeProcesses, setActiveProcesses] = useState([])
35
+ const [inputSource, setInputSource] = useState('pc')
36
+ const [activeTopic, setActiveTopic] = useState(null)
37
+ const [currentActiveSessionId, setCurrentActiveSessionId] = useState('1')
38
+ const [isChatLoaded, setIsChatLoaded] = useState(false)
39
+ const [isBooting, setIsBooting] = useState(true)
40
+ const sessionId = useRef('mark-main-thread')
41
+
42
+ const abortControllerRef = useRef(null)
43
+
44
+ const loadConfig = async () => {
45
+ const data = await getAllConfig()
46
+ if (data.length > 0) setConfig(data)
47
+ }
48
+
49
+ useEffect(() => {
50
+ loadConfig()
51
+ loadMainThread()
52
+
53
+ const handleConfigUpdate = (e) => {
54
+ if (e.detail) {
55
+ setConfig([e.detail])
56
+ }
57
+ }
58
+
59
+ const handleAiReset = () => {
60
+ setChatData([])
61
+ setCurrentActiveSessionId('1')
62
+ setActiveTopic(null)
63
+ setCurrentResponse(null)
64
+ setNotifications([])
65
+ setActiveProcesses([])
66
+ }
67
+
68
+ window.addEventListener('config-updated', handleConfigUpdate)
69
+ window.addEventListener('ai-reset-complete', handleAiReset)
70
+ return () => {
71
+ window.removeEventListener('config-updated', handleConfigUpdate)
72
+ window.removeEventListener('ai-reset-complete', handleAiReset)
73
+ }
74
+ }, [])
75
+
76
+ const loadMainThread = async () => {
77
+ const data = await getMainThread()
78
+ if (data && data.length > 0) {
79
+ setChatData(data)
80
+ }
81
+ setIsChatLoaded(true)
82
+ }
83
+
84
+ useEffect(() => {
85
+ // Debounce simpan ke DB agar tidak membanjiri SQLite REST endpoint saat streaming token
86
+ if (chatData !== undefined && isChatLoaded) {
87
+ const timer = setTimeout(() => {
88
+ saveMainThread(chatData)
89
+ }, 800)
90
+ return () => clearTimeout(timer)
91
+ }
92
+ }, [chatData, isChatLoaded])
93
+
94
+ const clearChat = () => {
95
+ setChatData([]) // saveMainThread will auto save the empty array
96
+ }
97
+
98
+ const pushNotification = (type, message) => {
99
+ setNotifications((prev) => [
100
+ ...prev,
101
+ { id: Date.now() + Math.random(), type, message, timestamp: Date.now() }
102
+ ])
103
+ }
104
+
105
+ const pushProcess = (process) => {
106
+ // process: { id, type, status, data }
107
+ setActiveProcesses((prev) => {
108
+ const existing = prev.findIndex((p) => p.id === process.id)
109
+ if (existing !== -1) {
110
+ // Update
111
+ const next = [...prev]
112
+ next[existing] = { ...next[existing], ...process }
113
+ return next
114
+ }
115
+ // Add new
116
+ return [...prev, process]
117
+ })
118
+ }
119
+
120
+ const dismissProcess = (id) => {
121
+ setActiveProcesses((prev) => prev.filter((p) => p.id !== id))
122
+ }
123
+
124
+ const handleStop = () => {
125
+ abortControllerRef.current?.abort()
126
+ if (window.api?.browserClose) {
127
+ window.api.browserClose()
128
+ }
129
+ }
130
+
131
+ return {
132
+ chatData,
133
+ setChatData,
134
+ sessionId: sessionId.current,
135
+ config,
136
+ setConfig,
137
+ message,
138
+ setMessage,
139
+ isLoading,
140
+ setIsLoading,
141
+ isAgentBusy,
142
+ setIsAgentBusy,
143
+ runningSessionId,
144
+ setRunningSessionId,
145
+ runningSessionIds,
146
+ setRunningSessionIds,
147
+ addRunningSessionId,
148
+ removeRunningSessionId,
149
+ isSpeak,
150
+ setIsSpeak,
151
+ orbStatus,
152
+ setOrbStatus,
153
+ currentResponse,
154
+ setCurrentResponse,
155
+ notifications,
156
+ pushNotification,
157
+ activeProcesses,
158
+ setActiveProcesses,
159
+ pushProcess,
160
+ dismissProcess,
161
+ inputSource,
162
+ setInputSource,
163
+ activeTopic,
164
+ setActiveTopic,
165
+ currentActiveSessionId,
166
+ setCurrentActiveSessionId,
167
+ isChatLoaded,
168
+ isBooting,
169
+ setIsBooting,
170
+ abortControllerRef,
171
+ handleStop
172
+ }
173
+ }
@@ -0,0 +1,82 @@
1
+ import axios from 'axios'
2
+ import { getYoutubeSummary } from '../../api/ai/tools'
3
+
4
+ export const useMarkYoutube = (setChatData) => {
5
+ const handleYoutubeSearch = async (answer, signal, customSetChatData) => {
6
+ const targetSet = customSetChatData || setChatData
7
+ try {
8
+ const searchResults = await window.api.searchYoutube(answer.command.query)
9
+ targetSet((prev) => [
10
+ ...prev.filter((item) => !item.isThinking),
11
+ {
12
+ role: 'ai',
13
+ content: answer.answer,
14
+ isYoutubeSearch: true,
15
+ youtubeLink: [...searchResults],
16
+ queryYoutube: answer.command.query
17
+ }
18
+ ])
19
+ } catch (error) {
20
+ console.error('Youtube Search Error:', error)
21
+ if (error.name === 'AbortError') {
22
+ targetSet((prev) => [...prev.filter((item) => !item.isThinking)])
23
+ targetSet((prev) => prev.slice(0, -1))
24
+ } else {
25
+ targetSet((prev) => [
26
+ ...prev.filter((item) => !item.isThinking),
27
+ {
28
+ role: 'ai',
29
+ content: 'Gagal dapet info dari youtube nih, koneksi atau captcha mungkin bermasalah.'
30
+ }
31
+ ])
32
+ }
33
+ }
34
+ }
35
+
36
+ const getYoutubeData = async (url) => {
37
+ try {
38
+ const endpoint = `https://www.youtube.com/embed?url=${encodeURIComponent(url)}&format=json`
39
+ const response = await axios.get(endpoint)
40
+ const data = response.data
41
+ return {
42
+ judul: data.title,
43
+ author: data.author_name,
44
+ thumbnail: data.thumbnail_url,
45
+ success: true
46
+ }
47
+ } catch (error) {
48
+ console.error('Gagal ambil data YouTube:', error.message)
49
+ return { judul: 'Video Tidak Ditemukan', author: '-', thumbnail: null, success: false }
50
+ }
51
+ }
52
+
53
+ const handleYoutubeSummary = async (url, signal, customSetChatData) => {
54
+ const targetSet = customSetChatData || setChatData
55
+ targetSet((prev) => [...prev, { role: 'ai', content: 'Sedang menonton video youtube (hal ini akan membutuhkan waktu beberapa saat mohon ditunggu)...', isSummarizing: true, youtubeLink: url }])
56
+ try {
57
+ const data = await getYoutubeData(url)
58
+ const searchResults = await getYoutubeSummary(url, data, signal)
59
+ targetSet((prev) => [
60
+ ...prev.filter((item) => !item.isSummarizing),
61
+ { role: 'ai', content: searchResults, isYoutubeSummary: true, youtubeLink: url }
62
+ ])
63
+ } catch (error) {
64
+ console.error('Youtube Summary Error:', error)
65
+ if (error.name === 'AbortError') {
66
+ targetSet((prev) => [...prev.filter((item) => !item.isSummarizing)])
67
+ targetSet((prev) => prev.slice(0, -1))
68
+ } else {
69
+ targetSet((prev) => [
70
+ ...prev.filter((item) => !item.isSummarizing),
71
+ {
72
+ role: 'ai',
73
+ content: 'Gagal dapet info dari youtube nih, koneksi atau captcha mungkin bermasalah.'
74
+ }
75
+ ])
76
+ }
77
+ }
78
+ }
79
+
80
+
81
+ return { handleYoutubeSearch, handleYoutubeSummary, getYoutubeData }
82
+ }
@@ -0,0 +1,201 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { getRelationship, saveRelationship, insertMemory, normalizeDbId } from '../../api/db'
3
+ import { evaluateTraitDrift } from '../../api/ai/relationship'
4
+
5
+ export const useRelationalGrowth = ({
6
+ chatData,
7
+ currentActiveSessionId = '1',
8
+ isLoading = false
9
+ }) => {
10
+ const lastEvalChatLenRef = useRef(0)
11
+
12
+ // --- RELATIONAL GROWTH EVALUATION (Event-Based) ---
13
+ useEffect(() => {
14
+ // Evaluasi hubungan emosi HANYA dilakukan di sesi utama ('1') dan saat turn chat selesai (tidak sedang loading)
15
+ if (normalizeDbId(currentActiveSessionId) !== '1' || isLoading) {
16
+ return
17
+ }
18
+
19
+ const evaluateGrowth = async () => {
20
+ try {
21
+ const allCleanChats = (chatData || []).filter(
22
+ (m) => !m.isThinking && !m.isSearching && !m.isSummarizing
23
+ )
24
+ const currentCleanLen = allCleanChats.length
25
+ if (currentCleanLen === 0) return
26
+
27
+ // Initialize state dari database saat pertama kali jalan
28
+ const oldTraits = await getRelationship('owner')
29
+ const dbLastIndex = oldTraits.lastChatIndex ?? oldTraits.last_chat_index ?? 0
30
+
31
+ if (lastEvalChatLenRef.current === 0) {
32
+ // Jika dbLastIndex belum diset (0) atau melebihi jumlah chat saat ini (karena prune/archive),
33
+ // set baseline awal ke currentCleanLen saat ini
34
+ if (dbLastIndex === 0 || dbLastIndex > currentCleanLen) {
35
+ lastEvalChatLenRef.current = currentCleanLen
36
+ await saveRelationship({
37
+ userId: 'owner',
38
+ ...oldTraits,
39
+ lastChatIndex: currentCleanLen,
40
+ last_chat_index: currentCleanLen
41
+ })
42
+ } else {
43
+ lastEvalChatLenRef.current = dbLastIndex
44
+ }
45
+ }
46
+
47
+ // Jika chat pernah dibersihkan/dipangkas sehingga lebih kecil dari baseline sebelumnya
48
+ if (currentCleanLen < lastEvalChatLenRef.current) {
49
+ lastEvalChatLenRef.current = currentCleanLen
50
+ await saveRelationship({
51
+ userId: 'owner',
52
+ ...oldTraits,
53
+ lastChatIndex: currentCleanLen,
54
+ last_chat_index: currentCleanLen
55
+ })
56
+ return
57
+ }
58
+
59
+ // Trigger evaluasi setiap selisih 15 pesan
60
+ if (currentCleanLen - lastEvalChatLenRef.current >= 15) {
61
+ console.log(
62
+ '[Relational Growth] Threshold 15 chat tercapai di sesi utama. Mengevaluasi mood...'
63
+ )
64
+
65
+ // Batasi maksimal 20 pesan terbaru dan batasi panjang teks per pesan
66
+ const startIndex = Math.max(lastEvalChatLenRef.current, currentCleanLen - 20)
67
+ const recentForEval = allCleanChats
68
+ .slice(startIndex)
69
+ .map((m) => {
70
+ let timeStr = ''
71
+ if (m.timestamp) {
72
+ timeStr =
73
+ typeof m.timestamp === 'number'
74
+ ? `[${new Date(m.timestamp).toLocaleString('id-ID')}] `
75
+ : `[${m.timestamp}] `
76
+ }
77
+ const rawContent =
78
+ typeof m.content === 'string'
79
+ ? m.content
80
+ : m.content
81
+ ? JSON.stringify(m.content)
82
+ : ''
83
+ const safeContent =
84
+ rawContent.length > 500 ? `${rawContent.slice(0, 500)}... [dipotong]` : rawContent
85
+ return `${timeStr}${m.role === 'user' ? 'User' : 'Mark'}: ${safeContent}`
86
+ })
87
+ .join('\n')
88
+
89
+ // Simpan state panjang chat saat ini untuk evaluasi berikutnya
90
+ lastEvalChatLenRef.current = currentCleanLen
91
+
92
+ // Evaluasi AI
93
+ const newTraits = await evaluateTraitDrift(oldTraits, recentForEval, 'owner')
94
+ console.log('[Relational Growth] Trait shift:', newTraits)
95
+
96
+ // Simpan trait baru ke database
97
+ const updatedRecord = {
98
+ userId: 'owner',
99
+ ...newTraits,
100
+ lastEvaluation: new Date().toISOString(),
101
+ evalCount: (oldTraits.evalCount || 0) + 1,
102
+ lastChatIndex: currentCleanLen,
103
+ last_chat_index: currentCleanLen
104
+ }
105
+ await saveRelationship(updatedRecord)
106
+
107
+ window.dispatchEvent(
108
+ new CustomEvent('relationship-updated', {
109
+ detail: updatedRecord
110
+ })
111
+ )
112
+
113
+ // Simpan relational memory jika AI merasa ada hal penting
114
+ if (newTraits.new_relational_memory) {
115
+ await insertMemory({
116
+ type: 'notes',
117
+ summary: '[Relational] Catatan hubungan otomatis',
118
+ memory: newTraits.new_relational_memory
119
+ })
120
+ console.log(
121
+ '[Relational Growth] Relational memory tersimpan:',
122
+ newTraits.new_relational_memory
123
+ )
124
+ }
125
+ }
126
+ } catch (err) {
127
+ console.error('[Relational Growth] Gagal mengevaluasi hubungan:', err)
128
+ }
129
+ }
130
+
131
+ evaluateGrowth()
132
+ }, [chatData, currentActiveSessionId, isLoading])
133
+
134
+ // Listener untuk evaluasi relasi berbasis event (misal dari Private Journal / akhir sesi)
135
+ useEffect(() => {
136
+ const handleForceRelationalEval = async (e) => {
137
+ try {
138
+ const oldTraits = await getRelationship('owner')
139
+ const contextText =
140
+ e.detail?.summary || 'Sesi kerja panjang dan refleksi batin harian bersama pengguna.'
141
+ const newTraits = await evaluateTraitDrift(oldTraits, contextText, 'owner')
142
+ const updatedRecord = {
143
+ userId: 'owner',
144
+ ...newTraits,
145
+ lastEvaluation: new Date().toISOString(),
146
+ evalCount: (oldTraits.evalCount || 0) + 1
147
+ }
148
+ await saveRelationship(updatedRecord)
149
+ window.dispatchEvent(
150
+ new CustomEvent('relationship-updated', {
151
+ detail: updatedRecord
152
+ })
153
+ )
154
+ if (newTraits.new_relational_memory) {
155
+ await insertMemory({
156
+ type: 'notes',
157
+ summary: '[Relational] Catatan hubungan otomatis',
158
+ memory: newTraits.new_relational_memory
159
+ })
160
+ }
161
+ } catch (err) {
162
+ console.warn('[Relational Growth] Gagal evaluasi relasi dari event:', err)
163
+ }
164
+ }
165
+ window.addEventListener('mark:evaluate-relationship', handleForceRelationalEval)
166
+ return () => window.removeEventListener('mark:evaluate-relationship', handleForceRelationalEval)
167
+ }, [])
168
+
169
+ // --- TACTILE INTERACTION DRIFT (Touch, Pet, Bonk) ---
170
+ useEffect(() => {
171
+ const handleTactile = async (e) => {
172
+ const type = e.detail?.type
173
+ if (!type) return
174
+ try {
175
+ const current = await getRelationship('owner')
176
+ let { warmth = 0.5, trust = 0.5, sarcasm_level = 0.5 } = current || {}
177
+ if (type === 'petting' || type === 'pet_purr') {
178
+ warmth = Math.min(1.0, warmth + 0.005)
179
+ trust = Math.min(1.0, trust + 0.005)
180
+ } else if (type === 'bonk_annoyed') {
181
+ sarcasm_level = Math.min(1.0, sarcasm_level + 0.005)
182
+ } else if (type === 'bonk_dizzy' || type === 'shield_block') {
183
+ sarcasm_level = Math.min(1.0, sarcasm_level + 0.01)
184
+ trust = Math.max(0.15, trust - 0.005)
185
+ }
186
+ const updatedRecord = {
187
+ ...current,
188
+ userId: 'owner',
189
+ warmth,
190
+ trust,
191
+ sarcasm_level,
192
+ lastEvaluation: new Date().toISOString()
193
+ }
194
+ await saveRelationship(updatedRecord)
195
+ window.dispatchEvent(new CustomEvent('relationship-updated', { detail: updatedRecord }))
196
+ } catch (_) {}
197
+ }
198
+ window.addEventListener('mark-tactile-interaction', handleTactile)
199
+ return () => window.removeEventListener('mark-tactile-interaction', handleTactile)
200
+ }, [])
201
+ }
@@ -0,0 +1,63 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { webApi } from '../../api/web-bridge'
3
+
4
+ export const useTelegramBot = () => {
5
+ const [status, setStatus] = useState('disconnected')
6
+ const [messages, setMessages] = useState([])
7
+ const [isThinking, setIsThinking] = useState(false)
8
+ const [currentSender, setCurrentSender] = useState('')
9
+
10
+ useEffect(() => {
11
+ webApi.tgGetStatus().then((res) => {
12
+ if (res?.status) setStatus(res.status)
13
+ })
14
+
15
+ webApi.tgGetHistory().then((history) => {
16
+ if (history && history.length > 0) {
17
+ setMessages(history)
18
+ }
19
+ })
20
+
21
+ const unsubConn = webApi.onTgConnection((newStatus) => {
22
+ setStatus(newStatus)
23
+ })
24
+
25
+ const unsubThink = webApi.onTgThinking(({ sender }) => {
26
+ setIsThinking(true)
27
+ setCurrentSender(sender)
28
+ })
29
+
30
+ const unsubMsg = webApi.onTgMessage((data) => {
31
+ setMessages((prev) => [...prev, { type: 'incoming', ...data }])
32
+ })
33
+
34
+ const unsubReply = webApi.onTgReplySent((data) => {
35
+ setMessages((prev) => [...prev, { type: 'outgoing', ...data }])
36
+ setIsThinking(false)
37
+ })
38
+
39
+ return () => {
40
+ if (typeof unsubConn === 'function') unsubConn()
41
+ if (typeof unsubThink === 'function') unsubThink()
42
+ if (typeof unsubMsg === 'function') unsubMsg()
43
+ if (typeof unsubReply === 'function') unsubReply()
44
+ }
45
+ }, [])
46
+
47
+ const startBot = (token) => {
48
+ webApi.tgStart(token)
49
+ }
50
+
51
+ const stopBot = () => {
52
+ webApi.tgStop()
53
+ }
54
+
55
+ return {
56
+ status,
57
+ messages,
58
+ isThinking,
59
+ currentSender,
60
+ startBot,
61
+ stopBot
62
+ }
63
+ }