bare-agent 0.49.0 → 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.
@@ -976,6 +976,8 @@ All return `{ text, toolCalls, usage: { inputTokens, outputTokens }, model?, cos
976
976
 
977
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.
978
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
+
979
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.
980
982
 
981
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-agent",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "files": [
5
5
  "index.js",
6
6
  "index.d.ts",
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?: {name: string|undefined, error: string, rawArguments?: string, rawTruncated?: true}, temperatureDropped?: boolean}>}
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?: {name: string|undefined, error: string, rawArguments?: string, rawTruncated?: true}, temperatureDropped?: boolean}>}
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?: {name: string|undefined, error: string, rawArguments?: string, rawTruncated?: true}, temperatureDropped?: boolean}>}
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?: {name: string|undefined, error: string, rawArguments?: string, rawTruncated?: true}, temperatureDropped?: boolean}>}
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 });
@@ -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, capped at 500
19
- * chars, `rawTruncated:true` when longer) to `malformedToolCall.rawArguments`. Off by default,
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, capped at 500
36
- * chars, `rawTruncated:true` when longer) to `malformedToolCall.rawArguments`. Off by default,
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 {
@@ -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, capped at 500
27
- * chars, `rawTruncated:true` when longer) to `malformedToolCall.rawArguments`. Off by default,
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
 
@@ -40,10 +40,9 @@ 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, capped at 500 chars, `rawTruncated:true`
44
- * when longer) to `malformedToolCall.rawArguments`. Off by default, independent of `exposeErrorBody` —
45
- * the raw blob is model-generated, not an upstream error body, but the same "don't leak an unbounded
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;
49
48
  /**
@@ -86,10 +85,9 @@ export type OpenAIOptions = {
86
85
  * Set `true` for an OpenAI-compatible server that only understands the legacy key (e.g. some
87
86
  * self-hosted / proxy endpoints). No model-name sniffing — the caller declares the dialect.
88
87
  * @property {boolean} [exposeMalformedArgs=false] - BA-27 follow-up: when a tool call's `function.arguments`
89
- * JSON fails to parse, also attach the raw string (verbatim, capped at 500 chars, `rawTruncated:true`
90
- * when longer) to `malformedToolCall.rawArguments`. Off by default, independent of `exposeErrorBody` —
91
- * the raw blob is model-generated, not an upstream error body, but the same "don't leak an unbounded
92
- * unexpected string into logs by default" reasoning applies. See `src/provider-toolcalls.js`.
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`.
93
91
  * @property {any} [thinking] - Opt-in `thinking` request field (fwdloop DeepSeek ask — extends BA-7(b)'s Anthropic option), forwarded to `body.thinking`
94
92
  * VERBATIM and unvalidated — e.g. `{ type: 'disabled' }`. Generalises the Anthropic provider's option to
95
93
  * OpenAI-compatible endpoints. Motivating case: DeepSeek's OpenAI-compatible API (`baseUrl`
@@ -73,10 +73,9 @@ 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, capped at 500 chars, `rawTruncated:true`
77
- * when longer) to `malformedToolCall.rawArguments`. Off by default, independent of `exposeErrorBody` —
78
- * the raw blob is model-generated, not an upstream error body, but the same "don't leak an unbounded
79
- * unexpected string into logs by default" reasoning applies. See `src/provider-toolcalls.js`.
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`.
80
79
  * @property {any} [thinking] - Opt-in `thinking` request field (fwdloop DeepSeek ask — extends BA-7(b)'s Anthropic option), forwarded to `body.thinking`
81
80
  * VERBATIM and unvalidated — e.g. `{ type: 'disabled' }`. Generalises the Anthropic provider's option to
82
81
  * OpenAI-compatible endpoints. Motivating case: DeepSeek's OpenAI-compatible API (`baseUrl`
@@ -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, capped at 500 chars (`rawTruncated:
20
- * true` added only when it was longer). Non-string raw arguments (e.g. Ollama's well-formed object
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. Off by default for the same reason as `exposeErrorBody`: an arbitrary model-generated string
24
- * landing in a log/audit row unbounded is its own leak surface.
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, rawTruncated?: true } }}
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, capped at 500 chars (`rawTruncated:
28
- * true` added only when it was longer). Non-string raw arguments (e.g. Ollama's well-formed object
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. Off by default for the same reason as `exposeErrorBody`: an arbitrary model-generated string
32
- * landing in a log/audit row unbounded is its own leak surface.
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, rawTruncated?: true } }}
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, rawTruncated?: true }} */
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.slice(0, RAW_ARGUMENTS_MAX_CHARS);
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`/`rawTruncated` (BA-27 follow-up) are present only when the provider was
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, capped at 500 chars, with
149
- * `rawTruncated: true` added only when it was longer.
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; rawTruncated?: true };
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