@rallycry/conveyor-agent 3.3.3 → 3.5.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.
- package/dist/{chunk-PLQELWUW.js → chunk-6DVMC6EU.js} +205 -16
- package/dist/chunk-6DVMC6EU.js.map +1 -0
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-PLQELWUW.js.map +0 -1
|
@@ -299,6 +299,37 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
299
299
|
)
|
|
300
300
|
]);
|
|
301
301
|
}
|
|
302
|
+
startChildCloudBuild(childTaskId) {
|
|
303
|
+
const socket = this.socket;
|
|
304
|
+
if (!socket) throw new Error("Not connected");
|
|
305
|
+
return new Promise((resolve2, reject) => {
|
|
306
|
+
socket.emit(
|
|
307
|
+
"agentRunner:startChildCloudBuild",
|
|
308
|
+
{ childTaskId },
|
|
309
|
+
(response) => {
|
|
310
|
+
if (response.success && response.data) resolve2(response.data);
|
|
311
|
+
else reject(new Error(response.error ?? "Failed to start child cloud build"));
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
approveAndMergePR(childTaskId) {
|
|
317
|
+
const socket = this.socket;
|
|
318
|
+
if (!socket) throw new Error("Not connected");
|
|
319
|
+
return new Promise((resolve2, reject) => {
|
|
320
|
+
socket.emit(
|
|
321
|
+
"agentRunner:approveAndMergePR",
|
|
322
|
+
{ childTaskId },
|
|
323
|
+
(response) => {
|
|
324
|
+
if (response.success && response.data) {
|
|
325
|
+
resolve2(response.data);
|
|
326
|
+
} else {
|
|
327
|
+
reject(new Error(response.error ?? "Failed to approve and merge PR"));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
302
333
|
fetchTask(slugOrId) {
|
|
303
334
|
const socket = this.socket;
|
|
304
335
|
if (!socket) throw new Error("Not connected");
|
|
@@ -1051,17 +1082,113 @@ Address the requested changes directly. Do NOT re-investigate the codebase from
|
|
|
1051
1082
|
}
|
|
1052
1083
|
return parts;
|
|
1053
1084
|
}
|
|
1054
|
-
function
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1085
|
+
function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
1086
|
+
const parts = [
|
|
1087
|
+
`You are an autonomous Pack Runner managing child tasks for the "${context.title}" project.`,
|
|
1088
|
+
`You are running locally with full access to the repository and task management tools.`,
|
|
1089
|
+
`Your job is to sequentially execute child tasks by firing cloud builds, reviewing their PRs, and merging them.`,
|
|
1090
|
+
``,
|
|
1091
|
+
`## Autonomous Loop`,
|
|
1092
|
+
`Follow this loop continuously until all children are complete:`,
|
|
1093
|
+
`1. Call list_subtasks to see the current state of all child tasks.`,
|
|
1094
|
+
`2. Find the next child in "Open" status (by ordinal order).`,
|
|
1095
|
+
`3. Fire its cloud build with start_child_cloud_build.`,
|
|
1096
|
+
`4. Report to chat which task you fired, then state you are going idle to wait.`,
|
|
1097
|
+
`5. When you are relaunched with a notification that a child completed:`,
|
|
1098
|
+
` - If CI/CD is passing: review the PR (use get_task to read the child, check quality), then call approve_and_merge_pr.`,
|
|
1099
|
+
` - If CI/CD is failing: post guidance to the child's chat with post_to_chat, or notify the team if stuck.`,
|
|
1100
|
+
`6. After merge: loop back to step 1.`,
|
|
1101
|
+
`7. When ALL children are complete: do a final review, summarize results in chat, and mark this parent task complete with update_task_status.`,
|
|
1102
|
+
``,
|
|
1103
|
+
`## Important Rules`,
|
|
1104
|
+
`- Process children ONE at a time, in ordinal order.`,
|
|
1105
|
+
`- After firing a child build, explicitly state you are going idle. The system will disconnect you until the child completes.`,
|
|
1106
|
+
`- Do NOT attempt to write code yourself. Your role is coordination only.`,
|
|
1107
|
+
`- If a child task is missing story points or an assigned agent, notify the team in chat and wait.`,
|
|
1108
|
+
`- If a child fails repeatedly, escalate to the team rather than retrying indefinitely.`,
|
|
1109
|
+
`- You can use get_task and get_task_cli to inspect child task details and logs.`,
|
|
1110
|
+
`- You can use read_task_chat to check for team messages.`
|
|
1111
|
+
];
|
|
1112
|
+
if (context.storyPoints && context.storyPoints.length > 0) {
|
|
1113
|
+
parts.push(``, `## Story Point Tiers`);
|
|
1114
|
+
for (const sp of context.storyPoints) {
|
|
1115
|
+
const desc = sp.description ? ` \u2014 ${sp.description}` : "";
|
|
1116
|
+
parts.push(`- Value ${sp.value}: "${sp.name}"${desc}`);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
if (context.projectAgents && context.projectAgents.length > 0) {
|
|
1120
|
+
parts.push(``, `## Project Agents`);
|
|
1121
|
+
for (const pa of context.projectAgents) {
|
|
1122
|
+
const role = pa.role ? `role: ${pa.role}` : "role: unassigned";
|
|
1123
|
+
const sp = pa.storyPoints != null ? `, story points: ${pa.storyPoints}` : "";
|
|
1124
|
+
parts.push(`- ${pa.agent.name} (${role}${sp})`);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
if (setupLog.length > 0) {
|
|
1128
|
+
parts.push(``, `## Environment setup log`, "```", ...setupLog, "```");
|
|
1129
|
+
}
|
|
1130
|
+
if (context.agentInstructions) {
|
|
1131
|
+
parts.push(``, `## Agent Instructions`, context.agentInstructions);
|
|
1132
|
+
}
|
|
1133
|
+
if (config.instructions) {
|
|
1134
|
+
parts.push(``, `## Additional Instructions`, config.instructions);
|
|
1135
|
+
}
|
|
1136
|
+
parts.push(
|
|
1137
|
+
``,
|
|
1138
|
+
`Your responses are sent directly to the task chat \u2014 the team sees everything you say.`,
|
|
1139
|
+
`Do NOT call the post_to_chat tool for your own task; your replies already appear in chat automatically.`,
|
|
1140
|
+
`Only use post_to_chat if you need to message a different task's chat (e.g. a child task).`,
|
|
1141
|
+
`Use read_task_chat only if you need to re-read earlier messages beyond the chat context above.`
|
|
1142
|
+
);
|
|
1143
|
+
return parts.join("\n");
|
|
1144
|
+
}
|
|
1145
|
+
function buildPackRunnerInstructions(context, scenario) {
|
|
1146
|
+
const parts = [`
|
|
1147
|
+
## Instructions`];
|
|
1148
|
+
if (scenario === "fresh") {
|
|
1149
|
+
parts.push(
|
|
1150
|
+
`You are the Pack Runner for this task and its subtasks.`,
|
|
1151
|
+
`Begin your autonomous loop immediately: call list_subtasks to assess the current state, then fire the first "Open" child task.`
|
|
1152
|
+
);
|
|
1153
|
+
} else if (scenario === "idle_relaunch") {
|
|
1154
|
+
parts.push(
|
|
1155
|
+
`You have been relaunched \u2014 a child task may have completed.`,
|
|
1156
|
+
`Call list_subtasks to check the current state of all children.`,
|
|
1157
|
+
`If a child is in "ReviewPR" status, review and merge its PR.`,
|
|
1158
|
+
`Then continue your autonomous loop with the next "Open" child.`
|
|
1159
|
+
);
|
|
1160
|
+
} else {
|
|
1161
|
+
const lastAgentIdx = findLastAgentMessageIndex(context.chatHistory);
|
|
1162
|
+
const newMessages = context.chatHistory.slice(lastAgentIdx + 1).filter((m) => m.role === "user");
|
|
1163
|
+
parts.push(
|
|
1164
|
+
`You have been relaunched with new messages.`,
|
|
1165
|
+
`
|
|
1166
|
+
New messages since your last run:`,
|
|
1167
|
+
...newMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`),
|
|
1168
|
+
`
|
|
1169
|
+
Review these messages, then continue your autonomous loop: call list_subtasks and proceed accordingly.`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
return parts;
|
|
1173
|
+
}
|
|
1174
|
+
function buildInitialPrompt(mode, context, isAuto) {
|
|
1175
|
+
const isPackRunner = mode === "pm" && !!isAuto && !!context.isParentTask;
|
|
1176
|
+
if (!isPackRunner) {
|
|
1177
|
+
const sessionRelaunch = buildRelaunchWithSession(mode, context);
|
|
1178
|
+
if (sessionRelaunch) return sessionRelaunch;
|
|
1179
|
+
}
|
|
1057
1180
|
const scenario = detectRelaunchScenario(context);
|
|
1058
1181
|
const body = buildTaskBody(context);
|
|
1059
|
-
const instructions = buildInstructions(mode, context, scenario);
|
|
1182
|
+
const instructions = isPackRunner ? buildPackRunnerInstructions(context, scenario) : buildInstructions(mode, context, scenario);
|
|
1060
1183
|
return [...body, ...instructions].join("\n");
|
|
1061
1184
|
}
|
|
1062
1185
|
function buildSystemPrompt(mode, context, config, setupLog, pmSubMode = "planning") {
|
|
1063
1186
|
const isPm = mode === "pm";
|
|
1064
1187
|
const isPmActive = isPm && pmSubMode === "active";
|
|
1188
|
+
const isPackRunner = isPm && !!config.isAuto && !!context.isParentTask;
|
|
1189
|
+
if (isPackRunner) {
|
|
1190
|
+
return buildPackRunnerSystemPrompt(context, config, setupLog);
|
|
1191
|
+
}
|
|
1065
1192
|
const pmParts = [
|
|
1066
1193
|
`You are an AI project manager helping to plan tasks for the "${context.title}" project.`,
|
|
1067
1194
|
`You are running locally with full access to the repository.`,
|
|
@@ -1368,9 +1495,10 @@ function buildStoryPointDescription(storyPoints) {
|
|
|
1368
1495
|
}
|
|
1369
1496
|
return "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
|
|
1370
1497
|
}
|
|
1371
|
-
function buildPmTools(connection, storyPoints) {
|
|
1498
|
+
function buildPmTools(connection, storyPoints, options) {
|
|
1372
1499
|
const spDescription = buildStoryPointDescription(storyPoints);
|
|
1373
|
-
|
|
1500
|
+
const includePackTools = options?.includePackTools ?? false;
|
|
1501
|
+
const tools = [
|
|
1374
1502
|
tool2(
|
|
1375
1503
|
"update_task",
|
|
1376
1504
|
"Save the finalized task plan and/or description",
|
|
@@ -1456,6 +1584,46 @@ function buildPmTools(connection, storyPoints) {
|
|
|
1456
1584
|
{ annotations: { readOnlyHint: true } }
|
|
1457
1585
|
)
|
|
1458
1586
|
];
|
|
1587
|
+
if (!includePackTools) return tools;
|
|
1588
|
+
return [
|
|
1589
|
+
...tools,
|
|
1590
|
+
tool2(
|
|
1591
|
+
"start_child_cloud_build",
|
|
1592
|
+
"Start a cloud build for a child task. The child must be in Open status with story points and an agent assigned.",
|
|
1593
|
+
{
|
|
1594
|
+
childTaskId: z2.string().describe("The child task ID to start a cloud build for")
|
|
1595
|
+
},
|
|
1596
|
+
async ({ childTaskId }) => {
|
|
1597
|
+
try {
|
|
1598
|
+
const result = await connection.startChildCloudBuild(childTaskId);
|
|
1599
|
+
return textResult(`Cloud build started for child task: ${result.childTaskId}`);
|
|
1600
|
+
} catch (error) {
|
|
1601
|
+
return textResult(
|
|
1602
|
+
`Failed to start child cloud build: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
),
|
|
1607
|
+
tool2(
|
|
1608
|
+
"approve_and_merge_pr",
|
|
1609
|
+
"Approve and merge a child task's pull request. Only succeeds if all CI/CD checks are passing. The child task must be in ReviewPR status.",
|
|
1610
|
+
{
|
|
1611
|
+
childTaskId: z2.string().describe("The child task ID whose PR should be approved and merged")
|
|
1612
|
+
},
|
|
1613
|
+
async ({ childTaskId }) => {
|
|
1614
|
+
try {
|
|
1615
|
+
const result = await connection.approveAndMergePR(childTaskId);
|
|
1616
|
+
return textResult(
|
|
1617
|
+
`PR #${result.prNumber} approved and merged for task ${result.childTaskId}`
|
|
1618
|
+
);
|
|
1619
|
+
} catch (error) {
|
|
1620
|
+
return textResult(
|
|
1621
|
+
`Failed to approve and merge PR: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
)
|
|
1626
|
+
];
|
|
1459
1627
|
}
|
|
1460
1628
|
|
|
1461
1629
|
// src/tools/task-tools.ts
|
|
@@ -1494,7 +1662,8 @@ function textResult(text) {
|
|
|
1494
1662
|
}
|
|
1495
1663
|
function createConveyorMcpServer(connection, config, context) {
|
|
1496
1664
|
const commonTools = buildCommonTools(connection, config);
|
|
1497
|
-
const
|
|
1665
|
+
const isPackRunner = config.mode === "pm" && !!config.isAuto && !!context?.isParentTask;
|
|
1666
|
+
const modeTools = config.mode === "pm" ? buildPmTools(connection, context?.storyPoints, { includePackTools: isPackRunner }) : buildTaskTools(connection);
|
|
1498
1667
|
return createSdkMcpServer({
|
|
1499
1668
|
name: "conveyor",
|
|
1500
1669
|
tools: [...commonTools, ...modeTools]
|
|
@@ -1510,8 +1679,21 @@ var DESTRUCTIVE_CMD_PATTERN = /git\s+push\s+--force(?!\s*-with-lease)|git\s+rese
|
|
|
1510
1679
|
function buildCanUseTool(host) {
|
|
1511
1680
|
const QUESTION_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1512
1681
|
return async (toolName, input) => {
|
|
1682
|
+
const isPackRunner = host.config.mode === "pm" && !!host.config.isAuto;
|
|
1513
1683
|
const isPmPlanning = host.config.mode === "pm" && host.activeMode === "planning";
|
|
1514
1684
|
const isPmActive = host.config.mode === "pm" && host.activeMode === "active";
|
|
1685
|
+
if (isPackRunner) {
|
|
1686
|
+
if (toolName === "Bash") {
|
|
1687
|
+
const cmd = String(input.command ?? "");
|
|
1688
|
+
if (DESTRUCTIVE_CMD_PATTERN.test(cmd)) {
|
|
1689
|
+
return {
|
|
1690
|
+
behavior: "deny",
|
|
1691
|
+
message: "Destructive operation blocked. Use safer alternatives."
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
return { behavior: "allow", updatedInput: input };
|
|
1696
|
+
}
|
|
1515
1697
|
if (isPmPlanning && PM_PLAN_FILE_TOOLS.has(toolName)) {
|
|
1516
1698
|
const filePath = String(input.file_path ?? input.path ?? "");
|
|
1517
1699
|
if (filePath.includes(".claude/plans/")) {
|
|
@@ -1684,7 +1866,8 @@ function buildMultimodalPrompt(textPrompt, context, skipImages = false) {
|
|
|
1684
1866
|
async function runSdkQuery(host, context, followUpContent) {
|
|
1685
1867
|
if (host.isStopped()) return;
|
|
1686
1868
|
const isPm = host.config.mode === "pm";
|
|
1687
|
-
const
|
|
1869
|
+
const isPackRunner = isPm && !!host.config.isAuto;
|
|
1870
|
+
const isPmPlanning = isPm && host.activeMode === "planning" && !isPackRunner;
|
|
1688
1871
|
if (isPmPlanning) {
|
|
1689
1872
|
host.snapshotPlanFiles();
|
|
1690
1873
|
}
|
|
@@ -1695,7 +1878,7 @@ async function runSdkQuery(host, context, followUpContent) {
|
|
|
1695
1878
|
const followUpImages = typeof followUpContent === "string" ? [] : followUpContent.filter(
|
|
1696
1879
|
(b) => b.type === "image"
|
|
1697
1880
|
);
|
|
1698
|
-
const textPrompt = isPm ? `${buildInitialPrompt(host.config.mode, context)}
|
|
1881
|
+
const textPrompt = isPm ? `${buildInitialPrompt(host.config.mode, context, host.config.isAuto)}
|
|
1699
1882
|
|
|
1700
1883
|
---
|
|
1701
1884
|
|
|
@@ -1720,7 +1903,7 @@ ${followUpText}` : followUpText;
|
|
|
1720
1903
|
} else if (isPmPlanning) {
|
|
1721
1904
|
return;
|
|
1722
1905
|
} else {
|
|
1723
|
-
const initialPrompt = buildInitialPrompt(host.config.mode, context);
|
|
1906
|
+
const initialPrompt = buildInitialPrompt(host.config.mode, context, host.config.isAuto);
|
|
1724
1907
|
const prompt = buildMultimodalPrompt(initialPrompt, context);
|
|
1725
1908
|
const agentQuery = query({
|
|
1726
1909
|
prompt: host.createInputStream(prompt),
|
|
@@ -1743,7 +1926,7 @@ async function runWithRetry(initialQuery, context, host, options) {
|
|
|
1743
1926
|
);
|
|
1744
1927
|
}
|
|
1745
1928
|
const retryPrompt = buildMultimodalPrompt(
|
|
1746
|
-
buildInitialPrompt(host.config.mode, context),
|
|
1929
|
+
buildInitialPrompt(host.config.mode, context, host.config.isAuto),
|
|
1747
1930
|
context,
|
|
1748
1931
|
lastErrorWasImage
|
|
1749
1932
|
);
|
|
@@ -1763,7 +1946,7 @@ async function runWithRetry(initialQuery, context, host, options) {
|
|
|
1763
1946
|
context.claudeSessionId = null;
|
|
1764
1947
|
host.connection.storeSessionId("");
|
|
1765
1948
|
const freshPrompt = buildMultimodalPrompt(
|
|
1766
|
-
buildInitialPrompt(host.config.mode, context),
|
|
1949
|
+
buildInitialPrompt(host.config.mode, context, host.config.isAuto),
|
|
1767
1950
|
context
|
|
1768
1951
|
);
|
|
1769
1952
|
const freshQuery = query({
|
|
@@ -2061,7 +2244,12 @@ var AgentRunner = class _AgentRunner {
|
|
|
2061
2244
|
}
|
|
2062
2245
|
}
|
|
2063
2246
|
const isPm = this.config.mode === "pm";
|
|
2064
|
-
|
|
2247
|
+
const isPackRunner = isPm && !!this.config.isAuto && !!this.taskContext.isParentTask;
|
|
2248
|
+
if (isPackRunner) {
|
|
2249
|
+
await this.setState("running");
|
|
2250
|
+
await this.runQuerySafe(this.taskContext);
|
|
2251
|
+
if (!this.stopped) await this.setState("idle");
|
|
2252
|
+
} else if (isPm) {
|
|
2065
2253
|
await this.setState("idle");
|
|
2066
2254
|
} else {
|
|
2067
2255
|
await this.setState("running");
|
|
@@ -2518,7 +2706,7 @@ var ProjectRunner = class {
|
|
|
2518
2706
|
});
|
|
2519
2707
|
}
|
|
2520
2708
|
async handleAssignment(assignment) {
|
|
2521
|
-
const { taskId, taskToken, apiUrl, mode, branch, devBranch, useWorktree } = assignment;
|
|
2709
|
+
const { taskId, taskToken, apiUrl, mode, branch, devBranch, useWorktree, isAuto } = assignment;
|
|
2522
2710
|
const shortId = taskId.slice(0, 8);
|
|
2523
2711
|
if (this.activeAgents.has(taskId)) {
|
|
2524
2712
|
console.log(`[project-runner] Task ${shortId} already running, skipping`);
|
|
@@ -2567,7 +2755,8 @@ var ProjectRunner = class {
|
|
|
2567
2755
|
CONVEYOR_TASK_ID: taskId,
|
|
2568
2756
|
CONVEYOR_MODE: mode,
|
|
2569
2757
|
CONVEYOR_WORKSPACE: workDir,
|
|
2570
|
-
CONVEYOR_USE_WORKTREE: "false"
|
|
2758
|
+
CONVEYOR_USE_WORKTREE: "false",
|
|
2759
|
+
CONVEYOR_IS_AUTO: isAuto ? "true" : "false"
|
|
2571
2760
|
},
|
|
2572
2761
|
cwd: workDir,
|
|
2573
2762
|
stdio: ["pipe", "pipe", "pipe", "ipc"]
|
|
@@ -2745,4 +2934,4 @@ export {
|
|
|
2745
2934
|
ProjectRunner,
|
|
2746
2935
|
FileCache
|
|
2747
2936
|
};
|
|
2748
|
-
//# sourceMappingURL=chunk-
|
|
2937
|
+
//# sourceMappingURL=chunk-6DVMC6EU.js.map
|