@inferencesh/sdk 0.6.7 → 0.6.10

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 CHANGED
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.8] - 2026-05-20
11
+
12
+ ### Added
13
+
14
+ - README tool builder section: `httpTool`/`callTool` auth, `mcpTool`, and builder comparison table
15
+ - `examples/tool-builder.ts` demonstrates HTTP and MCP tool schemas
16
+
17
+ - Typed SDK constants for integrations: `IntegrationProvider*`, `IntegrationAuthType*`, `IntegrationStatus*`
18
+ - `IntegrationDTO` fields (`provider`, `type`, `auth`, `status`) now use those typed aliases
19
+ - Additional `InstanceStatus*` constants (`creating`, `pending_provider`, `error`, `deleting`)
20
+ - `ToolParamType*` constants for JSON Schema tool parameter types (distinct from `ToolCallType`)
21
+
22
+ ## [0.6.7] - 2026-05-19
23
+
24
+ ### Added
25
+
26
+ - `client.sessions` API: `get`, `list`, `keepalive`, and `end` for session lifecycle management
27
+ - Session error types: `SessionNotFoundError`, `SessionExpiredError`, `SessionEndedError`
28
+ - Agent chat: `sendMessage` file attachments (upload `Blob` or reuse uploaded file `uri`)
29
+ - Agent lifecycle: `stopChat()`, `reset()`, and `agent.run()` for structured output via polling
30
+ - Task streaming: `onPartialUpdate` callback for partial NDJSON stream payloads
31
+ - Client config: `stream` and `pollIntervalMs` for global streaming vs status polling
32
+
33
+ ### Changed
34
+
35
+ - README documents ad-hoc agent field names (`core_app`, `system_prompt`) and tool builder API
36
+ - Polling mode: `run()` rejects if full task fetch fails after a status transition
37
+
10
38
  ## [0.1.1] - 2024-11-30
11
39
 
12
40
  ### Added
@@ -42,6 +70,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
42
70
  - Configurable reconnection behavior
43
71
  - Comprehensive error handling
44
72
 
45
- [Unreleased]: https://github.com/inference-sh/sdk-js/compare/v0.1.0...HEAD
73
+ [Unreleased]: https://github.com/inference-sh/sdk-js/compare/v0.6.8...HEAD
74
+ [0.6.8]: https://github.com/inference-sh/sdk-js/compare/v0.6.7...v0.6.8
75
+ [0.6.7]: https://github.com/inference-sh/sdk-js/compare/v0.6.6...v0.6.7
76
+ [0.1.1]: https://github.com/inference-sh/sdk-js/compare/v0.1.0...v0.1.1
46
77
  [0.1.0]: https://github.com/inference-sh/sdk-js/releases/tag/v0.1.0
47
78
 
package/README.md CHANGED
@@ -90,18 +90,43 @@ console.log('Status:', task.status);
90
90
 
91
91
  ### Real-time Status Updates
92
92
 
93
+ By default, the client streams task progress over NDJSON (`/tasks/{id}/stream`) and invokes `onUpdate` as the task changes. Use `onPartialUpdate` when you only need specific fields from a partial stream payload:
94
+
93
95
  ```typescript
94
- const result = await client.tasks.run(
96
+ const result = await client.run(
95
97
  { app: 'my-app', input: { prompt: 'hello' } },
96
98
  {
97
99
  onUpdate: (update) => {
98
100
  console.log('Status:', update.status);
99
101
  console.log('Progress:', update.logs);
100
- }
102
+ },
103
+ onPartialUpdate: (update, fields) => {
104
+ console.log('Changed fields:', fields, update.status);
105
+ },
101
106
  }
102
107
  );
103
108
  ```
104
109
 
110
+ ### Streaming vs Polling
111
+
112
+ SSE/NDJSON streaming is the default. For edge runtimes that cannot keep long-lived connections open (Convex actions, Cloudflare Workers, etc.), disable streaming and use lightweight status polling instead:
113
+
114
+ ```typescript
115
+ const client = inference({
116
+ apiKey: 'your-api-key',
117
+ stream: false, // poll /tasks/{id}/status instead of streaming
118
+ pollIntervalMs: 2000, // default: 2000
119
+ });
120
+
121
+ // Per-call override
122
+ const result = await client.run(
123
+ { app: 'my-app', input: { prompt: 'hello' } },
124
+ { stream: false, onUpdate: (u) => console.log(u.status) }
125
+ );
126
+ ```
127
+
128
+ In polling mode, the SDK checks `/tasks/{id}/status` and fetches the full task when the status changes. If that fetch fails after a status transition, `run()` rejects with the underlying error.
129
+
105
130
  ### Batch Processing
106
131
 
107
132
  ```typescript
@@ -196,6 +221,58 @@ const result = await client.tasks.run({
196
221
  - Maximum timeout: 3600 seconds (1 hour)
197
222
  - Each successful call resets the idle timer
198
223
 
224
+ #### Session management API
225
+
226
+ Manage sessions directly without running a task:
227
+
228
+ ```typescript
229
+ // Inspect a session
230
+ const info = await client.sessions.get(sessionId);
231
+ console.log(info.status, info.expires_at, info.call_count);
232
+
233
+ // List active sessions
234
+ const sessions = await client.sessions.list();
235
+
236
+ // Extend idle timeout without a task call (sliding window)
237
+ await client.sessions.keepalive(sessionId);
238
+
239
+ // Release the worker immediately
240
+ await client.sessions.end(sessionId);
241
+ ```
242
+
243
+ #### Session errors
244
+
245
+ ```typescript
246
+ import {
247
+ SessionNotFoundError,
248
+ SessionExpiredError,
249
+ SessionEndedError,
250
+ } from '@inferencesh/sdk';
251
+
252
+ try {
253
+ await client.tasks.run({
254
+ app: 'my-stateful-app',
255
+ input: { prompt: 'hello' },
256
+ session: sessionId,
257
+ });
258
+ } catch (error) {
259
+ if (
260
+ error instanceof SessionNotFoundError ||
261
+ error instanceof SessionExpiredError ||
262
+ error instanceof SessionEndedError
263
+ ) {
264
+ // Start a new session and retry
265
+ const result = await client.tasks.run({
266
+ app: 'my-stateful-app',
267
+ input: { prompt: 'hello' },
268
+ session: 'new',
269
+ });
270
+ } else {
271
+ throw error;
272
+ }
273
+ }
274
+ ```
275
+
199
276
  For complete session documentation including error handling, best practices, and advanced patterns, see the [Sessions Developer Guide](https://inference.sh/docs/extend/sessions).
200
277
 
201
278
  ## Agent Chat
@@ -240,27 +317,111 @@ import { inference, tool, string } from '@inferencesh/sdk';
240
317
 
241
318
  const client = inference({ apiKey: 'your-api-key' });
242
319
 
243
- // Create ad-hoc agent
320
+ // Create ad-hoc agent (config uses API field names: core_app, system_prompt)
321
+ const weatherTool = tool('get_weather')
322
+ .describe('Get current weather')
323
+ .param('city', string('City name'))
324
+ .build();
325
+
244
326
  const agent = client.agents.create({
245
- coreApp: 'infsh/claude-sonnet-4@abc123', // LLM to use
246
- systemPrompt: 'You are a helpful assistant.',
247
- tools: [
248
- tool('get_weather')
249
- .description('Get current weather')
250
- .params({ city: string('City name') })
251
- .handler(async (args) => {
252
- // Your tool logic here
253
- return JSON.stringify({ temp: 72, conditions: 'sunny' });
254
- })
255
- .build()
256
- ]
327
+ core_app: { ref: 'infsh/claude-sonnet-4@abc123' },
328
+ system_prompt: 'You are a helpful assistant.',
329
+ tools: [weatherTool], // only schemas are sent to the API; handlers stay client-side
257
330
  });
258
331
 
259
332
  await agent.sendMessage('What is the weather in Paris?', {
260
333
  onMessage: (msg) => console.log(msg),
261
334
  onToolCall: async (call) => {
262
- // Tool handlers are auto-executed if defined
263
- }
335
+ const result = await runMyClientTool(call.name, call.args);
336
+ await agent.submitToolResult(call.id, result);
337
+ },
338
+ });
339
+ ```
340
+
341
+ For multi-turn chats, the SDK opens the chat stream before sending the next message so updates are not missed. Use `stopChat()` to cancel in-flight generation (`POST /chats/{id}/stop`), and `reset()` to clear the current chat and start fresh.
342
+
343
+ ### Tool builder
344
+
345
+ Use the fluent builders to define `AgentTool` schemas. Client tools (`tool`) run in your app via `onToolCall`; server-side tools run on inference.sh.
346
+
347
+ | Builder | Runs on | Description |
348
+ |---------|---------|-------------|
349
+ | `tool(name)` | Client | Local handler; only the schema is sent to the API |
350
+ | `appTool(name, appRef)` | Server | Invoke another inference app |
351
+ | `agentTool(name, agentRef)` | Server | Delegate to a sub-agent |
352
+ | `httpTool(name, url)` / `callTool(name, url)` | Server | HTTP request with credential injection (preferred over `webhookTool`) |
353
+ | `webhookTool(name, url)` | Server | Unsigned webhook (legacy; use `httpTool` for new tools) |
354
+ | `mcpTool(name, integrationId, toolName)` | Server | Call a tool on a connected MCP integration |
355
+ | `internalTools()` | Server | Built-in plan, memory, and widget tools |
356
+
357
+ ```typescript
358
+ import {
359
+ inference,
360
+ tool,
361
+ appTool,
362
+ httpTool,
363
+ mcpTool,
364
+ internalTools,
365
+ string,
366
+ IntegrationProviderGoogle,
367
+ } from '@inferencesh/sdk';
368
+
369
+ const clientTool = tool('get_weather')
370
+ .describe('Get current weather')
371
+ .param('city', string('City name'))
372
+ .build();
373
+
374
+ // HTTP tool with OAuth integration credentials (injected server-side)
375
+ const gmailSend = httpTool('gmail_send', 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send')
376
+ .describe('Send an email via Gmail')
377
+ .method('POST')
378
+ .auth({ integration: IntegrationProviderGoogle, integrationId: 'your-integration-id' })
379
+ .build();
380
+
381
+ // API key or bearer auth
382
+ const fetchData = httpTool('fetch', 'https://api.example.com/data')
383
+ .method('GET')
384
+ .auth({ apiKey: 'YOUR_KEY', header: 'X-API-Key' }) // default header: X-API-Key
385
+ .header('Accept', 'application/json')
386
+ .build();
387
+
388
+ const bearerFetch = httpTool('bearer_fetch', 'https://api.example.com')
389
+ .auth({ bearer: 'YOUR_TOKEN' })
390
+ .build();
391
+
392
+ const imageGen = appTool('generate_image', 'infsh/flux-schnell@abc123')
393
+ .param('prompt', string('Image description'))
394
+ .requireApproval()
395
+ .build();
396
+
397
+ const mcpSearch = mcpTool('notion_search', 'your-mcp-integration-id', 'search')
398
+ .describe('Search Notion pages')
399
+ .param('query', string('Search query'))
400
+ .build();
401
+
402
+ const agent = client.agents.create({
403
+ core_app: { ref: 'infsh/claude-sonnet-4@latest' },
404
+ system_prompt: 'You are helpful.',
405
+ tools: [clientTool, gmailSend, imageGen, mcpSearch],
406
+ internal_tools: internalTools().memory().build(),
407
+ });
408
+ ```
409
+
410
+ `callTool` is an alias for `httpTool`. Run `npx tsx examples/tool-builder.ts` for more schema examples (no API key required).
411
+
412
+ ### File attachments
413
+
414
+ Pass files in `sendMessage` options. `Blob` values are uploaded first; objects with a `uri` (already uploaded via `client.files.upload`) are attached as-is:
415
+
416
+ ```typescript
417
+ const uploaded = await client.files.upload(imageBlob, {
418
+ filename: 'photo.png',
419
+ contentType: 'image/png',
420
+ });
421
+
422
+ await agent.sendMessage('Describe this image', {
423
+ files: [imageBlob, uploaded], // Blob uploads; FileDTO reuses uri
424
+ onMessage: (msg) => console.log(msg),
264
425
  });
265
426
  ```
266
427
 
@@ -283,21 +444,23 @@ const agent = client.agents.create({
283
444
  internal_tools: { finish: true },
284
445
  });
285
446
 
286
- const response = await agent.sendMessage('Analyze: Great product!');
447
+ const output = await agent.run('Analyze: Great product!');
287
448
  ```
288
449
 
450
+ `agent.run()` sends a message with polling (no SSE), waits until the chat is idle, and returns `chat.output` (parsed finish-tool result, or `null` if none).
451
+
289
452
  ### Agent Methods
290
453
 
291
454
  | Method | Description |
292
455
  |--------|-------------|
293
- | `sendMessage(text, options?)` | Send a message to the agent |
294
- | `getChat(chatId?)` | Get chat history |
295
- | `stopChat(chatId?)` | Stop current generation |
296
- | `submitToolResult(toolId, resultOrAction)` | Submit result for a client tool (string or {action, form_data}) |
297
- | `streamMessages(chatId?, options?)` | Stream message updates |
298
- | `streamChat(chatId?, options?)` | Stream chat updates |
299
- | `disconnect()` | Clean up streams |
300
- | `reset()` | Start a new conversation |
456
+ | `sendMessage(text, options?)` | Send a message; streams or polls until idle when callbacks or `stream: false` |
457
+ | `run(text, options?)` | Send and return structured `chat.output` (always uses polling) |
458
+ | `getChat(chatId?)` | Get the current or specified chat (`chat_messages` on the returned chat) |
459
+ | `stopChat()` | Stop generation for the current chat (no-op if no active chat) |
460
+ | `submitToolResult(toolId, resultOrAction)` | Submit result for a client tool (string or `{action, form_data}`) |
461
+ | `startStreaming(options?)` | Manually attach to `/chats/{id}/stream` for the current chat |
462
+ | `disconnect()` | Stop active stream/poll connections |
463
+ | `reset()` | Disconnect and clear chat state so the next message starts a new chat |
301
464
 
302
465
  ## API Reference
303
466
 
@@ -309,6 +472,9 @@ Creates a new inference client.
309
472
  |-----------|------|----------|-------------|
310
473
  | `config.apiKey` | `string` | Yes | Your inference.sh API key |
311
474
  | `config.baseUrl` | `string` | No | Custom API URL (default: `https://api.inference.sh`) |
475
+ | `config.stream` | `boolean` | No | Use NDJSON streaming (`true`, default) or status polling (`false`) |
476
+ | `config.pollIntervalMs` | `number` | No | Poll interval when `stream: false` (default: `2000`) |
477
+ | `config.proxyUrl` | `string` | No | Proxy base URL for frontend apps (keeps API keys server-side) |
312
478
 
313
479
  ### `client.tasks.run(params, options?)`
314
480
 
@@ -331,10 +497,11 @@ Runs a task on inference.sh.
331
497
  | Option | Type | Default | Description |
332
498
  |--------|------|---------|-------------|
333
499
  | `wait` | `boolean` | `true` | Wait for task completion |
334
- | `onUpdate` | `function` | - | Callback for status updates |
335
- | `autoReconnect` | `boolean` | `true` | Auto-reconnect on connection loss |
336
- | `maxReconnects` | `number` | `5` | Max reconnection attempts |
337
- | `reconnectDelayMs` | `number` | `1000` | Delay between reconnects (ms) |
500
+ | `stream` | `boolean` | client default | Use NDJSON streaming or status polling |
501
+ | `pollIntervalMs` | `number` | client default | Poll interval when `stream: false` |
502
+ | `onUpdate` | `function` | - | Callback for task updates (full fetch on status change when polling) |
503
+ | `onPartialUpdate` | `function` | - | Callback for partial NDJSON stream updates `(task, fields)` |
504
+ | `maxReconnects` | `number` | `5` | Max poll retries when `stream: false` |
338
505
 
339
506
  ### `client.tasks.get(taskId)`
340
507
 
@@ -357,9 +524,11 @@ Uploads a file to inference.sh.
357
524
  | `options.contentType` | `string` | MIME type |
358
525
  | `options.public` | `boolean` | Make file publicly accessible |
359
526
 
360
- ### `client.agents.create(templateOrConfig)`
527
+ ### `client.agents.create(templateOrConfig)` / `client.agent(...)`
361
528
 
362
- Creates an agent instance from a template or ad-hoc configuration.
529
+ Creates an agent instance from a template or ad-hoc configuration. `client.agent(...)` is an alias for `client.agents.create(...)`.
530
+
531
+ **`sendMessage` options:** `onMessage`, `onChat`, `onToolCall`, `files`, `stream`, `pollIntervalMs`. Client tools with status `awaiting_input` are dispatched once per invocation ID via `onToolCall`.
363
532
 
364
533
  **Template mode:**
365
534
  ```typescript
@@ -369,12 +538,21 @@ const agent = client.agents.create('namespace/name@version');
369
538
  **Ad-hoc mode:**
370
539
  ```typescript
371
540
  const agent = client.agents.create({
372
- coreApp: 'infsh/claude-sonnet-4@abc123',
373
- systemPrompt: 'You are helpful.',
374
- tools: [...]
541
+ core_app: { ref: 'infsh/claude-sonnet-4@abc123' },
542
+ system_prompt: 'You are helpful.',
543
+ tools: [...],
375
544
  });
376
545
  ```
377
546
 
547
+ ### `client.sessions`
548
+
549
+ | Method | HTTP | Description |
550
+ |--------|------|-------------|
551
+ | `get(sessionId)` | `GET /sessions/{id}` | Session metadata (`status`, `expires_at`, `call_count`, …) |
552
+ | `list()` | `GET /sessions` | All sessions (empty array if none) |
553
+ | `keepalive(sessionId)` | `POST /sessions/{id}/keepalive` | Reset idle expiration |
554
+ | `end(sessionId)` | `DELETE /sessions/{id}` | End session and release worker |
555
+
378
556
  ## Task Status Constants
379
557
 
380
558
  ```typescript
@@ -391,12 +569,103 @@ if (task.status === TaskStatusCompleted) {
391
569
  }
392
570
  ```
393
571
 
572
+ ## Integration Constants
573
+
574
+ `IntegrationDTO` fields (`provider`, `type`, `auth`, `status`) use typed string unions exported as constants:
575
+
576
+ ```typescript
577
+ import type { IntegrationDTO } from '@inferencesh/sdk';
578
+ import {
579
+ IntegrationProviderGoogle,
580
+ IntegrationAuthTypeOAuth,
581
+ IntegrationStatusConnected,
582
+ IntegrationStatusDisconnected,
583
+ IntegrationStatusExpired,
584
+ IntegrationStatusError,
585
+ isRequirementsNotMetException,
586
+ } from '@inferencesh/sdk';
587
+
588
+ function isGoogleConnected(integration: IntegrationDTO): boolean {
589
+ return (
590
+ integration.provider === IntegrationProviderGoogle &&
591
+ integration.status === IntegrationStatusConnected
592
+ );
593
+ }
594
+
595
+ // HTTP 412 when an app requires a missing secret, integration, or scope
596
+ try {
597
+ await client.run({ app: 'my-app', input: {} });
598
+ } catch (error) {
599
+ if (isRequirementsNotMetException(error)) {
600
+ for (const req of error.errors) {
601
+ if (req.type === 'integration' && req.action?.provider === IntegrationProviderGoogle) {
602
+ // User must connect Google — see https://inference.sh/docs/extend/integrations
603
+ }
604
+ }
605
+ }
606
+ }
607
+ ```
608
+
609
+ | Constant group | Values |
610
+ |----------------|--------|
611
+ | `IntegrationProvider*` | `google`, `slack`, `notion`, `github`, `x`, `microsoft`, `salesforce`, `discord`, `gcp`, `mcp`, `reddit` |
612
+ | `IntegrationAuthType*` | `service_account`, `oauth`, `api_key`, `wif`, `mcp` |
613
+ | `IntegrationStatus*` | `connected`, `disconnected`, `expired`, `error` |
614
+
615
+ ## Instance Status Constants
616
+
617
+ Use when working with engine instance APIs (`InstanceDTO.status`):
618
+
619
+ ```typescript
620
+ import {
621
+ InstanceStatusCreating,
622
+ InstanceStatusPendingProvider,
623
+ InstanceStatusPending,
624
+ InstanceStatusActive,
625
+ InstanceStatusError,
626
+ InstanceStatusDeleting,
627
+ InstanceStatusDeleted,
628
+ } from '@inferencesh/sdk';
629
+ ```
630
+
631
+ ## Tool Parameter Types
632
+
633
+ When building `AgentTool` schemas manually (outside the tool builder), use `ToolParamType*` for JSON Schema `type` fields:
634
+
635
+ ```typescript
636
+ import {
637
+ ToolParamTypeObject,
638
+ ToolParamTypeString,
639
+ ToolParamTypeInteger,
640
+ ToolParamTypeNumber,
641
+ ToolParamTypeBoolean,
642
+ ToolParamTypeArray,
643
+ ToolParamTypeNull,
644
+ } from '@inferencesh/sdk';
645
+
646
+ const schema = {
647
+ type: ToolParamTypeObject,
648
+ properties: {
649
+ city: { type: ToolParamTypeString, description: 'City name' },
650
+ },
651
+ required: ['city'],
652
+ };
653
+ ```
654
+
655
+ The fluent tool builder (`string()`, `number()`, `object()`, …) infers these types automatically.
656
+
394
657
  ## TypeScript Support
395
658
 
396
659
  This SDK is written in TypeScript and includes full type definitions. All types are exported:
397
660
 
398
661
  ```typescript
399
- import type { Task, ApiTaskRequest, RunOptions } from '@inferencesh/sdk';
662
+ import type {
663
+ Task,
664
+ ApiAppRunRequest,
665
+ RunOptions,
666
+ IntegrationDTO,
667
+ AgentTool,
668
+ } from '@inferencesh/sdk';
400
669
  ```
401
670
 
402
671
  ## Requirements
@@ -0,0 +1 @@
1
+ export {};