deepcode-ai 1.0.2 → 1.0.4

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
@@ -374,7 +374,7 @@ var DeepCodeConfigSchema = z.object({
374
374
  maxIterations: z.number().int().positive().default(20),
375
375
  providerRetries: z.number().int().min(0).max(5).default(2),
376
376
  temperature: z.number().min(0).max(2).default(0.2),
377
- maxTokens: z.number().int().positive().default(4096),
377
+ maxTokens: z.number().int().positive().default(2048),
378
378
  cache: z.object({
379
379
  enabled: z.boolean().default(true),
380
380
  ttlSeconds: z.number().int().positive().max(86400).default(300)
@@ -621,6 +621,58 @@ import { Effect as Effect8 } from "effect";
621
621
  import { z as z9 } from "zod";
622
622
  import { Effect as Effect9 } from "effect";
623
623
  import { z as z10 } from "zod";
624
+ var DeepCodeError = class extends Error {
625
+ constructor(message, code, cause) {
626
+ super(message);
627
+ this.code = code;
628
+ this.cause = cause;
629
+ this.name = "DeepCodeError";
630
+ }
631
+ code;
632
+ cause;
633
+ };
634
+ var ConfigError = class extends DeepCodeError {
635
+ constructor(message, cause) {
636
+ super(message, "CONFIG_ERROR", cause);
637
+ this.name = "ConfigError";
638
+ }
639
+ };
640
+ var PermissionDeniedError = class extends DeepCodeError {
641
+ constructor(message) {
642
+ super(message, "PERMISSION_DENIED");
643
+ this.name = "PermissionDeniedError";
644
+ }
645
+ };
646
+ var PathNotAllowedError = class extends DeepCodeError {
647
+ constructor(path122, reason) {
648
+ super(`Path is not allowed: ${path122}. ${reason}`, "PATH_NOT_ALLOWED");
649
+ this.name = "PathNotAllowedError";
650
+ }
651
+ };
652
+ var ToolExecutionError = class extends DeepCodeError {
653
+ constructor(message, cause) {
654
+ super(message, "TOOL_EXECUTION_ERROR", cause);
655
+ this.name = "ToolExecutionError";
656
+ }
657
+ };
658
+ var ProviderError = class extends DeepCodeError {
659
+ constructor(message, provider, cause, options) {
660
+ super(message, "PROVIDER_ERROR", cause);
661
+ this.provider = provider;
662
+ this.name = "ProviderError";
663
+ this.statusCode = options?.statusCode;
664
+ this.retryAfterMs = options?.retryAfterMs;
665
+ }
666
+ provider;
667
+ statusCode;
668
+ retryAfterMs;
669
+ };
670
+ var BudgetExceededError = class extends DeepCodeError {
671
+ constructor(message) {
672
+ super(message, "TOKEN_BUDGET_EXCEEDED");
673
+ this.name = "BudgetExceededError";
674
+ }
675
+ };
624
676
  function resolveModelExecutionProfile(provider, model) {
625
677
  const normalized = model?.toLowerCase() ?? "";
626
678
  const openAIFamily = matchesAny(normalized, ["gpt-", "/gpt-", "o1", "o3", "o4", "o5"]);
@@ -1084,6 +1136,42 @@ function stripDisallowedControlChars(input) {
1084
1136
  }).join("");
1085
1137
  }
1086
1138
  var MAX_TOOL_OUTPUT_LENGTH = 16e3;
1139
+ var TOOL_CALL_OPEN = "<tool_call>";
1140
+ var TOOL_CALL_CLOSE = "</tool_call>";
1141
+ var XmlToolCallStreamFilter = class {
1142
+ buffer = "";
1143
+ inToolCall = false;
1144
+ filter(text) {
1145
+ this.buffer += text;
1146
+ let result = "";
1147
+ while (true) {
1148
+ if (!this.inToolCall) {
1149
+ const start = this.buffer.indexOf(TOOL_CALL_OPEN);
1150
+ if (start === -1) {
1151
+ const safe = Math.max(0, this.buffer.length - TOOL_CALL_OPEN.length);
1152
+ result += this.buffer.slice(0, safe);
1153
+ this.buffer = this.buffer.slice(safe);
1154
+ break;
1155
+ }
1156
+ result += this.buffer.slice(0, start);
1157
+ this.buffer = this.buffer.slice(start);
1158
+ this.inToolCall = true;
1159
+ } else {
1160
+ const end = this.buffer.indexOf(TOOL_CALL_CLOSE);
1161
+ if (end === -1) break;
1162
+ this.buffer = this.buffer.slice(end + TOOL_CALL_CLOSE.length);
1163
+ this.inToolCall = false;
1164
+ }
1165
+ }
1166
+ return result;
1167
+ }
1168
+ flush() {
1169
+ if (this.inToolCall) return "";
1170
+ const result = this.buffer.trim();
1171
+ this.buffer = "";
1172
+ return result;
1173
+ }
1174
+ };
1087
1175
  function compactToolDescription(description, schemaMode) {
1088
1176
  const maxLength = schemaMode === "full" ? 240 : schemaMode === "compact" ? 140 : 96;
1089
1177
  if (description.length <= maxLength) {
@@ -1259,14 +1347,17 @@ var DIRECT_SHELL_COMMAND_PATTERN = /^(?:ls|dir|pwd|date|tree|find|rg|grep|cat|st
1259
1347
  var DIRECT_UTILITY_PATH_PATTERN = /(?:^|\s)(?:~\/|\.{1,2}\/|\/)[^\s]*/;
1260
1348
  var DIRECT_UTILITY_VERB_PATTERN = /\b(?:list|lista|liste|listar|mostre|mostrar|show|display|open|abrir|abra|read|leia|print|imprima|exiba)\b/i;
1261
1349
  var DATE_TIME_QUESTION_PATTERN = /\b(?:que dia e hoje|que dia é hoje|data de hoje|dia de hoje|what day is it|what day is today|today'?s date|current date|que horas sao|que horas são|hora atual|current time|what time is it)\b/i;
1350
+ var SIMPLE_SHELL_COMMAND_PATTERN = /^(?:mkdir|touch|rmdir|cp|mv|chmod|chown|echo|ln|git\s+(?:init|clone|add|commit|push|pull|checkout|branch|stash|tag))\b/i;
1351
+ var SIMPLE_ACTION_VERB_RE = /^(?:cria|crie|criar|apaga|apague|apagar|deleta|delete|deletar|remove|mova|move|renomeia|renomeie|renomear|create|rename|mkdir|make)\b/;
1352
+ var COMPOUND_CONNECTOR_RE = /\b(?:entao|depois|tambem|alem|seguida|and then|also|afterwards|next step|subsequently)\b/;
1262
1353
  function resolveTurnStrategy(input, mode, policy) {
1263
1354
  if (mode === "build") {
1264
- if (policy.mode === "always-tools") {
1355
+ if (isDirectUtilityRequest(input, policy)) {
1265
1356
  return {
1266
1357
  allowTools: true,
1267
- shouldPlan: true,
1268
- systemPrompt: BUILD_SYSTEM_PROMPT_ALWAYS_TOOLS,
1269
- kind: "task"
1358
+ shouldPlan: false,
1359
+ systemPrompt: UTILITY_SYSTEM_PROMPT,
1360
+ kind: "utility"
1270
1361
  };
1271
1362
  }
1272
1363
  if (isConversationalTurn(input, policy)) {
@@ -1277,12 +1368,20 @@ function resolveTurnStrategy(input, mode, policy) {
1277
1368
  kind: "chat"
1278
1369
  };
1279
1370
  }
1280
- if (isDirectUtilityRequest(input, policy)) {
1371
+ if (policy.mode === "always-tools") {
1281
1372
  return {
1282
1373
  allowTools: true,
1283
1374
  shouldPlan: false,
1284
- systemPrompt: UTILITY_SYSTEM_PROMPT,
1285
- kind: "utility"
1375
+ systemPrompt: BUILD_SYSTEM_PROMPT_ALWAYS_TOOLS,
1376
+ kind: "task"
1377
+ };
1378
+ }
1379
+ if (isSimpleDirectCommand(input)) {
1380
+ return {
1381
+ allowTools: true,
1382
+ shouldPlan: false,
1383
+ systemPrompt: BUILD_SYSTEM_PROMPT,
1384
+ kind: "task"
1286
1385
  };
1287
1386
  }
1288
1387
  const looksLikeWorkspace = looksLikeWorkspaceRequest(input, policy);
@@ -1464,6 +1563,14 @@ function normalizeTurnInput(input) {
1464
1563
  function escapeRegex(input) {
1465
1564
  return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1466
1565
  }
1566
+ function isSimpleDirectCommand(input) {
1567
+ const trimmed = input.trim();
1568
+ if (SIMPLE_SHELL_COMMAND_PATTERN.test(trimmed)) return true;
1569
+ const normalized = normalizeTurnInput(trimmed);
1570
+ if (!SIMPLE_ACTION_VERB_RE.test(normalized)) return false;
1571
+ if (COMPOUND_CONNECTOR_RE.test(normalized)) return false;
1572
+ return normalized.split(/\s+/).length <= 20;
1573
+ }
1467
1574
  function resolveExecutionTarget(config, session, mode, explicitProvider) {
1468
1575
  const modeOverride = config.modeDefaults?.[mode];
1469
1576
  const provider = explicitProvider ?? modeOverride?.provider ?? session.provider ?? config.defaultProvider;
@@ -1536,42 +1643,55 @@ var Agent = class {
1536
1643
  session.status = "planning";
1537
1644
  session.metadata.plan = void 0;
1538
1645
  session.metadata.planError = void 0;
1539
- const planningProvider = this.providerManager.get(resolvedTarget.provider);
1540
- let plan;
1541
- if (turnStrategy.shouldPlan) {
1542
- try {
1543
- plan = await this.planner.plan(
1544
- options.input,
1545
- (prompt) => planningProvider.complete(prompt, {
1546
- model: resolvedModel,
1547
- maxTokens: Math.min(this.config.maxTokens, 2048),
1548
- temperature: 0,
1549
- signal: options.signal
1550
- })
1551
- );
1552
- session.metadata.plan = plan;
1553
- } catch (error) {
1554
- session.metadata.planError = error instanceof Error ? error.message : String(error);
1555
- this.eventBus.emit("app:warn", { message: `Task planning failed: ${session.metadata.planError}. Continuing without structured plan.` });
1556
- }
1557
- }
1558
1646
  this.activeBudgets.set(session.id, new SessionBudget(this.config.tokenBudget));
1559
- let finalText = "";
1560
- let iterations = 0;
1561
- const maxIterations = this.config.maxIterations;
1562
- session.status = "executing";
1563
- if (turnStrategy.kind === "utility") {
1564
- finalText = await this.executeUtilityTurn(session, options.input, mode, options);
1565
- } else if (plan && mode === "build") {
1566
- finalText = await this.executePlan(plan, session, mode, options);
1567
- } else {
1568
- finalText = await this.executeTraditional(session, mode, maxIterations, iterations, options, turnStrategy);
1647
+ try {
1648
+ const planningProvider = this.providerManager.get(resolvedTarget.provider);
1649
+ let plan;
1650
+ if (turnStrategy.shouldPlan) {
1651
+ try {
1652
+ plan = await this.planner.plan(
1653
+ options.input,
1654
+ (prompt) => planningProvider.complete(prompt, {
1655
+ model: resolvedModel,
1656
+ maxTokens: Math.min(this.config.maxTokens, 512),
1657
+ temperature: 0,
1658
+ signal: options.signal,
1659
+ onUsage: (inputTokens, outputTokens) => {
1660
+ this.recordUsage(session.id, inputTokens, outputTokens);
1661
+ }
1662
+ })
1663
+ );
1664
+ session.metadata.plan = plan;
1665
+ } catch (error) {
1666
+ if (error instanceof BudgetExceededError) {
1667
+ throw error;
1668
+ }
1669
+ session.metadata.planError = error instanceof Error ? error.message : String(error);
1670
+ this.eventBus.emit("app:warn", { message: `Task planning failed: ${session.metadata.planError}. Continuing without structured plan.` });
1671
+ }
1672
+ }
1673
+ let finalText = "";
1674
+ let iterations = 0;
1675
+ const maxIterations = this.config.maxIterations;
1676
+ session.status = "executing";
1677
+ if (turnStrategy.kind === "utility") {
1678
+ finalText = await this.executeUtilityTurn(session, options.input, mode, options);
1679
+ } else if (plan && mode === "build") {
1680
+ finalText = await this.executePlan(plan, session, mode, options);
1681
+ } else {
1682
+ finalText = await this.executeTraditional(session, mode, maxIterations, iterations, options, turnStrategy);
1683
+ }
1684
+ session.status = "idle";
1685
+ this.sessions.save(session);
1686
+ await this.sessions.persist(session.id);
1687
+ return finalText.trim();
1688
+ } catch (error) {
1689
+ session.status = "error";
1690
+ this.sessions.save(session);
1691
+ throw error;
1692
+ } finally {
1693
+ this.activeBudgets.delete(session.id);
1569
1694
  }
1570
- session.status = "idle";
1571
- this.sessions.save(session);
1572
- await this.sessions.persist(session.id);
1573
- this.activeBudgets.delete(session.id);
1574
- return finalText.trim();
1575
1695
  }
1576
1696
  /**
1577
1697
  * Execute tasks from plan in parallel rounds, respecting dependencies
@@ -1603,7 +1723,7 @@ var Agent = class {
1603
1723
  const progress = this.planner.getProgress(plan);
1604
1724
  const parallel = runnableTasks.length > 1;
1605
1725
  const taskLines = await Promise.all(
1606
- runnableTasks.map(async (task) => {
1726
+ runnableTasks.map(async (task, taskIndex) => {
1607
1727
  const taskPrompt = this.buildTaskPrompt(plan, task, progress);
1608
1728
  const executionSession = parallel ? this.createChildSession(session, task.id) : session;
1609
1729
  const maxAttempts = 1 + this.config.taskRetries;
@@ -1618,14 +1738,17 @@ Try a different approach.` : taskPrompt;
1618
1738
  const result = await this.executeTaskWithLLM(prompt, executionSession, mode, taskOptions, task.type);
1619
1739
  this.planner.updateTaskStatus(plan, task.id, "completed", result);
1620
1740
  options.onTaskUpdate?.(task, plan);
1621
- return `[${progress.completed + 1}/${progress.total}] \u2713 ${task.description}`;
1741
+ return `[${progress.completed + taskIndex + 1}/${progress.total}] \u2713 ${task.description}`;
1622
1742
  } catch (error) {
1743
+ if (error instanceof BudgetExceededError) {
1744
+ throw error;
1745
+ }
1623
1746
  lastError = error instanceof Error ? error.message : String(error);
1624
1747
  }
1625
1748
  }
1626
1749
  this.planner.updateTaskStatus(plan, task.id, "failed", void 0, lastError);
1627
1750
  options.onTaskUpdate?.(task, plan);
1628
- return `[${progress.completed + 1}/${progress.total}] \u2717 ${task.description} \u2014 ${lastError}`;
1751
+ return `[${progress.completed + taskIndex + 1}/${progress.total}] \u2717 ${task.description} \u2014 ${lastError}`;
1629
1752
  })
1630
1753
  );
1631
1754
  finalText += `${taskLines.join("\n")}
@@ -1680,6 +1803,7 @@ Execute this task using the available tools. Return a summary of what was done.`
1680
1803
  let finalAssistantText = "";
1681
1804
  while (taskIterations < maxTaskIterations) {
1682
1805
  taskIterations++;
1806
+ this.enforceBudget(session.id);
1683
1807
  const chunks = this.providerManager.chat(
1684
1808
  this.messagesForSystemPrompt(
1685
1809
  session,
@@ -1705,10 +1829,14 @@ Execute this task using the available tools. Return a summary of what was done.`
1705
1829
  );
1706
1830
  let assistantText = "";
1707
1831
  const toolCalls = [];
1832
+ const xmlFilter = textToolFallbackEnabled ? new XmlToolCallStreamFilter() : null;
1708
1833
  for await (const chunk of chunks) {
1709
1834
  if (chunk.type === "delta") {
1710
1835
  assistantText += chunk.content;
1711
- if (!textToolFallbackEnabled) {
1836
+ if (textToolFallbackEnabled) {
1837
+ const visible = xmlFilter.filter(chunk.content);
1838
+ if (visible) options.onChunk?.(visible);
1839
+ } else {
1712
1840
  options.onChunk?.(chunk.content);
1713
1841
  }
1714
1842
  }
@@ -1717,17 +1845,18 @@ Execute this task using the available tools. Return a summary of what was done.`
1717
1845
  }
1718
1846
  if (chunk.type === "usage") {
1719
1847
  options.onUsage?.(chunk.inputTokens, chunk.outputTokens);
1720
- this.activeBudgets.get(session.id)?.add(chunk.inputTokens, chunk.outputTokens);
1848
+ this.recordUsage(session.id, chunk.inputTokens, chunk.outputTokens);
1721
1849
  }
1722
1850
  }
1851
+ if (textToolFallbackEnabled) {
1852
+ const flushed = xmlFilter.flush();
1853
+ if (flushed) options.onChunk?.(flushed);
1854
+ }
1723
1855
  const turnResult = textToolFallbackEnabled ? applyFallbackToolCallParsing(assistantText, toolCalls, allowedToolNames) : { assistantText, toolCalls };
1724
1856
  assistantText = turnResult.assistantText;
1725
1857
  const nextToolCalls = [...turnResult.toolCalls];
1726
1858
  toolCalls.length = 0;
1727
1859
  toolCalls.push(...nextToolCalls);
1728
- if (textToolFallbackEnabled && assistantText) {
1729
- options.onChunk?.(assistantText);
1730
- }
1731
1860
  if (assistantText.trim() || toolCalls.length > 0) {
1732
1861
  this.sessions.addMessage(session.id, {
1733
1862
  role: "assistant",
@@ -1767,19 +1896,7 @@ ${assistantText}` : assistantText;
1767
1896
  while (iterations < maxIterations) {
1768
1897
  iterations += 1;
1769
1898
  options.onIteration?.(iterations, maxIterations);
1770
- const budget = this.activeBudgets.get(session.id);
1771
- if (budget) {
1772
- const budgetStatus = budget.check();
1773
- if (budgetStatus.status === "exceeded") {
1774
- this.eventBus.emit("budget:exceeded", budgetStatus);
1775
- throw new Error(
1776
- `Token budget exceeded (${budgetStatus.kind}): used ${budgetStatus.used.toFixed(budgetStatus.kind === "cost" ? 4 : 0)}, limit ${budgetStatus.limit}`
1777
- );
1778
- }
1779
- if (budgetStatus.status === "warning") {
1780
- this.eventBus.emit("budget:warning", budgetStatus);
1781
- }
1782
- }
1899
+ this.enforceBudget(session.id);
1783
1900
  await this.compressContextIfNeeded(session, turnStrategy.systemPrompt, options);
1784
1901
  const chunks = this.providerManager.chat(
1785
1902
  this.messagesForSystemPrompt(
@@ -1808,10 +1925,17 @@ ${assistantText}` : assistantText;
1808
1925
  );
1809
1926
  let assistantText = "";
1810
1927
  const toolCalls = [];
1928
+ const xmlFilter = textToolFallbackEnabled ? new XmlToolCallStreamFilter() : null;
1811
1929
  for await (const chunk of chunks) {
1812
1930
  if (chunk.type === "delta") {
1813
1931
  assistantText += chunk.content;
1814
- if (!textToolFallbackEnabled) {
1932
+ if (textToolFallbackEnabled) {
1933
+ const visible = xmlFilter.filter(chunk.content);
1934
+ if (visible) {
1935
+ finalText += visible;
1936
+ options.onChunk?.(visible);
1937
+ }
1938
+ } else {
1815
1939
  finalText += chunk.content;
1816
1940
  options.onChunk?.(chunk.content);
1817
1941
  }
@@ -1821,7 +1945,14 @@ ${assistantText}` : assistantText;
1821
1945
  }
1822
1946
  if (chunk.type === "usage") {
1823
1947
  options.onUsage?.(chunk.inputTokens, chunk.outputTokens);
1824
- this.activeBudgets.get(session.id)?.add(chunk.inputTokens, chunk.outputTokens);
1948
+ this.recordUsage(session.id, chunk.inputTokens, chunk.outputTokens);
1949
+ }
1950
+ }
1951
+ if (textToolFallbackEnabled) {
1952
+ const flushed = xmlFilter.flush();
1953
+ if (flushed) {
1954
+ finalText += flushed;
1955
+ options.onChunk?.(flushed);
1825
1956
  }
1826
1957
  }
1827
1958
  const turnResult = textToolFallbackEnabled ? applyFallbackToolCallParsing(assistantText, toolCalls, allowedToolNames) : { assistantText, toolCalls };
@@ -1829,10 +1960,6 @@ ${assistantText}` : assistantText;
1829
1960
  const nextToolCalls = [...turnResult.toolCalls];
1830
1961
  toolCalls.length = 0;
1831
1962
  toolCalls.push(...nextToolCalls);
1832
- if (textToolFallbackEnabled && assistantText) {
1833
- finalText += assistantText;
1834
- options.onChunk?.(assistantText);
1835
- }
1836
1963
  if (assistantText.trim() || toolCalls.length > 0) {
1837
1964
  this.sessions.addMessage(session.id, {
1838
1965
  role: "assistant",
@@ -2010,7 +2137,7 @@ ${assistantText}` : assistantText;
2010
2137
  }
2011
2138
  allowedToolNamesForTaskType(mode, taskType) {
2012
2139
  if (taskType === "research") return /* @__PURE__ */ new Set([...PLAN_ALLOWED_TOOLS]);
2013
- if (taskType === "verify") return /* @__PURE__ */ new Set(["read_file", "analyze_code", "search_text"]);
2140
+ if (taskType === "verify") return /* @__PURE__ */ new Set(["read_file", "list_dir", "analyze_code", "search_text", "bash"]);
2014
2141
  return this.allowedToolNamesForMode(mode);
2015
2142
  }
2016
2143
  toolDefinitionsForNames(names, schemaMode = "full") {
@@ -2110,6 +2237,9 @@ ${assistantText}` : assistantText;
2110
2237
  );
2111
2238
  for await (const chunk of summaryChunks) {
2112
2239
  if (chunk.type === "delta") summary += chunk.content;
2240
+ if (chunk.type === "usage") {
2241
+ this.recordUsage(session.id, chunk.inputTokens, chunk.outputTokens);
2242
+ }
2113
2243
  }
2114
2244
  const summaryMessage = buildSummaryMessage(summary);
2115
2245
  this.sessions.replaceMessages(session.id, [summaryMessage, ...toKeep, ...rest]);
@@ -2190,28 +2320,55 @@ ${assistantText}` : assistantText;
2190
2320
  utilityDateResponse() {
2191
2321
  return utilityDateResponse();
2192
2322
  }
2323
+ recordUsage(sessionId, inputTokens, outputTokens) {
2324
+ const budget = this.activeBudgets.get(sessionId);
2325
+ if (!budget) return;
2326
+ budget.add(inputTokens, outputTokens);
2327
+ this.reportBudgetStatus(budget.check());
2328
+ }
2329
+ enforceBudget(sessionId) {
2330
+ const budget = this.activeBudgets.get(sessionId);
2331
+ if (!budget) return;
2332
+ this.reportBudgetStatus(budget.check());
2333
+ }
2334
+ reportBudgetStatus(status) {
2335
+ if (status.status === "warning") {
2336
+ this.eventBus.emit("budget:warning", status);
2337
+ return;
2338
+ }
2339
+ if (status.status === "exceeded") {
2340
+ this.eventBus.emit("budget:exceeded", status);
2341
+ throw new BudgetExceededError(
2342
+ `Token budget exceeded (${status.kind}): used ${status.used.toFixed(status.kind === "cost" ? 4 : 0)}, limit ${status.limit}`
2343
+ );
2344
+ }
2345
+ }
2193
2346
  };
2194
2347
  function truncateForMetadata(value, maxLength = 2e3) {
2195
2348
  return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
2196
2349
  }
2197
2350
  var McpClient = class {
2198
2351
  process;
2352
+ ready;
2199
2353
  nextId = 1;
2200
2354
  pending = /* @__PURE__ */ new Map();
2201
- constructor(command, args, env) {
2202
- this.process = spawn(command, args, {
2355
+ constructor(command, args, env, spawnProcess = spawn) {
2356
+ this.process = spawnProcess(command, args, {
2203
2357
  stdio: ["pipe", "pipe", "pipe"],
2204
2358
  env: { ...process.env, ...env }
2205
2359
  });
2360
+ this.ready = new Promise((resolve, reject) => {
2361
+ this.process.once("spawn", () => resolve());
2362
+ this.process.once("error", reject);
2363
+ });
2364
+ let exitCode = null;
2206
2365
  const rejectAll = (error) => {
2207
2366
  for (const { reject } of this.pending.values()) reject(error);
2208
2367
  this.pending.clear();
2209
2368
  };
2210
2369
  this.process.on("error", (err) => rejectAll(err));
2211
2370
  this.process.on("exit", (code) => {
2212
- if (this.pending.size > 0) {
2213
- rejectAll(new Error(`MCP server exited unexpectedly (code ${code ?? "null"})`));
2214
- }
2371
+ exitCode = code ?? null;
2215
2372
  });
2216
2373
  const rl = createInterface({ input: this.process.stdout, terminal: false });
2217
2374
  rl.on("line", (line) => {
@@ -2230,8 +2387,14 @@ var McpClient = class {
2230
2387
  } catch {
2231
2388
  }
2232
2389
  });
2390
+ rl.on("close", () => {
2391
+ if (this.pending.size > 0) {
2392
+ rejectAll(new Error(`MCP server exited unexpectedly (code ${exitCode ?? this.process.exitCode ?? "null"})`));
2393
+ }
2394
+ });
2233
2395
  }
2234
2396
  async initialize() {
2397
+ await this.ready;
2235
2398
  await this.request("initialize", {
2236
2399
  protocolVersion: "2024-11-05",
2237
2400
  capabilities: { tools: {} },
@@ -2258,7 +2421,8 @@ var McpClient = class {
2258
2421
  }
2259
2422
  this.pending.clear();
2260
2423
  }
2261
- request(method, params) {
2424
+ async request(method, params) {
2425
+ await this.ready;
2262
2426
  const id = this.nextId++;
2263
2427
  return new Promise((resolve, reject) => {
2264
2428
  this.pending.set(id, { resolve, reject });
@@ -2308,16 +2472,18 @@ function adaptMcpTool(client, tool, serverName) {
2308
2472
  });
2309
2473
  }
2310
2474
  var McpManager = class {
2311
- constructor(events) {
2475
+ constructor(events, clientFactory = (server) => new McpClient(server.command, server.args, server.env)) {
2312
2476
  this.events = events;
2477
+ this.clientFactory = clientFactory;
2313
2478
  }
2314
2479
  events;
2480
+ clientFactory;
2315
2481
  clients = [];
2316
2482
  async connect(servers) {
2317
2483
  const tools = [];
2318
2484
  for (const server of servers) {
2319
2485
  try {
2320
- const client = new McpClient(server.command, server.args, server.env);
2486
+ const client = this.clientFactory(server);
2321
2487
  await client.initialize();
2322
2488
  const mcpTools = await client.listTools();
2323
2489
  this.clients.push({ name: server.name, client });
@@ -2453,52 +2619,6 @@ var ToolCache = class {
2453
2619
  function cacheKey(namespace, keyParts) {
2454
2620
  return createHash("sha256").update(JSON.stringify([namespace, ...keyParts])).digest("hex");
2455
2621
  }
2456
- var DeepCodeError = class extends Error {
2457
- constructor(message, code, cause) {
2458
- super(message);
2459
- this.code = code;
2460
- this.cause = cause;
2461
- this.name = "DeepCodeError";
2462
- }
2463
- code;
2464
- cause;
2465
- };
2466
- var ConfigError = class extends DeepCodeError {
2467
- constructor(message, cause) {
2468
- super(message, "CONFIG_ERROR", cause);
2469
- this.name = "ConfigError";
2470
- }
2471
- };
2472
- var PermissionDeniedError = class extends DeepCodeError {
2473
- constructor(message) {
2474
- super(message, "PERMISSION_DENIED");
2475
- this.name = "PermissionDeniedError";
2476
- }
2477
- };
2478
- var PathNotAllowedError = class extends DeepCodeError {
2479
- constructor(path122, reason) {
2480
- super(`Path is not allowed: ${path122}. ${reason}`, "PATH_NOT_ALLOWED");
2481
- this.name = "PathNotAllowedError";
2482
- }
2483
- };
2484
- var ToolExecutionError = class extends DeepCodeError {
2485
- constructor(message, cause) {
2486
- super(message, "TOOL_EXECUTION_ERROR", cause);
2487
- this.name = "ToolExecutionError";
2488
- }
2489
- };
2490
- var ProviderError = class extends DeepCodeError {
2491
- constructor(message, provider, cause, options) {
2492
- super(message, "PROVIDER_ERROR", cause);
2493
- this.provider = provider;
2494
- this.name = "ProviderError";
2495
- this.statusCode = options?.statusCode;
2496
- this.retryAfterMs = options?.retryAfterMs;
2497
- }
2498
- provider;
2499
- statusCode;
2500
- retryAfterMs;
2501
- };
2502
2622
  var ConfigLoader = class {
2503
2623
  resolveConfigPath(options) {
2504
2624
  return options.configPath ? path22.resolve(options.configPath) : path22.join(options.cwd, ".deepcode", "config.json");
@@ -3565,6 +3685,7 @@ var AnthropicProvider = class {
3565
3685
  ];
3566
3686
  for await (const chunk of this.chat(messages, options)) {
3567
3687
  if (chunk.type === "delta") output += chunk.content;
3688
+ if (chunk.type === "usage") options.onUsage?.(chunk.inputTokens, chunk.outputTokens);
3568
3689
  }
3569
3690
  return output;
3570
3691
  }
@@ -3886,6 +4007,7 @@ var OpenAICompatibleProvider = class {
3886
4007
  ];
3887
4008
  for await (const chunk of this.chat(messages, options)) {
3888
4009
  if (chunk.type === "delta") output += chunk.content;
4010
+ if (chunk.type === "usage") options.onUsage?.(chunk.inputTokens, chunk.outputTokens);
3889
4011
  }
3890
4012
  return output;
3891
4013
  }
@@ -3959,11 +4081,15 @@ var OpenAICompatibleProvider = class {
3959
4081
  }
3960
4082
  }
3961
4083
  async fetchJson(url, init) {
4084
+ const connectionTimeout = AbortSignal.timeout(3e4);
4085
+ const signal = init.signal ? AbortSignal.any([init.signal, connectionTimeout]) : connectionTimeout;
3962
4086
  try {
3963
- return await fetch(url, init);
4087
+ return await fetch(url, { ...init, signal });
3964
4088
  } catch (error) {
3965
4089
  if (isAbortError(error)) {
3966
- throw new ProviderError(`${this.name} request timed out or was cancelled`, this.id, error);
4090
+ const timedOut = connectionTimeout.aborted;
4091
+ const msg = timedOut ? `${this.name} connection timed out after 30s. Check the provider URL and network connectivity.` : `${this.name} request timed out or was cancelled`;
4092
+ throw new ProviderError(msg, this.id, error);
3967
4093
  }
3968
4094
  const message = `${this.name} network request failed: ${error instanceof Error ? error.message : String(error)}`;
3969
4095
  throw new ProviderError(redactText(message, this.secretValues()), this.id, error);
@@ -4411,13 +4537,11 @@ var PermissionGateway = class {
4411
4537
  await this.audit.log({ operation: check.operation, path: check.path, result: "allowed", reason: "session_allow" });
4412
4538
  return { allowed: true };
4413
4539
  }
4414
- const autoAllowedDirectoryListing = isAutoAllowedDirectoryListing(check, pathAccess);
4415
- if (mode === "allow" && (pathAccess === "allowed" || autoAllowedDirectoryListing)) {
4540
+ if (mode === "allow" && pathAccess === "allowed") {
4416
4541
  await this.audit.log({
4417
4542
  operation: check.operation,
4418
4543
  path: check.path,
4419
- result: "allowed",
4420
- reason: autoAllowedDirectoryListing ? "directory_probe" : void 0
4544
+ result: "allowed"
4421
4545
  });
4422
4546
  return { allowed: true };
4423
4547
  }
@@ -4589,9 +4713,6 @@ function isShellWhitelisted(allowList, operation) {
4589
4713
  (allowedOperation) => normalizeShellPermissionOperation(allowedOperation) === normalizedOperation
4590
4714
  );
4591
4715
  }
4592
- function isAutoAllowedDirectoryListing(check, pathAccess) {
4593
- return pathAccess === "outside_whitelist" && check.kind === "read" && check.operation === "list_dir";
4594
- }
4595
4716
  function configDeniedReason(check) {
4596
4717
  switch (check.kind) {
4597
4718
  case "read":
@@ -7797,7 +7918,7 @@ var en = {
7797
7918
  modelSelectorFree: "free",
7798
7919
  emptyChatSlashHint: "/provider \u2022 /model \u2022 /config \u2022 /help",
7799
7920
  deepCodeLabel: "DeepCode",
7800
- scrollHint: "PgUp/PgDn to scroll",
7921
+ scrollHint: "PgUp/PgDn \xB7 Ctrl+\u2191/\u2193 to scroll",
7801
7922
  normalModeIndicator: "NORMAL",
7802
7923
  normalModeHint: "press i to insert",
7803
7924
  streamingIndicator: "generating...",
@@ -8204,7 +8325,7 @@ var ptBR = {
8204
8325
  modelSelectorFree: "gr\xE1tis",
8205
8326
  emptyChatSlashHint: "/provider \u2022 /model \u2022 /config \u2022 /help",
8206
8327
  deepCodeLabel: "DeepCode",
8207
- scrollHint: "PgUp/PgDn para rolar",
8328
+ scrollHint: "PgUp/PgDn \xB7 Ctrl+\u2191/\u2193 para rolar",
8208
8329
  normalModeIndicator: "NORMAL",
8209
8330
  normalModeHint: "pressione i para inserir",
8210
8331
  streamingIndicator: "gerando...",
@@ -9023,9 +9144,9 @@ function ModeRoute({
9023
9144
  ] });
9024
9145
  }
9025
9146
  var DEFAULT_PANELS = {
9026
- context: { widthPercent: 28, collapsed: false },
9027
- execution: { widthPercent: 44, collapsed: false },
9028
- detail: { widthPercent: 28, collapsed: true }
9147
+ context: { widthPercent: 22, collapsed: false },
9148
+ execution: { widthPercent: 50, collapsed: false },
9149
+ detail: { widthPercent: 28, collapsed: false }
9029
9150
  };
9030
9151
  var MIN_WIDTH = 15;
9031
9152
  var RESIZE_STEP = 5;
@@ -11513,11 +11634,11 @@ function useVirtualScroll(items, viewportHeight, estimateHeight, isActive = true
11513
11634
  const halfPage = Math.max(1, Math.floor(viewportHeight / 4));
11514
11635
  useInput7(
11515
11636
  (inputChar, key) => {
11516
- if (key.pageUp) {
11637
+ if (key.pageUp || key.ctrl && key.upArrow) {
11517
11638
  scrollUp();
11518
11639
  return;
11519
11640
  }
11520
- if (key.pageDown) {
11641
+ if (key.pageDown || key.ctrl && key.downArrow) {
11521
11642
  scrollDown();
11522
11643
  return;
11523
11644
  }
@@ -12796,34 +12917,82 @@ function ConfigEditor({
12796
12917
  /* @__PURE__ */ jsx15(Text14, { children: runtime.config.github.token ? t("appPanelsGithubTokenSet") : t("appPanelsGithubTokenMissing") })
12797
12918
  ] });
12798
12919
  }
12920
+ var CMD_W = 20;
12921
+ function HelpRow({ keys, desc, theme, accent = false }) {
12922
+ return /* @__PURE__ */ jsxs15(Box14, { flexDirection: "row", children: [
12923
+ /* @__PURE__ */ jsx15(Box14, { width: CMD_W, flexShrink: 0, children: /* @__PURE__ */ jsx15(Text14, { color: accent ? theme.accent : theme.primary, bold: accent, children: keys }) }),
12924
+ /* @__PURE__ */ jsx15(Text14, { color: theme.fgMuted, children: desc })
12925
+ ] });
12926
+ }
12927
+ function HelpSection({ label, theme }) {
12928
+ return /* @__PURE__ */ jsxs15(Box14, { flexDirection: "row", gap: 1, marginTop: 1, children: [
12929
+ /* @__PURE__ */ jsx15(Text14, { color: theme.accent, bold: true, children: "\u258C" }),
12930
+ /* @__PURE__ */ jsx15(Text14, { color: theme.fg, bold: true, children: label })
12931
+ ] });
12932
+ }
12799
12933
  function HelpView({ theme }) {
12800
- return /* @__PURE__ */ jsxs15(Box14, { width: "65%", flexDirection: "column", borderStyle: "single", paddingX: 1, borderColor: theme.border, children: [
12801
- /* @__PURE__ */ jsx15(Text14, { bold: true, children: t("help") }),
12802
- /* @__PURE__ */ jsx15(Text14, { children: " " }),
12803
- /* @__PURE__ */ jsx15(Text14, { bold: true, children: t("commands") }),
12804
- /* @__PURE__ */ jsx15(Text14, { children: t("helpCommand") }),
12805
- /* @__PURE__ */ jsx15(Text14, { children: t("clearCommand") }),
12806
- /* @__PURE__ */ jsx15(Text14, { children: t("newCommand") }),
12807
- /* @__PURE__ */ jsx15(Text14, { children: t("sessionsCommand") }),
12808
- /* @__PURE__ */ jsx15(Text14, { children: t("configCommand") }),
12809
- /* @__PURE__ */ jsx15(Text14, { children: " " }),
12810
- /* @__PURE__ */ jsx15(Text14, { bold: true, children: t("generalShortcuts") }),
12811
- /* @__PURE__ */ jsx15(Text14, { children: t("ctrlShortcuts") }),
12812
- /* @__PURE__ */ jsx15(Text14, { children: t("ctrlHelpTelemetry") }),
12813
- /* @__PURE__ */ jsx15(Text14, { children: t("numberTabs") }),
12814
- /* @__PURE__ */ jsx15(Text14, { children: " " }),
12815
- /* @__PURE__ */ jsx15(Text14, { bold: true, children: t("vimBindingsChat") }),
12816
- /* @__PURE__ */ jsx15(Text14, { children: t("vimInsertMode") }),
12817
- /* @__PURE__ */ jsx15(Text14, { children: " " }),
12818
- /* @__PURE__ */ jsx15(Text14, { bold: true, children: t("vimBindingsConfig") }),
12819
- /* @__PURE__ */ jsx15(Text14, { children: t("vimConfigNav") }),
12820
- /* @__PURE__ */ jsx15(Text14, { children: " " }),
12821
- /* @__PURE__ */ jsx15(Text14, { bold: true, children: t("approvals") }),
12822
- /* @__PURE__ */ jsx15(Text14, { children: t("approvalKeys") }),
12823
- /* @__PURE__ */ jsx15(Text14, { children: " " }),
12824
- /* @__PURE__ */ jsx15(Text14, { bold: true, children: t("availableThemes") }),
12825
- /* @__PURE__ */ jsx15(Text14, { children: themeNames.join(", ") }),
12826
- /* @__PURE__ */ jsx15(Text14, { children: t("changeThemeHint") })
12934
+ return /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", borderStyle: "single", paddingX: 1, borderColor: theme.border, children: [
12935
+ /* @__PURE__ */ jsxs15(Box14, { flexDirection: "row", gap: 1, children: [
12936
+ /* @__PURE__ */ jsx15(Text14, { color: theme.primary, bold: true, children: "\u25C6" }),
12937
+ /* @__PURE__ */ jsx15(Text14, { color: theme.fg, bold: true, children: "Ajuda \u2014 DeepCode" }),
12938
+ /* @__PURE__ */ jsx15(Text14, { color: theme.fgMuted, dimColor: true, children: " Esc \xB7 Enter \xB7 q para fechar" })
12939
+ ] }),
12940
+ /* @__PURE__ */ jsx15(HelpSection, { label: "COMANDOS SLASH", theme }),
12941
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/help", desc: "Abre esta ajuda", theme }),
12942
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/provider", desc: "Configura provider e API key", theme }),
12943
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/model", desc: "Seleciona modelo ativo", theme }),
12944
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/mode plan", desc: "Modo PLAN \u2014 analisa sem editar arquivos", theme }),
12945
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/mode build", desc: "Modo BUILD \u2014 edita, executa e valida", theme }),
12946
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/config", desc: "Abre editor de configura\xE7\xE3o", theme }),
12947
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/sessions", desc: "Lista e troca entre sess\xF5es salvas", theme }),
12948
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/new", desc: "Cria uma sess\xE3o nova", theme }),
12949
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/clear", desc: "Limpa o chat sem apagar a sess\xE3o", theme }),
12950
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/undo", desc: "Reverte a \xFAltima altera\xE7\xE3o de arquivo", theme }),
12951
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/diff", desc: "Mostra resumo de todas as altera\xE7\xF5es pendentes", theme }),
12952
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/timeline", desc: "Linha do tempo de a\xE7\xF5es da sess\xE3o", theme }),
12953
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "/github-login", desc: "Autenticar GitHub via OAuth", theme }),
12954
+ /* @__PURE__ */ jsx15(HelpSection, { label: "ATALHOS GLOBAIS", theme }),
12955
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+C", desc: "Cancelar execu\xE7\xE3o em curso \xB7 Ctrl+Q para sair", theme, accent: true }),
12956
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+H", desc: "Abrir esta ajuda", theme, accent: true }),
12957
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+O", desc: "Seletor de sess\xF5es", theme, accent: true }),
12958
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+N", desc: "Nova sess\xE3o", theme, accent: true }),
12959
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+P", desc: "Modal de provider", theme, accent: true }),
12960
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+M", desc: "Seletor de modelo", theme, accent: true }),
12961
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+T", desc: "Painel de telemetria", theme, accent: true }),
12962
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+R", desc: "Buscar no hist\xF3rico de prompts", theme, accent: true }),
12963
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Tab", desc: "Alternar entre modos PLAN \u2194 BUILD", theme, accent: true }),
12964
+ /* @__PURE__ */ jsx15(HelpSection, { label: "PAIN\xC9IS (L=esquerdo C=central D=direito)", theme }),
12965
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+1/2/3", desc: "Fechar/abrir painel L / C / D", theme, accent: true }),
12966
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+B", desc: "Toggle sidebar (painel L)", theme, accent: true }),
12967
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+F", desc: "Alternar sidebar \u2194 \xE1rvore de arquivos", theme, accent: true }),
12968
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+L", desc: "Toggle timeline no painel D", theme, accent: true }),
12969
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+,", desc: "Toggle configura\xE7\xE3o no painel D", theme, accent: true }),
12970
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+\u2190/\u2192", desc: "Redimensionar painel ativo", theme, accent: true }),
12971
+ /* @__PURE__ */ jsx15(HelpSection, { label: "SCROLL DO CHAT", theme }),
12972
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "PgUp / PgDn", desc: "Rolar para cima/baixo", theme }),
12973
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Ctrl+\u2191 / Ctrl+\u2193", desc: "Rolar para cima/baixo (alternativo)", theme }),
12974
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Esc \u2192 j / k", desc: "Vim normal mode: j baixo \xB7 k cima", theme }),
12975
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Esc \u2192 G / gg", desc: "Ir ao fim / topo da conversa", theme }),
12976
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "Esc \u2192 Ctrl+D/U", desc: "Meio-p\xE1gina para baixo / cima", theme }),
12977
+ /* @__PURE__ */ jsx15(HelpSection, { label: "VIM (Esc = insert\u2192normal \xB7 i = normal\u2192insert)", theme }),
12978
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "i / a / A", desc: "Insert: na pos / ap\xF3s cursor / no fim", theme }),
12979
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "I / S", desc: "Insert: no in\xEDcio / limpar tudo + insert", theme }),
12980
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "h / l", desc: "Mover cursor \u2190 / \u2192", theme }),
12981
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "w / b / e", desc: "Palavra: pr\xF3xima / anterior / fim", theme }),
12982
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "0 / $ / ^", desc: "In\xEDcio / fim / 1\xBA char n\xE3o-vazio da linha", theme }),
12983
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "x / X", desc: "Deletar char sob cursor / \xE0 esquerda", theme }),
12984
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "D / C", desc: "Deletar / mudar at\xE9 o fim da linha", theme }),
12985
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "dd / dw", desc: "Deletar linha inteira / palavra", theme }),
12986
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "cc / cw / cb", desc: "Mudar linha / palavra / tr\xE1s", theme }),
12987
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "r<c>", desc: "Substituir o char sob o cursor", theme }),
12988
+ /* @__PURE__ */ jsx15(HelpSection, { label: "APROVA\xC7\xD5ES (quando o agente pede permiss\xE3o)", theme }),
12989
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "a", desc: "Aprovar uma vez", theme }),
12990
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "l", desc: "Aprovar sempre (adicionar ao allow-list)", theme }),
12991
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "s", desc: "Aprovar nesta sess\xE3o", theme }),
12992
+ /* @__PURE__ */ jsx15(HelpRow, { keys: "d / n / Esc", desc: "Negar", theme }),
12993
+ /* @__PURE__ */ jsx15(HelpSection, { label: "TEMAS", theme }),
12994
+ /* @__PURE__ */ jsx15(Box14, { paddingLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx15(Text14, { color: theme.fgMuted, children: themeNames.join(" \xB7 ") }) }),
12995
+ /* @__PURE__ */ jsx15(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx15(Text14, { color: theme.fgMuted, dimColor: true, children: "Alterar: /config \u2192 tui.theme" }) })
12827
12996
  ] });
12828
12997
  }
12829
12998
  function EmptyChatState({
@@ -13108,8 +13277,8 @@ function MessageList({
13108
13277
  return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", flexGrow: 1, children: [
13109
13278
  vimMode === "normal" && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx17(Text16, { color: theme.fgMuted, dimColor: true, children: "j/k scroll \xB7 gg top \xB7 G bottom \xB7 Ctrl+d/u half-page \xB7 i insert" }) }),
13110
13279
  canScrollUp && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "row", gap: 1, paddingX: 1, children: [
13111
- /* @__PURE__ */ jsx17(Text16, { color: theme.fgMuted, dimColor: true, children: "\u2574\u2574\u2574" }),
13112
- /* @__PURE__ */ jsxs17(Text16, { color: theme.fgMuted, dimColor: true, children: [
13280
+ /* @__PURE__ */ jsx17(Text16, { color: theme.fgMuted, children: "\u2574\u2574\u2574" }),
13281
+ /* @__PURE__ */ jsxs17(Text16, { color: theme.accent, children: [
13113
13282
  "\u2191 ",
13114
13283
  t("scrollHint")
13115
13284
  ] })
@@ -13132,8 +13301,8 @@ function MessageList({
13132
13301
  /* @__PURE__ */ jsx17(Box16, { paddingLeft: 2, children: /* @__PURE__ */ jsx17(MarkdownText, { text: assistantDraft, theme }) })
13133
13302
  ] }),
13134
13303
  canScrollDown && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "row", gap: 1, paddingX: 1, children: [
13135
- /* @__PURE__ */ jsx17(Text16, { color: theme.fgMuted, dimColor: true, children: "\u2574\u2574\u2574" }),
13136
- /* @__PURE__ */ jsxs17(Text16, { color: theme.fgMuted, dimColor: true, children: [
13304
+ /* @__PURE__ */ jsx17(Text16, { color: theme.fgMuted, children: "\u2574\u2574\u2574" }),
13305
+ /* @__PURE__ */ jsxs17(Text16, { color: theme.accent, children: [
13137
13306
  "\u2193 ",
13138
13307
  t("scrollHint")
13139
13308
  ] })
@@ -14097,6 +14266,26 @@ function formatValue(v, max = 60) {
14097
14266
  const s = typeof v === "string" ? v : JSON.stringify(v) ?? "";
14098
14267
  return s.length > max ? `${s.slice(0, max)}\u2026` : s;
14099
14268
  }
14269
+ function toolIcon(name) {
14270
+ if (name === "bash" || name === "shell" || name.includes("bash")) return "\u26A1";
14271
+ if (name.includes("read") || name.includes("file")) return "\u{1F4C4}";
14272
+ if (name.includes("write") || name.includes("edit")) return "\u270F";
14273
+ if (name.includes("search") || name.includes("grep")) return "\u{1F50D}";
14274
+ if (name.includes("git")) return "\u2387";
14275
+ if (name.includes("web") || name.includes("fetch")) return "\u{1F310}";
14276
+ return "\u25C6";
14277
+ }
14278
+ function toolPreview(name, args) {
14279
+ if (name === "bash" || name === "shell") {
14280
+ const cmd = args.command ?? args.cmd ?? args.input;
14281
+ return typeof cmd === "string" ? cmd.slice(0, 40) : "";
14282
+ }
14283
+ const path42 = args.path ?? args.file_path ?? args.filePath ?? args.filename;
14284
+ if (typeof path42 === "string") return path42.split("/").slice(-2).join("/");
14285
+ const query = args.query ?? args.pattern ?? args.search ?? args.command;
14286
+ if (typeof query === "string") return query.slice(0, 40);
14287
+ return "";
14288
+ }
14100
14289
  function ToolInspector({
14101
14290
  toolCalls,
14102
14291
  toolExecuting,
@@ -14128,36 +14317,48 @@ function ToolInspector({
14128
14317
  parsedArgs = JSON.parse(selected?.args ?? "{}");
14129
14318
  } catch {
14130
14319
  }
14320
+ const preview = selected ? toolPreview(selected.name, parsedArgs) : "";
14131
14321
  return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [
14132
14322
  /* @__PURE__ */ jsxs32(Box31, { flexDirection: "row", justifyContent: "space-between", marginBottom: 1, children: [
14133
14323
  /* @__PURE__ */ jsx32(Text31, { bold: true, color: theme.primary, children: t("toolInspectorTitle") }),
14134
- /* @__PURE__ */ jsx32(Text31, { color: theme.fgMuted, dimColor: true, children: t("toolInspectorHint") })
14324
+ /* @__PURE__ */ jsx32(Text31, { color: theme.fgMuted, dimColor: true, children: toolCalls.length > 0 ? `${toolCalls.length}` : "" })
14135
14325
  ] }),
14136
14326
  toolCalls.length === 0 ? /* @__PURE__ */ jsx32(Text31, { color: theme.fgMuted, dimColor: true, children: toolExecuting ? t("toolInspectorExecuting") : t("toolInspectorEmpty") }) : /* @__PURE__ */ jsxs32(Fragment6, { children: [
14137
14327
  /* @__PURE__ */ jsx32(Box31, { flexDirection: "column", marginBottom: 1, children: visible.map((tc, i) => {
14138
14328
  const absIdx = windowStart + i;
14139
14329
  const sel = absIdx === clamped;
14140
14330
  const done = tc.result !== void 0;
14141
- return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "row", gap: 1, children: [
14142
- /* @__PURE__ */ jsx32(Text31, { color: sel ? theme.primary : theme.fgMuted, children: sel ? "\u25B6" : " " }),
14143
- /* @__PURE__ */ jsx32(Text31, { color: done ? theme.success : theme.warning, children: done ? "\u2713" : toolExecuting && absIdx === toolCalls.length - 1 ? "\u2026" : "\u25CB" }),
14144
- /* @__PURE__ */ jsx32(Text31, { color: sel ? theme.fg : theme.fgMuted, bold: sel, children: tc.name })
14145
- ] }, tc.id);
14331
+ const running = toolExecuting && absIdx === toolCalls.length - 1;
14332
+ let tcArgs = {};
14333
+ try {
14334
+ tcArgs = JSON.parse(tc.args ?? "{}");
14335
+ } catch {
14336
+ }
14337
+ const linePreview = toolPreview(tc.name, tcArgs);
14338
+ return /* @__PURE__ */ jsx32(Box31, { flexDirection: "column", children: /* @__PURE__ */ jsxs32(Box31, { flexDirection: "row", gap: 1, children: [
14339
+ /* @__PURE__ */ jsx32(Text31, { color: done ? theme.success : running ? theme.warning : theme.fgMuted, children: done ? "\u2713" : running ? "\u2026" : "\u25CB" }),
14340
+ /* @__PURE__ */ jsx32(Text31, { color: theme.accent, children: toolIcon(tc.name) }),
14341
+ /* @__PURE__ */ jsx32(Text31, { color: sel ? theme.fg : theme.fgMuted, bold: sel, children: tc.name }),
14342
+ linePreview && /* @__PURE__ */ jsx32(Text31, { color: theme.fgMuted, dimColor: true, wrap: "truncate", children: linePreview })
14343
+ ] }) }, tc.id);
14146
14344
  }) }),
14147
14345
  selected && /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", borderStyle: "single", borderColor: theme.border, paddingX: 1, children: [
14148
- /* @__PURE__ */ jsx32(Text31, { bold: true, color: theme.fg, children: selected.name }),
14149
- /* @__PURE__ */ jsx32(Text31, { children: " " }),
14150
- Object.entries(parsedArgs).slice(0, 5).map(([k, v]) => /* @__PURE__ */ jsxs32(Box31, { flexDirection: "row", gap: 1, children: [
14151
- /* @__PURE__ */ jsxs32(Text31, { color: theme.accent, children: [
14346
+ /* @__PURE__ */ jsxs32(Box31, { flexDirection: "row", gap: 1, children: [
14347
+ /* @__PURE__ */ jsx32(Text31, { color: theme.accent, children: toolIcon(selected.name) }),
14348
+ /* @__PURE__ */ jsx32(Text31, { bold: true, color: theme.fg, children: selected.name })
14349
+ ] }),
14350
+ preview && /* @__PURE__ */ jsx32(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text31, { color: theme.warning, wrap: "wrap", children: preview }) }),
14351
+ Object.entries(parsedArgs).slice(0, 4).map(([k, v]) => /* @__PURE__ */ jsxs32(Box31, { flexDirection: "row", gap: 1, children: [
14352
+ /* @__PURE__ */ jsxs32(Text31, { color: theme.fgMuted, children: [
14152
14353
  k,
14153
14354
  ":"
14154
14355
  ] }),
14155
- /* @__PURE__ */ jsx32(Text31, { color: theme.fgMuted, wrap: "truncate", children: formatValue(v) })
14356
+ /* @__PURE__ */ jsx32(Text31, { color: theme.fgMuted, dimColor: true, wrap: "truncate", children: formatValue(v) })
14156
14357
  ] }, k)),
14157
14358
  selected.result !== void 0 && /* @__PURE__ */ jsxs32(Fragment6, { children: [
14158
14359
  /* @__PURE__ */ jsx32(Text31, { children: " " }),
14159
14360
  /* @__PURE__ */ jsx32(Text31, { bold: true, color: theme.fg, children: t("toolInspectorResult") }),
14160
- /* @__PURE__ */ jsx32(Text31, { color: theme.fgMuted, wrap: "wrap", children: formatValue(selected.result, 300) })
14361
+ /* @__PURE__ */ jsx32(Text31, { color: theme.fgMuted, wrap: "wrap", children: formatValue(selected.result, 200) })
14161
14362
  ] })
14162
14363
  ] }),
14163
14364
  toolCalls.length > LIST_SIZE && /* @__PURE__ */ jsx32(Box31, { marginTop: 1, children: /* @__PURE__ */ jsxs32(Text31, { color: theme.fgMuted, dimColor: true, children: [
@@ -14730,7 +14931,7 @@ function App(props) {
14730
14931
  return /* @__PURE__ */ jsx33(Text32, { children: t("loadingDeepCode") });
14731
14932
  }
14732
14933
  const activeApproval = approvals[0];
14733
- const hasParallelTasks = Object.keys(taskBuffers).length > 1;
14934
+ const hasParallelTasks = Object.keys(taskBuffers).length > 0;
14734
14935
  const approvalHasDiff = Boolean(activeApproval?.diff);
14735
14936
  const detailPanelContent = detailContent === "timeline" ? /* @__PURE__ */ jsx33(
14736
14937
  SessionTimeline,
@@ -14775,7 +14976,7 @@ function App(props) {
14775
14976
  toolCalls,
14776
14977
  toolExecuting,
14777
14978
  theme,
14778
- isActive: detailContent === "none" && !activeModal,
14979
+ isActive: false,
14779
14980
  onClose: () => useUIStore.getState().closePanel("detail")
14780
14981
  }
14781
14982
  );