Skip to content

Commit 298e3bf

Browse files
Agentforce Employee Auth Support (#5)
* Integrate employee agent in bridge with dynamic loading of Salesforce Mobile SDK * Support OAuth Tokens * Support agent switching * Remove unnecessary delay after initializing Agentforce, add logic on Android to register EmployeeAgentAuthBridge if MobileSDK is present at runtime, and add necessary logic to get refresh tokens when auth tokens expire.
1 parent 15f5945 commit 298e3bf

File tree

63 files changed

+5990
-1402
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+5990
-1402
lines changed

.gitignore

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ project.xcworkspace
2323
**/.xcode.env.local
2424

2525
# Gradle
26-
/build/
2726
/packages/rn-tester/build
2827
/packages/rn-tester/android/app/.cxx/
2928
/packages/rn-tester/android/app/build/
@@ -62,13 +61,22 @@ buck-out
6261
.watchmanconfig
6362

6463
# Android
65-
.idea
64+
*.apk
65+
*.ap_
66+
*.dex
67+
*.class
68+
bin/
69+
build/
70+
gen/
71+
out/
72+
.idea/
6673
.gradle
6774
local.properties
6875
*.iml
6976
/packages/react-native/android/*
7077
!/packages/react-native/android/README.md
7178
.kotlin/
79+
/.claude
7280

7381
# Node
7482
node_modules
@@ -113,6 +121,11 @@ package-lock.json
113121
vendor/
114122

115123
# iOS / CocoaPods
124+
/ios/build
125+
/ios/Pods
126+
ios/.xcode.env
127+
/node_modules
128+
/ios/.claude
116129
/private/helloworld/ios/build/
117130
/private/helloworld/ios/Pods/
118131
/private/helloworld/ios/Podfile.lock
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/ios/build
2+
/ios/Pods
3+
ios/.xcode.env
4+
/node_modules
5+
/mobile_sdk
6+
/ios/ReactAgentforce.xcworkspace/xcuserdata
7+
/ios/.claude
8+
/build
9+
ios/Frameworks/AgentforceSDK
10+
ios/Frameworks/AgentforceService
11+
12+
# Android
13+
*.apk
14+
*.ap_
15+
*.dex
16+
*.class
17+
bin/
18+
gen/
19+
out/
20+
build/
21+
.gradle/
22+
local.properties
23+
.idea/
24+
*.iml
25+
.navigation/
26+
/.claude
27+
28+
# Employee config with auth for development
29+
src/config/employeeAgentConfig.local.ts
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Agentforce React Native Bridge
2+
3+
This directory contains the Agentforce bridge module: JavaScript API layer, sample app, and native iOS/Android code. It has **no Mobile SDK dependency**; a host app that adds the Mobile SDK can use the included auth bridge for Employee Agent.
4+
5+
## Installation
6+
7+
### iOS (CocoaPods)
8+
9+
In your app’s `Podfile`:
10+
11+
```ruby
12+
pod 'ReactNativeAgentforce', :path => '../node_modules/react-native-agentforce/ios'
13+
```
14+
15+
Your app must also include the Agentforce iOS SDK in the Podfile so the bridge can link. For **Employee Agent**, the host app must additionally include the Salesforce Mobile SDK and perform bootconfig + SDK initialization.
16+
17+
### Android
18+
19+
- Link the Android library and register `AgentforcePackage` in your app.
20+
- For **Employee Agent** (see below), the host app must add the Salesforce React dependency and bootconfig + SDK init.
21+
22+
### JavaScript
23+
24+
Import the API from this package when the native module is linked:
25+
26+
```ts
27+
import { AgentforceService } from 'react-native-agentforce';
28+
```
29+
30+
Use the sample app in `app/` as reference or replace with your own UI.
31+
32+
---
33+
34+
## Employee Agent: host app requirements
35+
36+
For **Employee Agent** (authenticated) mode, the host app must provide the Salesforce Mobile SDK and initialize it. This bridge does not bundle the SDK.
37+
38+
### Android
39+
40+
Add the Salesforce React dependency in your app’s `build.gradle` (e.g. `app/build.gradle`):
41+
42+
```gradle
43+
implementation "com.salesforce.mobilesdk:SalesforceReact:13.1.1"
44+
```
45+
46+
You must also configure **bootconfig** and perform **SDK initialization** (e.g. via react-native-force or your existing Salesforce Mobile SDK setup). Without these, Employee Agent auth will not work.
47+
48+
### iOS
49+
50+
Include the Salesforce Mobile SDK pods in your Podfile and perform **bootconfig** and **SDK initialization** as required for your app. The bridge’s Employee Agent auth layer relies on the SDK being initialized at runtime.
51+
52+
---
53+
54+
For full integration steps (e.g. autolinking, registration), see `docs/AGENTFORCE_MODULE_EXTRACTION_PLAN.md` in the parent repo if available.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
apply plugin: "com.android.library"
2+
apply plugin: "kotlin-android"
3+
apply plugin: "org.jetbrains.kotlin.plugin.compose"
4+
apply plugin: 'kotlinx-serialization'
5+
6+
android {
7+
namespace = "com.salesforce.android.reactagentforce"
8+
compileSdk 35
9+
10+
defaultConfig {
11+
minSdkVersion 29
12+
targetSdkVersion 35
13+
versionCode 1
14+
versionName "1.0"
15+
}
16+
17+
buildFeatures {
18+
buildConfig = true
19+
compose = true
20+
}
21+
22+
kotlinOptions {
23+
jvmTarget = "17"
24+
}
25+
26+
compileOptions {
27+
sourceCompatibility JavaVersion.VERSION_17
28+
targetCompatibility JavaVersion.VERSION_17
29+
coreLibraryDesugaringEnabled true
30+
}
31+
32+
buildTypes {
33+
release {
34+
minifyEnabled false
35+
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
36+
}
37+
}
38+
}
39+
40+
repositories {
41+
google()
42+
mavenCentral()
43+
maven { url 'https://jitpack.io' }
44+
maven { url 'https://opensource.salesforce.com/AgentforceMobileSDK-Android/agentforce-sdk-repository-experimental' }
45+
maven { url 'https://s3.amazonaws.com/salesforce-async-messaging-experimental/public/android' }
46+
}
47+
48+
dependencies {
49+
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.1.5"
50+
implementation "com.facebook.react:react-android"
51+
api "com.salesforce.android.agentforcesdk:agentforce-sdk:14.20.0-experimental"
52+
// compileOnly: host app adds this for Employee Agent. Bridge compiles; host provides at runtime.
53+
compileOnly "com.salesforce.mobilesdk:SalesforceReact:13.1.1"
54+
55+
def composeBom = platform('androidx.compose:compose-bom:2024.02.00')
56+
implementation composeBom
57+
implementation 'androidx.compose.ui:ui'
58+
implementation 'androidx.compose.ui:ui-tooling-preview'
59+
implementation 'androidx.compose.runtime:runtime'
60+
implementation 'androidx.compose.material3:material3'
61+
implementation 'androidx.activity:activity-compose:1.8.2'
62+
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0'
63+
64+
implementation "org.jetbrains.kotlin:kotlin-stdlib:2.0.20"
65+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
66+
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0"
67+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Add project specific ProGuard rules here.
2+
# Keep options for Agentforce / React Native if needed when consumer enables minify.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
7+
8+
<!-- Components merged into host app's application -->
9+
<application>
10+
<activity
11+
android:name="com.salesforce.android.reactagentforce.AgentforceConversationActivity"
12+
android:theme="@style/Theme.Material3.DayNight"
13+
android:exported="false" />
14+
</application>
15+
</manifest>
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright (c) 2024-present, salesforce.com, inc.
3+
* All rights reserved.
4+
*/
5+
package com.salesforce.android.reactagentforce
6+
7+
import com.salesforce.android.agentforcesdkimpl.AgentforceClient
8+
import com.salesforce.android.agentforcesdkimpl.AgentforceConversation
9+
import com.salesforce.android.reactagentforce.models.AgentMode
10+
11+
/**
12+
* Singleton holder for AgentforceClient to share across activities.
13+
* This is necessary because ViewModels are activity-scoped.
14+
* Updated to support both Service Agent and Employee Agent modes.
15+
*/
16+
object AgentforceClientHolder {
17+
18+
@Volatile
19+
var agentforceClient: AgentforceClient? = null
20+
private set
21+
22+
@Volatile
23+
var currentConversation: AgentforceConversation? = null
24+
private set
25+
26+
@Volatile
27+
var isConfigured: Boolean = false
28+
private set
29+
30+
/** Current agent mode (Service or Employee) */
31+
@Volatile
32+
var currentMode: AgentMode? = null
33+
private set
34+
35+
/** Agent ID for Employee Agent mode */
36+
@Volatile
37+
var agentId: String? = null
38+
private set
39+
40+
/** ES Developer Name for Service Agent mode */
41+
@Volatile
42+
var esDeveloperName: String? = null
43+
private set
44+
45+
/**
46+
* Set the Agentforce client
47+
*/
48+
fun setClient(client: AgentforceClient) {
49+
agentforceClient = client
50+
isConfigured = true
51+
}
52+
53+
/**
54+
* Set the current conversation
55+
*/
56+
fun setConversation(conversation: AgentforceConversation?) {
57+
currentConversation = conversation
58+
}
59+
60+
/**
61+
* Set the current agent mode
62+
*/
63+
fun setMode(mode: AgentMode) {
64+
currentMode = mode
65+
66+
// Extract agent identifiers based on mode
67+
when (mode) {
68+
is AgentMode.Service -> {
69+
esDeveloperName = mode.config.esDeveloperName
70+
agentId = null
71+
}
72+
is AgentMode.Employee -> {
73+
agentId = mode.config.agentId
74+
esDeveloperName = null
75+
}
76+
}
77+
}
78+
79+
/**
80+
* Set the agent ID for Employee Agent mode
81+
*/
82+
fun setAgentId(id: String) {
83+
agentId = id
84+
}
85+
86+
/**
87+
* Set the ES Developer Name for Service Agent mode
88+
*/
89+
fun setEsDeveloperName(name: String) {
90+
esDeveloperName = name
91+
}
92+
93+
/**
94+
* Clear the conversation reference but keep the client
95+
*/
96+
fun clearConversation() {
97+
currentConversation = null
98+
}
99+
100+
/**
101+
* Clear all state
102+
*/
103+
fun clear() {
104+
currentConversation = null
105+
agentforceClient = null
106+
isConfigured = false
107+
currentMode = null
108+
agentId = null
109+
esDeveloperName = null
110+
}
111+
112+
/**
113+
* Check if the holder is configured for Employee Agent mode
114+
*/
115+
val isEmployeeAgent: Boolean
116+
get() = currentMode is AgentMode.Employee
117+
118+
/**
119+
* Check if the holder is configured for Service Agent mode
120+
*/
121+
val isServiceAgent: Boolean
122+
get() = currentMode is AgentMode.Service
123+
124+
/**
125+
* Get the mode type string
126+
*/
127+
val modeTypeString: String?
128+
get() = currentMode?.typeString
129+
}

0 commit comments

Comments
 (0)