@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,425 @@
1
+ import React, { createContext, useState, useContext, useCallback, useRef, useEffect } from 'react'
2
+ import { useLocation } from 'react-router-dom'
3
+ import { ShieldAlert, Terminal, FileCode } from 'lucide-react'
4
+ import { getAlwaysAllowedPaths, addAlwaysAllowedPath, getSessionAutoMode } from '../api/db'
5
+
6
+ const ApprovalContext = createContext()
7
+
8
+ export function extractToolTarget(tool, query) {
9
+ if (!query) return { type: 'unknown', value: '' }
10
+
11
+ if (tool === 'run-powershell') {
12
+ const cmd =
13
+ typeof query === 'object' && query !== null
14
+ ? query.command || query.cmd || ''
15
+ : String(query || '')
16
+ return { type: 'command', value: String(cmd).trim() }
17
+ }
18
+
19
+ let rawPath = ''
20
+ if (typeof query === 'object' && query !== null) {
21
+ rawPath = query.path || query.filePath || query.cwd || query.target || ''
22
+ } else if (typeof query === 'string') {
23
+ rawPath = query.split('||')[0].trim()
24
+ }
25
+
26
+ return {
27
+ type: 'path',
28
+ value: String(rawPath)
29
+ .replace(/^["']|["']$/g, '')
30
+ .trim()
31
+ }
32
+ }
33
+
34
+ export function getFolderFromPath(filePath) {
35
+ if (!filePath) return ''
36
+ const normalized = filePath.replace(/[\\/]+/g, '/')
37
+ const lastSlash = normalized.lastIndexOf('/')
38
+ if (lastSlash !== -1) {
39
+ return normalized.substring(0, lastSlash)
40
+ }
41
+ return normalized
42
+ }
43
+
44
+ export const ApprovalProvider = ({ children }) => {
45
+ const [approvalData, setApprovalData] = useState(null)
46
+ const approvalRef = useRef(null)
47
+ const [alwaysAllowedPaths, setAlwaysAllowedPaths] = useState([])
48
+ const sessionAllowedMapRef = useRef(new Map())
49
+ const autoModeSessionsRef = useRef(new Set())
50
+
51
+ const location = useLocation()
52
+ const isChatStudio =
53
+ location?.pathname === '/chat' ||
54
+ (typeof window !== 'undefined' && window.location.hash.includes('/chat'))
55
+
56
+ const alwaysAllowedPathsRef = useRef(alwaysAllowedPaths)
57
+ useEffect(() => {
58
+ alwaysAllowedPathsRef.current = alwaysAllowedPaths
59
+ }, [alwaysAllowedPaths])
60
+
61
+ // Muat data alwaysAllowedPaths dari SQLite database saat startup
62
+ useEffect(() => {
63
+ getAlwaysAllowedPaths().then((paths) => {
64
+ if (Array.isArray(paths)) {
65
+ setAlwaysAllowedPaths(paths)
66
+ }
67
+ })
68
+
69
+ const handleConfigUpdated = (e) => {
70
+ if (Array.isArray(e.detail?.alwaysAllowedPaths)) {
71
+ setAlwaysAllowedPaths(e.detail.alwaysAllowedPaths)
72
+ }
73
+ }
74
+ window.addEventListener('config-updated', handleConfigUpdated)
75
+ return () => window.removeEventListener('config-updated', handleConfigUpdated)
76
+ }, [])
77
+
78
+ // Pastikan ref selalu sinkron dengan state
79
+ useEffect(() => {
80
+ approvalRef.current = approvalData
81
+ }, [approvalData])
82
+
83
+ const handleApproveAlwaysInternal = useCallback(async (tool, targetQuery) => {
84
+ const target = extractToolTarget(tool, targetQuery)
85
+ let identifierToAdd = ''
86
+
87
+ if (target.type === 'command') {
88
+ identifierToAdd = `cmd:${target.value.toLowerCase()}`
89
+ } else if (target.type === 'path' && target.value) {
90
+ const normalizedPath = target.value.replace(/[\\/]+/g, '/').toLowerCase()
91
+ const folder = getFolderFromPath(normalizedPath)
92
+ identifierToAdd = folder || normalizedPath
93
+ }
94
+
95
+ if (identifierToAdd) {
96
+ const updated = await addAlwaysAllowedPath(identifierToAdd)
97
+ if (Array.isArray(updated)) {
98
+ setAlwaysAllowedPaths(updated)
99
+ }
100
+ }
101
+ }, [])
102
+
103
+ const resolveApproval = useCallback(
104
+ (approvalId, decisionType) => {
105
+ // decisionType: 'approve_once' | 'approve_session' | 'approve_always' | 'reject'
106
+ const current = approvalRef.current
107
+ if (!current) return
108
+ if (approvalId && current.id !== approvalId) return
109
+
110
+ approvalRef.current = null
111
+ setApprovalData(null)
112
+
113
+ const sid = current.meta?.sessionId ? String(current.meta.sessionId) : '1'
114
+
115
+ if (decisionType === 'approve_always') {
116
+ handleApproveAlwaysInternal(current.tool, current.query)
117
+ } else if (decisionType === 'approve_session') {
118
+ if (!sessionAllowedMapRef.current.has(sid)) {
119
+ sessionAllowedMapRef.current.set(sid, new Set())
120
+ }
121
+ const target = extractToolTarget(current.tool, current.query)
122
+ const targetKey =
123
+ target.type === 'command'
124
+ ? `cmd:${target.value.toLowerCase()}`
125
+ : target.value
126
+ ? target.value.replace(/[\\/]+/g, '/').toLowerCase()
127
+ : current.tool
128
+ sessionAllowedMapRef.current.get(sid).add(targetKey)
129
+ if (target.type === 'path' && target.value) {
130
+ const folder = getFolderFromPath(target.value.replace(/[\\/]+/g, '/').toLowerCase())
131
+ if (folder) sessionAllowedMapRef.current.get(sid).add(folder)
132
+ }
133
+ }
134
+
135
+ const isApproved =
136
+ decisionType === 'approve_once' ||
137
+ decisionType === 'approve_session' ||
138
+ decisionType === 'approve_always'
139
+
140
+ // Update bubble state in chat feed if targetSetChatData exists
141
+ if (typeof current.meta?.targetSetChatData === 'function') {
142
+ current.meta.targetSetChatData((prev) =>
143
+ (prev || []).map((msg) =>
144
+ msg.id === current.id || msg.approvalId === current.id
145
+ ? { ...msg, status: decisionType }
146
+ : msg
147
+ )
148
+ )
149
+ }
150
+
151
+ if (typeof current.resolve === 'function') {
152
+ current.resolve(isApproved)
153
+ }
154
+ },
155
+ [handleApproveAlwaysInternal]
156
+ )
157
+
158
+ const handleRemoteDecision = useCallback(
159
+ (decisionType, chatId) => {
160
+ const current = approvalRef.current
161
+ if (current) {
162
+ resolveApproval(current.id, decisionType)
163
+
164
+ if (chatId && window.api?.tgSendMessage) {
165
+ let msg = '[INFO]: Permintaan persetujuan telah ditolak.'
166
+ if (decisionType === 'approve_always') {
167
+ msg = '[INFO]: Permintaan persetujuan diizinkan SELAMANYA.'
168
+ } else if (decisionType === 'approve_session') {
169
+ msg = '[INFO]: Permintaan persetujuan telah diizinkan untuk sesi ini.'
170
+ } else if (decisionType === 'approve_once') {
171
+ msg = '[INFO]: Permintaan persetujuan telah diizinkan sekali.'
172
+ }
173
+ window.api.tgSendMessage(chatId, msg)
174
+ }
175
+ } else {
176
+ if (chatId && window.api?.tgSendMessage) {
177
+ window.api.tgSendMessage(
178
+ chatId,
179
+ '[INFO]: Tidak ada permintaan persetujuan yang sedang menunggu.'
180
+ )
181
+ }
182
+ }
183
+ },
184
+ [resolveApproval]
185
+ )
186
+
187
+ useEffect(() => {
188
+ const unsubAccept = window.api?.onTgCommandAccept
189
+ ? window.api.onTgCommandAccept((data) => {
190
+ handleRemoteDecision('approve_once', data?.chatId)
191
+ })
192
+ : null
193
+
194
+ const unsubSession = window.api?.onTgCommandSession
195
+ ? window.api.onTgCommandSession((data) => {
196
+ handleRemoteDecision('approve_session', data?.chatId)
197
+ })
198
+ : null
199
+
200
+ const unsubAlways = window.api?.onTgCommandAlways
201
+ ? window.api.onTgCommandAlways((data) => {
202
+ handleRemoteDecision('approve_always', data?.chatId)
203
+ })
204
+ : null
205
+
206
+ const unsubReject = window.api?.onTgCommandReject
207
+ ? window.api.onTgCommandReject((data) => {
208
+ handleRemoteDecision('reject', data?.chatId)
209
+ })
210
+ : null
211
+
212
+ return () => {
213
+ if (typeof unsubAccept === 'function') unsubAccept()
214
+ if (typeof unsubSession === 'function') unsubSession()
215
+ if (typeof unsubAlways === 'function') unsubAlways()
216
+ if (typeof unsubReject === 'function') unsubReject()
217
+ }
218
+ }, [handleRemoteDecision])
219
+
220
+ useEffect(() => {
221
+ const handleAutoModeUpdate = (e) => {
222
+ const sid = String(e.detail?.sessionId || '1')
223
+ if (e.detail?.isAutoMode) {
224
+ autoModeSessionsRef.current.add(sid)
225
+ const current = approvalRef.current
226
+ if (current && String(current.meta?.sessionId || '1') === sid) {
227
+ resolveApproval(current.id, 'approve_session')
228
+ }
229
+ } else {
230
+ autoModeSessionsRef.current.delete(sid)
231
+ }
232
+ }
233
+ window.addEventListener('session-auto-mode-updated', handleAutoModeUpdate)
234
+ return () => window.removeEventListener('session-auto-mode-updated', handleAutoModeUpdate)
235
+ }, [resolveApproval])
236
+
237
+ const checkIsAlwaysAllowed = useCallback((tool, query) => {
238
+ const target = extractToolTarget(tool, query)
239
+ const allowedList = alwaysAllowedPathsRef.current || []
240
+
241
+ if (target.type === 'command') {
242
+ const cmdKey = `cmd:${target.value.toLowerCase()}`
243
+ return allowedList.some((item) => (item || '').toLowerCase() === cmdKey)
244
+ }
245
+
246
+ if (target.type === 'path' && target.value) {
247
+ const normTarget = target.value.replace(/[\\/]+/g, '/').toLowerCase()
248
+ const targetFolder = getFolderFromPath(normTarget)
249
+
250
+ return allowedList.some((item) => {
251
+ const normAllowed = (item || '').toLowerCase().replace(/[\\/]+/g, '/')
252
+ if (!normAllowed || normAllowed.startsWith('cmd:')) return false
253
+ return (
254
+ normTarget === normAllowed ||
255
+ targetFolder === normAllowed ||
256
+ normTarget.startsWith(normAllowed.endsWith('/') ? normAllowed : normAllowed + '/')
257
+ )
258
+ })
259
+ }
260
+
261
+ return false
262
+ }, [])
263
+
264
+ const checkIsSessionAllowed = useCallback((sessionId, tool, query) => {
265
+ const sid = sessionId ? String(sessionId) : '1'
266
+ const sessionSet = sessionAllowedMapRef.current.get(sid)
267
+ if (!sessionSet) return false
268
+
269
+ const target = extractToolTarget(tool, query)
270
+ const targetKey =
271
+ target.type === 'command'
272
+ ? `cmd:${target.value.toLowerCase()}`
273
+ : target.value
274
+ ? target.value.replace(/[\\/]+/g, '/').toLowerCase()
275
+ : tool
276
+ const folder = target.type === 'path' ? getFolderFromPath(targetKey) : ''
277
+
278
+ return sessionSet.has(targetKey) || (folder && sessionSet.has(folder))
279
+ }, [])
280
+
281
+ const requestApproval = useCallback(
282
+ async (message, tool, query, meta = {}) => {
283
+ const sid = meta.sessionId ? String(meta.sessionId) : '1'
284
+
285
+ // 1. Bypass otomatis jika is_auto_mode aktif pada sesi ini (in-memory ref ATAU database)
286
+ if (autoModeSessionsRef.current.has(sid)) {
287
+ return true
288
+ }
289
+ const isAuto = await getSessionAutoMode(sid)
290
+ if (isAuto) {
291
+ autoModeSessionsRef.current.add(sid)
292
+ return true
293
+ }
294
+
295
+ // 2. Bypass jika sudah diizinkan selamanya (whitelist permanen)
296
+ if (checkIsAlwaysAllowed(tool, query)) {
297
+ return true
298
+ }
299
+
300
+ // 3. Bypass jika sudah diizinkan untuk sesi ini (in-memory)
301
+ if (checkIsSessionAllowed(sid, tool, query)) {
302
+ return true
303
+ }
304
+
305
+ if (window.api?.tgBroadcastToAdmins) {
306
+ window.api.tgBroadcastToAdmins(
307
+ `[INFO]: Persetujuan Dibutuhkan\nTool: \`${tool}\`\n\n${message}\n\nKetik /accept untuk sekali, /session untuk sesi ini, /always untuk selamanya, atau /reject untuk menolak.`
308
+ )
309
+ }
310
+
311
+ const approvalId = `approval_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`
312
+
313
+ return new Promise((resolve) => {
314
+ const dataObj = {
315
+ id: approvalId,
316
+ message,
317
+ tool,
318
+ query,
319
+ meta,
320
+ status: 'pending',
321
+ resolve
322
+ }
323
+ approvalRef.current = dataObj
324
+ setApprovalData(dataObj)
325
+ })
326
+ },
327
+ [checkIsAlwaysAllowed, checkIsSessionAllowed]
328
+ )
329
+
330
+ const activeTargetInfo = approvalData
331
+ ? extractToolTarget(approvalData.tool, approvalData.query)
332
+ : null
333
+
334
+ return (
335
+ <ApprovalContext.Provider
336
+ value={{
337
+ requestApproval,
338
+ resolveApproval,
339
+ activeApproval: approvalData,
340
+ alwaysAllowedPaths,
341
+ setAlwaysAllowedPaths
342
+ }}
343
+ >
344
+ {children}
345
+
346
+ {/* Floating Dialog: Hanya ditampilkan di MarkHome atau halaman non-ChatStudio */}
347
+ {approvalData && !isChatStudio && (
348
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 backdrop-blur-sm animate-[response-fade-in_0.15s_ease-out_forwards]">
349
+ <div className="bg-base-200 border border-warning/30 p-6 rounded-2xl shadow-2xl max-w-lg w-full">
350
+ <div className="flex items-center justify-between gap-2 mb-2">
351
+ <h3 className="text-base font-bold text-warning flex items-center gap-2">
352
+ <ShieldAlert className="w-5 h-5 text-warning" /> Mark Meminta Izin
353
+ </h3>
354
+ {approvalData.tool && (
355
+ <span className="badge badge-sm font-mono bg-warning/15 text-warning border-warning/30 font-semibold">
356
+ {approvalData.tool}
357
+ </span>
358
+ )}
359
+ </div>
360
+
361
+ <p className="mb-3 text-xs text-base-content/70">
362
+ Mark membutuhkan persetujuan Anda untuk mengeksekusi aksi berikut.
363
+ </p>
364
+
365
+ <div className="bg-base-300 p-3.5 rounded-xl overflow-x-auto max-h-56 overflow-y-auto shadow-inner border border-white/5 mb-4 text-xs font-mono text-base-content/90 custom-scrollbar">
366
+ {activeTargetInfo?.value ? (
367
+ <div className="space-y-1.5">
368
+ <div className="text-[10px] uppercase font-bold text-warning/80 flex items-center gap-1.5 select-none">
369
+ {activeTargetInfo.type === 'command' ? (
370
+ <>
371
+ <Terminal className="w-3.5 h-3.5" />
372
+ <span>Perintah Shell:</span>
373
+ </>
374
+ ) : (
375
+ <>
376
+ <FileCode className="w-3.5 h-3.5" />
377
+ <span>Target Berkas:</span>
378
+ </>
379
+ )}
380
+ </div>
381
+ <div className="text-white whitespace-pre-wrap break-all">
382
+ {activeTargetInfo.value}
383
+ </div>
384
+ </div>
385
+ ) : (
386
+ <div className="whitespace-pre-wrap break-all">{approvalData.message}</div>
387
+ )}
388
+ </div>
389
+
390
+ <div className="flex flex-wrap items-center justify-between gap-2 mt-4">
391
+ <button
392
+ className="btn btn-ghost btn-sm text-error hover:bg-error/10 border border-error/20 cursor-pointer"
393
+ onClick={() => resolveApproval(approvalData.id, 'reject')}
394
+ >
395
+ Tolak
396
+ </button>
397
+ <div className="flex flex-wrap items-center gap-2">
398
+ <button
399
+ className="btn btn-outline btn-sm cursor-pointer"
400
+ onClick={() => resolveApproval(approvalData.id, 'approve_once')}
401
+ >
402
+ Izinkan Sekali
403
+ </button>
404
+ <button
405
+ className="btn btn-outline btn-warning btn-sm cursor-pointer"
406
+ onClick={() => resolveApproval(approvalData.id, 'approve_session')}
407
+ >
408
+ Izinkan Sesi Ini
409
+ </button>
410
+ <button
411
+ className="btn btn-error btn-sm shadow-md cursor-pointer font-semibold"
412
+ onClick={() => resolveApproval(approvalData.id, 'approve_always')}
413
+ >
414
+ Izinkan Selamanya
415
+ </button>
416
+ </div>
417
+ </div>
418
+ </div>
419
+ </div>
420
+ )}
421
+ </ApprovalContext.Provider>
422
+ )
423
+ }
424
+
425
+ export const useApproval = () => useContext(ApprovalContext)
@@ -0,0 +1,61 @@
1
+ import { useContext, createContext } from 'react'
2
+ import { useMarkAgent } from '../hooks/useMarkAgent'
3
+
4
+ const ChatContext = createContext()
5
+
6
+ export const ChatProvider = ({ children }) => {
7
+ const markAgent = useMarkAgent()
8
+
9
+ return <ChatContext.Provider value={markAgent}>{children}</ChatContext.Provider>
10
+ }
11
+
12
+ const defaultChatContext = {
13
+ chatData: [],
14
+ setChatData: () => {},
15
+ clearChat: () => {},
16
+ message: '',
17
+ setMessage: () => {},
18
+ isLoading: false,
19
+ setIsLoading: () => {},
20
+ isAgentBusy: false,
21
+ setIsAgentBusy: () => {},
22
+ runningSessionId: null,
23
+ setRunningSessionId: () => {},
24
+ runningSessionIds: [],
25
+ setRunningSessionIds: () => {},
26
+ addRunningSessionId: () => {},
27
+ removeRunningSessionId: () => {},
28
+ isSpeak: false,
29
+ setIsSpeak: () => {},
30
+ orbStatus: 'idle',
31
+ setOrbStatus: () => {},
32
+ currentResponse: null,
33
+ setCurrentResponse: () => {},
34
+ notifications: [],
35
+ pushNotification: () => {},
36
+ activeProcesses: [],
37
+ setActiveProcesses: () => {},
38
+ pushProcess: () => {},
39
+ dismissProcess: () => {},
40
+ inputSource: 'pc',
41
+ setInputSource: () => {},
42
+ handlePlanningCommand: () => {},
43
+ handleIntervention: () => {},
44
+ handleStop: () => {},
45
+ handleSubmit: () => {},
46
+ isBooting: false,
47
+ isRecording: false,
48
+ isProcessing: false,
49
+ audioIntensity: 0,
50
+ startRecording: () => {},
51
+ stopRecording: () => {},
52
+ toggleRecording: () => {},
53
+ toastMessage: null,
54
+ currentActiveSessionId: '1',
55
+ setCurrentActiveSessionId: () => {}
56
+ }
57
+
58
+ export const useChat = () => {
59
+ const context = useContext(ChatContext)
60
+ return context || defaultChatContext
61
+ }