claude-code-rust 0.7.1 → 0.8.1

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.
@@ -13,6 +13,9 @@ export const sessions = new Map();
13
13
  const DEFAULT_SETTING_SOURCES = ["user", "project", "local"];
14
14
  const DEFAULT_MODEL_NAME = "default";
15
15
  const DEFAULT_PERMISSION_MODE = "default";
16
+ function settingsObjectFromLaunchSettings(launchSettings) {
17
+ return launchSettings.settings;
18
+ }
16
19
  export function sessionById(sessionId) {
17
20
  return sessions.get(sessionId) ?? null;
18
21
  }
@@ -32,6 +35,14 @@ export async function closeSession(session) {
32
35
  pending.onOutcome?.({ outcome: "cancelled" });
33
36
  }
34
37
  session.pendingPermissions.clear();
38
+ for (const pending of session.pendingQuestions.values()) {
39
+ pending.onOutcome({ outcome: "cancelled" });
40
+ }
41
+ session.pendingQuestions.clear();
42
+ for (const pending of session.pendingElicitations.values()) {
43
+ pending.resolve({ action: "cancel" });
44
+ }
45
+ session.pendingElicitations.clear();
35
46
  }
36
47
  export async function closeAllSessions() {
37
48
  const active = Array.from(sessions.values());
@@ -54,7 +65,7 @@ export async function createSession(params) {
54
65
  `decision_reason=${options.decisionReason ?? "<none>"} suggestions=${formatPermissionUpdates(options.suggestions)}`);
55
66
  const existing = ensureToolCallVisible(session, toolUseId, toolName, inputData);
56
67
  if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
57
- return await requestAskUserQuestionAnswers(session, toolUseId, toolName, inputData, existing);
68
+ return await requestAskUserQuestionAnswers(session, toolUseId, inputData, existing);
58
69
  }
59
70
  const request = {
60
71
  tool_call: existing,
@@ -117,6 +128,9 @@ export async function createSession(params) {
117
128
  toolCalls: new Map(),
118
129
  taskToolUseIds: new Map(),
119
130
  pendingPermissions: new Map(),
131
+ pendingQuestions: new Map(),
132
+ pendingElicitations: new Map(),
133
+ mcpStatusRevalidatedAt: new Map(),
120
134
  authHintSent: false,
121
135
  ...(params.resumeUpdates && params.resumeUpdates.length > 0
122
136
  ? { resumeUpdates: params.resumeUpdates }
@@ -183,8 +197,8 @@ export async function createSession(params) {
183
197
  }
184
198
  })();
185
199
  }
186
- function permissionModeFromLaunchSettings(rawMode) {
187
- if (rawMode === undefined) {
200
+ function permissionModeFromSettingsValue(rawMode) {
201
+ if (typeof rawMode !== "string") {
188
202
  return undefined;
189
203
  }
190
204
  switch (rawMode) {
@@ -195,32 +209,41 @@ function permissionModeFromLaunchSettings(rawMode) {
195
209
  case "dontAsk":
196
210
  return rawMode;
197
211
  default:
198
- throw new Error(`unsupported launch_settings.permission_mode: ${rawMode}`);
212
+ throw new Error(`unsupported launch_settings.settings.permissions.defaultMode: ${rawMode}`);
199
213
  }
200
214
  }
201
215
  function initialSessionModel(launchSettings) {
202
- return launchSettings.model?.trim() || DEFAULT_MODEL_NAME;
216
+ const settings = settingsObjectFromLaunchSettings(launchSettings);
217
+ const model = typeof settings?.model === "string" ? settings.model.trim() : "";
218
+ return model || DEFAULT_MODEL_NAME;
203
219
  }
204
- function initialSessionMode(launchSettings) {
205
- return permissionModeFromLaunchSettings(launchSettings.permission_mode) ?? DEFAULT_PERMISSION_MODE;
220
+ function startupModelOption(launchSettings) {
221
+ const settings = settingsObjectFromLaunchSettings(launchSettings);
222
+ const model = typeof settings?.model === "string" ? settings.model.trim() : "";
223
+ return model ? { model } : {};
206
224
  }
207
- function thinkingConfigFromLaunchSettings(launchSettings) {
208
- switch (launchSettings.thinking_mode) {
209
- case undefined:
210
- return undefined;
211
- case "adaptive":
212
- return { type: "adaptive" };
213
- case "disabled":
214
- return { type: "disabled" };
215
- default:
216
- throw new Error(`unsupported launch_settings.thinking_mode: ${String(launchSettings.thinking_mode)}`);
217
- }
225
+ function initialSessionMode(launchSettings) {
226
+ const settings = settingsObjectFromLaunchSettings(launchSettings);
227
+ const permissions = settings?.permissions && typeof settings.permissions === "object" && !Array.isArray(settings.permissions)
228
+ ? settings.permissions
229
+ : undefined;
230
+ return permissionModeFromSettingsValue(permissions?.defaultMode) ?? DEFAULT_PERMISSION_MODE;
218
231
  }
219
- function effortFromLaunchSettings(launchSettings) {
220
- if (launchSettings.thinking_mode !== "adaptive") {
221
- return undefined;
232
+ function startupPermissionModeOptions(launchSettings) {
233
+ const settings = settingsObjectFromLaunchSettings(launchSettings);
234
+ const permissions = settings?.permissions && typeof settings.permissions === "object" && !Array.isArray(settings.permissions)
235
+ ? settings.permissions
236
+ : undefined;
237
+ const permissionMode = permissionModeFromSettingsValue(permissions?.defaultMode);
238
+ if (!permissionMode) {
239
+ return {};
222
240
  }
223
- return launchSettings.effort_level;
241
+ return permissionMode === "bypassPermissions"
242
+ ? {
243
+ permissionMode,
244
+ allowDangerouslySkipPermissions: true,
245
+ }
246
+ : { permissionMode };
224
247
  }
225
248
  function systemPromptFromLaunchSettings(launchSettings) {
226
249
  const language = launchSettings.language?.trim();
@@ -235,20 +258,22 @@ function systemPromptFromLaunchSettings(launchSettings) {
235
258
  };
236
259
  }
237
260
  export function buildQueryOptions(params) {
238
- const permissionMode = permissionModeFromLaunchSettings(params.launchSettings.permission_mode);
239
- const thinking = thinkingConfigFromLaunchSettings(params.launchSettings);
240
- const effort = effortFromLaunchSettings(params.launchSettings);
241
261
  const systemPrompt = systemPromptFromLaunchSettings(params.launchSettings);
262
+ const modelOption = startupModelOption(params.launchSettings);
263
+ const permissionModeOptions = startupPermissionModeOptions(params.launchSettings);
242
264
  return {
243
265
  cwd: params.cwd,
244
266
  includePartialMessages: true,
245
267
  executable: "node",
246
268
  ...(params.resume ? {} : { sessionId: params.provisionalSessionId }),
247
- ...(params.launchSettings.model ? { model: params.launchSettings.model } : {}),
269
+ ...(params.launchSettings.settings ? { settings: params.launchSettings.settings } : {}),
270
+ ...modelOption,
271
+ ...permissionModeOptions,
272
+ toolConfig: { askUserQuestion: { previewFormat: "markdown" } },
248
273
  ...(systemPrompt ? { systemPrompt } : {}),
249
- ...(permissionMode ? { permissionMode } : {}),
250
- ...(thinking ? { thinking } : {}),
251
- ...(effort ? { effort } : {}),
274
+ ...(params.launchSettings.agent_progress_summaries !== undefined
275
+ ? { agentProgressSummaries: params.launchSettings.agent_progress_summaries }
276
+ : {}),
252
277
  ...(params.claudeCodeExecutable
253
278
  ? { pathToClaudeCodeExecutable: params.claudeCodeExecutable }
254
279
  : {}),
@@ -283,21 +308,52 @@ export function buildQueryOptions(params) {
283
308
  resume: params.resume,
284
309
  canUseTool: params.canUseTool,
285
310
  onElicitation: async (request) => {
286
- const requestMode = typeof request.mode === "string" ? request.mode : "unknown";
287
- const requestServer = typeof request.serverName === "string" && request.serverName.trim().length > 0
288
- ? request.serverName
289
- : "unknown";
290
- const requestMessage = typeof request.message === "string" && request.message.trim().length > 0
291
- ? request.message
292
- : "<no message>";
293
- console.error(`[sdk warn] elicitation unsupported without MCP settings UI; ` +
294
- `auto-canceling session_id=${params.sessionIdForLogs()} server=${requestServer} ` +
295
- `mode=${requestMode} message=${JSON.stringify(requestMessage)}`);
296
- return { action: "cancel" };
311
+ const requestId = randomUUID();
312
+ const mode = request.mode === "form" || request.mode === "url"
313
+ ? request.mode
314
+ : typeof request.url === "string" && request.url.trim().length > 0
315
+ ? "url"
316
+ : "form";
317
+ const normalized = {
318
+ request_id: requestId,
319
+ server_name: typeof request.serverName === "string" && request.serverName.trim().length > 0
320
+ ? request.serverName
321
+ : "unknown",
322
+ message: typeof request.message === "string" && request.message.trim().length > 0
323
+ ? request.message
324
+ : "<no message>",
325
+ mode,
326
+ ...(typeof request.url === "string" && request.url.trim().length > 0
327
+ ? { url: request.url }
328
+ : {}),
329
+ ...(typeof request.elicitationId === "string" && request.elicitationId.trim().length > 0
330
+ ? { elicitation_id: request.elicitationId }
331
+ : {}),
332
+ ...(request.requestedSchema
333
+ ? { requested_schema: request.requestedSchema }
334
+ : {}),
335
+ };
336
+ writeEvent({
337
+ event: "elicitation_request",
338
+ session_id: params.sessionIdForLogs(),
339
+ request: normalized,
340
+ });
341
+ return await new Promise((resolve) => {
342
+ const currentSession = sessions.get(params.sessionIdForLogs());
343
+ if (!currentSession) {
344
+ resolve({ action: "cancel" });
345
+ return;
346
+ }
347
+ currentSession.pendingElicitations.set(requestId, {
348
+ resolve,
349
+ serverName: normalized.server_name,
350
+ elicitationId: normalized.elicitation_id,
351
+ });
352
+ });
297
353
  },
298
354
  };
299
355
  }
300
- function mapAvailableModels(models) {
356
+ export function mapAvailableModels(models) {
301
357
  if (!Array.isArray(models)) {
302
358
  return [];
303
359
  }
@@ -315,6 +371,15 @@ function mapAvailableModels(models) {
315
371
  supported_effort_levels: Array.isArray(entry.supportedEffortLevels)
316
372
  ? entry.supportedEffortLevels.filter((level) => level === "low" || level === "medium" || level === "high")
317
373
  : [],
374
+ ...(typeof entry.supportsAdaptiveThinking === "boolean"
375
+ ? { supports_adaptive_thinking: entry.supportsAdaptiveThinking }
376
+ : {}),
377
+ ...(typeof entry.supportsFastMode === "boolean"
378
+ ? { supports_fast_mode: entry.supportsFastMode }
379
+ : {}),
380
+ ...(typeof entry.supportsAutoMode === "boolean"
381
+ ? { supports_auto_mode: entry.supportsAutoMode }
382
+ : {}),
318
383
  ...(typeof entry.description === "string" && entry.description.trim().length > 0
319
384
  ? { description: entry.description }
320
385
  : {}),
@@ -366,3 +431,36 @@ export function handlePermissionResponse(command) {
366
431
  }
367
432
  resolver.resolve(permissionResult);
368
433
  }
434
+ export function handleQuestionResponse(command) {
435
+ const session = sessionById(command.session_id);
436
+ if (!session) {
437
+ logPermissionDebug(`question response dropped: unknown session session_id=${command.session_id} tool_call_id=${command.tool_call_id}`);
438
+ return;
439
+ }
440
+ const resolver = session.pendingQuestions.get(command.tool_call_id);
441
+ if (!resolver) {
442
+ logPermissionDebug(`question response dropped: no pending resolver session_id=${command.session_id} tool_call_id=${command.tool_call_id}`);
443
+ return;
444
+ }
445
+ session.pendingQuestions.delete(command.tool_call_id);
446
+ resolver.onOutcome(command.outcome);
447
+ }
448
+ export function handleElicitationResponse(command) {
449
+ const session = sessionById(command.session_id);
450
+ if (!session) {
451
+ console.error(`[sdk warn] elicitation response dropped: unknown session ` +
452
+ `session_id=${command.session_id} request_id=${command.elicitation_request_id}`);
453
+ return;
454
+ }
455
+ const pending = session.pendingElicitations.get(command.elicitation_request_id);
456
+ if (!pending) {
457
+ console.error(`[sdk warn] elicitation response dropped: no pending request ` +
458
+ `session_id=${command.session_id} request_id=${command.elicitation_request_id}`);
459
+ return;
460
+ }
461
+ session.pendingElicitations.delete(command.elicitation_request_id);
462
+ pending.resolve({
463
+ action: command.action,
464
+ ...(command.content ? { content: command.content } : {}),
465
+ });
466
+ }
@@ -67,9 +67,9 @@ export function emitPlanIfTodoWrite(session, name, input) {
67
67
  emitSessionUpdate(session.sessionId, { type: "plan", entries });
68
68
  }
69
69
  }
70
- export function emitToolResultUpdate(session, toolUseId, isError, rawContent) {
70
+ export function emitToolResultUpdate(session, toolUseId, isError, rawContent, rawResult = rawContent) {
71
71
  const base = session.toolCalls.get(toolUseId);
72
- const fields = buildToolResultFields(isError, rawContent, base);
72
+ const fields = buildToolResultFields(isError, rawContent, base, rawResult);
73
73
  const update = { tool_call_id: toolUseId, fields };
74
74
  emitSessionUpdate(session.sessionId, { type: "tool_call_update", tool_call_update: update });
75
75
  if (base) {
@@ -80,6 +80,9 @@ export function emitToolResultUpdate(session, toolUseId, isError, rawContent) {
80
80
  if (fields.content) {
81
81
  base.content = fields.content;
82
82
  }
83
+ if (fields.output_metadata) {
84
+ base.output_metadata = fields.output_metadata;
85
+ }
83
86
  }
84
87
  }
85
88
  export function finalizeOpenToolCalls(session, status) {
@@ -101,7 +104,9 @@ export function emitToolProgressUpdate(session, toolUseId, toolName) {
101
104
  emitToolCall(session, toolUseId, toolName, {});
102
105
  return;
103
106
  }
104
- if (existing.status === "in_progress") {
107
+ if (existing.status === "in_progress" ||
108
+ existing.status === "completed" ||
109
+ existing.status === "failed") {
105
110
  return;
106
111
  }
107
112
  const fields = { status: "in_progress" };
@@ -159,6 +164,10 @@ export function resolveTaskToolUseId(session, msg) {
159
164
  return session.taskToolUseIds.get(taskId) ?? "";
160
165
  }
161
166
  export function taskProgressText(msg) {
167
+ const summary = typeof msg.summary === "string" ? msg.summary.trim() : "";
168
+ if (summary) {
169
+ return summary;
170
+ }
162
171
  const description = typeof msg.description === "string" ? msg.description : "";
163
172
  const lastTool = typeof msg.last_tool_name === "string" ? msg.last_tool_name : "";
164
173
  if (description && lastTool) {
@@ -18,6 +18,7 @@ export function normalizeToolKind(name) {
18
18
  case "Bash":
19
19
  return "execute";
20
20
  case "Read":
21
+ case "ReadMcpResource":
21
22
  return "read";
22
23
  case "Write":
23
24
  case "Edit":
@@ -75,6 +76,16 @@ export function toolTitle(name, input) {
75
76
  if ((name === "Read" || name === "Write" || name === "Edit") && typeof input.file_path === "string") {
76
77
  return `${name} ${input.file_path}`;
77
78
  }
79
+ if (name === "ReadMcpResource") {
80
+ const uri = typeof input.uri === "string" ? input.uri : "";
81
+ const server = typeof input.server === "string" ? input.server : "";
82
+ if (server && uri) {
83
+ return `ReadMcpResource ${server} ${uri}`;
84
+ }
85
+ if (uri) {
86
+ return `ReadMcpResource ${uri}`;
87
+ }
88
+ }
78
89
  return name;
79
90
  }
80
91
  function editDiffContent(name, input) {
@@ -115,6 +126,145 @@ export function createToolCall(toolUseId, name, input) {
115
126
  },
116
127
  };
117
128
  }
129
+ function resultRecordCandidates(rawResult, rawContent) {
130
+ const candidates = [];
131
+ const pushRecord = (value) => {
132
+ const record = asRecordOrNull(value);
133
+ if (record) {
134
+ candidates.push(record);
135
+ }
136
+ };
137
+ const pushNestedRecords = (value) => {
138
+ const record = asRecordOrNull(value);
139
+ if (!record) {
140
+ return;
141
+ }
142
+ pushRecord(record.result);
143
+ pushRecord(record.data);
144
+ pushRecord(record.content);
145
+ };
146
+ pushRecord(rawResult);
147
+ pushNestedRecords(rawResult);
148
+ pushRecord(rawContent);
149
+ pushNestedRecords(rawContent);
150
+ return candidates;
151
+ }
152
+ function parseJsonCandidate(value) {
153
+ const text = typeof value === "string" ? value : extractText(value);
154
+ const trimmed = text.trim();
155
+ if (!(trimmed.startsWith("{") || trimmed.startsWith("["))) {
156
+ return undefined;
157
+ }
158
+ try {
159
+ return JSON.parse(trimmed);
160
+ }
161
+ catch {
162
+ return undefined;
163
+ }
164
+ }
165
+ function pushStructuredRecordCandidates(candidates, value) {
166
+ const record = asRecordOrNull(value);
167
+ if (!record) {
168
+ return;
169
+ }
170
+ candidates.push(record);
171
+ const nestedResult = asRecordOrNull(record.result);
172
+ if (nestedResult) {
173
+ candidates.push(nestedResult);
174
+ }
175
+ const nestedData = asRecordOrNull(record.data);
176
+ if (nestedData) {
177
+ candidates.push(nestedData);
178
+ }
179
+ const nestedContent = asRecordOrNull(record.content);
180
+ if (nestedContent) {
181
+ candidates.push(nestedContent);
182
+ }
183
+ }
184
+ function mcpResourceContentFromResult(rawResult, rawContent) {
185
+ const candidates = [];
186
+ for (const candidate of [rawResult, rawContent, parseJsonCandidate(rawResult), parseJsonCandidate(rawContent)]) {
187
+ pushStructuredRecordCandidates(candidates, candidate);
188
+ }
189
+ for (const candidate of candidates) {
190
+ const contents = Array.isArray(candidate.contents) ? candidate.contents : null;
191
+ if (!contents || contents.length === 0) {
192
+ continue;
193
+ }
194
+ const mapped = [];
195
+ for (const entry of contents) {
196
+ const record = asRecordOrNull(entry);
197
+ if (!record) {
198
+ continue;
199
+ }
200
+ const uri = typeof record.uri === "string" ? record.uri : "";
201
+ if (!uri) {
202
+ continue;
203
+ }
204
+ const text = typeof record.text === "string" && record.text.length > 0 ? record.text : undefined;
205
+ const mimeType = typeof record.mimeType === "string" && record.mimeType.trim().length > 0
206
+ ? record.mimeType.trim()
207
+ : undefined;
208
+ const blobSavedTo = typeof record.blobSavedTo === "string" && record.blobSavedTo.trim().length > 0
209
+ ? record.blobSavedTo.trim()
210
+ : undefined;
211
+ if (!text && !blobSavedTo) {
212
+ continue;
213
+ }
214
+ mapped.push({
215
+ type: "mcp_resource",
216
+ uri,
217
+ ...(mimeType ? { mime_type: mimeType } : {}),
218
+ ...(text ? { text } : {}),
219
+ ...(blobSavedTo ? { blob_saved_to: blobSavedTo } : {}),
220
+ });
221
+ }
222
+ if (mapped.length > 0) {
223
+ return mapped;
224
+ }
225
+ }
226
+ return [];
227
+ }
228
+ function extractToolOutputMetadata(toolName, rawResult, rawContent) {
229
+ const candidates = resultRecordCandidates(rawResult, rawContent);
230
+ if (toolName === "Bash") {
231
+ for (const candidate of candidates) {
232
+ const hasAssistantAutoBackgrounded = typeof candidate.assistantAutoBackgrounded === "boolean";
233
+ const hasTokenSaverOutput = typeof candidate.tokenSaverOutput === "string" && candidate.tokenSaverOutput.length > 0;
234
+ if (hasAssistantAutoBackgrounded || hasTokenSaverOutput) {
235
+ const bashMetadata = {};
236
+ if (hasAssistantAutoBackgrounded) {
237
+ bashMetadata.assistant_auto_backgrounded = candidate.assistantAutoBackgrounded;
238
+ }
239
+ if (hasTokenSaverOutput) {
240
+ bashMetadata.token_saver_active = true;
241
+ }
242
+ return {
243
+ bash: bashMetadata,
244
+ };
245
+ }
246
+ }
247
+ return undefined;
248
+ }
249
+ if (toolName === "ExitPlanMode") {
250
+ for (const candidate of candidates) {
251
+ if (typeof candidate.isUltraplan === "boolean") {
252
+ return { exit_plan_mode: { is_ultraplan: candidate.isUltraplan } };
253
+ }
254
+ }
255
+ return undefined;
256
+ }
257
+ if (toolName === "TodoWrite") {
258
+ for (const candidate of candidates) {
259
+ if (typeof candidate.verificationNudgeNeeded === "boolean") {
260
+ return {
261
+ todo_write: { verification_nudge_needed: candidate.verificationNudgeNeeded },
262
+ };
263
+ }
264
+ }
265
+ }
266
+ return undefined;
267
+ }
118
268
  export function extractText(value) {
119
269
  if (typeof value === "string") {
120
270
  return value;
@@ -255,21 +405,129 @@ function writeDiffFromResult(rawContent) {
255
405
  : "";
256
406
  const content = typeof record.content === "string" ? record.content : "";
257
407
  const originalRaw = "originalFile" in record ? record.originalFile : "original_file" in record ? record.original_file : undefined;
408
+ const gitDiff = asRecordOrNull(record.gitDiff);
409
+ const repository = typeof gitDiff?.repository === "string" && gitDiff.repository.trim().length > 0
410
+ ? gitDiff.repository.trim()
411
+ : undefined;
258
412
  if (!filePath || !content || originalRaw === undefined) {
259
413
  continue;
260
414
  }
261
415
  const original = typeof originalRaw === "string" ? originalRaw : originalRaw === null ? "" : "";
262
- return [{ type: "diff", old_path: filePath, new_path: filePath, old: original, new: content }];
416
+ return [
417
+ {
418
+ type: "diff",
419
+ old_path: filePath,
420
+ new_path: filePath,
421
+ old: original,
422
+ new: content,
423
+ ...(repository ? { repository } : {}),
424
+ },
425
+ ];
263
426
  }
264
427
  return [];
265
428
  }
266
- export function buildToolResultFields(isError, rawContent, base) {
267
- const rawOutput = normalizeToolResultText(rawContent, isError);
429
+ function editDiffFromResult(rawResult, rawInput) {
430
+ const input = asRecordOrNull(rawInput);
431
+ const filePath = typeof input?.file_path === "string" ? input.file_path : "";
432
+ const oldText = typeof input?.old_string === "string"
433
+ ? input.old_string
434
+ : typeof input?.oldString === "string"
435
+ ? input.oldString
436
+ : "";
437
+ const newText = typeof input?.new_string === "string"
438
+ ? input.new_string
439
+ : typeof input?.newString === "string"
440
+ ? input.newString
441
+ : "";
442
+ if (!filePath || (!oldText && !newText)) {
443
+ return [];
444
+ }
445
+ for (const candidate of resultRecordCandidates(rawResult, undefined)) {
446
+ const candidatePath = typeof candidate.filePath === "string"
447
+ ? candidate.filePath
448
+ : typeof candidate.file_path === "string"
449
+ ? candidate.file_path
450
+ : "";
451
+ const gitDiff = asRecordOrNull(candidate.gitDiff);
452
+ if (!candidatePath && !gitDiff) {
453
+ continue;
454
+ }
455
+ if (candidatePath && candidatePath !== filePath) {
456
+ continue;
457
+ }
458
+ const repository = typeof gitDiff?.repository === "string" && gitDiff.repository.trim().length > 0
459
+ ? gitDiff.repository.trim()
460
+ : undefined;
461
+ return [
462
+ {
463
+ type: "diff",
464
+ old_path: filePath,
465
+ new_path: filePath,
466
+ old: oldText,
467
+ new: newText,
468
+ ...(repository ? { repository } : {}),
469
+ },
470
+ ];
471
+ }
472
+ return editDiffFromInput(rawInput);
473
+ }
474
+ function findBashResultRecord(rawResult, rawContent) {
475
+ return resultRecordCandidates(rawResult, rawContent).find((candidate) => "stdout" in candidate ||
476
+ "stderr" in candidate ||
477
+ "backgroundTaskId" in candidate ||
478
+ "backgroundedByUser" in candidate ||
479
+ "assistantAutoBackgrounded" in candidate ||
480
+ "tokenSaverOutput" in candidate);
481
+ }
482
+ function bashBackgroundMessage(record) {
483
+ const backgroundTaskId = typeof record.backgroundTaskId === "string" ? record.backgroundTaskId : "";
484
+ if (!backgroundTaskId) {
485
+ return "";
486
+ }
487
+ if (record.assistantAutoBackgrounded === true) {
488
+ return `Command was auto-backgrounded by assistant mode with ID: ${backgroundTaskId}.`;
489
+ }
490
+ if (record.backgroundedByUser === true) {
491
+ return `Command was backgrounded by user with ID: ${backgroundTaskId}.`;
492
+ }
493
+ return `Command is running in background with ID: ${backgroundTaskId}.`;
494
+ }
495
+ function buildBashDisplayOutput(record) {
496
+ const segments = [];
497
+ const stdout = typeof record.stdout === "string" ? record.stdout : "";
498
+ const stderr = typeof record.stderr === "string" ? record.stderr : "";
499
+ if (stdout) {
500
+ segments.push(stdout);
501
+ }
502
+ if (stderr) {
503
+ segments.push(stderr);
504
+ }
505
+ if (record.interrupted === true) {
506
+ segments.push("Command was aborted before completion.");
507
+ }
508
+ const backgroundMessage = bashBackgroundMessage(record);
509
+ if (backgroundMessage) {
510
+ segments.push(backgroundMessage);
511
+ }
512
+ return segments.join("\n");
513
+ }
514
+ export function buildToolResultFields(isError, rawContent, base, rawResult) {
268
515
  const toolName = resolveToolName(base);
516
+ const bashResultRecord = toolName === "Bash" ? findBashResultRecord(rawResult, rawContent) : undefined;
517
+ const normalizedRawOutput = normalizeToolResultText(rawContent, isError);
518
+ const rawOutput = bashResultRecord
519
+ ? buildBashDisplayOutput(bashResultRecord)
520
+ : normalizedRawOutput || JSON.stringify(rawContent);
269
521
  const fields = {
270
522
  status: isError ? "failed" : "completed",
271
- raw_output: rawOutput || JSON.stringify(rawContent),
272
523
  };
524
+ if (rawOutput) {
525
+ fields.raw_output = rawOutput;
526
+ }
527
+ const outputMetadata = extractToolOutputMetadata(toolName, rawResult, rawContent);
528
+ if (outputMetadata) {
529
+ fields.output_metadata = outputMetadata;
530
+ }
273
531
  if (!isError && toolName === "Write") {
274
532
  const structuredDiff = writeDiffFromResult(rawContent);
275
533
  if (structuredDiff.length > 0) {
@@ -283,15 +541,22 @@ export function buildToolResultFields(isError, rawContent, base) {
283
541
  }
284
542
  }
285
543
  if (!isError && toolName === "Edit") {
286
- const inputDiff = editDiffFromInput(base?.raw_input);
287
- if (inputDiff.length > 0) {
288
- fields.content = inputDiff;
544
+ const structuredDiff = editDiffFromResult(rawResult, base?.raw_input);
545
+ if (structuredDiff.length > 0) {
546
+ fields.content = structuredDiff;
289
547
  return fields;
290
548
  }
291
549
  if (base?.content.some((entry) => entry.type === "diff")) {
292
550
  return fields;
293
551
  }
294
552
  }
553
+ if (!isError && toolName === "ReadMcpResource") {
554
+ const structuredResourceContent = mcpResourceContentFromResult(rawResult, rawContent);
555
+ if (structuredResourceContent.length > 0) {
556
+ fields.content = structuredResourceContent;
557
+ return fields;
558
+ }
559
+ }
295
560
  if (rawOutput) {
296
561
  fields.content = [{ type: "content", content: { type: "text", text: rawOutput } }];
297
562
  }