@praxisui/table 8.0.0-beta.52 → 8.0.0-beta.54

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.
@@ -39528,7 +39528,7 @@ class PraxisTable {
39528
39528
  if (this.aiAdapter || this.aiAdapterLoadStarted)
39529
39529
  return;
39530
39530
  this.aiAdapterLoadStarted = true;
39531
- import('./praxisui-table-table-ai.adapter-eGo1zPuy.mjs')
39531
+ import('./praxisui-table-table-ai.adapter-B3KQThqg.mjs')
39532
39532
  .then(({ TableAiAdapter }) => {
39533
39533
  this.aiAdapter = new TableAiAdapter(this);
39534
39534
  this.initializeAiAssistantController();
@@ -39678,7 +39678,7 @@ class PraxisTable {
39678
39678
  initializeAiAssistantController() {
39679
39679
  if (!this.aiAdapter || this.aiAssistantController)
39680
39680
  return;
39681
- import('./praxisui-table-table-agentic-authoring-turn-flow-DtoCwCVX.mjs')
39681
+ import('./praxisui-table-table-agentic-authoring-turn-flow-B075RDUj.mjs')
39682
39682
  .then(({ TableAgenticAuthoringTurnFlow }) => {
39683
39683
  if (this.aiAssistantController || !this.aiAdapter)
39684
39684
  return;
@@ -48,11 +48,9 @@ class TableAgenticAuthoringTurnFlow {
48
48
  this.selectedRecordFieldsForTurn = this.extractSelectedRecordFields(contextHints);
49
49
  this.selectedRecordsCountForTurn = this.extractSelectedRecordsCount(contextHints);
50
50
  this.filterExpressionSupportedForTurn = this.resolveFilterExpressionSupported(contextHints);
51
- const localResponse = this.selectedRecordLocalResponse(request, contextHints);
52
51
  const messages = this.withCapabilitySystemMessages(this.toChatMessages(request.messages, prompt), contextHints);
53
52
  return {
54
53
  contextHints,
55
- ...(localResponse ? { localResponse } : {}),
56
54
  patchRequest: {
57
55
  componentId,
58
56
  componentType,
@@ -80,21 +78,8 @@ class TableAgenticAuthoringTurnFlow {
80
78
  return this.submitViaSnapshot(request, currentState, run);
81
79
  }
82
80
  async submitViaSnapshot(request, currentState, buildRequest) {
83
- const { patchRequest, contextHints, localResponse } = await buildRequest();
84
- if (localResponse) {
85
- return this.toTurnResult(this.compileAdapterResponse(localResponse, request), request);
86
- }
87
- let response;
88
- try {
89
- response = await firstValueFrom(this.aiApi.getPatch(patchRequest));
90
- }
91
- catch (error) {
92
- const recovery = this.selectedRecordBackendFailureClarification(request, contextHints ?? null, error);
93
- if (recovery) {
94
- return this.toTurnResult(recovery, request);
95
- }
96
- throw error;
97
- }
81
+ const { patchRequest } = await buildRequest();
82
+ const response = await firstValueFrom(this.aiApi.getPatch(patchRequest));
98
83
  return this.toTurnResult(this.compileAdapterResponse(this.groundRelativeColumnOrder(response, request, currentState), request), request);
99
84
  }
100
85
  submitViaStream(request, prompt, currentState, componentId, buildRequest) {
@@ -226,15 +211,8 @@ class TableAgenticAuthoringTurnFlow {
226
211
  void (async () => {
227
212
  try {
228
213
  emitProgress(this.buildInitialProgressMessage(prompt, componentId));
229
- const { patchRequest, contextHints, localResponse } = await buildRequest();
214
+ const { patchRequest, contextHints } = await buildRequest();
230
215
  streamContextHints = contextHints ?? null;
231
- if (localResponse) {
232
- subscriber.next(this.toTurnResult(this.compileAdapterResponse(localResponse, request), request));
233
- subscriber.complete();
234
- closed = true;
235
- closeConnection();
236
- return;
237
- }
238
216
  if (this.shouldUseSelectedRecordSurfaceSnapshotFallback(streamContextHints)) {
239
217
  const result = await this.submitViaSnapshot(request, currentState, buildRequest);
240
218
  if (!closed) {
@@ -574,26 +552,6 @@ class TableAgenticAuthoringTurnFlow {
574
552
  isTurnInProgressResponse(response) {
575
553
  return this.normalizeLabel(response.code ?? '') === 'turn in progress';
576
554
  }
577
- selectedRecordLocalResponse(request, contextHints) {
578
- if (this.authoringContractHasResponseModes(contextHints)) {
579
- return this.completeSelectedRecordFilterClarification(request, contextHints);
580
- }
581
- return this.selectedRecordMissingSelectionAnswer(request, contextHints)
582
- ?? this.selectedRecordExportOnlyRequest(request, contextHints)
583
- ?? this.selectedRecordSingleCandidateQuestionAnswer(request, contextHints)
584
- ?? this.selectedRecordFieldQuestionAnswer(request, contextHints)
585
- ?? this.selectedRecordMultiFieldQuestionAnswer(request, contextHints)
586
- ?? this.completeSelectedRecordFilterApplyRequest(request, contextHints)
587
- ?? this.selectedRecordCommonQuestionAnswer(request, contextHints)
588
- ?? this.completeSelectedRecordFilterClarification(request, contextHints)
589
- ?? this.selectedRecordMultipleFilterFieldClarification(request, contextHints)
590
- ?? this.selectedRecordSimilarityClarification(request, contextHints);
591
- }
592
- authoringContractHasResponseModes(contextHints) {
593
- const authoringContract = this.toRecord(contextHints?.['authoringContract']);
594
- return Array.isArray(authoringContract?.['responseModes'])
595
- && authoringContract['responseModes'].length > 0;
596
- }
597
555
  compileAdapterResponse(response, request) {
598
556
  if (response.type === 'clarification' || response.type === 'info' || response.type === 'error') {
599
557
  return response;
@@ -700,851 +658,6 @@ class TableAgenticAuthoringTurnFlow {
700
658
  .map((operation) => this.toRecord(operation))
701
659
  .some((operation) => this.stringValue(operation?.['operationId']) === 'table.filter.apply');
702
660
  }
703
- completeSelectedRecordFilterClarification(request, contextHints) {
704
- if (request.action?.kind !== 'clarify')
705
- return null;
706
- const selectedRecordsFilter = this.toRecord(contextHints?.['selectedRecordsFilter']);
707
- const field = this.stringValue(selectedRecordsFilter?.['field']);
708
- if (!field)
709
- return null;
710
- const candidate = this.selectedRecordFilterCandidate(contextHints, field);
711
- const criteria = this.toRecord(candidate?.['criteria']);
712
- if (!criteria || Object.keys(criteria).length === 0)
713
- return null;
714
- const label = this.stringValue(candidate?.['label'])
715
- || this.stringValue(selectedRecordsFilter?.['label'])
716
- || this.humanizeFilterField(field);
717
- return this.selectedRecordFilterApplyResponse(label, criteria, [
718
- 'selected-record-filter-clarification-materialized',
719
- 'Clarification choice carried canonical filter field context; selectedRecordsContext.filterCandidates supplied runtime criteria.',
720
- ], request, contextHints);
721
- }
722
- completeSelectedRecordFilterApplyRequest(request, contextHints) {
723
- if (this.selectedRecordsCountForTurn <= 0)
724
- return null;
725
- const prompt = this.selectedRecordFilterGroundingPrompt(request, contextHints);
726
- if (!prompt || !this.selectedRecordFilterApplyRequested(prompt))
727
- return null;
728
- const availableCandidates = this.selectedRecordFilterCandidates(contextHints)
729
- .filter((candidate) => {
730
- const field = this.stringValue(candidate['field']);
731
- const criteria = this.toRecord(candidate['criteria']);
732
- return !!field
733
- && !!criteria
734
- && Object.keys(criteria).length > 0;
735
- });
736
- const candidates = availableCandidates
737
- .map((candidate) => ({
738
- candidate,
739
- score: this.selectedRecordFilterPromptGroundingScore(prompt, candidate),
740
- }))
741
- .filter((entry) => entry.score > 0)
742
- .sort((left, right) => right.score - left.score);
743
- if (!candidates.length)
744
- return null;
745
- if (candidates.length > 1 && candidates[0].score === candidates[1].score)
746
- return null;
747
- const candidate = candidates[0].candidate;
748
- const field = this.stringValue(candidate['field']);
749
- const criteria = this.toRecord(candidate['criteria']);
750
- if (!field || !criteria)
751
- return null;
752
- const label = this.stringValue(candidate['label']) || this.humanizeFilterField(field);
753
- return this.selectedRecordFilterApplyResponse(label, criteria, [
754
- 'selected-record-filter-request-materialized',
755
- 'Selected records supplied canonical filterCandidates; user prompt grounded one candidate through the declared filter field catalog.',
756
- ], request, contextHints);
757
- }
758
- selectedRecordFieldQuestionAnswer(request, contextHints) {
759
- if (this.selectedRecordsCountForTurn !== 1)
760
- return null;
761
- const prompt = request.prompt ?? '';
762
- const normalizedPrompt = this.normalizeLabel(prompt);
763
- if (!this.selectedRecordInformationalQuestionRequested(normalizedPrompt))
764
- return null;
765
- if (this.selectedRecordFilterApplyRequested(prompt))
766
- return null;
767
- const row = this.selectedRecordSampleRows(contextHints)[0];
768
- if (!row)
769
- return null;
770
- const candidates = Object.keys(row)
771
- .map((field) => ({
772
- field,
773
- score: this.selectedRecordQuestionFieldScore(normalizedPrompt, field),
774
- }))
775
- .filter((entry) => entry.score > 0)
776
- .sort((left, right) => right.score - left.score);
777
- const field = candidates.length
778
- ? this.singleTopSelectedRecordQuestionField(candidates)
779
- : this.singleTemporalSelectedRecordField(normalizedPrompt, row);
780
- if (!field)
781
- return null;
782
- const value = row[field];
783
- if (value === undefined || value === null || value === '')
784
- return null;
785
- const label = this.selectedRecordFieldLabel(field);
786
- return {
787
- type: 'info',
788
- message: `${label} do registro selecionado: ${this.formatSelectedRecordFilterValue(value)}.`,
789
- warnings: [
790
- 'selected-record-field-question-answered-locally',
791
- 'Single selected record supplied sampleRows; local answer grounded a field-level informational question before remote authoring.',
792
- ],
793
- };
794
- }
795
- selectedRecordSingleCandidateQuestionAnswer(request, contextHints) {
796
- if (this.selectedRecordsCountForTurn !== 1)
797
- return null;
798
- const prompt = request.prompt ?? '';
799
- const normalizedPrompt = this.normalizeLabel(prompt);
800
- if (!this.selectedRecordInformationalQuestionRequested(normalizedPrompt))
801
- return null;
802
- if (this.selectedRecordFilterApplyRequested(prompt))
803
- return null;
804
- const candidates = this.selectedRecordFilterCandidates(contextHints)
805
- .map((candidate) => ({
806
- candidate,
807
- score: this.selectedRecordCandidateQuestionScore(normalizedPrompt, candidate),
808
- }))
809
- .filter((entry) => entry.score > 0)
810
- .sort((left, right) => right.score - left.score);
811
- if (!candidates.length)
812
- return null;
813
- if (candidates.length > 1 && candidates[0].score === candidates[1].score)
814
- return null;
815
- const candidate = candidates[0].candidate;
816
- const values = this.selectedRecordCandidateDisplayValues(candidate);
817
- if (values.length !== 1)
818
- return null;
819
- const label = this.selectedRecordCandidateDisplayLabel(candidate);
820
- return {
821
- type: 'info',
822
- message: `${label} do registro selecionado: ${values[0]}.`,
823
- warnings: [
824
- 'selected-record-candidate-question-answered-locally',
825
- 'Single selected record supplied canonical filterCandidates; local answer used declared filter catalog metadata instead of materializing a table operation.',
826
- ],
827
- };
828
- }
829
- selectedRecordMissingSelectionAnswer(request, contextHints) {
830
- if (this.selectedRecordsCountForTurn > 0)
831
- return null;
832
- const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
833
- if (!this.selectedRecordReferentialRecordsPrompt(normalizedPrompt)
834
- && !this.selectedRecordExplicitSelectionPrompt(normalizedPrompt)) {
835
- return null;
836
- }
837
- if (!this.selectedRecordsContextExplicitlyEmpty(contextHints)
838
- && !this.selectedRecordMissingSelectionQuestionRequested(normalizedPrompt)) {
839
- return null;
840
- }
841
- return {
842
- type: 'info',
843
- message: 'Não encontrei registros selecionados na tabela. Selecione um ou mais registros para eu responder usando esse contexto.',
844
- warnings: [
845
- 'selected-record-context-missing',
846
- 'Prompt referenced selected/current records, but runtime.selectedRecordsContext reported no selected rows.',
847
- ],
848
- };
849
- }
850
- selectedRecordReferentialRecordsPrompt(normalizedPrompt) {
851
- if (!normalizedPrompt)
852
- return false;
853
- return [
854
- 'esses registros',
855
- 'estes registros',
856
- 'essas linhas',
857
- 'estas linhas',
858
- 'esses aqui',
859
- 'essas aqui',
860
- ].some((token) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, token));
861
- }
862
- selectedRecordExplicitSelectionPrompt(normalizedPrompt) {
863
- if (!normalizedPrompt)
864
- return false;
865
- return [
866
- 'selecionado',
867
- 'selecionados',
868
- 'selecionada',
869
- 'selecionadas',
870
- ].some((token) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, token));
871
- }
872
- selectedRecordMissingSelectionQuestionRequested(normalizedPrompt) {
873
- return this.selectedRecordInformationalQuestionRequested(normalizedPrompt)
874
- && this.selectedRecordPromptGroundsKnownFilterField(normalizedPrompt);
875
- }
876
- selectedRecordPromptGroundsKnownFilterField(normalizedPrompt) {
877
- if (!normalizedPrompt)
878
- return false;
879
- return this.filterFieldCatalogEntries.some((entry) => [
880
- entry.name,
881
- entry.label,
882
- ...entry.aliases,
883
- ...entry.relatedColumnLabels,
884
- ...entry.relatedColumnFields,
885
- ].some((candidate) => this.promptContainsAnyVariant(normalizedPrompt, candidate)));
886
- }
887
- selectedRecordsContextExplicitlyEmpty(contextHints) {
888
- const authoringContract = this.toRecord(contextHints?.['authoringContract']);
889
- const consultativeContext = this.toRecord(authoringContract?.['consultativeContext']);
890
- const selectedRecordsContext = this.toRecord(consultativeContext?.['selectedRecordsContext'])
891
- ?? this.toRecord(contextHints?.['selectedRecordsContext']);
892
- return !!selectedRecordsContext && this.extractSelectedRecordsCount(contextHints) <= 0;
893
- }
894
- singleTopSelectedRecordQuestionField(candidates) {
895
- if (!candidates.length)
896
- return null;
897
- if (candidates.length > 1 && candidates[0].score === candidates[1].score)
898
- return null;
899
- return candidates[0].field;
900
- }
901
- singleTemporalSelectedRecordField(normalizedPrompt, row) {
902
- if (!/(^|\s)quando\s/u.test(normalizedPrompt))
903
- return null;
904
- const temporalFields = Object.keys(row)
905
- .filter((field) => this.isDateOnlyValue(row[field]));
906
- return temporalFields.length === 1 ? temporalFields[0] : null;
907
- }
908
- isDateOnlyValue(value) {
909
- return typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/u.test(value.trim());
910
- }
911
- selectedRecordCommonQuestionAnswer(request, contextHints) {
912
- if (this.selectedRecordsCountForTurn < 2)
913
- return null;
914
- const prompt = request.prompt ?? '';
915
- const normalizedPrompt = this.normalizeLabel(prompt);
916
- if (!this.selectedRecordCommonQuestionRequested(normalizedPrompt))
917
- return null;
918
- if (this.selectedRecordCommonQuestionAlsoRequestsFilter(normalizedPrompt))
919
- return null;
920
- const rows = this.selectedRecordSampleRows(contextHints);
921
- if (rows.length < 2)
922
- return null;
923
- const requestedField = this.selectedRecordRequestedCommonField(normalizedPrompt, rows[0]);
924
- if (requestedField) {
925
- return this.selectedRecordFieldCommonAnswer(requestedField, rows);
926
- }
927
- const commonEntries = this.selectedRecordCommonEntries(rows).slice(0, 4);
928
- if (!commonEntries.length) {
929
- return {
930
- type: 'info',
931
- message: `Não encontrei um valor comum evidente nos ${rows.length} registros selecionados.`,
932
- warnings: [
933
- 'selected-record-common-question-answered-locally',
934
- 'Selected records supplied sampleRows; local answer summarized common values without materializing table operations.',
935
- ],
936
- };
937
- }
938
- const summary = commonEntries
939
- .map((entry) => `${entry.label}: ${entry.value}`)
940
- .join('; ');
941
- return {
942
- type: 'info',
943
- message: `Em comum nos ${rows.length} registros selecionados: ${summary}.`,
944
- warnings: [
945
- 'selected-record-common-question-answered-locally',
946
- 'Selected records supplied sampleRows; local answer summarized common values without materializing table operations.',
947
- ],
948
- };
949
- }
950
- selectedRecordMultiFieldQuestionAnswer(request, contextHints) {
951
- if (this.selectedRecordsCountForTurn < 2)
952
- return null;
953
- const prompt = request.prompt ?? '';
954
- const normalizedPrompt = this.normalizeLabel(prompt);
955
- if (!this.selectedRecordMultiFieldQuestionRequested(normalizedPrompt))
956
- return null;
957
- if (this.selectedRecordCommonQuestionAlsoRequestsFilter(normalizedPrompt))
958
- return null;
959
- const rows = this.selectedRecordSampleRows(contextHints);
960
- if (rows.length < 2)
961
- return null;
962
- const requestedField = this.selectedRecordRequestedCommonField(normalizedPrompt, rows[0]);
963
- if (requestedField) {
964
- const numericRangeAnswer = this.selectedRecordNumericMinMaxAnswer(normalizedPrompt, requestedField, rows);
965
- if (numericRangeAnswer)
966
- return numericRangeAnswer;
967
- const candidateAnswer = this.selectedRecordCandidateCommonAnswer(normalizedPrompt, contextHints, rows.length);
968
- if (candidateAnswer)
969
- return candidateAnswer;
970
- return this.selectedRecordFieldCommonAnswer(requestedField, rows);
971
- }
972
- const candidateAnswer = this.selectedRecordCandidateCommonAnswer(normalizedPrompt, contextHints, rows.length);
973
- if (candidateAnswer)
974
- return candidateAnswer;
975
- return null;
976
- }
977
- selectedRecordNumericMinMaxAnswer(normalizedPrompt, field, rows) {
978
- const asksMinMax = ['menor', 'minimo', 'mínimo', 'maior', 'maximo', 'máximo'].some((token) => (this.normalizedTextContainsApproxToken(normalizedPrompt, token)));
979
- if (!asksMinMax)
980
- return null;
981
- const values = rows
982
- .map((row) => row[field])
983
- .filter((value) => typeof value === 'number' && Number.isFinite(value));
984
- if (!values.length)
985
- return null;
986
- const min = Math.min(...values);
987
- const max = Math.max(...values);
988
- const label = this.selectedRecordFieldLabel(field);
989
- return {
990
- type: 'info',
991
- message: `${label} nos ${rows.length} registros selecionados: menor ${this.formatSelectedRecordFilterValue(min)} e maior ${this.formatSelectedRecordFilterValue(max)}.`,
992
- warnings: [
993
- 'selected-record-common-question-answered-locally',
994
- 'Selected records supplied sampleRows; local answer summarized numeric min/max without materializing table operations.',
995
- ],
996
- };
997
- }
998
- selectedRecordCandidateCommonAnswer(normalizedPrompt, contextHints, rowCount) {
999
- const candidates = this.selectedRecordFilterCandidates(contextHints)
1000
- .map((candidate) => ({
1001
- candidate,
1002
- score: this.selectedRecordCandidateQuestionScore(normalizedPrompt, candidate),
1003
- }))
1004
- .filter((entry) => entry.score > 0)
1005
- .sort((left, right) => right.score - left.score);
1006
- if (!candidates.length)
1007
- return null;
1008
- if (candidates.length > 1 && candidates[0].score === candidates[1].score)
1009
- return null;
1010
- const candidate = candidates[0].candidate;
1011
- const label = this.stringValue(candidate['label'])
1012
- || this.humanizeFilterField(this.stringValue(candidate['field']))
1013
- || 'Campo';
1014
- const values = this.selectedRecordCandidateDisplayValues(candidate);
1015
- if (!values.length)
1016
- return null;
1017
- const message = values.length === 1
1018
- ? `${label} em comum nos ${rowCount} registros selecionados: ${values[0]}.`
1019
- : `${label} nos ${rowCount} registros selecionados: ${values.slice(0, 5).join(', ')}${values.length > 5 ? ', ...' : ''}.`;
1020
- return {
1021
- type: 'info',
1022
- message,
1023
- warnings: [
1024
- 'selected-record-common-question-answered-locally',
1025
- 'Selected records supplied canonical filterCandidates; local answer summarized candidate values without materializing table operations.',
1026
- ],
1027
- };
1028
- }
1029
- selectedRecordCandidateQuestionScore(normalizedPrompt, candidate) {
1030
- let score = 0;
1031
- const candidateLabel = this.stringValue(candidate['label']);
1032
- const field = this.stringValue(candidate['field']);
1033
- const entry = this.filterFieldCatalogEntries.find((candidateEntry) => candidateEntry.name === field);
1034
- score += candidateLabel && this.promptContainsAnyVariant(normalizedPrompt, candidateLabel) ? 70 : 0;
1035
- score += field && this.promptContainsAnyVariant(normalizedPrompt, this.humanizeFilterField(field)) ? 50 : 0;
1036
- for (const value of [
1037
- ...this.stringArrayValue(candidate['aliases']),
1038
- ...(entry?.aliases ?? []),
1039
- ...this.selectedRecordCandidateDisplayValues(candidate),
1040
- ...this.stringArrayValue(candidate['relatedColumnLabels']),
1041
- ...(entry?.relatedColumnLabels ?? []),
1042
- ...this.stringArrayValue(candidate['relatedColumnFields']),
1043
- ...(entry?.relatedColumnFields ?? []),
1044
- ]) {
1045
- score += this.promptContainsAnyVariant(normalizedPrompt, value) ? 80 : 0;
1046
- }
1047
- return score;
1048
- }
1049
- selectedRecordCandidateDisplayLabel(candidate) {
1050
- const field = this.stringValue(candidate['field']);
1051
- const entry = this.filterFieldCatalogEntries.find((candidateEntry) => candidateEntry.name === field);
1052
- return this.stringArrayValue(candidate['relatedColumnLabels'])[0]
1053
- || entry?.relatedColumnLabels[0]
1054
- || this.stringValue(candidate['label'])
1055
- || entry?.label
1056
- || this.humanizeFilterField(field)
1057
- || 'Campo';
1058
- }
1059
- selectedRecordCandidateDisplayValues(candidate) {
1060
- const displayValues = Array.isArray(candidate['displayValues'])
1061
- ? candidate['displayValues']
1062
- .map((entry) => this.formatSelectedRecordFilterValue(entry))
1063
- .filter((entry) => !!entry)
1064
- : [];
1065
- if (displayValues.length)
1066
- return displayValues;
1067
- const criteria = this.toRecord(candidate['criteria']);
1068
- const field = this.stringValue(candidate['field']);
1069
- const value = field ? criteria?.[field] : undefined;
1070
- if (Array.isArray(value)) {
1071
- return value
1072
- .map((entry) => this.formatSelectedRecordFilterValue(entry))
1073
- .filter((entry, index, values) => !!entry && values.indexOf(entry) === index);
1074
- }
1075
- const record = this.toRecord(value);
1076
- if (record) {
1077
- const start = record['start'] ?? record['startDate'];
1078
- const end = record['end'] ?? record['endDate'];
1079
- if (start !== undefined && end !== undefined) {
1080
- return [`${this.formatSelectedRecordFilterValue(start)} até ${this.formatSelectedRecordFilterValue(end)}`];
1081
- }
1082
- }
1083
- return value === undefined || value === null
1084
- ? []
1085
- : [this.formatSelectedRecordFilterValue(value)];
1086
- }
1087
- selectedRecordMultiFieldQuestionRequested(normalizedPrompt) {
1088
- if (this.selectedRecordInformationalQuestionRequested(normalizedPrompt))
1089
- return true;
1090
- return /(^|\s)(eles|elas|esses|essas|estes|estas)\s+(estao|sao|ficam|ficaram|tem|têm)\s/u.test(normalizedPrompt);
1091
- }
1092
- selectedRecordCommonQuestionRequested(normalizedPrompt) {
1093
- if (!normalizedPrompt)
1094
- return false;
1095
- return ['comum', 'igual', 'padrao', 'mesma', 'mesmo'].some((token) => (this.normalizedTextContainsApproxToken(normalizedPrompt, token)));
1096
- }
1097
- selectedRecordCommonQuestionAlsoRequestsFilter(normalizedPrompt) {
1098
- if (this.selectedRecordOperationalAudienceRequested(normalizedPrompt))
1099
- return true;
1100
- return ['filtro', 'filtrar', 'filtre', 'filtra', 'aplique', 'aplicar', 'buscar', 'busque', 'procure', 'exportar', 'mostra', 'mostre', 'traz', 'traga', 'trazer', 'outro', 'outros', 'pega', 'pegue', 'acha', 'ache', 'encontra', 'encontre'].some((token) => (this.normalizedTextContainsApproxToken(normalizedPrompt, token)));
1101
- }
1102
- selectedRecordOperationalAudienceRequested(normalizedPrompt) {
1103
- if (!normalizedPrompt)
1104
- return false;
1105
- // Post-grounding bridge only: this distinguishes "show people/records like these"
1106
- // from an informational field question. Canonical filterCandidates still decide
1107
- // which field can be materialized.
1108
- return /(^|\s)(gente|pessoas|pessoal)\s+com\s+(os\s+|as\s+)?(mesmos|mesmas|mesmo|mesma)\b/u.test(normalizedPrompt);
1109
- }
1110
- selectedRecordRequestedCommonField(normalizedPrompt, row) {
1111
- const candidates = Object.keys(row)
1112
- .map((field) => ({
1113
- field,
1114
- score: this.selectedRecordQuestionFieldScore(normalizedPrompt, field),
1115
- }))
1116
- .filter((entry) => entry.score > 0)
1117
- .sort((left, right) => right.score - left.score);
1118
- return this.singleTopSelectedRecordQuestionField(candidates)
1119
- ?? this.selectedRecordRequestedFieldByValue(normalizedPrompt, row);
1120
- }
1121
- selectedRecordRequestedFieldByValue(normalizedPrompt, row) {
1122
- const candidates = Object.keys(row)
1123
- .filter((field) => {
1124
- const value = row[field];
1125
- if (value === undefined || value === null || value === '')
1126
- return false;
1127
- const formatted = this.formatSelectedRecordFilterValue(value);
1128
- return this.filterFieldMentionVariants(formatted)
1129
- .some((variant) => !!variant && this.normalizedTextContainsApproxPhrase(normalizedPrompt, variant));
1130
- });
1131
- return candidates.length === 1 ? candidates[0] : null;
1132
- }
1133
- selectedRecordFieldCommonAnswer(field, rows) {
1134
- const label = this.selectedRecordFieldLabel(field);
1135
- const values = this.selectedRecordDistinctFormattedValues(rows, field);
1136
- if (!values.length)
1137
- return null;
1138
- const message = values.length === 1
1139
- ? `${label} em comum nos ${rows.length} registros selecionados: ${values[0]}.`
1140
- : `${label} não é comum nos ${rows.length} registros selecionados. Valores encontrados: ${values.slice(0, 5).join(', ')}${values.length > 5 ? ', ...' : ''}.`;
1141
- return {
1142
- type: 'info',
1143
- message,
1144
- warnings: [
1145
- 'selected-record-common-question-answered-locally',
1146
- 'Selected records supplied sampleRows; local answer summarized a requested field without materializing table operations.',
1147
- ],
1148
- };
1149
- }
1150
- selectedRecordCommonEntries(rows) {
1151
- return Object.keys(rows[0] ?? {})
1152
- .map((field) => ({
1153
- field,
1154
- values: this.selectedRecordDistinctFormattedValues(rows, field),
1155
- }))
1156
- .filter((entry) => entry.values.length === 1)
1157
- .map((entry) => ({
1158
- label: this.selectedRecordFieldLabel(entry.field),
1159
- value: entry.values[0],
1160
- }));
1161
- }
1162
- selectedRecordDistinctFormattedValues(rows, field) {
1163
- return rows
1164
- .map((row) => row[field])
1165
- .filter((value) => value !== undefined && value !== null && value !== '')
1166
- .map((value) => this.formatSelectedRecordFilterValue(value))
1167
- .filter((value, index, values) => value && values.indexOf(value) === index);
1168
- }
1169
- selectedRecordInformationalQuestionRequested(normalizedPrompt) {
1170
- if (!normalizedPrompt)
1171
- return false;
1172
- return /(^|\s)(qual|quais|quando|quanto|quantos|quantas|quem)\s/u.test(normalizedPrompt);
1173
- }
1174
- selectedRecordQuestionFieldScore(normalizedPrompt, field) {
1175
- const rawCandidates = [
1176
- field,
1177
- this.humanizeField(field),
1178
- this.selectedRecordFieldLabel(field),
1179
- ...this.filterFieldCatalogEntries
1180
- .filter((entry) => entry.name === field || entry.relatedColumnFields.includes(field))
1181
- .flatMap((entry) => [
1182
- entry.label,
1183
- ...entry.aliases,
1184
- ...entry.relatedColumnLabels,
1185
- ...entry.relatedColumnFields,
1186
- ]),
1187
- ];
1188
- return rawCandidates
1189
- .flatMap((candidate) => this.filterFieldMentionVariants(candidate ?? ''))
1190
- .reduce((score, candidate) => (candidate && this.normalizedTextContainsApproxPhrase(normalizedPrompt, candidate)
1191
- ? Math.max(score, candidate.split(/\s+/u).length > 1 ? 80 : 50)
1192
- : score), 0);
1193
- }
1194
- selectedRecordFieldLabel(field) {
1195
- const entry = this.filterFieldCatalogEntries
1196
- .find((candidate) => candidate.name === field || candidate.relatedColumnFields.includes(field));
1197
- return entry?.relatedColumnLabels[0] || entry?.label || this.humanizeField(field);
1198
- }
1199
- selectedRecordSimilarityClarification(request, contextHints) {
1200
- if (this.selectedRecordsCountForTurn <= 0)
1201
- return null;
1202
- const prompt = this.selectedRecordFilterGroundingPrompt(request, contextHints);
1203
- if (!prompt || !this.selectedRecordSimilarityRequested(prompt))
1204
- return null;
1205
- const candidateFields = this.selectedRecordFilterCandidates(contextHints)
1206
- .map((candidate) => this.stringValue(candidate['field']))
1207
- .filter((field, index, fields) => !!field && fields.indexOf(field) === index);
1208
- if (candidateFields.length < 2)
1209
- return null;
1210
- const mentionedFields = candidateFields
1211
- .filter((field) => this.promptMentionsFilterField(prompt, field));
1212
- if (mentionedFields.length === 1)
1213
- return null;
1214
- const options = this.selectedRecordFilterClarificationOptionEntries(candidateFields);
1215
- if (options.length < 2)
1216
- return null;
1217
- return {
1218
- type: 'clarification',
1219
- message: [
1220
- `Encontrei ${this.selectedRecordsCountForTurn} registro${this.selectedRecordsCountForTurn === 1 ? '' : 's'} selecionado${this.selectedRecordsCountForTurn === 1 ? '' : 's'}.`,
1221
- `Para buscar registros parecidos, escolha qual propriedade deve guiar o filtro: ${this.joinHumanList(options.map((option) => option.label))}.`,
1222
- ].join(' '),
1223
- questions: ['Como você quer definir registros parecidos?'],
1224
- optionPayloads: options.map((entry) => this.selectedRecordFilterClarificationOptionPayload(entry, contextHints)),
1225
- warnings: [
1226
- 'selected-record-similarity-clarification-materialized',
1227
- 'Selected records supplied multiple canonical filterCandidates; ambiguous similarity was converted into governed clarification without calling the remote LLM.',
1228
- ],
1229
- };
1230
- }
1231
- selectedRecordMultipleFilterFieldClarification(request, contextHints) {
1232
- if (this.selectedRecordsCountForTurn <= 0)
1233
- return null;
1234
- const prompt = this.selectedRecordFilterGroundingPrompt(request, contextHints);
1235
- if (!prompt)
1236
- return null;
1237
- const candidateFields = this.selectedRecordFilterCandidates(contextHints)
1238
- .map((candidate) => this.stringValue(candidate['field']))
1239
- .filter((field, index, fields) => !!field && fields.indexOf(field) === index);
1240
- const mentionedFields = candidateFields
1241
- .filter((field) => this.promptMentionsFilterField(prompt, field));
1242
- if (mentionedFields.length < 2)
1243
- return null;
1244
- const options = this.selectedRecordFilterClarificationOptionEntries(mentionedFields);
1245
- if (options.length < 2)
1246
- return null;
1247
- return this.selectedRecordFilterFieldClarificationResponse(options, contextHints, [
1248
- 'selected-record-multiple-filter-fields-clarification',
1249
- 'Selected-record prompt mentioned multiple canonical filter candidates; local response asked for a governed target instead of routing by prose.',
1250
- ]);
1251
- }
1252
- selectedRecordFilterFieldClarificationResponse(options, contextHints, warnings) {
1253
- return {
1254
- type: 'clarification',
1255
- message: [
1256
- `Encontrei ${this.selectedRecordsCountForTurn} registro${this.selectedRecordsCountForTurn === 1 ? '' : 's'} selecionado${this.selectedRecordsCountForTurn === 1 ? '' : 's'}.`,
1257
- `Para buscar registros parecidos, escolha qual propriedade deve guiar o filtro: ${this.joinHumanList(options.map((option) => option.label))}.`,
1258
- ].join(' '),
1259
- questions: ['Como você quer definir registros parecidos?'],
1260
- optionPayloads: options.map((entry) => this.selectedRecordFilterClarificationOptionPayload(entry, contextHints)),
1261
- warnings,
1262
- };
1263
- }
1264
- joinHumanList(values) {
1265
- const filtered = values
1266
- .map((value) => value.trim())
1267
- .filter((value, index, items) => !!value && items.indexOf(value) === index);
1268
- if (filtered.length <= 1)
1269
- return filtered[0] ?? '';
1270
- if (filtered.length === 2)
1271
- return `${filtered[0]} ou ${filtered[1]}`;
1272
- return `${filtered.slice(0, -1).join(', ')} ou ${filtered[filtered.length - 1]}`;
1273
- }
1274
- selectedRecordFilterApplyResponse(label, criteria, warnings, request, contextHints) {
1275
- const operations = [
1276
- {
1277
- operationId: 'table.filter.apply',
1278
- input: {
1279
- criteria,
1280
- source: 'selected-records',
1281
- },
1282
- },
1283
- ];
1284
- const exportOperation = this.selectedRecordRequestedExportOperation(request, contextHints, 'filtered');
1285
- if (exportOperation) {
1286
- operations.push(exportOperation);
1287
- }
1288
- return {
1289
- type: 'patch',
1290
- tableRuntimeOperations: {
1291
- kind: 'praxis.table.runtime-operation.batch',
1292
- operations,
1293
- },
1294
- explanation: `Vou aplicar filtros por ${label}.`,
1295
- warnings,
1296
- };
1297
- }
1298
- selectedRecordExportOnlyRequest(request, contextHints) {
1299
- if (this.selectedRecordsCountForTurn <= 0)
1300
- return null;
1301
- const prompt = this.selectedRecordFilterGroundingPrompt(request, contextHints);
1302
- if (!this.selectedRecordPureExportRequested(prompt, contextHints))
1303
- return null;
1304
- const exportOperation = this.selectedRecordRequestedExportOperation(request, contextHints, 'selected');
1305
- if (!exportOperation)
1306
- return null;
1307
- const format = this.stringValue(this.toRecord(exportOperation['input'])?.['format']).toUpperCase();
1308
- return {
1309
- type: 'patch',
1310
- tableRuntimeOperations: {
1311
- kind: 'praxis.table.runtime-operation.batch',
1312
- operations: [exportOperation],
1313
- },
1314
- explanation: `Vou exportar as linhas selecionadas${format ? ` em ${format}` : ''}.`,
1315
- warnings: [
1316
- 'selected-record-export-request-materialized',
1317
- 'Selected records supplied runtime context; prompt requested export with an explicit format and no selected-record filter target.',
1318
- ],
1319
- };
1320
- }
1321
- selectedRecordRequestedExportOperation(request, contextHints, scope) {
1322
- if (!request || !this.tableRuntimeOperationAllowed(contextHints, 'table.export.run'))
1323
- return null;
1324
- const prompt = this.selectedRecordFilterGroundingPrompt(request, contextHints ?? null);
1325
- const format = this.resolveRuntimeExportFormat(prompt);
1326
- if (!format)
1327
- return null;
1328
- if (!this.tableRuntimeExportFormatAllowed(contextHints, format))
1329
- return null;
1330
- return {
1331
- operationId: 'table.export.run',
1332
- input: {
1333
- format,
1334
- scope,
1335
- },
1336
- };
1337
- }
1338
- tableRuntimeOperationAllowed(contextHints, operationId) {
1339
- const authoringContract = this.toRecord(contextHints?.['authoringContract']);
1340
- const runtimeOperations = this.toRecord(authoringContract?.['runtimeOperations']);
1341
- const allowedOperationIds = Array.isArray(runtimeOperations?.['allowedOperationIds'])
1342
- ? runtimeOperations['allowedOperationIds'].map((value) => this.stringValue(value))
1343
- : [];
1344
- if (allowedOperationIds.includes(operationId))
1345
- return true;
1346
- const operations = Array.isArray(runtimeOperations?.['operations']) ? runtimeOperations['operations'] : [];
1347
- return operations
1348
- .map((operation) => this.toRecord(operation))
1349
- .some((operation) => this.stringValue(operation?.['operationId']) === operationId);
1350
- }
1351
- tableRuntimeExportFormatAllowed(contextHints, format) {
1352
- const authoringContract = this.toRecord(contextHints?.['authoringContract']);
1353
- const runtimeOperations = this.toRecord(authoringContract?.['runtimeOperations']);
1354
- const operations = Array.isArray(runtimeOperations?.['operations']) ? runtimeOperations['operations'] : [];
1355
- const exportOperation = operations
1356
- .map((operation) => this.toRecord(operation))
1357
- .find((operation) => this.stringValue(operation?.['operationId']) === 'table.export.run');
1358
- const inputSchema = this.toRecord(exportOperation?.['inputSchema']);
1359
- const formats = Array.isArray(inputSchema?.['format'])
1360
- ? inputSchema['format'].map((value) => this.stringValue(value).toLowerCase()).filter(Boolean)
1361
- : [];
1362
- return !formats.length || formats.includes(format.toLowerCase());
1363
- }
1364
- resolveRuntimeExportFormat(prompt) {
1365
- const normalized = this.normalizeLabel(prompt);
1366
- if (!normalized)
1367
- return null;
1368
- if (normalized.includes('excel') || normalized.includes('xlsx'))
1369
- return 'excel';
1370
- if (normalized.includes('csv'))
1371
- return 'csv';
1372
- if (normalized.includes('json'))
1373
- return 'json';
1374
- if (normalized.includes('pdf'))
1375
- return 'pdf';
1376
- if (normalized.includes('print') || normalized.includes('impress') || normalized.includes('imprimir'))
1377
- return 'print';
1378
- return null;
1379
- }
1380
- selectedRecordPureExportRequested(prompt, contextHints) {
1381
- const normalized = this.normalizeLabel(prompt);
1382
- if (!normalized || !this.resolveRuntimeExportFormat(prompt))
1383
- return false;
1384
- if (!this.tableRuntimeOperationAllowed(contextHints, 'table.export.run'))
1385
- return false;
1386
- const hasExportIntent = [
1387
- 'exportar',
1388
- 'exporte',
1389
- 'exporta',
1390
- 'baixar',
1391
- 'baixe',
1392
- 'baixa',
1393
- 'download',
1394
- 'gerar',
1395
- 'gere',
1396
- 'gera',
1397
- 'mandar',
1398
- 'mande',
1399
- 'manda',
1400
- 'enviar',
1401
- 'envie',
1402
- 'envia',
1403
- ].some((token) => this.normalizedTextContainsApproxToken(normalized, token));
1404
- if (!hasExportIntent)
1405
- return false;
1406
- const hasFilterIntent = [
1407
- 'filtrar',
1408
- 'filtre',
1409
- 'filtra',
1410
- 'buscar',
1411
- 'busque',
1412
- 'busca',
1413
- 'procurar',
1414
- 'procure',
1415
- 'procura',
1416
- 'mostrar',
1417
- 'mostre',
1418
- 'mostra',
1419
- 'parecido',
1420
- 'parecidos',
1421
- 'semelhante',
1422
- 'semelhantes',
1423
- ].some((token) => this.normalizedTextContainsApproxToken(normalized, token));
1424
- if (hasFilterIntent)
1425
- return false;
1426
- return !this.selectedRecordFilterCandidates(contextHints)
1427
- .some((candidate) => this.promptMentionsFilterField(prompt, this.stringValue(candidate['field'])));
1428
- }
1429
- selectedRecordFilterGroundingPrompt(request, contextHints) {
1430
- const pendingClarification = this.toRecord(contextHints?.['pendingClarification'])
1431
- ?? this.toRecord(request.pendingClarification);
1432
- return [
1433
- request.prompt,
1434
- this.stringValue(pendingClarification?.['sourcePrompt']),
1435
- this.stringValue(pendingClarification?.['assistantMessage']),
1436
- ].filter((part) => !!part).join(' ');
1437
- }
1438
- selectedRecordFilterApplyRequested(prompt) {
1439
- const normalized = this.normalizeLabel(prompt);
1440
- if (!normalized)
1441
- return false;
1442
- // This is a post-grounding materialization guard: selectedRecordsContext.filterCandidates
1443
- // and filterFieldCatalog decide the canonical target; these verbs only distinguish
1444
- // an operational bridge request from an informational question about the field.
1445
- return [
1446
- 'aplicar',
1447
- 'aplique',
1448
- 'aplica',
1449
- 'filtrar',
1450
- 'filtre',
1451
- 'filtra',
1452
- 'buscar',
1453
- 'busque',
1454
- 'busca',
1455
- 'procurar',
1456
- 'procure',
1457
- 'procura',
1458
- 'mostrar',
1459
- 'mostre',
1460
- 'mostra',
1461
- 'trazer',
1462
- 'traga',
1463
- 'traz',
1464
- 'pegar',
1465
- 'pegue',
1466
- 'pega',
1467
- 'achar',
1468
- 'ache',
1469
- 'acha',
1470
- 'encontrar',
1471
- 'encontre',
1472
- 'encontra',
1473
- 'parecido',
1474
- 'parecidos',
1475
- 'semelhante',
1476
- 'semelhantes',
1477
- 'outro',
1478
- 'outros',
1479
- ].some((token) => this.normalizedTextContainsApproxToken(normalized, token))
1480
- || this.selectedRecordOperationalAudienceRequested(normalized)
1481
- || this.selectedRecordSameFieldFilterRequested(normalized)
1482
- || this.selectedRecordPrepositionalFilterRequested(normalized)
1483
- || this.selectedRecordProximityFilterRequested(normalized);
1484
- }
1485
- selectedRecordSameFieldFilterRequested(normalizedPrompt) {
1486
- if (!normalizedPrompt)
1487
- return false;
1488
- if (this.selectedRecordInformationalQuestionRequested(normalizedPrompt))
1489
- return false;
1490
- if (!this.selectedRecordReferentialRecordsPrompt(normalizedPrompt)
1491
- && !this.selectedRecordExplicitSelectionPrompt(normalizedPrompt)) {
1492
- return false;
1493
- }
1494
- const sameValueIntent = ['mesmo', 'mesma', 'mesmos', 'mesmas', 'igual', 'iguais'].some((token) => (this.normalizedTextContainsApproxToken(normalizedPrompt, token)));
1495
- return sameValueIntent && this.selectedRecordPromptGroundsKnownFilterField(normalizedPrompt);
1496
- }
1497
- selectedRecordPrepositionalFilterRequested(normalizedPrompt) {
1498
- if (/(^|\s)(qual|quais|oque|o que|como|quando|quanto|quantos|quantas)\s/u.test(normalizedPrompt)) {
1499
- return false;
1500
- }
1501
- const hasCriterionPreposition = /(^|\s)(por|pelo|pela|pelos|pelas)\s/u.test(normalizedPrompt);
1502
- if (!hasCriterionPreposition)
1503
- return false;
1504
- return [
1505
- 'faixa',
1506
- 'banda',
1507
- 'periodo',
1508
- 'data',
1509
- 'cargo',
1510
- 'cargos',
1511
- 'departamento',
1512
- 'departamentos',
1513
- 'status',
1514
- ].some((token) => this.normalizedTextContainsApproxToken(normalizedPrompt, token));
1515
- }
1516
- selectedRecordProximityFilterRequested(normalizedPrompt) {
1517
- if (/(^|\s)(qual|quais|oque|o que|como|quando|quanto|quantos|quantas)\s/u.test(normalizedPrompt)) {
1518
- return false;
1519
- }
1520
- const hasProximityIntent = ['perto', 'proximo', 'proximos', 'parecido', 'parecidos'].some((token) => (this.normalizedTextContainsApproxToken(normalizedPrompt, token)));
1521
- if (!hasProximityIntent)
1522
- return false;
1523
- return [
1524
- 'faixa',
1525
- 'banda',
1526
- 'periodo',
1527
- 'data',
1528
- 'cargo',
1529
- 'cargos',
1530
- 'departamento',
1531
- 'departamentos',
1532
- 'status',
1533
- ].some((token) => this.normalizedTextContainsApproxToken(normalizedPrompt, token));
1534
- }
1535
- selectedRecordSimilarityRequested(prompt) {
1536
- const normalized = this.normalizeLabel(prompt);
1537
- if (!normalized)
1538
- return false;
1539
- return [
1540
- 'parecido',
1541
- 'parecidos',
1542
- 'semelhante',
1543
- 'semelhantes',
1544
- 'comparavel',
1545
- 'comparaveis',
1546
- ].some((token) => this.normalizedTextContainsApproxToken(normalized, token));
1547
- }
1548
661
  selectedRecordFilterCandidate(contextHints, field) {
1549
662
  const normalizedField = field.trim();
1550
663
  if (!normalizedField)
@@ -1681,40 +794,6 @@ class TableAgenticAuthoringTurnFlow {
1681
794
  ],
1682
795
  };
1683
796
  }
1684
- selectedRecordBackendFailureClarification(request, contextHints, error) {
1685
- if (this.selectedRecordsCountForTurn <= 0)
1686
- return null;
1687
- const candidateFields = this.selectedRecordFilterCandidates(contextHints)
1688
- .map((candidate) => this.stringValue(candidate['field']))
1689
- .filter((field, index, fields) => !!field && fields.indexOf(field) === index);
1690
- if (!candidateFields.length)
1691
- return null;
1692
- const options = this.selectedRecordFilterClarificationOptionEntries(candidateFields);
1693
- if (!options.length)
1694
- return null;
1695
- return {
1696
- type: 'clarification',
1697
- message: [
1698
- 'Não consegui confirmar automaticamente esse pedido.',
1699
- `Encontrei ${this.selectedRecordsCountForTurn} registro${this.selectedRecordsCountForTurn === 1 ? '' : 's'} selecionado${this.selectedRecordsCountForTurn === 1 ? '' : 's'} e posso continuar usando uma propriedade derivada dessa seleção.`,
1700
- ].join(' '),
1701
- questions: ['Qual propriedade dos registros selecionados deve guiar o filtro?'],
1702
- optionPayloads: options.map((entry) => this.selectedRecordFilterClarificationOptionPayload(entry, contextHints)),
1703
- warnings: [
1704
- 'selected-record-backend-failure-clarification-materialized',
1705
- `Backend failed after selected-record context was prepared; recovered with canonical filterCandidates instead of exposing transport error. ${this.backendErrorSummary(error)}`,
1706
- ],
1707
- };
1708
- }
1709
- backendErrorSummary(error) {
1710
- const record = this.toRecord(error);
1711
- const status = this.stringValue(record?.['status']);
1712
- const statusText = this.stringValue(record?.['statusText']);
1713
- if (status || statusText) {
1714
- return `status=${status || 'unknown'} ${statusText || ''}`.trim();
1715
- }
1716
- return this.stringValue(record?.['message']) || 'status=unknown';
1717
- }
1718
797
  tableFilterApplyOperations(patch) {
1719
798
  const envelope = this.toRecord(this.toRecord(patch)?.['tableRuntimeOperations']);
1720
799
  const operations = Array.isArray(envelope?.['operations']) ? envelope['operations'] : [];
@@ -1,7 +1,7 @@
1
1
  import { firstValueFrom } from 'rxjs';
2
2
  import { BaseAiAdapter, createComponentAuthoringContext } from '@praxisui/ai';
3
3
  import { deepMerge } from '@praxisui/core';
4
- import { z as TABLE_COMPONENT_EDIT_PLAN_OPERATION_IDS, k as PRAXIS_TABLE_AUTHORING_MANIFEST, T as TABLE_AI_CAPABILITIES, R as coerceTableComponentEditPlans, W as compileTableComponentEditPlans, G as TASK_PRESETS, $ as getTableComponentEditPlanCapabilities, w as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, u as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, x as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, E as TABLE_COMPONENT_EDIT_PLAN_VERSION, v as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, y as TABLE_COMPONENT_EDIT_PLAN_KIND } from './praxisui-table-praxisui-table-GekTiEGg.mjs';
4
+ import { z as TABLE_COMPONENT_EDIT_PLAN_OPERATION_IDS, k as PRAXIS_TABLE_AUTHORING_MANIFEST, T as TABLE_AI_CAPABILITIES, R as coerceTableComponentEditPlans, W as compileTableComponentEditPlans, G as TASK_PRESETS, $ as getTableComponentEditPlanCapabilities, w as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, u as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, x as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, E as TABLE_COMPONENT_EDIT_PLAN_VERSION, v as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, y as TABLE_COMPONENT_EDIT_PLAN_KIND } from './praxisui-table-praxisui-table-KAnwisrq.mjs';
5
5
 
6
6
  const TABLE_COMPONENT_CONTEXT_PACK = {
7
7
  version: 'v1',
@@ -2023,6 +2023,7 @@ class TableAiAdapter extends BaseAiAdapter {
2023
2023
  'For filterFieldCatalog fields with criterionKind "range" and selectedRecordsContext as the source, derive criteria from the minimum and maximum selected values in relatedColumnFields and emit { start, end }; for criterionKind "date-range", emit { startDate, endDate } from the earliest and latest selected dates.',
2024
2024
  'When the user asks for similar, matching, or comparable records from selected records without naming which property should define similarity, return clarification with options derived from filterFieldCatalog labels instead of info text or a patch.',
2025
2025
  'When recordSurfaces exists and the user asks to open, consult, view, navigate, or inspect information related to the selected record, answer from recordSurfaces before considering selection-derived filters.',
2026
+ 'Resolve related-surface requests by the declared recordSurfaces semantic catalog, including label, description, semanticIntent, kind, scope, and tags; do not fall back to selected-record filters when a declared surface semantically satisfies the request.',
2026
2027
  'Use dynamicPage.surface.open only when recordSurfaces declares the requested surface and the user asks to open, show, navigate to, or display that related surface now.',
2027
2028
  'Do not use table.filter.apply for related-surface requests unless the user explicitly asks to find other records.',
2028
2029
  'When runtimeOperations.filterExpression.supported is not true, do not materialize OR, alternative groups, or nested boolean filters; ask which single filter field/value should be applied or explain the limitation.',
@@ -2054,6 +2055,7 @@ class TableAiAdapter extends BaseAiAdapter {
2054
2055
  'When selectedRecordsContext.selectedCount is greater than zero, do not claim that no record is selected; answer from selectedRecordsContext.sampleRows even when there is only one selected row.',
2055
2056
  'When selectedRecordsContext.filterCandidates exists, treat those candidates as the canonical selection-derived bridge to advanced filters; prefer their labels and criteria over re-inferring values from prose.',
2056
2057
  'When recordSurfaces exists and the user asks what can be opened, consulted, navigated, or viewed for selected records, answer or clarify using recordSurfaces before considering selection-derived filters.',
2058
+ 'For record-related surface requests, ground the target in recordSurfaces semantic fields such as label, description, semanticIntent, kind, scope, and tags before considering filters.',
2057
2059
  'Do not use selection-derived filters for requests about related surfaces, detail panels, timelines, teams, profiles, histories, payroll, or other record-adjacent information unless the user explicitly asks to find other records.',
2058
2060
  'If the user asks for a related surface that is not declared in recordSurfaces, say that it is not available in the current context and list the declared surfaces in human-facing labels.',
2059
2061
  'When the user asks how selected records can drive advanced filters or export, bridge the selected records to declared filterFieldCatalog fields and export scopes conceptually; ask for clarification when the shared property or export scope is ambiguous.',
@@ -2201,11 +2203,39 @@ class TableAiAdapter extends BaseAiAdapter {
2201
2203
  operationId: surface.operationId,
2202
2204
  ...(surface.description ? { description: surface.description } : {}),
2203
2205
  ...(surface.statePath ? { statePath: surface.statePath } : {}),
2206
+ ...this.buildRecordSurfaceSemanticDigest(surface),
2204
2207
  source: surface.source,
2205
2208
  target: surface.target,
2206
2209
  })),
2207
2210
  };
2208
2211
  }
2212
+ buildRecordSurfaceSemanticDigest(surface) {
2213
+ const resourceSurface = this.toRecord(surface['resourceSurface']);
2214
+ if (!resourceSurface) {
2215
+ return {};
2216
+ }
2217
+ const semanticIntent = this.stringValue(resourceSurface['intent']);
2218
+ const kind = this.stringValue(resourceSurface['kind']);
2219
+ const scope = this.stringValue(resourceSurface['scope']);
2220
+ const title = this.stringValue(resourceSurface['title']);
2221
+ const description = this.stringValue(resourceSurface['description']);
2222
+ const tags = this.compactStringArray(resourceSurface['tags']);
2223
+ const digest = {};
2224
+ if (kind)
2225
+ digest['kind'] = kind;
2226
+ if (scope)
2227
+ digest['scope'] = scope;
2228
+ if (semanticIntent)
2229
+ digest['semanticIntent'] = semanticIntent;
2230
+ if (title && title !== this.stringValue(surface['label']))
2231
+ digest['title'] = title;
2232
+ if (description && description !== this.stringValue(surface['description'])) {
2233
+ digest['semanticDescription'] = description;
2234
+ }
2235
+ if (tags.length)
2236
+ digest['tags'] = tags;
2237
+ return digest;
2238
+ }
2209
2239
  getResourceCapabilityAuthoringContext() {
2210
2240
  const tableWithCapabilities = this.table;
2211
2241
  try {
@@ -2709,6 +2739,14 @@ class TableAiAdapter extends BaseAiAdapter {
2709
2739
  stringValue(value) {
2710
2740
  return typeof value === 'string' ? value.trim() : '';
2711
2741
  }
2742
+ compactStringArray(value) {
2743
+ if (!Array.isArray(value)) {
2744
+ return [];
2745
+ }
2746
+ return value
2747
+ .map((item) => this.stringValue(item))
2748
+ .filter((item, index, items) => !!item && items.indexOf(item) === index);
2749
+ }
2712
2750
  normalizeString(value) {
2713
2751
  if (typeof value !== 'string') {
2714
2752
  return null;
@@ -1 +1 @@
1
- export { A as AnalyticsTableConfigAdapterService, a as AnalyticsTableContractService, b as AnalyticsTableStatsApiService, B as BOOLEAN_PRESETS, c as BehaviorConfigEditorComponent, C as CURRENCY_PRESETS, d as ColumnsConfigEditorComponent, D as DATE_PRESETS, e as DataFormatterComponent, f as DataFormattingService, F as FORMULA_TEMPLATES, g as FilterConfigService, h as FilterSettingsComponent, i as FormulaGeneratorService, J as JsonConfigEditorComponent, M as MessagesLocalizationEditorComponent, N as NUMBER_PRESETS, P as PERCENTAGE_PRESETS, j as PRAXIS_FILTER_COMPONENT_METADATA, k as PRAXIS_TABLE_AUTHORING_MANIFEST, l as PRAXIS_TABLE_COMPONENT_METADATA, m as PraxisFilter, n as PraxisFilterWidgetConfigEditor, o as PraxisTable, p as PraxisTableConfigEditor, q as PraxisTableInlineAuthoringEditorComponent, r as PraxisTableToolbar, s as PraxisTableWidgetConfigEditor, S as STRING_PRESETS, T as TABLE_AI_CAPABILITIES, t as TABLE_COMPONENT_AI_CAPABILITIES, u as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, v as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, w as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, x as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, y as TABLE_COMPONENT_EDIT_PLAN_KIND, E as TABLE_COMPONENT_EDIT_PLAN_VERSION, G as TASK_PRESETS, H as TableDefaultsProvider, I as TableRulesEditorComponent, K as ToolbarActionsEditorComponent, V as ValueMappingEditorComponent, L as VisualFormulaBuilderComponent, O as buildTableApplyPlan, Q as coerceTableComponentEditPlan, R as coerceTableComponentEditPlans, U as compileTableComponentEditPlan, W as compileTableComponentEditPlans, X as createTableAuthoringDocument, Y as getActionId, Z as getEnum, _ as getTableCapabilities, $ as getTableComponentEditPlanCapabilities, a0 as isTableRendererSupportedByRichContentP0, a1 as mapTableRendererToRichContentP0, a2 as normalizeTableAuthoringDocument, a3 as parseLegacyOrTableDocument, a4 as providePraxisFilterMetadata, a5 as providePraxisTableMetadata, a6 as serializeTableAuthoringDocument, a7 as toCanonicalTableConfig, a8 as validateTableAuthoringDocument } from './praxisui-table-praxisui-table-GekTiEGg.mjs';
1
+ export { A as AnalyticsTableConfigAdapterService, a as AnalyticsTableContractService, b as AnalyticsTableStatsApiService, B as BOOLEAN_PRESETS, c as BehaviorConfigEditorComponent, C as CURRENCY_PRESETS, d as ColumnsConfigEditorComponent, D as DATE_PRESETS, e as DataFormatterComponent, f as DataFormattingService, F as FORMULA_TEMPLATES, g as FilterConfigService, h as FilterSettingsComponent, i as FormulaGeneratorService, J as JsonConfigEditorComponent, M as MessagesLocalizationEditorComponent, N as NUMBER_PRESETS, P as PERCENTAGE_PRESETS, j as PRAXIS_FILTER_COMPONENT_METADATA, k as PRAXIS_TABLE_AUTHORING_MANIFEST, l as PRAXIS_TABLE_COMPONENT_METADATA, m as PraxisFilter, n as PraxisFilterWidgetConfigEditor, o as PraxisTable, p as PraxisTableConfigEditor, q as PraxisTableInlineAuthoringEditorComponent, r as PraxisTableToolbar, s as PraxisTableWidgetConfigEditor, S as STRING_PRESETS, T as TABLE_AI_CAPABILITIES, t as TABLE_COMPONENT_AI_CAPABILITIES, u as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, v as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, w as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, x as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, y as TABLE_COMPONENT_EDIT_PLAN_KIND, E as TABLE_COMPONENT_EDIT_PLAN_VERSION, G as TASK_PRESETS, H as TableDefaultsProvider, I as TableRulesEditorComponent, K as ToolbarActionsEditorComponent, V as ValueMappingEditorComponent, L as VisualFormulaBuilderComponent, O as buildTableApplyPlan, Q as coerceTableComponentEditPlan, R as coerceTableComponentEditPlans, U as compileTableComponentEditPlan, W as compileTableComponentEditPlans, X as createTableAuthoringDocument, Y as getActionId, Z as getEnum, _ as getTableCapabilities, $ as getTableComponentEditPlanCapabilities, a0 as isTableRendererSupportedByRichContentP0, a1 as mapTableRendererToRichContentP0, a2 as normalizeTableAuthoringDocument, a3 as parseLegacyOrTableDocument, a4 as providePraxisFilterMetadata, a5 as providePraxisTableMetadata, a6 as serializeTableAuthoringDocument, a7 as toCanonicalTableConfig, a8 as validateTableAuthoringDocument } from './praxisui-table-praxisui-table-KAnwisrq.mjs';
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@praxisui/table",
3
- "version": "8.0.0-beta.52",
3
+ "version": "8.0.0-beta.54",
4
4
  "description": "Advanced data table for Angular (Praxis UI) with editing, filtering, sorting, virtualization, and settings panel integration.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
- "@praxisui/ai": "^8.0.0-beta.52",
9
- "@praxisui/core": "^8.0.0-beta.52",
10
- "@praxisui/dynamic-fields": "^8.0.0-beta.52",
11
- "@praxisui/dynamic-form": "^8.0.0-beta.52",
12
- "@praxisui/metadata-editor": "^8.0.0-beta.52",
13
- "@praxisui/rich-content": "^8.0.0-beta.52",
14
- "@praxisui/settings-panel": "^8.0.0-beta.52",
15
- "@praxisui/table-rule-builder": "^8.0.0-beta.52",
8
+ "@praxisui/ai": "^8.0.0-beta.54",
9
+ "@praxisui/core": "^8.0.0-beta.54",
10
+ "@praxisui/dynamic-fields": "^8.0.0-beta.54",
11
+ "@praxisui/dynamic-form": "^8.0.0-beta.54",
12
+ "@praxisui/metadata-editor": "^8.0.0-beta.54",
13
+ "@praxisui/rich-content": "^8.0.0-beta.54",
14
+ "@praxisui/settings-panel": "^8.0.0-beta.54",
15
+ "@praxisui/table-rule-builder": "^8.0.0-beta.54",
16
16
  "@angular/cdk": "^21.0.0",
17
17
  "@angular/forms": "^21.0.0",
18
18
  "@angular/material": "^21.0.0",
19
19
  "@angular/router": "^21.0.0",
20
- "@praxisui/dialog": "^8.0.0-beta.52",
20
+ "@praxisui/dialog": "^8.0.0-beta.54",
21
21
  "rxjs": "~7.8.0"
22
22
  },
23
23
  "dependencies": {