@elytracms/studio 0.0.56 → 0.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/auth.ts +7 -0
- package/convex/users.ts +41 -14
- package/dist/components/ai/ai-chat-panel-lazy.js +3 -3
- package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.d.ts +0 -9
- package/dist/components/ai/ai-chat-panel.js +199 -60
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/chat-ui.d.ts +62 -10
- package/dist/components/ai/chat-ui.js +139 -23
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +30 -7
- package/dist/components/ai/proposal-card.js +81 -21
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/cms/detail-editor.js +12 -0
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +12 -1
- package/dist/components/cms/document-diff-view.js +119 -42
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/rich-text-preview.d.ts +15 -0
- package/dist/components/cms/rich-text-preview.js +81 -0
- package/dist/components/cms/rich-text-preview.js.map +1 -0
- package/dist/components/cms/version-compare-text.d.ts +26 -0
- package/dist/components/cms/version-compare-text.js +60 -0
- package/dist/components/cms/version-compare-text.js.map +1 -1
- package/dist/lib/ai/anthropic-provider.d.ts +0 -12
- package/dist/lib/ai/anthropic-provider.js +3 -11
- package/dist/lib/ai/anthropic-provider.js.map +1 -1
- package/dist/lib/ai/apply-content.js +52 -10
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +74 -0
- package/dist/lib/ai/chat-context.js +100 -0
- package/dist/lib/ai/chat-context.js.map +1 -0
- package/dist/lib/ai/chat-loop.d.ts +70 -0
- package/dist/lib/ai/chat-loop.js +46 -0
- package/dist/lib/ai/chat-loop.js.map +1 -0
- package/dist/lib/ai/chat-state.d.ts +73 -57
- package/dist/lib/ai/chat-state.js +49 -90
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +139 -6
- package/dist/lib/ai/fake-provider.js +261 -8
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/gateway-provider.d.ts +30 -0
- package/dist/lib/ai/gateway-provider.js +73 -0
- package/dist/lib/ai/gateway-provider.js.map +1 -0
- package/dist/lib/ai/index.d.ts +5 -1
- package/dist/lib/ai/index.js +5 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/node-handler.d.ts +13 -4
- package/dist/lib/ai/node-handler.js +33 -3
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/rich-text-input.d.ts +27 -0
- package/dist/lib/ai/rich-text-input.js +25 -0
- package/dist/lib/ai/rich-text-input.js.map +1 -0
- package/dist/lib/ai/route-provider.d.ts +35 -10
- package/dist/lib/ai/route-provider.js +13 -6
- package/dist/lib/ai/route-provider.js.map +1 -1
- package/dist/lib/ai/route.d.ts +50 -21
- package/dist/lib/ai/route.js +69 -40
- package/dist/lib/ai/route.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +12 -1
- package/dist/lib/ai/shape.js +46 -20
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +242 -37
- package/dist/lib/ai/tools.js +548 -63
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/types.d.ts +31 -16
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +68 -0
- package/dist/lib/ai/update-fields.js +296 -0
- package/dist/lib/ai/update-fields.js.map +1 -0
- package/dist/lib/ai-current-document.d.ts +53 -0
- package/dist/lib/ai-current-document.js +54 -0
- package/dist/lib/ai-current-document.js.map +1 -0
- package/dist/lib/auth/convex-auth-adapter.d.ts +9 -1
- package/dist/lib/auth/convex-auth-adapter.js +28 -2
- package/dist/lib/auth/convex-auth-adapter.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +6 -6
- package/dist/lib/i18n/messages.en.js +19 -1
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/package.json +4 -3
- package/dist/lib/ai/create-flow.d.ts +0 -132
- package/dist/lib/ai/create-flow.js +0 -280
- package/dist/lib/ai/create-flow.js.map +0 -1
package/dist/lib/ai/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/lib/ai/tools.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The narrow tool set the creation flow's model works against (EC-199).\n *\n * Per the ratified AD, tools execute CLIENT-side against the operations layer with\n * the user's own session — this module only declares their SHAPES (the model-facing\n * planner schema) and the reader interface the studio binds to operations. The engine\n * never gains a write path: `searchDocuments` / `listAssets` are reads that gather the\n * real ids a proposal may reference (a relation id is otherwise structurally\n * impossible — see `buildTargetShape`), and `createDocument` is the terminal action\n * that runs `applyContentWithShape` and yields a reviewable proposal. `updateFields`\n * and `translate` are deliberately NOT here — they are later slices (EC-332 / post-v1).\n */\nimport type { CmsDocument, JsonSchema } from '@elytracms/core/cms-core'\n\n/** A document the model may reference as a relation target (from a `searchDocuments` read). */\nexport interface DocumentSearchResult {\n id: string\n title?: string\n}\n\n/** An asset the model may reference (from a `listAssets` read). */\nexport interface AssetSearchResult {\n id: string\n title?: string\n}\n\n/**\n * The client-side, operations-backed readers the orchestrator calls. The studio binds\n * these to the user's authenticated operations client; tests bind an in-memory fake.\n * Reads only — there is no write tool (apply is a separate, human-gated draft write).\n */\nexport interface CreateFlowTools {\n searchDocuments(input: {\n collection: string\n query?: string\n limit?: number\n }): Promise<DocumentSearchResult[]>\n listAssets(input: { query?: string; limit?: number }): Promise<AssetSearchResult[]>\n /**\n * Fetch a full document (values + localized) for the translate flow (EC-332). Optional:\n * the creation flow never needs it, so a studio that only wires create can omit it.\n */\n getDocument?(input: { collection: string; id: string }): Promise<CmsDocument | null>\n}\n\n/** One planned step the model returns — the discriminated tool/action union. */\nexport type PlannerAction =\n | { action: 'searchDocuments'; collection: string; query?: string }\n | { action: 'listAssets'; query?: string }\n | { action: 'createDocument'; collection: string; instruction?: string }\n | { action: 'translateDocument'; collection: string; query?: string }\n | { action: 'respond'; message: string }\n\n/** The schema name the planner call carries — how a FakeProvider tells plan vs. author. */\nexport const PLANNER_SCHEMA_NAME = 'create_flow_action'\n\n/**\n * The structured-output schema for ONE planning step. `action` is a closed enum and\n * `collection` is leashed to the known collection ids, so the model can neither invent\n * an action nor target a collection that does not exist.\n */\nexport function plannerSchema(collectionIds: readonly string[]): JsonSchema {\n return {\n type: 'object',\n properties: {\n action: {\n type: 'string',\n enum: ['searchDocuments', 'listAssets', 'createDocument', 'translateDocument', 'respond'],\n description:\n 'searchDocuments: find real ids for a relation target before creating. ' +\n 'listAssets: find real asset ids. createDocument: author the document once ' +\n 'you have what you need. translateDocument: fill the empty locale values of an ' +\n 'existing document (search for it first to get its id). ' +\n 'respond: reply without creating anything.',\n },\n collection: {\n type: 'string',\n ...(collectionIds.length > 0 ? { enum: [...collectionIds] } : {}),\n description: 'Target collection id (for searchDocuments / createDocument / translateDocument).',\n },\n query: {\n type: 'string',\n description: 'Optional search text (searchDocuments / listAssets / translateDocument).',\n },\n instruction: {\n type: 'string',\n description: 'What to write, refining the user request (createDocument).',\n },\n message: { type: 'string', description: 'Assistant reply text (respond).' },\n },\n required: ['action'],\n additionalProperties: false,\n }\n}\n\n/** Coerce a raw structured-output object into a {@link PlannerAction}; `null` if unusable. */\nexport function parsePlannerAction(raw: unknown): PlannerAction | null {\n if (typeof raw !== 'object' || raw === null) return null\n const value = raw as Record<string, unknown>\n const action = value.action\n const str = (k: string): string | undefined =>\n typeof value[k] === 'string' && (value[k] as string).length > 0 ? (value[k] as string) : undefined\n switch (action) {\n case 'searchDocuments': {\n const collection = str('collection')\n if (!collection) return null\n return { action, collection, ...(str('query') ? { query: str('query')! } : {}) }\n }\n case 'listAssets':\n return { action, ...(str('query') ? { query: str('query')! } : {}) }\n case 'createDocument': {\n const collection = str('collection')\n if (!collection) return null\n return { action, collection, ...(str('instruction') ? { instruction: str('instruction')! } : {}) }\n }\n case 'translateDocument': {\n const collection = str('collection')\n if (!collection) return null\n return { action, collection, ...(str('query') ? { query: str('query')! } : {}) }\n }\n case 'respond':\n return { action, message: str('message') ?? '' }\n default:\n return null\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/lib/ai/tools.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The narrow tool set the assistant works against (EC-199, reshaped by EC-334).\n *\n * Per the ratified AD, tools execute CLIENT-side against the operations layer with\n * the user's own session. This module carries the three pieces of that contract:\n *\n * 1. {@link chatToolDefinitions} — the SDK-native tool DEFINITIONS the route's\n * chat branch declares in `streamText`, deliberately WITHOUT `execute`, so the\n * SDK forwards every call to the client (the route stays a zero-data-access\n * proxy). Plain `{ description, inputSchema }` objects with zod schemas —\n * structurally the SDK's `FunctionTool`, with no SDK import (bundle discipline).\n * 2. {@link ChatToolReaders} — the operations-backed read interface the studio\n * binds client-side (search / list / get). Reads only — the engine never gains\n * a write path; apply is a separate, human-gated draft write.\n * 3. {@link createChatToolExecutor} — the client executor map `useChat`'s\n * `onToolCall` dispatches into. Readers pass through; the terminal tools run\n * the deterministic engines (`applyContentWithShape` / `translateDocument` /\n * `buildUpdateProposal`) and return a COMPACT proposal summary as the tool\n * output — the full proposal goes to the review UI via\n * `onProposal(toolCallId, …)`, never to the model.\n *\n * EC-378: `updateFields` is the edit-flow terminal tool — the model authors\n * per-field changes directly (it has `getDocument` output + the conversation as\n * context); the executor reads the CURRENT document, builds a validated update\n * proposal (`buildUpdateProposal`), and hands it to the review sink. Its target\n * (and `getDocument`'s) may be omitted to mean the document currently open in\n * the editor — resolved via the {@link ChatToolExecutorOptions.currentDocument}\n * seam ({@link resolveDocumentTarget}); the context block the panel injects is\n * ADVISORY, the resolution here is what makes \"this document\" deterministic.\n *\n * The EC-199 planner enum/schema (`plannerSchema` / `parsePlannerAction`) retired\n * with the hand-rolled orchestrator (EC-334 slice 2).\n */\nimport { z } from 'zod'\nimport { resolveDocumentTitle } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n CollectionRegistry,\n CmsValidationIssue,\n FieldValues,\n LocaleConfig,\n ValidateDocumentOptions,\n} from '@elytracms/core/cms-core'\nimport { parseRichTextValue, richTextValueToMarkdownProse } from '@elytracms/core/rich-text'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { applyContentWithShape } from './apply-content'\nimport { translateDocument } from './translate-document'\nimport { buildUpdateProposal } from './update-fields'\nimport type { UpdateFieldChange } from './update-fields'\nimport { aiProvenance } from './provenance'\nimport type { ChatProposal } from './chat-state'\nimport type {\n AiProvider,\n AssetCandidate,\n AssetCandidates,\n RelationCandidate,\n RelationCandidates,\n ResolvedKnowledge,\n} from './types'\n\n/** A document the model may reference as a relation target (from a `searchDocuments` read). */\nexport interface DocumentSearchResult {\n id: string\n title?: string\n}\n\n/** An asset the model may reference (from a `listAssets` read). */\nexport interface AssetSearchResult {\n id: string\n title?: string\n}\n\n/**\n * The client-side, operations-backed readers the executor calls. The studio binds\n * these to the user's authenticated operations client; tests bind an in-memory fake.\n * Reads only — there is no write tool (apply is a separate, human-gated draft write).\n */\nexport interface ChatToolReaders {\n searchDocuments(input: {\n collection: string\n query?: string\n limit?: number\n }): Promise<DocumentSearchResult[]>\n listAssets(input: { query?: string; limit?: number }): Promise<AssetSearchResult[]>\n /**\n * Fetch a full document (values + localized) — the translate flow and the model's\n * `getDocument` tool. Optional: a studio that only wires create can omit it.\n */\n getDocument?(input: { collection: string; id: string }): Promise<CmsDocument | null>\n}\n\n/** Per-search result cap (protects the wire — results become model context). */\nexport const DEFAULT_SEARCH_LIMIT = 20\n\n// --- SDK-native chat tool definitions (EC-334) --------------------------------------\n\n/**\n * One model-facing tool definition for the route's `streamText` call. Structurally\n * the SDK's `FunctionTool` WITHOUT `execute` (execution is client-side, always) —\n * typed locally so this module needs no SDK import.\n */\nexport interface ChatToolDefinition {\n description: string\n /** Zod input schema (the AI SDK accepts zod schemas as `inputSchema` directly). */\n inputSchema: z.ZodType\n}\n\n/**\n * The tool set the native chat branch declares (EC-334). No `execute` on any entry:\n * the SDK forwards each call to the client, which executes reads against the\n * operations layer with the user's session and posts the result back; the terminal\n * tools (`createDocument` / `translateDocument`) run the deterministic engines\n * client-side and return a compact proposal summary as the tool output. The route\n * itself never executes anything.\n *\n * Collections are NOT enum-leashed here — the route has zero data access, so it has\n * no catalog; the catalog reaches the model through the request `context` (system\n * prompt), and every id a proposal references must come from a tool RESULT\n * (client-validated; `applyContentWithShape` still enum-leashes relation ids).\n */\nexport const chatToolDefinitions = {\n searchDocuments: {\n description:\n 'Search documents in a collection. Use it to find the REAL ids a relation may ' +\n 'reference, or to locate an existing document — never invent an id. Returns ' +\n '{ id, title } summaries.',\n inputSchema: z.object({\n collection: z\n .string()\n .describe('Target collection id (from the catalog in the system context).'),\n query: z.string().optional().describe('Optional search text (title match).'),\n limit: z.number().int().positive().optional().describe('Max results.'),\n }),\n },\n listAssets: {\n description:\n 'List or search assets (images, files). Use it to find REAL asset ids — never ' +\n 'invent one. Returns { id, title } summaries.',\n inputSchema: z.object({\n query: z.string().optional().describe('Optional search text.'),\n limit: z.number().int().positive().optional().describe('Max results.'),\n }),\n },\n getDocument: {\n description:\n 'Fetch one full document (values + localized values + block ids) by collection and ' +\n 'id. Omit both to read the document currently open in the editor. Rich-text field ' +\n 'values are returned as markdown strings where faithfully expressible.',\n inputSchema: z.object({\n collection: z\n .string()\n .optional()\n .describe('Collection id. Omit to target the currently open document.'),\n id: z\n .string()\n .optional()\n .describe(\n 'Document id (from a searchDocuments result). Omit to target the currently open document.',\n ),\n }),\n },\n createDocument: {\n description:\n 'Author a NEW document in a collection as a reviewable draft proposal. Terminal ' +\n 'step of a creation request — call it once you have gathered the real ids you ' +\n 'need. Returns a compact proposal summary.',\n inputSchema: z.object({\n collection: z.string().describe('Target collection id.'),\n instruction: z.string().describe('What to write, refining the user request.'),\n }),\n },\n updateFields: {\n description:\n 'Change specific fields of an EXISTING document as a reviewable proposal — THE ' +\n 'tool for every edit request (change / fix / remove / replace / rewrite wording, ' +\n 'values, or a page-builder element). Call getDocument first to see the current ' +\n 'values (and block ids), then pass ONLY the fields that change. A block prop ' +\n '(e.g. a Hero headline) is addressed as field + blockId + prop. Omit collection ' +\n 'and id to target the document currently open in the editor. A rich-text field ' +\n 'takes the FULL new value as a MARKDOWN string (headings, lists, links, bold/italic, ' +\n 'blockquotes — no images, code, tables, or HTML).',\n inputSchema: z.object({\n collection: z\n .string()\n .optional()\n .describe('Collection id. Omit to target the currently open document.'),\n id: z\n .string()\n .optional()\n .describe('Document id. Omit to target the currently open document.'),\n changes: z\n .array(\n z.object({\n field: z.string().describe('Field name from the collection schema.'),\n locale: z\n .string()\n .optional()\n .describe(\n \"Locale of a LOCALIZED field (defaults to the editor's working locale).\",\n ),\n blockId: z\n .string()\n .optional()\n .describe('For a blocks field: the id of the block to change (from getDocument).'),\n prop: z\n .string()\n .optional()\n .describe('With blockId: the component prop to change.'),\n value: z\n .unknown()\n .optional()\n .describe(\n 'The new value (a rich-text field takes a markdown string). null clears the field/prop.',\n ),\n }),\n )\n .min(1)\n .describe('The field changes — only what changes, never the whole document.'),\n }),\n },\n translateDocument: {\n description:\n 'Fill the EMPTY locale values of an existing document (search for it first to ' +\n 'get its id). Only for translation requests — never for edits (use updateFields ' +\n 'to change existing content). Returns a compact proposal summary.',\n inputSchema: z.object({\n collection: z.string().describe('Collection id.'),\n id: z.string().describe('Document id (from a searchDocuments result).'),\n locale: z.string().optional().describe('Target locale (defaults to the active one).'),\n }),\n },\n} satisfies Record<string, ChatToolDefinition>\n\n// --- Client executor map (EC-334) ----------------------------------------------------\n\n/** One forwarded tool call, as `useChat`'s `onToolCall` receives it. */\nexport interface ChatToolCall {\n toolCallId: string\n toolName: string\n input: unknown\n}\n\n/** The coordinates of the document currently open in the editor (EC-378 seam). */\nexport interface CurrentDocumentRef {\n collection: string\n id: string\n}\n\n/**\n * Resolve a tool call's target document from its (optional) input coordinates\n * and the current-document seam (EC-378). Deterministic:\n *\n * - both given → use them verbatim;\n * - both omitted → the currently open document (error when none is open);\n * - `collection` given with `id` omitted → the open document ONLY when the\n * collections match — a named collection must never silently cross onto a\n * different open document.\n *\n * Pure — tested directly; `getDocument` and `updateFields` share it.\n */\nexport function resolveDocumentTarget(\n input: { collection?: string; id?: string },\n current: CurrentDocumentRef | null | undefined,\n): { ok: true; ref: CurrentDocumentRef } | { ok: false; errorText: string } {\n if (input.collection !== undefined && input.id !== undefined) {\n return { ok: true, ref: { collection: input.collection, id: input.id } }\n }\n if (!current) {\n return {\n ok: false,\n errorText:\n 'No target document — pass collection and id, or ask the user to open the document in the editor.',\n }\n }\n if (input.collection !== undefined && input.collection !== current.collection) {\n return {\n ok: false,\n errorText: `The open document is in collection \"${current.collection}\", not \"${input.collection}\" — pass the document id explicitly.`,\n }\n }\n return { ok: true, ref: { collection: current.collection, id: input.id ?? current.id } }\n}\n\n/**\n * A tool execution outcome: an `output` posted back as the tool result, or an\n * `errorText` posted as an `output-error` part (the model sees the failure and\n * can react — never a thrown exception into the stream).\n */\nexport type ChatToolOutcome = { ok: true; output: unknown } | { ok: false; errorText: string }\n\n/**\n * The per-conversation candidate store: every `searchDocuments` result is recorded\n * per relation target collection, every `listAssets` result into one flat library\n * pool (EC-381), and the terminal tools may only reference the ids gathered here\n * (the `applyContentWithShape` / `buildUpdateProposal` enum + drop/loud leash makes\n * a hallucinated relation OR asset id structurally unrepresentable / fail loud).\n * Held by the panel per conversation — a new conversation (EC-380 clear) starts an\n * empty store.\n */\nexport interface ChatCandidateStore {\n record(collection: string, results: readonly DocumentSearchResult[]): void\n /** Record every `listAssets` result into the flat library pool (EC-381). */\n recordAssets(results: readonly AssetSearchResult[]): void\n /** Candidates for a target collection's relation fields, keyed by field name. */\n candidatesFor(collection: CollectionDef): RelationCandidates\n /**\n * Candidates for a target collection's asset/image fields (EC-381), keyed by\n * field name — every asset field shares the one flat searched-asset pool\n * (`listAssets` has no field dimension). Empty when nothing was searched, so the\n * asset field is then omitted from the shape (empty beats wrong).\n */\n assetCandidatesFor(collection: CollectionDef): AssetCandidates\n}\n\nexport function createChatCandidateStore(): ChatCandidateStore {\n const searched = new Map<string, Map<string, DocumentSearchResult>>()\n const searchedAssets = new Map<string, AssetSearchResult>()\n return {\n record(collection, results) {\n const byId = searched.get(collection) ?? new Map<string, DocumentSearchResult>()\n for (const result of results) byId.set(result.id, result)\n searched.set(collection, byId)\n },\n recordAssets(results) {\n for (const result of results) searchedAssets.set(result.id, result)\n },\n candidatesFor(collection) {\n const candidates: RelationCandidates = {}\n for (const field of collection.fields) {\n if (field.type !== 'relation') continue\n const found = searched.get(field.target)\n if (!found || found.size === 0) continue\n candidates[field.name] = [...found.values()].map(\n (r): RelationCandidate => ({ id: r.id, ...(r.title ? { label: r.title } : {}) }),\n )\n }\n return candidates\n },\n assetCandidatesFor(collection) {\n const candidates: AssetCandidates = {}\n if (searchedAssets.size === 0) return candidates\n const pool = [...searchedAssets.values()].map(\n (a): AssetCandidate => ({ id: a.id, ...(a.title ? { label: a.title } : {}) }),\n )\n for (const field of collection.fields) {\n if (field.type !== 'asset') continue\n candidates[field.name] = pool\n }\n return candidates\n },\n }\n}\n\nexport interface ChatToolExecutorOptions {\n /** The (route) provider the terminal engines proxy their model calls through. */\n provider: AiProvider\n registry: CollectionRegistry\n /** The collections the assistant may target (unknown ids fail loud). */\n collections: readonly CollectionDef[]\n /** Operations-backed readers (client-side, user's session). */\n readers: ChatToolReaders\n /** Mint a fresh document id for a new draft (studio: the CMS store's minter). */\n mintDocumentId: (collectionId: string) => string\n /** Per-conversation relation-candidate store (see {@link createChatCandidateStore}). */\n candidates: ChatCandidateStore\n /**\n * Full proposal sink, keyed by `toolCallId` — the review UI's entry point.\n * The tool OUTPUT stays a compact summary; the patch never reaches the model.\n */\n onProposal: (toolCallId: string, proposal: ChatProposal) => void\n /**\n * The document currently open in the editor (EC-378 seam) — a thunk read at\n * EXECUTION time, so \"this document\" resolves against what is open NOW.\n * `getDocument` / `updateFields` fall back to it when the model omits the\n * target (see {@link resolveDocumentTarget}).\n */\n currentDocument?: () => CurrentDocumentRef | null | undefined\n /** Current knowledge snapshot (thunk — the body thunk refreshes it per request). */\n knowledge?: () => ResolvedKnowledge | undefined\n /** Component manifests — context for `allow`-leashed richText/blocks fields. */\n components?: readonly ComponentManifest[]\n /** Target locale for localized fields. */\n locale?: string\n /** Locale config — required for `translateDocument`. */\n localeConfig?: LocaleConfig\n /** Injected leashes / store for `applyContentWithShape`'s validation. */\n validateRichTextEmbeds?: ValidateDocumentOptions['validateRichTextEmbeds']\n validateComposition?: ValidateDocumentOptions['validateComposition']\n store?: ValidateDocumentOptions['store']\n /** Max repair attempts inside `applyContentWithShape`. */\n maxRepairAttempts?: number\n /** Per-search result cap (default {@link DEFAULT_SEARCH_LIMIT}). */\n searchLimit?: number\n}\n\n/** The executor `useChat`'s `onToolCall` dispatches into. */\nexport interface ChatToolExecutor {\n execute(call: ChatToolCall): Promise<ChatToolOutcome>\n}\n\nfunction issueSummary(issues: readonly CmsValidationIssue[]): string {\n return issues\n .slice(0, 5)\n .map((issue) => issue.message)\n .join('; ')\n}\n\n/**\n * The MODEL-facing view of a document (EC-379): rich-text field values are\n * projected to markdown strings — but ONLY when the engine's loss-guarded\n * inverse can express them faithfully (subset-only content, round-trip proven).\n * A document carrying embeds, internal `ref` links, or out-of-subset marks keeps\n * its `{ version, doc }` envelope, so nothing the model later echoes back can\n * silently lose structure. Applies to the `getDocument` TOOL OUTPUT only; the\n * proposal/apply paths always read the raw stored document.\n */\nexport function projectRichTextAsMarkdown(\n document: CmsDocument,\n collection: CollectionDef,\n): CmsDocument {\n const richTextFields = collection.fields\n .filter((field) => field.type === 'richText')\n .map((field) => field.name)\n if (richTextFields.length === 0) return document\n const mapBucket = (bucket: FieldValues): FieldValues => {\n let next = bucket\n for (const name of richTextFields) {\n const value = next[name]\n if (value === undefined || value === null) continue\n const parsed = parseRichTextValue(value)\n if (!parsed.ok) continue\n const markdown = richTextValueToMarkdownProse(parsed.value)\n if (markdown === undefined) continue // not faithfully expressible → keep the envelope\n if (next === bucket) next = { ...bucket }\n next[name] = markdown\n }\n return next\n }\n const values = mapBucket(document.values)\n const localized = document.localized\n ? Object.fromEntries(\n Object.entries(document.localized).map(([locale, bucket]) => [locale, mapBucket(bucket)]),\n )\n : undefined\n return { ...document, values, ...(localized ? { localized } : {}) }\n}\n\n/**\n * Build the client executor map. Every entry validates its input against\n * {@link chatToolDefinitions}' zod schema (the route validates UIMessages only\n * structurally — deep input validation is the client's), executes with the\n * user's session, and returns a JSON-safe outcome. EC-378's `updateFields`\n * lands as one more case in this dispatch.\n */\nexport function createChatToolExecutor(options: ChatToolExecutorOptions): ChatToolExecutor {\n const searchLimit = options.searchLimit ?? DEFAULT_SEARCH_LIMIT\n\n function collectionById(id: string): CollectionDef | undefined {\n return options.collections.find((c) => c.id === id)\n }\n\n async function searchDocuments(input: {\n collection: string\n query?: string\n limit?: number\n }): Promise<ChatToolOutcome> {\n if (!collectionById(input.collection)) {\n return { ok: false, errorText: `Unknown collection \"${input.collection}\" — use a collection id from the catalog.` }\n }\n const limit = Math.min(input.limit ?? searchLimit, searchLimit)\n const results = (\n await options.readers.searchDocuments({\n collection: input.collection,\n ...(input.query ? { query: input.query } : {}),\n limit,\n })\n ).slice(0, limit)\n options.candidates.record(input.collection, results)\n return { ok: true, output: results }\n }\n\n async function listAssets(input: { query?: string; limit?: number }): Promise<ChatToolOutcome> {\n const limit = Math.min(input.limit ?? searchLimit, searchLimit)\n const results = (\n await options.readers.listAssets({\n ...(input.query ? { query: input.query } : {}),\n limit,\n })\n ).slice(0, limit)\n // EC-381 — leash asset fields to what the model actually searched, exactly as\n // searchDocuments feeds the relation store.\n options.candidates.recordAssets(results)\n return { ok: true, output: results }\n }\n\n async function getDocument(input: {\n collection?: string\n id?: string\n }): Promise<ChatToolOutcome> {\n if (!options.readers.getDocument) {\n return { ok: false, errorText: 'Reading documents is not available here.' }\n }\n const target = resolveDocumentTarget(input, options.currentDocument?.())\n if (!target.ok) return { ok: false, errorText: target.errorText }\n const document = await options.readers.getDocument(target.ref)\n if (!document) {\n return {\n ok: false,\n errorText: `Document \"${target.ref.collection}/${target.ref.id}\" was not found.`,\n }\n }\n // EC-379: the model reads (and re-authors) rich text as markdown.\n const collection = collectionById(target.ref.collection)\n return { ok: true, output: collection ? projectRichTextAsMarkdown(document, collection) : document }\n }\n\n /** EC-378 — the edit-flow terminal tool: current doc + model-authored changes → validated proposal. */\n async function updateFields(\n toolCallId: string,\n input: { collection?: string; id?: string; changes: UpdateFieldChange[] },\n ): Promise<ChatToolOutcome> {\n if (!options.readers.getDocument) {\n return { ok: false, errorText: 'Reading documents is not available here.' }\n }\n const target = resolveDocumentTarget(input, options.currentDocument?.())\n if (!target.ok) return { ok: false, errorText: target.errorText }\n const collection = collectionById(target.ref.collection)\n if (!collection) {\n return {\n ok: false,\n errorText: `Unknown collection \"${target.ref.collection}\" — use a collection id from the catalog.`,\n }\n }\n const document = await options.readers.getDocument(target.ref)\n if (!document) {\n return {\n ok: false,\n errorText: `Document \"${target.ref.collection}/${target.ref.id}\" was not found.`,\n }\n }\n const result = buildUpdateProposal({\n registry: options.registry,\n collection,\n document,\n changes: input.changes,\n candidates: options.candidates.candidatesFor(collection),\n assetCandidates: options.candidates.assetCandidatesFor(collection),\n ...(options.locale ? { locale: options.locale } : {}),\n ...(options.localeConfig ? { localeConfig: options.localeConfig } : {}),\n ...(options.validateRichTextEmbeds\n ? { validateRichTextEmbeds: options.validateRichTextEmbeds }\n : {}),\n ...(options.validateComposition ? { validateComposition: options.validateComposition } : {}),\n ...(options.store ? { store: options.store } : {}),\n })\n if (!result.ok) {\n return {\n ok: false,\n errorText: `The proposed changes did not pass validation: ${issueSummary(result.issues)}`,\n }\n }\n if (result.kind === 'no-change') {\n return {\n ok: true,\n output: {\n status: 'no-change',\n note: 'The requested change matches the current values — there is nothing to update.',\n },\n }\n }\n options.onProposal(toolCallId, {\n origin: 'update',\n collection: collection.id,\n documentId: document.id,\n patch: result.patch,\n provenance: aiProvenance(options.provider),\n attempts: 1,\n issues: result.issues,\n before: result.before,\n })\n // Compact summary only — the full patch goes to the review UI, not the model.\n const titleValues = {\n ...result.patch.values,\n ...(options.locale ? result.patch.localized?.[options.locale] : {}),\n }\n return {\n ok: true,\n output: {\n status: 'proposed',\n collection: collection.id,\n documentId: document.id,\n title: resolveDocumentTitle(collection, titleValues, document.id),\n changedFields: result.changedFields,\n note: 'Update proposal created; the editor reviews and applies it in the studio. Summarize the change briefly — do not repeat the content.',\n },\n }\n }\n\n async function createDocument(\n toolCallId: string,\n input: { collection: string; instruction: string },\n ): Promise<ChatToolOutcome> {\n const collection = collectionById(input.collection)\n if (!collection) {\n return { ok: false, errorText: `Unknown collection \"${input.collection}\" — use a collection id from the catalog.` }\n }\n const documentId = options.mintDocumentId(collection.id)\n const knowledge = options.knowledge?.()\n const result = await applyContentWithShape({\n provider: options.provider,\n registry: options.registry,\n collection,\n documentId,\n instruction: input.instruction,\n ...(knowledge ? { knowledge } : {}),\n ...(options.components ? { components: options.components } : {}),\n candidates: options.candidates.candidatesFor(collection),\n // EC-381 — asset/image fields are leashed to searched library assets.\n assetCandidates: options.candidates.assetCandidatesFor(collection),\n ...(options.locale ? { locale: options.locale } : {}),\n ...(options.validateRichTextEmbeds\n ? { validateRichTextEmbeds: options.validateRichTextEmbeds }\n : {}),\n ...(options.validateComposition ? { validateComposition: options.validateComposition } : {}),\n ...(options.store ? { store: options.store } : {}),\n ...(options.maxRepairAttempts !== undefined\n ? { maxRepairAttempts: options.maxRepairAttempts }\n : {}),\n })\n if (!result.ok) {\n return {\n ok: false,\n errorText: `The drafted ${collection.id} did not pass validation: ${issueSummary(result.issues)}`,\n }\n }\n options.onProposal(toolCallId, {\n origin: 'create',\n collection: collection.id,\n documentId,\n patch: result.patch,\n provenance: result.provenance,\n attempts: result.attempts,\n issues: result.issues,\n })\n // Compact summary only — the full patch goes to the review UI, not the model.\n return {\n ok: true,\n output: {\n status: 'proposed',\n collection: collection.id,\n documentId,\n title: resolveDocumentTitle(collection, result.patch.values, documentId),\n note: 'Draft proposal created; the editor reviews and applies it in the studio. Summarize briefly — do not repeat the content.',\n },\n }\n }\n\n async function translate(\n toolCallId: string,\n input: { collection: string; id: string; locale?: string },\n ): Promise<ChatToolOutcome> {\n const collection = collectionById(input.collection)\n if (!collection) {\n return { ok: false, errorText: `Unknown collection \"${input.collection}\" — use a collection id from the catalog.` }\n }\n if (!options.readers.getDocument || !options.localeConfig) {\n return { ok: false, errorText: 'Translation is not available here.' }\n }\n const document = await options.readers.getDocument({\n collection: input.collection,\n id: input.id,\n })\n if (!document) {\n return { ok: false, errorText: `Document \"${input.collection}/${input.id}\" was not found.` }\n }\n const knowledge = options.knowledge?.()\n const result = await translateDocument({\n provider: options.provider,\n collection,\n document,\n localeConfig: options.localeConfig,\n ...(input.locale ? { targetLocales: [input.locale] } : {}),\n ...(knowledge ? { knowledge } : {}),\n })\n if (result.total === 0) {\n return {\n ok: true,\n output: {\n status: 'nothing-to-translate',\n note: 'Every locale value is already filled — there is nothing to translate.',\n },\n }\n }\n if (result.translated === 0) {\n return { ok: false, errorText: 'The translation did not produce any content.' }\n }\n options.onProposal(toolCallId, {\n origin: 'translate',\n collection: collection.id,\n documentId: document.id,\n patch: {\n collection: collection.id,\n id: document.id,\n values: result.document.values,\n ...(result.document.localized ? { localized: result.document.localized } : {}),\n },\n provenance: result.provenance,\n attempts: 1,\n issues: [],\n before: {\n values: document.values,\n ...(document.localized ? { localized: document.localized } : {}),\n },\n })\n return {\n ok: true,\n output: {\n status: 'proposed',\n collection: collection.id,\n documentId: document.id,\n translatedUnits: result.translated,\n note: 'Translation proposal created; the editor reviews and applies it in the studio.',\n },\n }\n }\n\n return {\n async execute(call: ChatToolCall): Promise<ChatToolOutcome> {\n const definition = (chatToolDefinitions as Record<string, ChatToolDefinition>)[call.toolName]\n if (!definition) {\n return { ok: false, errorText: `Unknown tool \"${call.toolName}\".` }\n }\n const parsed = definition.inputSchema.safeParse(call.input)\n if (!parsed.success) {\n return { ok: false, errorText: `Invalid ${call.toolName} input: ${parsed.error.message}` }\n }\n try {\n switch (call.toolName) {\n case 'searchDocuments':\n return await searchDocuments(parsed.data as { collection: string; query?: string; limit?: number })\n case 'listAssets':\n return await listAssets(parsed.data as { query?: string; limit?: number })\n case 'getDocument':\n return await getDocument(parsed.data as { collection?: string; id?: string })\n case 'createDocument':\n return await createDocument(\n call.toolCallId,\n parsed.data as { collection: string; instruction: string },\n )\n case 'updateFields':\n return await updateFields(\n call.toolCallId,\n parsed.data as { collection?: string; id?: string; changes: UpdateFieldChange[] },\n )\n case 'translateDocument':\n return await translate(\n call.toolCallId,\n parsed.data as { collection: string; id: string; locale?: string },\n )\n default:\n return { ok: false, errorText: `Unknown tool \"${call.toolName}\".` }\n }\n } catch (error) {\n return {\n ok: false,\n errorText: `The ${call.toolName} step failed: ${error instanceof Error ? error.message : 'unknown error'}`,\n }\n }\n },\n }\n}\n"]}
|
package/dist/lib/ai/types.d.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { CollectionDef, CollectionRegistry, CmsValidationIssue, FieldValues, JsonSchema, ValidateDocumentOptions } from '@elytracms/core/cms-core';
|
|
14
14
|
import type { ComponentManifest } from '@elytracms/core/component-registry';
|
|
15
|
+
import type { LanguageModel } from 'ai';
|
|
15
16
|
/**
|
|
16
17
|
* The capabilities the engine requires of a provider. Both must be `true` — the
|
|
17
18
|
* capability gate (`resolveAiConfig`) fails loud otherwise (tool-calling +
|
|
@@ -46,20 +47,11 @@ export interface GenerateStructuredResult {
|
|
|
46
47
|
object: unknown;
|
|
47
48
|
usage?: AiUsage;
|
|
48
49
|
}
|
|
49
|
-
/** One chat message the streaming route relays to the provider. */
|
|
50
|
-
export interface AiChatMessage {
|
|
51
|
-
role: 'system' | 'user' | 'assistant';
|
|
52
|
-
content: string;
|
|
53
|
-
}
|
|
54
|
-
/** A streaming chat/completion request the server-route factory forwards to the provider. */
|
|
55
|
-
export interface StreamChatRequest {
|
|
56
|
-
system?: string;
|
|
57
|
-
messages: AiChatMessage[];
|
|
58
|
-
}
|
|
59
50
|
/**
|
|
60
|
-
* A provider adapter over a tool-calling, structured-output model. Anthropic
|
|
61
|
-
* production, {@link FakeProvider} (deterministic,
|
|
62
|
-
*
|
|
51
|
+
* A provider adapter over a tool-calling, structured-output model. Anthropic (or
|
|
52
|
+
* the Vercel AI Gateway) in production, {@link FakeProvider} (deterministic,
|
|
53
|
+
* keyless) in tests/playground — all implement this identical surface so the whole
|
|
54
|
+
* loop runs without a key.
|
|
63
55
|
*/
|
|
64
56
|
export interface AiProvider {
|
|
65
57
|
/** Stable provider id (e.g. `anthropic`, `fake`); recorded in provenance. */
|
|
@@ -71,10 +63,13 @@ export interface AiProvider {
|
|
|
71
63
|
/** Produce a single schema-constrained structured object. */
|
|
72
64
|
generateStructured(request: GenerateStructuredRequest): Promise<GenerateStructuredResult>;
|
|
73
65
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
66
|
+
* The AI-SDK `LanguageModel` behind this provider — the seam the route's native
|
|
67
|
+
* chat branch consumes (EC-334: `streamText` over UIMessages, tools declared
|
|
68
|
+
* without `execute` so they forward to the client). Optional: a pure structured
|
|
69
|
+
* proxy (the client-side route provider) exposes none — the route answers 501
|
|
70
|
+
* for chat requests then.
|
|
76
71
|
*/
|
|
77
|
-
|
|
72
|
+
languageModel?(): Promise<LanguageModel>;
|
|
78
73
|
}
|
|
79
74
|
/** Soft, advisory limits for the route counter and the engine repair loop. */
|
|
80
75
|
export interface AiLimits {
|
|
@@ -141,6 +136,20 @@ export interface RelationCandidate {
|
|
|
141
136
|
}
|
|
142
137
|
/** Relation candidate ids keyed by field name. A field absent here is omitted from the shape. */
|
|
143
138
|
export type RelationCandidates = Record<string, RelationCandidate[]>;
|
|
139
|
+
/**
|
|
140
|
+
* An asset candidate id (from a `listAssets` tool result). Asset/image fields may
|
|
141
|
+
* ONLY use ids present here — the target shape emits them as an enum, so a
|
|
142
|
+
* hallucinated (or invented / "to-be-uploaded") asset id is not even representable
|
|
143
|
+
* as valid structured output. From the EXISTING media library ONLY (EC-381): the
|
|
144
|
+
* assistant never generates or uploads an asset; empty beats wrong.
|
|
145
|
+
*/
|
|
146
|
+
export interface AssetCandidate {
|
|
147
|
+
id: string;
|
|
148
|
+
/** Human label (title / filename) for the model to choose against. */
|
|
149
|
+
label?: string;
|
|
150
|
+
}
|
|
151
|
+
/** Asset candidate ids keyed by field name. A field absent here is omitted from the shape. */
|
|
152
|
+
export type AssetCandidates = Record<string, AssetCandidate[]>;
|
|
144
153
|
/** The typed draft-values patch the engine emits — applied by the draft-write path. */
|
|
145
154
|
export interface ContentPatch {
|
|
146
155
|
collection: string;
|
|
@@ -174,6 +183,12 @@ export interface ApplyContentInput {
|
|
|
174
183
|
components?: readonly ComponentManifest[];
|
|
175
184
|
/** Relation candidate ids by field — the ONLY ids a relation field may use. */
|
|
176
185
|
candidates?: RelationCandidates;
|
|
186
|
+
/**
|
|
187
|
+
* Asset candidate ids by field — the ONLY ids an asset/image field may use
|
|
188
|
+
* (EC-381). Emitted as an enum; an asset field with none is omitted from the
|
|
189
|
+
* shape (so a create proposal simply leaves it empty — empty beats wrong).
|
|
190
|
+
*/
|
|
191
|
+
assetCandidates?: AssetCandidates;
|
|
177
192
|
/** Existing draft values (updates): the model output merges over these before validation. */
|
|
178
193
|
existing?: FieldValues;
|
|
179
194
|
/** Target locale for localized fields (their values land under `localized[locale]`). */
|
package/dist/lib/ai/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/ai/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * `@elytracms/studio/lib/ai` — headless AI platform seam types (EC-331, phase 1).\n *\n * Pure types only: this module imports nothing at runtime (every import is\n * `import type`), so referencing `StudioAiConfig` from the studio-config type does\n * NOT pull the `ai` / `@ai-sdk/*` runtime onto the critical path. The runtime lives\n * in the sibling modules (`config`, `anthropic-provider`, `fake-provider`,\n * `knowledge`, `shape`, `apply-content`, `route`, `provenance`), reached only when\n * the `ai` block is configured and the AI subsystem actually boots. The Anthropic\n * provider additionally loads `ai`/`@ai-sdk/anthropic` via dynamic `import()`, so\n * even importing this barrel never eagerly loads the SDK.\n */\nimport type {\n CollectionDef,\n CollectionRegistry,\n CmsValidationIssue,\n FieldValues,\n JsonSchema,\n ValidateDocumentOptions,\n} from '@elytracms/core/cms-core'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\n\n// --- Provider seam ----------------------------------------------------------------\n\n/**\n * The capabilities the engine requires of a provider. Both must be `true` — the\n * capability gate (`resolveAiConfig`) fails loud otherwise (tool-calling +\n * structured output are the non-negotiable floor for schema-guaranteed authoring).\n */\nexport interface AiProviderCapabilities {\n /** The provider can call tools (function/tool calling). */\n toolCalling: boolean\n /** The provider can return schema-constrained structured output. */\n structuredOutput: boolean\n}\n\n/** A request for one schema-constrained structured object — the engine's core primitive. */\nexport interface GenerateStructuredRequest {\n /** System prompt: rules, injection stance, closed-world constraints. */\n system?: string\n /** The task prompt plus assembled context (knowledge, existing values, prior issues). */\n prompt: string\n /** The target shape as a JSON Schema (draft 2020-12 subset) the output must satisfy. */\n schema: JsonSchema\n /** A short name for the schema (some providers surface it as the tool/output name). */\n schemaName?: string\n /** A one-line description of what the schema represents. */\n schemaDescription?: string\n}\n\n/** Token usage a provider may report (best-effort; recorded for soft spend counters). */\nexport interface AiUsage {\n inputTokens?: number\n outputTokens?: number\n}\n\nexport interface GenerateStructuredResult {\n /** The structured object the provider produced (best-effort match to the request schema). */\n object: unknown\n usage?: AiUsage\n}\n\n/** One chat message the streaming route relays to the provider. */\nexport interface AiChatMessage {\n role: 'system' | 'user' | 'assistant'\n content: string\n}\n\n/** A streaming chat/completion request the server-route factory forwards to the provider. */\nexport interface StreamChatRequest {\n system?: string\n messages: AiChatMessage[]\n}\n\n/**\n * A provider adapter over a tool-calling, structured-output model. Anthropic in\n * production, {@link FakeProvider} (deterministic, keyless) in tests/playground —\n * both implement this identical surface so the whole loop runs without a key.\n */\nexport interface AiProvider {\n /** Stable provider id (e.g. `anthropic`, `fake`); recorded in provenance. */\n readonly id: string\n /** The model id the provider targets, when fixed; recorded in provenance. */\n readonly model?: string\n /** The provider's capabilities — validated by the capability gate at config load. */\n capabilities(): AiProviderCapabilities\n /** Produce a single schema-constrained structured object. */\n generateStructured(request: GenerateStructuredRequest): Promise<GenerateStructuredResult>\n /**\n * Stream a chat/completion response as a Web `Response` — the server-route body.\n * Optional: headless callers may only use {@link generateStructured}.\n */\n streamChat?(request: StreamChatRequest): Promise<Response>\n}\n\n// --- Config seam ------------------------------------------------------------------\n\n/** Soft, advisory limits for the route counter and the engine repair loop. */\nexport interface AiLimits {\n /** Soft per-project ceiling on route requests per minute (advisory; route-enforced). */\n maxRequestsPerMinute?: number\n /** Cap on repair attempts after the first generation (engine default when omitted). */\n maxRepairAttempts?: number\n}\n\n/**\n * The `ai` block of `defineStudio({ ai })` (EC-331). Studio-only live code — like\n * `fieldRenderers` / `i18n`, it never reaches the delivery bundle. The block's\n * ABSENCE means the feature is fully absent (no AI UI, no AI deps on the critical\n * path); `enabled: false` is a temporary kill-switch that keeps the config.\n */\nexport interface StudioAiConfig {\n /**\n * Temporary kill-switch. Presence of the whole `ai` block enables the feature;\n * `enabled: false` disables it without removing the config. Omitted ⇒ enabled.\n */\n enabled?: boolean\n /** The provider adapter (live code): Anthropic in production, FakeProvider in tests/playground. */\n provider: AiProvider\n /** Project knowledge sources assembled into the model's context. */\n knowledge?: KnowledgeSource[]\n /** Soft rate / repair limits. */\n limits?: AiLimits\n}\n\n// --- Knowledge seam ---------------------------------------------------------------\n\n/** An inline knowledge literal (markdown prose versioned with the repo). */\nexport interface KnowledgeItem {\n title?: string\n content: string\n}\n\n/**\n * A `fromCollection('<id>')` source: studio-editable knowledge whose *content* is\n * prose held in the DB (data), while the mechanism stays code. Resolved by a\n * caller-supplied collection reader (starter-kit / studio wires it to operations).\n */\nexport interface KnowledgeCollectionSource {\n readonly kind: 'collection'\n /** Collection id whose documents supply editable knowledge prose. */\n readonly collectionId: string\n /** Field carrying the prose; when omitted the reader picks the collection's title/first text field. */\n readonly field?: string\n}\n\n/** An async (or sync) function producing knowledge items at resolve time. */\nexport type KnowledgeFn = () =>\n | KnowledgeItem\n | KnowledgeItem[]\n | Promise<KnowledgeItem | KnowledgeItem[]>\n\n/** A knowledge source: inline literal, async function, or `fromCollection(...)` sugar. */\nexport type KnowledgeSource = KnowledgeItem | KnowledgeFn | KnowledgeCollectionSource\n\n/** The assembled knowledge context — a flat, deterministic list of resolved items. */\nexport interface ResolvedKnowledge {\n items: KnowledgeItem[]\n}\n\n/** Reads knowledge prose out of a `fromCollection` source (wired to the operations layer). */\nexport type KnowledgeCollectionReader = (\n source: KnowledgeCollectionSource,\n) => KnowledgeItem[] | Promise<KnowledgeItem[]>\n\n// --- Engine seam ------------------------------------------------------------------\n\n/**\n * A relation candidate id (from a `searchDocuments`/tool result). Relation fields may\n * ONLY use ids present here — the target shape emits them as an enum, so a\n * hallucinated id is not even representable as valid structured output.\n */\nexport interface RelationCandidate {\n id: string\n /** Human label (title) for the model to choose against. */\n label?: string\n}\n\n/** Relation candidate ids keyed by field name. A field absent here is omitted from the shape. */\nexport type RelationCandidates = Record<string, RelationCandidate[]>\n\n/** The typed draft-values patch the engine emits — applied by the draft-write path. */\nexport interface ContentPatch {\n collection: string\n id: string\n /** Non-localized field values. */\n values: FieldValues\n /** Per-locale field values (localized fields, under the target locale). */\n localized?: Record<string, FieldValues>\n}\n\n/** Origin marker the draft-write path records so an AI-authored draft is attributable. */\nexport interface AiProvenance {\n origin: 'assistant'\n /** Provider id (e.g. `anthropic`, `fake`). */\n provider: string\n /** Model id, when the provider fixes one. */\n model?: string\n /** ISO timestamp when the content was generated. */\n at: string\n}\n\nexport interface ApplyContentInput {\n provider: AiProvider\n registry: CollectionRegistry\n collection: CollectionDef\n /** Document id the patch targets (an existing doc for updates, or a fresh id for creation). */\n documentId: string\n /** The author's instruction / prompt. */\n instruction: string\n /** Assembled knowledge context (from `resolveKnowledge`). */\n knowledge?: ResolvedKnowledge\n /** Component manifests — context for `allow`-leashed richText/blocks fields. */\n components?: readonly ComponentManifest[]\n /** Relation candidate ids by field — the ONLY ids a relation field may use. */\n candidates?: RelationCandidates\n /** Existing draft values (updates): the model output merges over these before validation. */\n existing?: FieldValues\n /** Target locale for localized fields (their values land under `localized[locale]`). */\n locale?: string\n /** Injected rich-text embed leash (studio-wired); enforced when present. */\n validateRichTextEmbeds?: ValidateDocumentOptions['validateRichTextEmbeds']\n /** Injected blocks composition leash (studio-wired); enforced when present. */\n validateComposition?: ValidateDocumentOptions['validateComposition']\n /** Document store for relation/asset existence checks (optional). */\n store?: ValidateDocumentOptions['store']\n /** Max repair attempts after the first generation (default {@link DEFAULT_MAX_REPAIR_ATTEMPTS}). */\n maxRepairAttempts?: number\n}\n\n/** The engine's outcome: a valid, leash-compliant patch, or the blocking issues. */\nexport type ApplyContentResult =\n | {\n ok: true\n patch: ContentPatch\n provenance: AiProvenance\n /** Total generation attempts made (1 = happy path, >1 = repaired). */\n attempts: number\n /** Non-error issues surfaced by the accepted document (e.g. warnings). */\n issues: CmsValidationIssue[]\n }\n | {\n ok: false\n /** The error-severity issues from the final failed attempt. */\n issues: CmsValidationIssue[]\n attempts: number\n provenance: AiProvenance\n }\n\n// --- Route seam -------------------------------------------------------------------\n\n/** A validated studio session, from the host-wired `getSession`. Opaque beyond `userId`. */\nexport interface AiSession {\n /** Authenticated principal id. */\n userId: string\n [key: string]: unknown\n}\n\nexport interface AiRouteHandlerOptions {\n /** The provider (holds the host-env key). The route is a thin proxy over it. */\n provider: AiProvider\n /**\n * Server-side session check (reject anonymous callers). Returns a session for an\n * authenticated caller, `null` otherwise. Wired by the host to its own auth — the\n * route consumes it, never re-implements it.\n */\n getSession: (request: Request) => AiSession | null | Promise<AiSession | null>\n /** Soft per-project rate limit (advisory spend guard). */\n limits?: AiLimits\n /** Derive the per-project rate-bucket key from a request (default: one global bucket). */\n projectKey?: (request: Request) => string\n}\n\n/** The mounted route handler: a framework-agnostic Web `Request → Response`. */\nexport type AiRouteHandler = (request: Request) => Promise<Response>\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/ai/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * `@elytracms/studio/lib/ai` — headless AI platform seam types (EC-331, phase 1).\n *\n * Pure types only: this module imports nothing at runtime (every import is\n * `import type`), so referencing `StudioAiConfig` from the studio-config type does\n * NOT pull the `ai` / `@ai-sdk/*` runtime onto the critical path. The runtime lives\n * in the sibling modules (`config`, `anthropic-provider`, `fake-provider`,\n * `knowledge`, `shape`, `apply-content`, `route`, `provenance`), reached only when\n * the `ai` block is configured and the AI subsystem actually boots. The Anthropic\n * provider additionally loads `ai`/`@ai-sdk/anthropic` via dynamic `import()`, so\n * even importing this barrel never eagerly loads the SDK.\n */\nimport type {\n CollectionDef,\n CollectionRegistry,\n CmsValidationIssue,\n FieldValues,\n JsonSchema,\n ValidateDocumentOptions,\n} from '@elytracms/core/cms-core'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\n// Type-only on purpose (erased at compile; `verbatimModuleSyntax`): referencing the\n// SDK's `LanguageModel` here creates NO runtime edge — pinned in `bundle.test.ts`.\nimport type { LanguageModel } from 'ai'\n\n// --- Provider seam ----------------------------------------------------------------\n\n/**\n * The capabilities the engine requires of a provider. Both must be `true` — the\n * capability gate (`resolveAiConfig`) fails loud otherwise (tool-calling +\n * structured output are the non-negotiable floor for schema-guaranteed authoring).\n */\nexport interface AiProviderCapabilities {\n /** The provider can call tools (function/tool calling). */\n toolCalling: boolean\n /** The provider can return schema-constrained structured output. */\n structuredOutput: boolean\n}\n\n/** A request for one schema-constrained structured object — the engine's core primitive. */\nexport interface GenerateStructuredRequest {\n /** System prompt: rules, injection stance, closed-world constraints. */\n system?: string\n /** The task prompt plus assembled context (knowledge, existing values, prior issues). */\n prompt: string\n /** The target shape as a JSON Schema (draft 2020-12 subset) the output must satisfy. */\n schema: JsonSchema\n /** A short name for the schema (some providers surface it as the tool/output name). */\n schemaName?: string\n /** A one-line description of what the schema represents. */\n schemaDescription?: string\n}\n\n/** Token usage a provider may report (best-effort; recorded for soft spend counters). */\nexport interface AiUsage {\n inputTokens?: number\n outputTokens?: number\n}\n\nexport interface GenerateStructuredResult {\n /** The structured object the provider produced (best-effort match to the request schema). */\n object: unknown\n usage?: AiUsage\n}\n\n/**\n * A provider adapter over a tool-calling, structured-output model. Anthropic (or\n * the Vercel AI Gateway) in production, {@link FakeProvider} (deterministic,\n * keyless) in tests/playground — all implement this identical surface so the whole\n * loop runs without a key.\n */\nexport interface AiProvider {\n /** Stable provider id (e.g. `anthropic`, `fake`); recorded in provenance. */\n readonly id: string\n /** The model id the provider targets, when fixed; recorded in provenance. */\n readonly model?: string\n /** The provider's capabilities — validated by the capability gate at config load. */\n capabilities(): AiProviderCapabilities\n /** Produce a single schema-constrained structured object. */\n generateStructured(request: GenerateStructuredRequest): Promise<GenerateStructuredResult>\n /**\n * The AI-SDK `LanguageModel` behind this provider — the seam the route's native\n * chat branch consumes (EC-334: `streamText` over UIMessages, tools declared\n * without `execute` so they forward to the client). Optional: a pure structured\n * proxy (the client-side route provider) exposes none — the route answers 501\n * for chat requests then.\n */\n languageModel?(): Promise<LanguageModel>\n}\n\n// --- Config seam ------------------------------------------------------------------\n\n/** Soft, advisory limits for the route counter and the engine repair loop. */\nexport interface AiLimits {\n /** Soft per-project ceiling on route requests per minute (advisory; route-enforced). */\n maxRequestsPerMinute?: number\n /** Cap on repair attempts after the first generation (engine default when omitted). */\n maxRepairAttempts?: number\n}\n\n/**\n * The `ai` block of `defineStudio({ ai })` (EC-331). Studio-only live code — like\n * `fieldRenderers` / `i18n`, it never reaches the delivery bundle. The block's\n * ABSENCE means the feature is fully absent (no AI UI, no AI deps on the critical\n * path); `enabled: false` is a temporary kill-switch that keeps the config.\n */\nexport interface StudioAiConfig {\n /**\n * Temporary kill-switch. Presence of the whole `ai` block enables the feature;\n * `enabled: false` disables it without removing the config. Omitted ⇒ enabled.\n */\n enabled?: boolean\n /** The provider adapter (live code): Anthropic in production, FakeProvider in tests/playground. */\n provider: AiProvider\n /** Project knowledge sources assembled into the model's context. */\n knowledge?: KnowledgeSource[]\n /** Soft rate / repair limits. */\n limits?: AiLimits\n}\n\n// --- Knowledge seam ---------------------------------------------------------------\n\n/** An inline knowledge literal (markdown prose versioned with the repo). */\nexport interface KnowledgeItem {\n title?: string\n content: string\n}\n\n/**\n * A `fromCollection('<id>')` source: studio-editable knowledge whose *content* is\n * prose held in the DB (data), while the mechanism stays code. Resolved by a\n * caller-supplied collection reader (starter-kit / studio wires it to operations).\n */\nexport interface KnowledgeCollectionSource {\n readonly kind: 'collection'\n /** Collection id whose documents supply editable knowledge prose. */\n readonly collectionId: string\n /** Field carrying the prose; when omitted the reader picks the collection's title/first text field. */\n readonly field?: string\n}\n\n/** An async (or sync) function producing knowledge items at resolve time. */\nexport type KnowledgeFn = () =>\n | KnowledgeItem\n | KnowledgeItem[]\n | Promise<KnowledgeItem | KnowledgeItem[]>\n\n/** A knowledge source: inline literal, async function, or `fromCollection(...)` sugar. */\nexport type KnowledgeSource = KnowledgeItem | KnowledgeFn | KnowledgeCollectionSource\n\n/** The assembled knowledge context — a flat, deterministic list of resolved items. */\nexport interface ResolvedKnowledge {\n items: KnowledgeItem[]\n}\n\n/** Reads knowledge prose out of a `fromCollection` source (wired to the operations layer). */\nexport type KnowledgeCollectionReader = (\n source: KnowledgeCollectionSource,\n) => KnowledgeItem[] | Promise<KnowledgeItem[]>\n\n// --- Engine seam ------------------------------------------------------------------\n\n/**\n * A relation candidate id (from a `searchDocuments`/tool result). Relation fields may\n * ONLY use ids present here — the target shape emits them as an enum, so a\n * hallucinated id is not even representable as valid structured output.\n */\nexport interface RelationCandidate {\n id: string\n /** Human label (title) for the model to choose against. */\n label?: string\n}\n\n/** Relation candidate ids keyed by field name. A field absent here is omitted from the shape. */\nexport type RelationCandidates = Record<string, RelationCandidate[]>\n\n/**\n * An asset candidate id (from a `listAssets` tool result). Asset/image fields may\n * ONLY use ids present here — the target shape emits them as an enum, so a\n * hallucinated (or invented / \"to-be-uploaded\") asset id is not even representable\n * as valid structured output. From the EXISTING media library ONLY (EC-381): the\n * assistant never generates or uploads an asset; empty beats wrong.\n */\nexport interface AssetCandidate {\n id: string\n /** Human label (title / filename) for the model to choose against. */\n label?: string\n}\n\n/** Asset candidate ids keyed by field name. A field absent here is omitted from the shape. */\nexport type AssetCandidates = Record<string, AssetCandidate[]>\n\n/** The typed draft-values patch the engine emits — applied by the draft-write path. */\nexport interface ContentPatch {\n collection: string\n id: string\n /** Non-localized field values. */\n values: FieldValues\n /** Per-locale field values (localized fields, under the target locale). */\n localized?: Record<string, FieldValues>\n}\n\n/** Origin marker the draft-write path records so an AI-authored draft is attributable. */\nexport interface AiProvenance {\n origin: 'assistant'\n /** Provider id (e.g. `anthropic`, `fake`). */\n provider: string\n /** Model id, when the provider fixes one. */\n model?: string\n /** ISO timestamp when the content was generated. */\n at: string\n}\n\nexport interface ApplyContentInput {\n provider: AiProvider\n registry: CollectionRegistry\n collection: CollectionDef\n /** Document id the patch targets (an existing doc for updates, or a fresh id for creation). */\n documentId: string\n /** The author's instruction / prompt. */\n instruction: string\n /** Assembled knowledge context (from `resolveKnowledge`). */\n knowledge?: ResolvedKnowledge\n /** Component manifests — context for `allow`-leashed richText/blocks fields. */\n components?: readonly ComponentManifest[]\n /** Relation candidate ids by field — the ONLY ids a relation field may use. */\n candidates?: RelationCandidates\n /**\n * Asset candidate ids by field — the ONLY ids an asset/image field may use\n * (EC-381). Emitted as an enum; an asset field with none is omitted from the\n * shape (so a create proposal simply leaves it empty — empty beats wrong).\n */\n assetCandidates?: AssetCandidates\n /** Existing draft values (updates): the model output merges over these before validation. */\n existing?: FieldValues\n /** Target locale for localized fields (their values land under `localized[locale]`). */\n locale?: string\n /** Injected rich-text embed leash (studio-wired); enforced when present. */\n validateRichTextEmbeds?: ValidateDocumentOptions['validateRichTextEmbeds']\n /** Injected blocks composition leash (studio-wired); enforced when present. */\n validateComposition?: ValidateDocumentOptions['validateComposition']\n /** Document store for relation/asset existence checks (optional). */\n store?: ValidateDocumentOptions['store']\n /** Max repair attempts after the first generation (default {@link DEFAULT_MAX_REPAIR_ATTEMPTS}). */\n maxRepairAttempts?: number\n}\n\n/** The engine's outcome: a valid, leash-compliant patch, or the blocking issues. */\nexport type ApplyContentResult =\n | {\n ok: true\n patch: ContentPatch\n provenance: AiProvenance\n /** Total generation attempts made (1 = happy path, >1 = repaired). */\n attempts: number\n /** Non-error issues surfaced by the accepted document (e.g. warnings). */\n issues: CmsValidationIssue[]\n }\n | {\n ok: false\n /** The error-severity issues from the final failed attempt. */\n issues: CmsValidationIssue[]\n attempts: number\n provenance: AiProvenance\n }\n\n// --- Route seam -------------------------------------------------------------------\n\n/** A validated studio session, from the host-wired `getSession`. Opaque beyond `userId`. */\nexport interface AiSession {\n /** Authenticated principal id. */\n userId: string\n [key: string]: unknown\n}\n\nexport interface AiRouteHandlerOptions {\n /** The provider (holds the host-env key). The route is a thin proxy over it. */\n provider: AiProvider\n /**\n * Server-side session check (reject anonymous callers). Returns a session for an\n * authenticated caller, `null` otherwise. Wired by the host to its own auth — the\n * route consumes it, never re-implements it.\n */\n getSession: (request: Request) => AiSession | null | Promise<AiSession | null>\n /** Soft per-project rate limit (advisory spend guard). */\n limits?: AiLimits\n /** Derive the per-project rate-bucket key from a request (default: one global bucket). */\n projectKey?: (request: Request) => string\n}\n\n/** The mounted route handler: a framework-agnostic Web `Request → Response`. */\nexport type AiRouteHandler = (request: Request) => Promise<Response>\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { CmsDocument, CmsValidationIssue, CollectionDef, CollectionRegistry, FieldValues, ValidateDocumentOptions, LocaleConfig } from '@elytracms/core/cms-core';
|
|
2
|
+
import type { AssetCandidates, ContentPatch, RelationCandidates } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* One field change of an `updateFields` proposal, addressed with the engine's
|
|
5
|
+
* existing conventions (diff / restore-scope): `field` (+ `locale` bucket for a
|
|
6
|
+
* localized field), and for a `blocks` field optionally `blockId` + `prop` to
|
|
7
|
+
* change one component prop of one root block. `value` is the new value
|
|
8
|
+
* (`null`/omitted clears the field or prop).
|
|
9
|
+
*/
|
|
10
|
+
export interface UpdateFieldChange {
|
|
11
|
+
field: string;
|
|
12
|
+
/** Locale bucket for a LOCALIZED field (defaults to the editor's working locale). */
|
|
13
|
+
locale?: string;
|
|
14
|
+
/** For a `blocks` field: the root block's stable node id (from `getDocument`). */
|
|
15
|
+
blockId?: string;
|
|
16
|
+
/** With `blockId`: the component prop to change. */
|
|
17
|
+
prop?: string;
|
|
18
|
+
/** The new value; `null` / omitted clears the field or prop. */
|
|
19
|
+
value?: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface BuildUpdateProposalInput {
|
|
22
|
+
registry: CollectionRegistry;
|
|
23
|
+
collection: CollectionDef;
|
|
24
|
+
/** The CURRENT document — the proposal's "before" side. Never mutated. */
|
|
25
|
+
document: CmsDocument;
|
|
26
|
+
changes: readonly UpdateFieldChange[];
|
|
27
|
+
/** The editor's working locale — the default bucket for localized fields. */
|
|
28
|
+
locale?: string;
|
|
29
|
+
localeConfig?: LocaleConfig;
|
|
30
|
+
/** Relation candidates (searched ids) keyed by field name — the id leash. */
|
|
31
|
+
candidates?: RelationCandidates;
|
|
32
|
+
/** Asset candidates (searched library ids) keyed by field name — the asset id leash (EC-381). */
|
|
33
|
+
assetCandidates?: AssetCandidates;
|
|
34
|
+
/** Injected studio leashes — same discipline as the create engine. */
|
|
35
|
+
validateRichTextEmbeds?: ValidateDocumentOptions['validateRichTextEmbeds'];
|
|
36
|
+
validateComposition?: ValidateDocumentOptions['validateComposition'];
|
|
37
|
+
store?: ValidateDocumentOptions['store'];
|
|
38
|
+
}
|
|
39
|
+
/** The `before` snapshot the review diff renders against. */
|
|
40
|
+
export interface UpdateBeforeSnapshot {
|
|
41
|
+
values: FieldValues;
|
|
42
|
+
localized?: Record<string, FieldValues>;
|
|
43
|
+
}
|
|
44
|
+
export type BuildUpdateProposalResult = {
|
|
45
|
+
ok: true;
|
|
46
|
+
kind: 'proposal';
|
|
47
|
+
/** The MERGED document as a patch (full values; the diff shows changed fields only). */
|
|
48
|
+
patch: ContentPatch;
|
|
49
|
+
before: UpdateBeforeSnapshot;
|
|
50
|
+
/** Human-readable descriptors of the changed fields (e.g. `title (de)`). */
|
|
51
|
+
changedFields: string[];
|
|
52
|
+
/** Non-error issues on the accepted result (warnings). */
|
|
53
|
+
issues: CmsValidationIssue[];
|
|
54
|
+
} | {
|
|
55
|
+
/** Every change resolved to the current values — nothing to propose. */
|
|
56
|
+
ok: true;
|
|
57
|
+
kind: 'no-change';
|
|
58
|
+
} | {
|
|
59
|
+
ok: false;
|
|
60
|
+
issues: CmsValidationIssue[];
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Build the reviewable update proposal: apply every change over a copy of the
|
|
64
|
+
* document (loud targeting), validate the merged result with the injected
|
|
65
|
+
* leashes (only errors the change INTRODUCES block), and report the semantic
|
|
66
|
+
* diff. `no-change` when the changes resolve to the current values.
|
|
67
|
+
*/
|
|
68
|
+
export declare function buildUpdateProposal(input: BuildUpdateProposalInput): BuildUpdateProposalResult;
|