@kenkaiiii/ggcoder 5.19.4 → 5.19.6

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.
@@ -64,6 +64,7 @@ import { loadProgress, peekProgress, updateProgress } from "./core/progress/stor
64
64
  import { awardPrompt, awardCommits } from "./core/progress/engine.js";
65
65
  import { detectNewCommits, repoKey } from "./core/progress/git-xp.js";
66
66
  import { rebuildFromSessions } from "./core/progress/rebuild.js";
67
+ import { captureSidecarError, flushSidecarErrors, shouldCaptureToolFailure, shouldCaptureUsagePollingError, wrapSidecarHandler, } from "./core/sidecar-error-reporter.js";
67
68
  const ALL_PROVIDERS = [
68
69
  // US
69
70
  "anthropic",
@@ -169,6 +170,7 @@ async function persistModelSelection(settingsFile, provider, model) {
169
170
  await sm.set("defaultModel", model);
170
171
  }
171
172
  catch (err) {
173
+ captureSidecarError(err, "app-sidecar.settings.persist-model");
172
174
  log("WARN", "app-sidecar", "failed to persist model selection", { err: String(err) });
173
175
  }
174
176
  }
@@ -185,6 +187,7 @@ async function persistThinkingLevel(settingsFile, level) {
185
187
  await sm.set("thinkingLevel", level);
186
188
  }
187
189
  catch (err) {
190
+ captureSidecarError(err, "app-sidecar.settings.persist-thinking");
188
191
  log("WARN", "app-sidecar", "failed to persist thinking level", { err: String(err) });
189
192
  }
190
193
  }
@@ -452,7 +455,11 @@ async function runJsonModeIfRequested() {
452
455
  maxTurns: maxTurnsRaw ? parseInt(maxTurnsRaw, 10) : undefined,
453
456
  allowedTools,
454
457
  promptCacheKey: values["prompt-cache-key"],
455
- }).catch((err) => {
458
+ }).catch(async (err) => {
459
+ captureSidecarError(err, "app-sidecar.json-mode", {
460
+ provider: String(values.provider ?? "anthropic"),
461
+ });
462
+ await flushSidecarErrors();
456
463
  process.stderr.write((err instanceof Error ? err.message : String(err)) + "\n");
457
464
  process.exit(1);
458
465
  });
@@ -590,6 +597,9 @@ async function main() {
590
597
  }
591
598
  catch (error) {
592
599
  const message = error instanceof Error ? error.message : String(error);
600
+ if (shouldCaptureUsagePollingError(error)) {
601
+ captureSidecarError(error, "app-sidecar.usage.fetch", { provider });
602
+ }
593
603
  log("WARN", "app-sidecar", "subscription usage fetch failed", { provider, message });
594
604
  if (error instanceof SubscriptionUsageError && error.status === 429) {
595
605
  usageRateLimitedUntil.set(provider, Date.now() + USAGE_RATE_LIMIT_BACKOFF_MS);
@@ -649,7 +659,7 @@ async function main() {
649
659
  return null;
650
660
  }
651
661
  }
652
- const server = http.createServer((req, res) => {
662
+ const server = http.createServer(wrapSidecarHandler((req, res) => {
653
663
  const url = req.url ?? "/";
654
664
  const method = req.method ?? "GET";
655
665
  // CORS preflight — the webview origin differs from 127.0.0.1.
@@ -693,6 +703,7 @@ async function main() {
693
703
  }
694
704
  catch (err) {
695
705
  const message = err instanceof Error ? err.message : String(err);
706
+ captureSidecarError(err, "app-sidecar.session.create");
696
707
  log("ERROR", "app-sidecar", "session create failed", { message });
697
708
  daemonJson(res, 500, { error: message });
698
709
  }
@@ -729,6 +740,7 @@ async function main() {
729
740
  }
730
741
  daemonJson(res, 200, await subscriptionUsage(provider));
731
742
  })().catch((error) => {
743
+ captureSidecarError(error, "app-sidecar.usage.request");
732
744
  log("ERROR", "app-sidecar", "subscription usage request failed", {
733
745
  message: error instanceof Error ? error.message : String(error),
734
746
  });
@@ -744,7 +756,7 @@ async function main() {
744
756
  return;
745
757
  }
746
758
  ctx.handle(req, res, url, method);
747
- });
759
+ }, "app-sidecar.http"));
748
760
  server.listen(port, host, () => {
749
761
  const addr = server.address();
750
762
  // The Rust shell reads this line to learn the daemon port.
@@ -889,6 +901,7 @@ async function createProgressManager(agentDir, broadcastAll) {
889
901
  broadcastAll(buildSnapshot(updated), originId);
890
902
  }
891
903
  catch (err) {
904
+ captureSidecarError(err, "app-sidecar.progress.award");
892
905
  log("DEBUG", "app-sidecar", "progress award failed", {
893
906
  message: err instanceof Error ? err.message : String(err),
894
907
  });
@@ -920,6 +933,7 @@ async function createProgressManager(agentDir, broadcastAll) {
920
933
  watcher.unref();
921
934
  }
922
935
  catch (err) {
936
+ captureSidecarError(err, "app-sidecar.progress.watch");
923
937
  log("DEBUG", "app-sidecar", "progress watch unavailable", {
924
938
  message: err instanceof Error ? err.message : String(err),
925
939
  });
@@ -1010,6 +1024,11 @@ async function createSession(deps, opts) {
1010
1024
  const f = formatError(err);
1011
1025
  const message = f.message ? desktopGuidance(f.message) : undefined;
1012
1026
  const guidance = desktopGuidance(f.guidance);
1027
+ captureSidecarError(err, `app-sidecar.${logLabel.replaceAll(" ", "-")}`, {
1028
+ scope: type,
1029
+ ...(f.provider ? { provider: f.provider } : {}),
1030
+ ...(f.statusCode != null ? { status: String(f.statusCode) } : {}),
1031
+ });
1013
1032
  log("ERROR", "app-sidecar", logLabel, {
1014
1033
  headline: f.headline,
1015
1034
  source: f.source,
@@ -1067,6 +1086,7 @@ async function createSession(deps, opts) {
1067
1086
  chatAgent = nextAgent;
1068
1087
  broadcast("chat_agent_change", { chatAgent: nextAgent });
1069
1088
  await session.persistAppMarker("agent_handoff", { chatAgent: nextAgent }).catch((error) => {
1089
+ captureSidecarError(error, "app-sidecar.chat-agent.persist-handoff");
1070
1090
  log("WARN", "app-sidecar", "agent handoff marker persist failed", {
1071
1091
  message: error instanceof Error ? error.message : String(error),
1072
1092
  });
@@ -1185,6 +1205,7 @@ async function createSession(deps, opts) {
1185
1205
  .catch(() => false)
1186
1206
  .then(() => syncApprovedPlanProgress(generation))
1187
1207
  .catch((error) => {
1208
+ captureSidecarError(error, "app-sidecar.plan.refresh-progress");
1188
1209
  log("WARN", "app-sidecar", "plan progress refresh failed", {
1189
1210
  message: error instanceof Error ? error.message : String(error),
1190
1211
  });
@@ -1239,6 +1260,11 @@ async function createSession(deps, opts) {
1239
1260
  session.eventBus.on("tool_call_end", (d) => {
1240
1261
  const name = toolCallNames.get(d.toolCallId) ?? "unknown";
1241
1262
  toolCallNames.delete(d.toolCallId);
1263
+ if (d.isError && shouldCaptureToolFailure(name, d.result)) {
1264
+ // Expected model-correctable validation failures stay in the local log and
1265
+ // conversation. Unexpected failures are reported without private result data.
1266
+ captureSidecarError(new Error(`Tool ${name} failed`), `tool.${name}`, { tool: name });
1267
+ }
1242
1268
  log(d.isError ? "ERROR" : "INFO", "tool", `Tool call ended: ${name}`, {
1243
1269
  id: d.toolCallId,
1244
1270
  durationMs: String(d.durationMs),
@@ -1579,6 +1605,7 @@ async function createSession(deps, opts) {
1579
1605
  catch (error) {
1580
1606
  // Keep tracking when prompt cleanup fails; hiding the widget here
1581
1607
  // would claim completion while the approved-plan contract remained.
1608
+ captureSidecarError(error, "app-sidecar.plan.cleanup");
1582
1609
  log("WARN", "app-sidecar", "completed plan cleanup failed", {
1583
1610
  message: error instanceof Error ? error.message : String(error),
1584
1611
  });
@@ -2058,7 +2085,10 @@ async function createSession(deps, opts) {
2058
2085
  void memoryStore
2059
2086
  .snapshot()
2060
2087
  .then((snapshot) => json(res, 200, snapshot))
2061
- .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
2088
+ .catch((error) => {
2089
+ captureSidecarError(error, "app-sidecar.memory.snapshot");
2090
+ json(res, 500, { error: error instanceof Error ? error.message : String(error) });
2091
+ });
2062
2092
  return;
2063
2093
  }
2064
2094
  if (method === "DELETE" && url.startsWith("/memories/")) {
@@ -2071,14 +2101,20 @@ async function createSession(deps, opts) {
2071
2101
  .forget(id)
2072
2102
  .then(() => memoryStore.snapshot())
2073
2103
  .then((snapshot) => json(res, 200, snapshot))
2074
- .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
2104
+ .catch((error) => {
2105
+ captureSidecarError(error, "app-sidecar.memory.forget");
2106
+ json(res, 500, { error: error instanceof Error ? error.message : String(error) });
2107
+ });
2075
2108
  return;
2076
2109
  }
2077
2110
  if (method === "GET" && url === "/jiwa") {
2078
2111
  void jiwaStore
2079
2112
  .snapshot()
2080
2113
  .then((snapshot) => json(res, 200, snapshot))
2081
- .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
2114
+ .catch((error) => {
2115
+ captureSidecarError(error, "app-sidecar.jiwa.snapshot");
2116
+ json(res, 500, { error: error instanceof Error ? error.message : String(error) });
2117
+ });
2082
2118
  return;
2083
2119
  }
2084
2120
  if (method === "DELETE" && url.startsWith("/jiwa/")) {
@@ -2091,7 +2127,10 @@ async function createSession(deps, opts) {
2091
2127
  .forget(id)
2092
2128
  .then(() => jiwaStore.snapshot())
2093
2129
  .then((snapshot) => json(res, 200, snapshot))
2094
- .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
2130
+ .catch((error) => {
2131
+ captureSidecarError(error, "app-sidecar.jiwa.forget");
2132
+ json(res, 500, { error: error instanceof Error ? error.message : String(error) });
2133
+ });
2095
2134
  return;
2096
2135
  }
2097
2136
  if (method === "GET" && (url === "/events" || url.startsWith("/events?"))) {
@@ -2204,6 +2243,7 @@ async function createSession(deps, opts) {
2204
2243
  json(res, 200, { path: dir });
2205
2244
  }
2206
2245
  catch (err) {
2246
+ captureSidecarError(err, "app-sidecar.project.create");
2207
2247
  json(res, 500, { error: err instanceof Error ? err.message : String(err) });
2208
2248
  }
2209
2249
  });
@@ -2214,6 +2254,7 @@ async function createSession(deps, opts) {
2214
2254
  void discoverProjects()
2215
2255
  .then((projects) => json(res, 200, { projects }))
2216
2256
  .catch((err) => {
2257
+ captureSidecarError(err, "app-sidecar.projects.discover");
2217
2258
  log("ERROR", "app-sidecar", "discoverProjects failed", {
2218
2259
  message: err instanceof Error ? err.message : String(err),
2219
2260
  });
@@ -2247,7 +2288,10 @@ async function createSession(deps, opts) {
2247
2288
  .map(({ item }) => item);
2248
2289
  json(res, 200, { sessions: recent });
2249
2290
  })
2250
- .catch(() => json(res, 200, { sessions: [] }));
2291
+ .catch((error) => {
2292
+ captureSidecarError(error, "app-sidecar.sessions.list-chat");
2293
+ json(res, 200, { sessions: [] });
2294
+ });
2251
2295
  return;
2252
2296
  }
2253
2297
  // The picker may be served by a sidecar currently running in Chat mode.
@@ -2258,7 +2302,10 @@ async function createSession(deps, opts) {
2258
2302
  : paths.sessionsDir;
2259
2303
  void listRecentSessions(target, 5, sessionsDir)
2260
2304
  .then((sessions) => json(res, 200, { sessions }))
2261
- .catch(() => json(res, 200, { sessions: [] }));
2305
+ .catch((error) => {
2306
+ captureSidecarError(error, "app-sidecar.sessions.list");
2307
+ json(res, 200, { sessions: [] });
2308
+ });
2262
2309
  return;
2263
2310
  }
2264
2311
  if (method === "GET" && url.startsWith("/files")) {
@@ -2266,6 +2313,7 @@ async function createSession(deps, opts) {
2266
2313
  void searchProjectFiles(cwd, q)
2267
2314
  .then((files) => json(res, 200, { files }))
2268
2315
  .catch((err) => {
2316
+ captureSidecarError(err, "app-sidecar.files.search");
2269
2317
  log("ERROR", "app-sidecar", "searchProjectFiles failed", {
2270
2318
  message: err instanceof Error ? err.message : String(err),
2271
2319
  });
@@ -2833,6 +2881,7 @@ async function createSession(deps, opts) {
2833
2881
  }
2834
2882
  catch (err) {
2835
2883
  const message = err instanceof Error ? err.message : String(err);
2884
+ captureSidecarError(err, "app-sidecar.prompt-enhancer");
2836
2885
  log("ERROR", "app-sidecar", "enhance failed", { message });
2837
2886
  json(res, 500, { error: message });
2838
2887
  }
@@ -3158,6 +3207,7 @@ async function createSession(deps, opts) {
3158
3207
  drained,
3159
3208
  });
3160
3209
  })().catch((error) => {
3210
+ captureSidecarError(error, "app-sidecar.run.cancel");
3161
3211
  broadcast("cancel_failed", { error: "cancel_failed", runState: runLifecycle.state });
3162
3212
  json(res, 500, {
3163
3213
  error: "cancel_failed",
@@ -3185,6 +3235,7 @@ async function createSession(deps, opts) {
3185
3235
  json(res, 200, { ok: true });
3186
3236
  })
3187
3237
  .catch((err) => {
3238
+ captureSidecarError(err, "app-sidecar.session.new");
3188
3239
  json(res, 500, { error: err instanceof Error ? err.message : String(err) });
3189
3240
  });
3190
3241
  return;
@@ -3239,6 +3290,7 @@ async function createSession(deps, opts) {
3239
3290
  json(res, 200, { ok: true, planTotal });
3240
3291
  }
3241
3292
  catch (err) {
3293
+ captureSidecarError(err, "app-sidecar.plan.accept");
3242
3294
  json(res, 500, { error: err instanceof Error ? err.message : String(err) });
3243
3295
  }
3244
3296
  });
@@ -3335,6 +3387,7 @@ async function createSession(deps, opts) {
3335
3387
  broadcast("auth_done", { provider });
3336
3388
  }
3337
3389
  catch (err) {
3390
+ captureSidecarError(err, "app-sidecar.auth.oauth", { provider });
3338
3391
  broadcast("auth_error", {
3339
3392
  provider,
3340
3393
  message: err instanceof Error ? err.message : String(err),
@@ -3476,6 +3529,7 @@ async function createSession(deps, opts) {
3476
3529
  }
3477
3530
  catch (err) {
3478
3531
  serveController = null;
3532
+ captureSidecarError(err, "app-sidecar.serve.start", { provider: st.provider });
3479
3533
  json(res, 400, { error: err instanceof Error ? err.message : String(err) });
3480
3534
  }
3481
3535
  })();
@@ -3502,6 +3556,7 @@ async function createSession(deps, opts) {
3502
3556
  void buildMcpRows(targetCwd)
3503
3557
  .then((servers) => json(res, 200, { servers }))
3504
3558
  .catch((err) => {
3559
+ captureSidecarError(err, "app-sidecar.mcp.list");
3505
3560
  log("ERROR", "app-sidecar", "buildMcpRows failed", {
3506
3561
  message: err instanceof Error ? err.message : String(err),
3507
3562
  });
@@ -3558,6 +3613,7 @@ async function createSession(deps, opts) {
3558
3613
  });
3559
3614
  }
3560
3615
  catch (err) {
3616
+ captureSidecarError(err, "app-sidecar.mcp.add", { server: config.name });
3561
3617
  json(res, 500, {
3562
3618
  error: err instanceof Error ? err.message : String(err),
3563
3619
  });
@@ -3647,6 +3703,7 @@ async function createSession(deps, opts) {
3647
3703
  }
3648
3704
  }
3649
3705
  catch (err) {
3706
+ captureSidecarError(err, "app-sidecar.mcp.login", { server: name });
3650
3707
  broadcast("mcp_auth_error", {
3651
3708
  name,
3652
3709
  message: err instanceof Error ? err.message : String(err),
@@ -3693,7 +3750,9 @@ async function createSession(deps, opts) {
3693
3750
  dispose,
3694
3751
  };
3695
3752
  }
3696
- main().catch((err) => {
3753
+ main().catch(async (err) => {
3754
+ captureSidecarError(err, "app-sidecar.main", { severity: "fatal" });
3755
+ await flushSidecarErrors();
3697
3756
  const message = err instanceof Error ? err.message : String(err);
3698
3757
  process.stderr.write(`GG_APP_FATAL ${message}\n`);
3699
3758
  process.exit(1);