@inferencesh/sdk 0.20.0 → 0.21.0

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,63 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.21.0] - 2026-10-08
11
+
12
+ Breaking: `AgentClient.http` requires `fetch`, the `AppTag*` constants are removed, and `StreamableManagerOptions` is a type alias. See Changed and Removed.
13
+
14
+ ### Added
15
+
16
+ - `HttpClient.fetch(endpoint, init?)`: fetches an endpoint with the client's auth, headers and credentials mode and resolves with the raw `Response`, for bodies `request()` does not parse (streams, text). A refused response throws the error `request()` would (`InferenceError`, `RequirementsNotMetException`). Type `HttpFetchInit`.
17
+ - `StreamRequest` and `StreamRequestInit`: a function that sends a stream's request. `StreamableManager` takes `{ request }` in place of `{ url, headers, credentials }`, and `streamable()` / `streamableRaw()` take one in place of the URL. With `(init) => http.fetch(endpoint, init)` the stream carries the token of the moment it connects and a refusal goes through `onError`. Type `StreamableSource`.
18
+ - `onError` gets a third argument, `FailedRequest` (`{ token }`): the bearer token the failed request carried, so a handler that refreshes credentials can tell a request sent before the refresh from one sent after it.
19
+ - `getToken` may be async (`() => string | null | undefined | Promise<string | null | undefined>`). `request()`, `fetch()` and the EventSource handshake wait for it.
20
+ - `HttpClient.request()` takes `handleErrors` (default `true`). With `handleErrors: false` the caller handles the request's failure itself: `onError` is not called for it, nothing is retried, and the error is thrown as it is.
21
+
22
+ - Types: `StoreCategoryDTO` and `StoreTagDTO` (`GET /store/categories`, `GET /store/tags`); the decision app contract (`DecisionInput`, `DecisionVisionInput`, `DecisionOutput` and its question and answer types); `ErrorCodeImpersonationReasonRequired`.
23
+
24
+ ### Changed
25
+
26
+ - `AgentClient.http` requires `fetch` and no longer requires `getStreamableConfig`. A hand-built `AgentClient` must add `http.fetch`; `Inference` and `HttpClient` already have it.
27
+ - The streams the SDK opens itself (`tasks.run()` and `tasks.watch()`, `Agent` chat streams, the agent chat in `@inferencesh/sdk/agent`) go through `HttpClient.fetch`. A stream the api refuses now reaches `onError` like a refused request, and fails with an `InferenceError` (`statusCode`, `responseBody`) where it failed with `Error('HTTP <status>: <body>')`.
28
+ - A request that got no response (the network failed) goes to `onError` from `fetch()` and the EventSource handshake too, as it already did from `request()`. The failure is thrown unless the handler's retry resolves with a response. A request aborted through its signal is not handed to `onError`.
29
+ - The EventSource handshake and `fetch()` take what `onError` resolves with only when it is a `Response`; anything else leaves the refused response in place.
30
+ - `StreamableManagerOptions` is a type alias (`StreamableSource & { ... }`), no longer an interface: it cannot be extended with `interface X extends StreamableManagerOptions<T>`; use an intersection.
31
+
32
+ ### Removed
33
+
34
+ - The `AppTag*` constants (`AppTagTextToImage` and the rest). App tags come from the store: `GET /store/tags`, typed `StoreTagDTO`.
35
+
36
+ ### Deprecated
37
+
38
+ - `HttpClient.getStreamableConfig()`: a request made from the config bypasses `onError` and carries the token of the moment the config was read. Use `fetch()` or a `StreamRequest`. It throws when `getToken` is async.
39
+
40
+ ## [0.20.1] - 2026-10-08
41
+
42
+ ### Added
43
+
44
+ - Types: app tags and the decision app category.
45
+
46
+ ## [0.20.0] - 2026-10-07
47
+
48
+ ### Added
49
+
50
+ - `tasks.files(id)`, `tasks.deleteFiles(id)` and `tasks.delete(id, { files })`: list a task's files and delete them with or without the task.
51
+ - Types: task files, MCP Apps (`_meta.ui`, `AppUIRef`, the MCP tool call UI fields), remote tags and policy rules, `AppDTO.resolved_function`, `DescriptionLimit`.
52
+
53
+ ### Changed
54
+
55
+ - Types: forms lose the bounty and reward fields; a bounty's proof form drops `proof_min_length`.
56
+
57
+ ### Security
58
+
59
+ - `next` ^16.3.6 (resolves 16.3.8) for GHSA-vcvr-r3jv-pc5j.
60
+
61
+ ## [0.19.0] - 2026-10-04
62
+
63
+ ### Changed
64
+
65
+ - Types regenerated; the run parameters docs drop `params.variant`.
66
+
10
67
  ## [0.18.0] - 2026-10-04
11
68
 
12
69
  ### Added
@@ -109,11 +109,8 @@ export function createActions(ctx) {
109
109
  return;
110
110
  }
111
111
  // Single unified stream with TypedEvents (both Chat and ChatMessage events)
112
- const { url, headers, credentials } = api.getChatStreamConfig(client, id);
113
112
  const manager = new StreamableManager({
114
- url,
115
- headers,
116
- credentials,
113
+ request: api.chatStreamRequest(client, id),
117
114
  onError: (error) => {
118
115
  console.warn('[AgentSDK] Stream error:', error);
119
116
  callbacks.onError?.(error);
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import type { AlwaysAllowOptionsDTO, AlwaysAllowResultDTO, ChatAgentDTO, ChatDTO, ChatMessageDTO, ChatSettingsDTO, ChatSettingsRequest, InterruptDTO, ToolExplanationDTO } from '../types';
11
11
  import type { AgentOptions, AgentClient, AgentInfo, FileRef } from './types';
12
+ import type { StreamRequest } from '../http/streamable';
12
13
  export interface SendResult {
13
14
  chatId: string;
14
15
  userMessage: ChatMessageDTO;
@@ -69,8 +70,4 @@ export declare function updateChatSettings(client: AgentClient, chatId: string,
69
70
  export declare function resolveInterrupt(client: AgentClient, interruptId: string, decision: 'allow' | 'deny'): Promise<InterruptDTO>;
70
71
  export declare function listRunInterrupts(client: AgentClient, runId: string): Promise<InterruptDTO[]>;
71
72
  export declare function uploadFile(client: AgentClient, file: globalThis.File): Promise<FileRef>;
72
- export declare function getChatStreamConfig(client: AgentClient, chatId: string): {
73
- url: string;
74
- headers: Record<string, string>;
75
- credentials: RequestCredentials;
76
- };
73
+ export declare function chatStreamRequest(client: AgentClient, chatId: string): StreamRequest;
package/dist/agent/api.js CHANGED
@@ -203,6 +203,6 @@ export async function listRunInterrupts(client, runId) {
203
203
  export async function uploadFile(client, file) {
204
204
  return client.files.upload(file);
205
205
  }
206
- export function getChatStreamConfig(client, chatId) {
207
- return client.http.getStreamableConfig(`/chats/${chatId}/stream`);
206
+ export function chatStreamRequest(client, chatId) {
207
+ return (init) => client.http.fetch(`/chats/${chatId}/stream`, init);
208
208
  }
@@ -19,7 +19,7 @@ export interface AlwaysAllowChoice {
19
19
  */
20
20
  export interface AgentClient {
21
21
  /** HTTP client for API requests */
22
- http: Pick<HttpClient, 'request' | 'getStreamableConfig' | 'getStreamDefault' | 'getPollIntervalMs'>;
22
+ http: Pick<HttpClient, 'request' | 'fetch' | 'getStreamDefault' | 'getPollIntervalMs'>;
23
23
  /** Files API for uploads */
24
24
  files: {
25
25
  upload: (data: string | Blob | globalThis.File) => Promise<FileRef>;
@@ -209,13 +209,11 @@ export class Agent {
209
209
  streamUntilIdle(options, gate) {
210
210
  if (!this.chatId)
211
211
  return Promise.resolve();
212
- const { url, headers, credentials } = this.http.getStreamableConfig(`/chats/${this.chatId}/stream`);
212
+ const endpoint = `/chats/${this.chatId}/stream`;
213
213
  return new Promise((resolve) => {
214
214
  this.stream?.stop();
215
215
  this.stream = new StreamableManager({
216
- url,
217
- headers,
218
- credentials,
216
+ request: (init) => this.http.fetch(endpoint, init),
219
217
  });
220
218
  // Last chat/run observation was idle but the gate wasn't settled yet;
221
219
  // a terminal message for this turn can settle it.
package/dist/api/tasks.js CHANGED
@@ -110,7 +110,6 @@ export class TasksAPI {
110
110
  const { onUpdate, onPartialUpdate, onDelta } = options;
111
111
  // Accumulate state across partial updates to preserve fields like session_id
112
112
  let accumulatedTask = { ...task };
113
- const { url, headers, credentials } = this.http.getStreamableConfig(`/tasks/${task.id}/stream`);
114
113
  let streamManager;
115
114
  const done = new Promise((resolve, reject) => {
116
115
  const settle = (data, stripped) => {
@@ -128,9 +127,7 @@ export class TasksAPI {
128
127
  }
129
128
  };
130
129
  streamManager = new StreamableManager({
131
- url,
132
- headers,
133
- credentials,
130
+ request: (init) => this.http.fetch(`/tasks/${task.id}/stream`, init),
134
131
  onDelta,
135
132
  onData: (data) => {
136
133
  // Merge new data, preserving existing fields if not in update
@@ -1,10 +1,26 @@
1
1
  import type { Response } from './response';
2
2
  import { EventSource } from 'eventsource';
3
+ /** What a failed request was sent with, for an error handler that acts on it. */
4
+ export interface FailedRequest {
5
+ /**
6
+ * The bearer token the request carried, or undefined when it carried none
7
+ * (proxy mode, or no apiKey and getToken returned nothing). A handler that
8
+ * refreshes credentials compares it with the token in hand: when they
9
+ * differ, the credentials were already replaced and a retry is enough.
10
+ */
11
+ token: string | undefined;
12
+ }
3
13
  /**
4
14
  * Error handler that can intercept errors and optionally retry the request.
5
15
  * Return a promise to retry with a new result, or throw/rethrow to propagate the error.
16
+ * `retry` sends the request again with the token of that moment and does not
17
+ * come back through the handler. `request` describes the attempt that failed.
6
18
  */
7
- export type ErrorHandler = (error: unknown, retry: () => Promise<unknown>) => Promise<unknown>;
19
+ export type ErrorHandler = (error: unknown, retry: () => Promise<unknown>, request: FailedRequest) => Promise<unknown>;
20
+ /** Options of HttpClient.fetch: a fetch init whose credentials mode is the client's. */
21
+ export type HttpFetchInit = Omit<RequestInit, 'headers' | 'credentials'> & {
22
+ headers?: Record<string, string>;
23
+ };
8
24
  export type MessageHandler = (messages: import('../types').ResponseMessage[]) => void;
9
25
  export interface HttpClientConfig {
10
26
  /** Your inference.sh API key (required unless using proxyUrl) */
@@ -16,8 +32,11 @@ export interface HttpClientConfig {
16
32
  * When set, requests are routed through your proxy server to protect API keys.
17
33
  */
18
34
  proxyUrl?: string;
19
- /** Dynamic token getter (alternative to apiKey) */
20
- getToken?: () => string | null | undefined;
35
+ /**
36
+ * Dynamic token getter (alternative to apiKey), asked before every request.
37
+ * It may be async: the request waits for the token.
38
+ */
39
+ getToken?: () => string | null | undefined | Promise<string | null | undefined>;
21
40
  /** Dynamic headers */
22
41
  headers?: Record<string, string | (() => string | undefined)>;
23
42
  /** Request credentials mode */
@@ -71,15 +90,53 @@ export declare class HttpClient {
71
90
  isProxyMode(): boolean;
72
91
  /** Resolve dynamic headers */
73
92
  private resolveHeaders;
74
- /** Get authorization token */
75
- private getAuthToken;
93
+ /** The bearer token a request sent now carries; none through a proxy, which adds its own. */
94
+ private bearerToken;
95
+ /** The configured headers plus what identifies the request: its bearer, or through a proxy its target. */
96
+ private requestHeaders;
97
+ /**
98
+ * Where a request whose caller cannot set headers (EventSource) or reads
99
+ * the URL (streams) goes: the API, or the proxy with the target as a query
100
+ * parameter.
101
+ */
102
+ private streamUrl;
76
103
  /**
77
104
  * Make an HTTP request to the API
78
105
  */
79
- request<T, P extends object = Record<string, unknown>>(method: 'get' | 'post' | 'put' | 'delete', endpoint: string, options?: {
106
+ request<T, P extends object = Record<string, unknown>>(method: 'get' | 'post' | 'put' | 'delete', endpoint: string, { handleErrors, ...options }?: {
80
107
  params?: P;
81
108
  data?: unknown;
109
+ /**
110
+ * `false` when the caller handles this request's failure itself: the
111
+ * client's error handler (onError) is not consulted, so there is no
112
+ * prompt, no redirect and no retry, and the error is thrown as it is.
113
+ */
114
+ handleErrors?: boolean;
82
115
  }): Promise<Response<T>>;
116
+ /**
117
+ * Fetch an API endpoint with the client's auth, headers and credentials
118
+ * mode and resolve with the raw response, for bodies request() does not
119
+ * parse (streams, text). A failure goes through onError like one of
120
+ * request() does: a refused response, or a request that got no response.
121
+ * It resolves with the response onError's retry got; when onError resolves
122
+ * with anything else the failure stands and is thrown, the error request()
123
+ * would throw. A request aborted through `init.signal` rejects with the
124
+ * abort and is not an error for onError.
125
+ */
126
+ fetch(endpoint: string, init?: HttpFetchInit): Promise<globalThis.Response>;
127
+ /**
128
+ * One fetch of `url` on behalf of `targetUrl`. A refused response, or a
129
+ * request that got none, goes to onError; the result is the response its
130
+ * retry got. Without one the failure stands: the error of a request that
131
+ * got no response is thrown, and a refusal is thrown as the error its body
132
+ * was read into or, for a caller that passes the response on
133
+ * (`refused: 'return'`), handed back with its body unread.
134
+ */
135
+ private fetchHandled;
136
+ /** The error a refused response stands for, read from its body. */
137
+ private readError;
138
+ /** HTTP errors → RFC 9457 problem+json */
139
+ private responseError;
83
140
  /**
84
141
  * Execute the actual HTTP request (internal)
85
142
  */
@@ -87,6 +144,10 @@ export declare class HttpClient {
87
144
  /**
88
145
  * Get URL and headers for NDJSON streaming.
89
146
  * Returns the full URL and auth headers needed for streamable requests.
147
+ * It answers synchronously, so it throws when getToken is async.
148
+ * @deprecated Use fetch(), or a StreamRequest built on it
149
+ * (`(init) => http.fetch(endpoint, init)`): a request made from this config
150
+ * bypasses onError and carries the token of the moment the config was read.
90
151
  */
91
152
  getStreamableConfig(endpoint: string): {
92
153
  url: string;
@@ -100,7 +161,7 @@ export declare class HttpClient {
100
161
  private extractErrorDetail;
101
162
  /**
102
163
  * Create an EventSource for SSE streaming
103
- * @deprecated Use getStreamableConfig() with StreamableManager instead
164
+ * @deprecated Use StreamableManager with a StreamRequest instead
104
165
  */
105
166
  createEventSource(endpoint: string): Promise<EventSource | null>;
106
167
  }
@@ -54,32 +54,139 @@ export class HttpClient {
54
54
  }
55
55
  return resolved;
56
56
  }
57
- /** Get authorization token */
58
- getAuthToken() {
59
- if (this.getToken) {
60
- return this.getToken();
57
+ /** The bearer token a request sent now carries; none through a proxy, which adds its own. */
58
+ async bearerToken() {
59
+ if (this.proxyUrl)
60
+ return undefined;
61
+ return (this.getToken ? await this.getToken() : this.apiKey) || undefined;
62
+ }
63
+ /** The configured headers plus what identifies the request: its bearer, or through a proxy its target. */
64
+ requestHeaders(targetUrl, token) {
65
+ const headers = this.resolveHeaders();
66
+ if (this.proxyUrl) {
67
+ headers['x-inf-target-url'] = targetUrl.toString();
68
+ }
69
+ else if (token) {
70
+ headers['Authorization'] = `Bearer ${token}`;
61
71
  }
62
- return this.apiKey;
72
+ return headers;
73
+ }
74
+ /**
75
+ * Where a request whose caller cannot set headers (EventSource) or reads
76
+ * the URL (streams) goes: the API, or the proxy with the target as a query
77
+ * parameter.
78
+ */
79
+ streamUrl(targetUrl) {
80
+ if (!this.proxyUrl)
81
+ return targetUrl.toString();
82
+ const proxyUrlWithQuery = new URL(this.proxyUrl, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
83
+ proxyUrlWithQuery.searchParams.set('__inf_target', targetUrl.toString());
84
+ return proxyUrlWithQuery.toString();
63
85
  }
64
86
  /**
65
87
  * Make an HTTP request to the API
66
88
  */
67
- async request(method, endpoint, options = {}) {
68
- const doRequest = () => this.executeRequest(method, endpoint, options);
89
+ async request(method, endpoint, { handleErrors = true, ...options } = {}) {
90
+ const send = (token) => this.executeRequest(method, endpoint, options, token);
91
+ const token = await this.bearerToken();
69
92
  try {
70
- return await doRequest();
93
+ return await send(token);
71
94
  }
72
95
  catch (error) {
73
- if (this.onError) {
74
- return await this.onError(error, doRequest);
96
+ if (this.onError && handleErrors) {
97
+ return await this.onError(error, async () => send(await this.bearerToken()), { token });
75
98
  }
76
99
  throw error;
77
100
  }
78
101
  }
102
+ /**
103
+ * Fetch an API endpoint with the client's auth, headers and credentials
104
+ * mode and resolve with the raw response, for bodies request() does not
105
+ * parse (streams, text). A failure goes through onError like one of
106
+ * request() does: a refused response, or a request that got no response.
107
+ * It resolves with the response onError's retry got; when onError resolves
108
+ * with anything else the failure stands and is thrown, the error request()
109
+ * would throw. A request aborted through `init.signal` rejects with the
110
+ * abort and is not an error for onError.
111
+ */
112
+ async fetch(endpoint, init = {}) {
113
+ const targetUrl = new URL(`${this.baseUrl}${endpoint}`);
114
+ const response = await this.fetchHandled(this.streamUrl(targetUrl), targetUrl, init, 'throw');
115
+ // What onError's retry got is handed over unread, and can be a refusal too.
116
+ if (!response.ok)
117
+ throw await this.readError(response);
118
+ return response;
119
+ }
120
+ /**
121
+ * One fetch of `url` on behalf of `targetUrl`. A refused response, or a
122
+ * request that got none, goes to onError; the result is the response its
123
+ * retry got. Without one the failure stands: the error of a request that
124
+ * got no response is thrown, and a refusal is thrown as the error its body
125
+ * was read into or, for a caller that passes the response on
126
+ * (`refused: 'return'`), handed back with its body unread.
127
+ */
128
+ async fetchHandled(url, targetUrl, init, refused) {
129
+ const send = (token) => fetch(url, {
130
+ ...init,
131
+ headers: { ...init.headers, ...this.requestHeaders(targetUrl, token) },
132
+ credentials: this.credentials,
133
+ });
134
+ const retry = async () => send(await this.bearerToken());
135
+ const token = await this.bearerToken();
136
+ let response;
137
+ try {
138
+ response = await send(token);
139
+ }
140
+ catch (error) {
141
+ // The caller aborted the request; nothing failed.
142
+ if (!this.onError || init.signal?.aborted)
143
+ throw error;
144
+ const handled = await this.onError(error, retry, { token });
145
+ if (handled instanceof globalThis.Response)
146
+ return handled;
147
+ throw error;
148
+ }
149
+ if (response.ok || (refused === 'return' && !this.onError))
150
+ return response;
151
+ // The body is read once: from a clone only when the response is handed back.
152
+ const error = await this.readError(refused === 'return' ? response.clone() : response);
153
+ const handled = await this.onError?.(error, retry, { token });
154
+ if (handled instanceof globalThis.Response)
155
+ return handled;
156
+ if (refused === 'return')
157
+ return response;
158
+ throw error;
159
+ }
160
+ /** The error a refused response stands for, read from its body. */
161
+ async readError(response) {
162
+ const responseText = await response.text().catch(() => '');
163
+ let data = null;
164
+ try {
165
+ data = JSON.parse(responseText);
166
+ }
167
+ catch {
168
+ // Not JSON
169
+ }
170
+ return this.responseError(response.status, responseText, data);
171
+ }
172
+ /** HTTP errors → RFC 9457 problem+json */
173
+ responseError(status, responseText, data) {
174
+ if (status === 412 && data && typeof data === 'object' && 'errors' in data && Array.isArray(data.errors)) {
175
+ return RequirementsNotMetException.fromResponse(data, status);
176
+ }
177
+ let errorDetail;
178
+ if (data && typeof data === 'object') {
179
+ errorDetail = this.extractErrorDetail(data) ?? JSON.stringify(data);
180
+ }
181
+ else if (responseText) {
182
+ errorDetail = responseText.slice(0, 500);
183
+ }
184
+ return new InferenceError(status, errorDetail || 'Request failed', responseText);
185
+ }
79
186
  /**
80
187
  * Execute the actual HTTP request (internal)
81
188
  */
82
- async executeRequest(method, endpoint, options = {}) {
189
+ async executeRequest(method, endpoint, options, token) {
83
190
  // Build the target URL (always points to the API)
84
191
  const targetUrl = new URL(`${this.baseUrl}${endpoint}`);
85
192
  if (options.params) {
@@ -92,23 +199,11 @@ export class HttpClient {
92
199
  });
93
200
  }
94
201
  // In proxy mode, requests go to the proxy with target URL in a header
95
- const isProxyMode = !!this.proxyUrl;
96
- const fetchUrl = isProxyMode ? this.proxyUrl : targetUrl.toString();
202
+ const fetchUrl = this.proxyUrl ?? targetUrl.toString();
97
203
  const headers = {
98
204
  'Content-Type': 'application/json',
99
- ...this.resolveHeaders(),
205
+ ...this.requestHeaders(targetUrl, token),
100
206
  };
101
- if (isProxyMode) {
102
- // Proxy mode: send target URL as header, no auth (proxy handles it)
103
- headers['x-inf-target-url'] = targetUrl.toString();
104
- }
105
- else {
106
- // Direct mode: include authorization header
107
- const token = this.getAuthToken();
108
- if (token) {
109
- headers['Authorization'] = `Bearer ${token}`;
110
- }
111
- }
112
207
  const fetchOptions = {
113
208
  method: method.toUpperCase(),
114
209
  headers,
@@ -126,19 +221,8 @@ export class HttpClient {
126
221
  catch {
127
222
  // Not JSON
128
223
  }
129
- // HTTP errors → RFC 9457 problem+json
130
224
  if (!response.ok) {
131
- if (response.status === 412 && data && 'errors' in data && Array.isArray(data.errors)) {
132
- throw RequirementsNotMetException.fromResponse(data, response.status);
133
- }
134
- let errorDetail;
135
- if (data && typeof data === 'object') {
136
- errorDetail = this.extractErrorDetail(data) ?? JSON.stringify(data);
137
- }
138
- else if (responseText) {
139
- errorDetail = responseText.slice(0, 500);
140
- }
141
- throw new InferenceError(response.status, errorDetail || 'Request failed', responseText);
225
+ throw this.responseError(response.status, responseText, data);
142
226
  }
143
227
  if (response.status === 204 || !responseText) {
144
228
  return { data: undefined, messages: [] };
@@ -156,26 +240,22 @@ export class HttpClient {
156
240
  /**
157
241
  * Get URL and headers for NDJSON streaming.
158
242
  * Returns the full URL and auth headers needed for streamable requests.
243
+ * It answers synchronously, so it throws when getToken is async.
244
+ * @deprecated Use fetch(), or a StreamRequest built on it
245
+ * (`(init) => http.fetch(endpoint, init)`): a request made from this config
246
+ * bypasses onError and carries the token of the moment the config was read.
159
247
  */
160
248
  getStreamableConfig(endpoint) {
161
249
  const targetUrl = new URL(`${this.baseUrl}${endpoint}`);
162
- const isProxyMode = !!this.proxyUrl;
163
- let url;
164
- const headers = { ...this.resolveHeaders() };
165
- if (isProxyMode) {
166
- const proxyUrlWithQuery = new URL(this.proxyUrl, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
167
- proxyUrlWithQuery.searchParams.set('__inf_target', targetUrl.toString());
168
- url = proxyUrlWithQuery.toString();
169
- headers['x-inf-target-url'] = targetUrl.toString();
170
- }
171
- else {
172
- url = targetUrl.toString();
173
- const token = this.getAuthToken();
174
- if (token) {
175
- headers['Authorization'] = `Bearer ${token}`;
176
- }
250
+ const token = this.proxyUrl ? undefined : this.getToken ? this.getToken() : this.apiKey;
251
+ if (token instanceof Promise) {
252
+ throw new Error('getStreamableConfig() cannot wait for an async getToken; use fetch() instead');
177
253
  }
178
- return { url, headers, credentials: this.credentials };
254
+ return {
255
+ url: this.streamUrl(targetUrl),
256
+ headers: this.requestHeaders(targetUrl, token || undefined),
257
+ credentials: this.credentials,
258
+ };
179
259
  }
180
260
  /**
181
261
  * Pull a human-readable detail string from a parsed problem+json / API error
@@ -189,56 +269,16 @@ export class HttpClient {
189
269
  }
190
270
  /**
191
271
  * Create an EventSource for SSE streaming
192
- * @deprecated Use getStreamableConfig() with StreamableManager instead
272
+ * @deprecated Use StreamableManager with a StreamRequest instead
193
273
  */
194
274
  createEventSource(endpoint) {
195
275
  const targetUrl = new URL(`${this.baseUrl}${endpoint}`);
196
- const isProxyMode = !!this.proxyUrl;
197
- // For proxy mode: Browser EventSource can't send custom headers,
198
- // so append target URL as query param instead
199
- let fetchUrl;
200
- if (isProxyMode) {
201
- const proxyUrlWithQuery = new URL(this.proxyUrl, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
202
- proxyUrlWithQuery.searchParams.set('__inf_target', targetUrl.toString());
203
- fetchUrl = proxyUrlWithQuery.toString();
204
- }
205
- else {
206
- fetchUrl = targetUrl.toString();
207
- }
208
- const resolvedHeaders = this.resolveHeaders();
209
276
  // The EventSource error event omits the response body, so a failed initial
210
277
  // connection (e.g. 403 otp_required) is handled here in the fetch wrapper:
211
278
  // route it through onError like request() does, and hand the retried
212
279
  // response (e.g. after OTP verification) back so the stream connects.
213
- const doFetch = async (input, init) => {
214
- const headers = {
215
- ...init?.headers,
216
- ...resolvedHeaders,
217
- };
218
- if (isProxyMode) {
219
- headers['x-inf-target-url'] = targetUrl.toString();
220
- }
221
- else {
222
- const token = this.getAuthToken();
223
- if (token) {
224
- headers['Authorization'] = `Bearer ${token}`;
225
- }
226
- }
227
- const response = await fetch(input, { ...init, headers, credentials: this.credentials });
228
- if (!response.ok && this.onError) {
229
- const body = await response.clone().text().catch(() => '');
230
- let data;
231
- try {
232
- data = JSON.parse(body);
233
- }
234
- catch { /* non-JSON body */ }
235
- const error = new InferenceError(response.status, this.extractErrorDetail(data) ?? 'Request failed', body);
236
- const handled = await this.onError(error, () => doFetch(input, init));
237
- return handled ?? response;
238
- }
239
- return response;
240
- };
241
- return Promise.resolve(new EventSource(fetchUrl, { fetch: doFetch }));
280
+ const doFetch = (input, init) => this.fetchHandled(input, targetUrl, init ?? {}, 'return');
281
+ return Promise.resolve(new EventSource(this.streamUrl(targetUrl), { fetch: doFetch }));
242
282
  }
243
283
  }
244
284
  /**
@@ -1,4 +1,4 @@
1
- export { HttpClient, type HttpClientConfig, type ErrorHandler, createHttpClient } from './client';
1
+ export { HttpClient, type HttpClientConfig, type ErrorHandler, type FailedRequest, type HttpFetchInit, createHttpClient } from './client';
2
2
  export { StreamManager, type StreamManagerOptions, type PartialDataWrapper } from './stream';
3
- export { streamable, streamableRaw, StreamableManager, type StreamableOptions, type StreamableMessage, type StreamableManagerOptions } from './streamable';
3
+ export { streamable, streamableRaw, StreamableManager, type StreamableOptions, type StreamableMessage, type StreamableManagerOptions, type StreamableSource, type StreamRequest, type StreamRequestInit } from './streamable';
4
4
  export { InferenceError, RequirementsNotMetException } from './errors';
@@ -3,6 +3,19 @@
3
3
  * This is the NDJSON alternative to SSE/EventSource that avoids
4
4
  * the browser's ~6 connection limit per domain.
5
5
  */
6
+ /** What a stream asks of whatever sends its request. */
7
+ export interface StreamRequestInit {
8
+ method: 'GET' | 'POST';
9
+ headers: Record<string, string>;
10
+ body?: string;
11
+ signal?: AbortSignal;
12
+ }
13
+ /**
14
+ * Sends a stream's request in place of a plain fetch of a URL. With
15
+ * `(init) => http.fetch(endpoint, init)` the request carries the client's
16
+ * auth and a refusal goes through its error handler.
17
+ */
18
+ export type StreamRequest = (init: StreamRequestInit) => Promise<Response>;
6
19
  export interface StreamableOptions {
7
20
  /** Additional headers to include */
8
21
  headers?: Record<string, string>;
@@ -14,7 +27,7 @@ export interface StreamableOptions {
14
27
  signal?: AbortSignal;
15
28
  /** Skip heartbeat messages (default: true) */
16
29
  skipHeartbeats?: boolean;
17
- /** Request credentials mode (for cookie-based auth) */
30
+ /** Request credentials mode (for cookie-based auth); a StreamRequest brings its own */
18
31
  credentials?: RequestCredentials;
19
32
  }
20
33
  export interface StreamableMessage<T = unknown> {
@@ -30,19 +43,26 @@ export interface StreamableMessage<T = unknown> {
30
43
  /**
31
44
  * Stream NDJSON from an HTTP endpoint using fetch + ReadableStream.
32
45
  * Yields parsed JSON objects, automatically filtering heartbeats.
46
+ * `source` is the URL to fetch, or a StreamRequest that sends the request.
33
47
  */
34
- export declare function streamable<T = unknown>(url: string, options?: StreamableOptions): AsyncGenerator<T>;
48
+ export declare function streamable<T = unknown>(source: string | StreamRequest, options?: StreamableOptions): AsyncGenerator<T>;
35
49
  /**
36
50
  * StreamableManager provides a callback-based API similar to StreamManager
37
51
  * but uses fetch + ReadableStream instead of EventSource.
38
52
  */
39
- export interface StreamableManagerOptions<T> {
53
+ /** Where a StreamableManager's stream comes from: a URL it fetches, or a StreamRequest. */
54
+ export type StreamableSource = {
40
55
  /** URL to connect to */
41
56
  url: string;
42
57
  /** Additional headers */
43
58
  headers?: Record<string, string>;
44
59
  /** Request credentials mode (for cookie-based auth) */
45
60
  credentials?: RequestCredentials;
61
+ } | {
62
+ /** Sends the request, e.g. `(init) => http.fetch(endpoint, init)` */
63
+ request: StreamRequest;
64
+ };
65
+ export type StreamableManagerOptions<T> = StreamableSource & {
46
66
  /** Request body */
47
67
  body?: unknown;
48
68
  /** Called for each message */
@@ -57,7 +77,7 @@ export interface StreamableManagerOptions<T> {
57
77
  onEnd?: () => void;
58
78
  /** Called for delta events (streaming token deltas) */
59
79
  onDelta?: (delta: Record<string, any>, seq: number) => void;
60
- }
80
+ };
61
81
  export declare class StreamableManager<T> {
62
82
  private options;
63
83
  private abortController;
@@ -76,4 +96,4 @@ export declare class StreamableManager<T> {
76
96
  * Raw streamable that yields the full message including wrapper.
77
97
  * Use this when you need access to event type or fields.
78
98
  */
79
- export declare function streamableRaw<T = unknown>(url: string, options?: StreamableOptions): AsyncGenerator<StreamableMessage<T> | T>;
99
+ export declare function streamableRaw<T = unknown>(source: string | StreamRequest, options?: StreamableOptions): AsyncGenerator<StreamableMessage<T> | T>;
@@ -3,12 +3,8 @@
3
3
  * This is the NDJSON alternative to SSE/EventSource that avoids
4
4
  * the browser's ~6 connection limit per domain.
5
5
  */
6
- /**
7
- * Stream NDJSON from an HTTP endpoint using fetch + ReadableStream.
8
- * Yields parsed JSON objects, automatically filtering heartbeats.
9
- */
10
- export async function* streamable(url, options = {}) {
11
- const { headers = {}, body, method = body ? 'POST' : 'GET', signal, skipHeartbeats = true, credentials, } = options;
6
+ /** Send the stream's request and hand back the body to read. */
7
+ async function openStream(source, { headers = {}, body, method = body ? 'POST' : 'GET', signal, credentials }) {
12
8
  const requestHeaders = {
13
9
  Accept: 'application/x-ndjson',
14
10
  ...headers,
@@ -16,13 +12,13 @@ export async function* streamable(url, options = {}) {
16
12
  if (body) {
17
13
  requestHeaders['Content-Type'] = 'application/json';
18
14
  }
19
- const res = await fetch(url, {
15
+ const init = {
20
16
  method,
21
17
  headers: requestHeaders,
22
18
  body: body ? JSON.stringify(body) : undefined,
23
19
  signal,
24
- credentials,
25
- });
20
+ };
21
+ const res = typeof source === 'string' ? await fetch(source, { ...init, credentials }) : await source(init);
26
22
  if (!res.ok) {
27
23
  const text = await res.text();
28
24
  throw new Error(`HTTP ${res.status}: ${text}`);
@@ -30,7 +26,16 @@ export async function* streamable(url, options = {}) {
30
26
  if (!res.body) {
31
27
  throw new Error('No response body');
32
28
  }
33
- const reader = res.body.getReader();
29
+ return res.body;
30
+ }
31
+ /**
32
+ * Stream NDJSON from an HTTP endpoint using fetch + ReadableStream.
33
+ * Yields parsed JSON objects, automatically filtering heartbeats.
34
+ * `source` is the URL to fetch, or a StreamRequest that sends the request.
35
+ */
36
+ export async function* streamable(source, options = {}) {
37
+ const { skipHeartbeats = true } = options;
38
+ const reader = (await openStream(source, options)).getReader();
34
39
  const decoder = new TextDecoder();
35
40
  let buffer = '';
36
41
  try {
@@ -109,12 +114,16 @@ export class StreamableManager {
109
114
  this.abortController = new AbortController();
110
115
  try {
111
116
  this.options.onStart?.();
112
- for await (const message of streamableRaw(this.options.url, {
113
- headers: this.options.headers,
114
- body: this.options.body,
115
- signal: this.abortController.signal,
116
- credentials: this.options.credentials,
117
- })) {
117
+ const { options } = this;
118
+ const stream = 'request' in options
119
+ ? streamableRaw(options.request, { body: options.body, signal: this.abortController.signal })
120
+ : streamableRaw(options.url, {
121
+ headers: options.headers,
122
+ body: options.body,
123
+ signal: this.abortController.signal,
124
+ credentials: options.credentials,
125
+ });
126
+ for await (const message of stream) {
118
127
  if (!this.isRunning)
119
128
  break;
120
129
  const wrapper = message;
@@ -179,30 +188,9 @@ export class StreamableManager {
179
188
  * Raw streamable that yields the full message including wrapper.
180
189
  * Use this when you need access to event type or fields.
181
190
  */
182
- export async function* streamableRaw(url, options = {}) {
183
- const { headers = {}, body, method = body ? 'POST' : 'GET', signal, skipHeartbeats = true, credentials, } = options;
184
- const requestHeaders = {
185
- Accept: 'application/x-ndjson',
186
- ...headers,
187
- };
188
- if (body) {
189
- requestHeaders['Content-Type'] = 'application/json';
190
- }
191
- const res = await fetch(url, {
192
- method,
193
- headers: requestHeaders,
194
- body: body ? JSON.stringify(body) : undefined,
195
- signal,
196
- credentials,
197
- });
198
- if (!res.ok) {
199
- const text = await res.text();
200
- throw new Error(`HTTP ${res.status}: ${text}`);
201
- }
202
- if (!res.body) {
203
- throw new Error('No response body');
204
- }
205
- const reader = res.body.getReader();
191
+ export async function* streamableRaw(source, options = {}) {
192
+ const { skipHeartbeats = true } = options;
193
+ const reader = (await openStream(source, options)).getReader();
206
194
  const decoder = new TextDecoder();
207
195
  let buffer = '';
208
196
  try {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { HttpClient, type HttpClientConfig, type ErrorHandler, type MessageHandler, createHttpClient } from './http/client';
1
+ export { HttpClient, type HttpClientConfig, type ErrorHandler, type FailedRequest, type HttpFetchInit, type MessageHandler, createHttpClient } from './http/client';
2
2
  export type { Response } from './http/response';
3
3
  import type { Response } from './http/response';
4
4
  export { StreamManager, type StreamManagerOptions, type PartialDataWrapper } from './http/stream';
5
- export { StreamableManager, type StreamableManagerOptions, type StreamableMessage, streamable, streamableRaw } from './http/streamable';
5
+ export { StreamableManager, type StreamableManagerOptions, type StreamableSource, type StreamRequest, type StreamRequestInit, type StreamableMessage, streamable, streamableRaw } from './http/streamable';
6
6
  export { PollManager, type PollManagerOptions } from './http/poll';
7
7
  export { InferenceError, RequirementsNotMetException, SessionError, SessionNotFoundError, SessionExpiredError, SessionEndedError, WorkerLostError, isRequirementsNotMetException, isInferenceError, isSessionError, } from './http/errors';
8
8
  export { TasksAPI, type RunOptions, type WatchOptions, type TaskWatch } from './api/tasks';
package/dist/types.d.ts CHANGED
@@ -481,6 +481,12 @@ export interface CreateAppRequest {
481
481
  agent_description?: string;
482
482
  category?: AppCategory;
483
483
  images?: AppImages;
484
+ /**
485
+ * Tags replace the app's tags when given; omitted, a deploy takes them
486
+ * from the version's metadata.tags, and keeps the stored ones if that is
487
+ * empty too.
488
+ */
489
+ tags?: string[];
484
490
  version?: AppVersionInput;
485
491
  preserve_current_version?: boolean;
486
492
  }
@@ -1220,6 +1226,12 @@ export interface AppDTO extends BaseModelDTO, PermissionModelDTO {
1220
1226
  agent_description: string;
1221
1227
  category: AppCategory;
1222
1228
  images: AppImages;
1229
+ /**
1230
+ * Tags name what the app does and its traits, as lowercase slugs
1231
+ * (text-to-image, open-weights). GET /store/tags lists the ones in use
1232
+ * with their titles.
1233
+ */
1234
+ tags: string[];
1223
1235
  version_id: string;
1224
1236
  version?: AppVersionDTO;
1225
1237
  status: AppStatus;
@@ -2427,7 +2439,7 @@ export interface DecideEntitlementRequestRequest {
2427
2439
  * and APIError.Code on the legacy envelope. Clients branch on these, so a
2428
2440
  * code is a named const here, where it generates into models and the SDKs.
2429
2441
  */
2430
- export type ErrorCode = "invalid_request" | "validation_error" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "name_conflict" | "already_exists" | "not_configured" | "method_not_allowed" | "rate_limited" | "internal_error" | "team_role_required" | "blocked_by_usage_policy" | "last_owner" | "account_deactivated" | "account_banned" | "person_required" | "otp_required" | "mcp_auth_expired" | "limit_exceeded" | "feature_not_available" | "entitlement_unavailable" | "payment_required" | "payment_method_required" | "form_closed" | "already_submitted" | "already_claimed" | "already_entitled" | "request_open" | "agents_disabled" | "remote_offline" | "remote_timeout" | "harness_not_drivable" | "harness_too_old";
2442
+ export type ErrorCode = "invalid_request" | "validation_error" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "name_conflict" | "already_exists" | "not_configured" | "method_not_allowed" | "rate_limited" | "internal_error" | "team_role_required" | "blocked_by_usage_policy" | "last_owner" | "account_deactivated" | "account_banned" | "person_required" | "otp_required" | "impersonation_reason_required" | "mcp_auth_expired" | "limit_exceeded" | "feature_not_available" | "entitlement_unavailable" | "payment_required" | "payment_method_required" | "form_closed" | "already_submitted" | "already_claimed" | "already_entitled" | "request_open" | "agents_disabled" | "remote_offline" | "remote_timeout" | "harness_not_drivable" | "harness_too_old";
2431
2443
  export declare const ErrorCodeInvalidRequest: ErrorCode;
2432
2444
  export declare const ErrorCodeValidationError: ErrorCode;
2433
2445
  export declare const ErrorCodeUnauthorized: ErrorCode;
@@ -2471,6 +2483,12 @@ export declare const ErrorCodeAccountBanned: ErrorCode;
2471
2483
  */
2472
2484
  export declare const ErrorCodePersonRequired: ErrorCode;
2473
2485
  export declare const ErrorCodeOTPRequired: ErrorCode;
2486
+ /**
2487
+ * ErrorCodeImpersonationReasonRequired (403): a platform admin named a
2488
+ * team they are not a member of without a live impersonation grant.
2489
+ * Clients stop viewing as the team on it.
2490
+ */
2491
+ export declare const ErrorCodeImpersonationReasonRequired: ErrorCode;
2474
2492
  export declare const ErrorCodeMCPAuthExpired: ErrorCode;
2475
2493
  /**
2476
2494
  * Entitlements. LimitExceeded (402) and FeatureNotAvailable (403) carry
@@ -4077,6 +4095,23 @@ export interface StatBuckets {
4077
4095
  this_week: number;
4078
4096
  all_time: number;
4079
4097
  }
4098
+ export interface StoreCategoryDTO {
4099
+ slug: string;
4100
+ name: string;
4101
+ description: string;
4102
+ icon: string;
4103
+ rank: number;
4104
+ count: number;
4105
+ }
4106
+ /**
4107
+ * StoreTagDTO is one app tag the store lists: a tag at least
4108
+ * shared.MinAppsPerListedTag public apps carry.
4109
+ */
4110
+ export interface StoreTagDTO {
4111
+ slug: string;
4112
+ title: string;
4113
+ count: number;
4114
+ }
4080
4115
  /**
4081
4116
  * SubscriptionDTO for API responses
4082
4117
  */
@@ -4833,7 +4868,7 @@ export declare const ApiKeyScopeUser: ApiKeyScope;
4833
4868
  * service account, not a person, and outlives the admin who created it.
4834
4869
  */
4835
4870
  export declare const ApiKeyScopeWorkspace: ApiKeyScope;
4836
- export type AppCategory = "image" | "video" | "audio" | "text" | "chat" | "3d" | "other" | "flow";
4871
+ export type AppCategory = "image" | "video" | "audio" | "text" | "chat" | "3d" | "other" | "flow" | "decision";
4837
4872
  export declare const AppCategoryImage: AppCategory;
4838
4873
  export declare const AppCategoryVideo: AppCategory;
4839
4874
  export declare const AppCategoryAudio: AppCategory;
@@ -4842,6 +4877,10 @@ export declare const AppCategoryChat: AppCategory;
4842
4877
  export declare const AppCategory3D: AppCategory;
4843
4878
  export declare const AppCategoryOther: AppCategory;
4844
4879
  export declare const AppCategoryFlow: AppCategory;
4880
+ /**
4881
+ * Decision models: typed questions in, a probability for every answer out, no generation.
4882
+ */
4883
+ export declare const AppCategoryDecision: AppCategory;
4845
4884
  export type AppStatus = "active" | "maintenance" | "deprecated" | "retired";
4846
4885
  export declare const AppStatusActive: AppStatus;
4847
4886
  export declare const AppStatusMaintenance: AppStatus;
@@ -5045,6 +5084,148 @@ export interface ChannelContext {
5045
5084
  channel_type?: ChannelType;
5046
5085
  channel_metadata?: any;
5047
5086
  }
5087
+ /**
5088
+ * DecisionChoiceOption is one answer option of a choice question.
5089
+ */
5090
+ export interface DecisionChoiceOption {
5091
+ /**
5092
+ * Name is returned as the choice and keys the probabilities.
5093
+ */
5094
+ name: string;
5095
+ /**
5096
+ * Description says what the option covers.
5097
+ */
5098
+ description?: any;
5099
+ }
5100
+ /**
5101
+ * DecisionChoiceQuestion asks which one of a fixed set of options holds.
5102
+ */
5103
+ export interface DecisionChoiceQuestion {
5104
+ /**
5105
+ * ID is the caller's key for the question; its answer comes back under it.
5106
+ */
5107
+ id: string;
5108
+ instructions: any;
5109
+ options: DecisionChoiceOption[];
5110
+ }
5111
+ /**
5112
+ * DecisionScoreQuestion asks where the state sits on ordered levels.
5113
+ */
5114
+ export interface DecisionScoreQuestion {
5115
+ id: string;
5116
+ instructions: any;
5117
+ /**
5118
+ * Levels are described low end to high end; a level's number is its index.
5119
+ */
5120
+ levels: any[];
5121
+ }
5122
+ /**
5123
+ * DecisionNoulCriteria pins down what yes and no mean for a noul question.
5124
+ */
5125
+ export interface DecisionNoulCriteria {
5126
+ true?: any;
5127
+ false?: any;
5128
+ }
5129
+ /**
5130
+ * DecisionNoulQuestion asks for the probability that something is true.
5131
+ */
5132
+ export interface DecisionNoulQuestion {
5133
+ id: string;
5134
+ instructions: any;
5135
+ criteria?: DecisionNoulCriteria;
5136
+ }
5137
+ /**
5138
+ * DecisionInput is one state and the questions asked of it.
5139
+ */
5140
+ export interface DecisionInput {
5141
+ state: any;
5142
+ choices?: DecisionChoiceQuestion[];
5143
+ scores?: DecisionScoreQuestion[];
5144
+ nouls?: DecisionNoulQuestion[];
5145
+ }
5146
+ /**
5147
+ * DecisionVisionInput is the input of a decision model that also sees
5148
+ * images: a DecisionInput plus the images the questions are about.
5149
+ */
5150
+ export interface DecisionVisionInput extends DecisionInput {
5151
+ /**
5152
+ * Images are file URIs. Every question sees them.
5153
+ */
5154
+ images?: string[];
5155
+ }
5156
+ /**
5157
+ * DecisionChoiceAnswer is the answer to a choice question.
5158
+ */
5159
+ export interface DecisionChoiceAnswer {
5160
+ /**
5161
+ * Choice is the highest-probability option.
5162
+ */
5163
+ choice: string;
5164
+ confidence: number;
5165
+ /**
5166
+ * Probabilities maps every option name to its probability.
5167
+ */
5168
+ probabilities: {
5169
+ [key: string]: number;
5170
+ };
5171
+ }
5172
+ /**
5173
+ * DecisionScoreAnswer is the answer to a score question.
5174
+ */
5175
+ export interface DecisionScoreAnswer {
5176
+ /**
5177
+ * Score is the probability-weighted level, 0 to the top level number.
5178
+ */
5179
+ score: number;
5180
+ /**
5181
+ * Normalized is Score over the top level number: 0 to 1.
5182
+ */
5183
+ normalized: number;
5184
+ confidence: number;
5185
+ /**
5186
+ * Probabilities maps each level number, as a string, to its probability.
5187
+ */
5188
+ probabilities: {
5189
+ [key: string]: number;
5190
+ };
5191
+ /**
5192
+ * Legend maps each level number back to its description.
5193
+ */
5194
+ legend: {
5195
+ [key: string]: any;
5196
+ };
5197
+ }
5198
+ /**
5199
+ * DecisionNoulAnswer is the answer to a noul question.
5200
+ */
5201
+ export interface DecisionNoulAnswer {
5202
+ /**
5203
+ * Noul is the probability that the answer is yes.
5204
+ */
5205
+ noul: number;
5206
+ }
5207
+ /**
5208
+ * DecisionOutput is the answers, keyed by question id within each kind.
5209
+ */
5210
+ export interface DecisionOutput {
5211
+ choices: {
5212
+ [key: string]: DecisionChoiceAnswer;
5213
+ };
5214
+ scores: {
5215
+ [key: string]: DecisionScoreAnswer;
5216
+ };
5217
+ nouls: {
5218
+ [key: string]: DecisionNoulAnswer;
5219
+ };
5220
+ /**
5221
+ * Model is the model that answered.
5222
+ */
5223
+ model: string;
5224
+ /**
5225
+ * InputTokens is what the model read; decision models write none.
5226
+ */
5227
+ input_tokens: number;
5228
+ }
5048
5229
  /**
5049
5230
  * DescriptionLimit is the most characters a resource's description may have.
5050
5231
  */
package/dist/types.js CHANGED
@@ -379,6 +379,12 @@ export const ErrorCodeAccountBanned = "account_banned";
379
379
  */
380
380
  export const ErrorCodePersonRequired = "person_required";
381
381
  export const ErrorCodeOTPRequired = "otp_required";
382
+ /**
383
+ * ErrorCodeImpersonationReasonRequired (403): a platform admin named a
384
+ * team they are not a member of without a live impersonation grant.
385
+ * Clients stop viewing as the team on it.
386
+ */
387
+ export const ErrorCodeImpersonationReasonRequired = "impersonation_reason_required";
382
388
  export const ErrorCodeMCPAuthExpired = "mcp_auth_expired";
383
389
  /**
384
390
  * Entitlements. LimitExceeded (402) and FeatureNotAvailable (403) carry
@@ -598,6 +604,10 @@ export const AppCategoryChat = "chat";
598
604
  export const AppCategory3D = "3d";
599
605
  export const AppCategoryOther = "other";
600
606
  export const AppCategoryFlow = "flow";
607
+ /**
608
+ * Decision models: typed questions in, a probability for every answer out, no generation.
609
+ */
610
+ export const AppCategoryDecision = "decision";
601
611
  export const AppStatusActive = "active";
602
612
  export const AppStatusMaintenance = "maintenance";
603
613
  export const AppStatusDeprecated = "deprecated";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inferencesh/sdk",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Official JavaScript/TypeScript SDK for inference.sh - Run AI models with a simple API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",