@inferencesh/sdk 0.22.1 → 0.24.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/agent/actions.d.ts +2 -2
  3. package/dist/agent/actions.js +131 -28
  4. package/dist/agent/api.d.ts +4 -4
  5. package/dist/agent/api.js +6 -3
  6. package/dist/agent/context.d.ts +1 -1
  7. package/dist/agent/hooks.d.ts +2 -2
  8. package/dist/agent/hooks.js +1 -1
  9. package/dist/agent/index.d.ts +6 -6
  10. package/dist/agent/index.js +5 -5
  11. package/dist/agent/mcp-input.d.ts +1 -1
  12. package/dist/agent/provider.d.ts +1 -1
  13. package/dist/agent/provider.js +5 -5
  14. package/dist/agent/reducer.d.ts +1 -1
  15. package/dist/agent/reducer.js +2 -2
  16. package/dist/agent/types.d.ts +20 -7
  17. package/dist/api/agents.d.ts +16 -14
  18. package/dist/api/agents.js +63 -40
  19. package/dist/api/api-keys.d.ts +3 -3
  20. package/dist/api/apps.d.ts +4 -4
  21. package/dist/api/artifacts.d.ts +3 -3
  22. package/dist/api/chats.d.ts +4 -4
  23. package/dist/api/credentials.d.ts +3 -3
  24. package/dist/api/engines.d.ts +4 -4
  25. package/dist/api/files.d.ts +3 -3
  26. package/dist/api/flow-runs.d.ts +3 -3
  27. package/dist/api/flows.d.ts +4 -4
  28. package/dist/api/index.d.ts +4 -4
  29. package/dist/api/index.js +4 -4
  30. package/dist/api/knowledge.d.ts +4 -4
  31. package/dist/api/mcp-servers.d.ts +4 -4
  32. package/dist/api/projects.d.ts +4 -4
  33. package/dist/api/search.d.ts +3 -3
  34. package/dist/api/secrets.d.ts +3 -3
  35. package/dist/api/sessions.d.ts +3 -3
  36. package/dist/api/sockets.d.ts +6 -6
  37. package/dist/api/sockets.js +2 -2
  38. package/dist/api/tasks.d.ts +3 -3
  39. package/dist/api/tasks.js +4 -4
  40. package/dist/api/teams.d.ts +4 -4
  41. package/dist/api/write-bodies.d.ts +1 -1
  42. package/dist/delta.d.ts +1 -1
  43. package/dist/delta.js +1 -1
  44. package/dist/hook-builder.d.ts +1 -1
  45. package/dist/hook-builder.js +1 -1
  46. package/dist/http/client.d.ts +2 -2
  47. package/dist/http/client.js +2 -2
  48. package/dist/http/errors.d.ts +1 -1
  49. package/dist/http/index.d.ts +4 -4
  50. package/dist/http/index.js +4 -4
  51. package/dist/http/response.d.ts +1 -1
  52. package/dist/http/stream-until-terminal.d.ts +22 -8
  53. package/dist/http/stream-until-terminal.js +17 -9
  54. package/dist/http/streamable.d.ts +63 -7
  55. package/dist/http/streamable.js +277 -119
  56. package/dist/index.d.ts +65 -65
  57. package/dist/index.js +53 -53
  58. package/dist/live/schema.js +1 -1
  59. package/dist/live/session.d.ts +3 -3
  60. package/dist/live/session.js +2 -2
  61. package/dist/proxy/express.d.ts +1 -1
  62. package/dist/proxy/express.js +1 -1
  63. package/dist/proxy/hono.d.ts +1 -1
  64. package/dist/proxy/hono.js +1 -1
  65. package/dist/proxy/index.js +0 -2
  66. package/dist/proxy/nextjs.d.ts +3 -3
  67. package/dist/proxy/nextjs.js +2 -2
  68. package/dist/proxy/remix.d.ts +1 -1
  69. package/dist/proxy/remix.js +1 -1
  70. package/dist/proxy/svelte.d.ts +1 -1
  71. package/dist/proxy/svelte.js +1 -1
  72. package/dist/tool-builder.d.ts +1 -1
  73. package/dist/tool-builder.js +1 -1
  74. package/dist/types.d.ts +182 -41
  75. package/dist/types.js +33 -9
  76. package/dist/utils.d.ts +2 -2
  77. package/dist/utils.js +2 -2
  78. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - Client tools can return files. A `ClientToolHandlerFn` may return `{ result: string; attachments?: FileRef[] }` (new `ClientToolResult` type) besides a string; the files are uploaded first with POST /files (`client.files.upload`, the chat's `uploadFile`) and sent as `attachments` with the result. Images reach the model as image input when it supports images. `submitToolResult` (agent api, `Agent`, `AgentsAPI`, the React chat actions) takes optional `attachments`, sent only when non-empty. `ToolResultRequest.attachments` is added by hand to the generated types until the next `make types` picks it up from the API. String returns work as before.
13
+
14
+ ## [0.23.0] - 2026-10-11
15
+
16
+ ### Changed
17
+
18
+ - Generated types follow the API: `MessageInput` (what an agent run takes), `ContextField.in_prompt`, `ApiAgentRunRequest` takes `agent_id`/`agent_version_id` and no `channel_context`; `CreateAgentMessageRequest` is gone. Per-function `credentials` and annotations on `AppFunction`, and `DeployWarning` on `AppVersionDTO` (INF-1053). `ScopesResponse.account_groups`. Agent events for subagent ids, context, limits and account.
19
+ - Dev dependency lockfile refreshed for security advisories (handlebars, devalue, sharp). Nothing shipped in the package changes.
20
+
21
+ ### Deprecated
22
+
23
+ - `SendMessageOptions.channel_context`. The API no longer reads a channel context from an agent run request; a chat's routing comes from the server's own channel receivers. The option is still accepted and is not sent. Chats still return `channel_context`.
24
+
25
+ ### Added
26
+
27
+ - `StreamableManager` reconnects on opt-in: `reconnect: true | { maxAttempts, baseDelayMs, maxDelayMs, idleTimeoutMs }` (defaults 10, 1000, 30000, 30000). A failed stream is retried with jittered exponential backoff; any line received, heartbeats included, resets the failure count; a stream silent for `idleTimeoutMs` is reconnected; in a browser, `online` or the tab becoming visible retries at once. A 4xx other than 408/429 is not retried. `onRetry(error, attempt, delayMs)` reports each scheduled retry; `onReconnect(attempt)` runs after a retry connects and before the new stream is read, so the caller can fetch what it missed. With `reconnect`, `onError` fires only when the manager gives up. `StreamReconnectOptions` is exported. Without the option nothing changes.
28
+
29
+ ### Fixed
30
+
31
+ - The React chat (`AgentChatProvider`) no longer stays busy after a network blip mid-turn (Chromium `ERR_NETWORK_CHANGED`, "Stream error: TypeError: network error"). The chat stream reconnects; each reconnect refetches the chat and its messages, so a turn that finished while disconnected ends and `onTurnEnd` fires. `connectionStatus` is `'connecting'` while it waits to retry and `'error'` (with `error` set) once it gives up. Whenever the stream ends on its own and the chat is still busy, the chat is polled until idle. A stop, reset or chat switch during a pending retry leaves the new state alone.
32
+ - `Agent.sendMessage`/`startStreaming` and `tasks.run`/`watch` in streaming mode survive a network error: the stream reconnects and refetches the chat (with its messages) or the task, so the wait settles when it finished meanwhile. A 4xx refusal still rejects at once; other errors reject once the stream stops retrying.
33
+
34
+ - The package loads in plain Node (no bundler). 0.22.1's dist used extensionless relative imports, so `import("@inferencesh/sdk")` and every subpath except `proxy` and `internal/upload` failed with ERR_MODULE_NOT_FOUND. The build now uses NodeNext resolution with `.js` import specifiers, and `@inferencesh/sdk/proxy/nextjs` imports `next/server.js`. `require()` of every subpath works on Node >=22.12. CI and publish load each export of the packed tarball (`pnpm run check:exports`).
35
+
10
36
  ## [0.22.1] - 2026-10-10
11
37
 
12
38
  ### Fixed
@@ -4,6 +4,6 @@
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 type { ActionsContext, ActionsResult } from './types';
7
+ import type { ActionsContext, ActionsResult } from './types.js';
8
8
  export declare function createActions(ctx: ActionsContext): ActionsResult;
9
- export declare function getClientToolHandlers(config: import('./types').AgentOptions | null): Map<string, import('./types').ClientToolHandlerFn>;
9
+ export declare function getClientToolHandlers(config: import('./types.js').AgentOptions | null): Map<string, import('./types.js').ClientToolHandlerFn>;
@@ -4,22 +4,57 @@
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, ToolInvocationStatusInProgress, ToolTypeClient, } from '../types';
8
- import { isChatBusy, isMessageTerminal, isRunInterrupted, isRunWorking } from '../utils';
9
- import { StreamableManager } from '../http/streamable';
10
- import { PollManager } from '../http/poll';
11
- import { createLLMDeltaAccumulator } from '../delta';
12
- import { isAdHocConfig, extractClientToolHandlers } from './types';
13
- import * as api from './api';
14
- import { buildMCPInputResult } from './mcp-input';
15
- import { isInferenceError } from '../http/errors';
7
+ import { ToolInvocationStatusAwaitingInput, ToolInvocationStatusInProgress, ToolTypeClient, } from '../types.js';
8
+ import { isChatBusy, isMessageTerminal, isRunInterrupted, isRunWorking } from '../utils.js';
9
+ import { StreamableManager } from '../http/streamable.js';
10
+ import { PollManager } from '../http/poll.js';
11
+ import { createLLMDeltaAccumulator } from '../delta.js';
12
+ import { isAdHocConfig, extractClientToolHandlers } from './types.js';
13
+ import * as api from './api.js';
14
+ import { buildMCPInputResult } from './mcp-input.js';
15
+ import { isInferenceError } from '../http/errors.js';
16
16
  // =============================================================================
17
17
  // Action Creators
18
18
  // =============================================================================
19
+ /** Up to about ten minutes of backoff (1s doubling to 30s, jittered) before a tool result is given up. */
20
+ const TOOL_RESULT_MAX_ATTEMPTS = 25;
21
+ /** A failure a retry can get past: no response at all, a 5xx, a 408 or a 429. */
22
+ function isTransient(err) {
23
+ const status = err?.statusCode;
24
+ if (typeof status !== 'number')
25
+ return true;
26
+ return status >= 500 || status === 408 || status === 429;
27
+ }
19
28
  export function createActions(ctx) {
20
29
  const dispatchedToolInvocations = new Set();
21
30
  const { client, dispatch, getState, getConfig, getChatId, getClientToolHandlers, getStreamManager, setStreamManager, getStreamEnabled, getPollIntervalMs, callbacks } = ctx;
22
31
  let prevChatWasBusy = false;
32
+ // Bumped by every streamChat() and stopStream(). A stream whose generation
33
+ // is no longer current (stopped, reset, switched to another chat) leaves
34
+ // state alone, including work it started before that.
35
+ let streamGeneration = 0;
36
+ // A client tool's result is the only way its turn continues: the server
37
+ // waits on it. A result lost to a network drop left the turn stuck on
38
+ // "running" for good, so it is sent again until it lands or is refused.
39
+ const deliverToolResult = async (invocationId, result, attachments) => {
40
+ for (let attempt = 1;; attempt++) {
41
+ try {
42
+ if (attachments?.length)
43
+ await api.submitToolResult(client, invocationId, result, attachments);
44
+ else
45
+ await api.submitToolResult(client, invocationId, result);
46
+ return;
47
+ }
48
+ catch (err) {
49
+ if (!isTransient(err) || attempt >= TOOL_RESULT_MAX_ATTEMPTS) {
50
+ console.error(`[AgentSDK] Failed to submit tool result for ${invocationId}:`, err);
51
+ return;
52
+ }
53
+ const step = Math.min(30000, 1000 * 2 ** (attempt - 1));
54
+ await new Promise((r) => setTimeout(r, step / 2 + Math.random() * (step / 2)));
55
+ }
56
+ }
57
+ };
23
58
  const checkTurnEnd = (chat) => {
24
59
  const busy = isChatBusy(chat);
25
60
  if (prevChatWasBusy && !busy) {
@@ -59,7 +94,7 @@ export function createActions(ctx) {
59
94
  const handler = clientToolHandlers.get(functionName);
60
95
  if (!handler) {
61
96
  console.warn(`[AgentSDK] No handler for client tool: ${functionName}`);
62
- api.submitToolResult(client, invocation.id, JSON.stringify({
97
+ deliverToolResult(invocation.id, JSON.stringify({
63
98
  status: 'not_available',
64
99
  message: `Client tool "${functionName}" is not available in this environment`,
65
100
  }));
@@ -69,12 +104,12 @@ export function createActions(ctx) {
69
104
  // Use Promise.resolve to handle both sync and async handlers
70
105
  const args = invocation.function?.arguments || {};
71
106
  Promise.resolve(handler(args))
72
- .then((result) => {
73
- api.submitToolResult(client, invocation.id, result);
74
- })
107
+ .then((output) => typeof output === 'string'
108
+ ? deliverToolResult(invocation.id, output)
109
+ : deliverToolResult(invocation.id, output.result, output.attachments))
75
110
  .catch((error) => {
76
111
  console.error(`[AgentSDK] Client tool ${functionName} error:`, error);
77
- api.submitToolResult(client, invocation.id, JSON.stringify({
112
+ return deliverToolResult(invocation.id, JSON.stringify({
78
113
  error: String(error)
79
114
  }));
80
115
  });
@@ -83,6 +118,7 @@ export function createActions(ctx) {
83
118
  }
84
119
  };
85
120
  const streamChat = async (id) => {
121
+ const generation = ++streamGeneration;
86
122
  const existingManager = getStreamManager();
87
123
  if (existingManager) {
88
124
  existingManager.stop();
@@ -93,6 +129,9 @@ export function createActions(ctx) {
93
129
  try {
94
130
  // Fetch initial chat (fetchChat loads messages separately since Chat.Get no longer preloads them)
95
131
  const chat = await api.fetchChat(client, id);
132
+ // Stopped or switched to another chat while fetching.
133
+ if (generation !== streamGeneration)
134
+ return;
96
135
  if (chat) {
97
136
  setChat(chat);
98
137
  }
@@ -108,11 +147,53 @@ export function createActions(ctx) {
108
147
  pollChat(id);
109
148
  return;
110
149
  }
150
+ // Token-by-token streaming state, one accumulator per message being
151
+ // streamed. Deltas name their message (DeltaEvent.resource_id), so state
152
+ // never leaks between messages the way a single shared accumulator allowed.
153
+ const deltaAccums = new Map();
154
+ const isCurrent = () => generation === streamGeneration;
155
+ let failed = false;
156
+ // The API does not replay what the stream missed: the chat as fetched is
157
+ // the source of truth. Messages are merged, so older pages stay loaded.
158
+ const resync = async () => {
159
+ const chat = await api.fetchChat(client, id);
160
+ if (!chat || !isCurrent())
161
+ return null;
162
+ for (const message of chat.chat_messages ?? []) {
163
+ updateMessage(message);
164
+ }
165
+ dispatch({ type: 'UPDATE_CHAT', payload: chat });
166
+ callbacks.onStatusChange?.(isChatBusy(chat) ? 'streaming' : 'idle');
167
+ checkTurnEnd(chat);
168
+ return chat;
169
+ };
111
170
  // Single unified stream with TypedEvents (both Chat and ChatMessage events)
112
171
  const manager = new StreamableManager({
113
172
  request: api.chatStreamRequest(client, id),
173
+ reconnect: true,
174
+ onRetry: (error, attempt, delayMs) => {
175
+ console.warn(`[AgentSDK] Stream error, reconnecting in ${delayMs}ms (attempt ${attempt}):`, error);
176
+ if (!isCurrent())
177
+ return;
178
+ dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'connecting' });
179
+ callbacks.onStatusChange?.('connecting');
180
+ },
181
+ onReconnect: async () => {
182
+ // Partial text from before the drop is replaced by the fetched messages.
183
+ deltaAccums.clear();
184
+ if (!isCurrent())
185
+ return;
186
+ dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'streaming' });
187
+ await resync();
188
+ },
114
189
  onError: (error) => {
190
+ // The manager gave up: a refusal, or every retry failed.
115
191
  console.warn('[AgentSDK] Stream error:', error);
192
+ if (!isCurrent())
193
+ return;
194
+ failed = true;
195
+ dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'error' });
196
+ dispatch({ type: 'SET_ERROR', payload: error.message });
116
197
  callbacks.onError?.(error);
117
198
  },
118
199
  onStart: () => {
@@ -120,14 +201,28 @@ export function createActions(ctx) {
120
201
  callbacks.onStatusChange?.('streaming');
121
202
  },
122
203
  onEnd: () => {
123
- // Only reset if this is an unexpected stop (stream died, max reconnects exhausted).
124
- // If stopStream() was called intentionally, it clears the manager ref first,
125
- // so getStreamManager() will be undefined and we skip the duplicate dispatch.
126
- if (getStreamManager()) {
127
- setStreamManager(undefined);
204
+ // An intentional stop or a newer stream skips all of this.
205
+ if (!isCurrent() || getStreamManager() !== manager)
206
+ return;
207
+ // The stream ended cleanly or gave up. Release the ref now, so a
208
+ // sendMessage meanwhile opens a new stream (and makes this one stale).
209
+ setStreamManager(undefined);
210
+ // A turn still running would leave the UI busy with nothing to end
211
+ // it: read the chat, and poll it until idle if it is still busy.
212
+ resync()
213
+ .catch(() => null)
214
+ .then((chat) => {
215
+ if (!isCurrent() || getStreamManager())
216
+ return;
217
+ if (chat ? isChatBusy(chat) : isChatBusy(getState().chat)) {
218
+ pollChat(id, { untilIdle: true });
219
+ return;
220
+ }
221
+ if (failed)
222
+ return; // keep the 'error' status
128
223
  dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'idle' });
129
224
  callbacks.onStatusChange?.('idle');
130
- }
225
+ });
131
226
  },
132
227
  });
133
228
  // Listen for Chat object updates (status changes only — don't replace messages)
@@ -138,10 +233,6 @@ export function createActions(ctx) {
138
233
  checkTurnEnd(chatData);
139
234
  }
140
235
  });
141
- // Token-by-token streaming state, one accumulator per message being
142
- // streamed. Deltas name their message (DeltaEvent.resource_id), so state
143
- // never leaks between messages the way a single shared accumulator allowed.
144
- const deltaAccums = new Map();
145
236
  // Listen for ChatMessage updates
146
237
  manager.addEventListener('chat_messages', (message, fields) => {
147
238
  // A message that has reached a terminal state will receive no further
@@ -182,8 +273,12 @@ export function createActions(ctx) {
182
273
  setStreamManager(manager);
183
274
  manager.start();
184
275
  };
185
- /** Poll-based alternative to streaming for restricted environments */
186
- const pollChat = (id) => {
276
+ /**
277
+ * Poll-based alternative to streaming for restricted environments. With
278
+ * `untilIdle` (the fallback when a stream gave up mid-turn) polling stops
279
+ * once the chat is idle; the next sendMessage streams again.
280
+ */
281
+ const pollChat = (id, { untilIdle = false } = {}) => {
187
282
  let prevStatus = null;
188
283
  const manager = new PollManager({
189
284
  pollFunction: async () => {
@@ -205,6 +300,13 @@ export function createActions(ctx) {
205
300
  updateMessage(message);
206
301
  }
207
302
  }
303
+ if (untilIdle && !isChatBusy(chat) && getStreamManager() === manager) {
304
+ // Clear the ref first so onStop is a no-op.
305
+ setStreamManager(undefined);
306
+ manager.stop();
307
+ dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'idle' });
308
+ callbacks.onStatusChange?.('idle');
309
+ }
208
310
  }
209
311
  }
210
312
  catch (err) {
@@ -232,6 +334,7 @@ export function createActions(ctx) {
232
334
  manager.start();
233
335
  };
234
336
  const stopStream = () => {
337
+ streamGeneration++;
235
338
  const manager = getStreamManager();
236
339
  // Clear ref first so onStop callback (from manager.stop) is a no-op
237
340
  setStreamManager(undefined);
@@ -309,9 +412,9 @@ export function createActions(ctx) {
309
412
  dispatch({ type: 'SET_ERROR', payload: undefined });
310
413
  dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'idle' });
311
414
  },
312
- submitToolResult: async (toolInvocationId, result) => {
415
+ submitToolResult: async (toolInvocationId, result, attachments) => {
313
416
  try {
314
- await api.submitToolResult(client, toolInvocationId, result);
417
+ await api.submitToolResult(client, toolInvocationId, result, attachments);
315
418
  }
316
419
  catch (error) {
317
420
  console.error('[AgentSDK] Failed to submit tool result:', error);
@@ -7,9 +7,9 @@
7
7
  *
8
8
  * /agents/run stays for A2A/programmatic use.
9
9
  */
10
- import type { AlwaysAllowOptionsDTO, AlwaysAllowResultDTO, ChatAgentDTO, ChatDTO, ChatMessageDTO, ChatSettingsDTO, ChatSettingsRequest, InterruptDTO, ToolExplanationDTO } from '../types';
11
- import type { AgentOptions, AgentClient, AgentInfo, FileRef } from './types';
12
- import type { StreamRequest } from '../http/streamable';
10
+ import type { AlwaysAllowOptionsDTO, AlwaysAllowResultDTO, ChatAgentDTO, ChatDTO, ChatMessageDTO, ChatSettingsDTO, ChatSettingsRequest, InterruptDTO, ToolExplanationDTO } from '../types.js';
11
+ import type { AgentOptions, AgentClient, AgentInfo, FileRef } from './types.js';
12
+ import type { StreamRequest } from '../http/streamable.js';
13
13
  export interface SendResult {
14
14
  chatId: string;
15
15
  userMessage: ChatMessageDTO;
@@ -45,7 +45,7 @@ export declare function submitToolResult(client: AgentClient, toolInvocationId:
45
45
  payload?: Record<string, unknown>;
46
46
  };
47
47
  form_data?: Record<string, unknown>;
48
- }): Promise<void>;
48
+ }, attachments?: FileRef[]): Promise<void>;
49
49
  /**
50
50
  * Approve a call awaiting approval; the api then runs it.
51
51
  *
package/dist/agent/api.js CHANGED
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * /agents/run stays for A2A/programmatic use.
9
9
  */
10
- import { isAdHocConfig } from './types';
10
+ import { isAdHocConfig } from './types.js';
11
11
  function isFileRef(input) {
12
12
  return 'uri' in input && typeof input.uri === 'string';
13
13
  }
@@ -144,8 +144,11 @@ export async function setAgent(client, chatId, agentRef) {
144
144
  // =========================================================================
145
145
  // Tool operations
146
146
  // =========================================================================
147
- export async function submitToolResult(client, toolInvocationId, resultOrAction) {
148
- const data = typeof resultOrAction === 'string' ? { result: resultOrAction } : resultOrAction;
147
+ export async function submitToolResult(client, toolInvocationId, resultOrAction, attachments) {
148
+ const data = typeof resultOrAction === 'string' ? { result: resultOrAction } : { ...resultOrAction };
149
+ if (attachments && attachments.length > 0) {
150
+ data.attachments = attachments;
151
+ }
149
152
  await client.http.request('post', `/tools/${toolInvocationId}`, { data });
150
153
  }
151
154
  /**
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * React Context for sharing chat state and actions.
5
5
  */
6
- import type { AgentChatState, AgentChatActions, AgentClient } from './types';
6
+ import type { AgentChatState, AgentChatActions, AgentClient } from './types.js';
7
7
  /**
8
8
  * Context value combining state, actions, and client
9
9
  */
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * React hooks for accessing chat state and actions.
5
5
  */
6
- import type { AgentChatState, AgentChatActions, AgentClient } from './types';
7
- import type { ChatMessageDTO } from '../types';
6
+ import type { AgentChatState, AgentChatActions, AgentClient } from './types.js';
7
+ import type { ChatMessageDTO } from '../types.js';
8
8
  /**
9
9
  * Hook to access chat state
10
10
  *
@@ -4,7 +4,7 @@
4
4
  * React hooks for accessing chat state and actions.
5
5
  */
6
6
  import { useContext } from 'react';
7
- import { AgentChatContext } from './context';
7
+ import { AgentChatContext } from './context.js';
8
8
  /**
9
9
  * Hook to access chat state
10
10
  *
@@ -43,9 +43,9 @@
43
43
  * }
44
44
  * ```
45
45
  */
46
- export { AgentChatProvider } from './provider';
47
- export { useAgentChat, useAgentActions, useAgentClient, useAgentChatContext, useMessage, } from './hooks';
48
- export { AgentChatContext, type AgentChatContextValue } from './context';
49
- export type { AgentClient, FileRef, AdHocAgentConfig, TemplateAgentConfig, AgentOptions, AgentChatState, AgentChatActions, AlwaysAllowChoice, ChatStatus, AgentChatProviderProps, ClientTool, ClientToolHandlerFn, } from './types';
50
- export { isAdHocConfig, isTemplateConfig, isClientTool, extractToolSchemas, extractClientToolHandlers, } from './types';
51
- export { MCPMethodElicitationCreate, parseMCPInputState, elicitParams, isURLElicitation, buildMCPInputResult, type MCPInputState, type ElicitRequestParams, type ElicitRequestedSchema, type ElicitPropertySchema, } from './mcp-input';
46
+ export { AgentChatProvider } from './provider.js';
47
+ export { useAgentChat, useAgentActions, useAgentClient, useAgentChatContext, useMessage, } from './hooks.js';
48
+ export { AgentChatContext, type AgentChatContextValue } from './context.js';
49
+ export type { AgentClient, FileRef, AdHocAgentConfig, TemplateAgentConfig, AgentOptions, AgentChatState, AgentChatActions, AlwaysAllowChoice, ChatStatus, AgentChatProviderProps, ClientTool, ClientToolHandlerFn, ClientToolResult, } from './types.js';
50
+ export { isAdHocConfig, isTemplateConfig, isClientTool, extractToolSchemas, extractClientToolHandlers, } from './types.js';
51
+ export { MCPMethodElicitationCreate, parseMCPInputState, elicitParams, isURLElicitation, buildMCPInputResult, type MCPInputState, type ElicitRequestParams, type ElicitRequestedSchema, type ElicitPropertySchema, } from './mcp-input.js';
@@ -44,12 +44,12 @@
44
44
  * ```
45
45
  */
46
46
  // Provider
47
- export { AgentChatProvider } from './provider';
47
+ export { AgentChatProvider } from './provider.js';
48
48
  // Hooks
49
- export { useAgentChat, useAgentActions, useAgentClient, useAgentChatContext, useMessage, } from './hooks';
49
+ export { useAgentChat, useAgentActions, useAgentClient, useAgentChatContext, useMessage, } from './hooks.js';
50
50
  // Context (for advanced use cases)
51
- export { AgentChatContext } from './context';
51
+ export { AgentChatContext } from './context.js';
52
52
  // Type guards and helpers
53
- export { isAdHocConfig, isTemplateConfig, isClientTool, extractToolSchemas, extractClientToolHandlers, } from './types';
53
+ export { isAdHocConfig, isTemplateConfig, isClientTool, extractToolSchemas, extractClientToolHandlers, } from './types.js';
54
54
  // MCP input requests
55
- export { MCPMethodElicitationCreate, parseMCPInputState, elicitParams, isURLElicitation, buildMCPInputResult, } from './mcp-input';
55
+ export { MCPMethodElicitationCreate, parseMCPInputState, elicitParams, isURLElicitation, buildMCPInputResult, } from './mcp-input.js';
@@ -7,7 +7,7 @@
7
7
  * key, and submits them with `submitMCPInput`; the runtime then re-sends the
8
8
  * tool call with those responses.
9
9
  */
10
- import type { ElicitResult, InputRequest } from '../types';
10
+ import type { ElicitResult, InputRequest } from '../types.js';
11
11
  export declare const MCPMethodElicitationCreate = "elicitation/create";
12
12
  /** Stored in ToolInvocationDTO.data while an MCP tool call waits for the user. */
13
13
  export interface MCPInputState {
@@ -5,7 +5,7 @@
5
5
  * Uses React useReducer + Context pattern.
6
6
  */
7
7
  import React from 'react';
8
- import type { AgentChatProviderProps } from './types';
8
+ import type { AgentChatProviderProps } from './types.js';
9
9
  /**
10
10
  * AgentChatProvider - Provides chat state and actions to children
11
11
  *
@@ -6,11 +6,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
6
6
  * Uses React useReducer + Context pattern.
7
7
  */
8
8
  import { useReducer, useRef, useEffect, useMemo } from 'react';
9
- import { AgentChatContext } from './context';
10
- import { chatReducer, initialState } from './reducer';
11
- import { createActions, getClientToolHandlers } from './actions';
12
- import { fetchAgentInfo } from './api';
13
- import { isTemplateConfig } from './types';
9
+ import { AgentChatContext } from './context.js';
10
+ import { chatReducer, initialState } from './reducer.js';
11
+ import { createActions, getClientToolHandlers } from './actions.js';
12
+ import { fetchAgentInfo } from './api.js';
13
+ import { isTemplateConfig } from './types.js';
14
14
  function mergeHandlers(base, extra) {
15
15
  if (!extra || extra.size === 0)
16
16
  return base;
@@ -3,6 +3,6 @@
3
3
  *
4
4
  * Pure reducer for managing agent chat state.
5
5
  */
6
- import type { AgentChatState, ChatAction } from './types';
6
+ import type { AgentChatState, ChatAction } from './types.js';
7
7
  export declare const initialState: AgentChatState;
8
8
  export declare function chatReducer(state: AgentChatState, action: ChatAction): AgentChatState;
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Pure reducer for managing agent chat state.
5
5
  */
6
- import { ChatStatusBusy, ChatStatusAwaitingInput, ChatStatusIdle } from '../types';
7
- import { isRunInterrupted, isRunWorking } from '../utils';
6
+ import { ChatStatusBusy, ChatStatusAwaitingInput, ChatStatusIdle } from '../types.js';
7
+ import { isRunInterrupted, isRunWorking } from '../utils.js';
8
8
  function deriveChatStatus(run) {
9
9
  if (!run)
10
10
  return ChatStatusIdle;
@@ -4,11 +4,11 @@
4
4
  * Public types for the Agent chat module.
5
5
  */
6
6
  import type { Dispatch } from 'react';
7
- import type { AgentRunDTO, AgentVersionDTO, AlwaysAllowOptionsDTO, AlwaysAllowResultDTO, ToolExplanationDTO, ChatAgentDTO, ChatDTO, ChatMessageDTO, ChatSettingsDTO, ChatSettingsRequest, AgentTool, AgentConfigInput as GeneratedAgentConfig, CoreAppConfigInput as CoreAppConfig, FileRef, ElicitResult } from '../types';
8
- import type { HttpClient } from '../http/client';
9
- import type { StreamableManager } from '../http/streamable';
10
- import type { PollManager } from '../http/poll';
11
- export type { FileRef } from '../types';
7
+ import type { AgentRunDTO, AgentVersionDTO, AlwaysAllowOptionsDTO, AlwaysAllowResultDTO, ToolExplanationDTO, ChatAgentDTO, ChatDTO, ChatMessageDTO, ChatSettingsDTO, ChatSettingsRequest, AgentTool, AgentConfigInput as GeneratedAgentConfig, CoreAppConfigInput as CoreAppConfig, FileRef, ElicitResult } from '../types.js';
8
+ import type { HttpClient } from '../http/client.js';
9
+ import type { StreamableManager } from '../http/streamable.js';
10
+ import type { PollManager } from '../http/poll.js';
11
+ export type { FileRef } from '../types.js';
12
12
  /** The "always allow" option to save: a key from getAlwaysAllowOptions. */
13
13
  export interface AlwaysAllowChoice {
14
14
  option?: string;
@@ -129,7 +129,7 @@ export interface AgentChatActions {
129
129
  /** Clear the current error */
130
130
  clearError: () => void;
131
131
  /** Submit a tool result (for widgets/awaiting input) - usually called automatically by SDK */
132
- submitToolResult: (toolInvocationId: string, result: string) => Promise<void>;
132
+ submitToolResult: (toolInvocationId: string, result: string, attachments?: FileRef[]) => Promise<void>;
133
133
  /** Answer an MCP tool call's input requests (AwaitingInput status with MCPInputState data) */
134
134
  submitMCPInput: (toolInvocationId: string, responses: Record<string, ElicitResult>) => Promise<void>;
135
135
  /**
@@ -221,10 +221,23 @@ export interface AgentChatProviderProps {
221
221
  /** Children */
222
222
  children: React.ReactNode;
223
223
  }
224
+ /**
225
+ * What a client tool handler returns: the result text, or the text plus files.
226
+ *
227
+ * `attachments` are files already uploaded with POST /files (for example with
228
+ * `client.files.upload(dataUrlOrBlob)` or the chat's `uploadFile`), passed as
229
+ * `FileRef`s, the same as message attachments. An image attachment reaches the
230
+ * model as image input when the model supports images; other files are listed
231
+ * on the tool message.
232
+ */
233
+ export type ClientToolResult = string | {
234
+ result: string;
235
+ attachments?: FileRef[];
236
+ };
224
237
  /**
225
238
  * Client tool handler function - takes args and returns a result (sync or async)
226
239
  */
227
- export type ClientToolHandlerFn = (args: Record<string, unknown>) => Promise<string> | string;
240
+ export type ClientToolHandlerFn = (args: Record<string, unknown>) => Promise<ClientToolResult> | ClientToolResult;
228
241
  /**
229
242
  * Client tool definition - combines schema with handler
230
243
  */
@@ -1,8 +1,8 @@
1
- import { HttpClient } from '../http/client';
2
- import type { Response } from '../http/response';
3
- import type { AgentUpdateBody } from './write-bodies';
4
- import { FilesAPI } from './files';
5
- import { ChatDTO, ChatMessageDTO, ChannelContext, LLMDelta, LLMOutput, AgentConfigInput as AgentConfig, AgentDTO, AgentVersionDTO, CreateAgentRequest, FileDTO as File, InterruptDTO, CursorListRequest, CursorListResponse } from '../types';
1
+ import { HttpClient } from '../http/client.js';
2
+ import type { Response } from '../http/response.js';
3
+ import type { AgentUpdateBody } from './write-bodies.js';
4
+ import { FilesAPI } from './files.js';
5
+ import { ChannelContext, ChatDTO, ChatMessageDTO, LLMDelta, LLMOutput, AgentConfigInput as AgentConfig, AgentDTO, AgentVersionDTO, CreateAgentRequest, FileDTO as File, InterruptDTO, CursorListRequest, CursorListResponse, FileRef } from '../types.js';
6
6
  /** Internal tool definition returned by getInternalTools */
7
7
  export interface InternalToolDefinition {
8
8
  id: string;
@@ -67,11 +67,9 @@ export interface SendMessageOptions {
67
67
  */
68
68
  signal?: AbortSignal;
69
69
  /**
70
- * The channel this message came through (slack, telegram, a wearable...):
71
- * `channel_type` plus whatever `channel_metadata` the integration needs to
72
- * route the reply (thread ids, chat ids). Sent as `channel_context` on the
73
- * message; the API records it on the chat the first time it sees it, so
74
- * later turns in that chat inherit the routing.
70
+ * @deprecated The API no longer reads a channel context from an agent run;
71
+ * a chat's routing comes from the server's own channel receivers. Accepted
72
+ * for compatibility and not sent.
75
73
  */
76
74
  channel_context?: ChannelContext;
77
75
  }
@@ -110,7 +108,9 @@ export declare class Agent {
110
108
  /** Stop the current chat generation */
111
109
  stopChat(): Promise<void>;
112
110
  /**
113
- * Submit a tool result
111
+ * Submit a tool result. `attachments` are files uploaded with POST /files
112
+ * (`files.upload`) that the tool returns; images reach the model as image
113
+ * input when it supports images.
114
114
  */
115
115
  submitToolResult(toolInvocationId: string, resultOrAction: string | {
116
116
  action: {
@@ -118,7 +118,7 @@ export declare class Agent {
118
118
  payload?: Record<string, unknown>;
119
119
  };
120
120
  form_data?: Record<string, unknown>;
121
- }): Promise<void>;
121
+ }, attachments?: FileRef[]): Promise<void>;
122
122
  /** Stop streaming/polling and cleanup */
123
123
  disconnect(): void;
124
124
  /**
@@ -207,7 +207,9 @@ export declare class AgentsAPI {
207
207
  */
208
208
  create(config: string | AgentConfig, options?: AgentOptions): Agent;
209
209
  /**
210
- * Submit a tool result
210
+ * Submit a tool result. `attachments` are files uploaded with POST /files
211
+ * (`files.upload`) that the tool returns; images reach the model as image
212
+ * input when it supports images.
211
213
  */
212
214
  submitToolResult(toolInvocationId: string, resultOrAction: string | {
213
215
  action: {
@@ -215,7 +217,7 @@ export declare class AgentsAPI {
215
217
  payload?: Record<string, unknown>;
216
218
  };
217
219
  form_data?: Record<string, unknown>;
218
- }): Promise<void>;
220
+ }, attachments?: FileRef[]): Promise<void>;
219
221
  /**
220
222
  * Resolve an interrupt gate on an agent run.
221
223
  *