@inferencesh/sdk 0.6.15 → 0.6.19

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.
Files changed (85) hide show
  1. package/dist/agent/provider.d.ts +2 -1
  2. package/dist/api/apps.d.ts +4 -0
  3. package/dist/api/apps.js +6 -0
  4. package/dist/api/index.d.ts +3 -3
  5. package/dist/http/index.d.ts +3 -3
  6. package/dist/index.d.ts +9 -9
  7. package/dist/types.d.ts +30 -4
  8. package/dist/types.js +4 -0
  9. package/package.json +8 -5
  10. package/dist/agent/actions.test.d.ts +0 -1
  11. package/dist/agent/actions.test.js +0 -820
  12. package/dist/agent/api.test.d.ts +0 -1
  13. package/dist/agent/api.test.js +0 -246
  14. package/dist/agent/reducer.test.d.ts +0 -1
  15. package/dist/agent/reducer.test.js +0 -131
  16. package/dist/agent/types.test.d.ts +0 -1
  17. package/dist/agent/types.test.js +0 -75
  18. package/dist/api/agents.test.d.ts +0 -1
  19. package/dist/api/agents.test.js +0 -1014
  20. package/dist/api/api-keys.test.d.ts +0 -1
  21. package/dist/api/api-keys.test.js +0 -44
  22. package/dist/api/apps.test.d.ts +0 -1
  23. package/dist/api/apps.test.js +0 -135
  24. package/dist/api/chats.test.d.ts +0 -1
  25. package/dist/api/chats.test.js +0 -85
  26. package/dist/api/engines.test.d.ts +0 -1
  27. package/dist/api/engines.test.js +0 -133
  28. package/dist/api/files.test.d.ts +0 -1
  29. package/dist/api/files.test.js +0 -256
  30. package/dist/api/flow-runs.test.d.ts +0 -1
  31. package/dist/api/flow-runs.test.js +0 -97
  32. package/dist/api/flows.test.d.ts +0 -1
  33. package/dist/api/flows.test.js +0 -118
  34. package/dist/api/integrations.test.d.ts +0 -1
  35. package/dist/api/integrations.test.js +0 -109
  36. package/dist/api/knowledge.test.d.ts +0 -1
  37. package/dist/api/knowledge.test.js +0 -238
  38. package/dist/api/mcp-servers.test.d.ts +0 -1
  39. package/dist/api/mcp-servers.test.js +0 -98
  40. package/dist/api/projects.test.d.ts +0 -1
  41. package/dist/api/projects.test.js +0 -61
  42. package/dist/api/search.test.d.ts +0 -1
  43. package/dist/api/search.test.js +0 -50
  44. package/dist/api/secrets.test.d.ts +0 -1
  45. package/dist/api/secrets.test.js +0 -61
  46. package/dist/api/sessions.test.d.ts +0 -1
  47. package/dist/api/sessions.test.js +0 -61
  48. package/dist/api/tasks.test.d.ts +0 -1
  49. package/dist/api/tasks.test.js +0 -381
  50. package/dist/api/teams.test.d.ts +0 -1
  51. package/dist/api/teams.test.js +0 -139
  52. package/dist/client.test.d.ts +0 -1
  53. package/dist/client.test.js +0 -446
  54. package/dist/http/client.test.d.ts +0 -1
  55. package/dist/http/client.test.js +0 -435
  56. package/dist/http/errors.test.d.ts +0 -1
  57. package/dist/http/errors.test.js +0 -66
  58. package/dist/http/poll.test.d.ts +0 -1
  59. package/dist/http/poll.test.js +0 -146
  60. package/dist/http/streamable.test.d.ts +0 -4
  61. package/dist/http/streamable.test.js +0 -510
  62. package/dist/integration.test.d.ts +0 -9
  63. package/dist/integration.test.js +0 -107
  64. package/dist/proxy/express.test.d.ts +0 -1
  65. package/dist/proxy/express.test.js +0 -106
  66. package/dist/proxy/hono.test.d.ts +0 -1
  67. package/dist/proxy/hono.test.js +0 -90
  68. package/dist/proxy/index.test.d.ts +0 -1
  69. package/dist/proxy/index.test.js +0 -173
  70. package/dist/proxy/nextjs.test.d.ts +0 -1
  71. package/dist/proxy/nextjs.test.js +0 -125
  72. package/dist/proxy/remix.test.d.ts +0 -1
  73. package/dist/proxy/remix.test.js +0 -66
  74. package/dist/proxy/svelte.test.d.ts +0 -1
  75. package/dist/proxy/svelte.test.js +0 -69
  76. package/dist/sessions.integration.test.d.ts +0 -13
  77. package/dist/sessions.integration.test.js +0 -310
  78. package/dist/stream.test.d.ts +0 -1
  79. package/dist/stream.test.js +0 -363
  80. package/dist/streamable.integration.test.d.ts +0 -11
  81. package/dist/streamable.integration.test.js +0 -150
  82. package/dist/tool-builder.test.d.ts +0 -1
  83. package/dist/tool-builder.test.js +0 -407
  84. package/dist/utils.test.d.ts +0 -1
  85. package/dist/utils.test.js +0 -43
@@ -1,150 +0,0 @@
1
- /**
2
- * Integration tests for streamable HTTP client
3
- *
4
- * Tests NDJSON streaming against the real API.
5
- * Run with: INFERENCE_API_KEY=xxx npm run test:integration
6
- *
7
- * Note: Format negotiation tests may be skipped if server doesn't support NDJSON yet.
8
- *
9
- * @jest-environment node
10
- */
11
- import { streamable, StreamableManager } from './http/streamable';
12
- import { Inference, TaskStatusCompleted, TaskStatusFailed, TaskStatusCancelled } from './index';
13
- const isTerminalStatus = (status) => status === TaskStatusCompleted || status === TaskStatusFailed || status === TaskStatusCancelled;
14
- const API_KEY = process.env.INFERENCE_API_KEY;
15
- const BASE_URL = process.env.INFERENCE_BASE_URL || 'https://api.inference.sh';
16
- // Simple test app that completes quickly
17
- const TEST_APP = 'infsh/text-templating@53bk0yzk';
18
- const describeIfApiKey = API_KEY ? describe : describe.skip;
19
- describeIfApiKey('Streamable Integration Tests', () => {
20
- let client;
21
- beforeAll(() => {
22
- client = new Inference({
23
- apiKey: API_KEY,
24
- baseUrl: BASE_URL,
25
- });
26
- });
27
- describe('Format Negotiation', () => {
28
- it('should negotiate format based on Accept header', async () => {
29
- // Create a task using the SDK
30
- const task = await client.run({ app: TEST_APP, input: { template: 'Format {1}!', strings: ['Test'] } }, { wait: false });
31
- // Test NDJSON request
32
- const ndjsonRes = await fetch(`${BASE_URL}/tasks/${task.id}/stream`, {
33
- headers: {
34
- 'Authorization': `Bearer ${API_KEY}`,
35
- 'Accept': 'application/x-ndjson',
36
- },
37
- });
38
- expect(ndjsonRes.ok).toBe(true);
39
- const ndjsonContentType = ndjsonRes.headers.get('content-type');
40
- // Test SSE request
41
- const sseRes = await fetch(`${BASE_URL}/tasks/${task.id}/stream`, {
42
- headers: {
43
- 'Authorization': `Bearer ${API_KEY}`,
44
- 'Accept': 'text/event-stream',
45
- },
46
- });
47
- expect(sseRes.ok).toBe(true);
48
- const sseContentType = sseRes.headers.get('content-type');
49
- // Log what we got for debugging
50
- console.log('NDJSON request Content-Type:', ndjsonContentType);
51
- console.log('SSE request Content-Type:', sseContentType);
52
- // At minimum, SSE should work
53
- expect(sseContentType).toContain('text/event-stream');
54
- // If NDJSON is supported, it should return NDJSON content-type
55
- // Otherwise it falls back to SSE (server not yet updated)
56
- const supportsNDJSON = ndjsonContentType?.includes('application/x-ndjson');
57
- if (supportsNDJSON) {
58
- console.log('✓ Server supports NDJSON format negotiation');
59
- }
60
- else {
61
- console.log('⚠ Server does not yet support NDJSON, falling back to SSE');
62
- }
63
- // Clean up - consume the streams
64
- for (const res of [ndjsonRes, sseRes]) {
65
- const reader = res.body?.getReader();
66
- if (reader) {
67
- while (true) {
68
- const { done } = await reader.read();
69
- if (done)
70
- break;
71
- }
72
- }
73
- }
74
- }, 60000);
75
- });
76
- describe('streamable() with NDJSON server', () => {
77
- // These tests require the server to support NDJSON
78
- // Skip if server doesn't support it yet
79
- it('should stream task updates when server supports NDJSON', async () => {
80
- // Create a task using the SDK (fire and forget)
81
- const task = await client.run({ app: TEST_APP, input: { template: 'Hello {1}!', strings: ['Streamable'] } }, { wait: false });
82
- expect(task.id).toBeDefined();
83
- // Stream updates using streamable - will request NDJSON format
84
- const updates = [];
85
- try {
86
- for await (const update of streamable(`${BASE_URL}/tasks/${task.id}/stream`, {
87
- headers: { 'Authorization': `Bearer ${API_KEY}` },
88
- })) {
89
- updates.push(update);
90
- // Stop when task reaches terminal status
91
- if (isTerminalStatus(update.status))
92
- break;
93
- }
94
- }
95
- catch (err) {
96
- // If server doesn't support NDJSON, streamable will fail to parse SSE
97
- console.log('⚠ Skipping - server returned non-NDJSON:', err.message);
98
- return;
99
- }
100
- expect(updates.length).toBeGreaterThan(0);
101
- // Last update should be terminal
102
- const lastUpdate = updates[updates.length - 1];
103
- expect(isTerminalStatus(lastUpdate.status)).toBe(true);
104
- }, 60000);
105
- });
106
- describe('StreamableManager with NDJSON server', () => {
107
- it('should receive updates via callbacks when server supports NDJSON', async () => {
108
- // Create a task using the SDK
109
- const task = await client.run({ app: TEST_APP, input: { template: 'Manager {1}!', strings: ['Test'] } }, { wait: false });
110
- const updates = [];
111
- let started = false;
112
- let ended = false;
113
- let error = null;
114
- const manager = new StreamableManager({
115
- url: `${BASE_URL}/tasks/${task.id}/stream`,
116
- headers: { 'Authorization': `Bearer ${API_KEY}` },
117
- onStart: () => { started = true; },
118
- onEnd: () => { ended = true; },
119
- onData: (data) => {
120
- updates.push(data);
121
- // Stop when terminal
122
- if (isTerminalStatus(data.status)) {
123
- manager.stop();
124
- }
125
- },
126
- onError: (err) => {
127
- error = err;
128
- },
129
- });
130
- await manager.start();
131
- // If server doesn't support NDJSON, we'll get a parse error
132
- if (error) {
133
- console.log('⚠ Skipping StreamableManager test - server returned non-NDJSON');
134
- return;
135
- }
136
- expect(started).toBe(true);
137
- expect(ended).toBe(true);
138
- expect(updates.length).toBeGreaterThan(0);
139
- }, 60000);
140
- });
141
- });
142
- // Ensure Jest doesn't complain when API key is not set
143
- describe('Streamable Integration Setup', () => {
144
- it('checks for API key', () => {
145
- if (!API_KEY) {
146
- console.log('⚠️ Skipping streamable integration tests - INFERENCE_API_KEY not set');
147
- }
148
- expect(true).toBe(true);
149
- });
150
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,407 +0,0 @@
1
- import { tool, appTool, agentTool, webhookTool, httpTool, callTool, mcpTool, internalTools, string, number, integer, boolean, enumOf, object, array, optional, } from './tool-builder';
2
- import { ToolTypeClient, ToolTypeApp, ToolTypeAgent, ToolTypeHook, ToolTypeHTTP, ToolTypeMCP, IntegrationProviderGoogle, IntegrationProviderGoogleSA, } from './types';
3
- describe('Schema Helpers', () => {
4
- describe('string', () => {
5
- it('creates string schema without description', () => {
6
- const schema = string();
7
- expect(schema).toEqual({ type: 'string' });
8
- });
9
- it('creates string schema with description', () => {
10
- const schema = string('User name');
11
- expect(schema).toEqual({ type: 'string', description: 'User name' });
12
- });
13
- });
14
- describe('number', () => {
15
- it('creates number schema', () => {
16
- expect(number()).toEqual({ type: 'number' });
17
- expect(number('Temperature')).toEqual({ type: 'number', description: 'Temperature' });
18
- });
19
- });
20
- describe('integer', () => {
21
- it('creates integer schema', () => {
22
- expect(integer()).toEqual({ type: 'integer' });
23
- expect(integer('Age')).toEqual({ type: 'integer', description: 'Age' });
24
- });
25
- });
26
- describe('boolean', () => {
27
- it('creates boolean schema', () => {
28
- expect(boolean()).toEqual({ type: 'boolean' });
29
- expect(boolean('Is active')).toEqual({ type: 'boolean', description: 'Is active' });
30
- });
31
- });
32
- describe('enumOf', () => {
33
- it('creates enum schema', () => {
34
- const schema = enumOf(['low', 'medium', 'high']);
35
- expect(schema).toEqual({ type: 'string', enum: ['low', 'medium', 'high'] });
36
- });
37
- it('creates enum schema with description', () => {
38
- const schema = enumOf(['a', 'b'], 'Priority level');
39
- expect(schema).toEqual({ type: 'string', enum: ['a', 'b'], description: 'Priority level' });
40
- });
41
- });
42
- describe('object', () => {
43
- it('creates object schema with nested properties', () => {
44
- const schema = object({
45
- name: string('Name'),
46
- age: integer('Age'),
47
- });
48
- expect(schema).toEqual({
49
- type: 'object',
50
- properties: {
51
- name: { type: 'string', description: 'Name' },
52
- age: { type: 'integer', description: 'Age' },
53
- },
54
- });
55
- });
56
- it('creates object schema with description', () => {
57
- const schema = object({ x: number() }, 'Coordinates');
58
- expect(schema.description).toBe('Coordinates');
59
- });
60
- });
61
- describe('array', () => {
62
- it('creates array schema', () => {
63
- const schema = array(string('Tag'));
64
- expect(schema).toEqual({
65
- type: 'array',
66
- items: { type: 'string', description: 'Tag' },
67
- });
68
- });
69
- it('creates array schema with description', () => {
70
- const schema = array(integer(), 'List of IDs');
71
- expect(schema).toEqual({
72
- type: 'array',
73
- items: { type: 'integer' },
74
- description: 'List of IDs',
75
- });
76
- });
77
- });
78
- describe('optional', () => {
79
- it('marks schema as optional', () => {
80
- const schema = optional(string('Optional field'));
81
- expect(schema).toEqual({ type: 'string', description: 'Optional field', optional: true });
82
- });
83
- it('preserves original schema properties', () => {
84
- const schema = optional(enumOf(['a', 'b'], 'Choice'));
85
- expect(schema.enum).toEqual(['a', 'b']);
86
- expect(schema.optional).toBe(true);
87
- });
88
- });
89
- });
90
- describe('ClientToolBuilder (tool)', () => {
91
- it('creates minimal tool', () => {
92
- const t = tool('my_tool').build();
93
- expect(t).toEqual({
94
- name: 'my_tool',
95
- display_name: 'my_tool',
96
- description: '',
97
- type: ToolTypeClient,
98
- require_approval: undefined,
99
- client: {
100
- input_schema: { type: 'object', properties: {}, required: [] },
101
- },
102
- });
103
- });
104
- it('creates tool with description', () => {
105
- const t = tool('greet').describe('Says hello').build();
106
- expect(t.description).toBe('Says hello');
107
- });
108
- it('creates tool with display name', () => {
109
- const t = tool('get_data').display('Get Data').build();
110
- expect(t.display_name).toBe('Get Data');
111
- });
112
- it('creates tool with displayName alias', () => {
113
- const t = tool('get_data').displayName('Get Data').build();
114
- expect(t.display_name).toBe('Get Data');
115
- });
116
- it('returns schema and handler from handler()', async () => {
117
- const clientTool = tool('greet')
118
- .describe('Say hi')
119
- .param('name', string('Name'))
120
- .handler(async (args) => `Hello, ${args.name}`);
121
- expect(clientTool.schema.name).toBe('greet');
122
- expect(await clientTool.handler({ name: 'Ada' })).toBe('Hello, Ada');
123
- });
124
- it('creates tool with parameters', () => {
125
- const t = tool('add')
126
- .param('a', number('First number'))
127
- .param('b', number('Second number'))
128
- .build();
129
- expect(t.client?.input_schema).toEqual({
130
- type: 'object',
131
- properties: {
132
- a: { type: 'number', description: 'First number' },
133
- b: { type: 'number', description: 'Second number' },
134
- },
135
- required: ['a', 'b'],
136
- });
137
- });
138
- it('handles optional parameters correctly', () => {
139
- const t = tool('search')
140
- .param('query', string('Search query'))
141
- .param('limit', optional(integer('Max results')))
142
- .build();
143
- expect(t.client?.input_schema.required).toEqual(['query']);
144
- expect(t.client?.input_schema.properties).toHaveProperty('limit');
145
- });
146
- it('creates tool with require approval', () => {
147
- const t = tool('dangerous').requireApproval().build();
148
- expect(t.require_approval).toBe(true);
149
- });
150
- it('creates tool with nested object parameters', () => {
151
- const t = tool('create_user')
152
- .param('user', object({
153
- name: string('Name'),
154
- email: string('Email'),
155
- }, 'User data'))
156
- .build();
157
- const schema = t.client?.input_schema;
158
- expect(schema?.properties?.user).toMatchObject({
159
- type: 'object',
160
- properties: {
161
- name: { type: 'string', description: 'Name' },
162
- email: { type: 'string', description: 'Email' },
163
- },
164
- required: ['name', 'email'],
165
- });
166
- });
167
- it('propagates required into nested objects with optional fields', () => {
168
- const t = tool('update_user')
169
- .param('user', object({
170
- name: string('Name'),
171
- bio: optional(string('Bio')),
172
- }))
173
- .build();
174
- const userProp = t.client?.input_schema.properties?.user;
175
- expect(userProp.required).toEqual(['name']);
176
- });
177
- it('creates tool with array parameters', () => {
178
- const t = tool('process_items')
179
- .param('items', array(string('Item'), 'List of items'))
180
- .build();
181
- expect(t.client?.input_schema.properties?.items).toMatchObject({
182
- type: 'array',
183
- items: { type: 'string', description: 'Item' },
184
- });
185
- });
186
- });
187
- describe('AppToolBuilder (appTool)', () => {
188
- it('creates app tool with reference', () => {
189
- const t = appTool('generate', 'infsh/flux@v1.0')
190
- .describe('Generate image')
191
- .build();
192
- expect(t.type).toBe(ToolTypeApp);
193
- expect(t.app).toEqual({ ref: 'infsh/flux@v1.0' });
194
- expect(t.description).toBe('Generate image');
195
- });
196
- it('handles app reference with latest version', () => {
197
- const t = appTool('browse', 'my-org/browser@latest').build();
198
- expect(t.app).toEqual({ ref: 'my-org/browser@latest' });
199
- });
200
- it('includes parameters', () => {
201
- const t = appTool('fetch', 'infsh/fetch@v1')
202
- .param('url', string('URL to fetch'))
203
- .build();
204
- expect(t.name).toBe('fetch');
205
- // Note: App tools don't use client input_schema, params are for documentation
206
- });
207
- it('supports function selection for multi-function apps', () => {
208
- const t = appTool('upscale', 'infsh/sdxl@v1')
209
- .function('upscale')
210
- .describe('Upscale an image')
211
- .build();
212
- expect(t.app?.function).toBe('upscale');
213
- });
214
- it('supports session enabled for stateful apps', () => {
215
- const t = appTool('browser', 'infsh/browser-use@v1')
216
- .sessionEnabled()
217
- .describe('Control a browser')
218
- .build();
219
- expect(t.app?.session_enabled).toBe(true);
220
- });
221
- it('supports full configuration with function and session', () => {
222
- const t = appTool('chat', 'infsh/chatbot@v1')
223
- .describe('Chat with context')
224
- .function('stream')
225
- .sessionEnabled()
226
- .setup({ api_key: 'secret' })
227
- .input({ temperature: 0.7 })
228
- .build();
229
- expect(t.app).toEqual({
230
- ref: 'infsh/chatbot@v1',
231
- function: 'stream',
232
- session_enabled: true,
233
- setup: { api_key: 'secret' },
234
- input: { temperature: 0.7 },
235
- });
236
- });
237
- });
238
- describe('AgentToolBuilder (agentTool)', () => {
239
- it('creates agent tool with reference', () => {
240
- const t = agentTool('research', 'acme/researcher@v2')
241
- .describe('Research a topic')
242
- .build();
243
- expect(t.type).toBe(ToolTypeAgent);
244
- expect(t.agent).toEqual({ ref: 'acme/researcher@v2' });
245
- });
246
- it('supports display name and approval', () => {
247
- const t = agentTool('coder', 'infsh/code-agent@latest')
248
- .display('Code Assistant')
249
- .requireApproval()
250
- .build();
251
- expect(t.display_name).toBe('Code Assistant');
252
- expect(t.require_approval).toBe(true);
253
- });
254
- });
255
- describe('HTTPToolBuilder (httpTool)', () => {
256
- it('includes custom method and headers in the built tool', () => {
257
- const t = httpTool('fetch', 'https://api.example.com/data')
258
- .method('GET')
259
- .header('X-Custom', '1')
260
- .build();
261
- expect(t.http?.method).toBe('GET');
262
- expect(t.http?.headers).toEqual({ 'X-Custom': '1' });
263
- });
264
- it('omits method when POST is the default', () => {
265
- const t = httpTool('post', 'https://api.example.com').build();
266
- expect(t.http?.method).toBeUndefined();
267
- });
268
- it('should attach integration auth with provider and integration id', () => {
269
- const t = httpTool('gmail_send', 'https://api.example.com/send')
270
- .auth({ integration: IntegrationProviderGoogle, integrationId: 'int-123' })
271
- .build();
272
- expect(t.type).toBe(ToolTypeHTTP);
273
- expect(t.http?.auth).toEqual({
274
- type: 'integration',
275
- provider: IntegrationProviderGoogle,
276
- integration_id: 'int-123',
277
- });
278
- });
279
- it('should attach integration auth for google-sa service account provider', () => {
280
- const t = httpTool('calendar_read', 'https://api.example.com/calendar')
281
- .auth({ integration: IntegrationProviderGoogleSA, integrationId: 'sa-int-1' })
282
- .build();
283
- expect(t.http?.auth).toEqual({
284
- type: 'integration',
285
- provider: IntegrationProviderGoogleSA,
286
- integration_id: 'sa-int-1',
287
- });
288
- });
289
- it('should attach api key auth with default header', () => {
290
- const t = httpTool('fetch', 'https://api.example.com').auth({ apiKey: 'KEY' }).build();
291
- expect(t.http?.auth).toEqual({
292
- type: 'api_key',
293
- secret: 'KEY',
294
- header: 'X-API-Key',
295
- });
296
- });
297
- it('should attach bearer auth', () => {
298
- const t = httpTool('fetch', 'https://api.example.com')
299
- .auth({ bearer: 'token-abc' })
300
- .build();
301
- expect(t.http?.auth).toEqual({
302
- type: 'bearer',
303
- secret: 'token-abc',
304
- });
305
- });
306
- it('callTool should be an alias for httpTool', () => {
307
- const t = callTool('ping', 'https://api.example.com/ping').method('GET').build();
308
- expect(t.type).toBe(ToolTypeHTTP);
309
- expect(t.http?.method).toBe('GET');
310
- });
311
- });
312
- describe('MCPToolBuilder (mcpTool)', () => {
313
- it('creates MCP tool with integration and tool name', () => {
314
- const t = mcpTool('search_docs', 'int-mcp-1', 'search').describe('Search docs').build();
315
- expect(t.type).toBe(ToolTypeMCP);
316
- expect(t.mcp).toEqual({ integration_id: 'int-mcp-1', tool_name: 'search' });
317
- expect(t.description).toBe('Search docs');
318
- });
319
- });
320
- describe('WebhookToolBuilder (webhookTool)', () => {
321
- it('creates webhook tool with URL', () => {
322
- const t = webhookTool('notify', 'https://api.example.com/webhook')
323
- .describe('Send notification')
324
- .build();
325
- expect(t.type).toBe(ToolTypeHook);
326
- expect(t.hook?.url).toBe('https://api.example.com/webhook');
327
- expect(t.description).toBe('Send notification');
328
- });
329
- it('includes secret key', () => {
330
- const t = webhookTool('slack', 'https://hooks.slack.com/services/xxx')
331
- .secret('SLACK_SECRET')
332
- .build();
333
- expect(t.hook?.secret).toBe('SLACK_SECRET');
334
- });
335
- it('generates input schema for parameters', () => {
336
- const t = webhookTool('send', 'https://api.example.com')
337
- .param('message', string('Message'))
338
- .param('priority', optional(enumOf(['low', 'high'])))
339
- .build();
340
- expect(t.hook?.input_schema).toEqual({
341
- type: 'object',
342
- properties: {
343
- message: { type: 'string', description: 'Message' },
344
- priority: { type: 'string', enum: ['low', 'high'] },
345
- },
346
- required: ['message'],
347
- });
348
- });
349
- });
350
- describe('InternalToolsBuilder (internalTools)', () => {
351
- it('creates empty config by default', () => {
352
- const config = internalTools().build();
353
- expect(config).toEqual({});
354
- });
355
- it('enables plan tools', () => {
356
- const config = internalTools().plan().build();
357
- expect(config).toEqual({ plan: true });
358
- });
359
- it('enables memory tools', () => {
360
- const config = internalTools().memory().build();
361
- expect(config).toEqual({ memory: true });
362
- });
363
- it('enables widget tools', () => {
364
- const config = internalTools().widget().build();
365
- expect(config).toEqual({ widget: true });
366
- });
367
- it('enables finish tool', () => {
368
- const config = internalTools().finish().build();
369
- expect(config).toEqual({ finish: true });
370
- });
371
- it('chains multiple tool enables', () => {
372
- const config = internalTools().plan().memory().widget().build();
373
- expect(config).toEqual({ plan: true, memory: true, widget: true });
374
- });
375
- it('enables all tools with all()', () => {
376
- const config = internalTools().all().build();
377
- expect(config).toEqual({ plan: true, memory: true, widget: true, finish: true });
378
- });
379
- it('disables all tools with none()', () => {
380
- const config = internalTools().none().build();
381
- expect(config).toEqual({ plan: false, memory: false, widget: false, finish: false });
382
- });
383
- it('allows explicit disable', () => {
384
- const config = internalTools().plan(false).memory(true).build();
385
- expect(config).toEqual({ plan: false, memory: true });
386
- });
387
- });
388
- describe('Fluent API chaining', () => {
389
- it('supports full fluent chain', () => {
390
- const t = tool('complex')
391
- .display('Complex Tool')
392
- .describe('A complex tool with many params')
393
- .param('name', string('Name'))
394
- .param('count', integer('Count'))
395
- .param('options', optional(object({
396
- verbose: boolean('Verbose mode'),
397
- tags: array(string('Tag')),
398
- })))
399
- .requireApproval()
400
- .build();
401
- expect(t.name).toBe('complex');
402
- expect(t.display_name).toBe('Complex Tool');
403
- expect(t.description).toBe('A complex tool with many params');
404
- expect(t.require_approval).toBe(true);
405
- expect(t.client?.input_schema.required).toEqual(['name', 'count']);
406
- });
407
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,43 +0,0 @@
1
- import { TaskStatusCancelled, TaskStatusCompleted, TaskStatusFailed, TaskStatusRunning, TaskStatusUnknown, } from './types';
2
- import { isTerminalStatus, parseStatus } from './utils';
3
- describe('parseStatus', () => {
4
- it('should return TaskStatusUnknown for null and undefined', () => {
5
- expect(parseStatus(null)).toBe(TaskStatusUnknown);
6
- expect(parseStatus(undefined)).toBe(TaskStatusUnknown);
7
- });
8
- it('should pass through numeric status values', () => {
9
- expect(parseStatus(TaskStatusRunning)).toBe(TaskStatusRunning);
10
- expect(parseStatus(TaskStatusCompleted)).toBe(TaskStatusCompleted);
11
- });
12
- it('should map lowercase string status names to TaskStatus', () => {
13
- expect(parseStatus('running')).toBe(TaskStatusRunning);
14
- expect(parseStatus('completed')).toBe(TaskStatusCompleted);
15
- expect(parseStatus('failed')).toBe(TaskStatusFailed);
16
- expect(parseStatus('cancelled')).toBe(TaskStatusCancelled);
17
- });
18
- it('should be case-insensitive for string statuses', () => {
19
- expect(parseStatus('COMPLETED')).toBe(TaskStatusCompleted);
20
- expect(parseStatus('Running')).toBe(TaskStatusRunning);
21
- });
22
- it('should return TaskStatusUnknown for unrecognized strings', () => {
23
- expect(parseStatus('not_a_real_status')).toBe(TaskStatusUnknown);
24
- });
25
- it('should return TaskStatusUnknown for non-string non-number values', () => {
26
- expect(parseStatus(true)).toBe(TaskStatusUnknown);
27
- });
28
- });
29
- describe('isTerminalStatus', () => {
30
- it('should be true for completed, failed, and cancelled (int or string)', () => {
31
- expect(isTerminalStatus(TaskStatusCompleted)).toBe(true);
32
- expect(isTerminalStatus(TaskStatusFailed)).toBe(true);
33
- expect(isTerminalStatus(TaskStatusCancelled)).toBe(true);
34
- expect(isTerminalStatus('completed')).toBe(true);
35
- expect(isTerminalStatus('failed')).toBe(true);
36
- expect(isTerminalStatus('cancelled')).toBe(true);
37
- });
38
- it('should be false for non-terminal statuses', () => {
39
- expect(isTerminalStatus(TaskStatusRunning)).toBe(false);
40
- expect(isTerminalStatus('running')).toBe(false);
41
- expect(isTerminalStatus(null)).toBe(false);
42
- });
43
- });