@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,780 @@
1
+ import { fetchAI } from '../ai/core'
2
+ import { subagentStore } from './subagentStore'
3
+ import { buildSubagentSystemPrompt } from './subagentPrompt'
4
+ import { core_tools_schema } from '../tools/core-tools'
5
+ import { webApi } from '../web-bridge.js'
6
+ import {
7
+ MAX_CONTEXT_TOKENS,
8
+ MAX_CONTEXT_CHARS,
9
+ IN_LOOP_COMPACT_THRESHOLD,
10
+ checkAndCompressInLoop
11
+ } from '../ai/contextManager'
12
+
13
+ // Registry AbortController aktif per sub-agent
14
+ const subagentAbortControllers = new Map()
15
+
16
+ /**
17
+ * Menjalankan satu putaran eksekusi ReAct untuk sub-agent menggunakan Native Function Calling & SSE Streaming.
18
+ */
19
+ export async function runSubagentTurn(subagentId, incomingMessage = null, senderType = 'mark') {
20
+ const subagent = await subagentStore.getSubagent(subagentId)
21
+ if (!subagent) {
22
+ return { success: false, error: 'Sub-agent tidak ditemukan.' }
23
+ }
24
+
25
+ if (subagent.status === 'completed' || subagent.status === 'killed') {
26
+ // Jika ada pesan baru ke subagent yang sudah selesai, hidupkan kembali (re-activate)
27
+ await subagentStore.updateSubagent(subagentId, { status: 'running' })
28
+ }
29
+
30
+ // Rekam pesan masuk jika ada
31
+ if (incomingMessage) {
32
+ const isUser = senderType === 'user'
33
+ const isPeer = senderType === 'subagent' || senderType === 'peer'
34
+ const tag = isUser
35
+ ? '[DARI CREATOR / USER]:'
36
+ : isPeer
37
+ ? '[DARI SESAMA SUB-AGENT]:'
38
+ : '[DARI LEAD AGENT (MARK)]:'
39
+ await subagentStore.addMessage(subagentId, {
40
+ sender: isUser ? 'user' : isPeer ? 'peer' : 'mark',
41
+ role: 'user',
42
+ content: `${tag} ${incomingMessage}`
43
+ })
44
+ }
45
+
46
+ // Siapkan AbortController
47
+ const abortController = new AbortController()
48
+ subagentAbortControllers.set(subagentId, abortController)
49
+ await subagentStore.updateSubagent(subagentId, { status: 'running' })
50
+
51
+ // Filter tools OpenAPI schema yang diizinkan untuk sub-agent ini
52
+ const forbiddenTools = ['spawn_subagent', 'kill_subagent', 'wait_subagents']
53
+ const specificAllowed =
54
+ Array.isArray(subagent.allowedTools) &&
55
+ subagent.allowedTools.length > 0 &&
56
+ !subagent.allowedTools.includes('*') &&
57
+ subagent.allowedTools.some((t) => t && t.trim() !== '')
58
+ ? subagent.allowedTools.map((t) => t.trim())
59
+ : null
60
+
61
+ const allowedSchemas = []
62
+ const registeredToolNames = new Set()
63
+
64
+ // 1. Core tools
65
+ for (const t of core_tools_schema) {
66
+ const name = t.function?.name
67
+ if (!name || forbiddenTools.includes(name) || registeredToolNames.has(name)) continue
68
+ if (specificAllowed && !specificAllowed.includes(name)) continue
69
+ allowedSchemas.push(t)
70
+ registeredToolNames.add(name)
71
+ }
72
+
73
+ // 2. Group tools
74
+ const groupToolsData = await webApi.getGroupTools()
75
+ const groupToolsSchema = groupToolsData?.schema || {}
76
+ for (const group of Object.values(groupToolsSchema)) {
77
+ for (const t of group.tools || []) {
78
+ const name = t.function?.name
79
+ if (!name || forbiddenTools.includes(name) || registeredToolNames.has(name)) continue
80
+ if (specificAllowed && !specificAllowed.includes(name)) continue
81
+ allowedSchemas.push(t)
82
+ registeredToolNames.add(name)
83
+ }
84
+ }
85
+
86
+ const systemPrompt = buildSubagentSystemPrompt({
87
+ role: subagent.role,
88
+ goal: subagent.goal
89
+ })
90
+
91
+ let currentTurn = subagent.turnCount || 0
92
+ let latestSubagentReply = ''
93
+
94
+ try {
95
+ while (!abortController.signal.aborted) {
96
+ currentTurn++
97
+ await subagentStore.updateSubagent(subagentId, { turnCount: currentTurn })
98
+
99
+ if (abortController.signal.aborted) {
100
+ break
101
+ }
102
+
103
+ // Ambil seluruh riwayat pesan sub-agent dari database
104
+ const history = await subagentStore.getMessages(subagentId)
105
+ let messagesPayload = [
106
+ { role: 'system', content: systemPrompt },
107
+ ...history.map((m) => {
108
+ let textContent = m.content
109
+ if (typeof textContent === 'string' && textContent.startsWith('[')) {
110
+ try {
111
+ const parsed = JSON.parse(textContent)
112
+ if (Array.isArray(parsed) && parsed.length > 0 && parsed[0]?.type) {
113
+ textContent = parsed
114
+ }
115
+ } catch {
116
+ /* ignore */
117
+ }
118
+ } else if (Array.isArray(textContent)) {
119
+ // Sudah berbentuk array multimodal
120
+ } else if (typeof textContent === 'object' && textContent !== null) {
121
+ textContent =
122
+ textContent.answer ||
123
+ textContent.content ||
124
+ textContent.message ||
125
+ JSON.stringify(textContent)
126
+ }
127
+
128
+ // Jika ada turn assistant kosong dan tanpa tool_calls, ubah menjadi user turn dengan prefix
129
+ if (
130
+ (m.role === 'assistant' || m.role === 'model') &&
131
+ !textContent &&
132
+ (!m.tool_calls || m.tool_calls.length === 0)
133
+ ) {
134
+ return {
135
+ role: 'user',
136
+ content: '[Catatan Sistem]: Lanjutkan analisis dan langkah kerja berikutnya.'
137
+ }
138
+ }
139
+
140
+ return {
141
+ role: m.role,
142
+ content: textContent,
143
+ ...(m.tool_calls ? { tool_calls: m.tool_calls } : {}),
144
+ ...(m.tool_call_id ? { tool_call_id: m.tool_call_id } : {})
145
+ }
146
+ })
147
+ ]
148
+
149
+ if (
150
+ messagesPayload.length > 1 &&
151
+ messagesPayload[messagesPayload.length - 1].role === 'assistant'
152
+ ) {
153
+ messagesPayload.push({
154
+ role: 'user',
155
+ content: '[Instruksi Lanjutan]: Lanjutkan giliran kerjamu.'
156
+ })
157
+ }
158
+
159
+ // In-Loop Context Guard (Hermes 50% Threshold + O(n) Pruning untuk Sub-Agent)
160
+ try {
161
+ const inLoopResult = await checkAndCompressInLoop({
162
+ loopMessages: messagesPayload,
163
+ sessionId: `subagent_${subagentId}`,
164
+ maxTokens: MAX_CONTEXT_TOKENS,
165
+ maxChars: MAX_CONTEXT_CHARS,
166
+ thresholdRatio: IN_LOOP_COMPACT_THRESHOLD,
167
+ protectLastN: 6,
168
+ protectFirstN: 1, // Pertahankan system prompt subagent
169
+ activeConfig: {}
170
+ })
171
+
172
+ if (inLoopResult?.compressed && inLoopResult.loopMessages) {
173
+ messagesPayload = inLoopResult.loopMessages
174
+ }
175
+ } catch (subErr) {
176
+ console.warn(`[subagentExecutor:${subagentId}] In-loop compaction warning:`, subErr)
177
+ }
178
+
179
+ let turnReasoning = ''
180
+ let turnContent = ''
181
+
182
+ const streamResult = await fetchAI(messagesPayload, true, {
183
+ sessionId: `subagent_${subagentId}`,
184
+ tools: allowedSchemas,
185
+ signal: abortController.signal,
186
+ onReasoning: (chunk) => {
187
+ turnReasoning += chunk
188
+ },
189
+ onToken: (token) => {
190
+ turnContent += token
191
+ }
192
+ })
193
+
194
+ let effectiveToolCalls = streamResult.toolCalls
195
+ if (
196
+ (!effectiveToolCalls || effectiveToolCalls.length === 0) &&
197
+ (streamResult.content || turnContent)
198
+ ) {
199
+ const checkText = (streamResult.content || turnContent || '').trim()
200
+ const rawMatch = checkText.match(/```(?:json)?\s*([\s\S]*?)\s*```/)
201
+ let cand = (rawMatch ? rawMatch[1] : checkText).trim()
202
+ const firstBrace = cand.indexOf('{')
203
+ const lastBrace = cand.lastIndexOf('}')
204
+ if (firstBrace !== -1 && lastBrace > firstBrace) {
205
+ cand = cand.substring(firstBrace, lastBrace + 1).trim()
206
+ }
207
+ if (
208
+ cand.includes('"tool_calls"') ||
209
+ cand.includes('"action"') ||
210
+ cand.includes('"tool"') ||
211
+ cand.includes('"name"')
212
+ ) {
213
+ try {
214
+ const { jsonrepair } = await import('jsonrepair')
215
+ let pObj = null
216
+ try {
217
+ pObj = JSON.parse(cand)
218
+ } catch (_) {
219
+ pObj = JSON.parse(jsonrepair(cand))
220
+ }
221
+ if (pObj) {
222
+ if (Array.isArray(pObj.tool_calls) && pObj.tool_calls.length > 0) {
223
+ effectiveToolCalls = pObj.tool_calls.map((tc, idx) => ({
224
+ id: tc.id || `call_subagent_${Date.now()}_${idx}`,
225
+ type: 'function',
226
+ function: {
227
+ name: tc.name || tc.function?.name,
228
+ arguments:
229
+ typeof tc.arguments === 'object'
230
+ ? JSON.stringify(tc.arguments)
231
+ : String(tc.arguments || '{}')
232
+ }
233
+ }))
234
+ } else if (Array.isArray(pObj.action) && pObj.action.length > 0) {
235
+ effectiveToolCalls = pObj.action
236
+ .filter((act) => act && (act.tool || act.name))
237
+ .map((act, idx) => ({
238
+ id: `call_subagent_${Date.now()}_${idx}`,
239
+ type: 'function',
240
+ function: {
241
+ name: act.tool || act.name,
242
+ arguments:
243
+ typeof act.arguments === 'object'
244
+ ? JSON.stringify(act.arguments)
245
+ : typeof act.query === 'object'
246
+ ? JSON.stringify(act.query)
247
+ : JSON.stringify(
248
+ act.query !== undefined
249
+ ? { query: act.query }
250
+ : act.arguments !== undefined
251
+ ? { query: act.arguments }
252
+ : {}
253
+ )
254
+ }
255
+ }))
256
+ } else if (Array.isArray(pObj) && pObj.length > 0) {
257
+ effectiveToolCalls = pObj
258
+ .filter((item) => item && (item.name || item.tool || item.function?.name))
259
+ .map((item, idx) => ({
260
+ id: item.id || `call_subagent_${Date.now()}_${idx}`,
261
+ type: 'function',
262
+ function: {
263
+ name: item.name || item.tool || item.function?.name,
264
+ arguments:
265
+ typeof item.arguments === 'object'
266
+ ? JSON.stringify(item.arguments)
267
+ : typeof item.query === 'object'
268
+ ? JSON.stringify(item.query)
269
+ : String(
270
+ item.arguments ||
271
+ (item.query !== undefined
272
+ ? JSON.stringify({ query: item.query })
273
+ : '{}')
274
+ )
275
+ }
276
+ }))
277
+ } else if (pObj.action && (pObj.action.tool || pObj.action.name)) {
278
+ effectiveToolCalls = [
279
+ {
280
+ id: `call_subagent_${Date.now()}_0`,
281
+ type: 'function',
282
+ function: {
283
+ name: pObj.action.tool || pObj.action.name,
284
+ arguments:
285
+ typeof pObj.action.arguments === 'object'
286
+ ? JSON.stringify(pObj.action.arguments)
287
+ : typeof pObj.action.query === 'object'
288
+ ? JSON.stringify(pObj.action.query)
289
+ : JSON.stringify(
290
+ pObj.action.query !== undefined ? { query: pObj.action.query } : {}
291
+ )
292
+ }
293
+ }
294
+ ]
295
+ } else if (pObj.tool) {
296
+ effectiveToolCalls = [
297
+ {
298
+ id: `call_subagent_${Date.now()}_0`,
299
+ type: 'function',
300
+ function: {
301
+ name: pObj.tool,
302
+ arguments:
303
+ typeof pObj.query === 'object'
304
+ ? JSON.stringify(pObj.query)
305
+ : JSON.stringify(
306
+ pObj.query !== undefined
307
+ ? { query: pObj.query }
308
+ : pObj.arguments || {}
309
+ )
310
+ }
311
+ }
312
+ ]
313
+ }
314
+ }
315
+ } catch (_) {}
316
+ }
317
+ }
318
+
319
+ // KONDISI 1: Sub-Agent Memanggil Native Tool Calls
320
+ if (effectiveToolCalls && effectiveToolCalls.length > 0) {
321
+ const assistantMsg = {
322
+ sender: 'subagent',
323
+ role: 'assistant',
324
+ content: streamResult.content || null,
325
+ thought: turnReasoning || null,
326
+ tool_calls: effectiveToolCalls
327
+ }
328
+ await subagentStore.addMessage(subagentId, assistantMsg)
329
+ const turnVisualUrls = []
330
+
331
+ for (const tc of effectiveToolCalls) {
332
+ const toolName = tc.function?.name
333
+ let parsedArgs = {}
334
+ try {
335
+ parsedArgs = JSON.parse(tc.function?.arguments || '{}')
336
+ } catch {
337
+ parsedArgs = { raw: tc.function?.arguments || '' }
338
+ }
339
+
340
+ if (!toolName || abortController.signal.aborted) continue
341
+
342
+ let resultString = ''
343
+
344
+ try {
345
+ let res
346
+ if (toolName === 'message_agent') {
347
+ const targetQuery = parsedArgs.target_agent || parsedArgs.targetAgent || ''
348
+ const msgText = parsedArgs.message || ''
349
+
350
+ if (!targetQuery || !msgText) {
351
+ res = {
352
+ success: false,
353
+ error:
354
+ 'Parameter message_agent tidak lengkap (target_agent dan message wajib ada).'
355
+ }
356
+ } else {
357
+ const allAgents = await subagentStore.listSubagents()
358
+ const targetAgent = allAgents.find(
359
+ (s) =>
360
+ s.id === targetQuery ||
361
+ s.name.toLowerCase() === targetQuery.toLowerCase() ||
362
+ s.name.toLowerCase().replace(/^@/, '') ===
363
+ targetQuery.toLowerCase().replace(/^@/, '')
364
+ )
365
+
366
+ if (!targetAgent) {
367
+ res = {
368
+ success: false,
369
+ error: `Sub-agent '${targetQuery}' tidak ditemukan. Daftar agen yang tersedia: ${allAgents.map((a) => `@${a.name}`).join(', ')}`
370
+ }
371
+ } else if (targetAgent.id === subagentId) {
372
+ res = {
373
+ success: false,
374
+ error: 'Dilarang mengirim message_agent ke diri sendiri.'
375
+ }
376
+ } else {
377
+ // Jalankan turn pada sub-agent target
378
+ const peerResult = await runSubagentTurn(
379
+ targetAgent.id,
380
+ `[PESAN DARI @${subagent.name}]: ${msgText}`,
381
+ 'subagent'
382
+ )
383
+ if (peerResult.success) {
384
+ res = {
385
+ success: true,
386
+ data: `[JAWABAN DARI @${targetAgent.name}]:\n"${peerResult.reply}"\n${peerResult.thought ? `(Reasoning: ${peerResult.thought})` : ''}`
387
+ }
388
+ } else {
389
+ res = {
390
+ success: false,
391
+ error: `Sub-agent @${targetAgent.name} error: ${peerResult.error}`
392
+ }
393
+ }
394
+ }
395
+ }
396
+ } else if (toolName === 'report_to_lead') {
397
+ const summary = parsedArgs.summary || 'Misi telah selesai.'
398
+ const artifact = parsedArgs.artifact || null
399
+ const parentSessionId = String(subagent.parentSessionId || '1')
400
+ const parentSessionTitle =
401
+ subagent.parentSessionTitle ||
402
+ (parentSessionId === '1' ? 'Main Thread' : `Sesi #${parentSessionId}`)
403
+
404
+ // Broadcast push notification ke WebSocket Hub jika tersedia
405
+ try {
406
+ if (window.api && window.api.broadcastWsEvent) {
407
+ window.api.broadcastWsEvent('subagent:report', {
408
+ subagentId,
409
+ subagentName: subagent.name,
410
+ role: subagent.role,
411
+ summary,
412
+ artifact,
413
+ parentSessionId,
414
+ parentSessionTitle,
415
+ timestamp: Date.now()
416
+ })
417
+ }
418
+ } catch {
419
+ /* ignore */
420
+ }
421
+
422
+ // Simpan record report ke subagent
423
+ await subagentStore.updateSubagent(subagentId, {
424
+ status: 'completed',
425
+ finalAnswer: summary
426
+ })
427
+
428
+ res = {
429
+ success: true,
430
+ data: `[LAPORAN TERKIRIM KE LEAD AGENT (MARK)]\nLaporan berhasil disampaikan ke sesi "${parentSessionTitle}". Mark telah menerima push notification.`
431
+ }
432
+ } else if (toolName === 'read-tools') {
433
+ const { definition: groups = {} } = await webApi.getGroupTools()
434
+ const groupName = (parsedArgs.group_name || '').trim()
435
+ if (!groupName) {
436
+ res = {
437
+ success: false,
438
+ error: 'Harap sebutkan nama_grup (misal: "advanced_browser").'
439
+ }
440
+ } else if (groups[groupName]) {
441
+ const formatted = Object.entries(groups[groupName].tools)
442
+ .map(([k, v]) => `- ${k}: ${v}`)
443
+ .join('\n')
444
+ res = {
445
+ success: true,
446
+ data: `[PANDUAN TOOL ${groupName.toUpperCase()}]:\n${formatted}`
447
+ }
448
+ } else {
449
+ res = { success: false, error: `Grup tool '${groupName}' tidak ditemukan.` }
450
+ }
451
+ } else if (toolName === 'memory-search') {
452
+ const { executeMemorySearch } = await import('../vectorMemory.js')
453
+ const formatted = await executeMemorySearch(parsedArgs.query || '')
454
+ res = { success: true, data: formatted }
455
+ } else if (toolName === 'analyze-screen') {
456
+ try {
457
+ const screens = await window.api?.takeScreenshot?.()
458
+ const screenArray = Array.isArray(screens) ? screens : screens ? [screens] : []
459
+ if (screenArray.length > 0) {
460
+ const promptText =
461
+ parsedArgs?.query ||
462
+ parsedArgs?.prompt ||
463
+ 'Jelaskan apa yang kamu lihat di monitor ini secara ringkas, fokus pada teks, editor kode, atau pesan error.'
464
+ const contentArray = [
465
+ { type: 'text', text: promptText },
466
+ ...screenArray.map((scr) => ({ type: 'image_url', image_url: { url: scr } }))
467
+ ]
468
+ const visionResponse = await fetchAI(
469
+ [{ role: 'user', content: contentArray }],
470
+ false,
471
+ {
472
+ isSmallTask: true
473
+ }
474
+ )
475
+ const textContent =
476
+ typeof visionResponse === 'object' && visionResponse.content
477
+ ? visionResponse.content
478
+ : String(visionResponse)
479
+ res = {
480
+ success: true,
481
+ data: `[Vision AI - analyze-screen]:\n${textContent}`,
482
+ imageUrls: screenArray
483
+ }
484
+ } else {
485
+ res = { success: false, error: 'Gagal mengambil tangkapan layar untuk analisis.' }
486
+ }
487
+ } catch (scrErr) {
488
+ res = { success: false, error: `Error analyze-screen: ${scrErr.message}` }
489
+ }
490
+ } else if (toolName === 'camera-look') {
491
+ try {
492
+ const cameraFrame = await window.api?.captureCameraFrame?.()
493
+ if (cameraFrame) {
494
+ const promptText =
495
+ parsedArgs?.query ||
496
+ parsedArgs?.prompt ||
497
+ 'Jelaskan apa yang terlihat di depan kamera secara ringkas.'
498
+ const contentArray = [
499
+ { type: 'text', text: promptText },
500
+ { type: 'image_url', image_url: { url: cameraFrame } }
501
+ ]
502
+ const visionResponse = await fetchAI(
503
+ [{ role: 'user', content: contentArray }],
504
+ false,
505
+ {
506
+ isSmallTask: true
507
+ }
508
+ )
509
+ const textContent =
510
+ typeof visionResponse === 'object' && visionResponse.content
511
+ ? visionResponse.content
512
+ : String(visionResponse)
513
+ res = {
514
+ success: true,
515
+ data: `[Vision AI - camera-look]:\n${textContent}`,
516
+ imageUrls: [cameraFrame]
517
+ }
518
+ } else {
519
+ res = {
520
+ success: false,
521
+ error: 'Kamera tidak aktif atau tidak dapat mengambil frame.'
522
+ }
523
+ }
524
+ } catch (camErr) {
525
+ res = { success: false, error: `Error camera-look: ${camErr.message}` }
526
+ }
527
+ } else if (window.api && window.api.executeNativeTool) {
528
+ res = await window.api.executeNativeTool(toolName, parsedArgs, {
529
+ sessionId: subagentId
530
+ })
531
+ if (res && res.success && res.dataUrl) {
532
+ if (toolName === 'read-image') {
533
+ const promptText =
534
+ parsedArgs?.query ||
535
+ parsedArgs?.prompt ||
536
+ 'Jelaskan apa yang kamu lihat pada gambar ini secara rinci.'
537
+ try {
538
+ const visionResponse = await fetchAI(
539
+ [
540
+ {
541
+ role: 'user',
542
+ content: [
543
+ { type: 'text', text: promptText },
544
+ { type: 'image_url', image_url: { url: res.dataUrl } }
545
+ ]
546
+ }
547
+ ],
548
+ false,
549
+ { isSmallTask: true }
550
+ )
551
+ const textContent =
552
+ typeof visionResponse === 'object' && visionResponse.content
553
+ ? visionResponse.content
554
+ : String(visionResponse)
555
+ res.data = `[Vision AI - read-image] Analisis berkas '${res.filename || 'gambar'}':\n${textContent}`
556
+ } catch (vErr) {
557
+ res.data = `Berkas gambar '${res.filename || 'gambar'}' berhasil dibaca. (Analisis teks awal dilewati: ${vErr.message}). Gambar visual diteruskan ke observasi.`
558
+ }
559
+ } else if (toolName === 'browser-screenshot' && (parsedArgs?.query || res.query)) {
560
+ const promptText =
561
+ parsedArgs?.query || res.query || 'Jelaskan tampilan visual halaman web ini.'
562
+ try {
563
+ const visionResponse = await fetchAI(
564
+ [
565
+ {
566
+ role: 'user',
567
+ content: [
568
+ { type: 'text', text: promptText },
569
+ { type: 'image_url', image_url: { url: res.dataUrl } }
570
+ ]
571
+ }
572
+ ],
573
+ false,
574
+ { isSmallTask: true }
575
+ )
576
+ const textContent =
577
+ typeof visionResponse === 'object' && visionResponse.content
578
+ ? visionResponse.content
579
+ : String(visionResponse)
580
+ res.data = `[Vision AI - browser-screenshot] ${res.message || 'Screenshot berhasil diambil.'}\nHasil analisis:\n${textContent}`
581
+ } catch (vErr) {
582
+ res.data = `${res.message || 'Screenshot berhasil diambil.'} (Analisis teks awal dilewati: ${vErr.message}). Gambar visual diteruskan ke observasi.`
583
+ }
584
+ }
585
+ res.imageUrls = [res.dataUrl]
586
+ }
587
+ } else {
588
+ res = { success: false, error: 'IPC executeNativeTool tidak tersedia.' }
589
+ }
590
+
591
+ if (res && res.success) {
592
+ if (res.data !== undefined) {
593
+ resultString = typeof res.data === 'string' ? res.data : JSON.stringify(res.data)
594
+ } else if (res.output !== undefined) {
595
+ resultString =
596
+ typeof res.output === 'string' ? res.output : JSON.stringify(res.output)
597
+ } else if (res.result !== undefined) {
598
+ resultString =
599
+ typeof res.result === 'string' ? res.result : JSON.stringify(res.result)
600
+ } else if (res.content !== undefined) {
601
+ resultString =
602
+ typeof res.content === 'string' ? res.content : JSON.stringify(res.content)
603
+ } else if (res.contents !== undefined) {
604
+ resultString =
605
+ typeof res.contents === 'string' ? res.contents : JSON.stringify(res.contents)
606
+ } else {
607
+ resultString = res.message || 'Success'
608
+ }
609
+ } else {
610
+ resultString = `[ERROR] ${res?.message || res?.error || 'Unknown error'}`
611
+ }
612
+
613
+ if (Array.isArray(res?.imageUrls) && res.imageUrls.length > 0) {
614
+ for (const u of res.imageUrls) {
615
+ if (u && !turnVisualUrls.includes(u)) {
616
+ turnVisualUrls.push(u)
617
+ }
618
+ }
619
+ }
620
+ } catch (err) {
621
+ resultString = `[ERROR] Tool ${toolName} crash: ${err.message}`
622
+ }
623
+
624
+ // Catat pesan role 'tool'
625
+ await subagentStore.addMessage(subagentId, {
626
+ sender: 'tool',
627
+ role: 'tool',
628
+ tool_call_id: tc.id,
629
+ name: toolName,
630
+ content: resultString
631
+ })
632
+ }
633
+
634
+ // Jika ada tool visual yang dieksekusi, suntikkan observasi multimodal
635
+ if (turnVisualUrls.length > 0) {
636
+ await subagentStore.addMessage(subagentId, {
637
+ sender: 'user',
638
+ role: 'user',
639
+ content: [
640
+ {
641
+ type: 'text',
642
+ text: '[Visual Observation]: Berikut adalah gambar visual aktual beresolusi penuh dari eksekusi tool di atas untuk kamu analisis secara langsung:'
643
+ },
644
+ ...turnVisualUrls.map((url) => ({
645
+ type: 'image_url',
646
+ image_url: { url }
647
+ }))
648
+ ]
649
+ })
650
+ }
651
+
652
+ // Lanjut ke giliran berikutnya agar subagent mengevaluasi hasil tool
653
+ continue
654
+ }
655
+
656
+ // KONDISI 2: Sub-Agent Menyelesaikan Misi (Direct text answer / Finish reason: stop)
657
+ latestSubagentReply = streamResult.content || turnContent || 'Misi teknis selesai.'
658
+ await subagentStore.addMessage(subagentId, {
659
+ sender: 'subagent',
660
+ role: 'assistant',
661
+ content: latestSubagentReply,
662
+ thought: turnReasoning || null
663
+ })
664
+
665
+ await subagentStore.updateSubagent(subagentId, {
666
+ status: 'completed',
667
+ finalAnswer: latestSubagentReply
668
+ })
669
+
670
+ // Otomatis laporkan hasil akhir ke Lead Agent (Mark) jika subagent menjawab teks
671
+ try {
672
+ const parentSessionId = String(subagent.parentSessionId || '1')
673
+ const parentSessionTitle =
674
+ subagent.parentSessionTitle ||
675
+ (parentSessionId === '1' ? 'Main Thread' : `Sesi #${parentSessionId}`)
676
+
677
+ if (window.api && window.api.broadcastWsEvent) {
678
+ window.api.broadcastWsEvent('subagent:report', {
679
+ subagentId,
680
+ subagentName: subagent.name,
681
+ role: subagent.role,
682
+ summary: latestSubagentReply,
683
+ artifact: null,
684
+ parentSessionId,
685
+ parentSessionTitle,
686
+ timestamp: Date.now()
687
+ })
688
+ }
689
+ } catch (reportErr) {
690
+ console.warn('[subagentExecutor] Gagal auto-report ke Lead Agent:', reportErr)
691
+ }
692
+
693
+ return {
694
+ success: true,
695
+ subagentId,
696
+ reply: latestSubagentReply,
697
+ thought: turnReasoning || '',
698
+ turnCount: currentTurn
699
+ }
700
+ }
701
+
702
+ // Jika turn berakhir secara alami tanpa pemanggilan tool di turn terakhir (status completed/selesai)
703
+ await subagentStore.updateSubagent(subagentId, {
704
+ status: 'completed',
705
+ finalAnswer: latestSubagentReply || 'Misi sub-agent selesai.'
706
+ })
707
+
708
+ if (latestSubagentReply) {
709
+ try {
710
+ const parentSessionId = String(subagent.parentSessionId || '1')
711
+ const parentSessionTitle =
712
+ subagent.parentSessionTitle ||
713
+ (parentSessionId === '1' ? 'Main Thread' : `Sesi #${parentSessionId}`)
714
+
715
+ if (window.api && window.api.broadcastWsEvent) {
716
+ window.api.broadcastWsEvent('subagent:report', {
717
+ subagentId,
718
+ subagentName: subagent.name,
719
+ role: subagent.role,
720
+ summary: latestSubagentReply,
721
+ artifact: null,
722
+ parentSessionId,
723
+ parentSessionTitle,
724
+ timestamp: Date.now()
725
+ })
726
+ }
727
+ } catch {
728
+ /* ignore */
729
+ }
730
+ }
731
+
732
+ return {
733
+ success: true,
734
+ subagentId,
735
+ reply: latestSubagentReply || 'Misi selesai.',
736
+ turnCount: currentTurn
737
+ }
738
+ } catch (err) {
739
+ if (abortController.signal.aborted) {
740
+ await subagentStore.updateSubagent(subagentId, { status: 'killed' })
741
+ return { success: false, subagentId, error: 'Eksekusi dibatalkan oleh pengguna.' }
742
+ }
743
+ console.error(`[Subagent Execution Error on ${subagentId}]:`, err)
744
+ // Rekam pesan error agar terlihat langsung di Agent Workspace UI
745
+ await subagentStore.addMessage(subagentId, {
746
+ sender: 'system',
747
+ role: 'system',
748
+ content: `[ERROR EKSEKUSI]: ${err.message || 'Terjadi kesalahan tidak terduga saat memproses AI.'}`
749
+ })
750
+ await subagentStore.updateSubagent(subagentId, {
751
+ status: 'failed',
752
+ finalAnswer: `Error: ${err.message}`
753
+ })
754
+ return { success: false, subagentId, error: err.message }
755
+ } finally {
756
+ subagentAbortControllers.delete(subagentId)
757
+ if (window.api && window.api.executeNativeTool) {
758
+ window.api.executeNativeTool('browser-close', '', { sessionId: subagentId }).catch(() => {})
759
+ }
760
+ }
761
+ }
762
+
763
+ /**
764
+ * Membatalkan paksa eksekusi sub-agent yang sedang berjalan
765
+ * @param {string} subagentId ID sub-agent
766
+ * @param {boolean} isDeleting Apakah pembatalan ini karena penghapusan entitas
767
+ */
768
+ export function killSubagentExecution(subagentId, isDeleting = false) {
769
+ const ctrl = subagentAbortControllers.get(subagentId)
770
+ if (ctrl) {
771
+ ctrl.abort()
772
+ subagentAbortControllers.delete(subagentId)
773
+ }
774
+ if (!isDeleting) {
775
+ subagentStore.updateSubagent(subagentId, { status: 'killed' }).catch(() => {})
776
+ }
777
+ if (window.api && window.api.executeNativeTool) {
778
+ window.api.executeNativeTool('browser-close', '', { sessionId: subagentId }).catch(() => {})
779
+ }
780
+ }