@rallycry/conveyor-agent 4.10.2 → 5.0.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.
@@ -664,6 +664,33 @@ function unshallowRepo(workspaceDir) {
664
664
  }
665
665
  }
666
666
 
667
+ // src/utils/logger.ts
668
+ import winston from "winston";
669
+ var { combine, timestamp, printf, colorize } = winston.format;
670
+ var customFormat = printf(({ level, message, timestamp: ts, service, ...meta }) => {
671
+ const svc = service ? `[${service}] ` : "";
672
+ const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : "";
673
+ return `${ts} [${level}]: ${svc}${message}${metaStr}`;
674
+ });
675
+ var logger = winston.createLogger({
676
+ level: process.env.LOG_LEVEL ?? "info",
677
+ format: combine(timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), combine(colorize(), customFormat)),
678
+ transports: [new winston.transports.Console()]
679
+ });
680
+ function createServiceLogger(serviceName) {
681
+ return logger.child({ service: serviceName });
682
+ }
683
+ function errorMeta(error) {
684
+ if (error instanceof Error) {
685
+ return {
686
+ message: error.message,
687
+ stack: error.stack,
688
+ ..."code" in error && typeof error.code === "string" ? { code: error.code } : {}
689
+ };
690
+ }
691
+ return { message: String(error) };
692
+ }
693
+
667
694
  // src/runner/agent-runner.ts
668
695
  import { randomUUID as randomUUID2 } from "crypto";
669
696
  import { execSync as execSync4 } from "child_process";
@@ -2851,6 +2878,74 @@ async function executeSetupConfig(config, runnerConfig, connection, setupLog, ef
2851
2878
  }
2852
2879
  return deferredStartConfig;
2853
2880
  }
2881
+ async function checkoutTaskBranch(runnerConfig, connection, callbacks, setupLog) {
2882
+ const taskBranch = process.env.CONVEYOR_TASK_BRANCH;
2883
+ if (!taskBranch) return true;
2884
+ pushSetupLog(setupLog, `[conveyor] Switching to task branch ${taskBranch}...`);
2885
+ connection.sendEvent({
2886
+ type: "setup_output",
2887
+ stream: "stdout",
2888
+ data: `Switching to task branch ${taskBranch}...
2889
+ `
2890
+ });
2891
+ try {
2892
+ await runSetupCommand(
2893
+ `git fetch origin ${taskBranch} && git checkout ${taskBranch}`,
2894
+ runnerConfig.workspaceDir,
2895
+ (stream, data) => {
2896
+ connection.sendEvent({ type: "setup_output", stream, data });
2897
+ for (const line of data.split("\n").filter(Boolean)) {
2898
+ pushSetupLog(setupLog, `[${stream}] ${line}`);
2899
+ }
2900
+ }
2901
+ );
2902
+ pushSetupLog(setupLog, `[conveyor] Switched to ${taskBranch}`);
2903
+ return true;
2904
+ } catch (error) {
2905
+ const message = `Failed to checkout ${taskBranch}: ${error instanceof Error ? error.message : "unknown error"}`;
2906
+ connection.sendEvent({ type: "setup_error", message });
2907
+ await callbacks.onEvent({ type: "setup_error", message });
2908
+ connection.postChatMessage(`Failed to switch to task branch \`${taskBranch}\`.
2909
+
2910
+ ${message}`);
2911
+ return false;
2912
+ }
2913
+ }
2914
+ async function rebasePullBranch(runnerConfig, connection, callbacks, setupLog) {
2915
+ const pullBranch = process.env.CONVEYOR_PULL_BRANCH;
2916
+ if (!pullBranch) return true;
2917
+ pushSetupLog(setupLog, `[conveyor] Rebasing onto latest ${pullBranch}...`);
2918
+ connection.sendEvent({
2919
+ type: "setup_output",
2920
+ stream: "stdout",
2921
+ data: `Rebasing onto latest ${pullBranch}...
2922
+ `
2923
+ });
2924
+ try {
2925
+ await runSetupCommand(
2926
+ `git fetch origin ${pullBranch} && git rebase origin/${pullBranch}`,
2927
+ runnerConfig.workspaceDir,
2928
+ (stream, data) => {
2929
+ connection.sendEvent({ type: "setup_output", stream, data });
2930
+ for (const line of data.split("\n").filter(Boolean)) {
2931
+ pushSetupLog(setupLog, `[${stream}] ${line}`);
2932
+ }
2933
+ }
2934
+ );
2935
+ pushSetupLog(setupLog, `[conveyor] Rebase complete`);
2936
+ return true;
2937
+ } catch (error) {
2938
+ const message = `Failed to rebase onto ${pullBranch}: ${error instanceof Error ? error.message : "unknown error"}`;
2939
+ connection.sendEvent({ type: "setup_error", message });
2940
+ await callbacks.onEvent({ type: "setup_error", message });
2941
+ connection.postChatMessage(
2942
+ `Failed to rebase onto latest \`${pullBranch}\`. There may be conflicts between the task branch and dev.
2943
+
2944
+ ${message}`
2945
+ );
2946
+ return false;
2947
+ }
2948
+ }
2854
2949
  async function runSetupSafe(runnerConfig, connection, callbacks, setupLog, effectiveAgentMode, setState) {
2855
2950
  await setState("setup");
2856
2951
  const ports = await loadForwardPorts(runnerConfig.workspaceDir);
@@ -2862,18 +2957,24 @@ async function runSetupSafe(runnerConfig, connection, callbacks, setupLog, effec
2862
2957
  () => void 0
2863
2958
  );
2864
2959
  }
2865
- const pullBranch = process.env.CONVEYOR_PULL_BRANCH;
2866
- if (pullBranch) {
2867
- pushSetupLog(setupLog, `[conveyor] Merging latest from ${pullBranch}...`);
2960
+ if (!await checkoutTaskBranch(runnerConfig, connection, callbacks, setupLog)) {
2961
+ return { ok: false, deferredStartConfig: null };
2962
+ }
2963
+ if (!await rebasePullBranch(runnerConfig, connection, callbacks, setupLog)) {
2964
+ return { ok: false, deferredStartConfig: null };
2965
+ }
2966
+ const taskBranch = process.env.CONVEYOR_TASK_BRANCH;
2967
+ if (taskBranch) {
2968
+ pushSetupLog(setupLog, `[conveyor] Switching to task branch ${taskBranch}...`);
2868
2969
  connection.sendEvent({
2869
2970
  type: "setup_output",
2870
2971
  stream: "stdout",
2871
- data: `Merging latest from ${pullBranch}...
2972
+ data: `Switching to task branch ${taskBranch}...
2872
2973
  `
2873
2974
  });
2874
2975
  try {
2875
2976
  await runSetupCommand(
2876
- `git fetch origin ${pullBranch} && git merge origin/${pullBranch} --no-edit`,
2977
+ `git fetch origin ${taskBranch} && git checkout ${taskBranch}`,
2877
2978
  runnerConfig.workspaceDir,
2878
2979
  (stream, data) => {
2879
2980
  connection.sendEvent({ type: "setup_output", stream, data });
@@ -2882,13 +2983,13 @@ async function runSetupSafe(runnerConfig, connection, callbacks, setupLog, effec
2882
2983
  }
2883
2984
  }
2884
2985
  );
2885
- pushSetupLog(setupLog, `[conveyor] Merge complete`);
2986
+ pushSetupLog(setupLog, `[conveyor] Switched to ${taskBranch}`);
2886
2987
  } catch (error) {
2887
- const message = `Failed to merge ${pullBranch}: ${error instanceof Error ? error.message : "unknown error"}`;
2988
+ const message = `Failed to checkout ${taskBranch}: ${error instanceof Error ? error.message : "unknown error"}`;
2888
2989
  connection.sendEvent({ type: "setup_error", message });
2889
2990
  await callbacks.onEvent({ type: "setup_error", message });
2890
2991
  connection.postChatMessage(
2891
- `Failed to merge latest code from \`${pullBranch}\`. There may be conflicts between the prebuild branch and dev.
2992
+ `Failed to switch to task branch \`${taskBranch}\`.
2892
2993
 
2893
2994
  ${message}`
2894
2995
  );
@@ -3064,6 +3165,7 @@ function buildQueryHost(deps) {
3064
3165
  }
3065
3166
 
3066
3167
  // src/runner/agent-runner.ts
3168
+ var logger2 = createServiceLogger("AgentRunner");
3067
3169
  var HEARTBEAT_INTERVAL_MS = 3e4;
3068
3170
  var IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
3069
3171
  var AgentRunner = class {
@@ -3245,8 +3347,10 @@ var AgentRunner = class {
3245
3347
  "Auto-nudge: Task is still In Progress with no PR. Prompting agent to continue..."
3246
3348
  );
3247
3349
  await this.setState("running");
3350
+ const ctx = this.taskContext ?? fresh;
3351
+ if (!ctx) return false;
3248
3352
  await this.runQuerySafe(
3249
- this.taskContext,
3353
+ ctx,
3250
3354
  "You are in Auto mode and your task is still In Progress with no pull request. You MUST create a pull request before finishing. Use the create_pull_request tool now to open a PR for your changes. If you are blocked, explain what is preventing you from creating a PR."
3251
3355
  );
3252
3356
  return true;
@@ -3336,7 +3440,12 @@ var AgentRunner = class {
3336
3440
  handleRunStartCommand() {
3337
3441
  if (!this.deferredStartConfig?.startCommand || this.startCommandStarted) return;
3338
3442
  this.startCommandStarted = true;
3339
- runDeferredStartCommand(this.deferredStartConfig, this.config, this.connection, this.setupLog);
3443
+ runDeferredStartCommand(
3444
+ this.deferredStartConfig,
3445
+ this.config,
3446
+ this.connection,
3447
+ this.setupLog
3448
+ );
3340
3449
  this.deferredStartConfig = null;
3341
3450
  }
3342
3451
  logEffectiveSettings() {
@@ -3344,18 +3453,17 @@ var AgentRunner = class {
3344
3453
  const s = this.taskContext.agentSettings ?? this.config.agentSettings ?? {};
3345
3454
  const model = this.taskContext.model || this.config.model;
3346
3455
  const thinking = formatThinkingSetting(s.thinking);
3347
- const parts = [
3348
- `model=${model}`,
3349
- `mode=${this.config.mode ?? "task"}`,
3350
- `effort=${s.effort ?? "default"}`,
3351
- `thinking=${thinking}`,
3352
- `maxBudget=$${s.maxBudgetUsd ?? 50}`,
3353
- `maxTurns=${s.maxTurns ?? "unlimited"}`
3354
- ];
3355
- if (s.betas?.length) parts.push(`betas=${s.betas.join(",")}`);
3356
- if (s.disallowedTools?.length) parts.push(`disallowed=[${s.disallowedTools.join(",")}]`);
3357
- if (s.enableFileCheckpointing) parts.push(`checkpointing=on`);
3358
- console.log(`[conveyor-agent] ${parts.join(", ")}`);
3456
+ logger2.info("Effective agent settings", {
3457
+ model,
3458
+ mode: this.config.mode ?? "task",
3459
+ effort: s.effort ?? "default",
3460
+ thinking,
3461
+ maxBudget: s.maxBudgetUsd ?? 50,
3462
+ maxTurns: s.maxTurns ?? "unlimited",
3463
+ ...s.betas?.length ? { betas: s.betas } : {},
3464
+ ...s.disallowedTools?.length ? { disallowedTools: s.disallowedTools } : {},
3465
+ ...s.enableFileCheckpointing ? { checkpointing: "on" } : {}
3466
+ });
3359
3467
  }
3360
3468
  injectHumanMessage(content, files) {
3361
3469
  const messageContent = buildMessageContent(content, files);
@@ -3386,9 +3494,9 @@ var AgentRunner = class {
3386
3494
  this.idleTimer = setTimeout(() => {
3387
3495
  this.clearIdleTimers();
3388
3496
  this.inputResolver = null;
3389
- console.log(
3390
- `[conveyor-agent] Idle for ${IDLE_TIMEOUT_MS / 6e4} minutes \u2014 shutting down.`
3391
- );
3497
+ logger2.info("Idle timeout reached, shutting down", {
3498
+ idleMinutes: IDLE_TIMEOUT_MS / 6e4
3499
+ });
3392
3500
  this.connection.postChatMessage(
3393
3501
  `Agent idle for ${IDLE_TIMEOUT_MS / 6e4} minutes with no new messages \u2014 shutting down.`
3394
3502
  );
@@ -3509,6 +3617,7 @@ import { fileURLToPath } from "url";
3509
3617
 
3510
3618
  // src/runner/project-chat-handler.ts
3511
3619
  import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
3620
+ var logger3 = createServiceLogger("ProjectChat");
3512
3621
  var FALLBACK_MODEL = "claude-sonnet-4-20250514";
3513
3622
  function buildSystemPrompt2(projectDir, agentCtx) {
3514
3623
  const parts = [];
@@ -3561,7 +3670,7 @@ function processContentBlock(block, responseParts, turnToolCalls) {
3561
3670
  input: inputStr.slice(0, 1e4),
3562
3671
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3563
3672
  });
3564
- console.log(`[project-chat] [tool_use] ${block.name}`);
3673
+ logger3.debug("Tool use", { tool: block.name });
3565
3674
  }
3566
3675
  }
3567
3676
  async function fetchContext(connection) {
@@ -3569,13 +3678,13 @@ async function fetchContext(connection) {
3569
3678
  try {
3570
3679
  agentCtx = await connection.fetchAgentContext();
3571
3680
  } catch {
3572
- console.log("[project-chat] Could not fetch agent context, using defaults");
3681
+ logger3.warn("Could not fetch agent context, using defaults");
3573
3682
  }
3574
3683
  let chatHistory = [];
3575
3684
  try {
3576
3685
  chatHistory = await connection.fetchChatHistory(30);
3577
3686
  } catch {
3578
- console.log("[project-chat] Could not fetch chat history, proceeding without it");
3687
+ logger3.warn("Could not fetch chat history, proceeding without it");
3579
3688
  }
3580
3689
  return { agentCtx, chatHistory };
3581
3690
  }
@@ -3649,10 +3758,7 @@ async function handleProjectChatMessage(message, connection, projectDir) {
3649
3758
  try {
3650
3759
  await runChatQuery(message, connection, projectDir);
3651
3760
  } catch (error) {
3652
- console.error(
3653
- "[project-chat] Failed to handle message:",
3654
- error instanceof Error ? error.message : error
3655
- );
3761
+ logger3.error("Failed to handle message", errorMeta(error));
3656
3762
  try {
3657
3763
  await connection.emitChatMessage(
3658
3764
  "I encountered an error processing your message. Please try again."
@@ -3665,6 +3771,7 @@ async function handleProjectChatMessage(message, connection, projectDir) {
3665
3771
  }
3666
3772
 
3667
3773
  // src/runner/project-runner.ts
3774
+ var logger4 = createServiceLogger("ProjectRunner");
3668
3775
  var __filename = fileURLToPath(import.meta.url);
3669
3776
  var __dirname = path.dirname(__filename);
3670
3777
  var HEARTBEAT_INTERVAL_MS2 = 3e4;
@@ -3687,7 +3794,7 @@ function setupWorkDir(projectDir, assignment) {
3687
3794
  try {
3688
3795
  execSync5(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
3689
3796
  } catch {
3690
- console.log(`[task:${shortId}] Warning: could not checkout branch ${branch}`);
3797
+ logger4.warn("Could not checkout branch", { taskId: shortId, branch });
3691
3798
  }
3692
3799
  }
3693
3800
  }
@@ -3725,13 +3832,13 @@ function spawnChildAgent(assignment, workDir) {
3725
3832
  child.stdout?.on("data", (data) => {
3726
3833
  const lines = data.toString().trimEnd().split("\n");
3727
3834
  for (const line of lines) {
3728
- console.log(`[task:${shortId}] ${line}`);
3835
+ logger4.info(line, { taskId: shortId });
3729
3836
  }
3730
3837
  });
3731
3838
  child.stderr?.on("data", (data) => {
3732
3839
  const lines = data.toString().trimEnd().split("\n");
3733
3840
  for (const line of lines) {
3734
- console.error(`[task:${shortId}] ${line}`);
3841
+ logger4.error(line, { taskId: shortId });
3735
3842
  }
3736
3843
  });
3737
3844
  return child;
@@ -3760,17 +3867,17 @@ var ProjectRunner = class {
3760
3867
  this.handleStopTask(data.taskId);
3761
3868
  });
3762
3869
  this.connection.onShutdown(() => {
3763
- console.log("[project-runner] Received shutdown signal from server");
3870
+ logger4.info("Received shutdown signal from server");
3764
3871
  void this.stop();
3765
3872
  });
3766
3873
  this.connection.onChatMessage((msg) => {
3767
- console.log("[project-runner] Received project chat message");
3874
+ logger4.debug("Received project chat message");
3768
3875
  void handleProjectChatMessage(msg, this.connection, this.projectDir);
3769
3876
  });
3770
3877
  this.heartbeatTimer = setInterval(() => {
3771
3878
  this.connection.sendHeartbeat();
3772
3879
  }, HEARTBEAT_INTERVAL_MS2);
3773
- console.log("[project-runner] Connected, waiting for task assignments...");
3880
+ logger4.info("Connected, waiting for task assignments");
3774
3881
  await new Promise((resolve2) => {
3775
3882
  this.resolveLifecycle = resolve2;
3776
3883
  process.on("SIGTERM", () => void this.stop());
@@ -3781,13 +3888,14 @@ var ProjectRunner = class {
3781
3888
  const { taskId, mode } = assignment;
3782
3889
  const shortId = taskId.slice(0, 8);
3783
3890
  if (this.activeAgents.has(taskId)) {
3784
- console.log(`[project-runner] Task ${shortId} already running, skipping`);
3891
+ logger4.info("Task already running, skipping", { taskId: shortId });
3785
3892
  return;
3786
3893
  }
3787
3894
  if (this.activeAgents.size >= MAX_CONCURRENT) {
3788
- console.log(
3789
- `[project-runner] Max concurrent agents (${MAX_CONCURRENT}) reached, rejecting task ${shortId}`
3790
- );
3895
+ logger4.warn("Max concurrent agents reached, rejecting task", {
3896
+ maxConcurrent: MAX_CONCURRENT,
3897
+ taskId: shortId
3898
+ });
3791
3899
  this.connection.emitTaskStopped(taskId, "max_concurrent_reached");
3792
3900
  return;
3793
3901
  }
@@ -3795,7 +3903,7 @@ var ProjectRunner = class {
3795
3903
  try {
3796
3904
  execSync5("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
3797
3905
  } catch {
3798
- console.log(`[task:${shortId}] Warning: git fetch failed`);
3906
+ logger4.warn("Git fetch failed", { taskId: shortId });
3799
3907
  }
3800
3908
  const { workDir, usesWorktree } = setupWorkDir(this.projectDir, assignment);
3801
3909
  const child = spawnChildAgent(assignment, workDir);
@@ -3806,12 +3914,12 @@ var ProjectRunner = class {
3806
3914
  usesWorktree
3807
3915
  });
3808
3916
  this.connection.emitTaskStarted(taskId);
3809
- console.log(`[project-runner] Started task ${shortId} in ${mode} mode at ${workDir}`);
3917
+ logger4.info("Started task", { taskId: shortId, mode, workDir });
3810
3918
  child.on("exit", (code) => {
3811
3919
  this.activeAgents.delete(taskId);
3812
3920
  const reason = code === 0 ? "completed" : `exited with code ${code}`;
3813
3921
  this.connection.emitTaskStopped(taskId, reason);
3814
- console.log(`[project-runner] Task ${shortId} ${reason}`);
3922
+ logger4.info("Task exited", { taskId: shortId, reason });
3815
3923
  if (code === 0 && usesWorktree) {
3816
3924
  try {
3817
3925
  removeWorktree(this.projectDir, taskId);
@@ -3820,10 +3928,10 @@ var ProjectRunner = class {
3820
3928
  }
3821
3929
  });
3822
3930
  } catch (error) {
3823
- console.error(
3824
- `[project-runner] Failed to start task ${shortId}:`,
3825
- error instanceof Error ? error.message : error
3826
- );
3931
+ logger4.error("Failed to start task", {
3932
+ taskId: shortId,
3933
+ ...errorMeta(error)
3934
+ });
3827
3935
  this.connection.emitTaskStopped(
3828
3936
  taskId,
3829
3937
  `start_failed: ${error instanceof Error ? error.message : "Unknown"}`
@@ -3834,7 +3942,7 @@ var ProjectRunner = class {
3834
3942
  const agent = this.activeAgents.get(taskId);
3835
3943
  if (!agent) return;
3836
3944
  const shortId = taskId.slice(0, 8);
3837
- console.log(`[project-runner] Stopping task ${shortId}`);
3945
+ logger4.info("Stopping task", { taskId: shortId });
3838
3946
  agent.process.kill("SIGTERM");
3839
3947
  const timer = setTimeout(() => {
3840
3948
  if (this.activeAgents.has(taskId)) {
@@ -3854,7 +3962,7 @@ var ProjectRunner = class {
3854
3962
  async stop() {
3855
3963
  if (this.stopping) return;
3856
3964
  this.stopping = true;
3857
- console.log("[project-runner] Shutting down...");
3965
+ logger4.info("Shutting down");
3858
3966
  if (this.heartbeatTimer) {
3859
3967
  clearInterval(this.heartbeatTimer);
3860
3968
  this.heartbeatTimer = null;
@@ -3879,7 +3987,7 @@ var ProjectRunner = class {
3879
3987
  })
3880
3988
  ]);
3881
3989
  this.connection.disconnect();
3882
- console.log("[project-runner] Shutdown complete");
3990
+ logger4.info("Shutdown complete");
3883
3991
  if (this.resolveLifecycle) {
3884
3992
  this.resolveLifecycle();
3885
3993
  this.resolveLifecycle = null;
@@ -3959,8 +4067,10 @@ export {
3959
4067
  loadConveyorConfig,
3960
4068
  runSetupCommand,
3961
4069
  runStartCommand,
4070
+ createServiceLogger,
4071
+ errorMeta,
3962
4072
  AgentRunner,
3963
4073
  ProjectRunner,
3964
4074
  FileCache
3965
4075
  };
3966
- //# sourceMappingURL=chunk-VJRAWAWQ.js.map
4076
+ //# sourceMappingURL=chunk-DODEEME6.js.map