autopilot-ai 1.11.0 → 1.12.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/index.js +372 -48
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -20836,6 +20836,39 @@ var init_help_menu = __esm({
|
|
|
20836
20836
|
}
|
|
20837
20837
|
});
|
|
20838
20838
|
|
|
20839
|
+
// src/agent/queue-batch.ts
|
|
20840
|
+
function createQueueBatch(items) {
|
|
20841
|
+
if (items.length < 2) return null;
|
|
20842
|
+
if (items.some((item) => isCommand(item.full))) return null;
|
|
20843
|
+
const prompts = items.flatMap((item) => item.batchPrompts ?? [item.full.trim()]);
|
|
20844
|
+
const sourceKeys = items.flatMap((item) => item.sourceKeys ?? [item.key]);
|
|
20845
|
+
const numberedPrompts = prompts.map((prompt, index) => `${index + 1}. ${prompt}`).join("\n\n");
|
|
20846
|
+
const full = [
|
|
20847
|
+
`The user chose to group ${prompts.length} queued follow-ups into one coordinated task.`,
|
|
20848
|
+
"First identify dependencies and an efficient order. Treat every follow-up as a requirement. Delegate bounded, independent research to subagents when it materially helps; worker calls still require the normal user permission. Keep dependent changes coordinated in this session, and do not have multiple workers edit overlapping files.",
|
|
20849
|
+
"",
|
|
20850
|
+
"Queued follow-ups:",
|
|
20851
|
+
numberedPrompts
|
|
20852
|
+
].join("\n");
|
|
20853
|
+
const display = `Coordinate ${prompts.length} queued follow-ups as one task. Use subagents to research independent parts where useful; keep dependent changes coordinated.`;
|
|
20854
|
+
return {
|
|
20855
|
+
full,
|
|
20856
|
+
display,
|
|
20857
|
+
key: sourceKeys[0],
|
|
20858
|
+
batchPrompts: prompts,
|
|
20859
|
+
sourceKeys
|
|
20860
|
+
};
|
|
20861
|
+
}
|
|
20862
|
+
function isCommand(prompt) {
|
|
20863
|
+
const trimmed = prompt.trimStart();
|
|
20864
|
+
return trimmed.startsWith("/") || trimmed.startsWith("!");
|
|
20865
|
+
}
|
|
20866
|
+
var init_queue_batch = __esm({
|
|
20867
|
+
"src/agent/queue-batch.ts"() {
|
|
20868
|
+
"use strict";
|
|
20869
|
+
}
|
|
20870
|
+
});
|
|
20871
|
+
|
|
20839
20872
|
// src/ui/app-helpers.ts
|
|
20840
20873
|
var app_helpers_exports = {};
|
|
20841
20874
|
__export(app_helpers_exports, {
|
|
@@ -20858,12 +20891,50 @@ __export(app_helpers_exports, {
|
|
|
20858
20891
|
mkKey: () => mkKey,
|
|
20859
20892
|
openBrowser: () => openBrowser,
|
|
20860
20893
|
rebuildSystemPromptForMode: () => rebuildSystemPromptForMode,
|
|
20894
|
+
replaceQueuedPrompt: () => replaceQueuedPrompt,
|
|
20895
|
+
replaceUserPromptEvent: () => replaceUserPromptEvent,
|
|
20861
20896
|
trackRecentFile: () => trackRecentFile
|
|
20862
20897
|
});
|
|
20863
20898
|
import { execSync as execSync3, spawn as spawn6 } from "child_process";
|
|
20864
20899
|
import { existsSync as existsSync5, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
|
|
20865
20900
|
import { join as join32 } from "path";
|
|
20866
20901
|
import { platform as platform3 } from "os";
|
|
20902
|
+
function replaceQueuedPrompt(queue2, key, full, display) {
|
|
20903
|
+
const index = queue2.findIndex((item) => item.key === key);
|
|
20904
|
+
if (index >= 0) {
|
|
20905
|
+
const next2 = [...queue2];
|
|
20906
|
+
next2[index] = { ...next2[index], full, display };
|
|
20907
|
+
return next2;
|
|
20908
|
+
}
|
|
20909
|
+
const groupedIndex = queue2.findIndex((item) => item.sourceKeys?.includes(key));
|
|
20910
|
+
if (groupedIndex < 0) return null;
|
|
20911
|
+
const grouped = queue2[groupedIndex];
|
|
20912
|
+
if (!grouped.sourceKeys || !grouped.batchPrompts) return null;
|
|
20913
|
+
const promptIndex = grouped.sourceKeys.indexOf(key);
|
|
20914
|
+
const replacement = createQueueBatch(
|
|
20915
|
+
grouped.batchPrompts.map((prompt, index2) => ({
|
|
20916
|
+
key: grouped.sourceKeys[index2],
|
|
20917
|
+
full: index2 === promptIndex ? full : prompt,
|
|
20918
|
+
display: index2 === promptIndex ? display : prompt
|
|
20919
|
+
}))
|
|
20920
|
+
);
|
|
20921
|
+
if (!replacement) return null;
|
|
20922
|
+
const next = [...queue2];
|
|
20923
|
+
next[groupedIndex] = replacement;
|
|
20924
|
+
return next;
|
|
20925
|
+
}
|
|
20926
|
+
function replaceUserPromptEvent(events, key, text, discardTurnOutput = false) {
|
|
20927
|
+
const index = events.findIndex((event2) => event2.kind === "user" && event2.key === key);
|
|
20928
|
+
if (index < 0) return events;
|
|
20929
|
+
const event = events[index];
|
|
20930
|
+
if (event.kind !== "user") return events;
|
|
20931
|
+
const replacement = { ...event, text, images: void 0 };
|
|
20932
|
+
if (!discardTurnOutput) {
|
|
20933
|
+
return events.map((item, i) => i === index ? replacement : item);
|
|
20934
|
+
}
|
|
20935
|
+
const queuedFollowUps = events.slice(index + 1).filter((item) => item.kind === "user" && item.queued);
|
|
20936
|
+
return [...events.slice(0, index), replacement, ...queuedFollowUps];
|
|
20937
|
+
}
|
|
20867
20938
|
function buildFilePickerIgnoreList(cwd) {
|
|
20868
20939
|
const hardcoded = [
|
|
20869
20940
|
// Dependencies
|
|
@@ -21095,6 +21166,7 @@ var init_app_helpers = __esm({
|
|
|
21095
21166
|
init_custom_endpoint();
|
|
21096
21167
|
init_system_prompt();
|
|
21097
21168
|
init_image();
|
|
21169
|
+
init_queue_batch();
|
|
21098
21170
|
MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
|
|
21099
21171
|
CONTEXT_LIMIT = 262e3;
|
|
21100
21172
|
AUTO_COMPACT_THRESHOLD = 0.8;
|
|
@@ -23944,6 +24016,67 @@ var init_hooks_wizard = __esm({
|
|
|
23944
24016
|
}
|
|
23945
24017
|
});
|
|
23946
24018
|
|
|
24019
|
+
// src/ui/activity.ts
|
|
24020
|
+
function activityFromJob(job) {
|
|
24021
|
+
const active = job.status === "starting" || job.status === "running" || job.status === "cancelling";
|
|
24022
|
+
const status = active ? "running" : job.status === "completed" ? "done" : job.status === "cancelled" ? "stopped" : "failed";
|
|
24023
|
+
const summary = job.status === "cancelling" ? "Stopping\u2026" : job.status === "timed_out" ? "Timed out" : job.status === "unknown" ? "Process state could not be verified" : job.exitCode !== null && job.exitCode !== 0 ? `Exited with code ${job.exitCode}` : void 0;
|
|
24024
|
+
return {
|
|
24025
|
+
id: job.id,
|
|
24026
|
+
kind: "job",
|
|
24027
|
+
title: job.command,
|
|
24028
|
+
status,
|
|
24029
|
+
stoppable: job.status === "starting" || job.status === "running",
|
|
24030
|
+
...summary ? { summary } : {},
|
|
24031
|
+
started_at_ms: job.createdAt,
|
|
24032
|
+
...job.finishedAt !== null ? { updated_at_ms: job.finishedAt } : {}
|
|
24033
|
+
};
|
|
24034
|
+
}
|
|
24035
|
+
function activityFromWorker(worker) {
|
|
24036
|
+
const status = worker.status === "pending" ? "waiting" : worker.status === "running" ? "running" : worker.status === "completed" ? "done" : worker.status === "budget_exhausted" ? "needs_attention" : "failed";
|
|
24037
|
+
const steps = worker.steps?.map((step) => ({
|
|
24038
|
+
title: step.label,
|
|
24039
|
+
status: step.status === "active" ? "running" : step.status === "completed" ? "done" : step.status === "failed" ? "failed" : "pending"
|
|
24040
|
+
}));
|
|
24041
|
+
const doneCount = steps?.filter((step) => step.status === "done" || step.status === "failed").length ?? 0;
|
|
24042
|
+
const summary = worker.error ?? worker.steps?.find((step) => step.status === "active")?.label ?? (worker.status === "budget_exhausted" ? "Budget exhausted; inspect partial results" : void 0);
|
|
24043
|
+
return {
|
|
24044
|
+
id: `agent:${worker.id}`,
|
|
24045
|
+
kind: "agent",
|
|
24046
|
+
title: `${worker.mode}: ${worker.task}`,
|
|
24047
|
+
status,
|
|
24048
|
+
// A single worker cannot currently be cancelled independently; the only
|
|
24049
|
+
// available AbortSignal cancels the entire multi-agent batch.
|
|
24050
|
+
stoppable: false,
|
|
24051
|
+
...summary ? { summary } : {},
|
|
24052
|
+
...steps && steps.length > 0 ? {
|
|
24053
|
+
progress: worker.status === "completed" ? 1 : doneCount / steps.length,
|
|
24054
|
+
steps
|
|
24055
|
+
} : {},
|
|
24056
|
+
started_at_ms: worker.startedAt
|
|
24057
|
+
};
|
|
24058
|
+
}
|
|
24059
|
+
function diffActivityItems(previous, current, snapshotSent) {
|
|
24060
|
+
if (!snapshotSent) return [{ type: "ActivitySnapshot", payload: { items: [...current] } }];
|
|
24061
|
+
const next = new Map(current.map((item) => [item.id, item]));
|
|
24062
|
+
const events = [];
|
|
24063
|
+
for (const [id, item] of next) {
|
|
24064
|
+
if (previous.get(id) !== JSON.stringify(item)) events.push({ type: "ActivityUpdate", payload: item });
|
|
24065
|
+
}
|
|
24066
|
+
for (const id of previous.keys()) {
|
|
24067
|
+
if (!next.has(id)) events.push({ type: "ActivityRemoved", payload: { id } });
|
|
24068
|
+
}
|
|
24069
|
+
return events;
|
|
24070
|
+
}
|
|
24071
|
+
function isActiveJob(job) {
|
|
24072
|
+
return job.status === "starting" || job.status === "running" || job.status === "cancelling";
|
|
24073
|
+
}
|
|
24074
|
+
var init_activity = __esm({
|
|
24075
|
+
"src/ui/activity.ts"() {
|
|
24076
|
+
"use strict";
|
|
24077
|
+
}
|
|
24078
|
+
});
|
|
24079
|
+
|
|
23947
24080
|
// src/camouflage-view.ts
|
|
23948
24081
|
var camouflage_view_exports = {};
|
|
23949
24082
|
__export(camouflage_view_exports, {
|
|
@@ -24005,6 +24138,7 @@ async function runCamouflageView(opts2) {
|
|
|
24005
24138
|
resume: () => sdk.resumeTerminal?.(cam)
|
|
24006
24139
|
});
|
|
24007
24140
|
cam.on("userInput", (text) => {
|
|
24141
|
+
if (view.routeActivityCommand(text)) return;
|
|
24008
24142
|
view.echoed.push(text.trim());
|
|
24009
24143
|
view.actions?.submit(text);
|
|
24010
24144
|
});
|
|
@@ -24020,10 +24154,15 @@ async function runCamouflageView(opts2) {
|
|
|
24020
24154
|
});
|
|
24021
24155
|
});
|
|
24022
24156
|
cam.on("modeChangeRequested", () => view.actions?.cycleMode());
|
|
24157
|
+
cam.on("activityStopRequested", ({ id }) => view.stopActivity(id));
|
|
24158
|
+
cam.on("activityViewChanged", (change) => {
|
|
24159
|
+
view.activityViewChanged(change);
|
|
24160
|
+
});
|
|
24023
24161
|
let closing = false;
|
|
24024
24162
|
const finish = async () => {
|
|
24025
24163
|
if (closing) return;
|
|
24026
24164
|
closing = true;
|
|
24165
|
+
view.dispose();
|
|
24027
24166
|
registerTerminalHandoff(null);
|
|
24028
24167
|
await cam.close().catch(() => void 0);
|
|
24029
24168
|
restoreConsole();
|
|
@@ -24243,6 +24382,8 @@ var init_camouflage_view = __esm({
|
|
|
24243
24382
|
init_hooks_wizard();
|
|
24244
24383
|
init_mode();
|
|
24245
24384
|
init_logger();
|
|
24385
|
+
init_manager();
|
|
24386
|
+
init_activity();
|
|
24246
24387
|
CamouflageUnavailable = class extends Error {
|
|
24247
24388
|
};
|
|
24248
24389
|
View = class {
|
|
@@ -24268,6 +24409,17 @@ var init_camouflage_view = __esm({
|
|
|
24268
24409
|
permission = null;
|
|
24269
24410
|
openPrompt = null;
|
|
24270
24411
|
commandsKey = "";
|
|
24412
|
+
jobs = new JobManager();
|
|
24413
|
+
sentActivities = /* @__PURE__ */ new Map();
|
|
24414
|
+
workerActivities = /* @__PURE__ */ new Map();
|
|
24415
|
+
jobLogOffsets = /* @__PURE__ */ new Map();
|
|
24416
|
+
workerLogOffsets = /* @__PURE__ */ new Map();
|
|
24417
|
+
latestJobs = [];
|
|
24418
|
+
latestWorkers = [];
|
|
24419
|
+
activitySnapshotSent = false;
|
|
24420
|
+
activityDetailId = null;
|
|
24421
|
+
activityPoll = null;
|
|
24422
|
+
disposed = false;
|
|
24271
24423
|
connect(actions) {
|
|
24272
24424
|
this.actions = actions;
|
|
24273
24425
|
}
|
|
@@ -24282,13 +24434,141 @@ var init_camouflage_view = __esm({
|
|
|
24282
24434
|
}
|
|
24283
24435
|
this.syncPermission(s);
|
|
24284
24436
|
this.syncPrompts(s);
|
|
24437
|
+
this.syncActivity(s.workers);
|
|
24285
24438
|
}
|
|
24286
24439
|
/** Built-in plus the user's custom commands, re-sent when they change. */
|
|
24440
|
+
routeActivityCommand(text) {
|
|
24441
|
+
if (!/^\/(?:jobs|agents)$/i.test(text.trim())) return false;
|
|
24442
|
+
this.cam.send("ActivityBrowserOpen", {});
|
|
24443
|
+
return true;
|
|
24444
|
+
}
|
|
24445
|
+
activityViewChanged(change) {
|
|
24446
|
+
this.activityDetailId = change.view === "detail" ? change.id ?? null : null;
|
|
24447
|
+
this.jobLogOffsets.clear();
|
|
24448
|
+
this.workerLogOffsets.clear();
|
|
24449
|
+
if (this.activityDetailId) {
|
|
24450
|
+
if (this.activityDetailId.startsWith("agent:")) this.streamWorkerLogs(this.activityDetailId, true);
|
|
24451
|
+
else this.streamJobLogs(this.activityDetailId, true);
|
|
24452
|
+
}
|
|
24453
|
+
this.ensureActivityPolling();
|
|
24454
|
+
}
|
|
24455
|
+
stopActivity(id) {
|
|
24456
|
+
try {
|
|
24457
|
+
const job = this.jobs.get(id);
|
|
24458
|
+
if (!job || !isActiveJob(job)) return;
|
|
24459
|
+
this.jobs.cancel(id);
|
|
24460
|
+
this.refreshActivities();
|
|
24461
|
+
} catch (error) {
|
|
24462
|
+
logger.warn("camouflage.activity_stop_failed", { id, error: String(error) });
|
|
24463
|
+
this.cam.send("RuntimeError", { message: `Unable to stop background job: ${error.message}`, severity: "error" });
|
|
24464
|
+
}
|
|
24465
|
+
}
|
|
24466
|
+
dispose() {
|
|
24467
|
+
this.disposed = true;
|
|
24468
|
+
if (this.activityPoll) clearInterval(this.activityPoll);
|
|
24469
|
+
this.activityPoll = null;
|
|
24470
|
+
this.jobs.close();
|
|
24471
|
+
}
|
|
24472
|
+
syncActivity(workers) {
|
|
24473
|
+
this.latestWorkers = workers;
|
|
24474
|
+
try {
|
|
24475
|
+
const records = this.jobs.list({ limit: 100 });
|
|
24476
|
+
this.latestJobs = [
|
|
24477
|
+
...records.filter(isActiveJob),
|
|
24478
|
+
...records.filter((job) => !isActiveJob(job)).slice(0, 5)
|
|
24479
|
+
];
|
|
24480
|
+
} catch (error) {
|
|
24481
|
+
logger.warn("camouflage.activity_jobs_read_failed", { error: String(error) });
|
|
24482
|
+
}
|
|
24483
|
+
const now2 = Date.now();
|
|
24484
|
+
const currentWorkers = /* @__PURE__ */ new Set();
|
|
24485
|
+
for (const worker of workers) {
|
|
24486
|
+
const id = `agent:${worker.id}`;
|
|
24487
|
+
currentWorkers.add(id);
|
|
24488
|
+
this.workerActivities.set(id, {
|
|
24489
|
+
item: activityFromWorker(worker),
|
|
24490
|
+
logs: [...worker.logs],
|
|
24491
|
+
active: true,
|
|
24492
|
+
expiresAt: Number.POSITIVE_INFINITY
|
|
24493
|
+
});
|
|
24494
|
+
}
|
|
24495
|
+
for (const [id, activity] of this.workerActivities) {
|
|
24496
|
+
if (currentWorkers.has(id) || !activity.active) continue;
|
|
24497
|
+
activity.active = false;
|
|
24498
|
+
activity.expiresAt = now2 + 10 * 6e4;
|
|
24499
|
+
if (activity.item.status === "running" || activity.item.status === "waiting") {
|
|
24500
|
+
const cancelled = activity.logs.some((line) => /cancelled by user|cancelling worker/i.test(line));
|
|
24501
|
+
const failed = activity.logs.some((line) => line.includes("[coordinator] Fetch failed:"));
|
|
24502
|
+
activity.item = {
|
|
24503
|
+
...activity.item,
|
|
24504
|
+
status: cancelled ? "stopped" : failed ? "failed" : "done",
|
|
24505
|
+
summary: cancelled ? "Stopped" : failed ? activity.item.summary ?? "Worker failed" : "Worker finished",
|
|
24506
|
+
updated_at_ms: now2
|
|
24507
|
+
};
|
|
24508
|
+
}
|
|
24509
|
+
}
|
|
24510
|
+
for (const [id, activity] of this.workerActivities) {
|
|
24511
|
+
if (!activity.active && activity.expiresAt <= now2) this.workerActivities.delete(id);
|
|
24512
|
+
}
|
|
24513
|
+
const finishedWorkers = [...this.workerActivities.entries()].filter(([, activity]) => !activity.active).sort((a, b) => (b[1].item.updated_at_ms ?? b[1].item.started_at_ms ?? 0) - (a[1].item.updated_at_ms ?? a[1].item.started_at_ms ?? 0));
|
|
24514
|
+
for (const [id] of finishedWorkers.slice(20)) this.workerActivities.delete(id);
|
|
24515
|
+
const next = /* @__PURE__ */ new Map();
|
|
24516
|
+
for (const job of this.latestJobs) next.set(job.id, activityFromJob(job));
|
|
24517
|
+
for (const [id, activity] of this.workerActivities) next.set(id, activity.item);
|
|
24518
|
+
for (const event of diffActivityItems(this.sentActivities, [...next.values()], this.activitySnapshotSent)) {
|
|
24519
|
+
this.cam.send(event.type, event.payload);
|
|
24520
|
+
}
|
|
24521
|
+
this.activitySnapshotSent = true;
|
|
24522
|
+
this.sentActivities.clear();
|
|
24523
|
+
for (const [id, item] of next) this.sentActivities.set(id, JSON.stringify(item));
|
|
24524
|
+
if (this.activityDetailId?.startsWith("agent:")) this.streamWorkerLogs(this.activityDetailId, false);
|
|
24525
|
+
this.ensureActivityPolling();
|
|
24526
|
+
}
|
|
24527
|
+
ensureActivityPolling() {
|
|
24528
|
+
const needsPolling = this.latestJobs.some(isActiveJob);
|
|
24529
|
+
if (needsPolling && !this.activityPoll && !this.disposed) {
|
|
24530
|
+
this.activityPoll = setInterval(() => this.refreshActivities(), 1e3);
|
|
24531
|
+
this.activityPoll.unref?.();
|
|
24532
|
+
} else if (!needsPolling && this.activityPoll) {
|
|
24533
|
+
clearInterval(this.activityPoll);
|
|
24534
|
+
this.activityPoll = null;
|
|
24535
|
+
}
|
|
24536
|
+
}
|
|
24537
|
+
refreshActivities() {
|
|
24538
|
+
if (this.disposed) return;
|
|
24539
|
+
this.syncActivity(this.latestWorkers);
|
|
24540
|
+
const id = this.activityDetailId;
|
|
24541
|
+
if (id && !id.startsWith("agent:")) this.streamJobLogs(id, false);
|
|
24542
|
+
}
|
|
24543
|
+
streamWorkerLogs(id, reset2) {
|
|
24544
|
+
const activity = this.workerActivities.get(id);
|
|
24545
|
+
if (!activity) return;
|
|
24546
|
+
const offset = reset2 ? 0 : this.workerLogOffsets.get(id) ?? 0;
|
|
24547
|
+
const chunk = activity.logs.slice(offset).join("\n");
|
|
24548
|
+
if (chunk) this.cam.send("ActivityLog", { id, chunk: `${chunk}
|
|
24549
|
+
`, stream: "stdout" });
|
|
24550
|
+
this.workerLogOffsets.set(id, activity.logs.length);
|
|
24551
|
+
}
|
|
24552
|
+
streamJobLogs(id, initial) {
|
|
24553
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
24554
|
+
const key = `${id}:${stream}`;
|
|
24555
|
+
try {
|
|
24556
|
+
const result2 = initial ? this.jobs.logs(id, stream, { tail: 1e3 }) : this.jobs.logs(id, stream, { offset: this.jobLogOffsets.get(key) ?? 0 });
|
|
24557
|
+
this.jobLogOffsets.set(key, result2.offset);
|
|
24558
|
+
if (result2.content) this.cam.send("ActivityLog", { id, chunk: result2.content, stream });
|
|
24559
|
+
} catch {
|
|
24560
|
+
}
|
|
24561
|
+
}
|
|
24562
|
+
}
|
|
24287
24563
|
syncCommands(custom) {
|
|
24288
24564
|
const key = custom.map((c) => `${c.name}:${c.description ?? ""}`).join("|");
|
|
24289
24565
|
if (key === this.commandsKey && this.commandsKey !== "") return;
|
|
24290
24566
|
this.commandsKey = key || "(none)";
|
|
24291
|
-
const builtin =
|
|
24567
|
+
const builtin = [
|
|
24568
|
+
...BUILTIN_COMMANDS.map((c) => ({ name: c.name, description: c.description, args_hint: c.argHint })),
|
|
24569
|
+
{ name: "jobs", description: "Browse background jobs and agents", args_hint: void 0 },
|
|
24570
|
+
{ name: "agents", description: "Browse background agents and jobs", args_hint: void 0 }
|
|
24571
|
+
];
|
|
24292
24572
|
const names = new Set(builtin.map((c) => c.name));
|
|
24293
24573
|
const mine = custom.filter((c) => !names.has(c.name)).map((c) => ({ name: c.name, description: c.description ? `${c.description} (custom)` : "Custom command" }));
|
|
24294
24574
|
this.cam.send("SlashCommandsRegistered", { commands: [...builtin, ...mine] });
|
|
@@ -27359,39 +27639,6 @@ var init_queue_plan_picker = __esm({
|
|
|
27359
27639
|
}
|
|
27360
27640
|
});
|
|
27361
27641
|
|
|
27362
|
-
// src/agent/queue-batch.ts
|
|
27363
|
-
function createQueueBatch(items) {
|
|
27364
|
-
if (items.length < 2) return null;
|
|
27365
|
-
if (items.some((item) => isCommand(item.full))) return null;
|
|
27366
|
-
const prompts = items.flatMap((item) => item.batchPrompts ?? [item.full.trim()]);
|
|
27367
|
-
const sourceKeys = items.flatMap((item) => item.sourceKeys ?? [item.key]);
|
|
27368
|
-
const numberedPrompts = prompts.map((prompt, index) => `${index + 1}. ${prompt}`).join("\n\n");
|
|
27369
|
-
const full = [
|
|
27370
|
-
`The user chose to group ${prompts.length} queued follow-ups into one coordinated task.`,
|
|
27371
|
-
"First identify dependencies and an efficient order. Treat every follow-up as a requirement. Delegate bounded, independent research to subagents when it materially helps; worker calls still require the normal user permission. Keep dependent changes coordinated in this session, and do not have multiple workers edit overlapping files.",
|
|
27372
|
-
"",
|
|
27373
|
-
"Queued follow-ups:",
|
|
27374
|
-
numberedPrompts
|
|
27375
|
-
].join("\n");
|
|
27376
|
-
const display = `Coordinate ${prompts.length} queued follow-ups as one task. Use subagents to research independent parts where useful; keep dependent changes coordinated.`;
|
|
27377
|
-
return {
|
|
27378
|
-
full,
|
|
27379
|
-
display,
|
|
27380
|
-
key: sourceKeys[0],
|
|
27381
|
-
batchPrompts: prompts,
|
|
27382
|
-
sourceKeys
|
|
27383
|
-
};
|
|
27384
|
-
}
|
|
27385
|
-
function isCommand(prompt) {
|
|
27386
|
-
const trimmed = prompt.trimStart();
|
|
27387
|
-
return trimmed.startsWith("/") || trimmed.startsWith("!");
|
|
27388
|
-
}
|
|
27389
|
-
var init_queue_batch = __esm({
|
|
27390
|
-
"src/agent/queue-batch.ts"() {
|
|
27391
|
-
"use strict";
|
|
27392
|
-
}
|
|
27393
|
-
});
|
|
27394
|
-
|
|
27395
27642
|
// src/ui/task-list.tsx
|
|
27396
27643
|
import { useEffect as useEffect6, useRef as useRef4, useState as useState14 } from "react";
|
|
27397
27644
|
import { Box as Box18, Text as Text19 } from "ink";
|
|
@@ -36595,6 +36842,8 @@ function App({
|
|
|
36595
36842
|
const [history, setHistory] = useState28([]);
|
|
36596
36843
|
const [historyIndex, setHistoryIndex] = useState28(-1);
|
|
36597
36844
|
const [draftInput, setDraftInput] = useState28("");
|
|
36845
|
+
const latestPromptRef = useRef9(null);
|
|
36846
|
+
const pendingPromptEditRef = useRef9(null);
|
|
36598
36847
|
const modesEnabled = cfg?.modesEnabled === true;
|
|
36599
36848
|
const [mode, setMode] = useState28(initialCfg?.modesEnabled ? "edit" : "auto");
|
|
36600
36849
|
useEffect11(() => {
|
|
@@ -37624,10 +37873,21 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
37624
37873
|
if (!cfg) return;
|
|
37625
37874
|
let trimmed = text.trim();
|
|
37626
37875
|
if (!trimmed) return;
|
|
37876
|
+
let display = displayText?.trim() || trimmed;
|
|
37877
|
+
const promptKey = opts2?.eventKey ?? opts2?.queuedKeys?.[0];
|
|
37878
|
+
if (promptKey && !trimmed.startsWith("/") && parseBangCommand(trimmed) === null) {
|
|
37879
|
+
const previousTarget = latestPromptRef.current;
|
|
37880
|
+
if (opts2?.eventKey || previousTarget?.key === promptKey) {
|
|
37881
|
+
latestPromptRef.current = {
|
|
37882
|
+
key: promptKey,
|
|
37883
|
+
historyText: opts2?.historyText ?? previousTarget?.historyText ?? display,
|
|
37884
|
+
messagesBefore: [...messagesRef.current]
|
|
37885
|
+
};
|
|
37886
|
+
}
|
|
37887
|
+
}
|
|
37627
37888
|
beginTurn();
|
|
37628
37889
|
let overrideModel;
|
|
37629
37890
|
let overrideEffort;
|
|
37630
|
-
let display = displayText?.trim() || trimmed;
|
|
37631
37891
|
const bangCommand = parseBangCommand(trimmed);
|
|
37632
37892
|
if (bangCommand !== null) {
|
|
37633
37893
|
await runUserShellCommand(bangCommand, display, opts2?.queuedKeys?.[0]);
|
|
@@ -37714,7 +37974,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
37714
37974
|
)
|
|
37715
37975
|
);
|
|
37716
37976
|
} else {
|
|
37717
|
-
setEvents((e) => [...e, { kind: "user", key: mkKey(), text: display, images: images.length > 0 ? images : void 0 }]);
|
|
37977
|
+
setEvents((e) => [...e, { kind: "user", key: opts2?.eventKey ?? mkKey(), text: display, images: images.length > 0 ? images : void 0 }]);
|
|
37718
37978
|
}
|
|
37719
37979
|
const nudge = maybeLspNudge(display, cfg?.lspEnabled ?? false, cfg?.lspServers ?? {});
|
|
37720
37980
|
if (nudge) {
|
|
@@ -38420,13 +38680,26 @@ ${conflicts.join("\n")}` }
|
|
|
38420
38680
|
[cfg, handleSlash, updateAssistant, updateTool, saveSessionSafe, updateResponseMeta, setShowPlanCompletePicker, runUserShellCommand]
|
|
38421
38681
|
);
|
|
38422
38682
|
useEffect11(() => {
|
|
38423
|
-
if (!busy && queuePlanDraft === null &&
|
|
38424
|
-
const
|
|
38425
|
-
|
|
38426
|
-
|
|
38427
|
-
|
|
38428
|
-
|
|
38429
|
-
|
|
38683
|
+
if (!busy && queuePlanDraft === null && supervisorRef.current.phase === "idle") {
|
|
38684
|
+
const pendingEdit = pendingPromptEditRef.current;
|
|
38685
|
+
if (pendingEdit) {
|
|
38686
|
+
pendingPromptEditRef.current = null;
|
|
38687
|
+
messagesRef.current = [...pendingEdit.messagesBefore];
|
|
38688
|
+
setEvents((events2) => replaceUserPromptEvent(events2, pendingEdit.key, pendingEdit.display, true));
|
|
38689
|
+
processMessage(pendingEdit.full, pendingEdit.display, {
|
|
38690
|
+
queuedKeys: [pendingEdit.key],
|
|
38691
|
+
historyText: pendingEdit.display
|
|
38692
|
+
});
|
|
38693
|
+
return;
|
|
38694
|
+
}
|
|
38695
|
+
if (queue2.length > 0) {
|
|
38696
|
+
const next = queue2[0];
|
|
38697
|
+
setQueue((q) => q.slice(1));
|
|
38698
|
+
processMessage(next.full, next.display, {
|
|
38699
|
+
queuedKeys: next.sourceKeys ?? [next.key],
|
|
38700
|
+
...next.batchPrompts ? { userPromptForHooks: next.batchPrompts.join("\n\n") } : {}
|
|
38701
|
+
});
|
|
38702
|
+
}
|
|
38430
38703
|
}
|
|
38431
38704
|
}, [busy, queue2, queuePlanDraft, processMessage]);
|
|
38432
38705
|
const handleQueuePlanPick = useCallback9(
|
|
@@ -38465,10 +38738,55 @@ ${conflicts.join("\n")}` }
|
|
|
38465
38738
|
return;
|
|
38466
38739
|
}
|
|
38467
38740
|
const historyEntry = trimmedDisplay;
|
|
38741
|
+
const editTarget = latestPromptRef.current;
|
|
38742
|
+
const selectedHistoryEntry = history[historyIndex];
|
|
38743
|
+
const isEditedLatestPrompt = historyIndex === history.length - 1 && editTarget !== null && events.some((event) => event.kind === "user" && event.key === editTarget.key) && selectedHistoryEntry === editTarget.historyText;
|
|
38744
|
+
if (isEditedLatestPrompt && editTarget) {
|
|
38745
|
+
if (trimmedDisplay === selectedHistoryEntry) {
|
|
38746
|
+
setInput("");
|
|
38747
|
+
setHistoryIndex(-1);
|
|
38748
|
+
return;
|
|
38749
|
+
}
|
|
38750
|
+
const updatedQueue = replaceQueuedPrompt(queue2, editTarget.key, trimmedFull, trimmedDisplay);
|
|
38751
|
+
if (updatedQueue || editTarget.messagesBefore) {
|
|
38752
|
+
setHistory(
|
|
38753
|
+
(items) => items.length > 0 && items[items.length - 1] === editTarget.historyText ? [...items.slice(0, -1), trimmedDisplay] : items
|
|
38754
|
+
);
|
|
38755
|
+
setInput("");
|
|
38756
|
+
setHistoryIndex(-1);
|
|
38757
|
+
latestPromptRef.current = { ...editTarget, historyText: trimmedDisplay };
|
|
38758
|
+
setEvents((events2) => replaceUserPromptEvent(events2, editTarget.key, trimmedDisplay));
|
|
38759
|
+
if (updatedQueue) {
|
|
38760
|
+
setQueue(updatedQueue);
|
|
38761
|
+
return;
|
|
38762
|
+
}
|
|
38763
|
+
const edit = {
|
|
38764
|
+
key: editTarget.key,
|
|
38765
|
+
full: trimmedFull,
|
|
38766
|
+
display: trimmedDisplay,
|
|
38767
|
+
messagesBefore: editTarget.messagesBefore
|
|
38768
|
+
};
|
|
38769
|
+
if (busyRef.current || supervisorRef.current.isRunning) {
|
|
38770
|
+
pendingPromptEditRef.current = edit;
|
|
38771
|
+
interruptTurn(interruptDepsRef.current);
|
|
38772
|
+
} else {
|
|
38773
|
+
messagesRef.current = [...edit.messagesBefore];
|
|
38774
|
+
setEvents((events2) => replaceUserPromptEvent(events2, edit.key, edit.display, true));
|
|
38775
|
+
processMessage(edit.full, edit.display, {
|
|
38776
|
+
queuedKeys: [edit.key],
|
|
38777
|
+
historyText: edit.display
|
|
38778
|
+
});
|
|
38779
|
+
}
|
|
38780
|
+
return;
|
|
38781
|
+
}
|
|
38782
|
+
}
|
|
38468
38783
|
if (busyRef.current || supervisorRef.current.isRunning) {
|
|
38469
|
-
const
|
|
38470
|
-
setEvents((e) => [...e, { kind: "user", key, text: trimmedDisplay, queued: true }]);
|
|
38471
|
-
setQueue((q) => [...q, { full: trimmedFull, display: trimmedDisplay, key }]);
|
|
38784
|
+
const key2 = mkKey();
|
|
38785
|
+
setEvents((e) => [...e, { kind: "user", key: key2, text: trimmedDisplay, queued: true }]);
|
|
38786
|
+
setQueue((q) => [...q, { full: trimmedFull, display: trimmedDisplay, key: key2 }]);
|
|
38787
|
+
if (!trimmedFull.startsWith("/") && parseBangCommand(trimmedFull) === null) {
|
|
38788
|
+
latestPromptRef.current = { key: key2, historyText: historyEntry, messagesBefore: null };
|
|
38789
|
+
}
|
|
38472
38790
|
setHistory((h) => h.length > 0 && h[h.length - 1] === historyEntry ? h : [...h, historyEntry]);
|
|
38473
38791
|
setInput("");
|
|
38474
38792
|
setHistoryIndex(-1);
|
|
@@ -38477,9 +38795,13 @@ ${conflicts.join("\n")}` }
|
|
|
38477
38795
|
setHistory((h) => h.length > 0 && h[h.length - 1] === historyEntry ? h : [...h, historyEntry]);
|
|
38478
38796
|
setInput("");
|
|
38479
38797
|
setHistoryIndex(-1);
|
|
38480
|
-
|
|
38798
|
+
const key = mkKey();
|
|
38799
|
+
processMessage(trimmedFull, trimmedDisplay !== trimmedFull ? trimmedDisplay : void 0, {
|
|
38800
|
+
eventKey: key,
|
|
38801
|
+
historyText: historyEntry
|
|
38802
|
+
});
|
|
38481
38803
|
},
|
|
38482
|
-
[processMessage, queue2]
|
|
38804
|
+
[processMessage, queue2, history, historyIndex, events]
|
|
38483
38805
|
);
|
|
38484
38806
|
submitRef.current = submit;
|
|
38485
38807
|
useEffect11(() => {
|
|
@@ -38719,6 +39041,7 @@ ${conflicts.join("\n")}` }
|
|
|
38719
39041
|
bridge.sync({
|
|
38720
39042
|
events,
|
|
38721
39043
|
busy,
|
|
39044
|
+
workers: activeWorkers,
|
|
38722
39045
|
mode,
|
|
38723
39046
|
model: cfg?.model ?? "",
|
|
38724
39047
|
usage,
|
|
@@ -38954,6 +39277,7 @@ ${conflicts.join("\n")}` }
|
|
|
38954
39277
|
}
|
|
38955
39278
|
),
|
|
38956
39279
|
parseBangCommand(input) !== null && /* @__PURE__ */ jsx43(Box41, { marginTop: 1, children: /* @__PURE__ */ jsx43(Text42, { dimColor: true, children: "! shell command \u2014 runs in your terminal (logins and prompts work); output is shared with the agent" }) }),
|
|
39280
|
+
historyIndex === history.length - 1 && latestPromptRef.current?.historyText === history[history.length - 1] && /* @__PURE__ */ jsx43(Box41, { marginTop: 1, children: /* @__PURE__ */ jsx43(Text42, { dimColor: true, children: "editing last message \u2014 Enter replaces it" }) }),
|
|
38957
39281
|
/* @__PURE__ */ jsxs40(Box41, { marginTop: parseBangCommand(input) !== null ? 0 : 1, children: [
|
|
38958
39282
|
/* @__PURE__ */ jsx43(Text42, { color: theme.prompt ?? theme.accent, children: "\u203A " }),
|
|
38959
39283
|
/* @__PURE__ */ jsx43(
|