@praxisui/table 8.0.0-beta.26 → 8.0.0-beta.28

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.
@@ -26,9 +26,6 @@ class TableAgenticAuthoringTurnFlow {
26
26
  ?.map((field) => this.toAiJsonObject(field))
27
27
  .filter((field) => Object.keys(field).length > 0);
28
28
  const contextHints = this.optionalJsonObject(this.adapter.getAuthoringContext?.());
29
- if (this.shouldRouteToGovernedDecision(prompt, contextHints)) {
30
- return this.toGovernedDecisionHandoff(prompt, request);
31
- }
32
29
  const response = await firstValueFrom(this.aiApi.getPatch({
33
30
  componentId,
34
31
  componentType,
@@ -133,6 +130,7 @@ class TableAgenticAuthoringTurnFlow {
133
130
  sessionId: response.sessionId ?? request.sessionId,
134
131
  assistantMessage: message,
135
132
  statusText: message,
133
+ quickReplies: this.toQuickReplies(response),
136
134
  canApply: false,
137
135
  };
138
136
  }
@@ -149,12 +147,11 @@ class TableAgenticAuthoringTurnFlow {
149
147
  }
150
148
  if (response.patch && Object.keys(response.patch).length > 0) {
151
149
  const warnings = response.warnings?.filter(Boolean) ?? [];
152
- const suffix = warnings.length ? ` Avisos: ${warnings.join('; ')}` : '';
153
150
  return {
154
151
  state: 'review',
155
152
  phase: 'review',
156
153
  sessionId: response.sessionId ?? request.sessionId,
157
- assistantMessage: `${response.explanation || 'Proposta de alteracao pronta para revisar.'}${suffix}`,
154
+ assistantMessage: response.explanation || 'Proposta de alteracao pronta para revisar.',
158
155
  statusText: 'Revise a proposta antes de aplicar.',
159
156
  canApply: true,
160
157
  pendingPatch: response.patch,
@@ -242,12 +239,17 @@ class TableAgenticAuthoringTurnFlow {
242
239
  return payloads
243
240
  .map((option, index) => {
244
241
  const label = option.label?.trim() || option.value?.trim() || `Opcao ${index + 1}`;
245
- const prompt = option.example?.trim() || option.value?.trim() || label;
242
+ const prompt = option.value?.trim() || option.example?.trim() || label;
246
243
  return {
247
244
  id: `option-${index + 1}`,
248
245
  label,
249
246
  prompt,
250
247
  kind: 'clarification-option',
248
+ description: this.optionDescription(option),
249
+ icon: this.optionIcon(option),
250
+ tone: this.optionTone(option),
251
+ presentation: this.optionPresentation(option),
252
+ contextHints: this.optionContextHints(option),
251
253
  };
252
254
  });
253
255
  }
@@ -260,6 +262,28 @@ class TableAgenticAuthoringTurnFlow {
260
262
  kind: 'clarification-option',
261
263
  }));
262
264
  }
265
+ optionContextHints(option) {
266
+ return this.toRecord(option.contextHints);
267
+ }
268
+ optionPresentation(option) {
269
+ const hints = this.optionContextHints(option);
270
+ return this.toRecord(hints?.['presentation']);
271
+ }
272
+ optionDescription(option) {
273
+ const presentation = this.optionPresentation(option);
274
+ const value = presentation?.['description'];
275
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
276
+ }
277
+ optionIcon(option) {
278
+ const presentation = this.optionPresentation(option);
279
+ const value = presentation?.['icon'];
280
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
281
+ }
282
+ optionTone(option) {
283
+ const presentation = this.optionPresentation(option);
284
+ const value = presentation?.['tone'];
285
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
286
+ }
263
287
  buildCurrentStateDigest(currentState, dataProfile) {
264
288
  const columns = Array.isArray(currentState['columns'])
265
289
  ? currentState['columns']
@@ -272,85 +296,6 @@ class TableAgenticAuthoringTurnFlow {
272
296
  ...(rowCount !== undefined ? { rowCount } : {}),
273
297
  };
274
298
  }
275
- shouldRouteToGovernedDecision(prompt, contextHints) {
276
- const normalized = prompt.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
277
- const recommendedFlow = this.toRecord(contextHints?.['domainCatalog'])?.['recommendedAuthoringFlow'];
278
- if (recommendedFlow === 'shared_rule_authoring')
279
- return true;
280
- return [
281
- 'regra',
282
- 'politica',
283
- 'policy',
284
- 'compliance',
285
- 'lgpd',
286
- 'privacidade',
287
- 'aprovacao',
288
- 'aprovar',
289
- 'publicar',
290
- 'materializar',
291
- 'enforcement',
292
- 'validacao de negocio',
293
- 'validar negocio',
294
- 'elegibilidade',
295
- 'permissao',
296
- 'acesso',
297
- ].some((term) => normalized.includes(term));
298
- }
299
- toGovernedDecisionHandoff(prompt, request) {
300
- const message = 'Esse pedido parece alterar uma decisao de negocio compartilhada. A tabela pode ajudar a descrever o alvo, mas a regra deve seguir pelo fluxo governado de domain-rules antes de qualquer materializacao runtime.';
301
- return {
302
- state: 'clarification',
303
- phase: 'clarify',
304
- sessionId: request.sessionId,
305
- assistantMessage: message,
306
- statusText: 'Handoff governado necessario.',
307
- canApply: false,
308
- quickReplies: [
309
- {
310
- id: 'shared-rule-handoff',
311
- label: 'Continuar como regra governada',
312
- prompt,
313
- kind: 'shared-rule-handoff',
314
- description: 'Criar intake de domain-rules em vez de aplicar patch local na tabela.',
315
- icon: 'rule',
316
- tone: 'warning',
317
- contextHints: {
318
- flowId: 'shared_rule_authoring',
319
- source: 'praxis-table',
320
- recommendedAction: 'domain-rules/intake',
321
- },
322
- },
323
- ],
324
- clarificationQuestions: [
325
- {
326
- id: 'table-governed-rule-confirmation',
327
- type: 'confirm',
328
- label: 'Deseja continuar pelo fluxo governado de regras compartilhadas?',
329
- description: 'Esse caminho permite intake, simulacao, aprovacao/publicacao, materializacao e validacao de enforcement.',
330
- required: true,
331
- options: [
332
- {
333
- id: 'shared-rule-handoff',
334
- label: 'Sim, continuar governado',
335
- value: prompt,
336
- description: 'Nao aplicar como patch local da tabela.',
337
- contextHints: {
338
- flowId: 'shared_rule_authoring',
339
- source: 'praxis-table',
340
- },
341
- },
342
- ],
343
- },
344
- ],
345
- diagnostics: {
346
- governedDecisionHandoff: {
347
- flowId: 'shared_rule_authoring',
348
- sourcePrompt: prompt,
349
- sourceComponent: 'praxis-table',
350
- },
351
- },
352
- };
353
- }
354
299
  optionalJsonObject(value) {
355
300
  if (value === undefined || value === null) {
356
301
  return undefined;
@@ -1,7 +1,7 @@
1
1
  import { firstValueFrom } from 'rxjs';
2
2
  import { BaseAiAdapter } from '@praxisui/ai';
3
3
  import { deepMerge } from '@praxisui/core';
4
- import { coerceTableComponentEditPlans, compileTableComponentEditPlans, TABLE_AI_CAPABILITIES, TASK_PRESETS, getTableComponentEditPlanCapabilities, TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, TABLE_COMPONENT_EDIT_PLAN_VERSION, TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, TABLE_COMPONENT_EDIT_PLAN_KIND } from './praxisui-table.mjs';
4
+ import { c as coerceTableComponentEditPlans, a as compileTableComponentEditPlans, T as TABLE_AI_CAPABILITIES, b as TASK_PRESETS, g as getTableComponentEditPlanCapabilities, d as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, e as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, f as TABLE_COMPONENT_EDIT_PLAN_OPERATION_IDS, h as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, i as TABLE_COMPONENT_EDIT_PLAN_VERSION, j as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, k as TABLE_COMPONENT_EDIT_PLAN_KIND } from './praxisui-table-praxisui-table-CKnX_qQK.mjs';
5
5
 
6
6
  /**
7
7
  * Analisa uma amostra de dados da tabela para gerar estatísticas
@@ -179,14 +179,16 @@ class TableAiAdapter extends BaseAiAdapter {
179
179
  batchKind: TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND,
180
180
  version: TABLE_COMPONENT_EDIT_PLAN_VERSION,
181
181
  schemaId: TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA.$id,
182
+ allowedOperationIds: [...TABLE_COMPONENT_EDIT_PLAN_OPERATION_IDS],
182
183
  allowedChangeKinds: [...TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS],
183
184
  expectedPaths: { ...TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS },
184
185
  capabilities: getTableComponentEditPlanCapabilities(),
185
186
  rules: [
186
- 'Use componentEditPlan instead of free patch when the request fits an allowed table edit changeKind.',
187
+ 'Use componentEditPlan instead of free patch when the request fits an allowed table operationId or changeKind.',
188
+ 'Prefer manifest operationId for governed agentic authoring responses.',
187
189
  'Use batch kind with operations for multiple table edits in one request.',
188
190
  'Use Json Logic objects for computed expressions and conditional rules.',
189
- 'Do not invent fields, changeKinds, capabilityPaths, formats, badge variants, or colors outside the provided contract.',
191
+ 'Do not invent fields, operationIds, changeKinds, capabilityPaths, formats, badge variants, or colors outside the provided contract.',
190
192
  ],
191
193
  },
192
194
  },
@@ -773,8 +775,8 @@ Columns Analysis:
773
775
  });
774
776
  }
775
777
  /**
776
- * If a patch adds only a currency format, ensure the column type is set to 'currency'
777
- * so the formatter runs. Avoid overriding an explicit non-currency type.
778
+ * If a patch adds a typed format, ensure the column type is set so the
779
+ * formatter runs. Avoid overriding an explicit type.
778
780
  */
779
781
  normalizePatch(patch) {
780
782
  if (!patch.columns || !Array.isArray(patch.columns))
@@ -792,9 +794,29 @@ Columns Analysis:
792
794
  if (!format)
793
795
  return false;
794
796
  const fmt = format.trim();
797
+ if (this.looksLikeMaskFormat(fmt))
798
+ return false;
795
799
  // Heuristics: contains date tokens like d/M/y or common date separators
796
800
  return /(d|M|y){2,}/i.test(fmt) || fmt.includes('/') || fmt.includes('-');
797
801
  }
802
+ looksLikeBooleanFormat(format) {
803
+ if (!format)
804
+ return false;
805
+ const fmt = format.trim();
806
+ return [
807
+ 'true-false',
808
+ 'yes-no',
809
+ 'active-inactive',
810
+ 'on-off',
811
+ 'enabled-disabled',
812
+ ].includes(fmt) || fmt.startsWith('custom|');
813
+ }
814
+ looksLikeMaskFormat(format) {
815
+ if (!format)
816
+ return false;
817
+ const fmt = format.trim();
818
+ return /[0#9X]/i.test(fmt) && /^[0#9X.\-/()\s]+$/i.test(fmt);
819
+ }
798
820
  isIconRendererHint(renderer) {
799
821
  if (!renderer)
800
822
  return false;
@@ -812,6 +834,9 @@ Columns Analysis:
812
834
  if (this.looksLikeCurrencyFormat(next.format)) {
813
835
  next.type = 'currency';
814
836
  }
837
+ else if (this.looksLikeBooleanFormat(next.format)) {
838
+ next.type = 'boolean';
839
+ }
815
840
  else if (this.looksLikeDateFormat(next.format)) {
816
841
  next.type = 'date';
817
842
  }