@vectoriox/iox-ui 5.61.0 → 5.62.1

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.
@@ -10826,7 +10826,7 @@ const BUSINESS_KEYS = shapeKeys(SHAPE['business']);
10826
10826
  const DESIGN_REQUIRED = requiredShapeKeys(SHAPE['design']);
10827
10827
  const DESIGN_KEYS = shapeKeys(SHAPE['design']);
10828
10828
  const CONTENT_TYPE_KEYS = shapeKeys(SHAPE['contentType']);
10829
- const ROLE_KEYS = shapeKeys(SHAPE['roles']);
10829
+ const ROLE_KEYS$1 = shapeKeys(SHAPE['roles']);
10830
10830
  const PAGE_SEO_KEYS = shapeKeys(SHAPE['pageSeo']);
10831
10831
  const ARRAY_TYPE = 'array';
10832
10832
  const FIELD_KEYS = shapeKeys(SHAPE['field']);
@@ -10984,8 +10984,8 @@ function validateRoles(c, roles, path, typeName, fields, fieldTypes) {
10984
10984
  }
10985
10985
  const out = {};
10986
10986
  for (const [role, field] of Object.entries(roles)) {
10987
- if (!ROLE_KEYS.includes(role)) {
10988
- c.add(SitePlanIssueCode.UnknownRole, `${path}.${role}`, `"${role}" is not a role (${ROLE_KEYS.join(', ')})`);
10987
+ if (!ROLE_KEYS$1.includes(role)) {
10988
+ c.add(SitePlanIssueCode.UnknownRole, `${path}.${role}`, `"${role}" is not a role (${ROLE_KEYS$1.join(', ')})`);
10989
10989
  continue;
10990
10990
  }
10991
10991
  if (typeof field !== 'string' || !fields.has(field)) {
@@ -13381,179 +13381,21 @@ function buildStepFacts(plan, actions = {}) {
13381
13381
  }
13382
13382
 
13383
13383
  /**
13384
- * The AI pipeline's WIRE vocabulary (AI plan §3c, §3n, M0): what iox-cms's `ai` API answers and what every client
13385
- * of it reads — the steps a model works on, who pays (IOX credits or the studio's own key), a model of the vetted
13386
- * list with its price per token class, the one cost → credits rule (1 credit = 1 US cent of what the AI cost, D4),
13387
- * the refusals (board 11) and a generation's stages. ONE definition: iox-cms meters and refuses with it, the
13388
- * Settings ▸ AI screen and the wizard show it. Pure types, enums and helpers; no transport, no prices typed here.
13389
- * The step and capability spellings are the control plane's (`GET /instances/me/entitlements`, AI plan "M0 control
13390
- * plane — the instance-route contract") — the platform has ONE spelling of each.
13391
- */
13392
- var AiProviderId;
13393
- (function (AiProviderId) {
13394
- AiProviderId["Anthropic"] = "anthropic";
13395
- AiProviderId["OpenRouter"] = "openrouter";
13396
- })(AiProviderId || (AiProviderId = {}));
13397
- /** Who pays: the platform's credits, or the studio's own provider key (metered for display, never charged). */
13398
- var AiKeyMode;
13399
- (function (AiKeyMode) {
13400
- AiKeyMode["Platform"] = "platform";
13401
- AiKeyMode["Own"] = "own";
13402
- })(AiKeyMode || (AiKeyMode = {}));
13403
- /** The steps a model works on (board 9's rows), in screen order. */
13404
- var AiStep;
13405
- (function (AiStep) {
13406
- AiStep["ReadMessage"] = "analysis";
13407
- AiStep["PlanSite"] = "plan";
13408
- AiStep["ChangePlan"] = "planEdit";
13409
- AiStep["MakePictures"] = "image";
13410
- AiStep["NameStructures"] = "scanNaming";
13411
- })(AiStep || (AiStep = {}));
13412
- const AI_STEPS = Object.values(AiStep);
13413
- /** Each step's name on the wire (the control plane's `label`, the console) — the screens translate their own. */
13414
- const AI_STEP_LABELS = {
13415
- [AiStep.ReadMessage]: 'Read your message',
13416
- [AiStep.PlanSite]: 'Plan the site',
13417
- [AiStep.ChangePlan]: 'Change the plan',
13418
- [AiStep.MakePictures]: 'Make pictures',
13419
- [AiStep.NameStructures]: 'Name repeated structures',
13420
- };
13421
- /** What a model must be able to do for a step to run on it. */
13422
- var AiCapability;
13423
- (function (AiCapability) {
13424
- AiCapability["StructuredOutput"] = "structuredOutputs";
13425
- AiCapability["ImageInput"] = "imageInput";
13426
- AiCapability["ImageOutput"] = "imageOutput";
13427
- })(AiCapability || (AiCapability = {}));
13428
- const AI_STEP_NEEDS = {
13429
- [AiStep.ReadMessage]: [AiCapability.StructuredOutput, AiCapability.ImageInput],
13430
- [AiStep.PlanSite]: [AiCapability.StructuredOutput],
13431
- [AiStep.ChangePlan]: [AiCapability.StructuredOutput],
13432
- [AiStep.MakePictures]: [AiCapability.ImageOutput],
13433
- [AiStep.NameStructures]: [AiCapability.StructuredOutput],
13434
- };
13435
- /** Tokens a typical use of a step reads and writes (plan §3d "Where credits go") — what a shown estimate is made of. */
13436
- const AI_STEP_TYPICAL_TOKENS = {
13437
- [AiStep.ReadMessage]: { input: 8_800, output: 1_500, cacheWrite: 0, cacheRead: 0 },
13438
- [AiStep.PlanSite]: { input: 12_000, output: 7_000, cacheWrite: 0, cacheRead: 0 },
13439
- [AiStep.ChangePlan]: { input: 6_000, output: 500, cacheWrite: 0, cacheRead: 0 },
13440
- // a 1 K picture is 1,120 image tokens (Google's figure for Nano Banana 2) — I-D4
13441
- [AiStep.MakePictures]: { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, imageOutput: 1_120 },
13442
- [AiStep.NameStructures]: { input: 80_000, output: 10_000, cacheWrite: 0, cacheRead: 0 },
13443
- };
13444
- /** 1 credit = 1 US cent of what the AI actually cost (decision D4). */
13445
- const CREDITS_PER_USD = 100;
13446
- const TOKENS_PER_PRICE_UNIT = 1_000_000;
13447
- const CREDIT_PRECISION = 1e6;
13448
- const NO_TOKENS = Object.freeze({ input: 0, output: 0, cacheWrite: 0, cacheRead: 0 });
13449
- /** What `tokens` cost at `price`, in US dollars. */
13450
- function costUsd(tokens, price) {
13451
- const perToken = (perMillion, fallback) => (perMillion ?? fallback) / TOKENS_PER_PRICE_UNIT;
13452
- return tokens.input * perToken(price.input, 0)
13453
- + tokens.output * perToken(price.output, 0)
13454
- + tokens.cacheWrite * perToken(price.cacheWrite, price.input)
13455
- + tokens.cacheRead * perToken(price.cacheRead, price.input)
13456
- + (tokens.imageOutput ?? 0) * perToken(price.imageOutput, price.output);
13457
- }
13458
- /** Dollars → credits, kept to a millionth of a credit (a short read costs a fraction of one). */
13459
- function creditsOfUsd(usd) {
13460
- return Math.round(usd * CREDITS_PER_USD * CREDIT_PRECISION) / CREDIT_PRECISION;
13461
- }
13462
- /** The credits a typical use of `step` costs on a model of `price` — never typed, always computed (§3d). */
13463
- function estimateStepCredits(step, price, tokens = AI_STEP_TYPICAL_TOKENS[step]) {
13464
- return creditsOfUsd(costUsd(tokens, price));
13465
- }
13466
- /** Can `model` do `step`? The step's needs against the model's capabilities. */
13467
- function modelCanDo(model, step) {
13468
- return AI_STEP_NEEDS[step].every(need => model.capabilities.includes(need));
13469
- }
13470
- /** Why AI did not run (board 11). The server refuses with these BEFORE any model call. */
13471
- var AiRefusalCode;
13472
- (function (AiRefusalCode) {
13473
- AiRefusalCode["NotInPlan"] = "ai.notInPlan";
13474
- AiRefusalCode["OutOfCredits"] = "ai.outOfCredits";
13475
- AiRefusalCode["CapReached"] = "ai.capReached";
13476
- AiRefusalCode["NotConnected"] = "ai.notConnected";
13477
- AiRefusalCode["KeyInvalid"] = "ai.keyInvalid";
13478
- AiRefusalCode["PlanUnknown"] = "ai.planUnknown";
13479
- })(AiRefusalCode || (AiRefusalCode = {}));
13480
- /** The HTTP status each refusal answers with (board 11). */
13481
- const AI_REFUSAL_STATUS = {
13482
- [AiRefusalCode.NotInPlan]: 403,
13483
- [AiRefusalCode.OutOfCredits]: 402,
13484
- [AiRefusalCode.CapReached]: 402,
13485
- [AiRefusalCode.NotConnected]: 409,
13486
- [AiRefusalCode.KeyInvalid]: 409,
13487
- [AiRefusalCode.PlanUnknown]: 503,
13488
- };
13489
- /** What a refusal offers next. */
13490
- var AiNextKind;
13491
- (function (AiNextKind) {
13492
- /** Ask IOX (a prefilled email to the operator, until a plan can be bought — D2). */
13493
- AiNextKind["Contact"] = "contact";
13494
- /** Open Settings ▸ AI. */
13495
- AiNextKind["Settings"] = "settings";
13496
- /** Try again later, or with less. */
13497
- AiNextKind["Retry"] = "retry";
13498
- })(AiNextKind || (AiNextKind = {}));
13499
- const AI_REFUSAL_NEXT = {
13500
- [AiRefusalCode.NotInPlan]: AiNextKind.Contact,
13501
- [AiRefusalCode.OutOfCredits]: AiNextKind.Contact,
13502
- [AiRefusalCode.CapReached]: AiNextKind.Retry,
13503
- [AiRefusalCode.NotConnected]: AiNextKind.Settings,
13504
- [AiRefusalCode.KeyInvalid]: AiNextKind.Settings,
13505
- [AiRefusalCode.PlanUnknown]: AiNextKind.Retry,
13506
- };
13507
- /** The plan entitlements AI reads (the control plane's `features` / `limits` keys) — ONE spelling. */
13508
- var AiFeature;
13509
- (function (AiFeature) {
13510
- AiFeature["SiteGeneration"] = "aiSiteGeneration";
13511
- })(AiFeature || (AiFeature = {}));
13512
- var AiLimit;
13513
- (function (AiLimit) {
13514
- AiLimit["CreditsPerMonth"] = "aiCreditsPerMonth";
13515
- AiLimit["CreditsPerGeneration"] = "aiCreditsPerGeneration";
13516
- })(AiLimit || (AiLimit = {}));
13517
- /** A generation's stages (§3d). The runner works while `task` is set; otherwise the owner has the next move. */
13518
- var AiGenerationStage;
13519
- (function (AiGenerationStage) {
13520
- AiGenerationStage["Reading"] = "reading";
13521
- AiGenerationStage["Analysis"] = "analysis";
13522
- AiGenerationStage["Design"] = "design";
13523
- AiGenerationStage["Plan"] = "plan";
13524
- AiGenerationStage["Build"] = "build";
13525
- AiGenerationStage["Done"] = "done";
13526
- AiGenerationStage["Failed"] = "failed";
13527
- AiGenerationStage["Cancelled"] = "cancelled";
13528
- })(AiGenerationStage || (AiGenerationStage = {}));
13529
- /** The stages nothing advances any more. */
13530
- const FINAL_GENERATION_STAGES = [AiGenerationStage.Done, AiGenerationStage.Failed, AiGenerationStage.Cancelled];
13531
- /** How one model call ended — a ledger row's status. */
13532
- var AiCallStatus;
13533
- (function (AiCallStatus) {
13534
- /** Written before the call: a reservation at the estimate until the call settles. */
13535
- AiCallStatus["Started"] = "started";
13536
- AiCallStatus["Ok"] = "ok";
13537
- /** The answer came back and the shared validator refused it (with a path). */
13538
- AiCallStatus["Invalid"] = "invalid";
13539
- /** The model declined (a provider refusal) — charged what the provider reports. */
13540
- AiCallStatus["Refused"] = "refused";
13541
- AiCallStatus["Error"] = "error";
13542
- /** The process died during the call: charged at the estimate, since what it cost was never reported. */
13543
- AiCallStatus["Interrupted"] = "interrupted";
13544
- })(AiCallStatus || (AiCallStatus = {}));
13545
-
13546
- /**
13547
- * The plan WIRE (AI plan M1-3, vectoriox/iox-cms#45): what a model writes when it plans a site — a compact format inside
13548
- * the Claude API's structured-output limits (≤ 24 optional parameters and ≤ 16 unions per request; `strict-schema.ts`).
13549
- * The Site Plan itself (`SPEC_SITE_PLAN_SHAPE`) has 213 optional keys once every pattern is a branch, so the model never
13550
- * writes it: it writes this, and ONE pure `expandPlanWire` turns it into the Site Plan that `validateSitePlan` and the
13551
- * stage's gate judge exactly as before.
13384
+ * The plan WIRE (AI plan M1-3, vectoriox/iox-cms#45, #47): what a model writes when it plans a site — a compact format
13385
+ * inside the Claude API's structured-output limits (≤ 24 optional parameters and ≤ 16 unions per request, and a compiled
13386
+ * grammar under the size Claude refuses — measured as ≤ 60 properties; `strict-schema.ts`). The Site Plan itself
13387
+ * (`SPEC_SITE_PLAN_SHAPE`) has 213 optional keys once every pattern is a branch, so the model never writes it: it writes
13388
+ * this, and ONE pure `expandPlanWire` turns it into the Site Plan that `validateSitePlan` and the stage's gate judge
13389
+ * exactly as before.
13390
+ *
13391
+ * - **Written in two PARTS, one request each** (#47 — the whole plan in one format was 64 properties): the **structure** (the business,
13392
+ * the content types, the data sources) and then the **pages** (the pages, the header and footer), written knowing the
13393
+ * structure. Every top-level key belongs to exactly one part (`part` below); `expandPlanWire` puts the parts together.
13552
13394
  *
13553
13395
  * - **Every key required.** A plan key that may be absent is written with its stated "none" — `''`, `0`, `false`, `[]`.
13554
13396
  * - **Closed lists as enums** (patterns, page types, needs, slots, parts, forms, entrances, header/footer), from the
13555
13397
  * contract — never per-pattern branches: the gate refuses a slot or a part the chosen pattern does not have, with its path.
13556
- * - **Maps as lists:** slots `[{ slot, text }]`, links `[{ slot, route }]`, seed values `[{ field, value }]`, roles fixed.
13398
+ * - **Maps as lists:** slots `[{ slot, text }]`, links `[{ slot, route }]`, seed values `[{ field, value }]`, roles `[{ role, field }]`.
13557
13399
  * - **One data source object** for both kinds; `type` says which keys are read (the other kind's hold their none).
13558
13400
  * - **What the model does not decide is not on the wire** (`PLAN_WIRE_DERIVED`): the look (the owner's pick), the run,
13559
13401
  * the language (the reading's), the facts (the owner's own, verbatim), the version, the home page (the page at `/`).
@@ -13563,13 +13405,24 @@ var AiCallStatus;
13563
13405
  * every plan key is either carried or derived (`planWireGaps()`); a key the plan shape gains breaks the builder until it
13564
13406
  * is placed. The expanders translate, never repair: a value the model got wrong stays wrong, for the gate to refuse.
13565
13407
  */
13566
- const PLAN_WIRE_SCHEMA_NAME = 'site_plan';
13408
+ /** The plan's parts, in the order they are written: the pages bind to the structure's sources and types. */
13409
+ var PlanPart;
13410
+ (function (PlanPart) {
13411
+ PlanPart["Structure"] = "structure";
13412
+ PlanPart["Pages"] = "pages";
13413
+ })(PlanPart || (PlanPart = {}));
13414
+ const PLAN_PARTS = [PlanPart.Structure, PlanPart.Pages];
13415
+ /** Each part's format name (a request names the format it asks for). */
13416
+ const PLAN_WIRE_PART_SCHEMA_NAMES = {
13417
+ [PlanPart.Structure]: 'site_plan_structure',
13418
+ [PlanPart.Pages]: 'site_plan_pages',
13419
+ };
13567
13420
  var PlanWireEntry;
13568
13421
  (function (PlanWireEntry) {
13569
13422
  PlanWireEntry["Plan"] = "plan";
13570
13423
  PlanWireEntry["Business"] = "business";
13571
13424
  PlanWireEntry["ContentType"] = "contentType";
13572
- PlanWireEntry["Roles"] = "roles";
13425
+ PlanWireEntry["RoleField"] = "roleField";
13573
13426
  PlanWireEntry["Field"] = "field";
13574
13427
  PlanWireEntry["Seed"] = "seed";
13575
13428
  PlanWireEntry["SeedValue"] = "seedValue";
@@ -13583,13 +13436,17 @@ var PlanWireEntry;
13583
13436
  })(PlanWireEntry || (PlanWireEntry = {}));
13584
13437
  const PLAN_SHAPE = SPEC_SITE_PLAN_SHAPE;
13585
13438
  const key = (ref, none) => ({ refs: [ref], ...(none !== undefined ? { none } : {}) });
13439
+ const inPart = (part, spec) => ({ ...spec, part });
13440
+ /** The roles a content type's fields play (`roles` of the plan shape), in the plan shape's order. */
13441
+ const ROLE_KEYS = shapeKeys(PLAN_SHAPE['roles']);
13442
+ const roleWords = ROLE_KEYS.map(role => `${role}: ${describeShapeKey(PLAN_SHAPE['roles'], role).replace(/^string — /, '')}`).join('; ');
13586
13443
  const WIRE_KEYS = {
13587
13444
  plan: {
13588
- business: key('plan.business'),
13589
- contentTypes: key('plan.contentTypes'),
13590
- dataSources: { refs: ['plan.dataSources'], text: 'dataSource[] — merged by alias into the org library' },
13591
- pages: key('plan.pages'),
13592
- globals: key('plan.globals'),
13445
+ business: inPart(PlanPart.Structure, key('plan.business')),
13446
+ contentTypes: inPart(PlanPart.Structure, key('plan.contentTypes')),
13447
+ dataSources: inPart(PlanPart.Structure, { refs: ['plan.dataSources'], text: 'dataSource[] — merged by alias into the org library' }),
13448
+ pages: inPart(PlanPart.Pages, key('plan.pages')),
13449
+ globals: inPart(PlanPart.Pages, key('plan.globals')),
13593
13450
  },
13594
13451
  business: {
13595
13452
  name: key('business.name'),
@@ -13598,17 +13455,13 @@ const WIRE_KEYS = {
13598
13455
  contentType: {
13599
13456
  name: key('contentType.name'),
13600
13457
  label: key('contentType.label'),
13601
- roles: key('contentType.roles'),
13458
+ roles: { refs: ['contentType.roles'], text: `roleField[] — ${describeShapeKey(PLAN_SHAPE['contentType'], 'roles').replace(/^roles — /, '')}; a role no field plays is left out` },
13602
13459
  fields: key('contentType.fields'),
13603
13460
  seed: { refs: ['contentType.seed'], text: 'seed — the sample items', none: 'count 0 and no values' },
13604
13461
  },
13605
- roles: {
13606
- title: key('roles.title'),
13607
- summary: key('roles.summary', "''"),
13608
- image: key('roles.image', "''"),
13609
- meta: key('roles.meta', "''"),
13610
- date: key('roles.date', "''"),
13611
- gallery: key('roles.gallery', "''"),
13462
+ roleField: {
13463
+ role: { refs: ROLE_KEYS.map(role => `roles.${role}`), text: `${ROLE_KEYS.map(r => `'${r}'`).join(' | ')} — ${roleWords}` },
13464
+ field: { refs: ['roles.title'], text: 'string — the field of the type that plays it' },
13612
13465
  },
13613
13466
  field: {
13614
13467
  name: key('field.name'),
@@ -13697,6 +13550,19 @@ const wireText = (spec) => {
13697
13550
  };
13698
13551
  /** The wire's shape table, in the plan table's words — what the model reads, and what the format is built from. */
13699
13552
  const PLAN_WIRE_SHAPE = Object.fromEntries(Object.entries(WIRE_KEYS).map(([entry, keys]) => [entry, Object.fromEntries(Object.entries(keys).map(([k, spec]) => [k, wireText(spec)]))]));
13553
+ /** Each part's top-level keys, from the ONE wire table. */
13554
+ const PLAN_PART_KEYS = Object.fromEntries(PLAN_PARTS.map(part => [part, Object.entries(WIRE_KEYS.plan).filter(([, spec]) => spec.part === part).map(([k]) => k)]));
13555
+ /** The keys of a shape entry, but these. */
13556
+ const shapeWithout = (entry, keys) => Object.fromEntries(Object.entries(entry).filter(([k]) => !keys.includes(k.replace(/\?$/, ''))));
13557
+ /**
13558
+ * The part a plan path is the answer of (`contentTypes[1].seed.title` → structure, `pages[0].sections[2]` → pages). A
13559
+ * path no part writes — the root (the composer), or a key the generation decides — is the LAST part's: it is written
13560
+ * knowing everything before it.
13561
+ */
13562
+ function planPartOfPath(path) {
13563
+ const top = path.split(/[.[]/, 1)[0];
13564
+ return PLAN_PARTS.find(part => PLAN_PART_KEYS[part].includes(top)) ?? PLAN_PARTS[PLAN_PARTS.length - 1];
13565
+ }
13700
13566
  /** Plan keys neither carried by a wire key nor derived — always empty (a spec and the builder hold it). */
13701
13567
  function planWireGaps() {
13702
13568
  const carried = new Set(Object.values(WIRE_KEYS).flatMap(keys => Object.values(keys).flatMap(spec => spec.refs)));
@@ -13729,13 +13595,23 @@ const globalIds = (position) => Object.entries(SITE_PATTERNS).filter(([, p]) =>
13729
13595
  function wireFieldJsonSchema() {
13730
13596
  return shaped(PlanWireEntry.Field, { name: jsonText(), type: jsonText(), controller: jsonText(), required: jsonBoolean(), note: jsonText() });
13731
13597
  }
13732
- function wireContentTypeJsonSchema() {
13733
- const roleKeys = shapeKeys(PLAN_WIRE_SHAPE.roles);
13598
+ /** The keys an entry leaves out under `options`. */
13599
+ const CHILD_KEYS = { contentType: 'fields', page: 'sections' };
13600
+ const childless = (entry, options) => (options.children === false && CHILD_KEYS[entry] ? [CHILD_KEYS[entry]] : []);
13601
+ /** The shape an entry is written in, under `options`. */
13602
+ function wireEntryShape(entry, options = {}) {
13603
+ return shapeWithout(PLAN_WIRE_SHAPE[entry], childless(entry, options));
13604
+ }
13605
+ const shapedAs = (entry, options, properties) => {
13606
+ const left = childless(entry, options);
13607
+ return jsonShapedObject(wireEntryShape(entry, options), Object.fromEntries(Object.entries(properties).filter(([k]) => !left.includes(k))), `wire ${entry}`);
13608
+ };
13609
+ function wireContentTypeJsonSchema(options = {}) {
13734
13610
  const value = shaped(PlanWireEntry.SeedValue, { field: jsonText(), value: jsonAnyOf([jsonText(), jsonListOf(jsonText())]) });
13735
- return shaped(PlanWireEntry.ContentType, {
13611
+ return shapedAs(PlanWireEntry.ContentType, options, {
13736
13612
  name: jsonText(),
13737
13613
  label: jsonText(),
13738
- roles: shaped(PlanWireEntry.Roles, Object.fromEntries(roleKeys.map(k => [k, jsonText()]))),
13614
+ roles: jsonListOf(shaped(PlanWireEntry.RoleField, { role: jsonOneOf(ROLE_KEYS), field: jsonText() })),
13739
13615
  fields: jsonListOf(wireFieldJsonSchema()),
13740
13616
  seed: shaped(PlanWireEntry.Seed, { count: jsonInteger(), sample: jsonBoolean(), isPublished: jsonBoolean(), values: jsonListOf(value) }),
13741
13617
  });
@@ -13766,8 +13642,8 @@ function wireSectionJsonSchema() {
13766
13642
  enter: orNone(ANIMATE_IDS),
13767
13643
  });
13768
13644
  }
13769
- function wirePageJsonSchema() {
13770
- return shaped(PlanWireEntry.Page, {
13645
+ function wirePageJsonSchema(options = {}) {
13646
+ return shapedAs(PlanWireEntry.Page, options, {
13771
13647
  key: jsonText(),
13772
13648
  name: jsonText(),
13773
13649
  route: jsonText(),
@@ -13780,21 +13656,32 @@ function wirePageJsonSchema() {
13780
13656
  sections: jsonListOf(wireSectionJsonSchema()),
13781
13657
  });
13782
13658
  }
13783
- /** The format a model writes a plan in. The same for every studio and run: nothing in it is pinned. */
13784
- function planWireJsonSchema() {
13659
+ const PLAN_TOP_SCHEMAS = {
13660
+ business: () => shaped(PlanWireEntry.Business, { name: jsonText(), tagline: jsonText() }),
13661
+ contentTypes: () => jsonListOf(wireContentTypeJsonSchema()),
13662
+ dataSources: () => jsonListOf(wireDataSourceJsonSchema()),
13663
+ pages: () => jsonListOf(wirePageJsonSchema()),
13664
+ globals: () => shaped(PlanWireEntry.Globals, {
13665
+ [GlobalSlot.Header]: jsonOneOf(globalIds(PatternGlobalPosition.Before)),
13666
+ [GlobalSlot.Footer]: jsonOneOf(globalIds(PatternGlobalPosition.After)),
13667
+ }),
13668
+ };
13669
+ /** The plan's top-level shape for one part (its keys only) — what that part's request shows and is built from. */
13670
+ function planWirePartShape(part) {
13671
+ return Object.fromEntries(Object.entries(PLAN_WIRE_SHAPE.plan).filter(([k]) => PLAN_PART_KEYS[part].includes(k)));
13672
+ }
13673
+ /**
13674
+ * The format a model writes one part of a plan in — one request each (vectoriox/iox-cms#47). The same for every studio
13675
+ * and run: nothing in it is pinned, so the provider compiles each part's grammar once.
13676
+ */
13677
+ function planWirePartJsonSchema(part) {
13785
13678
  const gaps = planWireGaps();
13786
13679
  if (gaps.length)
13787
13680
  throw new PlanWireShapeError(`the plan wire neither carries nor derives ${gaps.join(', ')}`);
13788
- return shaped(PlanWireEntry.Plan, {
13789
- business: shaped(PlanWireEntry.Business, { name: jsonText(), tagline: jsonText() }),
13790
- contentTypes: jsonListOf(wireContentTypeJsonSchema()),
13791
- dataSources: jsonListOf(wireDataSourceJsonSchema()),
13792
- pages: jsonListOf(wirePageJsonSchema()),
13793
- globals: shaped(PlanWireEntry.Globals, {
13794
- [GlobalSlot.Header]: jsonOneOf(globalIds(PatternGlobalPosition.Before)),
13795
- [GlobalSlot.Footer]: jsonOneOf(globalIds(PatternGlobalPosition.After)),
13796
- }),
13797
- });
13681
+ const missing = Object.keys(WIRE_KEYS.plan).filter(k => !PLAN_TOP_SCHEMAS[k] || !WIRE_KEYS.plan[k].part);
13682
+ if (missing.length)
13683
+ throw new PlanWireShapeError(`the plan wire's ${missing.join(', ')} has no part or no format`);
13684
+ return jsonShapedObject(planWirePartShape(part), Object.fromEntries(PLAN_PART_KEYS[part].map(k => [k, PLAN_TOP_SCHEMAS[k]()])), `wire plan ${part}`);
13798
13685
  }
13799
13686
  /** A value standing for "absent": `''` (or nothing at all). */
13800
13687
  const isNone = (v) => v === undefined || v === NONE;
@@ -13820,11 +13707,12 @@ function expandWireField(wire) {
13820
13707
  const { required, note, ...rest } = wire;
13821
13708
  return { ...rest, ...optional('required', required, v => v === undefined || v === false), ...optional('note', note) };
13822
13709
  }
13823
- function expandWireContentType(wire) {
13710
+ function expandWireContentType(wire, options = {}) {
13824
13711
  if (!isPlainObject(wire))
13825
13712
  return wire;
13826
- const { roles, fields, seed, ...rest } = wire;
13827
- const out = { ...rest, roles: isPlainObject(roles) ? Object.fromEntries(Object.entries(roles).filter(([, v]) => !isNone(v))) : roles, fields: each(fields, expandWireField) };
13713
+ const written = options.children === false ? { fields: [], ...wire } : wire;
13714
+ const { roles, fields, seed, ...rest } = written;
13715
+ const out = { ...rest, roles: listToMap(roles, 'role', 'field'), fields: each(fields, expandWireField) };
13828
13716
  if (!isPlainObject(seed))
13829
13717
  return seed === undefined ? out : { ...out, seed };
13830
13718
  const { values, ...fixed } = seed;
@@ -13859,10 +13747,11 @@ function expandWireSection(wire) {
13859
13747
  ...optional('enter', enter),
13860
13748
  };
13861
13749
  }
13862
- function expandWirePage(wire) {
13750
+ function expandWirePage(wire, options = {}) {
13863
13751
  if (!isPlainObject(wire))
13864
13752
  return wire;
13865
- const { archetype, need, template, routeSource, seo, sections, ...rest } = wire;
13753
+ const written = options.children === false ? { sections: [], ...wire } : wire;
13754
+ const { archetype, need, template, routeSource, seo, sections, ...rest } = written;
13866
13755
  const words = isPlainObject(seo) ? { ...optional('title', seo['title']), ...optional('description', seo['description']) } : seo;
13867
13756
  return {
13868
13757
  ...rest,
@@ -13882,12 +13771,17 @@ function expandWireBusiness(wire, facts) {
13882
13771
  return { ...rest, ...optional('tagline', tagline), ...(facts && Object.keys(facts).length ? { facts: { ...facts } } : {}) };
13883
13772
  }
13884
13773
  /**
13885
- * A model's plan (the wire) + what the generation decides (the look, the run, the language, the owner's facts) → the
13886
- * Site Plan. Pure. Translates, never repairs: whatever the answer got wrong is still wrong, for `validateSitePlan`.
13774
+ * A model's plan (its parts, on the wire) + what the generation decides (the look, the run, the language, the owner's
13775
+ * facts) → the Site Plan. Pure. Translates, never repairs: whatever an answer got wrong is still wrong, for
13776
+ * `validateSitePlan` — a part that is not an object is the result itself, a part not written yet leaves its keys out
13777
+ * (the validator then names them, at their own paths).
13887
13778
  */
13888
- function expandPlanWire(wire, context) {
13889
- if (!isPlainObject(wire))
13890
- return wire;
13779
+ function expandPlanWire(parts, context) {
13780
+ const written = PLAN_PARTS.filter(part => parts[part] !== undefined).map(part => parts[part]);
13781
+ const odd = written.find(value => !isPlainObject(value));
13782
+ if (odd !== undefined)
13783
+ return odd;
13784
+ const wire = Object.assign({}, ...written);
13891
13785
  const { business, contentTypes, dataSources, pages, ...rest } = wire;
13892
13786
  return {
13893
13787
  ...rest,
@@ -13905,11 +13799,12 @@ function expandPlanWire(wire, context) {
13905
13799
  function fieldToWire(field) {
13906
13800
  return { name: field.name, type: field.type, controller: field.controller, required: field.required ?? false, note: field.note ?? NONE };
13907
13801
  }
13908
- function contentTypeToWire(type) {
13909
- const roles = Object.fromEntries(shapeKeys(PLAN_WIRE_SHAPE.roles).map(role => [role, type.roles[role] ?? NONE]));
13802
+ function contentTypeToWire(type, options = {}) {
13803
+ const played = type.roles;
13804
+ const roles = ROLE_KEYS.filter(role => played[role]).map(role => ({ role, field: played[role] }));
13910
13805
  const { count = 0, sample = false, isPublished = false, ...values } = type.seed ?? {};
13911
13806
  return {
13912
- name: type.name, label: type.label, roles, fields: type.fields.map(fieldToWire),
13807
+ name: type.name, label: type.label, roles, ...(options.children === false ? {} : { fields: type.fields.map(fieldToWire) }),
13913
13808
  seed: { count, sample, isPublished, values: mapToList(values, 'field', 'value') },
13914
13809
  };
13915
13810
  }
@@ -13928,24 +13823,224 @@ function sectionToWire(section) {
13928
13823
  binding: section.binding ?? NONE, without: [...(section.without ?? [])], form: section.form ?? NONE, enter: section.enter ?? NONE,
13929
13824
  };
13930
13825
  }
13931
- function pageToWire(page) {
13826
+ function pageToWire(page, options = {}) {
13932
13827
  return {
13933
13828
  key: page.key, name: page.name, route: page.route, inNav: page.inNav ?? false, archetype: page.archetype ?? NONE, need: page.need ?? NONE,
13934
13829
  template: page.template ?? NONE, routeSource: page.routeSource ?? NONE,
13935
- seo: { title: page.seo?.title ?? NONE, description: page.seo?.description ?? NONE }, sections: page.sections.map(sectionToWire),
13830
+ seo: { title: page.seo?.title ?? NONE, description: page.seo?.description ?? NONE },
13831
+ ...(options.children === false ? {} : { sections: page.sections.map(sectionToWire) }),
13936
13832
  };
13937
13833
  }
13938
- /** The plan → the wire: what a model would have written (the plan edits' prompt shows it; fakes and evals write it). */
13834
+ const PLAN_TO_WIRE = {
13835
+ business: plan => ({ name: plan.business.name, tagline: plan.business.tagline ?? NONE }),
13836
+ contentTypes: plan => plan.contentTypes.map(type => contentTypeToWire(type)),
13837
+ dataSources: plan => plan.dataSources.map(dataSourceToWire),
13838
+ pages: plan => plan.pages.map(page => pageToWire(page)),
13839
+ globals: plan => ({ ...plan.globals }),
13840
+ };
13841
+ /** One part of the plan on the wire: what that part's request would have answered. */
13842
+ function planPartToWire(plan, part) {
13843
+ return Object.fromEntries(PLAN_PART_KEYS[part].map(k => [k, PLAN_TO_WIRE[k](plan)]));
13844
+ }
13845
+ /** The plan → the wire, by part: what a model would have written (the change prompt shows it; fakes and evals write it). */
13939
13846
  function planToWire(plan) {
13940
- return {
13941
- business: { name: plan.business.name, tagline: plan.business.tagline ?? NONE },
13942
- contentTypes: plan.contentTypes.map(contentTypeToWire),
13943
- dataSources: plan.dataSources.map(dataSourceToWire),
13944
- pages: plan.pages.map(pageToWire),
13945
- globals: { ...plan.globals },
13946
- };
13847
+ return Object.fromEntries(PLAN_PARTS.map(part => [part, planPartToWire(plan, part)]));
13947
13848
  }
13948
13849
 
13850
+ /**
13851
+ * The AI pipeline's WIRE vocabulary (AI plan §3c, §3n, M0): what iox-cms's `ai` API answers and what every client
13852
+ * of it reads — the steps a model works on, who pays (IOX credits or the studio's own key), a model of the vetted
13853
+ * list with its price per token class, the one cost → credits rule (1 credit = 1 US cent of what the AI cost, D4),
13854
+ * the refusals (board 11) and a generation's stages. ONE definition: iox-cms meters and refuses with it, the
13855
+ * Settings ▸ AI screen and the wizard show it. Pure types, enums and helpers; no transport, no prices typed here.
13856
+ * The step and capability spellings are the control plane's (`GET /instances/me/entitlements`, AI plan "M0 control
13857
+ * plane — the instance-route contract") — the platform has ONE spelling of each.
13858
+ */
13859
+ var AiProviderId;
13860
+ (function (AiProviderId) {
13861
+ AiProviderId["Anthropic"] = "anthropic";
13862
+ AiProviderId["OpenRouter"] = "openrouter";
13863
+ })(AiProviderId || (AiProviderId = {}));
13864
+ /** Who pays: the platform's credits, or the studio's own provider key (metered for display, never charged). */
13865
+ var AiKeyMode;
13866
+ (function (AiKeyMode) {
13867
+ AiKeyMode["Platform"] = "platform";
13868
+ AiKeyMode["Own"] = "own";
13869
+ })(AiKeyMode || (AiKeyMode = {}));
13870
+ /** The steps a model works on (board 9's rows), in screen order. */
13871
+ var AiStep;
13872
+ (function (AiStep) {
13873
+ AiStep["ReadMessage"] = "analysis";
13874
+ AiStep["PlanSite"] = "plan";
13875
+ AiStep["ChangePlan"] = "planEdit";
13876
+ AiStep["MakePictures"] = "image";
13877
+ AiStep["NameStructures"] = "scanNaming";
13878
+ })(AiStep || (AiStep = {}));
13879
+ const AI_STEPS = Object.values(AiStep);
13880
+ /** Each step's name on the wire (the control plane's `label`, the console) — the screens translate their own. */
13881
+ const AI_STEP_LABELS = {
13882
+ [AiStep.ReadMessage]: 'Read your message',
13883
+ [AiStep.PlanSite]: 'Plan the site',
13884
+ [AiStep.ChangePlan]: 'Change the plan',
13885
+ [AiStep.MakePictures]: 'Make pictures',
13886
+ [AiStep.NameStructures]: 'Name repeated structures',
13887
+ };
13888
+ /** What a model must be able to do for a step to run on it. */
13889
+ var AiCapability;
13890
+ (function (AiCapability) {
13891
+ AiCapability["StructuredOutput"] = "structuredOutputs";
13892
+ AiCapability["ImageInput"] = "imageInput";
13893
+ AiCapability["ImageOutput"] = "imageOutput";
13894
+ })(AiCapability || (AiCapability = {}));
13895
+ const AI_STEP_NEEDS = {
13896
+ [AiStep.ReadMessage]: [AiCapability.StructuredOutput, AiCapability.ImageInput],
13897
+ [AiStep.PlanSite]: [AiCapability.StructuredOutput],
13898
+ [AiStep.ChangePlan]: [AiCapability.StructuredOutput],
13899
+ [AiStep.MakePictures]: [AiCapability.ImageOutput],
13900
+ [AiStep.NameStructures]: [AiCapability.StructuredOutput],
13901
+ };
13902
+ /**
13903
+ * The steps a model answers in the plan's parts, one request per part (vectoriox/iox-cms#47 — the whole plan's format
13904
+ * compiles to a grammar Claude refuses), and the tokens a typical request of each part reads and writes. Each part reads
13905
+ * the same instructions; the pages read the structure too and write most of the words.
13906
+ */
13907
+ const AI_STEP_PART_TOKENS = {
13908
+ [AiStep.PlanSite]: {
13909
+ [PlanPart.Structure]: { input: 10_000, output: 1_500, cacheWrite: 0, cacheRead: 0 },
13910
+ [PlanPart.Pages]: { input: 12_000, output: 5_500, cacheWrite: 0, cacheRead: 0 },
13911
+ },
13912
+ [AiStep.ChangePlan]: {
13913
+ [PlanPart.Structure]: { input: 6_000, output: 300, cacheWrite: 0, cacheRead: 0 },
13914
+ [PlanPart.Pages]: { input: 6_500, output: 400, cacheWrite: 0, cacheRead: 0 },
13915
+ },
13916
+ };
13917
+ /** The parts a step is asked in, in order — none for a step asked in one request. */
13918
+ function aiStepParts(step) {
13919
+ return AI_STEP_PART_TOKENS[step] ? PLAN_PARTS : [];
13920
+ }
13921
+ const addTokens = (a, b) => ({
13922
+ input: a.input + b.input, output: a.output + b.output, cacheWrite: a.cacheWrite + b.cacheWrite, cacheRead: a.cacheRead + b.cacheRead,
13923
+ ...(a.imageOutput !== undefined || b.imageOutput !== undefined ? { imageOutput: (a.imageOutput ?? 0) + (b.imageOutput ?? 0) } : {}),
13924
+ });
13925
+ const partsTotal = (step) => PLAN_PARTS.map(part => AI_STEP_PART_TOKENS[step][part]).reduce(addTokens);
13926
+ /**
13927
+ * Tokens a typical use of a step reads and writes (plan §3d "Where credits go") — what a shown estimate is made of. A
13928
+ * step asked in parts is every part's request together, so the estimate (and a generation's start gate) counts them all.
13929
+ */
13930
+ const AI_STEP_TYPICAL_TOKENS = {
13931
+ [AiStep.ReadMessage]: { input: 8_800, output: 1_500, cacheWrite: 0, cacheRead: 0 },
13932
+ [AiStep.PlanSite]: partsTotal(AiStep.PlanSite),
13933
+ [AiStep.ChangePlan]: partsTotal(AiStep.ChangePlan),
13934
+ // a 1 K picture is 1,120 image tokens (Google's figure for Nano Banana 2) — I-D4
13935
+ [AiStep.MakePictures]: { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, imageOutput: 1_120 },
13936
+ [AiStep.NameStructures]: { input: 80_000, output: 10_000, cacheWrite: 0, cacheRead: 0 },
13937
+ };
13938
+ /** 1 credit = 1 US cent of what the AI actually cost (decision D4). */
13939
+ const CREDITS_PER_USD = 100;
13940
+ const TOKENS_PER_PRICE_UNIT = 1_000_000;
13941
+ const CREDIT_PRECISION = 1e6;
13942
+ const NO_TOKENS = Object.freeze({ input: 0, output: 0, cacheWrite: 0, cacheRead: 0 });
13943
+ /** What `tokens` cost at `price`, in US dollars. */
13944
+ function costUsd(tokens, price) {
13945
+ const perToken = (perMillion, fallback) => (perMillion ?? fallback) / TOKENS_PER_PRICE_UNIT;
13946
+ return tokens.input * perToken(price.input, 0)
13947
+ + tokens.output * perToken(price.output, 0)
13948
+ + tokens.cacheWrite * perToken(price.cacheWrite, price.input)
13949
+ + tokens.cacheRead * perToken(price.cacheRead, price.input)
13950
+ + (tokens.imageOutput ?? 0) * perToken(price.imageOutput, price.output);
13951
+ }
13952
+ /** Dollars → credits, kept to a millionth of a credit (a short read costs a fraction of one). */
13953
+ function creditsOfUsd(usd) {
13954
+ return Math.round(usd * CREDITS_PER_USD * CREDIT_PRECISION) / CREDIT_PRECISION;
13955
+ }
13956
+ /** What a typical request of `step` reads and writes — one part's when the step is asked in parts, else the whole step's. */
13957
+ function typicalStepTokens(step, part) {
13958
+ return (part && AI_STEP_PART_TOKENS[step]?.[part]) || AI_STEP_TYPICAL_TOKENS[step];
13959
+ }
13960
+ /** The credits a typical use of `step` costs on a model of `price` — never typed, always computed (§3d). */
13961
+ function estimateStepCredits(step, price, tokens = AI_STEP_TYPICAL_TOKENS[step]) {
13962
+ return creditsOfUsd(costUsd(tokens, price));
13963
+ }
13964
+ /** Can `model` do `step`? The step's needs against the model's capabilities. */
13965
+ function modelCanDo(model, step) {
13966
+ return AI_STEP_NEEDS[step].every(need => model.capabilities.includes(need));
13967
+ }
13968
+ /** Why AI did not run (board 11). The server refuses with these BEFORE any model call. */
13969
+ var AiRefusalCode;
13970
+ (function (AiRefusalCode) {
13971
+ AiRefusalCode["NotInPlan"] = "ai.notInPlan";
13972
+ AiRefusalCode["OutOfCredits"] = "ai.outOfCredits";
13973
+ AiRefusalCode["CapReached"] = "ai.capReached";
13974
+ AiRefusalCode["NotConnected"] = "ai.notConnected";
13975
+ AiRefusalCode["KeyInvalid"] = "ai.keyInvalid";
13976
+ AiRefusalCode["PlanUnknown"] = "ai.planUnknown";
13977
+ })(AiRefusalCode || (AiRefusalCode = {}));
13978
+ /** The HTTP status each refusal answers with (board 11). */
13979
+ const AI_REFUSAL_STATUS = {
13980
+ [AiRefusalCode.NotInPlan]: 403,
13981
+ [AiRefusalCode.OutOfCredits]: 402,
13982
+ [AiRefusalCode.CapReached]: 402,
13983
+ [AiRefusalCode.NotConnected]: 409,
13984
+ [AiRefusalCode.KeyInvalid]: 409,
13985
+ [AiRefusalCode.PlanUnknown]: 503,
13986
+ };
13987
+ /** What a refusal offers next. */
13988
+ var AiNextKind;
13989
+ (function (AiNextKind) {
13990
+ /** Ask IOX (a prefilled email to the operator, until a plan can be bought — D2). */
13991
+ AiNextKind["Contact"] = "contact";
13992
+ /** Open Settings ▸ AI. */
13993
+ AiNextKind["Settings"] = "settings";
13994
+ /** Try again later, or with less. */
13995
+ AiNextKind["Retry"] = "retry";
13996
+ })(AiNextKind || (AiNextKind = {}));
13997
+ const AI_REFUSAL_NEXT = {
13998
+ [AiRefusalCode.NotInPlan]: AiNextKind.Contact,
13999
+ [AiRefusalCode.OutOfCredits]: AiNextKind.Contact,
14000
+ [AiRefusalCode.CapReached]: AiNextKind.Retry,
14001
+ [AiRefusalCode.NotConnected]: AiNextKind.Settings,
14002
+ [AiRefusalCode.KeyInvalid]: AiNextKind.Settings,
14003
+ [AiRefusalCode.PlanUnknown]: AiNextKind.Retry,
14004
+ };
14005
+ /** The plan entitlements AI reads (the control plane's `features` / `limits` keys) — ONE spelling. */
14006
+ var AiFeature;
14007
+ (function (AiFeature) {
14008
+ AiFeature["SiteGeneration"] = "aiSiteGeneration";
14009
+ })(AiFeature || (AiFeature = {}));
14010
+ var AiLimit;
14011
+ (function (AiLimit) {
14012
+ AiLimit["CreditsPerMonth"] = "aiCreditsPerMonth";
14013
+ AiLimit["CreditsPerGeneration"] = "aiCreditsPerGeneration";
14014
+ })(AiLimit || (AiLimit = {}));
14015
+ /** A generation's stages (§3d). The runner works while `task` is set; otherwise the owner has the next move. */
14016
+ var AiGenerationStage;
14017
+ (function (AiGenerationStage) {
14018
+ AiGenerationStage["Reading"] = "reading";
14019
+ AiGenerationStage["Analysis"] = "analysis";
14020
+ AiGenerationStage["Design"] = "design";
14021
+ AiGenerationStage["Plan"] = "plan";
14022
+ AiGenerationStage["Build"] = "build";
14023
+ AiGenerationStage["Done"] = "done";
14024
+ AiGenerationStage["Failed"] = "failed";
14025
+ AiGenerationStage["Cancelled"] = "cancelled";
14026
+ })(AiGenerationStage || (AiGenerationStage = {}));
14027
+ /** The stages nothing advances any more. */
14028
+ const FINAL_GENERATION_STAGES = [AiGenerationStage.Done, AiGenerationStage.Failed, AiGenerationStage.Cancelled];
14029
+ /** How one model call ended — a ledger row's status. */
14030
+ var AiCallStatus;
14031
+ (function (AiCallStatus) {
14032
+ /** Written before the call: a reservation at the estimate until the call settles. */
14033
+ AiCallStatus["Started"] = "started";
14034
+ AiCallStatus["Ok"] = "ok";
14035
+ /** The answer came back and the shared validator refused it (with a path). */
14036
+ AiCallStatus["Invalid"] = "invalid";
14037
+ /** The model declined (a provider refusal) — charged what the provider reports. */
14038
+ AiCallStatus["Refused"] = "refused";
14039
+ AiCallStatus["Error"] = "error";
14040
+ /** The process died during the call: charged at the estimate, since what it cost was never reported. */
14041
+ AiCallStatus["Interrupted"] = "interrupted";
14042
+ })(AiCallStatus || (AiCallStatus = {}));
14043
+
13949
14044
  /**
13950
14045
  * What a structured-output request costs the provider's grammar compiler, counted the way the Claude API counts it
13951
14046
  * (platform.claude.com/docs/en/build-with-claude/structured-outputs, "Schema complexity limits", read 2026-10-27):
@@ -13955,10 +14050,24 @@ function planToWire(plan) {
13955
14050
  * The limits are per REQUEST — every strict schema in it together. The ONE counter: the specs hold every schema iox-cms
13956
14051
  * sends to it, and the fake Claude API refuses with it exactly as Claude does — the plan stage once sent 213 optional
13957
14052
  * parameters and every suite stayed green because the fake accepted anything (vectoriox/iox-cms#45).
14053
+ *
14054
+ * Beyond those, Claude refuses a grammar it compiles too large — a limit the docs name ("additional internal limits on
14055
+ * the compiled grammar size") without a number. MEASURED against the API on 2026-10-27 (vectoriox/iox-cms#47): the
14056
+ * total number of properties decides it, not enums or descriptions — 64 and 61 properties refused, 53 and fewer
14057
+ * accepted (53 already took ~9 s to compile). So 60 is the line we refuse above, and 40 is what we send.
14058
+ */
14059
+ const STRICT_SCHEMA_LIMITS = {
14060
+ strictTools: 20,
14061
+ optional: 24,
14062
+ unions: 16,
14063
+ /** MEASURED, not documented (2026-10-27): the smallest refused request held 61 properties, the largest accepted 53. */
14064
+ properties: 60,
14065
+ };
14066
+ /**
14067
+ * What one request iox-cms sends may use: half of each documented limit, so a step can gain a field (or a strict tool)
14068
+ * without a 400 — and 40 properties, a third under the measured grammar line, where compiling stays a few seconds.
13958
14069
  */
13959
- const STRICT_SCHEMA_LIMITS = { strictTools: 20, optional: 24, unions: 16 };
13960
- /** What a schema iox-cms sends may use — half of each limit, so a step can gain a field (or a strict tool) without a 400. */
13961
- const STRICT_SCHEMA_BUDGET = { optional: 12, unions: 8 };
14070
+ const STRICT_SCHEMA_BUDGET = { optional: 12, unions: 8, properties: 40 };
13962
14071
  /** The keywords the docs list as not supported ("Numerical constraints", "String constraints", "Array constraints beyond `minItems` of 0 or 1"). */
13963
14072
  const UNSUPPORTED_KEYWORDS = [
13964
14073
  'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'multipleOf', 'minLength', 'maxLength', 'maxItems', 'uniqueItems', 'contains',
@@ -13977,6 +14086,9 @@ function count(node, path, out) {
13977
14086
  if (node['type'] === 'object' || properties) {
13978
14087
  const required = new Set(Array.isArray(node['required']) ? node['required'] : []);
13979
14088
  out.optional += Object.keys(properties ?? {}).filter(key => !required.has(key)).length;
14089
+ out.properties += Object.keys(properties ?? {}).length;
14090
+ if (properties)
14091
+ out.objects++;
13980
14092
  if (node['additionalProperties'] !== false)
13981
14093
  out.unsupported.push(`${at('additionalProperties')}: must be false`);
13982
14094
  }
@@ -14005,14 +14117,17 @@ function count(node, path, out) {
14005
14117
  }
14006
14118
  /** The cost of ONE request's strict schemas together: its JSON output format and its strict tools' input schemas. */
14007
14119
  function strictSchemaComplexity(schemas, strictTools = 0) {
14008
- const out = { optional: 0, unions: 0, strictTools, unsupported: [] };
14120
+ const out = { optional: 0, unions: 0, strictTools, properties: 0, objects: 0, unsupported: [] };
14009
14121
  schemas.forEach((schema, i) => count(schema, schemas.length > 1 ? `[${i}]` : '', out));
14010
14122
  return out;
14011
14123
  }
14124
+ /** Claude's words for a grammar it compiles too large (2026-10-27, the plan stage — vectoriox/iox-cms#47), verbatim. */
14125
+ const GRAMMAR_TOO_LARGE_MESSAGE = 'The compiled grammar is too large, which would cause performance issues. Simplify your tool schemas or reduce the number of strict tools.';
14012
14126
  /**
14013
- * The 400 the Claude API answers a request over its limits with, or null. The optional-parameter message is the one
14014
- * Claude answered on 2026-10-27, verbatim; the union, strict-tool and unsupported messages follow its shape (not yet
14015
- * captured).
14127
+ * The 400 the Claude API answers a request over its limits with, or null. The optional-parameter and the grammar-size
14128
+ * messages are the ones Claude answered on 2026-10-27, verbatim; the union, strict-tool and unsupported messages follow
14129
+ * their shape (not yet captured). The documented limits are checked first: a request over one of them never reaches the
14130
+ * grammar compiler.
14016
14131
  */
14017
14132
  function strictSchemaRefusal(complexity) {
14018
14133
  const refuse = (message) => ({ status: 400, type: 'invalid_request_error', message });
@@ -14027,6 +14142,8 @@ function strictSchemaRefusal(complexity) {
14027
14142
  if (complexity.unions > STRICT_SCHEMA_LIMITS.unions) {
14028
14143
  return refuse(`Schemas contains too many parameters with union types (${complexity.unions}), which would make grammar compilation inefficient. Reduce the number of parameters with union types in your tool schemas (limit: ${STRICT_SCHEMA_LIMITS.unions}).`);
14029
14144
  }
14145
+ if (complexity.properties > STRICT_SCHEMA_LIMITS.properties)
14146
+ return refuse(GRAMMAR_TOO_LARGE_MESSAGE);
14030
14147
  return null;
14031
14148
  }
14032
14149
 
@@ -14070,9 +14187,32 @@ const PLAN_EDIT_SHAPE = {
14070
14187
  [PlanEditOp.SwapPattern]: { op: "'swapPattern'", page: 'string — a page key', index: 'integer — the section', pattern: 'string — the pattern it becomes (its slots kept where the new one has them)' },
14071
14188
  [PlanEditOp.SetSlot]: { op: "'setSlot'", page: 'string — a page key', index: 'integer — the section', slot: "string — one of the pattern's slots", text: "string — the words, in the owner's language and voice" },
14072
14189
  [PlanEditOp.AddContentType]: { op: "'addContentType'", contentType: 'contentType — a unique name', 'dataSources?': 'dataSource[] — the list / single sources its pages read' },
14073
- [PlanEditOp.AddField]: { op: "'addField'", contentType: 'string — a content type of the plan', field: 'field', 'sample?': "string | string[] — the samples' value for it" },
14190
+ [PlanEditOp.AddField]: { op: "'addField'", contentType: 'string — a content type of the plan', field: 'field', 'sample?': "string | string[] — the samples' value for it; absent: what the type's seed already holds for it, else ''" },
14074
14191
  [PlanEditOp.SetSectionData]: { op: "'setSectionData'", page: 'string — a page key', index: 'integer — the section', binding: 'string — the list source the section repeats' },
14075
14192
  };
14193
+ /**
14194
+ * The plan part each op changes (vectoriox/iox-cms#47): a sentence is answered in the plan's two parts, one request each —
14195
+ * the structure's edits first, then the pages' written knowing them.
14196
+ */
14197
+ const PLAN_EDIT_OP_PART = {
14198
+ [PlanEditOp.AddPage]: PlanPart.Pages,
14199
+ [PlanEditOp.RemovePage]: PlanPart.Pages,
14200
+ [PlanEditOp.RenamePage]: PlanPart.Pages,
14201
+ [PlanEditOp.MergePages]: PlanPart.Pages,
14202
+ [PlanEditOp.MovePage]: PlanPart.Pages,
14203
+ [PlanEditOp.AddSection]: PlanPart.Pages,
14204
+ [PlanEditOp.RemoveSection]: PlanPart.Pages,
14205
+ [PlanEditOp.MoveSection]: PlanPart.Pages,
14206
+ [PlanEditOp.SwapPattern]: PlanPart.Pages,
14207
+ [PlanEditOp.SetSlot]: PlanPart.Pages,
14208
+ [PlanEditOp.SetSectionData]: PlanPart.Pages,
14209
+ [PlanEditOp.AddContentType]: PlanPart.Structure,
14210
+ [PlanEditOp.AddField]: PlanPart.Structure,
14211
+ };
14212
+ /** The ops written in one part, in `PlanEditOp` order. */
14213
+ function planEditOps(part) {
14214
+ return Object.values(PlanEditOp).filter(op => PLAN_EDIT_OP_PART[op] === part);
14215
+ }
14076
14216
  var PlanChangeKind;
14077
14217
  (function (PlanChangeKind) {
14078
14218
  PlanChangeKind["Page"] = "page";
@@ -14193,6 +14333,8 @@ function findSection(d, pageAt, index, c, path) {
14193
14333
  return ok;
14194
14334
  }
14195
14335
  const setSections = (d, at, sections) => { d.pages[at] = { ...d.pages[at], sections }; };
14336
+ /** A child added to a page or type the same edits added is part of that addition — the map marks the parent only. */
14337
+ const addedHere = (changed, kind, at) => changed.some(ch => ch.kind === kind && ch.action === PlanChangeAction.Added && (kind === PlanChangeKind.Page ? ch.page === at.page : ch.name === at.name));
14196
14338
  /** One handler per op — a data table, never an if-chain. */
14197
14339
  const APPLY = {
14198
14340
  [PlanEditOp.AddPage]: (d, e, c, path, changed) => {
@@ -14257,7 +14399,8 @@ const APPLY = {
14257
14399
  const index = e.at === undefined ? sections.length : clamp(e.at, sections.length);
14258
14400
  sections.splice(index, 0, e.section);
14259
14401
  setSections(d, at, sections);
14260
- changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Added, page: d.pages[at].key, index });
14402
+ if (!addedHere(changed, PlanChangeKind.Page, { page: d.pages[at].key }))
14403
+ changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Added, page: d.pages[at].key, index });
14261
14404
  },
14262
14405
  [PlanEditOp.RemoveSection]: (d, e, c, path, changed) => {
14263
14406
  const at = findPage(d, e.page, c, `${path}.page`);
@@ -14329,9 +14472,10 @@ const APPLY = {
14329
14472
  const type = d.contentTypes[at];
14330
14473
  if (type.fields.some(f => f.name === e.field.name))
14331
14474
  return c.add(PlanEditIssueCode.Duplicate, `${path}.field.name`, `"${type.name}" already has a field "${e.field.name}"`);
14332
- const seed = type.seed ? { ...type.seed, [e.field.name]: e.sample ?? '' } : undefined;
14475
+ const seed = type.seed ? { ...type.seed, [e.field.name]: e.sample ?? type.seed[e.field.name] ?? '' } : undefined;
14333
14476
  d.contentTypes[at] = { ...type, fields: [...type.fields, e.field], ...(seed ? { seed } : {}) };
14334
- changed.push({ kind: PlanChangeKind.Field, action: PlanChangeAction.Added, name: `${type.name}.${e.field.name}` });
14477
+ if (!addedHere(changed, PlanChangeKind.ContentType, { name: type.name }))
14478
+ changed.push({ kind: PlanChangeKind.Field, action: PlanChangeAction.Added, name: `${type.name}.${e.field.name}` });
14335
14479
  },
14336
14480
  [PlanEditOp.SetSectionData]: (d, e, c, path, changed) => {
14337
14481
  const at = findPage(d, e.page, c, `${path}.page`);
@@ -14370,20 +14514,34 @@ function applyPlanEdits(plan, edits) {
14370
14514
  return { plan: { ...plan, pages: draft.pages, contentTypes: draft.contentTypes, dataSources }, changed, issues: [] };
14371
14515
  }
14372
14516
  // ── The format a model writes edits in ─────────────────────────────────────────────────────────────────
14373
- const PLAN_EDITS_SCHEMA_NAME = 'plan_edits';
14517
+ /** Each part's answer format name. */
14518
+ const PLAN_EDITS_PART_SCHEMA_NAMES = {
14519
+ [PlanPart.Structure]: 'plan_structure_edits',
14520
+ [PlanPart.Pages]: 'plan_page_edits',
14521
+ };
14374
14522
  /** A sentence's answer: the edits, or why the ops cannot say it (one line, in the owner's language). */
14375
14523
  var PlanEditsOutcome;
14376
14524
  (function (PlanEditsOutcome) {
14377
14525
  PlanEditsOutcome["Edits"] = "edits";
14378
14526
  PlanEditsOutcome["Cannot"] = "cannot";
14379
14527
  })(PlanEditsOutcome || (PlanEditsOutcome = {}));
14380
- /** The answer's own keys, explained. */
14381
- const PLAN_EDITS_ANSWER_SHAPE = {
14382
- outcome: "'edits' | 'cannot' — cannot: the operations cannot do what the sentence asks",
14383
- edits: 'edit[] — applied in order, all or none; empty when cannot',
14384
- reason: "string — when cannot, ONE short line for the owner in their language (what to do instead); else ''",
14528
+ /**
14529
+ * Each part's answer, explained. The structure is asked first, so it says whether the sentence is a plan change at all
14530
+ * (`outcome`); the pages are asked only when it is, and write only their edits.
14531
+ */
14532
+ const PLAN_EDITS_ANSWER_SHAPES = {
14533
+ [PlanPart.Structure]: {
14534
+ outcome: "'edits' | 'cannot' — cannot: no operation of either part (below) can do what the sentence asks",
14535
+ edits: 'edit[] — the content types and fields the sentence adds, in order; empty when it adds none, or cannot',
14536
+ reason: "string — when cannot, ONE short line for the owner in their language (what to do instead); else ''",
14537
+ },
14538
+ [PlanPart.Pages]: {
14539
+ edits: 'edit[] — the page and section edits, in order, made after the structure edits (already applied); empty when none',
14540
+ },
14385
14541
  };
14386
14542
  const NO_INDEX = -1;
14543
+ /** A new page or content type on the wire: its children are edits of their own. */
14544
+ const CHILDLESS = { children: false };
14387
14545
  /** A list holding exactly one object: none → absent, one → it (expanded), more → the list (the gate refuses it). */
14388
14546
  const oneOf = (schema, expand, toWire, what) => ({
14389
14547
  text: `${what}[] — exactly one when the op takes it`,
@@ -14401,17 +14559,22 @@ const EDIT_WIRE = {
14401
14559
  after: textKey('addPage: the key of the page it follows in the menu'),
14402
14560
  into: textKey('mergePages: the page that stays'),
14403
14561
  from: textKey('mergePages: the page whose sections move into it, then removed'),
14404
- index: indexKey('the section, 0-based on its page'),
14562
+ index: indexKey('the section, 0-based on its page (addSection: the place the new one goes)'),
14405
14563
  to: indexKey('movePage / moveSection: the new 0-based place'),
14406
- at: indexKey('addSection: its 0-based place on the page'),
14407
14564
  pattern: { text: 'string — swapPattern: the section pattern it becomes', none: '', schema: () => jsonOneOf(['', ...sectionPatternIds()]) },
14408
14565
  slot: textKey("setSlot: one of the pattern's slots"),
14409
14566
  text: textKey("setSlot: the words, in the owner's language and voice"),
14410
14567
  binding: textKey('setSectionData: the list source the section repeats'),
14411
14568
  contentType: textKey('addField: a content type of the plan'),
14412
- newPage: oneOf(wirePageJsonSchema, expandWirePage, pageToWire, 'page'),
14569
+ newPage: {
14570
+ ...oneOf(() => wirePageJsonSchema(CHILDLESS), v => expandWirePage(v, CHILDLESS), p => pageToWire(p, CHILDLESS), 'page'),
14571
+ text: 'page[] — exactly one when the op takes it, WITHOUT sections: each is an addSection after it',
14572
+ },
14413
14573
  newSection: oneOf(wireSectionJsonSchema, expandWireSection, sectionToWire, 'section'),
14414
- newContentType: oneOf(wireContentTypeJsonSchema, expandWireContentType, contentTypeToWire, 'contentType'),
14574
+ newContentType: {
14575
+ ...oneOf(() => wireContentTypeJsonSchema(CHILDLESS), v => expandWireContentType(v, CHILDLESS), t => contentTypeToWire(t, CHILDLESS), 'contentType'),
14576
+ text: "contentType[] — exactly one when the op takes it, WITHOUT fields: each is an addField after it (the samples' values stay in its seed)",
14577
+ },
14415
14578
  dataSources: {
14416
14579
  text: 'dataSource[] — addContentType: the list / single sources its pages read', none: [],
14417
14580
  schema: () => jsonListOf(wireDataSourceJsonSchema()),
@@ -14428,7 +14591,7 @@ const PLAN_EDIT_WIRE_KEYS = {
14428
14591
  [PlanEditOp.RenamePage]: { page: 'page', name: 'name', route: 'route' },
14429
14592
  [PlanEditOp.MergePages]: { into: 'into', from: 'from' },
14430
14593
  [PlanEditOp.MovePage]: { page: 'page', to: 'to' },
14431
- [PlanEditOp.AddSection]: { page: 'page', section: 'newSection', at: 'at' },
14594
+ [PlanEditOp.AddSection]: { page: 'page', section: 'newSection', at: 'index' },
14432
14595
  [PlanEditOp.RemoveSection]: { page: 'page', index: 'index' },
14433
14596
  [PlanEditOp.MoveSection]: { page: 'page', from: 'index', to: 'to' },
14434
14597
  [PlanEditOp.SwapPattern]: { page: 'page', index: 'index', pattern: 'pattern' },
@@ -14437,19 +14600,27 @@ const PLAN_EDIT_WIRE_KEYS = {
14437
14600
  [PlanEditOp.AddField]: { contentType: 'contentType', field: 'newField', sample: 'sample' },
14438
14601
  [PlanEditOp.SetSectionData]: { page: 'page', index: 'index', binding: 'binding' },
14439
14602
  };
14603
+ /** The wire keys a part's edit carries: every key its ops read, in `EDIT_WIRE` order. */
14604
+ function planEditWireKeys(part) {
14605
+ const read = new Set(planEditOps(part).flatMap(op => Object.values(PLAN_EDIT_WIRE_KEYS[op])));
14606
+ return Object.keys(EDIT_WIRE).filter(k => read.has(k));
14607
+ }
14440
14608
  const isEditWireNone = (wireKey, v) => {
14441
14609
  const none = EDIT_WIRE[wireKey].none;
14442
14610
  return v === undefined || (Array.isArray(none) ? Array.isArray(v) && v.length === 0 : v === none) || (wireKey === 'sample' && Array.isArray(v) && v.length === 0);
14443
14611
  };
14444
14612
  const noneText = (none) => (Array.isArray(none) ? '[]' : typeof none === 'string' ? "''" : String(none));
14445
- /** The flat edit's shape table: every key required, each with its none. */
14446
- const PLAN_EDIT_WIRE_SHAPE = {
14447
- op: `${OPS.map(op => `'${op}'`).join(' | ')} — which keys below are read`,
14448
- ...Object.fromEntries(Object.entries(EDIT_WIRE).map(([k, spec]) => [k, `${spec.text} — ${noneText(spec.none)} when the op does not read it`])),
14449
- };
14450
- /** Per op, the wire keys it reads, in `PLAN_EDIT_SHAPE`'s words (`?` = may hold its none) — what the model reads. */
14451
- function planEditWireOps() {
14452
- return Object.fromEntries(OPS.map(op => {
14613
+ const opsText = (part) => planEditOps(part).map(op => `'${op}'`).join(' | ');
14614
+ /** A part's flat edit, as a shape table: every key required, each with its none. */
14615
+ function planEditWireShape(part) {
14616
+ return {
14617
+ op: `${opsText(part)} — which keys below are read`,
14618
+ ...Object.fromEntries(planEditWireKeys(part).map(k => [k, `${EDIT_WIRE[k].text} — ${noneText(EDIT_WIRE[k].none)} when the op does not read it`])),
14619
+ };
14620
+ }
14621
+ /** Per op of a part, the wire keys it reads, in `PLAN_EDIT_SHAPE`'s words (`?` = may hold its none) — what the model reads. */
14622
+ function planEditWireOps(part) {
14623
+ return Object.fromEntries(planEditOps(part).map(op => {
14453
14624
  const entry = PLAN_EDIT_SHAPE[op];
14454
14625
  const keys = PLAN_EDIT_WIRE_KEYS[op];
14455
14626
  return [op, Object.fromEntries(Object.entries(keys).map(([planKey, wireKey]) => {
@@ -14477,20 +14648,21 @@ function planEditWireGaps() {
14477
14648
  }
14478
14649
  return gaps;
14479
14650
  }
14480
- /** The format a sentence's answer is written in: the outcome, the flat edits, the reason. */
14481
- function planEditsJsonSchema() {
14651
+ const ANSWER_SCHEMAS = {
14652
+ outcome: () => jsonOneOf(Object.values(PlanEditsOutcome)),
14653
+ reason: () => jsonText(),
14654
+ edits: part => jsonListOf(jsonShapedObject(planEditWireShape(part), {
14655
+ op: jsonOneOf(planEditOps(part)),
14656
+ ...Object.fromEntries(planEditWireKeys(part).map(k => [k, EDIT_WIRE[k].schema()])),
14657
+ }, `edit ${part}`)),
14658
+ };
14659
+ /** The format one part of a sentence's answer is written in — one request each (vectoriox/iox-cms#47). */
14660
+ function planEditsJsonSchema(part) {
14482
14661
  const gaps = planEditWireGaps();
14483
14662
  if (gaps.length)
14484
14663
  throw new PlanWireShapeError(`the plan edits wire does not carry ${gaps.join(', ')}`);
14485
- const edit = jsonShapedObject(PLAN_EDIT_WIRE_SHAPE, {
14486
- op: jsonOneOf(OPS),
14487
- ...Object.fromEntries(Object.entries(EDIT_WIRE).map(([k, spec]) => [k, spec.schema()])),
14488
- }, 'edit');
14489
- return jsonShapedObject(PLAN_EDITS_ANSWER_SHAPE, {
14490
- outcome: jsonOneOf(Object.values(PlanEditsOutcome)),
14491
- edits: jsonListOf(edit),
14492
- reason: jsonText(),
14493
- }, 'planEdits');
14664
+ const shape = PLAN_EDITS_ANSWER_SHAPES[part];
14665
+ return jsonShapedObject(shape, Object.fromEntries(shapeKeys(shape).map(k => [k, ANSWER_SCHEMAS[k](part)])), `planEdits ${part}`);
14494
14666
  }
14495
14667
  /** One flat edit → the op's edit: the keys it reads, a none left out where the op may go without it. Never repaired. */
14496
14668
  function expandWireEdit(wire) {
@@ -14511,9 +14683,10 @@ function expandWireEdit(wire) {
14511
14683
  }
14512
14684
  return out;
14513
14685
  }
14514
- /** An edit → the flat wire edit (what a model would write — fakes and specs write it). */
14515
- function planEditToWire(edit) {
14516
- const out = { op: edit.op, ...Object.fromEntries(Object.entries(EDIT_WIRE).map(([k, spec]) => [k, Array.isArray(spec.none) ? [] : spec.none])) };
14686
+ /** An edit's own keys → its flat wire edit, in its part's keys. */
14687
+ function flatEdit(edit) {
14688
+ const part = PLAN_EDIT_OP_PART[edit.op];
14689
+ const out = { op: edit.op, ...Object.fromEntries(planEditWireKeys(part).map(k => [k, Array.isArray(EDIT_WIRE[k].none) ? [] : EDIT_WIRE[k].none])) };
14517
14690
  for (const [planKey, wireKey] of Object.entries(PLAN_EDIT_WIRE_KEYS[edit.op])) {
14518
14691
  const value = edit[planKey];
14519
14692
  if (value === undefined)
@@ -14522,7 +14695,26 @@ function planEditToWire(edit) {
14522
14695
  }
14523
14696
  return out;
14524
14697
  }
14525
- /** A model's answer → edits (each flat edit expanded); never repaired. */
14698
+ /** The children an added page or content type travels without, as the edits that add them after it. */
14699
+ function childEdits(edit) {
14700
+ if (edit.op === PlanEditOp.AddPage)
14701
+ return edit.page.sections.map(section => ({ op: PlanEditOp.AddSection, page: edit.page.key, section }));
14702
+ if (edit.op === PlanEditOp.AddContentType)
14703
+ return edit.contentType.fields.map(field => ({ op: PlanEditOp.AddField, contentType: edit.contentType.name, field }));
14704
+ return [];
14705
+ }
14706
+ /** An edit → the flat wire edits a model would write for it: itself, then its children (fakes and specs write them). */
14707
+ function planEditToWire(edit) {
14708
+ return [flatEdit(edit), ...childEdits(edit).map(flatEdit)];
14709
+ }
14710
+ /** Edits → each part's flat wire edits, in order (what each part's answer would hold). */
14711
+ function planEditsToWire(edits) {
14712
+ const out = Object.fromEntries(PLAN_PARTS.map(part => [part, []]));
14713
+ for (const edit of edits)
14714
+ out[PLAN_EDIT_OP_PART[edit.op]].push(...planEditToWire(edit));
14715
+ return out;
14716
+ }
14717
+ /** A model's answer (either part's) → edits (each flat edit expanded); never repaired. */
14526
14718
  function planEditsFromWire(answer) {
14527
14719
  if (!isPlainObject(answer) || !Array.isArray(answer['edits']))
14528
14720
  return answer;
@@ -15105,5 +15297,5 @@ function addStockCredits(footer, credits) {
15105
15297
  * Generated bundle index. Do not edit.
15106
15298
  */
15107
15299
 
15108
- export { ACTIVE_BUILD_STATUSES, AI_REFUSAL_NEXT, AI_REFUSAL_STATUS, AI_STEPS, AI_STEP_LABELS, AI_STEP_NEEDS, AI_STEP_TYPICAL_TOKENS, AiCallStatus, AiCapability, AiFeature, AiGenerationStage, AiKeyMode, AiLimit, AiNextKind, AiProviderId, AiRefusalCode, AiStep, BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BUILD_STEPS, BaselineRole, BindScope, BuildNoticeCode, BuildStep, CONTENT_ROLE_MEANINGS, CREDITS_PER_USD, CardBindTarget, CheckSeverity, ComposeError, ComposeIssueCode, ContentRole, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, DEFAULT_SITE_LOCALE, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, EntranceSide, FACT_KEYS, FINAL_GENERATION_STAGES, FINDING_SEVERITY, GlobalSlot, HEBREW_PREFIXES, JSON_SCHEMA_BANNED_KEYWORDS, JsonSchemaShapeError, KeptReason, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NO_TOKENS, NavPagesSource, NavRole, NavShape, NavShapeError, PAGE_ARCHETYPES, PLAN_EDITS_ANSWER_SHAPE, PLAN_EDITS_SCHEMA_NAME, PLAN_EDIT_SHAPE, PLAN_EDIT_WIRE_KEYS, PLAN_EDIT_WIRE_SHAPE, PLAN_WIRE_DERIVED, PLAN_WIRE_SCHEMA_NAME, PLAN_WIRE_SHAPE, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanChangeAction, PlanChangeKind, PlanDataSourceMode, PlanDataSourceType, PlanEditIssueCode, PlanEditOp, PlanEditsOutcome, PlanWireEntry, PlanWireShapeError, PlanWordIssueCode, PreflightRefusalCode, PublishRefusalCode, REQUIRED_CONTENT_ROLES, RelativeOp, SAMPLE_IMAGE_REF, SITE_CHECKS, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_LIBRARY, SITE_LOCALES, SITE_NEED_IDS, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_ARCHETYPES, SPEC_SITE_COPY_LENGTHS, SPEC_SITE_DENSITY_COPY, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_LOCALES, SPEC_SITE_NEED_IDS, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_REQUIRED_ROLES, SPEC_SITE_ROLES, SPEC_SITE_TEMPLATES, STOCK_FAMILIES, STOCK_PROVIDER_TERMS, STRICT_SCHEMA_BUDGET, STRICT_SCHEMA_LIMITS, STUDIO_TEMPLATE, SiteBuildKind, SiteBuildStatus, SiteCheckId, SiteFindingCode, SiteLocale, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, StepState, StockProviderId, addBarItem, addStockCredits, applyPlanEdits, applyStockPhotos, archetypeIssues, assertStyleSlots, assertValidSitePlan, auditSite, barIconForRoute, blocksPublish, buildKeys, buildStepFacts, builtHomePageId, composeNavHeader, composeSection, composeSite, contentTypePreviewFields, contentTypeToWire, copyLengthOf, costUsd, createSerializedNode, creditsOfUsd, dataSourceToWire, describePlanData, describeShapeKey, entranceAction, entranceInteraction, estimateStepCredits, expandPlanWire, expandWireContentType, expandWireDataSource, expandWireEdit, expandWireField, expandWirePage, expandWireSection, failedBuildStep, fieldToWire, findNavRoot, isActiveBuild, isNonEmptyString, isPlainObject, isRtlLocale, jsonAnyOf, jsonBoolean, jsonClosedObject, jsonInteger, jsonListOf, jsonOneOf, jsonSchemaKeywords, jsonSchemaObjects, jsonShapedObject, jsonText, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, pageToWire, parsePatternPath, parseSampleImageRef, pathAt, patternById, patternHasForm, patternLinkSlots, patternManifest, patternNeedsBinding, patternNodes, patternOptionalParts, patternRoleNeeds, patternScopes, patternSlotBudgets, patternSlots, patternTakesBinding, phoneMenuPages, planEditToWire, planEditWireGaps, planEditWireOps, planEditsFromWire, planEditsJsonSchema, planEntrances, planFromTemplate, planNavShape, planStockPhotos, planToWire, planWireGaps, planWireJsonSchema, planWordIssues, publishRefusal, publishVerdict, releasedLayoutDefaults, removeBarItem, requiredAcknowledgements, requiredShapeKeys, roleField, routeMatches, routeParams, sampleImageRef, sectionEntrances, sectionPatternIds, sectionToWire, setStyleProps, shapeKeys, showcaseGlobals, showcasePages, showcasePlans, siteNavPages, slotBudget, stockCopyKey, stockCreditLine, stockCreditLines, stockCreditNode, stockCreditNodeId, stockSlots, strictSchemaComplexity, strictSchemaRefusal, validatePlanEdits, validateSitePlan, walkSerialized, wireContentTypeJsonSchema, wireDataSourceJsonSchema, wireFieldJsonSchema, wirePageJsonSchema, wireSectionJsonSchema, wordPattern, writtenTexts };
15300
+ export { ACTIVE_BUILD_STATUSES, AI_REFUSAL_NEXT, AI_REFUSAL_STATUS, AI_STEPS, AI_STEP_LABELS, AI_STEP_NEEDS, AI_STEP_PART_TOKENS, AI_STEP_TYPICAL_TOKENS, AiCallStatus, AiCapability, AiFeature, AiGenerationStage, AiKeyMode, AiLimit, AiNextKind, AiProviderId, AiRefusalCode, AiStep, BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BUILD_STEPS, BaselineRole, BindScope, BuildNoticeCode, BuildStep, CONTENT_ROLE_MEANINGS, CREDITS_PER_USD, CardBindTarget, CheckSeverity, ComposeError, ComposeIssueCode, ContentRole, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, DEFAULT_SITE_LOCALE, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, EntranceSide, FACT_KEYS, FINAL_GENERATION_STAGES, FINDING_SEVERITY, GRAMMAR_TOO_LARGE_MESSAGE, GlobalSlot, HEBREW_PREFIXES, JSON_SCHEMA_BANNED_KEYWORDS, JsonSchemaShapeError, KeptReason, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NO_TOKENS, NavPagesSource, NavRole, NavShape, NavShapeError, PAGE_ARCHETYPES, PLAN_EDITS_ANSWER_SHAPES, PLAN_EDITS_PART_SCHEMA_NAMES, PLAN_EDIT_OP_PART, PLAN_EDIT_SHAPE, PLAN_EDIT_WIRE_KEYS, PLAN_PARTS, PLAN_PART_KEYS, PLAN_WIRE_DERIVED, PLAN_WIRE_PART_SCHEMA_NAMES, PLAN_WIRE_SHAPE, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanChangeAction, PlanChangeKind, PlanDataSourceMode, PlanDataSourceType, PlanEditIssueCode, PlanEditOp, PlanEditsOutcome, PlanPart, PlanWireEntry, PlanWireShapeError, PlanWordIssueCode, PreflightRefusalCode, PublishRefusalCode, REQUIRED_CONTENT_ROLES, RelativeOp, SAMPLE_IMAGE_REF, SITE_CHECKS, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_LIBRARY, SITE_LOCALES, SITE_NEED_IDS, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_ARCHETYPES, SPEC_SITE_COPY_LENGTHS, SPEC_SITE_DENSITY_COPY, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_LOCALES, SPEC_SITE_NEED_IDS, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_REQUIRED_ROLES, SPEC_SITE_ROLES, SPEC_SITE_TEMPLATES, STOCK_FAMILIES, STOCK_PROVIDER_TERMS, STRICT_SCHEMA_BUDGET, STRICT_SCHEMA_LIMITS, STUDIO_TEMPLATE, SiteBuildKind, SiteBuildStatus, SiteCheckId, SiteFindingCode, SiteLocale, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, StepState, StockProviderId, addBarItem, addStockCredits, aiStepParts, applyPlanEdits, applyStockPhotos, archetypeIssues, assertStyleSlots, assertValidSitePlan, auditSite, barIconForRoute, blocksPublish, buildKeys, buildStepFacts, builtHomePageId, composeNavHeader, composeSection, composeSite, contentTypePreviewFields, contentTypeToWire, copyLengthOf, costUsd, createSerializedNode, creditsOfUsd, dataSourceToWire, describePlanData, describeShapeKey, entranceAction, entranceInteraction, estimateStepCredits, expandPlanWire, expandWireContentType, expandWireDataSource, expandWireEdit, expandWireField, expandWirePage, expandWireSection, failedBuildStep, fieldToWire, findNavRoot, isActiveBuild, isNonEmptyString, isPlainObject, isRtlLocale, jsonAnyOf, jsonBoolean, jsonClosedObject, jsonInteger, jsonListOf, jsonOneOf, jsonSchemaKeywords, jsonSchemaObjects, jsonShapedObject, jsonText, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, pageToWire, parsePatternPath, parseSampleImageRef, pathAt, patternById, patternHasForm, patternLinkSlots, patternManifest, patternNeedsBinding, patternNodes, patternOptionalParts, patternRoleNeeds, patternScopes, patternSlotBudgets, patternSlots, patternTakesBinding, phoneMenuPages, planEditOps, planEditToWire, planEditWireGaps, planEditWireKeys, planEditWireOps, planEditWireShape, planEditsFromWire, planEditsJsonSchema, planEditsToWire, planEntrances, planFromTemplate, planNavShape, planPartOfPath, planPartToWire, planStockPhotos, planToWire, planWireGaps, planWirePartJsonSchema, planWirePartShape, planWordIssues, publishRefusal, publishVerdict, releasedLayoutDefaults, removeBarItem, requiredAcknowledgements, requiredShapeKeys, roleField, routeMatches, routeParams, sampleImageRef, sectionEntrances, sectionPatternIds, sectionToWire, setStyleProps, shapeKeys, showcaseGlobals, showcasePages, showcasePlans, siteNavPages, slotBudget, stockCopyKey, stockCreditLine, stockCreditLines, stockCreditNode, stockCreditNodeId, stockSlots, strictSchemaComplexity, strictSchemaRefusal, typicalStepTokens, validatePlanEdits, validateSitePlan, walkSerialized, wireContentTypeJsonSchema, wireDataSourceJsonSchema, wireFieldJsonSchema, wirePageJsonSchema, wireSectionJsonSchema, wordPattern, writtenTexts };
15109
15301
  //# sourceMappingURL=vectoriox-iox-ui-generation-core.mjs.map