@burtson-labs/bandit-engine 2.1.8 → 2.1.9

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 (62) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/{AdvancedTab-HZTDXFST.mjs → AdvancedTab-D7SDAQEL.mjs} +4 -4
  3. package/dist/CrewSection-N2J3TJGU.mjs +15 -0
  4. package/dist/{PersonalizationTab-2XHFTYYW.mjs → PersonalizationTab-KPTVJKB3.mjs} +6 -6
  5. package/dist/{ProjectsPane-KE5DGFN4.mjs → ProjectsPane-C63C6GW5.mjs} +11 -10
  6. package/dist/ProjectsPane-C63C6GW5.mjs.map +1 -0
  7. package/dist/{SchedulesTab-AM5QP36K.mjs → SchedulesTab-TEOM3NWQ.mjs} +12 -12
  8. package/dist/{chat-D66YM4MB.mjs → chat-QAEEYO73.mjs} +16 -13
  9. package/dist/chunk-3CDCJJ4S.mjs +64 -0
  10. package/dist/chunk-3CDCJJ4S.mjs.map +1 -0
  11. package/dist/{chunk-FBLITVVY.mjs → chunk-5JNKGMMN.mjs} +31 -5
  12. package/dist/chunk-5JNKGMMN.mjs.map +1 -0
  13. package/dist/chunk-F56OTXXR.mjs +17 -0
  14. package/dist/chunk-F56OTXXR.mjs.map +1 -0
  15. package/dist/{chunk-LAH6UL3Q.mjs → chunk-GN3X7IIZ.mjs} +10 -10
  16. package/dist/{chunk-2FI2IICG.mjs → chunk-JGJRY4MR.mjs} +2 -2
  17. package/dist/chunk-MR643IO6.mjs +294 -0
  18. package/dist/chunk-MR643IO6.mjs.map +1 -0
  19. package/dist/{chunk-WZGDQ4EA.mjs → chunk-MX37LD3Z.mjs} +382 -81
  20. package/dist/chunk-MX37LD3Z.mjs.map +1 -0
  21. package/dist/{chunk-2THUYNVU.mjs → chunk-MX7Z4RSP.mjs} +2 -2
  22. package/dist/{chunk-5WCKKD3S.mjs → chunk-P42M52OW.mjs} +18 -28
  23. package/dist/chunk-P42M52OW.mjs.map +1 -0
  24. package/dist/{chunk-5URP2E5V.mjs → chunk-UT46TXNG.mjs} +42 -6
  25. package/dist/chunk-UT46TXNG.mjs.map +1 -0
  26. package/dist/{chunk-2HFSBPOE.mjs → chunk-WDFQG3RM.mjs} +21 -1
  27. package/dist/chunk-WDFQG3RM.mjs.map +1 -0
  28. package/dist/chunk-XWUHUDAV.mjs +814 -0
  29. package/dist/chunk-XWUHUDAV.mjs.map +1 -0
  30. package/dist/{chunk-K4AO466X.mjs → chunk-YY7BX2HM.mjs} +10 -14
  31. package/dist/chunk-YY7BX2HM.mjs.map +1 -0
  32. package/dist/crew-delegate-card-I4SSZU5H.mjs +229 -0
  33. package/dist/crew-delegate-card-I4SSZU5H.mjs.map +1 -0
  34. package/dist/crew-mention-picker-OSJ2NHUB.mjs +118 -0
  35. package/dist/crew-mention-picker-OSJ2NHUB.mjs.map +1 -0
  36. package/dist/crew-task-card-ZCXFTR34.mjs +416 -0
  37. package/dist/crew-task-card-ZCXFTR34.mjs.map +1 -0
  38. package/dist/index.js +7142 -5177
  39. package/dist/index.js.map +1 -1
  40. package/dist/index.mjs +64 -59
  41. package/dist/index.mjs.map +1 -1
  42. package/dist/{schedule-confirm-card-D4K76HEA.mjs → schedule-confirm-card-Y2JF2RBV.mjs} +5 -5
  43. package/package.json +1 -1
  44. package/dist/CrewSection-YM26YD2N.mjs +0 -12
  45. package/dist/ProjectsPane-KE5DGFN4.mjs.map +0 -1
  46. package/dist/chunk-2HFSBPOE.mjs.map +0 -1
  47. package/dist/chunk-5URP2E5V.mjs.map +0 -1
  48. package/dist/chunk-5WCKKD3S.mjs.map +0 -1
  49. package/dist/chunk-EKPPIS3X.mjs +0 -336
  50. package/dist/chunk-EKPPIS3X.mjs.map +0 -1
  51. package/dist/chunk-FBLITVVY.mjs.map +0 -1
  52. package/dist/chunk-K4AO466X.mjs.map +0 -1
  53. package/dist/chunk-WZGDQ4EA.mjs.map +0 -1
  54. /package/dist/{AdvancedTab-HZTDXFST.mjs.map → AdvancedTab-D7SDAQEL.mjs.map} +0 -0
  55. /package/dist/{CrewSection-YM26YD2N.mjs.map → CrewSection-N2J3TJGU.mjs.map} +0 -0
  56. /package/dist/{PersonalizationTab-2XHFTYYW.mjs.map → PersonalizationTab-KPTVJKB3.mjs.map} +0 -0
  57. /package/dist/{SchedulesTab-AM5QP36K.mjs.map → SchedulesTab-TEOM3NWQ.mjs.map} +0 -0
  58. /package/dist/{chat-D66YM4MB.mjs.map → chat-QAEEYO73.mjs.map} +0 -0
  59. /package/dist/{chunk-LAH6UL3Q.mjs.map → chunk-GN3X7IIZ.mjs.map} +0 -0
  60. /package/dist/{chunk-2FI2IICG.mjs.map → chunk-JGJRY4MR.mjs.map} +0 -0
  61. /package/dist/{chunk-2THUYNVU.mjs.map → chunk-MX7Z4RSP.mjs.map} +0 -0
  62. /package/dist/{schedule-confirm-card-D4K76HEA.mjs.map → schedule-confirm-card-Y2JF2RBV.mjs.map} +0 -0
@@ -1,3 +1,21 @@
1
+ import {
2
+ getStreamingTTSClient,
3
+ sanitizeForTTS,
4
+ stopTTS,
5
+ useVoiceStore
6
+ } from "./chunk-LC64VSDS.mjs";
7
+ import {
8
+ useImageEditAckStore
9
+ } from "./chunk-WBKTQJNX.mjs";
10
+ import {
11
+ useScheduleConfirmStore
12
+ } from "./chunk-HS62C62A.mjs";
13
+ import {
14
+ IS_STORE_BUILD,
15
+ crewSurfacesEnabled,
16
+ managementConsoleVariant,
17
+ useCrewStore
18
+ } from "./chunk-3CDCJJ4S.mjs";
1
19
  import {
2
20
  MarkdownImage,
3
21
  StreamingMarkdown_default,
@@ -5,7 +23,12 @@ import {
5
23
  markdownSanitizeSchema,
6
24
  maybeHandleFileLinkClick,
7
25
  renderLowlightChildren
8
- } from "./chunk-2FI2IICG.mjs";
26
+ } from "./chunk-JGJRY4MR.mjs";
27
+ import {
28
+ browserTimeZone,
29
+ formatInZone,
30
+ normalizeScheduleArgs
31
+ } from "./chunk-MAKXJD7A.mjs";
9
32
  import {
10
33
  ATTRIBUTION_RULE,
11
34
  CONTEXT_USAGE_RULES,
@@ -16,6 +39,7 @@ import {
16
39
  buildCitationGuide,
17
40
  canonicalToolName,
18
41
  contextNoteForTrail,
42
+ crewTaskIdOf,
19
43
  fetchConversationById,
20
44
  formatToolResultForModel,
21
45
  mapConversationFromDTO,
@@ -31,16 +55,7 @@ import {
31
55
  useMemoryStore,
32
56
  useVectorStore,
33
57
  vectorDatabaseService
34
- } from "./chunk-FBLITVVY.mjs";
35
- import {
36
- useInstructionsStore
37
- } from "./chunk-H2XUGGKV.mjs";
38
- import {
39
- getStreamingTTSClient,
40
- sanitizeForTTS,
41
- stopTTS,
42
- useVoiceStore
43
- } from "./chunk-LC64VSDS.mjs";
58
+ } from "./chunk-5JNKGMMN.mjs";
44
59
  import {
45
60
  AlertCircle as AlertCircle2,
46
61
  ArrowDown,
@@ -75,7 +90,10 @@ import {
75
90
  X as X2,
76
91
  ZoomIn,
77
92
  ZoomOut
78
- } from "./chunk-K4AO466X.mjs";
93
+ } from "./chunk-YY7BX2HM.mjs";
94
+ import {
95
+ useInstructionsStore
96
+ } from "./chunk-H2XUGGKV.mjs";
79
97
  import {
80
98
  GatewayService,
81
99
  useAIProviderStore
@@ -91,8 +109,6 @@ import {
91
109
  brandingService_default,
92
110
  groupByRecency,
93
111
  initialSidebarOpen,
94
- isNativeShell,
95
- isStandalonePwa,
96
112
  listable,
97
113
  managementPathFor,
98
114
  move_conversation_modal_default,
@@ -104,7 +120,11 @@ import {
104
120
  useSectionsLayout,
105
121
  useSidebarStore,
106
122
  writePersistedCollapsed
107
- } from "./chunk-5WCKKD3S.mjs";
123
+ } from "./chunk-P42M52OW.mjs";
124
+ import {
125
+ isNativeShell,
126
+ isStandalonePwa
127
+ } from "./chunk-F56OTXXR.mjs";
108
128
  import {
109
129
  ComposerControl,
110
130
  accentTokens,
@@ -132,7 +152,7 @@ import {
132
152
  useFeatureVisibility,
133
153
  useFeatures,
134
154
  useProjectStore
135
- } from "./chunk-LAH6UL3Q.mjs";
155
+ } from "./chunk-GN3X7IIZ.mjs";
136
156
  import {
137
157
  useModelStore,
138
158
  usePreferencesStore
@@ -141,16 +161,16 @@ import {
141
161
  indexedDBService_default
142
162
  } from "./chunk-KPT7NNNK.mjs";
143
163
  import {
144
- useImageEditAckStore
145
- } from "./chunk-WBKTQJNX.mjs";
164
+ usePackageSettingsStore
165
+ } from "./chunk-K7SQWDNF.mjs";
146
166
  import {
147
- useScheduleConfirmStore
148
- } from "./chunk-HS62C62A.mjs";
167
+ authenticationService,
168
+ readPersistedToken,
169
+ useAuthenticationStore
170
+ } from "./chunk-S3DWWUBD.mjs";
149
171
  import {
150
- browserTimeZone,
151
- formatInZone,
152
- normalizeScheduleArgs
153
- } from "./chunk-MAKXJD7A.mjs";
172
+ debugLogger
173
+ } from "./chunk-NI7BKLC7.mjs";
154
174
  import {
155
175
  Activity,
156
176
  Agent,
@@ -206,27 +226,17 @@ import {
206
226
  ThumbsUp,
207
227
  Trash,
208
228
  TrendingUp,
229
+ Users,
209
230
  Waveform,
210
231
  Wrench,
211
232
  X
212
- } from "./chunk-5URP2E5V.mjs";
213
- import {
214
- usePackageSettingsStore
215
- } from "./chunk-K7SQWDNF.mjs";
216
- import {
217
- authenticationService,
218
- readPersistedToken,
219
- useAuthenticationStore
220
- } from "./chunk-S3DWWUBD.mjs";
221
- import {
222
- debugLogger
223
- } from "./chunk-NI7BKLC7.mjs";
233
+ } from "./chunk-UT46TXNG.mjs";
224
234
 
225
235
  // src/chat/chat.tsx
226
- import React24 from "react";
236
+ import React25 from "react";
227
237
  import {
228
- lazy as lazy2,
229
- Suspense as Suspense3,
238
+ lazy as lazy3,
239
+ Suspense as Suspense4,
230
240
  useCallback as useCallback12,
231
241
  useEffect as useEffect24,
232
242
  useLayoutEffect as useLayoutEffect2,
@@ -487,12 +497,6 @@ function ChatStartupSkeleton() {
487
497
  );
488
498
  }
489
499
 
490
- // src/utils/storeBuild.ts
491
- var IS_STORE_BUILD = typeof __BANDIT_STORE_BUILD__ !== "undefined" && __BANDIT_STORE_BUILD__ === true;
492
-
493
- // src/management/shell/console.ts
494
- var managementConsoleVariant = () => IS_STORE_BUILD || isNativeShell() ? "native" : "web";
495
-
496
500
  // src/store/canvasStore.ts
497
501
  import { create } from "zustand";
498
502
  var useCanvasStore = create((set) => ({
@@ -762,6 +766,7 @@ var BanditChatLogo = ({
762
766
  var bandit_chat_logo_default = BanditChatLogo;
763
767
 
764
768
  // src/chat/chat-messages.tsx
769
+ import React7 from "react";
765
770
  import { Box as Box10 } from "@mui/material";
766
771
 
767
772
  // src/modals/chat-modal/ai-response-text-field.tsx
@@ -4910,6 +4915,7 @@ var ICONS = {
4910
4915
  create_file: FileText,
4911
4916
  ask_user: HelpCircle,
4912
4917
  create_schedule: Clock,
4918
+ crew: Users,
4913
4919
  health_check: Activity,
4914
4920
  context: Library,
4915
4921
  transcribe: Mic,
@@ -5041,6 +5047,7 @@ var tool_trail_view_default = ToolTrail;
5041
5047
 
5042
5048
  // src/chat/chat-messages.tsx
5043
5049
  import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
5050
+ var CrewTaskCard = React7.lazy(() => import("./crew-task-card-ZCXFTR34.mjs"));
5044
5051
  var WORKING_TOKEN_RE = /\s*<<working:([^>]*)>>\s*$/;
5045
5052
  var BYTE_TOKEN_RUN_RE = /(?:<0x[0-9A-Fa-f]{2}>)+/g;
5046
5053
  var decodeByteTokens = (text) => {
@@ -5146,6 +5153,7 @@ var ChatMessages = ({
5146
5153
  },
5147
5154
  children: [
5148
5155
  /* @__PURE__ */ jsx12(tool_trail_view_default, { items: entry.toolsUsed, attachments: entry.images }),
5156
+ crewTaskIdOf(entry.toolsUsed) && /* @__PURE__ */ jsx12(React7.Suspense, { fallback: null, children: /* @__PURE__ */ jsx12(CrewTaskCard, { taskId: crewTaskIdOf(entry.toolsUsed) }) }),
5149
5157
  /* @__PURE__ */ jsx12(
5150
5158
  Box10,
5151
5159
  {
@@ -5253,7 +5261,7 @@ var ChatMessages = ({
5253
5261
  var chat_messages_default = ChatMessages;
5254
5262
 
5255
5263
  // src/chat/chat-input.tsx
5256
- import React8, { useEffect as useEffect9, useRef as useRef5, useState as useState12 } from "react";
5264
+ import React9, { Suspense as Suspense2, lazy, useEffect as useEffect9, useRef as useRef5, useState as useState12 } from "react";
5257
5265
  import {
5258
5266
  Box as Box13,
5259
5267
  Button as Button3,
@@ -6391,6 +6399,102 @@ function markAiReminderSeen(now = Date.now(), storage = defaultStorage()) {
6391
6399
  // src/chat/chat-input.tsx
6392
6400
  import { useTheme as useTheme10, alpha as alpha7 } from "@mui/material/styles";
6393
6401
 
6402
+ // src/services/crew/crewTool.ts
6403
+ var DELEGATE_TOOL_NAME = "delegate_to_bandit";
6404
+ var isDelegateTool = (name) => name === DELEGATE_TOOL_NAME || name === "delegate-to-bandit";
6405
+ var pick = (raw, ...keys) => {
6406
+ for (const key of keys) {
6407
+ const value = raw[key];
6408
+ if (value !== void 0 && value !== null && value !== "") return value;
6409
+ }
6410
+ return void 0;
6411
+ };
6412
+ var asString = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
6413
+ var banditHandle = (name) => name.trim().replace(/^@/, "").toLowerCase();
6414
+ var normalizeDelegateArgs = (raw, bandits = []) => {
6415
+ const args = raw ?? {};
6416
+ const problems = [];
6417
+ const named = asString(
6418
+ pick(
6419
+ args,
6420
+ "bandit",
6421
+ "bandit_name",
6422
+ "banditName",
6423
+ "name",
6424
+ "to",
6425
+ "teammate",
6426
+ "assignee"
6427
+ )
6428
+ );
6429
+ const prompt = asString(
6430
+ pick(args, "task", "prompt", "instruction", "instructions", "request", "goal")
6431
+ ) ?? "";
6432
+ const context = asString(pick(args, "context", "background", "notes", "details"));
6433
+ let banditId;
6434
+ let banditName = named ? named.replace(/^@/, "") : void 0;
6435
+ if (banditName) {
6436
+ const match = bandits.find(
6437
+ (b) => banditHandle(b.name) === banditHandle(banditName)
6438
+ );
6439
+ if (match) {
6440
+ banditId = match.id;
6441
+ banditName = match.name;
6442
+ } else if (bandits.length > 0) {
6443
+ problems.push(
6444
+ `There is no Bandit called ${banditName} on your crew; pick one below.`
6445
+ );
6446
+ banditName = void 0;
6447
+ }
6448
+ } else {
6449
+ problems.push("Choose which Bandit should take this.");
6450
+ }
6451
+ if (!prompt) problems.push("Describe what the Bandit should do.");
6452
+ return { input: { banditId, banditName, prompt, context }, problems };
6453
+ };
6454
+ var parseCrewMention = (text, bandits) => {
6455
+ if (!bandits.length) return null;
6456
+ const match = /^\s*@([A-Za-z][\w-]{0,30})(?![\w.-]*@)(?:[\s,:—–-]+|$)([\s\S]*)$/.exec(
6457
+ text
6458
+ );
6459
+ if (!match) return null;
6460
+ const handle = match[1].toLowerCase();
6461
+ const bandit = bandits.find((b) => banditHandle(b.name) === handle);
6462
+ if (!bandit) return null;
6463
+ return { bandit, prompt: match[2].trim() };
6464
+ };
6465
+ var matchingBandits = (partial, bandits) => {
6466
+ const prefix = partial.trim().replace(/^@/, "").toLowerCase();
6467
+ return bandits.filter((b) => banditHandle(b.name).startsWith(prefix));
6468
+ };
6469
+ var mentionInProgress = (draft) => {
6470
+ const match = /^\s*@([\w-]*)$/.exec(draft);
6471
+ return match ? match[1] : null;
6472
+ };
6473
+ var requestCrewDelegation = async (args) => {
6474
+ const store = useCrewStore.getState();
6475
+ const bandits = store.loaded ? store.bandits : await store.refresh();
6476
+ const { input, problems } = normalizeDelegateArgs(args, bandits);
6477
+ return useCrewStore.getState().request(input, problems);
6478
+ };
6479
+ var crewOutcomeForModel = (outcome) => {
6480
+ switch (outcome.status) {
6481
+ case "started": {
6482
+ const t = outcome.task;
6483
+ return `The user confirmed and ${t.banditName} has started the task (task id ${t.id}): "${t.prompt}". The task runs in the background; its steps, any approval it needs and the finished result (with a Stash link when there is a file) show in a card in this conversation as they happen. Tell the user in one or two sentences that ${t.banditName} is on it and that you will not have the result in this reply. Do not call delegate_to_bandit again for the same task and do not invent a result.`;
6484
+ }
6485
+ case "cancelled":
6486
+ return "The user did not hand the task to a Bandit, so nothing was started. Ask briefly whether they want it changed or want you to do it yourself, or move on.";
6487
+ default:
6488
+ return `Starting the Bandit task failed: ${outcome.error}. Tell the user plainly. If no Bandit exists yet, mention they can create one under Settings \u2192 Crew.`;
6489
+ }
6490
+ };
6491
+ var crewToolProtocolLine = (bandits) => {
6492
+ if (!bandits.length) return "";
6493
+ const names = bandits.map((b) => b.name).join(", ");
6494
+ return `
6495
+ * ${DELEGATE_TOOL_NAME}({"bandit": "Riley", "task": "Research Acme's 2026 pricing changes and draft a one-page brief"}) - ONLY when the user asks to hand work to one of their Bandits BY NAME ("ask Riley to\u2026", "have Scout\u2026", "@Riley \u2026"), or asks for a longer research/drafting job to be done in the background by the crew. The user's Bandits: ${names}. Put the complete task in "task" (self-contained: the Bandit has no chat context unless you pass the relevant facts in "context"). This opens a confirmation card the user must approve; wait for the tool result and never say a Bandit is working until it reports the task started. Do not use it for questions you can answer yourself.`;
6496
+ };
6497
+
6394
6498
  // src/services/images/imageTools.ts
6395
6499
  var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set(["image_generation", "image_edit"]);
6396
6500
  var IMAGE_TOOL_TIMEOUT_MS = 12e4;
@@ -6696,6 +6800,7 @@ var useVoiceModeStore = create3((set) => ({
6696
6800
  // src/chat/chat-input.tsx
6697
6801
  import { useShallow } from "zustand/react/shallow";
6698
6802
  import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
6803
+ var CrewMentionPicker = lazy(() => import("./crew-mention-picker-OSJ2NHUB.mjs"));
6699
6804
  var DOCUMENT_ACCEPT = ".txt,.docx,.pdf,.md,.json,.js,.ts,.tsx,.py,.java,.c,.cpp,.cs,image/*,.heic,.heif";
6700
6805
  var MEETING_ACCEPT = `${DOCUMENT_ACCEPT},.vtt,.srt,audio/*,video/*`;
6701
6806
  var isTextDocument = (file) => /\.(txt|md|docx)$/i.test(file.name) || file.type.startsWith("text/");
@@ -6778,6 +6883,17 @@ var ChatInput = (props) => {
6778
6883
  const canTranscribeRecordings = meetingNotesEnabled && hasSTT();
6779
6884
  const gatewayUrlLower = packageSettings?.gatewayApiUrl?.toLowerCase?.() ?? "";
6780
6885
  const isPlaygroundMode2 = packageSettings?.playgroundMode === true || gatewayUrlLower.startsWith("playground://") || typeof window !== "undefined" && window.location.pathname.includes("/playground");
6886
+ const crewBandits = useCrewStore((state) => state.bandits);
6887
+ const [mentionIndex, setMentionIndex] = useState12(0);
6888
+ const mentionQuery = !isPlaygroundMode2 && crewBandits.length > 0 && crewSurfacesEnabled() ? mentionInProgress(inputValue) : null;
6889
+ const mentionMatches = mentionQuery !== null ? matchingBandits(mentionQuery, crewBandits) : [];
6890
+ const mentionOpen = mentionMatches.length > 0;
6891
+ const highlightedMention = Math.min(mentionIndex, Math.max(0, mentionMatches.length - 1));
6892
+ const pickMention = (bandit) => {
6893
+ setInputValue(`@${bandit.name} `);
6894
+ setMentionIndex(0);
6895
+ inputRef.current?.focus();
6896
+ };
6781
6897
  useEffect9(() => {
6782
6898
  const lockViewportHeight = () => {
6783
6899
  if (isMobile) {
@@ -6808,7 +6924,7 @@ var ChatInput = (props) => {
6808
6924
  loadBrandingText();
6809
6925
  }, []);
6810
6926
  const [attachmentNotice, setAttachmentNotice] = useState12(null);
6811
- const attachImages = React8.useCallback(
6927
+ const attachImages = React9.useCallback(
6812
6928
  async (images) => {
6813
6929
  if (images.length === 0) return;
6814
6930
  let notice = null;
@@ -7055,6 +7171,23 @@ var ChatInput = (props) => {
7055
7171
  };
7056
7172
  const handleKeyPress = (e) => {
7057
7173
  if (isImeComposing(e)) return;
7174
+ if (mentionOpen) {
7175
+ if (e.key === "ArrowDown") {
7176
+ e.preventDefault();
7177
+ setMentionIndex((i) => (i + 1) % mentionMatches.length);
7178
+ return;
7179
+ }
7180
+ if (e.key === "ArrowUp") {
7181
+ e.preventDefault();
7182
+ setMentionIndex((i) => (i - 1 + mentionMatches.length) % mentionMatches.length);
7183
+ return;
7184
+ }
7185
+ if (e.key === "Enter" || e.key === "Tab") {
7186
+ e.preventDefault();
7187
+ pickMention(mentionMatches[highlightedMention]);
7188
+ return;
7189
+ }
7190
+ }
7058
7191
  if (e.key === "Enter" && !e.shiftKey) {
7059
7192
  e.preventDefault();
7060
7193
  void handleSubmit();
@@ -7165,6 +7298,7 @@ var ChatInput = (props) => {
7165
7298
  ...dropHandlers,
7166
7299
  "data-dragging": dragging ? "true" : void 0,
7167
7300
  sx: {
7301
+ position: "relative",
7168
7302
  width: "100%",
7169
7303
  bgcolor: tokens.composer.surface,
7170
7304
  color: tokens.composer.foreground,
@@ -7194,6 +7328,15 @@ var ChatInput = (props) => {
7194
7328
  gap: isMobile ? compactMobile ? 0.75 : 1 : 0.5
7195
7329
  },
7196
7330
  children: [
7331
+ mentionOpen && /* @__PURE__ */ jsx16(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx16(
7332
+ CrewMentionPicker,
7333
+ {
7334
+ bandits: mentionMatches,
7335
+ highlighted: highlightedMention,
7336
+ onPick: pickMention,
7337
+ onHover: setMentionIndex
7338
+ }
7339
+ ) }),
7197
7340
  attachmentNotice && !sendError && /* @__PURE__ */ jsx16(
7198
7341
  Box13,
7199
7342
  {
@@ -9469,6 +9612,33 @@ var createScheduleTool = {
9469
9612
  },
9470
9613
  isBuiltIn: true
9471
9614
  };
9615
+ var delegateToBanditTool = {
9616
+ id: "delegate-to-bandit",
9617
+ name: "delegate_to_bandit",
9618
+ description: "Hand a task to one of the user's named Bandits (Bandit Crew). The Bandit works in the background with web search and file creation, asks before any action that affects other people, and files the result in Stash. Opens a confirmation card the user must approve.",
9619
+ enabled: true,
9620
+ type: "function",
9621
+ function: {
9622
+ name: "delegate_to_bandit",
9623
+ description: `Delegate a task to one of the user's Bandits by name ("ask Riley to\u2026", "@Riley \u2026"). The Bandit runs the task in the background on the user's account; its steps, approvals and result appear in a card in the conversation. The user confirms (and can edit) the hand-off in a card before it starts; never claim a Bandit is working until the tool result says the task started.`,
9624
+ parameters: {
9625
+ type: "object",
9626
+ properties: {
9627
+ bandit: { type: "string", description: "The Bandit's name, e.g. 'Riley'" },
9628
+ task: {
9629
+ type: "string",
9630
+ description: "The complete task, self-contained (the Bandit has no chat context)."
9631
+ },
9632
+ context: {
9633
+ type: "string",
9634
+ description: "Optional facts from the conversation the Bandit needs (passed as data, not instructions)."
9635
+ }
9636
+ },
9637
+ required: ["bandit", "task"]
9638
+ }
9639
+ },
9640
+ isBuiltIn: true
9641
+ };
9472
9642
  var defaultTools = [
9473
9643
  healthCheckTool,
9474
9644
  webSearchTool,
@@ -9476,7 +9646,8 @@ var defaultTools = [
9476
9646
  imageGenerationTool,
9477
9647
  imageEditTool,
9478
9648
  createFileTool,
9479
- createScheduleTool
9649
+ createScheduleTool,
9650
+ delegateToBanditTool
9480
9651
  ];
9481
9652
  var useMCPToolsStore = create6((set, get) => {
9482
9653
  const persist = async () => {
@@ -9859,7 +10030,8 @@ var executeMCPTool = async (toolCall) => {
9859
10030
  var getEnabledMCPToolsForAI = () => {
9860
10031
  const { getEnabledTools } = useMCPToolsStore.getState();
9861
10032
  const enabledTools = getEnabledTools();
9862
- return enabledTools.map((tool) => ({
10033
+ const crewOffered = crewSurfacesEnabled() && useCrewStore.getState().bandits.length > 0;
10034
+ return enabledTools.filter((tool) => tool.function.name !== "delegate_to_bandit" || crewOffered).map((tool) => ({
9863
10035
  type: tool.type,
9864
10036
  function: tool.function
9865
10037
  }));
@@ -10925,6 +11097,7 @@ ${CONTEXT_USAGE_RULES}`;
10925
11097
  const paramSuffix = params.length ? ` (parameters: ${params.join(", ")})` : "";
10926
11098
  return `- ${tool.function.name}: ${tool.function.description}${paramSuffix}`;
10927
11099
  }).join("\n");
11100
+ const crewProtocolLine = crewSurfacesEnabled() ? crewToolProtocolLine(useCrewStore.getState().bandits) : "";
10928
11101
  const protocol = `
10929
11102
 
10930
11103
  TOOL USAGE PROTOCOL
@@ -10936,7 +11109,7 @@ ${TOOL_JUDGMENT_RULES}
10936
11109
  * image_edit({"instruction": "..."}) - ONLY when the user attached a photo to THIS message and asks to change it (touch up lighting, restyle, remove the background, remove or add something). The attached photo is sent automatically; never ask for a link or describe the photo back. Same daily allowance as image_generation. Nothing sexual involving real people, nothing involving minors in revealing contexts, nothing meant to deceive (fake documents, compromising scenes, impersonation) \u2014 refuse those yourself
10937
11110
  * 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, xlsx, pdf. For docx/pptx write well-structured Markdown (use "## " headings to start each slide for pptx). For pdf, prefer complete HTML with inline CSS. For a SPREADSHEET use format "xlsx" and put the data in a "spreadsheet" object, e.g. create_file({"content": "FY26 budget by region", "filename": "budget.xlsx", "format": "xlsx", "spreadsheet": {"title": "FY26 Budget", "sheets": [{"name": "Summary", "columns": [{"header": "Region", "type": "text"}, {"header": "Revenue", "type": "currency", "currency": "USD"}, {"header": "Growth", "type": "percent"}, {"header": "Closed", "type": "date"}], "rows": [["North", 1200.5, 0.12, "2026-01-31"], ["South", 800, 0.08, "2026-02-28"]], "totals": true}]}}) \u2014 numbers as JSON numbers (never "1,200" strings), dates as ISO strings, percents as fractions, {"formula": "B2*C2"} for a formula, one entry in "sheets" per tab; never CSV text for xlsx. The tool result says whether the file was saved to the user's Stash (lasting link \u2014 say so) or is a temporary link (~1 hour \u2014 tell the user it expires). If it is unclear whether they want it shown inline vs. as a downloadable file, use ask_user first.
10938
11111
  * 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.
10939
- * create_schedule({"name": "Morning AI brief", "prompt": "Summarise the most important AI news of the past day with sources", "preset": "weekdays", "time": "08:00"}) - when the user wants something done on a RECURRING schedule or at a set time each day or week ("every weekday at 8", "each morning", "daily", "every Monday", "send me \u2026 every \u2026"). Put the complete task in "prompt" (self-contained: the run has no chat context). preset: daily | weekdays | weekly (add "weekday") | custom (add "cron"). Omit time_zone to use the user's own. This opens a confirmation card the user must approve and may edit; wait for the tool result and never say a schedule exists until it reports it was created.
11112
+ * create_schedule({"name": "Morning AI brief", "prompt": "Summarise the most important AI news of the past day with sources", "preset": "weekdays", "time": "08:00"}) - when the user wants something done on a RECURRING schedule or at a set time each day or week ("every weekday at 8", "each morning", "daily", "every Monday", "send me \u2026 every \u2026"). Put the complete task in "prompt" (self-contained: the run has no chat context). preset: daily | weekdays | weekly (add "weekday") | custom (add "cron"). Omit time_zone to use the user's own. This opens a confirmation card the user must approve and may edit; wait for the tool result and never say a schedule exists until it reports it was created.${crewProtocolLine}
10940
11113
  - Examples of what NOT to use tools for: "who are you?", "what is React?", "explain machine learning", "how does X work?", general programming questions.
10941
11114
  - When a tool is truly needed, call exactly ONE tool that best matches the request.
10942
11115
  - Begin tool usage with a fenced code block: \`\`\`tool_code
@@ -11054,6 +11227,8 @@ ${protocol}`;
11054
11227
  "ask-user",
11055
11228
  "create_schedule",
11056
11229
  "create-schedule",
11230
+ "delegate_to_bandit",
11231
+ "delegate-to-bandit",
11057
11232
  ...getEnabledMCPToolsForAI().map((t) => t.function?.name).filter((n) => !!n && /^[\w-]+$/.test(n))
11058
11233
  ])
11059
11234
  );
@@ -11071,11 +11246,11 @@ ${protocol}`;
11071
11246
  const fenceIdx = t.search(/```(?:tool_code|TOOL_CODE)/);
11072
11247
  if (fenceIdx !== -1) t = t.slice(0, fenceIdx);
11073
11248
  const callIdx = t.search(
11074
- /\b(?:web_search|web_fetch|image_generation|image_edit|create_file|ask_user|create_schedule)\s*\(/
11249
+ /\b(?:web_search|web_fetch|image_generation|image_edit|create_file|ask_user|create_schedule|delegate_to_bandit)\s*\(/
11075
11250
  );
11076
11251
  if (callIdx !== -1) t = t.slice(0, callIdx);
11077
11252
  const jsonIdx = t.search(
11078
- /\{\s*"name"\s*:\s*"(?:web_search|web_fetch|image_generation|image_edit|create_file|ask_user|create_schedule)"/
11253
+ /\{\s*"name"\s*:\s*"(?:web_search|web_fetch|image_generation|image_edit|create_file|ask_user|create_schedule|delegate_to_bandit)"/
11079
11254
  );
11080
11255
  if (jsonIdx !== -1) t = t.slice(0, jsonIdx);
11081
11256
  t = t.replace(/```[\w-]*\s*$/, "");
@@ -11149,17 +11324,17 @@ ${protocol}`;
11149
11324
  setIsThinking?.(inThinkBlock);
11150
11325
  const visibleMessage = stripThinking2(fullMessage);
11151
11326
  const bareToolMatch = visibleMessage.match(
11152
- /\b(web_search|web_fetch|image_generation|image_edit|create_file|ask_user|create_schedule)\s*\(/
11327
+ /\b(web_search|web_fetch|image_generation|image_edit|create_file|ask_user|create_schedule|delegate_to_bandit)\s*\(/
11153
11328
  ) || // JSON-form calls from cloud/native-tool engines.
11154
11329
  visibleMessage.match(
11155
- /\{\s*"name"\s*:\s*"(web_search|web_fetch|image_generation|image_edit|create_file|ask_user|create_schedule)"/
11330
+ /\{\s*"name"\s*:\s*"(web_search|web_fetch|image_generation|image_edit|create_file|ask_user|create_schedule|delegate_to_bandit)"/
11156
11331
  );
11157
11332
  if (/```(?:tool_code|TOOL_CODE)/.test(visibleMessage) || bareToolMatch) {
11158
11333
  sawToolBlock = true;
11159
11334
  clearFlushTimer();
11160
11335
  const fn = bareToolMatch?.[1];
11161
11336
  startWorking(
11162
- fn === "create_schedule" ? "Setting up your schedule" : fn === "create_file" ? "Creating your file" : fn === "web_search" ? "Searching the web" : fn === "web_fetch" ? "Reading the page" : fn === "image_generation" ? "Generating the image" : fn === "image_edit" ? "Editing the photo" : "Working on it"
11337
+ fn === "delegate_to_bandit" ? "Handing this to the crew" : fn === "create_schedule" ? "Setting up your schedule" : fn === "create_file" ? "Creating your file" : fn === "web_search" ? "Searching the web" : fn === "web_fetch" ? "Reading the page" : fn === "image_generation" ? "Generating the image" : fn === "image_edit" ? "Editing the photo" : "Working on it"
11163
11338
  );
11164
11339
  }
11165
11340
  latestDisplayMessage = visibleMessage;
@@ -11487,6 +11662,30 @@ A: ${(answers[q.id] || "").trim() || "(no answer)"}`
11487
11662
  });
11488
11663
  continue;
11489
11664
  }
11665
+ if (isDelegateTool(functionName)) {
11666
+ enhancedMessage = enhancedMessage.replace(match, "");
11667
+ clearFlushTimer();
11668
+ stopWorking();
11669
+ const preamble = stripToolBlocks(fullMessage).trim();
11670
+ setStreamBuffer(preamble || "_Confirm the hand-off below\u2026_");
11671
+ pushTrail(pendingTrailItem(functionName, parsedParams));
11672
+ const outcome = await requestCrewDelegation(
11673
+ parsedParams
11674
+ );
11675
+ telemetryEvent("tool_loop:delegate_to_bandit", { status: outcome.status });
11676
+ const crewItem = trailItemForTool(functionName, parsedParams, {
11677
+ success: outcome.status === "started",
11678
+ data: outcome.status === "started" ? { taskId: outcome.task.id, banditName: outcome.task.banditName } : {},
11679
+ error: outcome.status === "failed" ? outcome.error : void 0
11680
+ });
11681
+ pushTrail(crewItem);
11682
+ if (outcome.status === "started") passCalls.push(crewItem);
11683
+ summarizableResults.push({
11684
+ name: functionName,
11685
+ output: crewOutcomeForModel(outcome)
11686
+ });
11687
+ continue;
11688
+ }
11490
11689
  const placeholderToken = `<<TOOL_LOADING_${functionName}_${Math.random().toString(36).slice(2)}>>`;
11491
11690
  enhancedMessage = enhancedMessage.replace(match, placeholderToken);
11492
11691
  clearFlushTimer();
@@ -11701,6 +11900,22 @@ A: ${(ans[q.id] || "").trim() || "(no answer)"}`
11701
11900
  );
11702
11901
  return scheduleOutcomeForModel(outcome);
11703
11902
  }
11903
+ if (isDelegateTool(fn)) {
11904
+ stopWorking();
11905
+ pushTrail(pendingTrailItem(fn, params));
11906
+ const outcome = await requestCrewDelegation(
11907
+ params
11908
+ );
11909
+ telemetryEvent("tool_loop:delegate_to_bandit", { status: outcome.status });
11910
+ record(
11911
+ trailItemForTool(fn, params, {
11912
+ success: outcome.status === "started",
11913
+ data: outcome.status === "started" ? { taskId: outcome.task.id, banditName: outcome.task.banditName } : {},
11914
+ error: outcome.status === "failed" ? outcome.error : void 0
11915
+ })
11916
+ );
11917
+ return crewOutcomeForModel(outcome);
11918
+ }
11704
11919
  const status = fn === "create_file" || fn === "create-file" ? "Creating your file" : fn === "web_search" || fn === "web-search" ? "Searching the web" : fn === "web_fetch" || fn === "web-fetch" ? "Reading the page" : fn === "image_generation" || fn === "image-generation" ? "Generating the image" : isImageEditTool(fn) ? "Editing the photo" : "Working on it";
11705
11920
  startWorking(status);
11706
11921
  pushTrail(pendingTrailItem(fn, params));
@@ -17184,12 +17399,12 @@ var MobileSidebar = ({ open, onClose, drag }) => {
17184
17399
  };
17185
17400
 
17186
17401
  // src/chat/sidebar/ProjectsPaneLazy.tsx
17187
- import { Suspense as Suspense2, lazy } from "react";
17402
+ import { Suspense as Suspense3, lazy as lazy2 } from "react";
17188
17403
  import { jsx as jsx28 } from "react/jsx-runtime";
17189
- var Pane = lazy(
17190
- () => import("./ProjectsPane-KE5DGFN4.mjs").then((module) => ({ default: module.ProjectsPane }))
17404
+ var Pane = lazy2(
17405
+ () => import("./ProjectsPane-C63C6GW5.mjs").then((module) => ({ default: module.ProjectsPane }))
17191
17406
  );
17192
- var ProjectsPane = (props) => /* @__PURE__ */ jsx28(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx28(Pane, { ...props }) });
17407
+ var ProjectsPane = (props) => /* @__PURE__ */ jsx28(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx28(Pane, { ...props }) });
17193
17408
 
17194
17409
  // src/chat/sidebar/useSidebarShortcuts.ts
17195
17410
  import { useEffect as useEffect18 } from "react";
@@ -17864,18 +18079,18 @@ ${rest}` : rest;
17864
18079
  if (current) chunks.push(current);
17865
18080
  return chunks;
17866
18081
  };
17867
- var asString = (value) => {
18082
+ var asString2 = (value) => {
17868
18083
  if (typeof value === "number") return String(value);
17869
18084
  if (typeof value !== "string") return void 0;
17870
18085
  const trimmed = value.trim();
17871
18086
  return trimmed === "" || /^(null|none|n\/a|unknown|not stated|tbd)$/i.test(trimmed) ? void 0 : trimmed;
17872
18087
  };
17873
18088
  var asStringList = (value) => {
17874
- if (typeof value === "string") return asString(value) ? [value.trim()] : [];
18089
+ if (typeof value === "string") return asString2(value) ? [value.trim()] : [];
17875
18090
  if (!Array.isArray(value)) return [];
17876
18091
  const out = [];
17877
18092
  for (const item of value) {
17878
- const text = asString(item) ?? (item && typeof item === "object" ? asString(
18093
+ const text = asString2(item) ?? (item && typeof item === "object" ? asString2(
17879
18094
  item.text ?? item.name ?? item.question ?? item.decision
17880
18095
  ) : void 0);
17881
18096
  if (text && !out.some((existing) => existing.toLowerCase() === text.toLowerCase())) {
@@ -17889,18 +18104,18 @@ var asActionItems = (value) => {
17889
18104
  const items = [];
17890
18105
  for (const raw of value) {
17891
18106
  if (typeof raw === "string") {
17892
- const task2 = asString(raw);
18107
+ const task2 = asString2(raw);
17893
18108
  if (task2) items.push({ task: task2 });
17894
18109
  continue;
17895
18110
  }
17896
18111
  if (!raw || typeof raw !== "object") continue;
17897
18112
  const record = raw;
17898
- const task = asString(record.task ?? record.action ?? record.item ?? record.text);
18113
+ const task = asString2(record.task ?? record.action ?? record.item ?? record.text);
17899
18114
  if (!task) continue;
17900
18115
  items.push({
17901
18116
  task,
17902
- owner: asString(record.owner ?? record.assignee ?? record.who),
17903
- due: asString(record.due ?? record.dueDate ?? record.deadline ?? record.when)
18117
+ owner: asString2(record.owner ?? record.assignee ?? record.who),
18118
+ due: asString2(record.due ?? record.dueDate ?? record.deadline ?? record.when)
17904
18119
  });
17905
18120
  }
17906
18121
  return items;
@@ -17945,10 +18160,10 @@ var parseNotesJson = (raw) => {
17945
18160
  if (!parsed || typeof parsed !== "object") return null;
17946
18161
  const record = parsed;
17947
18162
  const notes = {
17948
- title: asString(record.title) ?? "Meeting notes",
17949
- date: asString(record.date),
18163
+ title: asString2(record.title) ?? "Meeting notes",
18164
+ date: asString2(record.date),
17950
18165
  attendees: asStringList(record.attendees ?? record.participants),
17951
- summary: asString(record.summary) ?? "",
18166
+ summary: asString2(record.summary) ?? "",
17952
18167
  decisions: asStringList(record.decisions),
17953
18168
  actionItems: asActionItems(
17954
18169
  record.actionItems ?? record.action_items ?? record.actions
@@ -19389,7 +19604,7 @@ function createBanditTheme(config = {}, ...overrides) {
19389
19604
 
19390
19605
  // ../bandit-themes/src/provider.tsx
19391
19606
  import { ThemeProvider } from "@mui/material/styles";
19392
- import * as React20 from "react";
19607
+ import * as React21 from "react";
19393
19608
  import { jsx as jsx32 } from "react/jsx-runtime";
19394
19609
  function BanditThemeProvider({
19395
19610
  theme,
@@ -19399,9 +19614,9 @@ function BanditThemeProvider({
19399
19614
  style,
19400
19615
  children
19401
19616
  }) {
19402
- const resolved = React20.useMemo(() => theme ?? createBanditTheme(config), [theme, config]);
19617
+ const resolved = React21.useMemo(() => theme ?? createBanditTheme(config), [theme, config]);
19403
19618
  const { cssVars, mode } = resolved.bandit;
19404
- React20.useLayoutEffect(() => {
19619
+ React21.useLayoutEffect(() => {
19405
19620
  if (scope !== "document" || typeof document === "undefined") return;
19406
19621
  const root = document.documentElement;
19407
19622
  const previous = /* @__PURE__ */ new Map();
@@ -19549,7 +19764,7 @@ import Paper5 from "@mui/material/Paper";
19549
19764
  import Stack2 from "@mui/material/Stack";
19550
19765
  import Typography21 from "@mui/material/Typography";
19551
19766
  import { useTheme as useTheme24 } from "@mui/material/styles";
19552
- import * as React21 from "react";
19767
+ import * as React22 from "react";
19553
19768
  import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
19554
19769
  function TenantPreview({
19555
19770
  accent,
@@ -19602,7 +19817,7 @@ function PreviewPane({
19602
19817
  density,
19603
19818
  renderBody
19604
19819
  }) {
19605
- const theme = React21.useMemo(
19820
+ const theme = React22.useMemo(
19606
19821
  () => createBanditTheme({
19607
19822
  mode,
19608
19823
  skin,
@@ -20396,8 +20611,9 @@ var useVoiceMode = (config) => {
20396
20611
 
20397
20612
  // src/chat/chat.tsx
20398
20613
  import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
20399
- var ImageEditAcknowledgement = React24.lazy(() => import("./image-edit-acknowledgement-RZPQYKYJ.mjs"));
20400
- var ScheduleConfirmCard = lazy2(() => import("./schedule-confirm-card-D4K76HEA.mjs"));
20614
+ var ImageEditAcknowledgement = React25.lazy(() => import("./image-edit-acknowledgement-RZPQYKYJ.mjs"));
20615
+ var ScheduleConfirmCard = lazy3(() => import("./schedule-confirm-card-Y2JF2RBV.mjs"));
20616
+ var CrewDelegateCard = lazy3(() => import("./crew-delegate-card-I4SSZU5H.mjs"));
20401
20617
  var CLASSIC_DRAWER_WIDTH = 340;
20402
20618
  var ChatContent = () => {
20403
20619
  const packageSettings = usePackageSettingsStore((state) => state.settings);
@@ -20436,6 +20652,7 @@ var ChatContent = () => {
20436
20652
  const isVoiceModeEnabled = useVoiceModeStore((state) => state.enabled);
20437
20653
  const canvasOpen = useCanvasStore((state) => state.open);
20438
20654
  const schedulePending = useScheduleConfirmStore((state) => state.pending !== null);
20655
+ const crewPending = useCrewStore((state) => state.pending !== null);
20439
20656
  const previousVoiceModeEnabledRef = useRef19(isVoiceModeEnabled);
20440
20657
  const historyRef = useRef19(history);
20441
20658
  useEffect24(() => {
@@ -20625,6 +20842,9 @@ var ChatContent = () => {
20625
20842
  const GRACE_OFFSET_DESKTOP = 28;
20626
20843
  const GRACE_OFFSET_MOBILE = 20;
20627
20844
  const followStreamRef = useRef19(true);
20845
+ useEffect24(() => {
20846
+ if (crewSurfacesEnabled()) void useCrewStore.getState().refresh();
20847
+ }, []);
20628
20848
  const lastSpokenResponseRef = useRef19(null);
20629
20849
  const previousConversationIdRef = useRef19(null);
20630
20850
  const logoFadeTimeoutRef = useRef19(null);
@@ -21254,8 +21474,89 @@ var ChatContent = () => {
21254
21474
  handleStop();
21255
21475
  }
21256
21476
  }, [ttsStop, isStreaming, handleStop]);
21477
+ const delegateFromComposer = useCallback12(
21478
+ async (mention, question) => {
21479
+ setPendingMessage({ question });
21480
+ setIsStreaming(true);
21481
+ setResponseStarted(true);
21482
+ setIsSubmitting(true);
21483
+ setStreamBuffer("<<working:Waiting for your confirmation>>");
21484
+ const store = useConversationStore.getState();
21485
+ if (!store.conversations.find((c) => c.id === store.currentId)) {
21486
+ store.createConversation(question);
21487
+ setLogoVisible(true);
21488
+ }
21489
+ useConversationStore.getState().addToCurrent({
21490
+ question,
21491
+ answer: "...",
21492
+ placeholder: true,
21493
+ rawQuestion: question
21494
+ });
21495
+ const conversationId = useConversationStore.getState().currentId;
21496
+ followStreamRef.current = getScrollState().isNearBottom;
21497
+ setTimeout(() => {
21498
+ if (followStreamRef.current) scrollToBottom();
21499
+ }, 50);
21500
+ try {
21501
+ const outcome = await useCrewStore.getState().request(
21502
+ { banditId: mention.bandit.id, banditName: mention.bandit.name, prompt: mention.prompt },
21503
+ mention.prompt ? [] : ["Describe what the Bandit should do."]
21504
+ );
21505
+ const current = useConversationStore.getState();
21506
+ if (current.currentId !== conversationId) {
21507
+ debugLogger.warn("The Bandit hand-off finished for a conversation that is no longer open");
21508
+ return;
21509
+ }
21510
+ if (outcome.status === "started") {
21511
+ current.setLastToolTrail([
21512
+ {
21513
+ kind: "crew",
21514
+ tool: "delegate_to_bandit",
21515
+ label: `Delegated to ${outcome.task.banditName}`,
21516
+ detail: outcome.task.prompt,
21517
+ ok: true,
21518
+ crewTaskId: outcome.task.id
21519
+ }
21520
+ ]);
21521
+ current.replaceLastAnswer(
21522
+ `**${outcome.task.banditName}** is on it. The steps, any approval it needs and the result show below as they happen.`
21523
+ );
21524
+ } else if (outcome.status === "cancelled") {
21525
+ current.replaceLastAnswer("_No task was started._", void 0, false, void 0, true);
21526
+ } else {
21527
+ current.replaceLastAnswer(
21528
+ `_The task could not be started: ${outcome.error}_`,
21529
+ void 0,
21530
+ false,
21531
+ void 0,
21532
+ true
21533
+ );
21534
+ }
21535
+ } finally {
21536
+ setStreamBuffer("");
21537
+ setIsStreaming(false);
21538
+ setResponseStarted(false);
21539
+ setIsSubmitting(false);
21540
+ setPendingMessage(null);
21541
+ setLogoVisible(false);
21542
+ }
21543
+ },
21544
+ [
21545
+ getScrollState,
21546
+ scrollToBottom,
21547
+ setIsStreaming,
21548
+ setLogoVisible,
21549
+ setPendingMessage,
21550
+ setResponseStarted,
21551
+ setStreamBuffer
21552
+ ]
21553
+ );
21257
21554
  const handleSend = useCallback12(
21258
21555
  (question, images, displayQuestion) => {
21556
+ const mention = images.length === 0 && crewSurfacesEnabled() ? parseCrewMention(question, useCrewStore.getState().bandits) : null;
21557
+ if (mention) {
21558
+ return delegateFromComposer(mention, displayQuestion || question);
21559
+ }
21259
21560
  sendAckRef.current?.resolve();
21260
21561
  const acknowledged = new Promise((resolve, reject) => {
21261
21562
  sendAckRef.current = { resolve, reject };
@@ -21394,6 +21695,7 @@ var ChatContent = () => {
21394
21695
  },
21395
21696
  [
21396
21697
  aiProvider,
21698
+ delegateFromComposer,
21397
21699
  settleSendAck,
21398
21700
  chatContainerRef,
21399
21701
  generateName,
@@ -22110,8 +22412,9 @@ var ChatContent = () => {
22110
22412
  ) }),
22111
22413
  /* @__PURE__ */ jsx38(rate_limit_prompt_default, {}),
22112
22414
  /* @__PURE__ */ jsx38(ask_user_card_default, {}),
22113
- /* @__PURE__ */ jsx38(React24.Suspense, { fallback: null, children: /* @__PURE__ */ jsx38(ImageEditAcknowledgement, {}) }),
22114
- schedulePending && /* @__PURE__ */ jsx38(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx38(ScheduleConfirmCard, {}) }),
22415
+ /* @__PURE__ */ jsx38(React25.Suspense, { fallback: null, children: /* @__PURE__ */ jsx38(ImageEditAcknowledgement, {}) }),
22416
+ schedulePending && /* @__PURE__ */ jsx38(Suspense4, { fallback: null, children: /* @__PURE__ */ jsx38(ScheduleConfirmCard, {}) }),
22417
+ crewPending && /* @__PURE__ */ jsx38(Suspense4, { fallback: null, children: /* @__PURE__ */ jsx38(CrewDelegateCard, {}) }),
22115
22418
  /* @__PURE__ */ jsx38(
22116
22419
  chat_input_default,
22117
22420
  {
@@ -22202,8 +22505,6 @@ export {
22202
22505
  useNotification,
22203
22506
  NotificationProvider,
22204
22507
  styleInject,
22205
- IS_STORE_BUILD,
22206
- managementConsoleVariant,
22207
22508
  DocumentCard_default,
22208
22509
  knowledge_file_modal_default,
22209
22510
  useTTS,
@@ -22248,4 +22549,4 @@ export {
22248
22549
  useNotificationService,
22249
22550
  chat_default
22250
22551
  };
22251
- //# sourceMappingURL=chunk-WZGDQ4EA.mjs.map
22552
+ //# sourceMappingURL=chunk-MX37LD3Z.mjs.map