@praxisui/page-builder 9.0.0-beta.72 → 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.
@@ -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': 'Save',
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': 'Review the pending points before saving.',
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': 'Salvar',
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': 'Revise os pontos pendentes antes de salvar.',
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: 'single-value',
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: 'single-value',
14486
+ mode,
14339
14487
  steps: [
14340
14488
  {
14341
14489
  id: transform.id,
14342
- kind: 'template',
14490
+ kind: structuredKind,
14343
14491
  phase: 'link-propagation',
14344
14492
  input: { source: transform.inputSource ?? 'event' },
14345
14493
  config: {
14346
- template: clone(transform.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, authoringContext.contextHints),
15281
+ selectedWidgetKey: this.resolveSelectedWidgetKey(selectedWidgetKey, previewAuthoringContext.contextHints),
15132
15282
  intentResolution,
15133
15283
  componentCapabilities,
15134
- ...authoringContext,
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 widgets = this.arrayFromUnknown(plan['widgets'])
15427
- .map((widget) => this.toJsonObject(widget))
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 detailKey = this.dashboardDetailWidgetKey(page) ?? this.dashboardDetailWidgetKeyFromRepairContext(contextHints);
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 (detailKey && this.ensureDashboardKpiContextLinks(page, detailKey, kpiKey)) {
15609
+ if (telemetrySourceKey && this.ensureDashboardKpiContextLinks(page, telemetrySourceKey, kpiKey)) {
15528
15610
  repaired = true;
15529
15611
  }
15530
15612
  }
15531
- if (!repaired) {
15613
+ if (!hasKpiWidget) {
15532
15614
  const resourcePath = this.dashboardRepairResourcePath(contextHints);
15533
- const kpiKey = this.uniqueDashboardWidgetKey(page, 'dashboard-kpis');
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
- if (detailKey) {
15554
- this.ensureDashboardKpiContextLinks(page, detailKey, kpiKey);
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') ?? this.readString(item, 'title') ?? '')));
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 hasRuntimeExpression = !!this.readString(node, 'valueExpr');
15616
- if (hasRuntimeExpression && value !== '-' && value !== '--' && value.toLowerCase() !== 'n/a') {
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 (!hasRuntimeExpression && value && value !== '-' && value !== '--' && value.toLowerCase() !== 'n/a') {
15712
+ if (!valueExpr && !placeholderValue) {
15620
15713
  return false;
15621
15714
  }
15622
- const label = this.readString(node, 'label') ?? this.readString(node, 'title') ?? '';
15623
- node['valueExpr'] = this.dashboardKpiValueExpression(label);
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'] = this.dashboardKpiCaptionExpression(label);
15726
+ node['captionExpr'] = nextCaptionExpr;
15727
+ changed = Object.prototype.hasOwnProperty.call(node, 'caption') || changed;
15629
15728
  delete node['caption'];
15630
- return true;
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 '${statusLabel}';
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: '${statusLabel}',
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
- totalItems: 0,
15696
- loadedItemsCount: 0,
15697
- statusLabel: this.context.tx('agentic.dashboardQuality.kpiStatus.loading', 'Carregando'),
15698
- totalCaption: this.context.tx('agentic.dashboardQuality.kpiCaption.total', 'Total retornado pela consulta filtrada'),
15699
- loadedItemsCaption: this.context.tx('agentic.dashboardQuality.kpiCaption.loaded', 'Itens carregados nesta pagina'),
15700
- statusCaption: this.context.tx('agentic.dashboardQuality.kpiCaption.status', 'Atualizado pelo runtime da tabela'),
15701
- ...(this.cloneJsonObject(this.toJsonObject(current)) ?? {}),
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 writeId = `${tableKey}.loadingStateChange->dashboardKpis`;
15707
- const readId = `dashboardKpis->${kpiKey}.context`;
15708
- let changed = false;
15709
- if (!links.some((link) => this.readString(link, 'id') === writeId)) {
15710
- links.push({
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: 'dashboardKpis', layer: 'values', writable: true },
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
- totalItems: '${payload.totalItems}',
15735
- loadedItemsCount: '${payload.loadedItemsCount}',
15736
- statusLabel: '${payload.status}',
15737
- totalCaption: 'Total retornado pela consulta filtrada',
15738
- loadedItemsCaption: 'Itens carregados nesta pagina',
15739
- statusCaption: '${payload.message}',
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
- metadata: { source: 'persisted-composition-link' },
15746
- });
15747
- changed = true;
15748
- }
15749
- if (!links.some((link) => this.readString(link, 'id') === readId)) {
15750
- links.push({
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: 'dashboardKpis', layer: 'values' },
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
- metadata: { source: 'persisted-composition-link' },
15762
- });
15763
- changed = true;
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') ?? '1.0.0',
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') ?? '1.0.0',
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') ?? '1.0.0',
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
- this.ensureDashboardCanvasItem(page, this.dashboardWidgetKey(widget), this.dashboardWidgetComponentId(widget));
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
- ?? this.readString(this.toJsonObject(this.toJsonObject(contextHints['dashboardQuality'])?.['dto']) ?? {}, 'resourcePath')
16105
- ?? this.dashboardFirstResourcePath(this.toJsonObject(contextHints['materializedPage']))
16106
- ?? '/api/resource';
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 authoringContext = this.buildAuthoringContext(request);
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 = widgets.flatMap((widget) => widget.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 validation = this.buildDashboardValidationSummary(widgets, connections, surfaces);
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
- ?? this.readString(layoutPresetOptions ?? {}, 'blueprint')
16845
- ?? this.readString(layoutPresetOptions ?? {}, 'presetFamily')
16846
- ?? null,
17343
+ || this.readString(layoutPresetOptions ?? {}, 'blueprint')
17344
+ || this.readString(layoutPresetOptions ?? {}, 'presetFamily')
17345
+ || null,
16847
17346
  layoutPreset: this.readString(plan ?? {}, 'layoutPreset')
16848
- ?? this.readString(page ?? {}, 'layoutPreset'),
17347
+ || this.readString(page ?? {}, 'layoutPreset'),
16849
17348
  layoutPresetOptions,
16850
17349
  themePreset: this.readString(plan ?? {}, 'themePreset')
16851
- ?? this.readString(page ?? {}, 'themePreset'),
16852
- selectedBy: intentResolution.authoringProfile ?? intentResolution.changeKind ?? null,
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
- ?? this.readString(widget, 'id')
16867
- ?? this.readString(definition ?? {}, 'key')
16868
- ?? this.readString(definition ?? {}, 'id')
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
- ?? this.readString(definition ?? {}, 'id')
16889
- ?? this.readString(widget, 'type')
16890
- ?? 'unknown';
16891
- const key = this.readString(widget, 'key') ?? this.readString(widget, 'id') ?? 'unknown';
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
- ?? this.readString(inputs, 'label')
16900
- ?? this.readString(shell ?? {}, 'title'),
17398
+ || this.readString(inputs, 'label')
17399
+ || this.readString(shell ?? {}, 'title'),
16901
17400
  subtitle: this.readString(inputs, 'subtitle')
16902
- ?? this.readString(shell ?? {}, 'subtitle'),
17401
+ || this.readString(shell ?? {}, 'subtitle'),
16903
17402
  resourcePath: this.readString(inputs, 'resourcePath')
16904
- ?? this.readString(inputs, 'apiEndpoint')
16905
- ?? this.readString(inputs, 'url'),
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') ?? 'unknown',
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 candidates = [
16932
- ...this.arrayFromUnknown(inputs['surfaces']),
16933
- ...this.arrayFromUnknown(this.toJsonObject(inputs['recordSurfaces'])?.['surfaces']),
16934
- ...this.arrayFromUnknown(inputs['actions']),
16935
- ...this.arrayFromUnknown(inputs['rowActions']),
16936
- ...this.arrayFromUnknown(inputs['toolbarActions']),
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
- ?? this.readString(surface, 'actionId')
16945
- ?? this.readString(surface, 'operationId');
16946
- const resourceSurface = this.toJsonObject(surface['resourceSurface']);
16947
- const widget = this.toJsonObject(surface['widget']);
16948
- if (action !== 'surface.open' && action !== 'dynamicPage.surface.open' && !resourceSurface && !widget) {
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') ?? this.readString(resourceSurface ?? {}, 'id'),
17464
+ id: this.readString(surface, 'id') || this.readString(resourceSurface ?? {}, 'id'),
16954
17465
  action,
16955
- title: this.readString(surface, 'title') ?? this.readString(resourceSurface ?? {}, '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
- ?? this.readString(resourceSurface ?? {}, 'presentation'),
16959
- resourcePath: this.readString(resourceSurface ?? {}, 'resourcePath'),
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
- ?? this.readString(this.toJsonObject(widget?.['definition']) ?? {}, 'id'),
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') ?? this.readString(node, 'id') ?? 'metric';
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 (!componentIds.some((id) => id.includes('table') || id.includes('list'))) {
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 (chartWidgets.length
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') ?? this.readString(ref ?? {}, 'widget'),
17158
- port: this.readString(endpoint, 'port') ?? this.readString(ref ?? {}, 'port'),
17159
- direction: this.readString(endpoint, 'direction') ?? this.readString(ref ?? {}, 'direction'),
17160
- path: this.readString(endpoint, 'path') ?? this.readString(ref ?? {}, 'path'),
17161
- actionId: this.readString(endpoint, 'actionId') ?? this.readString(ref ?? {}, '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.length > 0
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') ?? 'unknown';
17979
+ const key = this.readString(widget, 'key') || 'unknown';
17465
17980
  return {
17466
17981
  key,
17467
- componentId: this.readString(widget, 'componentId') ?? 'unknown',
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') ?? 'unknown',
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
- return toPraxisAssistantConversationMessages(messages, 12);
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
- if (changes['pageLifecycleResetRevision'] && !changes['pageLifecycleResetRevision'].isFirstChange()) {
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', 'Review the pending points before saving.'));
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.hasBlockingAgenticReview();
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', 'Salvar'),
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
- hasBlockingAgenticReview() {
19873
- return !!this.agenticAuthoringPreviewResult()?.valid && !this.agenticAuthoringCanApply();
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(this.resolveAgenticAuthoringSessionId());
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 = this.pageIdentity?.routePath?.trim();
20004
- const component = this.componentInstanceId?.trim();
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: governedDomainContext.resolutionStatus || this.tx('agentic.context.governedDomainContextUnknown', 'unknown'),
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 && this.enableCustomization) {
21665
- this.enterPreviewMode();
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.agenticAuthoringCanApply.set(!!preview?.valid && !!state.canApply);
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
- const selectedResource = this.toRecord(semanticDecision['selectedResource']);
22011
- if (!selectedResource) {
22012
- return semanticDecision;
22013
- }
22014
- const selectedResourceLabel = this.agenticSemanticResourceLabel(selectedResource);
22015
- if (selectedResourceLabel) {
22016
- return semanticDecision;
22017
- }
22018
- const selectedCandidate = this.toRecord(intentResolution?.['selectedCandidate']);
22019
- const contextHints = this.toRecord(intentResolution?.['contextHints']);
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
- return this.formatAgenticFailures(result.failureCodes);
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)"