@anthropic-ai/claude-code 1.0.84 → 1.0.86

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.
package/sdk.mjs CHANGED
@@ -2,17 +2,14 @@
2
2
 
3
3
  // (c) Anthropic PBC. All rights reserved. Use is subject to Anthropic's Commercial Terms of Service (https://www.anthropic.com/legal/commercial-terms).
4
4
 
5
- // Version: 1.0.84
5
+ // Version: 1.0.86
6
6
 
7
7
  // Want to see the unminified source? We're hiring!
8
8
  // https://job-boards.greenhouse.io/anthropic/jobs/4816199008
9
9
 
10
10
  // src/entrypoints/sdk.ts
11
- import { spawn } from "child_process";
12
- import { join } from "path";
13
- import { fileURLToPath } from "url";
14
- import { createInterface } from "readline";
15
- import { existsSync } from "fs";
11
+ import { join as join2 } from "path";
12
+ import { fileURLToPath as fileURLToPath2 } from "url";
16
13
 
17
14
  // src/utils/stream.ts
18
15
  class Stream {
@@ -97,36 +94,478 @@ function createAbortController(maxListeners = DEFAULT_MAX_LISTENERS) {
97
94
  return controller;
98
95
  }
99
96
 
97
+ // src/transport/ProcessTransport.ts
98
+ import { spawn } from "child_process";
99
+ import { join } from "path";
100
+ import { fileURLToPath } from "url";
101
+ import { createInterface } from "readline";
102
+
103
+ // src/utils/fsOperations.ts
104
+ import * as fs from "fs";
105
+ import { stat as statPromise } from "fs/promises";
106
+ var NodeFsOperations = {
107
+ accessSync(fsPath, mode) {
108
+ fs.accessSync(fsPath, mode);
109
+ },
110
+ cwd() {
111
+ return process.cwd();
112
+ },
113
+ chmodSync(fsPath, mode) {
114
+ fs.chmodSync(fsPath, mode);
115
+ },
116
+ existsSync(fsPath) {
117
+ return fs.existsSync(fsPath);
118
+ },
119
+ async stat(fsPath) {
120
+ return statPromise(fsPath);
121
+ },
122
+ statSync(fsPath) {
123
+ return fs.statSync(fsPath);
124
+ },
125
+ readFileSync(fsPath, options) {
126
+ return fs.readFileSync(fsPath, { encoding: options.encoding });
127
+ },
128
+ readFileBytesSync(fsPath) {
129
+ return fs.readFileSync(fsPath);
130
+ },
131
+ readSync(fsPath, options) {
132
+ let fd = undefined;
133
+ try {
134
+ fd = fs.openSync(fsPath, "r");
135
+ const buffer = Buffer.alloc(options.length);
136
+ const bytesRead = fs.readSync(fd, buffer, 0, options.length, 0);
137
+ return { buffer, bytesRead };
138
+ } finally {
139
+ if (fd)
140
+ fs.closeSync(fd);
141
+ }
142
+ },
143
+ writeFileSync(fsPath, data, options) {
144
+ if (!options.flush) {
145
+ fs.writeFileSync(fsPath, data, { encoding: options.encoding });
146
+ return;
147
+ }
148
+ let fd;
149
+ try {
150
+ fd = fs.openSync(fsPath, "w");
151
+ fs.writeFileSync(fd, data, { encoding: options.encoding });
152
+ fs.fsyncSync(fd);
153
+ } finally {
154
+ if (fd) {
155
+ fs.closeSync(fd);
156
+ }
157
+ }
158
+ },
159
+ appendFileSync(path, data) {
160
+ fs.appendFileSync(path, data);
161
+ },
162
+ copyFileSync(src, dest) {
163
+ fs.copyFileSync(src, dest);
164
+ },
165
+ unlinkSync(path) {
166
+ fs.unlinkSync(path);
167
+ },
168
+ renameSync(oldPath, newPath) {
169
+ fs.renameSync(oldPath, newPath);
170
+ },
171
+ symlinkSync(target, path) {
172
+ fs.symlinkSync(target, path);
173
+ },
174
+ readlinkSync(path) {
175
+ return fs.readlinkSync(path);
176
+ },
177
+ realpathSync(path) {
178
+ return fs.realpathSync(path);
179
+ },
180
+ mkdirSync(dirPath) {
181
+ if (!fs.existsSync(dirPath)) {
182
+ fs.mkdirSync(dirPath, { recursive: true });
183
+ }
184
+ },
185
+ readdirSync(dirPath) {
186
+ return fs.readdirSync(dirPath, { withFileTypes: true });
187
+ },
188
+ readdirStringSync(dirPath) {
189
+ return fs.readdirSync(dirPath);
190
+ },
191
+ isDirEmptySync(dirPath) {
192
+ const files = this.readdirSync(dirPath);
193
+ return files.length === 0;
194
+ },
195
+ rmdirSync(dirPath) {
196
+ fs.rmdirSync(dirPath);
197
+ },
198
+ rmSync(path, options) {
199
+ fs.rmSync(path, options);
200
+ }
201
+ };
202
+ var activeFs = NodeFsOperations;
203
+ function getFsImplementation() {
204
+ return activeFs;
205
+ }
206
+
100
207
  // src/entrypoints/sdkTypes.ts
101
208
  class AbortError extends Error {
102
209
  }
103
210
 
211
+ // src/transport/ProcessTransport.ts
212
+ class ProcessTransport {
213
+ options;
214
+ child;
215
+ childStdin;
216
+ childStdout;
217
+ ready = false;
218
+ abortController;
219
+ exitError;
220
+ exitListeners = [];
221
+ processExitHandler;
222
+ abortHandler;
223
+ isStreaming;
224
+ constructor(options) {
225
+ this.options = options;
226
+ this.abortController = options.abortController || createAbortController();
227
+ this.isStreaming = typeof options.prompt !== "string";
228
+ this.initialize();
229
+ }
230
+ initialize() {
231
+ try {
232
+ const {
233
+ prompt,
234
+ additionalDirectories = [],
235
+ cwd,
236
+ executable = this.isRunningWithBun() ? "bun" : "node",
237
+ executableArgs = [],
238
+ pathToClaudeCodeExecutable,
239
+ env = { ...process.env },
240
+ stderr,
241
+ customSystemPrompt,
242
+ appendSystemPrompt,
243
+ maxTurns,
244
+ model,
245
+ fallbackModel,
246
+ permissionMode,
247
+ permissionPromptToolName,
248
+ continueConversation,
249
+ resume,
250
+ allowedTools = [],
251
+ disallowedTools = [],
252
+ mcpServers,
253
+ strictMcpConfig,
254
+ canUseTool
255
+ } = this.options;
256
+ const args = ["--output-format", "stream-json", "--verbose"];
257
+ if (customSystemPrompt)
258
+ args.push("--system-prompt", customSystemPrompt);
259
+ if (appendSystemPrompt)
260
+ args.push("--append-system-prompt", appendSystemPrompt);
261
+ if (maxTurns)
262
+ args.push("--max-turns", maxTurns.toString());
263
+ if (model)
264
+ args.push("--model", model);
265
+ if (env.DEBUG)
266
+ args.push("--debug-to-stderr");
267
+ if (canUseTool) {
268
+ if (typeof prompt === "string") {
269
+ throw new Error("canUseTool callback requires --input-format stream-json. Please set prompt as an AsyncIterable.");
270
+ }
271
+ if (permissionPromptToolName) {
272
+ throw new Error("canUseTool callback cannot be used with permissionPromptToolName. Please use one or the other.");
273
+ }
274
+ args.push("--permission-prompt-tool", "stdio");
275
+ } else if (permissionPromptToolName) {
276
+ args.push("--permission-prompt-tool", permissionPromptToolName);
277
+ }
278
+ if (continueConversation)
279
+ args.push("--continue");
280
+ if (resume)
281
+ args.push("--resume", resume);
282
+ if (allowedTools.length > 0) {
283
+ args.push("--allowedTools", allowedTools.join(","));
284
+ }
285
+ if (disallowedTools.length > 0) {
286
+ args.push("--disallowedTools", disallowedTools.join(","));
287
+ }
288
+ if (mcpServers && Object.keys(mcpServers).length > 0) {
289
+ args.push("--mcp-config", JSON.stringify({ mcpServers }));
290
+ }
291
+ if (strictMcpConfig) {
292
+ args.push("--strict-mcp-config");
293
+ }
294
+ if (permissionMode && permissionMode !== "default") {
295
+ args.push("--permission-mode", permissionMode);
296
+ }
297
+ if (fallbackModel) {
298
+ if (model && fallbackModel === model) {
299
+ throw new Error("Fallback model cannot be the same as the main model. Please specify a different model for fallbackModel option.");
300
+ }
301
+ args.push("--fallback-model", fallbackModel);
302
+ }
303
+ if (typeof prompt === "string") {
304
+ args.push("--print");
305
+ args.push("--", prompt.trim());
306
+ } else {
307
+ args.push("--input-format", "stream-json");
308
+ }
309
+ for (const dir of additionalDirectories) {
310
+ args.push("--add-dir", dir);
311
+ }
312
+ if (!env.CLAUDE_CODE_ENTRYPOINT) {
313
+ env.CLAUDE_CODE_ENTRYPOINT = "sdk-ts";
314
+ }
315
+ const claudeCodePath = pathToClaudeCodeExecutable || this.getDefaultExecutablePath();
316
+ const fs2 = getFsImplementation();
317
+ if (!fs2.existsSync(claudeCodePath)) {
318
+ throw new Error(`Claude Code executable not found at ${claudeCodePath}. Is options.pathToClaudeCodeExecutable set?`);
319
+ }
320
+ this.logDebug(`Spawning Claude Code process: ${executable} ${[...executableArgs, claudeCodePath, ...args].join(" ")}`);
321
+ const stderrMode = env.DEBUG || stderr ? "pipe" : "ignore";
322
+ this.child = spawn(executable, [...executableArgs, claudeCodePath, ...args], {
323
+ cwd,
324
+ stdio: ["pipe", "pipe", stderrMode],
325
+ signal: this.abortController.signal,
326
+ env
327
+ });
328
+ this.childStdin = this.child.stdin;
329
+ this.childStdout = this.child.stdout;
330
+ if (typeof prompt === "string") {
331
+ this.childStdin.end();
332
+ this.childStdin = undefined;
333
+ }
334
+ if (env.DEBUG || stderr) {
335
+ this.child.stderr.on("data", (data) => {
336
+ this.logDebug(`Claude Code stderr: ${data.toString()}`);
337
+ if (stderr) {
338
+ stderr(data.toString());
339
+ }
340
+ });
341
+ }
342
+ const cleanup = () => {
343
+ if (this.child && !this.child.killed) {
344
+ this.child.kill("SIGTERM");
345
+ }
346
+ };
347
+ this.processExitHandler = cleanup;
348
+ this.abortHandler = cleanup;
349
+ process.on("exit", this.processExitHandler);
350
+ this.abortController.signal.addEventListener("abort", this.abortHandler);
351
+ this.child.on("error", (error) => {
352
+ this.ready = false;
353
+ if (this.abortController.signal.aborted) {
354
+ this.exitError = new AbortError("Claude Code process aborted by user");
355
+ } else {
356
+ this.exitError = new Error(`Failed to spawn Claude Code process: ${error.message}`);
357
+ this.logDebug(this.exitError.message);
358
+ }
359
+ });
360
+ this.child.on("close", (code, signal) => {
361
+ this.ready = false;
362
+ if (this.abortController.signal.aborted) {
363
+ this.exitError = new AbortError("Claude Code process aborted by user");
364
+ } else {
365
+ const error = this.getProcessExitError(code, signal);
366
+ if (error) {
367
+ this.exitError = error;
368
+ this.logDebug(error.message);
369
+ }
370
+ }
371
+ });
372
+ this.ready = true;
373
+ } catch (error) {
374
+ this.ready = false;
375
+ throw error;
376
+ }
377
+ }
378
+ getProcessExitError(code, signal) {
379
+ if (code !== 0 && code !== null) {
380
+ return new Error(`Claude Code process exited with code ${code}`);
381
+ } else if (signal) {
382
+ return new Error(`Claude Code process terminated by signal ${signal}`);
383
+ }
384
+ return;
385
+ }
386
+ getDefaultExecutablePath() {
387
+ const filename = fileURLToPath(import.meta.url);
388
+ const dirname = join(filename, "..", "..");
389
+ return join(dirname, "entrypoints", "cli.js");
390
+ }
391
+ isRunningWithBun() {
392
+ return process.versions.bun !== undefined || process.env.BUN_INSTALL !== undefined;
393
+ }
394
+ logDebug(message) {
395
+ if (process.env.DEBUG) {
396
+ process.stderr.write(`${message}
397
+ `);
398
+ }
399
+ }
400
+ write(data) {
401
+ if (this.abortController.signal.aborted) {
402
+ throw new AbortError("Operation aborted");
403
+ }
404
+ if (!this.ready || !this.childStdin) {
405
+ throw new Error("ProcessTransport is not ready for writing");
406
+ }
407
+ if (this.child?.killed || this.child?.exitCode !== null) {
408
+ throw new Error("Cannot write to terminated process");
409
+ }
410
+ if (this.exitError) {
411
+ throw new Error(`Cannot write to process that exited with error: ${this.exitError.message}`);
412
+ }
413
+ if (process.env.DEBUG_SDK) {
414
+ process.stderr.write(`[ProcessTransport] Writing to stdin: ${data.substring(0, 100)}
415
+ `);
416
+ }
417
+ try {
418
+ const written = this.childStdin.write(data);
419
+ if (!written && process.env.DEBUG_SDK) {
420
+ console.warn("[ProcessTransport] Write buffer full, data queued");
421
+ }
422
+ } catch (error) {
423
+ this.ready = false;
424
+ throw new Error(`Failed to write to process stdin: ${error.message}`);
425
+ }
426
+ }
427
+ close() {
428
+ if (this.childStdin) {
429
+ this.childStdin.end();
430
+ this.childStdin = undefined;
431
+ }
432
+ if (this.processExitHandler) {
433
+ process.off("exit", this.processExitHandler);
434
+ this.processExitHandler = undefined;
435
+ }
436
+ if (this.abortHandler) {
437
+ this.abortController.signal.removeEventListener("abort", this.abortHandler);
438
+ this.abortHandler = undefined;
439
+ }
440
+ for (const { handler } of this.exitListeners) {
441
+ this.child?.off("exit", handler);
442
+ }
443
+ this.exitListeners = [];
444
+ if (this.child && !this.child.killed) {
445
+ this.child.kill("SIGTERM");
446
+ setTimeout(() => {
447
+ if (this.child && !this.child.killed) {
448
+ this.child.kill("SIGKILL");
449
+ }
450
+ }, 5000);
451
+ }
452
+ this.ready = false;
453
+ }
454
+ isReady() {
455
+ return this.ready;
456
+ }
457
+ async* readMessages() {
458
+ if (!this.childStdout) {
459
+ throw new Error("ProcessTransport output stream not available");
460
+ }
461
+ const rl = createInterface({ input: this.childStdout });
462
+ try {
463
+ for await (const line of rl) {
464
+ if (line.trim()) {
465
+ const message = JSON.parse(line);
466
+ yield message;
467
+ }
468
+ }
469
+ await this.waitForExit();
470
+ } catch (error) {
471
+ throw error;
472
+ } finally {
473
+ rl.close();
474
+ }
475
+ }
476
+ endInput() {
477
+ if (this.childStdin) {
478
+ this.childStdin.end();
479
+ }
480
+ }
481
+ getInputStream() {
482
+ return this.childStdin;
483
+ }
484
+ onExit(callback) {
485
+ if (!this.child)
486
+ return () => {};
487
+ const handler = (code, signal) => {
488
+ const error = this.getProcessExitError(code, signal);
489
+ callback(error);
490
+ };
491
+ this.child.on("exit", handler);
492
+ this.exitListeners.push({ callback, handler });
493
+ return () => {
494
+ if (this.child) {
495
+ this.child.off("exit", handler);
496
+ }
497
+ const index = this.exitListeners.findIndex((l) => l.handler === handler);
498
+ if (index !== -1) {
499
+ this.exitListeners.splice(index, 1);
500
+ }
501
+ };
502
+ }
503
+ async waitForExit() {
504
+ if (!this.child) {
505
+ if (this.exitError) {
506
+ throw this.exitError;
507
+ }
508
+ return;
509
+ }
510
+ if (this.child.exitCode !== null || this.child.killed) {
511
+ if (this.exitError) {
512
+ throw this.exitError;
513
+ }
514
+ return;
515
+ }
516
+ return new Promise((resolve, reject) => {
517
+ const exitHandler = (code, signal) => {
518
+ if (this.abortController.signal.aborted) {
519
+ reject(new AbortError("Operation aborted"));
520
+ return;
521
+ }
522
+ const error = this.getProcessExitError(code, signal);
523
+ if (error) {
524
+ reject(error);
525
+ } else {
526
+ resolve();
527
+ }
528
+ };
529
+ this.child.once("exit", exitHandler);
530
+ const errorHandler = (error) => {
531
+ this.child.off("exit", exitHandler);
532
+ reject(error);
533
+ };
534
+ this.child.once("error", errorHandler);
535
+ this.child.once("exit", () => {
536
+ this.child.off("error", errorHandler);
537
+ });
538
+ });
539
+ }
540
+ }
541
+
104
542
  // src/entrypoints/sdk.ts
105
543
  function query({
106
544
  prompt,
107
545
  options: {
108
546
  abortController = createAbortController(),
547
+ additionalDirectories = [],
109
548
  allowedTools = [],
110
549
  appendSystemPrompt,
550
+ canUseTool,
551
+ continue: continueConversation,
111
552
  customSystemPrompt,
112
553
  cwd,
113
554
  disallowedTools = [],
555
+ env,
114
556
  executable = isRunningWithBun() ? "bun" : "node",
115
557
  executableArgs = [],
558
+ fallbackModel,
559
+ hooks,
116
560
  maxTurns,
117
561
  mcpServers,
562
+ model,
118
563
  pathToClaudeCodeExecutable,
119
564
  permissionMode = "default",
120
565
  permissionPromptToolName,
121
- canUseTool,
122
- continue: continueConversation,
123
566
  resume,
124
- model,
125
- fallbackModel,
126
- strictMcpConfig,
127
567
  stderr,
128
- env,
129
- additionalDirectories = []
568
+ strictMcpConfig
130
569
  } = {}
131
570
  }) {
132
571
  if (!env) {
@@ -136,151 +575,87 @@ function query({
136
575
  env.CLAUDE_CODE_ENTRYPOINT = "sdk-ts";
137
576
  }
138
577
  if (pathToClaudeCodeExecutable === undefined) {
139
- const filename = fileURLToPath(import.meta.url);
140
- const dirname = join(filename, "..");
141
- pathToClaudeCodeExecutable = join(dirname, "cli.js");
142
- }
143
- const args = ["--output-format", "stream-json", "--verbose"];
144
- if (customSystemPrompt)
145
- args.push("--system-prompt", customSystemPrompt);
146
- if (appendSystemPrompt)
147
- args.push("--append-system-prompt", appendSystemPrompt);
148
- if (maxTurns)
149
- args.push("--max-turns", maxTurns.toString());
150
- if (model)
151
- args.push("--model", model);
152
- if (canUseTool) {
153
- if (typeof prompt === "string") {
154
- throw new Error("canUseTool callback requires --input-format stream-json. Please set prompt as an AsyncIterable.");
155
- }
156
- if (permissionPromptToolName) {
157
- throw new Error("canUseTool callback cannot be used with permissionPromptToolName. Please use one or the other.");
158
- }
159
- permissionPromptToolName = "stdio";
160
- }
161
- if (permissionPromptToolName) {
162
- args.push("--permission-prompt-tool", permissionPromptToolName);
163
- }
164
- if (continueConversation)
165
- args.push("--continue");
166
- if (resume)
167
- args.push("--resume", resume);
168
- if (allowedTools.length > 0) {
169
- args.push("--allowedTools", allowedTools.join(","));
170
- }
171
- if (disallowedTools.length > 0) {
172
- args.push("--disallowedTools", disallowedTools.join(","));
173
- }
174
- if (mcpServers && Object.keys(mcpServers).length > 0) {
175
- args.push("--mcp-config", JSON.stringify({ mcpServers }));
176
- }
177
- if (strictMcpConfig) {
178
- args.push("--strict-mcp-config");
179
- }
180
- if (permissionMode !== "default") {
181
- args.push("--permission-mode", permissionMode);
182
- }
183
- if (fallbackModel) {
184
- if (model && fallbackModel === model) {
185
- throw new Error("Fallback model cannot be the same as the main model. Please specify a different model for fallbackModel option.");
186
- }
187
- args.push("--fallback-model", fallbackModel);
188
- }
189
- if (typeof prompt === "string") {
190
- args.push("--print");
191
- args.push("--", prompt.trim());
192
- } else {
193
- args.push("--input-format", "stream-json");
194
- }
195
- for (const dir of additionalDirectories) {
196
- args.push("--add-dir", dir);
197
- }
198
- if (!existsSync(pathToClaudeCodeExecutable)) {
199
- throw new ReferenceError(`Claude Code executable not found at ${pathToClaudeCodeExecutable}. Is options.pathToClaudeCodeExecutable set?`);
200
- }
201
- logDebug(`Spawning Claude Code process: ${executable} ${[...executableArgs, pathToClaudeCodeExecutable, ...args].join(" ")}`);
202
- const stderrMode = env.DEBUG || stderr ? "pipe" : "ignore";
203
- const child = spawn(executable, [...executableArgs, pathToClaudeCodeExecutable, ...args], {
578
+ const filename = fileURLToPath2(import.meta.url);
579
+ const dirname = join2(filename, "..");
580
+ pathToClaudeCodeExecutable = join2(dirname, "cli.js");
581
+ }
582
+ const isStreamingMode = typeof prompt !== "string";
583
+ const transport = new ProcessTransport({
584
+ prompt,
585
+ abortController,
586
+ additionalDirectories,
204
587
  cwd,
205
- stdio: ["pipe", "pipe", stderrMode],
206
- signal: abortController.signal,
207
- env
588
+ executable,
589
+ executableArgs,
590
+ pathToClaudeCodeExecutable,
591
+ env,
592
+ stderr,
593
+ customSystemPrompt,
594
+ appendSystemPrompt,
595
+ maxTurns,
596
+ model,
597
+ fallbackModel,
598
+ permissionMode,
599
+ permissionPromptToolName,
600
+ continueConversation,
601
+ resume,
602
+ allowedTools,
603
+ disallowedTools,
604
+ mcpServers,
605
+ strictMcpConfig,
606
+ canUseTool: !!canUseTool,
607
+ hooks: !!hooks
208
608
  });
209
- let childStdin;
210
- if (typeof prompt === "string") {
211
- child.stdin.end();
212
- } else {
213
- streamToStdin(prompt, child.stdin, abortController);
214
- childStdin = child.stdin;
215
- }
216
- if (env.DEBUG || stderr) {
217
- child.stderr.on("data", (data) => {
218
- if (env.DEBUG) {
219
- console.error("Claude Code stderr:", data.toString());
220
- }
221
- if (stderr) {
222
- stderr(data.toString());
223
- }
224
- });
609
+ const query2 = new Query(transport, isStreamingMode, canUseTool, hooks, abortController);
610
+ if (typeof prompt !== "string") {
611
+ query2.streamInput(prompt);
225
612
  }
226
- const cleanup = () => {
227
- if (!child.killed) {
228
- child.kill("SIGTERM");
229
- }
230
- };
231
- abortController.signal.addEventListener("abort", cleanup);
232
- process.on("exit", cleanup);
233
- const processExitPromise = new Promise((resolve) => {
234
- child.on("close", (code) => {
235
- if (abortController.signal.aborted) {
236
- query2.setError(new AbortError("Claude Code process aborted by user"));
237
- }
238
- if (code !== 0) {
239
- query2.setError(new Error(`Claude Code process exited with code ${code}`));
240
- } else {
241
- resolve();
242
- }
243
- });
244
- });
245
- const query2 = new Query(childStdin, child.stdout, processExitPromise, canUseTool);
246
- child.on("error", (error) => {
247
- if (abortController.signal.aborted) {
248
- query2.setError(new AbortError("Claude Code process aborted by user"));
249
- } else {
250
- query2.setError(new Error(`Failed to spawn Claude Code process: ${error.message}`));
251
- }
252
- });
253
- processExitPromise.finally(() => {
254
- cleanup();
255
- abortController.signal.removeEventListener("abort", cleanup);
256
- });
257
613
  return query2;
258
614
  }
259
615
 
260
616
  class Query {
261
- childStdin;
262
- childStdout;
263
- processExitPromise;
617
+ transport;
618
+ isStreamingMode;
264
619
  canUseTool;
620
+ hooks;
621
+ abortController;
265
622
  pendingControlResponses = new Map;
623
+ cleanupPerformed = false;
266
624
  sdkMessages;
267
625
  inputStream = new Stream;
268
626
  intialization;
269
627
  cancelControllers = new Map;
270
- constructor(childStdin, childStdout, processExitPromise, canUseTool) {
271
- this.childStdin = childStdin;
272
- this.childStdout = childStdout;
273
- this.processExitPromise = processExitPromise;
628
+ hookCallbacks = new Map;
629
+ nextCallbackId = 0;
630
+ constructor(transport, isStreamingMode, canUseTool, hooks, abortController) {
631
+ this.transport = transport;
632
+ this.isStreamingMode = isStreamingMode;
274
633
  this.canUseTool = canUseTool;
275
- this.readMessages();
634
+ this.hooks = hooks;
635
+ this.abortController = abortController;
276
636
  this.sdkMessages = this.readSdkMessages();
277
- if (this.childStdin) {
637
+ this.readMessages();
638
+ if (this.isStreamingMode) {
278
639
  this.intialization = this.initialize();
279
640
  }
280
641
  }
281
642
  setError(error) {
282
643
  this.inputStream.error(error);
283
644
  }
645
+ cleanup(error) {
646
+ if (this.cleanupPerformed)
647
+ return;
648
+ this.cleanupPerformed = true;
649
+ try {
650
+ this.transport.close();
651
+ this.pendingControlResponses.clear();
652
+ if (error) {
653
+ this.inputStream.error(error);
654
+ } else {
655
+ this.inputStream.done();
656
+ }
657
+ } catch (_error) {}
658
+ }
284
659
  next(...[value]) {
285
660
  return this.sdkMessages.next(...[value]);
286
661
  }
@@ -297,33 +672,28 @@ class Query {
297
672
  return this.sdkMessages[Symbol.asyncDispose]();
298
673
  }
299
674
  async readMessages() {
300
- const rl = createInterface({ input: this.childStdout });
301
675
  try {
302
- for await (const line of rl) {
303
- if (line.trim()) {
304
- const message = JSON.parse(line);
305
- if (message.type === "control_response") {
306
- const handler = this.pendingControlResponses.get(message.response.request_id);
307
- if (handler) {
308
- handler(message.response);
309
- }
310
- continue;
311
- } else if (message.type === "control_request") {
312
- this.handleControlRequest(message);
313
- continue;
314
- } else if (message.type === "control_cancel_request") {
315
- this.handleControlCancelRequest(message);
316
- continue;
676
+ for await (const message of this.transport.readMessages()) {
677
+ if (message.type === "control_response") {
678
+ const handler = this.pendingControlResponses.get(message.response.request_id);
679
+ if (handler) {
680
+ handler(message.response);
317
681
  }
318
- this.inputStream.enqueue(message);
682
+ continue;
683
+ } else if (message.type === "control_request") {
684
+ this.handleControlRequest(message);
685
+ continue;
686
+ } else if (message.type === "control_cancel_request") {
687
+ this.handleControlCancelRequest(message);
688
+ continue;
319
689
  }
690
+ this.inputStream.enqueue(message);
320
691
  }
321
- await this.processExitPromise;
692
+ this.inputStream.done();
693
+ this.cleanup();
322
694
  } catch (error) {
323
695
  this.inputStream.error(error);
324
- } finally {
325
- this.inputStream.done();
326
- rl.close();
696
+ this.cleanup(error);
327
697
  }
328
698
  }
329
699
  async handleControlRequest(request) {
@@ -339,7 +709,7 @@ class Query {
339
709
  response
340
710
  }
341
711
  };
342
- this.childStdin?.write(JSON.stringify(controlResponse) + `
712
+ this.transport.write(JSON.stringify(controlResponse) + `
343
713
  `);
344
714
  } catch (error) {
345
715
  const controlErrorResponse = {
@@ -350,7 +720,7 @@ class Query {
350
720
  error: error.message || String(error)
351
721
  }
352
722
  };
353
- this.childStdin?.write(JSON.stringify(controlErrorResponse) + `
723
+ this.transport.write(JSON.stringify(controlErrorResponse) + `
354
724
  `);
355
725
  } finally {
356
726
  this.cancelControllers.delete(request.request_id);
@@ -371,6 +741,9 @@ class Query {
371
741
  return this.canUseTool(request.request.tool_name, request.request.input, {
372
742
  signal
373
743
  });
744
+ } else if (request.request.subtype === "hook_callback") {
745
+ const result = await this.handleHookCallbacks(request.request.callback_id, request.request.input, request.request.tool_use_id, signal);
746
+ return result;
374
747
  }
375
748
  throw new Error("Unsupported control request subtype: " + request.request.subtype);
376
749
  }
@@ -380,24 +753,51 @@ class Query {
380
753
  }
381
754
  }
382
755
  async initialize() {
383
- if (!this.childStdin) {
384
- throw new Error("Cannot initialize without child stdin");
756
+ let hooks;
757
+ if (this.hooks) {
758
+ hooks = {};
759
+ for (const [event, matchers] of Object.entries(this.hooks)) {
760
+ if (matchers.length > 0) {
761
+ hooks[event] = matchers.map((matcher) => {
762
+ const callbackIds = [];
763
+ for (const callback of matcher.hooks) {
764
+ const callbackId = `hook_${this.nextCallbackId++}`;
765
+ this.hookCallbacks.set(callbackId, callback);
766
+ callbackIds.push(callbackId);
767
+ }
768
+ return {
769
+ matcher: matcher.matcher,
770
+ hookCallbackIds: callbackIds
771
+ };
772
+ });
773
+ }
774
+ }
385
775
  }
386
776
  const initRequest = {
387
- subtype: "initialize"
777
+ subtype: "initialize",
778
+ hooks
388
779
  };
389
- const response = await this.request(initRequest, this.childStdin);
780
+ const response = await this.request(initRequest);
390
781
  return response.response;
391
782
  }
392
783
  async interrupt() {
393
- if (!this.childStdin) {
784
+ if (!this.isStreamingMode) {
394
785
  throw new Error("Interrupt requires --input-format stream-json");
395
786
  }
396
787
  await this.request({
397
788
  subtype: "interrupt"
398
- }, this.childStdin);
789
+ });
790
+ }
791
+ async setPermissionMode(mode) {
792
+ if (!this.isStreamingMode) {
793
+ throw new Error("setPermissionMode requires --input-format stream-json");
794
+ }
795
+ await this.request({
796
+ subtype: "set_permission_mode",
797
+ mode
798
+ });
399
799
  }
400
- request(request, childStdin) {
800
+ request(request) {
401
801
  const requestId = Math.random().toString(36).substring(2, 15);
402
802
  const sdkRequest = {
403
803
  request_id: requestId,
@@ -412,29 +812,42 @@ class Query {
412
812
  reject(new Error(response.error));
413
813
  }
414
814
  });
415
- childStdin.write(JSON.stringify(sdkRequest) + `
815
+ this.transport.write(JSON.stringify(sdkRequest) + `
416
816
  `);
417
817
  });
418
818
  }
419
819
  async supportedCommands() {
820
+ if (!this.isStreamingMode) {
821
+ throw new Error("supportedCommands requires --input-format stream-json");
822
+ }
420
823
  if (!this.intialization) {
421
- throw new Error("Interrupt requires --input-format stream-json");
824
+ throw new Error("supportedCommands requires transport with bidirectional communication");
422
825
  }
423
826
  return (await this.intialization).commands;
424
827
  }
425
- }
426
- async function streamToStdin(stream, stdin, abortController) {
427
- for await (const message of stream) {
428
- if (abortController.signal.aborted)
429
- break;
430
- stdin.write(JSON.stringify(message) + `
828
+ async streamInput(stream) {
829
+ try {
830
+ for await (const message of stream) {
831
+ if (this.abortController?.signal.aborted)
832
+ break;
833
+ this.transport.write(JSON.stringify(message) + `
431
834
  `);
835
+ }
836
+ this.transport.endInput();
837
+ } catch (error) {
838
+ if (!(error instanceof AbortError)) {
839
+ throw error;
840
+ }
841
+ }
432
842
  }
433
- stdin.end();
434
- }
435
- function logDebug(message) {
436
- if (process.env.DEBUG) {
437
- console.debug(message);
843
+ handleHookCallbacks(callbackId, input, toolUseID, abortSignal) {
844
+ const callback = this.hookCallbacks.get(callbackId);
845
+ if (!callback) {
846
+ throw new Error(`No hook callback found for ID: ${callbackId}`);
847
+ }
848
+ return callback(input, toolUseID, {
849
+ signal: abortSignal
850
+ });
438
851
  }
439
852
  }
440
853
  function isRunningWithBun() {
@@ -442,5 +855,6 @@ function isRunningWithBun() {
442
855
  }
443
856
  export {
444
857
  query,
445
- Query
858
+ Query,
859
+ AbortError
446
860
  };