@rallycry/conveyor-agent 3.9.0 → 4.0.1
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-6Z262BNH.js → chunk-Q2LN2YBW.js} +805 -138
- package/dist/chunk-Q2LN2YBW.js.map +1 -0
- package/dist/cli.js +3 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +40 -4
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-6Z262BNH.js.map +0 -1
|
@@ -12,6 +12,7 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
12
12
|
chatMessageCallback = null;
|
|
13
13
|
stopCallback = null;
|
|
14
14
|
modeChangeCallback = null;
|
|
15
|
+
runStartCommandCallback = null;
|
|
15
16
|
pendingQuestionResolvers = /* @__PURE__ */ new Map();
|
|
16
17
|
constructor(config) {
|
|
17
18
|
this.config = config;
|
|
@@ -61,6 +62,11 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
61
62
|
this.earlyModeChanges.push(data);
|
|
62
63
|
}
|
|
63
64
|
});
|
|
65
|
+
this.socket.on("agentRunner:runStartCommand", () => {
|
|
66
|
+
if (this.runStartCommandCallback) {
|
|
67
|
+
this.runStartCommandCallback();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
64
70
|
this.socket.on("connect", () => {
|
|
65
71
|
if (!settled) {
|
|
66
72
|
settled = true;
|
|
@@ -76,13 +82,13 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
76
82
|
});
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
|
-
fetchChatMessages(limit) {
|
|
85
|
+
fetchChatMessages(limit, taskId) {
|
|
80
86
|
const socket = this.socket;
|
|
81
87
|
if (!socket) throw new Error("Not connected");
|
|
82
88
|
return new Promise((resolve2, reject) => {
|
|
83
89
|
socket.emit(
|
|
84
90
|
"agentRunner:getChatMessages",
|
|
85
|
-
{ limit },
|
|
91
|
+
{ limit, taskId },
|
|
86
92
|
(response) => {
|
|
87
93
|
if (response.success && response.data) {
|
|
88
94
|
resolve2(response.data);
|
|
@@ -226,9 +232,12 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
226
232
|
}
|
|
227
233
|
this.earlyModeChanges = [];
|
|
228
234
|
}
|
|
229
|
-
|
|
235
|
+
onRunStartCommand(callback) {
|
|
236
|
+
this.runStartCommandCallback = callback;
|
|
237
|
+
}
|
|
238
|
+
emitModeChanged(agentMode) {
|
|
230
239
|
if (!this.socket) return;
|
|
231
|
-
this.socket.emit("agentRunner:modeChanged", {
|
|
240
|
+
this.socket.emit("agentRunner:modeChanged", { agentMode });
|
|
232
241
|
}
|
|
233
242
|
trackSpending(params) {
|
|
234
243
|
if (!this.socket) throw new Error("Not connected");
|
|
@@ -330,6 +339,48 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
330
339
|
);
|
|
331
340
|
});
|
|
332
341
|
}
|
|
342
|
+
postChildChatMessage(childTaskId, content) {
|
|
343
|
+
const socket = this.socket;
|
|
344
|
+
if (!socket) throw new Error("Not connected");
|
|
345
|
+
return new Promise((resolve2, reject) => {
|
|
346
|
+
socket.emit(
|
|
347
|
+
"agentRunner:postChildChatMessage",
|
|
348
|
+
{ childTaskId, content },
|
|
349
|
+
(response) => {
|
|
350
|
+
if (response.success) resolve2();
|
|
351
|
+
else reject(new Error(response.error ?? "Failed to post to child chat"));
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
updateChildStatus(childTaskId, status) {
|
|
357
|
+
const socket = this.socket;
|
|
358
|
+
if (!socket) throw new Error("Not connected");
|
|
359
|
+
return new Promise((resolve2, reject) => {
|
|
360
|
+
socket.emit(
|
|
361
|
+
"agentRunner:updateChildStatus",
|
|
362
|
+
{ childTaskId, status },
|
|
363
|
+
(response) => {
|
|
364
|
+
if (response.success) resolve2();
|
|
365
|
+
else reject(new Error(response.error ?? "Failed to update child status"));
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
stopChildBuild(childTaskId) {
|
|
371
|
+
const socket = this.socket;
|
|
372
|
+
if (!socket) throw new Error("Not connected");
|
|
373
|
+
return new Promise((resolve2, reject) => {
|
|
374
|
+
socket.emit(
|
|
375
|
+
"agentRunner:stopChildBuild",
|
|
376
|
+
{ childTaskId },
|
|
377
|
+
(response) => {
|
|
378
|
+
if (response.success) resolve2();
|
|
379
|
+
else reject(new Error(response.error ?? "Failed to stop child build"));
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
});
|
|
383
|
+
}
|
|
333
384
|
fetchTask(slugOrId) {
|
|
334
385
|
const socket = this.socket;
|
|
335
386
|
if (!socket) throw new Error("Not connected");
|
|
@@ -347,6 +398,66 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
347
398
|
);
|
|
348
399
|
});
|
|
349
400
|
}
|
|
401
|
+
updateTaskProperties(data) {
|
|
402
|
+
if (!this.socket) throw new Error("Not connected");
|
|
403
|
+
this.socket.emit("agentRunner:updateTaskProperties", data);
|
|
404
|
+
}
|
|
405
|
+
listIcons() {
|
|
406
|
+
const socket = this.socket;
|
|
407
|
+
if (!socket) throw new Error("Not connected");
|
|
408
|
+
return new Promise((resolve2, reject) => {
|
|
409
|
+
socket.emit("agentRunner:listIcons", {}, (response) => {
|
|
410
|
+
if (response.success && response.data) resolve2(response.data);
|
|
411
|
+
else reject(new Error(response.error ?? "Failed to list icons"));
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
generateTaskIcon(prompt, aspectRatio) {
|
|
416
|
+
const socket = this.socket;
|
|
417
|
+
if (!socket) throw new Error("Not connected");
|
|
418
|
+
return new Promise((resolve2, reject) => {
|
|
419
|
+
socket.emit(
|
|
420
|
+
"agentRunner:generateTaskIcon",
|
|
421
|
+
{ prompt, aspectRatio },
|
|
422
|
+
(response) => {
|
|
423
|
+
if (response.success && response.data) resolve2(response.data);
|
|
424
|
+
else reject(new Error(response.error ?? "Failed to generate icon"));
|
|
425
|
+
}
|
|
426
|
+
);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
getTaskProperties() {
|
|
430
|
+
const socket = this.socket;
|
|
431
|
+
if (!socket) throw new Error("Not connected");
|
|
432
|
+
return new Promise((resolve2, reject) => {
|
|
433
|
+
socket.emit(
|
|
434
|
+
"agentRunner:getTaskProperties",
|
|
435
|
+
{},
|
|
436
|
+
(response) => {
|
|
437
|
+
if (response.success && response.data) resolve2(response.data);
|
|
438
|
+
else reject(new Error(response.error ?? "Failed to get task properties"));
|
|
439
|
+
}
|
|
440
|
+
);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
triggerIdentification() {
|
|
444
|
+
const socket = this.socket;
|
|
445
|
+
if (!socket) throw new Error("Not connected");
|
|
446
|
+
return new Promise((resolve2, reject) => {
|
|
447
|
+
socket.emit(
|
|
448
|
+
"agentRunner:triggerIdentification",
|
|
449
|
+
{},
|
|
450
|
+
(response) => {
|
|
451
|
+
if (response.success && response.data) resolve2(response.data);
|
|
452
|
+
else reject(new Error(response.error ?? "Identification failed"));
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
emitModeTransition(payload) {
|
|
458
|
+
if (!this.socket) return;
|
|
459
|
+
this.socket.emit("agentRunner:modeTransition", payload);
|
|
460
|
+
}
|
|
350
461
|
disconnect() {
|
|
351
462
|
this.flushEvents();
|
|
352
463
|
this.socket?.disconnect();
|
|
@@ -799,6 +910,13 @@ async function processEvents(events, context, host) {
|
|
|
799
910
|
const turnToolCalls = [];
|
|
800
911
|
for await (const event of events) {
|
|
801
912
|
if (host.isStopped()) break;
|
|
913
|
+
if (host.pendingModeRestart) {
|
|
914
|
+
host.pendingModeRestart = false;
|
|
915
|
+
if (isTyping) {
|
|
916
|
+
host.connection.sendTypingStop();
|
|
917
|
+
}
|
|
918
|
+
return { retriable: false, modeRestart: true };
|
|
919
|
+
}
|
|
802
920
|
switch (event.type) {
|
|
803
921
|
case "system": {
|
|
804
922
|
const systemEvent = event;
|
|
@@ -1007,12 +1125,22 @@ ${context.plan}`);
|
|
|
1007
1125
|
}
|
|
1008
1126
|
return parts;
|
|
1009
1127
|
}
|
|
1010
|
-
function buildInstructions(mode, context, scenario) {
|
|
1128
|
+
function buildInstructions(mode, context, scenario, agentMode) {
|
|
1011
1129
|
const parts = [`
|
|
1012
1130
|
## Instructions`];
|
|
1013
1131
|
const isPm = mode === "pm";
|
|
1132
|
+
const isAutoMode = agentMode === "auto";
|
|
1014
1133
|
if (scenario === "fresh") {
|
|
1015
|
-
if (
|
|
1134
|
+
if (isAutoMode && isPm) {
|
|
1135
|
+
parts.push(
|
|
1136
|
+
`You are operating autonomously. Begin planning immediately.`,
|
|
1137
|
+
`1. Explore the codebase to understand the architecture and relevant files`,
|
|
1138
|
+
`2. Draft a clear implementation plan and save it with update_task`,
|
|
1139
|
+
`3. Set story points (set_story_points), tags (set_task_tags), and title (set_task_title)`,
|
|
1140
|
+
`4. When the plan and all required properties are set, call ExitPlanMode to transition to building`,
|
|
1141
|
+
`Do NOT wait for team input \u2014 proceed autonomously.`
|
|
1142
|
+
);
|
|
1143
|
+
} else if (isPm && context.isParentTask) {
|
|
1016
1144
|
parts.push(
|
|
1017
1145
|
`You are the project manager for this task and its subtasks.`,
|
|
1018
1146
|
`Use list_subtasks to review the current state of child tasks.`,
|
|
@@ -1091,6 +1219,112 @@ Address the requested changes directly. Do NOT re-investigate the codebase from
|
|
|
1091
1219
|
}
|
|
1092
1220
|
return parts;
|
|
1093
1221
|
}
|
|
1222
|
+
function buildPropertyInstructions(context) {
|
|
1223
|
+
const parts = [];
|
|
1224
|
+
parts.push(
|
|
1225
|
+
``,
|
|
1226
|
+
`### Proactive Property Management`,
|
|
1227
|
+
`As you plan this task, proactively fill in task properties when you have enough context:`,
|
|
1228
|
+
`- Once you understand the scope, use set_story_points to assign a value`,
|
|
1229
|
+
`- Use set_task_tags to categorize the work`,
|
|
1230
|
+
`- For icons: FIRST call list_icons to check for existing matches. Use set_task_icon if one fits.`,
|
|
1231
|
+
` Only call generate_task_icon if no existing icon is a good fit.`,
|
|
1232
|
+
`- Use set_task_title if the current title doesn't accurately reflect the plan`,
|
|
1233
|
+
``,
|
|
1234
|
+
`Don't wait for the user to ask \u2014 fill these in naturally as the plan takes shape.`,
|
|
1235
|
+
`If the user adjusts the plan significantly, update the properties to match.`
|
|
1236
|
+
);
|
|
1237
|
+
if (context.storyPoints && context.storyPoints.length > 0) {
|
|
1238
|
+
parts.push(``, `Available story point tiers:`);
|
|
1239
|
+
for (const sp of context.storyPoints) {
|
|
1240
|
+
const desc = sp.description ? ` \u2014 ${sp.description}` : "";
|
|
1241
|
+
parts.push(`- Value ${sp.value}: "${sp.name}"${desc}`);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
if (context.projectTags && context.projectTags.length > 0) {
|
|
1245
|
+
parts.push(``, `Available project tags:`);
|
|
1246
|
+
for (const tag of context.projectTags) {
|
|
1247
|
+
parts.push(`- ID: "${tag.id}", Name: "${tag.name}"`);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return parts;
|
|
1251
|
+
}
|
|
1252
|
+
function buildModePrompt(agentMode, context) {
|
|
1253
|
+
switch (agentMode) {
|
|
1254
|
+
case "discovery": {
|
|
1255
|
+
const parts = [
|
|
1256
|
+
`
|
|
1257
|
+
## Mode: Discovery`,
|
|
1258
|
+
`You are in Discovery mode \u2014 helping plan and scope this task.`,
|
|
1259
|
+
`- You have read-only codebase access (can read files, run git commands, search code)`,
|
|
1260
|
+
`- You have MCP tools: update_task (title, description, plan, tags, SP, icon)`,
|
|
1261
|
+
`- You can create and manage subtasks`,
|
|
1262
|
+
`- You cannot write code or edit files (except .claude/plans/)`,
|
|
1263
|
+
`- Goal: collaborate with the user to create a clear plan`,
|
|
1264
|
+
`- Proactively fill task properties (SP, tags, icon) as the plan takes shape`
|
|
1265
|
+
];
|
|
1266
|
+
if (context) {
|
|
1267
|
+
parts.push(...buildPropertyInstructions(context));
|
|
1268
|
+
}
|
|
1269
|
+
return parts.join("\n");
|
|
1270
|
+
}
|
|
1271
|
+
case "building":
|
|
1272
|
+
return [
|
|
1273
|
+
`
|
|
1274
|
+
## Mode: Building`,
|
|
1275
|
+
`You are in Building mode \u2014 executing the plan.`,
|
|
1276
|
+
`- You have full coding access (read, write, edit, bash, git)`,
|
|
1277
|
+
`- Safety rules: no destructive operations, use --force-with-lease instead of --force`,
|
|
1278
|
+
`- If this is a leaf task (no children): execute the plan directly`,
|
|
1279
|
+
`- Goal: implement the plan, run tests, open a PR when done`
|
|
1280
|
+
].join("\n");
|
|
1281
|
+
case "review":
|
|
1282
|
+
return [
|
|
1283
|
+
`
|
|
1284
|
+
## Mode: Review`,
|
|
1285
|
+
`You are in Review mode \u2014 reviewing and coordinating.`,
|
|
1286
|
+
`- You have read-only access plus light edit capability (can suggest fixes, run tests, check linting)`,
|
|
1287
|
+
`- For parent tasks: you can manage children, review child PRs, fire next child builds`,
|
|
1288
|
+
`- You have Pack Runner coordination tools (list_subtasks, fire builds, approve PRs)`,
|
|
1289
|
+
`- Goal: ensure quality, provide feedback, coordinate progression`
|
|
1290
|
+
].join("\n");
|
|
1291
|
+
case "auto": {
|
|
1292
|
+
const parts = [
|
|
1293
|
+
`
|
|
1294
|
+
## Mode: Auto`,
|
|
1295
|
+
`You are in Auto mode \u2014 operating autonomously through planning \u2192 building \u2192 PR.`,
|
|
1296
|
+
``,
|
|
1297
|
+
`### Phase 1: Discovery & Planning (current)`,
|
|
1298
|
+
`- You have read-only codebase access (can read files, run git commands, search code)`,
|
|
1299
|
+
`- You can write plan files in .claude/plans/ only \u2014 no other file writes`,
|
|
1300
|
+
`- You have MCP tools for task properties: update_task, set_story_points, set_task_tags, set_task_icon, set_task_title`,
|
|
1301
|
+
``,
|
|
1302
|
+
`### Required before transitioning:`,
|
|
1303
|
+
`Before calling ExitPlanMode, you MUST fill in ALL of these:`,
|
|
1304
|
+
`1. **Plan** \u2014 Save a clear implementation plan using update_task`,
|
|
1305
|
+
`2. **Story Points** \u2014 Assign via set_story_points`,
|
|
1306
|
+
`3. **Title** \u2014 Set an accurate title via set_task_title (if the current one is vague or "Untitled")`,
|
|
1307
|
+
``,
|
|
1308
|
+
`### Transitioning to Building:`,
|
|
1309
|
+
`When your plan is complete and all required properties are set, call the **ExitPlanMode** tool.`,
|
|
1310
|
+
`- If any required properties are missing, ExitPlanMode will be denied with details on what's missing`,
|
|
1311
|
+
`- Once ExitPlanMode succeeds, the system will automatically restart your session in Building mode with the appropriate model`,
|
|
1312
|
+
`- You do NOT need to do anything after calling ExitPlanMode \u2014 the transition is handled for you`,
|
|
1313
|
+
``,
|
|
1314
|
+
`### Autonomous Guidelines:`,
|
|
1315
|
+
`- Make decisions independently \u2014 do not ask the team for approval at each step`,
|
|
1316
|
+
`- Only escalate when genuinely blocked (ambiguous requirements, missing access, conflicting instructions)`,
|
|
1317
|
+
`- Be thorough in discovery: read relevant files, understand the codebase architecture, then plan`
|
|
1318
|
+
];
|
|
1319
|
+
if (context) {
|
|
1320
|
+
parts.push(...buildPropertyInstructions(context));
|
|
1321
|
+
}
|
|
1322
|
+
return parts.join("\n");
|
|
1323
|
+
}
|
|
1324
|
+
default:
|
|
1325
|
+
return null;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1094
1328
|
function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
1095
1329
|
const parts = [
|
|
1096
1330
|
`You are an autonomous Pack Runner managing child tasks for the "${context.title}" project.`,
|
|
@@ -1201,7 +1435,7 @@ After addressing the feedback, resume your autonomous loop: call list_subtasks a
|
|
|
1201
1435
|
}
|
|
1202
1436
|
return parts;
|
|
1203
1437
|
}
|
|
1204
|
-
function buildInitialPrompt(mode, context, isAuto) {
|
|
1438
|
+
function buildInitialPrompt(mode, context, isAuto, agentMode) {
|
|
1205
1439
|
const isPackRunner = mode === "pm" && !!isAuto && !!context.isParentTask;
|
|
1206
1440
|
if (!isPackRunner) {
|
|
1207
1441
|
const sessionRelaunch = buildRelaunchWithSession(mode, context);
|
|
@@ -1209,12 +1443,12 @@ function buildInitialPrompt(mode, context, isAuto) {
|
|
|
1209
1443
|
}
|
|
1210
1444
|
const scenario = detectRelaunchScenario(context);
|
|
1211
1445
|
const body = buildTaskBody(context);
|
|
1212
|
-
const instructions = isPackRunner ? buildPackRunnerInstructions(context, scenario) : buildInstructions(mode, context, scenario);
|
|
1446
|
+
const instructions = isPackRunner ? buildPackRunnerInstructions(context, scenario) : buildInstructions(mode, context, scenario, agentMode);
|
|
1213
1447
|
return [...body, ...instructions].join("\n");
|
|
1214
1448
|
}
|
|
1215
|
-
function buildSystemPrompt(mode, context, config, setupLog,
|
|
1449
|
+
function buildSystemPrompt(mode, context, config, setupLog, agentMode) {
|
|
1216
1450
|
const isPm = mode === "pm";
|
|
1217
|
-
const isPmActive = isPm &&
|
|
1451
|
+
const isPmActive = isPm && agentMode === "building";
|
|
1218
1452
|
const isPackRunner = isPm && !!config.isAuto && !!context.isParentTask;
|
|
1219
1453
|
if (isPackRunner) {
|
|
1220
1454
|
return buildPackRunnerSystemPrompt(context, config, setupLog);
|
|
@@ -1340,6 +1574,10 @@ Your responses are sent directly to the task chat \u2014 the team sees everythin
|
|
|
1340
1574
|
`Use the create_pull_request tool to open PRs \u2014 do NOT use gh CLI or shell commands for PR creation.`
|
|
1341
1575
|
);
|
|
1342
1576
|
}
|
|
1577
|
+
const modePrompt = buildModePrompt(agentMode, context);
|
|
1578
|
+
if (modePrompt) {
|
|
1579
|
+
parts.push(modePrompt);
|
|
1580
|
+
}
|
|
1343
1581
|
return parts.join("\n");
|
|
1344
1582
|
}
|
|
1345
1583
|
|
|
@@ -1389,13 +1627,14 @@ function buildCommonTools(connection, config) {
|
|
|
1389
1627
|
return [
|
|
1390
1628
|
tool(
|
|
1391
1629
|
"read_task_chat",
|
|
1392
|
-
"Read recent messages from
|
|
1630
|
+
"Read recent messages from a task chat. Omit task_id to read the current task's chat, or provide a child task ID to read a child's chat.",
|
|
1393
1631
|
{
|
|
1394
|
-
limit: z.number().optional().describe("Number of recent messages to fetch (default 20)")
|
|
1632
|
+
limit: z.number().optional().describe("Number of recent messages to fetch (default 20)"),
|
|
1633
|
+
task_id: z.string().optional().describe("Child task ID to read chat from. Omit to read the current task's chat.")
|
|
1395
1634
|
},
|
|
1396
|
-
async ({ limit }) => {
|
|
1635
|
+
async ({ limit, task_id }) => {
|
|
1397
1636
|
try {
|
|
1398
|
-
const messages = await connection.fetchChatMessages(limit);
|
|
1637
|
+
const messages = await connection.fetchChatMessages(limit, task_id);
|
|
1399
1638
|
return textResult(JSON.stringify(messages, null, 2));
|
|
1400
1639
|
} catch {
|
|
1401
1640
|
return textResult(
|
|
@@ -1409,22 +1648,46 @@ function buildCommonTools(connection, config) {
|
|
|
1409
1648
|
),
|
|
1410
1649
|
tool(
|
|
1411
1650
|
"post_to_chat",
|
|
1412
|
-
"Post a message to
|
|
1413
|
-
{
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1651
|
+
"Post a message to a task chat. Your normal replies already appear in chat \u2014 only use this for explicit out-of-band updates or posting to a child task's chat.",
|
|
1652
|
+
{
|
|
1653
|
+
message: z.string().describe("The message to post to the team"),
|
|
1654
|
+
task_id: z.string().optional().describe("Child task ID to post to. Omit to post to the current task's chat.")
|
|
1655
|
+
},
|
|
1656
|
+
async ({ message, task_id }) => {
|
|
1657
|
+
try {
|
|
1658
|
+
if (task_id) {
|
|
1659
|
+
await connection.postChildChatMessage(task_id, message);
|
|
1660
|
+
return textResult(`Message posted to child task ${task_id} chat.`);
|
|
1661
|
+
}
|
|
1662
|
+
connection.postChatMessage(message);
|
|
1663
|
+
return textResult("Message posted to task chat.");
|
|
1664
|
+
} catch (error) {
|
|
1665
|
+
return textResult(
|
|
1666
|
+
`Failed to post message: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1417
1669
|
}
|
|
1418
1670
|
),
|
|
1419
1671
|
tool(
|
|
1420
1672
|
"update_task_status",
|
|
1421
|
-
"Update
|
|
1673
|
+
"Update a task's status on the Kanban board. Omit task_id to update the current task, or provide a child task ID to update a child's status.",
|
|
1422
1674
|
{
|
|
1423
|
-
status: z.enum(["InProgress", "ReviewPR", "Complete"]).describe("The new status for the task")
|
|
1675
|
+
status: z.enum(["InProgress", "ReviewPR", "ReviewDev", "Complete"]).describe("The new status for the task"),
|
|
1676
|
+
task_id: z.string().optional().describe("Child task ID to update. Omit to update the current task.")
|
|
1424
1677
|
},
|
|
1425
|
-
({ status }) => {
|
|
1426
|
-
|
|
1427
|
-
|
|
1678
|
+
async ({ status, task_id }) => {
|
|
1679
|
+
try {
|
|
1680
|
+
if (task_id) {
|
|
1681
|
+
await connection.updateChildStatus(task_id, status);
|
|
1682
|
+
return textResult(`Child task ${task_id} status updated to ${status}.`);
|
|
1683
|
+
}
|
|
1684
|
+
connection.updateStatus(status);
|
|
1685
|
+
return textResult(`Task status updated to ${status}.`);
|
|
1686
|
+
} catch (error) {
|
|
1687
|
+
return textResult(
|
|
1688
|
+
`Failed to update status: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1428
1691
|
}
|
|
1429
1692
|
),
|
|
1430
1693
|
tool(
|
|
@@ -1655,6 +1918,23 @@ function buildPmTools(connection, storyPoints, options) {
|
|
|
1655
1918
|
}
|
|
1656
1919
|
}
|
|
1657
1920
|
),
|
|
1921
|
+
tool2(
|
|
1922
|
+
"stop_child_build",
|
|
1923
|
+
"Stop a running cloud build for a child task. Sends a stop signal to the child agent.",
|
|
1924
|
+
{
|
|
1925
|
+
childTaskId: z2.string().describe("The child task ID whose build should be stopped")
|
|
1926
|
+
},
|
|
1927
|
+
async ({ childTaskId }) => {
|
|
1928
|
+
try {
|
|
1929
|
+
await connection.stopChildBuild(childTaskId);
|
|
1930
|
+
return textResult(`Stop signal sent to child task: ${childTaskId}`);
|
|
1931
|
+
} catch (error) {
|
|
1932
|
+
return textResult(
|
|
1933
|
+
`Failed to stop child build: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
),
|
|
1658
1938
|
tool2(
|
|
1659
1939
|
"approve_and_merge_pr",
|
|
1660
1940
|
"Approve and merge a child task's pull request. Only succeeds if all CI/CD checks are passing. Returns an error if checks are pending (retry after waiting) or failed (investigate). The child task must be in ReviewPR status.",
|
|
@@ -1707,6 +1987,117 @@ function buildTaskTools(connection) {
|
|
|
1707
1987
|
];
|
|
1708
1988
|
}
|
|
1709
1989
|
|
|
1990
|
+
// src/tools/discovery-tools.ts
|
|
1991
|
+
import { tool as tool4 } from "@anthropic-ai/claude-agent-sdk";
|
|
1992
|
+
import { z as z4 } from "zod";
|
|
1993
|
+
function buildDiscoveryTools(connection, context) {
|
|
1994
|
+
const spDescription = buildStoryPointDescription(context?.storyPoints);
|
|
1995
|
+
return [
|
|
1996
|
+
tool4(
|
|
1997
|
+
"set_story_points",
|
|
1998
|
+
"Set the story point estimate for this task. Use after understanding the scope of the work.",
|
|
1999
|
+
{ value: z4.number().describe(spDescription) },
|
|
2000
|
+
async ({ value }) => {
|
|
2001
|
+
try {
|
|
2002
|
+
connection.updateTaskProperties({ storyPointValue: value });
|
|
2003
|
+
return textResult(`Story points set to ${value}`);
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
return textResult(
|
|
2006
|
+
`Failed to set story points: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2007
|
+
);
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
),
|
|
2011
|
+
tool4(
|
|
2012
|
+
"set_task_tags",
|
|
2013
|
+
"Assign tags to this task from the project's available tags. Use the tag IDs from the project tags list.",
|
|
2014
|
+
{
|
|
2015
|
+
tagIds: z4.array(z4.string()).describe("Array of tag IDs to assign")
|
|
2016
|
+
},
|
|
2017
|
+
async ({ tagIds }) => {
|
|
2018
|
+
try {
|
|
2019
|
+
connection.updateTaskProperties({ tagIds });
|
|
2020
|
+
return textResult(`Tags assigned: ${tagIds.length} tag(s)`);
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
return textResult(
|
|
2023
|
+
`Failed to set tags: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
),
|
|
2028
|
+
tool4(
|
|
2029
|
+
"set_task_title",
|
|
2030
|
+
"Update the task title to better reflect the planned work.",
|
|
2031
|
+
{
|
|
2032
|
+
title: z4.string().describe("The new task title")
|
|
2033
|
+
},
|
|
2034
|
+
async ({ title }) => {
|
|
2035
|
+
try {
|
|
2036
|
+
connection.updateTaskProperties({ title });
|
|
2037
|
+
return textResult(`Task title updated to: ${title}`);
|
|
2038
|
+
} catch (error) {
|
|
2039
|
+
return textResult(
|
|
2040
|
+
`Failed to update title: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
),
|
|
2045
|
+
tool4(
|
|
2046
|
+
"list_icons",
|
|
2047
|
+
"List available icons (default library + user-created). Returns icon IDs, names, and whether they're defaults. Call this FIRST before set_task_icon to check for existing matches.",
|
|
2048
|
+
{},
|
|
2049
|
+
async () => {
|
|
2050
|
+
try {
|
|
2051
|
+
const icons = await connection.listIcons();
|
|
2052
|
+
return textResult(JSON.stringify(icons, null, 2));
|
|
2053
|
+
} catch (error) {
|
|
2054
|
+
return textResult(
|
|
2055
|
+
`Failed to list icons: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2056
|
+
);
|
|
2057
|
+
}
|
|
2058
|
+
},
|
|
2059
|
+
{ annotations: { readOnlyHint: true } }
|
|
2060
|
+
),
|
|
2061
|
+
tool4(
|
|
2062
|
+
"set_task_icon",
|
|
2063
|
+
"Assign an existing icon to this task by its ID. Use list_icons first to find a matching icon.",
|
|
2064
|
+
{
|
|
2065
|
+
iconId: z4.string().describe("The icon ID to assign")
|
|
2066
|
+
},
|
|
2067
|
+
async ({ iconId }) => {
|
|
2068
|
+
try {
|
|
2069
|
+
connection.updateTaskProperties({ iconId });
|
|
2070
|
+
return textResult("Icon assigned to task.");
|
|
2071
|
+
} catch (error) {
|
|
2072
|
+
return textResult(
|
|
2073
|
+
`Failed to set icon: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
),
|
|
2078
|
+
tool4(
|
|
2079
|
+
"generate_task_icon",
|
|
2080
|
+
"Generate a new SVG icon using AI and assign it to this task. Only use if no existing icon from list_icons is a good fit. Provide a concise visual description.",
|
|
2081
|
+
{
|
|
2082
|
+
prompt: z4.string().describe(
|
|
2083
|
+
"Description of the icon to generate (e.g. 'minimal flat gear and wrench icon')"
|
|
2084
|
+
),
|
|
2085
|
+
aspectRatio: z4.enum(["auto", "portrait", "landscape", "square"]).optional().describe("Icon aspect ratio, defaults to square")
|
|
2086
|
+
},
|
|
2087
|
+
async ({ prompt, aspectRatio }) => {
|
|
2088
|
+
try {
|
|
2089
|
+
const result = await connection.generateTaskIcon(prompt, aspectRatio ?? "square");
|
|
2090
|
+
return textResult(`Icon generated and assigned: ${result.iconId}`);
|
|
2091
|
+
} catch (error) {
|
|
2092
|
+
return textResult(
|
|
2093
|
+
`Failed to generate icon: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
)
|
|
2098
|
+
];
|
|
2099
|
+
}
|
|
2100
|
+
|
|
1710
2101
|
// src/tools/index.ts
|
|
1711
2102
|
function textResult(text) {
|
|
1712
2103
|
return { content: [{ type: "text", text }] };
|
|
@@ -1716,11 +2107,34 @@ function imageBlock(data, mimeType) {
|
|
|
1716
2107
|
}
|
|
1717
2108
|
function createConveyorMcpServer(connection, config, context) {
|
|
1718
2109
|
const commonTools = buildCommonTools(connection, config);
|
|
1719
|
-
const
|
|
1720
|
-
|
|
2110
|
+
const agentMode = context?.agentMode;
|
|
2111
|
+
let modeTools;
|
|
2112
|
+
switch (agentMode) {
|
|
2113
|
+
case "building":
|
|
2114
|
+
modeTools = buildTaskTools(connection);
|
|
2115
|
+
break;
|
|
2116
|
+
case "review":
|
|
2117
|
+
modeTools = buildPmTools(connection, context?.storyPoints, {
|
|
2118
|
+
includePackTools: !!context?.isParentTask
|
|
2119
|
+
});
|
|
2120
|
+
break;
|
|
2121
|
+
case "auto":
|
|
2122
|
+
modeTools = buildPmTools(connection, context?.storyPoints, {
|
|
2123
|
+
includePackTools: !!context?.isParentTask
|
|
2124
|
+
});
|
|
2125
|
+
break;
|
|
2126
|
+
case "discovery":
|
|
2127
|
+
case "help":
|
|
2128
|
+
modeTools = buildPmTools(connection, context?.storyPoints, { includePackTools: false });
|
|
2129
|
+
break;
|
|
2130
|
+
default:
|
|
2131
|
+
modeTools = config.mode === "pm" ? buildPmTools(connection, context?.storyPoints, { includePackTools: false }) : buildTaskTools(connection);
|
|
2132
|
+
break;
|
|
2133
|
+
}
|
|
2134
|
+
const discoveryTools = agentMode === "discovery" || agentMode === "auto" ? buildDiscoveryTools(connection, context) : [];
|
|
1721
2135
|
return createSdkMcpServer({
|
|
1722
2136
|
name: "conveyor",
|
|
1723
|
-
tools: [...commonTools, ...modeTools]
|
|
2137
|
+
tools: [...commonTools, ...modeTools, ...discoveryTools]
|
|
1724
2138
|
});
|
|
1725
2139
|
}
|
|
1726
2140
|
|
|
@@ -1730,84 +2144,159 @@ var IMAGE_ERROR_PATTERN = /Could not process image/i;
|
|
|
1730
2144
|
var RETRY_DELAYS_MS = [6e4, 12e4, 18e4, 3e5];
|
|
1731
2145
|
var PM_PLAN_FILE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit"]);
|
|
1732
2146
|
var DESTRUCTIVE_CMD_PATTERN = /git\s+push\s+--force(?!\s*-with-lease)|git\s+reset\s+--hard|rm\s+-rf\s+\//;
|
|
2147
|
+
function handleDiscoveryToolAccess(toolName, input) {
|
|
2148
|
+
if (PM_PLAN_FILE_TOOLS.has(toolName)) {
|
|
2149
|
+
const filePath = String(input.file_path ?? input.path ?? "");
|
|
2150
|
+
if (filePath.includes(".claude/plans/")) {
|
|
2151
|
+
return { behavior: "allow", updatedInput: input };
|
|
2152
|
+
}
|
|
2153
|
+
return {
|
|
2154
|
+
behavior: "deny",
|
|
2155
|
+
message: "Discovery mode is read-only. File writes are restricted to plan files."
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
return { behavior: "allow", updatedInput: input };
|
|
2159
|
+
}
|
|
2160
|
+
function handleBuildingToolAccess(toolName, input) {
|
|
2161
|
+
if (toolName === "Bash") {
|
|
2162
|
+
const cmd = String(input.command ?? "");
|
|
2163
|
+
if (DESTRUCTIVE_CMD_PATTERN.test(cmd)) {
|
|
2164
|
+
return {
|
|
2165
|
+
behavior: "deny",
|
|
2166
|
+
message: "Destructive operation blocked. Use safer alternatives."
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
return { behavior: "allow", updatedInput: input };
|
|
2171
|
+
}
|
|
2172
|
+
function handleReviewToolAccess(toolName, input, isParentTask) {
|
|
2173
|
+
if (isParentTask) {
|
|
2174
|
+
return handleBuildingToolAccess(toolName, input);
|
|
2175
|
+
}
|
|
2176
|
+
if (PM_PLAN_FILE_TOOLS.has(toolName)) {
|
|
2177
|
+
const filePath = String(input.file_path ?? input.path ?? "");
|
|
2178
|
+
if (filePath.includes(".claude/plans/")) {
|
|
2179
|
+
return { behavior: "allow", updatedInput: input };
|
|
2180
|
+
}
|
|
2181
|
+
return {
|
|
2182
|
+
behavior: "deny",
|
|
2183
|
+
message: "Review mode restricts file writes. Use bash to run tests and linting instead."
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
if (toolName === "Bash") {
|
|
2187
|
+
const cmd = String(input.command ?? "");
|
|
2188
|
+
if (DESTRUCTIVE_CMD_PATTERN.test(cmd)) {
|
|
2189
|
+
return { behavior: "deny", message: "Destructive operation blocked in review mode." };
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
return { behavior: "allow", updatedInput: input };
|
|
2193
|
+
}
|
|
2194
|
+
function handleAutoToolAccess(toolName, input, hasExitedPlanMode, isParentTask) {
|
|
2195
|
+
if (hasExitedPlanMode) {
|
|
2196
|
+
return isParentTask ? handleReviewToolAccess(toolName, input, true) : handleBuildingToolAccess(toolName, input);
|
|
2197
|
+
}
|
|
2198
|
+
if (PM_PLAN_FILE_TOOLS.has(toolName)) {
|
|
2199
|
+
const filePath = String(input.file_path ?? input.path ?? "");
|
|
2200
|
+
if (filePath.includes(".claude/plans/")) {
|
|
2201
|
+
return { behavior: "allow", updatedInput: input };
|
|
2202
|
+
}
|
|
2203
|
+
return {
|
|
2204
|
+
behavior: "deny",
|
|
2205
|
+
message: "You are in auto plan mode. File writes are restricted to plan files. Call ExitPlanMode when your plan is ready to start building."
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
return { behavior: "allow", updatedInput: input };
|
|
2209
|
+
}
|
|
1733
2210
|
function buildCanUseTool(host) {
|
|
1734
2211
|
const QUESTION_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1735
2212
|
return async (toolName, input) => {
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
if (
|
|
2213
|
+
if (toolName === "ExitPlanMode" && host.agentMode === "auto" && !host.hasExitedPlanMode) {
|
|
2214
|
+
try {
|
|
2215
|
+
const taskProps = await host.connection.getTaskProperties();
|
|
2216
|
+
const missingProps = [];
|
|
2217
|
+
if (!taskProps.plan?.trim()) missingProps.push("plan (save via update_task)");
|
|
2218
|
+
if (!taskProps.storyPointId) missingProps.push("story points (use set_story_points)");
|
|
2219
|
+
if (!taskProps.title || taskProps.title === "Untitled")
|
|
2220
|
+
missingProps.push("title (use set_task_title)");
|
|
2221
|
+
if (missingProps.length > 0) {
|
|
1743
2222
|
return {
|
|
1744
2223
|
behavior: "deny",
|
|
1745
|
-
message:
|
|
2224
|
+
message: [
|
|
2225
|
+
"Cannot exit plan mode yet. Required task properties are missing:",
|
|
2226
|
+
...missingProps.map((p) => `- ${p}`),
|
|
2227
|
+
"",
|
|
2228
|
+
"Fill these in using MCP tools, then try ExitPlanMode again."
|
|
2229
|
+
].join("\n")
|
|
1746
2230
|
};
|
|
1747
2231
|
}
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
2232
|
+
await host.connection.triggerIdentification();
|
|
2233
|
+
host.hasExitedPlanMode = true;
|
|
2234
|
+
const newMode = host.isParentTask ? "review" : "building";
|
|
2235
|
+
host.pendingModeRestart = true;
|
|
2236
|
+
if (host.onModeTransition) {
|
|
2237
|
+
host.onModeTransition(newMode);
|
|
2238
|
+
}
|
|
1754
2239
|
return { behavior: "allow", updatedInput: input };
|
|
2240
|
+
} catch (err) {
|
|
2241
|
+
return {
|
|
2242
|
+
behavior: "deny",
|
|
2243
|
+
message: `Identification failed: ${err instanceof Error ? err.message : String(err)}. Fix the issue and try again.`
|
|
2244
|
+
};
|
|
1755
2245
|
}
|
|
1756
|
-
return {
|
|
1757
|
-
behavior: "deny",
|
|
1758
|
-
message: "File write tools are only available for plan files in PM mode."
|
|
1759
|
-
};
|
|
1760
2246
|
}
|
|
1761
|
-
if (
|
|
1762
|
-
const
|
|
1763
|
-
|
|
2247
|
+
if (toolName === "AskUserQuestion") {
|
|
2248
|
+
const questions = input.questions;
|
|
2249
|
+
const requestId = randomUUID();
|
|
2250
|
+
host.connection.emitStatus("waiting_for_input");
|
|
2251
|
+
host.connection.sendEvent({
|
|
2252
|
+
type: "tool_use",
|
|
2253
|
+
tool: "AskUserQuestion",
|
|
2254
|
+
input: JSON.stringify(input)
|
|
2255
|
+
});
|
|
2256
|
+
const answerPromise = host.connection.askUserQuestion(requestId, questions);
|
|
2257
|
+
const timeoutPromise = new Promise((resolve2) => {
|
|
2258
|
+
setTimeout(() => resolve2(null), QUESTION_TIMEOUT_MS);
|
|
2259
|
+
});
|
|
2260
|
+
const answers = await Promise.race([answerPromise, timeoutPromise]);
|
|
2261
|
+
host.connection.emitStatus("running");
|
|
2262
|
+
if (!answers) {
|
|
1764
2263
|
return {
|
|
1765
2264
|
behavior: "deny",
|
|
1766
|
-
message: "
|
|
2265
|
+
message: "User did not respond to clarifying questions in time. Proceed with your best judgment."
|
|
1767
2266
|
};
|
|
1768
2267
|
}
|
|
2268
|
+
return { behavior: "allow", updatedInput: { questions: input.questions, answers } };
|
|
2269
|
+
}
|
|
2270
|
+
switch (host.agentMode) {
|
|
2271
|
+
case "discovery":
|
|
2272
|
+
return handleDiscoveryToolAccess(toolName, input);
|
|
2273
|
+
case "building":
|
|
2274
|
+
return handleBuildingToolAccess(toolName, input);
|
|
2275
|
+
case "review":
|
|
2276
|
+
return handleReviewToolAccess(toolName, input, host.isParentTask);
|
|
2277
|
+
case "auto":
|
|
2278
|
+
return handleAutoToolAccess(toolName, input, host.hasExitedPlanMode, host.isParentTask);
|
|
2279
|
+
default:
|
|
2280
|
+
return { behavior: "allow", updatedInput: input };
|
|
1769
2281
|
}
|
|
1770
|
-
if (toolName !== "AskUserQuestion") {
|
|
1771
|
-
return { behavior: "allow", updatedInput: input };
|
|
1772
|
-
}
|
|
1773
|
-
const questions = input.questions;
|
|
1774
|
-
const requestId = randomUUID();
|
|
1775
|
-
host.connection.emitStatus("waiting_for_input");
|
|
1776
|
-
host.connection.sendEvent({
|
|
1777
|
-
type: "tool_use",
|
|
1778
|
-
tool: "AskUserQuestion",
|
|
1779
|
-
input: JSON.stringify(input)
|
|
1780
|
-
});
|
|
1781
|
-
const answerPromise = host.connection.askUserQuestion(requestId, questions);
|
|
1782
|
-
const timeoutPromise = new Promise((resolve2) => {
|
|
1783
|
-
setTimeout(() => resolve2(null), QUESTION_TIMEOUT_MS);
|
|
1784
|
-
});
|
|
1785
|
-
const answers = await Promise.race([answerPromise, timeoutPromise]);
|
|
1786
|
-
host.connection.emitStatus("running");
|
|
1787
|
-
if (!answers) {
|
|
1788
|
-
return {
|
|
1789
|
-
behavior: "deny",
|
|
1790
|
-
message: "User did not respond to clarifying questions in time. Proceed with your best judgment."
|
|
1791
|
-
};
|
|
1792
|
-
}
|
|
1793
|
-
return { behavior: "allow", updatedInput: { questions: input.questions, answers } };
|
|
1794
2282
|
};
|
|
1795
2283
|
}
|
|
1796
2284
|
function buildQueryOptions(host, context) {
|
|
1797
2285
|
const settings = context.agentSettings ?? host.config.agentSettings ?? {};
|
|
1798
|
-
const
|
|
1799
|
-
const
|
|
2286
|
+
const mode = host.agentMode;
|
|
2287
|
+
const isActiveMode = mode === "building" || mode === "review" || mode === "auto" && host.hasExitedPlanMode;
|
|
2288
|
+
const shouldSandbox = host.config.mode === "pm" && isActiveMode && context.useSandbox !== false;
|
|
1800
2289
|
const systemPromptText = buildSystemPrompt(
|
|
1801
2290
|
host.config.mode,
|
|
1802
2291
|
context,
|
|
1803
2292
|
host.config,
|
|
1804
2293
|
host.setupLog,
|
|
1805
|
-
|
|
2294
|
+
mode
|
|
1806
2295
|
);
|
|
1807
2296
|
const conveyorMcp = createConveyorMcpServer(host.connection, host.config, context);
|
|
1808
|
-
const
|
|
1809
|
-
const
|
|
1810
|
-
const disallowedTools = [...settings.disallowedTools ?? [], ...
|
|
2297
|
+
const isReadOnlyMode = mode === "discovery" || mode === "help" || mode === "auto" && !host.hasExitedPlanMode;
|
|
2298
|
+
const modeDisallowedTools = isReadOnlyMode ? ["TodoWrite", "TodoRead", "NotebookEdit"] : [];
|
|
2299
|
+
const disallowedTools = [...settings.disallowedTools ?? [], ...modeDisallowedTools];
|
|
1811
2300
|
const settingSources = settings.settingSources ?? ["user", "project"];
|
|
1812
2301
|
const hooks = {
|
|
1813
2302
|
PostToolUse: [
|
|
@@ -1920,10 +2409,10 @@ function buildMultimodalPrompt(textPrompt, context, skipImages = false) {
|
|
|
1920
2409
|
}
|
|
1921
2410
|
async function runSdkQuery(host, context, followUpContent) {
|
|
1922
2411
|
if (host.isStopped()) return;
|
|
1923
|
-
const
|
|
1924
|
-
const
|
|
1925
|
-
const
|
|
1926
|
-
if (
|
|
2412
|
+
const mode = host.agentMode;
|
|
2413
|
+
const isPmMode = host.config.mode === "pm";
|
|
2414
|
+
const isDiscoveryLike = mode === "discovery" || mode === "help";
|
|
2415
|
+
if (isDiscoveryLike) {
|
|
1927
2416
|
host.snapshotPlanFiles();
|
|
1928
2417
|
}
|
|
1929
2418
|
const options = buildQueryOptions(host, context);
|
|
@@ -1933,14 +2422,14 @@ async function runSdkQuery(host, context, followUpContent) {
|
|
|
1933
2422
|
const followUpImages = typeof followUpContent === "string" ? [] : followUpContent.filter(
|
|
1934
2423
|
(b) => b.type === "image"
|
|
1935
2424
|
);
|
|
1936
|
-
const textPrompt =
|
|
2425
|
+
const textPrompt = isPmMode ? `${buildInitialPrompt(host.config.mode, context, host.config.isAuto, mode)}
|
|
1937
2426
|
|
|
1938
2427
|
---
|
|
1939
2428
|
|
|
1940
2429
|
The team says:
|
|
1941
2430
|
${followUpText}` : followUpText;
|
|
1942
2431
|
let prompt;
|
|
1943
|
-
if (
|
|
2432
|
+
if (isPmMode) {
|
|
1944
2433
|
prompt = buildMultimodalPrompt(textPrompt, context);
|
|
1945
2434
|
if (followUpImages.length > 0 && Array.isArray(prompt)) {
|
|
1946
2435
|
prompt.push(...followUpImages);
|
|
@@ -1955,10 +2444,10 @@ ${followUpText}` : followUpText;
|
|
|
1955
2444
|
options: { ...options, resume }
|
|
1956
2445
|
});
|
|
1957
2446
|
await runWithRetry(agentQuery, context, host, options);
|
|
1958
|
-
} else if (
|
|
2447
|
+
} else if (isDiscoveryLike) {
|
|
1959
2448
|
return;
|
|
1960
2449
|
} else {
|
|
1961
|
-
const initialPrompt = buildInitialPrompt(host.config.mode, context, host.config.isAuto);
|
|
2450
|
+
const initialPrompt = buildInitialPrompt(host.config.mode, context, host.config.isAuto, mode);
|
|
1962
2451
|
const prompt = buildMultimodalPrompt(initialPrompt, context);
|
|
1963
2452
|
const agentQuery = query({
|
|
1964
2453
|
prompt: host.createInputStream(prompt),
|
|
@@ -1966,7 +2455,7 @@ ${followUpText}` : followUpText;
|
|
|
1966
2455
|
});
|
|
1967
2456
|
await runWithRetry(agentQuery, context, host, options);
|
|
1968
2457
|
}
|
|
1969
|
-
if (
|
|
2458
|
+
if (isDiscoveryLike) {
|
|
1970
2459
|
host.syncPlanFile();
|
|
1971
2460
|
}
|
|
1972
2461
|
}
|
|
@@ -1981,7 +2470,7 @@ async function runWithRetry(initialQuery, context, host, options) {
|
|
|
1981
2470
|
);
|
|
1982
2471
|
}
|
|
1983
2472
|
const retryPrompt = buildMultimodalPrompt(
|
|
1984
|
-
buildInitialPrompt(host.config.mode, context, host.config.isAuto),
|
|
2473
|
+
buildInitialPrompt(host.config.mode, context, host.config.isAuto, host.agentMode),
|
|
1985
2474
|
context,
|
|
1986
2475
|
lastErrorWasImage
|
|
1987
2476
|
);
|
|
@@ -1991,7 +2480,12 @@ async function runWithRetry(initialQuery, context, host, options) {
|
|
|
1991
2480
|
});
|
|
1992
2481
|
})();
|
|
1993
2482
|
try {
|
|
1994
|
-
const { retriable, resultSummary } = await processEvents(
|
|
2483
|
+
const { retriable, resultSummary, modeRestart } = await processEvents(
|
|
2484
|
+
agentQuery,
|
|
2485
|
+
context,
|
|
2486
|
+
host
|
|
2487
|
+
);
|
|
2488
|
+
if (modeRestart) return;
|
|
1995
2489
|
if (!retriable || host.isStopped()) return;
|
|
1996
2490
|
lastErrorWasImage = IMAGE_ERROR_PATTERN.test(resultSummary ?? "");
|
|
1997
2491
|
} catch (error) {
|
|
@@ -2001,7 +2495,7 @@ async function runWithRetry(initialQuery, context, host, options) {
|
|
|
2001
2495
|
context.claudeSessionId = null;
|
|
2002
2496
|
host.connection.storeSessionId("");
|
|
2003
2497
|
const freshPrompt = buildMultimodalPrompt(
|
|
2004
|
-
buildInitialPrompt(host.config.mode, context, host.config.isAuto),
|
|
2498
|
+
buildInitialPrompt(host.config.mode, context, host.config.isAuto, host.agentMode),
|
|
2005
2499
|
context
|
|
2006
2500
|
);
|
|
2007
2501
|
const freshQuery = query({
|
|
@@ -2187,7 +2681,15 @@ var AgentRunner = class _AgentRunner {
|
|
|
2187
2681
|
planSync;
|
|
2188
2682
|
costTracker = new CostTracker();
|
|
2189
2683
|
worktreeActive = false;
|
|
2190
|
-
|
|
2684
|
+
agentMode = null;
|
|
2685
|
+
hasExitedPlanMode = false;
|
|
2686
|
+
pendingModeRestart = false;
|
|
2687
|
+
sessionIds = /* @__PURE__ */ new Map();
|
|
2688
|
+
lastQueryModeRestart = false;
|
|
2689
|
+
deferredStartConfig = null;
|
|
2690
|
+
startCommandStarted = false;
|
|
2691
|
+
idleTimer = null;
|
|
2692
|
+
idleCheckInterval = null;
|
|
2191
2693
|
static MAX_SETUP_LOG_LINES = 50;
|
|
2192
2694
|
constructor(config, callbacks) {
|
|
2193
2695
|
this.config = config;
|
|
@@ -2198,6 +2700,18 @@ var AgentRunner = class _AgentRunner {
|
|
|
2198
2700
|
get state() {
|
|
2199
2701
|
return this._state;
|
|
2200
2702
|
}
|
|
2703
|
+
/**
|
|
2704
|
+
* Resolve the effective AgentMode from explicit agentMode or legacy config flags.
|
|
2705
|
+
* This is the single axis of behavior for all execution path decisions.
|
|
2706
|
+
*/
|
|
2707
|
+
get effectiveAgentMode() {
|
|
2708
|
+
if (this.agentMode) return this.agentMode;
|
|
2709
|
+
if (this.config.mode === "pm") {
|
|
2710
|
+
if (this.config.isAuto) return "auto";
|
|
2711
|
+
return "discovery";
|
|
2712
|
+
}
|
|
2713
|
+
return "building";
|
|
2714
|
+
}
|
|
2201
2715
|
async setState(status) {
|
|
2202
2716
|
this._state = status;
|
|
2203
2717
|
this.connection.emitStatus(status);
|
|
@@ -2216,6 +2730,16 @@ var AgentRunner = class _AgentRunner {
|
|
|
2216
2730
|
this.heartbeatTimer = null;
|
|
2217
2731
|
}
|
|
2218
2732
|
}
|
|
2733
|
+
clearIdleTimers() {
|
|
2734
|
+
if (this.idleTimer) {
|
|
2735
|
+
clearTimeout(this.idleTimer);
|
|
2736
|
+
this.idleTimer = null;
|
|
2737
|
+
}
|
|
2738
|
+
if (this.idleCheckInterval) {
|
|
2739
|
+
clearInterval(this.idleCheckInterval);
|
|
2740
|
+
this.idleCheckInterval = null;
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2219
2743
|
async start() {
|
|
2220
2744
|
await this.setState("connecting");
|
|
2221
2745
|
await this.connection.connect();
|
|
@@ -2223,7 +2747,8 @@ var AgentRunner = class _AgentRunner {
|
|
|
2223
2747
|
this.connection.onChatMessage(
|
|
2224
2748
|
(message) => this.injectHumanMessage(message.content, message.files)
|
|
2225
2749
|
);
|
|
2226
|
-
this.connection.onModeChange((data) => this.handleModeChange(data.
|
|
2750
|
+
this.connection.onModeChange((data) => this.handleModeChange(data.agentMode));
|
|
2751
|
+
this.connection.onRunStartCommand(() => this.runDeferredStartCommand());
|
|
2227
2752
|
await this.setState("connected");
|
|
2228
2753
|
this.connection.sendEvent({ type: "connected", taskId: this.config.taskId });
|
|
2229
2754
|
this.startHeartbeat();
|
|
@@ -2262,6 +2787,9 @@ var AgentRunner = class _AgentRunner {
|
|
|
2262
2787
|
return;
|
|
2263
2788
|
}
|
|
2264
2789
|
this.taskContext._runnerSessionId = randomUUID2();
|
|
2790
|
+
if (this.taskContext.agentMode) {
|
|
2791
|
+
this.agentMode = this.taskContext.agentMode;
|
|
2792
|
+
}
|
|
2265
2793
|
this.logEffectiveSettings();
|
|
2266
2794
|
if (process.env.CODESPACES === "true") {
|
|
2267
2795
|
unshallowRepo(this.config.workspaceDir);
|
|
@@ -2298,18 +2826,30 @@ var AgentRunner = class _AgentRunner {
|
|
|
2298
2826
|
} catch {
|
|
2299
2827
|
}
|
|
2300
2828
|
}
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2829
|
+
switch (this.effectiveAgentMode) {
|
|
2830
|
+
case "discovery":
|
|
2831
|
+
case "help":
|
|
2832
|
+
await this.setState("idle");
|
|
2833
|
+
break;
|
|
2834
|
+
case "building":
|
|
2835
|
+
await this.setState("running");
|
|
2836
|
+
await this.runQuerySafe(this.taskContext);
|
|
2837
|
+
if (!this.stopped) await this.setState("idle");
|
|
2838
|
+
break;
|
|
2839
|
+
case "review":
|
|
2840
|
+
if (this.taskContext.isParentTask) {
|
|
2841
|
+
await this.setState("running");
|
|
2842
|
+
await this.runQuerySafe(this.taskContext);
|
|
2843
|
+
if (!this.stopped) await this.setState("idle");
|
|
2844
|
+
} else {
|
|
2845
|
+
await this.setState("idle");
|
|
2846
|
+
}
|
|
2847
|
+
break;
|
|
2848
|
+
case "auto":
|
|
2849
|
+
await this.setState("running");
|
|
2850
|
+
await this.runQuerySafe(this.taskContext);
|
|
2851
|
+
if (!this.stopped) await this.setState("idle");
|
|
2852
|
+
break;
|
|
2313
2853
|
}
|
|
2314
2854
|
await this.runCoreLoop();
|
|
2315
2855
|
this.stopHeartbeat();
|
|
@@ -2317,8 +2857,13 @@ var AgentRunner = class _AgentRunner {
|
|
|
2317
2857
|
this.connection.disconnect();
|
|
2318
2858
|
}
|
|
2319
2859
|
async runQuerySafe(context, followUp) {
|
|
2860
|
+
this.lastQueryModeRestart = false;
|
|
2320
2861
|
try {
|
|
2321
2862
|
await runSdkQuery(this.asQueryHost(), context, followUp);
|
|
2863
|
+
if (this.pendingModeRestart) {
|
|
2864
|
+
this.lastQueryModeRestart = true;
|
|
2865
|
+
this.pendingModeRestart = false;
|
|
2866
|
+
}
|
|
2322
2867
|
} catch (error) {
|
|
2323
2868
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2324
2869
|
this.connection.sendEvent({ type: "error", message });
|
|
@@ -2329,6 +2874,11 @@ var AgentRunner = class _AgentRunner {
|
|
|
2329
2874
|
async runCoreLoop() {
|
|
2330
2875
|
if (!this.taskContext) return;
|
|
2331
2876
|
while (!this.stopped) {
|
|
2877
|
+
if (this.lastQueryModeRestart) {
|
|
2878
|
+
this.lastQueryModeRestart = false;
|
|
2879
|
+
await this.handleAutoModeRestart();
|
|
2880
|
+
continue;
|
|
2881
|
+
}
|
|
2332
2882
|
if (this._state === "idle") {
|
|
2333
2883
|
const msg = await this.waitForUserContent();
|
|
2334
2884
|
if (!msg) break;
|
|
@@ -2342,6 +2892,65 @@ var AgentRunner = class _AgentRunner {
|
|
|
2342
2892
|
}
|
|
2343
2893
|
}
|
|
2344
2894
|
}
|
|
2895
|
+
/**
|
|
2896
|
+
* Handle auto mode transition after ExitPlanMode.
|
|
2897
|
+
* Saves the current session, switches model/mode, and restarts with a fresh query.
|
|
2898
|
+
*/
|
|
2899
|
+
async handleAutoModeRestart() {
|
|
2900
|
+
if (!this.taskContext) return;
|
|
2901
|
+
const currentModel = this.taskContext.model;
|
|
2902
|
+
const currentSessionId = this.taskContext.claudeSessionId;
|
|
2903
|
+
if (currentSessionId && currentModel) {
|
|
2904
|
+
this.sessionIds.set(currentModel, currentSessionId);
|
|
2905
|
+
}
|
|
2906
|
+
const newMode = this.agentMode;
|
|
2907
|
+
const isParentTask = !!this.taskContext.isParentTask;
|
|
2908
|
+
const newModel = this.getModelForMode(newMode, isParentTask);
|
|
2909
|
+
const resumeSessionId = newModel ? this.sessionIds.get(newModel) : null;
|
|
2910
|
+
if (resumeSessionId) {
|
|
2911
|
+
this.taskContext.claudeSessionId = resumeSessionId;
|
|
2912
|
+
} else {
|
|
2913
|
+
this.taskContext.claudeSessionId = null;
|
|
2914
|
+
this.connection.storeSessionId("");
|
|
2915
|
+
}
|
|
2916
|
+
if (newModel) {
|
|
2917
|
+
this.taskContext.model = newModel;
|
|
2918
|
+
}
|
|
2919
|
+
this.taskContext.agentMode = newMode;
|
|
2920
|
+
this.connection.emitModeTransition({
|
|
2921
|
+
fromMode: "auto",
|
|
2922
|
+
toMode: newMode ?? "building"
|
|
2923
|
+
});
|
|
2924
|
+
this.connection.emitModeChanged(newMode);
|
|
2925
|
+
this.connection.postChatMessage(
|
|
2926
|
+
`Transitioning to **${newMode}** mode${newModel ? ` with ${newModel}` : ""}. Restarting session...`
|
|
2927
|
+
);
|
|
2928
|
+
try {
|
|
2929
|
+
const freshContext = await this.connection.fetchTaskContext();
|
|
2930
|
+
freshContext._runnerSessionId = this.taskContext._runnerSessionId;
|
|
2931
|
+
freshContext.claudeSessionId = this.taskContext.claudeSessionId;
|
|
2932
|
+
freshContext.agentMode = newMode;
|
|
2933
|
+
if (newModel) freshContext.model = newModel;
|
|
2934
|
+
this.taskContext = freshContext;
|
|
2935
|
+
} catch {
|
|
2936
|
+
}
|
|
2937
|
+
await this.setState("running");
|
|
2938
|
+
await this.runQuerySafe(this.taskContext);
|
|
2939
|
+
if (!this.stopped && this._state !== "error") {
|
|
2940
|
+
await this.setState("idle");
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
/**
|
|
2944
|
+
* Get the appropriate model for a given mode.
|
|
2945
|
+
* Building uses the builder model (Sonnet), Discovery/Review use PM model (Opus).
|
|
2946
|
+
*/
|
|
2947
|
+
getModelForMode(mode, isParentTask) {
|
|
2948
|
+
if (!this.taskContext) return null;
|
|
2949
|
+
if (mode === "building" && !isParentTask) {
|
|
2950
|
+
return this.taskContext.builderModel ?? this.taskContext.model;
|
|
2951
|
+
}
|
|
2952
|
+
return this.taskContext.pmModel ?? this.taskContext.model;
|
|
2953
|
+
}
|
|
2345
2954
|
async runSetupSafe() {
|
|
2346
2955
|
await this.setState("setup");
|
|
2347
2956
|
const ports = await loadForwardPorts(this.config.workspaceDir);
|
|
@@ -2363,7 +2972,8 @@ var AgentRunner = class _AgentRunner {
|
|
|
2363
2972
|
await this.executeSetupConfig(config);
|
|
2364
2973
|
const setupEvent = {
|
|
2365
2974
|
type: "setup_complete",
|
|
2366
|
-
previewPort: config.previewPort ?? void 0
|
|
2975
|
+
previewPort: config.previewPort ?? void 0,
|
|
2976
|
+
startCommandDeferred: this.deferredStartConfig !== null ? true : void 0
|
|
2367
2977
|
};
|
|
2368
2978
|
this.connection.sendEvent(setupEvent);
|
|
2369
2979
|
await this.callbacks.onEvent(setupEvent);
|
|
@@ -2415,12 +3025,33 @@ The agent cannot start until this is resolved.`
|
|
|
2415
3025
|
this.pushSetupLog("(exit 0)");
|
|
2416
3026
|
}
|
|
2417
3027
|
if (config.startCommand) {
|
|
2418
|
-
this.
|
|
2419
|
-
|
|
2420
|
-
this.
|
|
2421
|
-
}
|
|
3028
|
+
if (this.effectiveAgentMode === "auto") {
|
|
3029
|
+
this.deferredStartConfig = config;
|
|
3030
|
+
this.pushSetupLog(`[conveyor] startCommand deferred (auto mode)`);
|
|
3031
|
+
} else {
|
|
3032
|
+
this.pushSetupLog(`$ ${config.startCommand} & (background)`);
|
|
3033
|
+
runStartCommand(config.startCommand, this.config.workspaceDir, (stream, data) => {
|
|
3034
|
+
this.connection.sendEvent({ type: "start_command_output", stream, data });
|
|
3035
|
+
});
|
|
3036
|
+
}
|
|
2422
3037
|
}
|
|
2423
3038
|
}
|
|
3039
|
+
runDeferredStartCommand() {
|
|
3040
|
+
if (!this.deferredStartConfig?.startCommand || this.startCommandStarted) return;
|
|
3041
|
+
this.startCommandStarted = true;
|
|
3042
|
+
const config = this.deferredStartConfig;
|
|
3043
|
+
this.deferredStartConfig = null;
|
|
3044
|
+
this.pushSetupLog(`$ ${config.startCommand} & (background, deferred)`);
|
|
3045
|
+
this.connection.sendEvent({ type: "start_command_started" });
|
|
3046
|
+
runStartCommand(config.startCommand, this.config.workspaceDir, (stream, data) => {
|
|
3047
|
+
this.connection.sendEvent({ type: "start_command_output", stream, data });
|
|
3048
|
+
});
|
|
3049
|
+
const setupEvent = {
|
|
3050
|
+
type: "setup_complete",
|
|
3051
|
+
previewPort: config.previewPort ?? void 0
|
|
3052
|
+
};
|
|
3053
|
+
this.connection.sendEvent(setupEvent);
|
|
3054
|
+
}
|
|
2424
3055
|
injectHumanMessage(content, files) {
|
|
2425
3056
|
let messageContent;
|
|
2426
3057
|
if (files?.length) {
|
|
@@ -2469,17 +3100,17 @@ ${f.content}
|
|
|
2469
3100
|
}
|
|
2470
3101
|
}
|
|
2471
3102
|
waitForMessage() {
|
|
3103
|
+
this.clearIdleTimers();
|
|
2472
3104
|
return new Promise((resolve2) => {
|
|
2473
|
-
|
|
3105
|
+
this.idleCheckInterval = setInterval(() => {
|
|
2474
3106
|
if (this.stopped) {
|
|
2475
|
-
|
|
2476
|
-
clearTimeout(idleTimer);
|
|
3107
|
+
this.clearIdleTimers();
|
|
2477
3108
|
this.inputResolver = null;
|
|
2478
3109
|
resolve2(null);
|
|
2479
3110
|
}
|
|
2480
3111
|
}, 1e3);
|
|
2481
|
-
|
|
2482
|
-
|
|
3112
|
+
this.idleTimer = setTimeout(() => {
|
|
3113
|
+
this.clearIdleTimers();
|
|
2483
3114
|
this.inputResolver = null;
|
|
2484
3115
|
console.log(
|
|
2485
3116
|
`[conveyor-agent] Idle for ${IDLE_TIMEOUT_MS / 6e4} minutes \u2014 shutting down.`
|
|
@@ -2490,8 +3121,7 @@ ${f.content}
|
|
|
2490
3121
|
resolve2(null);
|
|
2491
3122
|
}, IDLE_TIMEOUT_MS);
|
|
2492
3123
|
this.inputResolver = (msg) => {
|
|
2493
|
-
|
|
2494
|
-
clearTimeout(idleTimer);
|
|
3124
|
+
this.clearIdleTimers();
|
|
2495
3125
|
resolve2(msg);
|
|
2496
3126
|
};
|
|
2497
3127
|
});
|
|
@@ -2517,48 +3147,84 @@ ${f.content}
|
|
|
2517
3147
|
parent_tool_use_id: null
|
|
2518
3148
|
});
|
|
2519
3149
|
yield makeUserMessage(initialPrompt);
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
3150
|
+
try {
|
|
3151
|
+
while (!this.stopped) {
|
|
3152
|
+
if (this.pendingMessages.length > 0) {
|
|
3153
|
+
const next = this.pendingMessages.shift();
|
|
3154
|
+
if (next) yield next;
|
|
3155
|
+
continue;
|
|
3156
|
+
}
|
|
3157
|
+
this.connection.emitStatus("waiting_for_input");
|
|
3158
|
+
await this.callbacks.onStatusChange("waiting_for_input");
|
|
3159
|
+
const msg = await this.waitForMessage();
|
|
3160
|
+
if (!msg) break;
|
|
3161
|
+
this.connection.emitStatus("running");
|
|
3162
|
+
await this.callbacks.onStatusChange("running");
|
|
3163
|
+
yield msg;
|
|
2525
3164
|
}
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
const msg = await this.waitForMessage();
|
|
2529
|
-
if (!msg) break;
|
|
2530
|
-
this.connection.emitStatus("running");
|
|
2531
|
-
await this.callbacks.onStatusChange("running");
|
|
2532
|
-
yield msg;
|
|
3165
|
+
} finally {
|
|
3166
|
+
this.clearIdleTimers();
|
|
2533
3167
|
}
|
|
2534
3168
|
}
|
|
2535
3169
|
asQueryHost() {
|
|
2536
|
-
const
|
|
3170
|
+
const getEffectiveAgentMode = () => this.effectiveAgentMode;
|
|
3171
|
+
const getHasExitedPlanMode = () => this.hasExitedPlanMode;
|
|
3172
|
+
const setHasExitedPlanMode = (val) => {
|
|
3173
|
+
this.hasExitedPlanMode = val;
|
|
3174
|
+
};
|
|
3175
|
+
const getPendingModeRestart = () => this.pendingModeRestart;
|
|
3176
|
+
const setPendingModeRestart = (val) => {
|
|
3177
|
+
this.pendingModeRestart = val;
|
|
3178
|
+
};
|
|
3179
|
+
const getIsParentTask = () => !!this.taskContext?.isParentTask;
|
|
2537
3180
|
return {
|
|
2538
3181
|
config: this.config,
|
|
2539
3182
|
connection: this.connection,
|
|
2540
3183
|
callbacks: this.callbacks,
|
|
2541
3184
|
setupLog: this.setupLog,
|
|
2542
3185
|
costTracker: this.costTracker,
|
|
2543
|
-
get
|
|
2544
|
-
return
|
|
3186
|
+
get agentMode() {
|
|
3187
|
+
return getEffectiveAgentMode();
|
|
3188
|
+
},
|
|
3189
|
+
get isParentTask() {
|
|
3190
|
+
return getIsParentTask();
|
|
3191
|
+
},
|
|
3192
|
+
get hasExitedPlanMode() {
|
|
3193
|
+
return getHasExitedPlanMode();
|
|
3194
|
+
},
|
|
3195
|
+
set hasExitedPlanMode(val) {
|
|
3196
|
+
setHasExitedPlanMode(val);
|
|
2545
3197
|
},
|
|
3198
|
+
get pendingModeRestart() {
|
|
3199
|
+
return getPendingModeRestart();
|
|
3200
|
+
},
|
|
3201
|
+
set pendingModeRestart(val) {
|
|
3202
|
+
setPendingModeRestart(val);
|
|
3203
|
+
},
|
|
3204
|
+
sessionIds: this.sessionIds,
|
|
2546
3205
|
isStopped: () => this.stopped,
|
|
2547
3206
|
createInputStream: (prompt) => this.createInputStream(prompt),
|
|
2548
3207
|
snapshotPlanFiles: () => this.planSync.snapshotPlanFiles(),
|
|
2549
|
-
syncPlanFile: () => this.planSync.syncPlanFile()
|
|
3208
|
+
syncPlanFile: () => this.planSync.syncPlanFile(),
|
|
3209
|
+
onModeTransition: (newMode) => {
|
|
3210
|
+
this.agentMode = newMode;
|
|
3211
|
+
}
|
|
2550
3212
|
};
|
|
2551
3213
|
}
|
|
2552
|
-
handleModeChange(
|
|
3214
|
+
handleModeChange(newAgentMode) {
|
|
2553
3215
|
if (this.config.mode !== "pm") return;
|
|
2554
|
-
|
|
2555
|
-
|
|
3216
|
+
if (newAgentMode) {
|
|
3217
|
+
this.agentMode = newAgentMode;
|
|
3218
|
+
}
|
|
3219
|
+
const effectiveMode = this.effectiveAgentMode;
|
|
3220
|
+
this.connection.emitModeChanged(effectiveMode);
|
|
2556
3221
|
this.connection.postChatMessage(
|
|
2557
|
-
`Mode switched to **${
|
|
3222
|
+
`Mode switched to **${effectiveMode}**${effectiveMode === "building" ? " \u2014 I now have direct coding access." : ""}`
|
|
2558
3223
|
);
|
|
2559
3224
|
}
|
|
2560
3225
|
stop() {
|
|
2561
3226
|
this.stopped = true;
|
|
3227
|
+
this.clearIdleTimers();
|
|
2562
3228
|
if (this.inputResolver) {
|
|
2563
3229
|
this.inputResolver(null);
|
|
2564
3230
|
this.inputResolver = null;
|
|
@@ -2811,6 +3477,7 @@ var ProjectRunner = class {
|
|
|
2811
3477
|
CONVEYOR_MODE: mode,
|
|
2812
3478
|
CONVEYOR_WORKSPACE: workDir,
|
|
2813
3479
|
CONVEYOR_USE_WORKTREE: "false",
|
|
3480
|
+
CONVEYOR_AGENT_MODE: isAuto ? "auto" : "",
|
|
2814
3481
|
CONVEYOR_IS_AUTO: isAuto ? "true" : "false",
|
|
2815
3482
|
CONVEYOR_USE_SANDBOX: useSandbox === false ? "false" : "true"
|
|
2816
3483
|
},
|
|
@@ -2990,4 +3657,4 @@ export {
|
|
|
2990
3657
|
ProjectRunner,
|
|
2991
3658
|
FileCache
|
|
2992
3659
|
};
|
|
2993
|
-
//# sourceMappingURL=chunk-
|
|
3660
|
+
//# sourceMappingURL=chunk-Q2LN2YBW.js.map
|