@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.mjs
CHANGED
|
@@ -1154,6 +1154,14 @@ function vendorOfModel(modelId) {
|
|
|
1154
1154
|
return void 0;
|
|
1155
1155
|
}
|
|
1156
1156
|
|
|
1157
|
+
// ../../foundation-ai/dist/esm/utils/image-mime.js
|
|
1158
|
+
function normalizeImageMime(mimeType) {
|
|
1159
|
+
return mimeType === "image/jpg" ? "image/jpeg" : mimeType;
|
|
1160
|
+
}
|
|
1161
|
+
function stripImageDataUrl(data) {
|
|
1162
|
+
return data.replace(/^data:[^,]*;base64,/, "");
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1157
1165
|
// ../../foundation-ai/dist/esm/utils/temperature.js
|
|
1158
1166
|
var DEFAULT_ANCHOR = 0.5;
|
|
1159
1167
|
function scaleTemperature(normalized, { defaultTemp, maxTemp }) {
|
|
@@ -1762,8 +1770,28 @@ function anthropicThinking(model, policy) {
|
|
|
1762
1770
|
if (policy === "auto") {
|
|
1763
1771
|
return supportsAdaptiveThinking(model) ? adaptive : void 0;
|
|
1764
1772
|
}
|
|
1773
|
+
if (policy !== void 0) {
|
|
1774
|
+
return supportsAdaptiveThinking(model) ? adaptive : void 0;
|
|
1775
|
+
}
|
|
1765
1776
|
return defaultsToThinking ? adaptive : void 0;
|
|
1766
1777
|
}
|
|
1778
|
+
function supportsEffort(model) {
|
|
1779
|
+
return model === "claude-fable-5" || model === "claude-opus-4-8" || model === "claude-sonnet-5";
|
|
1780
|
+
}
|
|
1781
|
+
var ANTHROPIC_EFFORT = {
|
|
1782
|
+
minimal: "low",
|
|
1783
|
+
low: "low",
|
|
1784
|
+
medium: "medium",
|
|
1785
|
+
high: "high",
|
|
1786
|
+
max: "max"
|
|
1787
|
+
};
|
|
1788
|
+
function anthropicEffort(model, policy) {
|
|
1789
|
+
if (policy === void 0 || policy === "auto" || policy === "off")
|
|
1790
|
+
return void 0;
|
|
1791
|
+
if (!supportsEffort(model) || !supportsAdaptiveThinking(model))
|
|
1792
|
+
return void 0;
|
|
1793
|
+
return ANTHROPIC_EFFORT[policy];
|
|
1794
|
+
}
|
|
1767
1795
|
var ANTHROPIC_PROVIDER_KEY = "anthropic";
|
|
1768
1796
|
var ResponseTruncatedError = class extends Error {
|
|
1769
1797
|
constructor(model, maxTokens, outputTokens, toolNames) {
|
|
@@ -1788,11 +1816,42 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1788
1816
|
warnIfThinkingUnclampable(policy) {
|
|
1789
1817
|
if (this.warnedThinkingClamped)
|
|
1790
1818
|
return;
|
|
1791
|
-
const
|
|
1792
|
-
if (!
|
|
1819
|
+
const message = this.clampWarning(policy);
|
|
1820
|
+
if (!message)
|
|
1793
1821
|
return;
|
|
1794
1822
|
this.warnedThinkingClamped = true;
|
|
1795
|
-
logger.warn(
|
|
1823
|
+
logger.warn(message);
|
|
1824
|
+
}
|
|
1825
|
+
/**
|
|
1826
|
+
* The warning for a policy this model cannot honour, or `undefined` when it can.
|
|
1827
|
+
*
|
|
1828
|
+
* An exhaustive `switch` with a `never` check rather than a condition chain: a new
|
|
1829
|
+
* {@link ChatThinkingPolicy} member must be considered HERE or the build fails. The condition
|
|
1830
|
+
* chain this replaced would have let every graded level clamp in silence, which is the one
|
|
1831
|
+
* outcome the type's own docs promise callers never happens.
|
|
1832
|
+
*/
|
|
1833
|
+
clampWarning(policy) {
|
|
1834
|
+
switch (policy) {
|
|
1835
|
+
case void 0:
|
|
1836
|
+
return void 0;
|
|
1837
|
+
case "off":
|
|
1838
|
+
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;
|
|
1839
|
+
case "auto":
|
|
1840
|
+
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.`;
|
|
1841
|
+
case "minimal":
|
|
1842
|
+
case "low":
|
|
1843
|
+
case "medium":
|
|
1844
|
+
case "high":
|
|
1845
|
+
case "max":
|
|
1846
|
+
if (!supportsAdaptiveThinking(this.model) || !supportsEffort(this.model)) {
|
|
1847
|
+
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.`;
|
|
1848
|
+
}
|
|
1849
|
+
return void 0;
|
|
1850
|
+
default: {
|
|
1851
|
+
const exhaustive = policy;
|
|
1852
|
+
return exhaustive;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1796
1855
|
}
|
|
1797
1856
|
constructor(config = {}) {
|
|
1798
1857
|
var _a, _b, _c, _d;
|
|
@@ -1854,9 +1913,7 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1854
1913
|
const useNative = responseSchema != null && supportsNativeStructuredOutput(this.model);
|
|
1855
1914
|
const useForcedTool = responseSchema != null && !useNative;
|
|
1856
1915
|
if (useNative) {
|
|
1857
|
-
body.output_config = {
|
|
1858
|
-
format: { type: "json_schema", schema: responseSchema }
|
|
1859
|
-
};
|
|
1916
|
+
body.output_config = Object.assign(Object.assign({}, body.output_config), { format: { type: "json_schema", schema: responseSchema } });
|
|
1860
1917
|
} else if (useForcedTool) {
|
|
1861
1918
|
body.tools = [
|
|
1862
1919
|
{
|
|
@@ -1880,7 +1937,7 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1880
1937
|
// ── ChatTransport (multi-turn chat) ────────────────────────────────────
|
|
1881
1938
|
sendChatMessage(history, userMessage, options) {
|
|
1882
1939
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1883
|
-
var _a, _b, _c, _d;
|
|
1940
|
+
var _a, _b, _c, _d, _e;
|
|
1884
1941
|
const reachableModels = /* @__PURE__ */ new Set([
|
|
1885
1942
|
this.model,
|
|
1886
1943
|
...((_a = options === null || options === void 0 ? void 0 : options.fallbacks) !== null && _a !== void 0 ? _a : []).map((f) => f.model)
|
|
@@ -1888,15 +1945,16 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1888
1945
|
const messages = this.toAnthropicMessages(history, userMessage, options === null || options === void 0 ? void 0 : options.attachments, reachableModels);
|
|
1889
1946
|
const body = {
|
|
1890
1947
|
model: this.model,
|
|
1891
|
-
|
|
1948
|
+
// Per-turn override wins over the instance default; see ChatRequestOptions.maxTokens.
|
|
1949
|
+
max_tokens: (_b = options === null || options === void 0 ? void 0 : options.maxTokens) !== null && _b !== void 0 ? _b : this.maxTokens,
|
|
1892
1950
|
messages
|
|
1893
1951
|
};
|
|
1894
1952
|
if (options === null || options === void 0 ? void 0 : options.systemPrompt)
|
|
1895
1953
|
body.system = options.systemPrompt;
|
|
1896
|
-
if ((
|
|
1954
|
+
if ((_c = options === null || options === void 0 ? void 0 : options.tools) === null || _c === void 0 ? void 0 : _c.length) {
|
|
1897
1955
|
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 } : {}));
|
|
1898
1956
|
}
|
|
1899
|
-
if ((
|
|
1957
|
+
if ((_d = body.tools) === null || _d === void 0 ? void 0 : _d.length) {
|
|
1900
1958
|
const toolChoice = toAnthropicToolChoice(options === null || options === void 0 ? void 0 : options.toolChoice);
|
|
1901
1959
|
if (toolChoice)
|
|
1902
1960
|
body.tool_choice = toolChoice;
|
|
@@ -1905,6 +1963,9 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1905
1963
|
const thinking = anthropicThinking(this.model, options === null || options === void 0 ? void 0 : options.thinkingPolicy);
|
|
1906
1964
|
if (thinking)
|
|
1907
1965
|
body.thinking = thinking;
|
|
1966
|
+
const effort = anthropicEffort(this.model, options === null || options === void 0 ? void 0 : options.thinkingPolicy);
|
|
1967
|
+
if (effort)
|
|
1968
|
+
body.output_config = Object.assign(Object.assign({}, body.output_config), { effort });
|
|
1908
1969
|
const thinkingEnabled = (thinking === null || thinking === void 0 ? void 0 : thinking.type) === "adaptive";
|
|
1909
1970
|
if ((options === null || options === void 0 ? void 0 : options.temperature) != null && !rejectsSamplingParams(this.model) && !thinkingEnabled) {
|
|
1910
1971
|
body.temperature = scaleTemperature(options.temperature, {
|
|
@@ -1913,11 +1974,9 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1913
1974
|
});
|
|
1914
1975
|
}
|
|
1915
1976
|
if ((options === null || options === void 0 ? void 0 : options.responseSchema) && supportsNativeStructuredOutput(this.model)) {
|
|
1916
|
-
body.output_config = {
|
|
1917
|
-
format: { type: "json_schema", schema: options.responseSchema }
|
|
1918
|
-
};
|
|
1977
|
+
body.output_config = Object.assign(Object.assign({}, body.output_config), { format: { type: "json_schema", schema: options.responseSchema } });
|
|
1919
1978
|
}
|
|
1920
|
-
if ((
|
|
1979
|
+
if ((_e = options === null || options === void 0 ? void 0 : options.fallbacks) === null || _e === void 0 ? void 0 : _e.length) {
|
|
1921
1980
|
body.fallbacks = options.fallbacks.map((f) => f.maxTokens != null ? { model: f.model, max_tokens: f.maxTokens } : { model: f.model });
|
|
1922
1981
|
}
|
|
1923
1982
|
if (options === null || options === void 0 ? void 0 : options.cachePolicy) {
|
|
@@ -1927,7 +1986,7 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
1927
1986
|
this.appendTailContext(body, options.tailContext);
|
|
1928
1987
|
}
|
|
1929
1988
|
const response = yield this.post(body, options === null || options === void 0 ? void 0 : options.signal);
|
|
1930
|
-
return this.fromAnthropicResponse(response);
|
|
1989
|
+
return this.fromAnthropicResponse(response, body.max_tokens);
|
|
1931
1990
|
});
|
|
1932
1991
|
}
|
|
1933
1992
|
/**
|
|
@@ -2020,7 +2079,7 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
2020
2079
|
* the payload tidy.
|
|
2021
2080
|
*/
|
|
2022
2081
|
toAnthropicMessages(history, userMessage, attachments, reachableModels = /* @__PURE__ */ new Set([this.model])) {
|
|
2023
|
-
var _a, _b, _c;
|
|
2082
|
+
var _a, _b, _c, _d;
|
|
2024
2083
|
const messages = [];
|
|
2025
2084
|
const pushBlock = (role, block) => {
|
|
2026
2085
|
const last = messages[messages.length - 1];
|
|
@@ -2030,18 +2089,54 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
2030
2089
|
}
|
|
2031
2090
|
messages.push({ role, content: [block] });
|
|
2032
2091
|
};
|
|
2092
|
+
const splitAttachments = (atts) => {
|
|
2093
|
+
const images = [];
|
|
2094
|
+
const texts = [];
|
|
2095
|
+
for (const att of atts) {
|
|
2096
|
+
if (att.kind === "image") {
|
|
2097
|
+
images.push({
|
|
2098
|
+
type: "image",
|
|
2099
|
+
source: {
|
|
2100
|
+
type: "base64",
|
|
2101
|
+
media_type: normalizeImageMime(att.mimeType),
|
|
2102
|
+
data: stripImageDataUrl(att.data)
|
|
2103
|
+
}
|
|
2104
|
+
});
|
|
2105
|
+
} else {
|
|
2106
|
+
texts.push({ type: "text", text: `[File: ${att.name}]
|
|
2107
|
+
${att.content}` });
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
return { images, texts };
|
|
2111
|
+
};
|
|
2033
2112
|
for (const msg of history) {
|
|
2034
2113
|
if (msg.role === "system" || msg.role === "system-event" || msg.role === "synthetic-user" || msg.role === "compacted-summary" || msg.category === "reasoning" || msg.category === "narration" || msg.thinking)
|
|
2035
2114
|
continue;
|
|
2036
2115
|
if (msg.toolResult) {
|
|
2116
|
+
const resultImages = (_a = msg.toolResult.attachments) !== null && _a !== void 0 ? _a : [];
|
|
2037
2117
|
pushBlock("user", {
|
|
2038
2118
|
type: "tool_result",
|
|
2039
2119
|
tool_use_id: msg.toolResult.toolCallId,
|
|
2040
|
-
|
|
2120
|
+
// Bare string when there are no images — the historical shape, byte for byte.
|
|
2121
|
+
content: resultImages.length ? [
|
|
2122
|
+
// Text block only when there IS text. A handler can return an image with no
|
|
2123
|
+
// caption (`{ content: '', attachments: [img] }` — the mainline vision-agent
|
|
2124
|
+
// "just look at this" shape), and Anthropic 400s an empty text block, live and on
|
|
2125
|
+
// replay. Guarded like every user-turn path; the array is then image-only.
|
|
2126
|
+
...msg.toolResult.content ? [{ type: "text", text: msg.toolResult.content }] : [],
|
|
2127
|
+
...resultImages.map((att) => ({
|
|
2128
|
+
type: "image",
|
|
2129
|
+
source: {
|
|
2130
|
+
type: "base64",
|
|
2131
|
+
media_type: normalizeImageMime(att.mimeType),
|
|
2132
|
+
data: stripImageDataUrl(att.data)
|
|
2133
|
+
}
|
|
2134
|
+
}))
|
|
2135
|
+
] : msg.toolResult.content
|
|
2041
2136
|
});
|
|
2042
2137
|
continue;
|
|
2043
2138
|
}
|
|
2044
|
-
if ((
|
|
2139
|
+
if ((_b = msg.toolCalls) === null || _b === void 0 ? void 0 : _b.length) {
|
|
2045
2140
|
for (const block of this.reasoningToReplay(msg.toolCalls[0], reachableModels)) {
|
|
2046
2141
|
pushBlock("assistant", block);
|
|
2047
2142
|
}
|
|
@@ -2053,30 +2148,33 @@ var AnthropicTransport = class _AnthropicTransport {
|
|
|
2053
2148
|
type: "tool_use",
|
|
2054
2149
|
id: tc.id,
|
|
2055
2150
|
name: tc.name,
|
|
2056
|
-
input: (
|
|
2151
|
+
input: (_c = tc.args) !== null && _c !== void 0 ? _c : {}
|
|
2057
2152
|
});
|
|
2058
2153
|
}
|
|
2059
2154
|
continue;
|
|
2060
2155
|
}
|
|
2061
2156
|
const role = msg.role === "user" ? "user" : "assistant";
|
|
2062
|
-
if (role === "user" && ((
|
|
2063
|
-
|
|
2064
|
-
for (const
|
|
2065
|
-
pushBlock(role,
|
|
2066
|
-
|
|
2067
|
-
|
|
2157
|
+
if (role === "user" && ((_d = msg.attachments) === null || _d === void 0 ? void 0 : _d.length)) {
|
|
2158
|
+
const { images, texts } = splitAttachments(msg.attachments);
|
|
2159
|
+
for (const image of images)
|
|
2160
|
+
pushBlock(role, image);
|
|
2161
|
+
if (msg.content)
|
|
2162
|
+
pushBlock(role, { type: "text", text: msg.content });
|
|
2163
|
+
for (const text of texts)
|
|
2164
|
+
pushBlock(role, text);
|
|
2068
2165
|
} else if (msg.content) {
|
|
2069
2166
|
pushBlock(role, { type: "text", text: msg.content });
|
|
2070
2167
|
}
|
|
2071
2168
|
}
|
|
2072
2169
|
if (userMessage || (attachments === null || attachments === void 0 ? void 0 : attachments.length)) {
|
|
2170
|
+
const { images, texts } = splitAttachments(attachments !== null && attachments !== void 0 ? attachments : []);
|
|
2171
|
+
for (const image of images)
|
|
2172
|
+
pushBlock("user", image);
|
|
2073
2173
|
if (userMessage) {
|
|
2074
2174
|
pushBlock("user", { type: "text", text: userMessage });
|
|
2075
2175
|
}
|
|
2076
|
-
for (const
|
|
2077
|
-
pushBlock("user",
|
|
2078
|
-
${att.content}` });
|
|
2079
|
-
}
|
|
2176
|
+
for (const text of texts)
|
|
2177
|
+
pushBlock("user", text);
|
|
2080
2178
|
}
|
|
2081
2179
|
return messages;
|
|
2082
2180
|
}
|
|
@@ -2183,7 +2281,7 @@ ${att.content}` });
|
|
|
2183
2281
|
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;
|
|
2184
2282
|
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);
|
|
2185
2283
|
}
|
|
2186
|
-
fromAnthropicResponse(response) {
|
|
2284
|
+
fromAnthropicResponse(response, effectiveMaxTokens = this.maxTokens) {
|
|
2187
2285
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
2188
2286
|
let inputTokens;
|
|
2189
2287
|
let outputTokens;
|
|
@@ -2260,7 +2358,7 @@ ${att.content}` });
|
|
|
2260
2358
|
toolCalls[0].providerMetadata = Object.assign(Object.assign({}, toolCalls[0].providerMetadata), { [ANTHROPIC_PROVIDER_KEY]: state });
|
|
2261
2359
|
}
|
|
2262
2360
|
if (response.stop_reason === "max_tokens" && toolCalls.length > 0) {
|
|
2263
|
-
throw new ResponseTruncatedError(this.model,
|
|
2361
|
+
throw new ResponseTruncatedError(this.model, effectiveMaxTokens, outputTokens, toolCalls.map((tc) => tc.name));
|
|
2264
2362
|
}
|
|
2265
2363
|
const reasoning = thoughtParts.join("") || void 0;
|
|
2266
2364
|
const base = toolCalls.length > 0 ? { role: "assistant", content: textParts.join(""), reasoning, toolCalls } : { role: "assistant", content: textParts.join(""), reasoning };
|
|
@@ -2586,7 +2684,43 @@ var GEMINI_THINKING_DISABLEABLE = [
|
|
|
2586
2684
|
"gemini-2.5-flash",
|
|
2587
2685
|
"gemini-2.5-flash-lite"
|
|
2588
2686
|
];
|
|
2687
|
+
var GEMINI_MEDIA_RESOLUTION_TIERS = [
|
|
2688
|
+
"gemini-3.5-flash",
|
|
2689
|
+
"gemini-3.1-flash-lite",
|
|
2690
|
+
"gemini-3.1-pro-preview"
|
|
2691
|
+
];
|
|
2692
|
+
var GEMINI_THINKING_LEVEL_TIERS = [
|
|
2693
|
+
"gemini-3.5-flash",
|
|
2694
|
+
"gemini-3.1-flash-lite",
|
|
2695
|
+
"gemini-3.1-pro-preview"
|
|
2696
|
+
];
|
|
2697
|
+
var GEMINI_THINKING_LEVEL = {
|
|
2698
|
+
minimal: "minimal",
|
|
2699
|
+
low: "low",
|
|
2700
|
+
medium: "medium",
|
|
2701
|
+
high: "high",
|
|
2702
|
+
max: "high"
|
|
2703
|
+
};
|
|
2704
|
+
var GEMINI_COARSE_LEVEL_TIERS = [
|
|
2705
|
+
"gemini-3.5-flash",
|
|
2706
|
+
"gemini-3.1-flash-lite",
|
|
2707
|
+
"gemini-3.1-pro-preview"
|
|
2708
|
+
];
|
|
2709
|
+
var GEMINI_COARSE_LEVEL = {
|
|
2710
|
+
minimal: "low",
|
|
2711
|
+
low: "low",
|
|
2712
|
+
medium: "high",
|
|
2713
|
+
high: "high"
|
|
2714
|
+
};
|
|
2715
|
+
var isGradedLevel = (p) => p !== void 0 && p !== "auto" && p !== "off";
|
|
2589
2716
|
function geminiThinkingConfig(model, policy) {
|
|
2717
|
+
if (isGradedLevel(policy) && GEMINI_THINKING_LEVEL_TIERS.includes(model)) {
|
|
2718
|
+
const level = GEMINI_THINKING_LEVEL[policy];
|
|
2719
|
+
return {
|
|
2720
|
+
includeThoughts: true,
|
|
2721
|
+
thinkingLevel: GEMINI_COARSE_LEVEL_TIERS.includes(model) ? GEMINI_COARSE_LEVEL[level] : level
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2590
2724
|
if (policy === "off" && GEMINI_THINKING_DISABLEABLE.includes(model)) {
|
|
2591
2725
|
return { includeThoughts: false, thinkingBudget: 0 };
|
|
2592
2726
|
}
|
|
@@ -2597,6 +2731,24 @@ function geminiThinkingConfig(model, policy) {
|
|
|
2597
2731
|
}
|
|
2598
2732
|
var SKIP_SIGNATURE = "skip_thought_signature_validator";
|
|
2599
2733
|
var GEMINI_PROVIDER_KEY = "gemini";
|
|
2734
|
+
function splitAttachmentParts(atts) {
|
|
2735
|
+
const imageParts = [];
|
|
2736
|
+
const textParts = [];
|
|
2737
|
+
for (const att of atts) {
|
|
2738
|
+
if (att.kind === "image") {
|
|
2739
|
+
imageParts.push({
|
|
2740
|
+
inlineData: {
|
|
2741
|
+
mimeType: normalizeImageMime(att.mimeType),
|
|
2742
|
+
data: stripImageDataUrl(att.data)
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2745
|
+
} else {
|
|
2746
|
+
textParts.push({ text: `[File: ${att.name}]
|
|
2747
|
+
${att.content}` });
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
return { imageParts, textParts };
|
|
2751
|
+
}
|
|
2600
2752
|
var MalformedFunctionCallError = class extends Error {
|
|
2601
2753
|
constructor(finishMessage) {
|
|
2602
2754
|
super("Gemini returned MALFORMED_FUNCTION_CALL");
|
|
@@ -2606,21 +2758,32 @@ var MalformedFunctionCallError = class extends Error {
|
|
|
2606
2758
|
};
|
|
2607
2759
|
var GeminiTransport = class _GeminiTransport {
|
|
2608
2760
|
/**
|
|
2609
|
-
* Warn once when a requested policy
|
|
2610
|
-
*
|
|
2611
|
-
*
|
|
2761
|
+
* Warn once when a requested policy cannot be honoured on this model — a *cost* decision the
|
|
2762
|
+
* caller made that the wire silently reversed, so they hear about it, but only once, not per
|
|
2763
|
+
* turn. Two shapes reach here (the Anthropic twin warns on the same two):
|
|
2612
2764
|
*
|
|
2613
|
-
*
|
|
2614
|
-
*
|
|
2615
|
-
*
|
|
2616
|
-
*
|
|
2765
|
+
* - `'off'` on a tier that can't disable thinking — only the flash tiers accept a zero thinking
|
|
2766
|
+
* budget; everything else keeps paying for reasoning it was told to stop. (`'auto'` is NOT this
|
|
2767
|
+
* case: it is already what an omitted budget produces on every model here, so warning it was
|
|
2768
|
+
* "ignored" would be false and would spend the one warning the genuine cases need.)
|
|
2769
|
+
* - A graded depth (`minimal`…`max`) on a 2.5 tier — those speak the numeric-budget dialect,
|
|
2770
|
+
* not the Gemini 3 `thinkingLevel` enum, so the level is dropped and the turn runs at the
|
|
2771
|
+
* default posture. Without this, a caller asking for `high` on `gemini-2.5-pro` and quietly
|
|
2772
|
+
* getting the default is left to wonder why.
|
|
2617
2773
|
*/
|
|
2618
2774
|
warnIfThinkingUnclampable(policy) {
|
|
2619
|
-
if (
|
|
2775
|
+
if (this.warnedThinkingClamped) {
|
|
2620
2776
|
return;
|
|
2621
2777
|
}
|
|
2622
|
-
this.
|
|
2623
|
-
|
|
2778
|
+
if (policy === "off" && !GEMINI_THINKING_DISABLEABLE.includes(this.model)) {
|
|
2779
|
+
this.warnedThinkingClamped = true;
|
|
2780
|
+
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.`);
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2783
|
+
if (isGradedLevel(policy) && !GEMINI_THINKING_LEVEL_TIERS.includes(this.model)) {
|
|
2784
|
+
this.warnedThinkingClamped = true;
|
|
2785
|
+
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.`);
|
|
2786
|
+
}
|
|
2624
2787
|
}
|
|
2625
2788
|
constructor(config = {}) {
|
|
2626
2789
|
var _a, _b, _c;
|
|
@@ -2677,7 +2840,7 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2677
2840
|
// ── ChatTransport (multi-turn chat) ────────────────────────────────────
|
|
2678
2841
|
sendChatMessage(history, userMessage, options) {
|
|
2679
2842
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2680
|
-
var _a, _b;
|
|
2843
|
+
var _a, _b, _c;
|
|
2681
2844
|
const contents = this.toGeminiContents(history, userMessage, options === null || options === void 0 ? void 0 : options.attachments);
|
|
2682
2845
|
if (options === null || options === void 0 ? void 0 : options.tailContext) {
|
|
2683
2846
|
contents.push({ role: "user", parts: [{ text: options.tailContext }] });
|
|
@@ -2695,13 +2858,33 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2695
2858
|
const toolConfig = tools ? toGeminiToolConfig(options === null || options === void 0 ? void 0 : options.toolChoice) : void 0;
|
|
2696
2859
|
this.warnIfThinkingUnclampable(options === null || options === void 0 ? void 0 : options.thinkingPolicy);
|
|
2697
2860
|
const generationConfig = { thinkingConfig: geminiThinkingConfig(this.model, options === null || options === void 0 ? void 0 : options.thinkingPolicy) };
|
|
2861
|
+
if ((options === null || options === void 0 ? void 0 : options.maxTokens) != null) {
|
|
2862
|
+
generationConfig.maxOutputTokens = options.maxTokens;
|
|
2863
|
+
}
|
|
2864
|
+
const resolutionHint = [
|
|
2865
|
+
...(_b = options === null || options === void 0 ? void 0 : options.attachments) !== null && _b !== void 0 ? _b : [],
|
|
2866
|
+
...history.flatMap((m) => {
|
|
2867
|
+
var _a2;
|
|
2868
|
+
return (_a2 = m.attachments) !== null && _a2 !== void 0 ? _a2 : [];
|
|
2869
|
+
}),
|
|
2870
|
+
// Tool-result images too: `ChatToolResult.attachments` is typed ChatImageAttachment[], so
|
|
2871
|
+
// `detail` is offerable there, and a render handed back by a tool is the whole point of
|
|
2872
|
+
// the tool-result image path. Omitting this source made the field silently inert there.
|
|
2873
|
+
...history.flatMap((m) => {
|
|
2874
|
+
var _a2, _b2;
|
|
2875
|
+
return (_b2 = (_a2 = m.toolResult) === null || _a2 === void 0 ? void 0 : _a2.attachments) !== null && _b2 !== void 0 ? _b2 : [];
|
|
2876
|
+
})
|
|
2877
|
+
].find((att) => att.kind === "image" && att.detail);
|
|
2878
|
+
if ((resolutionHint === null || resolutionHint === void 0 ? void 0 : resolutionHint.kind) === "image" && resolutionHint.detail && GEMINI_MEDIA_RESOLUTION_TIERS.includes(this.model)) {
|
|
2879
|
+
generationConfig.mediaResolution = `MEDIA_RESOLUTION_${resolutionHint.detail.toUpperCase()}`;
|
|
2880
|
+
}
|
|
2698
2881
|
if ((options === null || options === void 0 ? void 0 : options.temperature) != null) {
|
|
2699
2882
|
generationConfig.temperature = scaleTemperature(options.temperature, {
|
|
2700
2883
|
defaultTemp: GEMINI_DEFAULT_TEMPERATURE,
|
|
2701
2884
|
maxTemp: GEMINI_MAX_TEMPERATURE
|
|
2702
2885
|
});
|
|
2703
2886
|
}
|
|
2704
|
-
const offeredToolNames = new Set(((
|
|
2887
|
+
const offeredToolNames = new Set(((_c = options === null || options === void 0 ? void 0 : options.tools) !== null && _c !== void 0 ? _c : []).map((t) => t.name));
|
|
2705
2888
|
const schemaCoexistsWithTools = this.model.startsWith("gemini-3");
|
|
2706
2889
|
const applyResponseSchema = (options === null || options === void 0 ? void 0 : options.responseSchema) != null && (!tools || schemaCoexistsWithTools);
|
|
2707
2890
|
const response = yield this.post(Object.assign({
|
|
@@ -2712,7 +2895,7 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2712
2895
|
toolConfig,
|
|
2713
2896
|
generationConfig
|
|
2714
2897
|
}, applyResponseSchema ? { responseSchema: toGeminiSchema(options.responseSchema) } : {}), options === null || options === void 0 ? void 0 : options.signal);
|
|
2715
|
-
return this.fromGeminiResponse(response, offeredToolNames);
|
|
2898
|
+
return this.fromGeminiResponse(response, offeredToolNames, generationConfig.maxOutputTokens);
|
|
2716
2899
|
});
|
|
2717
2900
|
}
|
|
2718
2901
|
/**
|
|
@@ -2740,7 +2923,7 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2740
2923
|
return costUsd;
|
|
2741
2924
|
}
|
|
2742
2925
|
toGeminiContents(history, userMessage, attachments) {
|
|
2743
|
-
var _a, _b, _c;
|
|
2926
|
+
var _a, _b, _c, _d;
|
|
2744
2927
|
const contents = [];
|
|
2745
2928
|
const toolCallNameById = /* @__PURE__ */ new Map();
|
|
2746
2929
|
for (const msg of history) {
|
|
@@ -2760,7 +2943,16 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2760
2943
|
}
|
|
2761
2944
|
]
|
|
2762
2945
|
});
|
|
2763
|
-
|
|
2946
|
+
const resultImages = (_b = msg.toolResult.attachments) !== null && _b !== void 0 ? _b : [];
|
|
2947
|
+
if (resultImages.length) {
|
|
2948
|
+
contents.push({
|
|
2949
|
+
role: "user",
|
|
2950
|
+
parts: resultImages.map((att) => ({
|
|
2951
|
+
inlineData: { mimeType: normalizeImageMime(att.mimeType), data: att.data }
|
|
2952
|
+
}))
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
} else if ((_c = msg.toolCalls) === null || _c === void 0 ? void 0 : _c.length) {
|
|
2764
2956
|
for (const tc of msg.toolCalls) {
|
|
2765
2957
|
toolCallNameById.set(tc.id, tc.name);
|
|
2766
2958
|
}
|
|
@@ -2779,22 +2971,22 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
2779
2971
|
return part;
|
|
2780
2972
|
})
|
|
2781
2973
|
});
|
|
2782
|
-
} else if (role === "user" && ((
|
|
2783
|
-
const
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2974
|
+
} else if (role === "user" && ((_d = msg.attachments) === null || _d === void 0 ? void 0 : _d.length)) {
|
|
2975
|
+
const { imageParts: imageParts2, textParts: textParts2 } = splitAttachmentParts(msg.attachments);
|
|
2976
|
+
contents.push({
|
|
2977
|
+
role: "user",
|
|
2978
|
+
parts: [...imageParts2, ...msg.content ? [{ text: msg.content }] : [], ...textParts2]
|
|
2979
|
+
});
|
|
2788
2980
|
} else {
|
|
2789
2981
|
contents.push({ role, parts: [{ text: msg.content }] });
|
|
2790
2982
|
}
|
|
2791
2983
|
}
|
|
2792
|
-
const
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2984
|
+
const { imageParts, textParts } = splitAttachmentParts(attachments !== null && attachments !== void 0 ? attachments : []);
|
|
2985
|
+
if (userMessage || imageParts.length || textParts.length) {
|
|
2986
|
+
contents.push({
|
|
2987
|
+
role: "user",
|
|
2988
|
+
parts: [...imageParts, ...userMessage ? [{ text: userMessage }] : [], ...textParts]
|
|
2989
|
+
});
|
|
2798
2990
|
}
|
|
2799
2991
|
return contents;
|
|
2800
2992
|
}
|
|
@@ -2831,7 +3023,7 @@ ${att.content}`
|
|
|
2831
3023
|
cost
|
|
2832
3024
|
};
|
|
2833
3025
|
}
|
|
2834
|
-
fromGeminiResponse(response, offeredToolNames = /* @__PURE__ */ new Set()) {
|
|
3026
|
+
fromGeminiResponse(response, offeredToolNames = /* @__PURE__ */ new Set(), effectiveMaxTokens) {
|
|
2835
3027
|
var _a, _b, _c, _d;
|
|
2836
3028
|
const { inputTokens, outputTokens, thoughtsTokens, cacheReadTokens, cost } = this.usageFromGemini(response.usageMetadata);
|
|
2837
3029
|
const candidates = response === null || response === void 0 ? void 0 : response.candidates;
|
|
@@ -2878,7 +3070,7 @@ ${att.content}`
|
|
|
2878
3070
|
textParts: textParts.length
|
|
2879
3071
|
});
|
|
2880
3072
|
}
|
|
2881
|
-
this.guardMaxTokensTruncation(finishReason, toolCalls, narration, outputTokens);
|
|
3073
|
+
this.guardMaxTokensTruncation(finishReason, toolCalls, narration, outputTokens, effectiveMaxTokens);
|
|
2882
3074
|
if (inputTokens != null)
|
|
2883
3075
|
base.inputTokens = inputTokens;
|
|
2884
3076
|
if (outputTokens != null)
|
|
@@ -2916,12 +3108,12 @@ ${att.content}`
|
|
|
2916
3108
|
* `responseMeta.finishReason`, for the caller to decide. Usage/cost is logged
|
|
2917
3109
|
* before this check runs, so the spent tokens stay accounted for.
|
|
2918
3110
|
*/
|
|
2919
|
-
guardMaxTokensTruncation(finishReason, toolCalls, narration, outputTokens) {
|
|
3111
|
+
guardMaxTokensTruncation(finishReason, toolCalls, narration, outputTokens, effectiveMaxTokens) {
|
|
2920
3112
|
if (finishReason !== "MAX_TOKENS")
|
|
2921
3113
|
return;
|
|
2922
3114
|
if (toolCalls.length === 0 && narration.trim() !== "")
|
|
2923
3115
|
return;
|
|
2924
|
-
throw new ResponseTruncatedError(this.model,
|
|
3116
|
+
throw new ResponseTruncatedError(this.model, effectiveMaxTokens, outputTokens, toolCalls.map((tc) => tc.name));
|
|
2925
3117
|
}
|
|
2926
3118
|
/**
|
|
2927
3119
|
* Log the full shape of a blank or non-STOP response so its cause is legible
|
|
@@ -3482,11 +3674,12 @@ function triggerReason(trigger) {
|
|
|
3482
3674
|
return "superseded";
|
|
3483
3675
|
}
|
|
3484
3676
|
}
|
|
3485
|
-
function condenseStub(target, tool, trigger, origLen, restorable) {
|
|
3677
|
+
function condenseStub(target, tool, trigger, origLen, restorable, imageCount = 0) {
|
|
3486
3678
|
const what = target === "args" ? "args" : "result";
|
|
3487
3679
|
const key = trigger.kind === "superseded" ? ` ${trigger.by}` : "";
|
|
3488
3680
|
const restore = restorable ? "; re-call to restore" : "";
|
|
3489
|
-
|
|
3681
|
+
const images = imageCount > 0 ? ` + ${imageCount} image${imageCount === 1 ? "" : "s"}` : "";
|
|
3682
|
+
return `[${tool}${key} \u2014 ${what} elided, ~${origLen} chars${images} (${triggerReason(trigger)})${restore}]`;
|
|
3490
3683
|
}
|
|
3491
3684
|
function triggerLabel(trigger) {
|
|
3492
3685
|
switch (trigger.kind) {
|
|
@@ -3585,12 +3778,14 @@ function applyCondensation(history, policies, ctx, onCondensed) {
|
|
|
3585
3778
|
}
|
|
3586
3779
|
if (msg.toolResult) {
|
|
3587
3780
|
const entry = policies.get(msg.toolResult.toolCallId);
|
|
3588
|
-
const
|
|
3781
|
+
const textLen = msg.toolResult.content?.length ?? 0;
|
|
3782
|
+
const images = msg.toolResult.attachments ?? [];
|
|
3783
|
+
const origLen = textLen + images.reduce((n, att) => n + (att.data?.length ?? 0), 0);
|
|
3589
3784
|
const fired = entry?.policy.response ? firstFired(msg.toolResult.toolCallId, entry) : void 0;
|
|
3590
3785
|
if (entry && fired && origLen >= CONDENSE_MIN_CHARS) {
|
|
3591
3786
|
const tool = nameById.get(msg.toolResult.toolCallId) ?? msg.toolResult.toolCallId;
|
|
3592
3787
|
const result = entry.policy.response;
|
|
3593
|
-
const content = result === "drop" || result === "pointer" ? condenseStub("response", tool, fired,
|
|
3788
|
+
const content = result === "drop" || result === "pointer" ? condenseStub("response", tool, fired, textLen, result === "pointer", images.length) : result.replaceWith;
|
|
3594
3789
|
if (!entry.reportedResponse) {
|
|
3595
3790
|
entry.reportedResponse = true;
|
|
3596
3791
|
onCondensed({
|
|
@@ -3603,7 +3798,8 @@ function applyCondensation(history, policies, ctx, onCondensed) {
|
|
|
3603
3798
|
tokensSaved: estimateTokensSaved(origLen, content.length)
|
|
3604
3799
|
});
|
|
3605
3800
|
}
|
|
3606
|
-
|
|
3801
|
+
const { attachments: _elided, ...restOfResult } = msg.toolResult;
|
|
3802
|
+
return { ...msg, toolResult: { ...restOfResult, content } };
|
|
3607
3803
|
}
|
|
3608
3804
|
}
|
|
3609
3805
|
return msg;
|
|
@@ -3616,9 +3812,10 @@ function maskToolPayload(msg) {
|
|
|
3616
3812
|
return { ...msg, toolCalls: msg.toolCalls.map((tc) => ({ ...tc, args: {} })) };
|
|
3617
3813
|
}
|
|
3618
3814
|
if (msg.toolResult) {
|
|
3815
|
+
const { attachments: _masked, ...restOfResult } = msg.toolResult;
|
|
3619
3816
|
return {
|
|
3620
3817
|
...msg,
|
|
3621
|
-
toolResult: { ...
|
|
3818
|
+
toolResult: { ...restOfResult, content: "[other agent's tool result omitted]" }
|
|
3622
3819
|
};
|
|
3623
3820
|
}
|
|
3624
3821
|
return msg;
|
|
@@ -3772,6 +3969,15 @@ function accumulate(messages, into) {
|
|
|
3772
3969
|
var TOOL_FOLD_SYMBOL = Symbol("toolFold");
|
|
3773
3970
|
|
|
3774
3971
|
// src/components/chat-driver/chat-driver.ts
|
|
3972
|
+
var toolResultImages = (result) => {
|
|
3973
|
+
if (!result || typeof result !== "object") return void 0;
|
|
3974
|
+
const candidate = result.attachments;
|
|
3975
|
+
if (!Array.isArray(candidate) || candidate.length === 0) return void 0;
|
|
3976
|
+
const allImages = candidate.every(
|
|
3977
|
+
(att) => !!att && typeof att === "object" && att.kind === "image"
|
|
3978
|
+
);
|
|
3979
|
+
return allImages ? candidate : void 0;
|
|
3980
|
+
};
|
|
3775
3981
|
var providerRefusedDetailOf = (e) => ({
|
|
3776
3982
|
vendorLabel: e.vendorLabel,
|
|
3777
3983
|
kind: e.kind,
|
|
@@ -5902,10 +6108,12 @@ ${tailBody}
|
|
|
5902
6108
|
const capturedTrace = () => traceCapture.traces.length ? traceCapture.traces.flat() : void 0;
|
|
5903
6109
|
try {
|
|
5904
6110
|
const result = await handler(tc.args, this.buildHandlerContext(tc.id, traceCapture));
|
|
5905
|
-
const
|
|
6111
|
+
const resultImages = toolResultImages(result);
|
|
6112
|
+
const content = resultImages ? typeof result.content === "string" ? result.content : JSON.stringify({ ...result, attachments: void 0 }) : typeof result === "string" ? result : JSON.stringify(result);
|
|
5906
6113
|
executedById.set(tc.id, {
|
|
5907
6114
|
toolCallId: tc.id,
|
|
5908
6115
|
content,
|
|
6116
|
+
...resultImages ? { attachments: resultImages } : {},
|
|
5909
6117
|
// Concatenated when a handler invoked several children, so none is lost.
|
|
5910
6118
|
// Stays `undefined` when nothing was captured — readers key off presence,
|
|
5911
6119
|
// and an empty array is a different claim from "no sub-agent ran".
|
|
@@ -5954,7 +6162,14 @@ RECOVERY: this tool failed once \u2014 you may retry it, or take a different val
|
|
|
5954
6162
|
this.appendToHistory({
|
|
5955
6163
|
role: "tool",
|
|
5956
6164
|
content: "",
|
|
5957
|
-
toolResult: {
|
|
6165
|
+
toolResult: {
|
|
6166
|
+
toolCallId: r.toolCallId,
|
|
6167
|
+
content: r.content,
|
|
6168
|
+
// Images DO persist, unlike the sub-agent trace above: they are the payload
|
|
6169
|
+
// the model must see on every replay of this turn, not a one-off UI artefact.
|
|
6170
|
+
// `condenseWhen` is what sheds them once they are spent — see condense-history.
|
|
6171
|
+
...r.attachments ? { attachments: r.attachments } : {}
|
|
6172
|
+
}
|
|
5958
6173
|
});
|
|
5959
6174
|
}
|
|
5960
6175
|
}
|