@braze/sanity-plugin-ai-translation 0.2.69 → 0.2.71

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/dist/index.js CHANGED
@@ -1039,6 +1039,57 @@ const fetchTranslationPrompts = async ({
1039
1039
  payload
1040
1040
  }), mainSourceDocumentId && updateActiveTranslationJob(mainSourceDocumentId, payload);
1041
1041
  }
1042
+ }, emitTelemetryEvent = (config, event) => {
1043
+ if (config?.onEvent)
1044
+ try {
1045
+ config.onEvent(event), devLog.debug("Telemetry event emitted", { eventName: event.name });
1046
+ } catch (error) {
1047
+ devLog.warn("Failed to emit telemetry event", {
1048
+ eventName: event.name,
1049
+ error: error instanceof Error ? error.message : String(error)
1050
+ });
1051
+ }
1052
+ }, emitTelemetryError = (config, bundle) => {
1053
+ if (config?.onError)
1054
+ try {
1055
+ config.onError(bundle), devLog.debug("Telemetry error emitted", {
1056
+ documentId: bundle.documentId,
1057
+ errorType: bundle.error.type
1058
+ });
1059
+ } catch (error) {
1060
+ devLog.warn("Failed to emit telemetry error", {
1061
+ documentId: bundle.documentId,
1062
+ error: error instanceof Error ? error.message : String(error)
1063
+ });
1064
+ }
1065
+ }, buildErrorBundle = (params) => {
1066
+ const durationMs = Date.now() - params.startTime;
1067
+ return {
1068
+ documentId: params.documentId,
1069
+ documentType: params.documentType,
1070
+ targetLanguage: params.targetLanguage,
1071
+ sourceLanguage: params.sourceLanguage,
1072
+ translationMode: params.translationMode,
1073
+ error: {
1074
+ message: params.error.message,
1075
+ stack: params.error.stack,
1076
+ type: params.errorType
1077
+ },
1078
+ debug: {
1079
+ originalDocument: params.originalDocument,
1080
+ serializedDocument: params.serializedDocument,
1081
+ openaiResponse: params.openaiResponse,
1082
+ translationPrompts: params.translationPrompts,
1083
+ validationErrors: params.validationErrors
1084
+ },
1085
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1086
+ durationMs
1087
+ };
1088
+ }, createTimestamp = () => (/* @__PURE__ */ new Date()).toISOString(), classifyError = (error, context) => {
1089
+ if (context)
1090
+ return context;
1091
+ const message = error.message.toLowerCase();
1092
+ return message.includes("network") || message.includes("fetch") || message.includes("timeout") || message.includes("econnrefused") || message.includes("enotfound") ? "network" : message.includes("openai") || message.includes("gpt") || message.includes("api error") ? "openai_api" : message.includes("validation") || message.includes("structure") || message.includes("invalid") ? "validation" : message.includes("deserialize") || message.includes("parse") ? "deserialization" : message.includes("serialize") || message.includes("xml") ? "serialization" : message.includes("agent") || message.includes("sanity") ? "sanity_agent" : "unknown";
1042
1093
  }, updateTranslationMetadata = async ({
1043
1094
  client,
1044
1095
  targetLanguage,
@@ -2231,16 +2282,18 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2231
2282
  translationPrompts,
2232
2283
  targetLanguage,
2233
2284
  openaiConfig,
2234
- previousError,
2235
- errorContext
2285
+ previousError: _previousError,
2286
+ errorContext,
2287
+ telemetryConfig
2236
2288
  }) => {
2237
- const sourceLanguage = sourceDocument[languageFieldName], logContext = {
2289
+ const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], logContext = {
2238
2290
  sourceId: sourceDocument._id,
2239
2291
  sourceLanguage
2240
2292
  };
2293
+ let serializedDocument, openaiResponseContent;
2241
2294
  try {
2242
2295
  const sourceText = serializeDocument(sourceDocument);
2243
- devLog.info("Serialized document for OpenAI", {
2296
+ serializedDocument = sourceText, devLog.info("Serialized document for OpenAI", {
2244
2297
  ...logContext,
2245
2298
  characterCount: sourceText.length,
2246
2299
  estimatedTokens: Math.ceil(sourceText.length / 4),
@@ -2263,7 +2316,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2263
2316
  model: openaiConfig.model,
2264
2317
  reasoningEffort: openaiConfig.reasoningEffort
2265
2318
  });
2266
- devLog.info("OpenAI translation response received", {
2319
+ openaiResponseContent = response.content, devLog.info("OpenAI translation response received", {
2267
2320
  ...logContext,
2268
2321
  tokensUsed: response.tokensUsed,
2269
2322
  cost: response.cost,
@@ -2301,9 +2354,35 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2301
2354
  })), setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating_with_openai", {
2302
2355
  tokensUsed: response.tokensUsed.total,
2303
2356
  estimatedCost: response.cost
2304
- }), result;
2357
+ }), {
2358
+ document: result,
2359
+ tokensUsed: response.tokensUsed.total,
2360
+ cost: response.cost
2361
+ };
2305
2362
  } catch (error) {
2306
- throw devLog.error("OpenAI translation failed", error instanceof Error ? error : new Error(String(error)), logContext), error;
2363
+ const err = error instanceof Error ? error : new Error(String(error));
2364
+ throw devLog.error("OpenAI translation failed", err, logContext), emitTelemetryError(
2365
+ telemetryConfig,
2366
+ buildErrorBundle({
2367
+ documentId: sourceDocument._id,
2368
+ documentType: sourceDocument._type,
2369
+ targetLanguage,
2370
+ sourceLanguage,
2371
+ translationMode: "openai-only",
2372
+ error: err,
2373
+ errorType: classifyError(err, "openai_api"),
2374
+ startTime,
2375
+ originalDocument: sourceDocument,
2376
+ serializedDocument,
2377
+ openaiResponse: openaiResponseContent,
2378
+ translationPrompts: {
2379
+ globalPrompt: translationPrompts.globalPrompt,
2380
+ localePrompt: translationPrompts.localeInstructions[targetLanguage]?.prompt,
2381
+ protectedPhrases: translationPrompts.protectedPhrases,
2382
+ glossaryTerms: translationPrompts.localeInstructions[targetLanguage]?.glossaryTerms ? [translationPrompts.localeInstructions[targetLanguage].glossaryTerms] : void 0
2383
+ }
2384
+ })
2385
+ ), error;
2307
2386
  }
2308
2387
  }, buildTranslationPrompt$1 = ({
2309
2388
  sourceText,
@@ -2539,9 +2618,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2539
2618
  releaseId,
2540
2619
  excludeFields = [],
2541
2620
  replaceExisting = !1,
2542
- mainSourceDocumentId
2621
+ mainSourceDocumentId,
2622
+ telemetryConfig
2543
2623
  }) => {
2544
- const safeExcludeFields = excludeFields.filter((field) => field !== languageFieldName);
2624
+ const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], safeExcludeFields = excludeFields.filter((field) => field !== languageFieldName);
2545
2625
  excludeFields.length !== safeExcludeFields.length && devLog.warn("Language field was in excludeFields - removing it to prevent translation errors", {
2546
2626
  documentId: sourceDocument._id,
2547
2627
  languageFieldName,
@@ -2580,29 +2660,55 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2580
2660
  documentId: sourceDocument._id,
2581
2661
  documentType: sourceDocument._type
2582
2662
  }), documentToTranslate = filteredDocument;
2583
- let translatedDoc, previousError = null;
2663
+ let translatedDoc, translationMetrics = {}, previousError = null;
2584
2664
  const performTranslation = async (errorContext) => {
2585
2665
  switch (translationMode) {
2586
- case "sanity-only":
2587
- return setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating", void 0, mainSourceDocumentId), await getSanityAgentTranslation({
2588
- sourceDocument: documentToTranslate,
2589
- targetLanguage,
2590
- languageFieldName,
2591
- schemaId,
2592
- client,
2593
- translationPrompts
2594
- });
2666
+ case "sanity-only": {
2667
+ setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating", void 0, mainSourceDocumentId);
2668
+ try {
2669
+ return { document: await getSanityAgentTranslation({
2670
+ sourceDocument: documentToTranslate,
2671
+ targetLanguage,
2672
+ languageFieldName,
2673
+ schemaId,
2674
+ client,
2675
+ translationPrompts
2676
+ }) };
2677
+ } catch (error) {
2678
+ const err = error instanceof Error ? error : new Error(String(error));
2679
+ throw emitTelemetryError(
2680
+ telemetryConfig,
2681
+ buildErrorBundle({
2682
+ documentId: sourceDocument._id,
2683
+ documentType: sourceDocument._type,
2684
+ targetLanguage,
2685
+ sourceLanguage,
2686
+ translationMode: "sanity-only",
2687
+ error: err,
2688
+ errorType: classifyError(err, "sanity_agent"),
2689
+ startTime,
2690
+ originalDocument: sourceDocument,
2691
+ translationPrompts: {
2692
+ globalPrompt: translationPrompts.globalPrompt,
2693
+ localePrompt: translationPrompts.localeInstructions[targetLanguage]?.prompt,
2694
+ protectedPhrases: translationPrompts.protectedPhrases
2695
+ }
2696
+ })
2697
+ ), error;
2698
+ }
2699
+ }
2595
2700
  case "openai-only": {
2596
2701
  if (!openaiConfig?.enabled || !openaiConfig?.apiKey)
2597
2702
  throw new Error("OpenAI is not configured but openai-only mode was selected");
2598
- return setJobStatus(
2703
+ setJobStatus(
2599
2704
  dispatch,
2600
2705
  sourceDocument._id,
2601
2706
  targetLanguage,
2602
2707
  "translating_with_openai",
2603
2708
  void 0,
2604
2709
  mainSourceDocumentId
2605
- ), await getOpenAITranslation({
2710
+ );
2711
+ const result = await getOpenAITranslation({
2606
2712
  dispatch,
2607
2713
  sourceDocument: documentToTranslate,
2608
2714
  targetLanguage,
@@ -2610,14 +2716,23 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2610
2716
  translationPrompts,
2611
2717
  openaiConfig,
2612
2718
  previousError: errorContext && previousError ? previousError : void 0,
2613
- errorContext
2719
+ errorContext,
2720
+ telemetryConfig
2614
2721
  });
2722
+ return result ? {
2723
+ document: result.document,
2724
+ tokensUsed: result.tokensUsed,
2725
+ cost: result.cost
2726
+ } : { document: null };
2615
2727
  }
2616
2728
  default:
2617
2729
  throw new Error(`Invalid translation mode: ${translationMode}`);
2618
2730
  }
2619
- };
2620
- if (translatedDoc = await performTranslation(), !translatedDoc)
2731
+ }, translationResult = await performTranslation();
2732
+ if (translatedDoc = translationResult.document, translationMetrics = {
2733
+ tokensUsed: translationResult.tokensUsed,
2734
+ cost: translationResult.cost
2735
+ }, !translatedDoc)
2621
2736
  return null;
2622
2737
  let transformedResult;
2623
2738
  Object.keys(originalStructure).length > 0 ? (devLog.debug("Restoring field transforms after translation", {
@@ -2680,9 +2795,13 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2680
2795
  targetLanguage,
2681
2796
  error: error instanceof Error ? error.message : String(error)
2682
2797
  }), previousError = error instanceof Error ? error : new Error(String(error));
2683
- const errorContext = `Previous translation attempt failed with Sanity validation error: ${previousError.message}. Please ensure the translated document structure exactly matches the source document structure.`;
2684
- if (translatedDoc = await performTranslation(errorContext), !translatedDoc)
2798
+ const errorContext = `Previous translation attempt failed with Sanity validation error: ${previousError.message}. Please ensure the translated document structure exactly matches the source document structure.`, retryResult = await performTranslation(errorContext);
2799
+ if (translatedDoc = retryResult.document, !translatedDoc)
2685
2800
  throw new Error("Translation retry failed: no document returned");
2801
+ translationMetrics = {
2802
+ tokensUsed: retryResult.tokensUsed,
2803
+ cost: retryResult.cost
2804
+ };
2686
2805
  let retryTransformedResult;
2687
2806
  Object.keys(originalStructure).length > 0 ? retryTransformedResult = restoreTransforms({
2688
2807
  translatedDocument: translatedDoc,
@@ -2713,11 +2832,19 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2713
2832
  isRetry: !0
2714
2833
  }
2715
2834
  }
2716
- ), retryNewDoc;
2835
+ ), {
2836
+ document: retryNewDoc,
2837
+ tokensUsed: translationMetrics.tokensUsed,
2838
+ cost: translationMetrics.cost
2839
+ };
2717
2840
  }
2718
2841
  throw error;
2719
2842
  }
2720
- return newDoc;
2843
+ return {
2844
+ document: newDoc,
2845
+ tokensUsed: translationMetrics.tokensUsed,
2846
+ cost: translationMetrics.cost
2847
+ };
2721
2848
  }, translateDocument = async ({
2722
2849
  dispatch,
2723
2850
  client,
@@ -2732,9 +2859,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2732
2859
  releaseId,
2733
2860
  excludeFields,
2734
2861
  replaceExisting,
2735
- mainSourceDocumentId
2862
+ mainSourceDocumentId,
2863
+ telemetryConfig
2736
2864
  }) => {
2737
- const logContext = {
2865
+ const jobStartTime = Date.now(), logContext = {
2738
2866
  documentId: sourceDocument._id,
2739
2867
  targetLanguage,
2740
2868
  schemaId,
@@ -2746,7 +2874,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2746
2874
  client,
2747
2875
  sourceDocument,
2748
2876
  sourceLanguage
2749
- }), existingTranslationId = getExistingTranslationId(translationMetadata, targetLanguage), newDocument = await handleTranslation({
2877
+ }), existingTranslationId = getExistingTranslationId(translationMetadata, targetLanguage), translationResult = await handleTranslation({
2750
2878
  dispatch,
2751
2879
  client,
2752
2880
  schemaId,
@@ -2760,8 +2888,9 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2760
2888
  releaseId,
2761
2889
  excludeFields,
2762
2890
  replaceExisting,
2763
- mainSourceDocumentId
2764
- });
2891
+ mainSourceDocumentId,
2892
+ telemetryConfig
2893
+ }), newDocument = translationResult?.document;
2765
2894
  if (!newDocument) {
2766
2895
  devLog.error("Translation failed: no document returned", new Error("Translation returned null"), logContext), setJobStatus(
2767
2896
  dispatch,
@@ -2798,12 +2927,27 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2798
2927
  targetLanguage,
2799
2928
  "completed",
2800
2929
  {
2801
- targetId: newDocument._id
2930
+ targetId: newDocument._id,
2931
+ tokensUsed: translationResult?.tokensUsed,
2932
+ estimatedCost: translationResult?.cost
2802
2933
  },
2803
2934
  mainSourceDocumentId
2804
2935
  ), devLog.info("Translation completed successfully", {
2805
2936
  ...logContext,
2806
2937
  newDocumentId: newDocument._id
2938
+ }), emitTelemetryEvent(telemetryConfig, {
2939
+ name: "translation.job_completed",
2940
+ timestamp: createTimestamp(),
2941
+ data: {
2942
+ documentId: sourceDocument._id,
2943
+ documentType: sourceDocument._type,
2944
+ sourceLanguage,
2945
+ targetLanguage,
2946
+ translationMode,
2947
+ durationMs: Date.now() - jobStartTime,
2948
+ tokensUsed: translationResult?.tokensUsed,
2949
+ estimatedCost: translationResult?.cost
2950
+ }
2807
2951
  }), devLog.timeEnd("translateDocument"), devLog.groupEnd(), newDocument._id;
2808
2952
  } catch (error) {
2809
2953
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -2831,9 +2975,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2831
2975
  translationMode,
2832
2976
  openaiConfig,
2833
2977
  excludeFields,
2834
- releaseConfig
2978
+ releaseConfig,
2979
+ telemetryConfig
2835
2980
  }) => {
2836
- const logContext = {
2981
+ const batchStartTime = Date.now(), logContext = {
2837
2982
  documentId: sourceDocument._id,
2838
2983
  documentRev: sourceDocument._rev,
2839
2984
  targetLanguages,
@@ -2847,7 +2992,19 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2847
2992
  const sourceLanguage = sourceDocument[languageFieldName];
2848
2993
  if (!sourceLanguage)
2849
2994
  throw devLog.error("Source language not found"), new Error("Source language not found");
2850
- if (maxConcurrentTranslationJobs && translationSemaphore.updatePermits(maxConcurrentTranslationJobs), dispatch({
2995
+ if (emitTelemetryEvent(telemetryConfig, {
2996
+ name: "translation.batch_started",
2997
+ timestamp: createTimestamp(),
2998
+ data: {
2999
+ documentId: sourceDocument._id,
3000
+ documentType: sourceDocument._type,
3001
+ sourceLanguage,
3002
+ targetLanguages,
3003
+ translationMode,
3004
+ referenceCount: references.length,
3005
+ releaseMode: releaseConfig.mode
3006
+ }
3007
+ }), maxConcurrentTranslationJobs && translationSemaphore.updatePermits(maxConcurrentTranslationJobs), dispatch({
2851
3008
  type: "SET_OVERALL_STATUS",
2852
3009
  payload: "translating"
2853
3010
  }), releaseConfig.mode === "existing" && releaseConfig.existingReleaseId)
@@ -2940,7 +3097,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2940
3097
  excludeFields,
2941
3098
  replaceExisting: releaseConfig.replaceExisting,
2942
3099
  // Pass main source document ID for active translations tracking
2943
- mainSourceDocumentId: sourceDocument._id
3100
+ mainSourceDocumentId: sourceDocument._id,
3101
+ telemetryConfig
2944
3102
  });
2945
3103
  versionedTargetDocumentId && versionedTargetDocumentIds.push(versionedTargetDocumentId);
2946
3104
  }
@@ -2965,6 +3123,20 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2965
3123
  failed,
2966
3124
  skipped: skippedCount,
2967
3125
  targetDocuments: versionedTargetDocumentIds.length
3126
+ }), emitTelemetryEvent(telemetryConfig, {
3127
+ name: "translation.batch_completed",
3128
+ timestamp: createTimestamp(),
3129
+ data: {
3130
+ documentId: sourceDocument._id,
3131
+ documentType: sourceDocument._type,
3132
+ sourceLanguage,
3133
+ targetLanguages,
3134
+ translationMode,
3135
+ succeeded,
3136
+ failed,
3137
+ skipped: skippedCount,
3138
+ durationMs: Date.now() - batchStartTime
3139
+ }
2968
3140
  }), dispatch({
2969
3141
  type: "SET_OVERALL_STATUS",
2970
3142
  payload: "linking_references"
@@ -3110,7 +3282,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3110
3282
  // Use the override release ID if provided (from conflict context),
3111
3283
  // otherwise use the current releaseConfig value
3112
3284
  existingReleaseId: overrideReleaseId ?? releaseConfig.existingReleaseId
3113
- }
3285
+ },
3286
+ telemetryConfig: config.telemetry
3114
3287
  });
3115
3288
  },
3116
3289
  [
@@ -3124,6 +3297,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3124
3297
  config.maxReferenceDepth,
3125
3298
  config.openai,
3126
3299
  config.excludeFields,
3300
+ config.telemetry,
3127
3301
  filteredReferences,
3128
3302
  translationMode,
3129
3303
  releaseConfig
@@ -3343,7 +3517,9 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3343
3517
  }), error;
3344
3518
  }
3345
3519
  }, ReconcileReferencesButton = ({ sourceLanguage }) => {
3346
- const { dispatch, releaseConfig, overallStatus, reconcilingStatus, reconciliationProgress, config } = useTranslationContext(), client = useClient({ apiVersion: "vX" }), [documentCount, setDocumentCount] = useState(null), [isLoadingCount, setIsLoadingCount] = useState(!1), releaseId = releaseConfig.existingReleaseId, languageFieldName = config?.languageFieldName ?? "language";
3520
+ const { releaseConfig, overallStatus, config } = useTranslationContext(), client = useClient({ apiVersion: "vX" }), [documentCount, setDocumentCount] = useState(null), [isLoadingCount, setIsLoadingCount] = useState(!1), [reconcilingStatus, setReconcilingStatus] = useState("idle"), [reconciliationProgress, setReconciliationProgress] = useState(
3521
+ void 0
3522
+ ), releaseId = releaseConfig.existingReleaseId, languageFieldName = config?.languageFieldName ?? "language";
3347
3523
  useEffect(() => {
3348
3524
  if (!releaseId || releaseConfig.mode !== "existing") {
3349
3525
  setDocumentCount(null);
@@ -3378,7 +3554,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3378
3554
  releaseId,
3379
3555
  sourceLanguage,
3380
3556
  component: "ReconcileReferencesButton"
3381
- }), dispatch({ type: "SET_RECONCILING_STATUS", payload: "reconciling" });
3557
+ }), setReconcilingStatus("reconciling");
3382
3558
  try {
3383
3559
  const result = await reconcileReleaseReferences({
3384
3560
  client,
@@ -3386,27 +3562,24 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3386
3562
  sourceLanguage,
3387
3563
  languageFieldName,
3388
3564
  onProgress: (current, total) => {
3389
- dispatch({
3390
- type: "SET_RECONCILIATION_PROGRESS",
3391
- payload: { current, total }
3392
- });
3565
+ setReconciliationProgress({ current, total });
3393
3566
  }
3394
3567
  });
3395
3568
  devLog.info("Reference reconciliation completed", {
3396
3569
  releaseId,
3397
3570
  ...result,
3398
3571
  component: "ReconcileReferencesButton"
3399
- }), dispatch({ type: "SET_RECONCILING_STATUS", payload: "completed" }), setTimeout(() => {
3400
- dispatch({ type: "SET_RECONCILING_STATUS", payload: "idle" });
3572
+ }), setReconcilingStatus("completed"), setTimeout(() => {
3573
+ setReconcilingStatus("idle"), setReconciliationProgress(void 0);
3401
3574
  }, 2e3);
3402
3575
  } catch (error) {
3403
3576
  devLog.error("Reference reconciliation failed", error instanceof Error ? error : new Error(String(error)), {
3404
3577
  releaseId,
3405
3578
  component: "ReconcileReferencesButton"
3406
- }), dispatch({ type: "SET_RECONCILING_STATUS", payload: "error" });
3579
+ }), setReconcilingStatus("error"), setReconciliationProgress(void 0);
3407
3580
  }
3408
3581
  }
3409
- }, [releaseId, sourceLanguage, languageFieldName, client, dispatch]);
3582
+ }, [releaseId, sourceLanguage, languageFieldName, client]);
3410
3583
  if (releaseConfig.mode !== "existing" || !releaseId)
3411
3584
  return null;
3412
3585
  const isTranslating = overallStatus === "translating" || overallStatus === "linking_references", isReconciling = reconcilingStatus === "reconciling";
@@ -3583,7 +3756,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3583
3756
  }
3584
3757
  )
3585
3758
  ] }),
3586
- releaseConfig.existingReleaseId && sourceLanguage && showReconcileUI && /* @__PURE__ */ jsx(ReconcileReferencesButton, { sourceLanguage })
3759
+ isSelected && sourceLanguage && showReconcileUI && /* @__PURE__ */ jsx(ReconcileReferencesButton, { sourceLanguage })
3587
3760
  ] })
3588
3761
  },
3589
3762
  release._id