@burtson-labs/bandit-engine 2.0.125 → 2.0.127

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  chat_default
3
- } from "./chunk-2WFTEJPS.mjs";
3
+ } from "./chunk-RRSACJI2.mjs";
4
4
  import "./chunk-ONQMRE2G.mjs";
5
5
  import "./chunk-574ILQTT.mjs";
6
6
  import "./chunk-K2JGFKEB.mjs";
@@ -13,4 +13,4 @@ import "./chunk-BJTO5JO5.mjs";
13
13
  export {
14
14
  chat_default as default
15
15
  };
16
- //# sourceMappingURL=chat-SC6CFUED.mjs.map
16
+ //# sourceMappingURL=chat-2XYHFUT2.mjs.map
@@ -9754,7 +9754,7 @@ var MCPToolsTabV2_default = MCPToolsTabV2;
9754
9754
 
9755
9755
  // src/management/management.tsx
9756
9756
  import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
9757
- var preloadChatPage = () => import("./chat-SC6CFUED.mjs");
9757
+ var preloadChatPage = () => import("./chat-2XYHFUT2.mjs");
9758
9758
  var buildCapabilitiesUrl = (gatewayApiUrl) => {
9759
9759
  const trimmed = gatewayApiUrl.replace(/\/$/, "");
9760
9760
  if (trimmed.endsWith("/api")) {
@@ -11110,4 +11110,4 @@ export {
11110
11110
  useGatewayMemory,
11111
11111
  management_default
11112
11112
  };
11113
- //# sourceMappingURL=chunk-ID6CTZ3G.mjs.map
11113
+ //# sourceMappingURL=chunk-7NBLIL76.mjs.map
@@ -723,6 +723,38 @@ var source_chips_default = SourceChips;
723
723
 
724
724
  // src/chat/chat-messages.tsx
725
725
  import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
726
+ var WORKING_TOKEN_RE = /\s*<<working:([^>]*)>>\s*$/;
727
+ var ToolWorkingChip = ({ label }) => /* @__PURE__ */ jsxs3(
728
+ Box4,
729
+ {
730
+ sx: (theme) => ({
731
+ display: "inline-flex",
732
+ alignItems: "center",
733
+ gap: 1.25,
734
+ mt: 1.5,
735
+ px: 1.75,
736
+ py: 0.9,
737
+ borderRadius: 999,
738
+ bgcolor: theme.palette.action.hover,
739
+ border: `1px solid ${theme.palette.divider}`
740
+ }),
741
+ children: [
742
+ /* @__PURE__ */ jsxs3("div", { className: "typing-only", children: [
743
+ /* @__PURE__ */ jsx6("span", { className: "dot" }),
744
+ /* @__PURE__ */ jsx6("span", { className: "dot" }),
745
+ /* @__PURE__ */ jsx6("span", { className: "dot" })
746
+ ] }),
747
+ /* @__PURE__ */ jsx6(
748
+ Box4,
749
+ {
750
+ component: "span",
751
+ sx: { fontSize: "0.85rem", fontWeight: 500, color: "text.secondary", userSelect: "none" },
752
+ children: label || "Working on it"
753
+ }
754
+ )
755
+ ]
756
+ }
757
+ );
726
758
  var ChatMessages = ({
727
759
  pendingMessage,
728
760
  streamBuffer,
@@ -748,7 +780,9 @@ var ChatMessages = ({
748
780
  const isPlaceholder = entry.answer === "...";
749
781
  const showLoader = isLast && isStreaming && streamBuffer.trim() === "";
750
782
  const showThinking = showLoader && isThinking;
751
- const content = isLast ? isStreaming ? streamBuffer || "" : isPlaceholder ? "" : entry.answer : entry.answer;
783
+ const rawContent = isLast ? isStreaming ? streamBuffer || "" : isPlaceholder ? "" : entry.answer : entry.answer;
784
+ const workingMatch = isLast && isStreaming ? rawContent.match(WORKING_TOKEN_RE) : null;
785
+ const content = workingMatch ? rawContent.replace(WORKING_TOKEN_RE, "") : rawContent;
752
786
  const rawSources = entry.sourceFiles;
753
787
  const sourceSummaries = rawSources ? rawSources.filter((doc) => doc && typeof doc.name === "string" && doc.name.trim()).map((doc) => ({ id: doc.id || doc.name, name: doc.name.trim() })) : void 0;
754
788
  const responseNode = /* @__PURE__ */ jsxs3(
@@ -821,6 +855,7 @@ var ChatMessages = ({
821
855
  sources: sourceSummaries
822
856
  }
823
857
  ),
858
+ workingMatch && /* @__PURE__ */ jsx6(ToolWorkingChip, { label: workingMatch[1] }),
824
859
  /* @__PURE__ */ jsx6(source_chips_default, { content })
825
860
  ]
826
861
  }
@@ -833,7 +868,7 @@ var ChatMessages = ({
833
868
  {
834
869
  question: entry.question,
835
870
  response: responseNode,
836
- responseText: isLast ? isStreaming ? streamBuffer || "" : isPlaceholder ? "" : entry.answer : entry.answer,
871
+ responseText: content,
837
872
  backgroundColor: "#444",
838
873
  images: entry.images,
839
874
  memoryUpdated: entry.memoryUpdated && !isPlaceholder,
@@ -1705,23 +1740,24 @@ ${sanitize(
1705
1740
  bgcolor: inputBackground,
1706
1741
  borderRadius: "24px",
1707
1742
  boxShadow: isMobile ? `0 0 0 1px ${theme.palette.mode === "dark" ? "#444" : "#eee"}` : "0 4px 20px rgba(0, 0, 0, 0.4)",
1708
- px: isMobile ? compactMobile ? 1 : 1.5 : 3,
1709
- pt: isMobile ? compactMobile ? 0.6 : 1 : 1,
1710
- pb: isMobile ? compactMobile ? 0.9 : 1.5 : 1.5,
1743
+ px: isMobile ? compactMobile ? 1 : 1.5 : 2,
1744
+ pt: isMobile ? compactMobile ? 0.6 : 1 : 0.75,
1745
+ pb: isMobile ? compactMobile ? 0.9 : 1.5 : 0.75,
1711
1746
  display: "flex",
1712
1747
  flexDirection: "column",
1713
- gap: isMobile ? compactMobile ? 0.75 : 1 : 1
1748
+ gap: isMobile ? compactMobile ? 0.75 : 1 : 0.5
1714
1749
  },
1715
1750
  children: [
1716
1751
  /* @__PURE__ */ jsxs6(
1717
1752
  Box6,
1718
1753
  {
1719
1754
  sx: {
1720
- display: "flex",
1755
+ display: fileInputs.length > 0 || pastedImages.length > 0 ? "flex" : "none",
1721
1756
  flexWrap: "wrap",
1722
1757
  gap: attachmentsGap,
1723
1758
  alignItems: "center",
1724
- flexDirection: "row"
1759
+ flexDirection: "row",
1760
+ pt: isMobile ? 0 : 0.5
1725
1761
  },
1726
1762
  children: [
1727
1763
  fileInputs.map((file, idx) => /* @__PURE__ */ jsxs6(
@@ -1845,7 +1881,7 @@ ${sanitize(
1845
1881
  ]
1846
1882
  }
1847
1883
  ),
1848
- /* @__PURE__ */ jsx9(
1884
+ isMobile && /* @__PURE__ */ jsx9(
1849
1885
  Box6,
1850
1886
  {
1851
1887
  sx: {
@@ -1858,7 +1894,7 @@ ${sanitize(
1858
1894
  {
1859
1895
  fullWidth: true,
1860
1896
  multiline: true,
1861
- maxRows: isMobile ? compactMobile ? 6 : 12 : 6,
1897
+ maxRows: compactMobile ? 6 : 12,
1862
1898
  placeholder: isIncognito ? "Incognito chat \u2014 not saved" : "What's on your mind?",
1863
1899
  value: inputValue,
1864
1900
  onChange: (e) => setInputValue(e.target.value),
@@ -1869,18 +1905,15 @@ ${sanitize(
1869
1905
  disableUnderline: true,
1870
1906
  sx: {
1871
1907
  flex: "1 1 auto",
1872
- maxHeight: isMobile ? compactMobile ? "120px" : "150px" : "none",
1908
+ maxHeight: compactMobile ? "120px" : "150px",
1873
1909
  overflowY: "auto",
1874
1910
  color: fileText,
1875
1911
  "& .MuiInputBase-inputMultiline": {
1876
- fontSize: isMobile ? compactMobile ? "0.95rem" : "1rem" : "1rem"
1912
+ fontSize: compactMobile ? "0.95rem" : "1rem"
1877
1913
  }
1878
1914
  }
1879
1915
  },
1880
- sx: {
1881
- flex: 1,
1882
- "& .MuiInputBase-root": {}
1883
- }
1916
+ sx: { flex: 1 }
1884
1917
  }
1885
1918
  )
1886
1919
  }
@@ -1891,9 +1924,9 @@ ${sanitize(
1891
1924
  sx: {
1892
1925
  display: "flex",
1893
1926
  justifyContent: "space-between",
1894
- alignItems: "center",
1927
+ alignItems: "flex-end",
1895
1928
  gap: isMobile ? 0.6 : 1,
1896
- mt: isMobile ? 0.5 : 1
1929
+ mt: isMobile ? 0.5 : 0
1897
1930
  },
1898
1931
  children: [
1899
1932
  /* @__PURE__ */ jsxs6(
@@ -1980,6 +2013,30 @@ ${sanitize(
1980
2013
  ]
1981
2014
  }
1982
2015
  ),
2016
+ !isMobile && /* @__PURE__ */ jsx9(
2017
+ TextField2,
2018
+ {
2019
+ fullWidth: true,
2020
+ multiline: true,
2021
+ maxRows: 6,
2022
+ placeholder: isIncognito ? "Incognito chat \u2014 not saved" : "What's on your mind?",
2023
+ value: inputValue,
2024
+ onChange: (e) => setInputValue(e.target.value),
2025
+ onKeyDown: handleKeyPress,
2026
+ inputRef,
2027
+ variant: "standard",
2028
+ InputProps: {
2029
+ disableUnderline: true,
2030
+ sx: {
2031
+ color: fileText,
2032
+ maxHeight: "160px",
2033
+ overflowY: "auto",
2034
+ "& .MuiInputBase-inputMultiline": { fontSize: "1rem" }
2035
+ }
2036
+ },
2037
+ sx: { flex: 1, alignSelf: "center", mx: 0.5 }
2038
+ }
2039
+ ),
1983
2040
  /* @__PURE__ */ jsx9(Box6, { sx: { display: "flex", alignItems: "center", gap: isMobile ? 0.6 : 1 }, children: /* @__PURE__ */ jsx9(Tooltip2, { title: isStreaming ? "Stop response" : "Send message", children: /* @__PURE__ */ jsx9("span", { children: /* @__PURE__ */ jsx9(
1984
2041
  IconButton5,
1985
2042
  {
@@ -2082,8 +2139,9 @@ var chat_input_default = ChatInput;
2082
2139
 
2083
2140
  // src/chat/ask-user-card.tsx
2084
2141
  import { useEffect as useEffect5, useState as useState7 } from "react";
2085
- import { Box as Box7, Paper, Typography as Typography4, Button as Button2, TextField as TextField3, Chip, Stack } from "@mui/material";
2142
+ import { Box as Box7, Paper, Typography as Typography4, Button as Button2, TextField as TextField3, Chip, Grow } from "@mui/material";
2086
2143
  import { useTheme as useTheme7, alpha as alpha4 } from "@mui/material/styles";
2144
+ import { CircleHelp as QuestionIcon, Check as CheckIcon3 } from "lucide-react";
2087
2145
 
2088
2146
  // src/store/askUserStore.ts
2089
2147
  import { create as create3 } from "zustand";
@@ -2174,57 +2232,142 @@ var AskUserCard = () => {
2174
2232
  });
2175
2233
  submit(final);
2176
2234
  };
2177
- return /* @__PURE__ */ jsx10(Box7, { sx: { width: "100%", display: "flex", justifyContent: "center", px: { xs: 1, sm: 2 }, mb: 1.5 }, children: /* @__PURE__ */ jsxs7(
2235
+ const isSingleQuestion = pending.questions.length === 1;
2236
+ const handleOptionTap = (qid, label) => {
2237
+ setFreeform((p) => ({ ...p, [qid]: "" }));
2238
+ if (isSingleQuestion) {
2239
+ submit({ [qid]: label });
2240
+ return;
2241
+ }
2242
+ setSelected((p) => ({ ...p, [qid]: label }));
2243
+ };
2244
+ return /* @__PURE__ */ jsx10(Grow, { in: true, timeout: 300, children: /* @__PURE__ */ jsx10(Box7, { sx: { width: "100%", display: "flex", justifyContent: "center", px: { xs: 1, sm: 2 }, mb: 1.5 }, children: /* @__PURE__ */ jsxs7(
2178
2245
  Paper,
2179
2246
  {
2180
2247
  elevation: 0,
2181
2248
  sx: {
2182
2249
  width: "100%",
2183
2250
  maxWidth: 760,
2184
- p: { xs: 1.75, sm: 2.25 },
2185
- borderRadius: 2,
2186
- border: `1px solid ${alpha4(theme.palette.primary.main, 0.45)}`,
2187
- bgcolor: alpha4(theme.palette.primary.main, 0.06)
2251
+ p: { xs: 2, sm: 2.5 },
2252
+ borderRadius: 3,
2253
+ border: `1px solid ${theme.palette.divider}`,
2254
+ bgcolor: theme.palette.background.paper,
2255
+ boxShadow: `0 8px 32px ${alpha4(theme.palette.common.black, theme.palette.mode === "dark" ? 0.35 : 0.1)}`
2188
2256
  },
2189
2257
  children: [
2190
- /* @__PURE__ */ jsx10(Typography4, { variant: "caption", sx: { color: theme.palette.primary.main, fontWeight: 700, letterSpacing: 0.4 }, children: "BANDIT NEEDS A QUICK DECISION" }),
2191
- pending.questions.map((q) => /* @__PURE__ */ jsxs7(Box7, { sx: { mt: 1.5 }, children: [
2192
- q.header && /* @__PURE__ */ jsx10(Chip, { label: q.header, size: "small", sx: { mb: 0.75, fontWeight: 600 }, color: "primary", variant: "outlined" }),
2193
- /* @__PURE__ */ jsx10(Typography4, { sx: { fontWeight: 600, color: theme.palette.text.primary, mb: 1 }, children: q.question }),
2194
- q.options && q.options.length > 0 && /* @__PURE__ */ jsx10(Stack, { spacing: 1, children: q.options.map((opt) => {
2195
- const isSel = selected[q.id] === opt.label && !freeform[q.id]?.trim();
2196
- return /* @__PURE__ */ jsx10(
2197
- Button2,
2258
+ /* @__PURE__ */ jsxs7(Box7, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 0.5 }, children: [
2259
+ /* @__PURE__ */ jsx10(
2260
+ Box7,
2261
+ {
2262
+ sx: {
2263
+ width: 26,
2264
+ height: 26,
2265
+ borderRadius: "50%",
2266
+ display: "flex",
2267
+ alignItems: "center",
2268
+ justifyContent: "center",
2269
+ bgcolor: alpha4(theme.palette.primary.main, 0.12),
2270
+ color: theme.palette.primary.main,
2271
+ flexShrink: 0
2272
+ },
2273
+ children: /* @__PURE__ */ jsx10(QuestionIcon, { size: 16 })
2274
+ }
2275
+ ),
2276
+ /* @__PURE__ */ jsx10(Typography4, { variant: "body2", sx: { color: theme.palette.text.secondary, fontWeight: 600 }, children: "Quick question before I continue" })
2277
+ ] }),
2278
+ pending.questions.map((q) => /* @__PURE__ */ jsxs7(Box7, { sx: { mt: 1.75 }, children: [
2279
+ /* @__PURE__ */ jsxs7(Box7, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 1.25 }, children: [
2280
+ /* @__PURE__ */ jsx10(Typography4, { sx: { fontWeight: 600, fontSize: "1.02rem", color: theme.palette.text.primary }, children: q.question }),
2281
+ q.header && /* @__PURE__ */ jsx10(
2282
+ Chip,
2198
2283
  {
2199
- onClick: () => {
2200
- setSelected((p) => ({ ...p, [q.id]: opt.label }));
2201
- setFreeform((p) => ({ ...p, [q.id]: "" }));
2202
- },
2203
- variant: isSel ? "contained" : "outlined",
2204
- color: "primary",
2284
+ label: q.header,
2285
+ size: "small",
2205
2286
  sx: {
2206
- justifyContent: "flex-start",
2207
- textAlign: "left",
2208
- textTransform: "none",
2209
- py: 1,
2210
- px: 1.5,
2211
- borderColor: alpha4(theme.palette.primary.main, 0.4)
2212
- },
2213
- children: /* @__PURE__ */ jsxs7(Box7, { children: [
2214
- /* @__PURE__ */ jsx10(Typography4, { sx: { fontWeight: 600, lineHeight: 1.3 }, children: opt.label }),
2215
- opt.description && /* @__PURE__ */ jsx10(
2216
- Typography4,
2217
- {
2218
- variant: "caption",
2219
- sx: { color: isSel ? alpha4("#fff", 0.85) : theme.palette.text.secondary, display: "block" },
2220
- children: opt.description
2221
- }
2222
- )
2223
- ] })
2287
+ height: 20,
2288
+ fontSize: "0.7rem",
2289
+ fontWeight: 600,
2290
+ color: theme.palette.text.secondary,
2291
+ bgcolor: alpha4(theme.palette.text.primary, 0.06)
2292
+ }
2293
+ }
2294
+ )
2295
+ ] }),
2296
+ q.options && q.options.length > 0 && /* @__PURE__ */ jsx10(
2297
+ Box7,
2298
+ {
2299
+ sx: {
2300
+ display: "grid",
2301
+ gridTemplateColumns: { xs: "1fr", sm: q.options.length > 1 ? "1fr 1fr" : "1fr" },
2302
+ gap: 1
2224
2303
  },
2225
- opt.label
2226
- );
2227
- }) }),
2304
+ children: q.options.map((opt) => {
2305
+ const isSel = selected[q.id] === opt.label && !freeform[q.id]?.trim();
2306
+ return /* @__PURE__ */ jsxs7(
2307
+ Box7,
2308
+ {
2309
+ component: "button",
2310
+ type: "button",
2311
+ onClick: () => handleOptionTap(q.id, opt.label),
2312
+ sx: {
2313
+ font: "inherit",
2314
+ textAlign: "left",
2315
+ cursor: "pointer",
2316
+ display: "flex",
2317
+ alignItems: "flex-start",
2318
+ gap: 1,
2319
+ px: 1.5,
2320
+ py: 1.25,
2321
+ borderRadius: 2,
2322
+ border: `1.5px solid ${isSel ? theme.palette.primary.main : theme.palette.divider}`,
2323
+ bgcolor: isSel ? alpha4(theme.palette.primary.main, 0.08) : "transparent",
2324
+ color: "inherit",
2325
+ transition: "border-color 0.15s ease, background-color 0.15s ease, transform 0.1s ease",
2326
+ "&:hover": {
2327
+ borderColor: theme.palette.primary.main,
2328
+ bgcolor: alpha4(theme.palette.primary.main, 0.05)
2329
+ },
2330
+ "&:active": { transform: "scale(0.99)" }
2331
+ },
2332
+ children: [
2333
+ /* @__PURE__ */ jsx10(
2334
+ Box7,
2335
+ {
2336
+ sx: {
2337
+ width: 18,
2338
+ height: 18,
2339
+ mt: "2px",
2340
+ borderRadius: "50%",
2341
+ flexShrink: 0,
2342
+ display: "flex",
2343
+ alignItems: "center",
2344
+ justifyContent: "center",
2345
+ border: `1.5px solid ${isSel ? theme.palette.primary.main : alpha4(theme.palette.text.secondary, 0.5)}`,
2346
+ bgcolor: isSel ? theme.palette.primary.main : "transparent",
2347
+ color: theme.palette.getContrastText(theme.palette.primary.main),
2348
+ transition: "all 0.15s ease"
2349
+ },
2350
+ children: isSel && /* @__PURE__ */ jsx10(CheckIcon3, { size: 12, strokeWidth: 3 })
2351
+ }
2352
+ ),
2353
+ /* @__PURE__ */ jsxs7(Box7, { sx: { minWidth: 0 }, children: [
2354
+ /* @__PURE__ */ jsx10(Typography4, { sx: { fontWeight: 600, lineHeight: 1.35, fontSize: "0.92rem" }, children: opt.label }),
2355
+ opt.description && /* @__PURE__ */ jsx10(
2356
+ Typography4,
2357
+ {
2358
+ variant: "caption",
2359
+ sx: { color: theme.palette.text.secondary, display: "block", mt: 0.25 },
2360
+ children: opt.description
2361
+ }
2362
+ )
2363
+ ] })
2364
+ ]
2365
+ },
2366
+ opt.label
2367
+ );
2368
+ })
2369
+ }
2370
+ ),
2228
2371
  q.allowFreeform !== false && /* @__PURE__ */ jsx10(
2229
2372
  TextField3,
2230
2373
  {
@@ -2233,17 +2376,45 @@ var AskUserCard = () => {
2233
2376
  placeholder: q.options && q.options.length ? "Or type your own answer\u2026" : "Type your answer\u2026",
2234
2377
  value: freeform[q.id] ?? "",
2235
2378
  onChange: (e) => setFreeform((p) => ({ ...p, [q.id]: e.target.value })),
2236
- sx: { mt: q.options && q.options.length ? 1.25 : 0 }
2379
+ onKeyDown: (e) => {
2380
+ if (e.key === "Enter" && !e.shiftKey && answerFor(q.id)) {
2381
+ e.preventDefault();
2382
+ if (allAnswered) handleSubmit();
2383
+ }
2384
+ },
2385
+ sx: {
2386
+ mt: q.options && q.options.length ? 1.25 : 0,
2387
+ "& .MuiOutlinedInput-root": { borderRadius: 2 }
2388
+ }
2237
2389
  }
2238
2390
  )
2239
2391
  ] }, q.id)),
2240
- /* @__PURE__ */ jsxs7(Box7, { sx: { display: "flex", justifyContent: "flex-end", gap: 1, mt: 2 }, children: [
2241
- /* @__PURE__ */ jsx10(Button2, { onClick: cancel, color: "inherit", sx: { textTransform: "none", color: theme.palette.text.secondary }, children: "Skip" }),
2242
- /* @__PURE__ */ jsx10(Button2, { onClick: handleSubmit, disabled: !allAnswered, variant: "contained", sx: { textTransform: "none" }, children: "Submit" })
2392
+ /* @__PURE__ */ jsxs7(Box7, { sx: { display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 1, mt: 2 }, children: [
2393
+ /* @__PURE__ */ jsx10(
2394
+ Button2,
2395
+ {
2396
+ onClick: cancel,
2397
+ color: "inherit",
2398
+ size: "small",
2399
+ sx: { textTransform: "none", color: theme.palette.text.secondary },
2400
+ children: "Skip \u2014 use your best judgment"
2401
+ }
2402
+ ),
2403
+ (!isSingleQuestion || Object.values(freeform).some((v) => v.trim())) && /* @__PURE__ */ jsx10(
2404
+ Button2,
2405
+ {
2406
+ onClick: handleSubmit,
2407
+ disabled: !allAnswered,
2408
+ variant: "contained",
2409
+ size: "small",
2410
+ sx: { textTransform: "none", borderRadius: 999, px: 2.5 },
2411
+ children: "Answer"
2412
+ }
2413
+ )
2243
2414
  ] })
2244
2415
  ]
2245
2416
  }
2246
- ) });
2417
+ ) }) });
2247
2418
  };
2248
2419
  var ask_user_card_default = AskUserCard;
2249
2420
 
@@ -4152,14 +4323,14 @@ TOOL USAGE PROTOCOL (conservative approach)
4152
4323
  * 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)
4153
4324
  * image_generation() - ONLY when explicitly asked to create or generate an image
4154
4325
  * 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.
4155
- * ask_user({"questions": [{"question": "...", "header": "Format", "options": [{"label": "Inline (Recommended)"}, {"label": "Download a file"}]}]}) - when you are genuinely BLOCKED on a decision that is the USER's to make and cannot resolve from the request, context, or sensible defaults (e.g. show content inline vs. let them download it, which format/option they want). Renders clickable options the user answers in one step \u2014 better than asking in prose and ending your turn. Give 1-4 questions, each with 2-4 options; if one is clearly best, list it first and append " (Recommended)". The user may also type their own answer; act on it directly.
4326
+ * 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.
4156
4327
  - For general questions about concepts, definitions, explanations, or how-to topics, use your built-in knowledge WITHOUT calling tools.
4157
4328
  - Examples of what NOT to use tools for: "who are you?", "what is React?", "explain machine learning", "how does X work?", general programming questions.
4158
4329
  - When a tool is truly needed, call exactly ONE tool that best matches the request.
4159
4330
  - Begin tool usage with a fenced code block: \`\`\`tool_code
4160
4331
  functionName({"param": "value"})
4161
4332
  \`\`\`
4162
- - If you cannot answer with your knowledge and context, and no suitable tool exists, ask a clarifying question.
4333
+ - If you cannot answer with your knowledge and context, and no suitable tool exists, use ask_user to ask a clarifying question with clickable options.
4163
4334
 
4164
4335
  Examples of appropriate tool usage:
4165
4336
 
@@ -4214,8 +4385,20 @@ ${protocol}`;
4214
4385
  return result.trimStart();
4215
4386
  };
4216
4387
  const stripToolBlocks = (text) => text.replace(/```(?:tool_code|TOOL_CODE)\s*\n[\s\S]*?\n```/gi, "").trim();
4388
+ const truncateAtToolApparatus = (text) => {
4389
+ let t = text;
4390
+ const fenceIdx = t.search(/```(?:tool_code|TOOL_CODE)/);
4391
+ if (fenceIdx !== -1) t = t.slice(0, fenceIdx);
4392
+ const callIdx = t.search(
4393
+ /\b(?:web_search|web_fetch|image_generation|create_file|ask_user)\s*\(/
4394
+ );
4395
+ if (callIdx !== -1) t = t.slice(0, callIdx);
4396
+ t = t.replace(/```[\w-]*\s*$/, "");
4397
+ const fenceCount = (t.match(/```/g) || []).length;
4398
+ if (fenceCount % 2 === 1) t = t.slice(0, t.lastIndexOf("```"));
4399
+ return t.trim();
4400
+ };
4217
4401
  let workingLabel = "Working on it";
4218
- let workingPreamble = "";
4219
4402
  const stopWorking = () => {
4220
4403
  if (workingTimer) {
4221
4404
  clearInterval(workingTimer);
@@ -4224,17 +4407,10 @@ ${protocol}`;
4224
4407
  };
4225
4408
  const startWorking = (label = "Working on it") => {
4226
4409
  workingLabel = label.replace(/[.…]+\s*$/, "").trim() || "Working on it";
4227
- workingPreamble = stripToolBlocks(stripThinking(fullMessage)).trim();
4228
- if (workingTimer) return;
4229
- let dots = 0;
4230
- const render = () => {
4231
- dots = (dots + 1) % 4;
4232
- setStreamBuffer(
4233
- `${workingPreamble}${workingPreamble ? "\n\n" : ""}_${workingLabel}${".".repeat(dots)}_`
4234
- );
4235
- };
4236
- render();
4237
- workingTimer = setInterval(render, 450);
4410
+ const preamble = truncateAtToolApparatus(stripToolBlocks(stripThinking(fullMessage)));
4411
+ setStreamBuffer(
4412
+ `${preamble}${preamble ? "\n\n" : ""}<<working:${workingLabel}>>`
4413
+ );
4238
4414
  };
4239
4415
  const flushNow = () => {
4240
4416
  clearFlushTimer();
@@ -4280,10 +4456,16 @@ ${protocol}`;
4280
4456
  const inThinkBlock = /<think>/.test(fullMessage) && !/<think>[\s\S]*<\/think>/.test(fullMessage);
4281
4457
  setIsThinking?.(inThinkBlock);
4282
4458
  const visibleMessage = stripThinking(fullMessage);
4283
- if (/```(?:tool_code|TOOL_CODE)/.test(visibleMessage) || /\b(?:web_search|web_fetch|image_generation|create_file|ask_user)\s*\(/.test(visibleMessage)) {
4459
+ const bareToolMatch = visibleMessage.match(
4460
+ /\b(web_search|web_fetch|image_generation|create_file|ask_user)\s*\(/
4461
+ );
4462
+ if (/```(?:tool_code|TOOL_CODE)/.test(visibleMessage) || bareToolMatch) {
4284
4463
  sawToolBlock = true;
4285
4464
  clearFlushTimer();
4286
- startWorking();
4465
+ const fn = bareToolMatch?.[1];
4466
+ startWorking(
4467
+ fn === "create_file" ? "Creating your file" : fn === "web_search" ? "Searching the web" : fn === "web_fetch" ? "Reading the page" : fn === "image_generation" ? "Generating the image" : "Working on it"
4468
+ );
4287
4469
  }
4288
4470
  latestDisplayMessage = visibleMessage;
4289
4471
  if (!sawToolBlock) {
@@ -6413,7 +6595,7 @@ import {
6413
6595
  TextField as TextField6,
6414
6596
  alpha as alpha7
6415
6597
  } from "@mui/material";
6416
- import { ChevronDown as ExpandMoreIcon2, ChevronUp as ExpandLessIcon, Plus as AddIcon3, Folder as FolderIcon3, FolderOpen as FolderOpenIcon, Inbox as InboxIcon2, X as CloseIcon5, Check as CheckIcon3 } from "lucide-react";
6598
+ import { ChevronDown as ExpandMoreIcon2, ChevronUp as ExpandLessIcon, Plus as AddIcon3, Folder as FolderIcon3, FolderOpen as FolderOpenIcon, Inbox as InboxIcon2, X as CloseIcon5, Check as CheckIcon4 } from "lucide-react";
6417
6599
  import { useTheme as useTheme12 } from "@mui/material/styles";
6418
6600
  import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
6419
6601
  var ProjectHeader = ({
@@ -6647,7 +6829,7 @@ var ProjectHeader = ({
6647
6829
  commitRename();
6648
6830
  },
6649
6831
  sx: { color: theme.palette.success.main },
6650
- children: /* @__PURE__ */ jsx15(CheckIcon3, { size: 16 })
6832
+ children: /* @__PURE__ */ jsx15(CheckIcon4, { size: 16 })
6651
6833
  }
6652
6834
  ) })
6653
6835
  ] }),
@@ -11498,4 +11680,4 @@ var chat_default = Chat;
11498
11680
  export {
11499
11681
  chat_default
11500
11682
  };
11501
- //# sourceMappingURL=chunk-2WFTEJPS.mjs.map
11683
+ //# sourceMappingURL=chunk-RRSACJI2.mjs.map