agentgui 1.0.797 → 1.0.799
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/package.json +1 -1
- package/static/index.html +2 -0
- package/static/js/client.js +12 -20
- package/static/js/recording-machine.js +49 -0
- package/static/js/stt-handler.js +12 -3
- package/static/js/terminal-machine.js +51 -0
- package/static/js/terminal.js +5 -0
package/package.json
CHANGED
package/static/index.html
CHANGED
|
@@ -290,6 +290,8 @@
|
|
|
290
290
|
<script defer src="/gm/js/voice-machine.js"></script>
|
|
291
291
|
<script defer src="/gm/js/conv-list-machine.js"></script>
|
|
292
292
|
<script defer src="/gm/js/prompt-machine.js"></script>
|
|
293
|
+
<script defer src="/gm/js/recording-machine.js"></script>
|
|
294
|
+
<script defer src="/gm/js/terminal-machine.js"></script>
|
|
293
295
|
<script defer src="/gm/js/conversations.js"></script>
|
|
294
296
|
<script defer src="/gm/lib/msgpackr.min.js"></script>
|
|
295
297
|
<script defer src="/gm/js/websocket-manager.js"></script>
|
package/static/js/client.js
CHANGED
|
@@ -2173,16 +2173,17 @@ class AgentGUIClient {
|
|
|
2173
2173
|
blockFrag.appendChild(el);
|
|
2174
2174
|
}
|
|
2175
2175
|
blocksEl.appendChild(blockFrag);
|
|
2176
|
+
// Build tool-use element index once for O(1) lookups instead of O(n) querySelectorAll per chunk
|
|
2177
|
+
const toolUseIndex = new Map();
|
|
2178
|
+
blocksEl.querySelectorAll('.block-tool-use[data-tool-use-id]').forEach(el => toolUseIndex.set(el.dataset.toolUseId, el));
|
|
2176
2179
|
for (const chunk of deferred) {
|
|
2177
2180
|
const b = chunk.block;
|
|
2178
2181
|
if (b.type === 'tool_result') {
|
|
2179
|
-
const
|
|
2180
|
-
const toolUseEl = (tid ? blocksEl.querySelector(`.block-tool-use[data-tool-use-id="${tid}"]`) : null)
|
|
2182
|
+
const toolUseEl = (b.tool_use_id && toolUseIndex.get(b.tool_use_id))
|
|
2181
2183
|
|| (blocksEl.lastElementChild?.classList.contains('block-tool-use') ? blocksEl.lastElementChild : null);
|
|
2182
2184
|
if (toolUseEl) this.renderer.mergeResultIntoToolUse(toolUseEl, b);
|
|
2183
2185
|
} else if (b.type === 'tool_status') {
|
|
2184
|
-
const
|
|
2185
|
-
const toolUseEl = tid && blocksEl.querySelector(`.block-tool-use[data-tool-use-id="${tid}"]`);
|
|
2186
|
+
const toolUseEl = b.tool_use_id && toolUseIndex.get(b.tool_use_id);
|
|
2186
2187
|
if (toolUseEl) {
|
|
2187
2188
|
const isError = b.status === 'failed';
|
|
2188
2189
|
const isDone = b.status === 'completed';
|
|
@@ -2845,10 +2846,9 @@ class AgentGUIClient {
|
|
|
2845
2846
|
if (cached && (Date.now() - cached.timestamp) < 300000) {
|
|
2846
2847
|
const outputEl = document.getElementById('output');
|
|
2847
2848
|
if (outputEl) {
|
|
2848
|
-
|
|
2849
|
-
while (cached.dom.firstChild)
|
|
2850
|
-
|
|
2851
|
-
}
|
|
2849
|
+
const children = [];
|
|
2850
|
+
while (cached.dom.firstChild) children.push(cached.dom.firstChild);
|
|
2851
|
+
outputEl.replaceChildren(...children);
|
|
2852
2852
|
window.ConversationState?.selectConversation(conversationId, 'dom_cache_load', 1);
|
|
2853
2853
|
this.state.currentConversation = cached.conversation;
|
|
2854
2854
|
window.dispatchEvent(new CustomEvent('conversation-changed', { detail: { conversationId, conversation: cached.conversation } }));
|
|
@@ -2921,23 +2921,15 @@ class AgentGUIClient {
|
|
|
2921
2921
|
const hasActivity = (allMessages && allMessages.length > 0) || isActivelyStreaming || latestSession || this._convIsStreaming(conversationId);
|
|
2922
2922
|
this.applyAgentAndModelSelection(conversation, hasActivity);
|
|
2923
2923
|
|
|
2924
|
+
// Parse chunk data and fetch queue in parallel
|
|
2925
|
+
const queuePromise = window.wsClient.rpc('q.ls', { id: conversationId }).catch(() => ({ queue: [] }));
|
|
2924
2926
|
const chunks = (rawChunks || []).map(chunk => ({
|
|
2925
2927
|
...chunk,
|
|
2926
2928
|
block: typeof chunk.data === 'string' ? JSON.parse(chunk.data) : chunk.data
|
|
2927
2929
|
}));
|
|
2928
2930
|
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
try {
|
|
2932
|
-
const { queue } = await window.wsClient.rpc('q.ls', { id: conversationId });
|
|
2933
|
-
if (queue && queue.length > 0) {
|
|
2934
|
-
queuedMessageIds = new Set(queue.map(q => q.messageId));
|
|
2935
|
-
}
|
|
2936
|
-
} catch (e) {
|
|
2937
|
-
console.warn('Failed to fetch queue:', e.message);
|
|
2938
|
-
}
|
|
2939
|
-
|
|
2940
|
-
// Filter out queued messages from user messages - they'll be rendered in queue indicator instead
|
|
2931
|
+
const { queue: queueResult } = await queuePromise;
|
|
2932
|
+
const queuedMessageIds = new Set((queueResult || []).map(q => q.messageId));
|
|
2941
2933
|
const userMessages = (allMessages || []).filter(m => m.role === 'user' && !queuedMessageIds.has(m.id));
|
|
2942
2934
|
const hasMoreChunks = totalChunks && chunks.length < totalChunks;
|
|
2943
2935
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
const { createMachine, createActor, assign } = XState;
|
|
3
|
+
|
|
4
|
+
const recordingMachine = createMachine({
|
|
5
|
+
id: 'recording',
|
|
6
|
+
initial: 'idle',
|
|
7
|
+
context: {
|
|
8
|
+
error: null,
|
|
9
|
+
},
|
|
10
|
+
states: {
|
|
11
|
+
idle: {
|
|
12
|
+
entry: assign({ error: null }),
|
|
13
|
+
on: {
|
|
14
|
+
START: { target: 'recording' },
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
recording: {
|
|
18
|
+
on: {
|
|
19
|
+
STOP: { target: 'processing' },
|
|
20
|
+
ERROR: {
|
|
21
|
+
target: 'idle',
|
|
22
|
+
actions: assign(({ event }) => ({ error: event.error || 'Recording failed' })),
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
processing: {
|
|
27
|
+
on: {
|
|
28
|
+
COMPLETE: { target: 'idle' },
|
|
29
|
+
ERROR: {
|
|
30
|
+
target: 'idle',
|
|
31
|
+
actions: assign(({ event }) => ({ error: event.error || 'Processing failed' })),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const actor = createActor(recordingMachine);
|
|
39
|
+
actor.start();
|
|
40
|
+
|
|
41
|
+
window.recordingMachineAPI = {
|
|
42
|
+
send: function(event) { actor.send(event); },
|
|
43
|
+
getState: function() { return actor.getSnapshot().value; },
|
|
44
|
+
isRecording: function() { return actor.getSnapshot().value === 'recording'; },
|
|
45
|
+
isProcessing: function() { return actor.getSnapshot().value === 'processing'; },
|
|
46
|
+
subscribe: function(fn) { return actor.subscribe(fn); },
|
|
47
|
+
};
|
|
48
|
+
window.__recordingMachine = actor;
|
|
49
|
+
})();
|
package/static/js/stt-handler.js
CHANGED
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
var recordedChunks = [];
|
|
8
8
|
var TARGET_SAMPLE_RATE = 16000;
|
|
9
9
|
|
|
10
|
+
function rmApi() { return window.recordingMachineAPI; }
|
|
11
|
+
|
|
10
12
|
window.STTHandler = {
|
|
11
|
-
isRecording: function() { return isRecording; },
|
|
13
|
+
isRecording: function() { return rmApi()?.isRecording() || isRecording; },
|
|
12
14
|
|
|
13
15
|
startRecording: async function() {
|
|
14
|
-
if (isRecording) return;
|
|
16
|
+
if (isRecording || rmApi()?.isRecording()) return;
|
|
15
17
|
try {
|
|
16
18
|
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
17
19
|
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
@@ -24,16 +26,19 @@
|
|
|
24
26
|
};
|
|
25
27
|
source.connect(workletNode);
|
|
26
28
|
isRecording = true;
|
|
29
|
+
if (rmApi()) rmApi().send({ type: 'START' });
|
|
27
30
|
return { success: true };
|
|
28
31
|
} catch (e) {
|
|
29
32
|
isRecording = false;
|
|
33
|
+
if (rmApi()) rmApi().send({ type: 'ERROR', error: e.message });
|
|
30
34
|
return { success: false, error: e.message };
|
|
31
35
|
}
|
|
32
36
|
},
|
|
33
37
|
|
|
34
38
|
stopRecording: async function() {
|
|
35
|
-
if (!isRecording) return { success: false, error: 'Not recording' };
|
|
39
|
+
if (!isRecording && !rmApi()?.isRecording()) return { success: false, error: 'Not recording' };
|
|
36
40
|
isRecording = false;
|
|
41
|
+
if (rmApi()) rmApi().send({ type: 'STOP' });
|
|
37
42
|
|
|
38
43
|
if (workletNode) {
|
|
39
44
|
workletNode.port.postMessage('stop');
|
|
@@ -77,13 +82,17 @@
|
|
|
77
82
|
});
|
|
78
83
|
var data = await resp.json();
|
|
79
84
|
if (data.text) {
|
|
85
|
+
if (rmApi()) rmApi().send({ type: 'COMPLETE' });
|
|
80
86
|
return { success: true, text: data.text };
|
|
81
87
|
} else if (data.error) {
|
|
88
|
+
if (rmApi()) rmApi().send({ type: 'ERROR', error: data.error });
|
|
82
89
|
return { success: false, error: data.error };
|
|
83
90
|
} else {
|
|
91
|
+
if (rmApi()) rmApi().send({ type: 'ERROR', error: 'No transcription returned' });
|
|
84
92
|
return { success: false, error: 'No transcription returned' };
|
|
85
93
|
}
|
|
86
94
|
} catch (e) {
|
|
95
|
+
if (rmApi()) rmApi().send({ type: 'ERROR', error: e.message });
|
|
87
96
|
return { success: false, error: 'Transcription failed: ' + e.message };
|
|
88
97
|
}
|
|
89
98
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
const { createMachine, createActor, assign } = XState;
|
|
3
|
+
|
|
4
|
+
const terminalMachine = createMachine({
|
|
5
|
+
id: 'terminal',
|
|
6
|
+
initial: 'inactive',
|
|
7
|
+
context: {
|
|
8
|
+
cwd: null,
|
|
9
|
+
},
|
|
10
|
+
states: {
|
|
11
|
+
inactive: {
|
|
12
|
+
on: {
|
|
13
|
+
START: { target: 'initializing' },
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
initializing: {
|
|
17
|
+
on: {
|
|
18
|
+
READY: { target: 'active' },
|
|
19
|
+
ERROR: { target: 'inactive' },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
active: {
|
|
23
|
+
on: {
|
|
24
|
+
STOP: { target: 'inactive' },
|
|
25
|
+
SESSION_EXIT: { target: 'restarting' },
|
|
26
|
+
SET_CWD: {
|
|
27
|
+
actions: assign(({ event }) => ({ cwd: event.cwd })),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
restarting: {
|
|
32
|
+
on: {
|
|
33
|
+
READY: { target: 'active' },
|
|
34
|
+
ERROR: { target: 'inactive' },
|
|
35
|
+
STOP: { target: 'inactive' },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const actor = createActor(terminalMachine);
|
|
42
|
+
actor.start();
|
|
43
|
+
|
|
44
|
+
window.terminalMachineAPI = {
|
|
45
|
+
send: function(event) { actor.send(event); },
|
|
46
|
+
getState: function() { return actor.getSnapshot().value; },
|
|
47
|
+
isActive: function() { var s = actor.getSnapshot().value; return s === 'active' || s === 'restarting'; },
|
|
48
|
+
subscribe: function(fn) { return actor.subscribe(fn); },
|
|
49
|
+
};
|
|
50
|
+
window.__terminalMachine = actor;
|
|
51
|
+
})();
|
package/static/js/terminal.js
CHANGED
|
@@ -101,10 +101,14 @@
|
|
|
101
101
|
var cwd = getCwd();
|
|
102
102
|
var dims = term ? { cols: term.cols, rows: term.rows } : { cols: 80, rows: 24 };
|
|
103
103
|
wsSend({ type: 'terminal_start', cwd: cwd, cols: dims.cols, rows: dims.rows });
|
|
104
|
+
if (tmApi()) tmApi().send({ type: 'READY' });
|
|
104
105
|
setTimeout(function() { if (term && term.focus) term.focus(); }, 100);
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
function tmApi() { return window.terminalMachineAPI; }
|
|
109
|
+
|
|
107
110
|
function startTerminal() {
|
|
111
|
+
if (tmApi()) tmApi().send({ type: 'START' });
|
|
108
112
|
if (!ensureTerm()) {
|
|
109
113
|
setTimeout(startTerminal, 200);
|
|
110
114
|
return;
|
|
@@ -124,6 +128,7 @@
|
|
|
124
128
|
|
|
125
129
|
function stopTerminal() {
|
|
126
130
|
termActive = false;
|
|
131
|
+
if (tmApi()) tmApi().send({ type: 'STOP' });
|
|
127
132
|
if (_wsListener && window.wsManager) { window.wsManager.off('message', _wsListener); _wsListener = null; }
|
|
128
133
|
wsSend({ type: 'terminal_stop' });
|
|
129
134
|
}
|