@to11ai/sdk 0.1.1 → 0.2.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/behaviors/store.d.ts +62 -0
- package/dist/client.cjs +37 -4
- package/dist/client.d.ts +41 -2
- package/dist/client.js +45 -4
- package/dist/errors.cjs +90 -1
- package/dist/errors.d.ts +142 -0
- package/dist/errors.js +90 -1
- package/dist/gateway.cjs +12 -0
- package/dist/gateway.d.ts +26 -12
- package/dist/gateway.js +14 -0
- package/dist/generated/index.d.ts +1 -0
- package/dist/generated/types.gen.d.ts +22974 -0
- package/dist/generated/zod.gen.d.ts +13531 -0
- package/dist/projectEnvironments/resource.cjs +7 -2
- package/dist/projectEnvironments/resource.js +7 -2
- package/dist/projectEnvironments/types.d.ts +4 -75
- package/dist/prompts/cache.d.ts +24 -3
- package/dist/prompts/resource.cjs +101 -3
- package/dist/prompts/resource.d.ts +47 -3
- package/dist/prompts/resource.js +103 -5
- package/dist/prompts/types.d.ts +36 -177
- package/dist/routingRules/resource.cjs +9 -2
- package/dist/routingRules/resource.js +9 -2
- package/dist/routingRules/types.d.ts +12 -123
- package/dist/transport.d.ts +23 -0
- package/package.json +23 -12
- package/src/behaviors/store.ts +175 -0
- package/src/client.ts +115 -3
- package/src/errors.ts +261 -0
- package/src/gateway.ts +54 -12
- package/src/generated/index.ts +3 -0
- package/src/generated/types.gen.ts +24083 -0
- package/src/generated/zod.gen.ts +12648 -0
- package/src/projectEnvironments/resource.ts +10 -0
- package/src/projectEnvironments/types.ts +9 -91
- package/src/prompts/cache.ts +39 -4
- package/src/prompts/resource.ts +258 -5
- package/src/prompts/types.ts +56 -237
- package/src/routingRules/resource.ts +10 -1
- package/src/routingRules/types.ts +21 -155
- package/src/traceparent.ts +69 -4
- package/src/transport.ts +188 -3
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Published-SDK behavior resolution (TO11-2100 / feature-flagging Slice S7).
|
|
3
|
+
*
|
|
4
|
+
* The to11 platform evaluates flags that influence *shipped-SDK behavior*
|
|
5
|
+
* server-side and delivers their values on the optional `sdkBehavior` field of
|
|
6
|
+
* the success envelope (PRD Decision 7). This SDK carries **no** LaunchDarkly
|
|
7
|
+
* dependency and never connects to to11's control plane — it only reads the
|
|
8
|
+
* value the server already evaluated.
|
|
9
|
+
*
|
|
10
|
+
* Resolution precedence, applied by {@link BehaviorStore.resolve}:
|
|
11
|
+
* 1. local override — init option, then env var (init option wins ties)
|
|
12
|
+
* 2. server-delivered value — the last value stored for that key
|
|
13
|
+
* 3. coded default — the value the caller passes to `resolve`
|
|
14
|
+
*
|
|
15
|
+
* A value whose runtime type disagrees with the coded default's type falls
|
|
16
|
+
* through to the next tier (ultimately the coded default), mirroring the
|
|
17
|
+
* server wrapper's kind-guard. An absent envelope field never clears
|
|
18
|
+
* previously-stored values.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** The wire shape of the optional `sdkBehavior` envelope field. */
|
|
22
|
+
export interface SdkBehaviorEnvelope {
|
|
23
|
+
readonly v: number;
|
|
24
|
+
readonly flags: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The only `sdkBehavior.v` this SDK understands. A response carrying any other
|
|
29
|
+
* version is ignored wholesale (the store is left untouched), so a future
|
|
30
|
+
* envelope shape can never break this SDK — it degrades to coded defaults, the
|
|
31
|
+
* safe state.
|
|
32
|
+
*/
|
|
33
|
+
const SUPPORTED_SDK_BEHAVIOR_VERSION = 1;
|
|
34
|
+
|
|
35
|
+
/** Env-var prefix for local SDK-behavior overrides: `TO11_SDK_BEHAVIOR_<KEY>`. */
|
|
36
|
+
const ENV_PREFIX = "TO11_SDK_BEHAVIOR_";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convert a namespaced flag key to its env-var name: prefix + uppercased key
|
|
40
|
+
* with `.` and `-` replaced by `_`. e.g. `sdk.retry-v2` →
|
|
41
|
+
* `TO11_SDK_BEHAVIOR_SDK_RETRY_V2`.
|
|
42
|
+
*/
|
|
43
|
+
export const envVarNameForKey = (key: string): string =>
|
|
44
|
+
`${ENV_PREFIX}${key.toUpperCase().replace(/[.-]/g, "_")}`;
|
|
45
|
+
|
|
46
|
+
/** Parse a JSON value out of an env-var string, falling back to the raw string. */
|
|
47
|
+
const parseEnvValue = (raw: string): unknown => {
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(raw);
|
|
50
|
+
} catch {
|
|
51
|
+
return raw;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reject a candidate value whose runtime type disagrees with the coded
|
|
57
|
+
* default's type, for primitive defaults. Objects/arrays (JSON defaults) accept
|
|
58
|
+
* any non-null object candidate. `null`/`undefined` candidates are always
|
|
59
|
+
* rejected (treated as absent).
|
|
60
|
+
*/
|
|
61
|
+
const typeMatchesDefault = <T>(
|
|
62
|
+
candidate: unknown,
|
|
63
|
+
codedDefault: T,
|
|
64
|
+
): boolean => {
|
|
65
|
+
if (candidate === null || candidate === undefined) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
const defaultType = typeof codedDefault;
|
|
69
|
+
if (
|
|
70
|
+
defaultType === "boolean" ||
|
|
71
|
+
defaultType === "string" ||
|
|
72
|
+
defaultType === "number"
|
|
73
|
+
) {
|
|
74
|
+
return typeof candidate === defaultType;
|
|
75
|
+
}
|
|
76
|
+
// JSON default: preserve container kind (array vs object) so an array
|
|
77
|
+
// default does not accept a plain object and vice versa. This matches the
|
|
78
|
+
// python-sdk store (which distinguishes mapping vs list/tuple) so identical
|
|
79
|
+
// payloads resolve identically across SDKs.
|
|
80
|
+
if (defaultType === "object" && codedDefault !== null) {
|
|
81
|
+
if (Array.isArray(codedDefault)) {
|
|
82
|
+
return Array.isArray(candidate);
|
|
83
|
+
}
|
|
84
|
+
return typeof candidate === "object" && !Array.isArray(candidate);
|
|
85
|
+
}
|
|
86
|
+
// Unusual default types — accept on exact typeof match.
|
|
87
|
+
return typeof candidate === defaultType;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export interface BehaviorStoreOptions {
|
|
91
|
+
/** Init-time local overrides, keyed by namespaced flag key. Highest precedence. */
|
|
92
|
+
readonly initOverrides?: Record<string, unknown>;
|
|
93
|
+
/**
|
|
94
|
+
* The process environment to read `TO11_SDK_BEHAVIOR_*` overrides from.
|
|
95
|
+
* Pass `undefined` (the default) in browser contexts so the SDK never
|
|
96
|
+
* touches `process.env`.
|
|
97
|
+
*/
|
|
98
|
+
readonly env?: Record<string, string | undefined>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export class BehaviorStore {
|
|
102
|
+
/** Last-write-wins server-delivered values, keyed by namespaced flag key. */
|
|
103
|
+
private readonly serverValues = new Map<string, unknown>();
|
|
104
|
+
/** Init-option overrides, keyed by flag key. */
|
|
105
|
+
private readonly initOverrides: ReadonlyMap<string, unknown>;
|
|
106
|
+
/** Env-var overrides, keyed by full env-var name (e.g. `TO11_SDK_BEHAVIOR_SDK_RETRY_V2`). */
|
|
107
|
+
private readonly envOverrides: ReadonlyMap<string, unknown>;
|
|
108
|
+
|
|
109
|
+
constructor(options?: BehaviorStoreOptions) {
|
|
110
|
+
this.initOverrides = new Map(Object.entries(options?.initOverrides ?? {}));
|
|
111
|
+
|
|
112
|
+
const env = options?.env;
|
|
113
|
+
const envMap = new Map<string, unknown>();
|
|
114
|
+
if (env) {
|
|
115
|
+
for (const [name, raw] of Object.entries(env)) {
|
|
116
|
+
if (raw !== undefined && name.startsWith(ENV_PREFIX)) {
|
|
117
|
+
envMap.set(name, parseEnvValue(raw));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.envOverrides = envMap;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Update the server-delivered values from a response's `sdkBehavior` field.
|
|
126
|
+
* No-op when the field is absent or carries an unsupported version. Never
|
|
127
|
+
* clears previously-stored values.
|
|
128
|
+
*/
|
|
129
|
+
updateFromEnvelope(sdkBehavior: SdkBehaviorEnvelope | undefined): void {
|
|
130
|
+
if (
|
|
131
|
+
sdkBehavior === undefined ||
|
|
132
|
+
sdkBehavior.v !== SUPPORTED_SDK_BEHAVIOR_VERSION ||
|
|
133
|
+
typeof sdkBehavior.flags !== "object" ||
|
|
134
|
+
sdkBehavior.flags === null
|
|
135
|
+
) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
for (const [key, value] of Object.entries(sdkBehavior.flags)) {
|
|
139
|
+
this.serverValues.set(key, value);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Resolve a behavior value under the fixed precedence: local override (init
|
|
145
|
+
* option, then env var) → server-delivered value → coded default. A tier
|
|
146
|
+
* whose value is absent or type-mismatched against `codedDefault` falls
|
|
147
|
+
* through to the next tier.
|
|
148
|
+
*/
|
|
149
|
+
resolve<T>(key: string, codedDefault: T): T {
|
|
150
|
+
// 1a. Init-option override (highest precedence).
|
|
151
|
+
if (this.initOverrides.has(key)) {
|
|
152
|
+
const value = this.initOverrides.get(key);
|
|
153
|
+
if (typeMatchesDefault(value, codedDefault)) {
|
|
154
|
+
return value as T;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// 1b. Env-var override.
|
|
158
|
+
const envName = envVarNameForKey(key);
|
|
159
|
+
if (this.envOverrides.has(envName)) {
|
|
160
|
+
const value = this.envOverrides.get(envName);
|
|
161
|
+
if (typeMatchesDefault(value, codedDefault)) {
|
|
162
|
+
return value as T;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// 2. Server-delivered value.
|
|
166
|
+
if (this.serverValues.has(key)) {
|
|
167
|
+
const value = this.serverValues.get(key);
|
|
168
|
+
if (typeMatchesDefault(value, codedDefault)) {
|
|
169
|
+
return value as T;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// 3. Coded default.
|
|
173
|
+
return codedDefault;
|
|
174
|
+
}
|
|
175
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
+
import { BehaviorStore } from "./behaviors/store";
|
|
1
2
|
import { ProjectEnvironmentResource } from "./projectEnvironments/resource";
|
|
2
3
|
import type { CacheConfig } from "./prompts/cache";
|
|
3
4
|
import { PromptResource } from "./prompts/resource";
|
|
5
|
+
import { ENV_SLUG_REGEX } from "./prompts/types";
|
|
4
6
|
import { RoutingRulesResource } from "./routingRules/resource";
|
|
5
7
|
import { Transport } from "./transport";
|
|
6
8
|
|
|
9
|
+
// SDK-behavior delivery (TO11-2100): the resolution store + helpers.
|
|
10
|
+
export {
|
|
11
|
+
BehaviorStore,
|
|
12
|
+
type BehaviorStoreOptions,
|
|
13
|
+
envVarNameForKey,
|
|
14
|
+
type SdkBehaviorEnvelope,
|
|
15
|
+
} from "./behaviors/store";
|
|
7
16
|
// Re-export all error types for convenience
|
|
8
|
-
export type {
|
|
17
|
+
export type {
|
|
18
|
+
AmbiguousEnvLabelsReason,
|
|
19
|
+
EnvNotFoundReason,
|
|
20
|
+
NoActiveReleaseReason,
|
|
21
|
+
NoApplicableRouteReason,
|
|
22
|
+
PromptNotFoundReason,
|
|
23
|
+
PromptPolicyViolationReason,
|
|
24
|
+
} from "./errors";
|
|
9
25
|
export {
|
|
26
|
+
// TO11-980 (S9) prompt-resolution typed errors.
|
|
27
|
+
AmbiguousEnvLabelsError,
|
|
10
28
|
EnvNotEnabledForProviderError,
|
|
29
|
+
EnvNotFoundError,
|
|
30
|
+
NoActiveReleaseError,
|
|
11
31
|
NoApplicableRouteError,
|
|
12
32
|
PromptLabelNotRegisteredError,
|
|
33
|
+
PromptNotFoundError,
|
|
34
|
+
PromptPolicyViolationError,
|
|
13
35
|
RuleNotFoundError,
|
|
14
36
|
RuleVersionInvalidSnapshotError,
|
|
15
37
|
To11aiApiError,
|
|
@@ -39,7 +61,20 @@ export type {
|
|
|
39
61
|
} from "./projectEnvironments/resource";
|
|
40
62
|
export { ProjectEnvironmentResource } from "./projectEnvironments/resource";
|
|
41
63
|
export type { CacheConfig } from "./prompts/cache";
|
|
64
|
+
export type { PromptResourceConfig } from "./prompts/resource";
|
|
42
65
|
export { PromptResource } from "./prompts/resource";
|
|
66
|
+
// TO11-980 (S9): env-aware fetch() surface types at the package entrypoint (AC9).
|
|
67
|
+
export type {
|
|
68
|
+
BlockRenderRecord,
|
|
69
|
+
FetchedPrompt,
|
|
70
|
+
FetchPromptOptions,
|
|
71
|
+
PromptContext,
|
|
72
|
+
RenderedChatMessage,
|
|
73
|
+
RenderedPrompt,
|
|
74
|
+
ResolvedPrompt,
|
|
75
|
+
ResolvedPromptEnvelope,
|
|
76
|
+
} from "./prompts/types";
|
|
77
|
+
export { ENV_SLUG_REGEX } from "./prompts/types";
|
|
43
78
|
export type {
|
|
44
79
|
CreateRuleVersionInput,
|
|
45
80
|
CreateRuleVersionResult,
|
|
@@ -92,12 +127,37 @@ type AuthOptions = ServerAuthOptions | BrowserAuthOptions;
|
|
|
92
127
|
|
|
93
128
|
export type ClientOptions = AuthOptions & {
|
|
94
129
|
baseUrl: string;
|
|
130
|
+
/**
|
|
131
|
+
* Project the client is bound to (TO11-980 / S9). Required for the
|
|
132
|
+
* env-aware `prompts.fetch(slug, …)` surface, which builds the
|
|
133
|
+
* project-scoped resolve path from it. Validated non-empty at
|
|
134
|
+
* construction. The deprecated `prompts.resolve()` keeps its per-call
|
|
135
|
+
* `projectId` and ignores this.
|
|
136
|
+
*/
|
|
137
|
+
projectId?: string;
|
|
138
|
+
/**
|
|
139
|
+
* Default serving environment for `prompts.fetch(slug)` (TO11-980 / S9,
|
|
140
|
+
* Style-A). When set, every `fetch()` call inherits it unless overridden
|
|
141
|
+
* per call. Validated eagerly against the project-environments registry
|
|
142
|
+
* rule (lowercase-kebab, `^[a-z0-9-]{1,63}$`): empty / whitespace-only /
|
|
143
|
+
* uppercase / 64+-char values throw at construction with a loud
|
|
144
|
+
* `[to11ai/sdk]` message before any request hits the wire.
|
|
145
|
+
*/
|
|
146
|
+
env?: string;
|
|
95
147
|
/** LRU cache configuration for resolve(). Defaults: ttlSeconds=300, maxEntries=100 */
|
|
96
148
|
cache?: CacheConfig;
|
|
97
149
|
/** Request timeout in milliseconds. Default: 30_000 */
|
|
98
150
|
timeoutMs?: number;
|
|
99
151
|
/** Max retry attempts for network errors / 5xx. Default: 3 */
|
|
100
152
|
maxRetries?: number;
|
|
153
|
+
/**
|
|
154
|
+
* Purely local overrides for server-evaluated SDK-behavior flags (TO11-2100),
|
|
155
|
+
* keyed by namespaced flag key (e.g. `{ "sdk.retry-v2": true }`). These take
|
|
156
|
+
* precedence over server-delivered values and the SDK's coded defaults. For
|
|
157
|
+
* the same flag key, an init option here wins over a `TO11_SDK_BEHAVIOR_*`
|
|
158
|
+
* environment variable. Published-SDK only — no LaunchDarkly involved.
|
|
159
|
+
*/
|
|
160
|
+
behaviorOverrides?: Record<string, unknown>;
|
|
101
161
|
};
|
|
102
162
|
|
|
103
163
|
// ─── Client interface ──────────────────────────────────────────────────────
|
|
@@ -106,6 +166,16 @@ export interface To11aiClient {
|
|
|
106
166
|
readonly prompts: PromptResource;
|
|
107
167
|
readonly projectEnvironments: ProjectEnvironmentResource;
|
|
108
168
|
readonly routingRules: RoutingRulesResource;
|
|
169
|
+
/**
|
|
170
|
+
* Resolve a server-evaluated SDK-behavior flag value (TO11-2100) under the
|
|
171
|
+
* fixed precedence: local override (init option → `TO11_SDK_BEHAVIOR_*` env
|
|
172
|
+
* var) → server-delivered value (captured from responses the client has
|
|
173
|
+
* already made) → the `codedDefault` passed here. A value whose runtime type
|
|
174
|
+
* disagrees with `codedDefault` falls through to the next tier. The SDK
|
|
175
|
+
* carries no LaunchDarkly dependency; this only reads what the server
|
|
176
|
+
* delivered on the envelope.
|
|
177
|
+
*/
|
|
178
|
+
readonly resolveBehavior: <T>(key: string, codedDefault: T) => T;
|
|
109
179
|
}
|
|
110
180
|
|
|
111
181
|
// ─── Factory ───────────────────────────────────────────────────────────────
|
|
@@ -147,6 +217,23 @@ export function createClient(options: ClientOptions): To11aiClient {
|
|
|
147
217
|
throw new Error("[to11ai/sdk] `apiKey` must not be an empty string.");
|
|
148
218
|
}
|
|
149
219
|
|
|
220
|
+
// R0: validate `projectId` eagerly when supplied (loud throw). An empty /
|
|
221
|
+
// whitespace-only value is a misconfiguration; reject before any request.
|
|
222
|
+
if (options.projectId !== undefined && options.projectId.trim() === "") {
|
|
223
|
+
throw new Error(
|
|
224
|
+
"[to11ai/sdk] `projectId` must not be an empty string — omit it or pass a valid project id.",
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// R1: validate the init-time `env` against the project-environments
|
|
229
|
+
// registry rule (stricter than gatewayAuthHeaders' empty-only check) so a
|
|
230
|
+
// malformed env fails locally rather than round-tripping to ENV_NOT_FOUND.
|
|
231
|
+
if (options.env !== undefined && !ENV_SLUG_REGEX.test(options.env)) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
`[to11ai/sdk] \`env\` must match ${ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(options.env)}`,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
150
237
|
// TypeScript's union type prevents callers from providing both, but JS consumers can.
|
|
151
238
|
// Perform an explicit XOR check at runtime to catch the case where both are supplied.
|
|
152
239
|
const hasApiKey = options.apiKey !== undefined;
|
|
@@ -164,16 +251,41 @@ export function createClient(options: ClientOptions): To11aiClient {
|
|
|
164
251
|
? ({ apiKey: options.apiKey } as const)
|
|
165
252
|
: ({ getAccessToken: getAccessToken as () => Promise<string> } as const);
|
|
166
253
|
|
|
254
|
+
// SDK-behavior store (TO11-2100). Env-var overrides (`TO11_SDK_BEHAVIOR_*`)
|
|
255
|
+
// are read ONLY in a non-browser context so the published browser bundle
|
|
256
|
+
// never touches `process.env`; init-option overrides apply everywhere.
|
|
257
|
+
const isBrowser = isDomBrowser || isWorkerBrowser;
|
|
258
|
+
const processEnv =
|
|
259
|
+
!isBrowser &&
|
|
260
|
+
typeof process !== "undefined" &&
|
|
261
|
+
typeof process.env === "object"
|
|
262
|
+
? process.env
|
|
263
|
+
: undefined;
|
|
264
|
+
const behaviorStore = new BehaviorStore({
|
|
265
|
+
initOverrides: options.behaviorOverrides,
|
|
266
|
+
env: processEnv,
|
|
267
|
+
});
|
|
268
|
+
|
|
167
269
|
const transport = new Transport({
|
|
168
270
|
baseUrl: options.baseUrl,
|
|
169
271
|
auth,
|
|
170
272
|
timeoutMs: options.timeoutMs,
|
|
171
273
|
maxRetries: options.maxRetries,
|
|
274
|
+
behaviorStore,
|
|
172
275
|
});
|
|
173
276
|
|
|
174
|
-
const prompts = new PromptResource(transport, options.cache
|
|
277
|
+
const prompts = new PromptResource(transport, options.cache, {
|
|
278
|
+
projectId: options.projectId,
|
|
279
|
+
env: options.env,
|
|
280
|
+
});
|
|
175
281
|
const projectEnvironments = new ProjectEnvironmentResource(transport);
|
|
176
282
|
const routingRules = new RoutingRulesResource(transport);
|
|
177
283
|
|
|
178
|
-
return {
|
|
284
|
+
return {
|
|
285
|
+
prompts,
|
|
286
|
+
projectEnvironments,
|
|
287
|
+
routingRules,
|
|
288
|
+
resolveBehavior: (key, codedDefault) =>
|
|
289
|
+
behaviorStore.resolve(key, codedDefault),
|
|
290
|
+
};
|
|
179
291
|
}
|
package/src/errors.ts
CHANGED
|
@@ -90,6 +90,29 @@ export class To11aiNetworkError extends To11aiError {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Thrown when a 2xx response body fails runtime validation against the
|
|
95
|
+
* generated wire schema. Distinct from `To11aiValidationError`, which models a
|
|
96
|
+
* server-side 4xx validation rejection.
|
|
97
|
+
*/
|
|
98
|
+
export class To11aiResponseValidationError extends To11aiError {
|
|
99
|
+
readonly path: string;
|
|
100
|
+
readonly issues: { message: string; path: (string | number)[] }[];
|
|
101
|
+
|
|
102
|
+
constructor(path: string, zodError: unknown, cause?: unknown) {
|
|
103
|
+
super(`[to11ai/sdk] Response from ${path} failed schema validation`, {
|
|
104
|
+
cause,
|
|
105
|
+
});
|
|
106
|
+
this.name = "To11aiResponseValidationError";
|
|
107
|
+
this.path = path;
|
|
108
|
+
const candidate = (zodError as { issues?: unknown })?.issues;
|
|
109
|
+
this.issues = Array.isArray(candidate)
|
|
110
|
+
? (candidate as { message: string; path: (string | number)[] }[])
|
|
111
|
+
: [];
|
|
112
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
/**
|
|
94
117
|
* Thrown when strict-mode rejects a label move for an unregistered label.
|
|
95
118
|
*
|
|
@@ -227,6 +250,244 @@ export class EnvNotEnabledForProviderError extends To11aiApiError {
|
|
|
227
250
|
}
|
|
228
251
|
}
|
|
229
252
|
|
|
253
|
+
// ─── Prompt resolution typed errors (TO11-980 / S9) ────────────────────────
|
|
254
|
+
//
|
|
255
|
+
// These four subclasses give `prompts.fetch()` callers a discriminable error
|
|
256
|
+
// family for prompt resolution. Each:
|
|
257
|
+
// - extends `To11aiApiError`, so existing broad `catch (err instanceof
|
|
258
|
+
// To11aiApiError)` blocks keep matching (AC8),
|
|
259
|
+
// - carries a structured `reason` payload (not a bare string),
|
|
260
|
+
// - ships a `static is()` type guard like its siblings.
|
|
261
|
+
//
|
|
262
|
+
// IMPORTANT — wire mapping (server TO11-1925/1926): the canonical
|
|
263
|
+
// SCREAMING_SNAKE error code lives in the envelope's `error.details.code`
|
|
264
|
+
// (e.g. `PROMPT_NOT_FOUND`), NOT top-level `error.code` (which carries the
|
|
265
|
+
// generic HTTP class `not_found` / `conflict` / `unprocessable_entity`). The
|
|
266
|
+
// transport mapper reads `details.code` for these four branches — see
|
|
267
|
+
// `transport.ts`. `errorCode` on the constructed error is set to that
|
|
268
|
+
// canonical `details.code` so call sites can switch on it consistently.
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Reason payload for `PromptNotFoundError`.
|
|
272
|
+
*
|
|
273
|
+
* Surfaced when the server cannot resolve the prompt for the given slug
|
|
274
|
+
* (`PROMPT_NOT_FOUND`) or cannot find a version matching the supplied
|
|
275
|
+
* labels (`PROMPT_LABELS_NOT_FOUND`). `slug` / `projectId` are echoed from
|
|
276
|
+
* the request context when the server provides them in `details`.
|
|
277
|
+
*/
|
|
278
|
+
export interface PromptNotFoundReason {
|
|
279
|
+
slug?: string;
|
|
280
|
+
projectId?: string;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Thrown when a prompt cannot be resolved. Covers two server codes (single
|
|
285
|
+
* class so one `catch` matches both):
|
|
286
|
+
*
|
|
287
|
+
* * `PROMPT_NOT_FOUND` — no prompt exists for the requested slug.
|
|
288
|
+
* * `PROMPT_LABELS_NOT_FOUND` — the prompt exists but no version matches
|
|
289
|
+
* all the supplied labels (the `env`→label resolution found nothing).
|
|
290
|
+
*
|
|
291
|
+
* Surfaces at HTTP 404 (the canonical code lives in `details.code`).
|
|
292
|
+
*/
|
|
293
|
+
export class PromptNotFoundError extends To11aiApiError {
|
|
294
|
+
readonly reason: PromptNotFoundReason;
|
|
295
|
+
|
|
296
|
+
constructor(
|
|
297
|
+
message: string,
|
|
298
|
+
errorCode: string,
|
|
299
|
+
reason: PromptNotFoundReason,
|
|
300
|
+
details?: unknown,
|
|
301
|
+
statusCode = 404,
|
|
302
|
+
) {
|
|
303
|
+
super(message, statusCode, errorCode, details);
|
|
304
|
+
this.name = "PromptNotFoundError";
|
|
305
|
+
this.reason = reason;
|
|
306
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
static is(err: unknown): err is PromptNotFoundError {
|
|
310
|
+
return err instanceof PromptNotFoundError;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Reason payload for `EnvNotFoundError`.
|
|
316
|
+
*
|
|
317
|
+
* `source` discriminates the two failure modes (D2):
|
|
318
|
+
* - `"missing"` — no env was supplied at init OR at call time. Raised
|
|
319
|
+
* CLIENT-side, eagerly, before any request (R4 / AC4); `env` is absent.
|
|
320
|
+
* - `"unknown"` — an env was supplied but the server's registry has no
|
|
321
|
+
* such (active) env. Surfaced from the `ENV_NOT_FOUND` server code; `env`
|
|
322
|
+
* carries the rejected slug when the server echoes it.
|
|
323
|
+
*/
|
|
324
|
+
export interface EnvNotFoundReason {
|
|
325
|
+
env?: string;
|
|
326
|
+
source: "missing" | "unknown";
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Thrown when no serving environment can be resolved for a `prompts.fetch()`
|
|
331
|
+
* call. One class, two `reason.source` discriminants (see
|
|
332
|
+
* `EnvNotFoundReason`): client-eager `"missing"` (R4) and server-forwarded
|
|
333
|
+
* `"unknown"` (`ENV_NOT_FOUND`).
|
|
334
|
+
*
|
|
335
|
+
* The client-eager variant carries no `statusCode`/`errorCode` from the wire
|
|
336
|
+
* (no request was made); it reports status `0` and errorCode `ENV_NOT_FOUND`
|
|
337
|
+
* for a uniform catch surface.
|
|
338
|
+
*/
|
|
339
|
+
export class EnvNotFoundError extends To11aiApiError {
|
|
340
|
+
readonly reason: EnvNotFoundReason;
|
|
341
|
+
|
|
342
|
+
constructor(
|
|
343
|
+
message: string,
|
|
344
|
+
reason: EnvNotFoundReason,
|
|
345
|
+
details: unknown,
|
|
346
|
+
// Required (no misleading default): the client-eager `missing()` variant
|
|
347
|
+
// passes 0 (no HTTP call) and the transport mapper passes the real server
|
|
348
|
+
// status, so a hardcoded default would never reflect reality.
|
|
349
|
+
statusCode: number,
|
|
350
|
+
) {
|
|
351
|
+
super(message, statusCode, "ENV_NOT_FOUND", details);
|
|
352
|
+
this.name = "EnvNotFoundError";
|
|
353
|
+
this.reason = reason;
|
|
354
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Build the client-eager (missing-at-both) variant — no transport call. */
|
|
358
|
+
static missing(env?: string): EnvNotFoundError {
|
|
359
|
+
return new EnvNotFoundError(
|
|
360
|
+
"[to11ai/sdk] No environment resolvable for prompts.fetch(): none supplied at client init and none supplied per call. Pass `env` to createClient or fetch(slug, { env }).",
|
|
361
|
+
{ env, source: "missing" },
|
|
362
|
+
undefined,
|
|
363
|
+
0,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
static is(err: unknown): err is EnvNotFoundError {
|
|
368
|
+
return err instanceof EnvNotFoundError;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Reason payload for `NoActiveReleaseError`.
|
|
374
|
+
*
|
|
375
|
+
* Identifies the prompt (by `slug` and/or `promptId`) and the `env` for
|
|
376
|
+
* which no active release row exists.
|
|
377
|
+
*/
|
|
378
|
+
export interface NoActiveReleaseReason {
|
|
379
|
+
slug?: string;
|
|
380
|
+
promptId?: string;
|
|
381
|
+
env?: string;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Thrown when a prompt resolves but has no active release for the requested
|
|
386
|
+
* env (`NO_ACTIVE_RELEASE`). The release primitive (S3) found the prompt but
|
|
387
|
+
* nothing is currently serving in that environment.
|
|
388
|
+
*/
|
|
389
|
+
export class NoActiveReleaseError extends To11aiApiError {
|
|
390
|
+
readonly reason: NoActiveReleaseReason;
|
|
391
|
+
|
|
392
|
+
constructor(
|
|
393
|
+
message: string,
|
|
394
|
+
reason: NoActiveReleaseReason,
|
|
395
|
+
details?: unknown,
|
|
396
|
+
statusCode = 409,
|
|
397
|
+
) {
|
|
398
|
+
super(message, statusCode, "NO_ACTIVE_RELEASE", details);
|
|
399
|
+
this.name = "NoActiveReleaseError";
|
|
400
|
+
this.reason = reason;
|
|
401
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
static is(err: unknown): err is NoActiveReleaseError {
|
|
405
|
+
return err instanceof NoActiveReleaseError;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Reason payload for `PromptPolicyViolationError`.
|
|
411
|
+
*
|
|
412
|
+
* `code` is the specific server policy code (`POLICY_VIOLATION` or one of the
|
|
413
|
+
* `ENV_POLICY_*` family from S5 / TO11-976). `policy` / `detail` are
|
|
414
|
+
* server-shaped and forwarded verbatim when present.
|
|
415
|
+
*/
|
|
416
|
+
export interface PromptPolicyViolationReason {
|
|
417
|
+
code: string;
|
|
418
|
+
policy?: string;
|
|
419
|
+
detail?: unknown;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Thrown when the server's S5 environment-policy machinery rejects a
|
|
424
|
+
* resolution. Forwarded verbatim from the server policy codes — the generic
|
|
425
|
+
* `POLICY_VIOLATION` and the `ENV_POLICY_*` family (required-source-env,
|
|
426
|
+
* minimum-dwell-time, requires-approval, separation-of-duties, …).
|
|
427
|
+
*
|
|
428
|
+
* CRITICAL (D3 branch-ordering): the server can return a policy violation at
|
|
429
|
+
* HTTP 403. The transport mapper MUST match this (by `details.code`) BEFORE
|
|
430
|
+
* the generic `401 || 403 → To11aiAuthError` guard, otherwise a 403 policy
|
|
431
|
+
* error would be swallowed as an auth error (AC6).
|
|
432
|
+
*/
|
|
433
|
+
export class PromptPolicyViolationError extends To11aiApiError {
|
|
434
|
+
readonly reason: PromptPolicyViolationReason;
|
|
435
|
+
|
|
436
|
+
constructor(
|
|
437
|
+
message: string,
|
|
438
|
+
reason: PromptPolicyViolationReason,
|
|
439
|
+
details?: unknown,
|
|
440
|
+
statusCode = 403,
|
|
441
|
+
) {
|
|
442
|
+
super(message, statusCode, reason.code, details);
|
|
443
|
+
this.name = "PromptPolicyViolationError";
|
|
444
|
+
this.reason = reason;
|
|
445
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
static is(err: unknown): err is PromptPolicyViolationError {
|
|
449
|
+
return err instanceof PromptPolicyViolationError;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Reason payload for `AmbiguousEnvLabelsError`.
|
|
455
|
+
*
|
|
456
|
+
* Carried when the supplied env-label matched more than one candidate
|
|
457
|
+
* version and the server cannot pick deterministically
|
|
458
|
+
* (`AMBIGUOUS_ENV_LABELS`).
|
|
459
|
+
*/
|
|
460
|
+
export interface AmbiguousEnvLabelsReason {
|
|
461
|
+
env?: string;
|
|
462
|
+
slug?: string;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Thrown when the env→label resolution is ambiguous on the server
|
|
467
|
+
* (`AMBIGUOUS_ENV_LABELS`) — the label matched multiple candidates and the
|
|
468
|
+
* server declines to guess. Surfaces (rather than 500ing) so callers get a
|
|
469
|
+
* typed, actionable error.
|
|
470
|
+
*/
|
|
471
|
+
export class AmbiguousEnvLabelsError extends To11aiApiError {
|
|
472
|
+
readonly reason: AmbiguousEnvLabelsReason;
|
|
473
|
+
|
|
474
|
+
constructor(
|
|
475
|
+
message: string,
|
|
476
|
+
reason: AmbiguousEnvLabelsReason,
|
|
477
|
+
details?: unknown,
|
|
478
|
+
statusCode = 409,
|
|
479
|
+
) {
|
|
480
|
+
super(message, statusCode, "AMBIGUOUS_ENV_LABELS", details);
|
|
481
|
+
this.name = "AmbiguousEnvLabelsError";
|
|
482
|
+
this.reason = reason;
|
|
483
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
static is(err: unknown): err is AmbiguousEnvLabelsError {
|
|
487
|
+
return err instanceof AmbiguousEnvLabelsError;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
230
491
|
/**
|
|
231
492
|
* Thrown when version create rejects the snapshot for a structural
|
|
232
493
|
* reason — invalid variant params, malformed `when` clause, illegal
|