@elytracms/studio 0.0.57 → 0.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ai/ai-chat-panel.js +44 -6
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/chat-ui.d.ts +1 -1
- package/dist/components/ai/chat-ui.js +11 -2
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +30 -7
- package/dist/components/ai/proposal-card.js +81 -21
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/cms/detail-editor.js +12 -0
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +12 -1
- package/dist/components/cms/document-diff-view.js +119 -42
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/rich-text-preview.d.ts +15 -0
- package/dist/components/cms/rich-text-preview.js +81 -0
- package/dist/components/cms/rich-text-preview.js.map +1 -0
- package/dist/components/cms/version-compare-text.d.ts +26 -0
- package/dist/components/cms/version-compare-text.js +60 -0
- package/dist/components/cms/version-compare-text.js.map +1 -1
- package/dist/lib/ai/apply-content.js +52 -10
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +19 -3
- package/dist/lib/ai/chat-context.js +33 -4
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +6 -3
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +60 -5
- package/dist/lib/ai/fake-provider.js +74 -6
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/index.d.ts +2 -0
- package/dist/lib/ai/index.js +2 -0
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/rich-text-input.d.ts +27 -0
- package/dist/lib/ai/rich-text-input.js +25 -0
- package/dist/lib/ai/rich-text-input.js.map +1 -0
- package/dist/lib/ai/shape.d.ts +12 -1
- package/dist/lib/ai/shape.js +46 -20
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +88 -13
- package/dist/lib/ai/tools.js +253 -14
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/types.d.ts +20 -0
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +68 -0
- package/dist/lib/ai/update-fields.js +296 -0
- package/dist/lib/ai/update-fields.js.map +1 -0
- package/dist/lib/ai-current-document.d.ts +53 -0
- package/dist/lib/ai-current-document.js +54 -0
- package/dist/lib/ai-current-document.js.map +1 -0
- package/dist/lib/cms-views/view-model.d.ts +6 -6
- package/dist/lib/i18n/messages.en.js +10 -1
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/package.json +2 -2
package/dist/lib/ai/shape.js
CHANGED
|
@@ -10,30 +10,33 @@
|
|
|
10
10
|
* - relation fields become an ENUM over the provided candidate ids (or are omitted
|
|
11
11
|
* when no candidates exist) — so a hallucinated relation id is not representable
|
|
12
12
|
* as valid structured output.
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
13
|
+
* - asset/image fields become an ENUM over the searched library asset ids (or are
|
|
14
|
+
* omitted when none were searched) — EC-381, the SAME leash as relations: a
|
|
15
|
+
* proposed asset id must be a real, `listAssets`-returned library asset, and a
|
|
16
|
+
* create shape with no searched assets simply has no asset property (empty beats
|
|
17
|
+
* wrong; the assistant never generates or uploads).
|
|
18
|
+
* - rich-text fields are MARKDOWN STRINGS (EC-379): the description names the
|
|
19
|
+
* allowed subset, and the engine converts md → the `{ version, doc }` storage
|
|
20
|
+
* envelope at proposal-build time. The field's `allow` config leashes component
|
|
21
|
+
* EMBEDS only — markdown cannot express an embed, so the leash is satisfied by
|
|
22
|
+
* construction (and still enforced downstream by `validateRichTextEmbeds`).
|
|
15
23
|
*
|
|
16
24
|
* Localized fields stay flat in the shape (the model authors one locale at a time);
|
|
17
25
|
* the engine routes their values under `localized[locale]`.
|
|
18
26
|
*/
|
|
19
27
|
import { collectionJsonSchema, resolveLabel } from '@elytracms/core/cms-core';
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
import { MARKDOWN_PROSE_DESCRIPTION } from '@elytracms/core/rich-text';
|
|
29
|
+
/**
|
|
30
|
+
* A rich-text field's model-facing schema (EC-379): a MARKDOWN string. The
|
|
31
|
+
* description carries the allowed subset (from the engine converter — one source
|
|
32
|
+
* of truth), so the model has something concrete to aim at; the engine converts
|
|
33
|
+
* the markdown into the `{ version, doc }` storage envelope at proposal-build
|
|
34
|
+
* time and rejects out-of-subset constructs with explicit issues.
|
|
35
|
+
*/
|
|
36
|
+
function richTextMarkdownSchema() {
|
|
22
37
|
return {
|
|
23
|
-
type: '
|
|
24
|
-
description:
|
|
25
|
-
properties: {
|
|
26
|
-
version: { type: 'number' },
|
|
27
|
-
doc: {
|
|
28
|
-
type: 'object',
|
|
29
|
-
properties: {
|
|
30
|
-
type: { type: 'string', const: 'doc' },
|
|
31
|
-
content: { type: 'array', items: { type: 'object' } },
|
|
32
|
-
},
|
|
33
|
-
required: ['type', 'content'],
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
required: ['version', 'doc'],
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: `Rich text, written as ${MARKDOWN_PROSE_DESCRIPTION} Write genuine, publishable prose.`,
|
|
37
40
|
};
|
|
38
41
|
}
|
|
39
42
|
/** A field's model-facing description: resolved label + optional `ai.hint`. */
|
|
@@ -56,6 +59,7 @@ export function buildTargetShape(input) {
|
|
|
56
59
|
const excluded = [];
|
|
57
60
|
const localizedFields = [];
|
|
58
61
|
const relationFields = [];
|
|
62
|
+
const assetFields = [];
|
|
59
63
|
for (const field of input.collection.fields) {
|
|
60
64
|
if (field.localized)
|
|
61
65
|
localizedFields.push(field.name);
|
|
@@ -89,8 +93,30 @@ export function buildTargetShape(input) {
|
|
|
89
93
|
candidateIds: ids,
|
|
90
94
|
});
|
|
91
95
|
}
|
|
96
|
+
else if (field.type === 'asset') {
|
|
97
|
+
// EC-381 — same leash as relations: an asset field is an enum over the ids
|
|
98
|
+
// the model actually searched in the media library (`listAssets`). No
|
|
99
|
+
// searched assets ⇒ omit the field, so the proposal simply leaves it empty
|
|
100
|
+
// (an invented / to-be-uploaded id is structurally unrepresentable).
|
|
101
|
+
const candidates = input.assetCandidates?.[field.name] ?? [];
|
|
102
|
+
if (candidates.length === 0) {
|
|
103
|
+
delete properties[field.name];
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const ids = candidates.map((c) => c.id);
|
|
107
|
+
const label = candidates
|
|
108
|
+
.map((c) => (c.label ? `${c.id} (${c.label})` : c.id))
|
|
109
|
+
.join(', ');
|
|
110
|
+
const idSchema = {
|
|
111
|
+
type: 'string',
|
|
112
|
+
enum: ids,
|
|
113
|
+
description: `Choose an EXISTING library asset id, one of: ${label}. Leave empty if none fits.`,
|
|
114
|
+
};
|
|
115
|
+
properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema;
|
|
116
|
+
assetFields.push({ fieldName: field.name, multiple: field.multiple, candidateIds: ids });
|
|
117
|
+
}
|
|
92
118
|
else if (field.type === 'richText') {
|
|
93
|
-
properties[field.name] =
|
|
119
|
+
properties[field.name] = richTextMarkdownSchema();
|
|
94
120
|
}
|
|
95
121
|
const prop = properties[field.name];
|
|
96
122
|
if (prop) {
|
|
@@ -108,6 +134,6 @@ export function buildTargetShape(input) {
|
|
|
108
134
|
additionalProperties: false,
|
|
109
135
|
...(required.length > 0 ? { required } : {}),
|
|
110
136
|
};
|
|
111
|
-
return { schema, excluded, localizedFields, relationFields };
|
|
137
|
+
return { schema, excluded, localizedFields, relationFields, assetFields };
|
|
112
138
|
}
|
|
113
139
|
//# sourceMappingURL=shape.js.map
|
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 * - rich-text fields are emitted in the `{ version, doc }` ProseMirror storage shape\n * (the `allow` leash on embeds is enforced later 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 */\nimport { collectionJsonSchema, resolveLabel } from '@elytracms/core/cms-core'\nimport type { CollectionDef, FieldDef, JsonSchema } from '@elytracms/core/cms-core'\nimport type { 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/** 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}\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 /** UI locale for resolving field labels into descriptions. */\n locale?: string\n}\n\n/** The rich-text `{ version, doc }` storage envelope, as a JSON Schema. */\nfunction richTextEnvelopeSchema(): JsonSchema {\n return {\n type: 'object',\n description:\n 'Rich text in the ProseMirror storage shape: { version, doc: { type: \"doc\", content: [...] } }.',\n properties: {\n version: { type: 'number' },\n doc: {\n type: 'object',\n properties: {\n type: { type: 'string', const: 'doc' },\n content: { type: 'array', items: { type: 'object' } },\n },\n required: ['type', 'content'],\n },\n },\n required: ['version', 'doc'],\n }\n}\n\n/** A field's model-facing description: resolved label + optional `ai.hint`. */\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 parts = [label, hint].filter((p): p is string => typeof p === 'string' && p.length > 0)\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\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 === 'richText') {\n properties[field.name] = richTextEnvelopeSchema()\n }\n\n const prop = properties[field.name]\n if (prop) {\n const desc = describe(field, input.locale)\n // Preserve an existing description (e.g. relation enum / rich-text note).\n if (desc && typeof prop.description !== 'string') prop.description = desc\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 }\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 */\nimport { collectionJsonSchema, 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 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. The\n * description carries the allowed subset (from the engine converter — one source\n * of truth), so the model has something concrete to aim at; the engine converts\n * the markdown into the `{ version, doc }` storage envelope at proposal-build\n * time and rejects out-of-subset constructs with explicit issues.\n */\nfunction richTextMarkdownSchema(): JsonSchema {\n return {\n type: 'string',\n description: `Rich text, written as ${MARKDOWN_PROSE_DESCRIPTION} Write genuine, publishable prose.`,\n }\n}\n\n/** A field's model-facing description: resolved label + optional `ai.hint`. */\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 parts = [label, hint].filter((p): p is string => typeof p === 'string' && p.length > 0)\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()\n }\n\n const prop = properties[field.name]\n if (prop) {\n const desc = describe(field, input.locale)\n // Preserve an existing description (e.g. relation enum / rich-text note).\n if (desc && typeof prop.description !== 'string') prop.description = desc\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
|
@@ -14,13 +14,19 @@
|
|
|
14
14
|
* a write path; apply is a separate, human-gated draft write.
|
|
15
15
|
* 3. {@link createChatToolExecutor} — the client executor map `useChat`'s
|
|
16
16
|
* `onToolCall` dispatches into. Readers pass through; the terminal tools run
|
|
17
|
-
* the deterministic engines (`applyContentWithShape` / `translateDocument`
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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
20
|
* `onProposal(toolCallId, …)`, never to the model.
|
|
21
21
|
*
|
|
22
|
-
* EC-378
|
|
23
|
-
*
|
|
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.
|
|
24
30
|
*
|
|
25
31
|
* The EC-199 planner enum/schema (`plannerSchema` / `parsePlannerAction`) retired
|
|
26
32
|
* with the hand-rolled orchestrator (EC-334 slice 2).
|
|
@@ -29,7 +35,7 @@ import { z } from 'zod';
|
|
|
29
35
|
import type { CmsDocument, CollectionDef, CollectionRegistry, LocaleConfig, ValidateDocumentOptions } from '@elytracms/core/cms-core';
|
|
30
36
|
import type { ComponentManifest } from '@elytracms/core/component-registry';
|
|
31
37
|
import type { ChatProposal } from './chat-state.js';
|
|
32
|
-
import type { AiProvider, RelationCandidates, ResolvedKnowledge } from './types.js';
|
|
38
|
+
import type { AiProvider, AssetCandidates, RelationCandidates, ResolvedKnowledge } from './types.js';
|
|
33
39
|
/** A document the model may reference as a relation target (from a `searchDocuments` read). */
|
|
34
40
|
export interface DocumentSearchResult {
|
|
35
41
|
id: string;
|
|
@@ -108,8 +114,8 @@ export declare const chatToolDefinitions: {
|
|
|
108
114
|
getDocument: {
|
|
109
115
|
description: string;
|
|
110
116
|
inputSchema: z.ZodObject<{
|
|
111
|
-
collection: z.ZodString
|
|
112
|
-
id: z.ZodString
|
|
117
|
+
collection: z.ZodOptional<z.ZodString>;
|
|
118
|
+
id: z.ZodOptional<z.ZodString>;
|
|
113
119
|
}, z.core.$strip>;
|
|
114
120
|
};
|
|
115
121
|
createDocument: {
|
|
@@ -119,6 +125,20 @@ export declare const chatToolDefinitions: {
|
|
|
119
125
|
instruction: z.ZodString;
|
|
120
126
|
}, z.core.$strip>;
|
|
121
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
|
+
};
|
|
122
142
|
translateDocument: {
|
|
123
143
|
description: string;
|
|
124
144
|
inputSchema: z.ZodObject<{
|
|
@@ -134,6 +154,33 @@ export interface ChatToolCall {
|
|
|
134
154
|
toolName: string;
|
|
135
155
|
input: unknown;
|
|
136
156
|
}
|
|
157
|
+
/** The coordinates of the document currently open in the editor (EC-378 seam). */
|
|
158
|
+
export interface CurrentDocumentRef {
|
|
159
|
+
collection: 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;
|
|
180
|
+
} | {
|
|
181
|
+
ok: false;
|
|
182
|
+
errorText: string;
|
|
183
|
+
};
|
|
137
184
|
/**
|
|
138
185
|
* A tool execution outcome: an `output` posted back as the tool result, or an
|
|
139
186
|
* `errorText` posted as an `output-error` part (the model sees the failure and
|
|
@@ -147,16 +194,27 @@ export type ChatToolOutcome = {
|
|
|
147
194
|
errorText: string;
|
|
148
195
|
};
|
|
149
196
|
/**
|
|
150
|
-
* The per-conversation
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
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.
|
|
155
204
|
*/
|
|
156
205
|
export interface ChatCandidateStore {
|
|
157
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;
|
|
158
209
|
/** Candidates for a target collection's relation fields, keyed by field name. */
|
|
159
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;
|
|
160
218
|
}
|
|
161
219
|
export declare function createChatCandidateStore(): ChatCandidateStore;
|
|
162
220
|
export interface ChatToolExecutorOptions {
|
|
@@ -176,6 +234,13 @@ export interface ChatToolExecutorOptions {
|
|
|
176
234
|
* The tool OUTPUT stays a compact summary; the patch never reaches the model.
|
|
177
235
|
*/
|
|
178
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;
|
|
179
244
|
/** Current knowledge snapshot (thunk — the body thunk refreshes it per request). */
|
|
180
245
|
knowledge?: () => ResolvedKnowledge | undefined;
|
|
181
246
|
/** Component manifests — context for `allow`-leashed richText/blocks fields. */
|
|
@@ -197,6 +262,16 @@ export interface ChatToolExecutorOptions {
|
|
|
197
262
|
export interface ChatToolExecutor {
|
|
198
263
|
execute(call: ChatToolCall): Promise<ChatToolOutcome>;
|
|
199
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;
|
|
200
275
|
/**
|
|
201
276
|
* Build the client executor map. Every entry validates its input against
|
|
202
277
|
* {@link chatToolDefinitions}' zod schema (the route validates UIMessages only
|