@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.
- package/LICENSE +29 -0
- package/README.md +209 -0
- package/bin/mark.js +27 -0
- package/out/renderer/assets/icon-DHo2CYph.svg +1 -0
- package/out/renderer/assets/index-G2fADWO-.js +4768 -0
- package/out/renderer/assets/index-f3mVse3c.css +1 -0
- package/out/renderer/assets/music-cover-CG4vi3kZ.png +0 -0
- package/out/renderer/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-700-normal-O-jipLrW.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-700-normal-fHs-vx92.woff +0 -0
- package/out/renderer/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
- package/out/renderer/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
- package/out/renderer/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
- package/out/renderer/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
- package/out/renderer/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 +0 -0
- package/out/renderer/favicon.ico +0 -0
- package/out/renderer/icon-256.png +0 -0
- package/out/renderer/icon.svg +1 -0
- package/out/renderer/index.html +25 -0
- package/out/renderer/manifest.json +25 -0
- package/out/renderer/robot-simulator.html +1640 -0
- package/package.json +106 -0
- package/src/cli/index.js +332 -0
- package/src/cli/monitor.js +413 -0
- package/src/cli/theme.js +347 -0
- package/src/launcher/MarkTray.cs +227 -0
- package/src/main/browser-agent.js +1132 -0
- package/src/main/git-service.js +90 -0
- package/src/main/google/google-calendar.js +69 -0
- package/src/main/google/google-drive.js +263 -0
- package/src/main/google/google-gmail.js +131 -0
- package/src/main/google/google-service.js +241 -0
- package/src/main/node-tools.js +23 -0
- package/src/main/pc-agent-scripts/mouse-locker.ps1 +76 -0
- package/src/main/pc-agent-scripts/ocr-region.ps1 +185 -0
- package/src/main/pc-agent-scripts/pc-daemon.ps1 +726 -0
- package/src/main/pc-agent-scripts/pc-overlay.ps1 +253 -0
- package/src/main/pc-agent-scripts/pick-folder.ps1 +33 -0
- package/src/main/pc-agent-scripts/read-ui.ps1 +181 -0
- package/src/main/pc-agent-scripts/take-screenshot.ps1 +24 -0
- package/src/main/pc-agent-scripts/win-action.ps1 +380 -0
- package/src/main/plugins/plugin-loader.js +306 -0
- package/src/main/skills/skill-manager.js +265 -0
- package/src/main/syntax-validator.js +455 -0
- package/src/main/task-daemon.js +117 -0
- package/src/main/telegram/telegram-service.js +835 -0
- package/src/main/tools/browser-tools.js +516 -0
- package/src/main/tools/file-tools.js +1126 -0
- package/src/main/tools/git-tools.js +79 -0
- package/src/main/tools/google-tools.js +566 -0
- package/src/main/tools/index.js +36 -0
- package/src/main/tools/media-tools.js +42 -0
- package/src/main/tools/system-tools.js +295 -0
- package/src/main/tools/task-tools.js +73 -0
- package/src/main/tools/telegram-tools.js +101 -0
- package/src/renderer/index.html +24 -0
- package/src/renderer/public/favicon.ico +0 -0
- package/src/renderer/public/icon-256.png +0 -0
- package/src/renderer/public/icon.svg +1 -0
- package/src/renderer/public/manifest.json +25 -0
- package/src/renderer/public/robot-simulator.html +1640 -0
- package/src/renderer/src/App.jsx +304 -0
- package/src/renderer/src/api/ai/awareness.js +294 -0
- package/src/renderer/src/api/ai/chatSummarizer.js +79 -0
- package/src/renderer/src/api/ai/contextCompactor.js +129 -0
- package/src/renderer/src/api/ai/contextManager.js +1130 -0
- package/src/renderer/src/api/ai/core.js +167 -0
- package/src/renderer/src/api/ai/index.js +4 -0
- package/src/renderer/src/api/ai/memoryGroomer.js +101 -0
- package/src/renderer/src/api/ai/persona.js +115 -0
- package/src/renderer/src/api/ai/planning.js +317 -0
- package/src/renderer/src/api/ai/relationship.js +177 -0
- package/src/renderer/src/api/ai/skillSynthesizer.js +168 -0
- package/src/renderer/src/api/ai/tools.js +167 -0
- package/src/renderer/src/api/ai/utils.js +359 -0
- package/src/renderer/src/api/db.js +1013 -0
- package/src/renderer/src/api/oramaStore.js +592 -0
- package/src/renderer/src/api/ragPipeline.js +87 -0
- package/src/renderer/src/api/subagent/subagentExecutor.js +780 -0
- package/src/renderer/src/api/subagent/subagentPrompt.js +44 -0
- package/src/renderer/src/api/subagent/subagentStore.js +173 -0
- package/src/renderer/src/api/taskExecutor.js +18 -0
- package/src/renderer/src/api/taskStore.js +315 -0
- package/src/renderer/src/api/tools/core-tools.js +707 -0
- package/src/renderer/src/api/tools/index.js +151 -0
- package/src/renderer/src/api/turnPairMigrator.js +200 -0
- package/src/renderer/src/api/vectorMemory.js +136 -0
- package/src/renderer/src/api/wakeWord.js +101 -0
- package/src/renderer/src/api/web-bridge.js +885 -0
- package/src/renderer/src/api/webSpeech.js +178 -0
- package/src/renderer/src/api/workspaceRag.js +76 -0
- package/src/renderer/src/assets/electron.svg +10 -0
- package/src/renderer/src/assets/icon.svg +1 -0
- package/src/renderer/src/assets/main.css +589 -0
- package/src/renderer/src/assets/music-cover.png +0 -0
- package/src/renderer/src/assets/wavy-lines.svg +25 -0
- package/src/renderer/src/components/Chat/ApprovalBubble.jsx +127 -0
- package/src/renderer/src/components/Chat/CodeBlock.jsx +83 -0
- package/src/renderer/src/components/Chat/DurableTaskBubble.jsx +455 -0
- package/src/renderer/src/components/Chat/FileDiffModal.jsx +328 -0
- package/src/renderer/src/components/Chat/GeminiWidgets.jsx +82 -0
- package/src/renderer/src/components/Chat/MemoryFooterBubble.jsx +34 -0
- package/src/renderer/src/components/Chat/MessageBubble.jsx +459 -0
- package/src/renderer/src/components/Chat/PluginExecutionBubble.jsx +41 -0
- package/src/renderer/src/components/Chat/ThinkingBubble.jsx +316 -0
- package/src/renderer/src/components/Chat/YoutubeSearchBubble.jsx +43 -0
- package/src/renderer/src/components/Chat/YoutubeSummaryBubble.jsx +40 -0
- package/src/renderer/src/components/Chat/index.js +11 -0
- package/src/renderer/src/components/ChatList.jsx +434 -0
- package/src/renderer/src/components/GlobalCameraManager.jsx +67 -0
- package/src/renderer/src/components/Versions.jsx +15 -0
- package/src/renderer/src/components/YoutubeMusicPlayer.jsx +375 -0
- package/src/renderer/src/components/camera/CameraPreview.jsx +236 -0
- package/src/renderer/src/components/config/AiEngineSection.jsx +363 -0
- package/src/renderer/src/components/config/CameraSection.jsx +100 -0
- package/src/renderer/src/components/config/PermissionsSection.jsx +130 -0
- package/src/renderer/src/components/config/ShortcutSection.jsx +102 -0
- package/src/renderer/src/components/config/VoiceSection.jsx +110 -0
- package/src/renderer/src/components/config/index.js +5 -0
- package/src/renderer/src/components/core/Avatar.jsx +2197 -0
- package/src/renderer/src/components/core/ConfirmModal.jsx +80 -0
- package/src/renderer/src/components/core/DraggableHoloCard.jsx +125 -0
- package/src/renderer/src/components/core/FloatingMenu.jsx +272 -0
- package/src/renderer/src/components/core/HoloCard.jsx +64 -0
- package/src/renderer/src/components/core/InputBar.jsx +1346 -0
- package/src/renderer/src/components/core/ProcessPanel.jsx +216 -0
- package/src/renderer/src/components/core/ResponseArea.jsx +149 -0
- package/src/renderer/src/components/core/StatusIndicator.jsx +59 -0
- package/src/renderer/src/components/core/ThoughtNeuralFlow.jsx +168 -0
- package/src/renderer/src/components/core/ToolClustersDeck.jsx +210 -0
- package/src/renderer/src/components/core/native-skills.js +43 -0
- package/src/renderer/src/components/neural-core/EmotionalCortexLobe.jsx +359 -0
- package/src/renderer/src/components/neural-core/NodeDissectionModal.jsx +57 -0
- package/src/renderer/src/components/neural-core/ProceduralMatrixLobe.jsx +193 -0
- package/src/renderer/src/components/neural-core/ProceduralSkillModal.jsx +53 -0
- package/src/renderer/src/components/neural-core/ResetAiModal.jsx +198 -0
- package/src/renderer/src/components/neural-core/SynapticMemoryLobe.jsx +204 -0
- package/src/renderer/src/components/neural-core/traitMeta.js +102 -0
- package/src/renderer/src/components/subagent/AgentChatFeed.jsx +603 -0
- package/src/renderer/src/components/subagent/AgentInspector.jsx +224 -0
- package/src/renderer/src/components/subagent/AgentSidebar.jsx +216 -0
- package/src/renderer/src/components/subagent/SubagentIntercom.jsx +572 -0
- package/src/renderer/src/components/subagent/SubagentTopologyMap.jsx +423 -0
- package/src/renderer/src/contexts/ApprovalContext.jsx +425 -0
- package/src/renderer/src/contexts/ChatContext.jsx +61 -0
- package/src/renderer/src/contexts/YoutubeMusicContext.jsx +333 -0
- package/src/renderer/src/data/faqData.js +202 -0
- package/src/renderer/src/hooks/agent/executeAgentTool.js +895 -0
- package/src/renderer/src/hooks/agent/index.js +4 -0
- package/src/renderer/src/hooks/agent/useMarkMusic.js +75 -0
- package/src/renderer/src/hooks/agent/useMarkPlan.js +2739 -0
- package/src/renderer/src/hooks/agent/useMarkState.js +173 -0
- package/src/renderer/src/hooks/agent/useMarkYoutube.js +82 -0
- package/src/renderer/src/hooks/agent/useRelationalGrowth.js +201 -0
- package/src/renderer/src/hooks/telegram/useTelegramBot.js +63 -0
- package/src/renderer/src/hooks/useAwareness.js +289 -0
- package/src/renderer/src/hooks/useChatArchiver.js +52 -0
- package/src/renderer/src/hooks/useConfirm.jsx +68 -0
- package/src/renderer/src/hooks/useMarkAgent.js +454 -0
- package/src/renderer/src/hooks/useMemoryGroomer.js +152 -0
- package/src/renderer/src/hooks/useVAD.js +511 -0
- package/src/renderer/src/hooks/useWebSpeechWakeWord.js +122 -0
- package/src/renderer/src/main.jsx +12 -0
- package/src/renderer/src/pages/ChatStudio.jsx +922 -0
- package/src/renderer/src/pages/Configuration.jsx +360 -0
- package/src/renderer/src/pages/GoogleWorkspace.jsx +293 -0
- package/src/renderer/src/pages/Guidebook.jsx +917 -0
- package/src/renderer/src/pages/Knowledge.jsx +241 -0
- package/src/renderer/src/pages/MarkHome.jsx +491 -0
- package/src/renderer/src/pages/NeuralCore.jsx +786 -0
- package/src/renderer/src/pages/Plugins.jsx +630 -0
- package/src/renderer/src/pages/SkillEditor.jsx +380 -0
- package/src/renderer/src/pages/Skills.jsx +219 -0
- package/src/renderer/src/pages/Subagents.jsx +342 -0
- package/src/renderer/src/pages/TelegramBot.jsx +343 -0
- package/src/renderer/src/utils/chatContentParser.js +203 -0
- package/src/renderer/src/utils/toolClusters.js +201 -0
- package/src/server/bin/sha3_wasm_bg.wasm +0 -0
- package/src/server/config-manager.js +57 -0
- package/src/server/index.js +195 -0
- package/src/server/launcher.js +87 -0
- package/src/server/memory/db-store.js +758 -0
- package/src/server/memory/orama-store.js +177 -0
- package/src/server/memory/vector-engine.js +109 -0
- package/src/server/routes/ai.routes.js +280 -0
- package/src/server/routes/awareness.routes.js +62 -0
- package/src/server/routes/chat.routes.js +61 -0
- package/src/server/routes/config.routes.js +41 -0
- package/src/server/routes/index.js +35 -0
- package/src/server/routes/integrations.routes.js +116 -0
- package/src/server/routes/memory.routes.js +256 -0
- package/src/server/routes/plugins.routes.js +72 -0
- package/src/server/routes/skills.routes.js +139 -0
- package/src/server/routes/subagents.routes.js +70 -0
- package/src/server/routes/tasks.routes.js +175 -0
- package/src/server/routes/workspace.routes.js +127 -0
- package/src/server/services/ai/ai-utils.js +161 -0
- package/src/server/services/ai/openai-provider.js +757 -0
- package/src/server/services/ai/web-provider.js +729 -0
- package/src/server/services/ai-bridge.js +46 -0
- package/src/server/services/deepseek-web.js +956 -0
- package/src/server/services/gemini-web.js +170 -0
- package/src/server/services/notification-service.js +91 -0
- package/src/server/services/updater.js +125 -0
- package/src/server/tools/awareness-tracker.js +292 -0
- package/src/server/tools/group-tools.js +1164 -0
- package/src/server/tools/media-tools.js +244 -0
- package/src/server/tools/pc-agent.js +327 -0
- package/src/server/tools/screen-service.js +51 -0
- package/src/server/ws-hub.js +168 -0
- 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]">></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">></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
|
+
}
|