@rallycry/conveyor-agent 11.0.35 → 11.0.37

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.
@@ -383,8 +383,8 @@ var AgentConnection = class _AgentConnection {
383
383
  /** (Re)arm the recycle timer — called on every successful (re)connect. */
384
384
  scheduleSocketRecycle() {
385
385
  this.clearSocketRecycle();
386
- const delay2 = _AgentConnection.SOCKET_RECYCLE_BASE_MS + Math.random() * _AgentConnection.SOCKET_RECYCLE_JITTER_MS;
387
- this.armRecycleTimer(delay2);
386
+ const delay3 = _AgentConnection.SOCKET_RECYCLE_BASE_MS + Math.random() * _AgentConnection.SOCKET_RECYCLE_JITTER_MS;
387
+ this.armRecycleTimer(delay3);
388
388
  }
389
389
  clearSocketRecycle() {
390
390
  if (this.recycleTimer) {
@@ -392,11 +392,11 @@ var AgentConnection = class _AgentConnection {
392
392
  this.recycleTimer = null;
393
393
  }
394
394
  }
395
- armRecycleTimer(delay2) {
395
+ armRecycleTimer(delay3) {
396
396
  this.recycleTimer = setTimeout(() => {
397
397
  this.recycleTimer = null;
398
398
  this.attemptSocketRecycle();
399
- }, delay2);
399
+ }, delay3);
400
400
  this.recycleTimer.unref?.();
401
401
  }
402
402
  attemptSocketRecycle() {
@@ -4313,6 +4313,25 @@ async function verifyGitCredential(cwd) {
4313
4313
  // src/runner/git-utils.ts
4314
4314
  import { realpathSync } from "fs";
4315
4315
 
4316
+ // src/runner/git-fetch-retry.ts
4317
+ import { setTimeout as delay } from "timers/promises";
4318
+ async function fetchWithRetry(cwd, args) {
4319
+ for (let attempt = 0; ; attempt++) {
4320
+ try {
4321
+ return await git(cwd, ["fetch", ...args]);
4322
+ } catch (error) {
4323
+ const detail = error;
4324
+ const transient = detail.killed === true || /connection reset|could not resolve host|failed to connect|timed out|early EOF|HTTP\/2 stream|remote end hung up|TLS connection was non-properly terminated/i.test(
4325
+ `${detail.message ?? ""}
4326
+ ${detail.stderr ?? ""}`
4327
+ );
4328
+ if (!transient || attempt >= 2) throw error;
4329
+ process.stderr.write("[conveyor-agent] Retrying git fetch after a transport failure\n");
4330
+ await delay(1e3 * (attempt + 1));
4331
+ }
4332
+ }
4333
+ }
4334
+
4316
4335
  // src/runner/force-fresh-cooldown.ts
4317
4336
  var FORCE_FRESH_COOLDOWN_MS = 30 * 60 * 1e3;
4318
4337
  var blockedUntil = 0;
@@ -4340,8 +4359,7 @@ async function ensureOnTaskBranch(cwd, taskBranch, baseBranch) {
4340
4359
  if (await getCurrentBranch(cwd) === taskBranch) return true;
4341
4360
  let existsOnOrigin = true;
4342
4361
  try {
4343
- await git(cwd, [
4344
- "fetch",
4362
+ await fetchWithRetry(cwd, [
4345
4363
  "origin",
4346
4364
  `+refs/heads/${taskBranch}:refs/remotes/origin/${taskBranch}`
4347
4365
  ]);
@@ -4362,8 +4380,7 @@ async function ensureOnTaskBranch(cwd, taskBranch, baseBranch) {
4362
4380
  );
4363
4381
  return false;
4364
4382
  }
4365
- await git(cwd, [
4366
- "fetch",
4383
+ await fetchWithRetry(cwd, [
4367
4384
  "origin",
4368
4385
  `+refs/heads/${baseBranch}:refs/remotes/origin/${baseBranch}`
4369
4386
  ]);
@@ -4524,7 +4541,7 @@ async function restoreWipSnapshot(cwd, branch) {
4524
4541
  if (!branch) return "none";
4525
4542
  const ref = wipRefForBranch(branch);
4526
4543
  try {
4527
- await git(cwd, ["fetch", "origin", `+refs/heads/${ref}:refs/remotes/origin/${ref}`]);
4544
+ await fetchWithRetry(cwd, ["origin", `+refs/heads/${ref}:refs/remotes/origin/${ref}`]);
4528
4545
  } catch (err) {
4529
4546
  if (isMissingRefError(err)) return "none";
4530
4547
  wipRefPreserved.add(cwd);
@@ -4549,7 +4566,12 @@ async function restoreWipSnapshot(cwd, branch) {
4549
4566
  return "stale";
4550
4567
  }
4551
4568
  }
4552
- await git(cwd, ["stash", "apply", sha], GIT_SLOW_TIMEOUT_MS);
4569
+ try {
4570
+ await git(cwd, ["stash", "apply", sha], GIT_SLOW_TIMEOUT_MS);
4571
+ } catch {
4572
+ wipRefPreserved.add(cwd);
4573
+ return "conflict";
4574
+ }
4553
4575
  wipRefPushed.add(cwd);
4554
4576
  wipRefPreserved.delete(cwd);
4555
4577
  return "applied";
@@ -4916,11 +4938,11 @@ var Lifecycle = class {
4916
4938
  * window every time it defers. */
4917
4939
  startIdleTimer(overrideMs) {
4918
4940
  this.clearIdleTimers();
4919
- const delay2 = Math.max(0, overrideMs ?? this.config.idleTimeoutMs);
4941
+ const delay3 = Math.max(0, overrideMs ?? this.config.idleTimeoutMs);
4920
4942
  this.idleTimer = setTimeout(() => {
4921
4943
  this.idleTimer = null;
4922
4944
  this.callbacks.onIdleTimeout();
4923
- }, delay2);
4945
+ }, delay3);
4924
4946
  }
4925
4947
  cancelIdleTimer() {
4926
4948
  this.clearIdleTimers();
@@ -4941,10 +4963,10 @@ var Lifecycle = class {
4941
4963
  * deadline regardless of message volume. */
4942
4964
  startDormantTimer(overrideMs) {
4943
4965
  this.cancelDormantTimer();
4944
- const delay2 = Math.max(0, overrideMs ?? this.config.dormantTimeoutMs);
4966
+ const delay3 = Math.max(0, overrideMs ?? this.config.dormantTimeoutMs);
4945
4967
  this.dormantTimer = setTimeout(() => {
4946
4968
  this.callbacks.onDormantTimeout();
4947
- }, delay2);
4969
+ }, delay3);
4948
4970
  }
4949
4971
  cancelDormantTimer() {
4950
4972
  if (this.dormantTimer) {
@@ -4988,7 +5010,7 @@ function awaitGitReady(opts = {}) {
4988
5010
  opts.onLog?.("waiting for workspace git (workbench daemon)");
4989
5011
  return pollDaemon(clientFn, timeoutMs, pollMs, opts.onLog, opts.signal);
4990
5012
  }
4991
- function delay(ms, signal) {
5013
+ function delay2(ms, signal) {
4992
5014
  if (!signal) {
4993
5015
  return new Promise((resolve) => {
4994
5016
  setTimeout(resolve, ms);
@@ -5047,7 +5069,7 @@ async function pollDaemon(clientFn, timeoutMs, pollMs, onLog, signal) {
5047
5069
  onLog?.(`workspace git not ready after ${timeoutMs}ms \u2014 giving up`);
5048
5070
  return "timeout";
5049
5071
  }
5050
- await delay(pollMs, signal);
5072
+ await delay2(pollMs, signal);
5051
5073
  }
5052
5074
  }
5053
5075
 
@@ -97,7 +97,7 @@ import {
97
97
  statWorkspacePath,
98
98
  updateRemoteToken,
99
99
  verifyGitCredential
100
- } from "./chunk-VDK54VLS.js";
100
+ } from "./chunk-B4YQABDZ.js";
101
101
  import {
102
102
  registerBootMilestoneSocketFallback,
103
103
  reportBootMilestone
@@ -2265,6 +2265,23 @@ function createChatRecordMapper(rootDir) {
2265
2265
  };
2266
2266
  }
2267
2267
 
2268
+ // src/harness/pty/prompt-receipt.ts
2269
+ function isPromptReceipt(raw, text, sentAt) {
2270
+ if (!text || !raw || typeof raw !== "object") return false;
2271
+ const record = raw;
2272
+ if (record.type !== "user" || typeof record.timestamp !== "string") return false;
2273
+ if (!(Date.parse(record.timestamp) >= sentAt)) return false;
2274
+ if (!record.message || typeof record.message !== "object") return false;
2275
+ const message = record.message;
2276
+ const submitted = receiptText(message.content);
2277
+ const normalize2 = (value) => value.replace(/\r\n/g, "\n").trim();
2278
+ return message.role === "user" && typeof submitted === "string" && normalize2(text) !== "" && normalize2(submitted) === normalize2(text);
2279
+ }
2280
+ function receiptText(content) {
2281
+ const block = Array.isArray(content) && content.length === 1 ? content[0] : void 0;
2282
+ return typeof content === "string" ? content : block?.type === "text" && typeof block.text === "string" ? block.text : void 0;
2283
+ }
2284
+
2268
2285
  // src/harness/pty/output-coalescer.ts
2269
2286
  var DEFAULT_FLUSH_MS = 40;
2270
2287
  var DEFAULT_MAX_BUFFER_CHARS = 48 * 1024;
@@ -3215,6 +3232,8 @@ var PtySession = class {
3215
3232
  turn;
3216
3233
  codexPromptInArgv = false;
3217
3234
  codexInitialPrompt;
3235
+ awaitingPromptReceipt;
3236
+ promptReceiptSeen = false;
3218
3237
  /** A fresh CLI intentionally started without input for checkout overlap. */
3219
3238
  prewarmed = false;
3220
3239
  codexSource = null;
@@ -3386,6 +3405,8 @@ var PtySession = class {
3386
3405
  }
3387
3406
  /** Reset per-turn state shared by beginTurn/beginPassiveTurn. */
3388
3407
  resetForTurn() {
3408
+ this.promptReceiptSeen = false;
3409
+ this.awaitingPromptReceipt = void 0;
3389
3410
  this.sawResult = false;
3390
3411
  this.lastTurnCleanResult = false;
3391
3412
  this.passiveSignaled = false;
@@ -3661,11 +3682,10 @@ var PtySession = class {
3661
3682
  const transcriptPath = sessionTranscriptPath(this.options.cwd, sessionId);
3662
3683
  await mkdir3(dirname(transcriptPath), { recursive: true });
3663
3684
  const startOffset = this.resume ? await transcriptSize(transcriptPath) : 0;
3664
- const wantsRawRecords = typeof this.bridge?.sendChatEvent === "function" || typeof this.bridge?.notifyBackgroundTaskDone === "function";
3665
3685
  this.tailer = new JsonlTailer(
3666
3686
  transcriptPath,
3667
3687
  (event) => this.handleTranscriptEvent(event),
3668
- wantsRawRecords ? (raw) => this.handleRawRecord(raw) : void 0
3688
+ (raw) => this.handleRawRecord(raw)
3669
3689
  );
3670
3690
  this.tailer.start(startOffset);
3671
3691
  return { settingsPath, socketPath };
@@ -3676,6 +3696,12 @@ var PtySession = class {
3676
3696
  * backgrounded gate/subagent just finished) and the chat-proxy projection.
3677
3697
  */
3678
3698
  handleRawRecord(raw) {
3699
+ const pending = this.awaitingPromptReceipt;
3700
+ if (pending && isPromptReceipt(raw, pending.text, pending.sentAt)) {
3701
+ this.promptReceiptSeen = true;
3702
+ this.awaitingPromptReceipt = void 0;
3703
+ this.disarmSubmitNudge();
3704
+ }
3679
3705
  const completion = backgroundTaskCompletion(raw);
3680
3706
  if (completion) this.bridge?.notifyBackgroundTaskDone?.(completion.taskId);
3681
3707
  if (typeof this.bridge?.sendChatEvent === "function") this.relayChatRecord(raw);
@@ -4010,11 +4036,14 @@ var PtySession = class {
4010
4036
  }
4011
4037
  this.writeStdin(this.adapter.encodePromptBytes(text));
4012
4038
  if (this.turn.promptDelivery === "prefill") return;
4039
+ this.promptReceiptSeen = false;
4040
+ this.awaitingPromptReceipt = { text, sentAt: Date.now() };
4013
4041
  this.deliveredTexts.push(text);
4014
4042
  await sleep(resolveSubmitSettleMs());
4015
4043
  if (this._toreDown) return;
4016
4044
  this.writeStdin("\r");
4017
- if (this.adapter.capabilities.structuredEvents) this.armSubmitNudge();
4045
+ if (this.adapter.capabilities.structuredEvents && !this.promptReceiptSeen)
4046
+ this.armSubmitNudge();
4018
4047
  }
4019
4048
  async feedPrompt() {
4020
4049
  if (typeof this.turnPrompt === "string") {
@@ -15380,14 +15409,18 @@ var SessionRunner = class _SessionRunner {
15380
15409
  this.fullContext.baseBranch ?? void 0
15381
15410
  );
15382
15411
  if (!ok) {
15383
- process.stderr.write("[conveyor-agent] WARNING: task-branch checkout failed\n");
15412
+ const message = "Task branch checkout failed; workspace startup stopped. Resume to retry.";
15413
+ process.stderr.write(`[conveyor-agent] ${message}
15414
+ `);
15415
+ this.connection.sendEvent({ type: "error", message });
15416
+ await this.callbacks.onEvent({ type: "error", message });
15417
+ await prewarmInitial;
15418
+ await this.shutdown("error");
15419
+ return;
15384
15420
  }
15385
- if (ok) void reportBootMilestone({ key: "branch_ready" });
15421
+ void reportBootMilestone({ key: "branch_ready" });
15386
15422
  }
15387
15423
  if (this.fullContext?.githubBranch) await this.refreshSkillsForCheckout();
15388
- if (!this.stopped) {
15389
- this.lifecycle.startGitFlush();
15390
- }
15391
15424
  if (this.fullContext?.githubBranch) {
15392
15425
  const restored = await restoreWipSnapshot(
15393
15426
  this.config.workspaceDir,
@@ -15397,7 +15430,16 @@ var SessionRunner = class _SessionRunner {
15397
15430
  process.stderr.write(`[conveyor-agent] WIP snapshot restore: ${restored}
15398
15431
  `);
15399
15432
  }
15433
+ if (restored === "failed") {
15434
+ const message = "Saved work could not be restored; workspace startup stopped. Resume to retry.";
15435
+ this.connection.sendEvent({ type: "error", message });
15436
+ await this.callbacks.onEvent({ type: "error", message });
15437
+ await prewarmInitial;
15438
+ await this.shutdown("error");
15439
+ return;
15440
+ }
15400
15441
  }
15442
+ if (!this.stopped) this.lifecycle.startGitFlush();
15401
15443
  this.workspaceCommands?.notifyWorkspaceReady();
15402
15444
  if (this.fullContext?.isAuto && PRE_BUILD_TASK_STATUSES.has(this.taskContext.status) && this.mode.isBuildCapable && hasTaskPlan(this.fullContext.plan)) {
15403
15445
  void this.connection.triggerIdentification().catch(() => {
@@ -8,7 +8,7 @@ import {
8
8
  readWorkspaceBytes,
9
9
  statWorkspacePath,
10
10
  workspacePathExists
11
- } from "./chunk-VDK54VLS.js";
11
+ } from "./chunk-B4YQABDZ.js";
12
12
  import {
13
13
  reportBootMilestone
14
14
  } from "./chunk-Q4FQOJ7D.js";
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  WorkspaceCommandSupervisor,
8
8
  startWorkspaceCommandsAfterConnect,
9
9
  stopWorkspaceCommands
10
- } from "./chunk-6WVCWKDN.js";
10
+ } from "./chunk-WQC2YQT4.js";
11
11
  import {
12
12
  PtyHarness,
13
13
  SessionRunner,
@@ -27,7 +27,7 @@ import {
27
27
  runUsageProbe,
28
28
  sampleKeyUsage,
29
29
  usesNativeUsageReporting
30
- } from "./chunk-MYJQXN6E.js";
30
+ } from "./chunk-DINRKS2I.js";
31
31
  import "./chunk-37J5MMQT.js";
32
32
  import {
33
33
  inheritedEnv,
@@ -46,7 +46,7 @@ import {
46
46
  createServiceLogger,
47
47
  fetchBootstrap,
48
48
  loadConveyorConfig
49
- } from "./chunk-VDK54VLS.js";
49
+ } from "./chunk-B4YQABDZ.js";
50
50
  import "./chunk-Q4FQOJ7D.js";
51
51
  import "./chunk-W4LZ7R6Z.js";
52
52
  import "./chunk-372R6E4C.js";
@@ -1468,7 +1468,7 @@ if (!RUNNER_MODES.includes(CONVEYOR_MODE)) {
1468
1468
  process.exit(1);
1469
1469
  }
1470
1470
  if (CONVEYOR_MODE === "serving") {
1471
- const { runServingSession } = await import("./serve-boot-QOXREHJK.js");
1471
+ const { runServingSession } = await import("./serve-boot-KXWMUVAG.js");
1472
1472
  exitContext.runnerMode = "serving";
1473
1473
  exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? exitContext.sessionId;
1474
1474
  const outcome = await runServingSession({
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SessionRunner,
3
3
  unshallowRepo
4
- } from "./chunk-MYJQXN6E.js";
4
+ } from "./chunk-DINRKS2I.js";
5
5
  import "./chunk-37J5MMQT.js";
6
6
  import "./chunk-EKTPDG3G.js";
7
7
  import {
@@ -17,7 +17,7 @@ import {
17
17
  stageAndCommit,
18
18
  updateRemoteToken,
19
19
  workspacePathExists
20
- } from "./chunk-VDK54VLS.js";
20
+ } from "./chunk-B4YQABDZ.js";
21
21
  import "./chunk-Q4FQOJ7D.js";
22
22
  import {
23
23
  runAuthTokenCommand,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  WorkspaceCommandSupervisor
3
- } from "./chunk-6WVCWKDN.js";
3
+ } from "./chunk-WQC2YQT4.js";
4
4
  import {
5
5
  AgentConnection,
6
6
  CodespacePortVisibility,
@@ -11,7 +11,7 @@ import {
11
11
  createServiceLogger,
12
12
  ensureOnTaskBranch,
13
13
  loadConveyorConfig
14
- } from "./chunk-VDK54VLS.js";
14
+ } from "./chunk-B4YQABDZ.js";
15
15
  import "./chunk-Q4FQOJ7D.js";
16
16
  import "./chunk-W4LZ7R6Z.js";
17
17
  import "./chunk-IA45XHOA.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rallycry/conveyor-agent",
3
- "version": "11.0.35",
3
+ "version": "11.0.37",
4
4
  "description": "Conveyor Agent Runner v10 - PTY harness for the task chat (SDK harness for audit/project-chat). Agent-as-User architecture with BaseService patterns. Works locally too.",
5
5
  "keywords": [
6
6
  "agent",