@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
@@ -638,6 +638,16 @@ export declare interface AiDriver extends EventTarget {
638
638
  * retain the active config implement it.
639
639
  */
640
640
  getActiveAgent?(): AgentConfig | undefined;
641
+ /**
642
+ * Set the mid-loop context guard (GENC-1567). `undefined` disables it.
643
+ *
644
+ * Carries a margin rather than an absolute threshold: the window belongs to
645
+ * whichever provider the next call resolves to, which the driver learns during
646
+ * the call and the host cannot know until afterwards. The host owns the policy;
647
+ * the driver applies it against the model actually being addressed. Optional —
648
+ * only drivers that own a tool loop implement it.
649
+ */
650
+ setContextGuard?(policy?: ContextGuardPolicy): void;
641
651
  /**
642
652
  * Destructively compact older conversation turns into a single
643
653
  * `compacted-summary` message (GENC-1351 §5.7), summarizing them via the
@@ -645,15 +655,29 @@ export declare interface AiDriver extends EventTarget {
645
655
  * success. Resolves to the created summary message, or `null` when there is
646
656
  * nothing worth compacting or the provider cannot summarize. Optional — only
647
657
  * drivers that own history implement it.
658
+ *
659
+ * Pass the same `options` used for {@link AiDriver.getCompactionPlan} so the
660
+ * action does exactly what the affordance advertised.
648
661
  */
649
- compact?(): Promise<ChatMessage | null>;
662
+ compact?(options?: CompactionPlanOptions): Promise<ChatMessage | null>;
650
663
  /**
651
- * Whether `compact()` would compact something right now a summarizable run of
652
- * older turns exists behind a clean boundary. The UI gates its "Compact"
653
- * affordance on this, so the button reflects the exact history `compact()` acts
654
- * on (GENC-1351). Optional — only history-owning drivers implement it.
664
+ * What compacting right now would cost and reclaim, without running the
665
+ * summarizer (GENC-1567) or `null` when there is no safe cut, or the
666
+ * projected reclaim is not worth the call.
667
+ *
668
+ * The single source of truth for every compaction affordance: the menu's
669
+ * enabled state, its tooltip, the context gate's banner copy, and the action
670
+ * itself all read this, so none of them can promise a reclaim the action does
671
+ * not deliver. Optional — only history-owning drivers implement it.
655
672
  */
656
- canCompact?(): boolean;
673
+ getCompactionPlan?(options?: CompactionPlanOptions): CompactionProjection | null;
674
+ /**
675
+ * Whether `compact()` would compact something right now. Convenience over
676
+ * {@link AiDriver.getCompactionPlan} — prefer the plan where the caller needs
677
+ * to say how much would be reclaimed. Optional — only history-owning drivers
678
+ * implement it.
679
+ */
680
+ canCompact?(options?: CompactionPlanOptions): boolean;
657
681
  /**
658
682
  * Returns true if the driver is currently processing a request.
659
683
  */
@@ -1133,6 +1157,34 @@ declare interface BaseAgentConfig {
1133
1157
  * @beta
1134
1158
  */
1135
1159
  notResumableMessage?: string;
1160
+ /**
1161
+ * Tokens this agent wants kept free for one of its turns (GENC-1567).
1162
+ * Overrides `chatConfig.context.reserveTokens` while this agent is active;
1163
+ * leave unset to inherit it.
1164
+ *
1165
+ * The third member of the flow-safety group, alongside
1166
+ * {@link BaseAgentConfig.resumable} and {@link BaseAgentConfig.notResumableMessage}:
1167
+ * `resumable` says whether the agent may be interrupted, and this says how much
1168
+ * room it needs so that it does not have to be. An agent whose flow runs a long
1169
+ * tool loop, or returns large payloads, needs more than the global default.
1170
+ *
1171
+ * **When it takes effect.** The reserve is read from the agent that is already
1172
+ * ACTIVE, so it governs the gate from that agent's next send onwards — one turn
1173
+ * later than a true pre-flight. It is deliberately not enforced at flow entry:
1174
+ * `flow-owner-changed` fires while the driver is already routing the turn, so
1175
+ * gating there would mean aborting a turn in flight. An agent that activates
1176
+ * mid-turn therefore gets its first turn under the global reserve; the mid-loop
1177
+ * context guard is what stops that turn running away.
1178
+ *
1179
+ * **Set this from measurement, not from a guess.** Per-turn `inputTokens` is
1180
+ * recorded for every call, so a single real run of the flow tells you its peak
1181
+ * growth; a number invented up front will be wrong in whichever direction is
1182
+ * least convenient. Leaving it unset and inheriting the global default is the
1183
+ * right starting position.
1184
+ *
1185
+ * @beta
1186
+ */
1187
+ contextReserve?: ContextReserveInput;
1136
1188
  }
1137
1189
 
1138
1190
  /**
@@ -1201,16 +1253,6 @@ export { CachePolicy }
1201
1253
  */
1202
1254
  export declare type CachePolicyInput = CachePolicy | ((ctx: SystemPromptContext) => CachePolicy | Promise<CachePolicy>);
1203
1255
 
1204
- /**
1205
- * Plain TS class that drives a multi-turn chat conversation, including the tool-call loop.
1206
- * Owned by `FoundationAiAssistant` — created in `connectedCallback`, torn down in `disconnectedCallback`.
1207
- *
1208
- * Dispatches `'history-updated'` events on itself so the owning element can observe changes.
1209
- *
1210
- * @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>`
1211
- *
1212
- * @beta
1213
- */
1214
1256
  export declare class ChatDriver extends EventTarget implements AiDriver {
1215
1257
  private readonly providerRegistry;
1216
1258
  private history;
@@ -1819,6 +1861,39 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1819
1861
  * Optional transform applied to conversation history immediately before each LLM request.
1820
1862
  * Cleared when `undefined`. Does not alter stored history.
1821
1863
  */
1864
+ /**
1865
+ * Mid-loop context guard policy (GENC-1567). `undefined` disables the guard.
1866
+ *
1867
+ * Carries a MARGIN rather than an absolute threshold, and the difference is the
1868
+ * whole point: the threshold depends on the window of whichever provider the
1869
+ * next call resolves to, which the driver learns during the call and the host
1870
+ * cannot know until afterwards. The host owns the policy; the driver applies it
1871
+ * against the model actually being addressed.
1872
+ */
1873
+ private contextGuard?;
1874
+ /** Window of the provider the current call resolved to, if it reports one. */
1875
+ private lastResolvedContextLimit?;
1876
+ /**
1877
+ * Set the mid-loop context guard. Its margin is deliberately far smaller than
1878
+ * the reserve that blocks NEW turns: that reserve exists so an accepted turn can
1879
+ * spend it, and a guard set at the same line would kill every turn that used
1880
+ * the headroom it was given.
1881
+ */
1882
+ setContextGuard(policy?: ContextGuardPolicy): void;
1883
+ /**
1884
+ * Whether issuing `requestHistory` would run the context window out.
1885
+ *
1886
+ * Inert unless a margin has been set AND the resolved provider reports a
1887
+ * window — with no window there is nothing to measure against, and inventing
1888
+ * one to refuse a request is worse than letting the provider answer.
1889
+ *
1890
+ * Stopping here is strictly better than letting the request go: the provider
1891
+ * would reject an oversized prompt outright, leaving a transcript still too
1892
+ * large to retry and no explanation the user can act on, whereas ending the
1893
+ * turn keeps history intact so compaction is still available and the work so
1894
+ * far is not lost.
1895
+ */
1896
+ private contextExhausted;
1822
1897
  setProviderHistoryTransform(transform?: (history: ChatMessage[]) => ChatMessage[]): void;
1823
1898
  getHistory(): ReadonlyArray<ChatMessage>;
1824
1899
  getRawHistory(): readonly ChatMessage[];
@@ -1830,7 +1905,16 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1830
1905
  * turns exists behind a clean boundary. Uses the same `history` `compact()`
1831
1906
  * acts on, so the UI's gate can't disagree with the action (GENC-1351 follow-up).
1832
1907
  */
1833
- canCompact(): boolean;
1908
+ canCompact(options?: CompactionPlanOptions): boolean;
1909
+ /**
1910
+ * {@inheritDoc AiDriver.getCompactionPlan}
1911
+ *
1912
+ * Runs against the driver's own `history` — the exact list `compact()` acts on
1913
+ * — so a projection and the compaction it describes can never be computed from
1914
+ * different transcripts (the GENC-1351 follow-up that first made `canCompact`
1915
+ * read `history` rather than a mirrored copy).
1916
+ */
1917
+ getCompactionPlan(options?: CompactionPlanOptions): CompactionProjection | null;
1834
1918
  /**
1835
1919
  * Destructively compact older turns into a single `compacted-summary` message
1836
1920
  * (GENC-1351 §5.7). Summarizes everything before a clean recent-tail boundary
@@ -1840,7 +1924,7 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1840
1924
  * the new one. Returns the created summary message, or `null` when there is
1841
1925
  * nothing worth compacting or the default provider cannot summarize.
1842
1926
  */
1843
- compact(): Promise<ChatMessage | null>;
1927
+ compact(options?: CompactionPlanOptions): Promise<ChatMessage | null>;
1844
1928
  isBusy(): boolean;
1845
1929
  /**
1846
1930
  * Wire a parent driver as the host for this driver's interactions. When set,
@@ -2108,6 +2192,28 @@ export { ChatThinkingPolicy }
2108
2192
 
2109
2193
  export { ChatToolChoice }
2110
2194
 
2195
+ /** Options for {@link planCompaction}. */
2196
+ declare interface CompactionPlanOptions {
2197
+ /** Token budget for the verbatim tail. */
2198
+ tailTokenBudget?: number;
2199
+ /** Minimum reclaim for the plan to be considered worth running. */
2200
+ minReclaimTokens?: number;
2201
+ }
2202
+
2203
+ /** What a compaction at a given cut would cost and reclaim. */
2204
+ declare interface CompactionProjection {
2205
+ /** Index the verbatim tail starts at — everything before it is summarized. */
2206
+ cut: number;
2207
+ /** How many messages the summary would replace. */
2208
+ compactedCount: number;
2209
+ /** Estimated context now. */
2210
+ tokensBefore: number;
2211
+ /** Estimated context afterwards — preamble + summary allowance + the tail. */
2212
+ tokensAfter: number;
2213
+ /** `tokensBefore − tokensAfter`, floored at zero. */
2214
+ reclaimed: number;
2215
+ }
2216
+
2111
2217
  /**
2112
2218
  * `aria-describedby` for the composer's textarea, send button and attach button:
2113
2219
  * the banner's id whenever the banner has something to say, otherwise `null` (so
@@ -2133,6 +2239,51 @@ export { ChatToolChoice }
2133
2239
  */
2134
2240
  export declare const composerDescribedBy: (x: FoundationAiAssistant) => string | null;
2135
2241
 
2242
+ /**
2243
+ * Plain TS class that drives a multi-turn chat conversation, including the tool-call loop.
2244
+ * Owned by `FoundationAiAssistant` — created in `connectedCallback`, torn down in `disconnectedCallback`.
2245
+ *
2246
+ * Dispatches `'history-updated'` events on itself so the owning element can observe changes.
2247
+ *
2248
+ * @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>`
2249
+ *
2250
+ * @beta
2251
+ */
2252
+ /**
2253
+ * Mid-loop context guard policy, pushed down by the host (GENC-1567).
2254
+ *
2255
+ * @internal
2256
+ */
2257
+ export declare interface ContextGuardPolicy {
2258
+ /**
2259
+ * How close to the model's context window a running tool loop may get before
2260
+ * it is stopped.
2261
+ */
2262
+ marginTokens: number;
2263
+ /**
2264
+ * Window to assume when the resolved provider reports none. Consulted only in
2265
+ * that case — a provider that states its own window always wins.
2266
+ */
2267
+ fallbackLimit?: number;
2268
+ }
2269
+
2270
+ /**
2271
+ * Context headroom an agent reserves for itself, in tokens.
2272
+ *
2273
+ * A plain number, unlike the value-or-resolver shape `provider` / `temperature` /
2274
+ * `resumable` use. The resolver form was drafted for it and removed before
2275
+ * release: the gate is recomputed from a synchronous getter with no turn context
2276
+ * to resolve against, so no path could ever have invoked one. Accepting a
2277
+ * type-correct option that is guaranteed to do nothing is worse than not
2278
+ * offering it — a warning after the fact still leaves the configuration inert.
2279
+ *
2280
+ * It can widen to a resolver later without breaking a caller, since every
2281
+ * existing `number` stays valid.
2282
+ *
2283
+ * @beta
2284
+ */
2285
+ export declare type ContextReserveInput = number;
2286
+
2136
2287
  /** A model used during a recorded cost session. */
2137
2288
  export declare interface CostSessionModelEntry {
2138
2289
  model: string;
@@ -3664,6 +3815,27 @@ export declare class FoundationAiAssistant extends GenesisElement {
3664
3815
  sessionKeyChanged(_oldValue: string | undefined, _newValue: string | undefined): void;
3665
3816
  disconnectedCallback(): void;
3666
3817
  private resolveContextLimit;
3818
+ /**
3819
+ * Push the mid-loop context guard down to the driver (GENC-1567).
3820
+ *
3821
+ * Called wherever the inputs can change — the resolved provider status, and a
3822
+ * freshly built driver. A driver that never receives one simply runs without
3823
+ * the guard, which is the right default: it fires only when a real context
3824
+ * window is known.
3825
+ */
3826
+ private syncContextGuard;
3827
+ /**
3828
+ * Guard policy pushed to the driver, or `undefined` when the host has switched
3829
+ * the gate off — so `enabled: false` really does mean no local blocking of any
3830
+ * kind, UI or mid-loop.
3831
+ *
3832
+ * A MARGIN, not a threshold: the driver resolves the window of the provider
3833
+ * each call actually goes to. The fallback window rides along because the
3834
+ * driver cannot learn it any other way — without it, a host that knows its
3835
+ * window while its provider does not report one had `fallbackContextLimit`
3836
+ * protecting the composer and nothing else.
3837
+ */
3838
+ private get contextGuardPolicy();
3667
3839
  private loadProviderStatuses;
3668
3840
  chatConfigChanged(): void;
3669
3841
  /**
@@ -3795,16 +3967,175 @@ export declare class FoundationAiAssistant extends GenesisElement {
3795
3967
  * clearing/compacting mid-journey would be disruptive).
3796
3968
  */
3797
3969
  get sessionMenuEnabled(): boolean;
3970
+ /**
3971
+ * Whether the menu is on screen ONLY because the context gate put it there.
3972
+ *
3973
+ * @internal
3974
+ */
3975
+ get sessionMenuForcedByContext(): boolean;
3976
+ /**
3977
+ * Whether to withhold Clear from a menu the context gate forced open.
3978
+ *
3979
+ * The hosts who hid this menu did so to protect a flow from being cleared
3980
+ * mid-journey, and handing someone a Clear button at the moment they are stuck
3981
+ * hunting for a way out is how a conversation gets destroyed by accident.
3982
+ *
3983
+ * But only **while Compact is a real escape**. When the plan says compacting
3984
+ * would not free enough, starting a new conversation is the only way out — and
3985
+ * the blocked copy says exactly that, pointing the user at this menu to do it.
3986
+ * Withholding Clear there would send them to a menu that does not contain the
3987
+ * action they were just told to take.
3988
+ *
3989
+ * @internal
3990
+ */
3991
+ get clearWithheldByContext(): boolean;
3798
3992
  /** The persistence toggle row renders only when a provider is configured. */
3799
3993
  get persistenceToggleable(): boolean;
3800
3994
  /** Whether persistence is currently active (on + a provider) for this session. */
3801
3995
  get persistenceActive(): boolean;
3802
3996
  /**
3803
- * Whether the menu's "Compact" action would do anything (GENC-1351 §5.7).
3804
- * Delegates to `driver.canCompact()` so the gate uses the exact same history
3805
- * `compact()` acts on — one source of truth. Also reads `messages`, purely to
3806
- * stay reactive as the conversation grows: the driver's history isn't a tracked
3807
- * observable, so the button keys its re-evaluation off the Redux mirror.
3997
+ * Context-headroom thresholds for the active model (GENC-1567) — the warning
3998
+ * and block lines, plus the tail budget compaction aims at.
3999
+ *
4000
+ * The agent's own `contextReserve` is deliberately NOT folded in here. This is
4001
+ * the AMBIENT budget, which protects any turn from any agent; a per-agent
4002
+ * reserve is an additional, larger requirement checked when that agent takes
4003
+ * ownership of a flow, where there is a turn context to resolve its function
4004
+ * form against.
4005
+ *
4006
+ * Overhead is read off `messages` (the Redux mirror) rather than the driver's
4007
+ * own history, which the compaction plan uses. That is safe rather than sloppy:
4008
+ * `estimateSystemOverhead` only looks at messages BEFORE the first usage-bearing
4009
+ * one, and the two lists agree completely that early — the drift between mirror
4010
+ * and driver history is only ever at the tail.
4011
+ */
4012
+ get contextBudget(): ResolvedContextBudget;
4013
+ /**
4014
+ * The active agent's declared `contextReserve`, when it is a plain number.
4015
+ *
4016
+ * Read off the LIVE active agent rather than checked at flow entry, which is
4017
+ * where the design first put it. `flow-owner-changed` turned out to fire while
4018
+ * the driver is already routing the turn — after the send was allowed — so it
4019
+ * is not a pre-flight hook, and gating there would mean aborting a turn in
4020
+ * flight. Reading the active agent instead means a hungry agent's reserve
4021
+ * governs the gate from its next send onwards: one turn later than a true
4022
+ * pre-flight, but synchronous, and it never has to interrupt anything.
4023
+ *
4024
+ * `contextReserve` is a plain number by design — see `ContextReserveInput` for
4025
+ * why a resolver form was dropped rather than accepted and ignored.
4026
+ *
4027
+ * @internal
4028
+ */
4029
+ private get activeAgentReserveTokens();
4030
+ /**
4031
+ * What compacting right now would reclaim, or `null` when it would not be
4032
+ * worth it (GENC-1567). Sized against {@link FoundationAiAssistant.contextBudget},
4033
+ * so the plan aims at this model's window rather than a fixed default.
4034
+ *
4035
+ * Reads `messages` to stay reactive as the conversation grows: the driver's
4036
+ * history is not a tracked observable, so every consumer of this keys its
4037
+ * re-evaluation off the Redux mirror.
4038
+ */
4039
+ get compactionPlan(): CompactionProjection | null;
4040
+ /**
4041
+ * Tooltip and hint for the Compact row — why it is or is not available.
4042
+ *
4043
+ * Replaces a fixed "Not enough conversation yet to be worth compacting", which
4044
+ * since GENC-1567 could be flatly wrong: a long conversation whose tail is one
4045
+ * indivisible tool result has plenty of conversation and still cannot be
4046
+ * compacted. Reading the plan means the hint states the actual reason.
4047
+ *
4048
+ * @internal
4049
+ */
4050
+ get compactHint(): string;
4051
+ /**
4052
+ * How close this conversation is to the model's context window (GENC-1567).
4053
+ *
4054
+ * **Derived, never latched** — the opposite of {@link FoundationAiAssistant.blocked}.
4055
+ * A budget wall stays set because nothing the user does fixes it; this clears
4056
+ * itself the moment the conversation shrinks, which is precisely what the user
4057
+ * is being asked to do. The two must not be conflated: `blocked` is also
4058
+ * deliberately preserved across `resetSession`, and a context gate obviously
4059
+ * must not survive starting a new chat.
4060
+ *
4061
+ * **A flow in progress is never blocked, only warned.** Machine state lives
4062
+ * outside the transcript, so a stateful flow is not desynced by compaction —
4063
+ * but the summary can still drop detail the flow's own prompt leans on, and
4064
+ * blocking mid-journey would strand the user between a flow they cannot finish
4065
+ * and a compaction that is a poor idea right there. Letting the flow complete
4066
+ * and gating on the way out is the lesser harm; the headroom reserve exists so
4067
+ * this is rare rather than routine.
4068
+ */
4069
+ get contextGate(): 'ok' | 'warn' | 'blocked';
4070
+ /**
4071
+ * What the composer notice says about the context gate.
4072
+ *
4073
+ * The copy is a function of whether compaction can actually clear the gate,
4074
+ * which is why it reads {@link FoundationAiAssistant.compactionPlan} rather
4075
+ * than assuming. Telling someone to compact when the projection says it would
4076
+ * not free enough — a tail that is one indivisible tool result, say — sends
4077
+ * them into a summarizer call that leaves them exactly where they were.
4078
+ *
4079
+ * @internal
4080
+ */
4081
+ get contextGateReason(): string;
4082
+ /**
4083
+ * Short label the session-menu pill wears while the gate is active — the
4084
+ * primary visible signal, in place of a banner.
4085
+ *
4086
+ * Borrowed from the agent pin, which is the established way this composer says
4087
+ * "a mode is in force": a pill that grows a label rather than a strip of prose
4088
+ * above the input. It states the CONDITION only; the remedy lives in the
4089
+ * placeholder once the composer is actually blocked, which is the moment the
4090
+ * user needs to be told what to do rather than merely warned.
4091
+ *
4092
+ * @internal
4093
+ */
4094
+ get contextGateLabel(): string;
4095
+ /**
4096
+ * Whether the composer notice has anything to show — a budget wall or the
4097
+ * context gate. One region for both, so the live region stays single and
4098
+ * registered (see the banner template's accessibility note).
4099
+ *
4100
+ * @internal
4101
+ */
4102
+ get composerNoticeVisible(): boolean;
4103
+ /**
4104
+ * Text for that region. A budget wall outranks the context gate: it is the
4105
+ * condition the user cannot clear, so it must not be hidden behind advice
4106
+ * about compacting that would not restore sending anyway.
4107
+ *
4108
+ * For the context gate the region is present and announced but not drawn — see
4109
+ * {@link FoundationAiAssistant.composerNoticeSrOnly}.
4110
+ *
4111
+ * @internal
4112
+ */
4113
+ get composerNoticeText(): string;
4114
+ /**
4115
+ * Whether the notice region should be announced but not drawn.
4116
+ *
4117
+ * The context gate shows itself visually through the pill's label, the
4118
+ * composer outline and the placeholder — a strip of prose on top of those
4119
+ * would be a fourth statement of the same thing. But dropping the region
4120
+ * outright would be an accessibility regression, and a documented one: a
4121
+ * `role="status"` region announces MUTATIONS to a region already being
4122
+ * observed, and it is the only announcement available here, because the
4123
+ * composer flips to `disabled` at the same moment and a disabled control is out
4124
+ * of the tab order — so neither the placeholder nor an `aria-label` on it is
4125
+ * ever voiced.
4126
+ *
4127
+ * So the region stays mounted, keeps its text and keeps announcing; only the
4128
+ * visual treatment is withheld. A budget wall still draws its banner, because
4129
+ * that condition has no other visible expression.
4130
+ *
4131
+ * @internal
4132
+ */
4133
+ get composerNoticeSrOnly(): boolean;
4134
+ /**
4135
+ * Whether the menu's "Compact" action would do anything (GENC-1351 §5.7,
4136
+ * GENC-1567). Delegates to the driver's plan so the gate uses the exact same
4137
+ * history `compact()` acts on, judged against the same budget — one source of
4138
+ * truth for both whether a cut is legal and whether it reclaims enough.
3808
4139
  */
3809
4140
  get compactable(): boolean;
3810
4141
  toggleSessionMenu(): void;
@@ -4591,10 +4922,14 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
4591
4922
  setFlowOwner(name: string | null): void;
4592
4923
  loadHistory(messages: ChatMessage[]): void;
4593
4924
  getRawHistory(): readonly ChatMessage[];
4925
+ /** {@inheritDoc AiDriver.setContextGuard} */
4926
+ setContextGuard(policy?: ContextGuardPolicy): void;
4594
4927
  /** {@inheritDoc AiDriver.compact} */
4595
- compact(): Promise<ChatMessage | null>;
4928
+ compact(options?: CompactionPlanOptions): Promise<ChatMessage | null>;
4596
4929
  /** {@inheritDoc AiDriver.canCompact} */
4597
- canCompact(): boolean;
4930
+ canCompact(options?: CompactionPlanOptions): boolean;
4931
+ /** {@inheritDoc AiDriver.getCompactionPlan} */
4932
+ getCompactionPlan(options?: CompactionPlanOptions): CompactionProjection | null;
4598
4933
  /** Delegates to the inner {@link ChatDriver} — turns are captured there. */
4599
4934
  getTurnSnapshots(): ReadonlyArray<TurnSnapshot>;
4600
4935
  /** Delegates to the inner {@link ChatDriver} — interactions resolve there, so it holds the buffer. */
@@ -4745,6 +5080,50 @@ declare type ProviderRefusedDetail = NonNullable<Extract<ChatDriverResult, {
4745
5080
  /** Name reserved for the cross-agent handoff tool — injected by OrchestratingDriver. */
4746
5081
  export declare const REQUEST_CONTINUATION_TOOL = "request_continuation";
4747
5082
 
5083
+ /** Resolved context-headroom thresholds for the active model. */
5084
+ declare interface ResolvedContextBudget {
5085
+ /**
5086
+ * Whether the gate can act. False when the host disabled it, or when no
5087
+ * context limit is known — with no limit there is no percentage, and blocking
5088
+ * against a guessed window would be worse than not blocking at all.
5089
+ */
5090
+ enabled: boolean;
5091
+ /** The context window in play, from the provider or the configured fallback. */
5092
+ limit?: number;
5093
+ /** Headroom reserved for one turn. */
5094
+ reserveTokens: number;
5095
+ /** Context size at which the warning appears. */
5096
+ warnAt?: number;
5097
+ /** Context size at which new sends are blocked. */
5098
+ blockAt?: number;
5099
+ /**
5100
+ * Token budget for the verbatim tail a compaction leaves behind.
5101
+ *
5102
+ * Derived so a compaction lands back below the WARNING line, not merely below
5103
+ * the block: clearing the block by a hair would re-warn on the next turn and
5104
+ * make the user compact again almost immediately. The summary's own allowance
5105
+ * is subtracted because it occupies the space it is meant to free.
5106
+ */
5107
+ tailTokenBudget: number;
5108
+ /** Smallest reclaim for compaction to be worth running. */
5109
+ minReclaimTokens: number;
5110
+ /**
5111
+ * Context size at which a RUNNING turn is stopped mid-loop (GENC-1567).
5112
+ *
5113
+ * Deliberately far higher than {@link ResolvedContextBudget.blockAt}, and the
5114
+ * gap is the whole point: `blockAt` is where new turns stop being ACCEPTED, and
5115
+ * the reserve below it exists precisely so an accepted turn can spend it. A
5116
+ * mid-loop guard set at `blockAt` would kill every turn that used the headroom
5117
+ * it was given, which is the opposite of the intent.
5118
+ *
5119
+ * This is the last-resort edge — close enough to the window that one more
5120
+ * request would likely be rejected outright. Stopping here keeps the
5121
+ * transcript intact and compaction possible, where letting the request go
5122
+ * gives a provider error and a conversation still too big to retry.
5123
+ */
5124
+ abortAt?: number;
5125
+ }
5126
+
4748
5127
  /** Resolved cost-history settings from `chatConfig.costHistory`. */
4749
5128
  declare interface ResolvedCostHistoryConfig {
4750
5129
  /** When `true`, sessions are persisted automatically and history is shown. */