@genesislcap/foundation-ai 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.
@@ -813,6 +813,8 @@ export declare type ChatAttachment = ChatTextAttachment | ChatImageAttachment;
813
813
  export declare interface ChatConfig {
814
814
  /** UI display settings — toggles, file upload, animations, etc. */
815
815
  ui?: ChatUiConfig;
816
+ /** Context-window headroom and the compaction gate. */
817
+ context?: ChatContextConfig;
816
818
  /** Cost tab display and opt-in build-history persistence. */
817
819
  costHistory?: ChatCostHistoryConfig;
818
820
  /** Agent engine settings — iteration limits, handoff caps, classifier tuning. */
@@ -823,6 +825,74 @@ export declare interface ChatConfig {
823
825
  picker?: ChatAgentPickerConfig;
824
826
  }
825
827
 
828
+ /**
829
+ * Context-window management for the chat assistant (GENC-1567).
830
+ *
831
+ * The assistant reserves headroom rather than letting the conversation run to
832
+ * the edge of the model's window and compacting automatically at the last
833
+ * moment. The reason is that a turn is not atomic in cost: a send allowed just
834
+ * under a threshold can run a long tool loop and add a large amount on its own,
835
+ * so a threshold set as a fraction of the window bounds only the size at the
836
+ * START of a turn. What has to be bounded is the growth of one whole turn — and
837
+ * that is a number of tokens, not a percentage.
838
+ *
839
+ * It is also why compaction here is user-initiated rather than automatic:
840
+ * machine state for a stateful flow lives outside the transcript, so rewriting
841
+ * the transcript underneath a mid-flow agent is a judgement the user should
842
+ * make, at a moment of their choosing, rather than something that happens to
843
+ * them mid-journey.
844
+ *
845
+ * @beta
846
+ */
847
+ export declare interface ChatContextConfig {
848
+ /**
849
+ * Whether the context gate is active at all.
850
+ * - `true` or `undefined` (default) — warn, then block, as the window fills
851
+ * - `false` — no warning and no block; the context indicator still shows
852
+ *
853
+ * An escape hatch for a host that would rather take the provider error than
854
+ * ever have its composer disabled.
855
+ */
856
+ enabled?: boolean;
857
+ /**
858
+ * Tokens kept free for a single turn to complete in. The assistant blocks new
859
+ * sends once the conversation would leave less than this much headroom.
860
+ *
861
+ * Deliberately a token count and not a percentage of the window: the reserve
862
+ * needs to cover the worst-case growth of one agentic turn, which does not
863
+ * scale with the model's context size. A percentage gives a 1M-token model a
864
+ * reserve far larger than any turn needs while potentially starving a
865
+ * 200k-token one. Default: `25000`.
866
+ */
867
+ reserveTokens?: number;
868
+ /**
869
+ * How many reserves ahead of the block the warning appears, as a multiple of
870
+ * {@link ChatContextConfig.reserveTokens}. At the default `2` the warning
871
+ * arrives one full reserve before the block, leaving room to compact at a
872
+ * convenient moment rather than at the wall. Default: `2`.
873
+ */
874
+ warnMultiplier?: number;
875
+ /**
876
+ * Context window to assume when the active provider reports none.
877
+ *
878
+ * Without a limit there is no percentage and so no gate, which is the right
879
+ * default — guessing a window and blocking against the guess would be worse
880
+ * than not blocking. Set this when you know the window but your provider
881
+ * does not report it.
882
+ */
883
+ fallbackContextLimit?: number;
884
+ /**
885
+ * Smallest reclaim, in tokens, for compaction to be worth running and to be
886
+ * offered as the way past a block.
887
+ *
888
+ * A compaction spends a summarizer call on most of the conversation, so a
889
+ * plan that frees less than this costs more than it buys — and, when the
890
+ * composer is blocked, would be an escape that does not actually clear the
891
+ * block. Default: `5000`.
892
+ */
893
+ minReclaimTokens?: number;
894
+ }
895
+
826
896
  /**
827
897
  * Cost tab and build-history settings.
828
898
  *
@@ -2065,6 +2135,54 @@ export declare type CondenseTrigger = {
2065
2135
  kind: 'phaseEnd';
2066
2136
  };
2067
2137
 
2138
+ /**
2139
+ * Thrown when the provider rejects a request because the prompt exceeds the
2140
+ * model's context window.
2141
+ *
2142
+ * Terminal for the attempt in the same sense as its siblings — retrying the same
2143
+ * request cannot clear it — but unlike them it is terminal only until the user
2144
+ * acts. Compaction genuinely resolves it, which is why the copy says so.
2145
+ *
2146
+ * @beta
2147
+ */
2148
+ export declare class ContextOverflowError extends Error {
2149
+ /** Vendor label of the transport that refused (e.g. `'Anthropic'`). */
2150
+ readonly vendorLabel: string;
2151
+ /** Prompt size the provider reported, when its message stated one. */
2152
+ readonly promptTokens?: number;
2153
+ /** Window size the provider reported, when its message stated one. */
2154
+ readonly limitTokens?: number;
2155
+ /** The provider's human-readable message, verbatim, for the log. Never shown to the user. */
2156
+ readonly detail?: string;
2157
+ constructor(
2158
+ /** Vendor label of the transport that refused (e.g. `'Anthropic'`). */
2159
+ vendorLabel: string,
2160
+ /** Prompt size the provider reported, when its message stated one. */
2161
+ promptTokens?: number,
2162
+ /** Window size the provider reported, when its message stated one. */
2163
+ limitTokens?: number,
2164
+ /** The provider's human-readable message, verbatim, for the log. Never shown to the user. */
2165
+ detail?: string);
2166
+ }
2167
+
2168
+ /**
2169
+ * Classify a rejection as a context overflow, returning the reported figures
2170
+ * when the provider stated them, or `undefined` when it is something else.
2171
+ *
2172
+ * Vendor-agnostic, unlike `providerRefusalOf`: the failure is a property of
2173
+ * the request rather than of a vendor's account model, both observed shapes are
2174
+ * a `400` with a phrase, and — critically — a false positive here cannot break a
2175
+ * retry ladder, so the caution that scopes the spend classifier to Anthropic does
2176
+ * not apply.
2177
+ *
2178
+ * @beta
2179
+ */
2180
+ export declare function contextOverflowOf(_vendor: AIProviderType | undefined, status: number | undefined, payload: unknown): {
2181
+ promptTokens?: number;
2182
+ limitTokens?: number;
2183
+ message?: string;
2184
+ } | undefined;
2185
+
2068
2186
  /**
2069
2187
  * Optional capability implemented by transports that can report the estimated
2070
2188
  * USD cost they have incurred over their lifetime.
@@ -2160,6 +2278,18 @@ export declare interface CriteriaInterpretContext {
2160
2278
  */
2161
2279
  export declare const DEFAULT_BUDGET_EXHAUSTED_MESSAGE = "You've reached your AI usage limit. Contact your administrator to raise it.";
2162
2280
 
2281
+ /**
2282
+ * The single sentence shown to a user whose request overran the context window.
2283
+ *
2284
+ * Unlike {@link DEFAULT_PROVIDER_REFUSED_MESSAGE} this one deliberately DOES name
2285
+ * a cause and an action, because both are true and both are the user's: they can
2286
+ * see the conversation that caused it, and compacting fixes it. Telling them it
2287
+ * "needs no action from you" would be the wrong advice as well as the wrong fact.
2288
+ *
2289
+ * @beta
2290
+ */
2291
+ export declare const DEFAULT_CONTEXT_OVERFLOW_MESSAGE: string;
2292
+
2163
2293
  /**
2164
2294
  * The single sentence shown to a user whose provider has refused — the transcript bubble the chat
2165
2295
  * driver appends, and the string ai-service reuses for its own generation surfaces.
@@ -3072,7 +3202,15 @@ export declare type SubAgentFailureReason = 'max_iterations' | 'malformed_tool_c
3072
3202
  * because the very next call hits the same wall. N batched sub-agent calls would otherwise cost N
3073
3203
  * doomed children AND a doomed parent call.
3074
3204
  */
3075
- | 'provider_refused';
3205
+ | 'provider_refused'
3206
+ /**
3207
+ * The sub-agent was stopped because continuing would have overrun the context
3208
+ * window (GENC-1567). **Terminal for the PARENT turn too**, on the same
3209
+ * reasoning as `budget_exhausted` and `provider_refused`: the parent shares the
3210
+ * conversation whose size caused it, so calling the model again hits the same
3211
+ * wall.
3212
+ */
3213
+ | 'context_exhausted';
3076
3214
 
3077
3215
  /**
3078
3216
  * Options passed to `requestSubAgent` at call time.
@@ -3204,7 +3342,14 @@ export declare interface TokenRates {
3204
3342
  *
3205
3343
  * @beta
3206
3344
  */
3207
- export declare type TurnFailureReason = 'exception' | 'malformed-function-call' | 'empty-response' | 'unknown-tool-limit' | 'max-iterations' | 'response-truncated' | 'refusal' | 'budget-exhausted' | 'provider-refused';
3345
+ export declare type TurnFailureReason = 'exception' | 'malformed-function-call' | 'empty-response' | 'unknown-tool-limit' | 'max-iterations' | 'response-truncated' | 'refusal' | 'budget-exhausted' | 'provider-refused'
3346
+ /**
3347
+ * The turn was stopped because continuing it would have overrun the model's
3348
+ * context window (GENC-1567). Pre-emptive: the driver ends the tool loop at
3349
+ * the edge rather than issuing a request the provider would reject, so the
3350
+ * transcript stays intact and compaction is still possible.
3351
+ */
3352
+ | 'context-exhausted';
3208
3353
 
3209
3354
  /**
3210
3355
  * Display name for each concrete AI vendor: the **single source** every
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/foundation-ai",
3
3
  "description": "Genesis Foundation AI - Provider-agnostic AI configuration and shared utilities",
4
- "version": "15.19.6",
4
+ "version": "15.20.0",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -52,17 +52,17 @@
52
52
  }
53
53
  },
54
54
  "devDependencies": {
55
- "@genesislcap/foundation-testing": "15.19.6",
56
- "@genesislcap/genx": "15.19.6",
57
- "@genesislcap/rollup-builder": "15.19.6",
58
- "@genesislcap/ts-builder": "15.19.6",
59
- "@genesislcap/uvu-playwright-builder": "15.19.6",
60
- "@genesislcap/vite-builder": "15.19.6",
61
- "@genesislcap/webpack-builder": "15.19.6"
55
+ "@genesislcap/foundation-testing": "15.20.0",
56
+ "@genesislcap/genx": "15.20.0",
57
+ "@genesislcap/rollup-builder": "15.20.0",
58
+ "@genesislcap/ts-builder": "15.20.0",
59
+ "@genesislcap/uvu-playwright-builder": "15.20.0",
60
+ "@genesislcap/vite-builder": "15.20.0",
61
+ "@genesislcap/webpack-builder": "15.20.0"
62
62
  },
63
63
  "dependencies": {
64
- "@genesislcap/foundation-logger": "15.19.6",
65
- "@genesislcap/foundation-utils": "15.19.6",
64
+ "@genesislcap/foundation-logger": "15.20.0",
65
+ "@genesislcap/foundation-utils": "15.20.0",
66
66
  "@microsoft/fast-foundation": "2.50.0"
67
67
  },
68
68
  "repository": {