@rallycry/conveyor-agent 11.0.2 → 11.0.3

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.
@@ -13150,7 +13150,9 @@ function taskIdToSessionUuid(lineageKey) {
13150
13150
  return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-8${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
13151
13151
  }
13152
13152
  function sessionLineageKey(taskId, agentMode, runnerMode) {
13153
- return agentMode === "review" || runnerMode === "code-review" ? `${taskId}:review` : taskId;
13153
+ if (agentMode === "review" || runnerMode === "code-review") return `${taskId}:review`;
13154
+ if (runnerMode === "plan") return `${taskId}:plan`;
13155
+ return taskId;
13154
13156
  }
13155
13157
  function sessionFileExists(sessionUuid, cwd) {
13156
13158
  try {
@@ -13417,7 +13419,7 @@ async function handleTurnSilence(host, state, waitMs, silenceTimeoutMs, probeMou
13417
13419
  host,
13418
13420
  `[conveyor-agent] Turn produced no events for ${minutes}m with no heavy gate running \u2014 aborting the wedged turn
13419
13421
  `,
13420
- `Turn silent for ${minutes}m \u2014 aborting it so the agent can go idle (the conversation resumes on the next message)`
13422
+ `Turn silent for ${minutes}m \u2014 aborting it so the agent can go idle. A first turn retries automatically; otherwise the conversation resumes on the next message.`
13421
13423
  );
13422
13424
  return "keep_waiting";
13423
13425
  }
@@ -14632,7 +14634,6 @@ function findLiveChild(sources) {
14632
14634
 
14633
14635
  // src/runner/session-runner.ts
14634
14636
  var AUTO_RUN_MODES = /* @__PURE__ */ new Set(["building", "auto", "review", "discovery", "chat"]);
14635
- var AUTONOMOUS_RUNNER_MODES = /* @__PURE__ */ new Set(["pack", "pm", "code-review"]);
14636
14637
  var CHILD_DEFER_RECHECK_MS = 60 * 1e3;
14637
14638
  var SessionRunner = class _SessionRunner {
14638
14639
  connection;
@@ -15113,26 +15114,48 @@ var SessionRunner = class _SessionRunner {
15113
15114
  return true;
15114
15115
  }
15115
15116
  /**
15116
- * The mid-turn wedge watchdog aborts a turn that produced no events for 30
15117
- * minutes. Its documented recovery — "the next message respawns with
15118
- * `--resume`" — assumes a human or a follow-up is coming, which is exactly
15119
- * what an autonomous card does NOT have: after the abort the runner goes
15120
- * idle and the card parks until a pack watchdog or a human notices.
15117
+ * The mid-turn watchdog aborts a turn that produced no events for 30 minutes
15118
+ * (or one whose config-home mount died). Its documented recovery — "the next
15119
+ * message respawns with `--resume`" — assumes a human or a follow-up is
15120
+ * coming. A KICKOFF turn has neither by construction: it is the turn that
15121
+ * starts the conversation, so there is no next message, and the runner goes
15122
+ * idle with the card parked until a human notices.
15121
15123
  *
15122
- * So for an autonomous card whose INITIAL query was killed as wedged, re-run
15123
- * that initial query once against the (durable, on-disk) resumed session.
15124
- * Bounded to a single retry: a second wedge is a real failure that should
15125
- * surface as a parked card rather than loop the pod. Skipped when a pending
15126
- * message already exists — the core loop delivers that instead, which is the
15127
- * path the watchdog's original recovery assumed.
15124
+ * Observed live 2026-08-28 (card cmtd78iql): a spawned builder child's
15125
+ * kickoff turn produced nothing for 30 minutes, was aborted, and the child
15126
+ * then heartbeated idle for another 43 minutes until a human typed
15127
+ * "@[agent:builder] continue".
15128
+ *
15129
+ * So any submit-delivery INITIAL query killed by the watchdog is re-run once
15130
+ * against the (durable, on-disk) resumed session. Bounded to a single retry:
15131
+ * a second wedge is a real failure that should surface as a parked card
15132
+ * rather than loop the pod.
15133
+ *
15134
+ * Nothing recovers a card from there automatically, and that is the honest
15135
+ * state of it. The server's session sweep does NOT catch this shape: its
15136
+ * predicate is `agentRunningAt` still null, and a turn that started and then
15137
+ * wedged has already stamped it. The sweep covers a builder that never took a
15138
+ * turn at all; this retry covers one whose first turn stalled. Twice-wedged
15139
+ * needs a human, and a card left that way keeps its Builder tab and its
15140
+ * heartbeat, so it is visible rather than silently gone.
15141
+ *
15142
+ * Skipped when a pending message already exists, because the core loop
15143
+ * delivers that instead, which is the path the watchdog's original recovery
15144
+ * assumed.
15145
+ *
15146
+ * A mount-death abort retries too, and must: the repair for a dead config
15147
+ * home is the respawn that follows the abort, so a kickoff with nothing
15148
+ * behind it would otherwise never get one.
15149
+ *
15150
+ * NOT for follow-up turns — there "the conversation resumes on the next
15151
+ * message" is exactly right.
15128
15152
  */
15129
15153
  async requeueWedgedInitialQuery(delivery) {
15130
15154
  if (!this.queryBridge?.lastTurnWedgeAborted) return;
15131
15155
  if (this.stopped || this.interrupted || this.completedThisTurn) return;
15132
15156
  if (this.pendingMessages.length > 0) return;
15133
- if (!this.mode.isAuto && !AUTONOMOUS_RUNNER_MODES.has(this.config.runnerMode ?? "")) return;
15134
15157
  process.stderr.write(
15135
- "[conveyor-agent] Initial query was aborted as wedged with nothing queued behind it \u2014 re-running it once (an autonomous card has no follow-up message coming)\n"
15158
+ "[conveyor-agent] Initial query was aborted as wedged with nothing queued behind it \u2014 re-running it once (a kickoff turn has no follow-up message coming)\n"
15136
15159
  );
15137
15160
  this.connection.sendEvent({
15138
15161
  type: "error",
@@ -15730,6 +15753,15 @@ ${outcome.failures.join("\n")}
15730
15753
  get finalState() {
15731
15754
  return this._finalState;
15732
15755
  }
15756
+ /** Does a transcript already exist for this session's lineage? */
15757
+ resolvesToTranscriptResume() {
15758
+ const taskId = this.fullContext?.taskId;
15759
+ if (!taskId) return false;
15760
+ return hasExistingSessionFile(taskId, this.config.workspaceDir, {
15761
+ agentMode: this.mode.effectiveMode,
15762
+ runnerMode: this.config.runnerMode
15763
+ });
15764
+ }
15733
15765
  buildTaskContextSnapshot() {
15734
15766
  return {
15735
15767
  isParentTask: this.fullContext?.isParentTask ?? false,
@@ -15746,6 +15778,12 @@ ${outcome.failures.join("\n")}
15746
15778
  mode: this.mode.effectiveMode,
15747
15779
  runnerMode: this.config.runnerMode ?? "task",
15748
15780
  sessionId: this.sessionId,
15781
+ // Whether this run will RESUME a transcript, read from disk under this
15782
+ // session's own lineage. `hasExistingSession` below answers a different
15783
+ // question — does the server hold an sdkSessionId — and the two disagree
15784
+ // whenever a role's lineage is fresh. Resume is filesystem-driven, so
15785
+ // this is the field to read when a session's context looks wrong.
15786
+ resumesTranscript: this.resolvesToTranscriptResume(),
15749
15787
  ...this.buildTaskContextSnapshot(),
15750
15788
  model: this.taskContext?.model,
15751
15789
  isAuto: this.config.isAuto ?? false,
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  resolveTuiKindFromEnv,
28
28
  runUsageProbe,
29
29
  sampleKeyUsage
30
- } from "./chunk-QETNCXIR.js";
30
+ } from "./chunk-DHKAY7HQ.js";
31
31
  import "./chunk-XORJ6SII.js";
32
32
  import {
33
33
  AgentConnection,
package/dist/index.d.ts CHANGED
@@ -802,18 +802,41 @@ declare class SessionRunner {
802
802
  /** Returns true if an initial query was executed, false otherwise. */
803
803
  private executeInitialMode;
804
804
  /**
805
- * The mid-turn wedge watchdog aborts a turn that produced no events for 30
806
- * minutes. Its documented recovery — "the next message respawns with
807
- * `--resume`" — assumes a human or a follow-up is coming, which is exactly
808
- * what an autonomous card does NOT have: after the abort the runner goes
809
- * idle and the card parks until a pack watchdog or a human notices.
805
+ * The mid-turn watchdog aborts a turn that produced no events for 30 minutes
806
+ * (or one whose config-home mount died). Its documented recovery — "the next
807
+ * message respawns with `--resume`" — assumes a human or a follow-up is
808
+ * coming. A KICKOFF turn has neither by construction: it is the turn that
809
+ * starts the conversation, so there is no next message, and the runner goes
810
+ * idle with the card parked until a human notices.
810
811
  *
811
- * So for an autonomous card whose INITIAL query was killed as wedged, re-run
812
- * that initial query once against the (durable, on-disk) resumed session.
813
- * Bounded to a single retry: a second wedge is a real failure that should
814
- * surface as a parked card rather than loop the pod. Skipped when a pending
815
- * message already exists — the core loop delivers that instead, which is the
816
- * path the watchdog's original recovery assumed.
812
+ * Observed live 2026-08-28 (card cmtd78iql): a spawned builder child's
813
+ * kickoff turn produced nothing for 30 minutes, was aborted, and the child
814
+ * then heartbeated idle for another 43 minutes until a human typed
815
+ * "@[agent:builder] continue".
816
+ *
817
+ * So any submit-delivery INITIAL query killed by the watchdog is re-run once
818
+ * against the (durable, on-disk) resumed session. Bounded to a single retry:
819
+ * a second wedge is a real failure that should surface as a parked card
820
+ * rather than loop the pod.
821
+ *
822
+ * Nothing recovers a card from there automatically, and that is the honest
823
+ * state of it. The server's session sweep does NOT catch this shape: its
824
+ * predicate is `agentRunningAt` still null, and a turn that started and then
825
+ * wedged has already stamped it. The sweep covers a builder that never took a
826
+ * turn at all; this retry covers one whose first turn stalled. Twice-wedged
827
+ * needs a human, and a card left that way keeps its Builder tab and its
828
+ * heartbeat, so it is visible rather than silently gone.
829
+ *
830
+ * Skipped when a pending message already exists, because the core loop
831
+ * delivers that instead, which is the path the watchdog's original recovery
832
+ * assumed.
833
+ *
834
+ * A mount-death abort retries too, and must: the repair for a dead config
835
+ * home is the respawn that follows the abort, so a kickoff with nothing
836
+ * behind it would otherwise never get one.
837
+ *
838
+ * NOT for follow-up turns — there "the conversation resumes on the next
839
+ * message" is exactly right.
817
840
  */
818
841
  private requeueWedgedInitialQuery;
819
842
  /**
@@ -957,6 +980,8 @@ declare class SessionRunner {
957
980
  private _finalState;
958
981
  /** The final status after run() completes. Use to determine exit code. */
959
982
  get finalState(): AgentRunnerStatus | null;
983
+ /** Does a transcript already exist for this session's lineage? */
984
+ private resolvesToTranscriptResume;
960
985
  private buildTaskContextSnapshot;
961
986
  private buildInitializationContext;
962
987
  private logInitialization;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SessionRunner,
3
3
  unshallowRepo
4
- } from "./chunk-QETNCXIR.js";
4
+ } from "./chunk-DHKAY7HQ.js";
5
5
  import "./chunk-XORJ6SII.js";
6
6
  import {
7
7
  AgentConnection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rallycry/conveyor-agent",
3
- "version": "11.0.2",
3
+ "version": "11.0.3",
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",