@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
@@ -1205,6 +1205,7 @@ var SUPPORTED_GEMINI_MODEL_IDS = [
1205
1205
  "gemini-2.5-pro",
1206
1206
  "gemini-2.5-flash",
1207
1207
  "gemini-2.5-flash-lite",
1208
+ "gemini-3.8-flash",
1208
1209
  "gemini-3.5-flash",
1209
1210
  "gemini-3.1-flash-lite",
1210
1211
  "gemini-3.1-pro-preview"
@@ -1307,6 +1308,22 @@ var GEMINI_PRICING = {
1307
1308
  rate: { promptPerMillion: 0.25, candidatePerMillion: 1.5 }
1308
1309
  },
1309
1310
  "gemini-3.5-flash": { kind: "flat", rate: { promptPerMillion: 1.5, candidatePerMillion: 9 } },
1311
+ // INTRODUCTORY RATE, and the only dated entry in this file. Google publishes
1312
+ // $0.75 / $3.75 per MTok **through 2026-12-31**, rising to $1.50 / $7.50 on 2027-01-01 —
1313
+ // so today this model is CHEAPER than gemini-3.5-flash above and in January it is not.
1314
+ //
1315
+ // The listed figure is the one in force, not the one coming. That is the lesson the
1316
+ // `claude-sonnet-5` entry records from the other direction: charging a scheduled future
1317
+ // rate early over-billed every request by 50% for as long as nobody noticed. A rate that
1318
+ // has not taken effect is not a price, and `GeminiPricing` deliberately has no time
1319
+ // dimension to express one — flip these two numbers on 2027-01-01 instead.
1320
+ //
1321
+ // That flip is ENFORCED, not remembered: `token-cost.test.ts` carries a dated assertion that
1322
+ // turns the build red on 2027-01-01 and names the three-line edit (these two rates, plus the
1323
+ // now-false `< gemini-3.5-flash` comparison in that same test — both models are $1.50 input
1324
+ // from that date). There is no ticket to lose.
1325
+ // https://ai.google.dev/gemini-api/docs/pricing
1326
+ "gemini-3.8-flash": { kind: "flat", rate: { promptPerMillion: 0.75, candidatePerMillion: 3.75 } },
1310
1327
  "gemini-3.1-pro-preview": {
1311
1328
  kind: "tiered",
1312
1329
  standard: { promptPerMillion: 2, candidatePerMillion: 12 },
@@ -2793,6 +2810,7 @@ var GEMINI_CONTEXT_LIMITS = {
2793
2810
  "gemini-2.5-pro": 1048576,
2794
2811
  "gemini-2.5-flash": 1048576,
2795
2812
  "gemini-2.5-flash-lite": 1048576,
2813
+ "gemini-3.8-flash": 1048576,
2796
2814
  "gemini-3.5-flash": 1048576,
2797
2815
  "gemini-3.1-flash-lite": 1048576,
2798
2816
  "gemini-3.1-pro-preview": 1048576
@@ -2817,6 +2835,10 @@ var GEMINI_MODEL_WARNINGS = {
2817
2835
  "gemini-2.5-flash": "GeminiTransport: using gemini-2.5-flash \u2014 higher cost than flash-lite; use for harder reasoning or agent tasks.",
2818
2836
  "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.",
2819
2837
  "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.",
2838
+ // Warns despite currently costing LESS than gemini-3.5-flash, because that is temporary: the
2839
+ // rate doubles on 2027-01-01 (see GEMINI_PRICING). A caller that picks this tier on today's
2840
+ // price and never revisits the choice gets twice the bill in January without changing a line.
2841
+ "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.",
2820
2842
  "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."
2821
2843
  };
2822
2844
  var GEMINI_THINKING_DISABLEABLE = [
@@ -2824,15 +2846,32 @@ var GEMINI_THINKING_DISABLEABLE = [
2824
2846
  "gemini-2.5-flash-lite"
2825
2847
  ];
2826
2848
  var GEMINI_MEDIA_RESOLUTION_TIERS = [
2849
+ "gemini-3.8-flash",
2827
2850
  "gemini-3.5-flash",
2828
2851
  "gemini-3.1-flash-lite",
2829
2852
  "gemini-3.1-pro-preview"
2830
2853
  ];
2831
- var GEMINI_THINKING_LEVEL_TIERS = [
2832
- "gemini-3.5-flash",
2833
- "gemini-3.1-flash-lite",
2834
- "gemini-3.1-pro-preview"
2835
- ];
2854
+ var GEMINI_LEVEL_LADDER = ["minimal", "low", "medium", "high"];
2855
+ var GEMINI_THINKING_LEVELS = {
2856
+ "gemini-3.8-flash": ["low", "medium", "high"],
2857
+ "gemini-3.5-flash": ["minimal", "low", "medium", "high"],
2858
+ "gemini-3.1-flash-lite": ["minimal", "low", "medium", "high"],
2859
+ "gemini-3.1-pro-preview": ["low", "medium", "high"]
2860
+ };
2861
+ function clampThinkingLevel(supported, level) {
2862
+ if (!(supported === null || supported === void 0 ? void 0 : supported.length) || supported.includes(level)) {
2863
+ return level;
2864
+ }
2865
+ const target = GEMINI_LEVEL_LADDER.indexOf(level);
2866
+ const distance = (candidate) => Math.abs(GEMINI_LEVEL_LADDER.indexOf(candidate) - target);
2867
+ let best = supported[0];
2868
+ for (const candidate of supported) {
2869
+ if (distance(candidate) <= distance(best)) {
2870
+ best = candidate;
2871
+ }
2872
+ }
2873
+ return best;
2874
+ }
2836
2875
  var GEMINI_THINKING_LEVEL = {
2837
2876
  minimal: "minimal",
2838
2877
  low: "low",
@@ -2840,25 +2879,16 @@ var GEMINI_THINKING_LEVEL = {
2840
2879
  high: "high",
2841
2880
  max: "high"
2842
2881
  };
2843
- var GEMINI_COARSE_LEVEL_TIERS = [
2844
- "gemini-3.5-flash",
2845
- "gemini-3.1-flash-lite",
2846
- "gemini-3.1-pro-preview"
2847
- ];
2848
- var GEMINI_COARSE_LEVEL = {
2849
- minimal: "low",
2850
- low: "low",
2851
- medium: "high",
2852
- high: "high"
2853
- };
2854
2882
  var isGradedLevel = (p) => p !== void 0 && p !== "auto" && p !== "off";
2855
2883
  function geminiThinkingConfig(model, policy) {
2856
- if (isGradedLevel(policy) && GEMINI_THINKING_LEVEL_TIERS.includes(model)) {
2857
- const level = GEMINI_THINKING_LEVEL[policy];
2858
- return {
2859
- includeThoughts: true,
2860
- thinkingLevel: GEMINI_COARSE_LEVEL_TIERS.includes(model) ? GEMINI_COARSE_LEVEL[level] : level
2861
- };
2884
+ if (isGradedLevel(policy)) {
2885
+ const accepted = GEMINI_THINKING_LEVELS[model];
2886
+ if (accepted) {
2887
+ return {
2888
+ includeThoughts: true,
2889
+ thinkingLevel: clampThinkingLevel(accepted, GEMINI_THINKING_LEVEL[policy])
2890
+ };
2891
+ }
2862
2892
  }
2863
2893
  if (policy === "off" && GEMINI_THINKING_DISABLEABLE.includes(model)) {
2864
2894
  return { includeThoughts: false, thinkingBudget: 0 };
@@ -2899,7 +2929,7 @@ var GeminiTransport = class _GeminiTransport {
2899
2929
  /**
2900
2930
  * Warn once when a requested policy cannot be honoured on this model — a *cost* decision the
2901
2931
  * caller made that the wire silently reversed, so they hear about it, but only once, not per
2902
- * turn. Two shapes reach here (the Anthropic twin warns on the same two):
2932
+ * turn. Three shapes reach here (the Anthropic twin warns on the first two):
2903
2933
  *
2904
2934
  * - `'off'` on a tier that can't disable thinking — only the flash tiers accept a zero thinking
2905
2935
  * budget; everything else keeps paying for reasoning it was told to stop. (`'auto'` is NOT this
@@ -2909,6 +2939,12 @@ var GeminiTransport = class _GeminiTransport {
2909
2939
  * not the Gemini 3 `thinkingLevel` enum, so the level is dropped and the turn runs at the
2910
2940
  * default posture. Without this, a caller asking for `high` on `gemini-2.5-pro` and quietly
2911
2941
  * getting the default is left to wonder why.
2942
+ * - A graded depth whose DIALECT a Gemini 3 tier speaks but whose particular value it does not
2943
+ * accept — `minimal` on `gemini-3.8-flash` or `gemini-3.1-pro-preview`, the two tiers that
2944
+ * reject it outright. Nothing is dropped here; `clampThinkingLevel` MOVES the level onto the
2945
+ * nearest one the tier takes, and since ties resolve upward the move is usually to something
2946
+ * DEARER than the caller chose. A cost decision reversed on the wire in the expensive
2947
+ * direction is exactly what this warning exists for — without it the only trace is the bill.
2912
2948
  */
2913
2949
  warnIfThinkingUnclampable(policy) {
2914
2950
  if (this.warnedThinkingClamped) {
@@ -2919,9 +2955,19 @@ var GeminiTransport = class _GeminiTransport {
2919
2955
  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.`);
2920
2956
  return;
2921
2957
  }
2922
- if (isGradedLevel(policy) && !GEMINI_THINKING_LEVEL_TIERS.includes(this.model)) {
2923
- this.warnedThinkingClamped = true;
2924
- 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.`);
2958
+ if (isGradedLevel(policy)) {
2959
+ const accepted = GEMINI_THINKING_LEVELS[this.model];
2960
+ if (!accepted) {
2961
+ this.warnedThinkingClamped = true;
2962
+ 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.`);
2963
+ return;
2964
+ }
2965
+ const requested = GEMINI_THINKING_LEVEL[policy];
2966
+ const sent = clampThinkingLevel(accepted, requested);
2967
+ if (sent !== requested) {
2968
+ this.warnedThinkingClamped = true;
2969
+ 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.`);
2970
+ }
2925
2971
  }
2926
2972
  }
2927
2973
  constructor(config = {}) {
@@ -3812,6 +3858,21 @@ function createInteractionContext(interactionId) {
3812
3858
  var APPROX_CHARS_PER_TOKEN = 4;
3813
3859
  var IMAGE_TOKEN_ESTIMATE = 1600;
3814
3860
  var PER_MESSAGE_FRAMING_TOKENS = 4;
3861
+ var PREAMBLE_PROSE_CHARS_PER_TOKEN = 4;
3862
+ var PREAMBLE_JSON_CHARS_PER_TOKEN = 3;
3863
+ var PER_TOOL_FRAMING_TOKENS = 8;
3864
+ function estimatePreambleTokens(input) {
3865
+ const prose = (input.systemPrompt?.length ?? 0) + (input.tailContext?.length ?? 0);
3866
+ let json = 0;
3867
+ for (const def of input.toolDefinitions ?? []) {
3868
+ try {
3869
+ json += JSON.stringify(def).length;
3870
+ } catch {
3871
+ json += def.name?.length ?? 0;
3872
+ }
3873
+ }
3874
+ 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;
3875
+ }
3815
3876
  var COMPACTION_SUMMARY_TOKEN_ESTIMATE = 1200;
3816
3877
  function reachesProvider(m) {
3817
3878
  if (m.role === "system-event" || m.role === "synthetic-user") return false;
@@ -3856,66 +3917,66 @@ function usableAnchor(m, compactedAt) {
3856
3917
  function firstAnchor(history, compactedAt) {
3857
3918
  return history.findIndex((m) => usableAnchor(m, compactedAt));
3858
3919
  }
3859
- function estimateMessageTokens(history) {
3860
- const estimates = history.map(baselineTokens);
3861
- const compactedAt = compactedAtOf(history);
3862
- const anchors = [];
3863
- history.forEach((m, i) => {
3864
- if (usableAnchor(m, compactedAt)) anchors.push(i);
3865
- });
3866
- for (let a = 0; a < anchors.length - 1; a += 1) {
3867
- const from = anchors[a];
3868
- const to = anchors[a + 1];
3869
- const measured = history[to].inputTokens - history[from].inputTokens;
3870
- if (measured <= 0) continue;
3871
- let baseline = 0;
3872
- for (let i = from; i < to; i += 1) baseline += estimates[i];
3873
- if (baseline <= 0) {
3874
- estimates[from] = measured;
3875
- continue;
3876
- }
3877
- const scale = measured / baseline;
3878
- let assigned = 0;
3879
- for (let i = from; i < to - 1; i += 1) {
3880
- estimates[i] = Math.round(estimates[i] * scale);
3881
- assigned += estimates[i];
3882
- }
3883
- estimates[to - 1] = measured - assigned;
3920
+ function lastAnchor(history, compactedAt) {
3921
+ for (let i = history.length - 1; i >= 0; i -= 1) {
3922
+ if (usableAnchor(history[i], compactedAt)) return i;
3884
3923
  }
3885
- return estimates;
3924
+ return -1;
3886
3925
  }
3887
- function estimateSystemOverhead(history) {
3888
- const first = firstAnchor(history, compactedAtOf(history));
3889
- if (first < 0) return 0;
3890
- const estimates = estimateMessageTokens(history);
3891
- let messages = 0;
3892
- for (let i = 0; i < first; i += 1) messages += estimates[i];
3893
- return Math.max(0, history[first].inputTokens - messages);
3894
- }
3895
- function estimateContextTokens(history) {
3896
- const estimates = estimateMessageTokens(history);
3897
- let total = estimateSystemOverhead(history);
3898
- for (const t of estimates) total += t;
3899
- return total;
3900
- }
3901
- function estimateRequestTokens(storedHistory, requestHistory) {
3902
- const overhead = estimateSystemOverhead(storedHistory);
3903
- const calibratedMessages = Math.max(0, estimateContextTokens(storedHistory) - overhead);
3904
- let storedBaseline = 0;
3905
- for (const m of storedHistory) storedBaseline += baselineTokens(m);
3926
+ function calibrate(history, preambleTokens) {
3927
+ const baselines = history.map(baselineTokens);
3928
+ let baselineTotal = 0;
3929
+ for (const b of baselines) baselineTotal += b;
3930
+ const compactedAt = compactedAtOf(history);
3931
+ const first = firstAnchor(history, compactedAt);
3932
+ const last = lastAnchor(history, compactedAt);
3933
+ if (first < 0 || last < 0) {
3934
+ const measured = Math.max(0, preambleTokens ?? 0);
3935
+ return {
3936
+ overhead: measured,
3937
+ total: baselineTotal + measured,
3938
+ estimates: baselines,
3939
+ scale: 1
3940
+ };
3941
+ }
3942
+ let sinceLast = 0;
3943
+ for (let i = last; i < baselines.length; i += 1) sinceLast += baselines[i];
3944
+ const total = history[last].inputTokens + sinceLast;
3945
+ let beforeFirst = 0;
3946
+ for (let i = 0; i < first; i += 1) beforeFirst += baselines[i];
3947
+ const derived = Math.max(0, history[first].inputTokens - beforeFirst);
3948
+ const claimed = preambleTokens ?? derived;
3949
+ const contradicted = baselineTotal > 0 && claimed >= total;
3950
+ const overhead = Math.min(contradicted ? derived : claimed, total);
3951
+ const messages = Math.max(0, total - overhead);
3952
+ if (baselineTotal <= 0) {
3953
+ return { overhead, total, estimates: baselines, scale: 1 };
3954
+ }
3955
+ const scale = messages / baselineTotal;
3956
+ const estimates = baselines.map((b) => Math.round(b * scale));
3957
+ let assigned = 0;
3958
+ let heaviest = -1;
3959
+ for (let i = 0; i < estimates.length; i += 1) {
3960
+ assigned += estimates[i];
3961
+ if (baselines[i] > 0 && (heaviest < 0 || baselines[i] > baselines[heaviest])) heaviest = i;
3962
+ }
3963
+ if (heaviest >= 0) estimates[heaviest] += messages - assigned;
3964
+ return { overhead, total, estimates, scale };
3965
+ }
3966
+ function estimateMessageTokens(history, preambleTokens) {
3967
+ return calibrate(history, preambleTokens).estimates;
3968
+ }
3969
+ function estimateRequestTokens(storedHistory, requestHistory, preambleTokens) {
3970
+ const { overhead } = calibrate(storedHistory, preambleTokens);
3906
3971
  let requestBaseline = 0;
3907
3972
  for (const m of requestHistory) requestBaseline += baselineTokens(m);
3908
- if (storedBaseline <= 0) return overhead + requestBaseline;
3909
- return Math.round(overhead + calibratedMessages * (requestBaseline / storedBaseline));
3973
+ return Math.round(overhead + requestBaseline);
3910
3974
  }
3911
- function projectCompaction(history, cut, summaryTokens = COMPACTION_SUMMARY_TOKEN_ESTIMATE) {
3912
- const estimates = estimateMessageTokens(history);
3913
- const overhead = estimateSystemOverhead(history);
3975
+ function projectCompaction(history, cut, summaryTokens = COMPACTION_SUMMARY_TOKEN_ESTIMATE, preambleTokens) {
3976
+ const { estimates, overhead, total } = calibrate(history, preambleTokens);
3914
3977
  let tail = 0;
3915
3978
  for (let i = cut; i < estimates.length; i += 1) tail += estimates[i];
3916
- let all = 0;
3917
- for (const t of estimates) all += t;
3918
- const tokensBefore = overhead + all;
3979
+ const tokensBefore = total;
3919
3980
  const tokensAfter = overhead + summaryTokens + tail;
3920
3981
  return {
3921
3982
  cut,
@@ -4183,8 +4244,8 @@ function isSafeCompactionCut(history, i) {
4183
4244
  }
4184
4245
  return true;
4185
4246
  }
4186
- function findCompactionCut(history, tailTokenBudget = DEFAULT_TAIL_TOKEN_BUDGET) {
4187
- const estimates = estimateMessageTokens(history);
4247
+ function findCompactionCut(history, tailTokenBudget = DEFAULT_TAIL_TOKEN_BUDGET, preambleTokens) {
4248
+ const estimates = estimateMessageTokens(history, preambleTokens);
4188
4249
  let messagesTotal = 0;
4189
4250
  for (const t of estimates) messagesTotal += t;
4190
4251
  if (messagesTotal <= tailTokenBudget) return null;
@@ -4205,9 +4266,14 @@ function planCompaction(history, options = {}) {
4205
4266
  tailTokenBudget = DEFAULT_TAIL_TOKEN_BUDGET,
4206
4267
  minReclaimTokens = DEFAULT_MIN_RECLAIM_TOKENS
4207
4268
  } = options;
4208
- const cut = findCompactionCut(history, tailTokenBudget);
4269
+ const cut = findCompactionCut(history, tailTokenBudget, options.preambleTokens);
4209
4270
  if (cut == null) return null;
4210
- const projection = projectCompaction(history, cut);
4271
+ const projection = projectCompaction(
4272
+ history,
4273
+ cut,
4274
+ COMPACTION_SUMMARY_TOKEN_ESTIMATE,
4275
+ options.preambleTokens
4276
+ );
4211
4277
  return projection.reclaimed >= minReclaimTokens ? projection : null;
4212
4278
  }
4213
4279
  function renderMessageForSummary(m) {
@@ -5171,7 +5237,7 @@ var ChatDriver = class _ChatDriver extends EventTarget {
5171
5237
  attachments: pendingInput.attachments
5172
5238
  }
5173
5239
  ] : requestHistory;
5174
- const estimated = estimateRequestTokens(this.history, request);
5240
+ const estimated = estimateRequestTokens(this.history, request, this.lastPreambleTokens);
5175
5241
  if (estimated < threshold) return void 0;
5176
5242
  logger2.error(
5177
5243
  `ChatDriver: ending the turn \u2014 the request would fill the context window (~${estimated} of ${limit})`
@@ -5359,6 +5425,10 @@ Output format (strict):
5359
5425
  this.loadHistory([summaryMessage, ...tail]);
5360
5426
  return summaryMessage;
5361
5427
  }
5428
+ /** {@inheritDoc AiDriver.getPreambleTokens} */
5429
+ getPreambleTokens() {
5430
+ return this.lastPreambleTokens;
5431
+ }
5362
5432
  isBusy() {
5363
5433
  return this.busy;
5364
5434
  }
@@ -6221,6 +6291,11 @@ ${tailBody}
6221
6291
  const attachmentsForCall = currentAttachments;
6222
6292
  currentInput = "";
6223
6293
  currentAttachments = void 0;
6294
+ this.lastPreambleTokens = estimatePreambleTokens({
6295
+ systemPrompt,
6296
+ toolDefinitions: this.toolDefinitions,
6297
+ tailContext
6298
+ });
6224
6299
  const options = {
6225
6300
  systemPrompt,
6226
6301
  // Strip fold-only properties (foldEvent, foldPath) before sending to provider
@@ -6890,6 +6965,13 @@ function isSpecialist(agent) {
6890
6965
  function isFallback(agent) {
6891
6966
  return agent.fallback === true;
6892
6967
  }
6968
+ function normalizeContextReserve(input) {
6969
+ if (input == null) return void 0;
6970
+ const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0;
6971
+ if (typeof input === "number") return positive(input) ? { tokens: input } : void 0;
6972
+ if (!positive(input.tokens)) return void 0;
6973
+ return positive(input.warnAheadTokens) ? { tokens: input.tokens, warnAheadTokens: input.warnAheadTokens } : { tokens: input.tokens };
6974
+ }
6893
6975
  var UNROUTABLE_PLACEHOLDER = "{{unroutable}}";
6894
6976
  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.
6895
6977
 
@@ -7091,6 +7173,10 @@ var OrchestratingDriver = class extends EventTarget {
7091
7173
  canCompact(options) {
7092
7174
  return this.chatDriver.canCompact(options);
7093
7175
  }
7176
+ /** {@inheritDoc AiDriver.getPreambleTokens} */
7177
+ getPreambleTokens() {
7178
+ return this.chatDriver.getPreambleTokens();
7179
+ }
7094
7180
  /** {@inheritDoc AiDriver.getCompactionPlan} */
7095
7181
  getCompactionPlan(options) {
7096
7182
  return this.chatDriver.getCompactionPlan(options);
@@ -7212,6 +7298,32 @@ var OrchestratingDriver = class extends EventTarget {
7212
7298
  primeRestoredAgentState(snapshots) {
7213
7299
  this.restoredAgentStates = { ...snapshots };
7214
7300
  }
7301
+ /**
7302
+ * {@inheritDoc AiDriver.getContextReserve}
7303
+ *
7304
+ * Two cases, because routing has two modes and they need opposite answers.
7305
+ */
7306
+ getContextReserve() {
7307
+ const locked = this.resolvePinnedAgent();
7308
+ if (locked) {
7309
+ const reserve = normalizeContextReserve(locked.contextReserve);
7310
+ return reserve == null ? void 0 : { ...reserve, agentName: locked.name, basis: "locked" };
7311
+ }
7312
+ const candidates = [...this.specialists];
7313
+ if (this.fallback) candidates.push(this.fallback);
7314
+ if (this.activeAgent) candidates.push(this.activeAgent);
7315
+ let winner;
7316
+ let agentName = "";
7317
+ for (const agent of candidates) {
7318
+ const reserve = normalizeContextReserve(agent.contextReserve);
7319
+ if (!reserve) continue;
7320
+ if (!winner || reserve.tokens > winner.tokens) {
7321
+ winner = reserve;
7322
+ agentName = agent.name;
7323
+ }
7324
+ }
7325
+ return winner ? { ...winner, agentName, basis: "reachable-max" } : void 0;
7326
+ }
7215
7327
  /** {@inheritDoc AiDriver.getActiveAgent} */
7216
7328
  getActiveAgent() {
7217
7329
  return this.activeAgent;
@@ -7721,6 +7833,7 @@ function defineStatefulAgent(opts) {
7721
7833
  onUnresolvedTool: wrappedOnUnresolvedTool,
7722
7834
  resumable: wrappedResumable,
7723
7835
  notResumableMessage: opts.notResumableMessage,
7836
+ contextReserve: opts.contextReserve,
7724
7837
  onActivate: async (ctx) => {
7725
7838
  state = await opts.init(ctx);
7726
7839
  if (opts.toolHandlers) {