@to11ai/sdk 1.1.0 → 2.0.0-rc.2

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/client.cjs CHANGED
@@ -39,16 +39,19 @@ var __export = (target, all) => {
39
39
  // src/client.ts
40
40
  var exports_client = {};
41
41
  __export(exports_client, {
42
- gatewayPromptHeaders: () => import_gateway.gatewayPromptHeaders,
43
- gatewayAuthHeaders: () => import_gateway.gatewayAuthHeaders,
42
+ gatewayPromptHeaders: () => import_gateway2.gatewayPromptHeaders,
43
+ gatewayAuthHeaders: () => import_gateway2.gatewayAuthHeaders,
44
44
  envVarNameForKey: () => import_store2.envVarNameForKey,
45
45
  createClient: () => createClient,
46
+ UnknownRoleError: () => import_errors.UnknownRoleError,
47
+ Turn: () => import_context2.Turn,
46
48
  To11aiValidationError: () => import_errors.To11aiValidationError,
47
49
  To11aiRateLimitError: () => import_errors.To11aiRateLimitError,
48
50
  To11aiNetworkError: () => import_errors.To11aiNetworkError,
49
51
  To11aiError: () => import_errors.To11aiError,
50
52
  To11aiAuthError: () => import_errors.To11aiAuthError,
51
53
  To11aiApiError: () => import_errors.To11aiApiError,
54
+ Session: () => import_context2.Session,
52
55
  RuleVersionInvalidSnapshotError: () => import_errors.RuleVersionInvalidSnapshotError,
53
56
  RuleNotFoundError: () => import_errors.RuleNotFoundError,
54
57
  RoutingRulesResource: () => import_resource6.RoutingRulesResource,
@@ -64,29 +67,50 @@ __export(exports_client, {
64
67
  EnvNotFoundError: () => import_errors.EnvNotFoundError,
65
68
  EnvNotEnabledForProviderError: () => import_errors.EnvNotEnabledForProviderError,
66
69
  ENV_SLUG_REGEX: () => import_types2.ENV_SLUG_REGEX,
70
+ Conversation: () => import_context2.Conversation,
67
71
  BehaviorStore: () => import_store2.BehaviorStore,
68
72
  AmbiguousEnvLabelsError: () => import_errors.AmbiguousEnvLabelsError
69
73
  });
70
74
  module.exports = __toCommonJS(exports_client);
71
75
  var import_store = require("./behaviors/store");
76
+ var import_config = require("./config");
77
+ var import_context = require("./context");
78
+ var import_gateway = require("./gateway");
72
79
  var import_resource = require("./projectEnvironments/resource");
73
80
  var import_resource2 = require("./prompts/resource");
74
81
  var import_types = require("./prompts/types");
75
82
  var import_resource3 = require("./routingRules/resource");
76
83
  var import_transport = require("./transport");
77
84
  var import_store2 = require("./behaviors/store");
85
+ var import_context2 = require("./context");
78
86
  var import_errors = require("./errors");
79
- var import_gateway = require("./gateway");
87
+ var import_gateway2 = require("./gateway");
80
88
  var import_resource4 = require("./projectEnvironments/resource");
81
89
  var import_resource5 = require("./prompts/resource");
82
90
  var import_types2 = require("./prompts/types");
83
91
  var import_resource6 = require("./routingRules/resource");
84
- function createClient(options) {
92
+ function createClient(options = {}) {
85
93
  const isDomBrowser = typeof globalThis === "object" && typeof globalThis.document !== "undefined";
86
94
  const workerLikeGlobal = globalThis;
87
95
  const isBunMainThread = workerLikeGlobal.Bun?.isMainThread === true;
88
96
  const isWorkerBrowser = typeof globalThis === "object" && (typeof workerLikeGlobal.importScripts === "function" || !isBunMainThread && typeof workerLikeGlobal.postMessage === "function" && typeof workerLikeGlobal.onmessage !== "undefined");
89
- if ((isDomBrowser || isWorkerBrowser) && options.apiKey !== undefined) {
97
+ const isBrowser = isDomBrowser || isWorkerBrowser;
98
+ const processEnv = !isBrowser && typeof process !== "undefined" && typeof process.env === "object" ? process.env : undefined;
99
+ const pick = (opt, envKey) => {
100
+ const fromOpt = opt?.trim();
101
+ if (fromOpt)
102
+ return fromOpt;
103
+ const fromEnv = processEnv?.[envKey]?.trim();
104
+ return fromEnv ? fromEnv : undefined;
105
+ };
106
+ const baseUrl = pick(options.baseUrl, "TO11_API_URL") ?? import_config.DEFAULT_API_URL;
107
+ const gatewayUrl = (pick(options.gatewayUrl, "TO11_GATEWAY_URL") ?? import_config.DEFAULT_GATEWAY_URL).replace(/\/+$/, "").replace(/\/v1$/, "");
108
+ const getAccessToken = options.getAccessToken;
109
+ const apiKey = getAccessToken !== undefined ? options.apiKey : pick(options.apiKey, "TO11_API_KEY");
110
+ const projectId = pick(options.projectId, "TO11_PROJECT_ID");
111
+ const env = pick(options.env, "TO11_ENV");
112
+ const providerApiKey = options.providerApiKey?.trim() || undefined;
113
+ if (isBrowser && options.apiKey !== undefined) {
90
114
  throw new Error("[to11ai/sdk] Do not use `apiKey` in browser contexts — use `getAccessToken` instead.");
91
115
  }
92
116
  if (options.apiKey !== undefined && options.apiKey.trim() === "") {
@@ -95,39 +119,85 @@ function createClient(options) {
95
119
  if (options.projectId !== undefined && options.projectId.trim() === "") {
96
120
  throw new Error("[to11ai/sdk] `projectId` must not be an empty string — omit it or pass a valid project id.");
97
121
  }
98
- if (options.env !== undefined && !import_types.ENV_SLUG_REGEX.test(options.env)) {
99
- throw new Error(`[to11ai/sdk] \`env\` must match ${import_types.ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(options.env)}`);
122
+ if (options.env !== undefined && options.env.trim() === "") {
123
+ throw new Error("[to11ai/sdk] `env` must not be an empty string — omit it or pass a valid env slug.");
100
124
  }
101
- const hasApiKey = options.apiKey !== undefined;
102
- const hasGetAccessToken = options.getAccessToken !== undefined;
103
- const getAccessToken = options.getAccessToken;
125
+ if (env !== undefined && !import_types.ENV_SLUG_REGEX.test(env)) {
126
+ throw new Error(`[to11ai/sdk] \`env\` must match ${import_types.ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(env)}`);
127
+ }
128
+ const hasApiKey = apiKey !== undefined;
129
+ const hasGetAccessToken = getAccessToken !== undefined;
104
130
  if (hasApiKey === hasGetAccessToken) {
105
- throw new Error("[to11ai/sdk] Provide exactly one of `apiKey` (server) or `getAccessToken` (browser).");
131
+ throw new Error("[to11ai/sdk] Provide exactly one of `apiKey` (or `TO11_API_KEY`) or `getAccessToken`.");
106
132
  }
107
- const auth = options.apiKey !== undefined ? { apiKey: options.apiKey } : { getAccessToken };
108
- const isBrowser = isDomBrowser || isWorkerBrowser;
109
- const processEnv = !isBrowser && typeof process !== "undefined" && typeof process.env === "object" ? process.env : undefined;
133
+ const auth = hasApiKey ? { apiKey } : { getAccessToken };
110
134
  const behaviorStore = new import_store.BehaviorStore({
111
135
  initOverrides: options.behaviorOverrides,
112
136
  env: processEnv
113
137
  });
114
138
  const transport = new import_transport.Transport({
115
- baseUrl: options.baseUrl,
139
+ baseUrl,
116
140
  auth,
117
141
  timeoutMs: options.timeoutMs,
118
142
  maxRetries: options.maxRetries,
119
143
  behaviorStore
120
144
  });
121
145
  const prompts = new import_resource2.PromptResource(transport, options.cache, {
122
- projectId: options.projectId,
123
- env: options.env
146
+ projectId,
147
+ env,
148
+ format: options.format,
149
+ onUnknownRole: options.onUnknownRole
124
150
  });
125
151
  const projectEnvironments = new import_resource.ProjectEnvironmentResource(transport);
126
152
  const routingRules = new import_resource3.RoutingRulesResource(transport);
153
+ const gatewayAuthContext = () => {
154
+ if (apiKey === undefined) {
155
+ throw new Error("[to11ai/sdk] session()/turn()/openaiOptions() need an `apiKey` (or `TO11_API_KEY`) — gateway calls require a server-side key. In the browser use `getAccessToken` for control-plane calls only.");
156
+ }
157
+ if (projectId === undefined) {
158
+ throw new Error("[to11ai/sdk] session()/turn() need a `projectId` (or `TO11_PROJECT_ID`).");
159
+ }
160
+ return env !== undefined ? { apiKey, projectId, env } : { apiKey, projectId };
161
+ };
162
+ let defaultSession;
163
+ const clientDefaultSession = () => {
164
+ if (defaultSession === undefined) {
165
+ defaultSession = new import_context.Session(gatewayAuthContext());
166
+ }
167
+ return defaultSession;
168
+ };
169
+ const gatewayClientOptions = (defaultBaseURL, overrides) => {
170
+ if (apiKey === undefined) {
171
+ throw new Error("[to11ai/sdk] openaiOptions()/anthropicOptions() need an `apiKey` (or `TO11_API_KEY`) — gateway calls require a server-side key. In the browser use `getAccessToken` for control-plane calls only.");
172
+ }
173
+ if (projectId === undefined) {
174
+ throw new Error("[to11ai/sdk] openaiOptions()/anthropicOptions() need a `projectId` (or `TO11_PROJECT_ID`) — it authenticates the gateway call via `x-to11-project-id`.");
175
+ }
176
+ const authHeaders = import_gateway.gatewayAuthHeaders(env !== undefined ? { apiKey, projectId, env } : { apiKey, projectId });
177
+ const {
178
+ apiKey: overrideApiKey,
179
+ baseURL: overrideBaseURL,
180
+ defaultHeaders: overrideHeaders,
181
+ ...passthrough
182
+ } = overrides ?? {};
183
+ return {
184
+ ...passthrough,
185
+ baseURL: overrideBaseURL ?? defaultBaseURL,
186
+ apiKey: overrideApiKey ?? providerApiKey ?? apiKey,
187
+ defaultHeaders: { ...authHeaders, ...overrideHeaders }
188
+ };
189
+ };
127
190
  return {
128
191
  prompts,
129
192
  projectEnvironments,
130
193
  routingRules,
131
- resolveBehavior: (key, codedDefault) => behaviorStore.resolve(key, codedDefault)
194
+ resolveBehavior: (key, codedDefault) => behaviorStore.resolve(key, codedDefault),
195
+ apiKey,
196
+ gatewayUrl,
197
+ session: (id) => new import_context.Session(gatewayAuthContext(), id),
198
+ conversation: (id) => clientDefaultSession().conversation(id),
199
+ turn: () => clientDefaultSession().turn(),
200
+ openaiOptions: (overrides) => gatewayClientOptions(`${gatewayUrl}/v1`, overrides),
201
+ anthropicOptions: (overrides) => gatewayClientOptions(gatewayUrl, overrides)
132
202
  };
133
203
  }
package/dist/client.d.ts CHANGED
@@ -1,69 +1,116 @@
1
+ import { type Conversation, Session, type Turn } from "./context";
1
2
  import { ProjectEnvironmentResource } from "./projectEnvironments/resource";
2
3
  import type { CacheConfig } from "./prompts/cache";
3
4
  import { PromptResource } from "./prompts/resource";
5
+ import { type RenderFormat, type UnknownRoleMode } from "./prompts/types";
4
6
  import { RoutingRulesResource } from "./routingRules/resource";
5
7
  export { BehaviorStore, type BehaviorStoreOptions, envVarNameForKey, type SdkBehaviorEnvelope, } from "./behaviors/store";
8
+ export type { TurnAuthContext } from "./context";
9
+ export { Conversation, Session, Turn } from "./context";
6
10
  export type { AmbiguousEnvLabelsReason, EnvNotFoundReason, NoActiveReleaseReason, NoApplicableRouteReason, PromptNotFoundReason, PromptPolicyViolationReason, } from "./errors";
7
- export { AmbiguousEnvLabelsError, EnvNotEnabledForProviderError, EnvNotFoundError, NoActiveReleaseError, NoApplicableRouteError, PromptLabelNotRegisteredError, PromptNotFoundError, PromptPolicyViolationError, RuleNotFoundError, RuleVersionInvalidSnapshotError, To11aiApiError, To11aiAuthError, To11aiError, To11aiNetworkError, To11aiRateLimitError, To11aiValidationError, } from "./errors";
8
- export type { GatewayAuthOptions } from "./gateway";
11
+ export { AmbiguousEnvLabelsError, EnvNotEnabledForProviderError, EnvNotFoundError, NoActiveReleaseError, NoApplicableRouteError, PromptLabelNotRegisteredError, PromptNotFoundError, PromptPolicyViolationError, RuleNotFoundError, RuleVersionInvalidSnapshotError, To11aiApiError, To11aiAuthError, To11aiError, To11aiNetworkError, To11aiRateLimitError, To11aiValidationError, UnknownRoleError, } from "./errors";
12
+ export type { ConvertMessagesOptions, GatewayAuthOptions } from "./gateway";
9
13
  export { gatewayAuthHeaders, gatewayPromptHeaders } from "./gateway";
10
14
  export type { ArchiveProjectEnvironmentInput, ArchiveProjectEnvironmentResult, CreateProjectEnvironmentInput, CreateProjectEnvironmentResult, GetProjectEnvironmentInput, GetProjectEnvironmentResult, ListProjectEnvironmentsInput, ListProjectEnvironmentsResult, LivePointerCounts, ProjectEnvironment, RestoreProjectEnvironmentInput, RestoreProjectEnvironmentResult, UpdateProjectEnvironmentInput, UpdateProjectEnvironmentResult, } from "./projectEnvironments/resource";
11
15
  export { ProjectEnvironmentResource } from "./projectEnvironments/resource";
12
16
  export type { CacheConfig } from "./prompts/cache";
13
17
  export type { PromptResourceConfig } from "./prompts/resource";
14
18
  export { PromptResource } from "./prompts/resource";
15
- export type { BlockRenderRecord, DeveloperRole, FetchedPrompt, FetchPromptOptions, RenderedChatMessage, RenderedPrompt, ResolvedPrompt, ResolvedPromptEnvelope, } from "./prompts/types";
19
+ export type { AnthropicRenderedPrompt, OpenAiRenderedPrompt, PromptConfig, PromptMetadata, RenderResult, } from "./prompts/shape";
20
+ export type { BlockRenderRecord, RenderedChatMessage, RenderedPrompt, RenderFormat, RenderOptions, ResolvedPrompt, ResolvedPromptEnvelope, UnknownRoleMode, } from "./prompts/types";
16
21
  export { ENV_SLUG_REGEX } from "./prompts/types";
17
22
  export type { CreateRuleVersionInput, CreateRuleVersionResult, GetRuleVersionInput, GetRuleVersionResult, ListRuleReleasesInput, ListRuleReleasesResult, ListRuleVersionsInput, ListRuleVersionsResult, RollbackRuleReleaseInput, RollbackRuleReleaseResult, RuleModelBinding, RuleProviderRef, RuleRelease, RuleReleaseActorType, RuleReleaseApprovalStatus, RuleReleaseChangeKind, RuleReleaseStrategy, RuleReleaseWeight, RuleSnapshot, RuleStrategy, RuleVersion, SetRuleReleaseInput, SetRuleReleaseResult, } from "./routingRules/resource";
18
23
  export { RoutingRuleReleaseResource, RoutingRulesResource, RoutingRuleVersionResource, } from "./routingRules/resource";
19
24
  export type { Transport } from "./transport";
20
- interface ServerAuthOptions {
21
- /** API key for server-side use. Must not be used in browser contexts. */
22
- apiKey: string;
23
- getAccessToken?: never;
24
- }
25
- interface BrowserAuthOptions {
25
+ export interface ClientOptions {
26
+ /**
27
+ * Control-plane base URL. Precedence: this option → `TO11_API_URL` env →
28
+ * `https://api.to11.ai`.
29
+ */
30
+ baseUrl?: string;
31
+ /**
32
+ * Data-plane gateway URL (the app's provider-client `baseURL`). Precedence:
33
+ * this option → `TO11_GATEWAY_URL` env → `https://gw.to11.ai`.
34
+ */
35
+ gatewayUrl?: string;
36
+ /**
37
+ * API key for server-side use. Precedence: this option → `TO11_API_KEY` env.
38
+ * Rejected in browser contexts — use `getAccessToken` there.
39
+ */
40
+ apiKey?: string;
26
41
  /** Token callback for browser / edge contexts — avoids static secrets. */
27
- getAccessToken: () => Promise<string>;
28
- apiKey?: never;
29
- }
30
- type AuthOptions = ServerAuthOptions | BrowserAuthOptions;
31
- export type ClientOptions = AuthOptions & {
32
- baseUrl: string;
42
+ getAccessToken?: () => Promise<string>;
43
+ /**
44
+ * Provider API key (BYOK) carried by `openaiOptions()` / `anthropicOptions()`
45
+ * as the provider client's `apiKey`, so a BYOK call is a single
46
+ * `new OpenAI(to11.openaiOptions())`. When omitted, those helpers fall back to
47
+ * the to11 `apiKey` as a placeholder (the managed / server-side-credential
48
+ * path). Option only — no env fallback. Whether the gateway actually uses this
49
+ * key depends on the provider's passthrough mode; otherwise the project's
50
+ * stored credential wins.
51
+ */
52
+ providerApiKey?: string;
33
53
  /**
34
- * Project the client is bound to (TO11-980 / S9). Required for the
35
- * env-aware `prompts.fetch(slug, …)` surface, which builds the
36
- * project-scoped resolve path from it. Validated non-empty at
37
- * construction. The deprecated `prompts.resolve()` keeps its per-call
38
- * `projectId` and ignores this.
54
+ * Project the client is bound to. Precedence: this option → `TO11_PROJECT_ID`
55
+ * env. Required for `prompts.render()` and the gateway context helpers.
39
56
  */
40
57
  projectId?: string;
41
58
  /**
42
- * Default serving environment for `prompts.fetch(slug)` (TO11-980 / S9,
43
- * Style-A). When set, every `fetch()` call inherits it unless overridden
44
- * per call. Validated eagerly against the project-environments registry
45
- * rule (lowercase-kebab, `^[a-z0-9-]{1,63}$`): empty / whitespace-only /
46
- * uppercase / 64+-char values throw at construction with a loud
47
- * `[to11ai/sdk]` message before any request hits the wire.
59
+ * Default serving environment for `prompts.render(slug)`, inherited by every
60
+ * render unless overridden per call. Precedence: this option → `TO11_ENV`
61
+ * env. Validated eagerly against the project-environments slug rule
62
+ * (`^[a-z0-9-]{1,63}$`).
48
63
  */
49
64
  env?: string;
50
- /** LRU cache configuration for resolve(). Defaults: ttlSeconds=300, maxEntries=100 */
65
+ /**
66
+ * Default provider shape for `prompts.render()` output (`"openai"` |
67
+ * `"anthropic"`). Omit for the neutral result. Per-call override allowed.
68
+ */
69
+ format?: RenderFormat;
70
+ /**
71
+ * Default policy for a message with an unrecognized role while shaping
72
+ * `render({ format })` output: `"drop"` (default, silent), `"warn"` (drop +
73
+ * `console.warn`), or `"error"` (throw `UnknownRoleError`). Per-call override
74
+ * via `render(slug, { onUnknownRole })`.
75
+ */
76
+ onUnknownRole?: UnknownRoleMode;
77
+ /** LRU cache configuration for render(). Defaults: ttlSeconds=300, maxEntries=100 */
51
78
  cache?: CacheConfig;
52
79
  /** Request timeout in milliseconds. Default: 30_000 */
53
80
  timeoutMs?: number;
54
81
  /** Max retry attempts for network errors / 5xx. Default: 3 */
55
82
  maxRetries?: number;
56
83
  /**
57
- * Purely local overrides for server-evaluated SDK-behavior flags (TO11-2100),
58
- * keyed by namespaced flag key (e.g. `{ "sdk.retry-v2": true }`). These take
59
- * precedence over server-delivered values and the SDK's coded defaults. For
60
- * the same flag key, an init option here wins over a `TO11_SDK_BEHAVIOR_*`
61
- * environment variable. Published-SDK only — no LaunchDarkly involved.
84
+ * Purely local overrides for server-evaluated SDK-behavior flags (TO11-2100).
85
+ * An init option here wins over a `TO11_SDK_BEHAVIOR_*` env var. No LaunchDarkly.
62
86
  */
63
87
  behaviorOverrides?: Record<string, unknown>;
88
+ }
89
+ /**
90
+ * Overrides accepted by `openaiOptions()` / `anthropicOptions()`. The three
91
+ * known fields are merged (`apiKey` / `baseURL` override; `defaultHeaders`
92
+ * shallow-merges over the tenant-auth headers, caller wins); any other keys —
93
+ * `timeout`, `maxRetries`, `fetch`, … — pass through untouched to the provider
94
+ * client, so `new OpenAI(to11.openaiOptions({ apiKey, timeout }))` needs no
95
+ * outer spread.
96
+ */
97
+ export interface ProviderClientOverrides {
98
+ apiKey?: string;
99
+ baseURL?: string;
100
+ defaultHeaders?: Record<string, string>;
101
+ }
102
+ /**
103
+ * The spread-ready result of `openaiOptions()` / `anthropicOptions()`: the three
104
+ * managed fields plus whatever passthrough keys the caller supplied (their types
105
+ * preserved), so it drops straight into `new OpenAI(...)` / `new Anthropic(...)`.
106
+ */
107
+ export type ProviderClientOptions<T extends ProviderClientOverrides = ProviderClientOverrides> = Omit<T, "apiKey" | "baseURL" | "defaultHeaders"> & {
108
+ baseURL: string;
109
+ apiKey: string;
110
+ defaultHeaders: Record<string, string>;
64
111
  };
65
- export interface To11aiClient {
66
- readonly prompts: PromptResource;
112
+ export interface To11aiClient<F extends RenderFormat | undefined = undefined> {
113
+ readonly prompts: PromptResource<F>;
67
114
  readonly projectEnvironments: ProjectEnvironmentResource;
68
115
  readonly routingRules: RoutingRulesResource;
69
116
  /**
@@ -76,12 +123,67 @@ export interface To11aiClient {
76
123
  * delivered on the envelope.
77
124
  */
78
125
  readonly resolveBehavior: <T>(key: string, codedDefault: T) => T;
126
+ /** Resolved API key (server mode); `undefined` in browser (`getAccessToken`) mode. */
127
+ readonly apiKey: string | undefined;
128
+ /** Resolved data-plane gateway URL (option → `TO11_GATEWAY_URL` → default). */
129
+ readonly gatewayUrl: string;
130
+ /** A new session (unit of work). Pass an id to group telemetry by your own id. */
131
+ session(id?: string): Session;
132
+ /**
133
+ * A conversation in the client's default session (for side / parallel agents).
134
+ *
135
+ * @example Two agents under one session
136
+ * const research = to11.conversation(); // a separate thread from to11.turn()'s default
137
+ * const res = await openai.chat.completions.create(
138
+ * { ...researchPrompt.config, messages },
139
+ * { headers: research.turn().headers(researchPrompt) },
140
+ * );
141
+ */
142
+ conversation(id?: string): Conversation;
143
+ /** A turn in the client's default session + default conversation. */
144
+ turn(): Turn;
145
+ /**
146
+ * Spread-ready options for `new OpenAI(...)` targeting the gateway:
147
+ * `{ baseURL, apiKey, defaultHeaders }` (baseURL carries `/v1`). `defaultHeaders`
148
+ * holds the static tenant auth, so the client authenticates on its own; add
149
+ * `turn().headers(prompt)` per call for trace + prompt provenance. Pass
150
+ * `overrides` to change `apiKey`/`baseURL`, merge extra `defaultHeaders`, or
151
+ * forward any other provider-SDK option (`timeout`, …).
152
+ *
153
+ * @example
154
+ * const openai = new OpenAI(to11.openaiOptions());
155
+ * @example BYOK + a provider-SDK option, no outer spread
156
+ * const openai = new OpenAI(to11.openaiOptions({ apiKey: MY_KEY, timeout: 60_000 }));
157
+ */
158
+ openaiOptions<T extends ProviderClientOverrides = ProviderClientOverrides>(overrides?: T): ProviderClientOptions<T>;
159
+ /**
160
+ * Spread-ready options for `new Anthropic(...)` targeting the gateway root
161
+ * (the Anthropic SDK appends `/v1/messages` itself). Same shape and merge
162
+ * rules as {@link To11aiClient.openaiOptions}.
163
+ */
164
+ anthropicOptions<T extends ProviderClientOverrides = ProviderClientOverrides>(overrides?: T): ProviderClientOptions<T>;
79
165
  }
80
166
  /**
81
- * Create a to11ai SDK client.
167
+ * Create a to11ai SDK client. Resolution precedence differs per option:
168
+ * - URLs (`baseUrl` / `gatewayUrl`) — option → env var → built-in default.
169
+ * - `apiKey` / `projectId` — option → env var (no default).
170
+ * - `env` / `format` — option only.
171
+ * Auth mode:
172
+ * - `apiKey` (or `TO11_API_KEY`) → server-side; rejected in browser contexts.
173
+ * - `getAccessToken` → browser-safe; token fetched per request.
174
+ *
175
+ * @example Minimal setup (single call)
176
+ * import { createClient } from "@to11ai/sdk";
177
+ * import OpenAI from "openai";
82
178
  *
83
- * Auth mode is inferred from options:
84
- * - `apiKey` → server-side; rejected in browser contexts.
85
- * - `getAccessToken` → browser-safe; token fetched per request.
179
+ * const to11 = createClient({ env: "prod", format: "openai" });
180
+ * const openai = new OpenAI(to11.openaiOptions());
181
+ * const prompt = await to11.prompts.render("weather-concierge", { variables: { city: "NYC" } });
182
+ * const res = await openai.chat.completions.create(
183
+ * { ...prompt.config, messages: prompt.messages },
184
+ * { headers: to11.turn().headers(prompt) },
185
+ * );
86
186
  */
87
- export declare function createClient(options: ClientOptions): To11aiClient;
187
+ export declare function createClient<const F extends RenderFormat | undefined = undefined>(options?: Omit<ClientOptions, "format"> & {
188
+ format?: F;
189
+ }): To11aiClient<F>;
package/dist/client.js CHANGED
@@ -1,14 +1,22 @@
1
1
  // src/client.ts
2
2
  import { BehaviorStore } from "./behaviors/store";
3
+ import { DEFAULT_API_URL, DEFAULT_GATEWAY_URL } from "./config";
4
+ import {
5
+ Session
6
+ } from "./context";
7
+ import { gatewayAuthHeaders } from "./gateway";
3
8
  import { ProjectEnvironmentResource } from "./projectEnvironments/resource";
4
9
  import { PromptResource } from "./prompts/resource";
5
- import { ENV_SLUG_REGEX } from "./prompts/types";
10
+ import {
11
+ ENV_SLUG_REGEX
12
+ } from "./prompts/types";
6
13
  import { RoutingRulesResource } from "./routingRules/resource";
7
14
  import { Transport } from "./transport";
8
15
  import {
9
16
  BehaviorStore as BehaviorStore2,
10
17
  envVarNameForKey
11
18
  } from "./behaviors/store";
19
+ import { Conversation, Session as Session2, Turn } from "./context";
12
20
  import {
13
21
  AmbiguousEnvLabelsError,
14
22
  EnvNotEnabledForProviderError,
@@ -25,9 +33,10 @@ import {
25
33
  To11aiError,
26
34
  To11aiNetworkError,
27
35
  To11aiRateLimitError,
28
- To11aiValidationError
36
+ To11aiValidationError,
37
+ UnknownRoleError
29
38
  } from "./errors";
30
- import { gatewayAuthHeaders, gatewayPromptHeaders } from "./gateway";
39
+ import { gatewayAuthHeaders as gatewayAuthHeaders2, gatewayPromptHeaders } from "./gateway";
31
40
  import { ProjectEnvironmentResource as ProjectEnvironmentResource2 } from "./projectEnvironments/resource";
32
41
  import { PromptResource as PromptResource2 } from "./prompts/resource";
33
42
  import { ENV_SLUG_REGEX as ENV_SLUG_REGEX2 } from "./prompts/types";
@@ -36,12 +45,28 @@ import {
36
45
  RoutingRulesResource as RoutingRulesResource2,
37
46
  RoutingRuleVersionResource
38
47
  } from "./routingRules/resource";
39
- function createClient(options) {
48
+ function createClient(options = {}) {
40
49
  const isDomBrowser = typeof globalThis === "object" && typeof globalThis.document !== "undefined";
41
50
  const workerLikeGlobal = globalThis;
42
51
  const isBunMainThread = workerLikeGlobal.Bun?.isMainThread === true;
43
52
  const isWorkerBrowser = typeof globalThis === "object" && (typeof workerLikeGlobal.importScripts === "function" || !isBunMainThread && typeof workerLikeGlobal.postMessage === "function" && typeof workerLikeGlobal.onmessage !== "undefined");
44
- if ((isDomBrowser || isWorkerBrowser) && options.apiKey !== undefined) {
53
+ const isBrowser = isDomBrowser || isWorkerBrowser;
54
+ const processEnv = !isBrowser && typeof process !== "undefined" && typeof process.env === "object" ? process.env : undefined;
55
+ const pick = (opt, envKey) => {
56
+ const fromOpt = opt?.trim();
57
+ if (fromOpt)
58
+ return fromOpt;
59
+ const fromEnv = processEnv?.[envKey]?.trim();
60
+ return fromEnv ? fromEnv : undefined;
61
+ };
62
+ const baseUrl = pick(options.baseUrl, "TO11_API_URL") ?? DEFAULT_API_URL;
63
+ const gatewayUrl = (pick(options.gatewayUrl, "TO11_GATEWAY_URL") ?? DEFAULT_GATEWAY_URL).replace(/\/+$/, "").replace(/\/v1$/, "");
64
+ const getAccessToken = options.getAccessToken;
65
+ const apiKey = getAccessToken !== undefined ? options.apiKey : pick(options.apiKey, "TO11_API_KEY");
66
+ const projectId = pick(options.projectId, "TO11_PROJECT_ID");
67
+ const env = pick(options.env, "TO11_ENV");
68
+ const providerApiKey = options.providerApiKey?.trim() || undefined;
69
+ if (isBrowser && options.apiKey !== undefined) {
45
70
  throw new Error("[to11ai/sdk] Do not use `apiKey` in browser contexts — use `getAccessToken` instead.");
46
71
  }
47
72
  if (options.apiKey !== undefined && options.apiKey.trim() === "") {
@@ -50,53 +75,102 @@ function createClient(options) {
50
75
  if (options.projectId !== undefined && options.projectId.trim() === "") {
51
76
  throw new Error("[to11ai/sdk] `projectId` must not be an empty string — omit it or pass a valid project id.");
52
77
  }
53
- if (options.env !== undefined && !ENV_SLUG_REGEX.test(options.env)) {
54
- throw new Error(`[to11ai/sdk] \`env\` must match ${ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(options.env)}`);
78
+ if (options.env !== undefined && options.env.trim() === "") {
79
+ throw new Error("[to11ai/sdk] `env` must not be an empty string — omit it or pass a valid env slug.");
55
80
  }
56
- const hasApiKey = options.apiKey !== undefined;
57
- const hasGetAccessToken = options.getAccessToken !== undefined;
58
- const getAccessToken = options.getAccessToken;
81
+ if (env !== undefined && !ENV_SLUG_REGEX.test(env)) {
82
+ throw new Error(`[to11ai/sdk] \`env\` must match ${ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(env)}`);
83
+ }
84
+ const hasApiKey = apiKey !== undefined;
85
+ const hasGetAccessToken = getAccessToken !== undefined;
59
86
  if (hasApiKey === hasGetAccessToken) {
60
- throw new Error("[to11ai/sdk] Provide exactly one of `apiKey` (server) or `getAccessToken` (browser).");
87
+ throw new Error("[to11ai/sdk] Provide exactly one of `apiKey` (or `TO11_API_KEY`) or `getAccessToken`.");
61
88
  }
62
- const auth = options.apiKey !== undefined ? { apiKey: options.apiKey } : { getAccessToken };
63
- const isBrowser = isDomBrowser || isWorkerBrowser;
64
- const processEnv = !isBrowser && typeof process !== "undefined" && typeof process.env === "object" ? process.env : undefined;
89
+ const auth = hasApiKey ? { apiKey } : { getAccessToken };
65
90
  const behaviorStore = new BehaviorStore({
66
91
  initOverrides: options.behaviorOverrides,
67
92
  env: processEnv
68
93
  });
69
94
  const transport = new Transport({
70
- baseUrl: options.baseUrl,
95
+ baseUrl,
71
96
  auth,
72
97
  timeoutMs: options.timeoutMs,
73
98
  maxRetries: options.maxRetries,
74
99
  behaviorStore
75
100
  });
76
101
  const prompts = new PromptResource(transport, options.cache, {
77
- projectId: options.projectId,
78
- env: options.env
102
+ projectId,
103
+ env,
104
+ format: options.format,
105
+ onUnknownRole: options.onUnknownRole
79
106
  });
80
107
  const projectEnvironments = new ProjectEnvironmentResource(transport);
81
108
  const routingRules = new RoutingRulesResource(transport);
109
+ const gatewayAuthContext = () => {
110
+ if (apiKey === undefined) {
111
+ throw new Error("[to11ai/sdk] session()/turn()/openaiOptions() need an `apiKey` (or `TO11_API_KEY`) — gateway calls require a server-side key. In the browser use `getAccessToken` for control-plane calls only.");
112
+ }
113
+ if (projectId === undefined) {
114
+ throw new Error("[to11ai/sdk] session()/turn() need a `projectId` (or `TO11_PROJECT_ID`).");
115
+ }
116
+ return env !== undefined ? { apiKey, projectId, env } : { apiKey, projectId };
117
+ };
118
+ let defaultSession;
119
+ const clientDefaultSession = () => {
120
+ if (defaultSession === undefined) {
121
+ defaultSession = new Session(gatewayAuthContext());
122
+ }
123
+ return defaultSession;
124
+ };
125
+ const gatewayClientOptions = (defaultBaseURL, overrides) => {
126
+ if (apiKey === undefined) {
127
+ throw new Error("[to11ai/sdk] openaiOptions()/anthropicOptions() need an `apiKey` (or `TO11_API_KEY`) — gateway calls require a server-side key. In the browser use `getAccessToken` for control-plane calls only.");
128
+ }
129
+ if (projectId === undefined) {
130
+ throw new Error("[to11ai/sdk] openaiOptions()/anthropicOptions() need a `projectId` (or `TO11_PROJECT_ID`) — it authenticates the gateway call via `x-to11-project-id`.");
131
+ }
132
+ const authHeaders = gatewayAuthHeaders(env !== undefined ? { apiKey, projectId, env } : { apiKey, projectId });
133
+ const {
134
+ apiKey: overrideApiKey,
135
+ baseURL: overrideBaseURL,
136
+ defaultHeaders: overrideHeaders,
137
+ ...passthrough
138
+ } = overrides ?? {};
139
+ return {
140
+ ...passthrough,
141
+ baseURL: overrideBaseURL ?? defaultBaseURL,
142
+ apiKey: overrideApiKey ?? providerApiKey ?? apiKey,
143
+ defaultHeaders: { ...authHeaders, ...overrideHeaders }
144
+ };
145
+ };
82
146
  return {
83
147
  prompts,
84
148
  projectEnvironments,
85
149
  routingRules,
86
- resolveBehavior: (key, codedDefault) => behaviorStore.resolve(key, codedDefault)
150
+ resolveBehavior: (key, codedDefault) => behaviorStore.resolve(key, codedDefault),
151
+ apiKey,
152
+ gatewayUrl,
153
+ session: (id) => new Session(gatewayAuthContext(), id),
154
+ conversation: (id) => clientDefaultSession().conversation(id),
155
+ turn: () => clientDefaultSession().turn(),
156
+ openaiOptions: (overrides) => gatewayClientOptions(`${gatewayUrl}/v1`, overrides),
157
+ anthropicOptions: (overrides) => gatewayClientOptions(gatewayUrl, overrides)
87
158
  };
88
159
  }
89
160
  export {
90
161
  gatewayPromptHeaders,
91
- gatewayAuthHeaders,
162
+ gatewayAuthHeaders2 as gatewayAuthHeaders,
92
163
  envVarNameForKey,
93
164
  createClient,
165
+ UnknownRoleError,
166
+ Turn,
94
167
  To11aiValidationError,
95
168
  To11aiRateLimitError,
96
169
  To11aiNetworkError,
97
170
  To11aiError,
98
171
  To11aiAuthError,
99
172
  To11aiApiError,
173
+ Session2 as Session,
100
174
  RuleVersionInvalidSnapshotError,
101
175
  RuleNotFoundError,
102
176
  RoutingRulesResource2 as RoutingRulesResource,
@@ -112,6 +186,7 @@ export {
112
186
  EnvNotFoundError,
113
187
  EnvNotEnabledForProviderError,
114
188
  ENV_SLUG_REGEX2 as ENV_SLUG_REGEX,
189
+ Conversation,
115
190
  BehaviorStore2 as BehaviorStore,
116
191
  AmbiguousEnvLabelsError
117
192
  };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Internal default endpoints. Deliberately NOT re-exported from the package
3
+ * root: `createClient` resolves the URLs (option → env → default) and exposes
4
+ * the resolved data-plane URL as `to11.gatewayUrl`, so app code never imports a
5
+ * bare URL constant. Keeping these out of the public surface avoids a JS-only
6
+ * exported symbol that would need a Python-parity record.
7
+ */
8
+ export declare const DEFAULT_API_URL = "https://api.to11.ai";
9
+ export declare const DEFAULT_GATEWAY_URL = "https://gw.to11.ai";