@inferencesh/sdk 0.6.10 → 0.6.13

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 (75) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/actions.js +3 -3
  3. package/dist/agent/actions.test.js +231 -1
  4. package/dist/agent/api.test.js +18 -1
  5. package/dist/api/agents.d.ts +10 -0
  6. package/dist/api/agents.js +17 -3
  7. package/dist/api/agents.test.js +500 -0
  8. package/dist/api/api-keys.d.ts +24 -0
  9. package/dist/api/api-keys.js +26 -0
  10. package/dist/api/api-keys.test.d.ts +1 -0
  11. package/dist/api/api-keys.test.js +44 -0
  12. package/dist/api/apps.js +1 -1
  13. package/dist/api/apps.test.js +71 -3
  14. package/dist/api/chats.d.ts +14 -0
  15. package/dist/api/chats.js +18 -0
  16. package/dist/api/chats.test.js +52 -0
  17. package/dist/api/engines.d.ts +12 -0
  18. package/dist/api/engines.js +18 -0
  19. package/dist/api/engines.test.js +78 -0
  20. package/dist/api/files.test.js +183 -0
  21. package/dist/api/flow-runs.test.js +42 -0
  22. package/dist/api/flows.d.ts +4 -0
  23. package/dist/api/flows.js +6 -0
  24. package/dist/api/flows.test.js +75 -0
  25. package/dist/api/integrations.d.ts +41 -0
  26. package/dist/api/integrations.js +56 -0
  27. package/dist/api/integrations.test.d.ts +1 -0
  28. package/dist/api/integrations.test.js +109 -0
  29. package/dist/api/knowledge.d.ts +112 -0
  30. package/dist/api/knowledge.js +163 -0
  31. package/dist/api/knowledge.test.d.ts +1 -0
  32. package/dist/api/knowledge.test.js +238 -0
  33. package/dist/api/mcp-servers.d.ts +45 -0
  34. package/dist/api/mcp-servers.js +62 -0
  35. package/dist/api/mcp-servers.test.d.ts +1 -0
  36. package/dist/api/mcp-servers.test.js +98 -0
  37. package/dist/api/projects.d.ts +29 -0
  38. package/dist/api/projects.js +38 -0
  39. package/dist/api/projects.test.d.ts +1 -0
  40. package/dist/api/projects.test.js +61 -0
  41. package/dist/api/search.d.ts +21 -0
  42. package/dist/api/search.js +20 -0
  43. package/dist/api/search.test.d.ts +1 -0
  44. package/dist/api/search.test.js +39 -0
  45. package/dist/api/secrets.d.ts +29 -0
  46. package/dist/api/secrets.js +38 -0
  47. package/dist/api/secrets.test.d.ts +1 -0
  48. package/dist/api/secrets.test.js +61 -0
  49. package/dist/api/sessions.test.js +12 -0
  50. package/dist/api/tasks.d.ts +13 -1
  51. package/dist/api/tasks.js +18 -0
  52. package/dist/api/tasks.test.js +171 -0
  53. package/dist/api/teams.d.ts +71 -0
  54. package/dist/api/teams.js +92 -0
  55. package/dist/api/teams.test.d.ts +1 -0
  56. package/dist/api/teams.test.js +139 -0
  57. package/dist/client.test.js +112 -1
  58. package/dist/http/client.d.ts +5 -0
  59. package/dist/http/client.js +42 -23
  60. package/dist/http/client.test.js +245 -0
  61. package/dist/http/errors.test.js +13 -0
  62. package/dist/index.d.ts +25 -0
  63. package/dist/index.js +25 -0
  64. package/dist/proxy/hono.test.d.ts +1 -0
  65. package/dist/proxy/hono.test.js +90 -0
  66. package/dist/proxy/nextjs.test.d.ts +1 -0
  67. package/dist/proxy/nextjs.test.js +125 -0
  68. package/dist/proxy/remix.test.d.ts +1 -0
  69. package/dist/proxy/remix.test.js +66 -0
  70. package/dist/proxy/svelte.test.d.ts +1 -0
  71. package/dist/proxy/svelte.test.js +69 -0
  72. package/dist/tool-builder.test.js +11 -1
  73. package/dist/types.d.ts +110 -5
  74. package/dist/types.js +58 -2
  75. package/package.json +6 -6
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  official javascript/typescript sdk for [inference.sh](https://inference.sh) — the ai agent runtime for serverless ai inference.
9
9
 
10
- run ai models, build ai agents, and deploy generative ai applications with a simple api. access 250+ models including flux, stable diffusion, llms (claude, gpt, gemini), video generation (veo, seedance), and more.
10
+ run ai models, build ai agents, and deploy generative ai applications with a simple api. access models including flux, stable diffusion, llms (claude, gpt, gemini), video generation (veo, seedance), and more.
11
11
 
12
12
  ## Installation
13
13
 
@@ -4,7 +4,7 @@
4
4
  * Action creators that handle side effects (API calls, streaming).
5
5
  * These are created once per provider instance with access to dispatch.
6
6
  */
7
- import { ToolInvocationStatusAwaitingInput, ToolTypeClient, ChatStatusBusy, } from '../types';
7
+ import { ToolInvocationStatusAwaitingInput, ToolInvocationStatusInProgress, ToolTypeClient, ChatStatusBusy, } from '../types';
8
8
  import { StreamableManager } from '../http/streamable';
9
9
  import { PollManager } from '../http/poll';
10
10
  import { isAdHocConfig, extractClientToolHandlers } from './types';
@@ -35,12 +35,12 @@ export function createActions(ctx) {
35
35
  if (chatId && message.chat_id !== chatId && !message.chat_id.startsWith(chatId))
36
36
  return;
37
37
  dispatch({ type: 'UPDATE_MESSAGE', payload: message });
38
- // Check for client tool invocations that need execution
38
+ // Dispatch client tool handlers when ready (in_progress or awaiting_input for backwards compat)
39
39
  const clientToolHandlers = getClientToolHandlers();
40
40
  if (message.tool_invocations && chatId && clientToolHandlers.size > 0) {
41
41
  for (const invocation of message.tool_invocations) {
42
42
  if (invocation.type === ToolTypeClient &&
43
- invocation.status === ToolInvocationStatusAwaitingInput) {
43
+ (invocation.status === ToolInvocationStatusInProgress || invocation.status === ToolInvocationStatusAwaitingInput)) {
44
44
  // Skip if already dispatched
45
45
  if (dispatchedToolInvocations.has(invocation.id)) {
46
46
  continue;
@@ -1,4 +1,4 @@
1
- import { ChatStatusBusy, ToolInvocationStatusAwaitingInput, ToolTypeClient, } from '../types';
1
+ import { ChatStatusBusy, ToolInvocationStatusAwaitingInput, ToolInvocationStatusInProgress, ToolTypeClient, } from '../types';
2
2
  import { createActions } from './actions';
3
3
  import * as agentApi from './api';
4
4
  import { PollManager } from '../http/poll';
@@ -94,6 +94,12 @@ describe('createActions', () => {
94
94
  mockAgentApi.approveTool.mockResolvedValue(undefined);
95
95
  mockAgentApi.rejectTool.mockResolvedValue(undefined);
96
96
  mockAgentApi.alwaysAllowTool.mockResolvedValue(undefined);
97
+ mockAgentApi.uploadFile.mockResolvedValue({
98
+ id: 'file-1',
99
+ uri: 'inf://files/uploaded',
100
+ filename: 'notes.txt',
101
+ content_type: 'text/plain',
102
+ });
97
103
  });
98
104
  describe('updateMessage (via stream listeners)', () => {
99
105
  it('should ignore messages for a different chat when IDs do not prefix-match', async () => {
@@ -141,6 +147,30 @@ describe('createActions', () => {
141
147
  expect(handler).toHaveBeenCalledWith({ x: 1 });
142
148
  expect(mockAgentApi.submitToolResult).toHaveBeenCalledWith(ctx.client, 'tool-inv-ok', 'tool ok');
143
149
  });
150
+ it('should dispatch client tool handlers when status is in_progress', async () => {
151
+ const handler = jest.fn().mockResolvedValue('in progress ok');
152
+ const { ctx } = createTestContext({
153
+ getClientToolHandlers: () => new Map([['my_tool', handler]]),
154
+ });
155
+ const { internalActions } = createActions(ctx);
156
+ internalActions.streamChat('chat-full-id-123');
157
+ await Promise.resolve();
158
+ const onMessage = streamInstances[0].addEventListener.mock.calls.find(([event]) => event === 'chat_messages')?.[1];
159
+ onMessage(makeMessage({
160
+ chat_id: 'chat-short',
161
+ tool_invocations: [
162
+ {
163
+ id: 'tool-inv-progress',
164
+ type: ToolTypeClient,
165
+ status: ToolInvocationStatusInProgress,
166
+ function: { name: 'my_tool', arguments: { y: 2 } },
167
+ },
168
+ ],
169
+ }));
170
+ await Promise.resolve();
171
+ expect(handler).toHaveBeenCalledWith({ y: 2 });
172
+ expect(mockAgentApi.submitToolResult).toHaveBeenCalledWith(ctx.client, 'tool-inv-progress', 'in progress ok');
173
+ });
144
174
  it('should submit a JSON error when a client tool handler throws', async () => {
145
175
  const handler = jest.fn().mockRejectedValue(new Error('handler boom'));
146
176
  const { ctx } = createTestContext({
@@ -216,6 +246,49 @@ describe('createActions', () => {
216
246
  // Only the explicit stopStream dispatch, not a second from onEnd
217
247
  expect(idleDispatches).toHaveLength(1);
218
248
  });
249
+ it('should clear the manager ref before poll stop so onStop does not double-dispatch idle', async () => {
250
+ const onStatusChange = jest.fn();
251
+ const { ctx, dispatch, setStreamManager } = createTestContext({
252
+ getStreamEnabled: () => false,
253
+ callbacks: { onStatusChange },
254
+ });
255
+ const { internalActions } = createActions(ctx);
256
+ internalActions.streamChat('chat-full-id-123');
257
+ await Promise.resolve();
258
+ const manager = pollInstances[0];
259
+ internalActions.stopStream();
260
+ expect(setStreamManager).toHaveBeenCalledWith(undefined);
261
+ expect(manager.stop).toHaveBeenCalled();
262
+ manager.options.onStop?.();
263
+ const idleDispatches = dispatch.mock.calls.filter(([action]) => action.type === 'SET_CONNECTION_STATUS' && action.payload === 'idle');
264
+ expect(idleDispatches).toHaveLength(1);
265
+ expect(onStatusChange).toHaveBeenCalledWith('idle');
266
+ });
267
+ });
268
+ describe('stream and poll error callbacks', () => {
269
+ it('should forward stream errors to onError callback', async () => {
270
+ const onError = jest.fn();
271
+ const { ctx } = createTestContext({ callbacks: { onError } });
272
+ const { internalActions } = createActions(ctx);
273
+ internalActions.streamChat('chat-full-id-123');
274
+ await Promise.resolve();
275
+ const streamError = new Error('stream connection lost');
276
+ streamInstances[0].options.onError?.(streamError);
277
+ expect(onError).toHaveBeenCalledWith(streamError);
278
+ });
279
+ it('should forward poll manager errors to onError callback', async () => {
280
+ const onError = jest.fn();
281
+ const { ctx } = createTestContext({
282
+ getStreamEnabled: () => false,
283
+ callbacks: { onError },
284
+ });
285
+ const { internalActions } = createActions(ctx);
286
+ internalActions.streamChat('chat-full-id-123');
287
+ await Promise.resolve();
288
+ const pollError = new Error('poll transport failed');
289
+ pollInstances[0].options.onError?.(pollError);
290
+ expect(onError).toHaveBeenCalledWith(pollError);
291
+ });
219
292
  });
220
293
  describe('publicActions.sendMessage', () => {
221
294
  it('should call onChatCreated and start streaming for a new chat', async () => {
@@ -265,6 +338,19 @@ describe('createActions', () => {
265
338
  await publicActions.sendMessage(' ');
266
339
  expect(mockAgentApi.sendMessage).not.toHaveBeenCalled();
267
340
  });
341
+ it('should no-op when agent config is missing', async () => {
342
+ const consoleError = jest.spyOn(console, 'error').mockImplementation(() => { });
343
+ const { ctx, dispatch } = createTestContext({ getConfig: () => null });
344
+ const { publicActions } = createActions(ctx);
345
+ await publicActions.sendMessage('hello');
346
+ expect(mockAgentApi.sendMessage).not.toHaveBeenCalled();
347
+ expect(dispatch).not.toHaveBeenCalledWith({
348
+ type: 'SET_CONNECTION_STATUS',
349
+ payload: 'streaming',
350
+ });
351
+ expect(consoleError).toHaveBeenCalledWith('[AgentSDK] No agent config provided');
352
+ consoleError.mockRestore();
353
+ });
268
354
  });
269
355
  describe('streamChat error handling', () => {
270
356
  it('should reset to idle when initial fetchChat fails', async () => {
@@ -348,6 +434,77 @@ describe('createActions', () => {
348
434
  await Promise.resolve();
349
435
  expect(onError).toHaveBeenCalledWith(expect.objectContaining({ message: 'poll fetch failed' }));
350
436
  });
437
+ it('should not refetch chat when poll status is unchanged', async () => {
438
+ const { ctx: baseCtx } = createTestContext({ getStreamEnabled: () => false });
439
+ const { ctx } = createTestContext({
440
+ getStreamEnabled: () => false,
441
+ client: {
442
+ ...baseCtx.client,
443
+ http: { ...baseCtx.client.http, request: jest.fn().mockResolvedValue({ status: ChatStatusBusy }) },
444
+ },
445
+ });
446
+ const { internalActions } = createActions(ctx);
447
+ mockAgentApi.fetchChat.mockResolvedValue({
448
+ id: 'chat-full-id-123',
449
+ status: ChatStatusBusy,
450
+ chat_messages: [],
451
+ });
452
+ internalActions.streamChat('chat-full-id-123');
453
+ await Promise.resolve();
454
+ await pollInstances[0].options.onData?.({ status: ChatStatusBusy });
455
+ await Promise.resolve();
456
+ const fetchCountAfterFirst = mockAgentApi.fetchChat.mock.calls.length;
457
+ await pollInstances[0].options.onData?.({ status: ChatStatusBusy });
458
+ await Promise.resolve();
459
+ expect(mockAgentApi.fetchChat).toHaveBeenCalledTimes(fetchCountAfterFirst);
460
+ });
461
+ it('should dispatch client tools from poll path when status changes', async () => {
462
+ const handler = jest.fn().mockResolvedValue('poll ok');
463
+ const toolMessage = makeMessage({
464
+ chat_id: 'chat-short',
465
+ tool_invocations: [
466
+ {
467
+ id: 'tool-inv-poll',
468
+ type: ToolTypeClient,
469
+ status: ToolInvocationStatusInProgress,
470
+ function: { name: 'my_tool', arguments: { y: 2 } },
471
+ },
472
+ ],
473
+ });
474
+ const { ctx: baseCtx } = createTestContext({
475
+ getStreamEnabled: () => false,
476
+ getClientToolHandlers: () => new Map([['my_tool', handler]]),
477
+ });
478
+ const { ctx } = createTestContext({
479
+ getStreamEnabled: () => false,
480
+ getClientToolHandlers: () => new Map([['my_tool', handler]]),
481
+ client: {
482
+ ...baseCtx.client,
483
+ http: {
484
+ ...baseCtx.client.http,
485
+ request: jest.fn().mockResolvedValue({ status: ChatStatusBusy }),
486
+ },
487
+ },
488
+ });
489
+ const { internalActions } = createActions(ctx);
490
+ mockAgentApi.fetchChat
491
+ .mockResolvedValueOnce({
492
+ id: 'chat-full-id-123',
493
+ status: ChatStatusBusy,
494
+ chat_messages: [],
495
+ })
496
+ .mockResolvedValueOnce({
497
+ id: 'chat-full-id-123',
498
+ status: ChatStatusBusy,
499
+ chat_messages: [toolMessage],
500
+ });
501
+ internalActions.streamChat('chat-full-id-123');
502
+ await Promise.resolve();
503
+ await pollInstances[0].options.onData?.({ status: 'idle' });
504
+ await new Promise((resolve) => setImmediate(resolve));
505
+ expect(handler).toHaveBeenCalledWith({ y: 2 });
506
+ expect(mockAgentApi.submitToolResult).toHaveBeenCalledWith(ctx.client, 'tool-inv-poll', 'poll ok');
507
+ });
351
508
  });
352
509
  describe('client tool deduplication', () => {
353
510
  it('should not submit the same client tool invocation twice', async () => {
@@ -378,6 +535,14 @@ describe('createActions', () => {
378
535
  });
379
536
  });
380
537
  describe('publicActions lifecycle', () => {
538
+ it('uploadFile should delegate to the API layer', async () => {
539
+ const file = new File(['hello'], 'notes.txt', { type: 'text/plain' });
540
+ const { ctx } = createTestContext();
541
+ const { publicActions } = createActions(ctx);
542
+ const result = await publicActions.uploadFile(file);
543
+ expect(mockAgentApi.uploadFile).toHaveBeenCalledWith(ctx.client, file);
544
+ expect(result).toEqual(expect.objectContaining({ uri: 'inf://files/uploaded', filename: 'notes.txt' }));
545
+ });
381
546
  it('reset should stop stream and dispatch RESET', async () => {
382
547
  const { ctx, dispatch } = createTestContext();
383
548
  const { publicActions } = createActions(ctx);
@@ -391,6 +556,12 @@ describe('createActions', () => {
391
556
  publicActions.stopGeneration();
392
557
  expect(mockAgentApi.stopChat).toHaveBeenCalledWith(ctx.client, 'chat-short');
393
558
  });
559
+ it('stopGeneration should no-op when there is no chatId', () => {
560
+ const { ctx } = createTestContext({ getChatId: () => null });
561
+ const { publicActions } = createActions(ctx);
562
+ publicActions.stopGeneration();
563
+ expect(mockAgentApi.stopChat).not.toHaveBeenCalled();
564
+ });
394
565
  it('submitToolResult should set error state when API fails', async () => {
395
566
  mockAgentApi.submitToolResult.mockRejectedValueOnce(new Error('submit failed'));
396
567
  const onError = jest.fn();
@@ -416,6 +587,38 @@ describe('createActions', () => {
416
587
  payload: 'idle',
417
588
  });
418
589
  });
590
+ it('reset should allow the same client tool invocation to dispatch again', async () => {
591
+ const handler = jest.fn().mockResolvedValue('ok');
592
+ const { ctx } = createTestContext({
593
+ getClientToolHandlers: () => new Map([['my_tool', handler]]),
594
+ });
595
+ const { publicActions, internalActions } = createActions(ctx);
596
+ internalActions.streamChat('chat-full-id-123');
597
+ await Promise.resolve();
598
+ const onMessage = streamInstances[0].addEventListener.mock.calls.find(([event]) => event === 'chat_messages')?.[1];
599
+ const toolMessage = makeMessage({
600
+ chat_id: 'chat-short',
601
+ tool_invocations: [
602
+ {
603
+ id: 'tool-inv-reset',
604
+ type: ToolTypeClient,
605
+ status: ToolInvocationStatusAwaitingInput,
606
+ function: { name: 'my_tool', arguments: {} },
607
+ },
608
+ ],
609
+ });
610
+ onMessage(toolMessage);
611
+ await new Promise((resolve) => setImmediate(resolve));
612
+ expect(handler).toHaveBeenCalledTimes(1);
613
+ publicActions.reset();
614
+ internalActions.streamChat('chat-full-id-123');
615
+ await Promise.resolve();
616
+ const onMessageAfterReset = streamInstances[1].addEventListener.mock.calls.find(([event]) => event === 'chat_messages')?.[1];
617
+ onMessageAfterReset(toolMessage);
618
+ await new Promise((resolve) => setImmediate(resolve));
619
+ expect(handler).toHaveBeenCalledTimes(2);
620
+ expect(mockAgentApi.submitToolResult).toHaveBeenCalledTimes(2);
621
+ });
419
622
  });
420
623
  describe('HIL tool actions', () => {
421
624
  it('approveTool should delegate to the API', async () => {
@@ -454,6 +657,33 @@ describe('createActions', () => {
454
657
  });
455
658
  expect(onError).toHaveBeenCalledWith(expect.objectContaining({ message: 'approve failed' }));
456
659
  });
660
+ it('rejectTool should set error state when API fails', async () => {
661
+ mockAgentApi.rejectTool.mockRejectedValueOnce(new Error('reject failed'));
662
+ const onError = jest.fn();
663
+ const { ctx, dispatch } = createTestContext({ callbacks: { onError } });
664
+ const { publicActions } = createActions(ctx);
665
+ await expect(publicActions.rejectTool('inv-1', 'unsafe')).rejects.toThrow('reject failed');
666
+ expect(dispatch).toHaveBeenCalledWith({
667
+ type: 'SET_CONNECTION_STATUS',
668
+ payload: 'error',
669
+ });
670
+ expect(onError).toHaveBeenCalledWith(expect.objectContaining({ message: 'reject failed' }));
671
+ });
672
+ it('alwaysAllowTool should set error state when API fails', async () => {
673
+ mockAgentApi.alwaysAllowTool.mockRejectedValueOnce(new Error('allow failed'));
674
+ const onError = jest.fn();
675
+ const { ctx, dispatch } = createTestContext({
676
+ getChatId: () => 'chat-short',
677
+ callbacks: { onError },
678
+ });
679
+ const { publicActions } = createActions(ctx);
680
+ await expect(publicActions.alwaysAllowTool('inv-1', 'my_tool')).rejects.toThrow('allow failed');
681
+ expect(dispatch).toHaveBeenCalledWith({
682
+ type: 'SET_CONNECTION_STATUS',
683
+ payload: 'error',
684
+ });
685
+ expect(onError).toHaveBeenCalledWith(expect.objectContaining({ message: 'allow failed' }));
686
+ });
457
687
  });
458
688
  describe('setChatId', () => {
459
689
  it('should no-op when the chat id is unchanged', async () => {
@@ -1,6 +1,6 @@
1
1
  import { HttpClient } from '../http/client';
2
2
  import { FilesAPI } from '../api/files';
3
- import { sendAdHocMessage, sendTemplateMessage, sendMessage, submitToolResult, approveTool, rejectTool, alwaysAllowTool, fetchChat, stopChat, getChatStreamConfig, } from './api';
3
+ import { sendAdHocMessage, sendTemplateMessage, sendMessage, submitToolResult, approveTool, rejectTool, alwaysAllowTool, fetchChat, stopChat, getChatStreamConfig, uploadFile, } from './api';
4
4
  import { ToolTypeClient } from '../types';
5
5
  const mockFetch = jest.fn();
6
6
  global.fetch = mockFetch;
@@ -205,4 +205,21 @@ describe('agent/api', () => {
205
205
  expect(config.headers).toEqual(expect.objectContaining({ Authorization: expect.stringContaining('Bearer') }));
206
206
  });
207
207
  });
208
+ describe('uploadFile', () => {
209
+ it('should delegate to client.files.upload and return the uploaded file ref', async () => {
210
+ const client = makeClient();
211
+ const fileRecord = {
212
+ id: 'file-1',
213
+ uri: 'inf://files/uploaded',
214
+ filename: 'notes.txt',
215
+ content_type: 'text/plain',
216
+ };
217
+ const uploadSpy = jest.spyOn(client.files, 'upload').mockResolvedValue(fileRecord);
218
+ const file = new File(['hello'], 'notes.txt', { type: 'text/plain' });
219
+ const result = await uploadFile(client, file);
220
+ expect(uploadSpy).toHaveBeenCalledWith(file);
221
+ expect(result).toEqual(fileRecord);
222
+ uploadSpy.mockRestore();
223
+ });
224
+ });
208
225
  });
@@ -144,10 +144,20 @@ export declare class AgentsAPI {
144
144
  * Get a specific agent version
145
145
  */
146
146
  getVersion(agentId: string, versionId: string): Promise<AgentVersionDTO>;
147
+ /**
148
+ * Get an agent by namespace/name (e.g., "inference/my-agent")
149
+ */
150
+ getByName(namespace: string, name: string): Promise<AgentDTO>;
147
151
  /**
148
152
  * Get internal tools for the agent
149
153
  */
150
154
  getInternalTools(): Promise<InternalToolDefinition[]>;
155
+ /**
156
+ * Get A2A protocol agent card for an agent.
157
+ * Returns raw JSON (not wrapped in {data:...}) for direct upload to GCP Producer Portal.
158
+ * Spec: https://a2a-protocol.org/latest/specification/
159
+ */
160
+ getA2ACard(agentId: string): Promise<Record<string, unknown>>;
151
161
  /**
152
162
  * Create an agent instance for chat interactions
153
163
  */
@@ -1,6 +1,6 @@
1
1
  import { StreamableManager } from '../http/streamable';
2
2
  import { PollManager } from '../http/poll';
3
- import { ToolTypeClient, ToolInvocationStatusAwaitingInput, ChatStatusBusy, } from '../types';
3
+ import { ToolTypeClient, ToolInvocationStatusAwaitingInput, ToolInvocationStatusInProgress, ChatStatusBusy, } from '../types';
4
4
  /**
5
5
  * Agent for chat interactions
6
6
  *
@@ -166,7 +166,7 @@ export class Agent {
166
166
  for (const inv of message.tool_invocations) {
167
167
  if (this.dispatchedToolCalls.has(inv.id))
168
168
  continue;
169
- if (inv.type === ToolTypeClient && inv.status === ToolInvocationStatusAwaitingInput) {
169
+ if (inv.type === ToolTypeClient && (inv.status === ToolInvocationStatusInProgress || inv.status === ToolInvocationStatusAwaitingInput)) {
170
170
  this.dispatchedToolCalls.add(inv.id);
171
171
  options.onToolCall({
172
172
  id: inv.id,
@@ -222,7 +222,7 @@ export class Agent {
222
222
  for (const inv of message.tool_invocations) {
223
223
  if (this.dispatchedToolCalls.has(inv.id))
224
224
  continue;
225
- if (inv.type === ToolTypeClient && inv.status === ToolInvocationStatusAwaitingInput) {
225
+ if (inv.type === ToolTypeClient && (inv.status === ToolInvocationStatusInProgress || inv.status === ToolInvocationStatusAwaitingInput)) {
226
226
  this.dispatchedToolCalls.add(inv.id);
227
227
  options.onToolCall({
228
228
  id: inv.id,
@@ -319,12 +319,26 @@ export class AgentsAPI {
319
319
  async getVersion(agentId, versionId) {
320
320
  return this.http.request('get', `/agents/${agentId}/versions/${versionId}`);
321
321
  }
322
+ /**
323
+ * Get an agent by namespace/name (e.g., "inference/my-agent")
324
+ */
325
+ async getByName(namespace, name) {
326
+ return this.http.request('get', `/agents/${namespace}/${name}`);
327
+ }
322
328
  /**
323
329
  * Get internal tools for the agent
324
330
  */
325
331
  async getInternalTools() {
326
332
  return this.http.request('get', '/agents/internal-tools');
327
333
  }
334
+ /**
335
+ * Get A2A protocol agent card for an agent.
336
+ * Returns raw JSON (not wrapped in {data:...}) for direct upload to GCP Producer Portal.
337
+ * Spec: https://a2a-protocol.org/latest/specification/
338
+ */
339
+ async getA2ACard(agentId) {
340
+ return this.http.request('get', `/agents/${agentId}/card`);
341
+ }
328
342
  // ==========================================================================
329
343
  // Agent Runtime (chat interactions)
330
344
  // ==========================================================================