@lobu/gateway 3.0.5 → 3.0.6

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 (175) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/agent-config-routes.test.ts +254 -0
  3. package/src/__tests__/agent-history-routes.test.ts +72 -0
  4. package/src/__tests__/agent-routes.test.ts +68 -0
  5. package/src/__tests__/agent-schedules-routes.test.ts +59 -0
  6. package/src/__tests__/agent-settings-store.test.ts +323 -0
  7. package/src/__tests__/chat-instance-manager-slack.test.ts +204 -0
  8. package/src/__tests__/chat-response-bridge.test.ts +131 -0
  9. package/src/__tests__/config-memory-plugins.test.ts +92 -0
  10. package/src/__tests__/config-request-store.test.ts +127 -0
  11. package/src/__tests__/connection-routes.test.ts +144 -0
  12. package/src/__tests__/core-services-store-selection.test.ts +92 -0
  13. package/src/__tests__/docker-deployment.test.ts +1211 -0
  14. package/src/__tests__/embedded-deployment.test.ts +342 -0
  15. package/src/__tests__/grant-store.test.ts +148 -0
  16. package/src/__tests__/http-proxy.test.ts +281 -0
  17. package/src/__tests__/instruction-service.test.ts +37 -0
  18. package/src/__tests__/link-buttons.test.ts +112 -0
  19. package/src/__tests__/lobu.test.ts +32 -0
  20. package/src/__tests__/mcp-config-service.test.ts +347 -0
  21. package/src/__tests__/mcp-proxy.test.ts +696 -0
  22. package/src/__tests__/message-handler-bridge.test.ts +17 -0
  23. package/src/__tests__/model-selection.test.ts +172 -0
  24. package/src/__tests__/oauth-templates.test.ts +39 -0
  25. package/src/__tests__/platform-adapter-slack-send.test.ts +114 -0
  26. package/src/__tests__/platform-helpers-model-resolution.test.ts +253 -0
  27. package/src/__tests__/provider-inheritance.test.ts +212 -0
  28. package/src/__tests__/routes/cli-auth.test.ts +337 -0
  29. package/src/__tests__/routes/interactions.test.ts +121 -0
  30. package/src/__tests__/secret-proxy.test.ts +85 -0
  31. package/src/__tests__/session-manager.test.ts +572 -0
  32. package/src/__tests__/setup.ts +133 -0
  33. package/src/__tests__/skill-and-mcp-registry.test.ts +203 -0
  34. package/src/__tests__/slack-routes.test.ts +161 -0
  35. package/src/__tests__/system-config-resolver.test.ts +75 -0
  36. package/src/__tests__/system-message-limiter.test.ts +89 -0
  37. package/src/__tests__/system-skills-service.test.ts +362 -0
  38. package/src/__tests__/transcription-service.test.ts +222 -0
  39. package/src/__tests__/utils/rate-limiter.test.ts +102 -0
  40. package/src/__tests__/worker-connection-manager.test.ts +497 -0
  41. package/src/__tests__/worker-job-router.test.ts +722 -0
  42. package/src/api/index.ts +1 -0
  43. package/src/api/platform.ts +292 -0
  44. package/src/api/response-renderer.ts +157 -0
  45. package/src/auth/agent-metadata-store.ts +168 -0
  46. package/src/auth/api-auth-middleware.ts +69 -0
  47. package/src/auth/api-key-provider-module.ts +213 -0
  48. package/src/auth/base-provider-module.ts +201 -0
  49. package/src/auth/chatgpt/chatgpt-oauth-module.ts +185 -0
  50. package/src/auth/chatgpt/device-code-client.ts +218 -0
  51. package/src/auth/chatgpt/index.ts +1 -0
  52. package/src/auth/claude/oauth-module.ts +280 -0
  53. package/src/auth/cli/token-service.ts +249 -0
  54. package/src/auth/external/client.ts +560 -0
  55. package/src/auth/external/device-code-client.ts +225 -0
  56. package/src/auth/mcp/config-service.ts +392 -0
  57. package/src/auth/mcp/proxy.ts +1088 -0
  58. package/src/auth/mcp/string-substitution.ts +17 -0
  59. package/src/auth/mcp/tool-cache.ts +90 -0
  60. package/src/auth/oauth/base-client.ts +267 -0
  61. package/src/auth/oauth/client.ts +153 -0
  62. package/src/auth/oauth/credentials.ts +7 -0
  63. package/src/auth/oauth/providers.ts +69 -0
  64. package/src/auth/oauth/state-store.ts +150 -0
  65. package/src/auth/oauth-templates.ts +179 -0
  66. package/src/auth/provider-catalog.ts +220 -0
  67. package/src/auth/provider-model-options.ts +41 -0
  68. package/src/auth/settings/agent-settings-store.ts +565 -0
  69. package/src/auth/settings/auth-profiles-manager.ts +216 -0
  70. package/src/auth/settings/index.ts +12 -0
  71. package/src/auth/settings/model-preference-store.ts +52 -0
  72. package/src/auth/settings/model-selection.ts +135 -0
  73. package/src/auth/settings/resolved-settings-view.ts +298 -0
  74. package/src/auth/settings/template-utils.ts +44 -0
  75. package/src/auth/settings/token-service.ts +88 -0
  76. package/src/auth/system-env-store.ts +98 -0
  77. package/src/auth/user-agents-store.ts +68 -0
  78. package/src/channels/binding-service.ts +214 -0
  79. package/src/channels/index.ts +4 -0
  80. package/src/cli/gateway.ts +1304 -0
  81. package/src/cli/index.ts +74 -0
  82. package/src/commands/built-in-commands.ts +80 -0
  83. package/src/commands/command-dispatcher.ts +94 -0
  84. package/src/commands/command-reply-adapters.ts +27 -0
  85. package/src/config/file-loader.ts +618 -0
  86. package/src/config/index.ts +588 -0
  87. package/src/config/network-allowlist.ts +71 -0
  88. package/src/connections/chat-instance-manager.ts +1284 -0
  89. package/src/connections/chat-response-bridge.ts +618 -0
  90. package/src/connections/index.ts +7 -0
  91. package/src/connections/interaction-bridge.ts +831 -0
  92. package/src/connections/message-handler-bridge.ts +415 -0
  93. package/src/connections/platform-auth-methods.ts +15 -0
  94. package/src/connections/types.ts +84 -0
  95. package/src/gateway/connection-manager.ts +291 -0
  96. package/src/gateway/index.ts +700 -0
  97. package/src/gateway/job-router.ts +201 -0
  98. package/src/gateway-main.ts +200 -0
  99. package/src/index.ts +41 -0
  100. package/src/infrastructure/queue/index.ts +12 -0
  101. package/src/infrastructure/queue/queue-producer.ts +148 -0
  102. package/src/infrastructure/queue/redis-queue.ts +361 -0
  103. package/src/infrastructure/queue/types.ts +133 -0
  104. package/src/infrastructure/redis/system-message-limiter.ts +94 -0
  105. package/src/interactions/config-request-store.ts +198 -0
  106. package/src/interactions.ts +363 -0
  107. package/src/lobu.ts +311 -0
  108. package/src/metrics/prometheus.ts +159 -0
  109. package/src/modules/module-system.ts +179 -0
  110. package/src/orchestration/base-deployment-manager.ts +900 -0
  111. package/src/orchestration/deployment-utils.ts +98 -0
  112. package/src/orchestration/impl/docker-deployment.ts +620 -0
  113. package/src/orchestration/impl/embedded-deployment.ts +268 -0
  114. package/src/orchestration/impl/index.ts +8 -0
  115. package/src/orchestration/impl/k8s/deployment.ts +1061 -0
  116. package/src/orchestration/impl/k8s/helpers.ts +610 -0
  117. package/src/orchestration/impl/k8s/index.ts +1 -0
  118. package/src/orchestration/index.ts +333 -0
  119. package/src/orchestration/message-consumer.ts +584 -0
  120. package/src/orchestration/scheduled-wakeup.ts +704 -0
  121. package/src/permissions/approval-policy.ts +36 -0
  122. package/src/permissions/grant-store.ts +219 -0
  123. package/src/platform/file-handler.ts +66 -0
  124. package/src/platform/link-buttons.ts +57 -0
  125. package/src/platform/renderer-utils.ts +44 -0
  126. package/src/platform/response-renderer.ts +84 -0
  127. package/src/platform/unified-thread-consumer.ts +187 -0
  128. package/src/platform.ts +318 -0
  129. package/src/proxy/http-proxy.ts +752 -0
  130. package/src/proxy/proxy-manager.ts +81 -0
  131. package/src/proxy/secret-proxy.ts +402 -0
  132. package/src/proxy/token-refresh-job.ts +143 -0
  133. package/src/routes/internal/audio.ts +141 -0
  134. package/src/routes/internal/device-auth.ts +566 -0
  135. package/src/routes/internal/files.ts +226 -0
  136. package/src/routes/internal/history.ts +69 -0
  137. package/src/routes/internal/images.ts +127 -0
  138. package/src/routes/internal/interactions.ts +84 -0
  139. package/src/routes/internal/middleware.ts +23 -0
  140. package/src/routes/internal/schedule.ts +226 -0
  141. package/src/routes/internal/types.ts +22 -0
  142. package/src/routes/openapi-auto.ts +239 -0
  143. package/src/routes/public/agent-access.ts +23 -0
  144. package/src/routes/public/agent-config.ts +675 -0
  145. package/src/routes/public/agent-history.ts +422 -0
  146. package/src/routes/public/agent-schedules.ts +296 -0
  147. package/src/routes/public/agent.ts +1086 -0
  148. package/src/routes/public/agents.ts +373 -0
  149. package/src/routes/public/channels.ts +191 -0
  150. package/src/routes/public/cli-auth.ts +883 -0
  151. package/src/routes/public/connections.ts +574 -0
  152. package/src/routes/public/landing.ts +16 -0
  153. package/src/routes/public/oauth.ts +147 -0
  154. package/src/routes/public/settings-auth.ts +104 -0
  155. package/src/routes/public/slack.ts +173 -0
  156. package/src/routes/shared/agent-ownership.ts +101 -0
  157. package/src/routes/shared/token-verifier.ts +34 -0
  158. package/src/services/core-services.ts +1053 -0
  159. package/src/services/image-generation-service.ts +257 -0
  160. package/src/services/instruction-service.ts +318 -0
  161. package/src/services/mcp-registry.ts +94 -0
  162. package/src/services/platform-helpers.ts +287 -0
  163. package/src/services/session-manager.ts +262 -0
  164. package/src/services/settings-resolver.ts +74 -0
  165. package/src/services/system-config-resolver.ts +90 -0
  166. package/src/services/system-skills-service.ts +229 -0
  167. package/src/services/transcription-service.ts +684 -0
  168. package/src/session.ts +110 -0
  169. package/src/spaces/index.ts +1 -0
  170. package/src/spaces/space-resolver.ts +17 -0
  171. package/src/stores/in-memory-agent-store.ts +403 -0
  172. package/src/stores/redis-agent-store.ts +279 -0
  173. package/src/utils/public-url.ts +44 -0
  174. package/src/utils/rate-limiter.ts +94 -0
  175. package/tsconfig.json +33 -0
@@ -0,0 +1,1053 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import {
4
+ type AgentAccessStore,
5
+ type AgentConfigStore,
6
+ type AgentConnectionStore,
7
+ CommandRegistry,
8
+ createLogger,
9
+ moduleRegistry,
10
+ type SystemSkillEntry,
11
+ } from "@lobu/core";
12
+ import { AgentMetadataStore } from "../auth/agent-metadata-store";
13
+ import { ApiKeyProviderModule } from "../auth/api-key-provider-module";
14
+ import { ChatGPTOAuthModule } from "../auth/chatgpt";
15
+ import { ClaudeOAuthModule } from "../auth/claude/oauth-module";
16
+ import { ExternalAuthClient } from "../auth/external/client";
17
+ import { McpConfigService } from "../auth/mcp/config-service";
18
+ import { McpProxy } from "../auth/mcp/proxy";
19
+ import { McpToolCache } from "../auth/mcp/tool-cache";
20
+ import { OAuthClient } from "../auth/oauth/client";
21
+ import { CLAUDE_PROVIDER } from "../auth/oauth/providers";
22
+ import {
23
+ createOAuthStateStore,
24
+ type ProviderOAuthStateStore,
25
+ } from "../auth/oauth/state-store";
26
+ import { ProviderCatalogService } from "../auth/provider-catalog";
27
+ import { AgentSettingsStore, AuthProfilesManager } from "../auth/settings";
28
+ import { ModelPreferenceStore } from "../auth/settings/model-preference-store";
29
+ import { UserAgentsStore } from "../auth/user-agents-store";
30
+ import { ChannelBindingService } from "../channels";
31
+ import { registerBuiltInCommands } from "../commands/built-in-commands";
32
+ import type { AgentConfig, GatewayConfig } from "../config";
33
+ import {
34
+ type FileLoadedAgent,
35
+ loadAgentConfigFromFiles,
36
+ } from "../config/file-loader";
37
+ import { WorkerGateway } from "../gateway";
38
+ import type { IMessageQueue } from "../infrastructure/queue";
39
+ import {
40
+ QueueProducer,
41
+ RedisQueue,
42
+ type RedisQueueConfig,
43
+ } from "../infrastructure/queue";
44
+ import { InteractionService } from "../interactions";
45
+ import { getModelProviderModules } from "../modules/module-system";
46
+ import {
47
+ ScheduledWakeupService,
48
+ setScheduledWakeupService,
49
+ } from "../orchestration/scheduled-wakeup";
50
+ import { GrantStore } from "../permissions/grant-store";
51
+ import { SecretProxy } from "../proxy/secret-proxy";
52
+ import { TokenRefreshJob } from "../proxy/token-refresh-job";
53
+ import { InMemoryAgentStore } from "../stores/in-memory-agent-store";
54
+ import {
55
+ RedisAgentAccessStore,
56
+ RedisAgentConfigStore,
57
+ RedisAgentConnectionStore,
58
+ } from "../stores/redis-agent-store";
59
+ import { ImageGenerationService } from "./image-generation-service";
60
+ import { InstructionService } from "./instruction-service";
61
+ import { RedisSessionStore, SessionManager } from "./session-manager";
62
+ import { SettingsResolver } from "./settings-resolver";
63
+ import { SystemConfigResolver } from "./system-config-resolver";
64
+ import { SystemSkillsService } from "./system-skills-service";
65
+ import { TranscriptionService } from "./transcription-service";
66
+
67
+ const logger = createLogger("core-services");
68
+
69
+ /**
70
+ * Core Services - Centralized service initialization and lifecycle management
71
+ */
72
+ export class CoreServices {
73
+ // ============================================================================
74
+ // Queue Services
75
+ // ============================================================================
76
+ private queue?: IMessageQueue;
77
+ private queueProducer?: QueueProducer;
78
+
79
+ // ============================================================================
80
+ // Session Services
81
+ // ============================================================================
82
+ private sessionManager?: SessionManager;
83
+ private instructionService?: InstructionService;
84
+ private interactionService?: InteractionService;
85
+
86
+ // ============================================================================
87
+ // Auth & Provider Services
88
+ // ============================================================================
89
+ private authProfilesManager?: AuthProfilesManager;
90
+ private modelPreferenceStore?: ModelPreferenceStore;
91
+ private oauthStateStore?: ProviderOAuthStateStore;
92
+ private secretProxy?: SecretProxy;
93
+ private tokenRefreshJob?: TokenRefreshJob;
94
+
95
+ // ============================================================================
96
+ // MCP Services
97
+ // ============================================================================
98
+ private mcpConfigService?: McpConfigService;
99
+ private mcpProxy?: McpProxy;
100
+
101
+ // ============================================================================
102
+ // Permissions
103
+ // ============================================================================
104
+ private grantStore?: GrantStore;
105
+
106
+ // ============================================================================
107
+ // System Skills Service
108
+ // ============================================================================
109
+ private systemSkillsService?: SystemSkillsService;
110
+ private systemConfigResolver?: SystemConfigResolver;
111
+
112
+ // ============================================================================
113
+ // Worker Gateway
114
+ // ============================================================================
115
+ private workerGateway?: WorkerGateway;
116
+
117
+ // ============================================================================
118
+ // Agent Configuration Services
119
+ // ============================================================================
120
+ private agentSettingsStore?: AgentSettingsStore;
121
+ private channelBindingService?: ChannelBindingService;
122
+ private transcriptionService?: TranscriptionService;
123
+ private imageGenerationService?: ImageGenerationService;
124
+ private userAgentsStore?: UserAgentsStore;
125
+ private agentMetadataStore?: AgentMetadataStore;
126
+
127
+ // ============================================================================
128
+ // External OAuth
129
+ // ============================================================================
130
+ private externalAuthClient?: ExternalAuthClient;
131
+
132
+ // ============================================================================
133
+ // Provider Catalog
134
+ // ============================================================================
135
+ private providerCatalogService?: ProviderCatalogService;
136
+
137
+ // ============================================================================
138
+ // Command Registry
139
+ // ============================================================================
140
+ private commandRegistry?: CommandRegistry;
141
+
142
+ // ============================================================================
143
+ // Scheduled Wakeup Service
144
+ // ============================================================================
145
+ private scheduledWakeupService?: ScheduledWakeupService;
146
+
147
+ // ============================================================================
148
+ // Agent Sub-Stores (injectable — host can provide its own implementations)
149
+ // ============================================================================
150
+ private configStore?: AgentConfigStore;
151
+ private connectionStore?: AgentConnectionStore;
152
+ private accessStore?: AgentAccessStore;
153
+ private settingsResolver?: SettingsResolver;
154
+
155
+ // File-first architecture state
156
+ private fileLoadedAgents: FileLoadedAgent[] = [];
157
+ private projectPath: string | null = null;
158
+ private configAgents: AgentConfig[] = [];
159
+
160
+ // Options stored for deferred initialization
161
+ private options?: {
162
+ configStore?: AgentConfigStore;
163
+ connectionStore?: AgentConnectionStore;
164
+ accessStore?: AgentAccessStore;
165
+ systemSkills?: SystemSkillEntry[];
166
+ };
167
+
168
+ constructor(
169
+ private readonly config: GatewayConfig,
170
+ options?: {
171
+ configStore?: AgentConfigStore;
172
+ connectionStore?: AgentConnectionStore;
173
+ accessStore?: AgentAccessStore;
174
+ systemSkills?: SystemSkillEntry[];
175
+ }
176
+ ) {
177
+ this.options = options;
178
+ if (options?.configStore) this.configStore = options.configStore;
179
+ if (options?.connectionStore)
180
+ this.connectionStore = options.connectionStore;
181
+ if (options?.accessStore) this.accessStore = options.accessStore;
182
+ }
183
+
184
+ getConfigStore(): AgentConfigStore | undefined {
185
+ return this.configStore;
186
+ }
187
+
188
+ getConnectionStore(): AgentConnectionStore | undefined {
189
+ return this.connectionStore;
190
+ }
191
+
192
+ getAccessStore(): AgentAccessStore | undefined {
193
+ return this.accessStore;
194
+ }
195
+
196
+ getSettingsResolver(): SettingsResolver | undefined {
197
+ return this.settingsResolver;
198
+ }
199
+
200
+ /**
201
+ * Initialize all core services in dependency order
202
+ */
203
+ async initialize(): Promise<void> {
204
+ logger.debug("Initializing core services...");
205
+
206
+ // 1. Queue (foundation for everything else)
207
+ await this.initializeQueue();
208
+ logger.debug("Queue initialized");
209
+
210
+ // 2. Session management
211
+ await this.initializeSessionServices();
212
+ logger.debug("Session services initialized");
213
+
214
+ // 3. Auth & provider services
215
+ await this.initializeClaudeServices();
216
+ logger.debug("Auth & provider services initialized");
217
+
218
+ // 4. MCP ecosystem (depends on queue and Claude services)
219
+ await this.initializeMcpServices();
220
+ logger.debug("MCP services initialized");
221
+
222
+ // 5. Queue producer (depends on queue being ready)
223
+ await this.initializeQueueProducer();
224
+ logger.debug("Queue producer initialized");
225
+
226
+ // 6. Scheduled wakeup service (depends on queue)
227
+ await this.initializeScheduledWakeupService();
228
+ logger.debug("Scheduled wakeup service initialized");
229
+
230
+ // 7. Command registry (depends on agent settings store)
231
+ this.initializeCommandRegistry();
232
+ logger.debug("Command registry initialized");
233
+
234
+ logger.info("Core services initialized successfully");
235
+ }
236
+
237
+ // ============================================================================
238
+ // 1. Queue Services Initialization
239
+ // ============================================================================
240
+
241
+ private async initializeQueue(): Promise<void> {
242
+ if (!this.config.queues?.connectionString) {
243
+ throw new Error("Queue connection string is required");
244
+ }
245
+
246
+ const url = new URL(this.config.queues.connectionString);
247
+ if (url.protocol !== "redis:") {
248
+ throw new Error(
249
+ `Unsupported queue protocol: ${url.protocol}. Only redis:// is supported.`
250
+ );
251
+ }
252
+
253
+ const config: RedisQueueConfig = {
254
+ host: url.hostname,
255
+ port: Number.parseInt(url.port, 10) || 6379,
256
+ password: url.password || undefined,
257
+ db: url.pathname ? Number.parseInt(url.pathname.slice(1), 10) : 0,
258
+ maxRetriesPerRequest: 3,
259
+ };
260
+
261
+ this.queue = new RedisQueue(config);
262
+ await this.queue.start();
263
+ logger.debug("Queue connection established");
264
+ }
265
+
266
+ private async initializeQueueProducer(): Promise<void> {
267
+ if (!this.queue) {
268
+ throw new Error("Queue must be initialized before queue producer");
269
+ }
270
+
271
+ this.queueProducer = new QueueProducer(this.queue);
272
+ await this.queueProducer.start();
273
+ logger.debug("Queue producer initialized");
274
+ }
275
+
276
+ // ============================================================================
277
+ // Scheduled Wakeup Service Initialization
278
+ // ============================================================================
279
+
280
+ private async initializeScheduledWakeupService(): Promise<void> {
281
+ if (!this.queue) {
282
+ throw new Error(
283
+ "Queue must be initialized before scheduled wakeup service"
284
+ );
285
+ }
286
+
287
+ this.scheduledWakeupService = new ScheduledWakeupService(this.queue);
288
+ await this.scheduledWakeupService.start();
289
+ // Set global reference for BaseDeploymentManager cleanup
290
+ setScheduledWakeupService(this.scheduledWakeupService);
291
+ logger.debug("Scheduled wakeup service initialized");
292
+ }
293
+
294
+ // ============================================================================
295
+ // 2. Session Services Initialization
296
+ // ============================================================================
297
+
298
+ private async initializeSessionServices(): Promise<void> {
299
+ if (!this.queue) {
300
+ throw new Error("Queue must be initialized before session services");
301
+ }
302
+
303
+ const redisClient = this.queue.getRedisClient();
304
+
305
+ const sessionStore = new RedisSessionStore(this.queue);
306
+ this.sessionManager = new SessionManager(sessionStore);
307
+ logger.debug("Session manager initialized");
308
+
309
+ this.interactionService = new InteractionService();
310
+ logger.debug("Interaction service initialized");
311
+
312
+ // Initialize grant store for unified permissions
313
+ this.grantStore = new GrantStore(redisClient);
314
+ logger.debug("Grant store initialized");
315
+
316
+ // Initialize agent configuration stores
317
+ this.agentSettingsStore = new AgentSettingsStore(redisClient);
318
+ this.channelBindingService = new ChannelBindingService(redisClient);
319
+ this.userAgentsStore = new UserAgentsStore(redisClient);
320
+ this.agentMetadataStore = new AgentMetadataStore(redisClient);
321
+ logger.debug(
322
+ "Agent settings, channel binding, user agents & metadata stores initialized"
323
+ );
324
+
325
+ // Initialize agent sub-stores
326
+ if (!this.configStore || !this.connectionStore || !this.accessStore) {
327
+ if (this.config.agents?.length) {
328
+ const inMemoryStore = new InMemoryAgentStore();
329
+ if (!this.configStore) this.configStore = inMemoryStore;
330
+ if (!this.connectionStore) this.connectionStore = inMemoryStore;
331
+ if (!this.accessStore) this.accessStore = inMemoryStore;
332
+
333
+ await this.populateStoreFromAgentConfigs(
334
+ inMemoryStore,
335
+ this.config.agents
336
+ );
337
+ logger.debug(
338
+ `Agent sub-stores initialized (in-memory, ${this.config.agents.length} agent(s) from config)`
339
+ );
340
+ } else {
341
+ // Check if lobu.toml exists (file-first dev mode)
342
+ const { existsSync } = await import("node:fs");
343
+ const { resolve } = await import("node:path");
344
+ const workspaceRoot = process.env.LOBU_WORKSPACE_ROOT?.trim();
345
+ const candidatePaths = [
346
+ ...(workspaceRoot ? [resolve(workspaceRoot, "lobu.toml")] : []),
347
+ resolve(process.cwd(), "lobu.toml"),
348
+ resolve("/app/lobu.toml"),
349
+ ];
350
+ const tomlPath = candidatePaths.find((p) => existsSync(p));
351
+
352
+ if (tomlPath) {
353
+ const inMemoryStore = new InMemoryAgentStore();
354
+ if (!this.configStore) this.configStore = inMemoryStore;
355
+ if (!this.connectionStore) this.connectionStore = inMemoryStore;
356
+ if (!this.accessStore) this.accessStore = inMemoryStore;
357
+
358
+ // File-first dev mode: use InMemoryAgentStore populated from files
359
+ this.projectPath = resolve(tomlPath, "..");
360
+
361
+ // Load agents from files and populate store
362
+ this.fileLoadedAgents = await loadAgentConfigFromFiles(
363
+ this.projectPath
364
+ );
365
+ await this.populateStoreFromFiles(
366
+ inMemoryStore,
367
+ this.fileLoadedAgents
368
+ );
369
+ logger.debug(
370
+ `Agent sub-stores initialized (in-memory, ${this.fileLoadedAgents.length} agent(s) from files)`
371
+ );
372
+ } else {
373
+ if (!this.configStore) {
374
+ this.configStore = new RedisAgentConfigStore(
375
+ this.agentSettingsStore,
376
+ this.agentMetadataStore
377
+ );
378
+ }
379
+ if (!this.connectionStore) {
380
+ this.connectionStore = new RedisAgentConnectionStore(
381
+ redisClient,
382
+ this.channelBindingService
383
+ );
384
+ }
385
+ if (!this.accessStore) {
386
+ this.accessStore = new RedisAgentAccessStore(
387
+ this.grantStore,
388
+ this.userAgentsStore
389
+ );
390
+ }
391
+ logger.debug("Agent sub-stores initialized (Redis-backed defaults)");
392
+ }
393
+ }
394
+ } else {
395
+ logger.debug("Using host-provided agent sub-stores (embedded mode)");
396
+ }
397
+
398
+ // Create settings resolver (template fallback logic)
399
+ this.settingsResolver = new SettingsResolver(
400
+ this.configStore,
401
+ this.connectionStore
402
+ );
403
+
404
+ // Initialize external OAuth client if configured
405
+ this.externalAuthClient =
406
+ ExternalAuthClient.fromEnv(this.config.mcp.publicGatewayUrl, {
407
+ get: (key) => redisClient.get(key),
408
+ set: (key, value, ttlSeconds) =>
409
+ redisClient.setex(key, ttlSeconds, value),
410
+ }) ?? undefined;
411
+ if (this.externalAuthClient) {
412
+ logger.debug("External OAuth client initialized");
413
+ }
414
+ }
415
+
416
+ // ============================================================================
417
+ // 3. Auth & Provider Services Initialization
418
+ // ============================================================================
419
+
420
+ private async initializeClaudeServices(): Promise<void> {
421
+ if (!this.queue) {
422
+ throw new Error("Queue must be initialized before auth services");
423
+ }
424
+
425
+ const redisClient = this.queue.getRedisClient();
426
+
427
+ if (!this.agentSettingsStore) {
428
+ throw new Error(
429
+ "Agent settings store must be initialized before auth services"
430
+ );
431
+ }
432
+
433
+ if (this.fileLoadedAgents.length > 0) {
434
+ for (const agent of this.fileLoadedAgents) {
435
+ await this.syncAgentSettingsToRuntimeStore(
436
+ agent.agentId,
437
+ agent.settings
438
+ );
439
+ }
440
+ logger.debug(
441
+ `Synced settings for ${this.fileLoadedAgents.length} file-loaded agent(s)`
442
+ );
443
+ }
444
+
445
+ if (this.configAgents.length > 0) {
446
+ for (const agent of this.configAgents) {
447
+ await this.syncAgentSettingsToRuntimeStore(
448
+ agent.id,
449
+ this.buildSettingsFromAgentConfig(agent)
450
+ );
451
+ }
452
+ logger.debug(
453
+ `Synced settings for ${this.configAgents.length} config agent(s)`
454
+ );
455
+ }
456
+
457
+ // Initialize auth profile and preference stores
458
+ this.authProfilesManager = new AuthProfilesManager(this.agentSettingsStore);
459
+ this.transcriptionService = new TranscriptionService(
460
+ this.authProfilesManager
461
+ );
462
+ this.imageGenerationService = new ImageGenerationService(
463
+ this.authProfilesManager
464
+ );
465
+ this.modelPreferenceStore = new ModelPreferenceStore(redisClient, "claude");
466
+
467
+ // Seed provider credentials from file-loaded agents
468
+ if (this.authProfilesManager && this.fileLoadedAgents.length > 0) {
469
+ for (const agent of this.fileLoadedAgents) {
470
+ for (const cred of agent.credentials) {
471
+ await this.authProfilesManager.upsertProfile({
472
+ agentId: agent.agentId,
473
+ provider: cred.provider,
474
+ credential: cred.key,
475
+ authType: "api-key",
476
+ label: `${cred.provider} (from lobu.toml)`,
477
+ makePrimary: true,
478
+ });
479
+ }
480
+ }
481
+ logger.debug(
482
+ `Seeded credentials for ${this.fileLoadedAgents.length} file-loaded agent(s)`
483
+ );
484
+ }
485
+
486
+ // Seed provider credentials from config agents (embedded mode)
487
+ if (this.authProfilesManager && this.configAgents.length > 0) {
488
+ for (const agent of this.configAgents) {
489
+ for (const provider of agent.providers || []) {
490
+ if (!provider.key) continue;
491
+ await this.authProfilesManager.upsertProfile({
492
+ agentId: agent.id,
493
+ provider: provider.id,
494
+ credential: provider.key,
495
+ authType: "api-key",
496
+ label: `${provider.id} (from config)`,
497
+ makePrimary: true,
498
+ });
499
+ }
500
+ }
501
+ logger.debug(
502
+ `Seeded credentials for ${this.configAgents.length} config agent(s)`
503
+ );
504
+ }
505
+
506
+ logger.debug(
507
+ "Auth profile, model preference, transcription, and image generation services initialized"
508
+ );
509
+
510
+ // Initialize secret injection proxy (will be finalized after provider modules are registered)
511
+ this.secretProxy = new SecretProxy({
512
+ defaultUpstreamUrl:
513
+ this.config.anthropicProxy.anthropicBaseUrl ||
514
+ "https://api.anthropic.com",
515
+ });
516
+ this.secretProxy.initialize(redisClient);
517
+ logger.debug(
518
+ `Secret proxy initialized (upstream: ${this.config.anthropicProxy.anthropicBaseUrl || "https://api.anthropic.com"})`
519
+ );
520
+
521
+ // Start background token refresh job
522
+ if (!this.authProfilesManager) {
523
+ throw new Error(
524
+ "Auth profiles manager must be initialized before token refresh job"
525
+ );
526
+ }
527
+ this.tokenRefreshJob = new TokenRefreshJob(
528
+ this.authProfilesManager,
529
+ redisClient,
530
+ [{ providerId: "claude", oauthClient: new OAuthClient(CLAUDE_PROVIDER) }]
531
+ );
532
+ this.tokenRefreshJob.start();
533
+ logger.debug("Token refresh job started");
534
+
535
+ // Register Claude OAuth module
536
+ this.oauthStateStore = createOAuthStateStore("claude", redisClient);
537
+ const claudeOAuthModule = new ClaudeOAuthModule(
538
+ this.authProfilesManager,
539
+ this.modelPreferenceStore
540
+ );
541
+ moduleRegistry.register(claudeOAuthModule);
542
+ logger.debug(
543
+ `Claude OAuth module registered (system token: ${claudeOAuthModule.hasSystemKey() ? "available" : "not available"})`
544
+ );
545
+
546
+ // Register ChatGPT OAuth module
547
+ const chatgptOAuthModule = new ChatGPTOAuthModule(this.agentSettingsStore);
548
+ moduleRegistry.register(chatgptOAuthModule);
549
+ logger.debug(
550
+ `ChatGPT OAuth module registered (system token: ${chatgptOAuthModule.hasSystemKey() ? "available" : "not available"})`
551
+ );
552
+
553
+ // Initialize system skills — use injected skills if provided, else load from file
554
+ if (this.options?.systemSkills) {
555
+ this.systemSkillsService = new SystemSkillsService(
556
+ undefined,
557
+ this.options.systemSkills
558
+ );
559
+ } else {
560
+ this.systemSkillsService = new SystemSkillsService(
561
+ "config/system-skills.json"
562
+ );
563
+ }
564
+ this.systemConfigResolver = new SystemConfigResolver(
565
+ this.systemSkillsService
566
+ );
567
+ logger.debug("System skills service initialized");
568
+
569
+ this.transcriptionService?.setProviderConfigSource(() =>
570
+ this.systemConfigResolver
571
+ ? this.systemConfigResolver.getProviderConfigs()
572
+ : Promise.resolve({})
573
+ );
574
+
575
+ // Register config-driven providers from system skills
576
+ const configProviders =
577
+ await this.systemConfigResolver.getProviderConfigs();
578
+ const registeredIds = new Set(
579
+ getModelProviderModules().map((m) => m.providerId)
580
+ );
581
+ for (const [id, entry] of Object.entries(configProviders)) {
582
+ if (registeredIds.has(id)) {
583
+ logger.info(
584
+ `Skipping config-driven provider "${id}" — already registered`
585
+ );
586
+ continue;
587
+ }
588
+ const module = new ApiKeyProviderModule({
589
+ providerId: id,
590
+ providerDisplayName: entry.displayName,
591
+ providerIconUrl: entry.iconUrl,
592
+ envVarName: entry.envVarName,
593
+ slug: id,
594
+ upstreamBaseUrl: entry.upstreamBaseUrl,
595
+ modelsEndpoint: entry.modelsEndpoint,
596
+ sdkCompat: entry.sdkCompat,
597
+ defaultModel: entry.defaultModel,
598
+ registryAlias: entry.registryAlias,
599
+ apiKeyInstructions: entry.apiKeyInstructions,
600
+ apiKeyPlaceholder: entry.apiKeyPlaceholder,
601
+ agentSettingsStore: this.agentSettingsStore,
602
+ });
603
+ moduleRegistry.register(module);
604
+ registeredIds.add(id);
605
+ logger.debug(
606
+ `Registered config-driven provider: ${id} (system key: ${module.hasSystemKey() ? "available" : "not available"})`
607
+ );
608
+ }
609
+
610
+ // Initialize provider catalog service
611
+ this.providerCatalogService = new ProviderCatalogService(
612
+ this.agentSettingsStore,
613
+ this.authProfilesManager
614
+ );
615
+ logger.debug("Provider catalog service initialized");
616
+
617
+ // Register provider upstream configs with the secret proxy for path-based routing
618
+ if (this.secretProxy) {
619
+ this.secretProxy.setAuthProfilesManager(this.authProfilesManager);
620
+ for (const provider of getModelProviderModules()) {
621
+ const upstream = provider.getUpstreamConfig?.();
622
+ if (upstream) {
623
+ this.secretProxy.registerUpstream(upstream, provider.providerId);
624
+ }
625
+ }
626
+ // Register system key resolver for fallback when no per-agent auth profile exists
627
+ const modules = getModelProviderModules();
628
+ this.secretProxy.setSystemKeyResolver((providerId: string) => {
629
+ const mod = modules.find((m) => m.providerId === providerId);
630
+ if (!mod) return undefined;
631
+ // Use the module's injectSystemKeyFallback to resolve the system key.
632
+ // The fallback may inject into a different env var than credentialEnvVarName
633
+ // (e.g., Claude injects ANTHROPIC_API_KEY, not CLAUDE_CODE_OAUTH_TOKEN),
634
+ // so check all secret env var names.
635
+ const testEnv: Record<string, string> = {};
636
+ mod.injectSystemKeyFallback(testEnv);
637
+ for (const varName of mod.getSecretEnvVarNames()) {
638
+ if (testEnv[varName]) return testEnv[varName];
639
+ }
640
+ return testEnv[mod.getCredentialEnvVarName()] || undefined;
641
+ });
642
+ logger.debug("Provider upstreams registered with secret proxy");
643
+ }
644
+ }
645
+
646
+ // ============================================================================
647
+ // 4. MCP Services Initialization
648
+ // ============================================================================
649
+
650
+ private async initializeMcpServices(): Promise<void> {
651
+ if (!this.queue) {
652
+ throw new Error("Queue must be initialized before MCP services");
653
+ }
654
+
655
+ const redisClient = this.queue.getRedisClient();
656
+
657
+ // Initialize simplified MCP config service (no OAuth discovery)
658
+ this.mcpConfigService = new McpConfigService({
659
+ agentSettingsStore: this.agentSettingsStore,
660
+ configResolver: this.systemConfigResolver,
661
+ });
662
+
663
+ // Initialize instruction service (needed by WorkerGateway)
664
+ this.instructionService = new InstructionService(
665
+ this.mcpConfigService,
666
+ this.agentSettingsStore
667
+ );
668
+ logger.debug("Instruction service initialized");
669
+
670
+ // Initialize MCP tool cache and proxy
671
+ const mcpToolCache = new McpToolCache(redisClient);
672
+ this.mcpProxy = new McpProxy(
673
+ this.mcpConfigService,
674
+ this.queue,
675
+ mcpToolCache,
676
+ this.grantStore
677
+ );
678
+ logger.debug("MCP proxy initialized");
679
+
680
+ // Initialize worker gateway
681
+ if (!this.sessionManager) {
682
+ throw new Error(
683
+ "Session manager must be initialized before worker gateway"
684
+ );
685
+ }
686
+ this.workerGateway = new WorkerGateway(
687
+ this.queue,
688
+ this.config.mcp.publicGatewayUrl,
689
+ this.sessionManager,
690
+ this.mcpConfigService,
691
+ this.instructionService,
692
+ this.mcpProxy,
693
+ this.providerCatalogService,
694
+ this.settingsResolver,
695
+ this.systemSkillsService
696
+ );
697
+ logger.debug("Worker gateway initialized");
698
+
699
+ // Discover and initialize all available modules
700
+ await moduleRegistry.registerAvailableModules();
701
+ await moduleRegistry.initAll();
702
+ logger.debug("Modules initialized");
703
+ }
704
+
705
+ // ============================================================================
706
+ // 7. Command Registry Initialization
707
+ // ============================================================================
708
+
709
+ private initializeCommandRegistry(): void {
710
+ if (!this.agentSettingsStore) {
711
+ throw new Error(
712
+ "Agent settings store must be initialized before command registry"
713
+ );
714
+ }
715
+ this.commandRegistry = new CommandRegistry();
716
+ registerBuiltInCommands(this.commandRegistry, {
717
+ agentSettingsStore: this.agentSettingsStore,
718
+ });
719
+ logger.debug("Command registry initialized with built-in commands");
720
+ }
721
+
722
+ // ============================================================================
723
+ // File-First Helpers
724
+ // ============================================================================
725
+
726
+ private async populateStoreFromFiles(
727
+ store: InMemoryAgentStore,
728
+ agents: FileLoadedAgent[]
729
+ ): Promise<void> {
730
+ for (const agent of agents) {
731
+ await store.saveMetadata(agent.agentId, {
732
+ agentId: agent.agentId,
733
+ name: agent.name,
734
+ description: agent.description,
735
+ owner: { platform: "system", userId: "manifest" },
736
+ createdAt: Date.now(),
737
+ });
738
+ await store.saveSettings(agent.agentId, {
739
+ ...agent.settings,
740
+ updatedAt: Date.now(),
741
+ } as any);
742
+ }
743
+ }
744
+
745
+ private buildSettingsFromAgentConfig(
746
+ agent: AgentConfig
747
+ ): Record<string, any> {
748
+ const settings: Record<string, any> = {};
749
+ if (agent.identityMd) settings.identityMd = agent.identityMd;
750
+ if (agent.soulMd) settings.soulMd = agent.soulMd;
751
+ if (agent.userMd) settings.userMd = agent.userMd;
752
+
753
+ if (agent.providers?.length) {
754
+ settings.installedProviders = agent.providers.map((p) => ({
755
+ providerId: p.id,
756
+ installedAt: Date.now(),
757
+ }));
758
+ settings.modelSelection = { mode: "auto" };
759
+ const providerModelPreferences = Object.fromEntries(
760
+ agent.providers
761
+ .filter((p) => !!p.model?.trim())
762
+ .map((p) => [p.id, p.model!.trim()])
763
+ );
764
+ if (Object.keys(providerModelPreferences).length > 0) {
765
+ settings.providerModelPreferences = providerModelPreferences;
766
+ }
767
+ }
768
+
769
+ if (agent.skills?.mcp) {
770
+ settings.mcpServers = agent.skills.mcp;
771
+ }
772
+
773
+ if (agent.network) {
774
+ settings.networkConfig = {
775
+ allowedDomains: agent.network.allowed,
776
+ deniedDomains: agent.network.denied,
777
+ };
778
+ }
779
+
780
+ if (agent.nixPackages?.length) {
781
+ settings.nixConfig = { packages: agent.nixPackages };
782
+ }
783
+
784
+ return settings;
785
+ }
786
+
787
+ private async syncAgentSettingsToRuntimeStore(
788
+ agentId: string,
789
+ settings: Record<string, any>
790
+ ): Promise<void> {
791
+ if (!this.agentSettingsStore) {
792
+ throw new Error("Agent settings store must be initialized");
793
+ }
794
+
795
+ const existing = await this.agentSettingsStore.getSettings(agentId);
796
+ await this.agentSettingsStore.saveSettings(agentId, {
797
+ ...settings,
798
+ authProfiles: existing?.authProfiles,
799
+ mcpInstallNotified: existing?.mcpInstallNotified,
800
+ });
801
+ }
802
+
803
+ private async populateStoreFromAgentConfigs(
804
+ store: InMemoryAgentStore,
805
+ agents: AgentConfig[]
806
+ ): Promise<void> {
807
+ for (const agent of agents) {
808
+ await store.saveMetadata(agent.id, {
809
+ agentId: agent.id,
810
+ name: agent.name,
811
+ description: agent.description,
812
+ owner: { platform: "system", userId: "config" },
813
+ createdAt: Date.now(),
814
+ });
815
+ await store.saveSettings(agent.id, {
816
+ ...this.buildSettingsFromAgentConfig(agent),
817
+ updatedAt: Date.now(),
818
+ } as any);
819
+ }
820
+
821
+ // Store agent configs for credential seeding and connection seeding later
822
+ this.configAgents = agents;
823
+ }
824
+
825
+ /**
826
+ * Reload agent config from files (dev mode only).
827
+ * Re-reads lobu.toml + markdown, clears and re-populates the in-memory store.
828
+ */
829
+ async reloadFromFiles(): Promise<{ reloaded: boolean; agents: string[] }> {
830
+ if (!this.projectPath) {
831
+ return { reloaded: false, agents: [] };
832
+ }
833
+
834
+ // Re-load from disk
835
+ this.fileLoadedAgents = await loadAgentConfigFromFiles(this.projectPath);
836
+
837
+ // Re-populate the in-memory store (clear existing data first)
838
+ if (this.configStore instanceof InMemoryAgentStore) {
839
+ const store = this.configStore as InMemoryAgentStore;
840
+ // Clear existing agents by loading fresh
841
+ const existing = await store.listAgents();
842
+ for (const meta of existing) {
843
+ // Only clear file-managed agents (owner: system/manifest)
844
+ if (
845
+ meta.owner?.platform === "system" &&
846
+ meta.owner?.userId === "manifest"
847
+ ) {
848
+ await store.deleteSettings(meta.agentId);
849
+ await store.deleteMetadata(meta.agentId);
850
+ }
851
+ }
852
+ await this.populateStoreFromFiles(store, this.fileLoadedAgents);
853
+ }
854
+
855
+ if (this.agentSettingsStore) {
856
+ for (const agent of this.fileLoadedAgents) {
857
+ await this.syncAgentSettingsToRuntimeStore(
858
+ agent.agentId,
859
+ agent.settings
860
+ );
861
+ }
862
+ }
863
+
864
+ // Re-seed credentials
865
+ if (this.authProfilesManager) {
866
+ for (const agent of this.fileLoadedAgents) {
867
+ for (const cred of agent.credentials) {
868
+ await this.authProfilesManager.upsertProfile({
869
+ agentId: agent.agentId,
870
+ provider: cred.provider,
871
+ credential: cred.key,
872
+ authType: "api-key",
873
+ label: `${cred.provider} (from lobu.toml)`,
874
+ makePrimary: true,
875
+ });
876
+ }
877
+ }
878
+ }
879
+
880
+ const agentIds = this.fileLoadedAgents.map((a) => a.agentId);
881
+ logger.info(`Reloaded ${agentIds.length} agent(s) from files`);
882
+ return { reloaded: true, agents: agentIds };
883
+ }
884
+
885
+ getFileLoadedAgents(): FileLoadedAgent[] {
886
+ return this.fileLoadedAgents;
887
+ }
888
+
889
+ getConfigAgents(): AgentConfig[] {
890
+ return this.configAgents;
891
+ }
892
+
893
+ getProjectPath(): string | null {
894
+ return this.projectPath;
895
+ }
896
+
897
+ isFileFirstMode(): boolean {
898
+ return this.projectPath !== null;
899
+ }
900
+
901
+ // ============================================================================
902
+ // Shutdown
903
+ // ============================================================================
904
+
905
+ async shutdown(): Promise<void> {
906
+ logger.info("Shutting down core services...");
907
+
908
+ if (this.tokenRefreshJob) {
909
+ this.tokenRefreshJob.stop();
910
+ }
911
+
912
+ if (this.queueProducer) {
913
+ await this.queueProducer.stop();
914
+ }
915
+
916
+ if (this.workerGateway) {
917
+ this.workerGateway.shutdown();
918
+ logger.info("Worker gateway shutdown complete");
919
+ }
920
+
921
+ if (this.queue) {
922
+ await this.queue.stop();
923
+ }
924
+
925
+ logger.info("Core services shutdown complete");
926
+ }
927
+
928
+ // ============================================================================
929
+ // Service Accessors (implements ICoreServices interface)
930
+ // ============================================================================
931
+
932
+ getQueue(): IMessageQueue {
933
+ if (!this.queue) throw new Error("Queue not initialized");
934
+ return this.queue;
935
+ }
936
+
937
+ getQueueProducer(): QueueProducer {
938
+ if (!this.queueProducer) throw new Error("Queue producer not initialized");
939
+ return this.queueProducer;
940
+ }
941
+
942
+ getSecretProxy(): SecretProxy | undefined {
943
+ return this.secretProxy;
944
+ }
945
+
946
+ getWorkerGateway(): WorkerGateway | undefined {
947
+ return this.workerGateway;
948
+ }
949
+
950
+ getMcpProxy(): McpProxy | undefined {
951
+ return this.mcpProxy;
952
+ }
953
+
954
+ getMcpConfigService(): McpConfigService | undefined {
955
+ return this.mcpConfigService;
956
+ }
957
+
958
+ getModelPreferenceStore(): ModelPreferenceStore | undefined {
959
+ return this.modelPreferenceStore;
960
+ }
961
+
962
+ getOAuthStateStore(): ProviderOAuthStateStore | undefined {
963
+ return this.oauthStateStore;
964
+ }
965
+
966
+ getPublicGatewayUrl(): string {
967
+ return this.config.mcp.publicGatewayUrl;
968
+ }
969
+
970
+ getSessionManager(): SessionManager {
971
+ if (!this.sessionManager)
972
+ throw new Error("Session manager not initialized");
973
+ return this.sessionManager;
974
+ }
975
+
976
+ getInstructionService(): InstructionService | undefined {
977
+ return this.instructionService;
978
+ }
979
+
980
+ getInteractionService(): InteractionService {
981
+ if (!this.interactionService)
982
+ throw new Error("Interaction service not initialized");
983
+ return this.interactionService;
984
+ }
985
+
986
+ getAgentSettingsStore(): AgentSettingsStore {
987
+ if (!this.agentSettingsStore)
988
+ throw new Error("Agent settings store not initialized");
989
+ return this.agentSettingsStore;
990
+ }
991
+
992
+ getChannelBindingService(): ChannelBindingService {
993
+ if (!this.channelBindingService)
994
+ throw new Error("Channel binding service not initialized");
995
+ return this.channelBindingService;
996
+ }
997
+
998
+ getScheduledWakeupService(): ScheduledWakeupService | undefined {
999
+ return this.scheduledWakeupService;
1000
+ }
1001
+
1002
+ getTranscriptionService(): TranscriptionService | undefined {
1003
+ return this.transcriptionService;
1004
+ }
1005
+
1006
+ getImageGenerationService(): ImageGenerationService | undefined {
1007
+ return this.imageGenerationService;
1008
+ }
1009
+
1010
+ getUserAgentsStore(): UserAgentsStore {
1011
+ if (!this.userAgentsStore)
1012
+ throw new Error("User agents store not initialized");
1013
+ return this.userAgentsStore;
1014
+ }
1015
+
1016
+ getAgentMetadataStore(): AgentMetadataStore {
1017
+ if (!this.agentMetadataStore)
1018
+ throw new Error("Agent metadata store not initialized");
1019
+ return this.agentMetadataStore;
1020
+ }
1021
+
1022
+ getCommandRegistry(): CommandRegistry {
1023
+ if (!this.commandRegistry)
1024
+ throw new Error("Command registry not initialized");
1025
+ return this.commandRegistry;
1026
+ }
1027
+
1028
+ getProviderCatalogService(): ProviderCatalogService {
1029
+ if (!this.providerCatalogService)
1030
+ throw new Error("Provider catalog service not initialized");
1031
+ return this.providerCatalogService;
1032
+ }
1033
+
1034
+ getAuthProfilesManager(): AuthProfilesManager | undefined {
1035
+ return this.authProfilesManager;
1036
+ }
1037
+
1038
+ getGrantStore(): GrantStore | undefined {
1039
+ return this.grantStore;
1040
+ }
1041
+
1042
+ getSystemSkillsService(): SystemSkillsService | undefined {
1043
+ return this.systemSkillsService;
1044
+ }
1045
+
1046
+ getSystemConfigResolver(): SystemConfigResolver | undefined {
1047
+ return this.systemConfigResolver;
1048
+ }
1049
+
1050
+ getExternalAuthClient(): ExternalAuthClient | undefined {
1051
+ return this.externalAuthClient;
1052
+ }
1053
+ }