@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,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
|
+
}
|