agentgui 1.0.526 → 1.0.528

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.
@@ -7,6 +7,9 @@ function getSpawnOptions(cwd, additionalOptions = {}) {
7
7
  if (isWindows) {
8
8
  options.shell = true;
9
9
  }
10
+ if (!options.env) {
11
+ options.env = { ...process.env };
12
+ }
10
13
  return options;
11
14
  }
12
15
 
@@ -50,6 +53,7 @@ class AgentRunner {
50
53
  this.adapterCommand = config.adapterCommand || null;
51
54
  this.adapterArgs = config.adapterArgs || [];
52
55
  this.npxPackage = config.npxPackage || null;
56
+ this.spawnEnv = config.spawnEnv || {};
53
57
  }
54
58
 
55
59
  defaultBuildArgs(prompt, config) {
@@ -81,7 +85,11 @@ class AgentRunner {
81
85
  } = config;
82
86
 
83
87
  const args = this.buildArgs(prompt, config);
84
- const proc = spawn(this.command, args, getSpawnOptions(cwd));
88
+ const spawnOpts = getSpawnOptions(cwd);
89
+ if (Object.keys(this.spawnEnv).length > 0) {
90
+ spawnOpts.env = { ...spawnOpts.env, ...this.spawnEnv };
91
+ }
92
+ const proc = spawn(this.command, args, spawnOpts);
85
93
 
86
94
  if (config.onPid) {
87
95
  try { config.onPid(proc.pid); } catch (e) {}
@@ -256,7 +264,11 @@ class AgentRunner {
256
264
  args = [...resolved.prefixArgs, ...this.buildArgs(prompt, config)];
257
265
  }
258
266
 
259
- const proc = spawn(cmd, args, getSpawnOptions(cwd));
267
+ const spawnOpts = getSpawnOptions(cwd);
268
+ if (Object.keys(this.spawnEnv).length > 0) {
269
+ spawnOpts.env = { ...spawnOpts.env, ...this.spawnEnv };
270
+ }
271
+ const proc = spawn(cmd, args, spawnOpts);
260
272
 
261
273
  if (config.onPid) {
262
274
  try { config.onPid(proc.pid); } catch (e) {}
@@ -568,6 +580,7 @@ registry.register({
568
580
  protocol: 'direct',
569
581
  supportsStdin: true,
570
582
  supportedFeatures: ['streaming', 'resume', 'system-prompt', 'permissions-skip'],
583
+ spawnEnv: { MAX_THINKING_TOKENS: '0' },
571
584
 
572
585
  buildArgs(prompt, config) {
573
586
  const {
package/lib/speech.js CHANGED
@@ -1,4 +1,51 @@
1
1
  import { createRequire } from 'module';
2
2
  const require = createRequire(import.meta.url);
3
3
  const speech = require('webtalk/speech');
4
- export const { transcribe, synthesize, synthesizeStream, getSTT, getStatus, getVoices, preloadTTS, ttsCacheKey, ttsCacheGet, splitSentences, resetSTTError, clearCorruptedSTTCache } = speech;
4
+
5
+ const ttsMemCache = new Map();
6
+ const TTS_CACHE_MAX_BYTES = 10 * 1024 * 1024;
7
+ let ttsCacheBytes = 0;
8
+
9
+ function ttsCacheKey(text, voiceId) {
10
+ return (voiceId || 'default') + ':' + text;
11
+ }
12
+
13
+ function ttsCacheGet(key) {
14
+ return ttsMemCache.get(key) || null;
15
+ }
16
+
17
+ function ttsCacheSet(key, wav) {
18
+ if (ttsMemCache.has(key)) return;
19
+ const size = wav ? wav.length : 0;
20
+ while (ttsCacheBytes + size > TTS_CACHE_MAX_BYTES && ttsMemCache.size > 0) {
21
+ const oldest = ttsMemCache.keys().next().value;
22
+ const old = ttsMemCache.get(oldest);
23
+ ttsCacheBytes -= old ? old.length : 0;
24
+ ttsMemCache.delete(oldest);
25
+ }
26
+ ttsMemCache.set(key, wav);
27
+ ttsCacheBytes += size;
28
+ }
29
+
30
+ async function synthesizeWithCache(text, voiceId) {
31
+ const key = ttsCacheKey(text, voiceId);
32
+ const cached = ttsCacheGet(key);
33
+ if (cached) return cached;
34
+ const wav = await speech.synthesize(text, voiceId);
35
+ ttsCacheSet(key, wav);
36
+ return wav;
37
+ }
38
+
39
+ export const transcribe = speech.transcribe;
40
+ export const synthesize = synthesizeWithCache;
41
+ export const synthesizeStream = speech.synthesizeStream;
42
+ export const getSTT = speech.getSTT;
43
+ export const getStatus = speech.getStatus;
44
+ export const getVoices = speech.getVoices;
45
+ export const preloadTTS = speech.preloadTTS;
46
+ export { ttsCacheKey, ttsCacheGet, ttsCacheSet };
47
+ export const splitSentences = speech.splitSentences;
48
+ export const resetSTTError = speech.resetSTTError;
49
+ export const clearCorruptedSTTCache = speech.clearCorruptedSTTCache;
50
+ export const getSttOptions = speech.getSttOptions;
51
+ export const VOICE_DIRS = speech.VOICE_DIRS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.526",
3
+ "version": "1.0.528",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -64,7 +64,7 @@ const voiceCacheManager = {
64
64
  this.generating.set(cacheKey, true);
65
65
  try {
66
66
  const speech = await getSpeech();
67
- const audioBlob = await speech.ttsSync(text, 'en-US');
67
+ const audioBlob = await speech.synthesize(text, 'default');
68
68
  const saved = queries.saveVoiceCache(conversationId, text, audioBlob);
69
69
  const totalSize = queries.getVoiceCacheSize(conversationId);
70
70
  if (totalSize > this.maxCacheSize) {
@@ -240,14 +240,15 @@ function flushTTSaccumulator(key, conversationId, sessionId) {
240
240
  }
241
241
  }
242
242
  if (voices.size === 0) return;
243
- const cacheKey = speech.ttsCacheKey(text, vid);
244
243
  for (const vid of voices) {
244
+ const cacheKey = speech.ttsCacheKey(text, vid);
245
245
  const cached = speech.ttsCacheGet(cacheKey);
246
246
  if (cached) {
247
247
  pushTTSAudio(cacheKey, cached, conversationId, sessionId, vid);
248
248
  continue;
249
249
  }
250
250
  speech.synthesize(text, vid).then(wav => {
251
+ if (speech.ttsCacheSet) speech.ttsCacheSet(cacheKey, wav);
251
252
  pushTTSAudio(cacheKey, wav, conversationId, sessionId, vid);
252
253
  }).catch(() => {});
253
254
  }
@@ -2880,6 +2881,23 @@ const server = http.createServer(async (req, res) => {
2880
2881
  return;
2881
2882
  }
2882
2883
 
2884
+ if (pathOnly.startsWith('/api/tts-cache/') && req.method === 'GET') {
2885
+ const cacheKey = decodeURIComponent(pathOnly.slice('/api/tts-cache/'.length));
2886
+ try {
2887
+ const speech = await getSpeech();
2888
+ const cached = speech.ttsCacheGet(cacheKey);
2889
+ if (cached) {
2890
+ res.writeHead(200, { 'Content-Type': 'audio/wav', 'Content-Length': cached.length, 'Cache-Control': 'public, max-age=3600' });
2891
+ res.end(cached);
2892
+ } else {
2893
+ sendJSON(req, res, 404, { error: 'not cached' });
2894
+ }
2895
+ } catch (err) {
2896
+ sendJSON(req, res, 500, { error: err.message });
2897
+ }
2898
+ return;
2899
+ }
2900
+
2883
2901
  if (pathOnly === '/api/speech-status' && req.method === 'GET') {
2884
2902
  try {
2885
2903
  const { getStatus } = await getSpeech();
@@ -25,28 +25,39 @@
25
25
 
26
26
  if (!autoSpeakToggle || !voiceSelector) return;
27
27
 
28
- // Load saved preferences
29
28
  var savedAutoSpeak = localStorage.getItem('toolsAutoSpeak') === 'true';
30
- var savedVoice = localStorage.getItem('toolsVoice') || 'default';
31
-
32
29
  autoSpeakToggle.checked = savedAutoSpeak;
33
- voiceSelector.value = savedVoice;
34
30
 
35
- // Listen for voice list updates
36
31
  window.addEventListener('ws-message', function(e) {
37
32
  var data = e.detail;
38
- if (data && data.type === 'voice_list') {
39
- updateVoiceSelector(data.voices);
40
- }
33
+ if (data && data.type === 'voice_list') updateVoiceSelector(data.voices);
41
34
  });
42
35
 
43
- // Save preferences on change
36
+ function trySubscribeVsManager() {
37
+ if (window.wsManager && window.wsManager.subscribeToVoiceList) {
38
+ window.wsManager.subscribeToVoiceList(updateVoiceSelector);
39
+ } else {
40
+ var BASE = window.__BASE_URL || '';
41
+ fetch(BASE + '/api/voices').then(function(r) { return r.json(); }).then(function(d) {
42
+ if (d.ok && Array.isArray(d.voices)) updateVoiceSelector(d.voices);
43
+ }).catch(function() {});
44
+ setTimeout(function() {
45
+ if (window.wsManager && window.wsManager.subscribeToVoiceList) {
46
+ window.wsManager.subscribeToVoiceList(updateVoiceSelector);
47
+ }
48
+ }, 2000);
49
+ }
50
+ }
51
+ trySubscribeVsManager();
52
+
44
53
  autoSpeakToggle.addEventListener('change', function() {
45
54
  localStorage.setItem('toolsAutoSpeak', this.checked);
55
+ if (window.voiceModule) window.voiceModule.setAutoSpeak(this.checked);
46
56
  });
47
57
 
48
58
  voiceSelector.addEventListener('change', function() {
49
59
  localStorage.setItem('toolsVoice', this.value);
60
+ if (window.voiceModule) window.voiceModule.setVoice(this.value);
50
61
  });
51
62
  }
52
63
 
@@ -54,17 +65,40 @@
54
65
  var voiceSelector = document.getElementById('toolsVoiceSelector');
55
66
  if (!voiceSelector || !voices || !Array.isArray(voices)) return;
56
67
 
57
- var currentValue = voiceSelector.value;
58
- voiceSelector.innerHTML = '<option value="default">Voice</option>';
68
+ var currentValue = voiceSelector.value || localStorage.getItem('toolsVoice') || 'default';
69
+ voiceSelector.innerHTML = '';
70
+
71
+ var builtIn = voices.filter(function(v) { return !v.isCustom; });
72
+ var custom = voices.filter(function(v) { return v.isCustom; });
73
+
74
+ if (builtIn.length) {
75
+ var grp1 = document.createElement('optgroup');
76
+ grp1.label = 'Built-in Voices';
77
+ builtIn.forEach(function(voice) {
78
+ var opt = document.createElement('option');
79
+ opt.value = voice.id;
80
+ var parts = [];
81
+ if (voice.gender && voice.gender !== 'custom') parts.push(voice.gender);
82
+ if (voice.accent && voice.accent !== 'custom') parts.push(voice.accent);
83
+ opt.textContent = voice.name + (parts.length ? ' (' + parts.join(', ') + ')' : '');
84
+ grp1.appendChild(opt);
85
+ });
86
+ voiceSelector.appendChild(grp1);
87
+ }
59
88
 
60
- voices.forEach(function(voice) {
61
- var option = document.createElement('option');
62
- option.value = voice;
63
- option.textContent = voice;
64
- voiceSelector.appendChild(option);
65
- });
89
+ if (custom.length) {
90
+ var grp2 = document.createElement('optgroup');
91
+ grp2.label = 'Custom Voices';
92
+ custom.forEach(function(voice) {
93
+ var opt = document.createElement('option');
94
+ opt.value = voice.id;
95
+ opt.textContent = voice.name;
96
+ grp2.appendChild(opt);
97
+ });
98
+ voiceSelector.appendChild(grp2);
99
+ }
66
100
 
67
- if (voices.includes(currentValue)) {
101
+ if (voiceSelector.querySelector('option[value="' + currentValue + '"]')) {
68
102
  voiceSelector.value = currentValue;
69
103
  }
70
104
  }
@@ -194,10 +228,23 @@
194
228
  popup.classList.remove('open');
195
229
  }
196
230
 
231
+ function isAutoSpeakOn() {
232
+ var toggle = document.getElementById('toolsAutoSpeakToggle');
233
+ return toggle ? toggle.checked : false;
234
+ }
235
+
197
236
  function onWsMessage(e) {
198
237
  var data = e.detail;
199
238
  if (!data) return;
200
239
 
240
+ if (data.type === 'streaming_progress' && data.block && data.block.type === 'text' && data.block.text) {
241
+ if (isAutoSpeakOn() && (!data.blockRole || data.blockRole === 'assistant')) {
242
+ if (window.voiceModule && typeof window.voiceModule.speakText === 'function') {
243
+ window.voiceModule.speakText(data.block.text);
244
+ }
245
+ }
246
+ }
247
+
201
248
  if (data.type === 'tools_update_started') {
202
249
  var updateTools = data.tools || [];
203
250
  updateTools.forEach(function(toolId) {
@@ -18,7 +18,7 @@
18
18
  var _lastVoiceBlockText = null;
19
19
  var _lastVoiceBlockTime = 0;
20
20
  var _voiceBreakNext = false;
21
- var selectedVoiceId = localStorage.getItem('voice-selected-id') || 'default';
21
+ var selectedVoiceId = localStorage.getItem('gmgui-voice-selection') || 'default';
22
22
  var ttsAudioCache = new Map();
23
23
  var TTS_CLIENT_CACHE_MAX = 50;
24
24
 
@@ -33,7 +33,7 @@
33
33
  function setupVoiceSelector() {
34
34
  var selector = document.getElementById('voiceSelector');
35
35
  if (!selector) return;
36
- var saved = localStorage.getItem('voice-selected-id');
36
+ var saved = localStorage.getItem('gmgui-voice-selection');
37
37
  if (saved) selectedVoiceId = saved;
38
38
  if (window.wsManager) {
39
39
  window.wsManager.subscribeToVoiceList(function(voices) {
@@ -66,8 +66,8 @@
66
66
  });
67
67
  selector.appendChild(grp2);
68
68
  }
69
- if (saved && selector.querySelector('option[value="' + saved + '"]')) {
70
- selector.value = saved;
69
+ if (selectedVoiceId && selector.querySelector('option[value="' + selectedVoiceId + '"]')) {
70
+ selector.value = selectedVoiceId;
71
71
  }
72
72
  });
73
73
  return;
@@ -104,15 +104,15 @@
104
104
  });
105
105
  selector.appendChild(grp2);
106
106
  }
107
- if (saved && selector.querySelector('option[value="' + saved + '"]')) {
108
- selector.value = saved;
107
+ if (selectedVoiceId && selector.querySelector('option[value="' + selectedVoiceId + '"]')) {
108
+ selector.value = selectedVoiceId;
109
109
  }
110
110
  })
111
111
  .catch(function(err) { console.error('[Voice] Failed to load voices:', err); });
112
112
  }
113
113
  selector.addEventListener('change', function() {
114
114
  selectedVoiceId = selector.value;
115
- localStorage.setItem('voice-selected-id', selectedVoiceId);
115
+ localStorage.setItem('gmgui-voice-selection', selectedVoiceId);
116
116
  sendVoiceToServer();
117
117
  });
118
118
  }
@@ -211,14 +211,14 @@
211
211
  function setupTTSToggle() {
212
212
  var toggle = document.getElementById('voiceTTSToggle');
213
213
  if (toggle) {
214
- var saved = localStorage.getItem('voice-tts-enabled');
214
+ var saved = localStorage.getItem('gmgui-auto-speak');
215
215
  if (saved !== null) {
216
216
  ttsEnabled = saved === 'true';
217
217
  toggle.checked = ttsEnabled;
218
218
  }
219
219
  toggle.addEventListener('change', function() {
220
220
  ttsEnabled = toggle.checked;
221
- localStorage.setItem('voice-tts-enabled', ttsEnabled);
221
+ localStorage.setItem('gmgui-auto-speak', ttsEnabled);
222
222
  if (!ttsEnabled) stopSpeaking();
223
223
  });
224
224
  }
@@ -352,6 +352,10 @@
352
352
 
353
353
  function speak(text) {
354
354
  if (!ttsEnabled) return;
355
+ speakDirect(text);
356
+ }
357
+
358
+ function speakDirect(text) {
355
359
  var clean = text.replace(/<[^>]*>/g, '').trim();
356
360
  if (!clean) return;
357
361
  var parts = [];
@@ -946,10 +950,39 @@
946
950
  return text.replace(/[&<>"']/g, function(c) { return map[c]; });
947
951
  }
948
952
 
953
+ function getAutoSpeak() {
954
+ return ttsEnabled;
955
+ }
956
+
957
+ function setAutoSpeak(value) {
958
+ ttsEnabled = Boolean(value);
959
+ localStorage.setItem('gmgui-auto-speak', ttsEnabled);
960
+ var toggle = document.getElementById('voiceTTSToggle');
961
+ if (toggle) toggle.checked = ttsEnabled;
962
+ if (!ttsEnabled) stopSpeaking();
963
+ }
964
+
965
+ function getVoice() {
966
+ return selectedVoiceId;
967
+ }
968
+
969
+ function setVoice(voiceId) {
970
+ selectedVoiceId = String(voiceId);
971
+ localStorage.setItem('gmgui-voice-selection', selectedVoiceId);
972
+ var selector = document.getElementById('voiceSelector');
973
+ if (selector) selector.value = selectedVoiceId;
974
+ sendVoiceToServer();
975
+ }
976
+
949
977
  window.voiceModule = {
950
978
  activate: activate,
951
979
  deactivate: deactivate,
952
- handleBlock: handleVoiceBlock
980
+ handleBlock: handleVoiceBlock,
981
+ getAutoSpeak: getAutoSpeak,
982
+ setAutoSpeak: setAutoSpeak,
983
+ getVoice: getVoice,
984
+ setVoice: setVoice,
985
+ speakText: speakDirect
953
986
  };
954
987
 
955
988
  if (document.readyState === 'loading') {