@rallycry/conveyor-agent 8.2.0 → 8.3.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.
@@ -7,8 +7,9 @@ import {
7
7
  import {
8
8
  createHarness,
9
9
  createServiceLogger,
10
- defineTool
11
- } from "./chunk-KTNGU2WU.js";
10
+ defineTool,
11
+ sessionTranscriptPath
12
+ } from "./chunk-ND473JTK.js";
12
13
 
13
14
  // src/setup/bootstrap.ts
14
15
  var BOOTSTRAP_TIMEOUT_MS = 3e4;
@@ -1154,28 +1155,118 @@ function updateRemoteToken(cwd, token) {
1154
1155
  } catch {
1155
1156
  }
1156
1157
  }
1157
- async function flushPendingChanges(cwd, opts) {
1158
- let committed = false;
1159
- let pushed = false;
1160
- let hadWork = false;
1158
+ function wipRefForBranch(branch) {
1159
+ return `conveyor-wip/${branch}`;
1160
+ }
1161
+ var wipRefPushed = /* @__PURE__ */ new Set();
1162
+ function createWipSnapshot(cwd, message) {
1163
+ try {
1164
+ execSync("git add -A", { cwd, stdio: ["ignore", "pipe", "ignore"] });
1165
+ const sha = execSync(`git stash create ${JSON.stringify(message)}`, {
1166
+ cwd,
1167
+ stdio: ["ignore", "pipe", "ignore"]
1168
+ }).toString().trim();
1169
+ return sha || null;
1170
+ } catch {
1171
+ return null;
1172
+ } finally {
1173
+ try {
1174
+ execSync("git reset -q", { cwd, stdio: ["ignore", "pipe", "ignore"] });
1175
+ } catch {
1176
+ }
1177
+ }
1178
+ }
1179
+ function tryPushRefspec(cwd, refspec, force = false) {
1180
+ try {
1181
+ execSync(`git push ${force ? "--force " : ""}origin ${JSON.stringify(refspec)}`, {
1182
+ cwd,
1183
+ stdio: ["ignore", "pipe", "pipe"],
1184
+ timeout: 3e4
1185
+ });
1186
+ return true;
1187
+ } catch {
1188
+ return false;
1189
+ }
1190
+ }
1191
+ async function refreshRemoteToken(cwd, refreshToken) {
1192
+ if (!refreshToken) return;
1161
1193
  try {
1162
- if (hasUncommittedChanges(cwd)) {
1163
- hadWork = true;
1164
- const message = opts?.wipMessage ?? "WIP: auto-commit on conveyor-agent shutdown";
1165
- const hash = stageAndCommit(cwd, message);
1166
- committed = hash !== null;
1194
+ const token = await refreshToken();
1195
+ if (token) {
1196
+ updateRemoteToken(cwd, token);
1197
+ process.env.GITHUB_TOKEN = token;
1198
+ process.env.GH_TOKEN = token;
1167
1199
  }
1168
1200
  } catch {
1169
1201
  }
1202
+ }
1203
+ function restoreWipSnapshot(cwd, branch) {
1204
+ if (!branch) return "none";
1205
+ const ref = wipRefForBranch(branch);
1170
1206
  try {
1171
- if (hasUnpushedCommits(cwd)) {
1172
- hadWork = true;
1207
+ execSync(`git fetch origin +refs/heads/${ref}:refs/remotes/origin/${ref}`, {
1208
+ cwd,
1209
+ stdio: "ignore",
1210
+ timeout: 6e4
1211
+ });
1212
+ } catch {
1213
+ return "none";
1214
+ }
1215
+ wipRefPushed.add(cwd);
1216
+ try {
1217
+ const sha = execSync(`git rev-parse refs/remotes/origin/${ref}`, {
1218
+ cwd,
1219
+ stdio: ["ignore", "pipe", "ignore"]
1220
+ }).toString().trim();
1221
+ const parent = execSync(`git rev-parse "${sha}^"`, {
1222
+ cwd,
1223
+ stdio: ["ignore", "pipe", "ignore"]
1224
+ }).toString().trim();
1225
+ const head = execSync("git rev-parse HEAD", { cwd, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
1226
+ if (parent !== head) return "stale";
1227
+ execSync(`git stash apply ${sha}`, { cwd, stdio: ["ignore", "pipe", "pipe"] });
1228
+ return "applied";
1229
+ } catch {
1230
+ return "failed";
1231
+ }
1232
+ }
1233
+ async function flushPendingChanges(cwd, opts) {
1234
+ let committed = false;
1235
+ let pushed = false;
1236
+ let hadWork = false;
1237
+ try {
1238
+ const branch = getCurrentBranch(cwd);
1239
+ if (!branch) return { committed, pushed, hadWork };
1240
+ const dirty = hasUncommittedChanges(cwd);
1241
+ const unpushed = hasUnpushedCommits(cwd);
1242
+ if (!dirty && !unpushed) {
1243
+ await dropStaleWipRef(cwd, branch, opts?.refreshToken);
1244
+ return { committed, pushed, hadWork };
1245
+ }
1246
+ hadWork = true;
1247
+ await refreshRemoteToken(cwd, opts?.refreshToken);
1248
+ if (unpushed) {
1173
1249
  pushed = await pushToOrigin(cwd, opts?.refreshToken);
1174
1250
  }
1251
+ if (dirty) {
1252
+ const message = opts?.wipMessage ?? "WIP: conveyor-agent snapshot";
1253
+ const sha = createWipSnapshot(cwd, message);
1254
+ if (sha) {
1255
+ committed = tryPushRefspec(cwd, `${sha}:refs/heads/${wipRefForBranch(branch)}`, true);
1256
+ if (committed) wipRefPushed.add(cwd);
1257
+ }
1258
+ }
1175
1259
  } catch {
1176
1260
  }
1177
1261
  return { committed, pushed, hadWork };
1178
1262
  }
1263
+ async function dropStaleWipRef(cwd, branch, refreshToken) {
1264
+ if (!wipRefPushed.has(cwd)) return;
1265
+ await refreshRemoteToken(cwd, refreshToken);
1266
+ if (tryPushRefspec(cwd, `:refs/heads/${wipRefForBranch(branch)}`)) {
1267
+ wipRefPushed.delete(cwd);
1268
+ }
1269
+ }
1179
1270
  async function pushToOrigin(cwd, refreshToken) {
1180
1271
  try {
1181
1272
  const currentBranch = getCurrentBranch(cwd);
@@ -1302,9 +1393,7 @@ var PlanSync = class {
1302
1393
 
1303
1394
  // src/execution/query-executor.ts
1304
1395
  import { createHash } from "crypto";
1305
- import { existsSync } from "fs";
1306
- import { homedir } from "os";
1307
- import { join as join3 } from "path";
1396
+ import { existsSync, readFileSync as readFileSync2, truncateSync } from "fs";
1308
1397
 
1309
1398
  // ../shared/dist/index.js
1310
1399
  import { z } from "zod";
@@ -1313,6 +1402,7 @@ import { z as z3 } from "zod";
1313
1402
  var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
1314
1403
  var EMBED_THRESHOLD_IMAGES = 5 * 1024 * 1024;
1315
1404
  var EMBED_THRESHOLD_TEXT = 2 * 1024 * 1024;
1405
+ var IDLE_HEARTBEAT_MS = 90 * 1e3;
1316
1406
  var AgentHeartbeatSchema = z.object({
1317
1407
  sessionId: z.string().optional(),
1318
1408
  timestamp: z.string(),
@@ -6389,19 +6479,63 @@ function buildHooks(host) {
6389
6479
  ]
6390
6480
  };
6391
6481
  }
6392
- function taskIdToSessionUuid(taskId) {
6393
- const hash = createHash("sha256").update(taskId).digest("hex");
6482
+ function taskIdToSessionUuid(lineageKey) {
6483
+ const hash = createHash("sha256").update(lineageKey).digest("hex");
6394
6484
  return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-8${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
6395
6485
  }
6486
+ function sessionLineageKey(taskId, agentMode, runnerMode) {
6487
+ return agentMode === "review" || runnerMode === "code-review" ? `${taskId}:review` : taskId;
6488
+ }
6396
6489
  function sessionFileExists(sessionUuid, cwd) {
6397
6490
  try {
6398
- const cwdSlug = cwd.replace(/\//g, "-");
6399
- const sessionFile = join3(homedir(), ".claude", "projects", cwdSlug, `${sessionUuid}.jsonl`);
6400
- return existsSync(sessionFile);
6491
+ return existsSync(sessionTranscriptPath(cwd, sessionUuid));
6492
+ } catch {
6493
+ return false;
6494
+ }
6495
+ }
6496
+ function hasExistingSessionFile(taskId, cwd, lineage) {
6497
+ const key = sessionLineageKey(taskId, lineage.agentMode, lineage.runnerMode);
6498
+ return sessionFileExists(taskIdToSessionUuid(key), cwd);
6499
+ }
6500
+ function repairTornSessionFile(path2) {
6501
+ try {
6502
+ if (!existsSync(path2)) return false;
6503
+ const content = readFileSync2(path2, "utf8");
6504
+ if (content.length === 0) return false;
6505
+ let keepEnd = content.length;
6506
+ if (!content.endsWith("\n")) {
6507
+ keepEnd = content.lastIndexOf("\n") + 1;
6508
+ }
6509
+ while (keepEnd > 0) {
6510
+ const prevNewline = content.lastIndexOf("\n", keepEnd - 2);
6511
+ const line = content.slice(prevNewline + 1, keepEnd - 1).trim();
6512
+ if (line.length > 0) {
6513
+ try {
6514
+ JSON.parse(line);
6515
+ break;
6516
+ } catch {
6517
+ }
6518
+ }
6519
+ keepEnd = prevNewline + 1;
6520
+ }
6521
+ if (keepEnd === content.length) return false;
6522
+ truncateSync(path2, Buffer.byteLength(content.slice(0, keepEnd), "utf8"));
6523
+ logger2.warn("Repaired torn transcript before resume", {
6524
+ path: path2,
6525
+ trimmedBytes: content.length - keepEnd
6526
+ });
6527
+ return true;
6401
6528
  } catch {
6402
6529
  return false;
6403
6530
  }
6404
6531
  }
6532
+ function resolvePromptDelivery(inputs) {
6533
+ if (inputs.harnessKind !== "pty") return "submit";
6534
+ if ((inputs.runnerMode ?? "task") !== "task") return "submit";
6535
+ if (inputs.isFollowUp || inputs.hasExistingSession) return "submit";
6536
+ if (inputs.isAuto || inputs.agentMode === "auto") return "submit";
6537
+ return "prefill";
6538
+ }
6405
6539
  function isReadOnlyMode(mode, hasExitedPlanMode) {
6406
6540
  return mode === "discovery" || mode === "help" || mode === "auto" && !hasExitedPlanMode;
6407
6541
  }
@@ -6514,7 +6648,17 @@ ${followUpText}` : followUpText;
6514
6648
  }
6515
6649
  return textPrompt;
6516
6650
  }
6517
- async function runSdkQuery(host, context, followUpContent) {
6651
+ async function* notifyOnFirstEvent(inner, onFirst) {
6652
+ let fired = false;
6653
+ for await (const event of inner) {
6654
+ if (!fired && event.type !== "system") {
6655
+ fired = true;
6656
+ await onFirst();
6657
+ }
6658
+ yield event;
6659
+ }
6660
+ }
6661
+ async function runSdkQuery(host, context, followUpContent, promptDeliveryOverride) {
6518
6662
  if (host.isStopped()) return;
6519
6663
  const mode = host.agentMode;
6520
6664
  const isDiscoveryLike = mode === "discovery" || mode === "help";
@@ -6522,10 +6666,24 @@ async function runSdkQuery(host, context, followUpContent) {
6522
6666
  if (needsPlanSync) {
6523
6667
  host.snapshotPlanFiles();
6524
6668
  }
6525
- const sessionUuid = taskIdToSessionUuid(context.taskId);
6669
+ const sessionUuid = taskIdToSessionUuid(
6670
+ sessionLineageKey(context.taskId, mode, host.config.mode)
6671
+ );
6526
6672
  const hasExistingSession = sessionFileExists(sessionUuid, host.config.workspaceDir);
6673
+ if (hasExistingSession) {
6674
+ repairTornSessionFile(sessionTranscriptPath(host.config.workspaceDir, sessionUuid));
6675
+ }
6676
+ const promptDelivery = promptDeliveryOverride ?? resolvePromptDelivery({
6677
+ harnessKind: host.harnessKind,
6678
+ runnerMode: host.config.mode,
6679
+ isAuto: host.isAuto,
6680
+ agentMode: mode,
6681
+ isFollowUp: !!followUpContent,
6682
+ hasExistingSession
6683
+ });
6527
6684
  const options = {
6528
6685
  ...buildQueryOptions(host, context),
6686
+ promptDelivery,
6529
6687
  ...hasExistingSession ? {} : { sessionId: sessionUuid }
6530
6688
  };
6531
6689
  const resume = hasExistingSession ? sessionUuid : void 0;
@@ -6536,33 +6694,45 @@ async function runSdkQuery(host, context, followUpContent) {
6536
6694
  options: { ...options },
6537
6695
  resume
6538
6696
  });
6539
- host.activeQuery = agentQuery;
6540
- try {
6541
- await runWithRetry(agentQuery, context, host, options);
6542
- } finally {
6543
- host.activeQuery = null;
6544
- }
6545
- } else if (isDiscoveryLike) {
6697
+ await trackAndRun(host, context, options, agentQuery);
6698
+ } else if (isDiscoveryLike && promptDelivery !== "prefill") {
6546
6699
  return;
6547
6700
  } else {
6548
- const initialPrompt = await buildInitialPrompt(host.config.mode, context, host.isAuto, mode);
6549
- const prompt = buildMultimodalPrompt(initialPrompt, context);
6550
- const agentQuery = host.harness.executeQuery({
6551
- prompt: host.createInputStream(prompt),
6552
- options: { ...options },
6553
- resume
6554
- });
6555
- host.activeQuery = agentQuery;
6556
- try {
6557
- await runWithRetry(agentQuery, context, host, options);
6558
- } finally {
6559
- host.activeQuery = null;
6560
- }
6701
+ await runInitialQuery(host, context, options, resume, promptDelivery);
6561
6702
  }
6562
6703
  if (needsPlanSync) {
6563
6704
  await host.syncPlanFile();
6564
6705
  }
6565
6706
  }
6707
+ async function trackAndRun(host, context, options, agentQuery) {
6708
+ host.activeQuery = agentQuery;
6709
+ try {
6710
+ await runWithRetry(agentQuery, context, host, options);
6711
+ } finally {
6712
+ host.activeQuery = null;
6713
+ }
6714
+ }
6715
+ async function runInitialQuery(host, context, options, resume, promptDelivery) {
6716
+ const initialPrompt = await buildInitialPrompt(
6717
+ host.config.mode,
6718
+ context,
6719
+ host.isAuto,
6720
+ host.agentMode
6721
+ );
6722
+ const prompt = buildMultimodalPrompt(initialPrompt, context);
6723
+ let agentQuery = host.harness.executeQuery({
6724
+ prompt: host.createInputStream(prompt),
6725
+ options: { ...options },
6726
+ resume
6727
+ });
6728
+ if (promptDelivery === "prefill") {
6729
+ agentQuery = notifyOnFirstEvent(agentQuery, async () => {
6730
+ host.connection.emitStatus("running");
6731
+ await host.callbacks.onStatusChange("running");
6732
+ });
6733
+ }
6734
+ await trackAndRun(host, context, options, agentQuery);
6735
+ }
6566
6736
  async function buildRetryQuery(host, context, options, lastErrorWasImage) {
6567
6737
  if (lastErrorWasImage) {
6568
6738
  host.connection.postChatMessage(
@@ -6901,6 +7071,7 @@ var QueryBridge = class {
6901
7071
  this.runnerConfig = runnerConfig;
6902
7072
  this.callbacks = callbacks;
6903
7073
  const harnessKind = resolveHarnessKind();
7074
+ this.harnessKind = harnessKind;
6904
7075
  this.harness = createHarness(
6905
7076
  harnessKind,
6906
7077
  harnessKind === "pty" ? buildPtyBridge(connection) : void 0
@@ -6913,6 +7084,7 @@ var QueryBridge = class {
6913
7084
  runnerConfig;
6914
7085
  callbacks;
6915
7086
  harness;
7087
+ harnessKind;
6916
7088
  costTracker;
6917
7089
  planSync;
6918
7090
  sessionIds = /* @__PURE__ */ new Map();
@@ -6956,18 +7128,40 @@ var QueryBridge = class {
6956
7128
  seedCostTracker(totalCostUsd, modelUsage) {
6957
7129
  this.costTracker.seed(totalCostUsd, modelUsage);
6958
7130
  }
7131
+ /**
7132
+ * How the INITIAL instructions for this task would be delivered — "prefill"
7133
+ * means the TUI input is pre-filled but unsubmitted, awaiting the human.
7134
+ * Mirrors the decision runSdkQuery makes for the initial (non-follow-up)
7135
+ * query so SessionRunner can pick the matching state/timers.
7136
+ */
7137
+ initialPromptDelivery(context) {
7138
+ return resolvePromptDelivery({
7139
+ harnessKind: this.harnessKind,
7140
+ runnerMode: this.runnerConfig.mode,
7141
+ isAuto: this.mode.isAuto,
7142
+ agentMode: this.mode.effectiveMode,
7143
+ isFollowUp: false,
7144
+ hasExistingSession: hasExistingSessionFile(context.taskId, this.runnerConfig.workspaceDir, {
7145
+ agentMode: this.mode.effectiveMode,
7146
+ runnerMode: this.runnerConfig.mode
7147
+ })
7148
+ });
7149
+ }
6959
7150
  /**
6960
7151
  * Execute a Claude SDK query.
6961
7152
  * Without followUpContent: runs initial mode execution (build/plan).
6962
7153
  * With followUpContent: processes a follow-up user message.
7154
+ * `promptDelivery` overrides the executor's submit-vs-prefill resolution —
7155
+ * SessionRunner forces "submit" when pending chat messages already direct
7156
+ * the agent (prefilling would park them until a human touched the TUI).
6963
7157
  */
6964
- async execute(context, followUpContent) {
7158
+ async execute(context, followUpContent, promptDelivery) {
6965
7159
  this._stopped = false;
6966
7160
  this._wasRateLimited = false;
6967
7161
  this._abortController = new AbortController();
6968
7162
  const host = this.buildHost();
6969
7163
  try {
6970
- await runSdkQuery(host, context, followUpContent);
7164
+ await runSdkQuery(host, context, followUpContent, promptDelivery);
6971
7165
  } catch (err) {
6972
7166
  const msg = err instanceof Error ? err.message : String(err);
6973
7167
  const isAbort = this._stopped || /abort/i.test(msg);
@@ -6990,6 +7184,7 @@ var QueryBridge = class {
6990
7184
  connection: this.connection,
6991
7185
  callbacks: this.callbacks,
6992
7186
  harness: this.harness,
7187
+ harnessKind: this.harnessKind,
6993
7188
  setupLog: [],
6994
7189
  costTracker: this.costTracker,
6995
7190
  explorationTracker: bridge.mode.effectiveMode === "discovery" || bridge.mode.effectiveMode === "auto" && !bridge.mode.hasExitedPlanMode ? new ExplorationTracker(bridge._isParentTask) : null,
@@ -7061,8 +7256,8 @@ var QueryBridge = class {
7061
7256
  };
7062
7257
 
7063
7258
  // src/runner/session-runner-helpers.ts
7064
- import { readFileSync as readFileSync2 } from "fs";
7065
- import { dirname, join as join4 } from "path";
7259
+ import { readFileSync as readFileSync3 } from "fs";
7260
+ import { dirname, join as join3 } from "path";
7066
7261
  import { fileURLToPath } from "url";
7067
7262
  function mapChatHistory(messages) {
7068
7263
  if (!messages) return [];
@@ -7091,7 +7286,7 @@ function readAgentVersion() {
7091
7286
  const here = dirname(fileURLToPath(import.meta.url));
7092
7287
  for (const rel of ["../package.json", "../../package.json"]) {
7093
7288
  try {
7094
- const pkg = JSON.parse(readFileSync2(join4(here, rel), "utf-8"));
7289
+ const pkg = JSON.parse(readFileSync3(join3(here, rel), "utf-8"));
7095
7290
  if (pkg.version) return pkg.version;
7096
7291
  } catch {
7097
7292
  }
@@ -7183,6 +7378,7 @@ var SessionRunner = class _SessionRunner {
7183
7378
  onIdleTimeout: () => {
7184
7379
  process.stderr.write("[conveyor-agent] Idle timeout reached, stopping agent\n");
7185
7380
  this.stopped = true;
7381
+ this.queryBridge?.stop();
7186
7382
  if (this.inputResolver) {
7187
7383
  const resolver = this.inputResolver;
7188
7384
  this.inputResolver = null;
@@ -7192,6 +7388,7 @@ var SessionRunner = class _SessionRunner {
7192
7388
  onDormantTimeout: () => {
7193
7389
  process.stderr.write("[conveyor-agent] Dormant idle timeout reached, shutting down\n");
7194
7390
  this.stopped = true;
7391
+ this.queryBridge?.stop();
7195
7392
  if (this.inputResolver) {
7196
7393
  const resolver = this.inputResolver;
7197
7394
  this.inputResolver = null;
@@ -7276,6 +7473,13 @@ var SessionRunner = class _SessionRunner {
7276
7473
  syncWithBaseBranch(this.config.workspaceDir, this.fullContext.baseBranch);
7277
7474
  }
7278
7475
  }
7476
+ if (this.fullContext?.githubBranch) {
7477
+ const restored = restoreWipSnapshot(this.config.workspaceDir, this.fullContext.githubBranch);
7478
+ if (restored !== "none") {
7479
+ process.stderr.write(`[conveyor-agent] WIP snapshot restore: ${restored}
7480
+ `);
7481
+ }
7482
+ }
7279
7483
  this.mode.applyServerMode(this.fullContext?.agentMode, this.fullContext?.isAuto);
7280
7484
  this.mode.resolveInitialMode(this.taskContext);
7281
7485
  if (this.fullContext?.isAuto && this.taskContext.status === "Open" && this.mode.isBuildCapable) {
@@ -7290,6 +7494,12 @@ var SessionRunner = class _SessionRunner {
7290
7494
  if (staleMessageCount > 0 && didExecuteInitialQuery) {
7291
7495
  this.pendingMessages.splice(0, staleMessageCount);
7292
7496
  }
7497
+ if (this.queryBridge?.isDiscoveryCompleted) {
7498
+ process.stderr.write(
7499
+ "[conveyor-agent] Discovery completed during initial query \u2014 entering idle\n"
7500
+ );
7501
+ this.queryBridge.isDiscoveryCompleted = false;
7502
+ }
7293
7503
  if (!this.stopped && this.pendingMessages.length === 0) {
7294
7504
  await this.maybeSendPRNudge();
7295
7505
  }
@@ -7410,16 +7620,32 @@ var SessionRunner = class _SessionRunner {
7410
7620
  async executeInitialMode() {
7411
7621
  if (!this.taskContext || !this.fullContext) return false;
7412
7622
  const effectiveMode = this.mode.effectiveMode;
7413
- const shouldRun = effectiveMode === "building" || effectiveMode === "auto" || effectiveMode === "review";
7414
- if (shouldRun) {
7623
+ const delivery = this.pendingMessages.length > 0 ? "submit" : this.queryBridge?.initialPromptDelivery(this.fullContext) ?? "submit";
7624
+ const shouldRun = effectiveMode === "building" || effectiveMode === "auto" || effectiveMode === "review" || delivery === "prefill";
7625
+ if (!shouldRun) {
7626
+ await this.setState("idle");
7627
+ return false;
7628
+ }
7629
+ if (delivery === "prefill") {
7630
+ await this.setState("waiting_for_input");
7631
+ await this.callbacks.onEvent({ type: "execute_mode", mode: effectiveMode, delivery });
7632
+ if (this.pendingMessages.length > 0) {
7633
+ if (!this.stopped) await this.setState("idle");
7634
+ return false;
7635
+ }
7636
+ this.lifecycle.startIdleTimer();
7637
+ try {
7638
+ await this.executeQuery(void 0, delivery);
7639
+ } finally {
7640
+ this.lifecycle.cancelIdleTimer();
7641
+ }
7642
+ } else {
7415
7643
  await this.setState("running");
7416
7644
  await this.callbacks.onEvent({ type: "execute_mode", mode: effectiveMode });
7417
- await this.executeQuery();
7418
- if (!this.stopped) await this.setState("idle");
7419
- return true;
7645
+ await this.executeQuery(void 0, delivery);
7420
7646
  }
7421
- await this.setState("idle");
7422
- return false;
7647
+ if (!this.stopped) await this.setState("idle");
7648
+ return true;
7423
7649
  }
7424
7650
  // ── Message waiting ────────────────────────────────────────────────
7425
7651
  waitForMessage() {
@@ -7438,17 +7664,17 @@ var SessionRunner = class _SessionRunner {
7438
7664
  resolve2(msg);
7439
7665
  } else {
7440
7666
  this.pendingMessages.push(msg);
7441
- if (this._state === "running") {
7667
+ if (this._state === "running" || this._state === "waiting_for_input") {
7442
7668
  this.queryBridge?.stop();
7443
7669
  }
7444
7670
  }
7445
7671
  }
7446
7672
  // ── Query execution with abort handling ────────────────────────────
7447
7673
  /** Run queryBridge.execute, swallowing abort errors from stop/softStop. */
7448
- async executeQuery(followUpContent) {
7674
+ async executeQuery(followUpContent, promptDelivery) {
7449
7675
  if (!this.fullContext || !this.queryBridge) return;
7450
7676
  try {
7451
- await this.queryBridge.execute(this.fullContext, followUpContent);
7677
+ await this.queryBridge.execute(this.fullContext, followUpContent, promptDelivery);
7452
7678
  } catch (err) {
7453
7679
  if (this.interrupted || this.stopped) {
7454
7680
  process.stderr.write("[conveyor-agent] Query aborted by stop/softStop signal\n");
@@ -7647,7 +7873,13 @@ var SessionRunner = class _SessionRunner {
7647
7873
  this.mode,
7648
7874
  runnerConfig,
7649
7875
  {
7650
- onStatusChange: (status) => this.callbacks.onStatusChange(status),
7876
+ onStatusChange: (status) => {
7877
+ if (status === "running" && this._state === "waiting_for_input") {
7878
+ this._state = "running";
7879
+ this.lifecycle.cancelIdleTimer();
7880
+ }
7881
+ return this.callbacks.onStatusChange(status);
7882
+ },
7651
7883
  onEvent: (event) => {
7652
7884
  if (event.type === "completed") {
7653
7885
  this.completedThisTurn = true;
@@ -8104,13 +8336,13 @@ var CommitWatcher = class {
8104
8336
  // src/runner/worktree.ts
8105
8337
  import { execSync as execSync4 } from "child_process";
8106
8338
  import { existsSync as existsSync2 } from "fs";
8107
- import { join as join5 } from "path";
8339
+ import { join as join4 } from "path";
8108
8340
  var WORKTREE_DIR = ".worktrees";
8109
8341
  function ensureWorktree(projectDir, taskId, branch) {
8110
8342
  if (projectDir.includes(`/${WORKTREE_DIR}/`)) {
8111
8343
  return projectDir;
8112
8344
  }
8113
- const worktreePath = join5(projectDir, WORKTREE_DIR, taskId);
8345
+ const worktreePath = join4(projectDir, WORKTREE_DIR, taskId);
8114
8346
  if (existsSync2(worktreePath)) {
8115
8347
  if (branch) {
8116
8348
  if (hasUncommittedChanges(worktreePath)) {
@@ -8156,7 +8388,7 @@ function detachWorktreeBranch(projectDir, branch) {
8156
8388
  }
8157
8389
  }
8158
8390
  function removeWorktree(projectDir, taskId) {
8159
- const worktreePath = join5(projectDir, WORKTREE_DIR, taskId);
8391
+ const worktreePath = join4(projectDir, WORKTREE_DIR, taskId);
8160
8392
  if (!existsSync2(worktreePath)) return;
8161
8393
  try {
8162
8394
  execSync4(`git worktree remove "${worktreePath}" --force`, {
@@ -9838,7 +10070,7 @@ var ProjectRunner = class {
9838
10070
  async handleAuditTags(request) {
9839
10071
  this.connection.emitStatus("busy");
9840
10072
  try {
9841
- const { handleTagAudit } = await import("./tag-audit-handler-E6BZGVIG.js");
10073
+ const { handleTagAudit } = await import("./tag-audit-handler-RFGGQWG6.js");
9842
10074
  await handleTagAudit(request, this.connection, this.projectDir);
9843
10075
  } catch (error) {
9844
10076
  const msg = parseErrorMessage(error);
@@ -9861,7 +10093,7 @@ var ProjectRunner = class {
9861
10093
  async handleAuditTasks(request) {
9862
10094
  this.connection.emitStatus("busy");
9863
10095
  try {
9864
- const { handleTaskAudit } = await import("./task-audit-handler-MXSNTY22.js");
10096
+ const { handleTaskAudit } = await import("./task-audit-handler-BN42VOYM.js");
9865
10097
  await handleTaskAudit(request, this.connection, this.projectDir);
9866
10098
  } catch (error) {
9867
10099
  const msg = parseErrorMessage(error);
@@ -9910,12 +10142,12 @@ var ProjectRunner = class {
9910
10142
 
9911
10143
  // src/setup/config.ts
9912
10144
  import { readFile as readFile3 } from "fs/promises";
9913
- import { join as join6 } from "path";
10145
+ import { join as join5 } from "path";
9914
10146
  var DEVCONTAINER_PATH = ".devcontainer/conveyor/devcontainer.json";
9915
10147
  var DEVCONTAINER_PORT_DENY_LIST = /* @__PURE__ */ new Set([5432, 6379, 9200]);
9916
10148
  async function loadForwardPorts(workspaceDir) {
9917
10149
  try {
9918
- const raw = await readFile3(join6(workspaceDir, DEVCONTAINER_PATH), "utf-8");
10150
+ const raw = await readFile3(join5(workspaceDir, DEVCONTAINER_PATH), "utf-8");
9919
10151
  const parsed = JSON.parse(raw);
9920
10152
  const ports = (parsed.forwardPorts ?? []).filter(
9921
10153
  (p) => typeof p === "number" && !DEVCONTAINER_PORT_DENY_LIST.has(p)
@@ -10002,4 +10234,4 @@ export {
10002
10234
  loadConveyorConfig,
10003
10235
  unshallowRepo
10004
10236
  };
10005
- //# sourceMappingURL=chunk-W4NCD23G.js.map
10237
+ //# sourceMappingURL=chunk-WNPFZC3I.js.map