@praxisui/table 8.0.0-beta.81 → 8.0.0-beta.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{praxisui-table-praxisui-table-BigLNasG.mjs → praxisui-table-praxisui-table-mlOL4l4b.mjs} +119 -19
- package/fesm2022/{praxisui-table-table-agentic-authoring-turn-flow-CyJJHpMr.mjs → praxisui-table-table-agentic-authoring-turn-flow-DMglW7f1.mjs} +574 -52
- package/fesm2022/{praxisui-table-table-ai.adapter-D8fQSvC3.mjs → praxisui-table-table-ai.adapter-BJA9_YlX.mjs} +85 -2
- package/fesm2022/praxisui-table.mjs +1 -1
- package/package.json +10 -10
- package/types/praxisui-table.d.ts +25 -9
|
@@ -266,8 +266,7 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
266
266
|
|| this.shouldUseSelectedRecordSurfaceSnapshotFallback(contextHints));
|
|
267
267
|
}
|
|
268
268
|
shouldUseSelectedRecordSurfaceSnapshotFallback(contextHints) {
|
|
269
|
-
return this.
|
|
270
|
-
&& this.selectedRecordSurfaces(contextHints).length > 0;
|
|
269
|
+
return this.selectedRecordSurfaces(contextHints).length > 0;
|
|
271
270
|
}
|
|
272
271
|
selectedRecordSurfaces(contextHints) {
|
|
273
272
|
const authoringContract = this.toRecord(contextHints?.['authoringContract']);
|
|
@@ -561,13 +560,14 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
561
560
|
? this.adapter.compileAiResponse?.(response)
|
|
562
561
|
: null;
|
|
563
562
|
if (compiledExecutable?.patch && Object.keys(compiledExecutable.patch).length > 0) {
|
|
563
|
+
const normalizedExecutable = this.normalizeBulkRouteSelectionPayload(compiledExecutable, request);
|
|
564
564
|
const warnings = [
|
|
565
565
|
...(response.warnings ?? []),
|
|
566
|
-
...(
|
|
566
|
+
...(normalizedExecutable.warnings ?? []),
|
|
567
567
|
];
|
|
568
568
|
const executableResponse = {
|
|
569
569
|
...response,
|
|
570
|
-
patch:
|
|
570
|
+
patch: normalizedExecutable.patch,
|
|
571
571
|
warnings: warnings.length ? warnings : undefined,
|
|
572
572
|
};
|
|
573
573
|
delete executableResponse.type;
|
|
@@ -578,22 +578,42 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
578
578
|
if (continuedInvalidExecutable) {
|
|
579
579
|
return continuedInvalidExecutable;
|
|
580
580
|
}
|
|
581
|
+
const continuedBulkRouteAction = this.bulkRouteActionPlanForInvalidExecutable(response, request, compiledExecutable.warnings);
|
|
582
|
+
if (continuedBulkRouteAction) {
|
|
583
|
+
return this.compileAdapterResponse(continuedBulkRouteAction, request);
|
|
584
|
+
}
|
|
581
585
|
return {
|
|
582
586
|
type: 'error',
|
|
583
587
|
message: compiledExecutable.message || 'O componentEditPlan da tabela nao passou na validacao de capacidades.',
|
|
584
588
|
warnings: compiledExecutable.warnings,
|
|
585
589
|
};
|
|
586
590
|
}
|
|
591
|
+
const continuedSurfaceRowActionFromInfo = this.selectedRecordSurfaceRowActionPlanForInfo(response, request);
|
|
592
|
+
if (continuedSurfaceRowActionFromInfo) {
|
|
593
|
+
return this.compileAdapterResponse(continuedSurfaceRowActionFromInfo, request);
|
|
594
|
+
}
|
|
587
595
|
const continuedSurfaceRuntimeOperation = this.selectedRecordSurfaceRuntimeOperationForInfo(response, request);
|
|
588
596
|
if (continuedSurfaceRuntimeOperation) {
|
|
589
597
|
return continuedSurfaceRuntimeOperation;
|
|
590
598
|
}
|
|
599
|
+
const continuedBulkRouteNarrative = this.bulkRouteActionPlanForNonExecutableNarrative(response, request);
|
|
600
|
+
if (continuedBulkRouteNarrative) {
|
|
601
|
+
return this.compileAdapterResponse(continuedBulkRouteNarrative, request);
|
|
602
|
+
}
|
|
603
|
+
const continuedBulkRouteAction = this.bulkRouteActionPlanForClarification(response, request);
|
|
604
|
+
if (continuedBulkRouteAction) {
|
|
605
|
+
return this.compileAdapterResponse(continuedBulkRouteAction, request);
|
|
606
|
+
}
|
|
591
607
|
const continuedSurfaceRowAction = this.selectedRecordSurfaceRowActionPlanForClarification(response, request);
|
|
592
608
|
if (continuedSurfaceRowAction) {
|
|
593
609
|
return this.compileAdapterResponse(continuedSurfaceRowAction, request);
|
|
594
610
|
}
|
|
595
611
|
return response;
|
|
596
612
|
}
|
|
613
|
+
const rowActionGuidance = this.recordSurfaceRowActionGuidanceForConsult(response, request);
|
|
614
|
+
if (rowActionGuidance) {
|
|
615
|
+
return rowActionGuidance;
|
|
616
|
+
}
|
|
597
617
|
const compiled = this.adapter.compileAiResponse?.(response);
|
|
598
618
|
if (!compiled && response.patch && Object.keys(response.patch).length > 0) {
|
|
599
619
|
return {
|
|
@@ -606,12 +626,22 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
606
626
|
};
|
|
607
627
|
}
|
|
608
628
|
if (!compiled) {
|
|
629
|
+
const continuedBulkRouteNarrative = this.bulkRouteActionPlanForNonExecutableNarrative(response, request);
|
|
630
|
+
if (continuedBulkRouteNarrative) {
|
|
631
|
+
return this.compileAdapterResponse(continuedBulkRouteNarrative, request);
|
|
632
|
+
}
|
|
609
633
|
return response;
|
|
610
634
|
}
|
|
611
635
|
if (compiled.type === 'error') {
|
|
636
|
+
const continuedBulkRouteAction = this.bulkRouteActionPlanForInvalidExecutable(response, request, compiled.warnings);
|
|
637
|
+
if (continuedBulkRouteAction) {
|
|
638
|
+
return this.compileAdapterResponse(continuedBulkRouteAction, request);
|
|
639
|
+
}
|
|
612
640
|
const continuedInvalidExecutable = this.selectedRecordSurfaceRuntimeOperationForInvalidExecutable(response, request, compiled.warnings);
|
|
613
641
|
if (continuedInvalidExecutable) {
|
|
614
|
-
return continuedInvalidExecutable
|
|
642
|
+
return continuedInvalidExecutable.componentEditPlan
|
|
643
|
+
? this.compileAdapterResponse(continuedInvalidExecutable, request)
|
|
644
|
+
: continuedInvalidExecutable;
|
|
615
645
|
}
|
|
616
646
|
return {
|
|
617
647
|
type: 'error',
|
|
@@ -639,17 +669,253 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
639
669
|
if (implicitSelectedFilterClarification) {
|
|
640
670
|
return implicitSelectedFilterClarification;
|
|
641
671
|
}
|
|
672
|
+
const normalizedCompiled = this.normalizeBulkRouteSelectionPayload(compiled, request);
|
|
642
673
|
const warnings = [
|
|
643
674
|
...(response.warnings ?? []),
|
|
644
|
-
...(
|
|
675
|
+
...(normalizedCompiled.warnings ?? []),
|
|
645
676
|
];
|
|
646
677
|
return {
|
|
647
678
|
...response,
|
|
648
|
-
...
|
|
649
|
-
patch:
|
|
679
|
+
...normalizedCompiled,
|
|
680
|
+
patch: normalizedCompiled.patch,
|
|
650
681
|
warnings: warnings.length ? warnings : undefined,
|
|
651
682
|
};
|
|
652
683
|
}
|
|
684
|
+
normalizeBulkRouteSelectionPayload(compiled, request) {
|
|
685
|
+
if (!compiled?.patch || !request)
|
|
686
|
+
return compiled;
|
|
687
|
+
const text = this.normalizeLabel([
|
|
688
|
+
...(request.messages ?? [])
|
|
689
|
+
.filter((message) => message?.role === 'user')
|
|
690
|
+
.slice(-2)
|
|
691
|
+
.map((message) => message?.text ?? ''),
|
|
692
|
+
request.prompt ?? '',
|
|
693
|
+
].join(' '));
|
|
694
|
+
if (!this.textMentionsBulkActionRequest(text))
|
|
695
|
+
return compiled;
|
|
696
|
+
const routePath = this.extractRoutePath(text);
|
|
697
|
+
const patch = this.cloneJson(compiled.patch);
|
|
698
|
+
const componentEditPlan = compiled.componentEditPlan
|
|
699
|
+
? this.cloneJson(compiled.componentEditPlan)
|
|
700
|
+
: undefined;
|
|
701
|
+
const bulkActions = patch?.actions?.bulk?.actions;
|
|
702
|
+
let changed = false;
|
|
703
|
+
if (Array.isArray(bulkActions)) {
|
|
704
|
+
for (const action of bulkActions) {
|
|
705
|
+
if (!action || typeof action !== 'object')
|
|
706
|
+
continue;
|
|
707
|
+
if (this.ensureBulkRouteActionSelectionQuery(action, routePath)) {
|
|
708
|
+
changed = true;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (componentEditPlan && this.ensureBulkRouteActionSelectionQueryOnPlan(componentEditPlan, routePath)) {
|
|
713
|
+
changed = true;
|
|
714
|
+
}
|
|
715
|
+
if (!Array.isArray(bulkActions) && !componentEditPlan)
|
|
716
|
+
return compiled;
|
|
717
|
+
if (!changed)
|
|
718
|
+
return compiled;
|
|
719
|
+
return {
|
|
720
|
+
...compiled,
|
|
721
|
+
...(componentEditPlan ? { componentEditPlan } : {}),
|
|
722
|
+
patch,
|
|
723
|
+
warnings: [
|
|
724
|
+
...(compiled.warnings ?? []),
|
|
725
|
+
'bulk-route-action-selection-query-normalized',
|
|
726
|
+
],
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
ensureBulkRouteActionSelectionQueryOnPlan(plan, routePath) {
|
|
730
|
+
if (!plan || typeof plan !== 'object')
|
|
731
|
+
return false;
|
|
732
|
+
let changed = false;
|
|
733
|
+
for (const key of ['input', 'value']) {
|
|
734
|
+
if (this.ensureBulkRouteActionSelectionQuery(plan[key], routePath)) {
|
|
735
|
+
changed = true;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (Array.isArray(plan.operations)) {
|
|
739
|
+
for (const operation of plan.operations) {
|
|
740
|
+
if (!operation || typeof operation !== 'object')
|
|
741
|
+
continue;
|
|
742
|
+
for (const key of ['input', 'value']) {
|
|
743
|
+
if (this.ensureBulkRouteActionSelectionQuery(operation[key], routePath)) {
|
|
744
|
+
changed = true;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return changed;
|
|
750
|
+
}
|
|
751
|
+
ensureBulkRouteActionSelectionQuery(action, routePath) {
|
|
752
|
+
if (!action || typeof action !== 'object')
|
|
753
|
+
return false;
|
|
754
|
+
let changed = false;
|
|
755
|
+
if (this.ensureBulkRouteActionGlobalRouteRef(action, routePath)) {
|
|
756
|
+
changed = true;
|
|
757
|
+
}
|
|
758
|
+
if (this.ensureGlobalRouteActionSelectionQuery(action.globalAction)) {
|
|
759
|
+
changed = true;
|
|
760
|
+
}
|
|
761
|
+
if (Array.isArray(action.effects)) {
|
|
762
|
+
for (const effect of action.effects) {
|
|
763
|
+
if (effect?.kind === 'global-action' && this.ensureGlobalRouteActionSelectionQuery(effect.globalAction)) {
|
|
764
|
+
changed = true;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return changed;
|
|
769
|
+
}
|
|
770
|
+
ensureBulkRouteActionGlobalRouteRef(action, routePath) {
|
|
771
|
+
if (!routePath)
|
|
772
|
+
return false;
|
|
773
|
+
if (this.hasGlobalRouteActionRef(action))
|
|
774
|
+
return false;
|
|
775
|
+
const configuredAction = this.stringValue(action.action);
|
|
776
|
+
const looksLikeRouteAction = configuredAction === 'navigation.openRoute'
|
|
777
|
+
|| configuredAction === 'navigation.open route'
|
|
778
|
+
|| this.stringValue(action.globalAction?.actionId) === 'navigation.openRoute';
|
|
779
|
+
const hasRoutePayload = this.stringValue(action.payload?.path)
|
|
780
|
+
|| this.stringValue(action.globalAction?.payload?.path)
|
|
781
|
+
|| routePath;
|
|
782
|
+
if (!looksLikeRouteAction && !hasRoutePayload)
|
|
783
|
+
return false;
|
|
784
|
+
const globalAction = {
|
|
785
|
+
actionId: 'navigation.openRoute',
|
|
786
|
+
payload: {
|
|
787
|
+
path: this.stringValue(action.payload?.path)
|
|
788
|
+
|| this.stringValue(action.globalAction?.payload?.path)
|
|
789
|
+
|| routePath,
|
|
790
|
+
query: { ids: '${runtime.selectedIds}' },
|
|
791
|
+
},
|
|
792
|
+
};
|
|
793
|
+
action.globalAction = globalAction;
|
|
794
|
+
action.effects = [{ kind: 'global-action', globalAction }];
|
|
795
|
+
if (!this.stringValue(action.action)) {
|
|
796
|
+
action.action = 'navigation.openRoute';
|
|
797
|
+
}
|
|
798
|
+
return true;
|
|
799
|
+
}
|
|
800
|
+
hasGlobalRouteActionRef(action) {
|
|
801
|
+
if (this.stringValue(action?.globalAction?.actionId) === 'navigation.openRoute') {
|
|
802
|
+
return true;
|
|
803
|
+
}
|
|
804
|
+
if (!Array.isArray(action?.effects))
|
|
805
|
+
return false;
|
|
806
|
+
return action.effects.some((effect) => effect?.kind === 'global-action'
|
|
807
|
+
&& this.stringValue(effect.globalAction?.actionId) === 'navigation.openRoute');
|
|
808
|
+
}
|
|
809
|
+
ensureGlobalRouteActionSelectionQuery(globalAction) {
|
|
810
|
+
if (!globalAction || typeof globalAction !== 'object')
|
|
811
|
+
return false;
|
|
812
|
+
if (globalAction.actionId !== 'navigation.openRoute')
|
|
813
|
+
return false;
|
|
814
|
+
const payload = globalAction.payload;
|
|
815
|
+
if (!payload || typeof payload !== 'object')
|
|
816
|
+
return false;
|
|
817
|
+
if (!this.stringValue(payload.path))
|
|
818
|
+
return false;
|
|
819
|
+
if (!payload.query || typeof payload.query !== 'object' || Array.isArray(payload.query)) {
|
|
820
|
+
payload.query = {};
|
|
821
|
+
}
|
|
822
|
+
if (payload.query.ids !== undefined && payload.query.ids !== null && payload.query.ids !== '') {
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
payload.query.ids = '${runtime.selectedIds}';
|
|
826
|
+
return true;
|
|
827
|
+
}
|
|
828
|
+
recordSurfaceRowActionGuidanceForConsult(response, request) {
|
|
829
|
+
if (!request || !this.promptAsksRowActionCapabilities(request))
|
|
830
|
+
return null;
|
|
831
|
+
if (!this.responseCarriesRowActionAddPlan(response))
|
|
832
|
+
return null;
|
|
833
|
+
const surfaces = this.selectedRecordSurfaces(this.contextHintsFor(request));
|
|
834
|
+
if (!surfaces.length)
|
|
835
|
+
return null;
|
|
836
|
+
const labels = [...new Set(surfaces
|
|
837
|
+
.map((surface) => {
|
|
838
|
+
const resourceSurface = this.toRecord(surface['resourceSurface']);
|
|
839
|
+
return this.stringValue(surface['label'])
|
|
840
|
+
|| this.stringValue(resourceSurface?.['title'])
|
|
841
|
+
|| this.stringValue(surface['id']);
|
|
842
|
+
})
|
|
843
|
+
.filter((label) => !!label))];
|
|
844
|
+
if (!labels.length)
|
|
845
|
+
return null;
|
|
846
|
+
return {
|
|
847
|
+
type: 'info',
|
|
848
|
+
message: [
|
|
849
|
+
'Você pode adicionar botões de linha para superfícies relacionadas declaradas para este recurso.',
|
|
850
|
+
`Opções disponíveis agora: ${labels.map((label) => `**${label}**`).join(', ')}.`,
|
|
851
|
+
'Quando quiser materializar uma delas, peça algo como "adicione um botão na linha para abrir Histórico de folha".',
|
|
852
|
+
].join(' '),
|
|
853
|
+
warnings: [
|
|
854
|
+
'row-action-capability-question-preserved-as-consult',
|
|
855
|
+
'Residual consult guard acted only after the LLM proposed rowAction.add for a row-action capability-discovery question while declared recordSurfaces were available.',
|
|
856
|
+
],
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
promptAsksRowActionCapabilities(request) {
|
|
860
|
+
const currentPrompt = this.normalizeLabel(request.prompt ?? '');
|
|
861
|
+
const recentUserContext = this.normalizeLabel((request.messages ?? [])
|
|
862
|
+
.filter((message) => message?.role === 'user')
|
|
863
|
+
.slice(-2)
|
|
864
|
+
.map((message) => message?.text ?? '')
|
|
865
|
+
.join(' '));
|
|
866
|
+
const text = [recentUserContext, currentPrompt].filter(Boolean).join(' ');
|
|
867
|
+
if (!this.textMentionsRowActionRequest(text))
|
|
868
|
+
return false;
|
|
869
|
+
const asksAvailability = [
|
|
870
|
+
'quais',
|
|
871
|
+
'qual',
|
|
872
|
+
'o que',
|
|
873
|
+
'que botoes',
|
|
874
|
+
'que botões',
|
|
875
|
+
'botoes posso',
|
|
876
|
+
'botões posso',
|
|
877
|
+
'acoes disponiveis',
|
|
878
|
+
'ações disponíveis',
|
|
879
|
+
'acoes estao disponiveis',
|
|
880
|
+
'ações estão disponíveis',
|
|
881
|
+
'posso adicionar',
|
|
882
|
+
'can i add',
|
|
883
|
+
'available',
|
|
884
|
+
'what actions',
|
|
885
|
+
'which actions',
|
|
886
|
+
'which buttons',
|
|
887
|
+
].some((needle) => text.includes(this.normalizeLabel(needle)));
|
|
888
|
+
if (!asksAvailability)
|
|
889
|
+
return false;
|
|
890
|
+
const explicitMaterialization = [
|
|
891
|
+
'adicione',
|
|
892
|
+
'adicionar agora',
|
|
893
|
+
'crie',
|
|
894
|
+
'criar agora',
|
|
895
|
+
'coloque',
|
|
896
|
+
'aplique',
|
|
897
|
+
'materialize',
|
|
898
|
+
'create a',
|
|
899
|
+
'add a',
|
|
900
|
+
].some((needle) => currentPrompt.includes(this.normalizeLabel(needle)));
|
|
901
|
+
return !explicitMaterialization;
|
|
902
|
+
}
|
|
903
|
+
responseCarriesRowActionAddPlan(response) {
|
|
904
|
+
const record = response;
|
|
905
|
+
return [
|
|
906
|
+
this.toRecord(record['componentEditPlan']),
|
|
907
|
+
this.toRecord(this.toRecord(record['patch'])?.['componentEditPlan']),
|
|
908
|
+
this.toRecord(record['tableEditPlan']),
|
|
909
|
+
this.toRecord(record['editPlan']),
|
|
910
|
+
].some((plan) => {
|
|
911
|
+
if (!plan)
|
|
912
|
+
return false;
|
|
913
|
+
if (this.stringValue(plan['operationId']) === 'rowAction.add')
|
|
914
|
+
return true;
|
|
915
|
+
const operations = Array.isArray(plan['operations']) ? plan['operations'] : [];
|
|
916
|
+
return operations.some((operation) => this.stringValue(this.toRecord(operation)?.['operationId']) === 'rowAction.add');
|
|
917
|
+
});
|
|
918
|
+
}
|
|
653
919
|
responseCarriesClarificationChoices(response) {
|
|
654
920
|
if (response.type !== 'clarification')
|
|
655
921
|
return false;
|
|
@@ -930,7 +1196,7 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
930
1196
|
...(request.messages ?? []).slice(-8).map((message) => message?.text ?? ''),
|
|
931
1197
|
request.prompt ?? '',
|
|
932
1198
|
].join(' '));
|
|
933
|
-
if (!this.textMentionsRowActionRequest(normalizedPrompt
|
|
1199
|
+
if (!this.textMentionsRowActionRequest(`${normalizedPrompt} ${normalizedConversation}`)) {
|
|
934
1200
|
return null;
|
|
935
1201
|
}
|
|
936
1202
|
const clarificationKind = this.selectedRecordSurfaceRowActionClarificationKind(response);
|
|
@@ -950,52 +1216,65 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
950
1216
|
if (!ranked.length || (ranked.length > 1 && ranked[0].score === ranked[1].score)) {
|
|
951
1217
|
return null;
|
|
952
1218
|
}
|
|
953
|
-
const
|
|
954
|
-
const
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1219
|
+
const hasSelectedRecords = this.selectedRecordsCountForTurn > 0;
|
|
1220
|
+
const source = hasSelectedRecords
|
|
1221
|
+
? 'selected-record-surface-row-action-continued-from-clarification'
|
|
1222
|
+
: 'record-surface-row-action-continued-from-clarification';
|
|
1223
|
+
const warning = hasSelectedRecords
|
|
1224
|
+
? (clarificationKind === 'technical'
|
|
1225
|
+
? 'selected-record-surface-row-action-continued-from-technical-clarification'
|
|
1226
|
+
: 'selected-record-surface-row-action-continued-from-generic-clarification')
|
|
1227
|
+
: 'record-surface-row-action-continued-from-clarification';
|
|
1228
|
+
const warningDetail = clarificationKind === 'technical'
|
|
1229
|
+
? `Residual continuity guard acted only after LLM asked for technical row-action details while declared recordSurfaces${hasSelectedRecords ? ' and conversation history' : ''} already grounded the target surface.`
|
|
1230
|
+
: 'Residual continuity guard acted only after LLM asked a generic dataset/listing clarification while declared recordSurfaces and a row-action request already grounded the target surface.';
|
|
1231
|
+
const rowActionResponse = this.buildSelectedRecordSurfaceRowActionResponse(ranked[0].surface, source, warning, warningDetail);
|
|
1232
|
+
if (!rowActionResponse)
|
|
960
1233
|
return null;
|
|
961
|
-
const surfaceLabel = this.stringValue(surface['label'])
|
|
962
|
-
|| this.stringValue(resourceSurface['title'])
|
|
963
|
-
|| this.humanizeField(surfaceId);
|
|
964
|
-
const actionId = `open-${this.slugifyActionId(surfaceId)}`;
|
|
965
|
-
const rowAction = {
|
|
966
|
-
id: actionId,
|
|
967
|
-
label: surfaceLabel,
|
|
968
|
-
action: 'surface.open',
|
|
969
|
-
icon: 'open_in_new',
|
|
970
|
-
recordSurface: this.toAiJsonObject(resourceSurface),
|
|
971
|
-
};
|
|
972
1234
|
// Residual continuity guard: this runs only after the LLM returned a
|
|
973
1235
|
// clarification/info response for a row-button request. The target surface
|
|
974
1236
|
// is ranked from declared canonical recordSurfaces and recent conversation
|
|
975
1237
|
// context, so the guard repairs materialization without becoming primary
|
|
976
1238
|
// intent routing by local command text.
|
|
977
|
-
return
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1239
|
+
return rowActionResponse;
|
|
1240
|
+
}
|
|
1241
|
+
selectedRecordSurfaceRowActionPlanForInfo(response, request) {
|
|
1242
|
+
if (response.type !== 'info' || !request)
|
|
1243
|
+
return null;
|
|
1244
|
+
const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
|
|
1245
|
+
const normalizedConversation = this.normalizeLabel([
|
|
1246
|
+
...(request.messages ?? []).slice(-8).map((message) => message?.text ?? ''),
|
|
1247
|
+
request.prompt ?? '',
|
|
1248
|
+
].join(' '));
|
|
1249
|
+
if (!this.textMentionsRowActionRequest(`${normalizedPrompt} ${normalizedConversation}`)) {
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
const responseText = this.normalizeLabel([
|
|
1253
|
+
response.message ?? '',
|
|
1254
|
+
response.explanation ?? '',
|
|
1255
|
+
].join(' '));
|
|
1256
|
+
if (!this.textMentionsSurfaceOpenCommitment(responseText) && !this.textMentionsRowActionRequest(responseText)) {
|
|
1257
|
+
return null;
|
|
1258
|
+
}
|
|
1259
|
+
const contextHints = this.contextHintsFor(request);
|
|
1260
|
+
const surfaces = this.selectedRecordSurfaces(contextHints);
|
|
1261
|
+
if (!surfaces.length)
|
|
1262
|
+
return null;
|
|
1263
|
+
const ranked = surfaces
|
|
1264
|
+
.map((surface) => ({
|
|
1265
|
+
surface,
|
|
1266
|
+
score: this.selectedRecordSurfacePromptScore(`${normalizedConversation || normalizedPrompt} ${responseText}`, surface),
|
|
1267
|
+
}))
|
|
1268
|
+
.filter((entry) => entry.score >= 2)
|
|
1269
|
+
.sort((left, right) => right.score - left.score);
|
|
1270
|
+
if (!ranked.length || (ranked.length > 1 && ranked[0].score === ranked[1].score)) {
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
// Residual continuity guard: this runs only after the LLM has already
|
|
1274
|
+
// answered an info turn for a row-button request but failed to emit the
|
|
1275
|
+
// manifest-backed rowAction.add envelope. The target is ranked from the
|
|
1276
|
+
// governed recordSurfaces catalog plus LLM-authored evidence.
|
|
1277
|
+
return this.buildSelectedRecordSurfaceRowActionResponse(ranked[0].surface, 'selected-record-surface-row-action-info-continuity-guard', 'selected-record-surface-row-action-continued-from-info', 'Residual continuity guard acted only after LLM resolved a declared recordSurface in an info answer for a row-action request without emitting rowAction.add.');
|
|
999
1278
|
}
|
|
1000
1279
|
selectedRecordSurfaceRuntimeOperationForInfo(response, request) {
|
|
1001
1280
|
if (response.type !== 'info' || !request)
|
|
@@ -1062,9 +1341,14 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
1062
1341
|
if (!request || !this.responseMayContainExecutableEnvelope(response))
|
|
1063
1342
|
return null;
|
|
1064
1343
|
const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
|
|
1065
|
-
|
|
1344
|
+
const normalizedConversation = this.normalizeLabel([
|
|
1345
|
+
...(request.messages ?? []).slice(-8).map((message) => message?.text ?? ''),
|
|
1346
|
+
request.prompt ?? '',
|
|
1347
|
+
].join(' '));
|
|
1348
|
+
const requestText = `${normalizedPrompt} ${normalizedConversation}`;
|
|
1349
|
+
if (!this.textMentionsRecordSurfaceOpenRequest(requestText))
|
|
1066
1350
|
return null;
|
|
1067
|
-
if (this.promptRequestsSimilarRecords(
|
|
1351
|
+
if (this.promptRequestsSimilarRecords(requestText))
|
|
1068
1352
|
return null;
|
|
1069
1353
|
const contextHints = this.contextHintsFor(request);
|
|
1070
1354
|
const surfaces = this.selectedRecordSurfaces(contextHints);
|
|
@@ -1080,13 +1364,18 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
1080
1364
|
const ranked = surfaces
|
|
1081
1365
|
.map((surface) => ({
|
|
1082
1366
|
surface,
|
|
1083
|
-
score: this.selectedRecordSurfacePromptScore(`${
|
|
1367
|
+
score: this.selectedRecordSurfacePromptScore(`${requestText} ${responseEvidence}`, surface),
|
|
1084
1368
|
}))
|
|
1085
1369
|
.filter((entry) => entry.score >= 2)
|
|
1086
1370
|
.sort((left, right) => right.score - left.score);
|
|
1087
1371
|
if (!ranked.length || (ranked.length > 1 && ranked[0].score === ranked[1].score)) {
|
|
1088
1372
|
return null;
|
|
1089
1373
|
}
|
|
1374
|
+
if (this.textMentionsRowActionRequest(requestText)) {
|
|
1375
|
+
const rowActionResponse = this.buildSelectedRecordSurfaceRowActionResponse(ranked[0].surface, 'selected-record-surface-row-action-invalid-executable-continuity-guard', 'selected-record-surface-row-action-continued-from-invalid-executable', 'Residual continuity guard acted only after LLM emitted an executable envelope that failed table component capability validation for a row-action request while a declared recordSurface uniquely matched the selected-record request.');
|
|
1376
|
+
if (rowActionResponse)
|
|
1377
|
+
return rowActionResponse;
|
|
1378
|
+
}
|
|
1090
1379
|
const surface = ranked[0].surface;
|
|
1091
1380
|
const surfaceId = this.stringValue(surface['id']);
|
|
1092
1381
|
if (!surfaceId)
|
|
@@ -1121,6 +1410,152 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
1121
1410
|
],
|
|
1122
1411
|
};
|
|
1123
1412
|
}
|
|
1413
|
+
buildSelectedRecordSurfaceRowActionResponse(surface, source, warning, warningDetail) {
|
|
1414
|
+
const resourceSurface = this.toRecord(surface['resourceSurface']);
|
|
1415
|
+
if (!this.isResourceSurfaceCatalogDigest(resourceSurface)) {
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
const surfaceId = this.stringValue(surface['id']) || this.stringValue(resourceSurface['id']);
|
|
1419
|
+
if (!surfaceId)
|
|
1420
|
+
return null;
|
|
1421
|
+
const surfaceLabel = this.stringValue(surface['label'])
|
|
1422
|
+
|| this.stringValue(resourceSurface['title'])
|
|
1423
|
+
|| this.humanizeField(surfaceId);
|
|
1424
|
+
const actionId = `open-${this.slugifyActionId(surfaceId)}`;
|
|
1425
|
+
const rowAction = {
|
|
1426
|
+
id: actionId,
|
|
1427
|
+
label: surfaceLabel,
|
|
1428
|
+
action: 'surface.open',
|
|
1429
|
+
icon: 'open_in_new',
|
|
1430
|
+
recordSurface: this.toAiJsonObject(resourceSurface),
|
|
1431
|
+
};
|
|
1432
|
+
return {
|
|
1433
|
+
type: 'patch',
|
|
1434
|
+
componentEditPlan: {
|
|
1435
|
+
kind: 'praxis.table.component-edit-plan',
|
|
1436
|
+
version: '1.0',
|
|
1437
|
+
componentId: 'praxis-table',
|
|
1438
|
+
operationId: 'rowAction.add',
|
|
1439
|
+
changeKind: 'add_row_action',
|
|
1440
|
+
capabilityPath: 'actions.row.actions[]',
|
|
1441
|
+
input: rowAction,
|
|
1442
|
+
value: rowAction,
|
|
1443
|
+
source,
|
|
1444
|
+
},
|
|
1445
|
+
explanation: `Vou criar um botão em cada linha para abrir ${surfaceLabel}.`,
|
|
1446
|
+
warnings: [warning, warningDetail],
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
bulkRouteActionPlanForClarification(response, request) {
|
|
1450
|
+
if (response.type !== 'clarification' || !request)
|
|
1451
|
+
return null;
|
|
1452
|
+
const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
|
|
1453
|
+
if (!this.textMentionsBulkActionRequest(normalizedPrompt))
|
|
1454
|
+
return null;
|
|
1455
|
+
if (!this.textMentionsRecordSurfaceOpenRequest(normalizedPrompt))
|
|
1456
|
+
return null;
|
|
1457
|
+
const routePath = this.extractRoutePath(request.prompt ?? '');
|
|
1458
|
+
if (!routePath)
|
|
1459
|
+
return null;
|
|
1460
|
+
const clarificationKind = this.globalActionClarificationKind(response);
|
|
1461
|
+
if (!clarificationKind)
|
|
1462
|
+
return null;
|
|
1463
|
+
return this.buildBulkRouteActionPlan(routePath, 'bulk-route-action-continued-from-clarification', [
|
|
1464
|
+
'bulk-route-action-continued-from-technical-clarification',
|
|
1465
|
+
'Residual continuity guard acted only after LLM asked for technical bulk route details while a route path and selected-record bulk action intent were already grounded.',
|
|
1466
|
+
]);
|
|
1467
|
+
}
|
|
1468
|
+
bulkRouteActionPlanForInvalidExecutable(response, request, validationWarnings = []) {
|
|
1469
|
+
if (!request)
|
|
1470
|
+
return null;
|
|
1471
|
+
if (!response.componentEditPlan && !response.patch)
|
|
1472
|
+
return null;
|
|
1473
|
+
const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
|
|
1474
|
+
if (!this.textMentionsBulkActionRequest(normalizedPrompt))
|
|
1475
|
+
return null;
|
|
1476
|
+
if (!this.textMentionsRecordSurfaceOpenRequest(normalizedPrompt))
|
|
1477
|
+
return null;
|
|
1478
|
+
const routePath = this.extractRoutePath(request.prompt ?? '');
|
|
1479
|
+
if (!routePath)
|
|
1480
|
+
return null;
|
|
1481
|
+
return this.buildBulkRouteActionPlan(routePath, 'bulk-route-action-continued-from-invalid-executable', [
|
|
1482
|
+
...validationWarnings,
|
|
1483
|
+
'bulk-route-action-continued-from-invalid-executable',
|
|
1484
|
+
'Residual continuity guard acted only after LLM proposed an invalid executable table plan while route path and selected-record bulk action intent were already grounded.',
|
|
1485
|
+
]);
|
|
1486
|
+
}
|
|
1487
|
+
bulkRouteActionPlanForNonExecutableNarrative(response, request) {
|
|
1488
|
+
if (!request)
|
|
1489
|
+
return null;
|
|
1490
|
+
const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
|
|
1491
|
+
if (!this.textMentionsBulkActionRequest(normalizedPrompt))
|
|
1492
|
+
return null;
|
|
1493
|
+
if (!this.textMentionsRecordSurfaceOpenRequest(normalizedPrompt))
|
|
1494
|
+
return null;
|
|
1495
|
+
const routePath = this.extractRoutePath(request.prompt ?? '');
|
|
1496
|
+
if (!routePath)
|
|
1497
|
+
return null;
|
|
1498
|
+
const narrative = this.normalizeLabel([
|
|
1499
|
+
response.message ?? '',
|
|
1500
|
+
response.explanation ?? '',
|
|
1501
|
+
...(response.warnings ?? []),
|
|
1502
|
+
].join(' '));
|
|
1503
|
+
const hasCanonicalDecision = this.normalizedTextIncludesAny(narrative, ['componentEditPlan', 'component edit plan'])
|
|
1504
|
+
&& this.normalizedTextIncludesAny(narrative, ['bulkAction.add', 'bulk action.add'])
|
|
1505
|
+
&& this.normalizedTextIncludesAny(narrative, ['navigation.openRoute', 'navigation.open route'])
|
|
1506
|
+
&& this.normalizedTextIncludesAny(narrative, ['selectedIds', 'selected ids', 'runtime.selectedIds']);
|
|
1507
|
+
if (!hasCanonicalDecision)
|
|
1508
|
+
return null;
|
|
1509
|
+
return this.buildBulkRouteActionPlan(routePath, 'bulk-route-action-continued-from-non-executable-narrative', [
|
|
1510
|
+
'bulk-route-action-continued-from-non-executable-narrative',
|
|
1511
|
+
'Residual continuity guard acted only after LLM described the canonical bulkAction.add/navigation.openRoute decision without returning an executable componentEditPlan envelope.',
|
|
1512
|
+
]);
|
|
1513
|
+
}
|
|
1514
|
+
buildBulkRouteActionPlan(routePath, source, warnings) {
|
|
1515
|
+
const actionId = `open-${this.slugifyActionId(routePath)}-selected`;
|
|
1516
|
+
const label = this.humanizeBulkRouteActionLabel(routePath);
|
|
1517
|
+
const globalAction = {
|
|
1518
|
+
actionId: 'navigation.openRoute',
|
|
1519
|
+
payload: {
|
|
1520
|
+
path: routePath,
|
|
1521
|
+
query: { ids: '${runtime.selectedIds}' },
|
|
1522
|
+
},
|
|
1523
|
+
};
|
|
1524
|
+
const bulkAction = {
|
|
1525
|
+
id: actionId,
|
|
1526
|
+
label,
|
|
1527
|
+
action: 'navigation.openRoute',
|
|
1528
|
+
icon: 'route',
|
|
1529
|
+
globalAction,
|
|
1530
|
+
effects: [
|
|
1531
|
+
{
|
|
1532
|
+
kind: 'global-action',
|
|
1533
|
+
globalAction,
|
|
1534
|
+
},
|
|
1535
|
+
],
|
|
1536
|
+
};
|
|
1537
|
+
// Residual continuity guard: this executes only after the LLM already
|
|
1538
|
+
// resolved the turn as a clarification, while the user-provided route path
|
|
1539
|
+
// and the declared bulkAction.add/globalAction contract are sufficient to
|
|
1540
|
+
// materialize the operation. It grounds payload details in canonical runtime
|
|
1541
|
+
// selection templates instead of using local text as primary intent routing.
|
|
1542
|
+
return {
|
|
1543
|
+
type: 'patch',
|
|
1544
|
+
componentEditPlan: {
|
|
1545
|
+
kind: 'praxis.table.component-edit-plan',
|
|
1546
|
+
version: '1.0',
|
|
1547
|
+
componentId: 'praxis-table',
|
|
1548
|
+
operationId: 'bulkAction.add',
|
|
1549
|
+
changeKind: 'add_bulk_action',
|
|
1550
|
+
capabilityPath: 'actions.bulk.actions[]',
|
|
1551
|
+
input: bulkAction,
|
|
1552
|
+
value: bulkAction,
|
|
1553
|
+
source,
|
|
1554
|
+
},
|
|
1555
|
+
explanation: `Vou adicionar a ação ${label} para os registros selecionados.`,
|
|
1556
|
+
warnings,
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1124
1559
|
selectedRecordSurfaceRowActionClarificationKind(response) {
|
|
1125
1560
|
const text = this.normalizeLabel([
|
|
1126
1561
|
response.message ?? '',
|
|
@@ -1134,6 +1569,30 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
1134
1569
|
'mais detalhes sobre surface',
|
|
1135
1570
|
'mais detalhes sobre button',
|
|
1136
1571
|
'mais detalhes sobre placement',
|
|
1572
|
+
'mais detalhes sobre openmode',
|
|
1573
|
+
'mais detalhes sobre open mode',
|
|
1574
|
+
'mais detalhes sobre iconorstyle',
|
|
1575
|
+
'mais detalhes sobre appliesto',
|
|
1576
|
+
'mais detalhes sobre actionlabel',
|
|
1577
|
+
'mais detalhes sobre openbehavior',
|
|
1578
|
+
'mais detalhes sobre actionbehavior',
|
|
1579
|
+
'mais detalhes sobre destinationsurface',
|
|
1580
|
+
'mais detalhes sobre iconlabel',
|
|
1581
|
+
'qual condicao deve ser usada',
|
|
1582
|
+
'qual condição deve ser usada',
|
|
1583
|
+
'mais detalhes sobre action behavior',
|
|
1584
|
+
'o alvo do ajuste',
|
|
1585
|
+
'nome que voce prefere',
|
|
1586
|
+
'nome que você prefere',
|
|
1587
|
+
'openmode',
|
|
1588
|
+
'open mode',
|
|
1589
|
+
'iconorstyle',
|
|
1590
|
+
'appliesto',
|
|
1591
|
+
'actionlabel',
|
|
1592
|
+
'openbehavior',
|
|
1593
|
+
'actionbehavior',
|
|
1594
|
+
'destinationsurface',
|
|
1595
|
+
'iconlabel',
|
|
1137
1596
|
].some((needle) => text.includes(needle))) {
|
|
1138
1597
|
return 'technical';
|
|
1139
1598
|
}
|
|
@@ -1150,16 +1609,68 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
1150
1609
|
}
|
|
1151
1610
|
return null;
|
|
1152
1611
|
}
|
|
1612
|
+
globalActionClarificationKind(response) {
|
|
1613
|
+
const text = this.normalizeLabel([
|
|
1614
|
+
response.message ?? '',
|
|
1615
|
+
response.explanation ?? '',
|
|
1616
|
+
...(response.questions ?? []),
|
|
1617
|
+
].join(' '));
|
|
1618
|
+
if ([
|
|
1619
|
+
'recordidparamname',
|
|
1620
|
+
'record id param name',
|
|
1621
|
+
'o alvo do ajuste',
|
|
1622
|
+
'qual formato aplicar',
|
|
1623
|
+
'mais detalhes sobre target',
|
|
1624
|
+
'mais detalhes sobre payload',
|
|
1625
|
+
'mais detalhes sobre route',
|
|
1626
|
+
'mais detalhes sobre openmode',
|
|
1627
|
+
'mais detalhes sobre open mode',
|
|
1628
|
+
].some((needle) => text.includes(this.normalizeLabel(needle)))) {
|
|
1629
|
+
return 'technical';
|
|
1630
|
+
}
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1153
1633
|
textMentionsRowActionRequest(normalizedText) {
|
|
1154
1634
|
return [
|
|
1155
1635
|
'botao',
|
|
1156
1636
|
'acao de linha',
|
|
1157
1637
|
'acoes de linha',
|
|
1638
|
+
'acao por registro',
|
|
1639
|
+
'acoes por registro',
|
|
1158
1640
|
'nas linhas',
|
|
1159
1641
|
'por linha',
|
|
1160
1642
|
'em cada linha',
|
|
1161
1643
|
].some((needle) => normalizedText.includes(needle));
|
|
1162
1644
|
}
|
|
1645
|
+
textMentionsBulkActionRequest(normalizedText) {
|
|
1646
|
+
return [
|
|
1647
|
+
'acao em lote',
|
|
1648
|
+
'acoes em lote',
|
|
1649
|
+
'registros selecionados',
|
|
1650
|
+
'linhas selecionadas',
|
|
1651
|
+
'selecionados',
|
|
1652
|
+
'bulk action',
|
|
1653
|
+
].some((needle) => normalizedText.includes(this.normalizeLabel(needle)));
|
|
1654
|
+
}
|
|
1655
|
+
normalizedTextIncludesAny(normalizedText, needles) {
|
|
1656
|
+
return needles.some((needle) => normalizedText.includes(this.normalizeLabel(needle)));
|
|
1657
|
+
}
|
|
1658
|
+
extractRoutePath(text) {
|
|
1659
|
+
const match = String(text || '').match(/(?:^|\s)(\/[A-Za-z0-9._~:/?#[\]@!$&'()*+,;=%-]+)(?=$|\s|[,.])/u);
|
|
1660
|
+
return match?.[1]?.replace(/[,.]$/u, '') ?? '';
|
|
1661
|
+
}
|
|
1662
|
+
humanizeBulkRouteActionLabel(path) {
|
|
1663
|
+
const segments = path
|
|
1664
|
+
.split(/[?#]/u)[0]
|
|
1665
|
+
.split('/')
|
|
1666
|
+
.map((segment) => segment.trim())
|
|
1667
|
+
.filter(Boolean);
|
|
1668
|
+
const lastSegments = segments.slice(-2);
|
|
1669
|
+
const label = lastSegments
|
|
1670
|
+
.map((segment) => this.humanizeField(segment))
|
|
1671
|
+
.join(' - ');
|
|
1672
|
+
return label ? `Abrir ${label}` : 'Abrir rota dos selecionados';
|
|
1673
|
+
}
|
|
1163
1674
|
textMentionsRecordSurfaceOpenRequest(normalizedText) {
|
|
1164
1675
|
return [
|
|
1165
1676
|
'abrir',
|
|
@@ -1979,6 +2490,9 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
1979
2490
|
case 'rowAction.add':
|
|
1980
2491
|
case 'add_row_action':
|
|
1981
2492
|
return `Vou criar um botao em cada linha para abrir **${this.stringValue(input['label']) || this.stringValue(input['id']) || 'a opcao solicitada'}**.`;
|
|
2493
|
+
case 'bulkAction.add':
|
|
2494
|
+
case 'add_bulk_action':
|
|
2495
|
+
return `Vou adicionar a acao **${this.stringValue(input['label']) || this.stringValue(input['id']) || 'solicitada'}** para os registros selecionados.`;
|
|
1982
2496
|
case 'export.configure':
|
|
1983
2497
|
return this.describeExportConfigure(input);
|
|
1984
2498
|
case 'appearance.density.set':
|
|
@@ -3004,6 +3518,14 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
3004
3518
|
return {};
|
|
3005
3519
|
}
|
|
3006
3520
|
}
|
|
3521
|
+
cloneJson(value) {
|
|
3522
|
+
try {
|
|
3523
|
+
return JSON.parse(JSON.stringify(value));
|
|
3524
|
+
}
|
|
3525
|
+
catch {
|
|
3526
|
+
return value;
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3007
3529
|
toRecord(value) {
|
|
3008
3530
|
return value && typeof value === 'object' && !Array.isArray(value)
|
|
3009
3531
|
? value
|