@waniwani/sdk 0.15.2 → 0.17.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.
@@ -642,55 +642,287 @@ declare function isOpenAI(): boolean;
642
642
  */
643
643
  declare function isMCPApps(): boolean;
644
644
 
645
+ interface SearchResult {
646
+ source: string;
647
+ heading: string;
648
+ content: string;
649
+ score: number;
650
+ metadata?: Record<string, string>;
651
+ }
652
+ interface KbSearchTrace {
653
+ query: string;
654
+ resultCount: number;
655
+ results: Array<Pick<SearchResult, "source" | "heading" | "score">>;
656
+ }
657
+
658
+ /**
659
+ * Every event name in the typed taxonomy, as a runtime list. Single source of
660
+ * truth for {@link EventType} and for surfaces that need to recognize
661
+ * first-class names at runtime (the widget transport passes these through
662
+ * verbatim instead of namespacing them as custom widget events).
663
+ */
664
+ declare const EVENT_TYPES: readonly ["session.started", "page.viewed", "tool.called", "quote.requested", "quote.succeeded", "quote.failed", "link.clicked", "purchase.completed", "widget_render", "user.identified", "price_shown", "prices_compared", "option_selected", "lead_qualified", "converted"];
665
+ type EventType = (typeof EVENT_TYPES)[number];
666
+ /**
667
+ * Properties for `page.viewed` — emitted once when the chat widget initializes
668
+ * on a host page (the top of the funnel). Attributed to the anonymous
669
+ * `visitorId` (mapped to `externalUserId`), never to a session: a page view
670
+ * must not create a session, otherwise sessions would equal page views and the
671
+ * "landed → started a conversation" funnel collapses.
672
+ */
673
+ interface PageViewedProperties {
674
+ /** Full URL of the host page the widget loaded on. */
675
+ url?: string;
676
+ /** Referrer of the host page, if any. */
677
+ referrer?: string;
678
+ /** Embed mode the widget initialized in. */
679
+ mode?: "inline" | "floating";
680
+ /** Resolved device type from the visitor context. */
681
+ deviceType?: "mobile" | "tablet" | "desktop";
682
+ /** Primary browser language (BCP-47). */
683
+ language?: string;
684
+ /** IANA timezone of the visitor. */
685
+ timezone?: string;
686
+ }
687
+ interface ToolCalledProperties {
688
+ name?: string;
689
+ type?: "pricing" | "product_info" | "availability" | "support" | "other";
690
+ /** Retrieval traces for kb.search() calls made inside this tool handler. */
691
+ kbSearch?: KbSearchTrace[];
692
+ }
693
+ interface QuoteSucceededProperties {
694
+ amount?: number;
695
+ currency?: string;
696
+ }
697
+ interface LinkClickedProperties {
698
+ url?: string;
699
+ }
700
+ interface PurchaseCompletedProperties {
701
+ amount?: number;
702
+ currency?: string;
703
+ }
704
+ interface PriceShownProperties {
705
+ amount: number;
706
+ currency: string;
707
+ itemId?: string;
708
+ label?: string;
709
+ }
710
+ interface ComparedPriceOption {
711
+ id: string;
712
+ amount: number;
713
+ currency: string;
714
+ }
715
+ interface PricesComparedProperties {
716
+ options: ComparedPriceOption[];
717
+ }
718
+ interface OptionSelectedProperties {
719
+ id: string;
720
+ amount: number;
721
+ currency: string;
722
+ }
723
+ /**
724
+ * Properties for `lead_qualified` — your code declaring that a person met your
725
+ * qualification bar (finished the qualifying questions, requested a demo,
726
+ * matched your target profile). Sharing an email mid-conversation is
727
+ * `identify(userId, { email })`, not a qualified lead.
728
+ */
729
+ interface LeadQualifiedProperties {
730
+ /**
731
+ * Your own lead id (e.g. the record id your lead-gen API returns). The
732
+ * strongest dedup key and the stable reference back to your system.
733
+ */
734
+ externalId?: string;
735
+ /** The lead's email, if known. */
736
+ email?: string;
737
+ /** The lead's name, if known. */
738
+ name?: string;
739
+ }
740
+ interface ConvertedProperties {
741
+ amount: number;
742
+ currency: string;
743
+ /** When the conversion actually happened — for backdated off-platform sales. */
744
+ occurredAt?: string;
745
+ }
746
+ interface TrackingContext {
747
+ /**
748
+ * MCP request metadata passed through to the API.
749
+ *
750
+ * Location varies by MCP library:
751
+ * - `@vercel/mcp-handler`: `extra._meta`
752
+ * - `@modelcontextprotocol/sdk`: `request.params._meta`
753
+ */
754
+ meta?: Record<string, unknown>;
755
+ /** Legacy metadata field supported for backward compatibility. */
756
+ metadata?: Record<string, unknown>;
757
+ /** Optional explicit correlation fields. */
758
+ sessionId?: string;
759
+ traceId?: string;
760
+ requestId?: string;
761
+ correlationId?: string;
762
+ externalUserId?: string;
763
+ /**
764
+ * Anonymous visitor id (the analytics "device id"). Counts as identity on
765
+ * its own: the ingest API accepts `sessionId`, `externalUserId`, or
766
+ * `visitorId`. The chat widget uses it for pre-session events like
767
+ * `page.viewed`.
768
+ */
769
+ visitorId?: string;
770
+ /** Optional explicit envelope fields. */
771
+ eventId?: string;
772
+ timestamp?: string | Date;
773
+ source?: string;
774
+ }
775
+ /**
776
+ * Modern tracking shape (preferred).
777
+ */
778
+ interface BaseTrackEvent extends TrackingContext {
779
+ event: EventType;
780
+ properties?: Record<string, unknown>;
781
+ }
782
+ type TrackEvent = ({
783
+ event: "session.started";
784
+ } & BaseTrackEvent) | ({
785
+ event: "page.viewed";
786
+ properties?: PageViewedProperties;
787
+ } & BaseTrackEvent) | ({
788
+ event: "tool.called";
789
+ properties?: ToolCalledProperties;
790
+ } & BaseTrackEvent) | ({
791
+ event: "quote.requested";
792
+ } & BaseTrackEvent) | ({
793
+ event: "quote.succeeded";
794
+ properties?: QuoteSucceededProperties;
795
+ } & BaseTrackEvent) | ({
796
+ event: "quote.failed";
797
+ } & BaseTrackEvent) | ({
798
+ event: "link.clicked";
799
+ properties?: LinkClickedProperties;
800
+ } & BaseTrackEvent) | ({
801
+ event: "purchase.completed";
802
+ properties?: PurchaseCompletedProperties;
803
+ } & BaseTrackEvent) | ({
804
+ event: "user.identified";
805
+ } & BaseTrackEvent) | ({
806
+ event: "widget_render";
807
+ } & BaseTrackEvent) | ({
808
+ event: "price_shown";
809
+ properties?: PriceShownProperties;
810
+ } & BaseTrackEvent) | ({
811
+ event: "prices_compared";
812
+ properties?: PricesComparedProperties;
813
+ } & BaseTrackEvent) | ({
814
+ event: "option_selected";
815
+ properties?: OptionSelectedProperties;
816
+ } & BaseTrackEvent) | ({
817
+ event: "lead_qualified";
818
+ properties?: LeadQualifiedProperties;
819
+ } & BaseTrackEvent) | ({
820
+ event: "converted";
821
+ properties?: ConvertedProperties;
822
+ } & BaseTrackEvent);
823
+ /**
824
+ * Legacy tracking shape supported for existing integrations.
825
+ */
826
+ interface LegacyTrackEvent extends TrackingContext {
827
+ eventType: EventType;
828
+ properties?: Record<string, unknown>;
829
+ toolName?: string;
830
+ toolType?: ToolCalledProperties["type"];
831
+ quoteAmount?: number;
832
+ quoteCurrency?: string;
833
+ linkUrl?: string;
834
+ purchaseAmount?: number;
835
+ purchaseCurrency?: string;
836
+ }
837
+ /**
838
+ * Public track input accepted by `client.track()`.
839
+ */
840
+ type TrackInput = TrackEvent | LegacyTrackEvent;
841
+ interface RevenuePriceShownInput extends TrackingContext, PriceShownProperties {
842
+ }
843
+ interface RevenuePricesComparedInput extends TrackingContext, PricesComparedProperties {
844
+ }
845
+ interface RevenueOptionSelectedInput extends TrackingContext, OptionSelectedProperties {
846
+ }
847
+ /**
848
+ * Input for `track.leadQualified()` — identity only (`externalId`, `email`,
849
+ * `name`) plus the shared tracking context. There is no acquisition-source
850
+ * property; the envelope `source` (the origin channel) is set automatically.
851
+ */
852
+ interface RevenueLeadQualifiedInput extends TrackingContext, LeadQualifiedProperties {
853
+ }
854
+ interface RevenueConvertedInput extends TrackingContext, ConvertedProperties {
855
+ }
645
856
  /**
646
- * Opt-in toggles for the noisy auto-capture event types. Each defaults to
647
- * `false` so widget owners declare intent before the SDK starts emitting
648
- * coarse, high-volume events. Always-on capture covers widget_render,
649
- * widget_error, widget_link_click, and the labelled `data-ww-step` /
650
- * `data-ww-conversion` clicks.
857
+ * Revenue-oriented helpers, flat on `client.track.*` (e.g.
858
+ * `client.track.priceShown()`, `client.track.converted()`). Decoupled from
859
+ * product primitives each maps to a typed first-class revenue event.
860
+ */
861
+ interface RevenueTrackingApi {
862
+ priceShown: (input: RevenuePriceShownInput) => Promise<{
863
+ eventId: string;
864
+ }>;
865
+ pricesCompared: (input: RevenuePricesComparedInput) => Promise<{
866
+ eventId: string;
867
+ }>;
868
+ optionSelected: (input: RevenueOptionSelectedInput) => Promise<{
869
+ eventId: string;
870
+ }>;
871
+ leadQualified: (input?: RevenueLeadQualifiedInput) => Promise<{
872
+ eventId: string;
873
+ }>;
874
+ converted: (input: RevenueConvertedInput) => Promise<{
875
+ eventId: string;
876
+ }>;
877
+ }
878
+ /**
879
+ * `client.track` — callable for generic events (`track(event)`), with the
880
+ * revenue helpers attached flat: `track.priceShown()`, `track.leadQualified()`,
881
+ * `track.converted()`, etc.
651
882
  */
652
- interface AutoCaptureToggles {
653
- click?: boolean;
654
- scroll?: boolean;
655
- formField?: boolean;
656
- formSubmit?: boolean;
883
+ interface TrackFn extends RevenueTrackingApi {
884
+ (event: TrackInput): Promise<{
885
+ eventId: string;
886
+ }>;
657
887
  }
658
888
 
659
889
  interface BaseUseWaniwaniOptions {
660
890
  /**
661
891
  * JWT widget token for authenticating directly with the Waniwani backend.
662
892
  * If omitted, the hook resolves from tool response metadata
663
- * (`toolResponseMetadata.waniwani` or `toolResponseMetadata._meta.waniwani`).
893
+ * (`_meta["waniwani/widget"].token`).
664
894
  */
665
895
  token?: string;
666
896
  /**
667
897
  * Session ID to use for event correlation.
668
898
  * If omitted, the hook resolves from tool response metadata
669
- * (`toolResponseMetadata.waniwani.sessionId`), then falls back to a random UUID.
899
+ * (`_meta["waniwani/widget"].sessionId`), then falls back to a random UUID
900
+ * so the widget's own events still group together.
670
901
  */
671
902
  sessionId?: string;
672
903
  /**
673
- * Additional metadata to include with every tracked event.
904
+ * Additional fields merged into every tracked event's envelope metadata.
674
905
  */
675
906
  metadata?: Record<string, unknown>;
676
907
  /**
677
- * Opt-in toggles for noisy auto-capture event types. Default: all off.
678
- * Always-on capture: widget_render, widget_error, widget_link_click,
679
- * `data-ww-step` / `data-ww-conversion` clicks.
908
+ * Tool response metadata from a host you already have a connection to
909
+ * (e.g. an MCP-Apps / skybridge host exposing `useToolInfo().responseMetadata`).
680
910
  *
681
- * @example
682
- * useWaniwani({ capture: { click: true, scroll: true } });
911
+ * The hook resolves its config (`endpoint`, `source`, `token`, `sessionId`)
912
+ * from `_meta["waniwani/widget"]` in this object. The hook never opens a host
913
+ * connection of its own — your app owns the host bridge and passes the
914
+ * metadata in. Ignored when an explicit `endpoint` is passed.
683
915
  */
684
- capture?: AutoCaptureToggles;
916
+ toolResponseMetadata?: Record<string, unknown> | null;
685
917
  }
686
918
  /**
687
- * Context-driven options: `endpoint` and `source` are resolved from
688
- * `WidgetProvider`'s `toolResponseMetadata.waniwani`. `source` may be
689
- * overridden explicitly.
919
+ * Metadata-driven options: `endpoint` and `source` are resolved from the
920
+ * `toolResponseMetadata` you pass (`_meta["waniwani/widget"]`). `source` may
921
+ * be overridden explicitly.
690
922
  */
691
923
  interface ContextDrivenOptions extends BaseUseWaniwaniOptions {
692
924
  endpoint?: undefined;
693
- /** Optional override; otherwise resolved from context. */
925
+ /** Optional override; otherwise resolved from `toolResponseMetadata`. */
694
926
  source?: string;
695
927
  }
696
928
  /**
@@ -704,8 +936,8 @@ interface ExplicitEndpointOptions extends BaseUseWaniwaniOptions {
704
936
  source: string;
705
937
  }
706
938
  /**
707
- * Options for the useWaniwani hook. Either rely on `WidgetProvider`
708
- * context (omit `endpoint`) or pass `endpoint` + `source` explicitly.
939
+ * Options for the useWaniwani hook. Either rely on the widget host
940
+ * (omit `endpoint`) or pass `endpoint` + `source` explicitly.
709
941
  */
710
942
  type UseWaniwaniOptions = ContextDrivenOptions | ExplicitEndpointOptions;
711
943
  /**
@@ -714,44 +946,57 @@ type UseWaniwaniOptions = ContextDrivenOptions | ExplicitEndpointOptions;
714
946
  interface WaniwaniWidget {
715
947
  /**
716
948
  * The session ID stamped on every event this widget emits, so hosts can
717
- * correlate widget activity with server-side tracking without reaching into
718
- * the internal `_meta` shape.
949
+ * correlate widget activity with server-side tracking.
719
950
  *
720
- * Resolved from (1) the explicit `sessionId` option, (2)
721
- * `toolResponseMetadata.waniwani.sessionId`, else a random UUID generated
722
- * on mount. `undefined` until the widget initializes (same lifecycle as the
723
- * tracking methods) and when no config resolves (no-op widget).
951
+ * Resolved from (1) the explicit `sessionId` option, (2) the widget config
952
+ * injected by `withWaniwani` (`_meta["waniwani/widget"].sessionId`), else a
953
+ * random UUID generated on mount. `undefined` until the widget initializes
954
+ * and when no config resolves (no-op widget).
724
955
  */
725
956
  readonly sessionId?: string;
726
- /** Tie all subsequent widget events to this user. */
727
- identify(userId: string, traits?: Record<string, unknown>): void;
728
- /** Record a funnel step. Auto-incrementing sequence per session. */
729
- step(name: string, meta?: Record<string, unknown>): void;
730
- /** Record a generic custom event. */
731
- track(event: string, properties?: Record<string, unknown>): void;
732
- /** Record a conversion event. */
733
- conversion(name: string, data?: Record<string, unknown>): void;
957
+ /**
958
+ * Track a typed event. The exact same surface as the server client:
959
+ * `track({ event: "quote.succeeded", properties })`,
960
+ * `track.priceShown({ amount, currency })`, `track.converted({ ... })`.
961
+ * Identity (session, trace, user) is stamped automatically.
962
+ */
963
+ track: TrackFn;
964
+ /** Tie all subsequent widget events to this user (emits `user.identified`). */
965
+ identify(userId: string, traits?: Record<string, unknown>): Promise<{
966
+ eventId: string;
967
+ }>;
968
+ /** Flush buffered events immediately instead of waiting for the timer. */
969
+ flush(): Promise<void>;
734
970
  }
735
971
  /**
736
- * React hook for Waniwani widget tracking.
972
+ * React hook for tracking from inside an MCP-app widget. Returns the same
973
+ * `track` surface as the server client, with session identity stamped
974
+ * automatically from the config `withWaniwani` injects into tool responses.
737
975
  *
738
- * Auto-captures DOM events (clicks, link clicks, errors, scrolls, form
739
- * interactions) and provides manual tracking methods. Returns a singleton
740
- * instance shared across all consumers.
976
+ * The hook never opens a host connection of its own. Supply config one of
977
+ * two ways:
978
+ * 1. Explicit `endpoint` + `source` (plus optional `token` / `sessionId`)
979
+ * 2. `toolResponseMetadata` — the `_meta` object your host already exposes
980
+ * (e.g. skybridge's `useToolInfo().responseMetadata`); the hook reads
981
+ * `_meta["waniwani/widget"]` from it
741
982
  *
742
- * Config resolution order:
743
- * 1. Explicit `endpoint` / `token` / `sessionId` / `source` options
744
- * 2. `toolResponseMetadata.waniwani` from WidgetProvider context
745
- * 3. No-op if `endpoint` cannot be resolved or `source` is unknown
983
+ * No-op when neither resolves an `endpoint` and a `source`.
746
984
  *
747
985
  * @example
748
986
  * ```tsx
749
987
  * function MyWidget() {
750
- * const wani = useWaniwani();
751
- * // Auto-captures clicks, links, errors, scrolls, forms
752
- * // Optionally call wani.track("custom_event") for manual events
753
- * // Read wani.sessionId to correlate with server-side tracking
754
- * return <a href="https://example.com">Visit</a>;
988
+ * const { responseMetadata } = useToolInfo();
989
+ * const wani = useWaniwani({ toolResponseMetadata: responseMetadata });
990
+ * // wani.sessionId correlates with server-side tracking
991
+ * return (
992
+ * <button
993
+ * onClick={() =>
994
+ * wani.track.optionSelected({ id: "pro", amount: 49, currency: "EUR" })
995
+ * }
996
+ * >
997
+ * Choose Pro
998
+ * </button>
999
+ * );
755
1000
  * }
756
1001
  * ```
757
1002
  */