@to11ai/sdk 2.0.0-rc.2 → 2.0.0-rc.4

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.
@@ -1,9 +1,10 @@
1
- import { z } from "zod";
2
- import { EnvNotFoundError, To11aiNetworkError } from "../errors";
1
+ import {
2
+ MissingLabelError,
3
+ To11aiApiError,
4
+ To11aiNetworkError,
5
+ } from "../errors";
3
6
  import { zResolvedPrompt } from "../generated/zod.gen";
4
7
  import type { ApiEnvelope, Transport } from "../transport";
5
- import type { CacheConfig } from "./cache";
6
- import { buildRenderCacheKey, PromptCache } from "./cache";
7
8
  import type {
8
9
  AnthropicRenderedPrompt,
9
10
  OpenAiRenderedPrompt,
@@ -56,13 +57,12 @@ import type {
56
57
  } from "./types";
57
58
  import { ENV_SLUG_REGEX } from "./types";
58
59
 
59
- // Re-export CacheConfig so callers can import from this module
60
- export type { CacheConfig } from "./cache";
61
60
  export type {
62
61
  ArchivePromptInput,
63
62
  ArchivePromptResult,
64
63
  BlockRenderRecord,
65
64
  ChatMessage,
65
+ ChatRole,
66
66
  CreateProjectPromptLabelInput,
67
67
  CreateProjectPromptLabelResult,
68
68
  CreatePromptInput,
@@ -111,8 +111,6 @@ export type {
111
111
  ResolvedToolCall,
112
112
  ResolvedToolDefinition,
113
113
  RowError,
114
- TextChatMessage,
115
- TextChatRole,
116
114
  UnknownRoleMode,
117
115
  UpdateProjectPromptLabelInput,
118
116
  UpdateProjectPromptLabelResult,
@@ -120,9 +118,7 @@ export type {
120
118
  UpdatePromptResult,
121
119
  } from "./types";
122
120
 
123
- const zResolvedPromptEnvelope = zResolvedPrompt
124
- .partial({ releaseId: true })
125
- .extend({ strategy: z.enum(["single", "weighted"]).optional() });
121
+ const zResolvedPromptEnvelope = zResolvedPrompt.partial({ releaseId: true });
126
122
 
127
123
  function unwrap<T>(envelope: ApiEnvelope<T>): T {
128
124
  if (envelope.data === null || envelope.data === undefined) {
@@ -293,7 +289,6 @@ export interface PromptResourceConfig {
293
289
 
294
290
  export class PromptResource<F extends RenderFormat | undefined = undefined> {
295
291
  private readonly transport: Transport;
296
- private readonly cache: PromptCache;
297
292
  private readonly projectId: string | undefined;
298
293
  private readonly env: string | undefined;
299
294
  private readonly format: RenderFormat | undefined;
@@ -301,24 +296,41 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
301
296
 
302
297
  constructor(
303
298
  transport: Transport,
304
- cacheConfig?: CacheConfig,
305
299
  // `format` is bound to the class generic `F` (not the wider `RenderFormat`)
306
300
  // so the constructed default shape cannot disagree with what `render()`'s
307
301
  // return type (`RenderResult<F>`) promises.
308
302
  config?: Omit<PromptResourceConfig, "format"> & { format?: F },
309
303
  ) {
310
304
  this.transport = transport;
311
- this.cache = new PromptCache(cacheConfig);
312
305
  this.projectId = config?.projectId;
313
306
  this.env = config?.env;
314
307
  this.format = config?.format;
315
308
  this.onUnknownRole = config?.onUnknownRole;
316
309
  }
317
310
 
311
+ /**
312
+ * Resolve the project for a control-plane call: the per-call `projectId`
313
+ * wins, else the client-bound one from `createClient({ projectId })`. Throws
314
+ * if neither is set. Mirrors how `render()` resolves its project, so every
315
+ * method honors the bound `projectId` and callers need not repeat it.
316
+ */
317
+ private resolveProjectId(perCall?: string): string {
318
+ const projectId = perCall ?? this.projectId;
319
+ if (projectId === undefined || projectId.trim() === "") {
320
+ throw new Error(
321
+ "[to11ai/sdk] this call needs a `projectId` — pass it on the call or bind one via createClient({ projectId, ... }).",
322
+ );
323
+ }
324
+ // Trim so a whitespace-padded per-call id normalizes like the client-bound
325
+ // one (resolved via `pick()`), instead of reaching the wire untrimmed.
326
+ return projectId.trim();
327
+ }
328
+
318
329
  // ─── Create ──────────────────────────────────────────────────────────────
319
330
 
320
331
  async create(input: CreatePromptInput): Promise<CreatePromptResult> {
321
- const { projectId, ...body } = input;
332
+ const { projectId: perCallProjectId, ...body } = input;
333
+ const projectId = this.resolveProjectId(perCallProjectId);
322
334
  const envelope = await this.transport.request<CreatePromptResult>(
323
335
  "POST",
324
336
  getProjectPromptsPath(projectId),
@@ -330,9 +342,10 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
330
342
  // ─── List ─────────────────────────────────────────────────────────────────
331
343
 
332
344
  async list(
333
- input: ListPromptsInput,
345
+ input: ListPromptsInput = {},
334
346
  ): Promise<PaginatedResult<ListPromptsResult>> {
335
- const { projectId, ...queryRest } = input;
347
+ const { projectId: perCallProjectId, ...queryRest } = input;
348
+ const projectId = this.resolveProjectId(perCallProjectId);
336
349
  const envelope = await this.transport.request<
337
350
  | ListPromptsResult[]
338
351
  | {
@@ -364,7 +377,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
364
377
  // ─── Get ──────────────────────────────────────────────────────────────────
365
378
 
366
379
  async get(input: GetPromptInput): Promise<GetPromptResult> {
367
- const { projectId, promptId } = input;
380
+ const { projectId: perCallProjectId, promptId } = input;
381
+ const projectId = this.resolveProjectId(perCallProjectId);
368
382
  const envelope = await this.transport.request<GetPromptResult>(
369
383
  "GET",
370
384
  getProjectPromptPath(projectId, promptId),
@@ -375,7 +389,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
375
389
  // ─── Update ───────────────────────────────────────────────────────────────
376
390
 
377
391
  async update(input: UpdatePromptInput): Promise<UpdatePromptResult> {
378
- const { projectId, promptId, ...body } = input;
392
+ const { projectId: perCallProjectId, promptId, ...body } = input;
393
+ const projectId = this.resolveProjectId(perCallProjectId);
379
394
  const envelope = await this.transport.request<UpdatePromptResult>(
380
395
  "PATCH",
381
396
  getProjectPromptPath(projectId, promptId),
@@ -387,7 +402,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
387
402
  // ─── Archive ──────────────────────────────────────────────────────────────
388
403
 
389
404
  async archive(input: ArchivePromptInput): Promise<ArchivePromptResult> {
390
- const { projectId, promptId } = input;
405
+ const { projectId: perCallProjectId, promptId } = input;
406
+ const projectId = this.resolveProjectId(perCallProjectId);
391
407
  const envelope = await this.transport.request<ArchivePromptResult>(
392
408
  "DELETE",
393
409
  getProjectPromptPath(projectId, promptId),
@@ -400,7 +416,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
400
416
  async createVersion(
401
417
  input: CreatePromptVersionInput,
402
418
  ): Promise<CreatePromptVersionResult> {
403
- const { projectId, promptId, ...body } = input;
419
+ const { projectId: perCallProjectId, promptId, ...body } = input;
420
+ const projectId = this.resolveProjectId(perCallProjectId);
404
421
  const envelope = await this.transport.request<CreatePromptVersionResult>(
405
422
  "POST",
406
423
  `${getProjectPromptPath(projectId, promptId)}/versions`,
@@ -414,7 +431,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
414
431
  async listVersions(
415
432
  input: ListPromptVersionsInput,
416
433
  ): Promise<ListPromptVersionsResult> {
417
- const { projectId, promptId } = input;
434
+ const { projectId: perCallProjectId, promptId } = input;
435
+ const projectId = this.resolveProjectId(perCallProjectId);
418
436
  const envelope = await this.transport.request<ListPromptVersionsResult>(
419
437
  "GET",
420
438
  `${getProjectPromptPath(projectId, promptId)}/versions`,
@@ -427,7 +445,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
427
445
  async getVersion(
428
446
  input: GetPromptVersionInput,
429
447
  ): Promise<GetPromptVersionResult> {
430
- const { projectId, promptId, versionNumber } = input;
448
+ const { projectId: perCallProjectId, promptId, versionNumber } = input;
449
+ const projectId = this.resolveProjectId(perCallProjectId);
431
450
  const envelope = await this.transport.request<GetPromptVersionResult>(
432
451
  "GET",
433
452
  `${getProjectPromptPath(projectId, promptId)}/versions/${encodeURIComponent(String(versionNumber))}`,
@@ -438,7 +457,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
438
457
  // ─── Move Label ───────────────────────────────────────────────────────────
439
458
 
440
459
  async moveLabel(input: MovePromptLabelInput): Promise<MovePromptLabelResult> {
441
- const { projectId, promptId, label, ...body } = input;
460
+ const { projectId: perCallProjectId, promptId, label, ...body } = input;
461
+ const projectId = this.resolveProjectId(perCallProjectId);
442
462
  const envelope = await this.transport.request<MovePromptLabelResult>(
443
463
  "PUT",
444
464
  `${getProjectPromptPath(projectId, promptId)}/labels/${encodeURIComponent(label)}`,
@@ -452,7 +472,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
452
472
  async listLabels(
453
473
  input: ListPromptLabelsInput,
454
474
  ): Promise<ListPromptLabelsResult> {
455
- const { projectId, promptId } = input;
475
+ const { projectId: perCallProjectId, promptId } = input;
476
+ const projectId = this.resolveProjectId(perCallProjectId);
456
477
  const envelope = await this.transport.request<ListPromptLabelsResult>(
457
478
  "GET",
458
479
  `${getProjectPromptPath(projectId, promptId)}/labels`,
@@ -499,17 +520,14 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
499
520
 
500
521
  /**
501
522
  * Env-aware resolution → neutral `RenderedPrompt`. Carries the resolve /
502
- * cache / offline-fallback logic; `render()` is the public shaping surface.
523
+ * offline-fallback logic; `render()` is the public shaping surface.
503
524
  *
504
- * - **env→label wire mapping:** the resolve route is label-based
525
+ * - **label wire mapping:** the resolve route is label-based
505
526
  * (`/v1/projects/:projectId/prompts:resolve`, body `labels[]`); the
506
- * resolved env slug is sent as the single resolution label.
507
- * - **No silent fallback (R4):** if no env is resolvable (none at init AND
508
- * none per call) this throws `EnvNotFoundError` CLIENT-side, eagerly,
509
- * before any transport call.
510
- * - **Cache (D8):** caches `single`-strategy releases and weighted releases
511
- * WITH a `subject`; BYPASSES the cache for weighted releases with NO
512
- * `subject` (a uniform-random assignment must not be pinned for the TTL).
527
+ * resolved label is sent as the single resolution label.
528
+ * - **No silent fallback (R4):** if no label is resolvable (none per call
529
+ * AND no client `env` fallback) this throws `MissingLabelError`
530
+ * CLIENT-side, eagerly, before any transport call.
513
531
  */
514
532
  private async resolveToRendered(
515
533
  slug: string,
@@ -521,41 +539,28 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
521
539
  );
522
540
  }
523
541
 
524
- // R2 precedence: per-call env > init-time env. No third source.
525
- const env = options.env ?? this.env;
542
+ // R2 precedence: per-call label > client `env` fallback. No third source.
543
+ const label = options.label ?? this.env;
526
544
 
527
- // R4 / AC4: no env resolvable → throw eagerly, ZERO transport calls.
528
- if (env === undefined) {
529
- throw EnvNotFoundError.missing();
545
+ // R4 / AC4: no label resolvable → throw eagerly, ZERO transport calls.
546
+ if (label === undefined) {
547
+ throw new MissingLabelError();
530
548
  }
531
549
 
532
- // Validate the RESOLVED env (per-call OR init). createClient validates the
533
- // init-time env, but PromptResource has a public constructor that can
534
- // bypass createClient, so re-validate here so a malformed env (e.g.
535
- // "Prod") never reaches the wire as a resolution label (CodeRabbit).
536
- if (!ENV_SLUG_REGEX.test(env)) {
550
+ // Validate the RESOLVED label (per-call OR the client `env` fallback).
551
+ // createClient validates the init-time env, but PromptResource has a
552
+ // public constructor that can bypass createClient, so re-validate here so
553
+ // a malformed label (e.g. "Prod") never reaches the wire (CodeRabbit).
554
+ if (!ENV_SLUG_REGEX.test(label)) {
537
555
  throw new Error(
538
- `[to11ai/sdk] \`env\` must match ${ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(env)}`,
556
+ `[to11ai/sdk] \`label\` must match ${ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(label)}`,
539
557
  );
540
558
  }
541
559
 
542
- const { subject, variables, cache: useCache = true, fallback } = options;
560
+ const { subject, variables, fallback } = options;
543
561
 
544
- const cacheKey = buildRenderCacheKey(
545
- this.projectId,
546
- slug,
547
- env,
548
- subject,
549
- variables,
550
- );
551
-
552
- if (useCache) {
553
- const cached = this.cache.get(cacheKey);
554
- if (cached) return structuredClone(cached);
555
- }
556
-
557
- // env→label mapping: the env slug is the single resolution label.
558
- const body: Record<string, unknown> = { slug, labels: [env] };
562
+ // The resolved label is sent as the single resolution label.
563
+ const body: Record<string, unknown> = { slug, labels: [label] };
559
564
  if (subject !== undefined) body.subject = subject;
560
565
  if (variables !== undefined) body.variables = variables;
561
566
 
@@ -570,31 +575,16 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
570
575
  },
571
576
  );
572
577
  const raw = unwrap(envelope);
573
- const result = normalizeResolvedEnvelope(raw, slug);
574
-
575
- // D8: bypass the cache for a weighted release with NO subject —
576
- // caching a uniform-random assignment would pin one variant for the
577
- // whole TTL. Cache `single` releases and `subject`-keyed weighted
578
- // releases (deterministic, safe to cache). Weighted-ness is inferred
579
- // from `strategy === "weighted"` OR the presence of `variantName`
580
- // (which the server stamps iff weighted) — so a result that carries a
581
- // variant but omits `strategy` is still correctly bypassed rather than
582
- // pinning one random variant for the TTL (Cursor / Greptile).
583
- const isWeighted =
584
- raw.strategy === "weighted" || result.variantName !== undefined;
585
- const cacheable = !(isWeighted && subject === undefined);
586
- if (useCache && cacheable) {
587
- this.cache.set(cacheKey, structuredClone(result));
588
- }
589
-
590
- return result;
578
+ return normalizeResolvedEnvelope(raw, slug);
591
579
  } catch (err) {
592
- // D7: offline fallback is consulted ONLY on a network outage with an
593
- // empty cache. A server `ENV_NOT_FOUND` is a typed application error
594
- // (not a To11aiNetworkError), so it bypasses this branch. R4's
595
- // client-eager throw already happened above the try, so there is no
596
- // network error to trigger it on the env-missing path.
597
- if (err instanceof To11aiNetworkError && fallback !== undefined) {
580
+ // Serve the offline fallback on a TRANSIENT outage — a network error
581
+ // or a 5xx from the control plane. A typed 4xx (not-found / policy /
582
+ // validation) is a real application error and is NOT masked. The
583
+ // client-eager MissingLabelError already threw above the try.
584
+ const transient =
585
+ err instanceof To11aiNetworkError ||
586
+ (err instanceof To11aiApiError && err.statusCode >= 500);
587
+ if (transient && fallback !== undefined) {
598
588
  return liftFallbackToRendered(fallback, slug);
599
589
  }
600
590
  throw err;
@@ -611,9 +601,10 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
611
601
  * affordances). Gated on `prompt:read` server-side.
612
602
  */
613
603
  async listProjectLabels(
614
- input: ListProjectPromptLabelsInput,
604
+ input: ListProjectPromptLabelsInput = {},
615
605
  ): Promise<ListProjectPromptLabelsResult> {
616
- const { projectId, includeArchived } = input;
606
+ const { projectId: perCallProjectId, includeArchived } = input;
607
+ const projectId = this.resolveProjectId(perCallProjectId);
617
608
  const envelope =
618
609
  await this.transport.request<ListProjectPromptLabelsResult>(
619
610
  "GET",
@@ -635,7 +626,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
635
626
  async createProjectLabel(
636
627
  input: CreateProjectPromptLabelInput,
637
628
  ): Promise<CreateProjectPromptLabelResult> {
638
- const { projectId, ...body } = input;
629
+ const { projectId: perCallProjectId, ...body } = input;
630
+ const projectId = this.resolveProjectId(perCallProjectId);
639
631
  const envelope =
640
632
  await this.transport.request<CreateProjectPromptLabelResult>(
641
633
  "POST",
@@ -648,9 +640,10 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
648
640
  async getProjectLabel(
649
641
  input: GetProjectPromptLabelInput,
650
642
  ): Promise<GetProjectPromptLabelResult> {
643
+ const projectId = this.resolveProjectId(input.projectId);
651
644
  const envelope = await this.transport.request<GetProjectPromptLabelResult>(
652
645
  "GET",
653
- getProjectPromptLabelPath(input.projectId, input.label),
646
+ getProjectPromptLabelPath(projectId, input.label),
654
647
  );
655
648
  return unwrap(envelope);
656
649
  }
@@ -666,7 +659,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
666
659
  async updateProjectLabel(
667
660
  input: UpdateProjectPromptLabelInput,
668
661
  ): Promise<UpdateProjectPromptLabelResult> {
669
- const { projectId, label, ...rest } = input;
662
+ const { projectId: perCallProjectId, label, ...rest } = input;
663
+ const projectId = this.resolveProjectId(perCallProjectId);
670
664
  const body: Record<string, unknown> = {};
671
665
  if (rest.isDefault !== undefined) body.isDefault = rest.isDefault;
672
666
  if (rest.description !== undefined) body.description = rest.description;
@@ -692,10 +686,11 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
692
686
  async deleteProjectLabel(
693
687
  input: DeleteProjectPromptLabelInput,
694
688
  ): Promise<DeleteProjectPromptLabelResult> {
689
+ const projectId = this.resolveProjectId(input.projectId);
695
690
  const envelope =
696
691
  await this.transport.request<DeleteProjectPromptLabelResult>(
697
692
  "DELETE",
698
- getProjectPromptLabelPath(input.projectId, input.label),
693
+ getProjectPromptLabelPath(projectId, input.label),
699
694
  );
700
695
  return unwrap(envelope);
701
696
  }
@@ -714,7 +709,7 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
714
709
  const envelope =
715
710
  await this.transport.request<ListProjectPromptLabelUsagesResult>(
716
711
  "GET",
717
- `${getProjectPromptLabelPath(input.projectId, input.label)}/usages`,
712
+ `${getProjectPromptLabelPath(this.resolveProjectId(input.projectId), input.label)}/usages`,
718
713
  );
719
714
  return unwrap(envelope);
720
715
  }
@@ -739,7 +734,9 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
739
734
  async listLabelEvents(
740
735
  input: ListLabelEventsInput,
741
736
  ): Promise<ListLabelEventsResult> {
742
- const projectSeg = encodeURIComponent(input.projectId);
737
+ const projectSeg = encodeURIComponent(
738
+ this.resolveProjectId(input.projectId),
739
+ );
743
740
  let path: string;
744
741
  switch (input.scope) {
745
742
  case "label":
@@ -135,7 +135,7 @@ export function shapeAnthropic(
135
135
  return result;
136
136
  }
137
137
 
138
- /** Shape a fetched prompt for `format`, or return it neutral when unset. */
138
+ /** Shape a rendered prompt for `format`, or return it neutral when unset. */
139
139
  export function shapePrompt(
140
140
  prompt: RenderedPrompt,
141
141
  format: RenderFormat | undefined,
@@ -23,16 +23,14 @@ export type PromptVersion = GenPromptVersion;
23
23
  export type PromptFormat = PromptVersion["format"];
24
24
  export type PromptLabelPointer = GenPromptLabelPointer;
25
25
 
26
- export type TextChatRole = "system" | "user" | "assistant" | "developer";
26
+ export type ChatRole = "system" | "user" | "assistant" | "developer";
27
27
 
28
- export interface TextChatMessage {
29
- role: TextChatRole;
28
+ export interface ChatMessage {
29
+ role: ChatRole;
30
30
  content: string;
31
31
  disabled?: boolean;
32
32
  }
33
33
 
34
- export type ChatMessage = TextChatMessage;
35
-
36
34
  export type RenderedChatMessage = GenRenderedChatPrompt["messages"][number];
37
35
 
38
36
  /** Provider shape for `prompts.render()` output. Omit for the neutral result. */
@@ -86,7 +84,7 @@ export interface PaginatedResult<T> {
86
84
  }
87
85
 
88
86
  export interface CreatePromptInput {
89
- projectId: string;
87
+ projectId?: string;
90
88
  name: string;
91
89
  slug: string;
92
90
  description?: string;
@@ -96,7 +94,7 @@ export interface CreatePromptInput {
96
94
  export type CreatePromptResult = Prompt;
97
95
 
98
96
  export interface ListPromptsInput {
99
- projectId: string;
97
+ projectId?: string;
100
98
  status?: PromptStatus;
101
99
  sortBy?: "createdAt" | "updatedAt" | "name";
102
100
  tags?: string;
@@ -107,14 +105,14 @@ export interface ListPromptsInput {
107
105
  export type ListPromptsResult = Prompt;
108
106
 
109
107
  export interface GetPromptInput {
110
- projectId: string;
108
+ projectId?: string;
111
109
  promptId: string;
112
110
  }
113
111
 
114
112
  export type GetPromptResult = Prompt;
115
113
 
116
114
  export interface UpdatePromptInput {
117
- projectId: string;
115
+ projectId?: string;
118
116
  promptId: string;
119
117
  name?: string;
120
118
  description?: string | null;
@@ -124,14 +122,14 @@ export interface UpdatePromptInput {
124
122
  export type UpdatePromptResult = Prompt;
125
123
 
126
124
  export interface ArchivePromptInput {
127
- projectId: string;
125
+ projectId?: string;
128
126
  promptId: string;
129
127
  }
130
128
 
131
129
  export type ArchivePromptResult = Prompt;
132
130
 
133
131
  export interface CreatePromptVersionInput {
134
- projectId: string;
132
+ projectId?: string;
135
133
  promptId: string;
136
134
  format: PromptFormat;
137
135
  templateJson: unknown;
@@ -143,14 +141,14 @@ export interface CreatePromptVersionInput {
143
141
  export type CreatePromptVersionResult = PromptVersion;
144
142
 
145
143
  export interface ListPromptVersionsInput {
146
- projectId: string;
144
+ projectId?: string;
147
145
  promptId: string;
148
146
  }
149
147
 
150
148
  export type ListPromptVersionsResult = PromptVersion[];
151
149
 
152
150
  export interface GetPromptVersionInput {
153
- projectId: string;
151
+ projectId?: string;
154
152
  promptId: string;
155
153
  versionNumber: number;
156
154
  }
@@ -158,7 +156,7 @@ export interface GetPromptVersionInput {
158
156
  export type GetPromptVersionResult = PromptVersion;
159
157
 
160
158
  export interface MovePromptLabelInput {
161
- projectId: string;
159
+ projectId?: string;
162
160
  promptId: string;
163
161
  label: string;
164
162
  versionId: string;
@@ -168,12 +166,19 @@ export interface MovePromptLabelInput {
168
166
  export type MovePromptLabelResult = PromptLabelPointer;
169
167
 
170
168
  export interface ListPromptLabelsInput {
171
- projectId: string;
169
+ projectId?: string;
172
170
  promptId: string;
173
171
  }
174
172
 
175
173
  export type ListPromptLabelsResult = PromptLabelPointer[];
176
174
 
175
+ /**
176
+ * Slug rule for a `render()` label — lowercase-kebab, 1-63 chars. Env slugs are
177
+ * a special case of a label value, so labels are held to the same strict rule
178
+ * (no uppercase, no underscores): a label is a routing key, and case-insensitive
179
+ * routing keys invite silent "production" vs "Production" mismatches. Intentionally
180
+ * strict — do not loosen.
181
+ */
177
182
  export const ENV_SLUG_REGEX = /^[a-z0-9-]{1,63}$/;
178
183
 
179
184
  export type BlockRenderRecord = Record<string, "rendered" | "dimmed">;
@@ -223,20 +228,18 @@ export interface RenderedPrompt {
223
228
  toolChoice?: NonNullable<ResolvedPrompt["toolChoice"]>;
224
229
  }
225
230
 
226
- export type ResolvedPromptEnvelope = GenResolvedPrompt & {
227
- /** Active release strategy. Used to decide weighted-no-subject cache bypass (D8). */
228
- strategy?: "single" | "weighted";
229
- };
231
+ export type ResolvedPromptEnvelope = GenResolvedPrompt;
230
232
 
231
233
  export interface RenderOptions {
232
- /** Serving environment for this call; overrides the client default. */
233
- env?: string;
234
+ /**
235
+ * Label to resolve this call's prompt version; overrides the client default.
236
+ * When omitted, falls back to the client's `env` (env is a label value).
237
+ */
238
+ label?: string;
234
239
  /** Stable id so a weighted release sends the same caller to the same variant. */
235
240
  subject?: string;
236
241
  variables?: Record<string, unknown>;
237
- /** Set false to bypass the LRU cache for this call. Default: true */
238
- cache?: boolean;
239
- /** Prompt served if the API is unreachable on a network error and cache is empty. */
242
+ /** Prompt served if the API is unreachable on a network error. */
240
243
  fallback?: RenderedPrompt;
241
244
  /** Provider shape for this call's output; overrides the client default. */
242
245
  format?: RenderFormat;
@@ -257,7 +260,7 @@ export type ProjectPromptLabelUsage = GenProjectPromptLabelUsage;
257
260
  export type PromptLabelEvent = GenPromptLabelEvent;
258
261
 
259
262
  export interface ListProjectPromptLabelsInput {
260
- projectId: string;
263
+ projectId?: string;
261
264
  /** Set true to include archived (soft-deleted) entries. Default: false. */
262
265
  includeArchived?: boolean;
263
266
  }
@@ -265,7 +268,7 @@ export interface ListProjectPromptLabelsInput {
265
268
  export type ListProjectPromptLabelsResult = ProjectPromptLabel[];
266
269
 
267
270
  export interface CreateProjectPromptLabelInput {
268
- projectId: string;
271
+ projectId?: string;
269
272
  label: string;
270
273
  isDefault?: boolean;
271
274
  description?: string;
@@ -275,14 +278,14 @@ export interface CreateProjectPromptLabelInput {
275
278
  export type CreateProjectPromptLabelResult = ProjectPromptLabel;
276
279
 
277
280
  export interface GetProjectPromptLabelInput {
278
- projectId: string;
281
+ projectId?: string;
279
282
  label: string;
280
283
  }
281
284
 
282
285
  export type GetProjectPromptLabelResult = ProjectPromptLabel;
283
286
 
284
287
  export interface UpdateProjectPromptLabelInput {
285
- projectId: string;
288
+ projectId?: string;
286
289
  label: string;
287
290
  isDefault?: boolean;
288
291
  description?: string | null;
@@ -292,14 +295,14 @@ export interface UpdateProjectPromptLabelInput {
292
295
  export type UpdateProjectPromptLabelResult = ProjectPromptLabel;
293
296
 
294
297
  export interface DeleteProjectPromptLabelInput {
295
- projectId: string;
298
+ projectId?: string;
296
299
  label: string;
297
300
  }
298
301
 
299
302
  export type DeleteProjectPromptLabelResult = ProjectPromptLabel;
300
303
 
301
304
  export interface ListProjectPromptLabelUsagesInput {
302
- projectId: string;
305
+ projectId?: string;
303
306
  label: string;
304
307
  }
305
308
 
@@ -308,7 +311,7 @@ export type ListProjectPromptLabelUsagesResult = ProjectPromptLabelUsage[];
308
311
  export type ListLabelEventsInput =
309
312
  | {
310
313
  scope: "label";
311
- projectId: string;
314
+ projectId?: string;
312
315
  promptId: string;
313
316
  label: string;
314
317
  page?: number;
@@ -318,7 +321,7 @@ export type ListLabelEventsInput =
318
321
  }
319
322
  | {
320
323
  scope: "prompt";
321
- projectId: string;
324
+ projectId?: string;
322
325
  promptId: string;
323
326
  label?: string;
324
327
  page?: number;
@@ -328,7 +331,7 @@ export type ListLabelEventsInput =
328
331
  }
329
332
  | {
330
333
  scope: "project";
331
- projectId: string;
334
+ projectId?: string;
332
335
  promptId?: string;
333
336
  label?: string;
334
337
  page?: number;