@exreve/exk 1.0.30 → 1.0.32

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.
@@ -320,26 +320,15 @@ export class AgentSessionManager {
320
320
  existingSession.model = model;
321
321
  }
322
322
  // Update enabled modules and settings if provided
323
- const newModules = handler.enabledModules;
324
- if (newModules) {
325
- existingSession.enabledModules = newModules;
326
- }
327
- const newModuleSettings = handler.moduleSettings;
328
- if (newModuleSettings) {
329
- existingSession.moduleSettings = newModuleSettings;
330
- }
331
- existingSession.userChoiceEnabled = handler.userChoiceEnabled || false;
332
323
  // Ensure abort controller is fresh for new queries
333
324
  existingSession.abortController = new AbortController();
334
- // Update handler to keep onChoiceRequest callback fresh
325
+ // Update handler reference
335
326
  this.sessionHandlers.set(sessionId, handler);
336
327
  return;
337
328
  }
338
329
  // Store the handler for this session
339
330
  this.sessionHandlers.set(sessionId, handler);
340
331
  const abortController = new AbortController();
341
- const enabledModules = handler.enabledModules || [];
342
- const moduleSettings = handler.moduleSettings || {};
343
332
  // Restore claudeSessionId from disk (survives CLI restart)
344
333
  const persistedState = loadSessionState(sessionId);
345
334
  const restoredClaudeSessionId = persistedState?.claudeSessionId;
@@ -359,9 +348,6 @@ export class AgentSessionManager {
359
348
  claudeProcessGroupId: undefined,
360
349
  currentPromptId: undefined,
361
350
  model: sessionModel,
362
- userChoiceEnabled: handler.userChoiceEnabled || false,
363
- enabledModules,
364
- moduleSettings
365
351
  });
366
352
  // Auto-regenerate CLAUDE.md for fresh project context
367
353
  await this.regenerateClaudeMd(projectPath);
@@ -526,7 +512,7 @@ export class AgentSessionManager {
526
512
  // Create a fresh MCP server for each query call (SDK connects transport internally, cannot reuse)
527
513
  ...(() => {
528
514
  const mcpServer = this.buildMcpServer(sessionId, attachmentDir);
529
- return mcpServer ? { mcpServers: [mcpServer] } : {};
515
+ return { mcpServers: [mcpServer] };
530
516
  })(),
531
517
  ...(pathToClaudeCodeExecutable ? { pathToClaudeCodeExecutable } : {}),
532
518
  spawnClaudeCodeProcess: (spawnOptions) => {
@@ -1228,11 +1214,6 @@ export class AgentSessionManager {
1228
1214
  session.currentPromptId = undefined;
1229
1215
  // 8. Remove from emergency stop tracking
1230
1216
  this.emergencyStopInProgress.delete(sessionId);
1231
- // 9. Resolve any pending choice request with null (cancelled)
1232
- if (session.pendingChoice) {
1233
- session.pendingChoice.resolve({ choiceId: session.pendingChoice.request.choiceId, selectedValue: null });
1234
- session.pendingChoice = undefined;
1235
- }
1236
1217
  const message = currentPromptId
1237
1218
  ? `Emergency stop: Cancelled prompt '${currentPromptId}' and cleared ${queueSize} queued prompts`
1238
1219
  : `Emergency stop: Cleared ${queueSize} queued prompts`;
@@ -1251,89 +1232,9 @@ export class AgentSessionManager {
1251
1232
  * reuse a single instance across multiple queries. This must be called fresh each time.
1252
1233
  */
1253
1234
  buildMcpServer(sessionId, attachmentDir) {
1254
- const session = this.sessions.get(sessionId);
1255
- if (!session) {
1256
- console.log(`[buildMcpServer] No session found for ${sessionId}`);
1257
- return undefined;
1258
- }
1259
- const enabledModules = session.enabledModules || [];
1260
- console.log(`[buildMcpServer] Session ${sessionId}: enabledModules=${JSON.stringify(enabledModules)}, attachmentDir=${attachmentDir || 'none'}`);
1261
- if (enabledModules.length === 0 && !attachmentDir) {
1262
- console.log(`[buildMcpServer] No enabled modules and no attachments, skipping MCP server creation`);
1263
- return undefined;
1264
- }
1265
- const handler = this.sessionHandlers.get(sessionId);
1235
+ console.log(`[buildMcpServer] Session ${sessionId}: attachmentDir=${attachmentDir || 'none'}`);
1266
1236
  return createModuleMcpServer({
1267
- enabledModules,
1268
- moduleSettings: session.moduleSettings || {},
1269
1237
  attachmentDir,
1270
- onChoiceRequest: handler?.onChoiceRequest
1271
- ? async (request) => {
1272
- return new Promise((resolve) => {
1273
- const sess = this.sessions.get(sessionId);
1274
- if (sess) {
1275
- sess.pendingChoice = { request, resolve };
1276
- handler.onChoiceRequest(request);
1277
- }
1278
- else {
1279
- resolve({ choiceId: request.choiceId, selectedValue: null });
1280
- }
1281
- });
1282
- }
1283
- : undefined
1284
- });
1285
- }
1286
- /**
1287
- * Handle user choice response from frontend
1288
- */
1289
- async handleChoiceResponse(sessionId, response) {
1290
- const session = this.sessions.get(sessionId);
1291
- if (!session) {
1292
- console.error(`[agentSession] Session ${sessionId} not found for choice response`);
1293
- return;
1294
- }
1295
- if (!session.pendingChoice) {
1296
- console.warn(`[agentSession] No pending choice for session ${sessionId}`);
1297
- return;
1298
- }
1299
- if (session.pendingChoice.request.choiceId !== response.choiceId) {
1300
- console.warn(`[agentSession] Choice ID mismatch: expected ${session.pendingChoice.request.choiceId}, got ${response.choiceId}`);
1301
- return;
1302
- }
1303
- // Resolve the pending choice promise
1304
- session.pendingChoice.resolve(response);
1305
- session.pendingChoice = undefined;
1306
- }
1307
- /**
1308
- * Request user choice during agent execution
1309
- */
1310
- async requestUserChoice(sessionId, request) {
1311
- const session = this.sessions.get(sessionId);
1312
- if (!session) {
1313
- throw new Error(`Session ${sessionId} not found`);
1314
- }
1315
- // Check if user choice is enabled for this session
1316
- if (!session.userChoiceEnabled) {
1317
- throw new Error('User choice is not enabled for this session');
1318
- }
1319
- // Create a promise that will be resolved when the user responds
1320
- return new Promise((resolve) => {
1321
- session.pendingChoice = { request, resolve };
1322
- // Emit the choice request through the onOutput callback
1323
- // This will be picked up by the CLI and sent to the frontend
1324
- const handler = this.sessionHandlers.get(sessionId);
1325
- if (handler?.onChoiceRequest) {
1326
- handler.onChoiceRequest(request);
1327
- }
1328
- // Set timeout if specified
1329
- if (request.timeout) {
1330
- setTimeout(() => {
1331
- if (session.pendingChoice?.request.choiceId === request.choiceId) {
1332
- session.pendingChoice.resolve({ choiceId: request.choiceId, selectedValue: null });
1333
- session.pendingChoice = undefined;
1334
- }
1335
- }, request.timeout);
1336
- }
1337
1238
  });
1338
1239
  }
1339
1240
  }
package/dist/app-child.js CHANGED
@@ -1341,15 +1341,6 @@ async function runDaemon(foreground = false, email) {
1341
1341
  socket.emit('session:error', { sessionId, error: 'Session not found or projectPath missing' });
1342
1342
  return;
1343
1343
  }
1344
- // Check if user choice is enabled from the prompt data (sent by backend)
1345
- // Backend now includes enabledModules in the session:prompt event
1346
- const enabledModules = data.enabledModules || [];
1347
- const moduleSettings = data.moduleSettings || {};
1348
- const userChoiceEnabled = enabledModules.includes('user-choice') || false;
1349
- if (foreground) {
1350
- console.log(`[CLI] Enabled modules: ${enabledModules.join(', ') || 'none'}`);
1351
- console.log(`[CLI] User choice enabled: ${userChoiceEnabled}`);
1352
- }
1353
1344
  // Store in activeSessions with promptId and model
1354
1345
  activeSessions.set(sessionId, { projectPath, currentPromptId: promptId, model });
1355
1346
  // Capture promptId in closure to prevent race conditions when multiple prompts arrive quickly
@@ -1363,9 +1354,6 @@ async function runDaemon(foreground = false, email) {
1363
1354
  await agentSessionManager.createSession({
1364
1355
  sessionId,
1365
1356
  projectPath,
1366
- userChoiceEnabled, // Pass user choice enabled setting from backend
1367
- enabledModules, // Pass enabled modules for MCP server
1368
- moduleSettings, // Pass module settings for MCP server
1369
1357
  onOutput: (output) => {
1370
1358
  // Serialize data to string if it's an object
1371
1359
  const dataString = typeof output.data === 'string'
@@ -1393,16 +1381,6 @@ async function runDaemon(foreground = false, email) {
1393
1381
  // The actual session:result is emitted from sendPrompt handler below
1394
1382
  // This handler is kept for backward compatibility but may not be used
1395
1383
  },
1396
- onChoiceRequest: (request) => {
1397
- // Emit choice request to frontend
1398
- socket.emit('user:choice:request', {
1399
- sessionId,
1400
- choiceId: request.choiceId,
1401
- question: request.question,
1402
- options: request.options,
1403
- timeout: request.timeout
1404
- });
1405
- }
1406
1384
  });
1407
1385
  // Send prompt - status updates will be emitted from agentSession when processing starts/completes
1408
1386
  await agentSessionManager.sendPrompt(sessionId, prompt, enhancers || [], {
@@ -1555,25 +1533,6 @@ async function runDaemon(foreground = false, email) {
1555
1533
  callback?.({ success: false, message: error.message });
1556
1534
  }
1557
1535
  });
1558
- // Handle user choice response from frontend
1559
- socket.on('user:choice:response', async (data) => {
1560
- try {
1561
- const { sessionId, choiceId, selectedValue } = data;
1562
- if (foreground) {
1563
- console.log(`[CLI] 📝 Received user choice response: choiceId=${choiceId}, selectedValue=${selectedValue}`);
1564
- }
1565
- // Forward the response to the agent session manager
1566
- await agentSessionManager.handleChoiceResponse(sessionId, {
1567
- choiceId,
1568
- selectedValue
1569
- });
1570
- }
1571
- catch (error) {
1572
- if (foreground) {
1573
- console.error(`✗ Error handling user choice response: ${error.message}`);
1574
- }
1575
- }
1576
- });
1577
1536
  socket.on('connect', () => {
1578
1537
  if (foreground) {
1579
1538
  console.log(`✓ Connected to backend at ${config.apiUrl}`);
package/dist/index.js CHANGED
@@ -1286,24 +1286,6 @@ async function runDaemon(foreground = false, email) {
1286
1286
  socket.emit('session:error', { sessionId, error: 'Session not found or projectPath missing' });
1287
1287
  return;
1288
1288
  }
1289
- // Check if user choice is enabled from the prompt data (sent by backend)
1290
- // Backend now includes enabledModules in the session:prompt event
1291
- const enabledModules = data.enabledModules || [];
1292
- const moduleSettings = data.moduleSettings || {};
1293
- const userChoiceEnabled = enabledModules.includes('user-choice') || false;
1294
- if (foreground) {
1295
- console.log(`[CLI] Enabled modules: ${enabledModules.join(', ') || 'none'}`);
1296
- console.log(`[CLI] User choice enabled: ${userChoiceEnabled}`);
1297
- }
1298
- // Store in activeSessions with promptId and model
1299
- activeSessions.set(sessionId, { projectPath, currentPromptId: promptId, model });
1300
- if (!projectPath) {
1301
- if (foreground) {
1302
- console.error(`✗ Session not found: ${sessionId} (missing projectPath)`);
1303
- }
1304
- socket.emit('session:error', { sessionId, error: 'Session not found or projectPath missing' });
1305
- return;
1306
- }
1307
1289
  // Store in activeSessions with promptId and model
1308
1290
  activeSessions.set(sessionId, { projectPath, currentPromptId: promptId, model });
1309
1291
  // Capture promptId in closure to prevent race conditions when multiple prompts arrive quickly
@@ -1317,9 +1299,6 @@ async function runDaemon(foreground = false, email) {
1317
1299
  await agentSessionManager.createSession({
1318
1300
  sessionId,
1319
1301
  projectPath,
1320
- userChoiceEnabled, // Pass user choice enabled setting from backend
1321
- enabledModules, // Pass enabled modules for MCP server
1322
- moduleSettings, // Pass module settings for MCP server
1323
1302
  onOutput: (output) => {
1324
1303
  // Serialize data to string if it's an object
1325
1304
  const dataString = typeof output.data === 'string'
@@ -1347,16 +1326,6 @@ async function runDaemon(foreground = false, email) {
1347
1326
  // The actual session:result is emitted from sendPrompt handler below
1348
1327
  // This handler is kept for backward compatibility but may not be used
1349
1328
  },
1350
- onChoiceRequest: (request) => {
1351
- // Emit choice request to frontend
1352
- socket.emit('user:choice:request', {
1353
- sessionId,
1354
- choiceId: request.choiceId,
1355
- question: request.question,
1356
- options: request.options,
1357
- timeout: request.timeout
1358
- });
1359
- }
1360
1329
  });
1361
1330
  // Send prompt - status updates will be emitted from agentSession when processing starts/completes
1362
1331
  await agentSessionManager.sendPrompt(sessionId, prompt, enhancers || [], {
@@ -1509,25 +1478,6 @@ async function runDaemon(foreground = false, email) {
1509
1478
  callback?.({ success: false, message: error.message });
1510
1479
  }
1511
1480
  });
1512
- // Handle user choice response from frontend
1513
- socket.on('user:choice:response', async (data) => {
1514
- try {
1515
- const { sessionId, choiceId, selectedValue } = data;
1516
- if (foreground) {
1517
- console.log(`[CLI] 📝 Received user choice response: choiceId=${choiceId}, selectedValue=${selectedValue}`);
1518
- }
1519
- // Forward the response to the agent session manager
1520
- await agentSessionManager.handleChoiceResponse(sessionId, {
1521
- choiceId,
1522
- selectedValue
1523
- });
1524
- }
1525
- catch (error) {
1526
- if (foreground) {
1527
- console.error(`✗ Error handling user choice response: ${error.message}`);
1528
- }
1529
- }
1530
- });
1531
1481
  // Handle image save request - saves base64 images to tmp directory
1532
1482
  socket.on('image:save', async (data) => {
1533
1483
  try {
@@ -1,9 +1,7 @@
1
1
  /**
2
2
  * Module MCP Server
3
3
  *
4
- * Exposes enabled modules as MCP tools to the agent.
5
- * This allows the agent to interact with modules like user-choice through standard tool calls.
6
- * Also provides built-in tools like analyze_image for vision capabilities.
4
+ * Provides built-in tools like analyze_image for vision capabilities.
7
5
  */
8
6
  import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
9
7
  import { z } from 'zod';
@@ -11,67 +9,6 @@ import * as fs from 'fs';
11
9
  import * as path from 'path';
12
10
  import * as os from 'os';
13
11
  import { getOpenrouterApiKey } from './agentSession.js';
14
- /**
15
- * Create a tool for the user-choice module
16
- */
17
- function createUserChoiceTool(onChoiceRequest) {
18
- const schema = {
19
- question: z.string(),
20
- options: z.array(z.object({ label: z.string(), value: z.string() })),
21
- timeout: z.number().optional()
22
- };
23
- return tool('user_choice_request', 'Request user input when making decisions. Use this when you need the user to choose between options or provide input on a decision. This tool will present a modal to the user with your question and wait for their response.', schema, async (args, _extra) => {
24
- if (!onChoiceRequest) {
25
- return {
26
- content: [
27
- {
28
- type: 'text',
29
- text: 'Error: User choice is not enabled. Please use an alternative approach or ask the user directly.'
30
- }
31
- ],
32
- isError: true
33
- };
34
- }
35
- const choiceId = `choice-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
36
- try {
37
- const response = await onChoiceRequest({
38
- choiceId,
39
- question: args.question,
40
- options: args.options,
41
- timeout: args.timeout
42
- });
43
- if (response.selectedValue === null) {
44
- return {
45
- content: [
46
- {
47
- type: 'text',
48
- text: 'The user did not respond within the timeout period. Please proceed with a reasonable default or ask again.'
49
- }
50
- ]
51
- };
52
- }
53
- return {
54
- content: [
55
- {
56
- type: 'text',
57
- text: `The user selected: ${response.selectedValue}`
58
- }
59
- ]
60
- };
61
- }
62
- catch (error) {
63
- return {
64
- content: [
65
- {
66
- type: 'text',
67
- text: `Error requesting user choice: ${error instanceof Error ? error.message : String(error)}`
68
- }
69
- ],
70
- isError: true
71
- };
72
- }
73
- });
74
- }
75
12
  /**
76
13
  * Convert a file to a data URI for vision API consumption
77
14
  */
@@ -141,24 +78,12 @@ function createAnalyzeImageTool(attachmentDir) {
141
78
  });
142
79
  }
143
80
  /**
144
- * Get available tools based on enabled modules
81
+ * Create the MCP server with built-in tools (always includes analyze_image)
145
82
  */
146
- function getModuleTools(config) {
83
+ export function createModuleMcpServer(config) {
147
84
  const tools = [];
148
- // User choice module
149
- if (config.enabledModules.includes('user-choice')) {
150
- tools.push(createUserChoiceTool(config.onChoiceRequest));
151
- }
152
85
  // Always add analyze_image tool (uses OpenRouter key from ai-config via backend)
153
86
  tools.push(createAnalyzeImageTool(config.attachmentDir));
154
- // Add more module tools here as they are implemented
155
- return tools;
156
- }
157
- /**
158
- * Create the module MCP server
159
- */
160
- export function createModuleMcpServer(config) {
161
- const tools = getModuleTools(config);
162
87
  const server = createSdkMcpServer({
163
88
  name: 'claude-voice-modules',
164
89
  version: '1.0.0',
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exreve/exk",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "exk - Control Claude CLI with voice and programmable interfaces",
5
5
  "type": "module",
6
6
  "bin": {