@exreve/exk 1.0.26 → 1.0.28
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/agentSession.js +159 -55
- package/dist/app-child.js +2590 -0
- package/dist/index.js +219 -25
- package/dist/moduleMcpServer.js +77 -23
- package/dist/ttc-cli.tar.gz +0 -0
- package/dist/updater.js +425 -0
- package/package.json +2 -2
package/dist/agentSession.js
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from '
|
|
|
4
4
|
import { symlink as fsSymlink } from 'fs';
|
|
5
5
|
import { getSkillContent } from './skills/index.js';
|
|
6
6
|
import { isLocalModel, unwrapModelName, startOpenAIAdapter, getAdapterConfig } from './openaiAdapter.js';
|
|
7
|
-
import { createModuleMcpServer
|
|
7
|
+
import { createModuleMcpServer } from './moduleMcpServer.js';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import os from 'os';
|
|
10
10
|
import { createRequire } from 'module';
|
|
@@ -127,6 +127,48 @@ function lookupToolNameFromHistory(messages, toolUseId) {
|
|
|
127
127
|
// (Do not read ANTHROPIC_* / CLAUDE_MODEL from the host environment — only this file + code default model.)
|
|
128
128
|
const AI_CONFIG_PATH = path.join(os.homedir(), '.talk-to-code', 'ai-config.json');
|
|
129
129
|
const DEFAULT_AI_MODEL = 'glm-5.1';
|
|
130
|
+
const PROVIDERS = {
|
|
131
|
+
zai: {
|
|
132
|
+
apiKey: process.env.ZHIPU_API_KEY || '',
|
|
133
|
+
baseUrl: process.env.CLI_AI_BASE_URL || 'https://api.z.ai/api/anthropic',
|
|
134
|
+
models: ['glm-5.1', 'glm-4.7', 'glm-4.5-air'],
|
|
135
|
+
},
|
|
136
|
+
minimax: {
|
|
137
|
+
apiKey: '', // Populated from ai-config.json (served by backend)
|
|
138
|
+
baseUrl: 'https://api.minimax.io/anthropic',
|
|
139
|
+
models: ['MiniMax-M2.7', 'MiniMax-M2.7-highspeed'],
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
/** Resolve which provider to use based on model name or explicit provider ID.
|
|
143
|
+
* 1. Populate provider API keys from ai-config.json (served by backend).
|
|
144
|
+
* 2. If explicit providerId is given and has an API key configured, use that provider.
|
|
145
|
+
* 3. Else if model name matches one of a provider's model list, use that provider.
|
|
146
|
+
* 4. Else fall back to zai (default). */
|
|
147
|
+
function resolveProvider(model, providerId) {
|
|
148
|
+
// Populate provider keys from ai-config.json
|
|
149
|
+
const aiConfig = loadAiConfig();
|
|
150
|
+
PROVIDERS.minimax.apiKey = aiConfig.minimaxApiKey || process.env.MINIMAX_API_KEY || '';
|
|
151
|
+
if (!PROVIDERS.zai.apiKey)
|
|
152
|
+
PROVIDERS.zai.apiKey = aiConfig.apiKey || '';
|
|
153
|
+
// 1. Explicit provider selection
|
|
154
|
+
if (providerId && PROVIDERS[providerId]?.apiKey) {
|
|
155
|
+
const provider = PROVIDERS[providerId];
|
|
156
|
+
return { provider: providerId, apiKey: provider.apiKey, baseUrl: provider.baseUrl, model };
|
|
157
|
+
}
|
|
158
|
+
// 2. Match model name to a provider
|
|
159
|
+
for (const [id, config] of Object.entries(PROVIDERS)) {
|
|
160
|
+
if (config.models.includes(model) && config.apiKey) {
|
|
161
|
+
return { provider: id, apiKey: config.apiKey, baseUrl: config.baseUrl, model };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// 3. Fallback: use ai-config.json credentials (z.ai default)
|
|
165
|
+
return {
|
|
166
|
+
provider: 'zai',
|
|
167
|
+
apiKey: aiConfig.apiKey,
|
|
168
|
+
baseUrl: aiConfig.baseUrl || PROVIDERS.zai.baseUrl,
|
|
169
|
+
model,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
130
172
|
function loadAiConfig() {
|
|
131
173
|
try {
|
|
132
174
|
const data = readFileSync(AI_CONFIG_PATH, 'utf-8');
|
|
@@ -134,10 +176,12 @@ function loadAiConfig() {
|
|
|
134
176
|
const apiKey = typeof config.authToken === 'string' ? config.authToken.trim() : '';
|
|
135
177
|
const baseUrl = typeof config.baseUrl === 'string' ? config.baseUrl.trim() : '';
|
|
136
178
|
const model = typeof config.model === 'string' && config.model.trim() ? config.model.trim() : DEFAULT_AI_MODEL;
|
|
137
|
-
|
|
179
|
+
const proxy = typeof config.proxy === 'string' ? config.proxy.trim() : '';
|
|
180
|
+
const minimaxApiKey = typeof config.minimaxApiKey === 'string' ? config.minimaxApiKey.trim() : '';
|
|
181
|
+
return { apiKey, baseUrl, model, proxy, minimaxApiKey };
|
|
138
182
|
}
|
|
139
183
|
catch {
|
|
140
|
-
return { apiKey: '', baseUrl: '', model: DEFAULT_AI_MODEL };
|
|
184
|
+
return { apiKey: '', baseUrl: '', model: DEFAULT_AI_MODEL, proxy: '', minimaxApiKey: '' };
|
|
141
185
|
}
|
|
142
186
|
}
|
|
143
187
|
/** Create (or reuse) an empty directory to use as CLAUDE_CONFIG_DIR.
|
|
@@ -151,32 +195,66 @@ function getEmptyConfigDir() {
|
|
|
151
195
|
}
|
|
152
196
|
return configDir;
|
|
153
197
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
function
|
|
198
|
+
const PROXY_CONFIG_PATH = path.join(os.homedir(), '.talk-to-code', 'proxy.json');
|
|
199
|
+
/** Read proxy toggle state from disk (synchronous) */
|
|
200
|
+
function readProxyToggle() {
|
|
201
|
+
try {
|
|
202
|
+
const data = readFileSync(PROXY_CONFIG_PATH, 'utf-8');
|
|
203
|
+
return JSON.parse(data);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return { enabled: false };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/** Env for the Claude Code child: copy of host env with host ANTHROPIC_* stripped, then inject from provider or ai-config.
|
|
210
|
+
* If a local model is provided, override baseUrl to point to the anthropic-proxy adapter.
|
|
211
|
+
* If resolvedProvider is provided, use its credentials instead of ai-config defaults. */
|
|
212
|
+
function envForClaudeCodeChild(localModel, resolvedProvider) {
|
|
157
213
|
const env = { ...process.env };
|
|
158
214
|
// Strip any host ANTHROPIC_* vars to prevent leaking credentials or wrong URLs
|
|
159
215
|
delete env.ANTHROPIC_API_KEY;
|
|
160
216
|
delete env.ANTHROPIC_BASE_URL;
|
|
161
217
|
delete env.ANTHROPIC_AUTH_TOKEN;
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
218
|
+
// Also strip model alias env vars to prevent stale overrides
|
|
219
|
+
delete env.ANTHROPIC_MODEL;
|
|
220
|
+
delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
221
|
+
delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
222
|
+
delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
223
|
+
// Determine credentials: use resolvedProvider if provided, else ai-config defaults
|
|
224
|
+
const { apiKey, baseUrl, proxy } = loadAiConfig();
|
|
225
|
+
const effectiveApiKey = resolvedProvider?.apiKey || apiKey;
|
|
226
|
+
const effectiveBaseUrl = resolvedProvider?.baseUrl || baseUrl;
|
|
227
|
+
if (effectiveApiKey)
|
|
228
|
+
env.ANTHROPIC_API_KEY = effectiveApiKey;
|
|
229
|
+
if (effectiveBaseUrl)
|
|
230
|
+
env.ANTHROPIC_BASE_URL = effectiveBaseUrl;
|
|
231
|
+
// For MiniMax specifically: override ALL model aliases so the SDK
|
|
232
|
+
// sends the correct model ID to the Anthropic-compatible endpoint
|
|
233
|
+
if (resolvedProvider?.provider === 'minimax') {
|
|
234
|
+
env.ANTHROPIC_MODEL = resolvedProvider.model;
|
|
235
|
+
env.ANTHROPIC_DEFAULT_SONNET_MODEL = resolvedProvider.model;
|
|
236
|
+
env.ANTHROPIC_DEFAULT_OPUS_MODEL = resolvedProvider.model;
|
|
237
|
+
env.ANTHROPIC_DEFAULT_HAIKU_MODEL = resolvedProvider.model;
|
|
238
|
+
env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
|
|
239
|
+
}
|
|
240
|
+
// Apply proxy if enabled
|
|
241
|
+
const proxyToggle = readProxyToggle();
|
|
242
|
+
if (proxyToggle.enabled && proxy) {
|
|
243
|
+
env.HTTPS_PROXY = proxy;
|
|
244
|
+
env.HTTP_PROXY = proxy;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Clear any inherited proxy env
|
|
248
|
+
delete env.HTTPS_PROXY;
|
|
249
|
+
delete env.HTTP_PROXY;
|
|
250
|
+
delete env.https_proxy;
|
|
251
|
+
delete env.http_proxy;
|
|
252
|
+
}
|
|
173
253
|
// Prevent ~/.claude/settings.json env section from overriding our base URL.
|
|
174
254
|
// This redirects the Claude config dir to an empty dir so that
|
|
175
255
|
// ~/.claude/settings.json (which may have ANTHROPIC_BASE_URL set to z.ai)
|
|
176
256
|
// is never read during the CLI process initialization.
|
|
177
257
|
env.CLAUDE_CONFIG_DIR = getEmptyConfigDir();
|
|
178
|
-
// Allow Claude to run as root (e.g. inside Docker containers)
|
|
179
|
-
env.IS_SANDBOX = '1';
|
|
180
258
|
return env;
|
|
181
259
|
}
|
|
182
260
|
/** Get env overrides for a local model (adapter proxy URL + dummy key). Returns null if not a local model. */
|
|
@@ -316,7 +394,8 @@ export class AgentSessionManager {
|
|
|
316
394
|
timestamp: Date.now(),
|
|
317
395
|
promptId: handler.promptId,
|
|
318
396
|
abortController: new AbortController(), // Pre-create for queued cancellation
|
|
319
|
-
model: handler.model || session.model // Use handler model or fall back to session model
|
|
397
|
+
model: handler.model || session.model, // Use handler model or fall back to session model
|
|
398
|
+
attachments: handler.attachments // Pass attachments through
|
|
320
399
|
});
|
|
321
400
|
// Start processing queue if not already processing
|
|
322
401
|
if (!session.isProcessingQueue) {
|
|
@@ -339,13 +418,30 @@ export class AgentSessionManager {
|
|
|
339
418
|
session.isProcessingQueue = true;
|
|
340
419
|
while (session.promptQueue.length > 0 && !this.emergencyStopInProgress.has(sessionId)) {
|
|
341
420
|
const queuedPrompt = session.promptQueue.shift();
|
|
342
|
-
const {
|
|
421
|
+
const { enhancers, handler, promptId: queuedPromptId, abortController: queuedAbortController } = queuedPrompt;
|
|
343
422
|
const { projectPath, promptId, onOutput, onError, onComplete, onStatusUpdate } = handler;
|
|
344
423
|
const promptStartTime = Date.now();
|
|
424
|
+
// Write attachments to temp dir and inject paths into prompt
|
|
425
|
+
let effectivePrompt = queuedPrompt.prompt;
|
|
426
|
+
let attachmentDir;
|
|
427
|
+
if (queuedPrompt.attachments && queuedPrompt.attachments.length > 0) {
|
|
428
|
+
attachmentDir = path.join(os.tmpdir(), 'talk-to-code', 'attachments', sessionId, String(promptId || Date.now()));
|
|
429
|
+
mkdirSync(attachmentDir, { recursive: true });
|
|
430
|
+
const attachmentLines = [];
|
|
431
|
+
for (const att of queuedPrompt.attachments) {
|
|
432
|
+
const safeName = att.filename.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
433
|
+
const filePath = path.join(attachmentDir, safeName);
|
|
434
|
+
const buf = Buffer.from(att.content, 'base64');
|
|
435
|
+
writeFileSync(filePath, buf);
|
|
436
|
+
attachmentLines.push(`- ${safeName} (${att.mimeType}): path="${filePath}"`);
|
|
437
|
+
}
|
|
438
|
+
effectivePrompt += `\n\n[Attachments]\nThe following files are attached and available on disk. Use the analyze_image tool to examine images.\n${attachmentLines.join('\n')}`;
|
|
439
|
+
console.log(`[agentSession] Wrote ${queuedPrompt.attachments.length} attachment(s) to ${attachmentDir}`);
|
|
440
|
+
}
|
|
345
441
|
try {
|
|
346
442
|
// Verify promptId is present in handler
|
|
347
443
|
if (!promptId) {
|
|
348
|
-
console.error(`[agentSession] Missing promptId in handler for prompt: ${prompt.substring(0, 50)}...`);
|
|
444
|
+
console.error(`[agentSession] Missing promptId in handler for prompt: ${queuedPrompt.prompt.substring(0, 50)}...`);
|
|
349
445
|
onError?.('Missing promptId in handler');
|
|
350
446
|
continue;
|
|
351
447
|
}
|
|
@@ -375,11 +471,11 @@ export class AgentSessionManager {
|
|
|
375
471
|
}
|
|
376
472
|
session.activeQueryStream = undefined;
|
|
377
473
|
// Build final prompt with enhancers
|
|
378
|
-
let finalPrompt =
|
|
474
|
+
let finalPrompt = effectivePrompt;
|
|
379
475
|
if (enhancers && enhancers.length > 0) {
|
|
380
476
|
const skillContent = getSkillContent(enhancers);
|
|
381
477
|
if (skillContent) {
|
|
382
|
-
finalPrompt = `${skillContent}\n\n${
|
|
478
|
+
finalPrompt = `${skillContent}\n\n${effectivePrompt}`;
|
|
383
479
|
}
|
|
384
480
|
}
|
|
385
481
|
// Add user message to history
|
|
@@ -418,25 +514,16 @@ export class AgentSessionManager {
|
|
|
418
514
|
apiKey: CLAUDE_CONFIG.apiKey,
|
|
419
515
|
model: CLAUDE_CONFIG.model,
|
|
420
516
|
tools: { type: 'preset', preset: 'claude_code' },
|
|
421
|
-
disallowedTools:
|
|
517
|
+
disallowedTools: attachmentDir
|
|
518
|
+
? ['AskUserQuestion', 'analyze_image'] // Disable built-in analyze_image when we have our own
|
|
519
|
+
: ['AskUserQuestion'],
|
|
422
520
|
settingSources: ['project'], // Enable CLAUDE.md loading
|
|
423
521
|
permissionMode: 'bypassPermissions',
|
|
424
522
|
allowDangerouslySkipPermissions: true,
|
|
425
523
|
// Create a fresh MCP server for each query call (SDK connects transport internally, cannot reuse)
|
|
426
524
|
...(() => {
|
|
427
|
-
const mcpServer = this.buildMcpServer(sessionId);
|
|
428
|
-
|
|
429
|
-
const toolHint = getModuleToolHint(session.enabledModules || []);
|
|
430
|
-
console.log(`[agentSession] MCP server created: name=${mcpServer.name}, hasHint=${!!toolHint}`);
|
|
431
|
-
console.log(`[agentSession] MCP server keys:`, Object.keys(mcpServer));
|
|
432
|
-
console.log(`[agentSession] MCP server type:`, mcpServer.type);
|
|
433
|
-
return {
|
|
434
|
-
mcpServers: { [mcpServer.name]: mcpServer },
|
|
435
|
-
...(toolHint ? { systemPrompt: { type: 'preset', preset: 'claude_code', append: toolHint } } : {})
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
console.log(`[agentSession] No MCP server created (enabledModules=${JSON.stringify(session.enabledModules)})`);
|
|
439
|
-
return {};
|
|
525
|
+
const mcpServer = this.buildMcpServer(sessionId, attachmentDir);
|
|
526
|
+
return mcpServer ? { mcpServers: [mcpServer] } : {};
|
|
440
527
|
})(),
|
|
441
528
|
...(pathToClaudeCodeExecutable ? { pathToClaudeCodeExecutable } : {}),
|
|
442
529
|
spawnClaudeCodeProcess: (spawnOptions) => {
|
|
@@ -582,29 +669,45 @@ export class AgentSessionManager {
|
|
|
582
669
|
console.log(`[agentSession] effectiveSettings for local model:`, JSON.stringify(effectiveSettings));
|
|
583
670
|
}
|
|
584
671
|
else {
|
|
585
|
-
//
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
672
|
+
// Resolve provider for multi-provider switching (Z.ai / MiniMax)
|
|
673
|
+
const resolved = resolveProvider(sessionModel);
|
|
674
|
+
console.log(`[agentSession] Resolved provider: ${resolved.provider} for model: ${sessionModel}`);
|
|
675
|
+
effectiveApiKey = resolved.apiKey;
|
|
676
|
+
effectiveEnv = envForClaudeCodeChild(undefined, resolved);
|
|
677
|
+
// Build settings env to prevent ~/.claude/settings.json from overriding our credentials
|
|
678
|
+
const settingsEnv = {
|
|
679
|
+
ANTHROPIC_API_KEY: resolved.apiKey,
|
|
680
|
+
ANTHROPIC_BASE_URL: resolved.baseUrl,
|
|
681
|
+
};
|
|
682
|
+
// For MiniMax: also override all model aliases in settings
|
|
683
|
+
if (resolved.provider === 'minimax') {
|
|
684
|
+
settingsEnv.ANTHROPIC_MODEL = resolved.model;
|
|
685
|
+
settingsEnv.ANTHROPIC_DEFAULT_SONNET_MODEL = resolved.model;
|
|
686
|
+
settingsEnv.ANTHROPIC_DEFAULT_OPUS_MODEL = resolved.model;
|
|
687
|
+
settingsEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL = resolved.model;
|
|
688
|
+
settingsEnv.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
|
|
594
689
|
}
|
|
690
|
+
effectiveSettings = { env: settingsEnv };
|
|
691
|
+
console.log(`[agentSession] Provider: ${resolved.provider}, baseUrl: ${resolved.baseUrl}, model: ${resolved.model}`);
|
|
595
692
|
}
|
|
596
693
|
// Create query stream - resume session if we have a Claude session ID
|
|
597
694
|
// Always explicitly set model even when resuming to ensure we use the session's model
|
|
598
695
|
const queryStream = query({
|
|
599
|
-
prompt,
|
|
696
|
+
prompt: finalPrompt,
|
|
600
697
|
options: {
|
|
601
698
|
...queryOptions,
|
|
602
699
|
apiKey: effectiveApiKey,
|
|
603
700
|
model: effectiveModel,
|
|
604
701
|
env: effectiveEnv,
|
|
605
702
|
...(effectiveSettings ? { settings: effectiveSettings } : {}),
|
|
606
|
-
...(session.claudeSessionId && !localOverrides
|
|
703
|
+
...(session.claudeSessionId && !localOverrides && (() => {
|
|
704
|
+
// Don't resume if provider changed since last session (context format may differ)
|
|
705
|
+
const persisted = loadSessionState(sessionId);
|
|
706
|
+
const currentProvider = resolveProvider(sessionModel).provider;
|
|
707
|
+
return persisted?.provider === currentProvider;
|
|
708
|
+
})() ? { resume: session.claudeSessionId } : {})
|
|
607
709
|
// Note: don't resume session for local models - context format differs
|
|
710
|
+
// Note: also don't resume if provider differs from persisted session provider (context format may differ)
|
|
608
711
|
}
|
|
609
712
|
});
|
|
610
713
|
session.activeQueryStream = queryStream;
|
|
@@ -633,7 +736,7 @@ export class AgentSessionManager {
|
|
|
633
736
|
const systemMsg = message;
|
|
634
737
|
if (systemMsg.session_id && !session.claudeSessionId) {
|
|
635
738
|
session.claudeSessionId = systemMsg.session_id;
|
|
636
|
-
saveSessionState(sessionId, { claudeSessionId: systemMsg.session_id, model: session.model, updatedAt: Date.now() });
|
|
739
|
+
saveSessionState(sessionId, { claudeSessionId: systemMsg.session_id, model: session.model, provider: resolveProvider(session.model).provider, updatedAt: Date.now() });
|
|
637
740
|
}
|
|
638
741
|
}
|
|
639
742
|
if (message.type === 'assistant') {
|
|
@@ -641,7 +744,7 @@ export class AgentSessionManager {
|
|
|
641
744
|
// Capture Claude session ID from assistant message if not already set
|
|
642
745
|
if (msg.session_id && !session.claudeSessionId) {
|
|
643
746
|
session.claudeSessionId = msg.session_id;
|
|
644
|
-
saveSessionState(sessionId, { claudeSessionId: msg.session_id, model: session.model, updatedAt: Date.now() });
|
|
747
|
+
saveSessionState(sessionId, { claudeSessionId: msg.session_id, model: session.model, provider: resolveProvider(session.model).provider, updatedAt: Date.now() });
|
|
645
748
|
}
|
|
646
749
|
session.messages.push({
|
|
647
750
|
role: 'assistant',
|
|
@@ -1144,22 +1247,23 @@ export class AgentSessionManager {
|
|
|
1144
1247
|
* The SDK's query() connects the MCP server's internal transport, so we cannot
|
|
1145
1248
|
* reuse a single instance across multiple queries. This must be called fresh each time.
|
|
1146
1249
|
*/
|
|
1147
|
-
buildMcpServer(sessionId) {
|
|
1250
|
+
buildMcpServer(sessionId, attachmentDir) {
|
|
1148
1251
|
const session = this.sessions.get(sessionId);
|
|
1149
1252
|
if (!session) {
|
|
1150
1253
|
console.log(`[buildMcpServer] No session found for ${sessionId}`);
|
|
1151
1254
|
return undefined;
|
|
1152
1255
|
}
|
|
1153
1256
|
const enabledModules = session.enabledModules || [];
|
|
1154
|
-
console.log(`[buildMcpServer] Session ${sessionId}: enabledModules=${JSON.stringify(enabledModules)}`);
|
|
1155
|
-
if (enabledModules.length === 0) {
|
|
1156
|
-
console.log(`[buildMcpServer] No enabled modules, skipping MCP server creation`);
|
|
1257
|
+
console.log(`[buildMcpServer] Session ${sessionId}: enabledModules=${JSON.stringify(enabledModules)}, attachmentDir=${attachmentDir || 'none'}`);
|
|
1258
|
+
if (enabledModules.length === 0 && !attachmentDir) {
|
|
1259
|
+
console.log(`[buildMcpServer] No enabled modules and no attachments, skipping MCP server creation`);
|
|
1157
1260
|
return undefined;
|
|
1158
1261
|
}
|
|
1159
1262
|
const handler = this.sessionHandlers.get(sessionId);
|
|
1160
1263
|
return createModuleMcpServer({
|
|
1161
1264
|
enabledModules,
|
|
1162
1265
|
moduleSettings: session.moduleSettings || {},
|
|
1266
|
+
attachmentDir,
|
|
1163
1267
|
onChoiceRequest: handler?.onChoiceRequest
|
|
1164
1268
|
? async (request) => {
|
|
1165
1269
|
return new Promise((resolve) => {
|