@rallycry/conveyor-agent 7.0.4 → 7.0.6
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-3H2FXJFD.js} +425 -121
- package/dist/chunk-3H2FXJFD.js.map +1 -0
- package/dist/cli.js +31 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -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
|
@@ -9,12 +9,16 @@ import {
|
|
|
9
9
|
// src/connection/agent-connection.ts
|
|
10
10
|
import { io } from "socket.io-client";
|
|
11
11
|
var EVENT_BATCH_MS = 500;
|
|
12
|
-
var AgentConnection = class {
|
|
12
|
+
var AgentConnection = class _AgentConnection {
|
|
13
13
|
socket = null;
|
|
14
14
|
config;
|
|
15
15
|
eventBuffer = [];
|
|
16
16
|
flushTimer = null;
|
|
17
17
|
lastEmittedStatus = null;
|
|
18
|
+
// Dedup: suppress near-identical messages within a short window
|
|
19
|
+
recentMessages = [];
|
|
20
|
+
static DEDUP_WINDOW_MS = 3e4;
|
|
21
|
+
static DEDUP_SIMILARITY_THRESHOLD = 0.7;
|
|
18
22
|
// Early-buffering: events that arrive before callbacks are registered
|
|
19
23
|
earlyMessages = [];
|
|
20
24
|
earlyStop = false;
|
|
@@ -237,6 +241,7 @@ var AgentConnection = class {
|
|
|
237
241
|
// ── Convenience methods (thin wrappers around call / emit) ─────────
|
|
238
242
|
emitStatus(status) {
|
|
239
243
|
this.lastEmittedStatus = status;
|
|
244
|
+
this.flushEvents();
|
|
240
245
|
void this.call("reportAgentStatus", {
|
|
241
246
|
sessionId: this.config.sessionId,
|
|
242
247
|
status
|
|
@@ -245,12 +250,42 @@ var AgentConnection = class {
|
|
|
245
250
|
}
|
|
246
251
|
postChatMessage(content) {
|
|
247
252
|
if (!this.socket) return;
|
|
253
|
+
if (this.isDuplicateMessage(content)) {
|
|
254
|
+
process.stderr.write(`[dedup] Suppressed near-duplicate message
|
|
255
|
+
`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
248
258
|
void this.call("postAgentMessage", {
|
|
249
259
|
sessionId: this.config.sessionId,
|
|
250
260
|
content
|
|
251
261
|
}).catch(() => {
|
|
252
262
|
});
|
|
253
263
|
}
|
|
264
|
+
isDuplicateMessage(content) {
|
|
265
|
+
const now = Date.now();
|
|
266
|
+
this.recentMessages = this.recentMessages.filter(
|
|
267
|
+
(m) => now - m.timestamp < _AgentConnection.DEDUP_WINDOW_MS
|
|
268
|
+
);
|
|
269
|
+
const words = new Set(
|
|
270
|
+
content.toLowerCase().split(/\s+/).filter((w) => w.length >= 3)
|
|
271
|
+
);
|
|
272
|
+
if (words.size === 0) return false;
|
|
273
|
+
for (const recent of this.recentMessages) {
|
|
274
|
+
let intersection = 0;
|
|
275
|
+
for (const w of words) {
|
|
276
|
+
if (recent.words.has(w)) intersection++;
|
|
277
|
+
}
|
|
278
|
+
const union = (/* @__PURE__ */ new Set([...words, ...recent.words])).size;
|
|
279
|
+
if (union > 0 && intersection / union > _AgentConnection.DEDUP_SIMILARITY_THRESHOLD) {
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
this.recentMessages.push({ words, timestamp: now });
|
|
284
|
+
if (this.recentMessages.length > 3) {
|
|
285
|
+
this.recentMessages.shift();
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
254
289
|
sendHeartbeat() {
|
|
255
290
|
if (!this.socket) return;
|
|
256
291
|
const statusMap = {
|
|
@@ -593,6 +628,34 @@ var Lifecycle = class {
|
|
|
593
628
|
|
|
594
629
|
// src/runner/git-utils.ts
|
|
595
630
|
import { execSync } from "child_process";
|
|
631
|
+
function syncWithBaseBranch(cwd, baseBranch) {
|
|
632
|
+
if (!baseBranch) return true;
|
|
633
|
+
try {
|
|
634
|
+
execSync(`git fetch origin ${baseBranch}`, { cwd, stdio: "ignore", timeout: 6e4 });
|
|
635
|
+
} catch {
|
|
636
|
+
process.stderr.write(
|
|
637
|
+
`[conveyor-agent] Warning: git fetch origin ${baseBranch} failed, continuing with current base
|
|
638
|
+
`
|
|
639
|
+
);
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
try {
|
|
643
|
+
execSync(`git merge origin/${baseBranch} --no-edit`, { cwd, stdio: "ignore", timeout: 3e4 });
|
|
644
|
+
} catch {
|
|
645
|
+
process.stderr.write(
|
|
646
|
+
`[conveyor-agent] Warning: merge origin/${baseBranch} failed, aborting merge and continuing
|
|
647
|
+
`
|
|
648
|
+
);
|
|
649
|
+
try {
|
|
650
|
+
execSync("git merge --abort", { cwd, stdio: "ignore" });
|
|
651
|
+
} catch {
|
|
652
|
+
}
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
process.stderr.write(`[conveyor-agent] Synced with latest origin/${baseBranch}
|
|
656
|
+
`);
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
596
659
|
function hasUncommittedChanges(cwd) {
|
|
597
660
|
const status = execSync("git status --porcelain", {
|
|
598
661
|
cwd,
|
|
@@ -668,10 +731,12 @@ function tryPush(cwd, branch) {
|
|
|
668
731
|
}
|
|
669
732
|
function isAuthError(cwd) {
|
|
670
733
|
try {
|
|
671
|
-
execSync("git push --dry-run
|
|
734
|
+
execSync("git push --dry-run", { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
672
735
|
return false;
|
|
673
736
|
} catch (err) {
|
|
674
|
-
const
|
|
737
|
+
const stderr = err.stderr?.toString() ?? "";
|
|
738
|
+
const stdout = err.stdout?.toString() ?? "";
|
|
739
|
+
const msg = stderr || stdout || (err instanceof Error ? err.message : "");
|
|
675
740
|
return /authentication|authorization|403|401|token/i.test(msg);
|
|
676
741
|
}
|
|
677
742
|
}
|
|
@@ -833,7 +898,6 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
|
833
898
|
`- "Open" \u2014 Ready to execute (if dependencies are met). Use start_child_cloud_build to fire it.`,
|
|
834
899
|
`- "InProgress" \u2014 Currently being worked on by a Task Runner. Wait \u2014 it will move to ReviewPR when done.`,
|
|
835
900
|
`- "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
901
|
`- "ReviewDev" \u2014 PR was merged to dev. This child is complete. Move on.`,
|
|
838
902
|
`- "Complete" \u2014 Fully done. Move on.`,
|
|
839
903
|
``,
|
|
@@ -850,7 +914,6 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
|
850
914
|
` - "InProgress": A Task Runner is actively working. Do nothing \u2014 wait.`,
|
|
851
915
|
` - "Open" + allDependenciesMet=true: Ready to fire. Use start_child_cloud_build.`,
|
|
852
916
|
` - "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
917
|
` - "ReviewDev" / "Complete": Already done. Skip.`,
|
|
855
918
|
` - "Planning": Not ready. If blocking progress, notify team.`,
|
|
856
919
|
``,
|
|
@@ -860,7 +923,7 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
|
860
923
|
``,
|
|
861
924
|
`5. After firing all ready tasks: report which tasks you fired to chat, then state you are going idle.`,
|
|
862
925
|
``,
|
|
863
|
-
`6. When ALL children are in "ReviewDev"
|
|
926
|
+
`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
927
|
``,
|
|
865
928
|
`## Important Rules`,
|
|
866
929
|
`- When dependencies are set on children, use them to determine execution order. Fire all ready tasks in parallel.`,
|
|
@@ -1158,6 +1221,15 @@ function buildDiscoveryPrompt(context) {
|
|
|
1158
1221
|
`- Goal: collaborate with the user to create a clear plan`,
|
|
1159
1222
|
`- Proactively fill task properties (SP, tags, icon) as the plan takes shape`,
|
|
1160
1223
|
``,
|
|
1224
|
+
`### Planning Checklist (complete ALL before calling ExitPlanMode)`,
|
|
1225
|
+
`Your PRIMARY goal is to create a thorough plan. Complete these steps in order:`,
|
|
1226
|
+
`1. Read the task description and chat history \u2014 respond to what's been discussed`,
|
|
1227
|
+
`2. Explore the codebase to understand relevant files and patterns`,
|
|
1228
|
+
`3. Save a detailed plan via \`update_task\``,
|
|
1229
|
+
`4. Set story points, tags, and title via \`update_task_properties\``,
|
|
1230
|
+
`5. Discuss the plan with the team if they're engaged, incorporate feedback`,
|
|
1231
|
+
`6. THEN call ExitPlanMode \u2014 it is the LAST step, not the first`,
|
|
1232
|
+
``,
|
|
1161
1233
|
`### Self-Identification Tools`,
|
|
1162
1234
|
`Use these MCP tools to set your own task properties:`,
|
|
1163
1235
|
`- \`update_task\` \u2014 save your plan and description`,
|
|
@@ -1169,9 +1241,16 @@ function buildDiscoveryPrompt(context) {
|
|
|
1169
1241
|
`- Add matching tags using \`update_task_properties\` \u2014 this links relevant documentation and rules that help you plan more effectively`,
|
|
1170
1242
|
`- Tags accelerate discovery by surfacing domain-specific context automatically`,
|
|
1171
1243
|
``,
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1244
|
+
...context?.isParentTask ? [
|
|
1245
|
+
`### Parent Task Coordination`,
|
|
1246
|
+
`You are a parent task with child tasks. Focus on breaking work into child tasks with detailed plans, not planning implementation for yourself.`,
|
|
1247
|
+
`- Use \`list_subtasks\` to review existing children. Create or update child tasks using \`create_subtask\` / \`update_subtask\`.`,
|
|
1248
|
+
`- Each child task should be a self-contained unit of work with a clear plan.`
|
|
1249
|
+
] : [
|
|
1250
|
+
`### Self-Update vs Subtasks`,
|
|
1251
|
+
`- If the work fits in a single task (1-3 SP), update YOUR OWN plan and properties \u2014 do not create subtasks`,
|
|
1252
|
+
`- Only create subtasks when the work genuinely requires multiple independent pieces (e.g., Pack-tier work, 8+ SP)`
|
|
1253
|
+
],
|
|
1175
1254
|
``,
|
|
1176
1255
|
`### Subtask Plan Requirements`,
|
|
1177
1256
|
`When creating subtasks, each MUST include a detailed \`plan\` field:`,
|
|
@@ -1181,11 +1260,12 @@ function buildDiscoveryPrompt(context) {
|
|
|
1181
1260
|
`- Include testing requirements and acceptance criteria`,
|
|
1182
1261
|
`- Set \`storyPointValue\` based on estimated complexity`,
|
|
1183
1262
|
``,
|
|
1184
|
-
`###
|
|
1185
|
-
`Once
|
|
1263
|
+
`### Completing Planning`,
|
|
1264
|
+
`Once ALL checklist items above are done, call the **ExitPlanMode** tool.`,
|
|
1186
1265
|
`- Required before ExitPlanMode will succeed: **plan** (via update_task), **story points** (via update_task_properties), **title** (via update_task_properties)`,
|
|
1187
1266
|
`- 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
|
|
1267
|
+
`- It does NOT start building \u2014 the team controls when to switch to Build mode`,
|
|
1268
|
+
`- Do NOT call ExitPlanMode until you have thoroughly explored the codebase and saved a detailed plan`
|
|
1189
1269
|
];
|
|
1190
1270
|
if (context) parts.push(...buildPropertyInstructions(context));
|
|
1191
1271
|
return parts.join("\n");
|
|
@@ -1220,6 +1300,14 @@ function buildAutoPrompt(context) {
|
|
|
1220
1300
|
`- Include testing requirements and acceptance criteria`,
|
|
1221
1301
|
`- Set \`storyPointValue\` based on estimated complexity`,
|
|
1222
1302
|
``,
|
|
1303
|
+
...context?.isParentTask ? [
|
|
1304
|
+
``,
|
|
1305
|
+
`### Parent Task Guidance`,
|
|
1306
|
+
`You are a parent task. Your plan should define child tasks with detailed plans, not direct implementation steps.`,
|
|
1307
|
+
`After ExitPlanMode, you'll transition to Review mode to coordinate child task execution.`,
|
|
1308
|
+
`Child task status lifecycle: Open \u2192 InProgress \u2192 ReviewPR \u2192 ReviewDev \u2192 Complete.`
|
|
1309
|
+
] : [],
|
|
1310
|
+
``,
|
|
1223
1311
|
`### Autonomous Guidelines:`,
|
|
1224
1312
|
`- Make decisions independently \u2014 do not ask the team for approval at each step`,
|
|
1225
1313
|
`- Only escalate when genuinely blocked (ambiguous requirements, missing access, conflicting instructions)`,
|
|
@@ -1239,8 +1327,14 @@ function buildModePrompt(agentMode, context) {
|
|
|
1239
1327
|
`You are in Building mode \u2014 executing the plan.`,
|
|
1240
1328
|
`- You have full coding access (read, write, edit, bash, git)`,
|
|
1241
1329
|
`- Safety rules: no destructive operations, use --force-with-lease instead of --force`,
|
|
1242
|
-
|
|
1243
|
-
|
|
1330
|
+
...context?.isParentTask ? [
|
|
1331
|
+
`- You are a parent task. Use \`list_subtasks\`, \`start_child_cloud_build\`, and subtask management tools to coordinate children.`,
|
|
1332
|
+
`- Do NOT implement code directly \u2014 fire child builds and review their work.`,
|
|
1333
|
+
`- Goal: coordinate child task execution and ensure all children complete successfully`
|
|
1334
|
+
] : [
|
|
1335
|
+
`- If this is a leaf task (no children): execute the plan directly`,
|
|
1336
|
+
`- Goal: implement the plan, run tests, open a PR when done`
|
|
1337
|
+
]
|
|
1244
1338
|
];
|
|
1245
1339
|
if (process.env.CLAUDESPACE_NAME) {
|
|
1246
1340
|
parts.push(
|
|
@@ -1257,15 +1351,7 @@ function buildModePrompt(agentMode, context) {
|
|
|
1257
1351
|
return parts.join("\n");
|
|
1258
1352
|
}
|
|
1259
1353
|
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");
|
|
1354
|
+
return buildReviewPrompt(context);
|
|
1269
1355
|
case "auto":
|
|
1270
1356
|
return buildAutoPrompt(context);
|
|
1271
1357
|
case "code-review":
|
|
@@ -1274,6 +1360,64 @@ function buildModePrompt(agentMode, context) {
|
|
|
1274
1360
|
return null;
|
|
1275
1361
|
}
|
|
1276
1362
|
}
|
|
1363
|
+
function buildReviewPrompt(context) {
|
|
1364
|
+
const parts = [
|
|
1365
|
+
`
|
|
1366
|
+
## Mode: Review`,
|
|
1367
|
+
`You are in Review mode \u2014 reviewing and coordinating.`,
|
|
1368
|
+
`- You have read-only access plus light edit capability (can suggest fixes, run tests, check linting)`,
|
|
1369
|
+
``
|
|
1370
|
+
];
|
|
1371
|
+
if (context?.isParentTask) {
|
|
1372
|
+
parts.push(
|
|
1373
|
+
`### Parent Task Review`,
|
|
1374
|
+
`You are reviewing and coordinating child tasks.`,
|
|
1375
|
+
`- Use \`list_subtasks\` to see current child task state and progress.`,
|
|
1376
|
+
`- For children in ReviewPR status: review their code quality and merge with \`approve_and_merge_pr\`.`,
|
|
1377
|
+
`- For children with failing CI: check with \`get_task_cli(childTaskId)\` and escalate if stuck.`,
|
|
1378
|
+
`- Fire next child builds with \`start_child_cloud_build\` when ready.`,
|
|
1379
|
+
`- Create follow-up tasks for issues discovered during review.`,
|
|
1380
|
+
``,
|
|
1381
|
+
`### Coordination Workflow`,
|
|
1382
|
+
`1. Check child task statuses with \`list_subtasks\``,
|
|
1383
|
+
`2. Review completed children \u2014 check PRs, run tests if needed`,
|
|
1384
|
+
`3. Approve and merge passing PRs`,
|
|
1385
|
+
`4. Fire builds for children that are ready`,
|
|
1386
|
+
`5. Create follow-up tasks for anything out of scope`
|
|
1387
|
+
);
|
|
1388
|
+
} else {
|
|
1389
|
+
parts.push(
|
|
1390
|
+
`### Leaf Task Review`,
|
|
1391
|
+
`You are reviewing your own work before completion.`,
|
|
1392
|
+
`- Run tests and check linting to verify the PR is ready.`,
|
|
1393
|
+
`- If the PR is already open, review the diff for correctness.`,
|
|
1394
|
+
`- You can get hands dirty \u2014 if a fix is small, make it directly.`,
|
|
1395
|
+
`- If follow-up work is needed, use \`create_follow_up_task\`.`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
parts.push(
|
|
1399
|
+
``,
|
|
1400
|
+
`### General Review Guidelines`,
|
|
1401
|
+
`- For larger issues, create a follow-up task rather than fixing directly.`,
|
|
1402
|
+
`- Focus on correctness, pattern consistency, and test coverage.`,
|
|
1403
|
+
`- Be concise in feedback \u2014 actionable specifics over general observations.`,
|
|
1404
|
+
`- Goal: ensure quality, provide feedback, coordinate progression.`
|
|
1405
|
+
);
|
|
1406
|
+
if (process.env.CLAUDESPACE_NAME) {
|
|
1407
|
+
parts.push(
|
|
1408
|
+
``,
|
|
1409
|
+
`### Resource Management`,
|
|
1410
|
+
`Your pod starts with minimal resources (0.25 CPU / 1 Gi). You MUST call \`scale_up_resources\``,
|
|
1411
|
+
`BEFORE running any of these operations \u2014 they WILL fail or OOM at baseline resources:`,
|
|
1412
|
+
`- **light** (1 CPU / 4 Gi) \u2014 bun/npm/yarn install, pip install, basic dev servers, light builds`,
|
|
1413
|
+
`- **standard** (2 CPU / 8 Gi) \u2014 full dev servers, test suites, typecheck, lint`,
|
|
1414
|
+
`- **heavy** (4 CPU / 16 Gi) \u2014 E2E/browser automation, large parallel builds`,
|
|
1415
|
+
`Scaling is one-way (up only) and capped by project limits.`,
|
|
1416
|
+
`CRITICAL: Always scale to at least "light" before running any package install command.`
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
return parts.join("\n");
|
|
1420
|
+
}
|
|
1277
1421
|
function buildCodeReviewPrompt() {
|
|
1278
1422
|
return [
|
|
1279
1423
|
`
|
|
@@ -1504,6 +1648,7 @@ Your responses are sent directly to the task chat \u2014 the team sees everythin
|
|
|
1504
1648
|
}
|
|
1505
1649
|
|
|
1506
1650
|
// src/execution/prompt-builder.ts
|
|
1651
|
+
var CHAT_HISTORY_LIMIT = 40;
|
|
1507
1652
|
function formatFileSize(bytes) {
|
|
1508
1653
|
if (bytes === void 0) return "";
|
|
1509
1654
|
if (bytes < 1024) return `${bytes}B`;
|
|
@@ -1649,7 +1794,7 @@ function formatTaskFile(file) {
|
|
|
1649
1794
|
return [];
|
|
1650
1795
|
}
|
|
1651
1796
|
function formatChatHistory(chatHistory) {
|
|
1652
|
-
const relevant = chatHistory.slice(-
|
|
1797
|
+
const relevant = chatHistory.slice(-CHAT_HISTORY_LIMIT);
|
|
1653
1798
|
const parts = [`
|
|
1654
1799
|
## Recent Chat Context`];
|
|
1655
1800
|
for (const msg of relevant) {
|
|
@@ -1676,6 +1821,12 @@ async function resolveTaskTagContext(context) {
|
|
|
1676
1821
|
async function buildTaskBody(context) {
|
|
1677
1822
|
const parts = [];
|
|
1678
1823
|
parts.push(`# Task: ${context.title}`);
|
|
1824
|
+
if (context.projectName) {
|
|
1825
|
+
const descLine = context.projectDescription ? `
|
|
1826
|
+
${context.projectDescription}` : "";
|
|
1827
|
+
parts.push(`
|
|
1828
|
+
## Project: ${context.projectName}${descLine}`);
|
|
1829
|
+
}
|
|
1679
1830
|
if (context.description) {
|
|
1680
1831
|
parts.push(`
|
|
1681
1832
|
## Description
|
|
@@ -1755,7 +1906,8 @@ CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or g
|
|
|
1755
1906
|
if (isPm && context.isParentTask) {
|
|
1756
1907
|
return [
|
|
1757
1908
|
`You are the project manager for this task and its subtasks.`,
|
|
1758
|
-
`
|
|
1909
|
+
`Review existing subtasks via \`list_subtasks\` and the chat history before taking action.`,
|
|
1910
|
+
`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
1911
|
`The task details are provided above. Wait for the team to provide instructions before taking action.`,
|
|
1760
1912
|
`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
1913
|
];
|
|
@@ -1763,6 +1915,7 @@ CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or g
|
|
|
1763
1915
|
if (isPm) {
|
|
1764
1916
|
return [
|
|
1765
1917
|
`You are the project manager for this task.`,
|
|
1918
|
+
`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
1919
|
`The task details are provided above. Wait for the team to ask questions or provide additional requirements before starting to plan.`,
|
|
1767
1920
|
`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
1921
|
];
|
|
@@ -2016,7 +2169,11 @@ function buildGetTaskCliTool(connection) {
|
|
|
2016
2169
|
taskId: task_id,
|
|
2017
2170
|
source
|
|
2018
2171
|
});
|
|
2019
|
-
const formatted = result.map((entry) =>
|
|
2172
|
+
const formatted = result.map((entry) => {
|
|
2173
|
+
const type = entry.type;
|
|
2174
|
+
const time = entry.timestamp;
|
|
2175
|
+
return `[${time}] [${type}] ${formatCliEvent(entry)}`;
|
|
2176
|
+
}).join("\n");
|
|
2020
2177
|
return textResult(formatted || "No CLI logs found.");
|
|
2021
2178
|
} catch (error) {
|
|
2022
2179
|
return textResult(
|
|
@@ -2264,7 +2421,7 @@ function buildForceUpdateTaskStatusTool(connection) {
|
|
|
2264
2421
|
"force_update_task_status",
|
|
2265
2422
|
"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
2423
|
{
|
|
2267
|
-
status: z.enum(["InProgress", "ReviewPR", "
|
|
2424
|
+
status: z.enum(["InProgress", "ReviewPR", "ReviewDev", "Complete"]).describe("The new status for the task"),
|
|
2268
2425
|
task_id: z.string().optional().describe("Child task ID to update. Omit to update the current task.")
|
|
2269
2426
|
},
|
|
2270
2427
|
async ({ status, task_id }) => {
|
|
@@ -2770,6 +2927,45 @@ function buildPmTools(connection, options) {
|
|
|
2770
2927
|
// src/tools/discovery-tools.ts
|
|
2771
2928
|
import { z as z3 } from "zod";
|
|
2772
2929
|
var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
|
|
2930
|
+
function buildSearchIconsTool(connection) {
|
|
2931
|
+
return defineTool(
|
|
2932
|
+
"search_icons",
|
|
2933
|
+
"Search for icons by keyword. Searches both the Conveyor database (existing icons) and FontAwesome library (~2200 icons). Returns matching icons with IDs and preview info. Use this to find an icon before generating one.",
|
|
2934
|
+
{
|
|
2935
|
+
query: z3.string().describe("Search query for icon name or keyword (e.g. 'bug', 'gear', 'star')")
|
|
2936
|
+
},
|
|
2937
|
+
async ({ query }) => {
|
|
2938
|
+
try {
|
|
2939
|
+
const [dbIcons, faIcons] = await Promise.all([
|
|
2940
|
+
connection.call("listIcons", { sessionId: connection.sessionId }),
|
|
2941
|
+
connection.call("searchFaIcons", { sessionId: connection.sessionId, query })
|
|
2942
|
+
]);
|
|
2943
|
+
const q = query.toLowerCase();
|
|
2944
|
+
const matchingDbIcons = dbIcons.filter((icon) => icon.name.toLowerCase().includes(q));
|
|
2945
|
+
const dbFaIds = new Set(matchingDbIcons.map((i) => i.name));
|
|
2946
|
+
const uniqueFaIcons = faIcons.filter((fa) => !dbFaIds.has(fa.fontAwesomeId));
|
|
2947
|
+
const results = {
|
|
2948
|
+
existingIcons: matchingDbIcons.map((i) => ({
|
|
2949
|
+
id: i.id,
|
|
2950
|
+
name: i.name,
|
|
2951
|
+
source: "database"
|
|
2952
|
+
})),
|
|
2953
|
+
fontAwesomeIcons: uniqueFaIcons.map((fa) => ({
|
|
2954
|
+
fontAwesomeId: fa.fontAwesomeId,
|
|
2955
|
+
label: fa.label,
|
|
2956
|
+
styles: fa.styles,
|
|
2957
|
+
source: "fontawesome"
|
|
2958
|
+
}))
|
|
2959
|
+
};
|
|
2960
|
+
return textResult(JSON.stringify(results, null, 2));
|
|
2961
|
+
} catch (error) {
|
|
2962
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2963
|
+
return textResult(`Failed to search icons: ${message}`);
|
|
2964
|
+
}
|
|
2965
|
+
},
|
|
2966
|
+
{ annotations: { readOnlyHint: true } }
|
|
2967
|
+
);
|
|
2968
|
+
}
|
|
2773
2969
|
function buildIconTools(connection) {
|
|
2774
2970
|
return [
|
|
2775
2971
|
defineTool(
|
|
@@ -2792,10 +2988,10 @@ function buildIconTools(connection) {
|
|
|
2792
2988
|
),
|
|
2793
2989
|
defineTool(
|
|
2794
2990
|
"generate_task_icon",
|
|
2795
|
-
"
|
|
2991
|
+
"Find a FontAwesome icon matching the description and assign it to this task. Falls back to a placeholder if no match is found. Provide a concise keyword or description.",
|
|
2796
2992
|
{
|
|
2797
2993
|
prompt: z3.string().describe(
|
|
2798
|
-
"
|
|
2994
|
+
"Keyword or description to search FontAwesome icons (e.g. 'bug', 'gear', 'rocket')"
|
|
2799
2995
|
),
|
|
2800
2996
|
aspectRatio: z3.enum(["auto", "portrait", "landscape", "square"]).optional().describe("Icon aspect ratio, defaults to square")
|
|
2801
2997
|
},
|
|
@@ -2850,6 +3046,7 @@ function buildDiscoveryTools(connection) {
|
|
|
2850
3046
|
}
|
|
2851
3047
|
}
|
|
2852
3048
|
),
|
|
3049
|
+
buildSearchIconsTool(connection),
|
|
2853
3050
|
...buildIconTools(connection)
|
|
2854
3051
|
];
|
|
2855
3052
|
}
|
|
@@ -4222,6 +4419,7 @@ function createConveyorMcpServer(harness, connection, config, context, agentMode
|
|
|
4222
4419
|
}
|
|
4223
4420
|
|
|
4224
4421
|
// src/execution/event-handlers.ts
|
|
4422
|
+
var logger = createServiceLogger("event-handlers");
|
|
4225
4423
|
function safeVoid(promise, context) {
|
|
4226
4424
|
if (promise && typeof promise.catch === "function") {
|
|
4227
4425
|
promise.catch((err) => {
|
|
@@ -4399,6 +4597,7 @@ async function emitResultEvent(event, host, context, startTime, lastAssistantUsa
|
|
|
4399
4597
|
}
|
|
4400
4598
|
function handleRateLimitEvent(event, host) {
|
|
4401
4599
|
const { rate_limit_info } = event;
|
|
4600
|
+
logger.info("Rate limit event received", { rate_limit_info });
|
|
4402
4601
|
const status = rate_limit_info.status;
|
|
4403
4602
|
const utilization = rate_limit_info.utilization ?? (status === "rejected" ? 1 : void 0);
|
|
4404
4603
|
if (utilization !== void 0 && rate_limit_info.rateLimitType) {
|
|
@@ -4616,6 +4815,16 @@ async function processEvents(events, context, host) {
|
|
|
4616
4815
|
};
|
|
4617
4816
|
}
|
|
4618
4817
|
|
|
4818
|
+
// src/execution/task-property-utils.ts
|
|
4819
|
+
function collectMissingProps(taskProps) {
|
|
4820
|
+
const missing = [];
|
|
4821
|
+
if (!taskProps.plan?.trim()) missing.push("plan (save via update_task)");
|
|
4822
|
+
if (!taskProps.storyPointId) missing.push("story points (use update_task_properties)");
|
|
4823
|
+
if (!taskProps.title || taskProps.title === "Untitled")
|
|
4824
|
+
missing.push("title (use update_task_properties)");
|
|
4825
|
+
return missing;
|
|
4826
|
+
}
|
|
4827
|
+
|
|
4619
4828
|
// src/execution/tool-access.ts
|
|
4620
4829
|
var PM_PLAN_FILE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit"]);
|
|
4621
4830
|
var DESTRUCTIVE_CMD_PATTERN = /git\s+push\s+--force(?!\s*-with-lease)|git\s+reset\s+--hard|rm\s+-rf\s+\//;
|
|
@@ -4701,18 +4910,11 @@ function handleAutoToolAccess(toolName, input, hasExitedPlanMode, isParentTask)
|
|
|
4701
4910
|
}
|
|
4702
4911
|
return { behavior: "allow", updatedInput: input };
|
|
4703
4912
|
}
|
|
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
4913
|
async function handleExitPlanMode(host, input) {
|
|
4713
4914
|
if (host.hasExitedPlanMode) {
|
|
4714
4915
|
return { behavior: "allow", updatedInput: input };
|
|
4715
4916
|
}
|
|
4917
|
+
host.exitPlanAttempts++;
|
|
4716
4918
|
try {
|
|
4717
4919
|
host.syncPlanFile();
|
|
4718
4920
|
const taskProps = await host.connection.getTaskProperties();
|
|
@@ -4735,21 +4937,27 @@ async function handleExitPlanMode(host, input) {
|
|
|
4735
4937
|
}
|
|
4736
4938
|
}
|
|
4737
4939
|
if (missingProps.length > 0) {
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4940
|
+
if (host.exitPlanAttempts <= 1) {
|
|
4941
|
+
return {
|
|
4942
|
+
behavior: "deny",
|
|
4943
|
+
message: [
|
|
4944
|
+
"Cannot exit plan mode yet. Required task properties are missing:",
|
|
4945
|
+
...missingProps.map((p) => `- ${p}`),
|
|
4946
|
+
"",
|
|
4947
|
+
"Fill these in using MCP tools, then try ExitPlanMode again."
|
|
4948
|
+
].join("\n")
|
|
4949
|
+
};
|
|
4950
|
+
}
|
|
4951
|
+
host.connection.postChatMessage(
|
|
4952
|
+
`\u26A0\uFE0F ExitPlanMode allowed with missing properties: ${missingProps.join(", ")}. Consider backfilling these later.`
|
|
4953
|
+
);
|
|
4747
4954
|
}
|
|
4748
4955
|
if (host.agentMode === "discovery") {
|
|
4749
4956
|
host.hasExitedPlanMode = true;
|
|
4750
4957
|
host.connection.postChatMessage(
|
|
4751
4958
|
"Plan complete. The task stays in Planning \u2014 identify it or switch to Build mode when ready."
|
|
4752
4959
|
);
|
|
4960
|
+
host.requestStop();
|
|
4753
4961
|
return { behavior: "allow", updatedInput: input };
|
|
4754
4962
|
}
|
|
4755
4963
|
await host.connection.triggerIdentification();
|
|
@@ -4848,7 +5056,7 @@ function buildCanUseTool(host) {
|
|
|
4848
5056
|
}
|
|
4849
5057
|
|
|
4850
5058
|
// src/execution/query-executor.ts
|
|
4851
|
-
var
|
|
5059
|
+
var logger2 = createServiceLogger("QueryExecutor");
|
|
4852
5060
|
var API_ERROR_PATTERN3 = /API Error: [45]\d\d/;
|
|
4853
5061
|
var IMAGE_ERROR_PATTERN2 = /Could not process image/i;
|
|
4854
5062
|
var RETRY_DELAYS_MS = [6e4, 12e4, 18e4, 3e5];
|
|
@@ -4868,6 +5076,18 @@ function buildHooks(host) {
|
|
|
4868
5076
|
isError: false
|
|
4869
5077
|
});
|
|
4870
5078
|
host.pendingToolOutputs.push(output);
|
|
5079
|
+
if (input.tool_name === "mcp__conveyor__create_pull_request") {
|
|
5080
|
+
try {
|
|
5081
|
+
const props = await host.connection.getTaskProperties();
|
|
5082
|
+
const missing = collectMissingProps(props);
|
|
5083
|
+
if (missing.length > 0) {
|
|
5084
|
+
host.connection.postChatMessage(
|
|
5085
|
+
`PR created! Please backfill missing task properties: ${missing.join(", ")}`
|
|
5086
|
+
);
|
|
5087
|
+
}
|
|
5088
|
+
} catch {
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
4871
5091
|
}
|
|
4872
5092
|
return await Promise.resolve({ continue: true });
|
|
4873
5093
|
}
|
|
@@ -4929,7 +5149,7 @@ function buildQueryOptions(host, context) {
|
|
|
4929
5149
|
disallowedTools: buildDisallowedTools(settings, mode, host.hasExitedPlanMode),
|
|
4930
5150
|
enableFileCheckpointing: settings.enableFileCheckpointing,
|
|
4931
5151
|
stderr: (data) => {
|
|
4932
|
-
|
|
5152
|
+
logger2.warn("Claude Code stderr", { data: data.trimEnd() });
|
|
4933
5153
|
}
|
|
4934
5154
|
};
|
|
4935
5155
|
}
|
|
@@ -5239,7 +5459,7 @@ var CostTracker = class {
|
|
|
5239
5459
|
};
|
|
5240
5460
|
|
|
5241
5461
|
// src/runner/query-bridge.ts
|
|
5242
|
-
var
|
|
5462
|
+
var logger3 = createServiceLogger("QueryBridge");
|
|
5243
5463
|
var QueryBridge = class {
|
|
5244
5464
|
constructor(connection, mode, runnerConfig, callbacks, workspaceDir) {
|
|
5245
5465
|
this.connection = connection;
|
|
@@ -5250,6 +5470,10 @@ var QueryBridge = class {
|
|
|
5250
5470
|
this.costTracker = new CostTracker();
|
|
5251
5471
|
this.planSync = new PlanSync(workspaceDir, connection);
|
|
5252
5472
|
}
|
|
5473
|
+
connection;
|
|
5474
|
+
mode;
|
|
5475
|
+
runnerConfig;
|
|
5476
|
+
callbacks;
|
|
5253
5477
|
harness;
|
|
5254
5478
|
costTracker;
|
|
5255
5479
|
planSync;
|
|
@@ -5290,8 +5514,13 @@ var QueryBridge = class {
|
|
|
5290
5514
|
await runSdkQuery(host, context, followUpContent);
|
|
5291
5515
|
} catch (err) {
|
|
5292
5516
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5293
|
-
|
|
5294
|
-
|
|
5517
|
+
const isAbort = this._stopped || /abort/i.test(msg);
|
|
5518
|
+
if (isAbort) {
|
|
5519
|
+
logger3.info("Query stopped by user", { error: msg });
|
|
5520
|
+
} else {
|
|
5521
|
+
logger3.error("Query execution failed", { error: msg });
|
|
5522
|
+
this.connection.sendEvent({ type: "error", message: msg });
|
|
5523
|
+
}
|
|
5295
5524
|
} finally {
|
|
5296
5525
|
this.mode.pendingModeRestart = false;
|
|
5297
5526
|
}
|
|
@@ -5321,6 +5550,7 @@ var QueryBridge = class {
|
|
|
5321
5550
|
set hasExitedPlanMode(val) {
|
|
5322
5551
|
bridge.mode.hasExitedPlanMode = val;
|
|
5323
5552
|
},
|
|
5553
|
+
exitPlanAttempts: 0,
|
|
5324
5554
|
get pendingModeRestart() {
|
|
5325
5555
|
return bridge.mode.pendingModeRestart;
|
|
5326
5556
|
},
|
|
@@ -5337,6 +5567,7 @@ var QueryBridge = class {
|
|
|
5337
5567
|
return bridge._abortController;
|
|
5338
5568
|
},
|
|
5339
5569
|
isStopped: () => bridge._stopped,
|
|
5570
|
+
requestStop: () => bridge.stop(),
|
|
5340
5571
|
createInputStream: (prompt) => bridge.createInputStream(prompt),
|
|
5341
5572
|
snapshotPlanFiles: () => bridge.planSync.snapshotPlanFiles(),
|
|
5342
5573
|
syncPlanFile: () => bridge.planSync.syncPlanFile(),
|
|
@@ -5381,9 +5612,11 @@ var SessionRunner = class _SessionRunner {
|
|
|
5381
5612
|
this.lifecycle = new Lifecycle(lifecycleConfig, {
|
|
5382
5613
|
onHeartbeat: () => this.connection.sendHeartbeat(),
|
|
5383
5614
|
onIdleTimeout: () => {
|
|
5615
|
+
process.stderr.write("[conveyor-agent] Idle timeout reached, entering sleep\n");
|
|
5384
5616
|
this.connection.emitStatus("sleeping");
|
|
5385
5617
|
},
|
|
5386
5618
|
onSleep: () => {
|
|
5619
|
+
process.stderr.write("[conveyor-agent] Sleep mode active\n");
|
|
5387
5620
|
this.connection.postChatMessage("Agent sleeping \u2014 send a message or click Resume to wake.");
|
|
5388
5621
|
},
|
|
5389
5622
|
onSleepGraceExpired: () => {
|
|
@@ -5395,6 +5628,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5395
5628
|
}
|
|
5396
5629
|
},
|
|
5397
5630
|
onWake: () => {
|
|
5631
|
+
process.stderr.write("[conveyor-agent] Woken from sleep\n");
|
|
5398
5632
|
this.lifecycle.cancelSleepShutdown();
|
|
5399
5633
|
}
|
|
5400
5634
|
});
|
|
@@ -5409,7 +5643,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5409
5643
|
return this.stopped;
|
|
5410
5644
|
}
|
|
5411
5645
|
// ── Main lifecycle ─────────────────────────────────────────────────
|
|
5412
|
-
// oxlint-disable-next-line max-lines-per-function -- lifecycle orchestration is inherently sequential
|
|
5646
|
+
// oxlint-disable-next-line max-lines-per-function, complexity -- lifecycle orchestration is inherently sequential
|
|
5413
5647
|
async start() {
|
|
5414
5648
|
await this.setState("connecting");
|
|
5415
5649
|
await this.connection.connect();
|
|
@@ -5447,6 +5681,9 @@ var SessionRunner = class _SessionRunner {
|
|
|
5447
5681
|
await this.shutdown("error");
|
|
5448
5682
|
return;
|
|
5449
5683
|
}
|
|
5684
|
+
if (this.fullContext?.baseBranch) {
|
|
5685
|
+
syncWithBaseBranch(this.config.workspaceDir, this.fullContext.baseBranch);
|
|
5686
|
+
}
|
|
5450
5687
|
this.mode.resolveInitialMode(this.taskContext);
|
|
5451
5688
|
this.queryBridge = this.createQueryBridge();
|
|
5452
5689
|
this.logInitialization();
|
|
@@ -5496,8 +5733,11 @@ var SessionRunner = class _SessionRunner {
|
|
|
5496
5733
|
content: msg.content,
|
|
5497
5734
|
userId: msg.userId
|
|
5498
5735
|
});
|
|
5499
|
-
|
|
5500
|
-
|
|
5736
|
+
await this.executeQuery(msg.content);
|
|
5737
|
+
if (this.stopped) break;
|
|
5738
|
+
if (this.interrupted) {
|
|
5739
|
+
this.interrupted = false;
|
|
5740
|
+
continue;
|
|
5501
5741
|
}
|
|
5502
5742
|
if (!this.stopped && this.pendingMessages.length === 0) {
|
|
5503
5743
|
await this.maybeSendPRNudge();
|
|
@@ -5518,7 +5758,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5518
5758
|
if (effectiveMode === "code-review") {
|
|
5519
5759
|
await this.setState("running");
|
|
5520
5760
|
await this.callbacks.onEvent({ type: "execute_mode", mode: effectiveMode });
|
|
5521
|
-
await this.
|
|
5761
|
+
await this.executeQuery();
|
|
5522
5762
|
this.stopped = true;
|
|
5523
5763
|
return;
|
|
5524
5764
|
}
|
|
@@ -5526,7 +5766,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5526
5766
|
if (shouldRun) {
|
|
5527
5767
|
await this.setState("running");
|
|
5528
5768
|
await this.callbacks.onEvent({ type: "execute_mode", mode: effectiveMode });
|
|
5529
|
-
await this.
|
|
5769
|
+
await this.executeQuery();
|
|
5530
5770
|
if (!this.stopped) await this.setState("idle");
|
|
5531
5771
|
} else {
|
|
5532
5772
|
await this.setState("idle");
|
|
@@ -5555,6 +5795,20 @@ var SessionRunner = class _SessionRunner {
|
|
|
5555
5795
|
}
|
|
5556
5796
|
}
|
|
5557
5797
|
}
|
|
5798
|
+
// ── Query execution with abort handling ────────────────────────────
|
|
5799
|
+
/** Run queryBridge.execute, swallowing abort errors from stop/softStop. */
|
|
5800
|
+
async executeQuery(followUpContent) {
|
|
5801
|
+
if (!this.fullContext || !this.queryBridge) return;
|
|
5802
|
+
try {
|
|
5803
|
+
await this.queryBridge.execute(this.fullContext, followUpContent);
|
|
5804
|
+
} catch (err) {
|
|
5805
|
+
if (this.interrupted || this.stopped) {
|
|
5806
|
+
process.stderr.write("[conveyor-agent] Query aborted by stop/softStop signal\n");
|
|
5807
|
+
return;
|
|
5808
|
+
}
|
|
5809
|
+
throw err;
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5558
5812
|
// ── Stop / soft-stop ───────────────────────────────────────────────
|
|
5559
5813
|
stop() {
|
|
5560
5814
|
this.stopped = true;
|
|
@@ -5624,9 +5878,8 @@ var SessionRunner = class _SessionRunner {
|
|
|
5624
5878
|
this.connection.postChatMessage(chatMsg);
|
|
5625
5879
|
await this.setState("running");
|
|
5626
5880
|
await this.callbacks.onEvent({ type: "pr_nudge", prompt });
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
}
|
|
5881
|
+
await this.executeQuery(prompt);
|
|
5882
|
+
if (this.interrupted || this.stopped) return;
|
|
5630
5883
|
await this.refreshTaskContext();
|
|
5631
5884
|
}
|
|
5632
5885
|
}
|
|
@@ -5663,6 +5916,8 @@ var SessionRunner = class _SessionRunner {
|
|
|
5663
5916
|
model: ctx.model,
|
|
5664
5917
|
githubBranch: ctx.githubBranch ?? "",
|
|
5665
5918
|
baseBranch: ctx.baseBranch ?? "",
|
|
5919
|
+
projectName: ctx.projectName ?? null,
|
|
5920
|
+
projectDescription: ctx.projectDescription ?? null,
|
|
5666
5921
|
githubPRUrl: ctx.githubPRUrl,
|
|
5667
5922
|
claudeSessionId: ctx.claudeSessionId ?? null,
|
|
5668
5923
|
isParentTask: !!ctx.parentTaskId,
|
|
@@ -5696,6 +5951,10 @@ var SessionRunner = class _SessionRunner {
|
|
|
5696
5951
|
);
|
|
5697
5952
|
bridge.isParentTask = this.fullContext?.isParentTask ?? false;
|
|
5698
5953
|
bridge.onModeTransition = (newMode) => {
|
|
5954
|
+
const oldMode = this.mode.effectiveMode;
|
|
5955
|
+
process.stderr.write(`[conveyor-agent] Mode transition: ${oldMode} \u2192 ${newMode}
|
|
5956
|
+
`);
|
|
5957
|
+
this.connection.sendEvent({ type: "mode_transition", from: oldMode, to: newMode });
|
|
5699
5958
|
this.mode.pendingModeRestart = true;
|
|
5700
5959
|
this.connection.emitModeChanged(newMode);
|
|
5701
5960
|
this.softStop();
|
|
@@ -5731,6 +5990,9 @@ var SessionRunner = class _SessionRunner {
|
|
|
5731
5990
|
await this.callbacks.onStatusChange(status);
|
|
5732
5991
|
}
|
|
5733
5992
|
async shutdown(finalState) {
|
|
5993
|
+
process.stderr.write(`[conveyor-agent] Shutdown: reason=${finalState}
|
|
5994
|
+
`);
|
|
5995
|
+
this.connection.sendEvent({ type: "shutdown", reason: finalState });
|
|
5734
5996
|
this.lifecycle.destroy();
|
|
5735
5997
|
await this.setState(finalState);
|
|
5736
5998
|
this.connection.disconnect();
|
|
@@ -5738,21 +6000,29 @@ var SessionRunner = class _SessionRunner {
|
|
|
5738
6000
|
logInitialization() {
|
|
5739
6001
|
const context = {
|
|
5740
6002
|
mode: this.mode.effectiveMode,
|
|
5741
|
-
model: this.taskContext?.model,
|
|
5742
6003
|
runnerMode: this.config.runnerMode ?? "task",
|
|
5743
6004
|
sessionId: this.sessionId,
|
|
6005
|
+
// Task context
|
|
5744
6006
|
isParentTask: this.fullContext?.isParentTask ?? false,
|
|
5745
|
-
status: this.taskContext?.status
|
|
6007
|
+
status: this.taskContext?.status,
|
|
6008
|
+
taskTitle: this.fullContext?.title,
|
|
6009
|
+
hasExistingPR: !!this.fullContext?.githubPRUrl,
|
|
6010
|
+
hasExistingSession: !!this.fullContext?.claudeSessionId,
|
|
6011
|
+
chatHistoryLength: this.fullContext?.chatHistory?.length ?? 0,
|
|
6012
|
+
tagIds: this.fullContext?.taskTagIds ?? [],
|
|
6013
|
+
// Agent config
|
|
6014
|
+
model: this.taskContext?.model,
|
|
6015
|
+
isAuto: this.config.isAuto ?? false
|
|
5746
6016
|
};
|
|
5747
6017
|
process.stderr.write(`[conveyor-agent] Initialized: ${JSON.stringify(context)}
|
|
5748
6018
|
`);
|
|
5749
|
-
this.connection.sendEvent({ type: "
|
|
6019
|
+
this.connection.sendEvent({ type: "session_manifest", ...context });
|
|
5750
6020
|
}
|
|
5751
6021
|
};
|
|
5752
6022
|
|
|
5753
6023
|
// src/connection/project-connection.ts
|
|
5754
6024
|
import { io as io2 } from "socket.io-client";
|
|
5755
|
-
var
|
|
6025
|
+
var logger4 = createServiceLogger("ProjectConnection");
|
|
5756
6026
|
var EVENT_BATCH_MS2 = 500;
|
|
5757
6027
|
var ProjectConnection = class {
|
|
5758
6028
|
socket = null;
|
|
@@ -5795,7 +6065,7 @@ var ProjectConnection = class {
|
|
|
5795
6065
|
let settled = false;
|
|
5796
6066
|
let attempts = 0;
|
|
5797
6067
|
const maxInitialAttempts = 30;
|
|
5798
|
-
|
|
6068
|
+
logger4.info("Connecting", { apiUrl: this.config.apiUrl, projectId: this.config.projectId });
|
|
5799
6069
|
this.socket = io2(this.config.apiUrl, {
|
|
5800
6070
|
auth: {
|
|
5801
6071
|
projectToken: this.config.projectToken,
|
|
@@ -5815,7 +6085,7 @@ var ProjectConnection = class {
|
|
|
5815
6085
|
settled = true;
|
|
5816
6086
|
resolve2();
|
|
5817
6087
|
}
|
|
5818
|
-
|
|
6088
|
+
logger4.info("Connected to API");
|
|
5819
6089
|
});
|
|
5820
6090
|
this.socket.on("connect_error", (error) => {
|
|
5821
6091
|
attempts++;
|
|
@@ -5827,10 +6097,10 @@ var ProjectConnection = class {
|
|
|
5827
6097
|
}
|
|
5828
6098
|
});
|
|
5829
6099
|
this.socket.on("disconnect", (reason) => {
|
|
5830
|
-
|
|
6100
|
+
logger4.warn("Disconnected from API", { reason });
|
|
5831
6101
|
});
|
|
5832
6102
|
this.socket.io.on("reconnect", () => {
|
|
5833
|
-
|
|
6103
|
+
logger4.info("Reconnected to API");
|
|
5834
6104
|
for (const cb of this.reconnectCallbacks) cb();
|
|
5835
6105
|
});
|
|
5836
6106
|
});
|
|
@@ -5977,12 +6247,14 @@ function runStartCommand(cmd, cwd, onOutput) {
|
|
|
5977
6247
|
|
|
5978
6248
|
// src/runner/commit-watcher.ts
|
|
5979
6249
|
import { execSync as execSync3 } from "child_process";
|
|
5980
|
-
var
|
|
6250
|
+
var logger5 = createServiceLogger("CommitWatcher");
|
|
5981
6251
|
var CommitWatcher = class {
|
|
5982
6252
|
constructor(config, callbacks) {
|
|
5983
6253
|
this.config = config;
|
|
5984
6254
|
this.callbacks = callbacks;
|
|
5985
6255
|
}
|
|
6256
|
+
config;
|
|
6257
|
+
callbacks;
|
|
5986
6258
|
interval = null;
|
|
5987
6259
|
lastKnownRemoteSha = null;
|
|
5988
6260
|
branch = null;
|
|
@@ -5993,7 +6265,7 @@ var CommitWatcher = class {
|
|
|
5993
6265
|
this.branch = branch;
|
|
5994
6266
|
this.lastKnownRemoteSha = this.getLocalHeadSha();
|
|
5995
6267
|
this.interval = setInterval(() => void this.poll(), this.config.pollIntervalMs);
|
|
5996
|
-
|
|
6268
|
+
logger5.info("Commit watcher started", {
|
|
5997
6269
|
branch,
|
|
5998
6270
|
baseSha: this.lastKnownRemoteSha?.slice(0, 8)
|
|
5999
6271
|
});
|
|
@@ -6058,7 +6330,7 @@ var CommitWatcher = class {
|
|
|
6058
6330
|
}
|
|
6059
6331
|
this.lastKnownRemoteSha = remoteSha;
|
|
6060
6332
|
this.isSyncing = true;
|
|
6061
|
-
|
|
6333
|
+
logger5.info("New commits detected", {
|
|
6062
6334
|
branch: this.branch,
|
|
6063
6335
|
commitCount,
|
|
6064
6336
|
sha: remoteSha.slice(0, 8)
|
|
@@ -6074,7 +6346,7 @@ var CommitWatcher = class {
|
|
|
6074
6346
|
});
|
|
6075
6347
|
} catch (err) {
|
|
6076
6348
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6077
|
-
|
|
6349
|
+
logger5.error("Error handling new commits", { error: msg });
|
|
6078
6350
|
} finally {
|
|
6079
6351
|
this.isSyncing = false;
|
|
6080
6352
|
}
|
|
@@ -6311,7 +6583,7 @@ function buildProjectTools(connection) {
|
|
|
6311
6583
|
}
|
|
6312
6584
|
|
|
6313
6585
|
// src/runner/project-chat-handler.ts
|
|
6314
|
-
var
|
|
6586
|
+
var logger6 = createServiceLogger("ProjectChat");
|
|
6315
6587
|
var FALLBACK_MODEL = "claude-sonnet-4-20250514";
|
|
6316
6588
|
function buildSystemPrompt2(projectDir, agentCtx) {
|
|
6317
6589
|
const parts = [];
|
|
@@ -6358,7 +6630,7 @@ async function fetchContext(connection, chatId) {
|
|
|
6358
6630
|
projectId: connection.projectId
|
|
6359
6631
|
});
|
|
6360
6632
|
} catch {
|
|
6361
|
-
|
|
6633
|
+
logger6.warn("Could not fetch agent context, using defaults");
|
|
6362
6634
|
}
|
|
6363
6635
|
let chatHistory = [];
|
|
6364
6636
|
try {
|
|
@@ -6368,7 +6640,7 @@ async function fetchContext(connection, chatId) {
|
|
|
6368
6640
|
chatId
|
|
6369
6641
|
});
|
|
6370
6642
|
} catch {
|
|
6371
|
-
|
|
6643
|
+
logger6.warn("Could not fetch chat history, proceeding without it");
|
|
6372
6644
|
}
|
|
6373
6645
|
return { agentCtx, chatHistory };
|
|
6374
6646
|
}
|
|
@@ -6486,7 +6758,7 @@ async function handleProjectChatMessage(message, connection, projectDir, session
|
|
|
6486
6758
|
return await runChatQuery(message, connection, projectDir, sessionId);
|
|
6487
6759
|
} catch (error) {
|
|
6488
6760
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6489
|
-
|
|
6761
|
+
logger6.error("Failed to handle message", { error: msg });
|
|
6490
6762
|
try {
|
|
6491
6763
|
await connection.call("postProjectAgentMessage", {
|
|
6492
6764
|
projectId: connection.projectId,
|
|
@@ -6502,7 +6774,7 @@ async function handleProjectChatMessage(message, connection, projectDir, session
|
|
|
6502
6774
|
}
|
|
6503
6775
|
|
|
6504
6776
|
// src/runner/project-runner.ts
|
|
6505
|
-
var
|
|
6777
|
+
var logger7 = createServiceLogger("ProjectRunner");
|
|
6506
6778
|
var __filename = fileURLToPath(import.meta.url);
|
|
6507
6779
|
var __dirname = path.dirname(__filename);
|
|
6508
6780
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
@@ -6521,7 +6793,7 @@ function setupWorkDir(projectDir, assignment) {
|
|
|
6521
6793
|
}
|
|
6522
6794
|
if (branch && branch !== devBranch) {
|
|
6523
6795
|
if (hasUncommittedChanges(workDir)) {
|
|
6524
|
-
|
|
6796
|
+
logger7.warn("Uncommitted changes, skipping checkout", { taskId: shortId, branch });
|
|
6525
6797
|
} else {
|
|
6526
6798
|
try {
|
|
6527
6799
|
execSync5(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
|
|
@@ -6529,7 +6801,7 @@ function setupWorkDir(projectDir, assignment) {
|
|
|
6529
6801
|
try {
|
|
6530
6802
|
execSync5(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
|
|
6531
6803
|
} catch {
|
|
6532
|
-
|
|
6804
|
+
logger7.warn("Could not checkout branch", { taskId: shortId, branch });
|
|
6533
6805
|
}
|
|
6534
6806
|
}
|
|
6535
6807
|
}
|
|
@@ -6545,7 +6817,7 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
6545
6817
|
const effectiveAgentMode = agentMode ?? (isAuto ? "auto" : "");
|
|
6546
6818
|
const effectiveApiUrl = apiUrl || process.env.CONVEYOR_API_URL || "";
|
|
6547
6819
|
if (!effectiveApiUrl) {
|
|
6548
|
-
|
|
6820
|
+
logger7.error("No API URL available for child agent", { taskId: taskId.slice(0, 8) });
|
|
6549
6821
|
}
|
|
6550
6822
|
const child = fork(cliPath, [], {
|
|
6551
6823
|
env: {
|
|
@@ -6573,12 +6845,12 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
6573
6845
|
const shortId = taskId.slice(0, 8);
|
|
6574
6846
|
child.stdout?.on("data", (data) => {
|
|
6575
6847
|
for (const line of data.toString().trimEnd().split("\n")) {
|
|
6576
|
-
|
|
6848
|
+
logger7.info(line, { taskId: shortId });
|
|
6577
6849
|
}
|
|
6578
6850
|
});
|
|
6579
6851
|
child.stderr?.on("data", (data) => {
|
|
6580
6852
|
for (const line of data.toString().trimEnd().split("\n")) {
|
|
6581
|
-
|
|
6853
|
+
logger7.info(line, { taskId: shortId });
|
|
6582
6854
|
}
|
|
6583
6855
|
});
|
|
6584
6856
|
return child;
|
|
@@ -6626,7 +6898,7 @@ var ProjectRunner = class {
|
|
|
6626
6898
|
capabilities: ["task", "pm", "code-review", "audit"]
|
|
6627
6899
|
});
|
|
6628
6900
|
this.branchSwitchCommand = registration.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
6629
|
-
|
|
6901
|
+
logger7.info("Registered as project agent", { agentName: registration.agentName });
|
|
6630
6902
|
try {
|
|
6631
6903
|
await this.executeSetupCommand();
|
|
6632
6904
|
this.executeStartCommand();
|
|
@@ -6637,7 +6909,7 @@ var ProjectRunner = class {
|
|
|
6637
6909
|
});
|
|
6638
6910
|
} catch (error) {
|
|
6639
6911
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6640
|
-
|
|
6912
|
+
logger7.error("Environment setup failed", { error: msg });
|
|
6641
6913
|
this.setupComplete = false;
|
|
6642
6914
|
}
|
|
6643
6915
|
this.wireEventHandlers();
|
|
@@ -6649,7 +6921,7 @@ var ProjectRunner = class {
|
|
|
6649
6921
|
if (currentBranch) {
|
|
6650
6922
|
this.commitWatcher.start(currentBranch);
|
|
6651
6923
|
}
|
|
6652
|
-
|
|
6924
|
+
logger7.info("Connected, waiting for task assignments");
|
|
6653
6925
|
await new Promise((resolve2) => {
|
|
6654
6926
|
this.resolveLifecycle = resolve2;
|
|
6655
6927
|
});
|
|
@@ -6657,7 +6929,7 @@ var ProjectRunner = class {
|
|
|
6657
6929
|
async stop() {
|
|
6658
6930
|
if (this.stopping) return;
|
|
6659
6931
|
this.stopping = true;
|
|
6660
|
-
|
|
6932
|
+
logger7.info("Shutting down");
|
|
6661
6933
|
this.commitWatcher.stop();
|
|
6662
6934
|
await this.killStartCommand();
|
|
6663
6935
|
if (this.heartbeatTimer) {
|
|
@@ -6688,7 +6960,7 @@ var ProjectRunner = class {
|
|
|
6688
6960
|
} catch {
|
|
6689
6961
|
}
|
|
6690
6962
|
this.connection.disconnect();
|
|
6691
|
-
|
|
6963
|
+
logger7.info("Shutdown complete");
|
|
6692
6964
|
if (this.resolveLifecycle) {
|
|
6693
6965
|
this.resolveLifecycle();
|
|
6694
6966
|
this.resolveLifecycle = null;
|
|
@@ -6724,10 +6996,10 @@ var ProjectRunner = class {
|
|
|
6724
6996
|
capabilities: ["task", "pm", "code-review", "audit"]
|
|
6725
6997
|
});
|
|
6726
6998
|
this.connection.emitStatus(this.activeAgents.size > 0 ? "busy" : "idle");
|
|
6727
|
-
|
|
6999
|
+
logger7.info("Re-registered after reconnect");
|
|
6728
7000
|
} catch (error) {
|
|
6729
7001
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6730
|
-
|
|
7002
|
+
logger7.error("Failed to re-register after reconnect", { error: msg });
|
|
6731
7003
|
}
|
|
6732
7004
|
}
|
|
6733
7005
|
// ── Tag audit ──────────────────────────────────────────────────────────
|
|
@@ -6738,7 +7010,7 @@ var ProjectRunner = class {
|
|
|
6738
7010
|
await handleTagAudit(request, this.connection, this.projectDir);
|
|
6739
7011
|
} catch (error) {
|
|
6740
7012
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6741
|
-
|
|
7013
|
+
logger7.error("Tag audit failed", { error: msg, requestId: request.requestId });
|
|
6742
7014
|
try {
|
|
6743
7015
|
await this.connection.call("reportTagAuditResult", {
|
|
6744
7016
|
projectId: this.connection.projectId,
|
|
@@ -6757,15 +7029,15 @@ var ProjectRunner = class {
|
|
|
6757
7029
|
async killAgent(agent, taskId) {
|
|
6758
7030
|
const shortId = taskId.slice(0, 8);
|
|
6759
7031
|
if (agent.process.exitCode !== null) {
|
|
6760
|
-
|
|
7032
|
+
logger7.info("Agent process already exited", { taskId: shortId });
|
|
6761
7033
|
return;
|
|
6762
7034
|
}
|
|
6763
|
-
|
|
7035
|
+
logger7.info("Killing agent process", { taskId: shortId });
|
|
6764
7036
|
agent.process.kill("SIGTERM");
|
|
6765
7037
|
await new Promise((resolve2) => {
|
|
6766
7038
|
const timer = setTimeout(() => {
|
|
6767
7039
|
if (agent.process.exitCode === null) {
|
|
6768
|
-
|
|
7040
|
+
logger7.warn("Agent did not exit after SIGTERM, sending SIGKILL", { taskId: shortId });
|
|
6769
7041
|
agent.process.kill("SIGKILL");
|
|
6770
7042
|
}
|
|
6771
7043
|
resolve2();
|
|
@@ -6784,15 +7056,15 @@ var ProjectRunner = class {
|
|
|
6784
7056
|
const existing = this.activeAgents.get(agentKey);
|
|
6785
7057
|
if (existing) {
|
|
6786
7058
|
if (existing.process.exitCode === null) {
|
|
6787
|
-
|
|
7059
|
+
logger7.info("Re-assignment received, killing existing agent", { taskId: shortId });
|
|
6788
7060
|
await this.killAgent(existing, taskId);
|
|
6789
7061
|
} else {
|
|
6790
|
-
|
|
7062
|
+
logger7.info("Stale agent entry (process already exited), cleaning up", { taskId: shortId });
|
|
6791
7063
|
}
|
|
6792
7064
|
this.activeAgents.delete(agentKey);
|
|
6793
7065
|
}
|
|
6794
7066
|
if (this.activeAgents.size >= MAX_CONCURRENT) {
|
|
6795
|
-
|
|
7067
|
+
logger7.warn("Max concurrent agents reached", { maxConcurrent: MAX_CONCURRENT });
|
|
6796
7068
|
this.connection.emitTaskStopped(taskId, "max_concurrent_reached");
|
|
6797
7069
|
return;
|
|
6798
7070
|
}
|
|
@@ -6800,9 +7072,12 @@ var ProjectRunner = class {
|
|
|
6800
7072
|
try {
|
|
6801
7073
|
execSync5("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
|
|
6802
7074
|
} catch {
|
|
6803
|
-
|
|
7075
|
+
logger7.warn("Git fetch failed", { taskId: shortId });
|
|
6804
7076
|
}
|
|
6805
7077
|
const { workDir, usesWorktree } = setupWorkDir(this.projectDir, assignment);
|
|
7078
|
+
if (assignment.devBranch) {
|
|
7079
|
+
syncWithBaseBranch(workDir, assignment.devBranch);
|
|
7080
|
+
}
|
|
6806
7081
|
const child = spawnChildAgent(assignment, workDir);
|
|
6807
7082
|
this.activeAgents.set(agentKey, {
|
|
6808
7083
|
process: child,
|
|
@@ -6811,12 +7086,12 @@ var ProjectRunner = class {
|
|
|
6811
7086
|
usesWorktree
|
|
6812
7087
|
});
|
|
6813
7088
|
this.connection.emitTaskStarted(taskId);
|
|
6814
|
-
|
|
7089
|
+
logger7.info("Started task", { taskId: shortId, mode, workDir });
|
|
6815
7090
|
child.on("exit", (code) => {
|
|
6816
7091
|
this.activeAgents.delete(agentKey);
|
|
6817
7092
|
const reason = code === 0 ? "completed" : `exited with code ${code}`;
|
|
6818
7093
|
this.connection.emitTaskStopped(taskId, reason);
|
|
6819
|
-
|
|
7094
|
+
logger7.info("Task exited", { taskId: shortId, reason });
|
|
6820
7095
|
if (code === 0 && usesWorktree) {
|
|
6821
7096
|
try {
|
|
6822
7097
|
removeWorktree(this.projectDir, taskId);
|
|
@@ -6826,7 +7101,7 @@ var ProjectRunner = class {
|
|
|
6826
7101
|
});
|
|
6827
7102
|
} catch (error) {
|
|
6828
7103
|
const msg = error instanceof Error ? error.message : "Unknown";
|
|
6829
|
-
|
|
7104
|
+
logger7.error("Failed to start task", { taskId: shortId, error: msg });
|
|
6830
7105
|
this.connection.emitTaskStopped(taskId, `start_failed: ${msg}`);
|
|
6831
7106
|
}
|
|
6832
7107
|
}
|
|
@@ -6834,7 +7109,7 @@ var ProjectRunner = class {
|
|
|
6834
7109
|
const agentKey = this.activeAgents.has(taskId) ? taskId : `${taskId}:code-review`;
|
|
6835
7110
|
const agent = this.activeAgents.get(agentKey);
|
|
6836
7111
|
if (!agent) return;
|
|
6837
|
-
|
|
7112
|
+
logger7.info("Stopping task", { taskId: taskId.slice(0, 8) });
|
|
6838
7113
|
void this.killAgent(agent, taskId).then(() => {
|
|
6839
7114
|
if (agent.usesWorktree) {
|
|
6840
7115
|
try {
|
|
@@ -6851,30 +7126,30 @@ var ProjectRunner = class {
|
|
|
6851
7126
|
const currentBranch = this.getCurrentBranch();
|
|
6852
7127
|
if (currentBranch === workspaceBranch) return;
|
|
6853
7128
|
if (hasUncommittedChanges(this.projectDir)) {
|
|
6854
|
-
|
|
7129
|
+
logger7.warn("Uncommitted changes, skipping workspace branch checkout");
|
|
6855
7130
|
return;
|
|
6856
7131
|
}
|
|
6857
7132
|
try {
|
|
6858
7133
|
execSync5(`git fetch origin ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
6859
7134
|
execSync5(`git checkout ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
6860
|
-
|
|
7135
|
+
logger7.info("Checked out workspace branch", { workspaceBranch });
|
|
6861
7136
|
} catch {
|
|
6862
|
-
|
|
7137
|
+
logger7.warn("Failed to checkout workspace branch", { workspaceBranch });
|
|
6863
7138
|
}
|
|
6864
7139
|
}
|
|
6865
7140
|
async executeSetupCommand() {
|
|
6866
7141
|
const cmd = process.env.CONVEYOR_SETUP_COMMAND;
|
|
6867
7142
|
if (!cmd) return;
|
|
6868
|
-
|
|
7143
|
+
logger7.info("Running setup command", { command: cmd });
|
|
6869
7144
|
try {
|
|
6870
7145
|
await runSetupCommand(cmd, this.projectDir, (stream, data) => {
|
|
6871
7146
|
this.connection.sendEvent({ type: "setup_output", stream, data });
|
|
6872
7147
|
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
6873
7148
|
});
|
|
6874
|
-
|
|
7149
|
+
logger7.info("Setup command completed");
|
|
6875
7150
|
} catch (error) {
|
|
6876
7151
|
const msg = error instanceof Error ? error.message : "Setup command failed";
|
|
6877
|
-
|
|
7152
|
+
logger7.error("Setup command failed", { error: msg });
|
|
6878
7153
|
this.connection.sendEvent({ type: "setup_error", message: msg });
|
|
6879
7154
|
throw error;
|
|
6880
7155
|
}
|
|
@@ -6882,7 +7157,7 @@ var ProjectRunner = class {
|
|
|
6882
7157
|
executeStartCommand() {
|
|
6883
7158
|
const cmd = process.env.CONVEYOR_START_COMMAND;
|
|
6884
7159
|
if (!cmd) return;
|
|
6885
|
-
|
|
7160
|
+
logger7.info("Running start command", { command: cmd });
|
|
6886
7161
|
const child = runStartCommand(cmd, this.projectDir, (stream, data) => {
|
|
6887
7162
|
this.connection.sendEvent({ type: "start_command_output", stream, data });
|
|
6888
7163
|
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
@@ -6892,13 +7167,13 @@ var ProjectRunner = class {
|
|
|
6892
7167
|
child.on("exit", (code, signal) => {
|
|
6893
7168
|
this.startCommandRunning = false;
|
|
6894
7169
|
this.startCommandChild = null;
|
|
6895
|
-
|
|
7170
|
+
logger7.info("Start command exited", { code, signal });
|
|
6896
7171
|
this.connection.sendEvent({ type: "start_command_exited", code, signal });
|
|
6897
7172
|
});
|
|
6898
7173
|
child.on("error", (err) => {
|
|
6899
7174
|
this.startCommandRunning = false;
|
|
6900
7175
|
this.startCommandChild = null;
|
|
6901
|
-
|
|
7176
|
+
logger7.error("Start command error", { error: err.message });
|
|
6902
7177
|
});
|
|
6903
7178
|
}
|
|
6904
7179
|
async killStartCommand() {
|
|
@@ -6941,7 +7216,7 @@ var ProjectRunner = class {
|
|
|
6941
7216
|
try {
|
|
6942
7217
|
execSync5("git fetch origin", { cwd: this.projectDir, stdio: "pipe" });
|
|
6943
7218
|
} catch {
|
|
6944
|
-
|
|
7219
|
+
logger7.warn("Git fetch failed during branch switch");
|
|
6945
7220
|
}
|
|
6946
7221
|
detachWorktreeBranch(this.projectDir, data.branch);
|
|
6947
7222
|
try {
|
|
@@ -6954,7 +7229,7 @@ var ProjectRunner = class {
|
|
|
6954
7229
|
try {
|
|
6955
7230
|
execSync5(`git pull origin ${data.branch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
6956
7231
|
} catch {
|
|
6957
|
-
|
|
7232
|
+
logger7.warn("Git pull failed during branch switch");
|
|
6958
7233
|
}
|
|
6959
7234
|
if (data.syncAfter !== false) {
|
|
6960
7235
|
await this.handleSyncEnvironment();
|
|
@@ -6963,7 +7238,7 @@ var ProjectRunner = class {
|
|
|
6963
7238
|
callback({ ok: true });
|
|
6964
7239
|
} catch (err) {
|
|
6965
7240
|
const msg = err instanceof Error ? err.message : "Branch switch failed";
|
|
6966
|
-
|
|
7241
|
+
logger7.error("Branch switch failed", { error: msg });
|
|
6967
7242
|
callback({ ok: false, error: msg });
|
|
6968
7243
|
}
|
|
6969
7244
|
}
|
|
@@ -6978,14 +7253,14 @@ var ProjectRunner = class {
|
|
|
6978
7253
|
});
|
|
6979
7254
|
} catch (err) {
|
|
6980
7255
|
const msg = err instanceof Error ? err.message : "Sync command failed";
|
|
6981
|
-
|
|
7256
|
+
logger7.error("Branch switch sync command failed", { error: msg });
|
|
6982
7257
|
}
|
|
6983
7258
|
}
|
|
6984
7259
|
this.executeStartCommand();
|
|
6985
7260
|
callback?.({ ok: true });
|
|
6986
7261
|
} catch (err) {
|
|
6987
7262
|
const msg = err instanceof Error ? err.message : "Sync failed";
|
|
6988
|
-
|
|
7263
|
+
logger7.error("Environment sync failed", { error: msg });
|
|
6989
7264
|
callback?.({ ok: false, error: msg });
|
|
6990
7265
|
}
|
|
6991
7266
|
}
|
|
@@ -7010,7 +7285,7 @@ var ProjectRunner = class {
|
|
|
7010
7285
|
setupComplete: this.setupComplete,
|
|
7011
7286
|
startCommandRunning: this.startCommandRunning
|
|
7012
7287
|
});
|
|
7013
|
-
|
|
7288
|
+
logger7.info("Commit sync complete", { steps: stepsRun.join(", ") });
|
|
7014
7289
|
}
|
|
7015
7290
|
async smartSync(previousSha, newSha, branch) {
|
|
7016
7291
|
if (hasUncommittedChanges(this.projectDir)) {
|
|
@@ -7029,7 +7304,7 @@ var ProjectRunner = class {
|
|
|
7029
7304
|
});
|
|
7030
7305
|
} catch (err) {
|
|
7031
7306
|
const msg = err instanceof Error ? err.message : "Pull failed";
|
|
7032
|
-
|
|
7307
|
+
logger7.error("Git pull failed during commit sync", { error: msg });
|
|
7033
7308
|
this.executeStartCommand();
|
|
7034
7309
|
return ["error:pull"];
|
|
7035
7310
|
}
|
|
@@ -7056,7 +7331,7 @@ var ProjectRunner = class {
|
|
|
7056
7331
|
stepsRun.push("branchSwitchCommand");
|
|
7057
7332
|
} catch (err) {
|
|
7058
7333
|
const msg = err instanceof Error ? err.message : "Sync command failed";
|
|
7059
|
-
|
|
7334
|
+
logger7.error("Branch switch command failed", { error: msg });
|
|
7060
7335
|
}
|
|
7061
7336
|
} else if (!cmd) {
|
|
7062
7337
|
this.runIndividualSyncSteps(needsInstall, needsPrisma, stepsRun);
|
|
@@ -7069,7 +7344,7 @@ var ProjectRunner = class {
|
|
|
7069
7344
|
stepsRun.push("install");
|
|
7070
7345
|
} catch (err) {
|
|
7071
7346
|
const msg = err instanceof Error ? err.message : "Install failed";
|
|
7072
|
-
|
|
7347
|
+
logger7.error("bun install failed", { error: msg });
|
|
7073
7348
|
}
|
|
7074
7349
|
}
|
|
7075
7350
|
if (needsPrisma) {
|
|
@@ -7083,7 +7358,7 @@ var ProjectRunner = class {
|
|
|
7083
7358
|
stepsRun.push("prisma");
|
|
7084
7359
|
} catch (err) {
|
|
7085
7360
|
const msg = err instanceof Error ? err.message : "Prisma sync failed";
|
|
7086
|
-
|
|
7361
|
+
logger7.error("Prisma sync failed", { error: msg });
|
|
7087
7362
|
}
|
|
7088
7363
|
}
|
|
7089
7364
|
}
|
|
@@ -7099,6 +7374,33 @@ var ProjectRunner = class {
|
|
|
7099
7374
|
}
|
|
7100
7375
|
};
|
|
7101
7376
|
|
|
7377
|
+
// src/setup/config.ts
|
|
7378
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
7379
|
+
import { join as join3 } from "path";
|
|
7380
|
+
var DEVCONTAINER_PATH = ".devcontainer/conveyor/devcontainer.json";
|
|
7381
|
+
async function loadForwardPorts(workspaceDir) {
|
|
7382
|
+
try {
|
|
7383
|
+
const raw = await readFile2(join3(workspaceDir, DEVCONTAINER_PATH), "utf-8");
|
|
7384
|
+
const parsed = JSON.parse(raw);
|
|
7385
|
+
return parsed.forwardPorts ?? [];
|
|
7386
|
+
} catch {
|
|
7387
|
+
return [];
|
|
7388
|
+
}
|
|
7389
|
+
}
|
|
7390
|
+
function loadConveyorConfig() {
|
|
7391
|
+
const envSetup = process.env.CONVEYOR_SETUP_COMMAND;
|
|
7392
|
+
const envStart = process.env.CONVEYOR_START_COMMAND;
|
|
7393
|
+
const envPort = process.env.CONVEYOR_PREVIEW_PORT;
|
|
7394
|
+
if (envSetup || envStart) {
|
|
7395
|
+
return {
|
|
7396
|
+
setupCommand: envSetup,
|
|
7397
|
+
startCommand: envStart,
|
|
7398
|
+
previewPort: envPort ? Number(envPort) : void 0
|
|
7399
|
+
};
|
|
7400
|
+
}
|
|
7401
|
+
return null;
|
|
7402
|
+
}
|
|
7403
|
+
|
|
7102
7404
|
export {
|
|
7103
7405
|
AgentConnection,
|
|
7104
7406
|
ModeController,
|
|
@@ -7121,6 +7423,8 @@ export {
|
|
|
7121
7423
|
ensureWorktree,
|
|
7122
7424
|
detachWorktreeBranch,
|
|
7123
7425
|
removeWorktree,
|
|
7124
|
-
ProjectRunner
|
|
7426
|
+
ProjectRunner,
|
|
7427
|
+
loadForwardPorts,
|
|
7428
|
+
loadConveyorConfig
|
|
7125
7429
|
};
|
|
7126
|
-
//# sourceMappingURL=chunk-
|
|
7430
|
+
//# sourceMappingURL=chunk-3H2FXJFD.js.map
|