@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,572 @@
1
+ import React, { useState, useEffect, useRef } from 'react'
2
+ import Markdown from 'react-markdown'
3
+ import remarkGfm from 'remark-gfm'
4
+ import { CodeBlock } from '../Chat/CodeBlock'
5
+ import { subagentStore } from '../../api/subagent/subagentStore'
6
+ import {
7
+ Bot,
8
+ Send,
9
+ Square,
10
+ Terminal,
11
+ Brain,
12
+ X,
13
+ Clock,
14
+ ArrowDown,
15
+ ChevronDown,
16
+ ChevronUp,
17
+ Loader2
18
+ } from 'lucide-react'
19
+ import { runSubagentTurn, killSubagentExecution } from '../../api/subagent/subagentExecutor'
20
+ import { useConfirm } from '../../hooks/useConfirm'
21
+
22
+ const markdownComponents = {
23
+ code({ node, inline, className, children, ...props }) {
24
+ const match = /language-(\w+)/.exec(className || '')
25
+ return !inline ? (
26
+ <CodeBlock match={match} children={children} />
27
+ ) : (
28
+ <code
29
+ className="bg-base-300/90 text-accent font-mono text-[11px] px-1.5 py-0.5 rounded border border-base-content/10"
30
+ {...props}
31
+ >
32
+ {children}
33
+ </code>
34
+ )
35
+ }
36
+ }
37
+
38
+ // Komponen Single Unified Bubble untuk Sub-Agent
39
+ function SubagentUnifiedBubble({ turn, subagentName, isRunning }) {
40
+ const [isThoughtOpen, setIsThoughtOpen] = useState(false)
41
+ const [isStepsOpen, setIsStepsOpen] = useState(false)
42
+ const [openStepIdx, setOpenStepIdx] = useState(null)
43
+
44
+ return (
45
+ <div className="chat chat-start animate-fade-in">
46
+ <div className="chat-image avatar placeholder">
47
+ <div className="w-8 h-8 rounded-2xl text-[10px] font-bold shadow-md flex items-center justify-center bg-primary/20 text-primary border border-primary/30">
48
+ SUB
49
+ </div>
50
+ </div>
51
+ <div className="chat-header text-[11px] opacity-50 mb-1 flex items-center gap-1.5">
52
+ <span>{subagentName}</span>
53
+ <span className="text-[10px]">
54
+ {new Date(turn.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
55
+ </span>
56
+ </div>
57
+
58
+ <div className="chat-bubble bg-base-200/95 text-base-content border border-base-content/15 shadow-xl max-w-[88%] rounded-2xl p-4 space-y-3.5">
59
+ {/* 1. Dropdown Thought (Reasoning Analisis) */}
60
+ {turn.thoughts && turn.thoughts.length > 0 && (
61
+ <div className="bg-base-300/80 rounded-xl border border-base-content/10 overflow-hidden text-xs select-none shadow-sm">
62
+ <button
63
+ type="button"
64
+ onClick={() => setIsThoughtOpen(!isThoughtOpen)}
65
+ className="w-full px-3.5 py-2.5 flex items-center justify-between hover:bg-base-content/5 transition-colors text-left"
66
+ >
67
+ <div className="flex items-center gap-2 font-semibold text-accent text-[11px]">
68
+ <Brain className="w-3.5 h-3.5 shrink-0" />
69
+ <span>
70
+ Pemikiran Sub-Agent
71
+ </span>
72
+ </div>
73
+ <div className="flex items-center gap-1 text-[10px] text-base-content/50">
74
+ <span>{isThoughtOpen ? 'Tutup' : 'Lihat'}</span>
75
+ {isThoughtOpen ? (
76
+ <ChevronUp className="w-3 h-3" />
77
+ ) : (
78
+ <ChevronDown className="w-3 h-3" />
79
+ )}
80
+ </div>
81
+ </button>
82
+ {isThoughtOpen && (
83
+ <div className="p-3.5 bg-base-100/90 border-t border-base-content/10 space-y-2 text-[11px] text-base-content/90 whitespace-pre-wrap leading-relaxed">
84
+ <div className="border-l-2 border-accent pl-2.5 py-0.5">
85
+ {turn.thoughts[turn.thoughts.length - 1] || ''}
86
+ </div>
87
+ </div>
88
+ )}
89
+ </div>
90
+ )}
91
+
92
+ {/* 2. Dropdown Langkah Eksekusi (Tools) */}
93
+ {turn.steps && turn.steps.length > 0 && (
94
+ <div className="bg-base-300/80 rounded-xl border border-base-content/10 overflow-hidden text-xs select-none shadow-sm">
95
+ <button
96
+ type="button"
97
+ onClick={() => setIsStepsOpen(!isStepsOpen)}
98
+ className="w-full px-3.5 py-2.5 flex items-center justify-between hover:bg-base-content/5 transition-colors text-left"
99
+ >
100
+ <div className="flex items-center gap-2 font-semibold text-warning text-[11px]">
101
+ <Terminal className="w-3.5 h-3.5 shrink-0" />
102
+ <span>Langkah Eksekusi ({turn.steps.length})</span>
103
+ </div>
104
+ <div className="flex items-center gap-1 text-[10px] text-base-content/50">
105
+ <span>{isStepsOpen ? 'Tutup' : 'Lihat'}</span>
106
+ {isStepsOpen ? (
107
+ <ChevronUp className="w-3 h-3" />
108
+ ) : (
109
+ <ChevronDown className="w-3 h-3" />
110
+ )}
111
+ </div>
112
+ </button>
113
+
114
+ {isStepsOpen && (
115
+ <div className="p-3.5 bg-base-100/90 border-t border-base-content/10 space-y-2 font-mono text-[11px]">
116
+ {turn.steps.map((step, idx) => {
117
+ const isObsOpen = openStepIdx === idx
118
+ return (
119
+ <div
120
+ key={idx}
121
+ className="space-y-1 bg-base-200/50 p-2 rounded-lg border border-base-content/5"
122
+ >
123
+ <div
124
+ onClick={() => step.observation && setOpenStepIdx(isObsOpen ? null : idx)}
125
+ className={`flex items-start gap-1.5 py-0.5 ${step.observation ? 'cursor-pointer hover:text-primary transition-colors' : ''}`}
126
+ >
127
+ <span className="text-warning font-semibold shrink-0">{idx + 1}.</span>
128
+ <span className="text-primary font-semibold shrink-0">{step.tool}</span>
129
+ <span className="text-base-content/40 shrink-0">:</span>
130
+ <span
131
+ className={`text-base-content/90 break-all flex-1 ${isObsOpen ? '' : 'truncate'}`}
132
+ >
133
+ {step.query || ''}
134
+ </span>
135
+ {step.observation &&
136
+ (isObsOpen ? (
137
+ <ChevronUp className="w-3 h-3" />
138
+ ) : (
139
+ <ChevronDown className="w-3 h-3" />
140
+ ))}
141
+ </div>
142
+
143
+ {isObsOpen && step.observation && (
144
+ <div className="p-2.5 bg-black/40 rounded-lg text-base-content/80 whitespace-pre-wrap break-all text-[10px] max-h-44 overflow-y-auto mt-1 border border-base-content/10 font-mono">
145
+ {(typeof step.observation === 'string' ? step.observation : JSON.stringify(step.observation)).replace(/^\[OBSERVATION\]:\s*/, '')}
146
+ </div>
147
+ )}
148
+ </div>
149
+ )
150
+ })}
151
+ </div>
152
+ )}
153
+ </div>
154
+ )}
155
+
156
+ {/* 3. Final Content / Answer or Loading */}
157
+ {turn.answer ? (
158
+ <div className="prose prose-invert prose-sm max-w-none text-xs leading-relaxed font-normal pt-1 text-base-content/95">
159
+ <Markdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
160
+ {turn.answer
161
+ .replace(/^\[DARI LEAD AGENT \(MARK\)\]:\s*/, '')
162
+ .replace(/^\[DARI CREATOR \/ USER \(MADA\)\]:\s*/, '')}
163
+ </Markdown>
164
+ </div>
165
+ ) : isRunning ? (
166
+ <div className="flex items-center gap-2 text-xs text-base-content/75 py-1 font-mono">
167
+ <Loader2 className="w-3.5 h-3.5 animate-spin text-primary shrink-0" />
168
+ <span className="text-[11px]">Memproses langkah...</span>
169
+ </div>
170
+ ) : null}
171
+ </div>
172
+ </div>
173
+ )
174
+ }
175
+
176
+ // Helper untuk mengelompokkan pesan mentah menjadi satu bubble per giliran
177
+ function groupSubagentMessages(rawMessages) {
178
+ if (!Array.isArray(rawMessages)) return []
179
+ const grouped = []
180
+ let currentSubTurn = null
181
+
182
+ for (const msg of rawMessages) {
183
+ if (msg.sender === 'user' || msg.sender === 'mark') {
184
+ if (currentSubTurn) {
185
+ grouped.push(currentSubTurn)
186
+ currentSubTurn = null
187
+ }
188
+ grouped.push({
189
+ type: 'dialogue',
190
+ id: msg.id,
191
+ sender: msg.sender,
192
+ content: msg.content,
193
+ timestamp: msg.timestamp
194
+ })
195
+ } else if (msg.sender === 'subagent') {
196
+ let parsed = {}
197
+ try {
198
+ parsed = typeof msg.content === 'object' ? msg.content : JSON.parse(msg.content)
199
+ } catch (e) {
200
+ parsed = { answer: msg.content }
201
+ }
202
+
203
+ const thought = msg.thought || parsed.thought
204
+ const action = msg.action || parsed.action
205
+ const answer = parsed.answer || (action ? null : msg.content)
206
+
207
+ if (!currentSubTurn) {
208
+ currentSubTurn = {
209
+ type: 'subagent_turn',
210
+ id: msg.id,
211
+ sender: 'subagent',
212
+ timestamp: msg.timestamp,
213
+ thoughts: [],
214
+ steps: [],
215
+ answer: null
216
+ }
217
+ }
218
+
219
+ if (thought && !currentSubTurn.thoughts.includes(thought)) {
220
+ currentSubTurn.thoughts.push(thought)
221
+ }
222
+
223
+ if (action) {
224
+ const acts = Array.isArray(action) ? action : [action]
225
+ acts.forEach((act) => {
226
+ if (act?.tool) {
227
+ currentSubTurn.steps.push({
228
+ tool: act.tool,
229
+ query: act.query || '',
230
+ observation: null
231
+ })
232
+ }
233
+ })
234
+ }
235
+
236
+ if (answer) {
237
+ currentSubTurn.answer = answer
238
+ }
239
+ } else if (msg.sender === 'tool') {
240
+ if (currentSubTurn && currentSubTurn.steps.length > 0) {
241
+ const lastStepWithoutObs = [...currentSubTurn.steps].reverse().find((s) => !s.observation)
242
+ if (lastStepWithoutObs) {
243
+ lastStepWithoutObs.observation = msg.content
244
+ } else {
245
+ currentSubTurn.steps[currentSubTurn.steps.length - 1].observation = msg.content
246
+ }
247
+ } else {
248
+ if (!currentSubTurn) {
249
+ currentSubTurn = {
250
+ type: 'subagent_turn',
251
+ id: msg.id,
252
+ sender: 'subagent',
253
+ timestamp: msg.timestamp,
254
+ thoughts: [],
255
+ steps: [{ tool: 'tool-execution', query: '', observation: msg.content }],
256
+ answer: null
257
+ }
258
+ }
259
+ }
260
+ }
261
+ }
262
+
263
+ if (currentSubTurn) {
264
+ grouped.push(currentSubTurn)
265
+ }
266
+
267
+ return grouped
268
+ }
269
+
270
+ export default function SubagentIntercom({ subagentId, onClose }) {
271
+ const [subagent, setSubagent] = useState(null)
272
+ const [messages, setMessages] = useState([])
273
+ const [inputText, setInputText] = useState('')
274
+ const [isSending, setIsSending] = useState(false)
275
+ const [showScrollBottomBtn, setShowScrollBottomBtn] = useState(false)
276
+
277
+ const messagesEndRef = useRef(null)
278
+ const containerRef = useRef(null)
279
+ const isAutoScrollRef = useRef(true)
280
+ const prevMsgCountRef = useRef(0)
281
+
282
+ const loadData = async () => {
283
+ if (!subagentId) return
284
+ try {
285
+ const sub = await subagentStore.getSubagent(subagentId)
286
+ const msgs = await subagentStore.getMessages(subagentId)
287
+ setSubagent(sub)
288
+ setMessages(msgs || [])
289
+ } catch (err) {
290
+ console.error('[SubagentIntercom] Load error:', err)
291
+ }
292
+ }
293
+
294
+ const scrollToBottom = (behavior = 'smooth') => {
295
+ if (messagesEndRef.current) {
296
+ messagesEndRef.current.scrollIntoView({ behavior })
297
+ isAutoScrollRef.current = true
298
+ setShowScrollBottomBtn(false)
299
+ }
300
+ }
301
+
302
+ const handleScroll = () => {
303
+ if (!containerRef.current) return
304
+ const { scrollTop, scrollHeight, clientHeight } = containerRef.current
305
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 60
306
+ isAutoScrollRef.current = isAtBottom
307
+ setShowScrollBottomBtn(!isAtBottom)
308
+ }
309
+
310
+ // Reset scroll dan muat data ketika subagentId berubah
311
+ useEffect(() => {
312
+ prevMsgCountRef.current = 0
313
+ isAutoScrollRef.current = true
314
+ setShowScrollBottomBtn(false)
315
+ loadData().then(() => {
316
+ setTimeout(() => scrollToBottom('auto'), 50)
317
+ })
318
+ const interval = setInterval(loadData, 1000)
319
+ return () => clearInterval(interval)
320
+ }, [subagentId])
321
+
322
+ // Hanya auto-scroll jika ada pesan baru DAN user sedang berada di posisi bawah
323
+ useEffect(() => {
324
+ if (messages.length > prevMsgCountRef.current) {
325
+ if (isAutoScrollRef.current) {
326
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
327
+ }
328
+ prevMsgCountRef.current = messages.length
329
+ }
330
+ }, [messages.length])
331
+
332
+ const { confirm, ModalComponent } = useConfirm()
333
+
334
+ const handleSendMessage = async (e) => {
335
+ e?.preventDefault()
336
+ if (!inputText.trim() || isSending) return
337
+
338
+ const textToSend = inputText.trim()
339
+ setInputText('')
340
+ setIsSending(true)
341
+ isAutoScrollRef.current = true
342
+ setShowScrollBottomBtn(false)
343
+
344
+ try {
345
+ await runSubagentTurn(subagentId, textToSend, 'user')
346
+ await loadData()
347
+ setTimeout(() => scrollToBottom('smooth'), 50)
348
+ } catch (err) {
349
+ console.error('[SubagentIntercom] Send error:', err)
350
+ } finally {
351
+ setIsSending(false)
352
+ }
353
+ }
354
+
355
+ const handleKill = async () => {
356
+ const result = await confirm({
357
+ title: 'Hentikan Eksekusi Sub-Agent',
358
+ message: 'Apakah kamu yakin ingin menghentikan eksekusi sub-agent ini secara paksa?',
359
+ isError: true,
360
+ confirmText: 'Hentikan',
361
+ cancelText: 'Batal'
362
+ })
363
+ if (result?.isConfirmed) {
364
+ killSubagentExecution(subagentId)
365
+ loadData()
366
+ }
367
+ }
368
+
369
+ if (!subagent) return null
370
+
371
+ const isRunning = subagent.status === 'running'
372
+ const groupedTurns = groupSubagentMessages(messages)
373
+
374
+ return (
375
+ <div className="flex flex-col h-full bg-base-300/60 rounded-3xl overflow-hidden select-none border border-base-content/10 relative font-['Poppins',sans-serif]">
376
+ {/* Header Intercom */}
377
+ <div className="p-4 bg-base-200/90 backdrop-blur-md border-b border-base-content/10 flex items-center justify-between flex-none z-10">
378
+ <div className="flex items-center gap-3">
379
+ <div className="p-2.5 bg-primary/20 text-primary rounded-2xl shadow-inner border border-primary/20">
380
+ <Bot className="w-5 h-5" />
381
+ </div>
382
+ <div>
383
+ <div className="flex items-center gap-2">
384
+ <h3 className="font-bold text-sm tracking-wide">{subagent.name}</h3>
385
+ <span
386
+ className={`badge badge-xs font-semibold uppercase tracking-wider ${
387
+ isRunning
388
+ ? 'badge-warning animate-pulse'
389
+ : subagent.status === 'idle'
390
+ ? 'badge-info'
391
+ : subagent.status === 'failed'
392
+ ? 'badge-error'
393
+ : 'badge-success'
394
+ }`}
395
+ >
396
+ {subagent.status}
397
+ </span>
398
+ <span className="text-[10px] text-base-content/40 font-mono">[{subagent.id}]</span>
399
+ </div>
400
+ <p className="text-xs text-base-content/60">{subagent.role}</p>
401
+ </div>
402
+ </div>
403
+
404
+ {/* Action Controls */}
405
+ <div className="flex items-center gap-2">
406
+ {isRunning && (
407
+ <button
408
+ onClick={handleKill}
409
+ className="btn btn-error btn-xs btn-outline gap-1 rounded-xl"
410
+ title="Hentikan Paksa"
411
+ >
412
+ <Square className="w-3 h-3" /> Stop
413
+ </button>
414
+ )}
415
+ {onClose && (
416
+ <button
417
+ onClick={onClose}
418
+ className="btn btn-ghost btn-sm btn-circle text-base-content/60 hover:text-base-content hover:bg-base-content/10"
419
+ title="Tutup Panel"
420
+ >
421
+ <X className="w-4 h-4" />
422
+ </button>
423
+ )}
424
+ </div>
425
+ </div>
426
+
427
+ {/* Goal & Mission Info Strip */}
428
+ <div className="px-5 py-3 bg-base-200/40 border-b border-base-content/5 text-xs flex items-center justify-between gap-4 flex-none z-10">
429
+ <div className="flex items-center gap-2 truncate">
430
+ <span className="font-bold text-primary shrink-0 uppercase text-[10px] tracking-wider">
431
+ Mission Goal:
432
+ </span>
433
+ <span className="text-base-content/80 truncate font-medium">{subagent.goal}</span>
434
+ </div>
435
+ <div className="flex items-center gap-1.5 text-[11px] text-base-content/40 font-mono shrink-0">
436
+ <Clock className="w-3 h-3" />
437
+ <span>Turns: {subagent.turnCount || 0}</span>
438
+ </div>
439
+ </div>
440
+
441
+ {/* Message Feed Container */}
442
+ <div
443
+ ref={containerRef}
444
+ onScroll={handleScroll}
445
+ className="flex-1 p-5 overflow-y-auto space-y-4 relative"
446
+ >
447
+ {groupedTurns.map((item, idx) => {
448
+ if (item.type === 'subagent_turn') {
449
+ const isLastTurn = idx === groupedTurns.length - 1
450
+ return (
451
+ <SubagentUnifiedBubble
452
+ key={item.id}
453
+ turn={item}
454
+ subagentName={subagent.name}
455
+ isRunning={isRunning && isLastTurn}
456
+ />
457
+ )
458
+ }
459
+
460
+ const isUser = item.sender === 'user'
461
+ const isMark = item.sender === 'mark'
462
+
463
+ return (
464
+ <div key={item.id} className="chat chat-end animate-fade-in">
465
+ <div className="chat-image avatar placeholder">
466
+ <div
467
+ className={`w-8 h-8 rounded-2xl text-[10px] font-bold shadow-md flex items-center justify-center ${
468
+ isUser
469
+ ? 'bg-accent text-accent-content border border-accent/40 shadow-accent/20'
470
+ : 'bg-primary text-primary-content border border-primary/30'
471
+ }`}
472
+ >
473
+ {isUser ? 'USER' : 'MARK'}
474
+ </div>
475
+ </div>
476
+ <div className="chat-header text-[11px] opacity-50 mb-1 flex items-center gap-1.5">
477
+ <span className={isUser ? 'text-accent font-semibold' : ''}>
478
+ {isUser ? 'Creator (Mada)' : 'Lead Agent (Mark)'}
479
+ </span>
480
+ <span className="text-[10px]">
481
+ {new Date(item.timestamp).toLocaleTimeString([], {
482
+ hour: '2-digit',
483
+ minute: '2-digit'
484
+ })}
485
+ </span>
486
+ </div>
487
+ <div
488
+ className={`chat-bubble text-xs leading-relaxed max-w-[85%] rounded-2xl shadow-md p-3.5 ${
489
+ isUser
490
+ ? 'bg-accent text-accent-content font-medium border border-accent/40 shadow-accent/20'
491
+ : 'bg-primary text-primary-content border border-primary/30'
492
+ }`}
493
+ >
494
+ <div className="whitespace-pre-wrap leading-relaxed font-normal">
495
+ {item.content
496
+ .replace(/^\[DARI LEAD AGENT \(MARK\)\]:\s*/, '')
497
+ .replace(/^\[DARI CREATOR \/ USER \(MADA\)\]:\s*/, '')}
498
+ </div>
499
+ </div>
500
+ </div>
501
+ )
502
+ })}
503
+
504
+ {/* Initial Loading State hanya jika belum ada bubble giliran sub-agent yang aktif */}
505
+ {isRunning &&
506
+ (groupedTurns.length === 0 ||
507
+ groupedTurns[groupedTurns.length - 1]?.type !== 'subagent_turn') && (
508
+ <div className="chat chat-start animate-fade-in">
509
+ <div className="chat-image avatar placeholder">
510
+ <div className="w-8 h-8 rounded-2xl text-[10px] font-bold shadow-md flex items-center justify-center bg-primary/20 text-primary border border-primary/30">
511
+ SUB
512
+ </div>
513
+ </div>
514
+ <div className="chat-header text-[11px] opacity-50 mb-1">
515
+ <span>{subagent.name}</span>
516
+ </div>
517
+ <div className="chat-bubble bg-base-200/95 text-base-content border border-base-content/15 text-xs flex items-center gap-2.5 py-2.5 px-4 shadow-md">
518
+ <Loader2 className="w-3.5 h-3.5 animate-spin text-primary" />
519
+ <span className="font-mono text-[11px] text-base-content/80">
520
+ Memproses langkah...
521
+ </span>
522
+ </div>
523
+ </div>
524
+ )}
525
+
526
+ <div ref={messagesEndRef} />
527
+ </div>
528
+
529
+ {/* Floating Scroll-to-Bottom Button */}
530
+ {showScrollBottomBtn && (
531
+ <button
532
+ onClick={() => scrollToBottom('smooth')}
533
+ className="absolute bottom-20 right-6 btn btn-circle btn-sm btn-primary shadow-xl shadow-primary/30 z-20 animate-bounce"
534
+ title="Scroll ke bawah"
535
+ >
536
+ <ArrowDown className="w-4 h-4" />
537
+ </button>
538
+ )}
539
+
540
+ {/* User Intervention Whisper Input */}
541
+ <form
542
+ onSubmit={handleSendMessage}
543
+ className="p-3.5 bg-base-200/90 border-t border-base-content/10 flex items-center gap-2 flex-none backdrop-blur-md z-10"
544
+ >
545
+ <input
546
+ type="text"
547
+ value={inputText}
548
+ onChange={(e) => setInputText(e.target.value)}
549
+ placeholder="Ketik instruksi/arahan langsung sebagai Creator (Mada)..."
550
+ disabled={isSending}
551
+ className="input input-sm input-bordered flex-1 rounded-xl bg-base-100/70 focus:bg-base-100 text-xs shadow-inner"
552
+ />
553
+ <button
554
+ type="submit"
555
+ disabled={!inputText.trim() || isSending}
556
+ className="btn btn-accent btn-sm rounded-xl px-4 gap-1.5 shadow-md shadow-accent/20 font-semibold"
557
+ >
558
+ {isSending ? (
559
+ <span className="loading loading-spinner loading-xs" />
560
+ ) : (
561
+ <>
562
+ <Send className="w-3.5 h-3.5" /> Kirim
563
+ </>
564
+ )}
565
+ </button>
566
+ </form>
567
+
568
+ {/* Confirmation Modal */}
569
+ <ModalComponent />
570
+ </div>
571
+ )
572
+ }