@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,289 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { getAllMemory, insertMemory, getRelationship } from '../api/db'
3
+ import { getRelevantMemory } from '../api/vectorMemory'
4
+ import { getAwarenessResponse, generateDailyJournalEntry } from '../api/ai/awareness'
5
+ import { subagentStore } from '../api/subagent/subagentStore'
6
+ import { runSubagentTurn } from '../api/subagent/subagentExecutor'
7
+ import { getCurrentTimeInfo } from '../api/ai/utils'
8
+ import { stripMarkTags } from '@shared/parsers/mark-tag-parser.js'
9
+
10
+ const CHECKIN_POLL_INTERVAL = 20 * 1000 // Polling telemetri setiap 20 detik
11
+ const MIN_CHECKIN_GAP = 3 * 60 * 1000 // Minimal 3 menit antar evaluasi normal
12
+
13
+ const formatAwarenessContent = (content) => {
14
+ if (typeof content === 'string') return content
15
+ if (content == null) return ''
16
+
17
+ if (Array.isArray(content)) {
18
+ return content
19
+ .map((part) => {
20
+ if (typeof part === 'string') return part
21
+ if (part?.type === 'text') return part.text || ''
22
+ if (part?.type === 'image_url') return '[Gambar]'
23
+ return ''
24
+ })
25
+ .filter(Boolean)
26
+ .join('\n')
27
+ }
28
+
29
+ return JSON.stringify(content)
30
+ }
31
+
32
+ const tokenizeForSimilarity = (text) => {
33
+ return new Set(
34
+ String(text || '')
35
+ .toLowerCase()
36
+ .replace(/[^\p{L}\p{N}\s]/gu, ' ')
37
+ .split(/\s+/)
38
+ .filter((word) => word.length > 3)
39
+ )
40
+ }
41
+
42
+ const isSimilarAwarenessMessage = (message, recentMessages) => {
43
+ const incomingTokens = tokenizeForSimilarity(message)
44
+ if (incomingTokens.size < 4) return false
45
+
46
+ return recentMessages.some((item) => {
47
+ const previousTokens = tokenizeForSimilarity(formatAwarenessContent(item.content))
48
+ if (previousTokens.size < 4) return false
49
+
50
+ const shared = [...incomingTokens].filter((word) => previousTokens.has(word)).length
51
+ return shared / Math.min(incomingTokens.size, previousTokens.size) >= 0.6
52
+ })
53
+ }
54
+
55
+ export const useAwareness = ({
56
+ isLoading,
57
+ isAgentBusy,
58
+ setChatData,
59
+ setOrbStatus,
60
+ config,
61
+ chatData,
62
+ handlePlanningCommand,
63
+ currentMusicTrack
64
+ }) => {
65
+ const isRequestingRef = useRef(false)
66
+ const chatDataRef = useRef(chatData)
67
+ const configRef = useRef(config)
68
+ const handlePlanningCommandRef = useRef(handlePlanningCommand)
69
+ const currentMusicTrackRef = useRef(currentMusicTrack)
70
+ const isLoadingRef = useRef(isLoading)
71
+ const isAgentBusyRef = useRef(isAgentBusy)
72
+ const lastCheckInRef = useRef(0)
73
+
74
+ // State Pelacak AFK
75
+ const wasAfkRef = useRef(false)
76
+ const lastJournalDateRef = useRef('')
77
+
78
+ useEffect(() => {
79
+ chatDataRef.current = chatData
80
+ configRef.current = config
81
+ handlePlanningCommandRef.current = handlePlanningCommand
82
+ currentMusicTrackRef.current = currentMusicTrack
83
+ isLoadingRef.current = isLoading
84
+ isAgentBusyRef.current = isAgentBusy
85
+ }, [chatData, config, handlePlanningCommand, currentMusicTrack, isLoading, isAgentBusy])
86
+
87
+ const isAwarenessEnabled = config?.[0]?.awarenessEnabled !== false
88
+
89
+ useEffect(() => {
90
+ if (!isAwarenessEnabled) return
91
+
92
+ const runEvaluation = async ({ isReturnFromAFK = false } = {}) => {
93
+ if (isAgentBusyRef.current || isLoadingRef.current || isRequestingRef.current) return
94
+
95
+ try {
96
+ isRequestingRef.current = true
97
+ lastCheckInRef.current = Date.now()
98
+
99
+ const buffer = await window.api.getActivityBuffer()
100
+ const telemetry = buffer?.telemetry || (await window.api.getSystemTelemetry?.()) || null
101
+ if (buffer && telemetry) {
102
+ buffer.telemetry = telemetry
103
+ }
104
+
105
+ const allMemory = await getAllMemory()
106
+ const memoryRef = await getRelevantMemory('aktivitas user bekerja dan rutinitas', allMemory)
107
+
108
+ const recentChat = (chatDataRef.current || [])
109
+ .filter((m) => !m.isThinking && !m.isSearching && !m.isSummarizing)
110
+ .slice(-5)
111
+ .map((m) => ({ role: m.role, content: m.content }))
112
+
113
+ if (window.api.clearActivityBuffer) {
114
+ window.api.clearActivityBuffer()
115
+ }
116
+
117
+ const result = await getAwarenessResponse(
118
+ buffer,
119
+ memoryRef,
120
+ configRef.current,
121
+ recentChat,
122
+ currentMusicTrackRef.current
123
+ )
124
+
125
+ // Broadcast thought ticker ke UI
126
+ if (result.thought) {
127
+ window.dispatchEvent(
128
+ new CustomEvent('mark:thought', {
129
+ detail: { thought: result.thought, mood: result.mood }
130
+ })
131
+ )
132
+ }
133
+
134
+ // 1. Aksi: Journal (Refleksi Batin)
135
+ if (
136
+ result.action_type === 'journal' ||
137
+ result.thought?.toLowerCase().includes('refleksi')
138
+ ) {
139
+ const journalText = result.thought || result.message
140
+ if (journalText) {
141
+ await insertMemory({
142
+ type: 'private_journal',
143
+ summary: `Refleksi Batin Mark - ${new Date().toLocaleDateString('id-ID')}`,
144
+ memory: journalText
145
+ })
146
+ }
147
+ }
148
+
149
+ // 2. Aksi: Autonomous Task (Riset Mandiri AFK / Peeking / Telegram via Sub-Agent)
150
+ if (result.autonomous_prompt) {
151
+ try {
152
+ const isAfk = Boolean(telemetry?.isUserAFK)
153
+ const subAgent = await subagentStore.createSubagent({
154
+ name: isAfk ? 'Mark-SideProject' : 'Mark-Autonomous',
155
+ role: isAfk ? 'Autonomous Curiosity Researcher' : 'Autonomous Background Specialist',
156
+ goal: result.autonomous_prompt,
157
+ parentSessionId: 'awareness',
158
+ parentSessionTitle: 'Awareness Engine'
159
+ })
160
+ if (subAgent?.id) {
161
+ runSubagentTurn(subAgent.id, result.autonomous_prompt, 'mark').catch((subErr) => {
162
+ console.warn('[useAwareness] Subagent run error:', subErr)
163
+ })
164
+ }
165
+ } catch (subErr) {
166
+ console.warn('[useAwareness] Gagal spawn autonomous subagent:', subErr)
167
+ }
168
+ }
169
+
170
+ // 3. Aksi: Vocal (Balon Chat & Sapaan)
171
+ const cleanMessage = stripMarkTags(result.message || '').trim()
172
+ if (result.action_type === 'vocal' && cleanMessage) {
173
+ const recentVisibleMessages = (chatDataRef.current || [])
174
+ .filter((m) => !m.isThinking && !m.isSearching && !m.isSummarizing)
175
+ .slice(-8)
176
+
177
+ if (!isSimilarAwarenessMessage(cleanMessage, recentVisibleMessages)) {
178
+ if (window.api.showNotification && !document.hasFocus()) {
179
+ window.api.showNotification('Mark', cleanMessage)
180
+ }
181
+
182
+ const now = Date.now()
183
+ setChatData((prev) => [
184
+ ...prev,
185
+ {
186
+ id: `awareness_${now}`,
187
+ role: 'ai',
188
+ content: cleanMessage,
189
+ isProactive: true,
190
+ mood: result.mood,
191
+ isReturnFromAFK,
192
+ timestamp: getCurrentTimeInfo(),
193
+ created_at: now
194
+ }
195
+ ])
196
+
197
+ if (typeof setOrbStatus === 'function') {
198
+ setOrbStatus('nudge')
199
+ setTimeout(() => {
200
+ if (typeof setOrbStatus === 'function') {
201
+ setOrbStatus('idle')
202
+ }
203
+ }, 3000)
204
+ }
205
+ }
206
+ }
207
+ } catch (err) {
208
+ console.error('[Awareness Hook] Error evaluasi:', err)
209
+ } finally {
210
+ isRequestingRef.current = false
211
+ }
212
+ }
213
+
214
+ // Loop Utama Polling Telemetri & Event-Driven Trigger
215
+ const pollTimer = setInterval(async () => {
216
+ try {
217
+ const idleSec = await (window.api?.getSystemIdleSeconds?.() || Promise.resolve(0))
218
+ const isAFK = idleSec >= 900 // 15 menit idle
219
+
220
+ // Deteksi Sleep State untuk Avatar
221
+ if (isAFK && !wasAfkRef.current) {
222
+ wasAfkRef.current = true
223
+ window.dispatchEvent(new CustomEvent('mark:sleeping', { detail: { isSleeping: true } }))
224
+ }
225
+
226
+ // Deteksi Return from AFK (user kembali menyentuh mouse/keyboard)
227
+ if (!isAFK && wasAfkRef.current) {
228
+ wasAfkRef.current = false
229
+ // Trigger wake-up pulse pada orb
230
+ window.dispatchEvent(
231
+ new CustomEvent('mark:sleeping', {
232
+ detail: { isSleeping: false, wakeUpPulse: true }
233
+ })
234
+ )
235
+ // Picu evaluasi awareness seketika (Welcome Back)
236
+ runEvaluation({ isReturnFromAFK: true })
237
+ return
238
+ }
239
+
240
+ // Cek Refleksi Harian Tengah Malam (00:00 - 00:15)
241
+ const now = new Date()
242
+ const todayKey = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`
243
+ if (now.getHours() === 0 && lastJournalDateRef.current !== todayKey) {
244
+ lastJournalDateRef.current = todayKey
245
+ try {
246
+ const traits = await getRelationship('owner')
247
+ const allMem = await getAllMemory()
248
+ const actSummaries = (allMem || [])
249
+ .filter((m) => m.type === 'notes' || m.type === 'profile')
250
+ .slice(0, 5)
251
+ .map((m) => m.memory)
252
+
253
+ const journalContent = await generateDailyJournalEntry(
254
+ actSummaries,
255
+ traits,
256
+ configRef.current
257
+ )
258
+ if (journalContent) {
259
+ await insertMemory({
260
+ type: 'private_journal',
261
+ summary: `Refleksi Batin Mark - ${now.toLocaleDateString('id-ID')}`,
262
+ memory: journalContent
263
+ })
264
+ window.dispatchEvent(
265
+ new CustomEvent('mark:evaluate-relationship', {
266
+ detail: { summary: journalContent }
267
+ })
268
+ )
269
+ }
270
+ } catch (jErr) {
271
+ console.warn('[Awareness] Gagal buat catatan harian tengah malam:', jErr)
272
+ }
273
+ }
274
+
275
+ // Evaluasi Berkala Normal
276
+ const nowMs = Date.now()
277
+ if (nowMs - lastCheckInRef.current >= MIN_CHECKIN_GAP) {
278
+ runEvaluation()
279
+ }
280
+ } catch (e) {
281
+ console.warn('[useAwareness] Poll error:', e)
282
+ }
283
+ }, CHECKIN_POLL_INTERVAL)
284
+
285
+ return () => {
286
+ clearInterval(pollTimer)
287
+ }
288
+ }, [isAwarenessEnabled, setChatData, setOrbStatus])
289
+ }
@@ -0,0 +1,52 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { indexSingleTurn } from '../api/turnPairMigrator'
3
+
4
+ export const useChatArchiver = ({
5
+ chatData,
6
+ activeTopic,
7
+ isLoading,
8
+ sessionId = 1
9
+ }) => {
10
+ const currentSessionId = sessionId || 1
11
+ const wasLoadingRef = useRef(false)
12
+ const lastIndexedPairIdRef = useRef(null)
13
+
14
+ useEffect(() => {
15
+ // Deteksi transisi ketika Mark selesai merespons (isLoading: true -> false)
16
+ const justFinishedLoading = wasLoadingRef.current && !isLoading
17
+ wasLoadingRef.current = isLoading
18
+
19
+ if (justFinishedLoading && Array.isArray(chatData) && chatData.length >= 2) {
20
+ // Cari pesan balasan AI terakhir yang valid
21
+ let lastAiMsg = null
22
+ let userMsg = null
23
+
24
+ for (let i = chatData.length - 1; i >= 0; i--) {
25
+ const msg = chatData[i]
26
+ if (!msg) continue
27
+ if (!lastAiMsg && msg.role === 'ai' && !msg.isThinking && !msg.isSearching && !msg.isSummarizing) {
28
+ lastAiMsg = msg
29
+ // Cari pesan user sebelum pesan AI ini
30
+ for (let j = i - 1; j >= 0; j--) {
31
+ if (chatData[j]?.role === 'user') {
32
+ userMsg = chatData[j]
33
+ break
34
+ }
35
+ }
36
+ break
37
+ }
38
+ }
39
+
40
+ if (userMsg && lastAiMsg) {
41
+ const turnKey = `${currentSessionId}-${userMsg.timestamp || ''}-${lastAiMsg.timestamp || ''}`
42
+ if (lastIndexedPairIdRef.current !== turnKey) {
43
+ lastIndexedPairIdRef.current = turnKey
44
+ // Index secara instan di background
45
+ indexSingleTurn(currentSessionId, activeTopic, userMsg, lastAiMsg).catch((err) => {
46
+ console.warn('[useChatArchiver] Realtime indexSingleTurn error:', err)
47
+ })
48
+ }
49
+ }
50
+ }
51
+ }, [chatData, activeTopic, isLoading, currentSessionId])
52
+ }
@@ -0,0 +1,68 @@
1
+ import React, { useState, useCallback, useRef } from 'react'
2
+ import ConfirmModal from '../components/core/ConfirmModal'
3
+
4
+ export const useConfirm = () => {
5
+ const [modalConfig, setModalConfig] = useState({
6
+ isOpen: false,
7
+ title: '',
8
+ message: '',
9
+ isError: false,
10
+ confirmText: 'Ya',
11
+ cancelText: 'Batal',
12
+ hideCancel: false
13
+ })
14
+ const promiseRef = useRef(null)
15
+
16
+ const confirm = useCallback((config) => {
17
+ return new Promise((resolve) => {
18
+ promiseRef.current = resolve
19
+ setModalConfig({
20
+ isOpen: true,
21
+ title: config.title || 'Konfirmasi',
22
+ message: config.message || config.text || '',
23
+ isError: config.isError || config.icon === 'error' || config.icon === 'warning' || false,
24
+ confirmText: config.confirmText || config.confirmButtonText || 'Ya',
25
+ cancelText: config.cancelText || config.cancelButtonText || 'Batal',
26
+ hideCancel:
27
+ config.hideCancel || (!config.showCancelButton && config.showCancelButton !== undefined)
28
+ ? true
29
+ : false
30
+ })
31
+ })
32
+ }, [])
33
+
34
+ const handleConfirm = useCallback(() => {
35
+ setModalConfig((prev) => ({ ...prev, isOpen: false }))
36
+ if (promiseRef.current) {
37
+ promiseRef.current({ isConfirmed: true })
38
+ promiseRef.current = null
39
+ }
40
+ }, [])
41
+
42
+ const handleCancel = useCallback(() => {
43
+ setModalConfig((prev) => ({ ...prev, isOpen: false }))
44
+ if (promiseRef.current) {
45
+ promiseRef.current({ isConfirmed: false })
46
+ promiseRef.current = null
47
+ }
48
+ }, [])
49
+
50
+ const ModalComponent = useCallback(
51
+ () => (
52
+ <ConfirmModal
53
+ isOpen={modalConfig.isOpen}
54
+ title={modalConfig.title}
55
+ message={modalConfig.message}
56
+ onConfirm={handleConfirm}
57
+ onCancel={handleCancel}
58
+ confirmText={modalConfig.confirmText}
59
+ cancelText={modalConfig.cancelText}
60
+ isError={modalConfig.isError}
61
+ hideCancel={modalConfig.hideCancel}
62
+ />
63
+ ),
64
+ [modalConfig, handleConfirm, handleCancel]
65
+ )
66
+
67
+ return { confirm, ModalComponent }
68
+ }