fix: return Error objects from invokeFunctionTool and create error invocations (#5351) * fix: return Error objects from invokeFunctionTool and create error invocations invokeFunctionTool previously called .toString() on caught errors, converting them to plain strings. This made the instanceof Error check in invokeFunctionTools dead code. Changes: - Return Error objects directly from invokeFunctionTool - Create error invocations with error: true flag when tools fail - Record failed stealth tools in stealthCalls - Preserve signature/reasoning on error invocations - Add error field to ToolInvocation typedef * fix: use Error.toString to avoid behavioral changes --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
Signed| @@ -21,11 +21,12 @@ import { isTrueBoolean } from './utils.js'; | |||
| 21 | * @property {string} result - The result of the tool invocation. | 21 | * @property {string} result - The result of the tool invocation. |
| 22 | * @property {string?} signature - The thought signature associated with the tool invocation. | 22 | * @property {string?} signature - The thought signature associated with the tool invocation. |
| 23 | * @property {string?} reasoning - The plaintext reasoning associated with this tool call turn. | 23 | * @property {string?} reasoning - The plaintext reasoning associated with this tool call turn. |
| 24 | * @property {boolean} [error] - Whether the tool invocation failed. | ||
| 24 | */ | 25 | */ |
| 25 | 26 | ||
| 26 | /** | 27 | /** |
| 27 | * @typedef {object} ToolInvocationResult | 28 | * @typedef {object} ToolInvocationResult |
| 28 | * @property {ToolInvocation[]} invocations Successful tool invocations | 29 | * @property {ToolInvocation[]} invocations Tool invocations (both successful and failed) |
| 29 | * @property {Error[]} errors Errors that occurred during tool invocation | 30 | * @property {Error[]} errors Errors that occurred during tool invocation |
| 30 | * @property {string[]} stealthCalls Names of stealth tools that were invoked | 31 | * @property {string[]} stealthCalls Names of stealth tools that were invoked |
| 31 | */ | 32 | */ |
| @@ -336,10 +337,10 @@ export class ToolManager { | |||
| 336 | 337 | ||
| 337 | if (error instanceof Error) { | 338 | if (error instanceof Error) { |
| 338 | error.cause = name; | 339 | error.cause = name; |
| 339 | return error.toString(); | 340 | return error; |
| 340 | } | 341 | } |
| 341 | 342 | ||
| 342 | return new Error('Unknown error occurred while invoking the tool.', { cause: name }).toString(); | 343 | return new Error('Unknown error occurred while invoking the tool.', { cause: name }); |
| 343 | } | 344 | } |
| 344 | } | 345 | } |
| 345 | 346 | ||
| @@ -796,9 +797,23 @@ export class ToolManager { | |||
| 796 | toastr.clear(toast); | 797 | toastr.clear(toast); |
| 797 | console.log('[ToolManager] Function tool result:', result); | 798 | console.log('[ToolManager] Function tool result:', result); |
| 798 | 799 | ||
| 799 | // Save a successful invocation | 800 | // Handle tool errors — still create an invocation so the LLM sees the failure |
| 800 | if (toolResult instanceof Error) { | 801 | if (toolResult instanceof Error) { |
| 801 | result.errors.push(toolResult); | 802 | result.errors.push(toolResult); |
| 803 | if (isStealth) { | ||
| 804 | result.stealthCalls.push(name); | ||
| 805 | } else { | ||
| 806 | result.invocations.push({ | ||
| 807 | id, | ||
| 808 | displayName, | ||
| 809 | name, | ||
| 810 | parameters: stringify(parameters), | ||
| 811 | result: toolResult.toString(), | ||
| 812 | error: true, | ||
| 813 | signature: toolCall.signature || null, | ||
| 814 | reasoning: reasoningText || null, | ||
| 815 | }); | ||
| 816 | } | ||
| 802 | continue; | 817 | continue; |
| 803 | } | 818 | } |
| 804 | 819 | ||
| @@ -814,6 +829,7 @@ export class ToolManager { | |||
| 814 | name, | 829 | name, |
| 815 | parameters: stringify(parameters), | 830 | parameters: stringify(parameters), |
| 816 | result: toolResult, | 831 | result: toolResult, |
| 832 | error: false, | ||
| 817 | signature: toolCall.signature || null, | 833 | signature: toolCall.signature || null, |
| 818 | reasoning: reasoningText || null, | 834 | reasoning: reasoningText || null, |
| 819 | }; | 835 | }; |