@bike4mind/cli 0.2.28-slack-native-search.18717 → 0.2.28

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-YY4HHPYV.js";
8
+ } from "./chunk-VGYTNJXN.js";
9
9
  import {
10
10
  ConfigStore
11
11
  } from "./chunk-23T2XGSZ.js";
@@ -13,8 +13,8 @@ import {
13
13
  selectActiveBackgroundAgents,
14
14
  useCliStore
15
15
  } from "./chunk-TVW4ZESU.js";
16
- import "./chunk-5VZSKEEG.js";
17
- import "./chunk-5ZYPV3TT.js";
16
+ import "./chunk-JJBDHUGY.js";
17
+ import "./chunk-ZEMWV6IR.js";
18
18
  import {
19
19
  BFLImageService,
20
20
  BaseStorage,
@@ -26,7 +26,7 @@ import {
26
26
  OpenAIBackend,
27
27
  OpenAIImageService,
28
28
  XAIImageService
29
- } from "./chunk-JQOZTED2.js";
29
+ } from "./chunk-UNOJBVD2.js";
30
30
  import {
31
31
  AiEvents,
32
32
  ApiKeyEvents,
@@ -82,7 +82,7 @@ import {
82
82
  XAI_IMAGE_MODELS,
83
83
  b4mLLMTools,
84
84
  getMcpProviderMetadata
85
- } from "./chunk-F3HPUK2I.js";
85
+ } from "./chunk-XJRPAAUS.js";
86
86
  import {
87
87
  Logger
88
88
  } from "./chunk-OCYRD7D6.js";
@@ -6080,7 +6080,10 @@ var imageGenerationTool = {
6080
6080
  return "Error: Image configuration is required";
6081
6081
  }
6082
6082
  const { prompt, n: toolN, quality: toolQuality, size: toolSize, safety_tolerance: toolSafetyTolerance } = val;
6083
- const model = imageConfig?.model || ImageModels.GPT_IMAGE_1;
6083
+ let model = imageConfig?.model || ImageModels.GPT_IMAGE_1_5;
6084
+ if (model === ImageModels.GPT_IMAGE_1) {
6085
+ model = ImageModels.GPT_IMAGE_1_5;
6086
+ }
6084
6087
  const n = toolN ?? imageConfig?.n ?? 1;
6085
6088
  const quality = imageConfig?.quality || toolQuality;
6086
6089
  const size = imageConfig?.size || toolSize;
@@ -7331,6 +7334,14 @@ Return only the edited content without any markdown code blocks or explanations.
7331
7334
  import axios7 from "axios";
7332
7335
  import { fileTypeFromBuffer as fileTypeFromBuffer3 } from "file-type";
7333
7336
  import { v4 as uuidv46 } from "uuid";
7337
+ var EDIT_SUPPORTED_MODELS = [
7338
+ ImageModels.GPT_IMAGE_1,
7339
+ ImageModels.GPT_IMAGE_1_5,
7340
+ ImageModels.GPT_IMAGE_1_MINI,
7341
+ ImageModels.FLUX_PRO_FILL,
7342
+ ImageModels.GEMINI_2_5_FLASH_IMAGE,
7343
+ ImageModels.GEMINI_3_PRO_IMAGE_PREVIEW
7344
+ ];
7334
7345
  async function downloadImage2(url) {
7335
7346
  if (url.startsWith("data:image/")) {
7336
7347
  const base64Data = url.split(",")[1];
@@ -7355,7 +7366,7 @@ async function imageUrlToBase64(imageUrl) {
7355
7366
  return buffer.toString("base64");
7356
7367
  }
7357
7368
  async function getImageFromFileId(fileId, context) {
7358
- const fabFile = await context.db.fabFiles?.findById(fileId);
7369
+ const fabFile = await context.db.fabfiles?.findById(fileId);
7359
7370
  if (!fabFile) {
7360
7371
  throw new NotFoundError(`File with ID ${fileId} not found`);
7361
7372
  }
@@ -7378,6 +7389,24 @@ async function processAndStoreImage(imageUrl, context) {
7378
7389
  const path18 = await context.imageGenerateStorage.upload(buffer, filename, {});
7379
7390
  return path18;
7380
7391
  }
7392
+ async function generateFullMask(imageBuffer) {
7393
+ const sharp = (await import("sharp")).default;
7394
+ const metadata = await sharp(imageBuffer).metadata();
7395
+ const width = metadata.width;
7396
+ const height = metadata.height;
7397
+ const maskData = Buffer.alloc(width * height * 4);
7398
+ for (let i = 0; i < maskData.length; i++) {
7399
+ maskData[i] = 255;
7400
+ }
7401
+ const maskBuffer = await sharp(maskData, {
7402
+ raw: {
7403
+ width,
7404
+ height,
7405
+ channels: 4
7406
+ }
7407
+ }).png().toBuffer();
7408
+ return maskBuffer.toString("base64");
7409
+ }
7381
7410
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
7382
7411
  await context.onFinish?.("edit_image", storedImagePath);
7383
7412
  await context.statusUpdate({ images: [storedImagePath] });
@@ -7389,14 +7418,32 @@ var imageEditTool = {
7389
7418
  toolFn: async (val) => {
7390
7419
  const imageConfig = config;
7391
7420
  const { image: toolImage, prompt, mask: toolMask, n: toolN, size: toolSize, safety_tolerance: toolSafetyTolerance, steps: toolSteps, guidance: toolGuidance } = val;
7392
- console.log(toolImage, "Image to edit");
7393
7421
  if (!toolImage) {
7394
7422
  return "Error: Image is required for editing";
7395
7423
  }
7396
7424
  if (!prompt) {
7397
7425
  return "Error: Prompt is required for editing";
7398
7426
  }
7399
- const model = imageConfig?.model || ImageModels.GPT_IMAGE_1;
7427
+ const generationModel = imageConfig?.model || ImageModels.GPT_IMAGE_1_5;
7428
+ let editModel = imageConfig?.editModel;
7429
+ if (!editModel) {
7430
+ const isGenBFLModel = BFL_IMAGE_MODELS.includes(generationModel);
7431
+ const isGenGeminiModel = GEMINI_IMAGE_MODELS.includes(generationModel);
7432
+ if (isGenBFLModel) {
7433
+ editModel = ImageModels.FLUX_PRO_FILL;
7434
+ } else if (isGenGeminiModel) {
7435
+ editModel = generationModel;
7436
+ } else {
7437
+ editModel = ImageModels.GPT_IMAGE_1_5;
7438
+ }
7439
+ console.log(`[DEBUG] No edit model specified, using fallback: ${editModel} (based on generation model: ${generationModel})`);
7440
+ }
7441
+ if (!EDIT_SUPPORTED_MODELS.includes(editModel)) {
7442
+ return `Error: Model ${editModel} does not support image editing. Supported models for editing: ${EDIT_SUPPORTED_MODELS.join(", ")}.
7443
+
7444
+ Please select a supported edit model in your image settings modal.`;
7445
+ }
7446
+ const model = generationModel;
7400
7447
  const n = toolN ?? imageConfig?.n ?? 1;
7401
7448
  const size = imageConfig?.size || toolSize;
7402
7449
  const safety_tolerance = imageConfig?.safety_tolerance || toolSafetyTolerance;
@@ -7405,8 +7452,8 @@ var imageEditTool = {
7405
7452
  const seed = imageConfig?.seed;
7406
7453
  const steps = toolSteps || 50;
7407
7454
  const guidance = toolGuidance || 60;
7408
- const isBFLModel = BFL_IMAGE_MODELS.includes(model);
7409
- const isGeminiModel = GEMINI_IMAGE_MODELS.includes(model);
7455
+ const isBFLModel = BFL_IMAGE_MODELS.includes(editModel);
7456
+ const isGeminiModel = GEMINI_IMAGE_MODELS.includes(editModel);
7410
7457
  await context.onStart?.("edit_image", {
7411
7458
  model,
7412
7459
  n,
@@ -7433,67 +7480,135 @@ var imageEditTool = {
7433
7480
  }
7434
7481
  if (isBFLModel) {
7435
7482
  const apiKey = await getEffectiveApiKey(context.userId, { type: ApiKeyType.bfl }, { db: context.db });
7436
- const service = new BFLImageService(apiKey, context.logger);
7437
- const bflModel = ImageModels.FLUX_PRO_FILL;
7483
+ const service = new BFLImageService(apiKey, context.logger, context.imageProcessorLambdaName);
7484
+ let bflModel = editModel;
7485
+ if (bflModel !== ImageModels.FLUX_PRO_FILL) {
7486
+ console.warn(`[WARN] BFL edit model ${bflModel} does not support editing. Falling back to FLUX_PRO_FILL.`);
7487
+ bflModel = ImageModels.FLUX_PRO_FILL;
7488
+ }
7438
7489
  if (!maskBase64Image) {
7439
- return "Error: Mask image is required for BFL image editing";
7490
+ const sourceBuffer = Buffer.from(sourceBase64Image, "base64");
7491
+ maskBase64Image = await generateFullMask(sourceBuffer);
7440
7492
  }
7441
- const editResponse = await service.edit(sourceBase64Image, prompt, {
7442
- mask: maskBase64Image,
7443
- model: bflModel,
7444
- safety_tolerance: safety_tolerance ?? BFL_SAFETY_TOLERANCE.DEFAULT,
7445
- prompt_upsampling,
7446
- seed: seed ?? void 0,
7447
- output_format: output_format ?? "jpeg",
7448
- steps,
7449
- guidance
7450
- });
7451
- if (editResponse.type === "success") {
7452
- const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7453
- return updateQuestAndReturnMarkdown2(storedImagePath, context);
7493
+ try {
7494
+ const editResponse = await service.edit(sourceBase64Image, prompt, {
7495
+ mask: maskBase64Image,
7496
+ model: bflModel,
7497
+ safety_tolerance: safety_tolerance ?? BFL_SAFETY_TOLERANCE.DEFAULT,
7498
+ prompt_upsampling,
7499
+ seed: seed ?? void 0,
7500
+ output_format: output_format ?? "jpeg",
7501
+ steps,
7502
+ guidance
7503
+ });
7504
+ if (editResponse.type === "success") {
7505
+ const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7506
+ return updateQuestAndReturnMarkdown2(storedImagePath, context);
7507
+ }
7508
+ return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7509
+ } catch (error) {
7510
+ console.error("[ERROR] BFL image editing failed:", error);
7511
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
7512
+ if (axios7.isAxiosError(error)) {
7513
+ const status = error.response?.status;
7514
+ const responseData = error.response?.data;
7515
+ if (status === 403) {
7516
+ return `Error: BFL API access denied (403). This usually means:
7517
+ - Your BFL API key is not set, invalid, or expired
7518
+ - Your BFL account lacks permissions for FLUX-PRO-FILL
7519
+ - Your BFL API credits have run out
7520
+
7521
+ Please check your BFL API key in settings and ensure it is configured correctly.`;
7522
+ } else if (status === 429) {
7523
+ return `Error: BFL API rate limit exceeded (429). Please wait a moment and try again.`;
7524
+ } else if (status === 402) {
7525
+ return `Error: BFL API payment required (402). Please add credits to your BFL account.`;
7526
+ } else if (responseData?.error) {
7527
+ return `Error: BFL API error - ${responseData.error}`;
7528
+ }
7529
+ }
7530
+ return `Error: Failed to edit image with FLUX-PRO-FILL. ${errorMessage}. Please try a different edit model like GPT-Image-1.5 or Gemini.`;
7454
7531
  }
7455
- return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7456
7532
  } else if (isGeminiModel) {
7457
7533
  const apiKey = await getEffectiveApiKey(context.userId, { type: ApiKeyType.gemini }, { db: context.db });
7458
- const service = new GeminiImageService(apiKey, context.logger);
7534
+ const service = new GeminiImageService(apiKey, context.logger, context.imageProcessorLambdaName);
7459
7535
  const dataUrlImage = sourceBase64Image.startsWith("data:") ? sourceBase64Image : `data:image/png;base64,${sourceBase64Image}`;
7460
- const editResponse = await service.edit(dataUrlImage, prompt, {
7461
- aspect_ratio: imageConfig?.aspect_ratio,
7462
- output_format: output_format ?? "png",
7463
- safety_tolerance
7464
- });
7465
- if (editResponse.type === "success") {
7466
- const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7467
- return updateQuestAndReturnMarkdown2(storedImagePath, context);
7536
+ try {
7537
+ const editResponse = await service.edit(dataUrlImage, prompt, {
7538
+ aspect_ratio: imageConfig?.aspect_ratio,
7539
+ output_format: output_format ?? "png",
7540
+ safety_tolerance,
7541
+ model: editModel
7542
+ // Pass edit model to service
7543
+ });
7544
+ if (editResponse.type === "success") {
7545
+ const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7546
+ return updateQuestAndReturnMarkdown2(storedImagePath, context);
7547
+ }
7548
+ return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7549
+ } catch (error) {
7550
+ console.error("[ERROR] Gemini image editing failed:", error);
7551
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
7552
+ if (axios7.isAxiosError(error)) {
7553
+ const status = error.response?.status;
7554
+ const responseData = error.response?.data;
7555
+ if (status === 403 || status === 401) {
7556
+ return `Error: Gemini API access denied (${status}). Your Gemini API key may be missing, invalid, or expired. Please check your Gemini API key in settings.`;
7557
+ } else if (status === 429) {
7558
+ return `Error: Gemini API rate limit exceeded (429). Please wait a moment and try again.`;
7559
+ } else if (responseData?.error) {
7560
+ return `Error: Gemini API error - ${JSON.stringify(responseData.error)}`;
7561
+ }
7562
+ }
7563
+ return `Error: Failed to edit image with Gemini. ${errorMessage}. Please try a different edit model like GPT-Image-1.5.`;
7468
7564
  }
7469
- return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7470
7565
  } else {
7471
7566
  const apiKey = await getEffectiveApiKey(context.userId, { type: ApiKeyType.openai }, { db: context.db });
7472
- const service = new OpenAIImageService(apiKey, context.logger);
7473
- const editResponse = await service.edit(sourceBase64Image, prompt, {
7474
- mask: maskBase64Image,
7475
- model: ImageModels.GPT_IMAGE_1,
7476
- n,
7477
- size,
7478
- response_format: "url",
7479
- user: context.userId
7480
- });
7481
- if (editResponse.type === "success") {
7482
- const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7483
- return updateQuestAndReturnMarkdown2(storedImagePath, context);
7567
+ const service = new OpenAIImageService(apiKey, context.logger, context.imageProcessorLambdaName);
7568
+ try {
7569
+ const editResponse = await service.edit(sourceBase64Image, prompt, {
7570
+ mask: maskBase64Image,
7571
+ model: editModel,
7572
+ // Use the configured edit model
7573
+ n,
7574
+ size,
7575
+ response_format: "url",
7576
+ user: context.userId
7577
+ });
7578
+ if (editResponse.type === "success") {
7579
+ const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7580
+ return updateQuestAndReturnMarkdown2(storedImagePath, context);
7581
+ }
7582
+ return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7583
+ } catch (error) {
7584
+ console.error("[ERROR] OpenAI image editing failed:", error);
7585
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
7586
+ if (axios7.isAxiosError(error)) {
7587
+ const status = error.response?.status;
7588
+ const responseData = error.response?.data;
7589
+ if (status === 403 || status === 401) {
7590
+ return `Error: OpenAI API access denied (${status}). Your OpenAI API key may be missing, invalid, or expired. Please check your OpenAI API key in settings.`;
7591
+ } else if (status === 429) {
7592
+ return `Error: OpenAI API rate limit exceeded (429). Please wait a moment and try again.`;
7593
+ } else if (status === 402) {
7594
+ return `Error: OpenAI API payment required (402). Please add credits to your OpenAI account.`;
7595
+ } else if (responseData?.error) {
7596
+ return `Error: OpenAI API error - ${responseData.error.message || JSON.stringify(responseData.error)}`;
7597
+ }
7598
+ }
7599
+ return `Error: Failed to edit image with OpenAI. ${errorMessage}. Please try again or use a different edit model.`;
7484
7600
  }
7485
- return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7486
7601
  }
7487
7602
  },
7488
7603
  toolSchema: {
7489
7604
  name: "edit_image",
7490
- description: "Edit an existing image based on a prompt. Can optionally use a mask image to specify which areas to edit.",
7605
+ description: "Edit an existing image based on a text prompt. Use this when the user wants to modify, change, edit, or transform an image they have uploaded or attached to the conversation. Supports removing backgrounds, changing colors, adding/removing objects, and other visual modifications.",
7491
7606
  parameters: {
7492
7607
  type: "object",
7493
7608
  properties: {
7494
7609
  image: {
7495
7610
  type: "string",
7496
- description: "The image to edit. This should be a FabFile S3 URL."
7611
+ description: 'The file ID of the image to edit (e.g., "67abc123def456"). Look for image file IDs in the "Available Files" system message. You can also use an S3 URL or data URL if provided by the user.'
7497
7612
  },
7498
7613
  prompt: {
7499
7614
  type: "string",
@@ -10716,7 +10831,7 @@ var cliOnlyTools = {
10716
10831
  // Git operations
10717
10832
  recent_changes: recentChangesTool
10718
10833
  };
10719
- var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, tools = b4mTools) => {
10834
+ var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools = b4mTools) => {
10720
10835
  const context = {
10721
10836
  userId,
10722
10837
  user,
@@ -10728,7 +10843,8 @@ var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorag
10728
10843
  onStart,
10729
10844
  onFinish,
10730
10845
  llm,
10731
- model
10846
+ model,
10847
+ imageProcessorLambdaName
10732
10848
  };
10733
10849
  return Object.entries(tools).reduce((acc, [key, tool]) => ({
10734
10850
  ...acc,
@@ -12062,6 +12178,8 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
12062
12178
  llm,
12063
12179
  {},
12064
12180
  model,
12181
+ void 0,
12182
+ // imageProcessorLambdaName (not needed for CLI)
12065
12183
  tools_to_generate
12066
12184
  );
12067
12185
  let tools = Object.entries(toolsMap).map(
@@ -13208,227 +13326,220 @@ var ServerLlmBackend = class {
13208
13326
  logger.debug("[ServerLlmBackend] Request aborted before start");
13209
13327
  return;
13210
13328
  }
13211
- return new Promise(async (resolve3, reject) => {
13212
- try {
13213
- logger.debug("[ServerLlmBackend] Making streaming request...");
13214
- const response = await this.makeStreamingRequest(model, messages, options);
13215
- logger.debug("[ServerLlmBackend] Got response, setting up SSE parser");
13216
- const isVerbose = process.env.B4M_VERBOSE === "1";
13217
- const isUltraVerbose = process.env.B4M_DEBUG_STREAM === "1";
13218
- const streamLogger = new StreamLogger(logger, "ServerLlmBackend", isVerbose, isUltraVerbose);
13219
- streamLogger.streamStart();
13220
- let eventCount = 0;
13221
- let accumulatedText = "";
13222
- let lastUsageInfo = {};
13223
- let toolsUsed = [];
13224
- let thinkingBlocks = [];
13225
- let receivedDone = false;
13226
- const parser = createParser({
13227
- onEvent: (event) => {
13228
- eventCount++;
13229
- streamLogger.onEvent(eventCount, event.data || "");
13230
- const data = event.data;
13231
- if (data === "[DONE]") {
13232
- receivedDone = true;
13233
- streamLogger.onCriticalEvent(eventCount, "[DONE]", `accumulated text length: ${accumulatedText.length}`);
13234
- const cleanedText = stripThinkingBlocks(accumulatedText);
13235
- streamLogger.streamComplete(accumulatedText);
13236
- if (toolsUsed.length > 0) {
13237
- const info = {
13238
- toolsUsed,
13239
- thinking: thinkingBlocks.length > 0 ? thinkingBlocks : void 0,
13240
- ...lastUsageInfo
13241
- };
13242
- logger.debug(
13243
- `[ServerLlmBackend] Calling callback with tools, thinking blocks: ${thinkingBlocks.length}`
13244
- );
13245
- callback([cleanedText], info).catch((err) => {
13246
- logger.error("[ServerLlmBackend] Callback error:", err);
13247
- reject(err);
13248
- }).then(() => {
13249
- logger.debug("[ServerLlmBackend] Callback completed, resolving");
13250
- resolve3();
13251
- });
13252
- } else if (cleanedText) {
13253
- callback([cleanedText], lastUsageInfo).catch((err) => {
13254
- logger.error("[ServerLlmBackend] Callback error:", err);
13255
- reject(err);
13256
- }).then(() => resolve3());
13257
- } else {
13258
- resolve3();
13329
+ logger.debug("[ServerLlmBackend] Making streaming request...");
13330
+ let response;
13331
+ try {
13332
+ response = await this.makeStreamingRequest(model, messages, options);
13333
+ } catch (error) {
13334
+ if (options.abortSignal?.aborted) {
13335
+ logger.debug("[ServerLlmBackend] Request was aborted, resolving gracefully");
13336
+ return;
13337
+ }
13338
+ if (isAxiosError(error) && error.code === "ERR_CANCELED") {
13339
+ logger.debug("[ServerLlmBackend] Request was canceled, resolving gracefully");
13340
+ return;
13341
+ }
13342
+ logger.error("LLM completion failed", error);
13343
+ if (isAxiosError(error)) {
13344
+ logger.debug(
13345
+ `[ServerLlmBackend] Axios error details: ${JSON.stringify({
13346
+ status: error.response?.status,
13347
+ statusText: error.response?.statusText,
13348
+ url: error.config?.url,
13349
+ method: error.config?.method
13350
+ })}`
13351
+ );
13352
+ if (error.response?.status === 403 && error.response.data) {
13353
+ let errorDetails = "";
13354
+ try {
13355
+ let responseText = "";
13356
+ const stream = error.response.data;
13357
+ if (Buffer.isBuffer(stream)) {
13358
+ responseText = stream.toString("utf-8");
13359
+ } else if (stream?._readableState?.buffer?.length > 0) {
13360
+ const chunks = [];
13361
+ for (const chunk of stream._readableState.buffer) {
13362
+ if (chunk?.data) {
13363
+ chunks.push(Buffer.from(chunk.data));
13364
+ }
13259
13365
  }
13260
- return;
13366
+ responseText = Buffer.concat(chunks).toString("utf-8");
13367
+ } else if (typeof stream === "string") {
13368
+ responseText = stream;
13261
13369
  }
13262
- try {
13263
- const parsed = JSON.parse(data);
13264
- if (parsed.type === "error") {
13265
- streamLogger.onCriticalEvent(eventCount, "ERROR", parsed.message || "Server error");
13266
- reject(new Error(parsed.message || "Server error"));
13267
- return;
13268
- }
13269
- if (parsed.type === "content") {
13270
- const textChunk = parsed.text || "";
13271
- accumulatedText += textChunk;
13272
- if (parsed.usage) {
13273
- lastUsageInfo = {
13274
- inputTokens: parsed.usage.inputTokens,
13275
- outputTokens: parsed.usage.outputTokens
13276
- };
13277
- }
13278
- streamLogger.onContent(eventCount, textChunk, accumulatedText);
13279
- } else if (parsed.type === "tool_use") {
13280
- streamLogger.onCriticalEvent(eventCount, "TOOL_USE", `tools: ${parsed.tools?.length}`);
13281
- if (parsed.tools && parsed.tools.length > 0) {
13282
- for (const tool of parsed.tools) {
13283
- logger.debug(`TOOL REQUEST: ${tool.name}`);
13284
- try {
13285
- const paramsStr = JSON.stringify(tool.input);
13286
- logger.debug(` Params: ${paramsStr}`);
13287
- } catch {
13288
- logger.debug(` Params: [Unable to stringify]`);
13289
- }
13290
- }
13291
- }
13292
- const textChunk = parsed.text || "";
13293
- if (textChunk) {
13294
- accumulatedText += textChunk;
13295
- }
13296
- if (parsed.tools && parsed.tools.length > 0) {
13297
- toolsUsed = parsed.tools;
13298
- }
13299
- if (parsed.thinking && parsed.thinking.length > 0) {
13300
- thinkingBlocks = parsed.thinking;
13301
- streamLogger.onCriticalEvent(eventCount, "THINKING", `${thinkingBlocks.length} thinking blocks`);
13302
- }
13303
- if (parsed.usage) {
13304
- lastUsageInfo = {
13305
- inputTokens: parsed.usage.inputTokens,
13306
- outputTokens: parsed.usage.outputTokens
13307
- };
13308
- }
13370
+ logger.debug(`[ServerLlmBackend] Response preview: ${responseText.substring(0, 200)}`);
13371
+ if (responseText.includes("<!DOCTYPE") || responseText.includes("<html")) {
13372
+ const titleMatch = responseText.match(/<title>(.*?)<\/title>/i);
13373
+ const h1Match = responseText.match(/<h1>(.*?)<\/h1>/i);
13374
+ if (titleMatch && titleMatch[1] !== "Error") {
13375
+ errorDetails = titleMatch[1].trim();
13376
+ } else if (h1Match) {
13377
+ errorDetails = h1Match[1].trim();
13309
13378
  }
13310
- } catch (parseError) {
13311
- streamLogger.streamError(parseError);
13379
+ } else if (responseText) {
13380
+ errorDetails = responseText.substring(0, 100).trim();
13312
13381
  }
13382
+ } catch (extractError) {
13383
+ logger.error("[ServerLlmBackend] Error extracting response:", extractError);
13313
13384
  }
13314
- });
13315
- if (options.abortSignal) {
13316
- const abortHandler = () => {
13317
- logger.debug("[ServerLlmBackend] Abort signal received, destroying stream");
13318
- response.data.destroy();
13319
- resolve3();
13320
- };
13321
- if (options.abortSignal.aborted) {
13322
- abortHandler();
13323
- return;
13324
- }
13325
- options.abortSignal.addEventListener("abort", abortHandler, { once: true });
13326
- response.data.on("close", () => {
13327
- options.abortSignal?.removeEventListener("abort", abortHandler);
13328
- });
13385
+ const errorMsg = errorDetails ? `403 Forbidden: ${errorDetails}` : "403 Forbidden - Request blocked by server. Check debug logs at ~/.bike4mind/debug/";
13386
+ throw new Error(errorMsg);
13329
13387
  }
13330
- response.data.on("data", (chunk) => {
13331
- if (options.abortSignal?.aborted) {
13332
- return;
13333
- }
13334
- parser.feed(chunk.toString());
13335
- });
13336
- response.data.on("end", () => {
13337
- if (!receivedDone) {
13338
- logger.debug("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
13339
- resolve3();
13340
- } else {
13341
- logger.debug("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
13342
- }
13343
- });
13344
- response.data.on("error", (error) => {
13345
- if (options.abortSignal?.aborted) {
13346
- resolve3();
13347
- return;
13348
- }
13349
- reject(error);
13350
- });
13351
- } catch (error) {
13352
- if (options.abortSignal?.aborted) {
13353
- logger.debug("[ServerLlmBackend] Request was aborted, resolving gracefully");
13354
- resolve3();
13355
- return;
13388
+ if (error.response) {
13389
+ throw new Error(
13390
+ `Request failed with status ${error.response.status}: ${error.response.statusText || "Unknown error"}`
13391
+ );
13356
13392
  }
13357
- if (isAxiosError(error) && error.code === "ERR_CANCELED") {
13358
- logger.debug("[ServerLlmBackend] Request was canceled, resolving gracefully");
13359
- resolve3();
13360
- return;
13393
+ }
13394
+ if (error instanceof Error) {
13395
+ if (error.message.includes("Authentication expired") || error.message.includes("Authentication failed")) {
13396
+ throw error;
13397
+ } else if (error.message.includes("ECONNREFUSED")) {
13398
+ throw new Error("Cannot connect to Bike4Mind server. Please check your internet connection.");
13399
+ } else if (error.message.includes("Rate limit exceeded")) {
13400
+ throw error;
13401
+ } else {
13402
+ throw new Error(`Failed to complete LLM request: ${error.message}`);
13361
13403
  }
13362
- logger.error("LLM completion failed", error);
13363
- if (isAxiosError(error)) {
13364
- logger.debug(
13365
- `[ServerLlmBackend] Axios error details: ${JSON.stringify({
13366
- status: error.response?.status,
13367
- statusText: error.response?.statusText,
13368
- url: error.config?.url,
13369
- method: error.config?.method
13370
- })}`
13371
- );
13372
- if (error.response?.status === 403 && error.response.data) {
13373
- let errorDetails = "";
13374
- try {
13375
- let responseText = "";
13376
- const stream = error.response.data;
13377
- if (Buffer.isBuffer(stream)) {
13378
- responseText = stream.toString("utf-8");
13379
- } else if (stream?._readableState?.buffer?.length > 0) {
13380
- const chunks = [];
13381
- for (const chunk of stream._readableState.buffer) {
13382
- if (chunk?.data) {
13383
- chunks.push(Buffer.from(chunk.data));
13404
+ } else {
13405
+ throw error;
13406
+ }
13407
+ }
13408
+ logger.debug("[ServerLlmBackend] Got response, setting up SSE parser");
13409
+ return new Promise((resolve3, reject) => {
13410
+ const isVerbose = process.env.B4M_VERBOSE === "1";
13411
+ const isUltraVerbose = process.env.B4M_DEBUG_STREAM === "1";
13412
+ const streamLogger = new StreamLogger(logger, "ServerLlmBackend", isVerbose, isUltraVerbose);
13413
+ streamLogger.streamStart();
13414
+ let eventCount = 0;
13415
+ let accumulatedText = "";
13416
+ let lastUsageInfo = {};
13417
+ let toolsUsed = [];
13418
+ let thinkingBlocks = [];
13419
+ let receivedDone = false;
13420
+ const parser = createParser({
13421
+ onEvent: (event) => {
13422
+ eventCount++;
13423
+ streamLogger.onEvent(eventCount, event.data || "");
13424
+ const data = event.data;
13425
+ if (data === "[DONE]") {
13426
+ receivedDone = true;
13427
+ streamLogger.onCriticalEvent(eventCount, "[DONE]", `accumulated text length: ${accumulatedText.length}`);
13428
+ const cleanedText = stripThinkingBlocks(accumulatedText);
13429
+ streamLogger.streamComplete(accumulatedText);
13430
+ if (toolsUsed.length > 0) {
13431
+ const info = {
13432
+ toolsUsed,
13433
+ thinking: thinkingBlocks.length > 0 ? thinkingBlocks : void 0,
13434
+ ...lastUsageInfo
13435
+ };
13436
+ logger.debug(`[ServerLlmBackend] Calling callback with tools, thinking blocks: ${thinkingBlocks.length}`);
13437
+ callback([cleanedText], info).catch((err) => {
13438
+ logger.error("[ServerLlmBackend] Callback error:", err);
13439
+ reject(err);
13440
+ }).then(() => {
13441
+ logger.debug("[ServerLlmBackend] Callback completed, resolving");
13442
+ resolve3();
13443
+ });
13444
+ } else if (cleanedText) {
13445
+ callback([cleanedText], lastUsageInfo).catch((err) => {
13446
+ logger.error("[ServerLlmBackend] Callback error:", err);
13447
+ reject(err);
13448
+ }).then(() => resolve3());
13449
+ } else {
13450
+ resolve3();
13451
+ }
13452
+ return;
13453
+ }
13454
+ try {
13455
+ const parsed = JSON.parse(data);
13456
+ if (parsed.type === "error") {
13457
+ streamLogger.onCriticalEvent(eventCount, "ERROR", parsed.message || "Server error");
13458
+ reject(new Error(parsed.message || "Server error"));
13459
+ return;
13460
+ }
13461
+ if (parsed.type === "content") {
13462
+ const textChunk = parsed.text || "";
13463
+ accumulatedText += textChunk;
13464
+ if (parsed.usage) {
13465
+ lastUsageInfo = {
13466
+ inputTokens: parsed.usage.inputTokens,
13467
+ outputTokens: parsed.usage.outputTokens
13468
+ };
13469
+ }
13470
+ streamLogger.onContent(eventCount, textChunk, accumulatedText);
13471
+ } else if (parsed.type === "tool_use") {
13472
+ streamLogger.onCriticalEvent(eventCount, "TOOL_USE", `tools: ${parsed.tools?.length}`);
13473
+ if (parsed.tools && parsed.tools.length > 0) {
13474
+ for (const tool of parsed.tools) {
13475
+ logger.debug(`TOOL REQUEST: ${tool.name}`);
13476
+ try {
13477
+ const paramsStr = JSON.stringify(tool.input);
13478
+ logger.debug(` Params: ${paramsStr}`);
13479
+ } catch {
13480
+ logger.debug(` Params: [Unable to stringify]`);
13384
13481
  }
13385
13482
  }
13386
- responseText = Buffer.concat(chunks).toString("utf-8");
13387
- } else if (typeof stream === "string") {
13388
- responseText = stream;
13389
13483
  }
13390
- logger.debug(`[ServerLlmBackend] Response preview: ${responseText.substring(0, 200)}`);
13391
- if (responseText.includes("<!DOCTYPE") || responseText.includes("<html")) {
13392
- const titleMatch = responseText.match(/<title>(.*?)<\/title>/i);
13393
- const h1Match = responseText.match(/<h1>(.*?)<\/h1>/i);
13394
- if (titleMatch && titleMatch[1] !== "Error") {
13395
- errorDetails = titleMatch[1].trim();
13396
- } else if (h1Match) {
13397
- errorDetails = h1Match[1].trim();
13398
- }
13399
- } else if (responseText) {
13400
- errorDetails = responseText.substring(0, 100).trim();
13484
+ const textChunk = parsed.text || "";
13485
+ if (textChunk) {
13486
+ accumulatedText += textChunk;
13487
+ }
13488
+ if (parsed.tools && parsed.tools.length > 0) {
13489
+ toolsUsed = parsed.tools;
13490
+ }
13491
+ if (parsed.thinking && parsed.thinking.length > 0) {
13492
+ thinkingBlocks = parsed.thinking;
13493
+ streamLogger.onCriticalEvent(eventCount, "THINKING", `${thinkingBlocks.length} thinking blocks`);
13494
+ }
13495
+ if (parsed.usage) {
13496
+ lastUsageInfo = {
13497
+ inputTokens: parsed.usage.inputTokens,
13498
+ outputTokens: parsed.usage.outputTokens
13499
+ };
13401
13500
  }
13402
- } catch (extractError) {
13403
- logger.error("[ServerLlmBackend] Error extracting response:", extractError);
13404
13501
  }
13405
- const errorMsg = errorDetails ? `403 Forbidden: ${errorDetails}` : "403 Forbidden - Request blocked by server. Check debug logs at ~/.bike4mind/debug/";
13406
- reject(new Error(errorMsg));
13407
- return;
13408
- }
13409
- if (error.response) {
13410
- reject(
13411
- new Error(
13412
- `Request failed with status ${error.response.status}: ${error.response.statusText || "Unknown error"}`
13413
- )
13414
- );
13415
- return;
13502
+ } catch (parseError) {
13503
+ streamLogger.streamError(parseError);
13416
13504
  }
13417
13505
  }
13418
- if (error instanceof Error) {
13419
- if (error.message.includes("Authentication expired") || error.message.includes("Authentication failed")) {
13420
- reject(error);
13421
- } else if (error.message.includes("ECONNREFUSED")) {
13422
- reject(new Error("Cannot connect to Bike4Mind server. Please check your internet connection."));
13423
- } else if (error.message.includes("Rate limit exceeded")) {
13424
- reject(error);
13425
- } else {
13426
- reject(new Error(`Failed to complete LLM request: ${error.message}`));
13427
- }
13428
- } else {
13429
- reject(error);
13506
+ });
13507
+ if (options.abortSignal) {
13508
+ const abortHandler = () => {
13509
+ logger.debug("[ServerLlmBackend] Abort signal received, destroying stream");
13510
+ response.data.destroy();
13511
+ resolve3();
13512
+ };
13513
+ if (options.abortSignal.aborted) {
13514
+ abortHandler();
13515
+ return;
13430
13516
  }
13517
+ options.abortSignal.addEventListener("abort", abortHandler, { once: true });
13518
+ response.data.on("close", () => {
13519
+ options.abortSignal?.removeEventListener("abort", abortHandler);
13520
+ });
13431
13521
  }
13522
+ response.data.on("data", (chunk) => {
13523
+ if (options.abortSignal?.aborted) {
13524
+ return;
13525
+ }
13526
+ parser.feed(chunk.toString());
13527
+ });
13528
+ response.data.on("end", () => {
13529
+ if (!receivedDone) {
13530
+ logger.debug("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
13531
+ resolve3();
13532
+ } else {
13533
+ logger.debug("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
13534
+ }
13535
+ });
13536
+ response.data.on("error", (error) => {
13537
+ if (options.abortSignal?.aborted) {
13538
+ resolve3();
13539
+ return;
13540
+ }
13541
+ reject(error);
13542
+ });
13432
13543
  });
13433
13544
  }
13434
13545
  /**
@@ -13690,7 +13801,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
13690
13801
  // package.json
13691
13802
  var package_default = {
13692
13803
  name: "@bike4mind/cli",
13693
- version: "0.2.28-slack-native-search.18717+8544a2350",
13804
+ version: "0.2.28",
13694
13805
  type: "module",
13695
13806
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
13696
13807
  license: "UNLICENSED",
@@ -13778,7 +13889,7 @@ var package_default = {
13778
13889
  mongoose: "^8.8.3",
13779
13890
  ollama: "^0.5.12",
13780
13891
  open: "^11.0.0",
13781
- openai: "^5.12.2",
13892
+ openai: "^6.18.0",
13782
13893
  "p-limit": "^6.2.0",
13783
13894
  qrcode: "^1.5.4",
13784
13895
  react: "^19.2.3",
@@ -13798,10 +13909,10 @@ var package_default = {
13798
13909
  },
13799
13910
  devDependencies: {
13800
13911
  "@bike4mind/agents": "0.1.0",
13801
- "@bike4mind/common": "2.49.1-slack-native-search.18717+8544a2350",
13802
- "@bike4mind/mcp": "1.28.3-slack-native-search.18717+8544a2350",
13803
- "@bike4mind/services": "2.47.2-slack-native-search.18717+8544a2350",
13804
- "@bike4mind/utils": "2.4.2-slack-native-search.18717+8544a2350",
13912
+ "@bike4mind/common": "2.50.0",
13913
+ "@bike4mind/mcp": "1.29.0",
13914
+ "@bike4mind/services": "2.48.0",
13915
+ "@bike4mind/utils": "2.5.0",
13805
13916
  "@types/better-sqlite3": "^7.6.13",
13806
13917
  "@types/diff": "^5.0.9",
13807
13918
  "@types/jsonwebtoken": "^9.0.4",
@@ -13818,7 +13929,7 @@ var package_default = {
13818
13929
  optionalDependencies: {
13819
13930
  "@vscode/ripgrep": "^1.17.0"
13820
13931
  },
13821
- gitHead: "8544a235088d45da796ccef6904d4b89597b63e6"
13932
+ gitHead: "28d4afd2bd097efcd751a14cbda1eee68cae2ad2"
13822
13933
  };
13823
13934
 
13824
13935
  // src/config/constants.ts
@@ -16598,7 +16709,7 @@ Custom Commands:
16598
16709
  await performCleanup();
16599
16710
  exit();
16600
16711
  break;
16601
- case "save":
16712
+ case "save": {
16602
16713
  if (!state.session) {
16603
16714
  console.log("No active session to save");
16604
16715
  return;
@@ -16612,6 +16723,7 @@ Custom Commands:
16612
16723
  await state.sessionStore.save(state.session);
16613
16724
  console.log(`\u2705 Session saved as "${sessionName}"`);
16614
16725
  break;
16726
+ }
16615
16727
  case "sessions": {
16616
16728
  const sessions = await state.sessionStore.list(20);
16617
16729
  if (sessions.length === 0) {
@@ -16800,7 +16912,7 @@ Custom Commands:
16800
16912
  }));
16801
16913
  break;
16802
16914
  }
16803
- case "untrust":
16915
+ case "untrust": {
16804
16916
  if (!state.permissionManager) {
16805
16917
  console.log("Permission manager not initialized");
16806
16918
  return;
@@ -16814,7 +16926,8 @@ Custom Commands:
16814
16926
  await state.configStore.untrustTool(toolToUntrust);
16815
16927
  console.log(`\u2705 Tool '${toolToUntrust}' removed from trusted list`);
16816
16928
  break;
16817
- case "trusted":
16929
+ }
16930
+ case "trusted": {
16818
16931
  if (!state.permissionManager) {
16819
16932
  console.log("Permission manager not initialized");
16820
16933
  return;
@@ -16828,6 +16941,7 @@ Custom Commands:
16828
16941
  }
16829
16942
  console.log("");
16830
16943
  break;
16944
+ }
16831
16945
  case "login":
16832
16946
  setState((prev) => ({ ...prev, showLoginFlow: true }));
16833
16947
  break;
@@ -16836,7 +16950,7 @@ Custom Commands:
16836
16950
  await state.configStore.clearAuthTokens();
16837
16951
  console.log("\u2705 Successfully logged out");
16838
16952
  break;
16839
- case "whoami":
16953
+ case "whoami": {
16840
16954
  const authTokens = await state.configStore.getAuthTokens();
16841
16955
  if (!authTokens) {
16842
16956
  console.log("Not authenticated. Run /login to authenticate.");
@@ -16849,6 +16963,7 @@ Custom Commands:
16849
16963
  console.log(`Expires: ${new Date(authTokens.expiresAt).toLocaleString()}`);
16850
16964
  console.log("");
16851
16965
  break;
16966
+ }
16852
16967
  case "clear":
16853
16968
  case "new": {
16854
16969
  console.clear();