@avocadostudio-ai/orchestrator-core 0.6.0 → 0.7.0

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.
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { blockSchemas, operationSchema, validateBlockProps, findManifestSchemaIssue, isChrome, getMediaFields, isInBlockCatalogue, catalogueBlockTypes, mapSemanticThemeTokens, generateItemId, isRichTextDoc, fromMarkdown, mergeRichTextDoc } from "@avocadostudio-ai/shared";
2
+ import { blockSchemas, operationSchema, validateBlockProps, findManifestSchemaIssue, isChrome, getMediaFields, isInBlockCatalogue, catalogueBlockTypes, mapSemanticThemeTokens, generateItemId, isRichTextDoc, fromMarkdown, mergeRichTextDoc, resolveManifestFieldMeta } from "@avocadostudio-ai/shared";
3
3
  import { normalizeRouteCandidate } from "../nlp/intent-helpers.js";
4
4
  import { pageIdFromSlug, pageTitleFromSlug } from "../nlp/plan-normalizer.js";
5
5
  import { OperationError, toErrorDetail as _unifiedToErrorDetail } from "../errors.js";
@@ -692,6 +692,49 @@ function _validateWithManifestIfPresent(manifestByType, blockType, nextProps) {
692
692
  }
693
693
  return propCheck.data;
694
694
  }
695
+ /**
696
+ * A row added to a polymorphic list must say which shape it is.
697
+ *
698
+ * A list whose field meta carries a `discriminator` holds several shapes, and
699
+ * the row's value for that key is the only thing that says which. A row that
700
+ * omits it is not a row with one field missing: it is a row that corresponds to
701
+ * nothing the site can draw, and on a CMS that stores each row as its own
702
+ * document — `component` in Storyblok, `_type` in Sanity — it cannot be
703
+ * constructed at all. An adapter's only choices are to drop it or to guess.
704
+ *
705
+ * Dropping it is what happened. Asked for a statistics section, a planner added
706
+ * a card grid and filled it with four rows carrying every declared field and no
707
+ * `component`; the adapter skipped all four; **the page rendered the grid's
708
+ * heading over empty white space**, and the only trace anywhere was a
709
+ * `console.warn` in a dev server log. The manifest had the answer the whole
710
+ * time — the list declares its discriminator and names the types it admits.
711
+ *
712
+ * So reject it here, in the same place and the same category as an unknown
713
+ * prop, where the failure is loud and lands at the moment of the mistake with
714
+ * the admissible types in the message.
715
+ *
716
+ * Only omission is rejected. A value naming no branch is left alone on purpose:
717
+ * `itemFieldsByType` is derived from a schema that may enumerate fewer types
718
+ * than the CMS has, and `resolveListItemFields` already falls back to the union
719
+ * for exactly that case. Rejecting there would refuse rows a site can render.
720
+ */
721
+ function _requireListDiscriminator(manifestByType, blockType, listKey, item) {
722
+ const definition = manifestByType.get(blockType);
723
+ if (!definition)
724
+ return;
725
+ const listField = resolveManifestFieldMeta(definition).listFields[listKey];
726
+ const discriminator = listField?.discriminator;
727
+ if (!discriminator)
728
+ return;
729
+ const value = item[discriminator];
730
+ if (typeof value === "string" && value.length > 0)
731
+ return;
732
+ if (typeof value === "number" || typeof value === "boolean")
733
+ return;
734
+ const admissible = Object.keys(listField.itemFieldsByType ?? {}).filter((t) => t.length > 0);
735
+ const choices = admissible.length > 0 ? `: ${admissible.join(", ")}` : "";
736
+ throw new OperationError(`A new ${listKey} row must set "${discriminator}", which names its type${choices}`, { category: "schema_violation" });
737
+ }
695
738
  function _requireManifestComponent(manifestByType, blockType, operationName) {
696
739
  if (manifestByType.size === 0)
697
740
  return;
@@ -1207,6 +1250,7 @@ async function _applyOpsAtomicallyUnsafe(session, ops, options) {
1207
1250
  // (id included), and a duplicate id would make every later id lookup
1208
1251
  // silently resolve to the first occurrence.
1209
1252
  const newItem = structuredClone(op.item);
1253
+ _requireListDiscriminator(manifestByType, block.type, op.listKey, newItem);
1210
1254
  if (typeof newItem.id !== "string" ||
1211
1255
  newItem.id.length === 0 ||
1212
1256
  _itemIndexById(nextList, newItem.id) !== -1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avocadostudio-ai/orchestrator-core",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -22,20 +22,8 @@
22
22
  "openai": "^4.87.1",
23
23
  "sharp": "^0.34.5",
24
24
  "zod": "^4.3.6",
25
- "@avocadostudio-ai/migration-sdk": "^0.6.0",
26
- "@avocadostudio-ai/shared": "^0.6.0"
27
- },
28
- "peerDependencies": {
29
- "googleapis": "^171.4.0",
30
- "@google/genai": "^1.46.0"
31
- },
32
- "peerDependenciesMeta": {
33
- "googleapis": {
34
- "optional": true
35
- },
36
- "@google/genai": {
37
- "optional": true
38
- }
25
+ "@avocadostudio-ai/migration-sdk": "^0.7.0",
26
+ "@avocadostudio-ai/shared": "^0.7.0"
39
27
  },
40
28
  "devDependencies": {
41
29
  "@anthropic-ai/claude-agent-sdk": "^0.3.220",
@@ -77,6 +65,22 @@
77
65
  "url": "https://github.com/avocadostudio-ai/avocado.git",
78
66
  "directory": "packages/orchestrator-core"
79
67
  },
68
+ "peerDependencies": {
69
+ "googleapis": "^171.4.0",
70
+ "@google/genai": "^1.46.0",
71
+ "@anthropic-ai/claude-agent-sdk": ">=0.3.0"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "googleapis": {
75
+ "optional": true
76
+ },
77
+ "@google/genai": {
78
+ "optional": true
79
+ },
80
+ "@anthropic-ai/claude-agent-sdk": {
81
+ "optional": true
82
+ }
83
+ },
80
84
  "scripts": {
81
85
  "typecheck": "tsc --noEmit",
82
86
  "build": "tsc -p tsconfig.build.json && node ./scripts/copy-assets.mjs",