@rallycry/conveyor-agent 7.0.2 → 7.0.4
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-NZ3IPMLO.js → chunk-K2TJHPMA.js} +164 -68
- package/dist/chunk-K2TJHPMA.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-NZ3IPMLO.js.map +0 -1
|
@@ -141,25 +141,7 @@ var AgentConnection = class {
|
|
|
141
141
|
});
|
|
142
142
|
this.socket.io.on("reconnect", () => {
|
|
143
143
|
process.stderr.write("[conveyor-agent] Reconnected\n");
|
|
144
|
-
|
|
145
|
-
for (const msg of pendingMessages) {
|
|
146
|
-
if (msg.content) {
|
|
147
|
-
if (this.messageCallback) {
|
|
148
|
-
this.messageCallback({ content: msg.content, userId: msg.userId });
|
|
149
|
-
} else {
|
|
150
|
-
this.earlyMessages.push({ content: msg.content, userId: msg.userId });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}).catch(() => {
|
|
155
|
-
});
|
|
156
|
-
if (this.lastEmittedStatus) {
|
|
157
|
-
void this.call("reportAgentStatus", {
|
|
158
|
-
sessionId: this.config.sessionId,
|
|
159
|
-
status: this.lastEmittedStatus
|
|
160
|
-
}).catch(() => {
|
|
161
|
-
});
|
|
162
|
-
}
|
|
144
|
+
void this.reconnectToSession();
|
|
163
145
|
});
|
|
164
146
|
this.socket.io.on("reconnect_attempt", () => {
|
|
165
147
|
});
|
|
@@ -174,6 +156,53 @@ var AgentConnection = class {
|
|
|
174
156
|
this.socket = null;
|
|
175
157
|
}
|
|
176
158
|
}
|
|
159
|
+
// ── Reconnect with retry ────────────────────────────────────────────
|
|
160
|
+
async reconnectToSession() {
|
|
161
|
+
const maxRetries = 5;
|
|
162
|
+
const baseDelayMs = 2e3;
|
|
163
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
164
|
+
try {
|
|
165
|
+
const { pendingMessages } = await this.call("connectAgent", {
|
|
166
|
+
sessionId: this.config.sessionId
|
|
167
|
+
});
|
|
168
|
+
this.drainPendingMessages(pendingMessages);
|
|
169
|
+
process.stderr.write("[conveyor-agent] Reconnected to session successfully\n");
|
|
170
|
+
if (this.lastEmittedStatus) {
|
|
171
|
+
void this.call("reportAgentStatus", {
|
|
172
|
+
sessionId: this.config.sessionId,
|
|
173
|
+
status: this.lastEmittedStatus
|
|
174
|
+
}).catch(() => {
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
} catch (err) {
|
|
179
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
180
|
+
const delayMs = baseDelayMs * 2 ** (attempt - 1);
|
|
181
|
+
process.stderr.write(
|
|
182
|
+
`[conveyor-agent] connectAgent failed (attempt ${attempt}/${maxRetries}): ${errMsg} \u2014 retrying in ${delayMs / 1e3}s
|
|
183
|
+
`
|
|
184
|
+
);
|
|
185
|
+
await new Promise((resolve2) => {
|
|
186
|
+
setTimeout(resolve2, delayMs);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
process.stderr.write(
|
|
191
|
+
`[conveyor-agent] Failed to reconnect to session after ${maxRetries} attempts, exiting
|
|
192
|
+
`
|
|
193
|
+
);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
drainPendingMessages(messages) {
|
|
197
|
+
for (const msg of messages) {
|
|
198
|
+
if (!msg.content) continue;
|
|
199
|
+
if (this.messageCallback) {
|
|
200
|
+
this.messageCallback({ content: msg.content, userId: msg.userId });
|
|
201
|
+
} else {
|
|
202
|
+
this.earlyMessages.push({ content: msg.content, userId: msg.userId });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
177
206
|
// ── Callback registration with early-buffer draining ───────────────
|
|
178
207
|
onMessage(callback) {
|
|
179
208
|
this.messageCallback = callback;
|
|
@@ -234,6 +263,7 @@ var AgentConnection = class {
|
|
|
234
263
|
};
|
|
235
264
|
const heartbeatStatus = statusMap[this.lastEmittedStatus ?? "idle"] ?? "active";
|
|
236
265
|
void this.call("heartbeat", {
|
|
266
|
+
sessionId: this.config.sessionId,
|
|
237
267
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
238
268
|
status: heartbeatStatus
|
|
239
269
|
}).catch(() => {
|
|
@@ -1143,6 +1173,14 @@ function buildDiscoveryPrompt(context) {
|
|
|
1143
1173
|
`- If the work fits in a single task (1-3 SP), update YOUR OWN plan and properties \u2014 do not create subtasks`,
|
|
1144
1174
|
`- Only create subtasks when the work genuinely requires multiple independent pieces (e.g., Pack-tier work, 8+ SP)`,
|
|
1145
1175
|
``,
|
|
1176
|
+
`### Subtask Plan Requirements`,
|
|
1177
|
+
`When creating subtasks, each MUST include a detailed \`plan\` field:`,
|
|
1178
|
+
`- Plans should be multi-step implementation guides, not vague descriptions`,
|
|
1179
|
+
`- Include specific file paths, function names, and code patterns to modify`,
|
|
1180
|
+
`- Reference existing implementations when relevant (e.g., "follow the pattern in src/services/foo.ts")`,
|
|
1181
|
+
`- Include testing requirements and acceptance criteria`,
|
|
1182
|
+
`- Set \`storyPointValue\` based on estimated complexity`,
|
|
1183
|
+
``,
|
|
1146
1184
|
`### Finishing Planning`,
|
|
1147
1185
|
`Once your plan is complete and all required properties are set, call the **ExitPlanMode** tool.`,
|
|
1148
1186
|
`- Required before ExitPlanMode will succeed: **plan** (via update_task), **story points** (via update_task_properties), **title** (via update_task_properties)`,
|
|
@@ -1174,6 +1212,14 @@ function buildAutoPrompt(context) {
|
|
|
1174
1212
|
`- Once ExitPlanMode succeeds, the system will automatically restart your session in Building mode with the appropriate model`,
|
|
1175
1213
|
`- You do NOT need to do anything after calling ExitPlanMode \u2014 the transition is handled for you`,
|
|
1176
1214
|
``,
|
|
1215
|
+
`### Subtask Plan Requirements`,
|
|
1216
|
+
`When creating subtasks, each MUST include a detailed \`plan\` field:`,
|
|
1217
|
+
`- Plans should be multi-step implementation guides, not vague descriptions`,
|
|
1218
|
+
`- Include specific file paths, function names, and code patterns to modify`,
|
|
1219
|
+
`- Reference existing implementations when relevant`,
|
|
1220
|
+
`- Include testing requirements and acceptance criteria`,
|
|
1221
|
+
`- Set \`storyPointValue\` based on estimated complexity`,
|
|
1222
|
+
``,
|
|
1177
1223
|
`### Autonomous Guidelines:`,
|
|
1178
1224
|
`- Make decisions independently \u2014 do not ask the team for approval at each step`,
|
|
1179
1225
|
`- Only escalate when genuinely blocked (ambiguous requirements, missing access, conflicting instructions)`,
|
|
@@ -1200,13 +1246,12 @@ function buildModePrompt(agentMode, context) {
|
|
|
1200
1246
|
parts.push(
|
|
1201
1247
|
``,
|
|
1202
1248
|
`### Resource Management`,
|
|
1203
|
-
`Your pod starts with minimal resources
|
|
1204
|
-
`BEFORE running any
|
|
1205
|
-
`- **
|
|
1206
|
-
`- **
|
|
1207
|
-
|
|
1208
|
-
`
|
|
1209
|
-
`CRITICAL: Always scale to at least "light" before running any package install command.`
|
|
1249
|
+
`Your pod starts with minimal resources. You MUST call \`scale_up_resources\``,
|
|
1250
|
+
`BEFORE running any resource-intensive operations \u2014 they WILL fail or OOM at baseline resources:`,
|
|
1251
|
+
`- **setup** \u2014 package installs, basic dev servers, light builds`,
|
|
1252
|
+
`- **build** \u2014 full dev servers, test suites, typecheck, lint, E2E tests`,
|
|
1253
|
+
`Actual CPU/memory values are configured per-project. Scaling is one-way (up only).`,
|
|
1254
|
+
`CRITICAL: Always scale to at least "setup" before running any package install command.`
|
|
1210
1255
|
);
|
|
1211
1256
|
}
|
|
1212
1257
|
return parts.join("\n");
|
|
@@ -2305,6 +2350,7 @@ Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>`;
|
|
|
2305
2350
|
}
|
|
2306
2351
|
}
|
|
2307
2352
|
const result = await connection.call("createPullRequest", {
|
|
2353
|
+
sessionId: connection.sessionId,
|
|
2308
2354
|
title,
|
|
2309
2355
|
body,
|
|
2310
2356
|
head: branch,
|
|
@@ -2318,7 +2364,15 @@ Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>`;
|
|
|
2318
2364
|
return textResult(`Pull request #${result.prNumber} created: ${result.prUrl}`);
|
|
2319
2365
|
} catch (error) {
|
|
2320
2366
|
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
2321
|
-
return textResult(
|
|
2367
|
+
return textResult(
|
|
2368
|
+
`Failed to create pull request: ${msg}
|
|
2369
|
+
|
|
2370
|
+
Troubleshooting:
|
|
2371
|
+
- Ensure all changes are committed and pushed to the remote branch
|
|
2372
|
+
- Check that the branch exists on the remote (run: git push -u origin HEAD)
|
|
2373
|
+
- Verify there isn't already an open PR for this branch
|
|
2374
|
+
- If git auth fails, the token may have expired \u2014 retry the operation`
|
|
2375
|
+
);
|
|
2322
2376
|
}
|
|
2323
2377
|
}
|
|
2324
2378
|
);
|
|
@@ -2455,9 +2509,9 @@ function buildVoteSuggestionTool(connection) {
|
|
|
2455
2509
|
function buildScaleUpResourcesTool(connection) {
|
|
2456
2510
|
return defineTool(
|
|
2457
2511
|
"scale_up_resources",
|
|
2458
|
-
"Scale up the pod's CPU and memory resources. Use before running dev servers, tests, builds, or other resource-intensive operations.
|
|
2512
|
+
"Scale up the pod's CPU and memory resources. Use before running dev servers, tests, builds, or other resource-intensive operations. Phases: 'setup' (installs, basic dev servers), 'build' (full dev servers, test suites, typecheck, builds). Actual CPU/memory values are configured per-project. Scaling is one-way (up only).",
|
|
2459
2513
|
{
|
|
2460
|
-
tier: z.enum(["initial", "
|
|
2514
|
+
tier: z.enum(["initial", "setup", "build"]).describe("The resource phase to scale up to"),
|
|
2461
2515
|
reason: z.string().optional().describe("Brief reason for scaling (e.g., 'running test suite')")
|
|
2462
2516
|
},
|
|
2463
2517
|
async ({ tier, reason }) => {
|
|
@@ -2468,13 +2522,8 @@ function buildScaleUpResourcesTool(connection) {
|
|
|
2468
2522
|
reason
|
|
2469
2523
|
});
|
|
2470
2524
|
if (result.success) {
|
|
2471
|
-
if (result.currentTier === result.previousTier) {
|
|
2472
|
-
return textResult(
|
|
2473
|
-
`Already at ${result.currentTier} tier (${result.cpu} CPU / ${result.memory} Gi). No scaling needed.`
|
|
2474
|
-
);
|
|
2475
|
-
}
|
|
2476
2525
|
return textResult(
|
|
2477
|
-
`Scaled to ${result.
|
|
2526
|
+
`Scaled to ${result.currentTier} phase (${result.cpu}). Was ${result.previousTier}.`
|
|
2478
2527
|
);
|
|
2479
2528
|
}
|
|
2480
2529
|
return textResult(
|
|
@@ -2551,14 +2600,15 @@ function buildCreateSubtaskTool(connection) {
|
|
|
2551
2600
|
ordinal: z2.number().optional().describe("Step/order number (0-based)"),
|
|
2552
2601
|
storyPointValue: z2.number().optional().describe(SP_DESCRIPTION)
|
|
2553
2602
|
},
|
|
2554
|
-
async ({ title, description, plan
|
|
2603
|
+
async ({ title, description, plan, ordinal, storyPointValue }) => {
|
|
2555
2604
|
try {
|
|
2556
2605
|
const result = await connection.call("createSubtask", {
|
|
2557
2606
|
sessionId: connection.sessionId,
|
|
2558
2607
|
title,
|
|
2559
|
-
description,
|
|
2560
|
-
|
|
2561
|
-
|
|
2608
|
+
...description !== void 0 && { description },
|
|
2609
|
+
...plan !== void 0 && { plan },
|
|
2610
|
+
...storyPointValue !== void 0 && { storyPointValue },
|
|
2611
|
+
...ordinal !== void 0 && { ordinal }
|
|
2562
2612
|
});
|
|
2563
2613
|
return textResult(`Subtask created with ID: ${result.id}`);
|
|
2564
2614
|
} catch (error) {
|
|
@@ -2581,20 +2631,15 @@ function buildUpdateSubtaskTool(connection) {
|
|
|
2581
2631
|
ordinal: z2.number().optional(),
|
|
2582
2632
|
storyPointValue: z2.number().optional().describe(SP_DESCRIPTION)
|
|
2583
2633
|
},
|
|
2584
|
-
async ({
|
|
2585
|
-
subtaskId,
|
|
2586
|
-
title,
|
|
2587
|
-
description,
|
|
2588
|
-
plan: _plan,
|
|
2589
|
-
ordinal: _ordinal,
|
|
2590
|
-
storyPointValue: _sp
|
|
2591
|
-
}) => {
|
|
2634
|
+
async ({ subtaskId, title, description, plan, storyPointValue }) => {
|
|
2592
2635
|
try {
|
|
2593
2636
|
await connection.call("updateSubtask", {
|
|
2594
2637
|
sessionId: connection.sessionId,
|
|
2595
2638
|
subtaskId,
|
|
2596
|
-
title,
|
|
2597
|
-
description
|
|
2639
|
+
...title !== void 0 && { title },
|
|
2640
|
+
...description !== void 0 && { description },
|
|
2641
|
+
...plan !== void 0 && { plan },
|
|
2642
|
+
...storyPointValue !== void 0 && { storyPointValue }
|
|
2598
2643
|
});
|
|
2599
2644
|
return textResult("Subtask updated.");
|
|
2600
2645
|
} catch (error) {
|
|
@@ -4656,15 +4701,39 @@ function handleAutoToolAccess(toolName, input, hasExitedPlanMode, isParentTask)
|
|
|
4656
4701
|
}
|
|
4657
4702
|
return { behavior: "allow", updatedInput: input };
|
|
4658
4703
|
}
|
|
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
|
+
}
|
|
4659
4712
|
async function handleExitPlanMode(host, input) {
|
|
4713
|
+
if (host.hasExitedPlanMode) {
|
|
4714
|
+
return { behavior: "allow", updatedInput: input };
|
|
4715
|
+
}
|
|
4660
4716
|
try {
|
|
4661
4717
|
host.syncPlanFile();
|
|
4662
4718
|
const taskProps = await host.connection.getTaskProperties();
|
|
4663
|
-
const missingProps =
|
|
4664
|
-
if (
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4719
|
+
const missingProps = collectMissingProps(taskProps);
|
|
4720
|
+
if (host.isParentTask) {
|
|
4721
|
+
try {
|
|
4722
|
+
const subtasks = await host.connection.call("listSubtasks", {
|
|
4723
|
+
sessionId: host.connection.sessionId
|
|
4724
|
+
});
|
|
4725
|
+
const subtasksWithoutPlans = subtasks.filter(
|
|
4726
|
+
(s) => !s.plan?.trim()
|
|
4727
|
+
);
|
|
4728
|
+
if (subtasksWithoutPlans.length > 0) {
|
|
4729
|
+
const names = subtasksWithoutPlans.map((s) => s.title).join(", ");
|
|
4730
|
+
missingProps.push(
|
|
4731
|
+
`subtask plans \u2014 these subtasks are missing plans: ${names} (use update_subtask with plan field)`
|
|
4732
|
+
);
|
|
4733
|
+
}
|
|
4734
|
+
} catch {
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4668
4737
|
if (missingProps.length > 0) {
|
|
4669
4738
|
return {
|
|
4670
4739
|
behavior: "deny",
|
|
@@ -5223,6 +5292,8 @@ var QueryBridge = class {
|
|
|
5223
5292
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5224
5293
|
logger2.error("Query execution failed", { error: msg });
|
|
5225
5294
|
this.connection.sendEvent({ type: "error", message: msg });
|
|
5295
|
+
} finally {
|
|
5296
|
+
this.mode.pendingModeRestart = false;
|
|
5226
5297
|
}
|
|
5227
5298
|
}
|
|
5228
5299
|
// ── QueryHost construction ──────────────────────────────────────────
|
|
@@ -5431,6 +5502,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
5431
5502
|
if (!this.stopped && this.pendingMessages.length === 0) {
|
|
5432
5503
|
await this.maybeSendPRNudge();
|
|
5433
5504
|
}
|
|
5505
|
+
this.hasCompleted = true;
|
|
5434
5506
|
if (!this.stopped) await this.setState("idle");
|
|
5435
5507
|
} else if (this._state === "error") {
|
|
5436
5508
|
await this.setState("idle");
|
|
@@ -6624,7 +6696,7 @@ var ProjectRunner = class {
|
|
|
6624
6696
|
}
|
|
6625
6697
|
// ── Event wiring ───────────────────────────────────────────────────────
|
|
6626
6698
|
wireEventHandlers() {
|
|
6627
|
-
this.connection.onTaskAssignment((assignment) => this.handleAssignment(assignment));
|
|
6699
|
+
this.connection.onTaskAssignment((assignment) => void this.handleAssignment(assignment));
|
|
6628
6700
|
this.connection.onStopTask((data) => this.handleStopTask(data.taskId));
|
|
6629
6701
|
this.connection.onShutdown(() => void this.stop());
|
|
6630
6702
|
this.connection.onChatMessage((msg) => {
|
|
@@ -6682,13 +6754,42 @@ var ProjectRunner = class {
|
|
|
6682
6754
|
}
|
|
6683
6755
|
}
|
|
6684
6756
|
// ── Task management ────────────────────────────────────────────────────
|
|
6685
|
-
|
|
6757
|
+
async killAgent(agent, taskId) {
|
|
6758
|
+
const shortId = taskId.slice(0, 8);
|
|
6759
|
+
if (agent.process.exitCode !== null) {
|
|
6760
|
+
logger6.info("Agent process already exited", { taskId: shortId });
|
|
6761
|
+
return;
|
|
6762
|
+
}
|
|
6763
|
+
logger6.info("Killing agent process", { taskId: shortId });
|
|
6764
|
+
agent.process.kill("SIGTERM");
|
|
6765
|
+
await new Promise((resolve2) => {
|
|
6766
|
+
const timer = setTimeout(() => {
|
|
6767
|
+
if (agent.process.exitCode === null) {
|
|
6768
|
+
logger6.warn("Agent did not exit after SIGTERM, sending SIGKILL", { taskId: shortId });
|
|
6769
|
+
agent.process.kill("SIGKILL");
|
|
6770
|
+
}
|
|
6771
|
+
resolve2();
|
|
6772
|
+
}, STOP_TIMEOUT_MS);
|
|
6773
|
+
agent.process.on("exit", () => {
|
|
6774
|
+
clearTimeout(timer);
|
|
6775
|
+
resolve2();
|
|
6776
|
+
});
|
|
6777
|
+
});
|
|
6778
|
+
}
|
|
6779
|
+
// oxlint-disable-next-line max-lines-per-function -- re-assignment logic requires sequential checks
|
|
6780
|
+
async handleAssignment(assignment) {
|
|
6686
6781
|
const { taskId, mode } = assignment;
|
|
6687
6782
|
const shortId = taskId.slice(0, 8);
|
|
6688
6783
|
const agentKey = assignment.agentMode === "code-review" ? `${taskId}:code-review` : taskId;
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6784
|
+
const existing = this.activeAgents.get(agentKey);
|
|
6785
|
+
if (existing) {
|
|
6786
|
+
if (existing.process.exitCode === null) {
|
|
6787
|
+
logger6.info("Re-assignment received, killing existing agent", { taskId: shortId });
|
|
6788
|
+
await this.killAgent(existing, taskId);
|
|
6789
|
+
} else {
|
|
6790
|
+
logger6.info("Stale agent entry (process already exited), cleaning up", { taskId: shortId });
|
|
6791
|
+
}
|
|
6792
|
+
this.activeAgents.delete(agentKey);
|
|
6692
6793
|
}
|
|
6693
6794
|
if (this.activeAgents.size >= MAX_CONCURRENT) {
|
|
6694
6795
|
logger6.warn("Max concurrent agents reached", { maxConcurrent: MAX_CONCURRENT });
|
|
@@ -6730,16 +6831,11 @@ var ProjectRunner = class {
|
|
|
6730
6831
|
}
|
|
6731
6832
|
}
|
|
6732
6833
|
handleStopTask(taskId) {
|
|
6733
|
-
|
|
6734
|
-
|
|
6834
|
+
const agentKey = this.activeAgents.has(taskId) ? taskId : `${taskId}:code-review`;
|
|
6835
|
+
const agent = this.activeAgents.get(agentKey);
|
|
6735
6836
|
if (!agent) return;
|
|
6736
6837
|
logger6.info("Stopping task", { taskId: taskId.slice(0, 8) });
|
|
6737
|
-
agent.
|
|
6738
|
-
const timer = setTimeout(() => {
|
|
6739
|
-
if (this.activeAgents.has(taskId)) agent.process.kill("SIGKILL");
|
|
6740
|
-
}, STOP_TIMEOUT_MS);
|
|
6741
|
-
agent.process.on("exit", () => {
|
|
6742
|
-
clearTimeout(timer);
|
|
6838
|
+
void this.killAgent(agent, taskId).then(() => {
|
|
6743
6839
|
if (agent.usesWorktree) {
|
|
6744
6840
|
try {
|
|
6745
6841
|
removeWorktree(this.projectDir, taskId);
|
|
@@ -7027,4 +7123,4 @@ export {
|
|
|
7027
7123
|
removeWorktree,
|
|
7028
7124
|
ProjectRunner
|
|
7029
7125
|
};
|
|
7030
|
-
//# sourceMappingURL=chunk-
|
|
7126
|
+
//# sourceMappingURL=chunk-K2TJHPMA.js.map
|