@bike4mind/cli 0.2.28-slack-native-search.18717 → 0.2.29-cli-resume-command.18763

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";
@@ -551,8 +551,9 @@ var COMMANDS = [
551
551
  args: "<name>"
552
552
  },
553
553
  {
554
- name: "sessions",
555
- description: "List saved sessions"
554
+ name: "resume",
555
+ description: "List and resume saved sessions",
556
+ aliases: ["sessions"]
556
557
  },
557
558
  {
558
559
  name: "config",
@@ -3717,18 +3718,23 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
3717
3718
  async executeToolWithQueueFallback(toolUse) {
3718
3719
  const queuedObs = this.observationQueue.find((obs) => obs.toolId === getToolId(toolUse));
3719
3720
  if (queuedObs) {
3720
- const result2 = queuedObs.result;
3721
+ const result = queuedObs.result;
3721
3722
  const index = this.observationQueue.indexOf(queuedObs);
3722
3723
  this.observationQueue.splice(index, 1);
3723
- return typeof result2 === "string" ? result2 : JSON.stringify(result2);
3724
+ return typeof result === "string" ? result : JSON.stringify(result);
3724
3725
  }
3725
3726
  const tool = this.context.tools.find((t) => t.toolSchema.name === toolUse.name);
3726
3727
  if (!tool) {
3727
- throw new Error(`Tool ${toolUse.name} not found in agent context`);
3728
+ return `Error: Tool ${toolUse.name} not found in agent context`;
3729
+ }
3730
+ try {
3731
+ const params = this.parseToolArguments(toolUse.arguments);
3732
+ const result = await tool.toolFn(params);
3733
+ return typeof result === "string" ? result : JSON.stringify(result);
3734
+ } catch (error) {
3735
+ const message = error instanceof Error ? error.message : String(error);
3736
+ return `Error: ${message}`;
3728
3737
  }
3729
- const params = this.parseToolArguments(toolUse.arguments);
3730
- const result = await tool.toolFn(params);
3731
- return typeof result === "string" ? result : JSON.stringify(result);
3732
3738
  }
3733
3739
  /**
3734
3740
  * Build and append tool call/result messages for the conversation history
@@ -6080,7 +6086,10 @@ var imageGenerationTool = {
6080
6086
  return "Error: Image configuration is required";
6081
6087
  }
6082
6088
  const { prompt, n: toolN, quality: toolQuality, size: toolSize, safety_tolerance: toolSafetyTolerance } = val;
6083
- const model = imageConfig?.model || ImageModels.GPT_IMAGE_1;
6089
+ let model = imageConfig?.model || ImageModels.GPT_IMAGE_1_5;
6090
+ if (model === ImageModels.GPT_IMAGE_1) {
6091
+ model = ImageModels.GPT_IMAGE_1_5;
6092
+ }
6084
6093
  const n = toolN ?? imageConfig?.n ?? 1;
6085
6094
  const quality = imageConfig?.quality || toolQuality;
6086
6095
  const size = imageConfig?.size || toolSize;
@@ -7331,6 +7340,14 @@ Return only the edited content without any markdown code blocks or explanations.
7331
7340
  import axios7 from "axios";
7332
7341
  import { fileTypeFromBuffer as fileTypeFromBuffer3 } from "file-type";
7333
7342
  import { v4 as uuidv46 } from "uuid";
7343
+ var EDIT_SUPPORTED_MODELS = [
7344
+ ImageModels.GPT_IMAGE_1,
7345
+ ImageModels.GPT_IMAGE_1_5,
7346
+ ImageModels.GPT_IMAGE_1_MINI,
7347
+ ImageModels.FLUX_PRO_FILL,
7348
+ ImageModels.GEMINI_2_5_FLASH_IMAGE,
7349
+ ImageModels.GEMINI_3_PRO_IMAGE_PREVIEW
7350
+ ];
7334
7351
  async function downloadImage2(url) {
7335
7352
  if (url.startsWith("data:image/")) {
7336
7353
  const base64Data = url.split(",")[1];
@@ -7355,7 +7372,7 @@ async function imageUrlToBase64(imageUrl) {
7355
7372
  return buffer.toString("base64");
7356
7373
  }
7357
7374
  async function getImageFromFileId(fileId, context) {
7358
- const fabFile = await context.db.fabFiles?.findById(fileId);
7375
+ const fabFile = await context.db.fabfiles?.findById(fileId);
7359
7376
  if (!fabFile) {
7360
7377
  throw new NotFoundError(`File with ID ${fileId} not found`);
7361
7378
  }
@@ -7378,6 +7395,24 @@ async function processAndStoreImage(imageUrl, context) {
7378
7395
  const path18 = await context.imageGenerateStorage.upload(buffer, filename, {});
7379
7396
  return path18;
7380
7397
  }
7398
+ async function generateFullMask(imageBuffer) {
7399
+ const sharp = (await import("sharp")).default;
7400
+ const metadata = await sharp(imageBuffer).metadata();
7401
+ const width = metadata.width;
7402
+ const height = metadata.height;
7403
+ const maskData = Buffer.alloc(width * height * 4);
7404
+ for (let i = 0; i < maskData.length; i++) {
7405
+ maskData[i] = 255;
7406
+ }
7407
+ const maskBuffer = await sharp(maskData, {
7408
+ raw: {
7409
+ width,
7410
+ height,
7411
+ channels: 4
7412
+ }
7413
+ }).png().toBuffer();
7414
+ return maskBuffer.toString("base64");
7415
+ }
7381
7416
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
7382
7417
  await context.onFinish?.("edit_image", storedImagePath);
7383
7418
  await context.statusUpdate({ images: [storedImagePath] });
@@ -7389,14 +7424,32 @@ var imageEditTool = {
7389
7424
  toolFn: async (val) => {
7390
7425
  const imageConfig = config;
7391
7426
  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
7427
  if (!toolImage) {
7394
7428
  return "Error: Image is required for editing";
7395
7429
  }
7396
7430
  if (!prompt) {
7397
7431
  return "Error: Prompt is required for editing";
7398
7432
  }
7399
- const model = imageConfig?.model || ImageModels.GPT_IMAGE_1;
7433
+ const generationModel = imageConfig?.model || ImageModels.GPT_IMAGE_1_5;
7434
+ let editModel = imageConfig?.editModel;
7435
+ if (!editModel) {
7436
+ const isGenBFLModel = BFL_IMAGE_MODELS.includes(generationModel);
7437
+ const isGenGeminiModel = GEMINI_IMAGE_MODELS.includes(generationModel);
7438
+ if (isGenBFLModel) {
7439
+ editModel = ImageModels.FLUX_PRO_FILL;
7440
+ } else if (isGenGeminiModel) {
7441
+ editModel = generationModel;
7442
+ } else {
7443
+ editModel = ImageModels.GPT_IMAGE_1_5;
7444
+ }
7445
+ console.log(`[DEBUG] No edit model specified, using fallback: ${editModel} (based on generation model: ${generationModel})`);
7446
+ }
7447
+ if (!EDIT_SUPPORTED_MODELS.includes(editModel)) {
7448
+ return `Error: Model ${editModel} does not support image editing. Supported models for editing: ${EDIT_SUPPORTED_MODELS.join(", ")}.
7449
+
7450
+ Please select a supported edit model in your image settings modal.`;
7451
+ }
7452
+ const model = generationModel;
7400
7453
  const n = toolN ?? imageConfig?.n ?? 1;
7401
7454
  const size = imageConfig?.size || toolSize;
7402
7455
  const safety_tolerance = imageConfig?.safety_tolerance || toolSafetyTolerance;
@@ -7405,8 +7458,8 @@ var imageEditTool = {
7405
7458
  const seed = imageConfig?.seed;
7406
7459
  const steps = toolSteps || 50;
7407
7460
  const guidance = toolGuidance || 60;
7408
- const isBFLModel = BFL_IMAGE_MODELS.includes(model);
7409
- const isGeminiModel = GEMINI_IMAGE_MODELS.includes(model);
7461
+ const isBFLModel = BFL_IMAGE_MODELS.includes(editModel);
7462
+ const isGeminiModel = GEMINI_IMAGE_MODELS.includes(editModel);
7410
7463
  await context.onStart?.("edit_image", {
7411
7464
  model,
7412
7465
  n,
@@ -7433,67 +7486,135 @@ var imageEditTool = {
7433
7486
  }
7434
7487
  if (isBFLModel) {
7435
7488
  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;
7489
+ const service = new BFLImageService(apiKey, context.logger, context.imageProcessorLambdaName);
7490
+ let bflModel = editModel;
7491
+ if (bflModel !== ImageModels.FLUX_PRO_FILL) {
7492
+ console.warn(`[WARN] BFL edit model ${bflModel} does not support editing. Falling back to FLUX_PRO_FILL.`);
7493
+ bflModel = ImageModels.FLUX_PRO_FILL;
7494
+ }
7438
7495
  if (!maskBase64Image) {
7439
- return "Error: Mask image is required for BFL image editing";
7496
+ const sourceBuffer = Buffer.from(sourceBase64Image, "base64");
7497
+ maskBase64Image = await generateFullMask(sourceBuffer);
7440
7498
  }
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);
7499
+ try {
7500
+ const editResponse = await service.edit(sourceBase64Image, prompt, {
7501
+ mask: maskBase64Image,
7502
+ model: bflModel,
7503
+ safety_tolerance: safety_tolerance ?? BFL_SAFETY_TOLERANCE.DEFAULT,
7504
+ prompt_upsampling,
7505
+ seed: seed ?? void 0,
7506
+ output_format: output_format ?? "jpeg",
7507
+ steps,
7508
+ guidance
7509
+ });
7510
+ if (editResponse.type === "success") {
7511
+ const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7512
+ return updateQuestAndReturnMarkdown2(storedImagePath, context);
7513
+ }
7514
+ return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7515
+ } catch (error) {
7516
+ console.error("[ERROR] BFL image editing failed:", error);
7517
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
7518
+ if (axios7.isAxiosError(error)) {
7519
+ const status = error.response?.status;
7520
+ const responseData = error.response?.data;
7521
+ if (status === 403) {
7522
+ return `Error: BFL API access denied (403). This usually means:
7523
+ - Your BFL API key is not set, invalid, or expired
7524
+ - Your BFL account lacks permissions for FLUX-PRO-FILL
7525
+ - Your BFL API credits have run out
7526
+
7527
+ Please check your BFL API key in settings and ensure it is configured correctly.`;
7528
+ } else if (status === 429) {
7529
+ return `Error: BFL API rate limit exceeded (429). Please wait a moment and try again.`;
7530
+ } else if (status === 402) {
7531
+ return `Error: BFL API payment required (402). Please add credits to your BFL account.`;
7532
+ } else if (responseData?.error) {
7533
+ return `Error: BFL API error - ${responseData.error}`;
7534
+ }
7535
+ }
7536
+ 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
7537
  }
7455
- return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7456
7538
  } else if (isGeminiModel) {
7457
7539
  const apiKey = await getEffectiveApiKey(context.userId, { type: ApiKeyType.gemini }, { db: context.db });
7458
- const service = new GeminiImageService(apiKey, context.logger);
7540
+ const service = new GeminiImageService(apiKey, context.logger, context.imageProcessorLambdaName);
7459
7541
  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);
7542
+ try {
7543
+ const editResponse = await service.edit(dataUrlImage, prompt, {
7544
+ aspect_ratio: imageConfig?.aspect_ratio,
7545
+ output_format: output_format ?? "png",
7546
+ safety_tolerance,
7547
+ model: editModel
7548
+ // Pass edit model to service
7549
+ });
7550
+ if (editResponse.type === "success") {
7551
+ const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7552
+ return updateQuestAndReturnMarkdown2(storedImagePath, context);
7553
+ }
7554
+ return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7555
+ } catch (error) {
7556
+ console.error("[ERROR] Gemini image editing failed:", error);
7557
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
7558
+ if (axios7.isAxiosError(error)) {
7559
+ const status = error.response?.status;
7560
+ const responseData = error.response?.data;
7561
+ if (status === 403 || status === 401) {
7562
+ 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.`;
7563
+ } else if (status === 429) {
7564
+ return `Error: Gemini API rate limit exceeded (429). Please wait a moment and try again.`;
7565
+ } else if (responseData?.error) {
7566
+ return `Error: Gemini API error - ${JSON.stringify(responseData.error)}`;
7567
+ }
7568
+ }
7569
+ return `Error: Failed to edit image with Gemini. ${errorMessage}. Please try a different edit model like GPT-Image-1.5.`;
7468
7570
  }
7469
- return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7470
7571
  } else {
7471
7572
  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);
7573
+ const service = new OpenAIImageService(apiKey, context.logger, context.imageProcessorLambdaName);
7574
+ try {
7575
+ const editResponse = await service.edit(sourceBase64Image, prompt, {
7576
+ mask: maskBase64Image,
7577
+ model: editModel,
7578
+ // Use the configured edit model
7579
+ n,
7580
+ size,
7581
+ response_format: "url",
7582
+ user: context.userId
7583
+ });
7584
+ if (editResponse.type === "success") {
7585
+ const storedImagePath = await processAndStoreImage(editResponse.dataUrl, context);
7586
+ return updateQuestAndReturnMarkdown2(storedImagePath, context);
7587
+ }
7588
+ return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7589
+ } catch (error) {
7590
+ console.error("[ERROR] OpenAI image editing failed:", error);
7591
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
7592
+ if (axios7.isAxiosError(error)) {
7593
+ const status = error.response?.status;
7594
+ const responseData = error.response?.data;
7595
+ if (status === 403 || status === 401) {
7596
+ 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.`;
7597
+ } else if (status === 429) {
7598
+ return `Error: OpenAI API rate limit exceeded (429). Please wait a moment and try again.`;
7599
+ } else if (status === 402) {
7600
+ return `Error: OpenAI API payment required (402). Please add credits to your OpenAI account.`;
7601
+ } else if (responseData?.error) {
7602
+ return `Error: OpenAI API error - ${responseData.error.message || JSON.stringify(responseData.error)}`;
7603
+ }
7604
+ }
7605
+ return `Error: Failed to edit image with OpenAI. ${errorMessage}. Please try again or use a different edit model.`;
7484
7606
  }
7485
- return `Error: ${"question" in editResponse ? editResponse.question : "Failed to edit image"}`;
7486
7607
  }
7487
7608
  },
7488
7609
  toolSchema: {
7489
7610
  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.",
7611
+ 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
7612
  parameters: {
7492
7613
  type: "object",
7493
7614
  properties: {
7494
7615
  image: {
7495
7616
  type: "string",
7496
- description: "The image to edit. This should be a FabFile S3 URL."
7617
+ 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
7618
  },
7498
7619
  prompt: {
7499
7620
  type: "string",
@@ -10716,7 +10837,7 @@ var cliOnlyTools = {
10716
10837
  // Git operations
10717
10838
  recent_changes: recentChangesTool
10718
10839
  };
10719
- var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, tools = b4mTools) => {
10840
+ var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools = b4mTools) => {
10720
10841
  const context = {
10721
10842
  userId,
10722
10843
  user,
@@ -10728,7 +10849,8 @@ var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorag
10728
10849
  onStart,
10729
10850
  onFinish,
10730
10851
  llm,
10731
- model
10852
+ model,
10853
+ imageProcessorLambdaName
10732
10854
  };
10733
10855
  return Object.entries(tools).reduce((acc, [key, tool]) => ({
10734
10856
  ...acc,
@@ -12062,6 +12184,8 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
12062
12184
  llm,
12063
12185
  {},
12064
12186
  model,
12187
+ void 0,
12188
+ // imageProcessorLambdaName (not needed for CLI)
12065
12189
  tools_to_generate
12066
12190
  );
12067
12191
  let tools = Object.entries(toolsMap).map(
@@ -13208,227 +13332,220 @@ var ServerLlmBackend = class {
13208
13332
  logger.debug("[ServerLlmBackend] Request aborted before start");
13209
13333
  return;
13210
13334
  }
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();
13335
+ logger.debug("[ServerLlmBackend] Making streaming request...");
13336
+ let response;
13337
+ try {
13338
+ response = await this.makeStreamingRequest(model, messages, options);
13339
+ } catch (error) {
13340
+ if (options.abortSignal?.aborted) {
13341
+ logger.debug("[ServerLlmBackend] Request was aborted, resolving gracefully");
13342
+ return;
13343
+ }
13344
+ if (isAxiosError(error) && error.code === "ERR_CANCELED") {
13345
+ logger.debug("[ServerLlmBackend] Request was canceled, resolving gracefully");
13346
+ return;
13347
+ }
13348
+ logger.error("LLM completion failed", error);
13349
+ if (isAxiosError(error)) {
13350
+ logger.debug(
13351
+ `[ServerLlmBackend] Axios error details: ${JSON.stringify({
13352
+ status: error.response?.status,
13353
+ statusText: error.response?.statusText,
13354
+ url: error.config?.url,
13355
+ method: error.config?.method
13356
+ })}`
13357
+ );
13358
+ if (error.response?.status === 403 && error.response.data) {
13359
+ let errorDetails = "";
13360
+ try {
13361
+ let responseText = "";
13362
+ const stream = error.response.data;
13363
+ if (Buffer.isBuffer(stream)) {
13364
+ responseText = stream.toString("utf-8");
13365
+ } else if (stream?._readableState?.buffer?.length > 0) {
13366
+ const chunks = [];
13367
+ for (const chunk of stream._readableState.buffer) {
13368
+ if (chunk?.data) {
13369
+ chunks.push(Buffer.from(chunk.data));
13370
+ }
13259
13371
  }
13260
- return;
13372
+ responseText = Buffer.concat(chunks).toString("utf-8");
13373
+ } else if (typeof stream === "string") {
13374
+ responseText = stream;
13261
13375
  }
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;
13376
+ logger.debug(`[ServerLlmBackend] Response preview: ${responseText.substring(0, 200)}`);
13377
+ if (responseText.includes("<!DOCTYPE") || responseText.includes("<html")) {
13378
+ const titleMatch = responseText.match(/<title>(.*?)<\/title>/i);
13379
+ const h1Match = responseText.match(/<h1>(.*?)<\/h1>/i);
13380
+ if (titleMatch && titleMatch[1] !== "Error") {
13381
+ errorDetails = titleMatch[1].trim();
13382
+ } else if (h1Match) {
13383
+ errorDetails = h1Match[1].trim();
13268
13384
  }
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
- }
13309
- }
13310
- } catch (parseError) {
13311
- streamLogger.streamError(parseError);
13385
+ } else if (responseText) {
13386
+ errorDetails = responseText.substring(0, 100).trim();
13312
13387
  }
13388
+ } catch (extractError) {
13389
+ logger.error("[ServerLlmBackend] Error extracting response:", extractError);
13313
13390
  }
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
- });
13391
+ const errorMsg = errorDetails ? `403 Forbidden: ${errorDetails}` : "403 Forbidden - Request blocked by server. Check debug logs at ~/.bike4mind/debug/";
13392
+ throw new Error(errorMsg);
13329
13393
  }
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;
13394
+ if (error.response) {
13395
+ throw new Error(
13396
+ `Request failed with status ${error.response.status}: ${error.response.statusText || "Unknown error"}`
13397
+ );
13356
13398
  }
13357
- if (isAxiosError(error) && error.code === "ERR_CANCELED") {
13358
- logger.debug("[ServerLlmBackend] Request was canceled, resolving gracefully");
13359
- resolve3();
13360
- return;
13399
+ }
13400
+ if (error instanceof Error) {
13401
+ if (error.message.includes("Authentication expired") || error.message.includes("Authentication failed")) {
13402
+ throw error;
13403
+ } else if (error.message.includes("ECONNREFUSED")) {
13404
+ throw new Error("Cannot connect to Bike4Mind server. Please check your internet connection.");
13405
+ } else if (error.message.includes("Rate limit exceeded")) {
13406
+ throw error;
13407
+ } else {
13408
+ throw new Error(`Failed to complete LLM request: ${error.message}`);
13361
13409
  }
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));
13410
+ } else {
13411
+ throw error;
13412
+ }
13413
+ }
13414
+ logger.debug("[ServerLlmBackend] Got response, setting up SSE parser");
13415
+ return new Promise((resolve3, reject) => {
13416
+ const isVerbose = process.env.B4M_VERBOSE === "1";
13417
+ const isUltraVerbose = process.env.B4M_DEBUG_STREAM === "1";
13418
+ const streamLogger = new StreamLogger(logger, "ServerLlmBackend", isVerbose, isUltraVerbose);
13419
+ streamLogger.streamStart();
13420
+ let eventCount = 0;
13421
+ let accumulatedText = "";
13422
+ let lastUsageInfo = {};
13423
+ let toolsUsed = [];
13424
+ let thinkingBlocks = [];
13425
+ let receivedDone = false;
13426
+ const parser = createParser({
13427
+ onEvent: (event) => {
13428
+ eventCount++;
13429
+ streamLogger.onEvent(eventCount, event.data || "");
13430
+ const data = event.data;
13431
+ if (data === "[DONE]") {
13432
+ receivedDone = true;
13433
+ streamLogger.onCriticalEvent(eventCount, "[DONE]", `accumulated text length: ${accumulatedText.length}`);
13434
+ const cleanedText = stripThinkingBlocks(accumulatedText);
13435
+ streamLogger.streamComplete(accumulatedText);
13436
+ if (toolsUsed.length > 0) {
13437
+ const info = {
13438
+ toolsUsed,
13439
+ thinking: thinkingBlocks.length > 0 ? thinkingBlocks : void 0,
13440
+ ...lastUsageInfo
13441
+ };
13442
+ logger.debug(`[ServerLlmBackend] Calling callback with tools, thinking blocks: ${thinkingBlocks.length}`);
13443
+ callback([cleanedText], info).catch((err) => {
13444
+ logger.error("[ServerLlmBackend] Callback error:", err);
13445
+ reject(err);
13446
+ }).then(() => {
13447
+ logger.debug("[ServerLlmBackend] Callback completed, resolving");
13448
+ resolve3();
13449
+ });
13450
+ } else if (cleanedText) {
13451
+ callback([cleanedText], lastUsageInfo).catch((err) => {
13452
+ logger.error("[ServerLlmBackend] Callback error:", err);
13453
+ reject(err);
13454
+ }).then(() => resolve3());
13455
+ } else {
13456
+ resolve3();
13457
+ }
13458
+ return;
13459
+ }
13460
+ try {
13461
+ const parsed = JSON.parse(data);
13462
+ if (parsed.type === "error") {
13463
+ streamLogger.onCriticalEvent(eventCount, "ERROR", parsed.message || "Server error");
13464
+ reject(new Error(parsed.message || "Server error"));
13465
+ return;
13466
+ }
13467
+ if (parsed.type === "content") {
13468
+ const textChunk = parsed.text || "";
13469
+ accumulatedText += textChunk;
13470
+ if (parsed.usage) {
13471
+ lastUsageInfo = {
13472
+ inputTokens: parsed.usage.inputTokens,
13473
+ outputTokens: parsed.usage.outputTokens
13474
+ };
13475
+ }
13476
+ streamLogger.onContent(eventCount, textChunk, accumulatedText);
13477
+ } else if (parsed.type === "tool_use") {
13478
+ streamLogger.onCriticalEvent(eventCount, "TOOL_USE", `tools: ${parsed.tools?.length}`);
13479
+ if (parsed.tools && parsed.tools.length > 0) {
13480
+ for (const tool of parsed.tools) {
13481
+ logger.debug(`TOOL REQUEST: ${tool.name}`);
13482
+ try {
13483
+ const paramsStr = JSON.stringify(tool.input);
13484
+ logger.debug(` Params: ${paramsStr}`);
13485
+ } catch {
13486
+ logger.debug(` Params: [Unable to stringify]`);
13384
13487
  }
13385
13488
  }
13386
- responseText = Buffer.concat(chunks).toString("utf-8");
13387
- } else if (typeof stream === "string") {
13388
- responseText = stream;
13389
13489
  }
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();
13490
+ const textChunk = parsed.text || "";
13491
+ if (textChunk) {
13492
+ accumulatedText += textChunk;
13493
+ }
13494
+ if (parsed.tools && parsed.tools.length > 0) {
13495
+ toolsUsed = parsed.tools;
13496
+ }
13497
+ if (parsed.thinking && parsed.thinking.length > 0) {
13498
+ thinkingBlocks = parsed.thinking;
13499
+ streamLogger.onCriticalEvent(eventCount, "THINKING", `${thinkingBlocks.length} thinking blocks`);
13500
+ }
13501
+ if (parsed.usage) {
13502
+ lastUsageInfo = {
13503
+ inputTokens: parsed.usage.inputTokens,
13504
+ outputTokens: parsed.usage.outputTokens
13505
+ };
13401
13506
  }
13402
- } catch (extractError) {
13403
- logger.error("[ServerLlmBackend] Error extracting response:", extractError);
13404
13507
  }
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;
13508
+ } catch (parseError) {
13509
+ streamLogger.streamError(parseError);
13416
13510
  }
13417
13511
  }
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);
13512
+ });
13513
+ if (options.abortSignal) {
13514
+ const abortHandler = () => {
13515
+ logger.debug("[ServerLlmBackend] Abort signal received, destroying stream");
13516
+ response.data.destroy();
13517
+ resolve3();
13518
+ };
13519
+ if (options.abortSignal.aborted) {
13520
+ abortHandler();
13521
+ return;
13430
13522
  }
13523
+ options.abortSignal.addEventListener("abort", abortHandler, { once: true });
13524
+ response.data.on("close", () => {
13525
+ options.abortSignal?.removeEventListener("abort", abortHandler);
13526
+ });
13431
13527
  }
13528
+ response.data.on("data", (chunk) => {
13529
+ if (options.abortSignal?.aborted) {
13530
+ return;
13531
+ }
13532
+ parser.feed(chunk.toString());
13533
+ });
13534
+ response.data.on("end", () => {
13535
+ if (!receivedDone) {
13536
+ logger.debug("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
13537
+ resolve3();
13538
+ } else {
13539
+ logger.debug("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
13540
+ }
13541
+ });
13542
+ response.data.on("error", (error) => {
13543
+ if (options.abortSignal?.aborted) {
13544
+ resolve3();
13545
+ return;
13546
+ }
13547
+ reject(error);
13548
+ });
13432
13549
  });
13433
13550
  }
13434
13551
  /**
@@ -13690,7 +13807,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
13690
13807
  // package.json
13691
13808
  var package_default = {
13692
13809
  name: "@bike4mind/cli",
13693
- version: "0.2.28-slack-native-search.18717+8544a2350",
13810
+ version: "0.2.29-cli-resume-command.18763+9e3ccc640",
13694
13811
  type: "module",
13695
13812
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
13696
13813
  license: "UNLICENSED",
@@ -13778,7 +13895,7 @@ var package_default = {
13778
13895
  mongoose: "^8.8.3",
13779
13896
  ollama: "^0.5.12",
13780
13897
  open: "^11.0.0",
13781
- openai: "^5.12.2",
13898
+ openai: "^6.18.0",
13782
13899
  "p-limit": "^6.2.0",
13783
13900
  qrcode: "^1.5.4",
13784
13901
  react: "^19.2.3",
@@ -13798,10 +13915,10 @@ var package_default = {
13798
13915
  },
13799
13916
  devDependencies: {
13800
13917
  "@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",
13918
+ "@bike4mind/common": "2.50.1-cli-resume-command.18763+9e3ccc640",
13919
+ "@bike4mind/mcp": "1.29.1-cli-resume-command.18763+9e3ccc640",
13920
+ "@bike4mind/services": "2.48.1-cli-resume-command.18763+9e3ccc640",
13921
+ "@bike4mind/utils": "2.5.1-cli-resume-command.18763+9e3ccc640",
13805
13922
  "@types/better-sqlite3": "^7.6.13",
13806
13923
  "@types/diff": "^5.0.9",
13807
13924
  "@types/jsonwebtoken": "^9.0.4",
@@ -13818,7 +13935,7 @@ var package_default = {
13818
13935
  optionalDependencies: {
13819
13936
  "@vscode/ripgrep": "^1.17.0"
13820
13937
  },
13821
- gitHead: "8544a235088d45da796ccef6904d4b89597b63e6"
13938
+ gitHead: "9e3ccc640da244f7528fa1a479d00e8b40b415a4"
13822
13939
  };
13823
13940
 
13824
13941
  // src/config/constants.ts
@@ -16564,7 +16681,7 @@ Available commands:
16564
16681
  /whoami - Show current authenticated user
16565
16682
  /usage - Show credit usage and balance
16566
16683
  /save <name> - Save current session
16567
- /sessions - List saved sessions
16684
+ /resume - List and resume saved sessions
16568
16685
  /config - Show configuration
16569
16686
 
16570
16687
  API Configuration:
@@ -16598,7 +16715,7 @@ Custom Commands:
16598
16715
  await performCleanup();
16599
16716
  exit();
16600
16717
  break;
16601
- case "save":
16718
+ case "save": {
16602
16719
  if (!state.session) {
16603
16720
  console.log("No active session to save");
16604
16721
  return;
@@ -16612,6 +16729,8 @@ Custom Commands:
16612
16729
  await state.sessionStore.save(state.session);
16613
16730
  console.log(`\u2705 Session saved as "${sessionName}"`);
16614
16731
  break;
16732
+ }
16733
+ case "resume":
16615
16734
  case "sessions": {
16616
16735
  const sessions = await state.sessionStore.list(20);
16617
16736
  if (sessions.length === 0) {
@@ -16800,7 +16919,7 @@ Custom Commands:
16800
16919
  }));
16801
16920
  break;
16802
16921
  }
16803
- case "untrust":
16922
+ case "untrust": {
16804
16923
  if (!state.permissionManager) {
16805
16924
  console.log("Permission manager not initialized");
16806
16925
  return;
@@ -16814,7 +16933,8 @@ Custom Commands:
16814
16933
  await state.configStore.untrustTool(toolToUntrust);
16815
16934
  console.log(`\u2705 Tool '${toolToUntrust}' removed from trusted list`);
16816
16935
  break;
16817
- case "trusted":
16936
+ }
16937
+ case "trusted": {
16818
16938
  if (!state.permissionManager) {
16819
16939
  console.log("Permission manager not initialized");
16820
16940
  return;
@@ -16828,6 +16948,7 @@ Custom Commands:
16828
16948
  }
16829
16949
  console.log("");
16830
16950
  break;
16951
+ }
16831
16952
  case "login":
16832
16953
  setState((prev) => ({ ...prev, showLoginFlow: true }));
16833
16954
  break;
@@ -16836,7 +16957,7 @@ Custom Commands:
16836
16957
  await state.configStore.clearAuthTokens();
16837
16958
  console.log("\u2705 Successfully logged out");
16838
16959
  break;
16839
- case "whoami":
16960
+ case "whoami": {
16840
16961
  const authTokens = await state.configStore.getAuthTokens();
16841
16962
  if (!authTokens) {
16842
16963
  console.log("Not authenticated. Run /login to authenticate.");
@@ -16849,6 +16970,7 @@ Custom Commands:
16849
16970
  console.log(`Expires: ${new Date(authTokens.expiresAt).toLocaleString()}`);
16850
16971
  console.log("");
16851
16972
  break;
16973
+ }
16852
16974
  case "clear":
16853
16975
  case "new": {
16854
16976
  console.clear();