@genesislcap/ai-assistant 15.22.2 → 15.23.1-FUI-0.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.
Files changed (36) hide show
  1. package/dist/ai-assistant.api.json +269 -5
  2. package/dist/ai-assistant.d.ts +256 -31
  3. package/dist/chat-driver.cjs +197 -84
  4. package/dist/chat-driver.cjs.map +2 -2
  5. package/dist/chat-driver.mjs +197 -84
  6. package/dist/chat-driver.mjs.map +2 -2
  7. package/dist/custom-elements.json +5483 -5364
  8. package/dist/dts/components/ai-driver/ai-driver.d.ts +63 -0
  9. package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
  10. package/dist/dts/components/chat-driver/chat-driver.d.ts +7 -0
  11. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  12. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +9 -1
  13. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  14. package/dist/dts/config/config.d.ts +53 -17
  15. package/dist/dts/config/config.d.ts.map +1 -1
  16. package/dist/dts/config/define-stateful-agent.d.ts +16 -1
  17. package/dist/dts/config/define-stateful-agent.d.ts.map +1 -1
  18. package/dist/dts/main/main.d.ts +102 -14
  19. package/dist/dts/main/main.d.ts.map +1 -1
  20. package/dist/dts/react.d.ts +12 -12
  21. package/dist/dts/utils/context-tokens.d.ts +100 -43
  22. package/dist/dts/utils/context-tokens.d.ts.map +1 -1
  23. package/dist/dts/utils/history-transform.d.ts +7 -1
  24. package/dist/dts/utils/history-transform.d.ts.map +1 -1
  25. package/dist/dts/utils/resolve-context-budget.d.ts +8 -5
  26. package/dist/dts/utils/resolve-context-budget.d.ts.map +1 -1
  27. package/dist/esm/components/chat-driver/chat-driver.js +22 -2
  28. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +92 -0
  29. package/dist/esm/config/define-stateful-agent.js +1 -0
  30. package/dist/esm/main/main.js +170 -27
  31. package/dist/esm/utils/context-tokens.js +331 -106
  32. package/dist/esm/utils/history-transform.js +5 -5
  33. package/dist/esm/utils/resolve-context-budget.js +37 -7
  34. package/dist/react.cjs +21 -21
  35. package/dist/react.mjs +20 -20
  36. package/package.json +16 -16
@@ -1134,6 +1134,7 @@ var SUPPORTED_GEMINI_MODEL_IDS = [
1134
1134
  "gemini-2.5-pro",
1135
1135
  "gemini-2.5-flash",
1136
1136
  "gemini-2.5-flash-lite",
1137
+ "gemini-3.8-flash",
1137
1138
  "gemini-3.5-flash",
1138
1139
  "gemini-3.1-flash-lite",
1139
1140
  "gemini-3.1-pro-preview"
@@ -1236,6 +1237,22 @@ var GEMINI_PRICING = {
1236
1237
  rate: { promptPerMillion: 0.25, candidatePerMillion: 1.5 }
1237
1238
  },
1238
1239
  "gemini-3.5-flash": { kind: "flat", rate: { promptPerMillion: 1.5, candidatePerMillion: 9 } },
1240
+ // INTRODUCTORY RATE, and the only dated entry in this file. Google publishes
1241
+ // $0.75 / $3.75 per MTok **through 2026-12-31**, rising to $1.50 / $7.50 on 2027-01-01 —
1242
+ // so today this model is CHEAPER than gemini-3.5-flash above and in January it is not.
1243
+ //
1244
+ // The listed figure is the one in force, not the one coming. That is the lesson the
1245
+ // `claude-sonnet-5` entry records from the other direction: charging a scheduled future
1246
+ // rate early over-billed every request by 50% for as long as nobody noticed. A rate that
1247
+ // has not taken effect is not a price, and `GeminiPricing` deliberately has no time
1248
+ // dimension to express one — flip these two numbers on 2027-01-01 instead.
1249
+ //
1250
+ // That flip is ENFORCED, not remembered: `token-cost.test.ts` carries a dated assertion that
1251
+ // turns the build red on 2027-01-01 and names the three-line edit (these two rates, plus the
1252
+ // now-false `< gemini-3.5-flash` comparison in that same test — both models are $1.50 input
1253
+ // from that date). There is no ticket to lose.
1254
+ // https://ai.google.dev/gemini-api/docs/pricing
1255
+ "gemini-3.8-flash": { kind: "flat", rate: { promptPerMillion: 0.75, candidatePerMillion: 3.75 } },
1239
1256
  "gemini-3.1-pro-preview": {
1240
1257
  kind: "tiered",
1241
1258
  standard: { promptPerMillion: 2, candidatePerMillion: 12 },
@@ -2722,6 +2739,7 @@ var GEMINI_CONTEXT_LIMITS = {
2722
2739
  "gemini-2.5-pro": 1048576,
2723
2740
  "gemini-2.5-flash": 1048576,
2724
2741
  "gemini-2.5-flash-lite": 1048576,
2742
+ "gemini-3.8-flash": 1048576,
2725
2743
  "gemini-3.5-flash": 1048576,
2726
2744
  "gemini-3.1-flash-lite": 1048576,
2727
2745
  "gemini-3.1-pro-preview": 1048576
@@ -2746,6 +2764,10 @@ var GEMINI_MODEL_WARNINGS = {
2746
2764
  "gemini-2.5-flash": "GeminiTransport: using gemini-2.5-flash \u2014 higher cost than flash-lite; use for harder reasoning or agent tasks.",
2747
2765
  "gemini-2.5-pro": "GeminiTransport: using gemini-2.5-pro \u2014 significantly higher, prompt-size-tiered cost; reserve for tasks where flash reliability is insufficient.",
2748
2766
  "gemini-3.5-flash": "GeminiTransport: using gemini-3.5-flash \u2014 frontier model, materially higher cost than flash-lite; use for harder reasoning or agent tasks.",
2767
+ // Warns despite currently costing LESS than gemini-3.5-flash, because that is temporary: the
2768
+ // rate doubles on 2027-01-01 (see GEMINI_PRICING). A caller that picks this tier on today's
2769
+ // price and never revisits the choice gets twice the bill in January without changing a line.
2770
+ "gemini-3.8-flash": "GeminiTransport: using gemini-3.8-flash \u2014 frontier model, materially higher cost than flash-lite. Its $0.75/$3.75 per-MTok rate is introductory and DOUBLES to $1.50/$7.50 on 2027-01-01; re-check this tier choice before then.",
2749
2771
  "gemini-3.1-pro-preview": "GeminiTransport: using gemini-3.1-pro-preview \u2014 PREVIEW model (no stability guarantee, may be withdrawn) with significantly higher, prompt-size-tiered cost; reserve for tasks where flash reliability is insufficient."
2750
2772
  };
2751
2773
  var GEMINI_THINKING_DISABLEABLE = [
@@ -2753,15 +2775,32 @@ var GEMINI_THINKING_DISABLEABLE = [
2753
2775
  "gemini-2.5-flash-lite"
2754
2776
  ];
2755
2777
  var GEMINI_MEDIA_RESOLUTION_TIERS = [
2778
+ "gemini-3.8-flash",
2756
2779
  "gemini-3.5-flash",
2757
2780
  "gemini-3.1-flash-lite",
2758
2781
  "gemini-3.1-pro-preview"
2759
2782
  ];
2760
- var GEMINI_THINKING_LEVEL_TIERS = [
2761
- "gemini-3.5-flash",
2762
- "gemini-3.1-flash-lite",
2763
- "gemini-3.1-pro-preview"
2764
- ];
2783
+ var GEMINI_LEVEL_LADDER = ["minimal", "low", "medium", "high"];
2784
+ var GEMINI_THINKING_LEVELS = {
2785
+ "gemini-3.8-flash": ["low", "medium", "high"],
2786
+ "gemini-3.5-flash": ["minimal", "low", "medium", "high"],
2787
+ "gemini-3.1-flash-lite": ["minimal", "low", "medium", "high"],
2788
+ "gemini-3.1-pro-preview": ["low", "medium", "high"]
2789
+ };
2790
+ function clampThinkingLevel(supported, level) {
2791
+ if (!(supported === null || supported === void 0 ? void 0 : supported.length) || supported.includes(level)) {
2792
+ return level;
2793
+ }
2794
+ const target = GEMINI_LEVEL_LADDER.indexOf(level);
2795
+ const distance = (candidate) => Math.abs(GEMINI_LEVEL_LADDER.indexOf(candidate) - target);
2796
+ let best = supported[0];
2797
+ for (const candidate of supported) {
2798
+ if (distance(candidate) <= distance(best)) {
2799
+ best = candidate;
2800
+ }
2801
+ }
2802
+ return best;
2803
+ }
2765
2804
  var GEMINI_THINKING_LEVEL = {
2766
2805
  minimal: "minimal",
2767
2806
  low: "low",
@@ -2769,25 +2808,16 @@ var GEMINI_THINKING_LEVEL = {
2769
2808
  high: "high",
2770
2809
  max: "high"
2771
2810
  };
2772
- var GEMINI_COARSE_LEVEL_TIERS = [
2773
- "gemini-3.5-flash",
2774
- "gemini-3.1-flash-lite",
2775
- "gemini-3.1-pro-preview"
2776
- ];
2777
- var GEMINI_COARSE_LEVEL = {
2778
- minimal: "low",
2779
- low: "low",
2780
- medium: "high",
2781
- high: "high"
2782
- };
2783
2811
  var isGradedLevel = (p) => p !== void 0 && p !== "auto" && p !== "off";
2784
2812
  function geminiThinkingConfig(model, policy) {
2785
- if (isGradedLevel(policy) && GEMINI_THINKING_LEVEL_TIERS.includes(model)) {
2786
- const level = GEMINI_THINKING_LEVEL[policy];
2787
- return {
2788
- includeThoughts: true,
2789
- thinkingLevel: GEMINI_COARSE_LEVEL_TIERS.includes(model) ? GEMINI_COARSE_LEVEL[level] : level
2790
- };
2813
+ if (isGradedLevel(policy)) {
2814
+ const accepted = GEMINI_THINKING_LEVELS[model];
2815
+ if (accepted) {
2816
+ return {
2817
+ includeThoughts: true,
2818
+ thinkingLevel: clampThinkingLevel(accepted, GEMINI_THINKING_LEVEL[policy])
2819
+ };
2820
+ }
2791
2821
  }
2792
2822
  if (policy === "off" && GEMINI_THINKING_DISABLEABLE.includes(model)) {
2793
2823
  return { includeThoughts: false, thinkingBudget: 0 };
@@ -2828,7 +2858,7 @@ var GeminiTransport = class _GeminiTransport {
2828
2858
  /**
2829
2859
  * Warn once when a requested policy cannot be honoured on this model — a *cost* decision the
2830
2860
  * caller made that the wire silently reversed, so they hear about it, but only once, not per
2831
- * turn. Two shapes reach here (the Anthropic twin warns on the same two):
2861
+ * turn. Three shapes reach here (the Anthropic twin warns on the first two):
2832
2862
  *
2833
2863
  * - `'off'` on a tier that can't disable thinking — only the flash tiers accept a zero thinking
2834
2864
  * budget; everything else keeps paying for reasoning it was told to stop. (`'auto'` is NOT this
@@ -2838,6 +2868,12 @@ var GeminiTransport = class _GeminiTransport {
2838
2868
  * not the Gemini 3 `thinkingLevel` enum, so the level is dropped and the turn runs at the
2839
2869
  * default posture. Without this, a caller asking for `high` on `gemini-2.5-pro` and quietly
2840
2870
  * getting the default is left to wonder why.
2871
+ * - A graded depth whose DIALECT a Gemini 3 tier speaks but whose particular value it does not
2872
+ * accept — `minimal` on `gemini-3.8-flash` or `gemini-3.1-pro-preview`, the two tiers that
2873
+ * reject it outright. Nothing is dropped here; `clampThinkingLevel` MOVES the level onto the
2874
+ * nearest one the tier takes, and since ties resolve upward the move is usually to something
2875
+ * DEARER than the caller chose. A cost decision reversed on the wire in the expensive
2876
+ * direction is exactly what this warning exists for — without it the only trace is the bill.
2841
2877
  */
2842
2878
  warnIfThinkingUnclampable(policy) {
2843
2879
  if (this.warnedThinkingClamped) {
@@ -2848,9 +2884,19 @@ var GeminiTransport = class _GeminiTransport {
2848
2884
  logger.warn(`GeminiTransport: thinkingPolicy 'off' ignored \u2014 ${this.model} runs its default thinking posture and takes no budget we can safely set. Reasoning tokens are still billed at the candidate rate; use a flash tier if you need them gone.`);
2849
2885
  return;
2850
2886
  }
2851
- if (isGradedLevel(policy) && !GEMINI_THINKING_LEVEL_TIERS.includes(this.model)) {
2852
- this.warnedThinkingClamped = true;
2853
- logger.warn(`GeminiTransport: thinkingPolicy '${policy}' ignored \u2014 ${this.model} takes a numeric thinking budget, not the graded 'thinkingLevel' the Gemini 3 tiers use, so this turn runs at its default reasoning posture. Use a Gemini 3 tier to set reasoning depth.`);
2887
+ if (isGradedLevel(policy)) {
2888
+ const accepted = GEMINI_THINKING_LEVELS[this.model];
2889
+ if (!accepted) {
2890
+ this.warnedThinkingClamped = true;
2891
+ logger.warn(`GeminiTransport: thinkingPolicy '${policy}' ignored \u2014 ${this.model} takes a numeric thinking budget, not the graded 'thinkingLevel' the Gemini 3 tiers use, so this turn runs at its default reasoning posture. Use a Gemini 3 tier to set reasoning depth.`);
2892
+ return;
2893
+ }
2894
+ const requested = GEMINI_THINKING_LEVEL[policy];
2895
+ const sent = clampThinkingLevel(accepted, requested);
2896
+ if (sent !== requested) {
2897
+ this.warnedThinkingClamped = true;
2898
+ logger.warn(`GeminiTransport: thinkingPolicy '${policy}' sent as thinkingLevel '${sent}' \u2014 ${this.model} does not accept '${requested}' (it takes ${accepted.join("/")}). Reasoning bills at the candidate rate, so an upward clamp costs more than you asked for.`);
2899
+ }
2854
2900
  }
2855
2901
  }
2856
2902
  constructor(config = {}) {
@@ -3741,6 +3787,21 @@ function createInteractionContext(interactionId) {
3741
3787
  var APPROX_CHARS_PER_TOKEN = 4;
3742
3788
  var IMAGE_TOKEN_ESTIMATE = 1600;
3743
3789
  var PER_MESSAGE_FRAMING_TOKENS = 4;
3790
+ var PREAMBLE_PROSE_CHARS_PER_TOKEN = 4;
3791
+ var PREAMBLE_JSON_CHARS_PER_TOKEN = 3;
3792
+ var PER_TOOL_FRAMING_TOKENS = 8;
3793
+ function estimatePreambleTokens(input) {
3794
+ const prose = (input.systemPrompt?.length ?? 0) + (input.tailContext?.length ?? 0);
3795
+ let json = 0;
3796
+ for (const def of input.toolDefinitions ?? []) {
3797
+ try {
3798
+ json += JSON.stringify(def).length;
3799
+ } catch {
3800
+ json += def.name?.length ?? 0;
3801
+ }
3802
+ }
3803
+ return Math.ceil(prose / PREAMBLE_PROSE_CHARS_PER_TOKEN) + Math.ceil(json / PREAMBLE_JSON_CHARS_PER_TOKEN) + (input.toolDefinitions?.length ?? 0) * PER_TOOL_FRAMING_TOKENS;
3804
+ }
3744
3805
  var COMPACTION_SUMMARY_TOKEN_ESTIMATE = 1200;
3745
3806
  function reachesProvider(m) {
3746
3807
  if (m.role === "system-event" || m.role === "synthetic-user") return false;
@@ -3785,66 +3846,66 @@ function usableAnchor(m, compactedAt) {
3785
3846
  function firstAnchor(history, compactedAt) {
3786
3847
  return history.findIndex((m) => usableAnchor(m, compactedAt));
3787
3848
  }
3788
- function estimateMessageTokens(history) {
3789
- const estimates = history.map(baselineTokens);
3790
- const compactedAt = compactedAtOf(history);
3791
- const anchors = [];
3792
- history.forEach((m, i) => {
3793
- if (usableAnchor(m, compactedAt)) anchors.push(i);
3794
- });
3795
- for (let a = 0; a < anchors.length - 1; a += 1) {
3796
- const from = anchors[a];
3797
- const to = anchors[a + 1];
3798
- const measured = history[to].inputTokens - history[from].inputTokens;
3799
- if (measured <= 0) continue;
3800
- let baseline = 0;
3801
- for (let i = from; i < to; i += 1) baseline += estimates[i];
3802
- if (baseline <= 0) {
3803
- estimates[from] = measured;
3804
- continue;
3805
- }
3806
- const scale = measured / baseline;
3807
- let assigned = 0;
3808
- for (let i = from; i < to - 1; i += 1) {
3809
- estimates[i] = Math.round(estimates[i] * scale);
3810
- assigned += estimates[i];
3811
- }
3812
- estimates[to - 1] = measured - assigned;
3849
+ function lastAnchor(history, compactedAt) {
3850
+ for (let i = history.length - 1; i >= 0; i -= 1) {
3851
+ if (usableAnchor(history[i], compactedAt)) return i;
3813
3852
  }
3814
- return estimates;
3853
+ return -1;
3815
3854
  }
3816
- function estimateSystemOverhead(history) {
3817
- const first = firstAnchor(history, compactedAtOf(history));
3818
- if (first < 0) return 0;
3819
- const estimates = estimateMessageTokens(history);
3820
- let messages = 0;
3821
- for (let i = 0; i < first; i += 1) messages += estimates[i];
3822
- return Math.max(0, history[first].inputTokens - messages);
3823
- }
3824
- function estimateContextTokens(history) {
3825
- const estimates = estimateMessageTokens(history);
3826
- let total = estimateSystemOverhead(history);
3827
- for (const t of estimates) total += t;
3828
- return total;
3829
- }
3830
- function estimateRequestTokens(storedHistory, requestHistory) {
3831
- const overhead = estimateSystemOverhead(storedHistory);
3832
- const calibratedMessages = Math.max(0, estimateContextTokens(storedHistory) - overhead);
3833
- let storedBaseline = 0;
3834
- for (const m of storedHistory) storedBaseline += baselineTokens(m);
3855
+ function calibrate(history, preambleTokens) {
3856
+ const baselines = history.map(baselineTokens);
3857
+ let baselineTotal = 0;
3858
+ for (const b of baselines) baselineTotal += b;
3859
+ const compactedAt = compactedAtOf(history);
3860
+ const first = firstAnchor(history, compactedAt);
3861
+ const last = lastAnchor(history, compactedAt);
3862
+ if (first < 0 || last < 0) {
3863
+ const measured = Math.max(0, preambleTokens ?? 0);
3864
+ return {
3865
+ overhead: measured,
3866
+ total: baselineTotal + measured,
3867
+ estimates: baselines,
3868
+ scale: 1
3869
+ };
3870
+ }
3871
+ let sinceLast = 0;
3872
+ for (let i = last; i < baselines.length; i += 1) sinceLast += baselines[i];
3873
+ const total = history[last].inputTokens + sinceLast;
3874
+ let beforeFirst = 0;
3875
+ for (let i = 0; i < first; i += 1) beforeFirst += baselines[i];
3876
+ const derived = Math.max(0, history[first].inputTokens - beforeFirst);
3877
+ const claimed = preambleTokens ?? derived;
3878
+ const contradicted = baselineTotal > 0 && claimed >= total;
3879
+ const overhead = Math.min(contradicted ? derived : claimed, total);
3880
+ const messages = Math.max(0, total - overhead);
3881
+ if (baselineTotal <= 0) {
3882
+ return { overhead, total, estimates: baselines, scale: 1 };
3883
+ }
3884
+ const scale = messages / baselineTotal;
3885
+ const estimates = baselines.map((b) => Math.round(b * scale));
3886
+ let assigned = 0;
3887
+ let heaviest = -1;
3888
+ for (let i = 0; i < estimates.length; i += 1) {
3889
+ assigned += estimates[i];
3890
+ if (baselines[i] > 0 && (heaviest < 0 || baselines[i] > baselines[heaviest])) heaviest = i;
3891
+ }
3892
+ if (heaviest >= 0) estimates[heaviest] += messages - assigned;
3893
+ return { overhead, total, estimates, scale };
3894
+ }
3895
+ function estimateMessageTokens(history, preambleTokens) {
3896
+ return calibrate(history, preambleTokens).estimates;
3897
+ }
3898
+ function estimateRequestTokens(storedHistory, requestHistory, preambleTokens) {
3899
+ const { overhead } = calibrate(storedHistory, preambleTokens);
3835
3900
  let requestBaseline = 0;
3836
3901
  for (const m of requestHistory) requestBaseline += baselineTokens(m);
3837
- if (storedBaseline <= 0) return overhead + requestBaseline;
3838
- return Math.round(overhead + calibratedMessages * (requestBaseline / storedBaseline));
3902
+ return Math.round(overhead + requestBaseline);
3839
3903
  }
3840
- function projectCompaction(history, cut, summaryTokens = COMPACTION_SUMMARY_TOKEN_ESTIMATE) {
3841
- const estimates = estimateMessageTokens(history);
3842
- const overhead = estimateSystemOverhead(history);
3904
+ function projectCompaction(history, cut, summaryTokens = COMPACTION_SUMMARY_TOKEN_ESTIMATE, preambleTokens) {
3905
+ const { estimates, overhead, total } = calibrate(history, preambleTokens);
3843
3906
  let tail = 0;
3844
3907
  for (let i = cut; i < estimates.length; i += 1) tail += estimates[i];
3845
- let all = 0;
3846
- for (const t of estimates) all += t;
3847
- const tokensBefore = overhead + all;
3908
+ const tokensBefore = total;
3848
3909
  const tokensAfter = overhead + summaryTokens + tail;
3849
3910
  return {
3850
3911
  cut,
@@ -4112,8 +4173,8 @@ function isSafeCompactionCut(history, i) {
4112
4173
  }
4113
4174
  return true;
4114
4175
  }
4115
- function findCompactionCut(history, tailTokenBudget = DEFAULT_TAIL_TOKEN_BUDGET) {
4116
- const estimates = estimateMessageTokens(history);
4176
+ function findCompactionCut(history, tailTokenBudget = DEFAULT_TAIL_TOKEN_BUDGET, preambleTokens) {
4177
+ const estimates = estimateMessageTokens(history, preambleTokens);
4117
4178
  let messagesTotal = 0;
4118
4179
  for (const t of estimates) messagesTotal += t;
4119
4180
  if (messagesTotal <= tailTokenBudget) return null;
@@ -4134,9 +4195,14 @@ function planCompaction(history, options = {}) {
4134
4195
  tailTokenBudget = DEFAULT_TAIL_TOKEN_BUDGET,
4135
4196
  minReclaimTokens = DEFAULT_MIN_RECLAIM_TOKENS
4136
4197
  } = options;
4137
- const cut = findCompactionCut(history, tailTokenBudget);
4198
+ const cut = findCompactionCut(history, tailTokenBudget, options.preambleTokens);
4138
4199
  if (cut == null) return null;
4139
- const projection = projectCompaction(history, cut);
4200
+ const projection = projectCompaction(
4201
+ history,
4202
+ cut,
4203
+ COMPACTION_SUMMARY_TOKEN_ESTIMATE,
4204
+ options.preambleTokens
4205
+ );
4140
4206
  return projection.reclaimed >= minReclaimTokens ? projection : null;
4141
4207
  }
4142
4208
  function renderMessageForSummary(m) {
@@ -5100,7 +5166,7 @@ var ChatDriver = class _ChatDriver extends EventTarget {
5100
5166
  attachments: pendingInput.attachments
5101
5167
  }
5102
5168
  ] : requestHistory;
5103
- const estimated = estimateRequestTokens(this.history, request);
5169
+ const estimated = estimateRequestTokens(this.history, request, this.lastPreambleTokens);
5104
5170
  if (estimated < threshold) return void 0;
5105
5171
  logger2.error(
5106
5172
  `ChatDriver: ending the turn \u2014 the request would fill the context window (~${estimated} of ${limit})`
@@ -5288,6 +5354,10 @@ Output format (strict):
5288
5354
  this.loadHistory([summaryMessage, ...tail]);
5289
5355
  return summaryMessage;
5290
5356
  }
5357
+ /** {@inheritDoc AiDriver.getPreambleTokens} */
5358
+ getPreambleTokens() {
5359
+ return this.lastPreambleTokens;
5360
+ }
5291
5361
  isBusy() {
5292
5362
  return this.busy;
5293
5363
  }
@@ -6150,6 +6220,11 @@ ${tailBody}
6150
6220
  const attachmentsForCall = currentAttachments;
6151
6221
  currentInput = "";
6152
6222
  currentAttachments = void 0;
6223
+ this.lastPreambleTokens = estimatePreambleTokens({
6224
+ systemPrompt,
6225
+ toolDefinitions: this.toolDefinitions,
6226
+ tailContext
6227
+ });
6153
6228
  const options = {
6154
6229
  systemPrompt,
6155
6230
  // Strip fold-only properties (foldEvent, foldPath) before sending to provider
@@ -6819,6 +6894,13 @@ function isSpecialist(agent) {
6819
6894
  function isFallback(agent) {
6820
6895
  return agent.fallback === true;
6821
6896
  }
6897
+ function normalizeContextReserve(input) {
6898
+ if (input == null) return void 0;
6899
+ const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0;
6900
+ if (typeof input === "number") return positive(input) ? { tokens: input } : void 0;
6901
+ if (!positive(input.tokens)) return void 0;
6902
+ return positive(input.warnAheadTokens) ? { tokens: input.tokens, warnAheadTokens: input.warnAheadTokens } : { tokens: input.tokens };
6903
+ }
6822
6904
  var UNROUTABLE_PLACEHOLDER = "{{unroutable}}";
6823
6905
  var UNROUTABLE_INSTRUCTION = `When the request is substantive but nothing above covers it, say so directly. Do not offer to do it yourself, and do not imply a specialist will pick it up.
6824
6906
 
@@ -7020,6 +7102,10 @@ var OrchestratingDriver = class extends EventTarget {
7020
7102
  canCompact(options) {
7021
7103
  return this.chatDriver.canCompact(options);
7022
7104
  }
7105
+ /** {@inheritDoc AiDriver.getPreambleTokens} */
7106
+ getPreambleTokens() {
7107
+ return this.chatDriver.getPreambleTokens();
7108
+ }
7023
7109
  /** {@inheritDoc AiDriver.getCompactionPlan} */
7024
7110
  getCompactionPlan(options) {
7025
7111
  return this.chatDriver.getCompactionPlan(options);
@@ -7141,6 +7227,32 @@ var OrchestratingDriver = class extends EventTarget {
7141
7227
  primeRestoredAgentState(snapshots) {
7142
7228
  this.restoredAgentStates = { ...snapshots };
7143
7229
  }
7230
+ /**
7231
+ * {@inheritDoc AiDriver.getContextReserve}
7232
+ *
7233
+ * Two cases, because routing has two modes and they need opposite answers.
7234
+ */
7235
+ getContextReserve() {
7236
+ const locked = this.resolvePinnedAgent();
7237
+ if (locked) {
7238
+ const reserve = normalizeContextReserve(locked.contextReserve);
7239
+ return reserve == null ? void 0 : { ...reserve, agentName: locked.name, basis: "locked" };
7240
+ }
7241
+ const candidates = [...this.specialists];
7242
+ if (this.fallback) candidates.push(this.fallback);
7243
+ if (this.activeAgent) candidates.push(this.activeAgent);
7244
+ let winner;
7245
+ let agentName = "";
7246
+ for (const agent of candidates) {
7247
+ const reserve = normalizeContextReserve(agent.contextReserve);
7248
+ if (!reserve) continue;
7249
+ if (!winner || reserve.tokens > winner.tokens) {
7250
+ winner = reserve;
7251
+ agentName = agent.name;
7252
+ }
7253
+ }
7254
+ return winner ? { ...winner, agentName, basis: "reachable-max" } : void 0;
7255
+ }
7144
7256
  /** {@inheritDoc AiDriver.getActiveAgent} */
7145
7257
  getActiveAgent() {
7146
7258
  return this.activeAgent;
@@ -7650,6 +7762,7 @@ function defineStatefulAgent(opts) {
7650
7762
  onUnresolvedTool: wrappedOnUnresolvedTool,
7651
7763
  resumable: wrappedResumable,
7652
7764
  notResumableMessage: opts.notResumableMessage,
7765
+ contextReserve: opts.contextReserve,
7653
7766
  onActivate: async (ctx) => {
7654
7767
  state = await opts.init(ctx);
7655
7768
  if (opts.toolHandlers) {