@combycode/llm-sdk 1.3.0 → 1.4.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
@@ -6,6 +6,27 @@ All notable changes to `@combycode/llm-sdk` are documented here. The format foll
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.4.0] - 2026-07-06
10
+
11
+ ### Fixed
12
+ - **xAI hosted code-execution files** now surface on `response.files` (parity with the other
13
+ providers). xAI returns code-interpreter output files inline inside the `code_interpreter_call`
14
+ `logs` JSON (`output_files:[{file_name, mime_type, data:[…bytes]}]`) and only when the request
15
+ asks for them — so `XAIResponsesAdapter` now sends `include:['code_interpreter_call.outputs']`
16
+ when `code_interpreter` is used and extracts the inline bytes into `FileOutput`. Verified live on
17
+ `complete()` and `stream()`. `OpenAIResponsesAdapter.filesFromOutputItem` is now a `protected`
18
+ overridable method so Responses-compatible providers can extend file extraction.
19
+
20
+ ### Added
21
+ - **Adapter-sourced builtin-tool capabilities in the catalog.** Every tool-capable (chat-family)
22
+ model now carries `capabilities.builtinTools` — `['web_search', 'code_interpreter']` for
23
+ anthropic/openai/google/xai, `['web_search']` for openrouter (it doesn't route hosted code
24
+ execution) — injected at catalog load from a single provider map (`PROVIDER_BUILTIN_TOOLS`) that
25
+ mirrors what the adapters actually support. New `catalog.builtinToolsFor()` /
26
+ `supportsBuiltinTool()` accessors and `select('web_search')` / `select('code_interpreter')`
27
+ queries (`search` stays an alias for `web_search`). Non-tool models (embeddings/tts/image) get
28
+ none. A reliable per-model source can refine this later.
29
+
9
30
  ## [1.3.0] - 2026-07-06
10
31
 
11
32
  ### Added
@@ -22105,6 +22105,15 @@ var catalog_default5 = {
22105
22105
  }
22106
22106
  };
22107
22107
 
22108
+ // src/llm/providers/builtin-tools.ts
22109
+ var PROVIDER_BUILTIN_TOOLS = {
22110
+ anthropic: ["web_search", "code_interpreter"],
22111
+ openai: ["web_search", "code_interpreter"],
22112
+ google: ["web_search", "code_interpreter"],
22113
+ xai: ["web_search", "code_interpreter"],
22114
+ openrouter: ["web_search"]
22115
+ };
22116
+
22108
22117
  // src/plugins/model-catalog/catalog.ts
22109
22118
  var PROVIDER_DEFAULT_CATALOGS = [
22110
22119
  catalog_default,
@@ -22142,6 +22151,13 @@ var DEFAULT_REASONING = {
22142
22151
  encryptedContent: false,
22143
22152
  summaryAvailable: false
22144
22153
  };
22154
+ function withBuiltinTools(provider, caps) {
22155
+ if (caps.builtinTools !== void 0) return caps;
22156
+ if (!caps.toolUse) return caps;
22157
+ const tools = PROVIDER_BUILTIN_TOOLS[provider];
22158
+ if (!tools || tools.length === 0) return caps;
22159
+ return { ...caps, builtinTools: [...tools] };
22160
+ }
22145
22161
  var ModelCatalog = class {
22146
22162
  models = /* @__PURE__ */ new Map();
22147
22163
  /** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
@@ -22160,7 +22176,7 @@ var ModelCatalog = class {
22160
22176
  supportedApis: info.supportedApis ?? [info.preferredApi ?? "completions"],
22161
22177
  contextWindow: info.contextWindow,
22162
22178
  maxOutput: info.maxOutput,
22163
- capabilities: { ...DEFAULT_CAPABILITIES, ...info.capabilities },
22179
+ capabilities: withBuiltinTools(provider, { ...DEFAULT_CAPABILITIES, ...info.capabilities }),
22164
22180
  reasoning: { ...DEFAULT_REASONING, ...info.reasoning },
22165
22181
  mediaOnly: info.mediaOnly,
22166
22182
  tokenizer: info.tokenizer,
@@ -22214,6 +22230,15 @@ var ModelCatalog = class {
22214
22230
  supportsTools(provider, model) {
22215
22231
  return this.get(provider, model)?.capabilities.toolUse ?? false;
22216
22232
  }
22233
+ /** Hosted server-side builtin tools this model supports (e.g. `['web_search',
22234
+ * 'code_interpreter']`). Empty when unknown or the model isn't tool-capable. */
22235
+ builtinToolsFor(provider, model) {
22236
+ return [...this.get(provider, model)?.capabilities.builtinTools ?? []];
22237
+ }
22238
+ /** Whether this model supports a specific hosted builtin tool. */
22239
+ supportsBuiltinTool(provider, model, tool) {
22240
+ return this.get(provider, model)?.capabilities.builtinTools?.includes(tool) ?? false;
22241
+ }
22217
22242
  supportsPreviousResponseId(provider, model) {
22218
22243
  const info = this.get(provider, model);
22219
22244
  if (info && !info.supportedApis.some((a) => a === "responses" || a === "interactions")) {
@@ -26216,7 +26241,7 @@ var OpenAIResponsesAdapter = class {
26216
26241
  let text = "";
26217
26242
  for (const item of output) {
26218
26243
  const type = item.type;
26219
- files.push(...filesFromResponsesOutputItem(item));
26244
+ files.push(...this.filesFromOutputItem(item));
26220
26245
  if (type === "message") {
26221
26246
  const itemContent = item.content ?? [];
26222
26247
  for (const c of itemContent) {
@@ -26318,7 +26343,7 @@ var OpenAIResponsesAdapter = class {
26318
26343
  }
26319
26344
  if (type === "response.output_item.done") {
26320
26345
  const item = data.item;
26321
- for (const file of filesFromResponsesOutputItem(item)) events.push({ type: "file", file });
26346
+ for (const file of this.filesFromOutputItem(item)) events.push({ type: "file", file });
26322
26347
  if (item?.type === "function_call") {
26323
26348
  events.push({ type: "tool_call_end", id: item.call_id ?? "" });
26324
26349
  }
@@ -26354,6 +26379,12 @@ var OpenAIResponsesAdapter = class {
26354
26379
  createStreamParser() {
26355
26380
  return (event) => this.parseStreamEvent(event);
26356
26381
  }
26382
+ /** Hosted code-execution output files from one output item. Overridable so
26383
+ * Responses-compatible providers with a different file shape (e.g. xAI, which
26384
+ * embeds files in the code-interpreter `logs` payload) can extend it. */
26385
+ filesFromOutputItem(item) {
26386
+ return filesFromResponsesOutputItem(item);
26387
+ }
26357
26388
  parseUsage(u) {
26358
26389
  if (!u) return emptyUsage();
26359
26390
  const input = u.input_tokens ?? 0;
@@ -26918,6 +26949,29 @@ var XAIMediaAdapter = class {
26918
26949
  };
26919
26950
 
26920
26951
  // src/llm/providers/xai/responses.ts
26952
+ function xaiCodeExecFiles(item) {
26953
+ if (item.type !== "code_interpreter_call") return [];
26954
+ const files = [];
26955
+ for (const out of item.outputs ?? []) {
26956
+ if (out.type !== "logs" || typeof out.logs !== "string") continue;
26957
+ let parsed;
26958
+ try {
26959
+ parsed = JSON.parse(out.logs);
26960
+ } catch {
26961
+ continue;
26962
+ }
26963
+ for (const f of parsed.output_files ?? []) {
26964
+ if (!Array.isArray(f.data)) continue;
26965
+ files.push({
26966
+ data: bytesToBase64(Uint8Array.from(f.data)),
26967
+ ...typeof f.file_name === "string" ? { name: f.file_name } : {},
26968
+ ...typeof f.mime_type === "string" ? { mimeType: f.mime_type } : {},
26969
+ source: "code_execution"
26970
+ });
26971
+ }
26972
+ }
26973
+ return files;
26974
+ }
26921
26975
  var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
26922
26976
  name = "xai";
26923
26977
  constructor(config) {
@@ -26937,8 +26991,20 @@ var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
26937
26991
  if (!req.model.includes("multi-agent")) {
26938
26992
  delete body.reasoning;
26939
26993
  }
26994
+ const usesCodeInterpreter = req.tools?.some(
26995
+ (t) => !isFunctionTool(t) && t.type === "code_interpreter"
26996
+ );
26997
+ if (usesCodeInterpreter) {
26998
+ const include = /* @__PURE__ */ new Set([...body.include ?? [], "code_interpreter_call.outputs"]);
26999
+ body.include = [...include];
27000
+ }
26940
27001
  return result;
26941
27002
  }
27003
+ /** xAI embeds code-execution files inline in the `logs` payload — extend the base
27004
+ * extraction (which handles OpenAI-style annotations / image URLs) with the xAI shape. */
27005
+ filesFromOutputItem(item) {
27006
+ return [...super.filesFromOutputItem(item), ...xaiCodeExecFiles(item)];
27007
+ }
26942
27008
  };
26943
27009
 
26944
27010
  // src/util/json-schema.ts
@@ -36863,12 +36929,16 @@ var DEFAULT_TAGS = {
36863
36929
  huge: "context:large"
36864
36930
  };
36865
36931
  var CAP_KEYS = {
36866
- search: "webSearch",
36867
36932
  vision: "vision",
36868
36933
  tools: "toolUse",
36869
36934
  audio: "audio",
36870
36935
  structured: "structuredOutput"
36871
36936
  };
36937
+ var BUILTIN_TOOL_KEYS = {
36938
+ search: "web_search",
36939
+ web_search: "web_search",
36940
+ code_interpreter: "code_interpreter"
36941
+ };
36872
36942
  var KNOWN_KEYS = /* @__PURE__ */ new Set([
36873
36943
  "price",
36874
36944
  "context",
@@ -36878,7 +36948,8 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
36878
36948
  "status",
36879
36949
  "provider",
36880
36950
  "active",
36881
- ...Object.keys(CAP_KEYS)
36951
+ ...Object.keys(CAP_KEYS),
36952
+ ...Object.keys(BUILTIN_TOOL_KEYS)
36882
36953
  ]);
36883
36954
  function parseNum(v) {
36884
36955
  const m = /^([\d.]+)\s*([kKmM]?)$/.exec(v.trim());
@@ -36947,6 +37018,13 @@ function matches(m, c, th, tier) {
36947
37018
  case "active":
36948
37019
  return isNo(c.value) ? m.active === false : m.active !== false;
36949
37020
  default: {
37021
+ const tool = BUILTIN_TOOL_KEYS[c.key];
37022
+ if (tool) {
37023
+ const inList = m.capabilities.builtinTools?.includes(tool) ?? false;
37024
+ const legacy = tool === "web_search" && !!m.capabilities.webSearch;
37025
+ const has2 = inList || legacy;
37026
+ return isNo(c.value) ? !has2 : has2;
37027
+ }
36950
37028
  const cap = CAP_KEYS[c.key];
36951
37029
  const has = !!m.capabilities[cap];
36952
37030
  return isNo(c.value) ? !has : has;
package/dist/index.js CHANGED
@@ -22032,6 +22032,15 @@ var catalog_default5 = {
22032
22032
  }
22033
22033
  };
22034
22034
 
22035
+ // src/llm/providers/builtin-tools.ts
22036
+ var PROVIDER_BUILTIN_TOOLS = {
22037
+ anthropic: ["web_search", "code_interpreter"],
22038
+ openai: ["web_search", "code_interpreter"],
22039
+ google: ["web_search", "code_interpreter"],
22040
+ xai: ["web_search", "code_interpreter"],
22041
+ openrouter: ["web_search"]
22042
+ };
22043
+
22035
22044
  // src/plugins/model-catalog/catalog.ts
22036
22045
  var PROVIDER_DEFAULT_CATALOGS = [
22037
22046
  catalog_default,
@@ -22069,6 +22078,13 @@ var DEFAULT_REASONING = {
22069
22078
  encryptedContent: false,
22070
22079
  summaryAvailable: false
22071
22080
  };
22081
+ function withBuiltinTools(provider, caps) {
22082
+ if (caps.builtinTools !== void 0) return caps;
22083
+ if (!caps.toolUse) return caps;
22084
+ const tools = PROVIDER_BUILTIN_TOOLS[provider];
22085
+ if (!tools || tools.length === 0) return caps;
22086
+ return { ...caps, builtinTools: [...tools] };
22087
+ }
22072
22088
  var ModelCatalog = class {
22073
22089
  models = /* @__PURE__ */ new Map();
22074
22090
  /** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
@@ -22087,7 +22103,7 @@ var ModelCatalog = class {
22087
22103
  supportedApis: info.supportedApis ?? [info.preferredApi ?? "completions"],
22088
22104
  contextWindow: info.contextWindow,
22089
22105
  maxOutput: info.maxOutput,
22090
- capabilities: { ...DEFAULT_CAPABILITIES, ...info.capabilities },
22106
+ capabilities: withBuiltinTools(provider, { ...DEFAULT_CAPABILITIES, ...info.capabilities }),
22091
22107
  reasoning: { ...DEFAULT_REASONING, ...info.reasoning },
22092
22108
  mediaOnly: info.mediaOnly,
22093
22109
  tokenizer: info.tokenizer,
@@ -22141,6 +22157,15 @@ var ModelCatalog = class {
22141
22157
  supportsTools(provider, model) {
22142
22158
  return this.get(provider, model)?.capabilities.toolUse ?? false;
22143
22159
  }
22160
+ /** Hosted server-side builtin tools this model supports (e.g. `['web_search',
22161
+ * 'code_interpreter']`). Empty when unknown or the model isn't tool-capable. */
22162
+ builtinToolsFor(provider, model) {
22163
+ return [...this.get(provider, model)?.capabilities.builtinTools ?? []];
22164
+ }
22165
+ /** Whether this model supports a specific hosted builtin tool. */
22166
+ supportsBuiltinTool(provider, model, tool) {
22167
+ return this.get(provider, model)?.capabilities.builtinTools?.includes(tool) ?? false;
22168
+ }
22144
22169
  supportsPreviousResponseId(provider, model) {
22145
22170
  const info = this.get(provider, model);
22146
22171
  if (info && !info.supportedApis.some((a) => a === "responses" || a === "interactions")) {
@@ -26143,7 +26168,7 @@ var OpenAIResponsesAdapter = class {
26143
26168
  let text = "";
26144
26169
  for (const item of output) {
26145
26170
  const type = item.type;
26146
- files.push(...filesFromResponsesOutputItem(item));
26171
+ files.push(...this.filesFromOutputItem(item));
26147
26172
  if (type === "message") {
26148
26173
  const itemContent = item.content ?? [];
26149
26174
  for (const c of itemContent) {
@@ -26245,7 +26270,7 @@ var OpenAIResponsesAdapter = class {
26245
26270
  }
26246
26271
  if (type === "response.output_item.done") {
26247
26272
  const item = data.item;
26248
- for (const file of filesFromResponsesOutputItem(item)) events.push({ type: "file", file });
26273
+ for (const file of this.filesFromOutputItem(item)) events.push({ type: "file", file });
26249
26274
  if (item?.type === "function_call") {
26250
26275
  events.push({ type: "tool_call_end", id: item.call_id ?? "" });
26251
26276
  }
@@ -26281,6 +26306,12 @@ var OpenAIResponsesAdapter = class {
26281
26306
  createStreamParser() {
26282
26307
  return (event) => this.parseStreamEvent(event);
26283
26308
  }
26309
+ /** Hosted code-execution output files from one output item. Overridable so
26310
+ * Responses-compatible providers with a different file shape (e.g. xAI, which
26311
+ * embeds files in the code-interpreter `logs` payload) can extend it. */
26312
+ filesFromOutputItem(item) {
26313
+ return filesFromResponsesOutputItem(item);
26314
+ }
26284
26315
  parseUsage(u) {
26285
26316
  if (!u) return emptyUsage();
26286
26317
  const input = u.input_tokens ?? 0;
@@ -26845,6 +26876,29 @@ var XAIMediaAdapter = class {
26845
26876
  };
26846
26877
 
26847
26878
  // src/llm/providers/xai/responses.ts
26879
+ function xaiCodeExecFiles(item) {
26880
+ if (item.type !== "code_interpreter_call") return [];
26881
+ const files = [];
26882
+ for (const out of item.outputs ?? []) {
26883
+ if (out.type !== "logs" || typeof out.logs !== "string") continue;
26884
+ let parsed;
26885
+ try {
26886
+ parsed = JSON.parse(out.logs);
26887
+ } catch {
26888
+ continue;
26889
+ }
26890
+ for (const f of parsed.output_files ?? []) {
26891
+ if (!Array.isArray(f.data)) continue;
26892
+ files.push({
26893
+ data: bytesToBase64(Uint8Array.from(f.data)),
26894
+ ...typeof f.file_name === "string" ? { name: f.file_name } : {},
26895
+ ...typeof f.mime_type === "string" ? { mimeType: f.mime_type } : {},
26896
+ source: "code_execution"
26897
+ });
26898
+ }
26899
+ }
26900
+ return files;
26901
+ }
26848
26902
  var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
26849
26903
  name = "xai";
26850
26904
  constructor(config) {
@@ -26864,8 +26918,20 @@ var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
26864
26918
  if (!req.model.includes("multi-agent")) {
26865
26919
  delete body.reasoning;
26866
26920
  }
26921
+ const usesCodeInterpreter = req.tools?.some(
26922
+ (t) => !isFunctionTool(t) && t.type === "code_interpreter"
26923
+ );
26924
+ if (usesCodeInterpreter) {
26925
+ const include = /* @__PURE__ */ new Set([...body.include ?? [], "code_interpreter_call.outputs"]);
26926
+ body.include = [...include];
26927
+ }
26867
26928
  return result;
26868
26929
  }
26930
+ /** xAI embeds code-execution files inline in the `logs` payload — extend the base
26931
+ * extraction (which handles OpenAI-style annotations / image URLs) with the xAI shape. */
26932
+ filesFromOutputItem(item) {
26933
+ return [...super.filesFromOutputItem(item), ...xaiCodeExecFiles(item)];
26934
+ }
26869
26935
  };
26870
26936
 
26871
26937
  // src/util/json-schema.ts
@@ -36790,12 +36856,16 @@ var DEFAULT_TAGS = {
36790
36856
  huge: "context:large"
36791
36857
  };
36792
36858
  var CAP_KEYS = {
36793
- search: "webSearch",
36794
36859
  vision: "vision",
36795
36860
  tools: "toolUse",
36796
36861
  audio: "audio",
36797
36862
  structured: "structuredOutput"
36798
36863
  };
36864
+ var BUILTIN_TOOL_KEYS = {
36865
+ search: "web_search",
36866
+ web_search: "web_search",
36867
+ code_interpreter: "code_interpreter"
36868
+ };
36799
36869
  var KNOWN_KEYS = /* @__PURE__ */ new Set([
36800
36870
  "price",
36801
36871
  "context",
@@ -36805,7 +36875,8 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
36805
36875
  "status",
36806
36876
  "provider",
36807
36877
  "active",
36808
- ...Object.keys(CAP_KEYS)
36878
+ ...Object.keys(CAP_KEYS),
36879
+ ...Object.keys(BUILTIN_TOOL_KEYS)
36809
36880
  ]);
36810
36881
  function parseNum(v) {
36811
36882
  const m = /^([\d.]+)\s*([kKmM]?)$/.exec(v.trim());
@@ -36874,6 +36945,13 @@ function matches(m, c, th, tier) {
36874
36945
  case "active":
36875
36946
  return isNo(c.value) ? m.active === false : m.active !== false;
36876
36947
  default: {
36948
+ const tool = BUILTIN_TOOL_KEYS[c.key];
36949
+ if (tool) {
36950
+ const inList = m.capabilities.builtinTools?.includes(tool) ?? false;
36951
+ const legacy = tool === "web_search" && !!m.capabilities.webSearch;
36952
+ const has2 = inList || legacy;
36953
+ return isNo(c.value) ? !has2 : has2;
36954
+ }
36877
36955
  const cap = CAP_KEYS[c.key];
36878
36956
  const has = !!m.capabilities[cap];
36879
36957
  return isNo(c.value) ? !has : has;
@@ -0,0 +1,15 @@
1
+ /** Hosted server-side tools each provider's chat models support.
2
+ *
3
+ * This is ADAPTER-SOURCED: it reflects what the provider adapters actually map
4
+ * the unified `{ type: 'web_search' | 'code_interpreter' }` builtins onto (each
5
+ * to the provider's native shape), verified by the live tools-parity test. It is
6
+ * applied PROVIDER-LEVEL to tool-capable (chat-family) models at catalog load —
7
+ * a deliberate first pass; a reliable per-model source can refine it later.
8
+ *
9
+ * Coverage (verified live 2026-07):
10
+ * - web_search → anthropic, openai, google, xai, openrouter
11
+ * - code_interpreter → anthropic, openai, google, xai (NOT openrouter: it
12
+ * proxies function tools + its own plugins, but does not route hosted code
13
+ * execution — confirmed against the API). */
14
+ import type { ProviderName } from '../types/provider';
15
+ export declare const PROVIDER_BUILTIN_TOOLS: Record<ProviderName, readonly string[]>;
@@ -5,7 +5,7 @@
5
5
  import type { SSEEvent } from '../../../network/types';
6
6
  import type { ProviderAdapter, ProviderHttpRequest } from '../../types/provider';
7
7
  import type { NormalizedRequest } from '../../types/request';
8
- import { type CompletionResponse, type Usage } from '../../types/response';
8
+ import { type CompletionResponse, type FileOutput, type Usage } from '../../types/response';
9
9
  import type { StreamEvent } from '../../types/stream';
10
10
  export interface OpenAIResponsesAdapterConfig {
11
11
  apiKey: string;
@@ -28,5 +28,9 @@ export declare class OpenAIResponsesAdapter implements ProviderAdapter {
28
28
  /** Stateless — each output item finalizes with all its file annotations in a
29
29
  * single response.output_item.done event. */
30
30
  createStreamParser(): (event: SSEEvent) => StreamEvent[];
31
+ /** Hosted code-execution output files from one output item. Overridable so
32
+ * Responses-compatible providers with a different file shape (e.g. xAI, which
33
+ * embeds files in the code-interpreter `logs` payload) can extend it. */
34
+ protected filesFromOutputItem(item: Record<string, unknown>): FileOutput[];
31
35
  protected parseUsage(u: Record<string, unknown> | undefined): Usage;
32
36
  }
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import type { ProviderAdapter, ProviderHttpRequest } from '../../types/provider';
9
9
  import type { NormalizedRequest } from '../../types/request';
10
+ import type { FileOutput } from '../../types/response';
10
11
  import { OpenAIResponsesAdapter } from '../openai/responses';
11
12
  export interface XAIResponsesAdapterConfig {
12
13
  apiKey: string;
@@ -17,4 +18,7 @@ export declare class XAIResponsesAdapter extends OpenAIResponsesAdapter {
17
18
  constructor(config: XAIResponsesAdapterConfig);
18
19
  baseURL(): string;
19
20
  buildRequest(req: NormalizedRequest): ProviderHttpRequest;
21
+ /** xAI embeds code-execution files inline in the `logs` payload — extend the base
22
+ * extraction (which handles OpenAI-style annotations / image URLs) with the xAI shape. */
23
+ protected filesFromOutputItem(item: Record<string, unknown>): FileOutput[];
20
24
  }
@@ -140,6 +140,11 @@ export declare class ModelCatalog {
140
140
  getPreferredApi(provider: string, model: string): ApiType | null;
141
141
  supportsApi(provider: string, model: string, api: ApiType): boolean;
142
142
  supportsTools(provider: string, model: string): boolean;
143
+ /** Hosted server-side builtin tools this model supports (e.g. `['web_search',
144
+ * 'code_interpreter']`). Empty when unknown or the model isn't tool-capable. */
145
+ builtinToolsFor(provider: string, model: string): string[];
146
+ /** Whether this model supports a specific hosted builtin tool. */
147
+ supportsBuiltinTool(provider: string, model: string, tool: string): boolean;
143
148
  supportsPreviousResponseId(provider: string, model: string): boolean;
144
149
  /** Server-state retention as a duration string ("30d", "72h"), or null if unsupported. */
145
150
  getStateRetention(provider: string, model: string): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@combycode/llm-sdk",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Unified, pluggable AI SDK for accessing the LLMs of every major provider (Anthropic, OpenAI, Google, xAI, OpenRouter) through one API. Cross-environment: Node, Bun, and the browser.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",