@burtson-labs/bandit-engine 2.0.152 → 2.0.154

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.
Files changed (47) hide show
  1. package/dist/chat-E3VSYTAB.mjs +16 -0
  2. package/dist/chat-provider.js +8 -10
  3. package/dist/chat-provider.js.map +1 -1
  4. package/dist/chat-provider.mjs +5 -5
  5. package/dist/{chunk-6STVJJSG.mjs → chunk-2HGBMNX4.mjs} +17 -3
  6. package/dist/chunk-2HGBMNX4.mjs.map +1 -0
  7. package/dist/{chunk-LPSM62OE.mjs → chunk-2X46TMFE.mjs} +19 -14
  8. package/dist/chunk-2X46TMFE.mjs.map +1 -0
  9. package/dist/{chunk-7ESJJN6D.mjs → chunk-7AMLR6P4.mjs} +263 -75
  10. package/dist/chunk-7AMLR6P4.mjs.map +1 -0
  11. package/dist/{chunk-RRCCSN3N.mjs → chunk-B2TJZM4Z.mjs} +2 -2
  12. package/dist/{chunk-U33KBX3Y.mjs → chunk-GQ3X6VPR.mjs} +4 -4
  13. package/dist/{chunk-CVFFNX7H.mjs → chunk-IS2LWSBZ.mjs} +10 -12
  14. package/dist/chunk-IS2LWSBZ.mjs.map +1 -0
  15. package/dist/{chunk-TYRYAAP2.mjs → chunk-RN7FRSMX.mjs} +3 -3
  16. package/dist/{chunk-QLATFSSO.mjs → chunk-TVPYBRWF.mjs} +5 -5
  17. package/dist/{chunk-EORIRPPX.mjs → chunk-ZOCXFQID.mjs} +2 -2
  18. package/dist/{chunk-EORIRPPX.mjs.map → chunk-ZOCXFQID.mjs.map} +1 -1
  19. package/dist/{chunk-VLUXQE24.mjs → chunk-ZW3YNAD7.mjs} +2 -2
  20. package/dist/index.d.mts +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +790 -570
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +10 -10
  25. package/dist/management/management.js +489 -269
  26. package/dist/management/management.js.map +1 -1
  27. package/dist/management/management.mjs +8 -8
  28. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  29. package/dist/modals/chat-modal/chat-modal.mjs +4 -4
  30. package/dist/{modelStore-U57LUIIB.mjs → modelStore-KBUW3XAV.mjs} +2 -2
  31. package/dist/{public-20My32EB.d.mts → public-0EYmtpGD.d.mts} +8 -0
  32. package/dist/{public-20My32EB.d.ts → public-0EYmtpGD.d.ts} +8 -0
  33. package/dist/public-types.d.mts +1 -1
  34. package/dist/public-types.d.ts +1 -1
  35. package/package.json +1 -1
  36. package/dist/chat-Y3WOTIKI.mjs +0 -16
  37. package/dist/chunk-6STVJJSG.mjs.map +0 -1
  38. package/dist/chunk-7ESJJN6D.mjs.map +0 -1
  39. package/dist/chunk-CVFFNX7H.mjs.map +0 -1
  40. package/dist/chunk-LPSM62OE.mjs.map +0 -1
  41. /package/dist/{chat-Y3WOTIKI.mjs.map → chat-E3VSYTAB.mjs.map} +0 -0
  42. /package/dist/{chunk-RRCCSN3N.mjs.map → chunk-B2TJZM4Z.mjs.map} +0 -0
  43. /package/dist/{chunk-U33KBX3Y.mjs.map → chunk-GQ3X6VPR.mjs.map} +0 -0
  44. /package/dist/{chunk-TYRYAAP2.mjs.map → chunk-RN7FRSMX.mjs.map} +0 -0
  45. /package/dist/{chunk-QLATFSSO.mjs.map → chunk-TVPYBRWF.mjs.map} +0 -0
  46. /package/dist/{chunk-VLUXQE24.mjs.map → chunk-ZW3YNAD7.mjs.map} +0 -0
  47. /package/dist/{modelStore-U57LUIIB.mjs.map → modelStore-KBUW3XAV.mjs.map} +0 -0
@@ -2,11 +2,13 @@ import {
2
2
  styleInject
3
3
  } from "./chunk-ONQMRE2G.mjs";
4
4
  import {
5
- StreamingMarkdown_default
6
- } from "./chunk-6STVJJSG.mjs";
5
+ StreamingMarkdown_default,
6
+ isNativeShell,
7
+ isStandalonePwa
8
+ } from "./chunk-2HGBMNX4.mjs";
7
9
  import {
8
10
  useMCPToolsStore
9
- } from "./chunk-CVFFNX7H.mjs";
11
+ } from "./chunk-IS2LWSBZ.mjs";
10
12
  import {
11
13
  AddIcon,
12
14
  ArrowDownwardIcon,
@@ -36,7 +38,7 @@ import {
36
38
  useNotificationService,
37
39
  useTTS,
38
40
  useVoiceStore
39
- } from "./chunk-TYRYAAP2.mjs";
41
+ } from "./chunk-RN7FRSMX.mjs";
40
42
  import {
41
43
  brandingService_default,
42
44
  detectMessageMood,
@@ -59,13 +61,13 @@ import {
59
61
  useMemoryStore,
60
62
  useProjectStore,
61
63
  useVectorStore
62
- } from "./chunk-VLUXQE24.mjs";
64
+ } from "./chunk-ZW3YNAD7.mjs";
63
65
  import {
64
66
  indexedDBService_default,
65
67
  useModelStore,
66
68
  usePackageSettingsStore,
67
69
  usePreferencesStore
68
- } from "./chunk-EORIRPPX.mjs";
70
+ } from "./chunk-ZOCXFQID.mjs";
69
71
  import {
70
72
  authenticationService as authenticationService2,
71
73
  readPersistedToken,
@@ -77,7 +79,7 @@ import {
77
79
  } from "./chunk-KCI46M23.mjs";
78
80
 
79
81
  // src/chat/chat.tsx
80
- import { useCallback as useCallback6, useEffect as useEffect17, useLayoutEffect, useMemo as useMemo4, useRef as useRef15, useState as useState19 } from "react";
82
+ import { useCallback as useCallback6, useEffect as useEffect17, useLayoutEffect, useMemo as useMemo4, useRef as useRef16, useState as useState19 } from "react";
81
83
 
82
84
  // src/chat/custom-logo.tsx
83
85
  import React, { useEffect } from "react";
@@ -164,7 +166,7 @@ var Logo = ({ visible, atTop = false }) => {
164
166
  var custom_logo_default = Logo;
165
167
 
166
168
  // src/chat/chat.tsx
167
- import { Box as Box20, ThemeProvider, CssBaseline, CircularProgress as CircularProgress4, Typography as Typography15 } from "@mui/material";
169
+ import { Box as Box20, ThemeProvider, CssBaseline, CircularProgress as CircularProgress4, Typography as Typography15, Button as Button10, IconButton as IconButton14 } from "@mui/material";
168
170
  import { createTheme } from "@mui/material/styles";
169
171
 
170
172
  // src/chat/canvas-panel.tsx
@@ -978,6 +980,7 @@ var EnginePickerPill = () => {
978
980
  const tier = String(getCurrentTier());
979
981
  return tier !== "trial" && tier !== "expired" && tier !== "free" && tier !== "basic";
980
982
  })();
983
+ const hideUpgradePrompts = usePackageSettingsStore((s) => s.settings?.hideUpgradePrompts === true);
981
984
  const plansPath = typeof window !== "undefined" && !(window.location.hostname === "banditailabs.com" || window.location.hostname.endsWith(".banditailabs.com") || window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") ? "https://banditailabs.com/plans" : "/plans";
982
985
  if (engines.length === 0) return null;
983
986
  return /* @__PURE__ */ jsxs4(Fragment3, { children: [
@@ -1056,8 +1059,8 @@ var EnginePickerPill = () => {
1056
1059
  ),
1057
1060
  engine.id === resolvedEngineId && !isGated && /* @__PURE__ */ jsx7(Box5, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: theme.palette.primary.main } })
1058
1061
  ] }),
1059
- /* @__PURE__ */ jsx7(Typography2, { variant: "caption", sx: { color: theme.palette.text.secondary }, children: isGated ? "Premium engine \u2014 available on a paid plan." : engine.available ? engine.description : engine.unavailableReason || "Unavailable" }),
1060
- isGated && /* @__PURE__ */ jsx7(
1062
+ /* @__PURE__ */ jsx7(Typography2, { variant: "caption", sx: { color: theme.palette.text.secondary }, children: isGated ? hideUpgradePrompts ? "Not available on your current plan." : "Premium engine \u2014 available on a paid plan." : engine.available ? engine.description : engine.unavailableReason || "Unavailable" }),
1063
+ isGated && !hideUpgradePrompts && /* @__PURE__ */ jsx7(
1061
1064
  Button,
1062
1065
  {
1063
1066
  size: "small",
@@ -2349,6 +2352,7 @@ var RateLimitPrompt = () => {
2349
2352
  const theme = useTheme8();
2350
2353
  const prompt = useRateLimitStore((s) => s.prompt);
2351
2354
  const dismiss = useRateLimitStore((s) => s.dismiss);
2355
+ const hideUpgradePrompts = usePackageSettingsStore((s) => s.settings?.hideUpgradePrompts === true);
2352
2356
  const hasLoggedRouterWarningRef = useRef3(false);
2353
2357
  let navigate = null;
2354
2358
  try {
@@ -2378,6 +2382,12 @@ var RateLimitPrompt = () => {
2378
2382
  const { scope, frontier } = prompt;
2379
2383
  const title = scope === "weekly" ? "You've reached your weekly limit" : "You've hit your session limit";
2380
2384
  const body = (() => {
2385
+ if (hideUpgradePrompts) {
2386
+ if (frontier) {
2387
+ return scope === "weekly" ? "You've used your weekly allowance on premium engines. It resets with your weekly cycle." : "You've hit your session limit on premium engines. It resets soon.";
2388
+ }
2389
+ return scope === "weekly" ? "Your weekly chat allowance is used up. It resets with your weekly cycle." : "It resets shortly \u2014 check back in a bit.";
2390
+ }
2381
2391
  if (frontier) {
2382
2392
  return scope === "weekly" ? "You've used your weekly allowance on premium engines. Upgrade to keep chatting on the most capable models." : "You've hit your session limit on premium engines. It resets soon \u2014 or upgrade for higher limits and uninterrupted access.";
2383
2393
  }
@@ -2422,7 +2432,7 @@ var RateLimitPrompt = () => {
2422
2432
  /* @__PURE__ */ jsx11(Typography5, { variant: "subtitle2", sx: { fontWeight: 600, color: theme.palette.text.primary }, children: title }),
2423
2433
  /* @__PURE__ */ jsx11(Typography5, { variant: "body2", sx: { color: theme.palette.text.secondary, mt: 0.25 }, children: body }),
2424
2434
  /* @__PURE__ */ jsxs8(Box8, { sx: { display: "flex", gap: 1, mt: 1, flexWrap: "wrap" }, children: [
2425
- /* @__PURE__ */ jsx11(
2435
+ !hideUpgradePrompts && /* @__PURE__ */ jsx11(
2426
2436
  Button3,
2427
2437
  {
2428
2438
  size: "small",
@@ -4215,7 +4225,7 @@ TOOL USAGE PROTOCOL (conservative approach)
4215
4225
  - Only call tools for SPECIFIC, CURRENT information that requires real-time data or a source you don't already have:
4216
4226
  * web_search() - when asked about recent/current events, breaking news, live information (weather, prices, sports scores), or when you need to look up documentation, libraries, APIs, error messages, or verify a specific fact
4217
4227
  * web_fetch() - to read the FULL contents of a specific URL you already have. Reach for this when the user wants to "tell me more", "go deeper", "read/open that article", or asks for details about a specific source, link, or article from an EARLIER answer: take that item's URL from the previous Sources list in this conversation and fetch it, then answer from the page's actual content (not just the prior summary)
4218
- * image_generation() - ONLY when explicitly asked to create or generate an image
4228
+ * image_generation() - ONLY when explicitly asked to create or generate an image. Runs on Burtson's local GPU; it can take a couple of minutes when the GPU is cold, and is limited to admin and partner accounts
4219
4229
  * create_file({"content": "...", "filename": "report.docx", "format": "docx"}) - when the user asks for a downloadable FILE (a document, spreadsheet, slides, markdown, code, etc.) or to "export"/"download"/"save" something. Formats: md, txt, csv, json, html, xml, yaml, docx, pptx. PDF is NOT a supported format \u2014 if the user asks for a PDF, create a docx instead. For docx/pptx write well-structured Markdown (use "## " headings to start each slide for pptx). It returns a temporary download link \u2014 ALWAYS tell the user the file expires (~1 hour). If it is unclear whether they want it shown inline vs. as a downloadable file, use ask_user first.
4220
4230
  * ask_user({"questions": [{"question": "...", "header": "Format", "options": [{"label": "Inline (Recommended)"}, {"label": "Download a file"}]}]}) - when the request is AMBIGUOUS or missing a detail you need to do a good job (unclear scope, audience, format, tone, which of several interpretations they meant), or you are blocked on a decision that is the user's to make and cannot resolve from the request, context, or sensible defaults. Renders clickable options the user answers in one step \u2014 ALWAYS prefer this over asking a clarifying question in prose and ending your turn. Give 1-4 questions, each with 2-4 short options; if one is clearly best, list it first and append " (Recommended)". The user may also type their own answer; act on it directly. Don't overuse it \u2014 when a sensible default exists, take it and note the assumption instead.
4221
4231
  - For general questions about concepts, definitions, explanations, or how-to topics, use your built-in knowledge WITHOUT calling tools.
@@ -5296,7 +5306,7 @@ var useNetworkStatus = () => {
5296
5306
 
5297
5307
  // src/chat/chat-app-bar.tsx
5298
5308
  import { Avatar as Avatar8 } from "@mui/material";
5299
- import { useEffect as useEffect13, useRef as useRef11, useState as useState16 } from "react";
5309
+ import { useEffect as useEffect13, useRef as useRef12, useState as useState16 } from "react";
5300
5310
  import {
5301
5311
  Box as Box15,
5302
5312
  IconButton as IconButton12,
@@ -5310,10 +5320,10 @@ import {
5310
5320
  Typography as Typography12,
5311
5321
  Button as Button9
5312
5322
  } from "@mui/material";
5313
- import { useNavigate as useNavigate2 } from "react-router-dom";
5323
+ import { useNavigate as useNavigate3 } from "react-router-dom";
5314
5324
 
5315
5325
  // src/chat/conversation-drawer.tsx
5316
- import { useState as useState14, useMemo as useMemo2, useEffect as useEffect11, useRef as useRef9, useCallback as useCallback4 } from "react";
5326
+ import { useState as useState14, useMemo as useMemo2, useEffect as useEffect11, useRef as useRef10, useCallback as useCallback4 } from "react";
5317
5327
  import {
5318
5328
  Drawer,
5319
5329
  Box as Box13,
@@ -5340,8 +5350,38 @@ import {
5340
5350
  import { X as CloseIcon6, X as ClearIcon, Search as SearchIcon, Folder as FolderIcon4, MoreVertical as MoreVertIcon3, Trash2 as DeleteSweepIcon, Inbox as InboxIcon3, Plus as AddIcon4, Settings as SettingsIcon, Brain as MemoryIcon, LogOut as LogOutIcon, House as HomeLucideIcon, RefreshCw as SyncLucideIcon, CircleAlert as SyncErrorIcon, SlidersHorizontal as ManagementIcon, MessageSquare as FeedbackMenuIcon } from "lucide-react";
5341
5351
  import { useTheme as useTheme13 } from "@mui/material/styles";
5342
5352
 
5353
+ // src/hooks/useSafeNavigate.ts
5354
+ import { useRef as useRef6 } from "react";
5355
+ import { useNavigate as useNavigate2 } from "react-router-dom";
5356
+ var useSafeNavigate = () => {
5357
+ const warned = useRef6(false);
5358
+ let navigate = null;
5359
+ try {
5360
+ navigate = useNavigate2();
5361
+ } catch (error) {
5362
+ if (!warned.current) {
5363
+ debugLogger.warn("useSafeNavigate: no Router context; falling back to location", { error });
5364
+ warned.current = true;
5365
+ }
5366
+ navigate = null;
5367
+ }
5368
+ return (to) => {
5369
+ if (typeof window !== "undefined" && /^[a-z]+:\/\//i.test(to)) {
5370
+ window.location.href = to;
5371
+ return;
5372
+ }
5373
+ if (navigate) {
5374
+ navigate(to);
5375
+ return;
5376
+ }
5377
+ if (typeof window !== "undefined") {
5378
+ window.location.href = to;
5379
+ }
5380
+ };
5381
+ };
5382
+
5343
5383
  // src/chat/project-management-modal.tsx
5344
- import { useState as useState10, useEffect as useEffect8, useRef as useRef6 } from "react";
5384
+ import { useState as useState10, useEffect as useEffect8, useRef as useRef7 } from "react";
5345
5385
  import {
5346
5386
  Modal,
5347
5387
  Button as Button4,
@@ -5404,7 +5444,7 @@ var ProjectManagementModal = ({
5404
5444
  const [selectedProject, setSelectedProject] = useState10(null);
5405
5445
  const [loading, setLoading] = useState10(false);
5406
5446
  const [error, setError] = useState10(null);
5407
- const modalContainerRef = useRef6(null);
5447
+ const modalContainerRef = useRef7(null);
5408
5448
  useEffect8(() => {
5409
5449
  if (open && !_hasHydrated) {
5410
5450
  hydrate();
@@ -6121,7 +6161,7 @@ var MoveConversationModal = ({
6121
6161
  var move_conversation_modal_default = MoveConversationModal;
6122
6162
 
6123
6163
  // src/chat/simple-conversation-item.tsx
6124
- import { useState as useState12, useRef as useRef7, useEffect as useEffect10 } from "react";
6164
+ import { useState as useState12, useRef as useRef8, useEffect as useEffect10 } from "react";
6125
6165
  import {
6126
6166
  Box as Box11,
6127
6167
  Typography as Typography8,
@@ -6164,10 +6204,10 @@ var SimpleConversationItem = ({
6164
6204
  const [isDragging, setIsDragging] = useState12(false);
6165
6205
  const [isTouchDragging, setIsTouchDragging] = useState12(false);
6166
6206
  const [showRenameDialog, setShowRenameDialog] = useState12(false);
6167
- const longPressTimeoutRef = useRef7(null);
6168
- const touchStartPointRef = useRef7(null);
6169
- const activeTouchIdRef = useRef7(null);
6170
- const suppressClickRef = useRef7(false);
6207
+ const longPressTimeoutRef = useRef8(null);
6208
+ const touchStartPointRef = useRef8(null);
6209
+ const activeTouchIdRef = useRef8(null);
6210
+ const suppressClickRef = useRef8(false);
6171
6211
  const highlightText = (text, query) => {
6172
6212
  if (!query || !query.trim()) {
6173
6213
  return text;
@@ -6570,7 +6610,7 @@ var SimpleConversationItem = ({
6570
6610
  var simple_conversation_item_default = SimpleConversationItem;
6571
6611
 
6572
6612
  // src/chat/project-header.tsx
6573
- import { useRef as useRef8, useState as useState13 } from "react";
6613
+ import { useRef as useRef9, useState as useState13 } from "react";
6574
6614
  import {
6575
6615
  Box as Box12,
6576
6616
  Typography as Typography9,
@@ -6603,7 +6643,7 @@ var ProjectHeader = ({
6603
6643
  const [isHovered, setIsHovered] = useState13(false);
6604
6644
  const [isDragOver, setIsDragOver] = useState13(false);
6605
6645
  const [renameDraft, setRenameDraft] = useState13(projectName);
6606
- const renameActionRef = useRef8("none");
6646
+ const renameActionRef = useRef9("none");
6607
6647
  const isUngrouped = projectId === null;
6608
6648
  const Icon = isCollapsed ? FolderIcon3 : FolderOpenIcon;
6609
6649
  const handleAddConversation = (e) => {
@@ -6923,7 +6963,7 @@ var ConversationDrawer = ({ open, onClose }) => {
6923
6963
  const [projectManagementOpen, setProjectManagementOpen] = useState14(false);
6924
6964
  const [memoryModalOpen, setMemoryModalOpen] = useState14(false);
6925
6965
  const [collapsedProjects, setCollapsedProjects] = useState14(/* @__PURE__ */ new Set());
6926
- const didInitCollapseRef = useRef9(false);
6966
+ const didInitCollapseRef = useRef10(false);
6927
6967
  const [searchQuery, setSearchQuery] = useState14("");
6928
6968
  const [menuAnchorEl, setMenuAnchorEl] = useState14(null);
6929
6969
  const [clearConfirmOpen, setClearConfirmOpen] = useState14(false);
@@ -7101,15 +7141,18 @@ var ConversationDrawer = ({ open, onClose }) => {
7101
7141
  const syncEnabled = useConversationSyncStore((s) => s.syncEnabled);
7102
7142
  const syncStatus = useConversationSyncStore((s) => s.status);
7103
7143
  const runSync = useConversationSyncStore((s) => s.runSync);
7144
+ const safeNavigate = useSafeNavigate();
7104
7145
  const goToHome = () => {
7105
7146
  const resolvedHomeUrl = preferences.homeUrl?.trim() || usePackageSettingsStore.getState().settings?.homeUrl?.trim() || "";
7106
7147
  if (resolvedHomeUrl) {
7107
7148
  window.location.href = resolvedHomeUrl;
7108
7149
  return;
7109
7150
  }
7110
- const navigatorWithStandalone = window.navigator;
7111
- const isStandalone = window.matchMedia?.("(display-mode: standalone)").matches || navigatorWithStandalone.standalone === true;
7112
- if (isStandalone) {
7151
+ if (isNativeShell()) {
7152
+ safeNavigate("/chat");
7153
+ return;
7154
+ }
7155
+ if (isStandalonePwa()) {
7113
7156
  try {
7114
7157
  sessionStorage.setItem("banditPwaStayOnHome", "true");
7115
7158
  } catch {
@@ -7580,7 +7623,7 @@ var ConversationDrawer = ({ open, onClose }) => {
7580
7623
  Box13,
7581
7624
  {
7582
7625
  onClick: user ? () => {
7583
- window.location.href = "/profile";
7626
+ safeNavigate("/profile");
7584
7627
  } : void 0,
7585
7628
  title: user ? "Profile & settings" : void 0,
7586
7629
  sx: {
@@ -7673,7 +7716,7 @@ var ConversationDrawer = ({ open, onClose }) => {
7673
7716
  title: "Management & settings",
7674
7717
  onClick: (event) => {
7675
7718
  event.stopPropagation();
7676
- window.location.href = managementPath;
7719
+ safeNavigate(managementPath);
7677
7720
  },
7678
7721
  sx: {
7679
7722
  flexShrink: 0,
@@ -7831,7 +7874,7 @@ var ConversationDrawer = ({ open, onClose }) => {
7831
7874
  var conversation_drawer_default = ConversationDrawer;
7832
7875
 
7833
7876
  // src/chat/enhanced-mobile-conversations-modal.tsx
7834
- import { useState as useState15, useMemo as useMemo3, useEffect as useEffect12, useRef as useRef10, useCallback as useCallback5 } from "react";
7877
+ import { useState as useState15, useMemo as useMemo3, useEffect as useEffect12, useRef as useRef11, useCallback as useCallback5 } from "react";
7835
7878
  import {
7836
7879
  Box as Box14,
7837
7880
  IconButton as IconButton11,
@@ -7887,7 +7930,8 @@ var deriveInitials2 = (value) => {
7887
7930
  };
7888
7931
  var EnhancedMobileConversationsModal = ({
7889
7932
  open,
7890
- onClose
7933
+ onClose,
7934
+ drag
7891
7935
  }) => {
7892
7936
  const theme = useTheme14();
7893
7937
  const { user } = useAuthenticationStore();
@@ -7914,15 +7958,18 @@ var EnhancedMobileConversationsModal = ({
7914
7958
  const syncEnabled = useConversationSyncStore((s) => s.syncEnabled);
7915
7959
  const syncStatus = useConversationSyncStore((s) => s.status);
7916
7960
  const runSync = useConversationSyncStore((s) => s.runSync);
7961
+ const safeNavigate = useSafeNavigate();
7917
7962
  const goToHome = () => {
7918
7963
  const resolvedHomeUrl = preferences.homeUrl?.trim() || usePackageSettingsStore.getState().settings?.homeUrl?.trim() || "";
7919
7964
  if (resolvedHomeUrl) {
7920
7965
  window.location.href = resolvedHomeUrl;
7921
7966
  return;
7922
7967
  }
7923
- const navigatorWithStandalone = window.navigator;
7924
- const isStandalone = window.matchMedia?.("(display-mode: standalone)").matches || navigatorWithStandalone.standalone === true;
7925
- if (isStandalone) {
7968
+ if (isNativeShell()) {
7969
+ safeNavigate("/chat");
7970
+ return;
7971
+ }
7972
+ if (isStandalonePwa()) {
7926
7973
  try {
7927
7974
  sessionStorage.setItem("banditPwaStayOnHome", "true");
7928
7975
  } catch {
@@ -7936,7 +7983,7 @@ var EnhancedMobileConversationsModal = ({
7936
7983
  const [projectManagementOpen, setProjectManagementOpen] = useState15(false);
7937
7984
  const [memoryModalOpen, setMemoryModalOpen] = useState15(false);
7938
7985
  const [collapsedProjects, setCollapsedProjects] = useState15(/* @__PURE__ */ new Set());
7939
- const didInitCollapseRef = useRef10(false);
7986
+ const didInitCollapseRef = useRef11(false);
7940
7987
  const [searchQuery, setSearchQuery] = useState15("");
7941
7988
  const [menuAnchorEl, setMenuAnchorEl] = useState15(null);
7942
7989
  const [clearConfirmOpen, setClearConfirmOpen] = useState15(false);
@@ -8226,6 +8273,11 @@ var EnhancedMobileConversationsModal = ({
8226
8273
  children: /* @__PURE__ */ jsx17(Slide, { direction: "right", in: open, children: /* @__PURE__ */ jsxs14(
8227
8274
  Box14,
8228
8275
  {
8276
+ style: drag?.style,
8277
+ onTouchStart: drag?.onTouchStart,
8278
+ onTouchMove: drag?.onTouchMove,
8279
+ onTouchEnd: drag?.onTouchEnd,
8280
+ onTouchCancel: drag?.onTouchEnd,
8229
8281
  sx: {
8230
8282
  position: "fixed",
8231
8283
  top: 0,
@@ -8733,7 +8785,7 @@ var EnhancedMobileConversationsModal = ({
8733
8785
  Box14,
8734
8786
  {
8735
8787
  onClick: user ? () => {
8736
- window.location.href = "/profile";
8788
+ safeNavigate("/profile");
8737
8789
  } : void 0,
8738
8790
  sx: {
8739
8791
  mt: "auto",
@@ -8809,7 +8861,7 @@ var EnhancedMobileConversationsModal = ({
8809
8861
  title: "Management & settings",
8810
8862
  onClick: (event) => {
8811
8863
  event.stopPropagation();
8812
- window.location.href = managementPath;
8864
+ safeNavigate(managementPath);
8813
8865
  },
8814
8866
  sx: {
8815
8867
  flexShrink: 0,
@@ -8989,14 +9041,15 @@ var ChatAppBar = ({
8989
9041
  drawerOpen,
8990
9042
  setDrawerOpen,
8991
9043
  mobileConversationsOpen,
8992
- onMobileConversationsOpenChange
9044
+ onMobileConversationsOpenChange,
9045
+ mobileConversationsDrag
8993
9046
  }) => {
8994
9047
  const theme = useTheme15();
8995
9048
  const isMobile = useMediaQuery6(theme.breakpoints.down("sm"));
8996
- const hasLoggedRouterWarningRef = useRef11(false);
9049
+ const hasLoggedRouterWarningRef = useRef12(false);
8997
9050
  let navigate = null;
8998
9051
  try {
8999
- navigate = useNavigate2();
9052
+ navigate = useNavigate3();
9000
9053
  } catch (error) {
9001
9054
  if (!hasLoggedRouterWarningRef.current) {
9002
9055
  debugLogger.warn("ChatAppBar: Navigation not available (missing Router context)", { error });
@@ -9341,7 +9394,7 @@ var ChatAppBar = ({
9341
9394
  }
9342
9395
  ),
9343
9396
  /* @__PURE__ */ jsx18(conversation_drawer_default, { open: drawerOpen, onClose: () => setDrawerOpen(false) }),
9344
- /* @__PURE__ */ jsx18(enhanced_mobile_conversations_modal_default, { open: modalOpen, onClose: () => setModalOpen(false) }),
9397
+ /* @__PURE__ */ jsx18(enhanced_mobile_conversations_modal_default, { open: modalOpen, onClose: () => setModalOpen(false), drag: mobileConversationsDrag }),
9345
9398
  /* @__PURE__ */ jsxs15(
9346
9399
  Dialog7,
9347
9400
  {
@@ -9469,7 +9522,7 @@ Respond with just the title and nothing else.
9469
9522
  };
9470
9523
 
9471
9524
  // src/chat/query-suggestion-picker.tsx
9472
- import { useEffect as useEffect14, useRef as useRef12, useState as useState17 } from "react";
9525
+ import { useEffect as useEffect14, useRef as useRef13, useState as useState17 } from "react";
9473
9526
  import { Box as Box16, useMediaQuery as useMediaQuery7 } from "@mui/material";
9474
9527
  import { useTheme as useTheme16, alpha as alpha10 } from "@mui/material/styles";
9475
9528
  import ReactMarkdown from "react-markdown";
@@ -9488,11 +9541,11 @@ var QuerySuggestionPicker = ({
9488
9541
  onSend,
9489
9542
  inputHeight
9490
9543
  }) => {
9491
- const hasGenerated = useRef12(false);
9544
+ const hasGenerated = useRef13(false);
9492
9545
  const [hasSentPrompt, setHasSentPrompt] = useState17(false);
9493
9546
  const [examplePrompts, setExamplePrompts] = useState17([]);
9494
9547
  const [visiblePrompts, setVisiblePrompts] = useState17([]);
9495
- const scrollRef = useRef12(null);
9548
+ const scrollRef = useRef13(null);
9496
9549
  const theme = useTheme16();
9497
9550
  const isMobile = useMediaQuery7((theme2) => theme2.breakpoints.down("sm"));
9498
9551
  const { background, text, border, hoverBackground, hoverBorder } = theme.palette.chat.suggestion;
@@ -9651,11 +9704,11 @@ var QuerySuggestionPicker = ({
9651
9704
 
9652
9705
  // src/components/UnderReview.tsx
9653
9706
  import { Box as Box17, Typography as Typography13, useTheme as useTheme17 } from "@mui/material";
9654
- import { useNavigate as useNavigate3 } from "react-router-dom";
9707
+ import { useNavigate as useNavigate4 } from "react-router-dom";
9655
9708
  import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
9656
9709
  var UnderReview = () => {
9657
9710
  const theme = useTheme17();
9658
- const navigate = useNavigate3();
9711
+ const navigate = useNavigate4();
9659
9712
  const handleResetSession = () => {
9660
9713
  authenticationService2.clearToken();
9661
9714
  navigate("/login");
@@ -9717,7 +9770,7 @@ var UnderReview = () => {
9717
9770
  var UnderReview_default = UnderReview;
9718
9771
 
9719
9772
  // src/components/feedback/FeedbackPulse.tsx
9720
- import { useEffect as useEffect15, useRef as useRef13, useState as useState18 } from "react";
9773
+ import { useEffect as useEffect15, useRef as useRef14, useState as useState18 } from "react";
9721
9774
  import { Box as Box18, IconButton as IconButton13, Paper as Paper2, Slide as Slide2, Typography as Typography14 } from "@mui/material";
9722
9775
  import { useTheme as useTheme18, alpha as alpha11 } from "@mui/material/styles";
9723
9776
  import { X as CloseIcon8, Smile as SmileIcon, Meh as MehIcon, Frown as FrownIcon } from "lucide-react";
@@ -9746,8 +9799,8 @@ var FeedbackPulse = ({ isMobile, feedbackEmail }) => {
9746
9799
  const [visible, setVisible] = useState18(false);
9747
9800
  const [thanked, setThanked] = useState18(false);
9748
9801
  const [modalOpen, setModalOpen] = useState18(false);
9749
- const mountedAtRef = useRef13(Date.now());
9750
- const baselineMessagesRef = useRef13(null);
9802
+ const mountedAtRef = useRef14(Date.now());
9803
+ const baselineMessagesRef = useRef14(null);
9751
9804
  const totalMessages = useConversationStore(
9752
9805
  (s) => s.conversations.reduce((n, c) => n + c.history.length, 0)
9753
9806
  );
@@ -9957,7 +10010,7 @@ var ConnectionStatus = ({
9957
10010
  };
9958
10011
 
9959
10012
  // src/hooks/useVoiceMode.ts
9960
- import { useEffect as useEffect16, useRef as useRef14 } from "react";
10013
+ import { useEffect as useEffect16, useRef as useRef15 } from "react";
9961
10014
  var RMS_BASELINE = 128;
9962
10015
  var RMS_NORMALIZER = 128;
9963
10016
  var computeRms = (data) => {
@@ -9987,7 +10040,7 @@ var useVoiceMode = (config) => {
9987
10040
  const setError = useVoiceModeStore((state) => state.setError);
9988
10041
  const resetTransientState = useVoiceModeStore((state) => state.resetTransientState);
9989
10042
  const setLastTranscript = useVoiceModeStore((state) => state.setLastTranscript);
9990
- const configRef = useRef14(config);
10043
+ const configRef = useRef15(config);
9991
10044
  useEffect16(() => {
9992
10045
  configRef.current = config;
9993
10046
  }, [config]);
@@ -10309,8 +10362,8 @@ var ChatContent = () => {
10309
10362
  } = useVoiceStore();
10310
10363
  const isVoiceModeEnabled = useVoiceModeStore((state) => state.enabled);
10311
10364
  const canvasOpen = useCanvasStore((state) => state.open);
10312
- const previousVoiceModeEnabledRef = useRef15(isVoiceModeEnabled);
10313
- const historyRef = useRef15(history);
10365
+ const previousVoiceModeEnabledRef = useRef16(isVoiceModeEnabled);
10366
+ const historyRef = useRef16(history);
10314
10367
  useEffect17(() => {
10315
10368
  historyRef.current = history;
10316
10369
  }, [history]);
@@ -10356,9 +10409,9 @@ var ChatContent = () => {
10356
10409
  }
10357
10410
  }, [packageSettings?.gatewayApiUrl, availableVoices.length, initialized, loadVoicesFromAPI, token]);
10358
10411
  const provider = useAIProviderStore((state) => state.provider);
10359
- const inputRef = useRef15(null);
10412
+ const inputRef = useRef16(null);
10360
10413
  const [pastedImages, setPastedImages] = useState19([]);
10361
- const inputContainerRef = useRef15(null);
10414
+ const inputContainerRef = useRef16(null);
10362
10415
  const [inputHeight, setInputHeight] = useState19(80);
10363
10416
  const [isSubmitting, setIsSubmitting] = useState19(false);
10364
10417
  const [pendingMessage, setPendingMessage] = useState19(null);
@@ -10367,7 +10420,70 @@ var ChatContent = () => {
10367
10420
  const [isMobile, setIsMobile] = useState19(false);
10368
10421
  const [drawerOpen, setDrawerOpen] = useState19(false);
10369
10422
  const [mobileConversationsOpen, setMobileConversationsOpen] = useState19(false);
10370
- const cardSwipeStartXRef = useRef15(null);
10423
+ const cardSwipeStartXRef = useRef16(null);
10424
+ const dragStartRef = useRef16(null);
10425
+ const [drawerDragX, setDrawerDragX] = useState19(null);
10426
+ const [drawerSettling, setDrawerSettling] = useState19(false);
10427
+ const [drawerSwap, setDrawerSwap] = useState19(false);
10428
+ const drawerOpenOffset = () => (typeof window === "undefined" ? 360 : Math.min(window.innerWidth - 56, 360)) - 12;
10429
+ const drawerDragStart = (e) => {
10430
+ const t = e.touches[0];
10431
+ if (!t) return;
10432
+ dragStartRef.current = { x: t.clientX, y: t.clientY, t: performance.now(), axis: null };
10433
+ };
10434
+ const drawerDragMove = (e) => {
10435
+ const s = dragStartRef.current;
10436
+ const t = e.touches[0];
10437
+ if (!s || !t) return;
10438
+ const dx = t.clientX - s.x;
10439
+ const dy = t.clientY - s.y;
10440
+ if (s.axis === null) {
10441
+ if (Math.abs(dx) < 8 && Math.abs(dy) < 8) return;
10442
+ s.axis = Math.abs(dx) > Math.abs(dy) ? "x" : "y";
10443
+ }
10444
+ if (s.axis !== "x") return;
10445
+ setDrawerDragX(dx < 0 ? Math.max(dx, -drawerOpenOffset()) : dx * 0.15);
10446
+ };
10447
+ const drawerDragEnd = () => {
10448
+ const s = dragStartRef.current;
10449
+ dragStartRef.current = null;
10450
+ if (!s || s.axis !== "x" || drawerDragX === null) {
10451
+ setDrawerDragX(null);
10452
+ return;
10453
+ }
10454
+ const dt = Math.max(1, performance.now() - s.t);
10455
+ const velocity = drawerDragX / dt;
10456
+ const offset = drawerOpenOffset();
10457
+ const shouldClose = drawerDragX < -offset * 0.35 || velocity < -0.45;
10458
+ if (shouldClose) {
10459
+ setDrawerSettling(true);
10460
+ setDrawerDragX(-offset);
10461
+ window.setTimeout(() => {
10462
+ setDrawerSwap(true);
10463
+ setMobileConversationsOpen(false);
10464
+ requestAnimationFrame(() => requestAnimationFrame(() => setDrawerSwap(false)));
10465
+ window.setTimeout(() => {
10466
+ setDrawerDragX(null);
10467
+ setDrawerSettling(false);
10468
+ }, 320);
10469
+ }, 220);
10470
+ } else {
10471
+ setDrawerSettling(true);
10472
+ setDrawerDragX(0);
10473
+ window.setTimeout(() => {
10474
+ setDrawerDragX(null);
10475
+ setDrawerSettling(false);
10476
+ }, 220);
10477
+ }
10478
+ };
10479
+ const drawerDragStyle = drawerSwap ? { translate: "0px 0", transition: "none" } : isMobile && mobileConversationsOpen && drawerDragX !== null ? {
10480
+ translate: `${drawerDragX}px 0`,
10481
+ transition: drawerSettling ? "translate 0.22s cubic-bezier(0.2, 0.8, 0.2, 1)" : "none"
10482
+ } : void 0;
10483
+ const drawerPanelDragStyle = isMobile && drawerDragX !== null ? {
10484
+ translate: `${drawerDragX}px 0`,
10485
+ transition: drawerSettling ? "translate 0.22s cubic-bezier(0.2, 0.8, 0.2, 1)" : "none"
10486
+ } : void 0;
10371
10487
  useEffect17(() => {
10372
10488
  if (typeof document === "undefined") return;
10373
10489
  const meta = document.querySelector('meta[name="theme-color"]');
@@ -10394,18 +10510,41 @@ var ChatContent = () => {
10394
10510
  const initialLogoState = history.length === 0;
10395
10511
  const [logoVisible, setLogoVisible] = useState19(initialLogoState);
10396
10512
  const [logoShouldRender, setLogoShouldRender] = useState19(initialLogoState);
10397
- const streamingGraceUntilRef = useRef15(0);
10513
+ const streamingGraceUntilRef = useRef16(0);
10398
10514
  const GRACE_MS = 450;
10399
10515
  const GRACE_OFFSET_DESKTOP = 28;
10400
10516
  const GRACE_OFFSET_MOBILE = 20;
10401
- const followStreamRef = useRef15(true);
10402
- const lastSpokenResponseRef = useRef15(null);
10403
- const previousConversationIdRef = useRef15(null);
10404
- const logoFadeTimeoutRef = useRef15(null);
10517
+ const followStreamRef = useRef16(true);
10518
+ const lastSpokenResponseRef = useRef16(null);
10519
+ const previousConversationIdRef = useRef16(null);
10520
+ const logoFadeTimeoutRef = useRef16(null);
10405
10521
  const [branding, setBranding] = useState19(null);
10406
10522
  const [brandingLoading, setBrandingLoading] = useState19(true);
10407
- const isBrandingLoadInProgressRef = useRef15(false);
10523
+ const isBrandingLoadInProgressRef = useRef16(false);
10408
10524
  const logoOnly = history.length === 0 && !brandingLoading;
10525
+ const knowledgeDocs = useKnowledgeStore((s) => s.docs);
10526
+ const knowledgeLoaded = useKnowledgeStore((s) => s.isLoaded);
10527
+ const [knowledgeNudgeDismissed, setKnowledgeNudgeDismissed] = useState19(() => {
10528
+ try {
10529
+ return localStorage.getItem("bandit.knowledgeNudge.dismissed") === "true";
10530
+ } catch {
10531
+ return false;
10532
+ }
10533
+ });
10534
+ const dismissKnowledgeNudge = useCallback6(() => {
10535
+ setKnowledgeNudgeDismissed(true);
10536
+ try {
10537
+ localStorage.setItem("bandit.knowledgeNudge.dismissed", "true");
10538
+ } catch {
10539
+ }
10540
+ }, []);
10541
+ const showKnowledgeNudge = logoOnly && !knowledgeNudgeDismissed && knowledgeLoaded && knowledgeDocs.length === 0 && featureFlag.hasFeature("documentKnowledge");
10542
+ const knowledgePath = typeof window !== "undefined" && window.location.pathname.startsWith("/playground") ? "/playground/management" : "/management";
10543
+ const safeNavigateTo = (to) => {
10544
+ if (typeof window !== "undefined") {
10545
+ window.location.assign(to);
10546
+ }
10547
+ };
10409
10548
  useEffect17(() => {
10410
10549
  const isEmptyConversation = (history?.length ?? 0) === 0;
10411
10550
  const isSending = Boolean(pendingMessage);
@@ -11252,27 +11391,23 @@ var ChatContent = () => {
11252
11391
  // is the same near-black as the shell.
11253
11392
  border: isMobile && mobileConversationsOpen ? `1px solid ${theme.palette.mode === "dark" ? "rgba(255,255,255,0.14)" : "rgba(0,0,0,0.12)"}` : "none",
11254
11393
  boxShadow: isMobile && mobileConversationsOpen ? `0 12px 40px ${theme.palette.mode === "dark" ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.25)"}` : "none",
11255
- transition: "left 0.3s ease-in-out, width 0.3s ease-in-out, transform 0.3s ease-in-out, border-radius 0.3s ease-in-out"
11394
+ transition: drawerSwap ? "none" : "left 0.3s ease-in-out, width 0.3s ease-in-out, transform 0.3s ease-in-out, border-radius 0.3s ease-in-out"
11256
11395
  }),
11257
11396
  onClick: isMobile && mobileConversationsOpen ? (e) => {
11258
11397
  if (e.currentTarget.contains(e.target)) {
11259
11398
  setMobileConversationsOpen(false);
11260
11399
  }
11261
11400
  } : void 0,
11401
+ style: drawerDragStyle,
11262
11402
  onTouchStart: isMobile && mobileConversationsOpen ? (e) => {
11263
11403
  if (e.currentTarget.contains(e.target)) {
11264
11404
  cardSwipeStartXRef.current = e.touches[0]?.clientX ?? null;
11405
+ drawerDragStart(e);
11265
11406
  }
11266
11407
  } : void 0,
11267
- onTouchMove: isMobile && mobileConversationsOpen ? (e) => {
11268
- const start = cardSwipeStartXRef.current;
11269
- if (start === null) return;
11270
- const dx = (e.touches[0]?.clientX ?? start) - start;
11271
- if (Math.abs(dx) > 40) {
11272
- cardSwipeStartXRef.current = null;
11273
- setMobileConversationsOpen(false);
11274
- }
11275
- } : void 0,
11408
+ onTouchMove: isMobile && mobileConversationsOpen ? drawerDragMove : void 0,
11409
+ onTouchEnd: isMobile && mobileConversationsOpen ? drawerDragEnd : void 0,
11410
+ onTouchCancel: isMobile && mobileConversationsOpen ? drawerDragEnd : void 0,
11276
11411
  children: [
11277
11412
  /* @__PURE__ */ jsx23(
11278
11413
  chat_app_bar_default,
@@ -11285,7 +11420,13 @@ var ChatContent = () => {
11285
11420
  drawerOpen,
11286
11421
  setDrawerOpen,
11287
11422
  mobileConversationsOpen,
11288
- onMobileConversationsOpenChange: setMobileConversationsOpen
11423
+ onMobileConversationsOpenChange: setMobileConversationsOpen,
11424
+ mobileConversationsDrag: isMobile ? {
11425
+ style: drawerPanelDragStyle,
11426
+ onTouchStart: drawerDragStart,
11427
+ onTouchMove: drawerDragMove,
11428
+ onTouchEnd: drawerDragEnd
11429
+ } : void 0
11289
11430
  }
11290
11431
  ),
11291
11432
  isTemporaryChat && /* @__PURE__ */ jsxs18(Fragment13, { children: [
@@ -11441,6 +11582,53 @@ var ChatContent = () => {
11441
11582
  }
11442
11583
  return branding?.logoBase64 ? /* @__PURE__ */ jsx23(custom_logo_default, { visible: logoVisible, atTop: true }) : /* @__PURE__ */ jsx23(bandit_chat_logo_default, { visible: logoVisible, atTop: true });
11443
11584
  })(),
11585
+ showKnowledgeNudge && /* @__PURE__ */ jsxs18(
11586
+ Box20,
11587
+ {
11588
+ sx: (theme) => ({
11589
+ mx: "auto",
11590
+ mt: 2,
11591
+ mb: 1,
11592
+ px: 2,
11593
+ py: 1.25,
11594
+ maxWidth: 420,
11595
+ display: "flex",
11596
+ alignItems: "center",
11597
+ gap: 1.25,
11598
+ borderRadius: 2.5,
11599
+ border: `1px solid ${theme.palette.divider}`,
11600
+ bgcolor: theme.palette.action.hover,
11601
+ opacity: logoVisible ? 1 : 0,
11602
+ transition: "opacity 0.5s ease"
11603
+ }),
11604
+ children: [
11605
+ /* @__PURE__ */ jsxs18(Box20, { sx: { flex: 1, minWidth: 0 }, children: [
11606
+ /* @__PURE__ */ jsx23(Typography15, { variant: "body2", sx: { fontWeight: 600 }, children: "Add your documents" }),
11607
+ /* @__PURE__ */ jsx23(Typography15, { variant: "caption", color: "text.secondary", children: "Upload your policies, handbooks or notes and Bandit answers from them, with sources." })
11608
+ ] }),
11609
+ /* @__PURE__ */ jsx23(
11610
+ Button10,
11611
+ {
11612
+ size: "small",
11613
+ variant: "outlined",
11614
+ onClick: () => safeNavigateTo(knowledgePath),
11615
+ sx: { textTransform: "none", fontWeight: 600, flexShrink: 0 },
11616
+ children: "Upload"
11617
+ }
11618
+ ),
11619
+ /* @__PURE__ */ jsx23(
11620
+ IconButton14,
11621
+ {
11622
+ size: "small",
11623
+ "aria-label": "Dismiss",
11624
+ onClick: dismissKnowledgeNudge,
11625
+ sx: { flexShrink: 0 },
11626
+ children: /* @__PURE__ */ jsx23(CloseIcon, { fontSize: "small" })
11627
+ }
11628
+ )
11629
+ ]
11630
+ }
11631
+ ),
11444
11632
  /* @__PURE__ */ jsx23(
11445
11633
  Box20,
11446
11634
  {
@@ -11586,4 +11774,4 @@ var chat_default = Chat;
11586
11774
  export {
11587
11775
  chat_default
11588
11776
  };
11589
- //# sourceMappingURL=chunk-7ESJJN6D.mjs.map
11777
+ //# sourceMappingURL=chunk-7AMLR6P4.mjs.map