@kenkaiiii/ggcoder 5.67.0 → 5.68.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 +121 -3
- package/dist/app-sidecar.js.map +1 -1
- package/dist/core/agent-session.d.ts +49 -3
- package/dist/core/agent-session.js +305 -22
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/ask-user.d.ts +90 -8
- package/dist/core/ask-user.js +124 -13
- package/dist/core/ask-user.js.map +1 -1
- package/dist/core/autopilot-verdict.d.ts +5 -1
- package/dist/core/autopilot-verdict.js +30 -13
- package/dist/core/autopilot-verdict.js.map +1 -1
- package/dist/core/compaction/plan-step-policy.d.ts +46 -0
- package/dist/core/compaction/plan-step-policy.js +57 -0
- package/dist/core/compaction/plan-step-policy.js.map +1 -0
- package/dist/core/destructive-git-guard.d.ts +63 -0
- package/dist/core/destructive-git-guard.js +831 -0
- package/dist/core/destructive-git-guard.js.map +1 -0
- package/dist/core/event-bus.d.ts +3 -0
- package/dist/core/event-bus.js +5 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/keep-awake.d.ts +88 -0
- package/dist/core/keep-awake.js +251 -0
- package/dist/core/keep-awake.js.map +1 -0
- package/dist/core/ken-context.d.ts +7 -5
- package/dist/core/ken-context.js +106 -16
- package/dist/core/ken-context.js.map +1 -1
- package/dist/core/ken-prompt.js +24 -21
- package/dist/core/ken-prompt.js.map +1 -1
- package/dist/core/package-threats.d.ts +18 -0
- package/dist/core/package-threats.js +168 -0
- package/dist/core/package-threats.js.map +1 -0
- package/dist/core/session-manager.d.ts +13 -1
- package/dist/core/session-manager.js +38 -18
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-summary-index.d.ts +37 -0
- package/dist/core/session-summary-index.js +172 -0
- package/dist/core/session-summary-index.js.map +1 -0
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.js +8 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/shell-threats-popular-packages.d.ts +11 -0
- package/dist/core/shell-threats-popular-packages.js +675 -0
- package/dist/core/shell-threats-popular-packages.js.map +1 -0
- package/dist/core/shell-threats.d.ts +8 -0
- package/dist/core/shell-threats.js +186 -0
- package/dist/core/shell-threats.js.map +1 -0
- package/dist/core/stream-rules.d.ts +30 -0
- package/dist/core/stream-rules.js +151 -0
- package/dist/core/stream-rules.js.map +1 -0
- package/dist/motion-agent/motion-check-tool.js +60 -29
- package/dist/motion-agent/motion-check-tool.js.map +1 -1
- package/dist/motion-agent/motion-review.d.ts +5 -2
- package/dist/motion-agent/motion-review.js +7 -4
- package/dist/motion-agent/motion-review.js.map +1 -1
- package/dist/tools/ask-user.js +3 -3
- package/dist/tools/ask-user.js.map +1 -1
- package/dist/tools/bash.js +37 -0
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/task-send.d.ts +1 -1
- package/dist/tools/task-send.js +15 -1
- package/dist/tools/task-send.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.js +1 -0
- package/dist/ui/components/ActivityIndicator.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +1 -1
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/package.json +4 -4
package/dist/app-sidecar.js
CHANGED
|
@@ -68,6 +68,7 @@ import { listSidecarSessions } from "./app-sidecar-sessions.js";
|
|
|
68
68
|
import { loadTasksSync, saveTasksSync, pruneDoneTasksSync, getNextPendingTask, markTaskInProgress, } from "./core/tasks-store.js";
|
|
69
69
|
import { initLogger, log } from "./core/logger.js";
|
|
70
70
|
import { installTerminationHandlers } from "./core/shutdown.js";
|
|
71
|
+
import { KeepAwake } from "./core/keep-awake.js";
|
|
71
72
|
import { RADIO_STATIONS, getCurrentStation, getRadioVolume, playRadio, setRadioVolume, stopRadio, } from "./core/radio.js";
|
|
72
73
|
import { enrichProcessPath } from "./core/shell-path.js";
|
|
73
74
|
import { downscaleForPreview, shrinkToFit, validateVisionImage } from "./utils/image.js";
|
|
@@ -75,13 +76,16 @@ import { startServeMode } from "./modes/serve-mode.js";
|
|
|
75
76
|
import { installSteroids, probeSteroids } from "./core/steroids.js";
|
|
76
77
|
import { loadTelegramConfig, saveTelegramConfig, verifyBotToken } from "./core/telegram-config.js";
|
|
77
78
|
import { loadServers, addServer, removeServer, getServer, parseMcpAddCommand, MCPClientManager, McpOAuthStore, createElicitationBridge, } from "./core/mcp/index.js";
|
|
78
|
-
import { createAskUserBridge } from "./core/ask-user.js";
|
|
79
|
+
import { askSoftDeadlineMs, createAskUserBridge, deliverLateAnswer, } from "./core/ask-user.js";
|
|
79
80
|
import { createAskUserTool } from "./tools/ask-user.js";
|
|
80
81
|
import { buildSnapshot, levelForXp, rankForLevel } from "./core/progress/ranks.js";
|
|
81
82
|
import { loadProgress, peekProgress, updateProgress } from "./core/progress/store.js";
|
|
82
83
|
import { awardPrompt, awardCommits } from "./core/progress/engine.js";
|
|
83
84
|
import { detectNewCommits, repoKey } from "./core/progress/git-xp.js";
|
|
84
85
|
import { rebuildFromSessions } from "./core/progress/rebuild.js";
|
|
86
|
+
/** App-wide idle-sleep guard: every window's runs share one OS assertion. The
|
|
87
|
+
* `keepAwake` setting is applied at daemon start and live via /keep-awake. */
|
|
88
|
+
const keepAwake = new KeepAwake();
|
|
85
89
|
const AUTOMATION_PROVENANCE = {
|
|
86
90
|
source: "runtime",
|
|
87
91
|
kind: "automation",
|
|
@@ -568,6 +572,8 @@ async function main() {
|
|
|
568
572
|
// ~/.gg/debug.log (initLogger truncates on each start).
|
|
569
573
|
const sidecarLog = path.join(paths.agentDir, "gg-app-sidecar.log");
|
|
570
574
|
initLogger(sidecarLog);
|
|
575
|
+
// Apply the saved keepAwake setting before any run can acquire the guard.
|
|
576
|
+
keepAwake.setEnabled((await new SettingsManager(paths.settingsFile).load()).keepAwake);
|
|
571
577
|
// The desktop sidecar previously omitted the stream diagnostic hook used by
|
|
572
578
|
// the CLI, leaving device-specific provider stalls impossible to distinguish from
|
|
573
579
|
// event-loop starvation or a broken streaming network path. Keep routine
|
|
@@ -906,6 +912,40 @@ async function main() {
|
|
|
906
912
|
daemonJson(res, 200, { ok: true });
|
|
907
913
|
return;
|
|
908
914
|
}
|
|
915
|
+
// Keep-awake is app-wide (one OS assertion for every window), so its
|
|
916
|
+
// setting lives at the daemon level and applies live to in-flight runs.
|
|
917
|
+
if (method === "GET" && url === "/keep-awake") {
|
|
918
|
+
daemonJson(res, 200, { enabled: keepAwake.isEnabled });
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (method === "POST" && url === "/keep-awake") {
|
|
922
|
+
void daemonReadBody(req, res).then(async (raw) => {
|
|
923
|
+
if (raw === null)
|
|
924
|
+
return;
|
|
925
|
+
let enabled;
|
|
926
|
+
try {
|
|
927
|
+
enabled = JSON.parse(raw).enabled;
|
|
928
|
+
}
|
|
929
|
+
catch {
|
|
930
|
+
enabled = undefined;
|
|
931
|
+
}
|
|
932
|
+
if (typeof enabled !== "boolean") {
|
|
933
|
+
daemonJson(res, 400, { error: "enabled must be a boolean" });
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
keepAwake.setEnabled(enabled);
|
|
937
|
+
try {
|
|
938
|
+
const sm = new SettingsManager(paths.settingsFile);
|
|
939
|
+
await sm.load();
|
|
940
|
+
await sm.set("keepAwake", enabled);
|
|
941
|
+
}
|
|
942
|
+
catch (err) {
|
|
943
|
+
log("WARN", "app-sidecar", "failed to persist keepAwake", { err: String(err) });
|
|
944
|
+
}
|
|
945
|
+
daemonJson(res, 200, { enabled });
|
|
946
|
+
});
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
909
949
|
// Progress is daemon-level so the Home screen can paint before a project
|
|
910
950
|
// session exists; per-session callers still work through the same endpoint.
|
|
911
951
|
if (method === "GET" && url === "/progress") {
|
|
@@ -959,6 +999,8 @@ async function main() {
|
|
|
959
999
|
// Radio playback is app-wide (one stream across all windows), so it stops
|
|
960
1000
|
// at the daemon level, not per session.
|
|
961
1001
|
stopRadio();
|
|
1002
|
+
// Drop the idle-sleep assertion first: session teardown may hang.
|
|
1003
|
+
keepAwake.dispose();
|
|
962
1004
|
// Close the ~/.gg progress fs.watch handle (baseline #8 leak fix).
|
|
963
1005
|
progress.dispose();
|
|
964
1006
|
await Promise.all([...sessions.values()].map((c) => c.dispose().catch(() => { })));
|
|
@@ -1313,9 +1355,33 @@ async function createSession(deps, opts) {
|
|
|
1313
1355
|
// MCP bridge above: broadcast over SSE, resolved when the webview POSTs
|
|
1314
1356
|
// /ask/:id. Registered ONLY here — a TUI/headless/subagent run has nobody to
|
|
1315
1357
|
// answer, so the tool is absent there rather than hanging on a dead channel.
|
|
1358
|
+
//
|
|
1359
|
+
// Soft deadline ("async ask"): past it the tool returns "no answer yet —
|
|
1360
|
+
// proceed on your best guess" and the question STAYS OPEN. A later answer
|
|
1361
|
+
// rides the ordinary user queue: steering into a live run, or the next turn
|
|
1362
|
+
// when idle. The deadline is short when nobody is watching (autopilot, task
|
|
1363
|
+
// run-all, a scheduled prompt).
|
|
1364
|
+
let scheduledRunActive = false;
|
|
1316
1365
|
const asks = createAskUserBridge({
|
|
1317
1366
|
broadcast: (prompt) => broadcast("ask_user", prompt),
|
|
1318
|
-
|
|
1367
|
+
timeoutMs: () => askSoftDeadlineMs(autopilot || autopilotActive || taskRunAll || scheduledRunActive),
|
|
1368
|
+
onTimeout: (prompt) => {
|
|
1369
|
+
log("WARN", "app-sidecar", "ask_user deadline passed; agent proceeding", { id: prompt.id });
|
|
1370
|
+
broadcast("ask_user_deferred", { id: prompt.id });
|
|
1371
|
+
},
|
|
1372
|
+
onClosed: (ids) => broadcast("ask_user_closed", { ids }),
|
|
1373
|
+
onLateAnswer: (late) => {
|
|
1374
|
+
log("INFO", "app-sidecar", "ask_user late answer queued", { id: late.prompt.id });
|
|
1375
|
+
deliverLateAnswer(late, {
|
|
1376
|
+
queueMessage: (text) => session.queueMessage(text),
|
|
1377
|
+
isBusy: () => running || runClaim.active || autopilotActive,
|
|
1378
|
+
onQueued: () => broadcast("queued", {
|
|
1379
|
+
count: session.getQueuedCount(),
|
|
1380
|
+
messages: session.listQueuedMessages(),
|
|
1381
|
+
}),
|
|
1382
|
+
startIdleRun: () => void runStrandedQueue(),
|
|
1383
|
+
});
|
|
1384
|
+
},
|
|
1319
1385
|
});
|
|
1320
1386
|
const askUserTool = createAskUserTool(asks.park);
|
|
1321
1387
|
// The session file path to resume (passed by the daemon's POST /session);
|
|
@@ -1860,11 +1926,14 @@ async function createSession(deps, opts) {
|
|
|
1860
1926
|
if (!d.silent)
|
|
1861
1927
|
broadcast("retry", { reason: d.reason, attempt: d.attempt, delayMs: d.delayMs });
|
|
1862
1928
|
});
|
|
1929
|
+
session.eventBus.on("stream_rule_triggered", (d) => broadcast("stream_rule_triggered", { rules: d.rules, source: d.source, toolName: d.toolName }));
|
|
1863
1930
|
session.eventBus.on("max_turns", (d) => broadcast("max_turns", d));
|
|
1864
1931
|
// The agent consumed queued steering at a turn boundary. Re-broadcast as the
|
|
1865
1932
|
// usual `queued` depth update so the webview drops the pending affordance the
|
|
1866
1933
|
// moment the message lands in the loop, not at run_end.
|
|
1867
1934
|
session.eventBus.on("queue_drained", (d) => broadcast("queued", { count: d.count, messages: session.listQueuedMessages() }));
|
|
1935
|
+
// A fresh/loaded session must not receive late answers to the old one's questions.
|
|
1936
|
+
session.eventBus.on("session_start", () => asks.closeDeferred());
|
|
1868
1937
|
session.eventBus.on("tool_call_start", (d) => {
|
|
1869
1938
|
toolCallNames.set(d.toolCallId, d.name);
|
|
1870
1939
|
broadcast("tool_call_start", d);
|
|
@@ -1924,6 +1993,26 @@ async function createSession(deps, opts) {
|
|
|
1924
1993
|
session.eventBus.on("subagent_state", (d) => broadcast("subagent_state", d));
|
|
1925
1994
|
session.eventBus.on("compaction_start", (d) => broadcast("compaction_start", d));
|
|
1926
1995
|
session.eventBus.on("compaction_end", (d) => broadcast("compaction_end", d));
|
|
1996
|
+
// Keep the computer awake while this window's agent works: owned runs and
|
|
1997
|
+
// autopilot cycles (RunLifecycle state), Ken replies, and background
|
|
1998
|
+
// sub-agents that outlive their parent run. Released on settle (completed,
|
|
1999
|
+
// failed or cancelled) and on session dispose.
|
|
2000
|
+
let releaseRunAwake = null;
|
|
2001
|
+
let releaseSubagentAwake = null;
|
|
2002
|
+
const activeSubagents = new Set();
|
|
2003
|
+
session.eventBus.on("subagent_state", (d) => {
|
|
2004
|
+
if (d.state === "starting" || d.state === "running")
|
|
2005
|
+
activeSubagents.add(d.agent_id);
|
|
2006
|
+
else
|
|
2007
|
+
activeSubagents.delete(d.agent_id);
|
|
2008
|
+
if (activeSubagents.size > 0) {
|
|
2009
|
+
releaseSubagentAwake ??= keepAwake.acquire("subagent");
|
|
2010
|
+
}
|
|
2011
|
+
else {
|
|
2012
|
+
releaseSubagentAwake?.();
|
|
2013
|
+
releaseSubagentAwake = null;
|
|
2014
|
+
}
|
|
2015
|
+
});
|
|
1927
2016
|
let running = false;
|
|
1928
2017
|
// Closes the window between `/prompt` deciding to start a run and `runAgent`
|
|
1929
2018
|
// flipping `running` — that stretch awaits, so Node yields inside it. See
|
|
@@ -1931,6 +2020,13 @@ async function createSession(deps, opts) {
|
|
|
1931
2020
|
const runClaim = new RunClaim();
|
|
1932
2021
|
const runLifecycle = new RunLifecycle((runState) => {
|
|
1933
2022
|
running = runState !== "idle";
|
|
2023
|
+
if (running) {
|
|
2024
|
+
releaseRunAwake ??= keepAwake.acquire("run");
|
|
2025
|
+
}
|
|
2026
|
+
else {
|
|
2027
|
+
releaseRunAwake?.();
|
|
2028
|
+
releaseRunAwake = null;
|
|
2029
|
+
}
|
|
1934
2030
|
if (runState === "cancelling")
|
|
1935
2031
|
broadcast("run_cancelling", { runState });
|
|
1936
2032
|
},
|
|
@@ -3598,6 +3694,8 @@ async function createSession(deps, opts) {
|
|
|
3598
3694
|
// Claim the run NOW, synchronously. Everything below this line may
|
|
3599
3695
|
// yield, and `running` does not flip until runAgent begins.
|
|
3600
3696
|
claimedStart = runClaim.claim();
|
|
3697
|
+
// A scheduled prompt has nobody watching: short ask deadline.
|
|
3698
|
+
scheduledRunActive = meta?.scheduled === true;
|
|
3601
3699
|
json(res, 202, { accepted: true });
|
|
3602
3700
|
// Gate inputs captured around the run: whether this turn is a workflow
|
|
3603
3701
|
// slash command (attachment prompts skip slash expansion entirely), and
|
|
@@ -3696,8 +3794,10 @@ async function createSession(deps, opts) {
|
|
|
3696
3794
|
await runStrandedQueue();
|
|
3697
3795
|
})
|
|
3698
3796
|
.finally(() => {
|
|
3699
|
-
if (claimedStart)
|
|
3797
|
+
if (claimedStart) {
|
|
3798
|
+
scheduledRunActive = false;
|
|
3700
3799
|
runClaim.release();
|
|
3800
|
+
}
|
|
3701
3801
|
});
|
|
3702
3802
|
return;
|
|
3703
3803
|
}
|
|
@@ -3733,6 +3833,7 @@ async function createSession(deps, opts) {
|
|
|
3733
3833
|
}
|
|
3734
3834
|
json(res, 202, { accepted: true });
|
|
3735
3835
|
kenRunning = true;
|
|
3836
|
+
const releaseKenAwake = keepAwake.acquire("ken");
|
|
3736
3837
|
broadcast("ken_run_start", { text });
|
|
3737
3838
|
try {
|
|
3738
3839
|
const ken = await ensureKenSession();
|
|
@@ -3749,6 +3850,7 @@ async function createSession(deps, opts) {
|
|
|
3749
3850
|
broadcastError("ken_error", "ken run failed", err);
|
|
3750
3851
|
}
|
|
3751
3852
|
finally {
|
|
3853
|
+
releaseKenAwake();
|
|
3752
3854
|
kenRunning = false;
|
|
3753
3855
|
broadcast("ken_run_end", {});
|
|
3754
3856
|
const pending = pendingKenModel;
|
|
@@ -4226,6 +4328,18 @@ async function createSession(deps, opts) {
|
|
|
4226
4328
|
json(res, 200, payload);
|
|
4227
4329
|
return;
|
|
4228
4330
|
}
|
|
4331
|
+
if (method === "POST" && url === "/prewarm") {
|
|
4332
|
+
// Best-effort Anthropic cache prewarm on the composer's first keystroke.
|
|
4333
|
+
// Fire-and-forget: AgentSession.prewarm() applies its own gating (provider,
|
|
4334
|
+
// setting, history size, TTL, in-flight) and swallows request errors.
|
|
4335
|
+
if (!running) {
|
|
4336
|
+
void session.prewarm().catch((err) => {
|
|
4337
|
+
log("WARN", "app-sidecar", "prewarm failed", { err: String(err) });
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
json(res, 202, { accepted: !running });
|
|
4341
|
+
return;
|
|
4342
|
+
}
|
|
4229
4343
|
if (method === "POST" && url === "/cancel") {
|
|
4230
4344
|
void (async () => {
|
|
4231
4345
|
// Even between task runs, cancellation stops the sweep. Active provider
|
|
@@ -5082,6 +5196,10 @@ async function createSession(deps, opts) {
|
|
|
5082
5196
|
json(res, 404, { error: "not found" });
|
|
5083
5197
|
}
|
|
5084
5198
|
async function dispose() {
|
|
5199
|
+
releaseRunAwake?.();
|
|
5200
|
+
releaseRunAwake = null;
|
|
5201
|
+
releaseSubagentAwake?.();
|
|
5202
|
+
releaseSubagentAwake = null;
|
|
5085
5203
|
elicitations.cancelAll();
|
|
5086
5204
|
asks.cancelAll();
|
|
5087
5205
|
tasksPollStopped = true;
|