@kenkaiiii/ggcoder 5.16.0 → 5.17.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 +129 -44
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-review-coverage.test.d.ts +2 -0
- package/dist/core/agent-session-review-coverage.test.d.ts.map +1 -0
- package/dist/core/agent-session-review-coverage.test.js +57 -0
- package/dist/core/agent-session-review-coverage.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +11 -4
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +133 -22
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +1 -1
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/event-bus.d.ts +2 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/ideal-review.d.ts +27 -0
- package/dist/core/ideal-review.d.ts.map +1 -1
- package/dist/core/ideal-review.js +64 -0
- package/dist/core/ideal-review.js.map +1 -1
- package/dist/core/ideal-review.test.js +34 -1
- package/dist/core/ideal-review.test.js.map +1 -1
- package/dist/core/lsp/client.d.ts +3 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +20 -0
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/lsp/manager.d.ts +32 -9
- package/dist/core/lsp/manager.d.ts.map +1 -1
- package/dist/core/lsp/manager.js +89 -37
- package/dist/core/lsp/manager.js.map +1 -1
- package/dist/core/lsp/manager.test.js +56 -4
- package/dist/core/lsp/manager.test.js.map +1 -1
- package/dist/core/run-lifecycle.d.ts +40 -0
- package/dist/core/run-lifecycle.d.ts.map +1 -0
- package/dist/core/run-lifecycle.js +95 -0
- package/dist/core/run-lifecycle.js.map +1 -0
- package/dist/core/run-lifecycle.test.d.ts +2 -0
- package/dist/core/run-lifecycle.test.d.ts.map +1 -0
- package/dist/core/run-lifecycle.test.js +64 -0
- package/dist/core/run-lifecycle.test.js.map +1 -0
- package/dist/core/session-manager.d.ts +25 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +66 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.js +80 -2
- package/dist/core/session-manager.test.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +40 -3
- package/dist/core/subagent-manager.d.ts.map +1 -1
- package/dist/core/subagent-manager.js +342 -30
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-manager.test.js +189 -3
- package/dist/core/subagent-manager.test.js.map +1 -1
- package/dist/core/subagent-store.d.ts +20 -0
- package/dist/core/subagent-store.d.ts.map +1 -0
- package/dist/core/subagent-store.js +177 -0
- package/dist/core/subagent-store.js.map +1 -0
- package/dist/core/subagent-store.test.d.ts +2 -0
- package/dist/core/subagent-store.test.d.ts.map +1 -0
- package/dist/core/subagent-store.test.js +94 -0
- package/dist/core/subagent-store.test.js.map +1 -0
- package/dist/modes/agent-home-mode.d.ts.map +1 -1
- package/dist/modes/agent-home-mode.js +10 -3
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +10 -3
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.d.ts +2 -0
- package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
- package/dist/modes/subagent-worker-mode.js +13 -3
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/tools/tasks.d.ts +1 -1
- package/dist/tools/web-fetch.d.ts +1 -1
- package/dist/ui/App.d.ts +7 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +63 -17
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +1 -0
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +3 -3
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/Footer.test.d.ts +2 -0
- package/dist/ui/components/Footer.test.d.ts.map +1 -0
- package/dist/ui/components/Footer.test.js +16 -0
- package/dist/ui/components/Footer.test.js.map +1 -0
- package/dist/ui/hooks/useAgentLoop.d.ts +8 -3
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +64 -8
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useSessionPersistence.d.ts +4 -2
- package/dist/ui/hooks/useSessionPersistence.d.ts.map +1 -1
- package/dist/ui/hooks/useSessionPersistence.js +7 -1
- package/dist/ui/hooks/useSessionPersistence.js.map +1 -1
- package/dist/ui/render.d.ts +7 -0
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +4 -0
- package/dist/ui/render.js.map +1 -1
- package/package.json +4 -4
package/dist/app-sidecar.js
CHANGED
|
@@ -18,10 +18,11 @@ import os from "node:os";
|
|
|
18
18
|
import path from "node:path";
|
|
19
19
|
import { randomUUID } from "node:crypto";
|
|
20
20
|
import { parseArgs } from "node:util";
|
|
21
|
-
import { formatError } from "@kenkaiiii/gg-ai";
|
|
21
|
+
import { environmentSecrets, formatError, redactValue, } from "@kenkaiiii/gg-ai";
|
|
22
22
|
import { runJsonMode } from "./modes/json-mode.js";
|
|
23
23
|
import { runSubagentWorkerMode } from "./modes/subagent-worker-mode.js";
|
|
24
24
|
import { AgentSession } from "./core/agent-session.js";
|
|
25
|
+
import { RunLifecycle } from "./core/run-lifecycle.js";
|
|
25
26
|
import { CHAT_AGENT_IDS, chatAgentSessionsDir, createChatAgent, parseChatAgentId, sessionsDirForChatAgent, switchChatAgent, } from "./chat-agents/index.js";
|
|
26
27
|
import { buildJiwaTools, JiwaStore } from "./chat-agents/jiwa.js";
|
|
27
28
|
import { buildMemoryTools, MemoryStore } from "./chat-agents/memory.js";
|
|
@@ -929,7 +930,8 @@ async function createSession(deps, opts) {
|
|
|
929
930
|
const clients = new Set();
|
|
930
931
|
let clientSeq = 0;
|
|
931
932
|
function broadcast(type, data) {
|
|
932
|
-
const
|
|
933
|
+
const safePayload = redactValue({ type, data }, { secrets: environmentSecrets(process.env) });
|
|
934
|
+
const frame = `data: ${JSON.stringify(safePayload)}\n\n`;
|
|
933
935
|
for (const c of clients)
|
|
934
936
|
c.res.write(frame);
|
|
935
937
|
}
|
|
@@ -1123,6 +1125,13 @@ async function createSession(deps, opts) {
|
|
|
1123
1125
|
session.eventBus.on("compaction_start", (d) => broadcast("compaction_start", d));
|
|
1124
1126
|
session.eventBus.on("compaction_end", (d) => broadcast("compaction_end", d));
|
|
1125
1127
|
let running = false;
|
|
1128
|
+
const runLifecycle = new RunLifecycle((runState) => {
|
|
1129
|
+
running = runState !== "idle";
|
|
1130
|
+
if (runState === "cancelling")
|
|
1131
|
+
broadcast("run_cancelling", { runState });
|
|
1132
|
+
});
|
|
1133
|
+
const cancelledRunEndGenerations = new Set();
|
|
1134
|
+
let pendingCancelDrain = null;
|
|
1126
1135
|
let titleGenerated = false;
|
|
1127
1136
|
// Bumped by /cancel — a run whose cancel generation changed mid-flight was
|
|
1128
1137
|
// canceled and earns no XP.
|
|
@@ -1145,6 +1154,7 @@ async function createSession(deps, opts) {
|
|
|
1145
1154
|
let autopilotCancelled = false;
|
|
1146
1155
|
// Hard cap on review→prompt→review rounds per user turn (loop safety).
|
|
1147
1156
|
const MAX_AUTOPILOT_ROUNDS = 3;
|
|
1157
|
+
const CANCEL_TIMEOUT_MS = 5_000;
|
|
1148
1158
|
// Prompt bodies Autopilot Ken injected into the BUILD session this
|
|
1149
1159
|
// conversation. Passed into every Ken digest so injected prompts render as
|
|
1150
1160
|
// "Ken autopilot (injected)" instead of `**User:**` — otherwise multi-round
|
|
@@ -1351,29 +1361,63 @@ async function createSession(deps, opts) {
|
|
|
1351
1361
|
});
|
|
1352
1362
|
}
|
|
1353
1363
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1364
|
+
function abortOwnedWork() {
|
|
1365
|
+
cancelGeneration++;
|
|
1366
|
+
abort.abort();
|
|
1367
|
+
// Stop a run-all sweep and every async child through AgentSession's signal.
|
|
1368
|
+
taskRunAll = false;
|
|
1369
|
+
autopilotCancelled = true;
|
|
1370
|
+
kenAutoAbort.abort();
|
|
1371
|
+
}
|
|
1372
|
+
function installFreshRunControllers() {
|
|
1373
|
+
abort = new AbortController();
|
|
1374
|
+
session.setSignal(abort.signal);
|
|
1375
|
+
kenAutoAbort = new AbortController();
|
|
1376
|
+
kenAutoSession?.setSignal(kenAutoAbort.signal);
|
|
1377
|
+
}
|
|
1378
|
+
function finishOwnedGeneration(generation, emitCancelledFallback) {
|
|
1379
|
+
const cancelled = runLifecycle.isCancellationRequested(generation);
|
|
1380
|
+
const settlement = runLifecycle.settle(generation);
|
|
1381
|
+
if (!settlement.settled)
|
|
1382
|
+
return cancelled;
|
|
1383
|
+
// A replacement signal is safe only after the provider-backed owner settled.
|
|
1384
|
+
installFreshRunControllers();
|
|
1385
|
+
if (cancelled && emitCancelledFallback && !cancelledRunEndGenerations.has(generation)) {
|
|
1386
|
+
cancelledRunEndGenerations.add(generation);
|
|
1387
|
+
broadcast("run_end", { cancelled: true, runState: runLifecycle.state });
|
|
1388
|
+
}
|
|
1389
|
+
return cancelled;
|
|
1390
|
+
}
|
|
1391
|
+
// Core provider-run bracket. Standalone runs own a lifecycle generation;
|
|
1392
|
+
// injected autopilot runs share the cycle's outer generation.
|
|
1358
1393
|
async function runAgent(label, run) {
|
|
1359
|
-
|
|
1394
|
+
const ownsGeneration = !runLifecycle.running;
|
|
1395
|
+
const generation = ownsGeneration
|
|
1396
|
+
? runLifecycle.begin(abortOwnedWork).generation
|
|
1397
|
+
: runLifecycle.generation;
|
|
1398
|
+
if (ownsGeneration)
|
|
1399
|
+
pendingCancelDrain = null;
|
|
1360
1400
|
// Progress (Ranks): completed, non-canceled runs with ≥1 assistant turn earn
|
|
1361
1401
|
// XP — prompt + any commits authored during the run window.
|
|
1362
1402
|
const runStartedAt = Date.now();
|
|
1363
1403
|
const cancelGenAtStart = cancelGeneration;
|
|
1364
1404
|
const assistantsBeforeRun = countAssistantMessages(session.getMessages());
|
|
1365
1405
|
let runSucceeded = false;
|
|
1366
|
-
broadcast("run_start", { text: label });
|
|
1406
|
+
broadcast("run_start", { text: label, runState: runLifecycle.state });
|
|
1367
1407
|
try {
|
|
1368
|
-
|
|
1408
|
+
if (!runLifecycle.isCancellationRequested(generation))
|
|
1409
|
+
await run();
|
|
1369
1410
|
runSucceeded = true;
|
|
1370
1411
|
}
|
|
1371
1412
|
catch (err) {
|
|
1372
|
-
|
|
1413
|
+
if (!runLifecycle.isCancellationRequested(generation)) {
|
|
1414
|
+
broadcastError("error", "run failed", err);
|
|
1415
|
+
}
|
|
1373
1416
|
}
|
|
1374
1417
|
finally {
|
|
1375
|
-
|
|
1418
|
+
const cancelled = runLifecycle.isCancellationRequested(generation);
|
|
1376
1419
|
if (runSucceeded &&
|
|
1420
|
+
!cancelled &&
|
|
1377
1421
|
cancelGeneration === cancelGenAtStart &&
|
|
1378
1422
|
countAssistantMessages(session.getMessages()) > assistantsBeforeRun) {
|
|
1379
1423
|
// Fire-and-forget — XP must never delay or break run teardown.
|
|
@@ -1383,21 +1427,24 @@ async function createSession(deps, opts) {
|
|
|
1383
1427
|
// background tasks — refresh the footer extras once it settles.
|
|
1384
1428
|
gitBranch = await getGitBranch(cwd).catch(() => gitBranch);
|
|
1385
1429
|
gitIsRepo = await isGitRepo(cwd).catch(() => gitIsRepo);
|
|
1386
|
-
|
|
1430
|
+
if (ownsGeneration)
|
|
1431
|
+
finishOwnedGeneration(generation, false);
|
|
1432
|
+
// A cancelled injected run is still owned by the surrounding autopilot
|
|
1433
|
+
// cycle; its outer finalizer emits the one terminal cancelled run_end.
|
|
1434
|
+
if (!(cancelled && !ownsGeneration)) {
|
|
1435
|
+
if (cancelled)
|
|
1436
|
+
cancelledRunEndGenerations.add(generation);
|
|
1437
|
+
broadcast("run_end", {
|
|
1438
|
+
...(cancelled ? { cancelled: true } : {}),
|
|
1439
|
+
runState: runLifecycle.state,
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1387
1442
|
// Autopilot's review loop is driven explicitly from POST /prompt (see
|
|
1388
|
-
// runAutopilotCycle), NOT from this shared finally — that keeps
|
|
1389
|
-
//
|
|
1390
|
-
// the loop through the same bracket.
|
|
1391
|
-
// The agent may have marked project tasks done during the run — prune the
|
|
1392
|
-
// completed ones so they drop out of the Tasks modal automatically (users
|
|
1393
|
-
// never have to delete finished tasks by hand).
|
|
1443
|
+
// runAutopilotCycle), NOT from this shared finally — that keeps injected
|
|
1444
|
+
// runs from recursively entering the same review loop.
|
|
1394
1445
|
broadcast("tasks_list", { tasks: pruneDoneTasksSync(cwd) });
|
|
1395
|
-
// Queue drains into the run as steering, so it's empty by run_end —
|
|
1396
|
-
// sync the webview indicator.
|
|
1397
1446
|
broadcast("queued", { count: session.getQueuedCount() });
|
|
1398
1447
|
broadcast("extras", footerExtras());
|
|
1399
|
-
// Generate a session title once, after the first run, for the title bar
|
|
1400
|
-
// (best-effort, async — don't block the response).
|
|
1401
1448
|
if (!titleGenerated) {
|
|
1402
1449
|
titleGenerated = true;
|
|
1403
1450
|
void session.generateTitle().then((title) => {
|
|
@@ -1430,7 +1477,8 @@ async function createSession(deps, opts) {
|
|
|
1430
1477
|
return parseAutopilotVerdict(lastAssistantText(ken.getMessages()));
|
|
1431
1478
|
}
|
|
1432
1479
|
catch (err) {
|
|
1433
|
-
|
|
1480
|
+
if (!autopilotCancelled)
|
|
1481
|
+
broadcastError("autopilot_error", "autopilot review failed", err);
|
|
1434
1482
|
return null;
|
|
1435
1483
|
}
|
|
1436
1484
|
finally {
|
|
@@ -1505,6 +1553,8 @@ async function createSession(deps, opts) {
|
|
|
1505
1553
|
async function runAutopilotCycle(originalRequest) {
|
|
1506
1554
|
if (!autopilot || autopilotCancelled)
|
|
1507
1555
|
return;
|
|
1556
|
+
const generation = runLifecycle.begin(abortOwnedWork).generation;
|
|
1557
|
+
pendingCancelDrain = null;
|
|
1508
1558
|
autopilotActive = true;
|
|
1509
1559
|
// Generation captured by the last plan review; acceptPlan re-checks it so
|
|
1510
1560
|
// a user Accept/Reject landing mid-review always wins.
|
|
@@ -1619,6 +1669,8 @@ async function createSession(deps, opts) {
|
|
|
1619
1669
|
}
|
|
1620
1670
|
finally {
|
|
1621
1671
|
autopilotActive = false;
|
|
1672
|
+
finishOwnedGeneration(generation, true);
|
|
1673
|
+
queueMicrotask(() => void runStrandedQueue());
|
|
1622
1674
|
}
|
|
1623
1675
|
}
|
|
1624
1676
|
// ── Stranded-queue drain ───────────────────────────────
|
|
@@ -1795,7 +1847,7 @@ async function createSession(deps, opts) {
|
|
|
1795
1847
|
});
|
|
1796
1848
|
}
|
|
1797
1849
|
function json(res, status, body) {
|
|
1798
|
-
const payload = JSON.stringify(body);
|
|
1850
|
+
const payload = JSON.stringify(redactValue(body, { secrets: environmentSecrets(process.env) }));
|
|
1799
1851
|
res.writeHead(status, {
|
|
1800
1852
|
"content-type": "application/json",
|
|
1801
1853
|
"access-control-allow-origin": "*",
|
|
@@ -1811,6 +1863,7 @@ async function createSession(deps, opts) {
|
|
|
1811
1863
|
mode,
|
|
1812
1864
|
chatAgent,
|
|
1813
1865
|
running,
|
|
1866
|
+
runState: runLifecycle.state,
|
|
1814
1867
|
ready: true,
|
|
1815
1868
|
thinkingLevel: session.getThinkingLevel() ?? null,
|
|
1816
1869
|
supportedThinkingLevels: getSupportedThinkingLevels(st.provider, st.model),
|
|
@@ -1883,6 +1936,7 @@ async function createSession(deps, opts) {
|
|
|
1883
1936
|
mode,
|
|
1884
1937
|
chatAgent,
|
|
1885
1938
|
running,
|
|
1939
|
+
runState: runLifecycle.state,
|
|
1886
1940
|
thinkingLevel: session.getThinkingLevel() ?? null,
|
|
1887
1941
|
supportedThinkingLevels: getSupportedThinkingLevels(st.provider, st.model),
|
|
1888
1942
|
supportsVideo: getModel(st.model)?.supportsVideo ?? false,
|
|
@@ -2378,6 +2432,13 @@ async function createSession(deps, opts) {
|
|
|
2378
2432
|
json(res, 400, { error: "empty prompt" });
|
|
2379
2433
|
return;
|
|
2380
2434
|
}
|
|
2435
|
+
if (runLifecycle.running && runLifecycle.isCancellationRequested(runLifecycle.generation)) {
|
|
2436
|
+
json(res, 409, {
|
|
2437
|
+
error: runLifecycle.state === "cancelling" ? "run_cancelling" : "cancel_failed",
|
|
2438
|
+
runState: runLifecycle.state,
|
|
2439
|
+
});
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2381
2442
|
if (running || autopilotActive) {
|
|
2382
2443
|
// Queue prompts as mid-run steering (mirrors the CLI). Also queue while
|
|
2383
2444
|
// an autopilot cycle is active but between injected runs (build idle,
|
|
@@ -2866,26 +2927,50 @@ async function createSession(deps, opts) {
|
|
|
2866
2927
|
return;
|
|
2867
2928
|
}
|
|
2868
2929
|
if (method === "POST" && url === "/cancel") {
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2930
|
+
void (async () => {
|
|
2931
|
+
// Even between task runs, cancellation stops the sweep. Active provider
|
|
2932
|
+
// ownership invokes the full abort hook exactly once through lifecycle.
|
|
2933
|
+
taskRunAll = false;
|
|
2934
|
+
autopilotCancelled = true;
|
|
2935
|
+
if (!runLifecycle.running) {
|
|
2936
|
+
kenAutoAbort.abort();
|
|
2937
|
+
kenAutoAbort = new AbortController();
|
|
2938
|
+
kenAutoSession?.setSignal(kenAutoAbort.signal);
|
|
2939
|
+
}
|
|
2940
|
+
const generation = runLifecycle.generation;
|
|
2941
|
+
if (!pendingCancelDrain || pendingCancelDrain.generation !== generation) {
|
|
2942
|
+
pendingCancelDrain = { generation, text: session.drainQueue() };
|
|
2943
|
+
broadcast("queued", { count: 0 });
|
|
2944
|
+
}
|
|
2945
|
+
const result = await runLifecycle.cancel(CANCEL_TIMEOUT_MS);
|
|
2946
|
+
const drained = pendingCancelDrain.text;
|
|
2947
|
+
if (result.status === "failed") {
|
|
2948
|
+
broadcast("cancel_failed", {
|
|
2949
|
+
error: "cancel_failed",
|
|
2950
|
+
reason: result.reason,
|
|
2951
|
+
runState: runLifecycle.state,
|
|
2952
|
+
});
|
|
2953
|
+
json(res, 504, {
|
|
2954
|
+
error: "cancel_failed",
|
|
2955
|
+
reason: result.reason,
|
|
2956
|
+
runState: runLifecycle.state,
|
|
2957
|
+
drained,
|
|
2958
|
+
});
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2961
|
+
json(res, 200, {
|
|
2962
|
+
cancelled: result.status === "cancelled",
|
|
2963
|
+
runState: runLifecycle.state,
|
|
2964
|
+
drained,
|
|
2965
|
+
});
|
|
2966
|
+
})().catch((error) => {
|
|
2967
|
+
broadcast("cancel_failed", { error: "cancel_failed", runState: runLifecycle.state });
|
|
2968
|
+
json(res, 500, {
|
|
2969
|
+
error: "cancel_failed",
|
|
2970
|
+
message: error instanceof Error ? error.message : String(error),
|
|
2971
|
+
runState: runLifecycle.state,
|
|
2972
|
+
});
|
|
2973
|
+
});
|
|
2889
2974
|
return;
|
|
2890
2975
|
}
|
|
2891
2976
|
if (method === "POST" && url === "/new-session") {
|