@praxisui/table 8.0.0-beta.86 → 8.0.0-beta.88

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
852
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
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,10 @@ 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
+ }
1812
2335
  const continuedSurfaceRowActionFromInfo = this.selectedRecordSurfaceRowActionPlanForInfo(response, request);
1813
2336
  if (continuedSurfaceRowActionFromInfo) {
1814
2337
  return this.compileAdapterResponse(continuedSurfaceRowActionFromInfo, request);
@@ -1887,6 +2410,10 @@ class TableAgenticAuthoringTurnFlow {
1887
2410
  };
1888
2411
  }
1889
2412
  if (!compiled) {
2413
+ const continuedColumnVisibilityNarrative = this.columnVisibilityPlanForGroundedNarrative(response, request);
2414
+ if (continuedColumnVisibilityNarrative) {
2415
+ return this.compileAdapterResponse(continuedColumnVisibilityNarrative, request);
2416
+ }
1890
2417
  const continuedColumnFormatNarrative = this.columnFormatPlanForNonExecutableNarrative(response, request);
1891
2418
  if (continuedColumnFormatNarrative) {
1892
2419
  return this.compileAdapterResponse(continuedColumnFormatNarrative, request);
@@ -1898,6 +2425,10 @@ class TableAgenticAuthoringTurnFlow {
1898
2425
  return response;
1899
2426
  }
1900
2427
  if (compiled.type === 'error') {
2428
+ const continuedColumnVisibilityError = this.columnVisibilityPlanForMisroutedSurfaceError(response, request, compiled);
2429
+ if (continuedColumnVisibilityError) {
2430
+ return this.compileAdapterResponse(continuedColumnVisibilityError, request);
2431
+ }
1901
2432
  const continuedBulkRouteAction = this.bulkRouteActionPlanForInvalidExecutable(response, request, compiled.warnings);
1902
2433
  if (continuedBulkRouteAction) {
1903
2434
  return this.compileAdapterResponse(continuedBulkRouteAction, request);
@@ -1953,6 +2484,10 @@ class TableAgenticAuthoringTurnFlow {
1953
2484
  patch: normalizedCompiled.patch,
1954
2485
  warnings: warnings.length ? warnings : undefined,
1955
2486
  };
2487
+ const columnVisibilitySurfaceMismatch = this.columnVisibilityPlanForMisroutedSurfaceRuntime(compiledResponse, request);
2488
+ if (columnVisibilitySurfaceMismatch) {
2489
+ return this.compileAdapterResponse(columnVisibilitySurfaceMismatch, request);
2490
+ }
1956
2491
  const visualSurfaceMismatch = this.visualPresentationSurfaceRuntimeMismatch(compiledResponse, request);
1957
2492
  if (visualSurfaceMismatch) {
1958
2493
  return visualSurfaceMismatch;
@@ -4100,6 +4635,28 @@ class TableAgenticAuthoringTurnFlow {
4100
4635
  ?? this.booleanSimNaoOperationsFromGroundedClarification(prompt, responseText, columns)
4101
4636
  ?? this.booleanSimNaoOperationsFromSingleGroundedBooleanColumn(prompt, responseText, columns)
4102
4637
  ?? this.booleanSimNaoOperationsFromGroundedResponseField(prompt, responseText, columns);
4638
+ const promptRequestsSimNao = this.normalizedTextIncludesAny(this.normalizeLabel(prompt), [
4639
+ 'sim nao',
4640
+ 'sim/nao',
4641
+ 'sim não',
4642
+ 'sim/não',
4643
+ 'sim ou nao',
4644
+ 'sim ou não',
4645
+ ]);
4646
+ const responseGroundsBooleanSimNao = this.normalizedTextIncludesAny(normalizedResponse, [
4647
+ 'sim quando true',
4648
+ 'sim quando verdadeiro',
4649
+ 'nao quando false',
4650
+ 'não quando false',
4651
+ 'nao quando falso',
4652
+ 'não quando falso',
4653
+ 'sim para true',
4654
+ 'sim para verdadeiro',
4655
+ 'nao para false',
4656
+ 'não para false',
4657
+ 'nao para falso',
4658
+ 'não para falso',
4659
+ ]);
4103
4660
  const booleanFormatClarification = !!booleanOperations?.length && this.normalizedTextIncludesAny(normalizedResponse, [
4104
4661
  'sim nao',
4105
4662
  'sim / nao',
@@ -4110,7 +4667,7 @@ class TableAgenticAuthoringTurnFlow {
4110
4667
  'badges com textos sim nao',
4111
4668
  'mostrar apenas texto sim nao',
4112
4669
  'valores booleanos como sim ou nao',
4113
- ]);
4670
+ ]) || (!!booleanOperations?.length && promptRequestsSimNao && responseGroundsBooleanSimNao);
4114
4671
  if (booleanOperations?.length && (booleanInfo || booleanFormatClarification)) {
4115
4672
  return this.componentEditPlanResponse(booleanOperations, 'Vou mostrar o booleano como Sim/Nao na coluna indicada.', [
4116
4673
  ...(response.warnings ?? []),
@@ -4882,6 +5439,199 @@ class TableAgenticAuthoringTurnFlow {
4882
5439
  ],
4883
5440
  };
4884
5441
  }
5442
+ columnVisibilityPlanForMisroutedSurfaceRuntime(response, request) {
5443
+ if (!request)
5444
+ return null;
5445
+ const prompt = this.columnVisibilityPromptForTurn(request);
5446
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5447
+ return null;
5448
+ if (!this.patchContainsRuntimeOperationId(response.patch, 'dynamicPage.surface.open'))
5449
+ return null;
5450
+ const currentConfig = this.adapter.getCurrentConfig?.();
5451
+ const columns = Array.isArray(currentConfig?.['columns'])
5452
+ ? currentConfig['columns']
5453
+ .map((column) => this.toRecord(column))
5454
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5455
+ : [];
5456
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5457
+ if (!operations.length) {
5458
+ return {
5459
+ type: 'clarification',
5460
+ sessionId: request.sessionId,
5461
+ message: 'Entendi que o pedido é sobre ocultar ou exibir colunas, não sobre abrir uma superfície do registro.',
5462
+ questions: ['Quais colunas você quer ocultar ou exibir?'],
5463
+ options: columns
5464
+ .slice(0, 8)
5465
+ .map((column) => this.stringValue(column['header']) || this.stringValue(column['field']))
5466
+ .filter((label) => !!label),
5467
+ warnings: [
5468
+ ...(response.warnings ?? []),
5469
+ 'column-visibility-request-blocked-surface-runtime-operation',
5470
+ ],
5471
+ };
5472
+ }
5473
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5474
+ ...(response.warnings ?? []),
5475
+ 'column-visibility-continued-from-surface-runtime-misroute',
5476
+ 'Residual continuity guard acted only after the LLM proposed dynamicPage.surface.open for a column visibility/privacy request grounded in declared table columns.',
5477
+ ]);
5478
+ }
5479
+ columnVisibilityPlanForGroundedClarification(response, request) {
5480
+ if (!request)
5481
+ return null;
5482
+ if (response.type !== 'clarification')
5483
+ return null;
5484
+ const prompt = this.columnVisibilityPromptForTurn(request);
5485
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5486
+ return null;
5487
+ const questionText = [
5488
+ response.message,
5489
+ ...(response.questions ?? []),
5490
+ ...(response.options ?? []),
5491
+ ].join(' ');
5492
+ const asksForColumn = this.normalizedTextIncludesAny(this.normalizeLabel(questionText), [
5493
+ 'qual coluna',
5494
+ 'quais colunas',
5495
+ 'coluna devo usar',
5496
+ 'colunas devo usar',
5497
+ ]);
5498
+ const asksForVisibilityScope = this.normalizedTextIncludesAny(this.normalizeLabel(questionText), [
5499
+ 'globalmente',
5500
+ 'todos os usuarios',
5501
+ 'todos os usuários',
5502
+ 'apresentacao publica',
5503
+ 'apresentação pública',
5504
+ 'visualizacao publica',
5505
+ 'visualização pública',
5506
+ 'vista padrao',
5507
+ 'vista padrão',
5508
+ 'confirma qual opcao',
5509
+ 'confirma qual opção',
5510
+ ]);
5511
+ if (!asksForColumn && !asksForVisibilityScope)
5512
+ return null;
5513
+ const currentConfig = this.adapter.getCurrentConfig?.();
5514
+ const columns = Array.isArray(currentConfig?.['columns'])
5515
+ ? currentConfig['columns']
5516
+ .map((column) => this.toRecord(column))
5517
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5518
+ : [];
5519
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5520
+ if (!operations.length)
5521
+ return null;
5522
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5523
+ ...(response.warnings ?? []),
5524
+ 'column-visibility-continued-from-grounded-clarification',
5525
+ '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.',
5526
+ ]);
5527
+ }
5528
+ columnVisibilityPlanForGroundedNarrative(response, request) {
5529
+ if (!request)
5530
+ return null;
5531
+ const prompt = this.columnVisibilityPromptForTurn(request);
5532
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5533
+ return null;
5534
+ const responseText = this.normalizeLabel([
5535
+ response.message,
5536
+ response.explanation,
5537
+ response.code,
5538
+ ].join(' '));
5539
+ const narratesVisibilityPlan = this.normalizedTextIncludesAny(responseText, [
5540
+ 'componenteditplan',
5541
+ 'column.visibility.set',
5542
+ 'visible false',
5543
+ 'invisivel',
5544
+ 'invisível',
5545
+ 'ocultar a coluna',
5546
+ 'ocultar as colunas',
5547
+ ]);
5548
+ if (!narratesVisibilityPlan)
5549
+ return null;
5550
+ const currentConfig = this.adapter.getCurrentConfig?.();
5551
+ const columns = Array.isArray(currentConfig?.['columns'])
5552
+ ? currentConfig['columns']
5553
+ .map((column) => this.toRecord(column))
5554
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5555
+ : [];
5556
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5557
+ if (!operations.length)
5558
+ return null;
5559
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5560
+ ...(response.warnings ?? []),
5561
+ 'column-visibility-continued-from-non-executable-narrative',
5562
+ 'Residual continuity guard acted only after the LLM narrated a column visibility plan without returning an executable envelope.',
5563
+ ]);
5564
+ }
5565
+ columnVisibilityPlanForMisroutedSurfaceError(response, request, compiledError) {
5566
+ if (!request)
5567
+ return null;
5568
+ const prompt = this.columnVisibilityPromptForTurn(request);
5569
+ if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
5570
+ return null;
5571
+ const errorText = this.normalizeLabel([
5572
+ response.message,
5573
+ response.explanation,
5574
+ compiledError.message,
5575
+ compiledError.explanation,
5576
+ ...(compiledError.warnings ?? []),
5577
+ ].join(' '));
5578
+ if (!this.normalizedTextIncludesAny(errorText, [
5579
+ 'dynamicpage.surface.open',
5580
+ 'surface.open',
5581
+ 'superficie',
5582
+ 'superfície',
5583
+ ]))
5584
+ return null;
5585
+ const currentConfig = this.adapter.getCurrentConfig?.();
5586
+ const columns = Array.isArray(currentConfig?.['columns'])
5587
+ ? currentConfig['columns']
5588
+ .map((column) => this.toRecord(column))
5589
+ .filter((column) => !!column && !!this.stringValue(column['field']))
5590
+ : [];
5591
+ const operations = this.columnVisibilityOperationsFromPrompt(prompt, columns);
5592
+ if (!operations.length)
5593
+ return null;
5594
+ return this.componentEditPlanResponse(operations, 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
5595
+ ...(response.warnings ?? []),
5596
+ ...(compiledError.warnings ?? []),
5597
+ 'column-visibility-continued-from-surface-runtime-error',
5598
+ 'Residual continuity guard acted only after a misrouted surface runtime operation failed validation for a column visibility request.',
5599
+ ]);
5600
+ }
5601
+ columnVisibilityPromptForTurn(request) {
5602
+ const currentPrompt = (request.prompt ?? '').trim();
5603
+ if (this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(currentPrompt))) {
5604
+ return currentPrompt;
5605
+ }
5606
+ const previousVisibilityPrompt = [...(request.messages ?? [])]
5607
+ .reverse()
5608
+ .filter((message) => message.role === 'user')
5609
+ .map((message) => message.text.trim())
5610
+ .find((message) => this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(message)));
5611
+ return [previousVisibilityPrompt, currentPrompt]
5612
+ .filter((part) => !!part)
5613
+ .join(' ');
5614
+ }
5615
+ columnVisibilityOperationsFromPrompt(prompt, columns) {
5616
+ const hide = !this.normalizedTextIncludesAny(this.normalizeLabel(prompt), [
5617
+ 'exibir',
5618
+ 'mostrar coluna',
5619
+ 'mostra coluna',
5620
+ 'revelar',
5621
+ ]);
5622
+ const operations = [];
5623
+ for (const column of columns) {
5624
+ const field = this.stringValue(column['field']);
5625
+ if (!field || !this.textMentionsColumn(prompt, column))
5626
+ continue;
5627
+ operations.push({
5628
+ operationId: 'column.visibility.set',
5629
+ target: { kind: 'column', field },
5630
+ input: { visible: !hide },
5631
+ });
5632
+ }
5633
+ return operations;
5634
+ }
4885
5635
  requestCarriesVisualPresentationContext(request) {
4886
5636
  const actionHints = this.toRecord(request.action?.contextHints);
4887
5637
  const requestHints = this.toRecord(request.contextHints);
@@ -4907,6 +5657,8 @@ class TableAgenticAuthoringTurnFlow {
4907
5657
  if (!normalizedPrompt)
4908
5658
  return false;
4909
5659
  const entry = this.filterFieldCatalogEntries.find((candidate) => candidate.name === fieldName);
5660
+ if (entry && this.promptMentionsFilterFieldAsNonRestrictiveScope(normalizedPrompt, entry))
5661
+ return false;
4910
5662
  const rawCandidates = [
4911
5663
  fieldName,
4912
5664
  this.humanizeFilterField(fieldName),
@@ -4919,6 +5671,59 @@ class TableAgenticAuthoringTurnFlow {
4919
5671
  .flatMap((candidate) => this.filterFieldMentionVariants(candidate ?? ''))
4920
5672
  .some((candidate) => candidate && this.normalizedTextContainsApproxPhrase(normalizedPrompt, candidate));
4921
5673
  }
5674
+ promptMentionsFilterFieldAsNonRestrictiveScope(normalizedPrompt, entry) {
5675
+ const variants = [
5676
+ entry.name,
5677
+ entry.label,
5678
+ this.humanizeFilterField(entry.name),
5679
+ ...entry.aliases,
5680
+ ...entry.relatedColumnFields,
5681
+ ...entry.relatedColumnLabels,
5682
+ ].flatMap((candidate) => this.filterFieldMentionVariants(candidate ?? ''));
5683
+ return variants.some((variant) => {
5684
+ if (!variant)
5685
+ return false;
5686
+ return [
5687
+ `qualquer ${variant}`,
5688
+ `qq ${variant}`,
5689
+ `tanto faz ${variant}`,
5690
+ `${variant} tanto faz`,
5691
+ `${variant} qualquer`,
5692
+ `independente de ${variant}`,
5693
+ `nao importa ${variant}`,
5694
+ `${variant} nao importa`,
5695
+ ].some((phrase) => this.normalizedTextContainsExactPhrase(normalizedPrompt, this.normalizeLabel(phrase)));
5696
+ });
5697
+ }
5698
+ removeNonRestrictiveFilterFieldClauses(value) {
5699
+ let normalized = value.trim();
5700
+ if (!normalized)
5701
+ return '';
5702
+ const fieldTerms = this.filterFieldCatalogEntries
5703
+ .flatMap((entry) => [
5704
+ entry.name,
5705
+ entry.label,
5706
+ this.humanizeFilterField(entry.name),
5707
+ ...entry.aliases,
5708
+ ...entry.relatedColumnFields,
5709
+ ...entry.relatedColumnLabels,
5710
+ ])
5711
+ .flatMap((term) => this.filterFieldMentionVariants(term ?? ''))
5712
+ .filter((term, index, terms) => !!term && terms.indexOf(term) === index);
5713
+ for (const term of fieldTerms) {
5714
+ const escaped = term.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
5715
+ normalized = normalized
5716
+ .replace(new RegExp(`(?:,|;)?\\s*(?:tanto faz|qualquer|qq|independente de|nao importa|não importa)\\s+${escaped}\\b`, 'giu'), '')
5717
+ .replace(new RegExp(`(?:,|;)?\\s*${escaped}\\s+(?:tanto faz|qualquer|nao importa|não importa)\\b`, 'giu'), '');
5718
+ }
5719
+ return normalized.trim();
5720
+ }
5721
+ normalizedTextContainsExactPhrase(normalizedText, normalizedPhrase) {
5722
+ if (!normalizedText || !normalizedPhrase)
5723
+ return false;
5724
+ const escaped = normalizedPhrase.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&').replace(/\s+/gu, '\\s+');
5725
+ return new RegExp(`(^|[^a-z0-9])${escaped}($|[^a-z0-9])`, 'u').test(normalizedText);
5726
+ }
4922
5727
  selectedRecordFilterPromptGroundingScore(prompt, candidate) {
4923
5728
  const fieldName = this.stringValue(candidate['field']);
4924
5729
  if (!fieldName)
@@ -5471,7 +6276,20 @@ class TableAgenticAuthoringTurnFlow {
5471
6276
  }
5472
6277
  buildClarificationDiagnostics(response) {
5473
6278
  const continuation = this.extractPendingComponentEditContinuation(response);
5474
- return continuation ? { tableComponentEditContinuation: continuation } : undefined;
6279
+ const responseDiagnostics = this.toRecord(response.diagnostics);
6280
+ if (!continuation && !responseDiagnostics)
6281
+ return undefined;
6282
+ return {
6283
+ ...(responseDiagnostics ?? {}),
6284
+ ...(continuation ? { tableComponentEditContinuation: continuation } : {}),
6285
+ };
6286
+ }
6287
+ pendingClarificationSourcePromptForTurn(request, diagnostics) {
6288
+ const declaredFilter = this.toRecord(diagnostics?.['tableDeclaredFilterClarification']);
6289
+ return this.stringValue(declaredFilter?.['sourcePrompt']).trim()
6290
+ || request.pendingClarification?.sourcePrompt?.trim()
6291
+ || request.prompt?.trim()
6292
+ || '';
5475
6293
  }
5476
6294
  buildReviewDiagnostics(response, warnings) {
5477
6295
  const memory = this.extractComponentEditDecisionMemory(response);
@@ -6355,6 +7173,9 @@ class TableAgenticAuthoringTurnFlow {
6355
7173
  const normalizedOptions = new Set(options
6356
7174
  .map((option) => this.normalizeLabel(option))
6357
7175
  .filter((option) => !!option));
7176
+ if (this.promptRequestsColumnVisibilityEdit(prompt)) {
7177
+ return null;
7178
+ }
6358
7179
  const promptLooksLikeFilter = this.promptRequestsRuntimeFilter(prompt)
6359
7180
  || ['filtro', 'filtrar', 'busca', 'buscar', 'procura', 'procurar', 'mostra', 'mostrar'].some((token) => this.normalizedTextContainsApproxToken(prompt, token));
6360
7181
  const responseLooksFilterFieldClarification = responseText.includes('filtro declarado')
@@ -6516,7 +7337,7 @@ class TableAgenticAuthoringTurnFlow {
6516
7337
  return quoted;
6517
7338
  const match = trimmed.match(/\b(?:de|da|do|dos|das|por|com|contendo|contem|contém)\s+(.{2,})$/iu);
6518
7339
  const candidate = (match?.[1] ?? '').trim();
6519
- return candidate
7340
+ return this.removeNonRestrictiveFilterFieldClauses(candidate)
6520
7341
  .replace(/[?.!,;:]+$/u, '')
6521
7342
  .trim();
6522
7343
  }
@@ -6554,6 +7375,7 @@ class TableAgenticAuthoringTurnFlow {
6554
7375
  };
6555
7376
  });
6556
7377
  }
7378
+ return [];
6557
7379
  }
6558
7380
  if ((response.type === 'clarification' || response.type === 'info')
6559
7381
  && this.shouldCurateVisibilityClarificationQuickReplies(response, prompt)) {
@@ -6662,6 +7484,49 @@ class TableAgenticAuthoringTurnFlow {
6662
7484
  // semantic scope choices the user needs to answer.
6663
7485
  return true;
6664
7486
  }
7487
+ promptRequestsColumnVisibilityEdit(normalizedPrompt) {
7488
+ if (!normalizedPrompt)
7489
+ return false;
7490
+ const asksVisibilityOrPrivacy = [
7491
+ 'ocultacao',
7492
+ 'ocultação',
7493
+ 'oculta',
7494
+ 'ocultar',
7495
+ 'esconde',
7496
+ 'esconder',
7497
+ 'remova a coluna',
7498
+ 'remover a coluna',
7499
+ 'remova as colunas',
7500
+ 'remover as colunas',
7501
+ 'tirar da visualizacao',
7502
+ 'tirar da visualização',
7503
+ 'publico',
7504
+ 'público',
7505
+ 'privacidade',
7506
+ 'sensivel',
7507
+ 'sensível',
7508
+ ].some((token) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, token));
7509
+ if (!asksVisibilityOrPrivacy)
7510
+ return false;
7511
+ const mentionsColumnScope = [
7512
+ 'coluna',
7513
+ 'colunas',
7514
+ 'visualizacao',
7515
+ 'visualização',
7516
+ 'tabela',
7517
+ 'email',
7518
+ 'e-mail',
7519
+ 'salario',
7520
+ 'salário',
7521
+ 'cpf',
7522
+ 'documento',
7523
+ 'telefone',
7524
+ ].some((token) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, token));
7525
+ // Residual guard only: the LLM/authoring contract still decides the primary
7526
+ // intent. Once a turn is already in a column-visibility scope, filter
7527
+ // quick-reply curation must not reinterpret it as row filtering.
7528
+ return mentionsColumnScope;
7529
+ }
6665
7530
  visibilityClarificationQuickReplyOptions() {
6666
7531
  return [
6667
7532
  {
@@ -6706,6 +7571,10 @@ class TableAgenticAuthoringTurnFlow {
6706
7571
  const optionCount = response.options?.length ?? 0;
6707
7572
  if (payloadCount <= 0 && optionCount <= 0)
6708
7573
  return false;
7574
+ if (this.promptRequestsColumnVisibilityEdit(normalizedPrompt))
7575
+ return false;
7576
+ if (payloadCount > 0 && normalizedPrompt && !this.promptRequestsRuntimeFilter(normalizedPrompt))
7577
+ return false;
6709
7578
  if (this.responseAsksForDeclaredFilterField(response))
6710
7579
  return true;
6711
7580
  if (!normalizedPrompt || !this.promptRequestsRuntimeFilter(normalizedPrompt))