@genesislcap/ai-assistant 15.14.2 → 15.15.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.
- package/dist/ai-assistant.api.json +5 -5
- package/dist/chat-driver.cjs +285 -70
- package/dist/chat-driver.cjs.map +4 -4
- package/dist/chat-driver.mjs +285 -70
- package/dist/chat-driver.mjs.map +4 -4
- package/dist/custom-elements.json +32 -0
- package/dist/dts/chat-driver-node.d.ts +1 -1
- package/dist/dts/chat-driver-node.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/utils/condense-history.d.ts.map +1 -1
- package/dist/dts/utils/history-transform.d.ts.map +1 -1
- package/dist/esm/components/chat-driver/chat-driver.js +34 -7
- package/dist/esm/utils/condense-history.js +31 -7
- package/dist/esm/utils/condense-history.test.js +68 -1
- package/dist/esm/utils/history-transform.js +8 -1
- package/dist/esm/utils/history-transform.test.js +22 -1
- package/package.json +17 -17
- package/src/chat-driver-node.ts +7 -0
- package/src/components/chat-driver/chat-driver.ts +48 -3
- package/src/utils/condense-history.test.ts +84 -2
- package/src/utils/condense-history.ts +27 -4
- package/src/utils/history-transform.test.ts +31 -0
- package/src/utils/history-transform.ts +7 -1
package/dist/chat-driver.cjs
CHANGED
|
@@ -1221,6 +1221,14 @@ function vendorOfModel(modelId) {
|
|
|
1221
1221
|
return void 0;
|
|
1222
1222
|
}
|
|
1223
1223
|
|
|
1224
|
+
// ../../foundation-ai/dist/esm/utils/image-mime.js
|
|
1225
|
+
function normalizeImageMime(mimeType) {
|
|
1226
|
+
return mimeType === "image/jpg" ? "image/jpeg" : mimeType;
|
|
1227
|
+
}
|
|
1228
|
+
function stripImageDataUrl(data) {
|
|
1229
|
+
return data.replace(/^data:[^,]*;base64,/, "");
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1224
1232
|
// ../../foundation-ai/dist/esm/utils/temperature.js
|
|
1225
1233
|
var DEFAULT_ANCHOR = 0.5;
|
|
1226
1234
|
function scaleTemperature(normalized, { defaultTemp, maxTemp }) {
|
|
@@ -1829,8 +1837,28 @@ function anthropicThinking(model, policy) {
|
|
|
1829
1837
|
if (policy === "auto") {
|
|
1830
1838
|
return supportsAdaptiveThinking(model) ? adaptive : void 0;
|
|
1831
1839
|
}
|
|
1840
|
+
if (policy !== void 0) {
|
|
1841
|
+
return supportsAdaptiveThinking(model) ? adaptive : void 0;
|
|
1842
|
+
}
|
|
1832
1843
|
return defaultsToThinking ? adaptive : void 0;
|
|
1833
1844
|
}
|
|
1845
|
+
function supportsEffort(model) {
|
|
1846
|
+
return model === "claude-fable-5" || model === "claude-opus-4-8" || model === "claude-sonnet-5";
|
|
1847
|
+
}
|
|
1848
|
+
var ANTHROPIC_EFFORT = {
|
|
1849
|
+
minimal: "low",
|
|
1850
|
+
low: "low",
|
|
1851
|
+
medium: "medium",
|
|
1852
|
+
high: "high",
|
|
1853
|
+
max: "max"
|
|
1854
|
+
};
|
|
1855
|
+
function anthropicEffort(model, policy) {
|
|
1856
|
+
if (policy === void 0 || policy === "auto" || policy === "off")
|
|
1857
|
+
return void 0;
|
|
1858
|
+
if (!supportsEffort(model) || !supportsAdaptiveThinking(model))
|
|
1859
|
+
return void 0;
|
|
1860
|
+
return ANTHROPIC_EFFORT[policy];
|
|
1861
|
+
}
|
|
1834
1862
|
var ANTHROPIC_PROVIDER_KEY = "anthropic";
|
|
1835
1863
|
var ResponseTruncatedError = class extends Error {
|
|
1836
1864
|
constructor(model, maxTokens, outputTokens, toolNames) {
|
|
@@ -1855,11 +1883,42 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1855
1883
|
warnIfThinkingUnclampable(policy) {
|
|
1856
1884
|
if (this.warnedThinkingClamped)
|
|
1857
1885
|
return;
|
|
1858
|
-
const
|
|
1859
|
-
if (!
|
|
1886
|
+
const message = this.clampWarning(policy);
|
|
1887
|
+
if (!message)
|
|
1860
1888
|
return;
|
|
1861
1889
|
this.warnedThinkingClamped = true;
|
|
1862
|
-
logger.warn(
|
|
1890
|
+
logger.warn(message);
|
|
1891
|
+
}
|
|
1892
|
+
/**
|
|
1893
|
+
* The warning for a policy this model cannot honour, or `undefined` when it can.
|
|
1894
|
+
*
|
|
1895
|
+
* An exhaustive `switch` with a `never` check rather than a condition chain: a new
|
|
1896
|
+
* {@link ChatThinkingPolicy} member must be considered HERE or the build fails. The condition
|
|
1897
|
+
* chain this replaced would have let every graded level clamp in silence, which is the one
|
|
1898
|
+
* outcome the type's own docs promise callers never happens.
|
|
1899
|
+
*/
|
|
1900
|
+
clampWarning(policy) {
|
|
1901
|
+
switch (policy) {
|
|
1902
|
+
case void 0:
|
|
1903
|
+
return void 0;
|
|
1904
|
+
case "off":
|
|
1905
|
+
return thinkingIsMandatory(this.model) ? `AnthropicTransport: thinkingPolicy 'off' ignored \u2014 ${this.model} always thinks and rejects an explicit disable. Reasoning tokens are still billed as output; switch model if you need them gone.` : void 0;
|
|
1906
|
+
case "auto":
|
|
1907
|
+
return supportsAdaptiveThinking(this.model) ? void 0 : `AnthropicTransport: thinkingPolicy 'auto' ignored \u2014 ${this.model} does not support adaptive thinking, so this turn runs without reasoning. Use a Sonnet or Opus tier if the agent needs it.`;
|
|
1908
|
+
case "minimal":
|
|
1909
|
+
case "low":
|
|
1910
|
+
case "medium":
|
|
1911
|
+
case "high":
|
|
1912
|
+
case "max":
|
|
1913
|
+
if (!supportsAdaptiveThinking(this.model) || !supportsEffort(this.model)) {
|
|
1914
|
+
return `AnthropicTransport: thinkingPolicy '${policy}' ignored \u2014 ${this.model} predates output_config.effort, so this turn runs at its default posture. Use Opus 4.8, Sonnet 5, or Fable 5 to set reasoning depth.`;
|
|
1915
|
+
}
|
|
1916
|
+
return void 0;
|
|
1917
|
+
default: {
|
|
1918
|
+
const exhaustive = policy;
|
|
1919
|
+
return exhaustive;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1863
1922
|
}
|
|
1864
1923
|
constructor(config = {}) {
|
|
1865
1924
|
var _a, _b, _c, _d;
|
|
@@ -1921,9 +1980,7 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1921
1980
|
const useNative = responseSchema != null && supportsNativeStructuredOutput(this.model);
|
|
1922
1981
|
const useForcedTool = responseSchema != null && !useNative;
|
|
1923
1982
|
if (useNative) {
|
|
1924
|
-
body.output_config = {
|
|
1925
|
-
format: { type: "json_schema", schema: responseSchema }
|
|
1926
|
-
};
|
|
1983
|
+
body.output_config = Object.assign(Object.assign({}, body.output_config), { format: { type: "json_schema", schema: responseSchema } });
|
|
1927
1984
|
} else if (useForcedTool) {
|
|
1928
1985
|
body.tools = [
|
|
1929
1986
|
{
|
|
@@ -1947,7 +2004,7 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1947
2004
|
// ── ChatTransport (multi-turn chat) ────────────────────────────────────
|
|
1948
2005
|
sendChatMessage(history, userMessage, options) {
|
|
1949
2006
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1950
|
-
var _a, _b, _c, _d;
|
|
2007
|
+
var _a, _b, _c, _d, _e;
|
|
1951
2008
|
const reachableModels = /* @__PURE__ */ new Set([
|
|
1952
2009
|
this.model,
|
|
1953
2010
|
...((_a = options === null || options === void 0 ? void 0 : options.fallbacks) !== null && _a !== void 0 ? _a : []).map((f) => f.model)
|
|
@@ -1955,15 +2012,16 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1955
2012
|
const messages = this.toAnthropicMessages(history, userMessage, options === null || options === void 0 ? void 0 : options.attachments, reachableModels);
|
|
1956
2013
|
const body = {
|
|
1957
2014
|
model: this.model,
|
|
1958
|
-
|
|
2015
|
+
// Per-turn override wins over the instance default; see ChatRequestOptions.maxTokens.
|
|
2016
|
+
max_tokens: (_b = options === null || options === void 0 ? void 0 : options.maxTokens) !== null && _b !== void 0 ? _b : this.maxTokens,
|
|
1959
2017
|
messages
|
|
1960
2018
|
};
|
|
1961
2019
|
if (options === null || options === void 0 ? void 0 : options.systemPrompt)
|
|
1962
2020
|
body.system = options.systemPrompt;
|
|
1963
|
-
if ((
|
|
2021
|
+
if ((_c = options === null || options === void 0 ? void 0 : options.tools) === null || _c === void 0 ? void 0 : _c.length) {
|
|
1964
2022
|
body.tools = options.tools.map((t) => Object.assign({ name: t.name, description: t.description, input_schema: t.enforceSchema ? enforceAnthropicToolSchema(t.parameters, t.name) : t.parameters }, t.enforceSchema ? { strict: true } : {}));
|
|
1965
2023
|
}
|
|
1966
|
-
if ((
|
|
2024
|
+
if ((_d = body.tools) === null || _d === void 0 ? void 0 : _d.length) {
|
|
1967
2025
|
const toolChoice = toAnthropicToolChoice(options === null || options === void 0 ? void 0 : options.toolChoice);
|
|
1968
2026
|
if (toolChoice)
|
|
1969
2027
|
body.tool_choice = toolChoice;
|
|
@@ -1972,6 +2030,9 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1972
2030
|
const thinking = anthropicThinking(this.model, options === null || options === void 0 ? void 0 : options.thinkingPolicy);
|
|
1973
2031
|
if (thinking)
|
|
1974
2032
|
body.thinking = thinking;
|
|
2033
|
+
const effort = anthropicEffort(this.model, options === null || options === void 0 ? void 0 : options.thinkingPolicy);
|
|
2034
|
+
if (effort)
|
|
2035
|
+
body.output_config = Object.assign(Object.assign({}, body.output_config), { effort });
|
|
1975
2036
|
const thinkingEnabled = (thinking === null || thinking === void 0 ? void 0 : thinking.type) === "adaptive";
|
|
1976
2037
|
if ((options === null || options === void 0 ? void 0 : options.temperature) != null && !rejectsSamplingParams(this.model) && !thinkingEnabled) {
|
|
1977
2038
|
body.temperature = scaleTemperature(options.temperature, {
|
|
@@ -1980,11 +2041,9 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1980
2041
|
});
|
|
1981
2042
|
}
|
|
1982
2043
|
if ((options === null || options === void 0 ? void 0 : options.responseSchema) && supportsNativeStructuredOutput(this.model)) {
|
|
1983
|
-
body.output_config = {
|
|
1984
|
-
format: { type: "json_schema", schema: options.responseSchema }
|
|
1985
|
-
};
|
|
2044
|
+
body.output_config = Object.assign(Object.assign({}, body.output_config), { format: { type: "json_schema", schema: options.responseSchema } });
|
|
1986
2045
|
}
|
|
1987
|
-
if ((
|
|
2046
|
+
if ((_e = options === null || options === void 0 ? void 0 : options.fallbacks) === null || _e === void 0 ? void 0 : _e.length) {
|
|
1988
2047
|
body.fallbacks = options.fallbacks.map((f) => f.maxTokens != null ? { model: f.model, max_tokens: f.maxTokens } : { model: f.model });
|
|
1989
2048
|
}
|
|
1990
2049
|
if (options === null || options === void 0 ? void 0 : options.cachePolicy) {
|
|
@@ -1994,7 +2053,7 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1994
2053
|
this.appendTailContext(body, options.tailContext);
|
|
1995
2054
|
}
|
|
1996
2055
|
const response = yield this.post(body, options === null || options === void 0 ? void 0 : options.signal);
|
|
1997
|
-
return this.fromAnthropicResponse(response);
|
|
2056
|
+
return this.fromAnthropicResponse(response, body.max_tokens);
|
|
1998
2057
|
});
|
|
1999
2058
|
}
|
|
2000
2059
|
/**
|
|
@@ -2087,7 +2146,7 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
2087
2146
|
* the payload tidy.
|
|
2088
2147
|
*/
|
|
2089
2148
|
toAnthropicMessages(history, userMessage, attachments, reachableModels = /* @__PURE__ */ new Set([this.model])) {
|
|
2090
|
-
var _a, _b, _c;
|
|
2149
|
+
var _a, _b, _c, _d;
|
|
2091
2150
|
const messages = [];
|
|
2092
2151
|
const pushBlock = (role, block) => {
|
|
2093
2152
|
const last = messages[messages.length - 1];
|
|
@@ -2097,18 +2156,54 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
2097
2156
|
}
|
|
2098
2157
|
messages.push({ role, content: [block] });
|
|
2099
2158
|
};
|
|
2159
|
+
const splitAttachments = (atts) => {
|
|
2160
|
+
const images = [];
|
|
2161
|
+
const texts = [];
|
|
2162
|
+
for (const att of atts) {
|
|
2163
|
+
if (att.kind === "image") {
|
|
2164
|
+
images.push({
|
|
2165
|
+
type: "image",
|
|
2166
|
+
source: {
|
|
2167
|
+
type: "base64",
|
|
2168
|
+
media_type: normalizeImageMime(att.mimeType),
|
|
2169
|
+
data: stripImageDataUrl(att.data)
|
|
2170
|
+
}
|
|
2171
|
+
});
|
|
2172
|
+
} else {
|
|
2173
|
+
texts.push({ type: "text", text: `[File: ${att.name}]
|
|
2174
|
+
${att.content}` });
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
return { images, texts };
|
|
2178
|
+
};
|
|
2100
2179
|
for (const msg of history) {
|
|
2101
2180
|
if (msg.role === "system" || msg.role === "system-event" || msg.role === "synthetic-user" || msg.role === "compacted-summary" || msg.category === "reasoning" || msg.category === "narration" || msg.thinking)
|
|
2102
2181
|
continue;
|
|
2103
2182
|
if (msg.toolResult) {
|
|
2183
|
+
const resultImages = (_a = msg.toolResult.attachments) !== null && _a !== void 0 ? _a : [];
|
|
2104
2184
|
pushBlock("user", {
|
|
2105
2185
|
type: "tool_result",
|
|
2106
2186
|
tool_use_id: msg.toolResult.toolCallId,
|
|
2107
|
-
|
|
2187
|
+
// Bare string when there are no images — the historical shape, byte for byte.
|
|
2188
|
+
content: resultImages.length ? [
|
|
2189
|
+
// Text block only when there IS text. A handler can return an image with no
|
|
2190
|
+
// caption (`{ content: '', attachments: [img] }` — the mainline vision-agent
|
|
2191
|
+
// "just look at this" shape), and Anthropic 400s an empty text block, live and on
|
|
2192
|
+
// replay. Guarded like every user-turn path; the array is then image-only.
|
|
2193
|
+
...msg.toolResult.content ? [{ type: "text", text: msg.toolResult.content }] : [],
|
|
2194
|
+
...resultImages.map((att) => ({
|
|
2195
|
+
type: "image",
|
|
2196
|
+
source: {
|
|
2197
|
+
type: "base64",
|
|
2198
|
+
media_type: normalizeImageMime(att.mimeType),
|
|
2199
|
+
data: stripImageDataUrl(att.data)
|
|
2200
|
+
}
|
|
2201
|
+
}))
|
|
2202
|
+
] : msg.toolResult.content
|
|
2108
2203
|
});
|
|
2109
2204
|
continue;
|
|
2110
2205
|
}
|
|
2111
|
-
if ((
|
|
2206
|
+
if ((_b = msg.toolCalls) === null || _b === void 0 ? void 0 : _b.length) {
|
|
2112
2207
|
for (const block of this.reasoningToReplay(msg.toolCalls[0], reachableModels)) {
|
|
2113
2208
|
pushBlock("assistant", block);
|
|
2114
2209
|
}
|
|
@@ -2120,30 +2215,33 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
2120
2215
|
type: "tool_use",
|
|
2121
2216
|
id: tc.id,
|
|
2122
2217
|
name: tc.name,
|
|
2123
|
-
input: (
|
|
2218
|
+
input: (_c = tc.args) !== null && _c !== void 0 ? _c : {}
|
|
2124
2219
|
});
|
|
2125
2220
|
}
|
|
2126
2221
|
continue;
|
|
2127
2222
|
}
|
|
2128
2223
|
const role = msg.role === "user" ? "user" : "assistant";
|
|
2129
|
-
if (role === "user" && ((
|
|
2130
|
-
|
|
2131
|
-
for (const
|
|
2132
|
-
pushBlock(role,
|
|
2133
|
-
|
|
2134
|
-
|
|
2224
|
+
if (role === "user" && ((_d = msg.attachments) === null || _d === void 0 ? void 0 : _d.length)) {
|
|
2225
|
+
const { images, texts } = splitAttachments(msg.attachments);
|
|
2226
|
+
for (const image of images)
|
|
2227
|
+
pushBlock(role, image);
|
|
2228
|
+
if (msg.content)
|
|
2229
|
+
pushBlock(role, { type: "text", text: msg.content });
|
|
2230
|
+
for (const text of texts)
|
|
2231
|
+
pushBlock(role, text);
|
|
2135
2232
|
} else if (msg.content) {
|
|
2136
2233
|
pushBlock(role, { type: "text", text: msg.content });
|
|
2137
2234
|
}
|
|
2138
2235
|
}
|
|
2139
2236
|
if (userMessage || (attachments === null || attachments === void 0 ? void 0 : attachments.length)) {
|
|
2237
|
+
const { images, texts } = splitAttachments(attachments !== null && attachments !== void 0 ? attachments : []);
|
|
2238
|
+
for (const image of images)
|
|
2239
|
+
pushBlock("user", image);
|
|
2140
2240
|
if (userMessage) {
|
|
2141
2241
|
pushBlock("user", { type: "text", text: userMessage });
|
|
2142
2242
|
}
|
|
2143
|
-
for (const
|
|
2144
|
-
pushBlock("user",
|
|
2145
|
-
${att.content}` });
|
|
2146
|
-
}
|
|
2243
|
+
for (const text of texts)
|
|
2244
|
+
pushBlock("user", text);
|
|
2147
2245
|
}
|
|
2148
2246
|
return messages;
|
|
2149
2247
|
}
|
|
@@ -2250,7 +2348,7 @@ ${att.content}` });
|
|
|
2250
2348
|
const cacheCreation = (_c = usage.cache_creation_input_tokens) !== null && _c !== void 0 ? _c : breakdown ? ((_d = breakdown.ephemeral_5m_input_tokens) !== null && _d !== void 0 ? _d : 0) + cacheCreation1h : 0;
|
|
2251
2349
|
return this.logTokenUsage(model, (_e = usage.input_tokens) !== null && _e !== void 0 ? _e : 0, (_f = usage.output_tokens) !== null && _f !== void 0 ? _f : 0, cacheRead, cacheCreation, cacheCreation1h);
|
|
2252
2350
|
}
|
|
2253
|
-
fromAnthropicResponse(response) {
|
|
2351
|
+
fromAnthropicResponse(response, effectiveMaxTokens = this.maxTokens) {
|
|
2254
2352
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
2255
2353
|
let inputTokens;
|
|
2256
2354
|
let outputTokens;
|
|
@@ -2327,7 +2425,7 @@ ${att.content}` });
|
|
|
2327
2425
|
toolCalls[0].providerMetadata = Object.assign(Object.assign({}, toolCalls[0].providerMetadata), { [ANTHROPIC_PROVIDER_KEY]: state });
|
|
2328
2426
|
}
|
|
2329
2427
|
if (response.stop_reason === "max_tokens" && toolCalls.length > 0) {
|
|
2330
|
-
throw new ResponseTruncatedError(this.model,
|
|
2428
|
+
throw new ResponseTruncatedError(this.model, effectiveMaxTokens, outputTokens, toolCalls.map((tc) => tc.name));
|
|
2331
2429
|
}
|
|
2332
2430
|
const reasoning = thoughtParts.join("") || void 0;
|
|
2333
2431
|
const base = toolCalls.length > 0 ? { role: "assistant", content: textParts.join(""), reasoning, toolCalls } : { role: "assistant", content: textParts.join(""), reasoning };
|
|
@@ -2653,7 +2751,43 @@ var GEMINI_THINKING_DISABLEABLE = [
|
|
|
2653
2751
|
"gemini-2.5-flash",
|
|
2654
2752
|
"gemini-2.5-flash-lite"
|
|
2655
2753
|
];
|
|
2754
|
+
var GEMINI_MEDIA_RESOLUTION_TIERS = [
|
|
2755
|
+
"gemini-3.5-flash",
|
|
2756
|
+
"gemini-3.1-flash-lite",
|
|
2757
|
+
"gemini-3.1-pro-preview"
|
|
2758
|
+
];
|
|
2759
|
+
var GEMINI_THINKING_LEVEL_TIERS = [
|
|
2760
|
+
"gemini-3.5-flash",
|
|
2761
|
+
"gemini-3.1-flash-lite",
|
|
2762
|
+
"gemini-3.1-pro-preview"
|
|
2763
|
+
];
|
|
2764
|
+
var GEMINI_THINKING_LEVEL = {
|
|
2765
|
+
minimal: "minimal",
|
|
2766
|
+
low: "low",
|
|
2767
|
+
medium: "medium",
|
|
2768
|
+
high: "high",
|
|
2769
|
+
max: "high"
|
|
2770
|
+
};
|
|
2771
|
+
var GEMINI_COARSE_LEVEL_TIERS = [
|
|
2772
|
+
"gemini-3.5-flash",
|
|
2773
|
+
"gemini-3.1-flash-lite",
|
|
2774
|
+
"gemini-3.1-pro-preview"
|
|
2775
|
+
];
|
|
2776
|
+
var GEMINI_COARSE_LEVEL = {
|
|
2777
|
+
minimal: "low",
|
|
2778
|
+
low: "low",
|
|
2779
|
+
medium: "high",
|
|
2780
|
+
high: "high"
|
|
2781
|
+
};
|
|
2782
|
+
var isGradedLevel = (p) => p !== void 0 && p !== "auto" && p !== "off";
|
|
2656
2783
|
function geminiThinkingConfig(model, policy) {
|
|
2784
|
+
if (isGradedLevel(policy) && GEMINI_THINKING_LEVEL_TIERS.includes(model)) {
|
|
2785
|
+
const level = GEMINI_THINKING_LEVEL[policy];
|
|
2786
|
+
return {
|
|
2787
|
+
includeThoughts: true,
|
|
2788
|
+
thinkingLevel: GEMINI_COARSE_LEVEL_TIERS.includes(model) ? GEMINI_COARSE_LEVEL[level] : level
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2657
2791
|
if (policy === "off" && GEMINI_THINKING_DISABLEABLE.includes(model)) {
|
|
2658
2792
|
return { includeThoughts: false, thinkingBudget: 0 };
|
|
2659
2793
|
}
|
|
@@ -2664,6 +2798,24 @@ function geminiThinkingConfig(model, policy) {
|
|
|
2664
2798
|
}
|
|
2665
2799
|
var SKIP_SIGNATURE = "skip_thought_signature_validator";
|
|
2666
2800
|
var GEMINI_PROVIDER_KEY = "gemini";
|
|
2801
|
+
function splitAttachmentParts(atts) {
|
|
2802
|
+
const imageParts = [];
|
|
2803
|
+
const textParts = [];
|
|
2804
|
+
for (const att of atts) {
|
|
2805
|
+
if (att.kind === "image") {
|
|
2806
|
+
imageParts.push({
|
|
2807
|
+
inlineData: {
|
|
2808
|
+
mimeType: normalizeImageMime(att.mimeType),
|
|
2809
|
+
data: stripImageDataUrl(att.data)
|
|
2810
|
+
}
|
|
2811
|
+
});
|
|
2812
|
+
} else {
|
|
2813
|
+
textParts.push({ text: `[File: ${att.name}]
|
|
2814
|
+
${att.content}` });
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
return { imageParts, textParts };
|
|
2818
|
+
}
|
|
2667
2819
|
var MalformedFunctionCallError = class extends Error {
|
|
2668
2820
|
constructor(finishMessage) {
|
|
2669
2821
|
super("Gemini returned MALFORMED_FUNCTION_CALL");
|
|
@@ -2673,21 +2825,32 @@ var MalformedFunctionCallError = class extends Error {
|
|
|
2673
2825
|
};
|
|
2674
2826
|
var GeminiTransport = class _GeminiTransport {
|
|
2675
2827
|
/**
|
|
2676
|
-
* Warn once when a requested policy
|
|
2677
|
-
*
|
|
2678
|
-
*
|
|
2828
|
+
* Warn once when a requested policy cannot be honoured on this model — a *cost* decision the
|
|
2829
|
+
* caller made that the wire silently reversed, so they hear about it, but only once, not per
|
|
2830
|
+
* turn. Two shapes reach here (the Anthropic twin warns on the same two):
|
|
2679
2831
|
*
|
|
2680
|
-
*
|
|
2681
|
-
*
|
|
2682
|
-
*
|
|
2683
|
-
*
|
|
2832
|
+
* - `'off'` on a tier that can't disable thinking — only the flash tiers accept a zero thinking
|
|
2833
|
+
* budget; everything else keeps paying for reasoning it was told to stop. (`'auto'` is NOT this
|
|
2834
|
+
* case: it is already what an omitted budget produces on every model here, so warning it was
|
|
2835
|
+
* "ignored" would be false and would spend the one warning the genuine cases need.)
|
|
2836
|
+
* - A graded depth (`minimal`…`max`) on a 2.5 tier — those speak the numeric-budget dialect,
|
|
2837
|
+
* not the Gemini 3 `thinkingLevel` enum, so the level is dropped and the turn runs at the
|
|
2838
|
+
* default posture. Without this, a caller asking for `high` on `gemini-2.5-pro` and quietly
|
|
2839
|
+
* getting the default is left to wonder why.
|
|
2684
2840
|
*/
|
|
2685
2841
|
warnIfThinkingUnclampable(policy) {
|
|
2686
|
-
if (
|
|
2842
|
+
if (this.warnedThinkingClamped) {
|
|
2687
2843
|
return;
|
|
2688
2844
|
}
|
|
2689
|
-
this.
|
|
2690
|
-
|
|
2845
|
+
if (policy === "off" && !GEMINI_THINKING_DISABLEABLE.includes(this.model)) {
|
|
2846
|
+
this.warnedThinkingClamped = true;
|
|
2847
|
+
logger.warn(`GeminiTransport: thinkingPolicy 'off' ignored \u2014 ${this.model} runs its default thinking posture and takes no budget we can safely set. Reasoning tokens are still billed at the candidate rate; use a flash tier if you need them gone.`);
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
if (isGradedLevel(policy) && !GEMINI_THINKING_LEVEL_TIERS.includes(this.model)) {
|
|
2851
|
+
this.warnedThinkingClamped = true;
|
|
2852
|
+
logger.warn(`GeminiTransport: thinkingPolicy '${policy}' ignored \u2014 ${this.model} takes a numeric thinking budget, not the graded 'thinkingLevel' the Gemini 3 tiers use, so this turn runs at its default reasoning posture. Use a Gemini 3 tier to set reasoning depth.`);
|
|
2853
|
+
}
|
|
2691
2854
|
}
|
|
2692
2855
|
constructor(config = {}) {
|
|
2693
2856
|
var _a, _b, _c;
|
|
@@ -2744,7 +2907,7 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2744
2907
|
// ── ChatTransport (multi-turn chat) ────────────────────────────────────
|
|
2745
2908
|
sendChatMessage(history, userMessage, options) {
|
|
2746
2909
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2747
|
-
var _a, _b;
|
|
2910
|
+
var _a, _b, _c;
|
|
2748
2911
|
const contents = this.toGeminiContents(history, userMessage, options === null || options === void 0 ? void 0 : options.attachments);
|
|
2749
2912
|
if (options === null || options === void 0 ? void 0 : options.tailContext) {
|
|
2750
2913
|
contents.push({ role: "user", parts: [{ text: options.tailContext }] });
|
|
@@ -2762,13 +2925,33 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2762
2925
|
const toolConfig = tools ? toGeminiToolConfig(options === null || options === void 0 ? void 0 : options.toolChoice) : void 0;
|
|
2763
2926
|
this.warnIfThinkingUnclampable(options === null || options === void 0 ? void 0 : options.thinkingPolicy);
|
|
2764
2927
|
const generationConfig = { thinkingConfig: geminiThinkingConfig(this.model, options === null || options === void 0 ? void 0 : options.thinkingPolicy) };
|
|
2928
|
+
if ((options === null || options === void 0 ? void 0 : options.maxTokens) != null) {
|
|
2929
|
+
generationConfig.maxOutputTokens = options.maxTokens;
|
|
2930
|
+
}
|
|
2931
|
+
const resolutionHint = [
|
|
2932
|
+
...(_b = options === null || options === void 0 ? void 0 : options.attachments) !== null && _b !== void 0 ? _b : [],
|
|
2933
|
+
...history.flatMap((m) => {
|
|
2934
|
+
var _a2;
|
|
2935
|
+
return (_a2 = m.attachments) !== null && _a2 !== void 0 ? _a2 : [];
|
|
2936
|
+
}),
|
|
2937
|
+
// Tool-result images too: `ChatToolResult.attachments` is typed ChatImageAttachment[], so
|
|
2938
|
+
// `detail` is offerable there, and a render handed back by a tool is the whole point of
|
|
2939
|
+
// the tool-result image path. Omitting this source made the field silently inert there.
|
|
2940
|
+
...history.flatMap((m) => {
|
|
2941
|
+
var _a2, _b2;
|
|
2942
|
+
return (_b2 = (_a2 = m.toolResult) === null || _a2 === void 0 ? void 0 : _a2.attachments) !== null && _b2 !== void 0 ? _b2 : [];
|
|
2943
|
+
})
|
|
2944
|
+
].find((att) => att.kind === "image" && att.detail);
|
|
2945
|
+
if ((resolutionHint === null || resolutionHint === void 0 ? void 0 : resolutionHint.kind) === "image" && resolutionHint.detail && GEMINI_MEDIA_RESOLUTION_TIERS.includes(this.model)) {
|
|
2946
|
+
generationConfig.mediaResolution = `MEDIA_RESOLUTION_${resolutionHint.detail.toUpperCase()}`;
|
|
2947
|
+
}
|
|
2765
2948
|
if ((options === null || options === void 0 ? void 0 : options.temperature) != null) {
|
|
2766
2949
|
generationConfig.temperature = scaleTemperature(options.temperature, {
|
|
2767
2950
|
defaultTemp: GEMINI_DEFAULT_TEMPERATURE,
|
|
2768
2951
|
maxTemp: GEMINI_MAX_TEMPERATURE
|
|
2769
2952
|
});
|
|
2770
2953
|
}
|
|
2771
|
-
const offeredToolNames = new Set(((
|
|
2954
|
+
const offeredToolNames = new Set(((_c = options === null || options === void 0 ? void 0 : options.tools) !== null && _c !== void 0 ? _c : []).map((t) => t.name));
|
|
2772
2955
|
const schemaCoexistsWithTools = this.model.startsWith("gemini-3");
|
|
2773
2956
|
const applyResponseSchema = (options === null || options === void 0 ? void 0 : options.responseSchema) != null && (!tools || schemaCoexistsWithTools);
|
|
2774
2957
|
const response = yield this.post(Object.assign({
|
|
@@ -2779,7 +2962,7 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2779
2962
|
toolConfig,
|
|
2780
2963
|
generationConfig
|
|
2781
2964
|
}, applyResponseSchema ? { responseSchema: toGeminiSchema(options.responseSchema) } : {}), options === null || options === void 0 ? void 0 : options.signal);
|
|
2782
|
-
return this.fromGeminiResponse(response, offeredToolNames);
|
|
2965
|
+
return this.fromGeminiResponse(response, offeredToolNames, generationConfig.maxOutputTokens);
|
|
2783
2966
|
});
|
|
2784
2967
|
}
|
|
2785
2968
|
/**
|
|
@@ -2807,7 +2990,7 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2807
2990
|
return costUsd;
|
|
2808
2991
|
}
|
|
2809
2992
|
toGeminiContents(history, userMessage, attachments) {
|
|
2810
|
-
var _a, _b, _c;
|
|
2993
|
+
var _a, _b, _c, _d;
|
|
2811
2994
|
const contents = [];
|
|
2812
2995
|
const toolCallNameById = /* @__PURE__ */ new Map();
|
|
2813
2996
|
for (const msg of history) {
|
|
@@ -2827,7 +3010,16 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2827
3010
|
}
|
|
2828
3011
|
]
|
|
2829
3012
|
});
|
|
2830
|
-
|
|
3013
|
+
const resultImages = (_b = msg.toolResult.attachments) !== null && _b !== void 0 ? _b : [];
|
|
3014
|
+
if (resultImages.length) {
|
|
3015
|
+
contents.push({
|
|
3016
|
+
role: "user",
|
|
3017
|
+
parts: resultImages.map((att) => ({
|
|
3018
|
+
inlineData: { mimeType: normalizeImageMime(att.mimeType), data: att.data }
|
|
3019
|
+
}))
|
|
3020
|
+
});
|
|
3021
|
+
}
|
|
3022
|
+
} else if ((_c = msg.toolCalls) === null || _c === void 0 ? void 0 : _c.length) {
|
|
2831
3023
|
for (const tc of msg.toolCalls) {
|
|
2832
3024
|
toolCallNameById.set(tc.id, tc.name);
|
|
2833
3025
|
}
|
|
@@ -2846,22 +3038,22 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2846
3038
|
return part;
|
|
2847
3039
|
})
|
|
2848
3040
|
});
|
|
2849
|
-
} else if (role === "user" && ((
|
|
2850
|
-
const
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
3041
|
+
} else if (role === "user" && ((_d = msg.attachments) === null || _d === void 0 ? void 0 : _d.length)) {
|
|
3042
|
+
const { imageParts: imageParts2, textParts: textParts2 } = splitAttachmentParts(msg.attachments);
|
|
3043
|
+
contents.push({
|
|
3044
|
+
role: "user",
|
|
3045
|
+
parts: [...imageParts2, ...msg.content ? [{ text: msg.content }] : [], ...textParts2]
|
|
3046
|
+
});
|
|
2855
3047
|
} else {
|
|
2856
3048
|
contents.push({ role, parts: [{ text: msg.content }] });
|
|
2857
3049
|
}
|
|
2858
3050
|
}
|
|
2859
|
-
const
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
3051
|
+
const { imageParts, textParts } = splitAttachmentParts(attachments !== null && attachments !== void 0 ? attachments : []);
|
|
3052
|
+
if (userMessage || imageParts.length || textParts.length) {
|
|
3053
|
+
contents.push({
|
|
3054
|
+
role: "user",
|
|
3055
|
+
parts: [...imageParts, ...userMessage ? [{ text: userMessage }] : [], ...textParts]
|
|
3056
|
+
});
|
|
2865
3057
|
}
|
|
2866
3058
|
return contents;
|
|
2867
3059
|
}
|
|
@@ -2898,7 +3090,7 @@ ${att.content}`
|
|
|
2898
3090
|
cost
|
|
2899
3091
|
};
|
|
2900
3092
|
}
|
|
2901
|
-
fromGeminiResponse(response, offeredToolNames = /* @__PURE__ */ new Set()) {
|
|
3093
|
+
fromGeminiResponse(response, offeredToolNames = /* @__PURE__ */ new Set(), effectiveMaxTokens) {
|
|
2902
3094
|
var _a, _b, _c, _d;
|
|
2903
3095
|
const { inputTokens, outputTokens, thoughtsTokens, cacheReadTokens, cost } = this.usageFromGemini(response.usageMetadata);
|
|
2904
3096
|
const candidates = response === null || response === void 0 ? void 0 : response.candidates;
|
|
@@ -2945,7 +3137,7 @@ ${att.content}`
|
|
|
2945
3137
|
textParts: textParts.length
|
|
2946
3138
|
});
|
|
2947
3139
|
}
|
|
2948
|
-
this.guardMaxTokensTruncation(finishReason, toolCalls, narration, outputTokens);
|
|
3140
|
+
this.guardMaxTokensTruncation(finishReason, toolCalls, narration, outputTokens, effectiveMaxTokens);
|
|
2949
3141
|
if (inputTokens != null)
|
|
2950
3142
|
base.inputTokens = inputTokens;
|
|
2951
3143
|
if (outputTokens != null)
|
|
@@ -2983,12 +3175,12 @@ ${att.content}`
|
|
|
2983
3175
|
* `responseMeta.finishReason`, for the caller to decide. Usage/cost is logged
|
|
2984
3176
|
* before this check runs, so the spent tokens stay accounted for.
|
|
2985
3177
|
*/
|
|
2986
|
-
guardMaxTokensTruncation(finishReason, toolCalls, narration, outputTokens) {
|
|
3178
|
+
guardMaxTokensTruncation(finishReason, toolCalls, narration, outputTokens, effectiveMaxTokens) {
|
|
2987
3179
|
if (finishReason !== "MAX_TOKENS")
|
|
2988
3180
|
return;
|
|
2989
3181
|
if (toolCalls.length === 0 && narration.trim() !== "")
|
|
2990
3182
|
return;
|
|
2991
|
-
throw new ResponseTruncatedError(this.model,
|
|
3183
|
+
throw new ResponseTruncatedError(this.model, effectiveMaxTokens, outputTokens, toolCalls.map((tc) => tc.name));
|
|
2992
3184
|
}
|
|
2993
3185
|
/**
|
|
2994
3186
|
* Log the full shape of a blank or non-STOP response so its cause is legible
|
|
@@ -3549,11 +3741,12 @@ function triggerReason(trigger) {
|
|
|
3549
3741
|
return "superseded";
|
|
3550
3742
|
}
|
|
3551
3743
|
}
|
|
3552
|
-
function condenseStub(target, tool, trigger, origLen, restorable) {
|
|
3744
|
+
function condenseStub(target, tool, trigger, origLen, restorable, imageCount = 0) {
|
|
3553
3745
|
const what = target === "args" ? "args" : "result";
|
|
3554
3746
|
const key = trigger.kind === "superseded" ? ` ${trigger.by}` : "";
|
|
3555
3747
|
const restore = restorable ? "; re-call to restore" : "";
|
|
3556
|
-
|
|
3748
|
+
const images = imageCount > 0 ? ` + ${imageCount} image${imageCount === 1 ? "" : "s"}` : "";
|
|
3749
|
+
return `[${tool}${key} \u2014 ${what} elided, ~${origLen} chars${images} (${triggerReason(trigger)})${restore}]`;
|
|
3557
3750
|
}
|
|
3558
3751
|
function triggerLabel(trigger) {
|
|
3559
3752
|
switch (trigger.kind) {
|
|
@@ -3652,12 +3845,14 @@ function applyCondensation(history, policies, ctx, onCondensed) {
|
|
|
3652
3845
|
}
|
|
3653
3846
|
if (msg.toolResult) {
|
|
3654
3847
|
const entry = policies.get(msg.toolResult.toolCallId);
|
|
3655
|
-
const
|
|
3848
|
+
const textLen = msg.toolResult.content?.length ?? 0;
|
|
3849
|
+
const images = msg.toolResult.attachments ?? [];
|
|
3850
|
+
const origLen = textLen + images.reduce((n, att) => n + (att.data?.length ?? 0), 0);
|
|
3656
3851
|
const fired = entry?.policy.response ? firstFired(msg.toolResult.toolCallId, entry) : void 0;
|
|
3657
3852
|
if (entry && fired && origLen >= CONDENSE_MIN_CHARS) {
|
|
3658
3853
|
const tool = nameById.get(msg.toolResult.toolCallId) ?? msg.toolResult.toolCallId;
|
|
3659
3854
|
const result = entry.policy.response;
|
|
3660
|
-
const content = result === "drop" || result === "pointer" ? condenseStub("response", tool, fired,
|
|
3855
|
+
const content = result === "drop" || result === "pointer" ? condenseStub("response", tool, fired, textLen, result === "pointer", images.length) : result.replaceWith;
|
|
3661
3856
|
if (!entry.reportedResponse) {
|
|
3662
3857
|
entry.reportedResponse = true;
|
|
3663
3858
|
onCondensed({
|
|
@@ -3670,7 +3865,8 @@ function applyCondensation(history, policies, ctx, onCondensed) {
|
|
|
3670
3865
|
tokensSaved: estimateTokensSaved(origLen, content.length)
|
|
3671
3866
|
});
|
|
3672
3867
|
}
|
|
3673
|
-
|
|
3868
|
+
const { attachments: _elided, ...restOfResult } = msg.toolResult;
|
|
3869
|
+
return { ...msg, toolResult: { ...restOfResult, content } };
|
|
3674
3870
|
}
|
|
3675
3871
|
}
|
|
3676
3872
|
return msg;
|
|
@@ -3683,9 +3879,10 @@ function maskToolPayload(msg) {
|
|
|
3683
3879
|
return { ...msg, toolCalls: msg.toolCalls.map((tc) => ({ ...tc, args: {} })) };
|
|
3684
3880
|
}
|
|
3685
3881
|
if (msg.toolResult) {
|
|
3882
|
+
const { attachments: _masked, ...restOfResult } = msg.toolResult;
|
|
3686
3883
|
return {
|
|
3687
3884
|
...msg,
|
|
3688
|
-
toolResult: { ...
|
|
3885
|
+
toolResult: { ...restOfResult, content: "[other agent's tool result omitted]" }
|
|
3689
3886
|
};
|
|
3690
3887
|
}
|
|
3691
3888
|
return msg;
|
|
@@ -3839,6 +4036,15 @@ function accumulate(messages, into) {
|
|
|
3839
4036
|
var TOOL_FOLD_SYMBOL = Symbol("toolFold");
|
|
3840
4037
|
|
|
3841
4038
|
// src/components/chat-driver/chat-driver.ts
|
|
4039
|
+
var toolResultImages = (result) => {
|
|
4040
|
+
if (!result || typeof result !== "object") return void 0;
|
|
4041
|
+
const candidate = result.attachments;
|
|
4042
|
+
if (!Array.isArray(candidate) || candidate.length === 0) return void 0;
|
|
4043
|
+
const allImages = candidate.every(
|
|
4044
|
+
(att) => !!att && typeof att === "object" && att.kind === "image"
|
|
4045
|
+
);
|
|
4046
|
+
return allImages ? candidate : void 0;
|
|
4047
|
+
};
|
|
3842
4048
|
var providerRefusedDetailOf = (e) => ({
|
|
3843
4049
|
vendorLabel: e.vendorLabel,
|
|
3844
4050
|
kind: e.kind,
|
|
@@ -5969,10 +6175,12 @@ ${tailBody}
|
|
|
5969
6175
|
const capturedTrace = () => traceCapture.traces.length ? traceCapture.traces.flat() : void 0;
|
|
5970
6176
|
try {
|
|
5971
6177
|
const result = await handler(tc.args, this.buildHandlerContext(tc.id, traceCapture));
|
|
5972
|
-
const
|
|
6178
|
+
const resultImages = toolResultImages(result);
|
|
6179
|
+
const content = resultImages ? typeof result.content === "string" ? result.content : JSON.stringify({ ...result, attachments: void 0 }) : typeof result === "string" ? result : JSON.stringify(result);
|
|
5973
6180
|
executedById.set(tc.id, {
|
|
5974
6181
|
toolCallId: tc.id,
|
|
5975
6182
|
content,
|
|
6183
|
+
...resultImages ? { attachments: resultImages } : {},
|
|
5976
6184
|
// Concatenated when a handler invoked several children, so none is lost.
|
|
5977
6185
|
// Stays `undefined` when nothing was captured — readers key off presence,
|
|
5978
6186
|
// and an empty array is a different claim from "no sub-agent ran".
|
|
@@ -6021,7 +6229,14 @@ RECOVERY: this tool failed once \u2014 you may retry it, or take a different val
|
|
|
6021
6229
|
this.appendToHistory({
|
|
6022
6230
|
role: "tool",
|
|
6023
6231
|
content: "",
|
|
6024
|
-
toolResult: {
|
|
6232
|
+
toolResult: {
|
|
6233
|
+
toolCallId: r.toolCallId,
|
|
6234
|
+
content: r.content,
|
|
6235
|
+
// Images DO persist, unlike the sub-agent trace above: they are the payload
|
|
6236
|
+
// the model must see on every replay of this turn, not a one-off UI artefact.
|
|
6237
|
+
// `condenseWhen` is what sheds them once they are spent — see condense-history.
|
|
6238
|
+
...r.attachments ? { attachments: r.attachments } : {}
|
|
6239
|
+
}
|
|
6025
6240
|
});
|
|
6026
6241
|
}
|
|
6027
6242
|
}
|