@lobu/gateway 3.0.8 → 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.
- package/dist/api/platform.d.ts.map +1 -1
- package/dist/api/platform.js +8 -26
- package/dist/api/platform.js.map +1 -1
- package/dist/auth/mcp/proxy.d.ts +14 -0
- package/dist/auth/mcp/proxy.d.ts.map +1 -1
- package/dist/auth/mcp/proxy.js +149 -13
- package/dist/auth/mcp/proxy.js.map +1 -1
- package/dist/cli/gateway.d.ts.map +1 -1
- package/dist/cli/gateway.js +29 -0
- package/dist/cli/gateway.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/connections/chat-instance-manager.d.ts.map +1 -1
- package/dist/connections/chat-instance-manager.js +2 -1
- package/dist/connections/chat-instance-manager.js.map +1 -1
- package/dist/connections/interaction-bridge.d.ts +9 -2
- package/dist/connections/interaction-bridge.d.ts.map +1 -1
- package/dist/connections/interaction-bridge.js +132 -230
- package/dist/connections/interaction-bridge.js.map +1 -1
- package/dist/connections/message-handler-bridge.d.ts.map +1 -1
- package/dist/connections/message-handler-bridge.js +44 -26
- package/dist/connections/message-handler-bridge.js.map +1 -1
- package/dist/interactions.d.ts +9 -43
- package/dist/interactions.d.ts.map +1 -1
- package/dist/interactions.js +10 -52
- package/dist/interactions.js.map +1 -1
- package/dist/orchestration/base-deployment-manager.js +7 -7
- package/dist/orchestration/base-deployment-manager.js.map +1 -1
- package/dist/platform/unified-thread-consumer.d.ts.map +1 -1
- package/dist/platform/unified-thread-consumer.js +38 -34
- package/dist/platform/unified-thread-consumer.js.map +1 -1
- package/dist/routes/public/agent.d.ts +4 -0
- package/dist/routes/public/agent.d.ts.map +1 -1
- package/dist/routes/public/agent.js +21 -0
- package/dist/routes/public/agent.js.map +1 -1
- package/dist/services/core-services.d.ts.map +1 -1
- package/dist/services/core-services.js +4 -0
- package/dist/services/core-services.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/agent-config-routes.test.ts +0 -254
- package/src/__tests__/agent-history-routes.test.ts +0 -72
- package/src/__tests__/agent-routes.test.ts +0 -68
- package/src/__tests__/agent-schedules-routes.test.ts +0 -59
- package/src/__tests__/agent-settings-store.test.ts +0 -323
- package/src/__tests__/bedrock-model-catalog.test.ts +0 -40
- package/src/__tests__/bedrock-openai-service.test.ts +0 -157
- package/src/__tests__/bedrock-provider-module.test.ts +0 -56
- package/src/__tests__/chat-instance-manager-slack.test.ts +0 -204
- package/src/__tests__/chat-response-bridge.test.ts +0 -131
- package/src/__tests__/config-memory-plugins.test.ts +0 -92
- package/src/__tests__/config-request-store.test.ts +0 -127
- package/src/__tests__/connection-routes.test.ts +0 -144
- package/src/__tests__/core-services-store-selection.test.ts +0 -92
- package/src/__tests__/docker-deployment.test.ts +0 -1211
- package/src/__tests__/embedded-deployment.test.ts +0 -342
- package/src/__tests__/grant-store.test.ts +0 -148
- package/src/__tests__/http-proxy.test.ts +0 -281
- package/src/__tests__/instruction-service.test.ts +0 -37
- package/src/__tests__/link-buttons.test.ts +0 -112
- package/src/__tests__/lobu.test.ts +0 -32
- package/src/__tests__/mcp-config-service.test.ts +0 -347
- package/src/__tests__/mcp-proxy.test.ts +0 -694
- package/src/__tests__/message-handler-bridge.test.ts +0 -17
- package/src/__tests__/model-selection.test.ts +0 -172
- package/src/__tests__/oauth-templates.test.ts +0 -39
- package/src/__tests__/platform-adapter-slack-send.test.ts +0 -114
- package/src/__tests__/platform-helpers-model-resolution.test.ts +0 -253
- package/src/__tests__/provider-inheritance.test.ts +0 -212
- package/src/__tests__/routes/cli-auth.test.ts +0 -337
- package/src/__tests__/routes/interactions.test.ts +0 -121
- package/src/__tests__/secret-proxy.test.ts +0 -85
- package/src/__tests__/session-manager.test.ts +0 -572
- package/src/__tests__/setup.ts +0 -133
- package/src/__tests__/skill-and-mcp-registry.test.ts +0 -203
- package/src/__tests__/slack-routes.test.ts +0 -161
- package/src/__tests__/system-config-resolver.test.ts +0 -75
- package/src/__tests__/system-message-limiter.test.ts +0 -89
- package/src/__tests__/system-skills-service.test.ts +0 -362
- package/src/__tests__/transcription-service.test.ts +0 -222
- package/src/__tests__/utils/rate-limiter.test.ts +0 -102
- package/src/__tests__/worker-connection-manager.test.ts +0 -497
- package/src/__tests__/worker-job-router.test.ts +0 -722
- package/src/api/index.ts +0 -1
- package/src/api/platform.ts +0 -292
- package/src/api/response-renderer.ts +0 -157
- package/src/auth/agent-metadata-store.ts +0 -168
- package/src/auth/api-auth-middleware.ts +0 -69
- package/src/auth/api-key-provider-module.ts +0 -213
- package/src/auth/base-provider-module.ts +0 -201
- package/src/auth/bedrock/provider-module.ts +0 -110
- package/src/auth/chatgpt/chatgpt-oauth-module.ts +0 -185
- package/src/auth/chatgpt/device-code-client.ts +0 -218
- package/src/auth/chatgpt/index.ts +0 -1
- package/src/auth/claude/oauth-module.ts +0 -280
- package/src/auth/cli/token-service.ts +0 -249
- package/src/auth/external/client.ts +0 -560
- package/src/auth/external/device-code-client.ts +0 -235
- package/src/auth/mcp/config-service.ts +0 -420
- package/src/auth/mcp/proxy.ts +0 -1086
- package/src/auth/mcp/string-substitution.ts +0 -17
- package/src/auth/mcp/tool-cache.ts +0 -90
- package/src/auth/oauth/base-client.ts +0 -267
- package/src/auth/oauth/client.ts +0 -153
- package/src/auth/oauth/credentials.ts +0 -7
- package/src/auth/oauth/providers.ts +0 -69
- package/src/auth/oauth/state-store.ts +0 -150
- package/src/auth/oauth-templates.ts +0 -179
- package/src/auth/provider-catalog.ts +0 -220
- package/src/auth/provider-model-options.ts +0 -41
- package/src/auth/settings/agent-settings-store.ts +0 -565
- package/src/auth/settings/auth-profiles-manager.ts +0 -216
- package/src/auth/settings/index.ts +0 -12
- package/src/auth/settings/model-preference-store.ts +0 -52
- package/src/auth/settings/model-selection.ts +0 -135
- package/src/auth/settings/resolved-settings-view.ts +0 -298
- package/src/auth/settings/template-utils.ts +0 -44
- package/src/auth/settings/token-service.ts +0 -88
- package/src/auth/system-env-store.ts +0 -98
- package/src/auth/user-agents-store.ts +0 -68
- package/src/channels/binding-service.ts +0 -214
- package/src/channels/index.ts +0 -4
- package/src/cli/gateway.ts +0 -1312
- package/src/cli/index.ts +0 -74
- package/src/commands/built-in-commands.ts +0 -80
- package/src/commands/command-dispatcher.ts +0 -94
- package/src/commands/command-reply-adapters.ts +0 -27
- package/src/config/file-loader.ts +0 -618
- package/src/config/index.ts +0 -588
- package/src/config/network-allowlist.ts +0 -71
- package/src/connections/chat-instance-manager.ts +0 -1284
- package/src/connections/chat-response-bridge.ts +0 -618
- package/src/connections/index.ts +0 -7
- package/src/connections/interaction-bridge.ts +0 -831
- package/src/connections/message-handler-bridge.ts +0 -415
- package/src/connections/platform-auth-methods.ts +0 -15
- package/src/connections/types.ts +0 -84
- package/src/gateway/connection-manager.ts +0 -291
- package/src/gateway/index.ts +0 -698
- package/src/gateway/job-router.ts +0 -201
- package/src/gateway-main.ts +0 -200
- package/src/index.ts +0 -41
- package/src/infrastructure/queue/index.ts +0 -12
- package/src/infrastructure/queue/queue-producer.ts +0 -148
- package/src/infrastructure/queue/redis-queue.ts +0 -361
- package/src/infrastructure/queue/types.ts +0 -133
- package/src/infrastructure/redis/system-message-limiter.ts +0 -94
- package/src/interactions/config-request-store.ts +0 -198
- package/src/interactions.ts +0 -363
- package/src/lobu.ts +0 -311
- package/src/metrics/prometheus.ts +0 -159
- package/src/modules/module-system.ts +0 -179
- package/src/orchestration/base-deployment-manager.ts +0 -900
- package/src/orchestration/deployment-utils.ts +0 -98
- package/src/orchestration/impl/docker-deployment.ts +0 -620
- package/src/orchestration/impl/embedded-deployment.ts +0 -268
- package/src/orchestration/impl/index.ts +0 -8
- package/src/orchestration/impl/k8s/deployment.ts +0 -1061
- package/src/orchestration/impl/k8s/helpers.ts +0 -610
- package/src/orchestration/impl/k8s/index.ts +0 -1
- package/src/orchestration/index.ts +0 -333
- package/src/orchestration/message-consumer.ts +0 -584
- package/src/orchestration/scheduled-wakeup.ts +0 -704
- package/src/permissions/approval-policy.ts +0 -36
- package/src/permissions/grant-store.ts +0 -219
- package/src/platform/file-handler.ts +0 -66
- package/src/platform/link-buttons.ts +0 -57
- package/src/platform/renderer-utils.ts +0 -44
- package/src/platform/response-renderer.ts +0 -84
- package/src/platform/unified-thread-consumer.ts +0 -187
- package/src/platform.ts +0 -318
- package/src/proxy/http-proxy.ts +0 -752
- package/src/proxy/proxy-manager.ts +0 -81
- package/src/proxy/secret-proxy.ts +0 -402
- package/src/proxy/token-refresh-job.ts +0 -143
- package/src/routes/internal/audio.ts +0 -141
- package/src/routes/internal/device-auth.ts +0 -652
- package/src/routes/internal/files.ts +0 -226
- package/src/routes/internal/history.ts +0 -69
- package/src/routes/internal/images.ts +0 -127
- package/src/routes/internal/interactions.ts +0 -84
- package/src/routes/internal/middleware.ts +0 -23
- package/src/routes/internal/schedule.ts +0 -226
- package/src/routes/internal/types.ts +0 -22
- package/src/routes/openapi-auto.ts +0 -239
- package/src/routes/public/agent-access.ts +0 -23
- package/src/routes/public/agent-config.ts +0 -675
- package/src/routes/public/agent-history.ts +0 -422
- package/src/routes/public/agent-schedules.ts +0 -296
- package/src/routes/public/agent.ts +0 -1086
- package/src/routes/public/agents.ts +0 -373
- package/src/routes/public/channels.ts +0 -191
- package/src/routes/public/cli-auth.ts +0 -896
- package/src/routes/public/connections.ts +0 -574
- package/src/routes/public/landing.ts +0 -16
- package/src/routes/public/oauth.ts +0 -147
- package/src/routes/public/settings-auth.ts +0 -104
- package/src/routes/public/slack.ts +0 -173
- package/src/routes/shared/agent-ownership.ts +0 -101
- package/src/routes/shared/token-verifier.ts +0 -34
- package/src/services/bedrock-model-catalog.ts +0 -217
- package/src/services/bedrock-openai-service.ts +0 -658
- package/src/services/core-services.ts +0 -1072
- package/src/services/image-generation-service.ts +0 -257
- package/src/services/instruction-service.ts +0 -318
- package/src/services/mcp-registry.ts +0 -94
- package/src/services/platform-helpers.ts +0 -287
- package/src/services/session-manager.ts +0 -262
- package/src/services/settings-resolver.ts +0 -74
- package/src/services/system-config-resolver.ts +0 -89
- package/src/services/system-skills-service.ts +0 -229
- package/src/services/transcription-service.ts +0 -684
- package/src/session.ts +0 -110
- package/src/spaces/index.ts +0 -1
- package/src/spaces/space-resolver.ts +0 -17
- package/src/stores/in-memory-agent-store.ts +0 -403
- package/src/stores/redis-agent-store.ts +0 -279
- package/src/utils/public-url.ts +0 -44
- package/src/utils/rate-limiter.ts +0 -94
- package/tsconfig.json +0 -33
|
@@ -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
|
-
}
|