@defai.digital/ax-cli 3.12.10 → 3.13.1

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 (81) hide show
  1. package/README.md +17 -5
  2. package/dist/agent/loop-detector.d.ts +2 -0
  3. package/dist/agent/loop-detector.js +11 -15
  4. package/dist/agent/loop-detector.js.map +1 -1
  5. package/dist/agent/subagent.js +3 -3
  6. package/dist/agent/subagent.js.map +1 -1
  7. package/dist/analyzers/git/churn-calculator.js +4 -1
  8. package/dist/analyzers/git/churn-calculator.js.map +1 -1
  9. package/dist/constants.js +1 -1
  10. package/dist/hooks/hook-runner.js +1 -1
  11. package/dist/hooks/hook-runner.js.map +1 -1
  12. package/dist/index.js +2 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/llm/tools.d.ts +39 -0
  15. package/dist/llm/tools.js +55 -0
  16. package/dist/llm/tools.js.map +1 -1
  17. package/dist/mcp/client-v2.d.ts +43 -0
  18. package/dist/mcp/client-v2.js +96 -0
  19. package/dist/mcp/client-v2.js.map +1 -1
  20. package/dist/mcp/client.d.ts +27 -0
  21. package/dist/mcp/client.js +24 -0
  22. package/dist/mcp/client.js.map +1 -1
  23. package/dist/mcp/error-formatter.js +219 -33
  24. package/dist/mcp/error-formatter.js.map +1 -1
  25. package/dist/mcp/index.d.ts +3 -1
  26. package/dist/mcp/index.js +3 -0
  27. package/dist/mcp/index.js.map +1 -1
  28. package/dist/mcp/prompts.d.ts +68 -0
  29. package/dist/mcp/prompts.js +129 -0
  30. package/dist/mcp/prompts.js.map +1 -0
  31. package/dist/mcp/resources.d.ts +1 -0
  32. package/dist/mcp/resources.js +9 -2
  33. package/dist/mcp/resources.js.map +1 -1
  34. package/dist/planner/task-planner.js +2 -2
  35. package/dist/planner/task-planner.js.map +1 -1
  36. package/dist/sdk/index.d.ts +5 -1
  37. package/dist/sdk/index.js +9 -1
  38. package/dist/sdk/index.js.map +1 -1
  39. package/dist/tools/ask-user.js +2 -1
  40. package/dist/tools/ask-user.js.map +1 -1
  41. package/dist/tools/bash.js +4 -5
  42. package/dist/tools/bash.js.map +1 -1
  43. package/dist/ui/components/chat-history.js +2 -63
  44. package/dist/ui/components/chat-history.js.map +1 -1
  45. package/dist/ui/components/chat-interface.js +11 -5
  46. package/dist/ui/components/chat-interface.js.map +1 -1
  47. package/dist/ui/components/collapsible-tool-result.d.ts +12 -0
  48. package/dist/ui/components/collapsible-tool-result.js +56 -50
  49. package/dist/ui/components/collapsible-tool-result.js.map +1 -1
  50. package/dist/ui/components/mcp-dashboard.d.ts +15 -0
  51. package/dist/ui/components/mcp-dashboard.js +520 -0
  52. package/dist/ui/components/mcp-dashboard.js.map +1 -0
  53. package/dist/ui/components/mcp-status.js +37 -15
  54. package/dist/ui/components/mcp-status.js.map +1 -1
  55. package/dist/ui/components/phase-progress.js +26 -69
  56. package/dist/ui/components/phase-progress.js.map +1 -1
  57. package/dist/ui/components/status-bar.d.ts +8 -0
  58. package/dist/ui/components/status-bar.js +53 -19
  59. package/dist/ui/components/status-bar.js.map +1 -1
  60. package/dist/ui/components/subagent-monitor.js +2 -25
  61. package/dist/ui/components/subagent-monitor.js.map +1 -1
  62. package/dist/ui/components/toast-notification.js +9 -14
  63. package/dist/ui/components/toast-notification.js.map +1 -1
  64. package/dist/ui/components/tool-group-display.js +8 -4
  65. package/dist/ui/components/tool-group-display.js.map +1 -1
  66. package/dist/ui/components/virtualized-chat-history.js +1 -44
  67. package/dist/ui/components/virtualized-chat-history.js.map +1 -1
  68. package/dist/ui/hooks/use-enhanced-input.js +10 -11
  69. package/dist/ui/hooks/use-enhanced-input.js.map +1 -1
  70. package/dist/ui/hooks/use-input-handler.d.ts +5 -1
  71. package/dist/ui/hooks/use-input-handler.js +277 -14
  72. package/dist/ui/hooks/use-input-handler.js.map +1 -1
  73. package/dist/ui/utils/change-summarizer.js +25 -11
  74. package/dist/ui/utils/change-summarizer.js.map +1 -1
  75. package/dist/ui/utils/semantic-action-detector.js +33 -30
  76. package/dist/ui/utils/semantic-action-detector.js.map +1 -1
  77. package/dist/ui/utils/tool-grouper.d.ts +4 -0
  78. package/dist/ui/utils/tool-grouper.js +71 -67
  79. package/dist/ui/utils/tool-grouper.js.map +1 -1
  80. package/eslint.config.js +2 -0
  81. package/package.json +2 -2
@@ -20,14 +20,21 @@ import { openExternalEditor, getPreferredEditor, getEditorDisplayName } from "..
20
20
  import { extractErrorMessage } from "../../utils/error-handler.js";
21
21
  import { getCustomCommandsManager } from "../../commands/custom-commands.js";
22
22
  import { getHooksManager } from "../../hooks/index.js";
23
+ import { getMCPPrompts, getMCPManager, getMCPResources } from "../../llm/tools.js";
24
+ import { promptToSlashCommand, parsePromptCommand, formatPromptResult, getPromptDescription } from "../../mcp/prompts.js";
25
+ import { getPermissionManager, PermissionTier } from "../../permissions/permission-manager.js";
23
26
  import { parseFileMentions } from "../../utils/file-mentions.js";
24
27
  import * as fs from "fs";
25
28
  import * as path from "path";
26
29
  export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProcessing, setIsStreaming, setTokenCount, setProcessingTime, processingStartTime, isProcessing, isStreaming, isConfirmationActive = false, onQuickActionsToggle, onVerboseModeChange, onBackgroundModeChange, onAutoEditModeChange, onTaskMovedToBackground, onOperationInterrupted, onChatCleared, onCopyLastResponse, onMemoryWarmed, onMemoryRefreshed, onCheckpointCreated: _onCheckpointCreated, // Reserved for future checkpoint UI
27
30
  onCheckpointRestored: _onCheckpointRestored, // Reserved for future checkpoint UI
28
- onLargePaste, onPasteTruncated, onKeyboardHelp, onThinkingModeChange, onEditorOpening, onEditorSuccess, onEditorCancelled, onEditorError, }) {
31
+ onLargePaste, onPasteTruncated, onKeyboardHelp, onMcpDashboardToggle, onThinkingModeChange, onEditorOpening, onEditorSuccess, onEditorCancelled, onEditorError, }) {
29
32
  const [showCommandSuggestions, setShowCommandSuggestions] = useState(false);
30
33
  const [selectedCommandIndex, setSelectedCommandIndex] = useState(0);
34
+ const [resourceSuggestions, setResourceSuggestions] = useState([]);
35
+ const [suggestionMode, setSuggestionMode] = useState("command");
36
+ // BUG FIX: Track current MCP query to prevent race condition with stale async results
37
+ const currentMcpQueryRef = useRef(null);
31
38
  const retryTimeoutRef = useRef(null);
32
39
  const [autoEditEnabled, setAutoEditEnabled] = useState(() => {
33
40
  const confirmationService = ConfirmationService.getInstance();
@@ -69,6 +76,11 @@ onLargePaste, onPasteTruncated, onKeyboardHelp, onThinkingModeChange, onEditorOp
69
76
  if (showCommandSuggestions) {
70
77
  setShowCommandSuggestions(false);
71
78
  setSelectedCommandIndex(0);
79
+ // Reset resource mode if active
80
+ if (suggestionMode === "resource") {
81
+ setResourceSuggestions([]);
82
+ setSuggestionMode("command");
83
+ }
72
84
  return true;
73
85
  }
74
86
  if (isProcessing || isStreaming) {
@@ -84,33 +96,51 @@ onLargePaste, onPasteTruncated, onKeyboardHelp, onThinkingModeChange, onEditorOp
84
96
  }
85
97
  return false; // Let default escape handling work
86
98
  }
87
- // Handle command suggestions navigation
99
+ // Handle command/resource suggestions navigation
88
100
  if (showCommandSuggestions) {
89
- const filteredSuggestions = filterCommandSuggestions(commandSuggestions, input);
90
- if (filteredSuggestions.length === 0) {
101
+ // Get appropriate suggestions based on mode
102
+ const currentSuggestions = suggestionMode === "resource"
103
+ ? resourceSuggestions.map((r) => ({ command: r.reference, description: r.description || r.name }))
104
+ : filterCommandSuggestions(commandSuggestions, input);
105
+ if (currentSuggestions.length === 0) {
91
106
  setShowCommandSuggestions(false);
92
107
  setSelectedCommandIndex(0);
93
108
  return false; // Continue processing
94
109
  }
95
110
  else {
96
111
  if (key.upArrow) {
97
- setSelectedCommandIndex((prev) => prev === 0 ? filteredSuggestions.length - 1 : prev - 1);
112
+ setSelectedCommandIndex((prev) => prev === 0 ? currentSuggestions.length - 1 : prev - 1);
98
113
  return true;
99
114
  }
100
115
  if (key.downArrow) {
101
- setSelectedCommandIndex((prev) => (prev + 1) % filteredSuggestions.length);
116
+ setSelectedCommandIndex((prev) => (prev + 1) % currentSuggestions.length);
102
117
  return true;
103
118
  }
104
119
  if (key.tab || key.return) {
105
120
  // Check if there are any suggestions available
106
- if (filteredSuggestions.length === 0) {
121
+ if (currentSuggestions.length === 0) {
107
122
  return true; // No suggestions, do nothing
108
123
  }
109
- const safeIndex = Math.min(selectedCommandIndex, filteredSuggestions.length - 1);
110
- const selectedCommand = filteredSuggestions[safeIndex];
111
- const newInput = selectedCommand.command + " ";
112
- setInput(newInput);
113
- setCursorPosition(newInput.length);
124
+ const safeIndex = Math.min(selectedCommandIndex, currentSuggestions.length - 1);
125
+ const selected = currentSuggestions[safeIndex];
126
+ if (suggestionMode === "resource") {
127
+ // Replace @mcp:partial with the full reference
128
+ const mcpMatch = input.match(/@mcp:[^\s]*$/);
129
+ if (mcpMatch) {
130
+ const newInput = input.replace(/@mcp:[^\s]*$/, selected.command + " ");
131
+ setInput(newInput);
132
+ setCursorPosition(newInput.length);
133
+ }
134
+ // Reset resource mode
135
+ setResourceSuggestions([]);
136
+ setSuggestionMode("command");
137
+ }
138
+ else {
139
+ // Command suggestion - replace entire input
140
+ const newInput = selected.command + " ";
141
+ setInput(newInput);
142
+ setCursorPosition(newInput.length);
143
+ }
114
144
  setShowCommandSuggestions(false);
115
145
  setSelectedCommandIndex(0);
116
146
  return true;
@@ -132,6 +162,41 @@ onLargePaste, onPasteTruncated, onKeyboardHelp, onThinkingModeChange, onEditorOp
132
162
  }
133
163
  };
134
164
  const handleInputChange = useCallback((newInput) => {
165
+ // Check for @mcp: resource auto-complete
166
+ const mcpMatch = newInput.match(/@mcp:([^\s]*)$/);
167
+ if (mcpMatch) {
168
+ const query = mcpMatch[1].toLowerCase();
169
+ // BUG FIX: Track current query to prevent race condition
170
+ // If user types faster than async completes, stale results are discarded
171
+ currentMcpQueryRef.current = query;
172
+ // Load resources asynchronously
173
+ getMCPResources().then((resources) => {
174
+ // BUG FIX: Only apply results if query hasn't changed (prevents stale suggestions)
175
+ if (currentMcpQueryRef.current !== query) {
176
+ return; // Query changed, discard stale results
177
+ }
178
+ const filtered = resources.filter((r) => r.reference.toLowerCase().includes(query) ||
179
+ r.name.toLowerCase().includes(query) ||
180
+ (r.description && r.description.toLowerCase().includes(query)));
181
+ setResourceSuggestions(filtered);
182
+ setSuggestionMode("resource");
183
+ setShowCommandSuggestions(true);
184
+ setSelectedCommandIndex(0);
185
+ }).catch(() => {
186
+ // Only clear if this is still the current query
187
+ if (currentMcpQueryRef.current === query) {
188
+ setResourceSuggestions([]);
189
+ }
190
+ });
191
+ return;
192
+ }
193
+ // Reset resource mode if it was active but no longer matches
194
+ // BUG FIX: Also clear the query ref when leaving resource mode
195
+ if (suggestionMode === "resource") {
196
+ currentMcpQueryRef.current = null;
197
+ setResourceSuggestions([]);
198
+ setSuggestionMode("command");
199
+ }
135
200
  // Update command suggestions based on input
136
201
  if (newInput.startsWith("/")) {
137
202
  setShowCommandSuggestions(true);
@@ -141,7 +206,7 @@ onLargePaste, onPasteTruncated, onKeyboardHelp, onThinkingModeChange, onEditorOp
141
206
  setShowCommandSuggestions(false);
142
207
  setSelectedCommandIndex(0);
143
208
  }
144
- }, []);
209
+ }, [suggestionMode]);
145
210
  const handleVerboseToggle = useCallback(() => {
146
211
  // Cycle through verbosity levels: QUIET -> CONCISE -> VERBOSE -> QUIET
147
212
  setVerbosityLevel((prev) => {
@@ -265,6 +330,8 @@ onLargePaste, onPasteTruncated, onKeyboardHelp, onThinkingModeChange, onEditorOp
265
330
  { command: "/init", description: "Initialize project with smart analysis" },
266
331
  { command: "/usage", description: "Show API usage statistics" },
267
332
  { command: "/doctor", description: "Run health check diagnostics" },
333
+ { command: "/mcp", description: "Open MCP server dashboard" },
334
+ { command: "/permissions", description: "View/manage tool permissions" },
268
335
  { command: "/tasks", description: "List background tasks" },
269
336
  { command: "/task", description: "View output of a background task" },
270
337
  { command: "/kill", description: "Kill a background task" },
@@ -291,7 +358,13 @@ onLargePaste, onPasteTruncated, onKeyboardHelp, onThinkingModeChange, onEditorOp
291
358
  command: `/${cmd.name}`,
292
359
  description: `${cmd.description} [${cmd.scope}]`,
293
360
  }));
294
- return [...builtIn, ...customSuggestions];
361
+ // Add MCP prompts as slash commands
362
+ const mcpPrompts = getMCPPrompts();
363
+ const mcpPromptSuggestions = mcpPrompts.map((prompt) => ({
364
+ command: promptToSlashCommand(prompt),
365
+ description: getPromptDescription(prompt),
366
+ }));
367
+ return [...builtIn, ...customSuggestions, ...mcpPromptSuggestions];
295
368
  }, [customCommandsManager]);
296
369
  // Load models from configuration with fallback to defaults
297
370
  const availableModels = useMemo(() => {
@@ -626,6 +699,7 @@ Built-in Commands:
626
699
  /shortcuts - Show keyboard shortcuts guide
627
700
  /usage - Show API usage statistics
628
701
  /doctor - Run health check diagnostics
702
+ /mcp - Open MCP server dashboard
629
703
  /exit - Exit application
630
704
  exit, quit - Exit application
631
705
 
@@ -819,6 +893,193 @@ Examples:
819
893
  clearInput();
820
894
  return true;
821
895
  }
896
+ // MCP Dashboard command
897
+ if (trimmedInput === "/mcp") {
898
+ if (onMcpDashboardToggle) {
899
+ onMcpDashboardToggle();
900
+ }
901
+ clearInput();
902
+ return true;
903
+ }
904
+ // Permissions management command
905
+ if (trimmedInput === "/permissions" || trimmedInput.startsWith("/permissions ")) {
906
+ const args = trimmedInput.replace("/permissions", "").trim();
907
+ const permManager = getPermissionManager();
908
+ const config = permManager.getConfig();
909
+ if (!args || args === "show" || args === "list") {
910
+ // Show current permissions configuration
911
+ const permissionLines = [
912
+ "**Permission Configuration**\n",
913
+ `Default Tier: **${config.permissions.default_tier}**\n`,
914
+ "\n**Tool Permissions:**\n",
915
+ ];
916
+ const tierEmoji = {
917
+ [PermissionTier.AutoApprove]: "✅",
918
+ [PermissionTier.Notify]: "🔔",
919
+ [PermissionTier.Confirm]: "⚠️",
920
+ [PermissionTier.Block]: "🚫",
921
+ };
922
+ for (const [tool, toolConfig] of Object.entries(config.permissions.tools)) {
923
+ const emoji = tierEmoji[toolConfig.tier] || "❓";
924
+ permissionLines.push(`- ${emoji} **${tool}**: ${toolConfig.tier}\n`);
925
+ }
926
+ permissionLines.push("\n**Session Settings:**\n");
927
+ permissionLines.push(`- Allow all bash: ${config.permissions.session_approvals.allow_all_bash ? "Yes" : "No"}\n`);
928
+ permissionLines.push(`- Trust current directory: ${config.permissions.session_approvals.trust_current_directory ? "Yes" : "No"}\n`);
929
+ permissionLines.push("\n*Tip: Use `/permissions set <tool> <tier>` to change permissions*\n");
930
+ permissionLines.push("*Tiers: auto_approve, notify, confirm, block*");
931
+ const permEntry = {
932
+ type: "assistant",
933
+ content: permissionLines.join(""),
934
+ timestamp: new Date(),
935
+ };
936
+ setChatHistory((prev) => [...prev, permEntry]);
937
+ }
938
+ else if (args.startsWith("set ")) {
939
+ // Set permission for a tool
940
+ const setArgs = args.replace("set ", "").trim().split(/\s+/);
941
+ if (setArgs.length >= 2) {
942
+ const [tool, tier] = setArgs;
943
+ const validTiers = ["auto_approve", "notify", "confirm", "block"];
944
+ if (!validTiers.includes(tier)) {
945
+ const errorEntry = {
946
+ type: "assistant",
947
+ content: `❌ Invalid tier: "${tier}"\n\nValid tiers are: ${validTiers.join(", ")}`,
948
+ timestamp: new Date(),
949
+ };
950
+ setChatHistory((prev) => [...prev, errorEntry]);
951
+ }
952
+ else {
953
+ // Update the permission
954
+ const newTools = { ...config.permissions.tools };
955
+ newTools[tool] = { tier: tier };
956
+ permManager.updateConfig({ tools: newTools }).then(() => {
957
+ const successEntry = {
958
+ type: "assistant",
959
+ content: `✅ Set **${tool}** permission to **${tier}**`,
960
+ timestamp: new Date(),
961
+ };
962
+ setChatHistory((prev) => [...prev, successEntry]);
963
+ }).catch((error) => {
964
+ const errorEntry = {
965
+ type: "assistant",
966
+ content: `❌ Failed to update permission: ${extractErrorMessage(error)}`,
967
+ timestamp: new Date(),
968
+ };
969
+ setChatHistory((prev) => [...prev, errorEntry]);
970
+ });
971
+ }
972
+ }
973
+ else {
974
+ const helpEntry = {
975
+ type: "assistant",
976
+ content: "Usage: `/permissions set <tool> <tier>`\n\nExample: `/permissions set bash confirm`",
977
+ timestamp: new Date(),
978
+ };
979
+ setChatHistory((prev) => [...prev, helpEntry]);
980
+ }
981
+ }
982
+ else if (args === "reset") {
983
+ // Reset to default permissions
984
+ permManager.clearSessionApprovals();
985
+ const resetEntry = {
986
+ type: "assistant",
987
+ content: "✅ Session permissions cleared. Tool permissions reset to defaults.",
988
+ timestamp: new Date(),
989
+ };
990
+ setChatHistory((prev) => [...prev, resetEntry]);
991
+ }
992
+ else {
993
+ // Show help
994
+ const helpEntry = {
995
+ type: "assistant",
996
+ content: "**Permission Commands:**\n\n" +
997
+ "- `/permissions` - Show current permissions\n" +
998
+ "- `/permissions set <tool> <tier>` - Set tool permission tier\n" +
999
+ "- `/permissions reset` - Reset session approvals\n\n" +
1000
+ "**Permission Tiers:**\n" +
1001
+ "- `auto_approve` - Automatically allow (safe operations)\n" +
1002
+ "- `notify` - Allow with notification\n" +
1003
+ "- `confirm` - Require user confirmation\n" +
1004
+ "- `block` - Always block",
1005
+ timestamp: new Date(),
1006
+ };
1007
+ setChatHistory((prev) => [...prev, helpEntry]);
1008
+ }
1009
+ clearInput();
1010
+ return true;
1011
+ }
1012
+ // MCP Prompt commands (format: /mcp__servername__promptname [args])
1013
+ if (trimmedInput.startsWith("/mcp__")) {
1014
+ const parts = trimmedInput.split(" ");
1015
+ const commandPart = parts[0];
1016
+ const argsPart = parts.slice(1).join(" ");
1017
+ const parsed = parsePromptCommand(commandPart);
1018
+ if (parsed) {
1019
+ const { serverName, promptName } = parsed;
1020
+ // Add user command to chat history
1021
+ const userEntry = {
1022
+ type: "user",
1023
+ content: trimmedInput,
1024
+ timestamp: new Date(),
1025
+ };
1026
+ setChatHistory((prev) => [...prev, userEntry]);
1027
+ // Execute the prompt asynchronously
1028
+ (async () => {
1029
+ try {
1030
+ const manager = getMCPManager();
1031
+ const v2 = manager.getV2Instance();
1032
+ const { createServerName } = await import("../../mcp/client-v2.js");
1033
+ const serverNameBranded = createServerName(serverName);
1034
+ if (!serverNameBranded) {
1035
+ throw new Error(`Invalid server name: ${serverName}`);
1036
+ }
1037
+ // Parse arguments if provided (format: key=value key2=value2)
1038
+ const promptArgs = {};
1039
+ if (argsPart) {
1040
+ const argMatches = argsPart.match(/(\w+)=("[^"]+"|[^\s]+)/g);
1041
+ if (argMatches) {
1042
+ for (const match of argMatches) {
1043
+ const [key, ...valueParts] = match.split("=");
1044
+ let value = valueParts.join("=");
1045
+ // Remove quotes if present
1046
+ if (value.startsWith('"') && value.endsWith('"')) {
1047
+ value = value.slice(1, -1);
1048
+ }
1049
+ promptArgs[key] = value;
1050
+ }
1051
+ }
1052
+ }
1053
+ const result = await v2.getPrompt(serverNameBranded, promptName, Object.keys(promptArgs).length > 0 ? promptArgs : undefined);
1054
+ if (!result.success) {
1055
+ throw result.error;
1056
+ }
1057
+ // Cast to GetPromptResult format for formatPromptResult
1058
+ const promptResult = result.value;
1059
+ const formattedContent = formatPromptResult(promptResult);
1060
+ // Add the prompt result as an assistant message
1061
+ const promptEntry = {
1062
+ type: "assistant",
1063
+ content: `**MCP Prompt: ${serverName}/${promptName}**\n\n${formattedContent}`,
1064
+ timestamp: new Date(),
1065
+ };
1066
+ setChatHistory((prev) => [...prev, promptEntry]);
1067
+ }
1068
+ catch (error) {
1069
+ const errorMessage = extractErrorMessage(error);
1070
+ // Use assistant type with error content since "error" is not a valid ChatEntry type
1071
+ const errorEntry = {
1072
+ type: "assistant",
1073
+ content: `❌ **Failed to execute MCP prompt:** ${errorMessage}`,
1074
+ timestamp: new Date(),
1075
+ };
1076
+ setChatHistory((prev) => [...prev, errorEntry]);
1077
+ }
1078
+ })();
1079
+ clearInput();
1080
+ return true;
1081
+ }
1082
+ }
822
1083
  if (trimmedInput === "/exit") {
823
1084
  process.exit(0);
824
1085
  return true;
@@ -1708,6 +1969,8 @@ Respond with ONLY the commit message, no additional text.`;
1708
1969
  showCommandSuggestions,
1709
1970
  selectedCommandIndex,
1710
1971
  commandSuggestions,
1972
+ resourceSuggestions,
1973
+ suggestionMode,
1711
1974
  availableModels,
1712
1975
  agent,
1713
1976
  autoEditEnabled,