@lobu/gateway 3.0.9 → 3.0.12

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 (210) hide show
  1. package/dist/api/platform.d.ts.map +1 -1
  2. package/dist/api/platform.js +7 -26
  3. package/dist/api/platform.js.map +1 -1
  4. package/dist/auth/mcp/proxy.d.ts +14 -0
  5. package/dist/auth/mcp/proxy.d.ts.map +1 -1
  6. package/dist/auth/mcp/proxy.js +149 -13
  7. package/dist/auth/mcp/proxy.js.map +1 -1
  8. package/dist/cli/gateway.d.ts.map +1 -1
  9. package/dist/cli/gateway.js +29 -0
  10. package/dist/cli/gateway.js.map +1 -1
  11. package/dist/connections/chat-instance-manager.d.ts.map +1 -1
  12. package/dist/connections/chat-instance-manager.js +2 -1
  13. package/dist/connections/chat-instance-manager.js.map +1 -1
  14. package/dist/connections/interaction-bridge.d.ts +9 -2
  15. package/dist/connections/interaction-bridge.d.ts.map +1 -1
  16. package/dist/connections/interaction-bridge.js +121 -261
  17. package/dist/connections/interaction-bridge.js.map +1 -1
  18. package/dist/interactions.d.ts +9 -43
  19. package/dist/interactions.d.ts.map +1 -1
  20. package/dist/interactions.js +10 -52
  21. package/dist/interactions.js.map +1 -1
  22. package/dist/routes/public/agent.d.ts +4 -0
  23. package/dist/routes/public/agent.d.ts.map +1 -1
  24. package/dist/routes/public/agent.js +21 -0
  25. package/dist/routes/public/agent.js.map +1 -1
  26. package/dist/services/core-services.d.ts.map +1 -1
  27. package/dist/services/core-services.js +4 -0
  28. package/dist/services/core-services.js.map +1 -1
  29. package/package.json +9 -9
  30. package/src/__tests__/agent-config-routes.test.ts +0 -254
  31. package/src/__tests__/agent-history-routes.test.ts +0 -72
  32. package/src/__tests__/agent-routes.test.ts +0 -68
  33. package/src/__tests__/agent-schedules-routes.test.ts +0 -59
  34. package/src/__tests__/agent-settings-store.test.ts +0 -323
  35. package/src/__tests__/bedrock-model-catalog.test.ts +0 -40
  36. package/src/__tests__/bedrock-openai-service.test.ts +0 -157
  37. package/src/__tests__/bedrock-provider-module.test.ts +0 -56
  38. package/src/__tests__/chat-instance-manager-slack.test.ts +0 -204
  39. package/src/__tests__/chat-response-bridge.test.ts +0 -131
  40. package/src/__tests__/config-memory-plugins.test.ts +0 -92
  41. package/src/__tests__/config-request-store.test.ts +0 -127
  42. package/src/__tests__/connection-routes.test.ts +0 -144
  43. package/src/__tests__/core-services-store-selection.test.ts +0 -92
  44. package/src/__tests__/docker-deployment.test.ts +0 -1211
  45. package/src/__tests__/embedded-deployment.test.ts +0 -342
  46. package/src/__tests__/grant-store.test.ts +0 -148
  47. package/src/__tests__/http-proxy.test.ts +0 -281
  48. package/src/__tests__/instruction-service.test.ts +0 -37
  49. package/src/__tests__/link-buttons.test.ts +0 -112
  50. package/src/__tests__/lobu.test.ts +0 -32
  51. package/src/__tests__/mcp-config-service.test.ts +0 -347
  52. package/src/__tests__/mcp-proxy.test.ts +0 -694
  53. package/src/__tests__/message-handler-bridge.test.ts +0 -17
  54. package/src/__tests__/model-selection.test.ts +0 -172
  55. package/src/__tests__/oauth-templates.test.ts +0 -39
  56. package/src/__tests__/platform-adapter-slack-send.test.ts +0 -114
  57. package/src/__tests__/platform-helpers-model-resolution.test.ts +0 -253
  58. package/src/__tests__/provider-inheritance.test.ts +0 -212
  59. package/src/__tests__/routes/cli-auth.test.ts +0 -337
  60. package/src/__tests__/routes/interactions.test.ts +0 -121
  61. package/src/__tests__/secret-proxy.test.ts +0 -85
  62. package/src/__tests__/session-manager.test.ts +0 -572
  63. package/src/__tests__/setup.ts +0 -133
  64. package/src/__tests__/skill-and-mcp-registry.test.ts +0 -203
  65. package/src/__tests__/slack-routes.test.ts +0 -161
  66. package/src/__tests__/system-config-resolver.test.ts +0 -75
  67. package/src/__tests__/system-message-limiter.test.ts +0 -89
  68. package/src/__tests__/system-skills-service.test.ts +0 -362
  69. package/src/__tests__/transcription-service.test.ts +0 -222
  70. package/src/__tests__/utils/rate-limiter.test.ts +0 -102
  71. package/src/__tests__/worker-connection-manager.test.ts +0 -497
  72. package/src/__tests__/worker-job-router.test.ts +0 -722
  73. package/src/api/index.ts +0 -1
  74. package/src/api/platform.ts +0 -292
  75. package/src/api/response-renderer.ts +0 -157
  76. package/src/auth/agent-metadata-store.ts +0 -168
  77. package/src/auth/api-auth-middleware.ts +0 -69
  78. package/src/auth/api-key-provider-module.ts +0 -213
  79. package/src/auth/base-provider-module.ts +0 -201
  80. package/src/auth/bedrock/provider-module.ts +0 -110
  81. package/src/auth/chatgpt/chatgpt-oauth-module.ts +0 -185
  82. package/src/auth/chatgpt/device-code-client.ts +0 -218
  83. package/src/auth/chatgpt/index.ts +0 -1
  84. package/src/auth/claude/oauth-module.ts +0 -280
  85. package/src/auth/cli/token-service.ts +0 -249
  86. package/src/auth/external/client.ts +0 -560
  87. package/src/auth/external/device-code-client.ts +0 -235
  88. package/src/auth/mcp/config-service.ts +0 -420
  89. package/src/auth/mcp/proxy.ts +0 -1086
  90. package/src/auth/mcp/string-substitution.ts +0 -17
  91. package/src/auth/mcp/tool-cache.ts +0 -90
  92. package/src/auth/oauth/base-client.ts +0 -267
  93. package/src/auth/oauth/client.ts +0 -153
  94. package/src/auth/oauth/credentials.ts +0 -7
  95. package/src/auth/oauth/providers.ts +0 -69
  96. package/src/auth/oauth/state-store.ts +0 -150
  97. package/src/auth/oauth-templates.ts +0 -179
  98. package/src/auth/provider-catalog.ts +0 -220
  99. package/src/auth/provider-model-options.ts +0 -41
  100. package/src/auth/settings/agent-settings-store.ts +0 -565
  101. package/src/auth/settings/auth-profiles-manager.ts +0 -216
  102. package/src/auth/settings/index.ts +0 -12
  103. package/src/auth/settings/model-preference-store.ts +0 -52
  104. package/src/auth/settings/model-selection.ts +0 -135
  105. package/src/auth/settings/resolved-settings-view.ts +0 -298
  106. package/src/auth/settings/template-utils.ts +0 -44
  107. package/src/auth/settings/token-service.ts +0 -88
  108. package/src/auth/system-env-store.ts +0 -98
  109. package/src/auth/user-agents-store.ts +0 -68
  110. package/src/channels/binding-service.ts +0 -214
  111. package/src/channels/index.ts +0 -4
  112. package/src/cli/gateway.ts +0 -1312
  113. package/src/cli/index.ts +0 -74
  114. package/src/commands/built-in-commands.ts +0 -80
  115. package/src/commands/command-dispatcher.ts +0 -94
  116. package/src/commands/command-reply-adapters.ts +0 -27
  117. package/src/config/file-loader.ts +0 -618
  118. package/src/config/index.ts +0 -588
  119. package/src/config/network-allowlist.ts +0 -71
  120. package/src/connections/chat-instance-manager.ts +0 -1284
  121. package/src/connections/chat-response-bridge.ts +0 -618
  122. package/src/connections/index.ts +0 -7
  123. package/src/connections/interaction-bridge.ts +0 -831
  124. package/src/connections/message-handler-bridge.ts +0 -440
  125. package/src/connections/platform-auth-methods.ts +0 -15
  126. package/src/connections/types.ts +0 -84
  127. package/src/gateway/connection-manager.ts +0 -291
  128. package/src/gateway/index.ts +0 -698
  129. package/src/gateway/job-router.ts +0 -201
  130. package/src/gateway-main.ts +0 -200
  131. package/src/index.ts +0 -41
  132. package/src/infrastructure/queue/index.ts +0 -12
  133. package/src/infrastructure/queue/queue-producer.ts +0 -148
  134. package/src/infrastructure/queue/redis-queue.ts +0 -361
  135. package/src/infrastructure/queue/types.ts +0 -133
  136. package/src/infrastructure/redis/system-message-limiter.ts +0 -94
  137. package/src/interactions/config-request-store.ts +0 -198
  138. package/src/interactions.ts +0 -363
  139. package/src/lobu.ts +0 -311
  140. package/src/metrics/prometheus.ts +0 -159
  141. package/src/modules/module-system.ts +0 -179
  142. package/src/orchestration/base-deployment-manager.ts +0 -900
  143. package/src/orchestration/deployment-utils.ts +0 -98
  144. package/src/orchestration/impl/docker-deployment.ts +0 -620
  145. package/src/orchestration/impl/embedded-deployment.ts +0 -268
  146. package/src/orchestration/impl/index.ts +0 -8
  147. package/src/orchestration/impl/k8s/deployment.ts +0 -1061
  148. package/src/orchestration/impl/k8s/helpers.ts +0 -610
  149. package/src/orchestration/impl/k8s/index.ts +0 -1
  150. package/src/orchestration/index.ts +0 -333
  151. package/src/orchestration/message-consumer.ts +0 -584
  152. package/src/orchestration/scheduled-wakeup.ts +0 -704
  153. package/src/permissions/approval-policy.ts +0 -36
  154. package/src/permissions/grant-store.ts +0 -219
  155. package/src/platform/file-handler.ts +0 -66
  156. package/src/platform/link-buttons.ts +0 -57
  157. package/src/platform/renderer-utils.ts +0 -44
  158. package/src/platform/response-renderer.ts +0 -84
  159. package/src/platform/unified-thread-consumer.ts +0 -194
  160. package/src/platform.ts +0 -318
  161. package/src/proxy/http-proxy.ts +0 -752
  162. package/src/proxy/proxy-manager.ts +0 -81
  163. package/src/proxy/secret-proxy.ts +0 -402
  164. package/src/proxy/token-refresh-job.ts +0 -143
  165. package/src/routes/internal/audio.ts +0 -141
  166. package/src/routes/internal/device-auth.ts +0 -652
  167. package/src/routes/internal/files.ts +0 -226
  168. package/src/routes/internal/history.ts +0 -69
  169. package/src/routes/internal/images.ts +0 -127
  170. package/src/routes/internal/interactions.ts +0 -84
  171. package/src/routes/internal/middleware.ts +0 -23
  172. package/src/routes/internal/schedule.ts +0 -226
  173. package/src/routes/internal/types.ts +0 -22
  174. package/src/routes/openapi-auto.ts +0 -239
  175. package/src/routes/public/agent-access.ts +0 -23
  176. package/src/routes/public/agent-config.ts +0 -675
  177. package/src/routes/public/agent-history.ts +0 -422
  178. package/src/routes/public/agent-schedules.ts +0 -296
  179. package/src/routes/public/agent.ts +0 -1086
  180. package/src/routes/public/agents.ts +0 -373
  181. package/src/routes/public/channels.ts +0 -191
  182. package/src/routes/public/cli-auth.ts +0 -896
  183. package/src/routes/public/connections.ts +0 -574
  184. package/src/routes/public/landing.ts +0 -16
  185. package/src/routes/public/oauth.ts +0 -147
  186. package/src/routes/public/settings-auth.ts +0 -104
  187. package/src/routes/public/slack.ts +0 -173
  188. package/src/routes/shared/agent-ownership.ts +0 -101
  189. package/src/routes/shared/token-verifier.ts +0 -34
  190. package/src/services/bedrock-model-catalog.ts +0 -217
  191. package/src/services/bedrock-openai-service.ts +0 -658
  192. package/src/services/core-services.ts +0 -1072
  193. package/src/services/image-generation-service.ts +0 -257
  194. package/src/services/instruction-service.ts +0 -318
  195. package/src/services/mcp-registry.ts +0 -94
  196. package/src/services/platform-helpers.ts +0 -287
  197. package/src/services/session-manager.ts +0 -262
  198. package/src/services/settings-resolver.ts +0 -74
  199. package/src/services/system-config-resolver.ts +0 -89
  200. package/src/services/system-skills-service.ts +0 -229
  201. package/src/services/transcription-service.ts +0 -684
  202. package/src/session.ts +0 -110
  203. package/src/spaces/index.ts +0 -1
  204. package/src/spaces/space-resolver.ts +0 -17
  205. package/src/stores/in-memory-agent-store.ts +0 -403
  206. package/src/stores/redis-agent-store.ts +0 -279
  207. package/src/utils/public-url.ts +0 -44
  208. package/src/utils/rate-limiter.ts +0 -94
  209. package/tsconfig.json +0 -33
  210. package/tsconfig.tsbuildinfo +0 -1
@@ -1,287 +0,0 @@
1
- /**
2
- * Shared platform helpers.
3
- * Extracts common logic duplicated across Slack, Telegram, and WhatsApp message handlers.
4
- */
5
-
6
- import {
7
- createLogger,
8
- type PluginConfig,
9
- type PluginsConfig,
10
- } from "@lobu/core";
11
- import type { AgentSettingsStore } from "../auth/settings";
12
- import { resolveEffectiveModelRef } from "../auth/settings/model-selection";
13
- import type { ChannelBindingService } from "../channels";
14
- import { buildMemoryPlugins, getInternalGatewayUrl } from "../config";
15
- import type { MessagePayload } from "../infrastructure/queue/queue-producer";
16
- import { getModelProviderModules } from "../modules/module-system";
17
- import { platformAgentId } from "../spaces";
18
-
19
- const logger = createLogger("platform-helpers");
20
- const OWLETTO_PLUGIN_SOURCE = "@lobu/owletto-openclaw";
21
-
22
- function readOwlettoRuntimeDefaults(): PluginConfig | null {
23
- const configuredPlugin = buildMemoryPlugins().find(
24
- (plugin) =>
25
- plugin.source === OWLETTO_PLUGIN_SOURCE && plugin.slot === "memory"
26
- );
27
- if (configuredPlugin) {
28
- return configuredPlugin;
29
- }
30
-
31
- const gatewayUrl = getInternalGatewayUrl();
32
- return {
33
- source: OWLETTO_PLUGIN_SOURCE,
34
- slot: "memory",
35
- enabled: true,
36
- config: {
37
- mcpUrl: `${gatewayUrl}/mcp/owletto`,
38
- gatewayAuthUrl: gatewayUrl,
39
- },
40
- };
41
- }
42
-
43
- function normalizeOwlettoPluginConfig(
44
- plugin: PluginConfig,
45
- runtimeDefault: PluginConfig | null
46
- ): PluginConfig {
47
- if (
48
- plugin.source !== OWLETTO_PLUGIN_SOURCE ||
49
- plugin.slot !== "memory" ||
50
- !runtimeDefault?.config ||
51
- !plugin.config
52
- ) {
53
- return plugin;
54
- }
55
-
56
- const storedMcpUrl = plugin.config.mcpUrl;
57
- const storedGatewayAuthUrl = plugin.config.gatewayAuthUrl;
58
- const runtimeMcpUrl = runtimeDefault.config.mcpUrl;
59
- const runtimeGatewayAuthUrl = runtimeDefault.config.gatewayAuthUrl;
60
-
61
- const shouldReplaceMcpUrl =
62
- typeof storedMcpUrl === "string" &&
63
- typeof runtimeMcpUrl === "string" &&
64
- runtimeMcpUrl !== storedMcpUrl &&
65
- /^https?:\/\/gateway(?::\d+)?\/mcp\/owletto\/?$/.test(storedMcpUrl);
66
- const shouldReplaceGatewayAuthUrl =
67
- typeof storedGatewayAuthUrl === "string" &&
68
- typeof runtimeGatewayAuthUrl === "string" &&
69
- runtimeGatewayAuthUrl !== storedGatewayAuthUrl &&
70
- /^https?:\/\/gateway(?::\d+)?\/?$/.test(storedGatewayAuthUrl);
71
-
72
- if (!shouldReplaceMcpUrl && !shouldReplaceGatewayAuthUrl) {
73
- return plugin;
74
- }
75
-
76
- return {
77
- ...plugin,
78
- config: {
79
- ...plugin.config,
80
- ...(shouldReplaceMcpUrl ? { mcpUrl: runtimeMcpUrl } : {}),
81
- ...(shouldReplaceGatewayAuthUrl
82
- ? { gatewayAuthUrl: runtimeGatewayAuthUrl }
83
- : {}),
84
- },
85
- };
86
- }
87
-
88
- function normalizePluginsConfig(
89
- pluginsConfig: PluginsConfig | undefined
90
- ): PluginsConfig | undefined {
91
- if (!pluginsConfig?.plugins?.length) {
92
- return pluginsConfig;
93
- }
94
-
95
- const runtimeDefault = readOwlettoRuntimeDefaults();
96
- let changed = false;
97
- const plugins = pluginsConfig.plugins.map((plugin) => {
98
- const normalized = normalizeOwlettoPluginConfig(plugin, runtimeDefault);
99
- if (normalized !== plugin) {
100
- changed = true;
101
- }
102
- return normalized;
103
- });
104
-
105
- return changed ? { ...pluginsConfig, plugins } : pluginsConfig;
106
- }
107
-
108
- /**
109
- * Resolve agent options by merging base options with per-agent settings.
110
- * Priority: agent settings > config defaults.
111
- */
112
- export async function resolveAgentOptions(
113
- agentId: string,
114
- baseOptions: Record<string, any>,
115
- agentSettingsStore?: AgentSettingsStore
116
- ): Promise<Record<string, any>> {
117
- if (!agentSettingsStore) {
118
- return { ...baseOptions };
119
- }
120
-
121
- const settings = await agentSettingsStore.getEffectiveSettings(agentId);
122
- if (!settings) {
123
- return { ...baseOptions };
124
- }
125
-
126
- const effectiveProviders = settings.installedProviders || [];
127
-
128
- const mergedOptions: Record<string, any> = { ...baseOptions };
129
- const effectiveModelRef = resolveEffectiveModelRef(settings);
130
- logger.info(
131
- {
132
- agentId,
133
- configuredModel: settings.model,
134
- effectiveModel: effectiveModelRef,
135
- },
136
- "Applying agent settings"
137
- );
138
-
139
- if (effectiveModelRef) {
140
- mergedOptions.model = effectiveModelRef;
141
- } else if (effectiveProviders.length > 0) {
142
- // Auto mode with installed providers: let worker resolve default model.
143
- delete mergedOptions.model;
144
- }
145
-
146
- if (settings.networkConfig) {
147
- mergedOptions.networkConfig = settings.networkConfig;
148
- }
149
- if (settings.nixConfig) {
150
- mergedOptions.nixConfig = settings.nixConfig;
151
- }
152
- if (settings.toolsConfig) {
153
- mergedOptions.toolsConfig = settings.toolsConfig;
154
- }
155
- if (settings.mcpServers) {
156
- mergedOptions.mcpServers = settings.mcpServers;
157
- }
158
- if (settings.pluginsConfig) {
159
- mergedOptions.pluginsConfig = normalizePluginsConfig(
160
- settings.pluginsConfig
161
- );
162
- }
163
- // Apply default memory plugins if no pluginsConfig from settings or baseOptions
164
- if (!mergedOptions.pluginsConfig) {
165
- mergedOptions.pluginsConfig = { plugins: buildMemoryPlugins() };
166
- }
167
- if (settings.verboseLogging !== undefined) {
168
- mergedOptions.verboseLogging = settings.verboseLogging;
169
- }
170
-
171
- return mergedOptions;
172
- }
173
-
174
- export async function hasConfiguredProvider(
175
- agentId: string,
176
- agentSettingsStore?: AgentSettingsStore
177
- ): Promise<boolean> {
178
- if (!agentSettingsStore) {
179
- return true;
180
- }
181
-
182
- const settings = await agentSettingsStore.getEffectiveSettings(agentId);
183
- const installedProviderIds = new Set(
184
- (settings?.installedProviders || []).map((provider) => provider.providerId)
185
- );
186
-
187
- if ((settings?.authProfiles?.length || 0) > 0) {
188
- return true;
189
- }
190
-
191
- const modules = getModelProviderModules();
192
- if (installedProviderIds.size > 0) {
193
- return modules.some(
194
- (module) =>
195
- installedProviderIds.has(module.providerId) && module.hasSystemKey()
196
- );
197
- }
198
-
199
- return modules.some((module) => module.hasSystemKey());
200
- }
201
-
202
- /**
203
- * Build a MessagePayload from common fields.
204
- * Extracts networkConfig, nixConfig, mcpServers from agentOptions before constructing the payload.
205
- */
206
- export function buildMessagePayload(params: {
207
- platform: string;
208
- userId: string;
209
- botId: string;
210
- conversationId: string;
211
- teamId: string;
212
- agentId: string;
213
- messageId: string;
214
- messageText: string;
215
- channelId: string;
216
- platformMetadata: Record<string, any>;
217
- agentOptions: Record<string, any>;
218
- }): MessagePayload {
219
- const { networkConfig, nixConfig, mcpServers, ...remainingOptions } =
220
- params.agentOptions;
221
-
222
- return {
223
- platform: params.platform,
224
- userId: params.userId,
225
- botId: params.botId,
226
- conversationId: params.conversationId,
227
- teamId: params.teamId,
228
- agentId: params.agentId,
229
- messageId: params.messageId,
230
- messageText: params.messageText,
231
- channelId: params.channelId,
232
- platformMetadata: params.platformMetadata,
233
- agentOptions: remainingOptions,
234
- networkConfig,
235
- nixConfig,
236
- mcpConfig: mcpServers ? { mcpServers } : undefined,
237
- };
238
- }
239
-
240
- /**
241
- * Resolve agent ID. Deterministic for all platforms.
242
- * Channel binding is checked first for Slack (multi-tenant), then falls back to platformAgentId.
243
- */
244
- export async function resolveAgentId(params: {
245
- platform: string;
246
- userId: string;
247
- channelId: string;
248
- isGroup: boolean;
249
- teamId?: string;
250
- channelBindingService?: ChannelBindingService;
251
- sendConfigPrompt?: () => Promise<boolean>;
252
- }): Promise<{ agentId: string; promptSent: boolean }> {
253
- const {
254
- platform,
255
- userId,
256
- channelId,
257
- isGroup,
258
- teamId,
259
- channelBindingService,
260
- sendConfigPrompt,
261
- } = params;
262
-
263
- // Check channel binding first (Slack multi-tenant)
264
- if (channelBindingService) {
265
- const binding = await channelBindingService.getBinding(
266
- platform,
267
- channelId,
268
- teamId
269
- );
270
- if (binding) {
271
- logger.info({ agentId: binding.agentId, channelId }, "Using bound agent");
272
- return { agentId: binding.agentId, promptSent: false };
273
- }
274
-
275
- if (sendConfigPrompt) {
276
- const sent = await sendConfigPrompt();
277
- if (sent) return { agentId: "", promptSent: true };
278
- }
279
- }
280
-
281
- const agentId = platformAgentId(platform, userId, channelId, isGroup);
282
- logger.info(
283
- { agentId, platform, channelId },
284
- "Deterministic agent ID resolved"
285
- );
286
- return { agentId, promptSent: false };
287
- }
@@ -1,262 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { createLogger, DEFAULTS, REDIS_KEYS } from "@lobu/core";
4
- import type Redis from "ioredis";
5
- import type { IMessageQueue } from "../infrastructure/queue";
6
- import {
7
- computeSessionKey,
8
- type ISessionManager,
9
- type SessionStore,
10
- type ThreadSession,
11
- } from "../session";
12
-
13
- const logger = createLogger("session-manager");
14
-
15
- /**
16
- * Redis-based session storage
17
- * Sessions are stored with automatic TTL expiration
18
- */
19
- export class RedisSessionStore implements SessionStore {
20
- private readonly SESSION_PREFIX = REDIS_KEYS.SESSION;
21
- private readonly THREAD_INDEX_PREFIX = "conversation_index:";
22
- private readonly DEFAULT_TTL_SECONDS = DEFAULTS.SESSION_TTL_SECONDS;
23
- private redis: Redis;
24
-
25
- constructor(queue: IMessageQueue) {
26
- // Get Redis client from queue connection pool
27
- this.redis = queue.getRedisClient();
28
- }
29
-
30
- private getSessionKey(sessionKey: string): string {
31
- return `${this.SESSION_PREFIX}${sessionKey}`;
32
- }
33
-
34
- private getThreadIndexKey(channelId: string, threadTs: string): string {
35
- return `${this.THREAD_INDEX_PREFIX}${channelId}:${threadTs}`;
36
- }
37
-
38
- async get(sessionKey: string): Promise<ThreadSession | null> {
39
- try {
40
- const key = this.getSessionKey(sessionKey);
41
- const data = await this.redis.get(key);
42
-
43
- if (!data) {
44
- return null;
45
- }
46
-
47
- // Parse JSON
48
- return JSON.parse(data) as ThreadSession;
49
- } catch (error) {
50
- logger.error(`Failed to get session ${sessionKey}:`, error);
51
- return null;
52
- }
53
- }
54
-
55
- async set(sessionKey: string, session: ThreadSession): Promise<void> {
56
- try {
57
- const key = this.getSessionKey(sessionKey);
58
- const indexKey = this.getThreadIndexKey(
59
- session.channelId,
60
- session.conversationId
61
- );
62
-
63
- // Atomically set both session and thread index keys
64
- const pipeline = this.redis.pipeline();
65
- pipeline.setex(key, this.DEFAULT_TTL_SECONDS, JSON.stringify(session));
66
- pipeline.setex(
67
- indexKey,
68
- this.DEFAULT_TTL_SECONDS,
69
- JSON.stringify({ sessionKey })
70
- );
71
- await pipeline.exec();
72
-
73
- logger.debug(`Stored session ${sessionKey}`);
74
- } catch (error) {
75
- logger.error(`Failed to set session ${sessionKey}:`, error);
76
- throw error;
77
- }
78
- }
79
-
80
- async delete(sessionKey: string): Promise<void> {
81
- try {
82
- // Get session first to clean up thread index
83
- const session = await this.get(sessionKey);
84
-
85
- const key = this.getSessionKey(sessionKey);
86
-
87
- // Atomically delete both session and thread index keys
88
- if (session?.conversationId) {
89
- const indexKey = this.getThreadIndexKey(
90
- session.channelId,
91
- session.conversationId
92
- );
93
- const pipeline = this.redis.pipeline();
94
- pipeline.del(key);
95
- pipeline.del(indexKey);
96
- await pipeline.exec();
97
- } else {
98
- await this.redis.del(key);
99
- }
100
-
101
- logger.debug(`Deleted session ${sessionKey}`);
102
- } catch (error) {
103
- logger.error(`Failed to delete session ${sessionKey}:`, error);
104
- throw error;
105
- }
106
- }
107
-
108
- async getByThread(
109
- channelId: string,
110
- threadTs: string
111
- ): Promise<ThreadSession | null> {
112
- try {
113
- const indexKey = this.getThreadIndexKey(channelId, threadTs);
114
- const indexData = await this.redis.get(indexKey);
115
-
116
- if (!indexData) {
117
- return null;
118
- }
119
-
120
- const index = JSON.parse(indexData) as { sessionKey: string };
121
- return await this.get(index.sessionKey);
122
- } catch (error) {
123
- logger.error(
124
- `Failed to get session by thread ${channelId}:${threadTs}:`,
125
- error
126
- );
127
- return null;
128
- }
129
- }
130
-
131
- /** Optional cleanup - Redis handles this via TTL */
132
- async cleanup?(): Promise<number> {
133
- logger.debug("Redis TTL handles automatic cleanup");
134
- return 0;
135
- }
136
- }
137
-
138
- /**
139
- * Session manager that abstracts session storage
140
- * Provides thread ownership validation and session lifecycle management
141
- */
142
- export class SessionManager implements ISessionManager {
143
- private store: SessionStore;
144
-
145
- constructor(store: SessionStore) {
146
- this.store = store;
147
- }
148
-
149
- /**
150
- * Create a new session
151
- */
152
- async createSession(
153
- channelId: string,
154
- userId: string,
155
- conversationId?: string,
156
- threadCreator?: string
157
- ): Promise<ThreadSession> {
158
- const effectiveConversationId = conversationId || userId;
159
- const session: ThreadSession = {
160
- conversationId: effectiveConversationId,
161
- channelId,
162
- userId,
163
- threadCreator: threadCreator || userId,
164
- lastActivity: Date.now(),
165
- createdAt: Date.now(),
166
- };
167
- const sessionKey = computeSessionKey(session);
168
- await this.store.set(sessionKey, session);
169
- return session;
170
- }
171
-
172
- /**
173
- * Update session
174
- */
175
- async updateSession(
176
- sessionKey: string,
177
- updates: Partial<ThreadSession>
178
- ): Promise<void> {
179
- const session = await this.getSession(sessionKey);
180
- if (session) {
181
- const updated = { ...session, ...updates };
182
- await this.store.set(sessionKey, updated);
183
- }
184
- }
185
-
186
- /**
187
- * Get session by session key
188
- */
189
- async getSession(sessionKey: string): Promise<ThreadSession | null> {
190
- return await this.store.get(sessionKey);
191
- }
192
-
193
- /**
194
- * Create or update a session
195
- */
196
- async setSession(session: ThreadSession): Promise<void> {
197
- const sessionKey = computeSessionKey(session);
198
- await this.store.set(sessionKey, session);
199
- }
200
-
201
- /**
202
- * Delete a session
203
- */
204
- async deleteSession(sessionKey: string): Promise<void> {
205
- await this.store.delete(sessionKey);
206
- }
207
-
208
- /**
209
- * Find session by thread
210
- */
211
- async findSessionByThread(
212
- channelId: string,
213
- threadTs: string
214
- ): Promise<ThreadSession | null> {
215
- return await this.store.getByThread(channelId, threadTs);
216
- }
217
-
218
- /**
219
- * Validate thread ownership
220
- * Returns true if the user is the thread creator or no session exists
221
- */
222
- async validateThreadOwnership(
223
- channelId: string,
224
- threadTs: string,
225
- userId: string
226
- ): Promise<{ allowed: boolean; owner?: string }> {
227
- const session = await this.findSessionByThread(channelId, threadTs);
228
-
229
- if (!session) {
230
- return { allowed: true }; // No session, allow creation
231
- }
232
-
233
- if (!session.threadCreator) {
234
- return { allowed: true }; // No owner set, allow
235
- }
236
-
237
- if (session.threadCreator === userId) {
238
- return { allowed: true, owner: session.threadCreator };
239
- }
240
-
241
- return { allowed: false, owner: session.threadCreator };
242
- }
243
-
244
- /**
245
- * Update session activity timestamp
246
- */
247
- async touchSession(sessionKey: string): Promise<void> {
248
- const session = await this.getSession(sessionKey);
249
- if (session) {
250
- session.lastActivity = Date.now();
251
- await this.setSession(session);
252
- }
253
- }
254
-
255
- /**
256
- * Cleanup expired sessions (for in-memory stores)
257
- * Note: Redis-based stores handle this automatically via TTL
258
- */
259
- async cleanupExpired(ttl: number): Promise<number> {
260
- return (await this.store.cleanup?.(ttl)) || 0;
261
- }
262
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * SettingsResolver — resolves effective agent settings with template fallback.
3
- *
4
- * Extracted from the store layer so sub-stores stay single-domain.
5
- * Orchestrates across AgentConfigStore (settings + metadata) and
6
- * AgentConnectionStore (connections) for template resolution.
7
- */
8
-
9
- import type {
10
- AgentConfigStore,
11
- AgentConnectionStore,
12
- AgentSettings,
13
- } from "@lobu/core";
14
-
15
- export class SettingsResolver {
16
- constructor(
17
- private readonly config: AgentConfigStore,
18
- private readonly connections: AgentConnectionStore
19
- ) {}
20
-
21
- /**
22
- * Get effective settings for an agent, with template agent fallback.
23
- * For sandbox agents, inherits from the template agent when own settings
24
- * are missing or have no providers configured.
25
- */
26
- async getEffectiveSettings(agentId: string): Promise<AgentSettings | null> {
27
- const settings = await this.config.getSettings(agentId);
28
-
29
- // If settings exist and have providers, use them directly
30
- if (settings?.installedProviders?.length) return settings;
31
-
32
- // Resolve template agent ID
33
- const templateAgentId = await this.resolveTemplateAgentId(
34
- agentId,
35
- settings
36
- );
37
- if (!templateAgentId) return settings;
38
-
39
- const templateSettings = await this.config.getSettings(templateAgentId);
40
- if (!templateSettings) return settings;
41
-
42
- // Merge: own settings override template, but inherit missing fields
43
- if (!settings) {
44
- return { ...templateSettings, templateAgentId };
45
- }
46
-
47
- return {
48
- ...templateSettings,
49
- ...Object.fromEntries(
50
- Object.entries(settings).filter(([, v]) => v !== undefined)
51
- ),
52
- templateAgentId,
53
- } as AgentSettings;
54
- }
55
-
56
- /**
57
- * Resolve the template agent ID for a sandbox agent.
58
- * Chain: settings.templateAgentId → metadata.parentConnectionId → connection.templateAgentId
59
- */
60
- private async resolveTemplateAgentId(
61
- agentId: string,
62
- settings: AgentSettings | null
63
- ): Promise<string | undefined> {
64
- if (settings?.templateAgentId) return settings.templateAgentId;
65
-
66
- const metadata = await this.config.getMetadata(agentId);
67
- if (!metadata?.parentConnectionId) return undefined;
68
-
69
- const conn = await this.connections.getConnection(
70
- metadata.parentConnectionId
71
- );
72
- return conn?.templateAgentId;
73
- }
74
- }
@@ -1,89 +0,0 @@
1
- /**
2
- * Resolves system config from system-skills.json.
3
- * Handles provider configs and MCP server resolution.
4
- *
5
- * NOTE: Integration OAuth config resolution (getIntegrationConfig, isOAuthConfigured,
6
- * overlayAgentOAuthCredentials, getSkillScopesForIntegration) has been removed.
7
- * OAuth for third-party APIs is now handled by Owletto.
8
- */
9
- import type { ProviderConfigEntry } from "@lobu/core";
10
- import type { SystemSkillsService } from "./system-skills-service";
11
-
12
- export interface ResolvedMcpRegistryServer {
13
- id: string;
14
- name: string;
15
- description: string;
16
- type: "oauth" | "stdio" | "sse" | "api-key";
17
- config: Record<string, unknown>;
18
- }
19
-
20
- export class SystemConfigResolver {
21
- constructor(private readonly systemSkillsService: SystemSkillsService) {}
22
-
23
- async getProviderConfigs(): Promise<Record<string, ProviderConfigEntry>> {
24
- return this.systemSkillsService.getProviderConfigs();
25
- }
26
-
27
- async getGlobalMcpServers(): Promise<
28
- Record<string, Record<string, unknown>>
29
- > {
30
- const systemSkills = await this.systemSkillsService.getSystemSkills();
31
- const mcpServers: Record<string, Record<string, unknown>> = {};
32
-
33
- for (const skill of systemSkills) {
34
- for (const mcp of skill.mcpServers || []) {
35
- if (!mcp?.id || mcpServers[mcp.id]) continue;
36
-
37
- const type = mcp.type || (mcp.command ? "stdio" : "sse");
38
- const config: Record<string, unknown> = { type };
39
-
40
- if (mcp.url) config.url = mcp.url;
41
- if (mcp.command) config.command = mcp.command;
42
- if (Array.isArray(mcp.args) && mcp.args.length > 0) {
43
- config.args = [...mcp.args];
44
- }
45
- if (mcp.oauth) config.oauth = mcp.oauth;
46
- if (mcp.inputs) config.inputs = mcp.inputs;
47
- if (mcp.headers) config.headers = mcp.headers;
48
-
49
- mcpServers[mcp.id] = config;
50
- }
51
- }
52
-
53
- return mcpServers;
54
- }
55
-
56
- async getMcpRegistryServers(): Promise<ResolvedMcpRegistryServer[]> {
57
- const systemSkills = await this.systemSkillsService.getSystemSkills();
58
- const entries: ResolvedMcpRegistryServer[] = [];
59
- const seenIds = new Set<string>();
60
-
61
- for (const skill of systemSkills) {
62
- for (const mcp of skill.mcpServers || []) {
63
- if (!mcp?.id || seenIds.has(mcp.id)) continue;
64
- seenIds.add(mcp.id);
65
-
66
- const type = mcp.type || (mcp.command ? "stdio" : "sse");
67
- const config: Record<string, unknown> = {
68
- type,
69
- };
70
-
71
- if (mcp.url) config.url = mcp.url;
72
- if (mcp.command) config.command = mcp.command;
73
- if (Array.isArray(mcp.args) && mcp.args.length > 0) {
74
- config.args = [...mcp.args];
75
- }
76
-
77
- entries.push({
78
- id: mcp.id,
79
- name: mcp.name || mcp.id,
80
- description: skill.description || `${skill.name} MCP server`,
81
- type,
82
- config,
83
- });
84
- }
85
- }
86
-
87
- return entries;
88
- }
89
- }