claude-code-rust 0.14.2 → 0.14.3

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.
@@ -8,7 +8,7 @@ import { linkTaskToolUse, unlinkTaskToolUse } from "./task_links.js";
8
8
  import { emitAuthRequired, classifyTurnErrorKind, emitFastModeUpdate, emitFastModeUpdateIfChanged, setFastModeSnapshotIfChanged, } from "./error_classification.js";
9
9
  import { mapAvailableAgentsFromNames, emitAvailableAgentsIfChanged, refreshAvailableAgents } from "./agents.js";
10
10
  import { mapInitSlashCommands, mapSdkSlashCommands, updateAvailableCommands, } from "./available_commands.js";
11
- import { buildApiRetryUpdate, buildRateLimitUpdate, buildSubagentRetryUpdate, normalizeSettingsParseErrors, numberField, parseApiRetryError, parseRuntimeSessionState, } from "./state_parsing.js";
11
+ import { buildApiRetryUpdate, buildRateLimitUpdate, buildSubagentRetryUpdate, normalizeSettingsParseErrors, nonNegativeIntegerField, numberField, parseApiRetryError, parseRuntimeSessionState, } from "./state_parsing.js";
12
12
  import { looksLikeAuthRequired } from "./auth.js";
13
13
  import { emitCurrentModelUpdate, refreshCurrentModel, updateSessionId } from "./session_lifecycle.js";
14
14
  import { bridgeLogger, LOG_TARGETS } from "./logger.js";
@@ -576,6 +576,18 @@ function logContentBlockLinkage(session, blockType, toolUseId, toolName, linkage
576
576
  },
577
577
  });
578
578
  }
579
+ function resolveToolProgressTarget(session, taskToolUseId, toolUseId, parentToolUseId) {
580
+ if (taskToolUseId && session.toolCalls.has(taskToolUseId)) {
581
+ return { toolUseId: taskToolUseId, source: "task" };
582
+ }
583
+ if (toolUseId && session.toolCalls.has(toolUseId)) {
584
+ return { toolUseId, source: "tool" };
585
+ }
586
+ if (parentToolUseId && session.toolCalls.has(parentToolUseId)) {
587
+ return { toolUseId: parentToolUseId, source: "parent" };
588
+ }
589
+ return null;
590
+ }
579
591
  function hideToolUse(session, toolUseId) {
580
592
  if (toolUseId) {
581
593
  session.hiddenToolUseIds.add(toolUseId);
@@ -1036,12 +1048,31 @@ export function handleSdkMessage(session, message) {
1036
1048
  emitSessionUpdate(session.sessionId, { type: "current_mode_update", current_mode_id: mode });
1037
1049
  }
1038
1050
  if (msg.status === "compacting") {
1039
- emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "compacting" });
1051
+ emitSessionUpdate(session.sessionId, { type: "compaction_update", phase: "started" });
1040
1052
  }
1041
1053
  else if (msg.status === "requesting") {
1042
1054
  emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "requesting" });
1043
1055
  }
1044
1056
  else if (msg.status === null) {
1057
+ if (msg.compact_result === "success") {
1058
+ emitSessionUpdate(session.sessionId, {
1059
+ type: "compaction_update",
1060
+ phase: "finished",
1061
+ result: "success",
1062
+ });
1063
+ }
1064
+ else if (msg.compact_result === "failed") {
1065
+ const compactError = typeof msg.compact_error === "string" && msg.compact_error.trim().length > 0
1066
+ ? msg.compact_error.trim()
1067
+ : undefined;
1068
+ emitSessionUpdate(session.sessionId, {
1069
+ type: "compaction_update",
1070
+ phase: "finished",
1071
+ result: "failed",
1072
+ error_code: compactError === "too_few_groups" ? "too_few_groups" : "unknown",
1073
+ ...(compactError ? { error: compactError } : {}),
1074
+ });
1075
+ }
1045
1076
  emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "idle" });
1046
1077
  }
1047
1078
  emitFastModeUpdateIfChanged(session, msg.fast_mode_state, msg.fast_mode_disabled_reason);
@@ -1053,12 +1084,17 @@ export function handleSdkMessage(session, message) {
1053
1084
  return;
1054
1085
  }
1055
1086
  const trigger = compactMetadata.trigger;
1056
- const preTokens = numberField(compactMetadata, "pre_tokens", "preTokens");
1087
+ const preTokens = nonNegativeIntegerField(compactMetadata, "pre_tokens", "preTokens");
1088
+ const postTokens = nonNegativeIntegerField(compactMetadata, "post_tokens", "postTokens");
1089
+ const durationMs = nonNegativeIntegerField(compactMetadata, "duration_ms", "durationMs");
1057
1090
  if ((trigger === "manual" || trigger === "auto") && preTokens !== undefined) {
1058
1091
  emitSessionUpdate(session.sessionId, {
1059
- type: "compaction_boundary",
1092
+ type: "compaction_update",
1093
+ phase: "boundary",
1060
1094
  trigger,
1061
1095
  pre_tokens: preTokens,
1096
+ ...(postTokens !== undefined ? { post_tokens: postTokens } : {}),
1097
+ ...(durationMs !== undefined ? { duration_ms: durationMs } : {}),
1062
1098
  });
1063
1099
  }
1064
1100
  return;
@@ -1141,25 +1177,29 @@ export function handleSdkMessage(session, message) {
1141
1177
  if (type === "tool_progress") {
1142
1178
  const toolUseId = typeof msg.tool_use_id === "string" ? msg.tool_use_id : "";
1143
1179
  const toolName = typeof msg.tool_name === "string" ? msg.tool_name : "Tool";
1180
+ const parentToolUseId = typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : "";
1144
1181
  const taskId = typeof msg.task_id === "string" ? msg.task_id : "";
1145
1182
  const taskToolUseId = taskId ? session.taskToolUseIds.get(taskId) ?? "" : "";
1146
- const resolvedToolUseId = taskToolUseId || toolUseId;
1147
- if (isHiddenToolUse(session, resolvedToolUseId, toolName)) {
1183
+ if (isHiddenToolUse(session, toolUseId, toolName)) {
1148
1184
  return;
1149
1185
  }
1186
+ const progressTarget = resolveToolProgressTarget(session, taskToolUseId, toolUseId, parentToolUseId);
1187
+ const resolvedToolUseId = progressTarget?.toolUseId ?? "";
1150
1188
  bridgeLogger.debug({
1151
1189
  target: LOG_TARGETS.APP_TOOL,
1152
1190
  eventName: "sdk_tool_progress_linkage_observed",
1153
1191
  message: "SDK tool progress linkage observed",
1154
- outcome: typeof msg.parent_tool_use_id === "string" ? "child" : "root_or_unknown",
1192
+ outcome: progressTarget?.source ?? "orphaned",
1155
1193
  sessionId: session.sessionId,
1156
- toolCallId: resolvedToolUseId || undefined,
1194
+ toolCallId: resolvedToolUseId || toolUseId || undefined,
1157
1195
  fields: {
1158
1196
  tool_name: toolName,
1159
1197
  tool_use_id: toolUseId || undefined,
1160
- parent_tool_use_id: typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : undefined,
1198
+ parent_tool_use_id: parentToolUseId || undefined,
1161
1199
  task_id: taskId || undefined,
1162
1200
  task_resolved_tool_use_id: taskToolUseId || undefined,
1201
+ resolved_tool_use_id: resolvedToolUseId || undefined,
1202
+ correlation_source: progressTarget?.source,
1163
1203
  },
1164
1204
  });
1165
1205
  if (resolvedToolUseId) {
@@ -1178,7 +1218,7 @@ export function handleSdkMessage(session, message) {
1178
1218
  const subagentType = typeof msg.subagent_type === "string" && msg.subagent_type.trim()
1179
1219
  ? msg.subagent_type.trim()
1180
1220
  : undefined;
1181
- emitToolProgressUpdate(session, resolvedToolUseId, toolName, {
1221
+ emitToolProgressUpdate(session, resolvedToolUseId, {
1182
1222
  ...(subagentRetry
1183
1223
  ? { subagentRetry }
1184
1224
  : hasSubagentRetry
@@ -15,7 +15,7 @@ function nonNegativeNumberField(record, ...keys) {
15
15
  }
16
16
  return value;
17
17
  }
18
- function nonNegativeIntegerField(record, ...keys) {
18
+ export function nonNegativeIntegerField(record, ...keys) {
19
19
  const value = numberField(record, ...keys);
20
20
  return value !== undefined && value >= 0 && Number.isInteger(value) ? value : undefined;
21
21
  }
@@ -339,14 +339,15 @@ export function finalizeOpenToolCalls(session, status) {
339
339
  emitToolCallUpdate(session, toolUseId, { status }, "finalize");
340
340
  }
341
341
  }
342
- export function emitToolProgressUpdate(session, toolUseId, toolName, progress = {}) {
343
- let existing = session.toolCalls.get(toolUseId);
342
+ /**
343
+ * Apply advisory progress only to a tool created by an authoritative tool-use event.
344
+ */
345
+ export function emitToolProgressUpdate(session, toolUseId, progress = {}) {
346
+ const existing = session.toolCalls.get(toolUseId);
344
347
  if (!existing) {
345
- emitToolCall(session, toolUseId, toolName, {});
346
- existing = session.toolCalls.get(toolUseId);
348
+ return;
347
349
  }
348
- if (!existing ||
349
- existing.status === "completed" ||
350
+ if (existing.status === "completed" ||
350
351
  existing.status === "failed" ||
351
352
  existing.status === "killed") {
352
353
  return;
@@ -23,6 +23,19 @@ const WORKFLOW_TOOL_NAME = "Workflow";
23
23
  const PROJECTS_TOOL_NAME = "Projects";
24
24
  const ARTIFACT_TOOL_NAME = "Artifact";
25
25
  const SHOW_ONBOARDING_ROLE_PICKER_TOOL_NAME = "ShowOnboardingRolePicker";
26
+ const SKILL_TOOL_NAME = "Skill";
27
+ const SKILL_WORD_OVERRIDES = {
28
+ api: "API",
29
+ ci: "CI",
30
+ cli: "CLI",
31
+ gh: "GH",
32
+ github: "GitHub",
33
+ mcp: "MCP",
34
+ pdf: "PDF",
35
+ sdk: "SDK",
36
+ ui: "UI",
37
+ ux: "UX",
38
+ };
26
39
  const READ_MCP_RESOURCE_TOOL_NAME = "ReadMcpResource";
27
40
  const READ_MCP_RESOURCE_DIR_TOOL_NAME = "ReadMcpResourceDir";
28
41
  const SEARCH_OUTPUT_MODES = new Set(["content", "files_with_matches", "count"]);
@@ -183,6 +196,7 @@ export function normalizeToolKind(name) {
183
196
  case "Projects":
184
197
  case "Artifact":
185
198
  case "ShowOnboardingRolePicker":
199
+ case SKILL_TOOL_NAME:
186
200
  return "other";
187
201
  case "Task":
188
202
  case "Agent":
@@ -194,6 +208,34 @@ export function normalizeToolKind(name) {
194
208
  return "think";
195
209
  }
196
210
  }
211
+ function skillDisplayName(rawName) {
212
+ const trimmed = rawName.trim();
213
+ const segments = trimmed.split(":");
214
+ if (segments.length > 2 ||
215
+ segments.some((segment) => !segment || !/^[a-zA-Z0-9]+(?:[-_][a-zA-Z0-9]+)*$/.test(segment))) {
216
+ return trimmed;
217
+ }
218
+ const displaySegments = segments.map((segment) => segment
219
+ .split(/[-_]/)
220
+ .map((word) => skillDisplayWord(word))
221
+ .join(" "));
222
+ if (displaySegments.length === 2 &&
223
+ displaySegments[0].toLowerCase() === displaySegments[1].toLowerCase()) {
224
+ return displaySegments[1];
225
+ }
226
+ return displaySegments.join(" / ");
227
+ }
228
+ function skillDisplayWord(word) {
229
+ const override = SKILL_WORD_OVERRIDES[word.toLowerCase()];
230
+ if (override) {
231
+ return override;
232
+ }
233
+ if (/^[A-Z0-9]+$/.test(word) ||
234
+ (/[a-z]/.test(word) && /[A-Z]/.test(word) && !/^[A-Z][a-z0-9]*$/.test(word))) {
235
+ return word;
236
+ }
237
+ return `${word.charAt(0).toUpperCase()}${word.slice(1).toLowerCase()}`;
238
+ }
197
239
  export function toolTitle(name, input, context = {}) {
198
240
  const agentTitle = agentInputTitle(name, input);
199
241
  if (agentTitle) {
@@ -263,6 +305,12 @@ export function toolTitle(name, input, context = {}) {
263
305
  if (name === SHOW_ONBOARDING_ROLE_PICKER_TOOL_NAME) {
264
306
  return SHOW_ONBOARDING_ROLE_PICKER_TOOL_NAME;
265
307
  }
308
+ if (name === SKILL_TOOL_NAME) {
309
+ const skillName = nonEmptyString(input.skill);
310
+ return skillName
311
+ ? `${SKILL_TOOL_NAME}: ${skillDisplayName(skillName)}`
312
+ : SKILL_TOOL_NAME;
313
+ }
266
314
  if (name === "EnterWorktree") {
267
315
  const worktreeName = typeof input.name === "string" ? input.name.trim() : "";
268
316
  return worktreeName || "EnterWorktree";
@@ -369,6 +417,17 @@ function resultRecordCandidates(rawResult, rawContent) {
369
417
  pushNestedRecords(rawContent);
370
418
  return candidates;
371
419
  }
420
+ function imageReadResultText(rawResult, rawContent, rawInput) {
421
+ const isImage = resultRecordCandidates(rawResult, rawContent).some((candidate) => candidate.type === "image");
422
+ if (!isImage) {
423
+ return undefined;
424
+ }
425
+ const input = asRecordOrNull(rawInput);
426
+ const filePath = typeof input?.file_path === "string" ? input.file_path.trim() : "";
427
+ const normalizedPath = filePath.replaceAll("\\", "/");
428
+ const fileName = normalizedPath.slice(normalizedPath.lastIndexOf("/") + 1);
429
+ return fileName ? `Viewed Image ${fileName}` : "Viewed Image";
430
+ }
372
431
  function parseJsonCandidate(value) {
373
432
  const text = typeof value === "string" ? value : extractText(value);
374
433
  const trimmed = text.trim();
@@ -1593,6 +1652,51 @@ function collectResultCandidates(rawResult, rawContent) {
1593
1652
  }
1594
1653
  return candidates;
1595
1654
  }
1655
+ function parseSkillResult(toolName, rawResult, rawContent) {
1656
+ if (toolName !== SKILL_TOOL_NAME) {
1657
+ return undefined;
1658
+ }
1659
+ for (const candidate of collectResultCandidates(rawResult, rawContent)) {
1660
+ const commandName = nonEmptyString(candidate.commandName);
1661
+ if (!commandName || typeof candidate.success !== "boolean") {
1662
+ continue;
1663
+ }
1664
+ if (candidate.status === "forked") {
1665
+ const agentId = nonEmptyString(candidate.agentId);
1666
+ if (!agentId || typeof candidate.result !== "string") {
1667
+ continue;
1668
+ }
1669
+ if (candidate.background !== undefined && typeof candidate.background !== "boolean") {
1670
+ continue;
1671
+ }
1672
+ return {
1673
+ success: candidate.success,
1674
+ commandName,
1675
+ status: "forked",
1676
+ agentId,
1677
+ result: candidate.result,
1678
+ ...(typeof candidate.background === "boolean"
1679
+ ? { background: candidate.background }
1680
+ : {}),
1681
+ };
1682
+ }
1683
+ if (candidate.status === undefined || candidate.status === "inline") {
1684
+ const allowedToolsValid = candidate.allowedTools === undefined ||
1685
+ (Array.isArray(candidate.allowedTools) &&
1686
+ candidate.allowedTools.every((tool) => typeof tool === "string"));
1687
+ const modelValid = candidate.model === undefined || typeof candidate.model === "string";
1688
+ if (!allowedToolsValid || !modelValid) {
1689
+ continue;
1690
+ }
1691
+ return {
1692
+ success: candidate.success,
1693
+ commandName,
1694
+ status: "inline",
1695
+ };
1696
+ }
1697
+ }
1698
+ return undefined;
1699
+ }
1596
1700
  function monitorResultFields(toolName, rawResult, rawContent) {
1597
1701
  if (toolName !== MONITOR_TOOL_NAME) {
1598
1702
  return undefined;
@@ -1797,6 +1901,32 @@ export function buildToolResultFields(isError, rawContent, base, rawResult, _con
1797
1901
  if (outputMetadata) {
1798
1902
  fields.output_metadata = outputMetadata;
1799
1903
  }
1904
+ const skillResult = !isError ? parseSkillResult(toolName, rawResult, rawContent) : undefined;
1905
+ if (skillResult) {
1906
+ fields.title = `${SKILL_TOOL_NAME}: ${skillDisplayName(skillResult.commandName)}`;
1907
+ if (!skillResult.success) {
1908
+ fields.status = "failed";
1909
+ }
1910
+ else if (skillResult.status === "inline") {
1911
+ return fields;
1912
+ }
1913
+ else {
1914
+ const output = skillResult.result.trim();
1915
+ if (output) {
1916
+ fields.raw_output = output;
1917
+ fields.content = [{ type: "content", content: { type: "text", text: output } }];
1918
+ }
1919
+ return fields;
1920
+ }
1921
+ }
1922
+ const imageReadText = !isError && toolName === "Read"
1923
+ ? imageReadResultText(rawResult, rawContent, base?.raw_input)
1924
+ : undefined;
1925
+ if (imageReadText !== undefined) {
1926
+ fields.raw_output = imageReadText;
1927
+ fields.content = [{ type: "content", content: { type: "text", text: imageReadText } }];
1928
+ return fields;
1929
+ }
1800
1930
  const fileUnchangedText = !isError && toolName === "Read" ? fileUnchangedResultText(rawResult, rawContent) : "";
1801
1931
  if (fileUnchangedText) {
1802
1932
  fields.raw_output = fileUnchangedText;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-rust",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
4
4
  "description": "Claude Code Rust - native Rust terminal interface for Claude Code",
5
5
  "keywords": [
6
6
  "cli",
@@ -36,12 +36,12 @@
36
36
  "@anthropic-ai/claude-agent-sdk": "0.3.220"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@srothgan/claude-code-rust-darwin-arm64": "0.14.2",
40
- "@srothgan/claude-code-rust-darwin-x64": "0.14.2",
41
- "@srothgan/claude-code-rust-linux-x64-gnu": "0.14.2",
42
- "@srothgan/claude-code-rust-linux-arm64-gnu": "0.14.2",
43
- "@srothgan/claude-code-rust-win32-x64-msvc": "0.14.2",
44
- "@srothgan/claude-code-rust-win32-arm64-msvc": "0.14.2"
39
+ "@srothgan/claude-code-rust-darwin-arm64": "0.14.3",
40
+ "@srothgan/claude-code-rust-darwin-x64": "0.14.3",
41
+ "@srothgan/claude-code-rust-linux-x64-gnu": "0.14.3",
42
+ "@srothgan/claude-code-rust-linux-arm64-gnu": "0.14.3",
43
+ "@srothgan/claude-code-rust-win32-x64-msvc": "0.14.3",
44
+ "@srothgan/claude-code-rust-win32-arm64-msvc": "0.14.3"
45
45
  },
46
46
  "engines": {
47
47
  "node": ">=24"