content-agent 1.1.0 → 1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/generated/openapi.ts","../../src/types.ts"],"mappings":"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"}
@@ -0,0 +1,310 @@
1
+ import { ERROR_CODES } from "@sanity/agent-types";
2
+ import { pickBy, isString } from "es-toolkit";
3
+ function buildHeaders(configHeaders, additionalHeaders) {
4
+ return pickBy(
5
+ {
6
+ "Content-Type": "application/json",
7
+ ...additionalHeaders,
8
+ ...configHeaders
9
+ },
10
+ isString
11
+ );
12
+ }
13
+ function buildApiUrl(baseURL, apiVersion, endpoint) {
14
+ return `${baseURL}/${apiVersion}/agent${endpoint}`;
15
+ }
16
+ class SanityAgentError extends Error {
17
+ status;
18
+ isRetryable;
19
+ body;
20
+ constructor(status, message) {
21
+ super(`Sanity Agent API error: ${status} ${message}`), this.name = "SanityAgentError", this.status = status, this.isRetryable = status >= 500, this.body = SanityAgentError.parseBody(message);
22
+ }
23
+ static parseBody(message) {
24
+ try {
25
+ return JSON.parse(message);
26
+ } catch {
27
+ return {};
28
+ }
29
+ }
30
+ }
31
+ class SanityAgentStreamError extends Error {
32
+ code;
33
+ constructor(message, code) {
34
+ super(message), this.name = "SanityAgentStreamError", this.code = code;
35
+ }
36
+ }
37
+ const GENERIC_ERROR_MESSAGE = "Sorry, something went wrong. Please try again.";
38
+ function mapErrorCode(code) {
39
+ switch (code) {
40
+ case ERROR_CODES.NO_COMPATIBLE_APPLICATIONS:
41
+ return { type: "no_applications" };
42
+ case ERROR_CODES.APPLICATION_NOT_FOUND:
43
+ return { type: "application_not_found" };
44
+ case ERROR_CODES.UPSTREAM_OVERLOADED:
45
+ return { type: "upstream_overloaded" };
46
+ case ERROR_CODES.UPSTREAM_ERROR:
47
+ return { type: "upstream_error" };
48
+ case ERROR_CODES.AI_USAGE_LIMIT_EXCEEDED:
49
+ return { type: "usage_limit_exceeded" };
50
+ case ERROR_CODES.STREAMING_ERROR:
51
+ return { type: "streaming_error" };
52
+ default:
53
+ return null;
54
+ }
55
+ }
56
+ function classifyAgentError(error) {
57
+ return error instanceof SanityAgentStreamError && error.code ? mapErrorCode(error.code) ?? { type: "unknown" } : error instanceof SanityAgentError ? error.status !== 400 ? { type: "unknown" } : mapErrorCode(error.body.code) ?? { type: "unknown" } : { type: "unknown" };
58
+ }
59
+ function isAppRelatedError(classified) {
60
+ return classified.type === "no_applications" || classified.type === "application_not_found";
61
+ }
62
+ async function resolveAgentErrorMessage(classified, error, resolveContext) {
63
+ const fallback = getAgentErrorMessage(error);
64
+ if (!resolveContext || !isAppRelatedError(classified)) return fallback;
65
+ const ctx = await resolveContext();
66
+ if (!ctx.orgName) return fallback;
67
+ switch (classified.type) {
68
+ case "no_applications":
69
+ return `Studio action required: To use the Content Agent in "${ctx.orgName}", you must have at least one Sanity Studio running version 5.1.0 or above. After upgrading, open your studio to connect it to the agent.`;
70
+ case "application_not_found":
71
+ return `The requested studio is not available in "${ctx.orgName}". You may not have access, or it may belong to a different organization.`;
72
+ default:
73
+ return fallback;
74
+ }
75
+ }
76
+ function getAgentErrorMessage(error) {
77
+ return error instanceof SanityAgentStreamError ? error.message : error instanceof SanityAgentError && error.body.message ? error.body.message : GENERIC_ERROR_MESSAGE;
78
+ }
79
+ async function fetchApi(options) {
80
+ const fetchFn = options.fetch ?? globalThis.fetch, method = options.method ?? "POST", hasBody = method !== "GET" && method !== "HEAD", response = await fetchFn(options.url, {
81
+ method,
82
+ headers: options.headers,
83
+ ...hasBody && { body: JSON.stringify(options.body) },
84
+ signal: options.signal,
85
+ ...options.credentials && { credentials: options.credentials }
86
+ });
87
+ if (!response.ok) {
88
+ const errorText = await response.text();
89
+ throw new SanityAgentError(response.status, errorText);
90
+ }
91
+ return response;
92
+ }
93
+ function extractTextFromParts(content) {
94
+ return typeof content == "string" ? content : content.filter(
95
+ (part) => part.type === "text" && typeof part.text == "string"
96
+ ).map((part) => part.text).join(`
97
+ `);
98
+ }
99
+ function createEmptyUsage() {
100
+ return {
101
+ inputTokens: {
102
+ total: 0,
103
+ noCache: void 0,
104
+ cacheRead: void 0,
105
+ cacheWrite: void 0
106
+ },
107
+ outputTokens: {
108
+ total: 0,
109
+ text: void 0,
110
+ reasoning: void 0
111
+ }
112
+ };
113
+ }
114
+ function mapFinishReason(reason) {
115
+ return { unified: (() => {
116
+ switch (reason) {
117
+ case "stop":
118
+ case "length":
119
+ case "content-filter":
120
+ case "tool-calls":
121
+ case "error":
122
+ return reason;
123
+ default:
124
+ return "other";
125
+ }
126
+ })(), raw: reason };
127
+ }
128
+ function parseStreamLine(line, textContentId) {
129
+ const trimmed = line.trim();
130
+ if (!trimmed.startsWith("data: ")) return null;
131
+ const payload = trimmed.slice(6);
132
+ if (payload === "[DONE]") return null;
133
+ let part;
134
+ try {
135
+ part = JSON.parse(payload);
136
+ } catch {
137
+ return null;
138
+ }
139
+ const type = part.type;
140
+ if (!type) return null;
141
+ switch (type) {
142
+ case "text-start":
143
+ return { kind: "part", part: { type: "text-start", id: part.id || textContentId } };
144
+ case "text-delta": {
145
+ const id = part.id || textContentId, delta = part.delta;
146
+ return typeof delta != "string" ? null : { kind: "part", part: { type: "text-delta", id, delta } };
147
+ }
148
+ case "text-end":
149
+ return { kind: "part", part: { type: "text-end", id: part.id || textContentId } };
150
+ case "tool-input-available": {
151
+ const input = part.input, isServerExecuted = part.providerExecuted === !0;
152
+ return {
153
+ kind: "part",
154
+ part: {
155
+ type: "tool-call",
156
+ toolCallId: part.toolCallId,
157
+ toolName: part.toolName,
158
+ input: typeof input == "string" ? input : JSON.stringify(input),
159
+ ...isServerExecuted && { providerExecuted: !0, dynamic: !0 }
160
+ }
161
+ };
162
+ }
163
+ case "tool-output-available": {
164
+ if (part.providerExecuted !== !0) return null;
165
+ const result = part.output ?? {};
166
+ return {
167
+ kind: "part",
168
+ part: {
169
+ type: "tool-result",
170
+ toolCallId: part.toolCallId,
171
+ toolName: part.toolName,
172
+ result,
173
+ dynamic: !0
174
+ }
175
+ };
176
+ }
177
+ case "error": {
178
+ const errorText = part.errorText ?? "Unknown error", errorCode = part.errorCode;
179
+ return {
180
+ kind: "part",
181
+ part: {
182
+ type: "error",
183
+ error: new SanityAgentStreamError(errorText, errorCode)
184
+ }
185
+ };
186
+ }
187
+ case "abort":
188
+ return { kind: "data", type: "abort", data: { reason: part.reason } };
189
+ case "finish":
190
+ return {
191
+ kind: "finish",
192
+ finishReason: mapFinishReason(part.finishReason)
193
+ };
194
+ default:
195
+ return type.startsWith("data-") ? { kind: "data", type: type.slice(5), data: part.data } : null;
196
+ }
197
+ }
198
+ async function extractTextFromStream(responseBody) {
199
+ const reader = responseBody.getReader(), decoder = new TextDecoder(), textContentId = "extract";
200
+ let text = "";
201
+ for (; ; ) {
202
+ const { done, value } = await reader.read();
203
+ if (done) break;
204
+ const chunk = decoder.decode(value, { stream: !0 });
205
+ for (const line of chunk.split(`
206
+ `)) {
207
+ const parsed = parseStreamLine(line, textContentId);
208
+ parsed?.kind === "part" && parsed.part.type === "text-delta" && (text += parsed.part.delta);
209
+ }
210
+ }
211
+ return text;
212
+ }
213
+ function isRetryableStreamError(error) {
214
+ if (!error || error?.name === "AbortError") return !1;
215
+ const status = error?.status;
216
+ if (status && status >= 500 && status < 600 || status === 408 || status === 425 || status === 429) return !0;
217
+ const message = error?.message ?? "";
218
+ if (/network|fetch failed|socket|ECONN|ETIMEDOUT|EPIPE|UND_ERR/i.test(message)) return !0;
219
+ const cause = error?.cause;
220
+ return cause && cause !== error ? isRetryableStreamError(cause) : !1;
221
+ }
222
+ function backoffMs(attempt) {
223
+ return 500 * 2 ** (attempt - 1);
224
+ }
225
+ function createSSEStream(responseBody, warnings, optionsOrOnData) {
226
+ const opts = typeof optionsOrOnData == "function" ? { onData: optionsOrOnData } : optionsOrOnData ?? {}, { onData, reconnect, maxRetries = 3 } = opts;
227
+ let finishReason = { unified: "other", raw: void 0 };
228
+ const fallbackTextId = crypto.randomUUID();
229
+ let hasToolCalls = !1, lastSeq = 0, attempt = 0;
230
+ return new ReadableStream({
231
+ async start(controller) {
232
+ const decoder = new TextDecoder();
233
+ let buffer = "", currentBody = responseBody;
234
+ const processLine = (line) => {
235
+ const idMatch = line.match(/^id: (\d+)$/);
236
+ if (idMatch) {
237
+ const seq = Number(idMatch[1]);
238
+ seq > lastSeq && (lastSeq = seq);
239
+ return;
240
+ }
241
+ const parsed = parseStreamLine(line, fallbackTextId);
242
+ parsed?.kind === "part" ? (parsed.part.type === "tool-call" && (hasToolCalls = !0), controller.enqueue(parsed.part)) : parsed?.kind === "finish" ? finishReason = parsed.finishReason : parsed?.kind === "data" && onData?.(parsed.type, parsed.data);
243
+ };
244
+ controller.enqueue({ type: "stream-start", warnings });
245
+ try {
246
+ for (; currentBody; ) {
247
+ const reader = currentBody.getReader();
248
+ let bodyDone = !1, chunksRead = 0;
249
+ try {
250
+ for (; ; ) {
251
+ const { done, value } = await reader.read();
252
+ if (done) {
253
+ bodyDone = !0;
254
+ break;
255
+ }
256
+ chunksRead++, buffer += decoder.decode(value, { stream: !0 });
257
+ const lines = buffer.split(`
258
+ `);
259
+ buffer = lines.pop() ?? "";
260
+ for (const line of lines) processLine(line);
261
+ }
262
+ } catch (err) {
263
+ if (!reconnect || attempt >= maxRetries || !isRetryableStreamError(err))
264
+ throw err;
265
+ attempt++, await new Promise((r) => setTimeout(r, backoffMs(attempt))), buffer = "";
266
+ const next = await reconnect(lastSeq);
267
+ if (next === null) {
268
+ currentBody = null, bodyDone = !0;
269
+ continue;
270
+ }
271
+ currentBody = next.body;
272
+ continue;
273
+ }
274
+ if (chunksRead > 0 && (attempt = 0), bodyDone) break;
275
+ }
276
+ buffer.trim() && processLine(buffer), hasToolCalls && finishReason.unified !== "tool-calls" && (finishReason = { unified: "tool-calls", raw: finishReason.raw ?? "tool-calls" }), controller.enqueue({
277
+ type: "finish",
278
+ finishReason,
279
+ usage: createEmptyUsage()
280
+ });
281
+ } catch (error) {
282
+ controller.enqueue({
283
+ type: "error",
284
+ error: error instanceof Error ? error : new Error(String(error))
285
+ });
286
+ } finally {
287
+ controller.close();
288
+ }
289
+ }
290
+ });
291
+ }
292
+ export {
293
+ GENERIC_ERROR_MESSAGE,
294
+ SanityAgentError,
295
+ SanityAgentStreamError,
296
+ buildApiUrl,
297
+ buildHeaders,
298
+ classifyAgentError,
299
+ createEmptyUsage,
300
+ createSSEStream,
301
+ extractTextFromParts,
302
+ extractTextFromStream,
303
+ fetchApi,
304
+ getAgentErrorMessage,
305
+ isAppRelatedError,
306
+ mapFinishReason,
307
+ parseStreamLine,
308
+ resolveAgentErrorMessage
309
+ };
310
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type {\n LanguageModelV3FinishReason,\n LanguageModelV3StreamPart,\n LanguageModelV3Usage,\n SharedV3Warning,\n} from '@ai-sdk/provider'\nimport { ERROR_CODES, type HeadlessErrorResponse } from '@sanity/agent-types'\nimport { isString, pickBy } from 'es-toolkit'\n\n/**\n * Builds headers for API requests, filtering out undefined values.\n *\n * @category Utilities\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 *\n * @category Utilities\n */\nexport function buildApiUrl(baseURL: string, apiVersion: string, endpoint: string): string {\n return `${baseURL}/${apiVersion}/agent${endpoint}`\n}\n\n/**\n * Custom error class for Sanity Agent API errors.\n * The {@link body} is parsed from the JSON response and follows the\n * {@link HeadlessErrorResponse} shape defined in `@sanity/agent-types`.\n *\n * @category Utilities\n */\nexport class SanityAgentError extends Error {\n readonly status: number\n readonly isRetryable: boolean\n readonly body: Partial<HeadlessErrorResponse>\n\n constructor(status: number, message: string) {\n super(`Sanity Agent API error: ${status} ${message}`)\n this.name = 'SanityAgentError'\n this.status = status\n this.isRetryable = status >= 500\n this.body = SanityAgentError.parseBody(message)\n }\n\n private static parseBody(message: string): Partial<HeadlessErrorResponse> {\n try {\n return JSON.parse(message)\n } catch {\n return {}\n }\n }\n}\n\n/**\n * Error class for errors received via SSE stream events.\n * Unlike {@link SanityAgentError} (which is thrown for HTTP-level errors before\n * streaming starts), this wraps errors received mid-stream as SSE `error` events.\n *\n * The {@link code} carries the backend's error classification (e.g. `UPSTREAM_OVERLOADED`)\n * so clients can take targeted action without parsing the message string.\n *\n * @category Utilities\n */\nexport class SanityAgentStreamError extends Error {\n readonly code: string | undefined\n\n constructor(message: string, code?: string) {\n super(message)\n this.name = 'SanityAgentStreamError'\n this.code = code\n }\n}\n\nexport const GENERIC_ERROR_MESSAGE = 'Sorry, something went wrong. Please try again.'\n\nexport type AgentErrorType =\n | { type: 'no_applications' }\n | { type: 'application_not_found' }\n | { type: 'upstream_overloaded' }\n | { type: 'upstream_error' }\n | { type: 'usage_limit_exceeded' }\n | { type: 'streaming_error' }\n | { type: 'unknown' }\n\nfunction mapErrorCode(code: string | undefined): AgentErrorType | null {\n switch (code) {\n case ERROR_CODES.NO_COMPATIBLE_APPLICATIONS:\n return { type: 'no_applications' }\n case ERROR_CODES.APPLICATION_NOT_FOUND:\n return { type: 'application_not_found' }\n case ERROR_CODES.UPSTREAM_OVERLOADED:\n return { type: 'upstream_overloaded' }\n case ERROR_CODES.UPSTREAM_ERROR:\n return { type: 'upstream_error' }\n case ERROR_CODES.AI_USAGE_LIMIT_EXCEEDED:\n return { type: 'usage_limit_exceeded' }\n case ERROR_CODES.STREAMING_ERROR:\n return { type: 'streaming_error' }\n default:\n return null\n }\n}\n\n/**\n * Classifies a caught error from the Content Agent API into a known error type.\n * Useful for clients that need to take different actions per error (e.g. show\n * resource-selection buttons). For the user-facing message, use {@link getAgentErrorMessage}.\n *\n * @category Utilities\n */\nexport function classifyAgentError(error: unknown): AgentErrorType {\n // Handle SSE stream errors (from parseStreamLine)\n if (error instanceof SanityAgentStreamError && error.code) {\n return mapErrorCode(error.code) ?? { type: 'unknown' }\n }\n\n // Handle HTTP-level errors (from fetchApi)\n if (error instanceof SanityAgentError) {\n if (error.status !== 400) {\n return { type: 'unknown' }\n }\n return mapErrorCode(error.body.code) ?? { type: 'unknown' }\n }\n\n return { type: 'unknown' }\n}\n\nexport function isAppRelatedError(classified: AgentErrorType): boolean {\n return classified.type === 'no_applications' || classified.type === 'application_not_found'\n}\n\nexport type AgentErrorMessageContext = {\n orgName?: string | null\n}\n\n/**\n * Resolves a user-facing error message for a classified agent error.\n * For errors that benefit from additional context (e.g. org name), the optional\n * {@link resolveContext} callback is invoked lazily — only when the error type\n * needs it. Non-contextual errors return the backend message as-is.\n *\n * @category Utilities\n */\nexport async function resolveAgentErrorMessage(\n classified: AgentErrorType,\n error: unknown,\n resolveContext?: () => Promise<AgentErrorMessageContext>,\n): Promise<string> {\n const fallback = getAgentErrorMessage(error)\n\n if (!resolveContext || !isAppRelatedError(classified)) return fallback\n\n const ctx = await resolveContext()\n if (!ctx.orgName) return fallback\n\n switch (classified.type) {\n case 'no_applications':\n return `Studio action required: To use the Content Agent in \"${ctx.orgName}\", you must have at least one Sanity Studio running version 5.1.0 or above. After upgrading, open your studio to connect it to the agent.`\n case 'application_not_found':\n return `The requested studio is not available in \"${ctx.orgName}\". You may not have access, or it may belong to a different organization.`\n default:\n return fallback\n }\n}\n\n/**\n * Extracts a user-facing error message from an agent API error.\n * Returns the message sent by the API when available, otherwise a generic fallback.\n *\n * @category Utilities\n */\nexport function getAgentErrorMessage(error: unknown): string {\n if (error instanceof SanityAgentStreamError) {\n return error.message\n }\n if (error instanceof SanityAgentError && error.body.message) {\n return error.body.message\n }\n return GENERIC_ERROR_MESSAGE\n}\n\nexport interface FetchOptions {\n url: string\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD'\n body?: unknown\n headers: Record<string, string>\n credentials?: RequestCredentials\n signal?: AbortSignal\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * Makes an API request and returns the response.\n * Throws SanityAgentError if the response is not ok.\n *\n * @category Utilities\n */\nexport async function fetchApi(options: FetchOptions): Promise<Response> {\n const fetchFn = options.fetch ?? globalThis.fetch\n const method = options.method ?? 'POST'\n const hasBody = method !== 'GET' && method !== 'HEAD'\n\n const response = await fetchFn(options.url, {\n method,\n headers: options.headers,\n ...(hasBody && { body: JSON.stringify(options.body) }),\n signal: options.signal,\n ...(options.credentials && { credentials: options.credentials }),\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 *\n * @category Utilities\n */\nexport function extractTextFromParts(\n content: string | Array<{ type: string; text?: string }>,\n): string {\n if (typeof content === 'string') {\n return content\n }\n\n return content\n .filter(\n (part): part is { type: 'text'; text: string } =>\n part.type === 'text' && typeof part.text === 'string',\n )\n .map((part) => part.text)\n .join('\\n')\n}\n\n/**\n * Creates an empty V3 usage object. Our API does not return token counts.\n *\n * @category Utilities\n */\nexport function createEmptyUsage(): LanguageModelV3Usage {\n return {\n inputTokens: {\n total: 0,\n noCache: undefined,\n cacheRead: undefined,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: 0,\n text: undefined,\n reasoning: undefined,\n },\n }\n}\n\n/**\n * Maps API finish reasons to LanguageModelV3FinishReason.\n *\n * @category Utilities\n */\nexport function mapFinishReason(reason: string | undefined): LanguageModelV3FinishReason {\n const unified = (() => {\n switch (reason) {\n case 'stop':\n case 'length':\n case 'content-filter':\n case 'tool-calls':\n case 'error':\n return reason\n default:\n return 'other' as const\n }\n })()\n\n return { unified, raw: reason }\n}\n\nexport type ParsedStreamLine =\n | { kind: 'part'; part: LanguageModelV3StreamPart }\n | { kind: 'finish'; finishReason: LanguageModelV3FinishReason }\n | { kind: 'data'; type: string; data: unknown }\n\n/**\n * Parses a single SSE line from the AI SDK v6 UI Message Stream protocol.\n * Returns null if the line cannot be parsed or should be ignored.\n *\n * Format: `data: <json>\\n`\n *\n * Supported types:\n * text-start – Text block start (forwarded as-is)\n * text-delta – Text chunk\n * text-end – Text block end (forwarded as-is)\n * tool-input-available – Tool call (client tools only, without providerExecuted)\n * tool-output-available – Tool result (informational, skipped)\n * finish – Finish event with reason\n * error – Error event\n * start – Stream start (skipped)\n * [DONE] – Stream termination (skipped)\n *\n * @see https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol\n *\n * @param line A single line from the SSE byte stream.\n * @param textContentId Fallback ID for text parts that lack an explicit id.\n *\n * @category Utilities\n */\nexport function parseStreamLine(line: string, textContentId: string): ParsedStreamLine | null {\n const trimmed = line.trim()\n if (!trimmed.startsWith('data: ')) return null\n\n const payload = trimmed.slice(6)\n if (payload === '[DONE]') return null\n\n let part: Record<string, unknown>\n try {\n part = JSON.parse(payload) as Record<string, unknown>\n } catch {\n return null\n }\n\n const type = part.type as string | undefined\n if (!type) return null\n\n switch (type) {\n case 'text-start': {\n const id = (part.id as string) || textContentId\n return { kind: 'part', part: { type: 'text-start', id } }\n }\n\n case 'text-delta': {\n const id = (part.id as string) || textContentId\n const delta = part.delta as string\n if (typeof delta !== 'string') return null\n return { kind: 'part', part: { type: 'text-delta', id, delta } }\n }\n\n case 'text-end': {\n const id = (part.id as string) || textContentId\n return { kind: 'part', part: { type: 'text-end', id } }\n }\n\n case 'tool-input-available': {\n // Forward both client- and server-executed tools to the consumer's\n // streamText loop. Stop conditions like `hasToolCall('final_answer')`\n // need to see the terminal `providerExecuted` tool call to fire; the\n // consumer's local-execute path skips tools without an `execute`\n // function (or that are not in the consumer's tool map), so a server\n // tool surfaced here cannot be double-executed. See SAGE-182.\n //\n // Server-executed tools (`providerExecuted: true`) are also marked\n // `dynamic: true`. The AI SDK's `parseToolCall` only accepts a\n // tool that is missing from the consumer's tool map when both\n // `providerExecuted` and `dynamic` are set — otherwise it throws\n // `NoSuchToolError` and emits a phantom `tool-error` chunk for a\n // tool that already executed server-side. The agent server's tools\n // are not exposed to the consumer at all (server-execute is the\n // whole point), so `dynamic` is the right flag to set here.\n const input = part.input\n const isServerExecuted = part.providerExecuted === true\n return {\n kind: 'part',\n part: {\n type: 'tool-call',\n toolCallId: part.toolCallId as string,\n toolName: part.toolName as string,\n input: typeof input === 'string' ? input : JSON.stringify(input),\n ...(isServerExecuted && { providerExecuted: true, dynamic: true }),\n },\n }\n }\n\n case 'tool-output-available': {\n // Forward the result of a provider-executed tool (e.g. `final_answer`)\n // so the AI SDK loop can pair it with the matching `tool-call` and\n // include it in `currentModelResponse.content`. Without the result\n // chunk the loop will request another step to satisfy the dangling\n // tool call, defeating the natural termination Phase 1 enables.\n //\n // Only forward when `providerExecuted` is set. Client-tool results\n // are produced locally on the consumer side via `tool.execute`, not\n // streamed from the server, so any `tool-output-available` we see\n // for a non-providerExecuted tool would be unexpected — drop it.\n if (part.providerExecuted !== true) return null\n // The AI SDK's `tool-result` part requires a non-null JSON-serializable\n // result. Fall back to an empty object when the server omits one (e.g.\n // tools that signal completion via side effects).\n const result = (part.output ?? {}) as NonNullable<typeof part.output>\n return {\n kind: 'part',\n part: {\n type: 'tool-result',\n toolCallId: part.toolCallId as string,\n toolName: part.toolName as string,\n result,\n dynamic: true,\n },\n }\n }\n\n case 'error': {\n const errorText = (part.errorText as string) ?? 'Unknown error'\n const errorCode = part.errorCode as string | undefined\n return {\n kind: 'part',\n part: {\n type: 'error',\n error: new SanityAgentStreamError(errorText, errorCode),\n },\n }\n }\n\n case 'abort': {\n return { kind: 'data', type: 'abort', data: { reason: part.reason } }\n }\n\n case 'finish': {\n return {\n kind: 'finish',\n finishReason: mapFinishReason(part.finishReason as string | undefined),\n }\n }\n\n default: {\n if (type.startsWith('data-')) {\n return { kind: 'data', type: type.slice(5), data: part.data }\n }\n return null\n }\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 *\n * @category Utilities\n */\nexport async function extractTextFromStream(\n responseBody: ReadableStream<Uint8Array>,\n): Promise<string> {\n const reader = responseBody.getReader()\n const decoder = new TextDecoder()\n const textContentId = 'extract'\n let text = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n for (const line of chunk.split('\\n')) {\n const parsed = parseStreamLine(line, textContentId)\n if (parsed?.kind === 'part' && parsed.part.type === 'text-delta') {\n text += parsed.part.delta\n }\n }\n }\n\n return text\n}\n\n/** @category Utilities */\nexport type CreateSSEStreamOptions = {\n /**\n * Hook to receive `data-*` chunks the AI SDK doesn't surface natively.\n */\n onData?: (type: string, data: unknown) => void\n /**\n * Optional reconnect callback. When set, retryable read errors trigger a\n * call with the last seen `id:` sequence number, expecting a fresh\n * `Response` whose body continues the stream from that frame. Return\n * `null` to signal a clean end (HTTP 204 from the server).\n */\n reconnect?: (afterSeq: number) => Promise<Response | null>\n maxRetries?: number\n}\n\n/**\n * Retryable error predicate. Matches the same network/5xx/AbortError logic\n * as `@repo/shared/client/retryable`, inlined here so `agent-provider` stays\n * dependency-free.\n */\nfunction isRetryableStreamError(error: unknown): boolean {\n if (!error) return false\n if ((error as { name?: string })?.name === 'AbortError') return false\n\n const status = (error as { status?: number })?.status\n if (status && status >= 500 && status < 600) return true\n if (status === 408 || status === 425 || status === 429) return true\n\n const message = (error as { message?: string })?.message ?? ''\n if (/network|fetch failed|socket|ECONN|ETIMEDOUT|EPIPE|UND_ERR/i.test(message)) return true\n\n const cause = (error as { cause?: unknown })?.cause\n if (cause && cause !== error) return isRetryableStreamError(cause)\n\n return false\n}\n\nfunction backoffMs(attempt: number): number {\n return 500 * 2 ** (attempt - 1)\n}\n\n/**\n * Creates a ReadableStream that parses AI SDK v6 UI Message Stream SSE data\n * from a response body and emits LanguageModelV3 stream parts.\n *\n * Text lifecycle events (text-start / text-end) are forwarded from the server.\n * Tool calls are tracked to ensure the finish reason is set correctly.\n *\n * @category Utilities\n */\nexport function createSSEStream(\n responseBody: ReadableStream<Uint8Array>,\n warnings: SharedV3Warning[],\n optionsOrOnData?: CreateSSEStreamOptions | ((type: string, data: unknown) => void),\n): ReadableStream<LanguageModelV3StreamPart> {\n // Backwards compat: callers used to pass `onData` as the third positional arg.\n const opts: CreateSSEStreamOptions =\n typeof optionsOrOnData === 'function' ? { onData: optionsOrOnData } : (optionsOrOnData ?? {})\n const { onData, reconnect, maxRetries = 3 } = opts\n\n let finishReason: LanguageModelV3FinishReason = { unified: 'other', raw: undefined }\n const fallbackTextId = crypto.randomUUID()\n let hasToolCalls = false\n // Last seen SSE `id:` — forwarded to `reconnect` (vercel/ai#14688).\n let lastSeq = 0\n let attempt = 0\n\n return new ReadableStream<LanguageModelV3StreamPart>({\n async start(controller) {\n const decoder = new TextDecoder()\n let buffer = ''\n let currentBody: ReadableStream<Uint8Array> | null = responseBody\n\n const processLine = (line: string): void => {\n // Peek at `id:` lines for resume cursor; parseStreamLine ignores them.\n const idMatch = line.match(/^id: (\\d+)$/)\n if (idMatch) {\n const seq = Number(idMatch[1])\n if (seq > lastSeq) lastSeq = seq\n return\n }\n\n const parsed = parseStreamLine(line, fallbackTextId)\n if (parsed?.kind === 'part') {\n if (parsed.part.type === 'tool-call') {\n hasToolCalls = true\n }\n controller.enqueue(parsed.part)\n } else if (parsed?.kind === 'finish') {\n finishReason = parsed.finishReason\n } else if (parsed?.kind === 'data') {\n onData?.(parsed.type, parsed.data)\n }\n }\n\n controller.enqueue({ type: 'stream-start', warnings })\n\n try {\n while (currentBody) {\n const reader = currentBody.getReader()\n let bodyDone = false\n let chunksRead = 0\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n bodyDone = true\n break\n }\n chunksRead++\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) processLine(line)\n }\n } catch (err) {\n if (!reconnect || attempt >= maxRetries || !isRetryableStreamError(err)) {\n throw err\n }\n attempt++\n await new Promise((r) => setTimeout(r, backoffMs(attempt)))\n // Discard partial line — resume replays complete frames.\n buffer = ''\n const next = await reconnect(lastSeq)\n if (next === null) {\n currentBody = null\n bodyDone = true\n continue\n }\n currentBody = next.body\n continue\n }\n // Reset retry budget after successful delivery.\n if (chunksRead > 0) attempt = 0\n if (bodyDone) break\n }\n\n if (buffer.trim()) processLine(buffer)\n\n // If tool calls were emitted, override finish reason so streamText\n // knows to execute tools and continue with the next step.\n if (hasToolCalls && finishReason.unified !== 'tool-calls') {\n finishReason = { unified: 'tool-calls', raw: finishReason.raw ?? 'tool-calls' }\n }\n\n controller.enqueue({\n type: 'finish',\n finishReason,\n usage: createEmptyUsage(),\n })\n } catch (error) {\n controller.enqueue({\n type: 'error',\n error: error instanceof Error ? error : new Error(String(error)),\n })\n } finally {\n controller.close()\n }\n },\n })\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import { DefaultChatTransport, HttpChatTransportInitOptions, UIMessage } from "ai";
2
+ /** Sanity token, either a static string or a getter read at request time. */
3
+ type TokenSource = string | (() => string | undefined) | undefined;
4
+ /**
5
+ * Options for {@link createSanityChatTransport}.
6
+ *
7
+ * @category Transport
8
+ */
9
+ interface SanityChatTransportOptions<UI_MESSAGE extends UIMessage = UIMessage> {
10
+ /** Sanity organization ID. */
11
+ organizationId: string;
12
+ /**
13
+ * Conversation thread ID. The transport posts new turns to
14
+ * `/{organizationId}/threads/{threadId}` and reconnects via
15
+ * `/{organizationId}/threads/{threadId}/stream`. Create one transport per
16
+ * thread so the resume cursor stays scoped to that thread.
17
+ */
18
+ threadId: string;
19
+ /**
20
+ * Base URL for the Sanity API.
21
+ * @default 'https://api.sanity.io'
22
+ */
23
+ baseURL?: string;
24
+ /**
25
+ * API version segment, e.g. `'v2026-01-12'`.
26
+ * @default 'v2026-01-01'
27
+ */
28
+ apiVersion?: string;
29
+ /**
30
+ * Bearer token for the `Authorization` header. Pass a getter to read the
31
+ * latest token at request time (e.g. `() => client.config().token`). When
32
+ * omitted, no `Authorization` header is sent — useful for cookie auth.
33
+ */
34
+ token?: TokenSource;
35
+ /**
36
+ * Fetch credentials mode.
37
+ * @default 'include'
38
+ */
39
+ credentials?: RequestCredentials;
40
+ /** Custom fetch implementation (testing / middleware). */
41
+ fetch?: typeof globalThis.fetch;
42
+ /**
43
+ * Customize the per-turn POST request. Use it to inject `x-request-id`,
44
+ * `application`, `context`, `format`, etc. into the body. The bearer
45
+ * `Authorization` header is merged back in automatically, so returning a
46
+ * `headers` object that omits it won't drop auth (your values still win on
47
+ * conflict).
48
+ */
49
+ prepareSendMessagesRequest?: HttpChatTransportInitOptions<UI_MESSAGE>['prepareSendMessagesRequest'];
50
+ }
51
+ /**
52
+ * Builds a Vercel AI SDK `DefaultChatTransport` wired to the Sanity Content
53
+ * Agent HTTP API, with transparent stream resume.
54
+ *
55
+ * It encapsulates the wire-protocol plumbing every browser client needs:
56
+ * - POST `/{org}/threads/{threadId}` for new turns
57
+ * - GET `/{org}/threads/{threadId}/stream?afterSeq=N` for resume
58
+ * - bearer auth from {@link SanityChatTransportOptions.token}
59
+ * - SSE `id:` cursor tracking so reconnects replay only unseen frames,
60
+ * sidestepping [vercel/ai#14688](https://github.com/vercel/ai/issues/14688)
61
+ *
62
+ * This is the browser counterpart to the Node clients' `createSSEStream` —
63
+ * same wire protocol, expressed as a `ChatTransport` for `useChat`. It imports
64
+ * only from `ai` (no React), so it's safe to use from any framework.
65
+ *
66
+ * @category Transport
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { createSanityChatTransport } from 'content-agent/transport'
71
+ *
72
+ * const transport = createSanityChatTransport({
73
+ * organizationId,
74
+ * threadId,
75
+ * baseURL: 'https://api.sanity.io',
76
+ * apiVersion: 'v2026-01-12',
77
+ * token: () => client.config().token,
78
+ * prepareSendMessagesRequest: ({messages, body, headers}) => ({
79
+ * headers: {
80
+ * ...headers,
81
+ * 'x-request-id': messages.findLast((m) => m.role === 'user')?.id,
82
+ * },
83
+ * body: {...body, application, context, stream: true, format: 'directives'},
84
+ * }),
85
+ * })
86
+ * ```
87
+ */
88
+ declare function createSanityChatTransport<UI_MESSAGE extends UIMessage = UIMessage>(options: SanityChatTransportOptions<UI_MESSAGE>): DefaultChatTransport<UI_MESSAGE>;
89
+ export { type SanityChatTransportOptions, createSanityChatTransport };
90
+ //# sourceMappingURL=chat-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-transport.d.ts","names":[],"sources":["../src/_internal/chat-transport.ts"],"mappings":";;KAcK,WAAA;;;;;AAOL;UAAiB,0BAAA,oBAA8C,SAAA,GAAY,SAAA;EAAhC;EAEzC,cAAA;EAFyE;;;;;;EASzE,QAAA;EA+ByD;;;;EA1BzD,OAAA;EALA;;;;EAUA,UAAA;EAWA;;;;;EALA,KAAA,GAAQ,WAAA;EAeqB;;;;EAV7B,WAAA,GAAc,kBAAA;EAkDyB;EAhDvC,KAAA,UAAe,UAAA,CAAW,KAAA;EAgDiC;;;;;;;EAxC3D,0BAAA,GAA6B,4BAAA,CAA6B,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwC5C,yBAAA,oBAA6C,SAAA,GAAY,SAAA,CAAA,CACvE,OAAA,EAAS,0BAAA,CAA2B,UAAA,IACnC,oBAAA,CAAqB,UAAA"}
@@ -0,0 +1,91 @@
1
+ import { DefaultChatTransport } from "ai";
2
+ import { buildApiUrl } from "./_chunks-es/utils.js";
3
+ const DEFAULT_BASE_URL = "https://api.sanity.io", DEFAULT_API_VERSION = "v2026-01-01", NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
4
+ function createSanityChatTransport(options) {
5
+ const {
6
+ organizationId,
7
+ threadId,
8
+ baseURL = DEFAULT_BASE_URL,
9
+ apiVersion = DEFAULT_API_VERSION,
10
+ token,
11
+ credentials = "include",
12
+ fetch: customFetch,
13
+ prepareSendMessagesRequest
14
+ } = options, apiBase = buildApiUrl(baseURL, apiVersion, `/${organizationId}/threads/${threadId}`);
15
+ let lastSeq = 0;
16
+ const recordSeq = (seq) => {
17
+ seq > lastSeq && (lastSeq = seq);
18
+ }, trackingFetch = createCursorTrackingFetch(customFetch ?? globalThis.fetch, recordSeq);
19
+ return new DefaultChatTransport({
20
+ api: apiBase,
21
+ credentials,
22
+ headers: () => buildAuthHeaders(token),
23
+ fetch: trackingFetch,
24
+ prepareSendMessagesRequest: withAuthHeaders(prepareSendMessagesRequest, token),
25
+ prepareReconnectToStreamRequest: () => ({
26
+ // `?afterSeq=N` skips already-processed frames (vercel/ai#14688). Auth
27
+ // headers come from the transport's `headers` option, which the SDK
28
+ // applies to reconnect requests when none are returned here.
29
+ api: lastSeq > 0 ? `${apiBase}/stream?afterSeq=${lastSeq}` : `${apiBase}/stream`
30
+ })
31
+ });
32
+ }
33
+ function buildAuthHeaders(token) {
34
+ const resolved = typeof token == "function" ? token() : token;
35
+ return resolved ? { Authorization: `Bearer ${resolved}` } : {};
36
+ }
37
+ function withAuthHeaders(prepare, token) {
38
+ return prepare && (async (options) => {
39
+ const result = await prepare(options);
40
+ return result.headers === void 0 ? result : { ...result, headers: mergeHeaders(buildAuthHeaders(token), result.headers) };
41
+ });
42
+ }
43
+ function mergeHeaders(base, override) {
44
+ const merged = { ...base };
45
+ return new Headers(override).forEach((value, key) => {
46
+ merged[key] = value;
47
+ }), merged;
48
+ }
49
+ function createCursorTrackingFetch(baseFetch, onSeq) {
50
+ return async function(input, init) {
51
+ const response = await baseFetch(input, init);
52
+ if (!response.ok || !response.body || NULL_BODY_STATUSES.has(response.status))
53
+ return response;
54
+ const [bodyForCaller, bodyForTap] = response.body.tee();
55
+ return tapIds(bodyForTap, onSeq).catch(() => {
56
+ }), new Response(bodyForCaller, {
57
+ status: response.status,
58
+ statusText: response.statusText,
59
+ headers: response.headers
60
+ });
61
+ };
62
+ }
63
+ async function tapIds(body, onSeq) {
64
+ const reader = body.getReader(), decoder = new TextDecoder("utf-8");
65
+ let buffer = "";
66
+ try {
67
+ for (; ; ) {
68
+ const { done, value } = await reader.read();
69
+ if (done) break;
70
+ buffer += decoder.decode(value, { stream: !0 });
71
+ let nl = buffer.indexOf(`
72
+ `);
73
+ for (; nl !== -1; )
74
+ recordIfIdLine(buffer.slice(0, nl), onSeq), buffer = buffer.slice(nl + 1), nl = buffer.indexOf(`
75
+ `);
76
+ }
77
+ buffer && recordIfIdLine(buffer, onSeq);
78
+ } finally {
79
+ reader.releaseLock();
80
+ }
81
+ }
82
+ function recordIfIdLine(line, onSeq) {
83
+ const match = line.match(/^id: (\d+)\s*$/);
84
+ if (!match) return;
85
+ const seq = Number(match[1]);
86
+ Number.isFinite(seq) && onSeq(seq);
87
+ }
88
+ export {
89
+ createSanityChatTransport
90
+ };
91
+ //# sourceMappingURL=chat-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-transport.js","sources":["../src/_internal/chat-transport.ts"],"sourcesContent":["import { DefaultChatTransport, type HttpChatTransportInitOptions, type UIMessage } from 'ai'\nimport { buildApiUrl } from '../utils'\n\nconst DEFAULT_BASE_URL = 'https://api.sanity.io'\nconst DEFAULT_API_VERSION = 'v2026-01-01'\n\n/**\n * HTTP statuses whose responses must not carry a body per the Fetch spec.\n * Constructing `new Response(body, {status: N})` throws TypeError for these.\n * See https://fetch.spec.whatwg.org/#null-body-status.\n */\nconst NULL_BODY_STATUSES = new Set<number>([101, 103, 204, 205, 304])\n\n/** Sanity token, either a static string or a getter read at request time. */\ntype TokenSource = string | (() => string | undefined) | undefined\n\n/**\n * Options for {@link createSanityChatTransport}.\n *\n * @category Transport\n */\nexport interface SanityChatTransportOptions<UI_MESSAGE extends UIMessage = UIMessage> {\n /** Sanity organization ID. */\n organizationId: string\n /**\n * Conversation thread ID. The transport posts new turns to\n * `/{organizationId}/threads/{threadId}` and reconnects via\n * `/{organizationId}/threads/{threadId}/stream`. Create one transport per\n * thread so the resume cursor stays scoped to that thread.\n */\n threadId: string\n /**\n * Base URL for the Sanity API.\n * @default 'https://api.sanity.io'\n */\n baseURL?: string\n /**\n * API version segment, e.g. `'v2026-01-12'`.\n * @default 'v2026-01-01'\n */\n apiVersion?: string\n /**\n * Bearer token for the `Authorization` header. Pass a getter to read the\n * latest token at request time (e.g. `() => client.config().token`). When\n * omitted, no `Authorization` header is sent — useful for cookie auth.\n */\n token?: TokenSource\n /**\n * Fetch credentials mode.\n * @default 'include'\n */\n credentials?: RequestCredentials\n /** Custom fetch implementation (testing / middleware). */\n fetch?: typeof globalThis.fetch\n /**\n * Customize the per-turn POST request. Use it to inject `x-request-id`,\n * `application`, `context`, `format`, etc. into the body. The bearer\n * `Authorization` header is merged back in automatically, so returning a\n * `headers` object that omits it won't drop auth (your values still win on\n * conflict).\n */\n prepareSendMessagesRequest?: HttpChatTransportInitOptions<UI_MESSAGE>['prepareSendMessagesRequest']\n}\n\n/**\n * Builds a Vercel AI SDK `DefaultChatTransport` wired to the Sanity Content\n * Agent HTTP API, with transparent stream resume.\n *\n * It encapsulates the wire-protocol plumbing every browser client needs:\n * - POST `/{org}/threads/{threadId}` for new turns\n * - GET `/{org}/threads/{threadId}/stream?afterSeq=N` for resume\n * - bearer auth from {@link SanityChatTransportOptions.token}\n * - SSE `id:` cursor tracking so reconnects replay only unseen frames,\n * sidestepping [vercel/ai#14688](https://github.com/vercel/ai/issues/14688)\n *\n * This is the browser counterpart to the Node clients' `createSSEStream` —\n * same wire protocol, expressed as a `ChatTransport` for `useChat`. It imports\n * only from `ai` (no React), so it's safe to use from any framework.\n *\n * @category Transport\n *\n * @example\n * ```ts\n * import { createSanityChatTransport } from 'content-agent/transport'\n *\n * const transport = createSanityChatTransport({\n * organizationId,\n * threadId,\n * baseURL: 'https://api.sanity.io',\n * apiVersion: 'v2026-01-12',\n * token: () => client.config().token,\n * prepareSendMessagesRequest: ({messages, body, headers}) => ({\n * headers: {\n * ...headers,\n * 'x-request-id': messages.findLast((m) => m.role === 'user')?.id,\n * },\n * body: {...body, application, context, stream: true, format: 'directives'},\n * }),\n * })\n * ```\n */\nexport function createSanityChatTransport<UI_MESSAGE extends UIMessage = UIMessage>(\n options: SanityChatTransportOptions<UI_MESSAGE>,\n): DefaultChatTransport<UI_MESSAGE> {\n const {\n organizationId,\n threadId,\n baseURL = DEFAULT_BASE_URL,\n apiVersion = DEFAULT_API_VERSION,\n token,\n credentials = 'include',\n fetch: customFetch,\n prepareSendMessagesRequest,\n } = options\n\n const apiBase = buildApiUrl(baseURL, apiVersion, `/${organizationId}/threads/${threadId}`)\n\n // Highest SSE `id:` seen on this transport — the resume cursor.\n let lastSeq = 0\n const recordSeq = (seq: number): void => {\n if (seq > lastSeq) lastSeq = seq\n }\n\n const trackingFetch = createCursorTrackingFetch(customFetch ?? globalThis.fetch, recordSeq)\n\n return new DefaultChatTransport<UI_MESSAGE>({\n api: apiBase,\n credentials,\n headers: () => buildAuthHeaders(token),\n fetch: trackingFetch,\n prepareSendMessagesRequest: withAuthHeaders(prepareSendMessagesRequest, token),\n prepareReconnectToStreamRequest: () => ({\n // `?afterSeq=N` skips already-processed frames (vercel/ai#14688). Auth\n // headers come from the transport's `headers` option, which the SDK\n // applies to reconnect requests when none are returned here.\n api: lastSeq > 0 ? `${apiBase}/stream?afterSeq=${lastSeq}` : `${apiBase}/stream`,\n }),\n })\n}\n\nfunction buildAuthHeaders(token: TokenSource): Record<string, string> {\n const resolved = typeof token === 'function' ? token() : token\n return resolved ? { Authorization: `Bearer ${resolved}` } : {}\n}\n\n/**\n * Wrap a consumer `prepareSendMessagesRequest` so the bearer header is always\n * present. The SDK replaces the transport headers when the hook returns a\n * `headers` key, which would otherwise drop auth if the consumer forgets to\n * spread the provided headers. Auth is the base; consumer values override.\n */\nfunction withAuthHeaders<UI_MESSAGE extends UIMessage>(\n prepare: HttpChatTransportInitOptions<UI_MESSAGE>['prepareSendMessagesRequest'],\n token: TokenSource,\n): HttpChatTransportInitOptions<UI_MESSAGE>['prepareSendMessagesRequest'] {\n if (!prepare) return prepare\n return async (options) => {\n const result = await prepare(options)\n // No `headers` key means the SDK falls back to the transport headers,\n // which already carry auth — leave it untouched.\n if (result.headers === undefined) return result\n return { ...result, headers: mergeHeaders(buildAuthHeaders(token), result.headers) }\n }\n}\n\nfunction mergeHeaders(base: Record<string, string>, override: HeadersInit): Record<string, string> {\n const merged: Record<string, string> = { ...base }\n new Headers(override).forEach((value, key) => {\n merged[key] = value\n })\n return merged\n}\n\n/**\n * Wraps a fetch implementation to tap SSE `id: N` lines from response bodies\n * and report the highest sequence via `onSeq`. The caller's body is untouched\n * (the stream is tee'd). Best-effort: tap errors are swallowed.\n */\nfunction createCursorTrackingFetch(\n baseFetch: typeof globalThis.fetch,\n onSeq: (seq: number) => void,\n): typeof globalThis.fetch {\n return async function trackingFetch(input, init) {\n const response = await baseFetch(input, init)\n\n // Fetch spec: the Response constructor throws for null-body statuses with\n // a body. Fastify 204s still carry a non-null stream, so check status too.\n if (!response.ok || !response.body || NULL_BODY_STATUSES.has(response.status)) {\n return response\n }\n\n const [bodyForCaller, bodyForTap] = response.body.tee()\n void tapIds(bodyForTap, onSeq).catch(() => {})\n\n return new Response(bodyForCaller, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n })\n }\n}\n\nasync function tapIds(\n body: ReadableStream<Uint8Array>,\n onSeq: (seq: number) => void,\n): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder('utf-8')\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 let nl = buffer.indexOf('\\n')\n while (nl !== -1) {\n recordIfIdLine(buffer.slice(0, nl), onSeq)\n buffer = buffer.slice(nl + 1)\n nl = buffer.indexOf('\\n')\n }\n }\n if (buffer) recordIfIdLine(buffer, onSeq)\n } finally {\n reader.releaseLock()\n }\n}\n\nfunction recordIfIdLine(line: string, onSeq: (seq: number) => void): void {\n const match = line.match(/^id: (\\d+)\\s*$/)\n if (!match) return\n const seq = Number(match[1])\n if (Number.isFinite(seq)) onSeq(seq)\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { c as AgentSettings, t as ContentAgentProvider } from "./_chunks-dts/sanity-agent-provider.js";
1
+ import { t as AgentSettings } from "./_chunks-dts/types.js";
2
+ import { t as ContentAgentProvider } from "./_chunks-dts/sanity-agent-provider.js";
2
3
  import { Output, ToolLoopAgent, ToolLoopAgentSettings, ToolSet } from "ai";
3
4
  /** Sanity-specific settings forwarded to `provider.agent(threadId, ...)`. */
4
5
  type ContentAgentModelSettings = Pick<AgentSettings, 'application' | 'config' | 'format' | 'clientId' | 'onStreamData'>;
@@ -1 +1 @@
1
- {"version":3,"file":"content-agent.d.ts","names":[],"sources":["../src/content-agent.ts"],"mappings":";;AAY4C;AAAA,KAMvC,yBAAA,GAA4B,IAAA,CAC/B,aAAA;;;;AASF;;KAAY,oBAAA,qCAEI,OAAA,GAAU,OAAA,iBACT,MAAA,CAAO,MAAA,YACpB,IAAA,CAAK,qBAAA,CAAsB,YAAA,EAAc,KAAA,EAAO,MAAA,cAClD,yBAAA;EAHc,wEAKZ,QAAA,EAAU,oBAAA,EAJG;EAMb,QAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;AAkHJ;;;;;;;;;;;;;;;;;;;cAAa,YAAA,qCAEG,OAAA,GAAU,OAAA,iBACT,MAAA,CAAO,MAAA,kBACd,aAAA,CAAc,YAAA,EAAc,KAAA,EAAO,MAAA;cAC/B,QAAA,EAAU,oBAAA,CAAqB,YAAA,EAAc,KAAA,EAAO,MAAA;AAAA"}
1
+ {"version":3,"file":"content-agent.d.ts","names":[],"sources":["../src/content-agent.ts"],"mappings":";;;;KAkBK,yBAAA,GAA4B,IAAA,CAC/B,aAAA;;;;;AASF;KAAY,oBAAA,qCAEI,OAAA,GAAU,OAAA,iBACT,MAAA,CAAO,MAAA,YACpB,IAAA,CAAK,qBAAA,CAAsB,YAAA,EAAc,KAAA,EAAO,MAAA,cAClD,yBAAA;EAL8B,wEAO5B,QAAA,EAAU,oBAAA,EALY;EAOtB,QAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkHJ;;;;;;;;;;;;;;;;;;cAAa,YAAA,qCAEG,OAAA,GAAU,OAAA,iBACT,MAAA,CAAO,MAAA,kBACd,aAAA,CAAc,YAAA,EAAc,KAAA,EAAO,MAAA;cAC/B,QAAA,EAAU,oBAAA,CAAqB,YAAA,EAAc,KAAA,EAAO,MAAA;AAAA"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { A as components, C as GenerateResult, D as OutputFormat, E as MessageRole, M as paths, O as UserMessageContext, S as GenerateRequest, T as Message, _ as Config, a as SanityClientLike, b as Filter, c as AgentSettings, d as Capabilities, f as Capability, g as ChatResponse, h as ChatRequest, i as SanityClientConfig, j as operations, k as ValidationIssue, l as Application, m as CapabilityPresetValue, n as createContentAgent, o as fromClient, p as CapabilityPreset, r as ClientDerivedConfig, s as getBaseUrl, t as ContentAgentProvider, u as ApplicationResource, v as ContentAgentProviderSettings, w as GenerateSettings, x as GenerateOptions, y as ErrorResponse } from "./_chunks-dts/sanity-agent-provider.js";
1
+ import { C as components, S as ValidationIssue, T as paths, _ as GenerateSettings, a as Capability, b as OutputFormat, c as ChatRequest, d as ContentAgentProviderSettings, f as ErrorResponse, g as GenerateResult, h as GenerateRequest, i as Capabilities, l as ChatResponse, m as GenerateOptions, n as Application, o as CapabilityPreset, p as Filter, r as ApplicationResource, s as CapabilityPresetValue, t as AgentSettings, u as Config, v as Message, w as operations, x as UserMessageContext, y as MessageRole } from "./_chunks-dts/types.js";
2
+ import { a as SanityClientLike, i as SanityClientConfig, n as createContentAgent, o as fromClient, r as ClientDerivedConfig, s as getBaseUrl, t as ContentAgentProvider } from "./_chunks-dts/sanity-agent-provider.js";
2
3
  import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3FinishReason, LanguageModelV3GenerateResult, LanguageModelV3StreamPart, LanguageModelV3StreamResult, SharedV3Warning } from "@ai-sdk/provider";
3
4
  import { ClientToolDefinition, HeadlessErrorResponse } from "@sanity/agent-types";
4
5
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/sanity-agent-language-model.ts","../src/studio.ts","../src/utils.ts"],"mappings":";;;;;AAyBA;UAAiB,uBAAA;EACf,QAAA;EACA,OAAA;EACA,UAAA;EACA,cAAA;EACA,OAAA,QAAe,MAAA;EACf,WAAA,GAAc,kBAAA;EACd,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;;;;;;;;AAoE5B;;;;;;;;;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,eAAA;EAAA,SAC/B,oBAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA;EAAA,SACA,aAAA,EAAe,MAAA,SAAe,MAAA;EAAA,iBAEtB,MAAA;EAAA,iBACA,QAAA;EAAA,iBACA,QAAA;cAGf,QAAA,UACA,QAAA,EAAU,aAAA,cACV,MAAA,EAAQ,uBAAA;EAAA;;;;;;;;;EAAA,QAkBF,uBAAA;EAkJ+C;;;;EAAA,QAzD/C,kBAAA;EAAA,QAiBA,eAAA;EAAA,QAoBA,gBAAA;EAAA,QAgBA,kBAAA;EAIF,UAAA,CAAW,OAAA,EAAS,0BAAA,GAA6B,OAAA,CAAQ,6BAAA;EAsFyB;;ACnV1F;;EDmV0F,QA9C1E,wBAAA;EA8CR,QAAA,CAAS,OAAA,EAAS,0BAAA,GAA6B,OAAA,CAAQ,2BAAA;AAAA;;AAlV/D;;KCDY,kBAAA;EDMK,uDCJf,KAAA,UAAe,UAAA,CAAW,KAAA;EDMX;;;;ECAf,eAAA,GAAkB,IAAA,CAAK,4BAAA;AAAA;;;;;;;;;;;iBAiIT,iBAAA,CACd,MAAA,EAAQ,gBAAA,EACR,SAAA,UACA,OAAA,GAAU,kBAAA,GACT,oBAAA;;;AD5IH;;;iBEXgB,YAAA,CACd,aAAA,EAAe,MAAA,8BACf,iBAAA,GAAoB,MAAA,mBACnB,MAAA;;;;;;iBAgBa,WAAA,CAAY,OAAA,UAAiB,UAAA,UAAoB,QAAA;;;;;;;;cAWpD,gBAAA,SAAyB,KAAA;EAAA,SAC3B,MAAA;EAAA,SACA,WAAA;EAAA,SACA,IAAA,EAAM,OAAA,CAAQ,qBAAA;cAEX,MAAA,UAAgB,OAAA;EAAA,eAQb,SAAA;AAAA;;;;;;;;;;;cAmBJ,sBAAA,SAA+B,KAAA;EAAA,SACjC,IAAA;cAEG,OAAA,UAAiB,IAAA;AAAA;AAAA,cAOlB,qBAAA;AAAA,KAED,cAAA;EACN,IAAA;AAAA;EACA,IAAA;AAAA;EACA,IAAA;AAAA;EACA,IAAA;AAAA;EACA,IAAA;AAAA;EACA,IAAA;AAAA;EACA,IAAA;AAAA;;;;;;;;iBA4BU,kBAAA,CAAmB,KAAA,YAAiB,cAAA;AAAA,iBAiBpC,iBAAA,CAAkB,UAAA,EAAY,cAAA;AAAA,KAIlC,wBAAA;EACV,OAAA;AAAA;;;;;;;ADzHF;;iBCoIsB,wBAAA,CACpB,UAAA,EAAY,cAAA,EACZ,KAAA,WACA,cAAA,SAAuB,OAAA,CAAQ,wBAAA,IAC9B,OAAA;;;;;;;iBAwBa,oBAAA,CAAqB,KAAA;AAAA,UAUpB,YAAA;EACf,GAAA;EACA,MAAA;EACA,IAAA;EACA,OAAA,EAAS,MAAA;EACT,WAAA,GAAc,kBAAA;EACd,MAAA,GAAS,WAAA;EACT,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;;;iBASN,QAAA,CAAS,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,QAAA;;;;;;iBA0B/C,oBAAA,CACd,OAAA,WAAkB,KAAA;EAAQ,IAAA;EAAc,IAAA;AAAA;;;;;;iBAyC1B,eAAA,CAAgB,MAAA,uBAA6B,2BAAA;AAAA,KAiBjD,gBAAA;EACN,IAAA;EAAc,IAAA,EAAM,yBAAA;AAAA;EACpB,IAAA;EAAgB,YAAA,EAAc,2BAAA;AAAA;EAC9B,IAAA;EAAc,IAAA;EAAc,IAAA;AAAA;;;;;;;;;;;;;;;;;;;;AA9NlC;;;;;iBAwPgB,eAAA,CAAgB,IAAA,UAAc,aAAA,WAAwB,gBAAA;;;;;;AA9OtE;iBAiXsB,qBAAA,CACpB,YAAA,EAAc,cAAA,CAAe,UAAA,IAC5B,OAAA;;KAuBS,sBAAA;EA1YsB;AAElC;;EA4YE,MAAA,IAAU,IAAA,UAAc,IAAA;EA5YA;;;;;;EAmZxB,SAAA,IAAa,QAAA,aAAqB,OAAA,CAAQ,QAAA;EAC1C,UAAA;AAAA;;AAjXF;;;;;AAiBA;;;iBAsYgB,eAAA,CACd,YAAA,EAAc,cAAA,CAAe,UAAA,GAC7B,QAAA,EAAU,eAAA,IACV,eAAA,GAAkB,sBAAA,KAA2B,IAAA,UAAc,IAAA,sBAC1D,cAAA,CAAe,yBAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/sanity-agent-language-model.ts","../src/studio.ts","../src/utils.ts"],"mappings":"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"}