@praxisui/page-builder 8.0.0-beta.11 → 8.0.0-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -100,6 +100,19 @@ Para gerar uma página a partir de um prompt usando o backend canônico do `prax
100
100
 
101
101
  O chrome conversacional do assistente usa `PraxisAiAssistantShellComponent` de `@praxisui/ai`. O Page Builder continua dono da semântica agentic de página: resolução de intenção, preview, apply, save, reopen e compilação do plano validado para `WidgetPageDefinition`.
102
102
 
103
+ ### Agentic Authoring Manifest
104
+
105
+ `PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST` declara o contrato executável de authoring do pacote e é exportado pelo `public-api` de `@praxisui/page-builder`.
106
+
107
+ O manifesto cobre as famílias de operação `page.configure`, `canvas.configure`, `widget.add`, `widget.remove`, `widget.moveResize`, `widget.shell.configure`, `composition.link.add`, `composition.link.remove`, `composition.plan.compile`, `state.set`, `page.preview.apply`, `page.persist.save` e `childOperation.delegate`.
108
+
109
+ As fronteiras canônicas são:
110
+ - o documento persistido continua sendo `WidgetPageDefinition`;
111
+ - `UiCompositionPlan` é apenas o plano intermediário de IA e deve compilar antes do preview/apply local;
112
+ - wiring persistido usa `page.composition.links`, incluindo `nestedPath` para component ports internas;
113
+ - o Page Builder não redefine inputs de widgets filhos. Edições de `definition.inputs` devem delegar para o manifesto do componente filho ou para `ComponentDocMeta.configEditor`;
114
+ - `pageIdentity` e ETag pertencem ao fluxo de persistência do `praxis-config-starter` e não entram no documento runtime da página.
115
+
103
116
  ```ts
104
117
  import { PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS } from '@praxisui/page-builder';
105
118
 
@@ -4811,6 +4811,7 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
4811
4811
  'Para criacao agentic de paginas, o preview pode trazer uiCompositionPlan como plano intermediario validavel; o page-builder compila esse plano para WidgetPageDefinition antes de aplicar.',
4812
4812
  'UiCompositionPlan usa widgets[].componentId e bindings declarativos. O JSON persistido continua sendo page.widgets[].definition.id e page.composition.links.',
4813
4813
  'Nested component ports usam endpoint component-port com nestedPath. O owner em ref.widget, bindings[].from.widget ou bindings[].to.widget continua sendo o widget top-level; a porta real do filho fica em port/direction e o caminho ate o filho fica em nestedPath.',
4814
+ 'Nested component ports usam endpoint component-port com nestedPath. O owner em ref.widget ou bindings[].*.widget continua sendo o widget top-level; a porta real do filho fica em port/direction e o caminho ate o filho fica em nestedPath.',
4814
4815
  'Nao gere links nested via owner.widgetEvent, bindingPath profundo, page.connections ou wrappers host-specific. widgetEvent e bridge legada/avancada, nao contrato principal de authoring.',
4815
4816
  'Payloads legados de layout ainda podem ser ingeridos, mas sao normalizados para canvas e nao devem ser reemitidos.',
4816
4817
  'pageIdentity identifica o escopo de persistencia e nao pertence ao objeto page.',
@@ -5080,7 +5081,7 @@ class PageBuilderAgenticAuthoringService {
5080
5081
  return this.http.post(`${this.baseUrl}/resource-candidates`, request, { headers: this.buildHeaders() });
5081
5082
  }
5082
5083
  streamTurn(request) {
5083
- return this.http.post(`${this.baseUrl}/turn/stream/start`, request, { headers: this.buildHeaders() }).pipe(switchMap((start) => this.connectTurnStream(start)));
5084
+ return this.http.post(`${this.baseUrl}/turn/stream/start`, request, { headers: this.buildHeaders(), withCredentials: true }).pipe(switchMap((start) => this.connectTurnStream(start)));
5084
5085
  }
5085
5086
  applyPage(request) {
5086
5087
  const { ifMatch, ...body } = request;
@@ -5114,17 +5115,14 @@ class PageBuilderAgenticAuthoringService {
5114
5115
  connectTurnStream(start) {
5115
5116
  return new Observable((observer) => {
5116
5117
  const url = this.buildStreamUrl(start);
5117
- let source;
5118
+ const probeUrl = this.buildStreamProbeUrl(start);
5119
+ let source = null;
5118
5120
  let closed = false;
5119
5121
  let connectionErrorTimer = null;
5120
5122
  const connectionErrorGraceMs = Math.max(0, this.options?.streamConnectionErrorGraceMs ?? 1500);
5121
- try {
5122
- source = this.createEventSource(url);
5123
- }
5124
- catch (error) {
5125
- observer.error(this.streamConnectionError(error));
5126
- return undefined;
5127
- }
5123
+ let probeAbort = typeof AbortController !== 'undefined'
5124
+ ? new AbortController()
5125
+ : null;
5128
5126
  const clearConnectionErrorTimer = () => {
5129
5127
  if (connectionErrorTimer) {
5130
5128
  clearTimeout(connectionErrorTimer);
@@ -5137,7 +5135,7 @@ class PageBuilderAgenticAuthoringService {
5137
5135
  }
5138
5136
  closed = true;
5139
5137
  clearConnectionErrorTimer();
5140
- source.close();
5138
+ source?.close();
5141
5139
  };
5142
5140
  const failConnection = (event) => {
5143
5141
  if (closed) {
@@ -5161,23 +5159,58 @@ class PageBuilderAgenticAuthoringService {
5161
5159
  closeSource();
5162
5160
  }
5163
5161
  };
5164
- source.onmessage = handleMessage;
5165
- if (source.addEventListener) {
5166
- for (const type of AI_STREAM_EVENT_TYPES) {
5167
- source.addEventListener(type, handleMessage);
5162
+ void this.probeTurnStreamEndpoint(probeUrl, probeAbort)
5163
+ .then((status) => {
5164
+ probeAbort = null;
5165
+ if (closed) {
5166
+ return;
5168
5167
  }
5169
- }
5170
- source.onerror = (event) => {
5171
- if (closed || connectionErrorTimer) {
5168
+ if (typeof status === 'number' && status >= 400) {
5169
+ closed = true;
5170
+ observer.error(this.streamConnectionError({ status }));
5172
5171
  return;
5173
5172
  }
5174
- if (connectionErrorGraceMs === 0) {
5175
- failConnection(event);
5173
+ try {
5174
+ source = this.createEventSource(url);
5175
+ }
5176
+ catch (error) {
5177
+ closed = true;
5178
+ observer.error(this.streamConnectionError(error));
5176
5179
  return;
5177
5180
  }
5178
- connectionErrorTimer = setTimeout(() => failConnection(event), connectionErrorGraceMs);
5181
+ source.onmessage = handleMessage;
5182
+ if (source.addEventListener) {
5183
+ for (const type of AI_STREAM_EVENT_TYPES) {
5184
+ source.addEventListener(type, handleMessage);
5185
+ }
5186
+ }
5187
+ source.onerror = (event) => {
5188
+ if (closed || connectionErrorTimer) {
5189
+ return;
5190
+ }
5191
+ if (connectionErrorGraceMs === 0) {
5192
+ failConnection(event);
5193
+ return;
5194
+ }
5195
+ connectionErrorTimer = setTimeout(() => failConnection(event), connectionErrorGraceMs);
5196
+ };
5197
+ })
5198
+ .catch((error) => {
5199
+ if (closed) {
5200
+ return;
5201
+ }
5202
+ closed = true;
5203
+ observer.error(this.streamConnectionError(error));
5204
+ });
5205
+ return () => {
5206
+ probeAbort?.abort();
5207
+ if (source) {
5208
+ closeSource();
5209
+ return;
5210
+ }
5211
+ closed = true;
5212
+ clearConnectionErrorTimer();
5179
5213
  };
5180
- return () => closeSource();
5181
5214
  });
5182
5215
  }
5183
5216
  streamConnectionError(cause) {
@@ -5195,6 +5228,15 @@ class PageBuilderAgenticAuthoringService {
5195
5228
  ? url.toString()
5196
5229
  : url.pathname + url.search;
5197
5230
  }
5231
+ buildStreamProbeUrl(start) {
5232
+ const url = new URL(`${this.baseUrl}/turn/stream/${start.streamId}/probe`, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
5233
+ if (start.streamAccessToken) {
5234
+ url.searchParams.set('accessToken', start.streamAccessToken);
5235
+ }
5236
+ return /^https?:\/\//i.test(this.baseUrl)
5237
+ ? url.toString()
5238
+ : url.pathname + url.search;
5239
+ }
5198
5240
  createEventSource(url) {
5199
5241
  const factory = this.options?.eventSourceFactory;
5200
5242
  if (factory) {
@@ -5202,6 +5244,18 @@ class PageBuilderAgenticAuthoringService {
5202
5244
  }
5203
5245
  return new EventSource(url);
5204
5246
  }
5247
+ async probeTurnStreamEndpoint(url, abort) {
5248
+ if (typeof fetch === 'undefined') {
5249
+ return null;
5250
+ }
5251
+ const response = await fetch(url, {
5252
+ method: 'GET',
5253
+ credentials: 'include',
5254
+ cache: 'no-store',
5255
+ signal: abort?.signal,
5256
+ });
5257
+ return response.status;
5258
+ }
5205
5259
  formatEtag(etag) {
5206
5260
  const trimmed = etag.trim();
5207
5261
  return trimmed.startsWith('"') ? trimmed : `"${trimmed}"`;
@@ -5214,6 +5268,522 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
5214
5268
  args: [{ providedIn: 'root' }]
5215
5269
  }] });
5216
5270
 
5271
+ const pageSchema = {
5272
+ type: 'object',
5273
+ properties: {
5274
+ title: { type: 'string' },
5275
+ layoutPreset: { type: 'string' },
5276
+ context: { type: 'object' },
5277
+ metadata: { type: 'object' },
5278
+ },
5279
+ };
5280
+ const canvasSchema = {
5281
+ type: 'object',
5282
+ properties: {
5283
+ columns: { type: 'number' },
5284
+ rowUnit: { type: 'number' },
5285
+ gap: { type: 'number' },
5286
+ items: { type: 'array' },
5287
+ },
5288
+ };
5289
+ const widgetSchema = {
5290
+ type: 'object',
5291
+ required: ['widgetKey', 'componentId'],
5292
+ properties: {
5293
+ widgetKey: { type: 'string' },
5294
+ componentId: { type: 'string' },
5295
+ title: { type: 'string' },
5296
+ inputs: { type: 'object' },
5297
+ canvasItem: { type: 'object' },
5298
+ },
5299
+ };
5300
+ const compositionLinkSchema = {
5301
+ type: 'object',
5302
+ required: ['linkId', 'from', 'to'],
5303
+ properties: {
5304
+ linkId: { type: 'string' },
5305
+ from: { type: 'object' },
5306
+ to: { type: 'object' },
5307
+ condition: { type: 'object' },
5308
+ policy: { type: 'object' },
5309
+ },
5310
+ };
5311
+ const childOperationSchema = {
5312
+ type: 'object',
5313
+ required: ['widgetKey', 'childComponentId', 'childOperationId', 'reason'],
5314
+ properties: {
5315
+ widgetKey: { type: 'string' },
5316
+ childComponentId: { type: 'string' },
5317
+ childOperationId: { type: 'string' },
5318
+ reason: { type: 'string' },
5319
+ childTarget: { type: 'object' },
5320
+ childParams: { type: 'object' },
5321
+ },
5322
+ };
5323
+ const PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST = {
5324
+ schemaVersion: '1.0.0',
5325
+ componentId: 'praxis-page-builder',
5326
+ ownerPackage: '@praxisui/page-builder',
5327
+ configSchemaId: 'WidgetPageDefinition',
5328
+ manifestVersion: '1.0.0',
5329
+ runtimeInputs: [
5330
+ { name: 'page', type: 'WidgetPageDefinition', description: 'Canonical renderable page document consumed by praxis-dynamic-page.' },
5331
+ { name: 'pageIdentity', type: 'PageIdentity | null', description: 'Persistence identity for ui_user_config save/apply flows; not persisted inside page.' },
5332
+ { name: 'enableCustomization', type: 'boolean', description: 'Enables page-builder authoring chrome around the runtime page.' },
5333
+ { name: 'context', type: 'Record<string, unknown>', description: 'Opaque host context passed to catalog resolution and agentic authoring.' },
5334
+ { name: 'pageChange', type: 'WidgetPageDefinition', description: 'Emitted after local apply/preview produces a renderable page.' },
5335
+ { name: 'agenticAuthoringEnableStreaming', type: 'boolean', description: 'Opt-in flag for the canonical streaming turn endpoints.' },
5336
+ ],
5337
+ editableTargets: [
5338
+ { kind: 'page', resolver: 'widget-page-definition-root', description: 'Root WidgetPageDefinition document: title, metadata, context and renderable page envelope.' },
5339
+ { kind: 'canvas', resolver: 'widget-page-canvas', description: 'Canvas geometry and item placement for top-level widgets.' },
5340
+ { kind: 'widget', resolver: 'widget-by-stable-key', description: 'Widget definitions addressed by stable widgetKey, never by array index.' },
5341
+ { kind: 'widgetShell', resolver: 'widget-shell-by-widget-key', description: 'Dashboard-card shell metadata such as title, action chrome and visual preset.' },
5342
+ { kind: 'compositionLink', resolver: 'composition-link-by-id', description: 'Declarative wiring in page.composition.links, including component-port nestedPath endpoints.' },
5343
+ { kind: 'state', resolver: 'page-state-path', description: 'Page-level state entries referenced by composition links.' },
5344
+ { kind: 'pageIdentity', resolver: 'page-builder-persistence-identity', description: 'ui_user_config identity, scope and ETag used by applyPage/save.' },
5345
+ { kind: 'agenticPreview', resolver: 'agentic-preview-result', description: 'Compiled backend preview envelope whose patch.page is applied locally.' },
5346
+ { kind: 'childOperation', resolver: 'widget-child-authoring-manifest-operation', description: 'Delegated operation owned by the child widget component manifest or configEditor.' },
5347
+ ],
5348
+ operations: [
5349
+ {
5350
+ operationId: 'page.configure',
5351
+ title: 'Configure page metadata',
5352
+ scope: 'global',
5353
+ targetKind: 'page',
5354
+ target: { kind: 'page', resolver: 'widget-page-definition-root', ambiguityPolicy: 'fail', required: false },
5355
+ inputSchema: pageSchema,
5356
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-page-configure', handlerContract: {
5357
+ reads: ['WidgetPageDefinition', 'DynamicPageConfigEditorComponent', 'SettingsPanelBridge'],
5358
+ writes: ['page.title', 'page.layoutPreset', 'page.context', 'page.metadata'],
5359
+ identityKeys: ['pageIdentity.componentId', 'pageIdentity.scope'],
5360
+ inputSchema: pageSchema,
5361
+ failureModes: ['page-not-loaded', 'context-not-json', 'settings-panel-bridge-missing'],
5362
+ description: 'Updates root page metadata while preserving the renderable WidgetPageDefinition shape.',
5363
+ } }],
5364
+ validators: ['widget-page-definition-valid', 'no-legacy-grid-page-definition', 'page-context-json-valid', 'settings-panel-round-trip-valid'],
5365
+ affectedPaths: ['title', 'layoutPreset', 'context', 'metadata'],
5366
+ submissionImpact: 'config-only',
5367
+ preconditions: ['page-loaded'],
5368
+ },
5369
+ {
5370
+ operationId: 'canvas.configure',
5371
+ title: 'Configure page canvas',
5372
+ scope: 'layout',
5373
+ targetKind: 'canvas',
5374
+ target: { kind: 'canvas', resolver: 'widget-page-canvas', ambiguityPolicy: 'fail', required: true },
5375
+ inputSchema: canvasSchema,
5376
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-canvas-configure', handlerContract: {
5377
+ reads: ['WidgetPageDefinition.canvas', 'DynamicPageBuilderComponent'],
5378
+ writes: ['page.canvas.columns', 'page.canvas.rowUnit', 'page.canvas.gap', 'page.canvas.items'],
5379
+ identityKeys: ['canvas'],
5380
+ inputSchema: canvasSchema,
5381
+ failureModes: ['canvas-missing', 'canvas-item-references-missing-widget', 'invalid-grid-geometry'],
5382
+ description: 'Configures the top-level canvas without introducing legacy GridPageDefinition semantics.',
5383
+ } }],
5384
+ validators: ['canvas-columns-integer', 'canvas-row-unit-valid', 'canvas-gap-valid', 'canvas-items-reference-existing-widgets'],
5385
+ affectedPaths: ['canvas.columns', 'canvas.rowUnit', 'canvas.gap', 'canvas.items'],
5386
+ submissionImpact: 'config-only',
5387
+ preconditions: ['page-loaded'],
5388
+ },
5389
+ {
5390
+ operationId: 'widget.add',
5391
+ title: 'Add widget to page',
5392
+ scope: 'layout',
5393
+ targetKind: 'widget',
5394
+ target: { kind: 'widget', resolver: 'widget-by-stable-key', ambiguityPolicy: 'fail', required: true },
5395
+ inputSchema: widgetSchema,
5396
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-add', handlerContract: {
5397
+ reads: ['WidgetPageDefinition.widgets', 'WidgetPageDefinition.canvas', 'ComponentDocRegistry', 'PAGE_BUILDER_WIDGET_AI_CATALOGS'],
5398
+ writes: ['page.widgets[]', 'page.canvas.items[]'],
5399
+ identityKeys: ['widgetKey'],
5400
+ inputSchema: widgetSchema,
5401
+ failureModes: ['widget-key-duplicate', 'component-not-registered', 'canvas-item-invalid', 'child-input-contract-unknown'],
5402
+ description: 'Adds a registered component as a widget and places it on the canvas using a stable widgetKey.',
5403
+ } }],
5404
+ validators: ['widget-key-unique', 'component-registered', 'canvas-item-valid', 'child-inputs-delegated', 'widget-key-not-array-index'],
5405
+ affectedPaths: ['widgets[]', 'canvas.items[]'],
5406
+ submissionImpact: 'config-only',
5407
+ preconditions: ['page-loaded', 'component-catalog-loaded'],
5408
+ },
5409
+ {
5410
+ operationId: 'widget.remove',
5411
+ title: 'Remove widget from page',
5412
+ scope: 'layout',
5413
+ targetKind: 'widget',
5414
+ target: { kind: 'widget', resolver: 'widget-by-stable-key', ambiguityPolicy: 'fail', required: true },
5415
+ inputSchema: { type: 'object', required: ['widgetKey'], properties: { widgetKey: { type: 'string' }, confirmed: { type: 'boolean' } } },
5416
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-remove', handlerContract: {
5417
+ reads: ['WidgetPageDefinition.widgets', 'WidgetPageDefinition.canvas.items', 'WidgetPageDefinition.composition.links'],
5418
+ writes: ['page.widgets[]', 'page.canvas.items[]', 'page.composition.links[]'],
5419
+ identityKeys: ['widgetKey'],
5420
+ failureModes: ['widget-not-found', 'destructive-removal-not-confirmed', 'dangling-composition-link'],
5421
+ description: 'Removes a widget and cleans canvas items and composition links that reference it.',
5422
+ } }],
5423
+ destructive: true,
5424
+ requiresConfirmation: true,
5425
+ validators: ['widget-exists', 'destructive-removal-confirmed', 'composition-links-cleaned', 'canvas-items-reference-existing-widgets'],
5426
+ affectedPaths: ['widgets[]', 'canvas.items[]', 'composition.links[]'],
5427
+ submissionImpact: 'config-only',
5428
+ preconditions: ['page-loaded', 'target-widget-exists'],
5429
+ },
5430
+ {
5431
+ operationId: 'widget.moveResize',
5432
+ title: 'Move or resize widget',
5433
+ scope: 'layout',
5434
+ targetKind: 'widget',
5435
+ target: { kind: 'widget', resolver: 'widget-by-stable-key', ambiguityPolicy: 'fail', required: true },
5436
+ inputSchema: { type: 'object', required: ['widgetKey', 'canvasItem'], properties: { widgetKey: { type: 'string' }, canvasItem: { type: 'object' } } },
5437
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-move-resize', handlerContract: {
5438
+ reads: ['WidgetPageDefinition.canvas.items', 'WidgetPageDefinition.widgets'],
5439
+ writes: ['page.canvas.items[]'],
5440
+ identityKeys: ['widgetKey'],
5441
+ failureModes: ['widget-not-found', 'canvas-item-invalid', 'canvas-overlap-invalid'],
5442
+ description: 'Updates canvas placement for an existing widget using widgetKey as the stable identity.',
5443
+ } }],
5444
+ validators: ['widget-exists', 'canvas-item-valid', 'canvas-item-targets-existing-widget', 'canvas-overlap-policy-valid'],
5445
+ affectedPaths: ['canvas.items[]'],
5446
+ submissionImpact: 'config-only',
5447
+ preconditions: ['page-loaded', 'target-widget-exists'],
5448
+ },
5449
+ {
5450
+ operationId: 'widget.shell.configure',
5451
+ title: 'Configure widget shell',
5452
+ scope: 'templating',
5453
+ targetKind: 'widgetShell',
5454
+ target: { kind: 'widgetShell', resolver: 'widget-shell-by-widget-key', ambiguityPolicy: 'fail', required: true },
5455
+ inputSchema: { type: 'object', required: ['widgetKey'], properties: { widgetKey: { type: 'string' }, shell: { type: 'object' } } },
5456
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-shell-configure', handlerContract: {
5457
+ reads: ['WidgetPageDefinition.widgets', 'WidgetShellEditorComponent', 'SettingsPanelBridge'],
5458
+ writes: ['page.widgets[].shell'],
5459
+ identityKeys: ['widgetKey'],
5460
+ failureModes: ['widget-not-found', 'settings-panel-bridge-missing', 'shell-config-invalid'],
5461
+ description: 'Updates the page-builder-owned widget shell without editing child component inputs.',
5462
+ } }],
5463
+ validators: ['widget-exists', 'widget-shell-shape-valid', 'settings-panel-bridge-available', 'child-inputs-not-mutated'],
5464
+ affectedPaths: ['widgets[].shell'],
5465
+ submissionImpact: 'config-only',
5466
+ preconditions: ['page-loaded', 'target-widget-exists'],
5467
+ },
5468
+ {
5469
+ operationId: 'composition.link.add',
5470
+ title: 'Add page composition link',
5471
+ scope: 'dataBinding',
5472
+ targetKind: 'compositionLink',
5473
+ target: { kind: 'compositionLink', resolver: 'composition-link-by-id', ambiguityPolicy: 'fail', required: true },
5474
+ inputSchema: compositionLinkSchema,
5475
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-composition-link-add', handlerContract: {
5476
+ reads: ['WidgetPageDefinition.composition.links', 'WidgetPageDefinition.widgets', 'PAGE_BUILDER_AI_CAPABILITIES'],
5477
+ writes: ['page.composition.links[]'],
5478
+ identityKeys: ['linkId'],
5479
+ inputSchema: compositionLinkSchema,
5480
+ failureModes: ['link-id-duplicate', 'endpoint-not-found', 'nested-path-invalid', 'json-logic-invalid'],
5481
+ description: 'Adds canonical declarative wiring in page.composition.links, including component-port nestedPath endpoints.',
5482
+ } }],
5483
+ validators: ['composition-link-id-unique', 'composition-endpoints-resolve', 'nested-path-terminal-widget-key-required', 'json-logic-condition-valid', 'no-legacy-connections-write'],
5484
+ affectedPaths: ['composition.links[]'],
5485
+ submissionImpact: 'config-only',
5486
+ preconditions: ['page-loaded', 'widgets-resolvable'],
5487
+ },
5488
+ {
5489
+ operationId: 'composition.link.remove',
5490
+ title: 'Remove page composition link',
5491
+ scope: 'dataBinding',
5492
+ targetKind: 'compositionLink',
5493
+ target: { kind: 'compositionLink', resolver: 'composition-link-by-id', ambiguityPolicy: 'fail', required: true },
5494
+ inputSchema: { type: 'object', required: ['linkId'], properties: { linkId: { type: 'string' }, confirmed: { type: 'boolean' } } },
5495
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-composition-link-remove', handlerContract: {
5496
+ reads: ['WidgetPageDefinition.composition.links'],
5497
+ writes: ['page.composition.links[]'],
5498
+ identityKeys: ['linkId'],
5499
+ failureModes: ['composition-link-not-found', 'destructive-removal-not-confirmed'],
5500
+ description: 'Removes a canonical composition link by stable linkId.',
5501
+ } }],
5502
+ destructive: true,
5503
+ requiresConfirmation: true,
5504
+ validators: ['composition-link-exists', 'destructive-removal-confirmed', 'composition-links-still-valid'],
5505
+ affectedPaths: ['composition.links[]'],
5506
+ submissionImpact: 'config-only',
5507
+ preconditions: ['page-loaded', 'target-link-exists'],
5508
+ },
5509
+ {
5510
+ operationId: 'composition.plan.compile',
5511
+ title: 'Compile UI composition plan',
5512
+ scope: 'rules',
5513
+ targetKind: 'agenticPreview',
5514
+ target: { kind: 'agenticPreview', resolver: 'agentic-preview-result', ambiguityPolicy: 'fail', required: true },
5515
+ inputSchema: { type: 'object', required: ['uiCompositionPlan'], properties: { uiCompositionPlan: { type: 'object' } } },
5516
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-ui-composition-plan-compile', handlerContract: {
5517
+ reads: ['UiCompositionPlan', 'compileUiCompositionPlan', 'PAGE_BUILDER_WIDGET_AI_CATALOGS', 'ComponentDocMeta.configEditor'],
5518
+ writes: ['compiledFormPatch.patch.page'],
5519
+ identityKeys: ['uiCompositionPlan.id', 'widgetKey', 'linkId'],
5520
+ failureModes: ['ui-composition-plan-invalid', 'widget-catalog-missing', 'compiled-page-invalid', 'child-config-editor-missing'],
5521
+ description: 'Compiles the agentic intermediate UiCompositionPlan into a renderable WidgetPageDefinition patch.page.',
5522
+ } }],
5523
+ validators: ['ui-composition-plan-valid', 'compiled-page-valid', 'widget-keys-unique', 'composition-endpoints-resolve', 'canvas-items-reference-existing-widgets'],
5524
+ affectedPaths: ['uiCompositionPlan', 'compiledFormPatch.patch.page'],
5525
+ submissionImpact: 'none',
5526
+ preconditions: ['agentic-preview-loaded', 'component-catalog-loaded'],
5527
+ },
5528
+ {
5529
+ operationId: 'state.set',
5530
+ title: 'Set page state value',
5531
+ scope: 'dataBinding',
5532
+ targetKind: 'state',
5533
+ target: { kind: 'state', resolver: 'page-state-path', ambiguityPolicy: 'fail', required: true },
5534
+ inputSchema: { type: 'object', required: ['path', 'value'], properties: { path: { type: 'string' }, value: {}, layer: { enum: ['page', 'session'] } } },
5535
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-state-set', handlerContract: {
5536
+ reads: ['WidgetPageDefinition.state', 'WidgetPageDefinition.composition.links'],
5537
+ writes: ['page.state'],
5538
+ identityKeys: ['path'],
5539
+ failureModes: ['state-path-invalid', 'state-json-invalid', 'composition-state-link-invalid'],
5540
+ description: 'Sets canonical page state used by composition links without creating host-only binding paths.',
5541
+ } }],
5542
+ validators: ['state-path-valid', 'state-layer-valid', 'state-json-valid', 'composition-state-links-still-valid'],
5543
+ affectedPaths: ['state'],
5544
+ submissionImpact: 'config-only',
5545
+ preconditions: ['page-loaded'],
5546
+ },
5547
+ {
5548
+ operationId: 'page.preview.apply',
5549
+ title: 'Apply agentic preview locally',
5550
+ scope: 'interaction',
5551
+ targetKind: 'agenticPreview',
5552
+ target: { kind: 'agenticPreview', resolver: 'agentic-preview-result', ambiguityPolicy: 'fail', required: true },
5553
+ inputSchema: { type: 'object', required: ['previewId'], properties: { previewId: { type: 'string' }, compiledFormPatch: { type: 'object' } } },
5554
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-preview-apply', handlerContract: {
5555
+ reads: ['PageBuilderAiAdapter.applyCompiledFormPatch', 'compiledFormPatch.patch.page', 'PraxisAssistantTurnViewState'],
5556
+ writes: ['DynamicPageBuilderComponent.page', 'pageChange'],
5557
+ identityKeys: ['previewId'],
5558
+ failureModes: ['preview-result-invalid', 'patch-page-missing', 'runtime-page-invalid', 'patch-envelope-persistence-attempted'],
5559
+ description: 'Applies only compiledFormPatch.patch.page to the local runtime page; the backend envelope remains diagnostic data.',
5560
+ } }],
5561
+ validators: ['preview-result-valid', 'compiled-page-patch-present', 'no-envelope-runtime-persistence', 'runtime-page-valid'],
5562
+ affectedPaths: ['compiledFormPatch.patch.page', 'page'],
5563
+ submissionImpact: 'visual-only',
5564
+ preconditions: ['agentic-preview-loaded'],
5565
+ },
5566
+ {
5567
+ operationId: 'page.persist.save',
5568
+ title: 'Persist page through config API',
5569
+ scope: 'interaction',
5570
+ targetKind: 'pageIdentity',
5571
+ target: { kind: 'pageIdentity', resolver: 'page-builder-persistence-identity', ambiguityPolicy: 'fail', required: true },
5572
+ inputSchema: { type: 'object', required: ['pageIdentity'], properties: { pageIdentity: { type: 'object' }, page: { type: 'object' }, etag: { type: 'string' } } },
5573
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-page-persist-save', handlerContract: {
5574
+ reads: ['pageIdentity', 'WidgetPageDefinition', 'PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS', 'ui_user_config ETag'],
5575
+ writes: ['POST/PUT /api/praxis/config/ui-user-config', 'If-Match'],
5576
+ identityKeys: ['pageIdentity.componentType', 'pageIdentity.componentId', 'pageIdentity.scope'],
5577
+ failureModes: ['page-identity-incomplete', 'etag-conflict', 'runtime-page-invalid', 'patch-envelope-persistence-attempted'],
5578
+ description: 'Persists the renderable WidgetPageDefinition with pageIdentity and ETag delegated to praxis-config-starter.',
5579
+ } }],
5580
+ validators: ['page-identity-complete', 'etag-policy-valid', 'runtime-page-valid', 'no-envelope-runtime-persistence'],
5581
+ affectedPaths: ['page', 'pageIdentity', 'etag'],
5582
+ submissionImpact: 'config-only',
5583
+ preconditions: ['page-loaded', 'page-identity-known'],
5584
+ },
5585
+ {
5586
+ operationId: 'childOperation.delegate',
5587
+ title: 'Delegate widget child operation',
5588
+ scope: 'global',
5589
+ targetKind: 'childOperation',
5590
+ target: { kind: 'childOperation', resolver: 'widget-child-authoring-manifest-operation', ambiguityPolicy: 'fail', required: false },
5591
+ inputSchema: childOperationSchema,
5592
+ effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-child-operation-delegate', handlerContract: {
5593
+ reads: ['WidgetPageDefinition.widgets', 'ComponentDocMeta.configEditor', 'child ComponentAuthoringManifest', 'PAGE_BUILDER_WIDGET_AI_CATALOGS'],
5594
+ writes: ['page.widgets[].definition.inputs'],
5595
+ identityKeys: ['widgetKey', 'childComponentId', 'childOperationId'],
5596
+ inputSchema: childOperationSchema,
5597
+ failureModes: ['widget-not-found', 'child-manifest-missing', 'child-operation-unknown', 'local-child-input-write-attempted'],
5598
+ description: 'Delegates child widget input edits to the owning manifest or configEditor instead of redefining child semantics in page-builder.',
5599
+ } }],
5600
+ validators: ['widget-exists', 'child-manifest-available', 'child-operation-known', 'component-config-editor-respected', 'no-local-child-input-write'],
5601
+ affectedPaths: ['widgets[].definition.inputs'],
5602
+ submissionImpact: 'config-only',
5603
+ preconditions: ['page-loaded', 'target-widget-exists', 'child-authoring-contract-available'],
5604
+ },
5605
+ ],
5606
+ validators: [
5607
+ { validatorId: 'widget-page-definition-valid', level: 'error', code: 'PAGE_BUILDER_PAGE_VALID', description: 'The document must remain a renderable WidgetPageDefinition.' },
5608
+ { validatorId: 'no-legacy-grid-page-definition', level: 'error', code: 'PAGE_BUILDER_NO_GRID_PAGE', description: 'Legacy GridPageDefinition semantics cannot be reintroduced.' },
5609
+ { validatorId: 'page-context-json-valid', level: 'error', code: 'PAGE_BUILDER_CONTEXT_JSON', description: 'Page context must stay JSON-serializable.' },
5610
+ { validatorId: 'settings-panel-round-trip-valid', level: 'error', code: 'PAGE_BUILDER_SETTINGS_ROUND_TRIP', description: 'Settings panel apply/save/reset/reopen must preserve the same page shape.' },
5611
+ { validatorId: 'canvas-columns-integer', level: 'error', code: 'PAGE_BUILDER_CANVAS_COLUMNS', description: 'Canvas columns must be an integer grid count.' },
5612
+ { validatorId: 'canvas-row-unit-valid', level: 'error', code: 'PAGE_BUILDER_CANVAS_ROW_UNIT', description: 'Canvas row unit must be a positive layout unit.' },
5613
+ { validatorId: 'canvas-gap-valid', level: 'error', code: 'PAGE_BUILDER_CANVAS_GAP', description: 'Canvas gap must be a valid non-negative spacing value.' },
5614
+ { validatorId: 'canvas-items-reference-existing-widgets', level: 'error', code: 'PAGE_BUILDER_CANVAS_WIDGET_REFS', description: 'Every canvas item must reference an existing widgetKey.' },
5615
+ { validatorId: 'widget-key-unique', level: 'error', code: 'PAGE_BUILDER_WIDGET_KEY_UNIQUE', description: 'Widget keys must be unique.' },
5616
+ { validatorId: 'component-registered', level: 'error', code: 'PAGE_BUILDER_COMPONENT_REGISTERED', description: 'Widget component id must exist in the component registry/catalog.' },
5617
+ { validatorId: 'canvas-item-valid', level: 'error', code: 'PAGE_BUILDER_CANVAS_ITEM', description: 'Canvas item geometry must be valid.' },
5618
+ { validatorId: 'child-inputs-delegated', level: 'error', code: 'PAGE_BUILDER_CHILD_INPUTS_DELEGATED', description: 'Child component input edits must delegate to the child owner.' },
5619
+ { validatorId: 'widget-key-not-array-index', level: 'error', code: 'PAGE_BUILDER_WIDGET_KEY_INDEX', description: 'Array index is not a stable widget identity.' },
5620
+ { validatorId: 'widget-exists', level: 'error', code: 'PAGE_BUILDER_WIDGET_EXISTS', description: 'Target widget must exist.' },
5621
+ { validatorId: 'destructive-removal-confirmed', level: 'error', code: 'PAGE_BUILDER_DESTRUCTIVE_CONFIRMED', description: 'Destructive removals require explicit confirmation.' },
5622
+ { validatorId: 'composition-links-cleaned', level: 'error', code: 'PAGE_BUILDER_LINKS_CLEANED', description: 'Removing a widget must remove dangling composition links.' },
5623
+ { validatorId: 'canvas-item-targets-existing-widget', level: 'error', code: 'PAGE_BUILDER_CANVAS_ITEM_TARGET', description: 'Moved canvas item must target an existing widget.' },
5624
+ { validatorId: 'canvas-overlap-policy-valid', level: 'warning', code: 'PAGE_BUILDER_CANVAS_OVERLAP', description: 'Canvas overlap policy must be respected.' },
5625
+ { validatorId: 'widget-shell-shape-valid', level: 'error', code: 'PAGE_BUILDER_SHELL_SHAPE', description: 'Widget shell config must match the shell editor contract.' },
5626
+ { validatorId: 'settings-panel-bridge-available', level: 'error', code: 'PAGE_BUILDER_SETTINGS_BRIDGE', description: 'Settings panel bridge must be available for shell/page editors.' },
5627
+ { validatorId: 'child-inputs-not-mutated', level: 'error', code: 'PAGE_BUILDER_CHILD_INPUTS_NOT_MUTATED', description: 'Shell edits cannot mutate child inputs.' },
5628
+ { validatorId: 'composition-link-id-unique', level: 'error', code: 'PAGE_BUILDER_LINK_ID_UNIQUE', description: 'Composition link ids must be unique.' },
5629
+ { validatorId: 'composition-endpoints-resolve', level: 'error', code: 'PAGE_BUILDER_LINK_ENDPOINTS', description: 'Composition endpoints must resolve to known widgets, ports and nested paths.' },
5630
+ { validatorId: 'nested-path-terminal-widget-key-required', level: 'error', code: 'PAGE_BUILDER_NESTED_PATH_WIDGET_KEY', description: 'Nested component-port paths must terminate at a stable widget key.' },
5631
+ { validatorId: 'json-logic-condition-valid', level: 'error', code: 'PAGE_BUILDER_JSON_LOGIC', description: 'Composition link condition must be valid Json Logic.' },
5632
+ { validatorId: 'no-legacy-connections-write', level: 'error', code: 'PAGE_BUILDER_NO_LEGACY_CONNECTIONS', description: 'Operations must write page.composition.links, not legacy connections.' },
5633
+ { validatorId: 'composition-link-exists', level: 'error', code: 'PAGE_BUILDER_LINK_EXISTS', description: 'Target composition link must exist.' },
5634
+ { validatorId: 'composition-links-still-valid', level: 'error', code: 'PAGE_BUILDER_LINKS_STILL_VALID', description: 'Remaining composition links must still validate after the operation.' },
5635
+ { validatorId: 'ui-composition-plan-valid', level: 'error', code: 'PAGE_BUILDER_UI_PLAN_VALID', description: 'UiCompositionPlan must match the intermediate agentic plan contract.' },
5636
+ { validatorId: 'compiled-page-valid', level: 'error', code: 'PAGE_BUILDER_COMPILED_PAGE_VALID', description: 'Compiled output must be a valid WidgetPageDefinition.' },
5637
+ { validatorId: 'widget-keys-unique', level: 'error', code: 'PAGE_BUILDER_WIDGET_KEYS_UNIQUE', description: 'Compiled plan must produce unique widget keys.' },
5638
+ { validatorId: 'state-path-valid', level: 'error', code: 'PAGE_BUILDER_STATE_PATH', description: 'State paths must be valid page state paths.' },
5639
+ { validatorId: 'state-layer-valid', level: 'error', code: 'PAGE_BUILDER_STATE_LAYER', description: 'State layer must be page or session.' },
5640
+ { validatorId: 'state-json-valid', level: 'error', code: 'PAGE_BUILDER_STATE_JSON', description: 'State values must be JSON-serializable.' },
5641
+ { validatorId: 'composition-state-links-still-valid', level: 'error', code: 'PAGE_BUILDER_STATE_LINKS', description: 'State edits must not break composition links.' },
5642
+ { validatorId: 'preview-result-valid', level: 'error', code: 'PAGE_BUILDER_PREVIEW_VALID', description: 'Agentic preview result must be structurally valid.' },
5643
+ { validatorId: 'compiled-page-patch-present', level: 'error', code: 'PAGE_BUILDER_PATCH_PAGE_PRESENT', description: 'Preview envelope must include compiledFormPatch.patch.page.' },
5644
+ { validatorId: 'no-envelope-runtime-persistence', level: 'error', code: 'PAGE_BUILDER_NO_ENVELOPE_PERSISTENCE', description: 'Runtime persistence cannot save the full preview envelope.' },
5645
+ { validatorId: 'runtime-page-valid', level: 'error', code: 'PAGE_BUILDER_RUNTIME_PAGE_VALID', description: 'Runtime page must remain renderable by praxis-dynamic-page.' },
5646
+ { validatorId: 'page-identity-complete', level: 'error', code: 'PAGE_BUILDER_PAGE_IDENTITY', description: 'Save requires component type, component id and scope identity.' },
5647
+ { validatorId: 'etag-policy-valid', level: 'error', code: 'PAGE_BUILDER_ETAG_POLICY', description: 'Save must respect backend ETag/If-Match policy.' },
5648
+ { validatorId: 'child-manifest-available', level: 'error', code: 'PAGE_BUILDER_CHILD_MANIFEST_AVAILABLE', description: 'Delegated child component must expose an authoring manifest or configEditor.' },
5649
+ { validatorId: 'child-operation-known', level: 'error', code: 'PAGE_BUILDER_CHILD_OPERATION_KNOWN', description: 'Delegated child operation id must be known.' },
5650
+ { validatorId: 'component-config-editor-respected', level: 'error', code: 'PAGE_BUILDER_CONFIG_EDITOR_RESPECTED', description: 'ComponentDocMeta.configEditor must remain the source for child input editing.' },
5651
+ { validatorId: 'no-local-child-input-write', level: 'error', code: 'PAGE_BUILDER_NO_LOCAL_CHILD_INPUT_WRITE', description: 'Page-builder must not define child component input semantics locally.' },
5652
+ ],
5653
+ roundTripRequirements: [
5654
+ 'WidgetPageDefinition is the canonical persisted runtime page shape for page-builder.',
5655
+ 'UiCompositionPlan is an intermediate agentic plan and must compile before local preview/apply.',
5656
+ 'Widget keys and composition link ids are stable identities; array indexes are never canonical identities.',
5657
+ 'Persisted wiring uses page.composition.links and must not write legacy connections.',
5658
+ 'Nested component ports use component-port refs with nestedPath ending in a stable widget key.',
5659
+ 'Child widget definition.inputs edits delegate to the child manifest or ComponentDocMeta.configEditor.',
5660
+ 'Agentic preview applies only compiledFormPatch.patch.page to runtime; the full patch envelope is diagnostics/audit data.',
5661
+ 'Save uses pageIdentity and ETag policy from praxis-config-starter; pageIdentity is not persisted inside WidgetPageDefinition.',
5662
+ 'Settings panel apply/save/reset/reopen must preserve page settings and widget shell config without hidden normalization drift.',
5663
+ ],
5664
+ examples: [
5665
+ {
5666
+ id: 'configure-dashboard-page',
5667
+ request: 'Set this page title to Payroll Overview and store payrollPeriod in the page context.',
5668
+ operationId: 'page.configure',
5669
+ params: { title: 'Payroll Overview', context: { payrollPeriod: 'current' } },
5670
+ isPositive: true,
5671
+ },
5672
+ {
5673
+ id: 'configure-canvas-grid',
5674
+ request: 'Use a 12 column dashboard grid with compact gaps.',
5675
+ operationId: 'canvas.configure',
5676
+ target: 'canvas',
5677
+ params: { columns: 12, rowUnit: 8, gap: 8 },
5678
+ isPositive: true,
5679
+ },
5680
+ {
5681
+ id: 'add-employees-table-widget',
5682
+ request: 'Add an employees table widget to the left half of the first row.',
5683
+ operationId: 'widget.add',
5684
+ target: 'employees-table',
5685
+ params: { widgetKey: 'employees-table', componentId: 'praxis-table', canvasItem: { x: 0, y: 0, w: 6, h: 8 } },
5686
+ isPositive: true,
5687
+ },
5688
+ {
5689
+ id: 'remove-summary-card',
5690
+ request: 'Remove the summary-card widget and its links.',
5691
+ operationId: 'widget.remove',
5692
+ target: 'summary-card',
5693
+ params: { widgetKey: 'summary-card', confirmed: true },
5694
+ isPositive: true,
5695
+ },
5696
+ {
5697
+ id: 'move-payroll-chart',
5698
+ request: 'Move payroll-chart to the right column and make it taller.',
5699
+ operationId: 'widget.moveResize',
5700
+ target: 'payroll-chart',
5701
+ params: { widgetKey: 'payroll-chart', canvasItem: { x: 6, y: 0, w: 6, h: 10 } },
5702
+ isPositive: true,
5703
+ },
5704
+ {
5705
+ id: 'configure-widget-shell',
5706
+ request: 'Rename the employees widget card and enable the refresh shell action.',
5707
+ operationId: 'widget.shell.configure',
5708
+ target: 'employees-table',
5709
+ params: { widgetKey: 'employees-table', shell: { title: 'Employees', actions: [{ id: 'refresh' }] } },
5710
+ isPositive: true,
5711
+ },
5712
+ {
5713
+ id: 'link-department-selection-to-table',
5714
+ request: 'When department-list selection changes, filter employees-table by department id.',
5715
+ operationId: 'composition.link.add',
5716
+ target: 'department-selection-to-employees',
5717
+ params: { linkId: 'department-selection-to-employees', from: { widget: 'department-list', port: 'selection' }, to: { widget: 'employees-table', port: 'filter' } },
5718
+ isPositive: true,
5719
+ },
5720
+ {
5721
+ id: 'remove-obsolete-link',
5722
+ request: 'Remove the old payroll-filter-link composition link.',
5723
+ operationId: 'composition.link.remove',
5724
+ target: 'payroll-filter-link',
5725
+ params: { linkId: 'payroll-filter-link', confirmed: true },
5726
+ isPositive: true,
5727
+ },
5728
+ {
5729
+ id: 'compile-resource-dashboard-plan',
5730
+ request: 'Compile the resource-dashboard plan returned by the assistant into a renderable page preview.',
5731
+ operationId: 'composition.plan.compile',
5732
+ target: 'preview-payroll-dashboard',
5733
+ params: { uiCompositionPlan: { layoutPreset: 'resource-dashboard' } },
5734
+ isPositive: true,
5735
+ },
5736
+ {
5737
+ id: 'set-selected-department-state',
5738
+ request: 'Store selectedDepartmentId in page state for downstream links.',
5739
+ operationId: 'state.set',
5740
+ target: 'state.selectedDepartmentId',
5741
+ params: { path: 'selectedDepartmentId', value: 'sales', layer: 'page' },
5742
+ isPositive: true,
5743
+ },
5744
+ {
5745
+ id: 'apply-agentic-preview',
5746
+ request: 'Apply the current assistant preview to the local page.',
5747
+ operationId: 'page.preview.apply',
5748
+ target: 'preview-current',
5749
+ params: { previewId: 'preview-current' },
5750
+ isPositive: true,
5751
+ },
5752
+ {
5753
+ id: 'persist-page',
5754
+ request: 'Save this page configuration using the current ETag.',
5755
+ operationId: 'page.persist.save',
5756
+ target: 'tenant-dashboard-page',
5757
+ params: { pageIdentity: { componentType: 'page-builder', componentId: 'tenant-dashboard', scope: 'tenant' }, etag: '"v7"' },
5758
+ isPositive: true,
5759
+ },
5760
+ {
5761
+ id: 'delegate-table-column-edit',
5762
+ request: 'Add a salary column to the employees table widget.',
5763
+ operationId: 'childOperation.delegate',
5764
+ target: 'employees-table',
5765
+ params: { widgetKey: 'employees-table', childComponentId: 'praxis-table', childOperationId: 'column.add', reason: 'TableConfig is owned by praxis-table.' },
5766
+ isPositive: true,
5767
+ },
5768
+ {
5769
+ id: 'reject-direct-dynamic-form-field-write',
5770
+ request: 'Directly write a new dynamic-form field into the widget inputs from page-builder.',
5771
+ operationId: 'childOperation.delegate',
5772
+ target: 'employee-form',
5773
+ params: { widgetKey: 'employee-form', childComponentId: 'praxis-dynamic-form', childOperationId: 'field.add', reason: 'Dynamic form fields are delegated.' },
5774
+ isPositive: false,
5775
+ },
5776
+ {
5777
+ id: 'reject-legacy-connections',
5778
+ request: 'Create the widget link under the old connections collection.',
5779
+ operationId: 'composition.link.add',
5780
+ target: 'legacy-connection',
5781
+ params: { linkId: 'legacy-connection', collection: 'connections' },
5782
+ isPositive: false,
5783
+ },
5784
+ ],
5785
+ };
5786
+
5217
5787
  function compileUiCompositionPlan(plan) {
5218
5788
  const diagnostics = validateUiCompositionPlan(plan);
5219
5789
  if (diagnostics.length) {
@@ -8216,4 +8786,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
8216
8786
  * Generated bundle index. Do not edit.
8217
8787
  */
8218
8788
 
8219
- export { ComponentPaletteDialogComponent, ConfirmDialogComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, WidgetShellEditorComponent, clearWidgetAiCatalogs, getPageAiCatalog, getWidgetAiCapabilities, providePageBuilderWidgetAiCatalogs, registerWidgetAiCatalog, registerWidgetAiCatalogs };
8789
+ export { ComponentPaletteDialogComponent, ConfirmDialogComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, WidgetShellEditorComponent, clearWidgetAiCatalogs, getPageAiCatalog, getWidgetAiCapabilities, providePageBuilderWidgetAiCatalogs, registerWidgetAiCatalog, registerWidgetAiCatalogs };
package/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { MatDialogRef, MatDialog } from '@angular/material/dialog';
2
2
  import * as _angular_core from '@angular/core';
3
3
  import { EventEmitter, OnInit, WritableSignal, InjectionToken, Provider, OnChanges, Type, SimpleChanges } from '@angular/core';
4
- import { ComponentDocMeta, ComponentMetadataRegistry, WidgetShellConfig, WidgetShellAction, SettingsValueProvider as SettingsValueProvider$1, WidgetPageDefinition, WidgetPageGroupingDefinition, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, PageIdentity, WidgetStateNode, WidgetDerivedStateNode, AiCapabilityCategory, AiValueKind, AiCapability, AiCapabilityCatalog, WidgetInstance, WidgetPageStateInput, CompositionLink, DynamicWidgetPageComponent, SettingsPanelBridge } from '@praxisui/core';
4
+ import { ComponentDocMeta, ComponentMetadataRegistry, WidgetShellConfig, WidgetShellAction, SettingsValueProvider as SettingsValueProvider$1, WidgetPageDefinition, WidgetPageGroupingDefinition, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, PageIdentity, WidgetStateNode, WidgetDerivedStateNode, AiCapabilityCategory, AiValueKind, AiCapability, AiCapabilityCatalog, WidgetInstance, WidgetPageStateInput, CompositionLink, ComponentAuthoringManifest, DynamicWidgetPageComponent, SettingsPanelBridge } from '@praxisui/core';
5
5
  export { WidgetShellComponent } from '@praxisui/core';
6
6
  import * as rxjs from 'rxjs';
7
7
  import { BehaviorSubject, Observable } from 'rxjs';
8
8
  import { FormGroup, FormControl, FormArray } from '@angular/forms';
9
9
  import { SettingsValueProvider } from '@praxisui/settings-panel';
10
- import { AgenticAuthoringPlanRequestContract, AgenticAuthoringCandidateContract, AiJsonValue, AgenticAuthoringQuickReplyContract, AgenticAuthoringPendingClarificationContract, AiJsonObject, AgenticAuthoringComponentCapabilitiesResultContract, AgenticAuthoringComponentCapabilityCatalogContract, AgenticAuthoringComponentCapabilityContract, AgenticAuthoringComponentFieldAliasContract, AgenticAuthoringComponentCapabilityExampleContract, AgenticAuthoringAttachmentSummaryContract, AgenticAuthoringIntentResolutionRequestContract, AgenticAuthoringConversationMessageContract, AgenticAuthoringResourceCandidatesRequestContract, AgenticAuthoringResourceCandidatesResultContract, AgenticAuthoringTurnStreamStartResponseContract, AgenticAuthoringTurnStreamEnvelopeContract, PraxisAssistantShellMessage, PraxisAssistantShellQuickReply, PraxisAssistantShellAttachment, PraxisAssistantShellLayout, PraxisAssistantShellLabels, PraxisAssistantShellState, PraxisAssistantShellContextItem } from '@praxisui/ai';
10
+ import { AgenticAuthoringPlanRequestContract, AgenticAuthoringCandidateContract, AiJsonValue, AgenticAuthoringQuickReplyContract, AgenticAuthoringPendingClarificationContract, AiJsonObject, AgenticAuthoringComponentCapabilitiesResultContract, AgenticAuthoringComponentCapabilityCatalogContract, AgenticAuthoringComponentCapabilityContract, AgenticAuthoringComponentFieldAliasContract, AgenticAuthoringComponentCapabilityExampleContract, AgenticAuthoringAttachmentSummaryContract, AgenticAuthoringIntentResolutionRequestContract, AgenticAuthoringConversationMessageContract, AiContextHintsContract, AgenticAuthoringResourceCandidatesRequestContract, AgenticAuthoringResourceCandidatesResultContract, AgenticAuthoringTurnStreamStartResponseContract, AgenticAuthoringTurnStreamEnvelopeContract, PraxisAssistantShellMessage, PraxisAssistantShellQuickReply, PraxisAssistantShellAttachment, PraxisAssistantShellLayout, PraxisAssistantShellLabels, PraxisAssistantShellState, PraxisAssistantShellContextItem } from '@praxisui/ai';
11
11
 
12
12
  declare const PLACEHOLDER = 1;
13
13
 
@@ -589,7 +589,7 @@ interface PageBuilderAgenticAuthoringTurnStreamRequest {
589
589
  pendingClarification?: PageBuilderAgenticAuthoringPendingClarification | null;
590
590
  componentCapabilities?: PageBuilderAgenticAuthoringComponentCapabilitiesResult | null;
591
591
  attachmentSummaries?: PageBuilderAgenticAuthoringAttachmentSummary[];
592
- contextHints?: AiJsonObject | null;
592
+ contextHints?: AiContextHintsContract | null;
593
593
  }
594
594
  type PageBuilderAgenticAuthoringConversationMessage = AgenticAuthoringConversationMessageContract;
595
595
  type PageBuilderAgenticAuthoringPendingClarification = AgenticAuthoringPendingClarificationContract;
@@ -748,7 +748,9 @@ declare class PageBuilderAgenticAuthoringService {
748
748
  private connectTurnStream;
749
749
  private streamConnectionError;
750
750
  private buildStreamUrl;
751
+ private buildStreamProbeUrl;
751
752
  private createEventSource;
753
+ private probeTurnStreamEndpoint;
752
754
  private formatEtag;
753
755
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<PageBuilderAgenticAuthoringService, never>;
754
756
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<PageBuilderAgenticAuthoringService>;
@@ -812,6 +814,8 @@ declare const PAGE_BUILDER_WIDGET_AI_CATALOGS: InjectionToken<Record<string, AiC
812
814
 
813
815
  declare function providePageBuilderWidgetAiCatalogs(): Provider;
814
816
 
817
+ declare const PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST: ComponentAuthoringManifest;
818
+
815
819
  interface AgenticAuthoringQuickReply extends PraxisAssistantShellQuickReply {
816
820
  kind: string;
817
821
  }
@@ -917,5 +921,5 @@ declare class DynamicPageBuilderComponent implements OnChanges {
917
921
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicPageBuilderComponent, "praxis-dynamic-page-builder", never, { "page": { "alias": "page"; "required": false; }; "context": { "alias": "context"; "required": false; }; "strictValidation": { "alias": "strictValidation"; "required": false; }; "autoPersist": { "alias": "autoPersist"; "required": false; }; "enableCustomization": { "alias": "enableCustomization"; "required": false; }; "showSettingsButton": { "alias": "showSettingsButton"; "required": false; }; "pageIdentity": { "alias": "pageIdentity"; "required": false; }; "componentInstanceId": { "alias": "componentInstanceId"; "required": false; }; "pageEditorComponent": { "alias": "pageEditorComponent"; "required": false; }; "enableAgenticAuthoring": { "alias": "enableAgenticAuthoring"; "required": false; }; "agenticAuthoringProvider": { "alias": "agenticAuthoringProvider"; "required": false; }; "agenticAuthoringModel": { "alias": "agenticAuthoringModel"; "required": false; }; "agenticAuthoringApiKey": { "alias": "agenticAuthoringApiKey"; "required": false; }; "agenticAuthoringComponentId": { "alias": "agenticAuthoringComponentId"; "required": false; }; "agenticAuthoringScope": { "alias": "agenticAuthoringScope"; "required": false; }; "agenticAuthoringEtag": { "alias": "agenticAuthoringEtag"; "required": false; }; "agenticAuthoringIncludeLlmDiagnostics": { "alias": "agenticAuthoringIncludeLlmDiagnostics"; "required": false; }; "agenticAuthoringEnableStreaming": { "alias": "agenticAuthoringEnableStreaming"; "required": false; }; }, { "pageChange": "pageChange"; "agenticAuthoringApplied": "agenticAuthoringApplied"; }, never, never, true, never>;
918
922
  }
919
923
 
920
- export { ComponentPaletteDialogComponent, ConfirmDialogComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, WidgetShellEditorComponent, clearWidgetAiCatalogs, getPageAiCatalog, getWidgetAiCapabilities, providePageBuilderWidgetAiCatalogs, registerWidgetAiCatalog, registerWidgetAiCatalogs };
924
+ export { ComponentPaletteDialogComponent, ConfirmDialogComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, WidgetShellEditorComponent, clearWidgetAiCatalogs, getPageAiCatalog, getWidgetAiCapabilities, providePageBuilderWidgetAiCatalogs, registerWidgetAiCatalog, registerWidgetAiCatalogs };
921
925
  export type { Capability, CapabilityCatalog, CapabilityCategory, ComponentPaletteData, ConfirmDialogData, PageBuilderAgenticAuthoringAttachmentSummary, PageBuilderAgenticAuthoringCandidate, PageBuilderAgenticAuthoringComponentCapabilitiesResult, PageBuilderAgenticAuthoringComponentCapability, PageBuilderAgenticAuthoringComponentCapabilityCatalog, PageBuilderAgenticAuthoringComponentCapabilityExample, PageBuilderAgenticAuthoringComponentFieldAlias, PageBuilderAgenticAuthoringConversationMessage, PageBuilderAgenticAuthoringEventSource, PageBuilderAgenticAuthoringEventSourceFactory, PageBuilderAgenticAuthoringGateResult, PageBuilderAgenticAuthoringIntentResolutionRequest, PageBuilderAgenticAuthoringIntentResolutionResult, PageBuilderAgenticAuthoringOptions, PageBuilderAgenticAuthoringPendingClarification, PageBuilderAgenticAuthoringPromptRequest, PageBuilderAgenticAuthoringProvider, PageBuilderAgenticAuthoringQuickReply, PageBuilderAgenticAuthoringResourceCandidatesRequest, PageBuilderAgenticAuthoringResourceCandidatesResult, PageBuilderAgenticAuthoringTarget, PageBuilderAgenticAuthoringTurnStreamConnectionError, PageBuilderAgenticAuthoringTurnStreamEvent, PageBuilderAgenticAuthoringTurnStreamRequest, PageBuilderAgenticAuthoringTurnStreamStartResponse, PageBuilderApplyRequest, PageBuilderApplyResult, PageBuilderCompiledFormPatch, PageBuilderMinimalFormPlanResult, PageBuilderPreviewDiagnostics, PageBuilderPreviewResult, ValueKind };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/page-builder",
3
- "version": "8.0.0-beta.11",
3
+ "version": "8.0.0-beta.13",
4
4
  "description": "Page and widget builder utilities for Praxis UI (grid, dynamic widgets, editors).",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^20.0.0",
@@ -8,9 +8,9 @@
8
8
  "@angular/forms": "^20.0.0",
9
9
  "@angular/cdk": "^20.0.0",
10
10
  "@angular/material": "^20.0.0",
11
- "@praxisui/ai": "^8.0.0-beta.11",
12
- "@praxisui/core": "^8.0.0-beta.11",
13
- "@praxisui/settings-panel": "^8.0.0-beta.11"
11
+ "@praxisui/ai": "^8.0.0-beta.13",
12
+ "@praxisui/core": "^8.0.0-beta.13",
13
+ "@praxisui/settings-panel": "^8.0.0-beta.13"
14
14
  },
15
15
  "dependencies": {
16
16
  "tslib": "^2.3.0"