@to11ai/sdk 1.1.0 → 2.0.0-rc.10

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/README.md CHANGED
@@ -2,325 +2,166 @@
2
2
 
3
3
  JavaScript/TypeScript SDK for [to11ai](https://to11ai.com) — the AI engineering platform for prompt management, tracing, and evals.
4
4
 
5
+ Use it to **render managed prompts** at runtime, **wire your own provider client** (OpenAI, Anthropic, …) to the to11 gateway with the right auth/trace headers, and group model calls into **sessions, conversations, and turns** for tracing.
6
+
5
7
  Ships ESM, CommonJS, and raw TypeScript source in the same package. Runtimes and bundlers pick the right variant automatically.
6
8
 
7
9
  ## Installation
8
10
 
9
11
  ```bash
10
12
  npm install @to11ai/sdk
11
- # or
12
- bun add @to11ai/sdk
13
- # or
14
- pnpm add @to11ai/sdk
15
- # or
16
- yarn add @to11ai/sdk
13
+ # or: bun add @to11ai/sdk · pnpm add @to11ai/sdk · yarn add @to11ai/sdk
17
14
  ```
18
15
 
19
- ## Runtime support
20
-
21
- | Runtime / loader | Resolves to | Notes |
22
- | --------------------------- | -------------------- | --------------------------------------------- |
23
- | Node.js (ESM: `import`) | `dist/*.js` | Bundled ESM, Node-targeted |
24
- | Node.js (CJS: `require`) | `dist/*.cjs` | Bundled CommonJS |
25
- | TypeScript (type resolution)| `dist/*.d.ts` | Declarations for editor + tsc |
26
- | Bun | `src/*.ts` | Raw source — zero build overhead, full maps |
27
- | Deno | `src/*.ts` | Raw source |
28
- | Browsers (via bundler) | `dist/*.js` | Tree-shakeable ESM |
29
- | Workers / edge runtimes | `dist/*.js` | ESM, Node-compatible |
16
+ Runs anywhere Node, Bun, Deno, or edge runs — servers, CLIs, and backend jobs. Not for browsers: a to11 API key carries gateway access and must never ship in client-side code.
30
17
 
31
- Resolution is driven by the package's `exports` conditional map. The tarball ships both `dist/` (compiled) and `src/` (TypeScript) so runtimes that support raw `.ts` can skip the build step.
18
+ ## Quick start
32
19
 
33
- ## Getting started
20
+ Create a client, render a prompt for the current environment, and send it through the gateway with your normal provider client:
34
21
 
35
22
  ```typescript
36
23
  import { createClient } from "@to11ai/sdk";
37
-
38
- const client = createClient({
39
- baseUrl: "https://api.to11ai.com",
40
- apiKey: process.env.TO11AI_API_KEY!,
24
+ import OpenAI from "openai";
25
+
26
+ const to11 = createClient({
27
+ // `process.env` is the Node/Bun form; on Deno/edge use that runtime's env API —
28
+ // or omit these entirely, since the SDK reads the matching `TO11_*` vars itself.
29
+ baseUrl: process.env.TO11_API_URL, // → https://api.to11.ai
30
+ apiKey: process.env.TO11_API_KEY, // → TO11_API_KEY
31
+ projectId: process.env.TO11_PROJECT_ID, // → TO11_PROJECT_ID
32
+ env: process.env.TO11_ENV, // "prod", "staging", … → TO11_ENV
41
33
  });
42
34
 
43
- const result = await client.prompts.resolve({
44
- projectId: "proj_abc123",
45
- slug: "chat-system-prompt",
46
- labels: ["production"],
47
- variables: { userName: "Alice" },
35
+ // Render a released prompt for the client's project + environment.
36
+ const prompt = await to11.prompts.render("welcome-message", {
37
+ variables: { name: "Ada" },
38
+ format: "openai", // omit for the provider-neutral result
48
39
  });
49
40
 
50
- console.log(result.rendered); // { format: "text", text: "Hello, Alice!" }
41
+ // Point your provider client at the gateway and send it.
42
+ const openai = new OpenAI(to11.openaiOptions());
43
+ const completion = await openai.chat.completions.create(
44
+ { ...prompt.config, messages: prompt.messages },
45
+ { headers: to11.turn().headers(prompt) }, // auth + trace id + prompt provenance
46
+ );
51
47
  ```
52
48
 
53
- ## Client construction
49
+ `prompt.config` carries the model parameters, `prompt.messages` the rendered messages, and `turn().headers(prompt)` the headers that authenticate the gateway call and record which prompt produced the trace. You never assemble a `provider::model` string or merge auth headers by hand.
54
50
 
55
- ### Server-side (API key)
51
+ ## Configuring the client
56
52
 
57
- Use an API key for server-side Node.js, Bun, Deno, or edge runtimes where a secret is safe:
53
+ `createClient(options)` is a factory (call it directly, no `new`). Each URL/credential resolves **option → environment variable → built-in default**, so a server app can construct it from the environment alone.
58
54
 
59
- ```typescript
60
- import { createClient } from "@to11ai/sdk";
55
+ | Option | Resolves from | Default |
56
+ | --- | --- | --- |
57
+ | `baseUrl` | option → `TO11_API_URL` | `https://api.to11.ai` |
58
+ | `gatewayUrl` | option → `TO11_GATEWAY_URL` | `https://gw.to11.ai` |
59
+ | `apiKey` | option → `TO11_API_KEY` | — (throws if constructed in a browser) |
60
+ | `projectId` | option → `TO11_PROJECT_ID` | — |
61
+ | `env` | option → `TO11_ENV` | — (must match `^[a-z0-9-]{1,63}$`) |
62
+ | `providerApiKey` | option | — (BYOK; carried by `openaiOptions()`/`anthropicOptions()`) |
63
+ | `format` | option | — (`"openai"` \| `"anthropic"`; omit for the neutral result) |
64
+ | `onUnknownRole` | option | `"drop"` |
65
+ | `timeoutMs` / `maxRetries` | option | `30000` / `3` |
61
66
 
62
- const client = createClient({
63
- baseUrl: "https://api.to11ai.com",
64
- apiKey: process.env.TO11AI_API_KEY!,
65
- });
66
- ```
67
+ > **Keep credentials server-side.** A to11 API key carries gateway access, so it must never ship in browser code — the SDK throws if you construct a client with `apiKey` in a browser context (like OpenAI's `dangerouslyAllowBrowser`). The same goes for a `providerApiKey` (BYOK). Use the SDK from a server, CLI, or backend job.
67
68
 
68
- ### Browser-side (access token callback)
69
+ The client exposes read-only accessors for the resolved values — **`to11.baseUrl`**, **`to11.gatewayUrl`**, **`to11.apiKey`**, **`to11.projectId`**, and **`to11.env`**. Use the non-secret ones (`baseUrl`/`gatewayUrl`/`projectId`/`env`) for diagnostics; `apiKey` is exposed to build manual auth headers — treat it as a secret and never log it. `providerApiKey` (BYOK) is deliberately **not** exposed back off the client.
69
70
 
70
- Use a `getAccessToken` callback to avoid shipping a static secret to the browser:
71
+ ## Rendering prompts
71
72
 
72
- ```typescript
73
- import { createClient } from "@to11ai/sdk";
73
+ `prompts.render(slug, options?)` resolves the released prompt for the client's `projectId` + `env` in one request. Set `format` (on the client or per call) to get a result shaped for that provider; omit it for the provider-neutral result.
74
74
 
75
- const client = createClient({
76
- baseUrl: "https://api.to11ai.com",
77
- getAccessToken: async () => {
78
- const res = await fetch("/api/auth/token");
79
- const { token } = await res.json();
80
- return token;
81
- },
75
+ ```typescript
76
+ const prompt = await to11.prompts.render("weather-concierge", {
77
+ variables: { city: "New York", units: "fahrenheit" },
78
+ // label?: label to resolve for this call (defaults to the client's env)
79
+ // subject?: a stable id so a weighted release pins the same caller to a variant
80
+ // fallback?: a previously-rendered RenderedPrompt served on a network error
81
+ // format?: "openai" | "anthropic"
82
82
  });
83
83
  ```
84
84
 
85
- Passing `apiKey` in a browser context throws immediately to prevent accidental secret exposure.
86
-
87
- ## Prompt resolution
85
+ - **With `format`** the result is spread-ready: `{ messages, config, metadata }` (`config` = the version's model params; `metadata` = prompt/version/release provenance).
86
+ - **Without `format`** you get the neutral **`RenderedPrompt`**: `messages`, `tools`, `toolChoice`, `promptId`, `version`, `versionId`, `releaseId`, `variantName`, `contentHash`, `label`, `slug`, `blockRenderRecord`, and `modelConfig`. This is the path for providers the shaper doesn't cover (Vercel AI, LangChain) or for driving one prompt through more than one provider.
88
87
 
89
- `prompts.resolve()` is the primary use case — it fetches a prompt by slug, applies labels to select a version, renders Liquid template variables, and returns the final text or chat messages.
88
+ Authored `developer` blocks come back as-is; the SDK maps them per provider (OpenAI keeps `developer`; Anthropic folds `system`/`developer` into the top-level `system`). `onUnknownRole` (`"drop"` default, `"warn"`, `"error"`) controls how an unrecognized role is handled during provider conversion.
90
89
 
91
- ```typescript
92
- const result = await client.prompts.resolve({
93
- projectId: "proj_abc123",
94
- slug: "summarise-article",
95
- labels: ["production", "en"],
96
- variables: {
97
- article: "OpenAI releases GPT-5...",
98
- maxLength: 200,
99
- },
100
- });
90
+ > Prompts render as **chat blocks**. The pre-2.0 `resolve()` and the `text` render format have been removed — use `render()` with a `label` (the client's `env` is the default; env is just a label value).
101
91
 
102
- if (result.rendered.format === "text") {
103
- console.log(result.rendered.text);
104
- } else {
105
- // chat format
106
- for (const msg of result.rendered.messages) {
107
- console.log(`[${msg.role}] ${msg.content}`);
108
- }
109
- }
110
- ```
92
+ ## Sending through the gateway
111
93
 
112
- ## All prompt methods
113
-
114
- | Method | Description |
115
- |---|---|
116
- | `prompts.resolve(input)` | Fetch a prompt by slug + labels, render variables, return rendered output. Cached by default. |
117
- | `prompts.create(input)` | Create a new prompt (name, slug, description, tags). |
118
- | `prompts.list(input)` | List prompts in a project with optional pagination. |
119
- | `prompts.get(input)` | Get a single prompt by ID. |
120
- | `prompts.update(input)` | Update prompt metadata (name, description, tags). |
121
- | `prompts.archive(input)` | Archive a prompt (soft delete). |
122
- | `prompts.createVersion(input)` | Create a new version for an existing prompt. |
123
- | `prompts.listVersions(input)` | List all versions of a prompt. |
124
- | `prompts.getVersion(input)` | Get a specific version by version number. |
125
- | `prompts.moveLabel(input)` | Point a label (e.g. `production`) at a specific version. |
126
- | `prompts.listLabels(input)` | List all label pointers for a prompt. |
127
- | `prompts.listProjectLabels(input)` | List the project-level label registry. Pass `includeArchived: true` for soft-deleted rows. |
128
- | `prompts.createProjectLabel(input)` | Create a new project label (requires `project:manage_labels`). |
129
- | `prompts.getProjectLabel(input)` | Get a single project label by name. |
130
- | `prompts.updateProjectLabel(input)` | Update a project label's `isDefault`, `description`, or `displayColor`. |
131
- | `prompts.deleteProjectLabel(input)` | Archive (soft-delete) a project label. Returns the archived row. |
132
- | `prompts.listProjectLabelUsages(input)` | List every prompt currently pointed at by a given project label. |
133
- | `prompts.listLabelEvents(input)` | List immutable label-movement events. Polymorphic by `scope`: `"label"`, `"prompt"`, or `"project"`. |
134
-
135
- All methods require at minimum `projectId` and the relevant resource identifier(s).
136
-
137
- ## Project label registry
138
-
139
- The project label registry stores the canonical list of labels available in a project (e.g. `production`, `staging`, `experiment`). When strict-mode is enabled on a project, `prompts.moveLabel()` only accepts labels that exist in the registry.
94
+ `openaiOptions()` / `anthropicOptions()` return spread-ready options (`{ baseURL, apiKey, defaultHeaders }`) for a provider client pointed at the gateway; `defaultHeaders` carries the static tenant auth. Add `turn().headers(prompt)` per call for the trace id and prompt provenance:
140
95
 
141
96
  ```typescript
142
- // Create a new project label
143
- const label = await client.prompts.createProjectLabel({
144
- projectId: "proj_abc123",
145
- label: "canary",
146
- isDefault: false,
147
- description: "5% traffic shadow",
148
- displayColor: "amber",
149
- });
150
-
151
- // List the project's labels (excludes archived rows by default)
152
- const labels = await client.prompts.listProjectLabels({
153
- projectId: "proj_abc123",
154
- });
155
-
156
- // Reverse-lookup: which prompts currently point at this label?
157
- const usages = await client.prompts.listProjectLabelUsages({
158
- projectId: "proj_abc123",
159
- label: "production",
160
- });
97
+ import Anthropic from "@anthropic-ai/sdk";
98
+ const anthropic = new Anthropic(to11.anthropicOptions());
99
+ ```
161
100
 
162
- // Event history — three scopes via one polymorphic method
163
- const allEvents = await client.prompts.listLabelEvents({
164
- scope: "project",
165
- projectId: "proj_abc123",
166
- page: 1,
167
- limit: 50,
168
- since: "2024-01-01T00:00:00Z",
169
- });
101
+ By default the gateway runs the call on the project's configured provider credential (managed). For **BYOK**, set `providerApiKey` on `createClient` (or pass `apiKey` to the helper per call). For Vercel AI / LangChain, build the base URL from `to11.gatewayUrl` and attach `turn().headers()` yourself.
170
102
 
171
- const promptEvents = await client.prompts.listLabelEvents({
172
- scope: "prompt",
173
- projectId: "proj_abc123",
174
- promptId: "prompt_xyz789",
175
- label: "production", // optional filter
176
- });
103
+ ## Sessions, conversations & turns
177
104
 
178
- const labelEvents = await client.prompts.listLabelEvents({
179
- scope: "label",
180
- projectId: "proj_abc123",
181
- promptId: "prompt_xyz789",
182
- label: "production",
183
- });
184
- ```
105
+ Group model calls for tracing. `to11.turn()` uses the client's default session; create explicit ones with `to11.session(id?)` / `to11.conversation(id?)`. `turn().headers(prompt?)` emits the session/conversation/`traceparent` headers (plus prompt provenance when a rendered prompt is passed).
185
106
 
186
- ## Error handling
107
+ ## Managing prompts, versions & labels
187
108
 
188
- The SDK exports six typed error classes. Catch `To11aiError` as a base or catch specific subclasses:
109
+ Beyond `render()`, the `prompts` namespace covers the control-plane lifecycle. **All of these default `projectId` from the client** — pass it per call only to override:
189
110
 
190
111
  ```typescript
191
- import {
192
- To11aiError,
193
- To11aiApiError,
194
- To11aiAuthError,
195
- To11aiRateLimitError,
196
- To11aiValidationError,
197
- To11aiNetworkError,
198
- PromptLabelNotRegisteredError,
199
- } from "@to11ai/sdk";
112
+ const to11 = createClient({ apiKey, projectId: "proj_abc123" });
200
113
 
201
- try {
202
- const result = await client.prompts.resolve({ ... });
203
- } catch (err) {
204
- if (err instanceof To11aiRateLimitError) {
205
- const retryAfter = err.retryAfterSeconds ?? 60;
206
- console.warn(`Rate limited — retry in ${retryAfter}s`);
207
- } else if (err instanceof To11aiAuthError) {
208
- console.error("Authentication failed:", err.statusCode, err.message);
209
- } else if (err instanceof To11aiValidationError) {
210
- console.error("Bad request:", err.message, err.details);
211
- } else if (err instanceof To11aiNetworkError) {
212
- console.error("Network failure:", err.message, err.cause);
213
- } else if (err instanceof To11aiApiError) {
214
- console.error(`API error ${err.statusCode} [${err.errorCode}]:`, err.message);
215
- } else {
216
- throw err;
217
- }
218
- }
114
+ await to11.prompts.list(); // uses the bound projectId
115
+ await to11.prompts.get({ promptId: "prompt_1" });
116
+ await to11.prompts.getVersion({ promptId: "prompt_1", versionNumber: 3 });
117
+ await to11.prompts.moveLabel({ promptId: "prompt_1", label: "production", versionId: "ver_9", reason: "promote" });
219
118
  ```
220
119
 
221
- ### Strict-mode label rejection
120
+ Full set: `create`, `list`, `get`, `update`, `archive`, `createVersion`, `listVersions`, `getVersion`, `moveLabel`, `listLabels`, and the project-label registry (`listProjectLabels`, `createProjectLabel`, `getProjectLabel`, `updateProjectLabel`, `deleteProjectLabel`, `listProjectLabelUsages`, `listLabelEvents`). `client.projectEnvironments` and `client.routingRules` expose the environment and routing-rule surfaces.
222
121
 
223
- When a project enables `enforce_label_registry`, calling `moveLabel` with a label that's not in the registry fails with a typed `PromptLabelNotRegisteredError`. Catch it explicitly to prompt the user to create the label first:
122
+ ## Error handling
123
+
124
+ `To11aiError` is the base; catch it broadly or match a subclass:
224
125
 
225
126
  ```typescript
226
- import { PromptLabelNotRegisteredError } from "@to11ai/sdk";
127
+ import { To11aiRateLimitError, PromptNotFoundError } from "@to11ai/sdk";
227
128
 
228
129
  try {
229
- await client.prompts.moveLabel({
230
- projectId: "proj_abc123",
231
- promptId: "prompt_xyz789",
232
- label: "canary",
233
- versionId: "ver_42",
234
- reason: "promote to canary",
235
- });
130
+ const prompt = await to11.prompts.render("welcome-message");
236
131
  } catch (err) {
237
- if (PromptLabelNotRegisteredError.is(err)) {
238
- console.error(
239
- `Label "${err.label}" is not registered in project ${err.projectId}.`,
240
- );
241
- // Offer to create it: client.prompts.createProjectLabel({ … })
242
- } else {
243
- throw err;
244
- }
132
+ if (err instanceof To11aiRateLimitError) { /* err.retryAfterSeconds */ }
133
+ else if (err instanceof PromptNotFoundError) { /* … */ }
134
+ else throw err;
245
135
  }
246
136
  ```
247
137
 
248
- | Class | When thrown |
249
- |---|---|
250
- | `To11aiError` | Base class for all SDK errors |
251
- | `To11aiApiError` | Non-2xx API response with structured error envelope |
252
- | `To11aiValidationError` | 400 schema validation failure |
253
- | `To11aiAuthError` | 401 or 403 authentication/authorisation failure |
254
- | `To11aiRateLimitError` | 429 rate limit hit; includes `retryAfterSeconds` |
255
- | `To11aiNetworkError` | Network failure or request timeout |
256
- | `PromptLabelNotRegisteredError` | 422 strict-mode rejection — label not in the project registry |
257
-
258
- ## Caching
259
-
260
- `prompts.resolve()` uses an in-memory LRU cache by default. Configure it at client construction time:
261
-
262
- ```typescript
263
- const client = createClient({
264
- baseUrl: "https://api.to11ai.com",
265
- apiKey: process.env.TO11AI_API_KEY!,
266
- cache: {
267
- ttlSeconds: 600, // default: 300 (5 minutes)
268
- maxEntries: 500, // default: 100
269
- },
270
- });
271
- ```
272
-
273
- **Per-call bypass:** pass `cache: false` to skip the cache for a single call:
138
+ | Class | When |
139
+ | --- | --- |
140
+ | `To11aiApiError` | non-2xx API response (`statusCode`, `errorCode`, `details`) |
141
+ | `To11aiAuthError` | 401/403 |
142
+ | `To11aiRateLimitError` | 429 (`retryAfterSeconds`) |
143
+ | `To11aiValidationError` | 400 |
144
+ | `To11aiNetworkError` | network failure / timeout |
145
+ | `PromptNotFoundError` · `LabelNotFoundError` · `MissingLabelError` · `NoActiveReleaseError` · `PromptPolicyViolationError` · `PromptLabelNotRegisteredError` | typed prompt-resolution errors |
146
+ | `UnknownRoleError` | an unrecognized message role with `onUnknownRole: "error"` |
274
147
 
275
- ```typescript
276
- const fresh = await client.prompts.resolve({
277
- projectId: "proj_abc123",
278
- slug: "chat-system-prompt",
279
- labels: ["production"],
280
- cache: false, // bypass cache, always fetch from API
281
- });
282
- ```
148
+ ## TypeScript types
283
149
 
284
- **Fallback on network error:** pass `fallback` to return a pre-defined value if the network request fails (e.g. during a deployment):
150
+ All inputs/outputs are typed; declarations ship as `dist/*.d.ts` (no separate `@types`). Import types from the entrypoint or the `@to11ai/sdk/prompts` subpath:
285
151
 
286
152
  ```typescript
287
- const result = await client.prompts.resolve({
288
- projectId: "proj_abc123",
289
- slug: "chat-system-prompt",
290
- labels: ["production"],
291
- fallback: {
292
- promptId: "...",
293
- versionId: "...",
294
- version: 1,
295
- rendered: { format: "text", text: "You are a helpful assistant." },
296
- },
297
- });
153
+ import type { ClientOptions, RenderOptions, RenderedPrompt } from "@to11ai/sdk";
298
154
  ```
299
155
 
300
- ## TypeScript types
156
+ ## Runtime support
301
157
 
302
- All inputs and outputs are fully typed. Import types directly if needed:
158
+ | Runtime / loader | Resolves to | Notes |
159
+ | --- | --- | --- |
160
+ | Node.js (ESM `import`) | `dist/*.js` | Bundled ESM |
161
+ | Node.js (CJS `require`) | `dist/*.cjs` | Bundled CommonJS |
162
+ | TypeScript | `dist/*.d.ts` | Declarations |
163
+ | Bun / Deno | `src/*.ts` | Raw source — zero build overhead |
303
164
 
304
- ```typescript
305
- import type {
306
- ClientOptions,
307
- CacheConfig,
308
- To11aiClient,
309
- } from "@to11ai/sdk";
310
-
311
- import type {
312
- ResolvePromptInput,
313
- ResolvePromptResult,
314
- ResolvedPrompt,
315
- Prompt,
316
- PromptVersion,
317
- // Project label registry
318
- ProjectPromptLabel,
319
- ProjectPromptLabelUsage,
320
- PromptLabelEvent,
321
- PaletteToken,
322
- ListLabelEventsInput,
323
- } from "@to11ai/sdk/prompts";
324
- ```
165
+ ## Documentation
325
166
 
326
- Type declarations ship in the package as `dist/*.d.ts`. No separate `@types` package is required. Bun and Deno consumers additionally get the raw TypeScript source.
167
+ Full reference: [TypeScript SDK docs](https://docs.to11.ai/reference/typescript-sdk) · [Rendering prompts](https://docs.to11.ai/platform/prompts/rendering). Licensed under MPL-2.0.
package/dist/client.cjs CHANGED
@@ -39,55 +39,66 @@ 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,
44
- envVarNameForKey: () => import_store2.envVarNameForKey,
42
+ gatewayPromptHeaders: () => import_gateway2.gatewayPromptHeaders,
43
+ gatewayAuthHeaders: () => import_gateway2.gatewayAuthHeaders,
45
44
  createClient: () => createClient,
45
+ UnknownRoleError: () => import_errors.UnknownRoleError,
46
+ Turn: () => import_context2.Turn,
46
47
  To11aiValidationError: () => import_errors.To11aiValidationError,
47
48
  To11aiRateLimitError: () => import_errors.To11aiRateLimitError,
48
49
  To11aiNetworkError: () => import_errors.To11aiNetworkError,
49
50
  To11aiError: () => import_errors.To11aiError,
50
51
  To11aiAuthError: () => import_errors.To11aiAuthError,
51
52
  To11aiApiError: () => import_errors.To11aiApiError,
52
- RuleVersionInvalidSnapshotError: () => import_errors.RuleVersionInvalidSnapshotError,
53
- RuleNotFoundError: () => import_errors.RuleNotFoundError,
54
- RoutingRulesResource: () => import_resource6.RoutingRulesResource,
55
- RoutingRuleVersionResource: () => import_resource6.RoutingRuleVersionResource,
56
- RoutingRuleReleaseResource: () => import_resource6.RoutingRuleReleaseResource,
57
- PromptResource: () => import_resource5.PromptResource,
53
+ Session: () => import_context2.Session,
54
+ PromptResource: () => import_resource4.PromptResource,
58
55
  PromptPolicyViolationError: () => import_errors.PromptPolicyViolationError,
59
56
  PromptNotFoundError: () => import_errors.PromptNotFoundError,
60
57
  PromptLabelNotRegisteredError: () => import_errors.PromptLabelNotRegisteredError,
61
- ProjectEnvironmentResource: () => import_resource4.ProjectEnvironmentResource,
62
- NoApplicableRouteError: () => import_errors.NoApplicableRouteError,
58
+ ProjectEnvironmentResource: () => import_resource3.ProjectEnvironmentResource,
63
59
  NoActiveReleaseError: () => import_errors.NoActiveReleaseError,
64
- EnvNotFoundError: () => import_errors.EnvNotFoundError,
65
- EnvNotEnabledForProviderError: () => import_errors.EnvNotEnabledForProviderError,
60
+ MissingLabelError: () => import_errors.MissingLabelError,
61
+ LabelNotFoundError: () => import_errors.LabelNotFoundError,
66
62
  ENV_SLUG_REGEX: () => import_types2.ENV_SLUG_REGEX,
67
- BehaviorStore: () => import_store2.BehaviorStore,
68
- AmbiguousEnvLabelsError: () => import_errors.AmbiguousEnvLabelsError
63
+ Conversation: () => import_context2.Conversation
69
64
  });
70
65
  module.exports = __toCommonJS(exports_client);
71
66
  var import_store = require("./behaviors/store");
67
+ var import_config = require("./config");
68
+ var import_context = require("./context");
69
+ var import_gateway = require("./gateway");
72
70
  var import_resource = require("./projectEnvironments/resource");
73
71
  var import_resource2 = require("./prompts/resource");
74
72
  var import_types = require("./prompts/types");
75
- var import_resource3 = require("./routingRules/resource");
76
73
  var import_transport = require("./transport");
77
- var import_store2 = require("./behaviors/store");
74
+ var import_context2 = require("./context");
78
75
  var import_errors = require("./errors");
79
- var import_gateway = require("./gateway");
80
- var import_resource4 = require("./projectEnvironments/resource");
81
- var import_resource5 = require("./prompts/resource");
76
+ var import_gateway2 = require("./gateway");
77
+ var import_resource3 = require("./projectEnvironments/resource");
78
+ var import_resource4 = require("./prompts/resource");
82
79
  var import_types2 = require("./prompts/types");
83
- var import_resource6 = require("./routingRules/resource");
84
- function createClient(options) {
80
+ function createClient(options = {}) {
85
81
  const isDomBrowser = typeof globalThis === "object" && typeof globalThis.document !== "undefined";
86
82
  const workerLikeGlobal = globalThis;
87
83
  const isBunMainThread = workerLikeGlobal.Bun?.isMainThread === true;
88
84
  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) {
90
- throw new Error("[to11ai/sdk] Do not use `apiKey` in browser contexts — use `getAccessToken` instead.");
85
+ const isBrowser = isDomBrowser || isWorkerBrowser;
86
+ const processEnv = !isBrowser && typeof process !== "undefined" && typeof process.env === "object" ? process.env : undefined;
87
+ const pick = (opt, envKey) => {
88
+ const fromOpt = opt?.trim();
89
+ if (fromOpt)
90
+ return fromOpt;
91
+ const fromEnv = processEnv?.[envKey]?.trim();
92
+ return fromEnv ? fromEnv : undefined;
93
+ };
94
+ const baseUrl = pick(options.baseUrl, "TO11_API_URL") ?? import_config.DEFAULT_API_URL;
95
+ const gatewayUrl = (pick(options.gatewayUrl, "TO11_GATEWAY_URL") ?? import_config.DEFAULT_GATEWAY_URL).replace(/\/+$/, "").replace(/\/v1$/, "");
96
+ const apiKey = pick(options.apiKey, "TO11_API_KEY");
97
+ const projectId = pick(options.projectId, "TO11_PROJECT_ID");
98
+ const env = pick(options.env, "TO11_ENV");
99
+ const providerApiKey = options.providerApiKey?.trim() || undefined;
100
+ if (isBrowser && options.apiKey !== undefined) {
101
+ throw new Error("[to11ai/sdk] Do not use `apiKey` in a browser — a to11 API key carries gateway access and must stay server-side (server, CLI, or backend job).");
91
102
  }
92
103
  if (options.apiKey !== undefined && options.apiKey.trim() === "") {
93
104
  throw new Error("[to11ai/sdk] `apiKey` must not be an empty string.");
@@ -95,39 +106,74 @@ function createClient(options) {
95
106
  if (options.projectId !== undefined && options.projectId.trim() === "") {
96
107
  throw new Error("[to11ai/sdk] `projectId` must not be an empty string — omit it or pass a valid project id.");
97
108
  }
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)}`);
109
+ if (options.env !== undefined && options.env.trim() === "") {
110
+ throw new Error("[to11ai/sdk] `env` must not be an empty string — omit it or pass a valid env slug.");
100
111
  }
101
- const hasApiKey = options.apiKey !== undefined;
102
- const hasGetAccessToken = options.getAccessToken !== undefined;
103
- const getAccessToken = options.getAccessToken;
104
- if (hasApiKey === hasGetAccessToken) {
105
- throw new Error("[to11ai/sdk] Provide exactly one of `apiKey` (server) or `getAccessToken` (browser).");
112
+ if (env !== undefined && !import_types.ENV_SLUG_REGEX.test(env)) {
113
+ throw new Error(`[to11ai/sdk] \`env\` must match ${import_types.ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(env)}`);
106
114
  }
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;
110
- const behaviorStore = new import_store.BehaviorStore({
111
- initOverrides: options.behaviorOverrides,
112
- env: processEnv
113
- });
115
+ if (apiKey === undefined) {
116
+ throw new Error("[to11ai/sdk] An `apiKey` (or `TO11_API_KEY`) is required.");
117
+ }
118
+ const auth = { apiKey };
119
+ const behaviorStore = new import_store.BehaviorStore({ env: processEnv });
114
120
  const transport = new import_transport.Transport({
115
- baseUrl: options.baseUrl,
121
+ baseUrl,
116
122
  auth,
117
123
  timeoutMs: options.timeoutMs,
118
124
  maxRetries: options.maxRetries,
119
125
  behaviorStore
120
126
  });
121
- const prompts = new import_resource2.PromptResource(transport, options.cache, {
122
- projectId: options.projectId,
123
- env: options.env
127
+ const prompts = new import_resource2.PromptResource(transport, {
128
+ projectId,
129
+ env,
130
+ format: options.format,
131
+ onUnknownRole: options.onUnknownRole
124
132
  });
125
133
  const projectEnvironments = new import_resource.ProjectEnvironmentResource(transport);
126
- const routingRules = new import_resource3.RoutingRulesResource(transport);
134
+ const gatewayAuthContext = () => {
135
+ if (projectId === undefined) {
136
+ throw new Error("[to11ai/sdk] session()/turn() need a `projectId` (or `TO11_PROJECT_ID`).");
137
+ }
138
+ return env !== undefined ? { apiKey, projectId, env } : { apiKey, projectId };
139
+ };
140
+ let defaultSession;
141
+ const clientDefaultSession = () => {
142
+ if (defaultSession === undefined) {
143
+ defaultSession = new import_context.Session(gatewayAuthContext());
144
+ }
145
+ return defaultSession;
146
+ };
147
+ const gatewayClientOptions = (defaultBaseURL, overrides) => {
148
+ if (projectId === undefined) {
149
+ throw new Error("[to11ai/sdk] openaiOptions()/anthropicOptions() need a `projectId` (or `TO11_PROJECT_ID`) — it authenticates the gateway call via `x-to11-project-id`.");
150
+ }
151
+ const authHeaders = import_gateway.gatewayAuthHeaders(env !== undefined ? { apiKey, projectId, env } : { apiKey, projectId });
152
+ const {
153
+ apiKey: overrideApiKey,
154
+ baseURL: overrideBaseURL,
155
+ defaultHeaders: overrideHeaders,
156
+ ...passthrough
157
+ } = overrides ?? {};
158
+ return {
159
+ ...passthrough,
160
+ baseURL: overrideBaseURL ?? defaultBaseURL,
161
+ apiKey: overrideApiKey ?? providerApiKey ?? apiKey,
162
+ defaultHeaders: { ...authHeaders, ...overrideHeaders }
163
+ };
164
+ };
127
165
  return {
128
166
  prompts,
129
167
  projectEnvironments,
130
- routingRules,
131
- resolveBehavior: (key, codedDefault) => behaviorStore.resolve(key, codedDefault)
168
+ apiKey,
169
+ baseUrl,
170
+ gatewayUrl,
171
+ projectId,
172
+ env,
173
+ session: (id) => new import_context.Session(gatewayAuthContext(), id),
174
+ conversation: (id) => clientDefaultSession().conversation(id),
175
+ turn: () => clientDefaultSession().turn(),
176
+ openaiOptions: (overrides) => gatewayClientOptions(`${gatewayUrl}/v1`, overrides),
177
+ anthropicOptions: (overrides) => gatewayClientOptions(gatewayUrl, overrides)
132
178
  };
133
179
  }