@rallycry/conveyor-agent 4.0.0 → 4.1.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.
@@ -82,13 +82,13 @@ var ConveyorConnection = class _ConveyorConnection {
82
82
  });
83
83
  });
84
84
  }
85
- fetchChatMessages(limit) {
85
+ fetchChatMessages(limit, taskId) {
86
86
  const socket = this.socket;
87
87
  if (!socket) throw new Error("Not connected");
88
88
  return new Promise((resolve2, reject) => {
89
89
  socket.emit(
90
90
  "agentRunner:getChatMessages",
91
- { limit },
91
+ { limit, taskId },
92
92
  (response) => {
93
93
  if (response.success && response.data) {
94
94
  resolve2(response.data);
@@ -339,6 +339,48 @@ var ConveyorConnection = class _ConveyorConnection {
339
339
  );
340
340
  });
341
341
  }
342
+ postChildChatMessage(childTaskId, content) {
343
+ const socket = this.socket;
344
+ if (!socket) throw new Error("Not connected");
345
+ return new Promise((resolve2, reject) => {
346
+ socket.emit(
347
+ "agentRunner:postChildChatMessage",
348
+ { childTaskId, content },
349
+ (response) => {
350
+ if (response.success) resolve2();
351
+ else reject(new Error(response.error ?? "Failed to post to child chat"));
352
+ }
353
+ );
354
+ });
355
+ }
356
+ updateChildStatus(childTaskId, status) {
357
+ const socket = this.socket;
358
+ if (!socket) throw new Error("Not connected");
359
+ return new Promise((resolve2, reject) => {
360
+ socket.emit(
361
+ "agentRunner:updateChildStatus",
362
+ { childTaskId, status },
363
+ (response) => {
364
+ if (response.success) resolve2();
365
+ else reject(new Error(response.error ?? "Failed to update child status"));
366
+ }
367
+ );
368
+ });
369
+ }
370
+ stopChildBuild(childTaskId) {
371
+ const socket = this.socket;
372
+ if (!socket) throw new Error("Not connected");
373
+ return new Promise((resolve2, reject) => {
374
+ socket.emit(
375
+ "agentRunner:stopChildBuild",
376
+ { childTaskId },
377
+ (response) => {
378
+ if (response.success) resolve2();
379
+ else reject(new Error(response.error ?? "Failed to stop child build"));
380
+ }
381
+ );
382
+ });
383
+ }
342
384
  fetchTask(slugOrId) {
343
385
  const socket = this.socket;
344
386
  if (!socket) throw new Error("Not connected");
@@ -1219,7 +1261,12 @@ function buildModePrompt(agentMode, context) {
1219
1261
  `- You can create and manage subtasks`,
1220
1262
  `- You cannot write code or edit files (except .claude/plans/)`,
1221
1263
  `- Goal: collaborate with the user to create a clear plan`,
1222
- `- Proactively fill task properties (SP, tags, icon) as the plan takes shape`
1264
+ `- Proactively fill task properties (SP, tags, icon) as the plan takes shape`,
1265
+ ``,
1266
+ `### Self-Update vs Subtasks`,
1267
+ `- If the work fits in a single task (1-3 SP), update YOUR OWN plan and properties \u2014 do not create subtasks`,
1268
+ `- Only create subtasks when the work genuinely requires multiple independent pieces (e.g., Pack-tier work, 8+ SP)`,
1269
+ `- When planning for yourself: use update_task to save the plan, then set_story_points, set_task_title, set_task_icon`
1223
1270
  ];
1224
1271
  if (context) {
1225
1272
  parts.push(...buildPropertyInstructions(context));
@@ -1585,13 +1632,14 @@ function buildCommonTools(connection, config) {
1585
1632
  return [
1586
1633
  tool(
1587
1634
  "read_task_chat",
1588
- "Read recent messages from the task chat to see team feedback or instructions",
1635
+ "Read recent messages from a task chat. Omit task_id to read the current task's chat, or provide a child task ID to read a child's chat.",
1589
1636
  {
1590
- limit: z.number().optional().describe("Number of recent messages to fetch (default 20)")
1637
+ limit: z.number().optional().describe("Number of recent messages to fetch (default 20)"),
1638
+ task_id: z.string().optional().describe("Child task ID to read chat from. Omit to read the current task's chat.")
1591
1639
  },
1592
- async ({ limit }) => {
1640
+ async ({ limit, task_id }) => {
1593
1641
  try {
1594
- const messages = await connection.fetchChatMessages(limit);
1642
+ const messages = await connection.fetchChatMessages(limit, task_id);
1595
1643
  return textResult(JSON.stringify(messages, null, 2));
1596
1644
  } catch {
1597
1645
  return textResult(
@@ -1605,22 +1653,46 @@ function buildCommonTools(connection, config) {
1605
1653
  ),
1606
1654
  tool(
1607
1655
  "post_to_chat",
1608
- "Post a message to the task chat. Your normal replies already appear in chat \u2014 only use this for explicit out-of-band updates or posting to a different task's chat",
1609
- { message: z.string().describe("The message to post to the team") },
1610
- ({ message }) => {
1611
- connection.postChatMessage(message);
1612
- return Promise.resolve(textResult("Message posted to task chat."));
1656
+ "Post a message to a task chat. Your normal replies already appear in chat \u2014 only use this for explicit out-of-band updates or posting to a child task's chat.",
1657
+ {
1658
+ message: z.string().describe("The message to post to the team"),
1659
+ task_id: z.string().optional().describe("Child task ID to post to. Omit to post to the current task's chat.")
1660
+ },
1661
+ async ({ message, task_id }) => {
1662
+ try {
1663
+ if (task_id) {
1664
+ await connection.postChildChatMessage(task_id, message);
1665
+ return textResult(`Message posted to child task ${task_id} chat.`);
1666
+ }
1667
+ connection.postChatMessage(message);
1668
+ return textResult("Message posted to task chat.");
1669
+ } catch (error) {
1670
+ return textResult(
1671
+ `Failed to post message: ${error instanceof Error ? error.message : "Unknown error"}`
1672
+ );
1673
+ }
1613
1674
  }
1614
1675
  ),
1615
1676
  tool(
1616
1677
  "update_task_status",
1617
- "Update the task status on the Kanban board",
1678
+ "Update a task's status on the Kanban board. Omit task_id to update the current task, or provide a child task ID to update a child's status.",
1618
1679
  {
1619
- status: z.enum(["InProgress", "ReviewPR", "Complete"]).describe("The new status for the task")
1680
+ status: z.enum(["InProgress", "ReviewPR", "ReviewDev", "Complete"]).describe("The new status for the task"),
1681
+ task_id: z.string().optional().describe("Child task ID to update. Omit to update the current task.")
1620
1682
  },
1621
- ({ status }) => {
1622
- connection.updateStatus(status);
1623
- return Promise.resolve(textResult(`Task status updated to ${status}.`));
1683
+ async ({ status, task_id }) => {
1684
+ try {
1685
+ if (task_id) {
1686
+ await connection.updateChildStatus(task_id, status);
1687
+ return textResult(`Child task ${task_id} status updated to ${status}.`);
1688
+ }
1689
+ connection.updateStatus(status);
1690
+ return textResult(`Task status updated to ${status}.`);
1691
+ } catch (error) {
1692
+ return textResult(
1693
+ `Failed to update status: ${error instanceof Error ? error.message : "Unknown error"}`
1694
+ );
1695
+ }
1624
1696
  }
1625
1697
  ),
1626
1698
  tool(
@@ -1851,6 +1923,23 @@ function buildPmTools(connection, storyPoints, options) {
1851
1923
  }
1852
1924
  }
1853
1925
  ),
1926
+ tool2(
1927
+ "stop_child_build",
1928
+ "Stop a running cloud build for a child task. Sends a stop signal to the child agent.",
1929
+ {
1930
+ childTaskId: z2.string().describe("The child task ID whose build should be stopped")
1931
+ },
1932
+ async ({ childTaskId }) => {
1933
+ try {
1934
+ await connection.stopChildBuild(childTaskId);
1935
+ return textResult(`Stop signal sent to child task: ${childTaskId}`);
1936
+ } catch (error) {
1937
+ return textResult(
1938
+ `Failed to stop child build: ${error instanceof Error ? error.message : "Unknown error"}`
1939
+ );
1940
+ }
1941
+ }
1942
+ ),
1854
1943
  tool2(
1855
1944
  "approve_and_merge_pr",
1856
1945
  "Approve and merge a child task's pull request. Only succeeds if all CI/CD checks are passing. Returns an error if checks are pending (retry after waiting) or failed (investigate). The child task must be in ReviewPR status.",
@@ -2027,7 +2116,10 @@ function createConveyorMcpServer(connection, config, context) {
2027
2116
  let modeTools;
2028
2117
  switch (agentMode) {
2029
2118
  case "building":
2030
- modeTools = buildTaskTools(connection);
2119
+ modeTools = context?.isParentTask ? [
2120
+ ...buildTaskTools(connection),
2121
+ ...buildPmTools(connection, context?.storyPoints, { includePackTools: true })
2122
+ ] : buildTaskTools(connection);
2031
2123
  break;
2032
2124
  case "review":
2033
2125
  modeTools = buildPmTools(connection, context?.storyPoints, {
@@ -2035,9 +2127,15 @@ function createConveyorMcpServer(connection, config, context) {
2035
2127
  });
2036
2128
  break;
2037
2129
  case "auto":
2130
+ modeTools = buildPmTools(connection, context?.storyPoints, {
2131
+ includePackTools: !!context?.isParentTask
2132
+ });
2133
+ break;
2038
2134
  case "discovery":
2039
2135
  case "help":
2040
- modeTools = buildPmTools(connection, context?.storyPoints, { includePackTools: false });
2136
+ modeTools = buildPmTools(connection, context?.storyPoints, {
2137
+ includePackTools: !!context?.isParentTask
2138
+ });
2041
2139
  break;
2042
2140
  default:
2043
2141
  modeTools = config.mode === "pm" ? buildPmTools(connection, context?.storyPoints, { includePackTools: false }) : buildTaskTools(connection);
@@ -2600,6 +2698,8 @@ var AgentRunner = class _AgentRunner {
2600
2698
  lastQueryModeRestart = false;
2601
2699
  deferredStartConfig = null;
2602
2700
  startCommandStarted = false;
2701
+ idleTimer = null;
2702
+ idleCheckInterval = null;
2603
2703
  static MAX_SETUP_LOG_LINES = 50;
2604
2704
  constructor(config, callbacks) {
2605
2705
  this.config = config;
@@ -2640,6 +2740,16 @@ var AgentRunner = class _AgentRunner {
2640
2740
  this.heartbeatTimer = null;
2641
2741
  }
2642
2742
  }
2743
+ clearIdleTimers() {
2744
+ if (this.idleTimer) {
2745
+ clearTimeout(this.idleTimer);
2746
+ this.idleTimer = null;
2747
+ }
2748
+ if (this.idleCheckInterval) {
2749
+ clearInterval(this.idleCheckInterval);
2750
+ this.idleCheckInterval = null;
2751
+ }
2752
+ }
2643
2753
  async start() {
2644
2754
  await this.setState("connecting");
2645
2755
  await this.connection.connect();
@@ -3000,17 +3110,17 @@ ${f.content}
3000
3110
  }
3001
3111
  }
3002
3112
  waitForMessage() {
3113
+ this.clearIdleTimers();
3003
3114
  return new Promise((resolve2) => {
3004
- const checkStopped = setInterval(() => {
3115
+ this.idleCheckInterval = setInterval(() => {
3005
3116
  if (this.stopped) {
3006
- clearInterval(checkStopped);
3007
- clearTimeout(idleTimer);
3117
+ this.clearIdleTimers();
3008
3118
  this.inputResolver = null;
3009
3119
  resolve2(null);
3010
3120
  }
3011
3121
  }, 1e3);
3012
- const idleTimer = setTimeout(() => {
3013
- clearInterval(checkStopped);
3122
+ this.idleTimer = setTimeout(() => {
3123
+ this.clearIdleTimers();
3014
3124
  this.inputResolver = null;
3015
3125
  console.log(
3016
3126
  `[conveyor-agent] Idle for ${IDLE_TIMEOUT_MS / 6e4} minutes \u2014 shutting down.`
@@ -3021,8 +3131,7 @@ ${f.content}
3021
3131
  resolve2(null);
3022
3132
  }, IDLE_TIMEOUT_MS);
3023
3133
  this.inputResolver = (msg) => {
3024
- clearInterval(checkStopped);
3025
- clearTimeout(idleTimer);
3134
+ this.clearIdleTimers();
3026
3135
  resolve2(msg);
3027
3136
  };
3028
3137
  });
@@ -3048,19 +3157,23 @@ ${f.content}
3048
3157
  parent_tool_use_id: null
3049
3158
  });
3050
3159
  yield makeUserMessage(initialPrompt);
3051
- while (!this.stopped) {
3052
- if (this.pendingMessages.length > 0) {
3053
- const next = this.pendingMessages.shift();
3054
- if (next) yield next;
3055
- continue;
3160
+ try {
3161
+ while (!this.stopped) {
3162
+ if (this.pendingMessages.length > 0) {
3163
+ const next = this.pendingMessages.shift();
3164
+ if (next) yield next;
3165
+ continue;
3166
+ }
3167
+ this.connection.emitStatus("waiting_for_input");
3168
+ await this.callbacks.onStatusChange("waiting_for_input");
3169
+ const msg = await this.waitForMessage();
3170
+ if (!msg) break;
3171
+ this.connection.emitStatus("running");
3172
+ await this.callbacks.onStatusChange("running");
3173
+ yield msg;
3056
3174
  }
3057
- this.connection.emitStatus("waiting_for_input");
3058
- await this.callbacks.onStatusChange("waiting_for_input");
3059
- const msg = await this.waitForMessage();
3060
- if (!msg) break;
3061
- this.connection.emitStatus("running");
3062
- await this.callbacks.onStatusChange("running");
3063
- yield msg;
3175
+ } finally {
3176
+ this.clearIdleTimers();
3064
3177
  }
3065
3178
  }
3066
3179
  asQueryHost() {
@@ -3121,6 +3234,7 @@ ${f.content}
3121
3234
  }
3122
3235
  stop() {
3123
3236
  this.stopped = true;
3237
+ this.clearIdleTimers();
3124
3238
  if (this.inputResolver) {
3125
3239
  this.inputResolver(null);
3126
3240
  this.inputResolver = null;
@@ -3553,4 +3667,4 @@ export {
3553
3667
  ProjectRunner,
3554
3668
  FileCache
3555
3669
  };
3556
- //# sourceMappingURL=chunk-Y4TAVPZV.js.map
3670
+ //# sourceMappingURL=chunk-BBGSX2AX.js.map