@rallycry/conveyor-agent 5.1.2 → 5.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.
@@ -752,7 +752,7 @@ function aggregateModelUsage(modelUsage) {
752
752
  }
753
753
  return { queryInputTokens, contextWindow, totalInputTokens, totalCacheRead, totalCacheCreation };
754
754
  }
755
- function emitContextUpdate(modelUsage, host, context) {
755
+ function emitContextUpdate(modelUsage, host, context, lastAssistantUsage) {
756
756
  const usage = aggregateModelUsage(modelUsage);
757
757
  let { contextWindow } = usage;
758
758
  const settings = context.agentSettings ?? host.config.agentSettings ?? {};
@@ -761,13 +761,15 @@ function emitContextUpdate(modelUsage, host, context) {
761
761
  contextWindow = 1e6;
762
762
  }
763
763
  if (contextWindow > 0) {
764
+ const currentContextTokens = lastAssistantUsage ? (lastAssistantUsage.input_tokens ?? 0) + (lastAssistantUsage.cache_read_input_tokens ?? 0) + (lastAssistantUsage.cache_creation_input_tokens ?? 0) : usage.queryInputTokens;
764
765
  host.connection.sendEvent({
765
766
  type: "context_update",
766
- contextTokens: usage.queryInputTokens,
767
+ contextTokens: currentContextTokens,
767
768
  contextWindow,
768
769
  inputTokens: usage.totalInputTokens,
769
770
  cacheReadInputTokens: usage.totalCacheRead,
770
- cacheCreationInputTokens: usage.totalCacheCreation
771
+ cacheCreationInputTokens: usage.totalCacheCreation,
772
+ totalTokensUsed: usage.queryInputTokens
771
773
  });
772
774
  }
773
775
  }
@@ -783,7 +785,7 @@ function trackCostSpending(host, context, cumulativeTotal) {
783
785
  });
784
786
  }
785
787
  }
786
- function handleSuccessResult(event, host, context, startTime) {
788
+ function handleSuccessResult(event, host, context, startTime, lastAssistantUsage) {
787
789
  const durationMs = Date.now() - startTime;
788
790
  const summary = event.result || "Task completed.";
789
791
  const retriable = isRetriableMessage(summary);
@@ -794,28 +796,38 @@ function handleSuccessResult(event, host, context, startTime) {
794
796
  }
795
797
  host.connection.sendEvent({ type: "completed", summary, costUsd: cumulativeTotal, durationMs });
796
798
  if (modelUsage && typeof modelUsage === "object") {
797
- emitContextUpdate(modelUsage, host, context);
799
+ emitContextUpdate(modelUsage, host, context, lastAssistantUsage);
798
800
  }
799
801
  trackCostSpending(host, context, cumulativeTotal);
800
802
  return { totalCostUsd: cumulativeTotal, retriable };
801
803
  }
802
804
  function handleErrorResult(event, host) {
803
805
  const errorMsg = event.errors.length > 0 ? event.errors.join(", ") : `Agent stopped: ${event.subtype}`;
806
+ const isStaleSession = errorMsg.includes("No conversation found with session ID");
807
+ if (isStaleSession) {
808
+ return { retriable: false, staleSession: true };
809
+ }
804
810
  const retriable = isRetriableMessage(errorMsg);
805
811
  host.connection.sendEvent({ type: "error", message: errorMsg });
806
812
  return { retriable };
807
813
  }
808
- function handleResultEvent(event, host, context, startTime) {
814
+ function handleResultEvent(event, host, context, startTime, lastAssistantUsage) {
809
815
  const resultSummary = event.subtype === "success" ? event.result : event.errors.join(", ");
810
816
  if (event.subtype === "success") {
811
- const result2 = handleSuccessResult(event, host, context, startTime);
817
+ const result2 = handleSuccessResult(
818
+ event,
819
+ host,
820
+ context,
821
+ startTime,
822
+ lastAssistantUsage
823
+ );
812
824
  return { ...result2, resultSummary };
813
825
  }
814
826
  const result = handleErrorResult(event, host);
815
827
  return { totalCostUsd: 0, ...result, resultSummary };
816
828
  }
817
- async function emitResultEvent(event, host, context, startTime) {
818
- const result = handleResultEvent(event, host, context, startTime);
829
+ async function emitResultEvent(event, host, context, startTime, lastAssistantUsage) {
830
+ const result = handleResultEvent(event, host, context, startTime, lastAssistantUsage);
819
831
  const durationMs = Date.now() - startTime;
820
832
  if (event.subtype === "success") {
821
833
  const successEvent = event;
@@ -826,12 +838,16 @@ async function emitResultEvent(event, host, context, startTime) {
826
838
  costUsd: result.totalCostUsd,
827
839
  durationMs
828
840
  });
829
- } else {
841
+ } else if (!result.staleSession) {
830
842
  const errorEvent = event;
831
843
  const errorMsg = errorEvent.errors.length > 0 ? errorEvent.errors.join(", ") : `Agent stopped: ${errorEvent.subtype}`;
832
844
  await host.callbacks.onEvent({ type: "error", message: errorMsg });
833
845
  }
834
- return { retriable: result.retriable, resultSummary: result.resultSummary };
846
+ return {
847
+ retriable: result.retriable,
848
+ resultSummary: result.resultSummary,
849
+ staleSession: result.staleSession
850
+ };
835
851
  }
836
852
  function handleRateLimitEvent(event, host) {
837
853
  const { rate_limit_info } = event;
@@ -878,6 +894,8 @@ async function processEvents(events, context, host) {
878
894
  let retriable = false;
879
895
  let resultSummary;
880
896
  let rateLimitResetsAt;
897
+ let staleSession;
898
+ let lastAssistantUsage;
881
899
  const turnToolCalls = [];
882
900
  for await (const event of events) {
883
901
  if (host.isStopped()) break;
@@ -899,7 +917,12 @@ async function processEvents(events, context, host) {
899
917
  setTimeout(() => host.connection.sendTypingStart(), 200);
900
918
  isTyping = true;
901
919
  }
902
- await processAssistantEvent(event, host, turnToolCalls);
920
+ const assistantEvent = event;
921
+ await processAssistantEvent(assistantEvent, host, turnToolCalls);
922
+ const msgUsage = assistantEvent.message.usage;
923
+ if (msgUsage) {
924
+ lastAssistantUsage = msgUsage;
925
+ }
903
926
  break;
904
927
  }
905
928
  case "result": {
@@ -911,10 +934,14 @@ async function processEvents(events, context, host) {
911
934
  event,
912
935
  host,
913
936
  context,
914
- startTime
937
+ startTime,
938
+ lastAssistantUsage
915
939
  );
916
940
  retriable = resultInfo.retriable;
917
941
  resultSummary = resultInfo.resultSummary;
942
+ if (resultInfo.staleSession) {
943
+ staleSession = true;
944
+ }
918
945
  break;
919
946
  }
920
947
  case "rate_limit_event": {
@@ -927,7 +954,12 @@ async function processEvents(events, context, host) {
927
954
  if (isTyping) {
928
955
  host.connection.sendTypingStop();
929
956
  }
930
- return { retriable, resultSummary, rateLimitResetsAt };
957
+ return {
958
+ retriable,
959
+ resultSummary,
960
+ rateLimitResetsAt,
961
+ ...staleSession && { staleSession }
962
+ };
931
963
  }
932
964
 
933
965
  // src/execution/query-executor.ts
@@ -1323,7 +1355,7 @@ Your responses are sent directly to the task chat \u2014 the team sees everythin
1323
1355
  );
1324
1356
  if (!isPm || isPmActive) {
1325
1357
  parts.push(
1326
- `Use the create_pull_request tool to open PRs \u2014 do NOT use gh CLI or shell commands for PR creation.`
1358
+ `Use the mcp__conveyor__create_pull_request tool to open PRs \u2014 do NOT use gh CLI or shell commands for PR creation.`
1327
1359
  );
1328
1360
  }
1329
1361
  const modePrompt = buildModePrompt(agentMode, context);
@@ -1394,7 +1426,7 @@ Address the requested changes. Do NOT re-investigate the codebase from scratch o
1394
1426
  );
1395
1427
  } else {
1396
1428
  parts.push(
1397
- `When finished, use the create_pull_request tool to open a PR. Do NOT use gh CLI.`
1429
+ `When finished, use the mcp__conveyor__create_pull_request tool to open a PR. Do NOT use gh CLI.`
1398
1430
  );
1399
1431
  }
1400
1432
  } else {
@@ -1501,11 +1533,13 @@ function buildFreshInstructions(isPm, isAutoMode, context, agentMode) {
1501
1533
  `Your plan has been approved. Begin implementing it now.`,
1502
1534
  `Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
1503
1535
  `Start by reading the relevant source files mentioned in the plan, then write code.`,
1504
- `When finished, commit and push your changes, then use the create_pull_request tool to open a PR. Do NOT use gh CLI.`,
1505
- ...isAutoMode ? [
1506
- `
1507
- IMPORTANT: You are in Auto mode. You MUST create a pull request using create_pull_request before finishing. Do NOT declare the task complete or go idle without opening a PR. If you are blocked from creating a PR, explain what is blocking you.`
1508
- ] : []
1536
+ `When finished, commit and push your changes, then use the mcp__conveyor__create_pull_request tool to open a PR. Do NOT use gh CLI.`,
1537
+ `
1538
+ CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or go idle without making code changes.`,
1539
+ `Your FIRST action must be reading source files from the plan, then immediately writing code.`,
1540
+ `Do NOT summarize the plan or say "ready to implement" \u2014 start implementing.`,
1541
+ `When all changes are committed and pushed, use mcp__conveyor__create_pull_request to open a PR.`,
1542
+ `If you are genuinely blocked, explain the specific blocker \u2014 do not go idle silently.`
1509
1543
  ];
1510
1544
  }
1511
1545
  if (isAutoMode && isPm) {
@@ -1533,13 +1567,23 @@ IMPORTANT: You are in Auto mode. You MUST create a pull request using create_pul
1533
1567
  `When you finish planning, save the plan with update_task and end your turn. Your reply summarizing the plan will be visible in chat. A separate task agent will execute the plan after review.`
1534
1568
  ];
1535
1569
  }
1536
- return [
1570
+ const parts = [
1537
1571
  `Begin executing the task plan above immediately.`,
1538
1572
  `Your FIRST action should be reading the relevant source files mentioned in the plan, then writing code. Do NOT run install, build, lint, test, or dev server commands first \u2014 the environment is already set up.`,
1539
1573
  `Work on the git branch "${context.githubBranch}". Stay on this branch for the entire task. Do not checkout or create other branches.`,
1540
1574
  `Your replies are visible to the team in chat \u2014 briefly describe what you're doing when you begin meaningful implementation, and again when the PR is ready.`,
1541
- `When finished, commit your changes, then run \`git fetch origin ${context.githubBranch}\` and \`git push origin ${context.githubBranch}\` (use --force-with-lease if push fails). Then use the create_pull_request tool to open a PR. Do NOT use gh CLI or any other method to create PRs.`
1575
+ `When finished, commit your changes, then run \`git fetch origin ${context.githubBranch}\` and \`git push origin ${context.githubBranch}\` (use --force-with-lease if push fails). Then use the mcp__conveyor__create_pull_request tool to open a PR. Do NOT use gh CLI or any other method to create PRs.`
1542
1576
  ];
1577
+ if (isAutoMode) {
1578
+ parts.push(
1579
+ `
1580
+ CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or go idle without making code changes.`,
1581
+ `Do NOT summarize the plan or say "ready to implement" \u2014 start implementing immediately.`,
1582
+ `When all changes are committed and pushed, you MUST use mcp__conveyor__create_pull_request to open a PR before finishing.`,
1583
+ `If you are genuinely blocked, explain the specific blocker \u2014 do not go idle silently.`
1584
+ );
1585
+ }
1586
+ return parts;
1543
1587
  }
1544
1588
  function buildFeedbackInstructions(context, isPm) {
1545
1589
  const lastAgentIdx = findLastAgentMessageIndex2(context.chatHistory);
@@ -1572,7 +1616,7 @@ Address the requested changes directly. Do NOT re-investigate the codebase from
1572
1616
  );
1573
1617
  } else {
1574
1618
  parts.push(
1575
- `When finished, use the create_pull_request tool to open a PR. Do NOT use gh CLI or any other method to create PRs.`
1619
+ `When finished, use the mcp__conveyor__create_pull_request tool to open a PR. Do NOT use gh CLI or any other method to create PRs.`
1576
1620
  );
1577
1621
  }
1578
1622
  return parts;
@@ -1884,6 +1928,30 @@ function buildGetTaskIncidentsTool(connection) {
1884
1928
  { annotations: { readOnlyHint: true } }
1885
1929
  );
1886
1930
  }
1931
+ function buildCreatePullRequestTool(connection) {
1932
+ return tool(
1933
+ "create_pull_request",
1934
+ "Create a GitHub pull request for this task. Use this instead of gh CLI or git commands to create PRs.",
1935
+ {
1936
+ title: z.string().describe("The PR title"),
1937
+ body: z.string().describe("The PR description/body in markdown")
1938
+ },
1939
+ async ({ title, body }) => {
1940
+ try {
1941
+ const result = await connection.createPR({ title, body });
1942
+ connection.sendEvent({
1943
+ type: "pr_created",
1944
+ url: result.url,
1945
+ number: result.number
1946
+ });
1947
+ return textResult(`Pull request #${result.number} created: ${result.url}`);
1948
+ } catch (error) {
1949
+ const msg = error instanceof Error ? error.message : "Unknown error";
1950
+ return textResult(`Failed to create pull request: ${msg}`);
1951
+ }
1952
+ }
1953
+ );
1954
+ }
1887
1955
  function buildCommonTools(connection, config) {
1888
1956
  return [
1889
1957
  buildReadTaskChatTool(connection),
@@ -1895,7 +1963,8 @@ function buildCommonTools(connection, config) {
1895
1963
  buildListTaskFilesTool(connection),
1896
1964
  buildGetTaskFileTool(connection),
1897
1965
  buildSearchIncidentsTool(connection),
1898
- buildGetTaskIncidentsTool(connection)
1966
+ buildGetTaskIncidentsTool(connection),
1967
+ buildCreatePullRequestTool(connection)
1899
1968
  ];
1900
1969
  }
1901
1970
 
@@ -2063,42 +2132,12 @@ function buildPackTools(connection) {
2063
2132
  ];
2064
2133
  }
2065
2134
 
2066
- // src/tools/task-tools.ts
2135
+ // src/tools/discovery-tools.ts
2067
2136
  import { tool as tool3 } from "@anthropic-ai/claude-agent-sdk";
2068
2137
  import { z as z3 } from "zod";
2069
- function buildTaskTools(connection) {
2070
- return [
2071
- tool3(
2072
- "create_pull_request",
2073
- "Create a GitHub pull request for this task. Use this instead of gh CLI or git commands to create PRs.",
2074
- {
2075
- title: z3.string().describe("The PR title"),
2076
- body: z3.string().describe("The PR description/body in markdown")
2077
- },
2078
- async ({ title, body }) => {
2079
- try {
2080
- const result = await connection.createPR({ title, body });
2081
- connection.sendEvent({
2082
- type: "pr_created",
2083
- url: result.url,
2084
- number: result.number
2085
- });
2086
- return textResult(`Pull request #${result.number} created: ${result.url}`);
2087
- } catch (error) {
2088
- const msg = error instanceof Error ? error.message : "Unknown error";
2089
- return textResult(`Failed to create pull request: ${msg}`);
2090
- }
2091
- }
2092
- )
2093
- ];
2094
- }
2095
-
2096
- // src/tools/discovery-tools.ts
2097
- import { tool as tool4 } from "@anthropic-ai/claude-agent-sdk";
2098
- import { z as z4 } from "zod";
2099
2138
  function buildIconTools(connection) {
2100
2139
  return [
2101
- tool4(
2140
+ tool3(
2102
2141
  "list_icons",
2103
2142
  "List available icons (default library + user-created). Returns icon IDs, names, and whether they're defaults. Call this FIRST before set_task_icon to check for existing matches.",
2104
2143
  {},
@@ -2114,11 +2153,11 @@ function buildIconTools(connection) {
2114
2153
  },
2115
2154
  { annotations: { readOnlyHint: true } }
2116
2155
  ),
2117
- tool4(
2156
+ tool3(
2118
2157
  "set_task_icon",
2119
2158
  "Assign an existing icon to this task by its ID. Use list_icons first to find a matching icon.",
2120
2159
  {
2121
- iconId: z4.string().describe("The icon ID to assign")
2160
+ iconId: z3.string().describe("The icon ID to assign")
2122
2161
  },
2123
2162
  async ({ iconId }) => {
2124
2163
  try {
@@ -2131,14 +2170,14 @@ function buildIconTools(connection) {
2131
2170
  }
2132
2171
  }
2133
2172
  ),
2134
- tool4(
2173
+ tool3(
2135
2174
  "generate_task_icon",
2136
2175
  "Generate a new SVG icon using AI and assign it to this task. Only use if no existing icon from list_icons is a good fit. Provide a concise visual description.",
2137
2176
  {
2138
- prompt: z4.string().describe(
2177
+ prompt: z3.string().describe(
2139
2178
  "Description of the icon to generate (e.g. 'minimal flat gear and wrench icon')"
2140
2179
  ),
2141
- aspectRatio: z4.enum(["auto", "portrait", "landscape", "square"]).optional().describe("Icon aspect ratio, defaults to square")
2180
+ aspectRatio: z3.enum(["auto", "portrait", "landscape", "square"]).optional().describe("Icon aspect ratio, defaults to square")
2142
2181
  },
2143
2182
  async ({ prompt, aspectRatio }) => {
2144
2183
  try {
@@ -2156,10 +2195,10 @@ function buildIconTools(connection) {
2156
2195
  function buildDiscoveryTools(connection, context) {
2157
2196
  const spDescription = buildStoryPointDescription(context?.storyPoints);
2158
2197
  return [
2159
- tool4(
2198
+ tool3(
2160
2199
  "set_story_points",
2161
2200
  "Set the story point estimate for this task. Use after understanding the scope of the work.",
2162
- { value: z4.number().describe(spDescription) },
2201
+ { value: z3.number().describe(spDescription) },
2163
2202
  async ({ value }) => {
2164
2203
  try {
2165
2204
  await Promise.resolve(connection.updateTaskProperties({ storyPointValue: value }));
@@ -2171,11 +2210,11 @@ function buildDiscoveryTools(connection, context) {
2171
2210
  }
2172
2211
  }
2173
2212
  ),
2174
- tool4(
2213
+ tool3(
2175
2214
  "set_task_tags",
2176
2215
  "Assign tags to this task from the project's available tags. Use the tag IDs from the project tags list.",
2177
2216
  {
2178
- tagIds: z4.array(z4.string()).describe("Array of tag IDs to assign")
2217
+ tagIds: z3.array(z3.string()).describe("Array of tag IDs to assign")
2179
2218
  },
2180
2219
  async ({ tagIds }) => {
2181
2220
  try {
@@ -2188,11 +2227,11 @@ function buildDiscoveryTools(connection, context) {
2188
2227
  }
2189
2228
  }
2190
2229
  ),
2191
- tool4(
2230
+ tool3(
2192
2231
  "set_task_title",
2193
2232
  "Update the task title to better reflect the planned work.",
2194
2233
  {
2195
- title: z4.string().describe("The new task title")
2234
+ title: z3.string().describe("The new task title")
2196
2235
  },
2197
2236
  async ({ title }) => {
2198
2237
  try {
@@ -2219,10 +2258,7 @@ function imageBlock(data, mimeType) {
2219
2258
  function getModeTools(agentMode, connection, config, context) {
2220
2259
  switch (agentMode) {
2221
2260
  case "building":
2222
- return context?.isParentTask ? [
2223
- ...buildTaskTools(connection),
2224
- ...buildPmTools(connection, context?.storyPoints, { includePackTools: true })
2225
- ] : buildTaskTools(connection);
2261
+ return context?.isParentTask ? buildPmTools(connection, context?.storyPoints, { includePackTools: true }) : [];
2226
2262
  case "review":
2227
2263
  case "auto":
2228
2264
  case "discovery":
@@ -2231,7 +2267,7 @@ function getModeTools(agentMode, connection, config, context) {
2231
2267
  includePackTools: !!context?.isParentTask
2232
2268
  });
2233
2269
  default:
2234
- return config.mode === "pm" ? buildPmTools(connection, context?.storyPoints, { includePackTools: false }) : buildTaskTools(connection);
2270
+ return config.mode === "pm" ? buildPmTools(connection, context?.storyPoints, { includePackTools: false }) : [];
2235
2271
  }
2236
2272
  }
2237
2273
  function createConveyorMcpServer(connection, config, context, agentMode) {
@@ -2684,16 +2720,15 @@ async function runWithRetry(initialQuery, context, host, options) {
2684
2720
  if (host.isStopped()) return;
2685
2721
  const agentQuery = attempt === 0 ? initialQuery : buildRetryQuery(host, context, options, lastErrorWasImage);
2686
2722
  try {
2687
- const { retriable, resultSummary, modeRestart, rateLimitResetsAt } = await processEvents(
2688
- agentQuery,
2689
- context,
2690
- host
2691
- );
2723
+ const { retriable, resultSummary, modeRestart, rateLimitResetsAt, staleSession } = await processEvents(agentQuery, context, host);
2692
2724
  if (modeRestart || host.isStopped()) return;
2693
2725
  if (rateLimitResetsAt) {
2694
2726
  handleRateLimitPause(host, rateLimitResetsAt);
2695
2727
  return;
2696
2728
  }
2729
+ if (staleSession && context.claudeSessionId) {
2730
+ return handleStaleSession(context, host, options);
2731
+ }
2697
2732
  if (!retriable) return;
2698
2733
  lastErrorWasImage = IMAGE_ERROR_PATTERN2.test(resultSummary ?? "");
2699
2734
  } catch (error) {
@@ -2913,39 +2948,6 @@ async function runSetupSafe(runnerConfig, connection, callbacks, setupLog, _effe
2913
2948
  if (!await checkoutTaskBranch(runnerConfig, connection, callbacks, setupLog)) {
2914
2949
  return { ok: false, deferredStartConfig: null };
2915
2950
  }
2916
- const taskBranch = process.env.CONVEYOR_TASK_BRANCH;
2917
- if (taskBranch) {
2918
- pushSetupLog(setupLog, `[conveyor] Switching to task branch ${taskBranch}...`);
2919
- connection.sendEvent({
2920
- type: "setup_output",
2921
- stream: "stdout",
2922
- data: `Switching to task branch ${taskBranch}...
2923
- `
2924
- });
2925
- try {
2926
- await runSetupCommand(
2927
- `git fetch origin ${taskBranch} && git checkout ${taskBranch}`,
2928
- runnerConfig.workspaceDir,
2929
- (stream, data) => {
2930
- connection.sendEvent({ type: "setup_output", stream, data });
2931
- for (const line of data.split("\n").filter(Boolean)) {
2932
- pushSetupLog(setupLog, `[${stream}] ${line}`);
2933
- }
2934
- }
2935
- );
2936
- pushSetupLog(setupLog, `[conveyor] Switched to ${taskBranch}`);
2937
- } catch (error) {
2938
- const message = `Failed to checkout ${taskBranch}: ${error instanceof Error ? error.message : "unknown error"}`;
2939
- connection.sendEvent({ type: "setup_error", message });
2940
- await callbacks.onEvent({ type: "setup_error", message });
2941
- connection.postChatMessage(
2942
- `Failed to switch to task branch \`${taskBranch}\`.
2943
-
2944
- ${message}`
2945
- );
2946
- return { ok: false, deferredStartConfig: null };
2947
- }
2948
- }
2949
2951
  const config = await loadConveyorConfig(runnerConfig.workspaceDir);
2950
2952
  if (!config) {
2951
2953
  connection.sendEvent({ type: "setup_complete" });
@@ -3138,7 +3140,7 @@ var AgentRunner = class {
3138
3140
  sessionIds = /* @__PURE__ */ new Map();
3139
3141
  lastQueryModeRestart = false;
3140
3142
  startCommandStarted = false;
3141
- nudgedForPR = false;
3143
+ prNudgeCount = 0;
3142
3144
  idleTimer = null;
3143
3145
  idleCheckInterval = null;
3144
3146
  deferredStartConfig = null;
@@ -3283,27 +3285,38 @@ var AgentRunner = class {
3283
3285
  } catch {
3284
3286
  }
3285
3287
  }
3288
+ needsPRNudge() {
3289
+ if (!this.config.isAuto || this.stopped) return false;
3290
+ const maxNudges = this.taskContext?.agentSettings?.maxPrNudges ?? this.config.agentSettings?.maxPrNudges ?? 3;
3291
+ if (this.prNudgeCount >= maxNudges) return false;
3292
+ const status = this.taskContext?.status;
3293
+ return status === "InProgress" && !this.taskContext?.githubPRUrl;
3294
+ }
3286
3295
  async maybeSendPRNudge() {
3287
- if (!this.config.isAuto || this.nudgedForPR || this.stopped) return false;
3288
3296
  const fresh = await this.connection.fetchTaskContext().catch(() => null);
3289
3297
  if (fresh) this.taskContext = fresh;
3290
- const status = this.taskContext?.status;
3291
- const hasPR = !!this.taskContext?.githubPRUrl;
3292
- if (status === "InProgress" && !hasPR) {
3293
- this.nudgedForPR = true;
3298
+ if (!this.needsPRNudge()) return false;
3299
+ this.prNudgeCount++;
3300
+ const ctx = this.taskContext ?? fresh;
3301
+ if (!ctx) return false;
3302
+ const maxNudges = this.taskContext?.agentSettings?.maxPrNudges ?? this.config.agentSettings?.maxPrNudges ?? 3;
3303
+ if (this.prNudgeCount >= maxNudges) {
3294
3304
  this.connection.postChatMessage(
3295
- "Auto-nudge: Task is still In Progress with no PR. Prompting agent to continue..."
3296
- );
3297
- await this.setState("running");
3298
- const ctx = this.taskContext ?? fresh;
3299
- if (!ctx) return false;
3300
- await this.runQuerySafe(
3301
- ctx,
3302
- "You are in Auto mode and your task is still In Progress with no pull request. You MUST create a pull request before finishing. Use the create_pull_request tool now to open a PR for your changes. If you are blocked, explain what is preventing you from creating a PR."
3305
+ `Auto-mode agent failed to open a PR after ${maxNudges} nudges. Shutting down.`
3303
3306
  );
3307
+ this.stop();
3304
3308
  return true;
3305
3309
  }
3306
- return false;
3310
+ await this.sendPRNudgeQuery(ctx, maxNudges);
3311
+ return true;
3312
+ }
3313
+ async sendPRNudgeQuery(ctx, maxNudges) {
3314
+ const isFirst = this.prNudgeCount === 1;
3315
+ const chatMsg = isFirst ? "Auto-nudge: Task is still In Progress with no PR. Prompting agent to continue..." : `Auto-nudge (attempt ${this.prNudgeCount}/${maxNudges}): Task still has no PR. Prompting agent again...`;
3316
+ const prompt = isFirst ? "You are in Auto mode and your task is still In Progress with no pull request. You MUST create a pull request before finishing. Use the create_pull_request tool now to open a PR for your changes. If you are blocked, explain what is preventing you from creating a PR." : `This is reminder ${this.prNudgeCount} of ${maxNudges}. You MUST open a pull request using create_pull_request NOW or explain what is blocking you. Do NOT go idle \u2014 either create the PR or describe the specific blocker.`;
3317
+ this.connection.postChatMessage(chatMsg);
3318
+ await this.setState("running");
3319
+ await this.runQuerySafe(ctx, prompt);
3307
3320
  }
3308
3321
  async executeInitialMode() {
3309
3322
  if (!this.taskContext) return;
@@ -3723,7 +3736,7 @@ var logger4 = createServiceLogger("ProjectRunner");
3723
3736
  var __filename = fileURLToPath(import.meta.url);
3724
3737
  var __dirname = path.dirname(__filename);
3725
3738
  var HEARTBEAT_INTERVAL_MS2 = 3e4;
3726
- var MAX_CONCURRENT = 5;
3739
+ var MAX_CONCURRENT = Math.max(1, parseInt(process.env.CONVEYOR_MAX_CONCURRENT ?? "10", 10) || 10);
3727
3740
  var STOP_TIMEOUT_MS = 3e4;
3728
3741
  function setupWorkDir(projectDir, assignment) {
3729
3742
  const { taskId, branch, devBranch, useWorktree } = assignment;
@@ -3806,7 +3819,7 @@ var ProjectRunner = class {
3806
3819
  projectId: config.projectId
3807
3820
  });
3808
3821
  }
3809
- async checkoutWorkspaceBranch() {
3822
+ checkoutWorkspaceBranch() {
3810
3823
  const workspaceBranch = process.env.CONVEYOR_WORKSPACE_BRANCH;
3811
3824
  if (!workspaceBranch) return;
3812
3825
  try {
@@ -3821,7 +3834,7 @@ var ProjectRunner = class {
3821
3834
  }
3822
3835
  }
3823
3836
  async start() {
3824
- await this.checkoutWorkspaceBranch();
3837
+ this.checkoutWorkspaceBranch();
3825
3838
  await this.connection.connect();
3826
3839
  this.connection.onTaskAssignment((assignment) => {
3827
3840
  this.handleAssignment(assignment);
@@ -4036,4 +4049,4 @@ export {
4036
4049
  ProjectRunner,
4037
4050
  FileCache
4038
4051
  };
4039
- //# sourceMappingURL=chunk-4SXL45ZH.js.map
4052
+ //# sourceMappingURL=chunk-DMWUXQ4M.js.map