content-agent 0.0.2 → 0.1.1

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,71 @@
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 } = await 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
+ ## Configuration
52
+
53
+ Pass settings to `.agent()` to configure capabilities and target a specific application:
54
+
55
+ ```typescript
56
+ const model = contentAgent.agent("my-thread", {
57
+ application: {
58
+ key: "projectId.datasetName",
59
+ },
60
+ config: {
61
+ capabilities: {
62
+ read: true,
63
+ write: false,
64
+ },
65
+ },
66
+ });
67
+ ```
68
+
69
+ ## License
70
+
71
+ MIT
@@ -0,0 +1,578 @@
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
+ '/chat': {
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
+ '/generate': {
29
+ parameters: {
30
+ query?: never;
31
+ header?: never;
32
+ path?: never;
33
+ cookie?: never;
34
+ };
35
+ get?: never;
36
+ put?: never;
37
+ /**
38
+ * Generate a one-shot response
39
+ * @description Stateless one-shot generation endpoint. No thread management or message persistence.
40
+ * Ideal for simple, single-turn interactions.
41
+ */
42
+ post: operations['generate'];
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
+ /**
54
+ * @description Your Sanity organization ID
55
+ * @example abc123
56
+ */
57
+ organizationId: string;
58
+ /**
59
+ * @description Unique identifier for the conversation thread
60
+ * @example my-conversation-thread
61
+ */
62
+ threadId: string; /** @description Array of message objects */
63
+ messages: components['schemas']['Message'][];
64
+ application?: components['schemas']['Application'];
65
+ config?: components['schemas']['Config'];
66
+ /**
67
+ * @description Enable or disable streaming responses
68
+ * @default true
69
+ */
70
+ stream: boolean;
71
+ };
72
+ GenerateRequest: {
73
+ /**
74
+ * @description Your Sanity organization ID
75
+ * @example abc123
76
+ */
77
+ organizationId: string;
78
+ /**
79
+ * @description The prompt message
80
+ * @example Summarize my latest blog posts
81
+ */
82
+ message: string;
83
+ config?: components['schemas']['Config'];
84
+ /**
85
+ * @description Custom instructions for the agent
86
+ * @example Be concise and use bullet points
87
+ */
88
+ instructions?: string;
89
+ };
90
+ Message: {
91
+ /**
92
+ * @description The role of the message sender
93
+ * @enum {string}
94
+ */
95
+ role: 'user' | 'assistant' | 'system'; /** @description The message content */
96
+ content: string; /** @description Optional unique identifier for the message */
97
+ id?: string; /** @description Optional name for the message sender */
98
+ name?: string;
99
+ }; /** @description Target Sanity application configuration */
100
+ Application: {
101
+ /**
102
+ * @description Application key in format "projectId.datasetName"
103
+ * @example abc123.production
104
+ */
105
+ key?: string;
106
+ resource?: {
107
+ /**
108
+ * @description Resource ID in format "projectId.datasetName"
109
+ * @example abc123.production
110
+ */
111
+ id?: string;
112
+ /**
113
+ * @description Resource type
114
+ * @enum {string}
115
+ */
116
+ type?: 'dataset';
117
+ };
118
+ /**
119
+ * Format: uri
120
+ * @description Base URL for the Sanity Studio
121
+ * @example https://your-studio.sanity.studio
122
+ */
123
+ intentBaseUrl?: string; /** @description Bundle version */
124
+ bundleVersion?: string; /** @description Application name */
125
+ name?: string; /** @description Application title */
126
+ title?: string; /** @description Schema descriptor ID */
127
+ schemaDescriptorId?: string;
128
+ }; /** @description Agent configuration for headless mode. Controls behavior, capabilities, and document access. */
129
+ Config: {
130
+ /**
131
+ * @description Custom instruction string to include in the system prompt
132
+ * @example You are a helpful content assistant.
133
+ */
134
+ instruction?: string;
135
+ /**
136
+ * @description Key-value pairs appended to each user message as XML tags.
137
+ * Each key becomes an XML tag with the value as content.
138
+ * Example: { "slack-channel": "#marketing" } renders as <slack-channel>#marketing</slack-channel>
139
+ * @example {
140
+ * "slack-channel": "#marketing",
141
+ * "user-role": "editor"
142
+ * }
143
+ */
144
+ userMessageContext?: {
145
+ [key: string]: string;
146
+ };
147
+ capabilities?: components['schemas']['Capabilities'];
148
+ filter?: components['schemas']['Filter'];
149
+ };
150
+ /**
151
+ * @description Agent capabilities that control what operations are allowed.
152
+ * Each capability can be a boolean or an object with a preset.
153
+ * @example {
154
+ * "read": true,
155
+ * "write": false
156
+ * }
157
+ */
158
+ Capabilities: {
159
+ read?: components['schemas']['Capability'];
160
+ write?: components['schemas']['Capability'];
161
+ };
162
+ /**
163
+ * @description A capability can be:
164
+ * - true: Full access (standard preset)
165
+ * - false: No access
166
+ * - { preset: 'minimal' | 'standard' }: Explicit preset selection
167
+ */
168
+ Capability: boolean | components['schemas']['CapabilityPreset'];
169
+ CapabilityPreset: {
170
+ /**
171
+ * @description - minimal: Basic tools only (excludes document/set processor tools)
172
+ * - standard: All tools available
173
+ * @enum {string}
174
+ */
175
+ preset: 'minimal' | 'standard';
176
+ }; /** @description GROQ filter expressions for document access control */
177
+ Filter: {
178
+ /**
179
+ * @description GROQ boolean expression for document visibility.
180
+ * Documents matching this filter are visible to the agent.
181
+ * @example _type in ['post', 'author']
182
+ */
183
+ read?: string;
184
+ /**
185
+ * @description GROQ boolean expression for mutation permissions.
186
+ * Documents matching this filter can be mutated.
187
+ * @example _type == 'post'
188
+ */
189
+ write?: string;
190
+ }; /** @description JSON response when streaming is disabled */
191
+ ChatResponse: {
192
+ /** @description The agent's response text */text?: string; /** @description The application key used for this conversation */
193
+ applicationKey?: string;
194
+ };
195
+ ErrorResponse: {
196
+ /**
197
+ * @description Error type
198
+ * @example Invalid request
199
+ */
200
+ error: string;
201
+ /**
202
+ * @description Human-readable error description
203
+ * @example Request body validation failed
204
+ */
205
+ details?: string; /** @description Validation issues (when applicable) */
206
+ issues?: components['schemas']['ValidationIssue'][];
207
+ };
208
+ ValidationIssue: {
209
+ /**
210
+ * @description Error code
211
+ * @example invalid_type
212
+ */
213
+ code?: string;
214
+ /**
215
+ * @description Path to the invalid field
216
+ * @example [
217
+ * "messages"
218
+ * ]
219
+ */
220
+ path?: string[];
221
+ /**
222
+ * @description Error message
223
+ * @example Required
224
+ */
225
+ message?: string;
226
+ };
227
+ };
228
+ responses: {
229
+ /** @description Bad Request - Request validation failed */BadRequest: {
230
+ headers: {
231
+ [name: string]: unknown;
232
+ };
233
+ content: {
234
+ /**
235
+ * @example {
236
+ * "error": "Invalid request",
237
+ * "details": "Request body validation failed",
238
+ * "issues": [
239
+ * {
240
+ * "code": "invalid_type",
241
+ * "path": [
242
+ * "messages"
243
+ * ],
244
+ * "message": "Required"
245
+ * }
246
+ * ]
247
+ * }
248
+ */
249
+ 'application/json': components['schemas']['ErrorResponse'];
250
+ };
251
+ }; /** @description Unauthorized - Missing or invalid authentication token */
252
+ Unauthorized: {
253
+ headers: {
254
+ [name: string]: unknown;
255
+ };
256
+ content: {
257
+ /**
258
+ * @example {
259
+ * "error": "Unauthorized",
260
+ * "details": "Missing authorization token"
261
+ * }
262
+ */
263
+ 'application/json': components['schemas']['ErrorResponse'];
264
+ };
265
+ }; /** @description Internal Server Error */
266
+ InternalServerError: {
267
+ headers: {
268
+ [name: string]: unknown;
269
+ };
270
+ content: {
271
+ /**
272
+ * @example {
273
+ * "error": "Internal Server Error",
274
+ * "details": "An unexpected error occurred"
275
+ * }
276
+ */
277
+ 'application/json': components['schemas']['ErrorResponse'];
278
+ };
279
+ };
280
+ };
281
+ parameters: never;
282
+ requestBodies: never;
283
+ headers: never;
284
+ pathItems: never;
285
+ };
286
+ interface operations {
287
+ chat: {
288
+ parameters: {
289
+ query?: never;
290
+ header?: never;
291
+ path?: never;
292
+ cookie?: never;
293
+ };
294
+ requestBody: {
295
+ content: {
296
+ 'application/json': components['schemas']['ChatRequest'];
297
+ };
298
+ };
299
+ responses: {
300
+ /** @description Streaming response */200: {
301
+ headers: {
302
+ 'Content-Type'?: string;
303
+ 'X-Vercel-AI-Data-Stream'?: string;
304
+ [name: string]: unknown;
305
+ };
306
+ content: {
307
+ 'text/event-stream': string; /** @description JSON response when stream is false */
308
+ 'application/json': components['schemas']['ChatResponse'];
309
+ };
310
+ };
311
+ 400: components['responses']['BadRequest'];
312
+ 401: components['responses']['Unauthorized'];
313
+ 500: components['responses']['InternalServerError'];
314
+ };
315
+ };
316
+ generate: {
317
+ parameters: {
318
+ query?: never;
319
+ header?: never;
320
+ path?: never;
321
+ cookie?: never;
322
+ };
323
+ requestBody: {
324
+ content: {
325
+ 'application/json': components['schemas']['GenerateRequest'];
326
+ };
327
+ };
328
+ responses: {
329
+ /** @description Streaming response */200: {
330
+ headers: {
331
+ 'Content-Type'?: string;
332
+ 'X-Vercel-AI-Data-Stream'?: string;
333
+ [name: string]: unknown;
334
+ };
335
+ content: {
336
+ 'text/event-stream': string;
337
+ };
338
+ };
339
+ 400: components['responses']['BadRequest'];
340
+ 401: components['responses']['Unauthorized'];
341
+ 500: components['responses']['InternalServerError'];
342
+ };
343
+ };
344
+ }
345
+ type ChatRequest = components['schemas']['ChatRequest'];
346
+ type GenerateRequest = components['schemas']['GenerateRequest'];
347
+ type ChatResponse = components['schemas']['ChatResponse'];
348
+ type ErrorResponse = components['schemas']['ErrorResponse'];
349
+ type ValidationIssue = components['schemas']['ValidationIssue'];
350
+ type Message = components['schemas']['Message'];
351
+ type MessageRole = Message['role'];
352
+ type Application = components['schemas']['Application'];
353
+ type ApplicationResource = NonNullable<Application['resource']>;
354
+ type Config = components['schemas']['Config'];
355
+ type Capabilities = components['schemas']['Capabilities'];
356
+ type Capability = components['schemas']['Capability'];
357
+ type CapabilityPreset = components['schemas']['CapabilityPreset'];
358
+ type CapabilityPresetValue = CapabilityPreset['preset'];
359
+ type Filter = components['schemas']['Filter'];
360
+ type UserMessageContext = NonNullable<Config['userMessageContext']>;
361
+ /**
362
+ * Settings for creating a Content Agent provider instance.
363
+ */
364
+ interface ContentAgentProviderSettings {
365
+ /**
366
+ * Your Sanity organization ID.
367
+ */
368
+ organizationId: string;
369
+ /**
370
+ * Base URL for the Sanity API.
371
+ * @default 'https://api.sanity.io'
372
+ */
373
+ baseURL?: string;
374
+ /**
375
+ * API version to use for requests.
376
+ * Should be in format 'vYYYY-MM-DD' (e.g., 'v2026-01-01').
377
+ * @default 'v2026-01-01'
378
+ */
379
+ apiVersion?: string;
380
+ /**
381
+ * Authentication token for the Sanity API.
382
+ * If not provided, requests will be made without authentication
383
+ * (useful for browser contexts where auth may come from cookies).
384
+ */
385
+ token?: string;
386
+ /**
387
+ * Custom headers to include in requests.
388
+ */
389
+ headers?: Record<string, string>;
390
+ /**
391
+ * Custom fetch implementation.
392
+ * Useful for testing or adding middleware.
393
+ */
394
+ fetch?: typeof globalThis.fetch;
395
+ }
396
+ /**
397
+ * Base settings shared between agent and generate requests.
398
+ */
399
+ interface BaseAgentSettings {
400
+ /**
401
+ * Target Sanity application configuration.
402
+ */
403
+ application?: Application;
404
+ /**
405
+ * Agent configuration for controlling behavior, capabilities, and document access.
406
+ */
407
+ config?: Config;
408
+ }
409
+ /**
410
+ * Settings for creating an agent instance (per-thread).
411
+ */
412
+ type AgentSettings = BaseAgentSettings;
413
+ /**
414
+ * Settings for one-shot generation requests.
415
+ */
416
+ interface GenerateSettings extends BaseAgentSettings {
417
+ /**
418
+ * Custom instructions for the agent.
419
+ */
420
+ instructions?: string;
421
+ }
422
+ interface GenerateOptions {
423
+ /**
424
+ * The prompt message to send.
425
+ */
426
+ message: string;
427
+ /**
428
+ * Enable streaming response.
429
+ * @default false
430
+ */
431
+ stream?: boolean;
432
+ /**
433
+ * Signal to abort the request.
434
+ */
435
+ abortSignal?: AbortSignal;
436
+ }
437
+ interface GenerateResult {
438
+ /**
439
+ * The generated text response.
440
+ */
441
+ text: string;
442
+ }
443
+ interface ContentAgentModelConfig {
444
+ provider: string;
445
+ baseURL: string;
446
+ apiVersion: string;
447
+ organizationId: string;
448
+ headers: () => Record<string, string | undefined>;
449
+ fetch?: typeof globalThis.fetch;
450
+ }
451
+ declare class ContentAgentModel implements LanguageModelV1 {
452
+ readonly specificationVersion = "v1";
453
+ readonly provider: string;
454
+ readonly modelId: string;
455
+ readonly defaultObjectGenerationMode: undefined;
456
+ private readonly config;
457
+ private readonly threadId;
458
+ private readonly settings;
459
+ constructor(threadId: string, settings: AgentSettings | undefined, config: ContentAgentModelConfig);
460
+ private convertPromptToMessages;
461
+ private collectWarnings;
462
+ private buildRequestBody;
463
+ private getResponseHeaders;
464
+ doGenerate(options: LanguageModelV1CallOptions): Promise<{
465
+ text?: string;
466
+ finishReason: LanguageModelV1FinishReason;
467
+ usage: {
468
+ promptTokens: number;
469
+ completionTokens: number;
470
+ };
471
+ rawCall: {
472
+ rawPrompt: unknown;
473
+ rawSettings: Record<string, unknown>;
474
+ };
475
+ rawResponse?: {
476
+ headers?: Record<string, string>;
477
+ };
478
+ warnings?: LanguageModelV1CallWarning[];
479
+ }>;
480
+ doStream(options: LanguageModelV1CallOptions): Promise<{
481
+ stream: ReadableStream<LanguageModelV1StreamPart>;
482
+ rawCall: {
483
+ rawPrompt: unknown;
484
+ rawSettings: Record<string, unknown>;
485
+ };
486
+ rawResponse?: {
487
+ headers?: Record<string, string>;
488
+ };
489
+ warnings?: LanguageModelV1CallWarning[];
490
+ }>;
491
+ }
492
+ interface ContentAgentProvider {
493
+ /**
494
+ * Creates an agent for a conversation thread.
495
+ */
496
+ agent(threadId: string, settings?: AgentSettings): LanguageModelV1;
497
+ /**
498
+ * Sends a one-shot prompt to the Content Agent (stateless).
499
+ *
500
+ * Unlike `.agent()`, this does not create or use a conversation thread.
501
+ * Each call is independent with no message history or persistence.
502
+ * Ideal for simple, single-turn interactions.
503
+ *
504
+ * @example
505
+ * ```ts
506
+ * const result = await contentAgent.prompt(
507
+ * { instructions: "Be concise" },
508
+ * { message: "List all authors in my dataset" }
509
+ * )
510
+ * console.log(result.text)
511
+ * ```
512
+ */
513
+ prompt(settings: GenerateSettings, options: GenerateOptions): Promise<GenerateResult>;
514
+ }
515
+ /**
516
+ * Creates a Content Agent provider instance.
517
+ */
518
+ declare function createContentAgent(options: ContentAgentProviderSettings): ContentAgentProvider;
519
+ /**
520
+ * Builds headers for API requests, filtering out undefined values.
521
+ */
522
+ declare function buildHeaders(configHeaders: Record<string, string | undefined>, additionalHeaders?: Record<string, string>): Record<string, string>;
523
+ /**
524
+ * Constructs the full API URL for an endpoint.
525
+ */
526
+ declare function buildApiUrl(baseURL: string, apiVersion: string, endpoint: string): string;
527
+ /**
528
+ * Custom error class for Sanity Agent API errors.
529
+ */
530
+ declare class SanityAgentError extends Error {
531
+ readonly status: number;
532
+ readonly isRetryable: boolean;
533
+ constructor(status: number, message: string);
534
+ }
535
+ interface FetchOptions {
536
+ url: string;
537
+ body: unknown;
538
+ headers: Record<string, string>;
539
+ signal?: AbortSignal;
540
+ fetch?: typeof globalThis.fetch;
541
+ }
542
+ /**
543
+ * Makes a POST request and returns the response.
544
+ * Throws SanityAgentError if the response is not ok.
545
+ */
546
+ declare function fetchApi(options: FetchOptions): Promise<Response>;
547
+ /**
548
+ * Extracts text content from message parts (user or assistant).
549
+ */
550
+ declare function extractTextFromParts(content: string | Array<{
551
+ type: string;
552
+ text?: string;
553
+ }>): string;
554
+ /**
555
+ * Maps API finish reasons to LanguageModelV1FinishReason.
556
+ */
557
+ declare function mapFinishReason(reason: string | undefined): LanguageModelV1FinishReason;
558
+ /**
559
+ * Parses a single line from Vercel AI Data Stream Protocol.
560
+ * Returns null if the line cannot be parsed or should be ignored.
561
+ */
562
+ declare function parseStreamLine(line: string): LanguageModelV1StreamPart | null;
563
+ /**
564
+ * Parses finish metadata from a stream line.
565
+ * Returns the finish reason if found, null otherwise.
566
+ */
567
+ declare function parseFinishLine(line: string): LanguageModelV1FinishReason | null;
568
+ /**
569
+ * Extracts accumulated text from a streaming response body.
570
+ * Used for simple streaming cases where only the final text is needed.
571
+ */
572
+ declare function extractTextFromStream(responseBody: ReadableStream<Uint8Array>): Promise<string>;
573
+ /**
574
+ * Creates a ReadableStream that parses SSE data from a response body.
575
+ */
576
+ declare function createSSEStream(responseBody: ReadableStream<Uint8Array>): ReadableStream<LanguageModelV1StreamPart>;
577
+ 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, SanityAgentError, type UserMessageContext, type ValidationIssue, buildApiUrl, buildHeaders, type components, createContentAgent, createSSEStream, extractTextFromParts, extractTextFromStream, fetchApi, mapFinishReason, type operations, parseFinishLine, parseStreamLine, type paths };
578
+ //# 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":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,278 @@
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
+ organizationId: this.config.organizationId,
183
+ threadId: this.threadId,
184
+ messages: this.convertPromptToMessages(options.prompt),
185
+ stream,
186
+ ...this.settings.application && { application: this.settings.application },
187
+ ...this.settings.config && { config: this.settings.config }
188
+ };
189
+ }
190
+ getResponseHeaders(response) {
191
+ return Object.fromEntries(response.headers.entries());
192
+ }
193
+ async doGenerate(options) {
194
+ const body = this.buildRequestBody(options, !1), warnings = this.collectWarnings(options), response = await fetchApi({
195
+ url: buildApiUrl(this.config.baseURL, this.config.apiVersion, "/chat"),
196
+ body,
197
+ headers: buildHeaders(this.config.headers()),
198
+ signal: options.abortSignal,
199
+ fetch: this.config.fetch
200
+ });
201
+ return {
202
+ text: (await response.json()).text ?? "",
203
+ finishReason: "stop",
204
+ usage: { promptTokens: 0, completionTokens: 0 },
205
+ rawCall: { rawPrompt: body.messages, rawSettings: {} },
206
+ rawResponse: { headers: this.getResponseHeaders(response) },
207
+ warnings
208
+ };
209
+ }
210
+ async doStream(options) {
211
+ const body = this.buildRequestBody(options, !0), warnings = this.collectWarnings(options), response = await fetchApi({
212
+ url: buildApiUrl(this.config.baseURL, this.config.apiVersion, "/chat"),
213
+ body,
214
+ headers: buildHeaders(this.config.headers()),
215
+ signal: options.abortSignal,
216
+ fetch: this.config.fetch
217
+ });
218
+ if (!response.body)
219
+ throw new Error("No response body for streaming request");
220
+ return {
221
+ stream: createSSEStream(response.body),
222
+ rawCall: { rawPrompt: body.messages, rawSettings: {} },
223
+ rawResponse: { headers: this.getResponseHeaders(response) },
224
+ warnings
225
+ };
226
+ }
227
+ }
228
+ const DEFAULT_BASE_URL = "https://api.sanity.io", DEFAULT_API_VERSION = "v2026-01-01";
229
+ function createContentAgent(options) {
230
+ const baseURL = options.baseURL?.replace(/\/$/, "") ?? DEFAULT_BASE_URL, apiVersion = options.apiVersion ?? DEFAULT_API_VERSION, { organizationId } = options, getHeaders = () => ({
231
+ ...options.token && { Authorization: `Bearer ${options.token}` },
232
+ ...options.headers
233
+ }), config = {
234
+ provider: "content-agent",
235
+ baseURL,
236
+ apiVersion,
237
+ organizationId,
238
+ headers: getHeaders,
239
+ fetch: options.fetch
240
+ };
241
+ return { agent: (threadId, settings) => new ContentAgentModel(threadId, settings, config), prompt: async (settings, generateOptions) => {
242
+ const body = {
243
+ organizationId,
244
+ message: generateOptions.message,
245
+ stream: generateOptions.stream ?? !1,
246
+ ...settings.application && { application: settings.application },
247
+ ...settings.config && { config: settings.config },
248
+ ...settings.instructions && { instructions: settings.instructions }
249
+ }, response = await fetchApi({
250
+ url: buildApiUrl(baseURL, apiVersion, "/generate"),
251
+ body,
252
+ headers: buildHeaders(getHeaders()),
253
+ signal: generateOptions.abortSignal,
254
+ fetch: options.fetch
255
+ });
256
+ if (body.stream) {
257
+ if (!response.body)
258
+ throw new SanityAgentError(500, "No response body");
259
+ return { text: await extractTextFromStream(response.body) };
260
+ }
261
+ return { text: (await response.json()).text ?? "" };
262
+ } };
263
+ }
264
+ export {
265
+ ContentAgentModel,
266
+ SanityAgentError,
267
+ buildApiUrl,
268
+ buildHeaders,
269
+ createContentAgent,
270
+ createSSEStream,
271
+ extractTextFromParts,
272
+ extractTextFromStream,
273
+ fetchApi,
274
+ mapFinishReason,
275
+ parseFinishLine,
276
+ parseStreamLine
277
+ };
278
+ //# 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 organizationId: this.config.organizationId,\n threadId: this.threadId,\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 }\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(this.config.baseURL, this.config.apiVersion, '/chat'),\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(this.config.baseURL, this.config.apiVersion, '/chat'),\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 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 * 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 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 }\n\n const response = await fetchApi({\n url: buildApiUrl(baseURL, apiVersion, '/generate'),\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, prompt }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,23 +1,64 @@
1
1
  {
2
2
  "name": "content-agent",
3
- "version": "0.0.2",
4
- "description": "Content Agent",
5
- "main": "src/index.js",
6
- "bin": {
7
- "content-agent": "./bin/content-agent.js"
3
+ "version": "0.1.1",
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.15.21",
37
+ "openapi-typescript": "^7.10.1",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^4.0.18",
40
+ "@repo/package.config": "0.0.0",
41
+ "@repo/tsconfig": "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
- #!/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
- }