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.
@@ -63,10 +63,15 @@ export function parseAskUserQuestionPrompts(inputData) {
63
63
  }
64
64
  const label = typeof optionRecord.label === "string" ? optionRecord.label.trim() : "";
65
65
  const description = typeof optionRecord.description === "string" ? optionRecord.description.trim() : "";
66
+ const preview = typeof optionRecord.preview === "string" ? optionRecord.preview.trim() : "";
66
67
  if (!label) {
67
68
  continue;
68
69
  }
69
- options.push({ label, description });
70
+ options.push({
71
+ label,
72
+ description,
73
+ ...(preview.length > 0 ? { preview } : {}),
74
+ });
70
75
  }
71
76
  if (options.length < 2) {
72
77
  continue;
@@ -78,38 +83,74 @@ export function parseAskUserQuestionPrompts(inputData) {
78
83
  function askUserQuestionOptions(prompt) {
79
84
  return prompt.options.map((option, index) => ({
80
85
  option_id: `question_${index}`,
81
- name: option.label,
86
+ label: option.label,
82
87
  description: option.description,
83
- kind: QUESTION_CHOICE_KIND,
88
+ ...(option.preview ? { preview: option.preview } : {}),
84
89
  }));
85
90
  }
91
+ function askUserQuestionPromptRawInput(prompt, index, total) {
92
+ return {
93
+ prompt: {
94
+ question: prompt.question,
95
+ header: prompt.header,
96
+ multi_select: prompt.multiSelect,
97
+ options: prompt.options.map((option, optionIndex) => ({
98
+ option_id: `question_${optionIndex}`,
99
+ label: option.label,
100
+ description: option.description,
101
+ ...(option.preview ? { preview: option.preview } : {}),
102
+ })),
103
+ },
104
+ question_index: index,
105
+ total_questions: total,
106
+ };
107
+ }
86
108
  function askUserQuestionPromptToolCall(base, prompt, index, total) {
87
109
  return {
88
110
  ...base,
89
111
  title: prompt.question,
90
- raw_input: {
91
- questions: [
92
- {
93
- question: prompt.question,
94
- header: prompt.header,
95
- multiSelect: prompt.multiSelect,
96
- options: prompt.options,
97
- },
98
- ],
99
- question_index: index,
100
- total_questions: total,
112
+ raw_input: askUserQuestionPromptRawInput(prompt, index, total),
113
+ };
114
+ }
115
+ function buildQuestionRequest(promptToolCall, prompt, index, total) {
116
+ return {
117
+ tool_call: promptToolCall,
118
+ prompt: {
119
+ question: prompt.question,
120
+ header: prompt.header,
121
+ multi_select: prompt.multiSelect,
122
+ options: askUserQuestionOptions(prompt),
101
123
  },
124
+ question_index: index,
125
+ total_questions: total,
102
126
  };
103
127
  }
104
128
  function askUserQuestionTranscript(answers) {
105
129
  return answers.map((entry) => `${entry.header}: ${entry.answer}\n ${entry.question}`).join("\n");
106
130
  }
107
- export async function requestAskUserQuestionAnswers(session, toolUseId, toolName, inputData, baseToolCall) {
131
+ function deriveAnnotation(selectedOptions, annotation) {
132
+ const preview = annotation?.preview?.trim().length
133
+ ? annotation.preview
134
+ : selectedOptions
135
+ .map((option) => option.preview?.trim() ?? "")
136
+ .filter((previewText) => previewText.length > 0)
137
+ .join("\n\n");
138
+ const notes = annotation?.notes?.trim().length ? annotation.notes.trim() : undefined;
139
+ if (!preview && !notes) {
140
+ return undefined;
141
+ }
142
+ return {
143
+ ...(preview ? { preview } : {}),
144
+ ...(notes ? { notes } : {}),
145
+ };
146
+ }
147
+ export async function requestAskUserQuestionAnswers(session, toolUseId, inputData, baseToolCall) {
108
148
  const prompts = parseAskUserQuestionPrompts(inputData);
109
149
  if (prompts.length === 0) {
110
150
  return { behavior: "allow", updatedInput: inputData, toolUseID: toolUseId };
111
151
  }
112
152
  const answers = {};
153
+ const annotations = {};
113
154
  const transcript = [];
114
155
  for (const [index, prompt] of prompts.entries()) {
115
156
  const promptToolCall = askUserQuestionPromptToolCall(baseToolCall, prompt, index, prompts.length);
@@ -128,29 +169,32 @@ export async function requestAskUserQuestionAnswers(session, toolUseId, toolName
128
169
  tracked.status = "in_progress";
129
170
  tracked.raw_input = promptToolCall.raw_input;
130
171
  }
131
- const request = {
132
- tool_call: promptToolCall,
133
- options: askUserQuestionOptions(prompt),
134
- };
172
+ const request = buildQuestionRequest(promptToolCall, prompt, index, prompts.length);
135
173
  const outcome = await new Promise((resolve) => {
136
- session.pendingPermissions.set(toolUseId, {
174
+ session.pendingQuestions.set(toolUseId, {
137
175
  onOutcome: resolve,
138
- toolName,
176
+ toolName: ASK_USER_QUESTION_TOOL_NAME,
139
177
  inputData,
140
178
  });
141
- writeEvent({ event: "permission_request", session_id: session.sessionId, request });
179
+ writeEvent({ event: "question_request", session_id: session.sessionId, request });
142
180
  });
143
- if (outcome.outcome !== "selected") {
181
+ if (outcome.outcome !== "answered") {
144
182
  setToolCallStatus(session, toolUseId, "failed", "Question cancelled");
145
183
  return { behavior: "deny", message: "Question cancelled", toolUseID: toolUseId };
146
184
  }
147
- const selected = request.options.find((option) => option.option_id === outcome.option_id);
148
- if (!selected) {
185
+ const selectedOptions = request.prompt.options.filter((option) => outcome.selected_option_ids.includes(option.option_id));
186
+ if (selectedOptions.length === 0 ||
187
+ (!prompt.multiSelect && selectedOptions.length !== 1)) {
149
188
  setToolCallStatus(session, toolUseId, "failed", "Question answer was invalid");
150
189
  return { behavior: "deny", message: "Question answer was invalid", toolUseID: toolUseId };
151
190
  }
152
- answers[prompt.question] = selected.name;
153
- transcript.push({ header: prompt.header, question: prompt.question, answer: selected.name });
191
+ const answer = selectedOptions.map((option) => option.label).join(", ");
192
+ answers[prompt.question] = answer;
193
+ const annotation = deriveAnnotation(selectedOptions, outcome.annotation);
194
+ if (annotation) {
195
+ annotations[prompt.question] = annotation;
196
+ }
197
+ transcript.push({ header: prompt.header, question: prompt.question, answer });
154
198
  const summary = askUserQuestionTranscript(transcript);
155
199
  const progressFields = {
156
200
  status: index + 1 >= prompts.length ? "completed" : "in_progress",
@@ -169,7 +213,11 @@ export async function requestAskUserQuestionAnswers(session, toolUseId, toolName
169
213
  }
170
214
  return {
171
215
  behavior: "allow",
172
- updatedInput: { ...inputData, answers },
216
+ updatedInput: {
217
+ ...inputData,
218
+ answers,
219
+ ...(Object.keys(annotations).length > 0 ? { annotations } : {}),
220
+ },
173
221
  toolUseID: toolUseId,
174
222
  };
175
223
  }
@@ -1,12 +1,15 @@
1
1
  import { createRequire } from "node:module";
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
2
4
  import readline from "node:readline";
3
5
  import { pathToFileURL } from "node:url";
4
- import { getSessionMessages, listSessions, } from "@anthropic-ai/claude-agent-sdk";
6
+ import { getSessionMessages, listSessions, renameSession, } from "@anthropic-ai/claude-agent-sdk";
5
7
  import { parseCommandEnvelope, toPermissionMode } from "./bridge/commands.js";
6
- import { writeEvent, failConnection, slashError, emitSessionUpdate, emitSessionsList, } from "./bridge/events.js";
8
+ import { writeEvent, failConnection, slashError, emitSessionUpdate, emitSessionsList, currentSessionListOptions, setSessionListingDir, } from "./bridge/events.js";
7
9
  import { textFromPrompt } from "./bridge/message_handlers.js";
8
- import { sessions, sessionById, createSession, closeAllSessions, handlePermissionResponse, } from "./bridge/session_lifecycle.js";
10
+ import { sessions, sessionById, createSession, closeAllSessions, handleElicitationResponse, handlePermissionResponse, handleQuestionResponse, } from "./bridge/session_lifecycle.js";
9
11
  import { mapSessionMessagesToUpdates } from "./bridge/history.js";
12
+ import { MCP_STALE_STATUS_REVALIDATION_COOLDOWN_MS, handleMcpAuthenticateCommand, handleMcpClearAuthCommand, handleMcpOauthCallbackUrlCommand, handleMcpReconnectCommand, handleMcpSetServersCommand, handleMcpStatusCommand, handleMcpToggleCommand, staleMcpAuthCandidates, } from "./bridge/mcp.js";
10
13
  // Re-exports: all symbols that tests and external consumers import from bridge.js.
11
14
  export { AsyncQueue, logPermissionDebug } from "./bridge/shared.js";
12
15
  export { asRecordOrNull } from "./bridge/shared.js";
@@ -14,16 +17,37 @@ export { CACHE_SPLIT_POLICY, previewKilobyteLabel } from "./bridge/cache_policy.
14
17
  export { buildToolResultFields, createToolCall, normalizeToolKind, normalizeToolResultText, unwrapToolUseResult, } from "./bridge/tooling.js";
15
18
  export { looksLikeAuthRequired } from "./bridge/auth.js";
16
19
  export { parseCommandEnvelope } from "./bridge/commands.js";
20
+ export { buildSessionListOptions } from "./bridge/events.js";
17
21
  export { permissionOptionsFromSuggestions, permissionResultFromOutcome, } from "./bridge/permissions.js";
18
22
  export { mapSessionMessagesToUpdates, mapSdkSessions, } from "./bridge/history.js";
23
+ export { handleTaskSystemMessage } from "./bridge/message_handlers.js";
19
24
  export { mapAvailableAgents } from "./bridge/agents.js";
20
- export { buildQueryOptions } from "./bridge/session_lifecycle.js";
25
+ export { buildQueryOptions, mapAvailableModels } from "./bridge/session_lifecycle.js";
21
26
  export { parseFastModeState, parseRateLimitStatus, buildRateLimitUpdate, } from "./bridge/state_parsing.js";
22
- const EXPECTED_AGENT_SDK_VERSION = "0.2.63";
27
+ export { MCP_STALE_STATUS_REVALIDATION_COOLDOWN_MS, staleMcpAuthCandidates };
28
+ export function buildSessionMutationOptions(cwd) {
29
+ return cwd ? { dir: cwd } : undefined;
30
+ }
31
+ export function canGenerateSessionTitle(query) {
32
+ return typeof query.generateSessionTitle === "function";
33
+ }
34
+ export async function generatePersistedSessionTitle(query, description) {
35
+ if (!canGenerateSessionTitle(query)) {
36
+ throw new Error("SDK query does not support generateSessionTitle");
37
+ }
38
+ const title = await query.generateSessionTitle(description, { persist: true });
39
+ if (typeof title !== "string" || title.trim().length === 0) {
40
+ throw new Error("SDK did not return a generated session title");
41
+ }
42
+ return title;
43
+ }
44
+ const EXPECTED_AGENT_SDK_VERSION = "0.2.74";
23
45
  const require = createRequire(import.meta.url);
24
46
  export function resolveInstalledAgentSdkVersion() {
25
47
  try {
26
- const pkg = require("@anthropic-ai/claude-agent-sdk/package.json");
48
+ const entryPath = require.resolve("@anthropic-ai/claude-agent-sdk");
49
+ const packageJsonPath = join(dirname(entryPath), "package.json");
50
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
27
51
  return typeof pkg.version === "string" ? pkg.version : undefined;
28
52
  }
29
53
  catch {
@@ -54,6 +78,7 @@ async function handleCommand(command, requestId) {
54
78
  failConnection(sdkVersionError, requestId);
55
79
  return;
56
80
  }
81
+ setSessionListingDir(command.cwd);
57
82
  writeEvent({
58
83
  event: "initialized",
59
84
  result: {
@@ -77,6 +102,7 @@ async function handleCommand(command, requestId) {
77
102
  await emitSessionsList(requestId);
78
103
  return;
79
104
  case "create_session":
105
+ setSessionListingDir(command.cwd);
80
106
  await createSession({
81
107
  cwd: command.cwd,
82
108
  resume: command.resume,
@@ -87,12 +113,13 @@ async function handleCommand(command, requestId) {
87
113
  return;
88
114
  case "resume_session": {
89
115
  try {
90
- const sdkSessions = await listSessions();
116
+ const sdkSessions = await listSessions(currentSessionListOptions());
91
117
  const matched = sdkSessions.find((entry) => entry.sessionId === command.session_id);
92
118
  if (!matched) {
93
119
  slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
94
120
  return;
95
121
  }
122
+ setSessionListingDir(matched.cwd ?? process.cwd());
96
123
  const historyMessages = await getSessionMessages(command.session_id, matched.cwd ? { dir: matched.cwd } : undefined);
97
124
  const resumeUpdates = mapSessionMessagesToUpdates(historyMessages);
98
125
  const staleSessions = Array.from(sessions.values());
@@ -115,6 +142,7 @@ async function handleCommand(command, requestId) {
115
142
  }
116
143
  case "new_session":
117
144
  await closeAllSessions();
145
+ setSessionListingDir(command.cwd);
118
146
  await createSession({
119
147
  cwd: command.cwd,
120
148
  launchSettings: command.launch_settings,
@@ -186,9 +214,132 @@ async function handleCommand(command, requestId) {
186
214
  });
187
215
  return;
188
216
  }
217
+ case "generate_session_title": {
218
+ const session = sessionById(command.session_id);
219
+ if (!session) {
220
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
221
+ return;
222
+ }
223
+ try {
224
+ await generatePersistedSessionTitle(session.query, command.description);
225
+ setSessionListingDir(session.cwd);
226
+ await emitSessionsList(requestId);
227
+ }
228
+ catch (error) {
229
+ const message = error instanceof Error ? error.message : String(error);
230
+ slashError(command.session_id, `failed to generate session title: ${message}`, requestId);
231
+ }
232
+ return;
233
+ }
234
+ case "rename_session": {
235
+ const session = sessionById(command.session_id);
236
+ if (!session) {
237
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
238
+ return;
239
+ }
240
+ try {
241
+ await renameSession(command.session_id, command.title, buildSessionMutationOptions(session.cwd));
242
+ setSessionListingDir(session.cwd);
243
+ await emitSessionsList(requestId);
244
+ }
245
+ catch (error) {
246
+ const message = error instanceof Error ? error.message : String(error);
247
+ slashError(command.session_id, `failed to rename session: ${message}`, requestId);
248
+ }
249
+ return;
250
+ }
251
+ case "get_status_snapshot": {
252
+ const session = sessionById(command.session_id);
253
+ if (!session) {
254
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
255
+ return;
256
+ }
257
+ const account = await session.query.accountInfo();
258
+ writeEvent({
259
+ event: "status_snapshot",
260
+ session_id: session.sessionId,
261
+ account: {
262
+ email: account.email,
263
+ organization: account.organization,
264
+ subscription_type: account.subscriptionType,
265
+ token_source: account.tokenSource,
266
+ api_key_source: account.apiKeySource,
267
+ },
268
+ }, requestId);
269
+ return;
270
+ }
271
+ case "mcp_status": {
272
+ const session = sessionById(command.session_id);
273
+ if (!session) {
274
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
275
+ return;
276
+ }
277
+ await handleMcpStatusCommand(session, requestId);
278
+ return;
279
+ }
280
+ case "mcp_reconnect": {
281
+ const session = sessionById(command.session_id);
282
+ if (!session) {
283
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
284
+ return;
285
+ }
286
+ await handleMcpReconnectCommand(session, command, requestId);
287
+ return;
288
+ }
289
+ case "mcp_toggle": {
290
+ const session = sessionById(command.session_id);
291
+ if (!session) {
292
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
293
+ return;
294
+ }
295
+ await handleMcpToggleCommand(session, command, requestId);
296
+ return;
297
+ }
298
+ case "mcp_set_servers": {
299
+ const session = sessionById(command.session_id);
300
+ if (!session) {
301
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
302
+ return;
303
+ }
304
+ await handleMcpSetServersCommand(session, command, requestId);
305
+ return;
306
+ }
307
+ case "mcp_authenticate": {
308
+ const session = sessionById(command.session_id);
309
+ if (!session) {
310
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
311
+ return;
312
+ }
313
+ await handleMcpAuthenticateCommand(session, command, requestId);
314
+ return;
315
+ }
316
+ case "mcp_clear_auth": {
317
+ const session = sessionById(command.session_id);
318
+ if (!session) {
319
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
320
+ return;
321
+ }
322
+ await handleMcpClearAuthCommand(session, command, requestId);
323
+ return;
324
+ }
325
+ case "mcp_oauth_callback_url": {
326
+ const session = sessionById(command.session_id);
327
+ if (!session) {
328
+ slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
329
+ return;
330
+ }
331
+ await handleMcpOauthCallbackUrlCommand(session, command, requestId);
332
+ return;
333
+ }
189
334
  case "permission_response":
190
335
  handlePermissionResponse(command);
191
336
  return;
337
+ case "question_response":
338
+ handleQuestionResponse(command);
339
+ return;
340
+ case "elicitation_response":
341
+ handleElicitationResponse(command);
342
+ return;
192
343
  case "shutdown":
193
344
  await closeAllSessions();
194
345
  process.exit(0);