@usecrow/ui 0.1.40 → 0.1.42

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/dist/index.d.cts CHANGED
@@ -243,8 +243,19 @@ interface CrowWidgetProps {
243
243
  navigate?: (path: string) => void;
244
244
  /** Callback fired when a server-side tool completes, with the tool name and full result data */
245
245
  onToolResult?: (toolName: string, result: Record<string, unknown>) => void;
246
+ /**
247
+ * Async function that returns a JWT for user identity verification.
248
+ * Called on mount and automatically on 401 (token refresh).
249
+ * Preferred over onIdentify for simpler integration.
250
+ */
251
+ getIdentityToken?: () => Promise<string>;
252
+ /**
253
+ * Page context data sent with every message. Reactive — updates whenever
254
+ * the object reference changes. Replaces window.crow('setContext', ...).
255
+ */
256
+ context?: Record<string, unknown>;
246
257
  }
247
- declare function CrowWidget({ productId, apiUrl, variant, styles: propStyles, previewMode, showThinking: showThinkingProp, agentName: agentNameProp, welcomeMessage: welcomeMessageProp, onReady, onIdentify, tools, navigate, onToolResult, }: CrowWidgetProps): react_jsx_runtime.JSX.Element;
258
+ declare function CrowWidget({ productId, apiUrl, variant, styles: propStyles, previewMode, showThinking: showThinkingProp, agentName: agentNameProp, welcomeMessage: welcomeMessageProp, onReady, onIdentify, tools, navigate, onToolResult, getIdentityToken, context, }: CrowWidgetProps): react_jsx_runtime.JSX.Element;
248
259
 
249
260
  /**
250
261
  * Shared TypeScript interfaces for the widget and copilot
@@ -399,8 +410,33 @@ interface CrowCopilotProps {
399
410
  * ```
400
411
  */
401
412
  toolRenderers?: ToolRenderers;
413
+ /**
414
+ * Async function that returns a JWT for user identity verification.
415
+ * Called on mount and automatically on 401 (token refresh).
416
+ * Replaces the need for a separate component calling window.crow('identify').
417
+ *
418
+ * @example
419
+ * ```tsx
420
+ * getIdentityToken={async () => {
421
+ * const res = await fetch('/api/crow-token');
422
+ * const { token } = await res.json();
423
+ * return token;
424
+ * }}
425
+ * ```
426
+ */
427
+ getIdentityToken?: () => Promise<string>;
428
+ /**
429
+ * Page context data sent with every message. Reactive — updates whenever
430
+ * the object reference changes. Replaces window.crow('setContext', ...).
431
+ *
432
+ * @example
433
+ * ```tsx
434
+ * context={{ cmsContentItemId: "abc-123", agentBlogId: "def-456" }}
435
+ * ```
436
+ */
437
+ context?: Record<string, unknown>;
402
438
  }
403
- declare function CrowCopilot({ productId, apiUrl, variant, title, agentName: agentNameProp, welcomeMessage: welcomeMessageProp, position, width, defaultOpen, showClose, onClose, styles: propStyles, previewMode, className, onReady, navigate, onToolResult, toolRenderers, }: CrowCopilotProps): react_jsx_runtime.JSX.Element;
439
+ declare function CrowCopilot({ productId, apiUrl, variant, title, agentName: agentNameProp, welcomeMessage: welcomeMessageProp, position, width, defaultOpen, showClose, onClose, styles: propStyles, previewMode, className, onReady, navigate, onToolResult, toolRenderers, getIdentityToken, context, }: CrowCopilotProps): react_jsx_runtime.JSX.Element;
404
440
 
405
441
  interface CrowProviderProps extends CrowClientConfig {
406
442
  children: React.ReactNode;
@@ -742,6 +778,8 @@ interface UseCopilotStylesResult {
742
778
  error: Error | null;
743
779
  /** Agent name from product config */
744
780
  agentName: string;
781
+ /** Whether browser_use is enabled for this product */
782
+ browserUseEnabled: boolean;
745
783
  /** Whether page navigation is enabled for this product */
746
784
  pageNavigationEnabled: boolean;
747
785
  /** Route definitions for page navigation */
package/dist/index.d.ts CHANGED
@@ -243,8 +243,19 @@ interface CrowWidgetProps {
243
243
  navigate?: (path: string) => void;
244
244
  /** Callback fired when a server-side tool completes, with the tool name and full result data */
245
245
  onToolResult?: (toolName: string, result: Record<string, unknown>) => void;
246
+ /**
247
+ * Async function that returns a JWT for user identity verification.
248
+ * Called on mount and automatically on 401 (token refresh).
249
+ * Preferred over onIdentify for simpler integration.
250
+ */
251
+ getIdentityToken?: () => Promise<string>;
252
+ /**
253
+ * Page context data sent with every message. Reactive — updates whenever
254
+ * the object reference changes. Replaces window.crow('setContext', ...).
255
+ */
256
+ context?: Record<string, unknown>;
246
257
  }
247
- declare function CrowWidget({ productId, apiUrl, variant, styles: propStyles, previewMode, showThinking: showThinkingProp, agentName: agentNameProp, welcomeMessage: welcomeMessageProp, onReady, onIdentify, tools, navigate, onToolResult, }: CrowWidgetProps): react_jsx_runtime.JSX.Element;
258
+ declare function CrowWidget({ productId, apiUrl, variant, styles: propStyles, previewMode, showThinking: showThinkingProp, agentName: agentNameProp, welcomeMessage: welcomeMessageProp, onReady, onIdentify, tools, navigate, onToolResult, getIdentityToken, context, }: CrowWidgetProps): react_jsx_runtime.JSX.Element;
248
259
 
249
260
  /**
250
261
  * Shared TypeScript interfaces for the widget and copilot
@@ -399,8 +410,33 @@ interface CrowCopilotProps {
399
410
  * ```
400
411
  */
401
412
  toolRenderers?: ToolRenderers;
413
+ /**
414
+ * Async function that returns a JWT for user identity verification.
415
+ * Called on mount and automatically on 401 (token refresh).
416
+ * Replaces the need for a separate component calling window.crow('identify').
417
+ *
418
+ * @example
419
+ * ```tsx
420
+ * getIdentityToken={async () => {
421
+ * const res = await fetch('/api/crow-token');
422
+ * const { token } = await res.json();
423
+ * return token;
424
+ * }}
425
+ * ```
426
+ */
427
+ getIdentityToken?: () => Promise<string>;
428
+ /**
429
+ * Page context data sent with every message. Reactive — updates whenever
430
+ * the object reference changes. Replaces window.crow('setContext', ...).
431
+ *
432
+ * @example
433
+ * ```tsx
434
+ * context={{ cmsContentItemId: "abc-123", agentBlogId: "def-456" }}
435
+ * ```
436
+ */
437
+ context?: Record<string, unknown>;
402
438
  }
403
- declare function CrowCopilot({ productId, apiUrl, variant, title, agentName: agentNameProp, welcomeMessage: welcomeMessageProp, position, width, defaultOpen, showClose, onClose, styles: propStyles, previewMode, className, onReady, navigate, onToolResult, toolRenderers, }: CrowCopilotProps): react_jsx_runtime.JSX.Element;
439
+ declare function CrowCopilot({ productId, apiUrl, variant, title, agentName: agentNameProp, welcomeMessage: welcomeMessageProp, position, width, defaultOpen, showClose, onClose, styles: propStyles, previewMode, className, onReady, navigate, onToolResult, toolRenderers, getIdentityToken, context, }: CrowCopilotProps): react_jsx_runtime.JSX.Element;
404
440
 
405
441
  interface CrowProviderProps extends CrowClientConfig {
406
442
  children: React.ReactNode;
@@ -742,6 +778,8 @@ interface UseCopilotStylesResult {
742
778
  error: Error | null;
743
779
  /** Agent name from product config */
744
780
  agentName: string;
781
+ /** Whether browser_use is enabled for this product */
782
+ browserUseEnabled: boolean;
745
783
  /** Whether page navigation is enabled for this product */
746
784
  pageNavigationEnabled: boolean;
747
785
  /** Route definitions for page navigation */
package/dist/index.js CHANGED
@@ -1279,6 +1279,9 @@ function useCopilotStyles({
1279
1279
  const [agentName, setAgentName] = useState(
1280
1280
  styleCache.get(key)?.agentName || "Assistant"
1281
1281
  );
1282
+ const [browserUseEnabled, setBrowserUseEnabled] = useState(
1283
+ styleCache.get(key)?.browserUseEnabled || false
1284
+ );
1282
1285
  const [pageNavigationEnabled, setPageNavigationEnabled] = useState(
1283
1286
  styleCache.get(key)?.pageNavigationEnabled || false
1284
1287
  );
@@ -1302,6 +1305,7 @@ function useCopilotStyles({
1302
1305
  styleCache.set(key, config);
1303
1306
  setDbStyles(config.copilotStyles);
1304
1307
  setAgentName(config.agentName || "Assistant");
1308
+ setBrowserUseEnabled(config.browserUseEnabled || false);
1305
1309
  setPageNavigationEnabled(config.pageNavigationEnabled || false);
1306
1310
  setPageNavigationRoutes(config.pageNavigationRoutes || []);
1307
1311
  setPersistAnonymousConversations(config.persistAnonymousConversations ?? true);
@@ -1320,6 +1324,7 @@ function useCopilotStyles({
1320
1324
  if (cached) {
1321
1325
  setDbStyles(cached.copilotStyles);
1322
1326
  setAgentName(cached.agentName || "Assistant");
1327
+ setBrowserUseEnabled(cached.browserUseEnabled || false);
1323
1328
  setPageNavigationEnabled(cached.pageNavigationEnabled || false);
1324
1329
  setPageNavigationRoutes(cached.pageNavigationRoutes || []);
1325
1330
  setPersistAnonymousConversations(cached.persistAnonymousConversations ?? true);
@@ -1337,6 +1342,7 @@ function useCopilotStyles({
1337
1342
  isLoading,
1338
1343
  error,
1339
1344
  agentName,
1345
+ browserUseEnabled,
1340
1346
  pageNavigationEnabled,
1341
1347
  pageNavigationRoutes,
1342
1348
  persistAnonymousConversations,
@@ -1822,7 +1828,14 @@ function ReasoningTrace({
1822
1828
  return /* @__PURE__ */ jsx("div", { className: "crow-flex crow-justify-start crow-mb-2 crow-w-full", children: /* @__PURE__ */ jsxs("div", { className: "crow-w-full crow-space-y-1.5", children: [
1823
1829
  isWaiting && !hasThinking && /* @__PURE__ */ jsx(WaitingIndicator, {}),
1824
1830
  hasThinking && /* @__PURE__ */ jsx(ThinkingBlock, { thinking, isComplete }),
1825
- toolCalls.map((tool) => /* @__PURE__ */ jsx(ToolCallBlock, { toolCall: tool, toolRenderers }, tool.id))
1831
+ toolCalls.map((tool) => /* @__PURE__ */ jsx(
1832
+ ToolCallBlock,
1833
+ {
1834
+ toolCall: tool,
1835
+ toolRenderers
1836
+ },
1837
+ tool.id
1838
+ ))
1826
1839
  ] }) });
1827
1840
  }
1828
1841
  function WaitingIndicator() {
@@ -2898,7 +2911,9 @@ function CrowWidget({
2898
2911
  onIdentify,
2899
2912
  tools,
2900
2913
  navigate,
2901
- onToolResult
2914
+ onToolResult,
2915
+ getIdentityToken,
2916
+ context
2902
2917
  }) {
2903
2918
  const {
2904
2919
  styles,
@@ -3076,6 +3091,35 @@ function CrowWidget({
3076
3091
  onIdentify(identify);
3077
3092
  }
3078
3093
  }, [isLoadingStyles, onIdentify]);
3094
+ useEffect(() => {
3095
+ if (!getIdentityToken || isLoadingStyles) return;
3096
+ let cancelled = false;
3097
+ const identify = async () => {
3098
+ try {
3099
+ const token = await getIdentityToken();
3100
+ if (!cancelled && token) {
3101
+ window.crow?.("identify", { token });
3102
+ }
3103
+ } catch (e) {
3104
+ console.error("[Crow] getIdentityToken failed:", e);
3105
+ }
3106
+ };
3107
+ identify();
3108
+ const handleRefresh = () => identify();
3109
+ window.addEventListener("crow:token-refresh-needed", handleRefresh);
3110
+ return () => {
3111
+ cancelled = true;
3112
+ window.removeEventListener("crow:token-refresh-needed", handleRefresh);
3113
+ };
3114
+ }, [getIdentityToken, isLoadingStyles]);
3115
+ useEffect(() => {
3116
+ if (typeof window === "undefined") return;
3117
+ if (context && Object.keys(context).length > 0) {
3118
+ window.__crow_page_context = context;
3119
+ } else {
3120
+ window.__crow_page_context = void 0;
3121
+ }
3122
+ }, [context]);
3079
3123
  const handleBrowserConfirmation = useCallback(
3080
3124
  (instruction) => {
3081
3125
  return new Promise((resolve) => {
@@ -3437,12 +3481,15 @@ function CrowCopilot({
3437
3481
  onReady,
3438
3482
  navigate,
3439
3483
  onToolResult,
3440
- toolRenderers
3484
+ toolRenderers,
3485
+ getIdentityToken,
3486
+ context
3441
3487
  }) {
3442
3488
  const {
3443
3489
  styles,
3444
3490
  isLoading: isLoadingStyles,
3445
3491
  agentName: agentNameFromAPI,
3492
+ browserUseEnabled,
3446
3493
  pageNavigationEnabled,
3447
3494
  pageNavigationRoutes,
3448
3495
  persistAnonymousConversations,
@@ -3457,6 +3504,20 @@ function CrowCopilot({
3457
3504
  const agentName = agentNameProp ?? agentNameFromAPI ?? title;
3458
3505
  const welcomeMessage = welcomeMessageProp ?? welcomeMessageFromAPI;
3459
3506
  const [autoTools, setAutoTools] = useState({});
3507
+ const browserUseLoaded = autoTools.browser_use;
3508
+ useEffect(() => {
3509
+ if (browserUseEnabled && !isLoadingStyles && !browserUseLoaded) {
3510
+ import('@usecrow/client/browser').then(({ createBrowserUseTool }) => {
3511
+ setAutoTools((prev) => ({
3512
+ ...prev,
3513
+ browser_use: createBrowserUseTool({ productId, apiUrl })
3514
+ }));
3515
+ console.log("[Crow] browser_use tool auto-loaded");
3516
+ }).catch((err) => {
3517
+ console.warn("[Crow] Failed to load browser_use:", err);
3518
+ });
3519
+ }
3520
+ }, [browserUseEnabled, isLoadingStyles, productId, apiUrl, browserUseLoaded]);
3460
3521
  useEffect(() => {
3461
3522
  if (pageNavigationEnabled && pageNavigationRoutes.length > 0 && !isLoadingStyles) {
3462
3523
  import('@usecrow/client').then(({ createNavigateToPageTool }) => {
@@ -3489,8 +3550,10 @@ function CrowCopilot({
3489
3550
  const [activeLocalTabId, setActiveLocalTabId] = useState("local-1");
3490
3551
  const [localTabMessages, setLocalTabMessages] = useState({});
3491
3552
  const [localTabCounter, setLocalTabCounter] = useState(1);
3553
+ const [isBrowserUseActive, setIsBrowserUseActive] = useState(false);
3492
3554
  const [pendingConfirmation, setPendingConfirmation] = useState(null);
3493
- const [pendingQuestion, setPendingQuestion] = useState(null);
3555
+ const [askUserResolver, setAskUserResolver] = useState(null);
3556
+ const [browserQuestion, setBrowserQuestion] = useState(null);
3494
3557
  const conversations = useConversations({ productId, apiUrl });
3495
3558
  const [shouldRestoreHistory, setShouldRestoreHistory] = useState(false);
3496
3559
  const hasRestoredHistoryRef = useRef(false);
@@ -3631,10 +3694,25 @@ function CrowCopilot({
3631
3694
  );
3632
3695
  const handleBrowserQuestion = useCallback(
3633
3696
  (question) => {
3634
- return new Promise((resolve) => {
3635
- setPendingQuestion({ question, resolve });
3697
+ return new Promise((resolve, reject) => {
3698
+ setAskUserResolver((prev) => {
3699
+ if (prev) {
3700
+ prev.reject(new Error("Superseded by new browser question"));
3701
+ }
3702
+ return { resolve, reject };
3703
+ });
3704
+ setBrowserQuestion(question);
3705
+ chat.addMessage("assistant", question);
3636
3706
  });
3637
3707
  },
3708
+ [chat]
3709
+ );
3710
+ const handleBrowserProgress = useCallback(
3711
+ (step, maxSteps) => {
3712
+ if (step >= maxSteps || step === -1) {
3713
+ setIsBrowserUseActive(false);
3714
+ }
3715
+ },
3638
3716
  []
3639
3717
  );
3640
3718
  const handleExitWorkflow = async () => {
@@ -3643,21 +3721,79 @@ function CrowCopilot({
3643
3721
  useEffect(() => {
3644
3722
  window.__crow_browser_callbacks = {
3645
3723
  onConfirmation: handleBrowserConfirmation,
3646
- onQuestion: handleBrowserQuestion
3724
+ onQuestion: handleBrowserQuestion,
3725
+ onProgress: handleBrowserProgress
3647
3726
  };
3648
3727
  return () => {
3649
3728
  delete window.__crow_browser_callbacks;
3650
3729
  };
3651
- }, [handleBrowserConfirmation, handleBrowserQuestion]);
3730
+ }, [handleBrowserConfirmation, handleBrowserQuestion, handleBrowserProgress]);
3652
3731
  useEffect(() => {
3653
3732
  if (!isLoadingStyles) {
3654
3733
  onReady?.();
3655
3734
  }
3656
3735
  }, [isLoadingStyles, onReady]);
3736
+ useEffect(() => {
3737
+ if (!getIdentityToken || isLoadingStyles) return;
3738
+ let cancelled = false;
3739
+ const identify = async () => {
3740
+ try {
3741
+ const token = await getIdentityToken();
3742
+ if (!cancelled && token) {
3743
+ window.crow?.("identify", { token });
3744
+ }
3745
+ } catch (e) {
3746
+ console.error("[Crow] getIdentityToken failed:", e);
3747
+ }
3748
+ };
3749
+ identify();
3750
+ const handleRefresh = () => identify();
3751
+ window.addEventListener("crow:token-refresh-needed", handleRefresh);
3752
+ return () => {
3753
+ cancelled = true;
3754
+ window.removeEventListener("crow:token-refresh-needed", handleRefresh);
3755
+ };
3756
+ }, [getIdentityToken, isLoadingStyles]);
3757
+ useEffect(() => {
3758
+ if (typeof window === "undefined") return;
3759
+ if (context && Object.keys(context).length > 0) {
3760
+ window.__crow_page_context = context;
3761
+ } else {
3762
+ window.__crow_page_context = void 0;
3763
+ }
3764
+ }, [context]);
3657
3765
  const handleSend = (message) => {
3658
3766
  if (!message.trim()) return;
3767
+ if (askUserResolver) {
3768
+ chat.addMessage("user", message);
3769
+ askUserResolver.resolve(message);
3770
+ setAskUserResolver(null);
3771
+ setBrowserQuestion(null);
3772
+ return;
3773
+ }
3659
3774
  chat.sendMessage(message);
3660
3775
  };
3776
+ const handleStop = () => {
3777
+ chat.stopGeneration();
3778
+ const currentResolver = askUserResolver;
3779
+ setAskUserResolver(null);
3780
+ setBrowserQuestion(null);
3781
+ if (browserUseEnabled && isBrowserUseActive) {
3782
+ import('@usecrow/client/browser').then(({ stopActiveBrowserUse }) => {
3783
+ stopActiveBrowserUse();
3784
+ currentResolver?.reject(new Error("Stopped by user"));
3785
+ }).catch(() => {
3786
+ currentResolver?.reject(new Error("Stopped by user"));
3787
+ });
3788
+ setIsBrowserUseActive(false);
3789
+ } else if (currentResolver) {
3790
+ currentResolver.reject(new Error("Stopped by user"));
3791
+ }
3792
+ if (pendingConfirmation) {
3793
+ pendingConfirmation.resolve(false);
3794
+ setPendingConfirmation(null);
3795
+ }
3796
+ };
3661
3797
  const handleNewChat = () => {
3662
3798
  if (!isVerifiedUser) {
3663
3799
  setLocalTabMessages((prev) => ({
@@ -3997,13 +4133,14 @@ function CrowCopilot({
3997
4133
  }
3998
4134
  }
3999
4135
  ) }),
4000
- pendingQuestion && /* @__PURE__ */ jsx("div", { className: "crow-px-4 crow-py-2", children: /* @__PURE__ */ jsx(
4136
+ askUserResolver && browserQuestion && /* @__PURE__ */ jsx("div", { className: "crow-px-4 crow-py-2", children: /* @__PURE__ */ jsx(
4001
4137
  BrowserUseQuestion,
4002
4138
  {
4003
- question: pendingQuestion.question,
4139
+ question: browserQuestion,
4004
4140
  onSubmit: (answer) => {
4005
- pendingQuestion.resolve(answer);
4006
- setPendingQuestion(null);
4141
+ askUserResolver.resolve(answer);
4142
+ setAskUserResolver(null);
4143
+ setBrowserQuestion(null);
4007
4144
  }
4008
4145
  }
4009
4146
  ) })
@@ -4019,7 +4156,7 @@ function CrowCopilot({
4019
4156
  PromptInputBox,
4020
4157
  {
4021
4158
  onSend: handleSend,
4022
- onStop: chat.stopGeneration,
4159
+ onStop: handleStop,
4023
4160
  placeholder: "Ask anything...",
4024
4161
  isLoading: chat.isLoading
4025
4162
  }