content-agent 0.2.0 → 0.4.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/README.md CHANGED
@@ -64,6 +64,44 @@ const model = contentAgent.agent("my-thread", {
64
64
  });
65
65
  ```
66
66
 
67
+ ## Tools
68
+
69
+ Pass AI SDK tools to `streamText` or `generateText` — tool schemas are forwarded to the agent, and execution happens locally on the client:
70
+
71
+ ```typescript
72
+ import { streamText, tool, stepCountIs } from "ai";
73
+ import { z } from "zod";
74
+
75
+ const result = streamText({
76
+ model: contentAgent.agent("my-thread", { application: { key: "my-studio" } }),
77
+ prompt: `Find all blog posts still in draft and submit them for review. Assign them to anna@example.com.`,
78
+ tools: {
79
+ submit_for_review: tool({
80
+ description: `Submit a document for editorial review. The document must be in draft state.`,
81
+ inputSchema: z.object({
82
+ documentId: z.string().describe("The Sanity document _id"),
83
+ email: z.string().email().describe("Assign a specific reviewer"),
84
+ note: z.string().optional().describe("Note for the reviewer"),
85
+ }),
86
+ execute: async ({ documentId, email, note }) => {
87
+ const res = await fetch(
88
+ "https://workflows.internal.example.com/api/review",
89
+ {
90
+ method: "POST",
91
+ body: JSON.stringify({ documentId, email, note }),
92
+ },
93
+ );
94
+
95
+ const { ticketUrl } = await res.json();
96
+ return { success: true, documentId, ticketUrl };
97
+ },
98
+ }),
99
+ },
100
+ });
101
+ ```
102
+
103
+ The model decides when to call your tools. `stopWhen` controls how many round-trips are allowed between the model and tool execution.
104
+
67
105
  ## Configuration
68
106
 
69
107
  Pass settings to `.agent()` to configure capabilities:
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { LanguageModelV1, LanguageModelV1CallOptions, LanguageModelV1CallWarning, LanguageModelV1FinishReason, LanguageModelV1StreamPart } from "@ai-sdk/provider";
1
+ import { ClientToolDefinition, HeadlessErrorResponse } from "@sanity/agent-types";
2
+ import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3FinishReason, LanguageModelV3GenerateResult, LanguageModelV3StreamPart, LanguageModelV3StreamResult, SharedV3Warning } from "@ai-sdk/provider";
2
3
  /**
3
4
  * This file was auto-generated by openapi-typescript.
4
5
  * Do not make direct changes to the file.
@@ -513,52 +514,46 @@ interface ContentAgentModelConfig {
513
514
  headers: () => Record<string, string | undefined>;
514
515
  fetch?: typeof globalThis.fetch;
515
516
  }
516
- declare class ContentAgentModel implements LanguageModelV1 {
517
- readonly specificationVersion = "v1";
517
+ declare class ContentAgentModel implements LanguageModelV3 {
518
+ readonly specificationVersion: "v3";
518
519
  readonly provider: string;
519
520
  readonly modelId: string;
520
- readonly defaultObjectGenerationMode: undefined;
521
+ readonly supportedUrls: Record<string, RegExp[]>;
521
522
  private readonly config;
522
523
  private readonly threadId;
523
524
  private readonly settings;
524
525
  constructor(threadId: string, settings: AgentSettings | undefined, config: ContentAgentModelConfig);
526
+ /**
527
+ * Converts a LanguageModelV3Prompt into wire messages for the agent API.
528
+ *
529
+ * Handles all message roles:
530
+ * - system: plain text system messages
531
+ * - user: extracts text from content parts
532
+ * - assistant: extracts text and/or tool-call parts
533
+ * - tool: converts tool-result parts for the API
534
+ */
525
535
  private convertPromptToMessages;
536
+ /**
537
+ * Extracts client tool definitions from the model call options.
538
+ * Only the schemas are sent — execute functions stay on the client.
539
+ */
540
+ private extractClientTools;
526
541
  private collectWarnings;
527
542
  private buildRequestBody;
528
543
  private getResponseHeaders;
529
- doGenerate(options: LanguageModelV1CallOptions): Promise<{
530
- text?: string;
531
- finishReason: LanguageModelV1FinishReason;
532
- usage: {
533
- promptTokens: number;
534
- completionTokens: number;
535
- };
536
- rawCall: {
537
- rawPrompt: unknown;
538
- rawSettings: Record<string, unknown>;
539
- };
540
- rawResponse?: {
541
- headers?: Record<string, string>;
542
- };
543
- warnings?: LanguageModelV1CallWarning[];
544
- }>;
545
- doStream(options: LanguageModelV1CallOptions): Promise<{
546
- stream: ReadableStream<LanguageModelV1StreamPart>;
547
- rawCall: {
548
- rawPrompt: unknown;
549
- rawSettings: Record<string, unknown>;
550
- };
551
- rawResponse?: {
552
- headers?: Record<string, string>;
553
- };
554
- warnings?: LanguageModelV1CallWarning[];
555
- }>;
544
+ doGenerate(options: LanguageModelV3CallOptions): Promise<LanguageModelV3GenerateResult>;
545
+ /**
546
+ * Parses a streamed response body into content parts (text and tool calls)
547
+ * and a finish reason. Used by doGenerate to buffer the full response.
548
+ */
549
+ private extractContentFromStream;
550
+ doStream(options: LanguageModelV3CallOptions): Promise<LanguageModelV3StreamResult>;
556
551
  }
557
552
  interface ContentAgentProvider {
558
553
  /**
559
554
  * Creates an agent for a conversation thread.
560
555
  */
561
- agent(threadId: string, settings?: AgentSettings): LanguageModelV1;
556
+ agent(threadId: string, settings?: AgentSettings): LanguageModelV3;
562
557
  /**
563
558
  * Lists available applications for the organization.
564
559
  *
@@ -608,12 +603,35 @@ declare function buildHeaders(configHeaders: Record<string, string | undefined>,
608
603
  declare function buildApiUrl(baseURL: string, apiVersion: string, endpoint: string): string;
609
604
  /**
610
605
  * Custom error class for Sanity Agent API errors.
606
+ * The {@link body} is parsed from the JSON response and follows the
607
+ * {@link HeadlessErrorResponse} shape defined in `@sanity/agent-types`.
611
608
  */
612
609
  declare class SanityAgentError extends Error {
613
610
  readonly status: number;
614
611
  readonly isRetryable: boolean;
612
+ readonly body: Partial<HeadlessErrorResponse>;
615
613
  constructor(status: number, message: string);
614
+ private static parseBody;
616
615
  }
616
+ declare const GENERIC_ERROR_MESSAGE = "Sorry, something went wrong. Please try again.";
617
+ type AgentErrorType = {
618
+ type: 'no_applications';
619
+ } | {
620
+ type: 'application_not_found';
621
+ } | {
622
+ type: 'unknown';
623
+ };
624
+ /**
625
+ * Classifies a caught error from the Content Agent API into a known error type.
626
+ * Useful for clients that need to take different actions per error (e.g. show
627
+ * resource-selection buttons). For the user-facing message, use {@link getAgentErrorMessage}.
628
+ */
629
+ declare function classifyAgentError(error: unknown): AgentErrorType;
630
+ /**
631
+ * Extracts a user-facing error message from an agent API error.
632
+ * Returns the message sent by the API when available, otherwise a generic fallback.
633
+ */
634
+ declare function getAgentErrorMessage(error: unknown): string;
617
635
  interface FetchOptions {
618
636
  url: string;
619
637
  body: unknown;
@@ -634,27 +652,40 @@ declare function extractTextFromParts(content: string | Array<{
634
652
  text?: string;
635
653
  }>): string;
636
654
  /**
637
- * Maps API finish reasons to LanguageModelV1FinishReason.
655
+ * Maps API finish reasons to LanguageModelV3FinishReason.
638
656
  */
639
- declare function mapFinishReason(reason: string | undefined): LanguageModelV1FinishReason;
657
+ declare function mapFinishReason(reason: string | undefined): LanguageModelV3FinishReason;
658
+ type ParsedStreamLine = {
659
+ kind: 'part';
660
+ part: LanguageModelV3StreamPart;
661
+ } | {
662
+ kind: 'finish';
663
+ finishReason: LanguageModelV3FinishReason;
664
+ };
640
665
  /**
641
- * Parses a single line from Vercel AI Data Stream Protocol.
666
+ * Parses a single line from the Vercel AI Data Stream Protocol.
642
667
  * Returns null if the line cannot be parsed or should be ignored.
668
+ *
669
+ * Protocol prefixes:
670
+ * 0:"text" – text content
671
+ * 2:[data] – data array (ignored)
672
+ * 3:"error" – error
673
+ * b:{toolCallId,toolName,args} – client tool call
674
+ * d:{finishReason, usage} – finish message
675
+ * e:{finishReason, usage} – finish step
676
+ *
677
+ * @see https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol
643
678
  */
644
- declare function parseStreamLine(line: string): LanguageModelV1StreamPart | null;
645
- /**
646
- * Parses finish metadata from a stream line.
647
- * Returns the finish reason if found, null otherwise.
648
- */
649
- declare function parseFinishLine(line: string): LanguageModelV1FinishReason | null;
679
+ declare function parseStreamLine(line: string, textContentId: string): ParsedStreamLine | null;
650
680
  /**
651
681
  * Extracts accumulated text from a streaming response body.
652
682
  * Used for simple streaming cases where only the final text is needed.
653
683
  */
654
684
  declare function extractTextFromStream(responseBody: ReadableStream<Uint8Array>): Promise<string>;
655
685
  /**
656
- * Creates a ReadableStream that parses SSE data from a response body.
686
+ * Creates a ReadableStream that parses SSE data from a response body
687
+ * and emits LanguageModelV3 stream parts with proper lifecycle events.
657
688
  */
658
- declare function createSSEStream(responseBody: ReadableStream<Uint8Array>): ReadableStream<LanguageModelV1StreamPart>;
659
- export { type AgentSettings, type Application, type ApplicationResource, type Capabilities, type Capability, type CapabilityPreset, type CapabilityPresetValue, type ChatRequest, type ChatResponse, type Config, ContentAgentModel, type ContentAgentModelConfig, type ContentAgentProvider, type ContentAgentProviderSettings, type ErrorResponse, type Filter, type GenerateOptions, type GenerateRequest, type GenerateResult, type GenerateSettings, type Message, type MessageRole, type OutputFormat, SanityAgentError, type UserMessageContext, type ValidationIssue, buildApiUrl, buildHeaders, type components, createContentAgent, createSSEStream, extractTextFromParts, extractTextFromStream, fetchApi, mapFinishReason, type operations, parseFinishLine, parseStreamLine, type paths };
689
+ declare function createSSEStream(responseBody: ReadableStream<Uint8Array>, warnings: SharedV3Warning[]): ReadableStream<LanguageModelV3StreamPart>;
690
+ export { type AgentErrorType, type AgentSettings, type Application, type ApplicationResource, type Capabilities, type Capability, type CapabilityPreset, type CapabilityPresetValue, type ChatRequest, type ChatResponse, type ClientToolDefinition, type Config, ContentAgentModel, type ContentAgentModelConfig, type ContentAgentProvider, type ContentAgentProviderSettings, type ErrorResponse, type Filter, GENERIC_ERROR_MESSAGE, type GenerateOptions, type GenerateRequest, type GenerateResult, type GenerateSettings, type Message, type MessageRole, type OutputFormat, type ParsedStreamLine, SanityAgentError, type UserMessageContext, type ValidationIssue, buildApiUrl, buildHeaders, classifyAgentError, type components, createContentAgent, createSSEStream, extractTextFromParts, extractTextFromStream, fetchApi, getAgentErrorMessage, mapFinishReason, type operations, parseStreamLine, type paths };
660
691
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/generated/openapi.ts","../src/types.ts","../src/sanity-agent-language-model.ts","../src/sanity-agent-provider.ts","../src/utils.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/generated/openapi.ts","../src/types.ts","../src/sanity-agent-language-model.ts","../src/sanity-agent-provider.ts","../src/utils.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { pickBy, isString } from "es-toolkit";
1
+ import { pickBy, isString, groupBy } from "es-toolkit";
2
+ import { HEADLESS_ERROR_CODES } from "@sanity/agent-types";
2
3
  function buildHeaders(configHeaders, additionalHeaders) {
3
4
  return pickBy(
4
5
  {
@@ -15,10 +16,34 @@ function buildApiUrl(baseURL, apiVersion, endpoint) {
15
16
  class SanityAgentError extends Error {
16
17
  status;
17
18
  isRetryable;
19
+ body;
18
20
  constructor(status, message) {
19
- super(`Sanity Agent API error: ${status} ${message}`), this.name = "SanityAgentError", this.status = status, this.isRetryable = status >= 500;
21
+ super(`Sanity Agent API error: ${status} ${message}`), this.name = "SanityAgentError", this.status = status, this.isRetryable = status >= 500, this.body = SanityAgentError.parseBody(message);
22
+ }
23
+ static parseBody(message) {
24
+ try {
25
+ return JSON.parse(message);
26
+ } catch {
27
+ return {};
28
+ }
29
+ }
30
+ }
31
+ const GENERIC_ERROR_MESSAGE = "Sorry, something went wrong. Please try again.";
32
+ function classifyAgentError(error) {
33
+ if (!(error instanceof SanityAgentError) || error.status !== 400)
34
+ return { type: "unknown" };
35
+ switch (error.body.code) {
36
+ case HEADLESS_ERROR_CODES.NO_COMPATIBLE_APPLICATIONS:
37
+ return { type: "no_applications" };
38
+ case HEADLESS_ERROR_CODES.APPLICATION_NOT_FOUND:
39
+ return { type: "application_not_found" };
40
+ default:
41
+ return { type: "unknown" };
20
42
  }
21
43
  }
44
+ function getAgentErrorMessage(error) {
45
+ return error instanceof SanityAgentError && error.body.message ? error.body.message : GENERIC_ERROR_MESSAGE;
46
+ }
22
47
  async function fetchApi(options) {
23
48
  const response = await (options.fetch ?? globalThis.fetch)(options.url, {
24
49
  method: "POST",
@@ -38,71 +63,116 @@ function extractTextFromParts(content) {
38
63
  ).map((part) => part.text).join(`
39
64
  `);
40
65
  }
41
- function mapFinishReason(reason) {
42
- switch (reason) {
43
- case "stop":
44
- case "length":
45
- case "content-filter":
46
- case "tool-calls":
47
- case "error":
48
- return reason;
49
- default:
50
- return "other";
51
- }
52
- }
53
- function parseStreamLine(line) {
54
- const trimmed = line.trim();
55
- if (!trimmed) return null;
56
- if (trimmed.startsWith("0:"))
57
- try {
58
- const text = JSON.parse(trimmed.slice(2));
59
- if (typeof text == "string")
60
- return { type: "text-delta", textDelta: text };
61
- } catch {
66
+ function createEmptyUsage() {
67
+ return {
68
+ inputTokens: {
69
+ total: 0,
70
+ noCache: void 0,
71
+ cacheRead: void 0,
72
+ cacheWrite: void 0
73
+ },
74
+ outputTokens: {
75
+ total: 0,
76
+ text: void 0,
77
+ reasoning: void 0
62
78
  }
63
- else if (trimmed.startsWith("e:"))
64
- try {
65
- const data = JSON.parse(trimmed.slice(2));
66
- return { type: "error", error: new Error(data.error ?? "Unknown error") };
67
- } catch {
68
- return { type: "error", error: new Error("Unknown streaming error") };
79
+ };
80
+ }
81
+ function mapFinishReason(reason) {
82
+ return { unified: (() => {
83
+ switch (reason) {
84
+ case "stop":
85
+ case "length":
86
+ case "content-filter":
87
+ case "tool-calls":
88
+ case "error":
89
+ return reason;
90
+ default:
91
+ return "other";
69
92
  }
70
- return null;
93
+ })(), raw: reason };
71
94
  }
72
- function parseFinishLine(line) {
95
+ function parseStreamLine(line, textContentId) {
73
96
  const trimmed = line.trim();
74
- if (!trimmed.startsWith("d:")) return null;
75
- try {
76
- const data = JSON.parse(trimmed.slice(2));
77
- return mapFinishReason(data.finishReason);
78
- } catch {
79
- return null;
97
+ if (!trimmed || trimmed.length < 2 || trimmed[1] !== ":") return null;
98
+ const prefix = trimmed[0], payload = trimmed.slice(2);
99
+ switch (prefix) {
100
+ case "0": {
101
+ try {
102
+ const text = JSON.parse(payload);
103
+ if (typeof text == "string")
104
+ return { kind: "part", part: { type: "text-delta", id: textContentId, delta: text } };
105
+ } catch {
106
+ }
107
+ return null;
108
+ }
109
+ case "3":
110
+ try {
111
+ const msg = JSON.parse(payload);
112
+ return {
113
+ kind: "part",
114
+ part: {
115
+ type: "error",
116
+ error: new Error(typeof msg == "string" ? msg : "Unknown error")
117
+ }
118
+ };
119
+ } catch {
120
+ return {
121
+ kind: "part",
122
+ part: { type: "error", error: new Error("Unknown streaming error") }
123
+ };
124
+ }
125
+ case "b":
126
+ try {
127
+ const data = JSON.parse(payload);
128
+ return {
129
+ kind: "part",
130
+ part: {
131
+ type: "tool-call",
132
+ toolCallId: data.toolCallId,
133
+ toolName: data.toolName,
134
+ input: typeof data.args == "string" ? data.args : JSON.stringify(data.args)
135
+ }
136
+ };
137
+ } catch {
138
+ return null;
139
+ }
140
+ case "d":
141
+ case "e":
142
+ try {
143
+ const data = JSON.parse(payload);
144
+ return { kind: "finish", finishReason: mapFinishReason(data.finishReason) };
145
+ } catch {
146
+ return null;
147
+ }
148
+ default:
149
+ return null;
80
150
  }
81
151
  }
82
152
  async function extractTextFromStream(responseBody) {
83
- const reader = responseBody.getReader(), decoder = new TextDecoder();
153
+ const reader = responseBody.getReader(), decoder = new TextDecoder(), textContentId = "extract";
84
154
  let text = "";
85
155
  for (; ; ) {
86
156
  const { done, value } = await reader.read();
87
157
  if (done) break;
88
158
  const chunk = decoder.decode(value, { stream: !0 });
89
159
  for (const line of chunk.split(`
90
- `))
91
- if (line.startsWith("0:"))
92
- try {
93
- const parsed = JSON.parse(line.slice(2));
94
- typeof parsed == "string" && (text += parsed);
95
- } catch {
96
- }
160
+ `)) {
161
+ const parsed = parseStreamLine(line, textContentId);
162
+ parsed?.kind === "part" && parsed.part.type === "text-delta" && (text += parsed.part.delta);
163
+ }
97
164
  }
98
165
  return text;
99
166
  }
100
- function createSSEStream(responseBody) {
101
- let finishReason = "other";
167
+ function createSSEStream(responseBody, warnings) {
168
+ let finishReason = { unified: "other", raw: void 0 };
169
+ const textContentId = crypto.randomUUID();
170
+ let textStarted = !1, hasToolCalls = !1;
102
171
  return new ReadableStream({
103
172
  async start(controller) {
104
173
  const reader = responseBody.getReader(), decoder = new TextDecoder();
105
174
  let buffer = "";
175
+ controller.enqueue({ type: "stream-start", warnings });
106
176
  try {
107
177
  for (; ; ) {
108
178
  const { done, value } = await reader.read();
@@ -112,20 +182,18 @@ function createSSEStream(responseBody) {
112
182
  `);
113
183
  buffer = lines.pop() ?? "";
114
184
  for (const line of lines) {
115
- const parsed = parseStreamLine(line);
116
- parsed && controller.enqueue(parsed);
117
- const finish = parseFinishLine(line);
118
- finish && (finishReason = finish);
185
+ const parsed = parseStreamLine(line, textContentId);
186
+ parsed?.kind === "part" ? (parsed.part.type === "text-delta" && !textStarted && (controller.enqueue({ type: "text-start", id: textContentId }), textStarted = !0), parsed.part.type === "tool-call" && (textStarted && (controller.enqueue({ type: "text-end", id: textContentId }), textStarted = !1), hasToolCalls = !0), controller.enqueue(parsed.part)) : parsed?.kind === "finish" && (finishReason = parsed.finishReason);
119
187
  }
120
188
  }
121
189
  if (buffer.trim()) {
122
- const parsed = parseStreamLine(buffer);
123
- parsed && controller.enqueue(parsed);
190
+ const parsed = parseStreamLine(buffer, textContentId);
191
+ parsed?.kind === "part" ? (parsed.part.type === "text-delta" && !textStarted && (controller.enqueue({ type: "text-start", id: textContentId }), textStarted = !0), parsed.part.type === "tool-call" && (textStarted && (controller.enqueue({ type: "text-end", id: textContentId }), textStarted = !1), hasToolCalls = !0), controller.enqueue(parsed.part)) : parsed?.kind === "finish" && (finishReason = parsed.finishReason);
124
192
  }
125
- controller.enqueue({
193
+ textStarted && controller.enqueue({ type: "text-end", id: textContentId }), hasToolCalls && finishReason.unified !== "tool-calls" && (finishReason = { unified: "tool-calls", raw: finishReason.raw ?? "tool-calls" }), controller.enqueue({
126
194
  type: "finish",
127
195
  finishReason,
128
- usage: { promptTokens: 0, completionTokens: 0 }
196
+ usage: createEmptyUsage()
129
197
  });
130
198
  } catch (error) {
131
199
  controller.enqueue({
@@ -139,7 +207,7 @@ function createSSEStream(responseBody) {
139
207
  });
140
208
  }
141
209
  const UNSUPPORTED_SETTINGS = [
142
- "maxTokens",
210
+ "maxOutputTokens",
143
211
  "temperature",
144
212
  "topP",
145
213
  "topK",
@@ -148,39 +216,96 @@ const UNSUPPORTED_SETTINGS = [
148
216
  "seed"
149
217
  ];
150
218
  class ContentAgentModel {
151
- specificationVersion = "v1";
219
+ specificationVersion = "v3";
152
220
  provider;
153
221
  modelId;
154
- defaultObjectGenerationMode = void 0;
222
+ supportedUrls = {};
155
223
  config;
156
224
  threadId;
157
225
  settings;
158
226
  constructor(threadId, settings, config) {
159
227
  this.threadId = threadId, this.modelId = `content-agent:${threadId}`, this.provider = config.provider, this.settings = settings ?? {}, this.config = config;
160
228
  }
229
+ /**
230
+ * Converts a LanguageModelV3Prompt into wire messages for the agent API.
231
+ *
232
+ * Handles all message roles:
233
+ * - system: plain text system messages
234
+ * - user: extracts text from content parts
235
+ * - assistant: extracts text and/or tool-call parts
236
+ * - tool: converts tool-result parts for the API
237
+ */
161
238
  convertPromptToMessages(prompt) {
162
239
  const messages = [];
163
240
  for (const message of prompt) {
164
- if (message.role === "tool") continue;
165
- const content = message.role === "system" ? message.content : extractTextFromParts(message.content);
166
- content && messages.push({ role: message.role, content });
241
+ if (message.role === "system") {
242
+ message.content && messages.push({ role: "system", content: message.content });
243
+ continue;
244
+ }
245
+ if (message.role === "tool") {
246
+ const toolResults = message.content.filter((part) => part.type === "tool-result").map((part) => {
247
+ const p = part;
248
+ return {
249
+ type: "tool-result",
250
+ toolCallId: p.toolCallId,
251
+ toolName: p.toolName,
252
+ output: p.output && typeof p.output == "object" && "value" in p.output ? p.output.value : p.output
253
+ };
254
+ });
255
+ toolResults.length > 0 && messages.push({ role: "tool", content: toolResults });
256
+ continue;
257
+ }
258
+ if (message.role === "assistant") {
259
+ const content = message.content, grouped = groupBy(content, (p) => p.type), toolCalls = grouped["tool-call"] ?? [], textParts = grouped.text ?? [];
260
+ if (toolCalls.length > 0) {
261
+ const parts = [
262
+ ...textParts.filter((p) => p.text).map((p) => ({ type: "text", text: p.text })),
263
+ ...toolCalls.map((tc) => ({
264
+ type: "tool-call",
265
+ toolCallId: tc.toolCallId,
266
+ toolName: tc.toolName,
267
+ input: tc.input
268
+ }))
269
+ ];
270
+ parts.length > 0 && messages.push({ role: "assistant", content: parts });
271
+ } else {
272
+ const text2 = extractTextFromParts(content);
273
+ text2 && messages.push({ role: "assistant", content: text2 });
274
+ }
275
+ continue;
276
+ }
277
+ const text = extractTextFromParts(message.content);
278
+ text && messages.push({ role: message.role, content: text });
167
279
  }
168
280
  return messages;
169
281
  }
282
+ /**
283
+ * Extracts client tool definitions from the model call options.
284
+ * Only the schemas are sent — execute functions stay on the client.
285
+ */
286
+ extractClientTools(options) {
287
+ if (!options.tools?.length) return;
288
+ const clientTools = options.tools.filter((t) => t.type === "function").map((t) => ({
289
+ type: "function",
290
+ name: t.name,
291
+ description: t.description,
292
+ inputSchema: t.inputSchema
293
+ }));
294
+ return clientTools.length > 0 ? clientTools : void 0;
295
+ }
170
296
  collectWarnings(options) {
171
297
  const warnings = [];
172
- options.mode.type === "regular" && (options.mode.tools?.length && warnings.push({ type: "other", message: "Tools are not supported by Content Agent API" }), options.mode.toolChoice && warnings.push({
173
- type: "other",
174
- message: "Tool choice is not supported by Content Agent API"
175
- }));
176
298
  for (const setting of UNSUPPORTED_SETTINGS)
177
- options[setting] !== void 0 && warnings.push({ type: "unsupported-setting", setting });
178
- return options.stopSequences?.length && warnings.push({ type: "unsupported-setting", setting: "stopSequences" }), warnings;
299
+ options[setting] !== void 0 && warnings.push({ type: "unsupported", feature: setting });
300
+ return options.stopSequences?.length && warnings.push({ type: "unsupported", feature: "stopSequences" }), options.responseFormat && options.responseFormat.type !== "text" && warnings.push({ type: "unsupported", feature: "responseFormat" }), warnings;
179
301
  }
180
302
  buildRequestBody(options, stream) {
303
+ const clientTools = this.extractClientTools(options), toolChoice = options.toolChoice;
181
304
  return {
182
305
  messages: this.convertPromptToMessages(options.prompt),
183
306
  stream,
307
+ ...clientTools && { clientTools },
308
+ ...toolChoice && { toolChoice },
184
309
  ...this.settings.application && { application: this.settings.application },
185
310
  ...this.settings.config && { config: this.settings.config },
186
311
  ...this.settings.format && { format: this.settings.format }
@@ -190,7 +315,7 @@ class ContentAgentModel {
190
315
  return Object.fromEntries(response.headers.entries());
191
316
  }
192
317
  async doGenerate(options) {
193
- const body = this.buildRequestBody(options, !1), warnings = this.collectWarnings(options), response = await fetchApi({
318
+ const body = this.buildRequestBody(options, !0), warnings = this.collectWarnings(options), response = await fetchApi({
194
319
  url: buildApiUrl(
195
320
  this.config.baseURL,
196
321
  this.config.apiVersion,
@@ -201,15 +326,51 @@ class ContentAgentModel {
201
326
  signal: options.abortSignal,
202
327
  fetch: this.config.fetch
203
328
  });
329
+ if (!response.body)
330
+ throw new Error("No response body for generate request");
331
+ const { content, finishReason } = await this.extractContentFromStream(response.body);
204
332
  return {
205
- text: (await response.json()).text ?? "",
206
- finishReason: "stop",
207
- usage: { promptTokens: 0, completionTokens: 0 },
208
- rawCall: { rawPrompt: body.messages, rawSettings: {} },
209
- rawResponse: { headers: this.getResponseHeaders(response) },
333
+ content,
334
+ finishReason,
335
+ usage: createEmptyUsage(),
336
+ request: { body },
337
+ response: { headers: this.getResponseHeaders(response) },
210
338
  warnings
211
339
  };
212
340
  }
341
+ /**
342
+ * Parses a streamed response body into content parts (text and tool calls)
343
+ * and a finish reason. Used by doGenerate to buffer the full response.
344
+ */
345
+ async extractContentFromStream(responseBody) {
346
+ const reader = responseBody.getReader(), decoder = new TextDecoder(), textContentId = "generate";
347
+ let text = "", hasToolCalls = !1;
348
+ const content = [];
349
+ for (; ; ) {
350
+ const { done, value } = await reader.read();
351
+ if (done) break;
352
+ const chunk = decoder.decode(value, { stream: !0 });
353
+ for (const line of chunk.split(`
354
+ `)) {
355
+ const parsed = parseStreamLine(line, textContentId);
356
+ if (parsed?.kind === "part") {
357
+ if (parsed.part.type === "text-delta")
358
+ text += parsed.part.delta;
359
+ else if (parsed.part.type === "tool-call") {
360
+ hasToolCalls = !0;
361
+ const tc = parsed.part;
362
+ content.push({
363
+ type: "tool-call",
364
+ toolCallId: tc.toolCallId,
365
+ toolName: tc.toolName,
366
+ input: tc.input
367
+ });
368
+ }
369
+ }
370
+ }
371
+ }
372
+ return text && content.unshift({ type: "text", text }), { content, finishReason: hasToolCalls ? { unified: "tool-calls", raw: "tool-calls" } : { unified: "stop", raw: void 0 } };
373
+ }
213
374
  async doStream(options) {
214
375
  const body = this.buildRequestBody(options, !0), warnings = this.collectWarnings(options), response = await fetchApi({
215
376
  url: buildApiUrl(
@@ -225,10 +386,9 @@ class ContentAgentModel {
225
386
  if (!response.body)
226
387
  throw new Error("No response body for streaming request");
227
388
  return {
228
- stream: createSSEStream(response.body),
229
- rawCall: { rawPrompt: body.messages, rawSettings: {} },
230
- rawResponse: { headers: this.getResponseHeaders(response) },
231
- warnings
389
+ stream: createSSEStream(response.body, warnings),
390
+ request: { body },
391
+ response: { headers: this.getResponseHeaders(response) }
232
392
  };
233
393
  }
234
394
  }
@@ -278,16 +438,18 @@ function createContentAgent(options) {
278
438
  }
279
439
  export {
280
440
  ContentAgentModel,
441
+ GENERIC_ERROR_MESSAGE,
281
442
  SanityAgentError,
282
443
  buildApiUrl,
283
444
  buildHeaders,
445
+ classifyAgentError,
284
446
  createContentAgent,
285
447
  createSSEStream,
286
448
  extractTextFromParts,
287
449
  extractTextFromStream,
288
450
  fetchApi,
451
+ getAgentErrorMessage,
289
452
  mapFinishReason,
290
- parseFinishLine,
291
453
  parseStreamLine
292
454
  };
293
455
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/utils.ts","../src/sanity-agent-language-model.ts","../src/sanity-agent-provider.ts"],"sourcesContent":["import type { LanguageModelV1FinishReason, LanguageModelV1StreamPart } from '@ai-sdk/provider'\nimport { isString, pickBy } from 'es-toolkit'\n\n/**\n * Builds headers for API requests, filtering out undefined values.\n */\nexport function buildHeaders(\n configHeaders: Record<string, string | undefined>,\n additionalHeaders?: Record<string, string>,\n): Record<string, string> {\n return pickBy(\n {\n 'Content-Type': 'application/json',\n ...additionalHeaders,\n ...configHeaders,\n },\n isString,\n )\n}\n\n/**\n * Constructs the full API URL for an endpoint.\n */\nexport function buildApiUrl(baseURL: string, apiVersion: string, endpoint: string): string {\n return `${baseURL}/${apiVersion}/agent${endpoint}`\n}\n\n/**\n * Custom error class for Sanity Agent API errors.\n */\nexport class SanityAgentError extends Error {\n readonly status: number\n readonly isRetryable: boolean\n\n constructor(status: number, message: string) {\n super(`Sanity Agent API error: ${status} ${message}`)\n this.name = 'SanityAgentError'\n this.status = status\n this.isRetryable = status >= 500\n }\n}\n\nexport interface FetchOptions {\n url: string\n body: unknown\n headers: Record<string, string>\n signal?: AbortSignal\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * Makes a POST request and returns the response.\n * Throws SanityAgentError if the response is not ok.\n */\nexport async function fetchApi(options: FetchOptions): Promise<Response> {\n const fetchFn = options.fetch ?? globalThis.fetch\n\n const response = await fetchFn(options.url, {\n method: 'POST',\n headers: options.headers,\n body: JSON.stringify(options.body),\n signal: options.signal,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new SanityAgentError(response.status, errorText)\n }\n\n return response\n}\n\n/**\n * Extracts text content from message parts (user or assistant).\n */\nexport function extractTextFromParts(\n content: string | Array<{ type: string; text?: string }>,\n): string {\n if (typeof content === 'string') {\n return content\n }\n\n return content\n .filter(\n (part): part is { type: 'text'; text: string } =>\n part.type === 'text' && typeof part.text === 'string',\n )\n .map((part) => part.text)\n .join('\\n')\n}\n\n/**\n * Maps API finish reasons to LanguageModelV1FinishReason.\n */\nexport function mapFinishReason(reason: string | undefined): LanguageModelV1FinishReason {\n switch (reason) {\n case 'stop':\n case 'length':\n case 'content-filter':\n case 'tool-calls':\n case 'error':\n return reason\n default:\n return 'other'\n }\n}\n\n/**\n * Parses a single line from Vercel AI Data Stream Protocol.\n * Returns null if the line cannot be parsed or should be ignored.\n */\nexport function parseStreamLine(line: string): LanguageModelV1StreamPart | null {\n const trimmed = line.trim()\n if (!trimmed) return null\n\n // Format: TYPE:JSON_DATA\n // 0:\"text\" - text content\n // 2:[data] - data array (ignored)\n // d:{finishReason, usage} - finish metadata (handled separately)\n // e:{error} - error\n\n if (trimmed.startsWith('0:')) {\n try {\n const text = JSON.parse(trimmed.slice(2)) as unknown\n if (typeof text === 'string') {\n return { type: 'text-delta', textDelta: text }\n }\n } catch {\n // Ignore parse errors\n }\n } else if (trimmed.startsWith('e:')) {\n try {\n const data = JSON.parse(trimmed.slice(2)) as { error?: string }\n return { type: 'error', error: new Error(data.error ?? 'Unknown error') }\n } catch {\n return { type: 'error', error: new Error('Unknown streaming error') }\n }\n }\n\n return null\n}\n\n/**\n * Parses finish metadata from a stream line.\n * Returns the finish reason if found, null otherwise.\n */\nexport function parseFinishLine(line: string): LanguageModelV1FinishReason | null {\n const trimmed = line.trim()\n if (!trimmed.startsWith('d:')) return null\n\n try {\n const data = JSON.parse(trimmed.slice(2)) as { finishReason?: string }\n return mapFinishReason(data.finishReason)\n } catch {\n return null\n }\n}\n\n/**\n * Extracts accumulated text from a streaming response body.\n * Used for simple streaming cases where only the final text is needed.\n */\nexport async function extractTextFromStream(\n responseBody: ReadableStream<Uint8Array>,\n): Promise<string> {\n const reader = responseBody.getReader()\n const decoder = new TextDecoder()\n let text = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n for (const line of chunk.split('\\n')) {\n if (line.startsWith('0:')) {\n try {\n const parsed = JSON.parse(line.slice(2)) as unknown\n if (typeof parsed === 'string') {\n text += parsed\n }\n } catch {\n // Ignore parse errors\n }\n }\n }\n }\n\n return text\n}\n\n/**\n * Creates a ReadableStream that parses SSE data from a response body.\n */\nexport function createSSEStream(\n responseBody: ReadableStream<Uint8Array>,\n): ReadableStream<LanguageModelV1StreamPart> {\n let finishReason: LanguageModelV1FinishReason = 'other'\n\n return new ReadableStream<LanguageModelV1StreamPart>({\n async start(controller) {\n const reader = responseBody.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n const parsed = parseStreamLine(line)\n if (parsed) {\n controller.enqueue(parsed)\n }\n\n const finish = parseFinishLine(line)\n if (finish) {\n finishReason = finish\n }\n }\n }\n\n // Process remaining buffer\n if (buffer.trim()) {\n const parsed = parseStreamLine(buffer)\n if (parsed) {\n controller.enqueue(parsed)\n }\n }\n\n controller.enqueue({\n type: 'finish',\n finishReason,\n usage: { promptTokens: 0, completionTokens: 0 },\n })\n } catch (error) {\n controller.enqueue({\n type: 'error',\n error: error instanceof Error ? error : new Error(String(error)),\n })\n } finally {\n controller.close()\n }\n },\n })\n}\n","import type {\n LanguageModelV1,\n LanguageModelV1CallOptions,\n LanguageModelV1CallWarning,\n LanguageModelV1FinishReason,\n LanguageModelV1Prompt,\n LanguageModelV1StreamPart,\n} from '@ai-sdk/provider'\nimport type { AgentSettings, Message } from './types'\nimport { buildApiUrl, buildHeaders, createSSEStream, extractTextFromParts, fetchApi } from './utils'\n\nexport interface ContentAgentModelConfig {\n provider: string\n baseURL: string\n apiVersion: string\n organizationId: string\n headers: () => Record<string, string | undefined>\n fetch?: typeof globalThis.fetch\n}\n\nconst UNSUPPORTED_SETTINGS = [\n 'maxTokens',\n 'temperature',\n 'topP',\n 'topK',\n 'frequencyPenalty',\n 'presencePenalty',\n 'seed',\n] as const\n\nexport class ContentAgentModel implements LanguageModelV1 {\n readonly specificationVersion = 'v1'\n readonly provider: string\n readonly modelId: string\n readonly defaultObjectGenerationMode = undefined\n\n private readonly config: ContentAgentModelConfig\n private readonly threadId: string\n private readonly settings: AgentSettings\n\n constructor(\n threadId: string,\n settings: AgentSettings | undefined,\n config: ContentAgentModelConfig,\n ) {\n this.threadId = threadId\n this.modelId = `content-agent:${threadId}`\n this.provider = config.provider\n this.settings = settings ?? {}\n this.config = config\n }\n\n private convertPromptToMessages(prompt: LanguageModelV1Prompt): Message[] {\n const messages: Message[] = []\n\n for (const message of prompt) {\n if (message.role === 'tool') continue\n\n const content =\n message.role === 'system'\n ? message.content\n : extractTextFromParts(message.content as string | Array<{ type: string; text?: string }>)\n\n if (content) {\n messages.push({ role: message.role, content })\n }\n }\n\n return messages\n }\n\n private collectWarnings(options: LanguageModelV1CallOptions): LanguageModelV1CallWarning[] {\n const warnings: LanguageModelV1CallWarning[] = []\n\n if (options.mode.type === 'regular') {\n if (options.mode.tools?.length) {\n warnings.push({ type: 'other', message: 'Tools are not supported by Content Agent API' })\n }\n if (options.mode.toolChoice) {\n warnings.push({\n type: 'other',\n message: 'Tool choice is not supported by Content Agent API',\n })\n }\n }\n\n for (const setting of UNSUPPORTED_SETTINGS) {\n if (options[setting] !== undefined) {\n warnings.push({ type: 'unsupported-setting', setting })\n }\n }\n\n if (options.stopSequences?.length) {\n warnings.push({ type: 'unsupported-setting', setting: 'stopSequences' })\n }\n\n return warnings\n }\n\n private buildRequestBody(options: LanguageModelV1CallOptions, stream: boolean) {\n return {\n messages: this.convertPromptToMessages(options.prompt),\n stream,\n ...(this.settings.application && { application: this.settings.application }),\n ...(this.settings.config && { config: this.settings.config }),\n ...(this.settings.format && { format: this.settings.format }),\n }\n }\n\n private getResponseHeaders(response: Response): Record<string, string> {\n return Object.fromEntries(response.headers.entries())\n }\n\n async doGenerate(options: LanguageModelV1CallOptions): Promise<{\n text?: string\n finishReason: LanguageModelV1FinishReason\n usage: { promptTokens: number; completionTokens: number }\n rawCall: { rawPrompt: unknown; rawSettings: Record<string, unknown> }\n rawResponse?: { headers?: Record<string, string> }\n warnings?: LanguageModelV1CallWarning[]\n }> {\n const body = this.buildRequestBody(options, false)\n const warnings = this.collectWarnings(options)\n\n const response = await fetchApi({\n url: buildApiUrl(\n this.config.baseURL,\n this.config.apiVersion,\n `/${this.config.organizationId}/threads/${this.threadId}`,\n ),\n body,\n headers: buildHeaders(this.config.headers()),\n signal: options.abortSignal,\n fetch: this.config.fetch,\n })\n\n const responseBody = (await response.json()) as { text?: string }\n\n return {\n text: responseBody.text ?? '',\n finishReason: 'stop',\n usage: { promptTokens: 0, completionTokens: 0 },\n rawCall: { rawPrompt: body.messages, rawSettings: {} },\n rawResponse: { headers: this.getResponseHeaders(response) },\n warnings,\n }\n }\n\n async doStream(options: LanguageModelV1CallOptions): Promise<{\n stream: ReadableStream<LanguageModelV1StreamPart>\n rawCall: { rawPrompt: unknown; rawSettings: Record<string, unknown> }\n rawResponse?: { headers?: Record<string, string> }\n warnings?: LanguageModelV1CallWarning[]\n }> {\n const body = this.buildRequestBody(options, true)\n const warnings = this.collectWarnings(options)\n\n const response = await fetchApi({\n url: buildApiUrl(\n this.config.baseURL,\n this.config.apiVersion,\n `/${this.config.organizationId}/threads/${this.threadId}`,\n ),\n body,\n headers: buildHeaders(this.config.headers()),\n signal: options.abortSignal,\n fetch: this.config.fetch,\n })\n\n if (!response.body) {\n throw new Error('No response body for streaming request')\n }\n\n return {\n stream: createSSEStream(response.body),\n rawCall: { rawPrompt: body.messages, rawSettings: {} },\n rawResponse: { headers: this.getResponseHeaders(response) },\n warnings,\n }\n }\n}\n","import type { LanguageModelV1 } from '@ai-sdk/provider'\nimport { ContentAgentModel, type ContentAgentModelConfig } from './sanity-agent-language-model'\nimport type {\n AgentSettings,\n Application,\n ContentAgentProviderSettings,\n GenerateOptions,\n GenerateResult,\n GenerateSettings,\n} from './types'\nimport {\n buildApiUrl,\n buildHeaders,\n extractTextFromStream,\n fetchApi,\n SanityAgentError,\n} from './utils'\n\nconst DEFAULT_BASE_URL = 'https://api.sanity.io'\nconst DEFAULT_API_VERSION = 'v2026-01-01'\n\nexport interface ContentAgentProvider {\n /**\n * Creates an agent for a conversation thread.\n */\n agent(threadId: string, settings?: AgentSettings): LanguageModelV1\n\n /**\n * Lists available applications for the organization.\n *\n * Returns all Sanity Studio applications the authenticated user has access to.\n * Use the `key` from a returned application to target it when creating an agent.\n *\n * @example\n * ```ts\n * const apps = await contentAgent.applications()\n * const app = apps.find((app) => app.title === \"My Studio\")\n *\n * const model = contentAgent.agent(\"my-thread\", {\n * application: { key: app.key },\n * })\n * ```\n */\n applications(): Promise<Application[]>\n\n /**\n * Sends a one-shot prompt to the Content Agent (stateless).\n *\n * Unlike `.agent()`, this does not create or use a conversation thread.\n * Each call is independent with no message history or persistence.\n * Ideal for simple, single-turn interactions.\n *\n * @example\n * ```ts\n * const result = await contentAgent.prompt(\n * { instructions: \"Be concise\" },\n * { message: \"List all authors in my dataset\" }\n * )\n * console.log(result.text)\n * ```\n */\n prompt(settings: GenerateSettings, options: GenerateOptions): Promise<GenerateResult>\n}\n\n/**\n * Creates a Content Agent provider instance.\n */\nexport function createContentAgent(options: ContentAgentProviderSettings): ContentAgentProvider {\n const baseURL = options.baseURL?.replace(/\\/$/, '') ?? DEFAULT_BASE_URL\n const apiVersion = options.apiVersion ?? DEFAULT_API_VERSION\n const { organizationId } = options\n\n const getHeaders = (): Record<string, string | undefined> => ({\n ...(options.token && { Authorization: `Bearer ${options.token}` }),\n ...options.headers,\n })\n\n const config: ContentAgentModelConfig = {\n provider: 'content-agent',\n baseURL,\n apiVersion,\n organizationId,\n headers: getHeaders,\n fetch: options.fetch,\n }\n\n const agent = (threadId: string, settings?: AgentSettings): LanguageModelV1 => {\n return new ContentAgentModel(threadId, settings, config)\n }\n\n const applications = async (): Promise<Application[]> => {\n const fetchFn = options.fetch ?? globalThis.fetch\n const url = buildApiUrl(baseURL, apiVersion, `/${organizationId}/applications`)\n const headers = buildHeaders(getHeaders())\n\n const response = await fetchFn(url, { method: 'GET', headers })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new SanityAgentError(response.status, errorText)\n }\n\n const json = (await response.json()) as { applications?: Application[] }\n return json.applications ?? []\n }\n\n const prompt = async (\n settings: GenerateSettings,\n generateOptions: GenerateOptions,\n ): Promise<GenerateResult> => {\n const body = {\n organizationId,\n message: generateOptions.message,\n stream: generateOptions.stream ?? false,\n ...(settings.application && { application: settings.application }),\n ...(settings.config && { config: settings.config }),\n ...(settings.instructions && { instructions: settings.instructions }),\n ...(settings.format && { format: settings.format }),\n }\n\n const response = await fetchApi({\n url: buildApiUrl(baseURL, apiVersion, '/prompt'),\n body,\n headers: buildHeaders(getHeaders()),\n signal: generateOptions.abortSignal,\n fetch: options.fetch,\n })\n\n if (body.stream) {\n if (!response.body) {\n throw new SanityAgentError(500, 'No response body')\n }\n return { text: await extractTextFromStream(response.body) }\n }\n\n const json = (await response.json()) as { text?: string }\n return { text: json.text ?? '' }\n }\n\n return { agent, applications, prompt }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/utils.ts","../src/sanity-agent-language-model.ts","../src/sanity-agent-provider.ts"],"sourcesContent":["import type {\n LanguageModelV3FinishReason,\n LanguageModelV3StreamPart,\n LanguageModelV3Usage,\n SharedV3Warning,\n} from '@ai-sdk/provider'\nimport { HEADLESS_ERROR_CODES, type HeadlessErrorResponse } from '@sanity/agent-types'\nimport { isString, pickBy } from 'es-toolkit'\n\n/**\n * Builds headers for API requests, filtering out undefined values.\n */\nexport function buildHeaders(\n configHeaders: Record<string, string | undefined>,\n additionalHeaders?: Record<string, string>,\n): Record<string, string> {\n return pickBy(\n {\n 'Content-Type': 'application/json',\n ...additionalHeaders,\n ...configHeaders,\n },\n isString,\n )\n}\n\n/**\n * Constructs the full API URL for an endpoint.\n */\nexport function buildApiUrl(baseURL: string, apiVersion: string, endpoint: string): string {\n return `${baseURL}/${apiVersion}/agent${endpoint}`\n}\n\n/**\n * Custom error class for Sanity Agent API errors.\n * The {@link body} is parsed from the JSON response and follows the\n * {@link HeadlessErrorResponse} shape defined in `@sanity/agent-types`.\n */\nexport class SanityAgentError extends Error {\n readonly status: number\n readonly isRetryable: boolean\n readonly body: Partial<HeadlessErrorResponse>\n\n constructor(status: number, message: string) {\n super(`Sanity Agent API error: ${status} ${message}`)\n this.name = 'SanityAgentError'\n this.status = status\n this.isRetryable = status >= 500\n this.body = SanityAgentError.parseBody(message)\n }\n\n private static parseBody(message: string): Partial<HeadlessErrorResponse> {\n try {\n return JSON.parse(message)\n } catch {\n return {}\n }\n }\n}\n\nexport const GENERIC_ERROR_MESSAGE = 'Sorry, something went wrong. Please try again.'\n\nexport type AgentErrorType =\n | { type: 'no_applications' }\n | { type: 'application_not_found' }\n | { type: 'unknown' }\n\n/**\n * Classifies a caught error from the Content Agent API into a known error type.\n * Useful for clients that need to take different actions per error (e.g. show\n * resource-selection buttons). For the user-facing message, use {@link getAgentErrorMessage}.\n */\nexport function classifyAgentError(error: unknown): AgentErrorType {\n if (!(error instanceof SanityAgentError) || error.status !== 400) {\n return { type: 'unknown' }\n }\n\n switch (error.body.code) {\n case HEADLESS_ERROR_CODES.NO_COMPATIBLE_APPLICATIONS:\n return { type: 'no_applications' }\n case HEADLESS_ERROR_CODES.APPLICATION_NOT_FOUND:\n return { type: 'application_not_found' }\n default:\n return { type: 'unknown' }\n }\n}\n\n/**\n * Extracts a user-facing error message from an agent API error.\n * Returns the message sent by the API when available, otherwise a generic fallback.\n */\nexport function getAgentErrorMessage(error: unknown): string {\n if (error instanceof SanityAgentError && error.body.message) {\n return error.body.message\n }\n return GENERIC_ERROR_MESSAGE\n}\n\nexport interface FetchOptions {\n url: string\n body: unknown\n headers: Record<string, string>\n signal?: AbortSignal\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * Makes a POST request and returns the response.\n * Throws SanityAgentError if the response is not ok.\n */\nexport async function fetchApi(options: FetchOptions): Promise<Response> {\n const fetchFn = options.fetch ?? globalThis.fetch\n\n const response = await fetchFn(options.url, {\n method: 'POST',\n headers: options.headers,\n body: JSON.stringify(options.body),\n signal: options.signal,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new SanityAgentError(response.status, errorText)\n }\n\n return response\n}\n\n/**\n * Extracts text content from message parts (user or assistant).\n */\nexport function extractTextFromParts(\n content: string | Array<{ type: string; text?: string }>,\n): string {\n if (typeof content === 'string') {\n return content\n }\n\n return content\n .filter(\n (part): part is { type: 'text'; text: string } =>\n part.type === 'text' && typeof part.text === 'string',\n )\n .map((part) => part.text)\n .join('\\n')\n}\n\n/**\n * Creates an empty V3 usage object. Our API does not return token counts.\n */\nexport function createEmptyUsage(): LanguageModelV3Usage {\n return {\n inputTokens: {\n total: 0,\n noCache: undefined,\n cacheRead: undefined,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: 0,\n text: undefined,\n reasoning: undefined,\n },\n }\n}\n\n/**\n * Maps API finish reasons to LanguageModelV3FinishReason.\n */\nexport function mapFinishReason(reason: string | undefined): LanguageModelV3FinishReason {\n const unified = (() => {\n switch (reason) {\n case 'stop':\n case 'length':\n case 'content-filter':\n case 'tool-calls':\n case 'error':\n return reason\n default:\n return 'other' as const\n }\n })()\n\n return { unified, raw: reason }\n}\n\nexport type ParsedStreamLine =\n | { kind: 'part'; part: LanguageModelV3StreamPart }\n | { kind: 'finish'; finishReason: LanguageModelV3FinishReason }\n\n/**\n * Parses a single line from the Vercel AI Data Stream Protocol.\n * Returns null if the line cannot be parsed or should be ignored.\n *\n * Protocol prefixes:\n * 0:\"text\" – text content\n * 2:[data] – data array (ignored)\n * 3:\"error\" – error\n * b:{toolCallId,toolName,args} – client tool call\n * d:{finishReason, usage} – finish message\n * e:{finishReason, usage} – finish step\n *\n * @see https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol\n */\nexport function parseStreamLine(line: string, textContentId: string): ParsedStreamLine | null {\n const trimmed = line.trim()\n if (!trimmed || trimmed.length < 2 || trimmed[1] !== ':') return null\n\n const prefix = trimmed[0]\n const payload = trimmed.slice(2)\n\n switch (prefix) {\n case '0': {\n try {\n const text = JSON.parse(payload) as unknown\n if (typeof text === 'string') {\n return { kind: 'part', part: { type: 'text-delta', id: textContentId, delta: text } }\n }\n } catch {\n // Ignore parse errors\n }\n return null\n }\n\n case '3': {\n try {\n const msg = JSON.parse(payload) as unknown\n return {\n kind: 'part',\n part: {\n type: 'error',\n error: new Error(typeof msg === 'string' ? msg : 'Unknown error'),\n },\n }\n } catch {\n return {\n kind: 'part',\n part: { type: 'error', error: new Error('Unknown streaming error') },\n }\n }\n }\n\n case 'b': {\n try {\n const data = JSON.parse(payload) as {\n toolCallId: string\n toolName: string\n args: unknown\n }\n return {\n kind: 'part',\n part: {\n type: 'tool-call',\n toolCallId: data.toolCallId,\n toolName: data.toolName,\n input: typeof data.args === 'string' ? data.args : JSON.stringify(data.args),\n },\n }\n } catch {\n return null\n }\n }\n\n case 'd':\n case 'e': {\n try {\n const data = JSON.parse(payload) as { finishReason?: string }\n return { kind: 'finish', finishReason: mapFinishReason(data.finishReason) }\n } catch {\n return null\n }\n }\n\n default:\n return null\n }\n}\n\n/**\n * Extracts accumulated text from a streaming response body.\n * Used for simple streaming cases where only the final text is needed.\n */\nexport async function extractTextFromStream(\n responseBody: ReadableStream<Uint8Array>,\n): Promise<string> {\n const reader = responseBody.getReader()\n const decoder = new TextDecoder()\n const textContentId = 'extract'\n let text = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n for (const line of chunk.split('\\n')) {\n const parsed = parseStreamLine(line, textContentId)\n if (parsed?.kind === 'part' && parsed.part.type === 'text-delta') {\n text += parsed.part.delta\n }\n }\n }\n\n return text\n}\n\n/**\n * Creates a ReadableStream that parses SSE data from a response body\n * and emits LanguageModelV3 stream parts with proper lifecycle events.\n */\nexport function createSSEStream(\n responseBody: ReadableStream<Uint8Array>,\n warnings: SharedV3Warning[],\n): ReadableStream<LanguageModelV3StreamPart> {\n let finishReason: LanguageModelV3FinishReason = { unified: 'other', raw: undefined }\n const textContentId = crypto.randomUUID()\n let textStarted = false\n let hasToolCalls = false\n\n return new ReadableStream<LanguageModelV3StreamPart>({\n async start(controller) {\n const reader = responseBody.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n\n controller.enqueue({ type: 'stream-start', warnings })\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n const parsed = parseStreamLine(line, textContentId)\n if (parsed?.kind === 'part') {\n if (parsed.part.type === 'text-delta' && !textStarted) {\n controller.enqueue({ type: 'text-start', id: textContentId })\n textStarted = true\n }\n if (parsed.part.type === 'tool-call') {\n // Close any open text block before emitting a tool call\n if (textStarted) {\n controller.enqueue({ type: 'text-end', id: textContentId })\n textStarted = false\n }\n hasToolCalls = true\n }\n controller.enqueue(parsed.part)\n } else if (parsed?.kind === 'finish') {\n finishReason = parsed.finishReason\n }\n }\n }\n\n // Process remaining buffer\n if (buffer.trim()) {\n const parsed = parseStreamLine(buffer, textContentId)\n if (parsed?.kind === 'part') {\n if (parsed.part.type === 'text-delta' && !textStarted) {\n controller.enqueue({ type: 'text-start', id: textContentId })\n textStarted = true\n }\n if (parsed.part.type === 'tool-call') {\n if (textStarted) {\n controller.enqueue({ type: 'text-end', id: textContentId })\n textStarted = false\n }\n hasToolCalls = true\n }\n controller.enqueue(parsed.part)\n } else if (parsed?.kind === 'finish') {\n finishReason = parsed.finishReason\n }\n }\n\n if (textStarted) {\n controller.enqueue({ type: 'text-end', id: textContentId })\n }\n\n // If tool calls were emitted, override finish reason so streamText\n // knows to execute tools and continue with the next step.\n if (hasToolCalls && finishReason.unified !== 'tool-calls') {\n finishReason = { unified: 'tool-calls', raw: finishReason.raw ?? 'tool-calls' }\n }\n\n controller.enqueue({\n type: 'finish',\n finishReason,\n usage: createEmptyUsage(),\n })\n } catch (error) {\n controller.enqueue({\n type: 'error',\n error: error instanceof Error ? error : new Error(String(error)),\n })\n } finally {\n controller.close()\n }\n },\n })\n}\n","import type {\n LanguageModelV3,\n LanguageModelV3CallOptions,\n LanguageModelV3Content,\n LanguageModelV3GenerateResult,\n LanguageModelV3Prompt,\n LanguageModelV3StreamResult,\n SharedV3Warning,\n} from '@ai-sdk/provider'\nimport type { ClientToolDefinition } from '@sanity/agent-types'\nimport { groupBy } from 'es-toolkit'\nimport type { AgentSettings, Message } from './types'\nimport {\n buildApiUrl,\n buildHeaders,\n createEmptyUsage,\n createSSEStream,\n extractTextFromParts,\n fetchApi,\n parseStreamLine,\n} from './utils'\n\nexport interface ContentAgentModelConfig {\n provider: string\n baseURL: string\n apiVersion: string\n organizationId: string\n headers: () => Record<string, string | undefined>\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * Extended message type for the wire format that supports tool calls and tool results\n * in addition to plain text messages.\n */\ntype WireMessage =\n | Message\n | {\n role: 'assistant'\n content: Array<\n | { type: 'text'; text: string }\n | { type: 'tool-call'; toolCallId: string; toolName: string; input: string }\n >\n }\n | {\n role: 'tool'\n content: Array<{\n type: 'tool-result'\n toolCallId: string\n toolName: string\n output: unknown\n }>\n }\n\nconst UNSUPPORTED_SETTINGS = [\n 'maxOutputTokens',\n 'temperature',\n 'topP',\n 'topK',\n 'frequencyPenalty',\n 'presencePenalty',\n 'seed',\n] as const\n\nexport class ContentAgentModel implements LanguageModelV3 {\n readonly specificationVersion = 'v3' as const\n readonly provider: string\n readonly modelId: string\n readonly supportedUrls: Record<string, RegExp[]> = {}\n\n private readonly config: ContentAgentModelConfig\n private readonly threadId: string\n private readonly settings: AgentSettings\n\n constructor(\n threadId: string,\n settings: AgentSettings | undefined,\n config: ContentAgentModelConfig,\n ) {\n this.threadId = threadId\n this.modelId = `content-agent:${threadId}`\n this.provider = config.provider\n this.settings = settings ?? {}\n this.config = config\n }\n\n /**\n * Converts a LanguageModelV3Prompt into wire messages for the agent API.\n *\n * Handles all message roles:\n * - system: plain text system messages\n * - user: extracts text from content parts\n * - assistant: extracts text and/or tool-call parts\n * - tool: converts tool-result parts for the API\n */\n private convertPromptToMessages(prompt: LanguageModelV3Prompt): WireMessage[] {\n const messages: WireMessage[] = []\n\n for (const message of prompt) {\n if (message.role === 'system') {\n if (message.content) {\n messages.push({ role: 'system', content: message.content })\n }\n continue\n }\n\n if (message.role === 'tool') {\n const toolResults = message.content\n .filter((part) => part.type === 'tool-result')\n .map((part) => {\n const p = part as {\n toolCallId: string\n toolName: string\n output: { type: string; value: unknown } | unknown\n }\n return {\n type: 'tool-result' as const,\n toolCallId: p.toolCallId,\n toolName: p.toolName,\n output:\n p.output && typeof p.output === 'object' && 'value' in p.output\n ? (p.output as { value: unknown }).value\n : p.output,\n }\n })\n\n if (toolResults.length > 0) {\n messages.push({ role: 'tool', content: toolResults })\n }\n continue\n }\n\n if (message.role === 'assistant') {\n const content = message.content as Array<{\n type: string\n text?: string\n toolCallId?: string\n toolName?: string\n input?: string\n }>\n\n const grouped = groupBy(content, (p) => p.type)\n const toolCalls = grouped['tool-call'] ?? []\n const textParts = grouped['text'] ?? []\n\n if (toolCalls.length > 0) {\n const parts = [\n ...textParts\n .filter((p) => p.text)\n .map((p) => ({ type: 'text' as const, text: p.text! })),\n ...toolCalls.map((tc) => ({\n type: 'tool-call' as const,\n toolCallId: tc.toolCallId!,\n toolName: tc.toolName!,\n input: tc.input!,\n })),\n ]\n\n if (parts.length > 0) {\n messages.push({ role: 'assistant', content: parts })\n }\n } else {\n const text = extractTextFromParts(content as Array<{ type: string; text?: string }>)\n if (text) {\n messages.push({ role: 'assistant', content: text })\n }\n }\n continue\n }\n\n // user messages\n const text = extractTextFromParts(message.content as Array<{ type: string; text?: string }>)\n if (text) {\n messages.push({ role: message.role, content: text })\n }\n }\n\n return messages\n }\n\n /**\n * Extracts client tool definitions from the model call options.\n * Only the schemas are sent — execute functions stay on the client.\n */\n private extractClientTools(\n options: LanguageModelV3CallOptions,\n ): ClientToolDefinition[] | undefined {\n if (!options.tools?.length) return undefined\n\n const clientTools = options.tools\n .filter((t) => t.type === 'function')\n .map((t) => ({\n type: 'function' as const,\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as Record<string, unknown>,\n }))\n\n return clientTools.length > 0 ? clientTools : undefined\n }\n\n private collectWarnings(options: LanguageModelV3CallOptions): SharedV3Warning[] {\n const warnings: SharedV3Warning[] = []\n\n for (const setting of UNSUPPORTED_SETTINGS) {\n if (options[setting] !== undefined) {\n warnings.push({ type: 'unsupported', feature: setting })\n }\n }\n\n if (options.stopSequences?.length) {\n warnings.push({ type: 'unsupported', feature: 'stopSequences' })\n }\n\n if (options.responseFormat && options.responseFormat.type !== 'text') {\n warnings.push({ type: 'unsupported', feature: 'responseFormat' })\n }\n\n return warnings\n }\n\n private buildRequestBody(options: LanguageModelV3CallOptions, stream: boolean) {\n const clientTools = this.extractClientTools(options)\n const toolChoice = options.toolChoice\n\n return {\n messages: this.convertPromptToMessages(options.prompt),\n stream,\n ...(clientTools && { clientTools }),\n ...(toolChoice && { toolChoice }),\n ...(this.settings.application && { application: this.settings.application }),\n ...(this.settings.config && { config: this.settings.config }),\n ...(this.settings.format && { format: this.settings.format }),\n }\n }\n\n private getResponseHeaders(response: Response): Record<string, string> {\n return Object.fromEntries(response.headers.entries())\n }\n\n async doGenerate(options: LanguageModelV3CallOptions): Promise<LanguageModelV3GenerateResult> {\n // Always request a stream from the backend and buffer the result.\n // This keeps the HTTP connection alive with incremental data, avoiding\n // 504 gateway timeouts on long-running agent requests.\n const body = this.buildRequestBody(options, true)\n const warnings = this.collectWarnings(options)\n\n const response = await fetchApi({\n url: buildApiUrl(\n this.config.baseURL,\n this.config.apiVersion,\n `/${this.config.organizationId}/threads/${this.threadId}`,\n ),\n body,\n headers: buildHeaders(this.config.headers()),\n signal: options.abortSignal,\n fetch: this.config.fetch,\n })\n\n if (!response.body) {\n throw new Error('No response body for generate request')\n }\n\n const { content, finishReason } = await this.extractContentFromStream(response.body)\n\n return {\n content,\n finishReason,\n usage: createEmptyUsage(),\n request: { body },\n response: { headers: this.getResponseHeaders(response) },\n warnings,\n }\n }\n\n /**\n * Parses a streamed response body into content parts (text and tool calls)\n * and a finish reason. Used by doGenerate to buffer the full response.\n */\n private async extractContentFromStream(responseBody: ReadableStream<Uint8Array>): Promise<{\n content: LanguageModelV3Content[]\n finishReason: LanguageModelV3GenerateResult['finishReason']\n }> {\n const reader = responseBody.getReader()\n const decoder = new TextDecoder()\n const textContentId = 'generate'\n let text = ''\n let hasToolCalls = false\n const content: LanguageModelV3Content[] = []\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n for (const line of chunk.split('\\n')) {\n const parsed = parseStreamLine(line, textContentId)\n if (parsed?.kind === 'part') {\n if (parsed.part.type === 'text-delta') {\n text += parsed.part.delta\n } else if (parsed.part.type === 'tool-call') {\n hasToolCalls = true\n const tc = parsed.part as { toolCallId: string; toolName: string; input: string }\n content.push({\n type: 'tool-call',\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n input: tc.input,\n })\n }\n }\n }\n }\n\n if (text) {\n content.unshift({ type: 'text', text })\n }\n\n const finishReason = hasToolCalls\n ? { unified: 'tool-calls' as const, raw: 'tool-calls' }\n : { unified: 'stop' as const, raw: undefined }\n\n return { content, finishReason }\n }\n\n async doStream(options: LanguageModelV3CallOptions): Promise<LanguageModelV3StreamResult> {\n const body = this.buildRequestBody(options, true)\n const warnings = this.collectWarnings(options)\n\n const response = await fetchApi({\n url: buildApiUrl(\n this.config.baseURL,\n this.config.apiVersion,\n `/${this.config.organizationId}/threads/${this.threadId}`,\n ),\n body,\n headers: buildHeaders(this.config.headers()),\n signal: options.abortSignal,\n fetch: this.config.fetch,\n })\n\n if (!response.body) {\n throw new Error('No response body for streaming request')\n }\n\n return {\n stream: createSSEStream(response.body, warnings),\n request: { body },\n response: { headers: this.getResponseHeaders(response) },\n }\n }\n}\n","import type { LanguageModelV3 } from '@ai-sdk/provider'\nimport { ContentAgentModel, type ContentAgentModelConfig } from './sanity-agent-language-model'\nimport type {\n AgentSettings,\n Application,\n ContentAgentProviderSettings,\n GenerateOptions,\n GenerateResult,\n GenerateSettings,\n} from './types'\nimport {\n buildApiUrl,\n buildHeaders,\n extractTextFromStream,\n fetchApi,\n SanityAgentError,\n} from './utils'\n\nconst DEFAULT_BASE_URL = 'https://api.sanity.io'\nconst DEFAULT_API_VERSION = 'v2026-01-01'\n\nexport interface ContentAgentProvider {\n /**\n * Creates an agent for a conversation thread.\n */\n agent(threadId: string, settings?: AgentSettings): LanguageModelV3\n\n /**\n * Lists available applications for the organization.\n *\n * Returns all Sanity Studio applications the authenticated user has access to.\n * Use the `key` from a returned application to target it when creating an agent.\n *\n * @example\n * ```ts\n * const apps = await contentAgent.applications()\n * const app = apps.find((app) => app.title === \"My Studio\")\n *\n * const model = contentAgent.agent(\"my-thread\", {\n * application: { key: app.key },\n * })\n * ```\n */\n applications(): Promise<Application[]>\n\n /**\n * Sends a one-shot prompt to the Content Agent (stateless).\n *\n * Unlike `.agent()`, this does not create or use a conversation thread.\n * Each call is independent with no message history or persistence.\n * Ideal for simple, single-turn interactions.\n *\n * @example\n * ```ts\n * const result = await contentAgent.prompt(\n * { instructions: \"Be concise\" },\n * { message: \"List all authors in my dataset\" }\n * )\n * console.log(result.text)\n * ```\n */\n prompt(settings: GenerateSettings, options: GenerateOptions): Promise<GenerateResult>\n}\n\n/**\n * Creates a Content Agent provider instance.\n */\nexport function createContentAgent(options: ContentAgentProviderSettings): ContentAgentProvider {\n const baseURL = options.baseURL?.replace(/\\/$/, '') ?? DEFAULT_BASE_URL\n const apiVersion = options.apiVersion ?? DEFAULT_API_VERSION\n const { organizationId } = options\n\n const getHeaders = (): Record<string, string | undefined> => ({\n ...(options.token && { Authorization: `Bearer ${options.token}` }),\n ...options.headers,\n })\n\n const config: ContentAgentModelConfig = {\n provider: 'content-agent',\n baseURL,\n apiVersion,\n organizationId,\n headers: getHeaders,\n fetch: options.fetch,\n }\n\n const agent = (threadId: string, settings?: AgentSettings): LanguageModelV3 => {\n return new ContentAgentModel(threadId, settings, config)\n }\n\n const applications = async (): Promise<Application[]> => {\n const fetchFn = options.fetch ?? globalThis.fetch\n const url = buildApiUrl(baseURL, apiVersion, `/${organizationId}/applications`)\n const headers = buildHeaders(getHeaders())\n\n const response = await fetchFn(url, { method: 'GET', headers })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new SanityAgentError(response.status, errorText)\n }\n\n const json = (await response.json()) as { applications?: Application[] }\n return json.applications ?? []\n }\n\n const prompt = async (\n settings: GenerateSettings,\n generateOptions: GenerateOptions,\n ): Promise<GenerateResult> => {\n const body = {\n organizationId,\n message: generateOptions.message,\n stream: generateOptions.stream ?? false,\n ...(settings.application && { application: settings.application }),\n ...(settings.config && { config: settings.config }),\n ...(settings.instructions && { instructions: settings.instructions }),\n ...(settings.format && { format: settings.format }),\n }\n\n const response = await fetchApi({\n url: buildApiUrl(baseURL, apiVersion, '/prompt'),\n body,\n headers: buildHeaders(getHeaders()),\n signal: generateOptions.abortSignal,\n fetch: options.fetch,\n })\n\n if (body.stream) {\n if (!response.body) {\n throw new SanityAgentError(500, 'No response body')\n }\n return { text: await extractTextFromStream(response.body) }\n }\n\n const json = (await response.json()) as { text?: string }\n return { text: json.text ?? '' }\n }\n\n return { agent, applications, prompt }\n}\n"],"names":["text"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "content-agent",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Vercel AI SDK provider for Sanity Content Agent API",
5
5
  "keywords": [
6
6
  "sanity",
@@ -27,9 +27,10 @@
27
27
  "dist"
28
28
  ],
29
29
  "dependencies": {
30
- "@ai-sdk/provider": "^1.1.3",
30
+ "@ai-sdk/provider": "^3.0.0",
31
31
  "@ai-sdk/provider-utils": "^2.2.8",
32
- "es-toolkit": "^1.32.0"
32
+ "es-toolkit": "^1.32.0",
33
+ "@sanity/agent-types": "0.0.79"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@sanity/pkg-utils": "^10.4.1",
@@ -37,11 +38,11 @@
37
38
  "openapi-typescript": "^7.10.1",
38
39
  "typescript": "^5.9.3",
39
40
  "vitest": "^4.0.18",
40
- "@repo/tsconfig": "0.0.0",
41
- "@repo/package.config": "0.0.0"
41
+ "@repo/package.config": "0.0.0",
42
+ "@repo/tsconfig": "0.0.0"
42
43
  },
43
44
  "peerDependencies": {
44
- "ai": ">=4.0.0"
45
+ "ai": ">=6.0.0"
45
46
  },
46
47
  "peerDependenciesMeta": {
47
48
  "ai": {