diff --git a/docs/plans/state.json b/docs/plans/state.json index dafe55d..bec4543 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -62,7 +62,7 @@ "gateway-services-dashboard": { "status": "completed", "date": "2026-02-14", - "summary": "Added a system.services RPC that returns a unified list of channel adapters plus automation subsystems (configured vs connected), and updated the web UI dashboard to render these as a Services grid.", + "summary": "Added a system.services RPC that returns a unified list of channel adapters plus automation + tooling subsystems (configured vs connected), including audio transcription and web search, and updated the web UI dashboard/settings to render these as a Services grid.", "files_modified": [ "src/gateway/handlers/services.ts", "src/gateway/handlers/services.test.ts", @@ -1916,7 +1916,7 @@ }, "overall_progress": { - "total_test_count": 1631, + "total_test_count": 1632, "all_tests_passing": true, "p0_completion": "3/3 (100%)", "p1_completion": "4/4 (100%)", diff --git a/src/gateway/handlers/services.test.ts b/src/gateway/handlers/services.test.ts index cf2b3a1..9bfffc3 100644 --- a/src/gateway/handlers/services.test.ts +++ b/src/gateway/handlers/services.test.ts @@ -38,6 +38,8 @@ describe('discoverServices', () => { expect.objectContaining({ name: 'telegram', status: 'not_configured' }), expect.objectContaining({ name: 'cron', status: 'not_configured' }), expect.objectContaining({ name: 'mcp', status: 'not_configured' }), + expect.objectContaining({ name: 'web_search', status: 'configured' }), + expect.objectContaining({ name: 'audio_transcription', status: 'not_configured' }), ])); }); @@ -83,5 +85,20 @@ describe('discoverServices', () => { expect(services.find(s => s.name === 'cron')?.itemCount).toBe(1); expect(services.find(s => s.name === 'mcp')?.metadata).toEqual({ serverCount: 1 }); }); -}); + it('marks audio transcription as configured and includes endpoint metadata', () => { + const cfg = makeBaseConfig(); + (cfg as any).audio = { enabled: true, provider: { type: 'custom', endpoint: 'http://localhost:18801/v1/audio/transcriptions', model: 'whisper-1' } }; + + const reg = new ChannelRegistry(); + const services = discoverServices(cfg, reg); + + const audio = services.find(s => s.name === 'audio_transcription'); + expect(audio?.status).toBe('configured'); + expect(audio?.metadata).toMatchObject({ + provider: 'custom', + endpoint: 'http://localhost:18801/v1/audio/transcriptions', + model: 'whisper-1', + }); + }); +}); diff --git a/src/gateway/handlers/services.ts b/src/gateway/handlers/services.ts index ee7e9e4..fbc8f17 100644 --- a/src/gateway/handlers/services.ts +++ b/src/gateway/handlers/services.ts @@ -72,6 +72,33 @@ export function discoverServices( services.push({ name, type: 'channel', status: 'disconnected', description }); } + // Web search (tooling subsystem) + services.push({ + name: 'web_search', + type: 'tool', + status: 'configured', + description: 'Web search provider', + metadata: { + provider: config.web_search?.provider ?? 'brave', + endpoint: config.web_search?.endpoint, + max_results: config.web_search?.max_results, + }, + }); + + // Audio transcription (tooling subsystem) + const audioEnabled = Boolean(config.audio?.enabled); + services.push({ + name: 'audio_transcription', + type: 'tool', + status: audioEnabled ? 'configured' : 'not_configured', + description: 'Audio transcription', + metadata: { + provider: config.audio?.provider?.type, + endpoint: config.audio?.provider?.endpoint, + model: config.audio?.provider?.model, + }, + }); + const automation = config.automation; const automationConfigs: Array<{ enabled: boolean; name: string; description: string; itemCount?: number }> = [