diff --git a/AgentforceSDK-ReactNative-Bridge/android/build.gradle b/AgentforceSDK-ReactNative-Bridge/android/build.gradle index b2c059e..a492575 100644 --- a/AgentforceSDK-ReactNative-Bridge/android/build.gradle +++ b/AgentforceSDK-ReactNative-Bridge/android/build.gradle @@ -60,8 +60,8 @@ repositories { dependencies { coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.1.5" implementation "com.facebook.react:react-android" - api "com.salesforce.android.agentforcesdk:agentforce-sdk:15.165.6" - api "com.salesforce.android.agentforcesdk:agentforce-sdk-voice:15.165.6" + api "com.salesforce.android.agentforcesdk:agentforce-sdk:15.195.3" + api "com.salesforce.android.agentforcesdk:agentforce-sdk-voice:15.195.3" // compileOnly: host app adds this for Employee Agent. Bridge compiles; host provides at runtime. compileOnly "com.salesforce.mobilesdk:SalesforceReact:13.1.1" diff --git a/AgentforceSDK-ReactNative-Bridge/ios/ReactNativeAgentforce.podspec b/AgentforceSDK-ReactNative-Bridge/ios/ReactNativeAgentforce.podspec index 662e80b..5a99b7a 100644 --- a/AgentforceSDK-ReactNative-Bridge/ios/ReactNativeAgentforce.podspec +++ b/AgentforceSDK-ReactNative-Bridge/ios/ReactNativeAgentforce.podspec @@ -33,12 +33,12 @@ Pod::Spec.new do |s| "ServiceAgent/**/*.{h,m,swift}" ] core.dependency "React-Core" - core.dependency "AgentforceSDK", "19.29.3" - core.dependency "AgentforceService", "6.17.3" - core.dependency "AgentforceVoice", "2.10.8" - # AgentforceSDK 17.31.6 (262.1) depends on SharedUI ('~> 1'); declare it so - # this target can resolve the design-system module. - core.dependency "SharedUI", "1.3.1" + core.dependency "AgentforceSDK", "264.0.0" + core.dependency "AgentforceService", "264.0.0" + core.dependency "AgentforceVoice", "264.0.0" + # AgentforceSDK 264.0.0 requires SharedUI >= 1.5.11; declare a compatible + # published binary so this target can resolve the design-system module. + core.dependency "SharedUI", "1.6.10" end # Optional: add for Employee Agent auth (OAuth via Salesforce Mobile SDK). diff --git a/AgentforceSDK-ReactNative-Bridge/package.json b/AgentforceSDK-ReactNative-Bridge/package.json index 5e10806..718546d 100644 --- a/AgentforceSDK-ReactNative-Bridge/package.json +++ b/AgentforceSDK-ReactNative-Bridge/package.json @@ -1,6 +1,6 @@ { "name": "@salesforce/react-native-agentforce", - "version": "0.7.3-patch2", + "version": "0.8.0", "description": "Agentforce React Native bridge: JS API and native module for Service Agent and Employee Agent (auth bridge included; host app adds Mobile SDK).", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/ios/Podfile.common.rb b/ios/Podfile.common.rb index 98b4ac8..c84ba19 100644 --- a/ios/Podfile.common.rb +++ b/ios/Podfile.common.rb @@ -20,31 +20,36 @@ def shared_pods :app_path => "#{Pod::Config.instance.installation_root}/.." ) - pod 'AgentforceSDK', '19.29.3' + pod 'AgentforceSDK', '264.0.0' # AgentforceSDK declares a broad `~> 6` service dependency; pin explicitly to - # the exact 262.3 service binary this SDK build was tested against. - pod 'AgentforceService', '6.17.3' - pod 'AgentforceVoice', '2.10.8' - pod 'Messaging-InApp-Core', '> 1.10.0' + # the exact 264.0 service binary selected for this release. + pod 'AgentforceService', '264.0.0' + pod 'AgentforceVoice', '264.0.0' + pod 'Messaging-InApp-Core', '~> 1.13.0' # Messaging-Multimedia-Core vends SMIMultimediaCore.framework, which # AgentforceVoice links at runtime (@rpath/SMIMultimediaCore.framework). - # Keep this explicit so the framework is embedded, and align it with the - # AgentforceVoice 2.10.8 podspec requirement. - pod 'Messaging-Multimedia-Core', '~> 1.12.0' + # The AgentforceVoice podspec resolved from this Specs source declares only + # AgentforceService/SalesforceLogging/SalesforceNetwork (not the Binary subspec + # that the iOS SDK uses), so this transitive multimedia dep isn't pulled in and + # the framework is never embedded — surfacing as a dyld "Library not loaded" + # crash once Service Agent voice is enabled. Declare it explicitly so it's + # installed and embedded. + # Keep the Messaging family on one patch line. Multimedia-Core 1.13.x consumes + # the prebuilt LiveKit 2.16.x XCFramework declared below. + pod 'Messaging-Multimedia-Core', '~> 1.13.0' # JWTKit is required by AgentforceService but not resolved automatically pod 'JWTKit' - # SharedUI provides the design-system module (Colors/Fonts/Dimensions) that - # AgentforceSDK 17.31.6 (262.1) depends on ('SharedUI', '~> 1'). Pin to the - # exact flat spec the iOS SDK ships with; SLDSIcons is held at 1.2.2 to avoid + # AgentforceSDK 264.0.0 requires SharedUI >= 1.5.11. Pin a compatible + # published binary; SLDSIcons is held at 1.2.2 to avoid # the "Multiple commands produce SLDSIcons.framework" collision. - pod 'SharedUI', '1.3.1' + pod 'SharedUI', '1.6.10' pod 'SLDSIcons', '1.2.2' - # LiveKit is needed for both Service and Employee agents. - # Messaging-Multimedia-Core 1.12.x requires the compatible 2.11.x line. - pod 'LiveKitClient', '2.11.0' # Required so CocoaPods looks in the correct source location + # LiveKit is needed for both Service and Employee agents. Use the same prebuilt + # pod and minor line required by Messaging-Multimedia-Core 1.13.x. + pod 'LiveKitClient-XCFramework', '~> 2.16.0' # AgentforceService links to Crypto.framework at runtime; SwiftCrypto provides it (avoids dyld "Library not loaded: Crypto.framework"). pod 'SwiftCrypto', '~> 3.15' @@ -66,13 +71,6 @@ def pod.build_type Pod::BuildType.static_library end end - - # AgentforceVoice.xcframework links against @rpath/LiveKit.framework/LiveKit, - # but CocoaPods builds LiveKitClient as LiveKitClient.framework by default. - # Override module_name so it produces LiveKit.framework instead. - if pod.name == 'LiveKitClient' - pod.root_spec.attributes_hash['module_name'] = 'LiveKit' - end end end diff --git a/package-lock.json b/package-lock.json index 695e6b2..ecd1110 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ }, "AgentforceSDK-ReactNative-Bridge": { "name": "@salesforce/react-native-agentforce", - "version": "0.7.3-patch2", + "version": "0.8.0", "license": "Apache-2.0", "devDependencies": { "@types/node": "^20.11.0", diff --git a/patches/react-native+0.74.7.patch b/patches/react-native+0.74.7.patch index c3d9c43..d8e8239 100644 --- a/patches/react-native+0.74.7.patch +++ b/patches/react-native+0.74.7.patch @@ -1,3 +1,136 @@ +diff --git a/node_modules/react-native/sdks/hermes-engine/hermes-utils.rb b/node_modules/react-native/sdks/hermes-engine/hermes-utils.rb +index c0c7613..eda20e9 100644 +--- a/node_modules/react-native/sdks/hermes-engine/hermes-utils.rb ++++ b/node_modules/react-native/sdks/hermes-engine/hermes-utils.rb +@@ -5,6 +5,7 @@ + + require 'net/http' + require 'rexml/document' ++require 'fileutils' + + HERMES_GITHUB_URL = "https://github.com/facebook/hermes.git" + ENV_BUILD_FROM_SOURCE = "RCT_BUILD_HERMES_FROM_SOURCE" +@@ -217,14 +218,49 @@ def download_hermes_tarball(react_native_path, tarball_url, version, configurati + "#{artifacts_dir()}/hermes-ios-#{version}.tar.gz" : + "#{artifacts_dir()}/hermes-ios-#{version}-#{configuration}.tar.gz" + +- unless File.exist?(destination_path) +- # Download to a temporary file first so we don't cache incomplete downloads. +- tmp_file = "#{artifacts_dir()}/hermes-ios.download" +- `mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"` ++ if File.exist?(destination_path) ++ return destination_path if valid_hermes_tarball?(destination_path) ++ ++ hermes_log("Removing invalid cached artifact: #{destination_path}", :error) ++ FileUtils.rm_f(destination_path) ++ end ++ ++ FileUtils.mkdir_p(artifacts_dir()) ++ tmp_file = "#{destination_path}.download-#{Process.pid}" ++ ++ begin ++ download_succeeded = system( ++ 'curl', ++ '--fail', ++ '--location', ++ '--http1.1', ++ '--retry', '5', ++ '--retry-all-errors', ++ '--retry-delay', '2', ++ '--connect-timeout', '30', ++ '--output', tmp_file, ++ tarball_url ++ ) ++ ++ unless download_succeeded && valid_hermes_tarball?(tmp_file) ++ raise "[Hermes] Failed to download a valid artifact from #{tarball_url}" ++ end ++ ++ # Only expose the final path after the complete archive has been validated. ++ File.rename(tmp_file, destination_path) ++ ensure ++ FileUtils.rm_f(tmp_file) + end ++ + return destination_path + end + ++def valid_hermes_tarball?(tarball_path) ++ return false unless File.file?(tarball_path) && File.size?(tarball_path) ++ ++ return system('tar', '-tzf', tarball_path, out: File::NULL, err: File::NULL) ++end ++ + def nightly_tarball_url(version) + params = "r=snapshots\&g=com.facebook.react\&a=react-native-artifacts\&c=hermes-ios-debug\&e=tar.gz\&v=#{version}-SNAPSHOT" + return resolve_url_redirects("http://oss.sonatype.org/service/local/artifact/maven/redirect\?#{params}") +diff --git a/node_modules/react-native/sdks/hermes-engine/utils/replace_hermes_version.js b/node_modules/react-native/sdks/hermes-engine/utils/replace_hermes_version.js +index f727aeb..4720eed 100644 +--- a/node_modules/react-native/sdks/hermes-engine/utils/replace_hermes_version.js ++++ b/node_modules/react-native/sdks/hermes-engine/utils/replace_hermes_version.js +@@ -9,7 +9,7 @@ + + 'use strict'; + +-const {execSync} = require('child_process'); ++const {execFileSync} = require('child_process'); + const fs = require('fs'); + const yargs = require('yargs'); + +@@ -55,13 +55,48 @@ function shouldReplaceHermesConfiguration(configuration) { + function replaceHermesConfiguration(configuration, version, podsRoot) { + const tarballURLPath = `${podsRoot}/hermes-engine-artifacts/hermes-ios-${version.toLowerCase()}-${configuration.toLowerCase()}.tar.gz`; + ++ validateHermesTarball(tarballURLPath); ++ + const finalLocation = 'hermes-engine'; +- console.log('Preparing the final location'); +- fs.rmSync(finalLocation, {force: true, recursive: true}); +- fs.mkdirSync(finalLocation, {recursive: true}); ++ const temporaryLocation = fs.mkdtempSync(`${finalLocation}.tmp-`); ++ const backupLocation = `${finalLocation}.backup-${process.pid}-${Date.now()}`; ++ ++ try { ++ console.log('Extracting the tarball'); ++ execFileSync('tar', ['-xf', tarballURLPath, '-C', temporaryLocation], { ++ stdio: 'inherit', ++ }); ++ ++ console.log('Preparing the final location'); ++ if (fs.existsSync(finalLocation)) { ++ fs.renameSync(finalLocation, backupLocation); ++ } ++ ++ try { ++ fs.renameSync(temporaryLocation, finalLocation); ++ } catch (error) { ++ if (fs.existsSync(backupLocation) && !fs.existsSync(finalLocation)) { ++ fs.renameSync(backupLocation, finalLocation); ++ } ++ throw error; ++ } ++ ++ fs.rmSync(backupLocation, {force: true, recursive: true}); ++ } finally { ++ fs.rmSync(temporaryLocation, {force: true, recursive: true}); ++ } ++} + +- console.log('Extracting the tarball'); +- execSync(`tar -xf ${tarballURLPath} -C ${finalLocation}`); ++function validateHermesTarball(tarballPath) { ++ if (!fs.existsSync(tarballPath) || fs.statSync(tarballPath).size === 0) { ++ throw new Error(`Hermes archive is missing or empty: ${tarballPath}`); ++ } ++ ++ try { ++ execFileSync('tar', ['-tzf', tarballPath], {stdio: 'ignore'}); ++ } catch (_error) { ++ throw new Error(`Hermes archive is invalid: ${tarballPath}`); ++ } + } + + function updateLastBuildConfiguration(configuration) { diff --git a/node_modules/react-native/third-party-podspecs/boost.podspec b/node_modules/react-native/third-party-podspecs/boost.podspec index 8e924af..edd3a44 100644 --- a/node_modules/react-native/third-party-podspecs/boost.podspec