@praxisui/page-builder 9.0.0-beta.73 → 9.0.0-beta.74
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 +2 -0
- package/ai/component-registry.json +10 -4
- package/fesm2022/praxisui-page-builder.mjs +1099 -293
- package/package.json +4 -4
- package/types/praxisui-page-builder.d.ts +16 -5
|
@@ -8,7 +8,6 @@ import * as i2$1 from '@angular/material/icon';
|
|
|
8
8
|
import { MatIconModule } from '@angular/material/icon';
|
|
9
9
|
import * as i2$2 from '@praxisui/core';
|
|
10
10
|
import { PraxisIconDirective, BUILTIN_SHELL_PRESETS, providePraxisI18n, PraxisI18nService, GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, deepMerge, generateId, ComponentMetadataRegistry, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
|
|
11
|
-
export { WidgetShellComponent } from '@praxisui/core';
|
|
12
11
|
import * as i3 from '@angular/material/tooltip';
|
|
13
12
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
14
13
|
import * as i3$1 from '@angular/material/form-field';
|
|
@@ -2609,6 +2608,8 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2609
2608
|
'praxis.pageBuilder.connections.validation.nestedPathTerminalMissing': 'Nested path must terminate in a stable widget key.',
|
|
2610
2609
|
'praxis.pageBuilder.connections.validation.nestedPathNotDeclared': 'Nested path was not found in the current widget structure.',
|
|
2611
2610
|
'praxis.pageBuilder.connections.validation.statePathNotDeclared': 'State path is not declared in page state values.',
|
|
2611
|
+
'praxis.pageBuilder.connections.validation.feedbackCycleUnguarded': 'Connection participates in an unguarded feedback cycle.',
|
|
2612
|
+
'praxis.pageBuilder.connections.validation.feedbackCycleGuarded': 'Connection participates in an intentional guarded feedback cycle.',
|
|
2612
2613
|
'praxis.pageBuilder.connections.suggestions.title': 'Smart suggestions',
|
|
2613
2614
|
'praxis.pageBuilder.connections.suggestions.recommendedTitle': 'Recommended connection',
|
|
2614
2615
|
'praxis.pageBuilder.connections.suggestions.quickConnect': 'Create connection',
|
|
@@ -2731,6 +2732,15 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2731
2732
|
'praxis.pageBuilder.agentic.emptyConversation.existingPage': 'Tell me what you want to improve on this screen. I will read what already exists and prepare a preview for you to review before saving.',
|
|
2732
2733
|
'praxis.pageBuilder.agentic.conversationAria': 'AI conversation',
|
|
2733
2734
|
'praxis.pageBuilder.agentic.quickRepliesAria': 'Quick replies',
|
|
2735
|
+
'praxis.pageBuilder.agentic.recommendedIntentsAria': 'Initial assistant suggestions',
|
|
2736
|
+
'praxis.pageBuilder.agentic.recommendedIntentsTitle': 'You can start here',
|
|
2737
|
+
'praxis.pageBuilder.agentic.recommendedIntentCta': 'Use suggestion',
|
|
2738
|
+
'praxis.pageBuilder.agentic.recommendedIntentGuidanceCta': 'Explore possibilities',
|
|
2739
|
+
'praxis.pageBuilder.agentic.recommendedIntentRequiresConfirmation': 'Requires review',
|
|
2740
|
+
'praxis.pageBuilder.agentic.recommended.platformGuide.group': 'Explore the platform',
|
|
2741
|
+
'praxis.pageBuilder.agentic.recommended.platformGuide.label': 'Discover what I can do',
|
|
2742
|
+
'praxis.pageBuilder.agentic.recommended.platformGuide.description': 'Learn how Praxis can help you create pages, forms, tables, and dashboards.',
|
|
2743
|
+
'praxis.pageBuilder.agentic.recommended.platformGuide.prompt': 'What can I do here?',
|
|
2734
2744
|
'praxis.pageBuilder.agentic.contextAria': 'Active context',
|
|
2735
2745
|
'praxis.pageBuilder.agentic.attachmentsAria': 'Attached context',
|
|
2736
2746
|
'praxis.pageBuilder.agentic.context.component': 'Component',
|
|
@@ -2738,6 +2748,9 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2738
2748
|
'praxis.pageBuilder.agentic.context.route': 'Route',
|
|
2739
2749
|
'praxis.pageBuilder.agentic.context.selection': 'Selection',
|
|
2740
2750
|
'praxis.pageBuilder.agentic.context.governedDomainContext': 'Governed context',
|
|
2751
|
+
'praxis.pageBuilder.agentic.context.governedDomainContextResolved': 'Available',
|
|
2752
|
+
'praxis.pageBuilder.agentic.context.governedDomainContextRequestedUnavailable': 'Requested, but unavailable',
|
|
2753
|
+
'praxis.pageBuilder.agentic.context.governedDomainContextNotRequested': 'Not requested',
|
|
2741
2754
|
'praxis.pageBuilder.agentic.context.governedDomainContextUnknown': 'unknown',
|
|
2742
2755
|
'praxis.pageBuilder.agentic.context.governedDomainContextPolicy': 'Policy profile',
|
|
2743
2756
|
'praxis.pageBuilder.agentic.context.governedDomainContextSource': 'Context source',
|
|
@@ -2780,8 +2793,6 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2780
2793
|
'praxis.pageBuilder.agentic.state.error': 'Error',
|
|
2781
2794
|
'praxis.pageBuilder.agentic.quickReplies.confirm': 'Yes, create it',
|
|
2782
2795
|
'praxis.pageBuilder.agentic.quickReplies.revise': 'I want to adjust',
|
|
2783
|
-
'praxis.pageBuilder.agentic.governedReview.quickReply.revise': 'Review pending points',
|
|
2784
|
-
'praxis.pageBuilder.agentic.governedReview.prompt.revise': 'Review the blocked preview, explain which points still need a decision, and propose a safe adjustment before saving.',
|
|
2785
2796
|
'praxis.pageBuilder.agentic.quickReplies.cancel': 'Cancel',
|
|
2786
2797
|
'praxis.pageBuilder.agentic.quickReplies.payrollExecutiveDashboard': 'Executive dashboard',
|
|
2787
2798
|
'praxis.pageBuilder.agentic.quickReplies.payrollDepartmentDrilldown': 'Category drill-down',
|
|
@@ -2822,7 +2833,7 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2822
2833
|
'praxis.pageBuilder.agentic.resizeHandleTooltip': 'Resize',
|
|
2823
2834
|
'praxis.pageBuilder.agentic.submit': 'Send request',
|
|
2824
2835
|
'praxis.pageBuilder.agentic.preview': 'Generate preview',
|
|
2825
|
-
'praxis.pageBuilder.agentic.persist': '
|
|
2836
|
+
'praxis.pageBuilder.agentic.persist': 'Apply and save',
|
|
2826
2837
|
'praxis.pageBuilder.pageLifecycle.restart': 'Restart page',
|
|
2827
2838
|
'praxis.pageBuilder.pageLifecycle.restartAria': 'Restart current page locally',
|
|
2828
2839
|
'praxis.pageBuilder.pageLifecycle.deleteSaved': 'Delete saved page',
|
|
@@ -2841,7 +2852,7 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2841
2852
|
'praxis.pageBuilder.agentic.status.previewCompile': 'Compiling preview...',
|
|
2842
2853
|
'praxis.pageBuilder.agentic.status.previewReady': 'Preview applied to the page.',
|
|
2843
2854
|
'praxis.pageBuilder.agentic.status.reviewReady': 'Ready for review.',
|
|
2844
|
-
'praxis.pageBuilder.agentic.status.reviewNeedsAttention': '
|
|
2855
|
+
'praxis.pageBuilder.agentic.status.reviewNeedsAttention': 'Complete this review in the assistant before saving.',
|
|
2845
2856
|
'praxis.pageBuilder.agentic.status.acceptedAddLocalField': 'Local field added to the form.',
|
|
2846
2857
|
'praxis.pageBuilder.agentic.status.acceptedRemoveLocalField': 'Local field removed from the form.',
|
|
2847
2858
|
'praxis.pageBuilder.agentic.status.acceptedRelabelField': 'Field label updated.',
|
|
@@ -2886,6 +2897,7 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2886
2897
|
'praxis.pageBuilder.agentic.errors.applyLocal': 'Preview could not be applied.',
|
|
2887
2898
|
'praxis.pageBuilder.agentic.errors.componentId': 'Configure a component id before saving.',
|
|
2888
2899
|
'praxis.pageBuilder.agentic.errors.invalidPreview': 'Generated preview is invalid.',
|
|
2900
|
+
'praxis.pageBuilder.agentic.errors.invalidPreviewWithDetails': 'I could not build a safe preview. Review the request or choose another data source.',
|
|
2889
2901
|
'praxis.pageBuilder.agentic.errors.intentResolution': 'Intent could not be resolved.',
|
|
2890
2902
|
'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.',
|
|
2891
2903
|
'praxis.pageBuilder.agentic.errors.streamProcessing': 'The assistant could not finish this authoring request. Try again or ask support to review the diagnostics.',
|
|
@@ -2893,6 +2905,13 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
|
|
|
2893
2905
|
'praxis.pageBuilder.agentic.errors.duplicateField': 'This field already exists in the selected form.',
|
|
2894
2906
|
'praxis.pageBuilder.agentic.errors.nonLocalFieldRemoval': 'Only local fields created by AI can be removed in this flow.',
|
|
2895
2907
|
'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.',
|
|
2908
|
+
'praxis.pageBuilder.agentic.errors.componentContextUnavailable': 'I could not load the authoring context for the selected component. Refresh the page or reopen the assistant from that component.',
|
|
2909
|
+
'praxis.pageBuilder.agentic.errors.componentSelectionChanged': 'The selected component changed or no longer matches the request. Select the intended component again and reopen the assistant.',
|
|
2910
|
+
'praxis.pageBuilder.agentic.errors.componentTargetUnavailable': 'The requested target is not available for this interaction. Review the page connections or choose another target component.',
|
|
2911
|
+
'praxis.pageBuilder.agentic.errors.componentFieldUnavailable': 'One or more requested fields are not available in this component data source. Review the source or choose fields published in the catalog.',
|
|
2912
|
+
'praxis.pageBuilder.agentic.errors.componentServiceUnavailable': 'The component authoring service is currently unavailable. Try again.',
|
|
2913
|
+
'praxis.pageBuilder.agentic.errors.componentManifestUnavailable': 'This component does not provide an AI authoring manifest compatible with the requested change.',
|
|
2914
|
+
'praxis.pageBuilder.agentic.errors.componentPlanInvalid': 'The proposed change did not pass the component validations. Review the request and try again.',
|
|
2896
2915
|
'praxis.pageBuilder.agentic.errors.generic': 'AI authoring failed.',
|
|
2897
2916
|
};
|
|
2898
2917
|
|
|
@@ -3179,6 +3198,8 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
3179
3198
|
'praxis.pageBuilder.connections.validation.nestedPathTerminalMissing': 'O nestedPath deve terminar em uma chave estável de widget.',
|
|
3180
3199
|
'praxis.pageBuilder.connections.validation.nestedPathNotDeclared': 'O nestedPath não foi encontrado na estrutura atual do widget.',
|
|
3181
3200
|
'praxis.pageBuilder.connections.validation.statePathNotDeclared': 'O caminho de estado não está declarado nos valores de estado da página.',
|
|
3201
|
+
'praxis.pageBuilder.connections.validation.feedbackCycleUnguarded': 'A conexão participa de um ciclo de feedback sem guarda.',
|
|
3202
|
+
'praxis.pageBuilder.connections.validation.feedbackCycleGuarded': 'A conexão participa de um ciclo de feedback intencional e guardado.',
|
|
3182
3203
|
'praxis.pageBuilder.connections.suggestions.title': 'Sugestões inteligentes',
|
|
3183
3204
|
'praxis.pageBuilder.connections.suggestions.recommendedTitle': 'Conexão recomendada',
|
|
3184
3205
|
'praxis.pageBuilder.connections.suggestions.quickConnect': 'Criar conexão',
|
|
@@ -3301,6 +3322,15 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
3301
3322
|
'praxis.pageBuilder.agentic.emptyConversation.existingPage': 'Diga o que você quer melhorar nesta tela. Eu leio o que já existe e preparo uma prévia para você revisar antes de salvar.',
|
|
3302
3323
|
'praxis.pageBuilder.agentic.conversationAria': 'Conversa com IA',
|
|
3303
3324
|
'praxis.pageBuilder.agentic.quickRepliesAria': 'Respostas rápidas',
|
|
3325
|
+
'praxis.pageBuilder.agentic.recommendedIntentsAria': 'Sugestões iniciais do assistente',
|
|
3326
|
+
'praxis.pageBuilder.agentic.recommendedIntentsTitle': 'Você pode começar por aqui',
|
|
3327
|
+
'praxis.pageBuilder.agentic.recommendedIntentCta': 'Usar sugestão',
|
|
3328
|
+
'praxis.pageBuilder.agentic.recommendedIntentGuidanceCta': 'Entender possibilidades',
|
|
3329
|
+
'praxis.pageBuilder.agentic.recommendedIntentRequiresConfirmation': 'Requer revisão',
|
|
3330
|
+
'praxis.pageBuilder.agentic.recommended.platformGuide.group': 'Explorar a plataforma',
|
|
3331
|
+
'praxis.pageBuilder.agentic.recommended.platformGuide.label': 'Descobrir o que posso fazer',
|
|
3332
|
+
'praxis.pageBuilder.agentic.recommended.platformGuide.description': 'Conheça as possibilidades do Praxis para criar páginas, formulários, tabelas e painéis.',
|
|
3333
|
+
'praxis.pageBuilder.agentic.recommended.platformGuide.prompt': 'O que posso fazer aqui?',
|
|
3304
3334
|
'praxis.pageBuilder.agentic.contextAria': 'Contexto ativo',
|
|
3305
3335
|
'praxis.pageBuilder.agentic.attachmentsAria': 'Contexto anexado',
|
|
3306
3336
|
'praxis.pageBuilder.agentic.context.component': 'Componente',
|
|
@@ -3308,6 +3338,9 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
3308
3338
|
'praxis.pageBuilder.agentic.context.route': 'Rota',
|
|
3309
3339
|
'praxis.pageBuilder.agentic.context.selection': 'Seleção',
|
|
3310
3340
|
'praxis.pageBuilder.agentic.context.governedDomainContext': 'Contexto governado',
|
|
3341
|
+
'praxis.pageBuilder.agentic.context.governedDomainContextResolved': 'Disponível',
|
|
3342
|
+
'praxis.pageBuilder.agentic.context.governedDomainContextRequestedUnavailable': 'Solicitado, mas indisponível',
|
|
3343
|
+
'praxis.pageBuilder.agentic.context.governedDomainContextNotRequested': 'Não solicitado',
|
|
3311
3344
|
'praxis.pageBuilder.agentic.context.governedDomainContextUnknown': 'desconhecido',
|
|
3312
3345
|
'praxis.pageBuilder.agentic.context.governedDomainContextPolicy': 'Perfil de política',
|
|
3313
3346
|
'praxis.pageBuilder.agentic.context.governedDomainContextSource': 'Fonte do contexto',
|
|
@@ -3350,8 +3383,6 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
3350
3383
|
'praxis.pageBuilder.agentic.state.error': 'Erro',
|
|
3351
3384
|
'praxis.pageBuilder.agentic.quickReplies.confirm': 'Sim, criar',
|
|
3352
3385
|
'praxis.pageBuilder.agentic.quickReplies.revise': 'Quero ajustar',
|
|
3353
|
-
'praxis.pageBuilder.agentic.governedReview.quickReply.revise': 'Revisar pontos pendentes',
|
|
3354
|
-
'praxis.pageBuilder.agentic.governedReview.prompt.revise': 'Revise a prévia bloqueada, explique os pontos que ainda precisam de decisão e proponha um ajuste seguro antes de salvar.',
|
|
3355
3386
|
'praxis.pageBuilder.agentic.quickReplies.cancel': 'Cancelar',
|
|
3356
3387
|
'praxis.pageBuilder.agentic.quickReplies.payrollExecutiveDashboard': 'Dashboard executivo',
|
|
3357
3388
|
'praxis.pageBuilder.agentic.quickReplies.payrollDepartmentDrilldown': 'Drill-down por categoria',
|
|
@@ -3392,7 +3423,7 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
3392
3423
|
'praxis.pageBuilder.agentic.resizeHandleTooltip': 'Redimensionar',
|
|
3393
3424
|
'praxis.pageBuilder.agentic.submit': 'Enviar pedido',
|
|
3394
3425
|
'praxis.pageBuilder.agentic.preview': 'Gerar prévia',
|
|
3395
|
-
'praxis.pageBuilder.agentic.persist': '
|
|
3426
|
+
'praxis.pageBuilder.agentic.persist': 'Aplicar e salvar',
|
|
3396
3427
|
'praxis.pageBuilder.pageLifecycle.restart': 'Recomeçar tela',
|
|
3397
3428
|
'praxis.pageBuilder.pageLifecycle.restartAria': 'Recomeçar a tela atual apenas localmente',
|
|
3398
3429
|
'praxis.pageBuilder.pageLifecycle.deleteSaved': 'Excluir página salva',
|
|
@@ -3411,7 +3442,7 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
3411
3442
|
'praxis.pageBuilder.agentic.status.previewCompile': 'Compilando pré-visualização...',
|
|
3412
3443
|
'praxis.pageBuilder.agentic.status.previewReady': 'Pré-visualização aplicada à página.',
|
|
3413
3444
|
'praxis.pageBuilder.agentic.status.reviewReady': 'Pronto para revisar.',
|
|
3414
|
-
'praxis.pageBuilder.agentic.status.reviewNeedsAttention': '
|
|
3445
|
+
'praxis.pageBuilder.agentic.status.reviewNeedsAttention': 'Conclua esta revisão no assistente antes de salvar.',
|
|
3415
3446
|
'praxis.pageBuilder.agentic.status.acceptedAddLocalField': 'Campo local adicionado ao formulário.',
|
|
3416
3447
|
'praxis.pageBuilder.agentic.status.acceptedRemoveLocalField': 'Campo local removido do formulário.',
|
|
3417
3448
|
'praxis.pageBuilder.agentic.status.acceptedRelabelField': 'Rótulo do campo atualizado.',
|
|
@@ -3456,6 +3487,7 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
3456
3487
|
'praxis.pageBuilder.agentic.errors.applyLocal': 'A pré-visualização não pôde ser aplicada.',
|
|
3457
3488
|
'praxis.pageBuilder.agentic.errors.componentId': 'Configure um id de componente antes de salvar.',
|
|
3458
3489
|
'praxis.pageBuilder.agentic.errors.invalidPreview': 'A pré-visualização gerada é inválida.',
|
|
3490
|
+
'praxis.pageBuilder.agentic.errors.invalidPreviewWithDetails': 'Não consegui montar uma prévia segura. Revise o pedido ou escolha outra fonte de dados.',
|
|
3459
3491
|
'praxis.pageBuilder.agentic.errors.intentResolution': 'A intenção não pôde ser resolvida.',
|
|
3460
3492
|
'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.',
|
|
3461
3493
|
'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.',
|
|
@@ -3463,6 +3495,13 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
|
|
|
3463
3495
|
'praxis.pageBuilder.agentic.errors.duplicateField': 'Este campo já existe no formulário selecionado.',
|
|
3464
3496
|
'praxis.pageBuilder.agentic.errors.nonLocalFieldRemoval': 'Somente campos locais criados pela IA podem ser removidos neste fluxo.',
|
|
3465
3497
|
'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.',
|
|
3498
|
+
'praxis.pageBuilder.agentic.errors.componentContextUnavailable': 'Não consegui carregar o contexto de autoria do componente selecionado. Atualize a página ou reabra o assistente a partir desse componente.',
|
|
3499
|
+
'praxis.pageBuilder.agentic.errors.componentSelectionChanged': 'O componente selecionado mudou ou não corresponde mais ao pedido. Selecione novamente o componente desejado e reabra o assistente.',
|
|
3500
|
+
'praxis.pageBuilder.agentic.errors.componentTargetUnavailable': 'O destino solicitado não está disponível para esta interação. Revise as conexões da página ou escolha outro componente de destino.',
|
|
3501
|
+
'praxis.pageBuilder.agentic.errors.componentFieldUnavailable': 'Um ou mais campos solicitados não estão disponíveis na fonte de dados deste componente. Revise a fonte ou escolha campos publicados no catálogo.',
|
|
3502
|
+
'praxis.pageBuilder.agentic.errors.componentServiceUnavailable': 'O serviço de autoria do componente está indisponível no momento. Tente novamente.',
|
|
3503
|
+
'praxis.pageBuilder.agentic.errors.componentManifestUnavailable': 'Este componente não disponibiliza um manifesto de autoria por IA compatível com a alteração solicitada.',
|
|
3504
|
+
'praxis.pageBuilder.agentic.errors.componentPlanInvalid': 'A alteração proposta não passou pelas validações do componente. Revise o pedido e tente novamente.',
|
|
3466
3505
|
'praxis.pageBuilder.agentic.errors.generic': 'Falha na autoria com IA.',
|
|
3467
3506
|
};
|
|
3468
3507
|
|
|
@@ -3620,6 +3659,10 @@ function deriveConnectionEditorGraph(page, options = {}) {
|
|
|
3620
3659
|
diagnostics,
|
|
3621
3660
|
};
|
|
3622
3661
|
});
|
|
3662
|
+
const feedbackDiagnostics = feedbackDiagnosticsForLinks(links);
|
|
3663
|
+
for (const connection of connections) {
|
|
3664
|
+
connection.diagnostics.push(...(feedbackDiagnostics.get(connection.id) ?? []));
|
|
3665
|
+
}
|
|
3623
3666
|
return {
|
|
3624
3667
|
nodes: allNodes,
|
|
3625
3668
|
connections,
|
|
@@ -3629,6 +3672,85 @@ function deriveConnectionEditorGraph(page, options = {}) {
|
|
|
3629
3672
|
diagnostics: connections.flatMap((connection) => connection.diagnostics),
|
|
3630
3673
|
};
|
|
3631
3674
|
}
|
|
3675
|
+
function feedbackDiagnosticsForLinks(links) {
|
|
3676
|
+
const edges = links.map((link) => ({
|
|
3677
|
+
link,
|
|
3678
|
+
from: feedbackNodeKey(link.from),
|
|
3679
|
+
to: feedbackNodeKey(link.to),
|
|
3680
|
+
}));
|
|
3681
|
+
const adjacency = new Map();
|
|
3682
|
+
const diagnostics = new Map();
|
|
3683
|
+
const seenCycles = new Set();
|
|
3684
|
+
for (const edge of edges) {
|
|
3685
|
+
const current = adjacency.get(edge.from) ?? [];
|
|
3686
|
+
current.push(edge);
|
|
3687
|
+
adjacency.set(edge.from, current);
|
|
3688
|
+
}
|
|
3689
|
+
for (const edge of edges) {
|
|
3690
|
+
const path = findFeedbackPath(edge.to, edge.from, adjacency, [edge.link.id]);
|
|
3691
|
+
if (!path) {
|
|
3692
|
+
continue;
|
|
3693
|
+
}
|
|
3694
|
+
const cycleLinkIds = Array.from(new Set(path)).sort((left, right) => left.localeCompare(right));
|
|
3695
|
+
const cycleKey = cycleLinkIds.join('|');
|
|
3696
|
+
if (seenCycles.has(cycleKey)) {
|
|
3697
|
+
continue;
|
|
3698
|
+
}
|
|
3699
|
+
seenCycles.add(cycleKey);
|
|
3700
|
+
const cycleLinks = cycleLinkIds
|
|
3701
|
+
.map((id) => links.find((link) => link.id === id))
|
|
3702
|
+
.filter((link) => !!link);
|
|
3703
|
+
const guarded = cycleLinks.every(isIntentionalGuardedFeedback);
|
|
3704
|
+
const diagnostic = {
|
|
3705
|
+
code: guarded ? 'feedback-cycle-guarded' : 'feedback-cycle-unguarded',
|
|
3706
|
+
severity: guarded ? 'warning' : 'error',
|
|
3707
|
+
endpoint: 'link',
|
|
3708
|
+
relatedLinkIds: cycleLinkIds,
|
|
3709
|
+
};
|
|
3710
|
+
for (const linkId of cycleLinkIds) {
|
|
3711
|
+
const current = diagnostics.get(linkId) ?? [];
|
|
3712
|
+
current.push({ ...diagnostic, relatedLinkIds: [...cycleLinkIds] });
|
|
3713
|
+
diagnostics.set(linkId, current);
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
return diagnostics;
|
|
3717
|
+
}
|
|
3718
|
+
function findFeedbackPath(current, target, adjacency, path, visited = new Set()) {
|
|
3719
|
+
if (current === target) {
|
|
3720
|
+
return path;
|
|
3721
|
+
}
|
|
3722
|
+
if (visited.has(current)) {
|
|
3723
|
+
return null;
|
|
3724
|
+
}
|
|
3725
|
+
visited.add(current);
|
|
3726
|
+
for (const edge of adjacency.get(current) ?? []) {
|
|
3727
|
+
const next = findFeedbackPath(edge.to, target, adjacency, [...path, edge.link.id], new Set(visited));
|
|
3728
|
+
if (next) {
|
|
3729
|
+
return next;
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
return null;
|
|
3733
|
+
}
|
|
3734
|
+
function feedbackNodeKey(endpoint) {
|
|
3735
|
+
if (endpoint.kind === 'state') {
|
|
3736
|
+
return `state:${endpoint.ref.layer ?? 'values'}:${endpoint.ref.path}`;
|
|
3737
|
+
}
|
|
3738
|
+
if (endpoint.kind === 'global-action') {
|
|
3739
|
+
return `global-action:${endpoint.ref.actionId}`;
|
|
3740
|
+
}
|
|
3741
|
+
const nested = endpoint.ref.nestedPath?.length
|
|
3742
|
+
? endpoint.ref.nestedPath.map(formatPathSegment).join('/')
|
|
3743
|
+
: '';
|
|
3744
|
+
return `component:${endpoint.ref.widget}:${nested}`;
|
|
3745
|
+
}
|
|
3746
|
+
function isIntentionalGuardedFeedback(link) {
|
|
3747
|
+
const declaresIntent = (link.metadata?.tags ?? []).includes('intentional-feedback');
|
|
3748
|
+
const hasGuard = !!link.condition
|
|
3749
|
+
|| !!link.policy?.distinct
|
|
3750
|
+
|| !!link.policy?.distinctBy
|
|
3751
|
+
|| typeof link.policy?.debounceMs === 'number';
|
|
3752
|
+
return declaresIntent && hasGuard;
|
|
3753
|
+
}
|
|
3632
3754
|
function prioritizedInputPorts(inputs, limit = 4) {
|
|
3633
3755
|
return [...inputs]
|
|
3634
3756
|
.sort((left, right) => inputPortPriority(right) - inputPortPriority(left) || left.localeCompare(right))
|
|
@@ -6011,6 +6133,10 @@ class ConnectionEditorComponent {
|
|
|
6011
6133
|
return this.tx('connections.validation.nestedPathNotDeclared', 'Nested path was not found in the current widget structure.');
|
|
6012
6134
|
case 'state-path-not-declared':
|
|
6013
6135
|
return this.tx('connections.validation.statePathNotDeclared', 'State path is not declared in page state values.');
|
|
6136
|
+
case 'feedback-cycle-unguarded':
|
|
6137
|
+
return this.tx('connections.validation.feedbackCycleUnguarded', 'Connection participates in an unguarded feedback cycle.');
|
|
6138
|
+
case 'feedback-cycle-guarded':
|
|
6139
|
+
return this.tx('connections.validation.feedbackCycleGuarded', 'Connection participates in an intentional guarded feedback cycle.');
|
|
6014
6140
|
}
|
|
6015
6141
|
}
|
|
6016
6142
|
suggestionReasonText(reason) {
|
|
@@ -12483,6 +12609,7 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
12483
12609
|
{ path: 'ComponentDocMeta.configEditor', category: 'widgets', valueKind: 'object', description: 'Editor canonico de inputs publicado pela lib dona do componente e aberto pelo settings-panel do host.' },
|
|
12484
12610
|
{ path: 'ComponentDocMeta.configEditor.component', category: 'widgets', valueKind: 'object', description: 'Componente standalone que implementa SettingsValueProvider para editar definition.inputs.' },
|
|
12485
12611
|
{ path: 'ComponentDocMeta.configEditor.title', category: 'widgets', valueKind: 'string', description: 'Titulo opcional usado pelo host ao abrir o editor de inputs do componente.' },
|
|
12612
|
+
{ path: 'ComponentDocMeta.configEditor.contextResolver', category: 'widgets', valueKind: 'object', description: 'Resolver canonico de contexto transiente para o editor; catálogos, schemas, capabilities, targets e diagnostics devem ser passados por context/contextDiagnostics e nao persistidos em definition.inputs.' },
|
|
12486
12613
|
{ path: 'ComponentDocMeta.authoringManifestRef', category: 'widgets', valueKind: 'object', description: 'Referencia canonica para o manifesto executavel de authoring por IA publicado pela lib dona do componente.' },
|
|
12487
12614
|
{ path: 'ComponentDocMeta.authoringManifestRef.componentId', category: 'widgets', valueKind: 'string', description: 'ID do componente no registry/backend de manifestos de authoring.' },
|
|
12488
12615
|
{ path: 'ComponentDocMeta.authoringManifestRef.source', category: 'widgets', valueKind: 'string', description: 'Simbolo, modulo ou registry id estavel do manifesto usado para delegar edicoes governadas.' },
|
|
@@ -13127,6 +13254,8 @@ class PageBuilderAgenticAuthoringService {
|
|
|
13127
13254
|
return this.http.post(`${this.baseUrl}/page-apply`, {
|
|
13128
13255
|
componentType: body.componentType || 'praxis-dynamic-page',
|
|
13129
13256
|
componentId: body.componentId,
|
|
13257
|
+
streamId: body.streamId,
|
|
13258
|
+
resultEventId: body.resultEventId,
|
|
13130
13259
|
scope: body.scope,
|
|
13131
13260
|
tags: body.tags,
|
|
13132
13261
|
compiledFormPatch: body.compiledFormPatch,
|
|
@@ -14313,11 +14442,11 @@ function toCompositionTransform(transform) {
|
|
|
14313
14442
|
return {
|
|
14314
14443
|
version: '2.0',
|
|
14315
14444
|
phase: 'link-propagation',
|
|
14316
|
-
mode: '
|
|
14445
|
+
mode: 'object-fragment',
|
|
14317
14446
|
steps: [
|
|
14318
14447
|
{
|
|
14319
14448
|
id: transform.id,
|
|
14320
|
-
kind: 'template',
|
|
14449
|
+
kind: 'object-template',
|
|
14321
14450
|
phase: 'link-propagation',
|
|
14322
14451
|
input: { source: transform.inputSource ?? 'event' },
|
|
14323
14452
|
config: {
|
|
@@ -14327,23 +14456,42 @@ function toCompositionTransform(transform) {
|
|
|
14327
14456
|
},
|
|
14328
14457
|
},
|
|
14329
14458
|
},
|
|
14459
|
+
output: {
|
|
14460
|
+
semanticKind: 'query-context',
|
|
14461
|
+
stableShape: true,
|
|
14462
|
+
},
|
|
14330
14463
|
},
|
|
14331
14464
|
],
|
|
14465
|
+
output: {
|
|
14466
|
+
semanticKind: 'query-context',
|
|
14467
|
+
stableShape: true,
|
|
14468
|
+
},
|
|
14332
14469
|
};
|
|
14333
14470
|
}
|
|
14334
14471
|
if (transform.kind === 'template') {
|
|
14472
|
+
const template = clone(transform.template);
|
|
14473
|
+
const structuredKind = Array.isArray(template)
|
|
14474
|
+
? 'array-template'
|
|
14475
|
+
: template !== null && typeof template === 'object'
|
|
14476
|
+
? 'object-template'
|
|
14477
|
+
: 'template';
|
|
14478
|
+
const mode = structuredKind === 'array-template'
|
|
14479
|
+
? 'collection'
|
|
14480
|
+
: structuredKind === 'object-template'
|
|
14481
|
+
? 'object-fragment'
|
|
14482
|
+
: 'single-value';
|
|
14335
14483
|
return {
|
|
14336
14484
|
version: '2.0',
|
|
14337
14485
|
phase: 'link-propagation',
|
|
14338
|
-
mode
|
|
14486
|
+
mode,
|
|
14339
14487
|
steps: [
|
|
14340
14488
|
{
|
|
14341
14489
|
id: transform.id,
|
|
14342
|
-
kind:
|
|
14490
|
+
kind: structuredKind,
|
|
14343
14491
|
phase: 'link-propagation',
|
|
14344
14492
|
input: { source: transform.inputSource ?? 'event' },
|
|
14345
14493
|
config: {
|
|
14346
|
-
template
|
|
14494
|
+
template,
|
|
14347
14495
|
},
|
|
14348
14496
|
},
|
|
14349
14497
|
],
|
|
@@ -14960,6 +15108,7 @@ class PageBuilderAiAdapter {
|
|
|
14960
15108
|
? {
|
|
14961
15109
|
available: true,
|
|
14962
15110
|
...(meta.configEditor.title ? { title: meta.configEditor.title } : {}),
|
|
15111
|
+
...(meta.configEditor.contextResolver ? { contextResolverAvailable: true } : {}),
|
|
14963
15112
|
}
|
|
14964
15113
|
: undefined,
|
|
14965
15114
|
authoringManifestRef: this.clone(meta.authoringManifestRef),
|
|
@@ -15024,7 +15173,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15024
15173
|
}
|
|
15025
15174
|
async submitSynchronously(request, prompt) {
|
|
15026
15175
|
try {
|
|
15027
|
-
const authoringContext = this.buildAuthoringContext(request);
|
|
15176
|
+
const authoringContext = this.buildAuthoringContext(request, prompt);
|
|
15028
15177
|
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
15029
15178
|
const selectedWidgetKey = this.context.selectedWidgetKey();
|
|
15030
15179
|
const intentResolution = await firstValueFrom(this.service.resolveIntent(this.buildIntentResolutionRequest(prompt, selectedWidgetKey, componentCapabilities, authoringContext)));
|
|
@@ -15122,16 +15271,17 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15122
15271
|
diagnostics: { intentResolution },
|
|
15123
15272
|
};
|
|
15124
15273
|
}
|
|
15274
|
+
const previewAuthoringContext = await this.withSelectedComponentAuthoringContext(authoringContext, selectedWidgetKey);
|
|
15125
15275
|
const preview = await firstValueFrom(this.service.previewPage({
|
|
15126
15276
|
userPrompt: prompt,
|
|
15127
15277
|
provider: this.context.provider(),
|
|
15128
15278
|
model: this.context.model(),
|
|
15129
15279
|
apiKey: this.context.apiKey(),
|
|
15130
15280
|
currentPage: this.context.currentPage(),
|
|
15131
|
-
selectedWidgetKey: this.resolveSelectedWidgetKey(selectedWidgetKey,
|
|
15281
|
+
selectedWidgetKey: this.resolveSelectedWidgetKey(selectedWidgetKey, previewAuthoringContext.contextHints),
|
|
15132
15282
|
intentResolution,
|
|
15133
15283
|
componentCapabilities,
|
|
15134
|
-
...
|
|
15284
|
+
...previewAuthoringContext,
|
|
15135
15285
|
}));
|
|
15136
15286
|
if (!preview.valid) {
|
|
15137
15287
|
const message = this.context.describePreviewFailure(preview);
|
|
@@ -15192,7 +15342,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15192
15342
|
}
|
|
15193
15343
|
submitWithTurnStream(request, prompt) {
|
|
15194
15344
|
const preparedRequest$ = from(this.buildTurnStreamRequest(request, prompt)).pipe(share());
|
|
15195
|
-
const stream$ = preparedRequest$.pipe(concatMap(({ streamRequest }) => this.service.streamTurn(streamRequest)), concatMap((event) => from(this.toTurnResultFromStreamEvent(event, request, prompt))), share());
|
|
15345
|
+
const stream$ = preparedRequest$.pipe(concatMap(({ streamRequest }) => this.service.streamTurn(streamRequest).pipe(map((event) => ({ event, contextHints: streamRequest.contextHints })))), concatMap(({ event, contextHints }) => from(this.toTurnResultFromStreamEvent(event, request, prompt, contextHints))), share());
|
|
15196
15346
|
return merge(of(this.toTurnStreamPreparationStatusResult()), preparedRequest$.pipe(map(({ diagnostics }) => this.toTurnStreamPreparedStatusResult(diagnostics))), timer(TURN_STREAM_PREPARATION_WAITING_MS).pipe(map(() => this.toTurnStreamPreparationWaitingResult()), takeUntil(stream$)), stream$).pipe(catchError((error) => {
|
|
15197
15347
|
if (this.shouldFailClosedFromTurnStreamError(request, prompt, error)) {
|
|
15198
15348
|
return of(this.toGovernedRouteFailClosedResult(request, prompt, error));
|
|
@@ -15407,14 +15557,14 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15407
15557
|
};
|
|
15408
15558
|
}
|
|
15409
15559
|
dashboardRepairBasePage(contextHints) {
|
|
15410
|
-
const materializedPage = this.cloneJsonObject(this.toJsonObject(contextHints['materializedPage']));
|
|
15411
|
-
if (this.hasPageWidgets(materializedPage)) {
|
|
15412
|
-
return materializedPage;
|
|
15413
|
-
}
|
|
15414
15560
|
const currentPage = this.cloneJsonObject(this.toJsonObject(this.context.currentPage()));
|
|
15415
15561
|
if (this.hasPageWidgets(currentPage)) {
|
|
15416
15562
|
return currentPage;
|
|
15417
15563
|
}
|
|
15564
|
+
const materializedPage = this.cloneJsonObject(this.toJsonObject(contextHints['materializedPage']));
|
|
15565
|
+
if (this.hasPageWidgets(materializedPage)) {
|
|
15566
|
+
return materializedPage;
|
|
15567
|
+
}
|
|
15418
15568
|
const plan = this.toJsonObject(contextHints['uiCompositionPlan']);
|
|
15419
15569
|
const pageFromPlan = this.pageFromUiCompositionPlan(plan);
|
|
15420
15570
|
return this.hasPageWidgets(pageFromPlan) ? pageFromPlan : null;
|
|
@@ -15423,88 +15573,18 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15423
15573
|
if (!plan) {
|
|
15424
15574
|
return null;
|
|
15425
15575
|
}
|
|
15426
|
-
const
|
|
15427
|
-
|
|
15428
|
-
.filter((widget) => !!widget)
|
|
15429
|
-
.map((widget) => ({
|
|
15430
|
-
key: this.readString(widget, 'key') ?? this.readString(widget, 'id') ?? 'dashboard-widget',
|
|
15431
|
-
...(this.toJsonObject(widget['shell']) ? { shell: this.cloneJsonObject(this.toJsonObject(widget['shell'])) } : {}),
|
|
15432
|
-
...(this.readString(widget, 'className') ? { className: this.readString(widget, 'className') } : {}),
|
|
15433
|
-
definition: {
|
|
15434
|
-
id: this.readString(widget, 'componentId') ?? this.readString(widget, 'type') ?? 'praxis-rich-content',
|
|
15435
|
-
bindingOrder: this.arrayFromUnknown(widget['bindingOrder']).map((item) => `${item}`),
|
|
15436
|
-
inputs: this.cloneJsonObject(this.toJsonObject(widget['inputs'])) ?? {},
|
|
15437
|
-
},
|
|
15438
|
-
}));
|
|
15439
|
-
const links = this.arrayFromUnknown(plan['bindings'])
|
|
15440
|
-
.map((binding) => this.toJsonObject(binding))
|
|
15441
|
-
.filter((binding) => !!binding)
|
|
15442
|
-
.map((binding) => this.pageCompositionLinkFromBinding(binding))
|
|
15443
|
-
.filter((link) => !!link);
|
|
15444
|
-
return {
|
|
15445
|
-
title: this.readString(plan, 'title') ?? 'Dashboard',
|
|
15446
|
-
layoutPreset: this.readString(plan, 'layoutPreset'),
|
|
15447
|
-
themePreset: this.readString(plan, 'themePreset'),
|
|
15448
|
-
canvas: this.cloneJsonObject(this.toJsonObject(plan['canvas'])),
|
|
15449
|
-
state: this.cloneJsonObject(this.toJsonObject(plan['state'])),
|
|
15450
|
-
widgets,
|
|
15451
|
-
...(links.length ? { composition: { version: '1.0.0', links } } : {}),
|
|
15452
|
-
};
|
|
15453
|
-
}
|
|
15454
|
-
pageCompositionLinkFromBinding(binding) {
|
|
15455
|
-
const from = this.pageCompositionEndpointFromPlanEndpoint(this.toJsonObject(binding['from']));
|
|
15456
|
-
const to = this.pageCompositionEndpointFromPlanEndpoint(this.toJsonObject(binding['to']));
|
|
15457
|
-
if (!from || !to) {
|
|
15576
|
+
const compiled = compileUiCompositionPlan(plan);
|
|
15577
|
+
if (!compiled.valid) {
|
|
15458
15578
|
return null;
|
|
15459
15579
|
}
|
|
15460
|
-
return
|
|
15461
|
-
id: this.readString(binding, 'id') ?? `${JSON.stringify(from)}->${JSON.stringify(to)}`,
|
|
15462
|
-
intent: this.readString(binding, 'intent') ?? 'filter',
|
|
15463
|
-
from,
|
|
15464
|
-
to,
|
|
15465
|
-
...(this.toJsonObject(binding['transform']) ? { transform: this.cloneJsonObject(this.toJsonObject(binding['transform'])) } : {}),
|
|
15466
|
-
};
|
|
15467
|
-
}
|
|
15468
|
-
pageCompositionEndpointFromPlanEndpoint(endpoint) {
|
|
15469
|
-
if (!endpoint) {
|
|
15470
|
-
return null;
|
|
15471
|
-
}
|
|
15472
|
-
const kind = this.readString(endpoint, 'kind');
|
|
15473
|
-
if (kind === 'component-port') {
|
|
15474
|
-
return {
|
|
15475
|
-
kind,
|
|
15476
|
-
ref: {
|
|
15477
|
-
widget: this.readString(endpoint, 'widget'),
|
|
15478
|
-
port: this.readString(endpoint, 'port'),
|
|
15479
|
-
direction: this.readString(endpoint, 'direction'),
|
|
15480
|
-
componentType: this.readString(endpoint, 'componentType'),
|
|
15481
|
-
nestedPath: this.cloneJsonObject(this.toJsonObject(endpoint['nestedPath'])),
|
|
15482
|
-
},
|
|
15483
|
-
};
|
|
15484
|
-
}
|
|
15485
|
-
if (kind === 'global-action') {
|
|
15486
|
-
return {
|
|
15487
|
-
kind,
|
|
15488
|
-
ref: {
|
|
15489
|
-
actionId: this.readString(endpoint, 'actionId') ?? this.readString(this.toJsonObject(endpoint['ref']) ?? {}, 'actionId'),
|
|
15490
|
-
payload: this.cloneJsonObject(this.toJsonObject(endpoint['payload'])),
|
|
15491
|
-
},
|
|
15492
|
-
};
|
|
15493
|
-
}
|
|
15494
|
-
if (kind === 'state') {
|
|
15495
|
-
return {
|
|
15496
|
-
kind,
|
|
15497
|
-
ref: {
|
|
15498
|
-
path: this.readString(endpoint, 'path'),
|
|
15499
|
-
},
|
|
15500
|
-
};
|
|
15501
|
-
}
|
|
15502
|
-
return null;
|
|
15580
|
+
return this.cloneJsonObject(compiled.page);
|
|
15503
15581
|
}
|
|
15504
15582
|
repairDashboardKpis(page, contextHints, repairLog) {
|
|
15505
15583
|
const widgets = this.dashboardPageWidgets(page);
|
|
15506
|
-
const
|
|
15584
|
+
const telemetrySourceKey = this.dashboardKpiTelemetrySourceWidgetKey(page)
|
|
15585
|
+
?? this.dashboardKpiTelemetrySourceWidgetKeyFromRepairContext(contextHints);
|
|
15507
15586
|
let repaired = false;
|
|
15587
|
+
let hasKpiWidget = false;
|
|
15508
15588
|
for (const widget of widgets) {
|
|
15509
15589
|
const componentId = this.dashboardWidgetComponentId(widget);
|
|
15510
15590
|
const inputs = this.dashboardWidgetInputs(widget);
|
|
@@ -15515,22 +15595,24 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15515
15595
|
if (!document) {
|
|
15516
15596
|
continue;
|
|
15517
15597
|
}
|
|
15598
|
+
hasKpiWidget = true;
|
|
15518
15599
|
const kpiKey = this.dashboardWidgetKey(widget);
|
|
15519
15600
|
repaired = this.repairRichContentKpiDocument(document) || repaired;
|
|
15520
15601
|
const context = this.dashboardKpiInitialContext(inputs['context']);
|
|
15602
|
+
repaired = !this.areSemanticJsonValuesEqual(inputs['context'], context) || repaired;
|
|
15521
15603
|
this.setDashboardWidgetInputs(widget, {
|
|
15522
15604
|
...inputs,
|
|
15523
15605
|
context,
|
|
15524
15606
|
document,
|
|
15525
15607
|
});
|
|
15526
15608
|
this.ensureDashboardWidgetBindingOrder(widget, ['document', 'context']);
|
|
15527
|
-
if (
|
|
15609
|
+
if (telemetrySourceKey && this.ensureDashboardKpiContextLinks(page, telemetrySourceKey, kpiKey)) {
|
|
15528
15610
|
repaired = true;
|
|
15529
15611
|
}
|
|
15530
15612
|
}
|
|
15531
|
-
if (!
|
|
15613
|
+
if (!hasKpiWidget) {
|
|
15532
15614
|
const resourcePath = this.dashboardRepairResourcePath(contextHints);
|
|
15533
|
-
const kpiKey = this.
|
|
15615
|
+
const kpiKey = this.dashboardKpiWidgetKey(page, contextHints);
|
|
15534
15616
|
const kpiWidget = {
|
|
15535
15617
|
key: kpiKey,
|
|
15536
15618
|
shell: {
|
|
@@ -15549,12 +15631,14 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15549
15631
|
},
|
|
15550
15632
|
},
|
|
15551
15633
|
};
|
|
15552
|
-
this.insertDashboardWidget(page, kpiWidget, 1);
|
|
15553
|
-
|
|
15554
|
-
|
|
15634
|
+
this.insertDashboardWidget(page, kpiWidget, 1, false);
|
|
15635
|
+
this.ensureDashboardKpiPlacement(page, kpiKey);
|
|
15636
|
+
if (telemetrySourceKey) {
|
|
15637
|
+
this.ensureDashboardKpiContextLinks(page, telemetrySourceKey, kpiKey);
|
|
15555
15638
|
}
|
|
15556
15639
|
repaired = true;
|
|
15557
15640
|
}
|
|
15641
|
+
repaired = this.removeLegacyDashboardKpiState(page) || repaired;
|
|
15558
15642
|
if (repaired) {
|
|
15559
15643
|
repairLog.push('dashboard-kpis-bound');
|
|
15560
15644
|
}
|
|
@@ -15599,7 +15683,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15599
15683
|
return true;
|
|
15600
15684
|
}
|
|
15601
15685
|
const canonicalLabels = new Set(['total filtrado', 'itens na pagina', 'status da consulta']);
|
|
15602
|
-
const hasOnlyResolvableLabels = items.every((item) => canonicalLabels.has(this.normalizeText(this.readString(item, 'label')
|
|
15686
|
+
const hasOnlyResolvableLabels = items.every((item) => canonicalLabels.has(this.normalizeText(this.readString(item, 'label') || this.readString(item, 'title') || '')));
|
|
15603
15687
|
if (hasOnlyResolvableLabels) {
|
|
15604
15688
|
return false;
|
|
15605
15689
|
}
|
|
@@ -15612,42 +15696,57 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15612
15696
|
return false;
|
|
15613
15697
|
}
|
|
15614
15698
|
const value = this.readString(node, 'value')?.trim() ?? '';
|
|
15615
|
-
const
|
|
15616
|
-
|
|
15699
|
+
const valueExpr = this.readString(node, 'valueExpr');
|
|
15700
|
+
const placeholderValue = !value || value === '-' || value === '--' || value.toLowerCase() === 'n/a';
|
|
15701
|
+
const legacyValueExpressions = new Set([
|
|
15702
|
+
'${totalItems}',
|
|
15703
|
+
'${loadedItemsCount}',
|
|
15704
|
+
'${statusLabel}',
|
|
15705
|
+
]);
|
|
15706
|
+
const label = this.readString(node, 'label') || this.readString(node, 'title') || '';
|
|
15707
|
+
const nextValueExpr = this.dashboardKpiValueExpression(label);
|
|
15708
|
+
const nextCaptionExpr = this.dashboardKpiCaptionExpression(label);
|
|
15709
|
+
if (valueExpr && valueExpr !== nextValueExpr && !legacyValueExpressions.has(valueExpr)) {
|
|
15617
15710
|
return false;
|
|
15618
15711
|
}
|
|
15619
|
-
if (!
|
|
15712
|
+
if (!valueExpr && !placeholderValue) {
|
|
15620
15713
|
return false;
|
|
15621
15714
|
}
|
|
15622
|
-
|
|
15623
|
-
|
|
15715
|
+
let changed = valueExpr !== nextValueExpr
|
|
15716
|
+
|| this.readString(node, 'captionExpr') !== nextCaptionExpr;
|
|
15717
|
+
node['valueExpr'] = nextValueExpr;
|
|
15718
|
+
changed = Object.prototype.hasOwnProperty.call(node, 'valuePath') || changed;
|
|
15624
15719
|
delete node['valuePath'];
|
|
15720
|
+
changed = Object.prototype.hasOwnProperty.call(node, 'valueTemplate') || changed;
|
|
15625
15721
|
delete node['valueTemplate'];
|
|
15722
|
+
changed = Object.prototype.hasOwnProperty.call(node, 'value') || changed;
|
|
15626
15723
|
delete node['value'];
|
|
15724
|
+
changed = !Object.prototype.hasOwnProperty.call(node, 'format') || changed;
|
|
15627
15725
|
node['format'] = node['format'] ?? 'integer';
|
|
15628
|
-
node['captionExpr'] =
|
|
15726
|
+
node['captionExpr'] = nextCaptionExpr;
|
|
15727
|
+
changed = Object.prototype.hasOwnProperty.call(node, 'caption') || changed;
|
|
15629
15728
|
delete node['caption'];
|
|
15630
|
-
return
|
|
15729
|
+
return changed;
|
|
15631
15730
|
}
|
|
15632
15731
|
dashboardKpiValueExpression(label) {
|
|
15633
15732
|
const normalized = this.normalizeText(label);
|
|
15634
15733
|
if (normalized.includes('pagina') || normalized.includes('carregad')) {
|
|
15635
|
-
return '${loadedItemsCount}';
|
|
15734
|
+
return '${table.loadedItemsCount}';
|
|
15636
15735
|
}
|
|
15637
15736
|
if (normalized.includes('status')) {
|
|
15638
|
-
return '${
|
|
15737
|
+
return '${table.status}';
|
|
15639
15738
|
}
|
|
15640
|
-
return '${totalItems}';
|
|
15739
|
+
return '${table.totalItems}';
|
|
15641
15740
|
}
|
|
15642
15741
|
dashboardKpiCaptionExpression(label) {
|
|
15643
15742
|
const normalized = this.normalizeText(label);
|
|
15644
15743
|
if (normalized.includes('pagina') || normalized.includes('carregad')) {
|
|
15645
|
-
return '${loadedItemsCaption}';
|
|
15744
|
+
return '${table.loadedItemsCaption}';
|
|
15646
15745
|
}
|
|
15647
15746
|
if (normalized.includes('status')) {
|
|
15648
|
-
return '${statusCaption}';
|
|
15747
|
+
return '${table.statusCaption}';
|
|
15649
15748
|
}
|
|
15650
|
-
return '${totalCaption}';
|
|
15749
|
+
return '${table.totalCaption}';
|
|
15651
15750
|
}
|
|
15652
15751
|
hasDynamicKpiBinding(node) {
|
|
15653
15752
|
return !!(this.readString(node, 'valueExpr') || this.readString(node, 'valueTemplate'));
|
|
@@ -15670,44 +15769,67 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15670
15769
|
return [
|
|
15671
15770
|
{
|
|
15672
15771
|
label: 'Total filtrado',
|
|
15673
|
-
valueExpr: '${totalItems}',
|
|
15772
|
+
valueExpr: '${table.totalItems}',
|
|
15674
15773
|
format: 'integer',
|
|
15675
15774
|
icon: 'monitoring',
|
|
15676
|
-
captionExpr: '${totalCaption}',
|
|
15775
|
+
captionExpr: '${table.totalCaption}',
|
|
15677
15776
|
},
|
|
15678
15777
|
{
|
|
15679
15778
|
label: 'Itens na pagina',
|
|
15680
|
-
valueExpr: '${loadedItemsCount}',
|
|
15779
|
+
valueExpr: '${table.loadedItemsCount}',
|
|
15681
15780
|
format: 'integer',
|
|
15682
15781
|
icon: 'view_list',
|
|
15683
|
-
captionExpr: '${loadedItemsCaption}',
|
|
15782
|
+
captionExpr: '${table.loadedItemsCaption}',
|
|
15684
15783
|
},
|
|
15685
15784
|
{
|
|
15686
15785
|
label: 'Status da consulta',
|
|
15687
|
-
valueExpr: '${
|
|
15786
|
+
valueExpr: '${table.status}',
|
|
15688
15787
|
icon: 'sync',
|
|
15689
|
-
captionExpr: '${statusCaption}',
|
|
15788
|
+
captionExpr: '${table.statusCaption}',
|
|
15690
15789
|
},
|
|
15691
15790
|
];
|
|
15692
15791
|
}
|
|
15693
15792
|
dashboardKpiInitialContext(current) {
|
|
15793
|
+
const currentContext = this.cloneJsonObject(this.toJsonObject(current)) ?? {};
|
|
15794
|
+
const currentTable = this.cloneJsonObject(this.toJsonObject(currentContext['table'])) ?? {};
|
|
15795
|
+
const runtimeProjectionKeys = [
|
|
15796
|
+
'totalItems',
|
|
15797
|
+
'loadedItemsCount',
|
|
15798
|
+
'status',
|
|
15799
|
+
'statusLabel',
|
|
15800
|
+
'caption',
|
|
15801
|
+
'totalCaption',
|
|
15802
|
+
'loadedItemsCaption',
|
|
15803
|
+
'statusCaption',
|
|
15804
|
+
'resourcePath',
|
|
15805
|
+
];
|
|
15806
|
+
for (const key of runtimeProjectionKeys) {
|
|
15807
|
+
delete currentContext[key];
|
|
15808
|
+
delete currentTable[key];
|
|
15809
|
+
}
|
|
15694
15810
|
return {
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
|
|
15811
|
+
...currentContext,
|
|
15812
|
+
table: {
|
|
15813
|
+
totalItems: 0,
|
|
15814
|
+
loadedItemsCount: 0,
|
|
15815
|
+
status: this.context.tx('agentic.dashboardQuality.kpiStatus.loading', 'Carregando'),
|
|
15816
|
+
totalCaption: this.context.tx('agentic.dashboardQuality.kpiCaption.total', 'Total retornado pela consulta filtrada'),
|
|
15817
|
+
loadedItemsCaption: this.context.tx('agentic.dashboardQuality.kpiCaption.loaded', 'Itens carregados nesta pagina'),
|
|
15818
|
+
statusCaption: this.context.tx('agentic.dashboardQuality.kpiCaption.status', 'Atualizado pelo runtime da tabela'),
|
|
15819
|
+
...currentTable,
|
|
15820
|
+
},
|
|
15702
15821
|
};
|
|
15703
15822
|
}
|
|
15704
15823
|
ensureDashboardKpiContextLinks(page, tableKey, kpiKey) {
|
|
15705
15824
|
const links = this.dashboardCompositionLinks(page);
|
|
15706
|
-
const
|
|
15707
|
-
const
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
|
|
15825
|
+
const statePath = this.dashboardKpiStatePath(tableKey);
|
|
15826
|
+
const writeId = `${tableKey}.loadingStateChange->${statePath}`;
|
|
15827
|
+
const readId = `${statePath}->${kpiKey}.context`;
|
|
15828
|
+
const deprecatedWriteId = `${tableKey}.loadingStateChange->dashboardKpis`;
|
|
15829
|
+
const deprecatedReadId = `dashboardKpis->${kpiKey}.context`;
|
|
15830
|
+
const deprecatedDirectId = `${tableKey}.loadingStateChange->${kpiKey}.context`;
|
|
15831
|
+
const canonicalLinks = [
|
|
15832
|
+
{
|
|
15711
15833
|
id: writeId,
|
|
15712
15834
|
intent: 'state-write',
|
|
15713
15835
|
from: {
|
|
@@ -15716,7 +15838,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15716
15838
|
},
|
|
15717
15839
|
to: {
|
|
15718
15840
|
kind: 'state',
|
|
15719
|
-
ref: { path:
|
|
15841
|
+
ref: { path: statePath, layer: 'transient', writable: true },
|
|
15720
15842
|
},
|
|
15721
15843
|
transform: {
|
|
15722
15844
|
version: '2.0',
|
|
@@ -15731,47 +15853,122 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15731
15853
|
input: { source: 'event' },
|
|
15732
15854
|
config: {
|
|
15733
15855
|
template: {
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15856
|
+
table: {
|
|
15857
|
+
totalItems: '${payload.context.totalItems}',
|
|
15858
|
+
loadedItemsCount: '${payload.context.loadedItemsCount}',
|
|
15859
|
+
status: '${payload.status}',
|
|
15860
|
+
totalCaption: 'Total retornado pela consulta filtrada',
|
|
15861
|
+
loadedItemsCaption: '${payload.context.loadedItemsCount} itens carregados nesta pagina',
|
|
15862
|
+
statusCaption: '${payload.message}',
|
|
15863
|
+
resourcePath: '${payload.context.resourcePath}',
|
|
15864
|
+
},
|
|
15740
15865
|
},
|
|
15741
15866
|
},
|
|
15742
15867
|
},
|
|
15743
15868
|
],
|
|
15744
15869
|
},
|
|
15745
|
-
|
|
15746
|
-
|
|
15747
|
-
|
|
15748
|
-
|
|
15749
|
-
|
|
15750
|
-
|
|
15870
|
+
policy: { distinct: true },
|
|
15871
|
+
metadata: {
|
|
15872
|
+
source: 'persisted-composition-link',
|
|
15873
|
+
tags: ['dashboard-kpi-runtime-projection'],
|
|
15874
|
+
},
|
|
15875
|
+
},
|
|
15876
|
+
{
|
|
15751
15877
|
id: readId,
|
|
15752
15878
|
intent: 'state-read',
|
|
15753
15879
|
from: {
|
|
15754
15880
|
kind: 'state',
|
|
15755
|
-
ref: { path:
|
|
15881
|
+
ref: { path: statePath, layer: 'transient' },
|
|
15756
15882
|
},
|
|
15757
15883
|
to: {
|
|
15758
15884
|
kind: 'component-port',
|
|
15759
15885
|
ref: { widget: kpiKey, port: 'context', direction: 'input' },
|
|
15760
15886
|
},
|
|
15761
|
-
|
|
15762
|
-
|
|
15763
|
-
|
|
15887
|
+
policy: { missingValuePolicy: 'skip' },
|
|
15888
|
+
metadata: {
|
|
15889
|
+
source: 'persisted-composition-link',
|
|
15890
|
+
tags: ['dashboard-kpi-runtime-projection'],
|
|
15891
|
+
},
|
|
15892
|
+
},
|
|
15893
|
+
];
|
|
15894
|
+
const writeIds = new Set([writeId, deprecatedWriteId]);
|
|
15895
|
+
const readIds = new Set([readId, deprecatedReadId]);
|
|
15896
|
+
const hasWriteCandidate = links.some((link) => writeIds.has(this.readString(link, 'id')));
|
|
15897
|
+
const hasReadCandidate = links.some((link) => readIds.has(this.readString(link, 'id')));
|
|
15898
|
+
const nextLinks = [];
|
|
15899
|
+
let writePlaced = false;
|
|
15900
|
+
let readPlaced = false;
|
|
15901
|
+
let directPlacementUsed = false;
|
|
15902
|
+
for (const link of links) {
|
|
15903
|
+
const id = this.readString(link, 'id') ?? '';
|
|
15904
|
+
if (writeIds.has(id)) {
|
|
15905
|
+
if (!writePlaced) {
|
|
15906
|
+
nextLinks.push(canonicalLinks[0]);
|
|
15907
|
+
writePlaced = true;
|
|
15908
|
+
if (!hasReadCandidate) {
|
|
15909
|
+
nextLinks.push(canonicalLinks[1]);
|
|
15910
|
+
readPlaced = true;
|
|
15911
|
+
}
|
|
15912
|
+
}
|
|
15913
|
+
continue;
|
|
15914
|
+
}
|
|
15915
|
+
if (readIds.has(id)) {
|
|
15916
|
+
if (!readPlaced) {
|
|
15917
|
+
if (!hasWriteCandidate) {
|
|
15918
|
+
nextLinks.push(canonicalLinks[0]);
|
|
15919
|
+
writePlaced = true;
|
|
15920
|
+
}
|
|
15921
|
+
nextLinks.push(canonicalLinks[1]);
|
|
15922
|
+
readPlaced = true;
|
|
15923
|
+
}
|
|
15924
|
+
continue;
|
|
15925
|
+
}
|
|
15926
|
+
if (id === deprecatedDirectId) {
|
|
15927
|
+
if (!hasWriteCandidate && !hasReadCandidate && !directPlacementUsed) {
|
|
15928
|
+
nextLinks.push(...canonicalLinks);
|
|
15929
|
+
writePlaced = true;
|
|
15930
|
+
readPlaced = true;
|
|
15931
|
+
directPlacementUsed = true;
|
|
15932
|
+
}
|
|
15933
|
+
continue;
|
|
15934
|
+
}
|
|
15935
|
+
nextLinks.push(link);
|
|
15764
15936
|
}
|
|
15937
|
+
if (!writePlaced) {
|
|
15938
|
+
nextLinks.push(canonicalLinks[0]);
|
|
15939
|
+
}
|
|
15940
|
+
if (!readPlaced) {
|
|
15941
|
+
nextLinks.push(canonicalLinks[1]);
|
|
15942
|
+
}
|
|
15943
|
+
const changed = !this.areSemanticJsonValuesEqual(links, nextLinks);
|
|
15765
15944
|
if (changed) {
|
|
15766
15945
|
const composition = this.toJsonObject(page['composition']) ?? {};
|
|
15767
15946
|
page['composition'] = {
|
|
15768
15947
|
...composition,
|
|
15769
|
-
version: this.readString(composition, 'version')
|
|
15770
|
-
links,
|
|
15948
|
+
version: this.readString(composition, 'version') || '1.0.0',
|
|
15949
|
+
links: nextLinks,
|
|
15771
15950
|
};
|
|
15772
15951
|
}
|
|
15773
15952
|
return changed;
|
|
15774
15953
|
}
|
|
15954
|
+
dashboardKpiStatePath(tableKey) {
|
|
15955
|
+
const encodedTableKey = encodeURIComponent(tableKey).replace(/\./g, '%2E');
|
|
15956
|
+
return `dashboardKpis.${encodedTableKey}`;
|
|
15957
|
+
}
|
|
15958
|
+
removeLegacyDashboardKpiState(page) {
|
|
15959
|
+
const state = this.toJsonObject(page['state']);
|
|
15960
|
+
const values = this.toJsonObject(state?.['values']);
|
|
15961
|
+
if (!state || !values || !Object.prototype.hasOwnProperty.call(values, 'dashboardKpis')) {
|
|
15962
|
+
return false;
|
|
15963
|
+
}
|
|
15964
|
+
const nextValues = { ...values };
|
|
15965
|
+
delete nextValues['dashboardKpis'];
|
|
15966
|
+
page['state'] = {
|
|
15967
|
+
...state,
|
|
15968
|
+
values: nextValues,
|
|
15969
|
+
};
|
|
15970
|
+
return true;
|
|
15971
|
+
}
|
|
15775
15972
|
repairDashboardChart(page, contextHints, repairLog) {
|
|
15776
15973
|
const widgets = this.dashboardPageWidgets(page);
|
|
15777
15974
|
if (widgets.some((widget) => this.dashboardWidgetComponentId(widget).includes('chart'))) {
|
|
@@ -15938,7 +16135,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15938
16135
|
const composition = this.toJsonObject(page['composition']) ?? {};
|
|
15939
16136
|
page['composition'] = {
|
|
15940
16137
|
...composition,
|
|
15941
|
-
version: this.readString(composition, 'version')
|
|
16138
|
+
version: this.readString(composition, 'version') || '1.0.0',
|
|
15942
16139
|
links,
|
|
15943
16140
|
};
|
|
15944
16141
|
repairLog.push('dashboard-widgets-connected');
|
|
@@ -15951,6 +16148,27 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15951
16148
|
});
|
|
15952
16149
|
return detailWidget ? this.dashboardWidgetKey(detailWidget) : null;
|
|
15953
16150
|
}
|
|
16151
|
+
dashboardKpiTelemetrySourceWidgetKey(page) {
|
|
16152
|
+
const tables = this.dashboardPageWidgets(page)
|
|
16153
|
+
.filter((widget) => this.dashboardWidgetComponentId(widget) === 'praxis-table');
|
|
16154
|
+
if (!tables.length) {
|
|
16155
|
+
return null;
|
|
16156
|
+
}
|
|
16157
|
+
const tableKeys = new Set(tables.map((widget) => this.dashboardWidgetKey(widget)));
|
|
16158
|
+
const slotAssignments = this.toJsonObject(page['slotAssignments']);
|
|
16159
|
+
const detailTableKey = Object.entries(slotAssignments ?? {})
|
|
16160
|
+
.find(([key, slot]) => (tableKeys.has(key)
|
|
16161
|
+
&& this.normalizeText(`${slot}`) === 'detail-table'))?.[0];
|
|
16162
|
+
return detailTableKey ?? this.dashboardWidgetKey(tables[0]);
|
|
16163
|
+
}
|
|
16164
|
+
dashboardKpiTelemetrySourceWidgetKeyFromRepairContext(contextHints) {
|
|
16165
|
+
const dashboardQuality = this.toJsonObject(contextHints['dashboardQuality']);
|
|
16166
|
+
const tables = this.arrayFromUnknown(dashboardQuality?.['widgets'])
|
|
16167
|
+
.map((widget) => this.toJsonObject(widget))
|
|
16168
|
+
.filter((widget) => (!!widget && this.readString(widget, 'componentId').toLowerCase() === 'praxis-table'));
|
|
16169
|
+
const detailTable = tables.find((widget) => (this.normalizeText(this.readString(widget, 'slot')) === 'detail-table'));
|
|
16170
|
+
return this.readString(detailTable ?? tables[0] ?? null, 'key') || null;
|
|
16171
|
+
}
|
|
15954
16172
|
dashboardDetailWidgetKeyFromRepairContext(contextHints) {
|
|
15955
16173
|
const dashboardQuality = this.toJsonObject(contextHints['dashboardQuality']);
|
|
15956
16174
|
for (const widget of this.arrayFromUnknown(dashboardQuality?.['widgets'])) {
|
|
@@ -15972,7 +16190,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
15972
16190
|
const composition = this.toJsonObject(page['composition']) ?? {};
|
|
15973
16191
|
page['composition'] = {
|
|
15974
16192
|
...composition,
|
|
15975
|
-
version: this.readString(composition, 'version')
|
|
16193
|
+
version: this.readString(composition, 'version') || '1.0.0',
|
|
15976
16194
|
links,
|
|
15977
16195
|
};
|
|
15978
16196
|
return true;
|
|
@@ -16050,11 +16268,257 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16050
16268
|
definition['bindingOrder'] = next;
|
|
16051
16269
|
widget['definition'] = definition;
|
|
16052
16270
|
}
|
|
16053
|
-
insertDashboardWidget(page, widget, index) {
|
|
16271
|
+
insertDashboardWidget(page, widget, index, ensureCanvas = true) {
|
|
16054
16272
|
const widgets = this.dashboardPageWidgets(page);
|
|
16055
16273
|
widgets.splice(Math.max(0, Math.min(index, widgets.length)), 0, widget);
|
|
16056
16274
|
page['widgets'] = widgets;
|
|
16057
|
-
|
|
16275
|
+
if (ensureCanvas) {
|
|
16276
|
+
this.ensureDashboardCanvasItem(page, this.dashboardWidgetKey(widget), this.dashboardWidgetComponentId(widget));
|
|
16277
|
+
}
|
|
16278
|
+
}
|
|
16279
|
+
dashboardKpiWidgetKey(page, contextHints) {
|
|
16280
|
+
const widgetKeys = new Set(this.dashboardPageWidgets(page).map((widget) => this.dashboardWidgetKey(widget)));
|
|
16281
|
+
const slotAssignments = this.toJsonObject(page['slotAssignments']);
|
|
16282
|
+
const reservedKpiKey = Object.entries(slotAssignments ?? {})
|
|
16283
|
+
.find(([key, slot]) => !widgetKeys.has(key) && this.normalizeText(`${slot}`) === 'kpis')?.[0];
|
|
16284
|
+
if (reservedKpiKey) {
|
|
16285
|
+
return reservedKpiKey;
|
|
16286
|
+
}
|
|
16287
|
+
const resourceSegment = this.dashboardRepairResourcePath(contextHints)
|
|
16288
|
+
.split('?')[0]
|
|
16289
|
+
.split('/')
|
|
16290
|
+
.filter(Boolean)
|
|
16291
|
+
.at(-1)
|
|
16292
|
+
?.normalize('NFD')
|
|
16293
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
16294
|
+
.toLowerCase()
|
|
16295
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
16296
|
+
.replace(/^-+|-+$/g, '');
|
|
16297
|
+
return this.uniqueDashboardWidgetKey(page, `${resourceSegment || 'dashboard'}-kpis`);
|
|
16298
|
+
}
|
|
16299
|
+
ensureDashboardKpiPlacement(page, kpiKey) {
|
|
16300
|
+
const slotAssignments = this.toJsonObject(page['slotAssignments']) ?? {};
|
|
16301
|
+
slotAssignments[kpiKey] = 'kpis';
|
|
16302
|
+
page['slotAssignments'] = slotAssignments;
|
|
16303
|
+
const anchorKeys = Object.entries(slotAssignments)
|
|
16304
|
+
.filter(([key, slot]) => key !== kpiKey && this.normalizeText(`${slot}`) === 'hero')
|
|
16305
|
+
.map(([key]) => key);
|
|
16306
|
+
for (const widget of this.dashboardPageWidgets(page)) {
|
|
16307
|
+
const key = this.dashboardWidgetKey(widget);
|
|
16308
|
+
if (key !== kpiKey && /(?:^|[-_.])(summary|overview|hero)(?:$|[-_.])/.test(key.toLowerCase())) {
|
|
16309
|
+
anchorKeys.push(key);
|
|
16310
|
+
}
|
|
16311
|
+
}
|
|
16312
|
+
const canvas = this.toJsonObject(page['canvas']);
|
|
16313
|
+
const originalCanvas = this.cloneJsonObject(canvas);
|
|
16314
|
+
if (canvas) {
|
|
16315
|
+
this.insertDashboardKpiCanvasItem(canvas, kpiKey, anchorKeys);
|
|
16316
|
+
page['canvas'] = canvas;
|
|
16317
|
+
}
|
|
16318
|
+
const deviceLayouts = this.toJsonObject(page['deviceLayouts']);
|
|
16319
|
+
for (const device of Object.values(deviceLayouts ?? {})) {
|
|
16320
|
+
const variant = this.toJsonObject(device);
|
|
16321
|
+
const deviceCanvas = this.toJsonObject(variant?.['canvas']);
|
|
16322
|
+
if (variant && deviceCanvas) {
|
|
16323
|
+
if (originalCanvas) {
|
|
16324
|
+
this.insertDashboardKpiCanvasVariant(originalCanvas, deviceCanvas, kpiKey, anchorKeys);
|
|
16325
|
+
}
|
|
16326
|
+
else {
|
|
16327
|
+
this.insertDashboardKpiCanvasItem(deviceCanvas, kpiKey, anchorKeys);
|
|
16328
|
+
}
|
|
16329
|
+
variant['canvas'] = deviceCanvas;
|
|
16330
|
+
}
|
|
16331
|
+
}
|
|
16332
|
+
if (deviceLayouts) {
|
|
16333
|
+
page['deviceLayouts'] = deviceLayouts;
|
|
16334
|
+
}
|
|
16335
|
+
this.ensureDashboardKpiGrouping(page, kpiKey, anchorKeys);
|
|
16336
|
+
}
|
|
16337
|
+
insertDashboardKpiCanvasVariant(baseCanvas, variantCanvas, kpiKey, anchorKeys) {
|
|
16338
|
+
const baseItems = this.toJsonObject(baseCanvas['items']) ?? {};
|
|
16339
|
+
const overrideItems = this.toJsonObject(variantCanvas['items']) ?? {};
|
|
16340
|
+
const effectiveItems = {};
|
|
16341
|
+
for (const [key, rawBaseItem] of Object.entries(baseItems)) {
|
|
16342
|
+
const baseItem = this.toJsonObject(rawBaseItem);
|
|
16343
|
+
if (!baseItem) {
|
|
16344
|
+
continue;
|
|
16345
|
+
}
|
|
16346
|
+
const override = this.toJsonObject(overrideItems[key]);
|
|
16347
|
+
if (override?.['hidden'] === true) {
|
|
16348
|
+
continue;
|
|
16349
|
+
}
|
|
16350
|
+
const effectiveItem = {
|
|
16351
|
+
...baseItem,
|
|
16352
|
+
...(override ?? {}),
|
|
16353
|
+
};
|
|
16354
|
+
delete effectiveItem['hidden'];
|
|
16355
|
+
const baseConstraints = this.toJsonObject(baseItem['constraints']);
|
|
16356
|
+
const overrideConstraints = this.toJsonObject(override?.['constraints']);
|
|
16357
|
+
if (baseConstraints || overrideConstraints) {
|
|
16358
|
+
effectiveItem['constraints'] = {
|
|
16359
|
+
...(baseConstraints ?? {}),
|
|
16360
|
+
...(overrideConstraints ?? {}),
|
|
16361
|
+
};
|
|
16362
|
+
}
|
|
16363
|
+
effectiveItems[key] = effectiveItem;
|
|
16364
|
+
}
|
|
16365
|
+
const effectiveCanvas = {
|
|
16366
|
+
columns: Math.max(1, Number(variantCanvas['columns'] ?? baseCanvas['columns']) || 12),
|
|
16367
|
+
items: effectiveItems,
|
|
16368
|
+
};
|
|
16369
|
+
this.insertDashboardKpiCanvasItem(effectiveCanvas, kpiKey, anchorKeys);
|
|
16370
|
+
const nextOverrides = { ...overrideItems };
|
|
16371
|
+
for (const [key, rawEffectiveItem] of Object.entries(this.toJsonObject(effectiveCanvas['items']) ?? {})) {
|
|
16372
|
+
const effectiveItem = this.toJsonObject(rawEffectiveItem);
|
|
16373
|
+
if (effectiveItem) {
|
|
16374
|
+
nextOverrides[key] = { ...effectiveItem };
|
|
16375
|
+
}
|
|
16376
|
+
}
|
|
16377
|
+
variantCanvas['items'] = nextOverrides;
|
|
16378
|
+
}
|
|
16379
|
+
insertDashboardKpiCanvasItem(canvas, kpiKey, anchorKeys) {
|
|
16380
|
+
const items = this.toJsonObject(canvas['items']) ?? {};
|
|
16381
|
+
if (this.toJsonObject(items[kpiKey])) {
|
|
16382
|
+
return;
|
|
16383
|
+
}
|
|
16384
|
+
const columns = Math.max(1, Number(canvas['columns']) || 12);
|
|
16385
|
+
const placements = Object.values(items)
|
|
16386
|
+
.map((item) => this.toJsonObject(item))
|
|
16387
|
+
.filter((item) => !!item);
|
|
16388
|
+
const anchor = anchorKeys
|
|
16389
|
+
.map((key) => this.toJsonObject(items[key]))
|
|
16390
|
+
.find((item) => !!item);
|
|
16391
|
+
const rowSpan = 3;
|
|
16392
|
+
let insertRow;
|
|
16393
|
+
if (anchor) {
|
|
16394
|
+
let bandStart = Math.max(1, Number(anchor['row']) || 1);
|
|
16395
|
+
let bandEnd = bandStart + Math.max(1, Number(anchor['rowSpan']) || 1);
|
|
16396
|
+
let expanded = true;
|
|
16397
|
+
while (expanded) {
|
|
16398
|
+
expanded = false;
|
|
16399
|
+
for (const item of placements) {
|
|
16400
|
+
const row = Math.max(1, Number(item['row']) || 1);
|
|
16401
|
+
const end = row + Math.max(1, Number(item['rowSpan']) || 1);
|
|
16402
|
+
if (row < bandEnd && end > bandStart) {
|
|
16403
|
+
const nextStart = Math.min(bandStart, row);
|
|
16404
|
+
const nextEnd = Math.max(bandEnd, end);
|
|
16405
|
+
if (nextStart !== bandStart || nextEnd !== bandEnd) {
|
|
16406
|
+
bandStart = nextStart;
|
|
16407
|
+
bandEnd = nextEnd;
|
|
16408
|
+
expanded = true;
|
|
16409
|
+
}
|
|
16410
|
+
}
|
|
16411
|
+
}
|
|
16412
|
+
}
|
|
16413
|
+
insertRow = bandEnd;
|
|
16414
|
+
}
|
|
16415
|
+
else {
|
|
16416
|
+
insertRow = placements.reduce((max, item) => Math.max(max, Math.max(1, Number(item['row']) || 1) + Math.max(1, Number(item['rowSpan']) || 1)), 1);
|
|
16417
|
+
}
|
|
16418
|
+
if (anchor) {
|
|
16419
|
+
for (const placement of placements) {
|
|
16420
|
+
const row = Number(placement?.['row']);
|
|
16421
|
+
if (placement && Number.isFinite(row) && row >= insertRow) {
|
|
16422
|
+
placement['row'] = row + rowSpan;
|
|
16423
|
+
}
|
|
16424
|
+
}
|
|
16425
|
+
}
|
|
16426
|
+
items[kpiKey] = {
|
|
16427
|
+
col: 1,
|
|
16428
|
+
row: insertRow,
|
|
16429
|
+
colSpan: columns,
|
|
16430
|
+
rowSpan,
|
|
16431
|
+
};
|
|
16432
|
+
canvas['items'] = items;
|
|
16433
|
+
}
|
|
16434
|
+
ensureDashboardKpiGrouping(page, kpiKey, anchorKeys) {
|
|
16435
|
+
const grouping = this.arrayFromUnknown(page['grouping'])
|
|
16436
|
+
.map((group) => this.toJsonObject(group))
|
|
16437
|
+
.filter((group) => !!group);
|
|
16438
|
+
if (!grouping.length) {
|
|
16439
|
+
return;
|
|
16440
|
+
}
|
|
16441
|
+
const target = grouping.find((group) => {
|
|
16442
|
+
const widgetKeys = this.readString(group, 'kind') === 'tabs'
|
|
16443
|
+
? this.arrayFromUnknown(group['tabs']).flatMap((tab) => this.arrayFromUnknown(this.toJsonObject(tab)?.['widgetKeys']).map((key) => `${key}`))
|
|
16444
|
+
: this.arrayFromUnknown(group['widgetKeys']).map((key) => `${key}`);
|
|
16445
|
+
return anchorKeys.some((key) => widgetKeys.includes(key))
|
|
16446
|
+
|| this.readString(group, 'kind') === 'hero'
|
|
16447
|
+
|| /overview|summary|kpi|indicador/.test(this.normalizeText(this.readString(group, 'id') ?? ''));
|
|
16448
|
+
});
|
|
16449
|
+
if (!target) {
|
|
16450
|
+
return;
|
|
16451
|
+
}
|
|
16452
|
+
const targetKind = this.readString(target, 'kind');
|
|
16453
|
+
let targetTabId = null;
|
|
16454
|
+
if (targetKind === 'tabs') {
|
|
16455
|
+
const tabs = this.arrayFromUnknown(target['tabs'])
|
|
16456
|
+
.map((tab) => this.toJsonObject(tab))
|
|
16457
|
+
.filter((tab) => !!tab);
|
|
16458
|
+
const targetTab = tabs.find((tab) => this.arrayFromUnknown(tab['widgetKeys']).some((key) => anchorKeys.includes(`${key}`))) ?? tabs[0];
|
|
16459
|
+
if (!targetTab) {
|
|
16460
|
+
return;
|
|
16461
|
+
}
|
|
16462
|
+
targetTabId = this.readString(targetTab, 'id');
|
|
16463
|
+
const widgetKeys = this.arrayFromUnknown(targetTab['widgetKeys']).map((key) => `${key}`);
|
|
16464
|
+
if (!widgetKeys.includes(kpiKey)) {
|
|
16465
|
+
const anchorIndex = widgetKeys.reduce((lastIndex, key, index) => anchorKeys.includes(key) ? index : lastIndex, -1);
|
|
16466
|
+
widgetKeys.splice(anchorIndex >= 0 ? anchorIndex + 1 : widgetKeys.length, 0, kpiKey);
|
|
16467
|
+
targetTab['widgetKeys'] = widgetKeys;
|
|
16468
|
+
}
|
|
16469
|
+
target['tabs'] = tabs;
|
|
16470
|
+
}
|
|
16471
|
+
else {
|
|
16472
|
+
const widgetKeys = this.arrayFromUnknown(target['widgetKeys']).map((key) => `${key}`);
|
|
16473
|
+
if (!widgetKeys.includes(kpiKey)) {
|
|
16474
|
+
const anchorIndex = widgetKeys.reduce((lastIndex, key, index) => anchorKeys.includes(key) ? index : lastIndex, -1);
|
|
16475
|
+
widgetKeys.splice(anchorIndex >= 0 ? anchorIndex + 1 : widgetKeys.length, 0, kpiKey);
|
|
16476
|
+
target['widgetKeys'] = widgetKeys;
|
|
16477
|
+
}
|
|
16478
|
+
}
|
|
16479
|
+
const targetId = this.readString(target, 'id');
|
|
16480
|
+
const deviceLayouts = this.toJsonObject(page['deviceLayouts']);
|
|
16481
|
+
for (const device of Object.values(deviceLayouts ?? {})) {
|
|
16482
|
+
const variant = this.toJsonObject(device);
|
|
16483
|
+
for (const override of this.arrayFromUnknown(variant?.['groupingOverrides'])) {
|
|
16484
|
+
const normalizedOverride = this.toJsonObject(override);
|
|
16485
|
+
if (!normalizedOverride || this.readString(normalizedOverride, 'id') !== targetId) {
|
|
16486
|
+
continue;
|
|
16487
|
+
}
|
|
16488
|
+
if (normalizedOverride['hidden'] === true) {
|
|
16489
|
+
continue;
|
|
16490
|
+
}
|
|
16491
|
+
if (targetKind === 'tabs') {
|
|
16492
|
+
const tabs = this.arrayFromUnknown(normalizedOverride['tabs'])
|
|
16493
|
+
.map((tab) => this.toJsonObject(tab))
|
|
16494
|
+
.filter((tab) => !!tab);
|
|
16495
|
+
if (!tabs.length) {
|
|
16496
|
+
continue;
|
|
16497
|
+
}
|
|
16498
|
+
const targetTab = tabs.find((tab) => this.readString(tab, 'id') === targetTabId)
|
|
16499
|
+
?? tabs.find((tab) => this.arrayFromUnknown(tab['widgetKeys']).some((key) => anchorKeys.includes(`${key}`)))
|
|
16500
|
+
?? tabs[0];
|
|
16501
|
+
const overrideKeys = this.arrayFromUnknown(targetTab['widgetKeys']).map((key) => `${key}`);
|
|
16502
|
+
if (!overrideKeys.includes(kpiKey)) {
|
|
16503
|
+
const anchorIndex = overrideKeys.reduce((lastIndex, key, index) => anchorKeys.includes(key) ? index : lastIndex, -1);
|
|
16504
|
+
overrideKeys.splice(anchorIndex >= 0 ? anchorIndex + 1 : overrideKeys.length, 0, kpiKey);
|
|
16505
|
+
targetTab['widgetKeys'] = overrideKeys;
|
|
16506
|
+
}
|
|
16507
|
+
normalizedOverride['tabs'] = tabs;
|
|
16508
|
+
continue;
|
|
16509
|
+
}
|
|
16510
|
+
if (!Array.isArray(normalizedOverride['widgetKeys'])) {
|
|
16511
|
+
continue;
|
|
16512
|
+
}
|
|
16513
|
+
const overrideKeys = this.arrayFromUnknown(normalizedOverride['widgetKeys']).map((key) => `${key}`);
|
|
16514
|
+
if (!overrideKeys.includes(kpiKey)) {
|
|
16515
|
+
const anchorIndex = overrideKeys.reduce((lastIndex, key, index) => anchorKeys.includes(key) ? index : lastIndex, -1);
|
|
16516
|
+
overrideKeys.splice(anchorIndex >= 0 ? anchorIndex + 1 : overrideKeys.length, 0, kpiKey);
|
|
16517
|
+
normalizedOverride['widgetKeys'] = overrideKeys;
|
|
16518
|
+
}
|
|
16519
|
+
}
|
|
16520
|
+
}
|
|
16521
|
+
page['grouping'] = grouping;
|
|
16058
16522
|
}
|
|
16059
16523
|
ensureDashboardCanvasItem(page, widgetKey, componentId) {
|
|
16060
16524
|
if (!widgetKey) {
|
|
@@ -16101,9 +16565,9 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16101
16565
|
}
|
|
16102
16566
|
dashboardRepairResourcePath(contextHints) {
|
|
16103
16567
|
return this.readString(contextHints, 'resourcePath')
|
|
16104
|
-
|
|
16105
|
-
|
|
16106
|
-
|
|
16568
|
+
|| this.readString(this.toJsonObject(this.toJsonObject(contextHints['dashboardQuality'])?.['dto']) ?? {}, 'resourcePath')
|
|
16569
|
+
|| this.dashboardFirstResourcePath(this.toJsonObject(contextHints['materializedPage']))
|
|
16570
|
+
|| '/api/resource';
|
|
16107
16571
|
}
|
|
16108
16572
|
dashboardFirstResourcePath(page) {
|
|
16109
16573
|
if (!page) {
|
|
@@ -16146,6 +16610,27 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16146
16610
|
}
|
|
16147
16611
|
return JSON.parse(JSON.stringify(value));
|
|
16148
16612
|
}
|
|
16613
|
+
areSemanticJsonValuesEqual(left, right) {
|
|
16614
|
+
if (Object.is(left, right)) {
|
|
16615
|
+
return true;
|
|
16616
|
+
}
|
|
16617
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
16618
|
+
return Array.isArray(left)
|
|
16619
|
+
&& Array.isArray(right)
|
|
16620
|
+
&& left.length === right.length
|
|
16621
|
+
&& left.every((item, index) => this.areSemanticJsonValuesEqual(item, right[index]));
|
|
16622
|
+
}
|
|
16623
|
+
const leftObject = this.toJsonObject(left);
|
|
16624
|
+
const rightObject = this.toJsonObject(right);
|
|
16625
|
+
if (!leftObject || !rightObject) {
|
|
16626
|
+
return false;
|
|
16627
|
+
}
|
|
16628
|
+
const leftKeys = Object.keys(leftObject).sort();
|
|
16629
|
+
const rightKeys = Object.keys(rightObject).sort();
|
|
16630
|
+
return leftKeys.length === rightKeys.length
|
|
16631
|
+
&& leftKeys.every((key, index) => (key === rightKeys[index]
|
|
16632
|
+
&& this.areSemanticJsonValuesEqual(leftObject[key], rightObject[key])));
|
|
16633
|
+
}
|
|
16149
16634
|
captureRuntimeRelatedSurfaceDisambiguationContext(payload, event) {
|
|
16150
16635
|
const disambiguation = this.findRuntimeRelatedSurfaceDisambiguation(payload);
|
|
16151
16636
|
const rawOptions = Array.isArray(disambiguation?.['options'])
|
|
@@ -16221,7 +16706,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16221
16706
|
const startedAt = Date.now();
|
|
16222
16707
|
const authoringContextStartedAt = Date.now();
|
|
16223
16708
|
const selectedWidgetKey = this.context.selectedWidgetKey();
|
|
16224
|
-
const authoringContext = this.buildAuthoringContext(request);
|
|
16709
|
+
const authoringContext = await this.withSelectedComponentAuthoringContext(this.buildAuthoringContext(request, prompt), selectedWidgetKey);
|
|
16225
16710
|
const authoringContextElapsedMs = Math.max(0, Date.now() - authoringContextStartedAt);
|
|
16226
16711
|
const runtimeObservationsStartedAt = Date.now();
|
|
16227
16712
|
const runtimeObservationResult = await this.collectRuntimeComponentObservations(request);
|
|
@@ -16263,6 +16748,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16263
16748
|
attachmentCount: authoringContext.attachmentSummaries?.length ?? 0,
|
|
16264
16749
|
hasPendingClarification: !!authoringContext.pendingClarification,
|
|
16265
16750
|
hasContextHints: !!authoringContext.contextHints,
|
|
16751
|
+
hasSelectedComponentValidationContext: !!this.toJsonObject(authoringContext.contextHints?.['validationContext']),
|
|
16752
|
+
selectedComponentContextDiagnosticCount: Array.isArray(authoringContext.contextHints?.['contextDiagnostics'])
|
|
16753
|
+
? authoringContext.contextHints['contextDiagnostics'].length
|
|
16754
|
+
: 0,
|
|
16266
16755
|
runtimeObservationCount: runtimeObservationResult.observations.length,
|
|
16267
16756
|
runtimeObservationSource: runtimeObservationResult.source,
|
|
16268
16757
|
runtimeObservationUnavailable: runtimeObservationResult.unavailable,
|
|
@@ -16295,10 +16784,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16295
16784
|
unavailable: false,
|
|
16296
16785
|
};
|
|
16297
16786
|
}
|
|
16298
|
-
async toTurnResultFromStreamEvent(event, request, prompt) {
|
|
16787
|
+
async toTurnResultFromStreamEvent(event, request, prompt, preparedContextHints) {
|
|
16299
16788
|
const payload = this.toJsonObject(event.payload) ?? {};
|
|
16300
16789
|
if (event.type === 'result') {
|
|
16301
|
-
return this.toResultTurnFromStreamPayload(payload, request, prompt, event);
|
|
16790
|
+
return this.toResultTurnFromStreamPayload(payload, request, prompt, event, preparedContextHints);
|
|
16302
16791
|
}
|
|
16303
16792
|
if (event.type === 'intent.resolved') {
|
|
16304
16793
|
const assistantMessage = this.readString(payload, 'userFacingUnderstanding')?.trim()
|
|
@@ -16351,7 +16840,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16351
16840
|
preview: null,
|
|
16352
16841
|
};
|
|
16353
16842
|
}
|
|
16354
|
-
async toResultTurnFromStreamPayload(payload, request, prompt, event) {
|
|
16843
|
+
async toResultTurnFromStreamPayload(payload, request, prompt, event, preparedContextHints) {
|
|
16355
16844
|
const intentResolution = this.normalizeIntentResolutionResult(payload['intentResolution']);
|
|
16356
16845
|
const preview = payload['preview'];
|
|
16357
16846
|
if (!intentResolution) {
|
|
@@ -16383,13 +16872,13 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16383
16872
|
? this.toExecutableContextualComponentIntent(intentResolution, request)
|
|
16384
16873
|
: null;
|
|
16385
16874
|
if (!canApply && contextualExecutableIntent && request && prompt) {
|
|
16386
|
-
return this.completeExecutableStreamPreview(request, prompt, contextualExecutableIntent, preview);
|
|
16875
|
+
return this.completeExecutableStreamPreview(request, prompt, contextualExecutableIntent, preview, preparedContextHints);
|
|
16387
16876
|
}
|
|
16388
16877
|
if (canApply
|
|
16389
16878
|
&& request
|
|
16390
16879
|
&& prompt
|
|
16391
16880
|
&& this.shouldRegenerateStreamPreview(intentResolution, preview)) {
|
|
16392
|
-
return this.completeExecutableStreamPreview(request, prompt, intentResolution, undefined);
|
|
16881
|
+
return this.completeExecutableStreamPreview(request, prompt, intentResolution, undefined, preparedContextHints);
|
|
16393
16882
|
}
|
|
16394
16883
|
if (!canApply) {
|
|
16395
16884
|
if (preview && this.hasMaterializablePreview(preview) && this.isGovernedBlockedStreamPreview(payload, preview)) {
|
|
@@ -16436,7 +16925,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16436
16925
|
&& !this.hasBlockingResourceQuickReply(quickReplies)
|
|
16437
16926
|
&& request
|
|
16438
16927
|
&& prompt) {
|
|
16439
|
-
return this.completeExecutableStreamPreview(request, prompt, intentResolution, preview);
|
|
16928
|
+
return this.completeExecutableStreamPreview(request, prompt, intentResolution, preview, preparedContextHints);
|
|
16440
16929
|
}
|
|
16441
16930
|
const requiresChoice = quickReplies.length > 0 && !this.isConsultativeCatalogIntent(intentResolution);
|
|
16442
16931
|
return {
|
|
@@ -16496,11 +16985,17 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16496
16985
|
return !!this.readString(contextHints, 'resourcePath') || this.isResourceQuickReply(reply);
|
|
16497
16986
|
});
|
|
16498
16987
|
}
|
|
16499
|
-
async completeExecutableStreamPreview(request, prompt, intentResolution, streamPreview) {
|
|
16988
|
+
async completeExecutableStreamPreview(request, prompt, intentResolution, streamPreview, preparedContextHints) {
|
|
16500
16989
|
try {
|
|
16501
|
-
const
|
|
16502
|
-
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
16990
|
+
const baseAuthoringContext = this.buildAuthoringContext(request, prompt);
|
|
16503
16991
|
const selectedWidgetKey = this.context.selectedWidgetKey();
|
|
16992
|
+
const authoringContext = preparedContextHints
|
|
16993
|
+
? {
|
|
16994
|
+
...baseAuthoringContext,
|
|
16995
|
+
contextHints: this.cloneJsonObject(this.toJsonObject(preparedContextHints)),
|
|
16996
|
+
}
|
|
16997
|
+
: await this.withSelectedComponentAuthoringContext(baseAuthoringContext, selectedWidgetKey);
|
|
16998
|
+
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
16504
16999
|
const effectiveSelectedWidgetKey = this.resolveSelectedWidgetKey(selectedWidgetKey, authoringContext.contextHints);
|
|
16505
17000
|
const reusableStreamPreview = streamPreview?.valid
|
|
16506
17001
|
&& !this.shouldRegenerateStreamPreview(intentResolution, streamPreview);
|
|
@@ -16645,9 +17140,13 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16645
17140
|
const selectedCandidate = this.toJsonObject(intentResolution.selectedCandidate);
|
|
16646
17141
|
const widgets = this.extractDiagnosticWidgets(plan, page);
|
|
16647
17142
|
const connections = this.extractDiagnosticConnections(plan, page);
|
|
16648
|
-
const surfaces =
|
|
17143
|
+
const surfaces = [
|
|
17144
|
+
...widgets.flatMap((widget) => widget.surfaces),
|
|
17145
|
+
...this.extractSurfaceActionsFromConnections(connections),
|
|
17146
|
+
];
|
|
16649
17147
|
const blueprint = this.extractDashboardBlueprint(intentResolution, preview, plan, page);
|
|
16650
|
-
const
|
|
17148
|
+
const detailStrategy = this.readString(this.toJsonObject(plan?.['layoutPresetOptions']) ?? {}, 'detailStrategy') ?? this.readString(this.toJsonObject(page?.['layoutPresetOptions']) ?? {}, 'detailStrategy');
|
|
17149
|
+
const validation = this.buildDashboardValidationSummary(widgets, connections, surfaces, detailStrategy !== 'aggregate-only');
|
|
16651
17150
|
const plannerDiagnostics = this.extractDashboardPlannerDiagnostics(plan);
|
|
16652
17151
|
return {
|
|
16653
17152
|
schemaVersion: 'praxis-dashboard-authoring-diagnostics.v1',
|
|
@@ -16841,15 +17340,15 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16841
17340
|
?? this.toJsonObject(page?.['layoutPresetOptions']);
|
|
16842
17341
|
return {
|
|
16843
17342
|
name: this.readString(diagnostics ?? {}, 'blueprint')
|
|
16844
|
-
|
|
16845
|
-
|
|
16846
|
-
|
|
17343
|
+
|| this.readString(layoutPresetOptions ?? {}, 'blueprint')
|
|
17344
|
+
|| this.readString(layoutPresetOptions ?? {}, 'presetFamily')
|
|
17345
|
+
|| null,
|
|
16847
17346
|
layoutPreset: this.readString(plan ?? {}, 'layoutPreset')
|
|
16848
|
-
|
|
17347
|
+
|| this.readString(page ?? {}, 'layoutPreset'),
|
|
16849
17348
|
layoutPresetOptions,
|
|
16850
17349
|
themePreset: this.readString(plan ?? {}, 'themePreset')
|
|
16851
|
-
|
|
16852
|
-
selectedBy: intentResolution.authoringProfile
|
|
17350
|
+
|| this.readString(page ?? {}, 'themePreset'),
|
|
17351
|
+
selectedBy: intentResolution.authoringProfile || intentResolution.changeKind || null,
|
|
16853
17352
|
};
|
|
16854
17353
|
}
|
|
16855
17354
|
extractDiagnosticWidgets(plan, page) {
|
|
@@ -16863,10 +17362,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16863
17362
|
.filter((widget) => {
|
|
16864
17363
|
const definition = this.toJsonObject(widget['definition']);
|
|
16865
17364
|
const key = this.readString(widget, 'key')
|
|
16866
|
-
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
|
|
17365
|
+
|| this.readString(widget, 'id')
|
|
17366
|
+
|| this.readString(definition ?? {}, 'key')
|
|
17367
|
+
|| this.readString(definition ?? {}, 'id')
|
|
17368
|
+
|| '';
|
|
16870
17369
|
if (!key) {
|
|
16871
17370
|
return true;
|
|
16872
17371
|
}
|
|
@@ -16885,10 +17384,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16885
17384
|
?? this.toJsonObject(widget['config'])
|
|
16886
17385
|
?? {};
|
|
16887
17386
|
const componentId = this.readString(widget, 'componentId')
|
|
16888
|
-
|
|
16889
|
-
|
|
16890
|
-
|
|
16891
|
-
const key = this.readString(widget, 'key')
|
|
17387
|
+
|| this.readString(definition ?? {}, 'id')
|
|
17388
|
+
|| this.readString(widget, 'type')
|
|
17389
|
+
|| 'unknown';
|
|
17390
|
+
const key = this.readString(widget, 'key') || this.readString(widget, 'id') || 'unknown';
|
|
16892
17391
|
const shell = this.toJsonObject(widget['shell']);
|
|
16893
17392
|
const surfaces = this.extractSurfacesFromInputs(key, inputs);
|
|
16894
17393
|
return {
|
|
@@ -16896,13 +17395,13 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16896
17395
|
componentId,
|
|
16897
17396
|
role: this.readString(widget, 'role'),
|
|
16898
17397
|
title: this.readString(inputs, 'title')
|
|
16899
|
-
|
|
16900
|
-
|
|
17398
|
+
|| this.readString(inputs, 'label')
|
|
17399
|
+
|| this.readString(shell ?? {}, 'title'),
|
|
16901
17400
|
subtitle: this.readString(inputs, 'subtitle')
|
|
16902
|
-
|
|
17401
|
+
|| this.readString(shell ?? {}, 'subtitle'),
|
|
16903
17402
|
resourcePath: this.readString(inputs, 'resourcePath')
|
|
16904
|
-
|
|
16905
|
-
|
|
17403
|
+
|| this.readString(inputs, 'apiEndpoint')
|
|
17404
|
+
|| this.readString(inputs, 'url'),
|
|
16906
17405
|
schemaUrl: this.readString(inputs, 'schemaUrl'),
|
|
16907
17406
|
kpiLike: this.isDashboardKpiWidget(componentId, widget, inputs, shell),
|
|
16908
17407
|
filterLike: this.isDashboardFilterWidget(componentId, widget, inputs, shell),
|
|
@@ -16919,7 +17418,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16919
17418
|
return (planBindings ?? pageLinks ?? [])
|
|
16920
17419
|
.filter((connection) => !!this.toJsonObject(connection))
|
|
16921
17420
|
.map((connection) => ({
|
|
16922
|
-
id: this.readString(connection, 'id')
|
|
17421
|
+
id: this.readString(connection, 'id') || 'unknown',
|
|
16923
17422
|
intent: this.readString(connection, 'intent'),
|
|
16924
17423
|
from: this.summarizeEndpoint(connection['from']),
|
|
16925
17424
|
to: this.summarizeEndpoint(connection['to']),
|
|
@@ -16928,41 +17427,78 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
16928
17427
|
}
|
|
16929
17428
|
extractSurfacesFromInputs(widgetKey, inputs) {
|
|
16930
17429
|
const surfaces = [];
|
|
16931
|
-
const
|
|
16932
|
-
|
|
16933
|
-
|
|
16934
|
-
|
|
16935
|
-
...this.arrayFromUnknown(
|
|
16936
|
-
...this.arrayFromUnknown(
|
|
16937
|
-
|
|
17430
|
+
const config = this.toJsonObject(inputs['config']);
|
|
17431
|
+
const candidates = [inputs, config]
|
|
17432
|
+
.filter((scope) => !!scope)
|
|
17433
|
+
.flatMap((scope) => [
|
|
17434
|
+
...this.arrayFromUnknown(scope['surfaces']),
|
|
17435
|
+
...this.arrayFromUnknown(this.toJsonObject(scope['recordSurfaces'])?.['surfaces']),
|
|
17436
|
+
...this.arrayFromUnknown(scope['actions']),
|
|
17437
|
+
...this.arrayFromUnknown(scope['rowActions']),
|
|
17438
|
+
...this.arrayFromUnknown(scope['toolbarActions']),
|
|
17439
|
+
]);
|
|
16938
17440
|
for (const candidate of candidates) {
|
|
16939
17441
|
const surface = this.toJsonObject(candidate);
|
|
16940
17442
|
if (!surface) {
|
|
16941
17443
|
continue;
|
|
16942
17444
|
}
|
|
17445
|
+
const globalAction = this.toJsonObject(surface['globalAction']);
|
|
17446
|
+
const globalActionPayload = this.toJsonObject(globalAction?.['payload']);
|
|
16943
17447
|
const action = this.readString(surface, 'action')
|
|
16944
|
-
|
|
16945
|
-
|
|
16946
|
-
|
|
16947
|
-
const
|
|
16948
|
-
|
|
17448
|
+
|| this.readString(surface, 'actionId')
|
|
17449
|
+
|| this.readString(surface, 'operationId')
|
|
17450
|
+
|| this.readString(globalAction ?? {}, 'actionId');
|
|
17451
|
+
const resourceSurface = this.toJsonObject(surface['resourceSurface'])
|
|
17452
|
+
?? this.toJsonObject(globalActionPayload?.['resourceSurface']);
|
|
17453
|
+
const widget = this.toJsonObject(surface['widget'])
|
|
17454
|
+
?? this.toJsonObject(globalActionPayload?.['widget']);
|
|
17455
|
+
const recordOpen = this.toJsonObject(surface['recordOpen']);
|
|
17456
|
+
const recordOpenTarget = this.toJsonObject(recordOpen?.['target']);
|
|
17457
|
+
if (!this.isSurfaceOpenAction(action) && !resourceSurface && !widget && !recordOpen) {
|
|
16949
17458
|
continue;
|
|
16950
17459
|
}
|
|
17460
|
+
const widgetInputs = this.toJsonObject(widget?.['inputs'])
|
|
17461
|
+
?? this.toJsonObject(this.toJsonObject(widget?.['definition'])?.['inputs']);
|
|
16951
17462
|
surfaces.push({
|
|
16952
17463
|
widgetKey,
|
|
16953
|
-
id: this.readString(surface, 'id')
|
|
17464
|
+
id: this.readString(surface, 'id') || this.readString(resourceSurface ?? {}, 'id'),
|
|
16954
17465
|
action,
|
|
16955
|
-
title: this.readString(surface, 'title')
|
|
17466
|
+
title: this.readString(surface, 'title')
|
|
17467
|
+
|| this.readString(resourceSurface ?? {}, 'title')
|
|
17468
|
+
|| this.readString(globalActionPayload ?? {}, 'title'),
|
|
16956
17469
|
mode: this.readString(resourceSurface ?? {}, 'mode'),
|
|
16957
17470
|
presentation: this.readString(surface, 'presentation')
|
|
16958
|
-
|
|
16959
|
-
|
|
17471
|
+
|| this.readString(resourceSurface ?? {}, 'presentation')
|
|
17472
|
+
|| this.readString(globalActionPayload ?? {}, 'presentation'),
|
|
17473
|
+
resourcePath: this.readString(resourceSurface ?? {}, 'resourcePath')
|
|
17474
|
+
|| this.readString(widgetInputs ?? {}, 'resourcePath'),
|
|
17475
|
+
resourceKey: this.readString(recordOpenTarget ?? {}, 'resourceKey'),
|
|
17476
|
+
surfaceId: this.readString(recordOpenTarget ?? {}, 'surfaceId'),
|
|
17477
|
+
sourceIdentityField: this.readString(recordOpen ?? {}, 'sourceIdentityField'),
|
|
16960
17478
|
componentId: this.readString(widget ?? {}, 'id')
|
|
16961
|
-
|
|
17479
|
+
|| this.readString(this.toJsonObject(widget?.['definition']) ?? {}, 'id'),
|
|
16962
17480
|
});
|
|
16963
17481
|
}
|
|
16964
17482
|
return surfaces;
|
|
16965
17483
|
}
|
|
17484
|
+
extractSurfaceActionsFromConnections(connections) {
|
|
17485
|
+
return connections.flatMap((connection) => {
|
|
17486
|
+
const from = this.toJsonObject(connection['from']);
|
|
17487
|
+
const to = this.toJsonObject(connection['to']);
|
|
17488
|
+
const action = this.readString(to ?? {}, 'actionId');
|
|
17489
|
+
if (this.readString(to ?? {}, 'kind') !== 'global-action' || !this.isSurfaceOpenAction(action)) {
|
|
17490
|
+
return [];
|
|
17491
|
+
}
|
|
17492
|
+
return [{
|
|
17493
|
+
widgetKey: this.readString(from ?? {}, 'widget'),
|
|
17494
|
+
id: this.readString(this.toJsonObject(connection), 'id'),
|
|
17495
|
+
action,
|
|
17496
|
+
}];
|
|
17497
|
+
});
|
|
17498
|
+
}
|
|
17499
|
+
isSurfaceOpenAction(action) {
|
|
17500
|
+
return action === 'surface.open' || action === 'dynamicPage.surface.open';
|
|
17501
|
+
}
|
|
16966
17502
|
isDashboardKpiWidget(componentId, widget, inputs, shell) {
|
|
16967
17503
|
const role = this.readString(widget, 'role')?.toLowerCase() ?? '';
|
|
16968
17504
|
if (['kpi', 'metric', 'metrics', 'indicator', 'indicators', 'summary'].includes(role)) {
|
|
@@ -17048,7 +17584,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17048
17584
|
}
|
|
17049
17585
|
const value = this.readString(node, 'value')?.trim() ?? '';
|
|
17050
17586
|
if (!value || value === '-' || value === '--' || value.toLowerCase() === 'n/a') {
|
|
17051
|
-
const label = this.readString(node, 'label')
|
|
17587
|
+
const label = this.readString(node, 'label') || this.readString(node, 'id') || 'metric';
|
|
17052
17588
|
placeholders.push(label);
|
|
17053
17589
|
}
|
|
17054
17590
|
}
|
|
@@ -17081,7 +17617,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17081
17617
|
|| inputs['filterConfig'] !== undefined
|
|
17082
17618
|
|| inputs['advancedOpenMode'] !== undefined;
|
|
17083
17619
|
}
|
|
17084
|
-
buildDashboardValidationSummary(widgets, connections, surfaces) {
|
|
17620
|
+
buildDashboardValidationSummary(widgets, connections, surfaces, requiresNominalDetails) {
|
|
17085
17621
|
const componentIds = widgets.map((widget) => `${widget['componentId']}`);
|
|
17086
17622
|
const filterWidgets = widgets.filter((widget) => widget['filterLike'] === true);
|
|
17087
17623
|
const chartWidgets = widgets.filter((widget) => `${widget['componentId']}`.includes('chart'));
|
|
@@ -17089,7 +17625,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17089
17625
|
if (!componentIds.some((id) => id.includes('chart'))) {
|
|
17090
17626
|
warnings.push('dashboard-without-chart-widget');
|
|
17091
17627
|
}
|
|
17092
|
-
if (!widgets.some((widget) => widget['kpiLike'] === true)) {
|
|
17628
|
+
if (requiresNominalDetails && !widgets.some((widget) => widget['kpiLike'] === true)) {
|
|
17093
17629
|
warnings.push('dashboard-without-kpi-widget');
|
|
17094
17630
|
}
|
|
17095
17631
|
if (widgets.some((widget) => widget['kpiLike'] === true
|
|
@@ -17100,7 +17636,8 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17100
17636
|
if (!widgets.some((widget) => widget['filterLike'] === true)) {
|
|
17101
17637
|
warnings.push('dashboard-without-filter-widget');
|
|
17102
17638
|
}
|
|
17103
|
-
if (
|
|
17639
|
+
if (requiresNominalDetails
|
|
17640
|
+
&& !componentIds.some((id) => id.includes('table') || id.includes('list'))) {
|
|
17104
17641
|
warnings.push('dashboard-without-record-detail-widget');
|
|
17105
17642
|
}
|
|
17106
17643
|
if (!connections.length) {
|
|
@@ -17110,12 +17647,13 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17110
17647
|
&& !filterWidgets.some((widget) => this.hasQueryContextConnectionFrom(`${widget['key']}`, connections))) {
|
|
17111
17648
|
warnings.push('dashboard-filter-not-connected');
|
|
17112
17649
|
}
|
|
17113
|
-
if (
|
|
17650
|
+
if (requiresNominalDetails
|
|
17651
|
+
&& chartWidgets.length
|
|
17114
17652
|
&& !chartWidgets.some((widget) => this.hasQueryContextConnectionFrom(`${widget['key']}`, connections)
|
|
17115
17653
|
|| surfaces.some((surface) => surface['widgetKey'] === widget['key']))) {
|
|
17116
17654
|
warnings.push('dashboard-chart-not-interactive');
|
|
17117
17655
|
}
|
|
17118
|
-
if (!surfaces.length) {
|
|
17656
|
+
if (requiresNominalDetails && !surfaces.length) {
|
|
17119
17657
|
warnings.push('dashboard-without-surface-actions');
|
|
17120
17658
|
}
|
|
17121
17659
|
return {
|
|
@@ -17154,11 +17692,11 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17154
17692
|
const ref = this.toJsonObject(endpoint['ref']);
|
|
17155
17693
|
return {
|
|
17156
17694
|
kind: this.readString(endpoint, 'kind'),
|
|
17157
|
-
widget: this.readString(endpoint, 'widget')
|
|
17158
|
-
port: this.readString(endpoint, 'port')
|
|
17159
|
-
direction: this.readString(endpoint, 'direction')
|
|
17160
|
-
path: this.readString(endpoint, 'path')
|
|
17161
|
-
actionId: this.readString(endpoint, 'actionId')
|
|
17695
|
+
widget: this.readString(endpoint, 'widget') || this.readString(ref ?? {}, 'widget'),
|
|
17696
|
+
port: this.readString(endpoint, 'port') || this.readString(ref ?? {}, 'port'),
|
|
17697
|
+
direction: this.readString(endpoint, 'direction') || this.readString(ref ?? {}, 'direction'),
|
|
17698
|
+
path: this.readString(endpoint, 'path') || this.readString(ref ?? {}, 'path'),
|
|
17699
|
+
actionId: this.readString(endpoint, 'actionId') || this.readString(ref ?? {}, 'actionId'),
|
|
17162
17700
|
};
|
|
17163
17701
|
}
|
|
17164
17702
|
summarizeTransform(value) {
|
|
@@ -17231,31 +17769,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17231
17769
|
&& replies.length === 0) {
|
|
17232
17770
|
return [this.dashboardQualityFallbackQuickReply(intentResolution, preview)];
|
|
17233
17771
|
}
|
|
17234
|
-
return replies
|
|
17235
|
-
? replies
|
|
17236
|
-
: [this.governedReviewFallbackQuickReply(intentResolution)];
|
|
17237
|
-
}
|
|
17238
|
-
governedReviewFallbackQuickReply(intentResolution) {
|
|
17239
|
-
const selectedCandidate = this.toJsonObject(intentResolution.selectedCandidate);
|
|
17240
|
-
const target = this.toJsonObject(intentResolution.target);
|
|
17241
|
-
const resourcePath = this.readString(selectedCandidate, 'resourcePath')
|
|
17242
|
-
|| this.readString(target, 'resourcePath')
|
|
17243
|
-
|| '';
|
|
17244
|
-
const label = this.context.tx('agentic.governedReview.quickReply.revise', 'Revisar pontos pendentes');
|
|
17245
|
-
return {
|
|
17246
|
-
id: 'governed-review-revise',
|
|
17247
|
-
kind: 'revise',
|
|
17248
|
-
label,
|
|
17249
|
-
prompt: this.context.tx('agentic.governedReview.prompt.revise', 'Revise a previa bloqueada, explique os pontos que ainda precisam de decisao e proponha um ajuste seguro antes de salvar.'),
|
|
17250
|
-
icon: 'rate_review',
|
|
17251
|
-
contextHints: {
|
|
17252
|
-
source: 'governed-review-gate',
|
|
17253
|
-
kind: 'governed-review-repair',
|
|
17254
|
-
artifactKind: intentResolution.artifactKind,
|
|
17255
|
-
resourcePath,
|
|
17256
|
-
requiresReview: true,
|
|
17257
|
-
},
|
|
17258
|
-
};
|
|
17772
|
+
return replies;
|
|
17259
17773
|
}
|
|
17260
17774
|
describeDashboardQualityWarning(code) {
|
|
17261
17775
|
switch (code) {
|
|
@@ -17419,6 +17933,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17419
17933
|
const item = this.toJsonObject(widget);
|
|
17420
17934
|
const componentId = this.readString(item ?? {}, 'componentId')
|
|
17421
17935
|
|| this.readString(item ?? {}, 'type')
|
|
17936
|
+
|| this.readString(this.toJsonObject(item?.['definition']) ?? {}, 'id')
|
|
17422
17937
|
|| this.readString(this.toJsonObject(item?.['definition']) ?? {}, 'componentId')
|
|
17423
17938
|
|| this.readString(this.toJsonObject(item?.['definition']) ?? {}, 'type');
|
|
17424
17939
|
return componentId === 'praxis-chart'
|
|
@@ -17461,10 +17976,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17461
17976
|
counts: this.toJsonObject(validation?.['counts']) ?? {},
|
|
17462
17977
|
},
|
|
17463
17978
|
widgets: widgets.slice(0, 12).map((widget) => {
|
|
17464
|
-
const key = this.readString(widget, 'key')
|
|
17979
|
+
const key = this.readString(widget, 'key') || 'unknown';
|
|
17465
17980
|
return {
|
|
17466
17981
|
key,
|
|
17467
|
-
componentId: this.readString(widget, 'componentId')
|
|
17982
|
+
componentId: this.readString(widget, 'componentId') || 'unknown',
|
|
17468
17983
|
role: this.readString(widget, 'role'),
|
|
17469
17984
|
title: this.readString(widget, 'title'),
|
|
17470
17985
|
kpiLike: widget['kpiLike'] === true,
|
|
@@ -17475,7 +17990,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17475
17990
|
};
|
|
17476
17991
|
}),
|
|
17477
17992
|
connections: connections.slice(0, 16).map((connection) => ({
|
|
17478
|
-
id: this.readString(connection, 'id')
|
|
17993
|
+
id: this.readString(connection, 'id') || 'unknown',
|
|
17479
17994
|
intent: this.readString(connection, 'intent'),
|
|
17480
17995
|
from: this.toJsonObject(connection['from']),
|
|
17481
17996
|
to: this.toJsonObject(connection['to']),
|
|
@@ -17875,7 +18390,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
17875
18390
|
return null;
|
|
17876
18391
|
}
|
|
17877
18392
|
const componentCapabilities = await this.context.loadComponentCapabilities();
|
|
17878
|
-
const authoringContext = this.buildAuthoringContext(request);
|
|
18393
|
+
const authoringContext = this.buildAuthoringContext(request, prompt);
|
|
17879
18394
|
const intentResolution = await firstValueFrom(this.service.resolveIntent(this.buildIntentResolutionRequest(prompt, this.context.selectedWidgetKey(), componentCapabilities, {
|
|
17880
18395
|
...authoringContext,
|
|
17881
18396
|
contextHints: this.resourceDiscoveryContextHints(contextHints, result),
|
|
@@ -18152,6 +18667,26 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
18152
18667
|
contextualPreviewActionContextFromRequest(request) {
|
|
18153
18668
|
const explicit = this.toJsonObject(request?.action?.contextHints);
|
|
18154
18669
|
if (this.isContextualPreviewActionContext(explicit)) {
|
|
18670
|
+
const source = this.readString(explicit, 'source');
|
|
18671
|
+
const kind = this.readString(explicit, 'kind');
|
|
18672
|
+
if (source === 'dashboard-quality-gate' || kind === 'dashboard-repair-action') {
|
|
18673
|
+
const currentPage = this.cloneJsonObject(this.toJsonObject(this.context.currentPage()));
|
|
18674
|
+
const actionId = request?.action?.id?.trim() ?? '';
|
|
18675
|
+
const changeKind = this.readString(explicit, 'changeKind')
|
|
18676
|
+
|| this.dashboardQualityChangeKindFromActionId(actionId);
|
|
18677
|
+
return {
|
|
18678
|
+
...explicit,
|
|
18679
|
+
source: 'dashboard-quality-gate',
|
|
18680
|
+
kind: 'dashboard-repair-action',
|
|
18681
|
+
operationKind: 'modify',
|
|
18682
|
+
artifactKind: 'dashboard',
|
|
18683
|
+
...(changeKind ? { changeKind } : {}),
|
|
18684
|
+
resourcePath: this.readString(explicit, 'resourcePath')
|
|
18685
|
+
|| this.dashboardFirstResourcePath(currentPage)
|
|
18686
|
+
|| '',
|
|
18687
|
+
...(this.hasPageWidgets(currentPage) ? { materializedPage: currentPage } : {}),
|
|
18688
|
+
};
|
|
18689
|
+
}
|
|
18155
18690
|
return explicit;
|
|
18156
18691
|
}
|
|
18157
18692
|
const actionId = request?.action?.id?.trim();
|
|
@@ -18521,6 +19056,8 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
18521
19056
|
tone: reply.tone,
|
|
18522
19057
|
presentation: null,
|
|
18523
19058
|
contextHints: this.toJsonObject(reply.contextHints),
|
|
19059
|
+
semanticDecision: this.toJsonObject(reply.semanticDecision),
|
|
19060
|
+
value: reply.value,
|
|
18524
19061
|
}));
|
|
18525
19062
|
}
|
|
18526
19063
|
contextualCapabilityFallbackQuickReplies(intentResolution) {
|
|
@@ -18769,6 +19306,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
18769
19306
|
const kind = (reply.kind || '').trim().toLowerCase();
|
|
18770
19307
|
const id = (reply.id || '').trim().toLowerCase();
|
|
18771
19308
|
const contextHints = this.toJsonObject(reply.contextHints);
|
|
19309
|
+
const contextKind = this.readString(contextHints, 'kind');
|
|
19310
|
+
if (kind === 'revise' || contextKind === 'governed-review-repair') {
|
|
19311
|
+
return false;
|
|
19312
|
+
}
|
|
18772
19313
|
if (this.isContextualPreviewActionReply(reply, contextHints)) {
|
|
18773
19314
|
return false;
|
|
18774
19315
|
}
|
|
@@ -18913,12 +19454,12 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
18913
19454
|
resolveEffectivePrompt(intentResolution, fallbackPrompt) {
|
|
18914
19455
|
return intentResolution.effectivePrompt?.trim() || fallbackPrompt;
|
|
18915
19456
|
}
|
|
18916
|
-
buildAuthoringContext(request) {
|
|
19457
|
+
buildAuthoringContext(request, currentPrompt) {
|
|
18917
19458
|
const contextHints = this.buildContextHints(request);
|
|
18918
19459
|
return {
|
|
18919
19460
|
sessionId: request.sessionId,
|
|
18920
19461
|
clientTurnId: request.clientTurnId,
|
|
18921
|
-
conversationMessages: this.toConversationMessages(request.messages ?? []),
|
|
19462
|
+
conversationMessages: this.toConversationMessages(request.messages ?? [], currentPrompt),
|
|
18922
19463
|
pendingClarification: this.toPendingClarification(request.pendingClarification),
|
|
18923
19464
|
attachmentSummaries: this.toAttachmentSummaries(request.attachments ?? []),
|
|
18924
19465
|
contextHints,
|
|
@@ -18962,6 +19503,28 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
18962
19503
|
includeLlmDiagnostics: true,
|
|
18963
19504
|
};
|
|
18964
19505
|
}
|
|
19506
|
+
async withSelectedComponentAuthoringContext(authoringContext, selectedWidgetKey) {
|
|
19507
|
+
const resolver = this.context.resolveSelectedComponentAuthoringContext;
|
|
19508
|
+
if (!selectedWidgetKey || !resolver) {
|
|
19509
|
+
return authoringContext;
|
|
19510
|
+
}
|
|
19511
|
+
const resolved = await resolver(selectedWidgetKey);
|
|
19512
|
+
if (!resolved) {
|
|
19513
|
+
return authoringContext;
|
|
19514
|
+
}
|
|
19515
|
+
const contextHints = this.toJsonObject(authoringContext.contextHints) ?? {};
|
|
19516
|
+
return {
|
|
19517
|
+
...authoringContext,
|
|
19518
|
+
contextHints: {
|
|
19519
|
+
...contextHints,
|
|
19520
|
+
selectedWidgetKey: resolved.widgetKey,
|
|
19521
|
+
selectedComponentId: resolved.selectedComponentId,
|
|
19522
|
+
authoringManifestRef: this.cloneJsonObject(resolved.authoringManifestRef) ?? {},
|
|
19523
|
+
validationContext: this.cloneJsonObject(resolved.validationContext) ?? {},
|
|
19524
|
+
contextDiagnostics: resolved.contextDiagnostics.map((diagnostic) => this.cloneJsonObject(diagnostic) ?? {}),
|
|
19525
|
+
},
|
|
19526
|
+
};
|
|
19527
|
+
}
|
|
18965
19528
|
normalizeAmbientDomainCatalogContextHints(contextHints) {
|
|
18966
19529
|
if (!contextHints) {
|
|
18967
19530
|
return null;
|
|
@@ -19010,8 +19573,25 @@ class PageBuilderAgenticAuthoringTurnFlow {
|
|
|
19010
19573
|
hasPreview: !!attachment.previewUrl,
|
|
19011
19574
|
}));
|
|
19012
19575
|
}
|
|
19013
|
-
toConversationMessages(messages) {
|
|
19014
|
-
|
|
19576
|
+
toConversationMessages(messages, currentPrompt) {
|
|
19577
|
+
const conversationMessages = toPraxisAssistantConversationMessages(messages, 12);
|
|
19578
|
+
for (let index = conversationMessages.length - 1; index >= 0; index -= 1) {
|
|
19579
|
+
const message = conversationMessages[index];
|
|
19580
|
+
if (message.role !== 'user') {
|
|
19581
|
+
continue;
|
|
19582
|
+
}
|
|
19583
|
+
if (this.sameTransportPrompt(message.text, currentPrompt)) {
|
|
19584
|
+
// Transport deduplication only; semantic intent remains exclusively backend-authored.
|
|
19585
|
+
conversationMessages.splice(index, 1);
|
|
19586
|
+
}
|
|
19587
|
+
break;
|
|
19588
|
+
}
|
|
19589
|
+
return conversationMessages;
|
|
19590
|
+
}
|
|
19591
|
+
sameTransportPrompt(left, right) {
|
|
19592
|
+
const compactLeft = left.trim().replace(/\s+/g, ' ').toLocaleLowerCase();
|
|
19593
|
+
const compactRight = right.trim().replace(/\s+/g, ' ').toLocaleLowerCase();
|
|
19594
|
+
return !!compactLeft && compactLeft === compactRight;
|
|
19015
19595
|
}
|
|
19016
19596
|
toPendingClarification(pending) {
|
|
19017
19597
|
if (!pending?.sourcePrompt) {
|
|
@@ -19247,7 +19827,9 @@ class DynamicPageBuilderComponent {
|
|
|
19247
19827
|
agenticAuthoringStatus = signal('', ...(ngDevMode ? [{ debugName: "agenticAuthoringStatus" }] : /* istanbul ignore next */ []));
|
|
19248
19828
|
agenticAuthoringError = signal('', ...(ngDevMode ? [{ debugName: "agenticAuthoringError" }] : /* istanbul ignore next */ []));
|
|
19249
19829
|
agenticAuthoringPreviewResult = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringPreviewResult" }] : /* istanbul ignore next */ []));
|
|
19830
|
+
agenticAuthoringMaterializationPending = signal(false, ...(ngDevMode ? [{ debugName: "agenticAuthoringMaterializationPending" }] : /* istanbul ignore next */ []));
|
|
19250
19831
|
agenticAuthoringSemanticDecision = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringSemanticDecision" }] : /* istanbul ignore next */ []));
|
|
19832
|
+
agenticAuthoringTurnResultRef = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringTurnResultRef" }] : /* istanbul ignore next */ []));
|
|
19251
19833
|
agenticAuthoringCanApply = signal(false, ...(ngDevMode ? [{ debugName: "agenticAuthoringCanApply" }] : /* istanbul ignore next */ []));
|
|
19252
19834
|
agenticAuthoringLastEtag = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringLastEtag" }] : /* istanbul ignore next */ []));
|
|
19253
19835
|
agenticAuthoringConversation = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringConversation" }] : /* istanbul ignore next */ []));
|
|
@@ -19305,7 +19887,19 @@ class DynamicPageBuilderComponent {
|
|
|
19305
19887
|
if (changes['page']) {
|
|
19306
19888
|
this.currentPage.set(this.parsePage(this.page) || { widgets: [] });
|
|
19307
19889
|
}
|
|
19308
|
-
|
|
19890
|
+
const pageIdentityChange = changes['pageIdentity'];
|
|
19891
|
+
const componentInstanceChange = changes['componentInstanceId'];
|
|
19892
|
+
const authoringOwnerChanged = (pageIdentityChange
|
|
19893
|
+
&& !pageIdentityChange.isFirstChange()
|
|
19894
|
+
&& this.pageIdentityFingerprint(pageIdentityChange.previousValue)
|
|
19895
|
+
!== this.pageIdentityFingerprint(pageIdentityChange.currentValue)) || (componentInstanceChange
|
|
19896
|
+
&& !componentInstanceChange.isFirstChange()
|
|
19897
|
+
&& `${componentInstanceChange.previousValue ?? ''}`.trim()
|
|
19898
|
+
!== `${componentInstanceChange.currentValue ?? ''}`.trim());
|
|
19899
|
+
if (authoringOwnerChanged) {
|
|
19900
|
+
this.resetAgenticAuthoringSessionState(this.resolveAgenticAuthoringSessionId(pageIdentityChange?.previousValue ?? this.pageIdentity, componentInstanceChange?.previousValue ?? this.componentInstanceId));
|
|
19901
|
+
}
|
|
19902
|
+
else if (changes['pageLifecycleResetRevision'] && !changes['pageLifecycleResetRevision'].isFirstChange()) {
|
|
19309
19903
|
this.resetAgenticAuthoringSessionState();
|
|
19310
19904
|
}
|
|
19311
19905
|
}
|
|
@@ -19484,7 +20078,7 @@ class DynamicPageBuilderComponent {
|
|
|
19484
20078
|
}
|
|
19485
20079
|
saveCurrentPage() {
|
|
19486
20080
|
if (!this.canSaveCurrentPage()) {
|
|
19487
|
-
this.agenticAuthoringStatus.set(this.tx('agentic.status.reviewNeedsAttention', '
|
|
20081
|
+
this.agenticAuthoringStatus.set(this.tx('agentic.status.reviewNeedsAttention', 'Complete this review in the assistant before saving.'));
|
|
19488
20082
|
return;
|
|
19489
20083
|
}
|
|
19490
20084
|
const runtimeSnapshot = this.runtime?.getPageSnapshot();
|
|
@@ -19494,7 +20088,7 @@ class DynamicPageBuilderComponent {
|
|
|
19494
20088
|
this.pageSaveRequested.emit(this.clonePage(cloned));
|
|
19495
20089
|
}
|
|
19496
20090
|
canSaveCurrentPage() {
|
|
19497
|
-
return this.hasPageWidgets() && !this.
|
|
20091
|
+
return this.hasPageWidgets() && !this.hasPendingAgenticPreview();
|
|
19498
20092
|
}
|
|
19499
20093
|
evaluatePremiumReadiness(page) {
|
|
19500
20094
|
const widgets = page.widgets || [];
|
|
@@ -19809,9 +20403,14 @@ class DynamicPageBuilderComponent {
|
|
|
19809
20403
|
promptPlaceholder: this.tx('agentic.promptPlaceholder', 'Descreva a página, dashboard, formulário ou alteração que você precisa.'),
|
|
19810
20404
|
emptyConversation: this.agenticAuthoringEmptyConversationMessage(),
|
|
19811
20405
|
submit: this.tx('agentic.submit', 'Enviar pedido'),
|
|
19812
|
-
apply: this.tx('agentic.persist', '
|
|
20406
|
+
apply: this.tx('agentic.persist', 'Aplicar e salvar'),
|
|
19813
20407
|
conversationAria: this.tx('agentic.conversationAria', 'Conversa com IA'),
|
|
19814
20408
|
quickRepliesAria: this.tx('agentic.quickRepliesAria', 'Respostas rápidas'),
|
|
20409
|
+
recommendedIntentsAria: this.tx('agentic.recommendedIntentsAria', 'Sugestões iniciais do assistente'),
|
|
20410
|
+
recommendedIntentsTitle: this.tx('agentic.recommendedIntentsTitle', 'Você pode começar por aqui'),
|
|
20411
|
+
recommendedIntentCta: this.tx('agentic.recommendedIntentCta', 'Usar sugestão'),
|
|
20412
|
+
recommendedIntentGuidanceCta: this.tx('agentic.recommendedIntentGuidanceCta', 'Entender possibilidades'),
|
|
20413
|
+
recommendedIntentRequiresConfirmation: this.tx('agentic.recommendedIntentRequiresConfirmation', 'Requer revisão'),
|
|
19815
20414
|
dragHandleAria: this.tx('agentic.dragHandleAria', 'Mover assistente de IA'),
|
|
19816
20415
|
resizeHandleAria: this.tx('agentic.resizeHandleAria', 'Redimensionar assistente de IA'),
|
|
19817
20416
|
contextAria: this.tx('agentic.contextAria', 'Contexto ativo'),
|
|
@@ -19836,6 +20435,42 @@ class DynamicPageBuilderComponent {
|
|
|
19836
20435
|
stateError: this.tx('agentic.state.error', 'Erro'),
|
|
19837
20436
|
};
|
|
19838
20437
|
}
|
|
20438
|
+
agenticAuthoringRecommendedIntents() {
|
|
20439
|
+
return [
|
|
20440
|
+
{
|
|
20441
|
+
kind: 'praxis.assistant.recommended-intent',
|
|
20442
|
+
id: 'page-builder-platform-capabilities',
|
|
20443
|
+
label: this.tx('agentic.recommended.platformGuide.label', 'Descobrir o que posso fazer'),
|
|
20444
|
+
description: this.tx('agentic.recommended.platformGuide.description', 'Conheça as possibilidades do Praxis para criar páginas, formulários, tabelas e painéis.'),
|
|
20445
|
+
group: {
|
|
20446
|
+
id: 'page-builder-platform-guidance',
|
|
20447
|
+
label: this.tx('agentic.recommended.platformGuide.group', 'Explorar a plataforma'),
|
|
20448
|
+
rank: 1,
|
|
20449
|
+
},
|
|
20450
|
+
icon: 'explore',
|
|
20451
|
+
tone: 'resource',
|
|
20452
|
+
rank: 1,
|
|
20453
|
+
requiresConfirmation: false,
|
|
20454
|
+
action: {
|
|
20455
|
+
kind: 'submit-prompt',
|
|
20456
|
+
prompt: this.tx('agentic.recommended.platformGuide.prompt', 'O que posso fazer aqui?'),
|
|
20457
|
+
contextHints: {
|
|
20458
|
+
source: 'page-builder-assistant-empty-state',
|
|
20459
|
+
opportunityId: 'page-builder.platform-capabilities.explore',
|
|
20460
|
+
semanticScope: 'platform-capabilities',
|
|
20461
|
+
target: {
|
|
20462
|
+
kind: 'component',
|
|
20463
|
+
id: 'praxis-dynamic-page-builder',
|
|
20464
|
+
},
|
|
20465
|
+
responseContract: {
|
|
20466
|
+
preferredResponse: 'info',
|
|
20467
|
+
mutatesPage: false,
|
|
20468
|
+
},
|
|
20469
|
+
},
|
|
20470
|
+
},
|
|
20471
|
+
},
|
|
20472
|
+
];
|
|
20473
|
+
}
|
|
19839
20474
|
agenticAuthoringEmptyConversationMessage() {
|
|
19840
20475
|
const authoredOpening = this.resolveAgenticAuthoredOpeningMessage();
|
|
19841
20476
|
if (authoredOpening) {
|
|
@@ -19869,8 +20504,9 @@ class DynamicPageBuilderComponent {
|
|
|
19869
20504
|
const widgets = this.currentPage().widgets;
|
|
19870
20505
|
return !Array.isArray(widgets) || widgets.length === 0;
|
|
19871
20506
|
}
|
|
19872
|
-
|
|
19873
|
-
return !!this.agenticAuthoringPreviewResult()?.valid
|
|
20507
|
+
hasPendingAgenticPreview() {
|
|
20508
|
+
return !!this.agenticAuthoringPreviewResult()?.valid
|
|
20509
|
+
|| this.agenticAuthoringMaterializationPending();
|
|
19874
20510
|
}
|
|
19875
20511
|
agenticAuthoringSubmitAction() {
|
|
19876
20512
|
const label = this.tx('agentic.submit', 'Enviar pedido');
|
|
@@ -19954,7 +20590,7 @@ class DynamicPageBuilderComponent {
|
|
|
19954
20590
|
|| !!this.agenticAuthoringStatus().trim()
|
|
19955
20591
|
|| this.agenticAuthoringBusy();
|
|
19956
20592
|
}
|
|
19957
|
-
resetAgenticAuthoringSessionState() {
|
|
20593
|
+
resetAgenticAuthoringSessionState(sessionIdToRemove = this.resolveAgenticAuthoringSessionId()) {
|
|
19958
20594
|
this.agenticTurnController = undefined;
|
|
19959
20595
|
this.sharedRuleHandoffRevision += 1;
|
|
19960
20596
|
this.agenticAuthoringBusy.set(false);
|
|
@@ -19962,7 +20598,9 @@ class DynamicPageBuilderComponent {
|
|
|
19962
20598
|
this.agenticAuthoringStatus.set('');
|
|
19963
20599
|
this.agenticAuthoringError.set('');
|
|
19964
20600
|
this.agenticAuthoringPreviewResult.set(null);
|
|
20601
|
+
this.agenticAuthoringMaterializationPending.set(false);
|
|
19965
20602
|
this.agenticAuthoringSemanticDecision.set(null);
|
|
20603
|
+
this.agenticAuthoringTurnResultRef.set(null);
|
|
19966
20604
|
this.agenticAuthoringCanApply.set(false);
|
|
19967
20605
|
this.agenticAuthoringConversation.set([]);
|
|
19968
20606
|
this.agenticAuthoringQuickReplies.set([]);
|
|
@@ -19975,7 +20613,7 @@ class DynamicPageBuilderComponent {
|
|
|
19975
20613
|
this.connectionsViewerOpen.set(false);
|
|
19976
20614
|
this.clearSharedRuleCockpitState();
|
|
19977
20615
|
this.clearProjectKnowledgeCockpitState();
|
|
19978
|
-
this.assistantSessions.removeSession(
|
|
20616
|
+
this.assistantSessions.removeSession(sessionIdToRemove);
|
|
19979
20617
|
if (this.agenticAuthoringOpen()) {
|
|
19980
20618
|
this.syncAgenticAuthoringSession('active');
|
|
19981
20619
|
}
|
|
@@ -19999,11 +20637,23 @@ class DynamicPageBuilderComponent {
|
|
|
19999
20637
|
icon: this.agenticAuthoringDockIcon(),
|
|
20000
20638
|
});
|
|
20001
20639
|
}
|
|
20002
|
-
resolveAgenticAuthoringSessionId() {
|
|
20003
|
-
const route =
|
|
20004
|
-
const component =
|
|
20640
|
+
resolveAgenticAuthoringSessionId(pageIdentity = this.pageIdentity, componentInstanceId = this.componentInstanceId) {
|
|
20641
|
+
const route = pageIdentity?.routePath?.trim();
|
|
20642
|
+
const component = componentInstanceId?.trim();
|
|
20005
20643
|
return `page-builder:${component || route || 'default'}`;
|
|
20006
20644
|
}
|
|
20645
|
+
pageIdentityFingerprint(pageIdentity) {
|
|
20646
|
+
if (!pageIdentity) {
|
|
20647
|
+
return '';
|
|
20648
|
+
}
|
|
20649
|
+
return [
|
|
20650
|
+
pageIdentity.appId,
|
|
20651
|
+
pageIdentity.tenantId,
|
|
20652
|
+
pageIdentity.userId,
|
|
20653
|
+
pageIdentity.routePath,
|
|
20654
|
+
pageIdentity.locale,
|
|
20655
|
+
].map((value) => value?.trim() ?? '').join('|');
|
|
20656
|
+
}
|
|
20007
20657
|
truncateAgenticDockText(text) {
|
|
20008
20658
|
const normalized = text.replace(/\s+/g, ' ').trim();
|
|
20009
20659
|
return normalized.length > 96 ? `${normalized.slice(0, 93)}...` : normalized;
|
|
@@ -20164,7 +20814,7 @@ class DynamicPageBuilderComponent {
|
|
|
20164
20814
|
id: 'governed-domain-context',
|
|
20165
20815
|
kind: 'custom',
|
|
20166
20816
|
label: this.tx('agentic.context.governedDomainContext', 'Governed context'),
|
|
20167
|
-
value:
|
|
20817
|
+
value: this.humanizeGovernedDomainContextResolutionStatus(governedDomainContext.resolutionStatus),
|
|
20168
20818
|
icon: governedDomainContext.resolutionStatus === 'resolved' ? 'verified' : 'policy',
|
|
20169
20819
|
});
|
|
20170
20820
|
if (governedDomainContext.policyProfile) {
|
|
@@ -20478,6 +21128,51 @@ class DynamicPageBuilderComponent {
|
|
|
20478
21128
|
}
|
|
20479
21129
|
}
|
|
20480
21130
|
}
|
|
21131
|
+
async submitAgenticRecommendedIntent(intent) {
|
|
21132
|
+
const action = intent.action;
|
|
21133
|
+
if (action.kind !== 'submit-prompt' || !('prompt' in action) || typeof action.prompt !== 'string') {
|
|
21134
|
+
return;
|
|
21135
|
+
}
|
|
21136
|
+
const prompt = action.prompt.trim();
|
|
21137
|
+
if (!prompt || this.agenticAuthoringBusy())
|
|
21138
|
+
return;
|
|
21139
|
+
this.agenticAuthoringEditingMessageId.set(null);
|
|
21140
|
+
this.agenticAuthoringPrompt.set(prompt);
|
|
21141
|
+
const turnRunId = this.beginAgenticTurn();
|
|
21142
|
+
this.agenticAuthoringQuickReplies.set([]);
|
|
21143
|
+
this.agenticAuthoringBusy.set(true);
|
|
21144
|
+
this.agenticAuthoringError.set('');
|
|
21145
|
+
this.agenticAuthoringPreviewResult.set(null);
|
|
21146
|
+
this.agenticAuthoringCanApply.set(false);
|
|
21147
|
+
this.agenticAuthoringSharedRuleHandoffState.set(null);
|
|
21148
|
+
this.clearSharedRuleCockpitState();
|
|
21149
|
+
this.clearProjectKnowledgeCockpitState();
|
|
21150
|
+
this.agenticAuthoringStatus.set(this.tx('agentic.status.resolvingIntent', 'Entendendo o pedido...'));
|
|
21151
|
+
try {
|
|
21152
|
+
const contextHints = action.contextHints ? { ...action.contextHints } : undefined;
|
|
21153
|
+
await this.consumeAgenticTurn(this.ensureAgenticTurnController().submitPrompt(prompt, {
|
|
21154
|
+
kind: 'recommended-intent',
|
|
21155
|
+
id: intent.id,
|
|
21156
|
+
value: prompt,
|
|
21157
|
+
displayPrompt: prompt,
|
|
21158
|
+
contextHints,
|
|
21159
|
+
}), turnRunId);
|
|
21160
|
+
this.agenticAuthoringPrompt.set('');
|
|
21161
|
+
}
|
|
21162
|
+
catch (error) {
|
|
21163
|
+
if (!this.isCurrentAgenticTurn(turnRunId))
|
|
21164
|
+
return;
|
|
21165
|
+
this.agenticAuthoringStatus.set('');
|
|
21166
|
+
const message = this.describeAgenticError(error);
|
|
21167
|
+
this.agenticAuthoringError.set(message);
|
|
21168
|
+
this.appendAgenticMessage('error', message);
|
|
21169
|
+
}
|
|
21170
|
+
finally {
|
|
21171
|
+
if (this.isCurrentAgenticTurn(turnRunId)) {
|
|
21172
|
+
this.agenticAuthoringBusy.set(false);
|
|
21173
|
+
}
|
|
21174
|
+
}
|
|
21175
|
+
}
|
|
20481
21176
|
async submitAgenticQuickReply(reply) {
|
|
20482
21177
|
const replyKind = (reply.kind || 'suggestion').trim().toLowerCase();
|
|
20483
21178
|
if (replyKind === 'cancel') {
|
|
@@ -20793,6 +21488,7 @@ class DynamicPageBuilderComponent {
|
|
|
20793
21488
|
this.agenticAuthoringPrompt.set('');
|
|
20794
21489
|
this.agenticAuthoringPreviewResult.set(null);
|
|
20795
21490
|
this.agenticAuthoringSemanticDecision.set(null);
|
|
21491
|
+
this.agenticAuthoringTurnResultRef.set(null);
|
|
20796
21492
|
this.agenticAuthoringCanApply.set(false);
|
|
20797
21493
|
this.agenticAuthoringSharedRuleHandoffState.set(null);
|
|
20798
21494
|
this.clearSharedRuleCockpitState();
|
|
@@ -20813,9 +21509,15 @@ class DynamicPageBuilderComponent {
|
|
|
20813
21509
|
this.agenticAuthoringStatus.set(this.tx('agentic.status.saving', 'Salvando tela...'));
|
|
20814
21510
|
try {
|
|
20815
21511
|
const compiledFormPatch = this.resolvePreviewCompiledFormPatch(preview);
|
|
21512
|
+
const turnResultRef = this.agenticAuthoringTurnResultRef();
|
|
21513
|
+
if (!turnResultRef) {
|
|
21514
|
+
throw new Error(this.tx('agentic.errors.turnResultRefMissing', 'A referência do resultado revisado expirou. Gere uma nova prévia antes de salvar.'));
|
|
21515
|
+
}
|
|
20816
21516
|
const result = await firstValueFrom(this.agenticAuthoring.applyPage({
|
|
20817
21517
|
compiledFormPatch,
|
|
20818
21518
|
componentId,
|
|
21519
|
+
streamId: turnResultRef.streamId,
|
|
21520
|
+
resultEventId: turnResultRef.resultEventId,
|
|
20819
21521
|
scope: this.agenticAuthoringScope,
|
|
20820
21522
|
ifMatch: this.agenticAuthoringEtag || this.agenticAuthoringLastEtag() || undefined,
|
|
20821
21523
|
tags: {
|
|
@@ -20826,7 +21528,9 @@ class DynamicPageBuilderComponent {
|
|
|
20826
21528
|
this.agenticAuthoringLastEtag.set(result.etag ?? null);
|
|
20827
21529
|
this.agenticAuthoringApplied.emit(result);
|
|
20828
21530
|
this.agenticAuthoringPreviewResult.set(null);
|
|
21531
|
+
this.agenticAuthoringMaterializationPending.set(false);
|
|
20829
21532
|
this.agenticAuthoringSemanticDecision.set(null);
|
|
21533
|
+
this.agenticAuthoringTurnResultRef.set(null);
|
|
20830
21534
|
this.agenticAuthoringCanApply.set(false);
|
|
20831
21535
|
this.agenticAuthoringQuickReplies.set([]);
|
|
20832
21536
|
this.agenticAuthoringPrompt.set('');
|
|
@@ -21616,6 +22320,70 @@ class DynamicPageBuilderComponent {
|
|
|
21616
22320
|
}
|
|
21617
22321
|
return this.runtimeObservationRegistry.listActive();
|
|
21618
22322
|
}
|
|
22323
|
+
async resolveSelectedComponentAuthoringContext(widgetKey) {
|
|
22324
|
+
const page = this.clonePage(this.currentPage());
|
|
22325
|
+
const widget = page.widgets.find((candidate) => candidate.key === widgetKey);
|
|
22326
|
+
const selectedComponentId = widget?.definition?.id?.trim();
|
|
22327
|
+
if (!widget || !selectedComponentId) {
|
|
22328
|
+
return null;
|
|
22329
|
+
}
|
|
22330
|
+
const metadata = this.componentMetadata.get(selectedComponentId);
|
|
22331
|
+
const authoringManifestRef = metadata?.authoringManifestRef;
|
|
22332
|
+
if (!metadata || !authoringManifestRef?.componentId?.trim()) {
|
|
22333
|
+
return null;
|
|
22334
|
+
}
|
|
22335
|
+
const persistedInputs = this.cloneValue(widget.definition.inputs ?? {});
|
|
22336
|
+
let context = {};
|
|
22337
|
+
let diagnostics = [];
|
|
22338
|
+
const resolver = metadata.configEditor?.contextResolver;
|
|
22339
|
+
if (resolver) {
|
|
22340
|
+
try {
|
|
22341
|
+
const resolved = await resolver({
|
|
22342
|
+
componentId: metadata.id,
|
|
22343
|
+
widgetKey,
|
|
22344
|
+
widgetType: selectedComponentId,
|
|
22345
|
+
persistedInputs: this.cloneValue(persistedInputs),
|
|
22346
|
+
page: this.clonePage(page),
|
|
22347
|
+
widget: this.cloneValue(widget),
|
|
22348
|
+
metadata,
|
|
22349
|
+
});
|
|
22350
|
+
if (this.isComponentConfigEditorContextResult(resolved)) {
|
|
22351
|
+
context = this.cloneValue(resolved.context ?? {});
|
|
22352
|
+
diagnostics = Array.isArray(resolved.diagnostics)
|
|
22353
|
+
? resolved.diagnostics.map((diagnostic) => ({ ...diagnostic }))
|
|
22354
|
+
: [];
|
|
22355
|
+
}
|
|
22356
|
+
else if (resolved && typeof resolved === 'object' && !Array.isArray(resolved)) {
|
|
22357
|
+
context = this.cloneValue(resolved);
|
|
22358
|
+
}
|
|
22359
|
+
}
|
|
22360
|
+
catch (error) {
|
|
22361
|
+
diagnostics = [{
|
|
22362
|
+
code: 'config-editor-context-resolution-failed',
|
|
22363
|
+
severity: 'error',
|
|
22364
|
+
message: error instanceof Error ? error.message : String(error),
|
|
22365
|
+
}];
|
|
22366
|
+
}
|
|
22367
|
+
}
|
|
22368
|
+
return {
|
|
22369
|
+
selectedComponentId,
|
|
22370
|
+
widgetKey,
|
|
22371
|
+
authoringManifestRef: this.cloneValue(authoringManifestRef),
|
|
22372
|
+
validationContext: this.cloneValue(context),
|
|
22373
|
+
contextDiagnostics: diagnostics.map((diagnostic) => ({
|
|
22374
|
+
code: diagnostic.code,
|
|
22375
|
+
...(diagnostic.severity ? { severity: diagnostic.severity } : {}),
|
|
22376
|
+
...(diagnostic.message ? { message: diagnostic.message } : {}),
|
|
22377
|
+
...(diagnostic.path ? { path: diagnostic.path } : {}),
|
|
22378
|
+
})),
|
|
22379
|
+
};
|
|
22380
|
+
}
|
|
22381
|
+
isComponentConfigEditorContextResult(value) {
|
|
22382
|
+
return !!value
|
|
22383
|
+
&& typeof value === 'object'
|
|
22384
|
+
&& !Array.isArray(value)
|
|
22385
|
+
&& ('context' in value || 'diagnostics' in value);
|
|
22386
|
+
}
|
|
21619
22387
|
ensureAgenticTurnController() {
|
|
21620
22388
|
if (!this.agenticTurnController) {
|
|
21621
22389
|
const flow = new PageBuilderAgenticAuthoringTurnFlow(this.agenticAuthoring, {
|
|
@@ -21629,6 +22397,7 @@ class DynamicPageBuilderComponent {
|
|
|
21629
22397
|
enableTurnStream: () => this.agenticAuthoringEnableStreaming,
|
|
21630
22398
|
includeLlmDiagnostics: () => this.agenticAuthoringIncludeLlmDiagnostics,
|
|
21631
22399
|
collectRuntimeComponentObservations: () => this.collectRuntimeComponentObservationsForAgenticTurn(),
|
|
22400
|
+
resolveSelectedComponentAuthoringContext: (widgetKey) => this.resolveSelectedComponentAuthoringContext(widgetKey),
|
|
21632
22401
|
loadComponentCapabilities: () => this.loadAgenticComponentCapabilities(),
|
|
21633
22402
|
applyLocalPreview: (result) => this.applyAgenticPreviewLocally(result),
|
|
21634
22403
|
describePreviewFailure: (result) => this.describeAgenticPreviewFailure(result),
|
|
@@ -21661,8 +22430,11 @@ class DynamicPageBuilderComponent {
|
|
|
21661
22430
|
error: this.tx('agentic.errors.invalidTableContract', 'Encontrei a fonte de dados, mas a tabela proposta usa informações que esse componente não aceita. Vou ajustar para usar apenas campos compatíveis.'),
|
|
21662
22431
|
};
|
|
21663
22432
|
}
|
|
21664
|
-
if (applied.success
|
|
21665
|
-
this.
|
|
22433
|
+
if (applied.success) {
|
|
22434
|
+
this.agenticAuthoringMaterializationPending.set(true);
|
|
22435
|
+
if (this.enableCustomization) {
|
|
22436
|
+
this.enterPreviewMode();
|
|
22437
|
+
}
|
|
21666
22438
|
}
|
|
21667
22439
|
return applied;
|
|
21668
22440
|
}
|
|
@@ -21708,7 +22480,9 @@ class DynamicPageBuilderComponent {
|
|
|
21708
22480
|
this.agenticAuthoringError.set(state.errorText);
|
|
21709
22481
|
this.agenticAuthoringPreviewResult.set(preview);
|
|
21710
22482
|
this.agenticAuthoringSemanticDecision.set(this.resolveAgenticSemanticDecision(state.diagnostics));
|
|
21711
|
-
this.
|
|
22483
|
+
const turnResultRef = this.resolveAgenticTurnResultRef(state.diagnostics);
|
|
22484
|
+
this.agenticAuthoringTurnResultRef.set(turnResultRef);
|
|
22485
|
+
this.agenticAuthoringCanApply.set(!!preview?.valid && !!state.canApply && !!turnResultRef);
|
|
21712
22486
|
this.moveAgenticAuthoringPanelToReviewSidecar(preview);
|
|
21713
22487
|
this.agenticAuthoringAttachments.set(state.attachments);
|
|
21714
22488
|
if (!this.sameSharedRuleHandoffIdentity(previousHandoff, handoff)) {
|
|
@@ -22007,29 +22781,18 @@ class DynamicPageBuilderComponent {
|
|
|
22007
22781
|
if (!semanticDecision) {
|
|
22008
22782
|
return null;
|
|
22009
22783
|
}
|
|
22010
|
-
|
|
22011
|
-
|
|
22012
|
-
|
|
22013
|
-
|
|
22014
|
-
const
|
|
22015
|
-
|
|
22016
|
-
|
|
22017
|
-
|
|
22018
|
-
|
|
22019
|
-
|
|
22020
|
-
const label = this.agenticSemanticResourceLabel(semanticDecision)
|
|
22021
|
-
?? this.agenticSemanticResourceLabel(selectedCandidate)
|
|
22022
|
-
?? this.agenticSemanticResourceLabel(contextHints);
|
|
22023
|
-
if (!label) {
|
|
22024
|
-
return semanticDecision;
|
|
22784
|
+
return semanticDecision;
|
|
22785
|
+
}
|
|
22786
|
+
resolveAgenticTurnResultRef(diagnostics) {
|
|
22787
|
+
const candidate = this.toRecord(diagnostics?.['turnResultRef']);
|
|
22788
|
+
const streamId = this.trimmedString(candidate?.['streamId']);
|
|
22789
|
+
const threadId = this.trimmedString(candidate?.['threadId']);
|
|
22790
|
+
const turnId = this.trimmedString(candidate?.['turnId']);
|
|
22791
|
+
const resultEventId = this.trimmedString(candidate?.['resultEventId']);
|
|
22792
|
+
if (!streamId || !threadId || !turnId || !resultEventId) {
|
|
22793
|
+
return null;
|
|
22025
22794
|
}
|
|
22026
|
-
return {
|
|
22027
|
-
...semanticDecision,
|
|
22028
|
-
selectedResource: {
|
|
22029
|
-
...selectedResource,
|
|
22030
|
-
label,
|
|
22031
|
-
},
|
|
22032
|
-
};
|
|
22795
|
+
return { streamId, threadId, turnId, resultEventId };
|
|
22033
22796
|
}
|
|
22034
22797
|
cloneAgenticContextHints(contextHints) {
|
|
22035
22798
|
if (!contextHints || typeof contextHints !== 'object' || Array.isArray(contextHints)) {
|
|
@@ -22066,6 +22829,19 @@ class DynamicPageBuilderComponent {
|
|
|
22066
22829
|
}
|
|
22067
22830
|
return !!summary.resourceKey || !!summary.query || summary.promptBlockLength > 0;
|
|
22068
22831
|
}
|
|
22832
|
+
humanizeGovernedDomainContextResolutionStatus(value) {
|
|
22833
|
+
const normalized = value?.trim().toLowerCase();
|
|
22834
|
+
if (normalized === 'resolved') {
|
|
22835
|
+
return this.tx('agentic.context.governedDomainContextResolved', 'Available');
|
|
22836
|
+
}
|
|
22837
|
+
if (normalized === 'requested_but_unavailable') {
|
|
22838
|
+
return this.tx('agentic.context.governedDomainContextRequestedUnavailable', 'Requested, but unavailable');
|
|
22839
|
+
}
|
|
22840
|
+
if (normalized === 'not_requested') {
|
|
22841
|
+
return this.tx('agentic.context.governedDomainContextNotRequested', 'Not requested');
|
|
22842
|
+
}
|
|
22843
|
+
return this.tx('agentic.context.governedDomainContextUnknown', 'unknown');
|
|
22844
|
+
}
|
|
22069
22845
|
trimmedString(value) {
|
|
22070
22846
|
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
22071
22847
|
}
|
|
@@ -22249,8 +23025,34 @@ class DynamicPageBuilderComponent {
|
|
|
22249
23025
|
case 'rejected-non-local-field-removal':
|
|
22250
23026
|
return this.tx('agentic.errors.nonLocalFieldRemoval', 'Só posso remover, por aqui, campos que foram criados nesta própria prévia.');
|
|
22251
23027
|
default:
|
|
22252
|
-
|
|
23028
|
+
break;
|
|
23029
|
+
}
|
|
23030
|
+
const failureCodes = (result.failureCodes ?? []).filter((code) => typeof code === 'string' && code.length > 0);
|
|
23031
|
+
const hasExactFailure = (...codes) => codes.some((code) => failureCodes.includes(code));
|
|
23032
|
+
const hasFailureFragment = (...fragments) => failureCodes.some((code) => fragments.some((fragment) => code.includes(fragment)));
|
|
23033
|
+
if (hasExactFailure('component-authoring-context-unavailable')
|
|
23034
|
+
|| hasFailureFragment('component-authoring-context-diagnostic:')) {
|
|
23035
|
+
return this.tx('agentic.errors.componentContextUnavailable', 'Não consegui carregar o contexto de autoria do componente selecionado. Atualize a página ou reabra o assistente a partir desse componente.');
|
|
23036
|
+
}
|
|
23037
|
+
if (hasExactFailure('component-edit-plan-selected-widget-required', 'component-edit-plan-selected-component-required', 'component-edit-plan-selected-widget-not-found', 'component-edit-plan-selected-widget-component-mismatch', 'component-edit-plan-semantic-target-mismatch')) {
|
|
23038
|
+
return this.tx('agentic.errors.componentSelectionChanged', 'O componente selecionado mudou ou não corresponde mais ao pedido. Selecione novamente o componente desejado e reabra o assistente.');
|
|
23039
|
+
}
|
|
23040
|
+
if (hasFailureFragment('target-not-in-catalog', 'target-catalog-missing', 'target-stale', 'event-target-governed', 'mapping-target-invalid', 'drilldown-target-invalid')) {
|
|
23041
|
+
return this.tx('agentic.errors.componentTargetUnavailable', 'O destino solicitado não está disponível para esta interação. Revise as conexões da página ou escolha outro componente de destino.');
|
|
23042
|
+
}
|
|
23043
|
+
if (hasFailureFragment('field-not-in-catalog', 'field-not-in-schema', 'mapping-source-field-missing', 'unknown field', 'chart-fields-exist')) {
|
|
23044
|
+
return this.tx('agentic.errors.componentFieldUnavailable', 'Um ou mais campos solicitados não estão disponíveis na fonte de dados deste componente. Revise a fonte ou escolha campos publicados no catálogo.');
|
|
23045
|
+
}
|
|
23046
|
+
if (hasExactFailure('component-edit-plan-service-unavailable', 'component-edit-plan-provider-failed')) {
|
|
23047
|
+
return this.tx('agentic.errors.componentServiceUnavailable', 'O serviço de autoria do componente está indisponível no momento. Tente novamente.');
|
|
23048
|
+
}
|
|
23049
|
+
if (hasExactFailure('component-authoring-manifest-not-found', 'component-authoring-manifest-operations-unavailable', 'component-edit-plan-manifest-component-required', 'component-edit-plan-manifest-component-mismatch')) {
|
|
23050
|
+
return this.tx('agentic.errors.componentManifestUnavailable', 'Este componente não disponibiliza um manifesto de autoria por IA compatível com a alteração solicitada.');
|
|
23051
|
+
}
|
|
23052
|
+
if (hasFailureFragment('component-edit-plan-')) {
|
|
23053
|
+
return this.tx('agentic.errors.componentPlanInvalid', 'A alteração proposta não passou pelas validações do componente. Revise o pedido e tente novamente.');
|
|
22253
23054
|
}
|
|
23055
|
+
return this.formatAgenticFailures(result.failureCodes);
|
|
22254
23056
|
}
|
|
22255
23057
|
describeAgenticPreviewStatus(result) {
|
|
22256
23058
|
const assistantMessage = typeof result.assistantMessage === 'string'
|
|
@@ -22404,6 +23206,7 @@ class DynamicPageBuilderComponent {
|
|
|
22404
23206
|
[contextItems]="agenticAuthoringContextItems()"
|
|
22405
23207
|
[attachments]="agenticAuthoringAttachments()"
|
|
22406
23208
|
[messages]="agenticAuthoringConversation()"
|
|
23209
|
+
[recommendedIntents]="agenticAuthoringRecommendedIntents()"
|
|
22407
23210
|
[quickReplies]="agenticAuthoringQuickReplies()"
|
|
22408
23211
|
[prompt]="agenticAuthoringPrompt()"
|
|
22409
23212
|
[statusText]="agenticAuthoringStatus()"
|
|
@@ -22422,6 +23225,7 @@ class DynamicPageBuilderComponent {
|
|
|
22422
23225
|
(apply)="persistAgenticAuthoring()"
|
|
22423
23226
|
(retryTurn)="retryAgenticAuthoring()"
|
|
22424
23227
|
(cancelTurn)="cancelAgenticAuthoring()"
|
|
23228
|
+
(recommendedIntent)="submitAgenticRecommendedIntent($event)"
|
|
22425
23229
|
(quickReply)="submitAgenticQuickReply($event)"
|
|
22426
23230
|
(attach)="attachAgenticContext()"
|
|
22427
23231
|
(removeAttachment)="removeAgenticAttachment($event)"
|
|
@@ -22902,6 +23706,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
22902
23706
|
[contextItems]="agenticAuthoringContextItems()"
|
|
22903
23707
|
[attachments]="agenticAuthoringAttachments()"
|
|
22904
23708
|
[messages]="agenticAuthoringConversation()"
|
|
23709
|
+
[recommendedIntents]="agenticAuthoringRecommendedIntents()"
|
|
22905
23710
|
[quickReplies]="agenticAuthoringQuickReplies()"
|
|
22906
23711
|
[prompt]="agenticAuthoringPrompt()"
|
|
22907
23712
|
[statusText]="agenticAuthoringStatus()"
|
|
@@ -22920,6 +23725,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
22920
23725
|
(apply)="persistAgenticAuthoring()"
|
|
22921
23726
|
(retryTurn)="retryAgenticAuthoring()"
|
|
22922
23727
|
(cancelTurn)="cancelAgenticAuthoring()"
|
|
23728
|
+
(recommendedIntent)="submitAgenticRecommendedIntent($event)"
|
|
22923
23729
|
(quickReply)="submitAgenticQuickReply($event)"
|
|
22924
23730
|
(attach)="attachAgenticContext()"
|
|
22925
23731
|
(removeAttachment)="removeAgenticAttachment($event)"
|