File size: 4,037 Bytes
7421850
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import { describe, it, expect } from 'vitest';
import { z } from 'zod';
import { SdkTool, tool, ModelVisibleError } from './tool.js';
import type { MessageBus } from '@google/gemini-cli-core';

// Mock MessageBus
const mockMessageBus = {} as unknown as MessageBus;

describe('tool()', () => {
  it('creates a tool definition with defaults', () => {
    const definition = tool(
      {
        name: 'testTool',
        description: 'A test tool',
        inputSchema: z.object({ foo: z.string() }),
      },
      async () => 'result',
    );

    expect(definition.name).toBe('testTool');
    expect(definition.description).toBe('A test tool');
    expect(definition.sendErrorsToModel).toBeUndefined();
  });

  it('creates a tool definition with explicit configuration', () => {
    const definition = tool(
      {
        name: 'testTool',
        description: 'A test tool',
        inputSchema: z.object({ foo: z.string() }),
        sendErrorsToModel: true,
      },
      async () => 'result',
    );

    expect(definition.sendErrorsToModel).toBe(true);
  });
});

describe('SdkTool Execution', () => {
  it('executes successfully', async () => {
    const definition = tool(
      {
        name: 'successTool',
        description: 'Always succeeds',
        inputSchema: z.object({ val: z.string() }),
      },
      async ({ val }) => `Success: ${val}`,
    );

    const sdkTool = new SdkTool(definition, mockMessageBus);
    const invocation = sdkTool.createInvocationWithContext(
      { val: 'test' },
      mockMessageBus,
      undefined,
    );
    const result = await invocation.execute({
      abortSignal: new AbortController().signal,
    });

    expect(result.llmContent).toBe('Success: test');
    expect(result.error).toBeUndefined();
  });

  it('throws standard Error by default', async () => {
    const definition = tool(
      {
        name: 'failTool',
        description: 'Always fails',
        inputSchema: z.object({}),
      },
      async () => {
        throw new Error('Standard error');
      },
    );

    const sdkTool = new SdkTool(definition, mockMessageBus);
    const invocation = sdkTool.createInvocationWithContext(
      {},
      mockMessageBus,
      undefined,
    );

    await expect(
      invocation.execute({ abortSignal: new AbortController().signal }),
    ).rejects.toThrow('Standard error');
  });

  it('catches ModelVisibleError and returns ToolResult error', async () => {
    const definition = tool(
      {
        name: 'visibleErrorTool',
        description: 'Fails with visible error',
        inputSchema: z.object({}),
      },
      async () => {
        throw new ModelVisibleError('Visible error');
      },
    );

    const sdkTool = new SdkTool(definition, mockMessageBus);
    const invocation = sdkTool.createInvocationWithContext(
      {},
      mockMessageBus,
      undefined,
    );
    const result = await invocation.execute({
      abortSignal: new AbortController().signal,
    });

    expect(result.error).toBeDefined();
    expect(result.error?.message).toBe('Visible error');
    expect(result.llmContent).toContain('Error: Visible error');
  });

  it('catches standard Error when sendErrorsToModel is true', async () => {
    const definition = tool(
      {
        name: 'catchAllTool',
        description: 'Catches all errors',
        inputSchema: z.object({}),
        sendErrorsToModel: true,
      },
      async () => {
        throw new Error('Standard error');
      },
    );

    const sdkTool = new SdkTool(definition, mockMessageBus);
    const invocation = sdkTool.createInvocationWithContext(
      {},
      mockMessageBus,
      undefined,
    );
    const result = await invocation.execute({
      abortSignal: new AbortController().signal,
    });

    expect(result.error).toBeDefined();
    expect(result.error?.message).toBe('Standard error');
    expect(result.llmContent).toContain('Error: Standard error');
  });
});