@rallycry/conveyor-agent 4.5.0 → 4.7.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.
@@ -103,9 +103,11 @@ var ConveyorConnection = class _ConveyorConnection {
103
103
  static EVENT_BATCH_MS = 500;
104
104
  earlyMessages = [];
105
105
  earlyStop = false;
106
+ earlySoftStop = false;
106
107
  earlyModeChanges = [];
107
108
  chatMessageCallback = null;
108
109
  stopCallback = null;
110
+ softStopCallback = null;
109
111
  modeChangeCallback = null;
110
112
  runStartCommandCallback = null;
111
113
  pendingQuestionResolvers = /* @__PURE__ */ new Map();
@@ -135,6 +137,10 @@ var ConveyorConnection = class _ConveyorConnection {
135
137
  if (this.stopCallback) this.stopCallback();
136
138
  else this.earlyStop = true;
137
139
  });
140
+ this.socket.on("agentRunner:softStop", () => {
141
+ if (this.softStopCallback) this.softStopCallback();
142
+ else this.earlySoftStop = true;
143
+ });
138
144
  this.socket.on("agentRunner:questionAnswer", (data) => {
139
145
  const resolver = this.pendingQuestionResolvers.get(data.requestId);
140
146
  if (resolver) {
@@ -233,6 +239,13 @@ var ConveyorConnection = class _ConveyorConnection {
233
239
  this.earlyStop = false;
234
240
  }
235
241
  }
242
+ onSoftStopRequested(callback) {
243
+ this.softStopCallback = callback;
244
+ if (this.earlySoftStop) {
245
+ callback();
246
+ this.earlySoftStop = false;
247
+ }
248
+ }
236
249
  onModeChange(callback) {
237
250
  this.modeChangeCallback = callback;
238
251
  for (const data of this.earlyModeChanges) callback(data);
@@ -485,6 +498,9 @@ import { existsSync } from "fs";
485
498
  import { join } from "path";
486
499
  var WORKTREE_DIR = ".worktrees";
487
500
  function ensureWorktree(projectDir, taskId, branch) {
501
+ if (projectDir.includes(`/${WORKTREE_DIR}/`)) {
502
+ return projectDir;
503
+ }
488
504
  const worktreePath = join(projectDir, WORKTREE_DIR, taskId);
489
505
  if (existsSync(worktreePath)) {
490
506
  if (branch) {
@@ -538,14 +554,6 @@ async function loadConveyorConfig(workspaceDir) {
538
554
  if (parsed.setupCommand || parsed.startCommand) return parsed;
539
555
  } catch {
540
556
  }
541
- try {
542
- const raw = await readFile(join2(workspaceDir, DEVCONTAINER_PATH), "utf-8");
543
- const parsed = JSON.parse(raw);
544
- if (parsed.conveyor && (parsed.conveyor.startCommand || parsed.conveyor.setupCommand)) {
545
- return parsed.conveyor;
546
- }
547
- } catch {
548
- }
549
557
  return null;
550
558
  }
551
559
 
@@ -2070,11 +2078,11 @@ function getModeTools(agentMode, connection, config, context) {
2070
2078
  return config.mode === "pm" ? buildPmTools(connection, context?.storyPoints, { includePackTools: false }) : buildTaskTools(connection);
2071
2079
  }
2072
2080
  }
2073
- function createConveyorMcpServer(connection, config, context) {
2081
+ function createConveyorMcpServer(connection, config, context, agentMode) {
2074
2082
  const commonTools = buildCommonTools(connection, config);
2075
- const agentMode = context?.agentMode ?? void 0;
2076
- const modeTools = getModeTools(agentMode, connection, config, context);
2077
- const discoveryTools = agentMode === "discovery" || agentMode === "auto" ? buildDiscoveryTools(connection, context) : [];
2083
+ const effectiveMode = agentMode ?? context?.agentMode ?? void 0;
2084
+ const modeTools = getModeTools(effectiveMode, connection, config, context);
2085
+ const discoveryTools = effectiveMode === "discovery" || effectiveMode === "auto" ? buildDiscoveryTools(connection, context) : [];
2078
2086
  return createSdkMcpServer({
2079
2087
  name: "conveyor",
2080
2088
  tools: [...commonTools, ...modeTools, ...discoveryTools]
@@ -2317,7 +2325,7 @@ function buildQueryOptions(host, context) {
2317
2325
  allowDangerouslySkipPermissions: !needsCanUseTool,
2318
2326
  canUseTool: buildCanUseTool(host),
2319
2327
  tools: { type: "preset", preset: "claude_code" },
2320
- mcpServers: { conveyor: createConveyorMcpServer(host.connection, host.config, context) },
2328
+ mcpServers: { conveyor: createConveyorMcpServer(host.connection, host.config, context, mode) },
2321
2329
  hooks: buildHooks(host),
2322
2330
  maxTurns: settings.maxTurns,
2323
2331
  effort: settings.effort,
@@ -2406,7 +2414,12 @@ async function runSdkQuery(host, context, followUpContent) {
2406
2414
  prompt: typeof prompt === "string" ? prompt : host.createInputStream(prompt),
2407
2415
  options: { ...options, resume }
2408
2416
  });
2409
- await runWithRetry(agentQuery, context, host, options);
2417
+ host.activeQuery = agentQuery;
2418
+ try {
2419
+ await runWithRetry(agentQuery, context, host, options);
2420
+ } finally {
2421
+ host.activeQuery = null;
2422
+ }
2410
2423
  } else if (isDiscoveryLike) {
2411
2424
  return;
2412
2425
  } else {
@@ -2416,7 +2429,12 @@ async function runSdkQuery(host, context, followUpContent) {
2416
2429
  prompt: host.createInputStream(prompt),
2417
2430
  options: { ...options, resume }
2418
2431
  });
2419
- await runWithRetry(agentQuery, context, host, options);
2432
+ host.activeQuery = agentQuery;
2433
+ try {
2434
+ await runWithRetry(agentQuery, context, host, options);
2435
+ } finally {
2436
+ host.activeQuery = null;
2437
+ }
2420
2438
  }
2421
2439
  if (isDiscoveryLike) {
2422
2440
  host.syncPlanFile();
@@ -2702,6 +2720,39 @@ async function runSetupSafe(runnerConfig, connection, callbacks, setupLog, effec
2702
2720
  () => void 0
2703
2721
  );
2704
2722
  }
2723
+ const pullBranch = process.env.CONVEYOR_PULL_BRANCH;
2724
+ if (pullBranch) {
2725
+ pushSetupLog(setupLog, `[conveyor] Merging latest from ${pullBranch}...`);
2726
+ connection.sendEvent({
2727
+ type: "setup_output",
2728
+ stream: "stdout",
2729
+ data: `Merging latest from ${pullBranch}...
2730
+ `
2731
+ });
2732
+ try {
2733
+ await runSetupCommand(
2734
+ `git fetch origin ${pullBranch} && git merge origin/${pullBranch} --no-edit`,
2735
+ runnerConfig.workspaceDir,
2736
+ (stream, data) => {
2737
+ connection.sendEvent({ type: "setup_output", stream, data });
2738
+ for (const line of data.split("\n").filter(Boolean)) {
2739
+ pushSetupLog(setupLog, `[${stream}] ${line}`);
2740
+ }
2741
+ }
2742
+ );
2743
+ pushSetupLog(setupLog, `[conveyor] Merge complete`);
2744
+ } catch (error) {
2745
+ const message = `Failed to merge ${pullBranch}: ${error instanceof Error ? error.message : "unknown error"}`;
2746
+ connection.sendEvent({ type: "setup_error", message });
2747
+ await callbacks.onEvent({ type: "setup_error", message });
2748
+ connection.postChatMessage(
2749
+ `Failed to merge latest code from \`${pullBranch}\`. There may be conflicts between the prebuild branch and dev.
2750
+
2751
+ ${message}`
2752
+ );
2753
+ return { ok: false, deferredStartConfig: null };
2754
+ }
2755
+ }
2705
2756
  const config = await loadConveyorConfig(runnerConfig.workspaceDir);
2706
2757
  if (!config) {
2707
2758
  connection.sendEvent({ type: "setup_complete" });
@@ -2850,6 +2901,7 @@ function buildQueryHost(deps) {
2850
2901
  deps.setPendingModeRestart(val);
2851
2902
  },
2852
2903
  sessionIds: deps.sessionIds,
2904
+ activeQuery: null,
2853
2905
  isStopped: deps.isStopped,
2854
2906
  createInputStream: deps.createInputStream,
2855
2907
  snapshotPlanFiles: () => deps.planSync.snapshotPlanFiles(),
@@ -2867,6 +2919,7 @@ var AgentRunner = class {
2867
2919
  callbacks;
2868
2920
  _state = "connecting";
2869
2921
  stopped = false;
2922
+ interrupted = false;
2870
2923
  inputResolver = null;
2871
2924
  pendingMessages = [];
2872
2925
  setupLog = [];
@@ -2884,6 +2937,7 @@ var AgentRunner = class {
2884
2937
  idleTimer = null;
2885
2938
  idleCheckInterval = null;
2886
2939
  deferredStartConfig = null;
2940
+ _queryHost = null;
2887
2941
  constructor(config, callbacks) {
2888
2942
  this.config = config;
2889
2943
  this.connection = new ConveyorConnection(config);
@@ -2930,6 +2984,7 @@ var AgentRunner = class {
2930
2984
  await this.setState("connecting");
2931
2985
  await this.connection.connect();
2932
2986
  this.connection.onStopRequested(() => this.stop());
2987
+ this.connection.onSoftStopRequested(() => this.softStop());
2933
2988
  this.connection.onChatMessage(
2934
2989
  (message) => this.injectHumanMessage(message.content, message.files)
2935
2990
  );
@@ -2991,7 +3046,7 @@ var AgentRunner = class {
2991
3046
  return true;
2992
3047
  }
2993
3048
  tryPostContextWorktree() {
2994
- if (process.env.CODESPACES !== "true" && !this.worktreeActive && this.taskContext?.useWorktree) {
3049
+ if (process.env.CODESPACES !== "true" && process.env.CONVEYOR_USE_WORKTREE !== "false" && !this.worktreeActive && this.taskContext?.useWorktree) {
2995
3050
  this.activateWorktree("[conveyor] Using worktree (from task config):");
2996
3051
  }
2997
3052
  }
@@ -3072,9 +3127,17 @@ var AgentRunner = class {
3072
3127
  }
3073
3128
  if (this._state === "idle") {
3074
3129
  const msg = await this.waitForUserContent();
3075
- if (!msg) break;
3130
+ if (!msg) {
3131
+ if (this.interrupted && !this.stopped) {
3132
+ this.interrupted = false;
3133
+ continue;
3134
+ }
3135
+ break;
3136
+ }
3076
3137
  await this.setState("running");
3138
+ this.interrupted = false;
3077
3139
  await this.runQuerySafe(this.taskContext, msg);
3140
+ if (this.interrupted) this.interrupted = false;
3078
3141
  if (!this.stopped && this._state !== "error") await this.setState("idle");
3079
3142
  } else if (this._state === "error") {
3080
3143
  await this.setState("idle");
@@ -3127,7 +3190,7 @@ var AgentRunner = class {
3127
3190
  this.clearIdleTimers();
3128
3191
  return new Promise((resolve2) => {
3129
3192
  this.idleCheckInterval = setInterval(() => {
3130
- if (this.stopped) {
3193
+ if (this.stopped || this.interrupted) {
3131
3194
  this.clearIdleTimers();
3132
3195
  this.inputResolver = null;
3133
3196
  resolve2(null);
@@ -3167,7 +3230,7 @@ var AgentRunner = class {
3167
3230
  });
3168
3231
  yield makeUserMessage(initialPrompt);
3169
3232
  try {
3170
- while (!this.stopped) {
3233
+ while (!this.stopped && !this.interrupted) {
3171
3234
  if (this.pendingMessages.length > 0) {
3172
3235
  const next = this.pendingMessages.shift();
3173
3236
  if (next) yield next;
@@ -3186,7 +3249,7 @@ var AgentRunner = class {
3186
3249
  }
3187
3250
  }
3188
3251
  asQueryHost() {
3189
- return buildQueryHost({
3252
+ const host = buildQueryHost({
3190
3253
  config: this.config,
3191
3254
  connection: this.connection,
3192
3255
  callbacks: this.callbacks,
@@ -3204,12 +3267,14 @@ var AgentRunner = class {
3204
3267
  setPendingModeRestart: (val) => {
3205
3268
  this.pendingModeRestart = val;
3206
3269
  },
3207
- isStopped: () => this.stopped,
3270
+ isStopped: () => this.stopped || this.interrupted,
3208
3271
  createInputStream: (prompt) => this.createInputStream(prompt),
3209
3272
  onModeTransition: (newMode) => {
3210
3273
  this.agentMode = newMode;
3211
3274
  }
3212
3275
  });
3276
+ this._queryHost = host;
3277
+ return host;
3213
3278
  }
3214
3279
  handleModeChange(newAgentMode) {
3215
3280
  if (this.config.mode !== "pm") return;
@@ -3224,6 +3289,21 @@ var AgentRunner = class {
3224
3289
  this.taskContext.status = "InProgress";
3225
3290
  }
3226
3291
  }
3292
+ softStop() {
3293
+ this.interrupted = true;
3294
+ const host = this._queryHost;
3295
+ if (host?.activeQuery) {
3296
+ const q = host.activeQuery;
3297
+ if (typeof q.interrupt === "function") {
3298
+ void q.interrupt();
3299
+ }
3300
+ host.activeQuery = null;
3301
+ }
3302
+ if (this.inputResolver) {
3303
+ this.inputResolver(null);
3304
+ this.inputResolver = null;
3305
+ }
3306
+ }
3227
3307
  stop() {
3228
3308
  this.stopped = true;
3229
3309
  this.clearIdleTimers();
@@ -3696,4 +3776,4 @@ export {
3696
3776
  ProjectRunner,
3697
3777
  FileCache
3698
3778
  };
3699
- //# sourceMappingURL=chunk-7Y3RP3ZA.js.map
3779
+ //# sourceMappingURL=chunk-F4RQRSK6.js.map