@elytracms/studio 0.0.86 → 0.0.88
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/asset-revalidate.test.ts +186 -0
- package/convex/assets.ts +53 -19
- package/convex/cms.ts +22 -0
- package/convex/delivery.ts +33 -116
- package/convex/functions.ts +3 -3
- package/convex/rail-write-parity.test.ts +180 -0
- package/convex/redirects.ts +33 -11
- package/convex/revalidate.ts +7 -5
- package/convex/triggers.ts +53 -3
- package/dist/components/ai/ai-chat-panel.js +8 -1
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/app-views/default-modules.d.ts +20 -20
- package/dist/components/app-views/default-modules.js +27 -48
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +5 -6
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +28 -19
- package/dist/components/assets/asset-metadata.js +24 -31
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +24 -5
- package/dist/components/assets/asset-selection.js +39 -9
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +17 -14
- package/dist/components/assets/assets-collection-view-source.js +25 -31
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +11 -1
- package/dist/components/assets/assets-store.js +10 -0
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/focal-point.d.ts +0 -7
- package/dist/components/assets/focal-point.js +0 -10
- package/dist/components/assets/focal-point.js.map +1 -1
- package/dist/components/builder/blocks-panel.js +3 -3
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +25 -1
- package/dist/components/builder/builder-store.js +3 -2
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-chrome-css.js +0 -17
- package/dist/components/builder/canvas-chrome-css.js.map +1 -1
- package/dist/components/builder/canvas-chrome.d.ts +10 -12
- package/dist/components/builder/canvas-chrome.js +12 -13
- package/dist/components/builder/canvas-chrome.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +8 -0
- package/dist/components/builder/canvas-dnd.js +5 -3
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +23 -24
- package/dist/components/builder/canvas-render-context.js +35 -60
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas.js +5 -11
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +14 -6
- package/dist/components/builder/clipboard-actions.js +29 -20
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +19 -26
- package/dist/components/builder/composition-field-editor.js +30 -52
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +3 -1
- package/dist/components/builder/insert-context.js +15 -10
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +29 -0
- package/dist/components/builder/page-tree.js +56 -58
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/reuse.js +5 -2
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +2 -1
- package/dist/components/cms/asset-field.js +39 -83
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +77 -40
- package/dist/components/cms/asset-picker-core.js +73 -46
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.d.ts +48 -19
- package/dist/components/cms/asset-picker.js +69 -30
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +3 -1
- package/dist/components/cms/cell-edit.js +2 -2
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cms-composition-validators.js +11 -2
- package/dist/components/cms/cms-composition-validators.js.map +1 -1
- package/dist/components/cms/cms-store.js +15 -23
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/editable-cell.js +3 -3
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +27 -2
- package/dist/components/cms/gate-validation.js +19 -1
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +2 -2
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +7 -0
- package/dist/components/cms/rich-text/insert.js +7 -0
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/use-document-versions.js +1 -0
- package/dist/components/cms/use-document-versions.js.map +1 -1
- package/dist/components/cms/versions-model.d.ts +0 -12
- package/dist/components/cms/versions-model.js +3 -15
- package/dist/components/cms/versions-model.js.map +1 -1
- package/dist/components/components/component-detail.js +1 -1
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +2 -4
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +13 -10
- package/dist/components/components/transfer.js +21 -19
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/nav-issues.d.ts +23 -3
- package/dist/components/nav-issues.js +38 -2
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/project-session.d.ts +9 -0
- package/dist/components/project-session.js +1 -1
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/redirects/redirects-collection-view-source.d.ts +17 -2
- package/dist/components/redirects/redirects-collection-view-source.js +20 -6
- package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
- package/dist/components/section-nav.js +26 -2
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-surface.js +3 -2
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/settings-form.js +1 -2
- package/dist/components/settings-form.js.map +1 -1
- package/dist/components/studio-context.js +1 -18
- package/dist/components/studio-context.js.map +1 -1
- package/dist/components/studio-project-screen.js +3 -5
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/lib/ai/apply-content.js +73 -29
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +35 -0
- package/dist/lib/ai/constraints.js +104 -4
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/route.js +4 -1
- package/dist/lib/ai/route.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +3 -2
- package/dist/lib/ai/shape.js +16 -24
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +10 -4
- package/dist/lib/ai/tools.js +12 -4
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/types.d.ts +8 -0
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.js +33 -12
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/assets/asset-placements.d.ts +35 -0
- package/dist/lib/assets/asset-placements.js +74 -0
- package/dist/lib/assets/asset-placements.js.map +1 -0
- package/dist/lib/assets/backfill-thumbs.d.ts +9 -8
- package/dist/lib/assets/backfill-thumbs.js +13 -25
- package/dist/lib/assets/backfill-thumbs.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.d.ts +17 -15
- package/dist/lib/clipboard/paste-issues.js +8 -45
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +25 -1
- package/dist/lib/clipboard/paste-target.js +13 -6
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/published-documents.js +1 -0
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +3 -3
- package/dist/lib/data/collection-content.d.ts +22 -1
- package/dist/lib/data/collection-content.js +15 -0
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/data/index.d.ts +0 -3
- package/dist/lib/data/index.js +4 -6
- package/dist/lib/data/index.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +45 -2
- package/dist/lib/dnd/drop-rules.js +64 -9
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +3 -3
- package/dist/lib/engine-version.js +3 -3
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +9 -56
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +8 -53
- package/dist/lib/i18n/messages.en.js +14 -57
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/locale/fallback.js +8 -2
- package/dist/lib/locale/fallback.js.map +1 -1
- package/dist/lib/locale/index.d.ts +0 -1
- package/dist/lib/locale/index.js +0 -1
- package/dist/lib/locale/index.js.map +1 -1
- package/dist/lib/locale/locale-store.d.ts +10 -3
- package/dist/lib/locale/locale-store.js +12 -5
- package/dist/lib/locale/locale-store.js.map +1 -1
- package/dist/lib/operations/session-hydration.d.ts +24 -22
- package/dist/lib/operations/session-hydration.js +15 -53
- package/dist/lib/operations/session-hydration.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +63 -75
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persona/labels.d.ts +0 -2
- package/dist/lib/persona/labels.js +1 -7
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/registry/index.d.ts +4 -2
- package/dist/lib/registry/index.js +26 -21
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/project-components.js +0 -3
- package/dist/lib/registry/project-components.js.map +1 -1
- package/dist/lib/routes/route-issues.js +4 -5
- package/dist/lib/routes/route-issues.js.map +1 -1
- package/dist/lib/theme.js +3 -2
- package/dist/lib/theme.js.map +1 -1
- package/dist/lib/webhooks/tags.js +11 -14
- package/dist/lib/webhooks/tags.js.map +1 -1
- package/dist/lib/workspace/config-issues.d.ts +6 -0
- package/dist/lib/workspace/config-issues.js +8 -4
- package/dist/lib/workspace/config-issues.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +2 -2
- package/dist/lib/workspace/index.js +2 -2
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/previews/enumerate.d.ts +27 -9
- package/dist/previews/enumerate.js +27 -6
- package/dist/previews/enumerate.js.map +1 -1
- package/dist/previews/manifest-format.d.ts +9 -0
- package/dist/previews/manifest-format.js.map +1 -1
- package/dist/router/match.d.ts +0 -2
- package/dist/router/match.js +0 -2
- package/dist/router/match.js.map +1 -1
- package/dist/router/navigate-once.d.ts +19 -4
- package/dist/router/navigate-once.js +22 -5
- package/dist/router/navigate-once.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/package.json +3 -3
- package/src/styles/studio.css +5 -4
- package/src/styles/tokens.css +72 -55
- package/convex/content.ts +0 -291
- package/dist/components/collab/collab-context.d.ts +0 -27
- package/dist/components/collab/collab-context.js +0 -39
- package/dist/components/collab/collab-context.js.map +0 -1
- package/dist/components/collab/collab-seed.d.ts +0 -15
- package/dist/components/collab/collab-seed.js +0 -20
- package/dist/components/collab/collab-seed.js.map +0 -1
- package/dist/components/extensions/extensions-view.d.ts +0 -7
- package/dist/components/extensions/extensions-view.js +0 -127
- package/dist/components/extensions/extensions-view.js.map +0 -1
- package/dist/lib/bindings/section-bindings.d.ts +0 -24
- package/dist/lib/bindings/section-bindings.js +0 -9
- package/dist/lib/bindings/section-bindings.js.map +0 -1
- package/dist/lib/data/data-store.d.ts +0 -98
- package/dist/lib/data/data-store.js +0 -211
- package/dist/lib/data/data-store.js.map +0 -1
- package/dist/lib/data/providers.d.ts +0 -143
- package/dist/lib/data/providers.js +0 -255
- package/dist/lib/data/providers.js.map +0 -1
- package/dist/lib/data/sample-capture.d.ts +0 -30
- package/dist/lib/data/sample-capture.js +0 -70
- package/dist/lib/data/sample-capture.js.map +0 -1
- package/dist/lib/data/sources-store.d.ts +0 -42
- package/dist/lib/data/sources-store.js +0 -120
- package/dist/lib/data/sources-store.js.map +0 -1
- package/dist/lib/locale/binding-fallback.d.ts +0 -35
- package/dist/lib/locale/binding-fallback.js +0 -73
- package/dist/lib/locale/binding-fallback.js.map +0 -1
- package/dist/lib/starters/kit-session.d.ts +0 -5
- package/dist/lib/starters/kit-session.js +0 -17
- package/dist/lib/starters/kit-session.js.map +0 -1
package/dist/lib/ai/route.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../src/lib/ai/route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Server-route factory (EC-331): the session-gated endpoint the consuming studio\n * app mounts. Key custody = host route (never the browser, never Convex): the\n * `provider` holds the host-env key and this handler is a THIN model proxy — NO data\n * access, NO service credentials, NO server-side tool execution.\n *\n * Framework-agnostic on purpose: it returns a Web `Request → Response` handler the\n * host wires into whatever server entry it has (`createAiDevPlugin` in dev,\n * `toNodeHandler` on a Vercel Node function, any fetch handler).\n *\n * Session-gated: `getSession` (host-wired, reading its own auth — this module never\n * touches `lib/auth`) must return a session for authenticated callers; anonymous\n * callers are rejected with 401 BEFORE any stream byte. Plus a soft, in-memory\n * per-project rate counter (spend guard) — also the backstop for the SDK's\n * client-driven tool-call continuations, each of which is a fresh request here.\n *\n * The route serves TWO model operations — both thin proxies, neither with any data\n * access:\n * - `{ kind: 'generate', request }` → `provider.generateStructured` (JSON) — the\n * proxy `applyContentWithShape` / `translateDocument` run CLIENT-side against,\n * so the key never touches the browser;\n * - `{ messages: UIMessage[], context? }` (EC-334, kind-less — the shape the AI\n * SDK's `DefaultChatTransport` posts) → the SDK's native streaming chat\n * protocol: `validateUIMessages` → `streamText` over\n * `provider.languageModel()` with {@link chatToolDefinitions} declared WITHOUT\n * `execute` (every tool call forwards to the client, which executes it against\n * the operations layer with the user's own session) →\n * `createUIMessageStreamResponse` + `toUIMessageStream` (the non-deprecated\n * pair — `result.toUIMessageStreamResponse()` dies in SDK v8). The `system`\n * prompt is assembled from the request's `context` sections; conversation\n * history rides natively in `messages`.\n *\n * BREAKING (EC-334, ratified): the legacy `{ kind: 'chat', messages:\n * AiChatMessage[] }` wire is gone — route factory and client ship lockstep in\n * `@elytracms/studio`, so consumers re-mount the same factory on upgrade.\n */\nimport type { AiRouteHandler, AiRouteHandlerOptions, AiSession, GenerateStructuredRequest } from './types'\nimport { chatToolDefinitions } from './tools'\n\nfunction jsonResponse(value: unknown, status: number): Response {\n return new Response(JSON.stringify(value), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\ninterface RateBucket {\n windowStart: number\n count: number\n}\n\n/**\n * The wire discriminator for the structured-generation branch. The native chat\n * branch (EC-334) is deliberately kind-less: the SDK's `DefaultChatTransport`\n * posts `{ messages, … }` with no `kind`.\n */\nexport type AiRouteKind = 'generate'\n\n/** Structurally validate a {@link GenerateStructuredRequest}, or `null` when malformed. */\nfunction parseGenerateBody(body: unknown): GenerateStructuredRequest | null {\n if (typeof body !== 'object' || body === null) return null\n const request = (body as { request?: unknown }).request\n if (typeof request !== 'object' || request === null) return null\n const { prompt, schema } = request as { prompt?: unknown; schema?: unknown }\n if (typeof prompt !== 'string' || typeof schema !== 'object' || schema === null) return null\n return request as GenerateStructuredRequest\n}\n\n/** The outcome of folding the request `context` into a system prompt. */\nexport type AssembledSystem = { ok: true; system?: string } | { ok: false }\n\n/**\n * Fold the request-body `context` into the system prompt (EC-334). The client sends\n * `context` as one string or ordered string sections (collections-catalog summary,\n * client-resolved knowledge, EC-378's current-document block) — the route only\n * ASSEMBLES them; it resolves no data itself (zero-data-access proxy). Malformed\n * context is a structural 400, never silently dropped (validation-first).\n */\nexport function assembleSystemPrompt(context: unknown): AssembledSystem {\n if (context === undefined || context === null) return { ok: true }\n const sections =\n typeof context === 'string'\n ? [context]\n : Array.isArray(context) && context.every((section) => typeof section === 'string')\n ? (context as string[])\n : null\n if (sections === null) return { ok: false }\n const system = sections\n .map((section) => section.trim())\n .filter((section) => section.length > 0)\n .join('\\n\\n')\n return { ok: true, ...(system.length > 0 ? { system } : {}) }\n}\n\n/**\n * Build the mounted route handler. The returned function is a plain\n * `(Request) => Promise<Response>` — session-gated, soft-rate-limited, and a thin\n * proxy to the provider. Never throws to the caller: every failure is a structured\n * JSON response with an appropriate status.\n */\nexport function createAiRouteHandler(options: AiRouteHandlerOptions): AiRouteHandler {\n const maxPerMinute = options.limits?.maxRequestsPerMinute\n const buckets = new Map<string, RateBucket>()\n const projectKey = options.projectKey ?? (() => 'global')\n\n function rateLimited(key: string): boolean {\n if (maxPerMinute === undefined || maxPerMinute <= 0) return false\n const now = Date.now()\n const bucket = buckets.get(key)\n if (!bucket || now - bucket.windowStart >= 60_000) {\n buckets.set(key, { windowStart: now, count: 1 })\n return false\n }\n bucket.count += 1\n return bucket.count > maxPerMinute\n }\n\n return async (request: Request): Promise<Response> => {\n // 1. Session gate — reject anonymous callers (the route holds spend, not data).\n // Position is FROZEN: the 401 must precede any stream byte (EC-371/EC-334).\n let session: AiSession | null\n try {\n session = await options.getSession(request)\n } catch {\n return jsonResponse({ error: 'unauthorized' }, 401)\n }\n if (!session) return jsonResponse({ error: 'unauthorized' }, 401)\n\n // 2. Method.\n if (request.method !== 'POST') return jsonResponse({ error: 'method-not-allowed' }, 405)\n\n // 3. Soft per-project rate / spend counter.\n if (rateLimited(projectKey(request))) return jsonResponse({ error: 'rate-limited' }, 429)\n\n // 4. Body → the requested model operation.\n let body: unknown\n try {\n body = await request.json()\n } catch {\n return jsonResponse({ error: 'invalid-json' }, 400)\n }\n if (typeof body !== 'object' || body === null) {\n return jsonResponse({ error: 'invalid-json' }, 400)\n }\n\n // 5a. Structured generation — the thin proxy that lets `applyContentWithShape`\n // run client-side without the key ever reaching the browser.\n if ((body as { kind?: unknown }).kind === 'generate') {\n const generate = parseGenerateBody(body)\n if (!generate) {\n return jsonResponse(\n { error: 'invalid-request', message: 'expected { kind: \"generate\", request }' },\n 400,\n )\n }\n try {\n const result = await options.provider.generateStructured(generate)\n return jsonResponse(result, 200)\n } catch {\n return jsonResponse({ error: 'provider-error' }, 502)\n }\n }\n\n // 5b. Native chat branch (EC-334): `{ messages: UIMessage[], context? }`.\n const rawMessages = (body as { messages?: unknown }).messages\n if (!Array.isArray(rawMessages) || rawMessages.length === 0) {\n return jsonResponse(\n {\n error: 'invalid-request',\n message: 'expected { kind: \"generate\", request } or { messages: UIMessage[] }',\n },\n 400,\n )\n }\n\n const assembled = assembleSystemPrompt((body as { context?: unknown }).context)\n if (!assembled.ok) {\n return jsonResponse(\n { error: 'invalid-request', message: 'context must be a string or string[]' },\n 400,\n )\n }\n\n if (!options.provider.languageModel) {\n return jsonResponse({ error: 'provider-cannot-stream' }, 501)\n }\n\n // The SDK loads lazily INSIDE the handler (the anthropic-provider discipline):\n // the client-imported barrel re-exports this module, and a static edge would\n // put the AI SDK on the studio critical path (pinned in bundle.test.ts).\n const {\n validateUIMessages,\n convertToModelMessages,\n streamText,\n createUIMessageStreamResponse,\n toUIMessageStream,\n } = await import('ai')\n\n // Structural UIMessage validation (id/role/parts). Tool parts validate by\n // shape — deep input validation belongs to the client executor (slice 2).\n let messages\n try {\n messages = await validateUIMessages({ messages: rawMessages })\n } catch {\n return jsonResponse(\n { error: 'invalid-request', message: 'expected { messages: UIMessage[] }' },\n 400,\n )\n }\n\n try {\n const model = await options.provider.languageModel()\n const result = streamText({\n model,\n ...(assembled.system !== undefined ? { system: assembled.system } : {}),\n messages: await convertToModelMessages(messages),\n // No `execute` on any tool — the SDK forwards each call to the client\n // (ratified trust boundary). Continuation is therefore client-driven: one\n // tool round-trip per REQUEST, throttled by the rate limit above; the\n // per-turn step cap lands client-side with slice 2.\n tools: chatToolDefinitions,\n })\n return createUIMessageStreamResponse({\n stream: toUIMessageStream({ stream: result.stream, originalMessages: messages }),\n })\n } catch {\n return jsonResponse({ error: 'provider-error' }, 502)\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../src/lib/ai/route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Server-route factory (EC-331): the session-gated endpoint the consuming studio\n * app mounts. Key custody = host route (never the browser, never Convex): the\n * `provider` holds the host-env key and this handler is a THIN model proxy — NO data\n * access, NO service credentials, NO server-side tool execution.\n *\n * Framework-agnostic on purpose: it returns a Web `Request → Response` handler the\n * host wires into whatever server entry it has (`createAiDevPlugin` in dev,\n * `toNodeHandler` on a Vercel Node function, any fetch handler).\n *\n * Session-gated: `getSession` (host-wired, reading its own auth — this module never\n * touches `lib/auth`) must return a session for authenticated callers; anonymous\n * callers are rejected with 401 BEFORE any stream byte. Plus a soft, in-memory\n * per-project rate counter (spend guard) — also the backstop for the SDK's\n * client-driven tool-call continuations, each of which is a fresh request here.\n *\n * The route serves TWO model operations — both thin proxies, neither with any data\n * access:\n * - `{ kind: 'generate', request }` → `provider.generateStructured` (JSON) — the\n * proxy `applyContentWithShape` / `translateDocument` run CLIENT-side against,\n * so the key never touches the browser;\n * - `{ messages: UIMessage[], context? }` (EC-334, kind-less — the shape the AI\n * SDK's `DefaultChatTransport` posts) → the SDK's native streaming chat\n * protocol: `validateUIMessages` → `streamText` over\n * `provider.languageModel()` with {@link chatToolDefinitions} declared WITHOUT\n * `execute` (every tool call forwards to the client, which executes it against\n * the operations layer with the user's own session) →\n * `createUIMessageStreamResponse` + `toUIMessageStream` (the non-deprecated\n * pair — `result.toUIMessageStreamResponse()` dies in SDK v8). The `system`\n * prompt is assembled from the request's `context` sections; conversation\n * history rides natively in `messages`.\n *\n * BREAKING (EC-334, ratified): the legacy `{ kind: 'chat', messages:\n * AiChatMessage[] }` wire is gone — route factory and client ship lockstep in\n * `@elytracms/studio`, so consumers re-mount the same factory on upgrade.\n */\nimport type { AiRouteHandler, AiRouteHandlerOptions, AiSession, GenerateStructuredRequest } from './types'\nimport { chatToolDefinitions } from './tools'\n\nfunction jsonResponse(value: unknown, status: number): Response {\n return new Response(JSON.stringify(value), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\ninterface RateBucket {\n windowStart: number\n count: number\n}\n\n/**\n * The wire discriminator for the structured-generation branch. The native chat\n * branch (EC-334) is deliberately kind-less: the SDK's `DefaultChatTransport`\n * posts `{ messages, … }` with no `kind`.\n */\nexport type AiRouteKind = 'generate'\n\n/** Structurally validate a {@link GenerateStructuredRequest}, or `null` when malformed. */\nfunction parseGenerateBody(body: unknown): GenerateStructuredRequest | null {\n if (typeof body !== 'object' || body === null) return null\n const request = (body as { request?: unknown }).request\n if (typeof request !== 'object' || request === null) return null\n const { prompt, schema } = request as { prompt?: unknown; schema?: unknown }\n if (typeof prompt !== 'string' || typeof schema !== 'object' || schema === null) return null\n return request as GenerateStructuredRequest\n}\n\n/** The outcome of folding the request `context` into a system prompt. */\nexport type AssembledSystem = { ok: true; system?: string } | { ok: false }\n\n/**\n * Fold the request-body `context` into the system prompt (EC-334). The client sends\n * `context` as one string or ordered string sections (collections-catalog summary,\n * client-resolved knowledge, EC-378's current-document block) — the route only\n * ASSEMBLES them; it resolves no data itself (zero-data-access proxy). Malformed\n * context is a structural 400, never silently dropped (validation-first).\n */\nexport function assembleSystemPrompt(context: unknown): AssembledSystem {\n if (context === undefined || context === null) return { ok: true }\n const sections =\n typeof context === 'string'\n ? [context]\n : Array.isArray(context) && context.every((section) => typeof section === 'string')\n ? (context as string[])\n : null\n if (sections === null) return { ok: false }\n const system = sections\n .map((section) => section.trim())\n .filter((section) => section.length > 0)\n .join('\\n\\n')\n return { ok: true, ...(system.length > 0 ? { system } : {}) }\n}\n\n/**\n * Build the mounted route handler. The returned function is a plain\n * `(Request) => Promise<Response>` — session-gated, soft-rate-limited, and a thin\n * proxy to the provider. Never throws to the caller: every failure is a structured\n * JSON response with an appropriate status.\n */\nexport function createAiRouteHandler(options: AiRouteHandlerOptions): AiRouteHandler {\n const maxPerMinute = options.limits?.maxRequestsPerMinute\n const buckets = new Map<string, RateBucket>()\n const projectKey = options.projectKey ?? (() => 'global')\n // EC-602: the ONLY clock read in this module — injected, never `Date.now()` in\n // the decision below.\n const clock = options.now ?? Date.now\n\n function rateLimited(key: string): boolean {\n if (maxPerMinute === undefined || maxPerMinute <= 0) return false\n const now = clock()\n const bucket = buckets.get(key)\n if (!bucket || now - bucket.windowStart >= 60_000) {\n buckets.set(key, { windowStart: now, count: 1 })\n return false\n }\n bucket.count += 1\n return bucket.count > maxPerMinute\n }\n\n return async (request: Request): Promise<Response> => {\n // 1. Session gate — reject anonymous callers (the route holds spend, not data).\n // Position is FROZEN: the 401 must precede any stream byte (EC-371/EC-334).\n let session: AiSession | null\n try {\n session = await options.getSession(request)\n } catch {\n return jsonResponse({ error: 'unauthorized' }, 401)\n }\n if (!session) return jsonResponse({ error: 'unauthorized' }, 401)\n\n // 2. Method.\n if (request.method !== 'POST') return jsonResponse({ error: 'method-not-allowed' }, 405)\n\n // 3. Soft per-project rate / spend counter.\n if (rateLimited(projectKey(request))) return jsonResponse({ error: 'rate-limited' }, 429)\n\n // 4. Body → the requested model operation.\n let body: unknown\n try {\n body = await request.json()\n } catch {\n return jsonResponse({ error: 'invalid-json' }, 400)\n }\n if (typeof body !== 'object' || body === null) {\n return jsonResponse({ error: 'invalid-json' }, 400)\n }\n\n // 5a. Structured generation — the thin proxy that lets `applyContentWithShape`\n // run client-side without the key ever reaching the browser.\n if ((body as { kind?: unknown }).kind === 'generate') {\n const generate = parseGenerateBody(body)\n if (!generate) {\n return jsonResponse(\n { error: 'invalid-request', message: 'expected { kind: \"generate\", request }' },\n 400,\n )\n }\n try {\n const result = await options.provider.generateStructured(generate)\n return jsonResponse(result, 200)\n } catch {\n return jsonResponse({ error: 'provider-error' }, 502)\n }\n }\n\n // 5b. Native chat branch (EC-334): `{ messages: UIMessage[], context? }`.\n const rawMessages = (body as { messages?: unknown }).messages\n if (!Array.isArray(rawMessages) || rawMessages.length === 0) {\n return jsonResponse(\n {\n error: 'invalid-request',\n message: 'expected { kind: \"generate\", request } or { messages: UIMessage[] }',\n },\n 400,\n )\n }\n\n const assembled = assembleSystemPrompt((body as { context?: unknown }).context)\n if (!assembled.ok) {\n return jsonResponse(\n { error: 'invalid-request', message: 'context must be a string or string[]' },\n 400,\n )\n }\n\n if (!options.provider.languageModel) {\n return jsonResponse({ error: 'provider-cannot-stream' }, 501)\n }\n\n // The SDK loads lazily INSIDE the handler (the anthropic-provider discipline):\n // the client-imported barrel re-exports this module, and a static edge would\n // put the AI SDK on the studio critical path (pinned in bundle.test.ts).\n const {\n validateUIMessages,\n convertToModelMessages,\n streamText,\n createUIMessageStreamResponse,\n toUIMessageStream,\n } = await import('ai')\n\n // Structural UIMessage validation (id/role/parts). Tool parts validate by\n // shape — deep input validation belongs to the client executor (slice 2).\n let messages\n try {\n messages = await validateUIMessages({ messages: rawMessages })\n } catch {\n return jsonResponse(\n { error: 'invalid-request', message: 'expected { messages: UIMessage[] }' },\n 400,\n )\n }\n\n try {\n const model = await options.provider.languageModel()\n const result = streamText({\n model,\n ...(assembled.system !== undefined ? { system: assembled.system } : {}),\n messages: await convertToModelMessages(messages),\n // No `execute` on any tool — the SDK forwards each call to the client\n // (ratified trust boundary). Continuation is therefore client-driven: one\n // tool round-trip per REQUEST, throttled by the rate limit above; the\n // per-turn step cap lands client-side with slice 2.\n tools: chatToolDefinitions,\n })\n return createUIMessageStreamResponse({\n stream: toUIMessageStream({ stream: result.stream, originalMessages: messages }),\n })\n } catch {\n return jsonResponse({ error: 'provider-error' }, 502)\n }\n }\n}\n"]}
|
package/dist/lib/ai/shape.d.ts
CHANGED
|
@@ -40,7 +40,8 @@ export interface BuildTargetShapeInput {
|
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
42
|
* Build the leash-aware target shape from a collection + candidates. Deterministic;
|
|
43
|
-
* never throws. Reuses `collectionJsonSchema` as the base
|
|
44
|
-
*
|
|
43
|
+
* never throws. Reuses `collectionJsonSchema` as the base (its `required` list), then
|
|
44
|
+
* gives every property its writer form (EC-556) or its id-enum leash, and strips
|
|
45
|
+
* excluded fields.
|
|
45
46
|
*/
|
|
46
47
|
export declare function buildTargetShape(input: BuildTargetShapeInput): TargetShape;
|
package/dist/lib/ai/shape.js
CHANGED
|
@@ -42,27 +42,16 @@
|
|
|
42
42
|
* accepts): a `date` property is `type: 'string'` + `format: 'date-time'` rather than
|
|
43
43
|
* the `['string','number']` union the general projection carries, because the model is
|
|
44
44
|
* asked to author an ISO instant (EC-472) and some providers reject a union `type`.
|
|
45
|
+
*
|
|
46
|
+
* EC-556 — the projection is VALIDATOR-facing (the value at rest: asset/relation
|
|
47
|
+
* envelopes, the v2 rich-text object). Every property here is its WRITER form
|
|
48
|
+
* (`writerFieldSchema`), derived from that projection at every depth — a bare asset
|
|
49
|
+
* id, a `{ collection, id }` reference, a markdown string — so an asset, relation or
|
|
50
|
+
* rich-text SUBFIELD of an `object` is told the same form as a top-level one. The
|
|
51
|
+
* top-level relation/asset id enums (EC-381) override the writer form there.
|
|
45
52
|
*/
|
|
46
|
-
import { collectionJsonSchema,
|
|
47
|
-
import {
|
|
48
|
-
import { constraintSentence, stripRedundantPattern, toModelFacingSchema } from './constraints.js';
|
|
49
|
-
/**
|
|
50
|
-
* A rich-text field's model-facing schema (EC-379): a MARKDOWN string, MERGED over
|
|
51
|
-
* the field's own constraint projection (EC-473 — it used to replace it, silently
|
|
52
|
-
* dropping a declared `min`/`max`/`pattern`). The description carries the allowed
|
|
53
|
-
* subset (from the engine converter — one source of truth), so the model has
|
|
54
|
-
* something concrete to aim at; the engine converts the markdown into the
|
|
55
|
-
* `{ version, doc }` storage envelope at proposal-build time and rejects
|
|
56
|
-
* out-of-subset constructs with explicit issues.
|
|
57
|
-
*/
|
|
58
|
-
function richTextMarkdownSchema(field) {
|
|
59
|
-
const constraints = stripRedundantPattern(fieldJsonSchema(field));
|
|
60
|
-
return {
|
|
61
|
-
...constraints,
|
|
62
|
-
type: 'string',
|
|
63
|
-
description: `Rich text, written as ${MARKDOWN_PROSE_DESCRIPTION} Write genuine, publishable prose.`,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
53
|
+
import { collectionJsonSchema, resolveLabel } from '@elytracms/core/cms-core';
|
|
54
|
+
import { constraintSentence, toModelFacingSchema, writerFieldSchema } from './constraints.js';
|
|
66
55
|
/**
|
|
67
56
|
* A field's model-facing description: resolved label + optional `ai.hint` + the
|
|
68
57
|
* constraint clause (EC-473). The constraints are stated in PROSE because that is
|
|
@@ -78,8 +67,9 @@ function describe(field, locale) {
|
|
|
78
67
|
}
|
|
79
68
|
/**
|
|
80
69
|
* Build the leash-aware target shape from a collection + candidates. Deterministic;
|
|
81
|
-
* never throws. Reuses `collectionJsonSchema` as the base
|
|
82
|
-
*
|
|
70
|
+
* never throws. Reuses `collectionJsonSchema` as the base (its `required` list), then
|
|
71
|
+
* gives every property its writer form (EC-556) or its id-enum leash, and strips
|
|
72
|
+
* excluded fields.
|
|
83
73
|
*/
|
|
84
74
|
export function buildTargetShape(input) {
|
|
85
75
|
const base = collectionJsonSchema(input.collection);
|
|
@@ -145,8 +135,10 @@ export function buildTargetShape(input) {
|
|
|
145
135
|
properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema;
|
|
146
136
|
assetFields.push({ fieldName: field.name, multiple: field.multiple, candidateIds: ids });
|
|
147
137
|
}
|
|
148
|
-
else
|
|
149
|
-
|
|
138
|
+
else {
|
|
139
|
+
// EC-556: the writer form at every depth (rich text → markdown, EC-379 / D2;
|
|
140
|
+
// nested asset / relation → the bare forms the write-time lens wraps).
|
|
141
|
+
properties[field.name] = writerFieldSchema(field);
|
|
150
142
|
}
|
|
151
143
|
const prop = properties[field.name];
|
|
152
144
|
if (prop) {
|
package/dist/lib/ai/shape.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../src/lib/ai/shape.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Target-shape derivation for `applyContentWithShape` (EC-331).\n *\n * Reuses the existing collection→JSON-Schema derivation (`collectionJsonSchema`,\n * cms-core) rather than writing a new one, then applies the three leashes the AI\n * engine adds on top:\n *\n * - `ai.exclude` fields are removed entirely (the model never sees them → cannot\n * write them).\n * - relation fields become an ENUM over the provided candidate ids (or are omitted\n * when no candidates exist) — so a hallucinated relation id is not representable\n * as valid structured output.\n * - asset/image fields become an ENUM over the searched library asset ids (or are\n * omitted when none were searched) — EC-381, the SAME leash as relations: a\n * proposed asset id must be a real, `listAssets`-returned library asset, and a\n * create shape with no searched assets simply has no asset property (empty beats\n * wrong; the assistant never generates or uploads).\n * - rich-text fields are MARKDOWN STRINGS (EC-379): the description names the\n * allowed subset, and the engine converts md → the `{ version, doc }` storage\n * envelope at proposal-build time. The field's `allow` config leashes component\n * EMBEDS only — markdown cannot express an embed, so the leash is satisfied by\n * construction (and still enforced downstream by `validateRichTextEmbeds`).\n *\n * Localized fields stay flat in the shape (the model authors one locale at a time);\n * the engine routes their values under `localized[locale]`.\n *\n * EC-473 — the shape now consumes the ONE constraint projection, so a rule declared\n * in config actually reaches the model:\n *\n * - `collectionJsonSchema` merges declarative rules with the fragment derived from\n * a schema-form `validation.validate`, so `{ min: 2 }` and `z.string().min(2)`\n * arrive identically (and a declared DATE RANGE arrives at all — it previously\n * reached no consumer whatsoever);\n * - a rich-text field no longer OVERWRITES its constraints: the markdown schema is\n * merged over them instead of replacing them (measured drift, EC-474 §2);\n * - a `pattern` is dropped wherever a `format` explains the same thing (a\n * 200-character email regex teaches a model nothing and costs real tokens);\n * - a field whose projection is knowingly partial (`schemaComplete: false`) SAYS SO\n * in its description rather than presenting the partial rule as the whole rule.\n *\n * EC-505 — one further narrowing of what the model is TOLD (never of what the engine\n * accepts): a `date` property is `type: 'string'` + `format: 'date-time'` rather than\n * the `['string','number']` union the general projection carries, because the model is\n * asked to author an ISO instant (EC-472) and some providers reject a union `type`.\n */\nimport { collectionJsonSchema, fieldJsonSchema, resolveLabel } from '@elytracms/core/cms-core'\nimport type { CollectionDef, FieldDef, JsonSchema } from '@elytracms/core/cms-core'\nimport { MARKDOWN_PROSE_DESCRIPTION } from '@elytracms/core/rich-text'\nimport { constraintSentence, stripRedundantPattern, toModelFacingSchema } from './constraints'\nimport type { AssetCandidates, RelationCandidates } from './types'\n\n/** A relation field's resolved candidate set — how the engine maps ids → reference envelopes. */\nexport interface TargetRelationField {\n fieldName: string\n /** The relation's target collection id. */\n target: string\n multiple: boolean\n /** The allowed candidate ids (the enum the model chose from). */\n candidateIds: string[]\n}\n\n/** An asset field's resolved candidate set — how the engine maps ids → asset envelopes (EC-381). */\nexport interface TargetAssetField {\n fieldName: string\n multiple: boolean\n /** The allowed library asset ids (the enum the model chose from). */\n candidateIds: string[]\n}\n\n/** The derived target shape + the metadata the engine needs to post-process the output. */\nexport interface TargetShape {\n /** The JSON Schema the structured output must satisfy. */\n schema: JsonSchema\n /** Field names removed by `ai.exclude` (untouchable). */\n excluded: string[]\n /** Field names whose values route under `localized[locale]`. */\n localizedFields: string[]\n /** Relation fields with their candidate enums. */\n relationFields: TargetRelationField[]\n /** Asset fields with their candidate enums (EC-381). */\n assetFields: TargetAssetField[]\n}\n\nexport interface BuildTargetShapeInput {\n collection: CollectionDef\n /** Relation candidate ids by field — a relation field with none is omitted from the shape. */\n candidates?: RelationCandidates\n /** Asset candidate ids by field — an asset field with none is omitted from the shape (EC-381). */\n assetCandidates?: AssetCandidates\n /** UI locale for resolving field labels into descriptions. */\n locale?: string\n}\n\n/**\n * A rich-text field's model-facing schema (EC-379): a MARKDOWN string, MERGED over\n * the field's own constraint projection (EC-473 — it used to replace it, silently\n * dropping a declared `min`/`max`/`pattern`). The description carries the allowed\n * subset (from the engine converter — one source of truth), so the model has\n * something concrete to aim at; the engine converts the markdown into the\n * `{ version, doc }` storage envelope at proposal-build time and rejects\n * out-of-subset constructs with explicit issues.\n */\nfunction richTextMarkdownSchema(field: FieldDef): JsonSchema {\n const constraints = stripRedundantPattern(fieldJsonSchema(field))\n return {\n ...constraints,\n type: 'string',\n description: `Rich text, written as ${MARKDOWN_PROSE_DESCRIPTION} Write genuine, publishable prose.`,\n }\n}\n\n/**\n * A field's model-facing description: resolved label + optional `ai.hint` + the\n * constraint clause (EC-473). The constraints are stated in PROSE because that is\n * what a model acts on — the same facts also ride as JSON-Schema keywords for the\n * structured-output validator, which is a different reader.\n */\nfunction describe(field: FieldDef, locale?: string): string | undefined {\n const label = resolveLabel(field.form?.label, locale) ?? field.name\n const hint = field.ai?.hint\n const constraints = constraintSentence(field)\n const parts = [label, hint, constraints].filter(\n (p): p is string => typeof p === 'string' && p.length > 0,\n )\n return parts.length > 0 ? parts.join(' — ') : undefined\n}\n\n/**\n * Build the leash-aware target shape from a collection + candidates. Deterministic;\n * never throws. Reuses `collectionJsonSchema` as the base, then rewrites relations /\n * rich text and strips excluded fields.\n */\nexport function buildTargetShape(input: BuildTargetShapeInput): TargetShape {\n const base = collectionJsonSchema(input.collection)\n const properties: Record<string, JsonSchema> = {\n ...((base.properties as Record<string, JsonSchema>) ?? {}),\n }\n const excluded: string[] = []\n const localizedFields: string[] = []\n const relationFields: TargetRelationField[] = []\n const assetFields: TargetAssetField[] = []\n\n for (const field of input.collection.fields) {\n if (field.localized) localizedFields.push(field.name)\n\n if (field.ai?.exclude) {\n excluded.push(field.name)\n delete properties[field.name]\n continue\n }\n\n if (field.type === 'relation') {\n const candidates = input.candidates?.[field.name] ?? []\n if (candidates.length === 0) {\n // No candidate ids ⇒ omit the field: the model cannot emit a relation value\n // at all, so a hallucinated id is structurally impossible.\n delete properties[field.name]\n continue\n }\n const ids = candidates.map((c) => c.id)\n const label = candidates\n .map((c) => (c.label ? `${c.id} (${c.label})` : c.id))\n .join(', ')\n const idSchema: JsonSchema = {\n type: 'string',\n enum: ids,\n description: `Choose one of: ${label}`,\n }\n properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema\n relationFields.push({\n fieldName: field.name,\n target: field.target,\n multiple: field.multiple,\n candidateIds: ids,\n })\n } else if (field.type === 'asset') {\n // EC-381 — same leash as relations: an asset field is an enum over the ids\n // the model actually searched in the media library (`listAssets`). No\n // searched assets ⇒ omit the field, so the proposal simply leaves it empty\n // (an invented / to-be-uploaded id is structurally unrepresentable).\n const candidates = input.assetCandidates?.[field.name] ?? []\n if (candidates.length === 0) {\n delete properties[field.name]\n continue\n }\n const ids = candidates.map((c) => c.id)\n const label = candidates\n .map((c) => (c.label ? `${c.id} (${c.label})` : c.id))\n .join(', ')\n const idSchema: JsonSchema = {\n type: 'string',\n enum: ids,\n description: `Choose an EXISTING library asset id, one of: ${label}. Leave empty if none fits.`,\n }\n properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema\n assetFields.push({ fieldName: field.name, multiple: field.multiple, candidateIds: ids })\n } else if (field.type === 'richText') {\n properties[field.name] = richTextMarkdownSchema(field)\n }\n\n const prop = properties[field.name]\n if (prop) {\n // EC-473: a `pattern` next to a `format` is 200+ characters that teach the\n // model nothing — the format (and the prose constraints below) say it better.\n // EC-505: and a `date`'s `['string','number']` union becomes the ISO string\n // the model is actually asked to author.\n const stripped = toModelFacingSchema(prop)\n const desc = describe(field, input.locale)\n // Preserve an existing description (e.g. relation enum / rich-text note), but\n // append the constraint clause to it — the enum leash and the value rules are\n // different facts and the model needs both.\n const next: JsonSchema = stripped === prop ? prop : { ...stripped }\n if (desc !== undefined) {\n const existing = typeof next.description === 'string' ? next.description : undefined\n const constraints = constraintSentence(field)\n next.description =\n existing === undefined\n ? desc\n : constraints !== undefined\n ? `${existing} — ${constraints}`\n : existing\n }\n properties[field.name] = next\n }\n }\n\n const requiredIn = Array.isArray(base.required) ? (base.required as string[]) : []\n const required = requiredIn.filter((n) => !excluded.includes(n) && properties[n] !== undefined)\n\n const schema: JsonSchema = {\n type: 'object',\n properties,\n additionalProperties: false,\n ...(required.length > 0 ? { required } : {}),\n }\n return { schema, excluded, localizedFields, relationFields, assetFields }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../src/lib/ai/shape.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Target-shape derivation for `applyContentWithShape` (EC-331).\n *\n * Reuses the existing collection→JSON-Schema derivation (`collectionJsonSchema`,\n * cms-core) rather than writing a new one, then applies the three leashes the AI\n * engine adds on top:\n *\n * - `ai.exclude` fields are removed entirely (the model never sees them → cannot\n * write them).\n * - relation fields become an ENUM over the provided candidate ids (or are omitted\n * when no candidates exist) — so a hallucinated relation id is not representable\n * as valid structured output.\n * - asset/image fields become an ENUM over the searched library asset ids (or are\n * omitted when none were searched) — EC-381, the SAME leash as relations: a\n * proposed asset id must be a real, `listAssets`-returned library asset, and a\n * create shape with no searched assets simply has no asset property (empty beats\n * wrong; the assistant never generates or uploads).\n * - rich-text fields are MARKDOWN STRINGS (EC-379): the description names the\n * allowed subset, and the engine converts md → the `{ version, doc }` storage\n * envelope at proposal-build time. The field's `allow` config leashes component\n * EMBEDS only — markdown cannot express an embed, so the leash is satisfied by\n * construction (and still enforced downstream by `validateRichTextEmbeds`).\n *\n * Localized fields stay flat in the shape (the model authors one locale at a time);\n * the engine routes their values under `localized[locale]`.\n *\n * EC-473 — the shape now consumes the ONE constraint projection, so a rule declared\n * in config actually reaches the model:\n *\n * - `collectionJsonSchema` merges declarative rules with the fragment derived from\n * a schema-form `validation.validate`, so `{ min: 2 }` and `z.string().min(2)`\n * arrive identically (and a declared DATE RANGE arrives at all — it previously\n * reached no consumer whatsoever);\n * - a rich-text field no longer OVERWRITES its constraints: the markdown schema is\n * merged over them instead of replacing them (measured drift, EC-474 §2);\n * - a `pattern` is dropped wherever a `format` explains the same thing (a\n * 200-character email regex teaches a model nothing and costs real tokens);\n * - a field whose projection is knowingly partial (`schemaComplete: false`) SAYS SO\n * in its description rather than presenting the partial rule as the whole rule.\n *\n * EC-505 — one further narrowing of what the model is TOLD (never of what the engine\n * accepts): a `date` property is `type: 'string'` + `format: 'date-time'` rather than\n * the `['string','number']` union the general projection carries, because the model is\n * asked to author an ISO instant (EC-472) and some providers reject a union `type`.\n *\n * EC-556 — the projection is VALIDATOR-facing (the value at rest: asset/relation\n * envelopes, the v2 rich-text object). Every property here is its WRITER form\n * (`writerFieldSchema`), derived from that projection at every depth — a bare asset\n * id, a `{ collection, id }` reference, a markdown string — so an asset, relation or\n * rich-text SUBFIELD of an `object` is told the same form as a top-level one. The\n * top-level relation/asset id enums (EC-381) override the writer form there.\n */\nimport { collectionJsonSchema, resolveLabel } from '@elytracms/core/cms-core'\nimport type { CollectionDef, FieldDef, JsonSchema } from '@elytracms/core/cms-core'\nimport { constraintSentence, toModelFacingSchema, writerFieldSchema } from './constraints'\nimport type { AssetCandidates, RelationCandidates } from './types'\n\n/** A relation field's resolved candidate set — how the engine maps ids → reference envelopes. */\nexport interface TargetRelationField {\n fieldName: string\n /** The relation's target collection id. */\n target: string\n multiple: boolean\n /** The allowed candidate ids (the enum the model chose from). */\n candidateIds: string[]\n}\n\n/** An asset field's resolved candidate set — how the engine maps ids → asset envelopes (EC-381). */\nexport interface TargetAssetField {\n fieldName: string\n multiple: boolean\n /** The allowed library asset ids (the enum the model chose from). */\n candidateIds: string[]\n}\n\n/** The derived target shape + the metadata the engine needs to post-process the output. */\nexport interface TargetShape {\n /** The JSON Schema the structured output must satisfy. */\n schema: JsonSchema\n /** Field names removed by `ai.exclude` (untouchable). */\n excluded: string[]\n /** Field names whose values route under `localized[locale]`. */\n localizedFields: string[]\n /** Relation fields with their candidate enums. */\n relationFields: TargetRelationField[]\n /** Asset fields with their candidate enums (EC-381). */\n assetFields: TargetAssetField[]\n}\n\nexport interface BuildTargetShapeInput {\n collection: CollectionDef\n /** Relation candidate ids by field — a relation field with none is omitted from the shape. */\n candidates?: RelationCandidates\n /** Asset candidate ids by field — an asset field with none is omitted from the shape (EC-381). */\n assetCandidates?: AssetCandidates\n /** UI locale for resolving field labels into descriptions. */\n locale?: string\n}\n\n/**\n * A field's model-facing description: resolved label + optional `ai.hint` + the\n * constraint clause (EC-473). The constraints are stated in PROSE because that is\n * what a model acts on — the same facts also ride as JSON-Schema keywords for the\n * structured-output validator, which is a different reader.\n */\nfunction describe(field: FieldDef, locale?: string): string | undefined {\n const label = resolveLabel(field.form?.label, locale) ?? field.name\n const hint = field.ai?.hint\n const constraints = constraintSentence(field)\n const parts = [label, hint, constraints].filter(\n (p): p is string => typeof p === 'string' && p.length > 0,\n )\n return parts.length > 0 ? parts.join(' — ') : undefined\n}\n\n/**\n * Build the leash-aware target shape from a collection + candidates. Deterministic;\n * never throws. Reuses `collectionJsonSchema` as the base (its `required` list), then\n * gives every property its writer form (EC-556) or its id-enum leash, and strips\n * excluded fields.\n */\nexport function buildTargetShape(input: BuildTargetShapeInput): TargetShape {\n const base = collectionJsonSchema(input.collection)\n const properties: Record<string, JsonSchema> = {\n ...((base.properties as Record<string, JsonSchema>) ?? {}),\n }\n const excluded: string[] = []\n const localizedFields: string[] = []\n const relationFields: TargetRelationField[] = []\n const assetFields: TargetAssetField[] = []\n\n for (const field of input.collection.fields) {\n if (field.localized) localizedFields.push(field.name)\n\n if (field.ai?.exclude) {\n excluded.push(field.name)\n delete properties[field.name]\n continue\n }\n\n if (field.type === 'relation') {\n const candidates = input.candidates?.[field.name] ?? []\n if (candidates.length === 0) {\n // No candidate ids ⇒ omit the field: the model cannot emit a relation value\n // at all, so a hallucinated id is structurally impossible.\n delete properties[field.name]\n continue\n }\n const ids = candidates.map((c) => c.id)\n const label = candidates\n .map((c) => (c.label ? `${c.id} (${c.label})` : c.id))\n .join(', ')\n const idSchema: JsonSchema = {\n type: 'string',\n enum: ids,\n description: `Choose one of: ${label}`,\n }\n properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema\n relationFields.push({\n fieldName: field.name,\n target: field.target,\n multiple: field.multiple,\n candidateIds: ids,\n })\n } else if (field.type === 'asset') {\n // EC-381 — same leash as relations: an asset field is an enum over the ids\n // the model actually searched in the media library (`listAssets`). No\n // searched assets ⇒ omit the field, so the proposal simply leaves it empty\n // (an invented / to-be-uploaded id is structurally unrepresentable).\n const candidates = input.assetCandidates?.[field.name] ?? []\n if (candidates.length === 0) {\n delete properties[field.name]\n continue\n }\n const ids = candidates.map((c) => c.id)\n const label = candidates\n .map((c) => (c.label ? `${c.id} (${c.label})` : c.id))\n .join(', ')\n const idSchema: JsonSchema = {\n type: 'string',\n enum: ids,\n description: `Choose an EXISTING library asset id, one of: ${label}. Leave empty if none fits.`,\n }\n properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema\n assetFields.push({ fieldName: field.name, multiple: field.multiple, candidateIds: ids })\n } else {\n // EC-556: the writer form at every depth (rich text → markdown, EC-379 / D2;\n // nested asset / relation → the bare forms the write-time lens wraps).\n properties[field.name] = writerFieldSchema(field)\n }\n\n const prop = properties[field.name]\n if (prop) {\n // EC-473: a `pattern` next to a `format` is 200+ characters that teach the\n // model nothing — the format (and the prose constraints below) say it better.\n // EC-505: and a `date`'s `['string','number']` union becomes the ISO string\n // the model is actually asked to author.\n const stripped = toModelFacingSchema(prop)\n const desc = describe(field, input.locale)\n // Preserve an existing description (e.g. relation enum / rich-text note), but\n // append the constraint clause to it — the enum leash and the value rules are\n // different facts and the model needs both.\n const next: JsonSchema = stripped === prop ? prop : { ...stripped }\n if (desc !== undefined) {\n const existing = typeof next.description === 'string' ? next.description : undefined\n const constraints = constraintSentence(field)\n next.description =\n existing === undefined\n ? desc\n : constraints !== undefined\n ? `${existing} — ${constraints}`\n : existing\n }\n properties[field.name] = next\n }\n }\n\n const requiredIn = Array.isArray(base.required) ? (base.required as string[]) : []\n const required = requiredIn.filter((n) => !excluded.includes(n) && properties[n] !== undefined)\n\n const schema: JsonSchema = {\n type: 'object',\n properties,\n additionalProperties: false,\n ...(required.length > 0 ? { required } : {}),\n }\n return { schema, excluded, localizedFields, relationFields, assetFields }\n}\n"]}
|
package/dist/lib/ai/tools.d.ts
CHANGED
|
@@ -41,10 +41,16 @@ export interface DocumentSearchResult {
|
|
|
41
41
|
id: string;
|
|
42
42
|
title?: string;
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* An asset the model may reference (from a `listAssets` read). EC-588: it carries the
|
|
46
|
+
* two facts a field's `accept` is judged on, so each asset field's candidates can be
|
|
47
|
+
* filtered to what that field takes — and the model can see what each asset is.
|
|
48
|
+
*/
|
|
45
49
|
export interface AssetSearchResult {
|
|
46
50
|
id: string;
|
|
47
51
|
title?: string;
|
|
52
|
+
contentType: string;
|
|
53
|
+
filename: string;
|
|
48
54
|
}
|
|
49
55
|
/**
|
|
50
56
|
* The client-side, operations-backed readers the executor calls. The studio binds
|
|
@@ -210,9 +216,9 @@ export interface ChatCandidateStore {
|
|
|
210
216
|
candidatesFor(collection: CollectionDef): RelationCandidates;
|
|
211
217
|
/**
|
|
212
218
|
* Candidates for a target collection's asset/image fields (EC-381), keyed by
|
|
213
|
-
* field name —
|
|
214
|
-
*
|
|
215
|
-
*
|
|
219
|
+
* field name — the one flat searched-asset pool (`listAssets` has no field
|
|
220
|
+
* dimension), filtered per field by that field's `accept` (EC-588). A field with no
|
|
221
|
+
* fitting candidate is absent, so it is omitted from the shape (empty beats wrong).
|
|
216
222
|
*/
|
|
217
223
|
assetCandidatesFor(collection: CollectionDef): AssetCandidates;
|
|
218
224
|
}
|
package/dist/lib/ai/tools.js
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* with the hand-rolled orchestrator (EC-334 slice 2).
|
|
33
33
|
*/
|
|
34
34
|
import { z } from 'zod';
|
|
35
|
-
import { resolveDocumentTitle } from '@elytracms/core/cms-core';
|
|
35
|
+
import { assetMatchesAccept, resolveDocumentTitle } from '@elytracms/core/cms-core';
|
|
36
36
|
import { parseRichTextValue, richTextValueToMarkdownProse } from '@elytracms/core/rich-text';
|
|
37
37
|
import { applyContentWithShape } from './apply-content.js';
|
|
38
38
|
import { translateDocument } from './translate-document.js';
|
|
@@ -68,7 +68,8 @@ export const chatToolDefinitions = {
|
|
|
68
68
|
},
|
|
69
69
|
listAssets: {
|
|
70
70
|
description: 'List or search assets (images, files). Use it to find REAL asset ids — never ' +
|
|
71
|
-
'invent one. Returns { id, title } summaries
|
|
71
|
+
'invent one. Returns { id, title, contentType, filename } summaries; an asset ' +
|
|
72
|
+
'field only takes the kinds it accepts.',
|
|
72
73
|
inputSchema: z.object({
|
|
73
74
|
query: z.string().optional().describe('Optional search text.'),
|
|
74
75
|
limit: z.number().int().positive().optional().describe('Max results.'),
|
|
@@ -219,11 +220,18 @@ export function createChatCandidateStore() {
|
|
|
219
220
|
const candidates = {};
|
|
220
221
|
if (searchedAssets.size === 0)
|
|
221
222
|
return candidates;
|
|
222
|
-
const pool = [...searchedAssets.values()]
|
|
223
|
+
const pool = [...searchedAssets.values()];
|
|
223
224
|
for (const field of collection.fields) {
|
|
224
225
|
if (field.type !== 'asset')
|
|
225
226
|
continue;
|
|
226
|
-
|
|
227
|
+
// EC-588 (A4): the field's enum is the pool filtered by ITS `accept` — an image
|
|
228
|
+
// field is never offered a PDF. The validator re-run at apply is the guarantee
|
|
229
|
+
// (`findAsset` checks the record); this keeps the model from spending its
|
|
230
|
+
// repair budget on a mismatch the leash can rule out up front.
|
|
231
|
+
const fitting = pool.filter((asset) => assetMatchesAccept(asset, field.accept));
|
|
232
|
+
if (fitting.length === 0)
|
|
233
|
+
continue;
|
|
234
|
+
candidates[field.name] = fitting.map((a) => ({ id: a.id, ...(a.title ? { label: a.title } : {}) }));
|
|
227
235
|
}
|
|
228
236
|
return candidates;
|
|
229
237
|
},
|
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 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\n .string()\n .describe(\n 'Field name from the collection schema. The catalog in the system context ' +\n 'lists every field with its type and the rules its value must satisfy.',\n ),\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 ' +\n \"field/prop. It MUST satisfy that field's rules as listed in the catalog \" +\n '(length bounds, format, allowed values, date range…); the studio validates ' +\n 'the merged document and rejects the proposal with the failing rules if it ' +\n 'does not. Where the catalog says further rules apply, keep the value ' +\n 'conventional and plausible.',\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 /**\n * THE validation option bag (EC-497) — the CMS session's\n * `gateValidateOptions(...)`, forwarded whole to every engine below. EC-473\n * made the assistant's gate the editor's gate; passing one bag instead of five\n * keys makes it stay that way, because a key can no longer be dropped by being\n * forgotten at one of the three call sites this replaced.\n */\n validateOptions?: ValidateDocumentOptions\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 /**\n * EC-497 — the ONE bag every engine below is handed, assembled in exactly one\n * place instead of at each of the three former call sites.\n *\n * The executor's own `localeConfig` is a FLOOR, never an override: the session's\n * bag wins wherever it speaks, but a bag that omits the project's locales must\n * not be able to make the gate locale-blind — a localized bound would then go\n * unchecked, which is precisely the class of hole this task closed.\n */\n const validateOptions: ValidateDocumentOptions = {\n ...(options.localeConfig ? { localeConfig: options.localeConfig } : {}),\n ...(options.validateOptions ?? {}),\n }\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 validateOptions,\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 validateOptions,\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 // EC-497: the engine gates its own output; both callers hand it the session\n // registry + THE bag so its verdict is the editor's verdict.\n registry: options.registry,\n validateOptions,\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 // EC-473 established that a translated draft is validated before it is proposed\n // (this path once proposed unvalidated content). EC-497 moved the gate INTO the\n // engine so the detail-editor's header action — the other caller — inherits it\n // instead of writing model output straight to the document. Only errors the\n // translation INTRODUCED block it; pre-existing brokenness is not the\n // assistant's doing.\n if (result.introducedErrors.length > 0) {\n return {\n ok: false,\n errorText: `The translation did not pass validation: ${issueSummary(result.introducedErrors)}`,\n }\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 // Warnings ride onto the proposal like every other path's (EC-496 renders them).\n issues: [...result.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"]}
|
|
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 { assetMatchesAccept, 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/**\n * An asset the model may reference (from a `listAssets` read). EC-588: it carries the\n * two facts a field's `accept` is judged on, so each asset field's candidates can be\n * filtered to what that field takes — and the model can see what each asset is.\n */\nexport interface AssetSearchResult {\n id: string\n title?: string\n contentType: string\n filename: 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, contentType, filename } summaries; an asset ' +\n 'field only takes the kinds it accepts.',\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\n .string()\n .describe(\n 'Field name from the collection schema. The catalog in the system context ' +\n 'lists every field with its type and the rules its value must satisfy.',\n ),\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 ' +\n \"field/prop. It MUST satisfy that field's rules as listed in the catalog \" +\n '(length bounds, format, allowed values, date range…); the studio validates ' +\n 'the merged document and rejects the proposal with the failing rules if it ' +\n 'does not. Where the catalog says further rules apply, keep the value ' +\n 'conventional and plausible.',\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 — the one flat searched-asset pool (`listAssets` has no field\n * dimension), filtered per field by that field's `accept` (EC-588). A field with no\n * fitting candidate is absent, so it is 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()]\n for (const field of collection.fields) {\n if (field.type !== 'asset') continue\n // EC-588 (A4): the field's enum is the pool filtered by ITS `accept` — an image\n // field is never offered a PDF. The validator re-run at apply is the guarantee\n // (`findAsset` checks the record); this keeps the model from spending its\n // repair budget on a mismatch the leash can rule out up front.\n const fitting = pool.filter((asset) => assetMatchesAccept(asset, field.accept))\n if (fitting.length === 0) continue\n candidates[field.name] = fitting.map(\n (a): AssetCandidate => ({ id: a.id, ...(a.title ? { label: a.title } : {}) }),\n )\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 /**\n * THE validation option bag (EC-497) — the CMS session's\n * `gateValidateOptions(...)`, forwarded whole to every engine below. EC-473\n * made the assistant's gate the editor's gate; passing one bag instead of five\n * keys makes it stay that way, because a key can no longer be dropped by being\n * forgotten at one of the three call sites this replaced.\n */\n validateOptions?: ValidateDocumentOptions\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 /**\n * EC-497 — the ONE bag every engine below is handed, assembled in exactly one\n * place instead of at each of the three former call sites.\n *\n * The executor's own `localeConfig` is a FLOOR, never an override: the session's\n * bag wins wherever it speaks, but a bag that omits the project's locales must\n * not be able to make the gate locale-blind — a localized bound would then go\n * unchecked, which is precisely the class of hole this task closed.\n */\n const validateOptions: ValidateDocumentOptions = {\n ...(options.localeConfig ? { localeConfig: options.localeConfig } : {}),\n ...(options.validateOptions ?? {}),\n }\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 validateOptions,\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 validateOptions,\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 // EC-497: the engine gates its own output; both callers hand it the session\n // registry + THE bag so its verdict is the editor's verdict.\n registry: options.registry,\n validateOptions,\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 // EC-473 established that a translated draft is validated before it is proposed\n // (this path once proposed unvalidated content). EC-497 moved the gate INTO the\n // engine so the detail-editor's header action — the other caller — inherits it\n // instead of writing model output straight to the document. Only errors the\n // translation INTRODUCED block it; pre-existing brokenness is not the\n // assistant's doing.\n if (result.introducedErrors.length > 0) {\n return {\n ok: false,\n errorText: `The translation did not pass validation: ${issueSummary(result.introducedErrors)}`,\n }\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 // Warnings ride onto the proposal like every other path's (EC-496 renders them).\n issues: [...result.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
|
@@ -242,6 +242,14 @@ export interface AiRouteHandlerOptions {
|
|
|
242
242
|
limits?: AiLimits;
|
|
243
243
|
/** Derive the per-project rate-bucket key from a request (default: one global bucket). */
|
|
244
244
|
projectKey?: (request: Request) => string;
|
|
245
|
+
/**
|
|
246
|
+
* Millisecond clock the rate window is decided against (default `Date.now`).
|
|
247
|
+
* Injected so the 60 s boundary is testable with a fixed clock (EC-602, the
|
|
248
|
+
* AGENTS.md §4 determinism rule). Milliseconds rather than the house ISO
|
|
249
|
+
* `Clock` (`@elytracms/core/determinism`): that one stamps records; this one
|
|
250
|
+
* does window arithmetic.
|
|
251
|
+
*/
|
|
252
|
+
now?: () => number;
|
|
245
253
|
}
|
|
246
254
|
/** The mounted route handler: a framework-agnostic Web `Request → Response`. */
|
|
247
255
|
export type AiRouteHandler = (request: Request) => Promise<Response>;
|