@kenkaiiii/ggcoder 5.18.0 → 5.19.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 (52) hide show
  1. package/dist/app-sidecar.js +53 -8
  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 +84 -1
  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.d.ts +11 -0
  24. package/dist/core/agent-session.d.ts.map +1 -1
  25. package/dist/core/agent-session.js +59 -11
  26. package/dist/core/agent-session.js.map +1 -1
  27. package/dist/core/auth-providers.d.ts.map +1 -1
  28. package/dist/core/auth-providers.js +14 -7
  29. package/dist/core/auth-providers.js.map +1 -1
  30. package/dist/core/compaction/compactor.d.ts +9 -1
  31. package/dist/core/compaction/compactor.d.ts.map +1 -1
  32. package/dist/core/compaction/compactor.js +43 -9
  33. package/dist/core/compaction/compactor.js.map +1 -1
  34. package/dist/core/compaction/compactor.test.js +48 -0
  35. package/dist/core/compaction/compactor.test.js.map +1 -1
  36. package/dist/core/resolve-start.test.js +1 -0
  37. package/dist/core/resolve-start.test.js.map +1 -1
  38. package/dist/core/settings-manager.d.ts +1 -0
  39. package/dist/core/settings-manager.d.ts.map +1 -1
  40. package/dist/core/settings-manager.js +1 -0
  41. package/dist/core/settings-manager.js.map +1 -1
  42. package/dist/core/subagent-manager.test.js +7 -3
  43. package/dist/core/subagent-manager.test.js.map +1 -1
  44. package/dist/ui/App.d.ts.map +1 -1
  45. package/dist/ui/App.js.map +1 -1
  46. package/dist/ui/components/ModelSelector.d.ts.map +1 -1
  47. package/dist/ui/components/ModelSelector.js +1 -0
  48. package/dist/ui/components/ModelSelector.js.map +1 -1
  49. package/dist/ui/login.d.ts.map +1 -1
  50. package/dist/ui/login.js +2 -1
  51. package/dist/ui/login.js.map +1 -1
  52. 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") {
@@ -2237,6 +2267,20 @@ async function createSession(deps, opts) {
2237
2267
  void (async () => {
2238
2268
  const commandCandidates = [...PROMPT_COMMANDS, ...(await loadCustomCommands(cwd))];
2239
2269
  const messages = session.getMessages();
2270
+ // An Ideal hook is injected immediately after the assistant's candidate
2271
+ // no-tool response. That response is review scratch, not a user-visible
2272
+ // final answer. Live SSE drops it when the hook event arrives; mark the
2273
+ // same assistant message here so resumed history stays identical.
2274
+ const hiddenIdealDrafts = new Set();
2275
+ for (let i = 0; i < messages.length - 1; i++) {
2276
+ const draft = messages[i];
2277
+ const hookPrompt = messages[i + 1];
2278
+ if (draft?.role !== "assistant" || hookPrompt?.role !== "user")
2279
+ continue;
2280
+ const restored = restoreUserRow(hookPrompt.content);
2281
+ if (detectHookKind(restored.text) === "ideal")
2282
+ hiddenIdealDrafts.add(draft);
2283
+ }
2240
2284
  // Pre-index tool results by toolCallId so we can pair tool calls with
2241
2285
  // their results (for sub-agent status + image extraction).
2242
2286
  const toolResultMap = new Map();
@@ -2454,10 +2498,11 @@ async function createSession(deps, opts) {
2454
2498
  }
2455
2499
  }
2456
2500
  }
2457
- else {
2501
+ else if (!hiddenIdealDrafts.has(msg)) {
2458
2502
  // Assistant: one wire row per persisted text block — live streaming
2459
2503
  // splits bubbles at server_tool_call boundaries, and the persisted
2460
- // content keeps those blocks separate.
2504
+ // content keeps those blocks separate. Ideal-review candidate drafts
2505
+ // are intentionally omitted to match the live pre-final hook flow.
2461
2506
  for (const blockText of restoreAssistantTexts(msg.content)) {
2462
2507
  history.push({
2463
2508
  role: "assistant",