@kenkaiiii/ggcoder 5.18.0 → 5.19.1

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 (93) hide show
  1. package/dist/app-sidecar.js +64 -9
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/cli/auth.d.ts.map +1 -1
  4. package/dist/cli/auth.js +5 -2
  5. package/dist/cli/auth.js.map +1 -1
  6. package/dist/cli/shared.d.ts.map +1 -1
  7. package/dist/cli/shared.js +2 -0
  8. package/dist/cli/shared.js.map +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +6 -1
  11. package/dist/cli.js.map +1 -1
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +1 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/config.test.js +7 -0
  16. package/dist/config.test.js.map +1 -1
  17. package/dist/core/agent-session-compaction.test.js +517 -9
  18. package/dist/core/agent-session-compaction.test.js.map +1 -1
  19. package/dist/core/agent-session-memory-tail.test.js +1 -1
  20. package/dist/core/agent-session-memory-tail.test.js.map +1 -1
  21. package/dist/core/agent-session-queue.test.js +1 -1
  22. package/dist/core/agent-session-queue.test.js.map +1 -1
  23. package/dist/core/agent-session-review-coverage.test.js +26 -0
  24. package/dist/core/agent-session-review-coverage.test.js.map +1 -1
  25. package/dist/core/agent-session-tool-result-policy.test.js +14 -1
  26. package/dist/core/agent-session-tool-result-policy.test.js.map +1 -1
  27. package/dist/core/agent-session.d.ts +32 -0
  28. package/dist/core/agent-session.d.ts.map +1 -1
  29. package/dist/core/agent-session.js +229 -42
  30. package/dist/core/agent-session.js.map +1 -1
  31. package/dist/core/auth-providers.d.ts.map +1 -1
  32. package/dist/core/auth-providers.js +14 -7
  33. package/dist/core/auth-providers.js.map +1 -1
  34. package/dist/core/compaction/active-context.d.ts +17 -0
  35. package/dist/core/compaction/active-context.d.ts.map +1 -0
  36. package/dist/core/compaction/active-context.js +20 -0
  37. package/dist/core/compaction/active-context.js.map +1 -0
  38. package/dist/core/compaction/active-context.test.d.ts +2 -0
  39. package/dist/core/compaction/active-context.test.d.ts.map +1 -0
  40. package/dist/core/compaction/active-context.test.js +36 -0
  41. package/dist/core/compaction/active-context.test.js.map +1 -0
  42. package/dist/core/compaction/compactor.d.ts +18 -17
  43. package/dist/core/compaction/compactor.d.ts.map +1 -1
  44. package/dist/core/compaction/compactor.js +54 -37
  45. package/dist/core/compaction/compactor.js.map +1 -1
  46. package/dist/core/compaction/compactor.test.js +101 -64
  47. package/dist/core/compaction/compactor.test.js.map +1 -1
  48. package/dist/core/compaction/tool-result-pruner.d.ts +37 -0
  49. package/dist/core/compaction/tool-result-pruner.d.ts.map +1 -0
  50. package/dist/core/compaction/tool-result-pruner.js +103 -0
  51. package/dist/core/compaction/tool-result-pruner.js.map +1 -0
  52. package/dist/core/compaction/tool-result-pruner.test.d.ts +2 -0
  53. package/dist/core/compaction/tool-result-pruner.test.d.ts.map +1 -0
  54. package/dist/core/compaction/tool-result-pruner.test.js +148 -0
  55. package/dist/core/compaction/tool-result-pruner.test.js.map +1 -0
  56. package/dist/core/ideal-review.d.ts +6 -0
  57. package/dist/core/ideal-review.d.ts.map +1 -1
  58. package/dist/core/ideal-review.js +14 -0
  59. package/dist/core/ideal-review.js.map +1 -1
  60. package/dist/core/ideal-review.test.js +10 -1
  61. package/dist/core/ideal-review.test.js.map +1 -1
  62. package/dist/core/resolve-start.test.js +1 -0
  63. package/dist/core/resolve-start.test.js.map +1 -1
  64. package/dist/core/settings-manager.d.ts +1 -0
  65. package/dist/core/settings-manager.d.ts.map +1 -1
  66. package/dist/core/settings-manager.js +1 -0
  67. package/dist/core/settings-manager.js.map +1 -1
  68. package/dist/core/subagent-manager.test.js +7 -3
  69. package/dist/core/subagent-manager.test.js.map +1 -1
  70. package/dist/ui/App.d.ts.map +1 -1
  71. package/dist/ui/App.js +3 -16
  72. package/dist/ui/App.js.map +1 -1
  73. package/dist/ui/components/ModelSelector.d.ts.map +1 -1
  74. package/dist/ui/components/ModelSelector.js +1 -0
  75. package/dist/ui/components/ModelSelector.js.map +1 -1
  76. package/dist/ui/hooks/useAgentLoop.d.ts +2 -4
  77. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  78. package/dist/ui/hooks/useAgentLoop.js +6 -2
  79. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  80. package/dist/ui/hooks/useAgentLoop.test.js +31 -0
  81. package/dist/ui/hooks/useAgentLoop.test.js.map +1 -1
  82. package/dist/ui/hooks/useContextCompaction.d.ts +5 -8
  83. package/dist/ui/hooks/useContextCompaction.d.ts.map +1 -1
  84. package/dist/ui/hooks/useContextCompaction.js +72 -14
  85. package/dist/ui/hooks/useContextCompaction.js.map +1 -1
  86. package/dist/ui/hooks/useContextCompaction.test.d.ts +2 -0
  87. package/dist/ui/hooks/useContextCompaction.test.d.ts.map +1 -0
  88. package/dist/ui/hooks/useContextCompaction.test.js +120 -0
  89. package/dist/ui/hooks/useContextCompaction.test.js.map +1 -0
  90. package/dist/ui/login.d.ts.map +1 -1
  91. package/dist/ui/login.js +2 -1
  92. package/dist/ui/login.js.map +1 -1
  93. package/package.json +4 -4
@@ -64,16 +64,20 @@ import { awardPrompt, awardCommits } from "./core/progress/engine.js";
64
64
  import { detectNewCommits, repoKey } from "./core/progress/git-xp.js";
65
65
  import { rebuildFromSessions } from "./core/progress/rebuild.js";
66
66
  const ALL_PROVIDERS = [
67
+ // US
67
68
  "anthropic",
68
- "xiaomi",
69
69
  "openai",
70
70
  "gemini",
71
- "glm",
71
+ "xai",
72
+ // China
72
73
  "moonshot",
74
+ "glm",
73
75
  "minimax",
76
+ "xiaomi",
74
77
  "deepseek",
75
- "openrouter",
78
+ // Japan, then provider-agnostic gateway last
76
79
  "sakana",
80
+ "openrouter",
77
81
  ];
78
82
  function appSettingsFile() {
79
83
  return path.join(os.homedir(), ".gg", "gg-app.json");
@@ -536,6 +540,11 @@ async function main() {
536
540
  });
537
541
  const usageCache = new Map();
538
542
  const usageRequests = new Map();
543
+ // 429 backoff: when the provider rate-limits the usage endpoint, hold the
544
+ // error result until this timestamp instead of re-polling (and re-logging a
545
+ // WARN) every 60s — the old cadence hammered a limited endpoint for hours.
546
+ const usageRateLimitedUntil = new Map();
547
+ const USAGE_RATE_LIMIT_BACKOFF_MS = 5 * 60_000;
539
548
  async function fetchUsageProvider(provider) {
540
549
  const displayName = provider === "anthropic" ? "Anthropic" : "Codex";
541
550
  if (!(await auth.hasProviderAuth(provider))) {
@@ -544,14 +553,24 @@ async function main() {
544
553
  try {
545
554
  let credentials = await auth.resolveCredentials(provider);
546
555
  try {
547
- return { ...(await fetchSubscriptionUsage(provider, credentials)), connected: true };
556
+ const snapshot = {
557
+ ...(await fetchSubscriptionUsage(provider, credentials)),
558
+ connected: true,
559
+ };
560
+ usageRateLimitedUntil.delete(provider);
561
+ return snapshot;
548
562
  }
549
563
  catch (error) {
550
564
  // A provider can revoke an access token before its stored expiry. Refresh
551
565
  // once on 401, matching inference auth recovery, then retry the usage call.
552
566
  if (error instanceof SubscriptionUsageError && error.status === 401) {
553
567
  credentials = await auth.resolveCredentials(provider, { forceRefresh: true });
554
- return { ...(await fetchSubscriptionUsage(provider, credentials)), connected: true };
568
+ const snapshot = {
569
+ ...(await fetchSubscriptionUsage(provider, credentials)),
570
+ connected: true,
571
+ };
572
+ usageRateLimitedUntil.delete(provider);
573
+ return snapshot;
555
574
  }
556
575
  throw error;
557
576
  }
@@ -559,6 +578,9 @@ async function main() {
559
578
  catch (error) {
560
579
  const message = error instanceof Error ? error.message : String(error);
561
580
  log("WARN", "app-sidecar", "subscription usage fetch failed", { provider, message });
581
+ if (error instanceof SubscriptionUsageError && error.status === 429) {
582
+ usageRateLimitedUntil.set(provider, Date.now() + USAGE_RATE_LIMIT_BACKOFF_MS);
583
+ }
562
584
  const connected = await auth.hasProviderAuth(provider);
563
585
  return {
564
586
  provider,
@@ -587,9 +609,12 @@ async function main() {
587
609
  const missingReset = result.connected &&
588
610
  result.windows.length > 0 &&
589
611
  result.windows.some((window) => window.resetsAt === undefined);
612
+ const rateLimitedUntil = usageRateLimitedUntil.get(provider) ?? 0;
590
613
  usageCache.set(provider, {
591
614
  result,
592
- expiresAt: Date.now() + (missingReset ? 10_000 : 60_000),
615
+ expiresAt: rateLimitedUntil > Date.now()
616
+ ? rateLimitedUntil
617
+ : Date.now() + (missingReset ? 10_000 : 60_000),
593
618
  });
594
619
  return result;
595
620
  }
@@ -1012,6 +1037,11 @@ async function createSession(deps, opts) {
1012
1037
  signal: abort.signal,
1013
1038
  // Keep MCP startup off the readiness path in both modes.
1014
1039
  backgroundMcpConnect: true,
1040
+ // Keep restore-time auto-compaction off the readiness path too: its summary
1041
+ // LLM call (30s timeout) used to freeze waitForReady — and with it the whole
1042
+ // window (project picker, session list) — whenever a resumed session was
1043
+ // over the context threshold. First prompt compacts instead, with UI events.
1044
+ deferLoadCompaction: true,
1015
1045
  };
1016
1046
  let session;
1017
1047
  if (mode === "chat") {
@@ -1238,8 +1268,10 @@ async function createSession(deps, opts) {
1238
1268
  // Autopilot (auto-review) toggle for THIS window's project. Loaded from
1239
1269
  // gg-app.json on boot; flipped via POST /autopilot. When on, POST /prompt runs
1240
1270
  // runAutopilotCycle after the user's turn settles — Ken auto-reviews the work
1241
- // and drives the review→prompt→review loop.
1271
+ // and drives the review→prompt→review loop. Ken is the sole verification
1272
+ // owner in this mode, so suppress the build session's redundant Ideal hook.
1242
1273
  let autopilot = mode === "code" && (await loadAutopilot(cwd));
1274
+ session.setIdealReviewSuppressed(autopilot);
1243
1275
  // True while an autopilot review is in flight (used to defer kenAuto model
1244
1276
  // switches, like kenRunning does for chat Ken, and to drive the spinner).
1245
1277
  let autopilotReviewing = false;
@@ -1436,6 +1468,9 @@ async function createSession(deps, opts) {
1436
1468
  // (often >5 min) regardless of the user's global speedProfile pick.
1437
1469
  forceLongCacheRetention: true,
1438
1470
  });
1471
+ // Ken is already the independent autopilot reviewer; recursively running
1472
+ // his own Ideal self-review adds latency and can corrupt the verdict shape.
1473
+ ken.setIdealReviewSuppressed(true);
1439
1474
  await ken.initialize();
1440
1475
  // Deliberately no bus bridge: the review is silent. Errors surface via the
1441
1476
  // runAutopilotReview try/catch as autopilot_error frames.
@@ -1658,6 +1693,7 @@ async function createSession(deps, opts) {
1658
1693
  const generation = runLifecycle.begin(abortOwnedWork).generation;
1659
1694
  pendingCancelDrain = null;
1660
1695
  autopilotActive = true;
1696
+ session.setIdealReviewSuppressed(true);
1661
1697
  // Generation captured by the last plan review; acceptPlan re-checks it so
1662
1698
  // a user Accept/Reject landing mid-review always wins.
1663
1699
  let planGenAtReview = -1;
@@ -1771,6 +1807,7 @@ async function createSession(deps, opts) {
1771
1807
  }
1772
1808
  finally {
1773
1809
  autopilotActive = false;
1810
+ session.setIdealReviewSuppressed(autopilot);
1774
1811
  finishOwnedGeneration(generation, true);
1775
1812
  queueMicrotask(() => void runStrandedQueue());
1776
1813
  }
@@ -2237,6 +2274,20 @@ async function createSession(deps, opts) {
2237
2274
  void (async () => {
2238
2275
  const commandCandidates = [...PROMPT_COMMANDS, ...(await loadCustomCommands(cwd))];
2239
2276
  const messages = session.getMessages();
2277
+ // An Ideal hook is injected immediately after the assistant's candidate
2278
+ // no-tool response. That response is review scratch, not a user-visible
2279
+ // final answer. Live SSE drops it when the hook event arrives; mark the
2280
+ // same assistant message here so resumed history stays identical.
2281
+ const hiddenIdealDrafts = new Set();
2282
+ for (let i = 0; i < messages.length - 1; i++) {
2283
+ const draft = messages[i];
2284
+ const hookPrompt = messages[i + 1];
2285
+ if (draft?.role !== "assistant" || hookPrompt?.role !== "user")
2286
+ continue;
2287
+ const restored = restoreUserRow(hookPrompt.content);
2288
+ if (detectHookKind(restored.text) === "ideal")
2289
+ hiddenIdealDrafts.add(draft);
2290
+ }
2240
2291
  // Pre-index tool results by toolCallId so we can pair tool calls with
2241
2292
  // their results (for sub-agent status + image extraction).
2242
2293
  const toolResultMap = new Map();
@@ -2454,10 +2505,11 @@ async function createSession(deps, opts) {
2454
2505
  }
2455
2506
  }
2456
2507
  }
2457
- else {
2508
+ else if (!hiddenIdealDrafts.has(msg)) {
2458
2509
  // Assistant: one wire row per persisted text block — live streaming
2459
2510
  // splits bubbles at server_tool_call boundaries, and the persisted
2460
- // content keeps those blocks separate.
2511
+ // content keeps those blocks separate. Ideal-review candidate drafts
2512
+ // are intentionally omitted to match the live pre-final hook flow.
2461
2513
  for (const blockText of restoreAssistantTexts(msg.content)) {
2462
2514
  history.push({
2463
2515
  role: "assistant",
@@ -2736,6 +2788,9 @@ async function createSession(deps, opts) {
2736
2788
  return;
2737
2789
  }
2738
2790
  autopilot = enabled;
2791
+ // A toggle-off during an active cycle takes effect after Ken finishes;
2792
+ // until then, injected build runs must not re-enable Ideal self-review.
2793
+ session.setIdealReviewSuppressed(enabled || autopilotActive);
2739
2794
  await saveAutopilot(cwd, enabled);
2740
2795
  log("INFO", "app-sidecar", "autopilot toggled", { enabled: String(enabled) });
2741
2796
  broadcast("autopilot", { autopilot: enabled });