@inferencesh/sdk 0.6.8 → 0.6.12
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/CHANGELOG.md +7 -1
- package/README.md +70 -1
- package/dist/agent/actions.js +3 -3
- package/dist/agent/actions.test.js +89 -0
- package/dist/agent/api.test.js +86 -8
- package/dist/api/agents.d.ts +10 -0
- package/dist/api/agents.js +17 -3
- package/dist/api/agents.test.js +303 -92
- package/dist/api/api-keys.d.ts +24 -0
- package/dist/api/api-keys.js +26 -0
- package/dist/api/api-keys.test.d.ts +1 -0
- package/dist/api/api-keys.test.js +44 -0
- package/dist/api/apps.js +1 -1
- package/dist/api/apps.test.d.ts +1 -0
- package/dist/api/apps.test.js +135 -0
- package/dist/api/chats.d.ts +14 -0
- package/dist/api/chats.js +18 -0
- package/dist/api/chats.test.d.ts +1 -0
- package/dist/api/chats.test.js +85 -0
- package/dist/api/engines.d.ts +12 -0
- package/dist/api/engines.js +18 -0
- package/dist/api/engines.test.d.ts +1 -0
- package/dist/api/engines.test.js +133 -0
- package/dist/api/files.test.js +186 -6
- package/dist/api/flow-runs.test.d.ts +1 -0
- package/dist/api/flow-runs.test.js +97 -0
- package/dist/api/flows.d.ts +4 -0
- package/dist/api/flows.js +6 -0
- package/dist/api/flows.test.d.ts +1 -0
- package/dist/api/flows.test.js +118 -0
- package/dist/api/integrations.d.ts +41 -0
- package/dist/api/integrations.js +56 -0
- package/dist/api/integrations.test.d.ts +1 -0
- package/dist/api/integrations.test.js +109 -0
- package/dist/api/knowledge.d.ts +112 -0
- package/dist/api/knowledge.js +163 -0
- package/dist/api/knowledge.test.d.ts +1 -0
- package/dist/api/knowledge.test.js +94 -0
- package/dist/api/mcp-servers.d.ts +45 -0
- package/dist/api/mcp-servers.js +62 -0
- package/dist/api/mcp-servers.test.d.ts +1 -0
- package/dist/api/mcp-servers.test.js +64 -0
- package/dist/api/projects.d.ts +29 -0
- package/dist/api/projects.js +38 -0
- package/dist/api/projects.test.d.ts +1 -0
- package/dist/api/projects.test.js +52 -0
- package/dist/api/search.d.ts +21 -0
- package/dist/api/search.js +20 -0
- package/dist/api/search.test.d.ts +1 -0
- package/dist/api/search.test.js +39 -0
- package/dist/api/secrets.d.ts +29 -0
- package/dist/api/secrets.js +38 -0
- package/dist/api/secrets.test.d.ts +1 -0
- package/dist/api/secrets.test.js +61 -0
- package/dist/api/sessions.test.js +4 -4
- package/dist/api/tasks.d.ts +13 -1
- package/dist/api/tasks.js +18 -0
- package/dist/api/tasks.test.js +174 -22
- package/dist/api/teams.d.ts +71 -0
- package/dist/api/teams.js +92 -0
- package/dist/api/teams.test.d.ts +1 -0
- package/dist/api/teams.test.js +79 -0
- package/dist/client.test.js +8 -8
- package/dist/http/client.d.ts +5 -0
- package/dist/http/client.js +46 -48
- package/dist/http/client.test.js +296 -13
- package/dist/http/errors.test.js +13 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +25 -0
- package/dist/proxy/express.test.d.ts +1 -0
- package/dist/proxy/express.test.js +106 -0
- package/dist/proxy/index.test.js +10 -1
- package/dist/tool-builder.test.js +11 -1
- package/dist/types.d.ts +793 -23
- package/dist/types.js +180 -8
- package/package.json +4 -4
package/dist/api/agents.test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HttpClient } from '../http/client';
|
|
2
|
+
import { StreamableManager } from '../http/streamable';
|
|
2
3
|
import { ChatStatusBusy, ChatStatusIdle, ToolInvocationStatusAwaitingInput, ToolTypeClient, } from '../types';
|
|
3
4
|
import { FilesAPI } from './files';
|
|
4
5
|
import { AgentsAPI } from './agents';
|
|
@@ -53,18 +54,15 @@ describe('Agent.sendMessage (polling mode)', () => {
|
|
|
53
54
|
const userMessage = makeMessage({ id: 'user-1', role: 'user' });
|
|
54
55
|
const assistantMessage = makeMessage({ id: 'asst-1' });
|
|
55
56
|
mockJsonResponse({
|
|
56
|
-
|
|
57
|
-
data: { user_message: userMessage, assistant_message: assistantMessage },
|
|
57
|
+
user_message: userMessage, assistant_message: assistantMessage,
|
|
58
58
|
});
|
|
59
|
-
mockJsonResponse({
|
|
59
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
60
60
|
mockJsonResponse({
|
|
61
|
-
|
|
62
|
-
data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
|
|
61
|
+
id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
|
|
63
62
|
});
|
|
64
|
-
mockJsonResponse({
|
|
63
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
65
64
|
mockJsonResponse({
|
|
66
|
-
|
|
67
|
-
data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
|
|
65
|
+
id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
|
|
68
66
|
});
|
|
69
67
|
const onChat = jest.fn();
|
|
70
68
|
const result = await agent().sendMessage('hello', { stream: false, onChat });
|
|
@@ -81,27 +79,20 @@ describe('Agent.sendMessage (polling mode)', () => {
|
|
|
81
79
|
};
|
|
82
80
|
const messageWithTool = makeMessage({ tool_invocations: [toolInvocation] });
|
|
83
81
|
mockJsonResponse({
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
87
|
-
assistant_message: makeMessage(),
|
|
88
|
-
},
|
|
82
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
83
|
+
assistant_message: makeMessage(),
|
|
89
84
|
});
|
|
90
|
-
mockJsonResponse({
|
|
85
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
91
86
|
mockJsonResponse({
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
status: ChatStatusBusy,
|
|
96
|
-
chat_messages: [messageWithTool],
|
|
97
|
-
},
|
|
87
|
+
id: 'chat-1',
|
|
88
|
+
status: ChatStatusBusy,
|
|
89
|
+
chat_messages: [messageWithTool],
|
|
98
90
|
});
|
|
99
91
|
// Same status again — stub poll should not re-dispatch tool
|
|
100
|
-
mockJsonResponse({
|
|
101
|
-
mockJsonResponse({
|
|
92
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
93
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
102
94
|
mockJsonResponse({
|
|
103
|
-
|
|
104
|
-
data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [messageWithTool] },
|
|
95
|
+
id: 'chat-1', status: ChatStatusIdle, chat_messages: [messageWithTool],
|
|
105
96
|
});
|
|
106
97
|
const onMessage = jest.fn();
|
|
107
98
|
const onToolCall = jest.fn();
|
|
@@ -117,22 +108,18 @@ describe('Agent.sendMessage (polling mode)', () => {
|
|
|
117
108
|
const userMessage = makeMessage({ id: 'user-1', role: 'user' });
|
|
118
109
|
const assistantMessage = makeMessage();
|
|
119
110
|
mockJsonResponse({
|
|
120
|
-
|
|
121
|
-
data: { user_message: userMessage, assistant_message: assistantMessage },
|
|
111
|
+
user_message: userMessage, assistant_message: assistantMessage,
|
|
122
112
|
});
|
|
123
|
-
mockJsonResponse({
|
|
113
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
124
114
|
mockJsonResponse({
|
|
125
|
-
|
|
126
|
-
data: { id: 'chat-1', status: ChatStatusBusy },
|
|
115
|
+
id: 'chat-1', status: ChatStatusBusy,
|
|
127
116
|
});
|
|
128
|
-
mockJsonResponse({
|
|
117
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
129
118
|
mockJsonResponse({
|
|
130
|
-
|
|
131
|
-
data: { id: 'chat-1', status: ChatStatusIdle },
|
|
119
|
+
id: 'chat-1', status: ChatStatusIdle,
|
|
132
120
|
});
|
|
133
121
|
mockJsonResponse({
|
|
134
|
-
|
|
135
|
-
data: { id: 'chat-1', status: ChatStatusIdle, output: { answer: 42 } },
|
|
122
|
+
id: 'chat-1', status: ChatStatusIdle, output: { answer: 42 },
|
|
136
123
|
});
|
|
137
124
|
const output = await agent().run('compute');
|
|
138
125
|
expect(output).toEqual({ answer: 42 });
|
|
@@ -155,8 +142,7 @@ describe('Agent.sendMessage (streaming mode)', () => {
|
|
|
155
142
|
ok: true,
|
|
156
143
|
status: 200,
|
|
157
144
|
text: () => Promise.resolve(JSON.stringify({
|
|
158
|
-
|
|
159
|
-
data: { user_message: userMessage, assistant_message: assistantMessage },
|
|
145
|
+
user_message: userMessage, assistant_message: assistantMessage,
|
|
160
146
|
})),
|
|
161
147
|
});
|
|
162
148
|
}
|
|
@@ -184,11 +170,8 @@ describe('Agent.sendMessage (streaming mode)', () => {
|
|
|
184
170
|
ok: true,
|
|
185
171
|
status: 200,
|
|
186
172
|
text: () => Promise.resolve(JSON.stringify({
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
190
|
-
assistant_message: makeMessage(),
|
|
191
|
-
},
|
|
173
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
174
|
+
assistant_message: makeMessage(),
|
|
192
175
|
})),
|
|
193
176
|
});
|
|
194
177
|
}
|
|
@@ -214,21 +197,16 @@ describe('Agent.sendMessage (streaming mode)', () => {
|
|
|
214
197
|
});
|
|
215
198
|
const agentInstance = new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
|
|
216
199
|
mockJsonResponse({
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
220
|
-
assistant_message: makeMessage(),
|
|
221
|
-
},
|
|
200
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
201
|
+
assistant_message: makeMessage(),
|
|
222
202
|
});
|
|
223
|
-
mockJsonResponse({
|
|
203
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
224
204
|
mockJsonResponse({
|
|
225
|
-
|
|
226
|
-
data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
|
|
205
|
+
id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
|
|
227
206
|
});
|
|
228
|
-
mockJsonResponse({
|
|
207
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
229
208
|
mockJsonResponse({
|
|
230
|
-
|
|
231
|
-
data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
|
|
209
|
+
id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
|
|
232
210
|
});
|
|
233
211
|
await agentInstance.sendMessage('first', { stream: false });
|
|
234
212
|
const callOrder = [];
|
|
@@ -239,11 +217,8 @@ describe('Agent.sendMessage (streaming mode)', () => {
|
|
|
239
217
|
ok: true,
|
|
240
218
|
status: 200,
|
|
241
219
|
text: () => Promise.resolve(JSON.stringify({
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
user_message: makeMessage({ id: 'user-2', role: 'user' }),
|
|
245
|
-
assistant_message: makeMessage({ id: 'asst-2' }),
|
|
246
|
-
},
|
|
220
|
+
user_message: makeMessage({ id: 'user-2', role: 'user' }),
|
|
221
|
+
assistant_message: makeMessage({ id: 'asst-2' }),
|
|
247
222
|
})),
|
|
248
223
|
});
|
|
249
224
|
}
|
|
@@ -285,21 +260,16 @@ describe('Agent.sendMessage (file attachments)', () => {
|
|
|
285
260
|
content_type: 'application/pdf',
|
|
286
261
|
};
|
|
287
262
|
mockJsonResponse({
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
291
|
-
assistant_message: makeMessage(),
|
|
292
|
-
},
|
|
263
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
264
|
+
assistant_message: makeMessage(),
|
|
293
265
|
});
|
|
294
|
-
mockJsonResponse({
|
|
266
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
295
267
|
mockJsonResponse({
|
|
296
|
-
|
|
297
|
-
data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
|
|
268
|
+
id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
|
|
298
269
|
});
|
|
299
|
-
mockJsonResponse({
|
|
270
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
300
271
|
mockJsonResponse({
|
|
301
|
-
|
|
302
|
-
data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
|
|
272
|
+
id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
|
|
303
273
|
});
|
|
304
274
|
await agent().sendMessage('see attachments', {
|
|
305
275
|
stream: false,
|
|
@@ -312,6 +282,44 @@ describe('Agent.sendMessage (file attachments)', () => {
|
|
|
312
282
|
expect(mockFetch.mock.calls.filter(([url]) => String(url).includes('/files')).length).toBe(0);
|
|
313
283
|
});
|
|
314
284
|
});
|
|
285
|
+
describe('Agent.sendMessage (ad-hoc config)', () => {
|
|
286
|
+
beforeEach(() => {
|
|
287
|
+
jest.clearAllMocks();
|
|
288
|
+
});
|
|
289
|
+
const adHocAgent = () => {
|
|
290
|
+
const http = new HttpClient({
|
|
291
|
+
apiKey: 'test-key',
|
|
292
|
+
stream: false,
|
|
293
|
+
pollIntervalMs: 20,
|
|
294
|
+
});
|
|
295
|
+
return new AgentsAPI(http, new FilesAPI(http)).create({
|
|
296
|
+
core_app: { ref: 'openrouter/claude@latest' },
|
|
297
|
+
system_prompt: 'You are helpful',
|
|
298
|
+
name: 'adhoc-bot',
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
it('should POST agent_config and agent_name instead of agent template ref', async () => {
|
|
302
|
+
mockJsonResponse({
|
|
303
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
304
|
+
assistant_message: makeMessage(),
|
|
305
|
+
});
|
|
306
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
307
|
+
mockJsonResponse({ id: 'chat-1', status: ChatStatusBusy, chat_messages: [] });
|
|
308
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
309
|
+
mockJsonResponse({ id: 'chat-1', status: ChatStatusIdle, chat_messages: [] });
|
|
310
|
+
await adHocAgent().sendMessage('hello', { stream: false });
|
|
311
|
+
const runCall = mockFetch.mock.calls.find(([url]) => String(url).includes('/agents/run'));
|
|
312
|
+
const body = JSON.parse(String(runCall[1].body));
|
|
313
|
+
expect(body.agent).toBeUndefined();
|
|
314
|
+
expect(body.agent_config).toEqual({
|
|
315
|
+
core_app: { ref: 'openrouter/claude@latest' },
|
|
316
|
+
system_prompt: 'You are helpful',
|
|
317
|
+
name: 'adhoc-bot',
|
|
318
|
+
});
|
|
319
|
+
expect(body.agent_name).toBe('adhoc-bot');
|
|
320
|
+
expect(body.input.text).toBe('hello');
|
|
321
|
+
});
|
|
322
|
+
});
|
|
315
323
|
describe('Agent lifecycle', () => {
|
|
316
324
|
beforeEach(() => {
|
|
317
325
|
jest.clearAllMocks();
|
|
@@ -331,46 +339,96 @@ describe('Agent lifecycle', () => {
|
|
|
331
339
|
it('stopChat should POST to /chats/{id}/stop when a chat exists', async () => {
|
|
332
340
|
const agentInstance = agent();
|
|
333
341
|
mockJsonResponse({
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
337
|
-
assistant_message: makeMessage(),
|
|
338
|
-
},
|
|
342
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
343
|
+
assistant_message: makeMessage(),
|
|
339
344
|
});
|
|
340
|
-
mockJsonResponse({
|
|
345
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
341
346
|
mockJsonResponse({
|
|
342
|
-
|
|
343
|
-
data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
|
|
347
|
+
id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
|
|
344
348
|
});
|
|
345
|
-
mockJsonResponse({
|
|
349
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
346
350
|
mockJsonResponse({
|
|
347
|
-
|
|
348
|
-
data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
|
|
351
|
+
id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
|
|
349
352
|
});
|
|
350
353
|
await agentInstance.sendMessage('hello', { stream: false });
|
|
351
354
|
jest.clearAllMocks();
|
|
352
|
-
mockJsonResponse(
|
|
355
|
+
mockJsonResponse(null);
|
|
353
356
|
await agentInstance.stopChat();
|
|
354
357
|
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/chats/chat-1/stop'), expect.anything());
|
|
355
358
|
});
|
|
359
|
+
it('disconnect should stop active stream managers', async () => {
|
|
360
|
+
const http = new HttpClient({
|
|
361
|
+
apiKey: 'test-key',
|
|
362
|
+
stream: true,
|
|
363
|
+
pollIntervalMs: 20,
|
|
364
|
+
});
|
|
365
|
+
const agentInstance = new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
|
|
366
|
+
const stopSpy = jest.spyOn(StreamableManager.prototype, 'stop');
|
|
367
|
+
mockFetch.mockImplementation((url) => {
|
|
368
|
+
if (url.includes('/agents/run')) {
|
|
369
|
+
return Promise.resolve({
|
|
370
|
+
ok: true,
|
|
371
|
+
status: 200,
|
|
372
|
+
text: () => Promise.resolve(JSON.stringify({
|
|
373
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
374
|
+
assistant_message: makeMessage(),
|
|
375
|
+
})),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
return Promise.resolve(mockNdjsonStream([
|
|
379
|
+
`${JSON.stringify({ event: 'chats', data: { id: 'chat-1', status: ChatStatusIdle } })}\n`,
|
|
380
|
+
]));
|
|
381
|
+
});
|
|
382
|
+
await agentInstance.sendMessage('hello', { onChat: jest.fn() });
|
|
383
|
+
agentInstance.disconnect();
|
|
384
|
+
expect(stopSpy).toHaveBeenCalled();
|
|
385
|
+
stopSpy.mockRestore();
|
|
386
|
+
});
|
|
387
|
+
it('startStreaming should no-op when there is no active chat', () => {
|
|
388
|
+
const agentInstance = agent();
|
|
389
|
+
agentInstance.startStreaming();
|
|
390
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
391
|
+
});
|
|
392
|
+
it('startStreaming should open the chat stream when chatId exists', async () => {
|
|
393
|
+
const http = new HttpClient({
|
|
394
|
+
apiKey: 'test-key',
|
|
395
|
+
stream: true,
|
|
396
|
+
pollIntervalMs: 20,
|
|
397
|
+
});
|
|
398
|
+
const agentInstance = new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
|
|
399
|
+
mockJsonResponse({
|
|
400
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
401
|
+
assistant_message: makeMessage(),
|
|
402
|
+
});
|
|
403
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
404
|
+
mockJsonResponse({
|
|
405
|
+
id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
|
|
406
|
+
});
|
|
407
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
408
|
+
mockJsonResponse({
|
|
409
|
+
id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
|
|
410
|
+
});
|
|
411
|
+
await agentInstance.sendMessage('hello', { stream: false });
|
|
412
|
+
jest.clearAllMocks();
|
|
413
|
+
mockFetch.mockResolvedValue(mockNdjsonStream([
|
|
414
|
+
`${JSON.stringify({ event: 'chats', data: { id: 'chat-1', status: ChatStatusIdle } })}\n`,
|
|
415
|
+
]));
|
|
416
|
+
agentInstance.startStreaming({ onChat: jest.fn() });
|
|
417
|
+
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/chats/chat-1/stream'), expect.anything());
|
|
418
|
+
});
|
|
356
419
|
it('reset should clear chat state so stopChat is a no-op', async () => {
|
|
357
420
|
const agentInstance = agent();
|
|
358
421
|
mockJsonResponse({
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
362
|
-
assistant_message: makeMessage(),
|
|
363
|
-
},
|
|
422
|
+
user_message: makeMessage({ id: 'user-1', role: 'user' }),
|
|
423
|
+
assistant_message: makeMessage(),
|
|
364
424
|
});
|
|
365
|
-
mockJsonResponse({
|
|
425
|
+
mockJsonResponse({ status: ChatStatusBusy });
|
|
366
426
|
mockJsonResponse({
|
|
367
|
-
|
|
368
|
-
data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
|
|
427
|
+
id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
|
|
369
428
|
});
|
|
370
|
-
mockJsonResponse({
|
|
429
|
+
mockJsonResponse({ status: ChatStatusIdle });
|
|
371
430
|
mockJsonResponse({
|
|
372
|
-
|
|
373
|
-
data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
|
|
431
|
+
id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
|
|
374
432
|
});
|
|
375
433
|
await agentInstance.sendMessage('hello', { stream: false });
|
|
376
434
|
agentInstance.reset();
|
|
@@ -379,6 +437,29 @@ describe('Agent lifecycle', () => {
|
|
|
379
437
|
expect(mockFetch).not.toHaveBeenCalled();
|
|
380
438
|
});
|
|
381
439
|
});
|
|
440
|
+
describe('Agent.getChat', () => {
|
|
441
|
+
beforeEach(() => {
|
|
442
|
+
jest.clearAllMocks();
|
|
443
|
+
});
|
|
444
|
+
const agent = () => {
|
|
445
|
+
const http = new HttpClient({ apiKey: 'test-key' });
|
|
446
|
+
return new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
|
|
447
|
+
};
|
|
448
|
+
it('should return null without a chat id and no active chat', async () => {
|
|
449
|
+
const result = await agent().getChat();
|
|
450
|
+
expect(result).toBeNull();
|
|
451
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
452
|
+
});
|
|
453
|
+
it('should GET /chats/{id} when chatId is provided', async () => {
|
|
454
|
+
const chat = { id: 'chat-42', status: 'idle', chat_messages: [] };
|
|
455
|
+
mockJsonResponse(chat);
|
|
456
|
+
const result = await agent().getChat('chat-42');
|
|
457
|
+
expect(result).toEqual(chat);
|
|
458
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
459
|
+
expect(url).toContain('/chats/chat-42');
|
|
460
|
+
expect(init.method).toBe('GET');
|
|
461
|
+
});
|
|
462
|
+
});
|
|
382
463
|
describe('Agent.submitToolResult', () => {
|
|
383
464
|
beforeEach(() => {
|
|
384
465
|
jest.clearAllMocks();
|
|
@@ -386,7 +467,7 @@ describe('Agent.submitToolResult', () => {
|
|
|
386
467
|
it('should JSON-stringify structured action results', async () => {
|
|
387
468
|
const http = new HttpClient({ apiKey: 'test-key' });
|
|
388
469
|
const agentInstance = new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
|
|
389
|
-
mockJsonResponse(
|
|
470
|
+
mockJsonResponse(null);
|
|
390
471
|
const payload = {
|
|
391
472
|
action: { type: 'form_submit', payload: { field: 'value' } },
|
|
392
473
|
form_data: { field: 'value' },
|
|
@@ -397,3 +478,133 @@ describe('Agent.submitToolResult', () => {
|
|
|
397
478
|
expect(body.result).toBe(JSON.stringify(payload));
|
|
398
479
|
});
|
|
399
480
|
});
|
|
481
|
+
describe('AgentsAPI.submitToolResult', () => {
|
|
482
|
+
beforeEach(() => {
|
|
483
|
+
jest.clearAllMocks();
|
|
484
|
+
});
|
|
485
|
+
const api = () => {
|
|
486
|
+
const http = new HttpClient({ apiKey: 'test-key' });
|
|
487
|
+
return new AgentsAPI(http, new FilesAPI(http));
|
|
488
|
+
};
|
|
489
|
+
it('should POST plain string results to /tools/{invocationId}', async () => {
|
|
490
|
+
mockJsonResponse(null);
|
|
491
|
+
await api().submitToolResult('inv-plain', 'done');
|
|
492
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
493
|
+
expect(url).toContain('/tools/inv-plain');
|
|
494
|
+
expect(init.method).toBe('POST');
|
|
495
|
+
expect(JSON.parse(String(init.body))).toEqual({ result: 'done' });
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
describe('AgentsAPI (template CRUD)', () => {
|
|
499
|
+
beforeEach(() => {
|
|
500
|
+
jest.clearAllMocks();
|
|
501
|
+
});
|
|
502
|
+
const api = () => {
|
|
503
|
+
const http = new HttpClient({ apiKey: 'test-key' });
|
|
504
|
+
return new AgentsAPI(http, new FilesAPI(http));
|
|
505
|
+
};
|
|
506
|
+
it('should GET /agents/internal-tools for getInternalTools()', async () => {
|
|
507
|
+
const tools = [{ name: 'search', description: 'Search the web' }];
|
|
508
|
+
mockJsonResponse(tools);
|
|
509
|
+
const result = await api().getInternalTools();
|
|
510
|
+
expect(result).toEqual(tools);
|
|
511
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
512
|
+
expect(url).toContain('/agents/internal-tools');
|
|
513
|
+
expect(init.method).toBe('GET');
|
|
514
|
+
});
|
|
515
|
+
it('should POST team_id for transferOwnership()', async () => {
|
|
516
|
+
const agent = { id: 'agent-1' };
|
|
517
|
+
mockJsonResponse(agent);
|
|
518
|
+
await api().transferOwnership('agent-1', 'team-42');
|
|
519
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
520
|
+
expect(url).toContain('/agents/agent-1/transfer');
|
|
521
|
+
expect(JSON.parse(init.body)).toEqual({ team_id: 'team-42' });
|
|
522
|
+
});
|
|
523
|
+
it('should POST /agents for createAgent()', async () => {
|
|
524
|
+
const payload = { name: 'support-bot', core_app: { ref: 'app/ref' } };
|
|
525
|
+
const created = { id: 'agent-new', ...payload };
|
|
526
|
+
mockJsonResponse(created);
|
|
527
|
+
const result = await api().createAgent(payload);
|
|
528
|
+
expect(result).toEqual(created);
|
|
529
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
530
|
+
expect(url).toContain('/agents');
|
|
531
|
+
expect(init.method).toBe('POST');
|
|
532
|
+
expect(JSON.parse(init.body)).toEqual(payload);
|
|
533
|
+
});
|
|
534
|
+
it('should GET /agents/{namespace}/{name} for getByName()', async () => {
|
|
535
|
+
const agent = { id: 'agent-1', name: 'my-agent' };
|
|
536
|
+
mockJsonResponse(agent);
|
|
537
|
+
const result = await api().getByName('inference', 'my-agent');
|
|
538
|
+
expect(result).toEqual(agent);
|
|
539
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
540
|
+
expect(url).toContain('/agents/inference/my-agent');
|
|
541
|
+
expect(init.method).toBe('GET');
|
|
542
|
+
});
|
|
543
|
+
it('should POST /agents/list for list()', async () => {
|
|
544
|
+
const page = { items: [{ id: 'agent-1' }], next_cursor: null };
|
|
545
|
+
mockJsonResponse(page);
|
|
546
|
+
const result = await api().list({ limit: 10 });
|
|
547
|
+
expect(result).toEqual(page);
|
|
548
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
549
|
+
expect(url).toContain('/agents/list');
|
|
550
|
+
expect(init.method).toBe('POST');
|
|
551
|
+
});
|
|
552
|
+
it('should GET /agents/{id} for get()', async () => {
|
|
553
|
+
const agent = { id: 'agent-1', name: 'support-bot' };
|
|
554
|
+
mockJsonResponse(agent);
|
|
555
|
+
const result = await api().get('agent-1');
|
|
556
|
+
expect(result).toEqual(agent);
|
|
557
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
558
|
+
expect(url).toContain('/agents/agent-1');
|
|
559
|
+
expect(init.method).toBe('GET');
|
|
560
|
+
});
|
|
561
|
+
it('should POST /agents/{id} for update()', async () => {
|
|
562
|
+
const agent = { id: 'agent-1', name: 'updated' };
|
|
563
|
+
mockJsonResponse(agent);
|
|
564
|
+
const result = await api().update('agent-1', { name: 'updated' });
|
|
565
|
+
expect(result).toEqual(agent);
|
|
566
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
567
|
+
expect(JSON.parse(init.body)).toEqual({ name: 'updated' });
|
|
568
|
+
});
|
|
569
|
+
it('should DELETE /agents/{id} for delete()', async () => {
|
|
570
|
+
mockJsonResponse(null);
|
|
571
|
+
await api().delete('agent-1');
|
|
572
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
573
|
+
expect(url).toContain('/agents/agent-1');
|
|
574
|
+
expect(init.method).toBe('DELETE');
|
|
575
|
+
});
|
|
576
|
+
it('should POST /agents/{id}/duplicate for duplicate()', async () => {
|
|
577
|
+
const agent = { id: 'agent-copy' };
|
|
578
|
+
mockJsonResponse(agent);
|
|
579
|
+
const result = await api().duplicate('agent-1');
|
|
580
|
+
expect(result).toEqual(agent);
|
|
581
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
582
|
+
expect(url).toContain('/agents/agent-1/duplicate');
|
|
583
|
+
expect(init.method).toBe('POST');
|
|
584
|
+
});
|
|
585
|
+
it('should POST /agents/{id}/versions/list for listVersions()', async () => {
|
|
586
|
+
const page = { items: [{ id: 'ver-1' }], next_cursor: null };
|
|
587
|
+
mockJsonResponse(page);
|
|
588
|
+
const result = await api().listVersions('agent-1', { limit: 5 });
|
|
589
|
+
expect(result).toEqual(page);
|
|
590
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
591
|
+
expect(url).toContain('/agents/agent-1/versions/list');
|
|
592
|
+
expect(JSON.parse(init.body)).toEqual({ limit: 5 });
|
|
593
|
+
});
|
|
594
|
+
it('should GET /agents/{id}/versions/{versionId} for getVersion()', async () => {
|
|
595
|
+
const version = { id: 'ver-1', agent_id: 'agent-1' };
|
|
596
|
+
mockJsonResponse(version);
|
|
597
|
+
const result = await api().getVersion('agent-1', 'ver-1');
|
|
598
|
+
expect(result).toEqual(version);
|
|
599
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
600
|
+
expect(url).toContain('/agents/agent-1/versions/ver-1');
|
|
601
|
+
expect(init.method).toBe('GET');
|
|
602
|
+
});
|
|
603
|
+
it('should POST visibility for updateVisibility()', async () => {
|
|
604
|
+
const agent = { id: 'agent-1', visibility: 'team' };
|
|
605
|
+
mockJsonResponse(agent);
|
|
606
|
+
await api().updateVisibility('agent-1', 'team');
|
|
607
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
608
|
+
expect(JSON.parse(init.body)).toEqual({ visibility: 'team' });
|
|
609
|
+
});
|
|
610
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { ApiKeyDTO, CursorListRequest, CursorListResponse } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* API Keys API
|
|
5
|
+
*/
|
|
6
|
+
export declare class ApiKeysAPI {
|
|
7
|
+
private readonly http;
|
|
8
|
+
constructor(http: HttpClient);
|
|
9
|
+
/**
|
|
10
|
+
* List API keys with cursor-based pagination
|
|
11
|
+
*/
|
|
12
|
+
list(params?: Partial<CursorListRequest>): Promise<CursorListResponse<ApiKeyDTO>>;
|
|
13
|
+
/**
|
|
14
|
+
* Create an API key
|
|
15
|
+
*/
|
|
16
|
+
create(data: {
|
|
17
|
+
name: string;
|
|
18
|
+
scopes?: string[];
|
|
19
|
+
}): Promise<ApiKeyDTO>;
|
|
20
|
+
/**
|
|
21
|
+
* Delete an API key
|
|
22
|
+
*/
|
|
23
|
+
delete(id: string): Promise<void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Keys API
|
|
3
|
+
*/
|
|
4
|
+
export class ApiKeysAPI {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.http = http;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* List API keys with cursor-based pagination
|
|
10
|
+
*/
|
|
11
|
+
async list(params) {
|
|
12
|
+
return this.http.request('post', '/apikeys/list', { data: params });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create an API key
|
|
16
|
+
*/
|
|
17
|
+
async create(data) {
|
|
18
|
+
return this.http.request('post', '/apikeys', { data });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Delete an API key
|
|
22
|
+
*/
|
|
23
|
+
async delete(id) {
|
|
24
|
+
return this.http.request('delete', `/apikeys/${id}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { ApiKeysAPI } from './api-keys';
|
|
3
|
+
const mockFetch = jest.fn();
|
|
4
|
+
global.fetch = mockFetch;
|
|
5
|
+
function mockJsonResponse(body) {
|
|
6
|
+
mockFetch.mockResolvedValueOnce({
|
|
7
|
+
ok: true,
|
|
8
|
+
status: 200,
|
|
9
|
+
text: () => Promise.resolve(JSON.stringify(body)),
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
describe('ApiKeysAPI', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
const api = () => new ApiKeysAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
17
|
+
it('should POST /apikeys/list for list()', async () => {
|
|
18
|
+
const page = { items: [{ id: 'key-1', name: 'CI' }], next_cursor: null };
|
|
19
|
+
mockJsonResponse(page);
|
|
20
|
+
const result = await api().list();
|
|
21
|
+
expect(result).toEqual(page);
|
|
22
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
23
|
+
expect(url).toContain('/apikeys/list');
|
|
24
|
+
expect(init.method).toBe('POST');
|
|
25
|
+
});
|
|
26
|
+
it('should POST /apikeys for create()', async () => {
|
|
27
|
+
const payload = { name: 'deploy-bot', scopes: ['tasks:read'] };
|
|
28
|
+
const key = { id: 'key-new', ...payload, key: 'inf_sk_abc' };
|
|
29
|
+
mockJsonResponse(key);
|
|
30
|
+
const result = await api().create(payload);
|
|
31
|
+
expect(result).toEqual(key);
|
|
32
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
33
|
+
expect(url).toContain('/apikeys');
|
|
34
|
+
expect(init.method).toBe('POST');
|
|
35
|
+
expect(JSON.parse(init.body)).toEqual(payload);
|
|
36
|
+
});
|
|
37
|
+
it('should DELETE /apikeys/{id} for delete()', async () => {
|
|
38
|
+
mockJsonResponse(null);
|
|
39
|
+
await api().delete('key-9');
|
|
40
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
41
|
+
expect(url).toContain('/apikeys/key-9');
|
|
42
|
+
expect(init.method).toBe('DELETE');
|
|
43
|
+
});
|
|
44
|
+
});
|
package/dist/api/apps.js
CHANGED
|
@@ -87,7 +87,7 @@ export class AppsAPI {
|
|
|
87
87
|
* Set the current (active) version of an app
|
|
88
88
|
*/
|
|
89
89
|
async setCurrentVersion(appId, versionId) {
|
|
90
|
-
return this.http.request('
|
|
90
|
+
return this.http.request('put', `/apps/${appId}/versions/${versionId}/current`);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
export function createAppsAPI(http) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|