@rallycry/conveyor-agent 7.3.5 → 7.3.7

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.
@@ -710,11 +710,19 @@ var Lifecycle = class {
710
710
  this.clearIdleTimers();
711
711
  }
712
712
  // ── Dormant timer ──────────────────────────────────────────────────
713
- startDormantTimer() {
713
+ /** Start (or restart) the dormant timer.
714
+ * @param overrideMs Optional custom delay in ms. When provided, the timer
715
+ * fires after exactly that delay instead of `dormantTimeoutMs`. SessionRunner
716
+ * uses this to enforce an *absolute* deadline across cycles: even if the
717
+ * dormant wait is interrupted by an inbound message, the next iteration
718
+ * passes the remaining time, so the agent shuts down at the original
719
+ * deadline regardless of message volume. */
720
+ startDormantTimer(overrideMs) {
714
721
  this.cancelDormantTimer();
722
+ const delay = Math.max(0, overrideMs ?? this.config.dormantTimeoutMs);
715
723
  this.dormantTimer = setTimeout(() => {
716
724
  this.callbacks.onDormantTimeout();
717
- }, this.config.dormantTimeoutMs);
725
+ }, delay);
718
726
  }
719
727
  cancelDormantTimer() {
720
728
  if (this.dormantTimer) {
@@ -6577,6 +6585,13 @@ var SessionRunner = class _SessionRunner {
6577
6585
  /** Defense-in-depth: set when the agent emits a "completed" event.
6578
6586
  * Prevents the core loop from processing any further messages. */
6579
6587
  completedThisTurn = false;
6588
+ /** Absolute deadline (ms epoch) at which the dormant idle wait must time
6589
+ * out. Set on the FIRST entry into dormant idle for a given completion
6590
+ * cycle and preserved across iterations so inbound critical messages
6591
+ * cannot extend the bound past the configured `dormantTimeoutMs`. Reset
6592
+ * to null when the agent transitions out of dormant idle (a wake actually
6593
+ * promotes it back to a working turn). */
6594
+ dormantDeadline = null;
6580
6595
  taskContext = null;
6581
6596
  fullContext = null;
6582
6597
  queryBridge = null;
@@ -6729,22 +6744,8 @@ var SessionRunner = class _SessionRunner {
6729
6744
  async coreLoop() {
6730
6745
  while (!this.stopped) {
6731
6746
  if (this.completedThisTurn) {
6732
- process.stderr.write(
6733
- "[conveyor-agent] Completed \u2014 entering dormant idle (staying connected)\n"
6734
- );
6735
- this.pendingMessages.length = 0;
6736
- if (this._state !== "idle") await this.setState("idle");
6737
- this.lifecycle.startDormantTimer();
6738
- const dormantMsg = await this.waitForMessage();
6739
- this.lifecycle.cancelDormantTimer();
6740
- if (!dormantMsg) break;
6741
- const contentPreview = dormantMsg.content.length > 80 ? `${dormantMsg.content.slice(0, 80)}...` : dormantMsg.content;
6742
- process.stderr.write(
6743
- `[conveyor-agent] Received message while dormant, resuming: userId=${dormantMsg.userId}, source=${dormantMsg.source || "unknown"}, content="${contentPreview.replace(/\n/g, "\\n")}"
6744
- `
6745
- );
6746
- this.completedThisTurn = false;
6747
- this.pendingMessages.unshift(dormantMsg);
6747
+ const resumed = await this.handleDormantIdle();
6748
+ if (!resumed) break;
6748
6749
  continue;
6749
6750
  }
6750
6751
  if (this._state === "idle") {
@@ -6798,6 +6799,41 @@ var SessionRunner = class _SessionRunner {
6798
6799
  }
6799
6800
  }
6800
6801
  }
6802
+ /**
6803
+ * Handle dormant-after-completed idle. Returns true if a critical message
6804
+ * woke us (caller should `continue` the loop), false if we should break
6805
+ * (stop signal or dormant timeout fired).
6806
+ *
6807
+ * The absolute deadline is set on first entry per completion cycle and
6808
+ * preserved across re-entries: an inbound critical message cannot extend
6809
+ * the bound past `dormantTimeoutMs`. Only a genuine wake (clearing
6810
+ * `completedThisTurn`) resets the deadline so the next dormant entry
6811
+ * starts a fresh window.
6812
+ */
6813
+ async handleDormantIdle() {
6814
+ if (this.dormantDeadline === null) {
6815
+ this.dormantDeadline = Date.now() + this.lifecycle.config.dormantTimeoutMs;
6816
+ process.stderr.write(
6817
+ "[conveyor-agent] Completed \u2014 entering dormant idle (staying connected)\n"
6818
+ );
6819
+ }
6820
+ this.pendingMessages.length = 0;
6821
+ if (this._state !== "idle") await this.setState("idle");
6822
+ const remainingMs = Math.max(0, this.dormantDeadline - Date.now());
6823
+ this.lifecycle.startDormantTimer(remainingMs);
6824
+ const dormantMsg = await this.waitForMessage();
6825
+ this.lifecycle.cancelDormantTimer();
6826
+ if (!dormantMsg) return false;
6827
+ const contentPreview = dormantMsg.content.length > 80 ? `${dormantMsg.content.slice(0, 80)}...` : dormantMsg.content;
6828
+ process.stderr.write(
6829
+ `[conveyor-agent] Received message while dormant, resuming: userId=${dormantMsg.userId}, source=${dormantMsg.source || "unknown"}, content="${contentPreview.replace(/\n/g, "\\n")}"
6830
+ `
6831
+ );
6832
+ this.completedThisTurn = false;
6833
+ this.dormantDeadline = null;
6834
+ this.pendingMessages.unshift(dormantMsg);
6835
+ return true;
6836
+ }
6801
6837
  // ── Initial mode execution ─────────────────────────────────────────
6802
6838
  /** Returns true if an initial query was executed, false otherwise. */
6803
6839
  async executeInitialMode() {
@@ -8768,4 +8804,4 @@ export {
8768
8804
  buildSessionPreviewPorts,
8769
8805
  loadConveyorConfig
8770
8806
  };
8771
- //# sourceMappingURL=chunk-KO2YQJEV.js.map
8807
+ //# sourceMappingURL=chunk-PC43BKMM.js.map