@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,168 @@
|
|
|
1
|
+
import { WebSocketServer, WebSocket } from 'ws'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WebSocket Hub untuk MARK Core.
|
|
5
|
+
* Mengelola koneksi real-time antara Node.js Core Backend, WebUI Client, dan CLI Streamer.
|
|
6
|
+
*/
|
|
7
|
+
class WebSocketHub {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.wss = null
|
|
10
|
+
this.clients = new Set()
|
|
11
|
+
this.eventHandlers = new Map()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Inisialisasi WebSocket Server pada HTTP Server yang ada
|
|
16
|
+
* @param {import('http').Server} server
|
|
17
|
+
*/
|
|
18
|
+
init(server) {
|
|
19
|
+
this.wss = new WebSocketServer({ server, path: '/stream' })
|
|
20
|
+
this.uiClients = new Set()
|
|
21
|
+
|
|
22
|
+
this.wss.on('error', (err) => {
|
|
23
|
+
// Delegasikan error EADDRINUSE ke HTTP Server listener agar auto-fallback berjalan
|
|
24
|
+
if (err.code !== 'EADDRINUSE') {
|
|
25
|
+
console.error('[WebSocket Hub Error]:', err)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
this.wss.on('connection', (ws) => {
|
|
30
|
+
this.clients.add(ws)
|
|
31
|
+
|
|
32
|
+
// Kirim event sambutan ready dan status presence UI terkini
|
|
33
|
+
this.send(ws, 'core:ready', {
|
|
34
|
+
version: '5.0.0',
|
|
35
|
+
timestamp: Date.now()
|
|
36
|
+
})
|
|
37
|
+
this.send(ws, 'ui:status', {
|
|
38
|
+
active: this.uiClients.size > 0,
|
|
39
|
+
count: this.uiClients.size
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
ws.on('message', (data) => {
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(data.toString())
|
|
45
|
+
const { event, id, payload } = parsed
|
|
46
|
+
|
|
47
|
+
if (event === 'ui:presence') {
|
|
48
|
+
this.uiClients.add(ws)
|
|
49
|
+
this.broadcast('ui:status', {
|
|
50
|
+
active: true,
|
|
51
|
+
count: this.uiClients.size
|
|
52
|
+
})
|
|
53
|
+
} else if (event === 'ui:goodbye') {
|
|
54
|
+
this.uiClients.delete(ws)
|
|
55
|
+
this.broadcast('ui:status', {
|
|
56
|
+
active: this.uiClients.size > 0,
|
|
57
|
+
count: this.uiClients.size
|
|
58
|
+
})
|
|
59
|
+
} else if (event) {
|
|
60
|
+
this.handleIncomingEvent(ws, event, payload, id)
|
|
61
|
+
}
|
|
62
|
+
} catch (_) {}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const cleanupClient = () => {
|
|
66
|
+
this.clients.delete(ws)
|
|
67
|
+
if (this.uiClients.has(ws)) {
|
|
68
|
+
this.uiClients.delete(ws)
|
|
69
|
+
this.broadcast('ui:status', {
|
|
70
|
+
active: this.uiClients.size > 0,
|
|
71
|
+
count: this.uiClients.size
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
ws.on('close', cleanupClient)
|
|
77
|
+
ws.on('error', cleanupClient)
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Daftarkan handler event masuk dari client
|
|
83
|
+
* @param {string} event
|
|
84
|
+
* @param {Function} handler
|
|
85
|
+
*/
|
|
86
|
+
on(event, handler) {
|
|
87
|
+
if (!this.eventHandlers.has(event)) {
|
|
88
|
+
this.eventHandlers.set(event, [])
|
|
89
|
+
}
|
|
90
|
+
this.eventHandlers.get(event).push(handler)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Handle event yang dikirim dari WebUI / CLI
|
|
95
|
+
*/
|
|
96
|
+
async handleIncomingEvent(ws, event, payload, id) {
|
|
97
|
+
const handlers = this.eventHandlers.get(event)
|
|
98
|
+
if (handlers && handlers.length > 0) {
|
|
99
|
+
for (const handler of handlers) {
|
|
100
|
+
try {
|
|
101
|
+
const result = await handler(payload, ws)
|
|
102
|
+
if (id) {
|
|
103
|
+
this.send(ws, `${event}:response`, { id, success: true, data: result })
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
if (id) {
|
|
107
|
+
this.send(ws, `${event}:response`, { id, success: false, error: err.message })
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
if (id) {
|
|
113
|
+
this.send(ws, `${event}:response`, {
|
|
114
|
+
id,
|
|
115
|
+
success: false,
|
|
116
|
+
error: `No handler for event ${event}`
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Kirim pesan ke client tertentu
|
|
124
|
+
* @param {WebSocket} ws
|
|
125
|
+
* @param {string} event
|
|
126
|
+
* @param {any} payload
|
|
127
|
+
*/
|
|
128
|
+
send(ws, event, payload) {
|
|
129
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
130
|
+
ws.send(JSON.stringify({ event, payload }))
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Broadcast event ke SEMUA client yang terhubung (WebUI, CLI, dll)
|
|
136
|
+
* @param {string} event
|
|
137
|
+
* @param {any} payload
|
|
138
|
+
*/
|
|
139
|
+
broadcast(event, payload) {
|
|
140
|
+
const message = JSON.stringify({ event, payload })
|
|
141
|
+
for (const client of this.clients) {
|
|
142
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
143
|
+
client.send(message)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Streaming token AI langsung ke semua client
|
|
150
|
+
* @param {string} token
|
|
151
|
+
* @param {string} [type='answer'] 'thought' | 'answer'
|
|
152
|
+
*/
|
|
153
|
+
streamToken(token, type = 'answer') {
|
|
154
|
+
this.broadcast('ai:token', { token, type, timestamp: Date.now() })
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Emit status tool execution
|
|
159
|
+
* @param {string} toolName
|
|
160
|
+
* @param {string} status 'start' | 'running' | 'done' | 'error'
|
|
161
|
+
* @param {any} [data=null]
|
|
162
|
+
*/
|
|
163
|
+
emitToolStatus(toolName, status, data = null) {
|
|
164
|
+
this.broadcast('tool:status', { tool: toolName, status, data, timestamp: Date.now() })
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const wsHub = new WebSocketHub()
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Mark Tag Parser (V5)
|
|
3
|
+
* Modul isomorphic murni (kompatibel dengan Node.js backend dan browser frontend).
|
|
4
|
+
* Mendukung fixed schema (<mark mood="..." done="..." />), CoT reasoning (<think>),
|
|
5
|
+
* serta sanitasi tag legacy (<mood:...>, [mood:...]) dari teks output.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const VALID_MOODS = [
|
|
9
|
+
'neutral',
|
|
10
|
+
'joy',
|
|
11
|
+
'happy',
|
|
12
|
+
'sadness',
|
|
13
|
+
'sad',
|
|
14
|
+
'fear',
|
|
15
|
+
'anger',
|
|
16
|
+
'angry',
|
|
17
|
+
'disgust',
|
|
18
|
+
'anxiety',
|
|
19
|
+
'envy',
|
|
20
|
+
'embarrassment',
|
|
21
|
+
'ennui',
|
|
22
|
+
'thinking',
|
|
23
|
+
'sarcasm',
|
|
24
|
+
'focused',
|
|
25
|
+
'excited',
|
|
26
|
+
'confused',
|
|
27
|
+
'annoyed',
|
|
28
|
+
'calm'
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
export const MOOD_NORMALIZATION = {
|
|
32
|
+
happy: 'joy',
|
|
33
|
+
sad: 'sadness',
|
|
34
|
+
angry: 'anger',
|
|
35
|
+
bored: 'ennui',
|
|
36
|
+
anxious: 'anxiety'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const MARK_TAG_SCHEMA = {
|
|
40
|
+
mood: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
default: 'neutral',
|
|
43
|
+
validate: (val) => {
|
|
44
|
+
if (!val) return 'neutral'
|
|
45
|
+
const clean = String(val).trim().toLowerCase()
|
|
46
|
+
const normalized = MOOD_NORMALIZATION[clean] || clean
|
|
47
|
+
return VALID_MOODS.includes(normalized) ? normalized : 'neutral'
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
done: {
|
|
51
|
+
type: 'boolean',
|
|
52
|
+
default: false,
|
|
53
|
+
validate: (val) => {
|
|
54
|
+
if (val === true || val === 'true' || val === 1 || val === '1') return true
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const MARK_TAG_REGEX = /<mark\b([^>]*)\/?>/i
|
|
61
|
+
const THINK_BLOCK_REGEX = /<think>([\s\S]*?)<\/think>/i
|
|
62
|
+
const LEGACY_MOOD_REGEX = /(?:<|\[)mood:[a-zA-Z0-9_-]+(?:>|\])/gi
|
|
63
|
+
const LEGACY_DONE_REGEX = /<\/?done\s*\/?>/gi
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Parsing atribut XML dari tag string ke objek JavaScript sesuai fixed schema.
|
|
67
|
+
* @param {string} attrString - String atribut di dalam tag <mark ...>
|
|
68
|
+
* @returns {Record<string, any>} Objek metadata yang divalidasi
|
|
69
|
+
*/
|
|
70
|
+
export function parseAttributes(attrString) {
|
|
71
|
+
const result = {
|
|
72
|
+
mood: MARK_TAG_SCHEMA.mood.default,
|
|
73
|
+
done: MARK_TAG_SCHEMA.done.default,
|
|
74
|
+
hasExplicitDone: false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!attrString || typeof attrString !== 'string') {
|
|
78
|
+
return result
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const attrPattern = /([a-zA-Z0-9_-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/g
|
|
82
|
+
let match
|
|
83
|
+
|
|
84
|
+
while ((match = attrPattern.exec(attrString)) !== null) {
|
|
85
|
+
const rawKey = match[1].toLowerCase()
|
|
86
|
+
const rawValue = match[2] ?? match[3] ?? match[4] ?? ''
|
|
87
|
+
|
|
88
|
+
if (rawKey in MARK_TAG_SCHEMA) {
|
|
89
|
+
result[rawKey] = MARK_TAG_SCHEMA[rawKey].validate(rawValue)
|
|
90
|
+
if (rawKey === 'done') {
|
|
91
|
+
result.hasExplicitDone = true
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Membersihkan semua tag kontrol (V5 dan legacy) dari teks agar riwayat dan tampilan bersih.
|
|
101
|
+
* @param {string} text - Teks mentah dari AI
|
|
102
|
+
* @returns {string} Teks bersih bebas dari tag kontrol
|
|
103
|
+
*/
|
|
104
|
+
export function stripMarkTags(text) {
|
|
105
|
+
if (!text || typeof text !== 'string') return ''
|
|
106
|
+
|
|
107
|
+
return text
|
|
108
|
+
.replace(THINK_BLOCK_REGEX, '')
|
|
109
|
+
.replace(MARK_TAG_REGEX, '')
|
|
110
|
+
.replace(/<\/mark>/gi, '')
|
|
111
|
+
.replace(LEGACY_MOOD_REGEX, '')
|
|
112
|
+
.replace(LEGACY_DONE_REGEX, '')
|
|
113
|
+
.replace(/^\s+/, '')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Parsing teks respons AI untuk mengekstrak metadata <mark>, CoT reasoning, dan cleanContent.
|
|
118
|
+
* @param {string} rawText - Teks mentah dari AI
|
|
119
|
+
* @returns {{ meta: { mood: string, done: boolean, hasTag: boolean, hasExplicitDone: boolean }, reasoning: string, cleanContent: string }}
|
|
120
|
+
*/
|
|
121
|
+
export function parseMarkTag(rawText) {
|
|
122
|
+
if (!rawText || typeof rawText !== 'string') {
|
|
123
|
+
return {
|
|
124
|
+
meta: {
|
|
125
|
+
mood: MARK_TAG_SCHEMA.mood.default,
|
|
126
|
+
done: MARK_TAG_SCHEMA.done.default,
|
|
127
|
+
hasTag: false,
|
|
128
|
+
hasExplicitDone: false
|
|
129
|
+
},
|
|
130
|
+
reasoning: '',
|
|
131
|
+
cleanContent: ''
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 1. Ekstraksi CoT Reasoning (<think>...</think>)
|
|
136
|
+
let reasoning = ''
|
|
137
|
+
const thinkMatch = rawText.match(THINK_BLOCK_REGEX)
|
|
138
|
+
if (thinkMatch) {
|
|
139
|
+
reasoning = thinkMatch[1].trim()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 2. Ekstraksi Tag <mark ... />
|
|
143
|
+
let meta = {
|
|
144
|
+
mood: MARK_TAG_SCHEMA.mood.default,
|
|
145
|
+
done: MARK_TAG_SCHEMA.done.default,
|
|
146
|
+
hasTag: false,
|
|
147
|
+
hasExplicitDone: false
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const markMatch = rawText.match(MARK_TAG_REGEX)
|
|
151
|
+
if (markMatch) {
|
|
152
|
+
const attrString = markMatch[1]
|
|
153
|
+
const parsedAttrs = parseAttributes(attrString)
|
|
154
|
+
meta = {
|
|
155
|
+
...parsedAttrs,
|
|
156
|
+
hasTag: true
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 3. Sanitasi teks untuk tampilan dan penyimpanan
|
|
161
|
+
const cleanContent = stripMarkTags(rawText)
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
meta,
|
|
165
|
+
reasoning,
|
|
166
|
+
cleanContent
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Factory untuk membuat stream filter stateful.
|
|
172
|
+
* Mem-buffer awal stream untuk mendeteksi <mark ... />, memicu onMeta, dan menyalurkan chunk bersih.
|
|
173
|
+
* @param {{ onMeta?: (meta: { mood: string, done: boolean }) => void, onChunk?: (chunk: string) => void }} callbacks
|
|
174
|
+
*/
|
|
175
|
+
export function createMarkStreamFilter({ onMeta, onChunk } = {}) {
|
|
176
|
+
let flushed = false
|
|
177
|
+
let buffer = ''
|
|
178
|
+
const MAX_BUFFER_LENGTH = 160
|
|
179
|
+
|
|
180
|
+
const emitMeta = (meta) => {
|
|
181
|
+
if (typeof onMeta === 'function') {
|
|
182
|
+
try {
|
|
183
|
+
onMeta(meta)
|
|
184
|
+
} catch (err) {
|
|
185
|
+
console.error('[MarkStreamFilter] Error in onMeta callback:', err)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const emitChunk = (chunk) => {
|
|
191
|
+
if (chunk && typeof onChunk === 'function') {
|
|
192
|
+
onChunk(chunk)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
/**
|
|
198
|
+
* Memproses potongan token baru dari stream.
|
|
199
|
+
* @param {string} chunk - Potongan teks token
|
|
200
|
+
*/
|
|
201
|
+
write(chunk) {
|
|
202
|
+
if (typeof chunk !== 'string') return
|
|
203
|
+
|
|
204
|
+
if (flushed) {
|
|
205
|
+
emitChunk(chunk)
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
buffer += chunk
|
|
210
|
+
|
|
211
|
+
// Cari apakah tag <mark ... /> sudah lengkap di dalam buffer
|
|
212
|
+
const markMatch = buffer.match(MARK_TAG_REGEX)
|
|
213
|
+
if (markMatch) {
|
|
214
|
+
const meta = parseAttributes(markMatch[1])
|
|
215
|
+
emitMeta(meta)
|
|
216
|
+
|
|
217
|
+
// Hapus tag <mark ... /> dari buffer dan trim newline awal jika ada
|
|
218
|
+
const tagIndex = buffer.indexOf(markMatch[0])
|
|
219
|
+
const afterTag = buffer.slice(tagIndex + markMatch[0].length).replace(/^[\r\n]+/, '')
|
|
220
|
+
flushed = true
|
|
221
|
+
buffer = ''
|
|
222
|
+
|
|
223
|
+
if (afterTag.length > 0) {
|
|
224
|
+
emitChunk(afterTag)
|
|
225
|
+
}
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Jika buffer melebihi batas atau ada baris baru tanpa awalan tag, flush default
|
|
230
|
+
const trimmed = buffer.trimStart()
|
|
231
|
+
const hasNewlineWithoutTag = trimmed.includes('\n') && !trimmed.startsWith('<')
|
|
232
|
+
if (buffer.length >= MAX_BUFFER_LENGTH || hasNewlineWithoutTag) {
|
|
233
|
+
emitMeta({
|
|
234
|
+
mood: MARK_TAG_SCHEMA.mood.default,
|
|
235
|
+
done: MARK_TAG_SCHEMA.done.default
|
|
236
|
+
})
|
|
237
|
+
flushed = true
|
|
238
|
+
const out = buffer
|
|
239
|
+
buffer = ''
|
|
240
|
+
emitChunk(out)
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Menuntaskan stream jika stream berakhir sebelum buffer mencapai batas.
|
|
246
|
+
*/
|
|
247
|
+
flush() {
|
|
248
|
+
if (flushed) return
|
|
249
|
+
|
|
250
|
+
const markMatch = buffer.match(MARK_TAG_REGEX)
|
|
251
|
+
if (markMatch) {
|
|
252
|
+
const meta = parseAttributes(markMatch[1])
|
|
253
|
+
emitMeta(meta)
|
|
254
|
+
const tagIndex = buffer.indexOf(markMatch[0])
|
|
255
|
+
const afterTag = buffer.slice(tagIndex + markMatch[0].length).replace(/^[\r\n]+/, '')
|
|
256
|
+
flushed = true
|
|
257
|
+
buffer = ''
|
|
258
|
+
if (afterTag.length > 0) {
|
|
259
|
+
emitChunk(afterTag)
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
emitMeta({
|
|
263
|
+
mood: MARK_TAG_SCHEMA.mood.default,
|
|
264
|
+
done: MARK_TAG_SCHEMA.done.default
|
|
265
|
+
})
|
|
266
|
+
flushed = true
|
|
267
|
+
const out = buffer
|
|
268
|
+
buffer = ''
|
|
269
|
+
if (out.length > 0) {
|
|
270
|
+
emitChunk(out)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|