@genesislcap/ai-assistant 15.19.6 → 15.20.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.
Files changed (42) hide show
  1. package/dist/ai-assistant.api.json +605 -72
  2. package/dist/ai-assistant.d.ts +404 -25
  3. package/dist/chat-driver.cjs +341 -28
  4. package/dist/chat-driver.cjs.map +4 -4
  5. package/dist/chat-driver.mjs +341 -28
  6. package/dist/chat-driver.mjs.map +4 -4
  7. package/dist/custom-elements.json +630 -20
  8. package/dist/dts/components/ai-driver/ai-driver.d.ts +33 -7
  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 +63 -2
  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 -3
  13. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  14. package/dist/dts/config/config.d.ts +44 -0
  15. package/dist/dts/config/config.d.ts.map +1 -1
  16. package/dist/dts/main/main.d.ts +187 -5
  17. package/dist/dts/main/main.d.ts.map +1 -1
  18. package/dist/dts/main/main.styles.d.ts.map +1 -1
  19. package/dist/dts/main/main.template.d.ts.map +1 -1
  20. package/dist/dts/utils/condense-history.d.ts.map +1 -1
  21. package/dist/dts/utils/context-tokens.d.ts +156 -0
  22. package/dist/dts/utils/context-tokens.d.ts.map +1 -0
  23. package/dist/dts/utils/history-transform.d.ts +76 -14
  24. package/dist/dts/utils/history-transform.d.ts.map +1 -1
  25. package/dist/dts/utils/resolve-context-budget.d.ts +98 -0
  26. package/dist/dts/utils/resolve-context-budget.d.ts.map +1 -0
  27. package/dist/esm/components/chat-driver/chat-driver.js +179 -34
  28. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +12 -4
  29. package/dist/esm/main/main.js +391 -21
  30. package/dist/esm/main/main.styles.js +128 -0
  31. package/dist/esm/main/main.template.js +64 -29
  32. package/dist/esm/state/debug-event-log.js +1 -1
  33. package/dist/esm/utils/condense-history.js +1 -5
  34. package/dist/esm/utils/context-tokens.js +339 -0
  35. package/dist/esm/utils/history-transform.js +101 -19
  36. package/dist/esm/utils/resolve-context-budget.js +84 -0
  37. package/package.json +16 -16
  38. package/sandbox/README.md +93 -4
  39. package/sandbox/controls.ts +77 -10
  40. package/sandbox/fixtures.ts +163 -6
  41. package/sandbox/sandbox.css +54 -1
  42. package/sandbox/sandbox.ts +384 -7
@@ -1,11 +1,12 @@
1
1
  import { __awaiter, __rest } from "tslib";
2
- import { BudgetExhaustedError, DEFAULT_BUDGET_EXHAUSTED_MESSAGE, DEFAULT_PROVIDER_REFUSED_MESSAGE, ProviderRefusedError, isObservableAIProviderRegistry, MalformedFunctionCallError, ResponseTruncatedError, vendorTypeOfLabel, } from '@genesislcap/foundation-ai';
2
+ import { BudgetExhaustedError, DEFAULT_BUDGET_EXHAUSTED_MESSAGE, DEFAULT_PROVIDER_REFUSED_MESSAGE, ContextOverflowError, DEFAULT_CONTEXT_OVERFLOW_MESSAGE, ProviderRefusedError, isObservableAIProviderRegistry, MalformedFunctionCallError, ResponseTruncatedError, vendorTypeOfLabel, } from '@genesislcap/foundation-ai';
3
3
  import { NOOP_ACTIVITY_BUS } from '../../channel/ai-activity-bus';
4
4
  import { resolveChatProvider } from '../../config/validate-providers';
5
5
  import { clearSession, getMetaEvents, mergeMetaEvents, recordMetaEvent, recordTurnError, recordTurnRetry, } from '../../state/debug-event-log';
6
6
  import { createInteractionContext, } from '../../state/interaction-context';
7
7
  import { applyCondensation } from '../../utils/condense-history';
8
- import { applyHistoryCap, buildCompactionSummaryPrompt, findCompactionCut, normalizeForProvider, } from '../../utils/history-transform';
8
+ import { estimateRequestTokens } from '../../utils/context-tokens';
9
+ import { applyHistoryCap, buildCompactionSummaryPrompt, planCompaction, normalizeForProvider, } from '../../utils/history-transform';
9
10
  import { logger } from '../../utils/logger';
10
11
  import { messageUsage, sumUsage } from '../../utils/sum-usage';
11
12
  import { TOOL_FOLD_SYMBOL } from '../../utils/tool-fold';
@@ -115,16 +116,6 @@ const HARVESTED_SUBAGENT_EVENTS = new Set([
115
116
  export const REQUEST_CONTINUATION_TOOL = 'request_continuation';
116
117
  /** Paired in history for each `request_continuation` so tool_calls stay balanced for the provider. */
117
118
  const HANDOFF_TOOL_RESULT_PLACEHOLDER = 'Handoff to another specialist — routing continues on the next turn.';
118
- /**
119
- * Plain TS class that drives a multi-turn chat conversation, including the tool-call loop.
120
- * Owned by `FoundationAiAssistant` — created in `connectedCallback`, torn down in `disconnectedCallback`.
121
- *
122
- * Dispatches `'history-updated'` events on itself so the owning element can observe changes.
123
- *
124
- * @fires history-updated - Fired whenever the in-memory chat history changes (append, tool loop, interaction resolution, post-resolve external cost) with the full history snapshot. detail: `ReadonlyArray<ChatMessage>`
125
- *
126
- * @beta
127
- */
128
119
  export class ChatDriver extends EventTarget {
129
120
  constructor(providerRegistry, config = {}) {
130
121
  super();
@@ -755,6 +746,12 @@ export class ChatDriver extends EventTarget {
755
746
  const status = yield this.resolveStatusForProvider(resolvedName, provider);
756
747
  this.lastResolvedModel = status.model;
757
748
  this.lastResolvedProvider = status.provider;
749
+ // The window of the provider THIS call will go to. Read here rather than
750
+ // pushed down by the host, because the host cannot know it until the later
751
+ // `provider-changed` event — so a per-agent provider switch would otherwise
752
+ // have its first call guarded by the previous model's limit, refusing a valid
753
+ // turn on the way up and waving an oversized one through on the way down.
754
+ this.lastResolvedContextLimit = status.contextLimit;
758
755
  if (resolvedName !== this.lastDispatchedProviderName) {
759
756
  this.lastDispatchedProviderName = resolvedName;
760
757
  recordMetaEvent(this.sessionKey, 'provider.selected', {
@@ -788,7 +785,13 @@ export class ChatDriver extends EventTarget {
788
785
  try {
789
786
  const resolved = yield ((_a = provider.getStatus) === null || _a === void 0 ? void 0 : _a.call(provider));
790
787
  if (resolved) {
791
- status = { model: resolved.model, provider: resolved.provider };
788
+ status = {
789
+ model: resolved.model,
790
+ provider: resolved.provider,
791
+ // Carried so the mid-loop guard can measure against the window of the
792
+ // provider this call actually resolved to (GENC-1567).
793
+ contextLimit: resolved.contextLimit,
794
+ };
792
795
  }
793
796
  }
794
797
  catch (_b) {
@@ -962,9 +965,62 @@ export class ChatDriver extends EventTarget {
962
965
  return snapshot;
963
966
  }
964
967
  /**
965
- * Optional transform applied to conversation history immediately before each LLM request.
966
- * Cleared when `undefined`. Does not alter stored history.
968
+ * Set the mid-loop context guard. Its margin is deliberately far smaller than
969
+ * the reserve that blocks NEW turns: that reserve exists so an accepted turn can
970
+ * spend it, and a guard set at the same line would kill every turn that used
971
+ * the headroom it was given.
967
972
  */
973
+ setContextGuard(policy) {
974
+ this.contextGuard = policy && policy.marginTokens > 0 ? policy : undefined;
975
+ }
976
+ /**
977
+ * Whether issuing `requestHistory` would run the context window out.
978
+ *
979
+ * Inert unless a margin has been set AND the resolved provider reports a
980
+ * window — with no window there is nothing to measure against, and inventing
981
+ * one to refuse a request is worse than letting the provider answer.
982
+ *
983
+ * Stopping here is strictly better than letting the request go: the provider
984
+ * would reject an oversized prompt outright, leaving a transcript still too
985
+ * large to retry and no explanation the user can act on, whereas ending the
986
+ * turn keeps history intact so compaction is still available and the work so
987
+ * far is not lost.
988
+ */
989
+ contextExhausted(requestHistory, pendingInput) {
990
+ var _a, _b;
991
+ const guard = this.contextGuard;
992
+ if (!guard)
993
+ return undefined;
994
+ // The resolved provider's own window, falling back to the host's assumption
995
+ // only when the provider reports none. Without the fallback a host that knows
996
+ // its window but uses a provider that does not report it got a UI gate and no
997
+ // running-turn guard — the fallback protecting the composer and nothing else.
998
+ const limit = (_a = this.lastResolvedContextLimit) !== null && _a !== void 0 ? _a : guard.fallbackLimit;
999
+ if (limit == null || limit <= 0)
1000
+ return undefined;
1001
+ const threshold = Math.max(0, limit - guard.marginTokens);
1002
+ // The first call of a turn passes the user's text and attachments SEPARATELY
1003
+ // (`sendMessage` appended them to history, and `baseHistory` drops that last
1004
+ // message precisely because they are supplied out of band). Measuring only
1005
+ // `requestHistory` therefore omitted them — so a large paste or a set of
1006
+ // images, the very payloads most likely to overrun a window, were invisible to
1007
+ // the guard that exists to catch them.
1008
+ const request = pendingInput && (pendingInput.content || ((_b = pendingInput.attachments) === null || _b === void 0 ? void 0 : _b.length))
1009
+ ? [
1010
+ ...requestHistory,
1011
+ {
1012
+ role: 'user',
1013
+ content: pendingInput.content,
1014
+ attachments: pendingInput.attachments,
1015
+ },
1016
+ ]
1017
+ : requestHistory;
1018
+ const estimated = estimateRequestTokens(this.history, request);
1019
+ if (estimated < threshold)
1020
+ return undefined;
1021
+ logger.error(`ChatDriver: ending the turn — the request would fill the context window (~${estimated} of ${limit})`);
1022
+ return { estimated, threshold };
1023
+ }
968
1024
  setProviderHistoryTransform(transform) {
969
1025
  this.providerHistoryTransform = transform;
970
1026
  }
@@ -1085,8 +1141,19 @@ export class ChatDriver extends EventTarget {
1085
1141
  * turns exists behind a clean boundary. Uses the same `history` `compact()`
1086
1142
  * acts on, so the UI's gate can't disagree with the action (GENC-1351 follow-up).
1087
1143
  */
1088
- canCompact() {
1089
- return findCompactionCut(this.history) != null;
1144
+ canCompact(options) {
1145
+ return this.getCompactionPlan(options) != null;
1146
+ }
1147
+ /**
1148
+ * {@inheritDoc AiDriver.getCompactionPlan}
1149
+ *
1150
+ * Runs against the driver's own `history` — the exact list `compact()` acts on
1151
+ * — so a projection and the compaction it describes can never be computed from
1152
+ * different transcripts (the GENC-1351 follow-up that first made `canCompact`
1153
+ * read `history` rather than a mirrored copy).
1154
+ */
1155
+ getCompactionPlan(options) {
1156
+ return planCompaction(this.history, options);
1090
1157
  }
1091
1158
  /**
1092
1159
  * Destructively compact older turns into a single `compacted-summary` message
@@ -1097,9 +1164,9 @@ export class ChatDriver extends EventTarget {
1097
1164
  * the new one. Returns the created summary message, or `null` when there is
1098
1165
  * nothing worth compacting or the default provider cannot summarize.
1099
1166
  */
1100
- compact() {
1167
+ compact(options) {
1101
1168
  return __awaiter(this, void 0, void 0, function* () {
1102
- var _a, _b, _c, _d, _e, _f;
1169
+ var _a, _b, _c, _d, _e, _f, _g;
1103
1170
  if (this.busy)
1104
1171
  return null;
1105
1172
  const defaultProvider = this.providerRegistry.default();
@@ -1108,7 +1175,10 @@ export class ChatDriver extends EventTarget {
1108
1175
  return null;
1109
1176
  }
1110
1177
  const history = this.history;
1111
- const cut = findCompactionCut(history);
1178
+ // Same plan the affordance was gated on — including its worth-it floor, so a
1179
+ // compaction that would reclaim too little is declined here as well as being
1180
+ // greyed out in the UI.
1181
+ const cut = (_a = planCompaction(history, options)) === null || _a === void 0 ? void 0 : _a.cut;
1112
1182
  if (cut == null)
1113
1183
  return null;
1114
1184
  const toCompact = history.slice(0, cut);
@@ -1122,7 +1192,7 @@ export class ChatDriver extends EventTarget {
1122
1192
  const failMessage = "Sorry, I couldn't compact the conversation just now. Nothing was changed — please try again.";
1123
1193
  let summaryText;
1124
1194
  try {
1125
- summaryText = (_b = (_a = (yield defaultProvider.prompt(userMessage, { systemPrompt }))) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
1195
+ summaryText = (_c = (_b = (yield defaultProvider.prompt(userMessage, { systemPrompt }))) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : '';
1126
1196
  }
1127
1197
  catch (e) {
1128
1198
  logger.warn(`ChatDriver: compaction summary failed: ${e instanceof Error ? e.message : e}`);
@@ -1141,7 +1211,7 @@ export class ChatDriver extends EventTarget {
1141
1211
  break;
1142
1212
  }
1143
1213
  }
1144
- const status = yield ((_c = defaultProvider.getStatus) === null || _c === void 0 ? void 0 : _c.call(defaultProvider));
1214
+ const status = yield ((_d = defaultProvider.getStatus) === null || _d === void 0 ? void 0 : _d.call(defaultProvider));
1145
1215
  const createdAt = new Date().toISOString();
1146
1216
  const summaryMessage = {
1147
1217
  role: 'compacted-summary',
@@ -1150,10 +1220,10 @@ export class ChatDriver extends EventTarget {
1150
1220
  // for the oldest messages and sits at the head of history, so this keeps it
1151
1221
  // sorted to the head in any timestamp-ordered view (the debug timeline) and
1152
1222
  // history time-monotonic. Its true creation time is in compaction.createdAt.
1153
- timestamp: (_e = (_d = toCompact[0]) === null || _d === void 0 ? void 0 : _d.timestamp) !== null && _e !== void 0 ? _e : createdAt,
1223
+ timestamp: (_f = (_e = toCompact[0]) === null || _e === void 0 ? void 0 : _e.timestamp) !== null && _f !== void 0 ? _f : createdAt,
1154
1224
  compaction: {
1155
1225
  compactedCount: toCompact.length,
1156
- coveredThroughTimestamp: (_f = toCompact[toCompact.length - 1]) === null || _f === void 0 ? void 0 : _f.timestamp,
1226
+ coveredThroughTimestamp: (_g = toCompact[toCompact.length - 1]) === null || _g === void 0 ? void 0 : _g.timestamp,
1157
1227
  tokensBefore,
1158
1228
  model: status === null || status === void 0 ? void 0 : status.model,
1159
1229
  createdAt,
@@ -1701,6 +1771,11 @@ export class ChatDriver extends EventTarget {
1701
1771
  // Mark before the first turn so the child forces tool use and reports a
1702
1772
  // typed failure (rather than user-facing text) if it never completes.
1703
1773
  child.markAsSubAgent();
1774
+ // The guard is policy, and a sub-agent runs the same tool loop against the
1775
+ // same window. Without this every child was inert — and the
1776
+ // `context_exhausted` sub-agent failure this feature added was unreachable by
1777
+ // any normal path.
1778
+ child.setContextGuard(this.contextGuard);
1704
1779
  // Propagate disposal: if this (parent) driver is torn down while the
1705
1780
  // sub-agent is mid-flight, dispose the child too so its in-flight request
1706
1781
  // aborts. Detached in the `finally` below once the sub-agent completes.
@@ -2085,7 +2160,7 @@ export class ChatDriver extends EventTarget {
2085
2160
  // oxlint-disable-next-line complexity
2086
2161
  runToolLoop(userInput, attachments, transientPrimer) {
2087
2162
  return __awaiter(this, void 0, void 0, function* () {
2088
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
2163
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
2089
2164
  if (!this.systemPrompt) {
2090
2165
  logger.warn('ChatDriver: no systemPrompt set. The assistant will have no instructions — provide a systemPrompt via agents config or the foundation-ai-assistant property.');
2091
2166
  }
@@ -2399,6 +2474,43 @@ export class ChatDriver extends EventTarget {
2399
2474
  turnSnapshot.provider = this.lastResolvedProvider;
2400
2475
  if (this.lastResolvedModel !== undefined)
2401
2476
  turnSnapshot.model = this.lastResolvedModel;
2477
+ // The context wall (GENC-1567) — checked HERE, at the last possible moment,
2478
+ // rather than at the top of the iteration where it started life.
2479
+ //
2480
+ // Two things are only knowable at this point, and getting either wrong
2481
+ // ended valid turns. `historyForCall` is what will actually be sent, after
2482
+ // `condenseWhen` has stubbed spent tool payloads and per-agent masking has
2483
+ // blanked another agent's — stored history deliberately keeps them in full,
2484
+ // so measuring it refused turns whose real request was a fraction of the
2485
+ // size. And `lastResolvedContextLimit` is the window of the provider THIS
2486
+ // call resolved to, which on an agent switch is not the one the host knew
2487
+ // about when it last pushed a threshold down.
2488
+ const contextStop = this.contextExhausted(historyForCall, {
2489
+ content: userInputForCall,
2490
+ attachments: attachmentsForCall,
2491
+ });
2492
+ if (contextStop) {
2493
+ recordTurnError(this.sessionKey, 'context-exhausted', {
2494
+ agent: this.activeAgentName,
2495
+ provider: this.lastResolvedProviderName,
2496
+ contextTokens: contextStop.estimated,
2497
+ guard: contextStop.threshold,
2498
+ limit: this.lastResolvedContextLimit,
2499
+ iterations,
2500
+ isSubAgent: this.isSubAgent,
2501
+ });
2502
+ if (this.isSubAgent) {
2503
+ this.failSubAgent('context_exhausted');
2504
+ }
2505
+ else {
2506
+ this.appendToHistory({
2507
+ role: 'assistant',
2508
+ content: 'I had to stop here — this conversation has filled the available context. ' +
2509
+ 'Compact it to free up room, then ask me to continue.',
2510
+ });
2511
+ }
2512
+ return this.turnDone('context-exhausted');
2513
+ }
2402
2514
  let response;
2403
2515
  try {
2404
2516
  // oxlint-disable-next-line no-await-in-loop
@@ -2484,13 +2596,46 @@ export class ChatDriver extends EventTarget {
2484
2596
  // error is an untyped transport failure, gets re-issued MAX_SETUP_TRANSPORT_RETRIES times
2485
2597
  // against a wall that cannot move, and then surfaces as "something went wrong on my end" —
2486
2598
  // wrong twice over, since nothing went wrong on our end and trying again will not help.
2599
+ // The REQUEST wall (GENC-1567) — the prompt outgrew the window. Placed
2600
+ // beside the account walls and before the transient-retry step for the
2601
+ // same reason: re-issuing an oversized prompt cannot make it fit, and
2602
+ // without this it surfaces as "something went wrong on my end" when in
2603
+ // fact the user has a clear, effective remedy.
2604
+ //
2605
+ // A backstop rather than the main defence — the mid-loop guard should
2606
+ // normally stop the turn before a request this large is built. It is
2607
+ // reached when the provider reports no context window (nothing for the
2608
+ // guard to measure against), when the estimate ran under, or when a
2609
+ // single request was already too large.
2610
+ if (e instanceof ContextOverflowError) {
2611
+ logger.error('ChatDriver: the prompt exceeded the model context window', e);
2612
+ recordTurnError(this.sessionKey, 'context-exhausted', {
2613
+ agent: this.activeAgentName,
2614
+ provider: this.lastResolvedProviderName,
2615
+ vendor: (_e = vendorTypeOfLabel(e.vendorLabel)) !== null && _e !== void 0 ? _e : this.lastResolvedProvider,
2616
+ promptTokens: e.promptTokens,
2617
+ limitTokens: e.limitTokens,
2618
+ via: 'provider',
2619
+ isSubAgent: this.isSubAgent,
2620
+ });
2621
+ if (this.isSubAgent) {
2622
+ this.failSubAgent('context_exhausted');
2623
+ }
2624
+ else {
2625
+ this.appendToHistory({
2626
+ role: 'assistant',
2627
+ content: DEFAULT_CONTEXT_OVERFLOW_MESSAGE,
2628
+ });
2629
+ }
2630
+ return this.turnDone('context-exhausted');
2631
+ }
2487
2632
  if (e instanceof ProviderRefusedError) {
2488
2633
  if (this.isSubAgent) {
2489
2634
  logger.error('ChatDriver: provider refused the request', e);
2490
2635
  recordTurnError(this.sessionKey, 'provider-refused', {
2491
2636
  agent: this.activeAgentName,
2492
2637
  provider: this.lastResolvedProviderName,
2493
- vendor: (_e = vendorTypeOfLabel(e.vendorLabel)) !== null && _e !== void 0 ? _e : this.lastResolvedProvider,
2638
+ vendor: (_f = vendorTypeOfLabel(e.vendorLabel)) !== null && _f !== void 0 ? _f : this.lastResolvedProvider,
2494
2639
  kind: e.kind,
2495
2640
  upstreamStatus: e.upstreamStatus,
2496
2641
  upstreamType: e.upstreamType,
@@ -2524,7 +2669,7 @@ export class ChatDriver extends EventTarget {
2524
2669
  recordTurnError(this.sessionKey, 'budget-exhausted', {
2525
2670
  agent: this.activeAgentName,
2526
2671
  provider: this.lastResolvedProviderName,
2527
- vendor: (_g = (_f = vendorTypeOfLabel(e.vendorLabel)) !== null && _f !== void 0 ? _f : vendorTypeOfLabel(e.serverVendor)) !== null && _g !== void 0 ? _g : this.lastResolvedProvider,
2672
+ vendor: (_h = (_g = vendorTypeOfLabel(e.vendorLabel)) !== null && _g !== void 0 ? _g : vendorTypeOfLabel(e.serverVendor)) !== null && _h !== void 0 ? _h : this.lastResolvedProvider,
2528
2673
  budgetUsd: e.budgetUsd,
2529
2674
  spentUsd: e.spentUsd,
2530
2675
  isSubAgent: true,
@@ -2622,15 +2767,15 @@ export class ChatDriver extends EventTarget {
2622
2767
  // fallback chain answered on a different model than the one we asked for.
2623
2768
  if (response.model !== undefined)
2624
2769
  turnSnapshot.model = response.model;
2625
- const isThinkingStep = response.content && ((_h = response.toolCalls) === null || _h === void 0 ? void 0 : _h.length);
2626
- const isEmptyResponse = !((_j = response.content) === null || _j === void 0 ? void 0 : _j.trim()) && !((_k = response.toolCalls) === null || _k === void 0 ? void 0 : _k.length);
2770
+ const isThinkingStep = response.content && ((_j = response.toolCalls) === null || _j === void 0 ? void 0 : _j.length);
2771
+ const isEmptyResponse = !((_k = response.content) === null || _k === void 0 ? void 0 : _k.trim()) && !((_l = response.toolCalls) === null || _l === void 0 ? void 0 : _l.length);
2627
2772
  // A pre-output refusal (safety-classifier decline, e.g. Fable 5 `stop_reason: 'refusal'`)
2628
2773
  // comes back with empty content, so it looks like a blank response — but it is deterministic:
2629
2774
  // retrying re-sends the identical request and refuses again, burning up to
2630
2775
  // MAX_EMPTY_RESPONSE_RETRIES turns on the most expensive models for the same outcome, ending
2631
2776
  // in the misleading "blank response" message. Treat it as a terminal, non-retried failure with
2632
2777
  // its own reason and message. (GENC-1461)
2633
- const isRefusal = ((_l = response.responseMeta) === null || _l === void 0 ? void 0 : _l.finishReason) === 'refusal';
2778
+ const isRefusal = ((_m = response.responseMeta) === null || _m === void 0 ? void 0 : _m.finishReason) === 'refusal';
2634
2779
  if (isEmptyResponse) {
2635
2780
  emptyResponseAttempts += 1;
2636
2781
  if (!isRefusal && emptyResponseAttempts < MAX_EMPTY_RESPONSE_RETRIES) {
@@ -2704,7 +2849,7 @@ export class ChatDriver extends EventTarget {
2704
2849
  emptyResponseAttempts = 0;
2705
2850
  malformedAttempts = 0;
2706
2851
  setupTransportAttempts = 0;
2707
- if (!((_m = response.toolCalls) === null || _m === void 0 ? void 0 : _m.length)) {
2852
+ if (!((_o = response.toolCalls) === null || _o === void 0 ? void 0 : _o.length)) {
2708
2853
  break;
2709
2854
  }
2710
2855
  const [toolCalls, systemCalls] = response.toolCalls.reduce((acc, tc) => {
@@ -2934,7 +3079,7 @@ export class ChatDriver extends EventTarget {
2934
3079
  // The response was appended before execution — find it and annotate.
2935
3080
  let tcMsgIdx = -1;
2936
3081
  for (let i = this.history.length - 1; i >= 0; i -= 1) {
2937
- if (this.history[i].role === 'assistant' && ((_o = this.history[i].toolCalls) === null || _o === void 0 ? void 0 : _o.length)) {
3082
+ if (this.history[i].role === 'assistant' && ((_p = this.history[i].toolCalls) === null || _p === void 0 ? void 0 : _p.length)) {
2938
3083
  tcMsgIdx = i;
2939
3084
  break;
2940
3085
  }
@@ -2972,7 +3117,7 @@ export class ChatDriver extends EventTarget {
2972
3117
  const unknownTools = [
2973
3118
  ...new Set([
2974
3119
  ...this.recentUnknownToolNames,
2975
- ...((_p = response.toolCalls) !== null && _p !== void 0 ? _p : [])
3120
+ ...((_q = response.toolCalls) !== null && _q !== void 0 ? _q : [])
2976
3121
  .filter((tc) => unknownToolIds.has(tc.id))
2977
3122
  .map((tc) => tc.name),
2978
3123
  ]),
@@ -2984,7 +3129,7 @@ export class ChatDriver extends EventTarget {
2984
3129
  const staleTools = [
2985
3130
  ...new Set([
2986
3131
  ...this.recentStaleToolNames,
2987
- ...((_q = response.toolCalls) !== null && _q !== void 0 ? _q : [])
3132
+ ...((_r = response.toolCalls) !== null && _r !== void 0 ? _r : [])
2988
3133
  .filter((tc) => staleToolIds.has(tc.id))
2989
3134
  .map((tc) => tc.name),
2990
3135
  ]),
@@ -203,13 +203,21 @@ export class OrchestratingDriver extends EventTarget {
203
203
  getRawHistory() {
204
204
  return this.chatDriver.getHistory();
205
205
  }
206
+ /** {@inheritDoc AiDriver.setContextGuard} */
207
+ setContextGuard(policy) {
208
+ this.chatDriver.setContextGuard(policy);
209
+ }
206
210
  /** {@inheritDoc AiDriver.compact} */
207
- compact() {
208
- return this.chatDriver.compact();
211
+ compact(options) {
212
+ return this.chatDriver.compact(options);
209
213
  }
210
214
  /** {@inheritDoc AiDriver.canCompact} */
211
- canCompact() {
212
- return this.chatDriver.canCompact();
215
+ canCompact(options) {
216
+ return this.chatDriver.canCompact(options);
217
+ }
218
+ /** {@inheritDoc AiDriver.getCompactionPlan} */
219
+ getCompactionPlan(options) {
220
+ return this.chatDriver.getCompactionPlan(options);
213
221
  }
214
222
  /** Delegates to the inner {@link ChatDriver} — turns are captured there. */
215
223
  getTurnSnapshots() {