@kenkaiiii/ggcoder 5.79.1 → 5.79.2

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.
@@ -776,6 +776,13 @@ export declare class AgentSession {
776
776
  ok: false;
777
777
  error: string;
778
778
  }>;
779
+ /**
780
+ * Name a root change in the prompt only while there is no conversation yet.
781
+ * Mid-conversation the env-delta note tells the model instead: the roots sit
782
+ * in the Environment section, and rewriting it would invalidate every cached
783
+ * message after it (a goal attaching a folder re-sent ~75k tokens).
784
+ */
785
+ private refreshPromptForRoots;
779
786
  /**
780
787
  * Names to advertise as available-on-demand. A tool the model already
781
788
  * promoted lives in `this.tools` and carries its own schema, so it drops out
@@ -45,7 +45,7 @@ import { decidePlanStepCompaction, DEFAULT_CACHE_WRITE_READ_RATIO, PLAN_STEP_KEE
45
45
  import { extractPlanSteps, findCompletedMarkers } from "../utils/plan-steps.js";
46
46
  import { readFileSync } from "node:fs";
47
47
  import { clampThinkingForPlanMode } from "./thinking-level.js";
48
- import { pruneStaleToolResults } from "./compaction/tool-result-pruner.js";
48
+ import { pruneStaleToolResults, shouldPruneToolResults } from "./compaction/tool-result-pruner.js";
49
49
  import { discoverAgents } from "./agents.js";
50
50
  import { enhancePrompt } from "../utils/prompt-enhancer.js";
51
51
  import { detectLanguages, detectProjectStack } from "./language-detector.js";
@@ -1691,36 +1691,6 @@ export class AgentSession {
1691
1691
  if (!force) {
1692
1692
  if (!this.settingsManager.get("autoCompact"))
1693
1693
  return messages;
1694
- // Cheap stale-tool-output pruning before the expensive LLM
1695
- // compaction check. In-place mutation preserves anchors; drop the
1696
- // retained usage afterwards since it counted the pruned content.
1697
- const pruneResult = pruneStaleToolResults(messages);
1698
- if (pruneResult.pruned) {
1699
- this.cacheDiagnostics.noteEdit("tool_prune", pruneResult.freedTokens);
1700
- this.providerContext = null;
1701
- log("INFO", "compaction", "Pruned stale tool outputs", {
1702
- prunedResults: String(pruneResult.prunedResults),
1703
- compactedToolCalls: String(pruneResult.compactedToolCalls),
1704
- freedTokens: String(pruneResult.freedTokens),
1705
- });
1706
- }
1707
- if (Date.now() < this.compactionRetryAfter)
1708
- return messages;
1709
- // The turn's own usage also counted the pruned content — after a
1710
- // prune, fall back to estimating the (now smaller) history so the
1711
- // freed tokens actually defer the LLM compaction.
1712
- let usage = pruneResult.pruned ? undefined : transformOpts.usage;
1713
- let pendingMessages = transformOpts.pendingMessages;
1714
- if (!usage && this.providerContext) {
1715
- const anchorIndex = messages.lastIndexOf(this.providerContext.anchor);
1716
- if (anchorIndex >= 0) {
1717
- usage = this.providerContext.usage;
1718
- pendingMessages = messages.slice(anchorIndex + 1);
1719
- }
1720
- else {
1721
- this.providerContext = null;
1722
- }
1723
- }
1724
1694
  const contextWindow = getContextWindow(this.model, {
1725
1695
  provider: this.provider,
1726
1696
  accountId,
@@ -1733,10 +1703,48 @@ export class AgentSession {
1733
1703
  accountId,
1734
1704
  approvedPlanPath: this.approvedPlanPath,
1735
1705
  });
1736
- const activeTokens = calculateActiveContextTokens(messages, {
1737
- usage,
1738
- pendingMessages,
1739
- });
1706
+ // Provider usage when there is some, else an estimate of the history.
1707
+ const measureActiveTokens = () => {
1708
+ let usage = transformOpts.usage;
1709
+ let pendingMessages = transformOpts.pendingMessages;
1710
+ if (!usage && this.providerContext) {
1711
+ const anchorIndex = messages.lastIndexOf(this.providerContext.anchor);
1712
+ if (anchorIndex >= 0) {
1713
+ usage = this.providerContext.usage;
1714
+ pendingMessages = messages.slice(anchorIndex + 1);
1715
+ }
1716
+ else {
1717
+ this.providerContext = null;
1718
+ }
1719
+ }
1720
+ return calculateActiveContextTokens(messages, { usage, pendingMessages });
1721
+ };
1722
+ let activeTokens = measureActiveTokens();
1723
+ // Cheap stale-tool-output pruning before the expensive LLM
1724
+ // compaction check, once the context is big enough for the freed
1725
+ // tokens to be worth a cache rewrite. In-place mutation preserves
1726
+ // anchors; drop the retained usage afterwards since it counted the
1727
+ // pruned content.
1728
+ if (shouldPruneToolResults(activeTokens, policy.targetTokens)) {
1729
+ const pruneResult = pruneStaleToolResults(messages);
1730
+ if (pruneResult.pruned) {
1731
+ this.cacheDiagnostics.noteEdit("tool_prune", pruneResult.freedTokens);
1732
+ this.providerContext = null;
1733
+ log("INFO", "compaction", "Pruned stale tool outputs", {
1734
+ prunedResults: String(pruneResult.prunedResults),
1735
+ compactedToolCalls: String(pruneResult.compactedToolCalls),
1736
+ freedTokens: String(pruneResult.freedTokens),
1737
+ });
1738
+ // The turn's own usage also counted the pruned content — estimate
1739
+ // the (now smaller) history so the freed tokens actually defer
1740
+ // the LLM compaction.
1741
+ activeTokens = calculateActiveContextTokens(messages, {
1742
+ pendingMessages: transformOpts.pendingMessages,
1743
+ });
1744
+ }
1745
+ }
1746
+ if (Date.now() < this.compactionRetryAfter)
1747
+ return messages;
1740
1748
  // An approved plan runs as ONE run, so step boundaries are only
1741
1749
  // visible here, between model steps — not after the run ends.
1742
1750
  const planStep = this.observePlanStepProgress(messages, contextWindow, activeTokens);
@@ -2793,7 +2801,7 @@ export class AgentSession {
2793
2801
  return relative.startsWith("..") || path.isAbsolute(relative);
2794
2802
  });
2795
2803
  this.additionalRoots.push(resolved);
2796
- await this.rebuildSystemPromptInPlace();
2804
+ await this.refreshPromptForRoots();
2797
2805
  return { ok: true, root: resolved };
2798
2806
  }
2799
2807
  /** Remove an exact root previously added with `/add-dir`. */
@@ -2804,9 +2812,20 @@ export class AgentSession {
2804
2812
  return { ok: false, error: `Not an additional workspace root: ${resolved}` };
2805
2813
  }
2806
2814
  this.additionalRoots.splice(index, 1);
2807
- await this.rebuildSystemPromptInPlace();
2815
+ await this.refreshPromptForRoots();
2808
2816
  return { ok: true, root: resolved };
2809
2817
  }
2818
+ /**
2819
+ * Name a root change in the prompt only while there is no conversation yet.
2820
+ * Mid-conversation the env-delta note tells the model instead: the roots sit
2821
+ * in the Environment section, and rewriting it would invalidate every cached
2822
+ * message after it (a goal attaching a folder re-sent ~75k tokens).
2823
+ */
2824
+ async refreshPromptForRoots() {
2825
+ if (this.messages.some((message) => message.role !== "system"))
2826
+ return;
2827
+ await this.rebuildSystemPromptInPlace();
2828
+ }
2810
2829
  /**
2811
2830
  * Names to advertise as available-on-demand. A tool the model already
2812
2831
  * promoted lives in `this.tools` and carries its own schema, so it drops out