@kenkaiiii/ggcoder 5.4.2 → 5.5.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/app-sidecar.js +212 -10
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.js +32 -30
- package/dist/config.js.map +1 -1
- package/dist/core/agent-tools-allowlist.test.d.ts +2 -0
- package/dist/core/agent-tools-allowlist.test.d.ts.map +1 -0
- package/dist/core/agent-tools-allowlist.test.js +72 -0
- package/dist/core/agent-tools-allowlist.test.js.map +1 -0
- package/dist/core/autopilot-verdict.d.ts +32 -0
- package/dist/core/autopilot-verdict.d.ts.map +1 -0
- package/dist/core/autopilot-verdict.js +93 -0
- package/dist/core/autopilot-verdict.js.map +1 -0
- package/dist/core/autopilot-verdict.test.d.ts +2 -0
- package/dist/core/autopilot-verdict.test.d.ts.map +1 -0
- package/dist/core/autopilot-verdict.test.js +80 -0
- package/dist/core/autopilot-verdict.test.js.map +1 -0
- package/dist/core/event-bus.d.ts +4 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +6 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/ken-context.d.ts +18 -0
- package/dist/core/ken-context.d.ts.map +1 -1
- package/dist/core/ken-context.js +19 -0
- package/dist/core/ken-context.js.map +1 -1
- package/dist/core/ken-context.test.js +23 -1
- package/dist/core/ken-context.test.js.map +1 -1
- package/dist/core/ken-prompt.d.ts +10 -0
- package/dist/core/ken-prompt.d.ts.map +1 -1
- package/dist/core/ken-prompt.js +48 -0
- package/dist/core/ken-prompt.js.map +1 -1
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +17 -3
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/prompt-commands.test.js +27 -1
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/core/tasks-store.d.ts +7 -0
- package/dist/core/tasks-store.d.ts.map +1 -1
- package/dist/core/tasks-store.js +13 -0
- package/dist/core/tasks-store.js.map +1 -1
- package/dist/modes/json-mode.d.ts +7 -0
- package/dist/modes/json-mode.d.ts.map +1 -1
- package/dist/modes/json-mode.js +4 -0
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +24 -2
- package/dist/tools/subagent.js.map +1 -1
- package/package.json +4 -4
package/dist/app-sidecar.js
CHANGED
|
@@ -20,8 +20,9 @@ import { parseArgs } from "node:util";
|
|
|
20
20
|
import { formatError } from "@kenkaiiii/gg-ai";
|
|
21
21
|
import { runJsonMode } from "./modes/json-mode.js";
|
|
22
22
|
import { AgentSession } from "./core/agent-session.js";
|
|
23
|
-
import { buildKenSystemPrompt } from "./core/ken-prompt.js";
|
|
24
|
-
import { buildKenDigest } from "./core/ken-context.js";
|
|
23
|
+
import { buildKenSystemPrompt, buildKenAutopilotSystemPrompt } from "./core/ken-prompt.js";
|
|
24
|
+
import { buildKenDigest, buildKenAutopilotContext } from "./core/ken-context.js";
|
|
25
|
+
import { parseAutopilotVerdict } from "./core/autopilot-verdict.js";
|
|
25
26
|
import { collectProjectContext } from "./system-prompt.js";
|
|
26
27
|
import { AuthStorage } from "./core/auth-storage.js";
|
|
27
28
|
import { MOONSHOT_OAUTH_KEY, XIAOMI_CREDITS_KEY } from "@kenkaiiii/gg-core";
|
|
@@ -39,7 +40,7 @@ import { getNextThinkingLevel, getSupportedThinkingLevels, isThinkingLevelSuppor
|
|
|
39
40
|
import { PROMPT_COMMANDS } from "./core/prompt-commands.js";
|
|
40
41
|
import { loadCustomCommands } from "./core/custom-commands.js";
|
|
41
42
|
import { discoverProjects, listRecentSessions } from "./core/project-discovery.js";
|
|
42
|
-
import { loadTasksSync, saveTasksSync, getNextPendingTask, markTaskInProgress, } from "./core/tasks-store.js";
|
|
43
|
+
import { loadTasksSync, saveTasksSync, pruneDoneTasksSync, getNextPendingTask, markTaskInProgress, } from "./core/tasks-store.js";
|
|
43
44
|
import { initLogger, log } from "./core/logger.js";
|
|
44
45
|
import { RADIO_STATIONS, getCurrentStation, playRadio, stopRadio } from "./core/radio.js";
|
|
45
46
|
import { enrichProcessPath } from "./core/shell-path.js";
|
|
@@ -80,6 +81,7 @@ async function loadAppSettings() {
|
|
|
80
81
|
// Preserve the per-project map verbatim (validated + written by the
|
|
81
82
|
// model/thinking handlers below).
|
|
82
83
|
projectModels: raw.projectModels && typeof raw.projectModels === "object" ? raw.projectModels : undefined,
|
|
84
|
+
autopilot: raw.autopilot && typeof raw.autopilot === "object" ? raw.autopilot : undefined,
|
|
83
85
|
};
|
|
84
86
|
}
|
|
85
87
|
catch {
|
|
@@ -103,6 +105,19 @@ async function saveProjectModelPrefs(cwd, prefs) {
|
|
|
103
105
|
s.projectModels = { ...(s.projectModels ?? {}), [key]: prefs };
|
|
104
106
|
await saveAppSettings(s);
|
|
105
107
|
}
|
|
108
|
+
/** Read this project's persisted autopilot flag (default off). */
|
|
109
|
+
async function loadAutopilot(cwd) {
|
|
110
|
+
const s = await loadAppSettings();
|
|
111
|
+
return s.autopilot?.[projectModelKey(cwd)] ?? false;
|
|
112
|
+
}
|
|
113
|
+
/** Persist this project's autopilot flag via read-modify-write so the rest of
|
|
114
|
+
* the settings file (projectsRoot, model map, other projects) is preserved. */
|
|
115
|
+
async function saveAutopilot(cwd, enabled) {
|
|
116
|
+
const s = await loadAppSettings();
|
|
117
|
+
const key = projectModelKey(cwd);
|
|
118
|
+
s.autopilot = { ...(s.autopilot ?? {}), [key]: enabled };
|
|
119
|
+
await saveAppSettings(s);
|
|
120
|
+
}
|
|
106
121
|
/**
|
|
107
122
|
* Persist the active model selection to ~/.gg/settings.json so it survives app
|
|
108
123
|
* restarts. Mirrors the CLI's handleModelSelect persistence (App.tsx).
|
|
@@ -759,6 +774,24 @@ async function createSession(deps, opts) {
|
|
|
759
774
|
session.eventBus.on("compaction_end", (d) => broadcast("compaction_end", d));
|
|
760
775
|
let running = false;
|
|
761
776
|
let titleGenerated = false;
|
|
777
|
+
// Autopilot (auto-review) toggle for THIS window's project. Loaded from
|
|
778
|
+
// gg-app.json on boot; flipped via POST /autopilot. When on, POST /prompt runs
|
|
779
|
+
// runAutopilotCycle after the user's turn settles — Ken auto-reviews the work
|
|
780
|
+
// and drives the review→prompt→review loop.
|
|
781
|
+
let autopilot = await loadAutopilot(cwd);
|
|
782
|
+
// True while an autopilot review is in flight (used to defer kenAuto model
|
|
783
|
+
// switches, like kenRunning does for chat Ken, and to drive the spinner).
|
|
784
|
+
let autopilotReviewing = false;
|
|
785
|
+
// True for the WHOLE autopilot cycle (reviews + injected runs). The build
|
|
786
|
+
// `running` flag is false during the review windows between injected runs, so
|
|
787
|
+
// this is the extra guard that makes a user /prompt queue as steering instead
|
|
788
|
+
// of starting a run that would collide with an injected one on the same
|
|
789
|
+
// session (AgentSession.prompt has no concurrency guard).
|
|
790
|
+
let autopilotActive = false;
|
|
791
|
+
// Set by /cancel to break out of an in-flight autopilot cycle between steps.
|
|
792
|
+
let autopilotCancelled = false;
|
|
793
|
+
// Hard cap on review→prompt→review rounds per user turn (loop safety).
|
|
794
|
+
const MAX_AUTOPILOT_ROUNDS = 3;
|
|
762
795
|
// ── Telegram serve (remote control via Telegram) ───────────
|
|
763
796
|
// A single embedded serve session lives in this sidecar process. Only the main
|
|
764
797
|
// window's home screen exposes the controls, so there's one bot per app.
|
|
@@ -827,6 +860,53 @@ async function createSession(deps, opts) {
|
|
|
827
860
|
log("INFO", "app-sidecar", "ken session ready", { provider: st.provider, model: st.model });
|
|
828
861
|
return ken;
|
|
829
862
|
}
|
|
863
|
+
// ── Autopilot Ken (auto-reviewer) ──────────────────────────
|
|
864
|
+
// A THIRD read-only AgentSession, separate from chat Ken. In autopilot mode
|
|
865
|
+
// Ken silently reviews each finished GG Coder turn and returns a verdict
|
|
866
|
+
// (PROMPT / ALL_CLEAR / HUMAN). Its bus is intentionally NOT bridged to the
|
|
867
|
+
// ken_* chat bubbles — the review is silent; we read its final assistant text
|
|
868
|
+
// and parse it. Uses the lean autopilot system prompt + the same read-only
|
|
869
|
+
// tools. Created lazily on the first autopilot cycle.
|
|
870
|
+
let kenAutoSession = null;
|
|
871
|
+
let kenAutoAbort = new AbortController();
|
|
872
|
+
let pendingKenAutoModel = null;
|
|
873
|
+
async function syncKenAutoModel(provider, model) {
|
|
874
|
+
if (autopilotReviewing) {
|
|
875
|
+
pendingKenAutoModel = { provider, model };
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
if (!kenAutoSession)
|
|
879
|
+
return;
|
|
880
|
+
const st = kenAutoSession.getState();
|
|
881
|
+
if (st.provider === provider && st.model === model)
|
|
882
|
+
return;
|
|
883
|
+
await kenAutoSession.switchModel(provider, model);
|
|
884
|
+
log("INFO", "app-sidecar", "ken autopilot session model synced", { provider, model });
|
|
885
|
+
}
|
|
886
|
+
async function ensureKenAutoSession() {
|
|
887
|
+
if (kenAutoSession)
|
|
888
|
+
return kenAutoSession;
|
|
889
|
+
const st = session.getState();
|
|
890
|
+
const ken = new AgentSession({
|
|
891
|
+
provider: st.provider,
|
|
892
|
+
model: st.model,
|
|
893
|
+
cwd,
|
|
894
|
+
systemPrompt: buildKenAutopilotSystemPrompt(),
|
|
895
|
+
allowedTools: KEN_ALLOWED_TOOLS,
|
|
896
|
+
allowedMcpServers: KEN_ALLOWED_MCP_SERVERS,
|
|
897
|
+
transient: true,
|
|
898
|
+
signal: kenAutoAbort.signal,
|
|
899
|
+
});
|
|
900
|
+
await ken.initialize();
|
|
901
|
+
// Deliberately no bus bridge: the review is silent. Errors surface via the
|
|
902
|
+
// runAutopilotReview try/catch as autopilot_error frames.
|
|
903
|
+
kenAutoSession = ken;
|
|
904
|
+
log("INFO", "app-sidecar", "ken autopilot session ready", {
|
|
905
|
+
provider: st.provider,
|
|
906
|
+
model: st.model,
|
|
907
|
+
});
|
|
908
|
+
return ken;
|
|
909
|
+
}
|
|
830
910
|
// Resumed session: if it already has a conversation, generate its title now so
|
|
831
911
|
// the title bar shows it immediately on load (not just after the next prompt).
|
|
832
912
|
{
|
|
@@ -861,6 +941,14 @@ async function createSession(deps, opts) {
|
|
|
861
941
|
gitBranch = await getGitBranch(cwd).catch(() => gitBranch);
|
|
862
942
|
gitIsRepo = await isGitRepo(cwd).catch(() => gitIsRepo);
|
|
863
943
|
broadcast("run_end", {});
|
|
944
|
+
// Autopilot's review loop is driven explicitly from POST /prompt (see
|
|
945
|
+
// runAutopilotCycle), NOT from this shared finally — that keeps the
|
|
946
|
+
// injected GG Coder runs this cycle triggers from recursively re-entering
|
|
947
|
+
// the loop through the same bracket.
|
|
948
|
+
// The agent may have marked project tasks done during the run — prune the
|
|
949
|
+
// completed ones so they drop out of the Tasks modal automatically (users
|
|
950
|
+
// never have to delete finished tasks by hand).
|
|
951
|
+
broadcast("tasks_list", { tasks: pruneDoneTasksSync(cwd) });
|
|
864
952
|
// Queue drains into the run as steering, so it's empty by run_end —
|
|
865
953
|
// sync the webview indicator.
|
|
866
954
|
broadcast("queued", { count: session.getQueuedCount() });
|
|
@@ -876,6 +964,79 @@ async function createSession(deps, opts) {
|
|
|
876
964
|
}
|
|
877
965
|
}
|
|
878
966
|
}
|
|
967
|
+
// ── Autopilot orchestration ─────────────────────────────────
|
|
968
|
+
// One review = prompt the kenAuto session with the review digest, read its
|
|
969
|
+
// final assistant text, parse a verdict. Returns null on failure (surfaced as
|
|
970
|
+
// an autopilot_error frame) so the cycle stops rather than looping blind.
|
|
971
|
+
async function runAutopilotReview() {
|
|
972
|
+
autopilotReviewing = true;
|
|
973
|
+
broadcast("autopilot_review_start", {});
|
|
974
|
+
try {
|
|
975
|
+
const ken = await ensureKenAutoSession();
|
|
976
|
+
const projectContext = await collectProjectContext(cwd).catch(() => []);
|
|
977
|
+
const digest = buildKenAutopilotContext({
|
|
978
|
+
projectContext,
|
|
979
|
+
cwd,
|
|
980
|
+
gitBranch,
|
|
981
|
+
messages: session.getMessages(),
|
|
982
|
+
});
|
|
983
|
+
await ken.prompt(digest);
|
|
984
|
+
return parseAutopilotVerdict(lastAssistantText(ken.getMessages()));
|
|
985
|
+
}
|
|
986
|
+
catch (err) {
|
|
987
|
+
broadcastError("autopilot_error", "autopilot review failed", err);
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
finally {
|
|
991
|
+
autopilotReviewing = false;
|
|
992
|
+
// Apply any model switch that landed mid-review.
|
|
993
|
+
const pending = pendingKenAutoModel;
|
|
994
|
+
pendingKenAutoModel = null;
|
|
995
|
+
if (pending)
|
|
996
|
+
await syncKenAutoModel(pending.provider, pending.model);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
// Drive the review→prompt→review loop for one finished user turn. Only ever
|
|
1000
|
+
// called from POST /prompt after the user's own run resolves — never from the
|
|
1001
|
+
// task runner, resume, /ken, or error paths, so there's no recursion and no
|
|
1002
|
+
// guard tangle. Bounded by MAX_AUTOPILOT_ROUNDS and cancellable between steps.
|
|
1003
|
+
async function runAutopilotCycle() {
|
|
1004
|
+
if (!autopilot || autopilotCancelled)
|
|
1005
|
+
return;
|
|
1006
|
+
autopilotActive = true;
|
|
1007
|
+
try {
|
|
1008
|
+
// Lean context per user turn: wipe prior review history so each new turn
|
|
1009
|
+
// starts cheap, while within this cycle the few review messages persist so
|
|
1010
|
+
// Ken remembers what he already asked GG Coder to fix.
|
|
1011
|
+
await kenAutoSession?.newSession().catch(() => { });
|
|
1012
|
+
for (let round = 1; round <= MAX_AUTOPILOT_ROUNDS; round++) {
|
|
1013
|
+
if (autopilotCancelled)
|
|
1014
|
+
return;
|
|
1015
|
+
const verdict = await runAutopilotReview();
|
|
1016
|
+
if (!verdict || autopilotCancelled)
|
|
1017
|
+
return;
|
|
1018
|
+
if (verdict.kind === "all_clear") {
|
|
1019
|
+
broadcast("autopilot_done", {});
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (verdict.kind === "human") {
|
|
1023
|
+
broadcast("autopilot_human", { reason: verdict.reason });
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
// prompt → show a compact Ken-tinted marker (not the prompt body), then
|
|
1027
|
+
// feed GG Coder. Bracketed by runAgent so the run streams normally; the
|
|
1028
|
+
// shared finally no longer re-triggers autopilot, so this can't recurse.
|
|
1029
|
+
broadcast("autopilot_prompted", { round, body: verdict.body });
|
|
1030
|
+
await runAgent(verdict.body, () => session.prompt(verdict.body));
|
|
1031
|
+
if (autopilotCancelled)
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
broadcast("autopilot_capped", { rounds: MAX_AUTOPILOT_ROUNDS });
|
|
1035
|
+
}
|
|
1036
|
+
finally {
|
|
1037
|
+
autopilotActive = false;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
879
1040
|
// ── Task runner (project task list → sessions) ──────────────
|
|
880
1041
|
// Mirrors the CLI's task flow: each task runs in its OWN fresh session, with a
|
|
881
1042
|
// completion hint instructing the agent to mark the task done via the tasks
|
|
@@ -897,8 +1058,8 @@ async function createSession(deps, opts) {
|
|
|
897
1058
|
`tasks({ action: "done", id: "${shortId}" })`;
|
|
898
1059
|
await runAgent(task.title, () => session.prompt(task.prompt + completionHint));
|
|
899
1060
|
// The agent typically marks the task done via the tasks tool during the run;
|
|
900
|
-
// push the refreshed list so the
|
|
901
|
-
broadcast("tasks_list", { tasks:
|
|
1061
|
+
// prune completed tasks and push the refreshed list so the modal drops them.
|
|
1062
|
+
broadcast("tasks_list", { tasks: pruneDoneTasksSync(cwd) });
|
|
902
1063
|
return true;
|
|
903
1064
|
}
|
|
904
1065
|
async function runTasks(startId, all) {
|
|
@@ -993,6 +1154,7 @@ async function createSession(deps, opts) {
|
|
|
993
1154
|
thinkingLevel: session.getThinkingLevel() ?? null,
|
|
994
1155
|
supportedThinkingLevels: getSupportedThinkingLevels(st.provider, st.model),
|
|
995
1156
|
supportsVideo: getModel(st.model)?.supportsVideo ?? false,
|
|
1157
|
+
autopilot,
|
|
996
1158
|
...footerExtras(),
|
|
997
1159
|
});
|
|
998
1160
|
return;
|
|
@@ -1016,6 +1178,7 @@ async function createSession(deps, opts) {
|
|
|
1016
1178
|
thinkingLevel: session.getThinkingLevel() ?? null,
|
|
1017
1179
|
supportedThinkingLevels: getSupportedThinkingLevels(st.provider, st.model),
|
|
1018
1180
|
supportsVideo: getModel(st.model)?.supportsVideo ?? false,
|
|
1181
|
+
autopilot,
|
|
1019
1182
|
...footerExtras(),
|
|
1020
1183
|
},
|
|
1021
1184
|
})}\n\n`);
|
|
@@ -1339,10 +1502,13 @@ async function createSession(deps, opts) {
|
|
|
1339
1502
|
json(res, 400, { error: "empty prompt" });
|
|
1340
1503
|
return;
|
|
1341
1504
|
}
|
|
1342
|
-
if (running) {
|
|
1343
|
-
// Queue prompts as mid-run steering (mirrors the CLI).
|
|
1344
|
-
//
|
|
1345
|
-
//
|
|
1505
|
+
if (running || autopilotActive) {
|
|
1506
|
+
// Queue prompts as mid-run steering (mirrors the CLI). Also queue while
|
|
1507
|
+
// an autopilot cycle is active but between injected runs (build idle,
|
|
1508
|
+
// Ken reviewing) so the message never starts a run that collides with
|
|
1509
|
+
// an injected one on the same session. Attachments are persisted to
|
|
1510
|
+
// .gg/uploads first so the queued media rides the same native-block
|
|
1511
|
+
// path as a non-queued attachment prompt when it drains.
|
|
1346
1512
|
const prepared = attachments.length > 0 ? await prepareAttachments(cwd, attachments) : [];
|
|
1347
1513
|
const count = session.queueMessage(text, prepared);
|
|
1348
1514
|
broadcast("queued", { count });
|
|
@@ -1350,6 +1516,9 @@ async function createSession(deps, opts) {
|
|
|
1350
1516
|
return;
|
|
1351
1517
|
}
|
|
1352
1518
|
json(res, 202, { accepted: true });
|
|
1519
|
+
// Fresh user turn: clear any cancel flag left from a prior cycle so this
|
|
1520
|
+
// turn's autopilot review can run.
|
|
1521
|
+
autopilotCancelled = false;
|
|
1353
1522
|
await runAgent(text, async () => {
|
|
1354
1523
|
if (attachments.length > 0) {
|
|
1355
1524
|
// Persist each attachment under .gg/uploads so files are inspectable
|
|
@@ -1365,6 +1534,11 @@ async function createSession(deps, opts) {
|
|
|
1365
1534
|
await session.prompt(text);
|
|
1366
1535
|
}
|
|
1367
1536
|
});
|
|
1537
|
+
// After the user's run settles, kick off Ken's auto-review loop. This is
|
|
1538
|
+
// the ONLY entry point into the cycle — it drives any follow-up GG Coder
|
|
1539
|
+
// runs itself, so the shared runAgent finally never recurses.
|
|
1540
|
+
if (autopilot && !autopilotCancelled)
|
|
1541
|
+
await runAutopilotCycle();
|
|
1368
1542
|
});
|
|
1369
1543
|
return;
|
|
1370
1544
|
}
|
|
@@ -1427,6 +1601,24 @@ async function createSession(deps, opts) {
|
|
|
1427
1601
|
json(res, 200, { cancelled: true });
|
|
1428
1602
|
return;
|
|
1429
1603
|
}
|
|
1604
|
+
if (method === "POST" && url === "/autopilot") {
|
|
1605
|
+
void readBody(req).then(async (raw) => {
|
|
1606
|
+
let enabled;
|
|
1607
|
+
try {
|
|
1608
|
+
enabled = Boolean(JSON.parse(raw).enabled);
|
|
1609
|
+
}
|
|
1610
|
+
catch {
|
|
1611
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
autopilot = enabled;
|
|
1615
|
+
await saveAutopilot(cwd, enabled);
|
|
1616
|
+
log("INFO", "app-sidecar", "autopilot toggled", { enabled: String(enabled) });
|
|
1617
|
+
broadcast("autopilot", { autopilot: enabled });
|
|
1618
|
+
json(res, 200, { autopilot: enabled });
|
|
1619
|
+
});
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1430
1622
|
if (method === "POST" && url === "/enhance") {
|
|
1431
1623
|
void readBody(req).then(async (raw) => {
|
|
1432
1624
|
let text;
|
|
@@ -1456,7 +1648,7 @@ async function createSession(deps, opts) {
|
|
|
1456
1648
|
return;
|
|
1457
1649
|
}
|
|
1458
1650
|
if (method === "GET" && url === "/tasks") {
|
|
1459
|
-
json(res, 200, { tasks:
|
|
1651
|
+
json(res, 200, { tasks: pruneDoneTasksSync(cwd) });
|
|
1460
1652
|
return;
|
|
1461
1653
|
}
|
|
1462
1654
|
// ── Radio (app-wide) ──────────────────────────────────────
|
|
@@ -1576,6 +1768,7 @@ async function createSession(deps, opts) {
|
|
|
1576
1768
|
}
|
|
1577
1769
|
await session.switchModel(target.provider, target.id);
|
|
1578
1770
|
await syncKenModel(target.provider, target.id);
|
|
1771
|
+
await syncKenAutoModel(target.provider, target.id);
|
|
1579
1772
|
// Clamp the reasoning level to what the new model supports (mirrors the
|
|
1580
1773
|
// CLI): keep thinking on at the first supported tier if it was on but
|
|
1581
1774
|
// the prior level is unsupported here; leave it off if it was off.
|
|
@@ -1657,6 +1850,13 @@ async function createSession(deps, opts) {
|
|
|
1657
1850
|
running = false;
|
|
1658
1851
|
// Stop a run-all sweep so the next pending task isn't auto-started.
|
|
1659
1852
|
taskRunAll = false;
|
|
1853
|
+
// Stop any in-flight autopilot cycle: flag it so the loop bails between
|
|
1854
|
+
// steps, and abort a review that's mid-prompt on the kenAuto session.
|
|
1855
|
+
autopilotCancelled = true;
|
|
1856
|
+
kenAutoAbort.abort();
|
|
1857
|
+
kenAutoAbort = new AbortController();
|
|
1858
|
+
kenAutoSession?.setSignal(kenAutoAbort.signal);
|
|
1859
|
+
autopilotReviewing = false;
|
|
1660
1860
|
// Drop any queued steering and return it so the webview can restore it to
|
|
1661
1861
|
// the composer.
|
|
1662
1862
|
const drained = session.drainQueue();
|
|
@@ -2143,7 +2343,9 @@ async function createSession(deps, opts) {
|
|
|
2143
2343
|
for (const c of clients)
|
|
2144
2344
|
c.res.end();
|
|
2145
2345
|
kenAbort.abort();
|
|
2346
|
+
kenAutoAbort.abort();
|
|
2146
2347
|
await kenSession?.dispose().catch(() => { });
|
|
2348
|
+
await kenAutoSession?.dispose().catch(() => { });
|
|
2147
2349
|
await session.dispose().catch(() => { });
|
|
2148
2350
|
}
|
|
2149
2351
|
return {
|