@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.
- package/dist/ai-assistant.api.json +269 -5
- package/dist/ai-assistant.d.ts +256 -31
- package/dist/chat-driver.cjs +197 -84
- package/dist/chat-driver.cjs.map +2 -2
- package/dist/chat-driver.mjs +197 -84
- package/dist/chat-driver.mjs.map +2 -2
- package/dist/custom-elements.json +5483 -5364
- package/dist/dts/components/ai-driver/ai-driver.d.ts +63 -0
- package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +7 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +9 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/config/config.d.ts +53 -17
- package/dist/dts/config/config.d.ts.map +1 -1
- package/dist/dts/config/define-stateful-agent.d.ts +16 -1
- package/dist/dts/config/define-stateful-agent.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +102 -14
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/react.d.ts +12 -12
- package/dist/dts/utils/context-tokens.d.ts +100 -43
- package/dist/dts/utils/context-tokens.d.ts.map +1 -1
- package/dist/dts/utils/history-transform.d.ts +7 -1
- package/dist/dts/utils/history-transform.d.ts.map +1 -1
- package/dist/dts/utils/resolve-context-budget.d.ts +8 -5
- package/dist/dts/utils/resolve-context-budget.d.ts.map +1 -1
- package/dist/esm/components/chat-driver/chat-driver.js +22 -2
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +92 -0
- package/dist/esm/config/define-stateful-agent.js +1 -0
- package/dist/esm/main/main.js +170 -27
- package/dist/esm/utils/context-tokens.js +331 -106
- package/dist/esm/utils/history-transform.js +5 -5
- package/dist/esm/utils/resolve-context-budget.js +37 -7
- package/dist/react.cjs +21 -21
- package/dist/react.mjs +20 -20
- package/package.json +16 -16
package/dist/dts/react.d.ts
CHANGED
|
@@ -47,18 +47,6 @@ interface HTMLWCProps extends React.AriaAttributes {
|
|
|
47
47
|
onInput?(e: Event): void;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export declare const FoundationAiAssistant: React.ForwardRefExoticComponent<
|
|
51
|
-
React.PropsWithChildren<
|
|
52
|
-
Omit<PublicOf<FoundationAiAssistantWC>, 'children' | 'style'> &
|
|
53
|
-
HTMLWCProps & {
|
|
54
|
-
onBudgetRefreshRequested?: (event: CustomEvent<unknown>) => void;
|
|
55
|
-
onChatHeaderMousedown?: (event: CustomEvent<ChatHeaderMouseDownDetail>) => void;
|
|
56
|
-
onSessionCleared?: (event: CustomEvent<SessionClearedDetail>) => void;
|
|
57
|
-
}
|
|
58
|
-
> & React.RefAttributes<FoundationAiAssistantWC>
|
|
59
|
-
>;
|
|
60
|
-
export type FoundationAiAssistantRef = FoundationAiAssistantWC;
|
|
61
|
-
|
|
62
50
|
export declare const AiFlowingWavesIndicator: React.ForwardRefExoticComponent<
|
|
63
51
|
React.PropsWithChildren<
|
|
64
52
|
Omit<PublicOf<AiFlowingWavesIndicatorWC>, 'children' | 'style'> &
|
|
@@ -95,6 +83,18 @@ export declare const AiWavesIndicator: React.ForwardRefExoticComponent<
|
|
|
95
83
|
>;
|
|
96
84
|
export type AiWavesIndicatorRef = AiWavesIndicatorWC;
|
|
97
85
|
|
|
86
|
+
export declare const FoundationAiAssistant: React.ForwardRefExoticComponent<
|
|
87
|
+
React.PropsWithChildren<
|
|
88
|
+
Omit<PublicOf<FoundationAiAssistantWC>, 'children' | 'style'> &
|
|
89
|
+
HTMLWCProps & {
|
|
90
|
+
onBudgetRefreshRequested?: (event: CustomEvent<unknown>) => void;
|
|
91
|
+
onChatHeaderMousedown?: (event: CustomEvent<ChatHeaderMouseDownDetail>) => void;
|
|
92
|
+
onSessionCleared?: (event: CustomEvent<SessionClearedDetail>) => void;
|
|
93
|
+
}
|
|
94
|
+
> & React.RefAttributes<FoundationAiAssistantWC>
|
|
95
|
+
>;
|
|
96
|
+
export type FoundationAiAssistantRef = FoundationAiAssistantWC;
|
|
97
|
+
|
|
98
98
|
export declare const ChatSuggestions: React.ForwardRefExoticComponent<
|
|
99
99
|
React.PropsWithChildren<
|
|
100
100
|
Omit<PublicOf<ChatSuggestionsWC>, 'children' | 'style'> &
|
|
@@ -16,6 +16,52 @@ import type { ChatMessage } from '@genesislcap/foundation-ai';
|
|
|
16
16
|
* shed as tokens) — one definition so the two never drift.
|
|
17
17
|
*/
|
|
18
18
|
export declare const APPROX_CHARS_PER_TOKEN = 4;
|
|
19
|
+
/**
|
|
20
|
+
* Token cost of everything a request carries that is NOT a message — the system
|
|
21
|
+
* prompt, the tool definitions, and the framed tail (GENC-1582).
|
|
22
|
+
*
|
|
23
|
+
* **Why this is measured per call rather than derived.** The preamble used to be
|
|
24
|
+
* inferred as a residual: the first measured prompt minus the char estimate of the
|
|
25
|
+
* messages it covered. That derivation is accurate at the time, but it is taken
|
|
26
|
+
* ONCE and never updated, while the preamble changes every time the agent changes
|
|
27
|
+
* — each agent brings its own system prompt and its own tool surface. Every
|
|
28
|
+
* attempt to patch the staleness downstream made it worse; the cap that used to
|
|
29
|
+
* sit in `calibrate` compared stored CHARACTERS against a wire-measured TOKEN
|
|
30
|
+
* total and collapsed the preamble to zero on any condensed session, which is the
|
|
31
|
+
* same category error this module exists to fix. Measuring what is actually being
|
|
32
|
+
* sent removes the whole class of problem.
|
|
33
|
+
*
|
|
34
|
+
* **What is measured and what is not.** From the isa-platform session of
|
|
35
|
+
* 2026-09-03 (project 067f0fca, `claude-sonnet-5`), the first anchor measured
|
|
36
|
+
* 5,249 tokens against a single 290-character user message, so:
|
|
37
|
+
*
|
|
38
|
+
* | quantity | value | how |
|
|
39
|
+
* |---|---|---|
|
|
40
|
+
* | whole preamble, Bootstrap's first call | **5,173 tok** | MEASURED (5,249 − ~76 for the message) |
|
|
41
|
+
* | system prompt | **8,387 chars** | MEASURED (diag `turn` records it verbatim) |
|
|
42
|
+
* | system prompt in tokens | 2,097 | prose ratio applied to the measured chars |
|
|
43
|
+
* | 3 tool definitions | 3,076 tok (~1,025/tool) | **INFERRED as the residual** |
|
|
44
|
+
*
|
|
45
|
+
* The tools figure is a residual in TOKENS, not a ratio: diagnostics record
|
|
46
|
+
* `toolNames` only, never the schemas, so their character count is unavailable and
|
|
47
|
+
* `PREAMBLE_JSON_CHARS_PER_TOKEN` could not be calibrated from it. It is consistent
|
|
48
|
+
* with 3 — ~1,025 tokens per tool implies ~3,075 characters of schema per tool,
|
|
49
|
+
* which is the right order for a Create tool carrying descriptions and enum
|
|
50
|
+
* options — but consistent is not measured, and the next person should not read it
|
|
51
|
+
* as such. To calibrate it properly, log serialized schema characters alongside the
|
|
52
|
+
* first anchor for one real session and solve for the ratio.
|
|
53
|
+
*
|
|
54
|
+
* `tailContext` counts. It is regenerated per turn rather than accumulated, so
|
|
55
|
+
* compaction cannot remove it any more than it can remove the system prompt, which
|
|
56
|
+
* is the property that matters to every consumer of this figure.
|
|
57
|
+
*/
|
|
58
|
+
export declare function estimatePreambleTokens(input: {
|
|
59
|
+
systemPrompt?: string;
|
|
60
|
+
toolDefinitions?: readonly {
|
|
61
|
+
name: string;
|
|
62
|
+
}[];
|
|
63
|
+
tailContext?: string;
|
|
64
|
+
}): number;
|
|
19
65
|
/**
|
|
20
66
|
* Allowance for the summary a compaction will produce, used when projecting the
|
|
21
67
|
* post-compaction size before the summarizer has run. The system prompt asks for
|
|
@@ -83,48 +129,48 @@ export declare function reachesProvider(m: ChatMessage): boolean;
|
|
|
83
129
|
*/
|
|
84
130
|
export declare function isContextMeasurable(history: readonly ChatMessage[]): boolean;
|
|
85
131
|
/**
|
|
86
|
-
* Per-message token estimates
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* needed, since compaction only ever cuts on boundaries.
|
|
97
|
-
*
|
|
98
|
-
* A non-positive delta means history SHRANK between the two calls (a compaction
|
|
99
|
-
* or a `condenseWhen` collapse landed in between); there is nothing to
|
|
100
|
-
* distribute, so those messages keep their baseline.
|
|
101
|
-
*
|
|
102
|
-
* The region before the first anchor keeps its baseline too: that anchor's
|
|
103
|
-
* measurement also contains the system prompt and tool definitions, which are
|
|
104
|
-
* not attributable to any message — see {@link estimateSystemOverhead}.
|
|
132
|
+
* Per-message token estimates — each message's share of {@link calibrate}'s
|
|
133
|
+
* total, in proportion to how big that message is NOW.
|
|
134
|
+
*
|
|
135
|
+
* Used to choose and evaluate a compaction cut, so what matters is that the
|
|
136
|
+
* shares are in the same unit as the total and that they track the current size
|
|
137
|
+
* of each message. A payload `condenseWhen` has stubbed costs what the stub
|
|
138
|
+
* costs; under the segment-chained scheme this replaced, it kept the cost it had
|
|
139
|
+
* when it was full and the difference was merely redistributed to its neighbours.
|
|
140
|
+
*
|
|
141
|
+
* Sums to exactly `estimateContextTokens(history) - estimateSystemOverhead(history)`.
|
|
105
142
|
*/
|
|
106
|
-
export declare function estimateMessageTokens(history: readonly ChatMessage[]): number[];
|
|
143
|
+
export declare function estimateMessageTokens(history: readonly ChatMessage[], preambleTokens?: number): number[];
|
|
107
144
|
/**
|
|
108
145
|
* Context consumed by everything that is NOT a message — the system prompt and
|
|
109
146
|
* the tool definitions.
|
|
110
147
|
*
|
|
111
148
|
* Derived rather than guessed: the first measured prompt covers the messages
|
|
112
|
-
* before it plus
|
|
113
|
-
*
|
|
114
|
-
*
|
|
149
|
+
* before it plus the preamble, so subtracting our estimate of those messages
|
|
150
|
+
* leaves the preamble. Floored at zero, since an over-estimate of the early
|
|
151
|
+
* messages would otherwise produce a negative overhead, and capped at what the
|
|
152
|
+
* current total has left after paying for each message's characters — see
|
|
153
|
+
* {@link calibrate} for why that cap is load-bearing.
|
|
154
|
+
*
|
|
155
|
+
* It is NOT a constant, which the first cut of this assumed. Each agent brings its
|
|
156
|
+
* own system prompt and its own tool surface, so it steps up and down as agents
|
|
157
|
+
* hand over; what is true is that it does not shrink when the MESSAGES do.
|
|
115
158
|
*
|
|
116
159
|
* It matters for the gate because it is real context the user never sees in the
|
|
117
|
-
* transcript,
|
|
118
|
-
* tool surface it is far from negligible.
|
|
160
|
+
* transcript, and on an agent with a large tool surface it is far from negligible.
|
|
119
161
|
*/
|
|
120
|
-
export declare function estimateSystemOverhead(history: readonly ChatMessage[]): number;
|
|
162
|
+
export declare function estimateSystemOverhead(history: readonly ChatMessage[], preambleTokens?: number): number;
|
|
121
163
|
/**
|
|
122
|
-
* Total estimated context for a transcript — the
|
|
164
|
+
* Total estimated context for a transcript — the figure the gauge shows and the
|
|
165
|
+
* composer gate is judged against.
|
|
166
|
+
*
|
|
167
|
+
* The most recent usable measurement plus the char estimate of everything
|
|
168
|
+
* appended since it, which is as close as anything gets to "how big is the prompt
|
|
169
|
+
* we are about to send". See {@link calibrate} for why it is anchored on that one
|
|
170
|
+
* measurement rather than accumulated across all of them.
|
|
123
171
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* that figure is that it stays defined for a hypothetical history (the tail a
|
|
127
|
-
* compaction would leave behind), which is what {@link projectCompaction} needs.
|
|
172
|
+
* Stays defined for a hypothetical history (the tail a compaction would leave
|
|
173
|
+
* behind), which is what {@link projectCompaction} needs.
|
|
128
174
|
*/
|
|
129
175
|
export declare function estimateContextTokens(history: readonly ChatMessage[]): number;
|
|
130
176
|
/**
|
|
@@ -137,18 +183,29 @@ export declare function estimateContextTokens(history: readonly ChatMessage[]):
|
|
|
137
183
|
* log. A guard that measured stored history would therefore refuse turns whose
|
|
138
184
|
* real request is a fraction of the size.
|
|
139
185
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
186
|
+
* So the request is estimated from its OWN characters, plus the preamble. Nothing
|
|
187
|
+
* is scaled: `requestHistory` IS the payload, so `baselineTokens` is already
|
|
188
|
+
* measuring the right bytes, and there is no transform left to correct for.
|
|
189
|
+
*
|
|
190
|
+
* **This previously applied a ratio, and a docblock here previously claimed it did
|
|
191
|
+
* not.** Both were wrong, and the second is the part worth recording. The original
|
|
192
|
+
* expression scaled the stored-history total by `requestChars / storedChars`,
|
|
193
|
+
* reasoning that `inputTokens` "measured the untransformed prompt". That premise is
|
|
194
|
+
* false — `inputTokens` measures the prompt that was SENT, transform already
|
|
195
|
+
* applied — and it is what made every figure in this module too large. The GENC-1582
|
|
196
|
+
* rewrite claimed to drop the ratio but only re-factored it: `scale` is
|
|
197
|
+
* `(total - overhead) / storedBaseline`, so `overhead + requestBaseline * scale` is
|
|
198
|
+
* the identical expression rearranged. With the total now anchored on a measurement
|
|
199
|
+
* of an already-condensed prompt, that discounted the same shrinkage twice —
|
|
200
|
+
* measured at **5,034 against a last measured prompt of 120,000**, i.e. a guard that
|
|
201
|
+
* believed it had 995k spare. Under-estimating is the one direction this figure must
|
|
202
|
+
* not err in, since its only job is refusing an overflow.
|
|
203
|
+
*
|
|
204
|
+
* The preamble must be passed in for the same reason: `requestBaseline` counts
|
|
205
|
+
* message characters, and the system prompt and tool definitions are not messages,
|
|
206
|
+
* so without it the guard silently ignores the entire tool surface.
|
|
150
207
|
*/
|
|
151
|
-
export declare function estimateRequestTokens(storedHistory: readonly ChatMessage[], requestHistory: readonly ChatMessage[]): number;
|
|
208
|
+
export declare function estimateRequestTokens(storedHistory: readonly ChatMessage[], requestHistory: readonly ChatMessage[], preambleTokens?: number): number;
|
|
152
209
|
/** What a compaction at a given cut would cost and reclaim. */
|
|
153
210
|
export interface CompactionProjection {
|
|
154
211
|
/** Index the verbatim tail starts at — everything before it is summarized. */
|
|
@@ -174,5 +231,5 @@ export interface CompactionProjection {
|
|
|
174
231
|
* The system preamble is carried across unchanged: compaction removes messages,
|
|
175
232
|
* never the system prompt or the tool definitions.
|
|
176
233
|
*/
|
|
177
|
-
export declare function projectCompaction(history: readonly ChatMessage[], cut: number, summaryTokens?: number): CompactionProjection;
|
|
234
|
+
export declare function projectCompaction(history: readonly ChatMessage[], cut: number, summaryTokens?: number, preambleTokens?: number): CompactionProjection;
|
|
178
235
|
//# sourceMappingURL=context-tokens.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-tokens.d.ts","sourceRoot":"","sources":["../../../src/utils/context-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"context-tokens.d.ts","sourceRoot":"","sources":["../../../src/utils/context-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AA6CxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAiBT;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,OAAQ,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAKvD;AA6SD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAE5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,EAAE,CAEV;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAER;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAE7E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,SAAS,WAAW,EAAE,EACrC,cAAc,EAAE,SAAS,WAAW,EAAE,EACtC,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAKR;AAED,+DAA+D;AAC/D,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,6BAA6B;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,GAAG,EAAE,MAAM,EACX,aAAa,GAAE,MAA0C,EACzD,cAAc,CAAC,EAAE,MAAM,GACtB,oBAAoB,CAkBtB"}
|
|
@@ -81,13 +81,19 @@ export declare function isSafeCompactionCut(history: readonly ChatMessage[], i:
|
|
|
81
81
|
* enormous, indivisible tool group, which no cut can shrink because a cut cannot
|
|
82
82
|
* split a message.
|
|
83
83
|
*/
|
|
84
|
-
export declare function findCompactionCut(history: readonly ChatMessage[], tailTokenBudget?: number): number | null;
|
|
84
|
+
export declare function findCompactionCut(history: readonly ChatMessage[], tailTokenBudget?: number, preambleTokens?: number): number | null;
|
|
85
85
|
/** Options for {@link planCompaction}. */
|
|
86
86
|
export interface CompactionPlanOptions {
|
|
87
87
|
/** Token budget for the verbatim tail. */
|
|
88
88
|
tailTokenBudget?: number;
|
|
89
89
|
/** Minimum reclaim for the plan to be considered worth running. */
|
|
90
90
|
minReclaimTokens?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Measured preamble cost, from `AiDriver.getPreambleTokens()` (GENC-1582).
|
|
93
|
+
* Without it the projection sizes the post-compaction context as if the system
|
|
94
|
+
* prompt and tool definitions were free, so it lands above the line it aimed at.
|
|
95
|
+
*/
|
|
96
|
+
preambleTokens?: number;
|
|
91
97
|
}
|
|
92
98
|
/**
|
|
93
99
|
* The single answer to "what would compacting do right now, and is it worth it?"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history-transform.d.ts","sourceRoot":"","sources":["../../../src/utils/history-transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,
|
|
1
|
+
{"version":3,"file":"history-transform.d.ts","sourceRoot":"","sources":["../../../src/utils/history-transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,kBAAkB,CAAC;AAoC1B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAsBjG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CAG3F;AAID;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CASvF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,eAAe,GAAE,MAAkC,EACnD,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,GAAG,IAAI,CAkBf;AAED,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,GAAG,IAAI,CAc7B;AAkCD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,SAAS,WAAW,EAAE,GAAG;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB,CAYA;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,EAAE,CAanF;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAG7D"}
|
|
@@ -77,15 +77,18 @@ export interface ContextBudgetInput {
|
|
|
77
77
|
/** Context window reported by the active provider, if any. */
|
|
78
78
|
contextLimit?: number;
|
|
79
79
|
/**
|
|
80
|
-
* Reserve declared by the
|
|
81
|
-
* global reserve for
|
|
80
|
+
* Reserve declared by the agent the next turn could land on, if any — overrides
|
|
81
|
+
* the configured global reserve for that agent only.
|
|
82
82
|
*/
|
|
83
83
|
agentReserveTokens?: number;
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
85
|
+
* Extra tokens above {@link ContextBudgetInput.agentReserveTokens} at which the
|
|
86
|
+
* warning appears, when that agent declared one (GENC-1582). Takes precedence
|
|
87
|
+
* over `warnMultiplier`, which cannot express it: the multiplier scales the whole
|
|
88
|
+
* reserve, so an agent that peaks large and keeps little gets a warning line
|
|
89
|
+
* every one of its turns crosses transiently and then clears.
|
|
87
90
|
*/
|
|
88
|
-
|
|
91
|
+
agentWarnAheadTokens?: number;
|
|
89
92
|
}
|
|
90
93
|
/**
|
|
91
94
|
* Resolve the headroom thresholds for the active model and agent.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-context-budget.d.ts","sourceRoot":"","sources":["../../../src/utils/resolve-context-budget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"resolve-context-budget.d.ts","sourceRoot":"","sources":["../../../src/utils/resolve-context-budget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAM7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAEtC;;;;GAIG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,OAAQ,CAAC;AAEjD,iEAAiE;AACjE,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,8CAA8C;AAC9C,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,UAAU,EAAE,UAAU,CAAC;IACvB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,qBAAqB,CAgFrF"}
|
|
@@ -5,7 +5,7 @@ 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, condenseForPersistence, } from '../../utils/condense-history';
|
|
8
|
-
import { estimateRequestTokens } from '../../utils/context-tokens';
|
|
8
|
+
import { estimatePreambleTokens, estimateRequestTokens, } from '../../utils/context-tokens';
|
|
9
9
|
import { applyHistoryCap, buildCompactionSummaryPrompt, planCompaction, normalizeForProvider, } from '../../utils/history-transform';
|
|
10
10
|
import { logger } from '../../utils/logger';
|
|
11
11
|
import { messageUsage, sumUsage } from '../../utils/sum-usage';
|
|
@@ -1037,7 +1037,14 @@ export class ChatDriver extends EventTarget {
|
|
|
1037
1037
|
},
|
|
1038
1038
|
]
|
|
1039
1039
|
: requestHistory;
|
|
1040
|
-
|
|
1040
|
+
// The preamble is passed EXPLICITLY, and this guard is the reason the parameter
|
|
1041
|
+
// exists (GENC-1582). `requestBaseline` counts message characters, and the
|
|
1042
|
+
// system prompt and tool definitions are not messages — so without it the
|
|
1043
|
+
// guard silently ignores the entire tool surface and, on a history with no
|
|
1044
|
+
// usable anchor, the whole preamble. `lastPreambleTokens` is assigned for THIS
|
|
1045
|
+
// request earlier in the same loop iteration, so it is fresh rather than a
|
|
1046
|
+
// previous turn's.
|
|
1047
|
+
const estimated = estimateRequestTokens(this.history, request, this.lastPreambleTokens);
|
|
1041
1048
|
if (estimated < threshold)
|
|
1042
1049
|
return undefined;
|
|
1043
1050
|
logger.error(`ChatDriver: ending the turn — the request would fill the context window (~${estimated} of ${limit})`);
|
|
@@ -1280,6 +1287,10 @@ export class ChatDriver extends EventTarget {
|
|
|
1280
1287
|
return summaryMessage;
|
|
1281
1288
|
});
|
|
1282
1289
|
}
|
|
1290
|
+
/** {@inheritDoc AiDriver.getPreambleTokens} */
|
|
1291
|
+
getPreambleTokens() {
|
|
1292
|
+
return this.lastPreambleTokens;
|
|
1293
|
+
}
|
|
1283
1294
|
isBusy() {
|
|
1284
1295
|
return this.busy;
|
|
1285
1296
|
}
|
|
@@ -2458,6 +2469,15 @@ export class ChatDriver extends EventTarget {
|
|
|
2458
2469
|
const attachmentsForCall = currentAttachments;
|
|
2459
2470
|
currentInput = '';
|
|
2460
2471
|
currentAttachments = undefined;
|
|
2472
|
+
// Record what this request's preamble costs, at the one point everything it
|
|
2473
|
+
// is made of has been resolved (GENC-1582). Read back by the host through
|
|
2474
|
+
// `getPreambleTokens()` to size the context gate and the compaction target
|
|
2475
|
+
// against the ACTIVE agent's surface rather than a stale first-call residual.
|
|
2476
|
+
this.lastPreambleTokens = estimatePreambleTokens({
|
|
2477
|
+
systemPrompt,
|
|
2478
|
+
toolDefinitions: this.toolDefinitions,
|
|
2479
|
+
tailContext,
|
|
2480
|
+
});
|
|
2461
2481
|
const options = {
|
|
2462
2482
|
systemPrompt,
|
|
2463
2483
|
// Strip fold-only properties (foldEvent, foldPath) before sending to provider
|
|
@@ -46,6 +46,40 @@ function isSpecialist(agent) {
|
|
|
46
46
|
function isFallback(agent) {
|
|
47
47
|
return agent.fallback === true;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Flatten a `contextReserve` declaration to the two numbers the gate reads
|
|
51
|
+
* (GENC-1582), or `undefined` when the agent declared nothing.
|
|
52
|
+
*
|
|
53
|
+
* A bare number is the peak-only form and leaves the warning to the host's
|
|
54
|
+
* `warnMultiplier`. Non-positive `tokens` is treated as no declaration rather
|
|
55
|
+
* than as "needs no headroom": zero would resolve to a reserve of 0, which sets
|
|
56
|
+
* `blockAt` to the whole window and switches the gate off for every agent the
|
|
57
|
+
* turn could reach — the opposite of what declaring a reserve is for.
|
|
58
|
+
*
|
|
59
|
+
* **Validated at runtime, not just by the type.** `contextReserve` is host-supplied
|
|
60
|
+
* `@beta` config and can arrive from JS, from JSON, or from a dynamic source, so
|
|
61
|
+
* `tokens` may be absent or not a number whatever the signature says. That matters
|
|
62
|
+
* more than a violated return type: `undefined <= 0` is `false`, so a bare
|
|
63
|
+
* comparison passed a malformed reserve straight through as `{ tokens: undefined }`,
|
|
64
|
+
* and the caller's `reserve.tokens > winner.tokens` then compared against
|
|
65
|
+
* `undefined` — always false. One malformed entry on an earlier-listed agent
|
|
66
|
+
* therefore SUPPRESSED every valid reserve behind it, and the gate fell back to the
|
|
67
|
+
* ambient default while still naming the malformed agent. Silent, because
|
|
68
|
+
* `resolveContextBudget` coalesces an undefined reserve to that same default
|
|
69
|
+
* (reviewer catch on #2504).
|
|
70
|
+
*/
|
|
71
|
+
function normalizeContextReserve(input) {
|
|
72
|
+
if (input == null)
|
|
73
|
+
return undefined;
|
|
74
|
+
const positive = (v) => typeof v === 'number' && Number.isFinite(v) && v > 0;
|
|
75
|
+
if (typeof input === 'number')
|
|
76
|
+
return positive(input) ? { tokens: input } : undefined;
|
|
77
|
+
if (!positive(input.tokens))
|
|
78
|
+
return undefined;
|
|
79
|
+
return positive(input.warnAheadTokens)
|
|
80
|
+
? { tokens: input.tokens, warnAheadTokens: input.warnAheadTokens }
|
|
81
|
+
: { tokens: input.tokens };
|
|
82
|
+
}
|
|
49
83
|
/**
|
|
50
84
|
* Placeholder a consumer can put in its fallback prompt to position
|
|
51
85
|
* {@link UNROUTABLE_INSTRUCTION} itself, instead of having it appended.
|
|
@@ -293,6 +327,10 @@ export class OrchestratingDriver extends EventTarget {
|
|
|
293
327
|
canCompact(options) {
|
|
294
328
|
return this.chatDriver.canCompact(options);
|
|
295
329
|
}
|
|
330
|
+
/** {@inheritDoc AiDriver.getPreambleTokens} */
|
|
331
|
+
getPreambleTokens() {
|
|
332
|
+
return this.chatDriver.getPreambleTokens();
|
|
333
|
+
}
|
|
296
334
|
/** {@inheritDoc AiDriver.getCompactionPlan} */
|
|
297
335
|
getCompactionPlan(options) {
|
|
298
336
|
return this.chatDriver.getCompactionPlan(options);
|
|
@@ -499,6 +537,60 @@ export class OrchestratingDriver extends EventTarget {
|
|
|
499
537
|
primeRestoredAgentState(snapshots) {
|
|
500
538
|
this.restoredAgentStates = Object.assign({}, snapshots);
|
|
501
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* {@inheritDoc AiDriver.getContextReserve}
|
|
542
|
+
*
|
|
543
|
+
* Two cases, because routing has two modes and they need opposite answers.
|
|
544
|
+
*/
|
|
545
|
+
getContextReserve() {
|
|
546
|
+
// LOCKED — a user pin or a flow-owner lock. `handleTurn` routes straight to
|
|
547
|
+
// this agent and the handoff branch at the bottom of the routing loop refuses
|
|
548
|
+
// to route away while a lock is held, so no other agent's headroom can come
|
|
549
|
+
// into play until it clears. Its reserve is the whole requirement.
|
|
550
|
+
const locked = this.resolvePinnedAgent();
|
|
551
|
+
if (locked) {
|
|
552
|
+
const reserve = normalizeContextReserve(locked.contextReserve);
|
|
553
|
+
return reserve == null ? undefined : Object.assign(Object.assign({}, reserve), { agentName: locked.name, basis: 'locked' });
|
|
554
|
+
}
|
|
555
|
+
// AUTOMATIC — the classifier picks a specialist, may fall through to the
|
|
556
|
+
// fallback, and may hand off and re-classify more than once inside the one
|
|
557
|
+
// turn. Any of them could be the agent that runs, and the send is allowed or
|
|
558
|
+
// refused before we know which, so the requirement is the largest reserve
|
|
559
|
+
// among them: the turn has to fit whichever way it routes.
|
|
560
|
+
//
|
|
561
|
+
// The MAX, not the sum, even though a handoff chain really does add each leg's
|
|
562
|
+
// growth on top of the last. Summing every reachable agent would reserve room
|
|
563
|
+
// for a journey nobody is taking and strand most of the window on a two-line
|
|
564
|
+
// question; the mid-loop context guard is what bounds a chain that does run
|
|
565
|
+
// long. The max is the smallest reserve that makes the FIRST leg safe, which
|
|
566
|
+
// is the only leg this gate can speak for.
|
|
567
|
+
//
|
|
568
|
+
// `excludeFromClassifier` agents are absent by construction — they are
|
|
569
|
+
// reachable only by pin or by a restored flow-owner lock, and both of those
|
|
570
|
+
// are the locked arm above. The active agent is folded in as a floor: on the
|
|
571
|
+
// turn after a lock clears it is still the agent holding the context, and it
|
|
572
|
+
// need not be one the classifier could have picked.
|
|
573
|
+
const candidates = [...this.specialists];
|
|
574
|
+
if (this.fallback)
|
|
575
|
+
candidates.push(this.fallback);
|
|
576
|
+
if (this.activeAgent)
|
|
577
|
+
candidates.push(this.activeAgent);
|
|
578
|
+
// Compared on `tokens` — the figure that decides whether a turn may start.
|
|
579
|
+
// `warnAheadTokens` is not a candidate for the max in its own right; it rides
|
|
580
|
+
// along with whichever agent wins.
|
|
581
|
+
let winner;
|
|
582
|
+
let agentName = '';
|
|
583
|
+
for (const agent of candidates) {
|
|
584
|
+
const reserve = normalizeContextReserve(agent.contextReserve);
|
|
585
|
+
if (!reserve)
|
|
586
|
+
continue;
|
|
587
|
+
if (!winner || reserve.tokens > winner.tokens) {
|
|
588
|
+
winner = reserve;
|
|
589
|
+
agentName = agent.name;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return winner ? Object.assign(Object.assign({}, winner), { agentName, basis: 'reachable-max' }) : undefined;
|
|
593
|
+
}
|
|
502
594
|
/** {@inheritDoc AiDriver.getActiveAgent} */
|
|
503
595
|
getActiveAgent() {
|
|
504
596
|
return this.activeAgent;
|
|
@@ -264,6 +264,7 @@ export function defineStatefulAgent(opts) {
|
|
|
264
264
|
onUnresolvedTool: wrappedOnUnresolvedTool,
|
|
265
265
|
resumable: wrappedResumable,
|
|
266
266
|
notResumableMessage: opts.notResumableMessage,
|
|
267
|
+
contextReserve: opts.contextReserve,
|
|
267
268
|
onActivate: (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
268
269
|
state = yield opts.init(ctx);
|
|
269
270
|
// Sample once with the init state to fail loud on fold misuse at the
|