@aiderdesk/aiderdesk 0.67.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 (66) hide show
  1. package/out/renderer/assets/{arc-BnPy4nhx.js → arc--tfU0Ahe.js} +1 -1
  2. package/out/renderer/assets/{architectureDiagram-3BPJPVTR-_SwpsbSs.js → architectureDiagram-3BPJPVTR-D2uDC6Pw.js} +3 -3
  3. package/out/renderer/assets/{blockDiagram-GPEHLZMM-D8Bc_xCY.js → blockDiagram-GPEHLZMM-BKopmU2x.js} +4 -4
  4. package/out/renderer/assets/{c4Diagram-AAUBKEIU-Ddic5IpI.js → c4Diagram-AAUBKEIU-CU9NH6bi.js} +2 -2
  5. package/out/renderer/assets/{channel-I2sEi2rW.js → channel-CBm2XBi8.js} +1 -1
  6. package/out/renderer/assets/{chunk-2J33WTMH-Cqrw7IHw.js → chunk-2J33WTMH-Yb07TKpF.js} +1 -1
  7. package/out/renderer/assets/{chunk-4BX2VUAB-DKY4go9E.js → chunk-4BX2VUAB-B1KgZMg1.js} +1 -1
  8. package/out/renderer/assets/{chunk-55IACEB6-COO6Gc0F.js → chunk-55IACEB6-BQ1g6F53.js} +1 -1
  9. package/out/renderer/assets/{chunk-727SXJPM-Bu8zhvGn.js → chunk-727SXJPM-nQzqwjgJ.js} +5 -5
  10. package/out/renderer/assets/{chunk-AQP2D5EJ-JHXEk1U1.js → chunk-AQP2D5EJ-8vVOUXUS.js} +3 -3
  11. package/out/renderer/assets/{chunk-FMBD7UC4-BqbYNISq.js → chunk-FMBD7UC4-dVquqYuV.js} +1 -1
  12. package/out/renderer/assets/{chunk-ND2GUHAM-D29ZgnDp.js → chunk-ND2GUHAM-Bu2VSodd.js} +1 -1
  13. package/out/renderer/assets/{chunk-QZHKN3VN-B-pbtAbR.js → chunk-QZHKN3VN-Bl58FmbX.js} +1 -1
  14. package/out/renderer/assets/{classDiagram-4FO5ZUOK-B7_tIdbP.js → classDiagram-4FO5ZUOK-Dmfohc7-.js} +6 -6
  15. package/out/renderer/assets/{classDiagram-v2-Q7XG4LA2-B7_tIdbP.js → classDiagram-v2-Q7XG4LA2-Dmfohc7-.js} +6 -6
  16. package/out/renderer/assets/{cose-bilkent-S5V4N54A-CGNgxRV6.js → cose-bilkent-S5V4N54A-8bgOaac3.js} +1 -1
  17. package/out/renderer/assets/{dagre-BM42HDAG-DB-tI7Ml.js → dagre-BM42HDAG-BZZ0JLHX.js} +3 -3
  18. package/out/renderer/assets/{diagram-2AECGRRQ-Cdwrgy7R.js → diagram-2AECGRRQ-9oArT1ZV.js} +3 -3
  19. package/out/renderer/assets/{diagram-5GNKFQAL-BnvZ78_N.js → diagram-5GNKFQAL-OMRICVmJ.js} +4 -4
  20. package/out/renderer/assets/{diagram-KO2AKTUF-VUrfwRCk.js → diagram-KO2AKTUF-pjPRIjL7.js} +3 -3
  21. package/out/renderer/assets/{diagram-LMA3HP47-_Vo8GrFC.js → diagram-LMA3HP47-cRVrdFbD.js} +3 -3
  22. package/out/renderer/assets/{diagram-OG6HWLK6-CoLz9kio.js → diagram-OG6HWLK6-gJvA5dOA.js} +4 -4
  23. package/out/renderer/assets/{erDiagram-TEJ5UH35-B3Bg3gW1.js → erDiagram-TEJ5UH35-BvHOuuhD.js} +4 -4
  24. package/out/renderer/assets/{flowDiagram-I6XJVG4X-BgyAk0Xe.js → flowDiagram-I6XJVG4X-D17Inzho.js} +6 -6
  25. package/out/renderer/assets/{ganttDiagram-6RSMTGT7-NnVRwQPs.js → ganttDiagram-6RSMTGT7-CIorTPIp.js} +1 -1
  26. package/out/renderer/assets/{gitGraphDiagram-PVQCEYII-C4vrRnDw.js → gitGraphDiagram-PVQCEYII-R3v9UanE.js} +4 -4
  27. package/out/renderer/assets/{graph-BZvTCUpv.js → graph-DNFReSa8.js} +1 -1
  28. package/out/renderer/assets/{index-P63PgYUG.js → index-COTYjomB.js} +28970 -133308
  29. package/out/renderer/assets/{index-zdiQSGqQ.css → index-D56aByCg.css} +40 -0
  30. package/out/renderer/assets/{infoDiagram-5YYISTIA-zTriWVJJ.js → infoDiagram-5YYISTIA-B4hJmlOG.js} +2 -2
  31. package/out/renderer/assets/{ishikawaDiagram-YF4QCWOH-DHO6Yeea.js → ishikawaDiagram-YF4QCWOH-DxohqJKP.js} +1 -1
  32. package/out/renderer/assets/{journeyDiagram-JHISSGLW-CZsRcg2X.js → journeyDiagram-JHISSGLW-BxFBZYTB.js} +4 -4
  33. package/out/renderer/assets/jsx-dev-runtime-BsbxiHH-.js +49 -0
  34. package/out/renderer/assets/{kanban-definition-UN3LZRKU-DSpAeh8p.js → kanban-definition-UN3LZRKU-B39Yt2kD.js} +2 -2
  35. package/out/renderer/assets/{layout-B5A8fT-Z.js → layout-yXTq0RED.js} +2 -2
  36. package/out/renderer/assets/{mindmap-definition-RKZ34NQL-Bns9Ab8p.js → mindmap-definition-RKZ34NQL-BD2OIaSH.js} +3 -3
  37. package/out/renderer/assets/{pieDiagram-4H26LBE5-BX36DY7W.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-DuD2qSni.js → quadrantDiagram-W4KKPZXB-BLY0H2F6.js} +1 -1
  43. package/out/renderer/assets/{requirementDiagram-4Y6WPE33-Th2uknlw.js → requirementDiagram-4Y6WPE33-DEJf5mz_.js} +3 -3
  44. package/out/renderer/assets/{sankeyDiagram-5OEKKPKP-DP3WODC7.js → sankeyDiagram-5OEKKPKP-D3BkTe8z.js} +1 -1
  45. package/out/renderer/assets/{sequenceDiagram-3UESZ5HK-BvpGEq-v.js → sequenceDiagram-3UESZ5HK-DUU_lUpD.js} +3 -3
  46. package/out/renderer/assets/{stateDiagram-AJRCARHV-Btrfma35.js → stateDiagram-AJRCARHV-DnH4p93i.js} +6 -6
  47. package/out/renderer/assets/{stateDiagram-v2-BHNVJYJU-v-8JiozM.js → stateDiagram-v2-BHNVJYJU-CRXRiP95.js} +4 -4
  48. package/out/renderer/assets/{timeline-definition-PNZ67QCA-prZy7cGx.js → timeline-definition-PNZ67QCA-Uv34KQP0.js} +2 -2
  49. package/out/renderer/assets/{vennDiagram-CIIHVFJN-XCVp5OjS.js → vennDiagram-CIIHVFJN-JyUQxmn1.js} +1 -1
  50. package/out/renderer/assets/{wardley-L42UT6IY-FZSNLjCs.js → wardley-L42UT6IY-4X2JoJpL.js} +1 -1
  51. package/out/renderer/assets/{wardleyDiagram-YWT4CUSO-Cla_7ryL.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-CKvwGbhk.js → xychartDiagram-2RQKCTM6-PJZgkel8.js} +1 -1
  54. package/out/renderer/index.html +2 -2
  55. package/out/resources/skills/extension-creator/SKILL.md +24 -0
  56. package/out/resources/skills/extension-creator/references/examples-gallery.md +11 -0
  57. package/out/resources/skills/extension-creator/references/extension-interface.md +4 -0
  58. package/out/resources/skills/extension-creator/references/external-libraries.md +159 -0
  59. package/out/resources/skills/extension-creator/references/ui-components.md +35 -0
  60. package/out/runner.js +546 -450
  61. package/package.json +20 -33
  62. package/patches/{ai+5.0.179.patch → ai+5.0.195.patch} +9 -9
  63. package/out/renderer/assets/pierre-dark-DADY5eR0.js +0 -4
  64. package/out/renderer/assets/pierre-light-DUjirxKp.js +0 -4
  65. package/patches/@ai-sdk+deepseek+1.0.37.patch +0 -150
  66. 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"],
@@ -2446,6 +2431,19 @@ const getDefaultProjectSettings = (store, providerModels, baseDir, defaultAgentP
2446
2431
  contextSidebarSectionsHidden: []
2447
2432
  };
2448
2433
  };
2434
+ const DEFAULT_FETCH_TIMEOUT_MS = 12e4;
2435
+ const fetchWithTimeout = async (url, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) => {
2436
+ const controller = new AbortController();
2437
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2438
+ try {
2439
+ const response = await fetch(url, { signal: controller.signal });
2440
+ clearTimeout(timeoutId);
2441
+ return response;
2442
+ } catch (error) {
2443
+ clearTimeout(timeoutId);
2444
+ throw error;
2445
+ }
2446
+ };
2449
2447
  const filenamify = filenamifyImport.default;
2450
2448
  const getFilePathSuggestions = async (currentPath, directoriesOnly = false) => {
2451
2449
  try {
@@ -3235,6 +3233,20 @@ const scrapeWeb = async (url, timeout = 6e4, abortSignal, format = "markdown") =
3235
3233
  const scraper = new WebScraper();
3236
3234
  return await scraper.scrape(url, timeout, abortSignal, format);
3237
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());
3238
3250
  const THINKING_RESPONSE_STAR_TAG = "---\n► **THINKING**\n";
3239
3251
  const ANSWER_RESPONSE_START_TAG = "---\n► **ANSWER**\n";
3240
3252
  const extractPromptContextFromToolResult = (toolResult) => {
@@ -3471,8 +3483,8 @@ Include enough lines in each to uniquely match each set of lines that need to ch
3471
3483
  Do not use escape characters \\ in the string like \\n or \\" and others. Do not start the search term with a \\ character.`
3472
3484
  ),
3473
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'),
3474
- 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."),
3475
- 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.")
3476
3488
  }),
3477
3489
  execute: async (input, { toolCallId }) => {
3478
3490
  const { filePath, searchTerm, replacementText, isRegex, replaceAll } = input;
@@ -3562,9 +3574,9 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
3562
3574
  description: POWER_TOOL_DESCRIPTIONS[POWER_TOOL_FILE_READ],
3563
3575
  inputSchema: zod.z.object({
3564
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)."),
3565
- withLines: zod.z.boolean().optional().default(false).describe('Whether to return the file content with line numbers in format "lineNumber|content". Default: false.'),
3566
- lineOffset: zod.z.number().int().min(0).optional().default(0).describe("The starting line number (0-based) to begin reading from. Default: 0."),
3567
- 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.")
3568
3580
  }),
3569
3581
  execute: async (input, { toolCallId }) => {
3570
3582
  const { filePath, withLines, lineOffset, lineLimit } = input;
@@ -3728,8 +3740,8 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
3728
3740
  filePattern: zod.z.string().describe("A glob pattern specifying the files to search within (e.g., src/**/*.tsx, *.py)."),
3729
3741
  searchTerm: zod.z.string().describe("The regular expression to search for within the files."),
3730
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."),
3731
- caseSensitive: zod.z.boolean().optional().default(false).describe("Whether the search should be case sensitive. Default: false."),
3732
- 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.")
3733
3745
  }),
3734
3746
  execute: async (input, { toolCallId }) => {
3735
3747
  const { filePattern, searchTerm, contextLines, caseSensitive, maxResults } = input;
@@ -3896,7 +3908,7 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
3896
3908
  inputSchema: zod.z.object({
3897
3909
  command: zod.z.string().min(1).describe("The shell command to execute (e.g., ls -la, npm install)."),
3898
3910
  cwd: zod.z.string().optional().describe("The working directory for the command (relative to <WorkingDirectory>). Default: <WorkingDirectory>."),
3899
- 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.")
3900
3912
  }),
3901
3913
  execute: async (input, { toolCallId }) => {
3902
3914
  const { command, cwd, timeout } = input;
@@ -4081,7 +4093,7 @@ Timeout: ${timeout}ms`;
4081
4093
  description: POWER_TOOL_DESCRIPTIONS[POWER_TOOL_FETCH],
4082
4094
  inputSchema: zod.z.object({
4083
4095
  url: zod.z.string().describe("The URL to fetch."),
4084
- 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."),
4085
4097
  format: zod.z.enum(["markdown", "html", "raw"]).optional().default("markdown").describe(
4086
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).'
4087
4099
  )
@@ -4131,10 +4143,10 @@ Format: ${format}`;
4131
4143
  inputSchema: zod.z.object({
4132
4144
  query: zod.z.string().describe("Search query with Elasticsearch syntax. Use + for important terms."),
4133
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.'),
4134
- allowTests: zod.z.boolean().optional().default(false).describe("Allow test files in search results"),
4135
- exact: zod.z.boolean().optional().default(false).describe("Perform exact search without tokenization (case-insensitive)"),
4136
- maxResults: zod.z.number().optional().describe("Maximum number of results to return"),
4137
- 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"),
4138
4150
  language: zod.z.string().optional().describe("Limit search to files of a specific programming language")
4139
4151
  }),
4140
4152
  execute: async (input, { toolCallId }) => {
@@ -4246,7 +4258,7 @@ const createTodoToolset = (task, profile, promptContext) => {
4246
4258
  items: zod.z.array(
4247
4259
  zod.z.object({
4248
4260
  name: zod.z.string().describe("The name of the todo item."),
4249
- 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.")
4250
4262
  })
4251
4263
  ).describe("An array of todo items."),
4252
4264
  initialUserPrompt: zod.z.string().describe("The original user prompt that initiated the task.")
@@ -4300,7 +4312,7 @@ Items: ${JSON.stringify(items)}`;
4300
4312
  description: TODO_TOOL_DESCRIPTIONS[TODO_TOOL_UPDATE_ITEM_COMPLETION],
4301
4313
  inputSchema: zod.z.object({
4302
4314
  name: zod.z.string().describe("The name of the todo item to update."),
4303
- 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.")
4304
4316
  }),
4305
4317
  execute: async (input, { toolCallId }) => {
4306
4318
  const { name, completed } = input;
@@ -4365,8 +4377,8 @@ const createTasksToolset = (settings, task, profile, promptContext) => {
4365
4377
  const listTasksTool = ai.tool({
4366
4378
  description: TASKS_TOOL_DESCRIPTIONS[TASKS_TOOL_LIST_TASKS],
4367
4379
  inputSchema: zod.z.object({
4368
- offset: zod.z.number().optional().describe("The number of tasks to skip (for pagination)"),
4369
- 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"),
4370
4382
  state: zod.z.string().optional().describe("Filter tasks by state (e.g., TODO, IN_PROGRESS, DONE)")
4371
4383
  }),
4372
4384
  execute: async (input, { toolCallId }) => {
@@ -4530,7 +4542,7 @@ const createTasksToolset = (settings, task, profile, promptContext) => {
4530
4542
  ),
4531
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."),
4532
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."),
4533
- 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.")
4534
4546
  }),
4535
4547
  execute: async (input, { toolCallId }) => {
4536
4548
  const { prompt, name, agentProfileId, modelId, mode = "agent", asSubtask = false, autonomyMode, worktree, executeAndWait, executeInBackground } = input;
@@ -4694,7 +4706,7 @@ Prompt: ${prompt}${executeAndWait ? "\nWait for completion: true" : "\nRun in ba
4694
4706
  inputSchema: zod.z.object({
4695
4707
  taskId: zod.z.string().describe("The ID of the task to search within"),
4696
4708
  query: zod.z.string().describe("Search query with Elasticsearch syntax. Use + for important terms."),
4697
- 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")
4698
4710
  }),
4699
4711
  execute: async (input, { toolCallId }) => {
4700
4712
  const { taskId, query, maxTokens } = input;
@@ -4771,7 +4783,7 @@ const createSearchParentTaskTool = (task, promptContext) => {
4771
4783
  description: TASKS_TOOL_DESCRIPTIONS[TASKS_TOOL_SEARCH_PARENT_TASK],
4772
4784
  inputSchema: zod.z.object({
4773
4785
  query: zod.z.string().describe("Search query with Elasticsearch syntax. Use + for important terms."),
4774
- 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")
4775
4787
  }),
4776
4788
  execute: async ({ query, maxTokens }, { toolCallId }) => {
4777
4789
  const parentTaskId = task.task.parentId;
@@ -4861,7 +4873,7 @@ const createAiderToolset = (task, profile, promptContext) => {
4861
4873
  description: AIDER_TOOL_DESCRIPTIONS[AIDER_TOOL_ADD_CONTEXT_FILES],
4862
4874
  inputSchema: zod.z.object({
4863
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).'),
4864
- 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.")
4865
4877
  }),
4866
4878
  execute: async (input, { toolCallId }) => {
4867
4879
  const { paths, readOnly = false } = input;
@@ -5090,7 +5102,7 @@ const createMemoryToolset = (task, profile, memoryManager, promptContext) => {
5090
5102
  description: MEMORY_TOOL_DESCRIPTIONS[MEMORY_TOOL_RETRIEVE],
5091
5103
  inputSchema: zod.z.object({
5092
5104
  query: zod.z.string().describe("The search query to find relevant memories"),
5093
- 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)")
5094
5106
  }),
5095
5107
  execute: async (input, { toolCallId }) => {
5096
5108
  const { query, limit } = input;
@@ -5157,7 +5169,7 @@ Timestamp: ${new Date(memory.timestamp).toLocaleString()}`;
5157
5169
  description: MEMORY_TOOL_DESCRIPTIONS[MEMORY_TOOL_LIST],
5158
5170
  inputSchema: zod.z.object({
5159
5171
  type: zod.z.enum(["task", "user-preference", "code-pattern"]).optional().describe("Filter by memory type"),
5160
- 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)")
5161
5173
  }),
5162
5174
  execute: async (input, { toolCallId }) => {
5163
5175
  const { type, limit } = input;
@@ -6581,14 +6593,16 @@ ${file.content}</content-with-line-numbers>
6581
6593
  logger.debug("Adding file list for non-image files", {
6582
6594
  files: nonImageFiles.map((f) => f.path)
6583
6595
  });
6596
+ const hasReadOnlyFiles = nonImageFiles.some((file) => file.readOnly);
6584
6597
  const fileList = nonImageFiles.map((file) => {
6585
- return `- ${file.path}`;
6598
+ return `- ${file.path}${file.readOnly ? " (read-only)" : ""}`;
6586
6599
  }).join("\n");
6600
+ const readOnlyNote = hasReadOnlyFiles ? "\n\nNote: Files marked as read-only must NOT be modified. Use them only for understanding context." : "";
6587
6601
  messages.push({
6588
6602
  role: "user",
6589
6603
  content: `The following files are currently in the working context:
6590
6604
 
6591
- ${fileList}`
6605
+ ${fileList}${readOnlyNote}`
6592
6606
  });
6593
6607
  messages.push({
6594
6608
  role: "assistant",
@@ -6686,7 +6700,7 @@ ${fileList}`
6686
6700
  const providerTools = await this.modelManager.getProviderTools(provider, model);
6687
6701
  Object.assign(toolSet, providerTools);
6688
6702
  if (this.extensionManager.isInitialized() && profile.useExtensionTools !== false) {
6689
- const extensionTools = this.extensionManager.createExtensionToolset(task, mode, profile, toolSet, abortSignal);
6703
+ const extensionTools = this.extensionManager.createExtensionToolset(task, mode, profile, toolSet, approvalManager, abortSignal);
6690
6704
  Object.assign(toolSet, extensionTools);
6691
6705
  }
6692
6706
  return this.wrapToolsWithHooks(task, profile, toolSet, abortSignal, promptContext);
@@ -6702,7 +6716,7 @@ ${fileList}`
6702
6716
  task.addToolMessage(options.toolCallId, serverName, messageToolName, effectiveInput, void 0, void 0, promptContext);
6703
6717
  const toolCalledExtensionResult = await this.extensionManager.dispatchEvent(
6704
6718
  "onToolCalled",
6705
- { toolName, agentProfile: profile, input: effectiveInput, abortSignal: options.abortSignal || abortSignal },
6719
+ { toolCallId: options.toolCallId, toolName, agentProfile: profile, input: effectiveInput, abortSignal: options.abortSignal || abortSignal },
6706
6720
  task.project,
6707
6721
  task
6708
6722
  );
@@ -6719,7 +6733,7 @@ ${fileList}`
6719
6733
  }
6720
6734
  const toolFinishedExtensionResult = await this.extensionManager.dispatchEvent(
6721
6735
  "onToolFinished",
6722
- { toolName, agentProfile: profile, input: effectiveInput, output: result },
6736
+ { toolCallId: options.toolCallId, toolName, agentProfile: profile, input: effectiveInput, output: result },
6723
6737
  task.project,
6724
6738
  task
6725
6739
  );
@@ -7838,7 +7852,7 @@ ${fileList}`
7838
7852
  const processToolResult = (toolResult) => {
7839
7853
  const [serverName, toolName] = extractServerNameToolName(toolResult.toolName);
7840
7854
  const toolPromptContext = extractPromptContextFromToolResult(toolResult.output) ?? promptContext;
7841
- 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);
7842
7856
  };
7843
7857
  for (let i = 0; i < content.length; i++) {
7844
7858
  let part = content[i];
@@ -8324,6 +8338,7 @@ class AgentProfileManager {
8324
8338
  maxIterations: loadedProfile.maxIterations ?? DEFAULT_AGENT_PROFILE.maxIterations,
8325
8339
  minTimeBetweenToolCalls: loadedProfile.minTimeBetweenToolCalls ?? DEFAULT_AGENT_PROFILE.minTimeBetweenToolCalls,
8326
8340
  enabledServers: loadedProfile.enabledServers ?? [],
8341
+ disabledExtensionTools: loadedProfile.disabledExtensionTools ?? [],
8327
8342
  customInstructions: loadedProfile.customInstructions ?? "",
8328
8343
  toolApprovals: {
8329
8344
  ...loadedProfile.toolApprovals
@@ -9293,11 +9308,22 @@ const MergeWorktreeToMainSchema = zod.z.object({
9293
9308
  targetBranch: zod.z.string().optional(),
9294
9309
  commitMessage: zod.z.string().optional()
9295
9310
  });
9296
- const MergeAndSwitchToLocalSchema = zod.z.object({
9311
+ const SwitchToLocalWorkingModeSchema = zod.z.object({
9297
9312
  projectDir: zod.z.string().min(1, "Project directory is required"),
9298
9313
  taskId: zod.z.string().min(1, "Task id is required"),
9314
+ mergeBeforeSwitch: zod.z.boolean().optional(),
9299
9315
  targetBranch: zod.z.string().optional()
9300
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
+ });
9301
9327
  const ApplyUncommittedChangesSchema = zod.z.object({
9302
9328
  projectDir: zod.z.string().min(1, "Project directory is required"),
9303
9329
  taskId: zod.z.string().min(1, "Task id is required"),
@@ -9748,15 +9774,39 @@ class ProjectApi extends BaseApi {
9748
9774
  })
9749
9775
  );
9750
9776
  router.post(
9751
- "/project/worktree/merge-and-switch-to-local",
9777
+ "/project/switch-to-local-working-mode",
9752
9778
  this.handleRequest(async (req, res) => {
9753
- const parsed = this.validateRequest(MergeAndSwitchToLocalSchema, req.body, res);
9779
+ const parsed = this.validateRequest(SwitchToLocalWorkingModeSchema, req.body, res);
9754
9780
  if (!parsed) {
9755
9781
  return;
9756
9782
  }
9757
- const { projectDir, taskId, targetBranch } = parsed;
9758
- await this.eventsHandler.mergeAndSwitchToLocal(projectDir, taskId, targetBranch);
9759
- 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);
9760
9810
  })
9761
9811
  );
9762
9812
  router.post(
@@ -10700,6 +10750,9 @@ class TerminalApi extends BaseApi {
10700
10750
  const GetInstalledExtensionsSchema = zod.z.object({
10701
10751
  projectDir: zod.z.string().optional()
10702
10752
  });
10753
+ const GetExtensionToolsInfoSchema = zod.z.object({
10754
+ projectDir: zod.z.string().optional()
10755
+ });
10703
10756
  const GetAvailableExtensionsSchema = zod.z.object({
10704
10757
  repositories: zod.z.string().optional(),
10705
10758
  // Comma-separated URLs
@@ -10738,6 +10791,18 @@ class ExtensionsApi extends BaseApi {
10738
10791
  res.status(200).json(extensions);
10739
10792
  })
10740
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
+ );
10741
10806
  router.get(
10742
10807
  "/extensions/available",
10743
10808
  this.handleRequest(async (req, res) => {
@@ -10830,6 +10895,18 @@ class ExtensionsApi extends BaseApi {
10830
10895
  res.status(200).json(result);
10831
10896
  })
10832
10897
  );
10898
+ router.post(
10899
+ "/extensions/load-library",
10900
+ this.handleRequest(async (req, res) => {
10901
+ const { librarySpec } = req.body;
10902
+ if (!librarySpec) {
10903
+ res.status(400).json({ message: "librarySpec is required" });
10904
+ return;
10905
+ }
10906
+ const source = await this.eventsHandler.loadExtensionLibrary(librarySpec);
10907
+ res.status(200).type("application/javascript").send(source);
10908
+ })
10909
+ );
10833
10910
  router.get(
10834
10911
  "/extensions/config-component",
10835
10912
  this.handleRequest(async (req, res) => {
@@ -12604,6 +12681,7 @@ ${JSON.stringify(part.output.value)}`
12604
12681
  toolMessage.response = JSON.stringify(part.output.value);
12605
12682
  toolMessage.usageReport = message.usageReport || toolMessage.usageReport;
12606
12683
  toolMessage.promptContext = promptContext;
12684
+ toolMessage.finished = true;
12607
12685
  }
12608
12686
  if (serverName === AIDER_TOOL_GROUP_NAME && toolName === AIDER_TOOL_RUN_PROMPT && part.output?.value && typeof part.output.value === "object" && "responses" in part.output.value) {
12609
12687
  const responses = part.output.value.responses;
@@ -12704,6 +12782,7 @@ ${JSON.stringify(part.output.value)}`
12704
12782
  const toolMessage2 = messagesData.find((message2) => message2.type === "tool" && message2.id === subPart.toolCallId);
12705
12783
  if (toolMessage2) {
12706
12784
  toolMessage2.response = JSON.stringify(subPart.output.value);
12785
+ toolMessage2.finished = true;
12707
12786
  }
12708
12787
  }
12709
12788
  }
@@ -12783,6 +12862,7 @@ ${JSON.stringify(part.output.value)}`
12783
12862
  if (toolMessage) {
12784
12863
  toolMessage.response = JSON.stringify(toolResultData.output);
12785
12864
  toolMessage.usageReport = message.usageReport || toolMessage.usageReport;
12865
+ toolMessage.finished = true;
12786
12866
  const promptContext = extractPromptContextFromToolResult(toolResultData.output);
12787
12867
  if (promptContext) {
12788
12868
  toolMessage.promptContext = promptContext;
@@ -15405,6 +15485,12 @@ Git output: ${err.stderr || err.stdout || err.message}`
15405
15485
  await this.pruneDeleted(projectDir);
15406
15486
  }
15407
15487
  }
15488
+ var CompactionLevel = /* @__PURE__ */ ((CompactionLevel2) => {
15489
+ CompactionLevel2[CompactionLevel2["One"] = 1] = "One";
15490
+ CompactionLevel2[CompactionLevel2["Two"] = 2] = "Two";
15491
+ CompactionLevel2[CompactionLevel2["Three"] = 3] = "Three";
15492
+ return CompactionLevel2;
15493
+ })(CompactionLevel || {});
15408
15494
  const extractFilePath = (args) => {
15409
15495
  if (typeof args === "object" && args !== null && "filePath" in args) {
15410
15496
  return args.filePath;
@@ -15545,8 +15631,9 @@ const mergeConsecutiveAssistantMessages = (messages) => {
15545
15631
  return true;
15546
15632
  });
15547
15633
  };
15548
- const truncateNonPowerToolResults = async (messages, protectedMessageCount = 10) => {
15634
+ const truncateNonPowerToolResults = async (messages, protectedMessageCount = 10, compactionLevel = 1) => {
15549
15635
  const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
15636
+ const redactionMessage = "Result redacted due to compaction, run again if needed.";
15550
15637
  for (let i = 0; i < protectedStart; i++) {
15551
15638
  const msg = messages[i];
15552
15639
  if (msg.role !== "tool") {
@@ -15561,6 +15648,20 @@ const truncateNonPowerToolResults = async (messages, protectedMessageCount = 10)
15561
15648
  if (isPowerTool(serverName)) {
15562
15649
  continue;
15563
15650
  }
15651
+ if (compactionLevel >= 3) {
15652
+ if (part.output.type === "text" || part.output.type === "error-text") {
15653
+ part.output = {
15654
+ type: part.output.type,
15655
+ value: redactionMessage
15656
+ };
15657
+ } else {
15658
+ part.output = {
15659
+ type: "text",
15660
+ value: redactionMessage
15661
+ };
15662
+ }
15663
+ continue;
15664
+ }
15564
15665
  if (part.output.type !== "text" && part.output.type !== "error-text") {
15565
15666
  continue;
15566
15667
  }
@@ -15568,11 +15669,14 @@ const truncateNonPowerToolResults = async (messages, protectedMessageCount = 10)
15568
15669
  if (!outputText) {
15569
15670
  continue;
15570
15671
  }
15672
+ const maxLines = compactionLevel >= 2 ? 10 : 20;
15673
+ const maxSizeKB = compactionLevel >= 2 ? 1 : 2;
15674
+ const maxTokens = compactionLevel >= 2 ? 1e3 : 2e3;
15571
15675
  const truncated = await truncateToolResult(
15572
15676
  outputText,
15573
- 20,
15574
- 2,
15575
- 2e3,
15677
+ maxLines,
15678
+ maxSizeKB,
15679
+ maxTokens,
15576
15680
  false,
15577
15681
  "Output truncated due to compaction, re-execute the tool if full output is needed."
15578
15682
  );
@@ -15586,17 +15690,17 @@ const truncateNonPowerToolResults = async (messages, protectedMessageCount = 10)
15586
15690
  }
15587
15691
  return messages;
15588
15692
  };
15589
- const smartCompactMessages = async (messages, protectedMessageCount = 10) => {
15693
+ const smartCompactMessages = async (messages, protectedMessageCount = 10, compactionLevel = 1) => {
15590
15694
  let result = cloneMessages(messages);
15591
15695
  result = removeErroredTools(result, protectedMessageCount);
15592
15696
  result = collapseFileEdits(result, protectedMessageCount);
15593
15697
  result = removeStaleFileReads(result, protectedMessageCount);
15594
- result = compactFileReads(result, protectedMessageCount);
15595
- result = removeObsoleteSearches(result, protectedMessageCount);
15596
- result = compactSemanticSearches(result, protectedMessageCount);
15597
- result = deduplicateBash(result, protectedMessageCount);
15698
+ result = compactFileReads(result, protectedMessageCount, compactionLevel);
15699
+ result = removeObsoleteSearches(result, protectedMessageCount, compactionLevel);
15700
+ result = compactSemanticSearches(result, protectedMessageCount, compactionLevel);
15701
+ result = compactBashOutputs(result, protectedMessageCount, compactionLevel);
15598
15702
  result = redactFetchOutputs(result, protectedMessageCount);
15599
- result = await truncateNonPowerToolResults(result, protectedMessageCount);
15703
+ result = await truncateNonPowerToolResults(result, protectedMessageCount, compactionLevel);
15600
15704
  result = mergeConsecutiveAssistantMessages(result);
15601
15705
  return result;
15602
15706
  };
@@ -15800,8 +15904,9 @@ const removeStaleFileReads = (messages, protectedMessageCount = 10) => {
15800
15904
  }
15801
15905
  return messages;
15802
15906
  };
15803
- const compactFileReads = (messages, protectedMessageCount = 10) => {
15907
+ const compactFileReads = (messages, protectedMessageCount = 10, compactionLevel = 1) => {
15804
15908
  const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
15909
+ const maxLines = compactionLevel === 3 ? 0 : compactionLevel === 2 ? 20 : 50;
15805
15910
  for (let i = 0; i < protectedStart; i++) {
15806
15911
  if (i >= messages.length) {
15807
15912
  break;
@@ -15822,18 +15927,25 @@ const compactFileReads = (messages, protectedMessageCount = 10) => {
15822
15927
  if (part.output.type !== "text") {
15823
15928
  continue;
15824
15929
  }
15930
+ if (compactionLevel === 3) {
15931
+ part.output = {
15932
+ type: "text",
15933
+ value: "<result redacted due to compaction, read the file again if content is needed>"
15934
+ };
15935
+ continue;
15936
+ }
15825
15937
  const lines = part.output.value.split("\n");
15826
- if (lines.length > 50) {
15938
+ if (lines.length > maxLines) {
15827
15939
  part.output = {
15828
15940
  type: "text",
15829
- value: lines.slice(0, 50).join("\n") + "\n<truncated due to compaction, read the file again if full content is needed>"
15941
+ value: lines.slice(0, maxLines).join("\n") + "\n<truncated due to compaction, read the file again if full content is needed>"
15830
15942
  };
15831
15943
  }
15832
15944
  }
15833
15945
  }
15834
15946
  return messages;
15835
15947
  };
15836
- const removeObsoleteSearches = (messages, protectedMessageCount = 10) => {
15948
+ const removeObsoleteSearches = (messages, protectedMessageCount = 10, compactionLevel = 1) => {
15837
15949
  const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
15838
15950
  const fileModificationPositions = [];
15839
15951
  for (let i = 0; i < messages.length; i++) {
@@ -15864,7 +15976,7 @@ const removeObsoleteSearches = (messages, protectedMessageCount = 10) => {
15864
15976
  }
15865
15977
  }
15866
15978
  const hasFileModifications = fileModificationPositions.length > 0;
15867
- if (!hasFileModifications) {
15979
+ if (!hasFileModifications && compactionLevel < 3) {
15868
15980
  return messages;
15869
15981
  }
15870
15982
  for (let i = protectedStart - 1; i >= 0; i--) {
@@ -15886,8 +15998,8 @@ const removeObsoleteSearches = (messages, protectedMessageCount = 10) => {
15886
15998
  if (toolName !== POWER_TOOL_GLOB && toolName !== POWER_TOOL_GREP) {
15887
15999
  continue;
15888
16000
  }
15889
- const hasLaterModification = fileModificationPositions.some((pos) => pos > i);
15890
- if (hasLaterModification) {
16001
+ const shouldRemove = compactionLevel >= 3 || fileModificationPositions.some((pos) => pos > i);
16002
+ if (shouldRemove) {
15891
16003
  removeToolCallFromAssistant(messages, part.toolCallId);
15892
16004
  removeToolResult(messages, part.toolCallId);
15893
16005
  }
@@ -15895,7 +16007,7 @@ const removeObsoleteSearches = (messages, protectedMessageCount = 10) => {
15895
16007
  }
15896
16008
  return messages;
15897
16009
  };
15898
- const compactSemanticSearches = (messages, protectedMessageCount = 10) => {
16010
+ const compactSemanticSearches = (messages, protectedMessageCount = 10, compactionLevel = 1) => {
15899
16011
  const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
15900
16012
  const searchIndices = [];
15901
16013
  for (let i = 0; i < protectedStart; i++) {
@@ -15918,7 +16030,14 @@ const compactSemanticSearches = (messages, protectedMessageCount = 10) => {
15918
16030
  searchIndices.push({ messageIndex: i, partIndex: j, toolCallId: part.toolCallId });
15919
16031
  }
15920
16032
  }
15921
- if (searchIndices.length <= 1) {
16033
+ if (searchIndices.length <= 1 && compactionLevel < 3) {
16034
+ return messages;
16035
+ }
16036
+ if (compactionLevel >= 3) {
16037
+ for (const search2 of searchIndices) {
16038
+ removeToolCallFromAssistant(messages, search2.toolCallId);
16039
+ removeToolResult(messages, search2.toolCallId);
16040
+ }
15922
16041
  return messages;
15923
16042
  }
15924
16043
  const toRemove = searchIndices.slice(0, -1);
@@ -15927,6 +16046,7 @@ const compactSemanticSearches = (messages, protectedMessageCount = 10) => {
15927
16046
  removeToolCallFromAssistant(messages, search2.toolCallId);
15928
16047
  removeToolResult(messages, search2.toolCallId);
15929
16048
  }
16049
+ const maxLines = compactionLevel === 2 ? 20 : 50;
15930
16050
  const keptToolIdx = messages.findIndex((m) => m.role === "tool" && m.content.some((p) => p.type === "tool-result" && p.toolCallId === toKeep.toolCallId));
15931
16051
  if (keptToolIdx !== -1) {
15932
16052
  const keptMsg = messages[keptToolIdx];
@@ -15934,18 +16054,19 @@ const compactSemanticSearches = (messages, protectedMessageCount = 10) => {
15934
16054
  const keptPart = keptMsg.content[keptPartIdx];
15935
16055
  if (keptPart && keptPart.output.type === "text") {
15936
16056
  const lines = keptPart.output.value.split("\n");
15937
- if (lines.length > 50) {
16057
+ if (lines.length > maxLines) {
15938
16058
  keptPart.output = {
15939
16059
  type: "text",
15940
- value: lines.slice(0, 50).join("\n") + "\n<truncated due to compaction, run again if full output is needed>"
16060
+ value: lines.slice(0, maxLines).join("\n") + "\n<truncated due to compaction, run again if full output is needed>"
15941
16061
  };
15942
16062
  }
15943
16063
  }
15944
16064
  }
15945
16065
  return messages;
15946
16066
  };
15947
- const deduplicateBash = (messages, protectedMessageCount = 10) => {
16067
+ const compactBashOutputs = (messages, protectedMessageCount = 10, compactionLevel = 1) => {
15948
16068
  const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
16069
+ logger.info(`Compacting bash outputs at level ${compactionLevel}`);
15949
16070
  const bashCommands = /* @__PURE__ */ new Map();
15950
16071
  for (let i = 0; i < protectedStart; i++) {
15951
16072
  const msg = messages[i];
@@ -16004,6 +16125,30 @@ const deduplicateBash = (messages, protectedMessageCount = 10) => {
16004
16125
  if (!isPowerTool(serverName) || toolName !== POWER_TOOL_BASH) {
16005
16126
  continue;
16006
16127
  }
16128
+ if (compactionLevel >= 3) {
16129
+ part.output = {
16130
+ type: "text",
16131
+ value: "<result redacted due to compaction, run again if needed>"
16132
+ };
16133
+ continue;
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
+ }
16007
16152
  if (part.output.type !== "text") {
16008
16153
  continue;
16009
16154
  }
@@ -16014,11 +16159,12 @@ const deduplicateBash = (messages, protectedMessageCount = 10) => {
16014
16159
  const stderr = typeof parsed.stderr === "string" ? parsed.stderr : "";
16015
16160
  const redactionMessage = "<output redacted due to compaction, run again if output is needed>";
16016
16161
  let modified = false;
16017
- if (stdout.length > 30) {
16162
+ const redactionThreshold = compactionLevel >= 2 ? 0 : 30;
16163
+ if (stdout.length > redactionThreshold) {
16018
16164
  parsed.stdout = redactionMessage;
16019
16165
  modified = true;
16020
16166
  }
16021
- if (stderr.length > 30) {
16167
+ if (stderr.length > redactionThreshold) {
16022
16168
  parsed.stderr = redactionMessage;
16023
16169
  modified = true;
16024
16170
  }
@@ -16148,6 +16294,8 @@ class Task {
16148
16294
  tokensInfo;
16149
16295
  queuedPrompts = [];
16150
16296
  isCompacting = false;
16297
+ lastSmartCompactionMessageCount = 0;
16298
+ smartCompactionLevel = CompactionLevel.One;
16151
16299
  taskDataPath;
16152
16300
  contextManager;
16153
16301
  agent;
@@ -16741,6 +16889,7 @@ class Task {
16741
16889
  provider: this.task.provider || profile.provider,
16742
16890
  model: this.task.model || profile.model
16743
16891
  });
16892
+ this.smartCompactionLevel = 1;
16744
16893
  const agentMessages = await this.agent.runAgent(
16745
16894
  this,
16746
16895
  profile,
@@ -18126,7 +18275,9 @@ ${contentText}</agent-response>`;
18126
18275
  const removedMessages = this.contextManager.removeMessagesUpToUserMessage(messageId);
18127
18276
  const originalUserMessage = removedMessages[0];
18128
18277
  if (!originalUserMessage || originalUserMessage.role !== MessageRole.User) {
18129
- logger.warn("Could not find the specified user message to redo.", { messageId });
18278
+ logger.warn("Could not find the specified user message to redo.", {
18279
+ messageId
18280
+ });
18130
18281
  return;
18131
18282
  }
18132
18283
  const originalText = extractTextContent(originalUserMessage.content);
@@ -18243,7 +18394,24 @@ ${contentText}</agent-response>`;
18243
18394
  return [];
18244
18395
  }
18245
18396
  await this.contextManager.backupContext();
18246
- const compactedMessages = await smartCompactMessages(contextMessages);
18397
+ const messagesSinceLastCompaction = contextMessages.length - this.lastSmartCompactionMessageCount;
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
+ });
18403
+ this.smartCompactionLevel = Math.min(this.smartCompactionLevel + 1, CompactionLevel.Three);
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);
18409
+ }
18410
+ logger.debug("Current compaction level:", {
18411
+ smartCompactionLevel: this.smartCompactionLevel
18412
+ });
18413
+ const compactedMessages = await smartCompactMessages(contextMessages, 10, this.smartCompactionLevel);
18414
+ this.lastSmartCompactionMessageCount = compactedMessages.length;
18247
18415
  this.contextManager.setContextMessages(compactedMessages);
18248
18416
  await this.contextManager.loadMessages(compactedMessages, false);
18249
18417
  await this.updateContextInfo();
@@ -18257,7 +18425,7 @@ ${contentText}</agent-response>`;
18257
18425
  if (!contextMessages) {
18258
18426
  contextMessages = await this.contextManager.getContextMessages();
18259
18427
  }
18260
- const userMessage = contextMessages[0];
18428
+ const userMessage = contextMessages.find((msg) => msg.role === MessageRole.User);
18261
18429
  if (!userMessage) {
18262
18430
  this.addLogMessage("warning", "No conversation to compact.");
18263
18431
  return;
@@ -18937,46 +19105,96 @@ Only answer with the commit message, nothing else.`,
18937
19105
  await this.sendUpdatedFilesUpdated();
18938
19106
  await this.sendWorktreeIntegrationStatusUpdated();
18939
19107
  }
18940
- async mergeAndSwitchToLocal(targetBranch) {
18941
- if (!this.task.worktree) {
19108
+ async switchToLocalWorkingMode(options) {
19109
+ if (options?.mergeBeforeSwitch && !this.task.worktree) {
18942
19110
  throw new Error("No worktree exists for this task");
18943
19111
  }
18944
- logger.info("Merging worktree and switching to local mode", {
19112
+ logger.info("Switching to local working mode", {
18945
19113
  baseDir: this.project.baseDir,
18946
- taskId: this.taskId
19114
+ taskId: this.taskId,
19115
+ mergeBeforeSwitch: options?.mergeBeforeSwitch ?? false
18947
19116
  });
18948
19117
  await this.waitForCurrentPromptToFinish();
18949
- try {
18950
- const effectiveTargetBranch = targetBranch || await this.worktreeManager.getProjectMainBranch(this.project.baseDir);
18951
- this.addLogMessage("loading", `Merging worktree to ${effectiveTargetBranch} branch and switching to local mode...`);
18952
- const settings = this.store.getSettings();
18953
- const symlinkFolders = settings.taskSettings.worktreeSymlinkFolders || [];
18954
- const mergeState = await this.worktreeManager.mergeWorktreeToMainWithUncommitted(
18955
- this.project.baseDir,
18956
- this.task.id,
18957
- this.task.worktree.path,
18958
- false,
18959
- this.task.name || `Task ${this.taskId} changes`,
18960
- targetBranch,
18961
- symlinkFolders
18962
- );
18963
- await this.saveTask({ lastMergeState: mergeState });
18964
- this.addLogMessage("info", `Successfully merged worktree to ${effectiveTargetBranch} branch`, true);
18965
- await this.updateTask({ workingMode: "local" });
18966
- } catch (error) {
18967
- logger.error("Failed to merge worktree and switch to local:", { error });
18968
- 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"));
18969
- this.addLogMessage(
18970
- "error",
18971
- isConflict ? "worktree.mergeConflicts" : error instanceof GitError ? error.getErrorDetails() : `Failed to merge worktree: ${error instanceof Error ? error.message : String(error)}`,
18972
- true,
18973
- void 0,
18974
- isConflict ? ["rebase-worktree"] : void 0
18975
- );
18976
- await this.sendUpdatedFilesUpdated();
18977
- await this.sendWorktreeIntegrationStatusUpdated();
18978
- 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
+ }
18979
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);
18980
19198
  }
18981
19199
  async applyUncommittedChanges(targetBranch) {
18982
19200
  if (!this.task.worktree) {
@@ -19166,7 +19384,13 @@ ${diff}
19166
19384
  if (success) {
19167
19385
  const newHead = await this.worktreeManager.getHeadCommit(this.task.worktree.path);
19168
19386
  if (newHead) {
19169
- await this.saveTask({ worktree: { ...this.task.worktree, baseCommit: newHead, baseBranch: effectiveFromBranch } });
19387
+ await this.saveTask({
19388
+ worktree: {
19389
+ ...this.task.worktree,
19390
+ baseCommit: newHead,
19391
+ baseBranch: effectiveFromBranch
19392
+ }
19393
+ });
19170
19394
  }
19171
19395
  this.addLogMessage("info", "Worktree rebased successfully", true);
19172
19396
  return;
@@ -20782,132 +21006,6 @@ const anthropicCompatibleProviderStrategy = {
20782
21006
  // Cache control
20783
21007
  getCacheControl: getAnthropicCacheControl
20784
21008
  };
20785
- const loadAuggieSdk = async () => import("@augmentcode/auggie-sdk");
20786
- const AUGGIE_MODELS = [
20787
- {
20788
- id: "claude-haiku-4-5",
20789
- maxInputTokens: 2e5,
20790
- maxOutputTokensLimit: 8192
20791
- },
20792
- {
20793
- id: "claude-sonnet-4",
20794
- maxInputTokens: 2e5,
20795
- maxOutputTokensLimit: 16384
20796
- },
20797
- {
20798
- id: "claude-sonnet-4-5",
20799
- maxInputTokens: 2e5,
20800
- maxOutputTokensLimit: 16384
20801
- },
20802
- {
20803
- id: "claude-opus-4-5",
20804
- maxInputTokens: 2e5,
20805
- maxOutputTokensLimit: 32768
20806
- },
20807
- {
20808
- id: "claude-opus-4-6",
20809
- maxInputTokens: 2e5,
20810
- maxOutputTokensLimit: 32768
20811
- },
20812
- {
20813
- id: "claude-opus-4-7",
20814
- maxInputTokens: 2e5,
20815
- maxOutputTokensLimit: 32768
20816
- },
20817
- { id: "gpt-5-1", maxInputTokens: 2e5, maxOutputTokensLimit: 16384 },
20818
- { id: "gpt-5-2", maxInputTokens: 2e5, maxOutputTokensLimit: 16384 },
20819
- { id: "gpt-5-4", maxInputTokens: 2e5, maxOutputTokensLimit: 16384 }
20820
- ];
20821
- const loadAuggieModels = async (profile, _settings) => {
20822
- if (!isAuggieProvider(profile.provider)) {
20823
- return {
20824
- models: [],
20825
- success: false
20826
- };
20827
- }
20828
- const models = AUGGIE_MODELS.map((model) => ({
20829
- id: model.id,
20830
- providerId: profile.id,
20831
- maxInputTokens: model.maxInputTokens,
20832
- maxOutputTokensLimit: model.maxOutputTokensLimit
20833
- }));
20834
- logger.info(`Loaded ${models.length} Auggie models for profile ${profile.id}`);
20835
- return { models, success: true };
20836
- };
20837
- const hasAuggieEnvVars = (settings) => {
20838
- if (getEffectiveEnvironmentVariable("AUGMENT_API_TOKEN", settings, void 0)?.value && getEffectiveEnvironmentVariable("AUGMENT_API_URL", settings, void 0)?.value) {
20839
- return true;
20840
- }
20841
- const auggieSessionFile = path.join(os.homedir(), ".augment", "session.json");
20842
- if (findExecutableInPath("auggie") && fs$1.existsSync(auggieSessionFile)) {
20843
- return true;
20844
- }
20845
- return false;
20846
- };
20847
- const getAuggieAiderMapping = (provider, modelId) => {
20848
- const auggieProvider = provider.provider;
20849
- const envVars = {};
20850
- if (auggieProvider.apiKey) {
20851
- envVars.AUGMENT_API_TOKEN = auggieProvider.apiKey;
20852
- }
20853
- if (auggieProvider.apiUrl) {
20854
- envVars.AUGMENT_API_URL = auggieProvider.apiUrl;
20855
- }
20856
- return {
20857
- modelName: `auggie/${modelId}`,
20858
- environmentVariables: envVars
20859
- };
20860
- };
20861
- const createAuggieLlm = async (profile, model, settings, projectDir) => {
20862
- const provider = profile.provider;
20863
- let apiKey = provider.apiKey;
20864
- let apiUrl = provider.apiUrl;
20865
- if (!apiKey) {
20866
- const effectiveVar = getEffectiveEnvironmentVariable("AUGMENT_API_TOKEN", settings, projectDir);
20867
- if (effectiveVar) {
20868
- apiKey = effectiveVar.value;
20869
- logger.debug(`Loaded AUGMENT_API_TOKEN from ${effectiveVar.source}`);
20870
- }
20871
- }
20872
- if (!apiUrl) {
20873
- const effectiveUrlVar = getEffectiveEnvironmentVariable("AUGMENT_API_URL", settings, projectDir);
20874
- if (effectiveUrlVar) {
20875
- apiUrl = effectiveUrlVar.value;
20876
- logger.debug(`Loaded AUGMENT_API_URL from ${effectiveUrlVar.source}`);
20877
- }
20878
- }
20879
- if (apiKey && !apiUrl) {
20880
- throw new Error("API URL is required when API key is provided.");
20881
- }
20882
- const { AugmentLanguageModel, resolveAugmentCredentials } = await loadAuggieSdk();
20883
- const AugmentLanguageModelCtor = AugmentLanguageModel;
20884
- if (apiKey && apiUrl) {
20885
- return new AugmentLanguageModelCtor(model.id, {
20886
- apiKey,
20887
- apiUrl
20888
- });
20889
- } else {
20890
- const credentials = await resolveAugmentCredentials();
20891
- return new AugmentLanguageModelCtor(model.id, credentials);
20892
- }
20893
- };
20894
- const getAuggieUsageReport = (_task, _provider, model) => {
20895
- return {
20896
- model: model.id,
20897
- sentTokens: 0,
20898
- receivedTokens: 0,
20899
- messageCost: 0,
20900
- agentTotalCost: 0
20901
- };
20902
- };
20903
- const auggieProviderStrategy = {
20904
- createLlm: createAuggieLlm,
20905
- getUsageReport: getAuggieUsageReport,
20906
- loadModels: loadAuggieModels,
20907
- hasEnvVars: hasAuggieEnvVars,
20908
- getAiderMapping: getAuggieAiderMapping,
20909
- getModelInfo: getDefaultModelInfo
20910
- };
20911
21009
  const extractResourceNameFromEndpoint = (endpoint) => {
20912
21010
  try {
20913
21011
  const url = new URL(endpoint);
@@ -21333,199 +21431,6 @@ const cerebrasProviderStrategy = {
21333
21431
  getAiderMapping: getCerebrasAiderMapping,
21334
21432
  getModelInfo: getDefaultModelInfo
21335
21433
  };
21336
- const loadClaudeAgentSdkModels = async (profile, _settings) => {
21337
- if (!isClaudeAgentSdkProvider(profile.provider)) {
21338
- return {
21339
- models: [],
21340
- success: false
21341
- };
21342
- }
21343
- try {
21344
- const models = [
21345
- {
21346
- id: "haiku",
21347
- providerId: profile.id,
21348
- maxInputTokens: 2e5,
21349
- maxOutputTokensLimit: 64e3
21350
- },
21351
- {
21352
- id: "sonnet",
21353
- providerId: profile.id,
21354
- maxInputTokens: 2e5,
21355
- maxOutputTokens: 64e3
21356
- },
21357
- {
21358
- id: "opus",
21359
- providerId: profile.id,
21360
- maxInputTokens: 2e5,
21361
- maxOutputTokensLimit: 64e3
21362
- }
21363
- ];
21364
- logger.info(`Loaded ${models.length} Claude Agent SDK models for profile ${profile.id}`);
21365
- return { models, success: true };
21366
- } catch (error) {
21367
- const errorMsg = typeof error === "string" ? error : error instanceof Error ? error.message : "Unknown error loading Claude Agent SDK models";
21368
- logger.error("Error loading Claude Agent SDK models:", error);
21369
- return { models: [], success: false, error: errorMsg };
21370
- }
21371
- };
21372
- const hasClaudeAgentSdkEnvVars = () => {
21373
- return findExecutableInPath("claude") !== null;
21374
- };
21375
- const getClaudeAgentSdkAiderMapping = (_provider, modelId) => {
21376
- return {
21377
- environmentVariables: {},
21378
- modelName: `claude-agent-sdk/${modelId}`
21379
- };
21380
- };
21381
- const normalizeAiSdkToolSet = (toolSet) => {
21382
- const normalizedToolSet = {};
21383
- for (const [toolName, toolDefinition] of Object.entries(toolSet)) {
21384
- let normalizedTool = { ...toolDefinition };
21385
- const inputSchema = toolDefinition.inputSchema;
21386
- if (inputSchema && typeof inputSchema === "object" && "jsonSchema" in inputSchema) {
21387
- const jsonSchemaObj = inputSchema.jsonSchema;
21388
- const schema = typeof jsonSchemaObj === "function" ? jsonSchemaObj() : jsonSchemaObj;
21389
- const zodShape = jsonSchemaToZod.JSONSchemaToZod.convert(schema);
21390
- normalizedTool = {
21391
- ...toolDefinition,
21392
- inputSchema: zodShape
21393
- };
21394
- logger.debug(`Converted JSONSchema to Zod for tool: ${toolName}`);
21395
- }
21396
- if (toolName === `${AIDER_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${AIDER_TOOL_RUN_PROMPT}`) {
21397
- const originalExecute = normalizedTool.execute;
21398
- if (!originalExecute) {
21399
- normalizedToolSet[toolName] = normalizedTool;
21400
- continue;
21401
- }
21402
- normalizedToolSet[toolName] = {
21403
- ...normalizedTool,
21404
- execute: async (args, options) => {
21405
- const result = await originalExecute(args, options);
21406
- if (result && typeof result === "object") {
21407
- delete result.responses;
21408
- }
21409
- return result;
21410
- }
21411
- };
21412
- } else if (toolName === `${SUBAGENTS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${SUBAGENTS_TOOL_RUN_TASK}`) {
21413
- const originalExecute = normalizedTool.execute;
21414
- if (!originalExecute) {
21415
- normalizedToolSet[toolName] = normalizedTool;
21416
- continue;
21417
- }
21418
- normalizedToolSet[toolName] = {
21419
- ...normalizedTool,
21420
- execute: async (args, options) => {
21421
- const result = await originalExecute(args, options);
21422
- if (result && typeof result === "object") {
21423
- try {
21424
- const messages = result.messages;
21425
- if (Array.isArray(messages) && messages.length > 0) {
21426
- const lastMessage = messages[messages.length - 1];
21427
- return {
21428
- messages: [lastMessage],
21429
- promptContext: result.promptContext
21430
- };
21431
- }
21432
- } catch (error) {
21433
- logger.warn("Failed to optimize subagent result:", error);
21434
- }
21435
- }
21436
- return result;
21437
- }
21438
- };
21439
- } else {
21440
- normalizedToolSet[toolName] = normalizedTool;
21441
- }
21442
- }
21443
- return normalizedToolSet;
21444
- };
21445
- const createClaudeAgentSdkLlm = (_profile, model, _settings, _projectDir, toolSet, systemPrompt, providerMetadata) => {
21446
- const settings = {
21447
- env: {
21448
- // setting MAX_MCP_OUTPUT_TOKENS to a high number to avoid errors
21449
- MAX_MCP_OUTPUT_TOKENS: "9999999"
21450
- },
21451
- // for now only tools from AiderDesk are allowed
21452
- disallowedTools: [
21453
- "AskUserQuestion",
21454
- "Bash",
21455
- "TaskOutput",
21456
- "Edit",
21457
- "EnterPlanMode",
21458
- "ExitPlanMode",
21459
- "Glob",
21460
- "Grep",
21461
- "KillShell",
21462
- "MCPSearch",
21463
- "NotebookEdit",
21464
- "Read",
21465
- "Skill",
21466
- "Task",
21467
- "TaskCreate",
21468
- "TaskGet",
21469
- "TaskList",
21470
- "TaskUpdate",
21471
- "TodoWrite",
21472
- "WebFetch",
21473
- "WebSearch",
21474
- "Write",
21475
- "LSP"
21476
- ]
21477
- };
21478
- if (providerMetadata && typeof providerMetadata === "object" && "claude-code" in providerMetadata) {
21479
- const metadata = providerMetadata["claude-code"] || {};
21480
- settings.resume = metadata.sessionId;
21481
- }
21482
- if (!isDev() && isElectron()) {
21483
- settings.pathToClaudeCodeExecutable = CLAUDE_CODE_EXECUTABLE_PATH;
21484
- }
21485
- if (toolSet) {
21486
- logger.debug(`Adding ${Object.keys(toolSet).length} tools to Claude Agent SDK`, {
21487
- tools: Object.keys(toolSet)
21488
- });
21489
- settings.aiSdkTools = normalizeAiSdkToolSet(toolSet);
21490
- settings.allowedTools = ["mcp__ai-sdk"];
21491
- } else {
21492
- settings.allowedTools = [];
21493
- }
21494
- if (systemPrompt) {
21495
- settings.systemPrompt = systemPrompt;
21496
- }
21497
- return aiSdkProviderClaudeCode.claudeCode(model.id, settings);
21498
- };
21499
- const getClaudeAgentSdkUsageReport = (task, provider, model, _usage, providerMetadata) => {
21500
- const data = providerMetadata["claude-code"] || {};
21501
- const usage = data.lastMessageRawUsage || data.rawUsage || {};
21502
- logger.debug("Claude Agent SDK usage:", { usage: _usage, providerMetadata });
21503
- const sentTokens = usage.input_tokens || 0;
21504
- const receivedTokens = usage.output_tokens || 0;
21505
- const cacheWriteTokens = usage.cache_creation_input_tokens || 0;
21506
- const cacheReadTokens = usage.cache_read_input_tokens || 0;
21507
- const messageCost = calculateCost(model, sentTokens, receivedTokens, cacheReadTokens);
21508
- return {
21509
- model: `${provider.id}/${model.id}`,
21510
- sentTokens: sentTokens + cacheReadTokens,
21511
- receivedTokens: receivedTokens + cacheWriteTokens,
21512
- messageCost,
21513
- agentTotalCost: task.task.agentTotalCost + messageCost
21514
- };
21515
- };
21516
- const getClaudeAgentSdkProviderParameters = () => {
21517
- return {
21518
- system: void 0
21519
- };
21520
- };
21521
- const claudeAgentSdkProviderStrategy = {
21522
- createLlm: createClaudeAgentSdkLlm,
21523
- getUsageReport: getClaudeAgentSdkUsageReport,
21524
- loadModels: loadClaudeAgentSdkModels,
21525
- hasEnvVars: hasClaudeAgentSdkEnvVars,
21526
- getAiderMapping: getClaudeAgentSdkAiderMapping,
21527
- getProviderParameters: getClaudeAgentSdkProviderParameters
21528
- };
21529
21434
  const loadDeepseekModels = async (profile, settings) => {
21530
21435
  if (!isDeepseekProvider(profile.provider)) {
21531
21436
  return { models: [], success: false };
@@ -22584,6 +22489,20 @@ const loadMinimaxModels = async (profile) => {
22584
22489
  };
22585
22490
  }
22586
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
+ },
22587
22506
  {
22588
22507
  id: "MiniMax-M2.7",
22589
22508
  providerId: profile.id,
@@ -22883,7 +22802,7 @@ const loadNeuralwattModels = async (profile, settings) => {
22883
22802
  return { models: [], success: false, error: errorMsg };
22884
22803
  }
22885
22804
  const data = await response.json();
22886
- logger.info(`Received response from Neuralwatt models API for profile ${profile.id}`, { data });
22805
+ logger.debug(`Received response from Neuralwatt models API for profile ${profile.id}`, { data });
22887
22806
  const models = data.data?.map((model) => {
22888
22807
  const metadata = model.metadata;
22889
22808
  const pricing = metadata?.pricing;
@@ -24302,7 +24221,7 @@ const zaiPlanProviderStrategy = {
24302
24221
  const MODELS_META_URL = "https://models.dev/api.json";
24303
24222
  const MODELS_FILE = path.join(AIDER_DESK_DATA_DIR, "models.json");
24304
24223
  const PROVIDER_MODELS_CACHE_FILE = path.join(AIDER_DESK_CACHE_DIR, "provider-models.json");
24305
- const PROVIDER_MODELS_CACHE_VERSION = 1;
24224
+ const PROVIDER_MODELS_CACHE_VERSION = 2;
24306
24225
  class ModelManager {
24307
24226
  constructor(store, eventManager) {
24308
24227
  this.store = store;
@@ -24318,11 +24237,9 @@ class ModelManager {
24318
24237
  providerRegistry = {
24319
24238
  anthropic: anthropicProviderStrategy,
24320
24239
  "anthropic-compatible": anthropicCompatibleProviderStrategy,
24321
- auggie: auggieProviderStrategy,
24322
24240
  azure: azureProviderStrategy,
24323
24241
  bedrock: bedrockProviderStrategy,
24324
24242
  cerebras: cerebrasProviderStrategy,
24325
- "claude-agent-sdk": claudeAgentSdkProviderStrategy,
24326
24243
  deepseek: deepseekProviderStrategy,
24327
24244
  gemini: geminiProviderStrategy,
24328
24245
  "gemini-cli": geminiCliProviderStrategy,
@@ -24995,7 +24912,7 @@ class ModelManager {
24995
24912
  const { provider } = registered;
24996
24913
  this.providerRegistry[provider.provider.name] = {
24997
24914
  ...provider.strategy,
24998
- 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),
24999
24916
  getUsageReport: provider.strategy.getUsageReport || getDefaultUsageReport,
25000
24917
  getProviderOptions: provider.strategy.getProviderOptions ? (_provider, model) => provider.strategy.getProviderOptions(model) : void 0,
25001
24918
  getProviderTools: provider.strategy.getProviderTools ? (_provider, model) => provider.strategy.getProviderTools(model) : void 0,
@@ -25053,7 +24970,11 @@ class ModelManager {
25053
24970
  this.eventManager.sendProvidersUpdated(this.getProviders());
25054
24971
  }
25055
24972
  getProviders() {
25056
- 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()];
25057
24978
  }
25058
24979
  getExtensionProviderProfiles() {
25059
24980
  return [...this.extensionProviders.values()].map((e) => e.profile);
@@ -26620,7 +26541,7 @@ class ExtensionContextImpl {
26620
26541
  return truncateToolResult(content, maxLines, maxSizeKB, maxTokens, saveToFile, truncationSuffix);
26621
26542
  }
26622
26543
  }
26623
- const CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
26544
+ const CACHE_TTL_MS$1 = 24 * 60 * 60 * 1e3;
26624
26545
  class ExtensionFetcher {
26625
26546
  cache = /* @__PURE__ */ new Map();
26626
26547
  getAvailableExtensionsPromise = null;
@@ -26651,7 +26572,7 @@ class ExtensionFetcher {
26651
26572
  async fetchExtensionsFromRepo(repoUrl, forceRefresh) {
26652
26573
  const cached = this.cache.get(repoUrl);
26653
26574
  const now = Date.now();
26654
- if (!forceRefresh && cached && now - cached.timestamp < CACHE_TTL_MS) {
26575
+ if (!forceRefresh && cached && now - cached.timestamp < CACHE_TTL_MS$1) {
26655
26576
  logger.debug(`[ExtensionFetcher] Using cached extensions for ${repoUrl}`);
26656
26577
  return cached.extensions;
26657
26578
  }
@@ -26963,6 +26884,67 @@ class ExtensionFetcher {
26963
26884
  return this.convertToGitHubRawUrl(webUrl);
26964
26885
  }
26965
26886
  }
26887
+ const ESM_SH_BASE_URL = "https://esm.sh";
26888
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
26889
+ const sanitizeLibraryName = (name) => {
26890
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_");
26891
+ };
26892
+ class ExtensionLibraryLoader {
26893
+ cacheDir;
26894
+ constructor() {
26895
+ this.cacheDir = path.join(AIDER_DESK_CACHE_DIR, "extension-libraries");
26896
+ }
26897
+ getCachePath(librarySpec) {
26898
+ const sanitized = sanitizeLibraryName(librarySpec);
26899
+ return path.join(this.cacheDir, `${sanitized}.js`);
26900
+ }
26901
+ getMetaPath(librarySpec) {
26902
+ const sanitized = sanitizeLibraryName(librarySpec);
26903
+ return path.join(this.cacheDir, `${sanitized}.meta.json`);
26904
+ }
26905
+ async loadLibrary(librarySpec) {
26906
+ const cachePath = this.getCachePath(librarySpec);
26907
+ const metaPath = this.getMetaPath(librarySpec);
26908
+ try {
26909
+ await fs.access(cachePath);
26910
+ await fs.access(metaPath);
26911
+ try {
26912
+ const meta = JSON.parse(await fs.readFile(metaPath, "utf-8"));
26913
+ const age = Date.now() - new Date(meta.fetchedAt).getTime();
26914
+ if (age < CACHE_TTL_MS) {
26915
+ logger.debug(`[ExtensionLibraryLoader] Cache hit for ${librarySpec}`);
26916
+ return await fs.readFile(cachePath, "utf-8");
26917
+ }
26918
+ logger.info(`[ExtensionLibraryLoader] Cache stale for ${librarySpec} (age: ${Math.round(age / 1e3 / 60)}min), re-fetching`);
26919
+ } catch {
26920
+ logger.warn(`[ExtensionLibraryLoader] Failed to read meta for ${librarySpec}, re-fetching`);
26921
+ }
26922
+ } catch {
26923
+ }
26924
+ logger.info(`[ExtensionLibraryLoader] Fetching ${librarySpec} from esm.sh`);
26925
+ const url = `${ESM_SH_BASE_URL}/${librarySpec}?external=react,react-dom,react/jsx-runtime,react/jsx-dev-runtime&bundle-deps&bundle`;
26926
+ let response = await fetchWithTimeout(url);
26927
+ if (!response.ok) {
26928
+ throw new Error(`Failed to fetch library ${librarySpec} from esm.sh: ${response.status} ${response.statusText}`);
26929
+ }
26930
+ let source = await response.text();
26931
+ const redirectMatch = source.match(/export\s+\*\s+from\s+["']([^"']+)["']/);
26932
+ if (redirectMatch) {
26933
+ const bundleUrl = `${ESM_SH_BASE_URL}${redirectMatch[1]}`;
26934
+ logger.debug(`[ExtensionLibraryLoader] Following redirect to ${bundleUrl}`);
26935
+ response = await fetchWithTimeout(bundleUrl);
26936
+ if (!response.ok) {
26937
+ throw new Error(`Failed to fetch bundle for ${librarySpec} from esm.sh: ${response.status} ${response.statusText}`);
26938
+ }
26939
+ source = await response.text();
26940
+ }
26941
+ await fs.mkdir(this.cacheDir, { recursive: true });
26942
+ await fs.writeFile(cachePath, source, "utf-8");
26943
+ await fs.writeFile(metaPath, JSON.stringify({ librarySpec, fetchedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
26944
+ logger.info(`[ExtensionLibraryLoader] Cached ${librarySpec}`);
26945
+ return source;
26946
+ }
26947
+ }
26966
26948
  class ExtensionManager {
26967
26949
  constructor(store, modelManager, eventManager, telemetryManager, memoryManager, registry = new ExtensionRegistry()) {
26968
26950
  this.store = store;
@@ -26973,6 +26955,7 @@ class ExtensionManager {
26973
26955
  this.registry = registry;
26974
26956
  this.loader = new ExtensionLoader();
26975
26957
  this.fetcher = new ExtensionFetcher();
26958
+ this.libraryLoader = new ExtensionLibraryLoader();
26976
26959
  }
26977
26960
  loader;
26978
26961
  fetcher;
@@ -26980,6 +26963,7 @@ class ExtensionManager {
26980
26963
  projectWatchers = /* @__PURE__ */ new Map();
26981
26964
  initialized = false;
26982
26965
  listeners = [];
26966
+ libraryLoader;
26983
26967
  debouncedNotifyListeners = debounce(() => {
26984
26968
  const extensions = this.registry.getExtensions();
26985
26969
  for (const listener of this.listeners) {
@@ -27462,15 +27446,48 @@ class ExtensionManager {
27462
27446
  errors
27463
27447
  };
27464
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
+ }
27465
27478
  getTools(task, mode, profile) {
27466
27479
  const collectedTools = [];
27467
27480
  const allExtensions = this.registry.getExtensions(task.getProjectDir());
27468
27481
  const extensions = this.filterEnabledExtensions(allExtensions);
27482
+ const disabledExtensionTools = profile.disabledExtensionTools ?? [];
27469
27483
  for (const loaded of extensions) {
27470
27484
  const { instance, metadata } = loaded;
27471
27485
  if (!instance.getTools) {
27472
27486
  continue;
27473
27487
  }
27488
+ if (disabledExtensionTools.includes(loaded.id)) {
27489
+ continue;
27490
+ }
27474
27491
  try {
27475
27492
  const context = new ExtensionContextImpl(
27476
27493
  loaded.id,
@@ -27493,6 +27510,10 @@ class ExtensionManager {
27493
27510
  logger.error(`[Extensions] Invalid tool '${tool.name}' from extension '${metadata.name}': ${validation.errors.join(", ")}`);
27494
27511
  continue;
27495
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
+ }
27496
27517
  collectedTools.push({
27497
27518
  extensionId: loaded.id,
27498
27519
  extensionName: metadata.name,
@@ -27516,11 +27537,12 @@ class ExtensionManager {
27516
27537
  * @param abortSignal - Optional AbortSignal for cancellation support
27517
27538
  * @returns A ToolSet containing all approved extension tools
27518
27539
  */
27519
- createExtensionToolset(task, mode, profile, allTools, abortSignal) {
27540
+ createExtensionToolset(task, mode, profile, allTools, approvalManager, abortSignal) {
27520
27541
  const toolSet = {};
27521
27542
  const registeredTools = this.getTools(task, mode, profile);
27522
27543
  for (const { extensionId, extensionName, tool } of registeredTools) {
27523
27544
  const toolId = tool.name;
27545
+ const fullToolId = `${extensionId}${TOOL_GROUP_NAME_SEPARATOR}${tool.name}`;
27524
27546
  const context = new ExtensionContextImpl(
27525
27547
  extensionId,
27526
27548
  extensionName,
@@ -27531,14 +27553,25 @@ class ExtensionManager {
27531
27553
  task.project,
27532
27554
  task
27533
27555
  );
27534
- if (profile.toolApprovals?.[toolId] === ToolApprovalState.Never) {
27535
- logger.debug(`[Extensions] Skipping tool '${tool.name}' (marked as Never approved)`);
27536
- continue;
27537
- }
27538
27556
  toolSet[toolId] = {
27539
27557
  description: tool.description,
27540
27558
  inputSchema: tool.inputSchema,
27541
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
+ }
27542
27575
  const allToolsInternal = Object.entries(allTools).reduce(
27543
27576
  (acc, [toolId2, tool2]) => {
27544
27577
  acc[toolId2] = {
@@ -27834,6 +27867,39 @@ class ExtensionManager {
27834
27867
  }
27835
27868
  return collectedComponents;
27836
27869
  }
27870
+ getUIComponentsLibraries(project) {
27871
+ const librariesByExtension = /* @__PURE__ */ new Map();
27872
+ const allExtensions = this.registry.getExtensions(project?.baseDir);
27873
+ const extensions = this.filterEnabledExtensions(allExtensions);
27874
+ for (const loaded of extensions) {
27875
+ const { instance, initialized, metadata } = loaded;
27876
+ if (!initialized || !instance.getUIComponentsLibraries) {
27877
+ continue;
27878
+ }
27879
+ try {
27880
+ const libraries = instance.getUIComponentsLibraries();
27881
+ if (typeof libraries !== "object" || libraries === null || Array.isArray(libraries)) {
27882
+ logger.error(`[Extensions] Extension '${metadata.name}' getUIComponentsLibraries() did not return a Record<string, string>`);
27883
+ continue;
27884
+ }
27885
+ const validLibs = {};
27886
+ for (const [key, spec] of Object.entries(libraries)) {
27887
+ if (typeof key === "string" && typeof spec === "string") {
27888
+ validLibs[key] = spec;
27889
+ }
27890
+ }
27891
+ if (Object.keys(validLibs).length > 0) {
27892
+ librariesByExtension.set(loaded.id, validLibs);
27893
+ }
27894
+ } catch (error) {
27895
+ logger.error(`[Extensions] Failed to get UI component libraries from extension '${metadata.name}':`, error);
27896
+ }
27897
+ }
27898
+ return librariesByExtension;
27899
+ }
27900
+ async loadExtensionLibrary(librarySpec) {
27901
+ return await this.libraryLoader.loadLibrary(librarySpec);
27902
+ }
27837
27903
  /**
27838
27904
  * Check if an extension provides a settings config component.
27839
27905
  * Uses the dedicated getConfigComponent() method on the Extension interface.
@@ -28743,12 +28809,26 @@ class EventsHandler {
28743
28809
  }
28744
28810
  await task.mergeWorktreeToMain(squash, targetBranch, commitMessage);
28745
28811
  }
28746
- async mergeAndSwitchToLocal(baseDir, taskId, targetBranch) {
28812
+ async switchToLocalWorkingMode(baseDir, taskId, options) {
28747
28813
  const task = this.projectManager.getProject(baseDir).getTask(taskId);
28748
28814
  if (!task) {
28749
28815
  throw new Error(`Task ${taskId} not found`);
28750
28816
  }
28751
- await task.mergeAndSwitchToLocal(targetBranch);
28817
+ await task.switchToLocalWorkingMode(options);
28818
+ }
28819
+ async switchToWorktreeWorkingMode(baseDir, taskId, options) {
28820
+ const task = this.projectManager.getProject(baseDir).getTask(taskId);
28821
+ if (!task) {
28822
+ throw new Error(`Task ${taskId} not found`);
28823
+ }
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();
28752
28832
  }
28753
28833
  async applyUncommittedChanges(baseDir, taskId, targetBranch) {
28754
28834
  const task = this.projectManager.getProject(baseDir).getTask(taskId);
@@ -29227,6 +29307,9 @@ ${error instanceof Error ? error.message : String(error)}`);
29227
29307
  readmeContent: ext.readmeContent
29228
29308
  }));
29229
29309
  }
29310
+ getExtensionToolsInfo(projectDir) {
29311
+ return this.extensionManager.getToolsInfo(projectDir);
29312
+ }
29230
29313
  async getAvailableExtensions(repositories, forceRefresh, fetchOnly) {
29231
29314
  return await this.extensionManager.getAvailableExtensions(repositories, forceRefresh, fetchOnly);
29232
29315
  }
@@ -29258,15 +29341,21 @@ ${error instanceof Error ? error.message : String(error)}`);
29258
29341
  const task = taskId && project ? project.getTask(taskId) ?? void 0 : void 0;
29259
29342
  const components = this.extensionManager.getUIComponents(project, task);
29260
29343
  const filtered = placement ? components.filter((c) => c.component.placement === placement) : components;
29344
+ const librariesByExtension = this.extensionManager.getUIComponentsLibraries(project);
29261
29345
  return filtered.map((c) => ({
29262
29346
  extensionId: c.extensionId,
29263
29347
  componentId: c.component.id,
29348
+ name: c.component.name,
29264
29349
  placement: c.component.placement,
29265
29350
  jsx: c.component.jsx,
29266
29351
  loadData: c.component.loadData,
29267
- noDataCache: c.component.noDataCache
29352
+ noDataCache: c.component.noDataCache,
29353
+ libraries: librariesByExtension.get(c.extensionId)
29268
29354
  }));
29269
29355
  }
29356
+ async loadExtensionLibrary(librarySpec) {
29357
+ return await this.extensionManager.loadExtensionLibrary(librarySpec);
29358
+ }
29270
29359
  async getUIExtensionData(extensionId, componentId, projectDir, taskId) {
29271
29360
  logger.debug("Getting UI extension data:", {
29272
29361
  extensionId,
@@ -30596,6 +30685,9 @@ const migrateSettingsV19toV20 = (settings) => {
30596
30685
  }
30597
30686
  };
30598
30687
  };
30688
+ const migrateProvidersV20toV21 = (providers) => {
30689
+ return providers.filter((p) => p.provider.name !== "claude-agent-sdk" && p.provider.name !== "auggie");
30690
+ };
30599
30691
  const DEFAULT_SETTINGS = {
30600
30692
  language: "en",
30601
30693
  startupMode: ProjectStartMode.Empty,
@@ -30675,7 +30767,7 @@ const DEFAULT_SETTINGS = {
30675
30767
  const compareBaseDirs = (baseDir1, baseDir2) => {
30676
30768
  return normalizeBaseDir(baseDir1) === normalizeBaseDir(baseDir2);
30677
30769
  };
30678
- const CURRENT_SETTINGS_VERSION = 20;
30770
+ const CURRENT_SETTINGS_VERSION = 21;
30679
30771
  class Store {
30680
30772
  // @ts-expect-error expected to be initialized
30681
30773
  store;
@@ -30830,6 +30922,10 @@ class Store {
30830
30922
  settings = migrateSettingsV19toV20(settings);
30831
30923
  settingsVersion = 20;
30832
30924
  }
30925
+ if (settingsVersion === 20) {
30926
+ providers = migrateProvidersV20toV21(providers || []);
30927
+ settingsVersion = 21;
30928
+ }
30833
30929
  this.store.set("settings", settings);
30834
30930
  this.store.set("openProjects", openProjects || []);
30835
30931
  this.store.set("providers", providers || []);