claude-code-rust 0.9.0 → 0.10.0

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.
@@ -6,12 +6,13 @@ import { pathToFileURL } from "node:url";
6
6
  import { getSessionMessages, listSessions, renameSession, } from "@anthropic-ai/claude-agent-sdk";
7
7
  import { parseCommandEnvelope, toPermissionMode } from "./bridge/commands.js";
8
8
  import { writeEvent, failConnection, slashError, emitSessionUpdate, emitSessionsList, currentSessionListOptions, setSessionListingDir, } from "./bridge/events.js";
9
- import { textFromPrompt } from "./bridge/message_handlers.js";
9
+ import { contentFromPrompt } from "./bridge/message_handlers.js";
10
10
  import { sessions, sessionById, createSession, closeAllSessions, handleElicitationResponse, handlePermissionResponse, handleQuestionResponse, } from "./bridge/session_lifecycle.js";
11
11
  import { mapSessionMessagesToUpdates } from "./bridge/history.js";
12
12
  import { MCP_STALE_STATUS_REVALIDATION_COOLDOWN_MS, handleMcpAuthenticateCommand, handleMcpClearAuthCommand, handleMcpOauthCallbackUrlCommand, handleMcpReconnectCommand, handleMcpSetServersCommand, handleMcpStatusCommand, handleMcpToggleCommand, staleMcpAuthCandidates, } from "./bridge/mcp.js";
13
+ import { bridgeLogger, LOG_TARGETS, logBridgeCommandReceived } from "./bridge/logger.js";
13
14
  // Re-exports: all symbols that tests and external consumers import from bridge.js.
14
- export { AsyncQueue, logPermissionDebug } from "./bridge/shared.js";
15
+ export { AsyncQueue } from "./bridge/shared.js";
15
16
  export { asRecordOrNull } from "./bridge/shared.js";
16
17
  export { CACHE_SPLIT_POLICY, previewKilobyteLabel } from "./bridge/cache_policy.js";
17
18
  export { buildToolResultFields, createToolCall, normalizeToolKind, normalizeToolResultText, unwrapToolUseResult, } from "./bridge/tooling.js";
@@ -67,14 +68,34 @@ export function agentSdkVersionCompatibilityError() {
67
68
  `found ${installed}.`);
68
69
  }
69
70
  async function handleCommand(command, requestId) {
71
+ logBridgeCommandReceived(command, requestId);
70
72
  const sdkVersionError = agentSdkVersionCompatibilityError();
71
73
  if (sdkVersionError && command.command !== "initialize" && command.command !== "shutdown") {
74
+ bridgeLogger.error({
75
+ target: LOG_TARGETS.BRIDGE_LIFECYCLE,
76
+ eventName: "bridge_command_rejected",
77
+ message: "bridge command rejected due to unsupported SDK version",
78
+ outcome: "failure",
79
+ ...(requestId ? { requestId } : {}),
80
+ fields: {
81
+ bridge_command: command.command,
82
+ error_message: sdkVersionError,
83
+ },
84
+ });
72
85
  failConnection(sdkVersionError, requestId);
73
86
  return;
74
87
  }
75
88
  switch (command.command) {
76
89
  case "initialize":
77
90
  if (sdkVersionError) {
91
+ bridgeLogger.error({
92
+ target: LOG_TARGETS.BRIDGE_LIFECYCLE,
93
+ eventName: "bridge_initialize_failed",
94
+ message: "bridge initialization failed due to unsupported SDK version",
95
+ outcome: "failure",
96
+ ...(requestId ? { requestId } : {}),
97
+ fields: { error_message: sdkVersionError },
98
+ });
78
99
  failConnection(sdkVersionError, requestId);
79
100
  return;
80
101
  }
@@ -92,7 +113,7 @@ async function handleCommand(command, requestId) {
92
113
  },
93
114
  ],
94
115
  capabilities: {
95
- prompt_image: false,
116
+ prompt_image: true,
96
117
  prompt_embedded_context: true,
97
118
  supports_session_listing: true,
98
119
  supports_resume_session: true,
@@ -102,6 +123,17 @@ async function handleCommand(command, requestId) {
102
123
  await emitSessionsList(requestId);
103
124
  return;
104
125
  case "create_session":
126
+ bridgeLogger.info({
127
+ target: LOG_TARGETS.APP_SESSION,
128
+ eventName: "session_create_requested",
129
+ message: "session creation requested",
130
+ outcome: "start",
131
+ ...(requestId ? { requestId } : {}),
132
+ fields: {
133
+ cwd: command.cwd,
134
+ resume_requested: command.resume !== undefined,
135
+ },
136
+ });
105
137
  setSessionListingDir(command.cwd);
106
138
  await createSession({
107
139
  cwd: command.cwd,
@@ -112,10 +144,27 @@ async function handleCommand(command, requestId) {
112
144
  });
113
145
  return;
114
146
  case "resume_session": {
147
+ bridgeLogger.info({
148
+ target: LOG_TARGETS.APP_SESSION,
149
+ eventName: "session_resume_requested",
150
+ message: "session resume requested",
151
+ outcome: "start",
152
+ ...(requestId ? { requestId } : {}),
153
+ sessionId: command.session_id,
154
+ });
115
155
  try {
116
156
  const sdkSessions = await listSessions(currentSessionListOptions());
117
157
  const matched = sdkSessions.find((entry) => entry.sessionId === command.session_id);
118
158
  if (!matched) {
159
+ bridgeLogger.warn({
160
+ target: LOG_TARGETS.APP_SESSION,
161
+ eventName: "session_resume_lookup_failed",
162
+ message: "session resume requested for an unknown session",
163
+ outcome: "failure",
164
+ ...(requestId ? { requestId } : {}),
165
+ sessionId: command.session_id,
166
+ fields: { reason: "unknown_session" },
167
+ });
119
168
  slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
120
169
  return;
121
170
  }
@@ -124,6 +173,18 @@ async function handleCommand(command, requestId) {
124
173
  const resumeUpdates = mapSessionMessagesToUpdates(historyMessages);
125
174
  const staleSessions = Array.from(sessions.values());
126
175
  const hadActiveSession = staleSessions.length > 0;
176
+ bridgeLogger.info({
177
+ target: LOG_TARGETS.APP_SESSION,
178
+ eventName: "session_resume_history_loaded",
179
+ message: "session resume history loaded",
180
+ outcome: "success",
181
+ ...(requestId ? { requestId } : {}),
182
+ sessionId: command.session_id,
183
+ fields: {
184
+ history_update_count: resumeUpdates.length,
185
+ stale_session_count: staleSessions.length,
186
+ },
187
+ });
127
188
  await createSession({
128
189
  cwd: matched.cwd ?? process.cwd(),
129
190
  resume: command.session_id,
@@ -136,12 +197,29 @@ async function handleCommand(command, requestId) {
136
197
  }
137
198
  catch (error) {
138
199
  const message = error instanceof Error ? error.message : String(error);
200
+ bridgeLogger.error({
201
+ target: LOG_TARGETS.APP_SESSION,
202
+ eventName: "session_resume_failed",
203
+ message: "session resume failed",
204
+ outcome: "failure",
205
+ ...(requestId ? { requestId } : {}),
206
+ sessionId: command.session_id,
207
+ fields: { error_message: message },
208
+ });
139
209
  slashError(command.session_id, `failed to resume session: ${message}`, requestId);
140
210
  }
141
211
  return;
142
212
  }
143
213
  case "new_session":
144
- await closeAllSessions();
214
+ bridgeLogger.info({
215
+ target: LOG_TARGETS.APP_SESSION,
216
+ eventName: "session_new_requested",
217
+ message: "replacement session requested",
218
+ outcome: "start",
219
+ ...(requestId ? { requestId } : {}),
220
+ fields: { cwd: command.cwd },
221
+ });
222
+ await closeAllSessions({ reason: "new_session_requested", requestId });
145
223
  setSessionListingDir(command.cwd);
146
224
  await createSession({
147
225
  cwd: command.cwd,
@@ -156,8 +234,8 @@ async function handleCommand(command, requestId) {
156
234
  slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
157
235
  return;
158
236
  }
159
- const text = textFromPrompt(command);
160
- if (!text.trim()) {
237
+ const content = contentFromPrompt(command);
238
+ if (content.length === 0) {
161
239
  return;
162
240
  }
163
241
  session.input.enqueue({
@@ -166,7 +244,7 @@ async function handleCommand(command, requestId) {
166
244
  parent_tool_use_id: null,
167
245
  message: {
168
246
  role: "user",
169
- content: [{ type: "text", text }],
247
+ content,
170
248
  },
171
249
  });
172
250
  return;
@@ -254,18 +332,48 @@ async function handleCommand(command, requestId) {
254
332
  slashError(command.session_id, `unknown session: ${command.session_id}`, requestId);
255
333
  return;
256
334
  }
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);
335
+ try {
336
+ const account = await session.query.accountInfo();
337
+ bridgeLogger.info({
338
+ target: LOG_TARGETS.APP_AUTH,
339
+ eventName: "status_snapshot_emitted",
340
+ message: "status snapshot emitted",
341
+ outcome: "success",
342
+ ...(requestId ? { requestId } : {}),
343
+ sessionId: session.sessionId,
344
+ fields: {
345
+ has_email: typeof account.email === "string" && account.email.trim().length > 0,
346
+ has_organization: account.organization !== undefined,
347
+ subscription_type: account.subscriptionType,
348
+ token_source: account.tokenSource,
349
+ api_key_source: account.apiKeySource,
350
+ },
351
+ });
352
+ writeEvent({
353
+ event: "status_snapshot",
354
+ session_id: session.sessionId,
355
+ account: {
356
+ email: account.email,
357
+ organization: account.organization,
358
+ subscription_type: account.subscriptionType,
359
+ token_source: account.tokenSource,
360
+ api_key_source: account.apiKeySource,
361
+ },
362
+ }, requestId);
363
+ }
364
+ catch (error) {
365
+ const message = error instanceof Error ? error.message : String(error);
366
+ bridgeLogger.warn({
367
+ target: LOG_TARGETS.APP_AUTH,
368
+ eventName: "status_snapshot_failed",
369
+ message: "failed to build status snapshot",
370
+ outcome: "failure",
371
+ ...(requestId ? { requestId } : {}),
372
+ sessionId: session.sessionId,
373
+ fields: { error_message: message },
374
+ });
375
+ throw error;
376
+ }
269
377
  return;
270
378
  }
271
379
  case "mcp_status": {
@@ -341,13 +449,45 @@ async function handleCommand(command, requestId) {
341
449
  handleElicitationResponse(command);
342
450
  return;
343
451
  case "shutdown":
344
- await closeAllSessions();
452
+ bridgeLogger.info({
453
+ target: LOG_TARGETS.BRIDGE_LIFECYCLE,
454
+ eventName: "bridge_shutdown_requested",
455
+ message: "bridge shutdown requested",
456
+ outcome: "start",
457
+ ...(requestId ? { requestId } : {}),
458
+ });
459
+ await closeAllSessions({ reason: "bridge_shutdown_requested", requestId });
460
+ bridgeLogger.info({
461
+ target: LOG_TARGETS.BRIDGE_LIFECYCLE,
462
+ eventName: "bridge_shutdown_completed",
463
+ message: "bridge shutdown completed",
464
+ outcome: "success",
465
+ ...(requestId ? { requestId } : {}),
466
+ });
345
467
  process.exit(0);
346
468
  default:
469
+ bridgeLogger.error({
470
+ target: LOG_TARGETS.BRIDGE_PROTOCOL,
471
+ eventName: "bridge_command_rejected",
472
+ message: "received unsupported bridge command",
473
+ outcome: "failure",
474
+ ...(requestId ? { requestId } : {}),
475
+ fields: {
476
+ bridge_command: command.command ?? "unknown",
477
+ reason: "unsupported_command",
478
+ },
479
+ });
347
480
  failConnection(`unhandled command: ${command.command ?? "unknown"}`, requestId);
348
481
  }
349
482
  }
350
483
  function main() {
484
+ bridgeLogger.info({
485
+ target: LOG_TARGETS.BRIDGE_LIFECYCLE,
486
+ eventName: "bridge_process_started",
487
+ message: "bridge process started",
488
+ outcome: "start",
489
+ fields: { pid: process.pid },
490
+ });
351
491
  const rl = readline.createInterface({
352
492
  input: process.stdin,
353
493
  crlfDelay: Number.POSITIVE_INFINITY,
@@ -363,6 +503,18 @@ function main() {
363
503
  }
364
504
  catch (error) {
365
505
  const message = error instanceof Error ? error.message : String(error);
506
+ bridgeLogger.error({
507
+ target: LOG_TARGETS.BRIDGE_PROTOCOL,
508
+ eventName: "bridge_command_decode_failed",
509
+ message: "failed to decode bridge command envelope",
510
+ outcome: "failure",
511
+ sizeBytes: Buffer.byteLength(line),
512
+ fields: {
513
+ preview: line.slice(0, 240),
514
+ preview_chars: Math.min(line.length, 240),
515
+ error_message: message,
516
+ },
517
+ });
366
518
  failConnection(`invalid command envelope: ${message}`);
367
519
  return;
368
520
  }
@@ -371,12 +523,42 @@ function main() {
371
523
  }
372
524
  catch (error) {
373
525
  const message = error instanceof Error ? error.message : String(error);
526
+ bridgeLogger.error({
527
+ target: LOG_TARGETS.BRIDGE_PROTOCOL,
528
+ eventName: "bridge_command_failed",
529
+ message: "bridge command handler failed",
530
+ outcome: "failure",
531
+ ...(parsed.requestId ? { requestId: parsed.requestId } : {}),
532
+ ...(parsed.command.command === "create_session" || parsed.command.command === "new_session"
533
+ ? {}
534
+ : "session_id" in parsed.command
535
+ ? { sessionId: parsed.command.session_id }
536
+ : {}),
537
+ fields: {
538
+ bridge_command: parsed.command.command,
539
+ error_message: message,
540
+ },
541
+ });
374
542
  failConnection(`bridge command failed (${parsed.command.command}): ${message}`, parsed.requestId);
375
543
  }
376
544
  })();
377
545
  });
378
546
  rl.on("close", () => {
379
- void closeAllSessions().finally(() => process.exit(0));
547
+ bridgeLogger.info({
548
+ target: LOG_TARGETS.BRIDGE_LIFECYCLE,
549
+ eventName: "bridge_input_closed",
550
+ message: "bridge stdin closed",
551
+ outcome: "success",
552
+ });
553
+ void closeAllSessions({ reason: "bridge_stdin_closed" }).finally(() => {
554
+ bridgeLogger.info({
555
+ target: LOG_TARGETS.BRIDGE_LIFECYCLE,
556
+ eventName: "bridge_shutdown_completed",
557
+ message: "bridge shutdown completed after stdin close",
558
+ outcome: "success",
559
+ });
560
+ process.exit(0);
561
+ });
380
562
  });
381
563
  }
382
564
  if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-rust",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Claude Code Rust - native Rust terminal interface for Claude Code",
5
5
  "keywords": [
6
6
  "cli",