@kenkaiiii/ggcoder 5.19.3 → 5.19.5

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 (34) hide show
  1. package/dist/app-sidecar.js +87 -21
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +8 -1
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/message-images.d.ts +12 -0
  7. package/dist/core/message-images.d.ts.map +1 -0
  8. package/dist/core/message-images.js +52 -0
  9. package/dist/core/message-images.js.map +1 -0
  10. package/dist/core/message-images.test.d.ts +2 -0
  11. package/dist/core/message-images.test.d.ts.map +1 -0
  12. package/dist/core/message-images.test.js +62 -0
  13. package/dist/core/message-images.test.js.map +1 -0
  14. package/dist/core/sidecar-error-reporter.d.ts +10 -0
  15. package/dist/core/sidecar-error-reporter.d.ts.map +1 -0
  16. package/dist/core/sidecar-error-reporter.js +28 -0
  17. package/dist/core/sidecar-error-reporter.js.map +1 -0
  18. package/dist/core/sidecar-error-reporter.test.d.ts +2 -0
  19. package/dist/core/sidecar-error-reporter.test.d.ts.map +1 -0
  20. package/dist/core/sidecar-error-reporter.test.js +41 -0
  21. package/dist/core/sidecar-error-reporter.test.js.map +1 -0
  22. package/dist/modes/json-mode.d.ts.map +1 -1
  23. package/dist/modes/json-mode.js +6 -0
  24. package/dist/modes/json-mode.js.map +1 -1
  25. package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
  26. package/dist/modes/subagent-worker-mode.js +10 -0
  27. package/dist/modes/subagent-worker-mode.js.map +1 -1
  28. package/dist/tools/subagent-shared.d.ts +1 -1
  29. package/dist/tools/subagent-shared.d.ts.map +1 -1
  30. package/dist/tools/subagent-shared.js +4 -1
  31. package/dist/tools/subagent-shared.js.map +1 -1
  32. package/dist/tools/subagent-shared.test.js +6 -1
  33. package/dist/tools/subagent-shared.test.js.map +1 -1
  34. package/package.json +4 -4
@@ -55,7 +55,7 @@ import { loadTasksSync, saveTasksSync, pruneDoneTasksSync, getNextPendingTask, m
55
55
  import { initLogger, log } from "./core/logger.js";
56
56
  import { RADIO_STATIONS, getCurrentStation, getRadioVolume, playRadio, setRadioVolume, stopRadio, } from "./core/radio.js";
57
57
  import { enrichProcessPath } from "./core/shell-path.js";
58
- import { downscaleForPreview, validateVisionImage } from "./utils/image.js";
58
+ import { downscaleForPreview, shrinkToFit, validateVisionImage } from "./utils/image.js";
59
59
  import { startServeMode } from "./modes/serve-mode.js";
60
60
  import { loadTelegramConfig, saveTelegramConfig, verifyBotToken } from "./core/telegram-config.js";
61
61
  import { loadServers, addServer, removeServer, getServer, parseMcpAddCommand, MCPClientManager, McpOAuthStore, } from "./core/mcp/index.js";
@@ -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, 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
  }
@@ -202,19 +205,28 @@ async function prepareAttachments(cwd, attachments) {
202
205
  const fileName = `${Date.now().toString(36)}-${safe}`;
203
206
  const filePath = path.join(dir, fileName);
204
207
  const buf = Buffer.from(a.data, "base64");
205
- // Validate image attachments before they become native image content blocks.
206
- // A corrupt or unsupported-format image (e.g. a malformed .ico, or a .png
207
- // with a bad IDAT) makes the provider reject the ENTIRE turn ("image data
208
- // ... not a valid image") before the agent can respond. Downgrade such files
209
- // to a plain "file" attachment so the model gets a path note and inspects
210
- // them with its tools instead of the request 400ing.
208
+ // Validate and cap image attachments before they become native image content
209
+ // blocks. Anthropic applies a 2000 px per-dimension cap once conversation
210
+ // history contains more than 20 images, so every attachment must be safe for
211
+ // later turns too. Keep the original on disk, but send the resized bytes.
212
+ // Corrupt or unsupported images become plain files so they cannot reject the
213
+ // entire provider request.
211
214
  let prepared = { ...a };
212
215
  if (a.kind === "image") {
213
- const validatedType = await validateVisionImage(buf).catch(() => null);
214
- if (validatedType)
215
- prepared = { ...a, mediaType: validatedType };
216
- else
216
+ try {
217
+ const resized = await shrinkToFit(buf, a.mediaType);
218
+ const validatedType = await validateVisionImage(resized.buffer);
219
+ prepared = validatedType
220
+ ? {
221
+ ...a,
222
+ mediaType: validatedType,
223
+ data: resized.buffer.toString("base64"),
224
+ }
225
+ : { ...a, kind: "file" };
226
+ }
227
+ catch {
217
228
  prepared = { ...a, kind: "file" };
229
+ }
218
230
  }
219
231
  try {
220
232
  await fs.writeFile(filePath, buf);
@@ -443,7 +455,11 @@ async function runJsonModeIfRequested() {
443
455
  maxTurns: maxTurnsRaw ? parseInt(maxTurnsRaw, 10) : undefined,
444
456
  allowedTools,
445
457
  promptCacheKey: values["prompt-cache-key"],
446
- }).catch((err) => {
458
+ }).catch(async (err) => {
459
+ captureSidecarError(err, "app-sidecar.json-mode", {
460
+ provider: String(values.provider ?? "anthropic"),
461
+ });
462
+ await flushSidecarErrors();
447
463
  process.stderr.write((err instanceof Error ? err.message : String(err)) + "\n");
448
464
  process.exit(1);
449
465
  });
@@ -581,6 +597,7 @@ async function main() {
581
597
  }
582
598
  catch (error) {
583
599
  const message = error instanceof Error ? error.message : String(error);
600
+ captureSidecarError(error, "app-sidecar.usage.fetch", { provider });
584
601
  log("WARN", "app-sidecar", "subscription usage fetch failed", { provider, message });
585
602
  if (error instanceof SubscriptionUsageError && error.status === 429) {
586
603
  usageRateLimitedUntil.set(provider, Date.now() + USAGE_RATE_LIMIT_BACKOFF_MS);
@@ -640,7 +657,7 @@ async function main() {
640
657
  return null;
641
658
  }
642
659
  }
643
- const server = http.createServer((req, res) => {
660
+ const server = http.createServer(wrapSidecarHandler((req, res) => {
644
661
  const url = req.url ?? "/";
645
662
  const method = req.method ?? "GET";
646
663
  // CORS preflight — the webview origin differs from 127.0.0.1.
@@ -684,6 +701,7 @@ async function main() {
684
701
  }
685
702
  catch (err) {
686
703
  const message = err instanceof Error ? err.message : String(err);
704
+ captureSidecarError(err, "app-sidecar.session.create");
687
705
  log("ERROR", "app-sidecar", "session create failed", { message });
688
706
  daemonJson(res, 500, { error: message });
689
707
  }
@@ -720,6 +738,7 @@ async function main() {
720
738
  }
721
739
  daemonJson(res, 200, await subscriptionUsage(provider));
722
740
  })().catch((error) => {
741
+ captureSidecarError(error, "app-sidecar.usage.request");
723
742
  log("ERROR", "app-sidecar", "subscription usage request failed", {
724
743
  message: error instanceof Error ? error.message : String(error),
725
744
  });
@@ -735,7 +754,7 @@ async function main() {
735
754
  return;
736
755
  }
737
756
  ctx.handle(req, res, url, method);
738
- });
757
+ }, "app-sidecar.http"));
739
758
  server.listen(port, host, () => {
740
759
  const addr = server.address();
741
760
  // The Rust shell reads this line to learn the daemon port.
@@ -880,6 +899,7 @@ async function createProgressManager(agentDir, broadcastAll) {
880
899
  broadcastAll(buildSnapshot(updated), originId);
881
900
  }
882
901
  catch (err) {
902
+ captureSidecarError(err, "app-sidecar.progress.award");
883
903
  log("DEBUG", "app-sidecar", "progress award failed", {
884
904
  message: err instanceof Error ? err.message : String(err),
885
905
  });
@@ -911,6 +931,7 @@ async function createProgressManager(agentDir, broadcastAll) {
911
931
  watcher.unref();
912
932
  }
913
933
  catch (err) {
934
+ captureSidecarError(err, "app-sidecar.progress.watch");
914
935
  log("DEBUG", "app-sidecar", "progress watch unavailable", {
915
936
  message: err instanceof Error ? err.message : String(err),
916
937
  });
@@ -1001,6 +1022,11 @@ async function createSession(deps, opts) {
1001
1022
  const f = formatError(err);
1002
1023
  const message = f.message ? desktopGuidance(f.message) : undefined;
1003
1024
  const guidance = desktopGuidance(f.guidance);
1025
+ captureSidecarError(err, `app-sidecar.${logLabel.replaceAll(" ", "-")}`, {
1026
+ scope: type,
1027
+ ...(f.provider ? { provider: f.provider } : {}),
1028
+ ...(f.statusCode != null ? { status: String(f.statusCode) } : {}),
1029
+ });
1004
1030
  log("ERROR", "app-sidecar", logLabel, {
1005
1031
  headline: f.headline,
1006
1032
  source: f.source,
@@ -1058,6 +1084,7 @@ async function createSession(deps, opts) {
1058
1084
  chatAgent = nextAgent;
1059
1085
  broadcast("chat_agent_change", { chatAgent: nextAgent });
1060
1086
  await session.persistAppMarker("agent_handoff", { chatAgent: nextAgent }).catch((error) => {
1087
+ captureSidecarError(error, "app-sidecar.chat-agent.persist-handoff");
1061
1088
  log("WARN", "app-sidecar", "agent handoff marker persist failed", {
1062
1089
  message: error instanceof Error ? error.message : String(error),
1063
1090
  });
@@ -1176,6 +1203,7 @@ async function createSession(deps, opts) {
1176
1203
  .catch(() => false)
1177
1204
  .then(() => syncApprovedPlanProgress(generation))
1178
1205
  .catch((error) => {
1206
+ captureSidecarError(error, "app-sidecar.plan.refresh-progress");
1179
1207
  log("WARN", "app-sidecar", "plan progress refresh failed", {
1180
1208
  message: error instanceof Error ? error.message : String(error),
1181
1209
  });
@@ -1230,6 +1258,11 @@ async function createSession(deps, opts) {
1230
1258
  session.eventBus.on("tool_call_end", (d) => {
1231
1259
  const name = toolCallNames.get(d.toolCallId) ?? "unknown";
1232
1260
  toolCallNames.delete(d.toolCallId);
1261
+ if (d.isError) {
1262
+ // Tool output can contain private project data. Report the failure and tool
1263
+ // identity without shipping the raw result body to Error Mom.
1264
+ captureSidecarError(new Error(`Tool ${name} failed`), `tool.${name}`, { tool: name });
1265
+ }
1233
1266
  log(d.isError ? "ERROR" : "INFO", "tool", `Tool call ended: ${name}`, {
1234
1267
  id: d.toolCallId,
1235
1268
  durationMs: String(d.durationMs),
@@ -1570,6 +1603,7 @@ async function createSession(deps, opts) {
1570
1603
  catch (error) {
1571
1604
  // Keep tracking when prompt cleanup fails; hiding the widget here
1572
1605
  // would claim completion while the approved-plan contract remained.
1606
+ captureSidecarError(error, "app-sidecar.plan.cleanup");
1573
1607
  log("WARN", "app-sidecar", "completed plan cleanup failed", {
1574
1608
  message: error instanceof Error ? error.message : String(error),
1575
1609
  });
@@ -2049,7 +2083,10 @@ async function createSession(deps, opts) {
2049
2083
  void memoryStore
2050
2084
  .snapshot()
2051
2085
  .then((snapshot) => json(res, 200, snapshot))
2052
- .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
2086
+ .catch((error) => {
2087
+ captureSidecarError(error, "app-sidecar.memory.snapshot");
2088
+ json(res, 500, { error: error instanceof Error ? error.message : String(error) });
2089
+ });
2053
2090
  return;
2054
2091
  }
2055
2092
  if (method === "DELETE" && url.startsWith("/memories/")) {
@@ -2062,14 +2099,20 @@ async function createSession(deps, opts) {
2062
2099
  .forget(id)
2063
2100
  .then(() => memoryStore.snapshot())
2064
2101
  .then((snapshot) => json(res, 200, snapshot))
2065
- .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
2102
+ .catch((error) => {
2103
+ captureSidecarError(error, "app-sidecar.memory.forget");
2104
+ json(res, 500, { error: error instanceof Error ? error.message : String(error) });
2105
+ });
2066
2106
  return;
2067
2107
  }
2068
2108
  if (method === "GET" && url === "/jiwa") {
2069
2109
  void jiwaStore
2070
2110
  .snapshot()
2071
2111
  .then((snapshot) => json(res, 200, snapshot))
2072
- .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
2112
+ .catch((error) => {
2113
+ captureSidecarError(error, "app-sidecar.jiwa.snapshot");
2114
+ json(res, 500, { error: error instanceof Error ? error.message : String(error) });
2115
+ });
2073
2116
  return;
2074
2117
  }
2075
2118
  if (method === "DELETE" && url.startsWith("/jiwa/")) {
@@ -2082,7 +2125,10 @@ async function createSession(deps, opts) {
2082
2125
  .forget(id)
2083
2126
  .then(() => jiwaStore.snapshot())
2084
2127
  .then((snapshot) => json(res, 200, snapshot))
2085
- .catch((error) => json(res, 500, { error: error instanceof Error ? error.message : String(error) }));
2128
+ .catch((error) => {
2129
+ captureSidecarError(error, "app-sidecar.jiwa.forget");
2130
+ json(res, 500, { error: error instanceof Error ? error.message : String(error) });
2131
+ });
2086
2132
  return;
2087
2133
  }
2088
2134
  if (method === "GET" && (url === "/events" || url.startsWith("/events?"))) {
@@ -2195,6 +2241,7 @@ async function createSession(deps, opts) {
2195
2241
  json(res, 200, { path: dir });
2196
2242
  }
2197
2243
  catch (err) {
2244
+ captureSidecarError(err, "app-sidecar.project.create");
2198
2245
  json(res, 500, { error: err instanceof Error ? err.message : String(err) });
2199
2246
  }
2200
2247
  });
@@ -2205,6 +2252,7 @@ async function createSession(deps, opts) {
2205
2252
  void discoverProjects()
2206
2253
  .then((projects) => json(res, 200, { projects }))
2207
2254
  .catch((err) => {
2255
+ captureSidecarError(err, "app-sidecar.projects.discover");
2208
2256
  log("ERROR", "app-sidecar", "discoverProjects failed", {
2209
2257
  message: err instanceof Error ? err.message : String(err),
2210
2258
  });
@@ -2238,7 +2286,10 @@ async function createSession(deps, opts) {
2238
2286
  .map(({ item }) => item);
2239
2287
  json(res, 200, { sessions: recent });
2240
2288
  })
2241
- .catch(() => json(res, 200, { sessions: [] }));
2289
+ .catch((error) => {
2290
+ captureSidecarError(error, "app-sidecar.sessions.list-chat");
2291
+ json(res, 200, { sessions: [] });
2292
+ });
2242
2293
  return;
2243
2294
  }
2244
2295
  // The picker may be served by a sidecar currently running in Chat mode.
@@ -2249,7 +2300,10 @@ async function createSession(deps, opts) {
2249
2300
  : paths.sessionsDir;
2250
2301
  void listRecentSessions(target, 5, sessionsDir)
2251
2302
  .then((sessions) => json(res, 200, { sessions }))
2252
- .catch(() => json(res, 200, { sessions: [] }));
2303
+ .catch((error) => {
2304
+ captureSidecarError(error, "app-sidecar.sessions.list");
2305
+ json(res, 200, { sessions: [] });
2306
+ });
2253
2307
  return;
2254
2308
  }
2255
2309
  if (method === "GET" && url.startsWith("/files")) {
@@ -2257,6 +2311,7 @@ async function createSession(deps, opts) {
2257
2311
  void searchProjectFiles(cwd, q)
2258
2312
  .then((files) => json(res, 200, { files }))
2259
2313
  .catch((err) => {
2314
+ captureSidecarError(err, "app-sidecar.files.search");
2260
2315
  log("ERROR", "app-sidecar", "searchProjectFiles failed", {
2261
2316
  message: err instanceof Error ? err.message : String(err),
2262
2317
  });
@@ -2824,6 +2879,7 @@ async function createSession(deps, opts) {
2824
2879
  }
2825
2880
  catch (err) {
2826
2881
  const message = err instanceof Error ? err.message : String(err);
2882
+ captureSidecarError(err, "app-sidecar.prompt-enhancer");
2827
2883
  log("ERROR", "app-sidecar", "enhance failed", { message });
2828
2884
  json(res, 500, { error: message });
2829
2885
  }
@@ -3149,6 +3205,7 @@ async function createSession(deps, opts) {
3149
3205
  drained,
3150
3206
  });
3151
3207
  })().catch((error) => {
3208
+ captureSidecarError(error, "app-sidecar.run.cancel");
3152
3209
  broadcast("cancel_failed", { error: "cancel_failed", runState: runLifecycle.state });
3153
3210
  json(res, 500, {
3154
3211
  error: "cancel_failed",
@@ -3176,6 +3233,7 @@ async function createSession(deps, opts) {
3176
3233
  json(res, 200, { ok: true });
3177
3234
  })
3178
3235
  .catch((err) => {
3236
+ captureSidecarError(err, "app-sidecar.session.new");
3179
3237
  json(res, 500, { error: err instanceof Error ? err.message : String(err) });
3180
3238
  });
3181
3239
  return;
@@ -3230,6 +3288,7 @@ async function createSession(deps, opts) {
3230
3288
  json(res, 200, { ok: true, planTotal });
3231
3289
  }
3232
3290
  catch (err) {
3291
+ captureSidecarError(err, "app-sidecar.plan.accept");
3233
3292
  json(res, 500, { error: err instanceof Error ? err.message : String(err) });
3234
3293
  }
3235
3294
  });
@@ -3326,6 +3385,7 @@ async function createSession(deps, opts) {
3326
3385
  broadcast("auth_done", { provider });
3327
3386
  }
3328
3387
  catch (err) {
3388
+ captureSidecarError(err, "app-sidecar.auth.oauth", { provider });
3329
3389
  broadcast("auth_error", {
3330
3390
  provider,
3331
3391
  message: err instanceof Error ? err.message : String(err),
@@ -3467,6 +3527,7 @@ async function createSession(deps, opts) {
3467
3527
  }
3468
3528
  catch (err) {
3469
3529
  serveController = null;
3530
+ captureSidecarError(err, "app-sidecar.serve.start", { provider: st.provider });
3470
3531
  json(res, 400, { error: err instanceof Error ? err.message : String(err) });
3471
3532
  }
3472
3533
  })();
@@ -3493,6 +3554,7 @@ async function createSession(deps, opts) {
3493
3554
  void buildMcpRows(targetCwd)
3494
3555
  .then((servers) => json(res, 200, { servers }))
3495
3556
  .catch((err) => {
3557
+ captureSidecarError(err, "app-sidecar.mcp.list");
3496
3558
  log("ERROR", "app-sidecar", "buildMcpRows failed", {
3497
3559
  message: err instanceof Error ? err.message : String(err),
3498
3560
  });
@@ -3549,6 +3611,7 @@ async function createSession(deps, opts) {
3549
3611
  });
3550
3612
  }
3551
3613
  catch (err) {
3614
+ captureSidecarError(err, "app-sidecar.mcp.add", { server: config.name });
3552
3615
  json(res, 500, {
3553
3616
  error: err instanceof Error ? err.message : String(err),
3554
3617
  });
@@ -3638,6 +3701,7 @@ async function createSession(deps, opts) {
3638
3701
  }
3639
3702
  }
3640
3703
  catch (err) {
3704
+ captureSidecarError(err, "app-sidecar.mcp.login", { server: name });
3641
3705
  broadcast("mcp_auth_error", {
3642
3706
  name,
3643
3707
  message: err instanceof Error ? err.message : String(err),
@@ -3684,7 +3748,9 @@ async function createSession(deps, opts) {
3684
3748
  dispose,
3685
3749
  };
3686
3750
  }
3687
- main().catch((err) => {
3751
+ main().catch(async (err) => {
3752
+ captureSidecarError(err, "app-sidecar.main", { severity: "fatal" });
3753
+ await flushSidecarErrors();
3688
3754
  const message = err instanceof Error ? err.message : String(err);
3689
3755
  process.stderr.write(`GG_APP_FATAL ${message}\n`);
3690
3756
  process.exit(1);