@praxisui/page-builder 8.0.0-beta.7 → 8.0.0-beta.9

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/README.md CHANGED
@@ -116,12 +116,18 @@ providers: [
116
116
 
117
117
  Fluxo canônico:
118
118
 
119
- - `resolveIntent` envia prompt, pagina atual e widget selecionado para resolver operacao, recurso, schema, surface e elegibilidade.
119
+ - `resolveIntent` envia todo prompt de authoring, pagina atual e widget selecionado para resolver operacao, recurso, schema, surface e elegibilidade no backend canonico. O Page Builder nao deve inferir intencao, recurso ou operacao por heuristica local antes dessa chamada.
120
120
  - `previewPage` envia o prompt junto de `intentResolution` elegivel e recebe `MinimalFormPlan` + `CompiledFormPatch`.
121
+ - Hosts podem habilitar `[agenticAuthoringEnableStreaming]="true"` para usar `/api/praxis/config/ai/authoring/turn/stream/**`. O Page Builder consome eventos SSE de progresso e usa o `result` terminal como a mesma fonte de preview; se o endpoint de start ainda nao estiver disponivel, o fluxo pode voltar para `resolveIntent` + `previewPage`. Depois que a conexao SSE e iniciada, erros de transporte sao tolerados por uma janela curta de reconexao e, se persistirem, sao exibidos como falha do stream em vez de disparar uma segunda execucao sincrona silenciosa.
122
+ - Em streaming, a conversa deve refletir estados intermediarios do turno, como resolucao de intencao, selecao de recurso, planejamento e geracao de preview. Nao trate o SSE apenas como transporte do resultado final: cada `PraxisAssistantTurnViewState` emitido precisa atualizar status, mensagens, anexos, respostas rapidas, diagnosticos e preview disponivel.
123
+ - Para cenarios corporativos, mantenha streaming e diagnosticos como capacidades opt-in do host. Streaming melhora feedback percebido em prompts curtos ou ambiguos; diagnosticos exibem prompt/contexto/catalogos e devem ficar restritos a auditoria, suporte ou ambientes controlados.
121
124
  - `resolveIntent` e `previewPage` tambem aceitam `sessionId`, `clientTurnId`, `conversationMessages`, `pendingClarification` e `attachmentSummaries` para que respostas curtas e contexto anexado continuem o turno sem reescrever o prompt no frontend.
122
125
  - `attachmentSummaries` deve conter apenas metadados serializaveis (`id`, `name`, `kind`, `mimeType`, `sizeBytes`, `source`, `hasPreview`). O frontend nao envia `File`, bytes, base64 nem URLs locais `blob:`. Quando uma pergunta de clarificacao nasce de um turno com anexos, o backend pode ecoar esses resumos em `pendingClarification.diagnostics.attachmentSummaries`; o Page Builder reenvia esse estado no proximo turno.
123
- - `PageBuilderAiAdapter.applyCompiledFormPatch` aplica somente `compiledFormPatch.patch.page` no runtime do builder.
124
- - `applyPage` persiste o mesmo `patch.page` em `ui_user_config`, com `componentType`, `componentId`, `scope` e `If-Match` delegados ao backend canônico.
126
+ - Quando `resolveIntent` retornar candidatos ambiguos, o Page Builder deve preservar `quickReplies` como chips clicaveis ricos. Campos como `description`, `icon`, `tone` e `contextHints` pertencem ao contrato do backend e nao devem ser recriados ou descartados no frontend.
127
+ - Hosts podem habilitar `[agenticAuthoringIncludeLlmDiagnostics]="true"` apenas em cenarios de auditoria/debug. O input adiciona `contextHints.includeLlmDiagnostics=true` nas chamadas `resolveIntent` e, quando o backend retorna `llmDiagnostics`, o Page Builder mostra esse JSON em um painel tecnico separado da conversa. O padrao permanece desligado para nao expor prompt/contexto operacional em fluxos comuns.
128
+ - Para criacao de dashboard apos escolha de recurso, `previewPage` pode retornar `uiCompositionPlan.layoutPreset=resource-dashboard`; o builder deve aplicar esse plano como preview de pagina, nao tratar o artefato como fluxo restrito a formulario.
129
+ - `PageBuilderAiAdapter.applyCompiledFormPatch` aplica somente `compiledFormPatch.patch.page` no runtime do builder, materializando uma cópia local antes de atualizar a página.
130
+ - `applyPage` persiste a página renderizável corrente após o preview local em `ui_user_config`, com `componentType`, `componentId`, `scope` e `If-Match` delegados ao backend canônico.
125
131
 
126
132
  O frontend não deve persistir o envelope completo do patch como payload de runtime. O envelope fica para preview, auditoria e diagnóstico; o documento salvo é a página renderizável.
127
133
 
@@ -25,13 +25,13 @@ import * as i6 from '@angular/material/divider';
25
25
  import { MatDividerModule } from '@angular/material/divider';
26
26
  import * as i6$1 from '@angular/material/select';
27
27
  import { MatSelectModule } from '@angular/material/select';
28
- import { BehaviorSubject, merge, firstValueFrom } from 'rxjs';
28
+ import { BehaviorSubject, merge, switchMap, Observable, firstValueFrom, from, concatMap, catchError, of, lastValueFrom, tap } from 'rxjs';
29
29
  import { SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
30
30
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
31
31
  import * as i7 from '@angular/material/tabs';
32
32
  import { MatTabsModule } from '@angular/material/tabs';
33
33
  import { HttpClient, HttpHeaders } from '@angular/common/http';
34
- import { PraxisAssistantTurnOrchestratorService, PraxisAiAssistantShellComponent } from '@praxisui/ai';
34
+ import { AI_STREAM_EVENT_TYPES, PraxisAssistantTurnOrchestratorService, PraxisAiAssistantShellComponent } from '@praxisui/ai';
35
35
 
36
36
  const PLACEHOLDER = 1;
37
37
 
@@ -2358,6 +2358,12 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
2358
2358
  'praxis.pageBuilder.agentic.exploratory.capability.table': 'tables',
2359
2359
  'praxis.pageBuilder.agentic.exploratory.capability.form': 'forms',
2360
2360
  'praxis.pageBuilder.agentic.exploratory.capability.masterDetail': 'master-detail pages',
2361
+ 'praxis.pageBuilder.agentic.resourceDiscovery.found': 'I found APIs that can feed this screen. Choose one before generating the preview.',
2362
+ 'praxis.pageBuilder.agentic.resourceDiscovery.empty': 'I did not find a matching API yet. Describe the business data this screen should use.',
2363
+ 'praxis.pageBuilder.agentic.resourceDiscovery.useResource': 'Use {resourcePath}',
2364
+ 'praxis.pageBuilder.agentic.diagnostics.title': 'LLM diagnostics',
2365
+ 'praxis.pageBuilder.agentic.diagnostics.badge': 'Debug',
2366
+ 'praxis.pageBuilder.agentic.diagnostics.description': 'Prompt, context bundle, and tool catalog returned by the backend for this turn.',
2361
2367
  'praxis.pageBuilder.agentic.dragHandleAria': 'Move AI assistant',
2362
2368
  'praxis.pageBuilder.agentic.resizeHandleAria': 'Resize AI assistant',
2363
2369
  'praxis.pageBuilder.agentic.resizeHandleTooltip': 'Resize',
@@ -2366,7 +2372,10 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
2366
2372
  'praxis.pageBuilder.agentic.status.resolvingIntent': 'Resolving intent...',
2367
2373
  'praxis.pageBuilder.agentic.status.waitingRevision': 'Refine your prompt and preview again.',
2368
2374
  'praxis.pageBuilder.agentic.status.cancelled': 'Request cancelled.',
2375
+ 'praxis.pageBuilder.agentic.status.contextBundle': 'Preparing context...',
2376
+ 'praxis.pageBuilder.agentic.status.resourceDiscovery': 'Finding API resources...',
2369
2377
  'praxis.pageBuilder.agentic.status.previewing': 'Generating preview...',
2378
+ 'praxis.pageBuilder.agentic.status.previewCompile': 'Compiling preview...',
2370
2379
  'praxis.pageBuilder.agentic.status.previewReady': 'Preview applied to the page.',
2371
2380
  'praxis.pageBuilder.agentic.status.acceptedAddLocalField': 'Local field added to the form.',
2372
2381
  'praxis.pageBuilder.agentic.status.acceptedRemoveLocalField': 'Local field removed from the form.',
@@ -2377,6 +2386,9 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
2377
2386
  'praxis.pageBuilder.agentic.errors.componentId': 'Configure a component id before saving.',
2378
2387
  'praxis.pageBuilder.agentic.errors.invalidPreview': 'Generated preview is invalid.',
2379
2388
  'praxis.pageBuilder.agentic.errors.intentResolution': 'Intent could not be resolved.',
2389
+ 'praxis.pageBuilder.agentic.errors.streamTimeout': 'The assistant took too long to finish this request. Try again with a narrower request or review the active context.',
2390
+ 'praxis.pageBuilder.agentic.errors.streamProcessing': 'The assistant could not finish this authoring request. Try again or ask support to review the diagnostics.',
2391
+ 'praxis.pageBuilder.agentic.errors.streamConnection': 'The assistant stream was interrupted before the turn finished. Try again or ask support to check the stream connection.',
2380
2392
  'praxis.pageBuilder.agentic.errors.duplicateField': 'This field already exists in the selected form.',
2381
2393
  'praxis.pageBuilder.agentic.errors.nonLocalFieldRemoval': 'Only local fields created by AI can be removed in this flow.',
2382
2394
  'praxis.pageBuilder.agentic.errors.generic': 'AI authoring failed.',
@@ -2599,6 +2611,12 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
2599
2611
  'praxis.pageBuilder.agentic.exploratory.capability.table': 'tabelas',
2600
2612
  'praxis.pageBuilder.agentic.exploratory.capability.form': 'formulários',
2601
2613
  'praxis.pageBuilder.agentic.exploratory.capability.masterDetail': 'páginas master-detail',
2614
+ 'praxis.pageBuilder.agentic.resourceDiscovery.found': 'Encontrei APIs que podem alimentar esta tela. Escolha uma antes de gerar a pré-visualização.',
2615
+ 'praxis.pageBuilder.agentic.resourceDiscovery.empty': 'Ainda não encontrei uma API correspondente. Descreva quais dados de negócio esta tela deve usar.',
2616
+ 'praxis.pageBuilder.agentic.resourceDiscovery.useResource': 'Usar {resourcePath}',
2617
+ 'praxis.pageBuilder.agentic.diagnostics.title': 'Diagnóstico do LLM',
2618
+ 'praxis.pageBuilder.agentic.diagnostics.badge': 'Debug',
2619
+ 'praxis.pageBuilder.agentic.diagnostics.description': 'Prompt, pacote de contexto e catálogo de ferramentas retornados pelo backend para este turno.',
2602
2620
  'praxis.pageBuilder.agentic.dragHandleAria': 'Mover assistente de IA',
2603
2621
  'praxis.pageBuilder.agentic.resizeHandleAria': 'Redimensionar assistente de IA',
2604
2622
  'praxis.pageBuilder.agentic.resizeHandleTooltip': 'Redimensionar',
@@ -2607,7 +2625,10 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
2607
2625
  'praxis.pageBuilder.agentic.status.resolvingIntent': 'Resolvendo intenção...',
2608
2626
  'praxis.pageBuilder.agentic.status.waitingRevision': 'Ajuste o prompt e pré-visualize novamente.',
2609
2627
  'praxis.pageBuilder.agentic.status.cancelled': 'Solicitação cancelada.',
2628
+ 'praxis.pageBuilder.agentic.status.contextBundle': 'Preparando contexto...',
2629
+ 'praxis.pageBuilder.agentic.status.resourceDiscovery': 'Buscando recursos de API...',
2610
2630
  'praxis.pageBuilder.agentic.status.previewing': 'Gerando pré-visualização...',
2631
+ 'praxis.pageBuilder.agentic.status.previewCompile': 'Compilando pré-visualização...',
2611
2632
  'praxis.pageBuilder.agentic.status.previewReady': 'Pré-visualização aplicada à página.',
2612
2633
  'praxis.pageBuilder.agentic.status.acceptedAddLocalField': 'Campo local adicionado ao formulário.',
2613
2634
  'praxis.pageBuilder.agentic.status.acceptedRemoveLocalField': 'Campo local removido do formulário.',
@@ -2618,6 +2639,9 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
2618
2639
  'praxis.pageBuilder.agentic.errors.componentId': 'Configure um id de componente antes de salvar.',
2619
2640
  'praxis.pageBuilder.agentic.errors.invalidPreview': 'A pré-visualização gerada é inválida.',
2620
2641
  'praxis.pageBuilder.agentic.errors.intentResolution': 'A intenção não pôde ser resolvida.',
2642
+ 'praxis.pageBuilder.agentic.errors.streamTimeout': 'Ainda não consegui concluir este pedido. Tente novamente com um pedido mais específico ou revise o contexto ativo.',
2643
+ 'praxis.pageBuilder.agentic.errors.streamProcessing': 'Não consegui concluir este pedido de autoria. Tente novamente ou peça ao suporte para revisar o diagnóstico.',
2644
+ 'praxis.pageBuilder.agentic.errors.streamConnection': 'O stream do assistente foi interrompido antes de concluir o turno. Tente novamente ou acione o suporte para verificar a conexão do stream.',
2621
2645
  'praxis.pageBuilder.agentic.errors.duplicateField': 'Este campo já existe no formulário selecionado.',
2622
2646
  'praxis.pageBuilder.agentic.errors.nonLocalFieldRemoval': 'Somente campos locais criados pela IA podem ser removidos neste fluxo.',
2623
2647
  'praxis.pageBuilder.agentic.errors.generic': 'Falha na autoria com IA.',
@@ -5026,6 +5050,12 @@ class PageBuilderAgenticAuthoringService {
5026
5050
  resolveIntent(request) {
5027
5051
  return this.http.post(`${this.baseUrl}/intent-resolution`, request, { headers: this.buildHeaders() });
5028
5052
  }
5053
+ searchResourceCandidates(request) {
5054
+ return this.http.post(`${this.baseUrl}/resource-candidates`, request, { headers: this.buildHeaders() });
5055
+ }
5056
+ streamTurn(request) {
5057
+ return this.http.post(`${this.baseUrl}/turn/stream/start`, request, { headers: this.buildHeaders() }).pipe(switchMap((start) => this.connectTurnStream(start)));
5058
+ }
5029
5059
  applyPage(request) {
5030
5060
  const { ifMatch, ...body } = request;
5031
5061
  return this.http.post(`${this.baseUrl}/page-apply`, {
@@ -5055,6 +5085,97 @@ class PageBuilderAgenticAuthoringService {
5055
5085
  }
5056
5086
  return new HttpHeaders(merged);
5057
5087
  }
5088
+ connectTurnStream(start) {
5089
+ return new Observable((observer) => {
5090
+ const url = this.buildStreamUrl(start);
5091
+ let source;
5092
+ let closed = false;
5093
+ let connectionErrorTimer = null;
5094
+ const connectionErrorGraceMs = Math.max(0, this.options?.streamConnectionErrorGraceMs ?? 1500);
5095
+ try {
5096
+ source = this.createEventSource(url);
5097
+ }
5098
+ catch (error) {
5099
+ observer.error(this.streamConnectionError(error));
5100
+ return undefined;
5101
+ }
5102
+ const clearConnectionErrorTimer = () => {
5103
+ if (connectionErrorTimer) {
5104
+ clearTimeout(connectionErrorTimer);
5105
+ connectionErrorTimer = null;
5106
+ }
5107
+ };
5108
+ const closeSource = () => {
5109
+ if (closed) {
5110
+ return;
5111
+ }
5112
+ closed = true;
5113
+ clearConnectionErrorTimer();
5114
+ source.close();
5115
+ };
5116
+ const failConnection = (event) => {
5117
+ if (closed) {
5118
+ return;
5119
+ }
5120
+ observer.error(this.streamConnectionError(event));
5121
+ closeSource();
5122
+ };
5123
+ const handleMessage = (event) => {
5124
+ try {
5125
+ clearConnectionErrorTimer();
5126
+ const parsed = JSON.parse(event.data);
5127
+ observer.next(parsed);
5128
+ if (parsed.type === 'result' || parsed.type === 'error' || parsed.type === 'cancelled') {
5129
+ observer.complete();
5130
+ closeSource();
5131
+ }
5132
+ }
5133
+ catch (error) {
5134
+ observer.error(this.streamConnectionError(error));
5135
+ closeSource();
5136
+ }
5137
+ };
5138
+ source.onmessage = handleMessage;
5139
+ if (source.addEventListener) {
5140
+ for (const type of AI_STREAM_EVENT_TYPES) {
5141
+ source.addEventListener(type, handleMessage);
5142
+ }
5143
+ }
5144
+ source.onerror = (event) => {
5145
+ if (closed || connectionErrorTimer) {
5146
+ return;
5147
+ }
5148
+ if (connectionErrorGraceMs === 0) {
5149
+ failConnection(event);
5150
+ return;
5151
+ }
5152
+ connectionErrorTimer = setTimeout(() => failConnection(event), connectionErrorGraceMs);
5153
+ };
5154
+ return () => closeSource();
5155
+ });
5156
+ }
5157
+ streamConnectionError(cause) {
5158
+ return {
5159
+ praxisAgenticTurnStreamConnectionError: true,
5160
+ cause,
5161
+ };
5162
+ }
5163
+ buildStreamUrl(start) {
5164
+ const url = new URL(`${this.baseUrl}/turn/stream/${start.streamId}`, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
5165
+ if (start.streamAccessToken) {
5166
+ url.searchParams.set('accessToken', start.streamAccessToken);
5167
+ }
5168
+ return /^https?:\/\//i.test(this.baseUrl)
5169
+ ? url.toString()
5170
+ : url.pathname + url.search;
5171
+ }
5172
+ createEventSource(url) {
5173
+ const factory = this.options?.eventSourceFactory;
5174
+ if (factory) {
5175
+ return factory(url);
5176
+ }
5177
+ return new EventSource(url);
5178
+ }
5058
5179
  formatEtag(etag) {
5059
5180
  const trimmed = etag.trim();
5060
5181
  return trimmed.startsWith('"') ? trimmed : `"${trimmed}"`;
@@ -5387,7 +5508,11 @@ class PageBuilderAiAdapter {
5387
5508
  error: 'CompiledFormPatch must contain patch.page.',
5388
5509
  };
5389
5510
  }
5390
- return this.applyPatch({ page }, 'agentic-authoring.compiled-form-patch');
5511
+ const materializedPage = this.clone(page);
5512
+ if (this.shouldPreserveLocalTransientFields(compiledFormPatch)) {
5513
+ this.preserveLocalTransientFields(materializedPage);
5514
+ }
5515
+ return this.applyPatch({ page: materializedPage }, 'agentic-authoring.compiled-form-patch');
5391
5516
  }
5392
5517
  async applyUiCompositionPlan(plan) {
5393
5518
  const compiled = compileUiCompositionPlan(plan);
@@ -5526,6 +5651,76 @@ class PageBuilderAiAdapter {
5526
5651
  }
5527
5652
  return merged;
5528
5653
  }
5654
+ shouldPreserveLocalTransientFields(compiledFormPatch) {
5655
+ const warnings = compiledFormPatch['warnings'];
5656
+ return Array.isArray(warnings) && warnings.includes('server-backed-field-labels-customized-locally');
5657
+ }
5658
+ preserveLocalTransientFields(patchPage) {
5659
+ const basePage = this.normalizePageForAi(this.parsePage(this.host.page));
5660
+ const baseWidgets = basePage?.widgets ?? [];
5661
+ for (const patchWidget of patchPage.widgets ?? []) {
5662
+ if (patchWidget.definition?.id !== 'praxis-dynamic-form')
5663
+ continue;
5664
+ const key = this.getWidgetKey(patchWidget);
5665
+ if (!key)
5666
+ continue;
5667
+ const baseWidget = baseWidgets.find((widget) => this.getWidgetKey(widget) === key);
5668
+ if (!baseWidget)
5669
+ continue;
5670
+ this.preserveLocalTransientFieldsForWidget(baseWidget, patchWidget);
5671
+ }
5672
+ }
5673
+ preserveLocalTransientFieldsForWidget(baseWidget, patchWidget) {
5674
+ const baseConfig = (baseWidget.definition?.inputs?.['config'] ?? {});
5675
+ const patchInputs = patchWidget.definition.inputs ??= {};
5676
+ const patchConfig = (patchInputs['config'] ??= {});
5677
+ const baseFields = Array.isArray(baseConfig['fieldMetadata']) ? baseConfig['fieldMetadata'] : [];
5678
+ const patchFields = Array.isArray(patchConfig['fieldMetadata'])
5679
+ ? patchConfig['fieldMetadata']
5680
+ : (patchConfig['fieldMetadata'] = []);
5681
+ const preservedNames = [];
5682
+ for (const field of baseFields) {
5683
+ if (!this.isLocalTransientField(field) || patchFields.some((entry) => entry?.name === field.name))
5684
+ continue;
5685
+ patchFields.push(this.clone(field));
5686
+ preservedNames.push(field.name);
5687
+ }
5688
+ if (preservedNames.length === 0)
5689
+ return;
5690
+ const baseSections = Array.isArray(baseConfig['sections']) ? baseConfig['sections'] : [];
5691
+ const patchSections = Array.isArray(patchConfig['sections'])
5692
+ ? patchConfig['sections']
5693
+ : (patchConfig['sections'] = []);
5694
+ for (const section of baseSections) {
5695
+ if (!this.sectionContainsAnyField(section, preservedNames))
5696
+ continue;
5697
+ if (this.sectionContainsAnyField({ rows: [{ columns: [{ fields: this.flattenSectionFields(patchSections) }] }] }, preservedNames)) {
5698
+ continue;
5699
+ }
5700
+ patchSections.push(this.clone(section));
5701
+ }
5702
+ }
5703
+ isLocalTransientField(field) {
5704
+ return field?.source === 'local' || field?.transient === true || field?.submitPolicy === 'omit';
5705
+ }
5706
+ sectionContainsAnyField(section, names) {
5707
+ const fields = this.flattenSectionFields([section]);
5708
+ return names.some((name) => fields.includes(name));
5709
+ }
5710
+ flattenSectionFields(sections) {
5711
+ const fields = [];
5712
+ for (const section of sections) {
5713
+ for (const row of section?.rows ?? []) {
5714
+ for (const column of row?.columns ?? []) {
5715
+ for (const field of column?.fields ?? []) {
5716
+ if (typeof field === 'string')
5717
+ fields.push(field);
5718
+ }
5719
+ }
5720
+ }
5721
+ }
5722
+ return fields;
5723
+ }
5529
5724
  mergeLinks(baseLinks, patchLinks) {
5530
5725
  const base = baseLinks || [];
5531
5726
  const patch = patchLinks || [];
@@ -5717,32 +5912,49 @@ class PageBuilderAgenticAuthoringTurnFlow {
5717
5912
  this.service = service;
5718
5913
  this.context = context;
5719
5914
  }
5720
- async submit(request) {
5915
+ submit(request) {
5721
5916
  const prompt = request.prompt?.trim();
5722
5917
  if (!prompt) {
5723
- return {
5918
+ return Promise.resolve({
5724
5919
  state: 'listening',
5725
5920
  phase: 'capture',
5726
- };
5921
+ });
5922
+ }
5923
+ if (this.isResourceDiscoveryTool(request)) {
5924
+ return this.handleResourceDiscoveryTool(request, prompt);
5727
5925
  }
5926
+ if (this.context.enableTurnStream?.() === true && this.service.streamTurn) {
5927
+ return this.submitWithTurnStream(request, prompt);
5928
+ }
5929
+ return this.submitSynchronously(request, prompt);
5930
+ }
5931
+ async submitSynchronously(request, prompt) {
5728
5932
  try {
5729
5933
  const authoringContext = this.buildAuthoringContext(request);
5730
5934
  const componentCapabilities = await this.context.loadComponentCapabilities();
5731
5935
  const selectedWidgetKey = this.context.selectedWidgetKey();
5732
- const intentResolution = await firstValueFrom(this.service.resolveIntent({
5733
- userPrompt: prompt,
5734
- targetApp: this.context.targetApp,
5735
- targetComponentId: this.context.targetComponentId,
5736
- currentPage: this.context.currentPage(),
5737
- selectedWidgetKey,
5738
- provider: this.context.provider(),
5739
- model: this.context.model(),
5740
- apiKey: this.context.apiKey(),
5741
- componentCapabilities,
5742
- ...authoringContext,
5743
- }));
5936
+ const intentResolution = await firstValueFrom(this.service.resolveIntent(this.buildIntentResolutionRequest(prompt, selectedWidgetKey, componentCapabilities, authoringContext)));
5744
5937
  const intentAssistantMessage = this.resolveIntentAssistantMessage(intentResolution);
5745
5938
  if (intentAssistantMessage) {
5939
+ const explicitPendingClarification = this.toShellPendingClarification(intentResolution.pendingClarification);
5940
+ const intentClarification = this.resolveIntentClarification(intentResolution);
5941
+ const pendingClarification = explicitPendingClarification
5942
+ ?? (intentClarification
5943
+ ? this.buildPendingClarificationForNextTurn(request, this.resolveEffectivePrompt(intentResolution, prompt), intentClarification)
5944
+ : null);
5945
+ if (!this.hasPendingClarificationQuestion(pendingClarification)) {
5946
+ return {
5947
+ state: 'success',
5948
+ phase: 'summarize',
5949
+ assistantMessage: intentAssistantMessage,
5950
+ quickReplies: this.toShellQuickReplies(intentResolution.quickReplies ?? []),
5951
+ canApply: false,
5952
+ statusText: '',
5953
+ errorText: '',
5954
+ preview: null,
5955
+ diagnostics: { intentResolution },
5956
+ };
5957
+ }
5746
5958
  return {
5747
5959
  state: 'clarification',
5748
5960
  phase: 'clarify',
@@ -5752,7 +5964,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
5752
5964
  statusText: '',
5753
5965
  errorText: '',
5754
5966
  preview: null,
5755
- pendingClarification: this.resolvePendingClarificationForNextTurn(intentResolution, request, this.resolveEffectivePrompt(intentResolution, prompt), intentAssistantMessage),
5967
+ pendingClarification,
5756
5968
  diagnostics: { intentResolution },
5757
5969
  };
5758
5970
  }
@@ -5847,6 +6059,11 @@ class PageBuilderAgenticAuthoringTurnFlow {
5847
6059
  };
5848
6060
  }
5849
6061
  }
6062
+ submitWithTurnStream(request, prompt) {
6063
+ return from(this.buildTurnStreamRequest(request, prompt)).pipe(concatMap((streamRequest) => this.service.streamTurn(streamRequest)), concatMap((event) => from(this.toTurnResultFromStreamEvent(event))), catchError((error) => this.shouldFallbackFromTurnStreamError(error)
6064
+ ? from(this.submitSynchronously(request, prompt))
6065
+ : of(this.toTurnStreamTransportErrorResult(error))));
6066
+ }
5850
6067
  async cancel() {
5851
6068
  return {
5852
6069
  state: 'listening',
@@ -5860,10 +6077,317 @@ class PageBuilderAgenticAuthoringTurnFlow {
5860
6077
  pendingPatch: null,
5861
6078
  };
5862
6079
  }
6080
+ async buildTurnStreamRequest(request, prompt) {
6081
+ const componentCapabilities = await this.context.loadComponentCapabilities();
6082
+ const selectedWidgetKey = this.context.selectedWidgetKey();
6083
+ return {
6084
+ userPrompt: prompt,
6085
+ targetApp: this.context.targetApp,
6086
+ targetComponentId: this.context.targetComponentId,
6087
+ currentRoute: null,
6088
+ currentPage: this.context.currentPage(),
6089
+ selectedWidgetKey,
6090
+ provider: this.context.provider(),
6091
+ model: this.context.model(),
6092
+ apiKey: this.context.apiKey(),
6093
+ componentCapabilities,
6094
+ ...this.buildAuthoringContext(request),
6095
+ };
6096
+ }
6097
+ async toTurnResultFromStreamEvent(event) {
6098
+ const payload = this.toJsonObject(event.payload) ?? {};
6099
+ if (event.type === 'result') {
6100
+ return this.toResultTurnFromStreamPayload(payload);
6101
+ }
6102
+ if (event.type === 'error') {
6103
+ const message = this.describeStreamError(payload);
6104
+ return {
6105
+ state: 'error',
6106
+ phase: 'contextualize',
6107
+ assistantMessage: message,
6108
+ canApply: false,
6109
+ statusText: '',
6110
+ errorText: message,
6111
+ preview: null,
6112
+ diagnostics: { streamError: payload },
6113
+ };
6114
+ }
6115
+ if (event.type === 'cancelled') {
6116
+ return {
6117
+ state: 'listening',
6118
+ phase: 'capture',
6119
+ assistantMessage: this.context.tx('agentic.status.cancelled', 'Request cancelled.'),
6120
+ quickReplies: [],
6121
+ canApply: false,
6122
+ statusText: '',
6123
+ errorText: '',
6124
+ preview: null,
6125
+ pendingPatch: null,
6126
+ };
6127
+ }
6128
+ return {
6129
+ state: 'processing',
6130
+ phase: this.phaseForStreamPayload(payload),
6131
+ assistantMessage: undefined,
6132
+ canApply: false,
6133
+ statusText: this.statusForStreamPayload(payload),
6134
+ errorText: '',
6135
+ preview: null,
6136
+ };
6137
+ }
6138
+ async toResultTurnFromStreamPayload(payload) {
6139
+ const intentResolution = payload['intentResolution'];
6140
+ const preview = payload['preview'];
6141
+ if (!intentResolution) {
6142
+ const message = this.context.tx('agentic.errors.intentResolution', 'Intent could not be resolved.');
6143
+ return {
6144
+ state: 'error',
6145
+ phase: 'contextualize',
6146
+ assistantMessage: message,
6147
+ canApply: false,
6148
+ statusText: '',
6149
+ errorText: message,
6150
+ preview: null,
6151
+ };
6152
+ }
6153
+ const assistantMessage = this.readString(payload, 'assistantMessage')
6154
+ || this.resolveIntentAssistantMessage(intentResolution)
6155
+ || (preview ? this.context.describePreviewStatus(preview) : '');
6156
+ const quickReplies = Array.isArray(payload['quickReplies'])
6157
+ ? this.toShellQuickReplies(payload['quickReplies'])
6158
+ : [];
6159
+ const canApply = payload['canApply'] === true && !!preview?.valid;
6160
+ if (!canApply) {
6161
+ const pendingClarification = this.toShellPendingClarification(intentResolution?.pendingClarification);
6162
+ return {
6163
+ state: pendingClarification ? 'clarification' : 'success',
6164
+ phase: pendingClarification ? 'clarify' : 'summarize',
6165
+ assistantMessage,
6166
+ quickReplies,
6167
+ canApply: false,
6168
+ statusText: '',
6169
+ errorText: '',
6170
+ preview: null,
6171
+ pendingClarification,
6172
+ diagnostics: { intentResolution, preview },
6173
+ };
6174
+ }
6175
+ const applied = await this.context.applyLocalPreview(preview);
6176
+ if (!applied.success) {
6177
+ const message = applied.error || this.context.tx('agentic.errors.applyLocal', 'Preview could not be applied.');
6178
+ return {
6179
+ state: 'error',
6180
+ phase: 'preview',
6181
+ assistantMessage: message,
6182
+ canApply: false,
6183
+ statusText: '',
6184
+ errorText: message,
6185
+ preview: null,
6186
+ diagnostics: { intentResolution, preview },
6187
+ };
6188
+ }
6189
+ const status = assistantMessage || this.context.describePreviewStatus(preview);
6190
+ return {
6191
+ state: 'review',
6192
+ phase: 'review',
6193
+ assistantMessage: status,
6194
+ quickReplies,
6195
+ canApply: true,
6196
+ statusText: status,
6197
+ errorText: '',
6198
+ preview,
6199
+ diagnostics: { intentResolution, preview },
6200
+ };
6201
+ }
6202
+ phaseForStreamPayload(payload) {
6203
+ const phase = this.readString(payload, 'phase');
6204
+ if (phase === 'intent.resolve')
6205
+ return 'contextualize';
6206
+ if (phase.startsWith('preview.'))
6207
+ return 'preview';
6208
+ if (phase === 'review')
6209
+ return 'review';
6210
+ return 'contextualize';
6211
+ }
6212
+ statusForStreamPayload(payload) {
6213
+ const phase = this.readString(payload, 'phase');
6214
+ switch (phase) {
6215
+ case 'context.bundle':
6216
+ return this.context.tx('agentic.status.contextBundle', 'Preparing context...');
6217
+ case 'intent.resolve':
6218
+ return this.context.tx('agentic.status.resolvingIntent', 'Resolving intent...');
6219
+ case 'resource.discovery':
6220
+ return this.context.tx('agentic.status.resourceDiscovery', 'Finding API resources...');
6221
+ case 'preview.plan':
6222
+ return this.context.tx('agentic.status.previewing', 'Generating preview...');
6223
+ case 'preview.compile':
6224
+ return this.context.tx('agentic.status.previewCompile', 'Compiling preview...');
6225
+ default:
6226
+ return this.readString(payload, 'summary') || this.readString(payload, 'message');
6227
+ }
6228
+ }
6229
+ describeStreamError(payload) {
6230
+ const code = this.readString(payload, 'code');
6231
+ if (code === 'agentic-authoring-timeout') {
6232
+ return this.context.tx('agentic.errors.streamTimeout', 'The assistant took too long to finish this request. Try again with a narrower request or review the active context.');
6233
+ }
6234
+ if (code === 'agentic-authoring-processing-failed') {
6235
+ return this.context.tx('agentic.errors.streamProcessing', 'The assistant could not finish this authoring request. Try again or ask support to review the diagnostics.');
6236
+ }
6237
+ return this.readString(payload, 'assistantMessage')
6238
+ || this.context.tx('agentic.errors.generic', 'AI authoring failed.');
6239
+ }
6240
+ toTurnStreamTransportErrorResult(error) {
6241
+ const message = this.isTurnStreamConnectionError(error)
6242
+ ? this.context.tx('agentic.errors.streamConnection', 'The assistant stream was interrupted before the turn finished. Try again or ask support to check the stream connection.')
6243
+ : this.context.describeError(error);
6244
+ return {
6245
+ state: 'error',
6246
+ phase: 'contextualize',
6247
+ assistantMessage: message,
6248
+ canApply: false,
6249
+ statusText: '',
6250
+ errorText: message,
6251
+ preview: null,
6252
+ diagnostics: { streamTransportError: this.toJsonObject(error) ?? { reason: 'stream-transport-error' } },
6253
+ };
6254
+ }
6255
+ shouldFallbackFromTurnStreamError(error) {
6256
+ if (this.isTurnStreamConnectionError(error)) {
6257
+ return false;
6258
+ }
6259
+ const status = this.readErrorStatus(error);
6260
+ if (status === 401 || status === 403) {
6261
+ return false;
6262
+ }
6263
+ return status === 404 || status === 501 || status === 503;
6264
+ }
6265
+ isTurnStreamConnectionError(error) {
6266
+ return this.toJsonObject(error)?.['praxisAgenticTurnStreamConnectionError'] === true;
6267
+ }
6268
+ readErrorStatus(error) {
6269
+ const status = this.toJsonObject(error)?.['status'];
6270
+ return typeof status === 'number' && Number.isFinite(status) ? status : null;
6271
+ }
6272
+ async handleResourceDiscoveryTool(request, prompt) {
6273
+ const contextHints = this.toJsonObject(request.action?.contextHints) ?? {};
6274
+ const retrievalQuery = this.readString(contextHints, 'retrievalQuery') || prompt;
6275
+ const artifactKind = this.readString(contextHints, 'artifactKind') || null;
6276
+ const limit = this.readNumber(contextHints, 'limit');
6277
+ const result = await firstValueFrom(this.service.searchResourceCandidates({
6278
+ retrievalQuery,
6279
+ userPrompt: prompt,
6280
+ artifactKind,
6281
+ limit,
6282
+ }));
6283
+ const quickReplies = this.toResourceCandidateQuickReplies(result.candidates, result.artifactKind);
6284
+ const assistantMessage = quickReplies.length
6285
+ ? this.context.tx('agentic.resourceDiscovery.found', 'I found APIs that can feed this screen. Choose one before generating the preview.')
6286
+ : this.context.tx('agentic.resourceDiscovery.empty', 'I did not find a matching API yet. Describe the business data this screen should use.');
6287
+ return {
6288
+ state: 'clarification',
6289
+ phase: 'clarify',
6290
+ assistantMessage,
6291
+ quickReplies,
6292
+ canApply: false,
6293
+ statusText: '',
6294
+ errorText: '',
6295
+ preview: null,
6296
+ pendingClarification: {
6297
+ sourcePrompt: request.pendingClarification?.sourcePrompt || prompt,
6298
+ assistantMessage,
6299
+ questions: [
6300
+ {
6301
+ id: 'resource',
6302
+ type: 'text',
6303
+ label: assistantMessage,
6304
+ },
6305
+ ],
6306
+ clientTurnId: request.clientTurnId,
6307
+ diagnostics: {
6308
+ resourceDiscovery: {
6309
+ tool: result.tool,
6310
+ retrievalQuery: result.retrievalQuery,
6311
+ artifactKind: result.artifactKind,
6312
+ warnings: result.warnings,
6313
+ },
6314
+ },
6315
+ },
6316
+ diagnostics: { resourceDiscovery: result },
6317
+ };
6318
+ }
6319
+ isResourceDiscoveryTool(request) {
6320
+ const contextHints = this.toJsonObject(request.action?.contextHints);
6321
+ return this.readString(contextHints, 'tool') === 'searchApiResources';
6322
+ }
6323
+ toResourceCandidateQuickReplies(candidates, artifactKind) {
6324
+ return candidates
6325
+ .filter((candidate) => !!candidate.resourcePath?.trim())
6326
+ .map((candidate) => {
6327
+ const resourcePath = candidate.resourcePath.trim();
6328
+ const submitUrl = candidate.submitUrl?.trim() || resourcePath;
6329
+ return {
6330
+ id: this.resourceCandidateId(resourcePath),
6331
+ kind: 'suggestion',
6332
+ label: this.resourceCandidateLabel(resourcePath),
6333
+ prompt: this.formatResourceCandidatePrompt(resourcePath),
6334
+ description: this.resourceCandidateDescription(candidate),
6335
+ icon: 'dataset',
6336
+ tone: 'resource',
6337
+ contextHints: {
6338
+ resourcePath,
6339
+ submitUrl,
6340
+ operation: candidate.operation,
6341
+ schemaUrl: candidate.schemaUrl,
6342
+ submitMethod: candidate.submitMethod,
6343
+ artifactKind,
6344
+ },
6345
+ };
6346
+ });
6347
+ }
6348
+ formatResourceCandidatePrompt(resourcePath) {
6349
+ return this.context
6350
+ .tx('agentic.resourceDiscovery.useResource', 'Use {resourcePath}')
6351
+ .replace('{resourcePath}', resourcePath);
6352
+ }
6353
+ resourceCandidateId(resourcePath) {
6354
+ const slug = resourcePath
6355
+ .toLowerCase()
6356
+ .replace(/[^a-z0-9]+/g, '-')
6357
+ .replace(/^-+|-+$/g, '');
6358
+ return slug ? `resource-${slug}` : 'resource-candidate';
6359
+ }
6360
+ resourceCandidateLabel(resourcePath) {
6361
+ const segment = resourcePath.split('/').filter((part) => !!part).pop() || resourcePath;
6362
+ const label = segment
6363
+ .replace(/[-_]+/g, ' ')
6364
+ .replace(/\s+/g, ' ')
6365
+ .trim();
6366
+ return label
6367
+ ? label.charAt(0).toUpperCase() + label.slice(1)
6368
+ : resourcePath;
6369
+ }
6370
+ resourceCandidateDescription(candidate) {
6371
+ const method = candidate.submitMethod?.trim();
6372
+ const url = candidate.submitUrl?.trim();
6373
+ return method && url ? `${method} ${url}` : url || undefined;
6374
+ }
5863
6375
  resolveIntentAssistantMessage(intentResolution) {
6376
+ if (this.isExecutableIntent(intentResolution)) {
6377
+ return null;
6378
+ }
5864
6379
  const message = intentResolution.assistantMessage?.trim();
5865
6380
  return message || null;
5866
6381
  }
6382
+ isExecutableIntent(intentResolution) {
6383
+ const operationKind = intentResolution.operationKind;
6384
+ const artifactKind = intentResolution.artifactKind;
6385
+ return !!intentResolution.valid
6386
+ && intentResolution.gate?.status === 'eligible'
6387
+ && !!intentResolution.selectedCandidate
6388
+ && (artifactKind === 'form' || artifactKind === 'dashboard' || artifactKind === 'table' || artifactKind === 'page')
6389
+ && (operationKind === 'create' || operationKind === 'modify' || operationKind === 'remove' || operationKind === 'compose');
6390
+ }
5867
6391
  resolveIntentClarification(intentResolution) {
5868
6392
  const questions = intentResolution.clarificationQuestions?.filter((question) => !!question) ?? [];
5869
6393
  if (!intentResolution.valid && questions.length) {
@@ -5872,7 +6396,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
5872
6396
  return null;
5873
6397
  }
5874
6398
  describeIntentResolutionFailure(intentResolution) {
5875
- if (intentResolution.valid && intentResolution.gate?.status === 'eligible' && intentResolution.selectedCandidate) {
6399
+ if (this.isExecutableIntent(intentResolution)) {
5876
6400
  return null;
5877
6401
  }
5878
6402
  const questions = intentResolution.clarificationQuestions?.filter((question) => !!question) ?? [];
@@ -5897,6 +6421,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
5897
6421
  kind: reply.kind,
5898
6422
  label: reply.label,
5899
6423
  prompt: reply.prompt,
6424
+ description: reply.description,
6425
+ icon: reply.icon,
6426
+ tone: reply.tone,
6427
+ contextHints: this.toJsonObject(reply.contextHints),
5900
6428
  }));
5901
6429
  }
5902
6430
  buildPendingClarificationForNextTurn(request, sourcePrompt, clarification) {
@@ -5947,16 +6475,47 @@ class PageBuilderAgenticAuthoringTurnFlow {
5947
6475
  diagnostics: this.toJsonObject(pending?.diagnostics) ?? undefined,
5948
6476
  };
5949
6477
  }
6478
+ hasPendingClarificationQuestion(pending) {
6479
+ return !!pending?.sourcePrompt?.trim() && pending.questions.some((question) => !!question.label?.trim());
6480
+ }
5950
6481
  resolveEffectivePrompt(intentResolution, fallbackPrompt) {
5951
6482
  return intentResolution.effectivePrompt?.trim() || fallbackPrompt;
5952
6483
  }
5953
6484
  buildAuthoringContext(request) {
6485
+ const contextHints = this.buildContextHints(request);
5954
6486
  return {
5955
6487
  sessionId: request.sessionId,
5956
6488
  clientTurnId: request.clientTurnId,
5957
6489
  conversationMessages: this.toConversationMessages(request.messages ?? []),
5958
6490
  pendingClarification: this.toPendingClarification(request.pendingClarification),
5959
6491
  attachmentSummaries: this.toAttachmentSummaries(request.attachments ?? []),
6492
+ contextHints,
6493
+ };
6494
+ }
6495
+ buildIntentResolutionRequest(prompt, selectedWidgetKey, componentCapabilities, authoringContext) {
6496
+ return {
6497
+ userPrompt: prompt,
6498
+ targetApp: this.context.targetApp,
6499
+ targetComponentId: this.context.targetComponentId,
6500
+ currentPage: this.context.currentPage(),
6501
+ selectedWidgetKey,
6502
+ provider: this.context.provider(),
6503
+ model: this.context.model(),
6504
+ apiKey: this.context.apiKey(),
6505
+ componentCapabilities,
6506
+ ...authoringContext,
6507
+ };
6508
+ }
6509
+ buildContextHints(request) {
6510
+ const base = this.toJsonObject(request.action?.contextHints)
6511
+ ?? this.toJsonObject(request.contextHints);
6512
+ const includeLlmDiagnostics = this.context.includeLlmDiagnostics?.() === true;
6513
+ if (!includeLlmDiagnostics) {
6514
+ return base ?? undefined;
6515
+ }
6516
+ return {
6517
+ ...(base ?? {}),
6518
+ includeLlmDiagnostics: true,
5960
6519
  };
5961
6520
  }
5962
6521
  toAttachmentSummaries(attachments) {
@@ -6005,6 +6564,14 @@ class PageBuilderAgenticAuthoringTurnFlow {
6005
6564
  ? value
6006
6565
  : null;
6007
6566
  }
6567
+ readString(value, key) {
6568
+ const raw = value?.[key];
6569
+ return typeof raw === 'string' ? raw.trim() : '';
6570
+ }
6571
+ readNumber(value, key) {
6572
+ const raw = value?.[key];
6573
+ return typeof raw === 'number' && Number.isFinite(raw) ? raw : null;
6574
+ }
6008
6575
  }
6009
6576
 
6010
6577
  function buildConnectionsViewerModel(page) {
@@ -6522,6 +7089,8 @@ class DynamicPageBuilderComponent {
6522
7089
  agenticAuthoringComponentId;
6523
7090
  agenticAuthoringScope = 'user';
6524
7091
  agenticAuthoringEtag;
7092
+ agenticAuthoringIncludeLlmDiagnostics = false;
7093
+ agenticAuthoringEnableStreaming = false;
6525
7094
  pageChange = new EventEmitter();
6526
7095
  agenticAuthoringApplied = new EventEmitter();
6527
7096
  currentPage = signal({ widgets: [] }, ...(ngDevMode ? [{ debugName: "currentPage" }] : []));
@@ -6536,6 +7105,7 @@ class DynamicPageBuilderComponent {
6536
7105
  agenticAuthoringConversation = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringConversation" }] : []));
6537
7106
  agenticAuthoringQuickReplies = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringQuickReplies" }] : []));
6538
7107
  agenticAuthoringAttachments = signal([], ...(ngDevMode ? [{ debugName: "agenticAuthoringAttachments" }] : []));
7108
+ agenticAuthoringLlmDiagnostics = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringLlmDiagnostics" }] : []));
6539
7109
  agenticAuthoringEditingMessageId = signal(null, ...(ngDevMode ? [{ debugName: "agenticAuthoringEditingMessageId" }] : []));
6540
7110
  agenticAuthoringPanelLayout = signal({
6541
7111
  left: 16,
@@ -6568,6 +7138,7 @@ class DynamicPageBuilderComponent {
6568
7138
  this.previewMode = !this.previewMode;
6569
7139
  if (this.previewMode) {
6570
7140
  this.connectionsViewerOpen.set(false);
7141
+ this.agenticAuthoringLlmDiagnostics.set(null);
6571
7142
  }
6572
7143
  }
6573
7144
  toggleConnectionsViewer() {
@@ -6728,11 +7299,10 @@ class DynamicPageBuilderComponent {
6728
7299
  try {
6729
7300
  const controller = this.ensureAgenticTurnController();
6730
7301
  const editingMessageId = this.agenticAuthoringEditingMessageId();
6731
- const state = await firstValueFrom(editingMessageId
7302
+ await this.consumeAgenticTurn(editingMessageId
6732
7303
  ? controller.submitEditedMessage(editingMessageId, prompt)
6733
7304
  : controller.submitPrompt(prompt));
6734
7305
  this.agenticAuthoringEditingMessageId.set(null);
6735
- this.applyAgenticTurnState(state);
6736
7306
  }
6737
7307
  catch (error) {
6738
7308
  this.agenticAuthoringStatus.set('');
@@ -6750,8 +7320,7 @@ class DynamicPageBuilderComponent {
6750
7320
  }
6751
7321
  if (reply.kind === 'cancel') {
6752
7322
  this.agenticAuthoringEditingMessageId.set(null);
6753
- const state = await firstValueFrom(this.ensureAgenticTurnController().cancel());
6754
- this.applyAgenticTurnState(state);
7323
+ await this.consumeAgenticTurn(this.ensureAgenticTurnController().cancel());
6755
7324
  return;
6756
7325
  }
6757
7326
  if (reply.kind === 'revise') {
@@ -6762,7 +7331,37 @@ class DynamicPageBuilderComponent {
6762
7331
  }
6763
7332
  this.agenticAuthoringEditingMessageId.set(null);
6764
7333
  this.agenticAuthoringPrompt.set(reply.prompt);
6765
- await this.previewAgenticAuthoring();
7334
+ this.agenticAuthoringBusy.set(true);
7335
+ this.agenticAuthoringError.set('');
7336
+ this.agenticAuthoringPreviewResult.set(null);
7337
+ this.agenticAuthoringStatus.set(this.tx('agentic.status.resolvingIntent', 'Resolving intent...'));
7338
+ try {
7339
+ const controller = this.ensureAgenticTurnController();
7340
+ const contextHints = reply.contextHints ? { ...reply.contextHints } : undefined;
7341
+ await this.consumeAgenticTurn(controller.snapshot().state === 'clarification'
7342
+ ? controller.answerClarification({
7343
+ id: reply.id,
7344
+ label: reply.prompt,
7345
+ value: reply.prompt,
7346
+ description: reply.description ?? undefined,
7347
+ contextHints,
7348
+ })
7349
+ : controller.submitPrompt(reply.prompt, {
7350
+ kind: reply.kind || 'quick-reply',
7351
+ id: reply.id,
7352
+ value: reply.prompt,
7353
+ contextHints,
7354
+ }));
7355
+ }
7356
+ catch (error) {
7357
+ this.agenticAuthoringStatus.set('');
7358
+ const message = this.describeAgenticError(error);
7359
+ this.agenticAuthoringError.set(message);
7360
+ this.appendAgenticMessage('error', message);
7361
+ }
7362
+ finally {
7363
+ this.agenticAuthoringBusy.set(false);
7364
+ }
6766
7365
  }
6767
7366
  attachAgenticContext() {
6768
7367
  const controller = this.ensureAgenticTurnController();
@@ -6807,8 +7406,7 @@ class DynamicPageBuilderComponent {
6807
7406
  this.agenticAuthoringError.set('');
6808
7407
  this.agenticAuthoringPreviewResult.set(null);
6809
7408
  try {
6810
- const state = await firstValueFrom(this.ensureAgenticTurnController().resendMessage(message.id));
6811
- this.applyAgenticTurnState(state);
7409
+ await this.consumeAgenticTurn(this.ensureAgenticTurnController().resendMessage(message.id));
6812
7410
  }
6813
7411
  catch (error) {
6814
7412
  this.agenticAuthoringStatus.set('');
@@ -6877,6 +7475,8 @@ class DynamicPageBuilderComponent {
6877
7475
  provider: () => this.agenticAuthoringProvider,
6878
7476
  model: () => this.agenticAuthoringModel,
6879
7477
  apiKey: () => this.agenticAuthoringApiKey,
7478
+ enableTurnStream: () => this.agenticAuthoringEnableStreaming,
7479
+ includeLlmDiagnostics: () => this.agenticAuthoringIncludeLlmDiagnostics,
6880
7480
  loadComponentCapabilities: () => this.loadAgenticComponentCapabilities(),
6881
7481
  applyLocalPreview: (result) => this.applyAgenticPreviewLocally(result),
6882
7482
  describePreviewFailure: (result) => this.describeAgenticPreviewFailure(result),
@@ -6909,9 +7509,36 @@ class DynamicPageBuilderComponent {
6909
7509
  this.agenticAuthoringError.set(state.errorText);
6910
7510
  this.agenticAuthoringPreviewResult.set(state.preview ?? null);
6911
7511
  this.agenticAuthoringAttachments.set(state.attachments);
7512
+ this.agenticAuthoringLlmDiagnostics.set(this.resolveAgenticLlmDiagnostics(state.diagnostics));
7513
+ }
7514
+ consumeAgenticTurn(states$) {
7515
+ return lastValueFrom(states$.pipe(tap((state) => this.applyAgenticTurnState(state))));
7516
+ }
7517
+ agenticAuthoringDiagnosticsTop() {
7518
+ return Math.max(16, this.agenticAuthoringPanelLayout().top);
7519
+ }
7520
+ agenticAuthoringLlmDiagnosticsText() {
7521
+ const diagnostics = this.agenticAuthoringLlmDiagnostics();
7522
+ if (!this.agenticAuthoringIncludeLlmDiagnostics || !diagnostics) {
7523
+ return '';
7524
+ }
7525
+ return JSON.stringify(diagnostics, null, 2);
7526
+ }
7527
+ resolveAgenticLlmDiagnostics(diagnostics) {
7528
+ if (!this.agenticAuthoringIncludeLlmDiagnostics) {
7529
+ return null;
7530
+ }
7531
+ const intentResolution = this.toRecord(diagnostics?.['intentResolution']);
7532
+ return this.toRecord(intentResolution?.['llmDiagnostics']);
7533
+ }
7534
+ toRecord(value) {
7535
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
7536
+ return null;
7537
+ }
7538
+ return value;
6912
7539
  }
6913
7540
  resolvePreviewCompiledFormPatch(preview) {
6914
- if (preview.uiCompositionPlan) {
7541
+ if (preview.uiCompositionPlan || preview.compiledFormPatch?.patch?.page) {
6915
7542
  return {
6916
7543
  ...preview.compiledFormPatch,
6917
7544
  patch: {
@@ -6920,9 +7547,6 @@ class DynamicPageBuilderComponent {
6920
7547
  },
6921
7548
  };
6922
7549
  }
6923
- if (preview.compiledFormPatch?.patch?.page) {
6924
- return preview.compiledFormPatch;
6925
- }
6926
7550
  return preview.compiledFormPatch;
6927
7551
  }
6928
7552
  focusCanvasWidget(widgetKey) {
@@ -7043,7 +7667,7 @@ class DynamicPageBuilderComponent {
7043
7667
  return resolvePraxisPageBuilderText(this.i18n, key, fallback);
7044
7668
  }
7045
7669
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicPageBuilderComponent, deps: [{ token: i1.MatDialog }, { token: SETTINGS_PANEL_BRIDGE, optional: true }], target: i0.ɵɵFactoryTarget.Component });
7046
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: DynamicPageBuilderComponent, isStandalone: true, selector: "praxis-dynamic-page-builder", inputs: { page: "page", context: "context", strictValidation: "strictValidation", autoPersist: "autoPersist", enableCustomization: "enableCustomization", showSettingsButton: "showSettingsButton", pageIdentity: "pageIdentity", componentInstanceId: "componentInstanceId", pageEditorComponent: "pageEditorComponent", enableAgenticAuthoring: "enableAgenticAuthoring", agenticAuthoringProvider: "agenticAuthoringProvider", agenticAuthoringModel: "agenticAuthoringModel", agenticAuthoringApiKey: "agenticAuthoringApiKey", agenticAuthoringComponentId: "agenticAuthoringComponentId", agenticAuthoringScope: "agenticAuthoringScope", agenticAuthoringEtag: "agenticAuthoringEtag" }, outputs: { pageChange: "pageChange", agenticAuthoringApplied: "agenticAuthoringApplied" }, providers: [
7670
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: DynamicPageBuilderComponent, isStandalone: true, selector: "praxis-dynamic-page-builder", inputs: { page: "page", context: "context", strictValidation: "strictValidation", autoPersist: "autoPersist", enableCustomization: "enableCustomization", showSettingsButton: "showSettingsButton", pageIdentity: "pageIdentity", componentInstanceId: "componentInstanceId", pageEditorComponent: "pageEditorComponent", enableAgenticAuthoring: "enableAgenticAuthoring", agenticAuthoringProvider: "agenticAuthoringProvider", agenticAuthoringModel: "agenticAuthoringModel", agenticAuthoringApiKey: "agenticAuthoringApiKey", agenticAuthoringComponentId: "agenticAuthoringComponentId", agenticAuthoringScope: "agenticAuthoringScope", agenticAuthoringEtag: "agenticAuthoringEtag", agenticAuthoringIncludeLlmDiagnostics: "agenticAuthoringIncludeLlmDiagnostics", agenticAuthoringEnableStreaming: "agenticAuthoringEnableStreaming" }, outputs: { pageChange: "pageChange", agenticAuthoringApplied: "agenticAuthoringApplied" }, providers: [
7047
7671
  providePraxisPageBuilderI18n(),
7048
7672
  { provide: DYNAMIC_PAGE_SHELL_EDITOR, useValue: WidgetShellEditorComponent },
7049
7673
  ], viewQueries: [{ propertyName: "runtime", first: true, predicate: ["runtime"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
@@ -7107,6 +7731,26 @@ class DynamicPageBuilderComponent {
7107
7731
  (close)="toggleAgenticAuthoring()"
7108
7732
  />
7109
7733
 
7734
+ <section
7735
+ *ngIf="agenticAuthoringIncludeLlmDiagnostics && agenticAuthoringLlmDiagnosticsText()"
7736
+ class="agentic-diagnostics-panel"
7737
+ data-testid="page-builder-agentic-llm-diagnostics"
7738
+ role="region"
7739
+ [attr.aria-label]="tx('agentic.diagnostics.title', 'LLM diagnostics')"
7740
+ [style.top.px]="agenticAuthoringDiagnosticsTop()"
7741
+ >
7742
+ <header class="agentic-diagnostics-panel__header">
7743
+ <span>{{ tx('agentic.diagnostics.title', 'LLM diagnostics') }}</span>
7744
+ <span class="agentic-diagnostics-panel__badge">
7745
+ {{ tx('agentic.diagnostics.badge', 'Debug') }}
7746
+ </span>
7747
+ </header>
7748
+ <p class="agentic-diagnostics-panel__description">
7749
+ {{ tx('agentic.diagnostics.description', 'Prompt, context bundle, and tool catalog returned by the backend for this turn.') }}
7750
+ </p>
7751
+ <pre>{{ agenticAuthoringLlmDiagnosticsText() }}</pre>
7752
+ </section>
7753
+
7110
7754
  <praxis-floating-toolbar
7111
7755
  [visible]="showSettings()"
7112
7756
  [canUndo]="false"
@@ -7141,7 +7785,7 @@ class DynamicPageBuilderComponent {
7141
7785
  </button>
7142
7786
  </praxis-floating-toolbar>
7143
7787
  </div>
7144
- `, isInline: true, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: DynamicWidgetPageComponent, selector: "praxis-dynamic-page", inputs: ["page", "context", "strictValidation", "enableCustomization", "showPageSettingsButton", "shellEditorComponent", "pageEditorComponent", "autoPersist", "pageIdentity", "componentInstanceId"], outputs: ["pageChange", "widgetEvent", "widgetDiagnosticsChange"] }, { kind: "component", type: FloatingToolbarComponent, selector: "praxis-floating-toolbar", inputs: ["visible", "canUndo", "canRedo"], outputs: ["add", "undo", "redo", "settings", "preview", "save"] }, { kind: "component", type: ConnectionsViewerPanelComponent, selector: "praxis-connections-viewer-panel", inputs: ["open", "page"], outputs: ["focusWidget", "openPageSettings"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "busy", "canSubmit", "canApply", "submitOnEnter", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "layoutChange"] }] });
7788
+ `, isInline: true, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%}.agentic-diagnostics-panel{position:absolute;z-index:21;right:16px;width:min(520px,calc(100% - 32px));max-height:min(440px,calc(100% - 32px));overflow:auto;padding:12px;border:1px solid rgba(107,114,128,.28);border-radius:8px;background:#fffffff5;color:#111827;box-shadow:0 14px 36px #0000003d}.agentic-diagnostics-panel__header{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px;font-weight:700}.agentic-diagnostics-panel__badge{padding:2px 6px;border:1px solid rgba(107,114,128,.28);border-radius:8px;font-size:11px;font-weight:600;color:#4b5563}.agentic-diagnostics-panel__description{margin:8px 0 10px;color:#4b5563;font-size:12px;line-height:1.4}.agentic-diagnostics-panel pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px;line-height:1.45;color:#1f2937}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: DynamicWidgetPageComponent, selector: "praxis-dynamic-page", inputs: ["page", "context", "strictValidation", "enableCustomization", "showPageSettingsButton", "shellEditorComponent", "pageEditorComponent", "autoPersist", "pageIdentity", "componentInstanceId"], outputs: ["pageChange", "widgetEvent", "widgetDiagnosticsChange"] }, { kind: "component", type: FloatingToolbarComponent, selector: "praxis-floating-toolbar", inputs: ["visible", "canUndo", "canRedo"], outputs: ["add", "undo", "redo", "settings", "preview", "save"] }, { kind: "component", type: ConnectionsViewerPanelComponent, selector: "praxis-connections-viewer-panel", inputs: ["open", "page"], outputs: ["focusWidget", "openPageSettings"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "busy", "canSubmit", "canApply", "submitOnEnter", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "layoutChange"] }] });
7145
7789
  }
7146
7790
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicPageBuilderComponent, decorators: [{
7147
7791
  type: Component,
@@ -7219,6 +7863,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
7219
7863
  (close)="toggleAgenticAuthoring()"
7220
7864
  />
7221
7865
 
7866
+ <section
7867
+ *ngIf="agenticAuthoringIncludeLlmDiagnostics && agenticAuthoringLlmDiagnosticsText()"
7868
+ class="agentic-diagnostics-panel"
7869
+ data-testid="page-builder-agentic-llm-diagnostics"
7870
+ role="region"
7871
+ [attr.aria-label]="tx('agentic.diagnostics.title', 'LLM diagnostics')"
7872
+ [style.top.px]="agenticAuthoringDiagnosticsTop()"
7873
+ >
7874
+ <header class="agentic-diagnostics-panel__header">
7875
+ <span>{{ tx('agentic.diagnostics.title', 'LLM diagnostics') }}</span>
7876
+ <span class="agentic-diagnostics-panel__badge">
7877
+ {{ tx('agentic.diagnostics.badge', 'Debug') }}
7878
+ </span>
7879
+ </header>
7880
+ <p class="agentic-diagnostics-panel__description">
7881
+ {{ tx('agentic.diagnostics.description', 'Prompt, context bundle, and tool catalog returned by the backend for this turn.') }}
7882
+ </p>
7883
+ <pre>{{ agenticAuthoringLlmDiagnosticsText() }}</pre>
7884
+ </section>
7885
+
7222
7886
  <praxis-floating-toolbar
7223
7887
  [visible]="showSettings()"
7224
7888
  [canUndo]="false"
@@ -7253,7 +7917,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
7253
7917
  </button>
7254
7918
  </praxis-floating-toolbar>
7255
7919
  </div>
7256
- `, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%}\n"] }]
7920
+ `, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%}.agentic-diagnostics-panel{position:absolute;z-index:21;right:16px;width:min(520px,calc(100% - 32px));max-height:min(440px,calc(100% - 32px));overflow:auto;padding:12px;border:1px solid rgba(107,114,128,.28);border-radius:8px;background:#fffffff5;color:#111827;box-shadow:0 14px 36px #0000003d}.agentic-diagnostics-panel__header{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px;font-weight:700}.agentic-diagnostics-panel__badge{padding:2px 6px;border:1px solid rgba(107,114,128,.28);border-radius:8px;font-size:11px;font-weight:600;color:#4b5563}.agentic-diagnostics-panel__description{margin:8px 0 10px;color:#4b5563;font-size:12px;line-height:1.4}.agentic-diagnostics-panel pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px;line-height:1.45;color:#1f2937}\n"] }]
7257
7921
  }], ctorParameters: () => [{ type: i1.MatDialog }, { type: undefined, decorators: [{
7258
7922
  type: Optional
7259
7923
  }, {
@@ -7294,6 +7958,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
7294
7958
  type: Input
7295
7959
  }], agenticAuthoringEtag: [{
7296
7960
  type: Input
7961
+ }], agenticAuthoringIncludeLlmDiagnostics: [{
7962
+ type: Input
7963
+ }], agenticAuthoringEnableStreaming: [{
7964
+ type: Input
7297
7965
  }], pageChange: [{
7298
7966
  type: Output
7299
7967
  }], agenticAuthoringApplied: [{
package/index.d.ts CHANGED
@@ -7,7 +7,7 @@ import * as rxjs from 'rxjs';
7
7
  import { BehaviorSubject, Observable } from 'rxjs';
8
8
  import { FormGroup, FormControl, FormArray } from '@angular/forms';
9
9
  import { SettingsValueProvider } from '@praxisui/settings-panel';
10
- import { AgenticAuthoringPlanRequestContract, AgenticAuthoringQuickReplyContract, AgenticAuthoringPendingClarificationContract, AgenticAuthoringAttachmentSummaryContract, AgenticAuthoringIntentResolutionRequestContract, AgenticAuthoringConversationMessageContract, PraxisAssistantShellMessage, PraxisAssistantShellQuickReply, PraxisAssistantShellAttachment, PraxisAssistantShellLayout, PraxisAssistantShellLabels, PraxisAssistantShellState, PraxisAssistantShellContextItem } from '@praxisui/ai';
10
+ import { AgenticAuthoringPlanRequestContract, AgenticAuthoringCandidateContract, AiJsonValue, AgenticAuthoringQuickReplyContract, AgenticAuthoringPendingClarificationContract, AiJsonObject, AgenticAuthoringComponentCapabilitiesResultContract, AgenticAuthoringComponentCapabilityCatalogContract, AgenticAuthoringComponentCapabilityContract, AgenticAuthoringComponentFieldAliasContract, AgenticAuthoringComponentCapabilityExampleContract, AgenticAuthoringAttachmentSummaryContract, AgenticAuthoringIntentResolutionRequestContract, AgenticAuthoringConversationMessageContract, AgenticAuthoringResourceCandidatesRequestContract, AgenticAuthoringResourceCandidatesResultContract, AgenticAuthoringTurnStreamStartResponseContract, AgenticAuthoringTurnStreamEnvelopeContract, PraxisAssistantShellMessage, PraxisAssistantShellQuickReply, PraxisAssistantShellAttachment, PraxisAssistantShellLayout, PraxisAssistantShellLabels, PraxisAssistantShellState, PraxisAssistantShellContextItem } from '@praxisui/ai';
11
11
 
12
12
  declare const PLACEHOLDER = 1;
13
13
 
@@ -545,6 +545,19 @@ interface PageBuilderAgenticAuthoringOptions {
545
545
  baseUrl?: string;
546
546
  headersFactory?: () => Record<string, string | undefined>;
547
547
  defaultHeaders?: Record<string, string>;
548
+ eventSourceFactory?: PageBuilderAgenticAuthoringEventSourceFactory;
549
+ streamConnectionErrorGraceMs?: number;
550
+ }
551
+ interface PageBuilderAgenticAuthoringEventSource {
552
+ onmessage: ((event: MessageEvent<string>) => void) | null;
553
+ onerror: ((event: Event) => void) | null;
554
+ addEventListener?(type: string, listener: (event: MessageEvent<string>) => void): void;
555
+ close(): void;
556
+ }
557
+ type PageBuilderAgenticAuthoringEventSourceFactory = (url: string) => PageBuilderAgenticAuthoringEventSource;
558
+ interface PageBuilderAgenticAuthoringTurnStreamConnectionError {
559
+ readonly praxisAgenticTurnStreamConnectionError: true;
560
+ readonly cause: unknown;
548
561
  }
549
562
  interface PageBuilderAgenticAuthoringPromptRequest extends Omit<AgenticAuthoringPlanRequestContract, 'currentPage' | 'intentResolution'> {
550
563
  provider?: PageBuilderAgenticAuthoringProvider | null;
@@ -560,11 +573,29 @@ interface PageBuilderAgenticAuthoringIntentResolutionRequest extends Omit<Agenti
560
573
  componentCapabilities?: PageBuilderAgenticAuthoringComponentCapabilitiesResult | null;
561
574
  attachmentSummaries?: PageBuilderAgenticAuthoringAttachmentSummary[];
562
575
  }
576
+ interface PageBuilderAgenticAuthoringTurnStreamRequest {
577
+ userPrompt: string;
578
+ targetApp?: string | null;
579
+ targetComponentId?: string | null;
580
+ currentRoute?: string | null;
581
+ provider?: PageBuilderAgenticAuthoringProvider | null;
582
+ model?: string | null;
583
+ apiKey?: string | null;
584
+ currentPage?: unknown;
585
+ selectedWidgetKey?: string | null;
586
+ sessionId?: string | null;
587
+ clientTurnId?: string | null;
588
+ conversationMessages?: PageBuilderAgenticAuthoringConversationMessage[];
589
+ pendingClarification?: PageBuilderAgenticAuthoringPendingClarification | null;
590
+ componentCapabilities?: PageBuilderAgenticAuthoringComponentCapabilitiesResult | null;
591
+ attachmentSummaries?: PageBuilderAgenticAuthoringAttachmentSummary[];
592
+ contextHints?: AiJsonObject | null;
593
+ }
563
594
  type PageBuilderAgenticAuthoringConversationMessage = AgenticAuthoringConversationMessageContract;
564
595
  type PageBuilderAgenticAuthoringPendingClarification = AgenticAuthoringPendingClarificationContract;
565
596
  type PageBuilderAgenticAuthoringQuickReply = AgenticAuthoringQuickReplyContract;
566
597
  type PageBuilderAgenticAuthoringAttachmentSummary = AgenticAuthoringAttachmentSummaryContract;
567
- interface PageBuilderAgenticAuthoringCandidate {
598
+ interface PageBuilderAgenticAuthoringCandidate extends AgenticAuthoringCandidateContract {
568
599
  resourcePath: string;
569
600
  operation: string;
570
601
  schemaUrl: string;
@@ -573,6 +604,17 @@ interface PageBuilderAgenticAuthoringCandidate {
573
604
  score: number;
574
605
  reason: string;
575
606
  evidence: string[];
607
+ [key: string]: AiJsonValue | undefined;
608
+ }
609
+ interface PageBuilderAgenticAuthoringResourceCandidatesRequest extends AgenticAuthoringResourceCandidatesRequestContract {
610
+ }
611
+ interface PageBuilderAgenticAuthoringResourceCandidatesResult extends Omit<AgenticAuthoringResourceCandidatesResultContract, 'candidates'> {
612
+ valid: boolean;
613
+ tool: string;
614
+ retrievalQuery: string;
615
+ artifactKind: string;
616
+ candidates: PageBuilderAgenticAuthoringCandidate[];
617
+ warnings: string[];
576
618
  }
577
619
  interface PageBuilderAgenticAuthoringTarget {
578
620
  widgetKey: string;
@@ -587,22 +629,28 @@ interface PageBuilderAgenticAuthoringGateResult {
587
629
  status: string;
588
630
  messages: string[];
589
631
  }
590
- interface PageBuilderAgenticAuthoringComponentFieldAlias {
632
+ interface PageBuilderAgenticAuthoringComponentFieldAlias extends AgenticAuthoringComponentFieldAliasContract {
591
633
  field: string;
592
634
  aliases: string[];
593
635
  }
594
- interface PageBuilderAgenticAuthoringComponentCapability {
636
+ interface PageBuilderAgenticAuthoringComponentCapability extends Omit<AgenticAuthoringComponentCapabilityContract, 'fieldAliases' | 'examples'> {
595
637
  id: string;
596
638
  changeKind: string;
597
639
  triggerTerms: string[];
598
640
  fieldAliases: PageBuilderAgenticAuthoringComponentFieldAlias[];
641
+ examples?: PageBuilderAgenticAuthoringComponentCapabilityExample[];
642
+ }
643
+ interface PageBuilderAgenticAuthoringComponentCapabilityExample extends AgenticAuthoringComponentCapabilityExampleContract {
644
+ prompt: string;
645
+ intent: string;
646
+ configHints: string[];
599
647
  }
600
- interface PageBuilderAgenticAuthoringComponentCapabilityCatalog {
648
+ interface PageBuilderAgenticAuthoringComponentCapabilityCatalog extends Omit<AgenticAuthoringComponentCapabilityCatalogContract, 'capabilities'> {
601
649
  componentId: string;
602
650
  version: string;
603
651
  capabilities: PageBuilderAgenticAuthoringComponentCapability[];
604
652
  }
605
- interface PageBuilderAgenticAuthoringComponentCapabilitiesResult {
653
+ interface PageBuilderAgenticAuthoringComponentCapabilitiesResult extends Omit<AgenticAuthoringComponentCapabilitiesResultContract, 'catalogs'> {
606
654
  version: string;
607
655
  catalogs: PageBuilderAgenticAuthoringComponentCapabilityCatalog[];
608
656
  }
@@ -626,6 +674,7 @@ interface PageBuilderAgenticAuthoringIntentResolutionResult {
626
674
  warnings: string[];
627
675
  failureCodes: string[];
628
676
  currentPageSummary: unknown;
677
+ llmDiagnostics?: AiJsonObject | null;
629
678
  }
630
679
  interface PageBuilderMinimalFormPlanResult {
631
680
  valid: boolean;
@@ -658,6 +707,8 @@ interface PageBuilderPreviewResult {
658
707
  compiledFormPatch: PageBuilderCompiledFormPatch;
659
708
  diagnostics?: PageBuilderPreviewDiagnostics | null;
660
709
  }
710
+ type PageBuilderAgenticAuthoringTurnStreamStartResponse = AgenticAuthoringTurnStreamStartResponseContract;
711
+ type PageBuilderAgenticAuthoringTurnStreamEvent = AgenticAuthoringTurnStreamEnvelopeContract;
661
712
  interface PageBuilderApplyRequest {
662
713
  compiledFormPatch: PageBuilderCompiledFormPatch;
663
714
  componentId: string;
@@ -687,8 +738,14 @@ declare class PageBuilderAgenticAuthoringService {
687
738
  getComponentCapabilities(): Observable<PageBuilderAgenticAuthoringComponentCapabilitiesResult>;
688
739
  previewPage(request: PageBuilderAgenticAuthoringPromptRequest): Observable<PageBuilderPreviewResult>;
689
740
  resolveIntent(request: PageBuilderAgenticAuthoringIntentResolutionRequest): Observable<PageBuilderAgenticAuthoringIntentResolutionResult>;
741
+ searchResourceCandidates(request: PageBuilderAgenticAuthoringResourceCandidatesRequest): Observable<PageBuilderAgenticAuthoringResourceCandidatesResult>;
742
+ streamTurn(request: PageBuilderAgenticAuthoringTurnStreamRequest): Observable<PageBuilderAgenticAuthoringTurnStreamEvent>;
690
743
  applyPage(request: PageBuilderApplyRequest): Observable<PageBuilderApplyResult>;
691
744
  private buildHeaders;
745
+ private connectTurnStream;
746
+ private streamConnectionError;
747
+ private buildStreamUrl;
748
+ private createEventSource;
692
749
  private formatEtag;
693
750
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<PageBuilderAgenticAuthoringService, never>;
694
751
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<PageBuilderAgenticAuthoringService>;
@@ -779,6 +836,8 @@ declare class DynamicPageBuilderComponent implements OnChanges {
779
836
  agenticAuthoringComponentId?: string;
780
837
  agenticAuthoringScope: 'user' | 'tenant';
781
838
  agenticAuthoringEtag?: string;
839
+ agenticAuthoringIncludeLlmDiagnostics: boolean;
840
+ agenticAuthoringEnableStreaming: boolean;
782
841
  pageChange: EventEmitter<WidgetPageDefinition>;
783
842
  agenticAuthoringApplied: EventEmitter<PageBuilderApplyResult>;
784
843
  readonly currentPage: _angular_core.WritableSignal<WidgetPageDefinition>;
@@ -793,6 +852,7 @@ declare class DynamicPageBuilderComponent implements OnChanges {
793
852
  readonly agenticAuthoringConversation: _angular_core.WritableSignal<PraxisAssistantShellMessage[]>;
794
853
  readonly agenticAuthoringQuickReplies: _angular_core.WritableSignal<AgenticAuthoringQuickReply[]>;
795
854
  readonly agenticAuthoringAttachments: _angular_core.WritableSignal<PraxisAssistantShellAttachment[]>;
855
+ readonly agenticAuthoringLlmDiagnostics: _angular_core.WritableSignal<Record<string, unknown> | null>;
796
856
  readonly agenticAuthoringEditingMessageId: _angular_core.WritableSignal<string | null>;
797
857
  readonly agenticAuthoringPanelLayout: _angular_core.WritableSignal<PraxisAssistantShellLayout>;
798
858
  private previewMode;
@@ -827,6 +887,11 @@ declare class DynamicPageBuilderComponent implements OnChanges {
827
887
  private ensureAgenticTurnController;
828
888
  private applyAgenticPreviewLocally;
829
889
  private applyAgenticTurnState;
890
+ private consumeAgenticTurn;
891
+ agenticAuthoringDiagnosticsTop(): number;
892
+ agenticAuthoringLlmDiagnosticsText(): string;
893
+ private resolveAgenticLlmDiagnostics;
894
+ private toRecord;
830
895
  private resolvePreviewCompiledFormPatch;
831
896
  focusCanvasWidget(widgetKey: string): void;
832
897
  private parsePage;
@@ -842,8 +907,8 @@ declare class DynamicPageBuilderComponent implements OnChanges {
842
907
  private cloneValue;
843
908
  tx(key: string, fallback: string): string;
844
909
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicPageBuilderComponent, [null, { optional: true; }]>;
845
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicPageBuilderComponent, "praxis-dynamic-page-builder", never, { "page": { "alias": "page"; "required": false; }; "context": { "alias": "context"; "required": false; }; "strictValidation": { "alias": "strictValidation"; "required": false; }; "autoPersist": { "alias": "autoPersist"; "required": false; }; "enableCustomization": { "alias": "enableCustomization"; "required": false; }; "showSettingsButton": { "alias": "showSettingsButton"; "required": false; }; "pageIdentity": { "alias": "pageIdentity"; "required": false; }; "componentInstanceId": { "alias": "componentInstanceId"; "required": false; }; "pageEditorComponent": { "alias": "pageEditorComponent"; "required": false; }; "enableAgenticAuthoring": { "alias": "enableAgenticAuthoring"; "required": false; }; "agenticAuthoringProvider": { "alias": "agenticAuthoringProvider"; "required": false; }; "agenticAuthoringModel": { "alias": "agenticAuthoringModel"; "required": false; }; "agenticAuthoringApiKey": { "alias": "agenticAuthoringApiKey"; "required": false; }; "agenticAuthoringComponentId": { "alias": "agenticAuthoringComponentId"; "required": false; }; "agenticAuthoringScope": { "alias": "agenticAuthoringScope"; "required": false; }; "agenticAuthoringEtag": { "alias": "agenticAuthoringEtag"; "required": false; }; }, { "pageChange": "pageChange"; "agenticAuthoringApplied": "agenticAuthoringApplied"; }, never, never, true, never>;
910
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicPageBuilderComponent, "praxis-dynamic-page-builder", never, { "page": { "alias": "page"; "required": false; }; "context": { "alias": "context"; "required": false; }; "strictValidation": { "alias": "strictValidation"; "required": false; }; "autoPersist": { "alias": "autoPersist"; "required": false; }; "enableCustomization": { "alias": "enableCustomization"; "required": false; }; "showSettingsButton": { "alias": "showSettingsButton"; "required": false; }; "pageIdentity": { "alias": "pageIdentity"; "required": false; }; "componentInstanceId": { "alias": "componentInstanceId"; "required": false; }; "pageEditorComponent": { "alias": "pageEditorComponent"; "required": false; }; "enableAgenticAuthoring": { "alias": "enableAgenticAuthoring"; "required": false; }; "agenticAuthoringProvider": { "alias": "agenticAuthoringProvider"; "required": false; }; "agenticAuthoringModel": { "alias": "agenticAuthoringModel"; "required": false; }; "agenticAuthoringApiKey": { "alias": "agenticAuthoringApiKey"; "required": false; }; "agenticAuthoringComponentId": { "alias": "agenticAuthoringComponentId"; "required": false; }; "agenticAuthoringScope": { "alias": "agenticAuthoringScope"; "required": false; }; "agenticAuthoringEtag": { "alias": "agenticAuthoringEtag"; "required": false; }; "agenticAuthoringIncludeLlmDiagnostics": { "alias": "agenticAuthoringIncludeLlmDiagnostics"; "required": false; }; "agenticAuthoringEnableStreaming": { "alias": "agenticAuthoringEnableStreaming"; "required": false; }; }, { "pageChange": "pageChange"; "agenticAuthoringApplied": "agenticAuthoringApplied"; }, never, never, true, never>;
846
911
  }
847
912
 
848
913
  export { ComponentPaletteDialogComponent, ConfirmDialogComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, WidgetShellEditorComponent, clearWidgetAiCatalogs, getPageAiCatalog, getWidgetAiCapabilities, providePageBuilderWidgetAiCatalogs, registerWidgetAiCatalog, registerWidgetAiCatalogs };
849
- export type { Capability, CapabilityCatalog, CapabilityCategory, ComponentPaletteData, ConfirmDialogData, PageBuilderAgenticAuthoringAttachmentSummary, PageBuilderAgenticAuthoringCandidate, PageBuilderAgenticAuthoringComponentCapabilitiesResult, PageBuilderAgenticAuthoringComponentCapability, PageBuilderAgenticAuthoringComponentCapabilityCatalog, PageBuilderAgenticAuthoringComponentFieldAlias, PageBuilderAgenticAuthoringConversationMessage, PageBuilderAgenticAuthoringGateResult, PageBuilderAgenticAuthoringIntentResolutionRequest, PageBuilderAgenticAuthoringIntentResolutionResult, PageBuilderAgenticAuthoringOptions, PageBuilderAgenticAuthoringPendingClarification, PageBuilderAgenticAuthoringPromptRequest, PageBuilderAgenticAuthoringProvider, PageBuilderAgenticAuthoringQuickReply, PageBuilderAgenticAuthoringTarget, PageBuilderApplyRequest, PageBuilderApplyResult, PageBuilderCompiledFormPatch, PageBuilderMinimalFormPlanResult, PageBuilderPreviewDiagnostics, PageBuilderPreviewResult, ValueKind };
914
+ export type { Capability, CapabilityCatalog, CapabilityCategory, ComponentPaletteData, ConfirmDialogData, PageBuilderAgenticAuthoringAttachmentSummary, PageBuilderAgenticAuthoringCandidate, PageBuilderAgenticAuthoringComponentCapabilitiesResult, PageBuilderAgenticAuthoringComponentCapability, PageBuilderAgenticAuthoringComponentCapabilityCatalog, PageBuilderAgenticAuthoringComponentCapabilityExample, PageBuilderAgenticAuthoringComponentFieldAlias, PageBuilderAgenticAuthoringConversationMessage, PageBuilderAgenticAuthoringEventSource, PageBuilderAgenticAuthoringEventSourceFactory, PageBuilderAgenticAuthoringGateResult, PageBuilderAgenticAuthoringIntentResolutionRequest, PageBuilderAgenticAuthoringIntentResolutionResult, PageBuilderAgenticAuthoringOptions, PageBuilderAgenticAuthoringPendingClarification, PageBuilderAgenticAuthoringPromptRequest, PageBuilderAgenticAuthoringProvider, PageBuilderAgenticAuthoringQuickReply, PageBuilderAgenticAuthoringResourceCandidatesRequest, PageBuilderAgenticAuthoringResourceCandidatesResult, PageBuilderAgenticAuthoringTarget, PageBuilderAgenticAuthoringTurnStreamConnectionError, PageBuilderAgenticAuthoringTurnStreamEvent, PageBuilderAgenticAuthoringTurnStreamRequest, PageBuilderAgenticAuthoringTurnStreamStartResponse, PageBuilderApplyRequest, PageBuilderApplyResult, PageBuilderCompiledFormPatch, PageBuilderMinimalFormPlanResult, PageBuilderPreviewDiagnostics, PageBuilderPreviewResult, ValueKind };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/page-builder",
3
- "version": "8.0.0-beta.7",
3
+ "version": "8.0.0-beta.9",
4
4
  "description": "Page and widget builder utilities for Praxis UI (grid, dynamic widgets, editors).",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^20.0.0",
@@ -8,9 +8,9 @@
8
8
  "@angular/forms": "^20.0.0",
9
9
  "@angular/cdk": "^20.0.0",
10
10
  "@angular/material": "^20.0.0",
11
- "@praxisui/ai": "^8.0.0-beta.7",
12
- "@praxisui/core": "^8.0.0-beta.7",
13
- "@praxisui/settings-panel": "^8.0.0-beta.7"
11
+ "@praxisui/ai": "^8.0.0-beta.9",
12
+ "@praxisui/core": "^8.0.0-beta.9",
13
+ "@praxisui/settings-panel": "^8.0.0-beta.9"
14
14
  },
15
15
  "dependencies": {
16
16
  "tslib": "^2.3.0"