@kenkaiiii/ggcoder 5.22.2 → 5.22.4

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 (44) hide show
  1. package/assets/skills/evidence-led-ui/SKILL.md +1 -1
  2. package/dist/app-sidecar.js +86 -7
  3. package/dist/app-sidecar.js.map +1 -1
  4. package/dist/chat-agents/memory.d.ts.map +1 -1
  5. package/dist/chat-agents/memory.js +14 -5
  6. package/dist/chat-agents/memory.js.map +1 -1
  7. package/dist/chat-agents/memory.test.js +11 -1
  8. package/dist/chat-agents/memory.test.js.map +1 -1
  9. package/dist/chat-agents/shared.js +1 -1
  10. package/dist/chat-agents/shared.js.map +1 -1
  11. package/dist/core/agent-session-marker-anchors.test.d.ts +2 -0
  12. package/dist/core/agent-session-marker-anchors.test.d.ts.map +1 -0
  13. package/dist/core/agent-session-marker-anchors.test.js +150 -0
  14. package/dist/core/agent-session-marker-anchors.test.js.map +1 -0
  15. package/dist/core/agent-session.d.ts +13 -0
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +19 -3
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/tools/grep.d.ts.map +1 -1
  20. package/dist/tools/grep.js +8 -3
  21. package/dist/tools/grep.js.map +1 -1
  22. package/dist/tools/grep.test.js +6 -0
  23. package/dist/tools/grep.test.js.map +1 -1
  24. package/dist/tools/source-path.d.ts.map +1 -1
  25. package/dist/tools/source-path.js +9 -0
  26. package/dist/tools/source-path.js.map +1 -1
  27. package/dist/tools/web-search.d.ts.map +1 -1
  28. package/dist/tools/web-search.js +13 -2
  29. package/dist/tools/web-search.js.map +1 -1
  30. package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
  31. package/dist/ui/components/ActivityIndicator.js +1 -0
  32. package/dist/ui/components/ActivityIndicator.js.map +1 -1
  33. package/dist/ui/hooks/useAgentLoop.d.ts +1 -1
  34. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  35. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  36. package/dist/utils/github.d.ts +20 -0
  37. package/dist/utils/github.d.ts.map +1 -0
  38. package/dist/utils/github.js +65 -0
  39. package/dist/utils/github.js.map +1 -0
  40. package/dist/utils/github.test.d.ts +2 -0
  41. package/dist/utils/github.test.d.ts.map +1 -0
  42. package/dist/utils/github.test.js +23 -0
  43. package/dist/utils/github.test.js.map +1 -0
  44. package/package.json +4 -4
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: evidence-led-ui
3
- description: Use for web/mobile UI changes or evaluation: pages, components, dashboards, design systems, responsiveness, accessibility, or visual polish, even without explicit design request. Exclude database/API schemas, CLI output, copy-only work, standalone image/SVG generation, and description-only tasks.
3
+ description: Use for broad or design-sensitive web/mobile UI work: net-new pages/screens, redesigns, design-system work, visual polish, responsive or accessibility overhauls, and UI quality review/critique. Do NOT use for small UI edits that follow existing patterns (bug fixes, minor styling tweaks, wiring, copy changes, small component additions matching neighbors); just match the surrounding code for those. Also exclude database/API schemas, CLI output, standalone image/SVG generation, and description-only tasks.
4
4
  license: See LICENSES.md
5
5
  compatibility: Full review requires filesystem inspection and rendered screenshots; web research and browser/device tooling are optional and unavailable checks must be reported honestly.
6
6
  ---
@@ -21,6 +21,7 @@ import { parseArgs } from "node:util";
21
21
  import { environmentSecrets, formatError, redactValue, } from "@kenkaiiii/gg-ai";
22
22
  import { runJsonMode } from "./modes/json-mode.js";
23
23
  import { runSubagentWorkerMode } from "./modes/subagent-worker-mode.js";
24
+ import { setStreamDiagnostic } from "@kenkaiiii/gg-agent";
24
25
  import { AgentSession } from "./core/agent-session.js";
25
26
  import { RunLifecycle } from "./core/run-lifecycle.js";
26
27
  import { CHAT_AGENT_IDS, chatAgentSessionsDir, createChatAgent, parseChatAgentId, switchChatAgent, } from "./chat-agents/index.js";
@@ -47,6 +48,7 @@ import { SettingsManager } from "./core/settings-manager.js";
47
48
  import { getModel, getDefaultThinkingLevel, getContextWindow, MODELS, } from "./core/model-registry.js";
48
49
  import { resolveStartOrFallback } from "./core/resolve-start.js";
49
50
  import { getGitBranch, getGitDirtyFileCount, isGitRepo } from "./utils/git.js";
51
+ import { getGitHubOpenCounts, getGitHubRepoSlug } from "./utils/github.js";
50
52
  import { extractPlanSteps } from "./utils/plan-steps.js";
51
53
  import { getNextThinkingLevel, getSupportedThinkingLevels, isThinkingLevelSupported, } from "./core/thinking-level.js";
52
54
  import { PROMPT_COMMANDS } from "./core/prompt-commands.js";
@@ -518,6 +520,31 @@ async function main() {
518
520
  // ~/.gg/debug.log (initLogger truncates on each start).
519
521
  const sidecarLog = path.join(paths.agentDir, "gg-app-sidecar.log");
520
522
  initLogger(sidecarLog);
523
+ // The desktop sidecar previously omitted the stream diagnostic hook used by
524
+ // the CLI, leaving device-specific provider stalls impossible to distinguish from
525
+ // event-loop starvation or a broken streaming network path. Keep routine
526
+ // phases lightweight; timeout phases include non-sensitive runtime context.
527
+ setStreamDiagnostic((phase, data) => {
528
+ const includeRuntime = phase === "idle_timeout_fired" ||
529
+ phase === "hard_timeout_fired" ||
530
+ phase === "stall_exhausted";
531
+ log("INFO", "stream", phase, {
532
+ ...(data ?? {}),
533
+ ...(includeRuntime
534
+ ? {
535
+ platform: process.platform,
536
+ arch: process.arch,
537
+ osRelease: os.release(),
538
+ node: process.version,
539
+ logicalCpus: os.cpus().length,
540
+ totalMemoryMb: Math.round(os.totalmem() / 1024 / 1024),
541
+ freeMemoryMb: Math.round(os.freemem() / 1024 / 1024),
542
+ rssMb: Math.round(process.memoryUsage().rss / 1024 / 1024),
543
+ processUptimeSec: Math.round(process.uptime()),
544
+ }
545
+ : {}),
546
+ });
547
+ });
521
548
  // Global last-resort guards, installed as early as the logger allows so they
522
549
  // cover the WHOLE lifecycle — including startup/initialize, the phase the
523
550
  // "sidecar did not start in time" bug lives in. The sidecar is a long-lived
@@ -576,11 +603,14 @@ async function main() {
576
603
  });
577
604
  const usageCache = new Map();
578
605
  const usageRequests = new Map();
579
- // 429 backoff: when the provider rate-limits the usage endpoint, hold the
580
- // error result until this timestamp instead of re-polling (and re-logging a
581
- // WARN) every 60s the old cadence hammered a limited endpoint for hours.
606
+ // 429 backoff: quota endpoints are auxiliary UI data. Honor Retry-After when
607
+ // provided; otherwise retain the unavailable snapshot for 30 minutes. Clamp
608
+ // the provider value so a malformed header can neither hammer the endpoint
609
+ // nor suppress usage data forever.
582
610
  const usageRateLimitedUntil = new Map();
583
- const USAGE_RATE_LIMIT_BACKOFF_MS = 5 * 60_000;
611
+ const USAGE_RATE_LIMIT_FALLBACK_BACKOFF_MS = 30 * 60_000;
612
+ const USAGE_RATE_LIMIT_MIN_BACKOFF_MS = 60_000;
613
+ const USAGE_RATE_LIMIT_MAX_BACKOFF_MS = 24 * 60 * 60_000;
584
614
  async function fetchUsageProvider(provider) {
585
615
  const displayName = provider === "anthropic" ? "Anthropic" : provider === "openai" ? "Codex" : "Kimi";
586
616
  // Kimi plan usage is tracked on the OAuth credential specifically — the
@@ -619,10 +649,16 @@ async function main() {
619
649
  if (shouldCaptureUsagePollingError(error)) {
620
650
  captureSidecarError(error, "app-sidecar.usage.fetch", { provider });
621
651
  }
622
- log("WARN", "app-sidecar", "subscription usage fetch failed", { provider, message });
652
+ let backoffMs;
623
653
  if (error instanceof SubscriptionUsageError && error.status === 429) {
624
- usageRateLimitedUntil.set(provider, Date.now() + USAGE_RATE_LIMIT_BACKOFF_MS);
654
+ backoffMs = Math.min(USAGE_RATE_LIMIT_MAX_BACKOFF_MS, Math.max(USAGE_RATE_LIMIT_MIN_BACKOFF_MS, error.retryAfterMs ?? USAGE_RATE_LIMIT_FALLBACK_BACKOFF_MS));
655
+ usageRateLimitedUntil.set(provider, Date.now() + backoffMs);
625
656
  }
657
+ log("WARN", "app-sidecar", "subscription usage fetch failed", {
658
+ provider,
659
+ message,
660
+ ...(backoffMs !== undefined && { backoffMs: String(backoffMs) }),
661
+ });
626
662
  const connected = await auth.hasProviderAuth(authKey);
627
663
  return {
628
664
  provider,
@@ -1183,14 +1219,20 @@ async function createSession(deps, opts) {
1183
1219
  // Workspace extras (context window, git status, background tasks). Git state
1184
1220
  // is resolved once at startup and refreshed after every run; the context
1185
1221
  // window follows the active model.
1186
- const [initialGitBranch, initialGitIsRepo, initialDirtyFileCount] = await Promise.all([
1222
+ const [initialGitBranch, initialGitIsRepo, initialDirtyFileCount, initialGitHubSlug] = await Promise.all([
1187
1223
  getGitBranch(cwd).catch(() => null),
1188
1224
  isGitRepo(cwd).catch(() => false),
1189
1225
  getGitDirtyFileCount(cwd).catch(() => 0),
1226
+ getGitHubRepoSlug(cwd).catch(() => null),
1190
1227
  ]);
1191
1228
  let gitBranch = initialGitBranch;
1192
1229
  let gitIsRepo = initialGitIsRepo;
1193
1230
  let gitDirtyFileCount = initialDirtyFileCount;
1231
+ // Open issue/PR counts for the origin repo's GitHub slug, via the `gh` CLI's
1232
+ // auth. null = unknown (gh missing/unauthed, non-GitHub origin) → chips hidden.
1233
+ const gitHubSlug = initialGitHubSlug;
1234
+ let gitHubIssues = null;
1235
+ let gitHubPRs = null;
1194
1236
  function currentContextWindow() {
1195
1237
  const st = session.getState();
1196
1238
  return getContextWindow(st.model, { provider: st.provider, accountId: st.accountId });
@@ -1203,9 +1245,26 @@ async function createSession(deps, opts) {
1203
1245
  gitBranch,
1204
1246
  isGitRepo: gitIsRepo,
1205
1247
  gitDirtyFileCount,
1248
+ gitHubIssues,
1249
+ gitHubPRs,
1250
+ gitHubRepoUrl: gitHubSlug ? `https://github.com/${gitHubSlug}` : null,
1206
1251
  tasks: session.listBackgroundProcesses(),
1207
1252
  };
1208
1253
  }
1254
+ // Refresh the GitHub counts and broadcast only on change. Transient failures
1255
+ // keep the last-known numbers so the chips don't flicker off on a timeout.
1256
+ async function refreshGitHubCounts() {
1257
+ if (!gitHubSlug)
1258
+ return;
1259
+ const counts = await getGitHubOpenCounts(gitHubSlug);
1260
+ if (!counts)
1261
+ return;
1262
+ if (counts.issues !== gitHubIssues || counts.prs !== gitHubPRs) {
1263
+ gitHubIssues = counts.issues;
1264
+ gitHubPRs = counts.prs;
1265
+ broadcast("extras", footerExtras());
1266
+ }
1267
+ }
1209
1268
  // tool_call_end carries no tool name (only the id), so remember each call's
1210
1269
  // name from tool_call_start to log a useful line on completion. Mirrors the
1211
1270
  // CLI's logging so the app sidecar's ~/.gg/gg-app-sidecar.log records tool
@@ -1641,6 +1700,9 @@ async function createSession(deps, opts) {
1641
1700
  isGitRepo(cwd).catch(() => gitIsRepo),
1642
1701
  getGitDirtyFileCount(cwd).catch(() => gitDirtyFileCount),
1643
1702
  ]);
1703
+ // A run may have opened/closed issues or PRs — refresh fire-and-forget so
1704
+ // teardown isn't delayed by the network. Broadcasts itself on change.
1705
+ void refreshGitHubCounts();
1644
1706
  // Serialize behind any marker/tool-triggered refresh so the terminal
1645
1707
  // progress snapshot uses the live plan file. Once every canonical step
1646
1708
  // is complete, remove the approved plan from future system prompts and
@@ -2093,6 +2155,20 @@ async function createSession(deps, opts) {
2093
2155
  gitPoll.unref?.();
2094
2156
  };
2095
2157
  scheduleGitPoll(5000);
2158
+ // GitHub issue/PR counts change outside the app (web UI, teammates), so poll
2159
+ // on a slow cadence. Network-bound, so keep it well under the search API's
2160
+ // rate budget (2 calls per tick). No-op when the origin isn't a GitHub repo.
2161
+ let gitHubPoll;
2162
+ let gitHubPollStopped = false;
2163
+ const scheduleGitHubPoll = (delay) => {
2164
+ if (gitHubPollStopped)
2165
+ return;
2166
+ gitHubPoll = setTimeout(() => {
2167
+ void refreshGitHubCounts().finally(() => scheduleGitHubPoll(60_000));
2168
+ }, delay);
2169
+ gitHubPoll.unref?.();
2170
+ };
2171
+ scheduleGitHubPoll(2000);
2096
2172
  function readBody(req, res) {
2097
2173
  return readCappedBody(req, res);
2098
2174
  }
@@ -3786,6 +3862,9 @@ async function createSession(deps, opts) {
3786
3862
  gitPollStopped = true;
3787
3863
  if (gitPoll)
3788
3864
  clearTimeout(gitPoll);
3865
+ gitHubPollStopped = true;
3866
+ if (gitHubPoll)
3867
+ clearTimeout(gitHubPoll);
3789
3868
  // Stop the Telegram serve loop + dispose its per-chat sessions.
3790
3869
  if (serveController)
3791
3870
  await serveController.stop().catch(() => { });