agentgui 1.0.930 → 1.0.932
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/AGENTS.md +35 -12
- package/database.js +31 -2
- package/lib/http-handler.js +11 -25
- package/lib/routes-registry.js +4 -48
- package/lib/server-startup.js +3 -11
- package/lib/ws-setup.js +2 -1
- package/package.json +3 -3
- package/server.js +7 -1
- package/site/app/js/app.js +64 -68
- package/site/app/js/backend.js +1 -1
- package/static/lib/xstate.umd.min.js +1 -1
- package/lib/db-queries-chunks.js +0 -195
- package/lib/db-queries-chunks2.js +0 -82
- package/lib/db-queries-cleanup.js +0 -74
- package/lib/db-queries-del.js +0 -141
- package/lib/db-queries-events.js +0 -68
- package/lib/db-queries-import.js +0 -133
- package/lib/db-queries-messages.js +0 -102
- package/lib/db-queries-sessions.js +0 -112
- package/lib/db-queries-streams.js +0 -100
- package/lib/db-queries.js +0 -89
- package/lib/jsonl-parser.js +0 -190
- package/lib/jsonl-watcher.js +0 -64
- package/lib/routes-agent-actions.js +0 -61
- package/lib/routes-auth-config.js +0 -30
- package/lib/routes-conversations.js +0 -96
- package/lib/routes-debug.js +0 -119
- package/lib/routes-messages.js +0 -139
- package/lib/routes-runs.js +0 -156
- package/lib/routes-scripts.js +0 -135
- package/lib/routes-sessions.js +0 -144
- package/lib/routes-threads.js +0 -100
- package/lib/routes-util.js +0 -110
- package/lib/ws-handlers-conv.js +0 -138
- package/lib/ws-handlers-conv2.js +0 -169
- package/lib/ws-handlers-msg.js +0 -121
- package/lib/ws-handlers-queue.js +0 -56
- package/lib/ws-handlers-run.js +0 -182
- package/lib/ws-handlers-scripts.js +0 -66
- package/lib/ws-handlers-session.js +0 -105
- package/lib/ws-handlers-session2.js +0 -85
- package/lib/ws-legacy-handlers.js +0 -51
- package/static/app.js +0 -261
- package/static/css/app-shell.css +0 -419
- package/static/css/brand-bible.css +0 -591
- package/static/css/colors_and_type.css +0 -568
- package/static/css/gmail-skin.css +0 -663
- package/static/css/main.css +0 -4015
- package/static/css/tools-popup.css +0 -472
- package/static/index.html +0 -418
- package/static/js/agent-auth.js +0 -146
- package/static/js/app-shortcuts.js +0 -30
- package/static/js/audio-recorder-processor.js +0 -18
- package/static/js/client-agents.js +0 -155
- package/static/js/client-cache.js +0 -171
- package/static/js/client-conv.js +0 -198
- package/static/js/client-events.js +0 -164
- package/static/js/client-exec.js +0 -160
- package/static/js/client-helpers.js +0 -199
- package/static/js/client-load.js +0 -175
- package/static/js/client-render.js +0 -132
- package/static/js/client-scroll.js +0 -178
- package/static/js/client-status.js +0 -167
- package/static/js/client-streaming.js +0 -117
- package/static/js/client-streaming2.js +0 -116
- package/static/js/client-streaming3.js +0 -153
- package/static/js/client-streaming4.js +0 -194
- package/static/js/client-ui-controls.js +0 -170
- package/static/js/client-ui.js +0 -128
- package/static/js/client-ui2.js +0 -160
- package/static/js/client-url.js +0 -93
- package/static/js/client-utils.js +0 -174
- package/static/js/client-ws-msg.js +0 -88
- package/static/js/client-ws.js +0 -161
- package/static/js/client.js +0 -145
- package/static/js/codec.js +0 -4
- package/static/js/conv-list-machine.js +0 -145
- package/static/js/conv-list-renderer.js +0 -198
- package/static/js/conv-machine.js +0 -110
- package/static/js/conv-sidebar-actions.js +0 -188
- package/static/js/conv-sidebar-clone.js +0 -91
- package/static/js/conversations.js +0 -116
- package/static/js/dialogs-types.js +0 -111
- package/static/js/dialogs.js +0 -53
- package/static/js/event-filter-config.js +0 -36
- package/static/js/event-processor.js +0 -181
- package/static/js/features.js +0 -187
- package/static/js/image-loader-element.js +0 -76
- package/static/js/image-loader.js +0 -146
- package/static/js/prompt-machine.js +0 -108
- package/static/js/recording-machine.js +0 -49
- package/static/js/script-runner.js +0 -192
- package/static/js/state-barrier.js +0 -105
- package/static/js/streaming-renderer-dispatch.js +0 -144
- package/static/js/streaming-renderer-events.js +0 -163
- package/static/js/streaming-renderer-events2.js +0 -125
- package/static/js/streaming-renderer-params.js +0 -38
- package/static/js/streaming-renderer-render-misc.js +0 -107
- package/static/js/streaming-renderer-render.js +0 -181
- package/static/js/streaming-renderer-render2.js +0 -149
- package/static/js/streaming-renderer-render3.js +0 -142
- package/static/js/streaming-renderer-static.js +0 -181
- package/static/js/streaming-renderer-static2.js +0 -140
- package/static/js/streaming-renderer-stream.js +0 -170
- package/static/js/streaming-renderer-text.js +0 -185
- package/static/js/streaming-renderer-tools.js +0 -189
- package/static/js/streaming-renderer-tools2.js +0 -92
- package/static/js/streaming-renderer.js +0 -200
- package/static/js/syntax-highlighter-render.js +0 -72
- package/static/js/syntax-highlighter.js +0 -131
- package/static/js/terminal-machine.js +0 -51
- package/static/js/terminal.js +0 -178
- package/static/js/ui-components-rendering.js +0 -62
- package/static/js/ui-components.js +0 -88
- package/static/js/websocket-manager.js +0 -107
- package/static/js/ws-client.js +0 -87
- package/static/js/ws-core.js +0 -162
- package/static/js/ws-latency.js +0 -88
- package/static/js/ws-machine.js +0 -68
- package/static/lib/msgpackr.min.js +0 -2
- package/static/theme.js +0 -74
- package/static/vendor/highlight-js.css +0 -10
- package/static/vendor/highlight.min.js +0 -1244
- package/static/vendor/prism-dark.css +0 -129
- package/static/vendor/rippleui.css +0 -35
- package/static/vendor/xterm-addon-fit.min.js +0 -8
- package/static/vendor/xterm.css +0 -8
- package/static/vendor/xterm.min.js +0 -8
package/static/js/client-exec.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
Object.assign(AgentGUIClient.prototype, {
|
|
2
|
-
async startExecution() {
|
|
3
|
-
const prompt = this.ui.messageInput?.value || '';
|
|
4
|
-
|
|
5
|
-
if (!prompt.trim()) {
|
|
6
|
-
this.showError('Please enter a prompt');
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
this.disableControls();
|
|
11
|
-
const ttsActive = window.TTSHandler && window.TTSHandler.getAutoSpeak && window.TTSHandler.getAutoSpeak();
|
|
12
|
-
const savedPrompt = ttsActive ? prompt + '\n\n[Respond optimized for text-to-speech: use short sentences, simple words, and focus on clarity.]' : prompt;
|
|
13
|
-
if (this.ui.messageInput) {
|
|
14
|
-
this.ui.messageInput.value = '';
|
|
15
|
-
this.ui.messageInput.style.height = 'auto';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const pendingId = 'pending-' + Date.now() + '-' + Math.random().toString(36).substr(2, 6);
|
|
19
|
-
|
|
20
|
-
const isStreaming = this._convIsStreaming(this.state.currentConversation?.id);
|
|
21
|
-
if (!isStreaming) {
|
|
22
|
-
this._showOptimisticMessage(pendingId, savedPrompt);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
let conv = this.state.currentConversation;
|
|
27
|
-
|
|
28
|
-
if (this._isLoadingConversation) {
|
|
29
|
-
this.showError('Conversation still loading. Please try again.');
|
|
30
|
-
this.enableControls();
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (conv && typeof conv === 'string') {
|
|
35
|
-
this.showError('Conversation state invalid. Please reload.');
|
|
36
|
-
this.enableControls();
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (conv?.id) {
|
|
41
|
-
const isNewConversation = !conv.messageCount && !this._convIsStreaming(conv.id);
|
|
42
|
-
const agentId = (isNewConversation ? this.getCurrentAgent() : null) || conv?.agentType || this.getCurrentAgent();
|
|
43
|
-
const subAgent = this.getEffectiveSubAgent() || conv?.subAgent || null;
|
|
44
|
-
const model = this.ui.modelSelector?.value || null;
|
|
45
|
-
|
|
46
|
-
this.lockAgentAndModel(agentId, model);
|
|
47
|
-
await this.streamToConversation(conv.id, savedPrompt, agentId, model, subAgent);
|
|
48
|
-
this.clearDraft(conv.id);
|
|
49
|
-
if (!isStreaming) {
|
|
50
|
-
this._confirmOptimisticMessage(pendingId);
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
const agentId = this.getCurrentAgent();
|
|
54
|
-
const subAgent = this.getEffectiveSubAgent() || null;
|
|
55
|
-
const model = this.ui.modelSelector?.value || null;
|
|
56
|
-
|
|
57
|
-
const body = { agentId, title: savedPrompt.substring(0, 50) };
|
|
58
|
-
if (model) body.model = model;
|
|
59
|
-
if (subAgent) body.subAgent = subAgent;
|
|
60
|
-
const { conversation } = await window.wsClient.rpc('conv.new', body);
|
|
61
|
-
window.ConversationState?.selectConversation(conversation.id, 'conversation_created', 1);
|
|
62
|
-
this.state.currentConversation = conversation;
|
|
63
|
-
this.lockAgentAndModel(agentId, model);
|
|
64
|
-
|
|
65
|
-
if (window.conversationManager) {
|
|
66
|
-
window.conversationManager.loadConversations();
|
|
67
|
-
window.conversationManager.select(conversation.id);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
await this.streamToConversation(conversation.id, savedPrompt, agentId, model, subAgent);
|
|
71
|
-
this.clearDraft(conversation.id);
|
|
72
|
-
this._confirmOptimisticMessage(pendingId);
|
|
73
|
-
}
|
|
74
|
-
} catch (error) {
|
|
75
|
-
console.error('Execution error:', error);
|
|
76
|
-
if (!isStreaming) {
|
|
77
|
-
this._failOptimisticMessage(pendingId, savedPrompt, error.message);
|
|
78
|
-
}
|
|
79
|
-
this.enableControls();
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
_showOptimisticMessage(pendingId, content) {
|
|
85
|
-
const messagesEl = document.querySelector('.conversation-messages');
|
|
86
|
-
if (!messagesEl) return;
|
|
87
|
-
messagesEl.querySelectorAll('p.text-secondary').forEach(p => p.remove());
|
|
88
|
-
const div = document.createElement('div');
|
|
89
|
-
div.className = 'message message-user message-sending';
|
|
90
|
-
div.id = pendingId;
|
|
91
|
-
div.innerHTML = `<div class="message-role">User</div><div class="message-text">${this.escapeHtml(content)}</div><div class="message-timestamp" style="opacity:0.5">Sending...</div>`;
|
|
92
|
-
messagesEl.appendChild(div);
|
|
93
|
-
this.scrollToBottom(true);
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
_confirmOptimisticMessage(pendingId) {
|
|
98
|
-
const el = document.getElementById(pendingId);
|
|
99
|
-
if (!el) return;
|
|
100
|
-
el.classList.remove('message-sending');
|
|
101
|
-
const ts = el.querySelector('.message-timestamp');
|
|
102
|
-
if (ts) {
|
|
103
|
-
ts.style.opacity = '1';
|
|
104
|
-
ts.textContent = new Date().toLocaleString();
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
_failOptimisticMessage(pendingId, content, errorMsg) {
|
|
110
|
-
const el = document.getElementById(pendingId);
|
|
111
|
-
if (!el) return;
|
|
112
|
-
el.classList.remove('message-sending');
|
|
113
|
-
el.classList.add('message-send-failed');
|
|
114
|
-
const ts = el.querySelector('.message-timestamp');
|
|
115
|
-
if (ts) {
|
|
116
|
-
ts.style.opacity = '1';
|
|
117
|
-
ts.innerHTML = `<span style="color:var(--color-error)">Failed: ${this.escapeHtml(errorMsg)}</span>`;
|
|
118
|
-
}
|
|
119
|
-
if (this.ui.messageInput) {
|
|
120
|
-
this.ui.messageInput.value = content;
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
_subscribeToConversationUpdates() {
|
|
126
|
-
if (!this.state.conversations || this.state.conversations.length === 0) return;
|
|
127
|
-
for (const conv of this.state.conversations) {
|
|
128
|
-
this.wsManager.subscribeToConversation(conv.id);
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
_flushBgCache(conversationId, sessionId) {
|
|
134
|
-
const entry = this._bgCache.get(conversationId);
|
|
135
|
-
if (!entry || entry.items.length === 0) return;
|
|
136
|
-
if (entry.sessionId !== sessionId) { this._bgCache.delete(conversationId); return; }
|
|
137
|
-
|
|
138
|
-
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
139
|
-
if (!streamingEl) return;
|
|
140
|
-
const blocksEl = streamingEl.querySelector('.streaming-blocks');
|
|
141
|
-
if (!blocksEl) return;
|
|
142
|
-
|
|
143
|
-
const seenSeqs = this._renderedSeqs[sessionId] || (this._renderedSeqs[sessionId] = new Set());
|
|
144
|
-
for (const item of entry.items) {
|
|
145
|
-
if (item.seq !== undefined && seenSeqs.has(item.seq)) continue;
|
|
146
|
-
try {
|
|
147
|
-
const block = (typeof msgpackr !== 'undefined' && item.packed instanceof Uint8Array)
|
|
148
|
-
? msgpackr.unpack(item.packed) : item.packed;
|
|
149
|
-
const el = this.renderer.renderBlock(block, { sessionId }, blocksEl);
|
|
150
|
-
if (el) {
|
|
151
|
-
if (item.seq !== undefined) seenSeqs.add(item.seq);
|
|
152
|
-
blocksEl.appendChild(el);
|
|
153
|
-
}
|
|
154
|
-
} catch (_) {}
|
|
155
|
-
}
|
|
156
|
-
this._bgCache.delete(conversationId);
|
|
157
|
-
this.scrollToBottom();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
});
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
Object.assign(AgentGUIClient.prototype, {
|
|
2
|
-
async _recoverMissedChunks() {
|
|
3
|
-
if (!this.state.currentSession?.id) return;
|
|
4
|
-
|
|
5
|
-
const sessionId = this.state.currentSession.id;
|
|
6
|
-
const lastSeq = this.wsManager.getLastSeq(sessionId);
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
const { chunks: rawChunks } = await window.wsClient.rpc('sess.chunks', { id: sessionId, sinceSeq: lastSeq });
|
|
10
|
-
if (!rawChunks || rawChunks.length === 0) return;
|
|
11
|
-
|
|
12
|
-
const chunks = rawChunks.map(c => ({
|
|
13
|
-
...c,
|
|
14
|
-
block: typeof c.data === 'string' ? JSON.parse(c.data) : c.data
|
|
15
|
-
})).filter(c => c.block && c.block.type);
|
|
16
|
-
|
|
17
|
-
const seenSeqs = (this._renderedSeqs || {})[sessionId];
|
|
18
|
-
const dedupedChunks = chunks.filter(c => !seenSeqs || !seenSeqs.has(c.sequence));
|
|
19
|
-
|
|
20
|
-
if (dedupedChunks.length > 0) {
|
|
21
|
-
for (const chunk of dedupedChunks) {
|
|
22
|
-
try { this.renderChunk(chunk); } catch (chunkErr) { console.warn('Skipping bad chunk:', chunkErr.message); }
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.warn('Chunk recovery failed:', e.message);
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
_dedupedFetch(key, fetchFn) {
|
|
32
|
-
if (this._inflightRequests.has(key)) {
|
|
33
|
-
return this._inflightRequests.get(key);
|
|
34
|
-
}
|
|
35
|
-
const promise = fetchFn().finally(() => {
|
|
36
|
-
this._inflightRequests.delete(key);
|
|
37
|
-
});
|
|
38
|
-
this._inflightRequests.set(key, promise);
|
|
39
|
-
return promise;
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
_insertPlaceholder(sessionId) {
|
|
44
|
-
this._removePlaceholder();
|
|
45
|
-
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
46
|
-
if (!streamingEl) return;
|
|
47
|
-
const blocksEl = streamingEl.querySelector('.streaming-blocks');
|
|
48
|
-
if (!blocksEl) return;
|
|
49
|
-
const ph = document.createElement('div');
|
|
50
|
-
ph.className = 'chunk-placeholder';
|
|
51
|
-
ph.id = 'chunk-placeholder-active';
|
|
52
|
-
blocksEl.appendChild(ph);
|
|
53
|
-
this._placeholderAutoRemove = setTimeout(() => this._removePlaceholder(), 500);
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
_removePlaceholder() {
|
|
58
|
-
if (this._placeholderAutoRemove) { clearTimeout(this._placeholderAutoRemove); this._placeholderAutoRemove = null; }
|
|
59
|
-
const ph = document.getElementById('chunk-placeholder-active');
|
|
60
|
-
if (ph && ph.parentNode) ph.remove();
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
_trackBlockHeight(block, element) {
|
|
65
|
-
if (!element || !block?.type) return;
|
|
66
|
-
const h = element.offsetHeight;
|
|
67
|
-
if (h <= 0) return;
|
|
68
|
-
if (!this._blockHeightAvg) this._blockHeightAvg = {};
|
|
69
|
-
const t = block.type;
|
|
70
|
-
if (!this._blockHeightAvg[t]) this._blockHeightAvg[t] = { sum: 0, count: 0 };
|
|
71
|
-
this._blockHeightAvg[t].sum += h;
|
|
72
|
-
this._blockHeightAvg[t].count++;
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
_estimatedBlockHeight(type) {
|
|
77
|
-
const defaults = { text: 40, tool_use: 60, tool_result: 40 };
|
|
78
|
-
if (this._blockHeightAvg?.[type]?.count >= 3) {
|
|
79
|
-
return this._blockHeightAvg[type].sum / this._blockHeightAvg[type].count;
|
|
80
|
-
}
|
|
81
|
-
return defaults[type] || 40;
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
_startThinkingCountdown() {
|
|
86
|
-
this._clearThinkingCountdown();
|
|
87
|
-
if (!this._lastSendTime) return;
|
|
88
|
-
const predicted = this.wsManager?.latency?.predicted || 0;
|
|
89
|
-
const estimatedWait = predicted + this._serverProcessingEstimate;
|
|
90
|
-
if (estimatedWait < 1000) return;
|
|
91
|
-
let remaining = Math.ceil(estimatedWait / 1000);
|
|
92
|
-
const update = () => {
|
|
93
|
-
const indicator = document.querySelector('.streaming-indicator');
|
|
94
|
-
if (!indicator) return;
|
|
95
|
-
if (remaining > 0) {
|
|
96
|
-
indicator.textContent = `Thinking... (~${remaining}s)`;
|
|
97
|
-
remaining--;
|
|
98
|
-
this._countdownTimer = setTimeout(update, 1000);
|
|
99
|
-
} else {
|
|
100
|
-
indicator.textContent = 'Thinking... (taking longer than expected)';
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
this._countdownTimer = setTimeout(update, 100);
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
_clearThinkingCountdown() {
|
|
108
|
-
if (this._countdownTimer) { clearTimeout(this._countdownTimer); this._countdownTimer = null; }
|
|
109
|
-
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
_setupDebugHooks() {
|
|
113
|
-
if (typeof window === 'undefined') return;
|
|
114
|
-
const self = this;
|
|
115
|
-
const snap = (actor) => (actor && actor.getSnapshot) ? actor.getSnapshot().value : 'uninitialized';
|
|
116
|
-
const mapSnap = (m) => m ? Object.fromEntries([...m].map(([k, a]) => [k, snap(a)])) : {};
|
|
117
|
-
const registry = {
|
|
118
|
-
getState: () => ({
|
|
119
|
-
latencyEma: self.wsManager?._latencyEma || null,
|
|
120
|
-
serverProcessingEstimate: self._serverProcessingEstimate,
|
|
121
|
-
latencyTrend: self.wsManager?.latency?.trend || null
|
|
122
|
-
}),
|
|
123
|
-
getSyncState: () => ({
|
|
124
|
-
currentConversation: self.state.currentConversation,
|
|
125
|
-
isStreaming: self._convIsStreaming(self.state.currentConversation?.id),
|
|
126
|
-
streamingConversations: Array.from(self.state.streamingConversations),
|
|
127
|
-
convMachineStates: mapSnap(window.__convMachines),
|
|
128
|
-
toolInstallMachineStates: mapSnap(window.__toolInstallMachines),
|
|
129
|
-
voiceMachineState: snap(window.__voiceMachine),
|
|
130
|
-
convListMachineState: snap(window.__convListMachine),
|
|
131
|
-
promptMachineState: snap(window.__promptMachine),
|
|
132
|
-
wsConnectionState: snap(self.wsManager?._wsActor),
|
|
133
|
-
rendererEventQueueLength: self.renderer?.eventQueue?.length || 0,
|
|
134
|
-
rendererEventHistoryLength: self.renderer?.eventHistory?.length || 0,
|
|
135
|
-
}),
|
|
136
|
-
getMessageState: () => {
|
|
137
|
-
const output = document.querySelector('.conversation-messages');
|
|
138
|
-
if (!output) return { error: 'No conversation output found' };
|
|
139
|
-
return {
|
|
140
|
-
messageCount: output.querySelectorAll('.message').length,
|
|
141
|
-
queueItems: output.querySelectorAll('.queue-item').length,
|
|
142
|
-
pendingMessages: output.querySelectorAll('.message-sending').length,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
const subkeys = {
|
|
147
|
-
machines: () => ({
|
|
148
|
-
conv: mapSnap(window.__convMachines),
|
|
149
|
-
toolInstall: mapSnap(window.__toolInstallMachines),
|
|
150
|
-
voice: snap(window.__voiceMachine),
|
|
151
|
-
convList: snap(window.__convListMachine),
|
|
152
|
-
prompt: snap(window.__promptMachine),
|
|
153
|
-
recording: snap(window.__recordingMachine),
|
|
154
|
-
terminal: snap(window.__terminalMachine),
|
|
155
|
-
ws: snap(self.wsManager?._wsActor),
|
|
156
|
-
}),
|
|
157
|
-
ws: () => ({
|
|
158
|
-
state: snap(self.wsManager?._wsActor),
|
|
159
|
-
latencyEma: self.wsManager?._latencyEma || null,
|
|
160
|
-
latencyTrend: self.wsManager?.latency?.trend || null,
|
|
161
|
-
url: window.__WS || null,
|
|
162
|
-
}),
|
|
163
|
-
auth: () => ({
|
|
164
|
-
state: window.__agentAuthState || null,
|
|
165
|
-
oauth: window.__agentAuthOAuth || null,
|
|
166
|
-
}),
|
|
167
|
-
perf: () => window.__convPerfMetrics || null,
|
|
168
|
-
config: () => ({ base: window.__BASE || null, server: window.__SERVER || null }),
|
|
169
|
-
renderer: () => ({
|
|
170
|
-
eventQueueLength: self.renderer?.eventQueue?.length || 0,
|
|
171
|
-
eventHistoryLength: self.renderer?.eventHistory?.length || 0,
|
|
172
|
-
}),
|
|
173
|
-
conv: () => ({
|
|
174
|
-
current: self.state.currentConversation,
|
|
175
|
-
streaming: Array.from(self.state.streamingConversations),
|
|
176
|
-
}),
|
|
177
|
-
};
|
|
178
|
-
for (const [k, fn] of Object.entries(subkeys)) Object.defineProperty(registry, k, { get: fn, enumerable: true });
|
|
179
|
-
window.__debug = registry;
|
|
180
|
-
},
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
showLoadingSpinner() {
|
|
184
|
-
document.documentElement.style.pointerEvents = 'auto';
|
|
185
|
-
const indicator = document.querySelector('[data-model-dl-indicator]');
|
|
186
|
-
if (indicator && !indicator.classList.contains('visible')) {
|
|
187
|
-
indicator.classList.add('visible');
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
hideLoadingSpinner() {
|
|
193
|
-
const indicator = document.querySelector('[data-model-dl-indicator]');
|
|
194
|
-
if (indicator && indicator.classList.contains('visible')) {
|
|
195
|
-
indicator.classList.remove('visible');
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
});
|
package/static/js/client-load.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
Object.assign(AgentGUIClient.prototype, {
|
|
2
|
-
_makeLoadRequest(conversationId) {
|
|
3
|
-
const requestId = ++this._currentRequestId;
|
|
4
|
-
const abortController = new AbortController();
|
|
5
|
-
|
|
6
|
-
if (this._loadInProgress[conversationId]) {
|
|
7
|
-
const prevReq = this._loadInProgress[conversationId];
|
|
8
|
-
try {
|
|
9
|
-
prevReq.abortController.abort();
|
|
10
|
-
} catch (e) {}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
this._loadInProgress[conversationId] = {
|
|
14
|
-
requestId,
|
|
15
|
-
abortController,
|
|
16
|
-
timestamp: Date.now(),
|
|
17
|
-
prevConversationId: this.state.currentConversation?.id
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
return { requestId, abortController: abortController.signal };
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
_verifyRequestId(conversationId, requestId) {
|
|
25
|
-
const current = this._loadInProgress[conversationId];
|
|
26
|
-
if (!current) return false;
|
|
27
|
-
if (current.requestId !== requestId) return false;
|
|
28
|
-
return true;
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
_completeLoadRequest(conversationId, requestId) {
|
|
33
|
-
const req = this._loadInProgress[conversationId];
|
|
34
|
-
if (req && req.requestId === requestId) {
|
|
35
|
-
delete this._loadInProgress[conversationId];
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
async _loadConvRender(conversationId, convSignal, prevConversationId, availableFallback, fullData) {
|
|
41
|
-
const { conversation, isActivelyStreaming, latestSession, chunks: rawChunks, totalChunks, messages: allMessages } = fullData;
|
|
42
|
-
|
|
43
|
-
window.ConversationState?.selectConversation(conversationId, 'server_load', 1);
|
|
44
|
-
this.state.currentConversation = conversation;
|
|
45
|
-
window.dispatchEvent(new CustomEvent('conversation-changed', { detail: { conversationId, conversation } }));
|
|
46
|
-
const hasActivity = (allMessages && allMessages.length > 0) || isActivelyStreaming || latestSession || this._convIsStreaming(conversationId);
|
|
47
|
-
this.applyAgentAndModelSelection(conversation, hasActivity);
|
|
48
|
-
|
|
49
|
-
const queuePromise = window.wsClient.rpc('q.ls', { id: conversationId }).catch(() => ({ queue: [] }));
|
|
50
|
-
const chunks = (rawChunks || []).map(chunk => ({
|
|
51
|
-
...chunk,
|
|
52
|
-
block: typeof chunk.data === 'string' ? JSON.parse(chunk.data) : chunk.data
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
|
-
const { queue: queueResult } = await queuePromise;
|
|
56
|
-
const queuedMessageIds = new Set((queueResult || []).map(q => q.messageId));
|
|
57
|
-
const userMessages = (allMessages || []).filter(m => m.role === 'user' && !queuedMessageIds.has(m.id));
|
|
58
|
-
const hasMoreChunks = totalChunks && chunks.length < totalChunks;
|
|
59
|
-
|
|
60
|
-
const clientKnowsStreaming = this._convIsStreaming(conversationId);
|
|
61
|
-
const shouldResumeStreaming = latestSession &&
|
|
62
|
-
(latestSession.status === 'active' || latestSession.status === 'pending');
|
|
63
|
-
|
|
64
|
-
if (shouldResumeStreaming) {
|
|
65
|
-
this._setConvStreaming(conversationId, true, latestSession?.id, null);
|
|
66
|
-
window.dispatchEvent(new CustomEvent('ws-message', { detail: { type: 'streaming_start', conversationId, sessionId: latestSession?.id } }));
|
|
67
|
-
} else {
|
|
68
|
-
this._setConvStreaming(conversationId, false);
|
|
69
|
-
window.dispatchEvent(new CustomEvent('ws-message', { detail: { type: 'streaming_complete', conversationId } }));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (this.ui.messageInput) {
|
|
73
|
-
this.ui.messageInput.disabled = false;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const outputEl = document.getElementById('output');
|
|
77
|
-
if (outputEl) {
|
|
78
|
-
const wdInfo = conversation.workingDirectory ? `${this.escapeHtml(conversation.workingDirectory)}` : '';
|
|
79
|
-
const timestamp = new Date(conversation.created_at).toLocaleDateString();
|
|
80
|
-
const metaParts = [timestamp];
|
|
81
|
-
if (wdInfo) metaParts.push(wdInfo);
|
|
82
|
-
outputEl.innerHTML = `
|
|
83
|
-
<div class="conversation-header">
|
|
84
|
-
<h2>${this.escapeHtml(conversation.title || 'Conversation')}</h2>
|
|
85
|
-
<p class="text-secondary">${metaParts.join(' - ')}</p>
|
|
86
|
-
</div>
|
|
87
|
-
<div class="conversation-messages"></div>
|
|
88
|
-
`;
|
|
89
|
-
|
|
90
|
-
const messagesEl = outputEl.querySelector('.conversation-messages');
|
|
91
|
-
|
|
92
|
-
if (hasMoreChunks) {
|
|
93
|
-
const loadMoreBtn = document.createElement('button');
|
|
94
|
-
loadMoreBtn.className = 'btn btn-secondary';
|
|
95
|
-
loadMoreBtn.style.cssText = 'width:100%;margin-bottom:1rem;padding:0.5rem;font-size:0.8rem;';
|
|
96
|
-
loadMoreBtn.textContent = `Load earlier messages (${totalChunks - chunks.length} more chunks)`;
|
|
97
|
-
loadMoreBtn.addEventListener('click', async () => {
|
|
98
|
-
loadMoreBtn.disabled = true;
|
|
99
|
-
loadMoreBtn.textContent = 'Loading...';
|
|
100
|
-
try {
|
|
101
|
-
try {
|
|
102
|
-
await window.wsClient.rpc('conv.full', { id: conversationId, allChunks: true });
|
|
103
|
-
} catch (wsErr) {}
|
|
104
|
-
this.invalidateCache(conversationId);
|
|
105
|
-
await this.loadConversationMessages(conversationId);
|
|
106
|
-
} catch (e) {
|
|
107
|
-
loadMoreBtn.textContent = 'Failed to load. Try again.';
|
|
108
|
-
loadMoreBtn.disabled = false;
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
messagesEl.appendChild(loadMoreBtn);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (chunks.length > 0) {
|
|
115
|
-
const activeSessionId = (shouldResumeStreaming && latestSession) ? latestSession.id : null;
|
|
116
|
-
performance.mark(`conv-render-start:${conversationId}`);
|
|
117
|
-
await new Promise(r => requestAnimationFrame(r));
|
|
118
|
-
if (!convSignal.aborted) this._renderConversationContent(messagesEl, chunks, userMessages, activeSessionId);
|
|
119
|
-
performance.mark(`conv-render-complete:${conversationId}`);
|
|
120
|
-
performance.measure(`conv-render:${conversationId}`, `conv-render-start:${conversationId}`, `conv-render-complete:${conversationId}`);
|
|
121
|
-
performance.measure(`conv-data-fetch:${conversationId}`, `conv-load-start:${conversationId}`, `conv-data-received:${conversationId}`);
|
|
122
|
-
} else {
|
|
123
|
-
if (!convSignal.aborted) messagesEl.appendChild(this.renderMessagesFragment(allMessages || []));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (!convSignal.aborted && shouldResumeStreaming && latestSession && chunks.length === 0) {
|
|
127
|
-
const streamDiv = document.createElement('div');
|
|
128
|
-
streamDiv.id = `streaming-${latestSession.id}`;
|
|
129
|
-
streamDiv.className = 'streaming-message';
|
|
130
|
-
const indicatorDiv = document.createElement('div');
|
|
131
|
-
indicatorDiv.className = 'streaming-indicator';
|
|
132
|
-
indicatorDiv.style = 'display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0;color:var(--color-text-secondary);font-size:0.875rem;';
|
|
133
|
-
indicatorDiv.innerHTML = `
|
|
134
|
-
<span class="animate-spin" style="display:inline-block;width:1rem;height:1rem;border:2px solid var(--color-border);border-top-color:var(--color-primary);border-radius:50%;"></span>
|
|
135
|
-
<span class="streaming-indicator-label">Agent is starting...</span>
|
|
136
|
-
`;
|
|
137
|
-
streamDiv.appendChild(indicatorDiv);
|
|
138
|
-
messagesEl.appendChild(streamDiv);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (shouldResumeStreaming && latestSession) {
|
|
142
|
-
this._setConvStreaming(conversationId, true, latestSession.id, null);
|
|
143
|
-
this.state.currentSession = {
|
|
144
|
-
id: latestSession.id,
|
|
145
|
-
conversationId: conversationId,
|
|
146
|
-
agentId: conversation.agentType || null,
|
|
147
|
-
startTime: latestSession.created_at
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
if (this.wsManager.isConnected) {
|
|
151
|
-
this.wsManager.subscribeToSession(latestSession.id);
|
|
152
|
-
this.wsManager.sendMessage({ type: 'subscribe', conversationId });
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
this.updateUrlForConversation(conversationId, latestSession.id);
|
|
156
|
-
|
|
157
|
-
this._flushBgCache(conversationId, latestSession.id);
|
|
158
|
-
|
|
159
|
-
this.syncPromptState(conversationId);
|
|
160
|
-
} else {
|
|
161
|
-
this.syncPromptState(conversationId);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (this.ui.sendButton) {
|
|
165
|
-
this.ui.sendButton.disabled = false;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
this.restoreScrollPosition(conversationId);
|
|
169
|
-
this.setupScrollUpDetection(conversationId);
|
|
170
|
-
|
|
171
|
-
this.fetchAndRenderQueue(conversationId);
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
});
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
Object.assign(AgentGUIClient.prototype, {
|
|
2
|
-
renderCodeBlock(language, code) {
|
|
3
|
-
if (language.toLowerCase() === 'html') {
|
|
4
|
-
return `
|
|
5
|
-
<div class="message-code">
|
|
6
|
-
<div class="html-rendered-label">
|
|
7
|
-
Rendered HTML
|
|
8
|
-
</div>
|
|
9
|
-
<div class="html-content">
|
|
10
|
-
${this.sanitizeHtml(code)}
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
13
|
-
`;
|
|
14
|
-
} else {
|
|
15
|
-
const lineCount = code.split('\n').length;
|
|
16
|
-
return `<div class="message-code"><details class="collapsible-code"><summary class="collapsible-code-summary">${this.escapeHtml(language)} - ${lineCount} line${lineCount !== 1 ? 's' : ''}</summary><pre style="margin:0;border-radius:0 0 0.375rem 0.375rem">${this.escapeHtml(code)}</pre></details></div>`;
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
renderMessageContent(content) {
|
|
22
|
-
if (typeof content === 'string') {
|
|
23
|
-
if (this.isHtmlContent(content)) {
|
|
24
|
-
return `<div class="message-text"><div class="html-content">${this.sanitizeHtml(content)}</div></div>`;
|
|
25
|
-
}
|
|
26
|
-
return `<div class="message-text">${this.escapeHtml(content)}</div>`;
|
|
27
|
-
} else if (content && typeof content === 'object' && content.type === 'claude_execution') {
|
|
28
|
-
let html = '<div class="message-blocks">';
|
|
29
|
-
if (content.blocks && Array.isArray(content.blocks)) {
|
|
30
|
-
let pendingToolUseClose = false;
|
|
31
|
-
let pendingHasInput = false;
|
|
32
|
-
content.blocks.forEach((block, blockIdx, blocks) => {
|
|
33
|
-
if (block.type !== 'tool_result' && pendingToolUseClose) {
|
|
34
|
-
if (pendingHasInput) html += '</div>';
|
|
35
|
-
html += '</details>';
|
|
36
|
-
pendingToolUseClose = false;
|
|
37
|
-
pendingHasInput = false;
|
|
38
|
-
}
|
|
39
|
-
if (block.type === 'text') {
|
|
40
|
-
const parts = this.parseMarkdownCodeBlocks(block.text);
|
|
41
|
-
parts.forEach(part => {
|
|
42
|
-
if (part.type === 'html') {
|
|
43
|
-
html += `<div class="message-text"><div class="html-content">${this.sanitizeHtml(part.content)}</div></div>`;
|
|
44
|
-
} else if (part.type === 'text') {
|
|
45
|
-
html += `<div class="message-text">${this.escapeHtml(part.content)}</div>`;
|
|
46
|
-
} else if (part.type === 'code') {
|
|
47
|
-
html += this.renderCodeBlock(part.language, part.code);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
} else if (block.type === 'code_block') {
|
|
51
|
-
if (block.language === 'html') {
|
|
52
|
-
html += `
|
|
53
|
-
<div class="message-code">
|
|
54
|
-
<div class="html-rendered-label">
|
|
55
|
-
Rendered HTML
|
|
56
|
-
</div>
|
|
57
|
-
<div class="html-content">
|
|
58
|
-
${this.sanitizeHtml(block.code)}
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
`;
|
|
62
|
-
} else {
|
|
63
|
-
const blkLineCount = block.code.split('\n').length;
|
|
64
|
-
html += `<div class="message-code"><details class="collapsible-code"><summary class="collapsible-code-summary">${this.escapeHtml(block.language || 'code')} - ${blkLineCount} line${blkLineCount !== 1 ? 's' : ''}</summary><pre style="margin:0;border-radius:0 0 0.375rem 0.375rem">${this.escapeHtml(block.code)}</pre></details></div>`;
|
|
65
|
-
}
|
|
66
|
-
} else if (block.type === 'tool_use') {
|
|
67
|
-
let inputContentHtml = '';
|
|
68
|
-
const hasInput = block.input && Object.keys(block.input).length > 0;
|
|
69
|
-
if (hasInput) {
|
|
70
|
-
const inputStr = JSON.stringify(block.input, null, 2);
|
|
71
|
-
inputContentHtml = `<pre class="tool-input-pre">${this.escapeHtml(inputStr)}</pre>`;
|
|
72
|
-
}
|
|
73
|
-
const tn = block.name || 'unknown';
|
|
74
|
-
const hasRenderer = typeof StreamingRenderer !== 'undefined';
|
|
75
|
-
const dName = hasRenderer ? StreamingRenderer.getToolDisplayName(tn) : tn;
|
|
76
|
-
const tTitle = hasRenderer && block.input ? StreamingRenderer.getToolTitle(tn, block.input) : '';
|
|
77
|
-
const iconHtml = hasRenderer && this.renderer ? `<span class="folded-tool-icon">${this.renderer.getToolIcon(tn)}</span>` : '';
|
|
78
|
-
const typeClass = hasRenderer && this.renderer ? this.renderer._getBlockTypeClass('tool_use') : 'block-type-tool_use';
|
|
79
|
-
const toolColorClass = hasRenderer && this.renderer ? this.renderer._getToolColorClass(tn) : 'tool-color-default';
|
|
80
|
-
const nextBlock = blocks[blockIdx + 1];
|
|
81
|
-
const resultClass = nextBlock?.type === 'tool_result' ? (nextBlock.is_error ? 'has-error tool-result-error' : 'has-success tool-result-success') : '';
|
|
82
|
-
const resultStatusIcon = nextBlock?.type === 'tool_result'
|
|
83
|
-
? `<span class="folded-tool-status">${nextBlock.is_error
|
|
84
|
-
? '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/></svg>'
|
|
85
|
-
: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>'
|
|
86
|
-
}</span>` : '';
|
|
87
|
-
if (hasInput) {
|
|
88
|
-
html += `<details class="block-tool-use folded-tool ${typeClass} ${toolColorClass} ${resultClass}"><summary class="folded-tool-bar">${iconHtml}<span class="folded-tool-name">${this.escapeHtml(dName)}</span>${tTitle ? `<span class="folded-tool-desc">${this.escapeHtml(tTitle)}</span>` : ''}${resultStatusIcon}</summary><div class="folded-tool-body">${inputContentHtml}`;
|
|
89
|
-
pendingHasInput = true;
|
|
90
|
-
} else {
|
|
91
|
-
html += `<details class="block-tool-use folded-tool ${typeClass} ${toolColorClass} ${resultClass}"><summary class="folded-tool-bar">${iconHtml}<span class="folded-tool-name">${this.escapeHtml(dName)}</span>${tTitle ? `<span class="folded-tool-desc">${this.escapeHtml(tTitle)}</span>` : ''}${resultStatusIcon}</summary>`;
|
|
92
|
-
pendingHasInput = false;
|
|
93
|
-
}
|
|
94
|
-
pendingToolUseClose = true;
|
|
95
|
-
} else if (block.type === 'tool_result') {
|
|
96
|
-
const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
97
|
-
const smartHtml = typeof StreamingRenderer !== 'undefined' ? StreamingRenderer.renderSmartContentHTML(content, this.escapeHtml.bind(this), true) : `<pre class="tool-result-pre">${this.escapeHtml(content.length > 2000 ? content.substring(0, 2000) + '\n... (truncated)' : content)}</pre>`;
|
|
98
|
-
const resultContentHtml = `<div class="folded-tool-result-content">${smartHtml}</div>`;
|
|
99
|
-
if (pendingToolUseClose) {
|
|
100
|
-
if (pendingHasInput) {
|
|
101
|
-
html += resultContentHtml + '</div></details>';
|
|
102
|
-
} else {
|
|
103
|
-
html += `<div class="folded-tool-body">${resultContentHtml}</div></details>`;
|
|
104
|
-
}
|
|
105
|
-
pendingToolUseClose = false;
|
|
106
|
-
} else {
|
|
107
|
-
html += resultContentHtml;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
if (pendingToolUseClose) {
|
|
112
|
-
if (pendingHasInput) html += '</div>';
|
|
113
|
-
html += '</details>';
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
html += '</div>';
|
|
117
|
-
return html;
|
|
118
|
-
} else {
|
|
119
|
-
if (typeof content === 'object' && content !== null) {
|
|
120
|
-
const fieldsHtml = Object.entries(content)
|
|
121
|
-
.map(([key, value]) => {
|
|
122
|
-
let displayValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
123
|
-
if (displayValue.length > 150) displayValue = displayValue.substring(0, 150) + '...';
|
|
124
|
-
return `<div style="font-size:0.8rem;margin-bottom:0.375rem"><span style="font-weight:600">${this.escapeHtml(key)}:</span> <code style="background:var(--color-bg-secondary);padding:0.125rem 0.25rem;border-radius:0.25rem">${this.escapeHtml(displayValue)}</code></div>`;
|
|
125
|
-
}).join('');
|
|
126
|
-
return `<div class="message-text" style="background:var(--color-bg-secondary);padding:0.75rem;border-radius:0.375rem">${fieldsHtml}</div>`;
|
|
127
|
-
}
|
|
128
|
-
return `<div class="message-text">${this.escapeHtml(String(content))}</div>`;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
});
|