PluginProbe
Extendify / 3.2.2
Extendify v3.2.2
3.2.2 3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 All 128 releases
← All changes | src/Agent/api.js +45 -24 3.2.0 → 3.2.2 View file →
@@ -5,21 +5,20 @@
5 5 classifyBlockEdit,
6 6 IGNORED_BLOCKS,
7 7 } from '@agent/lib/classify-block-edit';
8 8 import { getClientTools } from '@agent/lib/client-tools';
9 +import { isExtendableHeader } from '@agent/lib/extendable-header';
9 10 import { INSERTABLE_BLOCK_TYPES } from '@agent/lib/insertable-blocks';
11 +import { fetchLinkablePages } from '@agent/lib/linkable-pages';
10 12 import { ensureCoreBlocksRegistered } from '@agent/lib/register-blocks';
11 13 import {
12 14 buildSubtreeManifest,
13 15 buildSubtreeTree,
16 + ownerAncestorFor,
14 17 } from '@agent/lib/subtree-manifest';
15 18 import { activeCanvasStep } from '@agent/state/canvas';
16 19 import { useChatStore } from '@agent/state/chat';
17 20 import { useGlobalStore } from '@agent/state/global';
18 -import {
19 - currentHeaderState,
20 - isExtendableHeader,
21 -} from '@agent/workflows/theme/tools/update-header-style';
22 21 import { tools } from '@agent/workflows/workflows';
23 22 import { AI_HOST } from '@constants';
24 23 import { useQuickEditStore } from '@quick-edit/state/store';
25 24 import { digest } from '@shared/api/digest';
@@ -26,9 +25,14 @@
26 25 import { reqDataBasics } from '@shared/lib/data';
27 26 import { getBlockType } from '@wordpress/blocks';
28 27
29 28 // page-templates:v1 declares that find-blocks and stage-block reach into parts.
30 -export const API_FEATURES = ['qa-tool', 'page-templates:v1'];
29 +// block-duplicate:v1 declares that update-blocks applies the duplicate op.
30 +export const API_FEATURES = [
31 + 'qa-tool',
32 + 'page-templates:v1',
33 + 'block-duplicate:v1',
34 +];
31 35
32 36 const rootFor = (block) =>
33 37 block?.id && block?.target
34 38 ? findBlockEl(block.id, document, scopeOf(block))
@@ -40,14 +44,21 @@
40 44 // The staged selection's attribute schemas, with the install's palette baked in.
41 45 export const blockSchemasFor = async (block) => {
42 46 const root = rootFor(block);
43 47 const { bucket } = classifyBlockEdit({ block, root });
44 - const types =
45 - bucket === 'single'
46 - ? [block.blockType]
47 - : IGNORED_BLOCKS.has(block?.blockType)
48 - ? []
49 - : [...new Set(buildSubtreeManifest(root).map(({ type }) => type))];
48 + const owner = ownerAncestorFor(root);
49 + const types = [
50 + ...new Set(
51 + [
52 + ...(bucket === 'single'
53 + ? [block.blockType]
54 + : IGNORED_BLOCKS.has(block?.blockType)
55 + ? []
56 + : buildSubtreeManifest(root).map(({ type }) => type)),
57 + owner?.type,
58 + ].filter(Boolean),
59 + ),
60 + ];
50 61 if (!types.length || types.length > EAGER_LOAD_MAX) return [];
51 62 await ensureCoreBlocksRegistered();
52 63 return types
53 64 .map((type) => ({ type, schema: buildBlockSchema(getBlockType(type)) }))
@@ -53,11 +64,28 @@
53 64 .map((type) => ({ type, schema: buildBlockSchema(getBlockType(type)) }))
54 65 .filter(({ schema }) => schema);
55 66 };
56 67
68 +// Without the site's addresses the model links a named page to "#".
69 +const withLinkablePages = async (data) => {
70 + if (!data.blockSchemas?.some(({ type }) => type === 'core/navigation-link')) {
71 + return data;
72 + }
73 + const pages = await fetchLinkablePages();
74 + return {
75 + ...data,
76 + linkablePages: pages.map(({ title, url }) => ({ title, url })),
77 + };
78 +};
79 +
57 80 // Only consumed for a multi-block edit; inert for single/combo.
58 -export const subtreeManifestFor = (block) =>
59 - buildSubtreeManifest(rootFor(block));
81 +export const subtreeManifestFor = (block) => {
82 + const root = rootFor(block);
83 + const owner = ownerAncestorFor(root);
84 + const manifest = buildSubtreeManifest(root);
85 + // buildSubtreeManifest treats the first entry as the selection's baseline.
86 + return owner ? [...manifest, owner] : manifest;
87 +};
60 88
61 89 // Selection hierarchy for the backend's positional edits.
62 90 export const subtreeTreeFor = (block) => buildSubtreeTree(rootFor(block));
63 91
@@ -122,12 +150,9 @@
122 150 });
123 151
124 152 if (!response.ok) {
125 153 digest({
126 - error: {
127 - name: response.statusText,
128 - messages: response.statusMessage,
129 - },
154 + error: response,
130 155 details: { source: 'agent', caller: 'pick-workflow' },
131 156 });
132 157 const error = new Error('Bad response from server');
133 158 error.response = response;
@@ -146,14 +171,14 @@
146 171 const { blockSearch: _spent, ...carried } = workflowData ?? {};
147 172 // Schemas: pinned up front when the selection is small enough; otherwise
148 173 // empty until get-block-schemas fetches them into the same field.
149 174 const data = isBlockPatching
150 - ? {
175 + ? await withLinkablePages({
151 176 ...carried,
152 177 blockSchemas: workflowData?.blockSchemas?.length
153 178 ? workflowData.blockSchemas
154 179 : await blockSchemasFor(block),
155 - }
180 + })
156 181 : workflowData;
157 182 const response = await fetch(`${AI_HOST}/api/agent/handle-workflow`, {
158 183 method: 'POST',
159 184 headers: { 'Content-Type': 'application/json' },
@@ -164,13 +189,9 @@
164 189 workflowData: data,
165 190 messages: getCurrentMessages(),
166 191 previousMessages: getMessagesFor(workflow?.id),
167 192 context: window.extAgentData.context,
168 - agentContext: {
169 - ...window.extAgentData.agentContext,
170 - // Without this the model offers the state the header is already in.
171 - headerState: currentHeaderState(),
172 - },
193 + agentContext: window.extAgentData.agentContext,
173 194 wpAbilities: window.extAgentData.wpAbilities ?? [],
174 195 clientTools: getClientTools(),
175 196 // The manifest is the backend's block-patching signal — never send it
176 197 // for other workflows or they'd be routed into the patching handler.