@praxisui/table 8.0.0-beta.87 → 8.0.0-beta.89

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.
@@ -301,6 +301,9 @@ class TableAgenticAuthoringTurnFlow {
301
301
  return this.selectedRecordSurfaces(contextHints).length > 0;
302
302
  }
303
303
  localRecommendedIntentResponse(request, contextHints) {
304
+ const declaredFilterConfirmation = this.localDeclaredFilterConfirmationResponse(request, contextHints);
305
+ if (declaredFilterConfirmation)
306
+ return declaredFilterConfirmation;
304
307
  const declaredFilterClarification = this.localDeclaredFilterClarificationCompletionResponse(request, contextHints);
305
308
  if (declaredFilterClarification)
306
309
  return declaredFilterClarification;
@@ -559,6 +562,76 @@ class TableAgenticAuthoringTurnFlow {
559
562
  ],
560
563
  };
561
564
  }
565
+ localDeclaredFilterConfirmationResponse(request, contextHints) {
566
+ const pendingClarification = request.pendingClarification
567
+ ?? this.recoverDeclaredFilterConfirmationPendingClarificationFromMessages(request);
568
+ if (!pendingClarification)
569
+ return null;
570
+ if (request.action?.kind && !['clarify', 'submit'].includes(request.action.kind))
571
+ return null;
572
+ if (!this.runtimeOperationAllowed(contextHints, 'table.filter.apply'))
573
+ return null;
574
+ if (!this.pendingClarificationCanContinueDeclaredFilterApply(pendingClarification))
575
+ return null;
576
+ const normalizedPrompt = this.normalizeLabel([
577
+ request.action?.displayPrompt,
578
+ request.action?.value,
579
+ request.prompt,
580
+ ].map((value) => this.stringValue(value)).filter(Boolean).join(' '));
581
+ if (!normalizedPrompt)
582
+ return null;
583
+ if (this.isShortNegativeClarificationAnswer(normalizedPrompt)) {
584
+ return {
585
+ type: 'info',
586
+ sessionId: request.sessionId,
587
+ message: 'Tudo bem, não alterei a tabela.',
588
+ warnings: [
589
+ 'declared-filter-confirmation-cancelled-by-short-negative',
590
+ ],
591
+ };
592
+ }
593
+ if (!this.isShortAffirmativeClarificationAnswer(normalizedPrompt))
594
+ return null;
595
+ const sourcePrompt = this.declaredFilterClarificationSourcePrompt(pendingClarification);
596
+ const normalizedGroundingPrompt = this.normalizeLabel([
597
+ sourcePrompt,
598
+ pendingClarification.assistantMessage,
599
+ ].filter(Boolean).join(' '));
600
+ if (!normalizedGroundingPrompt || !this.promptRequestsRuntimeFilter(normalizedGroundingPrompt))
601
+ return null;
602
+ if (this.promptRequestsVisualOrStructuralEdit(normalizedGroundingPrompt))
603
+ return null;
604
+ const criteria = this.resolveDeclaredFilterConfirmationCriteria(normalizedGroundingPrompt, pendingClarification, contextHints);
605
+ if (!criteria || !Object.keys(criteria).length)
606
+ return null;
607
+ const completedCriteria = this.dedupeRuntimeFilterCriteriaAgainstDiscreteDimensions(this.completeRuntimeFilterCriteriaWithSelectedCandidates(criteria, {
608
+ ...request,
609
+ prompt: sourcePrompt || pendingClarification.assistantMessage || request.prompt,
610
+ }));
611
+ return {
612
+ type: 'patch',
613
+ sessionId: request.sessionId,
614
+ patch: {
615
+ tableRuntimeOperations: {
616
+ kind: 'praxis.table.runtime-operation.batch',
617
+ source: 'declared-filter-confirmation-continuation',
618
+ operations: [{
619
+ operationId: 'table.filter.apply',
620
+ input: {
621
+ criteria: completedCriteria,
622
+ criteriaSemantics: 'simple-conjunction',
623
+ source: 'declared-filter-confirmation',
624
+ },
625
+ }],
626
+ },
627
+ },
628
+ explanation: 'Filtro confirmado a partir da clarificação anterior.',
629
+ warnings: [
630
+ 'declared-filter-confirmation-continuation-materialized',
631
+ 'A resposta curta confirmou uma clarificacao anterior ja ancorada em table.filter.apply e no filterFieldCatalog.',
632
+ ],
633
+ };
634
+ }
562
635
  localDeclaredFilterClarificationCompletionResponse(request, contextHints) {
563
636
  const pendingClarification = request.pendingClarification
564
637
  ?? this.recoverDeclaredFilterPendingClarificationFromMessages(request);
@@ -568,7 +641,7 @@ class TableAgenticAuthoringTurnFlow {
568
641
  return null;
569
642
  if (!this.runtimeOperationAllowed(contextHints, 'table.filter.apply'))
570
643
  return null;
571
- const sourcePrompt = pendingClarification.sourcePrompt?.trim();
644
+ const sourcePrompt = this.declaredFilterClarificationSourcePrompt(pendingClarification);
572
645
  if (!sourcePrompt)
573
646
  return null;
574
647
  const normalizedSourcePrompt = this.normalizeLabel(sourcePrompt);
@@ -578,9 +651,15 @@ class TableAgenticAuthoringTurnFlow {
578
651
  return null;
579
652
  if (!this.pendingClarificationAsksForDeclaredFilter(request, pendingClarification))
580
653
  return null;
654
+ if (this.declaredFilterClarificationResponseIsNewFilterRequest(request, contextHints))
655
+ return null;
581
656
  const selectedEntry = this.filterFieldCatalogEntryForClarificationResponse(request, sourcePrompt);
582
- if (!selectedEntry)
657
+ if (!selectedEntry) {
658
+ const unresolvedShortResponse = this.unresolvedDeclaredFilterShortConfirmationResponse(request, pendingClarification, sourcePrompt);
659
+ if (unresolvedShortResponse)
660
+ return unresolvedShortResponse;
583
661
  return null;
662
+ }
584
663
  const criteria = this.resolveDeclaredFilterCriteriaFromPrompt(normalizedSourcePrompt, contextHints) ?? {};
585
664
  const clarified = this.resolveClarifiedFilterCriterionFromSourcePrompt(normalizedSourcePrompt, selectedEntry, contextHints, criteria);
586
665
  if (!clarified)
@@ -616,6 +695,119 @@ class TableAgenticAuthoringTurnFlow {
616
695
  ],
617
696
  };
618
697
  }
698
+ unresolvedDeclaredFilterShortConfirmationResponse(request, pendingClarification, sourcePrompt) {
699
+ const normalizedPrompt = this.normalizeLabel([
700
+ request.action?.displayPrompt,
701
+ request.action?.value,
702
+ request.prompt,
703
+ ].map((value) => this.stringValue(value)).filter(Boolean).join(' '));
704
+ if (!normalizedPrompt)
705
+ return null;
706
+ if (this.isShortNegativeClarificationAnswer(normalizedPrompt)) {
707
+ return {
708
+ type: 'info',
709
+ sessionId: request.sessionId,
710
+ message: 'Tudo bem, não alterei a tabela. Você pode escolher um filtro ou fazer um novo pedido.',
711
+ warnings: [
712
+ 'declared-filter-clarification-cancelled-by-short-negative',
713
+ ],
714
+ };
715
+ }
716
+ if (!this.isShortAffirmativeClarificationAnswer(normalizedPrompt))
717
+ return null;
718
+ const optionPayloads = this.declaredFilterClarificationReminderOptionPayloads(request, pendingClarification, sourcePrompt);
719
+ if (optionPayloads.length <= 1)
720
+ return null;
721
+ return {
722
+ type: 'clarification',
723
+ sessionId: request.sessionId,
724
+ message: 'Ainda preciso que você escolha qual filtro deve receber esse critério antes de alterar a tabela.',
725
+ questions: ['Escolha uma das opções abaixo para continuar.'],
726
+ optionPayloads,
727
+ diagnostics: {
728
+ tableDeclaredFilterClarification: {
729
+ sourcePrompt,
730
+ },
731
+ },
732
+ warnings: [
733
+ 'declared-filter-short-affirmative-needs-explicit-field-choice',
734
+ ],
735
+ };
736
+ }
737
+ declaredFilterClarificationSourcePrompt(pendingClarification) {
738
+ const diagnostics = this.toRecord(pendingClarification?.diagnostics);
739
+ const declaredFilter = this.toRecord(diagnostics?.['tableDeclaredFilterClarification']);
740
+ const diagnosticSourcePrompt = this.stringValue(declaredFilter?.['sourcePrompt']).trim();
741
+ if (diagnosticSourcePrompt)
742
+ return diagnosticSourcePrompt;
743
+ return pendingClarification?.sourcePrompt?.trim() ?? '';
744
+ }
745
+ isShortAffirmativeClarificationAnswer(normalizedPrompt) {
746
+ return ['sim', 's', 'ok', 'okay', 'isso', 'pode', 'confirmo', 'confirmar']
747
+ .includes(normalizedPrompt);
748
+ }
749
+ isShortNegativeClarificationAnswer(normalizedPrompt) {
750
+ return ['nao', 'não', 'n', 'cancelar', 'cancela', 'deixa', 'deixa pra la', 'deixa pra lá']
751
+ .includes(normalizedPrompt);
752
+ }
753
+ declaredFilterClarificationReminderOptionPayloads(request, pendingClarification, sourcePrompt) {
754
+ const normalizedSourcePrompt = this.normalizeLabel(sourcePrompt);
755
+ const labels = [
756
+ ...(pendingClarification?.questions ?? []).flatMap((question) => (question.options ?? []).map((option) => option.label || option.value || option.id)),
757
+ ...this.declaredFilterQuickReplyOptions(normalizedSourcePrompt, {
758
+ ...request,
759
+ prompt: sourcePrompt,
760
+ }),
761
+ ]
762
+ .map((label) => this.stringValue(label).trim())
763
+ .filter((label) => !!label);
764
+ const seen = new Set();
765
+ return labels
766
+ .map((label) => this.filterFieldCatalogEntryForClarificationLabel(label) ?? {
767
+ name: label,
768
+ label: this.humanizeClarificationOptionLabel(label),
769
+ })
770
+ .filter((entry) => {
771
+ const key = this.normalizeLabel(entry.name || entry.label);
772
+ if (!key || seen.has(key))
773
+ return false;
774
+ seen.add(key);
775
+ return true;
776
+ })
777
+ .map((entry) => ({
778
+ label: entry.label,
779
+ value: entry.name,
780
+ contextHints: {
781
+ tableFilterClarification: {
782
+ source: 'declared-filter-field',
783
+ field: entry.name,
784
+ label: entry.label,
785
+ },
786
+ },
787
+ }));
788
+ }
789
+ declaredFilterClarificationResponseIsNewFilterRequest(request, contextHints) {
790
+ if (this.toRecord(request.action?.contextHints?.['tableFilterClarification']))
791
+ return false;
792
+ if (request.action?.kind === 'clarify')
793
+ return false;
794
+ const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
795
+ if (!normalizedPrompt || !this.promptRequestsRuntimeFilter(normalizedPrompt))
796
+ return false;
797
+ if (this.promptRequestsVisualOrStructuralEdit(normalizedPrompt))
798
+ return false;
799
+ if (this.normalizedTextHasStandaloneToken(normalizedPrompt, 'agora'))
800
+ return true;
801
+ if (this.promptHasCompensationRangeIntent(normalizedPrompt) || this.promptHasDateRangeComparison(normalizedPrompt)) {
802
+ return true;
803
+ }
804
+ if (this.promptHasNumericRangeComparison(normalizedPrompt)
805
+ && this.filterFieldCatalogEntries.filter((entry) => this.isNumericRangeFilterEntry(entry)).length === 1) {
806
+ return true;
807
+ }
808
+ const criteria = this.resolveDeclaredFilterCriteriaFromPrompt(normalizedPrompt, contextHints);
809
+ return !!criteria && Object.keys(criteria).length > 0;
810
+ }
619
811
  pendingClarificationAsksForDeclaredFilter(request, pending) {
620
812
  const hinted = this.toRecord(request.action?.contextHints?.['tableFilterClarification']);
621
813
  if (hinted)
@@ -661,6 +853,78 @@ class TableAgenticAuthoringTurnFlow {
661
853
  }
662
854
  return null;
663
855
  }
856
+ recoverDeclaredFilterConfirmationPendingClarificationFromMessages(request) {
857
+ const messages = [...(request.messages ?? [])];
858
+ if (messages.length < 2)
859
+ return null;
860
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
861
+ const message = messages[index];
862
+ if (message.role !== 'assistant')
863
+ continue;
864
+ if (!this.pendingClarificationCanContinueDeclaredFilterApply({
865
+ assistantMessage: message.text,
866
+ sourcePrompt: '',
867
+ questions: [],
868
+ }))
869
+ continue;
870
+ const sourcePrompt = [...messages.slice(0, index)]
871
+ .reverse()
872
+ .find((candidate) => candidate.role === 'user')
873
+ ?.text?.trim();
874
+ if (!sourcePrompt)
875
+ return null;
876
+ return {
877
+ sourcePrompt,
878
+ assistantMessage: message.text,
879
+ questions: [],
880
+ };
881
+ }
882
+ return null;
883
+ }
884
+ pendingClarificationConfirmsDeclaredFilterApply(pending) {
885
+ const text = this.normalizeLabel([
886
+ pending?.assistantMessage,
887
+ ...(pending?.questions ?? []).map((question) => [
888
+ question.label,
889
+ ...(question.options ?? []).map((option) => option.label),
890
+ ].flat().join(' ')),
891
+ ].filter(Boolean).join(' '));
892
+ if (!text)
893
+ return false;
894
+ const asksConfirmation = text.includes('confirma')
895
+ || text.includes('confirmar')
896
+ || text.includes('posso filtrar')
897
+ || text.includes('aplique esse filtro')
898
+ || text.includes('aplicar esse filtro');
899
+ const mentionsRuntimeFilter = text.includes('filtrar')
900
+ || text.includes('filtro')
901
+ || text.includes('apenas funcionarios')
902
+ || text.includes('apenas funcionários');
903
+ return asksConfirmation && mentionsRuntimeFilter;
904
+ }
905
+ pendingClarificationCanContinueDeclaredFilterApply(pending) {
906
+ return this.pendingClarificationConfirmsDeclaredFilterApply(pending)
907
+ || this.pendingClarificationDeclaresDeclaredFilterApply(pending);
908
+ }
909
+ pendingClarificationDeclaresDeclaredFilterApply(pending) {
910
+ const text = this.normalizeLabel([
911
+ pending?.assistantMessage,
912
+ ...(pending?.questions ?? []).map((question) => [
913
+ question.label,
914
+ ...(question.options ?? []).map((option) => option.label),
915
+ ].flat().join(' ')),
916
+ ].filter(Boolean).join(' '));
917
+ if (!text)
918
+ return false;
919
+ const declaresRuntimeOperation = text.includes('table.filter.apply')
920
+ || text.includes('operacao table filter apply')
921
+ || text.includes('operação table filter apply');
922
+ const declaresFiltering = text.includes('vou filtrar')
923
+ || text.includes('filtrar a tabela')
924
+ || text.includes('executando operacao')
925
+ || text.includes('executando operação');
926
+ return declaresRuntimeOperation && declaresFiltering;
927
+ }
664
928
  filterFieldCatalogEntryForClarificationResponse(request, sourcePrompt) {
665
929
  const hinted = this.toRecord(request.action?.contextHints?.['tableFilterClarification']);
666
930
  const hintedField = this.stringValue(hinted?.['field']);
@@ -746,12 +1010,60 @@ class TableAgenticAuthoringTurnFlow {
746
1010
  const token = this.ungroundedFilterValueTokens(normalizedSourcePrompt, entry, criteria)[0];
747
1011
  return token ? this.restoreFilterValueToken(token, normalizedSourcePrompt) : '';
748
1012
  }
1013
+ resolveImplicitTextCriteriaFromValueHints(normalizedPrompt, criteria) {
1014
+ if (!normalizedPrompt)
1015
+ return;
1016
+ const matches = this.filterFieldCatalogEntries
1017
+ .filter((entry) => criteria[entry.name] === undefined)
1018
+ .filter((entry) => this.isTextualFilterCriterion(entry))
1019
+ .map((entry) => ({
1020
+ entry,
1021
+ match: this.bestFilterValueHintMatch(normalizedPrompt, entry),
1022
+ }))
1023
+ .filter((candidate) => !!candidate.match)
1024
+ .sort((left, right) => right.match.score - left.match.score || left.entry.label.localeCompare(right.entry.label));
1025
+ if (!matches.length)
1026
+ return;
1027
+ const topScore = matches[0].match.score;
1028
+ const winners = matches.filter((candidate) => candidate.match.score === topScore);
1029
+ if (winners.length !== 1)
1030
+ return;
1031
+ criteria[winners[0].entry.name] = this.normalizeRuntimeFilterCriterionForCatalogEntry(winners[0].match.value, winners[0].entry);
1032
+ }
1033
+ bestFilterValueHintMatch(normalizedPrompt, entry) {
1034
+ let best = null;
1035
+ for (const hint of entry.valueHints) {
1036
+ for (const candidate of this.filterValueHintCandidates(hint)) {
1037
+ const normalizedCandidate = this.normalizeLabel(candidate);
1038
+ if (!normalizedCandidate || normalizedCandidate.length < 4)
1039
+ continue;
1040
+ if (!this.normalizedTextContainsApproxPhrase(normalizedPrompt, normalizedCandidate))
1041
+ continue;
1042
+ const score = normalizedCandidate.split(/\s+/u).length * 25 + normalizedCandidate.length;
1043
+ if (!best || score > best.score) {
1044
+ best = { value: candidate, score };
1045
+ }
1046
+ }
1047
+ }
1048
+ return best;
1049
+ }
1050
+ filterValueHintCandidates(hint) {
1051
+ const trimmed = hint.trim();
1052
+ if (!trimmed)
1053
+ return [];
1054
+ const candidates = new Set([trimmed]);
1055
+ const prefix = trimmed.split(/\s+-\s+/u)[0]?.trim();
1056
+ if (prefix && prefix.length >= 4)
1057
+ candidates.add(prefix);
1058
+ return [...candidates];
1059
+ }
749
1060
  ungroundedFilterValueTokens(normalizedPrompt, entry, criteria) {
750
1061
  const ignoredTokens = new Set([
751
1062
  'filtra',
752
1063
  'filtrar',
753
1064
  'filtre',
754
1065
  'filtro',
1066
+ 'agora',
755
1067
  'quero',
756
1068
  'ver',
757
1069
  'mostra',
@@ -762,6 +1074,26 @@ class TableAgenticAuthoringTurnFlow {
762
1074
  'busca',
763
1075
  'procura',
764
1076
  'procurar',
1077
+ 'confirma',
1078
+ 'confirmar',
1079
+ 'aplique',
1080
+ 'aplicar',
1081
+ 'posso',
1082
+ 'para',
1083
+ 'eu',
1084
+ 'esse',
1085
+ 'essa',
1086
+ 'tabela',
1087
+ 'funcionario',
1088
+ 'funcionarios',
1089
+ 'funcionário',
1090
+ 'funcionários',
1091
+ 'qualquer',
1092
+ 'qq',
1093
+ 'depto',
1094
+ 'departamento',
1095
+ 'departamentos',
1096
+ 'serve',
765
1097
  'de',
766
1098
  'do',
767
1099
  'da',
@@ -805,6 +1137,18 @@ class TableAgenticAuthoringTurnFlow {
805
1137
  'admitidas',
806
1138
  'admitiu',
807
1139
  'admitiram',
1140
+ 'ganha',
1141
+ 'ganham',
1142
+ 'ganhando',
1143
+ 'recebe',
1144
+ 'recebem',
1145
+ 'recebendo',
1146
+ 'remuneracao',
1147
+ 'remuneração',
1148
+ 'pagamento',
1149
+ 'folha',
1150
+ 'salario',
1151
+ 'salário',
808
1152
  ].map((token) => this.normalizeLabel(token)));
809
1153
  const fieldMentionTokens = new Set([
810
1154
  entry.name,
@@ -848,8 +1192,41 @@ class TableAgenticAuthoringTurnFlow {
848
1192
  }
849
1193
  unresolvedFilterCriterionOptions(normalizedPrompt, criteria) {
850
1194
  const usedFields = new Set(Object.keys(criteria));
851
- const ranked = this.filterFieldCatalogEntries
1195
+ const hasSymbolicFilterValue = normalizedPrompt
1196
+ .split(/\s+/u)
1197
+ .some((token) => token.length >= 2
1198
+ && /[&/+.]/u.test(token)
1199
+ && !this.tokenIsGroundedByAppliedCriteria(token, criteria));
1200
+ const hasUnresolvedDiscreteFilter = this.promptContainsMentionedUnresolvedDiscreteFilter(normalizedPrompt, criteria);
1201
+ const narrowsByValueShape = !hasSymbolicFilterValue
1202
+ && !hasUnresolvedDiscreteFilter
1203
+ && (this.promptHasCompensationRangeIntent(normalizedPrompt)
1204
+ || this.promptHasNumericRangeComparison(normalizedPrompt)
1205
+ || this.promptHasDateRangeComparison(normalizedPrompt));
1206
+ const candidateEntries = this.filterFieldCatalogEntries
1207
+ .filter((entry) => !usedFields.has(entry.name))
1208
+ .filter((entry) => !this.promptMentionsFilterFieldAsNonRestrictiveScope(normalizedPrompt, entry))
1209
+ .filter((entry) => {
1210
+ if (!narrowsByValueShape)
1211
+ return true;
1212
+ if (this.promptHasCompensationRangeIntent(normalizedPrompt)) {
1213
+ return this.isCompensationRangeFilterEntry(entry);
1214
+ }
1215
+ if (this.promptHasNumericRangeComparison(normalizedPrompt)) {
1216
+ return this.isNumericRangeFilterEntry(entry);
1217
+ }
1218
+ if (this.promptHasDateRangeComparison(normalizedPrompt)) {
1219
+ return this.isDateRangeFilterEntry(entry);
1220
+ }
1221
+ return true;
1222
+ });
1223
+ if (narrowsByValueShape && !candidateEntries.length) {
1224
+ return [];
1225
+ }
1226
+ const fallbackEntries = this.filterFieldCatalogEntries
852
1227
  .filter((entry) => !usedFields.has(entry.name))
1228
+ .filter((entry) => !this.promptMentionsFilterFieldAsNonRestrictiveScope(normalizedPrompt, entry));
1229
+ const ranked = (candidateEntries.length ? candidateEntries : fallbackEntries)
853
1230
  .map((entry) => ({
854
1231
  entry,
855
1232
  score: this.selectedRecordFilterValuePromptScore(entry, normalizedPrompt)
@@ -920,6 +1297,7 @@ class TableAgenticAuthoringTurnFlow {
920
1297
  ...entry.aliases,
921
1298
  ...entry.relatedColumnFields,
922
1299
  ...entry.relatedColumnLabels,
1300
+ ...entry.valueHints,
923
1301
  ...displayValues,
924
1302
  ];
925
1303
  return values.some((value) => value && this.promptContainsAnyVariant(token, value));
@@ -937,7 +1315,9 @@ class TableAgenticAuthoringTurnFlow {
937
1315
  'filtrar',
938
1316
  'filtre',
939
1317
  'filtro',
1318
+ 'agora',
940
1319
  'quero',
1320
+ 'quem',
941
1321
  'ver',
942
1322
  'mostra',
943
1323
  'mostrar',
@@ -947,6 +1327,26 @@ class TableAgenticAuthoringTurnFlow {
947
1327
  'busca',
948
1328
  'procura',
949
1329
  'procurar',
1330
+ 'confirma',
1331
+ 'confirmar',
1332
+ 'aplique',
1333
+ 'aplicar',
1334
+ 'posso',
1335
+ 'para',
1336
+ 'eu',
1337
+ 'esse',
1338
+ 'essa',
1339
+ 'tabela',
1340
+ 'funcionario',
1341
+ 'funcionarios',
1342
+ 'funcionário',
1343
+ 'funcionários',
1344
+ 'qualquer',
1345
+ 'qq',
1346
+ 'depto',
1347
+ 'departamento',
1348
+ 'departamentos',
1349
+ 'serve',
950
1350
  'de',
951
1351
  'do',
952
1352
  'da',
@@ -990,6 +1390,18 @@ class TableAgenticAuthoringTurnFlow {
990
1390
  'admitidas',
991
1391
  'admitiu',
992
1392
  'admitiram',
1393
+ 'ganha',
1394
+ 'ganham',
1395
+ 'ganhando',
1396
+ 'recebe',
1397
+ 'recebem',
1398
+ 'recebendo',
1399
+ 'remuneracao',
1400
+ 'remuneração',
1401
+ 'pagamento',
1402
+ 'folha',
1403
+ 'salario',
1404
+ 'salário',
993
1405
  ].map((token) => this.normalizeLabel(token)));
994
1406
  const fieldMentionTokens = new Set([
995
1407
  entry.name,
@@ -1023,12 +1435,14 @@ class TableAgenticAuthoringTurnFlow {
1023
1435
  ? selectedCandidate['displayValues'].map((value) => this.stringValue(value)).filter((value) => !!value)
1024
1436
  : [];
1025
1437
  return [
1438
+ this.stringValue(criteria[field]),
1026
1439
  entry.name,
1027
1440
  entry.label,
1028
1441
  this.humanizeFilterField(entry.name),
1029
1442
  ...entry.aliases,
1030
1443
  ...entry.relatedColumnFields,
1031
1444
  ...entry.relatedColumnLabels,
1445
+ ...entry.valueHints,
1032
1446
  ...displayValues,
1033
1447
  ].some((value) => value && this.promptContainsAnyVariant(token, value));
1034
1448
  });
@@ -1046,6 +1460,7 @@ class TableAgenticAuthoringTurnFlow {
1046
1460
  'buscar',
1047
1461
  'procura',
1048
1462
  'procurar',
1463
+ 'quem',
1049
1464
  'mostra',
1050
1465
  'mostrar',
1051
1466
  'liste',
@@ -1062,6 +1477,19 @@ class TableAgenticAuthoringTurnFlow {
1062
1477
  'apos',
1063
1478
  'após',
1064
1479
  'desde',
1480
+ 'mais de',
1481
+ 'mais que',
1482
+ 'menos de',
1483
+ 'menos que',
1484
+ 'ganha',
1485
+ 'ganham',
1486
+ 'ganhando',
1487
+ 'recebe',
1488
+ 'recebem',
1489
+ 'remuneracao',
1490
+ 'remuneração',
1491
+ 'salario',
1492
+ 'salário',
1065
1493
  ].some((token) => normalizedPrompt.includes(this.normalizeLabel(token)));
1066
1494
  }
1067
1495
  promptRequestsVisualOrStructuralEdit(normalizedPrompt) {
@@ -1101,8 +1529,31 @@ class TableAgenticAuthoringTurnFlow {
1101
1529
  this.completeImplicitBooleanCriteriaFromPrompt(normalizedPrompt, criteria);
1102
1530
  this.completeImplicitDateRangeCriteriaFromPrompt(normalizedPrompt, criteria);
1103
1531
  this.completeImplicitNumericRangeCriteriaFromPrompt(normalizedPrompt, criteria);
1532
+ if (!Object.keys(criteria).length) {
1533
+ this.resolveImplicitTextCriteriaFromValueHints(normalizedPrompt, criteria);
1534
+ }
1104
1535
  return Object.keys(criteria).length ? criteria : null;
1105
1536
  }
1537
+ resolveDeclaredFilterConfirmationCriteria(normalizedGroundingPrompt, pendingClarification, contextHints) {
1538
+ const criteria = this.resolveDeclaredFilterCriteriaFromPrompt(normalizedGroundingPrompt, contextHints);
1539
+ if (criteria && Object.keys(criteria).length)
1540
+ return criteria;
1541
+ const assistantText = this.normalizeLabel(pendingClarification?.assistantMessage ?? '');
1542
+ if (!assistantText)
1543
+ return null;
1544
+ const confirmedCriteria = {};
1545
+ for (const entry of this.filterFieldCatalogEntries) {
1546
+ if (!this.isTextualFilterCriterion(entry))
1547
+ continue;
1548
+ if (!this.promptMentionsFilterField(assistantText, entry.name))
1549
+ continue;
1550
+ const textValue = this.extractClarifiedTextFilterValue(assistantText, entry, confirmedCriteria);
1551
+ if (textValue) {
1552
+ confirmedCriteria[entry.name] = this.normalizeRuntimeFilterCriterionForCatalogEntry(textValue, entry);
1553
+ }
1554
+ }
1555
+ return Object.keys(confirmedCriteria).length ? confirmedCriteria : null;
1556
+ }
1106
1557
  completeImplicitBooleanCriteriaFromPrompt(normalizedPrompt, criteria) {
1107
1558
  if (!this.promptHasBooleanFilterValue(normalizedPrompt))
1108
1559
  return;
@@ -1202,13 +1653,33 @@ class TableAgenticAuthoringTurnFlow {
1202
1653
  const candidates = this.filterFieldCatalogEntries
1203
1654
  .filter((entry) => criteria[entry.name] === undefined)
1204
1655
  .filter((entry) => this.isNumericRangeFilterEntry(entry));
1205
- if (candidates.length !== 1)
1656
+ const mentioned = candidates.filter((entry) => this.promptMentionsFilterField(normalizedPrompt, entry.name));
1657
+ const compensation = mentioned.length ? [] : candidates.filter((entry) => this.isCompensationRangeFilterEntry(entry));
1658
+ const eligible = mentioned.length ? mentioned : (this.promptHasCompensationRangeIntent(normalizedPrompt) ? compensation : candidates);
1659
+ if (eligible.length !== 1)
1206
1660
  return;
1207
- const criterion = this.resolveRangeFilterValue(normalizedPrompt, candidates[0]);
1661
+ const criterion = this.resolveRangeFilterValue(normalizedPrompt, eligible[0]);
1208
1662
  if (criterion !== undefined) {
1209
- criteria[candidates[0].name] = criterion;
1663
+ criteria[eligible[0].name] = criterion;
1210
1664
  }
1211
1665
  }
1666
+ promptHasCompensationRangeIntent(normalizedPrompt) {
1667
+ return this.promptHasNumericRangeComparison(normalizedPrompt)
1668
+ && [
1669
+ 'ganha',
1670
+ 'ganham',
1671
+ 'ganhando',
1672
+ 'recebe',
1673
+ 'recebem',
1674
+ 'recebendo',
1675
+ 'remuneracao',
1676
+ 'remuneração',
1677
+ 'pagamento',
1678
+ 'folha',
1679
+ 'salario',
1680
+ 'salário',
1681
+ ].some((token) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, this.normalizeLabel(token)));
1682
+ }
1212
1683
  promptHasNumericRangeComparison(normalizedPrompt) {
1213
1684
  return /\b\d+(?:[,.]\d+)?(?:\s*(?:mil|k))?\b/u.test(normalizedPrompt)
1214
1685
  && this.promptHasAnyToken(normalizedPrompt, [
@@ -1257,6 +1728,34 @@ class TableAgenticAuthoringTurnFlow {
1257
1728
  || valueShape.includes('number');
1258
1729
  return isRange && isNumericLike && !isDateLike;
1259
1730
  }
1731
+ isCompensationRangeFilterEntry(entry) {
1732
+ if (!this.isNumericRangeFilterEntry(entry))
1733
+ return false;
1734
+ const text = this.normalizeLabel([
1735
+ entry.name,
1736
+ entry.label,
1737
+ this.humanizeFilterField(entry.name),
1738
+ entry.controlType,
1739
+ entry.criterionKind,
1740
+ entry.criterionValueShape,
1741
+ ...entry.aliases,
1742
+ ...entry.relatedColumnFields,
1743
+ ...entry.relatedColumnLabels,
1744
+ ].join(' '));
1745
+ return [
1746
+ 'salario',
1747
+ 'salário',
1748
+ 'salary',
1749
+ 'remuneracao',
1750
+ 'remuneração',
1751
+ 'compensacao',
1752
+ 'compensação',
1753
+ 'pagamento',
1754
+ 'folha',
1755
+ 'wage',
1756
+ 'pay',
1757
+ ].some((token) => this.normalizedTextContainsApproxPhrase(text, this.normalizeLabel(token)));
1758
+ }
1260
1759
  resolveDeclaredFilterCriterionValue(normalizedPrompt, entry, contextHints) {
1261
1760
  const type = this.normalizeLabel(entry.type ?? '');
1262
1761
  const controlType = this.normalizeLabel(entry.controlType ?? '');
@@ -1671,16 +2170,24 @@ class TableAgenticAuthoringTurnFlow {
1671
2170
  if (response.type === 'clarification') {
1672
2171
  const questions = this.toClarificationQuestions(response, request);
1673
2172
  const diagnostics = this.buildClarificationDiagnostics(response);
2173
+ const sourcePrompt = this.pendingClarificationSourcePromptForTurn(request, diagnostics);
2174
+ const assistantMessage = response.message || 'Preciso de mais detalhes para continuar.';
1674
2175
  return {
1675
2176
  state: 'clarification',
1676
2177
  phase: 'clarify',
1677
2178
  sessionId: response.sessionId ?? request.sessionId,
1678
2179
  observationId: response.observationId ?? request.observationId ?? null,
1679
- assistantMessage: response.message || 'Preciso de mais detalhes para continuar.',
2180
+ assistantMessage,
1680
2181
  clarificationQuestions: questions,
1681
2182
  quickReplies: this.toQuickReplies(response, request),
1682
2183
  canApply: false,
1683
2184
  diagnostics,
2185
+ pendingClarification: {
2186
+ sourcePrompt,
2187
+ assistantMessage,
2188
+ questions,
2189
+ diagnostics,
2190
+ },
1684
2191
  };
1685
2192
  }
1686
2193
  if (response.type === 'info') {
@@ -1765,6 +2272,10 @@ class TableAgenticAuthoringTurnFlow {
1765
2272
  if (continuedEarlyColumnPlan) {
1766
2273
  return this.compileAdapterResponse(continuedEarlyColumnPlan, request);
1767
2274
  }
2275
+ const continuedColumnVisibilityPlan = this.columnVisibilityPlanForGroundedClarification(response, request);
2276
+ if (continuedColumnVisibilityPlan) {
2277
+ return this.compileAdapterResponse(continuedColumnVisibilityPlan, request);
2278
+ }
1768
2279
  const compiledExecutable = !this.responseCarriesClarificationChoices(response)
1769
2280
  && this.responseMayContainExecutableEnvelope(response)
1770
2281
  ? this.adapter.compileAiResponse?.(response)
@@ -1784,6 +2295,10 @@ class TableAgenticAuthoringTurnFlow {
1784
2295
  warnings: warnings.length ? warnings : undefined,
1785
2296
  };
1786
2297
  delete executableResponse.type;
2298
+ const columnVisibilitySurfaceMismatch = this.columnVisibilityPlanForMisroutedSurfaceRuntime(executableResponse, request);
2299
+ if (columnVisibilitySurfaceMismatch) {
2300
+ return this.compileAdapterResponse(columnVisibilitySurfaceMismatch, request);
2301
+ }
1787
2302
  const visualSurfaceMismatch = this.visualPresentationSurfaceRuntimeMismatch(executableResponse, request);
1788
2303
  if (visualSurfaceMismatch) {
1789
2304
  return visualSurfaceMismatch;
@@ -1795,6 +2310,10 @@ class TableAgenticAuthoringTurnFlow {
1795
2310
  return executableResponse;
1796
2311
  }
1797
2312
  if (compiledExecutable?.type === 'error') {
2313
+ const continuedColumnVisibilityError = this.columnVisibilityPlanForMisroutedSurfaceError(response, request, compiledExecutable);
2314
+ if (continuedColumnVisibilityError) {
2315
+ return this.compileAdapterResponse(continuedColumnVisibilityError, request);
2316
+ }
1798
2317
  const continuedInvalidExecutable = this.selectedRecordSurfaceRuntimeOperationForInvalidExecutable(response, request, compiledExecutable.warnings);
1799
2318
  if (continuedInvalidExecutable) {
1800
2319
  return continuedInvalidExecutable;
@@ -1809,6 +2328,14 @@ class TableAgenticAuthoringTurnFlow {
1809
2328
  warnings: compiledExecutable.warnings,
1810
2329
  };
1811
2330
  }
2331
+ const continuedColumnVisibilityNarrative = this.columnVisibilityPlanForGroundedNarrative(response, request);
2332
+ if (continuedColumnVisibilityNarrative) {
2333
+ return this.compileAdapterResponse(continuedColumnVisibilityNarrative, request);
2334
+ }
2335
+ const continuedColumnVisibilitySurfaceNarrative = this.columnVisibilityPlanForMisroutedSurfaceNarrative(response, request);
2336
+ if (continuedColumnVisibilitySurfaceNarrative) {
2337
+ return this.compileAdapterResponse(continuedColumnVisibilitySurfaceNarrative, request);
2338
+ }
1812
2339
  const continuedSurfaceRowActionFromInfo = this.selectedRecordSurfaceRowActionPlanForInfo(response, request);
1813
2340
  if (continuedSurfaceRowActionFromInfo) {
1814
2341
  return this.compileAdapterResponse(continuedSurfaceRowActionFromInfo, request);
@@ -1887,6 +2414,14 @@ class TableAgenticAuthoringTurnFlow {
1887
2414
  };
1888
2415
  }
1889
2416
  if (!compiled) {
2417
+ const continuedColumnVisibilityNarrative = this.columnVisibilityPlanForGroundedNarrative(response, request);
2418
+ if (continuedColumnVisibilityNarrative) {
2419
+ return this.compileAdapterResponse(continuedColumnVisibilityNarrative, request);
2420
+ }
2421
+ const continuedColumnVisibilitySurfaceNarrative = this.columnVisibilityPlanForMisroutedSurfaceNarrative(response, request);
2422
+ if (continuedColumnVisibilitySurfaceNarrative) {
2423
+ return this.compileAdapterResponse(continuedColumnVisibilitySurfaceNarrative, request);
2424
+ }
1890
2425
  const continuedColumnFormatNarrative = this.columnFormatPlanForNonExecutableNarrative(response, request);
1891
2426
  if (continuedColumnFormatNarrative) {
1892
2427
  return this.compileAdapterResponse(continuedColumnFormatNarrative, request);
@@ -1898,6 +2433,10 @@ class TableAgenticAuthoringTurnFlow {
1898
2433
  return response;
1899
2434
  }
1900
2435
  if (compiled.type === 'error') {
2436
+ const continuedColumnVisibilityError = this.columnVisibilityPlanForMisroutedSurfaceError(response, request, compiled);
2437
+ if (continuedColumnVisibilityError) {
2438
+ return this.compileAdapterResponse(continuedColumnVisibilityError, request);
2439
+ }
1901
2440
  const continuedBulkRouteAction = this.bulkRouteActionPlanForInvalidExecutable(response, request, compiled.warnings);
1902
2441
  if (continuedBulkRouteAction) {
1903
2442
  return this.compileAdapterResponse(continuedBulkRouteAction, request);
@@ -1953,6 +2492,10 @@ class TableAgenticAuthoringTurnFlow {
1953
2492
  patch: normalizedCompiled.patch,
1954
2493
  warnings: warnings.length ? warnings : undefined,
1955
2494
  };
2495
+ const columnVisibilitySurfaceMismatch = this.columnVisibilityPlanForMisroutedSurfaceRuntime(compiledResponse, request);
2496
+ if (columnVisibilitySurfaceMismatch) {
2497
+ return this.compileAdapterResponse(columnVisibilitySurfaceMismatch, request);
2498
+ }
1956
2499
  const visualSurfaceMismatch = this.visualPresentationSurfaceRuntimeMismatch(compiledResponse, request);
1957
2500
  if (visualSurfaceMismatch) {
1958
2501
  return visualSurfaceMismatch;
@@ -3432,6 +3975,8 @@ class TableAgenticAuthoringTurnFlow {
3432
3975
  return score;
3433
3976
  }
3434
3977
  selectedRecordSurfaceOperationForMisgroundedFilter(normalizedPrompt, request) {
3978
+ if (request && this.turnRequestsColumnVisibilityEdit(request))
3979
+ return null;
3435
3980
  if (this.promptRequestsSimilarRecords(normalizedPrompt))
3436
3981
  return null;
3437
3982
  const contextHints = this.contextHintsFor(request);
@@ -3483,6 +4028,8 @@ class TableAgenticAuthoringTurnFlow {
3483
4028
  selectedRecordSurfaceRowActionPlanForClarification(response, request) {
3484
4029
  if (!request)
3485
4030
  return null;
4031
+ if (this.turnRequestsColumnVisibilityEdit(request))
4032
+ return null;
3486
4033
  const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
3487
4034
  const normalizedConversation = this.normalizeLabel([
3488
4035
  ...(request.messages ?? []).slice(-8).map((message) => message?.text ?? ''),
@@ -3533,6 +4080,8 @@ class TableAgenticAuthoringTurnFlow {
3533
4080
  selectedRecordSurfaceRowActionPlanForInfo(response, request) {
3534
4081
  if (response.type !== 'info' || !request)
3535
4082
  return null;
4083
+ if (this.turnRequestsColumnVisibilityEdit(request))
4084
+ return null;
3536
4085
  const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
3537
4086
  const normalizedConversation = this.normalizeLabel([
3538
4087
  ...(request.messages ?? []).slice(-8).map((message) => message?.text ?? ''),
@@ -3577,6 +4126,8 @@ class TableAgenticAuthoringTurnFlow {
3577
4126
  selectedRecordSurfaceRuntimeOperationForInfo(response, request) {
3578
4127
  if (response.type !== 'info' || !request)
3579
4128
  return null;
4129
+ if (this.turnRequestsColumnVisibilityEdit(request))
4130
+ return null;
3580
4131
  const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
3581
4132
  if (!this.textMentionsRecordSurfaceOpenRequest(normalizedPrompt))
3582
4133
  return null;
@@ -3638,6 +4189,8 @@ class TableAgenticAuthoringTurnFlow {
3638
4189
  selectedRecordSurfaceRuntimeOperationForGroundedInfo(response, request) {
3639
4190
  if ((response.type !== 'info' && response.type !== 'clarification') || !request)
3640
4191
  return null;
4192
+ if (this.turnRequestsColumnVisibilityEdit(request))
4193
+ return null;
3641
4194
  const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
3642
4195
  if (!this.textMentionsRecordSurfaceOpenRequest(normalizedPrompt))
3643
4196
  return null;
@@ -3698,6 +4251,8 @@ class TableAgenticAuthoringTurnFlow {
3698
4251
  selectedRecordSurfaceRuntimeOperationForInvalidExecutable(response, request, warnings = []) {
3699
4252
  if (!request || !this.responseMayContainExecutableEnvelope(response))
3700
4253
  return null;
4254
+ if (this.turnRequestsColumnVisibilityEdit(request))
4255
+ return null;
3701
4256
  const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
3702
4257
  const normalizedConversation = this.normalizeLabel([
3703
4258
  ...(request.messages ?? []).slice(-8).map((message) => message?.text ?? ''),
@@ -3767,6 +4322,8 @@ class TableAgenticAuthoringTurnFlow {
3767
4322
  selectedRecordSurfaceRuntimeOperationForOpenOnlyExecutable(response, request) {
3768
4323
  if (!request)
3769
4324
  return null;
4325
+ if (this.turnRequestsColumnVisibilityEdit(request))
4326
+ return null;
3770
4327
  const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
3771
4328
  if (!this.promptExplicitlyRequestsRecordSurfaceOpenOnly(normalizedPrompt))
3772
4329
  return null;
@@ -4904,6 +5461,256 @@ class TableAgenticAuthoringTurnFlow {
4904
5461
  ],
4905
5462
  };
4906
5463
  }
5464
+ columnVisibilityPlanForMisroutedSurfaceRuntime(response, request) {
5465
+ if (!request)
5466
+ return null;
5467
+ const prompt = this.columnVisibilityPromptForTurn(request);
5468
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5469
+ return null;
5470
+ if (!this.patchContainsRuntimeOperationId(response.patch, 'dynamicPage.surface.open'))
5471
+ return null;
5472
+ const currentConfig = this.adapter.getCurrentConfig?.();
5473
+ const columns = Array.isArray(currentConfig?.['columns'])
5474
+ ? currentConfig['columns']
5475
+ .map((column) => this.toRecord(column))
5476
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5477
+ : [];
5478
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5479
+ if (!operations.length) {
5480
+ return {
5481
+ type: 'clarification',
5482
+ sessionId: request.sessionId,
5483
+ message: 'Entendi que o pedido é sobre ocultar ou exibir colunas, não sobre abrir uma superfície do registro.',
5484
+ questions: ['Quais colunas você quer ocultar ou exibir?'],
5485
+ options: columns
5486
+ .slice(0, 8)
5487
+ .map((column) => this.stringValue(column['header']) || this.stringValue(column['field']))
5488
+ .filter((label) => !!label),
5489
+ warnings: [
5490
+ ...(response.warnings ?? []),
5491
+ 'column-visibility-request-blocked-surface-runtime-operation',
5492
+ ],
5493
+ };
5494
+ }
5495
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5496
+ ...(response.warnings ?? []),
5497
+ 'column-visibility-continued-from-surface-runtime-misroute',
5498
+ 'Residual continuity guard acted only after the LLM proposed dynamicPage.surface.open for a column visibility/privacy request grounded in declared table columns.',
5499
+ ]);
5500
+ }
5501
+ columnVisibilityPlanForGroundedClarification(response, request) {
5502
+ if (!request)
5503
+ return null;
5504
+ if (response.type !== 'clarification')
5505
+ return null;
5506
+ const prompt = this.columnVisibilityPromptForTurn(request);
5507
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5508
+ return null;
5509
+ const questionText = [
5510
+ response.message,
5511
+ ...(response.questions ?? []),
5512
+ ...(response.options ?? []),
5513
+ ].join(' ');
5514
+ const asksForColumn = this.normalizedTextIncludesAny(this.normalizeLabel(questionText), [
5515
+ 'qual coluna',
5516
+ 'quais colunas',
5517
+ 'coluna devo usar',
5518
+ 'colunas devo usar',
5519
+ ]);
5520
+ const asksForVisibilityScope = this.normalizedTextIncludesAny(this.normalizeLabel(questionText), [
5521
+ 'globalmente',
5522
+ 'todos os usuarios',
5523
+ 'todos os usuários',
5524
+ 'apresentacao publica',
5525
+ 'apresentação pública',
5526
+ 'visualizacao publica',
5527
+ 'visualização pública',
5528
+ 'vista padrao',
5529
+ 'vista padrão',
5530
+ 'confirma qual opcao',
5531
+ 'confirma qual opção',
5532
+ ]);
5533
+ if (!asksForColumn && !asksForVisibilityScope)
5534
+ return null;
5535
+ const currentConfig = this.adapter.getCurrentConfig?.();
5536
+ const columns = Array.isArray(currentConfig?.['columns'])
5537
+ ? currentConfig['columns']
5538
+ .map((column) => this.toRecord(column))
5539
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5540
+ : [];
5541
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5542
+ if (!operations.length)
5543
+ return null;
5544
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5545
+ ...(response.warnings ?? []),
5546
+ 'column-visibility-continued-from-grounded-clarification',
5547
+ 'Residual continuity guard acted only after the LLM asked which column to use even though the column visibility request was grounded in declared table columns.',
5548
+ ]);
5549
+ }
5550
+ columnVisibilityPlanForGroundedNarrative(response, request) {
5551
+ if (!request)
5552
+ return null;
5553
+ const prompt = this.columnVisibilityPromptForTurn(request);
5554
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5555
+ return null;
5556
+ const responseText = this.normalizeLabel([
5557
+ response.message,
5558
+ response.explanation,
5559
+ response.code,
5560
+ ].join(' '));
5561
+ const narratesVisibilityPlan = this.normalizedTextIncludesAny(responseText, [
5562
+ 'componenteditplan',
5563
+ 'column.visibility.set',
5564
+ 'visible false',
5565
+ 'invisivel',
5566
+ 'invisível',
5567
+ 'ocultar a coluna',
5568
+ 'ocultar as colunas',
5569
+ ]);
5570
+ if (!narratesVisibilityPlan)
5571
+ return null;
5572
+ const currentConfig = this.adapter.getCurrentConfig?.();
5573
+ const columns = Array.isArray(currentConfig?.['columns'])
5574
+ ? currentConfig['columns']
5575
+ .map((column) => this.toRecord(column))
5576
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5577
+ : [];
5578
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5579
+ if (!operations.length)
5580
+ return null;
5581
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5582
+ ...(response.warnings ?? []),
5583
+ 'column-visibility-continued-from-non-executable-narrative',
5584
+ 'Residual continuity guard acted only after the LLM narrated a column visibility plan without returning an executable envelope.',
5585
+ ]);
5586
+ }
5587
+ columnVisibilityPlanForMisroutedSurfaceNarrative(response, request) {
5588
+ if (!request)
5589
+ return null;
5590
+ const prompt = this.columnVisibilityPromptForTurn(request);
5591
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5592
+ return null;
5593
+ const responseText = this.normalizeLabel([
5594
+ response.message,
5595
+ response.explanation,
5596
+ response.code,
5597
+ ...(response.warnings ?? []),
5598
+ ].join(' '));
5599
+ const narratesSurfaceOpen = this.normalizedTextIncludesAny(responseText, [
5600
+ 'dynamicpage surface open',
5601
+ 'dynamicpage.surface.open',
5602
+ 'surface open',
5603
+ 'surface.open',
5604
+ 'abrir a superficie',
5605
+ 'abrir a superfície',
5606
+ 'vou abrir',
5607
+ 'superficie relacionada',
5608
+ 'superfície relacionada',
5609
+ ]);
5610
+ if (!narratesSurfaceOpen)
5611
+ return null;
5612
+ const currentConfig = this.adapter.getCurrentConfig?.();
5613
+ const columns = Array.isArray(currentConfig?.['columns'])
5614
+ ? currentConfig['columns']
5615
+ .map((column) => this.toRecord(column))
5616
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5617
+ : [];
5618
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5619
+ if (!operations.length) {
5620
+ return {
5621
+ type: 'clarification',
5622
+ sessionId: request.sessionId,
5623
+ message: 'Entendi que o pedido é sobre ocultar ou exibir colunas, não sobre abrir uma superfície do registro.',
5624
+ questions: ['Quais colunas você quer ocultar ou exibir?'],
5625
+ options: columns
5626
+ .slice(0, 8)
5627
+ .map((column) => this.stringValue(column['header']) || this.stringValue(column['field']))
5628
+ .filter((label) => !!label),
5629
+ warnings: [
5630
+ ...(response.warnings ?? []),
5631
+ 'column-visibility-request-blocked-surface-narrative',
5632
+ ],
5633
+ };
5634
+ }
5635
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5636
+ ...(response.warnings ?? []),
5637
+ 'column-visibility-continued-from-surface-narrative-misroute',
5638
+ 'Residual continuity guard acted only after the LLM narrated opening a related surface for a column visibility/privacy request grounded in declared table columns.',
5639
+ ]);
5640
+ }
5641
+ columnVisibilityPlanForMisroutedSurfaceError(response, request, compiledError) {
5642
+ if (!request)
5643
+ return null;
5644
+ const prompt = this.columnVisibilityPromptForTurn(request);
5645
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5646
+ return null;
5647
+ const errorText = this.normalizeLabel([
5648
+ response.message,
5649
+ response.explanation,
5650
+ compiledError.message,
5651
+ compiledError.explanation,
5652
+ ...(compiledError.warnings ?? []),
5653
+ ].join(' '));
5654
+ if (!this.normalizedTextIncludesAny(errorText, [
5655
+ 'dynamicpage.surface.open',
5656
+ 'surface.open',
5657
+ 'superficie',
5658
+ 'superfície',
5659
+ ]))
5660
+ return null;
5661
+ const currentConfig = this.adapter.getCurrentConfig?.();
5662
+ const columns = Array.isArray(currentConfig?.['columns'])
5663
+ ? currentConfig['columns']
5664
+ .map((column) => this.toRecord(column))
5665
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5666
+ : [];
5667
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5668
+ if (!operations.length)
5669
+ return null;
5670
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5671
+ ...(response.warnings ?? []),
5672
+ ...(compiledError.warnings ?? []),
5673
+ 'column-visibility-continued-from-surface-runtime-error',
5674
+ 'Residual continuity guard acted only after a misrouted surface runtime operation failed validation for a column visibility request.',
5675
+ ]);
5676
+ }
5677
+ columnVisibilityPromptForTurn(request) {
5678
+ const currentPrompt = (request.prompt ?? '').trim();
5679
+ if (this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(currentPrompt))) {
5680
+ return currentPrompt;
5681
+ }
5682
+ const previousVisibilityPrompt = [...(request.messages ?? [])]
5683
+ .reverse()
5684
+ .filter((message) => message.role === 'user')
5685
+ .map((message) => message.text.trim())
5686
+ .find((message) => this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(message)));
5687
+ return [previousVisibilityPrompt, currentPrompt]
5688
+ .filter((part) => !!part)
5689
+ .join(' ');
5690
+ }
5691
+ turnRequestsColumnVisibilityEdit(request) {
5692
+ return this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(this.columnVisibilityPromptForTurn(request)));
5693
+ }
5694
+ columnVisibilityOperationsFromPrompt(prompt, columns) {
5695
+ const hide = !this.normalizedTextIncludesAny(this.normalizeLabel(prompt), [
5696
+ 'exibir',
5697
+ 'mostrar coluna',
5698
+ 'mostra coluna',
5699
+ 'revelar',
5700
+ ]);
5701
+ const operations = [];
5702
+ for (const column of columns) {
5703
+ const field = this.stringValue(column['field']);
5704
+ if (!field || !this.textMentionsColumn(prompt, column))
5705
+ continue;
5706
+ operations.push({
5707
+ operationId: 'column.visibility.set',
5708
+ target: { kind: 'column', field },
5709
+ input: { visible: !hide },
5710
+ });
5711
+ }
5712
+ return operations;
5713
+ }
4907
5714
  requestCarriesVisualPresentationContext(request) {
4908
5715
  const actionHints = this.toRecord(request.action?.contextHints);
4909
5716
  const requestHints = this.toRecord(request.contextHints);
@@ -4929,6 +5736,8 @@ class TableAgenticAuthoringTurnFlow {
4929
5736
  if (!normalizedPrompt)
4930
5737
  return false;
4931
5738
  const entry = this.filterFieldCatalogEntries.find((candidate) => candidate.name === fieldName);
5739
+ if (entry && this.promptMentionsFilterFieldAsNonRestrictiveScope(normalizedPrompt, entry))
5740
+ return false;
4932
5741
  const rawCandidates = [
4933
5742
  fieldName,
4934
5743
  this.humanizeFilterField(fieldName),
@@ -4941,6 +5750,59 @@ class TableAgenticAuthoringTurnFlow {
4941
5750
  .flatMap((candidate) => this.filterFieldMentionVariants(candidate ?? ''))
4942
5751
  .some((candidate) => candidate && this.normalizedTextContainsApproxPhrase(normalizedPrompt, candidate));
4943
5752
  }
5753
+ promptMentionsFilterFieldAsNonRestrictiveScope(normalizedPrompt, entry) {
5754
+ const variants = [
5755
+ entry.name,
5756
+ entry.label,
5757
+ this.humanizeFilterField(entry.name),
5758
+ ...entry.aliases,
5759
+ ...entry.relatedColumnFields,
5760
+ ...entry.relatedColumnLabels,
5761
+ ].flatMap((candidate) => this.filterFieldMentionVariants(candidate ?? ''));
5762
+ return variants.some((variant) => {
5763
+ if (!variant)
5764
+ return false;
5765
+ return [
5766
+ `qualquer ${variant}`,
5767
+ `qq ${variant}`,
5768
+ `tanto faz ${variant}`,
5769
+ `${variant} tanto faz`,
5770
+ `${variant} qualquer`,
5771
+ `independente de ${variant}`,
5772
+ `nao importa ${variant}`,
5773
+ `${variant} nao importa`,
5774
+ ].some((phrase) => this.normalizedTextContainsExactPhrase(normalizedPrompt, this.normalizeLabel(phrase)));
5775
+ });
5776
+ }
5777
+ removeNonRestrictiveFilterFieldClauses(value) {
5778
+ let normalized = value.trim();
5779
+ if (!normalized)
5780
+ return '';
5781
+ const fieldTerms = this.filterFieldCatalogEntries
5782
+ .flatMap((entry) => [
5783
+ entry.name,
5784
+ entry.label,
5785
+ this.humanizeFilterField(entry.name),
5786
+ ...entry.aliases,
5787
+ ...entry.relatedColumnFields,
5788
+ ...entry.relatedColumnLabels,
5789
+ ])
5790
+ .flatMap((term) => this.filterFieldMentionVariants(term ?? ''))
5791
+ .filter((term, index, terms) => !!term && terms.indexOf(term) === index);
5792
+ for (const term of fieldTerms) {
5793
+ const escaped = term.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
5794
+ normalized = normalized
5795
+ .replace(new RegExp(`(?:,|;)?\\s*(?:tanto faz|qualquer|qq|independente de|nao importa|não importa)\\s+${escaped}\\b`, 'giu'), '')
5796
+ .replace(new RegExp(`(?:,|;)?\\s*${escaped}\\s+(?:tanto faz|qualquer|nao importa|não importa)\\b`, 'giu'), '');
5797
+ }
5798
+ return normalized.trim();
5799
+ }
5800
+ normalizedTextContainsExactPhrase(normalizedText, normalizedPhrase) {
5801
+ if (!normalizedText || !normalizedPhrase)
5802
+ return false;
5803
+ const escaped = normalizedPhrase.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&').replace(/\s+/gu, '\\s+');
5804
+ return new RegExp(`(^|[^a-z0-9])${escaped}($|[^a-z0-9])`, 'u').test(normalizedText);
5805
+ }
4944
5806
  selectedRecordFilterPromptGroundingScore(prompt, candidate) {
4945
5807
  const fieldName = this.stringValue(candidate['field']);
4946
5808
  if (!fieldName)
@@ -5493,7 +6355,20 @@ class TableAgenticAuthoringTurnFlow {
5493
6355
  }
5494
6356
  buildClarificationDiagnostics(response) {
5495
6357
  const continuation = this.extractPendingComponentEditContinuation(response);
5496
- return continuation ? { tableComponentEditContinuation: continuation } : undefined;
6358
+ const responseDiagnostics = this.toRecord(response.diagnostics);
6359
+ if (!continuation && !responseDiagnostics)
6360
+ return undefined;
6361
+ return {
6362
+ ...(responseDiagnostics ?? {}),
6363
+ ...(continuation ? { tableComponentEditContinuation: continuation } : {}),
6364
+ };
6365
+ }
6366
+ pendingClarificationSourcePromptForTurn(request, diagnostics) {
6367
+ const declaredFilter = this.toRecord(diagnostics?.['tableDeclaredFilterClarification']);
6368
+ return this.stringValue(declaredFilter?.['sourcePrompt']).trim()
6369
+ || request.pendingClarification?.sourcePrompt?.trim()
6370
+ || request.prompt?.trim()
6371
+ || '';
5497
6372
  }
5498
6373
  buildReviewDiagnostics(response, warnings) {
5499
6374
  const memory = this.extractComponentEditDecisionMemory(response);
@@ -6377,6 +7252,9 @@ class TableAgenticAuthoringTurnFlow {
6377
7252
  const normalizedOptions = new Set(options
6378
7253
  .map((option) => this.normalizeLabel(option))
6379
7254
  .filter((option) => !!option));
7255
+ if (this.promptRequestsColumnVisibilityEdit(prompt)) {
7256
+ return null;
7257
+ }
6380
7258
  const promptLooksLikeFilter = this.promptRequestsRuntimeFilter(prompt)
6381
7259
  || ['filtro', 'filtrar', 'busca', 'buscar', 'procura', 'procurar', 'mostra', 'mostrar'].some((token) => this.normalizedTextContainsApproxToken(prompt, token));
6382
7260
  const responseLooksFilterFieldClarification = responseText.includes('filtro declarado')
@@ -6538,7 +7416,7 @@ class TableAgenticAuthoringTurnFlow {
6538
7416
  return quoted;
6539
7417
  const match = trimmed.match(/\b(?:de|da|do|dos|das|por|com|contendo|contem|contém)\s+(.{2,})$/iu);
6540
7418
  const candidate = (match?.[1] ?? '').trim();
6541
- return candidate
7419
+ return this.removeNonRestrictiveFilterFieldClauses(candidate)
6542
7420
  .replace(/[?.!,;:]+$/u, '')
6543
7421
  .trim();
6544
7422
  }
@@ -6576,6 +7454,7 @@ class TableAgenticAuthoringTurnFlow {
6576
7454
  };
6577
7455
  });
6578
7456
  }
7457
+ return [];
6579
7458
  }
6580
7459
  if ((response.type === 'clarification' || response.type === 'info')
6581
7460
  && this.shouldCurateVisibilityClarificationQuickReplies(response, prompt)) {
@@ -6684,6 +7563,49 @@ class TableAgenticAuthoringTurnFlow {
6684
7563
  // semantic scope choices the user needs to answer.
6685
7564
  return true;
6686
7565
  }
7566
+ promptRequestsColumnVisibilityEdit(normalizedPrompt) {
7567
+ if (!normalizedPrompt)
7568
+ return false;
7569
+ const asksVisibilityOrPrivacy = [
7570
+ 'ocultacao',
7571
+ 'ocultação',
7572
+ 'oculta',
7573
+ 'ocultar',
7574
+ 'esconde',
7575
+ 'esconder',
7576
+ 'remova a coluna',
7577
+ 'remover a coluna',
7578
+ 'remova as colunas',
7579
+ 'remover as colunas',
7580
+ 'tirar da visualizacao',
7581
+ 'tirar da visualização',
7582
+ 'publico',
7583
+ 'público',
7584
+ 'privacidade',
7585
+ 'sensivel',
7586
+ 'sensível',
7587
+ ].some((token) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, token));
7588
+ if (!asksVisibilityOrPrivacy)
7589
+ return false;
7590
+ const mentionsColumnScope = [
7591
+ 'coluna',
7592
+ 'colunas',
7593
+ 'visualizacao',
7594
+ 'visualização',
7595
+ 'tabela',
7596
+ 'email',
7597
+ 'e-mail',
7598
+ 'salario',
7599
+ 'salário',
7600
+ 'cpf',
7601
+ 'documento',
7602
+ 'telefone',
7603
+ ].some((token) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, token));
7604
+ // Residual guard only: the LLM/authoring contract still decides the primary
7605
+ // intent. Once a turn is already in a column-visibility scope, filter
7606
+ // quick-reply curation must not reinterpret it as row filtering.
7607
+ return mentionsColumnScope;
7608
+ }
6687
7609
  visibilityClarificationQuickReplyOptions() {
6688
7610
  return [
6689
7611
  {
@@ -6728,6 +7650,10 @@ class TableAgenticAuthoringTurnFlow {
6728
7650
  const optionCount = response.options?.length ?? 0;
6729
7651
  if (payloadCount <= 0 && optionCount <= 0)
6730
7652
  return false;
7653
+ if (this.promptRequestsColumnVisibilityEdit(normalizedPrompt))
7654
+ return false;
7655
+ if (payloadCount > 0 && normalizedPrompt && !this.promptRequestsRuntimeFilter(normalizedPrompt))
7656
+ return false;
6731
7657
  if (this.responseAsksForDeclaredFilterField(response))
6732
7658
  return true;
6733
7659
  if (!normalizedPrompt || !this.promptRequestsRuntimeFilter(normalizedPrompt))