@bike4mind/cli 0.2.29-feat-quantum-optimize-architecture.18875 → 0.2.29-feat-cli-websocket-streaming.18881

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.
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  getEffectiveApiKey,
6
6
  getOpenWeatherKey,
7
7
  getSerperKey
8
- } from "./chunk-MHDNMNRT.js";
8
+ } from "./chunk-QQL3OBKL.js";
9
9
  import {
10
10
  ConfigStore,
11
11
  logger
@@ -14,8 +14,8 @@ import {
14
14
  selectActiveBackgroundAgents,
15
15
  useCliStore
16
16
  } from "./chunk-TVW4ZESU.js";
17
- import "./chunk-Z7V2ITXT.js";
18
- import "./chunk-KFGSOQ6A.js";
17
+ import "./chunk-BXCARBUQ.js";
18
+ import "./chunk-EC2VEDD2.js";
19
19
  import {
20
20
  BFLImageService,
21
21
  BaseStorage,
@@ -27,7 +27,7 @@ import {
27
27
  OpenAIBackend,
28
28
  OpenAIImageService,
29
29
  XAIImageService
30
- } from "./chunk-H66G775W.js";
30
+ } from "./chunk-TY6Z2UV2.js";
31
31
  import {
32
32
  AiEvents,
33
33
  ApiKeyEvents,
@@ -82,10 +82,8 @@ import {
82
82
  VideoModels,
83
83
  XAI_IMAGE_MODELS,
84
84
  b4mLLMTools,
85
- getMcpProviderMetadata,
86
- getViewById,
87
- resolveNavigationIntents
88
- } from "./chunk-KISWANWA.js";
85
+ getMcpProviderMetadata
86
+ } from "./chunk-KPOK6V6E.js";
89
87
  import {
90
88
  Logger
91
89
  } from "./chunk-OCYRD7D6.js";
@@ -95,7 +93,7 @@ import React21, { useState as useState9, useEffect as useEffect6, useCallback as
95
93
  import { render, Box as Box20, Text as Text20, useApp, useInput as useInput9 } from "ink";
96
94
  import { execSync } from "child_process";
97
95
  import { randomBytes as randomBytes5 } from "crypto";
98
- import { v4 as uuidv411 } from "uuid";
96
+ import { v4 as uuidv413 } from "uuid";
99
97
 
100
98
  // src/components/App.tsx
101
99
  import React15, { useState as useState5, useEffect as useEffect4 } from "react";
@@ -3589,6 +3587,50 @@ function isReadOnlyTool(toolName, customCategories) {
3589
3587
  return getToolCategory(toolName, customCategories) !== "prompt_always";
3590
3588
  }
3591
3589
 
3590
+ // src/core/skillsPrompt.ts
3591
+ function getSkillDisplayName(cmd) {
3592
+ return cmd.displayName || cmd.name;
3593
+ }
3594
+ function formatSkillEntry(cmd) {
3595
+ const displayName = getSkillDisplayName(cmd);
3596
+ const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
3597
+ const nameDisplay = cmd.displayName && cmd.displayName !== cmd.name ? `**${displayName}** (\`${cmd.name}\`)` : `**${cmd.name}**`;
3598
+ return `- ${nameDisplay}${argHint}: ${cmd.description}
3599
+ `;
3600
+ }
3601
+ function formatSkillGroup(heading, commands) {
3602
+ if (commands.length === 0) {
3603
+ return "";
3604
+ }
3605
+ return `
3606
+ ### ${heading}
3607
+ ${commands.map(formatSkillEntry).join("")}`;
3608
+ }
3609
+ function filterAIVisibleSkills(commands) {
3610
+ return commands.filter((cmd) => !cmd.disableModelInvocation);
3611
+ }
3612
+ function filterSkillsByAllowedList(commands, allowedSkills) {
3613
+ if (!allowedSkills || allowedSkills.length === 0) {
3614
+ return commands;
3615
+ }
3616
+ return commands.filter((cmd) => allowedSkills.includes(cmd.name));
3617
+ }
3618
+ function buildSkillsPromptSection(commands, allowedSkills) {
3619
+ const filteredByAllowed = filterSkillsByAllowedList(commands, allowedSkills);
3620
+ const visibleCommands = filterAIVisibleSkills(filteredByAllowed);
3621
+ if (visibleCommands.length === 0) {
3622
+ return "";
3623
+ }
3624
+ const projectSkills = visibleCommands.filter((c) => c.source === "project");
3625
+ const globalSkills = visibleCommands.filter((c) => c.source === "global");
3626
+ return `
3627
+
3628
+ ## Available Skills
3629
+
3630
+ Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
3631
+ ` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
3632
+ }
3633
+
3592
3634
  // src/core/prompts.ts
3593
3635
  var TOOL_GREP_SEARCH = "grep_search";
3594
3636
  var TOOL_GLOB_FILES = "glob_files";
@@ -3599,7 +3641,38 @@ var TOOL_BASH_EXECUTE = "bash_execute";
3599
3641
  var TOOL_SUBAGENT_DELEGATE = "subagent_delegate";
3600
3642
  var TOOL_WRITE_TODOS = "write_todos";
3601
3643
  var EXPLORE_SUBAGENT_TYPE = "explore";
3602
- function buildCoreSystemPrompt(contextSection = "") {
3644
+ function buildCoreSystemPrompt(contextSection, config) {
3645
+ let projectContextSection = "";
3646
+ let agentDirectoryContext = "";
3647
+ let skillsSection = "";
3648
+ if (typeof contextSection === "string") {
3649
+ projectContextSection = contextSection;
3650
+ if (config) {
3651
+ if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) {
3652
+ skillsSection = buildSkillsPromptSection(config.customCommands);
3653
+ }
3654
+ if (config.agentStore) {
3655
+ agentDirectoryContext = config.agentStore.getDirectoryContext();
3656
+ }
3657
+ }
3658
+ } else if (contextSection && typeof contextSection === "object") {
3659
+ config = contextSection;
3660
+ if (config.contextContent) {
3661
+ projectContextSection = `
3662
+
3663
+ ## Project Context
3664
+
3665
+ Follow these project-specific instructions:
3666
+
3667
+ ${config.contextContent}`;
3668
+ }
3669
+ if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) {
3670
+ skillsSection = buildSkillsPromptSection(config.customCommands);
3671
+ }
3672
+ if (config.agentStore) {
3673
+ agentDirectoryContext = config.agentStore.getDirectoryContext();
3674
+ }
3675
+ }
3603
3676
  return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
3604
3677
 
3605
3678
  CORE BEHAVIOR:
@@ -3629,12 +3702,8 @@ When requested to perform tasks like fixing bugs, adding features, refactoring,
3629
3702
 
3630
3703
  SUBAGENT DELEGATION:
3631
3704
  - You have access to specialized subagents via the ${TOOL_SUBAGENT_DELEGATE} tool
3632
- - Use subagents for focused exploration, planning, or review tasks:
3633
- * explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
3634
- * plan: Break down complex tasks into actionable steps
3635
- * review: Analyze code quality and identify issues
3705
+ - ${agentDirectoryContext ? `${agentDirectoryContext}` : ""}
3636
3706
  - Subagents keep the main conversation clean and run faster with optimized models
3637
- - Delegate when you need to search extensively or analyze code structure
3638
3707
 
3639
3708
  CODE SEARCH BEST PRACTICES:
3640
3709
  When searching code, follow this hierarchy for speed and efficiency:
@@ -3691,7 +3760,7 @@ EXAMPLES:
3691
3760
  - "what packages installed?" \u2192 ${TOOL_GLOB_FILES} "**/package.json" \u2192 ${TOOL_FILE_READ}
3692
3761
  - "find all components using React hooks" \u2192 ${TOOL_SUBAGENT_DELEGATE}(task="find all components using React hooks", type="explore")
3693
3762
 
3694
- Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
3763
+ Remember: Use context from previous messages to understand follow-up questions.${projectContextSection}${skillsSection}`;
3695
3764
  }
3696
3765
 
3697
3766
  // ../../b4m-core/packages/services/dist/src/referService/generateCodes.js
@@ -8781,8 +8850,8 @@ var getHeliocentricCoords = (planet, T) => {
8781
8850
  const sinNode = Math.sin(longNode);
8782
8851
  const x = r * (cosNode * cosOmega - sinNode * sinOmega * cosI);
8783
8852
  const y = r * (sinNode * cosOmega + cosNode * sinOmega * cosI);
8784
- const z148 = r * sinOmega * sinI;
8785
- return { x, y, z: z148, r };
8853
+ const z147 = r * sinOmega * sinI;
8854
+ return { x, y, z: z147, r };
8786
8855
  };
8787
8856
  var getGeocentricEcliptic = (planet, earth) => {
8788
8857
  const dx = planet.x - earth.x;
@@ -9666,13 +9735,13 @@ function formatSearchResults(files) {
9666
9735
  const notes = file.notes ? `
9667
9736
  Notes: ${file.notes}` : "";
9668
9737
  const fileType = file.type || "FILE";
9669
- return `${index + 1}. **${file.fileName}** (ID: ${file.id})
9738
+ return `${index + 1}. **${file.fileName}**
9670
9739
  Type: ${fileType} | MIME: ${file.mimeType}
9671
9740
  Tags: ${tags}${notes}`;
9672
9741
  });
9673
9742
  return `Found ${files.length} document(s) in your knowledge base:
9674
9743
 
9675
- ` + formattedFiles.join("\n\n") + "\n\n*Use retrieve_knowledge_content with a file ID or tags to read the actual document content.*";
9744
+ ` + formattedFiles.join("\n\n") + "\n\n*To use these documents in your conversation, you can ask me to reference specific files by name.*";
9676
9745
  }
9677
9746
  var knowledgeBaseSearchTool = {
9678
9747
  name: "search_knowledge_base",
@@ -9699,8 +9768,6 @@ var knowledgeBaseSearchTool = {
9699
9768
  by: "fileName",
9700
9769
  direction: "asc"
9701
9770
  }, {
9702
- textSearch: true,
9703
- // Search across fileName + tags + notes for better recall
9704
9771
  includeShared: true,
9705
9772
  // Include owned + explicitly shared + org-shared files
9706
9773
  userGroups: context.user.groups || []
@@ -9715,18 +9782,18 @@ var knowledgeBaseSearchTool = {
9715
9782
  },
9716
9783
  toolSchema: {
9717
9784
  name: "search_knowledge_base",
9718
- description: "Search the user's uploaded knowledge base (fab files). Searches across file names, tags, and notes for broad recall. Returns relevant documents from the user's own files, organization-shared files, and files explicitly shared with them. Use this tool when the user asks about their own documents, uploaded files, or organization knowledge.",
9785
+ description: "Search the user's uploaded knowledge base (fab files). Returns relevant documents from the user's own files, organization-shared files, and files explicitly shared with them. Use this tool when the user asks about their own documents, uploaded files, or organization knowledge.",
9719
9786
  parameters: {
9720
9787
  type: "object",
9721
9788
  properties: {
9722
9789
  query: {
9723
9790
  type: "string",
9724
- description: "The search query to find relevant documents. Matches against file names, tags, and notes."
9791
+ description: "The search query to find relevant documents by filename or content"
9725
9792
  },
9726
9793
  tags: {
9727
9794
  type: "array",
9728
9795
  items: { type: "string" },
9729
- description: 'Optional: filter results by tag names. Supports partial matching. For optimization docs, use tags like "opti:family:scheduling", "opti:QUBO", "opti:solver:highs", etc. Any matching tag qualifies the file.'
9796
+ description: "Optional: filter results to only include files with these tags"
9730
9797
  },
9731
9798
  file_type: {
9732
9799
  type: "string",
@@ -9746,1297 +9813,68 @@ var knowledgeBaseSearchTool = {
9746
9813
  })
9747
9814
  };
9748
9815
 
9749
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/knowledgeBaseRetrieve/index.js
9750
- var DEFAULT_MAX_CHARS = 8e3;
9751
- var ABSOLUTE_MAX_CHARS = 16e3;
9752
- var knowledgeBaseRetrieveTool = {
9753
- name: "retrieve_knowledge_content",
9754
- implementation: (context) => ({
9755
- toolFn: async (value) => {
9756
- const params = value;
9757
- const { file_id, tags, query, max_chars } = params;
9758
- const charBudget = Math.min(max_chars ?? DEFAULT_MAX_CHARS, ABSOLUTE_MAX_CHARS);
9759
- context.logger.log("\u{1F4D6} Knowledge Retrieve: params", { file_id, tags, query, max_chars: charBudget });
9760
- if (!file_id && !tags?.length && !query) {
9761
- return "Error: You must provide at least one of file_id, tags, or query.";
9762
- }
9763
- if (!context.db.fabfiles) {
9764
- context.logger.error("\u274C Knowledge Retrieve: fabfiles repository not available");
9765
- return "Knowledge base retrieval is not available at this time.";
9766
- }
9767
- if (!context.db.fabfilechunks) {
9768
- context.logger.error("\u274C Knowledge Retrieve: fabfilechunks repository not available");
9769
- return "Knowledge base retrieval is not available at this time (chunk reader unavailable).";
9770
- }
9771
- try {
9772
- let files = [];
9773
- if (file_id) {
9774
- const file = await context.db.fabfiles.findByIdAndUserId(file_id, context.userId);
9775
- if (file) {
9776
- files = [file];
9777
- } else {
9778
- const searchResults = await context.db.fabfiles.search(context.userId, file_id, { tags: [], shared: false }, { page: 1, limit: 1 }, { by: "fileName", direction: "asc" }, { textSearch: true, includeShared: true, userGroups: context.user.groups || [] });
9779
- files = searchResults.data;
9780
- }
9781
- if (files.length === 0) {
9782
- return `No document found with ID "${file_id}". The file may not exist or you may not have access to it. Try using search_knowledge_base to find the correct file ID.`;
9783
- }
9784
- }
9785
- if (files.length === 0 && (tags?.length || query)) {
9786
- const searchResults = await context.db.fabfiles.search(context.userId, query || "", { tags: tags || [], shared: false }, { page: 1, limit: 5 }, { by: "fileName", direction: "asc" }, { textSearch: true, includeShared: true, userGroups: context.user.groups || [] });
9787
- files = searchResults.data;
9788
- if (files.length === 0) {
9789
- const searchDesc = [
9790
- query && `query "${query}"`,
9791
- tags?.length && `tags [${tags.join(", ")}]`
9792
- ].filter(Boolean).join(" and ");
9793
- return `No documents found matching ${searchDesc}. Try broadening your search with search_knowledge_base.`;
9794
- }
9795
- }
9796
- let totalCharsUsed = 0;
9797
- const sections = [];
9798
- let filesRetrieved = 0;
9799
- for (const file of files) {
9800
- if (totalCharsUsed >= charBudget)
9801
- break;
9802
- const chunks = await context.db.fabfilechunks.findByFabFileId(file.id);
9803
- if (chunks.length === 0) {
9804
- context.logger.log(`\u{1F4D6} Knowledge Retrieve: No chunks for file ${file.fileName} (${file.id})`);
9805
- continue;
9806
- }
9807
- const fullText = chunks.map((c) => c.text).join("\n");
9808
- const remainingBudget = charBudget - totalCharsUsed;
9809
- const truncated = fullText.length > remainingBudget;
9810
- const content = truncated ? fullText.slice(0, remainingBudget) : fullText;
9811
- const fileTags = file.tags?.map((t) => t.name).join(", ") || "none";
9812
- const charLabel = truncated ? `${content.length} (truncated from ${fullText.length})` : `${content.length}`;
9813
- sections.push(`### ${file.fileName} (ID: ${file.id})
9814
- Tags: ${fileTags}
9815
- Chunks: ${chunks.length} | Characters: ${charLabel}
9816
- ---
9817
- ` + content);
9818
- totalCharsUsed += content.length;
9819
- filesRetrieved++;
9820
- }
9821
- if (filesRetrieved === 0) {
9822
- return "Found matching documents but they have no indexed content. The files may not have been processed yet.";
9823
- }
9824
- const header = `Retrieved content from ${filesRetrieved} of ${files.length} document(s):
9825
- `;
9826
- return header + "\n" + sections.join("\n\n---\n\n");
9827
- } catch (error) {
9828
- context.logger.error("\u274C Knowledge Retrieve: Error during retrieval:", error);
9829
- return "An error occurred while retrieving document content. Please try again.";
9830
- }
9831
- },
9832
- toolSchema: {
9833
- name: "retrieve_knowledge_content",
9834
- description: "Read the actual text content of knowledge base documents. Use this after search_knowledge_base to read documents by file ID, or provide tags/query to find and read documents in one step. Returns the full text content (up to the character budget) for grounding your responses in the user's curated knowledge.",
9835
- parameters: {
9836
- type: "object",
9837
- properties: {
9838
- file_id: {
9839
- type: "string",
9840
- description: "The file ID to retrieve (from search_knowledge_base results). Most efficient for single-document retrieval."
9841
- },
9842
- tags: {
9843
- type: "array",
9844
- items: { type: "string" },
9845
- description: 'Filter documents by tags. For optimization docs, use tags like "opti:family:scheduling", "opti:solver:highs", etc.'
9846
- },
9847
- query: {
9848
- type: "string",
9849
- description: "Search query to find documents. Can be combined with tags for more targeted retrieval."
9850
- },
9851
- max_chars: {
9852
- type: "number",
9853
- description: "Maximum characters of content to return (default: 8000, max: 16000). Lower values for quick lookups, higher for detailed reading.",
9854
- minimum: 500,
9855
- maximum: 16e3
9856
- }
9857
- }
9858
- }
9859
- }
9860
- })
9861
- };
9862
-
9863
- // ../../b4m-core/packages/quantum/dist/src/types.js
9864
- import { z as z139 } from "zod";
9865
- var OperationSchema = z139.object({
9866
- jobId: z139.number(),
9867
- machineId: z139.number(),
9868
- duration: z139.number().positive()
9869
- });
9870
- var JobSchema = z139.object({
9871
- id: z139.number(),
9872
- name: z139.string(),
9873
- operations: z139.array(OperationSchema)
9874
- });
9875
- var MachineSchema = z139.object({
9876
- id: z139.number(),
9877
- name: z139.string()
9878
- });
9879
- var SchedulingProblemSchema = z139.object({
9880
- name: z139.string(),
9881
- description: z139.string().optional(),
9882
- jobs: z139.array(JobSchema),
9883
- machines: z139.array(MachineSchema)
9884
- });
9885
- var SolverIdSchema = z139.enum([
9886
- "greedy",
9887
- "naive",
9888
- "random-restart",
9889
- "simulated-annealing",
9890
- "simulated-annealing-medium",
9891
- "simulated-annealing-large",
9892
- "tabu",
9893
- "genetic-algorithm",
9894
- "ant-colony",
9895
- "highs"
9896
- ]);
9897
-
9898
- // ../../b4m-core/packages/quantum/dist/src/solvers/greedy-solver.js
9899
- var greedySolver = {
9900
- id: "greedy",
9901
- name: "Greedy (Earliest Start)",
9902
- description: "Schedules each operation at the earliest available time on its machine, respecting job ordering.",
9903
- async solve(problem, options) {
9904
- const startTime = performance.now();
9905
- const machineAvailableAt = /* @__PURE__ */ new Map();
9906
- for (const machine of problem.machines) {
9907
- machineAvailableAt.set(machine.id, 0);
9908
- }
9909
- const jobCompletedAt = /* @__PURE__ */ new Map();
9910
- for (const job of problem.jobs) {
9911
- jobCompletedAt.set(job.id, 0);
9912
- }
9913
- const schedule = [];
9914
- const totalOps = problem.jobs.reduce((sum2, job) => sum2 + job.operations.length, 0);
9915
- let opsScheduled = 0;
9916
- for (const job of problem.jobs) {
9917
- for (let opIndex = 0; opIndex < job.operations.length; opIndex++) {
9918
- const op = job.operations[opIndex];
9919
- const machineReady = machineAvailableAt.get(op.machineId) ?? 0;
9920
- const jobReady = jobCompletedAt.get(job.id) ?? 0;
9921
- const opStart = Math.max(machineReady, jobReady);
9922
- const opEnd = opStart + op.duration;
9923
- schedule.push({
9924
- jobId: job.id,
9925
- machineId: op.machineId,
9926
- operationIndex: opIndex,
9927
- startTime: opStart,
9928
- endTime: opEnd
9929
- });
9930
- machineAvailableAt.set(op.machineId, opEnd);
9931
- jobCompletedAt.set(job.id, opEnd);
9932
- opsScheduled++;
9933
- if (options?.onProgress) {
9934
- options.onProgress({
9935
- solverId: "greedy",
9936
- percentage: Math.round(opsScheduled / totalOps * 100),
9937
- bestMakespan: opEnd,
9938
- currentIteration: opsScheduled,
9939
- totalIterations: totalOps
9940
- });
9941
- }
9942
- }
9943
- }
9944
- const makespan = Math.max(...schedule.map((op) => op.endTime));
9945
- const elapsedMs = performance.now() - startTime;
9946
- return {
9947
- solverId: "greedy",
9948
- solverName: "Greedy (Earliest Start)",
9949
- makespan,
9950
- schedule,
9951
- elapsedMs,
9952
- iterations: totalOps
9953
- };
9954
- }
9955
- };
9956
-
9957
- // ../../b4m-core/packages/quantum/dist/src/solvers/schedule-utils.js
9958
- function buildOperationList(problem) {
9959
- const ops = [];
9960
- for (const job of problem.jobs) {
9961
- for (let i = 0; i < job.operations.length; i++) {
9962
- ops.push({ jobId: job.id, opIndex: i });
9963
- }
9964
- }
9965
- return ops;
9966
- }
9967
- function decodePermutation(permutation, problem) {
9968
- const schedule = [];
9969
- const machineAvailableAt = /* @__PURE__ */ new Map();
9970
- const jobCompletedAt = /* @__PURE__ */ new Map();
9971
- const jobNextOp = /* @__PURE__ */ new Map();
9972
- for (const machine of problem.machines) {
9973
- machineAvailableAt.set(machine.id, 0);
9974
- }
9975
- for (const job of problem.jobs) {
9976
- jobCompletedAt.set(job.id, 0);
9977
- jobNextOp.set(job.id, 0);
9978
- }
9979
- const remaining = [...permutation];
9980
- const maxPasses = remaining.length * remaining.length;
9981
- let passes = 0;
9982
- while (remaining.length > 0 && passes < maxPasses) {
9983
- let scheduled = false;
9984
- for (let i = 0; i < remaining.length; i++) {
9985
- const ref = remaining[i];
9986
- const nextOp = jobNextOp.get(ref.jobId) ?? 0;
9987
- if (ref.opIndex !== nextOp)
9988
- continue;
9989
- const job = problem.jobs.find((j) => j.id === ref.jobId);
9990
- const op = job.operations[ref.opIndex];
9991
- const machineReady = machineAvailableAt.get(op.machineId) ?? 0;
9992
- const jobReady = jobCompletedAt.get(ref.jobId) ?? 0;
9993
- const start = Math.max(machineReady, jobReady);
9994
- const end = start + op.duration;
9995
- schedule.push({
9996
- jobId: ref.jobId,
9997
- machineId: op.machineId,
9998
- operationIndex: ref.opIndex,
9999
- startTime: start,
10000
- endTime: end
10001
- });
10002
- machineAvailableAt.set(op.machineId, end);
10003
- jobCompletedAt.set(ref.jobId, end);
10004
- jobNextOp.set(ref.jobId, nextOp + 1);
10005
- remaining.splice(i, 1);
10006
- scheduled = true;
10007
- break;
10008
- }
10009
- if (!scheduled) {
10010
- remaining.push(remaining.shift());
10011
- }
10012
- passes++;
10013
- }
10014
- return schedule;
10015
- }
10016
- function computeMakespan(schedule) {
10017
- if (schedule.length === 0)
10018
- return 0;
10019
- return Math.max(...schedule.map((op) => op.endTime));
10020
- }
10021
- function randomPermutation(problem, rng = Math.random) {
10022
- const ops = buildOperationList(problem);
10023
- for (let i = ops.length - 1; i > 0; i--) {
10024
- const j = Math.floor(rng() * (i + 1));
10025
- [ops[i], ops[j]] = [ops[j], ops[i]];
10026
- }
10027
- return ops;
10028
- }
10029
- function swapNeighbor(perm, rng = Math.random) {
10030
- const newPerm = [...perm];
10031
- const i = Math.floor(rng() * newPerm.length);
10032
- let j = Math.floor(rng() * (newPerm.length - 1));
10033
- if (j >= i)
10034
- j++;
10035
- [newPerm[i], newPerm[j]] = [newPerm[j], newPerm[i]];
10036
- return newPerm;
10037
- }
10038
- function createRng(seed) {
10039
- let s = seed | 0;
10040
- return () => {
10041
- s = s + 1831565813 | 0;
10042
- let t = Math.imul(s ^ s >>> 15, 1 | s);
10043
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
10044
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
10045
- };
10046
- }
10047
-
10048
- // ../../b4m-core/packages/quantum/dist/src/solvers/naive-solver.js
10049
- var MAX_PERMUTATIONS = 40320;
10050
- function* permutations(arr) {
10051
- if (arr.length <= 1) {
10052
- yield [...arr];
10053
- return;
10054
- }
10055
- for (let i = 0; i < arr.length; i++) {
10056
- const rest = [...arr.slice(0, i), ...arr.slice(i + 1)];
10057
- for (const perm of permutations(rest)) {
10058
- yield [arr[i], ...perm];
10059
- }
10060
- }
10061
- }
10062
- var naiveSolver = {
10063
- id: "naive",
10064
- name: "Naive (Brute Force)",
10065
- description: "Evaluates all permutations to find the optimal schedule. Only feasible for small problems.",
10066
- async solve(problem, options) {
10067
- const startTime = performance.now();
10068
- const ops = buildOperationList(problem);
10069
- const totalPerms = factorial(ops.length);
10070
- const cappedTotal = Math.min(totalPerms, MAX_PERMUTATIONS);
10071
- let bestMakespan = Infinity;
10072
- let bestSchedule = decodePermutation(ops, problem);
10073
- let count = 0;
10074
- for (const perm of permutations(ops)) {
10075
- if (count >= MAX_PERMUTATIONS)
10076
- break;
10077
- const schedule = decodePermutation(perm, problem);
10078
- const makespan = computeMakespan(schedule);
10079
- if (makespan < bestMakespan) {
10080
- bestMakespan = makespan;
10081
- bestSchedule = schedule;
10082
- }
10083
- count++;
10084
- if (options?.onProgress && count % 100 === 0) {
10085
- options.onProgress({
10086
- solverId: "naive",
10087
- percentage: Math.min(99, Math.round(count / cappedTotal * 100)),
10088
- bestMakespan,
10089
- currentIteration: count,
10090
- totalIterations: cappedTotal
10091
- });
10092
- }
10093
- }
10094
- options?.onProgress?.({
10095
- solverId: "naive",
10096
- percentage: 100,
10097
- bestMakespan,
10098
- currentIteration: count,
10099
- totalIterations: cappedTotal
10100
- });
10101
- return {
10102
- solverId: "naive",
10103
- solverName: "Naive (Brute Force)",
10104
- makespan: bestMakespan,
10105
- schedule: bestSchedule,
10106
- elapsedMs: performance.now() - startTime,
10107
- iterations: count
10108
- };
10109
- }
10110
- };
10111
- function factorial(n) {
10112
- if (n <= 1)
10113
- return 1;
10114
- let result = 1;
10115
- for (let i = 2; i <= n; i++)
10116
- result *= i;
10117
- return result;
10118
- }
10119
-
10120
- // ../../b4m-core/packages/quantum/dist/src/solvers/random-restart-solver.js
10121
- var RESTARTS = 50;
10122
- var HILL_CLIMB_ITERATIONS = 500;
10123
- var randomRestartSolver = {
10124
- id: "random-restart",
10125
- name: "Random Restart Hill Climbing",
10126
- description: "Multiple random starts with local search improvement via operation swaps.",
10127
- async solve(problem, options) {
10128
- const startTime = performance.now();
10129
- const timeoutMs = options?.timeoutMs ?? 1e4;
10130
- const rng = createRng(42);
10131
- let globalBestMakespan = Infinity;
10132
- let globalBestSchedule = decodePermutation(randomPermutation(problem, rng), problem);
10133
- let totalIterations = 0;
10134
- for (let restart = 0; restart < RESTARTS; restart++) {
10135
- if (performance.now() - startTime > timeoutMs)
10136
- break;
10137
- let currentPerm = randomPermutation(problem, rng);
10138
- let currentSchedule = decodePermutation(currentPerm, problem);
10139
- let currentMakespan = computeMakespan(currentSchedule);
10140
- for (let i = 0; i < HILL_CLIMB_ITERATIONS; i++) {
10141
- if (performance.now() - startTime > timeoutMs)
10142
- break;
10143
- const neighborPerm = swapNeighbor(currentPerm, rng);
10144
- const neighborSchedule = decodePermutation(neighborPerm, problem);
10145
- const neighborMakespan = computeMakespan(neighborSchedule);
10146
- if (neighborMakespan < currentMakespan) {
10147
- currentPerm = neighborPerm;
10148
- currentSchedule = neighborSchedule;
10149
- currentMakespan = neighborMakespan;
10150
- }
10151
- totalIterations++;
10152
- }
10153
- if (currentMakespan < globalBestMakespan) {
10154
- globalBestMakespan = currentMakespan;
10155
- globalBestSchedule = currentSchedule;
10156
- }
10157
- options?.onProgress?.({
10158
- solverId: "random-restart",
10159
- percentage: Math.round((restart + 1) / RESTARTS * 100),
10160
- bestMakespan: globalBestMakespan,
10161
- currentIteration: restart + 1,
10162
- totalIterations: RESTARTS
10163
- });
10164
- }
10165
- return {
10166
- solverId: "random-restart",
10167
- solverName: "Random Restart Hill Climbing",
10168
- makespan: globalBestMakespan,
10169
- schedule: globalBestSchedule,
10170
- elapsedMs: performance.now() - startTime,
10171
- iterations: totalIterations
10172
- };
10173
- }
10174
- };
10175
-
10176
- // ../../b4m-core/packages/quantum/dist/src/solvers/simulated-annealing-solver.js
10177
- var SA_CONFIGS = [
9816
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
9817
+ import { spawn } from "child_process";
9818
+ import path11 from "path";
9819
+ var DEFAULT_TIMEOUT_MS = 6e4;
9820
+ var MAX_OUTPUT_SIZE = 100 * 1024;
9821
+ var DANGEROUS_PATTERNS = [
9822
+ // Destructive file operations
9823
+ {
9824
+ pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
9825
+ reason: "Recursive delete with path",
9826
+ block: true
9827
+ },
10178
9828
  {
10179
- id: "simulated-annealing",
10180
- name: "Simulated Annealing (S)",
10181
- description: "Fast simulated annealing with 5,000 iterations. Good for quick estimates.",
10182
- initialTemp: 100,
10183
- coolingRate: 0.995,
10184
- iterations: 5e3
9829
+ pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
9830
+ reason: "Force delete without preserve root",
9831
+ block: true
10185
9832
  },
10186
9833
  {
10187
- id: "simulated-annealing-medium",
10188
- name: "Simulated Annealing (M)",
10189
- description: "Medium simulated annealing with 25,000 iterations. Balanced speed/quality.",
10190
- initialTemp: 200,
10191
- coolingRate: 0.9985,
10192
- iterations: 25e3
9834
+ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
9835
+ reason: "Recursive force delete on root paths",
9836
+ block: true
10193
9837
  },
10194
9838
  {
10195
- id: "simulated-annealing-large",
10196
- name: "Simulated Annealing (L)",
10197
- description: "Long simulated annealing with 100,000 iterations. Best solution quality.",
10198
- initialTemp: 500,
10199
- coolingRate: 0.99995,
10200
- iterations: 1e5
10201
- }
10202
- ];
10203
- function createSASolver(config) {
10204
- return {
10205
- id: config.id,
10206
- name: config.name,
10207
- description: config.description,
10208
- async solve(problem, options) {
10209
- const startTime = performance.now();
10210
- const timeoutMs = options?.timeoutMs ?? 3e4;
10211
- const rng = createRng(42);
10212
- let currentPerm = randomPermutation(problem, rng);
10213
- let currentSchedule = decodePermutation(currentPerm, problem);
10214
- let currentMakespan = computeMakespan(currentSchedule);
10215
- let bestPerm = currentPerm;
10216
- let bestSchedule = currentSchedule;
10217
- let bestMakespan = currentMakespan;
10218
- let temperature = config.initialTemp;
10219
- const progressInterval = Math.max(1, Math.floor(config.iterations / 100));
10220
- for (let i = 0; i < config.iterations; i++) {
10221
- if (performance.now() - startTime > timeoutMs)
10222
- break;
10223
- const neighborPerm = swapNeighbor(currentPerm, rng);
10224
- const neighborSchedule = decodePermutation(neighborPerm, problem);
10225
- const neighborMakespan = computeMakespan(neighborSchedule);
10226
- const delta = neighborMakespan - currentMakespan;
10227
- if (delta < 0 || rng() < Math.exp(-delta / temperature)) {
10228
- currentPerm = neighborPerm;
10229
- currentSchedule = neighborSchedule;
10230
- currentMakespan = neighborMakespan;
10231
- if (currentMakespan < bestMakespan) {
10232
- bestPerm = currentPerm;
10233
- bestSchedule = currentSchedule;
10234
- bestMakespan = currentMakespan;
10235
- }
10236
- }
10237
- temperature *= config.coolingRate;
10238
- if (options?.onProgress && i % progressInterval === 0) {
10239
- options.onProgress({
10240
- solverId: config.id,
10241
- percentage: Math.round(i / config.iterations * 100),
10242
- bestMakespan,
10243
- currentIteration: i,
10244
- totalIterations: config.iterations
10245
- });
10246
- }
10247
- }
10248
- options?.onProgress?.({
10249
- solverId: config.id,
10250
- percentage: 100,
10251
- bestMakespan,
10252
- currentIteration: config.iterations,
10253
- totalIterations: config.iterations
10254
- });
10255
- return {
10256
- solverId: config.id,
10257
- solverName: config.name,
10258
- makespan: bestMakespan,
10259
- schedule: bestSchedule,
10260
- elapsedMs: performance.now() - startTime,
10261
- iterations: config.iterations
10262
- };
10263
- }
10264
- };
10265
- }
10266
- var simulatedAnnealingSolver = createSASolver(SA_CONFIGS[0]);
10267
- var simulatedAnnealingMediumSolver = createSASolver(SA_CONFIGS[1]);
10268
- var simulatedAnnealingLargeSolver = createSASolver(SA_CONFIGS[2]);
10269
-
10270
- // ../../b4m-core/packages/quantum/dist/src/solvers/tabu-solver.js
10271
- var ITERATIONS = 1e4;
10272
- var TABU_TENURE = 15;
10273
- var NEIGHBORHOOD_SIZE = 20;
10274
- var tabuSolver = {
10275
- id: "tabu",
10276
- name: "Tabu Search",
10277
- description: "Hill climbing with short-term memory to avoid revisiting recent solutions.",
10278
- async solve(problem, options) {
10279
- const startTime = performance.now();
10280
- const timeoutMs = options?.timeoutMs ?? 15e3;
10281
- const rng = createRng(42);
10282
- let currentPerm = randomPermutation(problem, rng);
10283
- let currentSchedule = decodePermutation(currentPerm, problem);
10284
- let currentMakespan = computeMakespan(currentSchedule);
10285
- let bestSchedule = currentSchedule;
10286
- let bestMakespan = currentMakespan;
10287
- const tabuList = /* @__PURE__ */ new Map();
10288
- const progressInterval = Math.max(1, Math.floor(ITERATIONS / 100));
10289
- for (let iter = 0; iter < ITERATIONS; iter++) {
10290
- if (performance.now() - startTime > timeoutMs)
10291
- break;
10292
- let bestNeighborPerm = null;
10293
- let bestNeighborMakespan = Infinity;
10294
- let bestSwapKey = "";
10295
- for (let n = 0; n < NEIGHBORHOOD_SIZE; n++) {
10296
- const i = Math.floor(rng() * currentPerm.length);
10297
- let j = Math.floor(rng() * (currentPerm.length - 1));
10298
- if (j >= i)
10299
- j++;
10300
- const swapKey = `${Math.min(i, j)},${Math.max(i, j)}`;
10301
- const candidatePerm = [...currentPerm];
10302
- [candidatePerm[i], candidatePerm[j]] = [candidatePerm[j], candidatePerm[i]];
10303
- const candidateSchedule = decodePermutation(candidatePerm, problem);
10304
- const candidateMakespan = computeMakespan(candidateSchedule);
10305
- const isTabu = (tabuList.get(swapKey) ?? 0) > iter;
10306
- const aspirationMet = candidateMakespan < bestMakespan;
10307
- if ((!isTabu || aspirationMet) && candidateMakespan < bestNeighborMakespan) {
10308
- bestNeighborPerm = candidatePerm;
10309
- bestNeighborMakespan = candidateMakespan;
10310
- bestSwapKey = swapKey;
10311
- }
10312
- }
10313
- if (bestNeighborPerm) {
10314
- currentPerm = bestNeighborPerm;
10315
- currentSchedule = decodePermutation(currentPerm, problem);
10316
- currentMakespan = bestNeighborMakespan;
10317
- tabuList.set(bestSwapKey, iter + TABU_TENURE);
10318
- if (currentMakespan < bestMakespan) {
10319
- bestMakespan = currentMakespan;
10320
- bestSchedule = currentSchedule;
10321
- }
10322
- }
10323
- if (iter % 100 === 0) {
10324
- for (const [key, expiry] of tabuList) {
10325
- if (expiry <= iter)
10326
- tabuList.delete(key);
10327
- }
10328
- }
10329
- if (options?.onProgress && iter % progressInterval === 0) {
10330
- options.onProgress({
10331
- solverId: "tabu",
10332
- percentage: Math.round(iter / ITERATIONS * 100),
10333
- bestMakespan,
10334
- currentIteration: iter,
10335
- totalIterations: ITERATIONS
10336
- });
10337
- }
10338
- }
10339
- options?.onProgress?.({
10340
- solverId: "tabu",
10341
- percentage: 100,
10342
- bestMakespan,
10343
- currentIteration: ITERATIONS,
10344
- totalIterations: ITERATIONS
10345
- });
10346
- return {
10347
- solverId: "tabu",
10348
- solverName: "Tabu Search",
10349
- makespan: bestMakespan,
10350
- schedule: bestSchedule,
10351
- elapsedMs: performance.now() - startTime,
10352
- iterations: ITERATIONS
10353
- };
10354
- }
10355
- };
10356
-
10357
- // ../../b4m-core/packages/quantum/dist/src/solvers/genetic-algorithm-solver.js
10358
- var POPULATION_SIZE = 50;
10359
- var GENERATIONS = 200;
10360
- var TOURNAMENT_SIZE = 5;
10361
- var MUTATION_RATE = 0.15;
10362
- var geneticAlgorithmSolver = {
10363
- id: "genetic-algorithm",
10364
- name: "Genetic Algorithm",
10365
- description: "Population-based search with tournament selection, crossover, and mutation.",
10366
- async solve(problem, options) {
10367
- const startTime = performance.now();
10368
- const timeoutMs = options?.timeoutMs ?? 2e4;
10369
- const rng = createRng(42);
10370
- let population = [];
10371
- for (let i = 0; i < POPULATION_SIZE; i++) {
10372
- const perm = randomPermutation(problem, rng);
10373
- const schedule = decodePermutation(perm, problem);
10374
- const makespan = computeMakespan(schedule);
10375
- population.push({ perm, makespan });
10376
- }
10377
- let bestIndividual = population.reduce((best, ind) => ind.makespan < best.makespan ? ind : best);
10378
- for (let gen = 0; gen < GENERATIONS; gen++) {
10379
- if (performance.now() - startTime > timeoutMs)
10380
- break;
10381
- const newPopulation = [];
10382
- newPopulation.push({ ...bestIndividual });
10383
- while (newPopulation.length < POPULATION_SIZE) {
10384
- const parent1 = tournamentSelect(population, TOURNAMENT_SIZE, rng);
10385
- const parent2 = tournamentSelect(population, TOURNAMENT_SIZE, rng);
10386
- let childPerm = crossover(parent1.perm, parent2.perm, rng);
10387
- if (rng() < MUTATION_RATE) {
10388
- childPerm = swapNeighbor(childPerm, rng);
10389
- }
10390
- const childSchedule = decodePermutation(childPerm, problem);
10391
- const childMakespan = computeMakespan(childSchedule);
10392
- newPopulation.push({ perm: childPerm, makespan: childMakespan });
10393
- }
10394
- population = newPopulation;
10395
- const genBest = population.reduce((best, ind) => ind.makespan < best.makespan ? ind : best);
10396
- if (genBest.makespan < bestIndividual.makespan) {
10397
- bestIndividual = genBest;
10398
- }
10399
- options?.onProgress?.({
10400
- solverId: "genetic-algorithm",
10401
- percentage: Math.round((gen + 1) / GENERATIONS * 100),
10402
- bestMakespan: bestIndividual.makespan,
10403
- currentIteration: gen + 1,
10404
- totalIterations: GENERATIONS
10405
- });
10406
- }
10407
- const bestSchedule = decodePermutation(bestIndividual.perm, problem);
10408
- return {
10409
- solverId: "genetic-algorithm",
10410
- solverName: "Genetic Algorithm",
10411
- makespan: bestIndividual.makespan,
10412
- schedule: bestSchedule,
10413
- elapsedMs: performance.now() - startTime,
10414
- iterations: GENERATIONS * POPULATION_SIZE
10415
- };
10416
- }
10417
- };
10418
- function tournamentSelect(population, size, rng) {
10419
- let best = population[Math.floor(rng() * population.length)];
10420
- for (let i = 1; i < size; i++) {
10421
- const candidate = population[Math.floor(rng() * population.length)];
10422
- if (candidate.makespan < best.makespan) {
10423
- best = candidate;
10424
- }
10425
- }
10426
- return best;
10427
- }
10428
- function crossover(parent1, parent2, rng) {
10429
- const len = parent1.length;
10430
- const start = Math.floor(rng() * len);
10431
- const end = start + Math.floor(rng() * (len - start));
10432
- const child = new Array(len).fill(null);
10433
- const used = /* @__PURE__ */ new Set();
10434
- for (let i = start; i <= end && i < len; i++) {
10435
- child[i] = parent1[i];
10436
- used.add(`${parent1[i].jobId}-${parent1[i].opIndex}`);
10437
- }
10438
- let pos = 0;
10439
- for (const op of parent2) {
10440
- const key = `${op.jobId}-${op.opIndex}`;
10441
- if (!used.has(key)) {
10442
- while (child[pos] !== null)
10443
- pos++;
10444
- child[pos] = op;
10445
- used.add(key);
10446
- }
10447
- }
10448
- return child;
10449
- }
10450
-
10451
- // ../../b4m-core/packages/quantum/dist/src/solvers/ant-colony-solver.js
10452
- var NUM_ANTS = 20;
10453
- var ITERATIONS2 = 100;
10454
- var ALPHA = 1;
10455
- var BETA = 2;
10456
- var EVAPORATION = 0.1;
10457
- var Q = 100;
10458
- var antColonySolver = {
10459
- id: "ant-colony",
10460
- name: "Ant Colony Optimization",
10461
- description: "Swarm intelligence using pheromone trails to guide solution construction.",
10462
- async solve(problem, options) {
10463
- const startTime = performance.now();
10464
- const timeoutMs = options?.timeoutMs ?? 2e4;
10465
- const rng = createRng(42);
10466
- const allOps = buildOperationList(problem);
10467
- const numOps = allOps.length;
10468
- const pheromone = Array.from({ length: numOps }, () => new Array(numOps).fill(1));
10469
- const heuristic = allOps.map((ref) => {
10470
- const job = problem.jobs.find((j) => j.id === ref.jobId);
10471
- const op = job.operations[ref.opIndex];
10472
- return 1 / op.duration;
10473
- });
10474
- let bestMakespan = Infinity;
10475
- let bestSchedule = decodePermutation(allOps, problem);
10476
- for (let iter = 0; iter < ITERATIONS2; iter++) {
10477
- if (performance.now() - startTime > timeoutMs)
10478
- break;
10479
- const antSolutions = [];
10480
- for (let ant = 0; ant < NUM_ANTS; ant++) {
10481
- const perm = constructSolution(allOps, pheromone, heuristic, rng);
10482
- const schedule = decodePermutation(perm, problem);
10483
- const makespan = computeMakespan(schedule);
10484
- antSolutions.push({ perm, makespan });
10485
- if (makespan < bestMakespan) {
10486
- bestMakespan = makespan;
10487
- bestSchedule = schedule;
10488
- }
10489
- }
10490
- for (let i = 0; i < numOps; i++) {
10491
- for (let j = 0; j < numOps; j++) {
10492
- pheromone[i][j] *= 1 - EVAPORATION;
10493
- }
10494
- }
10495
- for (const solution of antSolutions) {
10496
- const deposit = Q / solution.makespan;
10497
- for (let pos = 0; pos < solution.perm.length; pos++) {
10498
- const opIdx = allOps.findIndex((o) => o.jobId === solution.perm[pos].jobId && o.opIndex === solution.perm[pos].opIndex);
10499
- if (opIdx >= 0) {
10500
- pheromone[pos][opIdx] += deposit;
10501
- }
10502
- }
10503
- }
10504
- options?.onProgress?.({
10505
- solverId: "ant-colony",
10506
- percentage: Math.round((iter + 1) / ITERATIONS2 * 100),
10507
- bestMakespan,
10508
- currentIteration: iter + 1,
10509
- totalIterations: ITERATIONS2
10510
- });
10511
- }
10512
- return {
10513
- solverId: "ant-colony",
10514
- solverName: "Ant Colony Optimization",
10515
- makespan: bestMakespan,
10516
- schedule: bestSchedule,
10517
- elapsedMs: performance.now() - startTime,
10518
- iterations: ITERATIONS2 * NUM_ANTS
10519
- };
10520
- }
10521
- };
10522
- function constructSolution(allOps, pheromone, heuristic, rng) {
10523
- const remaining = new Set(allOps.map((_, i) => i));
10524
- const result = [];
10525
- for (let pos = 0; pos < allOps.length; pos++) {
10526
- const candidates = Array.from(remaining);
10527
- const weights = candidates.map((opIdx) => {
10528
- const tau = Math.pow(pheromone[pos][opIdx], ALPHA);
10529
- const eta = Math.pow(heuristic[opIdx], BETA);
10530
- return tau * eta;
10531
- });
10532
- const totalWeight = weights.reduce((sum2, w) => sum2 + w, 0);
10533
- let r = rng() * totalWeight;
10534
- let selectedIdx = candidates[0];
10535
- for (let i = 0; i < candidates.length; i++) {
10536
- r -= weights[i];
10537
- if (r <= 0) {
10538
- selectedIdx = candidates[i];
10539
- break;
10540
- }
10541
- }
10542
- result.push(allOps[selectedIdx]);
10543
- remaining.delete(selectedIdx);
10544
- }
10545
- return result;
10546
- }
10547
-
10548
- // ../../b4m-core/packages/quantum/dist/src/solvers/index.js
10549
- var allSolvers = [
10550
- greedySolver,
10551
- naiveSolver,
10552
- randomRestartSolver,
10553
- simulatedAnnealingSolver,
10554
- simulatedAnnealingMediumSolver,
10555
- simulatedAnnealingLargeSolver,
10556
- tabuSolver,
10557
- geneticAlgorithmSolver,
10558
- antColonySolver
10559
- ];
10560
- var solverRegistry = new Map(allSolvers.map((s) => [s.id, s]));
10561
- function getSolver(id) {
10562
- return solverRegistry.get(id);
10563
- }
10564
- function getAvailableSolverIds() {
10565
- return Array.from(solverRegistry.keys());
10566
- }
10567
-
10568
- // ../../b4m-core/packages/quantum/dist/src/prompts/system-prompt.js
10569
- var QUANTUM_CANVASSER_SYSTEM_PROMPT = `You are the Optimization Canvasser, an AI agent specializing in combinatorial optimization and job-shop scheduling. You help users formulate scheduling problems, run solver algorithms, and interpret optimization results. You are solver-agnostic \u2014 you route problems to the best solver whether classical (greedy, simulated annealing, genetic algorithms, HiGHS MIP) or quantum (QAOA), building credibility through honest recommendations.
10570
-
10571
- ## Your Capabilities
10572
-
10573
- You have two specialized tools:
10574
-
10575
- ### quantum_formulate
10576
- Converts natural language descriptions into structured SchedulingProblem definitions. When a user describes a scheduling scenario in plain English, use this tool to extract:
10577
- - Jobs (ordered sequences of operations)
10578
- - Machines (resources that process operations)
10579
- - Operation durations and machine assignments
10580
- - Precedence constraints (operations within a job must execute in order)
10581
-
10582
- ### quantum_schedule
10583
- Runs optimization solvers on a structured SchedulingProblem. Available solvers:
10584
- ${allSolvers.map((s) => `- **${s.name}** (\`${s.id}\`): ${s.description}`).join("\n")}
10585
-
10586
- ## How to Help Users
10587
-
10588
- ### Problem Formulation
10589
- When a user describes a scheduling scenario:
10590
- 1. Use \`quantum_formulate\` to convert their description into a structured problem
10591
- 2. Present the formulated problem clearly, showing jobs, machines, and operation sequences
10592
- 3. Ask if adjustments are needed before solving
10593
-
10594
- ### Running Solvers
10595
- When solving a problem:
10596
- 1. Start with the \`greedy\` solver for a quick baseline result
10597
- 2. For better solutions, suggest running multiple solvers: \`greedy\`, \`simulated-annealing\`, \`tabu\`, \`genetic-algorithm\`
10598
- 3. Explain that metaheuristic solvers explore larger solution spaces but take longer
10599
- 4. Compare results across solvers when multiple are run
10600
-
10601
- ### Interpreting Results
10602
- When presenting solver results:
10603
- - Explain the **makespan** (total schedule length) and why lower is better
10604
- - Walk through the schedule showing which jobs run on which machines at what times
10605
- - Identify bottleneck machines (those with the most continuous utilization)
10606
- - Suggest improvements if the problem structure allows (e.g., rebalancing operations)
10607
-
10608
- ### QUBO Encoding
10609
- If asked about quantum computing aspects:
10610
- - Explain how scheduling problems are encoded as QUBO (Quadratic Unconstrained Binary Optimization) matrices
10611
- - Time-indexed binary variables: x_{o,t} = 1 if operation o starts at time t
10612
- - Three constraint families: exactly-once (each operation starts exactly once), no-overlap (no two operations on same machine at same time), precedence (operations within a job maintain order)
10613
- - QUBO matrices can be solved by quantum annealers (D-Wave) or quantum-inspired classical solvers
10614
-
10615
- ## Communication Style
10616
- - Be precise with numbers and scheduling terminology
10617
- - Use clear formatting: tables for schedules, bullet points for comparisons
10618
- - When explaining optimization concepts, ground them in the user's specific problem
10619
- - Proactively suggest next steps (e.g., "Would you like to try more solvers?" or "Should I adjust the problem?")
10620
-
10621
- ## Important Notes
10622
- - All solvers run client-side in the browser via Web Workers \u2014 no server compute needed
10623
- - The greedy solver is deterministic and instant; metaheuristic solvers involve randomness
10624
- - For very large problems (50+ operations), recommend the greedy and simulated-annealing solvers
10625
- - The HiGHS MIP solver uses WASM and provides optimal solutions for small problems but may be slow on large ones`;
10626
- var PROBLEM_FORMULATION_PROMPT = `You are a job-shop scheduling problem formulator. Convert the user's natural language description into a structured SchedulingProblem JSON object.
10627
-
10628
- RULES:
10629
- 1. Each job has an id (number starting from 0), a name, and an ordered array of operations
10630
- 2. Each machine has an id (number starting from 0) and a name
10631
- 3. Each operation has jobId (matching its parent job), machineId (which machine it runs on), and duration (positive integer)
10632
- 4. Operations within a job execute in order (first operation must finish before second starts)
10633
- 5. Each machine can only process one operation at a time
10634
- 6. Assign reasonable durations if not specified (use whole numbers)
10635
- 7. Create meaningful names for jobs and machines based on context
10636
-
10637
- RESPOND WITH ONLY A JSON OBJECT matching this schema:
10638
- {
10639
- "name": "string - descriptive problem name",
10640
- "description": "string - brief description",
10641
- "jobs": [
10642
- {
10643
- "id": 0,
10644
- "name": "Job Name",
10645
- "operations": [
10646
- { "jobId": 0, "machineId": 0, "duration": 3 }
10647
- ]
10648
- }
10649
- ],
10650
- "machines": [
10651
- { "id": 0, "name": "Machine Name" }
10652
- ]
10653
- }
10654
-
10655
- No markdown, no explanation, no code blocks \u2014 just the raw JSON object.`;
10656
-
10657
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/quantumSchedule/index.js
10658
- function formatResult(result) {
10659
- const lines = [
10660
- `Solver: ${result.solverName} (${result.solverId})`,
10661
- `Makespan: ${result.makespan}`,
10662
- `Elapsed: ${result.elapsedMs.toFixed(1)}ms`
10663
- ];
10664
- if (result.iterations !== void 0) {
10665
- lines.push(`Iterations: ${result.iterations}`);
10666
- }
10667
- lines.push("Schedule:");
10668
- const byMachine = /* @__PURE__ */ new Map();
10669
- for (const op of result.schedule) {
10670
- if (!byMachine.has(op.machineId))
10671
- byMachine.set(op.machineId, []);
10672
- byMachine.get(op.machineId).push(op);
10673
- }
10674
- for (const [machineId, ops] of byMachine) {
10675
- const sorted = ops.sort((a, b) => a.startTime - b.startTime);
10676
- const timeline = sorted.map((op) => `Job${op.jobId}[${op.startTime}-${op.endTime}]`).join(" -> ");
10677
- lines.push(` Machine ${machineId}: ${timeline}`);
10678
- }
10679
- return lines.join("\n");
10680
- }
10681
- async function runQuantumSchedule(params) {
10682
- const parseResult = SchedulingProblemSchema.safeParse(params.problem);
10683
- if (!parseResult.success) {
10684
- return `Error: Invalid scheduling problem format.
10685
- ${parseResult.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n")}
10686
-
10687
- Expected format: { name, jobs: [{ id, name, operations: [{ jobId, machineId, duration }] }], machines: [{ id, name }] }`;
10688
- }
10689
- const problem = parseResult.data;
10690
- let solverIds;
10691
- if (params.solverIds && params.solverIds.length > 0) {
10692
- const invalid = params.solverIds.filter((id) => !SolverIdSchema.safeParse(id).success);
10693
- if (invalid.length > 0) {
10694
- return `Error: Unknown solver IDs: ${invalid.join(", ")}.
10695
- Available solvers: ${getAvailableSolverIds().join(", ")}`;
10696
- }
10697
- solverIds = params.solverIds;
10698
- } else {
10699
- solverIds = ["greedy"];
10700
- }
10701
- const timeoutMs = params.timeoutMs ?? 1e4;
10702
- const results = [];
10703
- const errors = [];
10704
- for (const solverId of solverIds) {
10705
- const solver = getSolver(solverId);
10706
- if (!solver) {
10707
- errors.push(`Solver "${solverId}" not found`);
10708
- continue;
10709
- }
10710
- try {
10711
- const result = await solver.solve(problem, { timeoutMs });
10712
- results.push(result);
10713
- } catch (err) {
10714
- errors.push(`${solver.name}: ${err instanceof Error ? err.message : String(err)}`);
10715
- }
10716
- }
10717
- if (results.length === 0) {
10718
- return `Error: No solvers completed successfully.
10719
- ${errors.join("\n")}`;
10720
- }
10721
- results.sort((a, b) => a.makespan - b.makespan);
10722
- const lines = [];
10723
- lines.push(`## Scheduling Results for "${problem.name}"`);
10724
- lines.push(`Problem: ${problem.jobs.length} jobs, ${problem.machines.length} machines, ${problem.jobs.reduce((s, j) => s + j.operations.length, 0)} operations`);
10725
- lines.push("");
10726
- if (results.length > 1) {
10727
- lines.push(`### Winner: ${results[0].solverName} (makespan: ${results[0].makespan})`);
10728
- lines.push("");
10729
- }
10730
- for (const result of results) {
10731
- lines.push(`### ${result.solverName}`);
10732
- lines.push(formatResult(result));
10733
- lines.push("");
10734
- }
10735
- if (errors.length > 0) {
10736
- lines.push("### Errors");
10737
- errors.forEach((e) => lines.push(`- ${e}`));
10738
- }
10739
- return lines.join("\n");
10740
- }
10741
- var quantumScheduleTool = {
10742
- name: "quantum_schedule",
10743
- implementation: () => ({
10744
- toolFn: (value) => runQuantumSchedule(value),
10745
- toolSchema: {
10746
- name: "quantum_schedule",
10747
- description: `Run quantum-inspired optimization solvers on a job-shop scheduling problem. Finds optimal or near-optimal schedules that minimize makespan (total completion time).
10748
-
10749
- Available solvers: ${allSolvers.map((s) => `${s.id} (${s.name})`).join(", ")}.
10750
-
10751
- The "greedy" solver runs instantly. Metaheuristic solvers (simulated-annealing, tabu, genetic-algorithm, ant-colony) explore larger solution spaces but take longer. Use multiple solvers to race them against each other.
10752
-
10753
- The problem must define jobs (each with ordered operations) and machines. Each operation runs on a specific machine for a specific duration. Operations within a job must run in order.`,
10754
- parameters: {
10755
- type: "object",
10756
- properties: {
10757
- problem: {
10758
- type: "object",
10759
- description: "The scheduling problem to solve. Must include name, jobs array, and machines array.",
10760
- properties: {
10761
- name: { type: "string", description: "Name of the scheduling problem" },
10762
- description: { type: "string", description: "Optional description" },
10763
- jobs: {
10764
- type: "array",
10765
- description: "Array of jobs, each with id, name, and operations",
10766
- items: {
10767
- type: "object",
10768
- properties: {
10769
- id: { type: "number" },
10770
- name: { type: "string" },
10771
- operations: {
10772
- type: "array",
10773
- items: {
10774
- type: "object",
10775
- properties: {
10776
- jobId: { type: "number", description: "Must match the parent job id" },
10777
- machineId: { type: "number", description: "Which machine this operation runs on" },
10778
- duration: { type: "number", description: "Processing time" }
10779
- },
10780
- required: ["jobId", "machineId", "duration"]
10781
- }
10782
- }
10783
- },
10784
- required: ["id", "name", "operations"]
10785
- }
10786
- },
10787
- machines: {
10788
- type: "array",
10789
- description: "Array of machines with id and name",
10790
- items: {
10791
- type: "object",
10792
- properties: {
10793
- id: { type: "number" },
10794
- name: { type: "string" }
10795
- },
10796
- required: ["id", "name"]
10797
- }
10798
- }
10799
- },
10800
- required: ["name", "jobs", "machines"]
10801
- },
10802
- solverIds: {
10803
- type: "array",
10804
- items: { type: "string" },
10805
- description: `Which solvers to run. Defaults to ["greedy"]. Available: ${getAvailableSolverIds().join(", ")}. Use multiple to race solvers.`
10806
- },
10807
- timeoutMs: {
10808
- type: "number",
10809
- description: "Maximum time per solver in milliseconds (default: 10000)"
10810
- }
10811
- },
10812
- required: ["problem"]
10813
- }
10814
- }
10815
- })
10816
- };
10817
-
10818
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/quantumFormulate/index.js
10819
- var quantumFormulateTool = {
10820
- name: "quantum_formulate",
10821
- implementation: (context) => ({
10822
- toolFn: async (value) => {
10823
- const params = value;
10824
- const userDescription = params.description?.trim();
10825
- if (!userDescription) {
10826
- return "Error: Please provide a description of the scheduling problem to formulate.";
10827
- }
10828
- try {
10829
- let rawContent = "";
10830
- await context.llm.complete(context.model ?? "gpt-4", [
10831
- { role: "system", content: PROBLEM_FORMULATION_PROMPT },
10832
- { role: "user", content: userDescription }
10833
- ], { temperature: 0.2, stream: false }, async (texts) => {
10834
- rawContent = texts.filter((t) => t !== null && t !== void 0).join("");
10835
- });
10836
- let jsonStr = rawContent.trim();
10837
- const codeBlockMatch = jsonStr.match(/```(?:json)?\s*([\s\S]*?)```/);
10838
- if (codeBlockMatch) {
10839
- jsonStr = codeBlockMatch[1].trim();
10840
- }
10841
- let parsed;
10842
- try {
10843
- parsed = JSON.parse(jsonStr);
10844
- } catch {
10845
- return `Error: Could not parse LLM response as JSON. Raw response:
10846
- ${rawContent}
10847
-
10848
- Please try rephrasing your description with more specific details about jobs, machines, and processing times.`;
10849
- }
10850
- const validated = SchedulingProblemSchema.safeParse(parsed);
10851
- if (!validated.success) {
10852
- return `Error: LLM generated an invalid problem structure.
10853
- ${validated.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n")}
10854
-
10855
- Raw output:
10856
- ${jsonStr}
10857
-
10858
- Please try rephrasing your description.`;
10859
- }
10860
- const problem = validated.data;
10861
- const totalOps = problem.jobs.reduce((s, j) => s + j.operations.length, 0);
10862
- const lines = [
10863
- `## Formulated: "${problem.name}"`,
10864
- problem.description ? `
10865
- ${problem.description}` : "",
10866
- "",
10867
- `**${problem.jobs.length} jobs, ${problem.machines.length} machines, ${totalOps} operations**`,
10868
- "",
10869
- "### Jobs:",
10870
- ...problem.jobs.map((j) => {
10871
- const ops = j.operations.map((op, i) => {
10872
- const machine = problem.machines.find((m) => m.id === op.machineId);
10873
- return `Step ${i + 1}: ${machine?.name ?? `Machine ${op.machineId}`} (${op.duration}t)`;
10874
- });
10875
- return `- **${j.name}**: ${ops.join(" -> ")}`;
10876
- }),
10877
- "",
10878
- "### Machines:",
10879
- ...problem.machines.map((m) => `- ${m.name} (id: ${m.id})`),
10880
- "",
10881
- "### Structured Problem (JSON):",
10882
- "```json",
10883
- JSON.stringify(problem, null, 2),
10884
- "```",
10885
- "",
10886
- "You can now use the `quantum_schedule` tool to solve this problem with various optimization algorithms."
10887
- ];
10888
- return lines.filter((l) => l !== void 0).join("\n");
10889
- } catch (err) {
10890
- return `Error formulating problem: ${err instanceof Error ? err.message : String(err)}`;
10891
- }
10892
- },
10893
- toolSchema: {
10894
- name: "quantum_formulate",
10895
- description: `Convert a natural language description of a scheduling/optimization problem into a structured SchedulingProblem that can be solved by the quantum_schedule tool.
10896
-
10897
- Describe the problem in plain English \u2014 what jobs need to be done, what machines or resources are available, how long each step takes, and any ordering constraints. The tool will produce a structured problem definition.
10898
-
10899
- Examples:
10900
- - "I have 3 orders to manufacture. Each needs cutting, welding, and painting. Cutting takes 2h, welding 3h, painting 1h."
10901
- - "Schedule 4 software builds across 2 CI runners. Build A needs compile (5min) then test (3min)..."
10902
- - "A bakery needs to make 5 cakes. Each cake goes through mixing, baking, and decorating on separate stations."`,
10903
- parameters: {
10904
- type: "object",
10905
- properties: {
10906
- description: {
10907
- type: "string",
10908
- description: "Natural language description of the scheduling problem to formulate"
10909
- }
10910
- },
10911
- required: ["description"]
10912
- }
10913
- }
10914
- })
10915
- };
10916
-
10917
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/navigateView/index.js
10918
- var navigateViewTool = {
10919
- name: "navigate_view",
10920
- implementation: (context) => ({
10921
- toolFn: async (value) => {
10922
- const params = value;
10923
- const { suggestions } = params;
10924
- context.logger.log("\u{1F9ED} navigate_view: Received suggestions:", JSON.stringify(suggestions));
10925
- if (!suggestions || !Array.isArray(suggestions) || suggestions.length === 0) {
10926
- return "No navigation suggestions provided.";
10927
- }
10928
- const capped = suggestions.slice(0, 3);
10929
- const invalid = capped.filter((s) => !getViewById(s.viewId));
10930
- if (invalid.length > 0) {
10931
- context.logger.log("\u{1F9ED} navigate_view: Unknown viewIds:", invalid.map((s) => s.viewId));
10932
- }
10933
- const isAdmin = context.user?.isAdmin === true;
10934
- const intents = resolveNavigationIntents(capped, isAdmin);
10935
- if (intents.length === 0) {
10936
- return "No valid navigation views matched the provided viewIds.";
10937
- }
10938
- context.logger.log("\u{1F9ED} navigate_view: Resolved intents:", intents.map((i) => i.viewId));
10939
- return JSON.stringify({
10940
- __navigationIntents: true,
10941
- intents,
10942
- message: `Suggested ${intents.length} navigation option(s): ${intents.map((i) => i.label).join(", ")}`
10943
- });
10944
- },
10945
- toolSchema: {
10946
- name: "navigate_view",
10947
- description: "Suggest navigation to relevant app views. Returns inline action buttons the user can click. ALWAYS use this tool when your response discusses a topic that has a matching view (e.g., scheduling \u2192 opti.scheduling, user management \u2192 admin.users). Call this tool alongside your text answer \u2014 answer the question AND suggest where to go.",
10948
- parameters: {
10949
- type: "object",
10950
- properties: {
10951
- suggestions: {
10952
- type: "array",
10953
- description: "Navigation suggestions (1-3 items). Each suggests a view the user might want to visit.",
10954
- items: {
10955
- type: "object",
10956
- properties: {
10957
- viewId: {
10958
- type: "string",
10959
- description: 'The view ID from the available views list (e.g., "opti.scheduling", "admin.users")'
10960
- },
10961
- reason: {
10962
- type: "string",
10963
- description: "Brief reason why this view is relevant (max 80 chars, shown as tooltip)"
10964
- }
10965
- },
10966
- required: ["viewId", "reason"]
10967
- },
10968
- maxItems: 3,
10969
- minItems: 1
10970
- }
10971
- },
10972
- required: ["suggestions"]
10973
- }
10974
- }
10975
- })
10976
- };
10977
-
10978
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
10979
- import { spawn } from "child_process";
10980
- import path11 from "path";
10981
- var DEFAULT_TIMEOUT_MS = 6e4;
10982
- var MAX_OUTPUT_SIZE = 100 * 1024;
10983
- var DANGEROUS_PATTERNS = [
10984
- // Destructive file operations
10985
- {
10986
- pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
10987
- reason: "Recursive delete with path",
10988
- block: true
10989
- },
10990
- {
10991
- pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
10992
- reason: "Force delete without preserve root",
10993
- block: true
10994
- },
10995
- {
10996
- pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
10997
- reason: "Recursive force delete on root paths",
10998
- block: true
10999
- },
11000
- {
11001
- pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
11002
- reason: "Force recursive delete on root paths",
11003
- block: true
11004
- },
11005
- // System-level dangerous commands
11006
- { pattern: /\bsudo\b/i, reason: "Elevated privileges (sudo)", block: true },
11007
- { pattern: /\bsu\s+(-|root)/i, reason: "Switch to root user", block: true },
11008
- { pattern: /\bchmod\s+777\b/i, reason: "Overly permissive chmod", block: false },
11009
- { pattern: /\bchown\s+-R\s+root/i, reason: "Recursive chown to root", block: true },
11010
- // Disk/partition operations
11011
- { pattern: /\bmkfs\b/i, reason: "Filesystem creation", block: true },
11012
- { pattern: /\bfdisk\b/i, reason: "Disk partitioning", block: true },
11013
- { pattern: /\bdd\s+.*of=\/dev\//i, reason: "Direct disk write", block: true },
11014
- // Network attacks
11015
- { pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i, reason: "Reverse shell attempt", block: true },
11016
- { pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
11017
- { pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
11018
- // Fork bombs and resource exhaustion
11019
- { pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i, reason: "Fork bomb", block: true },
11020
- { pattern: /\bwhile\s+true.*do.*done.*&/i, reason: "Infinite loop in background", block: false },
11021
- // Credential/sensitive data access
11022
- { pattern: /\/etc\/shadow/i, reason: "Access to shadow file", block: true },
11023
- { pattern: /\/etc\/passwd.*>/i, reason: "Modifying passwd file", block: true },
11024
- { pattern: /\baws\s+.*--profile\s+/i, reason: "AWS profile access", block: false },
11025
- // History/log manipulation
11026
- { pattern: /\bhistory\s+-c\b/i, reason: "Clearing shell history", block: false },
11027
- { pattern: />\s*\/var\/log\//i, reason: "Overwriting system logs", block: true },
11028
- // Dangerous redirects
11029
- { pattern: />\s*\/dev\/sda/i, reason: "Writing to block device", block: true },
11030
- { pattern: />\s*\/dev\/null.*2>&1.*</i, reason: "Potentially hiding output", block: false },
11031
- // Environment manipulation
11032
- { pattern: /\bexport\s+PATH\s*=\s*[^$]/i, reason: "Overwriting PATH", block: false },
11033
- { pattern: /\bexport\s+LD_PRELOAD/i, reason: "LD_PRELOAD manipulation", block: true },
11034
- // Process/system control
11035
- { pattern: /\bkill\s+-9\s+(-1|1)\b/i, reason: "Killing all processes", block: true },
11036
- { pattern: /\bkillall\s+-9\b/i, reason: "Force killing processes", block: false },
11037
- { pattern: /\bshutdown\b/i, reason: "System shutdown", block: true },
11038
- { pattern: /\breboot\b/i, reason: "System reboot", block: true },
11039
- { pattern: /\binit\s+[06]\b/i, reason: "System runlevel change", block: true }
9839
+ pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
9840
+ reason: "Force recursive delete on root paths",
9841
+ block: true
9842
+ },
9843
+ // System-level dangerous commands
9844
+ { pattern: /\bsudo\b/i, reason: "Elevated privileges (sudo)", block: true },
9845
+ { pattern: /\bsu\s+(-|root)/i, reason: "Switch to root user", block: true },
9846
+ { pattern: /\bchmod\s+777\b/i, reason: "Overly permissive chmod", block: false },
9847
+ { pattern: /\bchown\s+-R\s+root/i, reason: "Recursive chown to root", block: true },
9848
+ // Disk/partition operations
9849
+ { pattern: /\bmkfs\b/i, reason: "Filesystem creation", block: true },
9850
+ { pattern: /\bfdisk\b/i, reason: "Disk partitioning", block: true },
9851
+ { pattern: /\bdd\s+.*of=\/dev\//i, reason: "Direct disk write", block: true },
9852
+ // Network attacks
9853
+ { pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i, reason: "Reverse shell attempt", block: true },
9854
+ { pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
9855
+ { pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
9856
+ // Fork bombs and resource exhaustion
9857
+ { pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i, reason: "Fork bomb", block: true },
9858
+ { pattern: /\bwhile\s+true.*do.*done.*&/i, reason: "Infinite loop in background", block: false },
9859
+ // Credential/sensitive data access
9860
+ { pattern: /\/etc\/shadow/i, reason: "Access to shadow file", block: true },
9861
+ { pattern: /\/etc\/passwd.*>/i, reason: "Modifying passwd file", block: true },
9862
+ { pattern: /\baws\s+.*--profile\s+/i, reason: "AWS profile access", block: false },
9863
+ // History/log manipulation
9864
+ { pattern: /\bhistory\s+-c\b/i, reason: "Clearing shell history", block: false },
9865
+ { pattern: />\s*\/var\/log\//i, reason: "Overwriting system logs", block: true },
9866
+ // Dangerous redirects
9867
+ { pattern: />\s*\/dev\/sda/i, reason: "Writing to block device", block: true },
9868
+ { pattern: />\s*\/dev\/null.*2>&1.*</i, reason: "Potentially hiding output", block: false },
9869
+ // Environment manipulation
9870
+ { pattern: /\bexport\s+PATH\s*=\s*[^$]/i, reason: "Overwriting PATH", block: false },
9871
+ { pattern: /\bexport\s+LD_PRELOAD/i, reason: "LD_PRELOAD manipulation", block: true },
9872
+ // Process/system control
9873
+ { pattern: /\bkill\s+-9\s+(-1|1)\b/i, reason: "Killing all processes", block: true },
9874
+ { pattern: /\bkillall\s+-9\b/i, reason: "Force killing processes", block: false },
9875
+ { pattern: /\bshutdown\b/i, reason: "System shutdown", block: true },
9876
+ { pattern: /\breboot\b/i, reason: "System reboot", block: true },
9877
+ { pattern: /\binit\s+[06]\b/i, reason: "System runlevel change", block: true }
11040
9878
  ];
11041
9879
  var SAFE_COMMAND_PREFIXES = [
11042
9880
  "ls",
@@ -11224,7 +10062,7 @@ async function executeBashCommand(params) {
11224
10062
  });
11225
10063
  });
11226
10064
  }
11227
- function formatResult2(result, command) {
10065
+ function formatResult(result, command) {
11228
10066
  const parts = [];
11229
10067
  parts.push(`$ ${command}`);
11230
10068
  parts.push("");
@@ -11278,7 +10116,7 @@ var bashExecuteTool = {
11278
10116
  }
11279
10117
  try {
11280
10118
  const result = await executeBashCommand(params);
11281
- const formattedResult = formatResult2(result, params.command);
10119
+ const formattedResult = formatResult(result, params.command);
11282
10120
  context.logger.info("Bash: Command completed", {
11283
10121
  exitCode: result.exitCode,
11284
10122
  timedOut: result.timedOut,
@@ -11634,7 +10472,7 @@ async function getFileStats(files, since, filterPath) {
11634
10472
  });
11635
10473
  });
11636
10474
  }
11637
- function formatResult3(result) {
10475
+ function formatResult2(result) {
11638
10476
  const parts = [];
11639
10477
  if (result.error) {
11640
10478
  parts.push(`Error: ${result.error}`);
@@ -11709,7 +10547,7 @@ var recentChangesTool = {
11709
10547
  }
11710
10548
  try {
11711
10549
  const result = await getRecentChanges(params);
11712
- const formattedResult = formatResult3(result);
10550
+ const formattedResult = formatResult2(result);
11713
10551
  context.logger.info("RecentChanges: Retrieved file changes", {
11714
10552
  totalFiles: result.totalFiles,
11715
10553
  displayedFiles: result.files.length,
@@ -11807,14 +10645,8 @@ var b4mTools = {
11807
10645
  sunrise_sunset: sunriseSunsetTool,
11808
10646
  iss_tracker: issTrackerTool,
11809
10647
  planet_visibility: planetVisibilityTool,
11810
- // Knowledge base tools
11811
- search_knowledge_base: knowledgeBaseSearchTool,
11812
- retrieve_knowledge_content: knowledgeBaseRetrieveTool,
11813
- // Quantum optimization tools
11814
- quantum_schedule: quantumScheduleTool,
11815
- quantum_formulate: quantumFormulateTool,
11816
- // Navigation tool
11817
- navigate_view: navigateViewTool
10648
+ // Knowledge base search
10649
+ search_knowledge_base: knowledgeBaseSearchTool
11818
10650
  };
11819
10651
  var cliOnlyTools = {
11820
10652
  // File operation tools
@@ -11937,49 +10769,49 @@ var generateMcpTools = async (mcpData) => {
11937
10769
  import throttle2 from "lodash/throttle.js";
11938
10770
 
11939
10771
  // ../../b4m-core/packages/services/dist/src/llm/ChatCompletionFeatures.js
11940
- import { z as z140 } from "zod";
10772
+ import { z as z139 } from "zod";
11941
10773
  import uniq4 from "lodash/uniq.js";
11942
- var QuestStartBodySchema = z140.object({
11943
- userId: z140.string(),
11944
- sessionId: z140.string(),
11945
- questId: z140.string(),
11946
- message: z140.string(),
11947
- messageFileIds: z140.array(z140.string()),
11948
- historyCount: z140.number(),
11949
- fabFileIds: z140.array(z140.string()),
10774
+ var QuestStartBodySchema = z139.object({
10775
+ userId: z139.string(),
10776
+ sessionId: z139.string(),
10777
+ questId: z139.string(),
10778
+ message: z139.string(),
10779
+ messageFileIds: z139.array(z139.string()),
10780
+ historyCount: z139.number(),
10781
+ fabFileIds: z139.array(z139.string()),
11950
10782
  params: ChatCompletionCreateInputSchema,
11951
10783
  dashboardParams: DashboardParamsSchema.optional(),
11952
- enableQuestMaster: z140.boolean().optional(),
11953
- enableMementos: z140.boolean().optional(),
11954
- enableArtifacts: z140.boolean().optional(),
11955
- enableAgents: z140.boolean().optional(),
10784
+ enableQuestMaster: z139.boolean().optional(),
10785
+ enableMementos: z139.boolean().optional(),
10786
+ enableArtifacts: z139.boolean().optional(),
10787
+ enableAgents: z139.boolean().optional(),
11956
10788
  promptMeta: PromptMetaZodSchema,
11957
- tools: z140.array(z140.union([b4mLLMTools, z140.string()])).optional(),
11958
- mcpServers: z140.array(z140.string()).optional(),
11959
- projectId: z140.string().optional(),
11960
- organizationId: z140.string().nullable().optional(),
10789
+ tools: z139.array(z139.union([b4mLLMTools, z139.string()])).optional(),
10790
+ mcpServers: z139.array(z139.string()).optional(),
10791
+ projectId: z139.string().optional(),
10792
+ organizationId: z139.string().nullable().optional(),
11961
10793
  questMaster: QuestMasterParamsSchema.optional(),
11962
- toolPromptId: z140.string().optional(),
10794
+ toolPromptId: z139.string().optional(),
11963
10795
  researchMode: ResearchModeParamsSchema.optional(),
11964
- fallbackModel: z140.string().optional(),
11965
- embeddingModel: z140.string().optional(),
11966
- queryComplexity: z140.string(),
10796
+ fallbackModel: z139.string().optional(),
10797
+ embeddingModel: z139.string().optional(),
10798
+ queryComplexity: z139.string(),
11967
10799
  imageConfig: GenerateImageToolCallSchema.optional(),
11968
- deepResearchConfig: z140.object({
11969
- maxDepth: z140.number().optional(),
11970
- duration: z140.number().optional(),
10800
+ deepResearchConfig: z139.object({
10801
+ maxDepth: z139.number().optional(),
10802
+ duration: z139.number().optional(),
11971
10803
  // Note: searchers are passed via ToolContext and not through this API schema
11972
- searchers: z140.array(z140.any()).optional()
10804
+ searchers: z139.array(z139.any()).optional()
11973
10805
  }).optional(),
11974
- extraContextMessages: z140.array(z140.object({
11975
- role: z140.enum(["user", "assistant", "system", "function", "tool"]),
11976
- content: z140.union([z140.string(), z140.array(z140.any())]),
11977
- fabFileIds: z140.array(z140.string()).optional()
10806
+ extraContextMessages: z139.array(z139.object({
10807
+ role: z139.enum(["user", "assistant", "system", "function", "tool"]),
10808
+ content: z139.union([z139.string(), z139.array(z139.any())]),
10809
+ fabFileIds: z139.array(z139.string()).optional()
11978
10810
  })).optional(),
11979
10811
  /** User's timezone (IANA format, e.g., "America/New_York") */
11980
- timezone: z140.string().optional(),
10812
+ timezone: z139.string().optional(),
11981
10813
  /** Pre-fetched API key table from invoke phase — avoids redundant DB call in process (#6616 P1-a) */
11982
- apiKeyTable: z140.record(z140.string(), z140.string().nullable()).optional()
10814
+ apiKeyTable: z139.record(z139.string(), z139.string().nullable()).optional()
11983
10815
  });
11984
10816
 
11985
10817
  // ../../b4m-core/packages/services/dist/src/llm/StatusManager.js
@@ -12479,89 +11311,89 @@ var BUILT_IN_TOOL_SET = new Set(b4mLLMTools.options);
12479
11311
  import axios8 from "axios";
12480
11312
  import { fileTypeFromBuffer as fileTypeFromBuffer4 } from "file-type";
12481
11313
  import { v4 as uuidv47 } from "uuid";
12482
- import { z as z141 } from "zod";
11314
+ import { z as z140 } from "zod";
12483
11315
  import { fromZodError as fromZodError2 } from "zod-validation-error";
12484
11316
  var ImageGenerationBodySchema = OpenAIImageGenerationInput.extend({
12485
- sessionId: z141.string(),
12486
- questId: z141.string(),
12487
- userId: z141.string(),
12488
- prompt: z141.string(),
12489
- organizationId: z141.string().nullable().optional(),
12490
- safety_tolerance: z141.number().min(BFL_SAFETY_TOLERANCE.MIN).max(BFL_SAFETY_TOLERANCE.MAX).optional().default(BFL_SAFETY_TOLERANCE.DEFAULT),
12491
- prompt_upsampling: z141.boolean().optional().default(false),
12492
- seed: z141.number().nullable().optional(),
12493
- output_format: z141.enum(["jpeg", "png"]).nullable().optional().default("png"),
12494
- width: z141.number().optional(),
12495
- height: z141.number().optional(),
12496
- aspect_ratio: z141.string().optional(),
12497
- fabFileIds: z141.array(z141.string()).optional()
11317
+ sessionId: z140.string(),
11318
+ questId: z140.string(),
11319
+ userId: z140.string(),
11320
+ prompt: z140.string(),
11321
+ organizationId: z140.string().nullable().optional(),
11322
+ safety_tolerance: z140.number().min(BFL_SAFETY_TOLERANCE.MIN).max(BFL_SAFETY_TOLERANCE.MAX).optional().default(BFL_SAFETY_TOLERANCE.DEFAULT),
11323
+ prompt_upsampling: z140.boolean().optional().default(false),
11324
+ seed: z140.number().nullable().optional(),
11325
+ output_format: z140.enum(["jpeg", "png"]).nullable().optional().default("png"),
11326
+ width: z140.number().optional(),
11327
+ height: z140.number().optional(),
11328
+ aspect_ratio: z140.string().optional(),
11329
+ fabFileIds: z140.array(z140.string()).optional()
12498
11330
  });
12499
11331
 
12500
11332
  // ../../b4m-core/packages/services/dist/src/llm/VideoGeneration.js
12501
11333
  import axios9 from "axios";
12502
11334
  import { v4 as uuidv48 } from "uuid";
12503
- import { z as z142 } from "zod";
11335
+ import { z as z141 } from "zod";
12504
11336
  import { fromZodError as fromZodError3 } from "zod-validation-error";
12505
- var VideoGenerationBodySchema = z142.object({
12506
- sessionId: z142.string(),
12507
- questId: z142.string(),
12508
- userId: z142.string(),
12509
- prompt: z142.string(),
12510
- model: z142.nativeEnum(VideoModels).default(VideoModels.SORA_2),
12511
- seconds: z142.union([z142.literal(4), z142.literal(8), z142.literal(12)]).default(4),
12512
- size: z142.enum(["720x1280", "1280x720", "1024x1792", "1792x1024"]).default(VIDEO_SIZE_CONSTRAINTS.SORA.defaultSize),
12513
- organizationId: z142.string().nullable().optional()
11337
+ var VideoGenerationBodySchema = z141.object({
11338
+ sessionId: z141.string(),
11339
+ questId: z141.string(),
11340
+ userId: z141.string(),
11341
+ prompt: z141.string(),
11342
+ model: z141.nativeEnum(VideoModels).default(VideoModels.SORA_2),
11343
+ seconds: z141.union([z141.literal(4), z141.literal(8), z141.literal(12)]).default(4),
11344
+ size: z141.enum(["720x1280", "1280x720", "1024x1792", "1792x1024"]).default(VIDEO_SIZE_CONSTRAINTS.SORA.defaultSize),
11345
+ organizationId: z141.string().nullable().optional()
12514
11346
  });
12515
11347
 
12516
11348
  // ../../b4m-core/packages/services/dist/src/llm/ImageEdit.js
12517
11349
  import axios10 from "axios";
12518
11350
  import { fileTypeFromBuffer as fileTypeFromBuffer5 } from "file-type";
12519
11351
  import { v4 as uuidv49 } from "uuid";
12520
- import { z as z143 } from "zod";
11352
+ import { z as z142 } from "zod";
12521
11353
  import { fromZodError as fromZodError4 } from "zod-validation-error";
12522
11354
  var ImageEditBodySchema = OpenAIImageGenerationInput.extend({
12523
- sessionId: z143.string(),
12524
- questId: z143.string(),
12525
- userId: z143.string(),
12526
- prompt: z143.string(),
12527
- organizationId: z143.string().nullable().optional(),
12528
- safety_tolerance: z143.number().min(BFL_SAFETY_TOLERANCE.MIN).max(BFL_SAFETY_TOLERANCE.MAX).optional().default(BFL_SAFETY_TOLERANCE.DEFAULT),
12529
- prompt_upsampling: z143.boolean().optional().default(false),
12530
- seed: z143.number().nullable().optional(),
12531
- output_format: z143.enum(["jpeg", "png"]).optional().default("png"),
12532
- width: z143.number().optional(),
12533
- height: z143.number().optional(),
12534
- aspect_ratio: z143.string().optional(),
12535
- size: z143.string().optional(),
12536
- fabFileIds: z143.array(z143.string()).optional(),
12537
- image: z143.string()
11355
+ sessionId: z142.string(),
11356
+ questId: z142.string(),
11357
+ userId: z142.string(),
11358
+ prompt: z142.string(),
11359
+ organizationId: z142.string().nullable().optional(),
11360
+ safety_tolerance: z142.number().min(BFL_SAFETY_TOLERANCE.MIN).max(BFL_SAFETY_TOLERANCE.MAX).optional().default(BFL_SAFETY_TOLERANCE.DEFAULT),
11361
+ prompt_upsampling: z142.boolean().optional().default(false),
11362
+ seed: z142.number().nullable().optional(),
11363
+ output_format: z142.enum(["jpeg", "png"]).optional().default("png"),
11364
+ width: z142.number().optional(),
11365
+ height: z142.number().optional(),
11366
+ aspect_ratio: z142.string().optional(),
11367
+ size: z142.string().optional(),
11368
+ fabFileIds: z142.array(z142.string()).optional(),
11369
+ image: z142.string()
12538
11370
  });
12539
11371
 
12540
11372
  // ../../b4m-core/packages/services/dist/src/llm/refineText.js
12541
- import { z as z144 } from "zod";
12542
- var refineTextLLMSchema = z144.object({
12543
- text: z144.string(),
12544
- context: z144.string().optional()
11373
+ import { z as z143 } from "zod";
11374
+ var refineTextLLMSchema = z143.object({
11375
+ text: z143.string(),
11376
+ context: z143.string().optional()
12545
11377
  // tone: z.enum(['formal', 'informal', 'neutral']).optional(),
12546
11378
  // style: z.enum(['technical', 'creative', 'academic', 'business', 'narrative']).optional(),
12547
11379
  });
12548
11380
 
12549
11381
  // ../../b4m-core/packages/services/dist/src/llm/MementoEvaluationService.js
12550
- import { z as z145 } from "zod";
12551
- var SingleMementoEvalSchema = z145.object({
12552
- importance: z145.number().min(1).max(10),
11382
+ import { z as z144 } from "zod";
11383
+ var SingleMementoEvalSchema = z144.object({
11384
+ importance: z144.number().min(1).max(10),
12553
11385
  // 1-10 scale for personal info importance
12554
- summary: z145.string(),
12555
- tags: z145.array(z145.string()).optional()
11386
+ summary: z144.string(),
11387
+ tags: z144.array(z144.string()).optional()
12556
11388
  });
12557
- var MementoEvalResponseSchema = z145.object({
12558
- isPersonal: z145.boolean(),
12559
- mementos: z145.array(SingleMementoEvalSchema).optional()
11389
+ var MementoEvalResponseSchema = z144.object({
11390
+ isPersonal: z144.boolean(),
11391
+ mementos: z144.array(SingleMementoEvalSchema).optional()
12560
11392
  // Array of distinct personal information
12561
11393
  });
12562
11394
 
12563
11395
  // ../../b4m-core/packages/services/dist/src/llm/SmallLLMService.js
12564
- import { z as z146 } from "zod";
11396
+ import { z as z145 } from "zod";
12565
11397
 
12566
11398
  // ../../b4m-core/packages/services/dist/src/auth/AccessTokenGeneratorService.js
12567
11399
  import jwt2 from "jsonwebtoken";
@@ -12677,6 +11509,10 @@ var ServerToolExecutor = class {
12677
11509
  };
12678
11510
 
12679
11511
  // src/llm/ToolRouter.ts
11512
+ var wsToolExecutor = null;
11513
+ function setWebSocketToolExecutor(executor) {
11514
+ wsToolExecutor = executor;
11515
+ }
12680
11516
  var SERVER_TOOLS = ["weather_info", "web_search", "web_fetch"];
12681
11517
  var LOCAL_TOOLS = [
12682
11518
  "file_read",
@@ -12699,7 +11535,15 @@ function isLocalTool(toolName) {
12699
11535
  }
12700
11536
  async function executeTool(toolName, input, apiClient, localToolFn) {
12701
11537
  if (isServerTool(toolName)) {
12702
- logger.debug(`[ToolRouter] Routing ${toolName} to server`);
11538
+ if (wsToolExecutor) {
11539
+ logger.debug(`[ToolRouter] Routing ${toolName} to server via WebSocket`);
11540
+ const result = await wsToolExecutor.execute(toolName, input);
11541
+ if (!result.success) {
11542
+ return `Error executing ${toolName}: ${result.error || "Tool execution failed"}`;
11543
+ }
11544
+ return typeof result.content === "string" ? result.content : JSON.stringify(result.content ?? "");
11545
+ }
11546
+ logger.debug(`[ToolRouter] Routing ${toolName} to server via HTTP`);
12703
11547
  const executor = new ServerToolExecutor(apiClient);
12704
11548
  return await executor.executeTool(toolName, input);
12705
11549
  } else if (isLocalTool(toolName)) {
@@ -12854,7 +11698,7 @@ function buildHookContext(params) {
12854
11698
  }
12855
11699
 
12856
11700
  // src/agents/types.ts
12857
- import { z as z147 } from "zod";
11701
+ import { z as z146 } from "zod";
12858
11702
  var HookBlockedError = class extends Error {
12859
11703
  constructor(toolName, reason) {
12860
11704
  super(`Hook blocked execution of ${toolName}: ${reason || "No reason provided"}`);
@@ -12866,40 +11710,40 @@ var ALWAYS_DENIED_FOR_AGENTS = [
12866
11710
  "agent_delegate"
12867
11711
  // No agent chaining
12868
11712
  ];
12869
- var CommandHookSchema = z147.object({
12870
- type: z147.literal("command"),
12871
- command: z147.string().min(1, "Command is required for command hooks"),
12872
- timeout: z147.number().optional()
11713
+ var CommandHookSchema = z146.object({
11714
+ type: z146.literal("command"),
11715
+ command: z146.string().min(1, "Command is required for command hooks"),
11716
+ timeout: z146.number().optional()
12873
11717
  });
12874
- var PromptHookSchema = z147.object({
12875
- type: z147.literal("prompt"),
12876
- prompt: z147.string().min(1, "Prompt is required for prompt hooks"),
12877
- timeout: z147.number().optional()
11718
+ var PromptHookSchema = z146.object({
11719
+ type: z146.literal("prompt"),
11720
+ prompt: z146.string().min(1, "Prompt is required for prompt hooks"),
11721
+ timeout: z146.number().optional()
12878
11722
  });
12879
- var HookDefinitionSchema = z147.discriminatedUnion("type", [CommandHookSchema, PromptHookSchema]);
12880
- var HookMatcherSchema = z147.object({
12881
- matcher: z147.string().optional(),
12882
- hooks: z147.array(HookDefinitionSchema)
11723
+ var HookDefinitionSchema = z146.discriminatedUnion("type", [CommandHookSchema, PromptHookSchema]);
11724
+ var HookMatcherSchema = z146.object({
11725
+ matcher: z146.string().optional(),
11726
+ hooks: z146.array(HookDefinitionSchema)
12883
11727
  });
12884
- var AgentHooksSchema = z147.object({
12885
- PreToolUse: z147.array(HookMatcherSchema).optional(),
12886
- PostToolUse: z147.array(HookMatcherSchema).optional(),
12887
- PostToolUseFailure: z147.array(HookMatcherSchema).optional(),
12888
- Stop: z147.array(HookMatcherSchema).optional()
11728
+ var AgentHooksSchema = z146.object({
11729
+ PreToolUse: z146.array(HookMatcherSchema).optional(),
11730
+ PostToolUse: z146.array(HookMatcherSchema).optional(),
11731
+ PostToolUseFailure: z146.array(HookMatcherSchema).optional(),
11732
+ Stop: z146.array(HookMatcherSchema).optional()
12889
11733
  }).optional();
12890
- var AgentFrontmatterSchema = z147.object({
12891
- description: z147.string().min(1, "Agent description is required"),
12892
- model: z147.string().optional(),
12893
- "allowed-tools": z147.array(z147.string()).optional(),
12894
- "denied-tools": z147.array(z147.string()).optional(),
12895
- skills: z147.array(z147.string()).optional(),
12896
- "max-iterations": z147.object({
12897
- quick: z147.number().int().positive().optional(),
12898
- medium: z147.number().int().positive().optional(),
12899
- very_thorough: z147.number().int().positive().optional()
11734
+ var AgentFrontmatterSchema = z146.object({
11735
+ description: z146.string().min(1, "Agent description is required"),
11736
+ model: z146.string().optional(),
11737
+ "allowed-tools": z146.array(z146.string()).optional(),
11738
+ "denied-tools": z146.array(z146.string()).optional(),
11739
+ skills: z146.array(z146.string()).optional(),
11740
+ "max-iterations": z146.object({
11741
+ quick: z146.number().int().positive().optional(),
11742
+ medium: z146.number().int().positive().optional(),
11743
+ very_thorough: z146.number().int().positive().optional()
12900
11744
  }).optional(),
12901
- "default-thoroughness": z147.enum(["quick", "medium", "very_thorough"]).optional(),
12902
- variables: z147.record(z147.string()).optional(),
11745
+ "default-thoroughness": z146.enum(["quick", "medium", "very_thorough"]).optional(),
11746
+ variables: z146.record(z146.string()).optional(),
12903
11747
  hooks: AgentHooksSchema
12904
11748
  });
12905
11749
  var DEFAULT_MAX_ITERATIONS = {
@@ -14629,6 +13473,173 @@ var ServerLlmBackend = class {
14629
13473
  }
14630
13474
  };
14631
13475
 
13476
+ // src/llm/WebSocketLlmBackend.ts
13477
+ import { v4 as uuidv411 } from "uuid";
13478
+ function stripThinkingBlocks2(text) {
13479
+ return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
13480
+ }
13481
+ var WebSocketLlmBackend = class {
13482
+ constructor(options) {
13483
+ this.wsManager = options.wsManager;
13484
+ this.apiClient = options.apiClient;
13485
+ this.currentModel = options.model;
13486
+ this.tokenGetter = options.tokenGetter;
13487
+ this.wsCompletionUrl = options.wsCompletionUrl;
13488
+ }
13489
+ /**
13490
+ * Send completion request via HTTP POST, receive streaming response via WebSocket.
13491
+ * Collects all streamed chunks, then calls callback once at completion
13492
+ * with the full accumulated content.
13493
+ */
13494
+ async complete(model, messages, options, callback) {
13495
+ logger.debug(`[WebSocketLlmBackend] Starting complete() with model: ${model}`);
13496
+ if (options.abortSignal?.aborted) {
13497
+ logger.debug("[WebSocketLlmBackend] Request aborted before start");
13498
+ return;
13499
+ }
13500
+ if (!this.wsManager.isConnected) {
13501
+ throw new Error("WebSocket is not connected");
13502
+ }
13503
+ const requestId = uuidv411();
13504
+ return new Promise((resolve3, reject) => {
13505
+ const isVerbose = process.env.B4M_VERBOSE === "1";
13506
+ const isUltraVerbose = process.env.B4M_DEBUG_STREAM === "1";
13507
+ const streamLogger = new StreamLogger(logger, "WebSocketLlmBackend", isVerbose, isUltraVerbose);
13508
+ streamLogger.streamStart();
13509
+ let eventCount = 0;
13510
+ let accumulatedText = "";
13511
+ let lastUsageInfo = {};
13512
+ let toolsUsed = [];
13513
+ let thinkingBlocks = [];
13514
+ let settled = false;
13515
+ const settle = (action) => {
13516
+ if (settled) return;
13517
+ settled = true;
13518
+ this.wsManager.offRequest(requestId);
13519
+ this.wsManager.offDisconnect(onDisconnect);
13520
+ action();
13521
+ };
13522
+ const settleResolve = () => settle(() => resolve3());
13523
+ const settleReject = (err) => settle(() => reject(err));
13524
+ const onDisconnect = () => {
13525
+ logger.debug("[WebSocketLlmBackend] Connection dropped during completion");
13526
+ settleReject(new Error("WebSocket connection lost during completion"));
13527
+ };
13528
+ this.wsManager.onDisconnect(onDisconnect);
13529
+ if (options.abortSignal) {
13530
+ if (options.abortSignal.aborted) {
13531
+ settleResolve();
13532
+ return;
13533
+ }
13534
+ options.abortSignal.addEventListener(
13535
+ "abort",
13536
+ () => {
13537
+ logger.debug("[WebSocketLlmBackend] Abort signal received");
13538
+ settleResolve();
13539
+ },
13540
+ { once: true }
13541
+ );
13542
+ }
13543
+ const updateUsage = (usage) => {
13544
+ if (usage) {
13545
+ lastUsageInfo = { inputTokens: usage.inputTokens, outputTokens: usage.outputTokens };
13546
+ }
13547
+ };
13548
+ this.wsManager.onRequest(requestId, (message) => {
13549
+ if (options.abortSignal?.aborted) return;
13550
+ const action = message.action;
13551
+ if (action === "cli_completion_chunk") {
13552
+ eventCount++;
13553
+ const chunk = message.chunk;
13554
+ streamLogger.onEvent(eventCount, JSON.stringify(chunk));
13555
+ const textChunk = chunk.text || "";
13556
+ if (textChunk) accumulatedText += textChunk;
13557
+ updateUsage(chunk.usage);
13558
+ if (chunk.type === "content") {
13559
+ streamLogger.onContent(eventCount, textChunk, accumulatedText);
13560
+ } else if (chunk.type === "tool_use") {
13561
+ streamLogger.onCriticalEvent(eventCount, "TOOL_USE", `tools: ${chunk.tools?.length}`);
13562
+ if (chunk.tools && chunk.tools.length > 0) toolsUsed = chunk.tools;
13563
+ if (chunk.thinking && chunk.thinking.length > 0) thinkingBlocks = chunk.thinking;
13564
+ }
13565
+ } else if (action === "cli_completion_done") {
13566
+ streamLogger.streamComplete(accumulatedText);
13567
+ const cleanedText = stripThinkingBlocks2(accumulatedText);
13568
+ if (!cleanedText && toolsUsed.length === 0) {
13569
+ settleResolve();
13570
+ return;
13571
+ }
13572
+ const info = {
13573
+ ...lastUsageInfo,
13574
+ ...toolsUsed.length > 0 && { toolsUsed },
13575
+ ...thinkingBlocks.length > 0 && { thinking: thinkingBlocks }
13576
+ };
13577
+ callback([cleanedText], info).then(() => settleResolve()).catch((err) => settleReject(err));
13578
+ } else if (action === "cli_completion_error") {
13579
+ const errorMsg = message.error || "Server error";
13580
+ streamLogger.onCriticalEvent(eventCount, "ERROR", errorMsg);
13581
+ settleReject(new Error(errorMsg));
13582
+ }
13583
+ });
13584
+ const axiosInstance = this.apiClient.getAxiosInstance();
13585
+ axiosInstance.post(
13586
+ this.wsCompletionUrl,
13587
+ {
13588
+ requestId,
13589
+ model,
13590
+ messages,
13591
+ options: {
13592
+ temperature: options.temperature,
13593
+ maxTokens: options.maxTokens,
13594
+ stream: true,
13595
+ tools: options.tools || []
13596
+ }
13597
+ },
13598
+ { signal: options.abortSignal }
13599
+ ).catch((err) => {
13600
+ const msg = err instanceof Error ? err.message : String(err);
13601
+ settleReject(new Error(`HTTP request failed: ${msg}`));
13602
+ });
13603
+ });
13604
+ }
13605
+ /**
13606
+ * Get available models from server (REST call, not streaming).
13607
+ * Delegates to ApiClient -- same as ServerLlmBackend.
13608
+ */
13609
+ async getModelInfo() {
13610
+ try {
13611
+ logger.debug("[WebSocketLlmBackend] Fetching models from /api/models");
13612
+ const response = await this.apiClient.get("/api/models");
13613
+ if (!response || typeof response !== "object" || !Array.isArray(response.models)) {
13614
+ logger.warn("[WebSocketLlmBackend] Invalid API response format, using fallback models");
13615
+ return this.getFallbackModels();
13616
+ }
13617
+ const filteredModels = response.models.filter(
13618
+ (model) => model.type === "text" && model.supportsTools === true
13619
+ );
13620
+ if (filteredModels.length === 0) {
13621
+ logger.warn("[WebSocketLlmBackend] No CLI-compatible models found, using fallback");
13622
+ return this.getFallbackModels();
13623
+ }
13624
+ logger.debug(`[WebSocketLlmBackend] Loaded ${filteredModels.length} models`);
13625
+ return filteredModels;
13626
+ } catch (error) {
13627
+ logger.warn(
13628
+ `[WebSocketLlmBackend] Failed to fetch models: ${error instanceof Error ? error.message : String(error)}`
13629
+ );
13630
+ return this.getFallbackModels();
13631
+ }
13632
+ }
13633
+ getFallbackModels() {
13634
+ return [
13635
+ { id: "claude-sonnet-4-5-20250929", name: "Claude 4.5 Sonnet" },
13636
+ { id: "claude-3-5-haiku-20241022", name: "Claude 3.5 Haiku" },
13637
+ { id: "gpt-4o", name: "GPT-4o" },
13638
+ { id: "gpt-4o-mini", name: "GPT-4o Mini" }
13639
+ ];
13640
+ }
13641
+ };
13642
+
14632
13643
  // src/llm/NotifyingLlmBackend.ts
14633
13644
  var NotifyingLlmBackend = class {
14634
13645
  constructor(inner, backgroundManager) {
@@ -14663,6 +13674,253 @@ Please acknowledge these background agent results and incorporate them into your
14663
13674
  }
14664
13675
  };
14665
13676
 
13677
+ // src/ws/WebSocketConnectionManager.ts
13678
+ var WebSocketConnectionManager = class {
13679
+ constructor(wsUrl, getToken) {
13680
+ this.ws = null;
13681
+ this.heartbeatInterval = null;
13682
+ this.reconnectAttempts = 0;
13683
+ this.maxReconnectDelay = 3e4;
13684
+ this.handlers = /* @__PURE__ */ new Map();
13685
+ this.disconnectHandlers = /* @__PURE__ */ new Set();
13686
+ this.reconnectTimer = null;
13687
+ this.connected = false;
13688
+ this.connecting = false;
13689
+ this.closed = false;
13690
+ this.wsUrl = wsUrl;
13691
+ this.getToken = getToken;
13692
+ }
13693
+ /**
13694
+ * Connect to the WebSocket server.
13695
+ * Resolves when connection is established, rejects on failure.
13696
+ */
13697
+ async connect() {
13698
+ if (this.connected || this.connecting) return;
13699
+ this.connecting = true;
13700
+ const token = await this.getToken();
13701
+ if (!token) {
13702
+ this.connecting = false;
13703
+ throw new Error("No access token available for WebSocket connection");
13704
+ }
13705
+ return new Promise((resolve3, reject) => {
13706
+ logger.debug(`[WS] Connecting to ${this.wsUrl}...`);
13707
+ this.ws = new WebSocket(this.wsUrl, [`access_token.${token}`]);
13708
+ this.ws.onopen = () => {
13709
+ logger.debug("[WS] Connected");
13710
+ this.connected = true;
13711
+ this.connecting = false;
13712
+ this.reconnectAttempts = 0;
13713
+ this.startHeartbeat();
13714
+ resolve3();
13715
+ };
13716
+ this.ws.onmessage = (event) => {
13717
+ try {
13718
+ const data = typeof event.data === "string" ? event.data : event.data.toString();
13719
+ const message = JSON.parse(data);
13720
+ const requestId = message.requestId;
13721
+ if (requestId && this.handlers.has(requestId)) {
13722
+ this.handlers.get(requestId)(message);
13723
+ } else {
13724
+ logger.debug(`[WS] Unhandled message: ${message.action || "unknown"}`);
13725
+ }
13726
+ } catch (err) {
13727
+ logger.debug(`[WS] Failed to parse message: ${err}`);
13728
+ }
13729
+ };
13730
+ this.ws.onclose = () => {
13731
+ logger.debug("[WS] Connection closed");
13732
+ this.cleanup();
13733
+ this.notifyDisconnect();
13734
+ if (!this.closed) {
13735
+ this.scheduleReconnect();
13736
+ }
13737
+ };
13738
+ this.ws.onerror = (err) => {
13739
+ logger.debug(`[WS] Error: ${err}`);
13740
+ if (this.connecting) {
13741
+ this.connecting = false;
13742
+ this.connected = false;
13743
+ reject(new Error("WebSocket connection failed"));
13744
+ }
13745
+ };
13746
+ });
13747
+ }
13748
+ /** Whether the connection is currently established */
13749
+ get isConnected() {
13750
+ return this.connected;
13751
+ }
13752
+ /**
13753
+ * Send a JSON message over the WebSocket connection.
13754
+ */
13755
+ send(data) {
13756
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
13757
+ throw new Error("WebSocket is not connected");
13758
+ }
13759
+ const payload = JSON.stringify(data);
13760
+ const sizeKB = (payload.length / 1024).toFixed(1);
13761
+ logger.debug(`[WS] Sending ${sizeKB} KB (action: ${data.action})`);
13762
+ if (payload.length > 32e3) {
13763
+ logger.warn(`[WS] Payload ${sizeKB} KB exceeds API Gateway 32 KB frame limit \u2014 connection will be closed`);
13764
+ }
13765
+ this.ws.send(payload);
13766
+ }
13767
+ /**
13768
+ * Register a handler for messages matching a specific requestId.
13769
+ */
13770
+ onRequest(requestId, handler) {
13771
+ this.handlers.set(requestId, handler);
13772
+ }
13773
+ /**
13774
+ * Remove a handler for a specific requestId.
13775
+ */
13776
+ offRequest(requestId) {
13777
+ this.handlers.delete(requestId);
13778
+ }
13779
+ /**
13780
+ * Register a handler that fires when the connection drops.
13781
+ */
13782
+ onDisconnect(handler) {
13783
+ this.disconnectHandlers.add(handler);
13784
+ }
13785
+ /**
13786
+ * Remove a disconnect handler.
13787
+ */
13788
+ offDisconnect(handler) {
13789
+ this.disconnectHandlers.delete(handler);
13790
+ }
13791
+ /**
13792
+ * Close the connection and stop all heartbeat/reconnect logic.
13793
+ */
13794
+ disconnect() {
13795
+ this.closed = true;
13796
+ this.cleanup();
13797
+ if (this.ws) {
13798
+ this.ws.close();
13799
+ this.ws = null;
13800
+ }
13801
+ this.handlers.clear();
13802
+ this.disconnectHandlers.clear();
13803
+ }
13804
+ startHeartbeat() {
13805
+ this.stopHeartbeat();
13806
+ this.heartbeatInterval = setInterval(
13807
+ () => {
13808
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
13809
+ this.ws.send(JSON.stringify({ action: "heartbeat" }));
13810
+ logger.debug("[WS] Heartbeat sent");
13811
+ }
13812
+ },
13813
+ 5 * 60 * 1e3
13814
+ );
13815
+ }
13816
+ stopHeartbeat() {
13817
+ if (this.heartbeatInterval) {
13818
+ clearInterval(this.heartbeatInterval);
13819
+ this.heartbeatInterval = null;
13820
+ }
13821
+ }
13822
+ cleanup() {
13823
+ this.connected = false;
13824
+ this.connecting = false;
13825
+ this.stopHeartbeat();
13826
+ if (this.reconnectTimer) {
13827
+ clearTimeout(this.reconnectTimer);
13828
+ this.reconnectTimer = null;
13829
+ }
13830
+ }
13831
+ notifyDisconnect() {
13832
+ for (const handler of this.disconnectHandlers) {
13833
+ try {
13834
+ handler();
13835
+ } catch {
13836
+ }
13837
+ }
13838
+ }
13839
+ scheduleReconnect() {
13840
+ if (this.closed) return;
13841
+ this.reconnectAttempts++;
13842
+ const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts - 1), this.maxReconnectDelay);
13843
+ logger.debug(`[WS] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
13844
+ this.reconnectTimer = setTimeout(async () => {
13845
+ this.reconnectTimer = null;
13846
+ if (this.closed) return;
13847
+ try {
13848
+ await this.connect();
13849
+ } catch {
13850
+ logger.debug("[WS] Reconnection failed");
13851
+ }
13852
+ }, delay);
13853
+ }
13854
+ };
13855
+
13856
+ // src/ws/WebSocketToolExecutor.ts
13857
+ import { v4 as uuidv412 } from "uuid";
13858
+ var WebSocketToolExecutor = class {
13859
+ constructor(wsManager, tokenGetter) {
13860
+ this.wsManager = wsManager;
13861
+ this.tokenGetter = tokenGetter;
13862
+ }
13863
+ /**
13864
+ * Execute a server-side tool via WebSocket.
13865
+ * Returns the tool result or throws on error.
13866
+ */
13867
+ async execute(toolName, input, abortSignal) {
13868
+ if (!this.wsManager.isConnected) {
13869
+ throw new Error("WebSocket is not connected");
13870
+ }
13871
+ const token = await this.tokenGetter();
13872
+ if (!token) {
13873
+ throw new Error("No access token available");
13874
+ }
13875
+ const requestId = uuidv412();
13876
+ return new Promise((resolve3, reject) => {
13877
+ let settled = false;
13878
+ const settle = (action) => {
13879
+ if (settled) return;
13880
+ settled = true;
13881
+ this.wsManager.offRequest(requestId);
13882
+ this.wsManager.offDisconnect(onDisconnect);
13883
+ action();
13884
+ };
13885
+ const settleResolve = (result) => settle(() => resolve3(result));
13886
+ const settleReject = (err) => settle(() => reject(err));
13887
+ const onDisconnect = () => {
13888
+ settleReject(new Error("WebSocket connection lost during tool execution"));
13889
+ };
13890
+ this.wsManager.onDisconnect(onDisconnect);
13891
+ if (abortSignal) {
13892
+ if (abortSignal.aborted) {
13893
+ settleReject(new Error("Tool execution aborted"));
13894
+ return;
13895
+ }
13896
+ abortSignal.addEventListener("abort", () => settleReject(new Error("Tool execution aborted")), {
13897
+ once: true
13898
+ });
13899
+ }
13900
+ this.wsManager.onRequest(requestId, (message) => {
13901
+ if (message.action === "cli_tool_response") {
13902
+ settleResolve({
13903
+ success: message.success,
13904
+ content: message.content,
13905
+ error: message.error
13906
+ });
13907
+ }
13908
+ });
13909
+ try {
13910
+ this.wsManager.send({
13911
+ action: "cli_tool_request",
13912
+ accessToken: token,
13913
+ requestId,
13914
+ toolName,
13915
+ input
13916
+ });
13917
+ } catch (err) {
13918
+ settleReject(err instanceof Error ? err : new Error(String(err)));
13919
+ }
13920
+ });
13921
+ }
13922
+ };
13923
+
14666
13924
  // src/auth/ApiClient.ts
14667
13925
  import axios11 from "axios";
14668
13926
  var ApiClient = class {
@@ -14800,7 +14058,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
14800
14058
  // package.json
14801
14059
  var package_default = {
14802
14060
  name: "@bike4mind/cli",
14803
- version: "0.2.29-feat-quantum-optimize-architecture.18875+70392944c",
14061
+ version: "0.2.29-feat-cli-websocket-streaming.18881+1cfcea5f6",
14804
14062
  type: "module",
14805
14063
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
14806
14064
  license: "UNLICENSED",
@@ -14910,10 +14168,10 @@ var package_default = {
14910
14168
  },
14911
14169
  devDependencies: {
14912
14170
  "@bike4mind/agents": "0.1.0",
14913
- "@bike4mind/common": "2.50.1-feat-quantum-optimize-architecture.18875+70392944c",
14914
- "@bike4mind/mcp": "1.29.1-feat-quantum-optimize-architecture.18875+70392944c",
14915
- "@bike4mind/services": "2.48.1-feat-quantum-optimize-architecture.18875+70392944c",
14916
- "@bike4mind/utils": "2.5.1-feat-quantum-optimize-architecture.18875+70392944c",
14171
+ "@bike4mind/common": "2.50.1-feat-cli-websocket-streaming.18881+1cfcea5f6",
14172
+ "@bike4mind/mcp": "1.29.1-feat-cli-websocket-streaming.18881+1cfcea5f6",
14173
+ "@bike4mind/services": "2.48.1-feat-cli-websocket-streaming.18881+1cfcea5f6",
14174
+ "@bike4mind/utils": "2.5.1-feat-cli-websocket-streaming.18881+1cfcea5f6",
14917
14175
  "@types/better-sqlite3": "^7.6.13",
14918
14176
  "@types/diff": "^5.0.9",
14919
14177
  "@types/jsonwebtoken": "^9.0.4",
@@ -14930,7 +14188,7 @@ var package_default = {
14930
14188
  optionalDependencies: {
14931
14189
  "@vscode/ripgrep": "^1.17.0"
14932
14190
  },
14933
- gitHead: "70392944cc76ee8e8b2a9e9a1a82d42beb183058"
14191
+ gitHead: "1cfcea5f6e4c3d36587681e0d276669b66cdcc4e"
14934
14192
  };
14935
14193
 
14936
14194
  // src/config/constants.ts
@@ -15167,50 +14425,6 @@ ${expandedBody}
15167
14425
  };
15168
14426
  }
15169
14427
 
15170
- // src/core/skillsPrompt.ts
15171
- function getSkillDisplayName(cmd) {
15172
- return cmd.displayName || cmd.name;
15173
- }
15174
- function formatSkillEntry(cmd) {
15175
- const displayName = getSkillDisplayName(cmd);
15176
- const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
15177
- const nameDisplay = cmd.displayName && cmd.displayName !== cmd.name ? `**${displayName}** (\`${cmd.name}\`)` : `**${cmd.name}**`;
15178
- return `- ${nameDisplay}${argHint}: ${cmd.description}
15179
- `;
15180
- }
15181
- function formatSkillGroup(heading, commands) {
15182
- if (commands.length === 0) {
15183
- return "";
15184
- }
15185
- return `
15186
- ### ${heading}
15187
- ${commands.map(formatSkillEntry).join("")}`;
15188
- }
15189
- function filterAIVisibleSkills(commands) {
15190
- return commands.filter((cmd) => !cmd.disableModelInvocation);
15191
- }
15192
- function filterSkillsByAllowedList(commands, allowedSkills) {
15193
- if (!allowedSkills || allowedSkills.length === 0) {
15194
- return commands;
15195
- }
15196
- return commands.filter((cmd) => allowedSkills.includes(cmd.name));
15197
- }
15198
- function buildSkillsPromptSection(commands, allowedSkills) {
15199
- const filteredByAllowed = filterSkillsByAllowedList(commands, allowedSkills);
15200
- const visibleCommands = filterAIVisibleSkills(filteredByAllowed);
15201
- if (visibleCommands.length === 0) {
15202
- return "";
15203
- }
15204
- const projectSkills = visibleCommands.filter((c) => c.source === "project");
15205
- const globalSkills = visibleCommands.filter((c) => c.source === "global");
15206
- return `
15207
-
15208
- ## Available Skills
15209
-
15210
- Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
15211
- ` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
15212
- }
15213
-
15214
14428
  // src/agents/SubagentOrchestrator.ts
15215
14429
  var SubagentOrchestrator = class {
15216
14430
  constructor(deps) {
@@ -15791,6 +15005,21 @@ Describe the expected output format here.
15791
15005
  }
15792
15006
  return { builtin, global, project, total: this.agents.size };
15793
15007
  }
15008
+ /**
15009
+ * Generates a markdown "Phone Book" of available agents and their schemas.
15010
+ * This MUST be injected into the System Prompt of the parent agent.
15011
+ */
15012
+ getDirectoryContext() {
15013
+ if (this.agents.size === 0) {
15014
+ return "No sub-agents are currently available.";
15015
+ }
15016
+ let context = "Use `subagent_delegate` for complex tasks requiring specialized analysis.\n";
15017
+ for (const [name, def] of this.agents) {
15018
+ context += ` - **${name}**: ${def.description}
15019
+ `;
15020
+ }
15021
+ return context;
15022
+ }
15794
15023
  };
15795
15024
 
15796
15025
  // src/agents/delegateTool.ts
@@ -16738,7 +15967,8 @@ function CliApp() {
16738
15967
  agentStore: null,
16739
15968
  abortController: null,
16740
15969
  contextContent: "",
16741
- backgroundManager: null
15970
+ backgroundManager: null,
15971
+ wsManager: null
16742
15972
  });
16743
15973
  const [isInitialized, setIsInitialized] = useState9(false);
16744
15974
  const [initError, setInitError] = useState9(null);
@@ -16766,6 +15996,10 @@ function CliApp() {
16766
15996
  })
16767
15997
  );
16768
15998
  }
15999
+ if (state.wsManager) {
16000
+ state.wsManager.disconnect();
16001
+ setWebSocketToolExecutor(null);
16002
+ }
16769
16003
  if (state.agent) {
16770
16004
  state.agent.removeAllListeners();
16771
16005
  }
@@ -16784,7 +16018,7 @@ function CliApp() {
16784
16018
  setTimeout(() => {
16785
16019
  process.exit(0);
16786
16020
  }, 100);
16787
- }, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
16021
+ }, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore, state.wsManager]);
16788
16022
  useInput9((input, key) => {
16789
16023
  if (key.escape) {
16790
16024
  if (state.abortController) {
@@ -16854,7 +16088,7 @@ function CliApp() {
16854
16088
  if (!isAuthenticated) {
16855
16089
  console.log("\u2139\uFE0F AI features disabled. Available commands: /login, /help, /config\n");
16856
16090
  const minimalSession = {
16857
- id: uuidv411(),
16091
+ id: uuidv413(),
16858
16092
  name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
16859
16093
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
16860
16094
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -16882,10 +16116,45 @@ function CliApp() {
16882
16116
  console.log(`\u{1F30D} API Environment: ${envName} (${apiBaseURL})`);
16883
16117
  }
16884
16118
  const apiClient = new ApiClient(apiBaseURL, state.configStore);
16885
- const llm = new ServerLlmBackend({
16886
- apiClient,
16887
- model: config.defaultModel
16888
- });
16119
+ const tokenGetter = async () => {
16120
+ const tokens = await state.configStore.getAuthTokens();
16121
+ return tokens?.accessToken ?? null;
16122
+ };
16123
+ let wsManager = null;
16124
+ let llm;
16125
+ try {
16126
+ const serverConfig = await apiClient.get(
16127
+ "/api/settings/serverConfig"
16128
+ );
16129
+ const wsUrl = serverConfig?.websocketUrl;
16130
+ const wsCompletionUrl = serverConfig?.wsCompletionUrl;
16131
+ if (wsUrl && wsCompletionUrl) {
16132
+ wsManager = new WebSocketConnectionManager(wsUrl, tokenGetter);
16133
+ await wsManager.connect();
16134
+ const wsToolExecutor2 = new WebSocketToolExecutor(wsManager, tokenGetter);
16135
+ setWebSocketToolExecutor(wsToolExecutor2);
16136
+ llm = new WebSocketLlmBackend({
16137
+ wsManager,
16138
+ apiClient,
16139
+ model: config.defaultModel,
16140
+ tokenGetter,
16141
+ wsCompletionUrl
16142
+ });
16143
+ logger.debug("\u{1F50C} Using WebSocket transport (bypasses CloudFront timeout)");
16144
+ } else {
16145
+ throw new Error("No websocketUrl or wsCompletionUrl in server config");
16146
+ }
16147
+ } catch (wsError) {
16148
+ logger.debug(
16149
+ `[WS] WebSocket unavailable, using SSE fallback: ${wsError instanceof Error ? wsError.message : String(wsError)}`
16150
+ );
16151
+ wsManager = null;
16152
+ setWebSocketToolExecutor(null);
16153
+ llm = new ServerLlmBackend({
16154
+ apiClient,
16155
+ model: config.defaultModel
16156
+ });
16157
+ }
16889
16158
  const models = await llm.getModelInfo();
16890
16159
  if (models.length === 0) {
16891
16160
  throw new Error("No models available from server.");
@@ -16898,7 +16167,7 @@ function CliApp() {
16898
16167
  }
16899
16168
  llm.currentModel = modelInfo.id;
16900
16169
  const newSession = {
16901
- id: uuidv411(),
16170
+ id: uuidv413(),
16902
16171
  name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
16903
16172
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
16904
16173
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -17048,18 +16317,12 @@ function CliApp() {
17048
16317
  for (const error of contextResult.errors) {
17049
16318
  startupLog.push(`\u26A0\uFE0F Context file error: ${error}`);
17050
16319
  }
17051
- let contextSection = contextResult.mergedContent ? `
17052
-
17053
- ## Project Context
17054
-
17055
- Follow these project-specific instructions:
17056
-
17057
- ${contextResult.mergedContent}` : "";
17058
- if (enableSkillTool) {
17059
- const commands = state.customCommandStore.getAllCommands();
17060
- contextSection += buildSkillsPromptSection(commands);
17061
- }
17062
- const cliSystemPrompt = buildCoreSystemPrompt(contextSection);
16320
+ const cliSystemPrompt = buildCoreSystemPrompt({
16321
+ contextContent: contextResult.mergedContent,
16322
+ agentStore,
16323
+ customCommands: state.customCommandStore.getAllCommands(),
16324
+ enableSkillTool
16325
+ });
17063
16326
  const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
17064
16327
  const agent = new ReActAgent({
17065
16328
  userId: config.userId,
@@ -17118,8 +16381,10 @@ ${contextResult.mergedContent}` : "";
17118
16381
  // Store agent store for agent management commands
17119
16382
  contextContent: contextResult.mergedContent,
17120
16383
  // Store raw context for compact instructions
17121
- backgroundManager
16384
+ backgroundManager,
17122
16385
  // Store for grouped notification turn tracking
16386
+ wsManager
16387
+ // WebSocket connection manager (null if using SSE fallback)
17123
16388
  }));
17124
16389
  setStoreSession(newSession);
17125
16390
  const bannerLines = [
@@ -17214,13 +16479,13 @@ ${contextResult.mergedContent}` : "";
17214
16479
  messageContent = multimodalMessage.content;
17215
16480
  }
17216
16481
  const userMessage = {
17217
- id: uuidv411(),
16482
+ id: uuidv413(),
17218
16483
  role: "user",
17219
16484
  content: userMessageContent,
17220
16485
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
17221
16486
  };
17222
16487
  const pendingAssistantMessage = {
17223
- id: uuidv411(),
16488
+ id: uuidv413(),
17224
16489
  role: "assistant",
17225
16490
  content: "...",
17226
16491
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -17389,7 +16654,12 @@ ${contextResult.mergedContent}` : "";
17389
16654
  const tokenCounter2 = getTokenCounter();
17390
16655
  const contextWindow = tokenCounter2.getContextWindow(activeSession.model, state.availableModels);
17391
16656
  const threshold = contextWindow * 0.8;
17392
- const systemPrompt = buildCoreSystemPrompt(state.contextContent || "");
16657
+ const systemPrompt = buildCoreSystemPrompt({
16658
+ contextContent: state.contextContent,
16659
+ agentStore: state.agentStore || void 0,
16660
+ customCommands: state.customCommandStore.getAllCommands(),
16661
+ enableSkillTool: config?.preferences.enableSkillTool !== false
16662
+ });
17393
16663
  const contextUsage = tokenCounter2.countSessionTokens(activeSession, systemPrompt);
17394
16664
  if (contextUsage.totalTokens >= threshold) {
17395
16665
  console.log("\n\u26A0\uFE0F Context window 80% full. Auto-compacting...\n");
@@ -17426,13 +16696,13 @@ ${contextResult.mergedContent}` : "";
17426
16696
  userMessageContent = message;
17427
16697
  }
17428
16698
  const userMessage = {
17429
- id: uuidv411(),
16699
+ id: uuidv413(),
17430
16700
  role: "user",
17431
16701
  content: userMessageContent,
17432
16702
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
17433
16703
  };
17434
16704
  const pendingAssistantMessage = {
17435
- id: uuidv411(),
16705
+ id: uuidv413(),
17436
16706
  role: "assistant",
17437
16707
  content: "...",
17438
16708
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -17510,7 +16780,7 @@ ${contextResult.mergedContent}` : "";
17510
16780
  const currentSession = useCliStore.getState().session;
17511
16781
  if (currentSession) {
17512
16782
  const cancelMessage = {
17513
- id: uuidv411(),
16783
+ id: uuidv413(),
17514
16784
  role: "assistant",
17515
16785
  content: "\u26A0\uFE0F Operation cancelled by user",
17516
16786
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -17555,7 +16825,7 @@ ${contextResult.mergedContent}` : "";
17555
16825
  setState((prev) => ({ ...prev, abortController }));
17556
16826
  try {
17557
16827
  const pendingAssistantMessage = {
17558
- id: uuidv411(),
16828
+ id: uuidv413(),
17559
16829
  role: "assistant",
17560
16830
  content: "...",
17561
16831
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -17583,7 +16853,7 @@ ${contextResult.mergedContent}` : "";
17583
16853
  const currentSession = useCliStore.getState().session;
17584
16854
  if (!currentSession) return;
17585
16855
  const continuationMessage = {
17586
- id: uuidv411(),
16856
+ id: uuidv413(),
17587
16857
  role: "assistant",
17588
16858
  content: "---\n\n**Background Agent Results:**\n\n" + result.finalAnswer,
17589
16859
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -17642,13 +16912,13 @@ ${contextResult.mergedContent}` : "";
17642
16912
  isError = true;
17643
16913
  }
17644
16914
  const userMessage = {
17645
- id: uuidv411(),
16915
+ id: uuidv413(),
17646
16916
  role: "user",
17647
16917
  content: `$ ${command}`,
17648
16918
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
17649
16919
  };
17650
16920
  const assistantMessage = {
17651
- id: uuidv411(),
16921
+ id: uuidv413(),
17652
16922
  role: "assistant",
17653
16923
  content: isError ? `\u274C Error:
17654
16924
  ${output}` : output.trim() || "(no output)",
@@ -18117,7 +17387,7 @@ Keyboard Shortcuts:
18117
17387
  console.clear();
18118
17388
  const model = state.session?.model || state.config?.defaultModel || "claude-sonnet";
18119
17389
  const newSession = {
18120
- id: uuidv411(),
17390
+ id: uuidv413(),
18121
17391
  name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
18122
17392
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
18123
17393
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -18307,15 +17577,21 @@ No usage data available for the last ${USAGE_DAYS} days.`);
18307
17577
  }
18308
17578
  const tokenCounter2 = getTokenCounter();
18309
17579
  const contextWindow = tokenCounter2.getContextWindow(state.session.model, state.availableModels);
18310
- const corePromptTokens = tokenCounter2.countTokens(buildCoreSystemPrompt(""));
17580
+ const corePromptTokens = tokenCounter2.countTokens(buildCoreSystemPrompt());
18311
17581
  const projectContextTokens = state.contextContent ? tokenCounter2.countTokens(state.contextContent) : 0;
18312
17582
  const commands = state.customCommandStore.getAllCommands();
18313
17583
  const skillsSection = buildSkillsPromptSection(commands);
18314
17584
  const skillsTokens = skillsSection ? tokenCounter2.countTokens(skillsSection) : 0;
17585
+ const agentDirectoryTokens = state.agentStore ? tokenCounter2.countTokens(state.agentStore.getDirectoryContext()) : 0;
18315
17586
  const mcpTools = state.mcpManager?.getTools() || [];
18316
17587
  const mcpToolsTokens = tokenCounter2.countToolSchemaTokens(mcpTools);
18317
17588
  const mcpToolCount = state.mcpManager?.getToolCount() || [];
18318
- const systemPrompt = buildCoreSystemPrompt(state.contextContent || "");
17589
+ const systemPrompt = buildCoreSystemPrompt({
17590
+ contextContent: state.contextContent,
17591
+ agentStore: state.agentStore || void 0,
17592
+ customCommands: commands,
17593
+ enableSkillTool: state.config?.preferences.enableSkillTool !== false
17594
+ });
18319
17595
  const usage = tokenCounter2.countSessionTokens(state.session, systemPrompt);
18320
17596
  const totalWithTools = usage.totalTokens + mcpToolsTokens;
18321
17597
  const usagePercent = totalWithTools / contextWindow * 100;
@@ -18334,6 +17610,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
18334
17610
  if (commands.length > 0) {
18335
17611
  console.log(` Skills Section: ${skillsTokens.toLocaleString()} tokens (${commands.length} skills)`);
18336
17612
  }
17613
+ if (agentDirectoryTokens > 0) {
17614
+ const agentCount = state.agentStore?.getAgentCount() || 0;
17615
+ console.log(` Agent Directory: ${agentDirectoryTokens.toLocaleString()} tokens (${agentCount} agents)`);
17616
+ }
18337
17617
  if (mcpTools.length > 0) {
18338
17618
  console.log("\nMCP Tools:");
18339
17619
  console.log(` Total: ${mcpToolsTokens.toLocaleString()} tokens (${mcpTools.length} tools)`);
@@ -18612,9 +17892,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
18612
17892
  return { ...prev, config: updatedConfig };
18613
17893
  });
18614
17894
  if (modelChanged && state.agent) {
18615
- const llm = state.agent.context.llm;
18616
- if (llm) {
18617
- llm.currentModel = updatedConfig.defaultModel;
17895
+ const backend = state.agent.context.llm;
17896
+ if (backend) {
17897
+ backend.currentModel = updatedConfig.defaultModel;
18618
17898
  }
18619
17899
  }
18620
17900
  };