@praxisui/page-builder 8.0.0-beta.1 → 8.0.0-beta.11
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 +16 -3
- package/fesm2022/praxisui-page-builder.mjs +972 -65
- package/index.d.ts +81 -9
- 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,8 @@ 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
|
+
'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
4815
|
'Payloads legados de layout ainda podem ser ingeridos, mas sao normalizados para canvas e nao devem ser reemitidos.',
|
|
4782
4816
|
'pageIdentity identifica o escopo de persistencia e nao pertence ao objeto page.',
|
|
4783
4817
|
],
|
|
@@ -4796,6 +4830,18 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
4796
4830
|
{ path: 'uiCompositionPlan.bindings[].id', category: 'connections', valueKind: 'string', description: 'Identificador estavel do binding planejado.' },
|
|
4797
4831
|
{ path: 'uiCompositionPlan.bindings[].from', category: 'connections', valueKind: 'object', description: 'Endpoint de origem: component-port ou state.' },
|
|
4798
4832
|
{ path: 'uiCompositionPlan.bindings[].to', category: 'connections', valueKind: 'object', description: 'Endpoint de destino: component-port ou state.' },
|
|
4833
|
+
{ 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.' },
|
|
4834
|
+
{ 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.' },
|
|
4835
|
+
{ 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.' },
|
|
4836
|
+
{ path: 'uiCompositionPlan.bindings[].from.nestedPath[].id', category: 'connections', valueKind: 'string', description: 'Identificador estrutural estavel do container de origem no segmento nested, quando existir.' },
|
|
4837
|
+
{ 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 },
|
|
4838
|
+
{ 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.' },
|
|
4839
|
+
{ 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.' },
|
|
4840
|
+
{ 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.' },
|
|
4841
|
+
{ path: 'uiCompositionPlan.bindings[].to.nestedPath[].id', category: 'connections', valueKind: 'string', description: 'Identificador estrutural estavel do container de destino no segmento nested, quando existir.' },
|
|
4842
|
+
{ 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 },
|
|
4843
|
+
{ 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.' },
|
|
4844
|
+
{ 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
4845
|
{ path: 'uiCompositionPlan.bindings[].intent', category: 'connections', valueKind: 'string', description: 'Intencao semantica do binding.' },
|
|
4800
4846
|
{ path: 'uiCompositionPlan.bindings[].condition', category: 'connections', valueKind: 'expression', description: 'Guarda opcional em Json Logic.' },
|
|
4801
4847
|
{ path: 'uiCompositionPlan.bindings[].transform', category: 'connections', valueKind: 'object', description: 'Transform declarativo: pick-path, query-context, template ou constant.' },
|
|
@@ -4847,6 +4893,10 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
4847
4893
|
{ path: 'page.composition.links[].id', category: 'connections', valueKind: 'string', description: 'Identificador estavel do link.' },
|
|
4848
4894
|
{ path: 'page.composition.links[].from', category: 'connections', valueKind: 'object', description: 'Endpoint de origem do link.' },
|
|
4849
4895
|
{ path: 'page.composition.links[].to', category: 'connections', valueKind: 'object', description: 'Endpoint de destino do link.' },
|
|
4896
|
+
{ 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: [].' },
|
|
4897
|
+
{ 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 },
|
|
4898
|
+
{ 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: [].' },
|
|
4899
|
+
{ 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
4900
|
{ path: 'page.composition.links[].intent', category: 'connections', valueKind: 'string', description: 'Intencao semantica do link.' },
|
|
4851
4901
|
{ path: 'page.composition.links[].transform', category: 'connections', valueKind: 'object', description: 'Pipeline de transformacao do link.' },
|
|
4852
4902
|
{ 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 +5076,12 @@ class PageBuilderAgenticAuthoringService {
|
|
|
5026
5076
|
resolveIntent(request) {
|
|
5027
5077
|
return this.http.post(`${this.baseUrl}/intent-resolution`, request, { headers: this.buildHeaders() });
|
|
5028
5078
|
}
|
|
5079
|
+
searchResourceCandidates(request) {
|
|
5080
|
+
return this.http.post(`${this.baseUrl}/resource-candidates`, request, { headers: this.buildHeaders() });
|
|
5081
|
+
}
|
|
5082
|
+
streamTurn(request) {
|
|
5083
|
+
return this.http.post(`${this.baseUrl}/turn/stream/start`, request, { headers: this.buildHeaders() }).pipe(switchMap((start) => this.connectTurnStream(start)));
|
|
5084
|
+
}
|
|
5029
5085
|
applyPage(request) {
|
|
5030
5086
|
const { ifMatch, ...body } = request;
|
|
5031
5087
|
return this.http.post(`${this.baseUrl}/page-apply`, {
|
|
@@ -5055,6 +5111,97 @@ class PageBuilderAgenticAuthoringService {
|
|
|
5055
5111
|
}
|
|
5056
5112
|
return new HttpHeaders(merged);
|
|
5057
5113
|
}
|
|
5114
|
+
connectTurnStream(start) {
|
|
5115
|
+
return new Observable((observer) => {
|
|
5116
|
+
const url = this.buildStreamUrl(start);
|
|
5117
|
+
let source;
|
|
5118
|
+
let closed = false;
|
|
5119
|
+
let connectionErrorTimer = null;
|
|
5120
|
+
const connectionErrorGraceMs = Math.max(0, this.options?.streamConnectionErrorGraceMs ?? 1500);
|
|
5121
|
+
try {
|
|
5122
|
+
source = this.createEventSource(url);
|
|
5123
|
+
}
|
|
5124
|
+
catch (error) {
|
|
5125
|
+
observer.error(this.streamConnectionError(error));
|
|
5126
|
+
return undefined;
|
|
5127
|
+
}
|
|
5128
|
+
const clearConnectionErrorTimer = () => {
|
|
5129
|
+
if (connectionErrorTimer) {
|
|
5130
|
+
clearTimeout(connectionErrorTimer);
|
|
5131
|
+
connectionErrorTimer = null;
|
|
5132
|
+
}
|
|
5133
|
+
};
|
|
5134
|
+
const closeSource = () => {
|
|
5135
|
+
if (closed) {
|
|
5136
|
+
return;
|
|
5137
|
+
}
|
|
5138
|
+
closed = true;
|
|
5139
|
+
clearConnectionErrorTimer();
|
|
5140
|
+
source.close();
|
|
5141
|
+
};
|
|
5142
|
+
const failConnection = (event) => {
|
|
5143
|
+
if (closed) {
|
|
5144
|
+
return;
|
|
5145
|
+
}
|
|
5146
|
+
observer.error(this.streamConnectionError(event));
|
|
5147
|
+
closeSource();
|
|
5148
|
+
};
|
|
5149
|
+
const handleMessage = (event) => {
|
|
5150
|
+
try {
|
|
5151
|
+
clearConnectionErrorTimer();
|
|
5152
|
+
const parsed = JSON.parse(event.data);
|
|
5153
|
+
observer.next(parsed);
|
|
5154
|
+
if (parsed.type === 'result' || parsed.type === 'error' || parsed.type === 'cancelled') {
|
|
5155
|
+
observer.complete();
|
|
5156
|
+
closeSource();
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
catch (error) {
|
|
5160
|
+
observer.error(this.streamConnectionError(error));
|
|
5161
|
+
closeSource();
|
|
5162
|
+
}
|
|
5163
|
+
};
|
|
5164
|
+
source.onmessage = handleMessage;
|
|
5165
|
+
if (source.addEventListener) {
|
|
5166
|
+
for (const type of AI_STREAM_EVENT_TYPES) {
|
|
5167
|
+
source.addEventListener(type, handleMessage);
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
source.onerror = (event) => {
|
|
5171
|
+
if (closed || connectionErrorTimer) {
|
|
5172
|
+
return;
|
|
5173
|
+
}
|
|
5174
|
+
if (connectionErrorGraceMs === 0) {
|
|
5175
|
+
failConnection(event);
|
|
5176
|
+
return;
|
|
5177
|
+
}
|
|
5178
|
+
connectionErrorTimer = setTimeout(() => failConnection(event), connectionErrorGraceMs);
|
|
5179
|
+
};
|
|
5180
|
+
return () => closeSource();
|
|
5181
|
+
});
|
|
5182
|
+
}
|
|
5183
|
+
streamConnectionError(cause) {
|
|
5184
|
+
return {
|
|
5185
|
+
praxisAgenticTurnStreamConnectionError: true,
|
|
5186
|
+
cause,
|
|
5187
|
+
};
|
|
5188
|
+
}
|
|
5189
|
+
buildStreamUrl(start) {
|
|
5190
|
+
const url = new URL(`${this.baseUrl}/turn/stream/${start.streamId}`, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
|
|
5191
|
+
if (start.streamAccessToken) {
|
|
5192
|
+
url.searchParams.set('accessToken', start.streamAccessToken);
|
|
5193
|
+
}
|
|
5194
|
+
return /^https?:\/\//i.test(this.baseUrl)
|
|
5195
|
+
? url.toString()
|
|
5196
|
+
: url.pathname + url.search;
|
|
5197
|
+
}
|
|
5198
|
+
createEventSource(url) {
|
|
5199
|
+
const factory = this.options?.eventSourceFactory;
|
|
5200
|
+
if (factory) {
|
|
5201
|
+
return factory(url);
|
|
5202
|
+
}
|
|
5203
|
+
return new EventSource(url);
|
|
5204
|
+
}
|
|
5058
5205
|
formatEtag(etag) {
|
|
5059
5206
|
const trimmed = etag.trim();
|
|
5060
5207
|
return trimmed.startsWith('"') ? trimmed : `"${trimmed}"`;
|
|
@@ -5080,8 +5227,8 @@ function compileUiCompositionPlan(plan) {
|
|
|
5080
5227
|
valid: true,
|
|
5081
5228
|
page: {
|
|
5082
5229
|
layoutPreset: plan.layoutPreset,
|
|
5083
|
-
canvas: clone(plan.canvas),
|
|
5084
|
-
state: clone(plan.state),
|
|
5230
|
+
canvas: clone$1(plan.canvas),
|
|
5231
|
+
state: clone$1(plan.state),
|
|
5085
5232
|
widgets: plan.widgets.map(toWidgetInstance),
|
|
5086
5233
|
composition: {
|
|
5087
5234
|
version: '1.0.0',
|
|
@@ -5176,6 +5323,7 @@ function validateEndpoint(endpoint, widgetKeys, diagnostics, path) {
|
|
|
5176
5323
|
path: `${path}.port`,
|
|
5177
5324
|
});
|
|
5178
5325
|
}
|
|
5326
|
+
validateNestedPath(endpoint.nestedPath, diagnostics, `${path}.nestedPath`);
|
|
5179
5327
|
return;
|
|
5180
5328
|
}
|
|
5181
5329
|
if (!endpoint.path?.trim()) {
|
|
@@ -5186,14 +5334,26 @@ function validateEndpoint(endpoint, widgetKeys, diagnostics, path) {
|
|
|
5186
5334
|
});
|
|
5187
5335
|
}
|
|
5188
5336
|
}
|
|
5337
|
+
function validateNestedPath(nestedPath, diagnostics, path) {
|
|
5338
|
+
if (!nestedPath?.length)
|
|
5339
|
+
return;
|
|
5340
|
+
const terminal = nestedPath[nestedPath.length - 1];
|
|
5341
|
+
if (terminal?.kind !== 'widget' || !terminal.key?.trim()) {
|
|
5342
|
+
diagnostics.push({
|
|
5343
|
+
code: 'endpoint-nested-terminal-widget-key-required',
|
|
5344
|
+
message: 'Nested component endpoint must end with a widget segment with a stable key.',
|
|
5345
|
+
path,
|
|
5346
|
+
});
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5189
5349
|
function toWidgetInstance(widget) {
|
|
5190
5350
|
return {
|
|
5191
5351
|
key: widget.key,
|
|
5192
5352
|
definition: {
|
|
5193
5353
|
id: widget.componentId,
|
|
5194
5354
|
bindingOrder: widget.bindingOrder,
|
|
5195
|
-
inputs: clone(widget.inputs ?? {}),
|
|
5196
|
-
outputs: clone(widget.outputs ?? {}),
|
|
5355
|
+
inputs: clone$1(widget.inputs ?? {}),
|
|
5356
|
+
outputs: clone$1(widget.outputs ?? {}),
|
|
5197
5357
|
},
|
|
5198
5358
|
};
|
|
5199
5359
|
}
|
|
@@ -5204,10 +5364,10 @@ function toCompositionLink(binding) {
|
|
|
5204
5364
|
from: toCompositionEndpoint(binding.from),
|
|
5205
5365
|
to: toCompositionEndpoint(binding.to),
|
|
5206
5366
|
intent: binding.intent,
|
|
5207
|
-
condition: clone(binding.condition),
|
|
5367
|
+
condition: clone$1(binding.condition),
|
|
5208
5368
|
transform,
|
|
5209
|
-
policy: clone(binding.policy),
|
|
5210
|
-
metadata: clone(binding.metadata),
|
|
5369
|
+
policy: clone$1(binding.policy),
|
|
5370
|
+
metadata: clone$1(binding.metadata),
|
|
5211
5371
|
};
|
|
5212
5372
|
}
|
|
5213
5373
|
function toCompositionEndpoint(endpoint) {
|
|
@@ -5218,6 +5378,7 @@ function toCompositionEndpoint(endpoint) {
|
|
|
5218
5378
|
widget: endpoint.widget,
|
|
5219
5379
|
port: endpoint.port,
|
|
5220
5380
|
direction: endpoint.direction,
|
|
5381
|
+
...(endpoint.nestedPath?.length ? { nestedPath: clone$1(endpoint.nestedPath) } : {}),
|
|
5221
5382
|
},
|
|
5222
5383
|
};
|
|
5223
5384
|
}
|
|
@@ -5265,7 +5426,7 @@ function toCompositionTransform(transform) {
|
|
|
5265
5426
|
phase: 'link-propagation',
|
|
5266
5427
|
input: { source: transform.inputSource ?? 'event' },
|
|
5267
5428
|
config: {
|
|
5268
|
-
template: clone(transform.template),
|
|
5429
|
+
template: clone$1(transform.template),
|
|
5269
5430
|
},
|
|
5270
5431
|
},
|
|
5271
5432
|
],
|
|
@@ -5282,7 +5443,7 @@ function toCompositionTransform(transform) {
|
|
|
5282
5443
|
kind: 'constant',
|
|
5283
5444
|
phase: 'link-propagation',
|
|
5284
5445
|
config: {
|
|
5285
|
-
value: clone(transform.value),
|
|
5446
|
+
value: clone$1(transform.value),
|
|
5286
5447
|
},
|
|
5287
5448
|
},
|
|
5288
5449
|
],
|
|
@@ -5305,7 +5466,7 @@ function toCompositionTransform(transform) {
|
|
|
5305
5466
|
],
|
|
5306
5467
|
};
|
|
5307
5468
|
}
|
|
5308
|
-
function clone(value) {
|
|
5469
|
+
function clone$1(value) {
|
|
5309
5470
|
if (value == null || typeof value !== 'object')
|
|
5310
5471
|
return value;
|
|
5311
5472
|
try {
|
|
@@ -5387,7 +5548,11 @@ class PageBuilderAiAdapter {
|
|
|
5387
5548
|
error: 'CompiledFormPatch must contain patch.page.',
|
|
5388
5549
|
};
|
|
5389
5550
|
}
|
|
5390
|
-
|
|
5551
|
+
const materializedPage = this.clone(page);
|
|
5552
|
+
if (this.shouldPreserveLocalTransientFields(compiledFormPatch)) {
|
|
5553
|
+
this.preserveLocalTransientFields(materializedPage);
|
|
5554
|
+
}
|
|
5555
|
+
return this.applyPatch({ page: materializedPage }, 'agentic-authoring.compiled-form-patch');
|
|
5391
5556
|
}
|
|
5392
5557
|
async applyUiCompositionPlan(plan) {
|
|
5393
5558
|
const compiled = compileUiCompositionPlan(plan);
|
|
@@ -5526,6 +5691,76 @@ class PageBuilderAiAdapter {
|
|
|
5526
5691
|
}
|
|
5527
5692
|
return merged;
|
|
5528
5693
|
}
|
|
5694
|
+
shouldPreserveLocalTransientFields(compiledFormPatch) {
|
|
5695
|
+
const warnings = compiledFormPatch['warnings'];
|
|
5696
|
+
return Array.isArray(warnings) && warnings.includes('server-backed-field-labels-customized-locally');
|
|
5697
|
+
}
|
|
5698
|
+
preserveLocalTransientFields(patchPage) {
|
|
5699
|
+
const basePage = this.normalizePageForAi(this.parsePage(this.host.page));
|
|
5700
|
+
const baseWidgets = basePage?.widgets ?? [];
|
|
5701
|
+
for (const patchWidget of patchPage.widgets ?? []) {
|
|
5702
|
+
if (patchWidget.definition?.id !== 'praxis-dynamic-form')
|
|
5703
|
+
continue;
|
|
5704
|
+
const key = this.getWidgetKey(patchWidget);
|
|
5705
|
+
if (!key)
|
|
5706
|
+
continue;
|
|
5707
|
+
const baseWidget = baseWidgets.find((widget) => this.getWidgetKey(widget) === key);
|
|
5708
|
+
if (!baseWidget)
|
|
5709
|
+
continue;
|
|
5710
|
+
this.preserveLocalTransientFieldsForWidget(baseWidget, patchWidget);
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
preserveLocalTransientFieldsForWidget(baseWidget, patchWidget) {
|
|
5714
|
+
const baseConfig = (baseWidget.definition?.inputs?.['config'] ?? {});
|
|
5715
|
+
const patchInputs = patchWidget.definition.inputs ??= {};
|
|
5716
|
+
const patchConfig = (patchInputs['config'] ??= {});
|
|
5717
|
+
const baseFields = Array.isArray(baseConfig['fieldMetadata']) ? baseConfig['fieldMetadata'] : [];
|
|
5718
|
+
const patchFields = Array.isArray(patchConfig['fieldMetadata'])
|
|
5719
|
+
? patchConfig['fieldMetadata']
|
|
5720
|
+
: (patchConfig['fieldMetadata'] = []);
|
|
5721
|
+
const preservedNames = [];
|
|
5722
|
+
for (const field of baseFields) {
|
|
5723
|
+
if (!this.isLocalTransientField(field) || patchFields.some((entry) => entry?.name === field.name))
|
|
5724
|
+
continue;
|
|
5725
|
+
patchFields.push(this.clone(field));
|
|
5726
|
+
preservedNames.push(field.name);
|
|
5727
|
+
}
|
|
5728
|
+
if (preservedNames.length === 0)
|
|
5729
|
+
return;
|
|
5730
|
+
const baseSections = Array.isArray(baseConfig['sections']) ? baseConfig['sections'] : [];
|
|
5731
|
+
const patchSections = Array.isArray(patchConfig['sections'])
|
|
5732
|
+
? patchConfig['sections']
|
|
5733
|
+
: (patchConfig['sections'] = []);
|
|
5734
|
+
for (const section of baseSections) {
|
|
5735
|
+
if (!this.sectionContainsAnyField(section, preservedNames))
|
|
5736
|
+
continue;
|
|
5737
|
+
if (this.sectionContainsAnyField({ rows: [{ columns: [{ fields: this.flattenSectionFields(patchSections) }] }] }, preservedNames)) {
|
|
5738
|
+
continue;
|
|
5739
|
+
}
|
|
5740
|
+
patchSections.push(this.clone(section));
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
isLocalTransientField(field) {
|
|
5744
|
+
return field?.source === 'local' || field?.transient === true || field?.submitPolicy === 'omit';
|
|
5745
|
+
}
|
|
5746
|
+
sectionContainsAnyField(section, names) {
|
|
5747
|
+
const fields = this.flattenSectionFields([section]);
|
|
5748
|
+
return names.some((name) => fields.includes(name));
|
|
5749
|
+
}
|
|
5750
|
+
flattenSectionFields(sections) {
|
|
5751
|
+
const fields = [];
|
|
5752
|
+
for (const section of sections) {
|
|
5753
|
+
for (const row of section?.rows ?? []) {
|
|
5754
|
+
for (const column of row?.columns ?? []) {
|
|
5755
|
+
for (const field of column?.fields ?? []) {
|
|
5756
|
+
if (typeof field === 'string')
|
|
5757
|
+
fields.push(field);
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
5762
|
+
return fields;
|
|
5763
|
+
}
|
|
5529
5764
|
mergeLinks(baseLinks, patchLinks) {
|
|
5530
5765
|
const base = baseLinks || [];
|
|
5531
5766
|
const patch = patchLinks || [];
|
|
@@ -5646,20 +5881,37 @@ class PageBuilderAiAdapter {
|
|
|
5646
5881
|
linkKey(link) {
|
|
5647
5882
|
if (!link)
|
|
5648
5883
|
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);
|
|
5884
|
+
const from = this.endpointKey(link.from);
|
|
5885
|
+
const to = this.endpointKey(link.to);
|
|
5659
5886
|
if (!from || !to)
|
|
5660
5887
|
return null;
|
|
5661
5888
|
return `${from}->${to}`;
|
|
5662
5889
|
}
|
|
5890
|
+
endpointKey(endpoint) {
|
|
5891
|
+
if (endpoint.kind === 'component-port') {
|
|
5892
|
+
if (!endpoint.ref.widget || !endpoint.ref.port)
|
|
5893
|
+
return null;
|
|
5894
|
+
return [
|
|
5895
|
+
'widget',
|
|
5896
|
+
endpoint.ref.widget,
|
|
5897
|
+
endpoint.ref.port,
|
|
5898
|
+
endpoint.ref.direction,
|
|
5899
|
+
this.nestedPathKey(endpoint.ref.nestedPath),
|
|
5900
|
+
].filter((part) => part !== '').join('::');
|
|
5901
|
+
}
|
|
5902
|
+
return endpoint.ref.path ? `state:${endpoint.ref.path}::${endpoint.ref.layer || 'values'}` : null;
|
|
5903
|
+
}
|
|
5904
|
+
nestedPathKey(nestedPath) {
|
|
5905
|
+
if (!nestedPath?.length)
|
|
5906
|
+
return '';
|
|
5907
|
+
return `nested:${JSON.stringify(nestedPath.map((segment) => ({
|
|
5908
|
+
kind: segment.kind,
|
|
5909
|
+
id: segment.id,
|
|
5910
|
+
key: segment.key,
|
|
5911
|
+
index: segment.index,
|
|
5912
|
+
componentType: segment.componentType,
|
|
5913
|
+
})))}`;
|
|
5914
|
+
}
|
|
5663
5915
|
pickLayoutOptions(options) {
|
|
5664
5916
|
if (!options)
|
|
5665
5917
|
return undefined;
|
|
@@ -5717,42 +5969,60 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5717
5969
|
this.service = service;
|
|
5718
5970
|
this.context = context;
|
|
5719
5971
|
}
|
|
5720
|
-
|
|
5972
|
+
submit(request) {
|
|
5721
5973
|
const prompt = request.prompt?.trim();
|
|
5722
5974
|
if (!prompt) {
|
|
5723
|
-
return {
|
|
5975
|
+
return Promise.resolve({
|
|
5724
5976
|
state: 'listening',
|
|
5725
5977
|
phase: 'capture',
|
|
5726
|
-
};
|
|
5978
|
+
});
|
|
5979
|
+
}
|
|
5980
|
+
if (this.isResourceDiscoveryTool(request)) {
|
|
5981
|
+
return this.handleResourceDiscoveryTool(request, prompt);
|
|
5982
|
+
}
|
|
5983
|
+
if (this.context.enableTurnStream?.() === true && this.service.streamTurn) {
|
|
5984
|
+
return this.submitWithTurnStream(request, prompt);
|
|
5727
5985
|
}
|
|
5986
|
+
return this.submitSynchronously(request, prompt);
|
|
5987
|
+
}
|
|
5988
|
+
async submitSynchronously(request, prompt) {
|
|
5728
5989
|
try {
|
|
5729
5990
|
const authoringContext = this.buildAuthoringContext(request);
|
|
5730
5991
|
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
5731
5992
|
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
|
-
}));
|
|
5993
|
+
const intentResolution = await firstValueFrom(this.service.resolveIntent(this.buildIntentResolutionRequest(prompt, selectedWidgetKey, componentCapabilities, authoringContext)));
|
|
5744
5994
|
const intentAssistantMessage = this.resolveIntentAssistantMessage(intentResolution);
|
|
5745
5995
|
if (intentAssistantMessage) {
|
|
5996
|
+
const quickReplies = this.toShellQuickReplies(intentResolution.quickReplies ?? []);
|
|
5997
|
+
const explicitPendingClarification = this.toShellPendingClarification(intentResolution.pendingClarification);
|
|
5998
|
+
const intentClarification = this.resolveIntentClarification(intentResolution);
|
|
5999
|
+
const pendingClarification = explicitPendingClarification
|
|
6000
|
+
?? (intentClarification
|
|
6001
|
+
? this.buildPendingClarificationForNextTurn(request, this.resolveEffectivePrompt(intentResolution, prompt), intentClarification)
|
|
6002
|
+
: null);
|
|
6003
|
+
if (!this.hasPendingClarificationQuestion(pendingClarification) && quickReplies.length === 0) {
|
|
6004
|
+
return {
|
|
6005
|
+
state: 'success',
|
|
6006
|
+
phase: 'summarize',
|
|
6007
|
+
assistantMessage: intentAssistantMessage,
|
|
6008
|
+
quickReplies,
|
|
6009
|
+
canApply: false,
|
|
6010
|
+
statusText: '',
|
|
6011
|
+
errorText: '',
|
|
6012
|
+
preview: null,
|
|
6013
|
+
diagnostics: { intentResolution },
|
|
6014
|
+
};
|
|
6015
|
+
}
|
|
5746
6016
|
return {
|
|
5747
6017
|
state: 'clarification',
|
|
5748
6018
|
phase: 'clarify',
|
|
5749
6019
|
assistantMessage: intentAssistantMessage,
|
|
5750
|
-
quickReplies
|
|
6020
|
+
quickReplies,
|
|
5751
6021
|
canApply: false,
|
|
5752
6022
|
statusText: '',
|
|
5753
6023
|
errorText: '',
|
|
5754
6024
|
preview: null,
|
|
5755
|
-
pendingClarification
|
|
6025
|
+
pendingClarification,
|
|
5756
6026
|
diagnostics: { intentResolution },
|
|
5757
6027
|
};
|
|
5758
6028
|
}
|
|
@@ -5827,6 +6097,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5827
6097
|
state: 'review',
|
|
5828
6098
|
phase: 'review',
|
|
5829
6099
|
assistantMessage: status,
|
|
6100
|
+
quickReplies: [],
|
|
5830
6101
|
canApply: true,
|
|
5831
6102
|
statusText: status,
|
|
5832
6103
|
errorText: '',
|
|
@@ -5847,6 +6118,11 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5847
6118
|
};
|
|
5848
6119
|
}
|
|
5849
6120
|
}
|
|
6121
|
+
submitWithTurnStream(request, prompt) {
|
|
6122
|
+
return from(this.buildTurnStreamRequest(request, prompt)).pipe(concatMap((streamRequest) => this.service.streamTurn(streamRequest)), concatMap((event) => from(this.toTurnResultFromStreamEvent(event))), catchError((error) => this.shouldFallbackFromTurnStreamError(error)
|
|
6123
|
+
? from(this.submitSynchronously(request, prompt))
|
|
6124
|
+
: of(this.toTurnStreamTransportErrorResult(error))));
|
|
6125
|
+
}
|
|
5850
6126
|
async cancel() {
|
|
5851
6127
|
return {
|
|
5852
6128
|
state: 'listening',
|
|
@@ -5860,10 +6136,413 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5860
6136
|
pendingPatch: null,
|
|
5861
6137
|
};
|
|
5862
6138
|
}
|
|
6139
|
+
async buildTurnStreamRequest(request, prompt) {
|
|
6140
|
+
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
6141
|
+
const selectedWidgetKey = this.context.selectedWidgetKey();
|
|
6142
|
+
return {
|
|
6143
|
+
userPrompt: prompt,
|
|
6144
|
+
targetApp: this.context.targetApp,
|
|
6145
|
+
targetComponentId: this.context.targetComponentId,
|
|
6146
|
+
currentRoute: null,
|
|
6147
|
+
currentPage: this.context.currentPage(),
|
|
6148
|
+
selectedWidgetKey,
|
|
6149
|
+
provider: this.context.provider(),
|
|
6150
|
+
model: this.context.model(),
|
|
6151
|
+
apiKey: this.context.apiKey(),
|
|
6152
|
+
componentCapabilities,
|
|
6153
|
+
...this.buildAuthoringContext(request),
|
|
6154
|
+
};
|
|
6155
|
+
}
|
|
6156
|
+
async toTurnResultFromStreamEvent(event) {
|
|
6157
|
+
const payload = this.toJsonObject(event.payload) ?? {};
|
|
6158
|
+
if (event.type === 'result') {
|
|
6159
|
+
return this.toResultTurnFromStreamPayload(payload);
|
|
6160
|
+
}
|
|
6161
|
+
if (event.type === 'error') {
|
|
6162
|
+
const message = this.describeStreamError(payload);
|
|
6163
|
+
return {
|
|
6164
|
+
state: 'error',
|
|
6165
|
+
phase: 'contextualize',
|
|
6166
|
+
assistantMessage: message,
|
|
6167
|
+
canApply: false,
|
|
6168
|
+
statusText: '',
|
|
6169
|
+
errorText: message,
|
|
6170
|
+
preview: null,
|
|
6171
|
+
diagnostics: { streamError: payload },
|
|
6172
|
+
};
|
|
6173
|
+
}
|
|
6174
|
+
if (event.type === 'cancelled') {
|
|
6175
|
+
return {
|
|
6176
|
+
state: 'listening',
|
|
6177
|
+
phase: 'capture',
|
|
6178
|
+
assistantMessage: this.context.tx('agentic.status.cancelled', 'Request cancelled.'),
|
|
6179
|
+
quickReplies: [],
|
|
6180
|
+
canApply: false,
|
|
6181
|
+
statusText: '',
|
|
6182
|
+
errorText: '',
|
|
6183
|
+
preview: null,
|
|
6184
|
+
pendingPatch: null,
|
|
6185
|
+
};
|
|
6186
|
+
}
|
|
6187
|
+
return {
|
|
6188
|
+
state: 'processing',
|
|
6189
|
+
phase: this.phaseForStreamPayload(payload),
|
|
6190
|
+
assistantMessage: undefined,
|
|
6191
|
+
canApply: false,
|
|
6192
|
+
statusText: this.statusForStreamPayload(payload),
|
|
6193
|
+
errorText: '',
|
|
6194
|
+
preview: null,
|
|
6195
|
+
};
|
|
6196
|
+
}
|
|
6197
|
+
async toResultTurnFromStreamPayload(payload) {
|
|
6198
|
+
const intentResolution = payload['intentResolution'];
|
|
6199
|
+
const preview = payload['preview'];
|
|
6200
|
+
if (!intentResolution) {
|
|
6201
|
+
const message = this.context.tx('agentic.errors.intentResolution', 'Intent could not be resolved.');
|
|
6202
|
+
return {
|
|
6203
|
+
state: 'error',
|
|
6204
|
+
phase: 'contextualize',
|
|
6205
|
+
assistantMessage: message,
|
|
6206
|
+
canApply: false,
|
|
6207
|
+
statusText: '',
|
|
6208
|
+
errorText: message,
|
|
6209
|
+
preview: null,
|
|
6210
|
+
};
|
|
6211
|
+
}
|
|
6212
|
+
const assistantMessage = this.readString(payload, 'assistantMessage')
|
|
6213
|
+
|| this.resolveIntentAssistantMessage(intentResolution)
|
|
6214
|
+
|| (preview ? this.context.describePreviewStatus(preview) : '');
|
|
6215
|
+
const quickReplies = Array.isArray(payload['quickReplies'])
|
|
6216
|
+
? this.toShellQuickReplies(payload['quickReplies'])
|
|
6217
|
+
: [];
|
|
6218
|
+
const canApply = payload['canApply'] === true && !!preview?.valid;
|
|
6219
|
+
if (!canApply) {
|
|
6220
|
+
const pendingClarification = this.toShellPendingClarification(intentResolution?.pendingClarification);
|
|
6221
|
+
const requiresChoice = quickReplies.length > 0;
|
|
6222
|
+
return {
|
|
6223
|
+
state: pendingClarification || requiresChoice ? 'clarification' : 'success',
|
|
6224
|
+
phase: pendingClarification || requiresChoice ? 'clarify' : 'summarize',
|
|
6225
|
+
assistantMessage,
|
|
6226
|
+
quickReplies,
|
|
6227
|
+
canApply: false,
|
|
6228
|
+
statusText: '',
|
|
6229
|
+
errorText: '',
|
|
6230
|
+
preview: null,
|
|
6231
|
+
pendingClarification,
|
|
6232
|
+
diagnostics: { intentResolution, preview },
|
|
6233
|
+
};
|
|
6234
|
+
}
|
|
6235
|
+
const applied = await this.context.applyLocalPreview(preview);
|
|
6236
|
+
if (!applied.success) {
|
|
6237
|
+
const message = applied.error || this.context.tx('agentic.errors.applyLocal', 'Preview could not be applied.');
|
|
6238
|
+
return {
|
|
6239
|
+
state: 'error',
|
|
6240
|
+
phase: 'preview',
|
|
6241
|
+
assistantMessage: message,
|
|
6242
|
+
canApply: false,
|
|
6243
|
+
statusText: '',
|
|
6244
|
+
errorText: message,
|
|
6245
|
+
preview: null,
|
|
6246
|
+
diagnostics: { intentResolution, preview },
|
|
6247
|
+
};
|
|
6248
|
+
}
|
|
6249
|
+
const status = assistantMessage || this.context.describePreviewStatus(preview);
|
|
6250
|
+
return {
|
|
6251
|
+
state: 'review',
|
|
6252
|
+
phase: 'review',
|
|
6253
|
+
assistantMessage: status,
|
|
6254
|
+
quickReplies: [],
|
|
6255
|
+
canApply: true,
|
|
6256
|
+
statusText: status,
|
|
6257
|
+
errorText: '',
|
|
6258
|
+
preview,
|
|
6259
|
+
diagnostics: { intentResolution, preview },
|
|
6260
|
+
};
|
|
6261
|
+
}
|
|
6262
|
+
phaseForStreamPayload(payload) {
|
|
6263
|
+
const phase = this.readString(payload, 'phase');
|
|
6264
|
+
if (phase === 'intent.resolve')
|
|
6265
|
+
return 'contextualize';
|
|
6266
|
+
if (phase === 'resource.discovery')
|
|
6267
|
+
return 'contextualize';
|
|
6268
|
+
if (phase.startsWith('preview.'))
|
|
6269
|
+
return 'preview';
|
|
6270
|
+
if (phase === 'review')
|
|
6271
|
+
return 'review';
|
|
6272
|
+
return 'contextualize';
|
|
6273
|
+
}
|
|
6274
|
+
statusForStreamPayload(payload) {
|
|
6275
|
+
const phase = this.readString(payload, 'phase');
|
|
6276
|
+
switch (phase) {
|
|
6277
|
+
case 'context.bundle':
|
|
6278
|
+
return this.context.tx('agentic.status.contextBundle', 'Preparing context...');
|
|
6279
|
+
case 'intent.resolve':
|
|
6280
|
+
if (this.isSecondPassStreamPayload(payload)) {
|
|
6281
|
+
return this.context.tx('agentic.status.refinedCandidates', 'Reviewing the retrieved resources with the AI...');
|
|
6282
|
+
}
|
|
6283
|
+
return this.context.tx('agentic.status.resolvingIntent', 'Resolving intent...');
|
|
6284
|
+
case 'resource.discovery':
|
|
6285
|
+
if (this.isBackendResourceDiscoveryPayload(payload)) {
|
|
6286
|
+
return this.context.tx('agentic.status.resourceDiscoveryBackend', 'Found API resources in the backend catalog.');
|
|
6287
|
+
}
|
|
6288
|
+
return this.context.tx('agentic.status.resourceDiscovery', 'Finding API resources...');
|
|
6289
|
+
case 'preview.plan':
|
|
6290
|
+
return this.context.tx('agentic.status.previewing', 'Generating preview...');
|
|
6291
|
+
case 'preview.compile':
|
|
6292
|
+
return this.context.tx('agentic.status.previewCompile', 'Compiling preview...');
|
|
6293
|
+
default:
|
|
6294
|
+
return this.readString(payload, 'summary') || this.readString(payload, 'message');
|
|
6295
|
+
}
|
|
6296
|
+
}
|
|
6297
|
+
isBackendResourceDiscoveryPayload(payload) {
|
|
6298
|
+
const diagnostics = this.toJsonObject(payload['diagnostics']);
|
|
6299
|
+
return diagnostics?.['source'] === 'backend-resource-catalog';
|
|
6300
|
+
}
|
|
6301
|
+
isSecondPassStreamPayload(payload) {
|
|
6302
|
+
const diagnostics = this.toJsonObject(payload['diagnostics']);
|
|
6303
|
+
return diagnostics?.['secondPass'] === true;
|
|
6304
|
+
}
|
|
6305
|
+
describeStreamError(payload) {
|
|
6306
|
+
const code = this.readString(payload, 'code');
|
|
6307
|
+
if (code === 'agentic-authoring-timeout') {
|
|
6308
|
+
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.');
|
|
6309
|
+
}
|
|
6310
|
+
if (code === 'agentic-authoring-processing-failed') {
|
|
6311
|
+
return this.context.tx('agentic.errors.streamProcessing', 'The assistant could not finish this authoring request. Try again or ask support to review the diagnostics.');
|
|
6312
|
+
}
|
|
6313
|
+
return this.readString(payload, 'assistantMessage')
|
|
6314
|
+
|| this.context.tx('agentic.errors.generic', 'AI authoring failed.');
|
|
6315
|
+
}
|
|
6316
|
+
toTurnStreamTransportErrorResult(error) {
|
|
6317
|
+
const message = this.isTurnStreamConnectionError(error)
|
|
6318
|
+
? 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.')
|
|
6319
|
+
: this.context.describeError(error);
|
|
6320
|
+
return {
|
|
6321
|
+
state: 'error',
|
|
6322
|
+
phase: 'contextualize',
|
|
6323
|
+
assistantMessage: message,
|
|
6324
|
+
canApply: false,
|
|
6325
|
+
statusText: '',
|
|
6326
|
+
errorText: message,
|
|
6327
|
+
preview: null,
|
|
6328
|
+
diagnostics: { streamTransportError: this.toJsonObject(error) ?? { reason: 'stream-transport-error' } },
|
|
6329
|
+
};
|
|
6330
|
+
}
|
|
6331
|
+
shouldFallbackFromTurnStreamError(error) {
|
|
6332
|
+
if (this.isTurnStreamConnectionError(error)) {
|
|
6333
|
+
return false;
|
|
6334
|
+
}
|
|
6335
|
+
const status = this.readErrorStatus(error);
|
|
6336
|
+
if (status === 401 || status === 403) {
|
|
6337
|
+
return false;
|
|
6338
|
+
}
|
|
6339
|
+
return status === 404 || status === 501 || status === 503;
|
|
6340
|
+
}
|
|
6341
|
+
isTurnStreamConnectionError(error) {
|
|
6342
|
+
return this.toJsonObject(error)?.['praxisAgenticTurnStreamConnectionError'] === true;
|
|
6343
|
+
}
|
|
6344
|
+
readErrorStatus(error) {
|
|
6345
|
+
const status = this.toJsonObject(error)?.['status'];
|
|
6346
|
+
return typeof status === 'number' && Number.isFinite(status) ? status : null;
|
|
6347
|
+
}
|
|
6348
|
+
async handleResourceDiscoveryTool(request, prompt) {
|
|
6349
|
+
const contextHints = this.toJsonObject(request.action?.contextHints) ?? {};
|
|
6350
|
+
const retrievalQuery = this.readString(contextHints, 'retrievalQuery') || prompt;
|
|
6351
|
+
const artifactKind = this.readString(contextHints, 'artifactKind') || null;
|
|
6352
|
+
const limit = this.readNumber(contextHints, 'limit');
|
|
6353
|
+
const result = await firstValueFrom(this.service.searchResourceCandidates({
|
|
6354
|
+
retrievalQuery,
|
|
6355
|
+
userPrompt: prompt,
|
|
6356
|
+
artifactKind,
|
|
6357
|
+
limit,
|
|
6358
|
+
}));
|
|
6359
|
+
const quickReplies = result.quickReplies?.length
|
|
6360
|
+
? this.toShellQuickReplies(result.quickReplies)
|
|
6361
|
+
: this.toResourceCandidateQuickReplies(result.candidates, result.artifactKind);
|
|
6362
|
+
const assistantMessage = result.assistantMessage?.trim()
|
|
6363
|
+
|| (quickReplies.length
|
|
6364
|
+
? this.context.tx('agentic.resourceDiscovery.found', 'I found APIs that can feed this screen. Choose one before generating the preview.')
|
|
6365
|
+
: this.context.tx('agentic.resourceDiscovery.empty', 'I did not find a matching API yet. Describe the business data this screen should use.'));
|
|
6366
|
+
const intentResult = await this.resolveIntentAfterResourceDiscovery(request, request.pendingClarification?.sourcePrompt || prompt, contextHints, result, assistantMessage, quickReplies);
|
|
6367
|
+
if (intentResult) {
|
|
6368
|
+
return intentResult;
|
|
6369
|
+
}
|
|
6370
|
+
return {
|
|
6371
|
+
state: 'clarification',
|
|
6372
|
+
phase: 'clarify',
|
|
6373
|
+
assistantMessage,
|
|
6374
|
+
quickReplies,
|
|
6375
|
+
canApply: false,
|
|
6376
|
+
statusText: '',
|
|
6377
|
+
errorText: '',
|
|
6378
|
+
preview: null,
|
|
6379
|
+
pendingClarification: {
|
|
6380
|
+
sourcePrompt: request.pendingClarification?.sourcePrompt || prompt,
|
|
6381
|
+
assistantMessage,
|
|
6382
|
+
questions: [
|
|
6383
|
+
{
|
|
6384
|
+
id: 'resource',
|
|
6385
|
+
type: 'text',
|
|
6386
|
+
label: assistantMessage,
|
|
6387
|
+
},
|
|
6388
|
+
],
|
|
6389
|
+
clientTurnId: request.clientTurnId,
|
|
6390
|
+
diagnostics: {
|
|
6391
|
+
resourceDiscovery: {
|
|
6392
|
+
tool: result.tool,
|
|
6393
|
+
retrievalQuery: result.retrievalQuery,
|
|
6394
|
+
artifactKind: result.artifactKind,
|
|
6395
|
+
warnings: result.warnings,
|
|
6396
|
+
},
|
|
6397
|
+
},
|
|
6398
|
+
},
|
|
6399
|
+
diagnostics: { resourceDiscovery: result },
|
|
6400
|
+
};
|
|
6401
|
+
}
|
|
6402
|
+
async resolveIntentAfterResourceDiscovery(request, prompt, contextHints, result, fallbackAssistantMessage, fallbackQuickReplies) {
|
|
6403
|
+
if (!result.valid || !result.candidates?.length) {
|
|
6404
|
+
return null;
|
|
6405
|
+
}
|
|
6406
|
+
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
6407
|
+
const authoringContext = this.buildAuthoringContext(request);
|
|
6408
|
+
const intentResolution = await firstValueFrom(this.service.resolveIntent(this.buildIntentResolutionRequest(prompt, this.context.selectedWidgetKey(), componentCapabilities, {
|
|
6409
|
+
...authoringContext,
|
|
6410
|
+
contextHints: this.resourceDiscoveryContextHints(contextHints, result),
|
|
6411
|
+
})));
|
|
6412
|
+
const assistantMessage = this.resolveIntentAssistantMessage(intentResolution)
|
|
6413
|
+
|| this.resolveIntentClarification(intentResolution)
|
|
6414
|
+
|| fallbackAssistantMessage;
|
|
6415
|
+
const quickReplies = intentResolution.quickReplies?.length
|
|
6416
|
+
? this.toShellQuickReplies(intentResolution.quickReplies)
|
|
6417
|
+
: fallbackQuickReplies;
|
|
6418
|
+
const explicitPendingClarification = this.toShellPendingClarification(intentResolution.pendingClarification);
|
|
6419
|
+
const pendingClarification = explicitPendingClarification
|
|
6420
|
+
?? this.buildPendingClarificationForNextTurn(request, this.resolveEffectivePrompt(intentResolution, prompt), assistantMessage);
|
|
6421
|
+
if (!assistantMessage && quickReplies.length === 0) {
|
|
6422
|
+
return null;
|
|
6423
|
+
}
|
|
6424
|
+
return {
|
|
6425
|
+
state: quickReplies.length > 0 || this.hasPendingClarificationQuestion(pendingClarification)
|
|
6426
|
+
? 'clarification'
|
|
6427
|
+
: 'success',
|
|
6428
|
+
phase: quickReplies.length > 0 || this.hasPendingClarificationQuestion(pendingClarification)
|
|
6429
|
+
? 'clarify'
|
|
6430
|
+
: 'summarize',
|
|
6431
|
+
assistantMessage,
|
|
6432
|
+
quickReplies,
|
|
6433
|
+
canApply: false,
|
|
6434
|
+
statusText: '',
|
|
6435
|
+
errorText: '',
|
|
6436
|
+
preview: null,
|
|
6437
|
+
pendingClarification,
|
|
6438
|
+
diagnostics: { resourceDiscovery: result, intentResolution },
|
|
6439
|
+
};
|
|
6440
|
+
}
|
|
6441
|
+
resourceDiscoveryContextHints(contextHints, result) {
|
|
6442
|
+
const { tool: _tool, ...baseContextHints } = contextHints;
|
|
6443
|
+
return {
|
|
6444
|
+
...baseContextHints,
|
|
6445
|
+
resourceDiscovery: {
|
|
6446
|
+
tool: result.tool,
|
|
6447
|
+
retrievalQuery: result.retrievalQuery,
|
|
6448
|
+
artifactKind: result.artifactKind,
|
|
6449
|
+
assistantMessage: result.assistantMessage ?? null,
|
|
6450
|
+
candidates: result.candidates.map((candidate) => ({
|
|
6451
|
+
resourcePath: candidate.resourcePath,
|
|
6452
|
+
operation: candidate.operation,
|
|
6453
|
+
schemaUrl: candidate.schemaUrl,
|
|
6454
|
+
submitUrl: candidate.submitUrl,
|
|
6455
|
+
submitMethod: candidate.submitMethod,
|
|
6456
|
+
score: candidate.score,
|
|
6457
|
+
reason: candidate.reason,
|
|
6458
|
+
evidence: candidate.evidence,
|
|
6459
|
+
})),
|
|
6460
|
+
quickReplies: (result.quickReplies ?? []).map((reply) => ({
|
|
6461
|
+
id: reply.id,
|
|
6462
|
+
kind: reply.kind,
|
|
6463
|
+
label: reply.label,
|
|
6464
|
+
prompt: reply.prompt,
|
|
6465
|
+
description: reply.description ?? null,
|
|
6466
|
+
icon: reply.icon ?? null,
|
|
6467
|
+
tone: reply.tone ?? null,
|
|
6468
|
+
contextHints: this.toJsonObject(reply.contextHints) ?? null,
|
|
6469
|
+
})),
|
|
6470
|
+
warnings: result.warnings,
|
|
6471
|
+
},
|
|
6472
|
+
};
|
|
6473
|
+
}
|
|
6474
|
+
isResourceDiscoveryTool(request) {
|
|
6475
|
+
const contextHints = this.toJsonObject(request.action?.contextHints);
|
|
6476
|
+
return this.readString(contextHints, 'tool') === 'searchApiResources';
|
|
6477
|
+
}
|
|
6478
|
+
toResourceCandidateQuickReplies(candidates, artifactKind) {
|
|
6479
|
+
return candidates
|
|
6480
|
+
.filter((candidate) => !!candidate.resourcePath?.trim())
|
|
6481
|
+
.map((candidate) => {
|
|
6482
|
+
const resourcePath = candidate.resourcePath.trim();
|
|
6483
|
+
const submitUrl = candidate.submitUrl?.trim() || resourcePath;
|
|
6484
|
+
return {
|
|
6485
|
+
id: this.resourceCandidateId(resourcePath),
|
|
6486
|
+
kind: 'suggestion',
|
|
6487
|
+
label: this.resourceCandidateLabel(resourcePath),
|
|
6488
|
+
prompt: this.formatResourceCandidatePrompt(resourcePath),
|
|
6489
|
+
description: this.resourceCandidateDescription(candidate),
|
|
6490
|
+
icon: 'dataset',
|
|
6491
|
+
tone: 'resource',
|
|
6492
|
+
contextHints: {
|
|
6493
|
+
resourcePath,
|
|
6494
|
+
submitUrl,
|
|
6495
|
+
operation: candidate.operation,
|
|
6496
|
+
schemaUrl: candidate.schemaUrl,
|
|
6497
|
+
submitMethod: candidate.submitMethod,
|
|
6498
|
+
artifactKind,
|
|
6499
|
+
},
|
|
6500
|
+
};
|
|
6501
|
+
});
|
|
6502
|
+
}
|
|
6503
|
+
formatResourceCandidatePrompt(resourcePath) {
|
|
6504
|
+
return this.context
|
|
6505
|
+
.tx('agentic.resourceDiscovery.useResource', 'Use {resourcePath}')
|
|
6506
|
+
.replace('{resourcePath}', resourcePath);
|
|
6507
|
+
}
|
|
6508
|
+
resourceCandidateId(resourcePath) {
|
|
6509
|
+
const slug = resourcePath
|
|
6510
|
+
.toLowerCase()
|
|
6511
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
6512
|
+
.replace(/^-+|-+$/g, '');
|
|
6513
|
+
return slug ? `resource-${slug}` : 'resource-candidate';
|
|
6514
|
+
}
|
|
6515
|
+
resourceCandidateLabel(resourcePath) {
|
|
6516
|
+
const segment = resourcePath.split('/').filter((part) => !!part).pop() || resourcePath;
|
|
6517
|
+
const label = segment
|
|
6518
|
+
.replace(/[-_]+/g, ' ')
|
|
6519
|
+
.replace(/\s+/g, ' ')
|
|
6520
|
+
.trim();
|
|
6521
|
+
return label
|
|
6522
|
+
? label.charAt(0).toUpperCase() + label.slice(1)
|
|
6523
|
+
: resourcePath;
|
|
6524
|
+
}
|
|
6525
|
+
resourceCandidateDescription(candidate) {
|
|
6526
|
+
const method = candidate.submitMethod?.trim();
|
|
6527
|
+
const url = candidate.submitUrl?.trim();
|
|
6528
|
+
return method && url ? `${method} ${url}` : url || undefined;
|
|
6529
|
+
}
|
|
5863
6530
|
resolveIntentAssistantMessage(intentResolution) {
|
|
6531
|
+
if (this.isExecutableIntent(intentResolution)) {
|
|
6532
|
+
return null;
|
|
6533
|
+
}
|
|
5864
6534
|
const message = intentResolution.assistantMessage?.trim();
|
|
5865
6535
|
return message || null;
|
|
5866
6536
|
}
|
|
6537
|
+
isExecutableIntent(intentResolution) {
|
|
6538
|
+
const operationKind = intentResolution.operationKind;
|
|
6539
|
+
const artifactKind = intentResolution.artifactKind;
|
|
6540
|
+
return !!intentResolution.valid
|
|
6541
|
+
&& intentResolution.gate?.status === 'eligible'
|
|
6542
|
+
&& !!intentResolution.selectedCandidate
|
|
6543
|
+
&& (artifactKind === 'form' || artifactKind === 'dashboard' || artifactKind === 'table' || artifactKind === 'page')
|
|
6544
|
+
&& (operationKind === 'create' || operationKind === 'modify' || operationKind === 'remove' || operationKind === 'compose');
|
|
6545
|
+
}
|
|
5867
6546
|
resolveIntentClarification(intentResolution) {
|
|
5868
6547
|
const questions = intentResolution.clarificationQuestions?.filter((question) => !!question) ?? [];
|
|
5869
6548
|
if (!intentResolution.valid && questions.length) {
|
|
@@ -5872,7 +6551,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5872
6551
|
return null;
|
|
5873
6552
|
}
|
|
5874
6553
|
describeIntentResolutionFailure(intentResolution) {
|
|
5875
|
-
if (
|
|
6554
|
+
if (this.isExecutableIntent(intentResolution)) {
|
|
5876
6555
|
return null;
|
|
5877
6556
|
}
|
|
5878
6557
|
const questions = intentResolution.clarificationQuestions?.filter((question) => !!question) ?? [];
|
|
@@ -5897,6 +6576,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5897
6576
|
kind: reply.kind,
|
|
5898
6577
|
label: reply.label,
|
|
5899
6578
|
prompt: reply.prompt,
|
|
6579
|
+
description: reply.description,
|
|
6580
|
+
icon: reply.icon,
|
|
6581
|
+
tone: reply.tone,
|
|
6582
|
+
contextHints: this.toJsonObject(reply.contextHints),
|
|
5900
6583
|
}));
|
|
5901
6584
|
}
|
|
5902
6585
|
buildPendingClarificationForNextTurn(request, sourcePrompt, clarification) {
|
|
@@ -5947,16 +6630,47 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
5947
6630
|
diagnostics: this.toJsonObject(pending?.diagnostics) ?? undefined,
|
|
5948
6631
|
};
|
|
5949
6632
|
}
|
|
6633
|
+
hasPendingClarificationQuestion(pending) {
|
|
6634
|
+
return !!pending?.sourcePrompt?.trim() && pending.questions.some((question) => !!question.label?.trim());
|
|
6635
|
+
}
|
|
5950
6636
|
resolveEffectivePrompt(intentResolution, fallbackPrompt) {
|
|
5951
6637
|
return intentResolution.effectivePrompt?.trim() || fallbackPrompt;
|
|
5952
6638
|
}
|
|
5953
6639
|
buildAuthoringContext(request) {
|
|
6640
|
+
const contextHints = this.buildContextHints(request);
|
|
5954
6641
|
return {
|
|
5955
6642
|
sessionId: request.sessionId,
|
|
5956
6643
|
clientTurnId: request.clientTurnId,
|
|
5957
6644
|
conversationMessages: this.toConversationMessages(request.messages ?? []),
|
|
5958
6645
|
pendingClarification: this.toPendingClarification(request.pendingClarification),
|
|
5959
6646
|
attachmentSummaries: this.toAttachmentSummaries(request.attachments ?? []),
|
|
6647
|
+
contextHints,
|
|
6648
|
+
};
|
|
6649
|
+
}
|
|
6650
|
+
buildIntentResolutionRequest(prompt, selectedWidgetKey, componentCapabilities, authoringContext) {
|
|
6651
|
+
return {
|
|
6652
|
+
userPrompt: prompt,
|
|
6653
|
+
targetApp: this.context.targetApp,
|
|
6654
|
+
targetComponentId: this.context.targetComponentId,
|
|
6655
|
+
currentPage: this.context.currentPage(),
|
|
6656
|
+
selectedWidgetKey,
|
|
6657
|
+
provider: this.context.provider(),
|
|
6658
|
+
model: this.context.model(),
|
|
6659
|
+
apiKey: this.context.apiKey(),
|
|
6660
|
+
componentCapabilities,
|
|
6661
|
+
...authoringContext,
|
|
6662
|
+
};
|
|
6663
|
+
}
|
|
6664
|
+
buildContextHints(request) {
|
|
6665
|
+
const base = this.toJsonObject(request.action?.contextHints)
|
|
6666
|
+
?? this.toJsonObject(request.contextHints);
|
|
6667
|
+
const includeLlmDiagnostics = this.context.includeLlmDiagnostics?.() === true;
|
|
6668
|
+
if (!includeLlmDiagnostics) {
|
|
6669
|
+
return base ?? undefined;
|
|
6670
|
+
}
|
|
6671
|
+
return {
|
|
6672
|
+
...(base ?? {}),
|
|
6673
|
+
includeLlmDiagnostics: true,
|
|
5960
6674
|
};
|
|
5961
6675
|
}
|
|
5962
6676
|
toAttachmentSummaries(attachments) {
|
|
@@ -6005,6 +6719,14 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
6005
6719
|
? value
|
|
6006
6720
|
: null;
|
|
6007
6721
|
}
|
|
6722
|
+
readString(value, key) {
|
|
6723
|
+
const raw = value?.[key];
|
|
6724
|
+
return typeof raw === 'string' ? raw.trim() : '';
|
|
6725
|
+
}
|
|
6726
|
+
readNumber(value, key) {
|
|
6727
|
+
const raw = value?.[key];
|
|
6728
|
+
return typeof raw === 'number' && Number.isFinite(raw) ? raw : null;
|
|
6729
|
+
}
|
|
6008
6730
|
}
|
|
6009
6731
|
|
|
6010
6732
|
function buildConnectionsViewerModel(page) {
|
|
@@ -6017,6 +6739,12 @@ function buildConnectionsViewerModel(page) {
|
|
|
6017
6739
|
const edges = links.map((link) => {
|
|
6018
6740
|
const fromWidgetId = link.from.kind === 'component-port' ? link.from.ref.widget : null;
|
|
6019
6741
|
const toWidgetId = link.to.kind === 'component-port' ? link.to.ref.widget : null;
|
|
6742
|
+
const fromNestedPath = link.from.kind === 'component-port' && link.from.ref.nestedPath?.length
|
|
6743
|
+
? clone(link.from.ref.nestedPath)
|
|
6744
|
+
: null;
|
|
6745
|
+
const toNestedPath = link.to.kind === 'component-port' && link.to.ref.nestedPath?.length
|
|
6746
|
+
? clone(link.to.ref.nestedPath)
|
|
6747
|
+
: null;
|
|
6020
6748
|
const toStatePath = link.to.kind === 'state' ? link.to.ref.path : null;
|
|
6021
6749
|
const diagnosticReasons = [];
|
|
6022
6750
|
if (fromWidgetId && !counts.has(fromWidgetId)) {
|
|
@@ -6039,9 +6767,17 @@ function buildConnectionsViewerModel(page) {
|
|
|
6039
6767
|
id: link.id,
|
|
6040
6768
|
fromKind: link.from.kind,
|
|
6041
6769
|
fromWidgetId,
|
|
6770
|
+
fromEndpointId: link.from.kind === 'component-port'
|
|
6771
|
+
? buildComponentEndpointId(link.from.ref.widget, link.from.ref.nestedPath)
|
|
6772
|
+
: null,
|
|
6773
|
+
fromNestedPath,
|
|
6042
6774
|
fromPort: link.from.kind === 'component-port' ? link.from.ref.port : null,
|
|
6043
6775
|
toKind: link.to.kind,
|
|
6044
6776
|
toWidgetId,
|
|
6777
|
+
toEndpointId: link.to.kind === 'component-port'
|
|
6778
|
+
? buildComponentEndpointId(link.to.ref.widget, link.to.ref.nestedPath)
|
|
6779
|
+
: null,
|
|
6780
|
+
toNestedPath,
|
|
6045
6781
|
toPort: link.to.kind === 'component-port' ? link.to.ref.port : null,
|
|
6046
6782
|
toStatePath,
|
|
6047
6783
|
intent: link.intent,
|
|
@@ -6072,6 +6808,31 @@ function buildConnectionsViewerModel(page) {
|
|
|
6072
6808
|
diagnosticLinks: edges.filter((edge) => edge.hasDiagnostics).length,
|
|
6073
6809
|
};
|
|
6074
6810
|
}
|
|
6811
|
+
function buildComponentEndpointId(ownerWidgetKey, nestedPath) {
|
|
6812
|
+
if (!nestedPath?.length) {
|
|
6813
|
+
return ownerWidgetKey;
|
|
6814
|
+
}
|
|
6815
|
+
return `${ownerWidgetKey}${nestedPath
|
|
6816
|
+
.map((segment) => `/${formatPathSegment(segment)}`)
|
|
6817
|
+
.join('')}`;
|
|
6818
|
+
}
|
|
6819
|
+
function formatPathSegment(segment) {
|
|
6820
|
+
if (segment.kind === 'widget') {
|
|
6821
|
+
const identity = segment.key
|
|
6822
|
+
|| segment.componentType
|
|
6823
|
+
|| (typeof segment.index === 'number' ? String(segment.index) : 'unknown');
|
|
6824
|
+
return `widget:${identity}`;
|
|
6825
|
+
}
|
|
6826
|
+
return [
|
|
6827
|
+
segment.kind,
|
|
6828
|
+
segment.id || segment.key || segment.index,
|
|
6829
|
+
].filter((part) => part !== undefined && part !== '').join(':');
|
|
6830
|
+
}
|
|
6831
|
+
function clone(value) {
|
|
6832
|
+
if (value == null || typeof value !== 'object')
|
|
6833
|
+
return value;
|
|
6834
|
+
return JSON.parse(JSON.stringify(value));
|
|
6835
|
+
}
|
|
6075
6836
|
|
|
6076
6837
|
class ConnectionsViewerPanelComponent {
|
|
6077
6838
|
i18n = inject(PraxisI18nService);
|
|
@@ -6522,6 +7283,8 @@ class DynamicPageBuilderComponent {
|
|
|
6522
7283
|
agenticAuthoringComponentId;
|
|
6523
7284
|
agenticAuthoringScope = 'user';
|
|
6524
7285
|
agenticAuthoringEtag;
|
|
7286
|
+
agenticAuthoringIncludeLlmDiagnostics = false;
|
|
7287
|
+
agenticAuthoringEnableStreaming = false;
|
|
6525
7288
|
pageChange = new EventEmitter();
|
|
6526
7289
|
agenticAuthoringApplied = new EventEmitter();
|
|
6527
7290
|
currentPage = signal({ widgets: [] }, ...(ngDevMode ? [{ debugName: "currentPage" }] : []));
|
|
@@ -6536,6 +7299,7 @@ class DynamicPageBuilderComponent {
|
|
|
6536
7299
|
agenticAuthoringConversation = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringConversation" }] : []));
|
|
6537
7300
|
agenticAuthoringQuickReplies = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringQuickReplies" }] : []));
|
|
6538
7301
|
agenticAuthoringAttachments = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringAttachments" }] : []));
|
|
7302
|
+
agenticAuthoringLlmDiagnostics = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringLlmDiagnostics" }] : []));
|
|
6539
7303
|
agenticAuthoringEditingMessageId = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringEditingMessageId" }] : []));
|
|
6540
7304
|
agenticAuthoringPanelLayout = signal({
|
|
6541
7305
|
left: 16,
|
|
@@ -6568,6 +7332,7 @@ class DynamicPageBuilderComponent {
|
|
|
6568
7332
|
this.previewMode = !this.previewMode;
|
|
6569
7333
|
if (this.previewMode) {
|
|
6570
7334
|
this.connectionsViewerOpen.set(false);
|
|
7335
|
+
this.agenticAuthoringLlmDiagnostics.set(null);
|
|
6571
7336
|
}
|
|
6572
7337
|
}
|
|
6573
7338
|
toggleConnectionsViewer() {
|
|
@@ -6728,11 +7493,10 @@ class DynamicPageBuilderComponent {
|
|
|
6728
7493
|
try {
|
|
6729
7494
|
const controller = this.ensureAgenticTurnController();
|
|
6730
7495
|
const editingMessageId = this.agenticAuthoringEditingMessageId();
|
|
6731
|
-
|
|
7496
|
+
await this.consumeAgenticTurn(editingMessageId
|
|
6732
7497
|
? controller.submitEditedMessage(editingMessageId, prompt)
|
|
6733
7498
|
: controller.submitPrompt(prompt));
|
|
6734
7499
|
this.agenticAuthoringEditingMessageId.set(null);
|
|
6735
|
-
this.applyAgenticTurnState(state);
|
|
6736
7500
|
}
|
|
6737
7501
|
catch (error) {
|
|
6738
7502
|
this.agenticAuthoringStatus.set('');
|
|
@@ -6745,24 +7509,79 @@ class DynamicPageBuilderComponent {
|
|
|
6745
7509
|
}
|
|
6746
7510
|
}
|
|
6747
7511
|
async submitAgenticQuickReply(reply) {
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
}
|
|
6751
|
-
if (reply.kind === 'cancel') {
|
|
7512
|
+
const replyKind = (reply.kind || 'suggestion').trim().toLowerCase();
|
|
7513
|
+
if (replyKind === 'cancel') {
|
|
6752
7514
|
this.agenticAuthoringEditingMessageId.set(null);
|
|
6753
|
-
|
|
6754
|
-
this.applyAgenticTurnState(state);
|
|
7515
|
+
await this.consumeAgenticTurn(this.ensureAgenticTurnController().cancel());
|
|
6755
7516
|
return;
|
|
6756
7517
|
}
|
|
6757
|
-
if (
|
|
7518
|
+
if (replyKind === 'revise') {
|
|
6758
7519
|
this.agenticAuthoringEditingMessageId.set(null);
|
|
6759
7520
|
this.agenticAuthoringQuickReplies.set([]);
|
|
6760
7521
|
this.agenticAuthoringStatus.set(this.tx('agentic.status.waitingRevision', 'Refine your prompt and preview again.'));
|
|
6761
7522
|
return;
|
|
6762
7523
|
}
|
|
6763
7524
|
this.agenticAuthoringEditingMessageId.set(null);
|
|
6764
|
-
|
|
6765
|
-
|
|
7525
|
+
const contextHints = reply.contextHints ? { ...reply.contextHints } : undefined;
|
|
7526
|
+
const visiblePrompt = this.agenticQuickReplyVisiblePrompt(reply, contextHints);
|
|
7527
|
+
this.agenticAuthoringPrompt.set(visiblePrompt);
|
|
7528
|
+
this.agenticAuthoringBusy.set(true);
|
|
7529
|
+
this.agenticAuthoringError.set('');
|
|
7530
|
+
this.agenticAuthoringPreviewResult.set(null);
|
|
7531
|
+
this.agenticAuthoringStatus.set(this.tx('agentic.status.resolvingIntent', 'Resolving intent...'));
|
|
7532
|
+
try {
|
|
7533
|
+
const controller = this.ensureAgenticTurnController();
|
|
7534
|
+
await this.consumeAgenticTurn(controller.snapshot().state === 'clarification'
|
|
7535
|
+
? controller.answerClarification({
|
|
7536
|
+
id: reply.id,
|
|
7537
|
+
label: visiblePrompt,
|
|
7538
|
+
value: reply.prompt,
|
|
7539
|
+
description: reply.description ?? undefined,
|
|
7540
|
+
contextHints,
|
|
7541
|
+
})
|
|
7542
|
+
: controller.submitPrompt(visiblePrompt, {
|
|
7543
|
+
kind: replyKind || 'quick-reply',
|
|
7544
|
+
id: reply.id,
|
|
7545
|
+
value: reply.prompt,
|
|
7546
|
+
contextHints,
|
|
7547
|
+
}));
|
|
7548
|
+
}
|
|
7549
|
+
catch (error) {
|
|
7550
|
+
this.agenticAuthoringStatus.set('');
|
|
7551
|
+
const message = this.describeAgenticError(error);
|
|
7552
|
+
this.agenticAuthoringError.set(message);
|
|
7553
|
+
this.appendAgenticMessage('error', message);
|
|
7554
|
+
}
|
|
7555
|
+
finally {
|
|
7556
|
+
this.agenticAuthoringBusy.set(false);
|
|
7557
|
+
}
|
|
7558
|
+
}
|
|
7559
|
+
agenticQuickReplyVisiblePrompt(reply, contextHints) {
|
|
7560
|
+
if (!this.isResourceQuickReply(reply, contextHints)) {
|
|
7561
|
+
return reply.prompt;
|
|
7562
|
+
}
|
|
7563
|
+
const label = (reply.label || this.describeResourceContext(contextHints) || reply.prompt).trim();
|
|
7564
|
+
const resourcePath = String(contextHints?.['resourcePath'] || '').trim();
|
|
7565
|
+
return this.tx('agentic.quickReplies.resourceSelectionPrompt', 'Use {label} ({resourcePath}) as the data source.')
|
|
7566
|
+
.replace('{label}', label)
|
|
7567
|
+
.replace('{resourcePath}', resourcePath);
|
|
7568
|
+
}
|
|
7569
|
+
isResourceQuickReply(reply, contextHints) {
|
|
7570
|
+
return reply.kind === 'suggestion'
|
|
7571
|
+
&& typeof contextHints?.['resourcePath'] === 'string'
|
|
7572
|
+
&& !!String(contextHints['resourcePath']).trim();
|
|
7573
|
+
}
|
|
7574
|
+
describeResourceContext(contextHints) {
|
|
7575
|
+
const resourcePath = typeof contextHints?.['resourcePath'] === 'string'
|
|
7576
|
+
? contextHints['resourcePath'].trim()
|
|
7577
|
+
: '';
|
|
7578
|
+
if (!resourcePath)
|
|
7579
|
+
return '';
|
|
7580
|
+
const segment = resourcePath.split('/').filter((part) => !!part).pop() || resourcePath;
|
|
7581
|
+
return segment
|
|
7582
|
+
.replace(/[-_]+/g, ' ')
|
|
7583
|
+
.replace(/\s+/g, ' ')
|
|
7584
|
+
.trim();
|
|
6766
7585
|
}
|
|
6767
7586
|
attachAgenticContext() {
|
|
6768
7587
|
const controller = this.ensureAgenticTurnController();
|
|
@@ -6807,8 +7626,7 @@ class DynamicPageBuilderComponent {
|
|
|
6807
7626
|
this.agenticAuthoringError.set('');
|
|
6808
7627
|
this.agenticAuthoringPreviewResult.set(null);
|
|
6809
7628
|
try {
|
|
6810
|
-
|
|
6811
|
-
this.applyAgenticTurnState(state);
|
|
7629
|
+
await this.consumeAgenticTurn(this.ensureAgenticTurnController().resendMessage(message.id));
|
|
6812
7630
|
}
|
|
6813
7631
|
catch (error) {
|
|
6814
7632
|
this.agenticAuthoringStatus.set('');
|
|
@@ -6877,6 +7695,8 @@ class DynamicPageBuilderComponent {
|
|
|
6877
7695
|
provider: () => this.agenticAuthoringProvider,
|
|
6878
7696
|
model: () => this.agenticAuthoringModel,
|
|
6879
7697
|
apiKey: () => this.agenticAuthoringApiKey,
|
|
7698
|
+
enableTurnStream: () => this.agenticAuthoringEnableStreaming,
|
|
7699
|
+
includeLlmDiagnostics: () => this.agenticAuthoringIncludeLlmDiagnostics,
|
|
6880
7700
|
loadComponentCapabilities: () => this.loadAgenticComponentCapabilities(),
|
|
6881
7701
|
applyLocalPreview: (result) => this.applyAgenticPreviewLocally(result),
|
|
6882
7702
|
describePreviewFailure: (result) => this.describeAgenticPreviewFailure(result),
|
|
@@ -6898,20 +7718,55 @@ class DynamicPageBuilderComponent {
|
|
|
6898
7718
|
}
|
|
6899
7719
|
async applyAgenticPreviewLocally(result) {
|
|
6900
7720
|
const adapter = new PageBuilderAiAdapter(this.createAdapterHost(), this.componentMetadata);
|
|
6901
|
-
|
|
7721
|
+
const applied = await (result.uiCompositionPlan
|
|
6902
7722
|
? adapter.applyUiCompositionPlan(result.uiCompositionPlan)
|
|
6903
|
-
: adapter.applyCompiledFormPatch(result.compiledFormPatch);
|
|
7723
|
+
: adapter.applyCompiledFormPatch(result.compiledFormPatch));
|
|
7724
|
+
if (!applied.success && this.isAgenticTableContractError(applied.error)) {
|
|
7725
|
+
return {
|
|
7726
|
+
...applied,
|
|
7727
|
+
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.'),
|
|
7728
|
+
};
|
|
7729
|
+
}
|
|
7730
|
+
return applied;
|
|
6904
7731
|
}
|
|
6905
7732
|
applyAgenticTurnState(state) {
|
|
7733
|
+
const preview = state.preview ?? null;
|
|
6906
7734
|
this.agenticAuthoringConversation.set(state.messages);
|
|
6907
|
-
this.agenticAuthoringQuickReplies.set(state.quickReplies);
|
|
7735
|
+
this.agenticAuthoringQuickReplies.set(preview?.valid ? [] : state.quickReplies);
|
|
6908
7736
|
this.agenticAuthoringStatus.set(state.statusText);
|
|
6909
7737
|
this.agenticAuthoringError.set(state.errorText);
|
|
6910
|
-
this.agenticAuthoringPreviewResult.set(
|
|
7738
|
+
this.agenticAuthoringPreviewResult.set(preview);
|
|
6911
7739
|
this.agenticAuthoringAttachments.set(state.attachments);
|
|
7740
|
+
this.agenticAuthoringLlmDiagnostics.set(this.resolveAgenticLlmDiagnostics(state.diagnostics));
|
|
7741
|
+
}
|
|
7742
|
+
consumeAgenticTurn(states$) {
|
|
7743
|
+
return lastValueFrom(states$.pipe(tap((state) => this.applyAgenticTurnState(state))));
|
|
7744
|
+
}
|
|
7745
|
+
agenticAuthoringDiagnosticsTop() {
|
|
7746
|
+
return Math.max(16, this.agenticAuthoringPanelLayout().top);
|
|
7747
|
+
}
|
|
7748
|
+
agenticAuthoringLlmDiagnosticsText() {
|
|
7749
|
+
const diagnostics = this.agenticAuthoringLlmDiagnostics();
|
|
7750
|
+
if (!this.agenticAuthoringIncludeLlmDiagnostics || !diagnostics) {
|
|
7751
|
+
return '';
|
|
7752
|
+
}
|
|
7753
|
+
return JSON.stringify(diagnostics, null, 2);
|
|
7754
|
+
}
|
|
7755
|
+
resolveAgenticLlmDiagnostics(diagnostics) {
|
|
7756
|
+
if (!this.agenticAuthoringIncludeLlmDiagnostics) {
|
|
7757
|
+
return null;
|
|
7758
|
+
}
|
|
7759
|
+
const intentResolution = this.toRecord(diagnostics?.['intentResolution']);
|
|
7760
|
+
return this.toRecord(intentResolution?.['llmDiagnostics']);
|
|
7761
|
+
}
|
|
7762
|
+
toRecord(value) {
|
|
7763
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
7764
|
+
return null;
|
|
7765
|
+
}
|
|
7766
|
+
return value;
|
|
6912
7767
|
}
|
|
6913
7768
|
resolvePreviewCompiledFormPatch(preview) {
|
|
6914
|
-
if (preview.uiCompositionPlan) {
|
|
7769
|
+
if (preview.uiCompositionPlan || preview.compiledFormPatch?.patch?.page) {
|
|
6915
7770
|
return {
|
|
6916
7771
|
...preview.compiledFormPatch,
|
|
6917
7772
|
patch: {
|
|
@@ -6920,9 +7775,6 @@ class DynamicPageBuilderComponent {
|
|
|
6920
7775
|
},
|
|
6921
7776
|
};
|
|
6922
7777
|
}
|
|
6923
|
-
if (preview.compiledFormPatch?.patch?.page) {
|
|
6924
|
-
return preview.compiledFormPatch;
|
|
6925
|
-
}
|
|
6926
7778
|
return preview.compiledFormPatch;
|
|
6927
7779
|
}
|
|
6928
7780
|
focusCanvasWidget(widgetKey) {
|
|
@@ -6994,6 +7846,12 @@ class DynamicPageBuilderComponent {
|
|
|
6994
7846
|
}
|
|
6995
7847
|
}
|
|
6996
7848
|
describeAgenticPreviewStatus(result) {
|
|
7849
|
+
const assistantMessage = typeof result.assistantMessage === 'string'
|
|
7850
|
+
? result.assistantMessage.trim()
|
|
7851
|
+
: '';
|
|
7852
|
+
if (assistantMessage) {
|
|
7853
|
+
return assistantMessage;
|
|
7854
|
+
}
|
|
6997
7855
|
switch (result.diagnostics?.fieldScopeDecision) {
|
|
6998
7856
|
case 'accepted-add-local-field':
|
|
6999
7857
|
return this.tx('agentic.status.acceptedAddLocalField', 'Local field added to the form.');
|
|
@@ -7005,6 +7863,11 @@ class DynamicPageBuilderComponent {
|
|
|
7005
7863
|
return this.tx('agentic.status.previewReady', 'Preview applied to the page.');
|
|
7006
7864
|
}
|
|
7007
7865
|
}
|
|
7866
|
+
isAgenticTableContractError(error) {
|
|
7867
|
+
const message = typeof error === 'string' ? error.toLowerCase() : '';
|
|
7868
|
+
return message.includes('praxis-table')
|
|
7869
|
+
&& (message.includes('unknown input') || message.includes('unknown inputs') || message.includes('unsupported'));
|
|
7870
|
+
}
|
|
7008
7871
|
appendAgenticMessage(role, text) {
|
|
7009
7872
|
const normalized = text.trim();
|
|
7010
7873
|
if (!normalized)
|
|
@@ -7043,7 +7906,7 @@ class DynamicPageBuilderComponent {
|
|
|
7043
7906
|
return resolvePraxisPageBuilderText(this.i18n, key, fallback);
|
|
7044
7907
|
}
|
|
7045
7908
|
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: [
|
|
7909
|
+
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
7910
|
providePraxisPageBuilderI18n(),
|
|
7048
7911
|
{ provide: DYNAMIC_PAGE_SHELL_EDITOR, useValue: WidgetShellEditorComponent },
|
|
7049
7912
|
], viewQueries: [{ propertyName: "runtime", first: true, predicate: ["runtime"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
@@ -7107,6 +7970,26 @@ class DynamicPageBuilderComponent {
|
|
|
7107
7970
|
(close)="toggleAgenticAuthoring()"
|
|
7108
7971
|
/>
|
|
7109
7972
|
|
|
7973
|
+
<section
|
|
7974
|
+
*ngIf="agenticAuthoringIncludeLlmDiagnostics && agenticAuthoringLlmDiagnosticsText()"
|
|
7975
|
+
class="agentic-diagnostics-panel"
|
|
7976
|
+
data-testid="page-builder-agentic-llm-diagnostics"
|
|
7977
|
+
role="region"
|
|
7978
|
+
[attr.aria-label]="tx('agentic.diagnostics.title', 'LLM diagnostics')"
|
|
7979
|
+
[style.top.px]="agenticAuthoringDiagnosticsTop()"
|
|
7980
|
+
>
|
|
7981
|
+
<header class="agentic-diagnostics-panel__header">
|
|
7982
|
+
<span>{{ tx('agentic.diagnostics.title', 'LLM diagnostics') }}</span>
|
|
7983
|
+
<span class="agentic-diagnostics-panel__badge">
|
|
7984
|
+
{{ tx('agentic.diagnostics.badge', 'Debug') }}
|
|
7985
|
+
</span>
|
|
7986
|
+
</header>
|
|
7987
|
+
<p class="agentic-diagnostics-panel__description">
|
|
7988
|
+
{{ tx('agentic.diagnostics.description', 'Prompt, context bundle, and tool catalog returned by the backend for this turn.') }}
|
|
7989
|
+
</p>
|
|
7990
|
+
<pre>{{ agenticAuthoringLlmDiagnosticsText() }}</pre>
|
|
7991
|
+
</section>
|
|
7992
|
+
|
|
7110
7993
|
<praxis-floating-toolbar
|
|
7111
7994
|
[visible]="showSettings()"
|
|
7112
7995
|
[canUndo]="false"
|
|
@@ -7141,7 +8024,7 @@ class DynamicPageBuilderComponent {
|
|
|
7141
8024
|
</button>
|
|
7142
8025
|
</praxis-floating-toolbar>
|
|
7143
8026
|
</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"] }] });
|
|
8027
|
+
`, 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
8028
|
}
|
|
7146
8029
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicPageBuilderComponent, decorators: [{
|
|
7147
8030
|
type: Component,
|
|
@@ -7219,6 +8102,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7219
8102
|
(close)="toggleAgenticAuthoring()"
|
|
7220
8103
|
/>
|
|
7221
8104
|
|
|
8105
|
+
<section
|
|
8106
|
+
*ngIf="agenticAuthoringIncludeLlmDiagnostics && agenticAuthoringLlmDiagnosticsText()"
|
|
8107
|
+
class="agentic-diagnostics-panel"
|
|
8108
|
+
data-testid="page-builder-agentic-llm-diagnostics"
|
|
8109
|
+
role="region"
|
|
8110
|
+
[attr.aria-label]="tx('agentic.diagnostics.title', 'LLM diagnostics')"
|
|
8111
|
+
[style.top.px]="agenticAuthoringDiagnosticsTop()"
|
|
8112
|
+
>
|
|
8113
|
+
<header class="agentic-diagnostics-panel__header">
|
|
8114
|
+
<span>{{ tx('agentic.diagnostics.title', 'LLM diagnostics') }}</span>
|
|
8115
|
+
<span class="agentic-diagnostics-panel__badge">
|
|
8116
|
+
{{ tx('agentic.diagnostics.badge', 'Debug') }}
|
|
8117
|
+
</span>
|
|
8118
|
+
</header>
|
|
8119
|
+
<p class="agentic-diagnostics-panel__description">
|
|
8120
|
+
{{ tx('agentic.diagnostics.description', 'Prompt, context bundle, and tool catalog returned by the backend for this turn.') }}
|
|
8121
|
+
</p>
|
|
8122
|
+
<pre>{{ agenticAuthoringLlmDiagnosticsText() }}</pre>
|
|
8123
|
+
</section>
|
|
8124
|
+
|
|
7222
8125
|
<praxis-floating-toolbar
|
|
7223
8126
|
[visible]="showSettings()"
|
|
7224
8127
|
[canUndo]="false"
|
|
@@ -7253,7 +8156,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7253
8156
|
</button>
|
|
7254
8157
|
</praxis-floating-toolbar>
|
|
7255
8158
|
</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"] }]
|
|
8159
|
+
`, 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
8160
|
}], ctorParameters: () => [{ type: i1.MatDialog }, { type: undefined, decorators: [{
|
|
7258
8161
|
type: Optional
|
|
7259
8162
|
}, {
|
|
@@ -7294,6 +8197,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7294
8197
|
type: Input
|
|
7295
8198
|
}], agenticAuthoringEtag: [{
|
|
7296
8199
|
type: Input
|
|
8200
|
+
}], agenticAuthoringIncludeLlmDiagnostics: [{
|
|
8201
|
+
type: Input
|
|
8202
|
+
}], agenticAuthoringEnableStreaming: [{
|
|
8203
|
+
type: Input
|
|
7297
8204
|
}], pageChange: [{
|
|
7298
8205
|
type: Output
|
|
7299
8206
|
}], agenticAuthoringApplied: [{
|