bare-agent 0.48.1 → 0.50.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/bareagent.context.md +10 -0
- package/package.json +1 -1
- package/src/loop.d.ts +4 -14
- package/src/loop.js +2 -2
- package/src/provider-anthropic.d.ts +4 -0
- package/src/provider-anthropic.js +2 -0
- package/src/provider-fallback.d.ts +5 -0
- package/src/provider-fallback.js +5 -0
- package/src/provider-ollama.d.ts +4 -4
- package/src/provider-ollama.js +2 -2
- package/src/provider-openai.d.ts +31 -9
- package/src/provider-openai.js +22 -5
- package/src/provider-toolcalls.d.ts +7 -6
- package/src/provider-toolcalls.js +8 -13
- package/types/index.d.ts +7 -4
package/bareagent.context.md
CHANGED
|
@@ -505,6 +505,14 @@ The blocks now ride the transcript on `Message.providerBlocks` (`{provider, mode
|
|
|
505
505
|
|
|
506
506
|
`AnthropicProvider({ thinking })` is a separate, opt-in knob, forwarded to `body.thinking` verbatim (e.g. `{type:'adaptive', display:'summarized'}` to surface the reasoning; the default `display` is `'omitted'`). **It does not "turn thinking on"** — it's already on. Use it to pin the mode or reach `display`/`effort`. It is passed through unvalidated on purpose: `budget_tokens` was removed from the API and now **400s** on sonnet-5/Opus 4.7+, so a library that reshaped this parameter would need a release every time Anthropic moved.
|
|
507
507
|
|
|
508
|
+
`OpenAIProvider({ thinking })` is the OpenAI-compatible counterpart: same verbatim/unvalidated forwarding to `body.thinking`, overridable per call (`loop.run(msgs, tools, { thinking })` / `generate(..., { thinking })`), and per-call `null` suppresses the constructor default. Needed for DeepSeek, which runs thinking mode by default and 400s "Thinking mode does not support this tool_choice" on a forced `toolChoice`:
|
|
509
|
+
|
|
510
|
+
```js
|
|
511
|
+
new OpenAIProvider({ baseUrl: 'https://api.deepseek.com', model: 'deepseek-flash', legacyMaxTokens: true, thinking: { type: 'disabled' } })
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
> **Fallback caveat:** per-call options (incl. `thinking`) are forwarded to EVERY provider in a `FallbackProvider` chain. For a mixed-vendor chain set `thinking` on each provider's constructor, not per-call.
|
|
515
|
+
|
|
508
516
|
> **Honesty note, and we mean it.** This is a **protocol/data-loss fix, not a capability fix.** A head-to-head with thinking fully preserved vs. stock bare-agent produced **indistinguishable** outcomes. Do not adopt 0.27 expecting better reasoning — you will not get it, and we have the measurement.
|
|
509
517
|
|
|
510
518
|
Legacy `wrapTool` / `wrapTools` are retained as deprecation shims (one-shot console warning, removal in 1.0). Migration: replace `wrapTools(tools)` at `loop.run()` with `filterTools(tools)` once upfront + `onLlmResult` / `onToolResult` on `new Loop({...})` to pick up LLM-cost recording and `_ctx` threading.
|
|
@@ -968,6 +976,8 @@ All return `{ text, toolCalls, usage: { inputTokens, outputTokens }, model?, cos
|
|
|
968
976
|
|
|
969
977
|
**Error body (v0.11.0):** on an HTTP error the OpenAI/Anthropic/Ollama providers throw a `ProviderError` whose `message` carries the upstream error string. The full parsed response is **not** attached to `err.body` by default (so an unexpected field can't leak through logs that dump the error object). Pass `{ exposeErrorBody: true }` to attach it for debugging.
|
|
970
978
|
|
|
979
|
+
**Malformed tool-call arguments (BA-27):** when an OpenAI-compatible or Ollama model emits `function.arguments` that is not valid JSON, the provider returns `toolCalls: []` plus a `malformedToolCall: { name, error }` marker (round still metered; JSON never repaired); `Loop.run()` surfaces the same marker on its result. Pass `{ exposeMalformedArgs: true }` (OpenAI/Ollama; default off, independent of `exposeErrorBody`) to also get `malformedToolCall.rawArguments` — the FULL raw arguments string, verbatim and uncapped (since 0.50.0; earlier it was the first 500 chars plus `rawTruncated`, which hid the broken tail). Model-generated and possibly large or sensitive: bare-agent only returns it to you and never logs it, but anything you forward it to (logs, audit rows) is your call. `rawTruncated` is deprecated and never set.
|
|
980
|
+
|
|
971
981
|
**Request/idle timeout (BA-18, v0.34.0):** the four http(s) providers (Anthropic, OpenAI, Gemini, Ollama) accept a `timeoutMs` option — constructor default **600000 (10 min)**, overridable per call via `generate(..., { timeoutMs })`, and `0`/`Infinity` disables it. Before this they wired only `req.on('error')`, so a socket the server silently dropped — or a response that never starts — hung `generate()` until the OS TCP timeout (~2h): a hang, not an error, so retry/casualty policy above it never fired. `timeoutMs` bounds on socket **inactivity** (`req.setTimeout`), so a slow-but-streaming response is not killed — only a silent/never-answering socket trips it; the 10-min default clears any single non-streaming completion (TTFB ≈ generation time). On trip, `generate()` rejects with a retryable `TimeoutError` (`code: 'ETIMEDOUT'`, `context.bound: 'idle'`, `retryable: true`). **Retry is caller-side and already wired:** `new Loop({ provider, retry: new Retry() })` wraps `provider.generate`, and `DEFAULT_RETRY_ON` classifies `ETIMEDOUT` (and `ECONNRESET`/`ENOTFOUND`/429/5xx) as transient — so a wired `Retry` retries a timed-out request and rethrows under `retryOn: () => false`, with no extra wiring (`run-plan`'s `stepRetry` is a second consumer of the same seam). CLIPipe already bounded its child process (`timeout`, default 30000 for one-shot) and is unchanged.
|
|
972
982
|
|
|
973
983
|
**Total call-duration deadline (BA-19, v0.35.0):** `timeoutMs` bounds socket *inactivity*, and `req.setTimeout` resets on any activity by design — so a "zombie stream" that trickles a byte forever (bytes arriving, the response never completing) never trips it and hangs the caller for hours (an adopter saw one `generate()` run **274 min** and end in `ECONNRESET`, not a `TimeoutError`: the reset proves bytes *were* flowing, so the idle timer never fired). The four http(s) providers now also accept a `deadlineMs` option — an absolute, **non-resetting** wall-clock ceiling on the whole request. **Disabled by default** (a deliberately long single call — large `maxTokens`, slow model — is legitimate; a default here would kill it), overridable per call via `generate(..., { deadlineMs })`, `0`/`Infinity` disable. An *unset* deadline resolves to disabled, but an *explicitly-set* garbage value (`NaN`, a non-numeric string) throws a `ValidationError` at resolve time rather than silently disabling the bound and running unbounded — unlike `timeoutMs`, the deadline has no safe default to fall back to, so a config mistake must surface loudly. On trip, `generate()` rejects with a **terminal** `TimeoutError` distinguishable from the idle trip: `code: 'EDEADLINE'`, `context.bound: 'deadline'`, `retryable: false` — a deadline is a hard ceiling meant to STOP, so it is *not* auto-retried (retrying would re-spend up to another full `deadlineMs`); a consumer that wants retry opts in via `retryOn`. When both are armed and `timeoutMs < deadlineMs`, a silent socket trips the idle bound first; only a still-active-but-never-completing stream reaches the deadline. The idle bound (BA-18) and the deadline (BA-19) are two independent failure modes — a silent socket vs a zombie stream.
|
package/package.json
CHANGED
package/src/loop.d.ts
CHANGED
|
@@ -167,7 +167,7 @@ export class Loop {
|
|
|
167
167
|
* thunk is re-evaluated each round (D4/eval-assist F2) so a tool set that grows mid-run — e.g. a skill
|
|
168
168
|
* unlocking its tools — is offered on the next round; a static array is resolved once at wire time.
|
|
169
169
|
* @param {Record<string, any>} [options={}] - Per-run overrides (system, temperature, ctx, etc.).
|
|
170
|
-
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, stopReason: string|null, model: string|null, msgs: Message[], metrics: RunMetrics, malformedToolCall?:
|
|
170
|
+
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, stopReason: string|null, model: string|null, msgs: Message[], metrics: RunMetrics, malformedToolCall?: GenerateResult['malformedToolCall'], temperatureDropped?: boolean}>}
|
|
171
171
|
* On halt the returned `error` is `halt:<rule>` (or `halt:unknown` if the
|
|
172
172
|
* thrown HaltError carried no `rule`), and `msgs` is sanitized so any
|
|
173
173
|
* dangling assistant `tool_calls` from the halted round are paired with
|
|
@@ -205,12 +205,7 @@ export class Loop {
|
|
|
205
205
|
model: string | null;
|
|
206
206
|
msgs: Message[];
|
|
207
207
|
metrics: RunMetrics;
|
|
208
|
-
malformedToolCall?:
|
|
209
|
-
name: string | undefined;
|
|
210
|
-
error: string;
|
|
211
|
-
rawArguments?: string;
|
|
212
|
-
rawTruncated?: true;
|
|
213
|
-
};
|
|
208
|
+
malformedToolCall?: GenerateResult["malformedToolCall"];
|
|
214
209
|
temperatureDropped?: boolean;
|
|
215
210
|
}>;
|
|
216
211
|
_warnedTruncated: boolean | undefined;
|
|
@@ -240,7 +235,7 @@ export class Loop {
|
|
|
240
235
|
* @param {string} text - User message.
|
|
241
236
|
* @param {ToolDef[]} [tools=[]] - Tool definitions.
|
|
242
237
|
* @param {Record<string, any>} [options={}] - Per-run overrides.
|
|
243
|
-
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, stopReason: string|null, model: string|null, msgs: Message[], metrics: RunMetrics, malformedToolCall?:
|
|
238
|
+
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, stopReason: string|null, model: string|null, msgs: Message[], metrics: RunMetrics, malformedToolCall?: GenerateResult['malformedToolCall'], temperatureDropped?: boolean}>}
|
|
244
239
|
*/
|
|
245
240
|
chat(text: string, tools?: ToolDef[], options?: Record<string, any>): Promise<{
|
|
246
241
|
text: string;
|
|
@@ -252,12 +247,7 @@ export class Loop {
|
|
|
252
247
|
model: string | null;
|
|
253
248
|
msgs: Message[];
|
|
254
249
|
metrics: RunMetrics;
|
|
255
|
-
malformedToolCall?:
|
|
256
|
-
name: string | undefined;
|
|
257
|
-
error: string;
|
|
258
|
-
rawArguments?: string;
|
|
259
|
-
rawTruncated?: true;
|
|
260
|
-
};
|
|
250
|
+
malformedToolCall?: GenerateResult["malformedToolCall"];
|
|
261
251
|
temperatureDropped?: boolean;
|
|
262
252
|
}>;
|
|
263
253
|
/**
|
package/src/loop.js
CHANGED
|
@@ -455,7 +455,7 @@ class Loop {
|
|
|
455
455
|
* thunk is re-evaluated each round (D4/eval-assist F2) so a tool set that grows mid-run — e.g. a skill
|
|
456
456
|
* unlocking its tools — is offered on the next round; a static array is resolved once at wire time.
|
|
457
457
|
* @param {Record<string, any>} [options={}] - Per-run overrides (system, temperature, ctx, etc.).
|
|
458
|
-
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, stopReason: string|null, model: string|null, msgs: Message[], metrics: RunMetrics, malformedToolCall?:
|
|
458
|
+
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, stopReason: string|null, model: string|null, msgs: Message[], metrics: RunMetrics, malformedToolCall?: GenerateResult['malformedToolCall'], temperatureDropped?: boolean}>}
|
|
459
459
|
* On halt the returned `error` is `halt:<rule>` (or `halt:unknown` if the
|
|
460
460
|
* thrown HaltError carried no `rule`), and `msgs` is sanitized so any
|
|
461
461
|
* dangling assistant `tool_calls` from the halted round are paired with
|
|
@@ -1328,7 +1328,7 @@ class Loop {
|
|
|
1328
1328
|
* @param {string} text - User message.
|
|
1329
1329
|
* @param {ToolDef[]} [tools=[]] - Tool definitions.
|
|
1330
1330
|
* @param {Record<string, any>} [options={}] - Per-run overrides.
|
|
1331
|
-
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, stopReason: string|null, model: string|null, msgs: Message[], metrics: RunMetrics, malformedToolCall?:
|
|
1331
|
+
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, stopReason: string|null, model: string|null, msgs: Message[], metrics: RunMetrics, malformedToolCall?: GenerateResult['malformedToolCall'], temperatureDropped?: boolean}>}
|
|
1332
1332
|
*/
|
|
1333
1333
|
async chat(text, tools = [], options = {}) {
|
|
1334
1334
|
this._history.push({ role: 'user', content: text });
|
|
@@ -24,6 +24,8 @@ export type AnthropicOptions = {
|
|
|
24
24
|
cacheMessages?: boolean | undefined;
|
|
25
25
|
/**
|
|
26
26
|
* - Opt-in extended thinking (BA-7), forwarded to `body.thinking` VERBATIM and unvalidated — e.g. `{ type: 'adaptive' }`, or `{ type: 'adaptive', display: 'summarized' }` to surface the reasoning (the default `display` is `'omitted'`). Deliberately opaque: this parameter has already broken once (`budget_tokens` was removed and now 400s on `claude-sonnet-5` / Opus 4.7+), and a library that reshapes it would need a release every time the API moves. Overridable per call via `generate(..., { thinking })`; pass `null` there to suppress an instance default.
|
|
27
|
+
* WARNING: per-call options (incl. `thinking`) are forwarded to EVERY provider in a `FallbackProvider` chain — for a mixed-vendor
|
|
28
|
+
* chain set `thinking` on each provider's CONSTRUCTOR instead of per-call.
|
|
27
29
|
*
|
|
28
30
|
* **MEASURED CAVEAT — this option does not "turn thinking on".** On `claude-sonnet-5` adaptive thinking is ALREADY the default: sending this changed the observed thinking rate not at all (2/10 rounds with it vs 3/10 without — `poc/ba7-adaptive-default.mjs`). Its real use is pinning the mode and reaching `display`/`effort`. The change that mattered is that thinking blocks are now PRESERVED and replayed (see `Message.providerBlocks`), which happens whether or not you ever set this.
|
|
29
31
|
*/
|
|
@@ -52,6 +54,8 @@ export type AnthropicOptions = {
|
|
|
52
54
|
* @property {boolean} [cacheSystem=false] - Opt-in prompt caching: send the system prompt with a `cache_control` breakpoint so Anthropic caches it. Anthropic does NOT auto-cache, so without this its cache tiers are always 0. Overridable per call via `generate(..., { cacheSystem })`. NOTE: on its own this rarely helps a tool loop — Anthropic's minimum cacheable prefix is 1024–4096 tokens (model-dependent) and a typical system persona is a few hundred, so it silently never caches. The transcript is where a tool loop's tokens actually live — see `cacheMessages`.
|
|
53
55
|
* @property {boolean} [cacheMessages=false] - Opt-in TRANSCRIPT caching (BA-1): roll a `cache_control` breakpoint onto the last content block of the last message, so Anthropic caches the whole conversation prefix and the loop stops re-buying it at full price every round. In a tool loop the transcript IS the tool results (file bodies from `shell_read`) and it always ENDS on one, which `_toAnthropicMessage` rebuilds from scratch — so no caller-side seam (`assemble` included) can reach it, and this has to live in the provider. Measured on `claude-sonnet-5` with a ~15k-token tool-result transcript (`poc/ba1-message-caching.mjs`): steady state **$0.0753 → $0.0110 per round, 6.8x cheaper**; round 1 pays a 1.25x cache WRITE once. Off by default — it changes the wire format, so adopters opt in. Overridable per call via `generate(..., { cacheMessages })`. **Interaction:** a destructive `trim`/stash fold that rewrites the transcript PREFIX invalidates the cache (the prefix is the cache key), so a fold must keep the head stable or you re-pay the write premium every round for nothing.
|
|
54
56
|
* @property {any} [thinking] - Opt-in extended thinking (BA-7), forwarded to `body.thinking` VERBATIM and unvalidated — e.g. `{ type: 'adaptive' }`, or `{ type: 'adaptive', display: 'summarized' }` to surface the reasoning (the default `display` is `'omitted'`). Deliberately opaque: this parameter has already broken once (`budget_tokens` was removed and now 400s on `claude-sonnet-5` / Opus 4.7+), and a library that reshapes it would need a release every time the API moves. Overridable per call via `generate(..., { thinking })`; pass `null` there to suppress an instance default.
|
|
57
|
+
* WARNING: per-call options (incl. `thinking`) are forwarded to EVERY provider in a `FallbackProvider` chain — for a mixed-vendor
|
|
58
|
+
* chain set `thinking` on each provider's CONSTRUCTOR instead of per-call.
|
|
55
59
|
*
|
|
56
60
|
* **MEASURED CAVEAT — this option does not "turn thinking on".** On `claude-sonnet-5` adaptive thinking is ALREADY the default: sending this changed the observed thinking rate not at all (2/10 rounds with it vs 3/10 without — `poc/ba7-adaptive-default.mjs`). Its real use is pinning the mode and reaching `display`/`effort`. The change that mattered is that thinking blocks are now PRESERVED and replayed (see `Message.providerBlocks`), which happens whether or not you ever set this.
|
|
57
61
|
* @property {boolean} [exposeErrorBody=false] - Attach the full upstream response to `err.body` on HTTP errors (off by default to avoid leaking unexpected fields through error logs; `err.message` still carries the API error).
|
|
@@ -30,6 +30,8 @@ function isLoopbackHost(hostname) {
|
|
|
30
30
|
* @property {boolean} [cacheSystem=false] - Opt-in prompt caching: send the system prompt with a `cache_control` breakpoint so Anthropic caches it. Anthropic does NOT auto-cache, so without this its cache tiers are always 0. Overridable per call via `generate(..., { cacheSystem })`. NOTE: on its own this rarely helps a tool loop — Anthropic's minimum cacheable prefix is 1024–4096 tokens (model-dependent) and a typical system persona is a few hundred, so it silently never caches. The transcript is where a tool loop's tokens actually live — see `cacheMessages`.
|
|
31
31
|
* @property {boolean} [cacheMessages=false] - Opt-in TRANSCRIPT caching (BA-1): roll a `cache_control` breakpoint onto the last content block of the last message, so Anthropic caches the whole conversation prefix and the loop stops re-buying it at full price every round. In a tool loop the transcript IS the tool results (file bodies from `shell_read`) and it always ENDS on one, which `_toAnthropicMessage` rebuilds from scratch — so no caller-side seam (`assemble` included) can reach it, and this has to live in the provider. Measured on `claude-sonnet-5` with a ~15k-token tool-result transcript (`poc/ba1-message-caching.mjs`): steady state **$0.0753 → $0.0110 per round, 6.8x cheaper**; round 1 pays a 1.25x cache WRITE once. Off by default — it changes the wire format, so adopters opt in. Overridable per call via `generate(..., { cacheMessages })`. **Interaction:** a destructive `trim`/stash fold that rewrites the transcript PREFIX invalidates the cache (the prefix is the cache key), so a fold must keep the head stable or you re-pay the write premium every round for nothing.
|
|
32
32
|
* @property {any} [thinking] - Opt-in extended thinking (BA-7), forwarded to `body.thinking` VERBATIM and unvalidated — e.g. `{ type: 'adaptive' }`, or `{ type: 'adaptive', display: 'summarized' }` to surface the reasoning (the default `display` is `'omitted'`). Deliberately opaque: this parameter has already broken once (`budget_tokens` was removed and now 400s on `claude-sonnet-5` / Opus 4.7+), and a library that reshapes it would need a release every time the API moves. Overridable per call via `generate(..., { thinking })`; pass `null` there to suppress an instance default.
|
|
33
|
+
* WARNING: per-call options (incl. `thinking`) are forwarded to EVERY provider in a `FallbackProvider` chain — for a mixed-vendor
|
|
34
|
+
* chain set `thinking` on each provider's CONSTRUCTOR instead of per-call.
|
|
33
35
|
*
|
|
34
36
|
* **MEASURED CAVEAT — this option does not "turn thinking on".** On `claude-sonnet-5` adaptive thinking is ALREADY the default: sending this changed the observed thinking rate not at all (2/10 rounds with it vs 3/10 without — `poc/ba7-adaptive-default.mjs`). Its real use is pinning the mode and reaching `display`/`effort`. The change that mattered is that thinking blocks are now PRESERVED and replayed (see `Message.providerBlocks`), which happens whether or not you ever set this.
|
|
35
37
|
* @property {boolean} [exposeErrorBody=false] - Attach the full upstream response to `err.body` on HTTP errors (off by default to avoid leaking unexpected fields through error logs; `err.message` still carries the API error).
|
|
@@ -21,6 +21,11 @@ export type FallbackOptions = {
|
|
|
21
21
|
* @property {(error: any, index: number) => boolean} [shouldFallback] - Return false to stop.
|
|
22
22
|
* @property {(error: any, fromIndex: number, toIndex: number) => void} [onFallback] - Callback.
|
|
23
23
|
*/
|
|
24
|
+
/**
|
|
25
|
+
* NOTE: per-call `generate()` options (incl. `thinking`, `toolChoice`) are forwarded UNCHANGED to EVERY provider in the chain. For a
|
|
26
|
+
* mixed-vendor chain (e.g. OpenAI-compat DeepSeek + Anthropic) set `thinking` on each provider's CONSTRUCTOR instead of per-call,
|
|
27
|
+
* or a value shaped for one vendor lands in another's `body.thinking` on fallback.
|
|
28
|
+
*/
|
|
24
29
|
export class FallbackProvider {
|
|
25
30
|
/**
|
|
26
31
|
* Provider that tries multiple providers in order.
|
package/src/provider-fallback.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* @property {(error: any, fromIndex: number, toIndex: number) => void} [onFallback] - Callback.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* NOTE: per-call `generate()` options (incl. `thinking`, `toolChoice`) are forwarded UNCHANGED to EVERY provider in the chain. For a
|
|
16
|
+
* mixed-vendor chain (e.g. OpenAI-compat DeepSeek + Anthropic) set `thinking` on each provider's CONSTRUCTOR instead of per-call,
|
|
17
|
+
* or a value shaped for one vendor lands in another's `body.thinking` on fallback.
|
|
18
|
+
*/
|
|
14
19
|
class FallbackProvider {
|
|
15
20
|
/**
|
|
16
21
|
* Provider that tries multiple providers in order.
|
package/src/provider-ollama.d.ts
CHANGED
|
@@ -15,8 +15,8 @@ export type OllamaOptions = {
|
|
|
15
15
|
deadlineMs?: number | undefined;
|
|
16
16
|
/**
|
|
17
17
|
* - BA-27 follow-up: when a tool call's string-shaped
|
|
18
|
-
* `function.arguments` JSON fails to parse, also attach the raw string (verbatim,
|
|
19
|
-
*
|
|
18
|
+
* `function.arguments` JSON fails to parse, also attach the FULL raw string (verbatim, uncapped)
|
|
19
|
+
* to `malformedToolCall.rawArguments` (model-generated; may be large or sensitive). Off by default,
|
|
20
20
|
* independent of `exposeErrorBody`. See `src/provider-toolcalls.js`.
|
|
21
21
|
*/
|
|
22
22
|
exposeMalformedArgs?: boolean | undefined;
|
|
@@ -32,8 +32,8 @@ export type OllamaOptions = {
|
|
|
32
32
|
* @property {number} [timeoutMs=600000] - BA-18: request/idle timeout in ms. Bounds a silent or never-answering socket on inactivity so `generate()` rejects with a retryable `TimeoutError` (`code: 'ETIMEDOUT'`, `context.bound: 'idle'`) instead of hanging until the OS TCP timeout. `0`/`Infinity` disables it. Overridable per call via `generate(..., { timeoutMs })`. (A local Ollama that is loading a large model cold can be slow to first byte — raise this or disable it for very large local models.)
|
|
33
33
|
* @property {number} [deadlineMs=0] - BA-19: TOTAL call-duration deadline in ms, beside `timeoutMs`. The idle bound resets on any socket activity, so a response that trickles a byte forever never trips it and hangs for hours. This is an absolute, non-resetting ceiling; on trip, `generate()` rejects with a TERMINAL `TimeoutError` (`code: 'EDEADLINE'`, `context.bound: 'deadline'`, `retryable: false`). DISABLED by default; `0`/`Infinity` disable. Overridable per call via `generate(..., { deadlineMs })`. (A cold large-model load is a legitimate long single call — leave this disabled or set it generously for such models.)
|
|
34
34
|
* @property {boolean} [exposeMalformedArgs=false] - BA-27 follow-up: when a tool call's string-shaped
|
|
35
|
-
* `function.arguments` JSON fails to parse, also attach the raw string (verbatim,
|
|
36
|
-
*
|
|
35
|
+
* `function.arguments` JSON fails to parse, also attach the FULL raw string (verbatim, uncapped)
|
|
36
|
+
* to `malformedToolCall.rawArguments` (model-generated; may be large or sensitive). Off by default,
|
|
37
37
|
* independent of `exposeErrorBody`. See `src/provider-toolcalls.js`.
|
|
38
38
|
*/
|
|
39
39
|
export class OllamaProvider {
|
package/src/provider-ollama.js
CHANGED
|
@@ -23,8 +23,8 @@ const OLLAMA_USAGE_KEYS = ['prompt_eval_count', 'eval_count'];
|
|
|
23
23
|
* @property {number} [timeoutMs=600000] - BA-18: request/idle timeout in ms. Bounds a silent or never-answering socket on inactivity so `generate()` rejects with a retryable `TimeoutError` (`code: 'ETIMEDOUT'`, `context.bound: 'idle'`) instead of hanging until the OS TCP timeout. `0`/`Infinity` disables it. Overridable per call via `generate(..., { timeoutMs })`. (A local Ollama that is loading a large model cold can be slow to first byte — raise this or disable it for very large local models.)
|
|
24
24
|
* @property {number} [deadlineMs=0] - BA-19: TOTAL call-duration deadline in ms, beside `timeoutMs`. The idle bound resets on any socket activity, so a response that trickles a byte forever never trips it and hangs for hours. This is an absolute, non-resetting ceiling; on trip, `generate()` rejects with a TERMINAL `TimeoutError` (`code: 'EDEADLINE'`, `context.bound: 'deadline'`, `retryable: false`). DISABLED by default; `0`/`Infinity` disable. Overridable per call via `generate(..., { deadlineMs })`. (A cold large-model load is a legitimate long single call — leave this disabled or set it generously for such models.)
|
|
25
25
|
* @property {boolean} [exposeMalformedArgs=false] - BA-27 follow-up: when a tool call's string-shaped
|
|
26
|
-
* `function.arguments` JSON fails to parse, also attach the raw string (verbatim,
|
|
27
|
-
*
|
|
26
|
+
* `function.arguments` JSON fails to parse, also attach the FULL raw string (verbatim, uncapped)
|
|
27
|
+
* to `malformedToolCall.rawArguments` (model-generated; may be large or sensitive). Off by default,
|
|
28
28
|
* independent of `exposeErrorBody`. See `src/provider-toolcalls.js`.
|
|
29
29
|
*/
|
|
30
30
|
|
package/src/provider-openai.d.ts
CHANGED
|
@@ -40,12 +40,24 @@ export type OpenAIOptions = {
|
|
|
40
40
|
legacyMaxTokens?: boolean | undefined;
|
|
41
41
|
/**
|
|
42
42
|
* - BA-27 follow-up: when a tool call's `function.arguments`
|
|
43
|
-
* JSON fails to parse, also attach the raw string (verbatim,
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* unexpected string into logs by default" reasoning applies. See `src/provider-toolcalls.js`.
|
|
43
|
+
* JSON fails to parse, also attach the FULL raw string (verbatim, uncapped) to `malformedToolCall.rawArguments`.
|
|
44
|
+
* Off by default, independent of `exposeErrorBody` — the raw blob is model-generated and may be large
|
|
45
|
+
* or sensitive, so it only reaches logs/audit rows if the caller opts in. See `src/provider-toolcalls.js`.
|
|
47
46
|
*/
|
|
48
47
|
exposeMalformedArgs?: boolean | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* - Opt-in `thinking` request field (fwdloop DeepSeek ask — extends BA-7(b)'s Anthropic option), forwarded to `body.thinking`
|
|
50
|
+
* VERBATIM and unvalidated — e.g. `{ type: 'disabled' }`. Generalises the Anthropic provider's option to
|
|
51
|
+
* OpenAI-compatible endpoints. Motivating case: DeepSeek's OpenAI-compatible API (`baseUrl`
|
|
52
|
+
* `https://api.deepseek.com`, model `deepseek-flash`) runs thinking mode by default and 400s "Thinking mode
|
|
53
|
+
* does not support this tool_choice" on a named or `'required'` `toolChoice`; `{ type: 'disabled' }` makes the
|
|
54
|
+
* forced tool call return 200. Deliberately opaque (no model sniffing, no free-form extra-body field) — a
|
|
55
|
+
* library that reshapes it would need a release every time a vendor moves. Unset ⇒ request body unchanged.
|
|
56
|
+
* Overridable per call via `generate(..., { thinking })`; pass `null` there to suppress an instance default.
|
|
57
|
+
* WARNING: per-call options (incl. `thinking`) are forwarded to EVERY provider in a `FallbackProvider` chain — for a mixed-vendor
|
|
58
|
+
* chain set `thinking` on each provider's CONSTRUCTOR instead of per-call.
|
|
59
|
+
*/
|
|
60
|
+
thinking?: any;
|
|
49
61
|
};
|
|
50
62
|
/**
|
|
51
63
|
* @typedef {object} OpenAIOptions
|
|
@@ -73,10 +85,19 @@ export type OpenAIOptions = {
|
|
|
73
85
|
* Set `true` for an OpenAI-compatible server that only understands the legacy key (e.g. some
|
|
74
86
|
* self-hosted / proxy endpoints). No model-name sniffing — the caller declares the dialect.
|
|
75
87
|
* @property {boolean} [exposeMalformedArgs=false] - BA-27 follow-up: when a tool call's `function.arguments`
|
|
76
|
-
* JSON fails to parse, also attach the raw string (verbatim,
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
88
|
+
* JSON fails to parse, also attach the FULL raw string (verbatim, uncapped) to `malformedToolCall.rawArguments`.
|
|
89
|
+
* Off by default, independent of `exposeErrorBody` — the raw blob is model-generated and may be large
|
|
90
|
+
* or sensitive, so it only reaches logs/audit rows if the caller opts in. See `src/provider-toolcalls.js`.
|
|
91
|
+
* @property {any} [thinking] - Opt-in `thinking` request field (fwdloop DeepSeek ask — extends BA-7(b)'s Anthropic option), forwarded to `body.thinking`
|
|
92
|
+
* VERBATIM and unvalidated — e.g. `{ type: 'disabled' }`. Generalises the Anthropic provider's option to
|
|
93
|
+
* OpenAI-compatible endpoints. Motivating case: DeepSeek's OpenAI-compatible API (`baseUrl`
|
|
94
|
+
* `https://api.deepseek.com`, model `deepseek-flash`) runs thinking mode by default and 400s "Thinking mode
|
|
95
|
+
* does not support this tool_choice" on a named or `'required'` `toolChoice`; `{ type: 'disabled' }` makes the
|
|
96
|
+
* forced tool call return 200. Deliberately opaque (no model sniffing, no free-form extra-body field) — a
|
|
97
|
+
* library that reshapes it would need a release every time a vendor moves. Unset ⇒ request body unchanged.
|
|
98
|
+
* Overridable per call via `generate(..., { thinking })`; pass `null` there to suppress an instance default.
|
|
99
|
+
* WARNING: per-call options (incl. `thinking`) are forwarded to EVERY provider in a `FallbackProvider` chain — for a mixed-vendor
|
|
100
|
+
* chain set `thinking` on each provider's CONSTRUCTOR instead of per-call.
|
|
80
101
|
*/
|
|
81
102
|
export class OpenAIProvider {
|
|
82
103
|
/**
|
|
@@ -95,11 +116,12 @@ export class OpenAIProvider {
|
|
|
95
116
|
deadlineMs: number | undefined;
|
|
96
117
|
legacyMaxTokens: boolean;
|
|
97
118
|
exposeMalformedArgs: boolean;
|
|
119
|
+
thinking: any;
|
|
98
120
|
/**
|
|
99
121
|
* Generate a response from the OpenAI API.
|
|
100
122
|
* @param {Message[]} messages - Conversation messages.
|
|
101
123
|
* @param {ToolDef[]} [tools=[]] - Tool definitions.
|
|
102
|
-
* @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, timeoutMs — a per-call override of the constructor's `timeoutMs`, see BA-18; deadlineMs — a per-call override of the constructor's `deadlineMs`, see BA-19; toolChoice — `'auto'` | `'required'` | `{ name }`, forwarded as OpenAI `tool_choice`, applied only when `tools` are present).
|
|
124
|
+
* @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, timeoutMs — a per-call override of the constructor's `timeoutMs`, see BA-18; deadlineMs — a per-call override of the constructor's `deadlineMs`, see BA-19; toolChoice — `'auto'` | `'required'` | `{ name }`, forwarded as OpenAI `tool_choice`, applied only when `tools` are present; thinking — per-call override of the constructor's `thinking`, `null` suppresses an instance default, see BA-7 (b)).
|
|
103
125
|
* @returns {Promise<GenerateResult>}
|
|
104
126
|
* @throws {Error} `[OpenAIProvider] ...` — on HTTP errors (4xx/5xx) or invalid JSON response.
|
|
105
127
|
*/
|
package/src/provider-openai.js
CHANGED
|
@@ -73,10 +73,19 @@ function isLoopbackHost(hostname) {
|
|
|
73
73
|
* Set `true` for an OpenAI-compatible server that only understands the legacy key (e.g. some
|
|
74
74
|
* self-hosted / proxy endpoints). No model-name sniffing — the caller declares the dialect.
|
|
75
75
|
* @property {boolean} [exposeMalformedArgs=false] - BA-27 follow-up: when a tool call's `function.arguments`
|
|
76
|
-
* JSON fails to parse, also attach the raw string (verbatim,
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
76
|
+
* JSON fails to parse, also attach the FULL raw string (verbatim, uncapped) to `malformedToolCall.rawArguments`.
|
|
77
|
+
* Off by default, independent of `exposeErrorBody` — the raw blob is model-generated and may be large
|
|
78
|
+
* or sensitive, so it only reaches logs/audit rows if the caller opts in. See `src/provider-toolcalls.js`.
|
|
79
|
+
* @property {any} [thinking] - Opt-in `thinking` request field (fwdloop DeepSeek ask — extends BA-7(b)'s Anthropic option), forwarded to `body.thinking`
|
|
80
|
+
* VERBATIM and unvalidated — e.g. `{ type: 'disabled' }`. Generalises the Anthropic provider's option to
|
|
81
|
+
* OpenAI-compatible endpoints. Motivating case: DeepSeek's OpenAI-compatible API (`baseUrl`
|
|
82
|
+
* `https://api.deepseek.com`, model `deepseek-flash`) runs thinking mode by default and 400s "Thinking mode
|
|
83
|
+
* does not support this tool_choice" on a named or `'required'` `toolChoice`; `{ type: 'disabled' }` makes the
|
|
84
|
+
* forced tool call return 200. Deliberately opaque (no model sniffing, no free-form extra-body field) — a
|
|
85
|
+
* library that reshapes it would need a release every time a vendor moves. Unset ⇒ request body unchanged.
|
|
86
|
+
* Overridable per call via `generate(..., { thinking })`; pass `null` there to suppress an instance default.
|
|
87
|
+
* WARNING: per-call options (incl. `thinking`) are forwarded to EVERY provider in a `FallbackProvider` chain — for a mixed-vendor
|
|
88
|
+
* chain set `thinking` on each provider's CONSTRUCTOR instead of per-call.
|
|
80
89
|
*/
|
|
81
90
|
|
|
82
91
|
class OpenAIProvider {
|
|
@@ -100,13 +109,16 @@ class OpenAIProvider {
|
|
|
100
109
|
this.legacyMaxTokens = options.legacyMaxTokens === true;
|
|
101
110
|
// BA-27 follow-up: expose the raw malformed tool-call arguments string. Off by default.
|
|
102
111
|
this.exposeMalformedArgs = options.exposeMalformedArgs === true;
|
|
112
|
+
// BA-7 (b) / fwdloop: forwarded to `body.thinking` VERBATIM — unvalidated, un-reshaped (mirrors the
|
|
113
|
+
// Anthropic provider). Null/unset ⇒ no `thinking` key, byte-identical to the pre-option body.
|
|
114
|
+
this.thinking = options.thinking != null ? options.thinking : null;
|
|
103
115
|
}
|
|
104
116
|
|
|
105
117
|
/**
|
|
106
118
|
* Generate a response from the OpenAI API.
|
|
107
119
|
* @param {Message[]} messages - Conversation messages.
|
|
108
120
|
* @param {ToolDef[]} [tools=[]] - Tool definitions.
|
|
109
|
-
* @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, timeoutMs — a per-call override of the constructor's `timeoutMs`, see BA-18; deadlineMs — a per-call override of the constructor's `deadlineMs`, see BA-19; toolChoice — `'auto'` | `'required'` | `{ name }`, forwarded as OpenAI `tool_choice`, applied only when `tools` are present).
|
|
121
|
+
* @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, timeoutMs — a per-call override of the constructor's `timeoutMs`, see BA-18; deadlineMs — a per-call override of the constructor's `deadlineMs`, see BA-19; toolChoice — `'auto'` | `'required'` | `{ name }`, forwarded as OpenAI `tool_choice`, applied only when `tools` are present; thinking — per-call override of the constructor's `thinking`, `null` suppresses an instance default, see BA-7 (b)).
|
|
110
122
|
* @returns {Promise<GenerateResult>}
|
|
111
123
|
* @throws {Error} `[OpenAIProvider] ...` — on HTTP errors (4xx/5xx) or invalid JSON response.
|
|
112
124
|
*/
|
|
@@ -121,6 +133,11 @@ class OpenAIProvider {
|
|
|
121
133
|
...(options.temperature != null && { temperature: options.temperature }),
|
|
122
134
|
...(options.maxTokens && { [maxTokensKey]: options.maxTokens }),
|
|
123
135
|
};
|
|
136
|
+
// BA-7 (b) / fwdloop: DeepSeek-style compat endpoints default to thinking mode and 400 on a forced
|
|
137
|
+
// tool_choice; the caller pins it (e.g. `{type:'disabled'}`). Set on `body` before the temperature
|
|
138
|
+
// fallback so the retry (which only deletes `temperature`) keeps it. Per-call `null` suppresses.
|
|
139
|
+
const thinking = options.thinking !== undefined ? options.thinking : this.thinking;
|
|
140
|
+
if (thinking) body.thinking = thinking;
|
|
124
141
|
// Ask 4 (fwdloop): validate the toolChoice SHAPE unconditionally so an invalid value ALWAYS throws
|
|
125
142
|
// (a silently-dropped force is the exact confusion this surfaces) — even when tools happen to be
|
|
126
143
|
// empty. Attach it only when tools are present: OpenAI 400s on a tool_choice with no tools, so a
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type ToolCall = import("../types").ToolCall;
|
|
2
|
+
/** @typedef {import('../types').ToolCall} ToolCall */
|
|
2
3
|
/**
|
|
3
4
|
* BA-27 — parse a round's raw tool calls into the neutral {@link ToolCall} shape WITHOUT throwing on
|
|
4
5
|
* malformed arguments.
|
|
@@ -16,17 +17,18 @@ export type ToolCall = import("../types").ToolCall;
|
|
|
16
17
|
* running half a decomposed intent, so one bad call voids the whole round's calls.
|
|
17
18
|
*
|
|
18
19
|
* `opts.exposeMalformedArgs` (default off, mirrors `exposeErrorBody`): when on, the marker also
|
|
19
|
-
* carries `rawArguments` — the raw `function.arguments` VERBATIM,
|
|
20
|
-
*
|
|
20
|
+
* carries `rawArguments` — the FULL raw `function.arguments` string VERBATIM, uncapped (malformed JSON
|
|
21
|
+
* usually breaks at the END — a stray trailing brace, a truncation — so a head-only cap hid exactly the
|
|
22
|
+
* broken part; `rawTruncated` is never set). Non-string raw arguments (e.g. Ollama's well-formed object
|
|
21
23
|
* case reaching this catch some other way) are never exposed — only a string is a model-generated
|
|
22
24
|
* blob worth surfacing for debugging; an object was already structurally valid before whatever else
|
|
23
|
-
* threw.
|
|
24
|
-
*
|
|
25
|
+
* threw. Still opt-in and off by default: the raw text is model-generated and may be large or carry
|
|
26
|
+
* sensitive content into logs/audit rows, so the caller must choose to accept that.
|
|
25
27
|
*
|
|
26
28
|
* @param {any[]} rawToolCalls - provider-native tool-call entries (may be undefined/empty)
|
|
27
29
|
* @param {(tc: any) => ToolCall} mapOne - maps one raw entry to a ToolCall; MAY throw on bad arguments
|
|
28
30
|
* @param {{ exposeMalformedArgs?: boolean }} [opts]
|
|
29
|
-
* @returns {{ toolCalls: ToolCall[], malformedToolCall?: { name: string|undefined, error: string, rawArguments?: string
|
|
31
|
+
* @returns {{ toolCalls: ToolCall[], malformedToolCall?: { name: string|undefined, error: string, rawArguments?: string } }}
|
|
30
32
|
*/
|
|
31
33
|
export function parseToolCalls(rawToolCalls: any[], mapOne: (tc: any) => ToolCall, opts?: {
|
|
32
34
|
exposeMalformedArgs?: boolean;
|
|
@@ -36,6 +38,5 @@ export function parseToolCalls(rawToolCalls: any[], mapOne: (tc: any) => ToolCal
|
|
|
36
38
|
name: string | undefined;
|
|
37
39
|
error: string;
|
|
38
40
|
rawArguments?: string;
|
|
39
|
-
rawTruncated?: true;
|
|
40
41
|
};
|
|
41
42
|
};
|
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
/** @typedef {import('../types').ToolCall} ToolCall */
|
|
4
4
|
|
|
5
|
-
// Raw-arguments exposure cap. Matches the sibling opt-in error-body exposure pattern
|
|
6
|
-
// (`exposeErrorBody`) elsewhere in the providers: off by default, and even when on, bounded —
|
|
7
|
-
// an arbitrarily long model-generated string reaching a log/receipt unbounded is its own risk.
|
|
8
|
-
const RAW_ARGUMENTS_MAX_CHARS = 500;
|
|
9
|
-
|
|
10
5
|
/**
|
|
11
6
|
* BA-27 — parse a round's raw tool calls into the neutral {@link ToolCall} shape WITHOUT throwing on
|
|
12
7
|
* malformed arguments.
|
|
@@ -24,17 +19,18 @@ const RAW_ARGUMENTS_MAX_CHARS = 500;
|
|
|
24
19
|
* running half a decomposed intent, so one bad call voids the whole round's calls.
|
|
25
20
|
*
|
|
26
21
|
* `opts.exposeMalformedArgs` (default off, mirrors `exposeErrorBody`): when on, the marker also
|
|
27
|
-
* carries `rawArguments` — the raw `function.arguments` VERBATIM,
|
|
28
|
-
*
|
|
22
|
+
* carries `rawArguments` — the FULL raw `function.arguments` string VERBATIM, uncapped (malformed JSON
|
|
23
|
+
* usually breaks at the END — a stray trailing brace, a truncation — so a head-only cap hid exactly the
|
|
24
|
+
* broken part; `rawTruncated` is never set). Non-string raw arguments (e.g. Ollama's well-formed object
|
|
29
25
|
* case reaching this catch some other way) are never exposed — only a string is a model-generated
|
|
30
26
|
* blob worth surfacing for debugging; an object was already structurally valid before whatever else
|
|
31
|
-
* threw.
|
|
32
|
-
*
|
|
27
|
+
* threw. Still opt-in and off by default: the raw text is model-generated and may be large or carry
|
|
28
|
+
* sensitive content into logs/audit rows, so the caller must choose to accept that.
|
|
33
29
|
*
|
|
34
30
|
* @param {any[]} rawToolCalls - provider-native tool-call entries (may be undefined/empty)
|
|
35
31
|
* @param {(tc: any) => ToolCall} mapOne - maps one raw entry to a ToolCall; MAY throw on bad arguments
|
|
36
32
|
* @param {{ exposeMalformedArgs?: boolean }} [opts]
|
|
37
|
-
* @returns {{ toolCalls: ToolCall[], malformedToolCall?: { name: string|undefined, error: string, rawArguments?: string
|
|
33
|
+
* @returns {{ toolCalls: ToolCall[], malformedToolCall?: { name: string|undefined, error: string, rawArguments?: string } }}
|
|
38
34
|
*/
|
|
39
35
|
function parseToolCalls(rawToolCalls, mapOne, opts = {}) {
|
|
40
36
|
const raw = rawToolCalls || [];
|
|
@@ -44,7 +40,7 @@ function parseToolCalls(rawToolCalls, mapOne, opts = {}) {
|
|
|
44
40
|
try {
|
|
45
41
|
toolCalls.push(mapOne(tc));
|
|
46
42
|
} catch (e) {
|
|
47
|
-
/** @type {{ name: string|undefined, error: string, rawArguments?: string
|
|
43
|
+
/** @type {{ name: string|undefined, error: string, rawArguments?: string }} */
|
|
48
44
|
const malformedToolCall = {
|
|
49
45
|
name: tc && tc.function ? tc.function.name : undefined,
|
|
50
46
|
error: e instanceof Error ? e.message : String(e),
|
|
@@ -52,8 +48,7 @@ function parseToolCalls(rawToolCalls, mapOne, opts = {}) {
|
|
|
52
48
|
if (opts.exposeMalformedArgs) {
|
|
53
49
|
const rawArgs = tc && tc.function ? tc.function.arguments : undefined;
|
|
54
50
|
if (typeof rawArgs === 'string') {
|
|
55
|
-
malformedToolCall.rawArguments = rawArgs
|
|
56
|
-
if (rawArgs.length > RAW_ARGUMENTS_MAX_CHARS) malformedToolCall.rawTruncated = true;
|
|
51
|
+
malformedToolCall.rawArguments = rawArgs;
|
|
57
52
|
}
|
|
58
53
|
}
|
|
59
54
|
return { toolCalls: [], malformedToolCall };
|
package/types/index.d.ts
CHANGED
|
@@ -143,12 +143,15 @@ export interface GenerateResult {
|
|
|
143
143
|
* repaired. Absent on a clean round. OpenAI-compatible + Ollama string-arguments only; Anthropic
|
|
144
144
|
* arrives pre-parsed and cannot hit this.
|
|
145
145
|
*
|
|
146
|
-
* `rawArguments
|
|
146
|
+
* `rawArguments` (BA-27 follow-up) is present only when the provider was
|
|
147
147
|
* constructed with `exposeMalformedArgs: true` (default off, mirrors `exposeErrorBody`) AND the
|
|
148
|
-
* raw `function.arguments` was a string: the raw value verbatim,
|
|
149
|
-
*
|
|
148
|
+
* raw `function.arguments` was a string: the FULL raw value verbatim, uncapped (model-generated; may be
|
|
149
|
+
* large or sensitive).
|
|
150
150
|
*/
|
|
151
|
-
malformedToolCall?: { name: string | undefined; error: string; rawArguments?: string;
|
|
151
|
+
malformedToolCall?: { name: string | undefined; error: string; rawArguments?: string;
|
|
152
|
+
/** @deprecated never set since 0.50.0 (rawArguments is now uncapped) */
|
|
153
|
+
rawTruncated?: true;
|
|
154
|
+
};
|
|
152
155
|
/**
|
|
153
156
|
* BA-7 — provider-native content blocks the normalized `{text, toolCalls}` shape cannot express
|
|
154
157
|
* (Anthropic `thinking` / `redacted_thinking`), captured opaquely so the Loop can put them on the
|