@designcrowd/library.brand-page 6.0.1-jj-6 → 6.0.2

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.
Files changed (68) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-X6T8_3JP.js → CreatedOnBrandCrowd-de-de-Zptj2O2O.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-BC59Ss9F.js → CreatedOnBrandCrowd-en-us-Bxmo2Ps9.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-BF1QReGi.js → CreatedOnBrandCrowd-es-es-uytkzFdZ.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-BhI-30CT.js → CreatedOnBrandCrowd-fr-ca-26tDXsvZ.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-BhI-30CT.js → CreatedOnBrandCrowd-fr-fr-26tDXsvZ.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-BJQkhGhh.js → CreatedOnBrandCrowd-pt-br-DuNRkCU5.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-BJQkhGhh.js → CreatedOnBrandCrowd-pt-pt-DuNRkCU5.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-pQfxLfUx.js → CreatedOnDcom-de-de-D_aekURi.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-DQWSlN-g.js → CreatedOnDcom-en-us-CD3RyS8m.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-B5oZRbLp.js → CreatedOnDcom-es-es-BXCP18xT.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BBhSUN6J.js → CreatedOnDcom-fr-ca-BN04YN4o.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-CXc2On48.js → CreatedOnDcom-fr-fr-B8-sowys.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-B5AmbldX.js → CreatedOnDcom-pt-br-C0MTfZ_l.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-Df_mxV0U.js → CreatedOnDcom-pt-pt-BamTt9zq.js} +1 -1
  15. package/lib/src/brand-page-maker/composables/ai-chat/types.d.ts +0 -3
  16. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +171 -201
  17. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +0 -1
  18. package/lib/src/brand-page-maker/composables/useEventTracker.js +0 -1
  19. package/lib/src/brand-page-maker/constants/config-settings-constant.js +2 -2
  20. package/lib/src/css/library.brand-page-brandCrowd.min.css +27 -36
  21. package/lib/src/css/library.brand-page-brandPage.min.css +27 -36
  22. package/lib/src/css/library.brand-page-designCom.min.css +27 -36
  23. package/lib/src/css/library.brand-page-preview.min.css +27 -36
  24. package/lib/src/index.mjs +1 -1
  25. package/lib/src/{lib-DfeZjykC.js → lib-dQieWjFg.js} +20388 -20679
  26. package/lib/src/metadata-builder/ai-metadata-executor.js +1 -24
  27. package/lib/src/metadata-builder/builders/reorder-pages-builder.d.ts +13 -0
  28. package/lib/src/metadata-builder/builders/reorder-pages-builder.js +49 -0
  29. package/lib/src/metadata-builder/builders/section-builders/move-section-builder.d.ts +1 -13
  30. package/lib/src/metadata-builder/builders/section-builders/move-section-builder.js +30 -40
  31. package/lib/src/metadata-builder/builders/section-field-maps.js +0 -17
  32. package/lib/src/metadata-builder/metadata-executor.js +0 -4
  33. package/lib/src/metadata-builder/models.d.ts +0 -2
  34. package/lib/src/metadata-builder/models.js +0 -2
  35. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +3 -18
  36. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +0 -2
  37. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +0 -18
  38. package/package.json +1 -1
  39. package/src/brand-page/components/shop/Shop.vue +1 -0
  40. package/src/brand-page-maker/components/ai-text-edit/AiActionButtons.vue +12 -27
  41. package/src/brand-page-maker/components/ai-text-edit/AiChatbox.vue +2 -3
  42. package/src/brand-page-maker/components/ai-text-edit/TextBubble.vue +7 -7
  43. package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +1 -21
  44. package/src/brand-page-maker/composables/ai-chat/types.ts +0 -6
  45. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +255 -305
  46. package/src/brand-page-maker/composables/useEventTracker.ts +0 -1
  47. package/src/brand-page-maker/constants/config-settings-constant.ts +2 -2
  48. package/src/metadata-builder/ai-metadata-executor.ts +1 -31
  49. package/src/metadata-builder/builders/reorder-pages-builder.ts +80 -0
  50. package/src/metadata-builder/builders/section-builders/move-section-builder.ts +35 -58
  51. package/src/metadata-builder/builders/section-field-maps.ts +0 -18
  52. package/src/metadata-builder/metadata-executor.ts +0 -4
  53. package/src/metadata-builder/models.ts +0 -2
  54. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +3 -19
  55. package/src/metadata-builder/parsers/section-tool-arguments.ts +0 -18
  56. package/lib/src/brand-page-maker/composables/ai-chat/ui-actions.d.ts +0 -29
  57. package/lib/src/brand-page-maker/composables/ai-chat/ui-actions.js +0 -32
  58. package/lib/src/metadata-builder/builders/move-page-builder.d.ts +0 -32
  59. package/lib/src/metadata-builder/builders/move-page-builder.js +0 -137
  60. package/lib/src/metadata-builder/builders/nav-utils.d.ts +0 -26
  61. package/lib/src/metadata-builder/builders/nav-utils.js +0 -95
  62. package/lib/src/metadata-builder/builders/set-page-visibility-builder.d.ts +0 -23
  63. package/lib/src/metadata-builder/builders/set-page-visibility-builder.js +0 -83
  64. package/src/brand-page-maker/composables/ai-chat/ui-actions.test.ts +0 -80
  65. package/src/brand-page-maker/composables/ai-chat/ui-actions.ts +0 -58
  66. package/src/metadata-builder/builders/move-page-builder.ts +0 -227
  67. package/src/metadata-builder/builders/nav-utils.ts +0 -141
  68. package/src/metadata-builder/builders/set-page-visibility-builder.ts +0 -122
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-DfeZjykC.js";
2
+ import { _ as t } from "./lib-dQieWjFg.js";
3
3
  const i = {}, h = {
4
4
  width: "275",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-DfeZjykC.js";
2
+ import { _ as t } from "./lib-dQieWjFg.js";
3
3
  const i = {}, h = {
4
4
  width: "251",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-DfeZjykC.js";
2
+ import { _ as t } from "./lib-dQieWjFg.js";
3
3
  const i = {}, h = {
4
4
  width: "250",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-DfeZjykC.js";
2
+ import { _ as t } from "./lib-dQieWjFg.js";
3
3
  const i = {}, h = {
4
4
  width: "237",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-DfeZjykC.js";
2
+ import { _ as t } from "./lib-dQieWjFg.js";
3
3
  const i = {}, h = {
4
4
  width: "237",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-DfeZjykC.js";
2
+ import { _ as t } from "./lib-dQieWjFg.js";
3
3
  const i = {}, h = {
4
4
  width: "263",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-DfeZjykC.js";
2
+ import { _ as t } from "./lib-dQieWjFg.js";
3
3
  const i = {}, h = {
4
4
  width: "263",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-DfeZjykC.js";
2
+ import { _ as e } from "./lib-dQieWjFg.js";
3
3
  const M = {}, Z = {
4
4
  width: "242",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-DfeZjykC.js";
2
+ import { _ as e } from "./lib-dQieWjFg.js";
3
3
  const i = {}, M = {
4
4
  width: "231",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-DfeZjykC.js";
2
+ import { _ as e } from "./lib-dQieWjFg.js";
3
3
  const M = {}, Z = {
4
4
  width: "230",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-DfeZjykC.js";
2
+ import { _ as e } from "./lib-dQieWjFg.js";
3
3
  const i = {}, l = {
4
4
  width: "216",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-DfeZjykC.js";
2
+ import { _ as e } from "./lib-dQieWjFg.js";
3
3
  const i = {}, l = {
4
4
  width: "216",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as M } from "./lib-DfeZjykC.js";
2
+ import { _ as M } from "./lib-dQieWjFg.js";
3
3
  const Z = {}, e = {
4
4
  width: "232",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as M } from "./lib-DfeZjykC.js";
2
+ import { _ as M } from "./lib-dQieWjFg.js";
3
3
  const Z = {}, e = {
4
4
  width: "232",
5
5
  height: "36",
@@ -1,7 +1,5 @@
1
1
  import type { ComputedRef, Ref } from 'vue';
2
2
  import { type PlanItem } from '../../../clients/websocket-gateway-client';
3
- import type { AgentAction } from './ui-actions';
4
- export type { AgentAction };
5
3
  export interface BaseThreadMessage {
6
4
  id: string;
7
5
  role: 'user' | 'agent';
@@ -16,7 +14,6 @@ export interface TextThreadMessage extends BaseThreadMessage {
16
14
  initialMessage?: string;
17
15
  planItems?: PlanItem[];
18
16
  completedItemIds?: string[];
19
- actions?: AgentAction[];
20
17
  }
21
18
  export interface ImageThreadMessage extends BaseThreadMessage {
22
19
  type: 'image';
@@ -19,7 +19,6 @@ import { pickErrorMessage, pickWsErrorMessage, buildSubmitErrorReport, } from '.
19
19
  import raygunUtil from '../../../brand-page/utils/raygun.util';
20
20
  import { useEventTracker, AI_CTA_LOCATIONS } from '../useEventTracker';
21
21
  import { Logger } from '../../../metadata-builder/logger';
22
- import { partitionAiToolCalls } from './ui-actions';
23
22
  const BUILD_TIMEOUT_MS = 600000;
24
23
  const aiLogger = new Logger();
25
24
  let activeBuildWsClient = null;
@@ -373,7 +372,6 @@ export function useAiWebsiteBuilderChat() {
373
372
  let settled = false;
374
373
  let retryCount = 0;
375
374
  let lastAiMessage = null;
376
- let lastActions;
377
375
  let handler = null;
378
376
  // Vacant-slot queue scoped to this build session. Persists across
379
377
  // retry rounds so a failed CREATE returns to its planned slot via
@@ -393,173 +391,6 @@ export function useAiWebsiteBuilderChat() {
393
391
  finishBuild();
394
392
  reject(new Error('Build timeout'));
395
393
  }, BUILD_TIMEOUT_MS);
396
- // Transition the build to "done" exactly once (idempotent). Returns
397
- // false when already settled, so callers can early-return.
398
- const markDone = () => {
399
- if (settled)
400
- return false;
401
- settled = true;
402
- clearTimeout(timeout);
403
- finishBuild();
404
- return true;
405
- };
406
- // The single terminal path. Every resolve goes through here, so "where
407
- // does this Promise settle?" has one answer.
408
- const settle = (result) => {
409
- if (markDone())
410
- resolve(result);
411
- };
412
- // Streaming progress frames mutate the in-flight typing bubble and never
413
- // settle the build. Returns true when `msg` was a progress frame.
414
- const applyProgressUpdate = (msg) => {
415
- if (msg.type === 'chat_started' && msg.message) {
416
- withTypingMessage((m) => {
417
- m.initialMessage = msg.message;
418
- });
419
- return true;
420
- }
421
- if (msg.type === 'plan_announced' && msg.items) {
422
- withTypingMessage((m) => {
423
- if (!m.planItems)
424
- m.planItems = msg.items;
425
- });
426
- return true;
427
- }
428
- if (msg.type === 'item_complete' && msg.itemId) {
429
- const itemId = msg.itemId;
430
- withTypingMessage((m) => {
431
- if (!m.completedItemIds?.includes(itemId)) {
432
- m.completedItemIds = [...(m.completedItemIds ?? []), itemId];
433
- }
434
- });
435
- return true;
436
- }
437
- return false;
438
- };
439
- // Apply one round of build tool calls: settle on completion/failure, or
440
- // bump retryCount so the handler processes the next round's frame.
441
- const processServerResponse = async (aiResponse) => {
442
- const toolCalls = aiResponse.toolCalls ?? [];
443
- try {
444
- // No tool calls = build cycle complete (success, max retries, or no retryable sections).
445
- if (toolCalls.length === 0) {
446
- // Defence-in-depth: empty toolCalls AND empty message is a
447
- // degenerate response (graph reached END producing nothing). Never
448
- // show "Your website has been updated" — it hides real failures.
449
- const hasMeaningfulMessage = Boolean(aiResponse.message || lastAiMessage);
450
- if (!hasMeaningfulMessage) {
451
- raygunUtil.safeSendRaygunError(new Error('AI build server_response had empty toolCalls and empty message'), {
452
- tags: [
453
- 'feature:ai-website-builder',
454
- 'failure:degenerate_server_response',
455
- ],
456
- customData: {
457
- build_id: aiThreadStore.buildId ?? null,
458
- thread_id: aiThreadStore.threadId ?? null,
459
- },
460
- });
461
- settle({
462
- text: 'Something went wrong while updating your website. Please try again.',
463
- isError: true,
464
- });
465
- return;
466
- }
467
- // Mark settled BEFORE the await so concurrent frames are ignored.
468
- if (!markDone())
469
- return;
470
- await snapshotStore.refreshSnapshotAsync();
471
- resolve({
472
- text: aiResponse.message || lastAiMessage || '',
473
- actions: lastActions,
474
- });
475
- return;
476
- }
477
- // Tool calls present — apply locally and submit results back.
478
- const { toolResults, aiMessage } = await applyToolCallsAsync(aiResponse, userMessageId, vacantSlots);
479
- lastAiMessage = aiMessage;
480
- // Submit all tool results (success + failure) so the AI pipeline
481
- // persists ToolMessages matching every tool_call in the conversation.
482
- const failedResults = toolResults.filter((tr) => tr.success !== 'success');
483
- aiLogger.log('tool submission for results', {
484
- total: toolResults.length,
485
- failed: failedResults.length,
486
- failedResults,
487
- retryCount,
488
- });
489
- const submitResult = await client.submitToolResultsBuildAsync(aiThreadStore.threadId, {
490
- toolCalls: toolResults,
491
- agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
492
- connectionId,
493
- retryCount,
494
- userPlan,
495
- });
496
- if (failedResults.length === 0) {
497
- settle({
498
- text: aiMessage || 'Your website has been updated.',
499
- actions: lastActions,
500
- });
501
- return;
502
- }
503
- // Failed sections will retry: refresh buildId so the handler matches
504
- // the next round's frames, and count the round.
505
- if (submitResult.buildId) {
506
- aiThreadStore.buildId = submitResult.buildId;
507
- }
508
- retryCount += 1;
509
- }
510
- catch (err) {
511
- aiLogger.log('server_response apply error', { err });
512
- // Client-side apply failure (metadata builder, tool-call parser,
513
- // missing field, etc.) — the server thinks the build succeeded but we
514
- // couldn't materialise the changes. Surface it and report to Raygun
515
- // (the server didn't see this failure).
516
- raygunUtil.safeSendRaygunError(err instanceof Error
517
- ? err
518
- : new Error('AI apply failed: non-Error thrown'), {
519
- tags: [
520
- 'feature:ai-website-builder',
521
- 'failure:apply_error',
522
- 'stage:client_apply',
523
- ],
524
- customData: {
525
- thread_id: aiThreadStore.threadId ?? null,
526
- build_id: aiThreadStore.buildId ?? null,
527
- },
528
- });
529
- settle({
530
- text: "I couldn't apply those changes to your website. Please try again.",
531
- isError: true,
532
- });
533
- }
534
- };
535
- const handleBuildError = (msg) => {
536
- if (!markDone())
537
- return;
538
- // Backend already reports the underlying pipeline failure to Raygun
539
- // (with full traceback). We report the frontend's RECEIPT of the error
540
- // frame as a separate signal — distinct tag/grouping — for triangulation:
541
- // both sides → normal crash; frontend only → backend Raygun outage;
542
- // backend only → WS frame didn't reach client.
543
- const wsMsg = msg;
544
- raygunUtil.safeSendRaygunError(new Error(`AI build ${wsMsg.type}: ${wsMsg.code ?? wsMsg.error ?? 'unknown'}`), {
545
- tags: [
546
- 'feature:ai-website-builder',
547
- 'failure:websocket_error_received',
548
- `frame_type:${wsMsg.type}`,
549
- ],
550
- customData: {
551
- code: wsMsg.code ?? null,
552
- message: wsMsg.message ?? null,
553
- error: wsMsg.error ?? null,
554
- build_id: wsMsg.buildId ?? null,
555
- thread_id: aiThreadStore.threadId ?? null,
556
- },
557
- });
558
- resolve({ text: pickWsErrorMessage(msg), isError: true });
559
- };
560
- // Single dispatch point: ignore stale/already-settled frames, then route
561
- // by frame kind. Progress frames update the bubble; server_response and
562
- // error frames are the only ones that can settle the build.
563
394
  handler = (msg) => {
564
395
  if (settled)
565
396
  return;
@@ -567,32 +398,183 @@ export function useAiWebsiteBuilderChat() {
567
398
  if (msg.buildId !== aiThreadStore.buildId)
568
399
  return;
569
400
  if (isStopped.value) {
570
- settle({ text: null });
401
+ settled = true;
402
+ clearTimeout(timeout);
403
+ finishBuild();
404
+ resolve({ text: null });
405
+ return;
406
+ }
407
+ if (msg.type === 'chat_started' && msg.message) {
408
+ withTypingMessage((m) => {
409
+ m.initialMessage = msg.message;
410
+ });
411
+ return;
412
+ }
413
+ if (msg.type === 'plan_announced' && msg.items) {
414
+ withTypingMessage((m) => {
415
+ if (!m.planItems)
416
+ m.planItems = msg.items;
417
+ });
571
418
  return;
572
419
  }
573
- if (applyProgressUpdate(msg))
420
+ if (msg.type === 'item_complete' && msg.itemId) {
421
+ const itemId = msg.itemId;
422
+ withTypingMessage((m) => {
423
+ if (!m.completedItemIds?.includes(itemId)) {
424
+ m.completedItemIds = [
425
+ ...(m.completedItemIds ?? []),
426
+ itemId,
427
+ ];
428
+ }
429
+ });
574
430
  return;
431
+ }
575
432
  if (msg.type === 'server_response') {
576
- // CTA buttons (e.g. Upgrade) ride in toolCalls but are presentation,
577
- // not page edits. Split them out so only real mutations reach the
578
- // metadata-builder and the tool-result submission — a UI action must
579
- // never be answered with a tool result (it isn't in the agent's
580
- // conversation), or the next turn on the thread fails.
581
- const { mutationToolCalls, actions } = partitionAiToolCalls((msg.toolCalls || []));
582
- if (actions.length)
583
- lastActions = actions;
584
- void processServerResponse({
433
+ const toolCalls = (msg.toolCalls ||
434
+ []);
435
+ const aiResponse = {
585
436
  threadId: aiThreadStore.threadId || '',
586
- toolCalls: mutationToolCalls,
437
+ toolCalls,
587
438
  message: msg.message || null,
588
439
  type: 'tool_calls',
589
440
  totalPaidToolCalls: 0,
590
441
  timestamp: Date.now(),
591
- });
442
+ };
443
+ (async () => {
444
+ try {
445
+ // No tool calls = build cycle complete (success, max retries, or no retryable sections)
446
+ if (toolCalls.length === 0) {
447
+ // Defence-in-depth: a server_response with no toolCalls AND
448
+ // no message text is a degenerate response — it means the
449
+ // backend graph reached END without producing anything
450
+ // actionable. The server-side fix in stream_with_push now
451
+ // converts this to a build_error frame, but if a future
452
+ // regression slips through we must NOT show "Your website
453
+ // has been updated" — that hides real failures from users
454
+ // and from monitoring.
455
+ const hasMeaningfulMessage = Boolean(aiResponse.message || lastAiMessage);
456
+ if (!hasMeaningfulMessage) {
457
+ raygunUtil.safeSendRaygunError(new Error('AI build server_response had empty toolCalls and empty message'), {
458
+ tags: [
459
+ 'feature:ai-website-builder',
460
+ 'failure:degenerate_server_response',
461
+ ],
462
+ customData: {
463
+ build_id: aiThreadStore.buildId ?? null,
464
+ thread_id: aiThreadStore.threadId ?? null,
465
+ },
466
+ });
467
+ settled = true;
468
+ clearTimeout(timeout);
469
+ finishBuild();
470
+ resolve({
471
+ text: 'Something went wrong while updating your website. Please try again.',
472
+ isError: true,
473
+ });
474
+ return;
475
+ }
476
+ settled = true;
477
+ clearTimeout(timeout);
478
+ finishBuild();
479
+ resolve({
480
+ text: aiResponse.message || lastAiMessage || '',
481
+ });
482
+ return;
483
+ }
484
+ // Tool calls present — apply locally and submit results back
485
+ const { toolResults, aiMessage } = await applyToolCallsAsync(aiResponse, userMessageId, vacantSlots);
486
+ lastAiMessage = aiMessage;
487
+ // Submit all tool results (success + failure) so the AI pipeline
488
+ // persists ToolMessages matching every tool_call in the conversation.
489
+ const failedResults = toolResults.filter((tr) => tr.success !== 'success');
490
+ aiLogger.log('tool submission for results', {
491
+ total: toolResults.length,
492
+ failed: failedResults.length,
493
+ failedResults,
494
+ retryCount,
495
+ });
496
+ const submitResult = await client.submitToolResultsBuildAsync(aiThreadStore.threadId, {
497
+ toolCalls: toolResults,
498
+ agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
499
+ connectionId,
500
+ retryCount,
501
+ userPlan,
502
+ });
503
+ if (failedResults.length === 0) {
504
+ settled = true;
505
+ clearTimeout(timeout);
506
+ finishBuild();
507
+ resolve({
508
+ text: aiMessage || 'Your website has been updated.',
509
+ });
510
+ return;
511
+ }
512
+ // Update buildId so the handler matches subsequent WS messages
513
+ if (submitResult.buildId) {
514
+ aiThreadStore.buildId = submitResult.buildId;
515
+ }
516
+ // eslint-disable-next-line no-plusplus
517
+ retryCount++;
518
+ }
519
+ catch (err) {
520
+ aiLogger.log('server_response apply error', { err });
521
+ // Client-side apply failure (metadata builder, tool-call parser,
522
+ // missing field, etc.) — server thinks the build succeeded but we
523
+ // couldn't materialise the changes. Mark the chat bubble as an
524
+ // error so the user sees the real outcome, and report to Raygun
525
+ // so the failure is visible in monitoring (the server didn't see it).
526
+ raygunUtil.safeSendRaygunError(err instanceof Error
527
+ ? err
528
+ : new Error('AI apply failed: non-Error thrown'), {
529
+ tags: [
530
+ 'feature:ai-website-builder',
531
+ 'failure:apply_error',
532
+ 'stage:client_apply',
533
+ ],
534
+ customData: {
535
+ thread_id: aiThreadStore.threadId ?? null,
536
+ build_id: aiThreadStore.buildId ?? null,
537
+ },
538
+ });
539
+ settled = true;
540
+ clearTimeout(timeout);
541
+ finishBuild();
542
+ resolve({
543
+ text: "I couldn't apply those changes to your website. Please try again.",
544
+ isError: true,
545
+ });
546
+ }
547
+ })();
592
548
  return;
593
549
  }
594
550
  if (msg.type === 'server_error' || msg.type === 'build_error') {
595
- handleBuildError(msg);
551
+ settled = true;
552
+ clearTimeout(timeout);
553
+ finishBuild();
554
+ // Backend already reports the underlying pipeline failure to
555
+ // Raygun via its structured handler (stage:run_build /
556
+ // stage:section_builder / etc. with full Python traceback).
557
+ // We report the frontend's RECEIPT of the error frame as a
558
+ // separate signal — distinct tag, distinct grouping. This
559
+ // gives triangulation: both sides have entry → normal crash;
560
+ // frontend only → backend Raygun outage; backend only → WS
561
+ // frame didn't reach client (different bug shape).
562
+ const wsMsg = msg;
563
+ raygunUtil.safeSendRaygunError(new Error(`AI build ${wsMsg.type}: ${wsMsg.code ?? wsMsg.error ?? 'unknown'}`), {
564
+ tags: [
565
+ 'feature:ai-website-builder',
566
+ 'failure:websocket_error_received',
567
+ `frame_type:${wsMsg.type}`,
568
+ ],
569
+ customData: {
570
+ code: wsMsg.code ?? null,
571
+ message: wsMsg.message ?? null,
572
+ error: wsMsg.error ?? null,
573
+ build_id: wsMsg.buildId ?? null,
574
+ thread_id: aiThreadStore.threadId ?? null,
575
+ },
576
+ });
577
+ resolve({ text: pickWsErrorMessage(msg), isError: true });
596
578
  return;
597
579
  }
598
580
  }
@@ -601,10 +583,12 @@ export function useAiWebsiteBuilderChat() {
601
583
  err,
602
584
  type: msg.type,
603
585
  });
604
- settle({
605
- text: 'An error occurred during the build.',
606
- isError: true,
607
- });
586
+ if (!settled) {
587
+ settled = true;
588
+ clearTimeout(timeout);
589
+ finishBuild();
590
+ resolve({ text: 'An error occurred during the build.' });
591
+ }
608
592
  }
609
593
  };
610
594
  wsClient.onMessage(handler);
@@ -690,15 +674,6 @@ export function useAiWebsiteBuilderChat() {
690
674
  if (result.isError) {
691
675
  threadItems.value[messageIndex].isError = true;
692
676
  }
693
- // Attach agent-driven actions (e.g. upgrade CTA) only on successful
694
- // (non-error) responses. Suppressed for premium users client-side as
695
- // a defence-in-depth guard (server also vetoes via eligibility logic).
696
- if (!result.isError &&
697
- result.actions?.length &&
698
- !userStore.hasPremiumSubscription) {
699
- threadItems.value[messageIndex].actions =
700
- result.actions;
701
- }
702
677
  }
703
678
  else {
704
679
  threadItems.value[messageIndex].text =
@@ -785,11 +760,6 @@ export function useAiWebsiteBuilderChat() {
785
760
  planItems: msg.planItems ?? [],
786
761
  completedItemIds: msg.completedItemIds ?? [],
787
762
  }),
788
- ...(msg.type === 'text' &&
789
- !msg.isTyping &&
790
- msg.actions?.length && {
791
- actions: msg.actions,
792
- }),
793
763
  },
794
764
  };
795
765
  });
@@ -85,7 +85,6 @@ export declare const UPGRADE_CTA_LOCATIONS: {
85
85
  };
86
86
  export declare const AI_CTA_LOCATIONS: {
87
87
  AI_BUILDER_TAB: string;
88
- AI_BUILDER_CHAT_UPSELL: string;
89
88
  };
90
89
  export declare const TRIGGER_TYPE: {
91
90
  AUTOMATIC: string;
@@ -66,7 +66,6 @@ export const UPGRADE_CTA_LOCATIONS = {
66
66
  };
67
67
  export const AI_CTA_LOCATIONS = {
68
68
  AI_BUILDER_TAB: 'ai_builder_tab',
69
- AI_BUILDER_CHAT_UPSELL: 'ai_builder_chat_upsell',
70
69
  };
71
70
  export const TRIGGER_TYPE = {
72
71
  AUTOMATIC: 'automatic',
@@ -53,14 +53,14 @@ const PADDING_CONTROL_FIELDS = (groupId) => ({
53
53
  type: 'slider',
54
54
  title: contentSettingsTr(`topPaddingTitle`),
55
55
  minLength: 0,
56
- maxLength: 200,
56
+ maxLength: 100,
57
57
  groupId,
58
58
  },
59
59
  bottomPadding: {
60
60
  type: 'slider',
61
61
  title: contentSettingsTr(`bottomPaddingTitle`),
62
62
  minLength: 0,
63
- maxLength: 200,
63
+ maxLength: 100,
64
64
  groupId,
65
65
  },
66
66
  });