@elytracms/studio 0.0.56 → 0.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/convex/auth.ts +7 -0
  2. package/convex/users.ts +41 -14
  3. package/dist/components/ai/ai-chat-panel-lazy.js +3 -3
  4. package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
  5. package/dist/components/ai/ai-chat-panel.d.ts +0 -9
  6. package/dist/components/ai/ai-chat-panel.js +199 -60
  7. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  8. package/dist/components/ai/chat-ui.d.ts +62 -10
  9. package/dist/components/ai/chat-ui.js +139 -23
  10. package/dist/components/ai/chat-ui.js.map +1 -1
  11. package/dist/components/ai/proposal-card.d.ts +30 -7
  12. package/dist/components/ai/proposal-card.js +81 -21
  13. package/dist/components/ai/proposal-card.js.map +1 -1
  14. package/dist/components/cms/detail-editor.js +12 -0
  15. package/dist/components/cms/detail-editor.js.map +1 -1
  16. package/dist/components/cms/document-diff-view.d.ts +12 -1
  17. package/dist/components/cms/document-diff-view.js +119 -42
  18. package/dist/components/cms/document-diff-view.js.map +1 -1
  19. package/dist/components/cms/rich-text-preview.d.ts +15 -0
  20. package/dist/components/cms/rich-text-preview.js +81 -0
  21. package/dist/components/cms/rich-text-preview.js.map +1 -0
  22. package/dist/components/cms/version-compare-text.d.ts +26 -0
  23. package/dist/components/cms/version-compare-text.js +60 -0
  24. package/dist/components/cms/version-compare-text.js.map +1 -1
  25. package/dist/lib/ai/anthropic-provider.d.ts +0 -12
  26. package/dist/lib/ai/anthropic-provider.js +3 -11
  27. package/dist/lib/ai/anthropic-provider.js.map +1 -1
  28. package/dist/lib/ai/apply-content.js +52 -10
  29. package/dist/lib/ai/apply-content.js.map +1 -1
  30. package/dist/lib/ai/chat-context.d.ts +74 -0
  31. package/dist/lib/ai/chat-context.js +100 -0
  32. package/dist/lib/ai/chat-context.js.map +1 -0
  33. package/dist/lib/ai/chat-loop.d.ts +70 -0
  34. package/dist/lib/ai/chat-loop.js +46 -0
  35. package/dist/lib/ai/chat-loop.js.map +1 -0
  36. package/dist/lib/ai/chat-state.d.ts +73 -57
  37. package/dist/lib/ai/chat-state.js +49 -90
  38. package/dist/lib/ai/chat-state.js.map +1 -1
  39. package/dist/lib/ai/fake-provider.d.ts +139 -6
  40. package/dist/lib/ai/fake-provider.js +261 -8
  41. package/dist/lib/ai/fake-provider.js.map +1 -1
  42. package/dist/lib/ai/gateway-provider.d.ts +30 -0
  43. package/dist/lib/ai/gateway-provider.js +73 -0
  44. package/dist/lib/ai/gateway-provider.js.map +1 -0
  45. package/dist/lib/ai/index.d.ts +5 -1
  46. package/dist/lib/ai/index.js +5 -1
  47. package/dist/lib/ai/index.js.map +1 -1
  48. package/dist/lib/ai/node-handler.d.ts +13 -4
  49. package/dist/lib/ai/node-handler.js +33 -3
  50. package/dist/lib/ai/node-handler.js.map +1 -1
  51. package/dist/lib/ai/rich-text-input.d.ts +27 -0
  52. package/dist/lib/ai/rich-text-input.js +25 -0
  53. package/dist/lib/ai/rich-text-input.js.map +1 -0
  54. package/dist/lib/ai/route-provider.d.ts +35 -10
  55. package/dist/lib/ai/route-provider.js +13 -6
  56. package/dist/lib/ai/route-provider.js.map +1 -1
  57. package/dist/lib/ai/route.d.ts +50 -21
  58. package/dist/lib/ai/route.js +69 -40
  59. package/dist/lib/ai/route.js.map +1 -1
  60. package/dist/lib/ai/shape.d.ts +12 -1
  61. package/dist/lib/ai/shape.js +46 -20
  62. package/dist/lib/ai/shape.js.map +1 -1
  63. package/dist/lib/ai/tools.d.ts +242 -37
  64. package/dist/lib/ai/tools.js +548 -63
  65. package/dist/lib/ai/tools.js.map +1 -1
  66. package/dist/lib/ai/types.d.ts +31 -16
  67. package/dist/lib/ai/types.js.map +1 -1
  68. package/dist/lib/ai/update-fields.d.ts +68 -0
  69. package/dist/lib/ai/update-fields.js +296 -0
  70. package/dist/lib/ai/update-fields.js.map +1 -0
  71. package/dist/lib/ai-current-document.d.ts +53 -0
  72. package/dist/lib/ai-current-document.js +54 -0
  73. package/dist/lib/ai-current-document.js.map +1 -0
  74. package/dist/lib/auth/convex-auth-adapter.d.ts +9 -1
  75. package/dist/lib/auth/convex-auth-adapter.js +28 -2
  76. package/dist/lib/auth/convex-auth-adapter.js.map +1 -1
  77. package/dist/lib/cms-views/view-model.d.ts +6 -6
  78. package/dist/lib/i18n/messages.en.js +19 -1
  79. package/dist/lib/i18n/messages.en.js.map +1 -1
  80. package/dist/ui/badge.d.ts +1 -1
  81. package/package.json +4 -3
  82. package/dist/lib/ai/create-flow.d.ts +0 -132
  83. package/dist/lib/ai/create-flow.js +0 -280
  84. package/dist/lib/ai/create-flow.js.map +0 -1
@@ -1,16 +1,41 @@
1
1
  /**
2
- * The narrow tool set the creation flow's model works against (EC-199).
2
+ * The narrow tool set the assistant works against (EC-199, reshaped by EC-334).
3
3
  *
4
4
  * Per the ratified AD, tools execute CLIENT-side against the operations layer with
5
- * the user's own session — this module only declares their SHAPES (the model-facing
6
- * planner schema) and the reader interface the studio binds to operations. The engine
7
- * never gains a write path: `searchDocuments` / `listAssets` are reads that gather the
8
- * real ids a proposal may reference (a relation id is otherwise structurally
9
- * impossible — see `buildTargetShape`), and `createDocument` is the terminal action
10
- * that runs `applyContentWithShape` and yields a reviewable proposal. `updateFields`
11
- * and `translate` are deliberately NOT here — they are later slices (EC-332 / post-v1).
5
+ * the user's own session. This module carries the three pieces of that contract:
6
+ *
7
+ * 1. {@link chatToolDefinitions} — the SDK-native tool DEFINITIONS the route's
8
+ * chat branch declares in `streamText`, deliberately WITHOUT `execute`, so the
9
+ * SDK forwards every call to the client (the route stays a zero-data-access
10
+ * proxy). Plain `{ description, inputSchema }` objects with zod schemas —
11
+ * structurally the SDK's `FunctionTool`, with no SDK import (bundle discipline).
12
+ * 2. {@link ChatToolReaders} — the operations-backed read interface the studio
13
+ * binds client-side (search / list / get). Reads only — the engine never gains
14
+ * a write path; apply is a separate, human-gated draft write.
15
+ * 3. {@link createChatToolExecutor} — the client executor map `useChat`'s
16
+ * `onToolCall` dispatches into. Readers pass through; the terminal tools run
17
+ * the deterministic engines (`applyContentWithShape` / `translateDocument` /
18
+ * `buildUpdateProposal`) and return a COMPACT proposal summary as the tool
19
+ * output — the full proposal goes to the review UI via
20
+ * `onProposal(toolCallId, …)`, never to the model.
21
+ *
22
+ * EC-378: `updateFields` is the edit-flow terminal tool — the model authors
23
+ * per-field changes directly (it has `getDocument` output + the conversation as
24
+ * context); the executor reads the CURRENT document, builds a validated update
25
+ * proposal (`buildUpdateProposal`), and hands it to the review sink. Its target
26
+ * (and `getDocument`'s) may be omitted to mean the document currently open in
27
+ * the editor — resolved via the {@link ChatToolExecutorOptions.currentDocument}
28
+ * seam ({@link resolveDocumentTarget}); the context block the panel injects is
29
+ * ADVISORY, the resolution here is what makes "this document" deterministic.
30
+ *
31
+ * The EC-199 planner enum/schema (`plannerSchema` / `parsePlannerAction`) retired
32
+ * with the hand-rolled orchestrator (EC-334 slice 2).
12
33
  */
13
- import type { CmsDocument, JsonSchema } from '@elytracms/core/cms-core';
34
+ import { z } from 'zod';
35
+ import type { CmsDocument, CollectionDef, CollectionRegistry, LocaleConfig, ValidateDocumentOptions } from '@elytracms/core/cms-core';
36
+ import type { ComponentManifest } from '@elytracms/core/component-registry';
37
+ import type { ChatProposal } from './chat-state.js';
38
+ import type { AiProvider, AssetCandidates, RelationCandidates, ResolvedKnowledge } from './types.js';
14
39
  /** A document the model may reference as a relation target (from a `searchDocuments` read). */
15
40
  export interface DocumentSearchResult {
16
41
  id: string;
@@ -22,11 +47,11 @@ export interface AssetSearchResult {
22
47
  title?: string;
23
48
  }
24
49
  /**
25
- * The client-side, operations-backed readers the orchestrator calls. The studio binds
50
+ * The client-side, operations-backed readers the executor calls. The studio binds
26
51
  * these to the user's authenticated operations client; tests bind an in-memory fake.
27
52
  * Reads only — there is no write tool (apply is a separate, human-gated draft write).
28
53
  */
29
- export interface CreateFlowTools {
54
+ export interface ChatToolReaders {
30
55
  searchDocuments(input: {
31
56
  collection: string;
32
57
  query?: string;
@@ -37,41 +62,221 @@ export interface CreateFlowTools {
37
62
  limit?: number;
38
63
  }): Promise<AssetSearchResult[]>;
39
64
  /**
40
- * Fetch a full document (values + localized) for the translate flow (EC-332). Optional:
41
- * the creation flow never needs it, so a studio that only wires create can omit it.
65
+ * Fetch a full document (values + localized) — the translate flow and the model's
66
+ * `getDocument` tool. Optional: a studio that only wires create can omit it.
42
67
  */
43
68
  getDocument?(input: {
44
69
  collection: string;
45
70
  id: string;
46
71
  }): Promise<CmsDocument | null>;
47
72
  }
48
- /** One planned step the model returns — the discriminated tool/action union. */
49
- export type PlannerAction = {
50
- action: 'searchDocuments';
51
- collection: string;
52
- query?: string;
53
- } | {
54
- action: 'listAssets';
55
- query?: string;
56
- } | {
57
- action: 'createDocument';
73
+ /** Per-search result cap (protects the wire — results become model context). */
74
+ export declare const DEFAULT_SEARCH_LIMIT = 20;
75
+ /**
76
+ * One model-facing tool definition for the route's `streamText` call. Structurally
77
+ * the SDK's `FunctionTool` WITHOUT `execute` (execution is client-side, always) —
78
+ * typed locally so this module needs no SDK import.
79
+ */
80
+ export interface ChatToolDefinition {
81
+ description: string;
82
+ /** Zod input schema (the AI SDK accepts zod schemas as `inputSchema` directly). */
83
+ inputSchema: z.ZodType;
84
+ }
85
+ /**
86
+ * The tool set the native chat branch declares (EC-334). No `execute` on any entry:
87
+ * the SDK forwards each call to the client, which executes reads against the
88
+ * operations layer with the user's session and posts the result back; the terminal
89
+ * tools (`createDocument` / `translateDocument`) run the deterministic engines
90
+ * client-side and return a compact proposal summary as the tool output. The route
91
+ * itself never executes anything.
92
+ *
93
+ * Collections are NOT enum-leashed here — the route has zero data access, so it has
94
+ * no catalog; the catalog reaches the model through the request `context` (system
95
+ * prompt), and every id a proposal references must come from a tool RESULT
96
+ * (client-validated; `applyContentWithShape` still enum-leashes relation ids).
97
+ */
98
+ export declare const chatToolDefinitions: {
99
+ searchDocuments: {
100
+ description: string;
101
+ inputSchema: z.ZodObject<{
102
+ collection: z.ZodString;
103
+ query: z.ZodOptional<z.ZodString>;
104
+ limit: z.ZodOptional<z.ZodNumber>;
105
+ }, z.core.$strip>;
106
+ };
107
+ listAssets: {
108
+ description: string;
109
+ inputSchema: z.ZodObject<{
110
+ query: z.ZodOptional<z.ZodString>;
111
+ limit: z.ZodOptional<z.ZodNumber>;
112
+ }, z.core.$strip>;
113
+ };
114
+ getDocument: {
115
+ description: string;
116
+ inputSchema: z.ZodObject<{
117
+ collection: z.ZodOptional<z.ZodString>;
118
+ id: z.ZodOptional<z.ZodString>;
119
+ }, z.core.$strip>;
120
+ };
121
+ createDocument: {
122
+ description: string;
123
+ inputSchema: z.ZodObject<{
124
+ collection: z.ZodString;
125
+ instruction: z.ZodString;
126
+ }, z.core.$strip>;
127
+ };
128
+ updateFields: {
129
+ description: string;
130
+ inputSchema: z.ZodObject<{
131
+ collection: z.ZodOptional<z.ZodString>;
132
+ id: z.ZodOptional<z.ZodString>;
133
+ changes: z.ZodArray<z.ZodObject<{
134
+ field: z.ZodString;
135
+ locale: z.ZodOptional<z.ZodString>;
136
+ blockId: z.ZodOptional<z.ZodString>;
137
+ prop: z.ZodOptional<z.ZodString>;
138
+ value: z.ZodOptional<z.ZodUnknown>;
139
+ }, z.core.$strip>>;
140
+ }, z.core.$strip>;
141
+ };
142
+ translateDocument: {
143
+ description: string;
144
+ inputSchema: z.ZodObject<{
145
+ collection: z.ZodString;
146
+ id: z.ZodString;
147
+ locale: z.ZodOptional<z.ZodString>;
148
+ }, z.core.$strip>;
149
+ };
150
+ };
151
+ /** One forwarded tool call, as `useChat`'s `onToolCall` receives it. */
152
+ export interface ChatToolCall {
153
+ toolCallId: string;
154
+ toolName: string;
155
+ input: unknown;
156
+ }
157
+ /** The coordinates of the document currently open in the editor (EC-378 seam). */
158
+ export interface CurrentDocumentRef {
58
159
  collection: string;
59
- instruction?: string;
160
+ id: string;
161
+ }
162
+ /**
163
+ * Resolve a tool call's target document from its (optional) input coordinates
164
+ * and the current-document seam (EC-378). Deterministic:
165
+ *
166
+ * - both given → use them verbatim;
167
+ * - both omitted → the currently open document (error when none is open);
168
+ * - `collection` given with `id` omitted → the open document ONLY when the
169
+ * collections match — a named collection must never silently cross onto a
170
+ * different open document.
171
+ *
172
+ * Pure — tested directly; `getDocument` and `updateFields` share it.
173
+ */
174
+ export declare function resolveDocumentTarget(input: {
175
+ collection?: string;
176
+ id?: string;
177
+ }, current: CurrentDocumentRef | null | undefined): {
178
+ ok: true;
179
+ ref: CurrentDocumentRef;
60
180
  } | {
61
- action: 'translateDocument';
62
- collection: string;
63
- query?: string;
181
+ ok: false;
182
+ errorText: string;
183
+ };
184
+ /**
185
+ * A tool execution outcome: an `output` posted back as the tool result, or an
186
+ * `errorText` posted as an `output-error` part (the model sees the failure and
187
+ * can react — never a thrown exception into the stream).
188
+ */
189
+ export type ChatToolOutcome = {
190
+ ok: true;
191
+ output: unknown;
64
192
  } | {
65
- action: 'respond';
66
- message: string;
193
+ ok: false;
194
+ errorText: string;
67
195
  };
68
- /** The schema name the planner call carries — how a FakeProvider tells plan vs. author. */
69
- export declare const PLANNER_SCHEMA_NAME = "create_flow_action";
70
196
  /**
71
- * The structured-output schema for ONE planning step. `action` is a closed enum and
72
- * `collection` is leashed to the known collection ids, so the model can neither invent
73
- * an action nor target a collection that does not exist.
197
+ * The per-conversation candidate store: every `searchDocuments` result is recorded
198
+ * per relation target collection, every `listAssets` result into one flat library
199
+ * pool (EC-381), and the terminal tools may only reference the ids gathered here
200
+ * (the `applyContentWithShape` / `buildUpdateProposal` enum + drop/loud leash makes
201
+ * a hallucinated relation OR asset id structurally unrepresentable / fail loud).
202
+ * Held by the panel per conversation — a new conversation (EC-380 clear) starts an
203
+ * empty store.
204
+ */
205
+ export interface ChatCandidateStore {
206
+ record(collection: string, results: readonly DocumentSearchResult[]): void;
207
+ /** Record every `listAssets` result into the flat library pool (EC-381). */
208
+ recordAssets(results: readonly AssetSearchResult[]): void;
209
+ /** Candidates for a target collection's relation fields, keyed by field name. */
210
+ candidatesFor(collection: CollectionDef): RelationCandidates;
211
+ /**
212
+ * Candidates for a target collection's asset/image fields (EC-381), keyed by
213
+ * field name — every asset field shares the one flat searched-asset pool
214
+ * (`listAssets` has no field dimension). Empty when nothing was searched, so the
215
+ * asset field is then omitted from the shape (empty beats wrong).
216
+ */
217
+ assetCandidatesFor(collection: CollectionDef): AssetCandidates;
218
+ }
219
+ export declare function createChatCandidateStore(): ChatCandidateStore;
220
+ export interface ChatToolExecutorOptions {
221
+ /** The (route) provider the terminal engines proxy their model calls through. */
222
+ provider: AiProvider;
223
+ registry: CollectionRegistry;
224
+ /** The collections the assistant may target (unknown ids fail loud). */
225
+ collections: readonly CollectionDef[];
226
+ /** Operations-backed readers (client-side, user's session). */
227
+ readers: ChatToolReaders;
228
+ /** Mint a fresh document id for a new draft (studio: the CMS store's minter). */
229
+ mintDocumentId: (collectionId: string) => string;
230
+ /** Per-conversation relation-candidate store (see {@link createChatCandidateStore}). */
231
+ candidates: ChatCandidateStore;
232
+ /**
233
+ * Full proposal sink, keyed by `toolCallId` — the review UI's entry point.
234
+ * The tool OUTPUT stays a compact summary; the patch never reaches the model.
235
+ */
236
+ onProposal: (toolCallId: string, proposal: ChatProposal) => void;
237
+ /**
238
+ * The document currently open in the editor (EC-378 seam) — a thunk read at
239
+ * EXECUTION time, so "this document" resolves against what is open NOW.
240
+ * `getDocument` / `updateFields` fall back to it when the model omits the
241
+ * target (see {@link resolveDocumentTarget}).
242
+ */
243
+ currentDocument?: () => CurrentDocumentRef | null | undefined;
244
+ /** Current knowledge snapshot (thunk — the body thunk refreshes it per request). */
245
+ knowledge?: () => ResolvedKnowledge | undefined;
246
+ /** Component manifests — context for `allow`-leashed richText/blocks fields. */
247
+ components?: readonly ComponentManifest[];
248
+ /** Target locale for localized fields. */
249
+ locale?: string;
250
+ /** Locale config — required for `translateDocument`. */
251
+ localeConfig?: LocaleConfig;
252
+ /** Injected leashes / store for `applyContentWithShape`'s validation. */
253
+ validateRichTextEmbeds?: ValidateDocumentOptions['validateRichTextEmbeds'];
254
+ validateComposition?: ValidateDocumentOptions['validateComposition'];
255
+ store?: ValidateDocumentOptions['store'];
256
+ /** Max repair attempts inside `applyContentWithShape`. */
257
+ maxRepairAttempts?: number;
258
+ /** Per-search result cap (default {@link DEFAULT_SEARCH_LIMIT}). */
259
+ searchLimit?: number;
260
+ }
261
+ /** The executor `useChat`'s `onToolCall` dispatches into. */
262
+ export interface ChatToolExecutor {
263
+ execute(call: ChatToolCall): Promise<ChatToolOutcome>;
264
+ }
265
+ /**
266
+ * The MODEL-facing view of a document (EC-379): rich-text field values are
267
+ * projected to markdown strings — but ONLY when the engine's loss-guarded
268
+ * inverse can express them faithfully (subset-only content, round-trip proven).
269
+ * A document carrying embeds, internal `ref` links, or out-of-subset marks keeps
270
+ * its `{ version, doc }` envelope, so nothing the model later echoes back can
271
+ * silently lose structure. Applies to the `getDocument` TOOL OUTPUT only; the
272
+ * proposal/apply paths always read the raw stored document.
273
+ */
274
+ export declare function projectRichTextAsMarkdown(document: CmsDocument, collection: CollectionDef): CmsDocument;
275
+ /**
276
+ * Build the client executor map. Every entry validates its input against
277
+ * {@link chatToolDefinitions}' zod schema (the route validates UIMessages only
278
+ * structurally — deep input validation is the client's), executes with the
279
+ * user's session, and returns a JSON-safe outcome. EC-378's `updateFields`
280
+ * lands as one more case in this dispatch.
74
281
  */
75
- export declare function plannerSchema(collectionIds: readonly string[]): JsonSchema;
76
- /** Coerce a raw structured-output object into a {@link PlannerAction}; `null` if unusable. */
77
- export declare function parsePlannerAction(raw: unknown): PlannerAction | null;
282
+ export declare function createChatToolExecutor(options: ChatToolExecutorOptions): ChatToolExecutor;