@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.
- package/lib/src/{CreatedOnBrandCrowd-de-de-X6T8_3JP.js → CreatedOnBrandCrowd-de-de-Zptj2O2O.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-BC59Ss9F.js → CreatedOnBrandCrowd-en-us-Bxmo2Ps9.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-BF1QReGi.js → CreatedOnBrandCrowd-es-es-uytkzFdZ.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-BhI-30CT.js → CreatedOnBrandCrowd-fr-ca-26tDXsvZ.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-BhI-30CT.js → CreatedOnBrandCrowd-fr-fr-26tDXsvZ.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-BJQkhGhh.js → CreatedOnBrandCrowd-pt-br-DuNRkCU5.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-BJQkhGhh.js → CreatedOnBrandCrowd-pt-pt-DuNRkCU5.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-pQfxLfUx.js → CreatedOnDcom-de-de-D_aekURi.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-DQWSlN-g.js → CreatedOnDcom-en-us-CD3RyS8m.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-B5oZRbLp.js → CreatedOnDcom-es-es-BXCP18xT.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-BBhSUN6J.js → CreatedOnDcom-fr-ca-BN04YN4o.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-CXc2On48.js → CreatedOnDcom-fr-fr-B8-sowys.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-B5AmbldX.js → CreatedOnDcom-pt-br-C0MTfZ_l.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-Df_mxV0U.js → CreatedOnDcom-pt-pt-BamTt9zq.js} +1 -1
- package/lib/src/brand-page-maker/composables/ai-chat/types.d.ts +0 -3
- package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +171 -201
- package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +0 -1
- package/lib/src/brand-page-maker/composables/useEventTracker.js +0 -1
- package/lib/src/brand-page-maker/constants/config-settings-constant.js +2 -2
- package/lib/src/css/library.brand-page-brandCrowd.min.css +27 -36
- package/lib/src/css/library.brand-page-brandPage.min.css +27 -36
- package/lib/src/css/library.brand-page-designCom.min.css +27 -36
- package/lib/src/css/library.brand-page-preview.min.css +27 -36
- package/lib/src/index.mjs +1 -1
- package/lib/src/{lib-DfeZjykC.js → lib-dQieWjFg.js} +20388 -20679
- package/lib/src/metadata-builder/ai-metadata-executor.js +1 -24
- package/lib/src/metadata-builder/builders/reorder-pages-builder.d.ts +13 -0
- package/lib/src/metadata-builder/builders/reorder-pages-builder.js +49 -0
- package/lib/src/metadata-builder/builders/section-builders/move-section-builder.d.ts +1 -13
- package/lib/src/metadata-builder/builders/section-builders/move-section-builder.js +30 -40
- package/lib/src/metadata-builder/builders/section-field-maps.js +0 -17
- package/lib/src/metadata-builder/metadata-executor.js +0 -4
- package/lib/src/metadata-builder/models.d.ts +0 -2
- package/lib/src/metadata-builder/models.js +0 -2
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +3 -18
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +0 -2
- package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +0 -18
- package/package.json +1 -1
- package/src/brand-page/components/shop/Shop.vue +1 -0
- package/src/brand-page-maker/components/ai-text-edit/AiActionButtons.vue +12 -27
- package/src/brand-page-maker/components/ai-text-edit/AiChatbox.vue +2 -3
- package/src/brand-page-maker/components/ai-text-edit/TextBubble.vue +7 -7
- package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +1 -21
- package/src/brand-page-maker/composables/ai-chat/types.ts +0 -6
- package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +255 -305
- package/src/brand-page-maker/composables/useEventTracker.ts +0 -1
- package/src/brand-page-maker/constants/config-settings-constant.ts +2 -2
- package/src/metadata-builder/ai-metadata-executor.ts +1 -31
- package/src/metadata-builder/builders/reorder-pages-builder.ts +80 -0
- package/src/metadata-builder/builders/section-builders/move-section-builder.ts +35 -58
- package/src/metadata-builder/builders/section-field-maps.ts +0 -18
- package/src/metadata-builder/metadata-executor.ts +0 -4
- package/src/metadata-builder/models.ts +0 -2
- package/src/metadata-builder/parsers/ai-toolcall-parser.ts +3 -19
- package/src/metadata-builder/parsers/section-tool-arguments.ts +0 -18
- package/lib/src/brand-page-maker/composables/ai-chat/ui-actions.d.ts +0 -29
- package/lib/src/brand-page-maker/composables/ai-chat/ui-actions.js +0 -32
- package/lib/src/metadata-builder/builders/move-page-builder.d.ts +0 -32
- package/lib/src/metadata-builder/builders/move-page-builder.js +0 -137
- package/lib/src/metadata-builder/builders/nav-utils.d.ts +0 -26
- package/lib/src/metadata-builder/builders/nav-utils.js +0 -95
- package/lib/src/metadata-builder/builders/set-page-visibility-builder.d.ts +0 -23
- package/lib/src/metadata-builder/builders/set-page-visibility-builder.js +0 -83
- package/src/brand-page-maker/composables/ai-chat/ui-actions.test.ts +0 -80
- package/src/brand-page-maker/composables/ai-chat/ui-actions.ts +0 -58
- package/src/metadata-builder/builders/move-page-builder.ts +0 -227
- package/src/metadata-builder/builders/nav-utils.ts +0 -141
- package/src/metadata-builder/builders/set-page-visibility-builder.ts +0 -122
|
@@ -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
|
-
|
|
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 (
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
});
|
|
@@ -53,14 +53,14 @@ const PADDING_CONTROL_FIELDS = (groupId) => ({
|
|
|
53
53
|
type: 'slider',
|
|
54
54
|
title: contentSettingsTr(`topPaddingTitle`),
|
|
55
55
|
minLength: 0,
|
|
56
|
-
maxLength:
|
|
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:
|
|
63
|
+
maxLength: 100,
|
|
64
64
|
groupId,
|
|
65
65
|
},
|
|
66
66
|
});
|