@templatical/ai-kit 0.50.0 → 0.51.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.
@@ -1 +1,3 @@
1
- export { i as AI_TEMPLATE_CHANGED_MESSAGE, t as createHttpAiProvider, a as errorFromEvent, o as errorFromResponse, s as parseRetryAfter, r as readSse } from "../http-provider-BoRkCd7H.js";
1
+ import { i as parseRetryAfter, n as errorFromEvent, r as errorFromResponse, t as AI_TEMPLATE_CHANGED_MESSAGE } from "../errors-DWshWx18.js";
2
+ import { r as readSse, t as createHttpAiProvider } from "../http-provider-Cx2ZcLRB.js";
3
+ export { AI_TEMPLATE_CHANGED_MESSAGE, createHttpAiProvider, errorFromEvent, errorFromResponse, parseRetryAfter, readSse };
@@ -0,0 +1,55 @@
1
+ import { n as AI_ERROR_STATUS, t as AI_ERROR_MESSAGES } from "./wire-B6AF_UGT.js";
2
+ import { CollaborationConflict, SdkError, ThrottledError } from "@templatical/types";
3
+ //#region src/client/errors.ts
4
+ /** Shown when the editor refused an `operations` event (§4). */
5
+ const AI_TEMPLATE_CHANGED_MESSAGE = "The template changed while the AI was working.";
6
+ const CONFLICT_CODES = /* @__PURE__ */ new Set([
7
+ "block_exists",
8
+ "block_missing",
9
+ "anchor_missing",
10
+ "precondition_failed",
11
+ "lock_held",
12
+ "locked_by_others",
13
+ "invalid_operation"
14
+ ]);
15
+ const DEFAULT_RETRY_AFTER_MS = 1e3;
16
+ /** `Retry-After` (seconds or an HTTP date) in milliseconds; a second when unreadable. */
17
+ function parseRetryAfter(header, now = Date.now()) {
18
+ if (header === null || header.trim() === "") return DEFAULT_RETRY_AFTER_MS;
19
+ const seconds = Number(header);
20
+ if (Number.isFinite(seconds)) return Math.max(0, seconds * 1e3);
21
+ const date = Date.parse(header);
22
+ return Number.isNaN(date) ? DEFAULT_RETRY_AFTER_MS : Math.max(0, date - now);
23
+ }
24
+ function build(status, body, retryAfterMs) {
25
+ const { message: rawMessage, errors, code: rawCode, opIndex, holder, holders, type: _type, ...details } = body;
26
+ const firstValidation = typeof errors === "object" && errors !== null ? Object.values(errors)[0]?.[0] : void 0;
27
+ const message = typeof firstValidation === "string" ? firstValidation : typeof rawMessage === "string" && rawMessage !== "" ? rawMessage : AI_ERROR_MESSAGES.ai_provider_error;
28
+ const code = typeof rawCode === "string" ? rawCode : void 0;
29
+ if (status === 429 || code === "rate_limited") return new ThrottledError(message, retryAfterMs, code ?? "rate_limited", details);
30
+ if (code !== void 0 && CONFLICT_CODES.has(code)) return new CollaborationConflict(message, {
31
+ code,
32
+ ...status !== void 0 ? { status } : {},
33
+ ...typeof opIndex === "number" ? { opIndex } : {},
34
+ ...holder ? { holder } : {},
35
+ ...Array.isArray(holders) ? { holders } : {}
36
+ });
37
+ return new SdkError(message, status, code, details);
38
+ }
39
+ /** A refusal before the stream: `{ message, code, …details }` with its status. */
40
+ async function errorFromResponse(response) {
41
+ const body = await response.json().catch(() => ({}));
42
+ return build(response.status, typeof body === "object" && body !== null ? body : {}, parseRetryAfter(response.headers.get("Retry-After")));
43
+ }
44
+ /** A stream's `error` event, with the status its code has before a stream (S7 D2). */
45
+ function errorFromEvent(event) {
46
+ return build(AI_ERROR_STATUS[event.code], event, DEFAULT_RETRY_AFTER_MS);
47
+ }
48
+ /** The provider's failure: a cut stream, a network error, a malformed answer. */
49
+ function providerFailure() {
50
+ return new SdkError(AI_ERROR_MESSAGES.ai_provider_error, 502, "ai_provider_error");
51
+ }
52
+ //#endregion
53
+ export { providerFailure as a, parseRetryAfter as i, errorFromEvent as n, errorFromResponse as r, AI_TEMPLATE_CHANGED_MESSAGE as t };
54
+
55
+ //# sourceMappingURL=errors-DWshWx18.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-DWshWx18.js","names":[],"sources":["../src/client/errors.ts"],"sourcesContent":["import type { CollaborationConflictCode } from \"@templatical/types\";\nimport {\n CollaborationConflict,\n SdkError,\n ThrottledError,\n} from \"@templatical/types\";\nimport { AI_ERROR_MESSAGES, AI_ERROR_STATUS, type AiErrorEvent } from \"../wire\";\n\n/** Shown when the editor refused an `operations` event (§4). */\nexport const AI_TEMPLATE_CHANGED_MESSAGE =\n \"The template changed while the AI was working.\";\n\nconst CONFLICT_CODES: ReadonlySet<string> = new Set([\n \"block_exists\",\n \"block_missing\",\n \"anchor_missing\",\n \"precondition_failed\",\n \"lock_held\",\n \"locked_by_others\",\n \"invalid_operation\",\n]);\n\nconst DEFAULT_RETRY_AFTER_MS = 1000;\n\n/** `Retry-After` (seconds or an HTTP date) in milliseconds; a second when unreadable. */\nexport function parseRetryAfter(\n header: string | null,\n now = Date.now(),\n): number {\n if (header === null || header.trim() === \"\") return DEFAULT_RETRY_AFTER_MS;\n const seconds = Number(header);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const date = Date.parse(header);\n return Number.isNaN(date) ? DEFAULT_RETRY_AFTER_MS : Math.max(0, date - now);\n}\n\nfunction build(\n status: number | undefined,\n body: Record<string, unknown>,\n retryAfterMs: number,\n): SdkError {\n const {\n message: rawMessage,\n errors,\n code: rawCode,\n opIndex,\n holder,\n holders,\n type: _type,\n ...details\n } = body;\n const firstValidation =\n typeof errors === \"object\" && errors !== null\n ? (\n Object.values(errors as Record<string, unknown>)[0] as\n unknown[] | undefined\n )?.[0]\n : undefined;\n const message =\n typeof firstValidation === \"string\"\n ? firstValidation\n : typeof rawMessage === \"string\" && rawMessage !== \"\"\n ? rawMessage\n : AI_ERROR_MESSAGES.ai_provider_error;\n const code = typeof rawCode === \"string\" ? rawCode : undefined;\n if (status === 429 || code === \"rate_limited\") {\n return new ThrottledError(\n message,\n retryAfterMs,\n code ?? \"rate_limited\",\n details,\n );\n }\n if (code !== undefined && CONFLICT_CODES.has(code)) {\n return new CollaborationConflict(message, {\n code: code as CollaborationConflictCode,\n ...(status !== undefined ? { status } : {}),\n ...(typeof opIndex === \"number\" ? { opIndex } : {}),\n ...(holder ? { holder: holder as never } : {}),\n ...(Array.isArray(holders) ? { holders: holders as never } : {}),\n });\n }\n return new SdkError(message, status, code, details);\n}\n\n/** A refusal before the stream: `{ message, code, …details }` with its status. */\nexport async function errorFromResponse(response: Response): Promise<SdkError> {\n const body = (await response.json().catch(() => ({}))) as unknown;\n return build(\n response.status,\n typeof body === \"object\" && body !== null\n ? (body as Record<string, unknown>)\n : {},\n parseRetryAfter(response.headers.get(\"Retry-After\")),\n );\n}\n\n/** A stream's `error` event, with the status its code has before a stream (S7 D2). */\nexport function errorFromEvent(event: AiErrorEvent): SdkError {\n return build(AI_ERROR_STATUS[event.code], event, DEFAULT_RETRY_AFTER_MS);\n}\n\n/** The provider's failure: a cut stream, a network error, a malformed answer. */\nexport function providerFailure(): SdkError {\n return new SdkError(\n AI_ERROR_MESSAGES.ai_provider_error,\n 502,\n \"ai_provider_error\",\n );\n}\n"],"mappings":";;;;AASA,MAAa,8BACX;AAEF,MAAM,iCAAsC,IAAI,IAAI;CAClD;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,yBAAyB;;AAG/B,SAAgB,gBACd,QACA,MAAM,KAAK,IAAI,GACP;CACR,IAAI,WAAW,QAAQ,OAAO,KAAK,MAAM,IAAI,OAAO;CACpD,MAAM,UAAU,OAAO,MAAM;CAC7B,IAAI,OAAO,SAAS,OAAO,GAAG,OAAO,KAAK,IAAI,GAAG,UAAU,GAAI;CAC/D,MAAM,OAAO,KAAK,MAAM,MAAM;CAC9B,OAAO,OAAO,MAAM,IAAI,IAAI,yBAAyB,KAAK,IAAI,GAAG,OAAO,GAAG;AAC7E;AAEA,SAAS,MACP,QACA,MACA,cACU;CACV,MAAM,EACJ,SAAS,YACT,QACA,MAAM,SACN,SACA,QACA,SACA,MAAM,OACN,GAAG,YACD;CACJ,MAAM,kBACJ,OAAO,WAAW,YAAY,WAAW,OAEnC,OAAO,OAAO,MAAiC,CAAC,CAAC,EAAE,GAEjD,KACJ,KAAA;CACN,MAAM,UACJ,OAAO,oBAAoB,WACvB,kBACA,OAAO,eAAe,YAAY,eAAe,KAC/C,aACA,kBAAkB;CAC1B,MAAM,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;CACrD,IAAI,WAAW,OAAO,SAAS,gBAC7B,OAAO,IAAI,eACT,SACA,cACA,QAAQ,gBACR,OACF;CAEF,IAAI,SAAS,KAAA,KAAa,eAAe,IAAI,IAAI,GAC/C,OAAO,IAAI,sBAAsB,SAAS;EAClC;EACN,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;EACzC,GAAI,OAAO,YAAY,WAAW,EAAE,QAAQ,IAAI,CAAC;EACjD,GAAI,SAAS,EAAU,OAAgB,IAAI,CAAC;EAC5C,GAAI,MAAM,QAAQ,OAAO,IAAI,EAAW,QAAiB,IAAI,CAAC;CAChE,CAAC;CAEH,OAAO,IAAI,SAAS,SAAS,QAAQ,MAAM,OAAO;AACpD;;AAGA,eAAsB,kBAAkB,UAAuC;CAC7E,MAAM,OAAQ,MAAM,SAAS,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE;CACpD,OAAO,MACL,SAAS,QACT,OAAO,SAAS,YAAY,SAAS,OAChC,OACD,CAAC,GACL,gBAAgB,SAAS,QAAQ,IAAI,aAAa,CAAC,CACrD;AACF;;AAGA,SAAgB,eAAe,OAA+B;CAC5D,OAAO,MAAM,gBAAgB,MAAM,OAAO,OAAO,sBAAsB;AACzE;;AAGA,SAAgB,kBAA4B;CAC1C,OAAO,IAAI,SACT,kBAAkB,mBAClB,KACA,mBACF;AACF"}
@@ -1,55 +1,6 @@
1
- import { a as AI_ROUTES, l as aiRoutePath, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES } from "./wire-B6AF_UGT.js";
2
- import { CollaborationConflict, SdkError, ThrottledError } from "@templatical/types";
3
- //#region src/client/errors.ts
4
- /** Shown when the editor refused an `operations` event (§4). */
5
- const AI_TEMPLATE_CHANGED_MESSAGE = "The template changed while the AI was working.";
6
- const CONFLICT_CODES = /* @__PURE__ */ new Set([
7
- "block_exists",
8
- "block_missing",
9
- "anchor_missing",
10
- "precondition_failed",
11
- "lock_held",
12
- "locked_by_others",
13
- "invalid_operation"
14
- ]);
15
- const DEFAULT_RETRY_AFTER_MS = 1e3;
16
- /** `Retry-After` (seconds or an HTTP date) in milliseconds; a second when unreadable. */
17
- function parseRetryAfter(header, now = Date.now()) {
18
- if (header === null || header.trim() === "") return DEFAULT_RETRY_AFTER_MS;
19
- const seconds = Number(header);
20
- if (Number.isFinite(seconds)) return Math.max(0, seconds * 1e3);
21
- const date = Date.parse(header);
22
- return Number.isNaN(date) ? DEFAULT_RETRY_AFTER_MS : Math.max(0, date - now);
23
- }
24
- function build(status, body, retryAfterMs) {
25
- const { message: rawMessage, errors, code: rawCode, opIndex, holder, holders, type: _type, ...details } = body;
26
- const firstValidation = typeof errors === "object" && errors !== null ? Object.values(errors)[0]?.[0] : void 0;
27
- const message = typeof firstValidation === "string" ? firstValidation : typeof rawMessage === "string" && rawMessage !== "" ? rawMessage : AI_ERROR_MESSAGES.ai_provider_error;
28
- const code = typeof rawCode === "string" ? rawCode : void 0;
29
- if (status === 429 || code === "rate_limited") return new ThrottledError(message, retryAfterMs, code ?? "rate_limited", details);
30
- if (code !== void 0 && CONFLICT_CODES.has(code)) return new CollaborationConflict(message, {
31
- code,
32
- ...status !== void 0 ? { status } : {},
33
- ...typeof opIndex === "number" ? { opIndex } : {},
34
- ...holder ? { holder } : {},
35
- ...Array.isArray(holders) ? { holders } : {}
36
- });
37
- return new SdkError(message, status, code, details);
38
- }
39
- /** A refusal before the stream: `{ message, code, …details }` with its status. */
40
- async function errorFromResponse(response) {
41
- const body = await response.json().catch(() => ({}));
42
- return build(response.status, typeof body === "object" && body !== null ? body : {}, parseRetryAfter(response.headers.get("Retry-After")));
43
- }
44
- /** A stream's `error` event, with the status its code has before a stream (S7 D2). */
45
- function errorFromEvent(event) {
46
- return build(AI_ERROR_STATUS[event.code], event, DEFAULT_RETRY_AFTER_MS);
47
- }
48
- /** The provider's failure: a cut stream, a network error, a malformed answer. */
49
- function providerFailure() {
50
- return new SdkError(AI_ERROR_MESSAGES.ai_provider_error, 502, "ai_provider_error");
51
- }
52
- //#endregion
1
+ import { a as AI_ROUTES, l as aiRoutePath, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER } from "./wire-B6AF_UGT.js";
2
+ import { a as providerFailure, n as errorFromEvent, r as errorFromResponse, t as AI_TEMPLATE_CHANGED_MESSAGE } from "./errors-DWshWx18.js";
3
+ import { CollaborationConflict, SdkError } from "@templatical/types";
53
4
  //#region src/client/sse.ts
54
5
  const TYPES = /* @__PURE__ */ new Set([
55
6
  "text",
@@ -322,6 +273,6 @@ function createHttpAiProviderWithFetch(options, fetchRequest) {
322
273
  };
323
274
  }
324
275
  //#endregion
325
- export { errorFromEvent as a, AI_TEMPLATE_CHANGED_MESSAGE as i, createHttpAiProviderWithFetch as n, errorFromResponse as o, readSse as r, parseRetryAfter as s, createHttpAiProvider as t };
276
+ export { createHttpAiProviderWithFetch as n, readSse as r, createHttpAiProvider as t };
326
277
 
327
- //# sourceMappingURL=http-provider-BoRkCd7H.js.map
278
+ //# sourceMappingURL=http-provider-Cx2ZcLRB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-provider-Cx2ZcLRB.js","names":[],"sources":["../src/client/sse.ts","../src/client/http-provider.ts"],"sourcesContent":["import type { AiWireEvent } from \"../wire\";\n\nconst TYPES = new Set([\"text\", \"operations\", \"applied\", \"done\", \"error\"]);\n\nfunction parse(data: string): AiWireEvent | null {\n try {\n const event = JSON.parse(data) as { type?: unknown };\n return typeof event === \"object\" &&\n event !== null &&\n typeof event.type === \"string\" &&\n TYPES.has(event.type)\n ? (event as AiWireEvent)\n : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The wire's events from an SSE body: `data:` lines, joined per event and\n * dispatched at a blank line or the end; comments (`: ping`), other fields,\n * unparseable data and unknown types are skipped. Cancelling the generator\n * cancels the body.\n */\nexport async function* readSse(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<AiWireEvent> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let data: string[] = [];\n\n function* dispatch(): Generator<AiWireEvent> {\n if (data.length === 0) return;\n const event = parse(data.join(\"\\n\"));\n data = [];\n if (event) yield event;\n }\n\n function* lines(text: string): Generator<AiWireEvent> {\n for (const raw of text.split(\"\\n\")) {\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n if (line === \"\") {\n yield* dispatch();\n } else if (line.startsWith(\"data:\")) {\n data.push(line.slice(line.startsWith(\"data: \") ? 6 : 5));\n }\n }\n }\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const end = buffer.lastIndexOf(\"\\n\");\n if (end === -1) continue;\n const complete = buffer.slice(0, end);\n buffer = buffer.slice(end + 1);\n yield* lines(complete);\n }\n buffer += decoder.decode();\n if (buffer !== \"\") yield* lines(buffer);\n yield* dispatch();\n } finally {\n reader.cancel().catch(() => {});\n }\n}\n","import type {\n AiFeatureName,\n AiProvider,\n AiReplay,\n AiReplayOutcome,\n AiRequestBase,\n AiTurnHandlers,\n AiTurnResult,\n} from \"@templatical/types\";\nimport { CollaborationConflict, SdkError } from \"@templatical/types\";\nimport {\n AI_GENERATE_FIELDS,\n AI_ROUTES,\n AI_SESSION_HEADER,\n AI_WIRE_HEADER,\n AI_WIRE_VERSION,\n aiRoutePath,\n type AiDoneEvent,\n type AiRouteName,\n type AiWireEvent,\n} from \"../wire\";\nimport {\n AI_TEMPLATE_CHANGED_MESSAGE,\n errorFromEvent,\n errorFromResponse,\n providerFailure,\n} from \"./errors\";\nimport { readSse } from \"./sse\";\n\nexport type HttpAiFeatures = Partial<\n Record<AiFeatureName | \"conversation\", boolean>\n>;\n\nexport interface HttpAiProviderOptions {\n /** Where the wire's routes live: `{baseUrl}/templates/{t}/ai/…`. */\n baseUrl: string;\n /** Your auth headers, or a function (async for a token refresh) called per request. */\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n /** `fetch`'s `credentials`, for cookie auth. */\n credentials?: RequestCredentials;\n /** `false` turns a feature off, `conversation` included. Everything else is on. */\n features?: HttpAiFeatures;\n}\n\nfunction abortError(signal: AbortSignal): Error {\n const reason = signal.reason as unknown;\n return reason instanceof Error && reason.name === \"AbortError\"\n ? reason\n : new DOMException(\"The AI request was stopped.\", \"AbortError\");\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * An `AiProvider` over the wire (A1 §4): JSON or multipart requests, SSE\n * answers for turns and rewrite, JSON for history.\n * Errors are `SdkError`s; stopping a call rejects with an `AbortError`.\n */\nexport function createHttpAiProvider(\n options: HttpAiProviderOptions,\n): AiProvider {\n return createHttpAiProviderWithFetch(options, (input, init) =>\n fetch(input, init),\n );\n}\n\n/** @internal A per-instance transport for wire conformance; not a package export. */\nexport function createHttpAiProviderWithFetch(\n options: HttpAiProviderOptions,\n fetchRequest: typeof fetch,\n): AiProvider {\n const base = options.baseUrl.replace(/\\/+$/, \"\");\n const on = (feature: AiFeatureName | \"conversation\") =>\n options.features?.[feature] !== false;\n\n async function request(\n route: AiRouteName,\n params: { template: string; message?: string; replayId?: string },\n init: {\n json?: unknown;\n form?: FormData;\n session?: string;\n stream?: boolean;\n signal?: AbortSignal;\n },\n ): Promise<Response> {\n init.signal?.throwIfAborted();\n const own =\n typeof options.headers === \"function\"\n ? await options.headers()\n : (options.headers ?? {});\n const headers = new Headers(own);\n headers.set(AI_WIRE_HEADER, AI_WIRE_VERSION);\n headers.set(\n \"Accept\",\n init.stream ? \"text/event-stream\" : \"application/json\",\n );\n if (init.session) headers.set(AI_SESSION_HEADER, init.session);\n let body: BodyInit | undefined;\n if (init.form) {\n body = init.form;\n } else if (init.json !== undefined) {\n headers.set(\"Content-Type\", \"application/json\");\n body = JSON.stringify(init.json);\n }\n let response: Response;\n try {\n init.signal?.throwIfAborted();\n response = await fetchRequest(`${base}${aiRoutePath(route, params)}`, {\n method: AI_ROUTES[route].method,\n headers,\n body,\n ...(options.credentials ? { credentials: options.credentials } : {}),\n signal: init.signal,\n });\n } catch (error) {\n if (init.signal?.aborted) throw abortError(init.signal);\n throw error instanceof SdkError ? error : providerFailure();\n }\n if (!response.ok) throw await errorFromResponse(response);\n return response;\n }\n\n /**\n * One streamed call. `each` handles every event before `done`; it may\n * throw to end the call (a refusal, the editor's own error), which aborts\n * the request.\n */\n async function streamed<T>(\n route: AiRouteName,\n template: string,\n init: { json?: unknown; form?: FormData; session?: string },\n signal: AbortSignal,\n // Never called with `done` or `error`: those end the call first.\n each: (event: AiWireEvent) => Promise<void> | void,\n done: (event: AiDoneEvent) => T | undefined,\n ): Promise<T> {\n const controller = new AbortController();\n const stop = () => controller.abort(signal.reason);\n if (signal.aborted) stop();\n signal.addEventListener(\"abort\", stop, { once: true });\n try {\n const response = await request(\n route,\n { template },\n {\n ...init,\n stream: true,\n signal: controller.signal,\n },\n );\n if (!response.body) throw providerFailure();\n for await (const event of readSse(response.body)) {\n if (signal.aborted) {\n // Already received server edits still need reconciliation after Stop.\n if (event.type === \"applied\") await each(event);\n if (event.type === \"done\" || event.type === \"error\")\n throw abortError(signal);\n continue;\n }\n if (event.type === \"error\") throw errorFromEvent(event);\n if (event.type === \"done\") {\n const value = done(event);\n if (value === undefined) throw providerFailure();\n return value;\n }\n await each(event);\n }\n throw providerFailure();\n } catch (error) {\n if (signal.aborted) throw abortError(signal);\n controller.abort();\n if (error instanceof SdkError) throw error;\n if (error instanceof Error && error.name === \"AbortError\") throw error;\n if (error instanceof Error && !(error instanceof TypeError)) throw error;\n throw providerFailure();\n } finally {\n signal.removeEventListener(\"abort\", stop);\n }\n }\n\n function turn(\n route: \"generate\" | \"fixFinding\",\n req: AiRequestBase,\n init: { json?: unknown; form?: FormData },\n handlers: AiTurnHandlers,\n signal: AbortSignal,\n ): Promise<AiTurnResult> {\n let text = \"\";\n return streamed(\n route,\n req.templateId,\n { ...init, session: req.sessionId },\n signal,\n async (event) => {\n if (event.type === \"text\") {\n text += event.text;\n handlers.onText(event.text);\n } else if (event.type === \"operations\") {\n const result = await handlers.onOperations(event.ops);\n if (!result.applied) {\n // SSE is one-way: the backend can't be told mid-turn, so the\n // request ends here with the operation's own code (§4).\n throw new CollaborationConflict(AI_TEMPLATE_CHANGED_MESSAGE, {\n code: result.code,\n opIndex: result.opIndex,\n });\n }\n } else if (event.type === \"applied\") {\n handlers.onApplied({\n batchId: event.batchId,\n revision: event.revision,\n operationCount: event.operationCount,\n });\n }\n },\n (done) =>\n typeof done.messageId === \"string\" || done.messageId === null\n ? { messageId: done.messageId, text }\n : undefined,\n );\n }\n\n async function json<T>(response: Response): Promise<T> {\n try {\n return (await response.json()) as T;\n } catch {\n throw providerFailure();\n }\n }\n\n async function replay(\n route: \"undoMessage\" | \"redoMessage\",\n templateId: string,\n messageId: string,\n sessionId: string,\n ): Promise<AiReplay> {\n const response = await request(\n route,\n { template: templateId, message: messageId },\n { session: sessionId },\n );\n const body = await json<{\n data?: {\n operations?: unknown;\n replayId?: unknown;\n batchId?: unknown;\n revision?: unknown;\n };\n }>(response);\n if (!isRecord(body)) throw providerFailure();\n const data = body.data ?? {};\n if (\n Array.isArray(data.operations) &&\n typeof data.replayId === \"string\" &&\n data.replayId !== \"\"\n )\n return { operations: data.operations, replayId: data.replayId };\n if (typeof data.batchId === \"string\" && typeof data.revision === \"number\") {\n return { applied: { batchId: data.batchId, revision: data.revision } };\n }\n throw providerFailure();\n }\n\n return {\n chatAttachments: on(\"chat\") && on(\"chatAttachments\"),\n chat: on(\"chat\")\n ? (req, handlers, signal) => {\n const body = {\n message: req.message,\n selectedBlockId: req.selectedBlockId,\n mergeTags: req.mergeTags,\n content: req.content,\n revision: req.revision,\n };\n if (req.attachments.length === 0)\n return turn(\"generate\", req, { json: body }, handlers, signal);\n const form = new FormData();\n form.set(AI_GENERATE_FIELDS.message, req.message);\n if (req.selectedBlockId !== null)\n form.set(AI_GENERATE_FIELDS.selectedBlockId, req.selectedBlockId);\n form.set(AI_GENERATE_FIELDS.mergeTags, JSON.stringify(req.mergeTags));\n form.set(AI_GENERATE_FIELDS.content, JSON.stringify(req.content));\n if (req.revision !== null)\n form.set(AI_GENERATE_FIELDS.revision, String(req.revision));\n for (const attachment of req.attachments)\n form.append(\n AI_GENERATE_FIELDS.attachment,\n attachment.file,\n attachment.name,\n );\n return turn(\"generate\", req, { form }, handlers, signal);\n }\n : false,\n fixFinding: on(\"fixFinding\")\n ? (req, handlers, signal) =>\n turn(\n \"fixFinding\",\n req,\n {\n json: {\n finding: req.finding,\n mergeTags: req.mergeTags,\n content: req.content,\n revision: req.revision,\n },\n },\n handlers,\n signal,\n )\n : false,\n rewrite: on(\"rewrite\")\n ? (req, handlers, signal) =>\n streamed(\n \"rewriteText\",\n req.templateId,\n {\n json: {\n html: req.html,\n preset: req.preset,\n language: req.language,\n instruction: req.instruction,\n mergeTags: req.mergeTags,\n },\n },\n signal,\n (event) => {\n if (event.type === \"text\") handlers.onText(event.text);\n },\n (done) =>\n typeof done.html === \"string\" ? { html: done.html } : undefined,\n )\n : false,\n altText: on(\"altText\")\n ? (req, signal) => {\n const { templateId, sessionId, ...body } = req;\n return streamed(\n \"altText\",\n templateId,\n { json: body, session: sessionId },\n signal,\n () => {},\n (done) =>\n typeof done.alt === \"string\" ? { alt: done.alt } : undefined,\n );\n }\n : false,\n ...(on(\"conversation\")\n ? {\n conversation: {\n settleReplay: async (\n templateId: string,\n messageId: string,\n outcome: AiReplayOutcome,\n ) => {\n await request(\n \"settleReplay\",\n {\n template: templateId,\n message: messageId,\n replayId: outcome.replayId,\n },\n {\n session: outcome.sessionId,\n json: {\n direction: outcome.direction,\n applied: outcome.applied,\n },\n },\n );\n },\n list: async (templateId: string) => {\n const response = await request(\n \"conversationMessages\",\n { template: templateId },\n {},\n );\n const body = await json<{ data?: unknown }>(response);\n if (!isRecord(body)) throw providerFailure();\n if (!Array.isArray(body.data)) throw providerFailure();\n return body.data;\n },\n clear: async (templateId: string) => {\n await request(\"clearConversation\", { template: templateId }, {});\n },\n undo: (\n templateId: string,\n messageId: string,\n context: { sessionId: string },\n ) =>\n replay(\"undoMessage\", templateId, messageId, context.sessionId),\n redo: (\n templateId: string,\n messageId: string,\n context: { sessionId: string },\n ) =>\n replay(\"redoMessage\", templateId, messageId, context.sessionId),\n },\n }\n : {}),\n };\n}\n"],"mappings":"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"}
@@ -36,30 +36,40 @@ type AiModelMessage = {
36
36
  role: "assistant";
37
37
  content: string;
38
38
  toolCalls?: AiModelToolCall[];
39
+ /** What the adapter yielded as `state` for this step; it replays its own. */
40
+ state?: unknown;
39
41
  } | {
40
42
  role: "tool";
41
43
  toolCallId: string;
42
44
  name: string;
43
45
  content: string;
44
46
  };
47
+ interface AiUsage {
48
+ /** Input tokens billed at the full rate: not read from or written to a cache. */
49
+ inputTokens: number;
50
+ outputTokens: number;
51
+ /** Input tokens read from the provider's prompt cache, when it reports them. */
52
+ cacheReadInputTokens?: number;
53
+ /** Input tokens written to the provider's prompt cache, when it reports them. */
54
+ cacheWriteInputTokens?: number;
55
+ }
45
56
  type AiModelPart = {
46
57
  type: "text";
47
58
  text: string;
48
59
  } | ({
49
60
  type: "tool-call";
50
- } & AiModelToolCall) | {
61
+ } & AiModelToolCall) |
62
+ /** Opaque to the kit: the adapter's own record of the step (e.g. thinking), JSON-safe. */
63
+ {
64
+ type: "state";
65
+ state: unknown;
66
+ } | ({
51
67
  type: "usage";
52
- inputTokens: number;
53
- outputTokens: number;
54
- };
55
- interface AiUsage {
56
- inputTokens: number;
57
- outputTokens: number;
58
- }
68
+ } & AiUsage);
59
69
  /**
60
70
  * Your model, behind one method: stream a reply to `messages` with `system` as
61
- * the system prompt and `tools` offered. About twenty lines for the Vercel AI
62
- * SDK, OpenAI or Anthropic (see the guide). Honour `signal`.
71
+ * the system prompt and `tools` offered. `@templatical/ai-kit/adapters/*`
72
+ * implements it for Anthropic, OpenAI and the Vercel AI SDK. Honour `signal`.
63
73
  */
64
74
  interface AiModelAdapter {
65
75
  stream(input: {
@@ -71,4 +81,4 @@ interface AiModelAdapter {
71
81
  }
72
82
  //#endregion
73
83
  export { AiModelToolCall as a, applyOperationsTool as c, AiModelPart as i, AiModelAttachment as n, AiUsage as o, AiModelMessage as r, AiToolDefinition as s, AiModelAdapter as t };
74
- //# sourceMappingURL=model-BuHDCTlM.d.ts.map
84
+ //# sourceMappingURL=model-xJvGTfLB.d.ts.map
@@ -1,5 +1,5 @@
1
+ import { a as AiModelToolCall, c as applyOperationsTool, i as AiModelPart, n as AiModelAttachment, o as AiUsage, r as AiModelMessage, s as AiToolDefinition, t as AiModelAdapter } from "../model-xJvGTfLB.js";
1
2
  import { C as AiWireEvent, h as AiGenerateBody, l as AiAltTextBody, m as AiFixFindingBody, p as AiErrorEvent, v as AiRewriteBody } from "../wire-Cv7Fk-7n.js";
2
- import { a as AiModelToolCall, c as applyOperationsTool, i as AiModelPart, n as AiModelAttachment, o as AiUsage, r as AiModelMessage, s as AiToolDefinition, t as AiModelAdapter } from "../model-BuHDCTlM.js";
3
3
  import { AiAltTextRequest, AiFixFinding, AiMergeTagSyntax, AiMergeTags, AiRewritePreset, Operation, OperationError, SdkError, TemplateContent, ThrottledError } from "@templatical/types";
4
4
  //#region src/generated/assets.d.ts
5
5
  export declare const FIX_RULE_GUIDANCE: {
@@ -1,5 +1,5 @@
1
1
  import { i as AI_LIMITS, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as matchAiRoute } from "../wire-B6AF_UGT.js";
2
- import { C as mergeTagTokens, E as FIX_RULE_GUIDANCE, S as flattenMergeTags, T as unknownMergeTags, _ as buildEditingPrompt, a as checkRewrite, b as editingInstructions, c as createEditingSession, d as opRefusal, f as contentRuleRefusal, g as buildChatRequest, h as buildAltTextRequest, i as checkAltTextAnswer, l as applyOperationsTool, m as REWRITE_PRESET_INSTRUCTIONS, n as buildRewriteRepairPrompt, o as runEditingTurn, p as walkBlocks, r as checkAltText, s as EDITING_SESSION_LIMITS, t as buildAltTextRepairRequest, u as describeOp, v as buildFixRequest, w as mergeTagTokensIn, x as rewriteInstructions, y as buildRewritePrompt } from "../checks-Blbakkgc.js";
2
+ import { C as mergeTagTokens, E as FIX_RULE_GUIDANCE, S as flattenMergeTags, T as unknownMergeTags, _ as buildEditingPrompt, a as checkRewrite, b as editingInstructions, c as createEditingSession, d as opRefusal, f as contentRuleRefusal, g as buildChatRequest, h as buildAltTextRequest, i as checkAltTextAnswer, l as applyOperationsTool, m as REWRITE_PRESET_INSTRUCTIONS, n as buildRewriteRepairPrompt, o as runEditingTurn, p as walkBlocks, r as checkAltText, s as EDITING_SESSION_LIMITS, t as buildAltTextRepairRequest, u as describeOp, v as buildFixRequest, w as mergeTagTokensIn, x as rewriteInstructions, y as buildRewritePrompt } from "../checks-DvLD3892.js";
3
3
  import { CollaborationConflict, SdkError, SdkError as SdkError$1, ThrottledError, ThrottledError as ThrottledError$1 } from "@templatical/types";
4
4
  //#region src/server/errors.ts
5
5
  const PROVIDER_ERROR = {
@@ -0,0 +1,61 @@
1
+ import { t as AI_ERROR_MESSAGES } from "./wire-B6AF_UGT.js";
2
+ import { i as parseRetryAfter } from "./errors-DWshWx18.js";
3
+ import { SdkError, ThrottledError } from "@templatical/types";
4
+ //#region src/adapters/shared.ts
5
+ /** The content an adapter stored as `{ adapter, content }` state, if it's this adapter's. */
6
+ function ownState(state, adapter) {
7
+ if (typeof state !== "object" || state === null) return void 0;
8
+ const tagged = state;
9
+ return tagged.adapter === adapter && Array.isArray(tagged.content) ? tagged.content : void 0;
10
+ }
11
+ /** A tool call's arguments as an object (`{}` when the string isn't a JSON object). */
12
+ function toolInput(input) {
13
+ if (typeof input !== "string") return input;
14
+ try {
15
+ const parsed = JSON.parse(input);
16
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
17
+ } catch {
18
+ return {};
19
+ }
20
+ }
21
+ /** A tool call's arguments as a JSON string. */
22
+ function toolArguments(input) {
23
+ return typeof input === "string" ? input : JSON.stringify(input);
24
+ }
25
+ /** A Blob's bytes as base64, with web APIs only (the kit is isomorphic). */
26
+ async function blobBase64(blob) {
27
+ const bytes = new Uint8Array(await blob.arrayBuffer());
28
+ let binary = "";
29
+ for (let start = 0; start < bytes.length; start += 32768) binary += String.fromCharCode(...bytes.subarray(start, start + 32768));
30
+ return btoa(binary);
31
+ }
32
+ /** The model refused or a content filter stopped it. */
33
+ function refused() {
34
+ return new SdkError(AI_ERROR_MESSAGES.ai_provider_error, 502, "ai_provider_error");
35
+ }
36
+ /** The output hit the token limit while a tool call was being written. */
37
+ function cutOff() {
38
+ return new SdkError(AI_ERROR_MESSAGES.ai_output_invalid, 422, "ai_output_invalid");
39
+ }
40
+ function retryAfterHeader(headers) {
41
+ if (typeof Headers !== "undefined" && headers instanceof Headers) return headers.get("retry-after");
42
+ if (typeof headers !== "object" || headers === null) return null;
43
+ for (const [name, value] of Object.entries(headers)) if (name.toLowerCase() === "retry-after" && typeof value === "string") return value;
44
+ return null;
45
+ }
46
+ /**
47
+ * A provider's 429 (after the client's own retries) as the kit's
48
+ * `ThrottledError`; anything else unchanged, so it keeps its own details.
49
+ * Reads the provider SDKs' `status`/`headers` and the AI SDK's
50
+ * `statusCode`/`responseHeaders`, unwrapping a retry error's `lastError`.
51
+ */
52
+ function providerError(error) {
53
+ if (typeof error !== "object" || error === null) return error;
54
+ const failure = error.lastError ?? error;
55
+ if ((failure.status ?? failure.statusCode) !== 429) return error;
56
+ return new ThrottledError(AI_ERROR_MESSAGES.rate_limited, parseRetryAfter(retryAfterHeader(failure.headers ?? failure.responseHeaders)));
57
+ }
58
+ //#endregion
59
+ export { refused as a, providerError as i, cutOff as n, toolArguments as o, ownState as r, toolInput as s, blobBase64 as t };
60
+
61
+ //# sourceMappingURL=shared-BuhccY6c.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-BuhccY6c.js","names":[],"sources":["../src/adapters/shared.ts"],"sourcesContent":["import { SdkError, ThrottledError } from \"@templatical/types\";\nimport { parseRetryAfter } from \"../client/errors\";\nimport type { AiModelToolCall } from \"../server/model\";\nimport { AI_ERROR_MESSAGES } from \"../wire\";\n\n/** The content an adapter stored as `{ adapter, content }` state, if it's this adapter's. */\nexport function ownState<T>(state: unknown, adapter: string): T[] | undefined {\n if (typeof state !== \"object\" || state === null) return undefined;\n const tagged = state as { adapter?: unknown; content?: unknown };\n return tagged.adapter === adapter && Array.isArray(tagged.content)\n ? (tagged.content as T[])\n : undefined;\n}\n\n/** A tool call's arguments as an object (`{}` when the string isn't a JSON object). */\nexport function toolInput(\n input: AiModelToolCall[\"input\"],\n): Record<string, unknown> {\n if (typeof input !== \"string\") return input;\n try {\n const parsed: unknown = JSON.parse(input);\n return typeof parsed === \"object\" &&\n parsed !== null &&\n !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n/** A tool call's arguments as a JSON string. */\nexport function toolArguments(input: AiModelToolCall[\"input\"]): string {\n return typeof input === \"string\" ? input : JSON.stringify(input);\n}\n\n/** A Blob's bytes as base64, with web APIs only (the kit is isomorphic). */\nexport async function blobBase64(blob: Blob): Promise<string> {\n const bytes = new Uint8Array(await blob.arrayBuffer());\n let binary = \"\";\n for (let start = 0; start < bytes.length; start += 0x8000) {\n binary += String.fromCharCode(...bytes.subarray(start, start + 0x8000));\n }\n return btoa(binary);\n}\n\n/** The model refused or a content filter stopped it. */\nexport function refused(): SdkError {\n return new SdkError(\n AI_ERROR_MESSAGES.ai_provider_error,\n 502,\n \"ai_provider_error\",\n );\n}\n\n/** The output hit the token limit while a tool call was being written. */\nexport function cutOff(): SdkError {\n return new SdkError(\n AI_ERROR_MESSAGES.ai_output_invalid,\n 422,\n \"ai_output_invalid\",\n );\n}\n\nfunction retryAfterHeader(headers: unknown): string | null {\n if (typeof Headers !== \"undefined\" && headers instanceof Headers)\n return headers.get(\"retry-after\");\n if (typeof headers !== \"object\" || headers === null) return null;\n for (const [name, value] of Object.entries(headers)) {\n if (name.toLowerCase() === \"retry-after\" && typeof value === \"string\")\n return value;\n }\n return null;\n}\n\n/**\n * A provider's 429 (after the client's own retries) as the kit's\n * `ThrottledError`; anything else unchanged, so it keeps its own details.\n * Reads the provider SDKs' `status`/`headers` and the AI SDK's\n * `statusCode`/`responseHeaders`, unwrapping a retry error's `lastError`.\n */\nexport function providerError(error: unknown): unknown {\n if (typeof error !== \"object\" || error === null) return error;\n const failure = ((error as { lastError?: unknown }).lastError ?? error) as {\n status?: unknown;\n statusCode?: unknown;\n headers?: unknown;\n responseHeaders?: unknown;\n };\n if ((failure.status ?? failure.statusCode) !== 429) return error;\n return new ThrottledError(\n AI_ERROR_MESSAGES.rate_limited,\n parseRetryAfter(\n retryAfterHeader(failure.headers ?? failure.responseHeaders),\n ),\n );\n}\n"],"mappings":";;;;;AAMA,SAAgB,SAAY,OAAgB,SAAkC;CAC5E,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO,KAAA;CACxD,MAAM,SAAS;CACf,OAAO,OAAO,YAAY,WAAW,MAAM,QAAQ,OAAO,OAAO,IAC5D,OAAO,UACR,KAAA;AACN;;AAGA,SAAgB,UACd,OACyB;CACzB,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,KAAK;EACxC,OAAO,OAAO,WAAW,YACvB,WAAW,QACX,CAAC,MAAM,QAAQ,MAAM,IAClB,SACD,CAAC;CACP,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;AAGA,SAAgB,cAAc,OAAyC;CACrE,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AACjE;;AAGA,eAAsB,WAAW,MAA6B;CAC5D,MAAM,QAAQ,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC;CACrD,IAAI,SAAS;CACb,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,OACjD,UAAU,OAAO,aAAa,GAAG,MAAM,SAAS,OAAO,QAAQ,KAAM,CAAC;CAExE,OAAO,KAAK,MAAM;AACpB;;AAGA,SAAgB,UAAoB;CAClC,OAAO,IAAI,SACT,kBAAkB,mBAClB,KACA,mBACF;AACF;;AAGA,SAAgB,SAAmB;CACjC,OAAO,IAAI,SACT,kBAAkB,mBAClB,KACA,mBACF;AACF;AAEA,SAAS,iBAAiB,SAAiC;CACzD,IAAI,OAAO,YAAY,eAAe,mBAAmB,SACvD,OAAO,QAAQ,IAAI,aAAa;CAClC,IAAI,OAAO,YAAY,YAAY,YAAY,MAAM,OAAO;CAC5D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,GAChD,IAAI,KAAK,YAAY,MAAM,iBAAiB,OAAO,UAAU,UAC3D,OAAO;CAEX,OAAO;AACT;;;;;;;AAQA,SAAgB,cAAc,OAAyB;CACrD,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,UAAY,MAAkC,aAAa;CAMjE,KAAK,QAAQ,UAAU,QAAQ,gBAAgB,KAAK,OAAO;CAC3D,OAAO,IAAI,eACT,kBAAkB,cAClB,gBACE,iBAAiB,QAAQ,WAAW,QAAQ,eAAe,CAC7D,CACF;AACF"}
@@ -1,6 +1,6 @@
1
+ import { t as AiModelAdapter } from "../model-xJvGTfLB.js";
1
2
  import { t as HttpAiFeatures } from "../http-provider-Cb41NXd5.js";
2
3
  import { C as AiWireEvent } from "../wire-Cv7Fk-7n.js";
3
- import { t as AiModelAdapter } from "../model-BuHDCTlM.js";
4
4
  import { AiAltTextRequest, AiAppliedBatch, AiChatAttachment, AiChatRequest, AiFixFinding, AiFixRequest, AiMergeTags, AiOperationsResult, AiProvider, AiRewriteRequest, AiTurnHandlers, Operation, TemplateContent } from "@templatical/types";
5
5
  //#region src/testing/conformance.d.ts
6
6
  interface AiConformanceCheck {
@@ -1,9 +1,10 @@
1
1
  import { l as aiRoutePath, o as AI_SESSION_HEADER, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as matchAiRoute } from "../wire-B6AF_UGT.js";
2
- import { a as errorFromEvent, n as createHttpAiProviderWithFetch } from "../http-provider-BoRkCd7H.js";
3
- import { g as buildChatRequest, h as buildAltTextRequest, i as checkAltTextAnswer, o as runEditingTurn, p as walkBlocks, r as checkAltText, t as buildAltTextRepairRequest, v as buildFixRequest } from "../checks-Blbakkgc.js";
2
+ import { n as errorFromEvent } from "../errors-DWshWx18.js";
3
+ import { n as createHttpAiProviderWithFetch } from "../http-provider-Cx2ZcLRB.js";
4
+ import { g as buildChatRequest, h as buildAltTextRequest, i as checkAltTextAnswer, o as runEditingTurn, p as walkBlocks, r as checkAltText, t as buildAltTextRepairRequest, v as buildFixRequest } from "../checks-DvLD3892.js";
4
5
  import { CollaborationConflict, OperationError, SdkError, applyOperations, createButtonBlock, createParagraphBlock, createTitleBlock, invertOperations, safeClone } from "@templatical/types";
5
6
  //#region package.json
6
- var version = "0.50.0";
7
+ var version = "0.51.1";
7
8
  //#endregion
8
9
  //#region eval/cases.json
9
10
  var cases_default = [