@rallycry/conveyor-agent 4.4.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-FS3A4THO.js → chunk-XG5DMOJX.js} +175 -43
- package/dist/chunk-XG5DMOJX.js.map +1 -0
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-FS3A4THO.js.map +0 -1
|
@@ -103,9 +103,11 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
103
103
|
static EVENT_BATCH_MS = 500;
|
|
104
104
|
earlyMessages = [];
|
|
105
105
|
earlyStop = false;
|
|
106
|
+
earlySoftStop = false;
|
|
106
107
|
earlyModeChanges = [];
|
|
107
108
|
chatMessageCallback = null;
|
|
108
109
|
stopCallback = null;
|
|
110
|
+
softStopCallback = null;
|
|
109
111
|
modeChangeCallback = null;
|
|
110
112
|
runStartCommandCallback = null;
|
|
111
113
|
pendingQuestionResolvers = /* @__PURE__ */ new Map();
|
|
@@ -135,6 +137,10 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
135
137
|
if (this.stopCallback) this.stopCallback();
|
|
136
138
|
else this.earlyStop = true;
|
|
137
139
|
});
|
|
140
|
+
this.socket.on("agentRunner:softStop", () => {
|
|
141
|
+
if (this.softStopCallback) this.softStopCallback();
|
|
142
|
+
else this.earlySoftStop = true;
|
|
143
|
+
});
|
|
138
144
|
this.socket.on("agentRunner:questionAnswer", (data) => {
|
|
139
145
|
const resolver = this.pendingQuestionResolvers.get(data.requestId);
|
|
140
146
|
if (resolver) {
|
|
@@ -233,6 +239,13 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
233
239
|
this.earlyStop = false;
|
|
234
240
|
}
|
|
235
241
|
}
|
|
242
|
+
onSoftStopRequested(callback) {
|
|
243
|
+
this.softStopCallback = callback;
|
|
244
|
+
if (this.earlySoftStop) {
|
|
245
|
+
callback();
|
|
246
|
+
this.earlySoftStop = false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
236
249
|
onModeChange(callback) {
|
|
237
250
|
this.modeChangeCallback = callback;
|
|
238
251
|
for (const data of this.earlyModeChanges) callback(data);
|
|
@@ -253,6 +266,10 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
253
266
|
if (!this.socket) return;
|
|
254
267
|
this.socket.emit("agentRunner:statusUpdate", { status });
|
|
255
268
|
}
|
|
269
|
+
emitRateLimitPause(resetsAt) {
|
|
270
|
+
if (!this.socket) return;
|
|
271
|
+
this.socket.emit("agentRunner:rateLimitPause", { resetsAt });
|
|
272
|
+
}
|
|
256
273
|
sendHeartbeat() {
|
|
257
274
|
if (!this.socket) return;
|
|
258
275
|
this.socket.emit("agentRunner:heartbeat", {});
|
|
@@ -534,14 +551,6 @@ async function loadConveyorConfig(workspaceDir) {
|
|
|
534
551
|
if (parsed.setupCommand || parsed.startCommand) return parsed;
|
|
535
552
|
} catch {
|
|
536
553
|
}
|
|
537
|
-
try {
|
|
538
|
-
const raw = await readFile(join2(workspaceDir, DEVCONTAINER_PATH), "utf-8");
|
|
539
|
-
const parsed = JSON.parse(raw);
|
|
540
|
-
if (parsed.conveyor && (parsed.conveyor.startCommand || parsed.conveyor.setupCommand)) {
|
|
541
|
-
return parsed.conveyor;
|
|
542
|
-
}
|
|
543
|
-
} catch {
|
|
544
|
-
}
|
|
545
554
|
return null;
|
|
546
555
|
}
|
|
547
556
|
|
|
@@ -727,16 +736,19 @@ function handleRateLimitEvent(event, host) {
|
|
|
727
736
|
const { rate_limit_info } = event;
|
|
728
737
|
const status = rate_limit_info.status;
|
|
729
738
|
if (status === "rejected") {
|
|
730
|
-
const resetsAt = rate_limit_info.resetsAt ? new Date(rate_limit_info.resetsAt).toISOString() :
|
|
731
|
-
const
|
|
739
|
+
const resetsAt = rate_limit_info.resetsAt ? new Date(rate_limit_info.resetsAt).toISOString() : void 0;
|
|
740
|
+
const resetsAtDisplay = resetsAt ?? "unknown";
|
|
741
|
+
const message = `Rate limit rejected (type: ${rate_limit_info.rateLimitType ?? "unknown"}, resets at: ${resetsAtDisplay})`;
|
|
732
742
|
host.connection.sendEvent({ type: "error", message });
|
|
733
743
|
void host.callbacks.onEvent({ type: "error", message });
|
|
744
|
+
return resetsAt;
|
|
734
745
|
} else if (status === "allowed_warning") {
|
|
735
746
|
const utilization = rate_limit_info.utilization ? `${Math.round(rate_limit_info.utilization * 100)}%` : "high";
|
|
736
747
|
const message = `Rate limit warning: ${utilization} utilization (type: ${rate_limit_info.rateLimitType ?? "unknown"})`;
|
|
737
748
|
host.connection.sendEvent({ type: "thinking", message });
|
|
738
749
|
void host.callbacks.onEvent({ type: "thinking", message });
|
|
739
750
|
}
|
|
751
|
+
return void 0;
|
|
740
752
|
}
|
|
741
753
|
async function processEvents(events, context, host) {
|
|
742
754
|
const startTime = Date.now();
|
|
@@ -744,6 +756,7 @@ async function processEvents(events, context, host) {
|
|
|
744
756
|
let isTyping = false;
|
|
745
757
|
let retriable = false;
|
|
746
758
|
let resultSummary;
|
|
759
|
+
let rateLimitResetsAt;
|
|
747
760
|
const turnToolCalls = [];
|
|
748
761
|
for await (const event of events) {
|
|
749
762
|
if (host.isStopped()) break;
|
|
@@ -793,7 +806,8 @@ async function processEvents(events, context, host) {
|
|
|
793
806
|
break;
|
|
794
807
|
}
|
|
795
808
|
case "rate_limit_event": {
|
|
796
|
-
handleRateLimitEvent(event, host);
|
|
809
|
+
const resetsAt = handleRateLimitEvent(event, host);
|
|
810
|
+
if (resetsAt) rateLimitResetsAt = resetsAt;
|
|
797
811
|
break;
|
|
798
812
|
}
|
|
799
813
|
}
|
|
@@ -801,7 +815,7 @@ async function processEvents(events, context, host) {
|
|
|
801
815
|
if (isTyping) {
|
|
802
816
|
host.connection.sendTypingStop();
|
|
803
817
|
}
|
|
804
|
-
return { retriable, resultSummary };
|
|
818
|
+
return { retriable, resultSummary, rateLimitResetsAt };
|
|
805
819
|
}
|
|
806
820
|
|
|
807
821
|
// src/execution/query-executor.ts
|
|
@@ -974,16 +988,28 @@ function buildModePrompt(agentMode, context) {
|
|
|
974
988
|
## Mode: Discovery`,
|
|
975
989
|
`You are in Discovery mode \u2014 helping plan and scope this task.`,
|
|
976
990
|
`- You have read-only codebase access (can read files, run git commands, search code)`,
|
|
977
|
-
`- You
|
|
991
|
+
`- You can write plan files in .claude/plans/ only \u2014 no other file writes`,
|
|
978
992
|
`- You can create and manage subtasks`,
|
|
979
|
-
`- You cannot write code or edit files (except .claude/plans/)`,
|
|
980
993
|
`- Goal: collaborate with the user to create a clear plan`,
|
|
981
994
|
`- Proactively fill task properties (SP, tags, icon) as the plan takes shape`,
|
|
982
995
|
``,
|
|
996
|
+
`### Self-Identification Tools`,
|
|
997
|
+
`Use these MCP tools to set your own task properties:`,
|
|
998
|
+
`- \`update_task\` \u2014 save your plan and description`,
|
|
999
|
+
`- \`set_story_points\` \u2014 assign story points`,
|
|
1000
|
+
`- \`set_task_title\` \u2014 set an accurate title`,
|
|
1001
|
+
`- \`set_task_tags\` \u2014 categorize the work`,
|
|
1002
|
+
`- \`set_task_icon\` / \`generate_task_icon\` \u2014 set a task icon (call \`list_icons\` first)`,
|
|
1003
|
+
``,
|
|
983
1004
|
`### Self-Update vs Subtasks`,
|
|
984
1005
|
`- If the work fits in a single task (1-3 SP), update YOUR OWN plan and properties \u2014 do not create subtasks`,
|
|
985
1006
|
`- Only create subtasks when the work genuinely requires multiple independent pieces (e.g., Pack-tier work, 8+ SP)`,
|
|
986
|
-
|
|
1007
|
+
``,
|
|
1008
|
+
`### Finishing Planning`,
|
|
1009
|
+
`Once your plan is complete and all required properties are set, call the **ExitPlanMode** tool.`,
|
|
1010
|
+
`- Required before ExitPlanMode will succeed: **plan** (via update_task), **story points** (via set_story_points), **title** (via set_task_title)`,
|
|
1011
|
+
`- ExitPlanMode validates these properties and moves the task to Open status`,
|
|
1012
|
+
`- It does NOT start building \u2014 the team controls when to switch to Build mode`
|
|
987
1013
|
];
|
|
988
1014
|
if (context) {
|
|
989
1015
|
parts.push(...buildPropertyInstructions(context));
|
|
@@ -1357,7 +1383,15 @@ ${context.plan}`);
|
|
|
1357
1383
|
}
|
|
1358
1384
|
return parts;
|
|
1359
1385
|
}
|
|
1360
|
-
function buildFreshInstructions(isPm, isAutoMode, context) {
|
|
1386
|
+
function buildFreshInstructions(isPm, isAutoMode, context, agentMode) {
|
|
1387
|
+
if (isPm && agentMode === "building") {
|
|
1388
|
+
return [
|
|
1389
|
+
`Your plan has been approved. Begin implementing it now.`,
|
|
1390
|
+
`Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
|
|
1391
|
+
`Start by reading the relevant source files mentioned in the plan, then write code.`,
|
|
1392
|
+
`When finished, commit and push your changes, then use the create_pull_request tool to open a PR. Do NOT use gh CLI.`
|
|
1393
|
+
];
|
|
1394
|
+
}
|
|
1361
1395
|
if (isAutoMode && isPm) {
|
|
1362
1396
|
return [
|
|
1363
1397
|
`You are operating autonomously. Begin planning immediately.`,
|
|
@@ -1432,11 +1466,22 @@ function buildInstructions(mode, context, scenario, agentMode) {
|
|
|
1432
1466
|
## Instructions`];
|
|
1433
1467
|
const isPm = mode === "pm";
|
|
1434
1468
|
if (scenario === "fresh") {
|
|
1435
|
-
parts.push(...buildFreshInstructions(isPm, agentMode === "auto", context));
|
|
1469
|
+
parts.push(...buildFreshInstructions(isPm, agentMode === "auto", context, agentMode));
|
|
1436
1470
|
return parts;
|
|
1437
1471
|
}
|
|
1438
1472
|
if (scenario === "idle_relaunch") {
|
|
1439
|
-
if (isPm) {
|
|
1473
|
+
if (isPm && (agentMode === "building" || agentMode === "review")) {
|
|
1474
|
+
parts.push(
|
|
1475
|
+
`You were relaunched but no new instructions have been given since your last run.`,
|
|
1476
|
+
`Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
|
|
1477
|
+
`Run \`git log --oneline -10\` to review what you already committed, then verify the current state is correct.`,
|
|
1478
|
+
`Reply with a brief status update summarizing where things stand (visible in chat).`,
|
|
1479
|
+
`Then wait for further instructions \u2014 do NOT redo work that was already completed.`
|
|
1480
|
+
);
|
|
1481
|
+
if (context.githubPRUrl) {
|
|
1482
|
+
parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
|
|
1483
|
+
}
|
|
1484
|
+
} else if (isPm) {
|
|
1440
1485
|
parts.push(
|
|
1441
1486
|
`You were relaunched but no new instructions have been given since your last run.`,
|
|
1442
1487
|
`You are the project manager for this task.`,
|
|
@@ -2130,6 +2175,12 @@ async function handleExitPlanMode(host, input) {
|
|
|
2130
2175
|
}
|
|
2131
2176
|
await host.connection.triggerIdentification();
|
|
2132
2177
|
host.hasExitedPlanMode = true;
|
|
2178
|
+
if (host.agentMode === "discovery") {
|
|
2179
|
+
host.connection.postChatMessage(
|
|
2180
|
+
"Task identified and moved to Open. Switch to Build mode when ready to start implementation."
|
|
2181
|
+
);
|
|
2182
|
+
return { behavior: "allow", updatedInput: input };
|
|
2183
|
+
}
|
|
2133
2184
|
const newMode = host.isParentTask ? "review" : "building";
|
|
2134
2185
|
host.pendingModeRestart = true;
|
|
2135
2186
|
if (host.onModeTransition) {
|
|
@@ -2169,7 +2220,7 @@ async function handleAskUserQuestion(host, input) {
|
|
|
2169
2220
|
}
|
|
2170
2221
|
function buildCanUseTool(host) {
|
|
2171
2222
|
return async (toolName, input) => {
|
|
2172
|
-
if (toolName === "ExitPlanMode" && host.agentMode === "auto" && !host.hasExitedPlanMode) {
|
|
2223
|
+
if (toolName === "ExitPlanMode" && (host.agentMode === "auto" || host.agentMode === "discovery") && !host.hasExitedPlanMode) {
|
|
2173
2224
|
return await handleExitPlanMode(host, input);
|
|
2174
2225
|
}
|
|
2175
2226
|
if (toolName === "AskUserQuestion") {
|
|
@@ -2235,9 +2286,6 @@ function buildSandboxConfig(host) {
|
|
|
2235
2286
|
}
|
|
2236
2287
|
};
|
|
2237
2288
|
}
|
|
2238
|
-
function isActiveBuildMode(mode, hasExitedPlanMode) {
|
|
2239
|
-
return mode === "building" || mode === "review" || mode === "auto" && hasExitedPlanMode;
|
|
2240
|
-
}
|
|
2241
2289
|
function isReadOnlyMode(mode, hasExitedPlanMode) {
|
|
2242
2290
|
return mode === "discovery" || mode === "help" || mode === "auto" && !hasExitedPlanMode;
|
|
2243
2291
|
}
|
|
@@ -2250,7 +2298,9 @@ function buildDisallowedTools(settings, mode, hasExitedPlanMode) {
|
|
|
2250
2298
|
function buildQueryOptions(host, context) {
|
|
2251
2299
|
const settings = context.agentSettings ?? host.config.agentSettings ?? {};
|
|
2252
2300
|
const mode = host.agentMode;
|
|
2253
|
-
const
|
|
2301
|
+
const isCloud = host.config.mode === "pm";
|
|
2302
|
+
const isReadOnly = isReadOnlyMode(mode, host.hasExitedPlanMode);
|
|
2303
|
+
const needsCanUseTool = isCloud && isReadOnly;
|
|
2254
2304
|
const systemPromptText = buildSystemPrompt(
|
|
2255
2305
|
host.config.mode,
|
|
2256
2306
|
context,
|
|
@@ -2268,8 +2318,8 @@ function buildQueryOptions(host, context) {
|
|
|
2268
2318
|
},
|
|
2269
2319
|
settingSources,
|
|
2270
2320
|
cwd: host.config.workspaceDir,
|
|
2271
|
-
permissionMode:
|
|
2272
|
-
allowDangerouslySkipPermissions: !
|
|
2321
|
+
permissionMode: needsCanUseTool ? "acceptEdits" : "bypassPermissions",
|
|
2322
|
+
allowDangerouslySkipPermissions: !needsCanUseTool,
|
|
2273
2323
|
canUseTool: buildCanUseTool(host),
|
|
2274
2324
|
tools: { type: "preset", preset: "claude_code" },
|
|
2275
2325
|
mcpServers: { conveyor: createConveyorMcpServer(host.connection, host.config, context) },
|
|
@@ -2282,7 +2332,7 @@ function buildQueryOptions(host, context) {
|
|
|
2282
2332
|
disallowedTools: buildDisallowedTools(settings, mode, host.hasExitedPlanMode),
|
|
2283
2333
|
enableFileCheckpointing: settings.enableFileCheckpointing
|
|
2284
2334
|
};
|
|
2285
|
-
if (
|
|
2335
|
+
if (needsCanUseTool) {
|
|
2286
2336
|
baseOptions.sandbox = buildSandboxConfig(host);
|
|
2287
2337
|
}
|
|
2288
2338
|
return baseOptions;
|
|
@@ -2361,7 +2411,12 @@ async function runSdkQuery(host, context, followUpContent) {
|
|
|
2361
2411
|
prompt: typeof prompt === "string" ? prompt : host.createInputStream(prompt),
|
|
2362
2412
|
options: { ...options, resume }
|
|
2363
2413
|
});
|
|
2364
|
-
|
|
2414
|
+
host.activeQuery = agentQuery;
|
|
2415
|
+
try {
|
|
2416
|
+
await runWithRetry(agentQuery, context, host, options);
|
|
2417
|
+
} finally {
|
|
2418
|
+
host.activeQuery = null;
|
|
2419
|
+
}
|
|
2365
2420
|
} else if (isDiscoveryLike) {
|
|
2366
2421
|
return;
|
|
2367
2422
|
} else {
|
|
@@ -2371,7 +2426,12 @@ async function runSdkQuery(host, context, followUpContent) {
|
|
|
2371
2426
|
prompt: host.createInputStream(prompt),
|
|
2372
2427
|
options: { ...options, resume }
|
|
2373
2428
|
});
|
|
2374
|
-
|
|
2429
|
+
host.activeQuery = agentQuery;
|
|
2430
|
+
try {
|
|
2431
|
+
await runWithRetry(agentQuery, context, host, options);
|
|
2432
|
+
} finally {
|
|
2433
|
+
host.activeQuery = null;
|
|
2434
|
+
}
|
|
2375
2435
|
}
|
|
2376
2436
|
if (isDiscoveryLike) {
|
|
2377
2437
|
host.syncPlanFile();
|
|
@@ -2452,12 +2512,23 @@ async function runWithRetry(initialQuery, context, host, options) {
|
|
|
2452
2512
|
if (host.isStopped()) return;
|
|
2453
2513
|
const agentQuery = attempt === 0 ? initialQuery : buildRetryQuery(host, context, options, lastErrorWasImage);
|
|
2454
2514
|
try {
|
|
2455
|
-
const { retriable, resultSummary, modeRestart } = await processEvents(
|
|
2515
|
+
const { retriable, resultSummary, modeRestart, rateLimitResetsAt } = await processEvents(
|
|
2456
2516
|
agentQuery,
|
|
2457
2517
|
context,
|
|
2458
2518
|
host
|
|
2459
2519
|
);
|
|
2460
|
-
if (modeRestart ||
|
|
2520
|
+
if (modeRestart || host.isStopped()) return;
|
|
2521
|
+
if (rateLimitResetsAt) {
|
|
2522
|
+
const resetMs = new Date(rateLimitResetsAt).getTime() - Date.now();
|
|
2523
|
+
if (resetMs > 5 * 6e4) {
|
|
2524
|
+
host.connection.emitRateLimitPause(rateLimitResetsAt);
|
|
2525
|
+
host.connection.postChatMessage(
|
|
2526
|
+
`Rate limited. The task will be automatically re-queued and resume after ${new Date(rateLimitResetsAt).toLocaleString()}.`
|
|
2527
|
+
);
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
if (!retriable) return;
|
|
2461
2532
|
lastErrorWasImage = IMAGE_ERROR_PATTERN2.test(resultSummary ?? "");
|
|
2462
2533
|
} catch (error) {
|
|
2463
2534
|
if (isStaleOrExitedSession(error, context) && context.claudeSessionId) {
|
|
@@ -2646,6 +2717,39 @@ async function runSetupSafe(runnerConfig, connection, callbacks, setupLog, effec
|
|
|
2646
2717
|
() => void 0
|
|
2647
2718
|
);
|
|
2648
2719
|
}
|
|
2720
|
+
const pullBranch = process.env.CONVEYOR_PULL_BRANCH;
|
|
2721
|
+
if (pullBranch) {
|
|
2722
|
+
pushSetupLog(setupLog, `[conveyor] Merging latest from ${pullBranch}...`);
|
|
2723
|
+
connection.sendEvent({
|
|
2724
|
+
type: "setup_output",
|
|
2725
|
+
stream: "stdout",
|
|
2726
|
+
data: `Merging latest from ${pullBranch}...
|
|
2727
|
+
`
|
|
2728
|
+
});
|
|
2729
|
+
try {
|
|
2730
|
+
await runSetupCommand(
|
|
2731
|
+
`git fetch origin ${pullBranch} && git merge origin/${pullBranch} --no-edit`,
|
|
2732
|
+
runnerConfig.workspaceDir,
|
|
2733
|
+
(stream, data) => {
|
|
2734
|
+
connection.sendEvent({ type: "setup_output", stream, data });
|
|
2735
|
+
for (const line of data.split("\n").filter(Boolean)) {
|
|
2736
|
+
pushSetupLog(setupLog, `[${stream}] ${line}`);
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
);
|
|
2740
|
+
pushSetupLog(setupLog, `[conveyor] Merge complete`);
|
|
2741
|
+
} catch (error) {
|
|
2742
|
+
const message = `Failed to merge ${pullBranch}: ${error instanceof Error ? error.message : "unknown error"}`;
|
|
2743
|
+
connection.sendEvent({ type: "setup_error", message });
|
|
2744
|
+
await callbacks.onEvent({ type: "setup_error", message });
|
|
2745
|
+
connection.postChatMessage(
|
|
2746
|
+
`Failed to merge latest code from \`${pullBranch}\`. There may be conflicts between the prebuild branch and dev.
|
|
2747
|
+
|
|
2748
|
+
${message}`
|
|
2749
|
+
);
|
|
2750
|
+
return { ok: false, deferredStartConfig: null };
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2649
2753
|
const config = await loadConveyorConfig(runnerConfig.workspaceDir);
|
|
2650
2754
|
if (!config) {
|
|
2651
2755
|
connection.sendEvent({ type: "setup_complete" });
|
|
@@ -2748,13 +2852,8 @@ async function handleAutoModeRestart(state, connection, setState, runQuerySafe,
|
|
|
2748
2852
|
const newMode = state.agentMode;
|
|
2749
2853
|
const isParentTask = !!taskContext.isParentTask;
|
|
2750
2854
|
const newModel = getModelForMode(taskContext, newMode, isParentTask);
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
taskContext.claudeSessionId = resumeSessionId;
|
|
2754
|
-
} else {
|
|
2755
|
-
taskContext.claudeSessionId = null;
|
|
2756
|
-
connection.storeSessionId("");
|
|
2757
|
-
}
|
|
2855
|
+
taskContext.claudeSessionId = null;
|
|
2856
|
+
connection.storeSessionId("");
|
|
2758
2857
|
if (newModel) taskContext.model = newModel;
|
|
2759
2858
|
taskContext.agentMode = newMode;
|
|
2760
2859
|
connection.emitModeTransition({ fromMode: "auto", toMode: newMode ?? "building" });
|
|
@@ -2799,6 +2898,7 @@ function buildQueryHost(deps) {
|
|
|
2799
2898
|
deps.setPendingModeRestart(val);
|
|
2800
2899
|
},
|
|
2801
2900
|
sessionIds: deps.sessionIds,
|
|
2901
|
+
activeQuery: null,
|
|
2802
2902
|
isStopped: deps.isStopped,
|
|
2803
2903
|
createInputStream: deps.createInputStream,
|
|
2804
2904
|
snapshotPlanFiles: () => deps.planSync.snapshotPlanFiles(),
|
|
@@ -2816,6 +2916,7 @@ var AgentRunner = class {
|
|
|
2816
2916
|
callbacks;
|
|
2817
2917
|
_state = "connecting";
|
|
2818
2918
|
stopped = false;
|
|
2919
|
+
interrupted = false;
|
|
2819
2920
|
inputResolver = null;
|
|
2820
2921
|
pendingMessages = [];
|
|
2821
2922
|
setupLog = [];
|
|
@@ -2833,6 +2934,7 @@ var AgentRunner = class {
|
|
|
2833
2934
|
idleTimer = null;
|
|
2834
2935
|
idleCheckInterval = null;
|
|
2835
2936
|
deferredStartConfig = null;
|
|
2937
|
+
_queryHost = null;
|
|
2836
2938
|
constructor(config, callbacks) {
|
|
2837
2939
|
this.config = config;
|
|
2838
2940
|
this.connection = new ConveyorConnection(config);
|
|
@@ -2879,6 +2981,7 @@ var AgentRunner = class {
|
|
|
2879
2981
|
await this.setState("connecting");
|
|
2880
2982
|
await this.connection.connect();
|
|
2881
2983
|
this.connection.onStopRequested(() => this.stop());
|
|
2984
|
+
this.connection.onSoftStopRequested(() => this.softStop());
|
|
2882
2985
|
this.connection.onChatMessage(
|
|
2883
2986
|
(message) => this.injectHumanMessage(message.content, message.files)
|
|
2884
2987
|
);
|
|
@@ -3021,9 +3124,17 @@ var AgentRunner = class {
|
|
|
3021
3124
|
}
|
|
3022
3125
|
if (this._state === "idle") {
|
|
3023
3126
|
const msg = await this.waitForUserContent();
|
|
3024
|
-
if (!msg)
|
|
3127
|
+
if (!msg) {
|
|
3128
|
+
if (this.interrupted && !this.stopped) {
|
|
3129
|
+
this.interrupted = false;
|
|
3130
|
+
continue;
|
|
3131
|
+
}
|
|
3132
|
+
break;
|
|
3133
|
+
}
|
|
3025
3134
|
await this.setState("running");
|
|
3135
|
+
this.interrupted = false;
|
|
3026
3136
|
await this.runQuerySafe(this.taskContext, msg);
|
|
3137
|
+
if (this.interrupted) this.interrupted = false;
|
|
3027
3138
|
if (!this.stopped && this._state !== "error") await this.setState("idle");
|
|
3028
3139
|
} else if (this._state === "error") {
|
|
3029
3140
|
await this.setState("idle");
|
|
@@ -3076,7 +3187,7 @@ var AgentRunner = class {
|
|
|
3076
3187
|
this.clearIdleTimers();
|
|
3077
3188
|
return new Promise((resolve2) => {
|
|
3078
3189
|
this.idleCheckInterval = setInterval(() => {
|
|
3079
|
-
if (this.stopped) {
|
|
3190
|
+
if (this.stopped || this.interrupted) {
|
|
3080
3191
|
this.clearIdleTimers();
|
|
3081
3192
|
this.inputResolver = null;
|
|
3082
3193
|
resolve2(null);
|
|
@@ -3116,7 +3227,7 @@ var AgentRunner = class {
|
|
|
3116
3227
|
});
|
|
3117
3228
|
yield makeUserMessage(initialPrompt);
|
|
3118
3229
|
try {
|
|
3119
|
-
while (!this.stopped) {
|
|
3230
|
+
while (!this.stopped && !this.interrupted) {
|
|
3120
3231
|
if (this.pendingMessages.length > 0) {
|
|
3121
3232
|
const next = this.pendingMessages.shift();
|
|
3122
3233
|
if (next) yield next;
|
|
@@ -3135,7 +3246,7 @@ var AgentRunner = class {
|
|
|
3135
3246
|
}
|
|
3136
3247
|
}
|
|
3137
3248
|
asQueryHost() {
|
|
3138
|
-
|
|
3249
|
+
const host = buildQueryHost({
|
|
3139
3250
|
config: this.config,
|
|
3140
3251
|
connection: this.connection,
|
|
3141
3252
|
callbacks: this.callbacks,
|
|
@@ -3153,12 +3264,14 @@ var AgentRunner = class {
|
|
|
3153
3264
|
setPendingModeRestart: (val) => {
|
|
3154
3265
|
this.pendingModeRestart = val;
|
|
3155
3266
|
},
|
|
3156
|
-
isStopped: () => this.stopped,
|
|
3267
|
+
isStopped: () => this.stopped || this.interrupted,
|
|
3157
3268
|
createInputStream: (prompt) => this.createInputStream(prompt),
|
|
3158
3269
|
onModeTransition: (newMode) => {
|
|
3159
3270
|
this.agentMode = newMode;
|
|
3160
3271
|
}
|
|
3161
3272
|
});
|
|
3273
|
+
this._queryHost = host;
|
|
3274
|
+
return host;
|
|
3162
3275
|
}
|
|
3163
3276
|
handleModeChange(newAgentMode) {
|
|
3164
3277
|
if (this.config.mode !== "pm") return;
|
|
@@ -3168,6 +3281,25 @@ var AgentRunner = class {
|
|
|
3168
3281
|
this.connection.postChatMessage(
|
|
3169
3282
|
`Mode switched to **${effectiveMode}**${effectiveMode === "building" ? " \u2014 I now have direct coding access." : ""}`
|
|
3170
3283
|
);
|
|
3284
|
+
if (effectiveMode === "building" && this.taskContext?.status === "Open") {
|
|
3285
|
+
this.connection.updateStatus("InProgress");
|
|
3286
|
+
this.taskContext.status = "InProgress";
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
softStop() {
|
|
3290
|
+
this.interrupted = true;
|
|
3291
|
+
const host = this._queryHost;
|
|
3292
|
+
if (host?.activeQuery) {
|
|
3293
|
+
const q = host.activeQuery;
|
|
3294
|
+
if (typeof q.interrupt === "function") {
|
|
3295
|
+
void q.interrupt();
|
|
3296
|
+
}
|
|
3297
|
+
host.activeQuery = null;
|
|
3298
|
+
}
|
|
3299
|
+
if (this.inputResolver) {
|
|
3300
|
+
this.inputResolver(null);
|
|
3301
|
+
this.inputResolver = null;
|
|
3302
|
+
}
|
|
3171
3303
|
}
|
|
3172
3304
|
stop() {
|
|
3173
3305
|
this.stopped = true;
|
|
@@ -3641,4 +3773,4 @@ export {
|
|
|
3641
3773
|
ProjectRunner,
|
|
3642
3774
|
FileCache
|
|
3643
3775
|
};
|
|
3644
|
-
//# sourceMappingURL=chunk-
|
|
3776
|
+
//# sourceMappingURL=chunk-XG5DMOJX.js.map
|