@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,603 @@
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 {
6
+ Bot,
7
+ Send,
8
+ Square,
9
+ Terminal,
10
+ Brain,
11
+ ChevronDown,
12
+ ChevronRight,
13
+ Loader2,
14
+ CheckCircle2,
15
+ ArrowDown,
16
+ Sparkles,
17
+ Trash2,
18
+ Info
19
+ } from 'lucide-react'
20
+ import { subagentStore } from '../../api/subagent/subagentStore'
21
+ import { runSubagentTurn, killSubagentExecution } from '../../api/subagent/subagentExecutor'
22
+
23
+ function extractTextContent(raw) {
24
+ if (raw === undefined || raw === null) return ''
25
+ if (typeof raw === 'string') return raw
26
+ if (typeof raw === 'object') {
27
+ if (raw.answer) return extractTextContent(raw.answer)
28
+ if (raw.content) return extractTextContent(raw.content)
29
+ if (raw.message) return extractTextContent(raw.message)
30
+ if (raw.text) return extractTextContent(raw.text)
31
+ return JSON.stringify(raw, null, 2)
32
+ }
33
+ return String(raw)
34
+ }
35
+
36
+ function extractThoughtContent(raw, fallbackThought = null) {
37
+ if (fallbackThought) return fallbackThought
38
+ if (raw && typeof raw === 'object' && raw.thought) {
39
+ return String(raw.thought)
40
+ }
41
+ return null
42
+ }
43
+
44
+ const markdownComponents = {
45
+ code({ node, inline, className, children, ...props }) {
46
+ const match = /language-(\w+)/.exec(className || '')
47
+ return !inline ? (
48
+ <CodeBlock match={match} children={children} />
49
+ ) : (
50
+ <code
51
+ className="bg-base-300/90 text-primary font-mono text-[11px] px-1.5 py-0.5 rounded border border-base-content/10"
52
+ {...props}
53
+ >
54
+ {children}
55
+ </code>
56
+ )
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Format argument tool menjadi inline string bergaya Hermes / CLI terminal:
62
+ * e.g. message_agent -target "Mr Tester" -msg "Hello"
63
+ */
64
+ function formatToolArgsToCLI(_toolName, rawArgs) {
65
+ if (!rawArgs) return ''
66
+ let args = rawArgs
67
+ if (typeof rawArgs === 'string') {
68
+ try {
69
+ args = JSON.parse(rawArgs)
70
+ } catch {
71
+ return rawArgs
72
+ }
73
+ }
74
+
75
+ if (typeof args !== 'object') return String(args)
76
+
77
+ const parts = []
78
+ for (const [key, val] of Object.entries(args)) {
79
+ if (val === undefined || val === null || val === '') continue
80
+ const valStr = typeof val === 'object' ? JSON.stringify(val) : String(val)
81
+ const truncatedVal = valStr.length > 50 ? valStr.slice(0, 47) + '...' : valStr
82
+ parts.push(`--${key} "${truncatedVal}"`)
83
+ }
84
+ return parts.join(' ')
85
+ }
86
+
87
+ /**
88
+ * Komponen Accordion Thought (Hermes style "Thought >")
89
+ */
90
+ function ThoughtAccordion({ thought }) {
91
+ const [isOpen, setIsOpen] = useState(false)
92
+ if (!thought) return null
93
+
94
+ return (
95
+ <div className="my-1.5 bg-base-300/40 hover:bg-base-300/60 rounded-xl border border-base-content/10 transition-all overflow-hidden text-xs">
96
+ <button
97
+ type="button"
98
+ onClick={() => setIsOpen(!isOpen)}
99
+ className="w-full px-3 py-2 flex items-center justify-between text-left select-none text-base-content/75 hover:text-base-content"
100
+ >
101
+ <div className="flex items-center gap-2 font-mono text-[11px]">
102
+ <Brain className="w-3.5 h-3.5 text-accent shrink-0" />
103
+ <span className="font-semibold text-accent/90">Thought</span>
104
+ <span className="text-base-content/40 text-[10px]">&gt;</span>
105
+ <span className="text-base-content/50 text-[10px] truncate max-w-xs md:max-w-md">
106
+ {thought.slice(0, 60)}...
107
+ </span>
108
+ </div>
109
+ <div className="flex items-center gap-1 text-[10px] text-base-content/40 font-mono">
110
+ <span>{isOpen ? 'hide' : 'expand'}</span>
111
+ {isOpen ? <ChevronDown className="w-3 h-3" /> : <ChevronRight className="w-3 h-3" />}
112
+ </div>
113
+ </button>
114
+
115
+ {isOpen && (
116
+ <div className="p-3.5 bg-base-100/80 border-t border-base-content/10 text-[11px] text-base-content/90 font-mono leading-relaxed whitespace-pre-wrap">
117
+ {thought}
118
+ </div>
119
+ )}
120
+ </div>
121
+ )
122
+ }
123
+
124
+ /**
125
+ * Komponen Baris Eksekusi Tool Bergaya Terminal Inline
126
+ * e.g. "> Ran message_agent --target_agent "Mr Tester" ..."
127
+ */
128
+ function InlineToolExecution({ toolCall, observation }) {
129
+ const [isOpen, setIsOpen] = useState(false)
130
+ const toolName = toolCall?.function?.name || toolCall?.name || 'tool-execution'
131
+ const rawArgs = toolCall?.function?.arguments || toolCall?.arguments || toolCall?.query || ''
132
+ const cliArgs = formatToolArgsToCLI(toolName, rawArgs)
133
+
134
+ return (
135
+ <div className="my-1 font-mono text-[11px] bg-base-300/30 hover:bg-base-300/50 rounded-xl border border-base-content/10 transition-all overflow-hidden">
136
+ <button
137
+ type="button"
138
+ onClick={() => setIsOpen(!isOpen)}
139
+ className="w-full px-3 py-2 flex items-center justify-between text-left text-base-content/80 hover:text-base-content"
140
+ >
141
+ <div className="flex items-center gap-2 min-w-0 flex-1">
142
+ <Terminal className="w-3.5 h-3.5 text-warning shrink-0" />
143
+ <div className="flex items-center gap-1.5 min-w-0 truncate">
144
+ <span className="text-base-content/40 font-bold">&gt;</span>
145
+ <span className="text-base-content/60">Ran</span>
146
+ <span className="text-primary font-semibold">{toolName}</span>
147
+ {cliArgs && (
148
+ <span className="text-base-content/50 text-[10px] truncate max-w-sm">
149
+ {cliArgs}
150
+ </span>
151
+ )}
152
+ </div>
153
+ </div>
154
+
155
+ <div className="flex items-center gap-1.5 shrink-0 pl-2 text-[10px] text-base-content/40">
156
+ {observation ? (
157
+ <span className="text-success flex items-center gap-0.5">
158
+ <CheckCircle2 className="w-2.5 h-2.5" />
159
+ <span>result</span>
160
+ </span>
161
+ ) : (
162
+ <span className="text-warning animate-pulse">executing...</span>
163
+ )}
164
+ {isOpen ? <ChevronDown className="w-3 h-3" /> : <ChevronRight className="w-3 h-3" />}
165
+ </div>
166
+ </button>
167
+
168
+ {isOpen && (
169
+ <div className="p-3 bg-black/40 border-t border-base-content/10 space-y-2 text-[10px] text-base-content/85">
170
+ {rawArgs && (
171
+ <div>
172
+ <div className="text-[10px] text-base-content/40 uppercase tracking-wider mb-1">Arguments:</div>
173
+ <pre className="p-2 rounded-lg bg-base-100/50 overflow-x-auto text-warning font-mono whitespace-pre-wrap border border-base-content/5">
174
+ {typeof rawArgs === 'string' ? rawArgs : JSON.stringify(rawArgs, null, 2)}
175
+ </pre>
176
+ </div>
177
+ )}
178
+
179
+ {observation && (
180
+ <div>
181
+ <div className="text-[10px] text-base-content/40 uppercase tracking-wider mb-1">Output:</div>
182
+ <pre className="p-2 rounded-lg bg-base-100/50 overflow-x-auto text-base-content/90 font-mono whitespace-pre-wrap max-h-56 overflow-y-auto border border-base-content/5">
183
+ {(typeof observation === 'string' ? observation : JSON.stringify(observation)).replace(/^\[OBSERVATION\]:\s*/, '')}
184
+ </pre>
185
+ </div>
186
+ )}
187
+ </div>
188
+ )}
189
+ </div>
190
+ )
191
+ }
192
+
193
+ /**
194
+ * Bubble percakapan untuk pesan masuk (User / Lead Agent / Peer Sub-Agent)
195
+ */
196
+ function IncomingMessageBubble({ message }) {
197
+ const isMark = message.sender === 'mark'
198
+ const isPeer = message.sender === 'peer'
199
+
200
+ const senderBadge = isMark ? 'LEAD AGENT' : isPeer ? 'PEER AGENT' : 'CREATOR'
201
+ const badgeColor = isMark ? 'bg-primary/20 text-primary border-primary/30' : isPeer ? 'bg-accent/20 text-accent border-accent/30' : 'bg-secondary/20 text-secondary border-secondary/30'
202
+
203
+ // Bersihkan tag awalan jika ada
204
+ const cleanContent = extractTextContent(message.content)
205
+ .replace(/^\[DARI LEAD AGENT \(MARK\)\]:\s*/, '')
206
+ .replace(/^\[DARI CREATOR \/ USER\]:\s*/, '')
207
+ .replace(/^\[DARI SESAMA SUB-AGENT\]:\s*/, '')
208
+
209
+ return (
210
+ <div className="chat chat-end my-3 animate-fade-in">
211
+ <div className="chat-header text-[10px] font-mono opacity-50 mb-1 flex items-center gap-1.5">
212
+ <span className={`px-1.5 py-0.2 rounded-md border text-[9px] font-bold ${badgeColor}`}>
213
+ {senderBadge}
214
+ </span>
215
+ <span>
216
+ {new Date(message.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
217
+ </span>
218
+ </div>
219
+
220
+ <div className="chat-bubble bg-base-100 border border-base-content/15 shadow-md max-w-[85%] rounded-2xl p-3.5 text-xs text-base-content leading-relaxed">
221
+ <Markdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
222
+ {cleanContent}
223
+ </Markdown>
224
+ </div>
225
+ </div>
226
+ )
227
+ }
228
+
229
+ /**
230
+ * Bubble percakapan untuk pesan keluar dari Sub-Agent (Hermes unified block)
231
+ */
232
+ function SubagentMessageTurn({ turn, subagentName, isRunning }) {
233
+ return (
234
+ <div className="chat chat-start my-3 animate-fade-in">
235
+ <div className="chat-image avatar placeholder">
236
+ <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 font-mono">
237
+ {subagentName.slice(0, 2).toUpperCase()}
238
+ </div>
239
+ </div>
240
+ <div className="chat-header text-[10px] font-mono opacity-50 mb-1 flex items-center gap-1.5">
241
+ <span className="font-semibold text-base-content/80">{subagentName}</span>
242
+ <span>
243
+ {new Date(turn.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
244
+ </span>
245
+ </div>
246
+
247
+ <div className="chat-bubble bg-base-200/90 text-base-content border border-base-content/15 shadow-lg max-w-[90%] md:max-w-[85%] rounded-2xl p-4 space-y-2.5">
248
+ {/* 1. Reasoning Accordion */}
249
+ {turn.thoughts && turn.thoughts.length > 0 && (
250
+ <div className="space-y-1">
251
+ {turn.thoughts.map((th, idx) => (
252
+ <ThoughtAccordion key={idx} thought={th} />
253
+ ))}
254
+ </div>
255
+ )}
256
+
257
+ {/* 2. Inline Tool Execution Items */}
258
+ {turn.toolCalls && turn.toolCalls.length > 0 && (
259
+ <div className="space-y-1">
260
+ {turn.toolCalls.map((tc, idx) => (
261
+ <InlineToolExecution
262
+ key={idx}
263
+ toolCall={tc}
264
+ observation={turn.observations?.[tc.id] || turn.observations?.[tc.function?.name]}
265
+ />
266
+ ))}
267
+ </div>
268
+ )}
269
+
270
+ {/* 3. Final Output Text */}
271
+ {turn.content ? (
272
+ <div className="prose prose-invert prose-sm max-w-none text-xs leading-relaxed font-normal pt-1 text-base-content/95">
273
+ <Markdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
274
+ {extractTextContent(turn.content)}
275
+ </Markdown>
276
+ </div>
277
+ ) : isRunning ? (
278
+ <div className="flex items-center gap-2 text-xs text-base-content/60 py-1 font-mono">
279
+ <Loader2 className="w-3.5 h-3.5 animate-spin text-primary shrink-0" />
280
+ <span className="text-[11px]">Memproses langkah kerja...</span>
281
+ </div>
282
+ ) : null}
283
+ </div>
284
+ </div>
285
+ )
286
+ }
287
+
288
+ /**
289
+ * Parser kronologis untuk menggabungkan message history menjadi struktur turn terorganisir
290
+ */
291
+ function groupChronologicalMessages(rawMessages) {
292
+ if (!Array.isArray(rawMessages)) return []
293
+ const result = []
294
+ let currentTurn = null
295
+
296
+ for (const msg of rawMessages) {
297
+ if (msg.sender === 'user' || msg.sender === 'mark' || msg.sender === 'peer') {
298
+ if (currentTurn) {
299
+ result.push(currentTurn)
300
+ currentTurn = null
301
+ }
302
+ result.push({
303
+ type: 'incoming',
304
+ id: msg.id,
305
+ sender: msg.sender,
306
+ content: msg.content,
307
+ timestamp: msg.timestamp
308
+ })
309
+ } else if (msg.sender === 'system' || msg.role === 'system') {
310
+ if (currentTurn) {
311
+ result.push(currentTurn)
312
+ currentTurn = null
313
+ }
314
+ result.push({
315
+ type: 'system',
316
+ id: msg.id,
317
+ sender: 'system',
318
+ content: msg.content,
319
+ timestamp: msg.timestamp
320
+ })
321
+ } else if (msg.sender === 'subagent' || msg.role === 'assistant') {
322
+ if (!currentTurn) {
323
+ currentTurn = {
324
+ type: 'subagent_turn',
325
+ id: msg.id,
326
+ timestamp: msg.timestamp,
327
+ thoughts: [],
328
+ toolCalls: [],
329
+ observations: {},
330
+ content: null
331
+ }
332
+ }
333
+
334
+ const extractedThought = extractThoughtContent(msg.content, msg.thought)
335
+ if (extractedThought && !currentTurn.thoughts.includes(extractedThought)) {
336
+ currentTurn.thoughts.push(extractedThought)
337
+ }
338
+
339
+ if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
340
+ currentTurn.toolCalls.push(...msg.tool_calls)
341
+ }
342
+
343
+ if (msg.content) {
344
+ currentTurn.content = extractTextContent(msg.content)
345
+ }
346
+ } else if (msg.sender === 'tool' || msg.role === 'tool') {
347
+ if (currentTurn) {
348
+ const key = msg.tool_call_id || msg.name || 'last_tool'
349
+ currentTurn.observations[key] = msg.content
350
+ }
351
+ }
352
+ }
353
+
354
+ if (currentTurn) {
355
+ result.push(currentTurn)
356
+ }
357
+
358
+ return result
359
+ }
360
+
361
+ export default function AgentChatFeed({ subagentId, onOpenInspector, onDeleteAgent }) {
362
+ const [subagent, setSubagent] = useState(null)
363
+ const [messages, setMessages] = useState([])
364
+ const [inputText, setInputText] = useState('')
365
+ const [isSending, setIsSending] = useState(false)
366
+ const [showScrollBottomBtn, setShowScrollBottomBtn] = useState(false)
367
+
368
+ const messagesEndRef = useRef(null)
369
+ const containerRef = useRef(null)
370
+ const isAutoScrollRef = useRef(true)
371
+
372
+ const loadData = async () => {
373
+ if (!subagentId) return
374
+ try {
375
+ const agent = await subagentStore.getSubagent(subagentId)
376
+ const msgs = await subagentStore.getMessages(subagentId)
377
+ setSubagent(agent)
378
+ setMessages(msgs || [])
379
+ } catch (err) {
380
+ console.error('[AgentChatFeed] Load error:', err)
381
+ }
382
+ }
383
+
384
+ useEffect(() => {
385
+ loadData()
386
+ const interval = setInterval(loadData, 1000)
387
+ return () => clearInterval(interval)
388
+ }, [subagentId])
389
+
390
+ useEffect(() => {
391
+ if (isAutoScrollRef.current) {
392
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
393
+ }
394
+ }, [messages])
395
+
396
+ const handleScroll = () => {
397
+ if (!containerRef.current) return
398
+ const { scrollTop, scrollHeight, clientHeight } = containerRef.current
399
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 100
400
+ isAutoScrollRef.current = isAtBottom
401
+ setShowScrollBottomBtn(!isAtBottom)
402
+ }
403
+
404
+ const handleSendMessage = async (e) => {
405
+ e?.preventDefault()
406
+ if (!inputText.trim() || !subagentId || isSending) return
407
+
408
+ const text = inputText.trim()
409
+ setInputText('')
410
+ setIsSending(true)
411
+ isAutoScrollRef.current = true
412
+
413
+ try {
414
+ await runSubagentTurn(subagentId, text, 'user')
415
+ await loadData()
416
+ } catch (err) {
417
+ console.error('[AgentChatFeed] Send error:', err)
418
+ } finally {
419
+ setIsSending(false)
420
+ }
421
+ }
422
+
423
+ const handleStopExecution = () => {
424
+ if (!subagentId) return
425
+ killSubagentExecution(subagentId)
426
+ loadData()
427
+ }
428
+
429
+ const handleClearHistory = async () => {
430
+ if (!subagentId) return
431
+ await subagentStore.clearMessages(subagentId)
432
+ await loadData()
433
+ }
434
+
435
+ if (!subagent) {
436
+ return (
437
+ <div className="flex-1 flex flex-col items-center justify-center p-8 text-base-content/30 gap-2 font-mono">
438
+ <Bot className="w-8 h-8 stroke-[1.5]" />
439
+ <p className="text-xs">Memuat data sub-agent...</p>
440
+ </div>
441
+ )
442
+ }
443
+
444
+ const isRunning = subagent.status === 'running'
445
+ const groupedTurns = groupChronologicalMessages(messages)
446
+
447
+ return (
448
+ <div className="flex-1 flex flex-col h-full overflow-hidden bg-base-200/40 relative font-['Poppins',sans-serif]">
449
+ {/* Feed Header */}
450
+ <div className="p-3 px-4 border-b border-base-content/10 flex items-center justify-between bg-base-200/70 backdrop-blur-sm shrink-0">
451
+ <div className="flex items-center gap-3 min-w-0">
452
+ <div className="w-8 h-8 rounded-xl bg-primary/10 border border-primary/20 text-primary flex items-center justify-center font-bold text-xs font-mono">
453
+ {subagent.name.slice(0, 2).toUpperCase()}
454
+ </div>
455
+ <div className="min-w-0">
456
+ <div className="flex items-center gap-2">
457
+ <h2 className="text-xs font-bold text-base-content/90 truncate font-mono">
458
+ {subagent.name}
459
+ </h2>
460
+ {isRunning && (
461
+ <span className="badge badge-primary badge-xs gap-1 font-mono text-[9px] py-2">
462
+ <span className="w-1.5 h-1.5 rounded-full bg-base-100 animate-ping" />
463
+ Running
464
+ </span>
465
+ )}
466
+ </div>
467
+ <p className="text-[10px] text-base-content/50 truncate font-mono">
468
+ {subagent.role || 'Specialist'}
469
+ </p>
470
+ </div>
471
+ </div>
472
+
473
+ <div className="flex items-center gap-1.5">
474
+ {isRunning ? (
475
+ <button
476
+ type="button"
477
+ onClick={handleStopExecution}
478
+ className="btn btn-error btn-xs rounded-xl gap-1 px-2.5 font-mono text-[11px]"
479
+ title="Hentikan Eksekusi"
480
+ >
481
+ <Square className="w-3 h-3 fill-current" /> Stop
482
+ </button>
483
+ ) : (
484
+ <button
485
+ type="button"
486
+ onClick={handleClearHistory}
487
+ className="btn btn-ghost btn-xs text-base-content/40 hover:text-base-content rounded-xl font-mono text-[10px]"
488
+ title="Bersihkan Log Chat"
489
+ >
490
+ Clear Log
491
+ </button>
492
+ )}
493
+
494
+ {onDeleteAgent && (
495
+ <button
496
+ type="button"
497
+ onClick={() => onDeleteAgent(subagentId)}
498
+ className="btn btn-ghost btn-xs btn-circle text-base-content/40 hover:text-error transition-colors"
499
+ title="Hapus Sub-Agent"
500
+ >
501
+ <Trash2 className="w-3.5 h-3.5" />
502
+ </button>
503
+ )}
504
+
505
+ {onOpenInspector && (
506
+ <button
507
+ type="button"
508
+ onClick={onOpenInspector}
509
+ className="btn btn-ghost btn-xs btn-circle text-base-content/60 hover:text-base-content"
510
+ title="Lihat Detail Agen"
511
+ >
512
+ <Info className="w-4 h-4" />
513
+ </button>
514
+ )}
515
+ </div>
516
+ </div>
517
+
518
+ {/* Goal Banner */}
519
+ {subagent.goal && (
520
+ <div className="px-4 py-2 bg-base-300/40 border-b border-base-content/5 flex items-start gap-2 text-[11px] text-base-content/75 font-mono">
521
+ <Sparkles className="w-3.5 h-3.5 text-primary shrink-0 mt-0.5" />
522
+ <div className="truncate">
523
+ <span className="font-semibold text-primary/90 mr-1">Goal:</span>
524
+ <span>{subagent.goal}</span>
525
+ </div>
526
+ </div>
527
+ )}
528
+
529
+ {/* Messages Scroll Area */}
530
+ <div
531
+ ref={containerRef}
532
+ onScroll={handleScroll}
533
+ className="flex-1 overflow-y-auto p-4 md:px-6 space-y-2 custom-scrollbar"
534
+ >
535
+ {groupedTurns.length === 0 ? (
536
+ <div className="h-full flex flex-col items-center justify-center text-center p-8 text-base-content/30 gap-2.5">
537
+ <Bot className="w-8 h-8 stroke-[1.5]" />
538
+ <p className="text-xs font-mono">Belum ada aktivitas pada agen ini.</p>
539
+ <p className="text-[10px] text-base-content/40 max-w-xs font-mono">
540
+ Kirim instruksi melalui input di bawah untuk memulai giliran kerja mandiri.
541
+ </p>
542
+ </div>
543
+ ) : (
544
+ groupedTurns.map((turn, idx) => {
545
+ if (turn.type === 'incoming') {
546
+ return <IncomingMessageBubble key={turn.id || idx} message={turn} />
547
+ }
548
+ return (
549
+ <SubagentMessageTurn
550
+ key={turn.id || idx}
551
+ turn={turn}
552
+ subagentName={subagent.name}
553
+ isRunning={isRunning && idx === groupedTurns.length - 1}
554
+ />
555
+ )
556
+ })
557
+ )}
558
+ <div ref={messagesEndRef} />
559
+ </div>
560
+
561
+ {/* Floating Scroll to Bottom Button */}
562
+ {showScrollBottomBtn && (
563
+ <button
564
+ type="button"
565
+ onClick={() => {
566
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
567
+ isAutoScrollRef.current = true
568
+ setShowScrollBottomBtn(false)
569
+ }}
570
+ className="btn btn-circle btn-sm bg-base-100/90 border border-base-content/20 shadow-xl absolute bottom-18 right-6 z-20 hover:scale-105 transition-all text-primary"
571
+ title="Scroll ke Bawah"
572
+ >
573
+ <ArrowDown className="w-4 h-4" />
574
+ </button>
575
+ )}
576
+
577
+ {/* Message Input Footer */}
578
+ <div className="p-3 border-t border-base-content/10 bg-base-200/70 backdrop-blur-sm shrink-0">
579
+ <form onSubmit={handleSendMessage} className="flex items-center gap-2">
580
+ <input
581
+ type="text"
582
+ placeholder={`Kirim instruksi langsung ke @${subagent.name}...`}
583
+ value={inputText}
584
+ onChange={(e) => setInputText(e.target.value)}
585
+ disabled={isRunning || isSending}
586
+ className="input input-sm flex-1 rounded-xl bg-base-100/70 border-base-content/10 text-xs focus:outline-none focus:border-primary/50 font-mono"
587
+ />
588
+ <button
589
+ type="submit"
590
+ disabled={!inputText.trim() || isRunning || isSending}
591
+ className="btn btn-primary btn-sm rounded-xl px-3 font-medium shadow-sm shadow-primary/20"
592
+ >
593
+ {isSending ? (
594
+ <Loader2 className="w-3.5 h-3.5 animate-spin" />
595
+ ) : (
596
+ <Send className="w-3.5 h-3.5" />
597
+ )}
598
+ </button>
599
+ </form>
600
+ </div>
601
+ </div>
602
+ )
603
+ }