@kenkaiiii/ggcoder 5.11.0 → 5.13.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/README.md +25 -12
- package/dist/app-sidecar.js +69 -6
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +60 -11
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-subagent-policy.test.d.ts +2 -0
- package/dist/core/agent-session-subagent-policy.test.d.ts.map +1 -0
- package/dist/core/agent-session-subagent-policy.test.js +36 -0
- package/dist/core/agent-session-subagent-policy.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +8 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +26 -16
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-providers.js +5 -5
- package/dist/core/auth-providers.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/mcp/store.d.ts +2 -2
- package/dist/core/radio.d.ts +3 -0
- package/dist/core/radio.d.ts.map +1 -1
- package/dist/core/radio.js +54 -8
- package/dist/core/radio.js.map +1 -1
- package/dist/core/radio.test.d.ts +2 -0
- package/dist/core/radio.test.d.ts.map +1 -0
- package/dist/core/radio.test.js +45 -0
- package/dist/core/radio.test.js.map +1 -0
- package/dist/core/subagent-manager.d.ts +73 -0
- package/dist/core/subagent-manager.d.ts.map +1 -0
- package/dist/core/subagent-manager.js +408 -0
- package/dist/core/subagent-manager.js.map +1 -0
- package/dist/core/subagent-manager.test.d.ts +2 -0
- package/dist/core/subagent-manager.test.d.ts.map +1 -0
- package/dist/core/subagent-manager.test.js +86 -0
- package/dist/core/subagent-manager.test.js.map +1 -0
- package/dist/core/subagent-policy.d.ts +4 -0
- package/dist/core/subagent-policy.d.ts.map +1 -0
- package/dist/core/subagent-policy.js +19 -0
- package/dist/core/subagent-policy.js.map +1 -0
- package/dist/modes/subagent-worker-mode.d.ts +14 -0
- package/dist/modes/subagent-worker-mode.d.ts.map +1 -0
- package/dist/modes/subagent-worker-mode.js +185 -0
- package/dist/modes/subagent-worker-mode.js.map +1 -0
- package/dist/tools/index.d.ts +10 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +17 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/subagent-control.d.ts +6 -0
- package/dist/tools/subagent-control.d.ts.map +1 -0
- package/dist/tools/subagent-control.js +95 -0
- package/dist/tools/subagent-control.js.map +1 -0
- package/dist/tools/subagent-control.test.d.ts +2 -0
- package/dist/tools/subagent-control.test.d.ts.map +1 -0
- package/dist/tools/subagent-control.test.js +40 -0
- package/dist/tools/subagent-control.test.js.map +1 -0
- package/dist/tools/subagent-shared.d.ts +24 -0
- package/dist/tools/subagent-shared.d.ts.map +1 -0
- package/dist/tools/subagent-shared.js +53 -0
- package/dist/tools/subagent-shared.js.map +1 -0
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +21 -64
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagent.test.js +16 -0
- package/dist/tools/subagent.test.js.map +1 -1
- package/dist/ui/App.d.ts +2 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +62 -2
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/hooks/useModeState.d.ts +4 -2
- package/dist/ui/hooks/useModeState.d.ts.map +1 -1
- package/dist/ui/hooks/useModeState.js +14 -3
- package/dist/ui/hooks/useModeState.js.map +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +13 -5
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/render.d.ts +14 -0
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +2 -0
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/render.test.js +17 -0
- package/dist/ui/render.test.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -150,23 +150,36 @@ Plus built-in workflows that ship with the binary:
|
|
|
150
150
|
|
|
151
151
|
GG Coder comes with a focused set of tools. Each one is small, well-described, and earns its place in the prompt.
|
|
152
152
|
|
|
153
|
-
| Tool
|
|
154
|
-
|
|
155
|
-
| `bash`
|
|
156
|
-
| `read`
|
|
157
|
-
| `write`
|
|
158
|
-
| `edit`
|
|
159
|
-
| `grep`
|
|
160
|
-
| `find`
|
|
161
|
-
| `ls`
|
|
162
|
-
| `web_fetch`
|
|
163
|
-
| `screenshot`
|
|
164
|
-
| `subagent`
|
|
153
|
+
| Tool | What it does |
|
|
154
|
+
| --------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
155
|
+
| `bash` | Run shell commands |
|
|
156
|
+
| `read` | Read file contents |
|
|
157
|
+
| `write` | Write files |
|
|
158
|
+
| `edit` | Surgical string replacements |
|
|
159
|
+
| `grep` | Search file contents (regex) |
|
|
160
|
+
| `find` | Find files by glob pattern |
|
|
161
|
+
| `ls` | List directory contents |
|
|
162
|
+
| `web_fetch` | Fetch URL content |
|
|
163
|
+
| `screenshot` | Open a URL / dev server in a headless browser and capture a PNG so the agent can see the rendered page |
|
|
164
|
+
| `subagent` | Run one blocking, isolated child task (backward-compatible) |
|
|
165
|
+
| `spawn_agent` / `wait_agent` | Launch persistent child turns concurrently and collect results |
|
|
166
|
+
| `send_message` / `followup_task` | Steer a running child or reuse an idle child's context |
|
|
167
|
+
| `list_agents` / `interrupt_agent` | Inspect or interrupt persistent children |
|
|
165
168
|
|
|
166
169
|
The `screenshot` tool needs the optional `playwright` dependency plus a one-time `npx playwright install chromium`. Without it the tool returns an install hint instead of failing the turn. Captured images render inline in graphics-capable terminals (kitty, Ghostty, WezTerm, iTerm2); other terminals show a text line.
|
|
167
170
|
|
|
168
171
|
Plus the [Grep MCP](https://grep.dev) for searching across 1M+ public GitHub repos. Add your own MCPs in settings if you need more — but start lean.
|
|
169
172
|
|
|
173
|
+
### Async subagent lifecycle
|
|
174
|
+
|
|
175
|
+
`subagent` remains blocking. The async suite launches persistent NDJSON worker processes, so a parent can start up to four active child turns, keep working, steer them, and wait for any or all results. Up to eight idle workers remain available for follow-up; bounded snapshots retain the latest 20 agents.
|
|
176
|
+
|
|
177
|
+
Only GPT-5.6 Sol/Terra at **Ultra** delegates proactively. Lower Sol/Terra levels use async agents only when the user or project/skill instructions request delegation; other models receive no proactive policy.
|
|
178
|
+
|
|
179
|
+
Children share the parent working directory, not isolated worktrees. Parallel writes must target disjoint files or subsystems. Async fan-out is one level deep, child output is bounded, idle workers reap after 10 minutes, and workers are not resumable after a CLI/app restart.
|
|
180
|
+
|
|
181
|
+
Parent cancellation interrupts active children. Session disposal shuts down every worker process alongside background commands, LSP servers, and MCP connections.
|
|
182
|
+
|
|
170
183
|
---
|
|
171
184
|
|
|
172
185
|
## 🪄 Custom commands
|
package/dist/app-sidecar.js
CHANGED
|
@@ -20,6 +20,7 @@ import { randomUUID } from "node:crypto";
|
|
|
20
20
|
import { parseArgs } from "node:util";
|
|
21
21
|
import { formatError } from "@kenkaiiii/gg-ai";
|
|
22
22
|
import { runJsonMode } from "./modes/json-mode.js";
|
|
23
|
+
import { runSubagentWorkerMode } from "./modes/subagent-worker-mode.js";
|
|
23
24
|
import { AgentSession } from "./core/agent-session.js";
|
|
24
25
|
import { buildKenSystemPrompt, buildKenAutopilotSystemPrompt } from "./core/ken-prompt.js";
|
|
25
26
|
import { buildKenDigest, buildKenAutopilotContext, buildKenAutopilotPlanContext, } from "./core/ken-context.js";
|
|
@@ -46,7 +47,7 @@ import { loadCustomCommands } from "./core/custom-commands.js";
|
|
|
46
47
|
import { discoverProjects, listRecentSessions } from "./core/project-discovery.js";
|
|
47
48
|
import { loadTasksSync, saveTasksSync, pruneDoneTasksSync, getNextPendingTask, markTaskInProgress, } from "./core/tasks-store.js";
|
|
48
49
|
import { initLogger, log } from "./core/logger.js";
|
|
49
|
-
import { RADIO_STATIONS, getCurrentStation, playRadio, stopRadio } from "./core/radio.js";
|
|
50
|
+
import { RADIO_STATIONS, getCurrentStation, getRadioVolume, playRadio, setRadioVolume, stopRadio, } from "./core/radio.js";
|
|
50
51
|
import { enrichProcessPath } from "./core/shell-path.js";
|
|
51
52
|
import { downscaleForPreview, validateVisionImage } from "./utils/image.js";
|
|
52
53
|
import { startServeMode } from "./modes/serve-mode.js";
|
|
@@ -457,6 +458,11 @@ function daemonJson(res, status, body) {
|
|
|
457
458
|
res.end(JSON.stringify(body));
|
|
458
459
|
}
|
|
459
460
|
async function main() {
|
|
461
|
+
// Hidden persistent-worker dispatch must win before strict JSON/server parsing.
|
|
462
|
+
if (process.argv.includes("--subagent-worker")) {
|
|
463
|
+
await runSubagentWorkerMode();
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
460
466
|
// Sub-agent JSON-mode dispatch must win before any sidecar/server setup.
|
|
461
467
|
if (await runJsonModeIfRequested())
|
|
462
468
|
return;
|
|
@@ -681,16 +687,40 @@ async function main() {
|
|
|
681
687
|
process.stdout.write(`GG_APP_LISTENING ${addr.port}\n`);
|
|
682
688
|
log("INFO", "app-sidecar", "daemon listening", { port: String(addr.port), host });
|
|
683
689
|
});
|
|
684
|
-
const
|
|
690
|
+
const shellPid = process.ppid;
|
|
691
|
+
let shuttingDown = false;
|
|
692
|
+
async function shutdown() {
|
|
693
|
+
if (shuttingDown)
|
|
694
|
+
return;
|
|
695
|
+
shuttingDown = true;
|
|
696
|
+
clearInterval(parentWatch);
|
|
685
697
|
// Radio playback is app-wide (one stream across all windows), so it stops
|
|
686
698
|
// at the daemon level, not per session.
|
|
687
699
|
stopRadio();
|
|
688
700
|
await Promise.all([...sessions.values()].map((c) => c.dispose().catch(() => { })));
|
|
689
701
|
server.close();
|
|
690
702
|
process.exit(0);
|
|
691
|
-
}
|
|
703
|
+
}
|
|
692
704
|
process.on("SIGINT", () => void shutdown());
|
|
693
705
|
process.on("SIGTERM", () => void shutdown());
|
|
706
|
+
process.once("exit", stopRadio);
|
|
707
|
+
// Tauri can disappear without delivering a signal (force-quit, dev runner
|
|
708
|
+
// teardown, crash). Detect reparenting or a dead shell so the daemon and its
|
|
709
|
+
// radio player do not survive as audible orphans.
|
|
710
|
+
const parentWatch = setInterval(() => {
|
|
711
|
+
let parentAlive = process.ppid === shellPid;
|
|
712
|
+
if (parentAlive && shellPid > 1) {
|
|
713
|
+
try {
|
|
714
|
+
process.kill(shellPid, 0);
|
|
715
|
+
}
|
|
716
|
+
catch (error) {
|
|
717
|
+
parentAlive = error.code === "EPERM";
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (!parentAlive)
|
|
721
|
+
void shutdown();
|
|
722
|
+
}, 1_000);
|
|
723
|
+
parentWatch.unref?.();
|
|
694
724
|
}
|
|
695
725
|
/** Ken's read-only tool allow-list. Excludes every mutating tool (write/edit/
|
|
696
726
|
* bash/tasks/subagent/generate_image/enter_plan/exit_plan/task_*) so the mentor
|
|
@@ -1039,6 +1069,7 @@ async function createSession(deps, opts) {
|
|
|
1039
1069
|
});
|
|
1040
1070
|
session.eventBus.on("model_change", (d) => broadcast("model_change", d));
|
|
1041
1071
|
session.eventBus.on("hook", (d) => broadcast("hook", d));
|
|
1072
|
+
session.eventBus.on("subagent_state", (d) => broadcast("subagent_state", d));
|
|
1042
1073
|
session.eventBus.on("compaction_start", (d) => broadcast("compaction_start", d));
|
|
1043
1074
|
session.eventBus.on("compaction_end", (d) => broadcast("compaction_end", d));
|
|
1044
1075
|
let running = false;
|
|
@@ -2137,12 +2168,17 @@ async function createSession(deps, opts) {
|
|
|
2137
2168
|
}
|
|
2138
2169
|
// Assistant tool_call blocks: detect sub-agent delegations.
|
|
2139
2170
|
if (msg.role === "assistant" && typeof msg.content !== "string") {
|
|
2140
|
-
const subagentCalls = msg.content.filter((c) => c.type === "tool_call" && c.name === "subagent");
|
|
2171
|
+
const subagentCalls = msg.content.filter((c) => c.type === "tool_call" && (c.name === "subagent" || c.name === "spawn_agent"));
|
|
2141
2172
|
if (subagentCalls.length > 0) {
|
|
2142
2173
|
const agents = subagentCalls.map((c) => {
|
|
2143
2174
|
const result = toolResultMap.get(c.id);
|
|
2144
2175
|
return {
|
|
2145
|
-
agentName:
|
|
2176
|
+
agentName: c.name === "spawn_agent" && typeof c.args?.task_name === "string"
|
|
2177
|
+
? c.args.task_name
|
|
2178
|
+
: typeof c.args?.agent === "string"
|
|
2179
|
+
? c.args.agent
|
|
2180
|
+
: undefined,
|
|
2181
|
+
// Async workers are intentionally non-resumable; restored rows are historical.
|
|
2146
2182
|
status: result?.isError ? "error" : "done",
|
|
2147
2183
|
toolUseCount: 0,
|
|
2148
2184
|
};
|
|
@@ -2427,7 +2463,34 @@ async function createSession(deps, opts) {
|
|
|
2427
2463
|
// duplicate audio across windows (the original per-window goal), now for
|
|
2428
2464
|
// free. (To restore per-window radio, key playback by sessionId.)
|
|
2429
2465
|
if (method === "GET" && url === "/radio") {
|
|
2430
|
-
json(res, 200, {
|
|
2466
|
+
json(res, 200, {
|
|
2467
|
+
stations: RADIO_STATIONS,
|
|
2468
|
+
current: getCurrentStation(),
|
|
2469
|
+
volume: getRadioVolume(),
|
|
2470
|
+
});
|
|
2471
|
+
return;
|
|
2472
|
+
}
|
|
2473
|
+
if (method === "POST" && url === "/radio/volume") {
|
|
2474
|
+
void readBody(req).then((raw) => {
|
|
2475
|
+
let volume;
|
|
2476
|
+
try {
|
|
2477
|
+
volume = Number(JSON.parse(raw).volume);
|
|
2478
|
+
}
|
|
2479
|
+
catch {
|
|
2480
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
if (!Number.isFinite(volume)) {
|
|
2484
|
+
json(res, 400, { error: "volume must be a number" });
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
const result = setRadioVolume(volume);
|
|
2488
|
+
if (!result.ok) {
|
|
2489
|
+
json(res, 400, { error: result.error ?? "Radio volume failed to update." });
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
json(res, 200, { current: getCurrentStation(), volume: getRadioVolume() });
|
|
2493
|
+
});
|
|
2431
2494
|
return;
|
|
2432
2495
|
}
|
|
2433
2496
|
if (method === "POST" && url === "/radio") {
|