@burtson-labs/bandit-engine 2.0.71 → 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,6 +1,6 @@
1
1
  import {
2
2
  chat_default
3
- } from "./chunk-CMBYMC3G.mjs";
3
+ } from "./chunk-LDL4X6CB.mjs";
4
4
  import "./chunk-ONQMRE2G.mjs";
5
5
  import "./chunk-U633CJBV.mjs";
6
6
  import "./chunk-DHYP4K5O.mjs";
@@ -13,4 +13,4 @@ import "./chunk-BJTO5JO5.mjs";
13
13
  export {
14
14
  chat_default as default
15
15
  };
16
- //# sourceMappingURL=chat-2QWK6OUO.mjs.map
16
+ //# sourceMappingURL=chat-GLBQOPNT.mjs.map
@@ -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-2QWK6OUO.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-VKDU2OMI.mjs.map
10851
+ //# sourceMappingURL=chunk-7ZHLQXHL.mjs.map
@@ -3626,41 +3626,38 @@ _This link is temporary and expires in about ${mins} minutes._`;
3626
3626
  try {
3627
3627
  const toolResultsText = summarizableResults.map((r) => `## ${r.name}
3628
3628
  ${r.output}`).join("\n\n");
3629
- const summaryMessages = [
3630
- { role: "system", content: systemPromptForSummary },
3629
+ const MAX_CHAIN_ROUNDS = 4;
3630
+ const enabledToolsForChain = getEnabledMCPToolsForAI();
3631
+ const convo = [
3632
+ { role: "system", content: enhancedSystemPrompt },
3631
3633
  ...contextMessages,
3632
3634
  { role: "user", content: question },
3633
- { role: "assistant", content: stripToolBlocks(fullMessage) || "Let me look that up." },
3635
+ { role: "assistant", content: stripToolBlocks(fullMessage) || "Let me work on that." },
3634
3636
  {
3635
3637
  role: "user",
3636
- 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:
3637
3639
 
3638
3640
  ${toolResultsText}
3639
3641
 
3640
- 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.`
3641
3643
  }
3642
3644
  ];
3643
- const summaryRequest = {
3644
- model: modelName,
3645
- messages: summaryMessages,
3646
- stream: true,
3647
- options: { num_predict: tokenLimit + 250 }
3648
- };
3649
- clearFlushTimer();
3650
- setStreamBuffer("");
3651
- setIsThinking?.(true);
3652
- const summaryText = await new Promise((resolve) => {
3645
+ const streamTurn = (req) => new Promise((resolve) => {
3653
3646
  let acc = "";
3647
+ const native = [];
3654
3648
  let settled = false;
3655
3649
  let timer;
3656
- const done = (value) => {
3650
+ const finish = (value) => {
3657
3651
  if (settled) return;
3658
3652
  settled = true;
3659
3653
  if (timer) clearTimeout(timer);
3660
3654
  resolve(value);
3661
3655
  };
3662
- const summarySub = provider.chat(summaryRequest).subscribe({
3656
+ const sub2 = provider.chat(req).subscribe({
3663
3657
  next: (data) => {
3658
+ if (Array.isArray(data?.message?.tool_calls) && data.message.tool_calls.length) {
3659
+ native.push(...data.message.tool_calls);
3660
+ }
3664
3661
  if (data?.message?.content) {
3665
3662
  acc += data.message.content;
3666
3663
  const visible = stripThinking(acc);
@@ -3670,27 +3667,125 @@ Using these results together with your own knowledge, answer my original questio
3670
3667
  setStreamBuffer(visible);
3671
3668
  }
3672
3669
  },
3673
- error: (summaryErr) => {
3674
- debugLogger.error("Summarization pass failed", {
3675
- error: summaryErr instanceof Error ? summaryErr.message : String(summaryErr)
3676
- });
3677
- done("");
3678
- },
3679
- complete: () => done(stripThinking(acc).trim())
3670
+ error: () => finish({ text: stripThinking(acc).trim(), native }),
3671
+ complete: () => finish({ text: stripThinking(acc).trim(), native })
3680
3672
  });
3681
- currentSubRef.current = summarySub;
3673
+ currentSubRef.current = sub2;
3682
3674
  timer = setTimeout(() => {
3683
- debugLogger.warn("Summarization pass timed out; using inline tool output");
3684
3675
  try {
3685
- summarySub.unsubscribe();
3676
+ sub2.unsubscribe();
3686
3677
  } catch {
3687
3678
  }
3688
- done("");
3679
+ finish({ text: stripThinking(acc).trim(), native });
3689
3680
  }, 3e4);
3690
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
+ }
3691
3785
  setIsThinking?.(false);
3692
- if (summaryText.trim()) {
3693
- const cleanedSummary = summaryText.replace(
3786
+ const answerText = finalText.trim() ? finalText : lastTurnText;
3787
+ if (answerText.trim() || inlineImageBlocks.length) {
3788
+ const cleanedSummary = answerText.replace(
3694
3789
  /\n{1,}\s*(?:[*_#>\s]*)(?:sources?|references?|citations?|further reading)(?:\s*:)?\s*(?:[*_]*)\s*\n[\s\S]*$/i,
3695
3790
  ""
3696
3791
  ).trimEnd();
@@ -9954,4 +10049,4 @@ var chat_default = Chat;
9954
10049
  export {
9955
10050
  chat_default
9956
10051
  };
9957
- //# sourceMappingURL=chunk-CMBYMC3G.mjs.map
10052
+ //# sourceMappingURL=chunk-LDL4X6CB.mjs.map