autopilot-ai 1.11.0 → 1.12.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/index.js +427 -62
- 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, {
|
|
@@ -23952,6 +24085,9 @@ __export(camouflage_view_exports, {
|
|
|
23952
24085
|
listMentionDir: () => listMentionDir,
|
|
23953
24086
|
modelOptions: () => modelOptions,
|
|
23954
24087
|
runCamouflageView: () => runCamouflageView,
|
|
24088
|
+
shouldSendSubagentPolicyNotice: () => shouldSendSubagentPolicyNotice,
|
|
24089
|
+
splitBangResult: () => splitBangResult,
|
|
24090
|
+
syncSlashCommands: () => syncSlashCommands,
|
|
23955
24091
|
toolRow: () => toolRow,
|
|
23956
24092
|
toolSummary: () => toolSummary,
|
|
23957
24093
|
validateCommandName: () => validateCommandName
|
|
@@ -24005,6 +24141,7 @@ async function runCamouflageView(opts2) {
|
|
|
24005
24141
|
resume: () => sdk.resumeTerminal?.(cam)
|
|
24006
24142
|
});
|
|
24007
24143
|
cam.on("userInput", (text) => {
|
|
24144
|
+
if (view.routeActivityCommand(text)) return;
|
|
24008
24145
|
view.echoed.push(text.trim());
|
|
24009
24146
|
view.actions?.submit(text);
|
|
24010
24147
|
});
|
|
@@ -24020,10 +24157,15 @@ async function runCamouflageView(opts2) {
|
|
|
24020
24157
|
});
|
|
24021
24158
|
});
|
|
24022
24159
|
cam.on("modeChangeRequested", () => view.actions?.cycleMode());
|
|
24160
|
+
cam.on("activityStopRequested", ({ id }) => view.stopActivity(id));
|
|
24161
|
+
cam.on("activityViewChanged", (change) => {
|
|
24162
|
+
view.activityViewChanged(change);
|
|
24163
|
+
});
|
|
24023
24164
|
let closing = false;
|
|
24024
24165
|
const finish = async () => {
|
|
24025
24166
|
if (closing) return;
|
|
24026
24167
|
closing = true;
|
|
24168
|
+
view.dispose();
|
|
24027
24169
|
registerTerminalHandoff(null);
|
|
24028
24170
|
await cam.close().catch(() => void 0);
|
|
24029
24171
|
restoreConsole();
|
|
@@ -24052,6 +24194,38 @@ function runShell(command) {
|
|
|
24052
24194
|
child.on("error", (err) => resolve15({ ok: false, output: err.message }));
|
|
24053
24195
|
});
|
|
24054
24196
|
}
|
|
24197
|
+
function syncSlashCommands(custom, lastKey, send) {
|
|
24198
|
+
const builtin = [
|
|
24199
|
+
...BUILTIN_COMMANDS.map((c) => ({ name: c.name, description: c.description, args_hint: c.argHint })),
|
|
24200
|
+
{ name: "jobs", description: "Browse background jobs and agents" },
|
|
24201
|
+
{ name: "agents", description: "Browse background agents and jobs" }
|
|
24202
|
+
];
|
|
24203
|
+
const names = new Set(builtin.map((c) => c.name));
|
|
24204
|
+
const commands = [
|
|
24205
|
+
...builtin,
|
|
24206
|
+
...custom.filter((c) => !names.has(c.name)).map((c) => ({ name: c.name, description: c.description ? `${c.description} (custom)` : "Custom command" }))
|
|
24207
|
+
];
|
|
24208
|
+
const key = JSON.stringify(commands);
|
|
24209
|
+
if (key !== lastKey) send(commands);
|
|
24210
|
+
return key;
|
|
24211
|
+
}
|
|
24212
|
+
function shouldSendSubagentPolicyNotice(message2, sent) {
|
|
24213
|
+
if (!message2.startsWith("Subagent policy:") || message2.startsWith("Subagent policy: respecting your ")) return true;
|
|
24214
|
+
if (sent.has(message2)) return false;
|
|
24215
|
+
sent.add(message2);
|
|
24216
|
+
return true;
|
|
24217
|
+
}
|
|
24218
|
+
function splitBangResult(title, content) {
|
|
24219
|
+
if (!title?.startsWith("! ")) return { output: content };
|
|
24220
|
+
const match = /^(exit=\d+|signal=[^\r\n]+|exit=\?)(?:\r?\n)/.exec(content);
|
|
24221
|
+
if (!match) return { output: content };
|
|
24222
|
+
const exit = /^exit=(\d+)$/.exec(match[1]);
|
|
24223
|
+
return {
|
|
24224
|
+
output: content.slice(match[0].length),
|
|
24225
|
+
status: match[1],
|
|
24226
|
+
...exit ? { exitCode: Number(exit[1]) } : {}
|
|
24227
|
+
};
|
|
24228
|
+
}
|
|
24055
24229
|
function validateCommandName(name, existing) {
|
|
24056
24230
|
if (!name) return "a name is required";
|
|
24057
24231
|
if (!/^[a-zA-Z][a-zA-Z0-9_\-/]*$/.test(name)) return "use letters, numbers, _ - / and start with a letter";
|
|
@@ -24243,6 +24417,8 @@ var init_camouflage_view = __esm({
|
|
|
24243
24417
|
init_hooks_wizard();
|
|
24244
24418
|
init_mode();
|
|
24245
24419
|
init_logger();
|
|
24420
|
+
init_manager();
|
|
24421
|
+
init_activity();
|
|
24246
24422
|
CamouflageUnavailable = class extends Error {
|
|
24247
24423
|
};
|
|
24248
24424
|
View = class {
|
|
@@ -24267,7 +24443,19 @@ var init_camouflage_view = __esm({
|
|
|
24267
24443
|
todos = "";
|
|
24268
24444
|
permission = null;
|
|
24269
24445
|
openPrompt = null;
|
|
24270
|
-
commandsKey
|
|
24446
|
+
commandsKey;
|
|
24447
|
+
sentSubagentPolicyNotices = /* @__PURE__ */ new Set();
|
|
24448
|
+
jobs = new JobManager();
|
|
24449
|
+
sentActivities = /* @__PURE__ */ new Map();
|
|
24450
|
+
workerActivities = /* @__PURE__ */ new Map();
|
|
24451
|
+
jobLogOffsets = /* @__PURE__ */ new Map();
|
|
24452
|
+
workerLogOffsets = /* @__PURE__ */ new Map();
|
|
24453
|
+
latestJobs = [];
|
|
24454
|
+
latestWorkers = [];
|
|
24455
|
+
activitySnapshotSent = false;
|
|
24456
|
+
activityDetailId = null;
|
|
24457
|
+
activityPoll = null;
|
|
24458
|
+
disposed = false;
|
|
24271
24459
|
connect(actions) {
|
|
24272
24460
|
this.actions = actions;
|
|
24273
24461
|
}
|
|
@@ -24282,16 +24470,136 @@ var init_camouflage_view = __esm({
|
|
|
24282
24470
|
}
|
|
24283
24471
|
this.syncPermission(s);
|
|
24284
24472
|
this.syncPrompts(s);
|
|
24473
|
+
this.syncActivity(s.workers);
|
|
24285
24474
|
}
|
|
24286
24475
|
/** Built-in plus the user's custom commands, re-sent when they change. */
|
|
24476
|
+
routeActivityCommand(text) {
|
|
24477
|
+
if (!/^\/(?:jobs|agents)$/i.test(text.trim())) return false;
|
|
24478
|
+
this.cam.send("ActivityBrowserOpen", {});
|
|
24479
|
+
return true;
|
|
24480
|
+
}
|
|
24481
|
+
activityViewChanged(change) {
|
|
24482
|
+
this.activityDetailId = change.view === "detail" ? change.id ?? null : null;
|
|
24483
|
+
this.jobLogOffsets.clear();
|
|
24484
|
+
this.workerLogOffsets.clear();
|
|
24485
|
+
if (this.activityDetailId) {
|
|
24486
|
+
if (this.activityDetailId.startsWith("agent:")) this.streamWorkerLogs(this.activityDetailId, true);
|
|
24487
|
+
else this.streamJobLogs(this.activityDetailId, true);
|
|
24488
|
+
}
|
|
24489
|
+
this.ensureActivityPolling();
|
|
24490
|
+
}
|
|
24491
|
+
stopActivity(id) {
|
|
24492
|
+
try {
|
|
24493
|
+
const job = this.jobs.get(id);
|
|
24494
|
+
if (!job || !isActiveJob(job)) return;
|
|
24495
|
+
this.jobs.cancel(id);
|
|
24496
|
+
this.refreshActivities();
|
|
24497
|
+
} catch (error) {
|
|
24498
|
+
logger.warn("camouflage.activity_stop_failed", { id, error: String(error) });
|
|
24499
|
+
this.cam.send("RuntimeError", { message: `Unable to stop background job: ${error.message}`, severity: "error" });
|
|
24500
|
+
}
|
|
24501
|
+
}
|
|
24502
|
+
dispose() {
|
|
24503
|
+
this.disposed = true;
|
|
24504
|
+
if (this.activityPoll) clearInterval(this.activityPoll);
|
|
24505
|
+
this.activityPoll = null;
|
|
24506
|
+
this.jobs.close();
|
|
24507
|
+
}
|
|
24508
|
+
syncActivity(workers) {
|
|
24509
|
+
this.latestWorkers = workers;
|
|
24510
|
+
try {
|
|
24511
|
+
const records = this.jobs.list({ limit: 100 });
|
|
24512
|
+
this.latestJobs = [
|
|
24513
|
+
...records.filter(isActiveJob),
|
|
24514
|
+
...records.filter((job) => !isActiveJob(job)).slice(0, 5)
|
|
24515
|
+
];
|
|
24516
|
+
} catch (error) {
|
|
24517
|
+
logger.warn("camouflage.activity_jobs_read_failed", { error: String(error) });
|
|
24518
|
+
}
|
|
24519
|
+
const now2 = Date.now();
|
|
24520
|
+
const currentWorkers = /* @__PURE__ */ new Set();
|
|
24521
|
+
for (const worker of workers) {
|
|
24522
|
+
const id = `agent:${worker.id}`;
|
|
24523
|
+
currentWorkers.add(id);
|
|
24524
|
+
this.workerActivities.set(id, {
|
|
24525
|
+
item: activityFromWorker(worker),
|
|
24526
|
+
logs: [...worker.logs],
|
|
24527
|
+
active: true,
|
|
24528
|
+
expiresAt: Number.POSITIVE_INFINITY
|
|
24529
|
+
});
|
|
24530
|
+
}
|
|
24531
|
+
for (const [id, activity] of this.workerActivities) {
|
|
24532
|
+
if (currentWorkers.has(id) || !activity.active) continue;
|
|
24533
|
+
activity.active = false;
|
|
24534
|
+
activity.expiresAt = now2 + 10 * 6e4;
|
|
24535
|
+
if (activity.item.status === "running" || activity.item.status === "waiting") {
|
|
24536
|
+
const cancelled = activity.logs.some((line) => /cancelled by user|cancelling worker/i.test(line));
|
|
24537
|
+
const failed = activity.logs.some((line) => line.includes("[coordinator] Fetch failed:"));
|
|
24538
|
+
activity.item = {
|
|
24539
|
+
...activity.item,
|
|
24540
|
+
status: cancelled ? "stopped" : failed ? "failed" : "done",
|
|
24541
|
+
summary: cancelled ? "Stopped" : failed ? activity.item.summary ?? "Worker failed" : "Worker finished",
|
|
24542
|
+
updated_at_ms: now2
|
|
24543
|
+
};
|
|
24544
|
+
}
|
|
24545
|
+
}
|
|
24546
|
+
for (const [id, activity] of this.workerActivities) {
|
|
24547
|
+
if (!activity.active && activity.expiresAt <= now2) this.workerActivities.delete(id);
|
|
24548
|
+
}
|
|
24549
|
+
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));
|
|
24550
|
+
for (const [id] of finishedWorkers.slice(20)) this.workerActivities.delete(id);
|
|
24551
|
+
const next = /* @__PURE__ */ new Map();
|
|
24552
|
+
for (const job of this.latestJobs) next.set(job.id, activityFromJob(job));
|
|
24553
|
+
for (const [id, activity] of this.workerActivities) next.set(id, activity.item);
|
|
24554
|
+
for (const event of diffActivityItems(this.sentActivities, [...next.values()], this.activitySnapshotSent)) {
|
|
24555
|
+
this.cam.send(event.type, event.payload);
|
|
24556
|
+
}
|
|
24557
|
+
this.activitySnapshotSent = true;
|
|
24558
|
+
this.sentActivities.clear();
|
|
24559
|
+
for (const [id, item] of next) this.sentActivities.set(id, JSON.stringify(item));
|
|
24560
|
+
if (this.activityDetailId?.startsWith("agent:")) this.streamWorkerLogs(this.activityDetailId, false);
|
|
24561
|
+
this.ensureActivityPolling();
|
|
24562
|
+
}
|
|
24563
|
+
ensureActivityPolling() {
|
|
24564
|
+
const needsPolling = this.latestJobs.some(isActiveJob);
|
|
24565
|
+
if (needsPolling && !this.activityPoll && !this.disposed) {
|
|
24566
|
+
this.activityPoll = setInterval(() => this.refreshActivities(), 1e3);
|
|
24567
|
+
this.activityPoll.unref?.();
|
|
24568
|
+
} else if (!needsPolling && this.activityPoll) {
|
|
24569
|
+
clearInterval(this.activityPoll);
|
|
24570
|
+
this.activityPoll = null;
|
|
24571
|
+
}
|
|
24572
|
+
}
|
|
24573
|
+
refreshActivities() {
|
|
24574
|
+
if (this.disposed) return;
|
|
24575
|
+
this.syncActivity(this.latestWorkers);
|
|
24576
|
+
const id = this.activityDetailId;
|
|
24577
|
+
if (id && !id.startsWith("agent:")) this.streamJobLogs(id, false);
|
|
24578
|
+
}
|
|
24579
|
+
streamWorkerLogs(id, reset2) {
|
|
24580
|
+
const activity = this.workerActivities.get(id);
|
|
24581
|
+
if (!activity) return;
|
|
24582
|
+
const offset = reset2 ? 0 : this.workerLogOffsets.get(id) ?? 0;
|
|
24583
|
+
const chunk = activity.logs.slice(offset).join("\n");
|
|
24584
|
+
if (chunk) this.cam.send("ActivityLog", { id, chunk: `${chunk}
|
|
24585
|
+
`, stream: "stdout" });
|
|
24586
|
+
this.workerLogOffsets.set(id, activity.logs.length);
|
|
24587
|
+
}
|
|
24588
|
+
streamJobLogs(id, initial) {
|
|
24589
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
24590
|
+
const key = `${id}:${stream}`;
|
|
24591
|
+
try {
|
|
24592
|
+
const result2 = initial ? this.jobs.logs(id, stream, { tail: 1e3 }) : this.jobs.logs(id, stream, { offset: this.jobLogOffsets.get(key) ?? 0 });
|
|
24593
|
+
this.jobLogOffsets.set(key, result2.offset);
|
|
24594
|
+
if (result2.content) this.cam.send("ActivityLog", { id, chunk: result2.content, stream });
|
|
24595
|
+
} catch {
|
|
24596
|
+
}
|
|
24597
|
+
}
|
|
24598
|
+
}
|
|
24287
24599
|
syncCommands(custom) {
|
|
24288
|
-
|
|
24289
|
-
|
|
24290
|
-
|
|
24291
|
-
const builtin = BUILTIN_COMMANDS.map((c) => ({ name: c.name, description: c.description, args_hint: c.argHint }));
|
|
24292
|
-
const names = new Set(builtin.map((c) => c.name));
|
|
24293
|
-
const mine = custom.filter((c) => !names.has(c.name)).map((c) => ({ name: c.name, description: c.description ? `${c.description} (custom)` : "Custom command" }));
|
|
24294
|
-
this.cam.send("SlashCommandsRegistered", { commands: [...builtin, ...mine] });
|
|
24600
|
+
this.commandsKey = syncSlashCommands(custom, this.commandsKey, (commands) => {
|
|
24601
|
+
this.cam.send("SlashCommandsRegistered", { commands });
|
|
24602
|
+
});
|
|
24295
24603
|
}
|
|
24296
24604
|
syncEvents(events) {
|
|
24297
24605
|
if (events.length < this.settled || this.firstKey !== void 0 && events[0]?.key !== this.firstKey) {
|
|
@@ -24342,6 +24650,9 @@ var init_camouflage_view = __esm({
|
|
|
24342
24650
|
return this.syncTool(e, prev);
|
|
24343
24651
|
case "info":
|
|
24344
24652
|
case "memory":
|
|
24653
|
+
if (e.kind === "info" && !shouldSendSubagentPolicyNotice(e.text, this.sentSubagentPolicyNotices)) {
|
|
24654
|
+
return this.mark(e.key, 0, true);
|
|
24655
|
+
}
|
|
24345
24656
|
return this.notice(e.key, e.text, "info");
|
|
24346
24657
|
case "error":
|
|
24347
24658
|
return this.notice(e.key, e.text, "error");
|
|
@@ -24364,12 +24675,14 @@ var init_camouflage_view = __esm({
|
|
|
24364
24675
|
if (e.status === "queued" || e.status === "running") return this.mark(e.key, 0, false);
|
|
24365
24676
|
const ok = e.status === "done";
|
|
24366
24677
|
const content = e.result ?? "";
|
|
24678
|
+
const bang = splitBangResult(e.render?.title, content);
|
|
24679
|
+
const output = e.status === "rejected" ? "" : bang.output;
|
|
24367
24680
|
this.cam.send("ToolExecutionFinished", {
|
|
24368
24681
|
tool_id: e.key,
|
|
24369
|
-
exit_code: ok ? 0 : 1,
|
|
24682
|
+
exit_code: bang.exitCode ?? (ok ? 0 : 1),
|
|
24370
24683
|
status: e.status === "rejected" ? "rejected" : e.status === "cancelled" ? "cancelled" : ok ? "done" : "error",
|
|
24371
|
-
summary: toolSummary(e.name, ok, e.status === "rejected",
|
|
24372
|
-
output
|
|
24684
|
+
summary: bang.status ?? toolSummary(e.name, ok, e.status === "rejected", output),
|
|
24685
|
+
output,
|
|
24373
24686
|
...ok && e.render?.diff ? { diff: e.render.diff } : {}
|
|
24374
24687
|
});
|
|
24375
24688
|
return this.mark(e.key, 0, true);
|
|
@@ -26601,10 +26914,18 @@ var init_chat = __esm({
|
|
|
26601
26914
|
return /* @__PURE__ */ jsx13(ToolView, { evt, verbose, isRepeated, intentTier });
|
|
26602
26915
|
}
|
|
26603
26916
|
if (evt.kind === "info") {
|
|
26604
|
-
|
|
26605
|
-
|
|
26606
|
-
|
|
26607
|
-
|
|
26917
|
+
const isSubagentHint = /^(Subagent policy|Subagent suggestion):/.test(evt.text);
|
|
26918
|
+
return /* @__PURE__ */ jsxs10(
|
|
26919
|
+
Text12,
|
|
26920
|
+
{
|
|
26921
|
+
color: isSubagentHint ? theme.muted?.color ?? theme.info.color : theme.info.color,
|
|
26922
|
+
dimColor: isSubagentHint || void 0,
|
|
26923
|
+
children: [
|
|
26924
|
+
"\xB7 ",
|
|
26925
|
+
humanizeInfo(evt.text, intentTier)
|
|
26926
|
+
]
|
|
26927
|
+
}
|
|
26928
|
+
);
|
|
26608
26929
|
}
|
|
26609
26930
|
if (evt.kind === "jev") {
|
|
26610
26931
|
const resultColor = evt.tone === "yes" ? theme.palette.success : evt.tone === "no" ? theme.warn : theme.accent;
|
|
@@ -27359,39 +27680,6 @@ var init_queue_plan_picker = __esm({
|
|
|
27359
27680
|
}
|
|
27360
27681
|
});
|
|
27361
27682
|
|
|
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
27683
|
// src/ui/task-list.tsx
|
|
27396
27684
|
import { useEffect as useEffect6, useRef as useRef4, useState as useState14 } from "react";
|
|
27397
27685
|
import { Box as Box18, Text as Text19 } from "ink";
|
|
@@ -36595,6 +36883,8 @@ function App({
|
|
|
36595
36883
|
const [history, setHistory] = useState28([]);
|
|
36596
36884
|
const [historyIndex, setHistoryIndex] = useState28(-1);
|
|
36597
36885
|
const [draftInput, setDraftInput] = useState28("");
|
|
36886
|
+
const latestPromptRef = useRef9(null);
|
|
36887
|
+
const pendingPromptEditRef = useRef9(null);
|
|
36598
36888
|
const modesEnabled = cfg?.modesEnabled === true;
|
|
36599
36889
|
const [mode, setMode] = useState28(initialCfg?.modesEnabled ? "edit" : "auto");
|
|
36600
36890
|
useEffect11(() => {
|
|
@@ -37624,10 +37914,21 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
37624
37914
|
if (!cfg) return;
|
|
37625
37915
|
let trimmed = text.trim();
|
|
37626
37916
|
if (!trimmed) return;
|
|
37917
|
+
let display = displayText?.trim() || trimmed;
|
|
37918
|
+
const promptKey = opts2?.eventKey ?? opts2?.queuedKeys?.[0];
|
|
37919
|
+
if (promptKey && !trimmed.startsWith("/") && parseBangCommand(trimmed) === null) {
|
|
37920
|
+
const previousTarget = latestPromptRef.current;
|
|
37921
|
+
if (opts2?.eventKey || previousTarget?.key === promptKey) {
|
|
37922
|
+
latestPromptRef.current = {
|
|
37923
|
+
key: promptKey,
|
|
37924
|
+
historyText: opts2?.historyText ?? previousTarget?.historyText ?? display,
|
|
37925
|
+
messagesBefore: [...messagesRef.current]
|
|
37926
|
+
};
|
|
37927
|
+
}
|
|
37928
|
+
}
|
|
37627
37929
|
beginTurn();
|
|
37628
37930
|
let overrideModel;
|
|
37629
37931
|
let overrideEffort;
|
|
37630
|
-
let display = displayText?.trim() || trimmed;
|
|
37631
37932
|
const bangCommand = parseBangCommand(trimmed);
|
|
37632
37933
|
if (bangCommand !== null) {
|
|
37633
37934
|
await runUserShellCommand(bangCommand, display, opts2?.queuedKeys?.[0]);
|
|
@@ -37714,7 +38015,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
37714
38015
|
)
|
|
37715
38016
|
);
|
|
37716
38017
|
} else {
|
|
37717
|
-
setEvents((e) => [...e, { kind: "user", key: mkKey(), text: display, images: images.length > 0 ? images : void 0 }]);
|
|
38018
|
+
setEvents((e) => [...e, { kind: "user", key: opts2?.eventKey ?? mkKey(), text: display, images: images.length > 0 ? images : void 0 }]);
|
|
37718
38019
|
}
|
|
37719
38020
|
const nudge = maybeLspNudge(display, cfg?.lspEnabled ?? false, cfg?.lspServers ?? {});
|
|
37720
38021
|
if (nudge) {
|
|
@@ -38420,13 +38721,26 @@ ${conflicts.join("\n")}` }
|
|
|
38420
38721
|
[cfg, handleSlash, updateAssistant, updateTool, saveSessionSafe, updateResponseMeta, setShowPlanCompletePicker, runUserShellCommand]
|
|
38421
38722
|
);
|
|
38422
38723
|
useEffect11(() => {
|
|
38423
|
-
if (!busy && queuePlanDraft === null &&
|
|
38424
|
-
const
|
|
38425
|
-
|
|
38426
|
-
|
|
38427
|
-
|
|
38428
|
-
|
|
38429
|
-
|
|
38724
|
+
if (!busy && queuePlanDraft === null && supervisorRef.current.phase === "idle") {
|
|
38725
|
+
const pendingEdit = pendingPromptEditRef.current;
|
|
38726
|
+
if (pendingEdit) {
|
|
38727
|
+
pendingPromptEditRef.current = null;
|
|
38728
|
+
messagesRef.current = [...pendingEdit.messagesBefore];
|
|
38729
|
+
setEvents((events2) => replaceUserPromptEvent(events2, pendingEdit.key, pendingEdit.display, true));
|
|
38730
|
+
processMessage(pendingEdit.full, pendingEdit.display, {
|
|
38731
|
+
queuedKeys: [pendingEdit.key],
|
|
38732
|
+
historyText: pendingEdit.display
|
|
38733
|
+
});
|
|
38734
|
+
return;
|
|
38735
|
+
}
|
|
38736
|
+
if (queue2.length > 0) {
|
|
38737
|
+
const next = queue2[0];
|
|
38738
|
+
setQueue((q) => q.slice(1));
|
|
38739
|
+
processMessage(next.full, next.display, {
|
|
38740
|
+
queuedKeys: next.sourceKeys ?? [next.key],
|
|
38741
|
+
...next.batchPrompts ? { userPromptForHooks: next.batchPrompts.join("\n\n") } : {}
|
|
38742
|
+
});
|
|
38743
|
+
}
|
|
38430
38744
|
}
|
|
38431
38745
|
}, [busy, queue2, queuePlanDraft, processMessage]);
|
|
38432
38746
|
const handleQueuePlanPick = useCallback9(
|
|
@@ -38465,10 +38779,55 @@ ${conflicts.join("\n")}` }
|
|
|
38465
38779
|
return;
|
|
38466
38780
|
}
|
|
38467
38781
|
const historyEntry = trimmedDisplay;
|
|
38782
|
+
const editTarget = latestPromptRef.current;
|
|
38783
|
+
const selectedHistoryEntry = history[historyIndex];
|
|
38784
|
+
const isEditedLatestPrompt = historyIndex === history.length - 1 && editTarget !== null && events.some((event) => event.kind === "user" && event.key === editTarget.key) && selectedHistoryEntry === editTarget.historyText;
|
|
38785
|
+
if (isEditedLatestPrompt && editTarget) {
|
|
38786
|
+
if (trimmedDisplay === selectedHistoryEntry) {
|
|
38787
|
+
setInput("");
|
|
38788
|
+
setHistoryIndex(-1);
|
|
38789
|
+
return;
|
|
38790
|
+
}
|
|
38791
|
+
const updatedQueue = replaceQueuedPrompt(queue2, editTarget.key, trimmedFull, trimmedDisplay);
|
|
38792
|
+
if (updatedQueue || editTarget.messagesBefore) {
|
|
38793
|
+
setHistory(
|
|
38794
|
+
(items) => items.length > 0 && items[items.length - 1] === editTarget.historyText ? [...items.slice(0, -1), trimmedDisplay] : items
|
|
38795
|
+
);
|
|
38796
|
+
setInput("");
|
|
38797
|
+
setHistoryIndex(-1);
|
|
38798
|
+
latestPromptRef.current = { ...editTarget, historyText: trimmedDisplay };
|
|
38799
|
+
setEvents((events2) => replaceUserPromptEvent(events2, editTarget.key, trimmedDisplay));
|
|
38800
|
+
if (updatedQueue) {
|
|
38801
|
+
setQueue(updatedQueue);
|
|
38802
|
+
return;
|
|
38803
|
+
}
|
|
38804
|
+
const edit = {
|
|
38805
|
+
key: editTarget.key,
|
|
38806
|
+
full: trimmedFull,
|
|
38807
|
+
display: trimmedDisplay,
|
|
38808
|
+
messagesBefore: editTarget.messagesBefore
|
|
38809
|
+
};
|
|
38810
|
+
if (busyRef.current || supervisorRef.current.isRunning) {
|
|
38811
|
+
pendingPromptEditRef.current = edit;
|
|
38812
|
+
interruptTurn(interruptDepsRef.current);
|
|
38813
|
+
} else {
|
|
38814
|
+
messagesRef.current = [...edit.messagesBefore];
|
|
38815
|
+
setEvents((events2) => replaceUserPromptEvent(events2, edit.key, edit.display, true));
|
|
38816
|
+
processMessage(edit.full, edit.display, {
|
|
38817
|
+
queuedKeys: [edit.key],
|
|
38818
|
+
historyText: edit.display
|
|
38819
|
+
});
|
|
38820
|
+
}
|
|
38821
|
+
return;
|
|
38822
|
+
}
|
|
38823
|
+
}
|
|
38468
38824
|
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 }]);
|
|
38825
|
+
const key2 = mkKey();
|
|
38826
|
+
setEvents((e) => [...e, { kind: "user", key: key2, text: trimmedDisplay, queued: true }]);
|
|
38827
|
+
setQueue((q) => [...q, { full: trimmedFull, display: trimmedDisplay, key: key2 }]);
|
|
38828
|
+
if (!trimmedFull.startsWith("/") && parseBangCommand(trimmedFull) === null) {
|
|
38829
|
+
latestPromptRef.current = { key: key2, historyText: historyEntry, messagesBefore: null };
|
|
38830
|
+
}
|
|
38472
38831
|
setHistory((h) => h.length > 0 && h[h.length - 1] === historyEntry ? h : [...h, historyEntry]);
|
|
38473
38832
|
setInput("");
|
|
38474
38833
|
setHistoryIndex(-1);
|
|
@@ -38477,9 +38836,13 @@ ${conflicts.join("\n")}` }
|
|
|
38477
38836
|
setHistory((h) => h.length > 0 && h[h.length - 1] === historyEntry ? h : [...h, historyEntry]);
|
|
38478
38837
|
setInput("");
|
|
38479
38838
|
setHistoryIndex(-1);
|
|
38480
|
-
|
|
38839
|
+
const key = mkKey();
|
|
38840
|
+
processMessage(trimmedFull, trimmedDisplay !== trimmedFull ? trimmedDisplay : void 0, {
|
|
38841
|
+
eventKey: key,
|
|
38842
|
+
historyText: historyEntry
|
|
38843
|
+
});
|
|
38481
38844
|
},
|
|
38482
|
-
[processMessage, queue2]
|
|
38845
|
+
[processMessage, queue2, history, historyIndex, events]
|
|
38483
38846
|
);
|
|
38484
38847
|
submitRef.current = submit;
|
|
38485
38848
|
useEffect11(() => {
|
|
@@ -38719,6 +39082,7 @@ ${conflicts.join("\n")}` }
|
|
|
38719
39082
|
bridge.sync({
|
|
38720
39083
|
events,
|
|
38721
39084
|
busy,
|
|
39085
|
+
workers: activeWorkers,
|
|
38722
39086
|
mode,
|
|
38723
39087
|
model: cfg?.model ?? "",
|
|
38724
39088
|
usage,
|
|
@@ -38954,6 +39318,7 @@ ${conflicts.join("\n")}` }
|
|
|
38954
39318
|
}
|
|
38955
39319
|
),
|
|
38956
39320
|
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" }) }),
|
|
39321
|
+
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
39322
|
/* @__PURE__ */ jsxs40(Box41, { marginTop: parseBangCommand(input) !== null ? 0 : 1, children: [
|
|
38958
39323
|
/* @__PURE__ */ jsx43(Text42, { color: theme.prompt ?? theme.accent, children: "\u203A " }),
|
|
38959
39324
|
/* @__PURE__ */ jsx43(
|