@praxisui/page-builder 8.0.0-beta.1 → 8.0.0-beta.12
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 +29 -3
- package/fesm2022/praxisui-page-builder.mjs +1490 -66
- package/index.d.ts +85 -11
- package/package.json +4 -4
|
@@ -25,13 +25,13 @@ import * as i6 from '@angular/material/divider';
|
|
|
25
25
|
import { MatDividerModule } from '@angular/material/divider';
|
|
26
26
|
import * as i6$1 from '@angular/material/select';
|
|
27
27
|
import { MatSelectModule } from '@angular/material/select';
|
|
28
|
-
import { BehaviorSubject, merge, firstValueFrom } from 'rxjs';
|
|
28
|
+
import { BehaviorSubject, merge, switchMap, Observable, firstValueFrom, from, concatMap, catchError, of, lastValueFrom, tap } from 'rxjs';
|
|
29
29
|
import { SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
|
|
30
30
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
31
31
|
import * as i7 from '@angular/material/tabs';
|
|
32
32
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
33
33
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
34
|
-
import { PraxisAssistantTurnOrchestratorService, PraxisAiAssistantShellComponent } from '@praxisui/ai';
|
|
34
|
+
import { AI_STREAM_EVENT_TYPES, PraxisAssistantTurnOrchestratorService, PraxisAiAssistantShellComponent } from '@praxisui/ai';
|
|
35
35
|
|
|
36
36
|
const PLACEHOLDER = 1;
|
|
37
37
|
|
|
@@ -2345,6 +2345,7 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2345
2345
|
'praxis.pageBuilder.agentic.quickReplies.formSuggestion': 'Form',
|
|
2346
2346
|
'praxis.pageBuilder.agentic.quickReplies.masterDetailSuggestion': 'Master detail',
|
|
2347
2347
|
'praxis.pageBuilder.agentic.quickReplies.confirmPromptSuffix': 'Confirmed:',
|
|
2348
|
+
'praxis.pageBuilder.agentic.quickReplies.resourceSelectionPrompt': 'Use {label} ({resourcePath}) as the data source.',
|
|
2348
2349
|
'praxis.pageBuilder.agentic.attachments.currentPage': 'Current page',
|
|
2349
2350
|
'praxis.pageBuilder.agentic.suggestionPrompts.payrollExecutiveDashboard': 'Create a payroll dashboard with KPIs, payroll by department, monthly evolution, and a detail table.',
|
|
2350
2351
|
'praxis.pageBuilder.agentic.suggestionPrompts.payrollDepartmentDrilldown': 'Create a payroll dashboard with a department chart that filters a detail table when a bar is selected.',
|
|
@@ -2358,6 +2359,12 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2358
2359
|
'praxis.pageBuilder.agentic.exploratory.capability.table': 'tables',
|
|
2359
2360
|
'praxis.pageBuilder.agentic.exploratory.capability.form': 'forms',
|
|
2360
2361
|
'praxis.pageBuilder.agentic.exploratory.capability.masterDetail': 'master-detail pages',
|
|
2362
|
+
'praxis.pageBuilder.agentic.resourceDiscovery.found': 'I found APIs that can feed this screen. Choose one before generating the preview.',
|
|
2363
|
+
'praxis.pageBuilder.agentic.resourceDiscovery.empty': 'I did not find a matching API yet. Describe the business data this screen should use.',
|
|
2364
|
+
'praxis.pageBuilder.agentic.resourceDiscovery.useResource': 'Use {resourcePath}',
|
|
2365
|
+
'praxis.pageBuilder.agentic.diagnostics.title': 'LLM diagnostics',
|
|
2366
|
+
'praxis.pageBuilder.agentic.diagnostics.badge': 'Debug',
|
|
2367
|
+
'praxis.pageBuilder.agentic.diagnostics.description': 'Prompt, context bundle, and tool catalog returned by the backend for this turn.',
|
|
2361
2368
|
'praxis.pageBuilder.agentic.dragHandleAria': 'Move AI assistant',
|
|
2362
2369
|
'praxis.pageBuilder.agentic.resizeHandleAria': 'Resize AI assistant',
|
|
2363
2370
|
'praxis.pageBuilder.agentic.resizeHandleTooltip': 'Resize',
|
|
@@ -2366,7 +2373,12 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2366
2373
|
'praxis.pageBuilder.agentic.status.resolvingIntent': 'Resolving intent...',
|
|
2367
2374
|
'praxis.pageBuilder.agentic.status.waitingRevision': 'Refine your prompt and preview again.',
|
|
2368
2375
|
'praxis.pageBuilder.agentic.status.cancelled': 'Request cancelled.',
|
|
2376
|
+
'praxis.pageBuilder.agentic.status.contextBundle': 'Preparing context...',
|
|
2377
|
+
'praxis.pageBuilder.agentic.status.resourceDiscovery': 'Finding API resources...',
|
|
2378
|
+
'praxis.pageBuilder.agentic.status.resourceDiscoveryBackend': 'Found API resources in the backend catalog.',
|
|
2379
|
+
'praxis.pageBuilder.agentic.status.refinedCandidates': 'Reviewing the retrieved resources with the AI...',
|
|
2369
2380
|
'praxis.pageBuilder.agentic.status.previewing': 'Generating preview...',
|
|
2381
|
+
'praxis.pageBuilder.agentic.status.previewCompile': 'Compiling preview...',
|
|
2370
2382
|
'praxis.pageBuilder.agentic.status.previewReady': 'Preview applied to the page.',
|
|
2371
2383
|
'praxis.pageBuilder.agentic.status.acceptedAddLocalField': 'Local field added to the form.',
|
|
2372
2384
|
'praxis.pageBuilder.agentic.status.acceptedRemoveLocalField': 'Local field removed from the form.',
|
|
@@ -2377,8 +2389,12 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2377
2389
|
'praxis.pageBuilder.agentic.errors.componentId': 'Configure a component id before saving.',
|
|
2378
2390
|
'praxis.pageBuilder.agentic.errors.invalidPreview': 'Generated preview is invalid.',
|
|
2379
2391
|
'praxis.pageBuilder.agentic.errors.intentResolution': 'Intent could not be resolved.',
|
|
2392
|
+
'praxis.pageBuilder.agentic.errors.streamTimeout': 'The assistant took too long to finish this request. Try again with a narrower request or review the active context.',
|
|
2393
|
+
'praxis.pageBuilder.agentic.errors.streamProcessing': 'The assistant could not finish this authoring request. Try again or ask support to review the diagnostics.',
|
|
2394
|
+
'praxis.pageBuilder.agentic.errors.streamConnection': 'The assistant stream was interrupted before the turn finished. Try again or ask support to check the stream connection.',
|
|
2380
2395
|
'praxis.pageBuilder.agentic.errors.duplicateField': 'This field already exists in the selected form.',
|
|
2381
2396
|
'praxis.pageBuilder.agentic.errors.nonLocalFieldRemoval': 'Only local fields created by AI can be removed in this flow.',
|
|
2397
|
+
'praxis.pageBuilder.agentic.errors.invalidTableContract': 'I found the data source, but the generated plan used properties that are incompatible with the table component. I will adjust it to use only supported fields.',
|
|
2382
2398
|
'praxis.pageBuilder.agentic.errors.generic': 'AI authoring failed.',
|
|
2383
2399
|
};
|
|
2384
2400
|
|
|
@@ -2586,6 +2602,7 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
2586
2602
|
'praxis.pageBuilder.agentic.quickReplies.formSuggestion': 'Formulário',
|
|
2587
2603
|
'praxis.pageBuilder.agentic.quickReplies.masterDetailSuggestion': 'Master detail',
|
|
2588
2604
|
'praxis.pageBuilder.agentic.quickReplies.confirmPromptSuffix': 'Confirmado:',
|
|
2605
|
+
'praxis.pageBuilder.agentic.quickReplies.resourceSelectionPrompt': 'Usar {label} ({resourcePath}) como fonte de dados.',
|
|
2589
2606
|
'praxis.pageBuilder.agentic.attachments.currentPage': 'Página atual',
|
|
2590
2607
|
'praxis.pageBuilder.agentic.suggestionPrompts.payrollExecutiveDashboard': 'Crie um dashboard de folha de pagamento com KPIs, folha por departamento, evolução mensal e tabela de detalhes.',
|
|
2591
2608
|
'praxis.pageBuilder.agentic.suggestionPrompts.payrollDepartmentDrilldown': 'Crie um dashboard de folha de pagamento com gráfico por departamento que filtre uma tabela detalhada ao selecionar uma barra.',
|
|
@@ -2599,6 +2616,12 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
2599
2616
|
'praxis.pageBuilder.agentic.exploratory.capability.table': 'tabelas',
|
|
2600
2617
|
'praxis.pageBuilder.agentic.exploratory.capability.form': 'formulários',
|
|
2601
2618
|
'praxis.pageBuilder.agentic.exploratory.capability.masterDetail': 'páginas master-detail',
|
|
2619
|
+
'praxis.pageBuilder.agentic.resourceDiscovery.found': 'Encontrei APIs que podem alimentar esta tela. Escolha uma antes de gerar a pré-visualização.',
|
|
2620
|
+
'praxis.pageBuilder.agentic.resourceDiscovery.empty': 'Ainda não encontrei uma API correspondente. Descreva quais dados de negócio esta tela deve usar.',
|
|
2621
|
+
'praxis.pageBuilder.agentic.resourceDiscovery.useResource': 'Usar {resourcePath}',
|
|
2622
|
+
'praxis.pageBuilder.agentic.diagnostics.title': 'Diagnóstico do LLM',
|
|
2623
|
+
'praxis.pageBuilder.agentic.diagnostics.badge': 'Debug',
|
|
2624
|
+
'praxis.pageBuilder.agentic.diagnostics.description': 'Prompt, pacote de contexto e catálogo de ferramentas retornados pelo backend para este turno.',
|
|
2602
2625
|
'praxis.pageBuilder.agentic.dragHandleAria': 'Mover assistente de IA',
|
|
2603
2626
|
'praxis.pageBuilder.agentic.resizeHandleAria': 'Redimensionar assistente de IA',
|
|
2604
2627
|
'praxis.pageBuilder.agentic.resizeHandleTooltip': 'Redimensionar',
|
|
@@ -2607,7 +2630,12 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
2607
2630
|
'praxis.pageBuilder.agentic.status.resolvingIntent': 'Resolvendo intenção...',
|
|
2608
2631
|
'praxis.pageBuilder.agentic.status.waitingRevision': 'Ajuste o prompt e pré-visualize novamente.',
|
|
2609
2632
|
'praxis.pageBuilder.agentic.status.cancelled': 'Solicitação cancelada.',
|
|
2633
|
+
'praxis.pageBuilder.agentic.status.contextBundle': 'Preparando contexto...',
|
|
2634
|
+
'praxis.pageBuilder.agentic.status.resourceDiscovery': 'Buscando recursos de API...',
|
|
2635
|
+
'praxis.pageBuilder.agentic.status.resourceDiscoveryBackend': 'Encontrei recursos de API no catálogo do backend.',
|
|
2636
|
+
'praxis.pageBuilder.agentic.status.refinedCandidates': 'Estou revisando os recursos encontrados com a IA...',
|
|
2610
2637
|
'praxis.pageBuilder.agentic.status.previewing': 'Gerando pré-visualização...',
|
|
2638
|
+
'praxis.pageBuilder.agentic.status.previewCompile': 'Compilando pré-visualização...',
|
|
2611
2639
|
'praxis.pageBuilder.agentic.status.previewReady': 'Pré-visualização aplicada à página.',
|
|
2612
2640
|
'praxis.pageBuilder.agentic.status.acceptedAddLocalField': 'Campo local adicionado ao formulário.',
|
|
2613
2641
|
'praxis.pageBuilder.agentic.status.acceptedRemoveLocalField': 'Campo local removido do formulário.',
|
|
@@ -2618,8 +2646,12 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
2618
2646
|
'praxis.pageBuilder.agentic.errors.componentId': 'Configure um id de componente antes de salvar.',
|
|
2619
2647
|
'praxis.pageBuilder.agentic.errors.invalidPreview': 'A pré-visualização gerada é inválida.',
|
|
2620
2648
|
'praxis.pageBuilder.agentic.errors.intentResolution': 'A intenção não pôde ser resolvida.',
|
|
2649
|
+
'praxis.pageBuilder.agentic.errors.streamTimeout': 'Ainda não consegui concluir este pedido. Tente novamente com um pedido mais específico ou revise o contexto ativo.',
|
|
2650
|
+
'praxis.pageBuilder.agentic.errors.streamProcessing': 'Não consegui concluir este pedido de autoria. Tente novamente ou peça ao suporte para revisar o diagnóstico.',
|
|
2651
|
+
'praxis.pageBuilder.agentic.errors.streamConnection': 'O stream do assistente foi interrompido antes de concluir o turno. Tente novamente ou acione o suporte para verificar a conexão do stream.',
|
|
2621
2652
|
'praxis.pageBuilder.agentic.errors.duplicateField': 'Este campo já existe no formulário selecionado.',
|
|
2622
2653
|
'praxis.pageBuilder.agentic.errors.nonLocalFieldRemoval': 'Somente campos locais criados pela IA podem ser removidos neste fluxo.',
|
|
2654
|
+
'praxis.pageBuilder.agentic.errors.invalidTableContract': 'Encontrei a fonte de dados, mas o plano gerado usou propriedades incompatíveis com o componente de tabela. Vou ajustar para usar apenas os campos suportados.',
|
|
2623
2655
|
'praxis.pageBuilder.agentic.errors.generic': 'Falha na autoria com IA.',
|
|
2624
2656
|
};
|
|
2625
2657
|
|
|
@@ -4778,6 +4810,9 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
4778
4810
|
'Taxonomia editorial: condition usa Json Logic canonico; transform usa pipeline declarativo; policy cobre apenas comportamento operacional.',
|
|
4779
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.',
|
|
4780
4812
|
'UiCompositionPlan usa widgets[].componentId e bindings declarativos. O JSON persistido continua sendo page.widgets[].definition.id e page.composition.links.',
|
|
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.',
|
|
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.',
|
|
4781
4816
|
'Payloads legados de layout ainda podem ser ingeridos, mas sao normalizados para canvas e nao devem ser reemitidos.',
|
|
4782
4817
|
'pageIdentity identifica o escopo de persistencia e nao pertence ao objeto page.',
|
|
4783
4818
|
],
|
|
@@ -4796,6 +4831,18 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
4796
4831
|
{ path: 'uiCompositionPlan.bindings[].id', category: 'connections', valueKind: 'string', description: 'Identificador estavel do binding planejado.' },
|
|
4797
4832
|
{ path: 'uiCompositionPlan.bindings[].from', category: 'connections', valueKind: 'object', description: 'Endpoint de origem: component-port ou state.' },
|
|
4798
4833
|
{ path: 'uiCompositionPlan.bindings[].to', category: 'connections', valueKind: 'object', description: 'Endpoint de destino: component-port ou state.' },
|
|
4834
|
+
{ path: 'uiCompositionPlan.bindings[].from.nestedPath', category: 'connections', valueKind: 'array', description: 'Caminho estruturado para porta nested de origem. Use apenas com endpoint component-port; o owner top-level continua em from.widget.' },
|
|
4835
|
+
{ path: 'uiCompositionPlan.bindings[].to.nestedPath', category: 'connections', valueKind: 'array', description: 'Caminho estruturado para porta nested de destino. Use apenas com endpoint component-port; o owner top-level continua em to.widget.' },
|
|
4836
|
+
{ path: 'uiCompositionPlan.bindings[].from.nestedPath[].kind', category: 'connections', valueKind: 'string', description: 'Tipo do segmento nested de origem, como tab, nav, link, expansion, panel ou widget. O ultimo segmento deve ser widget.' },
|
|
4837
|
+
{ path: 'uiCompositionPlan.bindings[].from.nestedPath[].id', category: 'connections', valueKind: 'string', description: 'Identificador estrutural estavel do container de origem no segmento nested, quando existir.' },
|
|
4838
|
+
{ path: 'uiCompositionPlan.bindings[].from.nestedPath[].key', category: 'connections', valueKind: 'string', description: 'Chave estavel do widget filho no segmento terminal widget de origem. Obrigatoria para endpoint nested persistido.', critical: true },
|
|
4839
|
+
{ path: 'uiCompositionPlan.bindings[].from.nestedPath[].index', category: 'connections', valueKind: 'number', description: 'Indice auxiliar de origem para contexto visual/diagnostico; nao use como identidade primaria persistida.' },
|
|
4840
|
+
{ path: 'uiCompositionPlan.bindings[].from.nestedPath[].componentType', category: 'connections', valueKind: 'string', description: 'Tipo do componente real do widget filho de origem, usado como verificacao auxiliar, nao como identidade primaria.' },
|
|
4841
|
+
{ path: 'uiCompositionPlan.bindings[].to.nestedPath[].kind', category: 'connections', valueKind: 'string', description: 'Tipo do segmento nested de destino, como tab, nav, link, expansion, panel ou widget. O ultimo segmento deve ser widget.' },
|
|
4842
|
+
{ path: 'uiCompositionPlan.bindings[].to.nestedPath[].id', category: 'connections', valueKind: 'string', description: 'Identificador estrutural estavel do container de destino no segmento nested, quando existir.' },
|
|
4843
|
+
{ path: 'uiCompositionPlan.bindings[].to.nestedPath[].key', category: 'connections', valueKind: 'string', description: 'Chave estavel do widget filho no segmento terminal widget de destino. Obrigatoria para endpoint nested persistido.', critical: true },
|
|
4844
|
+
{ path: 'uiCompositionPlan.bindings[].to.nestedPath[].index', category: 'connections', valueKind: 'number', description: 'Indice auxiliar de destino para contexto visual/diagnostico; nao use como identidade primaria persistida.' },
|
|
4845
|
+
{ path: 'uiCompositionPlan.bindings[].to.nestedPath[].componentType', category: 'connections', valueKind: 'string', description: 'Tipo do componente real do widget filho de destino, usado como verificacao auxiliar, nao como identidade primaria.' },
|
|
4799
4846
|
{ path: 'uiCompositionPlan.bindings[].intent', category: 'connections', valueKind: 'string', description: 'Intencao semantica do binding.' },
|
|
4800
4847
|
{ path: 'uiCompositionPlan.bindings[].condition', category: 'connections', valueKind: 'expression', description: 'Guarda opcional em Json Logic.' },
|
|
4801
4848
|
{ path: 'uiCompositionPlan.bindings[].transform', category: 'connections', valueKind: 'object', description: 'Transform declarativo: pick-path, query-context, template ou constant.' },
|
|
@@ -4847,6 +4894,10 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
4847
4894
|
{ path: 'page.composition.links[].id', category: 'connections', valueKind: 'string', description: 'Identificador estavel do link.' },
|
|
4848
4895
|
{ path: 'page.composition.links[].from', category: 'connections', valueKind: 'object', description: 'Endpoint de origem do link.' },
|
|
4849
4896
|
{ path: 'page.composition.links[].to', category: 'connections', valueKind: 'object', description: 'Endpoint de destino do link.' },
|
|
4897
|
+
{ path: 'page.composition.links[].from.ref.nestedPath', category: 'connections', valueKind: 'array', description: 'NestedPath canonico em endpoint component-port de origem persistido. Omita para top-level; nao serialize nestedPath: [].' },
|
|
4898
|
+
{ path: 'page.composition.links[].from.ref.nestedPath[].key', category: 'connections', valueKind: 'string', description: 'Chave estavel do widget filho no segmento terminal widget de origem. Obrigatoria para endpoint nested persistido.', critical: true },
|
|
4899
|
+
{ path: 'page.composition.links[].to.ref.nestedPath', category: 'connections', valueKind: 'array', description: 'NestedPath canonico em endpoint component-port de destino persistido. Omita para top-level; nao serialize nestedPath: [].' },
|
|
4900
|
+
{ path: 'page.composition.links[].to.ref.nestedPath[].key', category: 'connections', valueKind: 'string', description: 'Chave estavel do widget filho no segmento terminal widget de destino. Obrigatoria para endpoint nested persistido.', critical: true },
|
|
4850
4901
|
{ path: 'page.composition.links[].intent', category: 'connections', valueKind: 'string', description: 'Intencao semantica do link.' },
|
|
4851
4902
|
{ path: 'page.composition.links[].transform', category: 'connections', valueKind: 'object', description: 'Pipeline de transformacao do link.' },
|
|
4852
4903
|
{ path: 'page.composition.links[].condition', category: 'connections', valueKind: 'expression', description: 'Guarda semantica opcional do link, expressa como um unico AST Json Logic canonico.' },
|
|
@@ -5026,6 +5077,12 @@ class PageBuilderAgenticAuthoringService {
|
|
|
5026
5077
|
resolveIntent(request) {
|
|
5027
5078
|
return this.http.post(`${this.baseUrl}/intent-resolution`, request, { headers: this.buildHeaders() });
|
|
5028
5079
|
}
|
|
5080
|
+
searchResourceCandidates(request) {
|
|
5081
|
+
return this.http.post(`${this.baseUrl}/resource-candidates`, request, { headers: this.buildHeaders() });
|
|
5082
|
+
}
|
|
5083
|
+
streamTurn(request) {
|
|
5084
|
+
return this.http.post(`${this.baseUrl}/turn/stream/start`, request, { headers: this.buildHeaders() }).pipe(switchMap((start) => this.connectTurnStream(start)));
|
|
5085
|
+
}
|
|
5029
5086
|
applyPage(request) {
|
|
5030
5087
|
const { ifMatch, ...body } = request;
|
|
5031
5088
|
return this.http.post(`${this.baseUrl}/page-apply`, {
|
|
@@ -5055,6 +5112,97 @@ class PageBuilderAgenticAuthoringService {
|
|
|
5055
5112
|
}
|
|
5056
5113
|
return new HttpHeaders(merged);
|
|
5057
5114
|
}
|
|
5115
|
+
connectTurnStream(start) {
|
|
5116
|
+
return new Observable((observer) => {
|
|
5117
|
+
const url = this.buildStreamUrl(start);
|
|
5118
|
+
let source;
|
|
5119
|
+
let closed = false;
|
|
5120
|
+
let connectionErrorTimer = null;
|
|
5121
|
+
const connectionErrorGraceMs = Math.max(0, this.options?.streamConnectionErrorGraceMs ?? 1500);
|
|
5122
|
+
try {
|
|
5123
|
+
source = this.createEventSource(url);
|
|
5124
|
+
}
|
|
5125
|
+
catch (error) {
|
|
5126
|
+
observer.error(this.streamConnectionError(error));
|
|
5127
|
+
return undefined;
|
|
5128
|
+
}
|
|
5129
|
+
const clearConnectionErrorTimer = () => {
|
|
5130
|
+
if (connectionErrorTimer) {
|
|
5131
|
+
clearTimeout(connectionErrorTimer);
|
|
5132
|
+
connectionErrorTimer = null;
|
|
5133
|
+
}
|
|
5134
|
+
};
|
|
5135
|
+
const closeSource = () => {
|
|
5136
|
+
if (closed) {
|
|
5137
|
+
return;
|
|
5138
|
+
}
|
|
5139
|
+
closed = true;
|
|
5140
|
+
clearConnectionErrorTimer();
|
|
5141
|
+
source.close();
|
|
5142
|
+
};
|
|
5143
|
+
const failConnection = (event) => {
|
|
5144
|
+
if (closed) {
|
|
5145
|
+
return;
|
|
5146
|
+
}
|
|
5147
|
+
observer.error(this.streamConnectionError(event));
|
|
5148
|
+
closeSource();
|
|
5149
|
+
};
|
|
5150
|
+
const handleMessage = (event) => {
|
|
5151
|
+
try {
|
|
5152
|
+
clearConnectionErrorTimer();
|
|
5153
|
+
const parsed = JSON.parse(event.data);
|
|
5154
|
+
observer.next(parsed);
|
|
5155
|
+
if (parsed.type === 'result' || parsed.type === 'error' || parsed.type === 'cancelled') {
|
|
5156
|
+
observer.complete();
|
|
5157
|
+
closeSource();
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
catch (error) {
|
|
5161
|
+
observer.error(this.streamConnectionError(error));
|
|
5162
|
+
closeSource();
|
|
5163
|
+
}
|
|
5164
|
+
};
|
|
5165
|
+
source.onmessage = handleMessage;
|
|
5166
|
+
if (source.addEventListener) {
|
|
5167
|
+
for (const type of AI_STREAM_EVENT_TYPES) {
|
|
5168
|
+
source.addEventListener(type, handleMessage);
|
|
5169
|
+
}
|
|
5170
|
+
}
|
|
5171
|
+
source.onerror = (event) => {
|
|
5172
|
+
if (closed || connectionErrorTimer) {
|
|
5173
|
+
return;
|
|
5174
|
+
}
|
|
5175
|
+
if (connectionErrorGraceMs === 0) {
|
|
5176
|
+
failConnection(event);
|
|
5177
|
+
return;
|
|
5178
|
+
}
|
|
5179
|
+
connectionErrorTimer = setTimeout(() => failConnection(event), connectionErrorGraceMs);
|
|
5180
|
+
};
|
|
5181
|
+
return () => closeSource();
|
|
5182
|
+
});
|
|
5183
|
+
}
|
|
5184
|
+
streamConnectionError(cause) {
|
|
5185
|
+
return {
|
|
5186
|
+
praxisAgenticTurnStreamConnectionError: true,
|
|
5187
|
+
cause,
|
|
5188
|
+
};
|
|
5189
|
+
}
|
|
5190
|
+
buildStreamUrl(start) {
|
|
5191
|
+
const url = new URL(`${this.baseUrl}/turn/stream/${start.streamId}`, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
|
|
5192
|
+
if (start.streamAccessToken) {
|
|
5193
|
+
url.searchParams.set('accessToken', start.streamAccessToken);
|
|
5194
|
+
}
|
|
5195
|
+
return /^https?:\/\//i.test(this.baseUrl)
|
|
5196
|
+
? url.toString()
|
|
5197
|
+
: url.pathname + url.search;
|
|
5198
|
+
}
|
|
5199
|
+
createEventSource(url) {
|
|
5200
|
+
const factory = this.options?.eventSourceFactory;
|
|
5201
|
+
if (factory) {
|
|
5202
|
+
return factory(url);
|
|
5203
|
+
}
|
|
5204
|
+
return new EventSource(url);
|
|
5205
|
+
}
|
|
5058
5206
|
formatEtag(etag) {
|
|
5059
5207
|
const trimmed = etag.trim();
|
|
5060
5208
|
return trimmed.startsWith('"') ? trimmed : `"${trimmed}"`;
|
|
@@ -5067,6 +5215,522 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
5067
5215
|
args: [{ providedIn: 'root' }]
|
|
5068
5216
|
}] });
|
|
5069
5217
|
|
|
5218
|
+
const pageSchema = {
|
|
5219
|
+
type: 'object',
|
|
5220
|
+
properties: {
|
|
5221
|
+
title: { type: 'string' },
|
|
5222
|
+
layoutPreset: { type: 'string' },
|
|
5223
|
+
context: { type: 'object' },
|
|
5224
|
+
metadata: { type: 'object' },
|
|
5225
|
+
},
|
|
5226
|
+
};
|
|
5227
|
+
const canvasSchema = {
|
|
5228
|
+
type: 'object',
|
|
5229
|
+
properties: {
|
|
5230
|
+
columns: { type: 'number' },
|
|
5231
|
+
rowUnit: { type: 'number' },
|
|
5232
|
+
gap: { type: 'number' },
|
|
5233
|
+
items: { type: 'array' },
|
|
5234
|
+
},
|
|
5235
|
+
};
|
|
5236
|
+
const widgetSchema = {
|
|
5237
|
+
type: 'object',
|
|
5238
|
+
required: ['widgetKey', 'componentId'],
|
|
5239
|
+
properties: {
|
|
5240
|
+
widgetKey: { type: 'string' },
|
|
5241
|
+
componentId: { type: 'string' },
|
|
5242
|
+
title: { type: 'string' },
|
|
5243
|
+
inputs: { type: 'object' },
|
|
5244
|
+
canvasItem: { type: 'object' },
|
|
5245
|
+
},
|
|
5246
|
+
};
|
|
5247
|
+
const compositionLinkSchema = {
|
|
5248
|
+
type: 'object',
|
|
5249
|
+
required: ['linkId', 'from', 'to'],
|
|
5250
|
+
properties: {
|
|
5251
|
+
linkId: { type: 'string' },
|
|
5252
|
+
from: { type: 'object' },
|
|
5253
|
+
to: { type: 'object' },
|
|
5254
|
+
condition: { type: 'object' },
|
|
5255
|
+
policy: { type: 'object' },
|
|
5256
|
+
},
|
|
5257
|
+
};
|
|
5258
|
+
const childOperationSchema = {
|
|
5259
|
+
type: 'object',
|
|
5260
|
+
required: ['widgetKey', 'childComponentId', 'childOperationId', 'reason'],
|
|
5261
|
+
properties: {
|
|
5262
|
+
widgetKey: { type: 'string' },
|
|
5263
|
+
childComponentId: { type: 'string' },
|
|
5264
|
+
childOperationId: { type: 'string' },
|
|
5265
|
+
reason: { type: 'string' },
|
|
5266
|
+
childTarget: { type: 'object' },
|
|
5267
|
+
childParams: { type: 'object' },
|
|
5268
|
+
},
|
|
5269
|
+
};
|
|
5270
|
+
const PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST = {
|
|
5271
|
+
schemaVersion: '1.0.0',
|
|
5272
|
+
componentId: 'praxis-page-builder',
|
|
5273
|
+
ownerPackage: '@praxisui/page-builder',
|
|
5274
|
+
configSchemaId: 'WidgetPageDefinition',
|
|
5275
|
+
manifestVersion: '1.0.0',
|
|
5276
|
+
runtimeInputs: [
|
|
5277
|
+
{ name: 'page', type: 'WidgetPageDefinition', description: 'Canonical renderable page document consumed by praxis-dynamic-page.' },
|
|
5278
|
+
{ name: 'pageIdentity', type: 'PageIdentity | null', description: 'Persistence identity for ui_user_config save/apply flows; not persisted inside page.' },
|
|
5279
|
+
{ name: 'enableCustomization', type: 'boolean', description: 'Enables page-builder authoring chrome around the runtime page.' },
|
|
5280
|
+
{ name: 'context', type: 'Record<string, unknown>', description: 'Opaque host context passed to catalog resolution and agentic authoring.' },
|
|
5281
|
+
{ name: 'pageChange', type: 'WidgetPageDefinition', description: 'Emitted after local apply/preview produces a renderable page.' },
|
|
5282
|
+
{ name: 'agenticAuthoringEnableStreaming', type: 'boolean', description: 'Opt-in flag for the canonical streaming turn endpoints.' },
|
|
5283
|
+
],
|
|
5284
|
+
editableTargets: [
|
|
5285
|
+
{ kind: 'page', resolver: 'widget-page-definition-root', description: 'Root WidgetPageDefinition document: title, metadata, context and renderable page envelope.' },
|
|
5286
|
+
{ kind: 'canvas', resolver: 'widget-page-canvas', description: 'Canvas geometry and item placement for top-level widgets.' },
|
|
5287
|
+
{ kind: 'widget', resolver: 'widget-by-stable-key', description: 'Widget definitions addressed by stable widgetKey, never by array index.' },
|
|
5288
|
+
{ kind: 'widgetShell', resolver: 'widget-shell-by-widget-key', description: 'Dashboard-card shell metadata such as title, action chrome and visual preset.' },
|
|
5289
|
+
{ kind: 'compositionLink', resolver: 'composition-link-by-id', description: 'Declarative wiring in page.composition.links, including component-port nestedPath endpoints.' },
|
|
5290
|
+
{ kind: 'state', resolver: 'page-state-path', description: 'Page-level state entries referenced by composition links.' },
|
|
5291
|
+
{ kind: 'pageIdentity', resolver: 'page-builder-persistence-identity', description: 'ui_user_config identity, scope and ETag used by applyPage/save.' },
|
|
5292
|
+
{ kind: 'agenticPreview', resolver: 'agentic-preview-result', description: 'Compiled backend preview envelope whose patch.page is applied locally.' },
|
|
5293
|
+
{ kind: 'childOperation', resolver: 'widget-child-authoring-manifest-operation', description: 'Delegated operation owned by the child widget component manifest or configEditor.' },
|
|
5294
|
+
],
|
|
5295
|
+
operations: [
|
|
5296
|
+
{
|
|
5297
|
+
operationId: 'page.configure',
|
|
5298
|
+
title: 'Configure page metadata',
|
|
5299
|
+
scope: 'global',
|
|
5300
|
+
targetKind: 'page',
|
|
5301
|
+
target: { kind: 'page', resolver: 'widget-page-definition-root', ambiguityPolicy: 'fail', required: false },
|
|
5302
|
+
inputSchema: pageSchema,
|
|
5303
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-page-configure', handlerContract: {
|
|
5304
|
+
reads: ['WidgetPageDefinition', 'DynamicPageConfigEditorComponent', 'SettingsPanelBridge'],
|
|
5305
|
+
writes: ['page.title', 'page.layoutPreset', 'page.context', 'page.metadata'],
|
|
5306
|
+
identityKeys: ['pageIdentity.componentId', 'pageIdentity.scope'],
|
|
5307
|
+
inputSchema: pageSchema,
|
|
5308
|
+
failureModes: ['page-not-loaded', 'context-not-json', 'settings-panel-bridge-missing'],
|
|
5309
|
+
description: 'Updates root page metadata while preserving the renderable WidgetPageDefinition shape.',
|
|
5310
|
+
} }],
|
|
5311
|
+
validators: ['widget-page-definition-valid', 'no-legacy-grid-page-definition', 'page-context-json-valid', 'settings-panel-round-trip-valid'],
|
|
5312
|
+
affectedPaths: ['title', 'layoutPreset', 'context', 'metadata'],
|
|
5313
|
+
submissionImpact: false,
|
|
5314
|
+
preconditions: ['page-loaded'],
|
|
5315
|
+
},
|
|
5316
|
+
{
|
|
5317
|
+
operationId: 'canvas.configure',
|
|
5318
|
+
title: 'Configure page canvas',
|
|
5319
|
+
scope: 'layout',
|
|
5320
|
+
targetKind: 'canvas',
|
|
5321
|
+
target: { kind: 'canvas', resolver: 'widget-page-canvas', ambiguityPolicy: 'fail', required: true },
|
|
5322
|
+
inputSchema: canvasSchema,
|
|
5323
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-canvas-configure', handlerContract: {
|
|
5324
|
+
reads: ['WidgetPageDefinition.canvas', 'DynamicPageBuilderComponent'],
|
|
5325
|
+
writes: ['page.canvas.columns', 'page.canvas.rowUnit', 'page.canvas.gap', 'page.canvas.items'],
|
|
5326
|
+
identityKeys: ['canvas'],
|
|
5327
|
+
inputSchema: canvasSchema,
|
|
5328
|
+
failureModes: ['canvas-missing', 'canvas-item-references-missing-widget', 'invalid-grid-geometry'],
|
|
5329
|
+
description: 'Configures the top-level canvas without introducing legacy GridPageDefinition semantics.',
|
|
5330
|
+
} }],
|
|
5331
|
+
validators: ['canvas-columns-integer', 'canvas-row-unit-valid', 'canvas-gap-valid', 'canvas-items-reference-existing-widgets'],
|
|
5332
|
+
affectedPaths: ['canvas.columns', 'canvas.rowUnit', 'canvas.gap', 'canvas.items'],
|
|
5333
|
+
submissionImpact: false,
|
|
5334
|
+
preconditions: ['page-loaded'],
|
|
5335
|
+
},
|
|
5336
|
+
{
|
|
5337
|
+
operationId: 'widget.add',
|
|
5338
|
+
title: 'Add widget to page',
|
|
5339
|
+
scope: 'layout',
|
|
5340
|
+
targetKind: 'widget',
|
|
5341
|
+
target: { kind: 'widget', resolver: 'widget-by-stable-key', ambiguityPolicy: 'fail', required: true },
|
|
5342
|
+
inputSchema: widgetSchema,
|
|
5343
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-add', handlerContract: {
|
|
5344
|
+
reads: ['WidgetPageDefinition.widgets', 'WidgetPageDefinition.canvas', 'ComponentDocRegistry', 'PAGE_BUILDER_WIDGET_AI_CATALOGS'],
|
|
5345
|
+
writes: ['page.widgets[]', 'page.canvas.items[]'],
|
|
5346
|
+
identityKeys: ['widgetKey'],
|
|
5347
|
+
inputSchema: widgetSchema,
|
|
5348
|
+
failureModes: ['widget-key-duplicate', 'component-not-registered', 'canvas-item-invalid', 'child-input-contract-unknown'],
|
|
5349
|
+
description: 'Adds a registered component as a widget and places it on the canvas using a stable widgetKey.',
|
|
5350
|
+
} }],
|
|
5351
|
+
validators: ['widget-key-unique', 'component-registered', 'canvas-item-valid', 'child-inputs-delegated', 'widget-key-not-array-index'],
|
|
5352
|
+
affectedPaths: ['widgets[]', 'canvas.items[]'],
|
|
5353
|
+
submissionImpact: false,
|
|
5354
|
+
preconditions: ['page-loaded', 'component-catalog-loaded'],
|
|
5355
|
+
},
|
|
5356
|
+
{
|
|
5357
|
+
operationId: 'widget.remove',
|
|
5358
|
+
title: 'Remove widget from page',
|
|
5359
|
+
scope: 'layout',
|
|
5360
|
+
targetKind: 'widget',
|
|
5361
|
+
target: { kind: 'widget', resolver: 'widget-by-stable-key', ambiguityPolicy: 'fail', required: true },
|
|
5362
|
+
inputSchema: { type: 'object', required: ['widgetKey'], properties: { widgetKey: { type: 'string' }, confirmed: { type: 'boolean' } } },
|
|
5363
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-remove', handlerContract: {
|
|
5364
|
+
reads: ['WidgetPageDefinition.widgets', 'WidgetPageDefinition.canvas.items', 'WidgetPageDefinition.composition.links'],
|
|
5365
|
+
writes: ['page.widgets[]', 'page.canvas.items[]', 'page.composition.links[]'],
|
|
5366
|
+
identityKeys: ['widgetKey'],
|
|
5367
|
+
failureModes: ['widget-not-found', 'destructive-removal-not-confirmed', 'dangling-composition-link'],
|
|
5368
|
+
description: 'Removes a widget and cleans canvas items and composition links that reference it.',
|
|
5369
|
+
} }],
|
|
5370
|
+
destructive: true,
|
|
5371
|
+
requiresConfirmation: true,
|
|
5372
|
+
validators: ['widget-exists', 'destructive-removal-confirmed', 'composition-links-cleaned', 'canvas-items-reference-existing-widgets'],
|
|
5373
|
+
affectedPaths: ['widgets[]', 'canvas.items[]', 'composition.links[]'],
|
|
5374
|
+
submissionImpact: false,
|
|
5375
|
+
preconditions: ['page-loaded', 'target-widget-exists'],
|
|
5376
|
+
},
|
|
5377
|
+
{
|
|
5378
|
+
operationId: 'widget.moveResize',
|
|
5379
|
+
title: 'Move or resize widget',
|
|
5380
|
+
scope: 'layout',
|
|
5381
|
+
targetKind: 'widget',
|
|
5382
|
+
target: { kind: 'widget', resolver: 'widget-by-stable-key', ambiguityPolicy: 'fail', required: true },
|
|
5383
|
+
inputSchema: { type: 'object', required: ['widgetKey', 'canvasItem'], properties: { widgetKey: { type: 'string' }, canvasItem: { type: 'object' } } },
|
|
5384
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-move-resize', handlerContract: {
|
|
5385
|
+
reads: ['WidgetPageDefinition.canvas.items', 'WidgetPageDefinition.widgets'],
|
|
5386
|
+
writes: ['page.canvas.items[]'],
|
|
5387
|
+
identityKeys: ['widgetKey'],
|
|
5388
|
+
failureModes: ['widget-not-found', 'canvas-item-invalid', 'canvas-overlap-invalid'],
|
|
5389
|
+
description: 'Updates canvas placement for an existing widget using widgetKey as the stable identity.',
|
|
5390
|
+
} }],
|
|
5391
|
+
validators: ['widget-exists', 'canvas-item-valid', 'canvas-item-targets-existing-widget', 'canvas-overlap-policy-valid'],
|
|
5392
|
+
affectedPaths: ['canvas.items[]'],
|
|
5393
|
+
submissionImpact: false,
|
|
5394
|
+
preconditions: ['page-loaded', 'target-widget-exists'],
|
|
5395
|
+
},
|
|
5396
|
+
{
|
|
5397
|
+
operationId: 'widget.shell.configure',
|
|
5398
|
+
title: 'Configure widget shell',
|
|
5399
|
+
scope: 'templating',
|
|
5400
|
+
targetKind: 'widgetShell',
|
|
5401
|
+
target: { kind: 'widgetShell', resolver: 'widget-shell-by-widget-key', ambiguityPolicy: 'fail', required: true },
|
|
5402
|
+
inputSchema: { type: 'object', required: ['widgetKey'], properties: { widgetKey: { type: 'string' }, shell: { type: 'object' } } },
|
|
5403
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-shell-configure', handlerContract: {
|
|
5404
|
+
reads: ['WidgetPageDefinition.widgets', 'WidgetShellEditorComponent', 'SettingsPanelBridge'],
|
|
5405
|
+
writes: ['page.widgets[].shell'],
|
|
5406
|
+
identityKeys: ['widgetKey'],
|
|
5407
|
+
failureModes: ['widget-not-found', 'settings-panel-bridge-missing', 'shell-config-invalid'],
|
|
5408
|
+
description: 'Updates the page-builder-owned widget shell without editing child component inputs.',
|
|
5409
|
+
} }],
|
|
5410
|
+
validators: ['widget-exists', 'widget-shell-shape-valid', 'settings-panel-bridge-available', 'child-inputs-not-mutated'],
|
|
5411
|
+
affectedPaths: ['widgets[].shell'],
|
|
5412
|
+
submissionImpact: false,
|
|
5413
|
+
preconditions: ['page-loaded', 'target-widget-exists'],
|
|
5414
|
+
},
|
|
5415
|
+
{
|
|
5416
|
+
operationId: 'composition.link.add',
|
|
5417
|
+
title: 'Add page composition link',
|
|
5418
|
+
scope: 'dataBinding',
|
|
5419
|
+
targetKind: 'compositionLink',
|
|
5420
|
+
target: { kind: 'compositionLink', resolver: 'composition-link-by-id', ambiguityPolicy: 'fail', required: true },
|
|
5421
|
+
inputSchema: compositionLinkSchema,
|
|
5422
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-composition-link-add', handlerContract: {
|
|
5423
|
+
reads: ['WidgetPageDefinition.composition.links', 'WidgetPageDefinition.widgets', 'PAGE_BUILDER_AI_CAPABILITIES'],
|
|
5424
|
+
writes: ['page.composition.links[]'],
|
|
5425
|
+
identityKeys: ['linkId'],
|
|
5426
|
+
inputSchema: compositionLinkSchema,
|
|
5427
|
+
failureModes: ['link-id-duplicate', 'endpoint-not-found', 'nested-path-invalid', 'json-logic-invalid'],
|
|
5428
|
+
description: 'Adds canonical declarative wiring in page.composition.links, including component-port nestedPath endpoints.',
|
|
5429
|
+
} }],
|
|
5430
|
+
validators: ['composition-link-id-unique', 'composition-endpoints-resolve', 'nested-path-terminal-widget-key-required', 'json-logic-condition-valid', 'no-legacy-connections-write'],
|
|
5431
|
+
affectedPaths: ['composition.links[]'],
|
|
5432
|
+
submissionImpact: false,
|
|
5433
|
+
preconditions: ['page-loaded', 'widgets-resolvable'],
|
|
5434
|
+
},
|
|
5435
|
+
{
|
|
5436
|
+
operationId: 'composition.link.remove',
|
|
5437
|
+
title: 'Remove page composition link',
|
|
5438
|
+
scope: 'dataBinding',
|
|
5439
|
+
targetKind: 'compositionLink',
|
|
5440
|
+
target: { kind: 'compositionLink', resolver: 'composition-link-by-id', ambiguityPolicy: 'fail', required: true },
|
|
5441
|
+
inputSchema: { type: 'object', required: ['linkId'], properties: { linkId: { type: 'string' }, confirmed: { type: 'boolean' } } },
|
|
5442
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-composition-link-remove', handlerContract: {
|
|
5443
|
+
reads: ['WidgetPageDefinition.composition.links'],
|
|
5444
|
+
writes: ['page.composition.links[]'],
|
|
5445
|
+
identityKeys: ['linkId'],
|
|
5446
|
+
failureModes: ['composition-link-not-found', 'destructive-removal-not-confirmed'],
|
|
5447
|
+
description: 'Removes a canonical composition link by stable linkId.',
|
|
5448
|
+
} }],
|
|
5449
|
+
destructive: true,
|
|
5450
|
+
requiresConfirmation: true,
|
|
5451
|
+
validators: ['composition-link-exists', 'destructive-removal-confirmed', 'composition-links-still-valid'],
|
|
5452
|
+
affectedPaths: ['composition.links[]'],
|
|
5453
|
+
submissionImpact: false,
|
|
5454
|
+
preconditions: ['page-loaded', 'target-link-exists'],
|
|
5455
|
+
},
|
|
5456
|
+
{
|
|
5457
|
+
operationId: 'composition.plan.compile',
|
|
5458
|
+
title: 'Compile UI composition plan',
|
|
5459
|
+
scope: 'rules',
|
|
5460
|
+
targetKind: 'agenticPreview',
|
|
5461
|
+
target: { kind: 'agenticPreview', resolver: 'agentic-preview-result', ambiguityPolicy: 'fail', required: true },
|
|
5462
|
+
inputSchema: { type: 'object', required: ['uiCompositionPlan'], properties: { uiCompositionPlan: { type: 'object' } } },
|
|
5463
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-ui-composition-plan-compile', handlerContract: {
|
|
5464
|
+
reads: ['UiCompositionPlan', 'compileUiCompositionPlan', 'PAGE_BUILDER_WIDGET_AI_CATALOGS', 'ComponentDocMeta.configEditor'],
|
|
5465
|
+
writes: ['compiledFormPatch.patch.page'],
|
|
5466
|
+
identityKeys: ['uiCompositionPlan.id', 'widgetKey', 'linkId'],
|
|
5467
|
+
failureModes: ['ui-composition-plan-invalid', 'widget-catalog-missing', 'compiled-page-invalid', 'child-config-editor-missing'],
|
|
5468
|
+
description: 'Compiles the agentic intermediate UiCompositionPlan into a renderable WidgetPageDefinition patch.page.',
|
|
5469
|
+
} }],
|
|
5470
|
+
validators: ['ui-composition-plan-valid', 'compiled-page-valid', 'widget-keys-unique', 'composition-endpoints-resolve', 'canvas-items-reference-existing-widgets'],
|
|
5471
|
+
affectedPaths: ['uiCompositionPlan', 'compiledFormPatch.patch.page'],
|
|
5472
|
+
submissionImpact: false,
|
|
5473
|
+
preconditions: ['agentic-preview-loaded', 'component-catalog-loaded'],
|
|
5474
|
+
},
|
|
5475
|
+
{
|
|
5476
|
+
operationId: 'state.set',
|
|
5477
|
+
title: 'Set page state value',
|
|
5478
|
+
scope: 'dataBinding',
|
|
5479
|
+
targetKind: 'state',
|
|
5480
|
+
target: { kind: 'state', resolver: 'page-state-path', ambiguityPolicy: 'fail', required: true },
|
|
5481
|
+
inputSchema: { type: 'object', required: ['path', 'value'], properties: { path: { type: 'string' }, value: {}, layer: { enum: ['page', 'session'] } } },
|
|
5482
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-state-set', handlerContract: {
|
|
5483
|
+
reads: ['WidgetPageDefinition.state', 'WidgetPageDefinition.composition.links'],
|
|
5484
|
+
writes: ['page.state'],
|
|
5485
|
+
identityKeys: ['path'],
|
|
5486
|
+
failureModes: ['state-path-invalid', 'state-json-invalid', 'composition-state-link-invalid'],
|
|
5487
|
+
description: 'Sets canonical page state used by composition links without creating host-only binding paths.',
|
|
5488
|
+
} }],
|
|
5489
|
+
validators: ['state-path-valid', 'state-layer-valid', 'state-json-valid', 'composition-state-links-still-valid'],
|
|
5490
|
+
affectedPaths: ['state'],
|
|
5491
|
+
submissionImpact: false,
|
|
5492
|
+
preconditions: ['page-loaded'],
|
|
5493
|
+
},
|
|
5494
|
+
{
|
|
5495
|
+
operationId: 'page.preview.apply',
|
|
5496
|
+
title: 'Apply agentic preview locally',
|
|
5497
|
+
scope: 'interaction',
|
|
5498
|
+
targetKind: 'agenticPreview',
|
|
5499
|
+
target: { kind: 'agenticPreview', resolver: 'agentic-preview-result', ambiguityPolicy: 'fail', required: true },
|
|
5500
|
+
inputSchema: { type: 'object', required: ['previewId'], properties: { previewId: { type: 'string' }, compiledFormPatch: { type: 'object' } } },
|
|
5501
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-preview-apply', handlerContract: {
|
|
5502
|
+
reads: ['PageBuilderAiAdapter.applyCompiledFormPatch', 'compiledFormPatch.patch.page', 'PraxisAssistantTurnViewState'],
|
|
5503
|
+
writes: ['DynamicPageBuilderComponent.page', 'pageChange'],
|
|
5504
|
+
identityKeys: ['previewId'],
|
|
5505
|
+
failureModes: ['preview-result-invalid', 'patch-page-missing', 'runtime-page-invalid', 'patch-envelope-persistence-attempted'],
|
|
5506
|
+
description: 'Applies only compiledFormPatch.patch.page to the local runtime page; the backend envelope remains diagnostic data.',
|
|
5507
|
+
} }],
|
|
5508
|
+
validators: ['preview-result-valid', 'compiled-page-patch-present', 'no-envelope-runtime-persistence', 'runtime-page-valid'],
|
|
5509
|
+
affectedPaths: ['compiledFormPatch.patch.page', 'page'],
|
|
5510
|
+
submissionImpact: false,
|
|
5511
|
+
preconditions: ['agentic-preview-loaded'],
|
|
5512
|
+
},
|
|
5513
|
+
{
|
|
5514
|
+
operationId: 'page.persist.save',
|
|
5515
|
+
title: 'Persist page through config API',
|
|
5516
|
+
scope: 'interaction',
|
|
5517
|
+
targetKind: 'pageIdentity',
|
|
5518
|
+
target: { kind: 'pageIdentity', resolver: 'page-builder-persistence-identity', ambiguityPolicy: 'fail', required: true },
|
|
5519
|
+
inputSchema: { type: 'object', required: ['pageIdentity'], properties: { pageIdentity: { type: 'object' }, page: { type: 'object' }, etag: { type: 'string' } } },
|
|
5520
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-page-persist-save', handlerContract: {
|
|
5521
|
+
reads: ['pageIdentity', 'WidgetPageDefinition', 'PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS', 'ui_user_config ETag'],
|
|
5522
|
+
writes: ['POST/PUT /api/praxis/config/ui-user-config', 'If-Match'],
|
|
5523
|
+
identityKeys: ['pageIdentity.componentType', 'pageIdentity.componentId', 'pageIdentity.scope'],
|
|
5524
|
+
failureModes: ['page-identity-incomplete', 'etag-conflict', 'runtime-page-invalid', 'patch-envelope-persistence-attempted'],
|
|
5525
|
+
description: 'Persists the renderable WidgetPageDefinition with pageIdentity and ETag delegated to praxis-config-starter.',
|
|
5526
|
+
} }],
|
|
5527
|
+
validators: ['page-identity-complete', 'etag-policy-valid', 'runtime-page-valid', 'no-envelope-runtime-persistence'],
|
|
5528
|
+
affectedPaths: ['page', 'pageIdentity', 'etag'],
|
|
5529
|
+
submissionImpact: false,
|
|
5530
|
+
preconditions: ['page-loaded', 'page-identity-known'],
|
|
5531
|
+
},
|
|
5532
|
+
{
|
|
5533
|
+
operationId: 'childOperation.delegate',
|
|
5534
|
+
title: 'Delegate widget child operation',
|
|
5535
|
+
scope: 'global',
|
|
5536
|
+
targetKind: 'childOperation',
|
|
5537
|
+
target: { kind: 'childOperation', resolver: 'widget-child-authoring-manifest-operation', ambiguityPolicy: 'fail', required: false },
|
|
5538
|
+
inputSchema: childOperationSchema,
|
|
5539
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-child-operation-delegate', handlerContract: {
|
|
5540
|
+
reads: ['WidgetPageDefinition.widgets', 'ComponentDocMeta.configEditor', 'child ComponentAuthoringManifest', 'PAGE_BUILDER_WIDGET_AI_CATALOGS'],
|
|
5541
|
+
writes: ['page.widgets[].definition.inputs'],
|
|
5542
|
+
identityKeys: ['widgetKey', 'childComponentId', 'childOperationId'],
|
|
5543
|
+
inputSchema: childOperationSchema,
|
|
5544
|
+
failureModes: ['widget-not-found', 'child-manifest-missing', 'child-operation-unknown', 'local-child-input-write-attempted'],
|
|
5545
|
+
description: 'Delegates child widget input edits to the owning manifest or configEditor instead of redefining child semantics in page-builder.',
|
|
5546
|
+
} }],
|
|
5547
|
+
validators: ['widget-exists', 'child-manifest-available', 'child-operation-known', 'component-config-editor-respected', 'no-local-child-input-write'],
|
|
5548
|
+
affectedPaths: ['widgets[].definition.inputs'],
|
|
5549
|
+
submissionImpact: false,
|
|
5550
|
+
preconditions: ['page-loaded', 'target-widget-exists', 'child-authoring-contract-available'],
|
|
5551
|
+
},
|
|
5552
|
+
],
|
|
5553
|
+
validators: [
|
|
5554
|
+
{ validatorId: 'widget-page-definition-valid', level: 'error', code: 'PAGE_BUILDER_PAGE_VALID', description: 'The document must remain a renderable WidgetPageDefinition.' },
|
|
5555
|
+
{ validatorId: 'no-legacy-grid-page-definition', level: 'error', code: 'PAGE_BUILDER_NO_GRID_PAGE', description: 'Legacy GridPageDefinition semantics cannot be reintroduced.' },
|
|
5556
|
+
{ validatorId: 'page-context-json-valid', level: 'error', code: 'PAGE_BUILDER_CONTEXT_JSON', description: 'Page context must stay JSON-serializable.' },
|
|
5557
|
+
{ 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.' },
|
|
5558
|
+
{ validatorId: 'canvas-columns-integer', level: 'error', code: 'PAGE_BUILDER_CANVAS_COLUMNS', description: 'Canvas columns must be an integer grid count.' },
|
|
5559
|
+
{ validatorId: 'canvas-row-unit-valid', level: 'error', code: 'PAGE_BUILDER_CANVAS_ROW_UNIT', description: 'Canvas row unit must be a positive layout unit.' },
|
|
5560
|
+
{ validatorId: 'canvas-gap-valid', level: 'error', code: 'PAGE_BUILDER_CANVAS_GAP', description: 'Canvas gap must be a valid non-negative spacing value.' },
|
|
5561
|
+
{ validatorId: 'canvas-items-reference-existing-widgets', level: 'error', code: 'PAGE_BUILDER_CANVAS_WIDGET_REFS', description: 'Every canvas item must reference an existing widgetKey.' },
|
|
5562
|
+
{ validatorId: 'widget-key-unique', level: 'error', code: 'PAGE_BUILDER_WIDGET_KEY_UNIQUE', description: 'Widget keys must be unique.' },
|
|
5563
|
+
{ validatorId: 'component-registered', level: 'error', code: 'PAGE_BUILDER_COMPONENT_REGISTERED', description: 'Widget component id must exist in the component registry/catalog.' },
|
|
5564
|
+
{ validatorId: 'canvas-item-valid', level: 'error', code: 'PAGE_BUILDER_CANVAS_ITEM', description: 'Canvas item geometry must be valid.' },
|
|
5565
|
+
{ validatorId: 'child-inputs-delegated', level: 'error', code: 'PAGE_BUILDER_CHILD_INPUTS_DELEGATED', description: 'Child component input edits must delegate to the child owner.' },
|
|
5566
|
+
{ validatorId: 'widget-key-not-array-index', level: 'error', code: 'PAGE_BUILDER_WIDGET_KEY_INDEX', description: 'Array index is not a stable widget identity.' },
|
|
5567
|
+
{ validatorId: 'widget-exists', level: 'error', code: 'PAGE_BUILDER_WIDGET_EXISTS', description: 'Target widget must exist.' },
|
|
5568
|
+
{ validatorId: 'destructive-removal-confirmed', level: 'error', code: 'PAGE_BUILDER_DESTRUCTIVE_CONFIRMED', description: 'Destructive removals require explicit confirmation.' },
|
|
5569
|
+
{ validatorId: 'composition-links-cleaned', level: 'error', code: 'PAGE_BUILDER_LINKS_CLEANED', description: 'Removing a widget must remove dangling composition links.' },
|
|
5570
|
+
{ validatorId: 'canvas-item-targets-existing-widget', level: 'error', code: 'PAGE_BUILDER_CANVAS_ITEM_TARGET', description: 'Moved canvas item must target an existing widget.' },
|
|
5571
|
+
{ validatorId: 'canvas-overlap-policy-valid', level: 'warning', code: 'PAGE_BUILDER_CANVAS_OVERLAP', description: 'Canvas overlap policy must be respected.' },
|
|
5572
|
+
{ validatorId: 'widget-shell-shape-valid', level: 'error', code: 'PAGE_BUILDER_SHELL_SHAPE', description: 'Widget shell config must match the shell editor contract.' },
|
|
5573
|
+
{ validatorId: 'settings-panel-bridge-available', level: 'error', code: 'PAGE_BUILDER_SETTINGS_BRIDGE', description: 'Settings panel bridge must be available for shell/page editors.' },
|
|
5574
|
+
{ validatorId: 'child-inputs-not-mutated', level: 'error', code: 'PAGE_BUILDER_CHILD_INPUTS_NOT_MUTATED', description: 'Shell edits cannot mutate child inputs.' },
|
|
5575
|
+
{ validatorId: 'composition-link-id-unique', level: 'error', code: 'PAGE_BUILDER_LINK_ID_UNIQUE', description: 'Composition link ids must be unique.' },
|
|
5576
|
+
{ validatorId: 'composition-endpoints-resolve', level: 'error', code: 'PAGE_BUILDER_LINK_ENDPOINTS', description: 'Composition endpoints must resolve to known widgets, ports and nested paths.' },
|
|
5577
|
+
{ 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.' },
|
|
5578
|
+
{ validatorId: 'json-logic-condition-valid', level: 'error', code: 'PAGE_BUILDER_JSON_LOGIC', description: 'Composition link condition must be valid Json Logic.' },
|
|
5579
|
+
{ validatorId: 'no-legacy-connections-write', level: 'error', code: 'PAGE_BUILDER_NO_LEGACY_CONNECTIONS', description: 'Operations must write page.composition.links, not legacy connections.' },
|
|
5580
|
+
{ validatorId: 'composition-link-exists', level: 'error', code: 'PAGE_BUILDER_LINK_EXISTS', description: 'Target composition link must exist.' },
|
|
5581
|
+
{ validatorId: 'composition-links-still-valid', level: 'error', code: 'PAGE_BUILDER_LINKS_STILL_VALID', description: 'Remaining composition links must still validate after the operation.' },
|
|
5582
|
+
{ validatorId: 'ui-composition-plan-valid', level: 'error', code: 'PAGE_BUILDER_UI_PLAN_VALID', description: 'UiCompositionPlan must match the intermediate agentic plan contract.' },
|
|
5583
|
+
{ validatorId: 'compiled-page-valid', level: 'error', code: 'PAGE_BUILDER_COMPILED_PAGE_VALID', description: 'Compiled output must be a valid WidgetPageDefinition.' },
|
|
5584
|
+
{ validatorId: 'widget-keys-unique', level: 'error', code: 'PAGE_BUILDER_WIDGET_KEYS_UNIQUE', description: 'Compiled plan must produce unique widget keys.' },
|
|
5585
|
+
{ validatorId: 'state-path-valid', level: 'error', code: 'PAGE_BUILDER_STATE_PATH', description: 'State paths must be valid page state paths.' },
|
|
5586
|
+
{ validatorId: 'state-layer-valid', level: 'error', code: 'PAGE_BUILDER_STATE_LAYER', description: 'State layer must be page or session.' },
|
|
5587
|
+
{ validatorId: 'state-json-valid', level: 'error', code: 'PAGE_BUILDER_STATE_JSON', description: 'State values must be JSON-serializable.' },
|
|
5588
|
+
{ validatorId: 'composition-state-links-still-valid', level: 'error', code: 'PAGE_BUILDER_STATE_LINKS', description: 'State edits must not break composition links.' },
|
|
5589
|
+
{ validatorId: 'preview-result-valid', level: 'error', code: 'PAGE_BUILDER_PREVIEW_VALID', description: 'Agentic preview result must be structurally valid.' },
|
|
5590
|
+
{ validatorId: 'compiled-page-patch-present', level: 'error', code: 'PAGE_BUILDER_PATCH_PAGE_PRESENT', description: 'Preview envelope must include compiledFormPatch.patch.page.' },
|
|
5591
|
+
{ validatorId: 'no-envelope-runtime-persistence', level: 'error', code: 'PAGE_BUILDER_NO_ENVELOPE_PERSISTENCE', description: 'Runtime persistence cannot save the full preview envelope.' },
|
|
5592
|
+
{ validatorId: 'runtime-page-valid', level: 'error', code: 'PAGE_BUILDER_RUNTIME_PAGE_VALID', description: 'Runtime page must remain renderable by praxis-dynamic-page.' },
|
|
5593
|
+
{ validatorId: 'page-identity-complete', level: 'error', code: 'PAGE_BUILDER_PAGE_IDENTITY', description: 'Save requires component type, component id and scope identity.' },
|
|
5594
|
+
{ validatorId: 'etag-policy-valid', level: 'error', code: 'PAGE_BUILDER_ETAG_POLICY', description: 'Save must respect backend ETag/If-Match policy.' },
|
|
5595
|
+
{ validatorId: 'child-manifest-available', level: 'error', code: 'PAGE_BUILDER_CHILD_MANIFEST_AVAILABLE', description: 'Delegated child component must expose an authoring manifest or configEditor.' },
|
|
5596
|
+
{ validatorId: 'child-operation-known', level: 'error', code: 'PAGE_BUILDER_CHILD_OPERATION_KNOWN', description: 'Delegated child operation id must be known.' },
|
|
5597
|
+
{ validatorId: 'component-config-editor-respected', level: 'error', code: 'PAGE_BUILDER_CONFIG_EDITOR_RESPECTED', description: 'ComponentDocMeta.configEditor must remain the source for child input editing.' },
|
|
5598
|
+
{ 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.' },
|
|
5599
|
+
],
|
|
5600
|
+
roundTripRequirements: [
|
|
5601
|
+
'WidgetPageDefinition is the canonical persisted runtime page shape for page-builder.',
|
|
5602
|
+
'UiCompositionPlan is an intermediate agentic plan and must compile before local preview/apply.',
|
|
5603
|
+
'Widget keys and composition link ids are stable identities; array indexes are never canonical identities.',
|
|
5604
|
+
'Persisted wiring uses page.composition.links and must not write legacy connections.',
|
|
5605
|
+
'Nested component ports use component-port refs with nestedPath ending in a stable widget key.',
|
|
5606
|
+
'Child widget definition.inputs edits delegate to the child manifest or ComponentDocMeta.configEditor.',
|
|
5607
|
+
'Agentic preview applies only compiledFormPatch.patch.page to runtime; the full patch envelope is diagnostics/audit data.',
|
|
5608
|
+
'Save uses pageIdentity and ETag policy from praxis-config-starter; pageIdentity is not persisted inside WidgetPageDefinition.',
|
|
5609
|
+
'Settings panel apply/save/reset/reopen must preserve page settings and widget shell config without hidden normalization drift.',
|
|
5610
|
+
],
|
|
5611
|
+
examples: [
|
|
5612
|
+
{
|
|
5613
|
+
id: 'configure-dashboard-page',
|
|
5614
|
+
request: 'Set this page title to Payroll Overview and store payrollPeriod in the page context.',
|
|
5615
|
+
operationId: 'page.configure',
|
|
5616
|
+
params: { title: 'Payroll Overview', context: { payrollPeriod: 'current' } },
|
|
5617
|
+
isPositive: true,
|
|
5618
|
+
},
|
|
5619
|
+
{
|
|
5620
|
+
id: 'configure-canvas-grid',
|
|
5621
|
+
request: 'Use a 12 column dashboard grid with compact gaps.',
|
|
5622
|
+
operationId: 'canvas.configure',
|
|
5623
|
+
target: 'canvas',
|
|
5624
|
+
params: { columns: 12, rowUnit: 8, gap: 8 },
|
|
5625
|
+
isPositive: true,
|
|
5626
|
+
},
|
|
5627
|
+
{
|
|
5628
|
+
id: 'add-employees-table-widget',
|
|
5629
|
+
request: 'Add an employees table widget to the left half of the first row.',
|
|
5630
|
+
operationId: 'widget.add',
|
|
5631
|
+
target: 'employees-table',
|
|
5632
|
+
params: { widgetKey: 'employees-table', componentId: 'praxis-table', canvasItem: { x: 0, y: 0, w: 6, h: 8 } },
|
|
5633
|
+
isPositive: true,
|
|
5634
|
+
},
|
|
5635
|
+
{
|
|
5636
|
+
id: 'remove-summary-card',
|
|
5637
|
+
request: 'Remove the summary-card widget and its links.',
|
|
5638
|
+
operationId: 'widget.remove',
|
|
5639
|
+
target: 'summary-card',
|
|
5640
|
+
params: { widgetKey: 'summary-card', confirmed: true },
|
|
5641
|
+
isPositive: true,
|
|
5642
|
+
},
|
|
5643
|
+
{
|
|
5644
|
+
id: 'move-payroll-chart',
|
|
5645
|
+
request: 'Move payroll-chart to the right column and make it taller.',
|
|
5646
|
+
operationId: 'widget.moveResize',
|
|
5647
|
+
target: 'payroll-chart',
|
|
5648
|
+
params: { widgetKey: 'payroll-chart', canvasItem: { x: 6, y: 0, w: 6, h: 10 } },
|
|
5649
|
+
isPositive: true,
|
|
5650
|
+
},
|
|
5651
|
+
{
|
|
5652
|
+
id: 'configure-widget-shell',
|
|
5653
|
+
request: 'Rename the employees widget card and enable the refresh shell action.',
|
|
5654
|
+
operationId: 'widget.shell.configure',
|
|
5655
|
+
target: 'employees-table',
|
|
5656
|
+
params: { widgetKey: 'employees-table', shell: { title: 'Employees', actions: [{ id: 'refresh' }] } },
|
|
5657
|
+
isPositive: true,
|
|
5658
|
+
},
|
|
5659
|
+
{
|
|
5660
|
+
id: 'link-department-selection-to-table',
|
|
5661
|
+
request: 'When department-list selection changes, filter employees-table by department id.',
|
|
5662
|
+
operationId: 'composition.link.add',
|
|
5663
|
+
target: 'department-selection-to-employees',
|
|
5664
|
+
params: { linkId: 'department-selection-to-employees', from: { widget: 'department-list', port: 'selection' }, to: { widget: 'employees-table', port: 'filter' } },
|
|
5665
|
+
isPositive: true,
|
|
5666
|
+
},
|
|
5667
|
+
{
|
|
5668
|
+
id: 'remove-obsolete-link',
|
|
5669
|
+
request: 'Remove the old payroll-filter-link composition link.',
|
|
5670
|
+
operationId: 'composition.link.remove',
|
|
5671
|
+
target: 'payroll-filter-link',
|
|
5672
|
+
params: { linkId: 'payroll-filter-link', confirmed: true },
|
|
5673
|
+
isPositive: true,
|
|
5674
|
+
},
|
|
5675
|
+
{
|
|
5676
|
+
id: 'compile-resource-dashboard-plan',
|
|
5677
|
+
request: 'Compile the resource-dashboard plan returned by the assistant into a renderable page preview.',
|
|
5678
|
+
operationId: 'composition.plan.compile',
|
|
5679
|
+
target: 'preview-payroll-dashboard',
|
|
5680
|
+
params: { uiCompositionPlan: { layoutPreset: 'resource-dashboard' } },
|
|
5681
|
+
isPositive: true,
|
|
5682
|
+
},
|
|
5683
|
+
{
|
|
5684
|
+
id: 'set-selected-department-state',
|
|
5685
|
+
request: 'Store selectedDepartmentId in page state for downstream links.',
|
|
5686
|
+
operationId: 'state.set',
|
|
5687
|
+
target: 'state.selectedDepartmentId',
|
|
5688
|
+
params: { path: 'selectedDepartmentId', value: 'sales', layer: 'page' },
|
|
5689
|
+
isPositive: true,
|
|
5690
|
+
},
|
|
5691
|
+
{
|
|
5692
|
+
id: 'apply-agentic-preview',
|
|
5693
|
+
request: 'Apply the current assistant preview to the local page.',
|
|
5694
|
+
operationId: 'page.preview.apply',
|
|
5695
|
+
target: 'preview-current',
|
|
5696
|
+
params: { previewId: 'preview-current' },
|
|
5697
|
+
isPositive: true,
|
|
5698
|
+
},
|
|
5699
|
+
{
|
|
5700
|
+
id: 'persist-page',
|
|
5701
|
+
request: 'Save this page configuration using the current ETag.',
|
|
5702
|
+
operationId: 'page.persist.save',
|
|
5703
|
+
target: 'tenant-dashboard-page',
|
|
5704
|
+
params: { pageIdentity: { componentType: 'page-builder', componentId: 'tenant-dashboard', scope: 'tenant' }, etag: '"v7"' },
|
|
5705
|
+
isPositive: true,
|
|
5706
|
+
},
|
|
5707
|
+
{
|
|
5708
|
+
id: 'delegate-table-column-edit',
|
|
5709
|
+
request: 'Add a salary column to the employees table widget.',
|
|
5710
|
+
operationId: 'childOperation.delegate',
|
|
5711
|
+
target: 'employees-table',
|
|
5712
|
+
params: { widgetKey: 'employees-table', childComponentId: 'praxis-table', childOperationId: 'column.add', reason: 'TableConfig is owned by praxis-table.' },
|
|
5713
|
+
isPositive: true,
|
|
5714
|
+
},
|
|
5715
|
+
{
|
|
5716
|
+
id: 'reject-direct-dynamic-form-field-write',
|
|
5717
|
+
request: 'Directly write a new dynamic-form field into the widget inputs from page-builder.',
|
|
5718
|
+
operationId: 'childOperation.delegate',
|
|
5719
|
+
target: 'employee-form',
|
|
5720
|
+
params: { widgetKey: 'employee-form', childComponentId: 'praxis-dynamic-form', childOperationId: 'field.add', reason: 'Dynamic form fields are delegated.' },
|
|
5721
|
+
isPositive: false,
|
|
5722
|
+
},
|
|
5723
|
+
{
|
|
5724
|
+
id: 'reject-legacy-connections',
|
|
5725
|
+
request: 'Create the widget link under the old connections collection.',
|
|
5726
|
+
operationId: 'composition.link.add',
|
|
5727
|
+
target: 'legacy-connection',
|
|
5728
|
+
params: { linkId: 'legacy-connection', collection: 'connections' },
|
|
5729
|
+
isPositive: false,
|
|
5730
|
+
},
|
|
5731
|
+
],
|
|
5732
|
+
};
|
|
5733
|
+
|
|
5070
5734
|
function compileUiCompositionPlan(plan) {
|
|
5071
5735
|
const diagnostics = validateUiCompositionPlan(plan);
|
|
5072
5736
|
if (diagnostics.length) {
|
|
@@ -5080,8 +5744,8 @@ function compileUiCompositionPlan(plan) {
|
|
|
5080
5744
|
valid: true,
|
|
5081
5745
|
page: {
|
|
5082
5746
|
layoutPreset: plan.layoutPreset,
|
|
5083
|
-
canvas: clone(plan.canvas),
|
|
5084
|
-
state: clone(plan.state),
|
|
5747
|
+
canvas: clone$1(plan.canvas),
|
|
5748
|
+
state: clone$1(plan.state),
|
|
5085
5749
|
widgets: plan.widgets.map(toWidgetInstance),
|
|
5086
5750
|
composition: {
|
|
5087
5751
|
version: '1.0.0',
|
|
@@ -5176,6 +5840,7 @@ function validateEndpoint(endpoint, widgetKeys, diagnostics, path) {
|
|
|
5176
5840
|
path: `${path}.port`,
|
|
5177
5841
|
});
|
|
5178
5842
|
}
|
|
5843
|
+
validateNestedPath(endpoint.nestedPath, diagnostics, `${path}.nestedPath`);
|
|
5179
5844
|
return;
|
|
5180
5845
|
}
|
|
5181
5846
|
if (!endpoint.path?.trim()) {
|
|
@@ -5186,14 +5851,26 @@ function validateEndpoint(endpoint, widgetKeys, diagnostics, path) {
|
|
|
5186
5851
|
});
|
|
5187
5852
|
}
|
|
5188
5853
|
}
|
|
5854
|
+
function validateNestedPath(nestedPath, diagnostics, path) {
|
|
5855
|
+
if (!nestedPath?.length)
|
|
5856
|
+
return;
|
|
5857
|
+
const terminal = nestedPath[nestedPath.length - 1];
|
|
5858
|
+
if (terminal?.kind !== 'widget' || !terminal.key?.trim()) {
|
|
5859
|
+
diagnostics.push({
|
|
5860
|
+
code: 'endpoint-nested-terminal-widget-key-required',
|
|
5861
|
+
message: 'Nested component endpoint must end with a widget segment with a stable key.',
|
|
5862
|
+
path,
|
|
5863
|
+
});
|
|
5864
|
+
}
|
|
5865
|
+
}
|
|
5189
5866
|
function toWidgetInstance(widget) {
|
|
5190
5867
|
return {
|
|
5191
5868
|
key: widget.key,
|
|
5192
5869
|
definition: {
|
|
5193
5870
|
id: widget.componentId,
|
|
5194
5871
|
bindingOrder: widget.bindingOrder,
|
|
5195
|
-
inputs: clone(widget.inputs ?? {}),
|
|
5196
|
-
outputs: clone(widget.outputs ?? {}),
|
|
5872
|
+
inputs: clone$1(widget.inputs ?? {}),
|
|
5873
|
+
outputs: clone$1(widget.outputs ?? {}),
|
|
5197
5874
|
},
|
|
5198
5875
|
};
|
|
5199
5876
|
}
|
|
@@ -5204,10 +5881,10 @@ function toCompositionLink(binding) {
|
|
|
5204
5881
|
from: toCompositionEndpoint(binding.from),
|
|
5205
5882
|
to: toCompositionEndpoint(binding.to),
|
|
5206
5883
|
intent: binding.intent,
|
|
5207
|
-
condition: clone(binding.condition),
|
|
5884
|
+
condition: clone$1(binding.condition),
|
|
5208
5885
|
transform,
|
|
5209
|
-
policy: clone(binding.policy),
|
|
5210
|
-
metadata: clone(binding.metadata),
|
|
5886
|
+
policy: clone$1(binding.policy),
|
|
5887
|
+
metadata: clone$1(binding.metadata),
|
|
5211
5888
|
};
|
|
5212
5889
|
}
|
|
5213
5890
|
function toCompositionEndpoint(endpoint) {
|
|
@@ -5218,6 +5895,7 @@ function toCompositionEndpoint(endpoint) {
|
|
|
5218
5895
|
widget: endpoint.widget,
|
|
5219
5896
|
port: endpoint.port,
|
|
5220
5897
|
direction: endpoint.direction,
|
|
5898
|
+
...(endpoint.nestedPath?.length ? { nestedPath: clone$1(endpoint.nestedPath) } : {}),
|
|
5221
5899
|
},
|
|
5222
5900
|
};
|
|
5223
5901
|
}
|
|
@@ -5265,7 +5943,7 @@ function toCompositionTransform(transform) {
|
|
|
5265
5943
|
phase: 'link-propagation',
|
|
5266
5944
|
input: { source: transform.inputSource ?? 'event' },
|
|
5267
5945
|
config: {
|
|
5268
|
-
template: clone(transform.template),
|
|
5946
|
+
template: clone$1(transform.template),
|
|
5269
5947
|
},
|
|
5270
5948
|
},
|
|
5271
5949
|
],
|
|
@@ -5282,7 +5960,7 @@ function toCompositionTransform(transform) {
|
|
|
5282
5960
|
kind: 'constant',
|
|
5283
5961
|
phase: 'link-propagation',
|
|
5284
5962
|
config: {
|
|
5285
|
-
value: clone(transform.value),
|
|
5963
|
+
value: clone$1(transform.value),
|
|
5286
5964
|
},
|
|
5287
5965
|
},
|
|
5288
5966
|
],
|
|
@@ -5305,7 +5983,7 @@ function toCompositionTransform(transform) {
|
|
|
5305
5983
|
],
|
|
5306
5984
|
};
|
|
5307
5985
|
}
|
|
5308
|
-
function clone(value) {
|
|
5986
|
+
function clone$1(value) {
|
|
5309
5987
|
if (value == null || typeof value !== 'object')
|
|
5310
5988
|
return value;
|
|
5311
5989
|
try {
|
|
@@ -5387,7 +6065,11 @@ class PageBuilderAiAdapter {
|
|
|
5387
6065
|
error: 'CompiledFormPatch must contain patch.page.',
|
|
5388
6066
|
};
|
|
5389
6067
|
}
|
|
5390
|
-
|
|
6068
|
+
const materializedPage = this.clone(page);
|
|
6069
|
+
if (this.shouldPreserveLocalTransientFields(compiledFormPatch)) {
|
|
6070
|
+
this.preserveLocalTransientFields(materializedPage);
|
|
6071
|
+
}
|
|
6072
|
+
return this.applyPatch({ page: materializedPage }, 'agentic-authoring.compiled-form-patch');
|
|
5391
6073
|
}
|
|
5392
6074
|
async applyUiCompositionPlan(plan) {
|
|
5393
6075
|
const compiled = compileUiCompositionPlan(plan);
|
|
@@ -5526,6 +6208,76 @@ class PageBuilderAiAdapter {
|
|
|
5526
6208
|
}
|
|
5527
6209
|
return merged;
|
|
5528
6210
|
}
|
|
6211
|
+
shouldPreserveLocalTransientFields(compiledFormPatch) {
|
|
6212
|
+
const warnings = compiledFormPatch['warnings'];
|
|
6213
|
+
return Array.isArray(warnings) && warnings.includes('server-backed-field-labels-customized-locally');
|
|
6214
|
+
}
|
|
6215
|
+
preserveLocalTransientFields(patchPage) {
|
|
6216
|
+
const basePage = this.normalizePageForAi(this.parsePage(this.host.page));
|
|
6217
|
+
const baseWidgets = basePage?.widgets ?? [];
|
|
6218
|
+
for (const patchWidget of patchPage.widgets ?? []) {
|
|
6219
|
+
if (patchWidget.definition?.id !== 'praxis-dynamic-form')
|
|
6220
|
+
continue;
|
|
6221
|
+
const key = this.getWidgetKey(patchWidget);
|
|
6222
|
+
if (!key)
|
|
6223
|
+
continue;
|
|
6224
|
+
const baseWidget = baseWidgets.find((widget) => this.getWidgetKey(widget) === key);
|
|
6225
|
+
if (!baseWidget)
|
|
6226
|
+
continue;
|
|
6227
|
+
this.preserveLocalTransientFieldsForWidget(baseWidget, patchWidget);
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
preserveLocalTransientFieldsForWidget(baseWidget, patchWidget) {
|
|
6231
|
+
const baseConfig = (baseWidget.definition?.inputs?.['config'] ?? {});
|
|
6232
|
+
const patchInputs = patchWidget.definition.inputs ??= {};
|
|
6233
|
+
const patchConfig = (patchInputs['config'] ??= {});
|
|
6234
|
+
const baseFields = Array.isArray(baseConfig['fieldMetadata']) ? baseConfig['fieldMetadata'] : [];
|
|
6235
|
+
const patchFields = Array.isArray(patchConfig['fieldMetadata'])
|
|
6236
|
+
? patchConfig['fieldMetadata']
|
|
6237
|
+
: (patchConfig['fieldMetadata'] = []);
|
|
6238
|
+
const preservedNames = [];
|
|
6239
|
+
for (const field of baseFields) {
|
|
6240
|
+
if (!this.isLocalTransientField(field) || patchFields.some((entry) => entry?.name === field.name))
|
|
6241
|
+
continue;
|
|
6242
|
+
patchFields.push(this.clone(field));
|
|
6243
|
+
preservedNames.push(field.name);
|
|
6244
|
+
}
|
|
6245
|
+
if (preservedNames.length === 0)
|
|
6246
|
+
return;
|
|
6247
|
+
const baseSections = Array.isArray(baseConfig['sections']) ? baseConfig['sections'] : [];
|
|
6248
|
+
const patchSections = Array.isArray(patchConfig['sections'])
|
|
6249
|
+
? patchConfig['sections']
|
|
6250
|
+
: (patchConfig['sections'] = []);
|
|
6251
|
+
for (const section of baseSections) {
|
|
6252
|
+
if (!this.sectionContainsAnyField(section, preservedNames))
|
|
6253
|
+
continue;
|
|
6254
|
+
if (this.sectionContainsAnyField({ rows: [{ columns: [{ fields: this.flattenSectionFields(patchSections) }] }] }, preservedNames)) {
|
|
6255
|
+
continue;
|
|
6256
|
+
}
|
|
6257
|
+
patchSections.push(this.clone(section));
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6260
|
+
isLocalTransientField(field) {
|
|
6261
|
+
return field?.source === 'local' || field?.transient === true || field?.submitPolicy === 'omit';
|
|
6262
|
+
}
|
|
6263
|
+
sectionContainsAnyField(section, names) {
|
|
6264
|
+
const fields = this.flattenSectionFields([section]);
|
|
6265
|
+
return names.some((name) => fields.includes(name));
|
|
6266
|
+
}
|
|
6267
|
+
flattenSectionFields(sections) {
|
|
6268
|
+
const fields = [];
|
|
6269
|
+
for (const section of sections) {
|
|
6270
|
+
for (const row of section?.rows ?? []) {
|
|
6271
|
+
for (const column of row?.columns ?? []) {
|
|
6272
|
+
for (const field of column?.fields ?? []) {
|
|
6273
|
+
if (typeof field === 'string')
|
|
6274
|
+
fields.push(field);
|
|
6275
|
+
}
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
}
|
|
6279
|
+
return fields;
|
|
6280
|
+
}
|
|
5529
6281
|
mergeLinks(baseLinks, patchLinks) {
|
|
5530
6282
|
const base = baseLinks || [];
|
|
5531
6283
|
const patch = patchLinks || [];
|
|
@@ -5646,20 +6398,37 @@ class PageBuilderAiAdapter {
|
|
|
5646
6398
|
linkKey(link) {
|
|
5647
6399
|
if (!link)
|
|
5648
6400
|
return null;
|
|
5649
|
-
const from = link.from
|
|
5650
|
-
|
|
5651
|
-
? `widget:${link.from.ref.widget}::${link.from.ref.port}::${link.from.ref.direction}`
|
|
5652
|
-
: null)
|
|
5653
|
-
: (link.from.ref.path ? `state:${link.from.ref.path}::${link.from.ref.layer || 'values'}` : null);
|
|
5654
|
-
const to = link.to.kind === 'component-port'
|
|
5655
|
-
? (link.to.ref.widget && link.to.ref.port
|
|
5656
|
-
? `widget:${link.to.ref.widget}::${link.to.ref.port}::${link.to.ref.direction}`
|
|
5657
|
-
: null)
|
|
5658
|
-
: (link.to.ref.path ? `state:${link.to.ref.path}::${link.to.ref.layer || 'values'}` : null);
|
|
6401
|
+
const from = this.endpointKey(link.from);
|
|
6402
|
+
const to = this.endpointKey(link.to);
|
|
5659
6403
|
if (!from || !to)
|
|
5660
6404
|
return null;
|
|
5661
6405
|
return `${from}->${to}`;
|
|
5662
6406
|
}
|
|
6407
|
+
endpointKey(endpoint) {
|
|
6408
|
+
if (endpoint.kind === 'component-port') {
|
|
6409
|
+
if (!endpoint.ref.widget || !endpoint.ref.port)
|
|
6410
|
+
return null;
|
|
6411
|
+
return [
|
|
6412
|
+
'widget',
|
|
6413
|
+
endpoint.ref.widget,
|
|
6414
|
+
endpoint.ref.port,
|
|
6415
|
+
endpoint.ref.direction,
|
|
6416
|
+
this.nestedPathKey(endpoint.ref.nestedPath),
|
|
6417
|
+
].filter((part) => part !== '').join('::');
|
|
6418
|
+
}
|
|
6419
|
+
return endpoint.ref.path ? `state:${endpoint.ref.path}::${endpoint.ref.layer || 'values'}` : null;
|
|
6420
|
+
}
|
|
6421
|
+
nestedPathKey(nestedPath) {
|
|
6422
|
+
if (!nestedPath?.length)
|
|
6423
|
+
return '';
|
|
6424
|
+
return `nested:${JSON.stringify(nestedPath.map((segment) => ({
|
|
6425
|
+
kind: segment.kind,
|
|
6426
|
+
id: segment.id,
|
|
6427
|
+
key: segment.key,
|
|
6428
|
+
index: segment.index,
|
|
6429
|
+
componentType: segment.componentType,
|
|
6430
|
+
})))}`;
|
|
6431
|
+
}
|
|
5663
6432
|
pickLayoutOptions(options) {
|
|
5664
6433
|
if (!options)
|
|
5665
6434
|
return undefined;
|
|
@@ -5717,42 +6486,60 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5717
6486
|
this.service = service;
|
|
5718
6487
|
this.context = context;
|
|
5719
6488
|
}
|
|
5720
|
-
|
|
6489
|
+
submit(request) {
|
|
5721
6490
|
const prompt = request.prompt?.trim();
|
|
5722
6491
|
if (!prompt) {
|
|
5723
|
-
return {
|
|
6492
|
+
return Promise.resolve({
|
|
5724
6493
|
state: 'listening',
|
|
5725
6494
|
phase: 'capture',
|
|
5726
|
-
};
|
|
6495
|
+
});
|
|
5727
6496
|
}
|
|
6497
|
+
if (this.isResourceDiscoveryTool(request)) {
|
|
6498
|
+
return this.handleResourceDiscoveryTool(request, prompt);
|
|
6499
|
+
}
|
|
6500
|
+
if (this.context.enableTurnStream?.() === true && this.service.streamTurn) {
|
|
6501
|
+
return this.submitWithTurnStream(request, prompt);
|
|
6502
|
+
}
|
|
6503
|
+
return this.submitSynchronously(request, prompt);
|
|
6504
|
+
}
|
|
6505
|
+
async submitSynchronously(request, prompt) {
|
|
5728
6506
|
try {
|
|
5729
6507
|
const authoringContext = this.buildAuthoringContext(request);
|
|
5730
6508
|
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
5731
6509
|
const selectedWidgetKey = this.context.selectedWidgetKey();
|
|
5732
|
-
const intentResolution = await firstValueFrom(this.service.resolveIntent(
|
|
5733
|
-
userPrompt: prompt,
|
|
5734
|
-
targetApp: this.context.targetApp,
|
|
5735
|
-
targetComponentId: this.context.targetComponentId,
|
|
5736
|
-
currentPage: this.context.currentPage(),
|
|
5737
|
-
selectedWidgetKey,
|
|
5738
|
-
provider: this.context.provider(),
|
|
5739
|
-
model: this.context.model(),
|
|
5740
|
-
apiKey: this.context.apiKey(),
|
|
5741
|
-
componentCapabilities,
|
|
5742
|
-
...authoringContext,
|
|
5743
|
-
}));
|
|
6510
|
+
const intentResolution = await firstValueFrom(this.service.resolveIntent(this.buildIntentResolutionRequest(prompt, selectedWidgetKey, componentCapabilities, authoringContext)));
|
|
5744
6511
|
const intentAssistantMessage = this.resolveIntentAssistantMessage(intentResolution);
|
|
5745
6512
|
if (intentAssistantMessage) {
|
|
6513
|
+
const quickReplies = this.toShellQuickReplies(intentResolution.quickReplies ?? []);
|
|
6514
|
+
const explicitPendingClarification = this.toShellPendingClarification(intentResolution.pendingClarification);
|
|
6515
|
+
const intentClarification = this.resolveIntentClarification(intentResolution);
|
|
6516
|
+
const pendingClarification = explicitPendingClarification
|
|
6517
|
+
?? (intentClarification
|
|
6518
|
+
? this.buildPendingClarificationForNextTurn(request, this.resolveEffectivePrompt(intentResolution, prompt), intentClarification)
|
|
6519
|
+
: null);
|
|
6520
|
+
if (!this.hasPendingClarificationQuestion(pendingClarification) && quickReplies.length === 0) {
|
|
6521
|
+
return {
|
|
6522
|
+
state: 'success',
|
|
6523
|
+
phase: 'summarize',
|
|
6524
|
+
assistantMessage: intentAssistantMessage,
|
|
6525
|
+
quickReplies,
|
|
6526
|
+
canApply: false,
|
|
6527
|
+
statusText: '',
|
|
6528
|
+
errorText: '',
|
|
6529
|
+
preview: null,
|
|
6530
|
+
diagnostics: { intentResolution },
|
|
6531
|
+
};
|
|
6532
|
+
}
|
|
5746
6533
|
return {
|
|
5747
6534
|
state: 'clarification',
|
|
5748
6535
|
phase: 'clarify',
|
|
5749
6536
|
assistantMessage: intentAssistantMessage,
|
|
5750
|
-
quickReplies
|
|
6537
|
+
quickReplies,
|
|
5751
6538
|
canApply: false,
|
|
5752
6539
|
statusText: '',
|
|
5753
6540
|
errorText: '',
|
|
5754
6541
|
preview: null,
|
|
5755
|
-
pendingClarification
|
|
6542
|
+
pendingClarification,
|
|
5756
6543
|
diagnostics: { intentResolution },
|
|
5757
6544
|
};
|
|
5758
6545
|
}
|
|
@@ -5827,6 +6614,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5827
6614
|
state: 'review',
|
|
5828
6615
|
phase: 'review',
|
|
5829
6616
|
assistantMessage: status,
|
|
6617
|
+
quickReplies: [],
|
|
5830
6618
|
canApply: true,
|
|
5831
6619
|
statusText: status,
|
|
5832
6620
|
errorText: '',
|
|
@@ -5847,6 +6635,11 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5847
6635
|
};
|
|
5848
6636
|
}
|
|
5849
6637
|
}
|
|
6638
|
+
submitWithTurnStream(request, prompt) {
|
|
6639
|
+
return from(this.buildTurnStreamRequest(request, prompt)).pipe(concatMap((streamRequest) => this.service.streamTurn(streamRequest)), concatMap((event) => from(this.toTurnResultFromStreamEvent(event))), catchError((error) => this.shouldFallbackFromTurnStreamError(error)
|
|
6640
|
+
? from(this.submitSynchronously(request, prompt))
|
|
6641
|
+
: of(this.toTurnStreamTransportErrorResult(error))));
|
|
6642
|
+
}
|
|
5850
6643
|
async cancel() {
|
|
5851
6644
|
return {
|
|
5852
6645
|
state: 'listening',
|
|
@@ -5860,10 +6653,413 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5860
6653
|
pendingPatch: null,
|
|
5861
6654
|
};
|
|
5862
6655
|
}
|
|
6656
|
+
async buildTurnStreamRequest(request, prompt) {
|
|
6657
|
+
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
6658
|
+
const selectedWidgetKey = this.context.selectedWidgetKey();
|
|
6659
|
+
return {
|
|
6660
|
+
userPrompt: prompt,
|
|
6661
|
+
targetApp: this.context.targetApp,
|
|
6662
|
+
targetComponentId: this.context.targetComponentId,
|
|
6663
|
+
currentRoute: null,
|
|
6664
|
+
currentPage: this.context.currentPage(),
|
|
6665
|
+
selectedWidgetKey,
|
|
6666
|
+
provider: this.context.provider(),
|
|
6667
|
+
model: this.context.model(),
|
|
6668
|
+
apiKey: this.context.apiKey(),
|
|
6669
|
+
componentCapabilities,
|
|
6670
|
+
...this.buildAuthoringContext(request),
|
|
6671
|
+
};
|
|
6672
|
+
}
|
|
6673
|
+
async toTurnResultFromStreamEvent(event) {
|
|
6674
|
+
const payload = this.toJsonObject(event.payload) ?? {};
|
|
6675
|
+
if (event.type === 'result') {
|
|
6676
|
+
return this.toResultTurnFromStreamPayload(payload);
|
|
6677
|
+
}
|
|
6678
|
+
if (event.type === 'error') {
|
|
6679
|
+
const message = this.describeStreamError(payload);
|
|
6680
|
+
return {
|
|
6681
|
+
state: 'error',
|
|
6682
|
+
phase: 'contextualize',
|
|
6683
|
+
assistantMessage: message,
|
|
6684
|
+
canApply: false,
|
|
6685
|
+
statusText: '',
|
|
6686
|
+
errorText: message,
|
|
6687
|
+
preview: null,
|
|
6688
|
+
diagnostics: { streamError: payload },
|
|
6689
|
+
};
|
|
6690
|
+
}
|
|
6691
|
+
if (event.type === 'cancelled') {
|
|
6692
|
+
return {
|
|
6693
|
+
state: 'listening',
|
|
6694
|
+
phase: 'capture',
|
|
6695
|
+
assistantMessage: this.context.tx('agentic.status.cancelled', 'Request cancelled.'),
|
|
6696
|
+
quickReplies: [],
|
|
6697
|
+
canApply: false,
|
|
6698
|
+
statusText: '',
|
|
6699
|
+
errorText: '',
|
|
6700
|
+
preview: null,
|
|
6701
|
+
pendingPatch: null,
|
|
6702
|
+
};
|
|
6703
|
+
}
|
|
6704
|
+
return {
|
|
6705
|
+
state: 'processing',
|
|
6706
|
+
phase: this.phaseForStreamPayload(payload),
|
|
6707
|
+
assistantMessage: undefined,
|
|
6708
|
+
canApply: false,
|
|
6709
|
+
statusText: this.statusForStreamPayload(payload),
|
|
6710
|
+
errorText: '',
|
|
6711
|
+
preview: null,
|
|
6712
|
+
};
|
|
6713
|
+
}
|
|
6714
|
+
async toResultTurnFromStreamPayload(payload) {
|
|
6715
|
+
const intentResolution = payload['intentResolution'];
|
|
6716
|
+
const preview = payload['preview'];
|
|
6717
|
+
if (!intentResolution) {
|
|
6718
|
+
const message = this.context.tx('agentic.errors.intentResolution', 'Intent could not be resolved.');
|
|
6719
|
+
return {
|
|
6720
|
+
state: 'error',
|
|
6721
|
+
phase: 'contextualize',
|
|
6722
|
+
assistantMessage: message,
|
|
6723
|
+
canApply: false,
|
|
6724
|
+
statusText: '',
|
|
6725
|
+
errorText: message,
|
|
6726
|
+
preview: null,
|
|
6727
|
+
};
|
|
6728
|
+
}
|
|
6729
|
+
const assistantMessage = this.readString(payload, 'assistantMessage')
|
|
6730
|
+
|| this.resolveIntentAssistantMessage(intentResolution)
|
|
6731
|
+
|| (preview ? this.context.describePreviewStatus(preview) : '');
|
|
6732
|
+
const quickReplies = Array.isArray(payload['quickReplies'])
|
|
6733
|
+
? this.toShellQuickReplies(payload['quickReplies'])
|
|
6734
|
+
: [];
|
|
6735
|
+
const canApply = payload['canApply'] === true && !!preview?.valid;
|
|
6736
|
+
if (!canApply) {
|
|
6737
|
+
const pendingClarification = this.toShellPendingClarification(intentResolution?.pendingClarification);
|
|
6738
|
+
const requiresChoice = quickReplies.length > 0;
|
|
6739
|
+
return {
|
|
6740
|
+
state: pendingClarification || requiresChoice ? 'clarification' : 'success',
|
|
6741
|
+
phase: pendingClarification || requiresChoice ? 'clarify' : 'summarize',
|
|
6742
|
+
assistantMessage,
|
|
6743
|
+
quickReplies,
|
|
6744
|
+
canApply: false,
|
|
6745
|
+
statusText: '',
|
|
6746
|
+
errorText: '',
|
|
6747
|
+
preview: null,
|
|
6748
|
+
pendingClarification,
|
|
6749
|
+
diagnostics: { intentResolution, preview },
|
|
6750
|
+
};
|
|
6751
|
+
}
|
|
6752
|
+
const applied = await this.context.applyLocalPreview(preview);
|
|
6753
|
+
if (!applied.success) {
|
|
6754
|
+
const message = applied.error || this.context.tx('agentic.errors.applyLocal', 'Preview could not be applied.');
|
|
6755
|
+
return {
|
|
6756
|
+
state: 'error',
|
|
6757
|
+
phase: 'preview',
|
|
6758
|
+
assistantMessage: message,
|
|
6759
|
+
canApply: false,
|
|
6760
|
+
statusText: '',
|
|
6761
|
+
errorText: message,
|
|
6762
|
+
preview: null,
|
|
6763
|
+
diagnostics: { intentResolution, preview },
|
|
6764
|
+
};
|
|
6765
|
+
}
|
|
6766
|
+
const status = assistantMessage || this.context.describePreviewStatus(preview);
|
|
6767
|
+
return {
|
|
6768
|
+
state: 'review',
|
|
6769
|
+
phase: 'review',
|
|
6770
|
+
assistantMessage: status,
|
|
6771
|
+
quickReplies: [],
|
|
6772
|
+
canApply: true,
|
|
6773
|
+
statusText: status,
|
|
6774
|
+
errorText: '',
|
|
6775
|
+
preview,
|
|
6776
|
+
diagnostics: { intentResolution, preview },
|
|
6777
|
+
};
|
|
6778
|
+
}
|
|
6779
|
+
phaseForStreamPayload(payload) {
|
|
6780
|
+
const phase = this.readString(payload, 'phase');
|
|
6781
|
+
if (phase === 'intent.resolve')
|
|
6782
|
+
return 'contextualize';
|
|
6783
|
+
if (phase === 'resource.discovery')
|
|
6784
|
+
return 'contextualize';
|
|
6785
|
+
if (phase.startsWith('preview.'))
|
|
6786
|
+
return 'preview';
|
|
6787
|
+
if (phase === 'review')
|
|
6788
|
+
return 'review';
|
|
6789
|
+
return 'contextualize';
|
|
6790
|
+
}
|
|
6791
|
+
statusForStreamPayload(payload) {
|
|
6792
|
+
const phase = this.readString(payload, 'phase');
|
|
6793
|
+
switch (phase) {
|
|
6794
|
+
case 'context.bundle':
|
|
6795
|
+
return this.context.tx('agentic.status.contextBundle', 'Preparing context...');
|
|
6796
|
+
case 'intent.resolve':
|
|
6797
|
+
if (this.isSecondPassStreamPayload(payload)) {
|
|
6798
|
+
return this.context.tx('agentic.status.refinedCandidates', 'Reviewing the retrieved resources with the AI...');
|
|
6799
|
+
}
|
|
6800
|
+
return this.context.tx('agentic.status.resolvingIntent', 'Resolving intent...');
|
|
6801
|
+
case 'resource.discovery':
|
|
6802
|
+
if (this.isBackendResourceDiscoveryPayload(payload)) {
|
|
6803
|
+
return this.context.tx('agentic.status.resourceDiscoveryBackend', 'Found API resources in the backend catalog.');
|
|
6804
|
+
}
|
|
6805
|
+
return this.context.tx('agentic.status.resourceDiscovery', 'Finding API resources...');
|
|
6806
|
+
case 'preview.plan':
|
|
6807
|
+
return this.context.tx('agentic.status.previewing', 'Generating preview...');
|
|
6808
|
+
case 'preview.compile':
|
|
6809
|
+
return this.context.tx('agentic.status.previewCompile', 'Compiling preview...');
|
|
6810
|
+
default:
|
|
6811
|
+
return this.readString(payload, 'summary') || this.readString(payload, 'message');
|
|
6812
|
+
}
|
|
6813
|
+
}
|
|
6814
|
+
isBackendResourceDiscoveryPayload(payload) {
|
|
6815
|
+
const diagnostics = this.toJsonObject(payload['diagnostics']);
|
|
6816
|
+
return diagnostics?.['source'] === 'backend-resource-catalog';
|
|
6817
|
+
}
|
|
6818
|
+
isSecondPassStreamPayload(payload) {
|
|
6819
|
+
const diagnostics = this.toJsonObject(payload['diagnostics']);
|
|
6820
|
+
return diagnostics?.['secondPass'] === true;
|
|
6821
|
+
}
|
|
6822
|
+
describeStreamError(payload) {
|
|
6823
|
+
const code = this.readString(payload, 'code');
|
|
6824
|
+
if (code === 'agentic-authoring-timeout') {
|
|
6825
|
+
return this.context.tx('agentic.errors.streamTimeout', 'The assistant took too long to finish this request. Try again with a narrower request or review the active context.');
|
|
6826
|
+
}
|
|
6827
|
+
if (code === 'agentic-authoring-processing-failed') {
|
|
6828
|
+
return this.context.tx('agentic.errors.streamProcessing', 'The assistant could not finish this authoring request. Try again or ask support to review the diagnostics.');
|
|
6829
|
+
}
|
|
6830
|
+
return this.readString(payload, 'assistantMessage')
|
|
6831
|
+
|| this.context.tx('agentic.errors.generic', 'AI authoring failed.');
|
|
6832
|
+
}
|
|
6833
|
+
toTurnStreamTransportErrorResult(error) {
|
|
6834
|
+
const message = this.isTurnStreamConnectionError(error)
|
|
6835
|
+
? this.context.tx('agentic.errors.streamConnection', 'The assistant stream was interrupted before the turn finished. Try again or ask support to check the stream connection.')
|
|
6836
|
+
: this.context.describeError(error);
|
|
6837
|
+
return {
|
|
6838
|
+
state: 'error',
|
|
6839
|
+
phase: 'contextualize',
|
|
6840
|
+
assistantMessage: message,
|
|
6841
|
+
canApply: false,
|
|
6842
|
+
statusText: '',
|
|
6843
|
+
errorText: message,
|
|
6844
|
+
preview: null,
|
|
6845
|
+
diagnostics: { streamTransportError: this.toJsonObject(error) ?? { reason: 'stream-transport-error' } },
|
|
6846
|
+
};
|
|
6847
|
+
}
|
|
6848
|
+
shouldFallbackFromTurnStreamError(error) {
|
|
6849
|
+
if (this.isTurnStreamConnectionError(error)) {
|
|
6850
|
+
return false;
|
|
6851
|
+
}
|
|
6852
|
+
const status = this.readErrorStatus(error);
|
|
6853
|
+
if (status === 401 || status === 403) {
|
|
6854
|
+
return false;
|
|
6855
|
+
}
|
|
6856
|
+
return status === 404 || status === 501 || status === 503;
|
|
6857
|
+
}
|
|
6858
|
+
isTurnStreamConnectionError(error) {
|
|
6859
|
+
return this.toJsonObject(error)?.['praxisAgenticTurnStreamConnectionError'] === true;
|
|
6860
|
+
}
|
|
6861
|
+
readErrorStatus(error) {
|
|
6862
|
+
const status = this.toJsonObject(error)?.['status'];
|
|
6863
|
+
return typeof status === 'number' && Number.isFinite(status) ? status : null;
|
|
6864
|
+
}
|
|
6865
|
+
async handleResourceDiscoveryTool(request, prompt) {
|
|
6866
|
+
const contextHints = this.toJsonObject(request.action?.contextHints) ?? {};
|
|
6867
|
+
const retrievalQuery = this.readString(contextHints, 'retrievalQuery') || prompt;
|
|
6868
|
+
const artifactKind = this.readString(contextHints, 'artifactKind') || null;
|
|
6869
|
+
const limit = this.readNumber(contextHints, 'limit');
|
|
6870
|
+
const result = await firstValueFrom(this.service.searchResourceCandidates({
|
|
6871
|
+
retrievalQuery,
|
|
6872
|
+
userPrompt: prompt,
|
|
6873
|
+
artifactKind,
|
|
6874
|
+
limit,
|
|
6875
|
+
}));
|
|
6876
|
+
const quickReplies = result.quickReplies?.length
|
|
6877
|
+
? this.toShellQuickReplies(result.quickReplies)
|
|
6878
|
+
: this.toResourceCandidateQuickReplies(result.candidates, result.artifactKind);
|
|
6879
|
+
const assistantMessage = result.assistantMessage?.trim()
|
|
6880
|
+
|| (quickReplies.length
|
|
6881
|
+
? this.context.tx('agentic.resourceDiscovery.found', 'I found APIs that can feed this screen. Choose one before generating the preview.')
|
|
6882
|
+
: this.context.tx('agentic.resourceDiscovery.empty', 'I did not find a matching API yet. Describe the business data this screen should use.'));
|
|
6883
|
+
const intentResult = await this.resolveIntentAfterResourceDiscovery(request, request.pendingClarification?.sourcePrompt || prompt, contextHints, result, assistantMessage, quickReplies);
|
|
6884
|
+
if (intentResult) {
|
|
6885
|
+
return intentResult;
|
|
6886
|
+
}
|
|
6887
|
+
return {
|
|
6888
|
+
state: 'clarification',
|
|
6889
|
+
phase: 'clarify',
|
|
6890
|
+
assistantMessage,
|
|
6891
|
+
quickReplies,
|
|
6892
|
+
canApply: false,
|
|
6893
|
+
statusText: '',
|
|
6894
|
+
errorText: '',
|
|
6895
|
+
preview: null,
|
|
6896
|
+
pendingClarification: {
|
|
6897
|
+
sourcePrompt: request.pendingClarification?.sourcePrompt || prompt,
|
|
6898
|
+
assistantMessage,
|
|
6899
|
+
questions: [
|
|
6900
|
+
{
|
|
6901
|
+
id: 'resource',
|
|
6902
|
+
type: 'text',
|
|
6903
|
+
label: assistantMessage,
|
|
6904
|
+
},
|
|
6905
|
+
],
|
|
6906
|
+
clientTurnId: request.clientTurnId,
|
|
6907
|
+
diagnostics: {
|
|
6908
|
+
resourceDiscovery: {
|
|
6909
|
+
tool: result.tool,
|
|
6910
|
+
retrievalQuery: result.retrievalQuery,
|
|
6911
|
+
artifactKind: result.artifactKind,
|
|
6912
|
+
warnings: result.warnings,
|
|
6913
|
+
},
|
|
6914
|
+
},
|
|
6915
|
+
},
|
|
6916
|
+
diagnostics: { resourceDiscovery: result },
|
|
6917
|
+
};
|
|
6918
|
+
}
|
|
6919
|
+
async resolveIntentAfterResourceDiscovery(request, prompt, contextHints, result, fallbackAssistantMessage, fallbackQuickReplies) {
|
|
6920
|
+
if (!result.valid || !result.candidates?.length) {
|
|
6921
|
+
return null;
|
|
6922
|
+
}
|
|
6923
|
+
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
6924
|
+
const authoringContext = this.buildAuthoringContext(request);
|
|
6925
|
+
const intentResolution = await firstValueFrom(this.service.resolveIntent(this.buildIntentResolutionRequest(prompt, this.context.selectedWidgetKey(), componentCapabilities, {
|
|
6926
|
+
...authoringContext,
|
|
6927
|
+
contextHints: this.resourceDiscoveryContextHints(contextHints, result),
|
|
6928
|
+
})));
|
|
6929
|
+
const assistantMessage = this.resolveIntentAssistantMessage(intentResolution)
|
|
6930
|
+
|| this.resolveIntentClarification(intentResolution)
|
|
6931
|
+
|| fallbackAssistantMessage;
|
|
6932
|
+
const quickReplies = intentResolution.quickReplies?.length
|
|
6933
|
+
? this.toShellQuickReplies(intentResolution.quickReplies)
|
|
6934
|
+
: fallbackQuickReplies;
|
|
6935
|
+
const explicitPendingClarification = this.toShellPendingClarification(intentResolution.pendingClarification);
|
|
6936
|
+
const pendingClarification = explicitPendingClarification
|
|
6937
|
+
?? this.buildPendingClarificationForNextTurn(request, this.resolveEffectivePrompt(intentResolution, prompt), assistantMessage);
|
|
6938
|
+
if (!assistantMessage && quickReplies.length === 0) {
|
|
6939
|
+
return null;
|
|
6940
|
+
}
|
|
6941
|
+
return {
|
|
6942
|
+
state: quickReplies.length > 0 || this.hasPendingClarificationQuestion(pendingClarification)
|
|
6943
|
+
? 'clarification'
|
|
6944
|
+
: 'success',
|
|
6945
|
+
phase: quickReplies.length > 0 || this.hasPendingClarificationQuestion(pendingClarification)
|
|
6946
|
+
? 'clarify'
|
|
6947
|
+
: 'summarize',
|
|
6948
|
+
assistantMessage,
|
|
6949
|
+
quickReplies,
|
|
6950
|
+
canApply: false,
|
|
6951
|
+
statusText: '',
|
|
6952
|
+
errorText: '',
|
|
6953
|
+
preview: null,
|
|
6954
|
+
pendingClarification,
|
|
6955
|
+
diagnostics: { resourceDiscovery: result, intentResolution },
|
|
6956
|
+
};
|
|
6957
|
+
}
|
|
6958
|
+
resourceDiscoveryContextHints(contextHints, result) {
|
|
6959
|
+
const { tool: _tool, ...baseContextHints } = contextHints;
|
|
6960
|
+
return {
|
|
6961
|
+
...baseContextHints,
|
|
6962
|
+
resourceDiscovery: {
|
|
6963
|
+
tool: result.tool,
|
|
6964
|
+
retrievalQuery: result.retrievalQuery,
|
|
6965
|
+
artifactKind: result.artifactKind,
|
|
6966
|
+
assistantMessage: result.assistantMessage ?? null,
|
|
6967
|
+
candidates: result.candidates.map((candidate) => ({
|
|
6968
|
+
resourcePath: candidate.resourcePath,
|
|
6969
|
+
operation: candidate.operation,
|
|
6970
|
+
schemaUrl: candidate.schemaUrl,
|
|
6971
|
+
submitUrl: candidate.submitUrl,
|
|
6972
|
+
submitMethod: candidate.submitMethod,
|
|
6973
|
+
score: candidate.score,
|
|
6974
|
+
reason: candidate.reason,
|
|
6975
|
+
evidence: candidate.evidence,
|
|
6976
|
+
})),
|
|
6977
|
+
quickReplies: (result.quickReplies ?? []).map((reply) => ({
|
|
6978
|
+
id: reply.id,
|
|
6979
|
+
kind: reply.kind,
|
|
6980
|
+
label: reply.label,
|
|
6981
|
+
prompt: reply.prompt,
|
|
6982
|
+
description: reply.description ?? null,
|
|
6983
|
+
icon: reply.icon ?? null,
|
|
6984
|
+
tone: reply.tone ?? null,
|
|
6985
|
+
contextHints: this.toJsonObject(reply.contextHints) ?? null,
|
|
6986
|
+
})),
|
|
6987
|
+
warnings: result.warnings,
|
|
6988
|
+
},
|
|
6989
|
+
};
|
|
6990
|
+
}
|
|
6991
|
+
isResourceDiscoveryTool(request) {
|
|
6992
|
+
const contextHints = this.toJsonObject(request.action?.contextHints);
|
|
6993
|
+
return this.readString(contextHints, 'tool') === 'searchApiResources';
|
|
6994
|
+
}
|
|
6995
|
+
toResourceCandidateQuickReplies(candidates, artifactKind) {
|
|
6996
|
+
return candidates
|
|
6997
|
+
.filter((candidate) => !!candidate.resourcePath?.trim())
|
|
6998
|
+
.map((candidate) => {
|
|
6999
|
+
const resourcePath = candidate.resourcePath.trim();
|
|
7000
|
+
const submitUrl = candidate.submitUrl?.trim() || resourcePath;
|
|
7001
|
+
return {
|
|
7002
|
+
id: this.resourceCandidateId(resourcePath),
|
|
7003
|
+
kind: 'suggestion',
|
|
7004
|
+
label: this.resourceCandidateLabel(resourcePath),
|
|
7005
|
+
prompt: this.formatResourceCandidatePrompt(resourcePath),
|
|
7006
|
+
description: this.resourceCandidateDescription(candidate),
|
|
7007
|
+
icon: 'dataset',
|
|
7008
|
+
tone: 'resource',
|
|
7009
|
+
contextHints: {
|
|
7010
|
+
resourcePath,
|
|
7011
|
+
submitUrl,
|
|
7012
|
+
operation: candidate.operation,
|
|
7013
|
+
schemaUrl: candidate.schemaUrl,
|
|
7014
|
+
submitMethod: candidate.submitMethod,
|
|
7015
|
+
artifactKind,
|
|
7016
|
+
},
|
|
7017
|
+
};
|
|
7018
|
+
});
|
|
7019
|
+
}
|
|
7020
|
+
formatResourceCandidatePrompt(resourcePath) {
|
|
7021
|
+
return this.context
|
|
7022
|
+
.tx('agentic.resourceDiscovery.useResource', 'Use {resourcePath}')
|
|
7023
|
+
.replace('{resourcePath}', resourcePath);
|
|
7024
|
+
}
|
|
7025
|
+
resourceCandidateId(resourcePath) {
|
|
7026
|
+
const slug = resourcePath
|
|
7027
|
+
.toLowerCase()
|
|
7028
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
7029
|
+
.replace(/^-+|-+$/g, '');
|
|
7030
|
+
return slug ? `resource-${slug}` : 'resource-candidate';
|
|
7031
|
+
}
|
|
7032
|
+
resourceCandidateLabel(resourcePath) {
|
|
7033
|
+
const segment = resourcePath.split('/').filter((part) => !!part).pop() || resourcePath;
|
|
7034
|
+
const label = segment
|
|
7035
|
+
.replace(/[-_]+/g, ' ')
|
|
7036
|
+
.replace(/\s+/g, ' ')
|
|
7037
|
+
.trim();
|
|
7038
|
+
return label
|
|
7039
|
+
? label.charAt(0).toUpperCase() + label.slice(1)
|
|
7040
|
+
: resourcePath;
|
|
7041
|
+
}
|
|
7042
|
+
resourceCandidateDescription(candidate) {
|
|
7043
|
+
const method = candidate.submitMethod?.trim();
|
|
7044
|
+
const url = candidate.submitUrl?.trim();
|
|
7045
|
+
return method && url ? `${method} ${url}` : url || undefined;
|
|
7046
|
+
}
|
|
5863
7047
|
resolveIntentAssistantMessage(intentResolution) {
|
|
7048
|
+
if (this.isExecutableIntent(intentResolution)) {
|
|
7049
|
+
return null;
|
|
7050
|
+
}
|
|
5864
7051
|
const message = intentResolution.assistantMessage?.trim();
|
|
5865
7052
|
return message || null;
|
|
5866
7053
|
}
|
|
7054
|
+
isExecutableIntent(intentResolution) {
|
|
7055
|
+
const operationKind = intentResolution.operationKind;
|
|
7056
|
+
const artifactKind = intentResolution.artifactKind;
|
|
7057
|
+
return !!intentResolution.valid
|
|
7058
|
+
&& intentResolution.gate?.status === 'eligible'
|
|
7059
|
+
&& !!intentResolution.selectedCandidate
|
|
7060
|
+
&& (artifactKind === 'form' || artifactKind === 'dashboard' || artifactKind === 'table' || artifactKind === 'page')
|
|
7061
|
+
&& (operationKind === 'create' || operationKind === 'modify' || operationKind === 'remove' || operationKind === 'compose');
|
|
7062
|
+
}
|
|
5867
7063
|
resolveIntentClarification(intentResolution) {
|
|
5868
7064
|
const questions = intentResolution.clarificationQuestions?.filter((question) => !!question) ?? [];
|
|
5869
7065
|
if (!intentResolution.valid && questions.length) {
|
|
@@ -5872,7 +7068,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5872
7068
|
return null;
|
|
5873
7069
|
}
|
|
5874
7070
|
describeIntentResolutionFailure(intentResolution) {
|
|
5875
|
-
if (
|
|
7071
|
+
if (this.isExecutableIntent(intentResolution)) {
|
|
5876
7072
|
return null;
|
|
5877
7073
|
}
|
|
5878
7074
|
const questions = intentResolution.clarificationQuestions?.filter((question) => !!question) ?? [];
|
|
@@ -5897,6 +7093,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5897
7093
|
kind: reply.kind,
|
|
5898
7094
|
label: reply.label,
|
|
5899
7095
|
prompt: reply.prompt,
|
|
7096
|
+
description: reply.description,
|
|
7097
|
+
icon: reply.icon,
|
|
7098
|
+
tone: reply.tone,
|
|
7099
|
+
contextHints: this.toJsonObject(reply.contextHints),
|
|
5900
7100
|
}));
|
|
5901
7101
|
}
|
|
5902
7102
|
buildPendingClarificationForNextTurn(request, sourcePrompt, clarification) {
|
|
@@ -5947,16 +7147,47 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5947
7147
|
diagnostics: this.toJsonObject(pending?.diagnostics) ?? undefined,
|
|
5948
7148
|
};
|
|
5949
7149
|
}
|
|
7150
|
+
hasPendingClarificationQuestion(pending) {
|
|
7151
|
+
return !!pending?.sourcePrompt?.trim() && pending.questions.some((question) => !!question.label?.trim());
|
|
7152
|
+
}
|
|
5950
7153
|
resolveEffectivePrompt(intentResolution, fallbackPrompt) {
|
|
5951
7154
|
return intentResolution.effectivePrompt?.trim() || fallbackPrompt;
|
|
5952
7155
|
}
|
|
5953
7156
|
buildAuthoringContext(request) {
|
|
7157
|
+
const contextHints = this.buildContextHints(request);
|
|
5954
7158
|
return {
|
|
5955
7159
|
sessionId: request.sessionId,
|
|
5956
7160
|
clientTurnId: request.clientTurnId,
|
|
5957
7161
|
conversationMessages: this.toConversationMessages(request.messages ?? []),
|
|
5958
7162
|
pendingClarification: this.toPendingClarification(request.pendingClarification),
|
|
5959
7163
|
attachmentSummaries: this.toAttachmentSummaries(request.attachments ?? []),
|
|
7164
|
+
contextHints,
|
|
7165
|
+
};
|
|
7166
|
+
}
|
|
7167
|
+
buildIntentResolutionRequest(prompt, selectedWidgetKey, componentCapabilities, authoringContext) {
|
|
7168
|
+
return {
|
|
7169
|
+
userPrompt: prompt,
|
|
7170
|
+
targetApp: this.context.targetApp,
|
|
7171
|
+
targetComponentId: this.context.targetComponentId,
|
|
7172
|
+
currentPage: this.context.currentPage(),
|
|
7173
|
+
selectedWidgetKey,
|
|
7174
|
+
provider: this.context.provider(),
|
|
7175
|
+
model: this.context.model(),
|
|
7176
|
+
apiKey: this.context.apiKey(),
|
|
7177
|
+
componentCapabilities,
|
|
7178
|
+
...authoringContext,
|
|
7179
|
+
};
|
|
7180
|
+
}
|
|
7181
|
+
buildContextHints(request) {
|
|
7182
|
+
const base = this.toJsonObject(request.action?.contextHints)
|
|
7183
|
+
?? this.toJsonObject(request.contextHints);
|
|
7184
|
+
const includeLlmDiagnostics = this.context.includeLlmDiagnostics?.() === true;
|
|
7185
|
+
if (!includeLlmDiagnostics) {
|
|
7186
|
+
return base ?? undefined;
|
|
7187
|
+
}
|
|
7188
|
+
return {
|
|
7189
|
+
...(base ?? {}),
|
|
7190
|
+
includeLlmDiagnostics: true,
|
|
5960
7191
|
};
|
|
5961
7192
|
}
|
|
5962
7193
|
toAttachmentSummaries(attachments) {
|
|
@@ -6005,6 +7236,14 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
6005
7236
|
? value
|
|
6006
7237
|
: null;
|
|
6007
7238
|
}
|
|
7239
|
+
readString(value, key) {
|
|
7240
|
+
const raw = value?.[key];
|
|
7241
|
+
return typeof raw === 'string' ? raw.trim() : '';
|
|
7242
|
+
}
|
|
7243
|
+
readNumber(value, key) {
|
|
7244
|
+
const raw = value?.[key];
|
|
7245
|
+
return typeof raw === 'number' && Number.isFinite(raw) ? raw : null;
|
|
7246
|
+
}
|
|
6008
7247
|
}
|
|
6009
7248
|
|
|
6010
7249
|
function buildConnectionsViewerModel(page) {
|
|
@@ -6017,6 +7256,12 @@ function buildConnectionsViewerModel(page) {
|
|
|
6017
7256
|
const edges = links.map((link) => {
|
|
6018
7257
|
const fromWidgetId = link.from.kind === 'component-port' ? link.from.ref.widget : null;
|
|
6019
7258
|
const toWidgetId = link.to.kind === 'component-port' ? link.to.ref.widget : null;
|
|
7259
|
+
const fromNestedPath = link.from.kind === 'component-port' && link.from.ref.nestedPath?.length
|
|
7260
|
+
? clone(link.from.ref.nestedPath)
|
|
7261
|
+
: null;
|
|
7262
|
+
const toNestedPath = link.to.kind === 'component-port' && link.to.ref.nestedPath?.length
|
|
7263
|
+
? clone(link.to.ref.nestedPath)
|
|
7264
|
+
: null;
|
|
6020
7265
|
const toStatePath = link.to.kind === 'state' ? link.to.ref.path : null;
|
|
6021
7266
|
const diagnosticReasons = [];
|
|
6022
7267
|
if (fromWidgetId && !counts.has(fromWidgetId)) {
|
|
@@ -6039,9 +7284,17 @@ function buildConnectionsViewerModel(page) {
|
|
|
6039
7284
|
id: link.id,
|
|
6040
7285
|
fromKind: link.from.kind,
|
|
6041
7286
|
fromWidgetId,
|
|
7287
|
+
fromEndpointId: link.from.kind === 'component-port'
|
|
7288
|
+
? buildComponentEndpointId(link.from.ref.widget, link.from.ref.nestedPath)
|
|
7289
|
+
: null,
|
|
7290
|
+
fromNestedPath,
|
|
6042
7291
|
fromPort: link.from.kind === 'component-port' ? link.from.ref.port : null,
|
|
6043
7292
|
toKind: link.to.kind,
|
|
6044
7293
|
toWidgetId,
|
|
7294
|
+
toEndpointId: link.to.kind === 'component-port'
|
|
7295
|
+
? buildComponentEndpointId(link.to.ref.widget, link.to.ref.nestedPath)
|
|
7296
|
+
: null,
|
|
7297
|
+
toNestedPath,
|
|
6045
7298
|
toPort: link.to.kind === 'component-port' ? link.to.ref.port : null,
|
|
6046
7299
|
toStatePath,
|
|
6047
7300
|
intent: link.intent,
|
|
@@ -6072,6 +7325,31 @@ function buildConnectionsViewerModel(page) {
|
|
|
6072
7325
|
diagnosticLinks: edges.filter((edge) => edge.hasDiagnostics).length,
|
|
6073
7326
|
};
|
|
6074
7327
|
}
|
|
7328
|
+
function buildComponentEndpointId(ownerWidgetKey, nestedPath) {
|
|
7329
|
+
if (!nestedPath?.length) {
|
|
7330
|
+
return ownerWidgetKey;
|
|
7331
|
+
}
|
|
7332
|
+
return `${ownerWidgetKey}${nestedPath
|
|
7333
|
+
.map((segment) => `/${formatPathSegment(segment)}`)
|
|
7334
|
+
.join('')}`;
|
|
7335
|
+
}
|
|
7336
|
+
function formatPathSegment(segment) {
|
|
7337
|
+
if (segment.kind === 'widget') {
|
|
7338
|
+
const identity = segment.key
|
|
7339
|
+
|| segment.componentType
|
|
7340
|
+
|| (typeof segment.index === 'number' ? String(segment.index) : 'unknown');
|
|
7341
|
+
return `widget:${identity}`;
|
|
7342
|
+
}
|
|
7343
|
+
return [
|
|
7344
|
+
segment.kind,
|
|
7345
|
+
segment.id || segment.key || segment.index,
|
|
7346
|
+
].filter((part) => part !== undefined && part !== '').join(':');
|
|
7347
|
+
}
|
|
7348
|
+
function clone(value) {
|
|
7349
|
+
if (value == null || typeof value !== 'object')
|
|
7350
|
+
return value;
|
|
7351
|
+
return JSON.parse(JSON.stringify(value));
|
|
7352
|
+
}
|
|
6075
7353
|
|
|
6076
7354
|
class ConnectionsViewerPanelComponent {
|
|
6077
7355
|
i18n = inject(PraxisI18nService);
|
|
@@ -6522,6 +7800,8 @@ class DynamicPageBuilderComponent {
|
|
|
6522
7800
|
agenticAuthoringComponentId;
|
|
6523
7801
|
agenticAuthoringScope = 'user';
|
|
6524
7802
|
agenticAuthoringEtag;
|
|
7803
|
+
agenticAuthoringIncludeLlmDiagnostics = false;
|
|
7804
|
+
agenticAuthoringEnableStreaming = false;
|
|
6525
7805
|
pageChange = new EventEmitter();
|
|
6526
7806
|
agenticAuthoringApplied = new EventEmitter();
|
|
6527
7807
|
currentPage = signal({ widgets: [] }, ...(ngDevMode ? [{ debugName: "currentPage" }] : []));
|
|
@@ -6536,6 +7816,7 @@ class DynamicPageBuilderComponent {
|
|
|
6536
7816
|
agenticAuthoringConversation = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringConversation" }] : []));
|
|
6537
7817
|
agenticAuthoringQuickReplies = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringQuickReplies" }] : []));
|
|
6538
7818
|
agenticAuthoringAttachments = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringAttachments" }] : []));
|
|
7819
|
+
agenticAuthoringLlmDiagnostics = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringLlmDiagnostics" }] : []));
|
|
6539
7820
|
agenticAuthoringEditingMessageId = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringEditingMessageId" }] : []));
|
|
6540
7821
|
agenticAuthoringPanelLayout = signal({
|
|
6541
7822
|
left: 16,
|
|
@@ -6568,6 +7849,7 @@ class DynamicPageBuilderComponent {
|
|
|
6568
7849
|
this.previewMode = !this.previewMode;
|
|
6569
7850
|
if (this.previewMode) {
|
|
6570
7851
|
this.connectionsViewerOpen.set(false);
|
|
7852
|
+
this.agenticAuthoringLlmDiagnostics.set(null);
|
|
6571
7853
|
}
|
|
6572
7854
|
}
|
|
6573
7855
|
toggleConnectionsViewer() {
|
|
@@ -6728,11 +8010,10 @@ class DynamicPageBuilderComponent {
|
|
|
6728
8010
|
try {
|
|
6729
8011
|
const controller = this.ensureAgenticTurnController();
|
|
6730
8012
|
const editingMessageId = this.agenticAuthoringEditingMessageId();
|
|
6731
|
-
|
|
8013
|
+
await this.consumeAgenticTurn(editingMessageId
|
|
6732
8014
|
? controller.submitEditedMessage(editingMessageId, prompt)
|
|
6733
8015
|
: controller.submitPrompt(prompt));
|
|
6734
8016
|
this.agenticAuthoringEditingMessageId.set(null);
|
|
6735
|
-
this.applyAgenticTurnState(state);
|
|
6736
8017
|
}
|
|
6737
8018
|
catch (error) {
|
|
6738
8019
|
this.agenticAuthoringStatus.set('');
|
|
@@ -6745,24 +8026,79 @@ class DynamicPageBuilderComponent {
|
|
|
6745
8026
|
}
|
|
6746
8027
|
}
|
|
6747
8028
|
async submitAgenticQuickReply(reply) {
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
}
|
|
6751
|
-
if (reply.kind === 'cancel') {
|
|
8029
|
+
const replyKind = (reply.kind || 'suggestion').trim().toLowerCase();
|
|
8030
|
+
if (replyKind === 'cancel') {
|
|
6752
8031
|
this.agenticAuthoringEditingMessageId.set(null);
|
|
6753
|
-
|
|
6754
|
-
this.applyAgenticTurnState(state);
|
|
8032
|
+
await this.consumeAgenticTurn(this.ensureAgenticTurnController().cancel());
|
|
6755
8033
|
return;
|
|
6756
8034
|
}
|
|
6757
|
-
if (
|
|
8035
|
+
if (replyKind === 'revise') {
|
|
6758
8036
|
this.agenticAuthoringEditingMessageId.set(null);
|
|
6759
8037
|
this.agenticAuthoringQuickReplies.set([]);
|
|
6760
8038
|
this.agenticAuthoringStatus.set(this.tx('agentic.status.waitingRevision', 'Refine your prompt and preview again.'));
|
|
6761
8039
|
return;
|
|
6762
8040
|
}
|
|
6763
8041
|
this.agenticAuthoringEditingMessageId.set(null);
|
|
6764
|
-
|
|
6765
|
-
|
|
8042
|
+
const contextHints = reply.contextHints ? { ...reply.contextHints } : undefined;
|
|
8043
|
+
const visiblePrompt = this.agenticQuickReplyVisiblePrompt(reply, contextHints);
|
|
8044
|
+
this.agenticAuthoringPrompt.set(visiblePrompt);
|
|
8045
|
+
this.agenticAuthoringBusy.set(true);
|
|
8046
|
+
this.agenticAuthoringError.set('');
|
|
8047
|
+
this.agenticAuthoringPreviewResult.set(null);
|
|
8048
|
+
this.agenticAuthoringStatus.set(this.tx('agentic.status.resolvingIntent', 'Resolving intent...'));
|
|
8049
|
+
try {
|
|
8050
|
+
const controller = this.ensureAgenticTurnController();
|
|
8051
|
+
await this.consumeAgenticTurn(controller.snapshot().state === 'clarification'
|
|
8052
|
+
? controller.answerClarification({
|
|
8053
|
+
id: reply.id,
|
|
8054
|
+
label: visiblePrompt,
|
|
8055
|
+
value: reply.prompt,
|
|
8056
|
+
description: reply.description ?? undefined,
|
|
8057
|
+
contextHints,
|
|
8058
|
+
})
|
|
8059
|
+
: controller.submitPrompt(visiblePrompt, {
|
|
8060
|
+
kind: replyKind || 'quick-reply',
|
|
8061
|
+
id: reply.id,
|
|
8062
|
+
value: reply.prompt,
|
|
8063
|
+
contextHints,
|
|
8064
|
+
}));
|
|
8065
|
+
}
|
|
8066
|
+
catch (error) {
|
|
8067
|
+
this.agenticAuthoringStatus.set('');
|
|
8068
|
+
const message = this.describeAgenticError(error);
|
|
8069
|
+
this.agenticAuthoringError.set(message);
|
|
8070
|
+
this.appendAgenticMessage('error', message);
|
|
8071
|
+
}
|
|
8072
|
+
finally {
|
|
8073
|
+
this.agenticAuthoringBusy.set(false);
|
|
8074
|
+
}
|
|
8075
|
+
}
|
|
8076
|
+
agenticQuickReplyVisiblePrompt(reply, contextHints) {
|
|
8077
|
+
if (!this.isResourceQuickReply(reply, contextHints)) {
|
|
8078
|
+
return reply.prompt;
|
|
8079
|
+
}
|
|
8080
|
+
const label = (reply.label || this.describeResourceContext(contextHints) || reply.prompt).trim();
|
|
8081
|
+
const resourcePath = String(contextHints?.['resourcePath'] || '').trim();
|
|
8082
|
+
return this.tx('agentic.quickReplies.resourceSelectionPrompt', 'Use {label} ({resourcePath}) as the data source.')
|
|
8083
|
+
.replace('{label}', label)
|
|
8084
|
+
.replace('{resourcePath}', resourcePath);
|
|
8085
|
+
}
|
|
8086
|
+
isResourceQuickReply(reply, contextHints) {
|
|
8087
|
+
return reply.kind === 'suggestion'
|
|
8088
|
+
&& typeof contextHints?.['resourcePath'] === 'string'
|
|
8089
|
+
&& !!String(contextHints['resourcePath']).trim();
|
|
8090
|
+
}
|
|
8091
|
+
describeResourceContext(contextHints) {
|
|
8092
|
+
const resourcePath = typeof contextHints?.['resourcePath'] === 'string'
|
|
8093
|
+
? contextHints['resourcePath'].trim()
|
|
8094
|
+
: '';
|
|
8095
|
+
if (!resourcePath)
|
|
8096
|
+
return '';
|
|
8097
|
+
const segment = resourcePath.split('/').filter((part) => !!part).pop() || resourcePath;
|
|
8098
|
+
return segment
|
|
8099
|
+
.replace(/[-_]+/g, ' ')
|
|
8100
|
+
.replace(/\s+/g, ' ')
|
|
8101
|
+
.trim();
|
|
6766
8102
|
}
|
|
6767
8103
|
attachAgenticContext() {
|
|
6768
8104
|
const controller = this.ensureAgenticTurnController();
|
|
@@ -6807,8 +8143,7 @@ class DynamicPageBuilderComponent {
|
|
|
6807
8143
|
this.agenticAuthoringError.set('');
|
|
6808
8144
|
this.agenticAuthoringPreviewResult.set(null);
|
|
6809
8145
|
try {
|
|
6810
|
-
|
|
6811
|
-
this.applyAgenticTurnState(state);
|
|
8146
|
+
await this.consumeAgenticTurn(this.ensureAgenticTurnController().resendMessage(message.id));
|
|
6812
8147
|
}
|
|
6813
8148
|
catch (error) {
|
|
6814
8149
|
this.agenticAuthoringStatus.set('');
|
|
@@ -6877,6 +8212,8 @@ class DynamicPageBuilderComponent {
|
|
|
6877
8212
|
provider: () => this.agenticAuthoringProvider,
|
|
6878
8213
|
model: () => this.agenticAuthoringModel,
|
|
6879
8214
|
apiKey: () => this.agenticAuthoringApiKey,
|
|
8215
|
+
enableTurnStream: () => this.agenticAuthoringEnableStreaming,
|
|
8216
|
+
includeLlmDiagnostics: () => this.agenticAuthoringIncludeLlmDiagnostics,
|
|
6880
8217
|
loadComponentCapabilities: () => this.loadAgenticComponentCapabilities(),
|
|
6881
8218
|
applyLocalPreview: (result) => this.applyAgenticPreviewLocally(result),
|
|
6882
8219
|
describePreviewFailure: (result) => this.describeAgenticPreviewFailure(result),
|
|
@@ -6898,20 +8235,55 @@ class DynamicPageBuilderComponent {
|
|
|
6898
8235
|
}
|
|
6899
8236
|
async applyAgenticPreviewLocally(result) {
|
|
6900
8237
|
const adapter = new PageBuilderAiAdapter(this.createAdapterHost(), this.componentMetadata);
|
|
6901
|
-
|
|
8238
|
+
const applied = await (result.uiCompositionPlan
|
|
6902
8239
|
? adapter.applyUiCompositionPlan(result.uiCompositionPlan)
|
|
6903
|
-
: adapter.applyCompiledFormPatch(result.compiledFormPatch);
|
|
8240
|
+
: adapter.applyCompiledFormPatch(result.compiledFormPatch));
|
|
8241
|
+
if (!applied.success && this.isAgenticTableContractError(applied.error)) {
|
|
8242
|
+
return {
|
|
8243
|
+
...applied,
|
|
8244
|
+
error: this.tx('agentic.errors.invalidTableContract', 'I found the data source, but the generated plan used properties that are incompatible with the table component. I will adjust it to use only supported fields.'),
|
|
8245
|
+
};
|
|
8246
|
+
}
|
|
8247
|
+
return applied;
|
|
6904
8248
|
}
|
|
6905
8249
|
applyAgenticTurnState(state) {
|
|
8250
|
+
const preview = state.preview ?? null;
|
|
6906
8251
|
this.agenticAuthoringConversation.set(state.messages);
|
|
6907
|
-
this.agenticAuthoringQuickReplies.set(state.quickReplies);
|
|
8252
|
+
this.agenticAuthoringQuickReplies.set(preview?.valid ? [] : state.quickReplies);
|
|
6908
8253
|
this.agenticAuthoringStatus.set(state.statusText);
|
|
6909
8254
|
this.agenticAuthoringError.set(state.errorText);
|
|
6910
|
-
this.agenticAuthoringPreviewResult.set(
|
|
8255
|
+
this.agenticAuthoringPreviewResult.set(preview);
|
|
6911
8256
|
this.agenticAuthoringAttachments.set(state.attachments);
|
|
8257
|
+
this.agenticAuthoringLlmDiagnostics.set(this.resolveAgenticLlmDiagnostics(state.diagnostics));
|
|
8258
|
+
}
|
|
8259
|
+
consumeAgenticTurn(states$) {
|
|
8260
|
+
return lastValueFrom(states$.pipe(tap((state) => this.applyAgenticTurnState(state))));
|
|
8261
|
+
}
|
|
8262
|
+
agenticAuthoringDiagnosticsTop() {
|
|
8263
|
+
return Math.max(16, this.agenticAuthoringPanelLayout().top);
|
|
8264
|
+
}
|
|
8265
|
+
agenticAuthoringLlmDiagnosticsText() {
|
|
8266
|
+
const diagnostics = this.agenticAuthoringLlmDiagnostics();
|
|
8267
|
+
if (!this.agenticAuthoringIncludeLlmDiagnostics || !diagnostics) {
|
|
8268
|
+
return '';
|
|
8269
|
+
}
|
|
8270
|
+
return JSON.stringify(diagnostics, null, 2);
|
|
8271
|
+
}
|
|
8272
|
+
resolveAgenticLlmDiagnostics(diagnostics) {
|
|
8273
|
+
if (!this.agenticAuthoringIncludeLlmDiagnostics) {
|
|
8274
|
+
return null;
|
|
8275
|
+
}
|
|
8276
|
+
const intentResolution = this.toRecord(diagnostics?.['intentResolution']);
|
|
8277
|
+
return this.toRecord(intentResolution?.['llmDiagnostics']);
|
|
8278
|
+
}
|
|
8279
|
+
toRecord(value) {
|
|
8280
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
8281
|
+
return null;
|
|
8282
|
+
}
|
|
8283
|
+
return value;
|
|
6912
8284
|
}
|
|
6913
8285
|
resolvePreviewCompiledFormPatch(preview) {
|
|
6914
|
-
if (preview.uiCompositionPlan) {
|
|
8286
|
+
if (preview.uiCompositionPlan || preview.compiledFormPatch?.patch?.page) {
|
|
6915
8287
|
return {
|
|
6916
8288
|
...preview.compiledFormPatch,
|
|
6917
8289
|
patch: {
|
|
@@ -6920,9 +8292,6 @@ class DynamicPageBuilderComponent {
|
|
|
6920
8292
|
},
|
|
6921
8293
|
};
|
|
6922
8294
|
}
|
|
6923
|
-
if (preview.compiledFormPatch?.patch?.page) {
|
|
6924
|
-
return preview.compiledFormPatch;
|
|
6925
|
-
}
|
|
6926
8295
|
return preview.compiledFormPatch;
|
|
6927
8296
|
}
|
|
6928
8297
|
focusCanvasWidget(widgetKey) {
|
|
@@ -6994,6 +8363,12 @@ class DynamicPageBuilderComponent {
|
|
|
6994
8363
|
}
|
|
6995
8364
|
}
|
|
6996
8365
|
describeAgenticPreviewStatus(result) {
|
|
8366
|
+
const assistantMessage = typeof result.assistantMessage === 'string'
|
|
8367
|
+
? result.assistantMessage.trim()
|
|
8368
|
+
: '';
|
|
8369
|
+
if (assistantMessage) {
|
|
8370
|
+
return assistantMessage;
|
|
8371
|
+
}
|
|
6997
8372
|
switch (result.diagnostics?.fieldScopeDecision) {
|
|
6998
8373
|
case 'accepted-add-local-field':
|
|
6999
8374
|
return this.tx('agentic.status.acceptedAddLocalField', 'Local field added to the form.');
|
|
@@ -7005,6 +8380,11 @@ class DynamicPageBuilderComponent {
|
|
|
7005
8380
|
return this.tx('agentic.status.previewReady', 'Preview applied to the page.');
|
|
7006
8381
|
}
|
|
7007
8382
|
}
|
|
8383
|
+
isAgenticTableContractError(error) {
|
|
8384
|
+
const message = typeof error === 'string' ? error.toLowerCase() : '';
|
|
8385
|
+
return message.includes('praxis-table')
|
|
8386
|
+
&& (message.includes('unknown input') || message.includes('unknown inputs') || message.includes('unsupported'));
|
|
8387
|
+
}
|
|
7008
8388
|
appendAgenticMessage(role, text) {
|
|
7009
8389
|
const normalized = text.trim();
|
|
7010
8390
|
if (!normalized)
|
|
@@ -7043,7 +8423,7 @@ class DynamicPageBuilderComponent {
|
|
|
7043
8423
|
return resolvePraxisPageBuilderText(this.i18n, key, fallback);
|
|
7044
8424
|
}
|
|
7045
8425
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicPageBuilderComponent, deps: [{ token: i1.MatDialog }, { token: SETTINGS_PANEL_BRIDGE, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
7046
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: DynamicPageBuilderComponent, isStandalone: true, selector: "praxis-dynamic-page-builder", inputs: { page: "page", context: "context", strictValidation: "strictValidation", autoPersist: "autoPersist", enableCustomization: "enableCustomization", showSettingsButton: "showSettingsButton", pageIdentity: "pageIdentity", componentInstanceId: "componentInstanceId", pageEditorComponent: "pageEditorComponent", enableAgenticAuthoring: "enableAgenticAuthoring", agenticAuthoringProvider: "agenticAuthoringProvider", agenticAuthoringModel: "agenticAuthoringModel", agenticAuthoringApiKey: "agenticAuthoringApiKey", agenticAuthoringComponentId: "agenticAuthoringComponentId", agenticAuthoringScope: "agenticAuthoringScope", agenticAuthoringEtag: "agenticAuthoringEtag" }, outputs: { pageChange: "pageChange", agenticAuthoringApplied: "agenticAuthoringApplied" }, providers: [
|
|
8426
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: DynamicPageBuilderComponent, isStandalone: true, selector: "praxis-dynamic-page-builder", inputs: { page: "page", context: "context", strictValidation: "strictValidation", autoPersist: "autoPersist", enableCustomization: "enableCustomization", showSettingsButton: "showSettingsButton", pageIdentity: "pageIdentity", componentInstanceId: "componentInstanceId", pageEditorComponent: "pageEditorComponent", enableAgenticAuthoring: "enableAgenticAuthoring", agenticAuthoringProvider: "agenticAuthoringProvider", agenticAuthoringModel: "agenticAuthoringModel", agenticAuthoringApiKey: "agenticAuthoringApiKey", agenticAuthoringComponentId: "agenticAuthoringComponentId", agenticAuthoringScope: "agenticAuthoringScope", agenticAuthoringEtag: "agenticAuthoringEtag", agenticAuthoringIncludeLlmDiagnostics: "agenticAuthoringIncludeLlmDiagnostics", agenticAuthoringEnableStreaming: "agenticAuthoringEnableStreaming" }, outputs: { pageChange: "pageChange", agenticAuthoringApplied: "agenticAuthoringApplied" }, providers: [
|
|
7047
8427
|
providePraxisPageBuilderI18n(),
|
|
7048
8428
|
{ provide: DYNAMIC_PAGE_SHELL_EDITOR, useValue: WidgetShellEditorComponent },
|
|
7049
8429
|
], viewQueries: [{ propertyName: "runtime", first: true, predicate: ["runtime"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
@@ -7107,6 +8487,26 @@ class DynamicPageBuilderComponent {
|
|
|
7107
8487
|
(close)="toggleAgenticAuthoring()"
|
|
7108
8488
|
/>
|
|
7109
8489
|
|
|
8490
|
+
<section
|
|
8491
|
+
*ngIf="agenticAuthoringIncludeLlmDiagnostics && agenticAuthoringLlmDiagnosticsText()"
|
|
8492
|
+
class="agentic-diagnostics-panel"
|
|
8493
|
+
data-testid="page-builder-agentic-llm-diagnostics"
|
|
8494
|
+
role="region"
|
|
8495
|
+
[attr.aria-label]="tx('agentic.diagnostics.title', 'LLM diagnostics')"
|
|
8496
|
+
[style.top.px]="agenticAuthoringDiagnosticsTop()"
|
|
8497
|
+
>
|
|
8498
|
+
<header class="agentic-diagnostics-panel__header">
|
|
8499
|
+
<span>{{ tx('agentic.diagnostics.title', 'LLM diagnostics') }}</span>
|
|
8500
|
+
<span class="agentic-diagnostics-panel__badge">
|
|
8501
|
+
{{ tx('agentic.diagnostics.badge', 'Debug') }}
|
|
8502
|
+
</span>
|
|
8503
|
+
</header>
|
|
8504
|
+
<p class="agentic-diagnostics-panel__description">
|
|
8505
|
+
{{ tx('agentic.diagnostics.description', 'Prompt, context bundle, and tool catalog returned by the backend for this turn.') }}
|
|
8506
|
+
</p>
|
|
8507
|
+
<pre>{{ agenticAuthoringLlmDiagnosticsText() }}</pre>
|
|
8508
|
+
</section>
|
|
8509
|
+
|
|
7110
8510
|
<praxis-floating-toolbar
|
|
7111
8511
|
[visible]="showSettings()"
|
|
7112
8512
|
[canUndo]="false"
|
|
@@ -7141,7 +8541,7 @@ class DynamicPageBuilderComponent {
|
|
|
7141
8541
|
</button>
|
|
7142
8542
|
</praxis-floating-toolbar>
|
|
7143
8543
|
</div>
|
|
7144
|
-
`, isInline: true, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: DynamicWidgetPageComponent, selector: "praxis-dynamic-page", inputs: ["page", "context", "strictValidation", "enableCustomization", "showPageSettingsButton", "shellEditorComponent", "pageEditorComponent", "autoPersist", "pageIdentity", "componentInstanceId"], outputs: ["pageChange", "widgetEvent", "widgetDiagnosticsChange"] }, { kind: "component", type: FloatingToolbarComponent, selector: "praxis-floating-toolbar", inputs: ["visible", "canUndo", "canRedo"], outputs: ["add", "undo", "redo", "settings", "preview", "save"] }, { kind: "component", type: ConnectionsViewerPanelComponent, selector: "praxis-connections-viewer-panel", inputs: ["open", "page"], outputs: ["focusWidget", "openPageSettings"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "busy", "canSubmit", "canApply", "submitOnEnter", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "layoutChange"] }] });
|
|
8544
|
+
`, isInline: true, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%}.agentic-diagnostics-panel{position:absolute;z-index:21;right:16px;width:min(520px,calc(100% - 32px));max-height:min(440px,calc(100% - 32px));overflow:auto;padding:12px;border:1px solid rgba(107,114,128,.28);border-radius:8px;background:#fffffff5;color:#111827;box-shadow:0 14px 36px #0000003d}.agentic-diagnostics-panel__header{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px;font-weight:700}.agentic-diagnostics-panel__badge{padding:2px 6px;border:1px solid rgba(107,114,128,.28);border-radius:8px;font-size:11px;font-weight:600;color:#4b5563}.agentic-diagnostics-panel__description{margin:8px 0 10px;color:#4b5563;font-size:12px;line-height:1.4}.agentic-diagnostics-panel pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px;line-height:1.45;color:#1f2937}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: DynamicWidgetPageComponent, selector: "praxis-dynamic-page", inputs: ["page", "context", "strictValidation", "enableCustomization", "showPageSettingsButton", "shellEditorComponent", "pageEditorComponent", "autoPersist", "pageIdentity", "componentInstanceId"], outputs: ["pageChange", "widgetEvent", "widgetDiagnosticsChange"] }, { kind: "component", type: FloatingToolbarComponent, selector: "praxis-floating-toolbar", inputs: ["visible", "canUndo", "canRedo"], outputs: ["add", "undo", "redo", "settings", "preview", "save"] }, { kind: "component", type: ConnectionsViewerPanelComponent, selector: "praxis-connections-viewer-panel", inputs: ["open", "page"], outputs: ["focusWidget", "openPageSettings"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "busy", "canSubmit", "canApply", "submitOnEnter", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "layoutChange"] }] });
|
|
7145
8545
|
}
|
|
7146
8546
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicPageBuilderComponent, decorators: [{
|
|
7147
8547
|
type: Component,
|
|
@@ -7219,6 +8619,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7219
8619
|
(close)="toggleAgenticAuthoring()"
|
|
7220
8620
|
/>
|
|
7221
8621
|
|
|
8622
|
+
<section
|
|
8623
|
+
*ngIf="agenticAuthoringIncludeLlmDiagnostics && agenticAuthoringLlmDiagnosticsText()"
|
|
8624
|
+
class="agentic-diagnostics-panel"
|
|
8625
|
+
data-testid="page-builder-agentic-llm-diagnostics"
|
|
8626
|
+
role="region"
|
|
8627
|
+
[attr.aria-label]="tx('agentic.diagnostics.title', 'LLM diagnostics')"
|
|
8628
|
+
[style.top.px]="agenticAuthoringDiagnosticsTop()"
|
|
8629
|
+
>
|
|
8630
|
+
<header class="agentic-diagnostics-panel__header">
|
|
8631
|
+
<span>{{ tx('agentic.diagnostics.title', 'LLM diagnostics') }}</span>
|
|
8632
|
+
<span class="agentic-diagnostics-panel__badge">
|
|
8633
|
+
{{ tx('agentic.diagnostics.badge', 'Debug') }}
|
|
8634
|
+
</span>
|
|
8635
|
+
</header>
|
|
8636
|
+
<p class="agentic-diagnostics-panel__description">
|
|
8637
|
+
{{ tx('agentic.diagnostics.description', 'Prompt, context bundle, and tool catalog returned by the backend for this turn.') }}
|
|
8638
|
+
</p>
|
|
8639
|
+
<pre>{{ agenticAuthoringLlmDiagnosticsText() }}</pre>
|
|
8640
|
+
</section>
|
|
8641
|
+
|
|
7222
8642
|
<praxis-floating-toolbar
|
|
7223
8643
|
[visible]="showSettings()"
|
|
7224
8644
|
[canUndo]="false"
|
|
@@ -7253,7 +8673,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7253
8673
|
</button>
|
|
7254
8674
|
</praxis-floating-toolbar>
|
|
7255
8675
|
</div>
|
|
7256
|
-
`, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%}\n"] }]
|
|
8676
|
+
`, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%}.agentic-diagnostics-panel{position:absolute;z-index:21;right:16px;width:min(520px,calc(100% - 32px));max-height:min(440px,calc(100% - 32px));overflow:auto;padding:12px;border:1px solid rgba(107,114,128,.28);border-radius:8px;background:#fffffff5;color:#111827;box-shadow:0 14px 36px #0000003d}.agentic-diagnostics-panel__header{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px;font-weight:700}.agentic-diagnostics-panel__badge{padding:2px 6px;border:1px solid rgba(107,114,128,.28);border-radius:8px;font-size:11px;font-weight:600;color:#4b5563}.agentic-diagnostics-panel__description{margin:8px 0 10px;color:#4b5563;font-size:12px;line-height:1.4}.agentic-diagnostics-panel pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px;line-height:1.45;color:#1f2937}\n"] }]
|
|
7257
8677
|
}], ctorParameters: () => [{ type: i1.MatDialog }, { type: undefined, decorators: [{
|
|
7258
8678
|
type: Optional
|
|
7259
8679
|
}, {
|
|
@@ -7294,6 +8714,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7294
8714
|
type: Input
|
|
7295
8715
|
}], agenticAuthoringEtag: [{
|
|
7296
8716
|
type: Input
|
|
8717
|
+
}], agenticAuthoringIncludeLlmDiagnostics: [{
|
|
8718
|
+
type: Input
|
|
8719
|
+
}], agenticAuthoringEnableStreaming: [{
|
|
8720
|
+
type: Input
|
|
7297
8721
|
}], pageChange: [{
|
|
7298
8722
|
type: Output
|
|
7299
8723
|
}], agenticAuthoringApplied: [{
|
|
@@ -7309,4 +8733,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7309
8733
|
* Generated bundle index. Do not edit.
|
|
7310
8734
|
*/
|
|
7311
8735
|
|
|
7312
|
-
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 };
|
|
8736
|
+
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 };
|