@mtreeai/msapling-cli 2.3.6-beta.60 → 2.3.6-beta.61

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.
Files changed (2) hide show
  1. package/dist/index.js +139 -82
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14450,6 +14450,14 @@ var init_Settings = __esm({
14450
14450
  });
14451
14451
 
14452
14452
  // ../core/src/agent/localModelPolicy.ts
14453
+ function buildDeterministicReadPlan(prompt4, availableTools) {
14454
+ if (MUTATING_INTENT.test(prompt4) || !LIST_DIRECTORY_INTENT.test(prompt4)) return null;
14455
+ if (!new Set(availableTools).has("list_directory")) return null;
14456
+ const absolute = (prompt4.match(QUOTED_WINDOWS_PATH)?.[1] ?? prompt4.match(WINDOWS_DRIVE_ROOT)?.[1] ?? prompt4.match(WINDOWS_ABSOLUTE_TOKEN)?.[1])?.trim();
14457
+ const drive = prompt4.match(WINDOWS_DRIVE_WORDS)?.[1] ?? prompt4.match(WINDOWS_DIRECTORY_THEN_DRIVE)?.[1];
14458
+ const path3 = absolute || (drive ? `${drive.toUpperCase()}:\\` : ".");
14459
+ return { tool: "list_directory", args: { path: path3 } };
14460
+ }
14453
14461
  function buildReadToolRecovery(prompt4, response, availableTools) {
14454
14462
  if (MUTATING_INTENT.test(prompt4)) return null;
14455
14463
  const hasReadIntent = SAFE_READ_INTENT.test(prompt4) || SAFE_READ_VERB.test(prompt4);
@@ -14478,7 +14486,7 @@ function inferCapability(model) {
14478
14486
  if (sizes.some((size) => size <= 7)) return "small";
14479
14487
  return "unknown";
14480
14488
  }
14481
- var LOCAL_ACCESS_DENIAL, SAFE_READ_INTENT, SAFE_READ_VERB, PATH_LIKE_INTENT, MUTATING_INTENT, READ_RECOVERY_TOOLS, SAFE_TOOLS;
14489
+ var LOCAL_ACCESS_DENIAL, SAFE_READ_INTENT, SAFE_READ_VERB, PATH_LIKE_INTENT, MUTATING_INTENT, READ_RECOVERY_TOOLS, LIST_DIRECTORY_INTENT, QUOTED_WINDOWS_PATH, WINDOWS_DRIVE_ROOT, WINDOWS_ABSOLUTE_TOKEN, WINDOWS_DRIVE_WORDS, WINDOWS_DIRECTORY_THEN_DRIVE, SAFE_TOOLS;
14482
14490
  var init_localModelPolicy = __esm({
14483
14491
  "../core/src/agent/localModelPolicy.ts"() {
14484
14492
  "use strict";
@@ -14489,6 +14497,12 @@ var init_localModelPolicy = __esm({
14489
14497
  PATH_LIKE_INTENT = /(?:[A-Za-z]:[\\/]|\/{1,2}[A-Za-z0-9_.-]+\/|\.\.?[\\/])[A-Za-z0-9_.\\/ -]*/;
14490
14498
  MUTATING_INTENT = /\b(?:create|overwrite|write|edit|modify|change|delete|remove|move|rename|execute|run|install|uninstall|commit|push|upload)\b/i;
14491
14499
  READ_RECOVERY_TOOLS = ["read_file", "list_directory", "glob_files", "grep_search"];
14500
+ LIST_DIRECTORY_INTENT = /\b(?:list|show|display|enumerate|review|inspect|what(?:'s| is)? (?:in|inside))\b[^\n]{0,120}\b(?:files?|folders?|director(?:y|ies)|drive|workspace|project)\b/i;
14501
+ QUOTED_WINDOWS_PATH = /["']([A-Za-z]:[\\/][^"']*)["']/;
14502
+ WINDOWS_DRIVE_ROOT = /\b([A-Za-z]:[\\/])(?=\s|$|[.,;:)])/;
14503
+ WINDOWS_ABSOLUTE_TOKEN = /\b([A-Za-z]:[\\/][^\s,;]*)/;
14504
+ WINDOWS_DRIVE_WORDS = /\b([A-Za-z])\s+(?:drive|directory)\b/i;
14505
+ WINDOWS_DIRECTORY_THEN_DRIVE = /\b(?:drive|directory)\s+([A-Za-z])\b/i;
14492
14506
  SAFE_TOOLS = /* @__PURE__ */ new Set([
14493
14507
  "read_file",
14494
14508
  "list_directory",
@@ -15028,6 +15042,33 @@ var init_Agent = __esm({
15028
15042
  }
15029
15043
  return this.executor.execute(toolName, args2, this.projectRoot);
15030
15044
  }
15045
+ async executeDeterministicReadAdapter(chatId, turnId, plan) {
15046
+ const toolCallId = `client-read-${randomUUID6()}`;
15047
+ const request = {
15048
+ toolName: plan.tool,
15049
+ toolCallId,
15050
+ turnId,
15051
+ input: plan.args
15052
+ };
15053
+ await this.modeContract?.recordToolRequest(request);
15054
+ try {
15055
+ const result = await this.executeTurnTool(plan.tool, plan.args, false);
15056
+ await this.modeContract?.recordToolResult(request, result.content, !!result.isError);
15057
+ this.recordRemoteToolTelemetry(
15058
+ chatId,
15059
+ turnId,
15060
+ toolCallId,
15061
+ plan.tool,
15062
+ result.isError ? "failed" : "completed"
15063
+ );
15064
+ return result;
15065
+ } catch (error) {
15066
+ const content = SafetyGuard.redact(error instanceof Error ? error.message : String(error));
15067
+ await this.modeContract?.recordToolResult(request, content, true);
15068
+ this.recordRemoteToolTelemetry(chatId, turnId, toolCallId, plan.tool, "failed");
15069
+ throw error;
15070
+ }
15071
+ }
15031
15072
  recordRemoteToolTelemetry(chatId, turnId, toolCallId, toolName, outcome) {
15032
15073
  if (this.executionMode !== "remote") return;
15033
15074
  if (this.toolTelemetryQueue.length >= TOOL_TELEMETRY_QUEUE_MAX) {
@@ -15277,7 +15318,37 @@ var init_Agent = __esm({
15277
15318
  structuredToolResults = false;
15278
15319
  }
15279
15320
  }
15280
- const queue = [{ prompt: prompt4, allowReadRecovery: true }];
15321
+ let initialQueueItem = { prompt: prompt4, allowReadRecovery: true };
15322
+ if (!localTurn) {
15323
+ const initialToolNames = this.getTurnToolSchemas(false).map((tool) => String(tool.name));
15324
+ const deterministicPlan = buildDeterministicReadPlan(prompt4, initialToolNames);
15325
+ if (deterministicPlan) {
15326
+ const diagnostic = `[MSapling: running safe local ${deterministicPlan.tool} before the connected model.]
15327
+ `;
15328
+ await this.modeContract?.recordOutput(chatId, contractTurnId, diagnostic);
15329
+ onContent(diagnostic);
15330
+ const result = await this.executeDeterministicReadAdapter(
15331
+ chatId,
15332
+ contractTurnId,
15333
+ deterministicPlan
15334
+ );
15335
+ const evidence = boundToolResultForModel(result.content);
15336
+ initialQueueItem = {
15337
+ prompt: `A trusted MSapling client read-only adapter executed ${deterministicPlan.tool} with ${JSON.stringify(deterministicPlan.args)} for the original request below.
15338
+
15339
+ <client_tool_result error="${result.isError === true}">
15340
+ ${evidence}
15341
+ </client_tool_result>
15342
+
15343
+ Original request: ${prompt4}
15344
+
15345
+ Answer directly from the tool result. Do not claim local access is unavailable, do not emit a patch or diff, and do not invent entries.`,
15346
+ allowedToolNames: [],
15347
+ allowReadRecovery: false
15348
+ };
15349
+ }
15350
+ }
15351
+ const queue = [initialQueueItem];
15281
15352
  let rounds = 0;
15282
15353
  let toolCallSeq = 0;
15283
15354
  let streamUsage = null;
@@ -15450,16 +15521,44 @@ ${next.prompt}`
15450
15521
  const recovery = !localTurn && allowReadRecovery && !remoteReadRecoveryUsed && !assistantResponse.includes("[MSapling: local model denied available file tools;") ? buildReadToolRecovery(prompt4, assistantResponse, turnTools.map((tool) => String(tool.name))) : null;
15451
15522
  if (recovery) {
15452
15523
  remoteReadRecoveryUsed = true;
15453
- const diagnostic = `
15524
+ const deterministicPlan = buildDeterministicReadPlan(prompt4, recovery.tools);
15525
+ if (deterministicPlan) {
15526
+ const diagnostic = `
15527
+
15528
+ [MSapling: connected model did not use the required read tool; running safe local ${deterministicPlan.tool}.]
15529
+ `;
15530
+ await this.modeContract?.recordOutput(chatId, contractTurnId, diagnostic);
15531
+ onContent(diagnostic);
15532
+ const result = await this.executeDeterministicReadAdapter(
15533
+ chatId,
15534
+ contractTurnId,
15535
+ deterministicPlan
15536
+ );
15537
+ const evidence = boundToolResultForModel(result.content);
15538
+ queue.push({
15539
+ prompt: `A trusted MSapling client read-only adapter executed ${deterministicPlan.tool} with ${JSON.stringify(deterministicPlan.args)} for the original request below.
15540
+
15541
+ <client_tool_result error="${result.isError === true}">
15542
+ ${evidence}
15543
+ </client_tool_result>
15544
+
15545
+ Original request: ${prompt4}
15546
+
15547
+ Answer from the tool result. Do not claim local access is unavailable.`,
15548
+ allowedToolNames: []
15549
+ });
15550
+ } else {
15551
+ const diagnostic = `
15454
15552
 
15455
15553
  [MSapling: connected model did not use required client file tools; retrying once with ${recovery.tools.join(", ")}.]
15456
15554
  `;
15457
- await this.modeContract?.recordOutput(chatId, contractTurnId, diagnostic);
15458
- onContent(diagnostic);
15459
- queue.push({
15460
- prompt: recovery.instruction,
15461
- allowedToolNames: recovery.tools
15462
- });
15555
+ await this.modeContract?.recordOutput(chatId, contractTurnId, diagnostic);
15556
+ onContent(diagnostic);
15557
+ queue.push({
15558
+ prompt: recovery.instruction,
15559
+ allowedToolNames: recovery.tools
15560
+ });
15561
+ }
15463
15562
  }
15464
15563
  continue;
15465
15564
  }
@@ -23566,7 +23665,7 @@ var init_version = __esm({
23566
23665
  description: "Show version information for CLI and core packages",
23567
23666
  category: "debug",
23568
23667
  handler: async (_args, context) => {
23569
- const cliVersion = true ? "2.3.6-beta.60" : "(dev)";
23668
+ const cliVersion = true ? "2.3.6-beta.61" : "(dev)";
23570
23669
  const coreVersion = true ? "2.3.6-beta.43" : "(dev)";
23571
23670
  const runtime = process.version;
23572
23671
  context.addMessage("system", "MSapling Version Info");
@@ -29593,7 +29692,7 @@ import { render } from "ink";
29593
29692
 
29594
29693
  // src/App.tsx
29595
29694
  init_esm_shims();
29596
- import { useState as useState5, useEffect as useEffect4, useCallback as useCallback2, useRef as useRef2 } from "react";
29695
+ import { useState as useState5, useEffect as useEffect3, useCallback as useCallback2, useRef } from "react";
29597
29696
  import { randomUUID as randomUUID10 } from "crypto";
29598
29697
  import { Box as Box7, Text as Text7, useApp, useInput as useInput4, useStdout } from "ink";
29599
29698
 
@@ -29603,11 +29702,11 @@ import { Box, Text } from "ink";
29603
29702
  import { jsx, jsxs } from "react/jsx-runtime";
29604
29703
  var Header = ({ compact: compact2 = false }) => compact2 ? /* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
29605
29704
  "\u25CF MSapling v",
29606
- "2.3.6-beta.60"
29705
+ "2.3.6-beta.61"
29607
29706
  ] }) : /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
29608
29707
  /* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
29609
29708
  "\u25CF MSapling CLI v",
29610
- "2.3.6-beta.60"
29709
+ "2.3.6-beta.61"
29611
29710
  ] }),
29612
29711
  /* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
29613
29712
  ] });
@@ -29711,42 +29810,6 @@ function promptLabel(user) {
29711
29810
  init_esm_shims();
29712
29811
  import { useState, useMemo } from "react";
29713
29812
  import { Box as Box3, Text as Text3, useInput } from "ink";
29714
-
29715
- // src/hooks/useTerminalMouseScroll.ts
29716
- init_esm_shims();
29717
- import { useEffect, useRef } from "react";
29718
- var ESC = String.fromCharCode(27);
29719
- function isMouseReport(input) {
29720
- return input.includes(`${ESC}[<`) && /\d+;\d+;\d+[mM]/.test(input);
29721
- }
29722
- function parseMouseScroll(input) {
29723
- const directions = [];
29724
- const pattern = new RegExp(`${ESC}\\[<(64|65);\\d+;\\d+[mM]`, "g");
29725
- for (const match of input.matchAll(pattern)) {
29726
- directions.push(match[1] === "64" ? "up" : "down");
29727
- }
29728
- return directions;
29729
- }
29730
- function useTerminalMouseScroll(onScroll) {
29731
- const callback = useRef(onScroll);
29732
- callback.current = onScroll;
29733
- useEffect(() => {
29734
- if (!process.stdin.isTTY || !process.stdout.isTTY) return;
29735
- const handleData = (chunk) => {
29736
- for (const direction of parseMouseScroll(String(chunk))) {
29737
- callback.current(direction);
29738
- }
29739
- };
29740
- process.stdout.write("\x1B[?1000h\x1B[?1006h");
29741
- process.stdin.on("data", handleData);
29742
- return () => {
29743
- process.stdin.off("data", handleData);
29744
- process.stdout.write("\x1B[?1006l\x1B[?1000l");
29745
- };
29746
- }, []);
29747
- }
29748
-
29749
- // src/components/ApprovalDialog.tsx
29750
29813
  import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
29751
29814
  function parseDiffLines(diffText) {
29752
29815
  if (!diffText) return [];
@@ -29813,7 +29876,6 @@ var ApprovalDialog = ({
29813
29876
  const { hasDiff, diffText, title } = useMemo(() => extractDiffFromCommand(command, diff), [command, diff]);
29814
29877
  const diffLines = useMemo(() => parseDiffLines(diffText), [diffText]);
29815
29878
  useInput((input, key) => {
29816
- if (isMouseReport(input)) return;
29817
29879
  if (key.escape) {
29818
29880
  onResolve("no");
29819
29881
  return;
@@ -29900,7 +29962,6 @@ var AskUserQuestion = ({ question, options, multiSelect, onResolve }) => {
29900
29962
  const [selectedIndex, setSelectedIndex] = useState2(0);
29901
29963
  const [selectedIndices, setSelectedIndices] = useState2(/* @__PURE__ */ new Set());
29902
29964
  useInput2((input, key) => {
29903
- if (isMouseReport(input)) return;
29904
29965
  if (key.upArrow) {
29905
29966
  setSelectedIndex((prev) => prev > 0 ? prev - 1 : options.length - 1);
29906
29967
  } else if (key.downArrow) {
@@ -30010,7 +30071,7 @@ var VirtualizedMessageList = ({
30010
30071
  );
30011
30072
  const separatorWidth = termColumns > 0 ? Math.min(termColumns, 60) : 60;
30012
30073
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", flexGrow: 1, children: [
30013
- (hiddenCount > 0 || hiddenAfter > 0) && /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: `[\u2191 ${hiddenCount} older \xB7 \u2193 ${hiddenAfter} newer \xB7 wheel/\u2191/\u2193 or PageUp/PageDown \xB7 Ctrl+E latest]` }),
30074
+ (hiddenCount > 0 || hiddenAfter > 0) && /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: `[\u2191 ${hiddenCount} older \xB7 \u2193 ${hiddenAfter} newer \xB7 \u2191/\u2193 or PageUp/PageDown \xB7 Ctrl+E latest]` }),
30014
30075
  displayMessages.map((msg, i) => /* @__PURE__ */ jsxs5(
30015
30076
  Box5,
30016
30077
  {
@@ -30042,7 +30103,7 @@ init_src3();
30042
30103
 
30043
30104
  // src/ui/TextInput.tsx
30044
30105
  init_esm_shims();
30045
- import { useState as useState3, useEffect as useEffect2 } from "react";
30106
+ import { useState as useState3, useEffect } from "react";
30046
30107
  import { Box as Box6, Text as Text6, useInput as useInput3 } from "ink";
30047
30108
 
30048
30109
  // src/state/commandHandler.ts
@@ -30642,11 +30703,10 @@ var TextInput = ({
30642
30703
  }) => {
30643
30704
  const [history, setHistory] = useState3([]);
30644
30705
  const [historyIndex, setHistoryIndex] = useState3(-1);
30645
- useEffect2(() => {
30706
+ useEffect(() => {
30646
30707
  storage.loadHistory().then((entries) => setHistory(filterSafeHistory(entries)));
30647
30708
  }, [storage]);
30648
30709
  useInput3((input, key) => {
30649
- if (isMouseReport(input)) return;
30650
30710
  if (key.ctrl && input === "c" || key.escape) {
30651
30711
  onCancel?.();
30652
30712
  return;
@@ -30869,7 +30929,7 @@ init_errorPresentation();
30869
30929
 
30870
30930
  // src/hooks/useTerminalResize.ts
30871
30931
  init_esm_shims();
30872
- import { useState as useState4, useEffect as useEffect3, useCallback } from "react";
30932
+ import { useState as useState4, useEffect as useEffect2, useCallback } from "react";
30873
30933
  function getCurrentDimensions() {
30874
30934
  return {
30875
30935
  columns: process.stdout.columns ?? 80,
@@ -30883,7 +30943,7 @@ function useTerminalResize() {
30883
30943
  const handleResize = useCallback(() => {
30884
30944
  setDimensions(getCurrentDimensions());
30885
30945
  }, []);
30886
- useEffect3(() => {
30946
+ useEffect2(() => {
30887
30947
  process.stdout.on("resize", handleResize);
30888
30948
  const onSigwinch = () => handleResize();
30889
30949
  process.on("SIGWINCH", onSigwinch);
@@ -30986,20 +31046,20 @@ var App = ({ compact: compact2 = false, continueSession: continueSession2 = fals
30986
31046
  const [billingProfile, setBillingProfile] = useState5(executionMode2 === "local" ? "ollama" : "account-metered");
30987
31047
  const [continuityProfile, setContinuityProfile] = useState5(executionMode2 === "local" ? "standalone-private" : "connected-mirrored");
30988
31048
  const [bootstrapState, setBootstrapState] = useState5("loading");
30989
- const submissionLockRef = useRef2(false);
31049
+ const submissionLockRef = useRef(false);
30990
31050
  const { exit } = useApp();
30991
31051
  const { stdout: termStdout } = useStdout();
30992
31052
  const { columns: termResizeCols, rows: termResizeRows } = useTerminalResize();
30993
- const storage = useRef2(new StorageManager()).current;
30994
- const client = useRef2(new MSaplingClient()).current;
31053
+ const storage = useRef(new StorageManager()).current;
31054
+ const client = useRef(new MSaplingClient()).current;
30995
31055
  const remoteEnvironment = describeRemoteEnvironment(client.getApiUrl());
30996
- const modeContract = useRef2(new CliModeRuntime({
31056
+ const modeContract = useRef(new CliModeRuntime({
30997
31057
  mode: executionMode2,
30998
31058
  provider: process.env.MSAPLING_LOCAL_LLM_PROVIDER ?? "ollama"
30999
31059
  })).current;
31000
- const sessionRecovery = useRef2(new CliSessionRecovery()).current;
31001
- const checkpointTurns = useRef2(/* @__PURE__ */ new Map()).current;
31002
- const agentRef = useRef2(null);
31060
+ const sessionRecovery = useRef(new CliSessionRecovery()).current;
31061
+ const checkpointTurns = useRef(/* @__PURE__ */ new Map()).current;
31062
+ const agentRef = useRef(null);
31003
31063
  const requestApproval = useCallback2((request) => {
31004
31064
  agentRef.current?.fireLifecycleHook(
31005
31065
  "notification",
@@ -31010,7 +31070,7 @@ var App = ({ compact: compact2 = false, continueSession: continueSession2 = fals
31010
31070
  setPendingApproval({ request, resolve: resolve31 });
31011
31071
  });
31012
31072
  }, []);
31013
- const agent = useRef2(new Agent(client, process.cwd(), requestApproval, { executionMode: executionMode2, modeContract })).current;
31073
+ const agent = useRef(new Agent(client, process.cwd(), requestApproval, { executionMode: executionMode2, modeContract })).current;
31014
31074
  agentRef.current = agent;
31015
31075
  const applyStandaloneSyncState = (state) => {
31016
31076
  const localChatId = "local-cli-chat";
@@ -31031,18 +31091,18 @@ var App = ({ compact: compact2 = false, continueSession: continueSession2 = fals
31031
31091
  setContinuityProfile(profile);
31032
31092
  if (profile === "standalone-private") agent.configureStandaloneSync(null);
31033
31093
  };
31034
- const trustStore = useRef2(new TrustStore()).current;
31035
- const lastActivityRef = useRef2(Date.now());
31036
- const pollingIntervalRef = useRef2(null);
31037
- useEffect4(() => {
31094
+ const trustStore = useRef(new TrustStore()).current;
31095
+ const lastActivityRef = useRef(Date.now());
31096
+ const pollingIntervalRef = useRef(null);
31097
+ useEffect3(() => {
31038
31098
  agent.setApprovalCallback(requestApproval);
31039
31099
  }, [agent, requestApproval]);
31040
- useEffect4(() => {
31100
+ useEffect3(() => {
31041
31101
  if (bypassExpiry === null) return;
31042
31102
  const timer = setInterval(() => setPermissionNow(Date.now()), 1e3);
31043
31103
  return () => clearInterval(timer);
31044
31104
  }, [bypassExpiry]);
31045
- useEffect4(() => {
31105
+ useEffect3(() => {
31046
31106
  agent.setOnModeChange((m) => {
31047
31107
  setModeState(m);
31048
31108
  addMessage("system", `Mode changed to: ${m} (via plan-mode tool)`);
@@ -31071,7 +31131,7 @@ var App = ({ compact: compact2 = false, continueSession: continueSession2 = fals
31071
31131
  setModeState(m);
31072
31132
  agent.setMode(m, source);
31073
31133
  }, [agent]);
31074
- useEffect4(() => {
31134
+ useEffect3(() => {
31075
31135
  if (mode !== "bypassPermissions" || !isBypassExpired(bypassExpiry, permissionNow)) return;
31076
31136
  setMode("default");
31077
31137
  setBypassExpiry(null);
@@ -31087,7 +31147,7 @@ var App = ({ compact: compact2 = false, continueSession: continueSession2 = fals
31087
31147
  setContextBudgetSnap(snapshotBudget(agent.getContextBudget()));
31088
31148
  }, [agent]);
31089
31149
  const getModel = useCallback2(() => activeModel, [activeModel]);
31090
- const cliProjectRef = useRef2(null);
31150
+ const cliProjectRef = useRef(null);
31091
31151
  const setProjectId = useCallback2((id) => {
31092
31152
  if (cliProjectRef.current && id !== cliProjectRef.current) return;
31093
31153
  setActiveProjectId(id);
@@ -31173,7 +31233,7 @@ ${renderCliError(classifyCliError(error, { executionMode: executionMode2 }))}`);
31173
31233
  client.setToken("");
31174
31234
  setStatus("Session expired - run /login");
31175
31235
  }, [client, storage]);
31176
- useEffect4(() => {
31236
+ useEffect3(() => {
31177
31237
  agent.setAuthFailureHandler(handle401);
31178
31238
  return () => agent.setAuthFailureHandler(null);
31179
31239
  }, [agent, handle401]);
@@ -31194,7 +31254,7 @@ ${renderCliError(classifyCliError(error, { executionMode: executionMode2 }))}`);
31194
31254
  setStatus(classifyCliError(error, { executionMode: executionMode2 }).summary);
31195
31255
  }
31196
31256
  }, [client, activeChatId, setProjectId, handle401]);
31197
- useEffect4(() => {
31257
+ useEffect3(() => {
31198
31258
  (async () => {
31199
31259
  try {
31200
31260
  if (executionMode2 === "local") {
@@ -31306,14 +31366,14 @@ ${renderCliError(classifyCliError(error, { executionMode: executionMode2 }))}`);
31306
31366
  }
31307
31367
  })();
31308
31368
  }, []);
31309
- useEffect4(() => {
31369
+ useEffect3(() => {
31310
31370
  return () => {
31311
31371
  void agent.flushPendingToolTelemetry();
31312
31372
  void agent.flushPendingUsageTelemetry();
31313
31373
  agent.fireLifecycleHook("session-end", { cwd: process.cwd() });
31314
31374
  };
31315
31375
  }, []);
31316
- useEffect4(() => {
31376
+ useEffect3(() => {
31317
31377
  if (!user) return;
31318
31378
  createIdleAwarePoll(pollingIntervalRef, lastActivityRef, client, setUser, setUsageError);
31319
31379
  return () => {
@@ -31412,9 +31472,9 @@ ${renderCliError(classifyCliError(error, { executionMode: executionMode2 }))}`);
31412
31472
  return { journalEvents, checkpoints };
31413
31473
  }
31414
31474
  });
31415
- const relayCommandRef = useRef2(handleCommand);
31475
+ const relayCommandRef = useRef(handleCommand);
31416
31476
  relayCommandRef.current = handleCommand;
31417
- useEffect4(() => {
31477
+ useEffect3(() => {
31418
31478
  if (executionMode2 !== "remote" || process.env.MSAPLING_RELAY_ENABLED !== "1" || !activeProjectId) return;
31419
31479
  let listener = null;
31420
31480
  let cancelled = false;
@@ -31463,9 +31523,6 @@ ${renderCliError(classifyCliError(error, { executionMode: executionMode2 }))}`);
31463
31523
  setHistoryOffset((current) => Math.max(0, current - amount));
31464
31524
  }
31465
31525
  }, [displayedHistory.length, historyPageStep]);
31466
- useTerminalMouseScroll((direction) => {
31467
- if (!pendingApproval && !pendingAskUser) scrollTranscript(direction);
31468
- });
31469
31526
  useInput4((input2, key) => {
31470
31527
  if (pendingApproval || pendingAskUser) return;
31471
31528
  if (key.pageUp) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mtreeai/msapling-cli",
3
- "version": "2.3.6-beta.60",
3
+ "version": "2.3.6-beta.61",
4
4
  "description": "MSapling CLI by MTreeAI — React/Ink terminal client for the MSapling backend: chat, projects, MDrive, agent tools, MCP server.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "MSapling Team",