@rallycry/conveyor-agent 7.0.4 → 7.0.5
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-K2TJHPMA.js → chunk-FCRGYU2M.js} +335 -116
- package/dist/chunk-FCRGYU2M.js.map +1 -0
- package/dist/cli.js +31 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-K2TJHPMA.js.map +0 -1
|
@@ -593,6 +593,34 @@ var Lifecycle = class {
|
|
|
593
593
|
|
|
594
594
|
// src/runner/git-utils.ts
|
|
595
595
|
import { execSync } from "child_process";
|
|
596
|
+
function syncWithBaseBranch(cwd, baseBranch) {
|
|
597
|
+
if (!baseBranch) return true;
|
|
598
|
+
try {
|
|
599
|
+
execSync(`git fetch origin ${baseBranch}`, { cwd, stdio: "ignore", timeout: 6e4 });
|
|
600
|
+
} catch {
|
|
601
|
+
process.stderr.write(
|
|
602
|
+
`[conveyor-agent] Warning: git fetch origin ${baseBranch} failed, continuing with current base
|
|
603
|
+
`
|
|
604
|
+
);
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
execSync(`git merge origin/${baseBranch} --no-edit`, { cwd, stdio: "ignore", timeout: 3e4 });
|
|
609
|
+
} catch {
|
|
610
|
+
process.stderr.write(
|
|
611
|
+
`[conveyor-agent] Warning: merge origin/${baseBranch} failed, aborting merge and continuing
|
|
612
|
+
`
|
|
613
|
+
);
|
|
614
|
+
try {
|
|
615
|
+
execSync("git merge --abort", { cwd, stdio: "ignore" });
|
|
616
|
+
} catch {
|
|
617
|
+
}
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
process.stderr.write(`[conveyor-agent] Synced with latest origin/${baseBranch}
|
|
621
|
+
`);
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
596
624
|
function hasUncommittedChanges(cwd) {
|
|
597
625
|
const status = execSync("git status --porcelain", {
|
|
598
626
|
cwd,
|
|
@@ -833,7 +861,6 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
|
833
861
|
`- "Open" \u2014 Ready to execute (if dependencies are met). Use start_child_cloud_build to fire it.`,
|
|
834
862
|
`- "InProgress" \u2014 Currently being worked on by a Task Runner. Wait \u2014 it will move to ReviewPR when done.`,
|
|
835
863
|
`- "ReviewPR" \u2014 Task Runner finished and opened a PR. Review and merge it.`,
|
|
836
|
-
`- "Hold" \u2014 PR exists but is on hold for team review. Do not merge \u2014 skip and move on.`,
|
|
837
864
|
`- "ReviewDev" \u2014 PR was merged to dev. This child is complete. Move on.`,
|
|
838
865
|
`- "Complete" \u2014 Fully done. Move on.`,
|
|
839
866
|
``,
|
|
@@ -850,7 +877,6 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
|
850
877
|
` - "InProgress": A Task Runner is actively working. Do nothing \u2014 wait.`,
|
|
851
878
|
` - "Open" + allDependenciesMet=true: Ready to fire. Use start_child_cloud_build.`,
|
|
852
879
|
` - "Open" + allDependenciesMet=false: Blocked \u2014 skip for now. Will be unblocked when deps complete.`,
|
|
853
|
-
` - "Hold": On hold \u2014 team must review before merge. Skip.`,
|
|
854
880
|
` - "ReviewDev" / "Complete": Already done. Skip.`,
|
|
855
881
|
` - "Planning": Not ready. If blocking progress, notify team.`,
|
|
856
882
|
``,
|
|
@@ -860,7 +886,7 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
|
860
886
|
``,
|
|
861
887
|
`5. After firing all ready tasks: report which tasks you fired to chat, then state you are going idle.`,
|
|
862
888
|
``,
|
|
863
|
-
`6. When ALL children are in "ReviewDev"
|
|
889
|
+
`6. When ALL children are in "ReviewDev" or "Complete" (no "Open", "InProgress", or "ReviewPR" remaining): do a final review, summarize results in chat, and mark this parent task complete with force_update_task_status("Complete").`,
|
|
864
890
|
``,
|
|
865
891
|
`## Important Rules`,
|
|
866
892
|
`- When dependencies are set on children, use them to determine execution order. Fire all ready tasks in parallel.`,
|
|
@@ -1158,6 +1184,15 @@ function buildDiscoveryPrompt(context) {
|
|
|
1158
1184
|
`- Goal: collaborate with the user to create a clear plan`,
|
|
1159
1185
|
`- Proactively fill task properties (SP, tags, icon) as the plan takes shape`,
|
|
1160
1186
|
``,
|
|
1187
|
+
`### Planning Checklist (complete ALL before calling ExitPlanMode)`,
|
|
1188
|
+
`Your PRIMARY goal is to create a thorough plan. Complete these steps in order:`,
|
|
1189
|
+
`1. Read the task description and chat history \u2014 respond to what's been discussed`,
|
|
1190
|
+
`2. Explore the codebase to understand relevant files and patterns`,
|
|
1191
|
+
`3. Save a detailed plan via \`update_task\``,
|
|
1192
|
+
`4. Set story points, tags, and title via \`update_task_properties\``,
|
|
1193
|
+
`5. Discuss the plan with the team if they're engaged, incorporate feedback`,
|
|
1194
|
+
`6. THEN call ExitPlanMode \u2014 it is the LAST step, not the first`,
|
|
1195
|
+
``,
|
|
1161
1196
|
`### Self-Identification Tools`,
|
|
1162
1197
|
`Use these MCP tools to set your own task properties:`,
|
|
1163
1198
|
`- \`update_task\` \u2014 save your plan and description`,
|
|
@@ -1169,9 +1204,16 @@ function buildDiscoveryPrompt(context) {
|
|
|
1169
1204
|
`- Add matching tags using \`update_task_properties\` \u2014 this links relevant documentation and rules that help you plan more effectively`,
|
|
1170
1205
|
`- Tags accelerate discovery by surfacing domain-specific context automatically`,
|
|
1171
1206
|
``,
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1207
|
+
...context?.isParentTask ? [
|
|
1208
|
+
`### Parent Task Coordination`,
|
|
1209
|
+
`You are a parent task with child tasks. Focus on breaking work into child tasks with detailed plans, not planning implementation for yourself.`,
|
|
1210
|
+
`- Use \`list_subtasks\` to review existing children. Create or update child tasks using \`create_subtask\` / \`update_subtask\`.`,
|
|
1211
|
+
`- Each child task should be a self-contained unit of work with a clear plan.`
|
|
1212
|
+
] : [
|
|
1213
|
+
`### Self-Update vs Subtasks`,
|
|
1214
|
+
`- If the work fits in a single task (1-3 SP), update YOUR OWN plan and properties \u2014 do not create subtasks`,
|
|
1215
|
+
`- Only create subtasks when the work genuinely requires multiple independent pieces (e.g., Pack-tier work, 8+ SP)`
|
|
1216
|
+
],
|
|
1175
1217
|
``,
|
|
1176
1218
|
`### Subtask Plan Requirements`,
|
|
1177
1219
|
`When creating subtasks, each MUST include a detailed \`plan\` field:`,
|
|
@@ -1181,11 +1223,12 @@ function buildDiscoveryPrompt(context) {
|
|
|
1181
1223
|
`- Include testing requirements and acceptance criteria`,
|
|
1182
1224
|
`- Set \`storyPointValue\` based on estimated complexity`,
|
|
1183
1225
|
``,
|
|
1184
|
-
`###
|
|
1185
|
-
`Once
|
|
1226
|
+
`### Completing Planning`,
|
|
1227
|
+
`Once ALL checklist items above are done, call the **ExitPlanMode** tool.`,
|
|
1186
1228
|
`- Required before ExitPlanMode will succeed: **plan** (via update_task), **story points** (via update_task_properties), **title** (via update_task_properties)`,
|
|
1187
1229
|
`- ExitPlanMode validates these properties and marks planning as complete`,
|
|
1188
|
-
`- It does NOT start building \u2014 the team controls when to switch to Build mode
|
|
1230
|
+
`- It does NOT start building \u2014 the team controls when to switch to Build mode`,
|
|
1231
|
+
`- Do NOT call ExitPlanMode until you have thoroughly explored the codebase and saved a detailed plan`
|
|
1189
1232
|
];
|
|
1190
1233
|
if (context) parts.push(...buildPropertyInstructions(context));
|
|
1191
1234
|
return parts.join("\n");
|
|
@@ -1220,6 +1263,14 @@ function buildAutoPrompt(context) {
|
|
|
1220
1263
|
`- Include testing requirements and acceptance criteria`,
|
|
1221
1264
|
`- Set \`storyPointValue\` based on estimated complexity`,
|
|
1222
1265
|
``,
|
|
1266
|
+
...context?.isParentTask ? [
|
|
1267
|
+
``,
|
|
1268
|
+
`### Parent Task Guidance`,
|
|
1269
|
+
`You are a parent task. Your plan should define child tasks with detailed plans, not direct implementation steps.`,
|
|
1270
|
+
`After ExitPlanMode, you'll transition to Review mode to coordinate child task execution.`,
|
|
1271
|
+
`Child task status lifecycle: Open \u2192 InProgress \u2192 ReviewPR \u2192 ReviewDev \u2192 Complete.`
|
|
1272
|
+
] : [],
|
|
1273
|
+
``,
|
|
1223
1274
|
`### Autonomous Guidelines:`,
|
|
1224
1275
|
`- Make decisions independently \u2014 do not ask the team for approval at each step`,
|
|
1225
1276
|
`- Only escalate when genuinely blocked (ambiguous requirements, missing access, conflicting instructions)`,
|
|
@@ -1239,8 +1290,14 @@ function buildModePrompt(agentMode, context) {
|
|
|
1239
1290
|
`You are in Building mode \u2014 executing the plan.`,
|
|
1240
1291
|
`- You have full coding access (read, write, edit, bash, git)`,
|
|
1241
1292
|
`- Safety rules: no destructive operations, use --force-with-lease instead of --force`,
|
|
1242
|
-
|
|
1243
|
-
|
|
1293
|
+
...context?.isParentTask ? [
|
|
1294
|
+
`- You are a parent task. Use \`list_subtasks\`, \`start_child_cloud_build\`, and subtask management tools to coordinate children.`,
|
|
1295
|
+
`- Do NOT implement code directly \u2014 fire child builds and review their work.`,
|
|
1296
|
+
`- Goal: coordinate child task execution and ensure all children complete successfully`
|
|
1297
|
+
] : [
|
|
1298
|
+
`- If this is a leaf task (no children): execute the plan directly`,
|
|
1299
|
+
`- Goal: implement the plan, run tests, open a PR when done`
|
|
1300
|
+
]
|
|
1244
1301
|
];
|
|
1245
1302
|
if (process.env.CLAUDESPACE_NAME) {
|
|
1246
1303
|
parts.push(
|
|
@@ -1257,15 +1314,7 @@ function buildModePrompt(agentMode, context) {
|
|
|
1257
1314
|
return parts.join("\n");
|
|
1258
1315
|
}
|
|
1259
1316
|
case "review":
|
|
1260
|
-
return
|
|
1261
|
-
`
|
|
1262
|
-
## Mode: Review`,
|
|
1263
|
-
`You are in Review mode \u2014 reviewing and coordinating.`,
|
|
1264
|
-
`- You have read-only access plus light edit capability (can suggest fixes, run tests, check linting)`,
|
|
1265
|
-
`- For parent tasks: you can manage children, review child PRs, fire next child builds`,
|
|
1266
|
-
`- You have Pack Runner coordination tools (list_subtasks, fire builds, approve PRs)`,
|
|
1267
|
-
`- Goal: ensure quality, provide feedback, coordinate progression`
|
|
1268
|
-
].join("\n");
|
|
1317
|
+
return buildReviewPrompt(context);
|
|
1269
1318
|
case "auto":
|
|
1270
1319
|
return buildAutoPrompt(context);
|
|
1271
1320
|
case "code-review":
|
|
@@ -1274,6 +1323,64 @@ function buildModePrompt(agentMode, context) {
|
|
|
1274
1323
|
return null;
|
|
1275
1324
|
}
|
|
1276
1325
|
}
|
|
1326
|
+
function buildReviewPrompt(context) {
|
|
1327
|
+
const parts = [
|
|
1328
|
+
`
|
|
1329
|
+
## Mode: Review`,
|
|
1330
|
+
`You are in Review mode \u2014 reviewing and coordinating.`,
|
|
1331
|
+
`- You have read-only access plus light edit capability (can suggest fixes, run tests, check linting)`,
|
|
1332
|
+
``
|
|
1333
|
+
];
|
|
1334
|
+
if (context?.isParentTask) {
|
|
1335
|
+
parts.push(
|
|
1336
|
+
`### Parent Task Review`,
|
|
1337
|
+
`You are reviewing and coordinating child tasks.`,
|
|
1338
|
+
`- Use \`list_subtasks\` to see current child task state and progress.`,
|
|
1339
|
+
`- For children in ReviewPR status: review their code quality and merge with \`approve_and_merge_pr\`.`,
|
|
1340
|
+
`- For children with failing CI: check with \`get_task_cli(childTaskId)\` and escalate if stuck.`,
|
|
1341
|
+
`- Fire next child builds with \`start_child_cloud_build\` when ready.`,
|
|
1342
|
+
`- Create follow-up tasks for issues discovered during review.`,
|
|
1343
|
+
``,
|
|
1344
|
+
`### Coordination Workflow`,
|
|
1345
|
+
`1. Check child task statuses with \`list_subtasks\``,
|
|
1346
|
+
`2. Review completed children \u2014 check PRs, run tests if needed`,
|
|
1347
|
+
`3. Approve and merge passing PRs`,
|
|
1348
|
+
`4. Fire builds for children that are ready`,
|
|
1349
|
+
`5. Create follow-up tasks for anything out of scope`
|
|
1350
|
+
);
|
|
1351
|
+
} else {
|
|
1352
|
+
parts.push(
|
|
1353
|
+
`### Leaf Task Review`,
|
|
1354
|
+
`You are reviewing your own work before completion.`,
|
|
1355
|
+
`- Run tests and check linting to verify the PR is ready.`,
|
|
1356
|
+
`- If the PR is already open, review the diff for correctness.`,
|
|
1357
|
+
`- You can get hands dirty \u2014 if a fix is small, make it directly.`,
|
|
1358
|
+
`- If follow-up work is needed, use \`create_follow_up_task\`.`
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
parts.push(
|
|
1362
|
+
``,
|
|
1363
|
+
`### General Review Guidelines`,
|
|
1364
|
+
`- For larger issues, create a follow-up task rather than fixing directly.`,
|
|
1365
|
+
`- Focus on correctness, pattern consistency, and test coverage.`,
|
|
1366
|
+
`- Be concise in feedback \u2014 actionable specifics over general observations.`,
|
|
1367
|
+
`- Goal: ensure quality, provide feedback, coordinate progression.`
|
|
1368
|
+
);
|
|
1369
|
+
if (process.env.CLAUDESPACE_NAME) {
|
|
1370
|
+
parts.push(
|
|
1371
|
+
``,
|
|
1372
|
+
`### Resource Management`,
|
|
1373
|
+
`Your pod starts with minimal resources (0.25 CPU / 1 Gi). You MUST call \`scale_up_resources\``,
|
|
1374
|
+
`BEFORE running any of these operations \u2014 they WILL fail or OOM at baseline resources:`,
|
|
1375
|
+
`- **light** (1 CPU / 4 Gi) \u2014 bun/npm/yarn install, pip install, basic dev servers, light builds`,
|
|
1376
|
+
`- **standard** (2 CPU / 8 Gi) \u2014 full dev servers, test suites, typecheck, lint`,
|
|
1377
|
+
`- **heavy** (4 CPU / 16 Gi) \u2014 E2E/browser automation, large parallel builds`,
|
|
1378
|
+
`Scaling is one-way (up only) and capped by project limits.`,
|
|
1379
|
+
`CRITICAL: Always scale to at least "light" before running any package install command.`
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
return parts.join("\n");
|
|
1383
|
+
}
|
|
1277
1384
|
function buildCodeReviewPrompt() {
|
|
1278
1385
|
return [
|
|
1279
1386
|
`
|
|
@@ -1504,6 +1611,7 @@ Your responses are sent directly to the task chat \u2014 the team sees everythin
|
|
|
1504
1611
|
}
|
|
1505
1612
|
|
|
1506
1613
|
// src/execution/prompt-builder.ts
|
|
1614
|
+
var CHAT_HISTORY_LIMIT = 40;
|
|
1507
1615
|
function formatFileSize(bytes) {
|
|
1508
1616
|
if (bytes === void 0) return "";
|
|
1509
1617
|
if (bytes < 1024) return `${bytes}B`;
|
|
@@ -1649,7 +1757,7 @@ function formatTaskFile(file) {
|
|
|
1649
1757
|
return [];
|
|
1650
1758
|
}
|
|
1651
1759
|
function formatChatHistory(chatHistory) {
|
|
1652
|
-
const relevant = chatHistory.slice(-
|
|
1760
|
+
const relevant = chatHistory.slice(-CHAT_HISTORY_LIMIT);
|
|
1653
1761
|
const parts = [`
|
|
1654
1762
|
## Recent Chat Context`];
|
|
1655
1763
|
for (const msg of relevant) {
|
|
@@ -1676,6 +1784,12 @@ async function resolveTaskTagContext(context) {
|
|
|
1676
1784
|
async function buildTaskBody(context) {
|
|
1677
1785
|
const parts = [];
|
|
1678
1786
|
parts.push(`# Task: ${context.title}`);
|
|
1787
|
+
if (context.projectName) {
|
|
1788
|
+
const descLine = context.projectDescription ? `
|
|
1789
|
+
${context.projectDescription}` : "";
|
|
1790
|
+
parts.push(`
|
|
1791
|
+
## Project: ${context.projectName}${descLine}`);
|
|
1792
|
+
}
|
|
1679
1793
|
if (context.description) {
|
|
1680
1794
|
parts.push(`
|
|
1681
1795
|
## Description
|
|
@@ -1755,7 +1869,8 @@ CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or g
|
|
|
1755
1869
|
if (isPm && context.isParentTask) {
|
|
1756
1870
|
return [
|
|
1757
1871
|
`You are the project manager for this task and its subtasks.`,
|
|
1758
|
-
`
|
|
1872
|
+
`Review existing subtasks via \`list_subtasks\` and the chat history before taking action.`,
|
|
1873
|
+
`Read the task description and chat history carefully \u2014 the team may have already provided context and requirements. Respond to what's been discussed rather than starting from scratch.`,
|
|
1759
1874
|
`The task details are provided above. Wait for the team to provide instructions before taking action.`,
|
|
1760
1875
|
`When you finish planning, save the plan with update_task and end your turn. Your reply will be visible to the team in chat.`
|
|
1761
1876
|
];
|
|
@@ -1763,6 +1878,7 @@ CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or g
|
|
|
1763
1878
|
if (isPm) {
|
|
1764
1879
|
return [
|
|
1765
1880
|
`You are the project manager for this task.`,
|
|
1881
|
+
`Read the task description and chat history carefully \u2014 the team may have already provided context and requirements. Respond to what's been discussed rather than starting from scratch.`,
|
|
1766
1882
|
`The task details are provided above. Wait for the team to ask questions or provide additional requirements before starting to plan.`,
|
|
1767
1883
|
`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.`
|
|
1768
1884
|
];
|
|
@@ -2016,7 +2132,11 @@ function buildGetTaskCliTool(connection) {
|
|
|
2016
2132
|
taskId: task_id,
|
|
2017
2133
|
source
|
|
2018
2134
|
});
|
|
2019
|
-
const formatted = result.map((entry) =>
|
|
2135
|
+
const formatted = result.map((entry) => {
|
|
2136
|
+
const type = entry.type;
|
|
2137
|
+
const time = entry.timestamp;
|
|
2138
|
+
return `[${time}] [${type}] ${formatCliEvent(entry)}`;
|
|
2139
|
+
}).join("\n");
|
|
2020
2140
|
return textResult(formatted || "No CLI logs found.");
|
|
2021
2141
|
} catch (error) {
|
|
2022
2142
|
return textResult(
|
|
@@ -2264,7 +2384,7 @@ function buildForceUpdateTaskStatusTool(connection) {
|
|
|
2264
2384
|
"force_update_task_status",
|
|
2265
2385
|
"EMERGENCY ONLY: Force-override a task's Kanban status. Status transitions happen automatically (building sets InProgress, PR creation sets ReviewPR, merge sets ReviewDev). Only use this if an automatic transition failed or a task is stuck in the wrong status. Omit task_id to update the current task, or provide a child task ID.",
|
|
2266
2386
|
{
|
|
2267
|
-
status: z.enum(["InProgress", "ReviewPR", "
|
|
2387
|
+
status: z.enum(["InProgress", "ReviewPR", "ReviewDev", "Complete"]).describe("The new status for the task"),
|
|
2268
2388
|
task_id: z.string().optional().describe("Child task ID to update. Omit to update the current task.")
|
|
2269
2389
|
},
|
|
2270
2390
|
async ({ status, task_id }) => {
|
|
@@ -4222,6 +4342,7 @@ function createConveyorMcpServer(harness, connection, config, context, agentMode
|
|
|
4222
4342
|
}
|
|
4223
4343
|
|
|
4224
4344
|
// src/execution/event-handlers.ts
|
|
4345
|
+
var logger = createServiceLogger("event-handlers");
|
|
4225
4346
|
function safeVoid(promise, context) {
|
|
4226
4347
|
if (promise && typeof promise.catch === "function") {
|
|
4227
4348
|
promise.catch((err) => {
|
|
@@ -4399,6 +4520,7 @@ async function emitResultEvent(event, host, context, startTime, lastAssistantUsa
|
|
|
4399
4520
|
}
|
|
4400
4521
|
function handleRateLimitEvent(event, host) {
|
|
4401
4522
|
const { rate_limit_info } = event;
|
|
4523
|
+
logger.info("Rate limit event received", { rate_limit_info });
|
|
4402
4524
|
const status = rate_limit_info.status;
|
|
4403
4525
|
const utilization = rate_limit_info.utilization ?? (status === "rejected" ? 1 : void 0);
|
|
4404
4526
|
if (utilization !== void 0 && rate_limit_info.rateLimitType) {
|
|
@@ -4616,6 +4738,16 @@ async function processEvents(events, context, host) {
|
|
|
4616
4738
|
};
|
|
4617
4739
|
}
|
|
4618
4740
|
|
|
4741
|
+
// src/execution/task-property-utils.ts
|
|
4742
|
+
function collectMissingProps(taskProps) {
|
|
4743
|
+
const missing = [];
|
|
4744
|
+
if (!taskProps.plan?.trim()) missing.push("plan (save via update_task)");
|
|
4745
|
+
if (!taskProps.storyPointId) missing.push("story points (use update_task_properties)");
|
|
4746
|
+
if (!taskProps.title || taskProps.title === "Untitled")
|
|
4747
|
+
missing.push("title (use update_task_properties)");
|
|
4748
|
+
return missing;
|
|
4749
|
+
}
|
|
4750
|
+
|
|
4619
4751
|
// src/execution/tool-access.ts
|
|
4620
4752
|
var PM_PLAN_FILE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit"]);
|
|
4621
4753
|
var DESTRUCTIVE_CMD_PATTERN = /git\s+push\s+--force(?!\s*-with-lease)|git\s+reset\s+--hard|rm\s+-rf\s+\//;
|
|
@@ -4701,18 +4833,11 @@ function handleAutoToolAccess(toolName, input, hasExitedPlanMode, isParentTask)
|
|
|
4701
4833
|
}
|
|
4702
4834
|
return { behavior: "allow", updatedInput: input };
|
|
4703
4835
|
}
|
|
4704
|
-
function collectMissingProps(taskProps) {
|
|
4705
|
-
const missing = [];
|
|
4706
|
-
if (!taskProps.plan?.trim()) missing.push("plan (save via update_task)");
|
|
4707
|
-
if (!taskProps.storyPointId) missing.push("story points (use update_task_properties)");
|
|
4708
|
-
if (!taskProps.title || taskProps.title === "Untitled")
|
|
4709
|
-
missing.push("title (use update_task_properties)");
|
|
4710
|
-
return missing;
|
|
4711
|
-
}
|
|
4712
4836
|
async function handleExitPlanMode(host, input) {
|
|
4713
4837
|
if (host.hasExitedPlanMode) {
|
|
4714
4838
|
return { behavior: "allow", updatedInput: input };
|
|
4715
4839
|
}
|
|
4840
|
+
host.exitPlanAttempts++;
|
|
4716
4841
|
try {
|
|
4717
4842
|
host.syncPlanFile();
|
|
4718
4843
|
const taskProps = await host.connection.getTaskProperties();
|
|
@@ -4735,15 +4860,20 @@ async function handleExitPlanMode(host, input) {
|
|
|
4735
4860
|
}
|
|
4736
4861
|
}
|
|
4737
4862
|
if (missingProps.length > 0) {
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4863
|
+
if (host.exitPlanAttempts <= 1) {
|
|
4864
|
+
return {
|
|
4865
|
+
behavior: "deny",
|
|
4866
|
+
message: [
|
|
4867
|
+
"Cannot exit plan mode yet. Required task properties are missing:",
|
|
4868
|
+
...missingProps.map((p) => `- ${p}`),
|
|
4869
|
+
"",
|
|
4870
|
+
"Fill these in using MCP tools, then try ExitPlanMode again."
|
|
4871
|
+
].join("\n")
|
|
4872
|
+
};
|
|
4873
|
+
}
|
|
4874
|
+
host.connection.postChatMessage(
|
|
4875
|
+
`\u26A0\uFE0F ExitPlanMode allowed with missing properties: ${missingProps.join(", ")}. Consider backfilling these later.`
|
|
4876
|
+
);
|
|
4747
4877
|
}
|
|
4748
4878
|
if (host.agentMode === "discovery") {
|
|
4749
4879
|
host.hasExitedPlanMode = true;
|
|
@@ -4848,7 +4978,7 @@ function buildCanUseTool(host) {
|
|
|
4848
4978
|
}
|
|
4849
4979
|
|
|
4850
4980
|
// src/execution/query-executor.ts
|
|
4851
|
-
var
|
|
4981
|
+
var logger2 = createServiceLogger("QueryExecutor");
|
|
4852
4982
|
var API_ERROR_PATTERN3 = /API Error: [45]\d\d/;
|
|
4853
4983
|
var IMAGE_ERROR_PATTERN2 = /Could not process image/i;
|
|
4854
4984
|
var RETRY_DELAYS_MS = [6e4, 12e4, 18e4, 3e5];
|
|
@@ -4868,6 +4998,18 @@ function buildHooks(host) {
|
|
|
4868
4998
|
isError: false
|
|
4869
4999
|
});
|
|
4870
5000
|
host.pendingToolOutputs.push(output);
|
|
5001
|
+
if (input.tool_name === "mcp__conveyor__create_pull_request") {
|
|
5002
|
+
try {
|
|
5003
|
+
const props = await host.connection.getTaskProperties();
|
|
5004
|
+
const missing = collectMissingProps(props);
|
|
5005
|
+
if (missing.length > 0) {
|
|
5006
|
+
host.connection.postChatMessage(
|
|
5007
|
+
`PR created! Please backfill missing task properties: ${missing.join(", ")}`
|
|
5008
|
+
);
|
|
5009
|
+
}
|
|
5010
|
+
} catch {
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
4871
5013
|
}
|
|
4872
5014
|
return await Promise.resolve({ continue: true });
|
|
4873
5015
|
}
|
|
@@ -4929,7 +5071,7 @@ function buildQueryOptions(host, context) {
|
|
|
4929
5071
|
disallowedTools: buildDisallowedTools(settings, mode, host.hasExitedPlanMode),
|
|
4930
5072
|
enableFileCheckpointing: settings.enableFileCheckpointing,
|
|
4931
5073
|
stderr: (data) => {
|
|
4932
|
-
|
|
5074
|
+
logger2.warn("Claude Code stderr", { data: data.trimEnd() });
|
|
4933
5075
|
}
|
|
4934
5076
|
};
|
|
4935
5077
|
}
|
|
@@ -5239,7 +5381,7 @@ var CostTracker = class {
|
|
|
5239
5381
|
};
|
|
5240
5382
|
|
|
5241
5383
|
// src/runner/query-bridge.ts
|
|
5242
|
-
var
|
|
5384
|
+
var logger3 = createServiceLogger("QueryBridge");
|
|
5243
5385
|
var QueryBridge = class {
|
|
5244
5386
|
constructor(connection, mode, runnerConfig, callbacks, workspaceDir) {
|
|
5245
5387
|
this.connection = connection;
|
|
@@ -5290,8 +5432,13 @@ var QueryBridge = class {
|
|
|
5290
5432
|
await runSdkQuery(host, context, followUpContent);
|
|
5291
5433
|
} catch (err) {
|
|
5292
5434
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5293
|
-
|
|
5294
|
-
|
|
5435
|
+
const isAbort = this._stopped || /abort/i.test(msg);
|
|
5436
|
+
if (isAbort) {
|
|
5437
|
+
logger3.info("Query stopped by user", { error: msg });
|
|
5438
|
+
} else {
|
|
5439
|
+
logger3.error("Query execution failed", { error: msg });
|
|
5440
|
+
this.connection.sendEvent({ type: "error", message: msg });
|
|
5441
|
+
}
|
|
5295
5442
|
} finally {
|
|
5296
5443
|
this.mode.pendingModeRestart = false;
|
|
5297
5444
|
}
|
|
@@ -5321,6 +5468,7 @@ var QueryBridge = class {
|
|
|
5321
5468
|
set hasExitedPlanMode(val) {
|
|
5322
5469
|
bridge.mode.hasExitedPlanMode = val;
|
|
5323
5470
|
},
|
|
5471
|
+
exitPlanAttempts: 0,
|
|
5324
5472
|
get pendingModeRestart() {
|
|
5325
5473
|
return bridge.mode.pendingModeRestart;
|
|
5326
5474
|
},
|
|
@@ -5381,9 +5529,11 @@ var SessionRunner = class _SessionRunner {
|
|
|
5381
5529
|
this.lifecycle = new Lifecycle(lifecycleConfig, {
|
|
5382
5530
|
onHeartbeat: () => this.connection.sendHeartbeat(),
|
|
5383
5531
|
onIdleTimeout: () => {
|
|
5532
|
+
process.stderr.write("[conveyor-agent] Idle timeout reached, entering sleep\n");
|
|
5384
5533
|
this.connection.emitStatus("sleeping");
|
|
5385
5534
|
},
|
|
5386
5535
|
onSleep: () => {
|
|
5536
|
+
process.stderr.write("[conveyor-agent] Sleep mode active\n");
|
|
5387
5537
|
this.connection.postChatMessage("Agent sleeping \u2014 send a message or click Resume to wake.");
|
|
5388
5538
|
},
|
|
5389
5539
|
onSleepGraceExpired: () => {
|
|
@@ -5395,6 +5545,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5395
5545
|
}
|
|
5396
5546
|
},
|
|
5397
5547
|
onWake: () => {
|
|
5548
|
+
process.stderr.write("[conveyor-agent] Woken from sleep\n");
|
|
5398
5549
|
this.lifecycle.cancelSleepShutdown();
|
|
5399
5550
|
}
|
|
5400
5551
|
});
|
|
@@ -5409,7 +5560,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5409
5560
|
return this.stopped;
|
|
5410
5561
|
}
|
|
5411
5562
|
// ── Main lifecycle ─────────────────────────────────────────────────
|
|
5412
|
-
// oxlint-disable-next-line max-lines-per-function -- lifecycle orchestration is inherently sequential
|
|
5563
|
+
// oxlint-disable-next-line max-lines-per-function, complexity -- lifecycle orchestration is inherently sequential
|
|
5413
5564
|
async start() {
|
|
5414
5565
|
await this.setState("connecting");
|
|
5415
5566
|
await this.connection.connect();
|
|
@@ -5447,6 +5598,9 @@ var SessionRunner = class _SessionRunner {
|
|
|
5447
5598
|
await this.shutdown("error");
|
|
5448
5599
|
return;
|
|
5449
5600
|
}
|
|
5601
|
+
if (this.fullContext?.baseBranch) {
|
|
5602
|
+
syncWithBaseBranch(this.config.workspaceDir, this.fullContext.baseBranch);
|
|
5603
|
+
}
|
|
5450
5604
|
this.mode.resolveInitialMode(this.taskContext);
|
|
5451
5605
|
this.queryBridge = this.createQueryBridge();
|
|
5452
5606
|
this.logInitialization();
|
|
@@ -5496,8 +5650,11 @@ var SessionRunner = class _SessionRunner {
|
|
|
5496
5650
|
content: msg.content,
|
|
5497
5651
|
userId: msg.userId
|
|
5498
5652
|
});
|
|
5499
|
-
|
|
5500
|
-
|
|
5653
|
+
await this.executeQuery(msg.content);
|
|
5654
|
+
if (this.stopped) break;
|
|
5655
|
+
if (this.interrupted) {
|
|
5656
|
+
this.interrupted = false;
|
|
5657
|
+
continue;
|
|
5501
5658
|
}
|
|
5502
5659
|
if (!this.stopped && this.pendingMessages.length === 0) {
|
|
5503
5660
|
await this.maybeSendPRNudge();
|
|
@@ -5518,7 +5675,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5518
5675
|
if (effectiveMode === "code-review") {
|
|
5519
5676
|
await this.setState("running");
|
|
5520
5677
|
await this.callbacks.onEvent({ type: "execute_mode", mode: effectiveMode });
|
|
5521
|
-
await this.
|
|
5678
|
+
await this.executeQuery();
|
|
5522
5679
|
this.stopped = true;
|
|
5523
5680
|
return;
|
|
5524
5681
|
}
|
|
@@ -5526,7 +5683,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5526
5683
|
if (shouldRun) {
|
|
5527
5684
|
await this.setState("running");
|
|
5528
5685
|
await this.callbacks.onEvent({ type: "execute_mode", mode: effectiveMode });
|
|
5529
|
-
await this.
|
|
5686
|
+
await this.executeQuery();
|
|
5530
5687
|
if (!this.stopped) await this.setState("idle");
|
|
5531
5688
|
} else {
|
|
5532
5689
|
await this.setState("idle");
|
|
@@ -5555,6 +5712,20 @@ var SessionRunner = class _SessionRunner {
|
|
|
5555
5712
|
}
|
|
5556
5713
|
}
|
|
5557
5714
|
}
|
|
5715
|
+
// ── Query execution with abort handling ────────────────────────────
|
|
5716
|
+
/** Run queryBridge.execute, swallowing abort errors from stop/softStop. */
|
|
5717
|
+
async executeQuery(followUpContent) {
|
|
5718
|
+
if (!this.fullContext || !this.queryBridge) return;
|
|
5719
|
+
try {
|
|
5720
|
+
await this.queryBridge.execute(this.fullContext, followUpContent);
|
|
5721
|
+
} catch (err) {
|
|
5722
|
+
if (this.interrupted || this.stopped) {
|
|
5723
|
+
process.stderr.write("[conveyor-agent] Query aborted by stop/softStop signal\n");
|
|
5724
|
+
return;
|
|
5725
|
+
}
|
|
5726
|
+
throw err;
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5558
5729
|
// ── Stop / soft-stop ───────────────────────────────────────────────
|
|
5559
5730
|
stop() {
|
|
5560
5731
|
this.stopped = true;
|
|
@@ -5624,9 +5795,8 @@ var SessionRunner = class _SessionRunner {
|
|
|
5624
5795
|
this.connection.postChatMessage(chatMsg);
|
|
5625
5796
|
await this.setState("running");
|
|
5626
5797
|
await this.callbacks.onEvent({ type: "pr_nudge", prompt });
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
}
|
|
5798
|
+
await this.executeQuery(prompt);
|
|
5799
|
+
if (this.interrupted || this.stopped) return;
|
|
5630
5800
|
await this.refreshTaskContext();
|
|
5631
5801
|
}
|
|
5632
5802
|
}
|
|
@@ -5663,6 +5833,8 @@ var SessionRunner = class _SessionRunner {
|
|
|
5663
5833
|
model: ctx.model,
|
|
5664
5834
|
githubBranch: ctx.githubBranch ?? "",
|
|
5665
5835
|
baseBranch: ctx.baseBranch ?? "",
|
|
5836
|
+
projectName: ctx.projectName ?? null,
|
|
5837
|
+
projectDescription: ctx.projectDescription ?? null,
|
|
5666
5838
|
githubPRUrl: ctx.githubPRUrl,
|
|
5667
5839
|
claudeSessionId: ctx.claudeSessionId ?? null,
|
|
5668
5840
|
isParentTask: !!ctx.parentTaskId,
|
|
@@ -5696,6 +5868,10 @@ var SessionRunner = class _SessionRunner {
|
|
|
5696
5868
|
);
|
|
5697
5869
|
bridge.isParentTask = this.fullContext?.isParentTask ?? false;
|
|
5698
5870
|
bridge.onModeTransition = (newMode) => {
|
|
5871
|
+
const oldMode = this.mode.effectiveMode;
|
|
5872
|
+
process.stderr.write(`[conveyor-agent] Mode transition: ${oldMode} \u2192 ${newMode}
|
|
5873
|
+
`);
|
|
5874
|
+
this.connection.sendEvent({ type: "mode_transition", from: oldMode, to: newMode });
|
|
5699
5875
|
this.mode.pendingModeRestart = true;
|
|
5700
5876
|
this.connection.emitModeChanged(newMode);
|
|
5701
5877
|
this.softStop();
|
|
@@ -5731,6 +5907,9 @@ var SessionRunner = class _SessionRunner {
|
|
|
5731
5907
|
await this.callbacks.onStatusChange(status);
|
|
5732
5908
|
}
|
|
5733
5909
|
async shutdown(finalState) {
|
|
5910
|
+
process.stderr.write(`[conveyor-agent] Shutdown: reason=${finalState}
|
|
5911
|
+
`);
|
|
5912
|
+
this.connection.sendEvent({ type: "shutdown", reason: finalState });
|
|
5734
5913
|
this.lifecycle.destroy();
|
|
5735
5914
|
await this.setState(finalState);
|
|
5736
5915
|
this.connection.disconnect();
|
|
@@ -5738,21 +5917,29 @@ var SessionRunner = class _SessionRunner {
|
|
|
5738
5917
|
logInitialization() {
|
|
5739
5918
|
const context = {
|
|
5740
5919
|
mode: this.mode.effectiveMode,
|
|
5741
|
-
model: this.taskContext?.model,
|
|
5742
5920
|
runnerMode: this.config.runnerMode ?? "task",
|
|
5743
5921
|
sessionId: this.sessionId,
|
|
5922
|
+
// Task context
|
|
5744
5923
|
isParentTask: this.fullContext?.isParentTask ?? false,
|
|
5745
|
-
status: this.taskContext?.status
|
|
5924
|
+
status: this.taskContext?.status,
|
|
5925
|
+
taskTitle: this.fullContext?.title,
|
|
5926
|
+
hasExistingPR: !!this.fullContext?.githubPRUrl,
|
|
5927
|
+
hasExistingSession: !!this.fullContext?.claudeSessionId,
|
|
5928
|
+
chatHistoryLength: this.fullContext?.chatHistory?.length ?? 0,
|
|
5929
|
+
tagIds: this.fullContext?.taskTagIds ?? [],
|
|
5930
|
+
// Agent config
|
|
5931
|
+
model: this.taskContext?.model,
|
|
5932
|
+
isAuto: this.config.isAuto ?? false
|
|
5746
5933
|
};
|
|
5747
5934
|
process.stderr.write(`[conveyor-agent] Initialized: ${JSON.stringify(context)}
|
|
5748
5935
|
`);
|
|
5749
|
-
this.connection.sendEvent({ type: "
|
|
5936
|
+
this.connection.sendEvent({ type: "session_manifest", ...context });
|
|
5750
5937
|
}
|
|
5751
5938
|
};
|
|
5752
5939
|
|
|
5753
5940
|
// src/connection/project-connection.ts
|
|
5754
5941
|
import { io as io2 } from "socket.io-client";
|
|
5755
|
-
var
|
|
5942
|
+
var logger4 = createServiceLogger("ProjectConnection");
|
|
5756
5943
|
var EVENT_BATCH_MS2 = 500;
|
|
5757
5944
|
var ProjectConnection = class {
|
|
5758
5945
|
socket = null;
|
|
@@ -5795,7 +5982,7 @@ var ProjectConnection = class {
|
|
|
5795
5982
|
let settled = false;
|
|
5796
5983
|
let attempts = 0;
|
|
5797
5984
|
const maxInitialAttempts = 30;
|
|
5798
|
-
|
|
5985
|
+
logger4.info("Connecting", { apiUrl: this.config.apiUrl, projectId: this.config.projectId });
|
|
5799
5986
|
this.socket = io2(this.config.apiUrl, {
|
|
5800
5987
|
auth: {
|
|
5801
5988
|
projectToken: this.config.projectToken,
|
|
@@ -5815,7 +6002,7 @@ var ProjectConnection = class {
|
|
|
5815
6002
|
settled = true;
|
|
5816
6003
|
resolve2();
|
|
5817
6004
|
}
|
|
5818
|
-
|
|
6005
|
+
logger4.info("Connected to API");
|
|
5819
6006
|
});
|
|
5820
6007
|
this.socket.on("connect_error", (error) => {
|
|
5821
6008
|
attempts++;
|
|
@@ -5827,10 +6014,10 @@ var ProjectConnection = class {
|
|
|
5827
6014
|
}
|
|
5828
6015
|
});
|
|
5829
6016
|
this.socket.on("disconnect", (reason) => {
|
|
5830
|
-
|
|
6017
|
+
logger4.warn("Disconnected from API", { reason });
|
|
5831
6018
|
});
|
|
5832
6019
|
this.socket.io.on("reconnect", () => {
|
|
5833
|
-
|
|
6020
|
+
logger4.info("Reconnected to API");
|
|
5834
6021
|
for (const cb of this.reconnectCallbacks) cb();
|
|
5835
6022
|
});
|
|
5836
6023
|
});
|
|
@@ -5977,7 +6164,7 @@ function runStartCommand(cmd, cwd, onOutput) {
|
|
|
5977
6164
|
|
|
5978
6165
|
// src/runner/commit-watcher.ts
|
|
5979
6166
|
import { execSync as execSync3 } from "child_process";
|
|
5980
|
-
var
|
|
6167
|
+
var logger5 = createServiceLogger("CommitWatcher");
|
|
5981
6168
|
var CommitWatcher = class {
|
|
5982
6169
|
constructor(config, callbacks) {
|
|
5983
6170
|
this.config = config;
|
|
@@ -5993,7 +6180,7 @@ var CommitWatcher = class {
|
|
|
5993
6180
|
this.branch = branch;
|
|
5994
6181
|
this.lastKnownRemoteSha = this.getLocalHeadSha();
|
|
5995
6182
|
this.interval = setInterval(() => void this.poll(), this.config.pollIntervalMs);
|
|
5996
|
-
|
|
6183
|
+
logger5.info("Commit watcher started", {
|
|
5997
6184
|
branch,
|
|
5998
6185
|
baseSha: this.lastKnownRemoteSha?.slice(0, 8)
|
|
5999
6186
|
});
|
|
@@ -6058,7 +6245,7 @@ var CommitWatcher = class {
|
|
|
6058
6245
|
}
|
|
6059
6246
|
this.lastKnownRemoteSha = remoteSha;
|
|
6060
6247
|
this.isSyncing = true;
|
|
6061
|
-
|
|
6248
|
+
logger5.info("New commits detected", {
|
|
6062
6249
|
branch: this.branch,
|
|
6063
6250
|
commitCount,
|
|
6064
6251
|
sha: remoteSha.slice(0, 8)
|
|
@@ -6074,7 +6261,7 @@ var CommitWatcher = class {
|
|
|
6074
6261
|
});
|
|
6075
6262
|
} catch (err) {
|
|
6076
6263
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6077
|
-
|
|
6264
|
+
logger5.error("Error handling new commits", { error: msg });
|
|
6078
6265
|
} finally {
|
|
6079
6266
|
this.isSyncing = false;
|
|
6080
6267
|
}
|
|
@@ -6311,7 +6498,7 @@ function buildProjectTools(connection) {
|
|
|
6311
6498
|
}
|
|
6312
6499
|
|
|
6313
6500
|
// src/runner/project-chat-handler.ts
|
|
6314
|
-
var
|
|
6501
|
+
var logger6 = createServiceLogger("ProjectChat");
|
|
6315
6502
|
var FALLBACK_MODEL = "claude-sonnet-4-20250514";
|
|
6316
6503
|
function buildSystemPrompt2(projectDir, agentCtx) {
|
|
6317
6504
|
const parts = [];
|
|
@@ -6358,7 +6545,7 @@ async function fetchContext(connection, chatId) {
|
|
|
6358
6545
|
projectId: connection.projectId
|
|
6359
6546
|
});
|
|
6360
6547
|
} catch {
|
|
6361
|
-
|
|
6548
|
+
logger6.warn("Could not fetch agent context, using defaults");
|
|
6362
6549
|
}
|
|
6363
6550
|
let chatHistory = [];
|
|
6364
6551
|
try {
|
|
@@ -6368,7 +6555,7 @@ async function fetchContext(connection, chatId) {
|
|
|
6368
6555
|
chatId
|
|
6369
6556
|
});
|
|
6370
6557
|
} catch {
|
|
6371
|
-
|
|
6558
|
+
logger6.warn("Could not fetch chat history, proceeding without it");
|
|
6372
6559
|
}
|
|
6373
6560
|
return { agentCtx, chatHistory };
|
|
6374
6561
|
}
|
|
@@ -6486,7 +6673,7 @@ async function handleProjectChatMessage(message, connection, projectDir, session
|
|
|
6486
6673
|
return await runChatQuery(message, connection, projectDir, sessionId);
|
|
6487
6674
|
} catch (error) {
|
|
6488
6675
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6489
|
-
|
|
6676
|
+
logger6.error("Failed to handle message", { error: msg });
|
|
6490
6677
|
try {
|
|
6491
6678
|
await connection.call("postProjectAgentMessage", {
|
|
6492
6679
|
projectId: connection.projectId,
|
|
@@ -6502,7 +6689,7 @@ async function handleProjectChatMessage(message, connection, projectDir, session
|
|
|
6502
6689
|
}
|
|
6503
6690
|
|
|
6504
6691
|
// src/runner/project-runner.ts
|
|
6505
|
-
var
|
|
6692
|
+
var logger7 = createServiceLogger("ProjectRunner");
|
|
6506
6693
|
var __filename = fileURLToPath(import.meta.url);
|
|
6507
6694
|
var __dirname = path.dirname(__filename);
|
|
6508
6695
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
@@ -6521,7 +6708,7 @@ function setupWorkDir(projectDir, assignment) {
|
|
|
6521
6708
|
}
|
|
6522
6709
|
if (branch && branch !== devBranch) {
|
|
6523
6710
|
if (hasUncommittedChanges(workDir)) {
|
|
6524
|
-
|
|
6711
|
+
logger7.warn("Uncommitted changes, skipping checkout", { taskId: shortId, branch });
|
|
6525
6712
|
} else {
|
|
6526
6713
|
try {
|
|
6527
6714
|
execSync5(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
|
|
@@ -6529,7 +6716,7 @@ function setupWorkDir(projectDir, assignment) {
|
|
|
6529
6716
|
try {
|
|
6530
6717
|
execSync5(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
|
|
6531
6718
|
} catch {
|
|
6532
|
-
|
|
6719
|
+
logger7.warn("Could not checkout branch", { taskId: shortId, branch });
|
|
6533
6720
|
}
|
|
6534
6721
|
}
|
|
6535
6722
|
}
|
|
@@ -6545,7 +6732,7 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
6545
6732
|
const effectiveAgentMode = agentMode ?? (isAuto ? "auto" : "");
|
|
6546
6733
|
const effectiveApiUrl = apiUrl || process.env.CONVEYOR_API_URL || "";
|
|
6547
6734
|
if (!effectiveApiUrl) {
|
|
6548
|
-
|
|
6735
|
+
logger7.error("No API URL available for child agent", { taskId: taskId.slice(0, 8) });
|
|
6549
6736
|
}
|
|
6550
6737
|
const child = fork(cliPath, [], {
|
|
6551
6738
|
env: {
|
|
@@ -6573,12 +6760,12 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
6573
6760
|
const shortId = taskId.slice(0, 8);
|
|
6574
6761
|
child.stdout?.on("data", (data) => {
|
|
6575
6762
|
for (const line of data.toString().trimEnd().split("\n")) {
|
|
6576
|
-
|
|
6763
|
+
logger7.info(line, { taskId: shortId });
|
|
6577
6764
|
}
|
|
6578
6765
|
});
|
|
6579
6766
|
child.stderr?.on("data", (data) => {
|
|
6580
6767
|
for (const line of data.toString().trimEnd().split("\n")) {
|
|
6581
|
-
|
|
6768
|
+
logger7.info(line, { taskId: shortId });
|
|
6582
6769
|
}
|
|
6583
6770
|
});
|
|
6584
6771
|
return child;
|
|
@@ -6626,7 +6813,7 @@ var ProjectRunner = class {
|
|
|
6626
6813
|
capabilities: ["task", "pm", "code-review", "audit"]
|
|
6627
6814
|
});
|
|
6628
6815
|
this.branchSwitchCommand = registration.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
6629
|
-
|
|
6816
|
+
logger7.info("Registered as project agent", { agentName: registration.agentName });
|
|
6630
6817
|
try {
|
|
6631
6818
|
await this.executeSetupCommand();
|
|
6632
6819
|
this.executeStartCommand();
|
|
@@ -6637,7 +6824,7 @@ var ProjectRunner = class {
|
|
|
6637
6824
|
});
|
|
6638
6825
|
} catch (error) {
|
|
6639
6826
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6640
|
-
|
|
6827
|
+
logger7.error("Environment setup failed", { error: msg });
|
|
6641
6828
|
this.setupComplete = false;
|
|
6642
6829
|
}
|
|
6643
6830
|
this.wireEventHandlers();
|
|
@@ -6649,7 +6836,7 @@ var ProjectRunner = class {
|
|
|
6649
6836
|
if (currentBranch) {
|
|
6650
6837
|
this.commitWatcher.start(currentBranch);
|
|
6651
6838
|
}
|
|
6652
|
-
|
|
6839
|
+
logger7.info("Connected, waiting for task assignments");
|
|
6653
6840
|
await new Promise((resolve2) => {
|
|
6654
6841
|
this.resolveLifecycle = resolve2;
|
|
6655
6842
|
});
|
|
@@ -6657,7 +6844,7 @@ var ProjectRunner = class {
|
|
|
6657
6844
|
async stop() {
|
|
6658
6845
|
if (this.stopping) return;
|
|
6659
6846
|
this.stopping = true;
|
|
6660
|
-
|
|
6847
|
+
logger7.info("Shutting down");
|
|
6661
6848
|
this.commitWatcher.stop();
|
|
6662
6849
|
await this.killStartCommand();
|
|
6663
6850
|
if (this.heartbeatTimer) {
|
|
@@ -6688,7 +6875,7 @@ var ProjectRunner = class {
|
|
|
6688
6875
|
} catch {
|
|
6689
6876
|
}
|
|
6690
6877
|
this.connection.disconnect();
|
|
6691
|
-
|
|
6878
|
+
logger7.info("Shutdown complete");
|
|
6692
6879
|
if (this.resolveLifecycle) {
|
|
6693
6880
|
this.resolveLifecycle();
|
|
6694
6881
|
this.resolveLifecycle = null;
|
|
@@ -6724,10 +6911,10 @@ var ProjectRunner = class {
|
|
|
6724
6911
|
capabilities: ["task", "pm", "code-review", "audit"]
|
|
6725
6912
|
});
|
|
6726
6913
|
this.connection.emitStatus(this.activeAgents.size > 0 ? "busy" : "idle");
|
|
6727
|
-
|
|
6914
|
+
logger7.info("Re-registered after reconnect");
|
|
6728
6915
|
} catch (error) {
|
|
6729
6916
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6730
|
-
|
|
6917
|
+
logger7.error("Failed to re-register after reconnect", { error: msg });
|
|
6731
6918
|
}
|
|
6732
6919
|
}
|
|
6733
6920
|
// ── Tag audit ──────────────────────────────────────────────────────────
|
|
@@ -6738,7 +6925,7 @@ var ProjectRunner = class {
|
|
|
6738
6925
|
await handleTagAudit(request, this.connection, this.projectDir);
|
|
6739
6926
|
} catch (error) {
|
|
6740
6927
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6741
|
-
|
|
6928
|
+
logger7.error("Tag audit failed", { error: msg, requestId: request.requestId });
|
|
6742
6929
|
try {
|
|
6743
6930
|
await this.connection.call("reportTagAuditResult", {
|
|
6744
6931
|
projectId: this.connection.projectId,
|
|
@@ -6757,15 +6944,15 @@ var ProjectRunner = class {
|
|
|
6757
6944
|
async killAgent(agent, taskId) {
|
|
6758
6945
|
const shortId = taskId.slice(0, 8);
|
|
6759
6946
|
if (agent.process.exitCode !== null) {
|
|
6760
|
-
|
|
6947
|
+
logger7.info("Agent process already exited", { taskId: shortId });
|
|
6761
6948
|
return;
|
|
6762
6949
|
}
|
|
6763
|
-
|
|
6950
|
+
logger7.info("Killing agent process", { taskId: shortId });
|
|
6764
6951
|
agent.process.kill("SIGTERM");
|
|
6765
6952
|
await new Promise((resolve2) => {
|
|
6766
6953
|
const timer = setTimeout(() => {
|
|
6767
6954
|
if (agent.process.exitCode === null) {
|
|
6768
|
-
|
|
6955
|
+
logger7.warn("Agent did not exit after SIGTERM, sending SIGKILL", { taskId: shortId });
|
|
6769
6956
|
agent.process.kill("SIGKILL");
|
|
6770
6957
|
}
|
|
6771
6958
|
resolve2();
|
|
@@ -6784,15 +6971,15 @@ var ProjectRunner = class {
|
|
|
6784
6971
|
const existing = this.activeAgents.get(agentKey);
|
|
6785
6972
|
if (existing) {
|
|
6786
6973
|
if (existing.process.exitCode === null) {
|
|
6787
|
-
|
|
6974
|
+
logger7.info("Re-assignment received, killing existing agent", { taskId: shortId });
|
|
6788
6975
|
await this.killAgent(existing, taskId);
|
|
6789
6976
|
} else {
|
|
6790
|
-
|
|
6977
|
+
logger7.info("Stale agent entry (process already exited), cleaning up", { taskId: shortId });
|
|
6791
6978
|
}
|
|
6792
6979
|
this.activeAgents.delete(agentKey);
|
|
6793
6980
|
}
|
|
6794
6981
|
if (this.activeAgents.size >= MAX_CONCURRENT) {
|
|
6795
|
-
|
|
6982
|
+
logger7.warn("Max concurrent agents reached", { maxConcurrent: MAX_CONCURRENT });
|
|
6796
6983
|
this.connection.emitTaskStopped(taskId, "max_concurrent_reached");
|
|
6797
6984
|
return;
|
|
6798
6985
|
}
|
|
@@ -6800,9 +6987,12 @@ var ProjectRunner = class {
|
|
|
6800
6987
|
try {
|
|
6801
6988
|
execSync5("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
|
|
6802
6989
|
} catch {
|
|
6803
|
-
|
|
6990
|
+
logger7.warn("Git fetch failed", { taskId: shortId });
|
|
6804
6991
|
}
|
|
6805
6992
|
const { workDir, usesWorktree } = setupWorkDir(this.projectDir, assignment);
|
|
6993
|
+
if (assignment.devBranch) {
|
|
6994
|
+
syncWithBaseBranch(workDir, assignment.devBranch);
|
|
6995
|
+
}
|
|
6806
6996
|
const child = spawnChildAgent(assignment, workDir);
|
|
6807
6997
|
this.activeAgents.set(agentKey, {
|
|
6808
6998
|
process: child,
|
|
@@ -6811,12 +7001,12 @@ var ProjectRunner = class {
|
|
|
6811
7001
|
usesWorktree
|
|
6812
7002
|
});
|
|
6813
7003
|
this.connection.emitTaskStarted(taskId);
|
|
6814
|
-
|
|
7004
|
+
logger7.info("Started task", { taskId: shortId, mode, workDir });
|
|
6815
7005
|
child.on("exit", (code) => {
|
|
6816
7006
|
this.activeAgents.delete(agentKey);
|
|
6817
7007
|
const reason = code === 0 ? "completed" : `exited with code ${code}`;
|
|
6818
7008
|
this.connection.emitTaskStopped(taskId, reason);
|
|
6819
|
-
|
|
7009
|
+
logger7.info("Task exited", { taskId: shortId, reason });
|
|
6820
7010
|
if (code === 0 && usesWorktree) {
|
|
6821
7011
|
try {
|
|
6822
7012
|
removeWorktree(this.projectDir, taskId);
|
|
@@ -6826,7 +7016,7 @@ var ProjectRunner = class {
|
|
|
6826
7016
|
});
|
|
6827
7017
|
} catch (error) {
|
|
6828
7018
|
const msg = error instanceof Error ? error.message : "Unknown";
|
|
6829
|
-
|
|
7019
|
+
logger7.error("Failed to start task", { taskId: shortId, error: msg });
|
|
6830
7020
|
this.connection.emitTaskStopped(taskId, `start_failed: ${msg}`);
|
|
6831
7021
|
}
|
|
6832
7022
|
}
|
|
@@ -6834,7 +7024,7 @@ var ProjectRunner = class {
|
|
|
6834
7024
|
const agentKey = this.activeAgents.has(taskId) ? taskId : `${taskId}:code-review`;
|
|
6835
7025
|
const agent = this.activeAgents.get(agentKey);
|
|
6836
7026
|
if (!agent) return;
|
|
6837
|
-
|
|
7027
|
+
logger7.info("Stopping task", { taskId: taskId.slice(0, 8) });
|
|
6838
7028
|
void this.killAgent(agent, taskId).then(() => {
|
|
6839
7029
|
if (agent.usesWorktree) {
|
|
6840
7030
|
try {
|
|
@@ -6851,30 +7041,30 @@ var ProjectRunner = class {
|
|
|
6851
7041
|
const currentBranch = this.getCurrentBranch();
|
|
6852
7042
|
if (currentBranch === workspaceBranch) return;
|
|
6853
7043
|
if (hasUncommittedChanges(this.projectDir)) {
|
|
6854
|
-
|
|
7044
|
+
logger7.warn("Uncommitted changes, skipping workspace branch checkout");
|
|
6855
7045
|
return;
|
|
6856
7046
|
}
|
|
6857
7047
|
try {
|
|
6858
7048
|
execSync5(`git fetch origin ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
6859
7049
|
execSync5(`git checkout ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
6860
|
-
|
|
7050
|
+
logger7.info("Checked out workspace branch", { workspaceBranch });
|
|
6861
7051
|
} catch {
|
|
6862
|
-
|
|
7052
|
+
logger7.warn("Failed to checkout workspace branch", { workspaceBranch });
|
|
6863
7053
|
}
|
|
6864
7054
|
}
|
|
6865
7055
|
async executeSetupCommand() {
|
|
6866
7056
|
const cmd = process.env.CONVEYOR_SETUP_COMMAND;
|
|
6867
7057
|
if (!cmd) return;
|
|
6868
|
-
|
|
7058
|
+
logger7.info("Running setup command", { command: cmd });
|
|
6869
7059
|
try {
|
|
6870
7060
|
await runSetupCommand(cmd, this.projectDir, (stream, data) => {
|
|
6871
7061
|
this.connection.sendEvent({ type: "setup_output", stream, data });
|
|
6872
7062
|
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
6873
7063
|
});
|
|
6874
|
-
|
|
7064
|
+
logger7.info("Setup command completed");
|
|
6875
7065
|
} catch (error) {
|
|
6876
7066
|
const msg = error instanceof Error ? error.message : "Setup command failed";
|
|
6877
|
-
|
|
7067
|
+
logger7.error("Setup command failed", { error: msg });
|
|
6878
7068
|
this.connection.sendEvent({ type: "setup_error", message: msg });
|
|
6879
7069
|
throw error;
|
|
6880
7070
|
}
|
|
@@ -6882,7 +7072,7 @@ var ProjectRunner = class {
|
|
|
6882
7072
|
executeStartCommand() {
|
|
6883
7073
|
const cmd = process.env.CONVEYOR_START_COMMAND;
|
|
6884
7074
|
if (!cmd) return;
|
|
6885
|
-
|
|
7075
|
+
logger7.info("Running start command", { command: cmd });
|
|
6886
7076
|
const child = runStartCommand(cmd, this.projectDir, (stream, data) => {
|
|
6887
7077
|
this.connection.sendEvent({ type: "start_command_output", stream, data });
|
|
6888
7078
|
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
@@ -6892,13 +7082,13 @@ var ProjectRunner = class {
|
|
|
6892
7082
|
child.on("exit", (code, signal) => {
|
|
6893
7083
|
this.startCommandRunning = false;
|
|
6894
7084
|
this.startCommandChild = null;
|
|
6895
|
-
|
|
7085
|
+
logger7.info("Start command exited", { code, signal });
|
|
6896
7086
|
this.connection.sendEvent({ type: "start_command_exited", code, signal });
|
|
6897
7087
|
});
|
|
6898
7088
|
child.on("error", (err) => {
|
|
6899
7089
|
this.startCommandRunning = false;
|
|
6900
7090
|
this.startCommandChild = null;
|
|
6901
|
-
|
|
7091
|
+
logger7.error("Start command error", { error: err.message });
|
|
6902
7092
|
});
|
|
6903
7093
|
}
|
|
6904
7094
|
async killStartCommand() {
|
|
@@ -6941,7 +7131,7 @@ var ProjectRunner = class {
|
|
|
6941
7131
|
try {
|
|
6942
7132
|
execSync5("git fetch origin", { cwd: this.projectDir, stdio: "pipe" });
|
|
6943
7133
|
} catch {
|
|
6944
|
-
|
|
7134
|
+
logger7.warn("Git fetch failed during branch switch");
|
|
6945
7135
|
}
|
|
6946
7136
|
detachWorktreeBranch(this.projectDir, data.branch);
|
|
6947
7137
|
try {
|
|
@@ -6954,7 +7144,7 @@ var ProjectRunner = class {
|
|
|
6954
7144
|
try {
|
|
6955
7145
|
execSync5(`git pull origin ${data.branch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
6956
7146
|
} catch {
|
|
6957
|
-
|
|
7147
|
+
logger7.warn("Git pull failed during branch switch");
|
|
6958
7148
|
}
|
|
6959
7149
|
if (data.syncAfter !== false) {
|
|
6960
7150
|
await this.handleSyncEnvironment();
|
|
@@ -6963,7 +7153,7 @@ var ProjectRunner = class {
|
|
|
6963
7153
|
callback({ ok: true });
|
|
6964
7154
|
} catch (err) {
|
|
6965
7155
|
const msg = err instanceof Error ? err.message : "Branch switch failed";
|
|
6966
|
-
|
|
7156
|
+
logger7.error("Branch switch failed", { error: msg });
|
|
6967
7157
|
callback({ ok: false, error: msg });
|
|
6968
7158
|
}
|
|
6969
7159
|
}
|
|
@@ -6978,14 +7168,14 @@ var ProjectRunner = class {
|
|
|
6978
7168
|
});
|
|
6979
7169
|
} catch (err) {
|
|
6980
7170
|
const msg = err instanceof Error ? err.message : "Sync command failed";
|
|
6981
|
-
|
|
7171
|
+
logger7.error("Branch switch sync command failed", { error: msg });
|
|
6982
7172
|
}
|
|
6983
7173
|
}
|
|
6984
7174
|
this.executeStartCommand();
|
|
6985
7175
|
callback?.({ ok: true });
|
|
6986
7176
|
} catch (err) {
|
|
6987
7177
|
const msg = err instanceof Error ? err.message : "Sync failed";
|
|
6988
|
-
|
|
7178
|
+
logger7.error("Environment sync failed", { error: msg });
|
|
6989
7179
|
callback?.({ ok: false, error: msg });
|
|
6990
7180
|
}
|
|
6991
7181
|
}
|
|
@@ -7010,7 +7200,7 @@ var ProjectRunner = class {
|
|
|
7010
7200
|
setupComplete: this.setupComplete,
|
|
7011
7201
|
startCommandRunning: this.startCommandRunning
|
|
7012
7202
|
});
|
|
7013
|
-
|
|
7203
|
+
logger7.info("Commit sync complete", { steps: stepsRun.join(", ") });
|
|
7014
7204
|
}
|
|
7015
7205
|
async smartSync(previousSha, newSha, branch) {
|
|
7016
7206
|
if (hasUncommittedChanges(this.projectDir)) {
|
|
@@ -7029,7 +7219,7 @@ var ProjectRunner = class {
|
|
|
7029
7219
|
});
|
|
7030
7220
|
} catch (err) {
|
|
7031
7221
|
const msg = err instanceof Error ? err.message : "Pull failed";
|
|
7032
|
-
|
|
7222
|
+
logger7.error("Git pull failed during commit sync", { error: msg });
|
|
7033
7223
|
this.executeStartCommand();
|
|
7034
7224
|
return ["error:pull"];
|
|
7035
7225
|
}
|
|
@@ -7056,7 +7246,7 @@ var ProjectRunner = class {
|
|
|
7056
7246
|
stepsRun.push("branchSwitchCommand");
|
|
7057
7247
|
} catch (err) {
|
|
7058
7248
|
const msg = err instanceof Error ? err.message : "Sync command failed";
|
|
7059
|
-
|
|
7249
|
+
logger7.error("Branch switch command failed", { error: msg });
|
|
7060
7250
|
}
|
|
7061
7251
|
} else if (!cmd) {
|
|
7062
7252
|
this.runIndividualSyncSteps(needsInstall, needsPrisma, stepsRun);
|
|
@@ -7069,7 +7259,7 @@ var ProjectRunner = class {
|
|
|
7069
7259
|
stepsRun.push("install");
|
|
7070
7260
|
} catch (err) {
|
|
7071
7261
|
const msg = err instanceof Error ? err.message : "Install failed";
|
|
7072
|
-
|
|
7262
|
+
logger7.error("bun install failed", { error: msg });
|
|
7073
7263
|
}
|
|
7074
7264
|
}
|
|
7075
7265
|
if (needsPrisma) {
|
|
@@ -7083,7 +7273,7 @@ var ProjectRunner = class {
|
|
|
7083
7273
|
stepsRun.push("prisma");
|
|
7084
7274
|
} catch (err) {
|
|
7085
7275
|
const msg = err instanceof Error ? err.message : "Prisma sync failed";
|
|
7086
|
-
|
|
7276
|
+
logger7.error("Prisma sync failed", { error: msg });
|
|
7087
7277
|
}
|
|
7088
7278
|
}
|
|
7089
7279
|
}
|
|
@@ -7099,6 +7289,33 @@ var ProjectRunner = class {
|
|
|
7099
7289
|
}
|
|
7100
7290
|
};
|
|
7101
7291
|
|
|
7292
|
+
// src/setup/config.ts
|
|
7293
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
7294
|
+
import { join as join3 } from "path";
|
|
7295
|
+
var DEVCONTAINER_PATH = ".devcontainer/conveyor/devcontainer.json";
|
|
7296
|
+
async function loadForwardPorts(workspaceDir) {
|
|
7297
|
+
try {
|
|
7298
|
+
const raw = await readFile2(join3(workspaceDir, DEVCONTAINER_PATH), "utf-8");
|
|
7299
|
+
const parsed = JSON.parse(raw);
|
|
7300
|
+
return parsed.forwardPorts ?? [];
|
|
7301
|
+
} catch {
|
|
7302
|
+
return [];
|
|
7303
|
+
}
|
|
7304
|
+
}
|
|
7305
|
+
function loadConveyorConfig() {
|
|
7306
|
+
const envSetup = process.env.CONVEYOR_SETUP_COMMAND;
|
|
7307
|
+
const envStart = process.env.CONVEYOR_START_COMMAND;
|
|
7308
|
+
const envPort = process.env.CONVEYOR_PREVIEW_PORT;
|
|
7309
|
+
if (envSetup || envStart) {
|
|
7310
|
+
return {
|
|
7311
|
+
setupCommand: envSetup,
|
|
7312
|
+
startCommand: envStart,
|
|
7313
|
+
previewPort: envPort ? Number(envPort) : void 0
|
|
7314
|
+
};
|
|
7315
|
+
}
|
|
7316
|
+
return null;
|
|
7317
|
+
}
|
|
7318
|
+
|
|
7102
7319
|
export {
|
|
7103
7320
|
AgentConnection,
|
|
7104
7321
|
ModeController,
|
|
@@ -7121,6 +7338,8 @@ export {
|
|
|
7121
7338
|
ensureWorktree,
|
|
7122
7339
|
detachWorktreeBranch,
|
|
7123
7340
|
removeWorktree,
|
|
7124
|
-
ProjectRunner
|
|
7341
|
+
ProjectRunner,
|
|
7342
|
+
loadForwardPorts,
|
|
7343
|
+
loadConveyorConfig
|
|
7125
7344
|
};
|
|
7126
|
-
//# sourceMappingURL=chunk-
|
|
7345
|
+
//# sourceMappingURL=chunk-FCRGYU2M.js.map
|