@exreve/exk 1.0.59 → 1.0.61

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.
@@ -151,7 +151,8 @@ function extractToolName(toolResult) {
151
151
  }
152
152
  catch { /* not JSON, fall through */ }
153
153
  // SDK 0.2.x: content-only results from nested tool calls (no stdout/stderr wrapper)
154
- return 'Bash';
154
+ // Don't default to Bash — the history lookup is authoritative
155
+ return 'unknown';
155
156
  }
156
157
  // ── Agent/Task output: {agentId, content, status}
157
158
  if (r.agentId && Array.isArray(r.content) && r.status) {
@@ -508,6 +509,7 @@ export class AgentSessionManager {
508
509
  this.sessions.set(sessionId, {
509
510
  abortController,
510
511
  messages: [],
512
+ toolNameMap: new Map(),
511
513
  totalInputTokens: 0,
512
514
  totalOutputTokens: 0,
513
515
  totalCostUsd: 0,
@@ -1001,6 +1003,14 @@ export class AgentSessionManager {
1001
1003
  content: msg.message,
1002
1004
  timestamp: Date.now()
1003
1005
  });
1006
+ // Populate toolNameMap from this assistant message's tool_use blocks
1007
+ if (Array.isArray(msg.message?.content)) {
1008
+ for (const block of msg.message.content) {
1009
+ if (block.type === 'tool_use' && block.id && block.name) {
1010
+ session.toolNameMap.set(block.id, block.name);
1011
+ }
1012
+ }
1013
+ }
1004
1014
  onOutput({
1005
1015
  type: 'assistant',
1006
1016
  data: msg.message,
@@ -1076,8 +1086,8 @@ export class AgentSessionManager {
1076
1086
  // The structured result lives in tool_use_result for built-in tools.
1077
1087
  // For MCP tools, tool_use_result is a content-block array we need to parse.
1078
1088
  let toolResult = null;
1079
- let toolUseId = msg.parent_tool_use_id;
1080
- // STEP 1: Always extract tool_use_id from message.content (authoritative source)
1089
+ let toolUseId = null;
1090
+ // STEP 1: Extract tool_use_id from message.content (authoritative source)
1081
1091
  if (Array.isArray(msg.message?.content)) {
1082
1092
  const contentBlocks = msg.message.content;
1083
1093
  const toolResultBlock = contentBlocks.find((c) => c.type === 'tool_result');
@@ -1085,6 +1095,11 @@ export class AgentSessionManager {
1085
1095
  toolUseId = toolResultBlock.tool_use_id;
1086
1096
  }
1087
1097
  }
1098
+ // STEP 1b: Fallback — use parent_tool_use_id if STEP 1 didn't find it
1099
+ // (subagent results where the message.content may not contain tool_result block)
1100
+ if (!toolUseId && msg.parent_tool_use_id) {
1101
+ toolUseId = msg.parent_tool_use_id;
1102
+ }
1088
1103
  // STEP 2: Use tool_use_result as the primary data source.
1089
1104
  // For built-in tools (Bash, Read, Edit, Write, Glob, Grep) it's a structured
1090
1105
  // object like {stdout, stderr} or {type:'text', file:{...}} — use directly.
@@ -1149,11 +1164,11 @@ export class AgentSessionManager {
1149
1164
  }
1150
1165
  }
1151
1166
  if (toolResult) {
1152
- // History lookup is authoritative: the assistant's tool_use block names the tool.
1153
- // Heuristic detection can't distinguish Bash/Grep/Glob/etc (all have {stdout,stderr}).
1154
- const historyName = lookupToolNameFromHistory(session.messages, toolUseId);
1155
- const detectedName = extractToolName(toolResult);
1156
- const resolvedName = historyName || detectedName;
1167
+ // Map lookup is O(1), falls back to O(n) history scan, then heuristic
1168
+ const mappedName = toolUseId ? session.toolNameMap.get(toolUseId) ?? null : null;
1169
+ const historyName = mappedName || lookupToolNameFromHistory(session.messages, toolUseId);
1170
+ const detectedName = historyName || extractToolName(toolResult);
1171
+ const resolvedName = detectedName;
1157
1172
  onOutput({
1158
1173
  type: 'tool_result',
1159
1174
  data: toolResult,
@@ -1183,7 +1198,7 @@ export class AgentSessionManager {
1183
1198
  const sysMsg = message;
1184
1199
  onOutput({
1185
1200
  type: 'system',
1186
- data: { ...sysMsg, subtype: sysMsg.subtype },
1201
+ data: sysMsg,
1187
1202
  timestamp: Date.now(),
1188
1203
  metadata: {
1189
1204
  subtype: sysMsg.subtype,
@@ -161,19 +161,19 @@ export function registerSessionHandlers(socket, foreground, activeSessions, getS
161
161
  });
162
162
  },
163
163
  onOutput: (output) => {
164
- const dataString = typeof output.data === 'string' ? output.data : JSON.stringify(output.data);
165
164
  if (!capturedPromptId) {
166
165
  console.error(`[CLI] Missing promptId for session ${sessionId}, cannot emit prompt:output`);
167
166
  return;
168
167
  }
169
168
  if (foreground && output.type === 'stdout') {
169
+ const dataString = typeof output.data === 'string' ? output.data : JSON.stringify(output.data);
170
170
  process.stdout.write(dataString);
171
171
  }
172
172
  getSocket().emit('prompt:output', {
173
173
  sessionId,
174
174
  promptId: capturedPromptId,
175
175
  type: output.type,
176
- data: dataString,
176
+ data: output.data,
177
177
  timestamp: output.timestamp,
178
178
  metadata: output.metadata
179
179
  });
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exreve/exk",
3
- "version": "1.0.59",
3
+ "version": "1.0.61",
4
4
  "description": "exk - Control Claude CLI with voice and programmable interfaces",
5
5
  "type": "module",
6
6
  "bin": {