@exreve/exk 1.0.29 → 1.0.31

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);
@@ -519,9 +505,7 @@ export class AgentSessionManager {
519
505
  apiKey: CLAUDE_CONFIG.apiKey,
520
506
  model: CLAUDE_CONFIG.model,
521
507
  tools: { type: 'preset', preset: 'claude_code' },
522
- disallowedTools: attachmentDir
523
- ? ['AskUserQuestion', 'analyze_image'] // Disable built-in analyze_image when we have our own
524
- : ['AskUserQuestion'],
508
+ disallowedTools: ['AskUserQuestion', 'analyze_image'], // Disable built-in analyze_image (we provide our own via MCP)
525
509
  settingSources: ['project'], // Enable CLAUDE.md loading
526
510
  permissionMode: 'bypassPermissions',
527
511
  allowDangerouslySkipPermissions: true,
@@ -1230,11 +1214,6 @@ export class AgentSessionManager {
1230
1214
  session.currentPromptId = undefined;
1231
1215
  // 8. Remove from emergency stop tracking
1232
1216
  this.emergencyStopInProgress.delete(sessionId);
1233
- // 9. Resolve any pending choice request with null (cancelled)
1234
- if (session.pendingChoice) {
1235
- session.pendingChoice.resolve({ choiceId: session.pendingChoice.request.choiceId, selectedValue: null });
1236
- session.pendingChoice = undefined;
1237
- }
1238
1217
  const message = currentPromptId
1239
1218
  ? `Emergency stop: Cancelled prompt '${currentPromptId}' and cleared ${queueSize} queued prompts`
1240
1219
  : `Emergency stop: Cleared ${queueSize} queued prompts`;
@@ -1258,84 +1237,9 @@ export class AgentSessionManager {
1258
1237
  console.log(`[buildMcpServer] No session found for ${sessionId}`);
1259
1238
  return undefined;
1260
1239
  }
1261
- const enabledModules = session.enabledModules || [];
1262
- console.log(`[buildMcpServer] Session ${sessionId}: enabledModules=${JSON.stringify(enabledModules)}, attachmentDir=${attachmentDir || 'none'}`);
1263
- if (enabledModules.length === 0 && !attachmentDir) {
1264
- console.log(`[buildMcpServer] No enabled modules and no attachments, skipping MCP server creation`);
1265
- return undefined;
1266
- }
1267
- const handler = this.sessionHandlers.get(sessionId);
1240
+ console.log(`[buildMcpServer] Session ${sessionId}: attachmentDir=${attachmentDir || 'none'}`);
1268
1241
  return createModuleMcpServer({
1269
- enabledModules,
1270
- moduleSettings: session.moduleSettings || {},
1271
1242
  attachmentDir,
1272
- onChoiceRequest: handler?.onChoiceRequest
1273
- ? async (request) => {
1274
- return new Promise((resolve) => {
1275
- const sess = this.sessions.get(sessionId);
1276
- if (sess) {
1277
- sess.pendingChoice = { request, resolve };
1278
- handler.onChoiceRequest(request);
1279
- }
1280
- else {
1281
- resolve({ choiceId: request.choiceId, selectedValue: null });
1282
- }
1283
- });
1284
- }
1285
- : undefined
1286
- });
1287
- }
1288
- /**
1289
- * Handle user choice response from frontend
1290
- */
1291
- async handleChoiceResponse(sessionId, response) {
1292
- const session = this.sessions.get(sessionId);
1293
- if (!session) {
1294
- console.error(`[agentSession] Session ${sessionId} not found for choice response`);
1295
- return;
1296
- }
1297
- if (!session.pendingChoice) {
1298
- console.warn(`[agentSession] No pending choice for session ${sessionId}`);
1299
- return;
1300
- }
1301
- if (session.pendingChoice.request.choiceId !== response.choiceId) {
1302
- console.warn(`[agentSession] Choice ID mismatch: expected ${session.pendingChoice.request.choiceId}, got ${response.choiceId}`);
1303
- return;
1304
- }
1305
- // Resolve the pending choice promise
1306
- session.pendingChoice.resolve(response);
1307
- session.pendingChoice = undefined;
1308
- }
1309
- /**
1310
- * Request user choice during agent execution
1311
- */
1312
- async requestUserChoice(sessionId, request) {
1313
- const session = this.sessions.get(sessionId);
1314
- if (!session) {
1315
- throw new Error(`Session ${sessionId} not found`);
1316
- }
1317
- // Check if user choice is enabled for this session
1318
- if (!session.userChoiceEnabled) {
1319
- throw new Error('User choice is not enabled for this session');
1320
- }
1321
- // Create a promise that will be resolved when the user responds
1322
- return new Promise((resolve) => {
1323
- session.pendingChoice = { request, resolve };
1324
- // Emit the choice request through the onOutput callback
1325
- // This will be picked up by the CLI and sent to the frontend
1326
- const handler = this.sessionHandlers.get(sessionId);
1327
- if (handler?.onChoiceRequest) {
1328
- handler.onChoiceRequest(request);
1329
- }
1330
- // Set timeout if specified
1331
- if (request.timeout) {
1332
- setTimeout(() => {
1333
- if (session.pendingChoice?.request.choiceId === request.choiceId) {
1334
- session.pendingChoice.resolve({ choiceId: request.choiceId, selectedValue: null });
1335
- session.pendingChoice = undefined;
1336
- }
1337
- }, request.timeout);
1338
- }
1339
1243
  });
1340
1244
  }
1341
1245
  }
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,26 +78,12 @@ function createAnalyzeImageTool(attachmentDir) {
141
78
  });
142
79
  }
143
80
  /**
144
- * Get available tools based on enabled modules
145
- */
146
- function getModuleTools(config) {
147
- const tools = [];
148
- // User choice module
149
- if (config.enabledModules.includes('user-choice')) {
150
- tools.push(createUserChoiceTool(config.onChoiceRequest));
151
- }
152
- // Always add analyze_image tool if attachmentDir is provided (i.e. there are attachments)
153
- if (config.attachmentDir) {
154
- tools.push(createAnalyzeImageTool(config.attachmentDir));
155
- }
156
- // Add more module tools here as they are implemented
157
- return tools;
158
- }
159
- /**
160
- * Create the module MCP server
81
+ * Create the MCP server with built-in tools (always includes analyze_image)
161
82
  */
162
83
  export function createModuleMcpServer(config) {
163
- const tools = getModuleTools(config);
84
+ const tools = [];
85
+ // Always add analyze_image tool (uses OpenRouter key from ai-config via backend)
86
+ tools.push(createAnalyzeImageTool(config.attachmentDir));
164
87
  const server = createSdkMcpServer({
165
88
  name: 'claude-voice-modules',
166
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.29",
3
+ "version": "1.0.31",
4
4
  "description": "exk - Control Claude CLI with voice and programmable interfaces",
5
5
  "type": "module",
6
6
  "bin": {