@kenkaiiii/ggcoder 5.13.3 → 5.14.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.
Files changed (89) hide show
  1. package/dist/app-sidecar.js +112 -47
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/chat-agents/general.d.ts +7 -0
  4. package/dist/chat-agents/general.d.ts.map +1 -0
  5. package/dist/chat-agents/general.js +16 -0
  6. package/dist/chat-agents/general.js.map +1 -0
  7. package/dist/chat-agents/general.test.d.ts +2 -0
  8. package/dist/chat-agents/general.test.d.ts.map +1 -0
  9. package/dist/chat-agents/general.test.js +47 -0
  10. package/dist/chat-agents/general.test.js.map +1 -0
  11. package/dist/chat-agents/index.d.ts +9 -0
  12. package/dist/chat-agents/index.d.ts.map +1 -0
  13. package/dist/chat-agents/index.js +99 -0
  14. package/dist/chat-agents/index.js.map +1 -0
  15. package/dist/chat-agents/memory.d.ts +53 -0
  16. package/dist/chat-agents/memory.d.ts.map +1 -0
  17. package/dist/chat-agents/memory.js +362 -0
  18. package/dist/chat-agents/memory.js.map +1 -0
  19. package/dist/chat-agents/memory.test.d.ts +2 -0
  20. package/dist/chat-agents/memory.test.d.ts.map +1 -0
  21. package/dist/chat-agents/memory.test.js +155 -0
  22. package/dist/chat-agents/memory.test.js.map +1 -0
  23. package/dist/chat-agents/research.d.ts +7 -0
  24. package/dist/chat-agents/research.d.ts.map +1 -0
  25. package/dist/chat-agents/research.js +18 -0
  26. package/dist/chat-agents/research.js.map +1 -0
  27. package/dist/chat-agents/shared.d.ts +9 -0
  28. package/dist/chat-agents/shared.d.ts.map +1 -0
  29. package/dist/chat-agents/shared.js +44 -0
  30. package/dist/chat-agents/shared.js.map +1 -0
  31. package/dist/chat-agents/specialists.test.d.ts +2 -0
  32. package/dist/chat-agents/specialists.test.d.ts.map +1 -0
  33. package/dist/chat-agents/specialists.test.js +63 -0
  34. package/dist/chat-agents/specialists.test.js.map +1 -0
  35. package/dist/chat-agents/therapist.d.ts +7 -0
  36. package/dist/chat-agents/therapist.d.ts.map +1 -0
  37. package/dist/chat-agents/therapist.js +20 -0
  38. package/dist/chat-agents/therapist.js.map +1 -0
  39. package/dist/chat-agents/types.d.ts +4 -0
  40. package/dist/chat-agents/types.d.ts.map +1 -0
  41. package/dist/chat-agents/types.js +7 -0
  42. package/dist/chat-agents/types.js.map +1 -0
  43. package/dist/core/agent-session-memory-tail.test.d.ts +2 -0
  44. package/dist/core/agent-session-memory-tail.test.d.ts.map +1 -0
  45. package/dist/core/agent-session-memory-tail.test.js +122 -0
  46. package/dist/core/agent-session-memory-tail.test.js.map +1 -0
  47. package/dist/core/agent-session.d.ts +23 -0
  48. package/dist/core/agent-session.d.ts.map +1 -1
  49. package/dist/core/agent-session.js +112 -65
  50. package/dist/core/agent-session.js.map +1 -1
  51. package/dist/core/agents.d.ts +1 -1
  52. package/dist/core/agents.d.ts.map +1 -1
  53. package/dist/core/agents.js +4 -2
  54. package/dist/core/agents.js.map +1 -1
  55. package/dist/core/progress/rebuild.d.ts +3 -3
  56. package/dist/core/progress/rebuild.d.ts.map +1 -1
  57. package/dist/core/progress/rebuild.js +33 -27
  58. package/dist/core/progress/rebuild.js.map +1 -1
  59. package/dist/core/progress/rebuild.test.js +15 -0
  60. package/dist/core/progress/rebuild.test.js.map +1 -1
  61. package/dist/core/project-discovery.d.ts +1 -1
  62. package/dist/core/project-discovery.d.ts.map +1 -1
  63. package/dist/core/project-discovery.js +1 -2
  64. package/dist/core/project-discovery.js.map +1 -1
  65. package/dist/core/project-discovery.test.js +14 -1
  66. package/dist/core/project-discovery.test.js.map +1 -1
  67. package/dist/core/subagent-manager.d.ts.map +1 -1
  68. package/dist/core/subagent-manager.js +4 -0
  69. package/dist/core/subagent-manager.js.map +1 -1
  70. package/dist/core/subagent-manager.test.js +5 -1
  71. package/dist/core/subagent-manager.test.js.map +1 -1
  72. package/dist/tools/generate-image.d.ts.map +1 -1
  73. package/dist/tools/generate-image.js +10 -3
  74. package/dist/tools/generate-image.js.map +1 -1
  75. package/dist/tools/generate-image.test.js +17 -0
  76. package/dist/tools/generate-image.test.js.map +1 -1
  77. package/dist/tools/web-fetch.d.ts +1 -0
  78. package/dist/tools/web-fetch.d.ts.map +1 -1
  79. package/dist/tools/web-fetch.js +112 -35
  80. package/dist/tools/web-fetch.js.map +1 -1
  81. package/dist/tools/web-fetch.test.js +146 -1
  82. package/dist/tools/web-fetch.test.js.map +1 -1
  83. package/dist/tools/web-search.d.ts +10 -0
  84. package/dist/tools/web-search.d.ts.map +1 -1
  85. package/dist/tools/web-search.js +236 -28
  86. package/dist/tools/web-search.js.map +1 -1
  87. package/dist/tools/web-search.test.js +77 -6
  88. package/dist/tools/web-search.test.js.map +1 -1
  89. package/package.json +4 -4
@@ -22,6 +22,8 @@ import { formatError } 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 { CHAT_AGENT_IDS, chatAgentSessionsDir, createChatAgent, parseChatAgentId, sessionsDirForChatAgent, } from "./chat-agents/index.js";
26
+ import { buildMemoryTools, MemoryStore } from "./chat-agents/memory.js";
25
27
  import { buildKenSystemPrompt, buildKenAutopilotSystemPrompt } from "./core/ken-prompt.js";
26
28
  import { buildKenDigest, buildKenAutopilotContext, buildKenAutopilotPlanContext, } from "./core/ken-context.js";
27
29
  import { parseAutopilotVerdict } from "./core/autopilot-verdict.js";
@@ -508,6 +510,13 @@ async function main() {
508
510
  // request to its window's session via the `x-gg-session` header (and the
509
511
  // `?session=` query for the SSE /events stream).
510
512
  const sessions = new Map();
513
+ const memoryStore = new MemoryStore({
514
+ onChange: ({ memories }) => {
515
+ for (const ctx of sessions.values()) {
516
+ ctx.broadcast("memory_change", { count: memories.length });
517
+ }
518
+ },
519
+ });
511
520
  // XP/rank progress — loaded once per daemon; awards fan out to every window.
512
521
  // Each frame is tagged `origin: true` only for the session that earned the
513
522
  // XP, so that window alone plays sounds/chips while the rest just re-render.
@@ -606,7 +615,7 @@ async function main() {
606
615
  return;
607
616
  }
608
617
  // ── Daemon-level routes (session lifecycle) ──────────────────────────
609
- // Create a session for a window: { cwd, sessionPath? } → { sessionId }.
618
+ // Create a session for a window: { mode?, cwd, sessionPath? } → { sessionId }.
610
619
  if (method === "POST" && url === "/session") {
611
620
  void daemonReadBody(req).then(async (raw) => {
612
621
  let body = {};
@@ -616,15 +625,22 @@ async function main() {
616
625
  catch {
617
626
  /* empty/invalid body → defaults below */
618
627
  }
628
+ const mode = body.mode === "chat" ? "chat" : "code";
629
+ const chatAgent = parseChatAgentId(body.chatAgent);
619
630
  const sessionCwd = typeof body.cwd === "string" && body.cwd
620
631
  ? body.cwd
621
632
  : (process.env.GG_APP_CWD ?? process.cwd());
622
633
  const sessionPath = typeof body.sessionPath === "string" && body.sessionPath ? body.sessionPath : undefined;
623
634
  const id = randomUUID();
624
635
  try {
625
- const ctx = await createSession({ auth, paths, progress }, { id, cwd: sessionCwd, sessionPath });
636
+ const ctx = await createSession({ auth, paths, progress, memoryStore }, { id, mode, chatAgent, cwd: sessionCwd, sessionPath });
626
637
  sessions.set(id, ctx);
627
- log("INFO", "app-sidecar", "session created", { id, cwd: sessionCwd });
638
+ log("INFO", "app-sidecar", "session created", {
639
+ id,
640
+ mode,
641
+ chatAgent,
642
+ cwd: sessionCwd,
643
+ });
628
644
  daemonJson(res, 200, { sessionId: id });
629
645
  }
630
646
  catch (err) {
@@ -775,8 +791,14 @@ function buildKenContext(buildSession, cwd, gitBranch, question, workflowCommand
775
791
  });
776
792
  }
777
793
  async function createProgressManager(agentDir, broadcastAll) {
778
- // Boot: recovery chain main → backup → one-time session-history rebuild → empty.
779
- let file = await loadProgress({ rebuild: () => rebuildFromSessions() });
794
+ // Boot: recovery chain main → backup → coding + chat session rebuild → empty.
795
+ const coderSessionsDir = path.join(agentDir, "sessions");
796
+ let file = await loadProgress({
797
+ rebuild: () => rebuildFromSessions([
798
+ coderSessionsDir,
799
+ ...CHAT_AGENT_IDS.map((agentId) => chatAgentSessionsDir(coderSessionsDir, agentId)),
800
+ ]),
801
+ });
780
802
  // Don't re-celebrate an old levelUp event on boot.
781
803
  let lastSeenNonce = file.lastEvent?.nonce ?? null;
782
804
  log("INFO", "app-sidecar", "progress loaded", {
@@ -864,8 +886,10 @@ async function createProgressManager(agentDir, broadcastAll) {
864
886
  * logger, PATH, shared auth file, and radio live at the daemon level.
865
887
  */
866
888
  async function createSession(deps, opts) {
867
- const { auth, progress } = deps;
889
+ const { auth, progress, memoryStore } = deps;
868
890
  const paths = deps.paths;
891
+ const mode = opts.mode;
892
+ const chatAgent = opts.chatAgent;
869
893
  const cwd = opts.cwd;
870
894
  // Base host for parsing request-URL query params (value is irrelevant to
871
895
  // parsing); the daemon owns the real listen host.
@@ -968,51 +992,51 @@ async function createSession(deps, opts) {
968
992
  // empty/unset starts a fresh session.
969
993
  const resumeSessionPath = opts.sessionPath;
970
994
  let abort = new AbortController();
971
- const session = new AgentSession({
995
+ const baseSessionOptions = {
972
996
  provider,
973
997
  model,
974
998
  cwd,
975
999
  thinkingLevel,
976
1000
  sessionId: resumeSessionPath,
977
1001
  signal: abort.signal,
978
- // The shell gates window readiness on the GG_APP_LISTENING handshake, which
979
- // can't fire until initialize() resolves. Connect MCP in the background so a
980
- // slow or hanging stdio server (e.g. a first-run `npx -y @playwright/mcp`
981
- // download) can't delay the sidecar past the webview's startup timeout
982
- // ("sidecar did not start in time"). Tools attach when the servers come up.
1002
+ // Keep MCP startup off the readiness path in both modes.
983
1003
  backgroundMcpConnect: true,
984
- // Plan mode: the agent's enter_plan/exit_plan tools drive these. We flip
985
- // session plan state (rebuilds the system prompt + enforces read-only
986
- // tools) and surface the transition to the webview.
987
- onEnterPlan: async (reason) => {
988
- await session.setPlanMode(true);
989
- broadcast("plan_enter", { reason: reason ?? "" });
990
- // Persist the plan-mode banner so a resumed session still shows it.
991
- void session.persistAppMarker("plan", { reason: reason ?? "" }).catch(() => { });
992
- },
993
- onExitPlan: async (planPath) => {
994
- await session.setPlanMode(false);
995
- // Surface the plan's path + markdown so the webview can show the review
996
- // modal (Accept / Feedback / Reject). Best-effort content read.
997
- let content;
998
- try {
999
- content = await fs.readFile(planPath, "utf-8");
1000
- }
1001
- catch {
1002
- content = "";
1003
- }
1004
- // Record the submitted plan so the autopilot gate can route this turn
1005
- // into a PLAN review instead of a stale work review (plan mode is
1006
- // already false here, so the gate's planMode check alone never catches
1007
- // a submission). setPendingPlan bumps planGeneration, which invalidates
1008
- // any in-flight Ken plan review racing a user action.
1009
- setPendingPlan(planPath, content);
1010
- broadcast("plan_exit", { planPath, content });
1011
- return "Plan submitted for user review. Wait for the user to approve, reject, or dismiss it before implementing.";
1012
- },
1013
- });
1004
+ };
1005
+ let session;
1006
+ if (mode === "chat") {
1007
+ session = createChatAgent(chatAgent, {
1008
+ ...baseSessionOptions,
1009
+ sessionsDir: paths.sessionsDir,
1010
+ additionalTools: buildMemoryTools(memoryStore),
1011
+ getSystemPromptTail: () => memoryStore.renderForPrompt(),
1012
+ });
1013
+ }
1014
+ else {
1015
+ session = new AgentSession({
1016
+ ...baseSessionOptions,
1017
+ // Plan mode belongs only to the coding agent.
1018
+ onEnterPlan: async (reason) => {
1019
+ await session.setPlanMode(true);
1020
+ broadcast("plan_enter", { reason: reason ?? "" });
1021
+ void session.persistAppMarker("plan", { reason: reason ?? "" }).catch(() => { });
1022
+ },
1023
+ onExitPlan: async (planPath) => {
1024
+ await session.setPlanMode(false);
1025
+ let content;
1026
+ try {
1027
+ content = await fs.readFile(planPath, "utf-8");
1028
+ }
1029
+ catch {
1030
+ content = "";
1031
+ }
1032
+ setPendingPlan(planPath, content);
1033
+ broadcast("plan_exit", { planPath, content });
1034
+ return "Plan submitted for user review. Wait for the user to approve, reject, or dismiss it before implementing.";
1035
+ },
1036
+ });
1037
+ }
1014
1038
  await session.initialize();
1015
- log("INFO", "app-sidecar", "session ready", { provider, model, cwd });
1039
+ log("INFO", "app-sidecar", "session ready", { provider, model, mode, chatAgent, cwd });
1016
1040
  // Footer extras (context window, git branch, background tasks). The git
1017
1041
  // branch is resolved once at startup and refreshed lazily; the context
1018
1042
  // window follows the active model.
@@ -1081,7 +1105,7 @@ async function createSession(deps, opts) {
1081
1105
  // gg-app.json on boot; flipped via POST /autopilot. When on, POST /prompt runs
1082
1106
  // runAutopilotCycle after the user's turn settles — Ken auto-reviews the work
1083
1107
  // and drives the review→prompt→review loop.
1084
- let autopilot = await loadAutopilot(cwd);
1108
+ let autopilot = mode === "code" && (await loadAutopilot(cwd));
1085
1109
  // True while an autopilot review is in flight (used to defer kenAuto model
1086
1110
  // switches, like kenRunning does for chat Ken, and to drive the spinner).
1087
1111
  let autopilotReviewing = false;
@@ -1758,6 +1782,8 @@ async function createSession(deps, opts) {
1758
1782
  const st = session.getState();
1759
1783
  json(res, 200, {
1760
1784
  ...st,
1785
+ mode,
1786
+ chatAgent,
1761
1787
  running,
1762
1788
  ready: true,
1763
1789
  thinkingLevel: session.getThinkingLevel() ?? null,
@@ -1773,6 +1799,26 @@ async function createSession(deps, opts) {
1773
1799
  json(res, 200, progress.snapshot());
1774
1800
  return;
1775
1801
  }
1802
+ if (method === "GET" && url === "/memories") {
1803
+ void memoryStore
1804
+ .snapshot()
1805
+ .then((snapshot) => json(res, 200, snapshot))
1806
+ .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
1807
+ return;
1808
+ }
1809
+ if (method === "DELETE" && url.startsWith("/memories/")) {
1810
+ const id = decodeURIComponent(url.slice("/memories/".length));
1811
+ if (!id) {
1812
+ json(res, 400, { error: "memory id is required" });
1813
+ return;
1814
+ }
1815
+ void memoryStore
1816
+ .forget(id)
1817
+ .then(() => memoryStore.snapshot())
1818
+ .then((snapshot) => json(res, 200, snapshot))
1819
+ .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
1820
+ return;
1821
+ }
1776
1822
  if (method === "GET" && (url === "/events" || url.startsWith("/events?"))) {
1777
1823
  res.writeHead(200, {
1778
1824
  "content-type": "text/event-stream",
@@ -1788,6 +1834,8 @@ async function createSession(deps, opts) {
1788
1834
  type: "ready",
1789
1835
  data: {
1790
1836
  ...st,
1837
+ mode,
1838
+ chatAgent,
1791
1839
  running,
1792
1840
  thinkingLevel: session.getThinkingLevel() ?? null,
1793
1841
  supportedThinkingLevels: getSupportedThinkingLevels(st.provider, st.model),
@@ -1903,7 +1951,11 @@ async function createSession(deps, opts) {
1903
1951
  json(res, 400, { error: "missing cwd query param" });
1904
1952
  return;
1905
1953
  }
1906
- void listRecentSessions(target, 5)
1954
+ const requestedAgent = new URL(url, `http://${host}`).searchParams.get("chatAgent");
1955
+ const sessionsDir = mode === "chat" || requestedAgent
1956
+ ? sessionsDirForChatAgent(paths.sessionsDir, requestedAgent ?? chatAgent)
1957
+ : paths.sessionsDir;
1958
+ void listRecentSessions(target, 5, sessionsDir)
1907
1959
  .then((sessions) => json(res, 200, { sessions }))
1908
1960
  .catch(() => json(res, 200, { sessions: [] }));
1909
1961
  return;
@@ -2210,9 +2262,12 @@ async function createSession(deps, opts) {
2210
2262
  return;
2211
2263
  }
2212
2264
  if (method === "GET" && url === "/commands") {
2265
+ if (mode === "chat") {
2266
+ json(res, 200, { commands: [] });
2267
+ return;
2268
+ }
2213
2269
  // Workflow commands with agent functionality: built-in prompt templates +
2214
- // the user's own `.gg/commands/*.md`. UI commands (model/quit/etc.) are
2215
- // handled webview-side and intentionally excluded.
2270
+ // the user's own `.gg/commands/*.md`.
2216
2271
  void (async () => {
2217
2272
  const builtins = PROMPT_COMMANDS.map((c) => ({
2218
2273
  name: c.name,
@@ -2350,6 +2405,10 @@ async function createSession(deps, opts) {
2350
2405
  // webview keeps the bubbles separate. The context digest is assembled fresh
2351
2406
  // from the BUILD session's transcript each turn (one-way mirror).
2352
2407
  if (method === "POST" && url === "/ken/prompt") {
2408
+ if (mode === "chat") {
2409
+ json(res, 404, { error: "Ken is not available in GG Chat." });
2410
+ return;
2411
+ }
2353
2412
  void readBody(req).then(async (raw) => {
2354
2413
  let text;
2355
2414
  try {
@@ -2405,6 +2464,10 @@ async function createSession(deps, opts) {
2405
2464
  return;
2406
2465
  }
2407
2466
  if (method === "POST" && url === "/autopilot") {
2467
+ if (mode === "chat") {
2468
+ json(res, 404, { error: "Autopilot is not available in GG Chat." });
2469
+ return;
2470
+ }
2408
2471
  void readBody(req).then(async (raw) => {
2409
2472
  let enabled;
2410
2473
  try {
@@ -3260,6 +3323,8 @@ async function createSession(deps, opts) {
3260
3323
  }
3261
3324
  return {
3262
3325
  id: opts.id,
3326
+ mode,
3327
+ chatAgent,
3263
3328
  cwd,
3264
3329
  sessionPath: opts.sessionPath,
3265
3330
  session,