@salesforce/agentforce-conversation-client 11.67.1 → 11.68.1
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/README.md +219 -46
- package/dist/constants.d.ts +18 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +16 -0
- package/dist/embed.d.ts +17 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +105 -0
- package/dist/frame.d.ts +7 -0
- package/dist/frame.d.ts.map +1 -0
- package/dist/frame.js +92 -0
- package/dist/index.d.ts +4 -83
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -296
- package/dist/index.spec.js +300 -6
- package/dist/session-keepalive.d.ts +17 -0
- package/dist/session-keepalive.d.ts.map +1 -0
- package/dist/session-keepalive.js +183 -0
- package/dist/styles.d.ts +7 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +105 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ This library embeds the Agentforce Conversation Client for employee agents using
|
|
|
11
11
|
- **One of `salesforceOrigin` or `frontdoorUrl` is required.**
|
|
12
12
|
- Use `salesforceOrigin` when your app is hosted inside a Salesforce org and already has an authenticated session.
|
|
13
13
|
- Use `frontdoorUrl` when embedding the chat client outside Salesforce (for example, a localhost or external app).
|
|
14
|
+
- In **frontdoor-URL-only** mode, ACC **automatically** keeps the Salesforce session alive via `sessionRefresh()`. For host-driven frontdoor **regeneration** when refresh can no longer help, see [Session management](#session-management).
|
|
14
15
|
|
|
15
16
|
- Use `sitePrefix` so LO 2.0 can correctly resolve asset URLs and route requests through the right site context. `site-prefix` is the path segment of the Experience Site URL after the host. For example, if your site URL is https://mydomain.my.site.com/sample, the site prefix is /sample
|
|
16
17
|
- Lightning Out 2.0 uses an existing session to initialize; without it, the embed will fail to start.
|
|
@@ -374,6 +375,151 @@ const { loApp } = embedAgentforceClient({
|
|
|
374
375
|
});
|
|
375
376
|
```
|
|
376
377
|
|
|
378
|
+
### Session management
|
|
379
|
+
|
|
380
|
+
Long-lived embeds can hit Salesforce session expiry. ACC splits ownership so users are not
|
|
381
|
+
left stuck mid-conversation:
|
|
382
|
+
|
|
383
|
+
| Owner | Responsibility |
|
|
384
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
385
|
+
| **ACC** | Session **maintenance** — watch TTL and call Lightning Out `sessionRefresh()` while the session is still alive (**on by default** in frontdoor-URL-only mode) |
|
|
386
|
+
| **Your app (host)** | Session **establishment / re-establishment** — provide the initial `frontdoorUrl`, and mint a **new** frontdoor URL when ACC asks |
|
|
387
|
+
|
|
388
|
+
Lightning Out only exposes primitives (`sessionExpiry`, `sessionRefresh`, assign `frontdoorUrl`).
|
|
389
|
+
It does not poll. ACC runs that policy automatically whenever you embed with `frontdoorUrl`
|
|
390
|
+
and without `salesforceOrigin`. Pass `sessionManagement` only to tune thresholds or handle
|
|
391
|
+
regeneration.
|
|
392
|
+
|
|
393
|
+
#### Requirements
|
|
394
|
+
|
|
395
|
+
- Use **frontdoor-URL-only mode**: pass `frontdoorUrl`, do **not** also pass `salesforceOrigin`.
|
|
396
|
+
Keepalive is unsupported in org-url mode.
|
|
397
|
+
- Mint frontdoor URLs **server-side** (token exchange). Never put a client secret in the browser.
|
|
398
|
+
- Handle regeneration in the host page — do not pass functions into Lightning Out component
|
|
399
|
+
props (they cannot cross the iframe `postMessage` boundary).
|
|
400
|
+
|
|
401
|
+
#### What ACC does automatically (refresh)
|
|
402
|
+
|
|
403
|
+
After `lo.application.ready` in frontdoor-URL-only mode, ACC:
|
|
404
|
+
|
|
405
|
+
1. Polls remaining TTL on an interval (default every **60 seconds**).
|
|
406
|
+
2. Does nothing while TTL is healthy (default: more than **10 minutes** left).
|
|
407
|
+
3. Calls `loApp.sessionRefresh()` in the keepalive window (default: **5–10 minutes** left).
|
|
408
|
+
That reloads only the hidden auth iframe — **no host call, no new credentials**, and the
|
|
409
|
+
chat conversation stays mounted.
|
|
410
|
+
|
|
411
|
+
You do **not** need to call `sessionRefresh()` yourself, and you do **not** need to pass
|
|
412
|
+
`sessionManagement` to get this behavior.
|
|
413
|
+
|
|
414
|
+
#### When you must regenerate the frontdoor URL
|
|
415
|
+
|
|
416
|
+
`sessionRefresh()` only extends a **live** session. It cannot help when:
|
|
417
|
+
|
|
418
|
+
- TTL is already at or near zero (absolute / idle timeout already hit)
|
|
419
|
+
- `sessionRefresh()` itself fails (network, IdP, LO timeout)
|
|
420
|
+
- TTL cannot be queried
|
|
421
|
+
- The user logged out / the session was revoked
|
|
422
|
+
|
|
423
|
+
In those cases ACC **stops trying to self-heal** and notifies your app **once per outage**
|
|
424
|
+
(not once per poll). Your job is to mint a fresh frontdoor URL and push it back with
|
|
425
|
+
`setFrontdoorUrl`. Only the auth iframe reloads; the chat iframe stays mounted, so the
|
|
426
|
+
conversation is not torn down.
|
|
427
|
+
|
|
428
|
+
#### How to handle regeneration
|
|
429
|
+
|
|
430
|
+
**Recommended — listen on the container:**
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
import {
|
|
434
|
+
embedAgentforceClient,
|
|
435
|
+
AGENTFORCE_FRONTDOOR_URL_REQUIRED_EVENT,
|
|
436
|
+
} from "@salesforce/agentforce-conversation-client";
|
|
437
|
+
|
|
438
|
+
const container = document.querySelector("#agentforce-container")!;
|
|
439
|
+
|
|
440
|
+
const { setFrontdoorUrl } = embedAgentforceClient({
|
|
441
|
+
container,
|
|
442
|
+
frontdoorUrl: initialFrontdoorUrl, // establishment — keepalive starts automatically
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
container.addEventListener(AGENTFORCE_FRONTDOOR_URL_REQUIRED_EVENT, async (event) => {
|
|
446
|
+
const { reason, ttlSeconds } = (event as CustomEvent).detail;
|
|
447
|
+
console.warn("ACC needs a new frontdoor URL", { reason, ttlSeconds });
|
|
448
|
+
|
|
449
|
+
// Re-establishment — call YOUR backend (example only)
|
|
450
|
+
const response = await fetch("/api/salesforce/frontdoor", { method: "POST" });
|
|
451
|
+
if (!response.ok) {
|
|
452
|
+
// Retry on your schedule, or prompt the user to sign in again
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const { frontdoorUrl } = await response.json();
|
|
456
|
+
setFrontdoorUrl(frontdoorUrl); // applies without remounting the chat
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Optional — same payload via callback** (instead of or in addition to the event):
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
const { setFrontdoorUrl } = embedAgentforceClient({
|
|
464
|
+
container: "#agentforce-container",
|
|
465
|
+
frontdoorUrl: initialFrontdoorUrl,
|
|
466
|
+
sessionManagement: {
|
|
467
|
+
onFrontdoorUrlRequired: async ({ reason, ttlSeconds }) => {
|
|
468
|
+
console.warn("ACC needs a new frontdoor URL", { reason, ttlSeconds });
|
|
469
|
+
const response = await fetch("/api/salesforce/frontdoor", { method: "POST" });
|
|
470
|
+
if (!response.ok) return;
|
|
471
|
+
const { frontdoorUrl } = await response.json();
|
|
472
|
+
setFrontdoorUrl(frontdoorUrl);
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Until you call `setFrontdoorUrl`, keepalive will not ask again for that outage. After a
|
|
479
|
+
successful push, ACC resumes watching; if that session later dies, you will be notified again.
|
|
480
|
+
|
|
481
|
+
`setFrontdoorUrl` is always returned in frontdoor-URL-only mode, so you can also push a
|
|
482
|
+
renewed URL proactively if your host already tracks Salesforce session lifetime.
|
|
483
|
+
|
|
484
|
+
#### TTL policy (defaults)
|
|
485
|
+
|
|
486
|
+
| TTL band | What ACC does | Host action |
|
|
487
|
+
| ------------------------------------------------------------------------------ | ------------------------------ | ---------------------------- |
|
|
488
|
+
| Above `skipThresholdSeconds` (default **600s** / 10 min) | Do nothing | None |
|
|
489
|
+
| Between refresh and skip thresholds (default **300–600s**) | Auto `sessionRefresh()` | None |
|
|
490
|
+
| At or below `refreshThresholdSeconds` (default **300s**), or refresh/TTL fails | Emit regeneration request once | Mint URL → `setFrontdoorUrl` |
|
|
491
|
+
|
|
492
|
+
Omit the threshold fields to use the defaults above. To tune them:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
embedAgentforceClient({
|
|
496
|
+
container: "#agentforce-container",
|
|
497
|
+
frontdoorUrl: initialFrontdoorUrl,
|
|
498
|
+
sessionManagement: {
|
|
499
|
+
checkIntervalMs: 120_000, // poll every 2 minutes (default: 60_000)
|
|
500
|
+
skipThresholdSeconds: 900, // do nothing above 15 min TTL (default: 600)
|
|
501
|
+
refreshThresholdSeconds: 180, // auto-refresh between 3–15 min; ask host at ≤ 3 min (default: 300)
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
`refreshThresholdSeconds` must be lower than `skipThresholdSeconds`.
|
|
507
|
+
|
|
508
|
+
Polling starts after `lo.application.ready`. It stops permanently on `lo.application.logout`
|
|
509
|
+
(a later `setFrontdoorUrl` is ignored so a logged-out user is not silently re-authenticated).
|
|
510
|
+
|
|
511
|
+
#### Regeneration event / callback payload
|
|
512
|
+
|
|
513
|
+
| Field | Type | Notes |
|
|
514
|
+
| ------------ | ------------------------------------------------------- | -------------------------------------------- |
|
|
515
|
+
| `reason` | `"expired" \| "refresh-failed" \| "expiry-unavailable"` | Why maintenance could not continue |
|
|
516
|
+
| `ttlSeconds` | `number \| undefined` | Absent when the TTL query itself failed |
|
|
517
|
+
| `error` | `unknown \| undefined` | Underlying Lightning Out error, when present |
|
|
518
|
+
|
|
519
|
+
Event name: `agentforce:frontdoorurlrequired` (`AGENTFORCE_FRONTDOOR_URL_REQUIRED_EVENT`).
|
|
520
|
+
Dispatched on the **embed container** (bubbles). Prefer this over putting handlers on LO
|
|
521
|
+
proxy elements.
|
|
522
|
+
|
|
377
523
|
### Listening for Events
|
|
378
524
|
|
|
379
525
|
#### Callback props (recommended)
|
|
@@ -390,17 +536,17 @@ embedAgentforceClient({
|
|
|
390
536
|
console.log("Lightning Out is ready", detail);
|
|
391
537
|
},
|
|
392
538
|
onError: (error) => {
|
|
393
|
-
// error.type is "lo.application.error" or "lo.
|
|
539
|
+
// error.type is "lo.application.error", "lo.iframe.error", or "lo.session.error"
|
|
394
540
|
// error.detail contains the error payload from Lightning Out
|
|
395
541
|
console.error(`[${error.type}]`, error.detail);
|
|
396
542
|
},
|
|
397
543
|
});
|
|
398
544
|
```
|
|
399
545
|
|
|
400
|
-
| Callback | Fires when
|
|
401
|
-
| --------- |
|
|
402
|
-
| `onReady` | Lightning Out application has finished loading
|
|
403
|
-
| `onError` | An application-level
|
|
546
|
+
| Callback | Fires when | Argument shape |
|
|
547
|
+
| --------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
548
|
+
| `onReady` | Lightning Out application has finished loading | `detail: unknown` — the raw detail from the `lo.application.ready` event |
|
|
549
|
+
| `onError` | An application-level, iframe-level, or session-callback error | `{ type: "lo.application.error" \| "lo.iframe.error" \| "lo.session.error", detail: unknown }` |
|
|
404
550
|
|
|
405
551
|
Both callbacks are optional.
|
|
406
552
|
|
|
@@ -437,48 +583,54 @@ Embeds the Agentforce Conversation Client by creating a Lightning Out 2.0 app an
|
|
|
437
583
|
|
|
438
584
|
#### Parameters
|
|
439
585
|
|
|
440
|
-
| Parameter | Type
|
|
441
|
-
| ----------------------------------------------------------- |
|
|
442
|
-
| `options.container` | `string \| HTMLElement`
|
|
443
|
-
| `options.salesforceOrigin` | `string`
|
|
444
|
-
| `options.appId` | `string`
|
|
445
|
-
| `options.frontdoorUrl` | `string`
|
|
446
|
-
| `options.sitePrefix` | `string`
|
|
447
|
-
| `options.agentforceClientConfig` | `AgentforceClientConfig`
|
|
448
|
-
| `agentforceClientConfig.agentId` | `string`
|
|
449
|
-
| `agentforceClientConfig.agentLabel` | `string`
|
|
450
|
-
| `agentforceClientConfig.styleTokens` | `Record<string, string>`
|
|
451
|
-
| `agentforceClientConfig.renderingConfig.mode` | `"inline" \| "floating"`
|
|
452
|
-
| `agentforceClientConfig.renderingConfig.width` | `string \| number`
|
|
453
|
-
| `agentforceClientConfig.renderingConfig.height` | `string \| number`
|
|
454
|
-
| `agentforceClientConfig.renderingConfig.headerEnabled` | `boolean`
|
|
455
|
-
| `agentforceClientConfig.renderingConfig.showHeaderIcon` | `boolean`
|
|
456
|
-
| `agentforceClientConfig.renderingConfig.showAvatar` | `boolean`
|
|
457
|
-
| `agentforceClientConfig.renderingConfig.agentAvatarUrl` | `string`
|
|
458
|
-
| `agentforceClientConfig.renderingConfig.agentAvatarAltText` | `string`
|
|
459
|
-
| `agentforceClientConfig.renderingConfig.headerIconName` | `string`
|
|
460
|
-
| `agentforceClientConfig.renderingConfig.headerIconSize` | `string`
|
|
461
|
-
| `agentforceClientConfig.renderingConfig.headerImageUrl` | `string`
|
|
462
|
-
| `agentforceClientConfig.renderingConfig.headerImageAlt` | `string`
|
|
463
|
-
| `agentforceClientConfig.channel` | `string`
|
|
464
|
-
| `agentforceClientConfig.messageInputPlaceholderText` | `string`
|
|
465
|
-
| `agentforceClientConfig.isHistoryIntroNeeded` | `boolean`
|
|
466
|
-
| `agentforceClientConfig.isFileBased` | `boolean`
|
|
467
|
-
| `agentforceClientConfig.typewriterConfig` | `TypewriterConfig`
|
|
468
|
-
| `agentforceClientConfig.floatingButtonLabel` | `string`
|
|
469
|
-
| `agentforceClientConfig.floatingButtonIcon` | `string`
|
|
470
|
-
| `agentforceClientConfig.floatingButtonImage` | `string`
|
|
471
|
-
| `agentforceClientConfig.floatingButtonImageAlt` | `string`
|
|
472
|
-
| `agentforceClientConfig.iconPosition` | `"left" \| "right"`
|
|
473
|
-
| `options.
|
|
474
|
-
| `
|
|
586
|
+
| Parameter | Type | Required | Description |
|
|
587
|
+
| ----------------------------------------------------------- | ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
588
|
+
| `options.container` | `string \| HTMLElement` | Yes | CSS selector or HTMLElement to embed into |
|
|
589
|
+
| `options.salesforceOrigin` | `string` | No | Salesforce org origin URL (use when hosted in a Salesforce org). At least one of `salesforceOrigin` or `frontdoorUrl` is required |
|
|
590
|
+
| `options.appId` | `string` | No | 18-digit Lightning Out 2.0 app ID (`app-id`); find it in Lightning Out 2.0 App Manager in Setup; not required for apps created before Spring '26 |
|
|
591
|
+
| `options.frontdoorUrl` | `string` | No | Frontdoor URL for authentication (use when embedding outside Salesforce). At least one of `salesforceOrigin` or `frontdoorUrl` is required |
|
|
592
|
+
| `options.sitePrefix` | `string` | No | Experience Cloud site prefix used by Lightning Out (for example, `"/my-site"`). Set as `site-prefix` on the LO app |
|
|
593
|
+
| `options.agentforceClientConfig` | `AgentforceClientConfig` | No | Configuration for the Agentforce client (see sub-properties below) |
|
|
594
|
+
| `agentforceClientConfig.agentId` | `string` | Yes\* | The agent to load — required in practice, will not work without it |
|
|
595
|
+
| `agentforceClientConfig.agentLabel` | `string` | No | Display name shown in the chat header. Falls back to the agent's configured name if omitted |
|
|
596
|
+
| `agentforceClientConfig.styleTokens` | `Record<string, string>` | No | Theme colors and style overrides (see Style Tokens section) |
|
|
597
|
+
| `agentforceClientConfig.renderingConfig.mode` | `"inline" \| "floating"` | No | Rendering mode. Defaults to `"floating"` |
|
|
598
|
+
| `agentforceClientConfig.renderingConfig.width` | `string \| number` | No | Width of the inline frame. Number values are treated as `px`, strings as CSS values (e.g. `"100%"`) |
|
|
599
|
+
| `agentforceClientConfig.renderingConfig.height` | `string \| number` | No | Height of the inline frame. Number values are treated as `px`, strings as CSS values (e.g. `"100%"`) |
|
|
600
|
+
| `agentforceClientConfig.renderingConfig.headerEnabled` | `boolean` | No | Show or hide the chat header bar. Defaults to hidden in inline mode. Set `true` to show it |
|
|
601
|
+
| `agentforceClientConfig.renderingConfig.showHeaderIcon` | `boolean` | No | Show or hide the icon in the header. Omit or set `false` to hide |
|
|
602
|
+
| `agentforceClientConfig.renderingConfig.showAvatar` | `boolean` | No | Show or hide avatars in message rows. Defaults to `true` |
|
|
603
|
+
| `agentforceClientConfig.renderingConfig.agentAvatarUrl` | `string` | No | Custom avatar image URL for agent messages |
|
|
604
|
+
| `agentforceClientConfig.renderingConfig.agentAvatarAltText` | `string` | No | Alt text for the custom agent avatar image |
|
|
605
|
+
| `agentforceClientConfig.renderingConfig.headerIconName` | `string` | No | SLDS icon name for the header (e.g. `"utility:agent"`) |
|
|
606
|
+
| `agentforceClientConfig.renderingConfig.headerIconSize` | `string` | No | Header icon size |
|
|
607
|
+
| `agentforceClientConfig.renderingConfig.headerImageUrl` | `string` | No | Custom header logo image URL |
|
|
608
|
+
| `agentforceClientConfig.renderingConfig.headerImageAlt` | `string` | No | Alt text for the custom header logo image |
|
|
609
|
+
| `agentforceClientConfig.channel` | `string` | No | Channel identifier for analytics/instrumentation |
|
|
610
|
+
| `agentforceClientConfig.messageInputPlaceholderText` | `string` | No | Custom placeholder text for the message input field |
|
|
611
|
+
| `agentforceClientConfig.isHistoryIntroNeeded` | `boolean` | No | Show a conversation history intro block |
|
|
612
|
+
| `agentforceClientConfig.isFileBased` | `boolean` | No | Indicates the selected agent is file-based; forwarded to `accSdkWrapper`/`accSdk` for file-agent-specific behavior |
|
|
613
|
+
| `agentforceClientConfig.typewriterConfig` | `TypewriterConfig` | No | Customize typewriter animation (see TypewriterConfig type below) |
|
|
614
|
+
| `agentforceClientConfig.floatingButtonLabel` | `string` | No | Label text for the floating action button (floating mode) |
|
|
615
|
+
| `agentforceClientConfig.floatingButtonIcon` | `string` | No | SLDS icon name for the FAB (e.g. `"utility:agent"`) |
|
|
616
|
+
| `agentforceClientConfig.floatingButtonImage` | `string` | No | URL to an external image (PNG/SVG) shown in place of the FAB icon |
|
|
617
|
+
| `agentforceClientConfig.floatingButtonImageAlt` | `string` | No | Alt text for the floating button image |
|
|
618
|
+
| `agentforceClientConfig.iconPosition` | `"left" \| "right"` | No | Icon position in the FAB. Defaults to `"left"` |
|
|
619
|
+
| `options.sessionManagement` | `AgentforceSessionManagementConfig` | No | Optional overrides for default keepalive (frontdoor-URL-only). Tune thresholds or handle regeneration; omit to use defaults |
|
|
620
|
+
| `sessionManagement.onFrontdoorUrlRequired` | `(request) => void` | No | Optional; same payload as `agentforce:frontdoorurlrequired`. Prefer the container event |
|
|
621
|
+
| `sessionManagement.checkIntervalMs` | `number` | No | How often ACC polls TTL. Defaults to `60000` |
|
|
622
|
+
| `sessionManagement.skipThresholdSeconds` | `number` | No | TTL above this → ACC does nothing. Defaults to `600` |
|
|
623
|
+
| `sessionManagement.refreshThresholdSeconds` | `number` | No | TTL above this (and ≤ skip) → ACC `sessionRefresh()`; at or below → ask host. Defaults to `300` |
|
|
624
|
+
| `options.onReady` | `AgentforceReadyHandler` | No | Callback invoked when the Lightning Out application is ready. Receives the event detail |
|
|
625
|
+
| `options.onError` | `AgentforceErrorHandler` | No | Callback invoked on Lightning Out or session-callback errors. Receives `{ type, detail }` |
|
|
475
626
|
|
|
476
627
|
#### Returns
|
|
477
628
|
|
|
478
|
-
| Property | Type
|
|
479
|
-
| --------------------- |
|
|
480
|
-
| `loApp` | `
|
|
481
|
-
| `chatClientComponent` | `HTMLElement`
|
|
629
|
+
| Property | Type | Description |
|
|
630
|
+
| --------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
631
|
+
| `loApp` | `AgentforceLightningOutApplication` | Lightning Out 2.0 app, including typed `sessionExpiry` / `sessionRefresh` / `frontdoorUrl` |
|
|
632
|
+
| `chatClientComponent` | `HTMLElement` | The chat client component element |
|
|
633
|
+
| `setFrontdoorUrl` | `(frontdoorUrl: string) => void` | Re-establish session after ACC asks for regeneration. Chat stays mounted. Ignored after logout |
|
|
482
634
|
|
|
483
635
|
## Types
|
|
484
636
|
|
|
@@ -529,13 +681,26 @@ interface AgentforceClientConfig {
|
|
|
529
681
|
|
|
530
682
|
// Error event passed to the onError callback
|
|
531
683
|
interface AgentforceLoErrorEvent {
|
|
532
|
-
type: "lo.application.error" | "lo.iframe.error";
|
|
684
|
+
type: "lo.application.error" | "lo.iframe.error" | "lo.session.error";
|
|
533
685
|
detail: unknown;
|
|
534
686
|
}
|
|
535
687
|
|
|
536
688
|
type AgentforceErrorHandler = (error: AgentforceLoErrorEvent) => void;
|
|
537
689
|
type AgentforceReadyHandler = (detail: unknown) => void;
|
|
538
690
|
|
|
691
|
+
interface AgentforceSessionRecoveryRequest {
|
|
692
|
+
reason: "expired" | "refresh-failed" | "expiry-unavailable";
|
|
693
|
+
ttlSeconds?: number;
|
|
694
|
+
error?: unknown;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
interface AgentforceSessionManagementConfig {
|
|
698
|
+
onFrontdoorUrlRequired?: (request: AgentforceSessionRecoveryRequest) => void;
|
|
699
|
+
checkIntervalMs?: number;
|
|
700
|
+
skipThresholdSeconds?: number;
|
|
701
|
+
refreshThresholdSeconds?: number;
|
|
702
|
+
}
|
|
703
|
+
|
|
539
704
|
interface EmbedAgentforceClientOptions {
|
|
540
705
|
container: string | HTMLElement;
|
|
541
706
|
salesforceOrigin?: string;
|
|
@@ -543,13 +708,21 @@ interface EmbedAgentforceClientOptions {
|
|
|
543
708
|
frontdoorUrl?: string;
|
|
544
709
|
sitePrefix?: string;
|
|
545
710
|
agentforceClientConfig?: AgentforceClientConfig;
|
|
711
|
+
sessionManagement?: AgentforceSessionManagementConfig;
|
|
546
712
|
onReady?: AgentforceReadyHandler;
|
|
547
713
|
onError?: AgentforceErrorHandler;
|
|
548
714
|
}
|
|
549
715
|
|
|
716
|
+
interface AgentforceLightningOutApplication extends HTMLElement {
|
|
717
|
+
sessionExpiry(): Promise<number>;
|
|
718
|
+
sessionRefresh(): Promise<void>;
|
|
719
|
+
frontdoorUrl: string;
|
|
720
|
+
}
|
|
721
|
+
|
|
550
722
|
interface EmbedAgentforceClientResult {
|
|
551
|
-
loApp:
|
|
723
|
+
loApp: AgentforceLightningOutApplication;
|
|
552
724
|
chatClientComponent: HTMLElement;
|
|
725
|
+
setFrontdoorUrl: (frontdoorUrl: string) => void;
|
|
553
726
|
}
|
|
554
727
|
```
|
|
555
728
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
export declare const AGENTFORCE_CLIENT_ELEMENT_TAG = "runtime_copilot-acc-sdk-wrapper";
|
|
7
|
+
export declare const AGENTFORCE_CLIENT_NAMESPACE = "runtime_copilot";
|
|
8
|
+
export declare const AGENTFORCE_CLIENT_COMPONENT_NAME = "accSdkWrapper";
|
|
9
|
+
export declare const AGENTFORCE_LO_APP_DATA_ATTR = "data-lo";
|
|
10
|
+
export declare const AGENTFORCE_CLIENT_COMPONENT_NAME_ALIAS = "runtime_copilot/accSdkWrapper as runtime_copilot-acc-sdk-wrapper";
|
|
11
|
+
/** Dispatched on the embed container when ACC cannot keepalive and needs a fresh frontdoor URL. */
|
|
12
|
+
export declare const AGENTFORCE_FRONTDOOR_URL_REQUIRED_EVENT = "agentforce:frontdoorurlrequired";
|
|
13
|
+
export declare const RenderingMode: Readonly<{
|
|
14
|
+
readonly INLINE: "inline";
|
|
15
|
+
readonly FLOATING: "floating";
|
|
16
|
+
}>;
|
|
17
|
+
export type RenderingModeValue = (typeof RenderingMode)[keyof typeof RenderingMode];
|
|
18
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,6BAA6B,oCAAoC,CAAC;AAC/E,eAAO,MAAM,2BAA2B,oBAAoB,CAAC;AAC7D,eAAO,MAAM,gCAAgC,kBAAkB,CAAC;AAChE,eAAO,MAAM,2BAA2B,YAAY,CAAC;AACrD,eAAO,MAAM,sCAAsC,qEAA2G,CAAC;AAC/J,mGAAmG;AACnG,eAAO,MAAM,uCAAuC,oCAAoC,CAAC;AAEzF,eAAO,MAAM,aAAa;;;EAGf,CAAC;AAEZ,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
export const AGENTFORCE_CLIENT_ELEMENT_TAG = "runtime_copilot-acc-sdk-wrapper";
|
|
7
|
+
export const AGENTFORCE_CLIENT_NAMESPACE = "runtime_copilot";
|
|
8
|
+
export const AGENTFORCE_CLIENT_COMPONENT_NAME = "accSdkWrapper";
|
|
9
|
+
export const AGENTFORCE_LO_APP_DATA_ATTR = "data-lo";
|
|
10
|
+
export const AGENTFORCE_CLIENT_COMPONENT_NAME_ALIAS = `${AGENTFORCE_CLIENT_NAMESPACE}/${AGENTFORCE_CLIENT_COMPONENT_NAME} as ${AGENTFORCE_CLIENT_ELEMENT_TAG}`;
|
|
11
|
+
/** Dispatched on the embed container when ACC cannot keepalive and needs a fresh frontdoor URL. */
|
|
12
|
+
export const AGENTFORCE_FRONTDOOR_URL_REQUIRED_EVENT = "agentforce:frontdoorurlrequired";
|
|
13
|
+
export const RenderingMode = Object.freeze({
|
|
14
|
+
INLINE: "inline",
|
|
15
|
+
FLOATING: "floating",
|
|
16
|
+
});
|
package/dist/embed.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EmbedAgentforceClientOptions, EmbedAgentforceClientResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Embeds the Agentforce Conversation Client into a DOM container.
|
|
4
|
+
* Embed is hidden (opacity 0) until the frame receives the "accready" event.
|
|
5
|
+
*
|
|
6
|
+
* In frontdoor-URL-only mode, ACC keepalives by default via `sessionRefresh()`
|
|
7
|
+
* and asks the host for a frontdoor URL only when recovery needs new credentials.
|
|
8
|
+
* Pass `sessionManagement` only to tune thresholds or handle regeneration.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const { loApp, setFrontdoorUrl } = embedAgentforceClient({
|
|
12
|
+
* container: '#agentforce-container',
|
|
13
|
+
* frontdoorUrl: initialUrl,
|
|
14
|
+
* });
|
|
15
|
+
*/
|
|
16
|
+
export declare function embedAgentforceClient(options?: EmbedAgentforceClientOptions | null): EmbedAgentforceClientResult;
|
|
17
|
+
//# sourceMappingURL=embed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAKX,4BAA4B,EAC5B,2BAA2B,EAC3B,MAAM,YAAY,CAAC;AA2GpB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACpC,OAAO,CAAC,EAAE,4BAA4B,GAAG,IAAI,GAC3C,2BAA2B,CAwC7B"}
|
package/dist/embed.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { LightningOutApplication } from "@salesforce/lightning-out";
|
|
7
|
+
import { AGENTFORCE_CLIENT_COMPONENT_NAME_ALIAS, AGENTFORCE_LO_APP_DATA_ATTR, } from "./constants.js";
|
|
8
|
+
import { createAndMountFrame } from "./frame.js";
|
|
9
|
+
import { createSessionKeepalive, resolveSessionManagementConfig, } from "./session-keepalive.js";
|
|
10
|
+
function createAndMountLoApp(container, salesforceOrigin, appId, frontdoorUrl, sitePrefix, sessionManagement, onError, onReady) {
|
|
11
|
+
const loApp = new LightningOutApplication();
|
|
12
|
+
if (salesforceOrigin)
|
|
13
|
+
loApp.setAttribute("org-url", salesforceOrigin);
|
|
14
|
+
loApp.setAttribute("components", AGENTFORCE_CLIENT_COMPONENT_NAME_ALIAS);
|
|
15
|
+
loApp.setAttribute(AGENTFORCE_LO_APP_DATA_ATTR, `acc`);
|
|
16
|
+
if (appId)
|
|
17
|
+
loApp.setAttribute("app-id", appId);
|
|
18
|
+
if (frontdoorUrl)
|
|
19
|
+
loApp.setAttribute("frontdoor-url", frontdoorUrl);
|
|
20
|
+
if (sitePrefix)
|
|
21
|
+
loApp.setAttribute("site-prefix", sitePrefix);
|
|
22
|
+
loApp.style.opacity = "0";
|
|
23
|
+
loApp.style.pointerEvents = "none";
|
|
24
|
+
attachLoEventHandlers(loApp, onError, onReady);
|
|
25
|
+
const setFrontdoorUrl = createSessionKeepalive(loApp, container, Boolean(salesforceOrigin), sessionManagement, onError);
|
|
26
|
+
container.appendChild(loApp);
|
|
27
|
+
// Public API types the LO element with session primitives; keepalive validates
|
|
28
|
+
// those methods at runtime after `lo.application.ready` before calling them.
|
|
29
|
+
return { loApp: loApp, setFrontdoorUrl };
|
|
30
|
+
}
|
|
31
|
+
function attachLoEventHandlers(loApp, onError, onReady) {
|
|
32
|
+
loApp.addEventListener("lo.application.ready", (e) => {
|
|
33
|
+
const detail = e.detail;
|
|
34
|
+
console.log("Agentforce Conversation Client: Lightning Out ready", detail != null ? detail : "");
|
|
35
|
+
onReady?.(detail);
|
|
36
|
+
});
|
|
37
|
+
loApp.addEventListener("lo.application.error", (e) => {
|
|
38
|
+
const detail = e.detail;
|
|
39
|
+
console.error("Agentforce Conversation Client: Lightning Out error:", detail);
|
|
40
|
+
onError?.({ type: "lo.application.error", detail });
|
|
41
|
+
});
|
|
42
|
+
loApp.addEventListener("lo.iframe.error", (e) => {
|
|
43
|
+
const detail = e.detail;
|
|
44
|
+
console.error("Agentforce Conversation Client: Lightning Out iframe error:", detail);
|
|
45
|
+
onError?.({ type: "lo.iframe.error", detail });
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function resolveContainer(container) {
|
|
49
|
+
if (container instanceof HTMLElement)
|
|
50
|
+
return container;
|
|
51
|
+
if (typeof container === "string")
|
|
52
|
+
return document.querySelector(container);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function embedIntoContainer(containerElement, options) {
|
|
56
|
+
const { salesforceOrigin, appId, frontdoorUrl, sitePrefix, agentforceClientConfig = {}, sessionManagement, onError, onReady, } = options;
|
|
57
|
+
containerElement.classList.add("acc-container");
|
|
58
|
+
const { loApp, setFrontdoorUrl } = createAndMountLoApp(containerElement, salesforceOrigin, appId, frontdoorUrl, sitePrefix, sessionManagement, onError, onReady);
|
|
59
|
+
const chatClientComponent = createAndMountFrame(containerElement, agentforceClientConfig, loApp);
|
|
60
|
+
return { loApp, chatClientComponent, setFrontdoorUrl };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Embeds the Agentforce Conversation Client into a DOM container.
|
|
64
|
+
* Embed is hidden (opacity 0) until the frame receives the "accready" event.
|
|
65
|
+
*
|
|
66
|
+
* In frontdoor-URL-only mode, ACC keepalives by default via `sessionRefresh()`
|
|
67
|
+
* and asks the host for a frontdoor URL only when recovery needs new credentials.
|
|
68
|
+
* Pass `sessionManagement` only to tune thresholds or handle regeneration.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* const { loApp, setFrontdoorUrl } = embedAgentforceClient({
|
|
72
|
+
* container: '#agentforce-container',
|
|
73
|
+
* frontdoorUrl: initialUrl,
|
|
74
|
+
* });
|
|
75
|
+
*/
|
|
76
|
+
export function embedAgentforceClient(options) {
|
|
77
|
+
const { container, salesforceOrigin, appId, frontdoorUrl, sitePrefix, agentforceClientConfig = {}, sessionManagement, onError, onReady, } = options ?? {};
|
|
78
|
+
if (!container)
|
|
79
|
+
throw new Error("Agentforce Conversation Client: container is required");
|
|
80
|
+
if (!salesforceOrigin && !frontdoorUrl) {
|
|
81
|
+
throw new Error("Agentforce Conversation Client: salesforceOrigin or frontdoorUrl is required");
|
|
82
|
+
}
|
|
83
|
+
const frontdoorOnlyMode = Boolean(frontdoorUrl) && !salesforceOrigin;
|
|
84
|
+
if (sessionManagement && !frontdoorOnlyMode) {
|
|
85
|
+
throw new Error("Agentforce Conversation Client: sessionManagement requires frontdoorUrl-only mode");
|
|
86
|
+
}
|
|
87
|
+
const resolvedSessionManagement = frontdoorOnlyMode
|
|
88
|
+
? resolveSessionManagementConfig(sessionManagement ?? {})
|
|
89
|
+
: undefined;
|
|
90
|
+
const containerElement = resolveContainer(container);
|
|
91
|
+
if (!containerElement) {
|
|
92
|
+
throw new Error(`Agentforce Conversation Client: container not found: ${container}`);
|
|
93
|
+
}
|
|
94
|
+
return embedIntoContainer(containerElement, {
|
|
95
|
+
container,
|
|
96
|
+
salesforceOrigin,
|
|
97
|
+
appId,
|
|
98
|
+
frontdoorUrl,
|
|
99
|
+
sitePrefix,
|
|
100
|
+
agentforceClientConfig,
|
|
101
|
+
sessionManagement: resolvedSessionManagement,
|
|
102
|
+
onError,
|
|
103
|
+
onReady,
|
|
104
|
+
});
|
|
105
|
+
}
|
package/dist/frame.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AgentforceClientConfig } from "./types.js";
|
|
2
|
+
interface AccFrameElement extends HTMLElement {
|
|
3
|
+
configuration?: AgentforceClientConfig;
|
|
4
|
+
}
|
|
5
|
+
export declare function createAndMountFrame(container: HTMLElement, clientConfig: AgentforceClientConfig | undefined, loApp: HTMLElement): AccFrameElement;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../src/frame.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAkBzD,UAAU,eAAgB,SAAQ,WAAW;IAC5C,aAAa,CAAC,EAAE,sBAAsB,CAAC;CACvC;AAsFD,wBAAgB,mBAAmB,CAClC,SAAS,EAAE,WAAW,EACtB,YAAY,EAAE,sBAAsB,GAAG,SAAS,EAChD,KAAK,EAAE,WAAW,GAChB,eAAe,CAUjB"}
|
package/dist/frame.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { AGENTFORCE_CLIENT_ELEMENT_TAG, RenderingMode } from "./constants.js";
|
|
7
|
+
import { injectStyles } from "./styles.js";
|
|
8
|
+
const EVENTS = Object.freeze({
|
|
9
|
+
ACC_MAXIMIZE: "accmaximize",
|
|
10
|
+
ACC_MINIMIZE: "accminimize",
|
|
11
|
+
ACC_READY: "accready",
|
|
12
|
+
});
|
|
13
|
+
const FLOATING_DIMENSIONS = Object.freeze({
|
|
14
|
+
MINIMIZED_HEIGHT: "56px",
|
|
15
|
+
});
|
|
16
|
+
function toCssLength(value) {
|
|
17
|
+
return typeof value === "number" ? `${value}px` : value;
|
|
18
|
+
}
|
|
19
|
+
function normalizeRenderingMode(value) {
|
|
20
|
+
if (value === RenderingMode.INLINE || value === RenderingMode.FLOATING)
|
|
21
|
+
return value;
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
function getRenderingContext(clientConfig) {
|
|
25
|
+
const renderingConfig = clientConfig?.renderingConfig ?? {};
|
|
26
|
+
const mode = normalizeRenderingMode(renderingConfig.mode) ?? RenderingMode.FLOATING;
|
|
27
|
+
const isFloating = mode !== RenderingMode.INLINE;
|
|
28
|
+
return { mode, isFloating, renderingConfig };
|
|
29
|
+
}
|
|
30
|
+
function applyInlineSizing(container, { width, height }) {
|
|
31
|
+
if (width != null)
|
|
32
|
+
container.style.setProperty("--acc-width", toCssLength(width));
|
|
33
|
+
if (height != null) {
|
|
34
|
+
const h = toCssLength(height);
|
|
35
|
+
container.style.setProperty("--acc-height", h);
|
|
36
|
+
container.style.setProperty("--agentic-chat-container-height", h);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function createFrameElement() {
|
|
40
|
+
const el = document.createElement(AGENTFORCE_CLIENT_ELEMENT_TAG);
|
|
41
|
+
el.classList.add("acc-frame");
|
|
42
|
+
return el;
|
|
43
|
+
}
|
|
44
|
+
function applyFrameInitialState(el, context, container) {
|
|
45
|
+
el.style.opacity = "0";
|
|
46
|
+
el.style.pointerEvents = "none";
|
|
47
|
+
el.style.setProperty("--agentic-chat-container-height", "100vh");
|
|
48
|
+
if (context.isFloating) {
|
|
49
|
+
el.classList.add("floating");
|
|
50
|
+
el.classList.remove("maximize", "minimize");
|
|
51
|
+
el.classList.add("initial");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
el.classList.add("inline");
|
|
55
|
+
applyInlineSizing(container, context.renderingConfig);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function attachFrameResizeHandlers(el, container, isFloating, loApp) {
|
|
59
|
+
const onAccReady = () => {
|
|
60
|
+
el.style.opacity = "";
|
|
61
|
+
el.style.pointerEvents = "";
|
|
62
|
+
if (loApp) {
|
|
63
|
+
loApp.style.opacity = "";
|
|
64
|
+
loApp.style.pointerEvents = "";
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
el.addEventListener(EVENTS.ACC_READY, onAccReady);
|
|
68
|
+
if (!isFloating)
|
|
69
|
+
return;
|
|
70
|
+
const onMaximize = () => {
|
|
71
|
+
el.classList.remove("initial", "minimize");
|
|
72
|
+
el.classList.add("maximize");
|
|
73
|
+
};
|
|
74
|
+
const onMinimize = () => {
|
|
75
|
+
el.classList.remove("maximize");
|
|
76
|
+
el.classList.add("minimize");
|
|
77
|
+
container.style.setProperty("--agentic-chat-container-height", FLOATING_DIMENSIONS.MINIMIZED_HEIGHT);
|
|
78
|
+
};
|
|
79
|
+
el.addEventListener(EVENTS.ACC_MAXIMIZE, onMaximize);
|
|
80
|
+
el.addEventListener(EVENTS.ACC_MINIMIZE, onMinimize);
|
|
81
|
+
}
|
|
82
|
+
export function createAndMountFrame(container, clientConfig, loApp) {
|
|
83
|
+
injectStyles();
|
|
84
|
+
const config = { ...(clientConfig ?? {}) };
|
|
85
|
+
const context = getRenderingContext(config);
|
|
86
|
+
const el = createFrameElement();
|
|
87
|
+
el.configuration = config;
|
|
88
|
+
applyFrameInitialState(el, context, container);
|
|
89
|
+
attachFrameResizeHandlers(el, container, context.isFloating, loApp);
|
|
90
|
+
container.appendChild(el);
|
|
91
|
+
return el;
|
|
92
|
+
}
|