agent-worker 0.13.0 → 0.14.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.
@@ -354,6 +354,12 @@ const DEFAULT_IDLE_TIMEOUT = 6e5;
354
354
  * producing output (tool calls, analysis, etc.).
355
355
  */
356
356
  /**
357
+ * Default startup timeout (30 seconds).
358
+ * If the process produces zero output within this window, it's killed.
359
+ * This catches unresponsive backends (e.g., nested `claude -p` inside Claude Code).
360
+ */
361
+ const DEFAULT_STARTUP_TIMEOUT = 3e4;
362
+ /**
357
363
  * Execute a command with idle timeout.
358
364
  *
359
365
  * The timeout resets every time the process writes to stdout or stderr.
@@ -364,7 +370,10 @@ const MIN_TIMEOUT_MS = 1e3;
364
370
  async function execWithIdleTimeout(options) {
365
371
  const { command, args, cwd, onStdout } = options;
366
372
  const timeout = Math.max(options.timeout, MIN_TIMEOUT_MS);
373
+ const rawStartup = options.startupTimeout !== void 0 ? options.startupTimeout : DEFAULT_STARTUP_TIMEOUT;
374
+ const startupTimeout = rawStartup > 0 ? Math.min(rawStartup, timeout) : 0;
367
375
  let idleTimedOut = false;
376
+ let hasReceivedOutput = false;
368
377
  let timer;
369
378
  let stdout = "";
370
379
  let stderr = "";
@@ -383,6 +392,7 @@ async function execWithIdleTimeout(options) {
383
392
  subprocess.stdout?.on("data", (chunk) => {
384
393
  const text = chunk.toString();
385
394
  stdout += text;
395
+ hasReceivedOutput = true;
386
396
  resetTimer();
387
397
  if (onStdout) try {
388
398
  onStdout(text);
@@ -392,9 +402,16 @@ async function execWithIdleTimeout(options) {
392
402
  });
393
403
  subprocess.stderr?.on("data", (chunk) => {
394
404
  stderr += chunk.toString();
405
+ hasReceivedOutput = true;
395
406
  resetTimer();
396
407
  });
397
- resetTimer();
408
+ if (startupTimeout > 0) timer = setTimeout(() => {
409
+ if (!hasReceivedOutput) {
410
+ idleTimedOut = true;
411
+ subprocess.kill();
412
+ }
413
+ }, startupTimeout);
414
+ else resetTimer();
398
415
  try {
399
416
  await subprocess;
400
417
  clearTimeout(timer);
@@ -404,7 +421,7 @@ async function execWithIdleTimeout(options) {
404
421
  };
405
422
  } catch (error) {
406
423
  clearTimeout(timer);
407
- if (idleTimedOut) throw new IdleTimeoutError(timeout, stdout, stderr);
424
+ if (idleTimedOut) throw new IdleTimeoutError(hasReceivedOutput ? timeout : startupTimeout, stdout, stderr);
408
425
  throw error;
409
426
  }
410
427
  }
@@ -415,7 +432,10 @@ async function execWithIdleTimeout(options) {
415
432
  function execWithIdleTimeoutAbortable(options) {
416
433
  const { command, args, cwd, onStdout } = options;
417
434
  const timeout = Math.max(options.timeout, MIN_TIMEOUT_MS);
435
+ const rawStartup = options.startupTimeout !== void 0 ? options.startupTimeout : DEFAULT_STARTUP_TIMEOUT;
436
+ const startupTimeout = rawStartup > 0 ? Math.min(rawStartup, timeout) : 0;
418
437
  let idleTimedOut = false;
438
+ let hasReceivedOutput = false;
419
439
  let timer;
420
440
  let stdout = "";
421
441
  let stderr = "";
@@ -435,6 +455,7 @@ function execWithIdleTimeoutAbortable(options) {
435
455
  subprocess.stdout?.on("data", (chunk) => {
436
456
  const text = chunk.toString();
437
457
  stdout += text;
458
+ hasReceivedOutput = true;
438
459
  resetTimer();
439
460
  if (onStdout) try {
440
461
  onStdout(text);
@@ -444,9 +465,16 @@ function execWithIdleTimeoutAbortable(options) {
444
465
  });
445
466
  subprocess.stderr?.on("data", (chunk) => {
446
467
  stderr += chunk.toString();
468
+ hasReceivedOutput = true;
447
469
  resetTimer();
448
470
  });
449
- resetTimer();
471
+ if (startupTimeout > 0) timer = setTimeout(() => {
472
+ if (!hasReceivedOutput) {
473
+ idleTimedOut = true;
474
+ subprocess.kill();
475
+ }
476
+ }, startupTimeout);
477
+ else resetTimer();
450
478
  const abort = () => {
451
479
  if (!isAborted) {
452
480
  isAborted = true;
@@ -469,7 +497,7 @@ function execWithIdleTimeoutAbortable(options) {
469
497
  } catch (error) {
470
498
  clearTimeout(timer);
471
499
  if (isAborted) throw new Error("Process aborted by user");
472
- if (idleTimedOut) throw new IdleTimeoutError(timeout, stdout, stderr);
500
+ if (idleTimedOut) throw new IdleTimeoutError(hasReceivedOutput ? timeout : startupTimeout, stdout, stderr);
473
501
  throw error;
474
502
  }
475
503
  })(),
@@ -813,7 +841,10 @@ var ClaudeCodeBackend = class {
813
841
  return { content: stdout.trim() };
814
842
  } catch (error) {
815
843
  this.currentAbort = void 0;
816
- if (error instanceof IdleTimeoutError) throw new Error(`claude timed out after ${timeout}ms of inactivity`);
844
+ if (error instanceof IdleTimeoutError) {
845
+ if (error.stdout === "" && error.stderr === "") throw new Error(`claude produced no output within ${error.timeout}ms. This often happens when running nested 'claude -p' inside an existing Claude Code session. Consider using the SDK backend (model: "anthropic/claude-sonnet-4-5") instead.`);
846
+ throw new Error(`claude timed out after ${timeout}ms of inactivity`);
847
+ }
817
848
  if (error && typeof error === "object" && "exitCode" in error) {
818
849
  const execError = error;
819
850
  throw new Error(`claude failed (exit ${execError.exitCode}): ${execError.stderr || execError.shortMessage}`);
@@ -975,8 +1006,13 @@ var CodexBackend = class {
975
1006
  var CursorBackend = class {
976
1007
  type = "cursor";
977
1008
  options;
978
- /** Resolved command: "cursor" (subcommand style) */
979
- resolvedCommand = null;
1009
+ /**
1010
+ * Resolved command style:
1011
+ * - "subcommand": `cursor agent -p ...` (IDE-bundled CLI)
1012
+ * - "direct": `agent -p ...` (standalone install via cursor.com/install)
1013
+ * - null: not yet resolved
1014
+ */
1015
+ resolvedStyle = null;
980
1016
  constructor(options = {}) {
981
1017
  this.options = {
982
1018
  timeout: DEFAULT_IDLE_TIMEOUT,
@@ -1016,7 +1052,7 @@ var CursorBackend = class {
1016
1052
  }
1017
1053
  }
1018
1054
  async isAvailable() {
1019
- return await this.resolveCommand() !== null;
1055
+ return await this.resolveStyle() !== null;
1020
1056
  }
1021
1057
  getInfo() {
1022
1058
  return {
@@ -1025,24 +1061,34 @@ var CursorBackend = class {
1025
1061
  };
1026
1062
  }
1027
1063
  /**
1028
- * Resolve which cursor command is available.
1029
- * Checks `cursor agent --version` (subcommand style).
1064
+ * Resolve which cursor command style is available.
1065
+ * Tries in order:
1066
+ * 1. `cursor agent --version` — IDE-bundled CLI (subcommand style)
1067
+ * 2. `agent --version` — standalone install via cursor.com/install (direct style)
1030
1068
  * Result is cached after first resolution.
1031
1069
  */
1032
- async resolveCommand() {
1033
- if (this.resolvedCommand !== null) return this.resolvedCommand;
1070
+ async resolveStyle() {
1071
+ if (this.resolvedStyle !== null) return this.resolvedStyle;
1034
1072
  try {
1035
1073
  await execa("cursor", ["agent", "--version"], {
1036
1074
  stdin: "ignore",
1037
1075
  timeout: 2e3
1038
1076
  });
1039
- this.resolvedCommand = "cursor";
1040
- return "cursor";
1077
+ this.resolvedStyle = "subcommand";
1078
+ return "subcommand";
1079
+ } catch {}
1080
+ try {
1081
+ await execa("agent", ["--version"], {
1082
+ stdin: "ignore",
1083
+ timeout: 2e3
1084
+ });
1085
+ this.resolvedStyle = "direct";
1086
+ return "direct";
1041
1087
  } catch {}
1042
1088
  return null;
1043
1089
  }
1044
1090
  async buildCommand(message) {
1045
- const cmd = await this.resolveCommand();
1091
+ const style = await this.resolveStyle();
1046
1092
  const agentArgs = [
1047
1093
  "-p",
1048
1094
  "--force",
@@ -1051,7 +1097,11 @@ var CursorBackend = class {
1051
1097
  message
1052
1098
  ];
1053
1099
  if (this.options.model) agentArgs.push("--model", this.options.model);
1054
- if (!cmd) throw new Error("cursor agent CLI not found");
1100
+ if (!style) throw new Error("cursor agent CLI not found. Install via: curl -fsS https://cursor.com/install | bash");
1101
+ if (style === "direct") return {
1102
+ command: "agent",
1103
+ args: agentArgs
1104
+ };
1055
1105
  return {
1056
1106
  command: "cursor",
1057
1107
  args: ["agent", ...agentArgs]
@@ -1,3 +1,3 @@
1
- import { C as CLAUDE_MODEL_MAP, D as SDK_MODEL_ALIASES, E as OPENCODE_MODEL_MAP, O as getModelForBackend, S as BACKEND_DEFAULT_MODELS, T as CURSOR_MODEL_MAP, _ as extractCodexResult, a as createMockBackend, b as execWithIdleTimeout, c as extractOpenCodeResult, d as CodexBackend, f as ClaudeCodeBackend, g as extractClaudeResult, h as createStreamParser, i as MockAIBackend, k as normalizeBackendType, l as opencodeAdapter, m as codexAdapter, n as createBackend, o as SdkBackend, p as claudeAdapter, r as listBackends, s as OpenCodeBackend, t as checkBackends, u as CursorBackend, v as formatEvent, w as CODEX_MODEL_MAP, x as DEFAULT_IDLE_TIMEOUT, y as IdleTimeoutError } from "./backends-CziIqKRg.mjs";
1
+ import { C as CLAUDE_MODEL_MAP, D as SDK_MODEL_ALIASES, E as OPENCODE_MODEL_MAP, O as getModelForBackend, S as BACKEND_DEFAULT_MODELS, T as CURSOR_MODEL_MAP, _ as extractCodexResult, a as createMockBackend, b as execWithIdleTimeout, c as extractOpenCodeResult, d as CodexBackend, f as ClaudeCodeBackend, g as extractClaudeResult, h as createStreamParser, i as MockAIBackend, k as normalizeBackendType, l as opencodeAdapter, m as codexAdapter, n as createBackend, o as SdkBackend, p as claudeAdapter, r as listBackends, s as OpenCodeBackend, t as checkBackends, u as CursorBackend, v as formatEvent, w as CODEX_MODEL_MAP, x as DEFAULT_IDLE_TIMEOUT, y as IdleTimeoutError } from "./backends-C6WBIn9H.mjs";
2
2
 
3
3
  export { listBackends };