@kenkaiiii/ggcoder 5.16.0 → 5.17.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 (99) hide show
  1. package/dist/app-sidecar.js +129 -44
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +15 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/agent-session-review-coverage.test.d.ts +2 -0
  7. package/dist/core/agent-session-review-coverage.test.d.ts.map +1 -0
  8. package/dist/core/agent-session-review-coverage.test.js +57 -0
  9. package/dist/core/agent-session-review-coverage.test.js.map +1 -0
  10. package/dist/core/agent-session.d.ts +11 -4
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +133 -22
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/compaction/compactor.test.js +1 -1
  15. package/dist/core/compaction/compactor.test.js.map +1 -1
  16. package/dist/core/event-bus.d.ts +2 -0
  17. package/dist/core/event-bus.d.ts.map +1 -1
  18. package/dist/core/event-bus.js.map +1 -1
  19. package/dist/core/ideal-review.d.ts +27 -0
  20. package/dist/core/ideal-review.d.ts.map +1 -1
  21. package/dist/core/ideal-review.js +64 -0
  22. package/dist/core/ideal-review.js.map +1 -1
  23. package/dist/core/ideal-review.test.js +34 -1
  24. package/dist/core/ideal-review.test.js.map +1 -1
  25. package/dist/core/lsp/client.d.ts +3 -0
  26. package/dist/core/lsp/client.d.ts.map +1 -1
  27. package/dist/core/lsp/client.js +20 -0
  28. package/dist/core/lsp/client.js.map +1 -1
  29. package/dist/core/lsp/manager.d.ts +32 -9
  30. package/dist/core/lsp/manager.d.ts.map +1 -1
  31. package/dist/core/lsp/manager.js +89 -37
  32. package/dist/core/lsp/manager.js.map +1 -1
  33. package/dist/core/lsp/manager.test.js +56 -4
  34. package/dist/core/lsp/manager.test.js.map +1 -1
  35. package/dist/core/run-lifecycle.d.ts +40 -0
  36. package/dist/core/run-lifecycle.d.ts.map +1 -0
  37. package/dist/core/run-lifecycle.js +95 -0
  38. package/dist/core/run-lifecycle.js.map +1 -0
  39. package/dist/core/run-lifecycle.test.d.ts +2 -0
  40. package/dist/core/run-lifecycle.test.d.ts.map +1 -0
  41. package/dist/core/run-lifecycle.test.js +64 -0
  42. package/dist/core/run-lifecycle.test.js.map +1 -0
  43. package/dist/core/session-manager.d.ts +25 -1
  44. package/dist/core/session-manager.d.ts.map +1 -1
  45. package/dist/core/session-manager.js +66 -1
  46. package/dist/core/session-manager.js.map +1 -1
  47. package/dist/core/session-manager.test.js +80 -2
  48. package/dist/core/session-manager.test.js.map +1 -1
  49. package/dist/core/subagent-manager.d.ts +40 -3
  50. package/dist/core/subagent-manager.d.ts.map +1 -1
  51. package/dist/core/subagent-manager.js +342 -30
  52. package/dist/core/subagent-manager.js.map +1 -1
  53. package/dist/core/subagent-manager.test.js +189 -3
  54. package/dist/core/subagent-manager.test.js.map +1 -1
  55. package/dist/core/subagent-store.d.ts +20 -0
  56. package/dist/core/subagent-store.d.ts.map +1 -0
  57. package/dist/core/subagent-store.js +177 -0
  58. package/dist/core/subagent-store.js.map +1 -0
  59. package/dist/core/subagent-store.test.d.ts +2 -0
  60. package/dist/core/subagent-store.test.d.ts.map +1 -0
  61. package/dist/core/subagent-store.test.js +94 -0
  62. package/dist/core/subagent-store.test.js.map +1 -0
  63. package/dist/modes/agent-home-mode.d.ts.map +1 -1
  64. package/dist/modes/agent-home-mode.js +10 -3
  65. package/dist/modes/agent-home-mode.js.map +1 -1
  66. package/dist/modes/serve-mode.d.ts.map +1 -1
  67. package/dist/modes/serve-mode.js +10 -3
  68. package/dist/modes/serve-mode.js.map +1 -1
  69. package/dist/modes/subagent-worker-mode.d.ts +2 -0
  70. package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
  71. package/dist/modes/subagent-worker-mode.js +13 -3
  72. package/dist/modes/subagent-worker-mode.js.map +1 -1
  73. package/dist/tools/tasks.d.ts +1 -1
  74. package/dist/tools/web-fetch.d.ts +1 -1
  75. package/dist/ui/App.d.ts +7 -1
  76. package/dist/ui/App.d.ts.map +1 -1
  77. package/dist/ui/App.js +63 -17
  78. package/dist/ui/App.js.map +1 -1
  79. package/dist/ui/components/Footer.d.ts +1 -0
  80. package/dist/ui/components/Footer.d.ts.map +1 -1
  81. package/dist/ui/components/Footer.js +3 -3
  82. package/dist/ui/components/Footer.js.map +1 -1
  83. package/dist/ui/components/Footer.test.d.ts +2 -0
  84. package/dist/ui/components/Footer.test.d.ts.map +1 -0
  85. package/dist/ui/components/Footer.test.js +16 -0
  86. package/dist/ui/components/Footer.test.js.map +1 -0
  87. package/dist/ui/hooks/useAgentLoop.d.ts +8 -3
  88. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  89. package/dist/ui/hooks/useAgentLoop.js +64 -8
  90. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  91. package/dist/ui/hooks/useSessionPersistence.d.ts +4 -2
  92. package/dist/ui/hooks/useSessionPersistence.d.ts.map +1 -1
  93. package/dist/ui/hooks/useSessionPersistence.js +7 -1
  94. package/dist/ui/hooks/useSessionPersistence.js.map +1 -1
  95. package/dist/ui/render.d.ts +7 -0
  96. package/dist/ui/render.d.ts.map +1 -1
  97. package/dist/ui/render.js +4 -0
  98. package/dist/ui/render.js.map +1 -1
  99. package/package.json +4 -4
package/dist/ui/App.js CHANGED
@@ -12,6 +12,7 @@ import { useTaskBarStore, useTaskBarPolling, focusTaskBar, exitTaskBar, expandTa
12
12
  import { playNotificationSound } from "../utils/sound.js";
13
13
  import {} from "@kenkaiiii/gg-ai";
14
14
  import { downscaleForPreview, extractMediaPaths } from "../utils/image.js";
15
+ import { buildSubAgentCompletionFollowUp, } from "../core/subagent-manager.js";
15
16
  import { useAgentLoop } from "./hooks/useAgentLoop.js";
16
17
  import { useTranscriptHistory } from "./hooks/useTranscriptHistory.js";
17
18
  import { createWebSearchTool } from "../tools/web-search.js";
@@ -247,6 +248,7 @@ export function App(props) {
247
248
  const sessionManagerRef = useRef(props.sessionsDir ? new SessionManager(props.sessionsDir) : null);
248
249
  const sessionPathRef = useRef(props.sessionStore?.sessionPath ?? props.sessionPath);
249
250
  const persistedIndexRef = useRef(messagesRef.current.length);
251
+ const turnMetricsRef = useRef(props.sessionStore?.turnMetrics ?? []);
250
252
  const sessionStatsRef = useRef(props.sessionStore?.sessionStats ??
251
253
  createSessionStats({ sessionId: props.sessionStore?.sessionId ?? props.sessionId }));
252
254
  const [idealReviewEnabled, setIdealReviewEnabled] = useState(props.sessionStore?.idealReviewEnabled ?? props.idealReviewEnabled ?? true);
@@ -410,9 +412,9 @@ export function App(props) {
410
412
  .map((key) => props.credentialsByProvider?.[key])
411
413
  .find((c) => c !== undefined);
412
414
  const activeApiKey = currentCreds?.accessToken ?? props.apiKey;
413
- const activeAccountId = currentCreds?.accountId ?? props.accountId;
414
- const activeProjectId = currentCreds?.projectId ?? props.projectId;
415
- const activeBaseUrl = currentProvider === "gemini" ? undefined : (currentCreds?.baseUrl ?? props.baseUrl);
415
+ const activeAccountId = currentCreds ? currentCreds.accountId : props.accountId;
416
+ const activeProjectId = currentCreds ? currentCreds.projectId : props.projectId;
417
+ const activeBaseUrl = currentProvider === "gemini" ? undefined : currentCreds ? currentCreds.baseUrl : props.baseUrl;
416
418
  const contextWindowOptions = useMemo(() => ({ provider: currentProvider, accountId: activeAccountId }), [currentProvider, activeAccountId]);
417
419
  // Load git branch — re-runs whenever the displayed cwd changes.
418
420
  useEffect(() => {
@@ -544,16 +546,19 @@ export function App(props) {
544
546
  useEffect(() => {
545
547
  void applyLanguageDetectionRef.current("initial");
546
548
  }, []);
549
+ const rebindSubagentsAfterCompaction = useCallback((sessionId) => props.subAgentManager?.rebindParentSession(sessionId) ?? Promise.resolve(), [props.subAgentManager]);
547
550
  const { persistCompactedSession, persistNewMessages } = useSessionPersistence({
548
551
  sessionManagerRef,
549
552
  sessionPathRef,
550
553
  sessionStatsRef,
551
554
  persistedIndexRef,
552
555
  messagesRef,
556
+ turnMetricsRef,
553
557
  cwdRef,
554
558
  currentProvider,
555
559
  currentModel,
556
560
  sessionStore,
561
+ onCompactedSession: rebindSubagentsAfterCompaction,
557
562
  });
558
563
  /**
559
564
  * Run the language detector against the current cwd. If the detected set is a
@@ -657,6 +662,8 @@ export function App(props) {
657
662
  projectId: activeProjectId,
658
663
  resolveCredentials,
659
664
  transformContext,
665
+ lspManager: props.lspManager,
666
+ reviewCoverageTracker: props.reviewCoverageTracker,
660
667
  getIdealReviewMessage: (stats, touchedFiles) => {
661
668
  if (!idealReviewEnabledRef.current)
662
669
  return null;
@@ -1243,14 +1250,37 @@ export function App(props) {
1243
1250
  return remaining;
1244
1251
  });
1245
1252
  }, [queueFlush]),
1246
- onTurnEnd: useCallback((turn, stopReason, usage) => {
1253
+ onTurnEnd: useCallback((turn, stopReason, usage, timing) => {
1247
1254
  recordTurnEnd(sessionStatsRef.current, usage);
1255
+ const metric = {
1256
+ version: 1,
1257
+ turn,
1258
+ provider: currentProvider,
1259
+ model: currentModel,
1260
+ stopReason,
1261
+ usage: { ...usage },
1262
+ timing: { ...timing },
1263
+ cost: {
1264
+ status: "unavailable",
1265
+ reason: "No authoritative effective-dated provider pricing is available",
1266
+ },
1267
+ };
1268
+ turnMetricsRef.current.push(metric);
1269
+ if (sessionStore)
1270
+ sessionStore.turnMetrics = [...turnMetricsRef.current];
1271
+ const metricSessionPath = sessionPathRef.current;
1272
+ const metricManager = sessionManagerRef.current;
1273
+ if (metricSessionPath && metricManager) {
1274
+ void metricManager.appendTurnMetric(metricSessionPath, metric);
1275
+ }
1248
1276
  log("INFO", "turn", `Turn ${turn} ended`, {
1249
1277
  stopReason,
1250
1278
  inputTokens: String(usage.inputTokens),
1251
1279
  outputTokens: String(usage.outputTokens),
1252
1280
  ...(usage.cacheRead != null && { cacheRead: String(usage.cacheRead) }),
1253
1281
  ...(usage.cacheWrite != null && { cacheWrite: String(usage.cacheWrite) }),
1282
+ providerDurationMs: String(timing.providerDurationMs),
1283
+ ...(timing.ttftMs != null && { ttftMs: String(timing.ttftMs) }),
1254
1284
  });
1255
1285
  // Track actual token count for compaction decisions.
1256
1286
  // Anthropic has separate input/output limits — only count input.
@@ -1268,7 +1298,7 @@ export function App(props) {
1268
1298
  }
1269
1299
  return remaining;
1270
1300
  });
1271
- }, [queueFlush]),
1301
+ }, [currentModel, currentProvider, queueFlush, sessionStore]),
1272
1302
  onDone: useCallback((durationMs, toolsUsed, runStats) => {
1273
1303
  log("INFO", "agent", `Agent done`, {
1274
1304
  duration: `${durationMs}ms`,
@@ -1398,6 +1428,9 @@ export function App(props) {
1398
1428
  // natural completion boundary regardless. The stuck-guard caps
1399
1429
  // nudges per step so a genuinely blocked agent surfaces.
1400
1430
  getFollowUpMessages: useCallback(() => {
1431
+ const childCompletionFollowUp = buildSubAgentCompletionFollowUp(props.subAgentManager);
1432
+ if (childCompletionFollowUp)
1433
+ return childCompletionFollowUp;
1401
1434
  const steps = planStepsRef.current;
1402
1435
  if (steps.length === 0 || !approvedPlanPathRef.current)
1403
1436
  return null;
@@ -1421,7 +1454,7 @@ export function App(props) {
1421
1454
  `or you genuinely need user input.`,
1422
1455
  },
1423
1456
  ];
1424
- }, []),
1457
+ }, [props.subAgentManager]),
1425
1458
  onRetry: useCallback(() => {
1426
1459
  // Roll back any pending progressive flushes from the aborted attempt.
1427
1460
  // Without this, a stall retry regenerates the preamble and the old
@@ -2140,6 +2173,10 @@ export function App(props) {
2140
2173
  try {
2141
2174
  const session = await sm.create(taskCwd, currentProvider, currentModel);
2142
2175
  newSessionPath = session.path;
2176
+ sessionStatsRef.current.sessionId = session.id;
2177
+ if (props.sessionStore)
2178
+ props.sessionStore.sessionId = session.id;
2179
+ await props.subAgentManager?.resetParentSession(session.id);
2143
2180
  log("INFO", "tasks", "New session for task", { path: session.path });
2144
2181
  }
2145
2182
  catch {
@@ -2165,22 +2202,27 @@ export function App(props) {
2165
2202
  agentLoop.reset();
2166
2203
  persistedIndexRef.current = messagesRef.current.length;
2167
2204
  const sm = sessionManagerRef.current;
2168
- if (sm) {
2169
- void sm.create(taskCwd, currentProvider, currentModel).then((session) => {
2170
- sessionPathRef.current = session.path;
2171
- log("INFO", "tasks", "New session for task", { path: session.path });
2172
- });
2173
- }
2174
2205
  const taskItem = { kind: "task", title, id: getId() };
2175
2206
  setLastUserMessage(title);
2176
2207
  setDoneStatus(null);
2177
2208
  setLiveItems([taskItem]);
2178
- void agentLoop.run(fullPrompt).catch((err) => {
2179
- if (agentLoop.isRunning) {
2180
- agentLoop.reset();
2209
+ void (async () => {
2210
+ try {
2211
+ if (sm) {
2212
+ const session = await sm.create(taskCwd, currentProvider, currentModel);
2213
+ sessionPathRef.current = session.path;
2214
+ sessionStatsRef.current.sessionId = session.id;
2215
+ await props.subAgentManager?.resetParentSession(session.id);
2216
+ log("INFO", "tasks", "New session for task", { path: session.path });
2217
+ }
2218
+ await agentLoop.run(fullPrompt);
2181
2219
  }
2182
- setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
2183
- });
2220
+ catch (err) {
2221
+ if (agentLoop.isRunning)
2222
+ agentLoop.reset();
2223
+ setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
2224
+ }
2225
+ })();
2184
2226
  }, [agentLoop, currentModel, currentProvider, props]);
2185
2227
  // Keep the ref in sync so stale closures (task picker, onDone run-all) can
2186
2228
  // start a task without being recreated each render.
@@ -2549,6 +2591,10 @@ export function App(props) {
2549
2591
  if (sm) {
2550
2592
  const s = await sm.create(props.cwd, currentProvider, currentModel);
2551
2593
  newSessionPath = s.path;
2594
+ sessionStatsRef.current.sessionId = s.id;
2595
+ if (props.sessionStore)
2596
+ props.sessionStore.sessionId = s.id;
2597
+ await props.subAgentManager?.resetParentSession(s.id);
2552
2598
  }
2553
2599
  if (props.resetUI && props.sessionStore) {
2554
2600
  // Clear the overlay so the new mount lands on the chat,