@wice24/tars-sdk 0.2.3 → 0.3.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/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- import type { Agent, AgentVersion, ClientTool, CreateAgentInput, FileMeta, ListMcpToolsInput, McpCredentialInput, McpCredentialStatus, McpServerTools, ReasoningConfig, SessionEvent, ModelInfo, SessionInfo, SessionListItem, UpdateAgentInput, UploadFileInput, UsageSummary, SessionUsageRow, DailyUsageRow, McpServer, Skill, SkillVersion, ApiKeyInfo, ClientIdentity, ClientRole, PlatformStats, Tenant } from './types.js';
1
+ import type { Agent, AgentVersion, ClientTool, CreateAgentInput, FileMeta, ListMcpToolsInput, McpCredentialInput, McpCredentialStatus, McpServerTools, ReasoningConfig, SessionEvent, ModelInfo, ProviderKeys, ProviderStatusReport, SessionInfo, SessionListItem, UpdateAgentInput, UploadFileInput, UsageSummary, SessionUsageRow, DailyUsageRow, McpServer, Skill, SkillVersion, ApiKeyInfo, ClientIdentity, ClientRole, PlatformStats, Tenant } from './types.js';
2
2
  type ToolHandler = (input: any) => unknown | Promise<unknown>;
3
3
  export * from './types.js';
4
+ /** The providers `providerKeys` accepts. */
5
+ export declare const KEYED_PROVIDERS: readonly ["anthropic", "openai", "openrouter", "tensorx"];
4
6
  export declare class TarsApiError extends Error {
5
7
  readonly status: number;
6
8
  readonly body: unknown;
@@ -23,12 +25,27 @@ export interface TarsClientOptions {
23
25
  * — use a client WITHOUT this option to administrate.
24
26
  */
25
27
  actAsTenant?: string;
28
+ /**
29
+ * Your own LLM provider keys, by provider (`openrouter`, `openai`,
30
+ * `anthropic`, `tensorx`). Sent with EVERY request (one
31
+ * `x-tars-key-<provider>` header each) and used for that request's model
32
+ * calls — session turns, completions, the model catalog — so usage bills to
33
+ * your provider accounts. TARS never stores them. A provider you omit
34
+ * routes on the instance's key only while that instance's cutover fallback
35
+ * is on; otherwise its models are refused with 400
36
+ * `provider_not_configured`. Check with `providerStatus()`.
37
+ *
38
+ * SERVER-SIDE clients only: in a browser these keys would ship to every
39
+ * user of your app.
40
+ */
41
+ providerKeys?: ProviderKeys;
26
42
  }
27
43
  export declare class TarsClient {
28
44
  private readonly baseUrl;
29
45
  private readonly apiKey;
30
46
  private readonly fetchImpl;
31
47
  private readonly actAsTenant?;
48
+ private readonly providerKeyHeaders;
32
49
  constructor(options: TarsClientOptions);
33
50
  /** Every request path — JSON, uploads, SSE — sends these, so act-as scoping
34
51
  * can never silently miss a surface. */
@@ -228,20 +245,28 @@ export declare class TarsClient {
228
245
  }>;
229
246
  };
230
247
  /**
231
- * Which model providers this instance is keyed for (`anthropic`, `openai`,
232
- * `openrouter`, `custom`). Ask before OFFERING models: a picker listing GPT
233
- * models on an instance with no OpenAI key produces turns that all fail at
234
- * the provider call.
248
+ * Which model providers YOU can route now (`anthropic`, `openai`,
249
+ * `openrouter`, `tensorx`, `custom`): those you passed in `providerKeys`,
250
+ * plus the instance's while its cutover fallback is on, plus `custom` on an
251
+ * instance pointed at a local runtime. Ask before OFFERING models: a picker
252
+ * listing GPT models without an OpenAI key produces turns that all fail.
235
253
  */
236
254
  providers(): Promise<string[]>;
255
+ /**
256
+ * Every provider with whether you can route it and on whose key
257
+ * (`request` = your `providerKeys`, `instance` = the deployment's), plus
258
+ * whether the instance still falls back to its own keys. What a settings
259
+ * page shows: all providers, the inactive ones disabled.
260
+ */
261
+ providerStatus(): Promise<ProviderStatusReport>;
237
262
  /**
238
263
  * The models this instance can route AND price — ids are full
239
- * `provider/model` specs. `openrouter/*` entries mirror OpenRouter's live
240
- * catalog, so those ids are guaranteed to exist right now; direct entries
264
+ * `provider/model` specs. `openrouter/*` and `tensorx/*` entries mirror
265
+ * those providers' live catalogs, so those ids exist right now; direct entries
241
266
  * come from the deployment's curated pricing table. Offer models from this
242
267
  * list rather than hardcoding ids that can drift.
243
268
  *
244
- * The list is "what is known now", not "everything routable": `openrouter`
269
+ * The list is "what is known now", not "everything routable": catalog
245
270
  * entries are absent before the instance's first successful catalog
246
271
  * refresh, and a deployment routing only `custom/*` has no priced entries
247
272
  * at all. When it comes back empty, fall back to `providers()` plus your
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from './types.js';
2
+ /** The providers `providerKeys` accepts. */
3
+ export const KEYED_PROVIDERS = ['anthropic', 'openai', 'openrouter', 'tensorx'];
2
4
  export class TarsApiError extends Error {
3
5
  status;
4
6
  body;
@@ -14,6 +16,7 @@ export class TarsClient {
14
16
  apiKey;
15
17
  fetchImpl;
16
18
  actAsTenant;
19
+ providerKeyHeaders;
17
20
  constructor(options) {
18
21
  if (!options.apiKey)
19
22
  throw new Error('TarsClient: apiKey is required');
@@ -22,6 +25,13 @@ export class TarsClient {
22
25
  // Bind: an unbound global fetch throws "Illegal invocation" in browsers.
23
26
  this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
24
27
  this.actAsTenant = options.actAsTenant;
28
+ const unknown = Object.keys(options.providerKeys ?? {}).filter((p) => !KEYED_PROVIDERS.includes(p));
29
+ if (unknown.length > 0) {
30
+ throw new Error(`TarsClient: unknown providerKeys ${unknown.join(', ')} (expected ${KEYED_PROVIDERS.join(', ')})`);
31
+ }
32
+ this.providerKeyHeaders = Object.fromEntries(Object.entries(options.providerKeys ?? {})
33
+ .filter(([, key]) => typeof key === 'string' && key.trim() !== '')
34
+ .map(([provider, key]) => [`x-tars-key-${provider}`, key.trim()]));
25
35
  }
26
36
  // ── low-level HTTP ───────────────────────────────────────────
27
37
  /** Every request path — JSON, uploads, SSE — sends these, so act-as scoping
@@ -30,6 +40,7 @@ export class TarsClient {
30
40
  return {
31
41
  authorization: `Bearer ${this.apiKey}`,
32
42
  ...(this.actAsTenant ? { 'x-tars-tenant': this.actAsTenant } : {}),
43
+ ...this.providerKeyHeaders,
33
44
  };
34
45
  }
35
46
  async request(method, path, body) {
@@ -176,6 +187,8 @@ export class TarsClient {
176
187
  params.set('auth', input.auth);
177
188
  if (input.endUserId)
178
189
  params.set('end_user_id', input.endUserId);
190
+ if (input.schemas)
191
+ params.set('schemas', '1');
179
192
  return this.request('GET', `/v1/mcp-servers/tools?${params}`);
180
193
  },
181
194
  };
@@ -254,23 +267,33 @@ export class TarsClient {
254
267
  }),
255
268
  };
256
269
  /**
257
- * Which model providers this instance is keyed for (`anthropic`, `openai`,
258
- * `openrouter`, `custom`). Ask before OFFERING models: a picker listing GPT
259
- * models on an instance with no OpenAI key produces turns that all fail at
260
- * the provider call.
270
+ * Which model providers YOU can route now (`anthropic`, `openai`,
271
+ * `openrouter`, `tensorx`, `custom`): those you passed in `providerKeys`,
272
+ * plus the instance's while its cutover fallback is on, plus `custom` on an
273
+ * instance pointed at a local runtime. Ask before OFFERING models: a picker
274
+ * listing GPT models without an OpenAI key produces turns that all fail.
261
275
  */
262
276
  async providers() {
263
277
  const body = await this.request('GET', '/v1/providers');
264
278
  return body.providers ?? [];
265
279
  }
280
+ /**
281
+ * Every provider with whether you can route it and on whose key
282
+ * (`request` = your `providerKeys`, `instance` = the deployment's), plus
283
+ * whether the instance still falls back to its own keys. What a settings
284
+ * page shows: all providers, the inactive ones disabled.
285
+ */
286
+ async providerStatus() {
287
+ return this.request('GET', '/v1/providers/status');
288
+ }
266
289
  /**
267
290
  * The models this instance can route AND price — ids are full
268
- * `provider/model` specs. `openrouter/*` entries mirror OpenRouter's live
269
- * catalog, so those ids are guaranteed to exist right now; direct entries
291
+ * `provider/model` specs. `openrouter/*` and `tensorx/*` entries mirror
292
+ * those providers' live catalogs, so those ids exist right now; direct entries
270
293
  * come from the deployment's curated pricing table. Offer models from this
271
294
  * list rather than hardcoding ids that can drift.
272
295
  *
273
- * The list is "what is known now", not "everything routable": `openrouter`
296
+ * The list is "what is known now", not "everything routable": catalog
274
297
  * entries are absent before the instance's first successful catalog
275
298
  * refresh, and a deployment routing only `custom/*` has no priced entries
276
299
  * at all. When it comes back empty, fall back to `providers()` plus your
package/dist/types.d.ts CHANGED
@@ -67,6 +67,23 @@ export type SessionEvent = {
67
67
  } & ({
68
68
  status: 'connected';
69
69
  tool_count: number;
70
+ /** Estimated tokens this server's kept tool definitions (names,
71
+ * descriptions, input schemas) add to every request of the turn,
72
+ * at ~4 chars/token — size connectors against each other and
73
+ * against the rest of the prompt. Absent on rows journaled
74
+ * before 0.2.5. */
75
+ schema_tokens?: number;
76
+ /** The tool names the model received from this server (its own
77
+ * unprefixed names, in server order) — what `tool_count` counts.
78
+ * Absent on rows journaled by servers before 0.2.4. */
79
+ tools?: string[];
80
+ /** Offered by the server but removed by the declaration's policy:
81
+ * the `blocked_tools` it offered (deny), or everything not in
82
+ * `tools` (allow). Empty with no policy. Absent before 0.2.4. */
83
+ filtered_out?: string[];
84
+ /** Either list was cut — at 256 names, a 200-character name, or
85
+ * the row's character budget; `tool_count` stays exact. */
86
+ truncated?: true;
70
87
  } | {
71
88
  status: 'failed';
72
89
  code: AgentErrorCode;
@@ -96,8 +113,9 @@ export type SessionEvent = {
96
113
  export type BuiltInTool = 'bash' | 'read_file' | 'write_file' | 'edit_file' | 'glob' | 'grep' | 'web_fetch';
97
114
  /**
98
115
  * Extended-thinking depth. Provider-neutral: TARS maps it to Anthropic's
99
- * `effort` (with adaptive thinking) or OpenRouter's `reasoning.effort`
100
- * (`max` → `xhigh`). Omit for the provider default. The reasoning text itself
116
+ * `effort` (with adaptive thinking), OpenRouter's `reasoning.effort`
117
+ * (`max` → `xhigh`), or TensorX's `reasoning_effort` (`xhigh`/`max` →
118
+ * `high`; models there may ignore it). Omit for the provider default. The reasoning text itself
101
119
  * is never streamed or journaled — only its depth is configurable.
102
120
  */
103
121
  export type ReasoningEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
@@ -174,6 +192,24 @@ export interface SkillVersion {
174
192
  size_bytes: number;
175
193
  created_at: string;
176
194
  }
195
+ /** Providers a client brings its own key for (`TarsClientOptions.providerKeys`). */
196
+ export type KeyedProvider = 'anthropic' | 'openai' | 'openrouter' | 'tensorx';
197
+ /** Every routable provider; `custom` is configured per instance, never per client. */
198
+ export type ProviderName = KeyedProvider | 'custom';
199
+ export type ProviderKeys = Partial<Record<KeyedProvider, string>>;
200
+ export interface ProviderStatus {
201
+ provider: ProviderName;
202
+ /** Can this client route a model on it right now? */
203
+ active: boolean;
204
+ /** Whose key would route it: yours (`request`), the instance's, or none. */
205
+ source: 'request' | 'instance' | null;
206
+ }
207
+ export interface ProviderStatusReport {
208
+ providers: ProviderStatus[];
209
+ /** True while the instance still routes providers you sent no key for on
210
+ * its own keys (the cutover fallback). */
211
+ instance_fallback: boolean;
212
+ }
177
213
  /** One routable-and-priced model from `GET /v1/models`. Prices are USD per
178
214
  * million tokens; `id` is the full `provider/model` spec, usable as-is when
179
215
  * creating agents/sessions. The list is "what is known now" — see
@@ -275,12 +311,34 @@ export interface ListMcpToolsInput {
275
311
  /** Resolve this end user's credential first, then the tenant-level one;
276
312
  * omit for tenant-level only. Only read when `auth` is `'required'`. */
277
313
  endUserId?: string;
314
+ /** Also return each tool's `input_schema` and its FULL description — the
315
+ * exact definition the model is shown, for weighing a connector's prompt
316
+ * cost per tool. Off (default): picker shape, descriptions capped. */
317
+ schemas?: boolean;
278
318
  }
279
319
  /** One tool as the server lists it. `description` is capped at 500
280
- * characters (an ellipsis marks a cut); `null` when the server gave none. */
320
+ * characters (an ellipsis marks a cut) unless `schemas` was requested;
321
+ * `null` when the server gave none. */
322
+ /** The MCP spec's tool annotations, as the server sent them (these five
323
+ * keys only). Hints for grouping and labelling tools in a picker, never
324
+ * guarantees: a policy (`tools` / `blocked_tools`) must not trust them. */
325
+ export interface McpToolAnnotations {
326
+ title?: string;
327
+ readOnlyHint?: boolean;
328
+ destructiveHint?: boolean;
329
+ idempotentHint?: boolean;
330
+ openWorldHint?: boolean;
331
+ }
281
332
  export interface McpToolInfo {
282
333
  name: string;
283
334
  description: string | null;
335
+ /** Human-readable name: the tool's `title`, else `annotations.title`,
336
+ * else null when the server gave neither. */
337
+ title: string | null;
338
+ /** The server's annotations; null when it sent none. */
339
+ annotations: McpToolAnnotations | null;
340
+ /** The input JSON Schema as listed — present only with `schemas: true`. */
341
+ input_schema?: Record<string, unknown>;
284
342
  }
285
343
  export interface McpServerTools {
286
344
  server: string;
@@ -309,6 +367,33 @@ export interface CreateAgentInput {
309
367
  mcp_servers?: McpServer[] | null;
310
368
  }
311
369
  export type UpdateAgentInput = Partial<CreateAgentInput>;
370
+ /** A client-executed tool as a session stores it: the wire declaration,
371
+ * without `execute` (that runs in your app) — the same shape as a
372
+ * `SessionInfo.custom_tools` element. */
373
+ export interface SessionClientTool {
374
+ name: string;
375
+ description: string;
376
+ input_schema: Record<string, unknown>;
377
+ timeout_sec?: number;
378
+ }
379
+ /**
380
+ * The complete tool surface a session's turns are built with, as declared:
381
+ * the enabled built-ins with the descriptions the model is shown, the
382
+ * client-executed tools, and the MCP servers with their `tools` /
383
+ * `blocked_tools` policy. What each MCP server actually contributed on a
384
+ * given turn is in that turn's `mcp_connect` event (`tools` / `filtered_out`).
385
+ */
386
+ export interface SessionToolSurface {
387
+ /** `input_schema`: the JSON Schema (draft-7) the model is shown for the
388
+ * tool — size it to know what the built-ins cost in context. */
389
+ builtin: Array<{
390
+ name: BuiltInTool;
391
+ description: string;
392
+ input_schema: Record<string, unknown>;
393
+ }>;
394
+ client: SessionClientTool[];
395
+ mcp_servers: McpServer[];
396
+ }
312
397
  export interface SessionInfo {
313
398
  id: string;
314
399
  status: SessionStatus;
@@ -328,6 +413,12 @@ export interface SessionInfo {
328
413
  input_schema: Record<string, unknown>;
329
414
  timeout_sec?: number;
330
415
  }> | null;
416
+ /**
417
+ * The declared tool surface (built-ins, client tools, MCP servers with
418
+ * policy) the session's turns run with. Optional: absent from servers
419
+ * predating this field.
420
+ */
421
+ tools?: SessionToolSurface;
331
422
  /**
332
423
  * The EFFECTIVE system prompt the session's turns run with — agent base
333
424
  * prompt + session system_suffix + skills appendix, fused at creation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wice24/tars-sdk",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Client SDK for TARS — the self-hosted managed-agents platform (sessions, streaming turns, client tools, MCP credentials, skills, files, usage, tenant administration)",
5
5
  "type": "module",
6
6
  "exports": {
@@ -12,13 +12,6 @@
12
12
  "files": [
13
13
  "dist"
14
14
  ],
15
- "scripts": {
16
- "build": "tsc -p tsconfig.build.json",
17
- "typecheck": "tsc --noEmit",
18
- "test": "vitest run",
19
- "test:watch": "vitest",
20
- "test:coverage": "vitest run --coverage"
21
- },
22
15
  "devDependencies": {
23
16
  "typescript": "^6.0.3",
24
17
  "@types/node": "^26.1.0",
@@ -40,5 +33,12 @@
40
33
  "mcp",
41
34
  "anthropic",
42
35
  "sdk"
43
- ]
44
- }
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.build.json",
39
+ "typecheck": "tsc --noEmit",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "test:coverage": "vitest run --coverage"
43
+ }
44
+ }