@burtson-labs/bandit-engine 2.0.70 → 2.0.72

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,10 +1,10 @@
1
1
  import {
2
2
  ChatProvider,
3
3
  chat_provider_default
4
- } from "./chunk-HHMGNCBS.mjs";
4
+ } from "./chunk-SXLI47FV.mjs";
5
5
  import "./chunk-ONQMRE2G.mjs";
6
6
  import "./chunk-6ITUH375.mjs";
7
- import "./chunk-IDZEEONG.mjs";
7
+ import "./chunk-DHYP4K5O.mjs";
8
8
  import "./chunk-4D7245ZO.mjs";
9
9
  import "./chunk-LWHSOEPR.mjs";
10
10
  import "./chunk-H3BYFEIE.mjs";
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-6ITUH375.mjs";
7
7
  import {
8
8
  useMCPToolsStore
9
- } from "./chunk-IDZEEONG.mjs";
9
+ } from "./chunk-DHYP4K5O.mjs";
10
10
  import {
11
11
  chat_modal_default
12
12
  } from "./chunk-JURUEF52.mjs";
@@ -9503,7 +9503,7 @@ var MCPToolsTabV2_default = MCPToolsTabV2;
9503
9503
 
9504
9504
  // src/management/management.tsx
9505
9505
  import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
9506
- var preloadChatPage = () => import("./chat-XW4JIAOE.mjs");
9506
+ var preloadChatPage = () => import("./chat-GLBQOPNT.mjs");
9507
9507
  var buildCapabilitiesUrl = (gatewayApiUrl) => {
9508
9508
  const trimmed = gatewayApiUrl.replace(/\/$/, "");
9509
9509
  if (trimmed.endsWith("/api")) {
@@ -10848,4 +10848,4 @@ export {
10848
10848
  useGatewayMemory,
10849
10849
  management_default
10850
10850
  };
10851
- //# sourceMappingURL=chunk-5SA7PQK4.mjs.map
10851
+ //# sourceMappingURL=chunk-7ZHLQXHL.mjs.map
@@ -94,7 +94,34 @@ var imageGenerationTool = {
94
94
  method: "POST",
95
95
  isBuiltIn: true
96
96
  };
97
- var defaultTools = [healthCheckTool, webSearchTool, webFetchTool, imageGenerationTool];
97
+ var createFileTool = {
98
+ id: "create-file",
99
+ name: "create_file",
100
+ description: "Create a downloadable file for the user (md, txt, csv, json, html, docx, pptx). Returns a temporary (~1 hour) download link.",
101
+ enabled: true,
102
+ type: "function",
103
+ function: {
104
+ name: "create_file",
105
+ description: "Generate a file the user can download. For docx/pptx write well-structured Markdown (headings, lists, tables; use '## ' headings to start each slide). Returns a short-lived (~1 hour) download URL \u2014 tell the user it expires.",
106
+ parameters: {
107
+ type: "object",
108
+ properties: {
109
+ content: { type: "string", description: "The file content (Markdown for docx/pptx; raw text for others)." },
110
+ filename: { type: "string", description: "Desired filename, e.g. 'report.docx' or 'notes.md'." },
111
+ format: {
112
+ type: "string",
113
+ enum: ["md", "txt", "csv", "json", "html", "xml", "yaml", "docx", "pptx"],
114
+ description: "File format. Defaults to md."
115
+ }
116
+ },
117
+ required: ["content", "format"]
118
+ }
119
+ },
120
+ endpoint: "/mcp/create-file",
121
+ method: "POST",
122
+ isBuiltIn: true
123
+ };
124
+ var defaultTools = [healthCheckTool, webSearchTool, webFetchTool, imageGenerationTool, createFileTool];
98
125
  var useMCPToolsStore = create((set, get) => ({
99
126
  tools: defaultTools,
100
127
  isLoaded: false,
@@ -181,4 +208,4 @@ var useMCPToolsStore = create((set, get) => ({
181
208
  export {
182
209
  useMCPToolsStore
183
210
  };
184
- //# sourceMappingURL=chunk-IDZEEONG.mjs.map
211
+ //# sourceMappingURL=chunk-DHYP4K5O.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store/mcpToolsStore.ts"],"sourcesContent":["/*\n © 2025 Burtson Labs — Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n ⚖️ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n 🔒 LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n 📋 AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-BF8A-82BFF7\nconst __banditFingerprint_store_mcpToolsStorets = 'BL-FP-414506-12AF';\nconst __auditTrail_store_mcpToolsStorets = 'BL-AU-MGOIKVW4-PIWV';\n// File: mcpToolsStore.ts | Path: src/store/mcpToolsStore.ts | Hash: bf8a12af\n\nimport { create } from \"zustand\";\nimport indexedDBService from \"../services/indexedDB/indexedDBService\";\nimport { debugLogger } from \"../services/logging/debugLogger\";\n\nexport interface MCPTool {\n id: string;\n name: string;\n description: string;\n enabled: boolean;\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: {\n type: \"object\";\n properties: Record<string, unknown>;\n required: string[];\n };\n };\n endpoint?: string; // API endpoint for gateway calls\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n isBuiltIn?: boolean;\n}\n\ninterface MCPToolsStore {\n tools: MCPTool[];\n isLoaded: boolean;\n addTool: (tool: Omit<MCPTool, \"id\">) => void;\n updateTool: (id: string, updates: Partial<MCPTool>) => void;\n deleteTool: (id: string) => void;\n toggleTool: (id: string) => void;\n loadTools: () => Promise<void>;\n saveTools: () => Promise<void>;\n getEnabledTools: () => MCPTool[];\n}\n\n// Built-in controller-backed tools\nconst healthCheckTool: MCPTool = {\n id: \"health-check\",\n name: \"check_gateway_health\",\n description: \"Check the health status of the gateway API service\",\n enabled: true,\n type: \"function\",\n function: {\n name: \"check_gateway_health\",\n description: \"Check the health status of the gateway API service\",\n parameters: {\n type: \"object\",\n properties: {},\n required: []\n }\n },\n endpoint: \"/mcp/health\",\n method: \"GET\",\n isBuiltIn: true\n};\n\nconst webSearchTool: MCPTool = {\n id: \"web-search\",\n name: \"web_search\",\n description: \"Search the web for current information, documentation, and facts\",\n enabled: true,\n type: \"function\",\n function: {\n name: \"web_search\",\n description: \"Search the web and return ranked results with snippets (and an optional summarized answer). Use for current events, documentation, libraries, error messages, and factual lookups.\",\n parameters: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"The search query — natural language or keywords\" },\n count: { type: \"number\", description: \"Number of results to return (1-10, default 5)\" },\n include_answer: { type: \"boolean\", description: \"Include a short summarized answer when available\" }\n },\n required: [\"query\"]\n }\n },\n endpoint: \"/mcp/web-search\",\n method: \"GET\",\n isBuiltIn: true\n};\n\nconst webFetchTool: MCPTool = {\n id: \"web-fetch\",\n name: \"web_fetch\",\n description: \"Fetch the text content of a specific URL\",\n enabled: true,\n type: \"function\",\n function: {\n name: \"web_fetch\",\n description: \"Fetch a single public URL and return its trimmed text content. Use when you already have a specific link you need to read.\",\n parameters: {\n type: \"object\",\n properties: {\n url: { type: \"string\", description: \"Absolute http(s) URL to fetch\" }\n },\n required: [\"url\"]\n }\n },\n endpoint: \"/mcp/web-fetch\",\n method: \"GET\",\n isBuiltIn: true\n};\n\n// New: Image Generation tool (OpenAI DALL·E 3)\nconst imageGenerationTool: MCPTool = {\n id: \"image-generation\",\n name: \"image_generation\",\n description: \"Generate high-quality images using DALL-E 3 from text prompts\",\n enabled: true,\n type: \"function\",\n function: {\n name: \"image_generation\",\n description: \"Generate high-quality images using DALL-E 3 from text prompts\",\n parameters: {\n type: \"object\",\n properties: {\n prompt: { type: \"string\", description: \"Text description of the image to generate\" },\n size: { type: \"string\", enum: [\"1024x1024\", \"1024x1792\", \"1792x1024\"], description: \"Image dimensions\" },\n quality: { type: \"string\", enum: [\"standard\", \"hd\"], description: \"Image quality\" },\n style: { type: \"string\", enum: [\"vivid\", \"natural\"], description: \"Style preference\" }\n },\n required: [\"prompt\"]\n }\n },\n endpoint: \"/mcp/generate-image\",\n method: \"POST\",\n isBuiltIn: true\n};\n\n// Create a downloadable file for the user (stored ~1 hour at a gateway URL).\nconst createFileTool: MCPTool = {\n id: \"create-file\",\n name: \"create_file\",\n description: \"Create a downloadable file for the user (md, txt, csv, json, html, docx, pptx). Returns a temporary (~1 hour) download link.\",\n enabled: true,\n type: \"function\",\n function: {\n name: \"create_file\",\n description:\n \"Generate a file the user can download. For docx/pptx write well-structured Markdown (headings, lists, tables; use '## ' headings to start each slide). Returns a short-lived (~1 hour) download URL — tell the user it expires.\",\n parameters: {\n type: \"object\",\n properties: {\n content: { type: \"string\", description: \"The file content (Markdown for docx/pptx; raw text for others).\" },\n filename: { type: \"string\", description: \"Desired filename, e.g. 'report.docx' or 'notes.md'.\" },\n format: {\n type: \"string\",\n enum: [\"md\", \"txt\", \"csv\", \"json\", \"html\", \"xml\", \"yaml\", \"docx\", \"pptx\"],\n description: \"File format. Defaults to md.\",\n },\n },\n required: [\"content\", \"format\"],\n },\n },\n endpoint: \"/mcp/create-file\",\n method: \"POST\",\n isBuiltIn: true,\n};\n\nconst defaultTools: MCPTool[] = [healthCheckTool, webSearchTool, webFetchTool, imageGenerationTool, createFileTool];\n\nexport const useMCPToolsStore = create<MCPToolsStore>((set, get) => ({\n tools: defaultTools,\n isLoaded: false,\n \n addTool: (toolData) => {\n const newTool: MCPTool = {\n ...toolData,\n id: `tool-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,\n };\n set((state) => ({\n tools: [...state.tools, newTool],\n }));\n get().saveTools();\n },\n \n updateTool: (id, updates) => {\n set((state) => ({\n tools: state.tools.map((tool) =>\n tool.id === id ? { ...tool, ...updates } : tool\n ),\n }));\n get().saveTools();\n },\n \n deleteTool: (id) => {\n set((state) => ({\n tools: state.tools.filter((tool) => tool.id !== id || tool.isBuiltIn),\n }));\n get().saveTools();\n },\n \n toggleTool: (id) => {\n set((state) => ({\n tools: state.tools.map((tool) =>\n tool.id === id ? { ...tool, enabled: !tool.enabled } : tool\n ),\n }));\n get().saveTools();\n },\n \n getEnabledTools: () => {\n return get().tools.filter((tool) => tool.enabled);\n },\n \n loadTools: async () => {\n try {\n const storeConfigs = [{ name: \"config\", keyPath: \"id\" }];\n const data = await indexedDBService.get(\"banditConfig\", 1, \"config\", \"mcpTools\", storeConfigs);\n \n if (data?.tools && Array.isArray(data.tools)) {\n // Merge saved tools with built-in tools, ensuring built-ins always use default definition\n const savedTools = data.tools as MCPTool[];\n const builtInIds = defaultTools.map(t => t.id);\n const customTools = savedTools.filter(\n (tool) => !tool.isBuiltIn && !builtInIds.includes(tool.id)\n );\n\n set({\n tools: [...defaultTools, ...customTools],\n isLoaded: true\n });\n debugLogger.info(\"MCP tools loaded from IndexedDB\", { \n totalTools: defaultTools.length + customTools.length,\n builtInTools: defaultTools.length,\n customTools: customTools.length\n });\n } else {\n // First time, save defaults\n set({ isLoaded: true });\n await get().saveTools();\n debugLogger.info(\"Default MCP tools initialized\");\n }\n } catch (error) {\n debugLogger.error(\"Failed to load MCP tools from IndexedDB\", { error });\n set({ isLoaded: true }); // Mark as loaded even if failed, so UI can render\n }\n },\n \n saveTools: async () => {\n try {\n const { tools } = get();\n const storeConfigs = [{ name: \"config\", keyPath: \"id\" }];\n \n const customTools = tools.filter((tool) => !tool.isBuiltIn);\n\n await indexedDBService.put(\"banditConfig\", 1, \"config\", {\n id: \"mcpTools\",\n tools: customTools,\n }, storeConfigs);\n\n debugLogger.debug(\"MCP tools saved to IndexedDB\", { toolCount: customTools.length });\n } catch (error) {\n debugLogger.error(\"Failed to save MCP tools to IndexedDB\", { error });\n }\n },\n}));\n"],"mappings":"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","names":[]}
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-U633CJBV.mjs";
7
7
  import {
8
8
  useMCPToolsStore
9
- } from "./chunk-IDZEEONG.mjs";
9
+ } from "./chunk-DHYP4K5O.mjs";
10
10
  import {
11
11
  AddIcon,
12
12
  ArrowDownwardIcon,
@@ -3286,6 +3286,7 @@ TOOL USAGE PROTOCOL (conservative approach)
3286
3286
  * 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
3287
3287
  * 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)
3288
3288
  * image_generation() - ONLY when explicitly asked to create or generate an image
3289
+ * 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. 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.
3289
3290
  * 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.
3290
3291
  - For general questions about concepts, definitions, explanations, or how-to topics, use your built-in knowledge WITHOUT calling tools.
3291
3292
  - Examples of what NOT to use tools for: "who are you?", "what is React?", "explain machine learning", "how does X work?", general programming questions.
@@ -3568,6 +3569,17 @@ _${toolStatus}_` : `_${toolStatus}_`);
3568
3569
 
3569
3570
  ${revisedPrompt ? `Prompt refinement: \u201C${revisedPrompt}\u201D
3570
3571
  ` : ""}Note: the image link may expire in ~2 hours.` : "Image generated successfully.";
3572
+ } else if (functionName === "create_file" || functionName === "create-file") {
3573
+ const fileData = result.data ?? {};
3574
+ if (fileData.url) {
3575
+ const mins = fileData.expiresInMinutes ?? 60;
3576
+ const name = fileData.filename || "your file";
3577
+ resultText = `\u{1F4C4} **[${name}](${fileData.url})** \u2014 ready to download.
3578
+
3579
+ _This link is temporary and expires in about ${mins} minutes._`;
3580
+ } else {
3581
+ resultText = "The file was created.";
3582
+ }
3571
3583
  } else if (typeof result.data === "string") {
3572
3584
  resultText = result.data;
3573
3585
  } else if (result.data) {
@@ -3582,7 +3594,7 @@ ${revisedPrompt ? `Prompt refinement: \u201C${revisedPrompt}\u201D
3582
3594
  }
3583
3595
  enhancedMessage = enhancedMessage.replace(placeholderToken, resultText);
3584
3596
  if (result.success) {
3585
- if (functionName === "image_generation" || functionName === "image-generation") {
3597
+ if (functionName === "image_generation" || functionName === "image-generation" || functionName === "create_file" || functionName === "create-file") {
3586
3598
  inlineImageBlocks.push(resultText);
3587
3599
  } else if (functionName !== "check_gateway_health") {
3588
3600
  summarizableResults.push({ name: functionName, output: resultText });
@@ -3614,41 +3626,38 @@ ${revisedPrompt ? `Prompt refinement: \u201C${revisedPrompt}\u201D
3614
3626
  try {
3615
3627
  const toolResultsText = summarizableResults.map((r) => `## ${r.name}
3616
3628
  ${r.output}`).join("\n\n");
3617
- const summaryMessages = [
3618
- { role: "system", content: systemPromptForSummary },
3629
+ const MAX_CHAIN_ROUNDS = 4;
3630
+ const enabledToolsForChain = getEnabledMCPToolsForAI();
3631
+ const convo = [
3632
+ { role: "system", content: enhancedSystemPrompt },
3619
3633
  ...contextMessages,
3620
3634
  { role: "user", content: question },
3621
- { role: "assistant", content: stripToolBlocks(fullMessage) || "Let me look that up." },
3635
+ { role: "assistant", content: stripToolBlocks(fullMessage) || "Let me work on that." },
3622
3636
  {
3623
3637
  role: "user",
3624
- content: `I ran the tool(s) you requested. Here are the raw results:
3638
+ content: `Here are the results of the tool(s) so far:
3625
3639
 
3626
3640
  ${toolResultsText}
3627
3641
 
3628
- Using these results together with your own knowledge, answer my original question concisely and in a natural, well-formatted way. Do NOT add a "Sources", "References", or "Citations" list of any kind \u2014 not names and not URLs. A clean, clickable Sources section is appended automatically below your answer, so any list you add just duplicates it. (Mentioning a source naturally inside a sentence is fine; a trailing list is not.) Do NOT output tool_code or call any tools again.`
3642
+ Use them to fully complete my original request. If you still need to take an action I asked for (for example, actually create a file I want to download), call the appropriate tool now with a \`\`\`tool_code\`\`\` block. Otherwise give your final answer. Do NOT add a "Sources"/"References"/"Citations" list \u2014 one is appended automatically.`
3629
3643
  }
3630
3644
  ];
3631
- const summaryRequest = {
3632
- model: modelName,
3633
- messages: summaryMessages,
3634
- stream: true,
3635
- options: { num_predict: tokenLimit + 250 }
3636
- };
3637
- clearFlushTimer();
3638
- setStreamBuffer("");
3639
- setIsThinking?.(true);
3640
- const summaryText = await new Promise((resolve) => {
3645
+ const streamTurn = (req) => new Promise((resolve) => {
3641
3646
  let acc = "";
3647
+ const native = [];
3642
3648
  let settled = false;
3643
3649
  let timer;
3644
- const done = (value) => {
3650
+ const finish = (value) => {
3645
3651
  if (settled) return;
3646
3652
  settled = true;
3647
3653
  if (timer) clearTimeout(timer);
3648
3654
  resolve(value);
3649
3655
  };
3650
- const summarySub = provider.chat(summaryRequest).subscribe({
3656
+ const sub2 = provider.chat(req).subscribe({
3651
3657
  next: (data) => {
3658
+ if (Array.isArray(data?.message?.tool_calls) && data.message.tool_calls.length) {
3659
+ native.push(...data.message.tool_calls);
3660
+ }
3652
3661
  if (data?.message?.content) {
3653
3662
  acc += data.message.content;
3654
3663
  const visible = stripThinking(acc);
@@ -3658,27 +3667,125 @@ Using these results together with your own knowledge, answer my original questio
3658
3667
  setStreamBuffer(visible);
3659
3668
  }
3660
3669
  },
3661
- error: (summaryErr) => {
3662
- debugLogger.error("Summarization pass failed", {
3663
- error: summaryErr instanceof Error ? summaryErr.message : String(summaryErr)
3664
- });
3665
- done("");
3666
- },
3667
- complete: () => done(stripThinking(acc).trim())
3670
+ error: () => finish({ text: stripThinking(acc).trim(), native }),
3671
+ complete: () => finish({ text: stripThinking(acc).trim(), native })
3668
3672
  });
3669
- currentSubRef.current = summarySub;
3673
+ currentSubRef.current = sub2;
3670
3674
  timer = setTimeout(() => {
3671
- debugLogger.warn("Summarization pass timed out; using inline tool output");
3672
3675
  try {
3673
- summarySub.unsubscribe();
3676
+ sub2.unsubscribe();
3674
3677
  } catch {
3675
3678
  }
3676
- done("");
3679
+ finish({ text: stripThinking(acc).trim(), native });
3677
3680
  }, 3e4);
3678
3681
  });
3682
+ const runChainedTool = async (fn, params) => {
3683
+ if (fn === "ask_user" || fn === "ask-user") {
3684
+ const qs = parseAskUserQuestions(params.questions ?? params);
3685
+ if (!qs.length) return "ask_user failed: it needs a questions array.";
3686
+ const ans = await useAskUserStore.getState().ask(qs);
3687
+ return ans ? "The user answered:\n\n" + qs.map((q) => `Q: ${q.question}
3688
+ A: ${(ans[q.id] || "").trim() || "(no answer)"}`).join("\n\n") : "The user dismissed the question(s). Proceed with your best judgment.";
3689
+ }
3690
+ const status = fn === "create_file" || fn === "create-file" ? "Creating the file\u2026" : fn === "web_search" || fn === "web-search" ? "Searching the web\u2026" : fn === "web_fetch" || fn === "web-fetch" ? "Reading the page\u2026" : fn === "image_generation" || fn === "image-generation" ? "Generating the image\u2026" : "Working on it\u2026";
3691
+ setStreamBuffer(`_${status}_`);
3692
+ const result = await executeMCPTool({ toolName: fn, parameters: params });
3693
+ if (!result.success) return `That step failed: ${result.error || "unknown error"}.`;
3694
+ if (fn === "create_file" || fn === "create-file") {
3695
+ const f = result.data ?? {};
3696
+ if (f.url) {
3697
+ const mins = f.expiresInMinutes ?? 60;
3698
+ const name = f.filename || "your file";
3699
+ inlineImageBlocks.push(`\u{1F4C4} **[${name}](${f.url})** \u2014 ready to download.
3700
+
3701
+ _This link is temporary and expires in about ${mins} minutes._`);
3702
+ return `File created and its download link is now shown to the user. Briefly confirm it's ready and that it expires in ~${mins} minutes.`;
3703
+ }
3704
+ return "The file was created.";
3705
+ }
3706
+ if (fn === "image_generation" || fn === "image-generation") {
3707
+ const img = result.data ?? {};
3708
+ if (img.imageUrl) {
3709
+ inlineImageBlocks.push(`![Generated image](${img.imageUrl})`);
3710
+ return "Image generated and shown to the user.";
3711
+ }
3712
+ }
3713
+ if (typeof result.data === "string") return result.data.slice(0, 2e3);
3714
+ if (result.data) return JSON.stringify(result.data).slice(0, 1500);
3715
+ return "Done.";
3716
+ };
3717
+ clearFlushTimer();
3718
+ let finalText = "";
3719
+ let lastTurnText = "";
3720
+ for (let round = 0; round < MAX_CHAIN_ROUNDS; round++) {
3721
+ setStreamBuffer("");
3722
+ setIsThinking?.(true);
3723
+ const turnRequest = {
3724
+ model: modelName,
3725
+ messages: convo,
3726
+ stream: true,
3727
+ tools: enabledToolsForChain.length ? enabledToolsForChain : void 0,
3728
+ options: { num_predict: tokenLimit + 250 }
3729
+ };
3730
+ const { text: turnText, native: turnNative } = await streamTurn(turnRequest);
3731
+ setIsThinking?.(false);
3732
+ if (turnText.trim()) lastTurnText = turnText;
3733
+ let toolText = turnText;
3734
+ if (turnNative.length && !/```(?:tool_code|TOOL_CODE)/.test(toolText)) {
3735
+ for (const raw of turnNative) {
3736
+ const tc = raw;
3737
+ const fnName = tc.function?.name ?? tc.name;
3738
+ if (!fnName) continue;
3739
+ const a = tc.function?.arguments ?? tc.arguments ?? {};
3740
+ toolText += `
3741
+
3742
+ \`\`\`tool_code
3743
+ ${fnName}(${typeof a === "string" ? a : JSON.stringify(a ?? {})})
3744
+ \`\`\``;
3745
+ }
3746
+ }
3747
+ const chainMatches = toolText.match(/```(?:tool_code|TOOL_CODE)\s*\n([^`]+)\n```/gi);
3748
+ if (!chainMatches || !chainMatches.length) {
3749
+ finalText = turnText;
3750
+ break;
3751
+ }
3752
+ const roundOut = [];
3753
+ for (const m of chainMatches) {
3754
+ const code = m.replace(/```(?:tool_code|TOOL_CODE)\s*\n|\n```/gi, "").trim();
3755
+ const fm = code.match(/^(\w+)\(\s*(.*?)\s*\)$/);
3756
+ if (!fm) continue;
3757
+ const [, fnName, rawParams] = fm;
3758
+ let parsed = {};
3759
+ const rp = rawParams.trim();
3760
+ if (rp) {
3761
+ try {
3762
+ parsed = JSON.parse(rp.startsWith("{") ? rp : `{${rp}}`);
3763
+ } catch {
3764
+ parsed = {};
3765
+ }
3766
+ }
3767
+ try {
3768
+ roundOut.push(`## ${fnName}
3769
+ ${await runChainedTool(fnName, parsed)}`);
3770
+ } catch (e) {
3771
+ roundOut.push(`## ${fnName}
3772
+ That step failed: ${e instanceof Error ? e.message : String(e)}`);
3773
+ }
3774
+ }
3775
+ convo.push({ role: "assistant", content: stripToolBlocks(turnText) || "(using a tool)" });
3776
+ convo.push({
3777
+ role: "user",
3778
+ content: `Tool results:
3779
+
3780
+ ${roundOut.join("\n\n")}
3781
+
3782
+ Now give your final answer to my original request, or call another tool if you still genuinely need to. Do NOT add a "Sources" list.`
3783
+ });
3784
+ }
3679
3785
  setIsThinking?.(false);
3680
- if (summaryText.trim()) {
3681
- const cleanedSummary = summaryText.replace(
3786
+ const answerText = finalText.trim() ? finalText : lastTurnText;
3787
+ if (answerText.trim() || inlineImageBlocks.length) {
3788
+ const cleanedSummary = answerText.replace(
3682
3789
  /\n{1,}\s*(?:[*_#>\s]*)(?:sources?|references?|citations?|further reading)(?:\s*:)?\s*(?:[*_]*)\s*\n[\s\S]*$/i,
3683
3790
  ""
3684
3791
  ).trimEnd();
@@ -9942,4 +10049,4 @@ var chat_default = Chat;
9942
10049
  export {
9943
10050
  chat_default
9944
10051
  };
9945
- //# sourceMappingURL=chunk-PUUL2R3T.mjs.map
10052
+ //# sourceMappingURL=chunk-LDL4X6CB.mjs.map