@templatical/ai-kit 0.0.0-stage → 0.50.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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -2
  3. package/dist/checks-Blbakkgc.js +649 -0
  4. package/dist/checks-Blbakkgc.js.map +1 -0
  5. package/dist/client/index.d.ts +24 -0
  6. package/dist/client/index.js +1 -0
  7. package/dist/http-provider-BoRkCd7H.js +327 -0
  8. package/dist/http-provider-BoRkCd7H.js.map +1 -0
  9. package/dist/http-provider-Cb41NXd5.d.ts +22 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.js +3 -0
  12. package/dist/model-BuHDCTlM.d.ts +74 -0
  13. package/dist/server/index.d.ts +313 -0
  14. package/dist/server/index.js +355 -0
  15. package/dist/server/index.js.map +1 -0
  16. package/dist/testing/index.d.ts +277 -0
  17. package/dist/testing/index.js +2444 -0
  18. package/dist/testing/index.js.map +1 -0
  19. package/dist/wire-B6AF_UGT.js +132 -0
  20. package/dist/wire-B6AF_UGT.js.map +1 -0
  21. package/dist/wire-Cv7Fk-7n.d.ts +136 -0
  22. package/eval/MANIFEST.sha256 +23 -0
  23. package/eval/cases.json +287 -0
  24. package/eval/designs/newsletter.png +0 -0
  25. package/eval/designs/one-page.pdf +0 -0
  26. package/eval/designs/promo.png +0 -0
  27. package/eval/domain-mismatch.json +25 -0
  28. package/eval/empty.json +11 -0
  29. package/eval/images/banner.png +0 -0
  30. package/eval/images/chart.png +0 -0
  31. package/eval/images/divider.png +0 -0
  32. package/eval/images/probe.png +0 -0
  33. package/eval/images/product.png +0 -0
  34. package/eval/injection.json +13 -0
  35. package/eval/low-text.json +41 -0
  36. package/eval/promo.json +12 -0
  37. package/eval/sources/banner.html +16 -0
  38. package/eval/sources/chart.html +16 -0
  39. package/eval/sources/divider.html +16 -0
  40. package/eval/sources/newsletter.html +16 -0
  41. package/eval/sources/one-page.html +16 -0
  42. package/eval/sources/probe.html +16 -0
  43. package/eval/sources/product.html +16 -0
  44. package/eval/sources/promo.html +16 -0
  45. package/eval/welcome.json +12 -0
  46. package/package.json +68 -3
  47. package/prompts/alt-text.md +7 -0
  48. package/prompts/chat-attachments.md +1 -0
  49. package/prompts/chat.md +2 -0
  50. package/prompts/editing.md +10 -0
  51. package/prompts/fix-rules.json +9 -0
  52. package/prompts/fix.md +8 -0
  53. package/prompts/operations.md +127 -0
  54. package/prompts/rewrite-presets.json +8 -0
  55. package/prompts/rewrite.md +4 -0
  56. package/schema/template-content.schema.json +1884 -0
@@ -0,0 +1,327 @@
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
53
+ //#region src/client/sse.ts
54
+ const TYPES = /* @__PURE__ */ new Set([
55
+ "text",
56
+ "operations",
57
+ "applied",
58
+ "done",
59
+ "error"
60
+ ]);
61
+ function parse(data) {
62
+ try {
63
+ const event = JSON.parse(data);
64
+ return typeof event === "object" && event !== null && typeof event.type === "string" && TYPES.has(event.type) ? event : null;
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ /**
70
+ * The wire's events from an SSE body: `data:` lines, joined per event and
71
+ * dispatched at a blank line or the end; comments (`: ping`), other fields,
72
+ * unparseable data and unknown types are skipped. Cancelling the generator
73
+ * cancels the body.
74
+ */
75
+ async function* readSse(body) {
76
+ const reader = body.getReader();
77
+ const decoder = new TextDecoder();
78
+ let buffer = "";
79
+ let data = [];
80
+ function* dispatch() {
81
+ if (data.length === 0) return;
82
+ const event = parse(data.join("\n"));
83
+ data = [];
84
+ if (event) yield event;
85
+ }
86
+ function* lines(text) {
87
+ for (const raw of text.split("\n")) {
88
+ const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
89
+ if (line === "") yield* dispatch();
90
+ else if (line.startsWith("data:")) data.push(line.slice(line.startsWith("data: ") ? 6 : 5));
91
+ }
92
+ }
93
+ try {
94
+ for (;;) {
95
+ const { done, value } = await reader.read();
96
+ if (done) break;
97
+ buffer += decoder.decode(value, { stream: true });
98
+ const end = buffer.lastIndexOf("\n");
99
+ if (end === -1) continue;
100
+ const complete = buffer.slice(0, end);
101
+ buffer = buffer.slice(end + 1);
102
+ yield* lines(complete);
103
+ }
104
+ buffer += decoder.decode();
105
+ if (buffer !== "") yield* lines(buffer);
106
+ yield* dispatch();
107
+ } finally {
108
+ reader.cancel().catch(() => {});
109
+ }
110
+ }
111
+ //#endregion
112
+ //#region src/client/http-provider.ts
113
+ function abortError(signal) {
114
+ const reason = signal.reason;
115
+ return reason instanceof Error && reason.name === "AbortError" ? reason : new DOMException("The AI request was stopped.", "AbortError");
116
+ }
117
+ function isRecord(value) {
118
+ return typeof value === "object" && value !== null && !Array.isArray(value);
119
+ }
120
+ /**
121
+ * An `AiProvider` over the wire (A1 §4): JSON or multipart requests, SSE
122
+ * answers for turns and rewrite, JSON for history.
123
+ * Errors are `SdkError`s; stopping a call rejects with an `AbortError`.
124
+ */
125
+ function createHttpAiProvider(options) {
126
+ return createHttpAiProviderWithFetch(options, (input, init) => fetch(input, init));
127
+ }
128
+ /** @internal A per-instance transport for wire conformance; not a package export. */
129
+ function createHttpAiProviderWithFetch(options, fetchRequest) {
130
+ const base = options.baseUrl.replace(/\/+$/, "");
131
+ const on = (feature) => options.features?.[feature] !== false;
132
+ async function request(route, params, init) {
133
+ init.signal?.throwIfAborted();
134
+ const own = typeof options.headers === "function" ? await options.headers() : options.headers ?? {};
135
+ const headers = new Headers(own);
136
+ headers.set(AI_WIRE_HEADER, "1");
137
+ headers.set("Accept", init.stream ? "text/event-stream" : "application/json");
138
+ if (init.session) headers.set(AI_SESSION_HEADER, init.session);
139
+ let body;
140
+ if (init.form) body = init.form;
141
+ else if (init.json !== void 0) {
142
+ headers.set("Content-Type", "application/json");
143
+ body = JSON.stringify(init.json);
144
+ }
145
+ let response;
146
+ try {
147
+ init.signal?.throwIfAborted();
148
+ response = await fetchRequest(`${base}${aiRoutePath(route, params)}`, {
149
+ method: AI_ROUTES[route].method,
150
+ headers,
151
+ body,
152
+ ...options.credentials ? { credentials: options.credentials } : {},
153
+ signal: init.signal
154
+ });
155
+ } catch (error) {
156
+ if (init.signal?.aborted) throw abortError(init.signal);
157
+ throw error instanceof SdkError ? error : providerFailure();
158
+ }
159
+ if (!response.ok) throw await errorFromResponse(response);
160
+ return response;
161
+ }
162
+ /**
163
+ * One streamed call. `each` handles every event before `done`; it may
164
+ * throw to end the call (a refusal, the editor's own error), which aborts
165
+ * the request.
166
+ */
167
+ async function streamed(route, template, init, signal, each, done) {
168
+ const controller = new AbortController();
169
+ const stop = () => controller.abort(signal.reason);
170
+ if (signal.aborted) stop();
171
+ signal.addEventListener("abort", stop, { once: true });
172
+ try {
173
+ const response = await request(route, { template }, {
174
+ ...init,
175
+ stream: true,
176
+ signal: controller.signal
177
+ });
178
+ if (!response.body) throw providerFailure();
179
+ for await (const event of readSse(response.body)) {
180
+ if (signal.aborted) {
181
+ if (event.type === "applied") await each(event);
182
+ if (event.type === "done" || event.type === "error") throw abortError(signal);
183
+ continue;
184
+ }
185
+ if (event.type === "error") throw errorFromEvent(event);
186
+ if (event.type === "done") {
187
+ const value = done(event);
188
+ if (value === void 0) throw providerFailure();
189
+ return value;
190
+ }
191
+ await each(event);
192
+ }
193
+ throw providerFailure();
194
+ } catch (error) {
195
+ if (signal.aborted) throw abortError(signal);
196
+ controller.abort();
197
+ if (error instanceof SdkError) throw error;
198
+ if (error instanceof Error && error.name === "AbortError") throw error;
199
+ if (error instanceof Error && !(error instanceof TypeError)) throw error;
200
+ throw providerFailure();
201
+ } finally {
202
+ signal.removeEventListener("abort", stop);
203
+ }
204
+ }
205
+ function turn(route, req, init, handlers, signal) {
206
+ let text = "";
207
+ return streamed(route, req.templateId, {
208
+ ...init,
209
+ session: req.sessionId
210
+ }, signal, async (event) => {
211
+ if (event.type === "text") {
212
+ text += event.text;
213
+ handlers.onText(event.text);
214
+ } else if (event.type === "operations") {
215
+ const result = await handlers.onOperations(event.ops);
216
+ if (!result.applied) throw new CollaborationConflict(AI_TEMPLATE_CHANGED_MESSAGE, {
217
+ code: result.code,
218
+ opIndex: result.opIndex
219
+ });
220
+ } else if (event.type === "applied") handlers.onApplied({
221
+ batchId: event.batchId,
222
+ revision: event.revision,
223
+ operationCount: event.operationCount
224
+ });
225
+ }, (done) => typeof done.messageId === "string" || done.messageId === null ? {
226
+ messageId: done.messageId,
227
+ text
228
+ } : void 0);
229
+ }
230
+ async function json(response) {
231
+ try {
232
+ return await response.json();
233
+ } catch {
234
+ throw providerFailure();
235
+ }
236
+ }
237
+ async function replay(route, templateId, messageId, sessionId) {
238
+ const body = await json(await request(route, {
239
+ template: templateId,
240
+ message: messageId
241
+ }, { session: sessionId }));
242
+ if (!isRecord(body)) throw providerFailure();
243
+ const data = body.data ?? {};
244
+ if (Array.isArray(data.operations) && typeof data.replayId === "string" && data.replayId !== "") return {
245
+ operations: data.operations,
246
+ replayId: data.replayId
247
+ };
248
+ if (typeof data.batchId === "string" && typeof data.revision === "number") return { applied: {
249
+ batchId: data.batchId,
250
+ revision: data.revision
251
+ } };
252
+ throw providerFailure();
253
+ }
254
+ return {
255
+ chatAttachments: on("chat") && on("chatAttachments"),
256
+ chat: on("chat") ? (req, handlers, signal) => {
257
+ const body = {
258
+ message: req.message,
259
+ selectedBlockId: req.selectedBlockId,
260
+ mergeTags: req.mergeTags,
261
+ content: req.content,
262
+ revision: req.revision
263
+ };
264
+ if (req.attachments.length === 0) return turn("generate", req, { json: body }, handlers, signal);
265
+ const form = new FormData();
266
+ form.set(AI_GENERATE_FIELDS.message, req.message);
267
+ if (req.selectedBlockId !== null) form.set(AI_GENERATE_FIELDS.selectedBlockId, req.selectedBlockId);
268
+ form.set(AI_GENERATE_FIELDS.mergeTags, JSON.stringify(req.mergeTags));
269
+ form.set(AI_GENERATE_FIELDS.content, JSON.stringify(req.content));
270
+ if (req.revision !== null) form.set(AI_GENERATE_FIELDS.revision, String(req.revision));
271
+ for (const attachment of req.attachments) form.append(AI_GENERATE_FIELDS.attachment, attachment.file, attachment.name);
272
+ return turn("generate", req, { form }, handlers, signal);
273
+ } : false,
274
+ fixFinding: on("fixFinding") ? (req, handlers, signal) => turn("fixFinding", req, { json: {
275
+ finding: req.finding,
276
+ mergeTags: req.mergeTags,
277
+ content: req.content,
278
+ revision: req.revision
279
+ } }, handlers, signal) : false,
280
+ rewrite: on("rewrite") ? (req, handlers, signal) => streamed("rewriteText", req.templateId, { json: {
281
+ html: req.html,
282
+ preset: req.preset,
283
+ language: req.language,
284
+ instruction: req.instruction,
285
+ mergeTags: req.mergeTags
286
+ } }, signal, (event) => {
287
+ if (event.type === "text") handlers.onText(event.text);
288
+ }, (done) => typeof done.html === "string" ? { html: done.html } : void 0) : false,
289
+ altText: on("altText") ? (req, signal) => {
290
+ const { templateId, sessionId, ...body } = req;
291
+ return streamed("altText", templateId, {
292
+ json: body,
293
+ session: sessionId
294
+ }, signal, () => {}, (done) => typeof done.alt === "string" ? { alt: done.alt } : void 0);
295
+ } : false,
296
+ ...on("conversation") ? { conversation: {
297
+ settleReplay: async (templateId, messageId, outcome) => {
298
+ await request("settleReplay", {
299
+ template: templateId,
300
+ message: messageId,
301
+ replayId: outcome.replayId
302
+ }, {
303
+ session: outcome.sessionId,
304
+ json: {
305
+ direction: outcome.direction,
306
+ applied: outcome.applied
307
+ }
308
+ });
309
+ },
310
+ list: async (templateId) => {
311
+ const body = await json(await request("conversationMessages", { template: templateId }, {}));
312
+ if (!isRecord(body)) throw providerFailure();
313
+ if (!Array.isArray(body.data)) throw providerFailure();
314
+ return body.data;
315
+ },
316
+ clear: async (templateId) => {
317
+ await request("clearConversation", { template: templateId }, {});
318
+ },
319
+ undo: (templateId, messageId, context) => replay("undoMessage", templateId, messageId, context.sessionId),
320
+ redo: (templateId, messageId, context) => replay("redoMessage", templateId, messageId, context.sessionId)
321
+ } } : {}
322
+ };
323
+ }
324
+ //#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 };
326
+
327
+ //# sourceMappingURL=http-provider-BoRkCd7H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-provider-BoRkCd7H.js","names":[],"sources":["../src/client/errors.ts","../src/client/sse.ts","../src/client/http-provider.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","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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"}
@@ -0,0 +1,22 @@
1
+ import { AiFeatureName, AiProvider } from "@templatical/types";
2
+ //#region src/client/http-provider.d.ts
3
+ type HttpAiFeatures = Partial<Record<AiFeatureName | "conversation", boolean>>;
4
+ interface HttpAiProviderOptions {
5
+ /** Where the wire's routes live: `{baseUrl}/templates/{t}/ai/…`. */
6
+ baseUrl: string;
7
+ /** Your auth headers, or a function (async for a token refresh) called per request. */
8
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
9
+ /** `fetch`'s `credentials`, for cookie auth. */
10
+ credentials?: RequestCredentials;
11
+ /** `false` turns a feature off, `conversation` included. Everything else is on. */
12
+ features?: HttpAiFeatures;
13
+ }
14
+ /**
15
+ * An `AiProvider` over the wire (A1 §4): JSON or multipart requests, SSE
16
+ * answers for turns and rewrite, JSON for history.
17
+ * Errors are `SdkError`s; stopping a call rejects with an `AbortError`.
18
+ */
19
+ declare function createHttpAiProvider(options: HttpAiProviderOptions): AiProvider;
20
+ //#endregion
21
+ export { HttpAiProviderOptions as n, createHttpAiProvider as r, HttpAiFeatures as t };
22
+ //# sourceMappingURL=http-provider-Cb41NXd5.d.ts.map
@@ -0,0 +1,3 @@
1
+ import { C as AiWireEvent, S as AiTextEvent, T as matchAiRoute, _ as AiReplayResponse, a as AI_ROUTES, b as AiRouteMatch, c as AI_WIRE_VERSION, d as AiConversationResponse, f as AiDoneEvent, g as AiOperationsEvent, h as AiGenerateBody, i as AI_LIMITS, l as AiAltTextBody, m as AiFixFindingBody, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, p as AiErrorEvent, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as AiAppliedEvent, v as AiRewriteBody, w as aiRoutePath, x as AiRouteName, y as AiRoute } from "./wire-Cv7Fk-7n.js";
2
+ import { AiAltTextRequest, AiAppliedBatch, AiChatAttachment, AiChatMessage, AiChatRequest, AiConversationProvider, AiErrorCode, AiFeatureName, AiFixFinding, AiFixRequest, AiLimitDetails, AiMergeTag, AiMergeTagSyntax, AiMergeTags, AiMessageBatch, AiMessageKind, AiOperationsResult, AiProvider, AiReplay, AiReplayContext, AiReplayOutcome, AiRequestBase, AiRewriteHandlers, AiRewritePreset, AiRewriteRequest, AiTurnHandlers, AiTurnResult, CollaborationConflict, Operation, SdkError, TemplateContent, ThrottledError } from "@templatical/types";
3
+ export { AI_ERROR_MESSAGES, AI_ERROR_STATUS, AI_GENERATE_FIELDS, AI_LIMITS, AI_ROUTES, AI_SESSION_HEADER, AI_WIRE_HEADER, AI_WIRE_VERSION, AiAltTextBody, type AiAltTextRequest, type AiAppliedBatch, AiAppliedEvent, type AiChatAttachment, type AiChatMessage, type AiChatRequest, type AiConversationProvider, AiConversationResponse, AiDoneEvent, type AiErrorCode, AiErrorEvent, type AiFeatureName, type AiFixFinding, AiFixFindingBody, type AiFixRequest, AiGenerateBody, type AiLimitDetails, type AiMergeTag, type AiMergeTagSyntax, type AiMergeTags, type AiMessageBatch, type AiMessageKind, AiOperationsEvent, type AiOperationsResult, type AiProvider, type AiReplay, type AiReplayContext, type AiReplayOutcome, AiReplayResponse, type AiRequestBase, AiRewriteBody, type AiRewriteHandlers, type AiRewritePreset, type AiRewriteRequest, AiRoute, AiRouteMatch, AiRouteName, AiTextEvent, type AiTurnHandlers, type AiTurnResult, AiWireEvent, CollaborationConflict, type Operation, SdkError, type TemplateContent, ThrottledError, aiRoutePath, matchAiRoute };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { a as AI_ROUTES, c as AI_WIRE_VERSION, i as AI_LIMITS, 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, u as matchAiRoute } from "./wire-B6AF_UGT.js";
2
+ import { CollaborationConflict, SdkError, ThrottledError } from "@templatical/types";
3
+ export { AI_ERROR_MESSAGES, AI_ERROR_STATUS, AI_GENERATE_FIELDS, AI_LIMITS, AI_ROUTES, AI_SESSION_HEADER, AI_WIRE_HEADER, AI_WIRE_VERSION, CollaborationConflict, SdkError, ThrottledError, aiRoutePath, matchAiRoute };
@@ -0,0 +1,74 @@
1
+ //#region src/server/tool.d.ts
2
+ /** A tool in neutral JSON-schema form; adapters map it to their SDK's shape. */
3
+ interface AiToolDefinition {
4
+ name: string;
5
+ description: string;
6
+ inputSchema: Record<string, unknown>;
7
+ }
8
+ /**
9
+ * The editing turns' one tool (S7 A4). `ops` is a JSON **string**: several
10
+ * SDKs turn tool arguments into maps that lose `{}` (S7 D8), and a string
11
+ * survives every one of them.
12
+ */
13
+ declare const applyOperationsTool: AiToolDefinition;
14
+ //#endregion
15
+ //#region src/server/model.d.ts
16
+ /** A design file or an image URL attached to a user message. */
17
+ type AiModelAttachment = {
18
+ kind: "image" | "pdf";
19
+ mediaType: string;
20
+ data: Blob;
21
+ } | {
22
+ kind: "image-url";
23
+ url: string;
24
+ };
25
+ interface AiModelToolCall {
26
+ id: string;
27
+ name: string;
28
+ /** The tool's arguments: a JSON string of the arguments object, or the object. */
29
+ input: string | Record<string, unknown>;
30
+ }
31
+ type AiModelMessage = {
32
+ role: "user";
33
+ content: string;
34
+ attachments?: AiModelAttachment[];
35
+ } | {
36
+ role: "assistant";
37
+ content: string;
38
+ toolCalls?: AiModelToolCall[];
39
+ } | {
40
+ role: "tool";
41
+ toolCallId: string;
42
+ name: string;
43
+ content: string;
44
+ };
45
+ type AiModelPart = {
46
+ type: "text";
47
+ text: string;
48
+ } | ({
49
+ type: "tool-call";
50
+ } & AiModelToolCall) | {
51
+ type: "usage";
52
+ inputTokens: number;
53
+ outputTokens: number;
54
+ };
55
+ interface AiUsage {
56
+ inputTokens: number;
57
+ outputTokens: number;
58
+ }
59
+ /**
60
+ * 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`.
63
+ */
64
+ interface AiModelAdapter {
65
+ stream(input: {
66
+ system: string;
67
+ messages: AiModelMessage[];
68
+ tools: AiToolDefinition[];
69
+ signal?: AbortSignal;
70
+ }): AsyncIterable<AiModelPart>;
71
+ }
72
+ //#endregion
73
+ 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