@inferencesh/sdk 0.15.0 → 0.16.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,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.16.0] - 2026-10-02
11
+
12
+ ### Added
13
+
14
+ - `useAgentActions().getAlwaysAllowOptions(toolInvocationId)`: what "always allow" can save for a call awaiting approval, narrowest first, computed by the api (`GET /chats/{id}/tools/{toolId}/always-allow/options`). Each option has a `key`, a `scope` (`exact`, `prefix`, `folder`, `remote`, `tool`), a `label` such as "npm run commands on laptop", and the chat `rules` it saves; `default` is the narrowest that answers the call again, and `unavailable` says why there are none.
15
+ - `useAgentActions().explainTool(toolInvocationId)`: a plain-words explanation of a call awaiting approval, `{ risk_level: 'low' | 'medium' | 'high', explanation, reasoning, risk }` (`POST /chats/{id}/tools/{toolId}/explain`). A model writes it the first time it is asked for and the api keeps it for the call; call it only when the person asks.
16
+ - Types: `ToolExplanationDTO`, `ToolRiskLevel`, `AlwaysAllowOptionsDTO`, `AlwaysAllowOptionDTO`, `AlwaysAllowScope`, `AlwaysAllowRequest`, `AlwaysAllowResultDTO`, `PolicyRuleDTO` (with `label`), `AlwaysAllowChoice`.
17
+
18
+ ### Changed
19
+
20
+ - `useAgentActions().alwaysAllowTool(toolInvocationId, { option })` saves the chosen option and resolves to the saved rules. The second argument used to be the tool name; a string is still accepted and means the api's default. A 409 (stale option) or 400 rejects without putting the chat in the error state.
21
+
22
+ ### Security
23
+
24
+ - `proxy-addr` 2.0.8 in the lockfile (CVE-2026-90711).
25
+
10
26
  ## [0.15.0] - 2026-10-02
11
27
 
12
28
  ### Added
@@ -368,16 +368,37 @@ export function createActions(ctx) {
368
368
  throw error;
369
369
  }
370
370
  },
371
- alwaysAllowTool: async (toolInvocationId, toolName) => {
371
+ getAlwaysAllowOptions: async (toolInvocationId) => {
372
+ const chatId = getChatId();
373
+ if (!chatId)
374
+ return null;
375
+ return api.getAlwaysAllowOptions(client, chatId, toolInvocationId);
376
+ },
377
+ explainTool: async (toolInvocationId) => {
378
+ const chatId = getChatId();
379
+ if (!chatId)
380
+ throw new Error('Cannot explain a tool call without a chat');
381
+ return api.explainTool(client, chatId, toolInvocationId);
382
+ },
383
+ alwaysAllowTool: async (toolInvocationId, choice) => {
372
384
  const chatId = getChatId();
373
385
  if (!chatId) {
374
386
  console.error('[AgentSDK] Cannot always-allow tool without a chatId');
375
- return;
387
+ return undefined;
376
388
  }
389
+ // A string is the tool name older callers passed; the api reads the
390
+ // tool from the call, so it only means "the default option".
391
+ const option = typeof choice === 'string' ? undefined : choice?.option;
377
392
  try {
378
- await api.alwaysAllowTool(client, chatId, toolInvocationId, toolName);
393
+ return await api.alwaysAllowTool(client, chatId, toolInvocationId, option);
379
394
  }
380
395
  catch (error) {
396
+ // 409: the option is stale (the rules changed since it was read);
397
+ // 400: nothing can be always-allowed here. The call is still
398
+ // waiting and the connection is fine: the caller reads the options
399
+ // again or shows why.
400
+ if (isInferenceError(error) && (error.statusCode === 409 || error.statusCode === 400))
401
+ throw error;
381
402
  console.error('[AgentSDK] Failed to always-allow tool:', error);
382
403
  const err = error instanceof Error ? error : new Error('Failed to always-allow tool');
383
404
  dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'error' });
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * /agents/run stays for A2A/programmatic use.
9
9
  */
10
- import type { ChatDTO, ChatMessageDTO, ChatSettingsRequest, InterruptDTO } from '../types';
10
+ import type { AlwaysAllowOptionsDTO, AlwaysAllowResultDTO, ChatDTO, ChatMessageDTO, ChatSettingsRequest, InterruptDTO, ToolExplanationDTO } from '../types';
11
11
  import type { AgentOptions, AgentClient, AgentInfo, FileRef } from './types';
12
12
  export interface SendResult {
13
13
  chatId: string;
@@ -46,7 +46,23 @@ export declare function submitToolResult(client: AgentClient, toolInvocationId:
46
46
  }): Promise<void>;
47
47
  export declare function approveTool(client: AgentClient, toolInvocationId: string): Promise<void>;
48
48
  export declare function rejectTool(client: AgentClient, toolInvocationId: string, reason?: string): Promise<void>;
49
- export declare function alwaysAllowTool(client: AgentClient, chatId: string, toolInvocationId: string, toolName: string): Promise<void>;
49
+ /**
50
+ * What "always allow" can save for a call awaiting approval, narrowest first.
51
+ * The api computes the options from the call; each one, saved, answers it again.
52
+ */
53
+ export declare function getAlwaysAllowOptions(client: AgentClient, chatId: string, toolInvocationId: string): Promise<AlwaysAllowOptionsDTO>;
54
+ /**
55
+ * Save an "always allow" option (a key from getAlwaysAllowOptions) as chat
56
+ * rules and approve the call once. Without an option the api saves its old
57
+ * default: the command exactly for remote_exec, the whole tool otherwise.
58
+ */
59
+ export declare function alwaysAllowTool(client: AgentClient, chatId: string, toolInvocationId: string, option?: string): Promise<AlwaysAllowResultDTO>;
60
+ /**
61
+ * Explain a call awaiting approval in plain words: what it does, why the agent
62
+ * appears to make it, and its risk. Generated by a model the first time and kept
63
+ * for the call, so asking again is free.
64
+ */
65
+ export declare function explainTool(client: AgentClient, chatId: string, toolInvocationId: string): Promise<ToolExplanationDTO>;
50
66
  export declare function updateChatSettings(client: AgentClient, chatId: string, settings: ChatSettingsRequest): Promise<ChatDTO>;
51
67
  export declare function resolveInterrupt(client: AgentClient, interruptId: string, decision: 'allow' | 'deny'): Promise<InterruptDTO>;
52
68
  export declare function listRunInterrupts(client: AgentClient, runId: string): Promise<InterruptDTO[]>;
package/dist/agent/api.js CHANGED
@@ -151,10 +151,32 @@ export async function approveTool(client, toolInvocationId) {
151
151
  export async function rejectTool(client, toolInvocationId, reason) {
152
152
  await client.http.request('post', `/tools/${toolInvocationId}/reject`, { data: { reason } });
153
153
  }
154
- export async function alwaysAllowTool(client, chatId, toolInvocationId, toolName) {
155
- await client.http.request('post', `/chats/${chatId}/tools/${toolInvocationId}/always-allow`, {
156
- data: { tool_name: toolName }
157
- });
154
+ /**
155
+ * What "always allow" can save for a call awaiting approval, narrowest first.
156
+ * The api computes the options from the call; each one, saved, answers it again.
157
+ */
158
+ export async function getAlwaysAllowOptions(client, chatId, toolInvocationId) {
159
+ const resp = await client.http.request('get', `/chats/${chatId}/tools/${toolInvocationId}/always-allow/options`);
160
+ return resp.data;
161
+ }
162
+ /**
163
+ * Save an "always allow" option (a key from getAlwaysAllowOptions) as chat
164
+ * rules and approve the call once. Without an option the api saves its old
165
+ * default: the command exactly for remote_exec, the whole tool otherwise.
166
+ */
167
+ export async function alwaysAllowTool(client, chatId, toolInvocationId, option) {
168
+ const data = option ? { option } : {};
169
+ const resp = await client.http.request('post', `/chats/${chatId}/tools/${toolInvocationId}/always-allow`, { data });
170
+ return resp.data;
171
+ }
172
+ /**
173
+ * Explain a call awaiting approval in plain words: what it does, why the agent
174
+ * appears to make it, and its risk. Generated by a model the first time and kept
175
+ * for the call, so asking again is free.
176
+ */
177
+ export async function explainTool(client, chatId, toolInvocationId) {
178
+ const resp = await client.http.request('post', `/chats/${chatId}/tools/${toolInvocationId}/explain`);
179
+ return resp.data;
158
180
  }
159
181
  export async function updateChatSettings(client, chatId, settings) {
160
182
  const resp = await client.http.request('post', `/chats/${chatId}/settings`, { data: settings });
@@ -46,6 +46,6 @@
46
46
  export { AgentChatProvider } from './provider';
47
47
  export { useAgentChat, useAgentActions, useAgentClient, useAgentChatContext, useMessage, } from './hooks';
48
48
  export { AgentChatContext, type AgentChatContextValue } from './context';
49
- export type { AgentClient, FileRef, AdHocAgentConfig, TemplateAgentConfig, AgentOptions, AgentChatState, AgentChatActions, ChatStatus, AgentChatProviderProps, ClientTool, ClientToolHandlerFn, } from './types';
49
+ export type { AgentClient, FileRef, AdHocAgentConfig, TemplateAgentConfig, AgentOptions, AgentChatState, AgentChatActions, AlwaysAllowChoice, ChatStatus, AgentChatProviderProps, ClientTool, ClientToolHandlerFn, } from './types';
50
50
  export { isAdHocConfig, isTemplateConfig, isClientTool, extractToolSchemas, extractClientToolHandlers, } from './types';
51
51
  export { MCPMethodElicitationCreate, parseMCPInputState, elicitParams, isURLElicitation, buildMCPInputResult, type MCPInputState, type ElicitRequestParams, type ElicitRequestedSchema, type ElicitPropertySchema, } from './mcp-input';
@@ -4,11 +4,15 @@
4
4
  * Public types for the Agent chat module.
5
5
  */
6
6
  import type { Dispatch } from 'react';
7
- import type { AgentRunDTO, AgentVersionDTO, ChatDTO, ChatMessageDTO, ChatSettingsRequest, AgentTool, AgentConfigInput as GeneratedAgentConfig, CoreAppConfigInput as CoreAppConfig, FileRef, ElicitResult } from '../types';
7
+ import type { AgentRunDTO, AgentVersionDTO, AlwaysAllowOptionsDTO, AlwaysAllowResultDTO, ToolExplanationDTO, ChatDTO, ChatMessageDTO, ChatSettingsRequest, AgentTool, AgentConfigInput as GeneratedAgentConfig, CoreAppConfigInput as CoreAppConfig, FileRef, ElicitResult } from '../types';
8
8
  import type { HttpClient } from '../http/client';
9
9
  import type { StreamableManager } from '../http/streamable';
10
10
  import type { PollManager } from '../http/poll';
11
11
  export type { FileRef } from '../types';
12
+ /** The "always allow" option to save: a key from getAlwaysAllowOptions. */
13
+ export interface AlwaysAllowChoice {
14
+ option?: string;
15
+ }
12
16
  /**
13
17
  * Minimal client interface required by the agent module.
14
18
  * This allows extended clients (like the app client) to be used.
@@ -132,8 +136,26 @@ export interface AgentChatActions {
132
136
  approveTool: (toolInvocationId: string) => Promise<void>;
133
137
  /** Reject a tool (for HIL approval - AwaitingApproval status) */
134
138
  rejectTool: (toolInvocationId: string, reason?: string) => Promise<void>;
135
- /** Always allow a tool for this chat (approves + auto-approves future invocations) */
136
- alwaysAllowTool: (toolInvocationId: string, toolName: string) => Promise<void>;
139
+ /**
140
+ * What "always allow" can save for a call awaiting approval, narrowest
141
+ * first, as computed by the api (null without a chat). Show the options'
142
+ * labels in order; `default` is the narrowest that answers the call again.
143
+ */
144
+ getAlwaysAllowOptions: (toolInvocationId: string) => Promise<AlwaysAllowOptionsDTO | null>;
145
+ /**
146
+ * Explain a call awaiting approval (risk level, what it does, why, what could
147
+ * go wrong). A model generates it the first time it is asked for; later calls
148
+ * return the kept answer. Call it only when the person asks.
149
+ */
150
+ explainTool: (toolInvocationId: string) => Promise<ToolExplanationDTO>;
151
+ /**
152
+ * Always allow a call: save the chosen option's chat rules (`{ option: key }`
153
+ * from getAlwaysAllowOptions) and approve the call once. Without an option the
154
+ * api saves its default. A string is accepted from older callers (it was the
155
+ * tool name) and means the default. Rejects with a 409 when the option is
156
+ * stale: read the options again.
157
+ */
158
+ alwaysAllowTool: (toolInvocationId: string, choice?: AlwaysAllowChoice | string) => Promise<AlwaysAllowResultDTO | undefined>;
137
159
  /** Change this chat's settings, e.g. allow_all_tools (switching it on approves the calls waiting) */
138
160
  updateChatSettings: (settings: ChatSettingsRequest) => Promise<void>;
139
161
  /** Hand this chat to another agent (namespace/name); the next message goes to it. Agents our loop runs only. */
package/dist/types.d.ts CHANGED
@@ -1834,6 +1834,189 @@ export interface ChatMessageDTO extends BaseModelDTO, PermissionModelDTO {
1834
1834
  tool_call_id?: string;
1835
1835
  tool_invocations?: ToolInvocationDTO[];
1836
1836
  }
1837
+ /**
1838
+ * PolicyEffect is a rule's outcome: allow, ask or deny.
1839
+ */
1840
+ export type PolicyEffect = "allow" | "ask" | "deny";
1841
+ export declare const PolicyEffectAllow: PolicyEffect;
1842
+ export declare const PolicyEffectAsk: PolicyEffect;
1843
+ export declare const PolicyEffectDeny: PolicyEffect;
1844
+ /**
1845
+ * PolicyRuleDTO is one rule, typed form Kind[selector](specifier).
1846
+ */
1847
+ export interface PolicyRuleDTO {
1848
+ id: string;
1849
+ effect: PolicyEffect;
1850
+ /**
1851
+ * Kind: RemoteExec, Workspace, Harness, Tool (and, from phase 3, App,
1852
+ * Agent, Knowledge, Mcp, Flow, WebFetch).
1853
+ */
1854
+ kind: string;
1855
+ /**
1856
+ * Selector narrows the rule to one remote (its id) or a tag (tag:<name>);
1857
+ * empty applies everywhere.
1858
+ */
1859
+ selector: string;
1860
+ /**
1861
+ * Specifier is the kind's pattern: a command (`npm test`, `git push:*`),
1862
+ * a folder (`~/proj/**`), a harness tool with its pattern
1863
+ * (`Bash(git status:*)`, `Edit`), or a loop tool's name. Empty is the
1864
+ * whole kind.
1865
+ */
1866
+ specifier: string;
1867
+ /**
1868
+ * Label is the rule in words, e.g. "git commit commands on Laptop", the
1869
+ * same words an approval prompt's "always allow" options use.
1870
+ */
1871
+ label: string;
1872
+ /**
1873
+ * CreatedAt is null for a rule that is not a stored row yet: an entry of
1874
+ * the chat's always-allow list from before rules existed, or the chat's
1875
+ * "allow every tool" setting.
1876
+ */
1877
+ created_at?: string;
1878
+ /**
1879
+ * CreatedBy is the user who wrote the rule; empty for those above.
1880
+ */
1881
+ created_by: string;
1882
+ }
1883
+ /**
1884
+ * AlwaysAllowScope is how far an "always allow" option reaches, narrowest
1885
+ * first.
1886
+ */
1887
+ export type AlwaysAllowScope = "exact" | "prefix" | "folder" | "remote" | "tool";
1888
+ /**
1889
+ * AlwaysAllowScopeExact: this call exactly (each piece of the command,
1890
+ * the file, the domain).
1891
+ */
1892
+ export declare const AlwaysAllowScopeExact: AlwaysAllowScope;
1893
+ /**
1894
+ * AlwaysAllowScopePrefix: commands that start the same way, per piece
1895
+ * (git commit:*, npm run:*).
1896
+ */
1897
+ export declare const AlwaysAllowScopePrefix: AlwaysAllowScope;
1898
+ /**
1899
+ * AlwaysAllowScopeFolder: files in the same folder.
1900
+ */
1901
+ export declare const AlwaysAllowScopeFolder: AlwaysAllowScope;
1902
+ /**
1903
+ * AlwaysAllowScopeRemote: every command on the machine.
1904
+ */
1905
+ export declare const AlwaysAllowScopeRemote: AlwaysAllowScope;
1906
+ /**
1907
+ * AlwaysAllowScopeTool: every call of the tool.
1908
+ */
1909
+ export declare const AlwaysAllowScopeTool: AlwaysAllowScope;
1910
+ /**
1911
+ * AlwaysAllowOptionDTO is one "always allow" choice on an approval prompt:
1912
+ * the chat rules it saves.
1913
+ */
1914
+ export interface AlwaysAllowOptionDTO {
1915
+ /**
1916
+ * Key names the option in POST .../always-allow. It changes when the
1917
+ * rules it stands for change.
1918
+ */
1919
+ key: string;
1920
+ scope: AlwaysAllowScope;
1921
+ /**
1922
+ * Label is the option in words, e.g. "git clone commands on Laptop".
1923
+ */
1924
+ label: string;
1925
+ /**
1926
+ * Description adds why a broad option is the narrowest offered, e.g.
1927
+ * the command uses shell syntax no narrower rule can match.
1928
+ */
1929
+ description?: string;
1930
+ /**
1931
+ * Rules are the chat rules the option saves (not stored yet: no id).
1932
+ */
1933
+ rules: PolicyRuleDTO[];
1934
+ }
1935
+ /**
1936
+ * AlwaysAllowOptionsDTO is GET /chats/{id}/tools/{toolId}/always-allow/options:
1937
+ * the options for a call awaiting approval, narrowest first. Each one,
1938
+ * saved, answers this call again. Computed by the api from the call; the
1939
+ * client never builds rules.
1940
+ */
1941
+ export interface AlwaysAllowOptionsDTO {
1942
+ options: AlwaysAllowOptionDTO[];
1943
+ /**
1944
+ * Default is the key of the narrowest option, "" when there is none.
1945
+ */
1946
+ default?: string;
1947
+ /**
1948
+ * Unavailable says why there are no options: a policy allows only its
1949
+ * own rules, or no chat rule could allow this call.
1950
+ */
1951
+ unavailable?: string;
1952
+ }
1953
+ /**
1954
+ * AlwaysAllowRequest is POST /chats/{id}/tools/{toolId}/always-allow. The
1955
+ * api saves the option's rules to the chat and approves the call once.
1956
+ */
1957
+ export interface AlwaysAllowRequest {
1958
+ /**
1959
+ * Option is an option key from the options endpoint. A key that no
1960
+ * longer names a current option is refused (409). Without one, the
1961
+ * call saves what "always allow" saved before options existed: the
1962
+ * command's pieces exactly for remote_exec, the whole tool otherwise.
1963
+ */
1964
+ option?: string;
1965
+ /**
1966
+ * ToolName is accepted from older clients and ignored: the call names
1967
+ * its own tool.
1968
+ */
1969
+ tool_name?: string;
1970
+ }
1971
+ /**
1972
+ * AlwaysAllowResultDTO is what an always-allow saved.
1973
+ */
1974
+ export interface AlwaysAllowResultDTO {
1975
+ /**
1976
+ * Option is the option chosen; null for a call without one.
1977
+ */
1978
+ option?: AlwaysAllowOptionDTO;
1979
+ /**
1980
+ * Rules are the chat rules now in place for it.
1981
+ */
1982
+ rules: PolicyRuleDTO[];
1983
+ }
1984
+ /**
1985
+ * ToolRiskLevel is how risky an explained call is.
1986
+ */
1987
+ export type ToolRiskLevel = "low" | "medium" | "high";
1988
+ /**
1989
+ * ToolRiskLow: safe development workflows (reads, builds, tests).
1990
+ */
1991
+ export declare const ToolRiskLow: ToolRiskLevel;
1992
+ /**
1993
+ * ToolRiskMedium: changes that can be undone.
1994
+ */
1995
+ export declare const ToolRiskMedium: ToolRiskLevel;
1996
+ /**
1997
+ * ToolRiskHigh: dangerous or irreversible changes.
1998
+ */
1999
+ export declare const ToolRiskHigh: ToolRiskLevel;
2000
+ /**
2001
+ * ToolExplanationDTO is POST /chats/{id}/tools/{toolId}/explain: a model's
2002
+ * plain-words reading of a call awaiting approval (Claude Code's permission
2003
+ * explainer). Generated once, when the person asks, and kept for the call.
2004
+ */
2005
+ export interface ToolExplanationDTO {
2006
+ risk_level: ToolRiskLevel;
2007
+ /**
2008
+ * Explanation is what the call does, in one or two sentences.
2009
+ */
2010
+ explanation: string;
2011
+ /**
2012
+ * Reasoning is why the agent appears to be making it.
2013
+ */
2014
+ reasoning: string;
2015
+ /**
2016
+ * Risk is what could go wrong, in a few words.
2017
+ */
2018
+ risk: string;
2019
+ }
1837
2020
  /**
1838
2021
  * CredentialDTO is the API response for a credential (never exposes secrets).
1839
2022
  */
package/dist/types.js CHANGED
@@ -302,6 +302,43 @@ export const ScopeGroupKnowledge = "knowledge";
302
302
  export const ScopeGroupArtifacts = "artifacts";
303
303
  export const ScopeGroupUser = "user";
304
304
  export const ScopeGroupSettings = "settings";
305
+ export const PolicyEffectAllow = "allow";
306
+ export const PolicyEffectAsk = "ask";
307
+ export const PolicyEffectDeny = "deny";
308
+ /**
309
+ * AlwaysAllowScopeExact: this call exactly (each piece of the command,
310
+ * the file, the domain).
311
+ */
312
+ export const AlwaysAllowScopeExact = "exact";
313
+ /**
314
+ * AlwaysAllowScopePrefix: commands that start the same way, per piece
315
+ * (git commit:*, npm run:*).
316
+ */
317
+ export const AlwaysAllowScopePrefix = "prefix";
318
+ /**
319
+ * AlwaysAllowScopeFolder: files in the same folder.
320
+ */
321
+ export const AlwaysAllowScopeFolder = "folder";
322
+ /**
323
+ * AlwaysAllowScopeRemote: every command on the machine.
324
+ */
325
+ export const AlwaysAllowScopeRemote = "remote";
326
+ /**
327
+ * AlwaysAllowScopeTool: every call of the tool.
328
+ */
329
+ export const AlwaysAllowScopeTool = "tool";
330
+ /**
331
+ * ToolRiskLow: safe development workflows (reads, builds, tests).
332
+ */
333
+ export const ToolRiskLow = "low";
334
+ /**
335
+ * ToolRiskMedium: changes that can be undone.
336
+ */
337
+ export const ToolRiskMedium = "medium";
338
+ /**
339
+ * ToolRiskHigh: dangerous or irreversible changes.
340
+ */
341
+ export const ToolRiskHigh = "high";
305
342
  export const ErrorCodeInvalidRequest = "invalid_request";
306
343
  export const ErrorCodeValidationError = "validation_error";
307
344
  export const ErrorCodeUnauthorized = "unauthorized";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inferencesh/sdk",
3
- "version": "0.15.0",
3
+ "version": "0.16.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",