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