@to11ai/sdk 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/behaviors/store.d.ts +62 -0
  2. package/dist/client.cjs +37 -4
  3. package/dist/client.d.ts +41 -2
  4. package/dist/client.js +45 -4
  5. package/dist/errors.cjs +90 -1
  6. package/dist/errors.d.ts +142 -0
  7. package/dist/errors.js +90 -1
  8. package/dist/gateway.cjs +12 -0
  9. package/dist/gateway.d.ts +26 -12
  10. package/dist/gateway.js +14 -0
  11. package/dist/generated/index.d.ts +1 -0
  12. package/dist/generated/types.gen.d.ts +22978 -0
  13. package/dist/generated/zod.gen.d.ts +13543 -0
  14. package/dist/projectEnvironments/resource.cjs +7 -2
  15. package/dist/projectEnvironments/resource.js +7 -2
  16. package/dist/projectEnvironments/types.d.ts +4 -75
  17. package/dist/prompts/cache.d.ts +24 -3
  18. package/dist/prompts/resource.cjs +101 -3
  19. package/dist/prompts/resource.d.ts +47 -3
  20. package/dist/prompts/resource.js +103 -5
  21. package/dist/prompts/types.d.ts +36 -177
  22. package/dist/routingRules/resource.cjs +9 -2
  23. package/dist/routingRules/resource.js +9 -2
  24. package/dist/routingRules/types.d.ts +12 -123
  25. package/dist/transport.d.ts +23 -0
  26. package/package.json +23 -12
  27. package/src/behaviors/store.ts +175 -0
  28. package/src/client.ts +115 -3
  29. package/src/errors.ts +261 -0
  30. package/src/gateway.ts +54 -12
  31. package/src/generated/index.ts +3 -0
  32. package/src/generated/types.gen.ts +24087 -0
  33. package/src/generated/zod.gen.ts +12660 -0
  34. package/src/projectEnvironments/resource.ts +10 -0
  35. package/src/projectEnvironments/types.ts +9 -91
  36. package/src/prompts/cache.ts +39 -4
  37. package/src/prompts/resource.ts +258 -5
  38. package/src/prompts/types.ts +56 -237
  39. package/src/routingRules/resource.ts +10 -1
  40. package/src/routingRules/types.ts +21 -155
  41. package/src/traceparent.ts +69 -4
  42. package/src/transport.ts +188 -3
@@ -42,6 +42,8 @@ __export(exports_resource, {
42
42
  ProjectEnvironmentResource: () => ProjectEnvironmentResource
43
43
  });
44
44
  module.exports = __toCommonJS(exports_resource);
45
+ var import_zod = require("zod");
46
+ var import_zod2 = require("../generated/zod.gen");
45
47
  function unwrap(envelope) {
46
48
  if (envelope.data === null || envelope.data === undefined) {
47
49
  throw new Error("API returned success but data is null");
@@ -62,12 +64,15 @@ class ProjectEnvironmentResource {
62
64
  }
63
65
  async list(input) {
64
66
  const envelope = await this.transport.request("GET", projectEnvironmentsPath(input.projectId), {
65
- query: input.includeArchived ? { includeArchived: "true" } : undefined
67
+ query: input.includeArchived ? { includeArchived: "true" } : undefined,
68
+ decode: (data) => import_zod.z.array(import_zod2.zProjectEnvironment).parse(data)
66
69
  });
67
70
  return unwrap(envelope);
68
71
  }
69
72
  async get(input) {
70
- const envelope = await this.transport.request("GET", projectEnvironmentPath(input.projectId, input.slug));
73
+ const envelope = await this.transport.request("GET", projectEnvironmentPath(input.projectId, input.slug), {
74
+ decode: (data) => import_zod2.zProjectEnvironment.parse(data)
75
+ });
71
76
  return unwrap(envelope);
72
77
  }
73
78
  async create(input) {
@@ -1,4 +1,6 @@
1
1
  // src/projectEnvironments/resource.ts
2
+ import { z } from "zod";
3
+ import { zProjectEnvironment } from "../generated/zod.gen";
2
4
  function unwrap(envelope) {
3
5
  if (envelope.data === null || envelope.data === undefined) {
4
6
  throw new Error("API returned success but data is null");
@@ -19,12 +21,15 @@ class ProjectEnvironmentResource {
19
21
  }
20
22
  async list(input) {
21
23
  const envelope = await this.transport.request("GET", projectEnvironmentsPath(input.projectId), {
22
- query: input.includeArchived ? { includeArchived: "true" } : undefined
24
+ query: input.includeArchived ? { includeArchived: "true" } : undefined,
25
+ decode: (data) => z.array(zProjectEnvironment).parse(data)
23
26
  });
24
27
  return unwrap(envelope);
25
28
  }
26
29
  async get(input) {
27
- const envelope = await this.transport.request("GET", projectEnvironmentPath(input.projectId, input.slug));
30
+ const envelope = await this.transport.request("GET", projectEnvironmentPath(input.projectId, input.slug), {
31
+ decode: (data) => zProjectEnvironment.parse(data)
32
+ });
28
33
  return unwrap(envelope);
29
34
  }
30
35
  async create(input) {
@@ -1,62 +1,9 @@
1
- /**
2
- * Project-scoped environment row as returned by
3
- * `GET /v1/projects/:projectId/environments/:slug`.
4
- *
5
- * Notes:
6
- * - `isSystem: true` for the seeded `prod` / `staging` rows. Those rows
7
- * cannot be renamed, archived, or have their `slug`/`isSystem` mutated.
8
- * - `isGatewayFallback: true` marks the env the gateway routes to when no
9
- * `x-to11-env` header is supplied. At most one env per project may carry
10
- * this flag; clearing the only fallback is rejected with 409
11
- * `CANNOT_CLEAR_ONLY_GATEWAY_FALLBACK`.
12
- * - `archivedAt` is `null` for live envs and an ISO-8601 timestamp for
13
- * soft-archived ones.
14
- */
15
- export interface ProjectEnvironment {
16
- id: string;
17
- projectId: string;
18
- slug: string;
19
- name: string;
20
- color: string | null;
21
- description: string | null;
22
- isPointer: boolean;
23
- requiresApproval: boolean;
24
- isDefault: boolean;
25
- isGatewayFallback: boolean;
26
- sortOrder: number;
27
- isSystem: boolean;
28
- archivedAt: string | null;
29
- createdBy: string;
30
- updatedBy: string;
31
- createdAt: string;
32
- updatedAt: string;
33
- }
34
- /**
35
- * Pointer-count payload attached to archive responses. `prompts` is real
36
- * today; `rules` / `providers` are 0 in v1 (reserved for Wave 6+ specs to
37
- * keep the response shape stable when those domains land).
38
- */
39
- export interface LivePointerCounts {
40
- prompts: number;
41
- rules: number;
42
- providers: number;
43
- }
44
- /**
45
- * Response body for `POST /v1/projects/:projectId/environments/:slug/archive`.
46
- * Carries the soft-archived environment plus the count of live pointers
47
- * cascade-cleared by the archive operation (informational, for the
48
- * confirmation modal in the web UI).
49
- */
50
- export interface ArchiveProjectEnvironmentResult {
51
- environment: ProjectEnvironment;
52
- livePointerCounts: LivePointerCounts;
53
- }
1
+ import type { ArchiveProjectEnvironmentResult as GenArchiveProjectEnvironmentResult, LivePointerCounts as GenLivePointerCounts, ProjectEnvironment as GenProjectEnvironment } from "../generated/types.gen";
2
+ export type ProjectEnvironment = GenProjectEnvironment;
3
+ export type LivePointerCounts = GenLivePointerCounts;
4
+ export type ArchiveProjectEnvironmentResult = GenArchiveProjectEnvironmentResult;
54
5
  export interface ListProjectEnvironmentsInput {
55
6
  projectId: string;
56
- /**
57
- * When `true`, the response includes archived envs (those with a
58
- * non-null `archivedAt`). Default behavior excludes them.
59
- */
60
7
  includeArchived?: boolean;
61
8
  }
62
9
  export type ListProjectEnvironmentsResult = ProjectEnvironment[];
@@ -65,15 +12,6 @@ export interface GetProjectEnvironmentInput {
65
12
  slug: string;
66
13
  }
67
14
  export type GetProjectEnvironmentResult = ProjectEnvironment;
68
- /**
69
- * Create input — must specify a unique `slug` matching lowercase-kebab
70
- * (a-z, 0-9, hyphen, 1..63 chars). Reserved system slugs (`prod`,
71
- * `staging`) are rejected by the API with `ENVIRONMENT_SLUG_TAKEN`.
72
- *
73
- * Setting `isGatewayFallback: true` here additionally requires the
74
- * `project:manage_default_environment` capability; the create use case
75
- * atomically clears the flag on any other env in the same project.
76
- */
77
15
  export interface CreateProjectEnvironmentInput {
78
16
  projectId: string;
79
17
  slug: string;
@@ -87,15 +25,6 @@ export interface CreateProjectEnvironmentInput {
87
25
  sortOrder?: number;
88
26
  }
89
27
  export type CreateProjectEnvironmentResult = ProjectEnvironment;
90
- /**
91
- * PATCH input — every field is optional. `slug` rename triggers a 7-day
92
- * alias row server-side. System envs (`isSystem: true`) reject `slug`,
93
- * `isSystem`, and other immutable-field mutations with
94
- * `SYSTEM_ENV_IMMUTABLE`.
95
- *
96
- * As with `create`, mutating `isGatewayFallback` requires the additional
97
- * `project:manage_default_environment` capability.
98
- */
99
28
  export interface UpdateProjectEnvironmentInput {
100
29
  projectId: string;
101
30
  slug: string;
@@ -1,10 +1,15 @@
1
- import type { ResolvedPrompt } from "./types";
1
+ import type { FetchedPrompt, PromptContext, ResolvedPrompt } from "./types";
2
2
  export interface CacheConfig {
3
3
  /** Time-to-live in seconds. Default: 300 (5 minutes) */
4
4
  ttlSeconds?: number;
5
5
  /** Maximum number of entries. Default: 100 */
6
6
  maxEntries?: number;
7
7
  }
8
+ /**
9
+ * The cache stores either a legacy `resolve()` result or an enriched
10
+ * `fetch()` result. Both are JSON-cloneable; the cache is shape-agnostic.
11
+ */
12
+ export type CacheableResolved = ResolvedPrompt | FetchedPrompt;
8
13
  /**
9
14
  * Build a canonical cache key for a resolve() call.
10
15
  *
@@ -18,6 +23,22 @@ export interface CacheConfig {
18
23
  * values cannot cause key collisions (e.g. a slug containing "|" or ",").
19
24
  */
20
25
  export declare function buildCacheKey(projectId: string, slug: string, labels: string[], variables?: Record<string, unknown>): string;
26
+ /**
27
+ * Build a cache key for an env-aware `fetch()` call.
28
+ *
29
+ * Folds in `env`, `context`, and `subject` ON TOP of the base
30
+ * project/slug/labels/variables key so two calls that differ only by
31
+ * env/context/subject do not return a stale cross-env / cross-cohort hit
32
+ * (spec §3 "Caching key changes"). The env slug is also forwarded as the
33
+ * single resolution label (env→label mapping, correction #3), so it appears
34
+ * in the `labels` segment too; including it explicitly here keeps the key
35
+ * unambiguous and self-documenting.
36
+ *
37
+ * `subject` participates in the key so a `subject`-keyed weighted assignment
38
+ * caches as the same deterministic variant on repeat (D8); the
39
+ * weighted-no-subject bypass is handled in the resource, not here.
40
+ */
41
+ export declare function buildFetchCacheKey(projectId: string, slug: string, env: string, context?: PromptContext, subject?: string, variables?: Record<string, unknown>): string;
21
42
  /**
22
43
  * In-memory LRU cache for ResolvedPrompt objects.
23
44
  *
@@ -30,8 +51,8 @@ export declare class PromptCache {
30
51
  private readonly maxEntries;
31
52
  private readonly store;
32
53
  constructor(config?: CacheConfig);
33
- get(key: string): ResolvedPrompt | undefined;
34
- set(key: string, value: ResolvedPrompt): void;
54
+ get(key: string): CacheableResolved | undefined;
55
+ set(key: string, value: CacheableResolved): void;
35
56
  delete(key: string): void;
36
57
  clear(): void;
37
58
  get size(): number;
@@ -42,8 +42,20 @@ __export(exports_resource, {
42
42
  PromptResource: () => PromptResource
43
43
  });
44
44
  module.exports = __toCommonJS(exports_resource);
45
+ var import_zod = require("zod");
45
46
  var import_errors = require("../errors");
47
+ var import_zod2 = require("../generated/zod.gen");
46
48
  var import_cache = require("./cache");
49
+ var import_types = require("./types");
50
+ var zResolvedPromptLegacy = import_zod2.zResolvedPrompt.pick({
51
+ promptId: true,
52
+ versionId: true,
53
+ version: true,
54
+ labels: true,
55
+ contentHash: true,
56
+ rendered: true
57
+ });
58
+ var zResolvedPromptEnvelope = import_zod2.zResolvedPrompt.partial({ releaseId: true }).extend({ strategy: import_zod.z.enum(["single", "weighted"]).optional() });
47
59
  function unwrap(envelope) {
48
60
  if (envelope.data === null || envelope.data === undefined) {
49
61
  throw new Error("API returned success but data is null");
@@ -53,6 +65,36 @@ function unwrap(envelope) {
53
65
  function getProjectPromptsPath(projectId) {
54
66
  return `/v1/projects/${encodeURIComponent(projectId)}/prompts`;
55
67
  }
68
+ function renderedToMessages(rendered) {
69
+ if (rendered.format === "chat") {
70
+ return rendered.messages;
71
+ }
72
+ return [{ role: "user", content: rendered.text }];
73
+ }
74
+ function normalizeResolvedEnvelope(raw) {
75
+ const provenance = raw;
76
+ const result = {
77
+ messages: renderedToMessages(raw.rendered),
78
+ version: raw.version,
79
+ releaseId: provenance.releaseId ?? null,
80
+ blockRenderRecord: provenance.blockRenderRecord ?? {},
81
+ promptId: raw.promptId,
82
+ versionId: raw.versionId,
83
+ contentHash: raw.contentHash,
84
+ labels: raw.labels,
85
+ rendered: raw.rendered
86
+ };
87
+ if (typeof provenance.variantName === "string" && provenance.variantName.length > 0) {
88
+ result.variantName = provenance.variantName;
89
+ }
90
+ return result;
91
+ }
92
+ function liftFallbackToFetched(fallback) {
93
+ if ("messages" in fallback) {
94
+ return structuredClone(fallback);
95
+ }
96
+ return normalizeResolvedEnvelope(fallback);
97
+ }
56
98
  function getProjectPromptPath(projectId, promptId) {
57
99
  return `${getProjectPromptsPath(projectId)}/${encodeURIComponent(promptId)}`;
58
100
  }
@@ -87,9 +129,13 @@ function buildLabelEventsQuery(input) {
87
129
  class PromptResource {
88
130
  transport;
89
131
  cache;
90
- constructor(transport, cacheConfig) {
132
+ projectId;
133
+ env;
134
+ constructor(transport, cacheConfig, config) {
91
135
  this.transport = transport;
92
136
  this.cache = new import_cache.PromptCache(cacheConfig);
137
+ this.projectId = config?.projectId;
138
+ this.env = config?.env;
93
139
  }
94
140
  async create(input) {
95
141
  const { projectId, ...body } = input;
@@ -153,6 +199,57 @@ class PromptResource {
153
199
  const envelope = await this.transport.request("GET", `${getProjectPromptPath(projectId, promptId)}/labels`);
154
200
  return unwrap(envelope);
155
201
  }
202
+ async fetch(slug, options = {}) {
203
+ if (this.projectId === undefined || this.projectId.trim() === "") {
204
+ throw new Error("[to11ai/sdk] `prompts.fetch()` requires a `projectId` on the client — pass it to createClient({ projectId, ... }).");
205
+ }
206
+ const env = options.env ?? this.env;
207
+ if (env === undefined) {
208
+ throw import_errors.EnvNotFoundError.missing();
209
+ }
210
+ if (!import_types.ENV_SLUG_REGEX.test(env)) {
211
+ throw new Error(`[to11ai/sdk] \`env\` must match ${import_types.ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(env)}`);
212
+ }
213
+ const {
214
+ context,
215
+ subject,
216
+ variables,
217
+ cache: useCache = true,
218
+ fallback
219
+ } = options;
220
+ const cacheKey = import_cache.buildFetchCacheKey(this.projectId, slug, env, context, subject, variables);
221
+ if (useCache) {
222
+ const cached = this.cache.get(cacheKey);
223
+ if (cached && "messages" in cached)
224
+ return structuredClone(cached);
225
+ }
226
+ const body = { slug, labels: [env] };
227
+ if (context !== undefined)
228
+ body.context = context;
229
+ if (subject !== undefined)
230
+ body.subject = subject;
231
+ if (variables !== undefined)
232
+ body.variables = variables;
233
+ try {
234
+ const envelope = await this.transport.request("POST", `${getProjectPromptsPath(this.projectId)}:resolve`, {
235
+ body,
236
+ decode: (data) => zResolvedPromptEnvelope.parse(data)
237
+ });
238
+ const raw = unwrap(envelope);
239
+ const result = normalizeResolvedEnvelope(raw);
240
+ const isWeighted = raw.strategy === "weighted" || result.variantName !== undefined;
241
+ const cacheable = !(isWeighted && subject === undefined);
242
+ if (useCache && cacheable) {
243
+ this.cache.set(cacheKey, structuredClone(result));
244
+ }
245
+ return result;
246
+ } catch (err) {
247
+ if (err instanceof import_errors.To11aiNetworkError && fallback !== undefined) {
248
+ return liftFallbackToFetched(fallback);
249
+ }
250
+ throw err;
251
+ }
252
+ }
156
253
  async resolve(input) {
157
254
  const {
158
255
  projectId,
@@ -165,12 +262,13 @@ class PromptResource {
165
262
  const cacheKey = import_cache.buildCacheKey(projectId, slug, labels, variables);
166
263
  if (useCache) {
167
264
  const cached = this.cache.get(cacheKey);
168
- if (cached)
265
+ if (cached && !("messages" in cached))
169
266
  return structuredClone(cached);
170
267
  }
171
268
  try {
172
269
  const envelope = await this.transport.request("POST", `${getProjectPromptsPath(projectId)}:resolve`, {
173
- body: { slug, labels, variables }
270
+ body: { slug, labels, variables },
271
+ decode: (data) => zResolvedPromptLegacy.parse(data)
174
272
  });
175
273
  const result = unwrap(envelope);
176
274
  if (useCache) {
@@ -1,12 +1,27 @@
1
1
  import type { Transport } from "../transport";
2
2
  import type { CacheConfig } from "./cache";
3
- import type { ArchivePromptInput, ArchivePromptResult, CreateProjectPromptLabelInput, CreateProjectPromptLabelResult, CreatePromptInput, CreatePromptResult, CreatePromptVersionInput, CreatePromptVersionResult, DeleteProjectPromptLabelInput, DeleteProjectPromptLabelResult, GetProjectPromptLabelInput, GetProjectPromptLabelResult, GetPromptInput, GetPromptResult, GetPromptVersionInput, GetPromptVersionResult, ListLabelEventsInput, ListLabelEventsResult, ListProjectPromptLabelsInput, ListProjectPromptLabelsResult, ListProjectPromptLabelUsagesInput, ListProjectPromptLabelUsagesResult, ListPromptLabelsInput, ListPromptLabelsResult, ListPromptsInput, ListPromptsResult, ListPromptVersionsInput, ListPromptVersionsResult, MovePromptLabelInput, MovePromptLabelResult, PaginatedResult, ResolvePromptInput, ResolvePromptResult, UpdateProjectPromptLabelInput, UpdateProjectPromptLabelResult, UpdatePromptInput, UpdatePromptResult } from "./types";
3
+ import type { ArchivePromptInput, ArchivePromptResult, CreateProjectPromptLabelInput, CreateProjectPromptLabelResult, CreatePromptInput, CreatePromptResult, CreatePromptVersionInput, CreatePromptVersionResult, DeleteProjectPromptLabelInput, DeleteProjectPromptLabelResult, FetchedPrompt, FetchPromptOptions, GetProjectPromptLabelInput, GetProjectPromptLabelResult, GetPromptInput, GetPromptResult, GetPromptVersionInput, GetPromptVersionResult, ListLabelEventsInput, ListLabelEventsResult, ListProjectPromptLabelsInput, ListProjectPromptLabelsResult, ListProjectPromptLabelUsagesInput, ListProjectPromptLabelUsagesResult, ListPromptLabelsInput, ListPromptLabelsResult, ListPromptsInput, ListPromptsResult, ListPromptVersionsInput, ListPromptVersionsResult, MovePromptLabelInput, MovePromptLabelResult, PaginatedResult, ResolvePromptInput, ResolvePromptResult, UpdateProjectPromptLabelInput, UpdateProjectPromptLabelResult, UpdatePromptInput, UpdatePromptResult } from "./types";
4
4
  export type { CacheConfig } from "./cache";
5
- export type { ArchivePromptInput, ArchivePromptResult, ChatMessage, CreateProjectPromptLabelInput, CreateProjectPromptLabelResult, CreatePromptInput, CreatePromptResult, CreatePromptVersionInput, CreatePromptVersionResult, DeleteProjectPromptLabelInput, DeleteProjectPromptLabelResult, GetProjectPromptLabelInput, GetProjectPromptLabelResult, GetPromptInput, GetPromptResult, GetPromptVersionInput, GetPromptVersionResult, ListLabelEventsInput, ListLabelEventsResult, ListProjectPromptLabelsInput, ListProjectPromptLabelsResult, ListProjectPromptLabelUsagesInput, ListProjectPromptLabelUsagesResult, ListPromptLabelsInput, ListPromptLabelsResult, ListPromptsInput, ListPromptsResult, ListPromptVersionsInput, ListPromptVersionsResult, MovePromptLabelInput, MovePromptLabelResult, PaginatedResult, PaginationInfo, PaletteToken, ProjectPromptLabel, ProjectPromptLabelUsage, Prompt, PromptFormat, PromptLabelEvent, PromptLabelPointer, PromptStatus, PromptVersion, RenderedChatMessage, RenderedPrompt, ResolvedPrompt, ResolvePromptInput, ResolvePromptResult, RowError, TextChatMessage, TextChatRole, ToolChatMessage, UpdateProjectPromptLabelInput, UpdateProjectPromptLabelResult, UpdatePromptInput, UpdatePromptResult, } from "./types";
5
+ export type { ArchivePromptInput, ArchivePromptResult, BlockRenderRecord, ChatMessage, CreateProjectPromptLabelInput, CreateProjectPromptLabelResult, CreatePromptInput, CreatePromptResult, CreatePromptVersionInput, CreatePromptVersionResult, DeleteProjectPromptLabelInput, DeleteProjectPromptLabelResult, FetchedPrompt, FetchPromptOptions, GetProjectPromptLabelInput, GetProjectPromptLabelResult, GetPromptInput, GetPromptResult, GetPromptVersionInput, GetPromptVersionResult, ListLabelEventsInput, ListLabelEventsResult, ListProjectPromptLabelsInput, ListProjectPromptLabelsResult, ListProjectPromptLabelUsagesInput, ListProjectPromptLabelUsagesResult, ListPromptLabelsInput, ListPromptLabelsResult, ListPromptsInput, ListPromptsResult, ListPromptVersionsInput, ListPromptVersionsResult, MovePromptLabelInput, MovePromptLabelResult, PaginatedResult, PaginationInfo, PaletteToken, ProjectPromptLabel, ProjectPromptLabelUsage, Prompt, PromptContext, PromptFormat, PromptLabelEvent, PromptLabelPointer, PromptStatus, PromptVersion, RenderedChatMessage, RenderedPrompt, ResolvedPrompt, ResolvedPromptEnvelope, ResolvePromptInput, ResolvePromptResult, RowError, TextChatMessage, TextChatRole, ToolChatMessage, UpdateProjectPromptLabelInput, UpdateProjectPromptLabelResult, UpdatePromptInput, UpdatePromptResult, } from "./types";
6
+ /**
7
+ * Init-time config for the env-aware `fetch()` surface (TO11-980 / S9).
8
+ *
9
+ * `projectId` and `env` come from `createClient`. `projectId` is required for
10
+ * `fetch()` (it builds the project-scoped resolve path); `env` is the default
11
+ * serving environment inherited by every `fetch(slug)` call.
12
+ */
13
+ export interface PromptResourceConfig {
14
+ /** Project the client is bound to. Required for `fetch()`; validated at createClient. */
15
+ projectId?: string;
16
+ /** Default serving env for `fetch()`. Validated against ENV_SLUG_REGEX at createClient. */
17
+ env?: string;
18
+ }
6
19
  export declare class PromptResource {
7
20
  private readonly transport;
8
21
  private readonly cache;
9
- constructor(transport: Transport, cacheConfig?: CacheConfig);
22
+ private readonly projectId;
23
+ private readonly env;
24
+ constructor(transport: Transport, cacheConfig?: CacheConfig, config?: PromptResourceConfig);
10
25
  create(input: CreatePromptInput): Promise<CreatePromptResult>;
11
26
  list(input: ListPromptsInput): Promise<PaginatedResult<ListPromptsResult>>;
12
27
  get(input: GetPromptInput): Promise<GetPromptResult>;
@@ -17,6 +32,35 @@ export declare class PromptResource {
17
32
  getVersion(input: GetPromptVersionInput): Promise<GetPromptVersionResult>;
18
33
  moveLabel(input: MovePromptLabelInput): Promise<MovePromptLabelResult>;
19
34
  listLabels(input: ListPromptLabelsInput): Promise<ListPromptLabelsResult>;
35
+ /**
36
+ * Resolve a prompt for the client's configured `projectId` + `env`
37
+ * (Style-A). The ergonomic slug-first surface: env is configured once at
38
+ * `createClient` and inherited here, with an optional per-call override.
39
+ *
40
+ * - **env→label wire mapping (correction #3):** the resolve route is
41
+ * label-based (`/v1/projects/:projectId/prompts:resolve`, body
42
+ * `labels[]`); it does NOT accept a top-level `env` field. The resolved
43
+ * env slug (lowercase-kebab — a valid label name) is therefore sent as
44
+ * the single resolution label: body `{ slug, labels: [env], context?,
45
+ * subject?, variables? }`.
46
+ * - **No silent fallback (R4):** if no env is resolvable (none at init AND
47
+ * none per call) this throws `EnvNotFoundError` (`reason.source =
48
+ * "missing"`) CLIENT-side, eagerly, before any transport call.
49
+ * - **Cache (D8):** caches `single`-strategy releases and weighted releases
50
+ * WITH a `subject`; BYPASSES the cache for weighted releases with NO
51
+ * `subject` (a uniform-random assignment must not be pinned for the TTL).
52
+ *
53
+ * Prefer this over the deprecated `resolve()`.
54
+ */
55
+ fetch(slug: string, options?: FetchPromptOptions): Promise<FetchedPrompt>;
56
+ /**
57
+ * @deprecated Use {@link PromptResource.fetch | `prompts.fetch(slug,
58
+ * options?)`} instead. `resolve()` is env-agnostic (no `env` / `context` /
59
+ * `subject`, no enriched release/variant provenance, no typed-env errors)
60
+ * and is retained only for back-compat. It is NOT subject to the R4
61
+ * no-silent-fallback env gate (it has no env concept). New code should
62
+ * configure `projectId` + `env` at `createClient` and call `fetch()`.
63
+ */
20
64
  resolve(input: ResolvePromptInput): Promise<ResolvePromptResult>;
21
65
  /**
22
66
  * List the project-level label registry.
@@ -1,6 +1,18 @@
1
1
  // src/prompts/resource.ts
2
- import { To11aiNetworkError } from "../errors";
3
- import { buildCacheKey, PromptCache } from "./cache";
2
+ import { z } from "zod";
3
+ import { EnvNotFoundError, To11aiNetworkError } from "../errors";
4
+ import { zResolvedPrompt } from "../generated/zod.gen";
5
+ import { buildCacheKey, buildFetchCacheKey, PromptCache } from "./cache";
6
+ import { ENV_SLUG_REGEX } from "./types";
7
+ var zResolvedPromptLegacy = zResolvedPrompt.pick({
8
+ promptId: true,
9
+ versionId: true,
10
+ version: true,
11
+ labels: true,
12
+ contentHash: true,
13
+ rendered: true
14
+ });
15
+ var zResolvedPromptEnvelope = zResolvedPrompt.partial({ releaseId: true }).extend({ strategy: z.enum(["single", "weighted"]).optional() });
4
16
  function unwrap(envelope) {
5
17
  if (envelope.data === null || envelope.data === undefined) {
6
18
  throw new Error("API returned success but data is null");
@@ -10,6 +22,36 @@ function unwrap(envelope) {
10
22
  function getProjectPromptsPath(projectId) {
11
23
  return `/v1/projects/${encodeURIComponent(projectId)}/prompts`;
12
24
  }
25
+ function renderedToMessages(rendered) {
26
+ if (rendered.format === "chat") {
27
+ return rendered.messages;
28
+ }
29
+ return [{ role: "user", content: rendered.text }];
30
+ }
31
+ function normalizeResolvedEnvelope(raw) {
32
+ const provenance = raw;
33
+ const result = {
34
+ messages: renderedToMessages(raw.rendered),
35
+ version: raw.version,
36
+ releaseId: provenance.releaseId ?? null,
37
+ blockRenderRecord: provenance.blockRenderRecord ?? {},
38
+ promptId: raw.promptId,
39
+ versionId: raw.versionId,
40
+ contentHash: raw.contentHash,
41
+ labels: raw.labels,
42
+ rendered: raw.rendered
43
+ };
44
+ if (typeof provenance.variantName === "string" && provenance.variantName.length > 0) {
45
+ result.variantName = provenance.variantName;
46
+ }
47
+ return result;
48
+ }
49
+ function liftFallbackToFetched(fallback) {
50
+ if ("messages" in fallback) {
51
+ return structuredClone(fallback);
52
+ }
53
+ return normalizeResolvedEnvelope(fallback);
54
+ }
13
55
  function getProjectPromptPath(projectId, promptId) {
14
56
  return `${getProjectPromptsPath(projectId)}/${encodeURIComponent(promptId)}`;
15
57
  }
@@ -44,9 +86,13 @@ function buildLabelEventsQuery(input) {
44
86
  class PromptResource {
45
87
  transport;
46
88
  cache;
47
- constructor(transport, cacheConfig) {
89
+ projectId;
90
+ env;
91
+ constructor(transport, cacheConfig, config) {
48
92
  this.transport = transport;
49
93
  this.cache = new PromptCache(cacheConfig);
94
+ this.projectId = config?.projectId;
95
+ this.env = config?.env;
50
96
  }
51
97
  async create(input) {
52
98
  const { projectId, ...body } = input;
@@ -110,6 +156,57 @@ class PromptResource {
110
156
  const envelope = await this.transport.request("GET", `${getProjectPromptPath(projectId, promptId)}/labels`);
111
157
  return unwrap(envelope);
112
158
  }
159
+ async fetch(slug, options = {}) {
160
+ if (this.projectId === undefined || this.projectId.trim() === "") {
161
+ throw new Error("[to11ai/sdk] `prompts.fetch()` requires a `projectId` on the client — pass it to createClient({ projectId, ... }).");
162
+ }
163
+ const env = options.env ?? this.env;
164
+ if (env === undefined) {
165
+ throw EnvNotFoundError.missing();
166
+ }
167
+ if (!ENV_SLUG_REGEX.test(env)) {
168
+ throw new Error(`[to11ai/sdk] \`env\` must match ${ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(env)}`);
169
+ }
170
+ const {
171
+ context,
172
+ subject,
173
+ variables,
174
+ cache: useCache = true,
175
+ fallback
176
+ } = options;
177
+ const cacheKey = buildFetchCacheKey(this.projectId, slug, env, context, subject, variables);
178
+ if (useCache) {
179
+ const cached = this.cache.get(cacheKey);
180
+ if (cached && "messages" in cached)
181
+ return structuredClone(cached);
182
+ }
183
+ const body = { slug, labels: [env] };
184
+ if (context !== undefined)
185
+ body.context = context;
186
+ if (subject !== undefined)
187
+ body.subject = subject;
188
+ if (variables !== undefined)
189
+ body.variables = variables;
190
+ try {
191
+ const envelope = await this.transport.request("POST", `${getProjectPromptsPath(this.projectId)}:resolve`, {
192
+ body,
193
+ decode: (data) => zResolvedPromptEnvelope.parse(data)
194
+ });
195
+ const raw = unwrap(envelope);
196
+ const result = normalizeResolvedEnvelope(raw);
197
+ const isWeighted = raw.strategy === "weighted" || result.variantName !== undefined;
198
+ const cacheable = !(isWeighted && subject === undefined);
199
+ if (useCache && cacheable) {
200
+ this.cache.set(cacheKey, structuredClone(result));
201
+ }
202
+ return result;
203
+ } catch (err) {
204
+ if (err instanceof To11aiNetworkError && fallback !== undefined) {
205
+ return liftFallbackToFetched(fallback);
206
+ }
207
+ throw err;
208
+ }
209
+ }
113
210
  async resolve(input) {
114
211
  const {
115
212
  projectId,
@@ -122,12 +219,13 @@ class PromptResource {
122
219
  const cacheKey = buildCacheKey(projectId, slug, labels, variables);
123
220
  if (useCache) {
124
221
  const cached = this.cache.get(cacheKey);
125
- if (cached)
222
+ if (cached && !("messages" in cached))
126
223
  return structuredClone(cached);
127
224
  }
128
225
  try {
129
226
  const envelope = await this.transport.request("POST", `${getProjectPromptsPath(projectId)}:resolve`, {
130
- body: { slug, labels, variables }
227
+ body: { slug, labels, variables },
228
+ decode: (data) => zResolvedPromptLegacy.parse(data)
131
229
  });
132
230
  const result = unwrap(envelope);
133
231
  if (useCache) {