@aiderdesk/aiderdesk 0.68.0 → 0.69.0

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 (62) hide show
  1. package/out/renderer/assets/{arc-Cju-fnUJ.js → arc--tfU0Ahe.js} +1 -1
  2. package/out/renderer/assets/{architectureDiagram-3BPJPVTR-hVK8LPcC.js → architectureDiagram-3BPJPVTR-D2uDC6Pw.js} +3 -3
  3. package/out/renderer/assets/{blockDiagram-GPEHLZMM-B04Ulwbh.js → blockDiagram-GPEHLZMM-BKopmU2x.js} +4 -4
  4. package/out/renderer/assets/{c4Diagram-AAUBKEIU-DSP8EUB6.js → c4Diagram-AAUBKEIU-CU9NH6bi.js} +2 -2
  5. package/out/renderer/assets/{channel-CM1Bn7Zy.js → channel-CBm2XBi8.js} +1 -1
  6. package/out/renderer/assets/{chunk-2J33WTMH-Bd_3pGvb.js → chunk-2J33WTMH-Yb07TKpF.js} +1 -1
  7. package/out/renderer/assets/{chunk-4BX2VUAB-Csrpxz3N.js → chunk-4BX2VUAB-B1KgZMg1.js} +1 -1
  8. package/out/renderer/assets/{chunk-55IACEB6-BB7RTMZZ.js → chunk-55IACEB6-BQ1g6F53.js} +1 -1
  9. package/out/renderer/assets/{chunk-727SXJPM-D6Y9H3aA.js → chunk-727SXJPM-nQzqwjgJ.js} +5 -5
  10. package/out/renderer/assets/{chunk-AQP2D5EJ-D5f9Zgag.js → chunk-AQP2D5EJ-8vVOUXUS.js} +3 -3
  11. package/out/renderer/assets/{chunk-FMBD7UC4-Bstyl64g.js → chunk-FMBD7UC4-dVquqYuV.js} +1 -1
  12. package/out/renderer/assets/{chunk-ND2GUHAM-CVaUW2HV.js → chunk-ND2GUHAM-Bu2VSodd.js} +1 -1
  13. package/out/renderer/assets/{chunk-QZHKN3VN-AMfy7Nbn.js → chunk-QZHKN3VN-Bl58FmbX.js} +1 -1
  14. package/out/renderer/assets/{classDiagram-4FO5ZUOK-CPmL8t3E.js → classDiagram-4FO5ZUOK-Dmfohc7-.js} +6 -6
  15. package/out/renderer/assets/{classDiagram-v2-Q7XG4LA2-CPmL8t3E.js → classDiagram-v2-Q7XG4LA2-Dmfohc7-.js} +6 -6
  16. package/out/renderer/assets/{cose-bilkent-S5V4N54A-px_eCLil.js → cose-bilkent-S5V4N54A-8bgOaac3.js} +1 -1
  17. package/out/renderer/assets/{dagre-BM42HDAG-c63xhfuM.js → dagre-BM42HDAG-BZZ0JLHX.js} +3 -3
  18. package/out/renderer/assets/{diagram-2AECGRRQ-DISIyKng.js → diagram-2AECGRRQ-9oArT1ZV.js} +3 -3
  19. package/out/renderer/assets/{diagram-5GNKFQAL-BSkGLBgD.js → diagram-5GNKFQAL-OMRICVmJ.js} +4 -4
  20. package/out/renderer/assets/{diagram-KO2AKTUF-xiSl-Lui.js → diagram-KO2AKTUF-pjPRIjL7.js} +3 -3
  21. package/out/renderer/assets/{diagram-LMA3HP47-lJ69G8gJ.js → diagram-LMA3HP47-cRVrdFbD.js} +3 -3
  22. package/out/renderer/assets/{diagram-OG6HWLK6-AoDpX3QT.js → diagram-OG6HWLK6-gJvA5dOA.js} +4 -4
  23. package/out/renderer/assets/{erDiagram-TEJ5UH35-O7UNRkDw.js → erDiagram-TEJ5UH35-BvHOuuhD.js} +4 -4
  24. package/out/renderer/assets/{flowDiagram-I6XJVG4X-Dd9XTtIY.js → flowDiagram-I6XJVG4X-D17Inzho.js} +6 -6
  25. package/out/renderer/assets/{ganttDiagram-6RSMTGT7-Bc7FUk0Y.js → ganttDiagram-6RSMTGT7-CIorTPIp.js} +1 -1
  26. package/out/renderer/assets/{gitGraphDiagram-PVQCEYII-DwEksAhs.js → gitGraphDiagram-PVQCEYII-R3v9UanE.js} +4 -4
  27. package/out/renderer/assets/{graph-BPvbzKl1.js → graph-DNFReSa8.js} +1 -1
  28. package/out/renderer/assets/{index-BmQl7j8n.js → index-COTYjomB.js} +27701 -132358
  29. package/out/renderer/assets/{index-z-BItG-u.css → index-D56aByCg.css} +32 -0
  30. package/out/renderer/assets/{infoDiagram-5YYISTIA-DvophXOV.js → infoDiagram-5YYISTIA-B4hJmlOG.js} +2 -2
  31. package/out/renderer/assets/{ishikawaDiagram-YF4QCWOH-CDT4EDOF.js → ishikawaDiagram-YF4QCWOH-DxohqJKP.js} +1 -1
  32. package/out/renderer/assets/{journeyDiagram-JHISSGLW-B5FCPOti.js → journeyDiagram-JHISSGLW-BxFBZYTB.js} +4 -4
  33. package/out/renderer/assets/{jsx-dev-runtime-DuBp-Rhq.js → jsx-dev-runtime-BsbxiHH-.js} +1 -1
  34. package/out/renderer/assets/{kanban-definition-UN3LZRKU-CM_TQl4S.js → kanban-definition-UN3LZRKU-B39Yt2kD.js} +2 -2
  35. package/out/renderer/assets/{layout-DdBKGefG.js → layout-yXTq0RED.js} +2 -2
  36. package/out/renderer/assets/{mindmap-definition-RKZ34NQL-BeGrpK-H.js → mindmap-definition-RKZ34NQL-BD2OIaSH.js} +3 -3
  37. package/out/renderer/assets/{pieDiagram-4H26LBE5-CnMhRK3I.js → pieDiagram-4H26LBE5-COAWItT4.js} +4 -4
  38. package/out/renderer/assets/pierre-dark-C-Drv5KU.js +4 -0
  39. package/out/renderer/assets/pierre-dark-soft-Cd74uQaL.js +4 -0
  40. package/out/renderer/assets/pierre-light-CfQnmt6J.js +4 -0
  41. package/out/renderer/assets/pierre-light-soft-DMVdU74b.js +4 -0
  42. package/out/renderer/assets/{quadrantDiagram-W4KKPZXB-O_Q_JI2s.js → quadrantDiagram-W4KKPZXB-BLY0H2F6.js} +1 -1
  43. package/out/renderer/assets/{requirementDiagram-4Y6WPE33-vf9wLf04.js → requirementDiagram-4Y6WPE33-DEJf5mz_.js} +3 -3
  44. package/out/renderer/assets/{sankeyDiagram-5OEKKPKP-CE4n-uB4.js → sankeyDiagram-5OEKKPKP-D3BkTe8z.js} +1 -1
  45. package/out/renderer/assets/{sequenceDiagram-3UESZ5HK-DebEZhql.js → sequenceDiagram-3UESZ5HK-DUU_lUpD.js} +3 -3
  46. package/out/renderer/assets/{stateDiagram-AJRCARHV-DCsVosBt.js → stateDiagram-AJRCARHV-DnH4p93i.js} +6 -6
  47. package/out/renderer/assets/{stateDiagram-v2-BHNVJYJU-8oguEvt8.js → stateDiagram-v2-BHNVJYJU-CRXRiP95.js} +4 -4
  48. package/out/renderer/assets/{timeline-definition-PNZ67QCA-CSfXVnNf.js → timeline-definition-PNZ67QCA-Uv34KQP0.js} +2 -2
  49. package/out/renderer/assets/{vennDiagram-CIIHVFJN-DxOEVeC4.js → vennDiagram-CIIHVFJN-JyUQxmn1.js} +1 -1
  50. package/out/renderer/assets/{wardley-L42UT6IY-Cz6T4MDn.js → wardley-L42UT6IY-4X2JoJpL.js} +1 -1
  51. package/out/renderer/assets/{wardleyDiagram-YWT4CUSO-B4sA3LOJ.js → wardleyDiagram-YWT4CUSO-B9pIhVmY.js} +3 -3
  52. package/out/renderer/assets/{worker-CfJUABHG.js → worker-BdXuKH1Q.js} +274 -171
  53. package/out/renderer/assets/{xychartDiagram-2RQKCTM6-DbJjruqS.js → xychartDiagram-2RQKCTM6-PJZgkel8.js} +1 -1
  54. package/out/renderer/index.html +2 -2
  55. package/out/resources/skills/extension-creator/references/ui-components.md +11 -0
  56. package/out/runner.js +323 -422
  57. package/package.json +19 -32
  58. package/patches/{ai+5.0.179.patch → ai+5.0.195.patch} +9 -9
  59. package/out/renderer/assets/pierre-dark-DADY5eR0.js +0 -4
  60. package/out/renderer/assets/pierre-light-DUjirxKp.js +0 -4
  61. package/patches/@ai-sdk+deepseek+1.0.37.patch +0 -150
  62. package/patches/ai-sdk-provider-claude-code+2.3.0.patch +0 -1024
package/out/runner.js CHANGED
@@ -73,8 +73,6 @@ const clientBedrock = require("@aws-sdk/client-bedrock");
73
73
  const credentialProviders = require("@aws-sdk/credential-providers");
74
74
  const amazonBedrock = require("@ai-sdk/amazon-bedrock");
75
75
  const cerebras = require("@ai-sdk/cerebras");
76
- const aiSdkProviderClaudeCode = require("ai-sdk-provider-claude-code");
77
- const jsonSchemaToZod = require("@dmitryrechkin/json-schema-to-zod");
78
76
  const deepseek = require("@ai-sdk/deepseek");
79
77
  const google = require("@ai-sdk/google");
80
78
  const genai = require("@google/genai");
@@ -868,7 +866,6 @@ const CLOUDFLARED_BINARY_PATH = path.join(
868
866
  "bin",
869
867
  process.platform === "win32" ? "cloudflared.exe" : "cloudflared"
870
868
  );
871
- const CLAUDE_CODE_EXECUTABLE_PATH = path.join(RESOURCES_DIR, "app.asar.unpacked", "node_modules", "@anthropic-ai", "claude-agent-sdk", "cli.js");
872
869
  const eventTransport = new EventTransport();
873
870
  const logger = winston.createLogger({
874
871
  level: process.env.LOG_LEVEL || "info",
@@ -1497,11 +1494,9 @@ const AVAILABLE_PROVIDERS = [
1497
1494
  "alibaba-plan",
1498
1495
  "anthropic",
1499
1496
  "anthropic-compatible",
1500
- "auggie",
1501
1497
  "azure",
1502
1498
  "bedrock",
1503
1499
  "cerebras",
1504
- "claude-agent-sdk",
1505
1500
  "deepseek",
1506
1501
  "gemini",
1507
1502
  "gemini-cli",
@@ -1532,7 +1527,6 @@ const isOpenAiProvider = (provider) => provider.name === "openai";
1532
1527
  const isAzureProvider = (provider) => provider.name === "azure";
1533
1528
  const isAnthropicProvider = (provider) => provider.name === "anthropic";
1534
1529
  const isAnthropicCompatibleProvider = (provider) => provider.name === "anthropic-compatible";
1535
- const isAuggieProvider = (provider) => provider.name === "auggie";
1536
1530
  const isKimiPlanProvider = (provider) => provider.name === "kimi-plan";
1537
1531
  const isAlibabaPlanProvider = (provider) => provider.name === "alibaba-plan";
1538
1532
  var GeminiVoiceModel = /* @__PURE__ */ ((GeminiVoiceModel2) => {
@@ -1545,7 +1539,6 @@ const isLmStudioProvider = (provider) => provider.name === "lmstudio";
1545
1539
  const isDeepseekProvider = (provider) => provider.name === "deepseek";
1546
1540
  const isGroqProvider = (provider) => provider.name === "groq";
1547
1541
  const isCerebrasProvider = (provider) => provider.name === "cerebras";
1548
- const isClaudeAgentSdkProvider = (provider) => provider.name === "claude-agent-sdk";
1549
1542
  const isGeminiCliProvider = (provider) => provider.name === "gemini-cli";
1550
1543
  const isBedrockProvider = (provider) => provider.name === "bedrock";
1551
1544
  const isOpenAiCompatibleProvider = (provider) => provider.name === "openai-compatible";
@@ -1563,10 +1556,8 @@ const isSyntheticProvider = (provider) => provider.name === "synthetic";
1563
1556
  const DEFAULT_PROVIDER_MODELS = {
1564
1557
  "alibaba-plan": "qwen3-coder-plus",
1565
1558
  anthropic: "claude-sonnet-4-6",
1566
- auggie: "gpt-5-4",
1567
1559
  bedrock: "global.anthropic.claude-sonnet-4-6",
1568
1560
  cerebras: "qwen-3-235b-a22b-instruct-2507",
1569
- "claude-agent-sdk": "sonnet",
1570
1561
  deepseek: "deepseek-v4-pro",
1571
1562
  gemini: "gemini-pro-latest",
1572
1563
  "gemini-cli": "gemini-2.5-pro",
@@ -1639,6 +1630,7 @@ const DEFAULT_AGENT_PROFILE = {
1639
1630
  useMemoryTools: true,
1640
1631
  useSkillsTools: true,
1641
1632
  useExtensionTools: true,
1633
+ disabledExtensionTools: [],
1642
1634
  customInstructions: "",
1643
1635
  enabledServers: [],
1644
1636
  subagent: {
@@ -1774,6 +1766,7 @@ const COMPACT_CONVERSATION_AGENT_PROFILE = {
1774
1766
  }
1775
1767
  };
1776
1768
  ({
1769
+ ...DEFAULT_AGENT_PROFILE,
1777
1770
  toolApprovals: {
1778
1771
  ...DEFAULT_AGENT_PROFILE.toolApprovals,
1779
1772
  [`${POWER_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${POWER_TOOL_FILE_EDIT}`]: ToolApprovalState.Never,
@@ -1823,13 +1816,6 @@ const getDefaultProviderParams = (providerName) => {
1823
1816
  baseUrl: ""
1824
1817
  };
1825
1818
  break;
1826
- case "auggie":
1827
- provider = {
1828
- name: "auggie",
1829
- apiKey: "",
1830
- apiUrl: ""
1831
- };
1832
- break;
1833
1819
  case "gemini":
1834
1820
  provider = {
1835
1821
  name: "gemini",
@@ -2262,7 +2248,6 @@ const readApiKeyFromConfFile = (filePath, envVarName) => {
2262
2248
  GOOGLE_API_KEY: ["gemini", "google"],
2263
2249
  OPENAI_API_KEY: ["openai"],
2264
2250
  ANTHROPIC_API_KEY: ["anthropic", "anthropic-compatible"],
2265
- AUGMENT_API_TOKEN: ["auggie"],
2266
2251
  GROQ_API_KEY: ["groq"],
2267
2252
  ALIBABA_PLAN_API_KEY: ["alibaba-plan"],
2268
2253
  KIMI_PLAN_API_KEY: ["kimi-plan"],
@@ -3248,6 +3233,20 @@ const scrapeWeb = async (url, timeout = 6e4, abortSignal, format = "markdown") =
3248
3233
  const scraper = new WebScraper();
3249
3234
  return await scraper.scrape(url, timeout, abortSignal, format);
3250
3235
  };
3236
+ const coerceBoolean = zod.z.preprocess((val) => {
3237
+ if (typeof val === "boolean") {
3238
+ return val;
3239
+ }
3240
+ if (typeof val === "string") {
3241
+ if (val.toLowerCase() === "true") {
3242
+ return true;
3243
+ }
3244
+ if (val.toLowerCase() === "false") {
3245
+ return false;
3246
+ }
3247
+ }
3248
+ return val;
3249
+ }, zod.z.boolean());
3251
3250
  const THINKING_RESPONSE_STAR_TAG = "---\n► **THINKING**\n";
3252
3251
  const ANSWER_RESPONSE_START_TAG = "---\n► **ANSWER**\n";
3253
3252
  const extractPromptContextFromToolResult = (toolResult) => {
@@ -3484,8 +3483,8 @@ Include enough lines in each to uniquely match each set of lines that need to ch
3484
3483
  Do not use escape characters \\ in the string like \\n or \\" and others. Do not start the search term with a \\ character.`
3485
3484
  ),
3486
3485
  replacementText: zod.z.string().describe('The string to replace the searchTerm with. Do not use escape characters \\ in the string like \\n or \\" and others'),
3487
- isRegex: zod.z.boolean().optional().default(false).describe("Whether the searchTerm should be treated as a regular expression. Use regex only when it is really needed. Default: false."),
3488
- replaceAll: zod.z.boolean().optional().default(false).describe("Whether to replace all occurrences or just the first one. Default: false.")
3486
+ isRegex: coerceBoolean.optional().default(false).describe("Whether the searchTerm should be treated as a regular expression. Use regex only when it is really needed. Default: false."),
3487
+ replaceAll: coerceBoolean.optional().default(false).describe("Whether to replace all occurrences or just the first one. Default: false.")
3489
3488
  }),
3490
3489
  execute: async (input, { toolCallId }) => {
3491
3490
  const { filePath, searchTerm, replacementText, isRegex, replaceAll } = input;
@@ -3575,9 +3574,9 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
3575
3574
  description: POWER_TOOL_DESCRIPTIONS[POWER_TOOL_FILE_READ],
3576
3575
  inputSchema: zod.z.object({
3577
3576
  filePath: zod.z.string().describe("The path to the file to be read (relative to the <WorkingDirectory> or absolute if outside of the directory)."),
3578
- withLines: zod.z.boolean().optional().default(false).describe('Whether to return the file content with line numbers in format "lineNumber|content". Default: false.'),
3579
- lineOffset: zod.z.number().int().min(0).optional().default(0).describe("The starting line number (0-based) to begin reading from. Default: 0."),
3580
- lineLimit: zod.z.number().int().min(1).optional().default(1e3).describe("The maximum number of lines to read. Default: 1000.")
3577
+ withLines: coerceBoolean.optional().default(false).describe('Whether to return the file content with line numbers in format "lineNumber|content". Default: false.'),
3578
+ lineOffset: zod.z.coerce.number().int().min(0).optional().default(0).describe("The starting line number (0-based) to begin reading from. Default: 0."),
3579
+ lineLimit: zod.z.coerce.number().int().min(1).optional().default(1e3).describe("The maximum number of lines to read. Default: 1000.")
3581
3580
  }),
3582
3581
  execute: async (input, { toolCallId }) => {
3583
3582
  const { filePath, withLines, lineOffset, lineLimit } = input;
@@ -3741,8 +3740,8 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
3741
3740
  filePattern: zod.z.string().describe("A glob pattern specifying the files to search within (e.g., src/**/*.tsx, *.py)."),
3742
3741
  searchTerm: zod.z.string().describe("The regular expression to search for within the files."),
3743
3742
  contextLines: zod.z.number().int().min(0).optional().default(0).describe("The number of lines of context to show before and after each matching line. Default: 0."),
3744
- caseSensitive: zod.z.boolean().optional().default(false).describe("Whether the search should be case sensitive. Default: false."),
3745
- maxResults: zod.z.number().int().min(1).optional().default(50).describe("Maximum number of results to return. Default: 50.")
3743
+ caseSensitive: coerceBoolean.optional().default(false).describe("Whether the search should be case sensitive. Default: false."),
3744
+ maxResults: zod.z.coerce.number().int().min(1).optional().default(50).describe("Maximum number of results to return. Default: 50.")
3746
3745
  }),
3747
3746
  execute: async (input, { toolCallId }) => {
3748
3747
  const { filePattern, searchTerm, contextLines, caseSensitive, maxResults } = input;
@@ -3909,7 +3908,7 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
3909
3908
  inputSchema: zod.z.object({
3910
3909
  command: zod.z.string().min(1).describe("The shell command to execute (e.g., ls -la, npm install)."),
3911
3910
  cwd: zod.z.string().optional().describe("The working directory for the command (relative to <WorkingDirectory>). Default: <WorkingDirectory>."),
3912
- timeout: zod.z.number().int().min(0).optional().default(12e4).describe("Timeout for the command execution in milliseconds. Default: 120000 ms.")
3911
+ timeout: zod.z.coerce.number().int().min(0).optional().default(12e4).describe("Timeout for the command execution in milliseconds. Default: 120000 ms.")
3913
3912
  }),
3914
3913
  execute: async (input, { toolCallId }) => {
3915
3914
  const { command, cwd, timeout } = input;
@@ -4094,7 +4093,7 @@ Timeout: ${timeout}ms`;
4094
4093
  description: POWER_TOOL_DESCRIPTIONS[POWER_TOOL_FETCH],
4095
4094
  inputSchema: zod.z.object({
4096
4095
  url: zod.z.string().describe("The URL to fetch."),
4097
- timeout: zod.z.number().int().min(0).optional().default(6e4).describe("Timeout for the fetch operation in milliseconds. Default: 60000 ms."),
4096
+ timeout: zod.z.coerce.number().int().min(0).optional().default(6e4).describe("Timeout for the fetch operation in milliseconds. Default: 60000 ms."),
4098
4097
  format: zod.z.enum(["markdown", "html", "raw"]).optional().default("markdown").describe(
4099
4098
  'Format of the response: "markdown" (default, converts HTML to markdown), "html" (returns raw HTML), "raw" (fetches raw content via HTTP, ideal for API responses or raw files).'
4100
4099
  )
@@ -4144,10 +4143,10 @@ Format: ${format}`;
4144
4143
  inputSchema: zod.z.object({
4145
4144
  query: zod.z.string().describe("Search query with Elasticsearch syntax. Use + for important terms."),
4146
4145
  path: zod.z.string().optional().default(task.getTaskDir()).describe('Absolute path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
4147
- allowTests: zod.z.boolean().optional().default(false).describe("Allow test files in search results"),
4148
- exact: zod.z.boolean().optional().default(false).describe("Perform exact search without tokenization (case-insensitive)"),
4149
- maxResults: zod.z.number().optional().describe("Maximum number of results to return"),
4150
- maxTokens: zod.z.number().optional().default(5e3).describe("Maximum number of tokens to return"),
4146
+ allowTests: coerceBoolean.optional().default(false).describe("Allow test files in search results"),
4147
+ exact: coerceBoolean.optional().default(false).describe("Perform exact search without tokenization (case-insensitive)"),
4148
+ maxResults: zod.z.coerce.number().optional().describe("Maximum number of results to return"),
4149
+ maxTokens: zod.z.coerce.number().optional().default(5e3).describe("Maximum number of tokens to return"),
4151
4150
  language: zod.z.string().optional().describe("Limit search to files of a specific programming language")
4152
4151
  }),
4153
4152
  execute: async (input, { toolCallId }) => {
@@ -4259,7 +4258,7 @@ const createTodoToolset = (task, profile, promptContext) => {
4259
4258
  items: zod.z.array(
4260
4259
  zod.z.object({
4261
4260
  name: zod.z.string().describe("The name of the todo item."),
4262
- completed: zod.z.boolean().optional().default(false).describe("Whether the todo item is completed.")
4261
+ completed: coerceBoolean.optional().default(false).describe("Whether the todo item is completed.")
4263
4262
  })
4264
4263
  ).describe("An array of todo items."),
4265
4264
  initialUserPrompt: zod.z.string().describe("The original user prompt that initiated the task.")
@@ -4313,7 +4312,7 @@ Items: ${JSON.stringify(items)}`;
4313
4312
  description: TODO_TOOL_DESCRIPTIONS[TODO_TOOL_UPDATE_ITEM_COMPLETION],
4314
4313
  inputSchema: zod.z.object({
4315
4314
  name: zod.z.string().describe("The name of the todo item to update."),
4316
- completed: zod.z.boolean().describe("The new completion status for the todo item.")
4315
+ completed: coerceBoolean.describe("The new completion status for the todo item.")
4317
4316
  }),
4318
4317
  execute: async (input, { toolCallId }) => {
4319
4318
  const { name, completed } = input;
@@ -4378,8 +4377,8 @@ const createTasksToolset = (settings, task, profile, promptContext) => {
4378
4377
  const listTasksTool = ai.tool({
4379
4378
  description: TASKS_TOOL_DESCRIPTIONS[TASKS_TOOL_LIST_TASKS],
4380
4379
  inputSchema: zod.z.object({
4381
- offset: zod.z.number().optional().describe("The number of tasks to skip (for pagination)"),
4382
- limit: zod.z.number().optional().describe("The maximum number of tasks to return"),
4380
+ offset: zod.z.coerce.number().optional().describe("The number of tasks to skip (for pagination)"),
4381
+ limit: zod.z.coerce.number().optional().describe("The maximum number of tasks to return"),
4383
4382
  state: zod.z.string().optional().describe("Filter tasks by state (e.g., TODO, IN_PROGRESS, DONE)")
4384
4383
  }),
4385
4384
  execute: async (input, { toolCallId }) => {
@@ -4543,7 +4542,7 @@ const createTasksToolset = (settings, task, profile, promptContext) => {
4543
4542
  ),
4544
4543
  worktree: zod.z.boolean().optional().default(false).describe("If true, the task will be created in worktree mode. Use only when explicitly requested by the user."),
4545
4544
  executeAndWait: zod.z.boolean().optional().default(false).describe("If true, the task will be created and executed immediately and the tool will wait for it to complete before returning."),
4546
- executeInBackground: zod.z.boolean().optional().default(false).describe("If true, the task will be created and executed in the background.")
4545
+ executeInBackground: coerceBoolean.optional().default(false).describe("If true, the task will be created and executed in the background.")
4547
4546
  }),
4548
4547
  execute: async (input, { toolCallId }) => {
4549
4548
  const { prompt, name, agentProfileId, modelId, mode = "agent", asSubtask = false, autonomyMode, worktree, executeAndWait, executeInBackground } = input;
@@ -4707,7 +4706,7 @@ Prompt: ${prompt}${executeAndWait ? "\nWait for completion: true" : "\nRun in ba
4707
4706
  inputSchema: zod.z.object({
4708
4707
  taskId: zod.z.string().describe("The ID of the task to search within"),
4709
4708
  query: zod.z.string().describe("Search query with Elasticsearch syntax. Use + for important terms."),
4710
- maxTokens: zod.z.number().optional().default(1e4).describe("Maximum number of tokens to return in the search results. Default: 10000")
4709
+ maxTokens: zod.z.coerce.number().optional().default(1e4).describe("Maximum number of tokens to return in the search results. Default: 10000")
4711
4710
  }),
4712
4711
  execute: async (input, { toolCallId }) => {
4713
4712
  const { taskId, query, maxTokens } = input;
@@ -4784,7 +4783,7 @@ const createSearchParentTaskTool = (task, promptContext) => {
4784
4783
  description: TASKS_TOOL_DESCRIPTIONS[TASKS_TOOL_SEARCH_PARENT_TASK],
4785
4784
  inputSchema: zod.z.object({
4786
4785
  query: zod.z.string().describe("Search query with Elasticsearch syntax. Use + for important terms."),
4787
- maxTokens: zod.z.number().optional().default(1e4).describe("Maximum number of tokens to return in the search results. Default: 10000")
4786
+ maxTokens: zod.z.coerce.number().optional().default(1e4).describe("Maximum number of tokens to return in the search results. Default: 10000")
4788
4787
  }),
4789
4788
  execute: async ({ query, maxTokens }, { toolCallId }) => {
4790
4789
  const parentTaskId = task.task.parentId;
@@ -4874,7 +4873,7 @@ const createAiderToolset = (task, profile, promptContext) => {
4874
4873
  description: AIDER_TOOL_DESCRIPTIONS[AIDER_TOOL_ADD_CONTEXT_FILES],
4875
4874
  inputSchema: zod.z.object({
4876
4875
  paths: zod.z.array(zod.z.string()).describe('One or more file paths to add to context. Relative to task directory (e.g. "src/file.ts") or absolute (e.g. "/tmp/log.txt" for read-only).'),
4877
- readOnly: zod.z.boolean().optional().describe("Whether the file(s) are read-only. Applies to all paths if true.")
4876
+ readOnly: coerceBoolean.optional().describe("Whether the file(s) are read-only. Applies to all paths if true.")
4878
4877
  }),
4879
4878
  execute: async (input, { toolCallId }) => {
4880
4879
  const { paths, readOnly = false } = input;
@@ -5103,7 +5102,7 @@ const createMemoryToolset = (task, profile, memoryManager, promptContext) => {
5103
5102
  description: MEMORY_TOOL_DESCRIPTIONS[MEMORY_TOOL_RETRIEVE],
5104
5103
  inputSchema: zod.z.object({
5105
5104
  query: zod.z.string().describe("The search query to find relevant memories"),
5106
- limit: zod.z.number().optional().default(3).describe("Maximum number of memories to retrieve (default: 3)")
5105
+ limit: zod.z.coerce.number().optional().default(3).describe("Maximum number of memories to retrieve (default: 3)")
5107
5106
  }),
5108
5107
  execute: async (input, { toolCallId }) => {
5109
5108
  const { query, limit } = input;
@@ -5170,7 +5169,7 @@ Timestamp: ${new Date(memory.timestamp).toLocaleString()}`;
5170
5169
  description: MEMORY_TOOL_DESCRIPTIONS[MEMORY_TOOL_LIST],
5171
5170
  inputSchema: zod.z.object({
5172
5171
  type: zod.z.enum(["task", "user-preference", "code-pattern"]).optional().describe("Filter by memory type"),
5173
- limit: zod.z.number().optional().default(20).describe("Maximum number of memories to list (default: 20)")
5172
+ limit: zod.z.coerce.number().optional().default(20).describe("Maximum number of memories to list (default: 20)")
5174
5173
  }),
5175
5174
  execute: async (input, { toolCallId }) => {
5176
5175
  const { type, limit } = input;
@@ -6594,14 +6593,16 @@ ${file.content}</content-with-line-numbers>
6594
6593
  logger.debug("Adding file list for non-image files", {
6595
6594
  files: nonImageFiles.map((f) => f.path)
6596
6595
  });
6596
+ const hasReadOnlyFiles = nonImageFiles.some((file) => file.readOnly);
6597
6597
  const fileList = nonImageFiles.map((file) => {
6598
- return `- ${file.path}`;
6598
+ return `- ${file.path}${file.readOnly ? " (read-only)" : ""}`;
6599
6599
  }).join("\n");
6600
+ const readOnlyNote = hasReadOnlyFiles ? "\n\nNote: Files marked as read-only must NOT be modified. Use them only for understanding context." : "";
6600
6601
  messages.push({
6601
6602
  role: "user",
6602
6603
  content: `The following files are currently in the working context:
6603
6604
 
6604
- ${fileList}`
6605
+ ${fileList}${readOnlyNote}`
6605
6606
  });
6606
6607
  messages.push({
6607
6608
  role: "assistant",
@@ -6699,7 +6700,7 @@ ${fileList}`
6699
6700
  const providerTools = await this.modelManager.getProviderTools(provider, model);
6700
6701
  Object.assign(toolSet, providerTools);
6701
6702
  if (this.extensionManager.isInitialized() && profile.useExtensionTools !== false) {
6702
- const extensionTools = this.extensionManager.createExtensionToolset(task, mode, profile, toolSet, abortSignal);
6703
+ const extensionTools = this.extensionManager.createExtensionToolset(task, mode, profile, toolSet, approvalManager, abortSignal);
6703
6704
  Object.assign(toolSet, extensionTools);
6704
6705
  }
6705
6706
  return this.wrapToolsWithHooks(task, profile, toolSet, abortSignal, promptContext);
@@ -7851,7 +7852,7 @@ ${fileList}`
7851
7852
  const processToolResult = (toolResult) => {
7852
7853
  const [serverName, toolName] = extractServerNameToolName(toolResult.toolName);
7853
7854
  const toolPromptContext = extractPromptContextFromToolResult(toolResult.output) ?? promptContext;
7854
- task.addToolMessage(toolResult.toolCallId, serverName, toolName, toolResult.input, JSON.stringify(toolResult.output), void 0, toolPromptContext);
7855
+ task.addToolMessage(toolResult.toolCallId, serverName, toolName, toolResult.input, JSON.stringify(toolResult.output), usageReport, toolPromptContext);
7855
7856
  };
7856
7857
  for (let i = 0; i < content.length; i++) {
7857
7858
  let part = content[i];
@@ -8337,6 +8338,7 @@ class AgentProfileManager {
8337
8338
  maxIterations: loadedProfile.maxIterations ?? DEFAULT_AGENT_PROFILE.maxIterations,
8338
8339
  minTimeBetweenToolCalls: loadedProfile.minTimeBetweenToolCalls ?? DEFAULT_AGENT_PROFILE.minTimeBetweenToolCalls,
8339
8340
  enabledServers: loadedProfile.enabledServers ?? [],
8341
+ disabledExtensionTools: loadedProfile.disabledExtensionTools ?? [],
8340
8342
  customInstructions: loadedProfile.customInstructions ?? "",
8341
8343
  toolApprovals: {
8342
8344
  ...loadedProfile.toolApprovals
@@ -9306,11 +9308,22 @@ const MergeWorktreeToMainSchema = zod.z.object({
9306
9308
  targetBranch: zod.z.string().optional(),
9307
9309
  commitMessage: zod.z.string().optional()
9308
9310
  });
9309
- const MergeAndSwitchToLocalSchema = zod.z.object({
9311
+ const SwitchToLocalWorkingModeSchema = zod.z.object({
9310
9312
  projectDir: zod.z.string().min(1, "Project directory is required"),
9311
9313
  taskId: zod.z.string().min(1, "Task id is required"),
9314
+ mergeBeforeSwitch: zod.z.boolean().optional(),
9312
9315
  targetBranch: zod.z.string().optional()
9313
9316
  });
9317
+ const SwitchToWorktreeWorkingModeSchema = zod.z.object({
9318
+ projectDir: zod.z.string().min(1, "Project directory is required"),
9319
+ taskId: zod.z.string().min(1, "Task id is required"),
9320
+ carryOverUncommittedChanges: zod.z.boolean().optional(),
9321
+ dropSourceChanges: zod.z.boolean().optional()
9322
+ });
9323
+ const LocalUncommittedFilesSchema = zod.z.object({
9324
+ projectDir: zod.z.string().min(1, "Project directory is required"),
9325
+ taskId: zod.z.string().min(1, "Task id is required")
9326
+ });
9314
9327
  const ApplyUncommittedChangesSchema = zod.z.object({
9315
9328
  projectDir: zod.z.string().min(1, "Project directory is required"),
9316
9329
  taskId: zod.z.string().min(1, "Task id is required"),
@@ -9761,15 +9774,39 @@ class ProjectApi extends BaseApi {
9761
9774
  })
9762
9775
  );
9763
9776
  router.post(
9764
- "/project/worktree/merge-and-switch-to-local",
9777
+ "/project/switch-to-local-working-mode",
9765
9778
  this.handleRequest(async (req, res) => {
9766
- const parsed = this.validateRequest(MergeAndSwitchToLocalSchema, req.body, res);
9779
+ const parsed = this.validateRequest(SwitchToLocalWorkingModeSchema, req.body, res);
9767
9780
  if (!parsed) {
9768
9781
  return;
9769
9782
  }
9770
- const { projectDir, taskId, targetBranch } = parsed;
9771
- await this.eventsHandler.mergeAndSwitchToLocal(projectDir, taskId, targetBranch);
9772
- res.status(200).json({ message: "Worktree merged and switched to local" });
9783
+ const { projectDir, taskId, mergeBeforeSwitch, targetBranch } = parsed;
9784
+ await this.eventsHandler.switchToLocalWorkingMode(projectDir, taskId, { mergeBeforeSwitch, targetBranch });
9785
+ res.status(200).json({ message: "Switched to local working mode" });
9786
+ })
9787
+ );
9788
+ router.post(
9789
+ "/project/switch-to-worktree-working-mode",
9790
+ this.handleRequest(async (req, res) => {
9791
+ const parsed = this.validateRequest(SwitchToWorktreeWorkingModeSchema, req.body, res);
9792
+ if (!parsed) {
9793
+ return;
9794
+ }
9795
+ const { projectDir, taskId, carryOverUncommittedChanges, dropSourceChanges } = parsed;
9796
+ await this.eventsHandler.switchToWorktreeWorkingMode(projectDir, taskId, { carryOverUncommittedChanges, dropSourceChanges });
9797
+ res.status(200).json({ message: "Switched to worktree working mode" });
9798
+ })
9799
+ );
9800
+ router.get(
9801
+ "/project/local-uncommitted-files",
9802
+ this.handleRequest(async (req, res) => {
9803
+ const parsed = this.validateRequest(LocalUncommittedFilesSchema, req.query, res);
9804
+ if (!parsed) {
9805
+ return;
9806
+ }
9807
+ const { projectDir, taskId } = parsed;
9808
+ const result = await this.eventsHandler.getLocalUncommittedFiles(projectDir, taskId);
9809
+ res.status(200).json(result);
9773
9810
  })
9774
9811
  );
9775
9812
  router.post(
@@ -10713,6 +10750,9 @@ class TerminalApi extends BaseApi {
10713
10750
  const GetInstalledExtensionsSchema = zod.z.object({
10714
10751
  projectDir: zod.z.string().optional()
10715
10752
  });
10753
+ const GetExtensionToolsInfoSchema = zod.z.object({
10754
+ projectDir: zod.z.string().optional()
10755
+ });
10716
10756
  const GetAvailableExtensionsSchema = zod.z.object({
10717
10757
  repositories: zod.z.string().optional(),
10718
10758
  // Comma-separated URLs
@@ -10751,6 +10791,18 @@ class ExtensionsApi extends BaseApi {
10751
10791
  res.status(200).json(extensions);
10752
10792
  })
10753
10793
  );
10794
+ router.get(
10795
+ "/extensions/tools-info",
10796
+ this.handleRequest(async (req, res) => {
10797
+ const parsed = this.validateRequest(GetExtensionToolsInfoSchema, req.query, res);
10798
+ if (!parsed) {
10799
+ return;
10800
+ }
10801
+ const { projectDir } = parsed;
10802
+ const toolsInfo = this.eventsHandler.getExtensionToolsInfo(projectDir);
10803
+ res.status(200).json(toolsInfo);
10804
+ })
10805
+ );
10754
10806
  router.get(
10755
10807
  "/extensions/available",
10756
10808
  this.handleRequest(async (req, res) => {
@@ -15646,7 +15698,7 @@ const smartCompactMessages = async (messages, protectedMessageCount = 10, compac
15646
15698
  result = compactFileReads(result, protectedMessageCount, compactionLevel);
15647
15699
  result = removeObsoleteSearches(result, protectedMessageCount, compactionLevel);
15648
15700
  result = compactSemanticSearches(result, protectedMessageCount, compactionLevel);
15649
- result = deduplicateBash(result, protectedMessageCount, compactionLevel);
15701
+ result = compactBashOutputs(result, protectedMessageCount, compactionLevel);
15650
15702
  result = redactFetchOutputs(result, protectedMessageCount);
15651
15703
  result = await truncateNonPowerToolResults(result, protectedMessageCount, compactionLevel);
15652
15704
  result = mergeConsecutiveAssistantMessages(result);
@@ -16012,8 +16064,9 @@ const compactSemanticSearches = (messages, protectedMessageCount = 10, compactio
16012
16064
  }
16013
16065
  return messages;
16014
16066
  };
16015
- const deduplicateBash = (messages, protectedMessageCount = 10, compactionLevel = 1) => {
16067
+ const compactBashOutputs = (messages, protectedMessageCount = 10, compactionLevel = 1) => {
16016
16068
  const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
16069
+ logger.info(`Compacting bash outputs at level ${compactionLevel}`);
16017
16070
  const bashCommands = /* @__PURE__ */ new Map();
16018
16071
  for (let i = 0; i < protectedStart; i++) {
16019
16072
  const msg = messages[i];
@@ -16079,6 +16132,23 @@ const deduplicateBash = (messages, protectedMessageCount = 10, compactionLevel =
16079
16132
  };
16080
16133
  continue;
16081
16134
  }
16135
+ if (part.output.type === "json" || part.output.type === "error-json") {
16136
+ const value = part.output.value;
16137
+ if (typeof value === "object" && value !== null) {
16138
+ const obj = value;
16139
+ const stdout = typeof obj.stdout === "string" ? obj.stdout : "";
16140
+ const stderr = typeof obj.stderr === "string" ? obj.stderr : "";
16141
+ const redactionMessage = "<output redacted due to compaction, run again if output is needed>";
16142
+ const redactionThreshold = compactionLevel >= 2 ? 0 : 30;
16143
+ if (stdout.length > redactionThreshold) {
16144
+ obj.stdout = redactionMessage;
16145
+ }
16146
+ if (stderr.length > redactionThreshold) {
16147
+ obj.stderr = redactionMessage;
16148
+ }
16149
+ }
16150
+ continue;
16151
+ }
16082
16152
  if (part.output.type !== "text") {
16083
16153
  continue;
16084
16154
  }
@@ -16819,6 +16889,7 @@ class Task {
16819
16889
  provider: this.task.provider || profile.provider,
16820
16890
  model: this.task.model || profile.model
16821
16891
  });
16892
+ this.smartCompactionLevel = 1;
16822
16893
  const agentMessages = await this.agent.runAgent(
16823
16894
  this,
16824
16895
  profile,
@@ -17917,7 +17988,6 @@ ${contentText}</agent-response>`;
17917
17988
  lastAgentProviderMetadata: null
17918
17989
  });
17919
17990
  }
17920
- this.smartCompactionLevel = CompactionLevel.One;
17921
17991
  this.contextManager.clearMessages(true, createSnapshot);
17922
17992
  await this.runCommand("clear", addToHistory);
17923
17993
  this.eventManager.sendClearTask(this.project.baseDir, this.taskId, true, false);
@@ -18326,10 +18396,20 @@ ${contentText}</agent-response>`;
18326
18396
  await this.contextManager.backupContext();
18327
18397
  const messagesSinceLastCompaction = contextMessages.length - this.lastSmartCompactionMessageCount;
18328
18398
  if (messagesSinceLastCompaction <= 3) {
18399
+ logger.info("Increasing compaction level to aggressive due to low message count since last compaction.", {
18400
+ messagesSinceLastCompaction,
18401
+ smartCompactionLevel: this.smartCompactionLevel
18402
+ });
18329
18403
  this.smartCompactionLevel = Math.min(this.smartCompactionLevel + 1, CompactionLevel.Three);
18330
- } else if (messagesSinceLastCompaction > 5) {
18331
- this.smartCompactionLevel = CompactionLevel.One;
18404
+ } else if (messagesSinceLastCompaction > 5 && this.smartCompactionLevel > CompactionLevel.One) {
18405
+ logger.info("Decreasing compaction level to mild due to high message count since last compaction.", {
18406
+ messagesSinceLastCompaction
18407
+ });
18408
+ this.smartCompactionLevel = Math.max(this.smartCompactionLevel - 1, CompactionLevel.One);
18332
18409
  }
18410
+ logger.debug("Current compaction level:", {
18411
+ smartCompactionLevel: this.smartCompactionLevel
18412
+ });
18333
18413
  const compactedMessages = await smartCompactMessages(contextMessages, 10, this.smartCompactionLevel);
18334
18414
  this.lastSmartCompactionMessageCount = compactedMessages.length;
18335
18415
  this.contextManager.setContextMessages(compactedMessages);
@@ -18345,7 +18425,7 @@ ${contentText}</agent-response>`;
18345
18425
  if (!contextMessages) {
18346
18426
  contextMessages = await this.contextManager.getContextMessages();
18347
18427
  }
18348
- const userMessage = contextMessages[0];
18428
+ const userMessage = contextMessages.find((msg) => msg.role === MessageRole.User);
18349
18429
  if (!userMessage) {
18350
18430
  this.addLogMessage("warning", "No conversation to compact.");
18351
18431
  return;
@@ -19025,46 +19105,96 @@ Only answer with the commit message, nothing else.`,
19025
19105
  await this.sendUpdatedFilesUpdated();
19026
19106
  await this.sendWorktreeIntegrationStatusUpdated();
19027
19107
  }
19028
- async mergeAndSwitchToLocal(targetBranch) {
19029
- if (!this.task.worktree) {
19108
+ async switchToLocalWorkingMode(options) {
19109
+ if (options?.mergeBeforeSwitch && !this.task.worktree) {
19030
19110
  throw new Error("No worktree exists for this task");
19031
19111
  }
19032
- logger.info("Merging worktree and switching to local mode", {
19112
+ logger.info("Switching to local working mode", {
19033
19113
  baseDir: this.project.baseDir,
19034
- taskId: this.taskId
19114
+ taskId: this.taskId,
19115
+ mergeBeforeSwitch: options?.mergeBeforeSwitch ?? false
19035
19116
  });
19036
19117
  await this.waitForCurrentPromptToFinish();
19037
- try {
19038
- const effectiveTargetBranch = targetBranch || await this.worktreeManager.getProjectMainBranch(this.project.baseDir);
19039
- this.addLogMessage("loading", `Merging worktree to ${effectiveTargetBranch} branch and switching to local mode...`);
19040
- const settings = this.store.getSettings();
19041
- const symlinkFolders = settings.taskSettings.worktreeSymlinkFolders || [];
19042
- const mergeState = await this.worktreeManager.mergeWorktreeToMainWithUncommitted(
19043
- this.project.baseDir,
19044
- this.task.id,
19045
- this.task.worktree.path,
19046
- false,
19047
- this.task.name || `Task ${this.taskId} changes`,
19048
- targetBranch,
19049
- symlinkFolders
19050
- );
19051
- await this.saveTask({ lastMergeState: mergeState });
19052
- this.addLogMessage("info", `Successfully merged worktree to ${effectiveTargetBranch} branch`, true);
19053
- await this.updateTask({ workingMode: "local" });
19054
- } catch (error) {
19055
- logger.error("Failed to merge worktree and switch to local:", { error });
19056
- const isConflict = error instanceof GitError && (error.gitOutput?.toLowerCase().includes("resolve all conflicts") || error.message?.toLowerCase().includes("conflicts must be resolved first") || error.gitOutput?.toLowerCase().includes("conflicts must be resolved first"));
19057
- this.addLogMessage(
19058
- "error",
19059
- isConflict ? "worktree.mergeConflicts" : error instanceof GitError ? error.getErrorDetails() : `Failed to merge worktree: ${error instanceof Error ? error.message : String(error)}`,
19060
- true,
19061
- void 0,
19062
- isConflict ? ["rebase-worktree"] : void 0
19063
- );
19064
- await this.sendUpdatedFilesUpdated();
19065
- await this.sendWorktreeIntegrationStatusUpdated();
19066
- throw error;
19118
+ if (options?.mergeBeforeSwitch && this.task.worktree) {
19119
+ try {
19120
+ const effectiveTargetBranch = options.targetBranch || await this.worktreeManager.getProjectMainBranch(this.project.baseDir);
19121
+ this.addLogMessage("loading", `Merging worktree to ${effectiveTargetBranch} branch and switching to local mode...`);
19122
+ const settings = this.store.getSettings();
19123
+ const symlinkFolders = settings.taskSettings.worktreeSymlinkFolders || [];
19124
+ const mergeState = await this.worktreeManager.mergeWorktreeToMainWithUncommitted(
19125
+ this.project.baseDir,
19126
+ this.task.id,
19127
+ this.task.worktree.path,
19128
+ false,
19129
+ this.task.name || `Task ${this.taskId} changes`,
19130
+ options.targetBranch,
19131
+ symlinkFolders
19132
+ );
19133
+ await this.saveTask({ lastMergeState: mergeState });
19134
+ this.addLogMessage("info", `Successfully merged worktree to ${effectiveTargetBranch} branch`, true);
19135
+ } catch (error) {
19136
+ logger.error("Failed to merge worktree and switch to local:", { error });
19137
+ const isConflict = error instanceof GitError && (error.gitOutput?.toLowerCase().includes("resolve all conflicts") || error.message?.toLowerCase().includes("conflicts must be resolved first") || error.gitOutput?.toLowerCase().includes("conflicts must be resolved first"));
19138
+ this.addLogMessage(
19139
+ "error",
19140
+ isConflict ? "worktree.mergeConflicts" : error instanceof GitError ? error.getErrorDetails() : `Failed to merge worktree: ${error instanceof Error ? error.message : String(error)}`,
19141
+ true,
19142
+ void 0,
19143
+ isConflict ? ["rebase-worktree"] : void 0
19144
+ );
19145
+ await this.sendUpdatedFilesUpdated();
19146
+ await this.sendWorktreeIntegrationStatusUpdated();
19147
+ throw error;
19148
+ }
19067
19149
  }
19150
+ await this.updateTask({ workingMode: "local" });
19151
+ }
19152
+ async switchToWorktreeWorkingMode(options) {
19153
+ logger.info("Switching to worktree working mode", {
19154
+ baseDir: this.project.baseDir,
19155
+ taskId: this.taskId,
19156
+ carryOverUncommittedChanges: options?.carryOverUncommittedChanges ?? false,
19157
+ dropSourceChanges: options?.dropSourceChanges ?? true
19158
+ });
19159
+ await this.waitForCurrentPromptToFinish();
19160
+ let stashId = null;
19161
+ const settings = this.store.getSettings();
19162
+ const symlinkFolders = settings.taskSettings.worktreeSymlinkFolders || [];
19163
+ if (options?.carryOverUncommittedChanges) {
19164
+ const timestamp = Date.now();
19165
+ const shortId = this.taskId.length > 24 ? this.taskId.substring(24) : this.taskId;
19166
+ stashId = `local-${shortId}-to-worktree-${timestamp}`;
19167
+ try {
19168
+ const stashResult = await this.worktreeManager.stashUncommittedChanges(
19169
+ stashId,
19170
+ this.project.baseDir,
19171
+ "Uncommitted changes to carry over to worktree",
19172
+ symlinkFolders
19173
+ );
19174
+ if (!stashResult) {
19175
+ stashId = null;
19176
+ }
19177
+ } catch (error) {
19178
+ logger.error("Failed to stash uncommitted changes before worktree switch:", { error });
19179
+ stashId = null;
19180
+ }
19181
+ }
19182
+ await this.updateTask({ workingMode: "worktree" });
19183
+ if (stashId && this.task.worktree) {
19184
+ try {
19185
+ await this.worktreeManager.applyStash(this.task.worktree.path, stashId);
19186
+ if (!options?.dropSourceChanges) {
19187
+ await this.worktreeManager.applyStash(this.project.baseDir, stashId);
19188
+ }
19189
+ await this.worktreeManager.dropStash(this.project.baseDir, stashId);
19190
+ } catch (error) {
19191
+ logger.error("Failed to apply stashed changes to worktree:", { error });
19192
+ }
19193
+ }
19194
+ void this.sendUpdatedFilesUpdated();
19195
+ }
19196
+ async getLocalUncommittedFiles() {
19197
+ return await this.worktreeManager.getUncommittedFiles(this.project.baseDir);
19068
19198
  }
19069
19199
  async applyUncommittedChanges(targetBranch) {
19070
19200
  if (!this.task.worktree) {
@@ -20876,132 +21006,6 @@ const anthropicCompatibleProviderStrategy = {
20876
21006
  // Cache control
20877
21007
  getCacheControl: getAnthropicCacheControl
20878
21008
  };
20879
- const loadAuggieSdk = async () => import("@augmentcode/auggie-sdk");
20880
- const AUGGIE_MODELS = [
20881
- {
20882
- id: "claude-haiku-4-5",
20883
- maxInputTokens: 2e5,
20884
- maxOutputTokensLimit: 8192
20885
- },
20886
- {
20887
- id: "claude-sonnet-4",
20888
- maxInputTokens: 2e5,
20889
- maxOutputTokensLimit: 16384
20890
- },
20891
- {
20892
- id: "claude-sonnet-4-5",
20893
- maxInputTokens: 2e5,
20894
- maxOutputTokensLimit: 16384
20895
- },
20896
- {
20897
- id: "claude-opus-4-5",
20898
- maxInputTokens: 2e5,
20899
- maxOutputTokensLimit: 32768
20900
- },
20901
- {
20902
- id: "claude-opus-4-6",
20903
- maxInputTokens: 2e5,
20904
- maxOutputTokensLimit: 32768
20905
- },
20906
- {
20907
- id: "claude-opus-4-7",
20908
- maxInputTokens: 2e5,
20909
- maxOutputTokensLimit: 32768
20910
- },
20911
- { id: "gpt-5-1", maxInputTokens: 2e5, maxOutputTokensLimit: 16384 },
20912
- { id: "gpt-5-2", maxInputTokens: 2e5, maxOutputTokensLimit: 16384 },
20913
- { id: "gpt-5-4", maxInputTokens: 2e5, maxOutputTokensLimit: 16384 }
20914
- ];
20915
- const loadAuggieModels = async (profile, _settings) => {
20916
- if (!isAuggieProvider(profile.provider)) {
20917
- return {
20918
- models: [],
20919
- success: false
20920
- };
20921
- }
20922
- const models = AUGGIE_MODELS.map((model) => ({
20923
- id: model.id,
20924
- providerId: profile.id,
20925
- maxInputTokens: model.maxInputTokens,
20926
- maxOutputTokensLimit: model.maxOutputTokensLimit
20927
- }));
20928
- logger.info(`Loaded ${models.length} Auggie models for profile ${profile.id}`);
20929
- return { models, success: true };
20930
- };
20931
- const hasAuggieEnvVars = (settings) => {
20932
- if (getEffectiveEnvironmentVariable("AUGMENT_API_TOKEN", settings, void 0)?.value && getEffectiveEnvironmentVariable("AUGMENT_API_URL", settings, void 0)?.value) {
20933
- return true;
20934
- }
20935
- const auggieSessionFile = path.join(os.homedir(), ".augment", "session.json");
20936
- if (findExecutableInPath("auggie") && fs$1.existsSync(auggieSessionFile)) {
20937
- return true;
20938
- }
20939
- return false;
20940
- };
20941
- const getAuggieAiderMapping = (provider, modelId) => {
20942
- const auggieProvider = provider.provider;
20943
- const envVars = {};
20944
- if (auggieProvider.apiKey) {
20945
- envVars.AUGMENT_API_TOKEN = auggieProvider.apiKey;
20946
- }
20947
- if (auggieProvider.apiUrl) {
20948
- envVars.AUGMENT_API_URL = auggieProvider.apiUrl;
20949
- }
20950
- return {
20951
- modelName: `auggie/${modelId}`,
20952
- environmentVariables: envVars
20953
- };
20954
- };
20955
- const createAuggieLlm = async (profile, model, settings, projectDir) => {
20956
- const provider = profile.provider;
20957
- let apiKey = provider.apiKey;
20958
- let apiUrl = provider.apiUrl;
20959
- if (!apiKey) {
20960
- const effectiveVar = getEffectiveEnvironmentVariable("AUGMENT_API_TOKEN", settings, projectDir);
20961
- if (effectiveVar) {
20962
- apiKey = effectiveVar.value;
20963
- logger.debug(`Loaded AUGMENT_API_TOKEN from ${effectiveVar.source}`);
20964
- }
20965
- }
20966
- if (!apiUrl) {
20967
- const effectiveUrlVar = getEffectiveEnvironmentVariable("AUGMENT_API_URL", settings, projectDir);
20968
- if (effectiveUrlVar) {
20969
- apiUrl = effectiveUrlVar.value;
20970
- logger.debug(`Loaded AUGMENT_API_URL from ${effectiveUrlVar.source}`);
20971
- }
20972
- }
20973
- if (apiKey && !apiUrl) {
20974
- throw new Error("API URL is required when API key is provided.");
20975
- }
20976
- const { AugmentLanguageModel, resolveAugmentCredentials } = await loadAuggieSdk();
20977
- const AugmentLanguageModelCtor = AugmentLanguageModel;
20978
- if (apiKey && apiUrl) {
20979
- return new AugmentLanguageModelCtor(model.id, {
20980
- apiKey,
20981
- apiUrl
20982
- });
20983
- } else {
20984
- const credentials = await resolveAugmentCredentials();
20985
- return new AugmentLanguageModelCtor(model.id, credentials);
20986
- }
20987
- };
20988
- const getAuggieUsageReport = (_task, _provider, model) => {
20989
- return {
20990
- model: model.id,
20991
- sentTokens: 0,
20992
- receivedTokens: 0,
20993
- messageCost: 0,
20994
- agentTotalCost: 0
20995
- };
20996
- };
20997
- const auggieProviderStrategy = {
20998
- createLlm: createAuggieLlm,
20999
- getUsageReport: getAuggieUsageReport,
21000
- loadModels: loadAuggieModels,
21001
- hasEnvVars: hasAuggieEnvVars,
21002
- getAiderMapping: getAuggieAiderMapping,
21003
- getModelInfo: getDefaultModelInfo
21004
- };
21005
21009
  const extractResourceNameFromEndpoint = (endpoint) => {
21006
21010
  try {
21007
21011
  const url = new URL(endpoint);
@@ -21427,199 +21431,6 @@ const cerebrasProviderStrategy = {
21427
21431
  getAiderMapping: getCerebrasAiderMapping,
21428
21432
  getModelInfo: getDefaultModelInfo
21429
21433
  };
21430
- const loadClaudeAgentSdkModels = async (profile, _settings) => {
21431
- if (!isClaudeAgentSdkProvider(profile.provider)) {
21432
- return {
21433
- models: [],
21434
- success: false
21435
- };
21436
- }
21437
- try {
21438
- const models = [
21439
- {
21440
- id: "haiku",
21441
- providerId: profile.id,
21442
- maxInputTokens: 2e5,
21443
- maxOutputTokensLimit: 64e3
21444
- },
21445
- {
21446
- id: "sonnet",
21447
- providerId: profile.id,
21448
- maxInputTokens: 2e5,
21449
- maxOutputTokens: 64e3
21450
- },
21451
- {
21452
- id: "opus",
21453
- providerId: profile.id,
21454
- maxInputTokens: 2e5,
21455
- maxOutputTokensLimit: 64e3
21456
- }
21457
- ];
21458
- logger.info(`Loaded ${models.length} Claude Agent SDK models for profile ${profile.id}`);
21459
- return { models, success: true };
21460
- } catch (error) {
21461
- const errorMsg = typeof error === "string" ? error : error instanceof Error ? error.message : "Unknown error loading Claude Agent SDK models";
21462
- logger.error("Error loading Claude Agent SDK models:", error);
21463
- return { models: [], success: false, error: errorMsg };
21464
- }
21465
- };
21466
- const hasClaudeAgentSdkEnvVars = () => {
21467
- return findExecutableInPath("claude") !== null;
21468
- };
21469
- const getClaudeAgentSdkAiderMapping = (_provider, modelId) => {
21470
- return {
21471
- environmentVariables: {},
21472
- modelName: `claude-agent-sdk/${modelId}`
21473
- };
21474
- };
21475
- const normalizeAiSdkToolSet = (toolSet) => {
21476
- const normalizedToolSet = {};
21477
- for (const [toolName, toolDefinition] of Object.entries(toolSet)) {
21478
- let normalizedTool = { ...toolDefinition };
21479
- const inputSchema = toolDefinition.inputSchema;
21480
- if (inputSchema && typeof inputSchema === "object" && "jsonSchema" in inputSchema) {
21481
- const jsonSchemaObj = inputSchema.jsonSchema;
21482
- const schema = typeof jsonSchemaObj === "function" ? jsonSchemaObj() : jsonSchemaObj;
21483
- const zodShape = jsonSchemaToZod.JSONSchemaToZod.convert(schema);
21484
- normalizedTool = {
21485
- ...toolDefinition,
21486
- inputSchema: zodShape
21487
- };
21488
- logger.debug(`Converted JSONSchema to Zod for tool: ${toolName}`);
21489
- }
21490
- if (toolName === `${AIDER_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${AIDER_TOOL_RUN_PROMPT}`) {
21491
- const originalExecute = normalizedTool.execute;
21492
- if (!originalExecute) {
21493
- normalizedToolSet[toolName] = normalizedTool;
21494
- continue;
21495
- }
21496
- normalizedToolSet[toolName] = {
21497
- ...normalizedTool,
21498
- execute: async (args, options) => {
21499
- const result = await originalExecute(args, options);
21500
- if (result && typeof result === "object") {
21501
- delete result.responses;
21502
- }
21503
- return result;
21504
- }
21505
- };
21506
- } else if (toolName === `${SUBAGENTS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${SUBAGENTS_TOOL_RUN_TASK}`) {
21507
- const originalExecute = normalizedTool.execute;
21508
- if (!originalExecute) {
21509
- normalizedToolSet[toolName] = normalizedTool;
21510
- continue;
21511
- }
21512
- normalizedToolSet[toolName] = {
21513
- ...normalizedTool,
21514
- execute: async (args, options) => {
21515
- const result = await originalExecute(args, options);
21516
- if (result && typeof result === "object") {
21517
- try {
21518
- const messages = result.messages;
21519
- if (Array.isArray(messages) && messages.length > 0) {
21520
- const lastMessage = messages[messages.length - 1];
21521
- return {
21522
- messages: [lastMessage],
21523
- promptContext: result.promptContext
21524
- };
21525
- }
21526
- } catch (error) {
21527
- logger.warn("Failed to optimize subagent result:", error);
21528
- }
21529
- }
21530
- return result;
21531
- }
21532
- };
21533
- } else {
21534
- normalizedToolSet[toolName] = normalizedTool;
21535
- }
21536
- }
21537
- return normalizedToolSet;
21538
- };
21539
- const createClaudeAgentSdkLlm = (_profile, model, _settings, _projectDir, toolSet, systemPrompt, providerMetadata) => {
21540
- const settings = {
21541
- env: {
21542
- // setting MAX_MCP_OUTPUT_TOKENS to a high number to avoid errors
21543
- MAX_MCP_OUTPUT_TOKENS: "9999999"
21544
- },
21545
- // for now only tools from AiderDesk are allowed
21546
- disallowedTools: [
21547
- "AskUserQuestion",
21548
- "Bash",
21549
- "TaskOutput",
21550
- "Edit",
21551
- "EnterPlanMode",
21552
- "ExitPlanMode",
21553
- "Glob",
21554
- "Grep",
21555
- "KillShell",
21556
- "MCPSearch",
21557
- "NotebookEdit",
21558
- "Read",
21559
- "Skill",
21560
- "Task",
21561
- "TaskCreate",
21562
- "TaskGet",
21563
- "TaskList",
21564
- "TaskUpdate",
21565
- "TodoWrite",
21566
- "WebFetch",
21567
- "WebSearch",
21568
- "Write",
21569
- "LSP"
21570
- ]
21571
- };
21572
- if (providerMetadata && typeof providerMetadata === "object" && "claude-code" in providerMetadata) {
21573
- const metadata = providerMetadata["claude-code"] || {};
21574
- settings.resume = metadata.sessionId;
21575
- }
21576
- if (!isDev() && isElectron()) {
21577
- settings.pathToClaudeCodeExecutable = CLAUDE_CODE_EXECUTABLE_PATH;
21578
- }
21579
- if (toolSet) {
21580
- logger.debug(`Adding ${Object.keys(toolSet).length} tools to Claude Agent SDK`, {
21581
- tools: Object.keys(toolSet)
21582
- });
21583
- settings.aiSdkTools = normalizeAiSdkToolSet(toolSet);
21584
- settings.allowedTools = ["mcp__ai-sdk"];
21585
- } else {
21586
- settings.allowedTools = [];
21587
- }
21588
- if (systemPrompt) {
21589
- settings.systemPrompt = systemPrompt;
21590
- }
21591
- return aiSdkProviderClaudeCode.claudeCode(model.id, settings);
21592
- };
21593
- const getClaudeAgentSdkUsageReport = (task, provider, model, _usage, providerMetadata) => {
21594
- const data = providerMetadata["claude-code"] || {};
21595
- const usage = data.lastMessageRawUsage || data.rawUsage || {};
21596
- logger.debug("Claude Agent SDK usage:", { usage: _usage, providerMetadata });
21597
- const sentTokens = usage.input_tokens || 0;
21598
- const receivedTokens = usage.output_tokens || 0;
21599
- const cacheWriteTokens = usage.cache_creation_input_tokens || 0;
21600
- const cacheReadTokens = usage.cache_read_input_tokens || 0;
21601
- const messageCost = calculateCost(model, sentTokens, receivedTokens, cacheReadTokens);
21602
- return {
21603
- model: `${provider.id}/${model.id}`,
21604
- sentTokens: sentTokens + cacheReadTokens,
21605
- receivedTokens: receivedTokens + cacheWriteTokens,
21606
- messageCost,
21607
- agentTotalCost: task.task.agentTotalCost + messageCost
21608
- };
21609
- };
21610
- const getClaudeAgentSdkProviderParameters = () => {
21611
- return {
21612
- system: void 0
21613
- };
21614
- };
21615
- const claudeAgentSdkProviderStrategy = {
21616
- createLlm: createClaudeAgentSdkLlm,
21617
- getUsageReport: getClaudeAgentSdkUsageReport,
21618
- loadModels: loadClaudeAgentSdkModels,
21619
- hasEnvVars: hasClaudeAgentSdkEnvVars,
21620
- getAiderMapping: getClaudeAgentSdkAiderMapping,
21621
- getProviderParameters: getClaudeAgentSdkProviderParameters
21622
- };
21623
21434
  const loadDeepseekModels = async (profile, settings) => {
21624
21435
  if (!isDeepseekProvider(profile.provider)) {
21625
21436
  return { models: [], success: false };
@@ -22678,6 +22489,20 @@ const loadMinimaxModels = async (profile) => {
22678
22489
  };
22679
22490
  }
22680
22491
  const hardcodedModels = [
22492
+ {
22493
+ id: "MiniMax-M3",
22494
+ providerId: profile.id,
22495
+ maxInputTokens: 1e6,
22496
+ maxOutputTokensLimit: 131072,
22497
+ inputCostPerToken: 3e-7,
22498
+ // 0.3 per 1M tokens
22499
+ outputCostPerToken: 12e-7,
22500
+ // 1.2 per 1M tokens
22501
+ cacheReadInputTokenCost: 6e-8,
22502
+ // 0.06 per 1M tokens
22503
+ cacheWriteInputTokenCost: 375e-9
22504
+ // 0.375 per 1M tokens
22505
+ },
22681
22506
  {
22682
22507
  id: "MiniMax-M2.7",
22683
22508
  providerId: profile.id,
@@ -24396,7 +24221,7 @@ const zaiPlanProviderStrategy = {
24396
24221
  const MODELS_META_URL = "https://models.dev/api.json";
24397
24222
  const MODELS_FILE = path.join(AIDER_DESK_DATA_DIR, "models.json");
24398
24223
  const PROVIDER_MODELS_CACHE_FILE = path.join(AIDER_DESK_CACHE_DIR, "provider-models.json");
24399
- const PROVIDER_MODELS_CACHE_VERSION = 1;
24224
+ const PROVIDER_MODELS_CACHE_VERSION = 2;
24400
24225
  class ModelManager {
24401
24226
  constructor(store, eventManager) {
24402
24227
  this.store = store;
@@ -24412,11 +24237,9 @@ class ModelManager {
24412
24237
  providerRegistry = {
24413
24238
  anthropic: anthropicProviderStrategy,
24414
24239
  "anthropic-compatible": anthropicCompatibleProviderStrategy,
24415
- auggie: auggieProviderStrategy,
24416
24240
  azure: azureProviderStrategy,
24417
24241
  bedrock: bedrockProviderStrategy,
24418
24242
  cerebras: cerebrasProviderStrategy,
24419
- "claude-agent-sdk": claudeAgentSdkProviderStrategy,
24420
24243
  deepseek: deepseekProviderStrategy,
24421
24244
  gemini: geminiProviderStrategy,
24422
24245
  "gemini-cli": geminiCliProviderStrategy,
@@ -25089,7 +24912,7 @@ class ModelManager {
25089
24912
  const { provider } = registered;
25090
24913
  this.providerRegistry[provider.provider.name] = {
25091
24914
  ...provider.strategy,
25092
- createLlm: (profile2, model, settings, projectDir) => provider.strategy.createLlm(profile2, model, settings, projectDir),
24915
+ createLlm: (profile2, model, settings, projectDir, toolSet, systemPrompt, providerMetadata) => provider.strategy.createLlm(profile2, model, settings, projectDir, toolSet, systemPrompt, providerMetadata),
25093
24916
  getUsageReport: provider.strategy.getUsageReport || getDefaultUsageReport,
25094
24917
  getProviderOptions: provider.strategy.getProviderOptions ? (_provider, model) => provider.strategy.getProviderOptions(model) : void 0,
25095
24918
  getProviderTools: provider.strategy.getProviderTools ? (_provider, model) => provider.strategy.getProviderTools(model) : void 0,
@@ -25147,7 +24970,11 @@ class ModelManager {
25147
24970
  this.eventManager.sendProvidersUpdated(this.getProviders());
25148
24971
  }
25149
24972
  getProviders() {
25150
- return [...this.store.getProviders(), ...this.getExtensionProviderProfiles()];
24973
+ const providersById = new Map(this.store.getProviders().map((p) => [p.id, p]));
24974
+ for (const extensionProfile of this.getExtensionProviderProfiles()) {
24975
+ providersById.set(extensionProfile.id, extensionProfile);
24976
+ }
24977
+ return [...providersById.values()];
25151
24978
  }
25152
24979
  getExtensionProviderProfiles() {
25153
24980
  return [...this.extensionProviders.values()].map((e) => e.profile);
@@ -27619,15 +27446,48 @@ class ExtensionManager {
27619
27446
  errors
27620
27447
  };
27621
27448
  }
27449
+ getToolsInfo(projectDir) {
27450
+ const allExtensions = this.registry.getExtensions(projectDir);
27451
+ const extensions = this.filterEnabledExtensions(allExtensions);
27452
+ const result = [];
27453
+ for (const loaded of extensions) {
27454
+ const { instance, metadata } = loaded;
27455
+ if (!instance.getTools) {
27456
+ continue;
27457
+ }
27458
+ try {
27459
+ const context = new ExtensionContextImpl(loaded.id, metadata.name, this.store, this.modelManager, this.eventManager, this.memoryManager);
27460
+ const tools = instance.getTools(context, "agent", DEFAULT_AGENT_PROFILE);
27461
+ if (!Array.isArray(tools)) {
27462
+ continue;
27463
+ }
27464
+ const toolInfos = tools.filter((tool) => tool.name && tool.description).map((tool) => ({ name: tool.name, description: tool.description }));
27465
+ if (toolInfos.length > 0) {
27466
+ result.push({
27467
+ extensionId: loaded.id,
27468
+ extensionName: metadata.name,
27469
+ tools: toolInfos
27470
+ });
27471
+ }
27472
+ } catch (error) {
27473
+ logger.error(`[Extensions] Failed to get tools info from extension '${metadata.name}':`, error);
27474
+ }
27475
+ }
27476
+ return result;
27477
+ }
27622
27478
  getTools(task, mode, profile) {
27623
27479
  const collectedTools = [];
27624
27480
  const allExtensions = this.registry.getExtensions(task.getProjectDir());
27625
27481
  const extensions = this.filterEnabledExtensions(allExtensions);
27482
+ const disabledExtensionTools = profile.disabledExtensionTools ?? [];
27626
27483
  for (const loaded of extensions) {
27627
27484
  const { instance, metadata } = loaded;
27628
27485
  if (!instance.getTools) {
27629
27486
  continue;
27630
27487
  }
27488
+ if (disabledExtensionTools.includes(loaded.id)) {
27489
+ continue;
27490
+ }
27631
27491
  try {
27632
27492
  const context = new ExtensionContextImpl(
27633
27493
  loaded.id,
@@ -27650,6 +27510,10 @@ class ExtensionManager {
27650
27510
  logger.error(`[Extensions] Invalid tool '${tool.name}' from extension '${metadata.name}': ${validation.errors.join(", ")}`);
27651
27511
  continue;
27652
27512
  }
27513
+ const fullToolId = `${loaded.id}${TOOL_GROUP_NAME_SEPARATOR}${tool.name}`;
27514
+ if (profile.toolApprovals?.[fullToolId] === ToolApprovalState.Never || profile.toolApprovals?.[tool.name] === ToolApprovalState.Never) {
27515
+ continue;
27516
+ }
27653
27517
  collectedTools.push({
27654
27518
  extensionId: loaded.id,
27655
27519
  extensionName: metadata.name,
@@ -27673,11 +27537,12 @@ class ExtensionManager {
27673
27537
  * @param abortSignal - Optional AbortSignal for cancellation support
27674
27538
  * @returns A ToolSet containing all approved extension tools
27675
27539
  */
27676
- createExtensionToolset(task, mode, profile, allTools, abortSignal) {
27540
+ createExtensionToolset(task, mode, profile, allTools, approvalManager, abortSignal) {
27677
27541
  const toolSet = {};
27678
27542
  const registeredTools = this.getTools(task, mode, profile);
27679
27543
  for (const { extensionId, extensionName, tool } of registeredTools) {
27680
27544
  const toolId = tool.name;
27545
+ const fullToolId = `${extensionId}${TOOL_GROUP_NAME_SEPARATOR}${tool.name}`;
27681
27546
  const context = new ExtensionContextImpl(
27682
27547
  extensionId,
27683
27548
  extensionName,
@@ -27688,14 +27553,25 @@ class ExtensionManager {
27688
27553
  task.project,
27689
27554
  task
27690
27555
  );
27691
- if (profile.toolApprovals?.[toolId] === ToolApprovalState.Never) {
27692
- logger.debug(`[Extensions] Skipping tool '${tool.name}' (marked as Never approved)`);
27693
- continue;
27694
- }
27695
27556
  toolSet[toolId] = {
27696
27557
  description: tool.description,
27697
27558
  inputSchema: tool.inputSchema,
27698
27559
  execute: async (input, options) => {
27560
+ const toolApproval = profile.toolApprovals?.[fullToolId];
27561
+ logger.debug(
27562
+ `[Extensions] Tool '${tool.name}' approval check: fullToolId='${fullToolId}', approval=${toolApproval}, allApprovals=${JSON.stringify(profile.toolApprovals)}`
27563
+ );
27564
+ if (toolApproval === ToolApprovalState.Ask) {
27565
+ const questionKey = fullToolId;
27566
+ const questionText = `Approve tool ${tool.name} from ${extensionName} extension?`;
27567
+ const questionSubject = input ? JSON.stringify(input) : void 0;
27568
+ const [isApproved, userInput] = await approvalManager.handleToolApproval(fullToolId, input, questionKey, questionText, questionSubject);
27569
+ if (!isApproved) {
27570
+ logger.warn(`Tool execution denied by user: ${fullToolId}`);
27571
+ return `Tool execution denied by user.${userInput ? ` User input: ${userInput}` : ""}`;
27572
+ }
27573
+ logger.debug(`Tool execution approved: ${fullToolId}`);
27574
+ }
27699
27575
  const allToolsInternal = Object.entries(allTools).reduce(
27700
27576
  (acc, [toolId2, tool2]) => {
27701
27577
  acc[toolId2] = {
@@ -28933,12 +28809,26 @@ class EventsHandler {
28933
28809
  }
28934
28810
  await task.mergeWorktreeToMain(squash, targetBranch, commitMessage);
28935
28811
  }
28936
- async mergeAndSwitchToLocal(baseDir, taskId, targetBranch) {
28812
+ async switchToLocalWorkingMode(baseDir, taskId, options) {
28813
+ const task = this.projectManager.getProject(baseDir).getTask(taskId);
28814
+ if (!task) {
28815
+ throw new Error(`Task ${taskId} not found`);
28816
+ }
28817
+ await task.switchToLocalWorkingMode(options);
28818
+ }
28819
+ async switchToWorktreeWorkingMode(baseDir, taskId, options) {
28937
28820
  const task = this.projectManager.getProject(baseDir).getTask(taskId);
28938
28821
  if (!task) {
28939
28822
  throw new Error(`Task ${taskId} not found`);
28940
28823
  }
28941
- await task.mergeAndSwitchToLocal(targetBranch);
28824
+ await task.switchToWorktreeWorkingMode(options);
28825
+ }
28826
+ async getLocalUncommittedFiles(baseDir, taskId) {
28827
+ const task = this.projectManager.getProject(baseDir).getTask(taskId);
28828
+ if (!task) {
28829
+ throw new Error(`Task ${taskId} not found`);
28830
+ }
28831
+ return await task.getLocalUncommittedFiles();
28942
28832
  }
28943
28833
  async applyUncommittedChanges(baseDir, taskId, targetBranch) {
28944
28834
  const task = this.projectManager.getProject(baseDir).getTask(taskId);
@@ -29417,6 +29307,9 @@ ${error instanceof Error ? error.message : String(error)}`);
29417
29307
  readmeContent: ext.readmeContent
29418
29308
  }));
29419
29309
  }
29310
+ getExtensionToolsInfo(projectDir) {
29311
+ return this.extensionManager.getToolsInfo(projectDir);
29312
+ }
29420
29313
  async getAvailableExtensions(repositories, forceRefresh, fetchOnly) {
29421
29314
  return await this.extensionManager.getAvailableExtensions(repositories, forceRefresh, fetchOnly);
29422
29315
  }
@@ -29452,6 +29345,7 @@ ${error instanceof Error ? error.message : String(error)}`);
29452
29345
  return filtered.map((c) => ({
29453
29346
  extensionId: c.extensionId,
29454
29347
  componentId: c.component.id,
29348
+ name: c.component.name,
29455
29349
  placement: c.component.placement,
29456
29350
  jsx: c.component.jsx,
29457
29351
  loadData: c.component.loadData,
@@ -30791,6 +30685,9 @@ const migrateSettingsV19toV20 = (settings) => {
30791
30685
  }
30792
30686
  };
30793
30687
  };
30688
+ const migrateProvidersV20toV21 = (providers) => {
30689
+ return providers.filter((p) => p.provider.name !== "claude-agent-sdk" && p.provider.name !== "auggie");
30690
+ };
30794
30691
  const DEFAULT_SETTINGS = {
30795
30692
  language: "en",
30796
30693
  startupMode: ProjectStartMode.Empty,
@@ -30870,7 +30767,7 @@ const DEFAULT_SETTINGS = {
30870
30767
  const compareBaseDirs = (baseDir1, baseDir2) => {
30871
30768
  return normalizeBaseDir(baseDir1) === normalizeBaseDir(baseDir2);
30872
30769
  };
30873
- const CURRENT_SETTINGS_VERSION = 20;
30770
+ const CURRENT_SETTINGS_VERSION = 21;
30874
30771
  class Store {
30875
30772
  // @ts-expect-error expected to be initialized
30876
30773
  store;
@@ -31025,6 +30922,10 @@ class Store {
31025
30922
  settings = migrateSettingsV19toV20(settings);
31026
30923
  settingsVersion = 20;
31027
30924
  }
30925
+ if (settingsVersion === 20) {
30926
+ providers = migrateProvidersV20toV21(providers || []);
30927
+ settingsVersion = 21;
30928
+ }
31028
30929
  this.store.set("settings", settings);
31029
30930
  this.store.set("openProjects", openProjects || []);
31030
30931
  this.store.set("providers", providers || []);