@rallycry/conveyor-agent 7.0.6 → 7.0.8

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.
@@ -15,6 +15,8 @@ var AgentConnection = class _AgentConnection {
15
15
  eventBuffer = [];
16
16
  flushTimer = null;
17
17
  lastEmittedStatus = null;
18
+ // Pending answer resolvers for askUserQuestion room-event fallback
19
+ pendingAnswerResolvers = /* @__PURE__ */ new Map();
18
20
  // Dedup: suppress near-identical messages within a short window
19
21
  recentMessages = [];
20
22
  static DEDUP_WINDOW_MS = 3e4;
@@ -114,6 +116,13 @@ var AgentConnection = class _AgentConnection {
114
116
  this.earlyMessages.push({ content: "", userId: "system" });
115
117
  }
116
118
  });
119
+ this.socket.on(
120
+ "session:answerQuestion",
121
+ (data) => {
122
+ const resolver = this.pendingAnswerResolvers.get(data.requestId);
123
+ if (resolver) resolver(data.answers);
124
+ }
125
+ );
117
126
  this.socket.on("agentRunner:updateApiKey", (data) => {
118
127
  if (this.apiKeyUpdateCallback) this.apiKeyUpdateCallback(data);
119
128
  });
@@ -267,7 +276,7 @@ var AgentConnection = class _AgentConnection {
267
276
  (m) => now - m.timestamp < _AgentConnection.DEDUP_WINDOW_MS
268
277
  );
269
278
  const words = new Set(
270
- content.toLowerCase().split(/\s+/).filter((w) => w.length >= 3)
279
+ content.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/).filter((w) => w.length >= 3)
271
280
  );
272
281
  if (words.size === 0) return false;
273
282
  for (const recent of this.recentMessages) {
@@ -364,13 +373,20 @@ var AgentConnection = class _AgentConnection {
364
373
  ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o.description}`).join("\n") : ""}`
365
374
  ).join("\n\n");
366
375
  const requestId = crypto.randomUUID();
367
- const { answers } = await this.call("askUserQuestion", {
376
+ const roomEventPromise = new Promise((resolve2) => {
377
+ this.pendingAnswerResolvers.set(requestId, resolve2);
378
+ });
379
+ const rpcPromise = this.call("askUserQuestion", {
368
380
  sessionId: this.config.sessionId,
369
381
  question: questionText,
370
382
  requestId,
371
383
  questions
372
- });
373
- return answers;
384
+ }).then((res) => res.answers);
385
+ try {
386
+ return await Promise.race([rpcPromise, roomEventPromise]);
387
+ } finally {
388
+ this.pendingAnswerResolvers.delete(requestId);
389
+ }
374
390
  }
375
391
  // ── Typed service method wrappers ───────────────────────────────────
376
392
  getTaskProperties() {
@@ -4957,7 +4973,7 @@ async function handleExitPlanMode(host, input) {
4957
4973
  host.connection.postChatMessage(
4958
4974
  "Plan complete. The task stays in Planning \u2014 identify it or switch to Build mode when ready."
4959
4975
  );
4960
- host.requestStop();
4976
+ host.requestSoftStop();
4961
4977
  return { behavior: "allow", updatedInput: input };
4962
4978
  }
4963
4979
  await host.connection.triggerIdentification();
@@ -5000,6 +5016,7 @@ async function handleAskUserQuestion(host, input) {
5000
5016
  return { behavior: "allow", updatedInput: { questions: input.questions, answers } };
5001
5017
  }
5002
5018
  var DENIAL_WARNING_THRESHOLD = 3;
5019
+ var DENIAL_FORCE_STOP_THRESHOLD = 8;
5003
5020
  function isResourceHeavyCommand(command) {
5004
5021
  return RESOURCE_HEAVY_PATTERNS.some((p) => p.test(command));
5005
5022
  }
@@ -5048,6 +5065,12 @@ function buildCanUseTool(host) {
5048
5065
  `\u26A0\uFE0F Multiple tool denials detected. You are in ${host.agentMode} mode \u2014 file writes outside .claude/plans/ are not permitted. Focus on creating a plan instead of implementing code changes.`
5049
5066
  );
5050
5067
  }
5068
+ if (consecutiveDenials >= DENIAL_FORCE_STOP_THRESHOLD) {
5069
+ host.connection.postChatMessage(
5070
+ `Agent force-stopped after ${DENIAL_FORCE_STOP_THRESHOLD} consecutive tool denials. The agent appears stuck \u2014 send a message to resume.`
5071
+ );
5072
+ host.requestStop();
5073
+ }
5051
5074
  } else {
5052
5075
  consecutiveDenials = 0;
5053
5076
  }
@@ -5485,6 +5508,8 @@ var QueryBridge = class {
5485
5508
  _abortController = null;
5486
5509
  /** Called by SessionRunner when ExitPlanMode triggers a mode transition. */
5487
5510
  onModeTransition;
5511
+ /** Called by tool handlers to soft-stop (abort query, keep session alive). */
5512
+ onSoftStop;
5488
5513
  get isStopped() {
5489
5514
  return this._stopped;
5490
5515
  }
@@ -5568,6 +5593,9 @@ var QueryBridge = class {
5568
5593
  },
5569
5594
  isStopped: () => bridge._stopped,
5570
5595
  requestStop: () => bridge.stop(),
5596
+ requestSoftStop: () => {
5597
+ if (bridge.onSoftStop) bridge.onSoftStop();
5598
+ },
5571
5599
  createInputStream: (prompt) => bridge.createInputStream(prompt),
5572
5600
  snapshotPlanFiles: () => bridge.planSync.snapshotPlanFiles(),
5573
5601
  syncPlanFile: () => bridge.planSync.syncPlanFile(),
@@ -5643,8 +5671,12 @@ var SessionRunner = class _SessionRunner {
5643
5671
  return this.stopped;
5644
5672
  }
5645
5673
  // ── Main lifecycle ─────────────────────────────────────────────────
5646
- // oxlint-disable-next-line max-lines-per-function, complexity -- lifecycle orchestration is inherently sequential
5647
- async start() {
5674
+ /**
5675
+ * Establish the API connection, wire callbacks, and join the session room.
5676
+ * Call this before run() when you need to send events (e.g. setup/start
5677
+ * command output) before the main agent lifecycle begins.
5678
+ */
5679
+ async connect() {
5648
5680
  await this.setState("connecting");
5649
5681
  await this.connection.connect();
5650
5682
  await this.setState("connected");
@@ -5659,6 +5691,13 @@ var SessionRunner = class _SessionRunner {
5659
5691
  this.pendingMessages.push({ content: msg.content, userId: msg.userId });
5660
5692
  }
5661
5693
  }
5694
+ }
5695
+ /**
5696
+ * Run the main agent lifecycle: fetch context, resolve mode, execute, loop.
5697
+ * Requires connect() to have been called first.
5698
+ */
5699
+ // oxlint-disable-next-line max-lines-per-function, complexity -- lifecycle orchestration is inherently sequential
5700
+ async run() {
5662
5701
  await this.setState("fetching_context");
5663
5702
  try {
5664
5703
  const ctx = await this.connection.call("getTaskContext", {
@@ -5706,6 +5745,11 @@ var SessionRunner = class _SessionRunner {
5706
5745
  }
5707
5746
  await this.shutdown("finished");
5708
5747
  }
5748
+ /** Convenience wrapper: connect() then run(). */
5749
+ async start() {
5750
+ await this.connect();
5751
+ await this.run();
5752
+ }
5709
5753
  // ── Core loop ──────────────────────────────────────────────────────
5710
5754
  async coreLoop() {
5711
5755
  while (!this.stopped) {
@@ -5863,7 +5907,7 @@ var SessionRunner = class _SessionRunner {
5863
5907
  async maybeSendPRNudge() {
5864
5908
  await this.refreshTaskContext();
5865
5909
  if (!this.needsPRNudge()) return;
5866
- while (!this.stopped && this.needsPRNudge()) {
5910
+ while (!this.stopped && !this.interrupted && this.needsPRNudge()) {
5867
5911
  this.prNudgeCount++;
5868
5912
  if (this.prNudgeCount > _SessionRunner.MAX_PR_NUDGES) {
5869
5913
  this.connection.postChatMessage(
@@ -5878,6 +5922,7 @@ var SessionRunner = class _SessionRunner {
5878
5922
  this.connection.postChatMessage(chatMsg);
5879
5923
  await this.setState("running");
5880
5924
  await this.callbacks.onEvent({ type: "pr_nudge", prompt });
5925
+ if (this.interrupted || this.stopped) return;
5881
5926
  await this.executeQuery(prompt);
5882
5927
  if (this.interrupted || this.stopped) return;
5883
5928
  await this.refreshTaskContext();
@@ -5950,6 +5995,10 @@ var SessionRunner = class _SessionRunner {
5950
5995
  this.config.workspaceDir
5951
5996
  );
5952
5997
  bridge.isParentTask = this.fullContext?.isParentTask ?? false;
5998
+ bridge.onSoftStop = () => {
5999
+ process.stderr.write("[conveyor-agent] Soft stop requested (discovery ExitPlanMode)\n");
6000
+ this.softStop();
6001
+ };
5953
6002
  bridge.onModeTransition = (newMode) => {
5954
6003
  const oldMode = this.mode.effectiveMode;
5955
6004
  process.stderr.write(`[conveyor-agent] Mode transition: ${oldMode} \u2192 ${newMode}
@@ -6814,6 +6863,8 @@ function spawnChildAgent(assignment, workDir) {
6814
6863
  const childEnv = { ...process.env };
6815
6864
  delete childEnv.CONVEYOR_PROJECT_TOKEN;
6816
6865
  delete childEnv.CONVEYOR_PROJECT_ID;
6866
+ delete childEnv.CONVEYOR_SETUP_COMMAND;
6867
+ delete childEnv.CONVEYOR_START_COMMAND;
6817
6868
  const effectiveAgentMode = agentMode ?? (isAuto ? "auto" : "");
6818
6869
  const effectiveApiUrl = apiUrl || process.env.CONVEYOR_API_URL || "";
6819
6870
  if (!effectiveApiUrl) {
@@ -7427,4 +7478,4 @@ export {
7427
7478
  loadForwardPorts,
7428
7479
  loadConveyorConfig
7429
7480
  };
7430
- //# sourceMappingURL=chunk-3H2FXJFD.js.map
7481
+ //# sourceMappingURL=chunk-PMDVB2EU.js.map