agentgui 1.0.916 → 1.0.918

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/CLAUDE.md +1 -382
  2. package/database-schema.js +0 -58
  3. package/lib/db-queries-cleanup.js +0 -12
  4. package/lib/db-queries-del.js +0 -1
  5. package/lib/db-queries.js +0 -4
  6. package/lib/http-handler.js +1 -10
  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
package/CLAUDE.md CHANGED
@@ -1,382 +1 @@
1
- # AgentGUI
2
-
3
- Multi-agent GUI client for AI coding agents (Claude Code, Gemini CLI, OpenCode, Goose, etc.) with real-time streaming, WebSocket sync, and SQLite persistence.
4
-
5
- ## Running
6
-
7
- ```bash
8
- npm install
9
- npm run dev # node server.js --watch
10
- ```
11
-
12
- Server starts on `http://localhost:3000`, redirects to `/gm/`.
13
-
14
- ## Architecture
15
-
16
- ```
17
- server.js Bootstrap: imports, constants, wiring of all factories; delegates to lib/ modules for HTTP handler, WS setup, routes, startup
18
- database.js SQLite connection, schema DDL, migrations (queries extracted to lib/db-queries.js)
19
- acp-queries.js ACP query helpers (UUID, timestamp, JSON utilities)
20
- bin/gmgui.cjs CLI entry point (npx agentgui / bun x agentgui)
21
-
22
- lib/claude-runner.js Agent framework - AgentRunner/AgentRegistry classes, direct protocol execution
23
- lib/acp-runner.js ACP JSON-RPC session lifecycle (init, session/new, prompt, drain)
24
- lib/acp-protocol.js ACP session/update message normalization (shared by all ACP agents)
25
- lib/acp-sdk-manager.js ACP tool lifecycle - on-demand start opencode/kilo/codex, health checks, idle timeout
26
- lib/acp-server-machine.js XState v5 machine per ACP tool: stopped/starting/running/crashed/restarting states
27
- lib/agent-discovery.js Agent binary detection (findCommand), ACP server query, discoverAgents, CLI wrapper logic
28
- lib/agent-descriptors.js Data-driven ACP agent descriptor builder
29
- lib/checkpoint-manager.js Session recovery - load checkpoints, inject into resume flow, idempotency
30
- lib/codec.js msgpack encode/decode (pack/unpack wrappers)
31
- lib/db-queries.js All 88 query functions (createQueries factory, extracted from database.js)
32
- lib/execution-machine.js XState v5 machine per conversation: idle/streaming/draining/rate_limited states
33
- lib/gm-agent-configs.js GM agent configuration and spawning
34
- lib/jsonl-parser.js JSONL event parsing, session tracking, streaming state (extracted from jsonl-watcher.js)
35
- lib/jsonl-watcher.js Watches ~/.claude/projects for JSONL file changes, delegates parsing to jsonl-parser.js
36
- lib/oauth-common.js Shared OAuth helpers (buildBaseUrl, isRemoteRequest, encodeOAuthState, result/relay pages)
37
- lib/oauth-gemini.js Gemini OAuth flow (credential discovery, token exchange, callback handling)
38
- lib/oauth-codex.js Codex CLI OAuth flow (PKCE S256, token exchange, callback handling)
39
- lib/plugin-loader.js Plugin discovery and loading (EventEmitter-based)
40
- lib/pm2-manager.js PM2 process management wrapper
41
- lib/speech.js Speech-to-text and text-to-speech via @huggingface/transformers
42
- lib/speech-manager.js TTS orchestration (eager TTS, voice cache, model download, broadcastModelProgress)
43
- lib/tool-install-machine.js XState v5 machine per tool: unchecked/checking/idle/installing/installed/updating/needs_update/failed states
44
- lib/tool-manager.js Tool facade - re-exports from tool-version-check, tool-version-fetch, tool-spawner, tool-provisioner
45
- lib/tool-version-check.js Sync/local version detection: BIN_MAP, FRAMEWORK_PATHS, checkCliInstalled, getCliVersion, checkToolInstalled, compareVersions, getInstalledVersion
46
- lib/tool-version-fetch.js Async/network version functions: getPublishedVersion, fetchPublishedVersion, clearVersionCache, checkToolViaBunx
47
- lib/tool-spawner.js npm/bun install/update spawn with timeout and heartbeat
48
- lib/tool-provisioner.js Auto-provisioning and periodic update checking
49
- lib/routes-speech.js Speech/TTS HTTP route handlers (stt, tts, voices, speech-status)
50
- lib/routes-oauth.js OAuth HTTP route handlers (gemini-oauth/*, codex-oauth/*)
51
- lib/routes-tools.js Tool management HTTP route handlers (list, install, update, history, refresh)
52
- lib/routes-util.js Utility HTTP route handlers (clone, folders, git, home, version, import)
53
- lib/routes-agents.js Agent list/search/auth-status/descriptor/models HTTP route handlers
54
- lib/routes-conversations.js Conversation CRUD HTTP route handlers (list, create, get, update, delete, archive, restore)
55
- lib/routes-messages.js Message/stream/queue HTTP route handlers (GET+POST messages, stream, queue CRUD)
56
- lib/routes-sessions.js Session/chunk/full/execution HTTP route handlers (session get, chunks, full load, execution events)
57
- lib/routes-runs.js Runs HTTP route handlers (POST /api/runs, runs search, run by id, wait, cancel, thread run cancel/wait)
58
- lib/routes-scripts.js Scripts/cancel/resume/inject HTTP route handlers (conversation scripts, run-script, stop-script, cancel, resume, inject)
59
- lib/routes-agent-actions.js Agent auth and update HTTP route handlers (POST /api/agents/:id/auth, POST /api/agents/:id/update)
60
- lib/routes-auth-config.js Auth config HTTP route handlers (GET /api/auth/configs, POST /api/auth/save-config)
61
- lib/routes-upload.js Express sub-app: POST /api/upload/:conversationId (Busboy file upload) + GET /files/:conversationId fsbrowse router; createExpressApp(deps) factory
62
- lib/http-utils.js HTTP utility functions (parseBody, acceptsEncoding, compressAndSend, sendJSON)
63
- lib/http-handler.js Main HTTP request handler factory: createHttpHandler(deps) => async (req,res); rate limiting, auth, CORS, route dispatch, static file serving
64
- lib/provider-config.js Provider config helpers (buildSystemPrompt, maskKey, getProviderConfigs, saveProviderConfig, PROVIDER_CONFIGS)
65
- lib/server-utils.js Server utility functions (logError, errLogPath, makeCleanupExecution, makeGetModelsForAgent)
66
- lib/routes-registry.js Route + WS handler registration: createRegistry(wsRouter, deps) => _routes; wires all HTTP route _match objects and all WS handler registrations
67
- lib/ws-setup.js WebSocket server setup: createWsSetup(server, deps) => { wss, hotReloadClients }; connection auth, client tracking, watch file reload, heartbeat
68
- lib/ws-legacy-handlers.js Legacy WS message handler: subscribe/unsubscribe/terminal PTY/pm2 commands; called from ws-setup.js onLegacy
69
- lib/server-startup.js Server startup: createOnServerReady(deps) => { onServerReady, getJsonlWatcher }; tools, ACP, speech, PM2 monitoring init
70
- lib/server-startup2.js Startup helpers: createAutoImport (hasIndexFilesChanged + performAutoImport), createDbRecovery (orphaned session cleanup), createPluginLoader (plugin extension loading)
71
- lib/routes-debug.js Debug/backup/restore/ws-stats HTTP route handlers
72
- lib/routes-threads.js Thread CRUD HTTP route handlers (ACP v0.2.3 thread API)
73
- lib/ws-protocol.js WebSocket RPC router (WsRouter class)
74
- lib/ws-optimizer.js Per-client priority queue for WS event batching
75
- lib/ws-handlers-conv.js Conversation CRUD, chunks, cancel, steer, inject RPC handlers
76
- lib/ws-handlers-msg.js Message send/stream/list RPC handlers + execution start/enqueue
77
- lib/ws-handlers-queue.js Queue list/delete/update RPC handlers
78
- lib/ws-handlers-session.js Session/agent RPC handlers
79
- lib/ws-handlers-run.js Thread/run RPC handlers
80
- lib/ws-handlers-util.js Utility RPC handlers (speech, auth, git, tools, voice)
81
- lib/ws-handlers-oauth.js Gemini + Codex OAuth WS RPC handlers
82
- lib/ws-handlers-scripts.js npm script run/stop WS RPC handlers
83
- lib/plugins/ Server plugins (acp, agents, auth, database, files, git, speech, stream, tools, websocket, workflow)
84
-
85
- static/index.html Main HTML shell
86
- static/app.js UI IIFEs (sidebar search, error boundary, import, archived view, presets)
87
- static/js/app-shortcuts.js Keyboard shortcuts overlay
88
- static/theme.js Theme switching
89
- static/css/main.css All application styles (extracted from index.html)
90
- static/css/tools-popup.css Tool popup styles
91
- static/js/client.js AgentGUIClient class (constructor + _dbg + init); instantiation at bottom
92
- static/js/client-ws.js WebSocket listeners, _convIsStreaming, _setConvStreaming, setupRendererListeners, restoreStateFromUrl, isValidId (prototype extension)
93
- static/js/client-url.js URL/scroll helpers: updateUrlForConversation, saveScrollPosition, restoreScrollPosition, setupScrollTracking (prototype extension)
94
- static/js/client-ui.js setupUI (modified, calls _setupUIButtonEvents/_setupUIWindowEvents) + setupChatMicButton (prototype extension)
95
- static/js/client-ui-controls.js _setupUIButtonEvents + _setupUIWindowEvents extracted helpers (prototype extension)
96
- static/js/client-ws-msg.js connectWebSocket, handleWebSocketMessage, queueEvent (prototype extension)
97
- static/js/client-streaming.js handleStreamingStart (prototype extension)
98
- static/js/client-streaming2.js handleStreamingResumed, handleStreamingProgress, _handleStreamingProgressInner (prototype extension)
99
- static/js/client-streaming3.js renderBlockContent, scrollToBottom, _showNewContentPill, _removeNewContentPill, handleStreamingError (prototype extension)
100
- static/js/client-streaming4.js handleStreamingComplete, _promptPushIfWeOwnRemote, handleConversationCreated, handleMessageCreated, queue handlers (prototype extension)
101
- static/js/client-events.js fetchAndRenderQueue, handleRateLimitHit/Clear, handleAllConversationsDeleted, isHtmlContent, sanitizeHtml, parseMarkdownCodeBlocks (prototype extension)
102
- static/js/client-render.js renderCodeBlock, renderMessageContent (prototype extension)
103
- static/js/client-exec.js startExecution, optimistic message helpers, _subscribeToConversationUpdates, _flushBgCache (prototype extension)
104
- static/js/client-helpers.js _recoverMissedChunks, cache/placeholder/height/countdown/debug helpers, showLoadingSpinner/hideLoadingSpinner (prototype extension)
105
- static/js/client-ui2.js _showWelcomeScreen, _showSkeletonLoading, streamToConversation, _hydrateSessionBlocks (prototype extension)
106
- static/js/client-conv.js _getLazyObserver, _renderConversationContent, renderChunk, _renderChunkInner, loadAgents, loadSubAgentsForCli (prototype extension)
107
- static/js/client-agents.js checkSpeechStatus, loadModelsForAgent, _populateModelSelector, lock/unlockAgentAndModel, applyAgentAndModelSelection, loadConversations, updateConnectionStatus (prototype extension)
108
- static/js/client-status.js _updateConnectionIndicator, _handleModelDownloadProgress, _handleTTSSetupProgress, _toggleConnectionTooltip, updateMetrics, controls, toggleTheme, createNewConversation (prototype extension)
109
- static/js/client-cache.js cacheCurrentConversation, invalidateCache, loadConversationMessages (prototype extension)
110
- static/js/client-load.js _makeLoadRequest, _verifyRequestId, _completeLoadRequest, _loadConvRender (prototype extension)
111
- static/js/client-scroll.js syncPromptState, updateBusyPromptArea, removeScrollUpDetection, setupScrollUpDetection (prototype extension)
112
- static/js/client-utils.js renderMessagesFragment, renderMessages, escapeHtml, showError, on, emit, agent/model getters, draft/prompt helpers, destroy (prototype extension)
113
- static/js/conversations.js Conversation management (class definition)
114
- static/js/conv-list-renderer.js Conversation list render, CRUD, WS listener (prototype extension)
115
- static/js/conv-sidebar-actions.js Sidebar delegated listeners, folder browser (prototype extension)
116
- static/js/conv-sidebar-clone.js Delete-all, clone UI, DOM-ready bootstrap (prototype extension)
117
- static/js/streaming-renderer.js Renders agent streaming events as HTML
118
- static/js/event-processor.js Processes incoming events
119
- static/js/event-filter-config.js Filters events by type
120
- static/js/websocket-manager.js WebSocket send/subscribe/disconnect methods (prototype extension)
121
- static/js/ws-core.js WebSocketManager class + connect/reconnect/heartbeat core
122
- static/js/ws-latency.js WebSocket latency tracking, ping/pong, quality tiers (prototype extension)
123
- static/js/ws-client.js WsClient RPC wrapper over WebSocketManager
124
- static/js/ui-components.js UI component helpers (modal, tabs, alert, spinner, progress, collapsible)
125
- static/js/ui-components-rendering.js Input/select/button/badge factory helpers (static extension)
126
- static/js/syntax-highlighter.js Code syntax highlighting (class definition)
127
- static/js/syntax-highlighter-render.js Token-to-HTML render logic (prototype extension)
128
- static/js/voice.js Voice input/output
129
- static/js/stt-handler.js Speech-to-text recording and upload
130
- static/js/features.js View toggle, drag-drop upload, model progress indicator
131
- static/js/tools-manager.js Tool install/update UI orchestrator
132
- static/js/tools-manager-ui.js Tool card rendering + voice selector helpers
133
- static/js/agent-auth.js Agent authentication UI (dropdown, auth-status, provider keys)
134
- static/js/agent-auth-oauth.js OAuth modal functions (triggerAuth, onWsMessage, paste fallback)
135
- static/js/dialogs.js Modal dialog system (class definition)
136
- static/js/dialogs-types.js Dialog type-specific render helpers (prototype extension)
137
- static/js/image-loader.js Lazy image loading orchestration for agent file read events
138
- static/js/image-loader-element.js ImageLoader DOM element rendering (prototype extension)
139
- static/js/pm2-monitor.js PM2 process monitor UI
140
- static/js/script-runner.js npm script runner UI
141
- static/js/state-barrier.js Atomic state machine for conversation management
142
- static/js/terminal.js xterm.js terminal integration
143
- static/js/ws-machine.js XState v5 WS connection machine: disconnected/connecting/connected/reconnecting
144
- static/js/conv-machine.js XState v5 per-conversation UI machine: idle/streaming/queued
145
- static/js/tool-install-machine.js XState v5 per-tool UI install machine: idle/installing/installed/updating/needs_update/failed
146
- static/js/voice-machine.js XState v5 voice/TTS machine: idle/queued/speaking/disabled (circuit-breaker)
147
- static/js/conv-list-machine.js XState v5 conversation list machine: unloaded/loading/loaded/error
148
- static/js/prompt-machine.js XState v5 prompt area machine: ready/loading/streaming/queued/disabled
149
- static/lib/xstate.umd.min.js XState v5 browser bundle (UMD, served locally from node_modules)
150
- static/lib/msgpackr.min.js msgpack browser bundle
151
- static/lib/webjsx.js WebJSX library
152
- static/vendor/ Third-party assets (highlight.js, Prism, RippleUI, xterm.js)
153
- ```
154
-
155
- ## XState State Machines
156
-
157
- XState v5 machines own their domains exclusively. No ad-hoc Maps/Sets parallel to machines.
158
-
159
- **Server** (lib/): `execution-machine` (per conversation: idle/streaming/draining/rate_limited), `acp-server-machine` (per tool: stopped/starting/running/crashed/restarting), `tool-install-machine` (per tool: unchecked→checking→idle/installed/needs_update/installing/updating/failed). API: `send(id, event)`, `isLocked()`, snapshots at `GET /api/debug/machines` when DEBUG=1.
160
-
161
- **Client** (static/js/, UMD): `ws-machine` (disconnected/connecting/connected/reconnecting), `conv-machine` (per conv: idle/streaming/queued), `tool-install-machine` (per tool), `voice-machine` (single: idle/queued/speaking/disabled circuit-breaker), `conv-list-machine` (single: unloaded/loading/loaded/error), `prompt-machine` (single: ready/loading/streaming/queued/disabled). Load order: xstate.umd.min.js → ws-machine → conv-machine → tool-install-machine → voice-machine → conv-list-machine → prompt-machine. Exposed at `window.__*` globals for debug.
162
-
163
- ## Key Details
164
-
165
- - Express is used only for file upload (`/api/upload/:conversationId`) and fsbrowse file browser (`/files/:conversationId`). All other routes use raw `http.createServer` with manual routing.
166
- - Agent discovery scans PATH for known CLI binaries (claude, opencode, gemini, goose, etc.) at startup.
167
- - Database lives at `~/.gmgui/data.db`. Tables: conversations, messages, events, sessions, stream chunks.
168
- - WebSocket endpoint is at `BASE_URL + /sync`. Supports subscribe/unsubscribe by sessionId or conversationId, and ping.
169
- - All WS RPC uses msgpack binary encoding (lib/codec.js). Wire format: `{ r, m, p }` request, `{ r, d }` reply, `{ type, seq }` broadcast push.
170
- - `perMessageDeflate` is disabled on the WS server — msgpack binary doesn't compress well and brotli/gzip was blocking the event loop. HTTP-layer gzip handles static assets.
171
- - Static assets use `Cache-Control: public, no-cache` + ETag. Browser always revalidates (sends If-None-Match), server returns 304 if unchanged. Compressed once on first request, served from RAM (`_assetCache` Map keyed by etag).
172
- - Deployment: runs behind Traefik/Caddy which handles TLS and can support WebTransport/QUIC.
173
-
174
- ## Environment Variables
175
-
176
- - `PORT` - Server port (default: 3000)
177
- - `BASE_URL` - URL prefix (default: /gm)
178
- - `STARTUP_CWD` - Working directory passed to agents
179
- - `HOT_RELOAD` - Set to "false" to disable watch mode
180
- - `CODEX_HOME` - Override Codex CLI home directory (default: `~/.codex`)
181
- - `RATE_LIMIT_MAX` - Max HTTP requests per IP per minute (default: 300)
182
- - `PASSWORD` - Basic auth password for all HTTP routes (optional)
183
- - `AGENTGUI_BASE_URL` - Override base URL for OAuth callbacks (e.g., `https://myserver.com`)
184
-
185
- ## ACP Tool Lifecycle
186
-
187
- On startup, agentgui auto-launches bundled ACP tools (opencode, kilo) as HTTP servers:
188
- - OpenCode: port 18100 (`opencode acp --port 18100`)
189
- - Kilo: port 18101 (`kilo acp --port 18101`)
190
-
191
- Managed by `lib/acp-sdk-manager.js`. Features: crash restart with exponential backoff (max 10 in 5min), health checks every 30s via `GET /provider`, clean shutdown on SIGTERM. The `acpPort` field on discovered agents is set automatically once healthy. Models are queried from the running ACP HTTP servers via their `/provider` endpoint.
192
-
193
- ## REST API
194
-
195
- All routes prefixed with `BASE_URL` (default `/gm`). Key endpoints:
196
-
197
- **Conversations**: `GET /api/conversations`, `POST /api/conversations`, `GET/POST/DELETE /api/conversations/:id`, `POST /api/conversations/:id/archive`, `POST /api/conversations/:id/restore`, `GET /api/conversations/:id/messages`, `POST /api/conversations/:id/messages`, `POST /api/conversations/:id/stream`, `GET /api/conversations/:id/full`, `GET /api/conversations/:id/chunks`, `GET /api/conversations/:id/sessions/latest`
198
-
199
- **Sessions**: `GET /api/sessions/:id`, `GET /api/sessions/:id/chunks`, `GET /api/sessions/:id/execution`
200
-
201
- **Agents & ACP**: `GET /api/agents`, `GET /api/acp/status`, `GET /api/health`
202
-
203
- **Speech**: `POST /api/stt`, `POST /api/tts`, `GET /api/speech-status`
204
-
205
- **Tools**: `GET /api/tools`, `GET/POST /api/tools/:id/install`, `POST /api/tools/:id/update`, `GET /api/tools/:id/history`, `POST /api/tools/update`, `POST /api/tools/refresh-all`
206
-
207
- **OAuth**: `POST /api/codex-oauth/start`, `GET /api/codex-oauth/status`, `POST /api/codex-oauth/relay`, `POST /api/codex-oauth/complete`, `GET /codex-oauth2callback`
208
-
209
- **Utility**: `POST /api/folders`, `GET /api/home`
210
-
211
- ## Tool Update System
212
-
213
- Tool updates are managed through a complete pipeline:
214
-
215
- **Update Flow:**
216
- 1. Frontend (`static/js/tools-manager.js`) initiates POST to `/api/tools/{id}/update`
217
- 2. Server (`server.js` lines 1904-1961 for individual, 1973-2003 for batch) spawns bun x process
218
- 3. Tool manager (`lib/tool-manager.js` lines 400-432) executes `bun x <package>` and detects new version
219
- 4. Version is saved to database: `queries.updateToolStatus(toolId, { version, status: 'installed' })`
220
- 5. WebSocket broadcasts `tool_update_complete` with version and status data
221
- 6. Frontend machine transitions to installed/failed via WS event, UI re-renders from machine state
222
-
223
- **Critical Detail:** When updating tools in batch (`/api/tools/update`), the version parameter MUST be included in the database update call. This ensures database persistence across page reloads.
224
-
225
- **Version Detection Sources** (`lib/tool-manager.js`):
226
- - Claude Code: `~/.claude/plugins/{pluginId}/plugin.json`
227
- - OpenCode: `~/.config/opencode/agents/{pluginId}/plugin.json`
228
- - Gemini CLI: `~/.gemini/extensions/{pluginId}/plugin.json`
229
- - Kilo: `~/.config/kilo/agents/{pluginId}/plugin.json`
230
-
231
- **Database Schema** (`database.js`):
232
- - Table: `tool_installations` (toolId, version, status, installed_at, error_message)
233
- - Table: `tool_install_history` (action, status, error_message for audit trail)
234
-
235
- ## Tool Detection System
236
-
237
- **TOOLS** array in `lib/tool-manager.js`: cli (via which + --version) or plugin (via plugin.json). Current: claude, opencode, gemini, kilo, codex, agent-browser (uses `-V`, not `--version`), + plugin tools (gm-cc, gm-oc, gm-gc, gm-kilo, gm-codex).
238
-
239
- **BIN_MAP**: Single constant in `lib/tool-version-check.js` shared by detect + version functions; new CLI tools must be added.
240
-
241
- **FRAMEWORK_PATHS**: Data table (pluginDir/versionFile/parseVersion/optional markerFile). New framework = one table entry.
242
-
243
- **Provisioning**: `autoProvision()` at startup (~10s), `startPeriodicUpdateCheck()` every 6h. Both broadcast tool status via WS.
244
-
245
- ### Tool Installation and Update UI Flow
246
-
247
- When user clicks Install/Update button on a tool:
248
-
249
- 1. **Frontend** (`static/js/tools-manager.js`): Sends INSTALL/UPDATE event to `toolInstallMachineAPI`, sends POST. Machine guards duplicate requests via `isLocked()`.
250
- 2. **Backend** (`server.js`): `tool-install-machine.js` sends INSTALL_START/UPDATE_START, runs async, sends INSTALL_COMPLETE/UPDATE_COMPLETE/FAILED. Broadcasts WS events.
251
- 3. **Frontend WebSocket Handler**: Sends COMPLETE/FAILED/PROGRESS to machine. UI renders from machine state only.
252
-
253
- ## WebSocket Protocol
254
-
255
- Endpoint: `BASE_URL + /sync`. Msgpack binary. Wire: RPC request `{r, m, p}`, reply `{r, d}` or `{r, e}`, broadcast `{type, seq, ...}` batched by `WSOptimizer`. Per-client priority queue: high-priority (streaming_start, message_created, streaming_complete) flush immediately; normal/low batch by latency tier. Rate limit: 100 msg/sec (re-queued if overflow).
256
-
257
- **Legacy messages** (onLegacy): subscribe/unsubscribe/ping/latency_report/terminal_*/pm2_*/set_voice/get_subscriptions
258
-
259
- **RPC methods** (86 total by category): agent (auth/authstat/desc/get/ls/models/search/subagents/update), auth (configs/save), codex (start/status/relay/complete), conv (ls/new/get/upd/del/cancel/chunks/full/steer/inject/search/prune/scripts/run-script), gemini (start/status/relay/complete), git (check/push), msg (send/stream/get/ls), q (ls/upd/del), run (new/stream/get/wait/cancel/search/resume), sess (get/latest/chunks/exec), speech (download/status), thread (new/get/upd/del/search/copy/history/run.stream/run.cancel/run.steer), tools (list), util (home/folders/clone/voices/voice.cache/voice.generate/ws.stats/discover.claude/import.claude)
260
-
261
- ## Steering
262
-
263
- Steering stops the running agent (SIGKILL) and immediately resumes with the new message:
264
-
265
- 1. `conv.steer` RPC (`ws-handlers-conv.js`) — kills active process, marks session interrupted, creates new user message, calls `startExecution()` to resume
266
- 2. Frontend inject button (`#injectBtn`) — when streaming: reads message input, fires `conv.steer`, clears input
267
- 3. `conv.claudeSessionId` on the conversation row ensures the resumed execution picks up `--resume <sessionId>` automatically
268
-
269
- ## Execution State Management
270
-
271
- Three parallel state stores (must stay in sync):
272
- 1. **In-memory maps:** `activeExecutions`, `messageQueues`
273
- 2. **Database:** `conversations.isStreaming`, `sessions.status`
274
- 3. **WebSocket clients:** `streamingConversations` Set on each client
275
-
276
- **`cleanupExecution(conversationId)`** — atomic cleanup function in server.js. Always use this, never inline-delete from maps. Clears `activeExecutions`, sets DB isStreaming=0.
277
-
278
- **Queue drain:** If `processMessageWithStreaming` throws, catch block calls `cleanupExecution` and retries drain after 100ms. Queue never deadlocks.
279
-
280
- ## Message Flow
281
-
282
- User send → check if streaming → (streaming: queue server-side, skip optimistic; else: show optimistic message) → RPC msg.stream → backend checks activeExecutions.has(convId) → (yes: queue, broadcast queue_status; no: execute, return session) → broadcast message_created (non-queued only). Queue renders as yellow blocks. On complete, remove .event-streaming-* DOM blocks.
283
-
284
- ## Conversations Sidebar
285
-
286
- `ConversationManager` in `static/js/conversations.js`:
287
- - Polls `/api/conversations` every 30s
288
- - On poll: if result is non-empty but smaller than cached list, **merges** (keeps cached items not in poll) rather than replacing — prevents transient server responses from dropping conversations
289
- - On empty result with existing cache: keeps existing (server error assumption)
290
- - `render()` uses DOM reconciliation by `data-conv-id` — reuses existing nodes, removes orphans
291
- - `showEmpty()` and `showLoading()` both clear `listEl.innerHTML` — only called when appropriate
292
- - `conversation_deleted` WS event handled in `setupWebSocketListener` — `deleteConversation()` filters array
293
- - `confirmDelete()` calls `deleteConversation()` directly AND server broadcasts `conversation_deleted` — double-call is safe (filter is idempotent)
294
-
295
- ## Base64 Image Rendering in File Read Events
296
-
297
- When an agent reads an image file, the event type may not be `'file_read'`. Three content structures exist:
298
-
299
- **Structure A** (nested): `event.content.source.type === 'base64'`, data at `event.content.source.data`
300
- **Structure B** (flat): `event.content.type === 'base64'`, data at `event.content.data`
301
- **Structure C** (raw string): `event.content` is a base64 string detected by magic-byte prefix
302
-
303
- `renderGeneric` checks for A and B first; if found with `event.path` present, delegates to `renderFileRead`. Without this fallback, non-`file_read` typed image events display as raw text.
304
-
305
- MIME type priority: `event.media_type` → magic-byte detection (PNG/JPEG/WebP/GIF) → `application/octet-stream`.
306
-
307
- ## Voice Model Download
308
-
309
- Models (~470MB: Whisper Base ~280MB + TTS ~190MB) downloaded at startup from GitHub LFS or HuggingFace (fallback). UI: voice tab hidden until ready; progress indicator in header; `model_download_progress` WS broadcast. Cache: `~/.gmgui/models/`.
310
-
311
- ## Performance & Observability
312
-
313
- **Asset serving**: gzip only (no brotli), pre-compressed once, cached in `_assetCache` (etag-keyed). HTML cached, invalidated on hot-reload. **/api/conversations**: single `DISTINCT` query (not N+1). **Chunks**: `getConversationChunksSince()` pushes filter to DB. **Client init**: loadAgents/loadConversations/checkSpeechStatus parallel. **WS**: perMessageDeflate: false (msgpack + zlib blocked event loop).
314
-
315
- **Debug API** (`DEBUG=1`): `/api/debug/machines` snapshots, `/api/debug/state` inspection, `/api/debug/ws-stats` latency. Browser: `window.__debug.getSyncState()` exposes all XState machines.
316
-
317
- PKCE S256 flow vs auth.openai.com. `POST /api/codex-oauth/start` → authUrl. User authenticates → redirect to `/codex-oauth2callback` (local: intercepts localhost:1455/auth/callback; remote: relay page POSTs to `/api/codex-oauth/relay`). Tokens saved to `$CODEX_HOME/auth.json`. WS handlers: codex.start/status/relay/complete.
318
-
319
- ## ACP SDK Integration
320
-
321
- - **@agentclientprotocol/sdk** (`^0.4.1`) added to dependencies
322
- - Full integration (replacing custom WS protocol) is optional/incremental — current WS already gives logical multiplexing via concurrent async handlers
323
-
324
- ## Theme-Aware Rendering
325
-
326
- CSS custom properties for code/thinking blocks live in `static/css/main.css`:
327
- - `--color-bg-code`, `--color-code-text`, `--color-code-border` — light values in `:root`, dark overrides in `html.dark`
328
- - `--color-thinking-bg` — light value in `:root` (`#f5f3ff`), dark override in `html.dark` (`.block-thinking`) set to `#1e1a2e`
329
-
330
- `static/js/streaming-renderer.js` uses `var(--color-bg-code)` etc. in inline styles — no hardcoded `#1e293b`/`#e2e8f0`/`#d1d5db` hex values. Remaining hardcoded hex in the renderer are **intentional semantic colors** (blue links, amber warnings, red errors, purple thinking accents) and must not be replaced with CSS vars.
331
-
332
- `parseAndRenderMarkdown()` in `streaming-renderer.js` handles: `##`/`###` headers, `-`/`*` ul lists, `1.` ol lists, `>` blockquotes, `---` hr, inline bold/italic/code/links via `_mdInline()`. Thinking block content is rendered through this function.
333
-
334
- ## README.md Documentation
335
-
336
- **GitHub Badges and Metrics:**
337
-
338
- The README.md uses shields.io badges with a consistent pattern:
339
- - **Header badges** (lines 7-9): Star count, last commit, latest release — each links to corresponding GitHub page
340
- - **GitHub Stats table** (lines 54-62): Detailed metrics (stars, forks, watchers, issues, activity) — each badge links to its resource page
341
- - **All badges use:** `style=flat-square`, `color=blue`, dynamic data (no hardcoded values)
342
-
343
- **Debug API section** (lines 174-192):
344
- - Documents `DEBUG=1` environment variable for state inspection
345
- - Lists `/api/debug/*` endpoints: machines, state, ws-stats
346
- - Lists browser console `window.__debug` properties with purpose
347
- - Links to CLAUDE.md for complete architecture documentation
348
-
349
- **Approach validated:** Header badges are compact (visual prominence); stats table is detailed (discoverability). Non-redundant, no duplicate metrics, complementary visibility.
350
-
351
- **For future observability improvements:** Use shields.io with the established pattern (flat-square, color=blue, dynamic endpoints). Link badges to corresponding GitHub resource pages. Document in README alongside the badge.
352
-
353
- ## Known Gotchas
354
-
355
- - **`agent-browser --version`** prints help, not version. Use `-V` flag.
356
- - **`all_conversations_deleted`** must be in `BROADCAST_TYPES` set in server.js or it won't fan-out to all clients.
357
- - **`streaming_start` and `message_created`** are high-priority in WSOptimizer — they flush immediately, not batched.
358
- - **Sidebar animation:** `transition: none !important` in index.html CSS — sidebar snaps instantly on toggle by design.
359
- - **Claude Code runs with plugins enabled** — `--dangerously-skip-permissions` was removed to allow gm plugin enforcement.
360
- - **Tool status race on startup:** `autoProvision()` broadcasts `tool_status_update` for already-installed tools so the UI shows correct state before the first manual fetch.
361
- - **Thinking blocks** are transient (not in DB), rendered only via `handleStreamingProgress()` in client.js. The `renderEvent` switch case for `thinking_block` is disabled to prevent double-render.
362
- - **Terminal output** is base64-encoded (`encoding: 'base64'` field on message). Client decodes with `decodeURIComponent(escape(atob(data)))` pattern for multibyte safety.
363
- - **HTML cache** (`_htmlCache`) is only populated when client accepts gzip. In watch mode it's never cached (always fresh).
364
- - **`app.js` and `app-shortcuts.js` script loading:** Both are `<script defer>` tags loaded AFTER `agent-auth.js` in index.html. They depend on `window.wsClient`, `window.conversationManager`, and `window._escHtml` being initialized first. Defer order is guaranteed by source order — adding new defer scripts that depend on these modules requires careful ordering.
365
- - **`window.__debug` registry:** Structured sub-keys via live getters — `machines` (conv/toolInstall/voice/convList/prompt/recording/terminal/ws), `ws` (state/latency/url), `auth`, `perf`, `config`, `renderer`, `conv`. Legacy methods preserved: `getState()`, `getSyncState()`, `getMessageState()`. Uninitialized machines return `'uninitialized'` rather than crashing. All XState v5 machines have no parallel ad-hoc state.
366
- - **`isJsonlBacked` flag:** Only `claude-code` (protocol: `direct`) writes JSONL files. All other agents use `stream-event-handler.js` for broadcasting. `isJsonlBacked = resolvedAgentId === 'claude-code'` — guards in `stream-event-handler.js` prevent double-broadcast for claude-code, and gates ACP streaming for non-JSONL agents.
367
- - **`toolIds` in `server-startup.js` must match `TOOLS` in `tool-manager.js`:** `initializeToolInstallations` runs for each toolId, creating the `tool_installations` row. `tool_install_history` has a FK to `tool_installations(tool_id)`. Any tool omitted from toolIds will cause a FOREIGN KEY constraint failure when the periodic update checker writes history for it.
368
- - **`JsonlWatcher._read(fp)` override:** Captures `this._currentFp` before calling `super._read(fp)`, making the file path available to `_line()` callbacks for project-directory decoding in `_conv()`. JSONL project dirs are encoded (e.g., `-config-workspace-agentgui`) — decoded via `'/' + dirName.slice(1).replace(/-/g, '/')`.
369
- - **`createHttpHandler` uses `getWss: () => wss` (lazy getter):** Passing `wss` directly would crash with TDZ since `wss` is declared after `createHttpHandler` is called. The function form defers access until request time when `wss` is initialized.
370
- - **`_promptPushIfWeOwnRemote` fires after every `streaming_complete`:** `client-streaming4.js` calls `git.check` on the server after each agent turn. If `ownsRemote && (hasChanges || hasUnpushed)`, it auto-sends "Push the changes to the remote repository." to the current conversation. `ownsRemote` is true for non-github remotes, and for github.com remotes only when `GITHUB_USER` env var is set and appears in the URL. Without `GITHUB_USER`, github.com remotes return `ownsRemote=false` (safe default).
371
-
372
- ## Critical Knowledge for Future Sessions
373
-
374
- **Codebase Insight Stale:** `.codeinsight` snapshot is outdated (v1.0.811 claimed server.js=3407L, db-queries.js=1412L, but actual as of 2026-04-17: server.js=201L, db-queries.js=94L). Heavy refactoring already done. Before acting on insight "issues" (SQL injection claims, hardcoded secrets, large files), always verify with grep/read against current state. Most reported issues are false positives or stale.
375
-
376
- **Test Harness Pattern:** test.js prefers `bun:sqlite`, falls back to `better-sqlite3` via createRequire (so it runs under both `bun test.js` and `node test.js` provided `bun install` / `npm install` ran). Call `initSchema()` → `migrateConversationColumns()` → `migrateACPSchema()` in order (conversations table needs agentType column from second migration). `createQueries` signature: `(db, prep, generateId)` where `prep=(sql)=>db.prepare(sql)`. Silence console during schema init to keep test output clean. CI runs `bun test.js` via `.github/workflows/test.yml` on every push/PR to main.
377
-
378
- **CI Auto-Rewrites History:** Every push to main triggers Auto-Declaudeify workflow that filters Claude coauthor commits and force-pushes filtered history. After a push, `git fetch origin` is needed — local SHA drifts from origin SHA.
379
-
380
- **Debug Endpoints Scattered:** Routes at `/api/debug`, `/api/debug/machines`, `/api/debug/state`, `/api/ws-stats`, `/api/debug/ws-stats` (alias). `routes-debug.js` wires all five + backup/restore. DEBUG API in browser: `window.__debug.getSyncState()`.
381
-
382
- **Plugin Files NOT Orphans:** lib/plugins/* are dynamically loaded via `lib/plugin-loader.js` (import with file:// URL + cache-busting `?v=` timestamp). Only truly dead plugin was `lib/plugins/git-plugin.js`. Static scan showing orphans is misleading.
1
+ @AGENTS.md
@@ -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/')) {
@@ -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)) {