content-agent 0.0.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1 +1,85 @@
1
1
  # content-agent
2
+
3
+ Vercel AI SDK provider for Sanity Content Agent API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install content-agent ai
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Create a provider instance with your organization ID and token, then use `.agent(threadId)` to get a model for a conversation thread:
14
+
15
+ ```typescript
16
+ import { createContentAgent } from "content-agent";
17
+ import { generateText } from "ai";
18
+
19
+ const contentAgent = createContentAgent({
20
+ organizationId: "your-org-id",
21
+ token: "your-sanity-token",
22
+ });
23
+
24
+ const model = contentAgent.agent("my-thread");
25
+
26
+ const result = await generateText({
27
+ model,
28
+ prompt: "What blog posts do I have?",
29
+ });
30
+
31
+ console.log(result.text);
32
+ ```
33
+
34
+ ## Streaming
35
+
36
+ Use `streamText` from the AI SDK for streaming responses:
37
+
38
+ ```typescript
39
+ import { streamText } from "ai";
40
+
41
+ const { textStream } = streamText({
42
+ model: contentAgent.agent("my-thread"),
43
+ prompt: "Summarize my latest content",
44
+ });
45
+
46
+ for await (const text of textStream) {
47
+ process.stdout.write(text);
48
+ }
49
+ ```
50
+
51
+ ## Applications
52
+
53
+ Each application key uniquely identifies a deployed Sanity Studio workspace. Since multiple studios can share the same project ID and dataset, the application key is used to unambiguously target the right one.
54
+
55
+ List available applications for your organization, then target one by its key:
56
+
57
+ ```typescript
58
+ const apps = await contentAgent.applications();
59
+
60
+ const app = apps.find((app) => app.title === "My Studio");
61
+
62
+ const model = contentAgent.agent("my-thread", {
63
+ application: { key: app.key },
64
+ });
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ Pass settings to `.agent()` to configure capabilities:
70
+
71
+ ```typescript
72
+ const model = contentAgent.agent("my-thread", {
73
+ application: { key: "your-application-key" },
74
+ config: {
75
+ capabilities: {
76
+ read: true,
77
+ write: false,
78
+ },
79
+ },
80
+ });
81
+ ```
82
+
83
+ ## License
84
+
85
+ MIT
@@ -0,0 +1,660 @@
1
+ import { LanguageModelV1, LanguageModelV1CallOptions, LanguageModelV1CallWarning, LanguageModelV1FinishReason, LanguageModelV1StreamPart } from "@ai-sdk/provider";
2
+ /**
3
+ * This file was auto-generated by openapi-typescript.
4
+ * Do not make direct changes to the file.
5
+ */
6
+ type paths = {
7
+ '/{organizationId}/threads/{threadId}': {
8
+ parameters: {
9
+ query?: never;
10
+ header?: never;
11
+ path?: never;
12
+ cookie?: never;
13
+ };
14
+ get?: never;
15
+ put?: never;
16
+ /**
17
+ * Send a chat message
18
+ * @description Send a message to the agent and receive a streaming response.
19
+ * This endpoint manages conversation threads and persists message history.
20
+ */
21
+ post: operations['chat'];
22
+ delete?: never;
23
+ options?: never;
24
+ head?: never;
25
+ patch?: never;
26
+ trace?: never;
27
+ };
28
+ '/prompt': {
29
+ parameters: {
30
+ query?: never;
31
+ header?: never;
32
+ path?: never;
33
+ cookie?: never;
34
+ };
35
+ get?: never;
36
+ put?: never;
37
+ /**
38
+ * Send a one-shot prompt
39
+ * @description Stateless one-shot prompt endpoint. No thread management or message persistence.
40
+ * Ideal for simple, single-turn interactions.
41
+ */
42
+ post: operations['prompt'];
43
+ delete?: never;
44
+ options?: never;
45
+ head?: never;
46
+ patch?: never;
47
+ trace?: never;
48
+ };
49
+ };
50
+ type components = {
51
+ schemas: {
52
+ ChatRequest: {
53
+ /** @description Array of message objects */messages: components['schemas']['Message'][];
54
+ application?: components['schemas']['Application'];
55
+ config?: components['schemas']['Config'];
56
+ /**
57
+ * @description Enable or disable streaming responses
58
+ * @default true
59
+ */
60
+ stream: boolean;
61
+ /**
62
+ * @description Controls how directives in the response are formatted.
63
+ * - markdown (default): Directives are resolved server-side into standard markdown (e.g., links with titles).
64
+ * - directives: Raw directive syntax is preserved (e.g., `::document{id="doc-id"}`), useful for clients that render custom UI components.
65
+ * @default markdown
66
+ * @enum {string}
67
+ */
68
+ format: 'markdown' | 'directives';
69
+ };
70
+ PromptRequest: {
71
+ /**
72
+ * @description Your Sanity organization ID
73
+ * @example abc123
74
+ */
75
+ organizationId: string;
76
+ /**
77
+ * @description The prompt message
78
+ * @example Summarize my latest blog posts
79
+ */
80
+ message: string;
81
+ config?: components['schemas']['Config'];
82
+ /**
83
+ * @description Custom instructions for the agent
84
+ * @example Be concise and use bullet points
85
+ */
86
+ instructions?: string;
87
+ /**
88
+ * @description Controls how directives in the response are formatted.
89
+ * - markdown (default): Directives are resolved server-side into standard markdown.
90
+ * - directives: Raw directive syntax is preserved for clients that render custom UI.
91
+ * @default markdown
92
+ * @enum {string}
93
+ */
94
+ format: 'markdown' | 'directives';
95
+ };
96
+ Message: {
97
+ /**
98
+ * @description The role of the message sender
99
+ * @enum {string}
100
+ */
101
+ role: 'user' | 'assistant' | 'system'; /** @description The message content */
102
+ content: string; /** @description Optional unique identifier for the message */
103
+ id?: string; /** @description Optional name for the message sender */
104
+ name?: string;
105
+ }; /** @description Target Sanity application configuration */
106
+ Application: {
107
+ /**
108
+ * @description Application key in format "projectId.datasetName"
109
+ * @example abc123.production
110
+ */
111
+ key?: string;
112
+ resource?: {
113
+ /**
114
+ * @description Resource ID in format "projectId.datasetName"
115
+ * @example abc123.production
116
+ */
117
+ id?: string;
118
+ /**
119
+ * @description Resource type
120
+ * @enum {string}
121
+ */
122
+ type?: 'dataset';
123
+ };
124
+ /**
125
+ * Format: uri
126
+ * @description Base URL for the Sanity Studio
127
+ * @example https://your-studio.sanity.studio
128
+ */
129
+ intentBaseUrl?: string; /** @description Bundle version */
130
+ bundleVersion?: string; /** @description Application name */
131
+ name?: string; /** @description Application title */
132
+ title?: string; /** @description Schema descriptor ID */
133
+ schemaDescriptorId?: string;
134
+ }; /** @description Agent configuration. Controls behavior, capabilities, and document access. */
135
+ Config: {
136
+ /**
137
+ * @description Custom instruction string to include in the system prompt
138
+ * @example You are a helpful content assistant.
139
+ */
140
+ instruction?: string;
141
+ /**
142
+ * @description Key-value pairs appended to each user message as XML tags.
143
+ * Each key becomes an XML tag with the value as content.
144
+ * Example: { "slack-channel": "#marketing" } renders as <slack-channel>#marketing</slack-channel>
145
+ * @example {
146
+ * "slack-channel": "#marketing",
147
+ * "user-role": "editor"
148
+ * }
149
+ */
150
+ userMessageContext?: {
151
+ [key: string]: string;
152
+ };
153
+ capabilities?: components['schemas']['Capabilities'];
154
+ filter?: components['schemas']['Filter'];
155
+ perspectives?: components['schemas']['Perspectives'];
156
+ };
157
+ /**
158
+ * @description Agent capabilities that control what operations are allowed.
159
+ * Each capability can be a boolean or an object with a preset.
160
+ * @example {
161
+ * "read": true,
162
+ * "write": false
163
+ * }
164
+ */
165
+ Capabilities: {
166
+ read?: components['schemas']['Capability'];
167
+ write?: components['schemas']['Capability'];
168
+ features?: components['schemas']['FeatureOverrides'];
169
+ };
170
+ /**
171
+ * @description Explicit feature overrides applied on top of capability-derived defaults.
172
+ * When not specified, features use values derived from read/write capability presets.
173
+ */
174
+ FeatureOverrides: {
175
+ /**
176
+ * @description Override web search availability.
177
+ * By default, web search is enabled when read capability is 'standard'.
178
+ */
179
+ webSearch?: boolean;
180
+ };
181
+ /**
182
+ * @description A capability can be:
183
+ * - true: Full access (standard preset)
184
+ * - false: No access
185
+ * - { preset: 'minimal' | 'standard' }: Explicit preset selection
186
+ */
187
+ Capability: boolean | components['schemas']['CapabilityPreset'];
188
+ CapabilityPreset: {
189
+ /**
190
+ * @description For read capability:
191
+ * - minimal: Basic queries only (no bulk document analysis)
192
+ * - standard: All read tools including bulk analysis
193
+ *
194
+ * For write capability:
195
+ * - minimal: Basic mutations only (no bulk mutations)
196
+ * - standard: All write tools including bulk mutations
197
+ * @enum {string}
198
+ */
199
+ preset: 'minimal' | 'standard';
200
+ }; /** @description GROQ filter expressions for document access control */
201
+ Filter: {
202
+ /**
203
+ * @description GROQ boolean expression for document visibility.
204
+ * Documents matching this filter are visible to the agent.
205
+ * @example _type in ['post', 'author']
206
+ */
207
+ read?: string;
208
+ /**
209
+ * @description GROQ boolean expression for mutation permissions.
210
+ * Documents matching this filter can be mutated.
211
+ * @example _type == 'post'
212
+ */
213
+ write?: string;
214
+ };
215
+ /**
216
+ * @description Perspective locking configuration.
217
+ * Controls which document perspectives the agent can read from and write to.
218
+ * Values are Sanity perspective IDs: 'drafts', 'published', 'raw', or release/bundle IDs.
219
+ */
220
+ Perspectives: {
221
+ /**
222
+ * @description Allowed read perspectives. When set, query tools can only use these perspectives.
223
+ * If the agent tries to use a disallowed perspective, the request is rejected with an error.
224
+ * @example [
225
+ * "drafts",
226
+ * "published"
227
+ * ]
228
+ */
229
+ read?: string[];
230
+ /**
231
+ * @description The write perspective. When set, new documents are created in this perspective
232
+ * and mutations are restricted to documents whose ID matches it.
233
+ * @example drafts
234
+ */
235
+ write?: string;
236
+ }; /** @description JSON response when streaming is disabled */
237
+ ChatResponse: {
238
+ /** @description The agent's response text */text?: string; /** @description The application key used for this conversation */
239
+ applicationKey?: string;
240
+ };
241
+ ErrorResponse: {
242
+ /**
243
+ * @description Error type
244
+ * @example Invalid request
245
+ */
246
+ error: string;
247
+ /**
248
+ * @description Human-readable error description
249
+ * @example Request body validation failed
250
+ */
251
+ details?: string; /** @description Validation issues (when applicable) */
252
+ issues?: components['schemas']['ValidationIssue'][];
253
+ };
254
+ ValidationIssue: {
255
+ /**
256
+ * @description Error code
257
+ * @example invalid_type
258
+ */
259
+ code?: string;
260
+ /**
261
+ * @description Path to the invalid field
262
+ * @example [
263
+ * "messages"
264
+ * ]
265
+ */
266
+ path?: string[];
267
+ /**
268
+ * @description Error message
269
+ * @example Required
270
+ */
271
+ message?: string;
272
+ };
273
+ };
274
+ responses: {
275
+ /** @description Bad Request - Request validation failed */BadRequest: {
276
+ headers: {
277
+ [name: string]: unknown;
278
+ };
279
+ content: {
280
+ /**
281
+ * @example {
282
+ * "error": "Invalid request",
283
+ * "details": "Request body validation failed",
284
+ * "issues": [
285
+ * {
286
+ * "code": "invalid_type",
287
+ * "path": [
288
+ * "messages"
289
+ * ],
290
+ * "message": "Required"
291
+ * }
292
+ * ]
293
+ * }
294
+ */
295
+ 'application/json': components['schemas']['ErrorResponse'];
296
+ };
297
+ }; /** @description Unauthorized - Missing or invalid authentication token */
298
+ Unauthorized: {
299
+ headers: {
300
+ [name: string]: unknown;
301
+ };
302
+ content: {
303
+ /**
304
+ * @example {
305
+ * "error": "Unauthorized",
306
+ * "details": "Missing authorization token"
307
+ * }
308
+ */
309
+ 'application/json': components['schemas']['ErrorResponse'];
310
+ };
311
+ }; /** @description Internal Server Error */
312
+ InternalServerError: {
313
+ headers: {
314
+ [name: string]: unknown;
315
+ };
316
+ content: {
317
+ /**
318
+ * @example {
319
+ * "error": "Internal Server Error",
320
+ * "details": "An unexpected error occurred"
321
+ * }
322
+ */
323
+ 'application/json': components['schemas']['ErrorResponse'];
324
+ };
325
+ };
326
+ };
327
+ parameters: never;
328
+ requestBodies: never;
329
+ headers: never;
330
+ pathItems: never;
331
+ };
332
+ interface operations {
333
+ chat: {
334
+ parameters: {
335
+ query?: never;
336
+ header?: never;
337
+ path: {
338
+ /**
339
+ * @description Your Sanity organization ID
340
+ * @example abc123
341
+ */
342
+ organizationId: string;
343
+ /**
344
+ * @description Unique identifier for the conversation thread
345
+ * @example my-conversation-thread
346
+ */
347
+ threadId: string;
348
+ };
349
+ cookie?: never;
350
+ };
351
+ requestBody: {
352
+ content: {
353
+ 'application/json': components['schemas']['ChatRequest'];
354
+ };
355
+ };
356
+ responses: {
357
+ /** @description Streaming response */200: {
358
+ headers: {
359
+ 'Content-Type'?: string;
360
+ 'X-Vercel-AI-Data-Stream'?: string;
361
+ [name: string]: unknown;
362
+ };
363
+ content: {
364
+ 'text/event-stream': string; /** @description JSON response when stream is false */
365
+ 'application/json': components['schemas']['ChatResponse'];
366
+ };
367
+ };
368
+ 400: components['responses']['BadRequest'];
369
+ 401: components['responses']['Unauthorized'];
370
+ 500: components['responses']['InternalServerError'];
371
+ };
372
+ };
373
+ prompt: {
374
+ parameters: {
375
+ query?: never;
376
+ header?: never;
377
+ path?: never;
378
+ cookie?: never;
379
+ };
380
+ requestBody: {
381
+ content: {
382
+ 'application/json': components['schemas']['PromptRequest'];
383
+ };
384
+ };
385
+ responses: {
386
+ /** @description Streaming response */200: {
387
+ headers: {
388
+ 'Content-Type'?: string;
389
+ 'X-Vercel-AI-Data-Stream'?: string;
390
+ [name: string]: unknown;
391
+ };
392
+ content: {
393
+ 'text/event-stream': string;
394
+ };
395
+ };
396
+ 400: components['responses']['BadRequest'];
397
+ 401: components['responses']['Unauthorized'];
398
+ 500: components['responses']['InternalServerError'];
399
+ };
400
+ };
401
+ }
402
+ type ChatRequest = components['schemas']['ChatRequest'];
403
+ type GenerateRequest = components['schemas']['PromptRequest'];
404
+ type OutputFormat = ChatRequest['format'];
405
+ type ChatResponse = components['schemas']['ChatResponse'];
406
+ type ErrorResponse = components['schemas']['ErrorResponse'];
407
+ type ValidationIssue = components['schemas']['ValidationIssue'];
408
+ type Message = components['schemas']['Message'];
409
+ type MessageRole = Message['role'];
410
+ type Application = components['schemas']['Application'];
411
+ type ApplicationResource = NonNullable<Application['resource']>;
412
+ type Config = components['schemas']['Config'];
413
+ type Capabilities = components['schemas']['Capabilities'];
414
+ type Capability = components['schemas']['Capability'];
415
+ type CapabilityPreset = components['schemas']['CapabilityPreset'];
416
+ type CapabilityPresetValue = CapabilityPreset['preset'];
417
+ type Filter = components['schemas']['Filter'];
418
+ type UserMessageContext = NonNullable<Config['userMessageContext']>;
419
+ /**
420
+ * Settings for creating a Content Agent provider instance.
421
+ */
422
+ interface ContentAgentProviderSettings {
423
+ /**
424
+ * Your Sanity organization ID.
425
+ */
426
+ organizationId: string;
427
+ /**
428
+ * Base URL for the Sanity API.
429
+ * @default 'https://api.sanity.io'
430
+ */
431
+ baseURL?: string;
432
+ /**
433
+ * API version to use for requests.
434
+ * Should be in format 'vYYYY-MM-DD' (e.g., 'v2026-01-01').
435
+ * @default 'v2026-01-01'
436
+ */
437
+ apiVersion?: string;
438
+ /**
439
+ * Authentication token for the Sanity API.
440
+ * If not provided, requests will be made without authentication
441
+ * (useful for browser contexts where auth may come from cookies).
442
+ */
443
+ token?: string;
444
+ /**
445
+ * Custom headers to include in requests.
446
+ */
447
+ headers?: Record<string, string>;
448
+ /**
449
+ * Custom fetch implementation.
450
+ * Useful for testing or adding middleware.
451
+ */
452
+ fetch?: typeof globalThis.fetch;
453
+ }
454
+ /**
455
+ * Base settings shared between agent and generate requests.
456
+ */
457
+ interface BaseAgentSettings {
458
+ /**
459
+ * Target Sanity application configuration.
460
+ */
461
+ application?: Application;
462
+ /**
463
+ * Agent configuration for controlling behavior, capabilities, and document access.
464
+ */
465
+ config?: Config;
466
+ /**
467
+ * Controls how directives in the response are formatted.
468
+ * - `markdown` (default): Directives are resolved server-side into standard markdown.
469
+ * - `directives`: Raw directive syntax is preserved for clients that render custom UI.
470
+ * @default 'markdown'
471
+ */
472
+ format?: OutputFormat;
473
+ }
474
+ /**
475
+ * Settings for creating an agent instance (per-thread).
476
+ */
477
+ type AgentSettings = BaseAgentSettings;
478
+ /**
479
+ * Settings for one-shot generation requests.
480
+ */
481
+ interface GenerateSettings extends BaseAgentSettings {
482
+ /**
483
+ * Custom instructions for the agent.
484
+ */
485
+ instructions?: string;
486
+ }
487
+ interface GenerateOptions {
488
+ /**
489
+ * The prompt message to send.
490
+ */
491
+ message: string;
492
+ /**
493
+ * Enable streaming response.
494
+ * @default false
495
+ */
496
+ stream?: boolean;
497
+ /**
498
+ * Signal to abort the request.
499
+ */
500
+ abortSignal?: AbortSignal;
501
+ }
502
+ interface GenerateResult {
503
+ /**
504
+ * The generated text response.
505
+ */
506
+ text: string;
507
+ }
508
+ interface ContentAgentModelConfig {
509
+ provider: string;
510
+ baseURL: string;
511
+ apiVersion: string;
512
+ organizationId: string;
513
+ headers: () => Record<string, string | undefined>;
514
+ fetch?: typeof globalThis.fetch;
515
+ }
516
+ declare class ContentAgentModel implements LanguageModelV1 {
517
+ readonly specificationVersion = "v1";
518
+ readonly provider: string;
519
+ readonly modelId: string;
520
+ readonly defaultObjectGenerationMode: undefined;
521
+ private readonly config;
522
+ private readonly threadId;
523
+ private readonly settings;
524
+ constructor(threadId: string, settings: AgentSettings | undefined, config: ContentAgentModelConfig);
525
+ private convertPromptToMessages;
526
+ private collectWarnings;
527
+ private buildRequestBody;
528
+ 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
+ }>;
556
+ }
557
+ interface ContentAgentProvider {
558
+ /**
559
+ * Creates an agent for a conversation thread.
560
+ */
561
+ agent(threadId: string, settings?: AgentSettings): LanguageModelV1;
562
+ /**
563
+ * Lists available applications for the organization.
564
+ *
565
+ * Returns all Sanity Studio applications the authenticated user has access to.
566
+ * Use the `key` from a returned application to target it when creating an agent.
567
+ *
568
+ * @example
569
+ * ```ts
570
+ * const apps = await contentAgent.applications()
571
+ * const app = apps.find((app) => app.title === "My Studio")
572
+ *
573
+ * const model = contentAgent.agent("my-thread", {
574
+ * application: { key: app.key },
575
+ * })
576
+ * ```
577
+ */
578
+ applications(): Promise<Application[]>;
579
+ /**
580
+ * Sends a one-shot prompt to the Content Agent (stateless).
581
+ *
582
+ * Unlike `.agent()`, this does not create or use a conversation thread.
583
+ * Each call is independent with no message history or persistence.
584
+ * Ideal for simple, single-turn interactions.
585
+ *
586
+ * @example
587
+ * ```ts
588
+ * const result = await contentAgent.prompt(
589
+ * { instructions: "Be concise" },
590
+ * { message: "List all authors in my dataset" }
591
+ * )
592
+ * console.log(result.text)
593
+ * ```
594
+ */
595
+ prompt(settings: GenerateSettings, options: GenerateOptions): Promise<GenerateResult>;
596
+ }
597
+ /**
598
+ * Creates a Content Agent provider instance.
599
+ */
600
+ declare function createContentAgent(options: ContentAgentProviderSettings): ContentAgentProvider;
601
+ /**
602
+ * Builds headers for API requests, filtering out undefined values.
603
+ */
604
+ declare function buildHeaders(configHeaders: Record<string, string | undefined>, additionalHeaders?: Record<string, string>): Record<string, string>;
605
+ /**
606
+ * Constructs the full API URL for an endpoint.
607
+ */
608
+ declare function buildApiUrl(baseURL: string, apiVersion: string, endpoint: string): string;
609
+ /**
610
+ * Custom error class for Sanity Agent API errors.
611
+ */
612
+ declare class SanityAgentError extends Error {
613
+ readonly status: number;
614
+ readonly isRetryable: boolean;
615
+ constructor(status: number, message: string);
616
+ }
617
+ interface FetchOptions {
618
+ url: string;
619
+ body: unknown;
620
+ headers: Record<string, string>;
621
+ signal?: AbortSignal;
622
+ fetch?: typeof globalThis.fetch;
623
+ }
624
+ /**
625
+ * Makes a POST request and returns the response.
626
+ * Throws SanityAgentError if the response is not ok.
627
+ */
628
+ declare function fetchApi(options: FetchOptions): Promise<Response>;
629
+ /**
630
+ * Extracts text content from message parts (user or assistant).
631
+ */
632
+ declare function extractTextFromParts(content: string | Array<{
633
+ type: string;
634
+ text?: string;
635
+ }>): string;
636
+ /**
637
+ * Maps API finish reasons to LanguageModelV1FinishReason.
638
+ */
639
+ declare function mapFinishReason(reason: string | undefined): LanguageModelV1FinishReason;
640
+ /**
641
+ * Parses a single line from Vercel AI Data Stream Protocol.
642
+ * Returns null if the line cannot be parsed or should be ignored.
643
+ */
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;
650
+ /**
651
+ * Extracts accumulated text from a streaming response body.
652
+ * Used for simple streaming cases where only the final text is needed.
653
+ */
654
+ declare function extractTextFromStream(responseBody: ReadableStream<Uint8Array>): Promise<string>;
655
+ /**
656
+ * Creates a ReadableStream that parses SSE data from a response body.
657
+ */
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 };
660
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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":";;;AAKA;;KAAY,KAAA;EACV,sCAAA;IACE,UAAA;MACE,KAAA;MACA,MAAA;MACA,IAAA;MACA,MAAA;IAAA;IAEF,GAAA;IACA,GAAA;IAMA;;;;;IAAA,IAAA,EAAM,UAAA;IACN,MAAA;IACA,OAAA;IACA,IAAA;IACA,KAAA;IACA,KAAA;EAAA;EAEF,SAAA;IACE,UAAA;MACE,KAAA;MACA,MAAA;MACA,IAAA;MACA,MAAA;IAAA;IAEF,GAAA;IACA,GAAA;IAWA;;;AAIJ;;IATI,IAAA,EAAM,UAAA;IACN,MAAA;IACA,OAAA;IACA,IAAA;IACA,KAAA;IACA,KAAA;EAAA;AAAA;AAAA,KAIQ,UAAA;EACV,OAAA;IACE,WAAA;MAuNW,4CArNT,QAAA,EAAU,UAAA;MACV,WAAA,GAAc,UAAA;MACd,MAAA,GAAS,UAAA;MA6RuB;;;;MAxRhC,MAAA;MAPU;;;;;;;MAeV,MAAA;IAAA;IAEF,aAAA;MAWE;;;;MANA,cAAA;MA0BA;;;;MArBA,OAAA;MACA,MAAA,GAAS,UAAA;MAmCT;;;;MA9BA,YAAA;MAmDA;;;;;;;MA3CA,MAAA;IAAA;IAEF,OAAA;MAmEW;;;;MA9DT,IAAA,mCA0EO;MAxEP,OAAA,UAyEQ;MAvER,EAAA,WAwEW;MAtEX,IAAA;IAAA,GAyFF;IAtFA,WAAA;MAuFA;;;;MAlFE,GAAA;MACA,QAAA;QA2HA;;;;QAtHE,EAAA;QAqIJ;;;;QAhII,IAAA;MAAA;MAmJF;;;;;MA5IA,aAAA,WA+JG;MA7JH,aAAA,WA+KE;MA7KF,IAAA,WAiLF;MA/KE,KAAA,WAiLG;MA/KH,kBAAA;IAAA,GAwLsB;IArLxB,MAAA;MA0LE;;;;MArLA,WAAA;MAmMJ;;;;;;AAMF;;;MA/LM,kBAAA;QAAA,CACG,GAAA;MAAA;MAEH,YAAA,GAAe,UAAA;MACf,MAAA,GAAS,UAAA;MACT,YAAA,GAAe,UAAA;IAAA;IA8PV;;;;;;;;IApPP,YAAA;MACE,IAAA,GAAO,UAAA;MACP,KAAA,GAAQ,UAAA;MACR,QAAA,GAAW,UAAA;IAAA;IAiMX;;;;IA3LF,gBAAA;MAkMI;;;;MA7LF,SAAA;IAAA;IAqMI;;;;;;IA7LN,UAAA,YAAsB,UAAA;IACtB,gBAAA;MAoMF;;;;;;;;;;MAzLI,MAAA;IAAA,GAwME;IArMJ,MAAA;MAuMM;;;;;MAjMJ,IAAA;MAyMA;;;;;MAnMA,KAAA;IAAA;IC1NM;;;;;IDiOR,YAAA;MChOuB;;;;AAE3B;;;;MDuOM,IAAA;MCpOM;;;;;MD0ON,KAAA;IAAA,GCzOsB;ID4OxB,YAAA;MC5OkC,6CD8OhC,IAAA,WC7OqB;MD+OrB,cAAA;IAAA;IAEF,aAAA;MC9OQ;;;;MDmPN,KAAA;MClPM;;;;MDuPN,OAAA,WCpPM;MDsPN,MAAA,GAAS,UAAA;IAAA;IAEX,eAAA;MCxPgC;AACpC;;;MD4PM,IAAA;MC5PmD;AAGzD;;;;;MDgQM,IAAA;MC/PkB;;;;MDoQlB,OAAA;IAAA;EAAA;EAGJ,SAAA;ICtQiC,2DDwQ/B,UAAA;MACE,OAAA;QAAA,CACG,IAAA;MAAA;MAEH,OAAA;QC1QM;;;;;AACZ;;;;;AACA;;;;;QDwRQ,kBAAA,EAAoB,UAAA;MAAA;IAAA,GChPK;IDoP7B,YAAA;MACE,OAAA;QAAA,CACG,IAAA;MAAA;MAEH,OAAA;QC9PM;;;;;;QDqQJ,kBAAA,EAAoB,UAAA;MAAA;IAAA,GCrPZ;IDyPZ,mBAAA;MACE,OAAA;QAAA,CACG,IAAA;MAAA;MAEH,OAAA;QC7PU;;;;;;QDoQR,kBAAA,EAAoB,UAAA;MAAA;IAAA;EAAA;EAI1B,UAAA;EACA,aAAA;EACA,OAAA;EACA,SAAA;AAAA;AAAA,UAGe,UAAA;EACf,IAAA;IACE,UAAA;MACE,KAAA;MACA,MAAA;MACA,IAAA;QC1OJ;;;;QD+OM,cAAA;QCvOS;;;;QD4OT,QAAA;MAAA;MAEF,MAAA;IAAA;IAEF,WAAA;MACE,OAAA;QACE,kBAAA,EAAoB,UAAA;MAAA;IAAA;IAGxB,SAAA;MEvWF,sCFyWI,GAAA;QACE,OAAA;UACE,cAAA;UACA,yBAAA;UAAA,CACC,IAAA;QAAA;QAEH,OAAA;UACE,mBAAA,UElWqB;UFoWrB,kBAAA,EAAoB,UAAA;QAAA;MAAA;MAGxB,GAAA,EAAK,UAAA;MACL,GAAA,EAAK,UAAA;MACL,GAAA,EAAK,UAAA;IAAA;EAAA;EAGT,MAAA;IACE,UAAA;MACE,KAAA;MACA,MAAA;MACA,IAAA;MACA,MAAA;IAAA;IAEF,WAAA;MACE,OAAA;QACE,kBAAA,EAAoB,UAAA;MAAA;IAAA;IAGxB,SAAA;MErXO,sCFuXL,GAAA;QACE,OAAA;UACE,cAAA;UACA,yBAAA;UAAA,CACC,IAAA;QAAA;QAEH,OAAA;UACE,mBAAA;QAAA;MAAA;MAGJ,GAAA,EAAK,UAAA;MACL,GAAA,EAAK,UAAA;MACL,GAAA,EAAK,UAAA;IAAA;EAAA;AAAA;AAAA,KC9ZC,WAAA,GAAc,UAAA;AAAA,KACd,eAAA,GAAkB,UAAA;AAAA,KAElB,YAAA,GAAe,WAAA;AAAA,KAGf,YAAA,GAAe,UAAA;AAAA,KACf,aAAA,GAAgB,UAAA;AAAA,KAChB,eAAA,GAAkB,UAAA;AAAA,KAGlB,OAAA,GAAU,UAAA;AAAA,KACV,WAAA,GAAc,OAAA;AAAA,KAGd,WAAA,GAAc,UAAA;AAAA,KACd,mBAAA,GAAsB,WAAA,CAAY,WAAA;AAAA,KAGlC,MAAA,GAAS,UAAA;AAAA,KACT,YAAA,GAAe,UAAA;AAAA,KACf,UAAA,GAAa,UAAA;AAAA,KACb,gBAAA,GAAmB,UAAA;AAAA,KACnB,qBAAA,GAAwB,gBAAA;AAAA,KACxB,MAAA,GAAS,UAAA;AAAA,KACT,kBAAA,GAAqB,WAAA,CAAY,MAAA;;;;UAK5B,4BAAA;EDLX;;;ECSJ,cAAA;EDCE;;;;ECKF,OAAA;EDDE;;;;AAKJ;ECGE,UAAA;;;;;;EAOA,KAAA;EDwGa;;;ECnGb,OAAA,GAAU,MAAA;EDiHK;;;;EC3Gf,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;UAMX,iBAAA;EDvBX;;;EC2BJ,WAAA,GAAc,WAAA;EDzBV;;;EC8BJ,MAAA,GAAS,MAAA;EDfP;;;;;;ECuBF,MAAA,GAAS,YAAA;AAAA;;;;KAMC,aAAA,GAAgB,iBAAA;;;;UAKX,gBAAA,SAAyB,iBAAA;EDsBlC;;;EClBN,YAAA;AAAA;AAAA,UAGe,eAAA;EDiCb;;;EC7BF,OAAA;ED+CI;;;;ECzCJ,MAAA;ED2CmB;;;ECtCnB,WAAA,GAAc,WAAA;AAAA;AAAA,UAGC,cAAA;EDgDX;;;EC5CJ,IAAA;AAAA;AAAA,UC3He,uBAAA;EACf,QAAA;EACA,OAAA;EACA,UAAA;EACA,cAAA;EACA,OAAA,QAAe,MAAA;EACf,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;AAAA,cAaf,iBAAA,YAA6B,eAAA;EAAA,SAC/B,oBAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA;EAAA,SACA,2BAAA;EAAA,iBAEQ,MAAA;EAAA,iBACA,QAAA;EAAA,iBACA,QAAA;cAGf,QAAA,UACA,QAAA,EAAU,aAAA,cACV,MAAA,EAAQ,uBAAA;EAAA,QASF,uBAAA;EAAA,QAmBA,eAAA;EAAA,QA4BA,gBAAA;EAAA,QAUA,kBAAA;EAIF,UAAA,CAAW,OAAA,EAAS,0BAAA,GAA6B,OAAA;IACrD,IAAA;IACA,YAAA,EAAc,2BAAA;IACd,KAAA;MAAS,YAAA;MAAsB,gBAAA;IAAA;IAC/B,OAAA;MAAW,SAAA;MAAoB,WAAA,EAAa,MAAA;IAAA;IAC5C,WAAA;MAAgB,OAAA,GAAU,MAAA;IAAA;IAC1B,QAAA,GAAW,0BAAA;EAAA;EA6BP,QAAA,CAAS,OAAA,EAAS,0BAAA,GAA6B,OAAA;IACnD,MAAA,EAAQ,cAAA,CAAe,yBAAA;IACvB,OAAA;MAAW,SAAA;MAAoB,WAAA,EAAa,MAAA;IAAA;IAC5C,WAAA;MAAgB,OAAA,GAAU,MAAA;IAAA;IAC1B,QAAA,GAAW,0BAAA;EAAA;AAAA;AAAA,UCnIE,oBAAA;EHhBA;;;EGoBf,KAAA,CAAM,QAAA,UAAkB,QAAA,GAAW,aAAA,GAAgB,eAAA;EHlBjD;;;;;;;;;;;;;;;;EGoCF,YAAA,IAAgB,OAAA,CAAQ,WAAA;EHbpB;;;;;;;;;;;;;;AAoBN;;EGWE,MAAA,CAAO,QAAA,EAAU,gBAAA,EAAkB,OAAA,EAAS,eAAA,GAAkB,OAAA,CAAQ,cAAA;AAAA;;;;iBAMxD,kBAAA,CAAmB,OAAA,EAAS,4BAAA,GAA+B,oBAAA;;AH9D3E;;iBICgB,YAAA,CACd,aAAA,EAAe,MAAA,8BACf,iBAAA,GAAoB,MAAA,mBACnB,MAAA;;;;iBAca,WAAA,CAAY,OAAA,UAAiB,UAAA,UAAoB,QAAA;;;;cAOpD,gBAAA,SAAyB,KAAA;EAAA,SAC3B,MAAA;EAAA,SACA,WAAA;cAEG,MAAA,UAAgB,OAAA;AAAA;AAAA,UAQb,YAAA;EACf,GAAA;EACA,IAAA;EACA,OAAA,EAAS,MAAA;EACT,MAAA,GAAS,WAAA;EACT,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;iBAON,QAAA,CAAS,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,QAAA;;;;iBAqB/C,oBAAA,CACd,OAAA,WAAkB,KAAA;EAAQ,IAAA;EAAc,IAAA;AAAA;;;AJ1B1C;iBI4CgB,eAAA,CAAgB,MAAA,uBAA6B,2BAAA;;;;;iBAiB7C,eAAA,CAAgB,IAAA,WAAe,yBAAA;;;;;iBAmC/B,eAAA,CAAgB,IAAA,WAAe,2BAAA;;;;;iBAgBzB,qBAAA,CACpB,YAAA,EAAc,cAAA,CAAe,UAAA,IAC5B,OAAA;;;;iBA8Ba,eAAA,CACd,YAAA,EAAc,cAAA,CAAe,UAAA,IAC5B,cAAA,CAAe,yBAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,293 @@
1
+ import { pickBy, isString } from "es-toolkit";
2
+ function buildHeaders(configHeaders, additionalHeaders) {
3
+ return pickBy(
4
+ {
5
+ "Content-Type": "application/json",
6
+ ...additionalHeaders,
7
+ ...configHeaders
8
+ },
9
+ isString
10
+ );
11
+ }
12
+ function buildApiUrl(baseURL, apiVersion, endpoint) {
13
+ return `${baseURL}/${apiVersion}/agent${endpoint}`;
14
+ }
15
+ class SanityAgentError extends Error {
16
+ status;
17
+ isRetryable;
18
+ constructor(status, message) {
19
+ super(`Sanity Agent API error: ${status} ${message}`), this.name = "SanityAgentError", this.status = status, this.isRetryable = status >= 500;
20
+ }
21
+ }
22
+ async function fetchApi(options) {
23
+ const response = await (options.fetch ?? globalThis.fetch)(options.url, {
24
+ method: "POST",
25
+ headers: options.headers,
26
+ body: JSON.stringify(options.body),
27
+ signal: options.signal
28
+ });
29
+ if (!response.ok) {
30
+ const errorText = await response.text();
31
+ throw new SanityAgentError(response.status, errorText);
32
+ }
33
+ return response;
34
+ }
35
+ function extractTextFromParts(content) {
36
+ return typeof content == "string" ? content : content.filter(
37
+ (part) => part.type === "text" && typeof part.text == "string"
38
+ ).map((part) => part.text).join(`
39
+ `);
40
+ }
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 {
62
+ }
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") };
69
+ }
70
+ return null;
71
+ }
72
+ function parseFinishLine(line) {
73
+ 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;
80
+ }
81
+ }
82
+ async function extractTextFromStream(responseBody) {
83
+ const reader = responseBody.getReader(), decoder = new TextDecoder();
84
+ let text = "";
85
+ for (; ; ) {
86
+ const { done, value } = await reader.read();
87
+ if (done) break;
88
+ const chunk = decoder.decode(value, { stream: !0 });
89
+ 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
+ }
97
+ }
98
+ return text;
99
+ }
100
+ function createSSEStream(responseBody) {
101
+ let finishReason = "other";
102
+ return new ReadableStream({
103
+ async start(controller) {
104
+ const reader = responseBody.getReader(), decoder = new TextDecoder();
105
+ let buffer = "";
106
+ try {
107
+ for (; ; ) {
108
+ const { done, value } = await reader.read();
109
+ if (done) break;
110
+ buffer += decoder.decode(value, { stream: !0 });
111
+ const lines = buffer.split(`
112
+ `);
113
+ buffer = lines.pop() ?? "";
114
+ for (const line of lines) {
115
+ const parsed = parseStreamLine(line);
116
+ parsed && controller.enqueue(parsed);
117
+ const finish = parseFinishLine(line);
118
+ finish && (finishReason = finish);
119
+ }
120
+ }
121
+ if (buffer.trim()) {
122
+ const parsed = parseStreamLine(buffer);
123
+ parsed && controller.enqueue(parsed);
124
+ }
125
+ controller.enqueue({
126
+ type: "finish",
127
+ finishReason,
128
+ usage: { promptTokens: 0, completionTokens: 0 }
129
+ });
130
+ } catch (error) {
131
+ controller.enqueue({
132
+ type: "error",
133
+ error: error instanceof Error ? error : new Error(String(error))
134
+ });
135
+ } finally {
136
+ controller.close();
137
+ }
138
+ }
139
+ });
140
+ }
141
+ const UNSUPPORTED_SETTINGS = [
142
+ "maxTokens",
143
+ "temperature",
144
+ "topP",
145
+ "topK",
146
+ "frequencyPenalty",
147
+ "presencePenalty",
148
+ "seed"
149
+ ];
150
+ class ContentAgentModel {
151
+ specificationVersion = "v1";
152
+ provider;
153
+ modelId;
154
+ defaultObjectGenerationMode = void 0;
155
+ config;
156
+ threadId;
157
+ settings;
158
+ constructor(threadId, settings, config) {
159
+ this.threadId = threadId, this.modelId = `content-agent:${threadId}`, this.provider = config.provider, this.settings = settings ?? {}, this.config = config;
160
+ }
161
+ convertPromptToMessages(prompt) {
162
+ const messages = [];
163
+ 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 });
167
+ }
168
+ return messages;
169
+ }
170
+ collectWarnings(options) {
171
+ 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
+ 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;
179
+ }
180
+ buildRequestBody(options, stream) {
181
+ return {
182
+ messages: this.convertPromptToMessages(options.prompt),
183
+ stream,
184
+ ...this.settings.application && { application: this.settings.application },
185
+ ...this.settings.config && { config: this.settings.config },
186
+ ...this.settings.format && { format: this.settings.format }
187
+ };
188
+ }
189
+ getResponseHeaders(response) {
190
+ return Object.fromEntries(response.headers.entries());
191
+ }
192
+ async doGenerate(options) {
193
+ const body = this.buildRequestBody(options, !1), warnings = this.collectWarnings(options), response = await fetchApi({
194
+ url: buildApiUrl(
195
+ this.config.baseURL,
196
+ this.config.apiVersion,
197
+ `/${this.config.organizationId}/threads/${this.threadId}`
198
+ ),
199
+ body,
200
+ headers: buildHeaders(this.config.headers()),
201
+ signal: options.abortSignal,
202
+ fetch: this.config.fetch
203
+ });
204
+ 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) },
210
+ warnings
211
+ };
212
+ }
213
+ async doStream(options) {
214
+ const body = this.buildRequestBody(options, !0), warnings = this.collectWarnings(options), response = await fetchApi({
215
+ url: buildApiUrl(
216
+ this.config.baseURL,
217
+ this.config.apiVersion,
218
+ `/${this.config.organizationId}/threads/${this.threadId}`
219
+ ),
220
+ body,
221
+ headers: buildHeaders(this.config.headers()),
222
+ signal: options.abortSignal,
223
+ fetch: this.config.fetch
224
+ });
225
+ if (!response.body)
226
+ throw new Error("No response body for streaming request");
227
+ return {
228
+ stream: createSSEStream(response.body),
229
+ rawCall: { rawPrompt: body.messages, rawSettings: {} },
230
+ rawResponse: { headers: this.getResponseHeaders(response) },
231
+ warnings
232
+ };
233
+ }
234
+ }
235
+ const DEFAULT_BASE_URL = "https://api.sanity.io", DEFAULT_API_VERSION = "v2026-01-01";
236
+ function createContentAgent(options) {
237
+ const baseURL = options.baseURL?.replace(/\/$/, "") ?? DEFAULT_BASE_URL, apiVersion = options.apiVersion ?? DEFAULT_API_VERSION, { organizationId } = options, getHeaders = () => ({
238
+ ...options.token && { Authorization: `Bearer ${options.token}` },
239
+ ...options.headers
240
+ }), config = {
241
+ provider: "content-agent",
242
+ baseURL,
243
+ apiVersion,
244
+ organizationId,
245
+ headers: getHeaders,
246
+ fetch: options.fetch
247
+ };
248
+ return { agent: (threadId, settings) => new ContentAgentModel(threadId, settings, config), applications: async () => {
249
+ const fetchFn = options.fetch ?? globalThis.fetch, url = buildApiUrl(baseURL, apiVersion, `/${organizationId}/applications`), headers = buildHeaders(getHeaders()), response = await fetchFn(url, { method: "GET", headers });
250
+ if (!response.ok) {
251
+ const errorText = await response.text();
252
+ throw new SanityAgentError(response.status, errorText);
253
+ }
254
+ return (await response.json()).applications ?? [];
255
+ }, prompt: async (settings, generateOptions) => {
256
+ const body = {
257
+ organizationId,
258
+ message: generateOptions.message,
259
+ stream: generateOptions.stream ?? !1,
260
+ ...settings.application && { application: settings.application },
261
+ ...settings.config && { config: settings.config },
262
+ ...settings.instructions && { instructions: settings.instructions },
263
+ ...settings.format && { format: settings.format }
264
+ }, response = await fetchApi({
265
+ url: buildApiUrl(baseURL, apiVersion, "/prompt"),
266
+ body,
267
+ headers: buildHeaders(getHeaders()),
268
+ signal: generateOptions.abortSignal,
269
+ fetch: options.fetch
270
+ });
271
+ if (body.stream) {
272
+ if (!response.body)
273
+ throw new SanityAgentError(500, "No response body");
274
+ return { text: await extractTextFromStream(response.body) };
275
+ }
276
+ return { text: (await response.json()).text ?? "" };
277
+ } };
278
+ }
279
+ export {
280
+ ContentAgentModel,
281
+ SanityAgentError,
282
+ buildApiUrl,
283
+ buildHeaders,
284
+ createContentAgent,
285
+ createSSEStream,
286
+ extractTextFromParts,
287
+ extractTextFromStream,
288
+ fetchApi,
289
+ mapFinishReason,
290
+ parseFinishLine,
291
+ parseStreamLine
292
+ };
293
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":";AAMO,SAAS,aACd,eACA,mBACwB;AACxB,SAAO;AAAA,IACL;AAAA,MACE,gBAAgB;AAAA,MAChB,GAAG;AAAA,MACH,GAAG;AAAA,IAAA;AAAA,IAEL;AAAA,EAAA;AAEJ;AAKO,SAAS,YAAY,SAAiB,YAAoB,UAA0B;AACzF,SAAO,GAAG,OAAO,IAAI,UAAU,SAAS,QAAQ;AAClD;AAKO,MAAM,yBAAyB,MAAM;AAAA,EACjC;AAAA,EACA;AAAA,EAET,YAAY,QAAgB,SAAiB;AAC3C,UAAM,2BAA2B,MAAM,IAAI,OAAO,EAAE,GACpD,KAAK,OAAO,oBACZ,KAAK,SAAS,QACd,KAAK,cAAc,UAAU;AAAA,EAC/B;AACF;AAcA,eAAsB,SAAS,SAA0C;AAGvE,QAAM,WAAW,OAFD,QAAQ,SAAS,WAAW,OAEb,QAAQ,KAAK;AAAA,IAC1C,QAAQ;AAAA,IACR,SAAS,QAAQ;AAAA,IACjB,MAAM,KAAK,UAAU,QAAQ,IAAI;AAAA,IACjC,QAAQ,QAAQ;AAAA,EAAA,CACjB;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,YAAY,MAAM,SAAS,KAAA;AACjC,UAAM,IAAI,iBAAiB,SAAS,QAAQ,SAAS;AAAA,EACvD;AAEA,SAAO;AACT;AAKO,SAAS,qBACd,SACQ;AACR,SAAI,OAAO,WAAY,WACd,UAGF,QACJ;AAAA,IACC,CAAC,SACC,KAAK,SAAS,UAAU,OAAO,KAAK,QAAS;AAAA,EAAA,EAEhD,IAAI,CAAC,SAAS,KAAK,IAAI,EACvB,KAAK;AAAA,CAAI;AACd;AAKO,SAAS,gBAAgB,QAAyD;AACvF,UAAQ,QAAA;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EAAA;AAEb;AAMO,SAAS,gBAAgB,MAAgD;AAC9E,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,CAAC,QAAS,QAAO;AAQrB,MAAI,QAAQ,WAAW,IAAI;AACzB,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,CAAC,CAAC;AACxC,UAAI,OAAO,QAAS;AAClB,eAAO,EAAE,MAAM,cAAc,WAAW,KAAA;AAAA,IAE5C,QAAQ;AAAA,IAER;AAAA,WACS,QAAQ,WAAW,IAAI;AAChC,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,CAAC,CAAC;AACxC,aAAO,EAAE,MAAM,SAAS,OAAO,IAAI,MAAM,KAAK,SAAS,eAAe,EAAA;AAAA,IACxE,QAAQ;AACN,aAAO,EAAE,MAAM,SAAS,OAAO,IAAI,MAAM,yBAAyB,EAAA;AAAA,IACpE;AAGF,SAAO;AACT;AAMO,SAAS,gBAAgB,MAAkD;AAChF,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,CAAC,QAAQ,WAAW,IAAI,EAAG,QAAO;AAEtC,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,CAAC,CAAC;AACxC,WAAO,gBAAgB,KAAK,YAAY;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,sBACpB,cACiB;AACjB,QAAM,SAAS,aAAa,UAAA,GACtB,UAAU,IAAI,YAAA;AACpB,MAAI,OAAO;AAEX,aAAa;AACX,UAAM,EAAE,MAAM,MAAA,IAAU,MAAM,OAAO,KAAA;AACrC,QAAI,KAAM;AAEV,UAAM,QAAQ,QAAQ,OAAO,OAAO,EAAE,QAAQ,IAAM;AACpD,eAAW,QAAQ,MAAM,MAAM;AAAA,CAAI;AACjC,UAAI,KAAK,WAAW,IAAI;AACtB,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,KAAK,MAAM,CAAC,CAAC;AACnC,iBAAO,UAAW,aACpB,QAAQ;AAAA,QAEZ,QAAQ;AAAA,QAER;AAAA,EAGN;AAEA,SAAO;AACT;AAKO,SAAS,gBACd,cAC2C;AAC3C,MAAI,eAA4C;AAEhD,SAAO,IAAI,eAA0C;AAAA,IACnD,MAAM,MAAM,YAAY;AACtB,YAAM,SAAS,aAAa,UAAA,GACtB,UAAU,IAAI,YAAA;AACpB,UAAI,SAAS;AAEb,UAAI;AACF,mBAAa;AACX,gBAAM,EAAE,MAAM,MAAA,IAAU,MAAM,OAAO,KAAA;AACrC,cAAI,KAAM;AAEV,oBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,IAAM;AAChD,gBAAM,QAAQ,OAAO,MAAM;AAAA,CAAI;AAC/B,mBAAS,MAAM,SAAS;AAExB,qBAAW,QAAQ,OAAO;AACxB,kBAAM,SAAS,gBAAgB,IAAI;AAC/B,sBACF,WAAW,QAAQ,MAAM;AAG3B,kBAAM,SAAS,gBAAgB,IAAI;AAC/B,uBACF,eAAe;AAAA,UAEnB;AAAA,QACF;AAGA,YAAI,OAAO,QAAQ;AACjB,gBAAM,SAAS,gBAAgB,MAAM;AACjC,oBACF,WAAW,QAAQ,MAAM;AAAA,QAE7B;AAEA,mBAAW,QAAQ;AAAA,UACjB,MAAM;AAAA,UACN;AAAA,UACA,OAAO,EAAE,cAAc,GAAG,kBAAkB,EAAA;AAAA,QAAE,CAC/C;AAAA,MACH,SAAS,OAAO;AACd,mBAAW,QAAQ;AAAA,UACjB,MAAM;AAAA,UACN,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,QAAA,CAChE;AAAA,MACH,UAAA;AACE,mBAAW,MAAA;AAAA,MACb;AAAA,IACF;AAAA,EAAA,CACD;AACH;ACtOA,MAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,kBAA6C;AAAA,EAC/C,uBAAuB;AAAA,EACvB;AAAA,EACA;AAAA,EACA,8BAA8B;AAAA,EAEtB;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YACE,UACA,UACA,QACA;AACA,SAAK,WAAW,UAChB,KAAK,UAAU,iBAAiB,QAAQ,IACxC,KAAK,WAAW,OAAO,UACvB,KAAK,WAAW,YAAY,CAAA,GAC5B,KAAK,SAAS;AAAA,EAChB;AAAA,EAEQ,wBAAwB,QAA0C;AACxE,UAAM,WAAsB,CAAA;AAE5B,eAAW,WAAW,QAAQ;AAC5B,UAAI,QAAQ,SAAS,OAAQ;AAE7B,YAAM,UACJ,QAAQ,SAAS,WACb,QAAQ,UACR,qBAAqB,QAAQ,OAA0D;AAEzF,iBACF,SAAS,KAAK,EAAE,MAAM,QAAQ,MAAM,SAAS;AAAA,IAEjD;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,gBAAgB,SAAmE;AACzF,UAAM,WAAyC,CAAA;AAE3C,YAAQ,KAAK,SAAS,cACpB,QAAQ,KAAK,OAAO,UACtB,SAAS,KAAK,EAAE,MAAM,SAAS,SAAS,gDAAgD,GAEtF,QAAQ,KAAK,cACf,SAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,SAAS;AAAA,IAAA,CACV;AAIL,eAAW,WAAW;AAChB,cAAQ,OAAO,MAAM,UACvB,SAAS,KAAK,EAAE,MAAM,uBAAuB,SAAS;AAI1D,WAAI,QAAQ,eAAe,UACzB,SAAS,KAAK,EAAE,MAAM,uBAAuB,SAAS,gBAAA,CAAiB,GAGlE;AAAA,EACT;AAAA,EAEQ,iBAAiB,SAAqC,QAAiB;AAC7E,WAAO;AAAA,MACL,UAAU,KAAK,wBAAwB,QAAQ,MAAM;AAAA,MACrD;AAAA,MACA,GAAI,KAAK,SAAS,eAAe,EAAE,aAAa,KAAK,SAAS,YAAA;AAAA,MAC9D,GAAI,KAAK,SAAS,UAAU,EAAE,QAAQ,KAAK,SAAS,OAAA;AAAA,MACpD,GAAI,KAAK,SAAS,UAAU,EAAE,QAAQ,KAAK,SAAS,OAAA;AAAA,IAAO;AAAA,EAE/D;AAAA,EAEQ,mBAAmB,UAA4C;AACrE,WAAO,OAAO,YAAY,SAAS,QAAQ,SAAS;AAAA,EACtD;AAAA,EAEA,MAAM,WAAW,SAOd;AACD,UAAM,OAAO,KAAK,iBAAiB,SAAS,EAAK,GAC3C,WAAW,KAAK,gBAAgB,OAAO,GAEvC,WAAW,MAAM,SAAS;AAAA,MAC9B,KAAK;AAAA,QACH,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,IAAI,KAAK,OAAO,cAAc,YAAY,KAAK,QAAQ;AAAA,MAAA;AAAA,MAEzD;AAAA,MACA,SAAS,aAAa,KAAK,OAAO,SAAS;AAAA,MAC3C,QAAQ,QAAQ;AAAA,MAChB,OAAO,KAAK,OAAO;AAAA,IAAA,CACpB;AAID,WAAO;AAAA,MACL,OAHoB,MAAM,SAAS,KAAA,GAGhB,QAAQ;AAAA,MAC3B,cAAc;AAAA,MACd,OAAO,EAAE,cAAc,GAAG,kBAAkB,EAAA;AAAA,MAC5C,SAAS,EAAE,WAAW,KAAK,UAAU,aAAa,CAAA,EAAC;AAAA,MACnD,aAAa,EAAE,SAAS,KAAK,mBAAmB,QAAQ,EAAA;AAAA,MACxD;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,MAAM,SAAS,SAKZ;AACD,UAAM,OAAO,KAAK,iBAAiB,SAAS,EAAI,GAC1C,WAAW,KAAK,gBAAgB,OAAO,GAEvC,WAAW,MAAM,SAAS;AAAA,MAC9B,KAAK;AAAA,QACH,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,IAAI,KAAK,OAAO,cAAc,YAAY,KAAK,QAAQ;AAAA,MAAA;AAAA,MAEzD;AAAA,MACA,SAAS,aAAa,KAAK,OAAO,SAAS;AAAA,MAC3C,QAAQ,QAAQ;AAAA,MAChB,OAAO,KAAK,OAAO;AAAA,IAAA,CACpB;AAED,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,wCAAwC;AAG1D,WAAO;AAAA,MACL,QAAQ,gBAAgB,SAAS,IAAI;AAAA,MACrC,SAAS,EAAE,WAAW,KAAK,UAAU,aAAa,CAAA,EAAC;AAAA,MACnD,aAAa,EAAE,SAAS,KAAK,mBAAmB,QAAQ,EAAA;AAAA,MACxD;AAAA,IAAA;AAAA,EAEJ;AACF;AClKA,MAAM,mBAAmB,yBACnB,sBAAsB;AAgDrB,SAAS,mBAAmB,SAA6D;AAC9F,QAAM,UAAU,QAAQ,SAAS,QAAQ,OAAO,EAAE,KAAK,kBACjD,aAAa,QAAQ,cAAc,qBACnC,EAAE,mBAAmB,SAErB,aAAa,OAA2C;AAAA,IAC5D,GAAI,QAAQ,SAAS,EAAE,eAAe,UAAU,QAAQ,KAAK,GAAA;AAAA,IAC7D,GAAG,QAAQ;AAAA,EAAA,IAGP,SAAkC;AAAA,IACtC,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,OAAO,QAAQ;AAAA,EAAA;AAwDjB,SAAO,EAAE,OArDK,CAAC,UAAkB,aACxB,IAAI,kBAAkB,UAAU,UAAU,MAAM,GAoDzC,cAjDK,YAAoC;AACvD,UAAM,UAAU,QAAQ,SAAS,WAAW,OACtC,MAAM,YAAY,SAAS,YAAY,IAAI,cAAc,eAAe,GACxE,UAAU,aAAa,WAAA,CAAY,GAEnC,WAAW,MAAM,QAAQ,KAAK,EAAE,QAAQ,OAAO,QAAA,CAAS;AAE9D,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAA;AACjC,YAAM,IAAI,iBAAiB,SAAS,QAAQ,SAAS;AAAA,IACvD;AAGA,YADc,MAAM,SAAS,KAAA,GACjB,gBAAgB,CAAA;AAAA,EAC9B,GAmC8B,QAjCf,OACb,UACA,oBAC4B;AAC5B,UAAM,OAAO;AAAA,MACX;AAAA,MACA,SAAS,gBAAgB;AAAA,MACzB,QAAQ,gBAAgB,UAAU;AAAA,MAClC,GAAI,SAAS,eAAe,EAAE,aAAa,SAAS,YAAA;AAAA,MACpD,GAAI,SAAS,UAAU,EAAE,QAAQ,SAAS,OAAA;AAAA,MAC1C,GAAI,SAAS,gBAAgB,EAAE,cAAc,SAAS,aAAA;AAAA,MACtD,GAAI,SAAS,UAAU,EAAE,QAAQ,SAAS,OAAA;AAAA,IAAO,GAG7C,WAAW,MAAM,SAAS;AAAA,MAC9B,KAAK,YAAY,SAAS,YAAY,SAAS;AAAA,MAC/C;AAAA,MACA,SAAS,aAAa,YAAY;AAAA,MAClC,QAAQ,gBAAgB;AAAA,MACxB,OAAO,QAAQ;AAAA,IAAA,CAChB;AAED,QAAI,KAAK,QAAQ;AACf,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI,iBAAiB,KAAK,kBAAkB;AAEpD,aAAO,EAAE,MAAM,MAAM,sBAAsB,SAAS,IAAI,EAAA;AAAA,IAC1D;AAGA,WAAO,EAAE,OADK,MAAM,SAAS,KAAA,GACT,QAAQ,GAAA;AAAA,EAC9B,EAAA;AAGF;"}
package/package.json CHANGED
@@ -1,23 +1,64 @@
1
1
  {
2
2
  "name": "content-agent",
3
- "version": "0.0.3",
4
- "description": "Content Agent",
5
- "main": "src/index.js",
6
- "bin": {
7
- "content-agent": "./bin/content-agent.js"
3
+ "version": "0.2.0",
4
+ "description": "Vercel AI SDK provider for Sanity Content Agent API",
5
+ "keywords": [
6
+ "sanity",
7
+ "content-agent",
8
+ "ai-sdk",
9
+ "vercel-ai",
10
+ "provider"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/sanity-io/sanity-agent.git",
15
+ "directory": "packages/agent-provider"
8
16
  },
9
- "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
17
+ "license": "MIT",
18
+ "type": "module",
19
+ "exports": {
20
+ ".": {
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./package.json": "./package.json"
11
24
  },
25
+ "types": "./dist/index.d.ts",
12
26
  "files": [
13
- "bin",
14
- "src"
27
+ "dist"
15
28
  ],
16
- "keywords": [],
17
- "author": "Sanity.io <hello@sanity.io>",
18
- "license": "MIT",
19
- "type": "module",
20
29
  "dependencies": {
21
- "open": "^11.0.0"
30
+ "@ai-sdk/provider": "^1.1.3",
31
+ "@ai-sdk/provider-utils": "^2.2.8",
32
+ "es-toolkit": "^1.32.0"
33
+ },
34
+ "devDependencies": {
35
+ "@sanity/pkg-utils": "^10.4.1",
36
+ "@types/node": "^22.19.7",
37
+ "openapi-typescript": "^7.10.1",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^4.0.18",
40
+ "@repo/tsconfig": "0.0.0",
41
+ "@repo/package.config": "0.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "ai": ">=4.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "ai": {
48
+ "optional": true
49
+ }
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "engines": {
55
+ "node": ">=18.0.0"
56
+ },
57
+ "scripts": {
58
+ "build": "pkg build --strict --check --clean",
59
+ "clean": "rm -rf dist",
60
+ "generate-types": "openapi-typescript ../agent/docs/headless/openapi.yaml -o src/generated/openapi.ts --export-type",
61
+ "test": "vitest run --passWithNoTests",
62
+ "test:watch": "vitest"
22
63
  }
23
- }
64
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2016 - 2026 Sanity.io
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import open from "open";
3
- import { getContentAgentUrl } from "../src/index.js";
4
-
5
- open(getContentAgentUrl());
package/src/index.js DELETED
@@ -1,3 +0,0 @@
1
- export function getContentAgentUrl() {
2
- return "https://www.sanity.io/content-agent";
3
- }