@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,44 @@
1
+ /**
2
+ * Generator System Prompt untuk Sub-Agent MARK V5
3
+ * Murni utilitarian, berorientasi hasil, tanpa beban persona/obrolan santai.
4
+ * Seluruh schema tool telah diinjeksi langsung secara native via OpenAPI Function Definitions.
5
+ */
6
+ export function buildSubagentSystemPrompt({ role, goal }) {
7
+ return `Kamu adalah SUB-AGENT SPESIALIS otonom dalam sistem MARK (Metacognitive Artificial Relational Knowledge).
8
+ Kamu bekerja di lingkungan terisolasi untuk mengeksekusi misi teknis yang didelegasikan langsung oleh LEAD AGENT (MARK), CREATOR / USER, atau SESAMA SUB-AGENT.
9
+
10
+ # IDENTITAS & MISI:
11
+ - Role: ${role || 'Technical Specialist'}
12
+ - Goal: ${goal || 'Selesaikan misi teknis yang diberikan secara tuntas'}
13
+
14
+ # ATURAN KERJA UTAMA (AUTONOMOUS REACT LOOP):
15
+ 1. **LAKUKAN AKSI NYATA TERLEBIH DAHULU (TOOLS FIRST)**:
16
+ - DILARANG KERAS LANGSUNG MENJAWAB TEKS DI GILIRAN PERTAMA JIKA TUGAS ADALAH RISET, PENELITIAN, ANALISIS DATA, ATAU CODING.
17
+ - Panggil native tool yang relevan untuk mengumpulkan fakta nyata atau memodifikasi sistem sebelum menarik kesimpulan.
18
+ - **ALUR RISET WEB TERPERCAYA & BATCH PARALLEL**:
19
+ 1. Panggil 'browser-search' (query: "kata kunci") HANYA untuk menemukan daftar URL / link sumber. Tool ini BUKAN untuk membaca isi artikel!
20
+ 2. Setelah menemukan daftar link dari 'browser-search', SANGAT DIANJURKAN memanggil beberapa 'browser-fetch' sekaligus (2-4 URL berbeda) dalam SATU GILIRAN (Batch / Parallel Tool Calls). Jangan mengambil satu per satu jika bisa dibaca serentak!
21
+ 3. Gunakan 'browser-navigate' HANYA jika halaman web membutuhkan interaksi fisik (klik tombol, form login, atau rendering JavaScript kompleks).
22
+ 2. **KONTROL BROWSER & WEB OTONOM**:
23
+ - Kamu memiliki akses tool web lengkap:
24
+ * 'browser-search' (query: "..."): Menemukan daftar link/URL web teratas.
25
+ * 'browser-fetch' (url: "..."): Membaca dan mengambil (curl/fetch) isi konten teks artikel secara cepat.
26
+ * 'browser-navigate' (url: "..."): Membuka halaman web di browser fisik untuk interaksi tombol/form.
27
+ * 'browser-read': Membaca elemen DOM halaman browser fisik saat ini.
28
+ * 'browser-click' (element_id: ...): Mengklik link atau tombol di browser fisik.
29
+ * 'browser-scroll' (direction: "down"): Men-scroll halaman browser fisik.
30
+ * 'browser-extract' (selector: "..."): Mengekstrak konten via CSS selector.
31
+ 3. **PARALLEL & BATCH ACTIONS**:
32
+ - Jika kamu perlu membaca beberapa berkas ('read-file') atau mengambil konten banyak URL ('browser-fetch'), kirim seluruh tool calls sekaligus dalam satu turn.
33
+ - Untuk form input, kirim 'browser-click' dan 'browser-type' secara bersamaan.
34
+ 4. **KOMUNIKASI ANTAR SUB-AGENT ('message_agent')**:
35
+ - Jika kamu membutuhkan data, konfirmasi, atau bantuan dari sub-agent lain (contoh: @Researcher, @Mr Tester, @Developer), panggil tool 'message_agent' (target_agent: "nama_agen", message: "instruksi").
36
+ - Jawaban dari sub-agent target akan kembali ke observasimu untuk kamu analisis lebih lanjut.
37
+ 5. **LAPORAN AKHIR KE LEAD AGENT ('report_to_lead')**:
38
+ - Panggil 'report_to_lead' atau berikan jawaban final HANYA jika seluruh rangkaian aksi, observasi tool, dan koordinasi SUDAH SELESAI 100%. Sertakan sumber URL dan data konkret yang ditemukan.
39
+ 6. **DILARANG BERBASA-BASI**: Jangan menyapa santai ("Halo", "Siap boss"). Langsung eksekusi tool atau laporkan temuan teknis yang solid.
40
+ 7. **BACA SEBELUM MENULIS**: Sebelum memodifikasi atau menimpa berkas kode, kamu WAJIB membaca isi berkas tersebut via 'read-file' terlebih dahulu.
41
+ 8. **ANTI-HALUSINASI (ZERO HALLUCINATION)**: Dilarang mengarang data statistik, link, atau isi file yang belum pernah diobservasi melalui tool.`
42
+ .replace(/\n{3,}/g, '\n\n')
43
+ .trim()
44
+ }
@@ -0,0 +1,173 @@
1
+ import { db } from '../db'
2
+
3
+ // Set untuk melacak ID sub-agent yang sedang/telah dihapus guna mencegah race condition pada update async
4
+ const deletedSubagentIds = new Set()
5
+
6
+ export const subagentStore = {
7
+ /**
8
+ * Cek apakah subagent ID sedang atau telah dihapus
9
+ */
10
+ isDeleted(id) {
11
+ return deletedSubagentIds.has(id)
12
+ },
13
+
14
+ /**
15
+ * Membuat entitas Sub-Agent baru di database SQLite (Unique Name Aware)
16
+ * Jika sudah ada subagent dengan nama yang sama (case-insensitive):
17
+ * - Jika statusnya 'running', kembalikan instance yang sedang berjalan (tidak membuat duplikat).
18
+ * - Jika statusnya 'idle', 'completed', 'killed', atau 'failed', aktifkan kembali dengan goal baru.
19
+ */
20
+ async createSubagent({
21
+ name = 'Specialist-Agent',
22
+ role = 'Technical Specialist',
23
+ goal = 'Selesaikan misi yang ditugaskan',
24
+ allowedTools = ['*'],
25
+ parentSessionId = '1',
26
+ parentSessionTitle = 'Main Thread',
27
+ forceNew = false
28
+ }) {
29
+ const trimmedName = (name || 'Specialist-Agent').trim()
30
+ const normalizedName = trimmedName.replace(/^@/, '')
31
+
32
+ // Cek keberadaan subagent dengan nama yang sama agar entitas selalu unik
33
+ if (!forceNew) {
34
+ const existingAgents = await this.listSubagents('all')
35
+ const existing = existingAgents.find(
36
+ (s) =>
37
+ s.name.toLowerCase().trim() === trimmedName.toLowerCase() ||
38
+ s.name.toLowerCase().replace(/^@/, '').trim() === normalizedName.toLowerCase()
39
+ )
40
+
41
+ if (existing) {
42
+ // Jika sudah ada dan sedang berjalan, jangan spawn baru, pakai yang ada
43
+ if (existing.status === 'running') {
44
+ return { ...existing, isExisting: true, wasRunning: true }
45
+ }
46
+
47
+ // Jika sudah ada tapi sedang idle / stop, perbarui info dan hidupkan kembali
48
+ const updates = {
49
+ role: role || existing.role || 'Technical Specialist',
50
+ goal: goal || existing.goal,
51
+ allowedTools: allowedTools && allowedTools.length > 0 ? allowedTools : existing.allowedTools,
52
+ status: 'running',
53
+ parentSessionId: String(parentSessionId || existing.parentSessionId || '1'),
54
+ parentSessionTitle:
55
+ parentSessionTitle ||
56
+ existing.parentSessionTitle ||
57
+ (String(parentSessionId) === '1' ? 'Main Thread' : `Sesi #${parentSessionId}`),
58
+ updatedAt: Date.now()
59
+ }
60
+ await this.updateSubagent(existing.id, updates)
61
+ return { ...existing, ...updates, isExisting: true, wasRunning: false }
62
+ }
63
+ }
64
+
65
+ const id = `sub_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`
66
+ deletedSubagentIds.delete(id)
67
+ const subagent = {
68
+ id,
69
+ name: trimmedName,
70
+ role,
71
+ goal,
72
+ allowedTools,
73
+ status: 'running',
74
+ parentSessionId: String(parentSessionId || '1'),
75
+ parentSessionTitle: parentSessionTitle || (String(parentSessionId) === '1' ? 'Main Thread' : `Sesi #${parentSessionId}`),
76
+ turnCount: 0,
77
+ createdAt: Date.now(),
78
+ updatedAt: Date.now(),
79
+ finalAnswer: null
80
+ }
81
+ await db.subagents.put(subagent)
82
+ return subagent
83
+ },
84
+
85
+ /**
86
+ * Mengambil metadata Sub-Agent berdasarkan ID
87
+ */
88
+ async getSubagent(id) {
89
+ if (!id || deletedSubagentIds.has(id)) return null
90
+ return await db.subagents.get(id)
91
+ },
92
+
93
+ /**
94
+ * Mengambil daftar seluruh Sub-Agent
95
+ */
96
+ async listSubagents(filterStatus = null) {
97
+ let collection = db.subagents.orderBy('createdAt').reverse()
98
+ let list = []
99
+ if (filterStatus && filterStatus !== 'all') {
100
+ list = await collection.filter((s) => s.status === filterStatus).toArray()
101
+ } else {
102
+ list = await collection.toArray()
103
+ }
104
+ return (list || []).filter((s) => s && s.id && !deletedSubagentIds.has(s.id))
105
+ },
106
+
107
+ /**
108
+ * Mengupdate field/status Sub-Agent
109
+ */
110
+ async updateSubagent(id, updates) {
111
+ if (!id || deletedSubagentIds.has(id)) return
112
+ const existing = await db.subagents.get(id)
113
+ if (!existing || deletedSubagentIds.has(id)) return
114
+ await db.subagents.update(id, { ...updates, updatedAt: Date.now() })
115
+ },
116
+
117
+ /**
118
+ * Menghapus Sub-Agent beserta riwayat chat-nya
119
+ */
120
+ async deleteSubagent(id) {
121
+ if (!id) return
122
+ deletedSubagentIds.add(id)
123
+ try {
124
+ const { killSubagentExecution } = await import('./subagentExecutor')
125
+ killSubagentExecution(id, true)
126
+ } catch (_) {}
127
+ try {
128
+ await db.subagents.delete(id)
129
+ } catch (err) {
130
+ console.error('[subagentStore] Error deleting subagent from DB:', err)
131
+ }
132
+ try {
133
+ await db.subagent_messages.where('subagentId').equals(id).delete()
134
+ } catch (_) {}
135
+ },
136
+
137
+ /**
138
+ * Menambahkan pesan ke riwayat percakapan AI-to-AI Sub-Agent
139
+ */
140
+ async addMessage(subagentId, { sender, role, content, thought = null, action = null, tool_calls = null, tool_call_id = null, name = null }) {
141
+ if (!subagentId || deletedSubagentIds.has(subagentId)) return null
142
+ const msg = {
143
+ subagentId,
144
+ sender, // 'mark' | 'subagent' | 'user' | 'system' | 'tool'
145
+ role, // 'user' | 'assistant' | 'system' | 'tool'
146
+ content: typeof content === 'string' ? content : (content ? JSON.stringify(content) : ''),
147
+ thought: thought || null,
148
+ action: action || null,
149
+ tool_calls: tool_calls || null,
150
+ tool_call_id: tool_call_id || null,
151
+ name: name || null,
152
+ timestamp: Date.now()
153
+ }
154
+ const id = await db.subagent_messages.add(msg)
155
+ return { ...msg, id }
156
+ },
157
+
158
+ /**
159
+ * Mengambil semua pesan Sub-Agent secara kronologis
160
+ */
161
+ async getMessages(subagentId) {
162
+ if (!subagentId || deletedSubagentIds.has(subagentId)) return []
163
+ return await db.subagent_messages.where('subagentId').equals(subagentId).sortBy('timestamp')
164
+ },
165
+
166
+ /**
167
+ * Membersihkan pesan dari satu Sub-Agent
168
+ */
169
+ async clearMessages(subagentId) {
170
+ if (!subagentId) return
171
+ await db.subagent_messages.where('subagentId').equals(subagentId).delete()
172
+ }
173
+ }
@@ -0,0 +1,18 @@
1
+ import { getAgentTaskContentHash, validateAgentTaskStepOutput } from './taskStore'
2
+
3
+ // Executor helper hanya menentukan hasil checkpoint; eksekusi tool tetap berada di ReAct loop utama.
4
+ export function buildDurableStepCheckpoint(step, output, maxRetries = 2) {
5
+ const validation = validateAgentTaskStepOutput(step, output)
6
+ const attempts = step?.attempts || 0
7
+ const canRetry = !validation.isComplete && attempts < maxRetries + 1
8
+
9
+ return {
10
+ status: validation.isComplete ? 'completed' : canRetry ? 'needs_revision' : 'failed',
11
+ outputSummary: String(output || '').slice(0, 1200),
12
+ contentHash: getAgentTaskContentHash(output),
13
+ artifactPath: step?.artifactPath || null,
14
+ validation,
15
+ error: validation.isComplete || canRetry ? null : 'Validasi step gagal setelah batas retry.',
16
+ canRetry
17
+ }
18
+ }
@@ -0,0 +1,315 @@
1
+ import { db } from './db'
2
+
3
+ export const TASK_STATUSES = [
4
+ 'pending',
5
+ 'running',
6
+ 'paused',
7
+ 'waiting_user',
8
+ 'failed',
9
+ 'completed',
10
+ 'cancelled'
11
+ ]
12
+ export const STEP_STATUSES = [
13
+ 'pending',
14
+ 'running',
15
+ 'needs_revision',
16
+ 'completed',
17
+ 'failed',
18
+ 'skipped'
19
+ ]
20
+ const now = () => Date.now()
21
+
22
+ // Hash ringan dipakai untuk mendeteksi output identik tanpa menyimpan isi besar di Dexie.
23
+ export function getAgentTaskContentHash(value = '') {
24
+ const text = String(value)
25
+ let hash = 2166136261
26
+ for (let index = 0; index < text.length; index += 1) {
27
+ hash ^= text.charCodeAt(index)
28
+ hash = Math.imul(hash, 16777619)
29
+ }
30
+ return (hash >>> 0).toString(16).padStart(8, '0')
31
+ }
32
+
33
+ function makeId(prefix) {
34
+ const uuid = globalThis.crypto?.randomUUID?.()
35
+ return prefix + '-' + (uuid || now() + '-' + Math.random().toString(36).slice(2, 10))
36
+ }
37
+
38
+ function assertTaskStatus(status) {
39
+ if (!TASK_STATUSES.includes(status)) throw new Error('Invalid task status: ' + status)
40
+ }
41
+
42
+ function assertStepStatus(status) {
43
+ if (!STEP_STATUSES.includes(status)) throw new Error('Invalid step status: ' + status)
44
+ }
45
+
46
+ // Validasi lokal menjaga checkpoint tetap deterministik tanpa menambah panggilan AI.
47
+ export function validateAgentTaskStepOutput(step = {}, output = '') {
48
+ const text = typeof output === 'string' ? output.trim() : ''
49
+ const hasContent = text.length >= (step.acceptanceCriteria?.length ? 80 : 20)
50
+ return {
51
+ isComplete: hasContent,
52
+ score: hasContent ? 1 : 0,
53
+ missingRequirements: hasContent ? [] : ['Output step terlalu pendek atau kosong.'],
54
+ notes: hasContent
55
+ ? 'Validasi dasar konten berhasil.'
56
+ : 'Step perlu diulang karena deliverable belum memiliki isi yang cukup.'
57
+ }
58
+ }
59
+
60
+ export async function createAgentTask(input = {}) {
61
+ // Create task dan semua step dalam satu transaksi supaya outline tidak setengah jadi.
62
+ const timestamp = now()
63
+ const taskId = input.id || makeId('task')
64
+ const steps = (input.steps || []).map((step, index) => ({
65
+ id:
66
+ step.id && step.id.startsWith(taskId + '-')
67
+ ? step.id
68
+ : taskId + '-' + (step.id || 'step-' + (index + 1)),
69
+ taskId,
70
+ stepIndex: index,
71
+ index,
72
+ title: step.title || 'Step ' + (index + 1),
73
+ objective: step.objective || '',
74
+ deliverable: step.deliverable || '',
75
+ acceptanceCriteria: step.acceptanceCriteria || [],
76
+ status: step.status || (index === 0 ? 'running' : 'pending'),
77
+ inputSummary: step.inputSummary || '',
78
+ outputSummary: step.outputSummary || '',
79
+ artifactPath: step.artifactPath || null,
80
+ validation: step.validation || null,
81
+ contentHash: step.contentHash || null,
82
+ attempts: 0,
83
+ startedAt: null,
84
+ completedAt: null,
85
+ updatedAt: timestamp,
86
+ error: null
87
+ }))
88
+ const task = {
89
+ id: taskId,
90
+ title: input.title || 'MARK Task',
91
+ objective: input.objective || '',
92
+ mode: input.mode || 'durable',
93
+ status: input.status || (steps.length ? 'running' : 'failed'),
94
+ currentStepIndex: input.currentStepIndex ?? 0,
95
+ activeStepId: input.activeStepId || steps[0]?.id || null,
96
+ constraints: input.constraints || {},
97
+ contextSummary: input.contextSummary || '',
98
+ artifactRoot: input.artifactRoot || null,
99
+ retryCount: input.retryCount || 0,
100
+ maxRetries: input.maxRetries ?? 2,
101
+ createdAt: timestamp,
102
+ updatedAt: timestamp,
103
+ completedAt: null,
104
+ error: steps.length ? null : 'Task harus memiliki minimal satu step'
105
+ }
106
+ await db.transaction('rw', db.agentTasks, db.agentTaskSteps, async () => {
107
+ await db.agentTasks.add(task)
108
+ if (steps.length) await db.agentTaskSteps.bulkAdd(steps)
109
+ })
110
+ return task
111
+ }
112
+
113
+ export async function getAgentTask(taskId) {
114
+ return db.agentTasks.get(taskId)
115
+ }
116
+
117
+ export async function getAgentTaskWithSteps(taskId) {
118
+ const task = await db.agentTasks.get(taskId)
119
+ if (!task) return null
120
+
121
+ let steps = []
122
+ try {
123
+ steps = await db.agentTaskSteps.where('taskId').equals(taskId).toArray()
124
+ } catch (err) {
125
+ void err
126
+ try {
127
+ const res = await fetch(`/api/tasks/${taskId}/steps`).then((r) => r.json())
128
+ if (res?.data) steps = res.data
129
+ } catch (e) {
130
+ console.warn('[taskStore] Failed to fetch task steps:', e)
131
+ }
132
+ }
133
+
134
+ steps.sort((a, b) => {
135
+ const idxA = a.stepIndex ?? a.step_index ?? a.index ?? 0
136
+ const idxB = b.stepIndex ?? b.step_index ?? b.index ?? 0
137
+ return idxA - idxB
138
+ })
139
+
140
+ return { ...task, steps }
141
+ }
142
+
143
+ export async function listAgentTasks({ status, limit = 50 } = {}) {
144
+ const collection = status
145
+ ? db.agentTasks.where('status').equals(status).reverse()
146
+ : db.agentTasks.orderBy('updatedAt').reverse()
147
+ return collection.limit(limit).toArray()
148
+ }
149
+
150
+ export async function updateAgentTask(taskId, changes = {}) {
151
+ if (changes.status) assertTaskStatus(changes.status)
152
+ await db.agentTasks.update(taskId, { ...changes, updatedAt: now() })
153
+ return getAgentTask(taskId)
154
+ }
155
+
156
+ export async function updateAgentTaskStep(stepId, changes = {}) {
157
+ if (changes.status) assertStepStatus(changes.status)
158
+ await db.agentTaskSteps.update(stepId, { ...changes, updatedAt: now() })
159
+ return db.agentTaskSteps.get(stepId)
160
+ }
161
+
162
+ export async function startAgentTask(taskId) {
163
+ return updateAgentTask(taskId, { status: 'running', error: null })
164
+ }
165
+
166
+ // Resume hanya membuka kembali task paused dan mengaktifkan step pointer terakhir.
167
+ export async function resumeAgentTask(taskId) {
168
+ const task = await getAgentTaskWithSteps(taskId)
169
+ if (!task) throw new Error('Task tidak ditemukan')
170
+ if (!['paused', 'pending'].includes(task.status)) {
171
+ throw new Error('Task tidak bisa di-resume dari status ' + task.status)
172
+ }
173
+ if (!task.activeStepId) return updateAgentTask(taskId, { status: 'completed', error: null })
174
+ return startAgentTaskStep(taskId, task.activeStepId)
175
+ }
176
+
177
+ // Retry manual mengulang step failed/needs_revision dengan batas yang sama seperti executor.
178
+ export async function retryAgentTaskStep(taskId, stepId) {
179
+ const task = await getAgentTaskWithSteps(taskId)
180
+ const step = task?.steps?.find((item) => item.id === stepId)
181
+ if (!task || !step) throw new Error('Task step tidak ditemukan')
182
+ if (!['failed', 'needs_revision'].includes(step.status)) {
183
+ throw new Error('Step belum berada pada status retryable')
184
+ }
185
+ if ((step.attempts || 0) >= (task.maxRetries || 2) + 1) {
186
+ throw new Error('Batas retry step sudah tercapai')
187
+ }
188
+ return startAgentTaskStep(taskId, stepId)
189
+ }
190
+
191
+ export async function startAgentTaskStep(taskId, stepId) {
192
+ // Step aktif ditandai running saat executor mulai memproses step itu.
193
+ const timestamp = now()
194
+ return db.transaction('rw', db.agentTasks, db.agentTaskSteps, async () => {
195
+ const step = await db.agentTaskSteps.get(stepId)
196
+ const currentTaskId = step?.taskId || step?.task_id
197
+ if (!step || (currentTaskId && currentTaskId !== taskId))
198
+ throw new Error('Task step tidak ditemukan')
199
+ await db.agentTaskSteps.update(stepId, {
200
+ status: 'running',
201
+ attempts: (step.attempts || 0) + 1,
202
+ startedAt: step.startedAt || timestamp,
203
+ error: null,
204
+ updatedAt: timestamp
205
+ })
206
+ await db.agentTasks.update(taskId, {
207
+ status: 'running',
208
+ activeStepId: stepId,
209
+ currentStepIndex: step.stepIndex ?? step.step_index ?? step.index ?? 0,
210
+ updatedAt: timestamp
211
+ })
212
+ return db.agentTaskSteps.get(stepId)
213
+ })
214
+ }
215
+
216
+ export async function checkpointAgentTaskStep(taskId, stepId, checkpoint = {}) {
217
+ // Checkpoint ini menyimpan hasil step dan memajukan pointer ke step pending berikutnya.
218
+ const timestamp = now()
219
+ return db.transaction('rw', db.agentTasks, db.agentTaskSteps, async () => {
220
+ const step = await db.agentTaskSteps.get(stepId)
221
+ const task = await db.agentTasks.get(taskId)
222
+ const currentTaskId = step?.taskId || step?.task_id
223
+ if (!step || (currentTaskId && currentTaskId !== taskId) || !task)
224
+ throw new Error('Task checkpoint tidak ditemukan')
225
+ if (checkpoint.status) assertStepStatus(checkpoint.status)
226
+ await db.agentTaskSteps.update(stepId, {
227
+ ...checkpoint,
228
+ updatedAt: timestamp,
229
+ ...(checkpoint.status === 'completed'
230
+ ? { completedAt: checkpoint.completedAt || timestamp }
231
+ : {})
232
+ })
233
+ const taskChanges = { updatedAt: timestamp }
234
+ if (checkpoint.status === 'completed') {
235
+ let allSteps = []
236
+ try {
237
+ allSteps = await db.agentTaskSteps.where('taskId').equals(taskId).toArray()
238
+ } catch (err) {
239
+ void err
240
+ }
241
+ if (!allSteps || allSteps.length === 0) {
242
+ try {
243
+ const res = await fetch(`/api/tasks/${taskId}/steps`).then((r) => r.json())
244
+ if (res?.data) allSteps = res.data
245
+ } catch (e) {
246
+ void e
247
+ }
248
+ }
249
+ const next = (allSteps || [])
250
+ .filter((item) => item.id !== stepId && ['pending', 'needs_revision'].includes(item.status))
251
+ .sort(
252
+ (a, b) =>
253
+ (a.stepIndex ?? a.step_index ?? a.index ?? 0) -
254
+ (b.stepIndex ?? b.step_index ?? b.index ?? 0)
255
+ )[0]
256
+ taskChanges.activeStepId = next?.id || null
257
+ taskChanges.active_step_id = next?.id || null
258
+ taskChanges.currentStepIndex =
259
+ next?.stepIndex ??
260
+ next?.step_index ??
261
+ next?.index ??
262
+ step.stepIndex ??
263
+ step.step_index ??
264
+ step.index ??
265
+ 0
266
+ if (!next) {
267
+ taskChanges.status = 'completed'
268
+ taskChanges.completedAt = timestamp
269
+ taskChanges.activeStepId = null
270
+ taskChanges.active_step_id = null
271
+ }
272
+ }
273
+ await db.agentTasks.update(taskId, taskChanges)
274
+ return getAgentTaskWithSteps(taskId)
275
+ })
276
+ }
277
+
278
+ export async function transitionAgentTask(taskId, status, error = null) {
279
+ assertTaskStatus(status)
280
+ return updateAgentTask(taskId, {
281
+ status,
282
+ error,
283
+ ...(status === 'completed' ? { completedAt: now() } : {})
284
+ })
285
+ }
286
+
287
+ export async function pauseStaleAgentTasks(reason = 'app_restart') {
288
+ // Saat app hidup lagi, task yang masih running diparkir dulu agar tidak dianggap selesai.
289
+ const active = await db.agentTasks.where('status').anyOf(['running', 'waiting_user']).toArray()
290
+ if (!active.length) return 0
291
+ const timestamp = now()
292
+ await db.transaction('rw', db.agentTasks, async () => {
293
+ await Promise.all(
294
+ active.map((task) =>
295
+ db.agentTasks.update(task.id, {
296
+ status: 'paused',
297
+ error: reason,
298
+ updatedAt: timestamp
299
+ })
300
+ )
301
+ )
302
+ })
303
+ return active.length
304
+ }
305
+
306
+ export async function cancelAgentTask(taskId) {
307
+ return transitionAgentTask(taskId, 'cancelled', 'cancelled_by_user')
308
+ }
309
+
310
+ export async function deleteAgentTask(taskId) {
311
+ await db.transaction('rw', db.agentTasks, db.agentTaskSteps, async () => {
312
+ await db.agentTaskSteps.where('taskId').equals(taskId).delete()
313
+ await db.agentTasks.delete(taskId)
314
+ })
315
+ }