@kenkaiiii/ggcoder 5.60.2 → 5.60.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.
- package/dist/app-sidecar.js +6 -2
- package/dist/app-sidecar.js.map +1 -1
- package/dist/core/agent-session.d.ts +29 -0
- package/dist/core/agent-session.js +139 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/thinking-level.d.ts +1 -1
- package/dist/core/thinking-level.js +1 -1
- package/dist/core/thinking-level.js.map +1 -1
- package/dist/system-prompt.js +10 -11
- package/dist/system-prompt.js.map +1 -1
- package/package.json +4 -4
|
@@ -255,6 +255,15 @@ export declare class AgentSession {
|
|
|
255
255
|
* only on a real edge. */
|
|
256
256
|
private verificationArmed;
|
|
257
257
|
private compactionOccurred;
|
|
258
|
+
/**
|
|
259
|
+
* Re-grounding carry-over for post-turn compaction. `resetHookState` clears
|
|
260
|
+
* `compactionOccurred` at run start — before the injection point — so a
|
|
261
|
+
* background compaction that finished after the final response parks its
|
|
262
|
+
* "re-ground next turn" signal here for pickup at the next run start.
|
|
263
|
+
*/
|
|
264
|
+
private compactionArmedForNextRun;
|
|
265
|
+
/** In-flight post-turn (background) compaction, if any. */
|
|
266
|
+
private postTurnCompaction?;
|
|
258
267
|
private lastCompactionCompacted;
|
|
259
268
|
private compactionRetryAfter;
|
|
260
269
|
/** A restored oversized checkpoint must be canonicalized before its first prompt is persisted. */
|
|
@@ -569,6 +578,26 @@ export declare class AgentSession {
|
|
|
569
578
|
/** Canonicalize a deferred restore before a new prompt can fork stale history. */
|
|
570
579
|
private adoptDeferredCheckpointBeforePrompt;
|
|
571
580
|
private persistCompactionCheckpoint;
|
|
581
|
+
/**
|
|
582
|
+
* Wait out any in-flight post-turn background compaction before a new
|
|
583
|
+
* entry point mutates `this.messages` (prompt, attachments, model switch).
|
|
584
|
+
* The background compact() snapshots and then REPLACES the array, so a
|
|
585
|
+
* message pushed while it runs would be silently dropped from the live
|
|
586
|
+
* history — the pre-run await in runLoop() sits after the push and cannot
|
|
587
|
+
* protect it.
|
|
588
|
+
*/
|
|
589
|
+
private settlePostTurnCompaction;
|
|
590
|
+
/**
|
|
591
|
+
* Post-turn compaction: once the final response has been delivered, compact
|
|
592
|
+
* in the background while the user reads the answer, instead of making the
|
|
593
|
+
* next prompt pay the summarizer latency up front (the pre-run path stays as
|
|
594
|
+
* the backstop, so a skipped or failed attempt costs nothing). Mirrors the
|
|
595
|
+
* Codex `model_post_turn_compact_threshold_percent` guards: skip when user
|
|
596
|
+
* input is already queued (it would race the next turn), when the run was
|
|
597
|
+
* aborted, or during the failure cooldown — and never let a compaction
|
|
598
|
+
* error surface in the completed turn.
|
|
599
|
+
*/
|
|
600
|
+
private maybeCompactPostTurn;
|
|
572
601
|
compact(existingCredentials?: {
|
|
573
602
|
accessToken: string;
|
|
574
603
|
accountId?: string;
|
|
@@ -37,6 +37,7 @@ import { log } from "./logger.js";
|
|
|
37
37
|
import { setEstimatorModel, calibrateEstimatorFromUsage } from "./compaction/token-estimator.js";
|
|
38
38
|
import { calculateActiveContextTokens } from "./compaction/active-context.js";
|
|
39
39
|
import { resolveCompactionPolicy } from "./compaction/policy.js";
|
|
40
|
+
import { clampThinkingForPlanMode } from "./thinking-level.js";
|
|
40
41
|
import { pruneStaleToolResults } from "./compaction/tool-result-pruner.js";
|
|
41
42
|
import { discoverAgents } from "./agents.js";
|
|
42
43
|
import { enhancePrompt } from "../utils/prompt-enhancer.js";
|
|
@@ -199,6 +200,15 @@ export class AgentSession {
|
|
|
199
200
|
* only on a real edge. */
|
|
200
201
|
verificationArmed = false;
|
|
201
202
|
compactionOccurred = false;
|
|
203
|
+
/**
|
|
204
|
+
* Re-grounding carry-over for post-turn compaction. `resetHookState` clears
|
|
205
|
+
* `compactionOccurred` at run start — before the injection point — so a
|
|
206
|
+
* background compaction that finished after the final response parks its
|
|
207
|
+
* "re-ground next turn" signal here for pickup at the next run start.
|
|
208
|
+
*/
|
|
209
|
+
compactionArmedForNextRun = false;
|
|
210
|
+
/** In-flight post-turn (background) compaction, if any. */
|
|
211
|
+
postTurnCompaction;
|
|
202
212
|
lastCompactionCompacted = false;
|
|
203
213
|
compactionRetryAfter = 0;
|
|
204
214
|
/** A restored oversized checkpoint must be canonicalized before its first prompt is persisted. */
|
|
@@ -895,6 +905,7 @@ export class AgentSession {
|
|
|
895
905
|
kind: "prompt",
|
|
896
906
|
visibility: "transcript",
|
|
897
907
|
}, options = {}) {
|
|
908
|
+
await this.settlePostTurnCompaction();
|
|
898
909
|
await this.adoptDeferredCheckpointBeforePrompt();
|
|
899
910
|
const slash = await this.resolveSlashInput(content);
|
|
900
911
|
if (slash?.kind === "template") {
|
|
@@ -929,6 +940,7 @@ export class AgentSession {
|
|
|
929
940
|
* attachments are always a direct conversational turn.
|
|
930
941
|
*/
|
|
931
942
|
async promptWithAttachments(text, attachments) {
|
|
943
|
+
await this.settlePostTurnCompaction();
|
|
932
944
|
await this.adoptDeferredCheckpointBeforePrompt();
|
|
933
945
|
const parts = this.buildAttachmentParts(text, attachments);
|
|
934
946
|
if (parts.length === 0)
|
|
@@ -1069,6 +1081,13 @@ export class AgentSession {
|
|
|
1069
1081
|
this.backgroundVerification.delete(id);
|
|
1070
1082
|
}
|
|
1071
1083
|
this.compactionOccurred = false;
|
|
1084
|
+
// Post-turn compaction may have landed between runs — adopt its armed
|
|
1085
|
+
// signal so the re-grounding hook fires for this run exactly as it would
|
|
1086
|
+
// after a pre-run compaction.
|
|
1087
|
+
if (this.compactionArmedForNextRun) {
|
|
1088
|
+
this.compactionArmedForNextRun = false;
|
|
1089
|
+
this.compactionOccurred = true;
|
|
1090
|
+
}
|
|
1072
1091
|
this.originalRequest = originalRequest;
|
|
1073
1092
|
}
|
|
1074
1093
|
/**
|
|
@@ -1936,6 +1955,11 @@ export class AgentSession {
|
|
|
1936
1955
|
// of the public API model window. Failed/no-op attempts cool down across
|
|
1937
1956
|
// prompts; provider overflow recovery still bypasses this path entirely.
|
|
1938
1957
|
if (this.settingsManager.get("autoCompact") && Date.now() >= this.compactionRetryAfter) {
|
|
1958
|
+
// A post-turn compaction may still be running in the background. Let it
|
|
1959
|
+
// settle first: it usually already compacted this history, so the
|
|
1960
|
+
// decision below turns into a no-op instead of duplicate work.
|
|
1961
|
+
if (this.postTurnCompaction)
|
|
1962
|
+
await this.postTurnCompaction;
|
|
1939
1963
|
const contextWindow = getContextWindow(this.model, {
|
|
1940
1964
|
provider: this.provider,
|
|
1941
1965
|
accountId: creds.accountId,
|
|
@@ -2003,7 +2027,13 @@ export class AgentSession {
|
|
|
2003
2027
|
maxTokens: this.maxTokens,
|
|
2004
2028
|
maxTurns: this.opts.maxTurns,
|
|
2005
2029
|
maxTurnExtensions: this.opts.maxTurnExtensions,
|
|
2006
|
-
|
|
2030
|
+
// Plan mode caps effort at medium (Codex `plan_mode_reasoning_effort`
|
|
2031
|
+
// preset): read-only exploration doesn't need xhigh/max reasoning, and
|
|
2032
|
+
// deep-reasoning models left at the ceiling burn enormous thinking
|
|
2033
|
+
// budgets re-deriving context they cannot act on.
|
|
2034
|
+
thinking: this.planModeRef.current
|
|
2035
|
+
? clampThinkingForPlanMode(this.thinkingLevel)
|
|
2036
|
+
: this.thinkingLevel,
|
|
2007
2037
|
apiKey,
|
|
2008
2038
|
// Per-turn credential resolution. A run can span many minutes; if any
|
|
2009
2039
|
// process sharing auth.json refreshes this grant meanwhile, the token
|
|
@@ -2269,8 +2299,15 @@ export class AgentSession {
|
|
|
2269
2299
|
await this.persistMessage(this.messages[i]);
|
|
2270
2300
|
}
|
|
2271
2301
|
this.lastPersistedIndex = this.messages.length;
|
|
2302
|
+
// Final response is delivered — compact in the background while the user
|
|
2303
|
+
// reads it, instead of charging the summarizer latency to their next
|
|
2304
|
+
// prompt. Detached by design; the pre-run path above remains the backstop.
|
|
2305
|
+
this.maybeCompactPostTurn(creds);
|
|
2272
2306
|
}
|
|
2273
2307
|
async switchModel(provider, model) {
|
|
2308
|
+
// The model-switch note is appended to `this.messages`; settle any
|
|
2309
|
+
// background compaction first so the note cannot be swapped out.
|
|
2310
|
+
await this.settlePostTurnCompaction();
|
|
2274
2311
|
const prevProvider = this.provider;
|
|
2275
2312
|
const prevModel = this.model;
|
|
2276
2313
|
// Diff gate: a "switch" to the model already in use is not state change.
|
|
@@ -2471,6 +2508,101 @@ export class AgentSession {
|
|
|
2471
2508
|
newCount: result.newCount,
|
|
2472
2509
|
});
|
|
2473
2510
|
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Wait out any in-flight post-turn background compaction before a new
|
|
2513
|
+
* entry point mutates `this.messages` (prompt, attachments, model switch).
|
|
2514
|
+
* The background compact() snapshots and then REPLACES the array, so a
|
|
2515
|
+
* message pushed while it runs would be silently dropped from the live
|
|
2516
|
+
* history — the pre-run await in runLoop() sits after the push and cannot
|
|
2517
|
+
* protect it.
|
|
2518
|
+
*/
|
|
2519
|
+
async settlePostTurnCompaction() {
|
|
2520
|
+
if (this.postTurnCompaction)
|
|
2521
|
+
await this.postTurnCompaction;
|
|
2522
|
+
}
|
|
2523
|
+
/**
|
|
2524
|
+
* Post-turn compaction: once the final response has been delivered, compact
|
|
2525
|
+
* in the background while the user reads the answer, instead of making the
|
|
2526
|
+
* next prompt pay the summarizer latency up front (the pre-run path stays as
|
|
2527
|
+
* the backstop, so a skipped or failed attempt costs nothing). Mirrors the
|
|
2528
|
+
* Codex `model_post_turn_compact_threshold_percent` guards: skip when user
|
|
2529
|
+
* input is already queued (it would race the next turn), when the run was
|
|
2530
|
+
* aborted, or during the failure cooldown — and never let a compaction
|
|
2531
|
+
* error surface in the completed turn.
|
|
2532
|
+
*/
|
|
2533
|
+
maybeCompactPostTurn(creds) {
|
|
2534
|
+
if (!this.settingsManager.get("autoCompact"))
|
|
2535
|
+
return;
|
|
2536
|
+
if (this.opts.signal?.aborted)
|
|
2537
|
+
return;
|
|
2538
|
+
if (this.userQueue.length > 0)
|
|
2539
|
+
return;
|
|
2540
|
+
if (this.postTurnCompaction)
|
|
2541
|
+
return;
|
|
2542
|
+
if (Date.now() < this.compactionRetryAfter)
|
|
2543
|
+
return;
|
|
2544
|
+
// One compaction per turn boundary: a pre-run or overflow-recovery
|
|
2545
|
+
// compaction already shrank this run's history — re-probing right after
|
|
2546
|
+
// the final response would only re-derive that decision.
|
|
2547
|
+
if (this.compactionOccurred)
|
|
2548
|
+
return;
|
|
2549
|
+
const contextWindow = getContextWindow(this.model, {
|
|
2550
|
+
provider: this.provider,
|
|
2551
|
+
accountId: creds.accountId,
|
|
2552
|
+
});
|
|
2553
|
+
const policy = resolveCompactionPolicy({
|
|
2554
|
+
provider: this.provider,
|
|
2555
|
+
model: this.model,
|
|
2556
|
+
contextWindow,
|
|
2557
|
+
threshold: this.settingsManager.get("compactThreshold"),
|
|
2558
|
+
accountId: creds.accountId,
|
|
2559
|
+
approvedPlanPath: this.approvedPlanPath,
|
|
2560
|
+
});
|
|
2561
|
+
let activeTokens;
|
|
2562
|
+
if (this.providerContext) {
|
|
2563
|
+
const anchorIndex = this.messages.lastIndexOf(this.providerContext.anchor);
|
|
2564
|
+
if (anchorIndex >= 0) {
|
|
2565
|
+
activeTokens = calculateActiveContextTokens(this.messages, {
|
|
2566
|
+
usage: this.providerContext.usage,
|
|
2567
|
+
pendingMessages: this.messages.slice(anchorIndex + 1),
|
|
2568
|
+
});
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
if (!shouldCompact(this.messages, contextWindow, policy.threshold, activeTokens))
|
|
2572
|
+
return;
|
|
2573
|
+
log("INFO", "compaction", "Post-turn compaction decision — compacting in background", {
|
|
2574
|
+
provider: this.provider,
|
|
2575
|
+
model: this.model,
|
|
2576
|
+
transport: this.provider === "openai" && creds.accountId ? "codex_oauth" : "public_api",
|
|
2577
|
+
contextWindow: String(contextWindow),
|
|
2578
|
+
activeTokens: activeTokens === undefined ? "estimated" : String(activeTokens),
|
|
2579
|
+
triggerLimit: String(policy.targetTokens),
|
|
2580
|
+
});
|
|
2581
|
+
this.postTurnCompaction = (async () => {
|
|
2582
|
+
try {
|
|
2583
|
+
await this.compact(creds, "automatic");
|
|
2584
|
+
if (this.lastCompactionCompacted) {
|
|
2585
|
+
// Arm re-grounding for the next turn. resetHookState clears
|
|
2586
|
+
// compactionOccurred at run start, before the injection point, so
|
|
2587
|
+
// park the signal for pickup there.
|
|
2588
|
+
this.compactionArmedForNextRun = true;
|
|
2589
|
+
this.compactionRetryAfter = 0;
|
|
2590
|
+
}
|
|
2591
|
+
else {
|
|
2592
|
+
this.compactionRetryAfter = Date.now() + 30_000;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
catch (error) {
|
|
2596
|
+
this.compactionRetryAfter = Date.now() + 30_000;
|
|
2597
|
+
if (isAbortError(error) || this.opts.signal?.aborted)
|
|
2598
|
+
return;
|
|
2599
|
+
log("WARN", "compaction", `Post-turn compaction failed; cooling down for 30s: ${error instanceof Error ? error.message : String(error)}`);
|
|
2600
|
+
}
|
|
2601
|
+
finally {
|
|
2602
|
+
this.postTurnCompaction = undefined;
|
|
2603
|
+
}
|
|
2604
|
+
})();
|
|
2605
|
+
}
|
|
2474
2606
|
async compact(existingCredentials, mode = "manual",
|
|
2475
2607
|
/** User-stated focus (`/compact <focus>`): what must survive verbatim. */
|
|
2476
2608
|
focus) {
|
|
@@ -3445,6 +3577,12 @@ export class AgentSession {
|
|
|
3445
3577
|
return this.getPromptCacheKey();
|
|
3446
3578
|
}
|
|
3447
3579
|
async dispose() {
|
|
3580
|
+
// Quiesce any in-flight post-turn compaction BEFORE tearing down state:
|
|
3581
|
+
// the background compact() snapshots and replaces `this.messages`, so
|
|
3582
|
+
// letting it run past this point would checkpoint a near-empty history
|
|
3583
|
+
// and leak a junk session file after teardown.
|
|
3584
|
+
if (this.postTurnCompaction)
|
|
3585
|
+
await this.postTurnCompaction;
|
|
3448
3586
|
this.diagnosticsRecorder?.finalize();
|
|
3449
3587
|
this.managerAbortSignal?.removeEventListener("abort", this.managerAbortHandler);
|
|
3450
3588
|
this.processManager?.shutdownAll();
|