@bike4mind/cli 0.2.7-ja-fix-confluence-table-data-5752.17142 → 0.2.8-cli-bash-tools.17146

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
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-GPTQ7T7Y.js";
8
- import "./chunk-6ATNPERY.js";
9
- import "./chunk-OK3T7VKG.js";
7
+ } from "./chunk-6S7HV2GF.js";
8
+ import "./chunk-F2K7OCDJ.js";
9
+ import "./chunk-RJYG7BUV.js";
10
10
  import {
11
11
  BFLImageService,
12
12
  BaseStorage,
@@ -15,7 +15,7 @@ import {
15
15
  OpenAIBackend,
16
16
  OpenAIImageService,
17
17
  XAIImageService
18
- } from "./chunk-KPBWTYME.js";
18
+ } from "./chunk-SE2T5GED.js";
19
19
  import {
20
20
  Logger
21
21
  } from "./chunk-AMDXHL6S.js";
@@ -73,19 +73,19 @@ import {
73
73
  XAI_IMAGE_MODELS,
74
74
  b4mLLMTools,
75
75
  getMcpProviderMetadata
76
- } from "./chunk-F2LBB4TN.js";
76
+ } from "./chunk-HD4QQGS6.js";
77
77
  import {
78
78
  __require
79
79
  } from "./chunk-PDX44BCA.js";
80
80
 
81
81
  // src/index.tsx
82
- import React13, { useState as useState4, useEffect as useEffect3, useCallback, useRef } from "react";
83
- import { render, Box as Box13, Text as Text12, useApp, useInput as useInput3 } from "ink";
82
+ import React14, { useState as useState5, useEffect as useEffect3, useCallback, useRef } from "react";
83
+ import { render, Box as Box14, Text as Text13, useApp, useInput as useInput4 } from "ink";
84
84
  import { v4 as uuidv49 } from "uuid";
85
85
 
86
86
  // src/components/App.tsx
87
87
  import React9 from "react";
88
- import { Box as Box9, Text as Text8, Static } from "ink";
88
+ import { Box as Box9, Text as Text8 } from "ink";
89
89
 
90
90
  // src/components/StatusBar.tsx
91
91
  import React from "react";
@@ -389,6 +389,11 @@ var COMMANDS = [
389
389
  name: "clear",
390
390
  description: "Start a new session",
391
391
  aliases: ["new"]
392
+ },
393
+ {
394
+ name: "rewind",
395
+ description: "Rewind conversation to a previous point",
396
+ aliases: ["undo"]
392
397
  }
393
398
  ];
394
399
 
@@ -625,12 +630,26 @@ function findAtTrigger(value) {
625
630
  }
626
631
  return null;
627
632
  }
628
- function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = [] }) {
633
+ function InputPrompt({
634
+ onSubmit,
635
+ onImageDetected,
636
+ disabled = false,
637
+ history = [],
638
+ prefillInput,
639
+ onPrefillConsumed
640
+ }) {
629
641
  const [value, setValue] = useState("");
630
642
  const [selectedIndex, setSelectedIndex] = useState(0);
631
643
  const [historyIndex, setHistoryIndex] = useState(-1);
632
644
  const [tempInput, setTempInput] = useState("");
633
645
  const [inputKey, setInputKey] = useState(0);
646
+ useEffect(() => {
647
+ if (prefillInput) {
648
+ setValue(prefillInput);
649
+ setInputKey((prev) => prev + 1);
650
+ onPrefillConsumed?.();
651
+ }
652
+ }, [prefillInput, onPrefillConsumed]);
634
653
  const [fileAutocomplete, setFileAutocomplete] = useState(null);
635
654
  const [fileSelectedIndex, setFileSelectedIndex] = useState(0);
636
655
  const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
@@ -779,7 +798,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
779
798
  value,
780
799
  onChange: handleChange,
781
800
  onSubmit: handleSubmit,
782
- placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
801
+ placeholder: disabled ? "" : "Type your message, /help for commands, or @file to reference",
783
802
  showCursor: !disabled
784
803
  }
785
804
  )), shouldShowCommandAutocomplete && /* @__PURE__ */ React4.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React4.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
@@ -810,6 +829,7 @@ var useCliStore = create((set) => ({
810
829
  addAgentStep: (step) => set((state) => ({
811
830
  agentSteps: [...state.agentSteps, step]
812
831
  })),
832
+ setAgentSteps: (steps) => set({ agentSteps: steps }),
813
833
  clearAgentSteps: () => set({ agentSteps: [] }),
814
834
  // Permission prompt
815
835
  permissionPrompt: null,
@@ -829,7 +849,7 @@ var AgentThinking = React6.memo(function AgentThinking2() {
829
849
  if (!isThinking) {
830
850
  return null;
831
851
  }
832
- return /* @__PURE__ */ React6.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React6.createElement(ThoughtStream, { steps: agentSteps }));
852
+ return /* @__PURE__ */ React6.createElement(Box6, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React6.createElement(ThoughtStream, { steps: agentSteps }));
833
853
  });
834
854
 
835
855
  // src/components/PermissionPrompt.tsx
@@ -1224,7 +1244,9 @@ function App({
1224
1244
  commandHistory = [],
1225
1245
  config,
1226
1246
  availableModels = [],
1227
- onSaveConfig
1247
+ onSaveConfig,
1248
+ prefillInput,
1249
+ onPrefillConsumed
1228
1250
  }) {
1229
1251
  const messages = useCliStore((state) => state.session?.messages || []);
1230
1252
  const sessionName = useCliStore((state) => state.session?.name || "New Session");
@@ -1282,18 +1304,21 @@ ${errorBlock}`;
1282
1304
  canBeTrusted: permissionPrompt.canBeTrusted,
1283
1305
  onResponse: onPermissionResponse
1284
1306
  }
1285
- )) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React9.createElement(Box9, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(MessageItem, { message }))), /* @__PURE__ */ React9.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React9.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React9.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
1307
+ )) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, messages.map((message, index) => /* @__PURE__ */ React9.createElement(Box9, { key: `${message.timestamp}-${message.role}-${index}`, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(MessageItem, { message })))), /* @__PURE__ */ React9.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React9.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React9.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
1286
1308
  InputPrompt,
1287
1309
  {
1288
1310
  onSubmit: handleSubmit,
1289
1311
  onImageDetected,
1290
1312
  disabled: isThinking,
1291
- history: commandHistory
1313
+ history: commandHistory,
1314
+ prefillInput,
1315
+ onPrefillConsumed
1292
1316
  }
1293
1317
  ))));
1294
1318
  }
1295
1319
  function MessageItem({ message }) {
1296
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React9.createElement(Text8, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1320
+ const isUser = message.role === "user";
1321
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: isUser ? "cyan" : "green" }, isUser ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1297
1322
  if (step.type === "thought") {
1298
1323
  return /* @__PURE__ */ React9.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
1299
1324
  }
@@ -1305,7 +1330,7 @@ function MessageItem({ message }) {
1305
1330
  return /* @__PURE__ */ React9.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
1306
1331
  }
1307
1332
  return null;
1308
- }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1333
+ }).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2, marginTop: message.metadata?.steps?.length ? 1 : 0 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React9.createElement(Text8, null, message.content)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1309
1334
  }
1310
1335
 
1311
1336
  // src/components/MessageList.tsx
@@ -1380,9 +1405,78 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
1380
1405
  ), /* @__PURE__ */ React11.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
1381
1406
  }
1382
1407
 
1408
+ // src/components/RewindSelector.tsx
1409
+ import React12, { useState as useState3 } from "react";
1410
+ import { Box as Box12, Text as Text11, useInput as useInput3 } from "ink";
1411
+ import SelectInput3 from "ink-select-input";
1412
+ function RewindSelector({ messages, onSelect, onCancel }) {
1413
+ const [step, setStep] = useState3("selection");
1414
+ const [selectedMessageIndex, setSelectedMessageIndex] = useState3(null);
1415
+ useInput3((input, key) => {
1416
+ if (key.escape) {
1417
+ if (step === "confirmation") {
1418
+ setStep("selection");
1419
+ setSelectedMessageIndex(null);
1420
+ } else {
1421
+ onCancel();
1422
+ }
1423
+ }
1424
+ });
1425
+ const userMessages = messages.map((msg, idx) => ({ msg, originalIndex: idx })).filter(({ msg }) => msg.role === "user");
1426
+ const items = userMessages.map(({ msg, originalIndex }, index) => {
1427
+ const preview = msg.content.length > 50 ? msg.content.substring(0, 50) + "..." : msg.content;
1428
+ const timestamp = new Date(msg.timestamp).toLocaleString();
1429
+ const position = index + 1;
1430
+ return {
1431
+ label: `[${position}] ${timestamp} - ${preview}`,
1432
+ value: originalIndex
1433
+ };
1434
+ });
1435
+ const handleSelectionSelect = (item) => {
1436
+ setSelectedMessageIndex(item.value);
1437
+ setStep("confirmation");
1438
+ };
1439
+ const handleConfirmationSelect = (item) => {
1440
+ if (item.value === "confirm" && selectedMessageIndex !== null) {
1441
+ onSelect(selectedMessageIndex);
1442
+ } else {
1443
+ setStep("selection");
1444
+ setSelectedMessageIndex(null);
1445
+ }
1446
+ };
1447
+ const messagesToRemove = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages.slice(selectedMessageIndex) : [];
1448
+ const tokensToRemove = messagesToRemove.reduce((sum2, msg) => {
1449
+ return sum2 + (msg.metadata?.tokenUsage?.total || 0);
1450
+ }, 0);
1451
+ if (step === "selection") {
1452
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Select a point to rewind to:")), /* @__PURE__ */ React12.createElement(
1453
+ SelectInput3,
1454
+ {
1455
+ items,
1456
+ onSelect: handleSelectionSelect,
1457
+ itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
1458
+ }
1459
+ ), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
1460
+ }
1461
+ const confirmationItems = [
1462
+ { label: "Yes, rewind to this point", value: "confirm" },
1463
+ { label: "No, go back to selection", value: "cancel" }
1464
+ ];
1465
+ const selectedMessage = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages[selectedMessageIndex] : null;
1466
+ const selectedPreview = selectedMessage ? selectedMessage.content.length > 50 ? selectedMessage.content.substring(0, 50) + "..." : selectedMessage.content : "";
1467
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Confirm rewind operation:"), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Message: ", selectedPreview), /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "This will remove ", messagesToRemove.length, " message(s) (", tokensToRemove.toLocaleString(), " tokens)"), /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, "The selected message will be placed in your input for editing.")), /* @__PURE__ */ React12.createElement(
1468
+ SelectInput3,
1469
+ {
1470
+ items: confirmationItems,
1471
+ onSelect: handleConfirmationSelect,
1472
+ itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
1473
+ }
1474
+ ), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
1475
+ }
1476
+
1383
1477
  // src/components/LoginFlow.tsx
1384
- import React12, { useState as useState3, useEffect as useEffect2 } from "react";
1385
- import { Box as Box12, Text as Text11 } from "ink";
1478
+ import React13, { useState as useState4, useEffect as useEffect2 } from "react";
1479
+ import { Box as Box13, Text as Text12 } from "ink";
1386
1480
  import Spinner2 from "ink-spinner";
1387
1481
  import jwt from "jsonwebtoken";
1388
1482
  import open from "open";
@@ -1498,10 +1592,10 @@ var OAuthClient = class {
1498
1592
 
1499
1593
  // src/components/LoginFlow.tsx
1500
1594
  function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
1501
- const [status, setStatus] = useState3("initiating");
1502
- const [deviceFlow, setDeviceFlow] = useState3(null);
1503
- const [statusMessage, setStatusMessage] = useState3("Initiating device authorization...");
1504
- const [error, setError] = useState3(null);
1595
+ const [status, setStatus] = useState4("initiating");
1596
+ const [deviceFlow, setDeviceFlow] = useState4(null);
1597
+ const [statusMessage, setStatusMessage] = useState4("Initiating device authorization...");
1598
+ const [error, setError] = useState4(null);
1505
1599
  useEffect2(() => {
1506
1600
  const runLoginFlow = async () => {
1507
1601
  const oauth = new OAuthClient(apiUrl);
@@ -1545,15 +1639,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
1545
1639
  }
1546
1640
  }, [deviceFlow, status]);
1547
1641
  if (status === "initiating") {
1548
- return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, /* @__PURE__ */ React12.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
1642
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
1549
1643
  }
1550
1644
  if (status === "error") {
1551
- return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: "red" }, error)));
1645
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { color: "red" }, error)));
1552
1646
  }
1553
1647
  if (status === "success") {
1554
- return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "You can now use B4M CLI with your account.")));
1648
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "You can now use B4M CLI with your account.")));
1555
1649
  }
1556
- return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "And enter this code when prompted:")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, /* @__PURE__ */ React12.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
1650
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "And enter this code when prompted:")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
1557
1651
  }
1558
1652
 
1559
1653
  // src/storage/SessionStore.ts
@@ -2439,10 +2533,9 @@ ${options.context}` : this.getSystemPrompt()
2439
2533
  content: query
2440
2534
  }
2441
2535
  ];
2442
- console.log("[DEBUG ReActAgent] Messages array being sent to LLM:");
2443
2536
  messages.forEach((msg, i) => {
2444
2537
  const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
2445
- console.log(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
2538
+ this.context.logger.debug(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
2446
2539
  });
2447
2540
  let finalAnswer = "";
2448
2541
  let reachedMaxIterations = false;
@@ -4835,8 +4928,8 @@ async function processAndStoreImages(images, context) {
4835
4928
  const buffer = await downloadImage(image);
4836
4929
  const fileType = await fileTypeFromBuffer2(buffer);
4837
4930
  const filename = `${uuidv45()}.${fileType?.ext}`;
4838
- const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
4839
- return path13;
4931
+ const path14 = await context.imageGenerateStorage.upload(buffer, filename, {});
4932
+ return path14;
4840
4933
  }));
4841
4934
  }
4842
4935
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -6048,8 +6141,8 @@ async function processAndStoreImage(imageUrl, context) {
6048
6141
  const buffer = await downloadImage2(imageUrl);
6049
6142
  const fileType = await fileTypeFromBuffer3(buffer);
6050
6143
  const filename = `${uuidv46()}.${fileType?.ext}`;
6051
- const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
6052
- return path13;
6144
+ const path14 = await context.imageGenerateStorage.upload(buffer, filename, {});
6145
+ return path14;
6053
6146
  }
6054
6147
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
6055
6148
  await context.onFinish?.("edit_image", storedImagePath);
@@ -8461,6 +8554,381 @@ var deleteFileTool = {
8461
8554
  })
8462
8555
  };
8463
8556
 
8557
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
8558
+ import { spawn } from "child_process";
8559
+ import path11 from "path";
8560
+ var DEFAULT_TIMEOUT_MS = 6e4;
8561
+ var MAX_OUTPUT_SIZE = 100 * 1024;
8562
+ var DANGEROUS_PATTERNS = [
8563
+ // Destructive file operations
8564
+ {
8565
+ pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
8566
+ reason: "Recursive delete with path",
8567
+ block: true
8568
+ },
8569
+ {
8570
+ pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
8571
+ reason: "Force delete without preserve root",
8572
+ block: true
8573
+ },
8574
+ {
8575
+ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
8576
+ reason: "Recursive force delete on root paths",
8577
+ block: true
8578
+ },
8579
+ {
8580
+ pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
8581
+ reason: "Force recursive delete on root paths",
8582
+ block: true
8583
+ },
8584
+ // System-level dangerous commands
8585
+ { pattern: /\bsudo\b/i, reason: "Elevated privileges (sudo)", block: true },
8586
+ { pattern: /\bsu\s+(-|root)/i, reason: "Switch to root user", block: true },
8587
+ { pattern: /\bchmod\s+777\b/i, reason: "Overly permissive chmod", block: false },
8588
+ { pattern: /\bchown\s+-R\s+root/i, reason: "Recursive chown to root", block: true },
8589
+ // Disk/partition operations
8590
+ { pattern: /\bmkfs\b/i, reason: "Filesystem creation", block: true },
8591
+ { pattern: /\bfdisk\b/i, reason: "Disk partitioning", block: true },
8592
+ { pattern: /\bdd\s+.*of=\/dev\//i, reason: "Direct disk write", block: true },
8593
+ // Network attacks
8594
+ { pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i, reason: "Reverse shell attempt", block: true },
8595
+ { pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
8596
+ { pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
8597
+ // Fork bombs and resource exhaustion
8598
+ { pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i, reason: "Fork bomb", block: true },
8599
+ { pattern: /\bwhile\s+true.*do.*done.*&/i, reason: "Infinite loop in background", block: false },
8600
+ // Credential/sensitive data access
8601
+ { pattern: /\/etc\/shadow/i, reason: "Access to shadow file", block: true },
8602
+ { pattern: /\/etc\/passwd.*>/i, reason: "Modifying passwd file", block: true },
8603
+ { pattern: /\baws\s+.*--profile\s+/i, reason: "AWS profile access", block: false },
8604
+ // History/log manipulation
8605
+ { pattern: /\bhistory\s+-c\b/i, reason: "Clearing shell history", block: false },
8606
+ { pattern: />\s*\/var\/log\//i, reason: "Overwriting system logs", block: true },
8607
+ // Dangerous redirects
8608
+ { pattern: />\s*\/dev\/sda/i, reason: "Writing to block device", block: true },
8609
+ { pattern: />\s*\/dev\/null.*2>&1.*</i, reason: "Potentially hiding output", block: false },
8610
+ // Environment manipulation
8611
+ { pattern: /\bexport\s+PATH\s*=\s*[^$]/i, reason: "Overwriting PATH", block: false },
8612
+ { pattern: /\bexport\s+LD_PRELOAD/i, reason: "LD_PRELOAD manipulation", block: true },
8613
+ // Process/system control
8614
+ { pattern: /\bkill\s+-9\s+(-1|1)\b/i, reason: "Killing all processes", block: true },
8615
+ { pattern: /\bkillall\s+-9\b/i, reason: "Force killing processes", block: false },
8616
+ { pattern: /\bshutdown\b/i, reason: "System shutdown", block: true },
8617
+ { pattern: /\breboot\b/i, reason: "System reboot", block: true },
8618
+ { pattern: /\binit\s+[06]\b/i, reason: "System runlevel change", block: true }
8619
+ ];
8620
+ var SAFE_COMMAND_PREFIXES = [
8621
+ "ls",
8622
+ "cat",
8623
+ "head",
8624
+ "tail",
8625
+ "grep",
8626
+ "find",
8627
+ "echo",
8628
+ "pwd",
8629
+ "whoami",
8630
+ "date",
8631
+ "cal",
8632
+ "wc",
8633
+ "sort",
8634
+ "uniq",
8635
+ "cut",
8636
+ "tr",
8637
+ "sed",
8638
+ "awk",
8639
+ "git",
8640
+ "npm",
8641
+ "pnpm",
8642
+ "yarn",
8643
+ "node",
8644
+ "npx",
8645
+ "tsx",
8646
+ "ts-node",
8647
+ "python",
8648
+ "python3",
8649
+ "pip",
8650
+ "pip3",
8651
+ "docker",
8652
+ "docker-compose",
8653
+ "curl",
8654
+ "wget",
8655
+ // These are safe for fetching, blocked when piped to shell
8656
+ "mkdir",
8657
+ "touch",
8658
+ "cp",
8659
+ "mv",
8660
+ // File operations (still require permission)
8661
+ "which",
8662
+ "whereis",
8663
+ "type",
8664
+ "file",
8665
+ "stat",
8666
+ "env",
8667
+ "printenv",
8668
+ "set",
8669
+ "man",
8670
+ "help",
8671
+ "info",
8672
+ "diff",
8673
+ "comm",
8674
+ "cmp",
8675
+ "tar",
8676
+ "zip",
8677
+ "unzip",
8678
+ "gzip",
8679
+ "gunzip",
8680
+ "ssh",
8681
+ "scp",
8682
+ "rsync",
8683
+ // Network tools
8684
+ "make",
8685
+ "cmake",
8686
+ "cargo",
8687
+ "go",
8688
+ "rustc",
8689
+ "gcc",
8690
+ "g++",
8691
+ // Build tools
8692
+ "jest",
8693
+ "vitest",
8694
+ "mocha",
8695
+ "pytest",
8696
+ // Test runners
8697
+ "eslint",
8698
+ "prettier",
8699
+ "tsc"
8700
+ // Linters/formatters
8701
+ ];
8702
+ function checkDangerousPatterns(command) {
8703
+ for (const { pattern, reason, block } of DANGEROUS_PATTERNS) {
8704
+ if (pattern.test(command)) {
8705
+ return { blocked: block, reason };
8706
+ }
8707
+ }
8708
+ return { blocked: false };
8709
+ }
8710
+ function getBaseCommand(command) {
8711
+ const trimmed = command.trim();
8712
+ const match = trimmed.match(/^(\S+)/);
8713
+ return match ? match[1] : "";
8714
+ }
8715
+ function isSafeCommandPrefix(command) {
8716
+ const baseCommand = getBaseCommand(command);
8717
+ return SAFE_COMMAND_PREFIXES.some((safe) => baseCommand === safe || baseCommand.endsWith(`/${safe}`));
8718
+ }
8719
+ async function executeBashCommand(params) {
8720
+ const { command, cwd: relativeCwd, timeout = DEFAULT_TIMEOUT_MS } = params;
8721
+ if (!command || command.trim().length === 0) {
8722
+ return {
8723
+ stdout: "",
8724
+ stderr: "Error: Command cannot be empty",
8725
+ exitCode: 1,
8726
+ timedOut: false,
8727
+ blocked: true,
8728
+ blockedReason: "Empty command"
8729
+ };
8730
+ }
8731
+ const dangerCheck = checkDangerousPatterns(command);
8732
+ if (dangerCheck.blocked) {
8733
+ return {
8734
+ stdout: "",
8735
+ stderr: `Command blocked for safety: ${dangerCheck.reason}`,
8736
+ exitCode: 1,
8737
+ timedOut: false,
8738
+ blocked: true,
8739
+ blockedReason: dangerCheck.reason
8740
+ };
8741
+ }
8742
+ const baseCwd = process.cwd();
8743
+ const targetCwd = relativeCwd ? path11.resolve(baseCwd, relativeCwd) : baseCwd;
8744
+ const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
8745
+ return new Promise((resolve3) => {
8746
+ let stdout = "";
8747
+ let stderr = "";
8748
+ let timedOut = false;
8749
+ const proc = spawn("bash", ["-c", command], {
8750
+ cwd: targetCwd,
8751
+ env: {
8752
+ ...process.env,
8753
+ // Prevent color codes that might be hard to read
8754
+ NO_COLOR: "1",
8755
+ FORCE_COLOR: "0"
8756
+ },
8757
+ stdio: ["ignore", "pipe", "pipe"]
8758
+ });
8759
+ const timeoutId = setTimeout(() => {
8760
+ timedOut = true;
8761
+ proc.kill("SIGTERM");
8762
+ setTimeout(() => {
8763
+ if (!proc.killed) {
8764
+ proc.kill("SIGKILL");
8765
+ }
8766
+ }, 5e3);
8767
+ }, effectiveTimeout);
8768
+ proc.stdout.on("data", (data) => {
8769
+ if (stdout.length < MAX_OUTPUT_SIZE) {
8770
+ stdout += data.toString();
8771
+ if (stdout.length > MAX_OUTPUT_SIZE) {
8772
+ stdout = stdout.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
8773
+ }
8774
+ }
8775
+ });
8776
+ proc.stderr.on("data", (data) => {
8777
+ if (stderr.length < MAX_OUTPUT_SIZE) {
8778
+ stderr += data.toString();
8779
+ if (stderr.length > MAX_OUTPUT_SIZE) {
8780
+ stderr = stderr.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
8781
+ }
8782
+ }
8783
+ });
8784
+ proc.on("close", (exitCode) => {
8785
+ clearTimeout(timeoutId);
8786
+ resolve3({
8787
+ stdout: stdout.trim(),
8788
+ stderr: stderr.trim(),
8789
+ exitCode,
8790
+ timedOut,
8791
+ blocked: false
8792
+ });
8793
+ });
8794
+ proc.on("error", (error) => {
8795
+ clearTimeout(timeoutId);
8796
+ resolve3({
8797
+ stdout: "",
8798
+ stderr: `Failed to execute command: ${error.message}`,
8799
+ exitCode: 1,
8800
+ timedOut: false,
8801
+ blocked: false
8802
+ });
8803
+ });
8804
+ });
8805
+ }
8806
+ function formatResult(result, command) {
8807
+ const parts = [];
8808
+ parts.push(`$ ${command}`);
8809
+ parts.push("");
8810
+ if (result.blocked) {
8811
+ parts.push(`BLOCKED: ${result.blockedReason}`);
8812
+ parts.push("");
8813
+ parts.push("This command was blocked for safety reasons.");
8814
+ parts.push("If you believe this is a false positive, please run the command manually.");
8815
+ return parts.join("\n");
8816
+ }
8817
+ if (result.timedOut) {
8818
+ parts.push("WARNING: Command timed out and was terminated.");
8819
+ parts.push("");
8820
+ }
8821
+ if (result.stdout) {
8822
+ parts.push(result.stdout);
8823
+ }
8824
+ if (result.stderr) {
8825
+ if (result.stdout) {
8826
+ parts.push("");
8827
+ }
8828
+ parts.push("STDERR:");
8829
+ parts.push(result.stderr);
8830
+ }
8831
+ if (result.exitCode !== 0 && result.exitCode !== null) {
8832
+ parts.push("");
8833
+ parts.push(`Exit code: ${result.exitCode}`);
8834
+ }
8835
+ if (!result.stdout && !result.stderr && !result.timedOut) {
8836
+ parts.push("(command completed with no output)");
8837
+ }
8838
+ return parts.join("\n");
8839
+ }
8840
+ var bashExecuteTool = {
8841
+ name: "bash_execute",
8842
+ implementation: (context) => ({
8843
+ toolFn: async (value) => {
8844
+ const params = value;
8845
+ const isSafe = isSafeCommandPrefix(params.command);
8846
+ context.logger.info("Bash: Executing command", {
8847
+ command: params.command,
8848
+ cwd: params.cwd || ".",
8849
+ timeout: params.timeout || DEFAULT_TIMEOUT_MS,
8850
+ isSafeCommand: isSafe
8851
+ });
8852
+ if (context.onStart) {
8853
+ await context.onStart("bash_execute", {
8854
+ command: params.command,
8855
+ cwd: params.cwd
8856
+ });
8857
+ }
8858
+ try {
8859
+ const result = await executeBashCommand(params);
8860
+ const formattedResult = formatResult(result, params.command);
8861
+ context.logger.info("Bash: Command completed", {
8862
+ exitCode: result.exitCode,
8863
+ timedOut: result.timedOut,
8864
+ blocked: result.blocked
8865
+ });
8866
+ if (context.onFinish) {
8867
+ await context.onFinish("bash_execute", {
8868
+ command: params.command,
8869
+ exitCode: result.exitCode,
8870
+ blocked: result.blocked
8871
+ });
8872
+ }
8873
+ return formattedResult;
8874
+ } catch (error) {
8875
+ context.logger.error("Bash: Command failed", error);
8876
+ if (context.onFinish) {
8877
+ await context.onFinish("bash_execute", {
8878
+ command: params.command,
8879
+ error: error instanceof Error ? error.message : "Unknown error"
8880
+ });
8881
+ }
8882
+ throw error;
8883
+ }
8884
+ },
8885
+ toolSchema: {
8886
+ name: "bash_execute",
8887
+ description: `Execute a bash command in the terminal. Use this for running shell commands, scripts, build tools, git operations, and other CLI tasks.
8888
+
8889
+ SAFETY NOTES:
8890
+ - Commands are executed in the current working directory (or specified cwd)
8891
+ - Dangerous commands (sudo, rm -rf /, etc.) are automatically blocked
8892
+ - Commands have a default timeout of 60 seconds (max 5 minutes)
8893
+ - Output is limited to prevent overwhelming responses
8894
+ - This tool ALWAYS requires user permission before execution
8895
+
8896
+ COMMON USE CASES:
8897
+ - Running build commands: npm run build, make, cargo build
8898
+ - Git operations: git status, git log, git diff
8899
+ - Viewing system info: ls, pwd, cat, head, tail
8900
+ - Running tests: npm test, pytest, cargo test
8901
+ - Package management: npm install, pip install
8902
+ - File operations: mkdir, cp, mv (with permission)
8903
+
8904
+ BLOCKED OPERATIONS:
8905
+ - sudo and privilege escalation
8906
+ - Recursive deletes on system paths
8907
+ - Direct disk operations
8908
+ - Fork bombs and resource exhaustion
8909
+ - Piping remote scripts to shell`,
8910
+ parameters: {
8911
+ type: "object",
8912
+ properties: {
8913
+ command: {
8914
+ type: "string",
8915
+ description: "The bash command to execute. Can include pipes, redirects, and chained commands."
8916
+ },
8917
+ cwd: {
8918
+ type: "string",
8919
+ description: "Working directory for the command (optional). Can be relative or absolute path. Defaults to current directory."
8920
+ },
8921
+ timeout: {
8922
+ type: "number",
8923
+ description: "Timeout in milliseconds (optional, default: 60000, max: 300000). Command will be terminated if it exceeds this time."
8924
+ }
8925
+ },
8926
+ required: ["command"]
8927
+ }
8928
+ }
8929
+ })
8930
+ };
8931
+
8464
8932
  // ../../b4m-core/packages/services/dist/src/llm/tools/index.js
8465
8933
  var tools = {
8466
8934
  dice_roll: diceRollTool,
@@ -8488,7 +8956,9 @@ var tools = {
8488
8956
  create_file: createFileTool,
8489
8957
  glob_files: globFilesTool,
8490
8958
  grep_search: grepSearchTool,
8491
- delete_file: deleteFileTool
8959
+ delete_file: deleteFileTool,
8960
+ // Shell execution
8961
+ bash_execute: bashExecuteTool
8492
8962
  };
8493
8963
  var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
8494
8964
  const context = {
@@ -8713,6 +9183,17 @@ var MementoEvalResponseSchema = z142.object({
8713
9183
  // ../../b4m-core/packages/services/dist/src/auth/AccessTokenGeneratorService.js
8714
9184
  import jwt2 from "jsonwebtoken";
8715
9185
 
9186
+ // ../../b4m-core/packages/services/dist/src/cliTools/types.js
9187
+ var ToolErrorType;
9188
+ (function(ToolErrorType2) {
9189
+ ToolErrorType2["INVALID_INPUT"] = "INVALID_INPUT";
9190
+ ToolErrorType2["API_KEY_MISSING"] = "API_KEY_MISSING";
9191
+ ToolErrorType2["EXTERNAL_API_ERROR"] = "EXTERNAL_API_ERROR";
9192
+ ToolErrorType2["RATE_LIMIT"] = "RATE_LIMIT";
9193
+ ToolErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
9194
+ ToolErrorType2["UNKNOWN"] = "UNKNOWN";
9195
+ })(ToolErrorType || (ToolErrorType = {}));
9196
+
8716
9197
  // src/utils/diffPreview.ts
8717
9198
  import * as Diff from "diff";
8718
9199
  import { readFile } from "fs/promises";
@@ -8763,74 +9244,406 @@ Last modified: ${stats.mtime.toLocaleString()}`;
8763
9244
  }
8764
9245
  }
8765
9246
 
8766
- // src/utils/toolsAdapter.ts
8767
- var NoOpStorage = class extends BaseStorage {
8768
- async upload(input, destination, options) {
8769
- return `/tmp/${destination}`;
8770
- }
8771
- async download(path13) {
8772
- throw new Error("Download not supported in CLI");
8773
- }
8774
- async delete(path13) {
9247
+ // src/utils/Logger.ts
9248
+ import fs10 from "fs/promises";
9249
+ import path12 from "path";
9250
+ import os from "os";
9251
+ var Logger2 = class _Logger {
9252
+ constructor() {
9253
+ this.logFilePath = null;
9254
+ this.sessionId = null;
9255
+ this.fileLoggingEnabled = true;
9256
+ this.consoleVerbose = false;
8775
9257
  }
8776
- async getSignedUrl(path13) {
8777
- return `/tmp/${path13}`;
9258
+ static {
9259
+ this.instance = null;
8778
9260
  }
8779
- getPublicUrl(path13) {
8780
- return `/tmp/${path13}`;
9261
+ static getInstance() {
9262
+ if (!_Logger.instance) {
9263
+ _Logger.instance = new _Logger();
9264
+ }
9265
+ return _Logger.instance;
8781
9266
  }
8782
- async getPreview(path13) {
8783
- return `/tmp/${path13}`;
9267
+ /**
9268
+ * Initialize the logger with a session ID
9269
+ */
9270
+ async initialize(sessionId) {
9271
+ this.sessionId = sessionId;
9272
+ const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
9273
+ await fs10.mkdir(debugDir, { recursive: true });
9274
+ this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
9275
+ await this.writeToFile("INFO", "=== CLI SESSION START ===");
8784
9276
  }
8785
- async getMetadata(path13) {
8786
- return { size: 0, contentType: "application/octet-stream" };
9277
+ /**
9278
+ * Set whether verbose console logging is enabled
9279
+ */
9280
+ setVerbose(enabled) {
9281
+ this.consoleVerbose = enabled;
8787
9282
  }
8788
- };
8789
- var CliLogger = class extends Logger {
8790
- constructor() {
8791
- super({ logInJson: false });
9283
+ /**
9284
+ * Set whether file logging is enabled
9285
+ */
9286
+ setFileLoggingEnabled(enabled) {
9287
+ this.fileLoggingEnabled = enabled;
8792
9288
  }
8793
- // Override all logging methods to be silent
8794
- info(...args) {
9289
+ /**
9290
+ * DEBUG level - verbose-only console, always file
9291
+ */
9292
+ debug(message) {
9293
+ this.writeToFile("DEBUG", message).catch(() => {
9294
+ });
9295
+ if (this.consoleVerbose) {
9296
+ console.log(message);
9297
+ }
8795
9298
  }
8796
- warn(...args) {
9299
+ /**
9300
+ * INFO level - always shown to user
9301
+ */
9302
+ info(message) {
9303
+ this.writeToFile("INFO", message).catch(() => {
9304
+ });
9305
+ console.log(message);
8797
9306
  }
8798
- error(...args) {
9307
+ /**
9308
+ * WARN level - always shown to user
9309
+ */
9310
+ warn(message) {
9311
+ this.writeToFile("WARN", message).catch(() => {
9312
+ });
9313
+ console.warn(message);
8799
9314
  }
8800
- debug(...args) {
9315
+ /**
9316
+ * ERROR level - always shown to user
9317
+ */
9318
+ error(message, err) {
9319
+ this.writeToFile("ERROR", message).catch(() => {
9320
+ });
9321
+ if (err) {
9322
+ this.logErrorDetailsToFile(err).catch(() => {
9323
+ });
9324
+ }
9325
+ console.error(message);
8801
9326
  }
8802
- log(...args) {
9327
+ /**
9328
+ * Write log entry to file
9329
+ */
9330
+ async writeToFile(level, message) {
9331
+ if (!this.fileLoggingEnabled || !this.logFilePath) {
9332
+ return;
9333
+ }
9334
+ try {
9335
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
9336
+ const logEntry = `[${timestamp}] [${level}] ${message}
9337
+ `;
9338
+ await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
9339
+ } catch (error) {
9340
+ console.error("File logging failed:", error);
9341
+ }
8803
9342
  }
8804
- };
8805
- function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore) {
8806
- const originalFn = tool.toolFn;
8807
- const toolName = tool.toolSchema.name;
8808
- return {
8809
- ...tool,
8810
- toolFn: async (args) => {
8811
- if (!permissionManager.needsPermission(toolName)) {
8812
- const result2 = await originalFn(args);
8813
- agentContext.observationQueue.push({ toolName, result: result2 });
8814
- return result2;
8815
- }
8816
- let preview;
8817
- if (toolName === "edit_file" && args?.path && args?.content) {
8818
- try {
8819
- preview = await generateFileDiffPreview({
8820
- path: args.path,
8821
- content: args.content
8822
- });
8823
- } catch (error) {
8824
- preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
9343
+ /**
9344
+ * Log error details to file
9345
+ */
9346
+ async logErrorDetailsToFile(err) {
9347
+ if (!this.fileLoggingEnabled || !this.logFilePath) {
9348
+ return;
9349
+ }
9350
+ try {
9351
+ if (err && typeof err === "object" && "response" in err && err.response) {
9352
+ const response = err.response;
9353
+ const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
9354
+ await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
9355
+ await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
9356
+ await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
9357
+ if (response.data) {
9358
+ const errorText = this.extractErrorMessage(response.data);
9359
+ if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
9360
+ await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
9361
+ const parsedError = this.parseHtmlError(errorText);
9362
+ if (parsedError) {
9363
+ await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
9364
+ }
9365
+ await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
9366
+ } else {
9367
+ const preview = this.truncate(errorText, 500);
9368
+ await this.writeToFile("ERROR", ` Response: ${preview}`);
9369
+ }
9370
+ } else {
9371
+ await this.writeToFile("ERROR", ` Response: (no data)`);
8825
9372
  }
8826
- } else if (toolName === "create_file" && args?.path && args?.content) {
8827
- try {
8828
- preview = await generateFileDiffPreview({
8829
- path: args.path,
8830
- content: args.content
8831
- });
8832
- } catch (error) {
8833
- preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
9373
+ }
9374
+ if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
9375
+ const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
9376
+ await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
9377
+ } else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
9378
+ await this.writeToFile("ERROR", ` Message: ${err.message}`);
9379
+ }
9380
+ } catch (error) {
9381
+ }
9382
+ }
9383
+ /**
9384
+ * Safely stringify object, handling circular references
9385
+ */
9386
+ safeStringify(obj) {
9387
+ try {
9388
+ return JSON.stringify(obj);
9389
+ } catch (error) {
9390
+ if (error instanceof Error && error.message.includes("circular")) {
9391
+ try {
9392
+ const seen = /* @__PURE__ */ new WeakSet();
9393
+ return JSON.stringify(obj, (key, value) => {
9394
+ if (typeof value === "object" && value !== null) {
9395
+ if (seen.has(value)) {
9396
+ return "[Circular]";
9397
+ }
9398
+ seen.add(value);
9399
+ }
9400
+ return value;
9401
+ });
9402
+ } catch {
9403
+ return "[Unable to stringify]";
9404
+ }
9405
+ }
9406
+ return "[Stringify error]";
9407
+ }
9408
+ }
9409
+ /**
9410
+ * Extract readable text from error response data
9411
+ */
9412
+ extractErrorMessage(data) {
9413
+ if (Buffer.isBuffer(data)) {
9414
+ return data.toString("utf-8");
9415
+ }
9416
+ if (typeof data === "string") {
9417
+ return data;
9418
+ }
9419
+ if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
9420
+ const chunks = [];
9421
+ for (const chunk of data._readableState.buffer) {
9422
+ if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
9423
+ chunks.push(Buffer.from(chunk.data));
9424
+ } else if (Buffer.isBuffer(chunk)) {
9425
+ chunks.push(chunk);
9426
+ } else if (chunk && typeof chunk === "object" && "data" in chunk) {
9427
+ if (Buffer.isBuffer(chunk.data)) {
9428
+ chunks.push(chunk.data);
9429
+ } else if (Array.isArray(chunk.data)) {
9430
+ chunks.push(Buffer.from(chunk.data));
9431
+ }
9432
+ }
9433
+ }
9434
+ if (chunks.length > 0) {
9435
+ return Buffer.concat(chunks).toString("utf-8");
9436
+ }
9437
+ }
9438
+ return this.safeStringify(data);
9439
+ }
9440
+ /**
9441
+ * Parse HTML error page to extract error message
9442
+ */
9443
+ parseHtmlError(html) {
9444
+ const titleMatch = html.match(/<title>(.*?)<\/title>/i);
9445
+ const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
9446
+ const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
9447
+ if (titleMatch && titleMatch[1] !== "Error") {
9448
+ return titleMatch[1].trim();
9449
+ }
9450
+ if (h1Match) {
9451
+ return h1Match[1].trim();
9452
+ }
9453
+ if (bodyMatch) {
9454
+ const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
9455
+ return text.substring(0, 200);
9456
+ }
9457
+ return null;
9458
+ }
9459
+ /**
9460
+ * Truncate string to max length with ellipsis
9461
+ */
9462
+ truncate(str, maxLength) {
9463
+ if (str.length <= maxLength) {
9464
+ return str;
9465
+ }
9466
+ return str.substring(0, maxLength) + "... [truncated]";
9467
+ }
9468
+ /**
9469
+ * Format bytes to human-readable size
9470
+ */
9471
+ formatBytes(bytes) {
9472
+ if (bytes === 0) return "0 B";
9473
+ const k = 1024;
9474
+ const sizes = ["B", "KB", "MB", "GB"];
9475
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
9476
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
9477
+ }
9478
+ /**
9479
+ * Clean up old debug logs (older than 30 days)
9480
+ */
9481
+ async cleanupOldLogs() {
9482
+ if (!this.fileLoggingEnabled) return;
9483
+ try {
9484
+ const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
9485
+ const files = await fs10.readdir(debugDir);
9486
+ const now = Date.now();
9487
+ const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
9488
+ for (const file of files) {
9489
+ const filePath = path12.join(debugDir, file);
9490
+ const stats = await fs10.stat(filePath);
9491
+ if (stats.mtime.getTime() < thirtyDaysAgo) {
9492
+ await fs10.unlink(filePath);
9493
+ }
9494
+ }
9495
+ } catch (error) {
9496
+ console.error("Failed to cleanup old logs:", error);
9497
+ }
9498
+ }
9499
+ };
9500
+ var logger = Logger2.getInstance();
9501
+
9502
+ // src/llm/ServerToolExecutor.ts
9503
+ var ServerToolExecutor = class {
9504
+ constructor(apiClient) {
9505
+ this.toolsEndpoint = "/api/ai/v1/tools";
9506
+ this.apiClient = apiClient;
9507
+ }
9508
+ /**
9509
+ * Execute a tool server-side
9510
+ *
9511
+ * @param toolName - Name of the tool to execute (e.g., 'weather_info', 'web_search')
9512
+ * @param input - Tool input parameters
9513
+ * @returns Tool execution result (content or error)
9514
+ */
9515
+ async executeTool(toolName, input) {
9516
+ logger.debug(`[ServerToolExecutor] Executing tool: ${toolName}`);
9517
+ logger.debug(`[ServerToolExecutor] Endpoint: ${this.toolsEndpoint}`);
9518
+ logger.debug(`[ServerToolExecutor] Input: ${JSON.stringify(input)}`);
9519
+ try {
9520
+ const result = await this.apiClient.post(this.toolsEndpoint, {
9521
+ toolName,
9522
+ input
9523
+ });
9524
+ logger.debug(`[ServerToolExecutor] Response: ${JSON.stringify(result)}`);
9525
+ if (!result.success) {
9526
+ const errorMessage = result.error || "Tool execution failed";
9527
+ logger.error(`[ServerToolExecutor] Tool ${toolName} failed: ${errorMessage}`);
9528
+ return `Error executing ${toolName}: ${errorMessage}`;
9529
+ }
9530
+ logger.debug(`[ServerToolExecutor] Tool ${toolName} succeeded in ${result.executionTimeMs}ms`);
9531
+ return result.content || "";
9532
+ } catch (error) {
9533
+ const err = error;
9534
+ const errorMessage = err?.response?.data?.error || err?.message || "Unknown error";
9535
+ const status = err?.response?.status || "unknown";
9536
+ logger.error(`[ServerToolExecutor] Request failed for ${toolName}: ${errorMessage} (status: ${status})`);
9537
+ logger.error(`[ServerToolExecutor] Full error: ${JSON.stringify(err, null, 2)}`);
9538
+ return `Error executing ${toolName}: ${errorMessage}`;
9539
+ }
9540
+ }
9541
+ };
9542
+
9543
+ // src/llm/ToolRouter.ts
9544
+ var SERVER_TOOLS = ["weather_info", "web_search"];
9545
+ var LOCAL_TOOLS = [
9546
+ "file_read",
9547
+ "create_file",
9548
+ "glob_files",
9549
+ "grep_search",
9550
+ "delete_file",
9551
+ "edit_file",
9552
+ "dice_roll",
9553
+ "math_evaluate",
9554
+ "current_datetime"
9555
+ ];
9556
+ function isServerTool(toolName) {
9557
+ return SERVER_TOOLS.includes(toolName);
9558
+ }
9559
+ function isLocalTool(toolName) {
9560
+ return LOCAL_TOOLS.includes(toolName);
9561
+ }
9562
+ async function executeTool(toolName, input, apiClient, localToolFn) {
9563
+ if (isServerTool(toolName)) {
9564
+ logger.debug(`[ToolRouter] Routing ${toolName} to server`);
9565
+ const executor = new ServerToolExecutor(apiClient);
9566
+ return await executor.executeTool(toolName, input);
9567
+ } else if (isLocalTool(toolName)) {
9568
+ logger.debug(`[ToolRouter] Executing ${toolName} locally`);
9569
+ if (!localToolFn) {
9570
+ throw new Error(`Local tool ${toolName} has no implementation`);
9571
+ }
9572
+ const result = await localToolFn(input);
9573
+ return typeof result === "string" ? result : JSON.stringify(result);
9574
+ } else {
9575
+ throw new Error(`Unknown tool: ${toolName}`);
9576
+ }
9577
+ }
9578
+
9579
+ // src/utils/toolsAdapter.ts
9580
+ var NoOpStorage = class extends BaseStorage {
9581
+ async upload(input, destination, options) {
9582
+ return `/tmp/${destination}`;
9583
+ }
9584
+ async download(path14) {
9585
+ throw new Error("Download not supported in CLI");
9586
+ }
9587
+ async delete(path14) {
9588
+ }
9589
+ async getSignedUrl(path14) {
9590
+ return `/tmp/${path14}`;
9591
+ }
9592
+ getPublicUrl(path14) {
9593
+ return `/tmp/${path14}`;
9594
+ }
9595
+ async getPreview(path14) {
9596
+ return `/tmp/${path14}`;
9597
+ }
9598
+ async getMetadata(path14) {
9599
+ return { size: 0, contentType: "application/octet-stream" };
9600
+ }
9601
+ };
9602
+ var CliLogger = class extends Logger {
9603
+ constructor() {
9604
+ super({ logInJson: false });
9605
+ }
9606
+ // Override all logging methods to be silent
9607
+ info(...args) {
9608
+ }
9609
+ warn(...args) {
9610
+ }
9611
+ error(...args) {
9612
+ }
9613
+ debug(...args) {
9614
+ }
9615
+ log(...args) {
9616
+ }
9617
+ };
9618
+ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
9619
+ const originalFn = tool.toolFn;
9620
+ const toolName = tool.toolSchema.name;
9621
+ return {
9622
+ ...tool,
9623
+ toolFn: async (args) => {
9624
+ if (!permissionManager.needsPermission(toolName)) {
9625
+ const result2 = await executeTool(toolName, args, apiClient, originalFn);
9626
+ agentContext.observationQueue.push({ toolName, result: result2 });
9627
+ return result2;
9628
+ }
9629
+ let preview;
9630
+ if (toolName === "edit_file" && args?.path && args?.content) {
9631
+ try {
9632
+ preview = await generateFileDiffPreview({
9633
+ path: args.path,
9634
+ content: args.content
9635
+ });
9636
+ } catch (error) {
9637
+ preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
9638
+ }
9639
+ } else if (toolName === "create_file" && args?.path && args?.content) {
9640
+ try {
9641
+ preview = await generateFileDiffPreview({
9642
+ path: args.path,
9643
+ content: args.content
9644
+ });
9645
+ } catch (error) {
9646
+ preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
8834
9647
  }
8835
9648
  } else if (toolName === "delete_file" && args?.path) {
8836
9649
  try {
@@ -8840,6 +9653,10 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
8840
9653
  } catch (error) {
8841
9654
  preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
8842
9655
  }
9656
+ } else if (toolName === "bash_execute" && args?.command) {
9657
+ const cwd = args.cwd ? ` (in ${args.cwd})` : "";
9658
+ const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
9659
+ preview = `$ ${args.command}${cwd}${timeout}`;
8843
9660
  }
8844
9661
  const response = await showPermissionPrompt(toolName, args, preview);
8845
9662
  if (response.action === "deny") {
@@ -8866,13 +9683,13 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
8866
9683
  }
8867
9684
  }
8868
9685
  }
8869
- const result = await originalFn(args);
9686
+ const result = await executeTool(toolName, args, apiClient, originalFn);
8870
9687
  agentContext.observationQueue.push({ toolName, result });
8871
9688
  return result;
8872
9689
  }
8873
9690
  };
8874
9691
  }
8875
- function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, toolApiKeys) {
9692
+ function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
8876
9693
  const logger2 = new CliLogger();
8877
9694
  const storage = new NoOpStorage();
8878
9695
  const user = {
@@ -8889,18 +9706,6 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
8889
9706
  };
8890
9707
  const mockAdminSettings = {
8891
9708
  findBySettingName: async (settingName) => {
8892
- if (settingName === "OpenWeatherKey") {
8893
- const key = toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY;
8894
- if (key) {
8895
- return { settingValue: key };
8896
- }
8897
- }
8898
- if (settingName === "SerperKey") {
8899
- const key = toolApiKeys?.serper || process.env.SERPER_API_KEY;
8900
- if (key) {
8901
- return { settingValue: key };
8902
- }
8903
- }
8904
9709
  return null;
8905
9710
  }
8906
9711
  };
@@ -8910,27 +9715,24 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
8910
9715
  adminSettings: mockAdminSettings
8911
9716
  }
8912
9717
  };
8913
- const hasSerperKey = !!(toolApiKeys?.serper || process.env.SERPER_API_KEY);
8914
- const hasOpenWeatherKey = !!(toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY);
8915
9718
  const toolConfig = {
8916
- // Enable these tools for CLI (no external API keys needed)
9719
+ // Local-only tools (no external API keys needed)
8917
9720
  dice_roll: {},
8918
9721
  math_evaluate: {},
8919
9722
  current_datetime: {},
8920
9723
  prompt_enhancement: {},
8921
- // File operation tools (CLI-specific)
9724
+ // File operation tools (CLI-specific, local execution)
8922
9725
  file_read: {},
8923
9726
  create_file: {},
8924
9727
  glob_files: {},
8925
9728
  grep_search: {},
8926
9729
  delete_file: {},
8927
- // Tools requiring API keys (only include if API key is available)
8928
- ...hasSerperKey && { web_search: {} },
8929
- // Requires SERPER_API_KEY
8930
- ...hasOpenWeatherKey && { weather_info: {} },
8931
- // Requires OPENWEATHER_API_KEY
8932
- ...hasSerperKey && { deep_research: {} }
8933
- // Requires SERPER_API_KEY
9730
+ // Shell execution (always requires permission)
9731
+ bash_execute: {},
9732
+ // Server-side tools (executed via /api/ai/v1/tools Lambda)
9733
+ // Routed by ToolRouter to use B4M company API keys
9734
+ weather_info: {},
9735
+ web_search: {}
8934
9736
  // Disable web-only tools
8935
9737
  // blog_publish: undefined,
8936
9738
  // blog_edit: undefined,
@@ -8940,6 +9742,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
8940
9742
  // mermaid_chart: undefined,
8941
9743
  // recharts: undefined,
8942
9744
  // edit_file: undefined,
9745
+ // deep_research: undefined,
8943
9746
  };
8944
9747
  const toolsMap = generateTools(
8945
9748
  userId,
@@ -8957,7 +9760,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
8957
9760
  model
8958
9761
  );
8959
9762
  const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
8960
- ([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore)
9763
+ ([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
8961
9764
  );
8962
9765
  return { tools: tools2, agentContext };
8963
9766
  }
@@ -8991,6 +9794,7 @@ var DEFAULT_TOOL_CATEGORIES = {
8991
9794
  create_file: "prompt_always",
8992
9795
  delete_file: "prompt_always",
8993
9796
  shell_execute: "prompt_always",
9797
+ bash_execute: "prompt_always",
8994
9798
  git_commit: "prompt_always",
8995
9799
  git_push: "prompt_always",
8996
9800
  // ===== PROMPT DEFAULT (Repository read tools) =====
@@ -9067,350 +9871,95 @@ var PermissionManager = class {
9067
9871
  if (!canTrustTool(toolName, categoryMap)) {
9068
9872
  return false;
9069
9873
  }
9070
- this.trustedTools.add(toolName);
9071
- return true;
9072
- }
9073
- /**
9074
- * Remove a tool from the trusted list
9075
- */
9076
- untrustTool(toolName) {
9077
- this.trustedTools.delete(toolName);
9078
- }
9079
- /**
9080
- * Get list of all trusted tools
9081
- */
9082
- getTrustedTools() {
9083
- return Array.from(this.trustedTools).sort();
9084
- }
9085
- /**
9086
- * Check if a tool is currently trusted
9087
- */
9088
- isTrusted(toolName) {
9089
- return this.trustedTools.has(toolName);
9090
- }
9091
- /**
9092
- * Get the category for a tool
9093
- */
9094
- getCategory(toolName) {
9095
- const categoryMap = Object.fromEntries(this.customCategories);
9096
- return getToolCategory(toolName, categoryMap);
9097
- }
9098
- /**
9099
- * Check if a tool can be trusted (not in prompt_always category or denied by project)
9100
- */
9101
- canBeTrusted(toolName) {
9102
- const categoryMap = Object.fromEntries(this.customCategories);
9103
- if (this.deniedTools.has(toolName)) {
9104
- return false;
9105
- }
9106
- return canTrustTool(toolName, categoryMap);
9107
- }
9108
- /**
9109
- * Check if a tool is denied by project config
9110
- */
9111
- isDenied(toolName) {
9112
- return this.deniedTools.has(toolName);
9113
- }
9114
- /**
9115
- * Get list of denied tools
9116
- */
9117
- getDeniedTools() {
9118
- return Array.from(this.deniedTools).sort();
9119
- }
9120
- /**
9121
- * Clear all trusted tools
9122
- */
9123
- clearTrustedTools() {
9124
- this.trustedTools.clear();
9125
- }
9126
- /**
9127
- * Get statistics about permissions
9128
- */
9129
- getStats() {
9130
- return {
9131
- trustedCount: this.trustedTools.size,
9132
- autoApproveCount: 0,
9133
- // TODO: Would need full tool list
9134
- promptAlwaysCount: 0,
9135
- promptDefaultCount: 0
9136
- };
9137
- }
9138
- };
9139
-
9140
- // src/utils/apiUrl.ts
9141
- var BIKE4MIND_URL = "https://app.bike4mind.com";
9142
- function getApiUrl(configApiConfig) {
9143
- if (configApiConfig?.customUrl) {
9144
- return configApiConfig.customUrl;
9145
- }
9146
- return BIKE4MIND_URL;
9147
- }
9148
- function getEnvironmentName(configApiConfig) {
9149
- if (configApiConfig?.customUrl) {
9150
- return "Self-Hosted";
9151
- }
9152
- return "Bike4Mind";
9153
- }
9154
-
9155
- // src/utils/Logger.ts
9156
- import fs10 from "fs/promises";
9157
- import path11 from "path";
9158
- import os from "os";
9159
- var Logger2 = class _Logger {
9160
- constructor() {
9161
- this.logFilePath = null;
9162
- this.sessionId = null;
9163
- this.fileLoggingEnabled = true;
9164
- this.consoleVerbose = false;
9165
- }
9166
- static {
9167
- this.instance = null;
9168
- }
9169
- static getInstance() {
9170
- if (!_Logger.instance) {
9171
- _Logger.instance = new _Logger();
9172
- }
9173
- return _Logger.instance;
9174
- }
9175
- /**
9176
- * Initialize the logger with a session ID
9177
- */
9178
- async initialize(sessionId) {
9179
- this.sessionId = sessionId;
9180
- const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9181
- await fs10.mkdir(debugDir, { recursive: true });
9182
- this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
9183
- await this.writeToFile("INFO", "=== CLI SESSION START ===");
9184
- }
9185
- /**
9186
- * Set whether verbose console logging is enabled
9187
- */
9188
- setVerbose(enabled) {
9189
- this.consoleVerbose = enabled;
9190
- }
9191
- /**
9192
- * Set whether file logging is enabled
9193
- */
9194
- setFileLoggingEnabled(enabled) {
9195
- this.fileLoggingEnabled = enabled;
9196
- }
9197
- /**
9198
- * DEBUG level - verbose-only console, always file
9199
- */
9200
- debug(message) {
9201
- this.writeToFile("DEBUG", message).catch(() => {
9202
- });
9203
- if (this.consoleVerbose) {
9204
- console.log(message);
9205
- }
9206
- }
9207
- /**
9208
- * INFO level - always shown to user
9209
- */
9210
- info(message) {
9211
- this.writeToFile("INFO", message).catch(() => {
9212
- });
9213
- console.log(message);
9214
- }
9215
- /**
9216
- * WARN level - always shown to user
9217
- */
9218
- warn(message) {
9219
- this.writeToFile("WARN", message).catch(() => {
9220
- });
9221
- console.warn(message);
9222
- }
9223
- /**
9224
- * ERROR level - always shown to user
9225
- */
9226
- error(message, err) {
9227
- this.writeToFile("ERROR", message).catch(() => {
9228
- });
9229
- if (err) {
9230
- this.logErrorDetailsToFile(err).catch(() => {
9231
- });
9232
- }
9233
- console.error(message);
9234
- }
9235
- /**
9236
- * Write log entry to file
9237
- */
9238
- async writeToFile(level, message) {
9239
- if (!this.fileLoggingEnabled || !this.logFilePath) {
9240
- return;
9241
- }
9242
- try {
9243
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
9244
- const logEntry = `[${timestamp}] [${level}] ${message}
9245
- `;
9246
- await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
9247
- } catch (error) {
9248
- console.error("File logging failed:", error);
9249
- }
9874
+ this.trustedTools.add(toolName);
9875
+ return true;
9250
9876
  }
9251
9877
  /**
9252
- * Log error details to file
9878
+ * Remove a tool from the trusted list
9253
9879
  */
9254
- async logErrorDetailsToFile(err) {
9255
- if (!this.fileLoggingEnabled || !this.logFilePath) {
9256
- return;
9257
- }
9258
- try {
9259
- if (err && typeof err === "object" && "response" in err && err.response) {
9260
- const response = err.response;
9261
- const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
9262
- await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
9263
- await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
9264
- await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
9265
- if (response.data) {
9266
- const errorText = this.extractErrorMessage(response.data);
9267
- if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
9268
- await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
9269
- const parsedError = this.parseHtmlError(errorText);
9270
- if (parsedError) {
9271
- await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
9272
- }
9273
- await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
9274
- } else {
9275
- const preview = this.truncate(errorText, 500);
9276
- await this.writeToFile("ERROR", ` Response: ${preview}`);
9277
- }
9278
- } else {
9279
- await this.writeToFile("ERROR", ` Response: (no data)`);
9280
- }
9281
- }
9282
- if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
9283
- const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
9284
- await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
9285
- } else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
9286
- await this.writeToFile("ERROR", ` Message: ${err.message}`);
9287
- }
9288
- } catch (error) {
9289
- }
9880
+ untrustTool(toolName) {
9881
+ this.trustedTools.delete(toolName);
9290
9882
  }
9291
9883
  /**
9292
- * Safely stringify object, handling circular references
9884
+ * Get list of all trusted tools
9293
9885
  */
9294
- safeStringify(obj) {
9295
- try {
9296
- return JSON.stringify(obj);
9297
- } catch (error) {
9298
- if (error instanceof Error && error.message.includes("circular")) {
9299
- try {
9300
- const seen = /* @__PURE__ */ new WeakSet();
9301
- return JSON.stringify(obj, (key, value) => {
9302
- if (typeof value === "object" && value !== null) {
9303
- if (seen.has(value)) {
9304
- return "[Circular]";
9305
- }
9306
- seen.add(value);
9307
- }
9308
- return value;
9309
- });
9310
- } catch {
9311
- return "[Unable to stringify]";
9312
- }
9313
- }
9314
- return "[Stringify error]";
9315
- }
9886
+ getTrustedTools() {
9887
+ return Array.from(this.trustedTools).sort();
9316
9888
  }
9317
9889
  /**
9318
- * Extract readable text from error response data
9890
+ * Check if a tool is currently trusted
9319
9891
  */
9320
- extractErrorMessage(data) {
9321
- if (Buffer.isBuffer(data)) {
9322
- return data.toString("utf-8");
9323
- }
9324
- if (typeof data === "string") {
9325
- return data;
9326
- }
9327
- if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
9328
- const chunks = [];
9329
- for (const chunk of data._readableState.buffer) {
9330
- if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
9331
- chunks.push(Buffer.from(chunk.data));
9332
- } else if (Buffer.isBuffer(chunk)) {
9333
- chunks.push(chunk);
9334
- } else if (chunk && typeof chunk === "object" && "data" in chunk) {
9335
- if (Buffer.isBuffer(chunk.data)) {
9336
- chunks.push(chunk.data);
9337
- } else if (Array.isArray(chunk.data)) {
9338
- chunks.push(Buffer.from(chunk.data));
9339
- }
9340
- }
9341
- }
9342
- if (chunks.length > 0) {
9343
- return Buffer.concat(chunks).toString("utf-8");
9344
- }
9345
- }
9346
- return this.safeStringify(data);
9892
+ isTrusted(toolName) {
9893
+ return this.trustedTools.has(toolName);
9347
9894
  }
9348
9895
  /**
9349
- * Parse HTML error page to extract error message
9896
+ * Get the category for a tool
9350
9897
  */
9351
- parseHtmlError(html) {
9352
- const titleMatch = html.match(/<title>(.*?)<\/title>/i);
9353
- const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
9354
- const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
9355
- if (titleMatch && titleMatch[1] !== "Error") {
9356
- return titleMatch[1].trim();
9357
- }
9358
- if (h1Match) {
9359
- return h1Match[1].trim();
9360
- }
9361
- if (bodyMatch) {
9362
- const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
9363
- return text.substring(0, 200);
9364
- }
9365
- return null;
9898
+ getCategory(toolName) {
9899
+ const categoryMap = Object.fromEntries(this.customCategories);
9900
+ return getToolCategory(toolName, categoryMap);
9366
9901
  }
9367
9902
  /**
9368
- * Truncate string to max length with ellipsis
9903
+ * Check if a tool can be trusted (not in prompt_always category or denied by project)
9369
9904
  */
9370
- truncate(str, maxLength) {
9371
- if (str.length <= maxLength) {
9372
- return str;
9905
+ canBeTrusted(toolName) {
9906
+ const categoryMap = Object.fromEntries(this.customCategories);
9907
+ if (this.deniedTools.has(toolName)) {
9908
+ return false;
9373
9909
  }
9374
- return str.substring(0, maxLength) + "... [truncated]";
9910
+ return canTrustTool(toolName, categoryMap);
9375
9911
  }
9376
9912
  /**
9377
- * Format bytes to human-readable size
9913
+ * Check if a tool is denied by project config
9378
9914
  */
9379
- formatBytes(bytes) {
9380
- if (bytes === 0) return "0 B";
9381
- const k = 1024;
9382
- const sizes = ["B", "KB", "MB", "GB"];
9383
- const i = Math.floor(Math.log(bytes) / Math.log(k));
9384
- return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
9915
+ isDenied(toolName) {
9916
+ return this.deniedTools.has(toolName);
9385
9917
  }
9386
9918
  /**
9387
- * Clean up old debug logs (older than 30 days)
9919
+ * Get list of denied tools
9388
9920
  */
9389
- async cleanupOldLogs() {
9390
- if (!this.fileLoggingEnabled) return;
9391
- try {
9392
- const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9393
- const files = await fs10.readdir(debugDir);
9394
- const now = Date.now();
9395
- const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
9396
- for (const file of files) {
9397
- const filePath = path11.join(debugDir, file);
9398
- const stats = await fs10.stat(filePath);
9399
- if (stats.mtime.getTime() < thirtyDaysAgo) {
9400
- await fs10.unlink(filePath);
9401
- }
9402
- }
9403
- } catch (error) {
9404
- console.error("Failed to cleanup old logs:", error);
9405
- }
9921
+ getDeniedTools() {
9922
+ return Array.from(this.deniedTools).sort();
9923
+ }
9924
+ /**
9925
+ * Clear all trusted tools
9926
+ */
9927
+ clearTrustedTools() {
9928
+ this.trustedTools.clear();
9929
+ }
9930
+ /**
9931
+ * Get statistics about permissions
9932
+ */
9933
+ getStats() {
9934
+ return {
9935
+ trustedCount: this.trustedTools.size,
9936
+ autoApproveCount: 0,
9937
+ // TODO: Would need full tool list
9938
+ promptAlwaysCount: 0,
9939
+ promptDefaultCount: 0
9940
+ };
9406
9941
  }
9407
9942
  };
9408
- var logger = Logger2.getInstance();
9943
+
9944
+ // src/utils/apiUrl.ts
9945
+ var BIKE4MIND_URL = "https://app.bike4mind.com";
9946
+ function getApiUrl(configApiConfig) {
9947
+ if (configApiConfig?.customUrl) {
9948
+ return configApiConfig.customUrl;
9949
+ }
9950
+ return BIKE4MIND_URL;
9951
+ }
9952
+ function getEnvironmentName(configApiConfig) {
9953
+ if (configApiConfig?.customUrl) {
9954
+ return "Self-Hosted";
9955
+ }
9956
+ return "Bike4Mind";
9957
+ }
9409
9958
 
9410
9959
  // ../../b4m-core/packages/mcp/dist/src/client.js
9411
9960
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
9412
9961
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
9413
- import path12 from "path";
9962
+ import path13 from "path";
9414
9963
  import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
9415
9964
  var MCPClient = class {
9416
9965
  // Note: This class handles MCP server communication with repository filtering
@@ -9451,16 +10000,16 @@ var MCPClient = class {
9451
10000
  const root = process.env.INIT_CWD || process.cwd();
9452
10001
  const candidatePaths = [
9453
10002
  // When running from SST Lambda with node_modules structure (copyFiles)
9454
- path12.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
10003
+ path13.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
9455
10004
  // When running from SST Lambda deployed environment (/var/task)
9456
- path12.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
10005
+ path13.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9457
10006
  // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
9458
- path12.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
10007
+ path13.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9459
10008
  // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
9460
- path12.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
10009
+ path13.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9461
10010
  // Original paths (backward compatibility)
9462
- path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9463
- path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
10011
+ path13.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
10012
+ path13.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
9464
10013
  ];
9465
10014
  const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
9466
10015
  if (!serverScriptPath) {
@@ -10210,7 +10759,10 @@ var ApiClient = class {
10210
10759
  * Make a POST request
10211
10760
  */
10212
10761
  async post(url, data, config) {
10762
+ logger.debug(`[ApiClient] POST ${this.client.defaults.baseURL}${url}`);
10763
+ logger.debug(`[ApiClient] Request body: ${JSON.stringify(data)}`);
10213
10764
  const response = await this.client.post(url, data, config);
10765
+ logger.debug(`[ApiClient] Response status: ${response.status}`);
10214
10766
  return response.data;
10215
10767
  }
10216
10768
  /**
@@ -10261,7 +10813,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
10261
10813
  // package.json
10262
10814
  var package_default = {
10263
10815
  name: "@bike4mind/cli",
10264
- version: "0.2.7-ja-fix-confluence-table-data-5752.17142+305b1c6f1",
10816
+ version: "0.2.8-cli-bash-tools.17146+9129ce1ea",
10265
10817
  type: "module",
10266
10818
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
10267
10819
  license: "UNLICENSED",
@@ -10364,10 +10916,10 @@ var package_default = {
10364
10916
  },
10365
10917
  devDependencies: {
10366
10918
  "@bike4mind/agents": "0.1.0",
10367
- "@bike4mind/common": "2.38.1-ja-fix-confluence-table-data-5752.17142+305b1c6f1",
10368
- "@bike4mind/mcp": "1.20.1-ja-fix-confluence-table-data-5752.17142+305b1c6f1",
10369
- "@bike4mind/services": "2.33.1-ja-fix-confluence-table-data-5752.17142+305b1c6f1",
10370
- "@bike4mind/utils": "2.1.2-ja-fix-confluence-table-data-5752.17142+305b1c6f1",
10919
+ "@bike4mind/common": "2.38.2-cli-bash-tools.17146+9129ce1ea",
10920
+ "@bike4mind/mcp": "1.20.2-cli-bash-tools.17146+9129ce1ea",
10921
+ "@bike4mind/services": "2.33.2-cli-bash-tools.17146+9129ce1ea",
10922
+ "@bike4mind/utils": "2.1.3-cli-bash-tools.17146+9129ce1ea",
10371
10923
  "@types/better-sqlite3": "^7.6.12",
10372
10924
  "@types/diff": "^5.0.9",
10373
10925
  "@types/jsonwebtoken": "^9.0.4",
@@ -10380,7 +10932,7 @@ var package_default = {
10380
10932
  typescript: "^5.9.3",
10381
10933
  vitest: "^3.2.4"
10382
10934
  },
10383
- gitHead: "305b1c6f12a188c60bbc63a1c1f5f6a13860c49e"
10935
+ gitHead: "9129ce1ea5d8589dec80ac92bf32ca954c6844fc"
10384
10936
  };
10385
10937
 
10386
10938
  // src/config/constants.ts
@@ -10402,7 +10954,7 @@ var usageCache = null;
10402
10954
  function CliApp() {
10403
10955
  const { exit } = useApp();
10404
10956
  const imageRenderer = new ImageRenderer();
10405
- const [state, setState] = useState4({
10957
+ const [state, setState] = useState5({
10406
10958
  session: null,
10407
10959
  sessionStore: new SessionStore(),
10408
10960
  configStore: new ConfigStore(),
@@ -10415,11 +10967,12 @@ function CliApp() {
10415
10967
  mcpManager: null,
10416
10968
  permissionManager: null,
10417
10969
  permissionPrompt: null,
10418
- trustLocationSelector: null
10970
+ trustLocationSelector: null,
10971
+ rewindSelector: null
10419
10972
  });
10420
- const [isInitialized, setIsInitialized] = useState4(false);
10421
- const [initError, setInitError] = useState4(null);
10422
- const [commandHistory, setCommandHistory] = useState4([]);
10973
+ const [isInitialized, setIsInitialized] = useState5(false);
10974
+ const [initError, setInitError] = useState5(null);
10975
+ const [commandHistory, setCommandHistory] = useState5([]);
10423
10976
  const imageStoreInitPromise = useRef(null);
10424
10977
  const setStoreSession = useCliStore((state2) => state2.setSession);
10425
10978
  const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
@@ -10460,7 +11013,7 @@ function CliApp() {
10460
11013
  process.exit(0);
10461
11014
  }, 100);
10462
11015
  }, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
10463
- useInput3((input, key) => {
11016
+ useInput4((input, key) => {
10464
11017
  if (key.ctrl && input === "c") {
10465
11018
  const now = Date.now();
10466
11019
  if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
@@ -10604,10 +11157,6 @@ function CliApp() {
10604
11157
  setPermissionPrompt(prompt);
10605
11158
  });
10606
11159
  };
10607
- console.log("\u{1F511} Tool API Keys:", {
10608
- openweather: config.toolApiKeys?.openweather ? "\u2713 configured" : "\u2717 missing",
10609
- serper: config.toolApiKeys?.serper ? "\u2713 configured" : "\u2717 missing"
10610
- });
10611
11160
  const agentContext = {
10612
11161
  currentAgent: null,
10613
11162
  observationQueue: []
@@ -10620,7 +11169,7 @@ function CliApp() {
10620
11169
  promptFn,
10621
11170
  agentContext,
10622
11171
  state.configStore,
10623
- config.toolApiKeys
11172
+ apiClient
10624
11173
  );
10625
11174
  console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
10626
11175
  const mcpManager = new McpManager(config);
@@ -10718,6 +11267,29 @@ Remember: Use context from previous messages to understand follow-up questions.`
10718
11267
  console.log("\u{1F4A1} Run /login to authenticate with your B4M account.\n");
10719
11268
  return;
10720
11269
  }
11270
+ const currentSteps = [];
11271
+ const stepHandler = (step) => {
11272
+ currentSteps.push(step);
11273
+ const currentSession = useCliStore.getState().session;
11274
+ if (currentSession) {
11275
+ const messages = [...currentSession.messages];
11276
+ const lastMessage = messages[messages.length - 1];
11277
+ if (lastMessage && lastMessage.role === "assistant") {
11278
+ messages[messages.length - 1] = {
11279
+ ...lastMessage,
11280
+ metadata: {
11281
+ ...lastMessage.metadata,
11282
+ steps: [...currentSteps]
11283
+ }
11284
+ };
11285
+ setStoreSession({
11286
+ ...currentSession,
11287
+ messages
11288
+ });
11289
+ }
11290
+ }
11291
+ };
11292
+ state.agent.on("action", stepHandler);
10721
11293
  try {
10722
11294
  let messageContent = message;
10723
11295
  let userMessageContent = message;
@@ -10731,6 +11303,21 @@ Remember: Use context from previous messages to understand follow-up questions.`
10731
11303
  content: userMessageContent,
10732
11304
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
10733
11305
  };
11306
+ const pendingAssistantMessage = {
11307
+ role: "assistant",
11308
+ content: "...",
11309
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11310
+ metadata: {
11311
+ steps: []
11312
+ }
11313
+ };
11314
+ const sessionWithMessages = {
11315
+ ...state.session,
11316
+ messages: [...state.session.messages, userMessage, pendingAssistantMessage],
11317
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
11318
+ };
11319
+ setState((prev) => ({ ...prev, session: sessionWithMessages }));
11320
+ setStoreSession(sessionWithMessages);
10734
11321
  const recentMessages = state.session.messages.slice(-20);
10735
11322
  const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
10736
11323
  role: msg.role,
@@ -10744,26 +11331,28 @@ Remember: Use context from previous messages to understand follow-up questions.`
10744
11331
  console.log("\n\u26A0\uFE0F Action denied by user\n");
10745
11332
  }
10746
11333
  const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
10747
- const assistantMessage = {
11334
+ const currentSession = useCliStore.getState().session;
11335
+ if (!currentSession) return;
11336
+ const finalMessages = [...currentSession.messages];
11337
+ const finalAssistantMessage = {
10748
11338
  role: "assistant",
10749
11339
  content: result.finalAnswer,
10750
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11340
+ timestamp: pendingAssistantMessage.timestamp,
10751
11341
  metadata: {
10752
11342
  tokenUsage: {
10753
11343
  prompt: 0,
10754
- // TODO: Extract from result
10755
11344
  completion: 0,
10756
11345
  total: result.completionInfo.totalTokens
10757
11346
  },
10758
11347
  steps: result.steps,
10759
11348
  // Complete history: thoughts, actions, observations
10760
11349
  permissionDenied
10761
- // Mark if this ended due to permission denial
10762
11350
  }
10763
11351
  };
11352
+ finalMessages[finalMessages.length - 1] = finalAssistantMessage;
10764
11353
  const updatedSession = {
10765
- ...state.session,
10766
- messages: [...state.session.messages, userMessage, assistantMessage],
11354
+ ...currentSession,
11355
+ messages: finalMessages,
10767
11356
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
10768
11357
  metadata: {
10769
11358
  ...state.session.metadata,
@@ -10783,6 +11372,8 @@ Remember: Use context from previous messages to understand follow-up questions.`
10783
11372
  }
10784
11373
  }
10785
11374
  console.error("Error processing message:", error);
11375
+ } finally {
11376
+ state.agent.off("action", stepHandler);
10786
11377
  }
10787
11378
  };
10788
11379
  const handleImageDetected = async (imageData) => {
@@ -10814,6 +11405,30 @@ Remember: Use context from previous messages to understand follow-up questions.`
10814
11405
  throw error;
10815
11406
  }
10816
11407
  };
11408
+ const recalculateSessionMetadata = (messages) => {
11409
+ let totalTokens = 0;
11410
+ let totalCost = 0;
11411
+ let toolCallCount = 0;
11412
+ for (const msg of messages) {
11413
+ if (msg.metadata) {
11414
+ if (msg.metadata.tokenUsage) {
11415
+ totalTokens += msg.metadata.tokenUsage.total || 0;
11416
+ }
11417
+ if (msg.metadata.cost) {
11418
+ totalCost += msg.metadata.cost;
11419
+ }
11420
+ if (msg.metadata.steps) {
11421
+ const observations = msg.metadata.steps.filter((s) => s.type === "observation");
11422
+ toolCallCount += observations.length;
11423
+ }
11424
+ }
11425
+ }
11426
+ return {
11427
+ totalTokens,
11428
+ totalCost,
11429
+ toolCallCount
11430
+ };
11431
+ };
10817
11432
  const handleCommand = async (command, args) => {
10818
11433
  switch (command) {
10819
11434
  case "help":
@@ -10822,6 +11437,7 @@ Available commands:
10822
11437
  /help - Show this help message
10823
11438
  /exit - Exit the CLI
10824
11439
  /clear - Start a new session
11440
+ /rewind - Rewind conversation to a previous point
10825
11441
  /login - Authenticate with your B4M account
10826
11442
  /logout - Clear authentication and sign out
10827
11443
  /whoami - Show current authenticated user
@@ -11097,6 +11713,65 @@ Project Configuration:
11097
11713
  `);
11098
11714
  break;
11099
11715
  }
11716
+ case "rewind":
11717
+ case "undo": {
11718
+ if (!state.session) {
11719
+ console.log("No active session to rewind");
11720
+ return;
11721
+ }
11722
+ if (state.session.messages.length === 0) {
11723
+ console.log("\u26A0\uFE0F Conversation is empty. Nothing to rewind.");
11724
+ return;
11725
+ }
11726
+ const userMessages = state.session.messages.filter((msg) => msg.role === "user");
11727
+ if (userMessages.length === 0) {
11728
+ console.log("\u26A0\uFE0F No user messages found. Nothing to rewind.");
11729
+ return;
11730
+ }
11731
+ if (userMessages.length === 1) {
11732
+ console.log("\u26A0\uFE0F Only one exchange in the conversation. Use /clear to start a new session instead.");
11733
+ return;
11734
+ }
11735
+ const handleRewindSelect = async (messageIndex) => {
11736
+ setState((prev) => ({ ...prev, rewindSelector: null }));
11737
+ if (messageIndex === null) {
11738
+ console.log("\u274C Rewind operation cancelled");
11739
+ return;
11740
+ }
11741
+ if (!state.session) {
11742
+ console.log("\u274C No active session");
11743
+ return;
11744
+ }
11745
+ const selectedMessage = state.session.messages[messageIndex];
11746
+ const prefillContent = selectedMessage?.content || "";
11747
+ const rewindedMessages = state.session.messages.slice(0, messageIndex);
11748
+ const newMetadata = recalculateSessionMetadata(rewindedMessages);
11749
+ const rewindedSession = {
11750
+ ...state.session,
11751
+ messages: rewindedMessages,
11752
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
11753
+ metadata: newMetadata
11754
+ };
11755
+ setState((prev) => ({
11756
+ ...prev,
11757
+ session: rewindedSession,
11758
+ prefillInput: prefillContent
11759
+ }));
11760
+ setStoreSession(rewindedSession);
11761
+ await state.sessionStore.save(rewindedSession);
11762
+ console.log("\u2705 Conversation rewound successfully");
11763
+ console.log(`\u{1F4CA} Current state: ${rewindedMessages.length} messages, ${newMetadata.totalTokens} tokens`);
11764
+ console.log(`\u{1F4DD} Your message has been placed in the input. Edit and send when ready.
11765
+ `);
11766
+ };
11767
+ setState((prev) => ({
11768
+ ...prev,
11769
+ rewindSelector: {
11770
+ resolve: handleRewindSelect
11771
+ }
11772
+ }));
11773
+ break;
11774
+ }
11100
11775
  case "usage": {
11101
11776
  const usageAuthTokens = await state.configStore.getAuthTokens();
11102
11777
  if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
@@ -11250,12 +11925,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
11250
11925
  setState((prev) => ({ ...prev, config: updatedConfig }));
11251
11926
  };
11252
11927
  if (initError) {
11253
- return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React13.createElement(Text12, null, initError), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
11928
+ return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React14.createElement(Text13, null, initError), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
11254
11929
  }
11255
11930
  if (state.trustLocationSelector) {
11256
11931
  const projectDir = state.configStore.getProjectConfigDir();
11257
11932
  const inProject = projectDir !== null;
11258
- return /* @__PURE__ */ React13.createElement(
11933
+ return /* @__PURE__ */ React14.createElement(
11259
11934
  TrustLocationSelector,
11260
11935
  {
11261
11936
  inProject,
@@ -11272,9 +11947,27 @@ No usage data available for the last ${USAGE_DAYS} days.`);
11272
11947
  }
11273
11948
  );
11274
11949
  }
11950
+ if (state.rewindSelector && state.session) {
11951
+ return /* @__PURE__ */ React14.createElement(
11952
+ RewindSelector,
11953
+ {
11954
+ messages: state.session.messages,
11955
+ onSelect: (messageIndex) => {
11956
+ if (state.rewindSelector) {
11957
+ state.rewindSelector.resolve(messageIndex);
11958
+ }
11959
+ },
11960
+ onCancel: () => {
11961
+ if (state.rewindSelector) {
11962
+ state.rewindSelector.resolve(null);
11963
+ }
11964
+ }
11965
+ }
11966
+ );
11967
+ }
11275
11968
  if (state.showLoginFlow) {
11276
11969
  const loginApiUrl = getApiUrl(state.config?.apiConfig);
11277
- return /* @__PURE__ */ React13.createElement(
11970
+ return /* @__PURE__ */ React14.createElement(
11278
11971
  LoginFlow,
11279
11972
  {
11280
11973
  apiUrl: loginApiUrl,
@@ -11297,9 +11990,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
11297
11990
  );
11298
11991
  }
11299
11992
  if (!isInitialized) {
11300
- return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "\u{1F680} Initializing..."));
11993
+ return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, "\u{1F680} Initializing..."));
11301
11994
  }
11302
- return /* @__PURE__ */ React13.createElement(
11995
+ return /* @__PURE__ */ React14.createElement(
11303
11996
  App,
11304
11997
  {
11305
11998
  onMessage: handleMessage,
@@ -11309,6 +12002,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
11309
12002
  config: state.config,
11310
12003
  availableModels: state.availableModels,
11311
12004
  onSaveConfig: handleSaveConfig,
12005
+ prefillInput: state.prefillInput,
12006
+ onPrefillConsumed: () => {
12007
+ setState((prev) => ({ ...prev, prefillInput: void 0 }));
12008
+ },
11312
12009
  onPermissionResponse: (response) => {
11313
12010
  if (state.permissionPrompt) {
11314
12011
  state.permissionPrompt.resolve({ action: response });
@@ -11334,6 +12031,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
11334
12031
  if (isDevMode) {
11335
12032
  console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
11336
12033
  }
11337
- render(/* @__PURE__ */ React13.createElement(CliApp, null), {
12034
+ render(/* @__PURE__ */ React14.createElement(CliApp, null), {
11338
12035
  exitOnCtrlC: false
11339
12036
  });