agentgui 1.0.917 → 1.0.919

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.
Files changed (53) hide show
  1. package/database-schema.js +0 -58
  2. package/lib/db-queries-cleanup.js +0 -12
  3. package/lib/db-queries-del.js +0 -1
  4. package/lib/db-queries.js +0 -4
  5. package/lib/http-handler.js +1 -10
  6. package/lib/plugins/acp-plugin.js +1 -2
  7. package/lib/plugins/database-plugin.js +1 -1
  8. package/lib/process-message.js +2 -2
  9. package/lib/provider-config.js +0 -16
  10. package/lib/recovery.js +2 -12
  11. package/lib/routes-agent-actions.js +2 -58
  12. package/lib/routes-debug.js +1 -7
  13. package/lib/routes-registry.js +5 -17
  14. package/lib/server-startup.js +1 -59
  15. package/lib/stream-event-handler.js +1 -3
  16. package/lib/ws-handlers-session2.js +2 -23
  17. package/lib/ws-handlers-util.js +106 -175
  18. package/lib/ws-legacy-handlers.js +1 -104
  19. package/lib/ws-setup.js +1 -3
  20. package/package.json +1 -15
  21. package/server.js +9 -26
  22. package/test.js +1 -21
  23. package/ecosystem.config.cjs +0 -22
  24. package/lib/checkpoint-manager.js +0 -182
  25. package/lib/db-queries-tools.js +0 -131
  26. package/lib/db-queries-voice.js +0 -85
  27. package/lib/oauth-codex.js +0 -164
  28. package/lib/oauth-common.js +0 -92
  29. package/lib/oauth-gemini.js +0 -199
  30. package/lib/plugins/auth-plugin.js +0 -132
  31. package/lib/plugins/speech-plugin.js +0 -72
  32. package/lib/plugins/tools-plugin.js +0 -120
  33. package/lib/pm2-manager.js +0 -170
  34. package/lib/routes-oauth.js +0 -105
  35. package/lib/routes-speech.js +0 -173
  36. package/lib/routes-tools.js +0 -184
  37. package/lib/speech-manager.js +0 -200
  38. package/lib/speech.js +0 -50
  39. package/lib/tool-install-machine.js +0 -157
  40. package/lib/tool-manager.js +0 -98
  41. package/lib/tool-provisioner.js +0 -98
  42. package/lib/tool-spawner.js +0 -163
  43. package/lib/tool-version-check.js +0 -196
  44. package/lib/tool-version-fetch.js +0 -68
  45. package/lib/ws-handlers-oauth.js +0 -76
  46. package/static/js/agent-auth-oauth.js +0 -159
  47. package/static/js/pm2-monitor.js +0 -151
  48. package/static/js/stt-handler.js +0 -147
  49. package/static/js/tool-install-machine.js +0 -155
  50. package/static/js/tools-manager-ui.js +0 -124
  51. package/static/js/tools-manager.js +0 -172
  52. package/static/js/voice-machine.js +0 -145
  53. package/static/js/voice.js +0 -134
@@ -94,52 +94,6 @@ export function initSchema(db) {
94
94
  CREATE INDEX IF NOT EXISTS idx_chunks_conv_created ON chunks(conversationId, created_at);
95
95
  CREATE INDEX IF NOT EXISTS idx_chunks_sess_created ON chunks(sessionId, created_at);
96
96
 
97
- CREATE TABLE IF NOT EXISTS voice_cache (
98
- id TEXT PRIMARY KEY,
99
- conversationId TEXT NOT NULL,
100
- text TEXT NOT NULL,
101
- audioBlob BLOB,
102
- byteSize INTEGER NOT NULL,
103
- created_at INTEGER NOT NULL,
104
- expires_at INTEGER NOT NULL,
105
- FOREIGN KEY (conversationId) REFERENCES conversations(id)
106
- );
107
-
108
- CREATE INDEX IF NOT EXISTS idx_voice_cache_conv ON voice_cache(conversationId);
109
- CREATE INDEX IF NOT EXISTS idx_voice_cache_expires ON voice_cache(expires_at);
110
-
111
- CREATE TABLE IF NOT EXISTS tool_installations (
112
- id TEXT PRIMARY KEY,
113
- tool_id TEXT NOT NULL UNIQUE,
114
- version TEXT,
115
- installed_at INTEGER,
116
- status TEXT NOT NULL DEFAULT 'not_installed',
117
- last_check_at INTEGER,
118
- error_message TEXT,
119
- update_available INTEGER DEFAULT 0,
120
- latest_version TEXT,
121
- created_at INTEGER NOT NULL,
122
- updated_at INTEGER NOT NULL
123
- );
124
-
125
- CREATE INDEX IF NOT EXISTS idx_tool_installations_status ON tool_installations(status);
126
- CREATE INDEX IF NOT EXISTS idx_tool_installations_last_check ON tool_installations(last_check_at);
127
-
128
- CREATE TABLE IF NOT EXISTS tool_install_history (
129
- id TEXT PRIMARY KEY,
130
- tool_id TEXT NOT NULL,
131
- action TEXT NOT NULL,
132
- started_at INTEGER NOT NULL,
133
- completed_at INTEGER,
134
- status TEXT NOT NULL,
135
- error_message TEXT,
136
- created_at INTEGER NOT NULL,
137
- FOREIGN KEY (tool_id) REFERENCES tool_installations(tool_id)
138
- );
139
-
140
- CREATE INDEX IF NOT EXISTS idx_tool_install_history_tool ON tool_install_history(tool_id);
141
- CREATE INDEX IF NOT EXISTS idx_tool_install_history_completed ON tool_install_history(completed_at);
142
-
143
97
  CREATE TABLE IF NOT EXISTS workflow_runs (
144
98
  id TEXT PRIMARY KEY,
145
99
  workflowName TEXT NOT NULL,
@@ -159,17 +113,5 @@ export function initSchema(db) {
159
113
  CREATE INDEX IF NOT EXISTS idx_workflow_runs_sha ON workflow_runs(sha);
160
114
  CREATE INDEX IF NOT EXISTS idx_workflow_runs_completed ON workflow_runs(completedAt);
161
115
 
162
- CREATE TABLE IF NOT EXISTS oauth_tokens (
163
- id TEXT PRIMARY KEY,
164
- provider TEXT NOT NULL,
165
- token TEXT NOT NULL,
166
- email TEXT,
167
- expires_at INTEGER,
168
- created_at INTEGER NOT NULL,
169
- updated_at INTEGER NOT NULL
170
- );
171
-
172
- CREATE INDEX IF NOT EXISTS idx_oauth_tokens_provider ON oauth_tokens(provider);
173
-
174
116
  `);
175
117
  }
@@ -16,8 +16,6 @@ export function addCleanupQueries(q, db, prep, generateId) {
16
16
 
17
17
  prep('DELETE FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?').run(thirtyDaysAgo);
18
18
 
19
- prep('DELETE FROM voice_cache WHERE expires_at <= ?').run(now);
20
-
21
19
  const deletedConvIds = prep("SELECT id FROM conversations WHERE status = 'deleted' AND updated_at < ?").all(sevenDaysAgo).map(r => r.id);
22
20
  for (const cid of deletedConvIds) {
23
21
  prep('DELETE FROM stream_updates WHERE conversationId = ?').run(cid);
@@ -25,25 +23,15 @@ export function addCleanupQueries(q, db, prep, generateId) {
25
23
  prep('DELETE FROM events WHERE conversationId = ?').run(cid);
26
24
  prep('DELETE FROM sessions WHERE conversationId = ?').run(cid);
27
25
  prep('DELETE FROM messages WHERE conversationId = ?').run(cid);
28
- prep('DELETE FROM voice_cache WHERE conversationId = ?').run(cid);
29
26
  prep('DELETE FROM thread_states WHERE thread_id = ?').run(cid);
30
- prep('DELETE FROM checkpoints WHERE thread_id = ?').run(cid);
31
27
  prep('DELETE FROM run_metadata WHERE thread_id = ?').run(cid);
32
28
  prep('DELETE FROM conversations WHERE id = ?').run(cid);
33
29
  }
34
30
 
35
31
  prep('DELETE FROM thread_states WHERE created_at < ?').run(thirtyDaysAgo);
36
- prep('DELETE FROM checkpoints WHERE created_at < ?').run(thirtyDaysAgo);
37
32
  prep('DELETE FROM run_metadata WHERE created_at < ? AND status NOT IN (?, ?)').run(thirtyDaysAgo, 'active', 'pending');
38
33
 
39
34
  prep('DELETE FROM workflow_runs WHERE created_at < ?').run(thirtyDaysAgo);
40
-
41
- const toolIds = prep('SELECT DISTINCT tool_id FROM tool_install_history').all().map(r => r.tool_id);
42
- for (const tid of toolIds) {
43
- prep(`DELETE FROM tool_install_history WHERE tool_id = ? AND id NOT IN (
44
- SELECT id FROM tool_install_history WHERE tool_id = ? ORDER BY created_at DESC LIMIT 50
45
- )`).run(tid, tid);
46
- }
47
35
  });
48
36
 
49
37
  cleanupStmt();
@@ -107,7 +107,6 @@ export function addDeleteQueries(q, db, prep, generateId) {
107
107
  prep('DELETE FROM stream_updates').run();
108
108
  prep('DELETE FROM chunks').run();
109
109
  prep('DELETE FROM events').run();
110
- prep('DELETE FROM voice_cache').run();
111
110
  prep('DELETE FROM sessions').run();
112
111
  prep('DELETE FROM messages').run();
113
112
  prep("UPDATE conversations SET status = 'deleted', updated_at = ? WHERE status != 'deleted'").run(Date.now());
package/lib/db-queries.js CHANGED
@@ -8,8 +8,6 @@ import { addImportQueries } from './db-queries-import.js';
8
8
  import { addStreamQueries } from './db-queries-streams.js';
9
9
  import { addChunkQueries } from './db-queries-chunks.js';
10
10
  import { addChunkQueries2 } from './db-queries-chunks2.js';
11
- import { addVoiceQueries } from './db-queries-voice.js';
12
- import { addToolQueries } from './db-queries-tools.js';
13
11
 
14
12
  export function createQueries(db, prep, generateId) {
15
13
  const q = {
@@ -86,8 +84,6 @@ export function createQueries(db, prep, generateId) {
86
84
  addStreamQueries(q, db, prep, generateId);
87
85
  addChunkQueries(q, db, prep, generateId);
88
86
  addChunkQueries2(q, db, prep, generateId);
89
- addVoiceQueries(q, db, prep, generateId);
90
- addToolQueries(q, db, prep, generateId);
91
87
  Object.assign(q, createACPQueries(db, prep));
92
88
  return q;
93
89
  }
@@ -3,7 +3,7 @@ import path from 'path';
3
3
  import os from 'os';
4
4
  import crypto from 'crypto';
5
5
 
6
- export function createHttpHandler({ BASE_URL, expressApp, queries, sendJSON, serveFile, staticDir, messageQueues, getWss, activeExecutions, getACPStatus, discoveredAgents, PKG_VERSION, RATE_LIMIT_MAX, rateLimitMap, routes, handleGeminiOAuthCallback, handleCodexOAuthCallback, PORT }) {
6
+ export function createHttpHandler({ BASE_URL, expressApp, queries, sendJSON, serveFile, staticDir, messageQueues, getWss, activeExecutions, getACPStatus, discoveredAgents, PKG_VERSION, RATE_LIMIT_MAX, rateLimitMap, routes, PORT }) {
7
7
  return async function httpHandler(req, res) {
8
8
  res.setHeader('Access-Control-Allow-Origin', '*');
9
9
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
@@ -56,9 +56,6 @@ export function createHttpHandler({ BASE_URL, expressApp, queries, sendJSON, ser
56
56
  try {
57
57
  const pathOnly = routePath.split('?')[0];
58
58
 
59
- if (pathOnly === '/oauth2callback' && req.method === 'GET') { await handleGeminiOAuthCallback(req, res, PORT); return; }
60
- if (pathOnly === '/codex-oauth2callback' && req.method === 'GET') { await handleCodexOAuthCallback(req, res, PORT); return; }
61
-
62
59
  if (pathOnly === '/api/health' && req.method === 'GET') {
63
60
  let dbStatus = { ok: true };
64
61
  try { queries._db.prepare('SELECT 1').get(); } catch (e) { dbStatus = { ok: false, error: e.message }; }
@@ -80,20 +77,14 @@ export function createHttpHandler({ BASE_URL, expressApp, queries, sendJSON, ser
80
77
  if (runsHandler) { await runsHandler(req, res); return; }
81
78
  const agentHandler = routes.agents._match(req.method, pathOnly);
82
79
  if (agentHandler) { await agentHandler(req, res); return; }
83
- const oauthHandler = routes.oauth._match(req.method, pathOnly);
84
- if (oauthHandler) { await oauthHandler(req, res); return; }
85
80
  const agentActionsHandler = routes.agentActions._match(req.method, pathOnly);
86
81
  if (agentActionsHandler) { await agentActionsHandler(req, res); return; }
87
82
  const authConfigHandler = routes.authConfig._match(req.method, pathOnly);
88
83
  if (authConfigHandler) { await authConfigHandler(req, res); return; }
89
- const speechHandler = routes.speech._match(req.method, pathOnly);
90
- if (speechHandler) { await speechHandler(req, res, pathOnly); return; }
91
84
  const utilHandler = routes.util._match(req.method, pathOnly);
92
85
  if (utilHandler) { await utilHandler(req, res); return; }
93
86
  const threadHandler = routes.threads._match(req.method, pathOnly);
94
87
  if (threadHandler) { await threadHandler(req, res); return; }
95
- const toolHandler = routes.tools._match(req.method, pathOnly);
96
- if (toolHandler) { await toolHandler(req, res); return; }
97
88
  const debugHandler = routes.debug._match(req.method, pathOnly);
98
89
  if (debugHandler) { await debugHandler(req, res); return; }
99
90
  if (routePath.startsWith('/api/image/')) {
@@ -7,10 +7,9 @@ import fs from 'fs';
7
7
  export default {
8
8
  name: 'acp',
9
9
  version: '1.0.0',
10
- dependencies: ['tools'],
10
+ dependencies: [],
11
11
 
12
12
  async init(config, plugins) {
13
- const tools = plugins.get('tools');
14
13
  const toolProcesses = new Map();
15
14
  const healthCheckIntervals = new Map();
16
15
  const restartCounts = new Map();
@@ -1,4 +1,4 @@
1
- // Database plugin - SQLite init, schema, checkpoint recovery
1
+ // Database plugin - SQLite init, schema
2
2
 
3
3
  import * as dbModule from '../../database.js';
4
4
 
@@ -1,4 +1,4 @@
1
- export function createProcessMessage({ queries, activeExecutions, rateLimitState, execMachine, broadcastSync, runClaudeWithStreaming, cleanupExecution, checkpointManager, discoveredAgents, STARTUP_CWD, buildSystemPrompt, parseRateLimitResetTime, eagerTTS, touchACP, getJsonlWatcher, debugLog, logError, scheduleRetry, drainMessageQueue, createEventHandler }) {
1
+ export function createProcessMessage({ queries, activeExecutions, rateLimitState, execMachine, broadcastSync, runClaudeWithStreaming, cleanupExecution, discoveredAgents, STARTUP_CWD, buildSystemPrompt, parseRateLimitResetTime, touchACP, getJsonlWatcher, debugLog, logError, scheduleRetry, drainMessageQueue, createEventHandler }) {
2
2
  async function processMessageWithStreaming(conversationId, messageId, sessionId, content, agentId, model, subAgent) {
3
3
  const startTime = Date.now();
4
4
  touchACP(agentId);
@@ -37,7 +37,7 @@ export function createProcessMessage({ queries, activeExecutions, rateLimitState
37
37
  const isJsonlBacked = resolvedAgentId === 'claude-code';
38
38
  // stateRef tracks eventCount (for session response metadata) and resumeSessionId
39
39
  const stateRef = { eventCount: 0, resumeSessionId: conv?.claudeSessionId || null };
40
- const onEvent = createEventHandler({ queries, activeExecutions, broadcastSync, rateLimitState, batcherRef: stateRef, sessionId, conversationId, messageId, content, agentId, model, subAgent, isJsonlBacked, getJsonlWatcher, scheduleRetry, eagerTTS, debugLog, parseRateLimitResetTime });
40
+ const onEvent = createEventHandler({ queries, activeExecutions, broadcastSync, rateLimitState, batcherRef: stateRef, sessionId, conversationId, messageId, content, agentId, model, subAgent, isJsonlBacked, getJsonlWatcher, scheduleRetry, debugLog, parseRateLimitResetTime });
41
41
  try {
42
42
  debugLog(`[stream] Starting: conversationId=${conversationId}, sessionId=${sessionId}, agent=${resolvedAgentId}, jsonlBacked=${isJsonlBacked}`);
43
43
  const resolvedModel = model || conv?.model || null;
@@ -1,8 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
- import { getGeminiOAuthStatus } from './oauth-gemini.js';
5
- import { getCodexOAuthStatus } from './oauth-codex.js';
6
4
 
7
5
  export function buildSystemPrompt(agentId, model, subAgent) {
8
6
  const parts = [];
@@ -96,20 +94,6 @@ export function maskKey(key) {
96
94
  export function getProviderConfigs() {
97
95
  const configs = {};
98
96
  for (const [providerId, config] of Object.entries(PROVIDER_CONFIGS)) {
99
- if (providerId === 'google') {
100
- const oauthStatus = getGeminiOAuthStatus();
101
- if (oauthStatus) {
102
- configs[providerId] = { name: config.name, ...oauthStatus };
103
- continue;
104
- }
105
- }
106
- if (providerId === 'codex') {
107
- const oauthStatus = getCodexOAuthStatus();
108
- if (oauthStatus) {
109
- configs[providerId] = { name: config.name, ...oauthStatus };
110
- continue;
111
- }
112
- }
113
97
  for (const configPath of config.configPaths) {
114
98
  try {
115
99
  if (fs.existsSync(configPath)) {
package/lib/recovery.js CHANGED
@@ -1,4 +1,4 @@
1
- export function createRecovery({ activeExecutions, processMessageWithStreaming, queries, broadcastSync, checkpointManager, drainMessageQueue, stuckThresholdMs, noPidGracePeriodMs }) {
1
+ export function createRecovery({ activeExecutions, processMessageWithStreaming, queries, broadcastSync, drainMessageQueue, stuckThresholdMs, noPidGracePeriodMs }) {
2
2
  function isProcessAlive(pid) {
3
3
  try {
4
4
  process.kill(pid, 0);
@@ -45,15 +45,11 @@ export function createRecovery({ activeExecutions, processMessageWithStreaming,
45
45
  async function resumeConversation(conversationId, previousSessionId, reason) {
46
46
  const conv = queries.getConversation(conversationId);
47
47
  if (!conv) throw new Error('Conversation not found');
48
- const checkpoint = previousSessionId ? checkpointManager.loadCheckpoint(previousSessionId) : null;
49
48
  if (previousSessionId) {
50
49
  const prev = queries.getSession ? queries.getSession(previousSessionId) : null;
51
50
  if (prev && prev.status !== 'interrupted') {
52
51
  queries.updateSession(previousSessionId, { status: 'interrupted', error: reason || 'Restarting', completed_at: Date.now() });
53
52
  }
54
- if (checkpoint) {
55
- checkpointManager.markSessionResumed(previousSessionId);
56
- }
57
53
  }
58
54
  const lastMsg = queries.getLastUserMessage(conversationId);
59
55
  const promptText = typeof lastMsg?.content === 'string' ? lastMsg.content : JSON.stringify(lastMsg?.content || 'continue');
@@ -61,8 +57,7 @@ export function createRecovery({ activeExecutions, processMessageWithStreaming,
61
57
  queries.createEvent('session.created', {
62
58
  sessionId: session.id,
63
59
  resumeReason: 'interrupted',
64
- claudeSessionId: conv.claudeSessionId,
65
- checkpointFrom: previousSessionId || null
60
+ claudeSessionId: conv.claudeSessionId
66
61
  }, conversationId, session.id);
67
62
  activeExecutions.set(conversationId, {
68
63
  pid: null,
@@ -76,13 +71,8 @@ export function createRecovery({ activeExecutions, processMessageWithStreaming,
76
71
  conversationId,
77
72
  agentId: conv.agentType,
78
73
  resumed: true,
79
- checkpointAvailable: !!checkpoint,
80
74
  timestamp: Date.now()
81
75
  });
82
- if (checkpoint) {
83
- checkpointManager.storeCheckpointForDelay(conversationId, checkpoint);
84
- console.log(`[RESUME] Checkpoint stored for conv ${conversationId}`);
85
- }
86
76
  console.log(`[RESUME] Restarting conv ${conversationId} (reason: ${reason})`);
87
77
  await processMessageWithStreaming(conversationId, lastMsg?.id || null, session.id, promptText, conv.agentType, conv.model, conv.subAgent);
88
78
  }
@@ -2,7 +2,7 @@ import os from 'os';
2
2
  import { spawn } from 'child_process';
3
3
 
4
4
  export function register(deps) {
5
- const { sendJSON, queries, broadcastSync, discoveredAgents, activeScripts, startGeminiOAuth, startCodexOAuth, getGeminiOAuthState, getCodexOAuthState, modelCache, PORT, BASE_URL, rootDir } = deps;
5
+ const { sendJSON, queries, broadcastSync, discoveredAgents, activeScripts, modelCache } = deps;
6
6
 
7
7
  const routes = {};
8
8
 
@@ -17,68 +17,12 @@ export function register(deps) {
17
17
  const agent = discoveredAgents.find(a => a.id === agentId);
18
18
  if (!agent) { sendJSON(req, res, 404, { error: 'Agent not found' }); return; }
19
19
 
20
- if (agentId === 'codex' || agentId === 'cli-codex') {
21
- try {
22
- const result = await startCodexOAuth(req, { PORT, BASE_URL });
23
- const conversationId = '__agent_auth__';
24
- broadcastSync({ type: 'script_started', conversationId, script: 'auth-codex', agentId: 'codex', timestamp: Date.now() });
25
- broadcastSync({ type: 'script_output', conversationId, data: `\x1b[36mOpening OpenAI OAuth in your browser...\x1b[0m\r\n\r\nIf it doesn't open automatically, visit:\r\n${result.authUrl}\r\n`, stream: 'stdout', timestamp: Date.now() });
26
- const pollId = setInterval(() => {
27
- const state = getCodexOAuthState();
28
- if (state.status === 'success') {
29
- clearInterval(pollId);
30
- const email = state.email || '';
31
- broadcastSync({ type: 'script_output', conversationId, data: `\r\n\x1b[32mAuthentication successful${email ? ' (' + email + ')' : ''}\x1b[0m\r\n`, stream: 'stdout', timestamp: Date.now() });
32
- broadcastSync({ type: 'script_stopped', conversationId, code: 0, timestamp: Date.now() });
33
- } else if (state.status === 'error') {
34
- clearInterval(pollId);
35
- broadcastSync({ type: 'script_output', conversationId, data: `\r\n\x1b[31mAuthentication failed: ${state.error}\x1b[0m\r\n`, stream: 'stderr', timestamp: Date.now() });
36
- broadcastSync({ type: 'script_stopped', conversationId, code: 1, error: state.error, timestamp: Date.now() });
37
- }
38
- }, 1000);
39
- setTimeout(() => clearInterval(pollId), 5 * 60 * 1000);
40
- sendJSON(req, res, 200, { ok: true, agentId, authUrl: result.authUrl, mode: result.mode });
41
- } catch (e) {
42
- console.error('[codex-oauth] /api/agents/codex/auth failed:', e);
43
- sendJSON(req, res, 500, { error: e.message });
44
- }
45
- return;
46
- }
47
-
48
- if (agentId === 'gemini') {
49
- try {
50
- const result = await startGeminiOAuth(req, { PORT, BASE_URL, rootDir });
51
- const conversationId = '__agent_auth__';
52
- broadcastSync({ type: 'script_started', conversationId, script: 'auth-gemini', agentId: 'gemini', timestamp: Date.now() });
53
- broadcastSync({ type: 'script_output', conversationId, data: `\x1b[36mOpening Google OAuth in your browser...\x1b[0m\r\n\r\nIf it doesn't open automatically, visit:\r\n${result.authUrl}\r\n`, stream: 'stdout', timestamp: Date.now() });
54
- const pollId = setInterval(() => {
55
- const state = getGeminiOAuthState();
56
- if (state.status === 'success') {
57
- clearInterval(pollId);
58
- const email = state.email || '';
59
- broadcastSync({ type: 'script_output', conversationId, data: `\r\n\x1b[32mAuthentication successful${email ? ' (' + email + ')' : ''}\x1b[0m\r\n`, stream: 'stdout', timestamp: Date.now() });
60
- broadcastSync({ type: 'script_stopped', conversationId, code: 0, timestamp: Date.now() });
61
- } else if (state.status === 'error') {
62
- clearInterval(pollId);
63
- broadcastSync({ type: 'script_output', conversationId, data: `\r\n\x1b[31mAuthentication failed: ${state.error}\x1b[0m\r\n`, stream: 'stderr', timestamp: Date.now() });
64
- broadcastSync({ type: 'script_stopped', conversationId, code: 1, error: state.error, timestamp: Date.now() });
65
- }
66
- }, 1000);
67
- setTimeout(() => clearInterval(pollId), 5 * 60 * 1000);
68
- sendJSON(req, res, 200, { ok: true, agentId, authUrl: result.authUrl, mode: result.mode });
69
- } catch (e) {
70
- console.error('[gemini-oauth] /api/agents/gemini/auth failed:', e);
71
- sendJSON(req, res, 500, { error: e.message });
72
- }
73
- return;
74
- }
75
-
76
20
  const authCommands = {
77
21
  'claude-code': { cmd: 'claude', args: ['setup-token'] },
78
22
  'opencode': { cmd: 'opencode', args: ['auth', 'login'] },
79
23
  };
80
24
  const authCmd = authCommands[agentId];
81
- if (!authCmd) { sendJSON(req, res, 400, { error: 'No auth command for this agent' }); return; }
25
+ if (!authCmd) { sendJSON(req, res, 400, { error: 'No auth command for this agent (OAuth flows removed; configure provider key via /api/auth/save-config)' }); return; }
82
26
  const conversationId = '__agent_auth__';
83
27
  if (activeScripts.has(conversationId)) { sendJSON(req, res, 409, { error: 'Auth process already running' }); return; }
84
28
  const child = spawn(authCmd.cmd, authCmd.args, { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, FORCE_COLOR: '1' }, shell: os.platform() === 'win32' });
@@ -1,16 +1,10 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
- import * as toolInstallMachine from './tool-install-machine.js';
5
4
  import * as execMachine from './execution-machine.js';
6
5
  import * as acpServerMachine from './acp-server-machine.js';
7
6
 
8
7
  function collectMachineSnapshots(activeExecutions) {
9
- const toolInstall = {};
10
- for (const [id, actor] of toolInstallMachine.getMachineActors()) {
11
- const s = actor.getSnapshot();
12
- toolInstall[id] = { state: s.value, context: s.context };
13
- }
14
8
  const execution = {};
15
9
  for (const id of activeExecutions.keys()) {
16
10
  const s = execMachine.snapshot(id);
@@ -21,7 +15,7 @@ function collectMachineSnapshots(activeExecutions) {
21
15
  const s = actor.getSnapshot();
22
16
  acpServer[id] = { state: s.value, context: { pid: s.context.pid, healthy: s.context.healthy, startedAt: s.context.startedAt, lastUsed: s.context.lastUsed, restarts: s.context.restarts?.length || 0, providerInfo: s.context.providerInfo } };
23
17
  }
24
- return { toolInstall, execution, acpServer };
18
+ return { execution, acpServer };
25
19
  }
26
20
 
27
21
  function readRecentErrors(errLogPath, limit = 50) {
@@ -1,7 +1,4 @@
1
- import { register as registerSpeechRoutes } from './routes-speech.js';
2
- import { register as registerOAuthRoutes } from './routes-oauth.js';
3
1
  import { register as registerUtilRoutes } from './routes-util.js';
4
- import { register as registerToolRoutes } from './routes-tools.js';
5
2
  import { register as registerThreadRoutes } from './routes-threads.js';
6
3
  import { register as registerDebugRoutes } from './routes-debug.js';
7
4
  import { register as registerConvRoutes } from './routes-conversations.js';
@@ -18,24 +15,16 @@ import { register as registerSessionHandlers } from './ws-handlers-session.js';
18
15
  import { register as registerSessionHandlers2 } from './ws-handlers-session2.js';
19
16
  import { register as registerRunHandlers } from './ws-handlers-run.js';
20
17
  import { register as registerUtilHandlers } from './ws-handlers-util.js';
21
- import { register as registerOAuthHandlers } from './ws-handlers-oauth.js';
22
18
  import { register as registerScriptHandlers } from './ws-handlers-scripts.js';
23
19
  import { register as registerQueueHandlers } from './ws-handlers-queue.js';
24
20
  import { register as registerMsgHandlers } from './ws-handlers-msg.js';
25
- import { initSpeechManager, getSpeech, voiceCacheManager, modelDownloadState, ensureModelsDownloaded } from './speech-manager.js';
26
21
  import { getAgentDescriptor } from './agent-descriptors.js';
27
- import { startGeminiOAuth, exchangeGeminiOAuthCode, getGeminiOAuthState } from './oauth-gemini.js';
28
- import { startCodexOAuth, exchangeCodexOAuthCode, getCodexOAuthState } from './oauth-codex.js';
29
22
  import { getProviderConfigs, saveProviderConfig } from './provider-config.js';
30
23
 
31
24
  export function createRegistry(wsRouter, deps) {
32
- const { queries, sendJSON, parseBody, broadcastSync, debugLog, PORT, BASE_URL, rootDir, STARTUP_CWD, PKG_VERSION, processMessageWithStreaming, activeExecutions, activeProcessesByRunId, activeScripts, messageQueues, rateLimitState, cleanupExecution, discoveredAgents, getACPStatus, modelCache, getModelsForAgent, logError, toolManager, syncClients, wsOptimizer, errLogPath, getJsonlWatcher, routes } = deps;
25
+ const { queries, sendJSON, parseBody, broadcastSync, debugLog, PORT, BASE_URL, rootDir, STARTUP_CWD, PKG_VERSION, processMessageWithStreaming, activeExecutions, activeProcessesByRunId, activeScripts, messageQueues, rateLimitState, cleanupExecution, discoveredAgents, getACPStatus, modelCache, getModelsForAgent, logError, syncClients, wsOptimizer, errLogPath, getJsonlWatcher, routes } = deps;
33
26
 
34
- initSpeechManager({ broadcastSync, syncClients, queries });
35
- routes.speech = registerSpeechRoutes({ sendJSON, parseBody, broadcastSync, debugLog });
36
- routes.oauth = registerOAuthRoutes({ sendJSON, parseBody, PORT, BASE_URL, rootDir });
37
27
  routes.util = registerUtilRoutes({ sendJSON, parseBody, queries, STARTUP_CWD, PKG_VERSION });
38
- routes.tools = registerToolRoutes({ sendJSON, parseBody, queries, broadcastSync, logError, toolManager });
39
28
  routes.threads = registerThreadRoutes({ sendJSON, parseBody, queries });
40
29
  routes.debug = registerDebugRoutes({ sendJSON, queries, activeExecutions, messageQueues, syncClients, wsOptimizer, _errLogPath: errLogPath });
41
30
  routes.conv = registerConvRoutes({ sendJSON, parseBody, queries, activeExecutions, broadcastSync });
@@ -44,7 +33,7 @@ export function createRegistry(wsRouter, deps) {
44
33
  routes.sessions = registerSessionsRoutes({ queries, sendJSON, activeExecutions, rateLimitState, debugLog });
45
34
  routes.runs = registerRunsRoutes({ sendJSON, parseBody, queries, broadcastSync, processMessageWithStreaming, activeExecutions, activeProcessesByRunId, discoveredAgents, STARTUP_CWD });
46
35
  routes.scripts = registerScriptsRoutes({ sendJSON, parseBody, queries, broadcastSync, activeScripts, activeExecutions, processMessageWithStreaming, STARTUP_CWD });
47
- routes.agentActions = registerAgentActionsRoutes({ sendJSON, queries, broadcastSync, discoveredAgents, activeScripts, startGeminiOAuth, startCodexOAuth, getGeminiOAuthState, getCodexOAuthState, modelCache, PORT, BASE_URL, rootDir });
36
+ routes.agentActions = registerAgentActionsRoutes({ sendJSON, queries, broadcastSync, discoveredAgents, activeScripts, modelCache, PORT, BASE_URL, rootDir });
48
37
  routes.authConfig = registerAuthConfigRoutes({ sendJSON, parseBody, getProviderConfigs, saveProviderConfig });
49
38
 
50
39
  registerConvHandlers(wsRouter, { queries, activeExecutions, rateLimitState, broadcastSync, processMessageWithStreaming, cleanupExecution, getJsonlWatcher });
@@ -52,11 +41,10 @@ export function createRegistry(wsRouter, deps) {
52
41
  registerMsgHandlers(wsRouter, { queries, activeExecutions, messageQueues, broadcastSync, processMessageWithStreaming, logError });
53
42
  registerQueueHandlers(wsRouter, { queries, messageQueues, broadcastSync });
54
43
  debugLog('[INIT] registerSessionHandlers, agents: ' + discoveredAgents.length);
55
- registerSessionHandlers(wsRouter, { db: queries, discoveredAgents, modelCache, getAgentDescriptor, activeScripts, broadcastSync, startGeminiOAuth: (req) => startGeminiOAuth(req, { PORT, BASE_URL, rootDir }), geminiOAuthState: getGeminiOAuthState });
56
- registerSessionHandlers2(wsRouter, { discoveredAgents, modelCache, activeScripts, broadcastSync, startGeminiOAuth: (req) => startGeminiOAuth(req, { PORT, BASE_URL, rootDir }), geminiOAuthState: getGeminiOAuthState });
44
+ registerSessionHandlers(wsRouter, { db: queries, discoveredAgents, modelCache, getAgentDescriptor, activeScripts, broadcastSync });
45
+ registerSessionHandlers2(wsRouter, { discoveredAgents, modelCache, activeScripts, broadcastSync });
57
46
  debugLog('[INIT] registerSessionHandlers completed');
58
47
  registerRunHandlers(wsRouter, { queries, discoveredAgents, activeExecutions, activeProcessesByRunId, broadcastSync, processMessageWithStreaming, cleanupExecution });
59
- registerUtilHandlers(wsRouter, { queries, wsOptimizer, modelDownloadState, ensureModelsDownloaded, broadcastSync, getSpeech, getProviderConfigs, saveProviderConfig, STARTUP_CWD, voiceCacheManager, toolManager, discoveredAgents });
48
+ registerUtilHandlers(wsRouter, { queries, wsOptimizer, broadcastSync, getProviderConfigs, saveProviderConfig, STARTUP_CWD, discoveredAgents });
60
49
  registerScriptHandlers(wsRouter, { queries, broadcastSync, STARTUP_CWD, activeScripts });
61
- registerOAuthHandlers(wsRouter, { startGeminiOAuth: (req) => startGeminiOAuth(req, { PORT, BASE_URL, rootDir }), exchangeGeminiOAuthCode, geminiOAuthState: getGeminiOAuthState, startCodexOAuth: (req) => startCodexOAuth(req, { PORT, BASE_URL }), exchangeCodexOAuthCode, codexOAuthState: getCodexOAuthState });
62
50
  }
@@ -1,12 +1,11 @@
1
1
  import { JsonlWatcher } from './jsonl-watcher.js';
2
2
 
3
- export function createOnServerReady({ queries, broadcastSync, warmAssetCache, staticDir, toolManager, discoveredAgents, PORT, BASE_URL, watch, setWatcher, resumeInterruptedStreams, activeExecutions, debugLog, installGMAgentConfigs, startACPTools, getACPStatus, execMachine, toolInstallMachine, getSpeech, ensureModelsDownloaded, performAutoImport, performAgentHealthCheck, pm2Manager, pm2Subscribers, recoverStaleSessions }) {
3
+ export function createOnServerReady({ queries, broadcastSync, warmAssetCache, staticDir, discoveredAgents, PORT, BASE_URL, watch, setWatcher, resumeInterruptedStreams, activeExecutions, debugLog, installGMAgentConfigs, startACPTools, getACPStatus, execMachine, performAutoImport, performAgentHealthCheck, recoverStaleSessions }) {
4
4
  let jsonlWatcher = null;
5
5
 
6
6
  function getJsonlWatcher() { return jsonlWatcher; }
7
7
 
8
8
  function onServerReady() {
9
- toolManager.clearStatusCache();
10
9
  console.log(`GMGUI running on http://localhost:${PORT}${BASE_URL}/`);
11
10
  console.log(`Agents: ${discoveredAgents.map(a => a.name).join(', ') || 'none'}`);
12
11
  console.log(`Hot reload: ${watch ? 'on' : 'off'}`);
@@ -55,66 +54,9 @@ export function createOnServerReady({ queries, broadcastSync, warmAssetCache, st
55
54
  }, 6000);
56
55
  }).catch(err => console.error('[ACP] Startup error:', err.message));
57
56
 
58
- const toolIds = ['cli-claude', 'cli-opencode', 'cli-gemini', 'cli-kilo', 'cli-codex', 'cli-agent-browser', 'gm-cc', 'gm-oc', 'gm-gc', 'gm-kilo', 'gm-codex'];
59
- queries.initializeToolInstallations(toolIds.map(id => ({ id })));
60
- console.log('[TOOLS] Starting background provisioning...');
61
-
62
- const toolBroadcaster = (evt) => {
63
- broadcastSync(evt);
64
- if (evt.type === 'tool_install_complete' || evt.type === 'tool_update_complete') {
65
- const d = evt.data || {};
66
- queries.updateToolStatus(evt.toolId, { status: 'installed', version: d.version || null, installed_at: Date.now() });
67
- queries.addToolInstallHistory(evt.toolId, evt.type.includes('update') ? 'update' : 'install', 'success', null);
68
- } else if (evt.type === 'tool_install_failed' || evt.type === 'tool_update_failed') {
69
- queries.updateToolStatus(evt.toolId, { status: 'failed', error_message: evt.data?.error });
70
- queries.addToolInstallHistory(evt.toolId, evt.type.includes('update') ? 'update' : 'install', 'failed', evt.data?.error);
71
- } else if (evt.type === 'tool_status_update') {
72
- const d = evt.data || {};
73
- if (d.installed) queries.updateToolStatus(evt.toolId, { status: 'installed', version: d.installedVersion || null, installed_at: Date.now() });
74
- }
75
- };
76
-
77
- toolManager.autoProvision(toolBroadcaster)
78
- .catch(err => console.error('[TOOLS] Auto-provision error:', err.message))
79
- .then(() => {
80
- const acpActors = ['opencode', 'kilo', 'codex'];
81
- console.log(`[MACHINES] tool-install: ${toolInstallMachine.getMachineActors().size} actors, acp-server: ${acpActors.length} configured`);
82
- console.log('[TOOLS] Starting periodic update checker...');
83
- toolManager.startPeriodicUpdateCheck(toolBroadcaster);
84
- });
85
-
86
- ensureModelsDownloaded().then(async ok => {
87
- if (ok) console.log('[MODELS] Speech models ready');
88
- else console.log('[MODELS] Speech model download failed');
89
- try { const { getVoices } = await getSpeech(); broadcastSync({ type: 'voice_list', voices: getVoices() }); }
90
- catch (err) { debugLog('[VOICE] Failed to broadcast voices: ' + err.message); broadcastSync({ type: 'voice_list', voices: [] }); }
91
- }).catch(async err => {
92
- console.error('[MODELS] Download error:', err.message);
93
- try { const { getVoices } = await getSpeech(); broadcastSync({ type: 'voice_list', voices: getVoices() }); }
94
- catch (err2) { debugLog('[VOICE] Failed to broadcast voices: ' + err2.message); broadcastSync({ type: 'voice_list', voices: [] }); }
95
- });
96
-
97
- getSpeech().then(s => s.preloadTTS()).catch(e => debugLog('[TTS] Preload failed: ' + e.message));
98
57
  performAutoImport();
99
58
  setInterval(performAutoImport, 30000);
100
59
  setInterval(performAgentHealthCheck, 30000);
101
-
102
- const broadcastPM2 = (update) => {
103
- const msg = JSON.stringify(update);
104
- for (const client of pm2Subscribers) { if (client.readyState === 1) { try { client.send(msg); } catch (_) {} } }
105
- };
106
-
107
- const startPM2Monitoring = async () => {
108
- try { await pm2Manager.connect(); await pm2Manager.startMonitoring(broadcastPM2); console.log('[PM2] Monitoring started'); }
109
- catch (err) { console.log('[PM2] Not available:', err.message); broadcastPM2({ type: 'pm2_unavailable', reason: err.message, timestamp: Date.now() }); }
110
- };
111
-
112
- setTimeout(startPM2Monitoring, 2000);
113
- setInterval(async () => {
114
- if (!pm2Manager.connected && !pm2Manager.monitoring) {
115
- try { const healed = await pm2Manager.heal(); if (healed.success) await pm2Manager.startMonitoring(broadcastPM2); } catch (_) {}
116
- }
117
- }, 30000);
118
60
  }
119
61
 
120
62
  return { onServerReady, getJsonlWatcher };
@@ -11,7 +11,7 @@
11
11
  * No JSONL file is written. This handler broadcasts streaming_start,
12
12
  * streaming_progress blocks, and persists chunks to the DB.
13
13
  */
14
- export function createEventHandler({ queries, activeExecutions, broadcastSync, rateLimitState, batcherRef, sessionId, conversationId, messageId, content, agentId, model, subAgent, isJsonlBacked, getJsonlWatcher, scheduleRetry, eagerTTS, debugLog, parseRateLimitResetTime }) {
14
+ export function createEventHandler({ queries, activeExecutions, broadcastSync, rateLimitState, batcherRef, sessionId, conversationId, messageId, content, agentId, model, subAgent, isJsonlBacked, getJsonlWatcher, scheduleRetry, debugLog, parseRateLimitResetTime }) {
15
15
  return function onEvent(parsed) {
16
16
  batcherRef.eventCount++;
17
17
  const entry = activeExecutions.get(conversationId);
@@ -91,7 +91,6 @@ export function createEventHandler({ queries, activeExecutions, broadcastSync, r
91
91
  }, retryAfterSec * 1000);
92
92
  return;
93
93
  }
94
- eagerTTS(block.text, conversationId, sessionId);
95
94
  }
96
95
  }
97
96
  }
@@ -125,7 +124,6 @@ export function createEventHandler({ queries, activeExecutions, broadcastSync, r
125
124
  }, retryAfterSec * 1000);
126
125
  return;
127
126
  }
128
- if (resultText) eagerTTS(resultText, conversationId, sessionId);
129
127
  }
130
128
  };
131
129
  }
@@ -61,35 +61,14 @@ function checkAgentAuth(agent) {
61
61
  }
62
62
 
63
63
  export function register(router, deps) {
64
- const { discoveredAgents, modelCache, activeScripts, broadcastSync,
65
- startGeminiOAuth, geminiOAuthState } = deps;
64
+ const { discoveredAgents, modelCache, activeScripts, broadcastSync } = deps;
66
65
 
67
66
  router.handle('agent.auth', async (p) => {
68
67
  const agentId = p.id;
69
68
  if (!discoveredAgents.find(a => a.id === agentId)) throw { code: 404, message: 'Agent not found' };
70
- if (agentId === 'gemini') {
71
- const result = await startGeminiOAuth();
72
- const cid = '__agent_auth__';
73
- broadcastSync({ type: 'script_started', conversationId: cid, script: 'auth-gemini', agentId: 'gemini', timestamp: Date.now() });
74
- broadcastSync({ type: 'script_output', conversationId: cid, data: `\x1b[36mOpening Google OAuth...\x1b[0m\r\n\r\nVisit:\r\n${result.authUrl}\r\n`, stream: 'stdout', timestamp: Date.now() });
75
- const pollId = setInterval(() => {
76
- const st = geminiOAuthState();
77
- if (st.status === 'success') {
78
- clearInterval(pollId);
79
- broadcastSync({ type: 'script_output', conversationId: cid, data: `\r\n\x1b[32mAuth OK${st.email ? ' (' + st.email + ')' : ''}\x1b[0m\r\n`, stream: 'stdout', timestamp: Date.now() });
80
- broadcastSync({ type: 'script_stopped', conversationId: cid, code: 0, timestamp: Date.now() });
81
- } else if (st.status === 'error') {
82
- clearInterval(pollId);
83
- broadcastSync({ type: 'script_output', conversationId: cid, data: `\r\n\x1b[31mAuth failed: ${st.error}\x1b[0m\r\n`, stream: 'stderr', timestamp: Date.now() });
84
- broadcastSync({ type: 'script_stopped', conversationId: cid, code: 1, error: st.error, timestamp: Date.now() });
85
- }
86
- }, 1000);
87
- setTimeout(() => clearInterval(pollId), 5 * 60 * 1000);
88
- return { ok: true, agentId, authUrl: result.authUrl, mode: result.mode };
89
- }
90
69
  const cmds = { 'claude-code': { cmd: 'claude', args: ['setup-token'] }, 'opencode': { cmd: 'opencode', args: ['auth', 'login'] } };
91
70
  const c = cmds[agentId];
92
- if (!c) throw { code: 400, message: 'No auth command for this agent' };
71
+ if (!c) throw { code: 400, message: 'No auth command for this agent (OAuth flows removed; configure provider key via /api/auth/save-config)' };
93
72
  const pid = spawnScript(c.cmd, c.args, '__agent_auth__', 'auth-' + agentId, agentId, { activeScripts, broadcastSync });
94
73
  return { ok: true, agentId, pid };
95
74
  });