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
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
Object.assign(AgentGUIClient.prototype, {
|
|
2
|
-
async checkSpeechStatus() {
|
|
3
|
-
try {
|
|
4
|
-
const status = await window.wsClient.rpc('speech.status');
|
|
5
|
-
if (status.modelsComplete) {
|
|
6
|
-
this._modelDownloadProgress = { done: true, complete: true };
|
|
7
|
-
this._modelDownloadInProgress = false;
|
|
8
|
-
} else if (status.modelsDownloading) {
|
|
9
|
-
this._modelDownloadProgress = status.modelsProgress || { downloading: true };
|
|
10
|
-
this._modelDownloadInProgress = true;
|
|
11
|
-
} else {
|
|
12
|
-
this._modelDownloadProgress = { done: false };
|
|
13
|
-
this._modelDownloadInProgress = false;
|
|
14
|
-
}
|
|
15
|
-
} catch (error) {
|
|
16
|
-
console.error('Failed to check speech status:', error);
|
|
17
|
-
this._modelDownloadProgress = { done: false };
|
|
18
|
-
this._modelDownloadInProgress = false;
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
async loadModelsForAgent(agentId) {
|
|
24
|
-
if (!agentId || !this.ui.modelSelector) return;
|
|
25
|
-
const cached = this._modelCache.get(agentId);
|
|
26
|
-
if (cached) {
|
|
27
|
-
this._populateModelSelector(cached);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
const { models } = await window.wsClient.rpc('agent.models', { id: agentId });
|
|
32
|
-
this._modelCache.set(agentId, models);
|
|
33
|
-
this._populateModelSelector(models);
|
|
34
|
-
} catch (error) {
|
|
35
|
-
console.error('Failed to load models:', error);
|
|
36
|
-
this._populateModelSelector([]);
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
_populateModelSelector(models) {
|
|
42
|
-
if (!this.ui.modelSelector) return;
|
|
43
|
-
if (!models || models.length === 0) {
|
|
44
|
-
this.ui.modelSelector.innerHTML = '';
|
|
45
|
-
this.ui.modelSelector.setAttribute('data-empty', 'true');
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
this.ui.modelSelector.removeAttribute('data-empty');
|
|
49
|
-
this.ui.modelSelector.innerHTML = models
|
|
50
|
-
.map(m => `<option value="${m.id}">${this.escapeHtml(m.label)}</option>`)
|
|
51
|
-
.join('');
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
lockAgentAndModel(agentId, model) {
|
|
56
|
-
this._agentLocked = true;
|
|
57
|
-
if (this.ui.cliSelector) {
|
|
58
|
-
this.ui.cliSelector.disabled = true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this.loadModelsForAgent(agentId).then(() => {
|
|
62
|
-
if (this.ui.modelSelector && model) {
|
|
63
|
-
this.ui.modelSelector.value = model;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
unlockAgentAndModel() {
|
|
70
|
-
this._agentLocked = false;
|
|
71
|
-
if (this.ui.cliSelector) {
|
|
72
|
-
this.ui.cliSelector.disabled = false;
|
|
73
|
-
if (this.ui.cliSelector.options.length > 0) {
|
|
74
|
-
this.ui.cliSelector.style.display = 'inline-block';
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (this.ui.modelSelector) {
|
|
78
|
-
this.ui.modelSelector.disabled = false;
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
applyAgentAndModelSelection(conversation, hasActivity) {
|
|
84
|
-
const agentId = conversation.agentId || conversation.agentType || null;
|
|
85
|
-
const model = conversation.model || null;
|
|
86
|
-
const subAgent = conversation.subAgent || null;
|
|
87
|
-
|
|
88
|
-
if (hasActivity) {
|
|
89
|
-
this._setCliSelectorToAgent(agentId);
|
|
90
|
-
this.lockAgentAndModel(agentId, model);
|
|
91
|
-
} else {
|
|
92
|
-
this.unlockAgentAndModel();
|
|
93
|
-
this._setCliSelectorToAgent(agentId);
|
|
94
|
-
|
|
95
|
-
const effectiveAgentId = agentId || this.getEffectiveAgentId();
|
|
96
|
-
this.loadSubAgentsForCli(effectiveAgentId).then(() => {
|
|
97
|
-
if (subAgent && this.ui.agentSelector) {
|
|
98
|
-
this.ui.agentSelector.value = subAgent;
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
this.loadModelsForAgent(effectiveAgentId).then(() => {
|
|
102
|
-
if (model && this.ui.modelSelector) {
|
|
103
|
-
this.ui.modelSelector.value = model;
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
_setCliSelectorToAgent(agentId) {
|
|
111
|
-
if (this.ui.cliSelector) {
|
|
112
|
-
this.ui.cliSelector.value = agentId;
|
|
113
|
-
if (!this.ui.cliSelector.value) {
|
|
114
|
-
this.ui.cliSelector.selectedIndex = 0;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
async loadConversations() {
|
|
121
|
-
const now = Date.now();
|
|
122
|
-
if (this.conversationListCache.data.length > 0 &&
|
|
123
|
-
(now - this.conversationListCache.timestamp) < this.conversationListCache.ttl) {
|
|
124
|
-
this.state.conversations = this.conversationListCache.data;
|
|
125
|
-
return this.conversationListCache.data;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return this._dedupedFetch('loadConversations', async () => {
|
|
129
|
-
try {
|
|
130
|
-
const { conversations } = await window.wsClient.rpc('conv.ls');
|
|
131
|
-
this.state.conversations = conversations;
|
|
132
|
-
this.conversationListCache.data = conversations;
|
|
133
|
-
this.conversationListCache.timestamp = Date.now();
|
|
134
|
-
return conversations;
|
|
135
|
-
} catch (error) {
|
|
136
|
-
console.error('Failed to load conversations:', error);
|
|
137
|
-
return [];
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
updateConnectionStatus(status) {
|
|
144
|
-
if (this.ui.statusIndicator) {
|
|
145
|
-
this.ui.statusIndicator.dataset.status = status;
|
|
146
|
-
this.ui.statusIndicator.textContent = status.charAt(0).toUpperCase() + status.slice(1);
|
|
147
|
-
}
|
|
148
|
-
if (status === 'disconnected' || status === 'reconnecting') {
|
|
149
|
-
this._updateConnectionIndicator(status);
|
|
150
|
-
} else if (status === 'connected') {
|
|
151
|
-
this._updateConnectionIndicator(this.wsManager?.latency?.quality || 'unknown');
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
});
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
Object.assign(AgentGUIClient.prototype, {
|
|
2
|
-
cacheCurrentConversation() {
|
|
3
|
-
const convId = this.state.currentConversation?.id;
|
|
4
|
-
if (!convId) return;
|
|
5
|
-
const outputEl = document.getElementById('output');
|
|
6
|
-
if (!outputEl || !outputEl.firstChild) return;
|
|
7
|
-
if (this._convIsStreaming(convId)) return;
|
|
8
|
-
|
|
9
|
-
this.saveScrollPosition(convId);
|
|
10
|
-
const clone = outputEl.cloneNode(true);
|
|
11
|
-
this.conversationCache.set(convId, {
|
|
12
|
-
dom: clone,
|
|
13
|
-
conversation: this.state.currentConversation,
|
|
14
|
-
timestamp: Date.now()
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
if (this.conversationCache.size > this.MAX_CACHE_SIZE) {
|
|
18
|
-
const oldest = this.conversationCache.keys().next().value;
|
|
19
|
-
this.conversationCache.delete(oldest);
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
invalidateCache(conversationId) {
|
|
25
|
-
this.conversationCache.delete(conversationId);
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
async loadConversationMessages(conversationId) {
|
|
30
|
-
performance.mark(`conv-load-start:${conversationId}`);
|
|
31
|
-
try {
|
|
32
|
-
if (this._previousConvAbort) {
|
|
33
|
-
this._previousConvAbort.abort();
|
|
34
|
-
}
|
|
35
|
-
this._previousConvAbort = new AbortController();
|
|
36
|
-
const convSignal = this._previousConvAbort.signal;
|
|
37
|
-
|
|
38
|
-
const prevConversationId = this.state.currentConversation?.id;
|
|
39
|
-
const availableFallback = this.state.conversations?.find(c => c.id !== conversationId) || null;
|
|
40
|
-
this.cacheCurrentConversation();
|
|
41
|
-
|
|
42
|
-
this.removeScrollUpDetection();
|
|
43
|
-
if (this.renderer.resetScrollState) this.renderer.resetScrollState();
|
|
44
|
-
this._userScrolledUp = false;
|
|
45
|
-
this._removeNewContentPill();
|
|
46
|
-
|
|
47
|
-
if (this.ui.messageInput) {
|
|
48
|
-
this.ui.messageInput.value = '';
|
|
49
|
-
this.ui.messageInput.style.height = 'auto';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
|
|
53
|
-
if (this.ui.injectButton) this.ui.injectButton.classList.remove('visible');
|
|
54
|
-
if (this.ui.queueButton) this.ui.queueButton.classList.remove('visible');
|
|
55
|
-
if (this.ui.sendButton) this.ui.sendButton.style.display = '';
|
|
56
|
-
|
|
57
|
-
var prevId = this.state.currentConversation?.id;
|
|
58
|
-
if (prevId && prevId !== conversationId) {
|
|
59
|
-
if (this.wsManager.isConnected && !this._convIsStreaming(prevId)) {
|
|
60
|
-
this.wsManager.sendMessage({ type: 'unsubscribe', conversationId: prevId });
|
|
61
|
-
}
|
|
62
|
-
this.state.currentSession = null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const cachedConv = this.state.conversations.find(c => c.id === conversationId);
|
|
66
|
-
if (cachedConv && this.state.currentConversation?.id !== conversationId) {
|
|
67
|
-
window.ConversationState?.selectConversation(conversationId, 'cache_load', 1);
|
|
68
|
-
this.state.currentConversation = cachedConv;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
this.updateUrlForConversation(conversationId);
|
|
72
|
-
if (this.wsManager.isConnected) {
|
|
73
|
-
this.wsManager.sendMessage({ type: 'subscribe', conversationId });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const cached = this.conversationCache.get(conversationId);
|
|
77
|
-
if (cached) { this.conversationCache.delete(conversationId); this.conversationCache.set(conversationId, cached); }
|
|
78
|
-
if (cached && (Date.now() - cached.timestamp) < 600000) {
|
|
79
|
-
const outputEl = document.getElementById('output');
|
|
80
|
-
if (outputEl) {
|
|
81
|
-
const children = [];
|
|
82
|
-
while (cached.dom.firstChild) children.push(cached.dom.firstChild);
|
|
83
|
-
outputEl.replaceChildren(...children);
|
|
84
|
-
window.ConversationState?.selectConversation(conversationId, 'dom_cache_load', 1);
|
|
85
|
-
this.state.currentConversation = cached.conversation;
|
|
86
|
-
window.dispatchEvent(new CustomEvent('conversation-changed', { detail: { conversationId, conversation: cached.conversation } }));
|
|
87
|
-
const cachedHasActivity = cached.conversation.messageCount > 0 || this._convIsStreaming(conversationId);
|
|
88
|
-
this.applyAgentAndModelSelection(cached.conversation, cachedHasActivity);
|
|
89
|
-
this.conversationCache.delete(conversationId);
|
|
90
|
-
if (this._lazyObserver) { this._lazyObserver.disconnect(); this._lazyObserver = null; }
|
|
91
|
-
this._flushBgCache && this._flushBgCache(conversationId);
|
|
92
|
-
this.setupScrollUpDetection && this.setupScrollUpDetection();
|
|
93
|
-
this.syncPromptState(conversationId);
|
|
94
|
-
this.restoreScrollPosition(conversationId);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
this.conversationCache.delete(conversationId);
|
|
100
|
-
|
|
101
|
-
if (this._lazyObserver) { this._lazyObserver.disconnect(); this._lazyObserver = null; }
|
|
102
|
-
this._showSkeletonLoading(conversationId);
|
|
103
|
-
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
104
|
-
|
|
105
|
-
let fullData;
|
|
106
|
-
try {
|
|
107
|
-
fullData = await window.wsClient.rpc('conv.full', { id: conversationId });
|
|
108
|
-
performance.mark(`conv-data-received:${conversationId}`);
|
|
109
|
-
if (convSignal.aborted) return;
|
|
110
|
-
} catch (wsErr) {
|
|
111
|
-
if (wsErr.code === 404) {
|
|
112
|
-
console.warn('Conversation no longer exists:', conversationId);
|
|
113
|
-
window.ConversationState?.clear('conversation_not_found');
|
|
114
|
-
this.state.currentConversation = null;
|
|
115
|
-
if (window.conversationManager) window.conversationManager.loadConversations();
|
|
116
|
-
const fallbackConv = prevConversationId ? prevConversationId : availableFallback?.id;
|
|
117
|
-
if (fallbackConv && fallbackConv !== conversationId) {
|
|
118
|
-
this._dbg('Resuming from fallback conversation:', fallbackConv);
|
|
119
|
-
this.showError('Conversation not found. Resuming previous conversation.');
|
|
120
|
-
await this.loadConversationMessages(fallbackConv);
|
|
121
|
-
} else {
|
|
122
|
-
const outputEl = document.getElementById('output');
|
|
123
|
-
if (outputEl) outputEl.innerHTML = '<p class="text-secondary" style="padding:2rem;text-align:center">Conversation not found. It may have been lost during a server restart.</p>';
|
|
124
|
-
this.enableControls();
|
|
125
|
-
}
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
const base = window.__BASE_URL || '';
|
|
130
|
-
const [convRes, chunksRes, msgsRes] = await Promise.all([
|
|
131
|
-
fetch(`${base}/api/conversations/${conversationId}`),
|
|
132
|
-
fetch(`${base}/api/conversations/${conversationId}/chunks`),
|
|
133
|
-
fetch(`${base}/api/conversations/${conversationId}/messages?limit=500`)
|
|
134
|
-
]);
|
|
135
|
-
const convData = await convRes.json();
|
|
136
|
-
const chunksData = await chunksRes.json();
|
|
137
|
-
const msgsData = await msgsRes.json();
|
|
138
|
-
fullData = {
|
|
139
|
-
conversation: convData.conversation,
|
|
140
|
-
isActivelyStreaming: false,
|
|
141
|
-
latestSession: null,
|
|
142
|
-
chunks: chunksData.chunks || [],
|
|
143
|
-
totalChunks: chunksData.totalChunks || (chunksData.chunks || []).length,
|
|
144
|
-
messages: msgsData.messages || []
|
|
145
|
-
};
|
|
146
|
-
if (convSignal.aborted) return;
|
|
147
|
-
} catch (restErr) {
|
|
148
|
-
throw wsErr;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (convSignal.aborted) return;
|
|
152
|
-
await this._loadConvRender(conversationId, convSignal, prevConversationId, availableFallback, fullData);
|
|
153
|
-
} catch (error) {
|
|
154
|
-
if (error.name === 'AbortError') return;
|
|
155
|
-
console.error('Failed to load conversation messages:', error);
|
|
156
|
-
const fallbackConv = prevConversationId ? prevConversationId : availableFallback?.id;
|
|
157
|
-
if (fallbackConv && fallbackConv !== conversationId) {
|
|
158
|
-
this._dbg('Resuming from fallback conversation due to error:', fallbackConv);
|
|
159
|
-
this.showError('Failed to load conversation. Resuming previous conversation.');
|
|
160
|
-
try {
|
|
161
|
-
await this.loadConversationMessages(fallbackConv);
|
|
162
|
-
} catch (fallbackError) {
|
|
163
|
-
console.error('Failed to resume fallback conversation:', fallbackError);
|
|
164
|
-
this.showError('Failed to load conversation: ' + error.message);
|
|
165
|
-
}
|
|
166
|
-
} else {
|
|
167
|
-
this.showError('Failed to load conversation: ' + error.message);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
});
|
package/static/js/client-conv.js
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
Object.assign(AgentGUIClient.prototype, {
|
|
2
|
-
_getLazyObserver() {
|
|
3
|
-
if (this._lazyObserver) return this._lazyObserver;
|
|
4
|
-
if (typeof IntersectionObserver === 'undefined') return null;
|
|
5
|
-
this._lazyObserver = new IntersectionObserver((entries) => {
|
|
6
|
-
for (const entry of entries) {
|
|
7
|
-
if (!entry.isIntersecting) continue;
|
|
8
|
-
const msgDiv = entry.target;
|
|
9
|
-
const pendingChunks = msgDiv._lazyChunks;
|
|
10
|
-
if (!pendingChunks) continue;
|
|
11
|
-
delete msgDiv._lazyChunks;
|
|
12
|
-
this._lazyObserver.unobserve(msgDiv);
|
|
13
|
-
const blocksEl = msgDiv.querySelector('.message-blocks');
|
|
14
|
-
if (blocksEl) this._hydrateSessionBlocks(blocksEl, pendingChunks);
|
|
15
|
-
}
|
|
16
|
-
}, { rootMargin: '400px 0px' });
|
|
17
|
-
return this._lazyObserver;
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
_renderConversationContent(messagesContainer, chunks, userMessages, activeSessionId) {
|
|
22
|
-
if (!chunks || chunks.length === 0) return;
|
|
23
|
-
const sessionMap = new Map();
|
|
24
|
-
for (const chunk of chunks) {
|
|
25
|
-
if (!sessionMap.has(chunk.sessionId)) sessionMap.set(chunk.sessionId, []);
|
|
26
|
-
sessionMap.get(chunk.sessionId).push(chunk);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const sessionIds = [...sessionMap.keys()];
|
|
30
|
-
const EAGER_TAIL = 8;
|
|
31
|
-
const eagerSet = new Set(sessionIds.slice(-EAGER_TAIL));
|
|
32
|
-
if (activeSessionId) eagerSet.add(activeSessionId);
|
|
33
|
-
const observer = sessionIds.length > EAGER_TAIL ? this._getLazyObserver() : null;
|
|
34
|
-
|
|
35
|
-
const frag = document.createDocumentFragment();
|
|
36
|
-
let ui = 0;
|
|
37
|
-
for (const [sid, list] of sessionMap) {
|
|
38
|
-
const sessionStart = list[0].created_at;
|
|
39
|
-
while (ui < userMessages.length && userMessages[ui].created_at <= sessionStart) {
|
|
40
|
-
const m = userMessages[ui++];
|
|
41
|
-
const uDiv = document.createElement('div');
|
|
42
|
-
uDiv.className = 'message message-user';
|
|
43
|
-
uDiv.setAttribute('data-msg-id', m.id);
|
|
44
|
-
uDiv.innerHTML = `<div class="message-role">User<button class="msg-edit-btn" data-edit-msg="${this.escapeHtml(m.id)}" title="Edit and re-run">✎</button></div>${this.renderMessageContent(m.content)}<div class="message-timestamp">${new Date(m.created_at).toLocaleString()}</div>`;
|
|
45
|
-
frag.appendChild(uDiv);
|
|
46
|
-
}
|
|
47
|
-
const isActive = sid === activeSessionId;
|
|
48
|
-
const msgDiv = document.createElement('div');
|
|
49
|
-
msgDiv.className = `message message-assistant${isActive ? ' streaming-message' : ''}`;
|
|
50
|
-
msgDiv.id = isActive ? `streaming-${sid}` : `message-${sid}`;
|
|
51
|
-
msgDiv.setAttribute('data-session-id', sid);
|
|
52
|
-
msgDiv.innerHTML = '<div class="message-role">Assistant</div><div class="message-blocks streaming-blocks"></div>';
|
|
53
|
-
const blocksEl = msgDiv.querySelector('.message-blocks');
|
|
54
|
-
|
|
55
|
-
if (observer && !eagerSet.has(sid)) {
|
|
56
|
-
msgDiv._lazyChunks = list;
|
|
57
|
-
observer.observe(msgDiv);
|
|
58
|
-
} else {
|
|
59
|
-
this._hydrateSessionBlocks(blocksEl, list);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (isActive) {
|
|
63
|
-
const ind = document.createElement('div');
|
|
64
|
-
ind.className = 'streaming-indicator';
|
|
65
|
-
ind.style = 'display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0;color:var(--color-text-secondary);font-size:0.875rem;';
|
|
66
|
-
ind.innerHTML = '<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><span class="streaming-indicator-label">Processing...</span>';
|
|
67
|
-
msgDiv.appendChild(ind);
|
|
68
|
-
} else {
|
|
69
|
-
const ts = document.createElement('div');
|
|
70
|
-
ts.className = 'message-timestamp';
|
|
71
|
-
ts.textContent = new Date(list[list.length - 1].created_at).toLocaleString();
|
|
72
|
-
msgDiv.appendChild(ts);
|
|
73
|
-
}
|
|
74
|
-
frag.appendChild(msgDiv);
|
|
75
|
-
}
|
|
76
|
-
while (ui < userMessages.length) {
|
|
77
|
-
const m = userMessages[ui++];
|
|
78
|
-
const uDiv = document.createElement('div');
|
|
79
|
-
uDiv.className = 'message message-user';
|
|
80
|
-
uDiv.setAttribute('data-msg-id', m.id);
|
|
81
|
-
uDiv.innerHTML = `<div class="message-role">User<button class="msg-edit-btn" data-edit-msg="${this.escapeHtml(m.id)}" title="Edit and re-run">✎</button></div>${this.renderMessageContent(m.content)}<div class="message-timestamp">${new Date(m.created_at).toLocaleString()}</div>`;
|
|
82
|
-
frag.appendChild(uDiv);
|
|
83
|
-
}
|
|
84
|
-
messagesContainer.appendChild(frag);
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
renderChunk(chunk) {
|
|
89
|
-
try { return this._renderChunkInner(chunk); } catch (e) { console.error('[render-error] chunk:', e); }
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
_renderChunkInner(chunk) {
|
|
93
|
-
if (!chunk || !chunk.block) return;
|
|
94
|
-
const seq = chunk.sequence;
|
|
95
|
-
if (seq !== undefined) {
|
|
96
|
-
const seen = (this._renderedSeqs = this._renderedSeqs || {})[chunk.sessionId] || (this._renderedSeqs[chunk.sessionId] = new Set());
|
|
97
|
-
if (seen.has(seq)) return;
|
|
98
|
-
seen.add(seq);
|
|
99
|
-
}
|
|
100
|
-
const streamingEl = document.getElementById(`streaming-${chunk.sessionId}`);
|
|
101
|
-
if (!streamingEl) return;
|
|
102
|
-
const blocksEl = streamingEl.querySelector('.streaming-blocks');
|
|
103
|
-
if (!blocksEl) return;
|
|
104
|
-
if (chunk.block.type === 'tool_result') {
|
|
105
|
-
const matchById = chunk.block.tool_use_id && blocksEl.querySelector(`.block-tool-use[data-tool-use-id="${chunk.block.tool_use_id}"]`);
|
|
106
|
-
const lastEl = blocksEl.lastElementChild;
|
|
107
|
-
const toolUseEl = matchById || (lastEl?.classList?.contains('block-tool-use') ? lastEl : null);
|
|
108
|
-
if (toolUseEl) {
|
|
109
|
-
this.renderer.mergeResultIntoToolUse(toolUseEl, chunk.block);
|
|
110
|
-
this.scrollToBottom();
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (chunk.block.type === 'tool_status' || chunk.block.type === 'hook_progress') return;
|
|
115
|
-
const element = this.renderer.renderBlock(chunk.block, chunk, blocksEl);
|
|
116
|
-
if (!element) { this.scrollToBottom(); return; }
|
|
117
|
-
blocksEl.appendChild(element);
|
|
118
|
-
this.scrollToBottom();
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
async loadAgents() {
|
|
123
|
-
return this._dedupedFetch('loadAgents', async () => {
|
|
124
|
-
try {
|
|
125
|
-
const { agents } = await window.wsClient.rpc('agent.ls');
|
|
126
|
-
this.state.agents = agents;
|
|
127
|
-
|
|
128
|
-
const displayAgents = agents;
|
|
129
|
-
|
|
130
|
-
if (this.ui.cliSelector) {
|
|
131
|
-
if (displayAgents.length > 0) {
|
|
132
|
-
this.ui.cliSelector.innerHTML = displayAgents
|
|
133
|
-
.map(a => `<option value="${a.id}">${a.name.split(/[\s\-]+/)[0]}</option>`)
|
|
134
|
-
.join('');
|
|
135
|
-
this.ui.cliSelector.style.display = 'inline-block';
|
|
136
|
-
} else {
|
|
137
|
-
this.ui.cliSelector.innerHTML = '';
|
|
138
|
-
this.ui.cliSelector.style.display = 'none';
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
window.dispatchEvent(new CustomEvent('agents-loaded', { detail: { agents } }));
|
|
143
|
-
|
|
144
|
-
if (displayAgents.length > 0 && !this._agentLocked) {
|
|
145
|
-
const firstId = displayAgents[0].id;
|
|
146
|
-
await this.loadSubAgentsForCli(firstId);
|
|
147
|
-
this.loadModelsForAgent(this.getEffectiveAgentId());
|
|
148
|
-
}
|
|
149
|
-
return agents;
|
|
150
|
-
} catch (error) {
|
|
151
|
-
console.error('Failed to load agents:', error);
|
|
152
|
-
return [];
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
async loadSubAgentsForCli(cliAgentId) {
|
|
159
|
-
if (this.ui.agentSelector) {
|
|
160
|
-
this.ui.agentSelector.innerHTML = '';
|
|
161
|
-
this.ui.agentSelector.style.display = 'none';
|
|
162
|
-
}
|
|
163
|
-
try {
|
|
164
|
-
const { subAgents } = await window.wsClient.rpc('agent.subagents', { id: cliAgentId });
|
|
165
|
-
if (subAgents && subAgents.length > 0 && this.ui.agentSelector) {
|
|
166
|
-
this.ui.agentSelector.innerHTML = subAgents
|
|
167
|
-
.map(a => `<option value="${a.id}">${a.name.split(/[\s\-]+/)[0]}</option>`)
|
|
168
|
-
.join('');
|
|
169
|
-
this.ui.agentSelector.style.display = 'inline-block';
|
|
170
|
-
this._dbg(`[Agent Selector] Loaded ${subAgents.length} sub-agents for ${cliAgentId}`);
|
|
171
|
-
const firstSubAgentId = subAgents[0].id;
|
|
172
|
-
this.ui.agentSelector.value = firstSubAgentId;
|
|
173
|
-
this.loadModelsForAgent(cliAgentId);
|
|
174
|
-
} else {
|
|
175
|
-
this._dbg(`[Agent Selector] No sub-agents found for ${cliAgentId}`);
|
|
176
|
-
const cliToAcpMap = {
|
|
177
|
-
'cli-opencode': 'opencode',
|
|
178
|
-
'cli-gemini': 'gemini',
|
|
179
|
-
'cli-kilo': 'kilo',
|
|
180
|
-
'cli-codex': 'codex'
|
|
181
|
-
};
|
|
182
|
-
const acpAgentId = cliToAcpMap[cliAgentId] || cliAgentId;
|
|
183
|
-
this.loadModelsForAgent(acpAgentId);
|
|
184
|
-
}
|
|
185
|
-
} catch (err) {
|
|
186
|
-
console.warn(`[Agent Selector] Failed to load sub-agents for ${cliAgentId}:`, err.message);
|
|
187
|
-
const cliToAcpMap = {
|
|
188
|
-
'cli-opencode': 'opencode',
|
|
189
|
-
'cli-gemini': 'gemini',
|
|
190
|
-
'cli-kilo': 'kilo',
|
|
191
|
-
'cli-codex': 'codex'
|
|
192
|
-
};
|
|
193
|
-
const acpAgentId = cliToAcpMap[cliAgentId] || cliAgentId;
|
|
194
|
-
this.loadModelsForAgent(acpAgentId);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
});
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
Object.assign(AgentGUIClient.prototype, {
|
|
2
|
-
async fetchAndRenderQueue(conversationId) {
|
|
3
|
-
const outputEl = document.querySelector('.conversation-messages');
|
|
4
|
-
if (!outputEl) return;
|
|
5
|
-
|
|
6
|
-
try {
|
|
7
|
-
const { queue } = await window.wsClient.rpc('q.ls', { id: conversationId });
|
|
8
|
-
|
|
9
|
-
let queueEl = outputEl.querySelector('.queue-indicator');
|
|
10
|
-
if (!queue || queue.length === 0) {
|
|
11
|
-
if (queueEl) queueEl.remove();
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (!queueEl) {
|
|
16
|
-
queueEl = document.createElement('div');
|
|
17
|
-
queueEl.className = 'queue-indicator';
|
|
18
|
-
outputEl.appendChild(queueEl);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
queueEl.innerHTML = queue.map((q, i) => `
|
|
22
|
-
<div class="queue-item" data-message-id="${q.messageId}" style="padding:0.5rem 1rem;margin:0.5rem 0;border-radius:0.375rem;background:var(--color-warning);color:#000;font-size:0.875rem;display:flex;align-items:center;gap:0.5rem;">
|
|
23
|
-
<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${i + 1}. ${this.escapeHtml(q.content)}</span>
|
|
24
|
-
<button class="queue-edit-btn" data-index="${i}" style="padding:0.25rem 0.5rem;background:transparent;border:1px solid #000;border-radius:0.25rem;cursor:pointer;font-size:0.75rem;">Edit</button>
|
|
25
|
-
<button class="queue-delete-btn" data-index="${i}" style="padding:0.25rem 0.5rem;background:transparent;border:1px solid #000;border-radius:0.25rem;cursor:pointer;font-size:0.75rem;">Delete</button>
|
|
26
|
-
</div>
|
|
27
|
-
`).join('');
|
|
28
|
-
|
|
29
|
-
if (!queueEl._listenersAttached) {
|
|
30
|
-
queueEl._listenersAttached = true;
|
|
31
|
-
queueEl.addEventListener('click', async (e) => {
|
|
32
|
-
if (e.target.classList.contains('queue-delete-btn')) {
|
|
33
|
-
const index = parseInt(e.target.dataset.index);
|
|
34
|
-
const msgId = queue[index].messageId;
|
|
35
|
-
if (await window.UIDialog.confirm('Delete this queued message?', 'Delete Message')) {
|
|
36
|
-
await window.wsClient.rpc('q.del', { id: conversationId, messageId: msgId });
|
|
37
|
-
}
|
|
38
|
-
} else if (e.target.classList.contains('queue-edit-btn')) {
|
|
39
|
-
const index = parseInt(e.target.dataset.index);
|
|
40
|
-
const q = queue[index];
|
|
41
|
-
const newContent = await window.UIDialog.prompt('Edit message:', q.content, 'Edit Queued Message');
|
|
42
|
-
if (newContent !== null && newContent !== q.content) {
|
|
43
|
-
window.wsClient.rpc('q.upd', { id: conversationId, messageId: q.messageId, content: newContent });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
} catch (err) {
|
|
49
|
-
console.error('Failed to fetch queue:', err);
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
handleRateLimitHit(data) {
|
|
55
|
-
if (data.conversationId !== this.state.currentConversation?.id) return;
|
|
56
|
-
this._setConvStreaming(data.conversationId, false);
|
|
57
|
-
|
|
58
|
-
this.enableControls();
|
|
59
|
-
|
|
60
|
-
const cooldownMs = data.retryAfterMs || 60000;
|
|
61
|
-
this._rateLimitSafetyTimer = setTimeout(() => {
|
|
62
|
-
this.enableControls();
|
|
63
|
-
}, cooldownMs + 10000);
|
|
64
|
-
|
|
65
|
-
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
66
|
-
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
67
|
-
if (streamingEl) {
|
|
68
|
-
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
69
|
-
if (indicator) {
|
|
70
|
-
const retrySeconds = Math.ceil(cooldownMs / 1000);
|
|
71
|
-
indicator.innerHTML = `<span style="color:var(--color-warning);">Rate limited. Retrying in ${retrySeconds}s...</span>`;
|
|
72
|
-
let remaining = retrySeconds;
|
|
73
|
-
const countdownTimer = setInterval(() => {
|
|
74
|
-
remaining--;
|
|
75
|
-
if (remaining <= 0) {
|
|
76
|
-
clearInterval(countdownTimer);
|
|
77
|
-
indicator.innerHTML = '<span style="color:var(--color-info);">Restarting...</span>';
|
|
78
|
-
} else {
|
|
79
|
-
indicator.innerHTML = `<span style="color:var(--color-warning);">Rate limited. Retrying in ${remaining}s...</span>`;
|
|
80
|
-
}
|
|
81
|
-
}, 1000);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
handleRateLimitClear(data) {
|
|
88
|
-
if (data.conversationId !== this.state.currentConversation?.id) return;
|
|
89
|
-
if (this._rateLimitSafetyTimer) {
|
|
90
|
-
clearTimeout(this._rateLimitSafetyTimer);
|
|
91
|
-
this._rateLimitSafetyTimer = null;
|
|
92
|
-
}
|
|
93
|
-
this.enableControls();
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
handleAllConversationsDeleted(data) {
|
|
98
|
-
window.ConversationState?.clear('all_deleted');
|
|
99
|
-
this.state.currentConversation = null;
|
|
100
|
-
this.state.conversations = [];
|
|
101
|
-
this.state.sessionEvents = [];
|
|
102
|
-
this.conversationCache.clear();
|
|
103
|
-
this.conversationListCache = { data: [], timestamp: 0, ttl: 30000 };
|
|
104
|
-
this.draftPrompts.clear();
|
|
105
|
-
window.dispatchEvent(new CustomEvent('conversation-deselected'));
|
|
106
|
-
const outputEl = document.getElementById('output');
|
|
107
|
-
if (outputEl) outputEl.innerHTML = '';
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
isHtmlContent(text) {
|
|
112
|
-
const htmlPattern = /<(?:div|table|section|article|ul|ol|dl|nav|header|footer|main|aside|figure|details|summary|h[1-6]|p|blockquote|pre|code|span|strong|em|a|img|br|hr|li|td|tr|th|thead|tbody|tfoot)\b[^>]*>/i;
|
|
113
|
-
return htmlPattern.test(text);
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
sanitizeHtml(html) {
|
|
118
|
-
const dangerous = /<\s*\/?\s*(script|iframe|object|embed|applet|form|input|button|select|textarea)\b[^>]*>/gi;
|
|
119
|
-
let cleaned = html.replace(dangerous, '');
|
|
120
|
-
cleaned = cleaned.replace(/\s+on\w+\s*=\s*["'][^"']*["']/gi, '');
|
|
121
|
-
cleaned = cleaned.replace(/\s+on\w+\s*=\s*[^\s>]+/gi, '');
|
|
122
|
-
cleaned = cleaned.replace(/javascript\s*:/gi, '');
|
|
123
|
-
return cleaned;
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
parseMarkdownCodeBlocks(text) {
|
|
128
|
-
const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
|
|
129
|
-
const parts = [];
|
|
130
|
-
let lastIndex = 0;
|
|
131
|
-
let match;
|
|
132
|
-
|
|
133
|
-
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
134
|
-
if (match.index > lastIndex) {
|
|
135
|
-
const segment = text.substring(lastIndex, match.index);
|
|
136
|
-
parts.push({
|
|
137
|
-
type: this.isHtmlContent(segment) ? 'html' : 'text',
|
|
138
|
-
content: segment
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
parts.push({
|
|
142
|
-
type: 'code',
|
|
143
|
-
language: match[1] || 'plain',
|
|
144
|
-
code: match[2]
|
|
145
|
-
});
|
|
146
|
-
lastIndex = codeBlockRegex.lastIndex;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (lastIndex < text.length) {
|
|
150
|
-
const segment = text.substring(lastIndex);
|
|
151
|
-
parts.push({
|
|
152
|
-
type: this.isHtmlContent(segment) ? 'html' : 'text',
|
|
153
|
-
content: segment
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (parts.length === 0) {
|
|
158
|
-
return [{ type: this.isHtmlContent(text) ? 'html' : 'text', content: text }];
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return parts;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
});
|