@rallycry/conveyor-agent 8.2.0 → 8.3.1

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