@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.
- package/dist/app-sidecar.js +53 -8
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +5 -2
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/shared.d.ts.map +1 -1
- package/dist/cli/shared.js +2 -0
- package/dist/cli/shared.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +7 -0
- package/dist/config.test.js.map +1 -1
- package/dist/core/agent-session-compaction.test.js +84 -1
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session-memory-tail.test.js +1 -1
- package/dist/core/agent-session-memory-tail.test.js.map +1 -1
- package/dist/core/agent-session-queue.test.js +1 -1
- package/dist/core/agent-session-queue.test.js.map +1 -1
- package/dist/core/agent-session.d.ts +11 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +59 -11
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-providers.d.ts.map +1 -1
- package/dist/core/auth-providers.js +14 -7
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/compaction/compactor.d.ts +9 -1
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +43 -9
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +48 -0
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/resolve-start.test.js +1 -0
- package/dist/core/resolve-start.test.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +1 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/subagent-manager.test.js +7 -3
- package/dist/core/subagent-manager.test.js.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/ModelSelector.d.ts.map +1 -1
- package/dist/ui/components/ModelSelector.js +1 -0
- package/dist/ui/components/ModelSelector.js.map +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +2 -1
- package/dist/ui/login.js.map +1 -1
- package/package.json +4 -4
package/dist/app-sidecar.js
CHANGED
|
@@ -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
|
-
"
|
|
71
|
+
"xai",
|
|
72
|
+
// China
|
|
72
73
|
"moonshot",
|
|
74
|
+
"glm",
|
|
73
75
|
"minimax",
|
|
76
|
+
"xiaomi",
|
|
74
77
|
"deepseek",
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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()
|
|
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",
|