@caelo-cms/shared 0.10.21 → 0.10.23
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/ai-tools.d.ts +291 -231
- package/dist/ai-tools.d.ts.map +1 -1
- package/dist/ai-tools.js +349 -281
- package/dist/ai-tools.js.map +1 -1
- package/dist/auth-forms.d.ts.map +1 -1
- package/dist/auth-forms.js +4 -1
- package/dist/auth-forms.js.map +1 -1
- package/dist/base-css.d.ts +24 -0
- package/dist/base-css.d.ts.map +1 -0
- package/dist/base-css.js +28 -0
- package/dist/base-css.js.map +1 -0
- package/dist/build-page.d.ts +330 -0
- package/dist/build-page.d.ts.map +1 -0
- package/dist/build-page.js +282 -0
- package/dist/build-page.js.map +1 -0
- package/dist/content.d.ts +322 -9
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +354 -11
- package/dist/content.js.map +1 -1
- package/dist/css-gradient-scan.d.ts +14 -0
- package/dist/css-gradient-scan.d.ts.map +1 -0
- package/dist/css-gradient-scan.js +81 -0
- package/dist/css-gradient-scan.js.map +1 -0
- package/dist/css-var-scan.d.ts +56 -0
- package/dist/css-var-scan.d.ts.map +1 -0
- package/dist/css-var-scan.js +97 -0
- package/dist/css-var-scan.js.map +1 -0
- package/dist/design-manifest.d.ts +36 -0
- package/dist/design-manifest.d.ts.map +1 -0
- package/dist/design-manifest.js +90 -0
- package/dist/design-manifest.js.map +1 -0
- package/dist/fonts.d.ts +89 -0
- package/dist/fonts.d.ts.map +1 -0
- package/dist/fonts.js +241 -0
- package/dist/fonts.js.map +1 -0
- package/dist/genesis-inventory.d.ts +32 -0
- package/dist/genesis-inventory.d.ts.map +1 -0
- package/dist/genesis-inventory.js +186 -0
- package/dist/genesis-inventory.js.map +1 -0
- package/dist/genesis.d.ts +62 -0
- package/dist/genesis.d.ts.map +1 -0
- package/dist/genesis.js +78 -0
- package/dist/genesis.js.map +1 -0
- package/dist/i18n.d.ts +44 -1
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +72 -6
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -1
- package/dist/interactions.d.ts +23 -0
- package/dist/interactions.d.ts.map +1 -0
- package/dist/interactions.js +44 -0
- package/dist/interactions.js.map +1 -0
- package/dist/media.d.ts +101 -16
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +126 -15
- package/dist/media.js.map +1 -1
- package/dist/page-log.d.ts +94 -0
- package/dist/page-log.d.ts.map +1 -0
- package/dist/page-log.js +111 -0
- package/dist/page-log.js.map +1 -0
- package/dist/preview-compose.d.ts +79 -0
- package/dist/preview-compose.d.ts.map +1 -1
- package/dist/preview-compose.js +155 -25
- package/dist/preview-compose.js.map +1 -1
- package/dist/proposal-status.d.ts +40 -0
- package/dist/proposal-status.d.ts.map +1 -0
- package/dist/proposal-status.js +34 -0
- package/dist/proposal-status.js.map +1 -0
- package/dist/responsive-images.d.ts +64 -0
- package/dist/responsive-images.d.ts.map +1 -0
- package/dist/responsive-images.js +98 -0
- package/dist/responsive-images.js.map +1 -0
- package/dist/safe-keys.d.ts +9 -0
- package/dist/safe-keys.d.ts.map +1 -0
- package/dist/safe-keys.js +20 -0
- package/dist/safe-keys.js.map +1 -0
- package/dist/seo.d.ts +8 -0
- package/dist/seo.d.ts.map +1 -1
- package/dist/seo.js +3 -1
- package/dist/seo.js.map +1 -1
- package/dist/skills.d.ts +14 -68
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +19 -113
- package/dist/skills.js.map +1 -1
- package/dist/strip-cdata.d.ts +7 -0
- package/dist/strip-cdata.d.ts.map +1 -0
- package/dist/strip-cdata.js +48 -0
- package/dist/strip-cdata.js.map +1 -0
- package/dist/structured-sets.d.ts +6 -16
- package/dist/structured-sets.d.ts.map +1 -1
- package/dist/structured-sets.js +5 -16
- package/dist/structured-sets.js.map +1 -1
- package/dist/subagents.d.ts +105 -3
- package/dist/subagents.d.ts.map +1 -1
- package/dist/subagents.js +224 -41
- package/dist/subagents.js.map +1 -1
- package/dist/template-engine.d.ts +85 -0
- package/dist/template-engine.d.ts.map +1 -0
- package/dist/template-engine.js +403 -0
- package/dist/template-engine.js.map +1 -0
- package/dist/theme-importers/auto-detect.d.ts +26 -0
- package/dist/theme-importers/auto-detect.d.ts.map +1 -0
- package/dist/theme-importers/auto-detect.js +42 -0
- package/dist/theme-importers/auto-detect.js.map +1 -0
- package/dist/theme-importers/css-comments.d.ts +12 -0
- package/dist/theme-importers/css-comments.d.ts.map +1 -0
- package/dist/theme-importers/css-comments.js +15 -0
- package/dist/theme-importers/css-comments.js.map +1 -0
- package/dist/theme-importers/dtcg.d.ts +46 -0
- package/dist/theme-importers/dtcg.d.ts.map +1 -0
- package/dist/theme-importers/dtcg.js +111 -0
- package/dist/theme-importers/dtcg.js.map +1 -0
- package/dist/theme-importers/loose.d.ts +3 -0
- package/dist/theme-importers/loose.d.ts.map +1 -0
- package/dist/theme-importers/loose.js +76 -0
- package/dist/theme-importers/loose.js.map +1 -0
- package/dist/theme-importers/shadcn.d.ts +24 -0
- package/dist/theme-importers/shadcn.d.ts.map +1 -0
- package/dist/theme-importers/shadcn.js +135 -0
- package/dist/theme-importers/shadcn.js.map +1 -0
- package/dist/theme-importers/style-dictionary.d.ts +17 -0
- package/dist/theme-importers/style-dictionary.d.ts.map +1 -0
- package/dist/theme-importers/style-dictionary.js +125 -0
- package/dist/theme-importers/style-dictionary.js.map +1 -0
- package/dist/theme-importers/tailwind.d.ts +3 -0
- package/dist/theme-importers/tailwind.d.ts.map +1 -0
- package/dist/theme-importers/tailwind.js +218 -0
- package/dist/theme-importers/tailwind.js.map +1 -0
- package/dist/theme-literal-binding.d.ts +37 -0
- package/dist/theme-literal-binding.d.ts.map +1 -0
- package/dist/theme-literal-binding.js +138 -0
- package/dist/theme-literal-binding.js.map +1 -0
- package/dist/theme-normalize.d.ts +31 -0
- package/dist/theme-normalize.d.ts.map +1 -0
- package/dist/theme-normalize.js +587 -0
- package/dist/theme-normalize.js.map +1 -0
- package/dist/theme-ramp.d.ts +55 -0
- package/dist/theme-ramp.d.ts.map +1 -0
- package/dist/theme-ramp.js +149 -0
- package/dist/theme-ramp.js.map +1 -0
- package/dist/theme-render.d.ts +105 -0
- package/dist/theme-render.d.ts.map +1 -0
- package/dist/theme-render.js +441 -0
- package/dist/theme-render.js.map +1 -0
- package/dist/themes-errors.d.ts +109 -0
- package/dist/themes-errors.d.ts.map +1 -0
- package/dist/themes-errors.js +170 -0
- package/dist/themes-errors.js.map +1 -0
- package/dist/themes.d.ts +343 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +697 -0
- package/dist/themes.js.map +1 -0
- package/dist/version.d.ts +7 -4
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +6 -3
- package/dist/version.js.map +1 -1
- package/package.json +10 -2
- package/src/__tests__/redos-hardening.test.ts +160 -0
- package/src/ai-tools-add-module-modes.test.ts +106 -0
- package/src/ai-tools-position.test.ts +134 -0
- package/src/ai-tools.test.ts +81 -0
- package/src/ai-tools.ts +1179 -0
- package/src/auth-forms.ts +36 -0
- package/src/base-css.ts +30 -0
- package/src/build-page.test.ts +228 -0
- package/src/build-page.ts +319 -0
- package/src/cap-failures.ts +67 -0
- package/src/content.test.ts +170 -0
- package/src/content.ts +620 -0
- package/src/context.ts +43 -0
- package/src/css-gradient-scan.ts +88 -0
- package/src/css-var-scan.test.ts +96 -0
- package/src/css-var-scan.ts +144 -0
- package/src/derive-module-type.test.ts +80 -0
- package/src/design-manifest.ts +93 -0
- package/src/fonts.test.ts +157 -0
- package/src/fonts.ts +296 -0
- package/src/genesis-inventory.test.ts +86 -0
- package/src/genesis-inventory.ts +215 -0
- package/src/genesis-sanitize.test.ts +35 -0
- package/src/genesis.ts +87 -0
- package/src/i18n.test.ts +274 -0
- package/src/i18n.ts +269 -0
- package/src/index.test.ts +10 -0
- package/src/index.ts +59 -0
- package/src/interactions.ts +48 -0
- package/src/logger.ts +147 -0
- package/src/media.test.ts +160 -0
- package/src/media.ts +355 -0
- package/src/page-log.test.ts +163 -0
- package/src/page-log.ts +124 -0
- package/src/preview-compose.test.ts +602 -0
- package/src/preview-compose.ts +709 -0
- package/src/preview-scanner.test.ts +96 -0
- package/src/preview-scanner.ts +214 -0
- package/src/proposal-status.test.ts +69 -0
- package/src/proposal-status.ts +40 -0
- package/src/responsive-images.test.ts +104 -0
- package/src/responsive-images.ts +151 -0
- package/src/result.ts +29 -0
- package/src/safe-keys.ts +21 -0
- package/src/seo.test.ts +234 -0
- package/src/seo.ts +261 -0
- package/src/skills.ts +48 -0
- package/src/snapshots.test.ts +80 -0
- package/src/snapshots.ts +81 -0
- package/src/strip-cdata.test.ts +41 -0
- package/src/strip-cdata.ts +50 -0
- package/src/structured-sets.ts +180 -0
- package/src/subagents.test.ts +262 -0
- package/src/subagents.ts +432 -0
- package/src/template-engine.test.ts +379 -0
- package/src/template-engine.ts +520 -0
- package/src/theme-gradient.test.ts +92 -0
- package/src/theme-importers/__tests__/proto-pollution.test.ts +54 -0
- package/src/theme-importers/auto-detect.ts +84 -0
- package/src/theme-importers/css-comments.ts +15 -0
- package/src/theme-importers/dtcg.ts +106 -0
- package/src/theme-importers/loose.ts +76 -0
- package/src/theme-importers/shadcn.ts +133 -0
- package/src/theme-importers/style-dictionary.ts +125 -0
- package/src/theme-importers/tailwind.ts +217 -0
- package/src/theme-literal-binding.test.ts +71 -0
- package/src/theme-literal-binding.ts +159 -0
- package/src/theme-motion.test.ts +115 -0
- package/src/theme-normalize-envelope.test.ts +43 -0
- package/src/theme-normalize-gradient.test.ts +135 -0
- package/src/theme-normalize.ts +661 -0
- package/src/theme-ramp.ts +187 -0
- package/src/theme-render-sanitize.test.ts +45 -0
- package/src/theme-render.test.ts +119 -0
- package/src/theme-render.ts +487 -0
- package/src/theme-shadow.test.ts +56 -0
- package/src/themes-errors.ts +199 -0
- package/src/themes.ts +842 -0
- package/src/translation.test.ts +160 -0
- package/src/translation.ts +295 -0
- package/src/version.ts +66 -0
package/dist/ai-tools.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* silent drops in the handler.
|
|
11
11
|
*/
|
|
12
12
|
import { z } from "zod";
|
|
13
|
-
import { MODULE_CSS_MAX, MODULE_HTML_MAX, MODULE_JS_MAX } from "./content.js";
|
|
13
|
+
import { contentInstanceCreateSchema, contentInstanceDeleteSchema, contentInstanceUpdateSchema, forkPlacementContentSchema, MODULE_CSS_MAX, MODULE_HTML_MAX, MODULE_JS_MAX, moduleFieldSchema, setPlacementContentSchema, slugSchema, } from "./content.js";
|
|
14
14
|
/**
|
|
15
15
|
* v0.6.2 — `position` argument shared across the three `add_module_to_*`
|
|
16
16
|
* tools. Accepts either the literal string `"top"` / `"bottom"` OR a
|
|
@@ -22,15 +22,49 @@ import { MODULE_CSS_MAX, MODULE_HTML_MAX, MODULE_JS_MAX } from "./content.js";
|
|
|
22
22
|
* description explicitly says to use bare integers; the coercion
|
|
23
23
|
* removes that ergonomic gotcha while still rejecting non-numeric
|
|
24
24
|
* strings (`"abc"` → still fails the int check cleanly).
|
|
25
|
+
*
|
|
26
|
+
* issue #106 (step-13 round-6) — an OUTER preprocess strips one layer of
|
|
27
|
+
* surrounding matching quotes first. The model occasionally over-quotes the
|
|
28
|
+
* literal: it emitted `position` as the JSON string `"\"bottom\""` (the
|
|
29
|
+
* 8-char value including the quote characters), which matched neither union
|
|
30
|
+
* branch and failed with a bare `Invalid input`. Unwrapping `"bottom"` →
|
|
31
|
+
* `bottom` (and `"\"0\""` → `0`, which the integer branch then coerces) turns
|
|
32
|
+
* that recurring first-call rejection into a clean accept. This is input
|
|
33
|
+
* NORMALISATION at the boundary (decoding a malformed encoding of a value the
|
|
34
|
+
* caller did supply), not a missing-data fallback — `undefined`/`null`/`""`
|
|
35
|
+
* still fall through to a loud rejection, per CLAUDE.md §2.
|
|
25
36
|
*/
|
|
26
|
-
|
|
37
|
+
function stripSurroundingQuotes(v) {
|
|
38
|
+
if (typeof v !== "string")
|
|
39
|
+
return v;
|
|
40
|
+
// Only when BOTH ends are the same quote char and there is at least one
|
|
41
|
+
// inner character — so a bare `"bottom"` / `"0"` is untouched and an empty
|
|
42
|
+
// `""` is not collapsed into a match here.
|
|
43
|
+
const m = v.match(/^(["'])([\s\S]+)\1$/);
|
|
44
|
+
return m ? m[2] : v;
|
|
45
|
+
}
|
|
46
|
+
export const positionInputSchema = z.preprocess(stripSurroundingQuotes, z.union([
|
|
27
47
|
z.enum(["top", "bottom"]),
|
|
28
48
|
z.preprocess((v) => (typeof v === "string" && /^\d+$/.test(v) ? Number.parseInt(v, 10) : v), z.number().int().min(0).max(1000)),
|
|
29
|
-
]);
|
|
49
|
+
]));
|
|
30
50
|
export const editModuleToolInput = z
|
|
31
51
|
.object({
|
|
32
52
|
moduleId: z.string().uuid(),
|
|
33
53
|
displayName: z.string().min(1).max(128).optional(),
|
|
54
|
+
/**
|
|
55
|
+
* v0.12.0 — rewrite the module's purpose. Optional; passing it
|
|
56
|
+
* updates the `## Modules` block your future self will read.
|
|
57
|
+
*/
|
|
58
|
+
description: z.string().max(1000).optional(),
|
|
59
|
+
/** v0.12.0 — re-classify the module's role tag. */
|
|
60
|
+
kind: z.enum(["chrome", "hero", "content", "cta", "utility"]).optional(),
|
|
61
|
+
/**
|
|
62
|
+
* v0.12.3 (issue #106) — re-classify the module's stable `type` (the
|
|
63
|
+
* class a parent's `allowedModuleTypes` whitelist matches against,
|
|
64
|
+
* e.g. `button`). Rarely needed; usually derived from displayName at
|
|
65
|
+
* create time. Set it to make this module satisfy a parent's allowlist.
|
|
66
|
+
*/
|
|
67
|
+
type: slugSchema.optional(),
|
|
34
68
|
html: z.string().max(MODULE_HTML_MAX).optional(),
|
|
35
69
|
css: z.string().max(MODULE_CSS_MAX).optional(),
|
|
36
70
|
js: z.string().max(MODULE_JS_MAX).optional(),
|
|
@@ -40,17 +74,9 @@ export const editModuleToolInput = z
|
|
|
40
74
|
* fill these via `set_page_module_content`. Optional on edits — pass
|
|
41
75
|
* to replace the declared schema.
|
|
42
76
|
*/
|
|
43
|
-
fields: z
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
name: z.string().regex(/^[a-z][a-z0-9_]{0,63}$/),
|
|
47
|
-
kind: z.enum(["text", "richtext", "url", "image", "number", "boolean", "link"]),
|
|
48
|
-
label: z.string().min(1).max(128),
|
|
49
|
-
default: z.unknown().optional(),
|
|
50
|
-
})
|
|
51
|
-
.strict())
|
|
52
|
-
.max(64)
|
|
53
|
-
.optional(),
|
|
77
|
+
fields: z.array(moduleFieldSchema).max(64).optional(),
|
|
78
|
+
/** issue #164 slice 2 — see addModuleToolInput.bindThemeLiterals. */
|
|
79
|
+
bindThemeLiterals: z.boolean().optional(),
|
|
54
80
|
})
|
|
55
81
|
.strict();
|
|
56
82
|
/**
|
|
@@ -75,74 +101,140 @@ export const siteMemoryProposeToolInput = z
|
|
|
75
101
|
})
|
|
76
102
|
.strict();
|
|
77
103
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
104
|
+
* Progressive-disclosure skill loading. The `## Skills` system-prompt block
|
|
105
|
+
* lists each active skill's slug + description; when a task matches one or
|
|
106
|
+
* more, the model calls `load_skill({slugs})` to pull their full instructions
|
|
107
|
+
* into the conversation (they persist as a tool result for the rest of the
|
|
108
|
+
* chat, so each skill loads at most once).
|
|
109
|
+
*
|
|
110
|
+
* Takes a LIST, not a single slug, per CLAUDE.md §11: a load is a routine
|
|
111
|
+
* operation and every routine operation ships in bulk form, with n=1 as its
|
|
112
|
+
* smallest case rather than a separate singular tool. Loading three skills was
|
|
113
|
+
* three provider round-trips whose only output was one tool call each.
|
|
114
|
+
*
|
|
115
|
+
* Capped at 5: skills are task-scoped, and a model that wants more than a
|
|
116
|
+
* handful at once has almost certainly matched the index too loosely — each
|
|
117
|
+
* body is a sizeable injection into history.
|
|
86
118
|
*/
|
|
87
|
-
export const
|
|
119
|
+
export const loadSkillToolInput = z
|
|
88
120
|
.object({
|
|
89
|
-
|
|
90
|
-
blockName: z.string().min(1).max(80),
|
|
91
|
-
/** "top" | "bottom" | a 0-based integer index. */
|
|
92
|
-
position: positionInputSchema,
|
|
93
|
-
displayName: z.string().min(1).max(128),
|
|
94
|
-
html: z.string().min(1).max(50_000),
|
|
95
|
-
css: z.string().max(50_000).optional(),
|
|
96
|
-
js: z.string().max(50_000).optional(),
|
|
97
|
-
/**
|
|
98
|
-
* v0.5.21 — module field schema (v0.4.0 module/content split). The
|
|
99
|
-
* AI declares fields here when creating a module that uses `{{name}}`
|
|
100
|
-
* substitutions; per-page content gets filled via
|
|
101
|
-
* `set_page_module_content` later. Optional — modules with no
|
|
102
|
-
* field schema are static HTML.
|
|
103
|
-
*/
|
|
104
|
-
fields: z
|
|
121
|
+
slugs: z
|
|
105
122
|
.array(z
|
|
106
|
-
.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
.strict())
|
|
113
|
-
.max(64)
|
|
114
|
-
.optional(),
|
|
123
|
+
.string()
|
|
124
|
+
.min(1)
|
|
125
|
+
.max(120)
|
|
126
|
+
.regex(/^[a-z0-9-]+$/, "lowercase letters/digits/hyphens"))
|
|
127
|
+
.min(1)
|
|
128
|
+
.max(5),
|
|
115
129
|
})
|
|
116
130
|
.strict();
|
|
117
131
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
|
|
121
|
-
|
|
132
|
+
* The set of tools shipped in P5. Other phases extend by adding a new
|
|
133
|
+
* entry; the dispatcher walks this map at registration time.
|
|
134
|
+
*/
|
|
135
|
+
/**
|
|
136
|
+
* The ONE `add_module` input — consolidates add_module_to_{page,layout,template}
|
|
137
|
+
* (audit #2). `target` picks the destination; `targetRef` is a slug OR a uuid
|
|
138
|
+
* (resolved server-side — a slug is friendlier for the AI to hold than a uuid).
|
|
139
|
+
* Same two modes as before: `moduleId` reuses an existing module (now for ALL
|
|
140
|
+
* three targets — layout used to be mint-only), or the authoring fields mint a
|
|
141
|
+
* new one (raw html is fine — moduleize turns it into a proper module). Exactly
|
|
142
|
+
* one mode, enforced by the shared superRefine.
|
|
122
143
|
*/
|
|
123
|
-
export const
|
|
144
|
+
export const addModuleToolInput = z
|
|
124
145
|
.object({
|
|
125
|
-
|
|
146
|
+
target: z.enum(["page", "layout", "template"]),
|
|
147
|
+
/** Slug or uuid of the page / layout / template. Resolved server-side. */
|
|
148
|
+
targetRef: z.string().min(1).max(200),
|
|
126
149
|
blockName: z.string().min(1).max(80),
|
|
127
150
|
position: positionInputSchema,
|
|
128
|
-
|
|
129
|
-
|
|
151
|
+
/** Reuse an existing module by id (any target). Mutually exclusive with the
|
|
152
|
+
* authoring fields. */
|
|
153
|
+
moduleId: z.string().uuid().optional(),
|
|
154
|
+
displayName: z.string().min(1).max(128).optional(),
|
|
155
|
+
description: z.string().max(1000).optional(),
|
|
156
|
+
kind: z.enum(["chrome", "hero", "content", "cta", "utility"]).optional(),
|
|
157
|
+
type: slugSchema.optional(),
|
|
158
|
+
html: z.string().min(1).max(50_000).optional(),
|
|
130
159
|
css: z.string().max(50_000).optional(),
|
|
131
160
|
js: z.string().max(50_000).optional(),
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
161
|
+
fields: z.array(moduleFieldSchema).max(64).optional(),
|
|
162
|
+
bindThemeLiterals: z.boolean().optional(),
|
|
163
|
+
/**
|
|
164
|
+
* The placement's initial content, applied IN THE SAME CALL (2026-07
|
|
165
|
+
* — a freshly minted module must never land empty and wait for a
|
|
166
|
+
* second round-trip). ONE rule for every target: content goes in
|
|
167
|
+
* `values`. target='page': fills the placement's content_instance.
|
|
168
|
+
* target='template': every fanned-out placement gets these values.
|
|
169
|
+
* target='layout': chrome has no content_instance, so the handler
|
|
170
|
+
* stores the values as the minted module's `fields[].default` —
|
|
171
|
+
* which requires explicit `fields` (moduleize field names are
|
|
172
|
+
* unknowable up front) and is rejected for `moduleId` reuse (a
|
|
173
|
+
* shared module renders its stored defaults; edit_module changes
|
|
174
|
+
* them).
|
|
175
|
+
*/
|
|
176
|
+
values: z.record(z.string(), z.unknown()).optional(),
|
|
144
177
|
})
|
|
145
|
-
.strict()
|
|
178
|
+
.strict()
|
|
179
|
+
.superRefine((input, ctx) => {
|
|
180
|
+
if (input.target === "layout" && input.values !== undefined) {
|
|
181
|
+
if (input.moduleId !== undefined) {
|
|
182
|
+
ctx.addIssue({
|
|
183
|
+
code: z.ZodIssueCode.custom,
|
|
184
|
+
message: "Reusing a module on a layout renders the module's STORED field defaults — `values` has nothing to bind to. Drop `values` (the chrome shows the module as-is), or change the shared copy via edit_module.",
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
else if (input.fields === undefined || input.fields.length === 0) {
|
|
188
|
+
ctx.addIssue({
|
|
189
|
+
code: z.ZodIssueCode.custom,
|
|
190
|
+
message: "target='layout' with `values` needs explicit `fields` — chrome stores the values as field defaults, so the field names must be declared in this call (raw-HTML field inference can't know your value keys).",
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
const known = new Set(input.fields.map((f) => f.name));
|
|
195
|
+
const unknown = Object.keys(input.values).filter((k) => !known.has(k));
|
|
196
|
+
if (unknown.length > 0) {
|
|
197
|
+
ctx.addIssue({
|
|
198
|
+
code: z.ZodIssueCode.custom,
|
|
199
|
+
message: `\`values\` keys [${unknown.join(", ")}] match no declared field — declared: [${[...known].join(", ")}].`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// `moduleId` is PLACEMENT-ONLY (see the buildPageModuleSchema note): a
|
|
205
|
+
// placement that CAN succeed must not fail over an extra authoring field
|
|
206
|
+
// (§1A/§11), so we do NOT reject a moduleId call that also carries authoring
|
|
207
|
+
// fields. Those fields are not applied here; the tool HANDLER surfaces an
|
|
208
|
+
// INFO for any ignored field whose value DIFFERS from the module's stored
|
|
209
|
+
// one, pointing at edit_module — so a dropped change is never silent (§2).
|
|
210
|
+
if (input.moduleId !== undefined)
|
|
211
|
+
return;
|
|
212
|
+
if (input.displayName === undefined || input.html === undefined) {
|
|
213
|
+
ctx.addIssue({
|
|
214
|
+
code: z.ZodIssueCode.custom,
|
|
215
|
+
message: "Pass either `moduleId` (place an existing module from ## Modules) " +
|
|
216
|
+
"or `displayName` + `html` (mint a new module).",
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// 2026-07 — a freshly minted module must carry its initial copy IN
|
|
220
|
+
// THIS call, via `values` on every target (`fields[].default` also
|
|
221
|
+
// satisfies it — that's where layout values land anyway). Without
|
|
222
|
+
// either, the placement rendered EMPTY until a second round-trip —
|
|
223
|
+
// the exact two-step build_page was created to kill. Only enforced
|
|
224
|
+
// when the caller authored explicit fields; the raw-HTML path goes
|
|
225
|
+
// through moduleize, which enforces defaults itself.
|
|
226
|
+
if (input.fields !== undefined &&
|
|
227
|
+
input.fields.length > 0 &&
|
|
228
|
+
input.values === undefined &&
|
|
229
|
+
!input.fields.some((f) => "default" in f && f.default !== undefined)) {
|
|
230
|
+
ctx.addIssue({
|
|
231
|
+
code: z.ZodIssueCode.custom,
|
|
232
|
+
message: "A minted module needs its initial content IN THIS CALL: pass `values` " +
|
|
233
|
+
"({fieldName: value, …}) for this placement, or give the fields `default`s. " +
|
|
234
|
+
"Without either, the module renders empty placeholders until a second call.",
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
});
|
|
146
238
|
/**
|
|
147
239
|
* v0.2.16 — `add_plugin_to_page` AI tool. Inserts a plugin's
|
|
148
240
|
* `<div data-caelo-plugin>` placeholder into a page's block via a
|
|
@@ -162,30 +254,69 @@ export const addPluginToPageToolInput = z
|
|
|
162
254
|
position: positionInputSchema,
|
|
163
255
|
})
|
|
164
256
|
.strict();
|
|
257
|
+
// ─── v0.12.0 — content_instances + placement AI tools ─────────────────
|
|
258
|
+
/**
|
|
259
|
+
* `list_content_instances` — browse the content library. The `placementCount`
|
|
260
|
+
* returned per row is a blast-radius affordance: the AI can decide whether
|
|
261
|
+
* to edit a shared instance (propagates everywhere) or fork it first.
|
|
262
|
+
*/
|
|
263
|
+
export const listContentInstancesToolInput = z
|
|
264
|
+
.object({
|
|
265
|
+
moduleId: z.string().uuid().optional(),
|
|
266
|
+
slug: z.string().min(1).max(64).optional(),
|
|
267
|
+
/** Free-text matches against displayName + slug. Case-insensitive substring. */
|
|
268
|
+
search: z.string().min(1).max(128).optional(),
|
|
269
|
+
/** Narrow to "instances referenced from this page" for chat-runner planning. */
|
|
270
|
+
pageId: z.string().uuid().optional(),
|
|
271
|
+
})
|
|
272
|
+
.strict();
|
|
273
|
+
export const getContentInstanceToolInput = z
|
|
274
|
+
.object({
|
|
275
|
+
id: z.string().uuid(),
|
|
276
|
+
})
|
|
277
|
+
.strict();
|
|
278
|
+
export const createContentInstanceToolInput = contentInstanceCreateSchema;
|
|
279
|
+
export const setContentInstanceValuesToolInput = contentInstanceUpdateSchema;
|
|
280
|
+
export const deleteContentInstanceToolInput = contentInstanceDeleteSchema;
|
|
281
|
+
export const setPlacementContentToolInput = setPlacementContentSchema;
|
|
282
|
+
export const forkPlacementContentToolInput = forkPlacementContentSchema;
|
|
165
283
|
export const AI_TOOLS = [
|
|
166
284
|
"edit_module",
|
|
167
285
|
"site_memory_propose",
|
|
168
|
-
"
|
|
169
|
-
"add_module_to_template",
|
|
286
|
+
"add_module",
|
|
170
287
|
"create_page",
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
"
|
|
175
|
-
|
|
288
|
+
// audit #3 — page metadata (name/title/slug/template/status) is one tool for
|
|
289
|
+
// 1..200 pages: `update_pages_many`. The former rename_page / set_page_title
|
|
290
|
+
// / change_page_slug were single-field wrappers over the same pages.update.
|
|
291
|
+
"update_pages_many",
|
|
292
|
+
// audit #4 — one delete tool for 1..200 pages, disposition per page.
|
|
293
|
+
// The former singular delete_page folded in (n=1 is a one-item array).
|
|
294
|
+
"delete_pages_many",
|
|
295
|
+
// one remove-module tool routed by target (page|layout).
|
|
296
|
+
"remove_module_from",
|
|
176
297
|
"set_structured_set",
|
|
177
298
|
"update_theme",
|
|
178
|
-
"add_module_to_layout",
|
|
179
|
-
"remove_module_from_layout",
|
|
180
299
|
"set_template_layout",
|
|
181
300
|
"create_layout",
|
|
182
301
|
"set_site_defaults",
|
|
183
302
|
"duplicate_page",
|
|
184
|
-
"
|
|
303
|
+
"repoint_page_template",
|
|
185
304
|
"move_module",
|
|
186
305
|
"reorder_module",
|
|
187
306
|
"set_nav_menu",
|
|
188
307
|
"add_plugin_to_page",
|
|
308
|
+
// v0.12.0 — content_instances + placement binding
|
|
309
|
+
"list_content_instances",
|
|
310
|
+
"get_content_instance",
|
|
311
|
+
"create_content_instance",
|
|
312
|
+
"set_content_instance_values",
|
|
313
|
+
"delete_content_instance",
|
|
314
|
+
"set_placement_content",
|
|
315
|
+
"fork_placement_content",
|
|
316
|
+
// issue #299 — bulk-first build path (§11)
|
|
317
|
+
"build_page",
|
|
318
|
+
"create_content_instances",
|
|
319
|
+
"set_page_module_content_many",
|
|
189
320
|
];
|
|
190
321
|
/** Chat ops input shapes — used by the SvelteKit form actions. */
|
|
191
322
|
export const chatCreateSessionInput = z
|
|
@@ -201,10 +332,38 @@ export const chatCreateSessionInput = z
|
|
|
201
332
|
parentChatSessionId: z.string().uuid().nullable().optional(),
|
|
202
333
|
})
|
|
203
334
|
.strict();
|
|
335
|
+
/**
|
|
336
|
+
* issue #190 — an operator-attached image riding a user chat message.
|
|
337
|
+
* References a media_assets row (the upload endpoint owns validation
|
|
338
|
+
* + storage); image/* only in v1 so the provider mapping is always an
|
|
339
|
+
* image part.
|
|
340
|
+
*/
|
|
341
|
+
export const chatAttachmentSchema = z
|
|
342
|
+
.object({
|
|
343
|
+
/** Set for an operator upload — a `media_assets` row. */
|
|
344
|
+
assetId: z.string().uuid().optional(),
|
|
345
|
+
/**
|
|
346
|
+
* Set for an AI-produced chat image — an object-store key under
|
|
347
|
+
* `CHAT_IMAGE_PREFIX`. Deliberately not a media asset: see the prefix's
|
|
348
|
+
* doc comment. Exactly one of `assetId` / `storageKey` is present.
|
|
349
|
+
*/
|
|
350
|
+
storageKey: z.string().min(1).max(512).optional(),
|
|
351
|
+
mime: z.enum(["image/png", "image/jpeg", "image/webp", "image/gif"]),
|
|
352
|
+
alt: z.string().max(2048).optional(),
|
|
353
|
+
})
|
|
354
|
+
.strict()
|
|
355
|
+
.refine((a) => (a.assetId === undefined) !== (a.storageKey === undefined), {
|
|
356
|
+
message: "exactly one of assetId (operator upload) or storageKey (chat image) must be set",
|
|
357
|
+
});
|
|
358
|
+
/** issue #190 — attachments-per-message cap; bounds provider payload size. */
|
|
359
|
+
export const CHAT_MAX_ATTACHMENTS = 4;
|
|
204
360
|
export const chatSendMessageInput = z
|
|
205
361
|
.object({
|
|
206
362
|
chatSessionId: z.string().uuid(),
|
|
207
|
-
|
|
363
|
+
// Optional as of Plan B: a resume turn (see `resumeApproval`) carries no
|
|
364
|
+
// operator content — it re-runs the paused gated turn. A refine below
|
|
365
|
+
// requires exactly one of content / resumeApproval.
|
|
366
|
+
content: z.string().min(1).max(8000).optional(),
|
|
208
367
|
/** Element-reference chips appended to the message. */
|
|
209
368
|
chips: z
|
|
210
369
|
.array(z
|
|
@@ -215,16 +374,45 @@ export const chatSendMessageInput = z
|
|
|
215
374
|
})
|
|
216
375
|
.strict())
|
|
217
376
|
.default([]),
|
|
377
|
+
/** issue #190 — operator-attached images (see chatAttachmentSchema). */
|
|
378
|
+
attachments: z.array(chatAttachmentSchema).max(CHAT_MAX_ATTACHMENTS).default([]),
|
|
379
|
+
/**
|
|
380
|
+
* issue #29 — provenance of this turn. 'system' marks an auto-injected
|
|
381
|
+
* message the operator did NOT type (crawl-completion nudge, post-
|
|
382
|
+
* approval continuation): the model still receives it as a user turn,
|
|
383
|
+
* but the UI renders it as a muted status note instead of "You:".
|
|
384
|
+
* Omitted / undefined = operator-authored.
|
|
385
|
+
*/
|
|
386
|
+
origin: z.enum(["operator", "system"]).optional(),
|
|
218
387
|
/**
|
|
219
388
|
* P6.7.3 — the active /edit page id, threaded so the chat-runner can
|
|
220
389
|
* compose a Current-page volatile chunk in the system prompt and so
|
|
221
|
-
* tools that operate on a page (
|
|
390
|
+
* tools that operate on a page (add_module target='page') know the target
|
|
222
391
|
* without a chip. Optional because the standalone chat editor at
|
|
223
392
|
* /content/chat doesn't have a page context.
|
|
224
393
|
*/
|
|
225
394
|
activePageId: z.string().uuid().optional(),
|
|
395
|
+
/**
|
|
396
|
+
* Plan B (SDK approval gate) — production resume of a paused gated turn.
|
|
397
|
+
* When set, this is NOT a new operator message: the runner appends the
|
|
398
|
+
* SDK tool-approval-response (the Owner's in-chat Approve/Reject) to the
|
|
399
|
+
* paused turn's history and re-runs so the SDK either executes the gated
|
|
400
|
+
* tool (approved) or lets the model react to the denial. `approvalId` is
|
|
401
|
+
* the SDK id surfaced on the tool-approval-request ClientEvent.
|
|
402
|
+
*/
|
|
403
|
+
resumeApproval: z
|
|
404
|
+
.object({
|
|
405
|
+
approvalId: z.string().min(1).max(200),
|
|
406
|
+
approved: z.boolean(),
|
|
407
|
+
reason: z.string().max(2000).optional(),
|
|
408
|
+
})
|
|
409
|
+
.strict()
|
|
410
|
+
.optional(),
|
|
226
411
|
})
|
|
227
|
-
.strict()
|
|
412
|
+
.strict()
|
|
413
|
+
.refine((d) => d.content !== undefined || d.resumeApproval !== undefined, {
|
|
414
|
+
message: "content or resumeApproval is required",
|
|
415
|
+
});
|
|
228
416
|
export const chatRenameSessionInput = z
|
|
229
417
|
.object({
|
|
230
418
|
chatSessionId: z.string().uuid(),
|
|
@@ -256,6 +444,10 @@ export const chatPublishInput = z
|
|
|
256
444
|
"pageModuleContent",
|
|
257
445
|
"layout",
|
|
258
446
|
"structuredSet",
|
|
447
|
+
// v0.11.0 (#45) — themes primitive joins the publish/Stage
|
|
448
|
+
// surface so chat-branched theme edits replay into live
|
|
449
|
+
// on chat.publish.
|
|
450
|
+
"theme",
|
|
259
451
|
]),
|
|
260
452
|
entityId: z.string().uuid(),
|
|
261
453
|
})
|
|
@@ -292,25 +484,6 @@ export const aiProvidersSetInput = z
|
|
|
292
484
|
apiKey: z.string().min(1).max(500).optional(),
|
|
293
485
|
})
|
|
294
486
|
.strict();
|
|
295
|
-
/**
|
|
296
|
-
* P19 — `compose_from_import` AI tool input. Wraps
|
|
297
|
-
* `imports.compose_from_run`. Single transaction synthesis: aggregates
|
|
298
|
-
* theme tokens, creates one template bound to the default layout,
|
|
299
|
-
* materialises every staged import_pages row into a draft page +
|
|
300
|
-
* modules. Idempotent — pages already accepted skip cleanly.
|
|
301
|
-
*/
|
|
302
|
-
export const composeFromImportToolInput = z
|
|
303
|
-
.object({
|
|
304
|
-
runId: z.string().uuid(),
|
|
305
|
-
templateSlug: z
|
|
306
|
-
.string()
|
|
307
|
-
.min(1)
|
|
308
|
-
.max(120)
|
|
309
|
-
.regex(/^[a-z0-9][a-z0-9-]*$/, "lowercase letters/digits/hyphens, leading non-hyphen")
|
|
310
|
-
.optional(),
|
|
311
|
-
includeImportPageIds: z.array(z.string().uuid()).optional(),
|
|
312
|
-
})
|
|
313
|
-
.strict();
|
|
314
487
|
/**
|
|
315
488
|
* Input for `ai_providers.clear_key` — Owner-only NULLs the encrypted
|
|
316
489
|
* triplet so the resolver falls back to the env-var path for that
|
|
@@ -331,22 +504,6 @@ const slugInputSchema = z
|
|
|
331
504
|
.min(1)
|
|
332
505
|
.max(120)
|
|
333
506
|
.regex(/^[a-z0-9][a-z0-9-]*$/, "lowercase letters/digits/hyphens, leading non-hyphen");
|
|
334
|
-
export const createPageToolInput = z
|
|
335
|
-
.object({
|
|
336
|
-
name: z.string().min(1).max(256),
|
|
337
|
-
title: z.string().min(1).max(256),
|
|
338
|
-
slug: slugInputSchema,
|
|
339
|
-
locale: z.string().min(2).max(10).default("en"),
|
|
340
|
-
/**
|
|
341
|
-
* Optional. When omitted, the underlying `pages.create` op resolves
|
|
342
|
-
* to `site_defaults.default_template_id` (P6.7.6). The AI should pass
|
|
343
|
-
* a UUID from `## Site defaults` / `## Templates → layouts` only when
|
|
344
|
-
* the user asks for a non-default template.
|
|
345
|
-
*/
|
|
346
|
-
templateId: z.string().uuid().optional(),
|
|
347
|
-
status: z.enum(["draft", "published"]).default("draft"),
|
|
348
|
-
})
|
|
349
|
-
.strict();
|
|
350
507
|
/**
|
|
351
508
|
* P18 AI-completeness — `create_template` AI tool input. Wraps
|
|
352
509
|
* `templates.create` (widened to AI in this pass per CLAUDE.md §11
|
|
@@ -360,102 +517,51 @@ export const createTemplateToolInput = z
|
|
|
360
517
|
html: z.string().min(1).max(2_000_000),
|
|
361
518
|
css: z.string().max(2_000_000).default(""),
|
|
362
519
|
layoutId: z.string().uuid().optional(),
|
|
520
|
+
// Optional block-set metadata, symmetric with create_layout. Omit to
|
|
521
|
+
// auto-derive blocks from <caelo-slot> tags; pass to set displayName /
|
|
522
|
+
// position (each name must match a slot). Enforced op-side in
|
|
523
|
+
// templates.create (loud reject on a slot-less block).
|
|
524
|
+
blocks: z
|
|
525
|
+
.array(z.object({
|
|
526
|
+
name: z.string().min(1).max(80),
|
|
527
|
+
displayName: z.string().min(1).max(200),
|
|
528
|
+
position: z.number().int().min(0).max(1000),
|
|
529
|
+
}))
|
|
530
|
+
.optional(),
|
|
363
531
|
})
|
|
364
532
|
.strict();
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
export const
|
|
372
|
-
.object({
|
|
373
|
-
pageId: z.string().uuid(),
|
|
374
|
-
newTitle: z.string().min(1).max(256),
|
|
375
|
-
})
|
|
376
|
-
.strict();
|
|
377
|
-
export const changePageSlugToolInput = z
|
|
378
|
-
.object({
|
|
379
|
-
pageId: z.string().uuid(),
|
|
380
|
-
newSlug: slugInputSchema,
|
|
381
|
-
/**
|
|
382
|
-
* `auto` (default): create a 301 from the old slug → new slug.
|
|
383
|
-
* `skip`: only choose when the user explicitly says they don't
|
|
384
|
-
* want existing inbound links to redirect.
|
|
385
|
-
*/
|
|
386
|
-
redirectFromOld: z.enum(["auto", "skip"]).default("auto"),
|
|
387
|
-
})
|
|
388
|
-
.strict();
|
|
389
|
-
export const deletePageToolInput = z
|
|
390
|
-
.object({
|
|
391
|
-
pageId: z.string().uuid(),
|
|
392
|
-
/** '404' returns a not-found; 'redirect' creates a 301 to redirectTo. */
|
|
393
|
-
disposition: z.enum(["404", "redirect"]),
|
|
394
|
-
redirectTo: z.string().min(1).max(500).optional(),
|
|
395
|
-
})
|
|
396
|
-
.strict()
|
|
397
|
-
.refine((v) => v.disposition === "404" || (v.redirectTo !== undefined && v.redirectTo.length > 0), {
|
|
398
|
-
message: "redirectTo is required when disposition='redirect'",
|
|
399
|
-
path: ["redirectTo"],
|
|
400
|
-
});
|
|
401
|
-
export const removeModuleFromPageToolInput = z
|
|
533
|
+
/**
|
|
534
|
+
* The ONE remove-module input — `target` picks page vs layout, `targetRef` is a
|
|
535
|
+
* slug OR a uuid (resolved server-side). Mirrors `add_module` so placement and
|
|
536
|
+
* un-placement share one shape. (No `template` target: there is no
|
|
537
|
+
* remove_module_from_template, matching the add-side history.)
|
|
538
|
+
*/
|
|
539
|
+
export const removeModuleFromToolInput = z
|
|
402
540
|
.object({
|
|
403
|
-
|
|
541
|
+
target: z.enum(["page", "layout"]),
|
|
542
|
+
targetRef: z.string().min(1).max(200),
|
|
404
543
|
moduleId: z.string().uuid(),
|
|
405
544
|
})
|
|
406
545
|
.strict();
|
|
407
546
|
export const setStructuredSetToolInput = z
|
|
408
547
|
.object({
|
|
409
|
-
|
|
548
|
+
// v0.10.22 — added "language-selector" to match @caelo-cms/shared
|
|
549
|
+
// structuredSetKind (the 6th kind that was previously unreachable
|
|
550
|
+
// from any AI tool — the kind-specific wrappers covered only
|
|
551
|
+
// nav-menu + theme).
|
|
552
|
+
kind: z.enum(["nav-menu", "taxonomy", "theme", "tags", "link-list", "language-selector"]),
|
|
410
553
|
slug: slugInputSchema,
|
|
411
554
|
displayName: z.string().min(1).max(200),
|
|
412
555
|
items: z.array(z.unknown()),
|
|
413
556
|
})
|
|
414
557
|
.strict();
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
* P6.7.6 — layout-layer tools. Layouts are site-wide chrome (header /
|
|
423
|
-
* footer / nav) that wraps every page on every template bound to the
|
|
424
|
-
* layout. `add_module_to_layout` reaches every page across the site
|
|
425
|
-
* with one call; `set_template_layout` re-points a template's chrome.
|
|
426
|
-
* `create_layout` and `set_site_defaults` are Owner-only at the op
|
|
427
|
-
* level — AI calls reject with ActorScopeRejected and the chat surfaces
|
|
428
|
-
* the permission requirement.
|
|
429
|
-
*/
|
|
430
|
-
export const addModuleToLayoutToolInput = z
|
|
431
|
-
.object({
|
|
432
|
-
layoutSlug: slugInputSchema,
|
|
433
|
-
blockName: z.string().min(1).max(80),
|
|
434
|
-
position: positionInputSchema,
|
|
435
|
-
displayName: z.string().min(1).max(128),
|
|
436
|
-
html: z.string().min(1).max(50_000),
|
|
437
|
-
css: z.string().max(50_000).optional(),
|
|
438
|
-
js: z.string().max(50_000).optional(),
|
|
439
|
-
/** v0.5.21 — see addModuleToPageToolInput.fields for context. */
|
|
440
|
-
fields: z
|
|
441
|
-
.array(z
|
|
442
|
-
.object({
|
|
443
|
-
name: z.string().regex(/^[a-z][a-z0-9_]{0,63}$/),
|
|
444
|
-
kind: z.enum(["text", "richtext", "url", "image", "number", "boolean", "link"]),
|
|
445
|
-
label: z.string().min(1).max(128),
|
|
446
|
-
default: z.unknown().optional(),
|
|
447
|
-
})
|
|
448
|
-
.strict())
|
|
449
|
-
.max(64)
|
|
450
|
-
.optional(),
|
|
451
|
-
})
|
|
452
|
-
.strict();
|
|
453
|
-
export const removeModuleFromLayoutToolInput = z
|
|
454
|
-
.object({
|
|
455
|
-
layoutSlug: slugInputSchema,
|
|
456
|
-
moduleId: z.string().uuid(),
|
|
457
|
-
})
|
|
458
|
-
.strict();
|
|
558
|
+
// v0.10.22 — `updateThemeToolInput` removed. The kind-specific `update_theme`
|
|
559
|
+
// wrapper was replaced by the unified `set_structured_set` surface.
|
|
560
|
+
// v0.11.0 (#45) — theme moved out of structured_sets entirely into its
|
|
561
|
+
// own `themes` primitive (DTCG-shaped jsonb). Theme edits are now
|
|
562
|
+
// `set_theme_tokens` (loose-name patch) / `propose_create_theme`
|
|
563
|
+
// (gated mint) / `propose_activate_theme` (gated flip) — see
|
|
564
|
+
// packages/admin-core/src/ai/tools/{update-theme-tokens,get-theme,…}.ts.
|
|
459
565
|
export const setTemplateLayoutToolInput = z
|
|
460
566
|
.object({
|
|
461
567
|
templateId: z.string().uuid(),
|
|
@@ -505,7 +611,7 @@ export const duplicatePageToolInput = z
|
|
|
505
611
|
locale: z.string().min(2).max(10).optional(),
|
|
506
612
|
})
|
|
507
613
|
.strict();
|
|
508
|
-
export const
|
|
614
|
+
export const repointPageTemplateToolInput = z
|
|
509
615
|
.object({
|
|
510
616
|
pageId: z.string().uuid(),
|
|
511
617
|
newTemplateId: z.string().uuid(),
|
|
@@ -544,20 +650,6 @@ export const reorderModuleToolInput = z
|
|
|
544
650
|
direction: z.union([z.enum(["up", "down"]), z.number().int().min(0).max(1000)]),
|
|
545
651
|
})
|
|
546
652
|
.strict();
|
|
547
|
-
/**
|
|
548
|
-
* Convenience wrapper over `set_structured_set` for the `nav-menu`
|
|
549
|
-
* kind specifically. Users say "edit the menu", not "set the
|
|
550
|
-
* structured set kind=nav-menu" — this maps natural language to the
|
|
551
|
-
* right tool. Items shape matches `navMenuItem` from
|
|
552
|
-
* @caelo-cms/shared/structured-sets.
|
|
553
|
-
*/
|
|
554
|
-
export const setNavMenuToolInput = z
|
|
555
|
-
.object({
|
|
556
|
-
slug: slugInputSchema,
|
|
557
|
-
displayName: z.string().min(1).max(200),
|
|
558
|
-
items: z.array(z.unknown()),
|
|
559
|
-
})
|
|
560
|
-
.strict();
|
|
561
653
|
/**
|
|
562
654
|
* P7 — `find_media`. Searches the media library by alt-text /
|
|
563
655
|
* filename / mime. Returns up to `limit` matches with the WebP-800
|
|
@@ -584,6 +676,22 @@ export const findMediaToolInput = z
|
|
|
584
676
|
limit: z.number().int().min(1).max(50).default(15),
|
|
585
677
|
})
|
|
586
678
|
.strict();
|
|
679
|
+
/**
|
|
680
|
+
* run #10 D4 — `regenerate_media_variants`. Recovery tool for the
|
|
681
|
+
* staging generator's "media references unresolved" failure: re-runs
|
|
682
|
+
* the image pipeline for the named assets (or every asset with an
|
|
683
|
+
* incomplete ladder) and reports per-asset what was added or why
|
|
684
|
+
* nothing could be.
|
|
685
|
+
*/
|
|
686
|
+
export const regenerateMediaVariantsToolInput = z
|
|
687
|
+
.object({
|
|
688
|
+
assetIds: z.array(z.string().uuid()).min(1).max(100).optional(),
|
|
689
|
+
allMissing: z.boolean().default(false),
|
|
690
|
+
})
|
|
691
|
+
.strict()
|
|
692
|
+
.refine((v) => (v.assetIds !== undefined) !== v.allMissing, {
|
|
693
|
+
message: "pass either assetIds or allMissing: true (exactly one)",
|
|
694
|
+
});
|
|
587
695
|
/**
|
|
588
696
|
* P7 — `set_media_alt`. AI may improve a11y on an existing asset
|
|
589
697
|
* without a human round-trip (e.g. when the editor uploaded an image
|
|
@@ -596,6 +704,31 @@ export const setMediaAltToolInput = z
|
|
|
596
704
|
alt: z.string().max(2048),
|
|
597
705
|
})
|
|
598
706
|
.strict();
|
|
707
|
+
/**
|
|
708
|
+
* 0184 — `set_home_page`. AI designates a page as the site homepage (per
|
|
709
|
+
* locale, the locale root). Records `locales.home_page_id`; the page keeps
|
|
710
|
+
* its own slug but resolves to `/`.
|
|
711
|
+
*/
|
|
712
|
+
export const setHomePageToolInput = z
|
|
713
|
+
.object({
|
|
714
|
+
pageId: z.string().uuid(),
|
|
715
|
+
locale: z.string().min(2).max(10).optional(),
|
|
716
|
+
})
|
|
717
|
+
.strict();
|
|
718
|
+
/**
|
|
719
|
+
* 0181 — `set_media_source`. AI records an asset's provenance (source
|
|
720
|
+
* kind + origin detail) and licence when known. All fields but the
|
|
721
|
+
* assetId are optional; the op COALESCEs so omitted fields stay
|
|
722
|
+
* unchanged.
|
|
723
|
+
*/
|
|
724
|
+
export const setMediaSourceToolInput = z
|
|
725
|
+
.object({
|
|
726
|
+
assetId: z.string().uuid(),
|
|
727
|
+
sourceKind: z.enum(["upload", "ai_generated", "imported", "external"]).optional(),
|
|
728
|
+
sourceDetail: z.string().max(2048).optional(),
|
|
729
|
+
license: z.string().max(200).optional(),
|
|
730
|
+
})
|
|
731
|
+
.strict();
|
|
599
732
|
/**
|
|
600
733
|
* P8 — `set_page_seo`. Manual / panel writes to the per-page SEO
|
|
601
734
|
* sidecar. AI calls this only on explicit user intent
|
|
@@ -687,12 +820,6 @@ export const bulkCreateRedirectsToolInput = z
|
|
|
687
820
|
* `status='published'`. Use the bulk form for "publish all drafts" or
|
|
688
821
|
* any N>1 flip — saves N round-trips + N snapshot writes + N audit rows.
|
|
689
822
|
*/
|
|
690
|
-
export const setPageStatusToolInput = z
|
|
691
|
-
.object({
|
|
692
|
-
pageId: z.string().uuid(),
|
|
693
|
-
status: z.enum(["draft", "published"]),
|
|
694
|
-
})
|
|
695
|
-
.strict();
|
|
696
823
|
export const setPagesStatusManyToolInput = z
|
|
697
824
|
.object({
|
|
698
825
|
pageIds: z.array(z.string().uuid()).min(1).max(200),
|
|
@@ -721,6 +848,7 @@ export const bulkOptimizeSeoToolInput = z
|
|
|
721
848
|
context: z.string().max(4000).optional(),
|
|
722
849
|
})
|
|
723
850
|
.strict();
|
|
851
|
+
// v0.10.22 — `SetNavMenuToolInput` removed alongside `set_nav_menu` tool.
|
|
724
852
|
/**
|
|
725
853
|
* P9 — locales propose tool schemas. Per CLAUDE.md §11.A all four
|
|
726
854
|
* write paths are TWO-STEP (AI proposes → Owner clicks Approve);
|
|
@@ -873,66 +1001,6 @@ export const bootstrapSiteScaffoldToolInput = z
|
|
|
873
1001
|
setAsDefaults: z.boolean().optional(),
|
|
874
1002
|
})
|
|
875
1003
|
.strict();
|
|
876
|
-
/**
|
|
877
|
-
* v0.6.0 W4 (deferred) — composite tool. Creates a page and attaches N
|
|
878
|
-
* modules to its content block in one tool call, mirroring the way the
|
|
879
|
-
* AI naturally describes a multi-section page: a title + a series of
|
|
880
|
-
* sections, each with its own HTML/CSS. The handler runs the chain
|
|
881
|
-
* server-side so the AI doesn't have to orchestrate create_page +
|
|
882
|
-
* N×add_module_to_page across N+1 round-trips.
|
|
883
|
-
*
|
|
884
|
-
* `sections[]` are placed on the block named in `blockName` (default
|
|
885
|
-
* `content`) in the order given.
|
|
886
|
-
*/
|
|
887
|
-
export const composePageFromSpecToolInput = z
|
|
888
|
-
.object({
|
|
889
|
-
/** Page identifiers (mirror create_page). */
|
|
890
|
-
slug: z
|
|
891
|
-
.string()
|
|
892
|
-
.min(1)
|
|
893
|
-
.max(120)
|
|
894
|
-
.regex(/^[a-z0-9][a-z0-9-]*$/),
|
|
895
|
-
name: z.string().min(1).max(256),
|
|
896
|
-
title: z.string().min(1).max(256),
|
|
897
|
-
locale: z.string().min(2).max(10).optional(),
|
|
898
|
-
/** Template UUID. Optional — resolves to site_defaults if absent,
|
|
899
|
-
* yielding the same nextAction recovery as create_page when missing. */
|
|
900
|
-
templateId: z.string().uuid().optional(),
|
|
901
|
-
status: z.enum(["draft", "published"]).optional(),
|
|
902
|
-
/** Block to place the sections in. Defaults to `content`. */
|
|
903
|
-
blockName: z.string().min(1).max(80).optional(),
|
|
904
|
-
/** Sections to place, in order. Each becomes a freshly-created module. */
|
|
905
|
-
sections: z
|
|
906
|
-
.array(z.object({
|
|
907
|
-
displayName: z.string().min(1).max(128),
|
|
908
|
-
html: z.string().min(1).max(50_000),
|
|
909
|
-
css: z.string().max(50_000).optional(),
|
|
910
|
-
js: z.string().max(50_000).optional(),
|
|
911
|
-
}))
|
|
912
|
-
.min(1)
|
|
913
|
-
.max(32),
|
|
914
|
-
/**
|
|
915
|
-
* v0.6.1 — optional SEO. When supplied, the composite calls
|
|
916
|
-
* `pages_seo.autofill` after the page is created. When omitted,
|
|
917
|
-
* the composite auto-derives a meta description from the page
|
|
918
|
-
* title + first section's displayName (capped at the recommended
|
|
919
|
-
* length). This is the "invisible-by-default" SEO step — caller
|
|
920
|
-
* never needs a separate set_page_seo round-trip.
|
|
921
|
-
*
|
|
922
|
-
* `metaDescription` overrides the auto-derived fallback. Set
|
|
923
|
-
* `skipSeo: true` to opt out entirely (e.g., stub pages where
|
|
924
|
-
* SEO would be noise).
|
|
925
|
-
*/
|
|
926
|
-
seo: z
|
|
927
|
-
.object({
|
|
928
|
-
metaDescription: z.string().min(1).max(320).optional(),
|
|
929
|
-
ogImageAssetId: z.string().uuid().optional(),
|
|
930
|
-
skipSeo: z.boolean().optional(),
|
|
931
|
-
})
|
|
932
|
-
.strict()
|
|
933
|
-
.optional(),
|
|
934
|
-
})
|
|
935
|
-
.strict();
|
|
936
1004
|
/**
|
|
937
1005
|
* v0.6.0 W4 (deferred) — composite revert. Undoes every snapshot tagged
|
|
938
1006
|
* with the given chat's branch_id. Per-entity revert ops handle the
|