@exreve/exk 1.0.14 → 1.0.15

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.
@@ -51,6 +51,9 @@ function extractToolName(toolResult) {
51
51
  if (toolResult.type === 'text' && toolResult.file)
52
52
  return 'Read';
53
53
  if (toolResult.file_path || toolResult.filePath) {
54
+ // Has old_string/new_string → Edit; has content/create → Write; else Read
55
+ if (toolResult.old_string !== undefined && toolResult.new_string !== undefined)
56
+ return 'Edit';
54
57
  return (toolResult.content !== undefined || toolResult.type === 'create') ? 'Write' : 'Read';
55
58
  }
56
59
  if (toolResult.stdout !== undefined || toolResult.stderr !== undefined)
@@ -60,6 +63,23 @@ function extractToolName(toolResult) {
60
63
  return 'Bash';
61
64
  return 'unknown';
62
65
  }
66
+ // Look up tool name from the most recent assistant message's tool_use blocks by tool_use_id
67
+ function lookupToolNameFromHistory(messages, toolUseId) {
68
+ if (!toolUseId)
69
+ return null;
70
+ for (let i = messages.length - 1; i >= 0; i--) {
71
+ const msg = messages[i];
72
+ if (msg.role !== 'assistant')
73
+ continue;
74
+ const content = typeof msg.content === 'string' ? null : msg.content;
75
+ if (!Array.isArray(content))
76
+ continue;
77
+ const toolUse = content.find((c) => c.type === 'tool_use' && c.id === toolUseId);
78
+ if (toolUse?.name)
79
+ return toolUse.name;
80
+ }
81
+ return null;
82
+ }
63
83
  // AI config - loaded from server after registration, stored in ~/.talk-to-code/ai-config.json
64
84
  // (Do not read ANTHROPIC_* / CLAUDE_MODEL from the host environment — only this file + code default model.)
65
85
  const AI_CONFIG_PATH = path.join(os.homedir(), '.talk-to-code', 'ai-config.json');
@@ -254,6 +274,13 @@ export class AgentSessionManager {
254
274
  if (!session.isProcessingQueue) {
255
275
  this.processPromptQueue(sessionId);
256
276
  }
277
+ else if (session.isProcessingQueue && !session.activeQueryStream && !this.emergencyStopInProgress.has(sessionId)) {
278
+ // Safety: isProcessingQueue is true but there's no active stream and no emergency stop
279
+ // This means the queue got stuck (e.g. from a previous abort return that bypassed cleanup)
280
+ console.warn(`[agentSession] Queue stuck detected for session ${sessionId}, resetting isProcessingQueue`);
281
+ session.isProcessingQueue = false;
282
+ this.processPromptQueue(sessionId);
283
+ }
257
284
  }
258
285
  async processPromptQueue(sessionId) {
259
286
  const session = this.sessions.get(sessionId);
@@ -696,12 +723,16 @@ export class AgentSessionManager {
696
723
  }
697
724
  }
698
725
  if (toolResult) {
726
+ const detectedName = extractToolName(toolResult);
727
+ const resolvedName = detectedName !== 'unknown'
728
+ ? detectedName
729
+ : (lookupToolNameFromHistory(session.messages, toolUseId) || detectedName);
699
730
  onOutput({
700
731
  type: 'tool_result',
701
732
  data: toolResult,
702
733
  timestamp: Date.now(),
703
734
  metadata: {
704
- toolName: extractToolName(toolResult),
735
+ toolName: resolvedName,
705
736
  toolResult: toolResult,
706
737
  toolUseId: toolUseId || undefined,
707
738
  parentToolUseId: msg.parent_tool_use_id,
@@ -827,7 +858,9 @@ export class AgentSessionManager {
827
858
  onComplete(null);
828
859
  session.activeQueryStream = undefined;
829
860
  session.currentPromptId = undefined;
830
- return;
861
+ // Use break instead of return to ensure isProcessingQueue gets reset
862
+ // after the while loop at the end of processPromptQueue
863
+ break;
831
864
  }
832
865
  // Re-throw non-abort errors
833
866
  throw streamError;
@@ -1014,24 +1047,46 @@ export class AgentSessionManager {
1014
1047
  if (!session) {
1015
1048
  return { success: false, message: 'Session not found' };
1016
1049
  }
1017
- // 1. Abort all controllers (session level + all prompt-level)
1050
+ // 1. Abort session-level controller only (not all sessions' controllers)
1018
1051
  session.abortController.abort();
1019
- for (const controller of this.promptAbortControllers.values()) {
1020
- controller.abort();
1052
+ // Abort only controllers belonging to THIS session
1053
+ // Find and abort controllers for prompts in this session's queue and current prompt
1054
+ if (session.currentPromptId) {
1055
+ const ctrl = this.promptAbortControllers.get(session.currentPromptId);
1056
+ if (ctrl)
1057
+ ctrl.abort();
1058
+ }
1059
+ for (const queued of session.promptQueue) {
1060
+ if (queued.abortController)
1061
+ queued.abortController.abort();
1062
+ if (queued.promptId) {
1063
+ const ctrl = this.promptAbortControllers.get(queued.promptId);
1064
+ if (ctrl)
1065
+ ctrl.abort();
1066
+ }
1021
1067
  }
1022
1068
  // 2. Kill the entire process tree
1023
1069
  await this.killProcessTree(sessionId);
1024
- // 3. Clear the prompt queue
1070
+ // 3. Collect prompt IDs from queue BEFORE clearing it
1025
1071
  const queueSize = session.promptQueue.length;
1072
+ const queuedPromptIds = session.promptQueue
1073
+ .map(p => p.promptId)
1074
+ .filter((id) => !!id);
1075
+ const currentPromptId = session.currentPromptId;
1076
+ // 4. Clear the prompt queue
1026
1077
  session.promptQueue = [];
1027
- // 4. Clear active stream
1078
+ // 5. Clear active stream
1028
1079
  session.activeQueryStream = undefined;
1029
- // 5. Reset processing state
1080
+ // 6. Reset processing state
1030
1081
  session.isProcessingQueue = false;
1031
- // 6. Clear current prompt tracking
1032
- const currentPromptId = session.currentPromptId;
1033
- // 7. Clean up abort controllers map
1034
- this.promptAbortControllers.clear();
1082
+ // 7. Clean up abort controllers map (only for this session's prompts, not ALL sessions)
1083
+ if (currentPromptId) {
1084
+ this.promptAbortControllers.delete(currentPromptId);
1085
+ }
1086
+ for (const pid of queuedPromptIds) {
1087
+ this.promptAbortControllers.delete(pid);
1088
+ }
1089
+ session.currentPromptId = undefined;
1035
1090
  // 8. Remove from emergency stop tracking
1036
1091
  this.emergencyStopInProgress.delete(sessionId);
1037
1092
  // 9. Resolve any pending choice request with null (cancelled)
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exreve/exk",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "exk - Control Claude CLI with voice and programmable interfaces",
5
5
  "type": "module",
6
6
  "bin": {