@vectoriox/iox-ui 5.61.0 → 5.62.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.
@@ -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,11 @@ 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 { roles, fields, seed, ...rest } = options.children === false ? { fields: [], ...wire } : wire;
13714
+ const out = { ...rest, roles: listToMap(roles, 'role', 'field'), fields: each(fields, expandWireField) };
13828
13715
  if (!isPlainObject(seed))
13829
13716
  return seed === undefined ? out : { ...out, seed };
13830
13717
  const { values, ...fixed } = seed;
@@ -13859,10 +13746,10 @@ function expandWireSection(wire) {
13859
13746
  ...optional('enter', enter),
13860
13747
  };
13861
13748
  }
13862
- function expandWirePage(wire) {
13749
+ function expandWirePage(wire, options = {}) {
13863
13750
  if (!isPlainObject(wire))
13864
13751
  return wire;
13865
- const { archetype, need, template, routeSource, seo, sections, ...rest } = wire;
13752
+ const { archetype, need, template, routeSource, seo, sections, ...rest } = options.children === false ? { sections: [], ...wire } : wire;
13866
13753
  const words = isPlainObject(seo) ? { ...optional('title', seo['title']), ...optional('description', seo['description']) } : seo;
13867
13754
  return {
13868
13755
  ...rest,
@@ -13882,12 +13769,17 @@ function expandWireBusiness(wire, facts) {
13882
13769
  return { ...rest, ...optional('tagline', tagline), ...(facts && Object.keys(facts).length ? { facts: { ...facts } } : {}) };
13883
13770
  }
13884
13771
  /**
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`.
13772
+ * A model's plan (its parts, on the wire) + what the generation decides (the look, the run, the language, the owner's
13773
+ * facts) → the Site Plan. Pure. Translates, never repairs: whatever an answer got wrong is still wrong, for
13774
+ * `validateSitePlan` — a part that is not an object is the result itself, a part not written yet leaves its keys out
13775
+ * (the validator then names them, at their own paths).
13887
13776
  */
13888
- function expandPlanWire(wire, context) {
13889
- if (!isPlainObject(wire))
13890
- return wire;
13777
+ function expandPlanWire(parts, context) {
13778
+ const written = PLAN_PARTS.filter(part => parts[part] !== undefined).map(part => parts[part]);
13779
+ const odd = written.find(value => !isPlainObject(value));
13780
+ if (odd !== undefined)
13781
+ return odd;
13782
+ const wire = Object.assign({}, ...written);
13891
13783
  const { business, contentTypes, dataSources, pages, ...rest } = wire;
13892
13784
  return {
13893
13785
  ...rest,
@@ -13905,11 +13797,12 @@ function expandPlanWire(wire, context) {
13905
13797
  function fieldToWire(field) {
13906
13798
  return { name: field.name, type: field.type, controller: field.controller, required: field.required ?? false, note: field.note ?? NONE };
13907
13799
  }
13908
- function contentTypeToWire(type) {
13909
- const roles = Object.fromEntries(shapeKeys(PLAN_WIRE_SHAPE.roles).map(role => [role, type.roles[role] ?? NONE]));
13800
+ function contentTypeToWire(type, options = {}) {
13801
+ const played = type.roles;
13802
+ const roles = ROLE_KEYS.filter(role => played[role]).map(role => ({ role, field: played[role] }));
13910
13803
  const { count = 0, sample = false, isPublished = false, ...values } = type.seed ?? {};
13911
13804
  return {
13912
- name: type.name, label: type.label, roles, fields: type.fields.map(fieldToWire),
13805
+ name: type.name, label: type.label, roles, ...(options.children === false ? {} : { fields: type.fields.map(fieldToWire) }),
13913
13806
  seed: { count, sample, isPublished, values: mapToList(values, 'field', 'value') },
13914
13807
  };
13915
13808
  }
@@ -13928,24 +13821,224 @@ function sectionToWire(section) {
13928
13821
  binding: section.binding ?? NONE, without: [...(section.without ?? [])], form: section.form ?? NONE, enter: section.enter ?? NONE,
13929
13822
  };
13930
13823
  }
13931
- function pageToWire(page) {
13824
+ function pageToWire(page, options = {}) {
13932
13825
  return {
13933
13826
  key: page.key, name: page.name, route: page.route, inNav: page.inNav ?? false, archetype: page.archetype ?? NONE, need: page.need ?? NONE,
13934
13827
  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),
13828
+ seo: { title: page.seo?.title ?? NONE, description: page.seo?.description ?? NONE },
13829
+ ...(options.children === false ? {} : { sections: page.sections.map(sectionToWire) }),
13936
13830
  };
13937
13831
  }
13938
- /** The plan → the wire: what a model would have written (the plan edits' prompt shows it; fakes and evals write it). */
13832
+ const PLAN_TO_WIRE = {
13833
+ business: plan => ({ name: plan.business.name, tagline: plan.business.tagline ?? NONE }),
13834
+ contentTypes: plan => plan.contentTypes.map(type => contentTypeToWire(type)),
13835
+ dataSources: plan => plan.dataSources.map(dataSourceToWire),
13836
+ pages: plan => plan.pages.map(page => pageToWire(page)),
13837
+ globals: plan => ({ ...plan.globals }),
13838
+ };
13839
+ /** One part of the plan on the wire: what that part's request would have answered. */
13840
+ function planPartToWire(plan, part) {
13841
+ return Object.fromEntries(PLAN_PART_KEYS[part].map(k => [k, PLAN_TO_WIRE[k](plan)]));
13842
+ }
13843
+ /** The plan → the wire, by part: what a model would have written (the change prompt shows it; fakes and evals write it). */
13939
13844
  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
- };
13845
+ return Object.fromEntries(PLAN_PARTS.map(part => [part, planPartToWire(plan, part)]));
13947
13846
  }
13948
13847
 
13848
+ /**
13849
+ * The AI pipeline's WIRE vocabulary (AI plan §3c, §3n, M0): what iox-cms's `ai` API answers and what every client
13850
+ * of it reads — the steps a model works on, who pays (IOX credits or the studio's own key), a model of the vetted
13851
+ * list with its price per token class, the one cost → credits rule (1 credit = 1 US cent of what the AI cost, D4),
13852
+ * the refusals (board 11) and a generation's stages. ONE definition: iox-cms meters and refuses with it, the
13853
+ * Settings ▸ AI screen and the wizard show it. Pure types, enums and helpers; no transport, no prices typed here.
13854
+ * The step and capability spellings are the control plane's (`GET /instances/me/entitlements`, AI plan "M0 control
13855
+ * plane — the instance-route contract") — the platform has ONE spelling of each.
13856
+ */
13857
+ var AiProviderId;
13858
+ (function (AiProviderId) {
13859
+ AiProviderId["Anthropic"] = "anthropic";
13860
+ AiProviderId["OpenRouter"] = "openrouter";
13861
+ })(AiProviderId || (AiProviderId = {}));
13862
+ /** Who pays: the platform's credits, or the studio's own provider key (metered for display, never charged). */
13863
+ var AiKeyMode;
13864
+ (function (AiKeyMode) {
13865
+ AiKeyMode["Platform"] = "platform";
13866
+ AiKeyMode["Own"] = "own";
13867
+ })(AiKeyMode || (AiKeyMode = {}));
13868
+ /** The steps a model works on (board 9's rows), in screen order. */
13869
+ var AiStep;
13870
+ (function (AiStep) {
13871
+ AiStep["ReadMessage"] = "analysis";
13872
+ AiStep["PlanSite"] = "plan";
13873
+ AiStep["ChangePlan"] = "planEdit";
13874
+ AiStep["MakePictures"] = "image";
13875
+ AiStep["NameStructures"] = "scanNaming";
13876
+ })(AiStep || (AiStep = {}));
13877
+ const AI_STEPS = Object.values(AiStep);
13878
+ /** Each step's name on the wire (the control plane's `label`, the console) — the screens translate their own. */
13879
+ const AI_STEP_LABELS = {
13880
+ [AiStep.ReadMessage]: 'Read your message',
13881
+ [AiStep.PlanSite]: 'Plan the site',
13882
+ [AiStep.ChangePlan]: 'Change the plan',
13883
+ [AiStep.MakePictures]: 'Make pictures',
13884
+ [AiStep.NameStructures]: 'Name repeated structures',
13885
+ };
13886
+ /** What a model must be able to do for a step to run on it. */
13887
+ var AiCapability;
13888
+ (function (AiCapability) {
13889
+ AiCapability["StructuredOutput"] = "structuredOutputs";
13890
+ AiCapability["ImageInput"] = "imageInput";
13891
+ AiCapability["ImageOutput"] = "imageOutput";
13892
+ })(AiCapability || (AiCapability = {}));
13893
+ const AI_STEP_NEEDS = {
13894
+ [AiStep.ReadMessage]: [AiCapability.StructuredOutput, AiCapability.ImageInput],
13895
+ [AiStep.PlanSite]: [AiCapability.StructuredOutput],
13896
+ [AiStep.ChangePlan]: [AiCapability.StructuredOutput],
13897
+ [AiStep.MakePictures]: [AiCapability.ImageOutput],
13898
+ [AiStep.NameStructures]: [AiCapability.StructuredOutput],
13899
+ };
13900
+ /**
13901
+ * The steps a model answers in the plan's parts, one request per part (vectoriox/iox-cms#47 — the whole plan's format
13902
+ * compiles to a grammar Claude refuses), and the tokens a typical request of each part reads and writes. Each part reads
13903
+ * the same instructions; the pages read the structure too and write most of the words.
13904
+ */
13905
+ const AI_STEP_PART_TOKENS = {
13906
+ [AiStep.PlanSite]: {
13907
+ [PlanPart.Structure]: { input: 10_000, output: 1_500, cacheWrite: 0, cacheRead: 0 },
13908
+ [PlanPart.Pages]: { input: 12_000, output: 5_500, cacheWrite: 0, cacheRead: 0 },
13909
+ },
13910
+ [AiStep.ChangePlan]: {
13911
+ [PlanPart.Structure]: { input: 6_000, output: 300, cacheWrite: 0, cacheRead: 0 },
13912
+ [PlanPart.Pages]: { input: 6_500, output: 400, cacheWrite: 0, cacheRead: 0 },
13913
+ },
13914
+ };
13915
+ /** The parts a step is asked in, in order — none for a step asked in one request. */
13916
+ function aiStepParts(step) {
13917
+ return AI_STEP_PART_TOKENS[step] ? PLAN_PARTS : [];
13918
+ }
13919
+ const addTokens = (a, b) => ({
13920
+ input: a.input + b.input, output: a.output + b.output, cacheWrite: a.cacheWrite + b.cacheWrite, cacheRead: a.cacheRead + b.cacheRead,
13921
+ ...(a.imageOutput !== undefined || b.imageOutput !== undefined ? { imageOutput: (a.imageOutput ?? 0) + (b.imageOutput ?? 0) } : {}),
13922
+ });
13923
+ const partsTotal = (step) => PLAN_PARTS.map(part => AI_STEP_PART_TOKENS[step][part]).reduce(addTokens);
13924
+ /**
13925
+ * Tokens a typical use of a step reads and writes (plan §3d "Where credits go") — what a shown estimate is made of. A
13926
+ * step asked in parts is every part's request together, so the estimate (and a generation's start gate) counts them all.
13927
+ */
13928
+ const AI_STEP_TYPICAL_TOKENS = {
13929
+ [AiStep.ReadMessage]: { input: 8_800, output: 1_500, cacheWrite: 0, cacheRead: 0 },
13930
+ [AiStep.PlanSite]: partsTotal(AiStep.PlanSite),
13931
+ [AiStep.ChangePlan]: partsTotal(AiStep.ChangePlan),
13932
+ // a 1 K picture is 1,120 image tokens (Google's figure for Nano Banana 2) — I-D4
13933
+ [AiStep.MakePictures]: { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, imageOutput: 1_120 },
13934
+ [AiStep.NameStructures]: { input: 80_000, output: 10_000, cacheWrite: 0, cacheRead: 0 },
13935
+ };
13936
+ /** 1 credit = 1 US cent of what the AI actually cost (decision D4). */
13937
+ const CREDITS_PER_USD = 100;
13938
+ const TOKENS_PER_PRICE_UNIT = 1_000_000;
13939
+ const CREDIT_PRECISION = 1e6;
13940
+ const NO_TOKENS = Object.freeze({ input: 0, output: 0, cacheWrite: 0, cacheRead: 0 });
13941
+ /** What `tokens` cost at `price`, in US dollars. */
13942
+ function costUsd(tokens, price) {
13943
+ const perToken = (perMillion, fallback) => (perMillion ?? fallback) / TOKENS_PER_PRICE_UNIT;
13944
+ return tokens.input * perToken(price.input, 0)
13945
+ + tokens.output * perToken(price.output, 0)
13946
+ + tokens.cacheWrite * perToken(price.cacheWrite, price.input)
13947
+ + tokens.cacheRead * perToken(price.cacheRead, price.input)
13948
+ + (tokens.imageOutput ?? 0) * perToken(price.imageOutput, price.output);
13949
+ }
13950
+ /** Dollars → credits, kept to a millionth of a credit (a short read costs a fraction of one). */
13951
+ function creditsOfUsd(usd) {
13952
+ return Math.round(usd * CREDITS_PER_USD * CREDIT_PRECISION) / CREDIT_PRECISION;
13953
+ }
13954
+ /** What a typical request of `step` reads and writes — one part's when the step is asked in parts, else the whole step's. */
13955
+ function typicalStepTokens(step, part) {
13956
+ return (part && AI_STEP_PART_TOKENS[step]?.[part]) || AI_STEP_TYPICAL_TOKENS[step];
13957
+ }
13958
+ /** The credits a typical use of `step` costs on a model of `price` — never typed, always computed (§3d). */
13959
+ function estimateStepCredits(step, price, tokens = AI_STEP_TYPICAL_TOKENS[step]) {
13960
+ return creditsOfUsd(costUsd(tokens, price));
13961
+ }
13962
+ /** Can `model` do `step`? The step's needs against the model's capabilities. */
13963
+ function modelCanDo(model, step) {
13964
+ return AI_STEP_NEEDS[step].every(need => model.capabilities.includes(need));
13965
+ }
13966
+ /** Why AI did not run (board 11). The server refuses with these BEFORE any model call. */
13967
+ var AiRefusalCode;
13968
+ (function (AiRefusalCode) {
13969
+ AiRefusalCode["NotInPlan"] = "ai.notInPlan";
13970
+ AiRefusalCode["OutOfCredits"] = "ai.outOfCredits";
13971
+ AiRefusalCode["CapReached"] = "ai.capReached";
13972
+ AiRefusalCode["NotConnected"] = "ai.notConnected";
13973
+ AiRefusalCode["KeyInvalid"] = "ai.keyInvalid";
13974
+ AiRefusalCode["PlanUnknown"] = "ai.planUnknown";
13975
+ })(AiRefusalCode || (AiRefusalCode = {}));
13976
+ /** The HTTP status each refusal answers with (board 11). */
13977
+ const AI_REFUSAL_STATUS = {
13978
+ [AiRefusalCode.NotInPlan]: 403,
13979
+ [AiRefusalCode.OutOfCredits]: 402,
13980
+ [AiRefusalCode.CapReached]: 402,
13981
+ [AiRefusalCode.NotConnected]: 409,
13982
+ [AiRefusalCode.KeyInvalid]: 409,
13983
+ [AiRefusalCode.PlanUnknown]: 503,
13984
+ };
13985
+ /** What a refusal offers next. */
13986
+ var AiNextKind;
13987
+ (function (AiNextKind) {
13988
+ /** Ask IOX (a prefilled email to the operator, until a plan can be bought — D2). */
13989
+ AiNextKind["Contact"] = "contact";
13990
+ /** Open Settings ▸ AI. */
13991
+ AiNextKind["Settings"] = "settings";
13992
+ /** Try again later, or with less. */
13993
+ AiNextKind["Retry"] = "retry";
13994
+ })(AiNextKind || (AiNextKind = {}));
13995
+ const AI_REFUSAL_NEXT = {
13996
+ [AiRefusalCode.NotInPlan]: AiNextKind.Contact,
13997
+ [AiRefusalCode.OutOfCredits]: AiNextKind.Contact,
13998
+ [AiRefusalCode.CapReached]: AiNextKind.Retry,
13999
+ [AiRefusalCode.NotConnected]: AiNextKind.Settings,
14000
+ [AiRefusalCode.KeyInvalid]: AiNextKind.Settings,
14001
+ [AiRefusalCode.PlanUnknown]: AiNextKind.Retry,
14002
+ };
14003
+ /** The plan entitlements AI reads (the control plane's `features` / `limits` keys) — ONE spelling. */
14004
+ var AiFeature;
14005
+ (function (AiFeature) {
14006
+ AiFeature["SiteGeneration"] = "aiSiteGeneration";
14007
+ })(AiFeature || (AiFeature = {}));
14008
+ var AiLimit;
14009
+ (function (AiLimit) {
14010
+ AiLimit["CreditsPerMonth"] = "aiCreditsPerMonth";
14011
+ AiLimit["CreditsPerGeneration"] = "aiCreditsPerGeneration";
14012
+ })(AiLimit || (AiLimit = {}));
14013
+ /** A generation's stages (§3d). The runner works while `task` is set; otherwise the owner has the next move. */
14014
+ var AiGenerationStage;
14015
+ (function (AiGenerationStage) {
14016
+ AiGenerationStage["Reading"] = "reading";
14017
+ AiGenerationStage["Analysis"] = "analysis";
14018
+ AiGenerationStage["Design"] = "design";
14019
+ AiGenerationStage["Plan"] = "plan";
14020
+ AiGenerationStage["Build"] = "build";
14021
+ AiGenerationStage["Done"] = "done";
14022
+ AiGenerationStage["Failed"] = "failed";
14023
+ AiGenerationStage["Cancelled"] = "cancelled";
14024
+ })(AiGenerationStage || (AiGenerationStage = {}));
14025
+ /** The stages nothing advances any more. */
14026
+ const FINAL_GENERATION_STAGES = [AiGenerationStage.Done, AiGenerationStage.Failed, AiGenerationStage.Cancelled];
14027
+ /** How one model call ended — a ledger row's status. */
14028
+ var AiCallStatus;
14029
+ (function (AiCallStatus) {
14030
+ /** Written before the call: a reservation at the estimate until the call settles. */
14031
+ AiCallStatus["Started"] = "started";
14032
+ AiCallStatus["Ok"] = "ok";
14033
+ /** The answer came back and the shared validator refused it (with a path). */
14034
+ AiCallStatus["Invalid"] = "invalid";
14035
+ /** The model declined (a provider refusal) — charged what the provider reports. */
14036
+ AiCallStatus["Refused"] = "refused";
14037
+ AiCallStatus["Error"] = "error";
14038
+ /** The process died during the call: charged at the estimate, since what it cost was never reported. */
14039
+ AiCallStatus["Interrupted"] = "interrupted";
14040
+ })(AiCallStatus || (AiCallStatus = {}));
14041
+
13949
14042
  /**
13950
14043
  * What a structured-output request costs the provider's grammar compiler, counted the way the Claude API counts it
13951
14044
  * (platform.claude.com/docs/en/build-with-claude/structured-outputs, "Schema complexity limits", read 2026-10-27):
@@ -13955,10 +14048,24 @@ function planToWire(plan) {
13955
14048
  * The limits are per REQUEST — every strict schema in it together. The ONE counter: the specs hold every schema iox-cms
13956
14049
  * sends to it, and the fake Claude API refuses with it exactly as Claude does — the plan stage once sent 213 optional
13957
14050
  * parameters and every suite stayed green because the fake accepted anything (vectoriox/iox-cms#45).
14051
+ *
14052
+ * Beyond those, Claude refuses a grammar it compiles too large — a limit the docs name ("additional internal limits on
14053
+ * the compiled grammar size") without a number. MEASURED against the API on 2026-10-27 (vectoriox/iox-cms#47): the
14054
+ * total number of properties decides it, not enums or descriptions — 64 and 61 properties refused, 53 and fewer
14055
+ * accepted (53 already took ~9 s to compile). So 60 is the line we refuse above, and 40 is what we send.
14056
+ */
14057
+ const STRICT_SCHEMA_LIMITS = {
14058
+ strictTools: 20,
14059
+ optional: 24,
14060
+ unions: 16,
14061
+ /** MEASURED, not documented (2026-10-27): the smallest refused request held 61 properties, the largest accepted 53. */
14062
+ properties: 60,
14063
+ };
14064
+ /**
14065
+ * What one request iox-cms sends may use: half of each documented limit, so a step can gain a field (or a strict tool)
14066
+ * without a 400 — and 40 properties, a third under the measured grammar line, where compiling stays a few seconds.
13958
14067
  */
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 };
14068
+ const STRICT_SCHEMA_BUDGET = { optional: 12, unions: 8, properties: 40 };
13962
14069
  /** The keywords the docs list as not supported ("Numerical constraints", "String constraints", "Array constraints beyond `minItems` of 0 or 1"). */
13963
14070
  const UNSUPPORTED_KEYWORDS = [
13964
14071
  'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'multipleOf', 'minLength', 'maxLength', 'maxItems', 'uniqueItems', 'contains',
@@ -13977,6 +14084,9 @@ function count(node, path, out) {
13977
14084
  if (node['type'] === 'object' || properties) {
13978
14085
  const required = new Set(Array.isArray(node['required']) ? node['required'] : []);
13979
14086
  out.optional += Object.keys(properties ?? {}).filter(key => !required.has(key)).length;
14087
+ out.properties += Object.keys(properties ?? {}).length;
14088
+ if (properties)
14089
+ out.objects++;
13980
14090
  if (node['additionalProperties'] !== false)
13981
14091
  out.unsupported.push(`${at('additionalProperties')}: must be false`);
13982
14092
  }
@@ -14005,14 +14115,17 @@ function count(node, path, out) {
14005
14115
  }
14006
14116
  /** The cost of ONE request's strict schemas together: its JSON output format and its strict tools' input schemas. */
14007
14117
  function strictSchemaComplexity(schemas, strictTools = 0) {
14008
- const out = { optional: 0, unions: 0, strictTools, unsupported: [] };
14118
+ const out = { optional: 0, unions: 0, strictTools, properties: 0, objects: 0, unsupported: [] };
14009
14119
  schemas.forEach((schema, i) => count(schema, schemas.length > 1 ? `[${i}]` : '', out));
14010
14120
  return out;
14011
14121
  }
14122
+ /** Claude's words for a grammar it compiles too large (2026-10-27, the plan stage — vectoriox/iox-cms#47), verbatim. */
14123
+ 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
14124
  /**
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).
14125
+ * The 400 the Claude API answers a request over its limits with, or null. The optional-parameter and the grammar-size
14126
+ * messages are the ones Claude answered on 2026-10-27, verbatim; the union, strict-tool and unsupported messages follow
14127
+ * their shape (not yet captured). The documented limits are checked first: a request over one of them never reaches the
14128
+ * grammar compiler.
14016
14129
  */
14017
14130
  function strictSchemaRefusal(complexity) {
14018
14131
  const refuse = (message) => ({ status: 400, type: 'invalid_request_error', message });
@@ -14027,6 +14140,8 @@ function strictSchemaRefusal(complexity) {
14027
14140
  if (complexity.unions > STRICT_SCHEMA_LIMITS.unions) {
14028
14141
  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
14142
  }
14143
+ if (complexity.properties > STRICT_SCHEMA_LIMITS.properties)
14144
+ return refuse(GRAMMAR_TOO_LARGE_MESSAGE);
14030
14145
  return null;
14031
14146
  }
14032
14147
 
@@ -14070,9 +14185,32 @@ const PLAN_EDIT_SHAPE = {
14070
14185
  [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
14186
  [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
14187
  [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" },
14188
+ [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
14189
  [PlanEditOp.SetSectionData]: { op: "'setSectionData'", page: 'string — a page key', index: 'integer — the section', binding: 'string — the list source the section repeats' },
14075
14190
  };
14191
+ /**
14192
+ * The plan part each op changes (vectoriox/iox-cms#47): a sentence is answered in the plan's two parts, one request each —
14193
+ * the structure's edits first, then the pages' written knowing them.
14194
+ */
14195
+ const PLAN_EDIT_OP_PART = {
14196
+ [PlanEditOp.AddPage]: PlanPart.Pages,
14197
+ [PlanEditOp.RemovePage]: PlanPart.Pages,
14198
+ [PlanEditOp.RenamePage]: PlanPart.Pages,
14199
+ [PlanEditOp.MergePages]: PlanPart.Pages,
14200
+ [PlanEditOp.MovePage]: PlanPart.Pages,
14201
+ [PlanEditOp.AddSection]: PlanPart.Pages,
14202
+ [PlanEditOp.RemoveSection]: PlanPart.Pages,
14203
+ [PlanEditOp.MoveSection]: PlanPart.Pages,
14204
+ [PlanEditOp.SwapPattern]: PlanPart.Pages,
14205
+ [PlanEditOp.SetSlot]: PlanPart.Pages,
14206
+ [PlanEditOp.SetSectionData]: PlanPart.Pages,
14207
+ [PlanEditOp.AddContentType]: PlanPart.Structure,
14208
+ [PlanEditOp.AddField]: PlanPart.Structure,
14209
+ };
14210
+ /** The ops written in one part, in `PlanEditOp` order. */
14211
+ function planEditOps(part) {
14212
+ return Object.values(PlanEditOp).filter(op => PLAN_EDIT_OP_PART[op] === part);
14213
+ }
14076
14214
  var PlanChangeKind;
14077
14215
  (function (PlanChangeKind) {
14078
14216
  PlanChangeKind["Page"] = "page";
@@ -14193,6 +14331,8 @@ function findSection(d, pageAt, index, c, path) {
14193
14331
  return ok;
14194
14332
  }
14195
14333
  const setSections = (d, at, sections) => { d.pages[at] = { ...d.pages[at], sections }; };
14334
+ /** A child added to a page or type the same edits added is part of that addition — the map marks the parent only. */
14335
+ 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
14336
  /** One handler per op — a data table, never an if-chain. */
14197
14337
  const APPLY = {
14198
14338
  [PlanEditOp.AddPage]: (d, e, c, path, changed) => {
@@ -14257,7 +14397,8 @@ const APPLY = {
14257
14397
  const index = e.at === undefined ? sections.length : clamp(e.at, sections.length);
14258
14398
  sections.splice(index, 0, e.section);
14259
14399
  setSections(d, at, sections);
14260
- changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Added, page: d.pages[at].key, index });
14400
+ if (!addedHere(changed, PlanChangeKind.Page, { page: d.pages[at].key }))
14401
+ changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Added, page: d.pages[at].key, index });
14261
14402
  },
14262
14403
  [PlanEditOp.RemoveSection]: (d, e, c, path, changed) => {
14263
14404
  const at = findPage(d, e.page, c, `${path}.page`);
@@ -14329,9 +14470,10 @@ const APPLY = {
14329
14470
  const type = d.contentTypes[at];
14330
14471
  if (type.fields.some(f => f.name === e.field.name))
14331
14472
  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;
14473
+ const seed = type.seed ? { ...type.seed, [e.field.name]: e.sample ?? type.seed[e.field.name] ?? '' } : undefined;
14333
14474
  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}` });
14475
+ if (!addedHere(changed, PlanChangeKind.ContentType, { name: type.name }))
14476
+ changed.push({ kind: PlanChangeKind.Field, action: PlanChangeAction.Added, name: `${type.name}.${e.field.name}` });
14335
14477
  },
14336
14478
  [PlanEditOp.SetSectionData]: (d, e, c, path, changed) => {
14337
14479
  const at = findPage(d, e.page, c, `${path}.page`);
@@ -14370,20 +14512,34 @@ function applyPlanEdits(plan, edits) {
14370
14512
  return { plan: { ...plan, pages: draft.pages, contentTypes: draft.contentTypes, dataSources }, changed, issues: [] };
14371
14513
  }
14372
14514
  // ── The format a model writes edits in ─────────────────────────────────────────────────────────────────
14373
- const PLAN_EDITS_SCHEMA_NAME = 'plan_edits';
14515
+ /** Each part's answer format name. */
14516
+ const PLAN_EDITS_PART_SCHEMA_NAMES = {
14517
+ [PlanPart.Structure]: 'plan_structure_edits',
14518
+ [PlanPart.Pages]: 'plan_page_edits',
14519
+ };
14374
14520
  /** A sentence's answer: the edits, or why the ops cannot say it (one line, in the owner's language). */
14375
14521
  var PlanEditsOutcome;
14376
14522
  (function (PlanEditsOutcome) {
14377
14523
  PlanEditsOutcome["Edits"] = "edits";
14378
14524
  PlanEditsOutcome["Cannot"] = "cannot";
14379
14525
  })(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 ''",
14526
+ /**
14527
+ * Each part's answer, explained. The structure is asked first, so it says whether the sentence is a plan change at all
14528
+ * (`outcome`); the pages are asked only when it is, and write only their edits.
14529
+ */
14530
+ const PLAN_EDITS_ANSWER_SHAPES = {
14531
+ [PlanPart.Structure]: {
14532
+ outcome: "'edits' | 'cannot' — cannot: no operation of either part (below) can do what the sentence asks",
14533
+ edits: 'edit[] — the content types and fields the sentence adds, in order; empty when it adds none, or cannot',
14534
+ reason: "string — when cannot, ONE short line for the owner in their language (what to do instead); else ''",
14535
+ },
14536
+ [PlanPart.Pages]: {
14537
+ edits: 'edit[] — the page and section edits, in order, made after the structure edits (already applied); empty when none',
14538
+ },
14385
14539
  };
14386
14540
  const NO_INDEX = -1;
14541
+ /** A new page or content type on the wire: its children are edits of their own. */
14542
+ const CHILDLESS = { children: false };
14387
14543
  /** A list holding exactly one object: none → absent, one → it (expanded), more → the list (the gate refuses it). */
14388
14544
  const oneOf = (schema, expand, toWire, what) => ({
14389
14545
  text: `${what}[] — exactly one when the op takes it`,
@@ -14401,17 +14557,22 @@ const EDIT_WIRE = {
14401
14557
  after: textKey('addPage: the key of the page it follows in the menu'),
14402
14558
  into: textKey('mergePages: the page that stays'),
14403
14559
  from: textKey('mergePages: the page whose sections move into it, then removed'),
14404
- index: indexKey('the section, 0-based on its page'),
14560
+ index: indexKey('the section, 0-based on its page (addSection: the place the new one goes)'),
14405
14561
  to: indexKey('movePage / moveSection: the new 0-based place'),
14406
- at: indexKey('addSection: its 0-based place on the page'),
14407
14562
  pattern: { text: 'string — swapPattern: the section pattern it becomes', none: '', schema: () => jsonOneOf(['', ...sectionPatternIds()]) },
14408
14563
  slot: textKey("setSlot: one of the pattern's slots"),
14409
14564
  text: textKey("setSlot: the words, in the owner's language and voice"),
14410
14565
  binding: textKey('setSectionData: the list source the section repeats'),
14411
14566
  contentType: textKey('addField: a content type of the plan'),
14412
- newPage: oneOf(wirePageJsonSchema, expandWirePage, pageToWire, 'page'),
14567
+ newPage: {
14568
+ ...oneOf(() => wirePageJsonSchema(CHILDLESS), v => expandWirePage(v, CHILDLESS), p => pageToWire(p, CHILDLESS), 'page'),
14569
+ text: 'page[] — exactly one when the op takes it, WITHOUT sections: each is an addSection after it',
14570
+ },
14413
14571
  newSection: oneOf(wireSectionJsonSchema, expandWireSection, sectionToWire, 'section'),
14414
- newContentType: oneOf(wireContentTypeJsonSchema, expandWireContentType, contentTypeToWire, 'contentType'),
14572
+ newContentType: {
14573
+ ...oneOf(() => wireContentTypeJsonSchema(CHILDLESS), v => expandWireContentType(v, CHILDLESS), t => contentTypeToWire(t, CHILDLESS), 'contentType'),
14574
+ text: "contentType[] — exactly one when the op takes it, WITHOUT fields: each is an addField after it (the samples' values stay in its seed)",
14575
+ },
14415
14576
  dataSources: {
14416
14577
  text: 'dataSource[] — addContentType: the list / single sources its pages read', none: [],
14417
14578
  schema: () => jsonListOf(wireDataSourceJsonSchema()),
@@ -14428,7 +14589,7 @@ const PLAN_EDIT_WIRE_KEYS = {
14428
14589
  [PlanEditOp.RenamePage]: { page: 'page', name: 'name', route: 'route' },
14429
14590
  [PlanEditOp.MergePages]: { into: 'into', from: 'from' },
14430
14591
  [PlanEditOp.MovePage]: { page: 'page', to: 'to' },
14431
- [PlanEditOp.AddSection]: { page: 'page', section: 'newSection', at: 'at' },
14592
+ [PlanEditOp.AddSection]: { page: 'page', section: 'newSection', at: 'index' },
14432
14593
  [PlanEditOp.RemoveSection]: { page: 'page', index: 'index' },
14433
14594
  [PlanEditOp.MoveSection]: { page: 'page', from: 'index', to: 'to' },
14434
14595
  [PlanEditOp.SwapPattern]: { page: 'page', index: 'index', pattern: 'pattern' },
@@ -14437,19 +14598,27 @@ const PLAN_EDIT_WIRE_KEYS = {
14437
14598
  [PlanEditOp.AddField]: { contentType: 'contentType', field: 'newField', sample: 'sample' },
14438
14599
  [PlanEditOp.SetSectionData]: { page: 'page', index: 'index', binding: 'binding' },
14439
14600
  };
14601
+ /** The wire keys a part's edit carries: every key its ops read, in `EDIT_WIRE` order. */
14602
+ function planEditWireKeys(part) {
14603
+ const read = new Set(planEditOps(part).flatMap(op => Object.values(PLAN_EDIT_WIRE_KEYS[op])));
14604
+ return Object.keys(EDIT_WIRE).filter(k => read.has(k));
14605
+ }
14440
14606
  const isEditWireNone = (wireKey, v) => {
14441
14607
  const none = EDIT_WIRE[wireKey].none;
14442
14608
  return v === undefined || (Array.isArray(none) ? Array.isArray(v) && v.length === 0 : v === none) || (wireKey === 'sample' && Array.isArray(v) && v.length === 0);
14443
14609
  };
14444
14610
  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 => {
14611
+ const opsText = (part) => planEditOps(part).map(op => `'${op}'`).join(' | ');
14612
+ /** A part's flat edit, as a shape table: every key required, each with its none. */
14613
+ function planEditWireShape(part) {
14614
+ return {
14615
+ op: `${opsText(part)} — which keys below are read`,
14616
+ ...Object.fromEntries(planEditWireKeys(part).map(k => [k, `${EDIT_WIRE[k].text} — ${noneText(EDIT_WIRE[k].none)} when the op does not read it`])),
14617
+ };
14618
+ }
14619
+ /** Per op of a part, the wire keys it reads, in `PLAN_EDIT_SHAPE`'s words (`?` = may hold its none) — what the model reads. */
14620
+ function planEditWireOps(part) {
14621
+ return Object.fromEntries(planEditOps(part).map(op => {
14453
14622
  const entry = PLAN_EDIT_SHAPE[op];
14454
14623
  const keys = PLAN_EDIT_WIRE_KEYS[op];
14455
14624
  return [op, Object.fromEntries(Object.entries(keys).map(([planKey, wireKey]) => {
@@ -14477,20 +14646,21 @@ function planEditWireGaps() {
14477
14646
  }
14478
14647
  return gaps;
14479
14648
  }
14480
- /** The format a sentence's answer is written in: the outcome, the flat edits, the reason. */
14481
- function planEditsJsonSchema() {
14649
+ const ANSWER_SCHEMAS = {
14650
+ outcome: () => jsonOneOf(Object.values(PlanEditsOutcome)),
14651
+ reason: () => jsonText(),
14652
+ edits: part => jsonListOf(jsonShapedObject(planEditWireShape(part), {
14653
+ op: jsonOneOf(planEditOps(part)),
14654
+ ...Object.fromEntries(planEditWireKeys(part).map(k => [k, EDIT_WIRE[k].schema()])),
14655
+ }, `edit ${part}`)),
14656
+ };
14657
+ /** The format one part of a sentence's answer is written in — one request each (vectoriox/iox-cms#47). */
14658
+ function planEditsJsonSchema(part) {
14482
14659
  const gaps = planEditWireGaps();
14483
14660
  if (gaps.length)
14484
14661
  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');
14662
+ const shape = PLAN_EDITS_ANSWER_SHAPES[part];
14663
+ return jsonShapedObject(shape, Object.fromEntries(shapeKeys(shape).map(k => [k, ANSWER_SCHEMAS[k](part)])), `planEdits ${part}`);
14494
14664
  }
14495
14665
  /** 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
14666
  function expandWireEdit(wire) {
@@ -14511,9 +14681,10 @@ function expandWireEdit(wire) {
14511
14681
  }
14512
14682
  return out;
14513
14683
  }
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])) };
14684
+ /** An edit's own keys → its flat wire edit, in its part's keys. */
14685
+ function flatEdit(edit) {
14686
+ const part = PLAN_EDIT_OP_PART[edit.op];
14687
+ const out = { op: edit.op, ...Object.fromEntries(planEditWireKeys(part).map(k => [k, Array.isArray(EDIT_WIRE[k].none) ? [] : EDIT_WIRE[k].none])) };
14517
14688
  for (const [planKey, wireKey] of Object.entries(PLAN_EDIT_WIRE_KEYS[edit.op])) {
14518
14689
  const value = edit[planKey];
14519
14690
  if (value === undefined)
@@ -14522,7 +14693,26 @@ function planEditToWire(edit) {
14522
14693
  }
14523
14694
  return out;
14524
14695
  }
14525
- /** A model's answer → edits (each flat edit expanded); never repaired. */
14696
+ /** The children an added page or content type travels without, as the edits that add them after it. */
14697
+ function childEdits(edit) {
14698
+ if (edit.op === PlanEditOp.AddPage)
14699
+ return edit.page.sections.map(section => ({ op: PlanEditOp.AddSection, page: edit.page.key, section }));
14700
+ if (edit.op === PlanEditOp.AddContentType)
14701
+ return edit.contentType.fields.map(field => ({ op: PlanEditOp.AddField, contentType: edit.contentType.name, field }));
14702
+ return [];
14703
+ }
14704
+ /** An edit → the flat wire edits a model would write for it: itself, then its children (fakes and specs write them). */
14705
+ function planEditToWire(edit) {
14706
+ return [flatEdit(edit), ...childEdits(edit).map(flatEdit)];
14707
+ }
14708
+ /** Edits → each part's flat wire edits, in order (what each part's answer would hold). */
14709
+ function planEditsToWire(edits) {
14710
+ const out = Object.fromEntries(PLAN_PARTS.map(part => [part, []]));
14711
+ for (const edit of edits)
14712
+ out[PLAN_EDIT_OP_PART[edit.op]].push(...planEditToWire(edit));
14713
+ return out;
14714
+ }
14715
+ /** A model's answer (either part's) → edits (each flat edit expanded); never repaired. */
14526
14716
  function planEditsFromWire(answer) {
14527
14717
  if (!isPlainObject(answer) || !Array.isArray(answer['edits']))
14528
14718
  return answer;
@@ -15105,5 +15295,5 @@ function addStockCredits(footer, credits) {
15105
15295
  * Generated bundle index. Do not edit.
15106
15296
  */
15107
15297
 
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 };
15298
+ 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
15299
  //# sourceMappingURL=vectoriox-iox-ui-generation-core.mjs.map