@salesforce/agentforce-conversation-client 11.67.0 → 11.68.0

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 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.iframe.error"
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 | Argument shape |
401
- | --------- | --------------------------------------------------------------- | ------------------------------------------------------------------------ |
402
- | `onReady` | Lightning Out application has finished loading | `detail: unknown` — the raw detail from the `lo.application.ready` event |
403
- | `onError` | An application-level or iframe-level Lightning Out error occurs | `{ type: "lo.application.error" \| "lo.iframe.error", detail: unknown }` |
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 | Required | Description |
441
- | ----------------------------------------------------------- | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
442
- | `options.container` | `string \| HTMLElement` | Yes | CSS selector or HTMLElement to embed into |
443
- | `options.salesforceOrigin` | `string` | No | Salesforce org origin URL (use when hosted in a Salesforce org). At least one of `salesforceOrigin` or `frontdoorUrl` is required |
444
- | `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 |
445
- | `options.frontdoorUrl` | `string` | No | Frontdoor URL for authentication (use when embedding outside Salesforce). At least one of `salesforceOrigin` or `frontdoorUrl` is required |
446
- | `options.sitePrefix` | `string` | No | Experience Cloud site prefix used by Lightning Out (for example, `"/my-site"`). Set as `site-prefix` on the LO app |
447
- | `options.agentforceClientConfig` | `AgentforceClientConfig` | No | Configuration for the Agentforce client (see sub-properties below) |
448
- | `agentforceClientConfig.agentId` | `string` | Yes\* | The agent to load — required in practice, will not work without it |
449
- | `agentforceClientConfig.agentLabel` | `string` | No | Display name shown in the chat header. Falls back to the agent's configured name if omitted |
450
- | `agentforceClientConfig.styleTokens` | `Record<string, string>` | No | Theme colors and style overrides (see Style Tokens section) |
451
- | `agentforceClientConfig.renderingConfig.mode` | `"inline" \| "floating"` | No | Rendering mode. Defaults to `"floating"` |
452
- | `agentforceClientConfig.renderingConfig.width` | `string \| number` | No | Width of the inline frame. Number values are treated as `px`, strings as CSS values (e.g. `"100%"`) |
453
- | `agentforceClientConfig.renderingConfig.height` | `string \| number` | No | Height of the inline frame. Number values are treated as `px`, strings as CSS values (e.g. `"100%"`) |
454
- | `agentforceClientConfig.renderingConfig.headerEnabled` | `boolean` | No | Show or hide the chat header bar. Defaults to hidden in inline mode. Set `true` to show it |
455
- | `agentforceClientConfig.renderingConfig.showHeaderIcon` | `boolean` | No | Show or hide the icon in the header. Omit or set `false` to hide |
456
- | `agentforceClientConfig.renderingConfig.showAvatar` | `boolean` | No | Show or hide avatars in message rows. Defaults to `true` |
457
- | `agentforceClientConfig.renderingConfig.agentAvatarUrl` | `string` | No | Custom avatar image URL for agent messages |
458
- | `agentforceClientConfig.renderingConfig.agentAvatarAltText` | `string` | No | Alt text for the custom agent avatar image |
459
- | `agentforceClientConfig.renderingConfig.headerIconName` | `string` | No | SLDS icon name for the header (e.g. `"utility:agent"`) |
460
- | `agentforceClientConfig.renderingConfig.headerIconSize` | `string` | No | Header icon size |
461
- | `agentforceClientConfig.renderingConfig.headerImageUrl` | `string` | No | Custom header logo image URL |
462
- | `agentforceClientConfig.renderingConfig.headerImageAlt` | `string` | No | Alt text for the custom header logo image |
463
- | `agentforceClientConfig.channel` | `string` | No | Channel identifier for analytics/instrumentation |
464
- | `agentforceClientConfig.messageInputPlaceholderText` | `string` | No | Custom placeholder text for the message input field |
465
- | `agentforceClientConfig.isHistoryIntroNeeded` | `boolean` | No | Show a conversation history intro block |
466
- | `agentforceClientConfig.isFileBased` | `boolean` | No | Indicates the selected agent is file-based; forwarded to `accSdkWrapper`/`accSdk` for file-agent-specific behavior |
467
- | `agentforceClientConfig.typewriterConfig` | `TypewriterConfig` | No | Customize typewriter animation (see TypewriterConfig type below) |
468
- | `agentforceClientConfig.floatingButtonLabel` | `string` | No | Label text for the floating action button (floating mode) |
469
- | `agentforceClientConfig.floatingButtonIcon` | `string` | No | SLDS icon name for the FAB (e.g. `"utility:agent"`) |
470
- | `agentforceClientConfig.floatingButtonImage` | `string` | No | URL to an external image (PNG/SVG) shown in place of the FAB icon |
471
- | `agentforceClientConfig.floatingButtonImageAlt` | `string` | No | Alt text for the floating button image |
472
- | `agentforceClientConfig.iconPosition` | `"left" \| "right"` | No | Icon position in the FAB. Defaults to `"left"` |
473
- | `options.onReady` | `AgentforceReadyHandler` | No | Callback invoked when the Lightning Out application is ready. Receives the event detail |
474
- | `options.onError` | `AgentforceErrorHandler` | No | Callback invoked on Lightning Out errors. Receives `{ type, detail }` with the error source and payload |
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 | Description |
479
- | --------------------- | ------------------------- | --------------------------------- |
480
- | `loApp` | `LightningOutApplication` | The Lightning Out 2.0 application |
481
- | `chatClientComponent` | `HTMLElement` | The chat client component element |
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: LightningOutApplication;
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
+ });
@@ -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
+ }
@@ -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
+ }