@aiderdesk/aiderdesk 0.59.0 → 0.61.0-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/renderer/assets/{_basePickBy-BXFYYah3.js → _basePickBy-BWoXHZ8Z.js} +2 -2
- package/out/renderer/assets/{_baseUniq-Cd1ra5R6.js → _baseUniq-Z8t1Ab1g.js} +1 -1
- package/out/renderer/assets/{arc-CzRQcQ8z.js → arc-CwESpVYE.js} +1 -1
- package/out/renderer/assets/{architectureDiagram-2XIMDMQ5-CfLvNO9v.js → architectureDiagram-2XIMDMQ5-C490EQr5.js} +6 -6
- package/out/renderer/assets/{blockDiagram-WCTKOSBZ-BLFI6WIC.js → blockDiagram-WCTKOSBZ-DbHssxrQ.js} +6 -6
- package/out/renderer/assets/{c4Diagram-IC4MRINW-D2fK1fs0.js → c4Diagram-IC4MRINW-BxE3GUKu.js} +2 -2
- package/out/renderer/assets/{channel-DrJKu4_6.js → channel-C9hJq_Xr.js} +1 -1
- package/out/renderer/assets/{chunk-4BX2VUAB-CceHu94f.js → chunk-4BX2VUAB-ChcKNpL6.js} +1 -1
- package/out/renderer/assets/{chunk-55IACEB6-Ceu45XlD.js → chunk-55IACEB6-oLT9lXTt.js} +1 -1
- package/out/renderer/assets/{chunk-FMBD7UC4-BKzuqZXn.js → chunk-FMBD7UC4-B5k8rETe.js} +1 -1
- package/out/renderer/assets/{chunk-JSJVCQXG-iYOtJ8I-.js → chunk-JSJVCQXG-DtjSx6Cj.js} +1 -1
- package/out/renderer/assets/{chunk-KX2RTZJC-bNHhs5xZ.js → chunk-KX2RTZJC-D32tV7H-.js} +1 -1
- package/out/renderer/assets/{chunk-NQ4KR5QH-C0IrVSiA.js → chunk-NQ4KR5QH-Bmqo2FpL.js} +3 -3
- package/out/renderer/assets/{chunk-QZHKN3VN-CQapQOOT.js → chunk-QZHKN3VN-8BpGifjS.js} +1 -1
- package/out/renderer/assets/{chunk-WL4C6EOR-DUYtOMll.js → chunk-WL4C6EOR-vhby2fZi.js} +5 -5
- package/out/renderer/assets/{classDiagram-VBA2DB6C-UeXpMFb8.js → classDiagram-VBA2DB6C-BxRSSb9e.js} +6 -6
- package/out/renderer/assets/{classDiagram-v2-RAHNMMFH-UeXpMFb8.js → classDiagram-v2-RAHNMMFH-BxRSSb9e.js} +6 -6
- package/out/renderer/assets/{clone-CFgMCpt9.js → clone-D9a-uIZa.js} +1 -1
- package/out/renderer/assets/{cose-bilkent-S5V4N54A-DnX12BL_.js → cose-bilkent-S5V4N54A-CUqqQ-6R.js} +1 -1
- package/out/renderer/assets/{dagre-KLK3FWXG-7-_OG7Kj.js → dagre-KLK3FWXG-ORIwMMhq.js} +6 -6
- package/out/renderer/assets/{diagram-E7M64L7V-DumXVEqI.js → diagram-E7M64L7V-bS9HcoDQ.js} +7 -7
- package/out/renderer/assets/{diagram-IFDJBPK2-UTrn1bff.js → diagram-IFDJBPK2-BRlTIR0J.js} +6 -6
- package/out/renderer/assets/{diagram-P4PSJMXO-Bk1LZhwV.js → diagram-P4PSJMXO-jsjGwH-p.js} +6 -6
- package/out/renderer/assets/{erDiagram-INFDFZHY-CMAH31O-.js → erDiagram-INFDFZHY-DGlgjHOQ.js} +4 -4
- package/out/renderer/assets/{flowDiagram-PKNHOUZH-uQFfaMJM.js → flowDiagram-PKNHOUZH-CzLC87bM.js} +6 -6
- package/out/renderer/assets/{ganttDiagram-A5KZAMGK-quRvalRu.js → ganttDiagram-A5KZAMGK-BbDv36wT.js} +1 -1
- package/out/renderer/assets/{gitGraphDiagram-K3NZZRJ6-CfwRyzfx.js → gitGraphDiagram-K3NZZRJ6-CBQnBjEi.js} +7 -7
- package/out/renderer/assets/{graph-W5JPvpWW.js → graph-Dl5N6maZ.js} +2 -2
- package/out/renderer/assets/{index-Bhs67jG4.js → index-6qedlt0q.js} +2 -2
- package/out/renderer/assets/{index-C1ZBVNfw.js → index-86jDpUJn.js} +2 -2
- package/out/renderer/assets/{index-Wwk_5mUs.js → index-BRjO8ber.js} +5 -5
- package/out/renderer/assets/{index-DjoaBGM_.js → index-BVgw7j0d.js} +2 -2
- package/out/renderer/assets/{index--j4qFXVt.js → index-BhkyI1q3.js} +2 -2
- package/out/renderer/assets/{index-BK7nk5p7.js → index-BqrmLPkg.js} +5 -5
- package/out/renderer/assets/{index-C3Glc2Zo.js → index-BuPbw4xM.js} +2 -2
- package/out/renderer/assets/{index-C327GPg5.js → index-CTO-LPBg.js} +5 -5
- package/out/renderer/assets/{index-CIjEi4HS.js → index-CTO4SzlI.js} +2 -2
- package/out/renderer/assets/{index-D6kgjSLw.js → index-CZ9IQK_0.js} +5 -5
- package/out/renderer/assets/{index-JCZJkVpl.js → index-CZxsVxBZ.js} +1 -1
- package/out/renderer/assets/{index-Bb7XYl8_.js → index-DIXV-3Xn.js} +2 -2
- package/out/renderer/assets/{index-DdsfZljo.js → index-DNzOtZX5.js} +2 -2
- package/out/renderer/assets/{index-33dprpTM.js → index-DZtJe7oC.js} +2 -2
- package/out/renderer/assets/{index-B4cKzfxL.js → index-DaWjZz_g.js} +5 -5
- package/out/renderer/assets/{index-BJyiVzcz.js → index-De056HHR.js} +3 -3
- package/out/renderer/assets/{index-CL49sSsZ.js → index-Dk3wSDSN.js} +5 -5
- package/out/renderer/assets/{index-CJqBFUF8.js → index-Dy4VRsnA.js} +4 -4
- package/out/renderer/assets/{index-BijwEwO9.js → index-FnnayPBc.js} +2 -2
- package/out/renderer/assets/{index-0KKkNp35.js → index-MDHavDF9.js} +2146 -2507
- package/out/renderer/assets/{index-HpcqfYbh.js → index-MZb_zy6R.js} +2 -2
- package/out/renderer/assets/{index-Do6DM72m.js → index-chzQl8rJ.js} +2 -2
- package/out/renderer/assets/{index-ChkFNtFm.js → index-s-Owx3Pm.js} +3 -3
- package/out/renderer/assets/{infoDiagram-LFFYTUFH-CLlREcM7.js → infoDiagram-LFFYTUFH-GtEDBEmz.js} +5 -5
- package/out/renderer/assets/{ishikawaDiagram-PHBUUO56-C8MCvpMI.js → ishikawaDiagram-PHBUUO56-Uj90gr3I.js} +1 -1
- package/out/renderer/assets/{journeyDiagram-4ABVD52K-D3AyVALG.js → journeyDiagram-4ABVD52K-DekFuOwS.js} +4 -4
- package/out/renderer/assets/{kanban-definition-K7BYSVSG-D6esJ1Mv.js → kanban-definition-K7BYSVSG-Hfz2L6NS.js} +2 -2
- package/out/renderer/assets/{layout-B-GkLewD.js → layout-B9K2VT39.js} +4 -4
- package/out/renderer/assets/{mindmap-definition-YRQLILUH-DiFIDGDM.js → mindmap-definition-YRQLILUH-DDcS7_GH.js} +3 -3
- package/out/renderer/assets/{pieDiagram-SKSYHLDU-DjzUpI9a.js → pieDiagram-SKSYHLDU-DwjEcJ0Q.js} +7 -7
- package/out/renderer/assets/{quadrantDiagram-337W2JSQ-BKx0O-wq.js → quadrantDiagram-337W2JSQ-CpGqN7vo.js} +1 -1
- package/out/renderer/assets/{requirementDiagram-Z7DCOOCP-qRcPUPwh.js → requirementDiagram-Z7DCOOCP-qDRUf1ip.js} +3 -3
- package/out/renderer/assets/{sankeyDiagram-WA2Y5GQK-CE8REafI.js → sankeyDiagram-WA2Y5GQK-DRKS8C1H.js} +1 -1
- package/out/renderer/assets/{sequenceDiagram-2WXFIKYE-a4AELpAg.js → sequenceDiagram-2WXFIKYE-DhVN2ug2.js} +3 -3
- package/out/renderer/assets/{stateDiagram-RAJIS63D-CXiPRAnM.js → stateDiagram-RAJIS63D-BpP4eSqu.js} +8 -8
- package/out/renderer/assets/{stateDiagram-v2-FVOUBMTO-CZKt-kP1.js → stateDiagram-v2-FVOUBMTO-B_qQJqra.js} +4 -4
- package/out/renderer/assets/{timeline-definition-YZTLITO2-BY3z14HB.js → timeline-definition-YZTLITO2-UoWXE_76.js} +2 -2
- package/out/renderer/assets/{treemap-KZPCXAKY-DZOzveSW.js → treemap-KZPCXAKY-W2a2L6ff.js} +4 -4
- package/out/renderer/assets/{vennDiagram-LZ73GAT5-Bus0Wjc8.js → vennDiagram-LZ73GAT5-Dp1FZ609.js} +1 -1
- package/out/renderer/assets/{xychartDiagram-JWTSCODW-BdGw2_Lw.js → xychartDiagram-JWTSCODW-CTzYHTbD.js} +1 -1
- package/out/renderer/index.html +1 -1
- package/out/resources/prompts/init-project.hbs +61 -49
- package/out/resources/prompts/system-prompt.hbs +5 -0
- package/out/resources/prompts/workflow.hbs +10 -0
- package/out/runner.js +446 -549
- package/package.json +1 -1
package/out/runner.js
CHANGED
|
@@ -772,8 +772,6 @@ const AIDER_DESK_RULES_DIR = "rules";
|
|
|
772
772
|
const AIDER_DESK_PROJECT_RULES_DIR = path.join(AIDER_DESK_DIR, AIDER_DESK_RULES_DIR);
|
|
773
773
|
const AIDER_DESK_GLOBAL_RULES_DIR = path.join(os.homedir(), AIDER_DESK_DIR, AIDER_DESK_RULES_DIR);
|
|
774
774
|
const AIDER_DESK_COMMANDS_DIR = path.join(AIDER_DESK_DIR, "commands");
|
|
775
|
-
const AIDER_DESK_HOOKS_DIR = path.join(AIDER_DESK_DIR, "hooks");
|
|
776
|
-
const AIDER_DESK_GLOBAL_HOOKS_DIR = path.join(os.homedir(), AIDER_DESK_DIR, "hooks");
|
|
777
775
|
const AIDER_DESK_EXTENSIONS_DIR = path.join(AIDER_DESK_DIR, "extensions");
|
|
778
776
|
const AIDER_DESK_GLOBAL_EXTENSIONS_DIR = path.join(os.homedir(), AIDER_DESK_DIR, "extensions");
|
|
779
777
|
const AIDER_DESK_PROMPTS_DIR = path.join(AIDER_DESK_DIR, "prompts");
|
|
@@ -784,6 +782,7 @@ const AIDER_DESK_TMP_DIR = path.join(AIDER_DESK_DIR, "tmp");
|
|
|
784
782
|
const AIDER_DESK_WATCH_FILES_LOCK = path.join(AIDER_DESK_DIR, "watch-files.lock");
|
|
785
783
|
const WORKTREE_BRANCH_PREFIX = "aider-desk/task/";
|
|
786
784
|
const AIDER_DESK_MEMORY_FILE = path.join(AIDER_DESK_DATA_DIR, "memory.db");
|
|
785
|
+
const EXTENSIONS_REPOS_CACHE_DIR = path.join(AIDER_DESK_CACHE_DIR, "extensions");
|
|
787
786
|
const POSTHOG_PUBLIC_API_KEY = "phc_AF4zkjrcziXLh8PBFsRSvVr4VZ38p3ezsdX0KDYuElI";
|
|
788
787
|
const POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
789
788
|
process.env.AIDER_DESK_HEADLESS === "true";
|
|
@@ -852,10 +851,6 @@ class ApprovalManager {
|
|
|
852
851
|
return this.handleApproval(key, text, subject);
|
|
853
852
|
}
|
|
854
853
|
async handleApproval(key, text, subject) {
|
|
855
|
-
const hookResult = await this.task.hookManager.trigger("onHandleApproval", { key, text, subject }, this.task, this.task.project);
|
|
856
|
-
if (typeof hookResult.result === "boolean") {
|
|
857
|
-
return [hookResult.result, void 0];
|
|
858
|
-
}
|
|
859
854
|
const extensionResult = await this.task.dispatchExtensionEvent("onHandleApproval", { key, text, subject });
|
|
860
855
|
if (extensionResult.blocked) {
|
|
861
856
|
return [false, void 0];
|
|
@@ -2315,7 +2310,7 @@ const getTelemetryEnvironmentVariablesForAider = (settings, baseDir) => {
|
|
|
2315
2310
|
...getLangfuseEnvironmentVariables(baseDir, settings)
|
|
2316
2311
|
};
|
|
2317
2312
|
};
|
|
2318
|
-
const getDefaultProjectSettings = (store, providerModels, baseDir, defaultAgentProfileId = DEFAULT_AGENT_PROFILE.id) => {
|
|
2313
|
+
const getDefaultProjectSettings = (store, providerModels, baseDir, defaultAgentProfileId = DEFAULT_AGENT_PROFILE.id, providers) => {
|
|
2319
2314
|
const openProjects = store.getOpenProjects();
|
|
2320
2315
|
const activeProject = openProjects.find((p) => p.active);
|
|
2321
2316
|
if (activeProject) {
|
|
@@ -2324,7 +2319,7 @@ const getDefaultProjectSettings = (store, providerModels, baseDir, defaultAgentP
|
|
|
2324
2319
|
};
|
|
2325
2320
|
}
|
|
2326
2321
|
return {
|
|
2327
|
-
mainModel: determineMainModel(store.getSettings(), store.getProviders(), providerModels, baseDir),
|
|
2322
|
+
mainModel: determineMainModel(store.getSettings(), providers ?? store.getProviders(), providerModels, baseDir),
|
|
2328
2323
|
weakModel: determineWeakModel(baseDir),
|
|
2329
2324
|
modelEditFormats: {},
|
|
2330
2325
|
currentMode: "agent",
|
|
@@ -5783,7 +5778,13 @@ class Agent {
|
|
|
5783
5778
|
const fileInfos = await Promise.all(
|
|
5784
5779
|
files.map(async (file) => {
|
|
5785
5780
|
try {
|
|
5786
|
-
const filePath =
|
|
5781
|
+
const filePath = await task.resolveContextFilePath(file.path);
|
|
5782
|
+
if (!filePath) {
|
|
5783
|
+
logger.error("Could not resolve context file path:", {
|
|
5784
|
+
path: file.path
|
|
5785
|
+
});
|
|
5786
|
+
return null;
|
|
5787
|
+
}
|
|
5787
5788
|
const fileContentBuffer = await fs.readFile(filePath);
|
|
5788
5789
|
if (istextorbinary.isBinary(filePath, fileContentBuffer)) {
|
|
5789
5790
|
try {
|
|
@@ -5827,9 +5828,13 @@ class Agent {
|
|
|
5827
5828
|
);
|
|
5828
5829
|
fileInfos.filter(Boolean).forEach((file) => {
|
|
5829
5830
|
if (file.isImage && file.imageBase64) {
|
|
5831
|
+
imageParts.push({
|
|
5832
|
+
type: "text",
|
|
5833
|
+
text: `Here is image ${path.basename(file.path)} for your reference.`
|
|
5834
|
+
});
|
|
5830
5835
|
imageParts.push({
|
|
5831
5836
|
type: "image",
|
|
5832
|
-
image:
|
|
5837
|
+
image: file.imageBase64,
|
|
5833
5838
|
mediaType: file.mimeType
|
|
5834
5839
|
});
|
|
5835
5840
|
} else if (!file.isImage && file.content) {
|
|
@@ -5905,16 +5910,27 @@ ${file.content}</content-with-line-numbers>
|
|
|
5905
5910
|
const nonImageFiles = [];
|
|
5906
5911
|
for (const file of contextFiles) {
|
|
5907
5912
|
try {
|
|
5908
|
-
const filePath =
|
|
5913
|
+
const filePath = await task.resolveContextFilePath(file.path);
|
|
5914
|
+
if (!filePath) {
|
|
5915
|
+
logger.error("Could not resolve context file path for working files:", {
|
|
5916
|
+
path: file.path
|
|
5917
|
+
});
|
|
5918
|
+
nonImageFiles.push(file);
|
|
5919
|
+
continue;
|
|
5920
|
+
}
|
|
5909
5921
|
const fileContentBuffer = await fs.readFile(filePath);
|
|
5910
5922
|
if (istextorbinary.isBinary(filePath, fileContentBuffer)) {
|
|
5911
5923
|
try {
|
|
5912
5924
|
const detected = await fileType.fileTypeFromBuffer(fileContentBuffer);
|
|
5913
5925
|
if (detected?.mime.startsWith("image/")) {
|
|
5914
5926
|
const imageBase64 = fileContentBuffer.toString("base64");
|
|
5927
|
+
imageParts.push({
|
|
5928
|
+
type: "text",
|
|
5929
|
+
text: `Here is image ${path.basename(file.path)} for your reference.`
|
|
5930
|
+
});
|
|
5915
5931
|
imageParts.push({
|
|
5916
5932
|
type: "image",
|
|
5917
|
-
image:
|
|
5933
|
+
image: imageBase64,
|
|
5918
5934
|
mediaType: detected.mime
|
|
5919
5935
|
});
|
|
5920
5936
|
continue;
|
|
@@ -6052,12 +6068,7 @@ ${fileList}`
|
|
|
6052
6068
|
wrappedToolSet[toolName] = {
|
|
6053
6069
|
...toolDef,
|
|
6054
6070
|
execute: async (input, options) => {
|
|
6055
|
-
|
|
6056
|
-
if (hookResult.blocked) {
|
|
6057
|
-
logger.warn(`Tool execution blocked by hook: ${toolName}`);
|
|
6058
|
-
return "Tool execution blocked by hook.";
|
|
6059
|
-
}
|
|
6060
|
-
let effectiveInput = hookResult.event.args ?? input;
|
|
6071
|
+
let effectiveInput = input;
|
|
6061
6072
|
const [serverName, messageToolName] = extractServerNameToolName(toolName);
|
|
6062
6073
|
task.addToolMessage(options.toolCallId, serverName, messageToolName, effectiveInput, void 0, void 0, promptContext);
|
|
6063
6074
|
const toolCalledExtensionResult = await this.extensionManager.dispatchEvent(
|
|
@@ -6074,7 +6085,6 @@ ${fileList}`
|
|
|
6074
6085
|
options.abortSignal = abortSignal;
|
|
6075
6086
|
}
|
|
6076
6087
|
const result = await toolDef.execute(effectiveInput, options);
|
|
6077
|
-
const toolFinishedHookResult = await task.hookManager.trigger("onToolFinished", { toolName, args: effectiveInput, result }, task, task.project);
|
|
6078
6088
|
const toolFinishedExtensionResult = await this.extensionManager.dispatchEvent(
|
|
6079
6089
|
"onToolFinished",
|
|
6080
6090
|
{ toolName, input: effectiveInput, output: result },
|
|
@@ -6083,8 +6093,6 @@ ${fileList}`
|
|
|
6083
6093
|
);
|
|
6084
6094
|
if (toolFinishedExtensionResult.output) {
|
|
6085
6095
|
return toolFinishedExtensionResult.output;
|
|
6086
|
-
} else if (toolFinishedHookResult.event.result) {
|
|
6087
|
-
return toolFinishedHookResult.event.result;
|
|
6088
6096
|
} else {
|
|
6089
6097
|
return result;
|
|
6090
6098
|
}
|
|
@@ -6230,18 +6238,6 @@ ${fileList}`
|
|
|
6230
6238
|
async runAgent(task, profile, prompt, mode = "agent", promptContext, initialContextMessages, initialContextFiles, systemPrompt, includeInContext = true, abortSignal) {
|
|
6231
6239
|
let contextMessages = initialContextMessages ?? await task.getContextMessages();
|
|
6232
6240
|
let contextFiles = initialContextFiles ?? await task.getContextFiles();
|
|
6233
|
-
const hookResult = await task.hookManager.trigger("onAgentStarted", { prompt, contextMessages, contextFiles }, task, task.project);
|
|
6234
|
-
if (hookResult.blocked) {
|
|
6235
|
-
logger.debug("Agent execution blocked by hook");
|
|
6236
|
-
return [];
|
|
6237
|
-
}
|
|
6238
|
-
prompt = hookResult.event.prompt;
|
|
6239
|
-
if (hookResult.event.contextMessages) {
|
|
6240
|
-
contextMessages = hookResult.event.contextMessages;
|
|
6241
|
-
}
|
|
6242
|
-
if (hookResult.event.contextFiles) {
|
|
6243
|
-
contextFiles = hookResult.event.contextFiles;
|
|
6244
|
-
}
|
|
6245
6241
|
if (!systemPrompt) {
|
|
6246
6242
|
systemPrompt = await this.promptsManager.getSystemPrompt(this.store.getSettings(), task, profile);
|
|
6247
6243
|
}
|
|
@@ -6254,7 +6250,7 @@ ${fileList}`
|
|
|
6254
6250
|
const settings = this.store.getSettings();
|
|
6255
6251
|
const projectProfiles = this.agentProfileManager.getProjectProfiles(task.project);
|
|
6256
6252
|
let resultMessages = userRequestMessage ? [userRequestMessage] : [];
|
|
6257
|
-
const providers = this.
|
|
6253
|
+
const providers = this.modelManager.getProviders();
|
|
6258
6254
|
let provider = providers.find((p) => p.id === profile.provider);
|
|
6259
6255
|
let modelName = profile.model;
|
|
6260
6256
|
if (provider) {
|
|
@@ -6555,13 +6551,6 @@ ${fileList}`
|
|
|
6555
6551
|
return;
|
|
6556
6552
|
}
|
|
6557
6553
|
responseMessages = await this.processStep(currentResponseId, stepResult, task, provider, modelName, promptContext, abortSignal);
|
|
6558
|
-
const hookResult3 = await task.hookManager.trigger("onAgentStepFinished", { stepResult, finishReason, responseMessages }, task, task.project);
|
|
6559
|
-
if (hookResult3?.event?.finishReason) {
|
|
6560
|
-
finishReason = hookResult3.event.finishReason;
|
|
6561
|
-
}
|
|
6562
|
-
if (hookResult3?.event?.responseMessages) {
|
|
6563
|
-
responseMessages = hookResult3.event.responseMessages;
|
|
6564
|
-
}
|
|
6565
6554
|
const extensionResult2 = await this.extensionManager.dispatchEvent(
|
|
6566
6555
|
"onAgentStepFinished",
|
|
6567
6556
|
{
|
|
@@ -6772,10 +6761,6 @@ ${fileList}`
|
|
|
6772
6761
|
break;
|
|
6773
6762
|
}
|
|
6774
6763
|
}
|
|
6775
|
-
const hookResult2 = await task.hookManager.trigger("onAgentFinished", { aborted: false, contextMessages, resultMessages }, task, task.project);
|
|
6776
|
-
if (hookResult2?.event?.resultMessages) {
|
|
6777
|
-
resultMessages = hookResult2.event.resultMessages;
|
|
6778
|
-
}
|
|
6779
6764
|
const extensionResult = await this.extensionManager.dispatchEvent(
|
|
6780
6765
|
"onAgentFinished",
|
|
6781
6766
|
{
|
|
@@ -6794,10 +6779,6 @@ ${fileList}`
|
|
|
6794
6779
|
if (streamingMessageIds.size > 0) {
|
|
6795
6780
|
this.removeUnfinishedStreamingMessages(task, streamingMessageIds);
|
|
6796
6781
|
}
|
|
6797
|
-
const hookResult2 = await task.hookManager.trigger("onAgentFinished", { aborted: true, contextMessages, resultMessages }, task, task.project);
|
|
6798
|
-
if (hookResult2?.event?.resultMessages) {
|
|
6799
|
-
resultMessages = hookResult2.event.resultMessages;
|
|
6800
|
-
}
|
|
6801
6782
|
const extensionResult = await this.extensionManager.dispatchEvent(
|
|
6802
6783
|
"onAgentFinished",
|
|
6803
6784
|
{
|
|
@@ -6830,20 +6811,40 @@ ${fileList}`
|
|
|
6830
6811
|
return resultMessages;
|
|
6831
6812
|
}
|
|
6832
6813
|
filterResultMessages(resultMessages) {
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6814
|
+
const helpersPrefix = `${HELPERS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}`;
|
|
6815
|
+
return resultMessages.reduce((acc, message) => {
|
|
6816
|
+
if (message.role === "tool") {
|
|
6817
|
+
const nonHelpersContent = message.content.filter((part) => !part.toolName.startsWith(helpersPrefix));
|
|
6818
|
+
if (nonHelpersContent.length === 0) {
|
|
6819
|
+
return acc;
|
|
6820
|
+
}
|
|
6821
|
+
if (nonHelpersContent.length === message.content.length) {
|
|
6822
|
+
acc.push(message);
|
|
6823
|
+
} else {
|
|
6824
|
+
acc.push({ ...message, content: nonHelpersContent });
|
|
6825
|
+
}
|
|
6826
|
+
return acc;
|
|
6836
6827
|
}
|
|
6837
6828
|
if (message.role === "assistant") {
|
|
6838
|
-
if (Array.isArray(message.content)
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6829
|
+
if (!Array.isArray(message.content)) {
|
|
6830
|
+
acc.push(message);
|
|
6831
|
+
return acc;
|
|
6832
|
+
}
|
|
6833
|
+
const hasHelpersToolCall = message.content.some((part) => part.type === "tool-call" && part.toolName.startsWith(helpersPrefix));
|
|
6834
|
+
if (!hasHelpersToolCall) {
|
|
6835
|
+
acc.push(message);
|
|
6836
|
+
return acc;
|
|
6842
6837
|
}
|
|
6843
|
-
|
|
6838
|
+
const nonHelpersContent = message.content.filter((part) => !(part.type === "tool-call" && part.toolName.startsWith(helpersPrefix)));
|
|
6839
|
+
if (nonHelpersContent.length === 0) {
|
|
6840
|
+
return acc;
|
|
6841
|
+
}
|
|
6842
|
+
acc.push({ ...message, content: nonHelpersContent });
|
|
6843
|
+
return acc;
|
|
6844
6844
|
}
|
|
6845
|
-
|
|
6846
|
-
|
|
6845
|
+
acc.push(message);
|
|
6846
|
+
return acc;
|
|
6847
|
+
}, []);
|
|
6847
6848
|
}
|
|
6848
6849
|
async getContextFilesAsToolCallMessages(task, profile, contextFiles) {
|
|
6849
6850
|
const messages = [];
|
|
@@ -6950,16 +6951,16 @@ ${fileList}`
|
|
|
6950
6951
|
}
|
|
6951
6952
|
}
|
|
6952
6953
|
for (const imageData of imageFiles) {
|
|
6953
|
-
fileMessages.push({
|
|
6954
|
-
role: "assistant",
|
|
6955
|
-
content: `Provide content of image file: ${imageData.relativePath}`
|
|
6956
|
-
});
|
|
6957
6954
|
const imageMessage = {
|
|
6958
6955
|
role: "user",
|
|
6959
6956
|
content: [
|
|
6957
|
+
{
|
|
6958
|
+
type: "text",
|
|
6959
|
+
text: `Here is content of image file ${path.basename(imageData.relativePath)}`
|
|
6960
|
+
},
|
|
6960
6961
|
{
|
|
6961
6962
|
type: "image",
|
|
6962
|
-
image:
|
|
6963
|
+
image: imageData.imageBase64,
|
|
6963
6964
|
mediaType: imageData.mimeType
|
|
6964
6965
|
}
|
|
6965
6966
|
]
|
|
@@ -7006,7 +7007,7 @@ ${fileList}`
|
|
|
7006
7007
|
}
|
|
7007
7008
|
async generateText(modelId, systemPrompt, prompt, projectDir, messages = [], abortable = true, abortSignal) {
|
|
7008
7009
|
const [providerId, modelName] = extractProviderModel(modelId);
|
|
7009
|
-
const providers = this.
|
|
7010
|
+
const providers = this.modelManager.getProviders();
|
|
7010
7011
|
const provider = providers.find((p) => p.id === providerId);
|
|
7011
7012
|
if (!provider) {
|
|
7012
7013
|
throw new Error(`Provider ${providerId} not found`);
|
|
@@ -7059,7 +7060,7 @@ ${fileList}`
|
|
|
7059
7060
|
}
|
|
7060
7061
|
async estimateTokens(task, profile) {
|
|
7061
7062
|
try {
|
|
7062
|
-
const providers = this.
|
|
7063
|
+
const providers = this.modelManager.getProviders();
|
|
7063
7064
|
const provider = providers.find((p) => p.id === profile.provider);
|
|
7064
7065
|
if (!provider) {
|
|
7065
7066
|
logger.warn(`Estimation failed: Provider ${profile.provider} not found`);
|
|
@@ -8328,7 +8329,8 @@ const RunCommandSchema = zod.z.object({
|
|
|
8328
8329
|
});
|
|
8329
8330
|
const InitProjectRulesFileSchema = zod.z.object({
|
|
8330
8331
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8331
|
-
taskId: zod.z.string().min(1, "Task id is required")
|
|
8332
|
+
taskId: zod.z.string().min(1, "Task id is required"),
|
|
8333
|
+
args: zod.z.string().optional()
|
|
8332
8334
|
});
|
|
8333
8335
|
const CreateNewTaskSchema = zod.z.object({
|
|
8334
8336
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
@@ -8440,9 +8442,9 @@ const GenerateCommitMessageSchema = zod.z.object({
|
|
|
8440
8442
|
const CommitChangesSchema = zod.z.object({
|
|
8441
8443
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8442
8444
|
taskId: zod.z.string().min(1, "Task id is required"),
|
|
8443
|
-
message: zod.z.string()
|
|
8445
|
+
message: zod.z.string(),
|
|
8444
8446
|
amend: zod.z.boolean()
|
|
8445
|
-
});
|
|
8447
|
+
}).refine((data) => data.amend || data.message.trim().length > 0, { message: "Commit message is required", path: ["message"] });
|
|
8446
8448
|
const ListBranchesSchema = zod.z.object({
|
|
8447
8449
|
projectDir: zod.z.string().min(1, "Project directory is required")
|
|
8448
8450
|
});
|
|
@@ -8601,8 +8603,8 @@ class ProjectApi extends BaseApi {
|
|
|
8601
8603
|
if (!parsed) {
|
|
8602
8604
|
return;
|
|
8603
8605
|
}
|
|
8604
|
-
const { projectDir, taskId } = parsed;
|
|
8605
|
-
await this.eventsHandler.initProjectRulesFile(projectDir, taskId);
|
|
8606
|
+
const { projectDir, taskId, args } = parsed;
|
|
8607
|
+
await this.eventsHandler.initProjectRulesFile(projectDir, taskId, args);
|
|
8606
8608
|
res.status(200).json({ message: "Project rules file initialized" });
|
|
8607
8609
|
})
|
|
8608
8610
|
);
|
|
@@ -9746,6 +9748,11 @@ const UninstallExtensionSchema = zod.z.object({
|
|
|
9746
9748
|
extensionId: zod.z.string().min(1, "Extension ID is required"),
|
|
9747
9749
|
projectDir: zod.z.string().optional()
|
|
9748
9750
|
});
|
|
9751
|
+
const UpdateExtensionSchema = zod.z.object({
|
|
9752
|
+
extensionId: zod.z.string().min(1, "Extension ID is required"),
|
|
9753
|
+
repositoryUrl: zod.z.string().url("Repository URL must be a valid URL"),
|
|
9754
|
+
projectDir: zod.z.string().optional()
|
|
9755
|
+
});
|
|
9749
9756
|
class ExtensionsApi extends BaseApi {
|
|
9750
9757
|
constructor(eventsHandler) {
|
|
9751
9758
|
super();
|
|
@@ -9808,6 +9815,22 @@ class ExtensionsApi extends BaseApi {
|
|
|
9808
9815
|
}
|
|
9809
9816
|
})
|
|
9810
9817
|
);
|
|
9818
|
+
router.post(
|
|
9819
|
+
"/extensions/update",
|
|
9820
|
+
this.handleRequest(async (req, res) => {
|
|
9821
|
+
const parsed = this.validateRequest(UpdateExtensionSchema, req.body, res);
|
|
9822
|
+
if (!parsed) {
|
|
9823
|
+
return;
|
|
9824
|
+
}
|
|
9825
|
+
const { extensionId, repositoryUrl, projectDir } = parsed;
|
|
9826
|
+
const success = await this.eventsHandler.updateExtension(extensionId, repositoryUrl, projectDir);
|
|
9827
|
+
if (success) {
|
|
9828
|
+
res.status(200).json({ message: "Extension updated successfully", success });
|
|
9829
|
+
} else {
|
|
9830
|
+
res.status(500).json({ message: "Failed to update extension", success });
|
|
9831
|
+
}
|
|
9832
|
+
})
|
|
9833
|
+
);
|
|
9811
9834
|
router.get(
|
|
9812
9835
|
"/extensions/ui-components",
|
|
9813
9836
|
this.handleRequest(async (req, res) => {
|
|
@@ -10849,12 +10872,18 @@ class ContextManager {
|
|
|
10849
10872
|
return isFileIgnored(this.task.getTaskDir(), contextFile.path);
|
|
10850
10873
|
}
|
|
10851
10874
|
async addContextFile(contextFile) {
|
|
10852
|
-
const absolutePath =
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10875
|
+
const absolutePath = await this.resolveContextFilePath(contextFile.path);
|
|
10876
|
+
if (!absolutePath) {
|
|
10877
|
+
logger.debug("Skipping non-existing file for task:", {
|
|
10878
|
+
taskId: this.taskId,
|
|
10879
|
+
path: contextFile.path
|
|
10880
|
+
});
|
|
10856
10881
|
return [];
|
|
10857
10882
|
}
|
|
10883
|
+
if (this.isContextFileAlreadyAdded(absolutePath)) {
|
|
10884
|
+
return [];
|
|
10885
|
+
}
|
|
10886
|
+
const isDir = await isDirectory(absolutePath);
|
|
10858
10887
|
if (isDir) {
|
|
10859
10888
|
logger.debug("Recursively adding files in directory to task context:", {
|
|
10860
10889
|
taskId: this.taskId,
|
|
@@ -10882,14 +10911,6 @@ class ContextManager {
|
|
|
10882
10911
|
}
|
|
10883
10912
|
return addedFiles;
|
|
10884
10913
|
} else {
|
|
10885
|
-
if (!await fileExists(absolutePath)) {
|
|
10886
|
-
logger.debug("Skipping non-existing file for task:", {
|
|
10887
|
-
taskId: this.taskId,
|
|
10888
|
-
path: contextFile.path,
|
|
10889
|
-
absolutePath
|
|
10890
|
-
});
|
|
10891
|
-
return [];
|
|
10892
|
-
}
|
|
10893
10914
|
if (await this.isFileIgnored(contextFile)) {
|
|
10894
10915
|
logger.debug("Skipping ignored file for task:", {
|
|
10895
10916
|
taskId: this.taskId,
|
|
@@ -10910,6 +10931,24 @@ class ContextManager {
|
|
|
10910
10931
|
return [newContextFile];
|
|
10911
10932
|
}
|
|
10912
10933
|
}
|
|
10934
|
+
/**
|
|
10935
|
+
* Resolves a relative file path by delegating to Task.resolveContextFilePath
|
|
10936
|
+
* which tries taskDir first, then falls back to projectDir.
|
|
10937
|
+
*/
|
|
10938
|
+
async resolveContextFilePath(relativePath) {
|
|
10939
|
+
return this.task.resolveContextFilePath(relativePath);
|
|
10940
|
+
}
|
|
10941
|
+
/**
|
|
10942
|
+
* Checks whether a resolved absolute path matches any already-added context file.
|
|
10943
|
+
* Accounts for files that may have been resolved from either taskDir or projectDir.
|
|
10944
|
+
*/
|
|
10945
|
+
isContextFileAlreadyAdded(absolutePath) {
|
|
10946
|
+
return this.files.some((file) => {
|
|
10947
|
+
const taskDirResolved = path.resolve(this.task.getTaskDir(), file.path);
|
|
10948
|
+
const projectDirResolved = path.resolve(this.task.getProjectDir(), file.path);
|
|
10949
|
+
return taskDirResolved === absolutePath || projectDirResolved === absolutePath;
|
|
10950
|
+
});
|
|
10951
|
+
}
|
|
10913
10952
|
dropContextFile(filePath) {
|
|
10914
10953
|
const absolutePath = path.resolve(this.task.getTaskDir(), filePath);
|
|
10915
10954
|
const droppedFiles = [];
|
|
@@ -12249,6 +12288,7 @@ class WorktreeManager {
|
|
|
12249
12288
|
await fs.mkdir(worktreePath, { recursive: true });
|
|
12250
12289
|
} catch (error) {
|
|
12251
12290
|
logger.error("Failed to create worktree directory:", error);
|
|
12291
|
+
throw error;
|
|
12252
12292
|
}
|
|
12253
12293
|
}
|
|
12254
12294
|
async createWorktree(projectPath, taskId, branch, baseBranch = "HEAD") {
|
|
@@ -13268,8 +13308,16 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
13268
13308
|
try {
|
|
13269
13309
|
const commitMap = /* @__PURE__ */ new Map();
|
|
13270
13310
|
let diffCommand = "git diff --numstat -z HEAD";
|
|
13311
|
+
let mergeBase = "";
|
|
13271
13312
|
if (workingMode === "worktree" && mainBranch) {
|
|
13272
|
-
|
|
13313
|
+
mergeBase = mainBranch;
|
|
13314
|
+
try {
|
|
13315
|
+
const { stdout: baseOutput } = await execWithShellPath(`git merge-base HEAD ${mainBranch}`, { cwd: worktreePath });
|
|
13316
|
+
mergeBase = baseOutput.trim();
|
|
13317
|
+
} catch (mergeBaseError) {
|
|
13318
|
+
logger.warn("Failed to get merge-base, falling back to main branch:", mergeBaseError);
|
|
13319
|
+
}
|
|
13320
|
+
diffCommand = `git diff --numstat -z ${mergeBase}`;
|
|
13273
13321
|
try {
|
|
13274
13322
|
logger.debug("Getting commit information for worktree", { worktreePath, mainBranch });
|
|
13275
13323
|
const { stdout: logOutput } = await execWithShellPath(`git log --pretty=format:'%H|%s' --name-only ${mainBranch}..HEAD`, { cwd: worktreePath });
|
|
@@ -13324,7 +13372,7 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
13324
13372
|
}
|
|
13325
13373
|
}
|
|
13326
13374
|
const escapedPath = filePath.replace(/"/g, '\\"');
|
|
13327
|
-
const gitDiffCmd = workingMode === "worktree" && mainBranch ? `git diff --unified=3 ${
|
|
13375
|
+
const gitDiffCmd = workingMode === "worktree" && mainBranch ? `git diff --unified=3 ${mergeBase} -- "${escapedPath}"` : `git diff --unified=3 HEAD -- "${escapedPath}"`;
|
|
13328
13376
|
const { stdout: diffOutput } = await execWithShellPath(gitDiffCmd, {
|
|
13329
13377
|
cwd: worktreePath,
|
|
13330
13378
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -13358,24 +13406,22 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
13358
13406
|
try {
|
|
13359
13407
|
logger.info(`Restoring file: ${filePath}`, { worktreePath });
|
|
13360
13408
|
const escapedPath = filePath.replace(/"/g, '\\"');
|
|
13361
|
-
let
|
|
13409
|
+
let existsInHead = false;
|
|
13362
13410
|
try {
|
|
13363
|
-
await execWithShellPath(`git
|
|
13364
|
-
|
|
13365
|
-
} catch
|
|
13366
|
-
|
|
13367
|
-
const isUntrackedFileError = errorMessage.includes("did not match");
|
|
13368
|
-
if (isUntrackedFileError) {
|
|
13369
|
-
isTracked = false;
|
|
13370
|
-
} else {
|
|
13371
|
-
throw error;
|
|
13372
|
-
}
|
|
13411
|
+
await execWithShellPath(`git cat-file -e HEAD:"${escapedPath}"`, { cwd: worktreePath });
|
|
13412
|
+
existsInHead = true;
|
|
13413
|
+
} catch {
|
|
13414
|
+
existsInHead = false;
|
|
13373
13415
|
}
|
|
13374
|
-
if (
|
|
13375
|
-
await execWithShellPath(`git restore -- "${escapedPath}"`, {
|
|
13416
|
+
if (existsInHead) {
|
|
13417
|
+
await execWithShellPath(`git restore --staged --worktree --source=HEAD -- "${escapedPath}"`, {
|
|
13376
13418
|
cwd: worktreePath
|
|
13377
13419
|
});
|
|
13378
13420
|
} else {
|
|
13421
|
+
try {
|
|
13422
|
+
await execWithShellPath(`git rm --cached -f -- "${escapedPath}"`, { cwd: worktreePath });
|
|
13423
|
+
} catch {
|
|
13424
|
+
}
|
|
13379
13425
|
const absolutePath = path.join(worktreePath, filePath);
|
|
13380
13426
|
try {
|
|
13381
13427
|
const stats = await fs.lstat(absolutePath);
|
|
@@ -13699,7 +13745,7 @@ const getTaskFinishedNotificationText = (task) => {
|
|
|
13699
13745
|
${TaskStateEmoji[task.state] || ""} ${task.state.toLowerCase().split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ")}` : ""}`;
|
|
13700
13746
|
};
|
|
13701
13747
|
class Task {
|
|
13702
|
-
constructor(project, taskId, store, mcpManager, customCommandManager, agentProfileManager, telemetryManager, dataManager, eventManager, modelManager, worktreeManager, memoryManager,
|
|
13748
|
+
constructor(project, taskId, store, mcpManager, customCommandManager, agentProfileManager, telemetryManager, dataManager, eventManager, modelManager, worktreeManager, memoryManager, promptsManager, extensionManager, initialTaskData) {
|
|
13703
13749
|
this.project = project;
|
|
13704
13750
|
this.taskId = taskId;
|
|
13705
13751
|
this.store = store;
|
|
@@ -13712,7 +13758,6 @@ class Task {
|
|
|
13712
13758
|
this.modelManager = modelManager;
|
|
13713
13759
|
this.worktreeManager = worktreeManager;
|
|
13714
13760
|
this.memoryManager = memoryManager;
|
|
13715
|
-
this.hookManager = hookManager;
|
|
13716
13761
|
this.promptsManager = promptsManager;
|
|
13717
13762
|
this.extensionManager = extensionManager;
|
|
13718
13763
|
this.task = {
|
|
@@ -13902,10 +13947,12 @@ class Task {
|
|
|
13902
13947
|
} else {
|
|
13903
13948
|
const branchName = this.generateBranchName();
|
|
13904
13949
|
this.task.worktree = await this.worktreeManager.createWorktree(this.project.baseDir, this.taskId, branchName);
|
|
13950
|
+
void this.sendUpdatedFilesUpdated();
|
|
13905
13951
|
}
|
|
13906
13952
|
} else if (workingMode === "local") {
|
|
13907
13953
|
if (existingWorktree) {
|
|
13908
13954
|
await this.worktreeManager.removeWorktree(this.project.baseDir, existingWorktree);
|
|
13955
|
+
void this.sendUpdatedFilesUpdated();
|
|
13909
13956
|
}
|
|
13910
13957
|
} else {
|
|
13911
13958
|
logger.debug("Empty workingMode, setting to local", {
|
|
@@ -13933,7 +13980,6 @@ class Task {
|
|
|
13933
13980
|
await this.updateAutocompletionData();
|
|
13934
13981
|
this.eventManager.sendTaskInitialized(this.task);
|
|
13935
13982
|
this.initialized = true;
|
|
13936
|
-
await this.hookManager.trigger("onTaskInitialized", { task: this.task }, this, this.project);
|
|
13937
13983
|
await this.extensionManager.dispatchEvent("onTaskInitialized", { task: this.task }, this.project, this);
|
|
13938
13984
|
}
|
|
13939
13985
|
async load() {
|
|
@@ -14005,6 +14051,30 @@ class Task {
|
|
|
14005
14051
|
getTaskDir() {
|
|
14006
14052
|
return this.task.worktree ? this.task.worktree.path : this.project.baseDir;
|
|
14007
14053
|
}
|
|
14054
|
+
/**
|
|
14055
|
+
* Resolves a relative file path against taskDir first, then falls back to projectDir.
|
|
14056
|
+
* This handles git worktree cases where files may exist in the project directory
|
|
14057
|
+
* but not in the worktree (taskDir).
|
|
14058
|
+
*
|
|
14059
|
+
* @param relativePath - Relative file path to resolve
|
|
14060
|
+
* @returns The resolved absolute path if the file/dir exists in either location, or null otherwise
|
|
14061
|
+
*/
|
|
14062
|
+
async resolveContextFilePath(relativePath) {
|
|
14063
|
+
const taskDirAbsolutePath = path.resolve(this.getTaskDir(), relativePath);
|
|
14064
|
+
if (await fileExists(taskDirAbsolutePath) || await isDirectory(taskDirAbsolutePath)) {
|
|
14065
|
+
return taskDirAbsolutePath;
|
|
14066
|
+
}
|
|
14067
|
+
const projectDirAbsolutePath = path.resolve(this.getProjectDir(), relativePath);
|
|
14068
|
+
if (await fileExists(projectDirAbsolutePath) || await isDirectory(projectDirAbsolutePath)) {
|
|
14069
|
+
logger.debug("File not found in taskDir, falling back to projectDir:", {
|
|
14070
|
+
taskId: this.taskId,
|
|
14071
|
+
relativePath,
|
|
14072
|
+
projectDirAbsolutePath
|
|
14073
|
+
});
|
|
14074
|
+
return projectDirAbsolutePath;
|
|
14075
|
+
}
|
|
14076
|
+
return null;
|
|
14077
|
+
}
|
|
14008
14078
|
/**
|
|
14009
14079
|
* Dispatches an extension event through the ExtensionManager.
|
|
14010
14080
|
* This public method provides controlled access to extension event dispatch
|
|
@@ -14038,7 +14108,6 @@ class Task {
|
|
|
14038
14108
|
baseDir: this.project.baseDir,
|
|
14039
14109
|
taskId: this.taskId
|
|
14040
14110
|
});
|
|
14041
|
-
await this.hookManager.trigger("onTaskClosed", { task: this.task }, this, this.project);
|
|
14042
14111
|
await this.extensionManager.dispatchEvent("onTaskClosed", { task: this.task }, this.project, this);
|
|
14043
14112
|
if (clearContext) {
|
|
14044
14113
|
this.eventManager.sendClearTask(this.project.baseDir, this.taskId, true, true);
|
|
@@ -14125,13 +14194,6 @@ class Task {
|
|
|
14125
14194
|
this.eventManager.sendQueuedPromptsUpdated(this.project.baseDir, this.taskId, this.queuedPrompts);
|
|
14126
14195
|
return [];
|
|
14127
14196
|
}
|
|
14128
|
-
const hookResult = await this.hookManager.trigger("onPromptSubmitted", { prompt, mode }, this, this.project);
|
|
14129
|
-
if (hookResult.blocked) {
|
|
14130
|
-
logger.debug("Prompt blocked by hook");
|
|
14131
|
-
return [];
|
|
14132
|
-
}
|
|
14133
|
-
prompt = hookResult.event.prompt;
|
|
14134
|
-
mode = hookResult.event.mode;
|
|
14135
14197
|
let promptContext = {
|
|
14136
14198
|
id: userMessageId
|
|
14137
14199
|
};
|
|
@@ -14206,14 +14268,6 @@ class Task {
|
|
|
14206
14268
|
}
|
|
14207
14269
|
async runPromptInAider(mode, prompt, promptContext, sendNotification = true) {
|
|
14208
14270
|
await this.waitForCurrentPromptToFinish();
|
|
14209
|
-
await this.hookManager.trigger("onPromptStarted", { prompt, mode }, this, this.project);
|
|
14210
|
-
const aiderHookResult = await this.hookManager.trigger("onAiderPromptStarted", { prompt, mode }, this, this.project);
|
|
14211
|
-
if (aiderHookResult.blocked) {
|
|
14212
|
-
logger.debug("Aider prompt blocked by hook");
|
|
14213
|
-
return [];
|
|
14214
|
-
}
|
|
14215
|
-
prompt = aiderHookResult.event.prompt;
|
|
14216
|
-
mode = aiderHookResult.event.mode;
|
|
14217
14271
|
await this.saveTask({
|
|
14218
14272
|
name: this.task.name || this.getTaskNameFromPrompt(prompt),
|
|
14219
14273
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -14268,11 +14322,7 @@ class Task {
|
|
|
14268
14322
|
this.contextManager.addContextMessage(assistantMessage);
|
|
14269
14323
|
}
|
|
14270
14324
|
}
|
|
14271
|
-
|
|
14272
|
-
void this.sendWorktreeIntegrationStatusUpdated();
|
|
14273
|
-
void this.sendUpdatedFilesUpdated();
|
|
14274
|
-
await this.hookManager.trigger("onAiderPromptFinished", { responses }, this, this.project);
|
|
14275
|
-
await this.hookManager.trigger("onPromptFinished", { responses }, this, this.project);
|
|
14325
|
+
this.sendStateUpdated();
|
|
14276
14326
|
if (this.task.state === DefaultTaskState.InProgress) {
|
|
14277
14327
|
await this.saveTask({
|
|
14278
14328
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -14287,7 +14337,6 @@ class Task {
|
|
|
14287
14337
|
return responses;
|
|
14288
14338
|
}
|
|
14289
14339
|
async runPromptInAgent(profile, mode, prompt, promptContext = { id: uuid.v4() }, contextMessages, contextFiles, systemPrompt, waitForCurrentAgentToFinish = true, sendNotification = true) {
|
|
14290
|
-
await this.hookManager.trigger("onPromptStarted", { prompt, mode: "agent" }, this, this.project);
|
|
14291
14340
|
if (waitForCurrentAgentToFinish) {
|
|
14292
14341
|
await this.waitForCurrentAgentToFinish();
|
|
14293
14342
|
}
|
|
@@ -14302,7 +14351,6 @@ class Task {
|
|
|
14302
14351
|
this.sendAddMessage(message.role, message.content, false);
|
|
14303
14352
|
});
|
|
14304
14353
|
}
|
|
14305
|
-
await this.hookManager.trigger("onPromptFinished", { responses: [] }, this, this.project);
|
|
14306
14354
|
void this.sendRequestContextInfo();
|
|
14307
14355
|
void this.sendWorktreeIntegrationStatusUpdated();
|
|
14308
14356
|
void this.sendUpdatedFilesUpdated();
|
|
@@ -14445,12 +14493,6 @@ ${contentText}</agent-response>`;
|
|
|
14445
14493
|
this.registerSubagentAbortController(interruptId, abortController);
|
|
14446
14494
|
}
|
|
14447
14495
|
try {
|
|
14448
|
-
const hookResult = await this.hookManager.trigger("onSubagentStarted", { subagentId: profile.id, prompt }, this, this.project);
|
|
14449
|
-
if (hookResult.blocked) {
|
|
14450
|
-
logger.debug("Subagent execution blocked by hook");
|
|
14451
|
-
return [];
|
|
14452
|
-
}
|
|
14453
|
-
prompt = hookResult.event.prompt;
|
|
14454
14496
|
if (!contextMessages) {
|
|
14455
14497
|
contextMessages = await this.getContextMessages();
|
|
14456
14498
|
}
|
|
@@ -14491,10 +14533,6 @@ ${contentText}</agent-response>`;
|
|
|
14491
14533
|
false,
|
|
14492
14534
|
abortController?.signal
|
|
14493
14535
|
);
|
|
14494
|
-
const finishedHookResult = await this.hookManager.trigger("onSubagentFinished", { subagentId: profile.id, resultMessages }, this, this.project);
|
|
14495
|
-
if (finishedHookResult.event.resultMessages) {
|
|
14496
|
-
resultMessages = finishedHookResult.event.resultMessages;
|
|
14497
|
-
}
|
|
14498
14536
|
const subagentFinishedExtensionResult = await this.extensionManager.dispatchEvent(
|
|
14499
14537
|
"onSubagentFinished",
|
|
14500
14538
|
{ subagentProfile: profile, resultMessages },
|
|
@@ -14542,10 +14580,6 @@ ${contentText}</agent-response>`;
|
|
|
14542
14580
|
}
|
|
14543
14581
|
}
|
|
14544
14582
|
async processResponseMessage(message, saveToDb = true) {
|
|
14545
|
-
const hookResult = await this.hookManager.trigger("onResponseMessageProcessed", { message }, this, this.project);
|
|
14546
|
-
if (hookResult.result) {
|
|
14547
|
-
message = { ...message, ...hookResult.result };
|
|
14548
|
-
}
|
|
14549
14583
|
if (!message.finished) {
|
|
14550
14584
|
const sendResponseChunk = async (chunk) => {
|
|
14551
14585
|
let data = {
|
|
@@ -14679,7 +14713,6 @@ ${contentText}</agent-response>`;
|
|
|
14679
14713
|
if (!this.currentQuestion) {
|
|
14680
14714
|
return false;
|
|
14681
14715
|
}
|
|
14682
|
-
void this.hookManager.trigger("onQuestionAnswered", { question: this.currentQuestion, answer, userInput }, this, this.project);
|
|
14683
14716
|
const extensionResult = await this.extensionManager.dispatchEvent(
|
|
14684
14717
|
"onQuestionAnswered",
|
|
14685
14718
|
{ question: this.currentQuestion, answer, userInput },
|
|
@@ -14733,12 +14766,6 @@ ${contentText}</agent-response>`;
|
|
|
14733
14766
|
async addFiles(...contextFiles) {
|
|
14734
14767
|
const addedFiles = [];
|
|
14735
14768
|
for (let contextFile of contextFiles) {
|
|
14736
|
-
const hookResult = await this.hookManager.trigger("onFileAdded", { file: contextFile }, this, this.project);
|
|
14737
|
-
if (hookResult.blocked) {
|
|
14738
|
-
logger.debug("File addition blocked by hook");
|
|
14739
|
-
return false;
|
|
14740
|
-
}
|
|
14741
|
-
contextFile = hookResult.event.file;
|
|
14742
14769
|
const extensionResult = await this.extensionManager.dispatchEvent("onFilesAdded", { files: [contextFile] }, this.project, this);
|
|
14743
14770
|
if (extensionResult.files.length === 0) {
|
|
14744
14771
|
logger.debug("File addition blocked by extension (empty files array)");
|
|
@@ -14767,7 +14794,6 @@ ${contentText}</agent-response>`;
|
|
|
14767
14794
|
this.findMessageConnectors("add-file").forEach((connector) => connector.sendAddFileMessage(contextFile, noUpdate));
|
|
14768
14795
|
}
|
|
14769
14796
|
async dropFile(filePath) {
|
|
14770
|
-
await this.hookManager.trigger("onFileDropped", { filePath }, this, this.project);
|
|
14771
14797
|
await this.extensionManager.dispatchEvent("onFilesDropped", { files: [{ path: filePath }] }, this.project, this);
|
|
14772
14798
|
const normalizedPath = this.normalizeFilePath(filePath);
|
|
14773
14799
|
logger.info("Dropping file or folder:", { path: normalizedPath });
|
|
@@ -14800,18 +14826,18 @@ ${contentText}</agent-response>`;
|
|
|
14800
14826
|
this.addLogMessage("warning", `Failed to add new file ${absolutePath} to git staging area: ${gitErrorMessage}`, false, promptContext);
|
|
14801
14827
|
}
|
|
14802
14828
|
}
|
|
14829
|
+
sendStateUpdated() {
|
|
14830
|
+
void this.sendRequestContextInfo();
|
|
14831
|
+
void this.sendWorktreeIntegrationStatusUpdated();
|
|
14832
|
+
void this.sendUpdatedFilesUpdated();
|
|
14833
|
+
void this.sendContextFilesUpdated();
|
|
14834
|
+
}
|
|
14803
14835
|
async sendContextFilesUpdated() {
|
|
14804
14836
|
const mode = this.getCurrentMode();
|
|
14805
14837
|
const allFiles = await this.getContextFiles(!AIDER_MODES.includes(mode));
|
|
14806
14838
|
this.eventManager.sendContextFilesUpdated(this.project.baseDir, this.taskId, allFiles);
|
|
14807
14839
|
}
|
|
14808
14840
|
async runCommand(command, addToHistory = true) {
|
|
14809
|
-
const hookResult = await this.hookManager.trigger("onCommandExecuted", { command }, this, this.project);
|
|
14810
|
-
if (hookResult.blocked) {
|
|
14811
|
-
logger.debug("Command execution blocked by hook");
|
|
14812
|
-
return;
|
|
14813
|
-
}
|
|
14814
|
-
command = hookResult.event.command;
|
|
14815
14841
|
const extensionResult = await this.extensionManager.dispatchEvent("onCommandExecuted", { command }, this.project, this);
|
|
14816
14842
|
if (extensionResult.blocked) {
|
|
14817
14843
|
logger.debug("Command execution blocked by extension");
|
|
@@ -14883,14 +14909,6 @@ ${contentText}</agent-response>`;
|
|
|
14883
14909
|
void this.updateContextInfo(true, true);
|
|
14884
14910
|
}
|
|
14885
14911
|
async askQuestion(questionData, awaitAnswer = true) {
|
|
14886
|
-
const hookResult = await this.hookManager.trigger("onQuestionAsked", { question: questionData }, this, this.project);
|
|
14887
|
-
if (hookResult.result && typeof hookResult.result === "string") {
|
|
14888
|
-
logger.info("Question answered by hook", {
|
|
14889
|
-
question: questionData.text,
|
|
14890
|
-
answer: hookResult.result
|
|
14891
|
-
});
|
|
14892
|
-
return [hookResult.result, void 0];
|
|
14893
|
-
}
|
|
14894
14912
|
const extensionResult = await this.extensionManager.dispatchEvent("onQuestionAsked", { question: questionData }, this.project, this);
|
|
14895
14913
|
if (extensionResult.answer) {
|
|
14896
14914
|
logger.info("Question answered by extension", {
|
|
@@ -15987,7 +16005,7 @@ ${contentText}</agent-response>`;
|
|
|
15987
16005
|
});
|
|
15988
16006
|
return [];
|
|
15989
16007
|
}
|
|
15990
|
-
async initProjectAgentsFile() {
|
|
16008
|
+
async initProjectAgentsFile(args) {
|
|
15991
16009
|
logger.info("Initializing AGENTS.md file", {
|
|
15992
16010
|
baseDir: this.project.baseDir
|
|
15993
16011
|
});
|
|
@@ -16006,24 +16024,15 @@ ${contentText}</agent-response>`;
|
|
|
16006
16024
|
provider: activeProfile.provider,
|
|
16007
16025
|
model: activeProfile.model
|
|
16008
16026
|
};
|
|
16009
|
-
|
|
16027
|
+
const systemPrompt = await this.promptsManager.getInitProjectSystemPrompt(this);
|
|
16028
|
+
const userPrompt = args ? `/init ${args}` : "/init";
|
|
16029
|
+
await this.runPromptInAgent(initProjectRulesAgentProfile, "init-project-agents-file", userPrompt, { id: uuid.v4() }, void 0, void 0, systemPrompt);
|
|
16010
16030
|
const projectAgentsPath = path.join(this.project.baseDir, "AGENTS.md");
|
|
16011
16031
|
const projectAgentsFileExists = await fileExists(projectAgentsPath);
|
|
16012
16032
|
if (projectAgentsFileExists) {
|
|
16013
16033
|
logger.info("AGENTS.md file created successfully", {
|
|
16014
16034
|
path: projectAgentsPath
|
|
16015
16035
|
});
|
|
16016
|
-
this.addLogMessage("info", "AGENTS.md has been successfully initialized.");
|
|
16017
|
-
const [answer] = await this.askQuestion({
|
|
16018
|
-
baseDir: this.project.baseDir,
|
|
16019
|
-
taskId: this.taskId,
|
|
16020
|
-
text: "Do you want to add AGENTS.md as read-only file for Aider (in .aider.conf.yml)?",
|
|
16021
|
-
defaultAnswer: "y",
|
|
16022
|
-
internal: false
|
|
16023
|
-
});
|
|
16024
|
-
if (answer === "y") {
|
|
16025
|
-
await this.addProjectAgentsToAiderConfig();
|
|
16026
|
-
}
|
|
16027
16036
|
} else {
|
|
16028
16037
|
logger.warn("AGENTS.md file was not created");
|
|
16029
16038
|
this.addLogMessage("warning", "AGENTS.md file was not created.");
|
|
@@ -16037,37 +16046,6 @@ ${contentText}</agent-response>`;
|
|
|
16037
16046
|
this.contextManager.setContextMessages(messages, false);
|
|
16038
16047
|
}
|
|
16039
16048
|
}
|
|
16040
|
-
async addProjectAgentsToAiderConfig() {
|
|
16041
|
-
const aiderConfigPath = path.join(this.project.baseDir, ".aider.conf.yml");
|
|
16042
|
-
const projectAgentsRelativePath = "AGENTS.md";
|
|
16043
|
-
try {
|
|
16044
|
-
let config = {};
|
|
16045
|
-
if (await fileExists(aiderConfigPath)) {
|
|
16046
|
-
const configContent = await fs.readFile(aiderConfigPath, "utf8");
|
|
16047
|
-
config = YAML.parse(configContent) || {};
|
|
16048
|
-
}
|
|
16049
|
-
if (!config.read) {
|
|
16050
|
-
config.read = [];
|
|
16051
|
-
} else if (!Array.isArray(config.read)) {
|
|
16052
|
-
config.read = [config.read];
|
|
16053
|
-
}
|
|
16054
|
-
if (!config.read.includes(projectAgentsRelativePath)) {
|
|
16055
|
-
config.read.push(projectAgentsRelativePath);
|
|
16056
|
-
const yamlContent = YAML.stringify(config);
|
|
16057
|
-
await fs.writeFile(aiderConfigPath, yamlContent, "utf8");
|
|
16058
|
-
logger.info("Added AGENTS.md to .aider.conf.yml", {
|
|
16059
|
-
path: aiderConfigPath
|
|
16060
|
-
});
|
|
16061
|
-
this.addLogMessage("info", `Added ${projectAgentsRelativePath} to .aider.conf.yml`);
|
|
16062
|
-
} else {
|
|
16063
|
-
logger.info("AGENTS.md already exists in .aider.conf.yml");
|
|
16064
|
-
}
|
|
16065
|
-
} catch (error) {
|
|
16066
|
-
logger.error("Error updating .aider.conf.yml:", error);
|
|
16067
|
-
this.addLogMessage("error", `Failed to update .aider.conf.yml: ${error instanceof Error ? error.message : String(error)}`);
|
|
16068
|
-
throw error;
|
|
16069
|
-
}
|
|
16070
|
-
}
|
|
16071
16049
|
async runExtensionCommand(extensionCommand, args, mode) {
|
|
16072
16050
|
const { command } = extensionCommand;
|
|
16073
16051
|
logger.info("Running extension command:", {
|
|
@@ -16939,7 +16917,7 @@ const migrateSessionFile = async (project, sessionsDirPath, tasksDirPath, sessio
|
|
|
16939
16917
|
}
|
|
16940
16918
|
};
|
|
16941
16919
|
class Project {
|
|
16942
|
-
constructor(baseDir, store, mcpManager, telemetryManager, dataManager, eventManager, modelManager, worktreeManager, agentProfileManager, memoryManager,
|
|
16920
|
+
constructor(baseDir, store, mcpManager, telemetryManager, dataManager, eventManager, modelManager, worktreeManager, agentProfileManager, memoryManager, promptsManager, extensionManager) {
|
|
16943
16921
|
this.baseDir = baseDir;
|
|
16944
16922
|
this.store = store;
|
|
16945
16923
|
this.mcpManager = mcpManager;
|
|
@@ -16950,7 +16928,6 @@ class Project {
|
|
|
16950
16928
|
this.worktreeManager = worktreeManager;
|
|
16951
16929
|
this.agentProfileManager = agentProfileManager;
|
|
16952
16930
|
this.memoryManager = memoryManager;
|
|
16953
|
-
this.hookManager = hookManager;
|
|
16954
16931
|
this.promptsManager = promptsManager;
|
|
16955
16932
|
this.extensionManager = extensionManager;
|
|
16956
16933
|
this.customCommandManager = new CustomCommandManager(this, this.eventManager, this.extensionManager);
|
|
@@ -16992,6 +16969,7 @@ class Project {
|
|
|
16992
16969
|
}
|
|
16993
16970
|
}
|
|
16994
16971
|
const sourceTask = parentTask || this.getMostRecentTask();
|
|
16972
|
+
const defaultWorkingMode = this.store.getSettings().taskSettings.defaultWorkingMode || "local";
|
|
16995
16973
|
let initialTaskData;
|
|
16996
16974
|
if (sourceTask) {
|
|
16997
16975
|
initialTaskData = {
|
|
@@ -17005,6 +16983,7 @@ class Project {
|
|
|
17005
16983
|
provider: parentTask?.task.provider,
|
|
17006
16984
|
model: parentTask?.task.model,
|
|
17007
16985
|
weakModelLocked: sourceTask.task.weakModelLocked,
|
|
16986
|
+
workingMode: defaultWorkingMode,
|
|
17008
16987
|
...parentTask ? {
|
|
17009
16988
|
workingMode: parentTask.task.workingMode,
|
|
17010
16989
|
worktree: parentTask.task.worktree
|
|
@@ -17014,9 +16993,10 @@ class Project {
|
|
|
17014
16993
|
} else {
|
|
17015
16994
|
const providerModels = await this.modelManager.getProviderModels();
|
|
17016
16995
|
initialTaskData = {
|
|
17017
|
-
mainModel: determineMainModel(this.store.getSettings(), this.
|
|
16996
|
+
mainModel: determineMainModel(this.store.getSettings(), this.modelManager.getProviders(), providerModels.models || [], this.baseDir),
|
|
17018
16997
|
weakModel: determineWeakModel(this.baseDir),
|
|
17019
16998
|
currentMode: "agent",
|
|
16999
|
+
workingMode: defaultWorkingMode,
|
|
17020
17000
|
...normalizedParams
|
|
17021
17001
|
};
|
|
17022
17002
|
}
|
|
@@ -17056,13 +17036,11 @@ class Project {
|
|
|
17056
17036
|
this.modelManager,
|
|
17057
17037
|
this.worktreeManager,
|
|
17058
17038
|
this.memoryManager,
|
|
17059
|
-
this.hookManager,
|
|
17060
17039
|
this.promptsManager,
|
|
17061
17040
|
this.extensionManager,
|
|
17062
17041
|
initialTaskData
|
|
17063
17042
|
);
|
|
17064
17043
|
this.tasks.set(taskId, task);
|
|
17065
|
-
void task.hookManager.trigger("onTaskCreated", { task: task.task }, task, this);
|
|
17066
17044
|
const extResult = await this.extensionManager.dispatchEvent("onTaskPrepared", { task: task.task }, this, task);
|
|
17067
17045
|
if (extResult.task) {
|
|
17068
17046
|
Object.assign(task.task, extResult.task);
|
|
@@ -17299,7 +17277,6 @@ class Project {
|
|
|
17299
17277
|
await this.extensionManager.dispatchEvent("onProjectStopped", { baseDir: this.baseDir }, this);
|
|
17300
17278
|
this.customCommandManager.dispose();
|
|
17301
17279
|
this.agentProfileManager.removeProject(this.baseDir);
|
|
17302
|
-
await this.hookManager.stopWatchingProject(this.baseDir);
|
|
17303
17280
|
await this.promptsManager.unwatchProject(this.baseDir);
|
|
17304
17281
|
this.extensionManager.stopProjectWatcher(this.baseDir);
|
|
17305
17282
|
await Promise.all(Array.from(this.tasks.values()).map((task) => task.close()));
|
|
@@ -17316,7 +17293,7 @@ class Project {
|
|
|
17316
17293
|
}
|
|
17317
17294
|
}
|
|
17318
17295
|
class ProjectManager {
|
|
17319
|
-
constructor(store, mcpManager, telemetryManager, dataManager, eventManager, modelManager, worktreeManager, agentProfileManager, memoryManager,
|
|
17296
|
+
constructor(store, mcpManager, telemetryManager, dataManager, eventManager, modelManager, worktreeManager, agentProfileManager, memoryManager, promptsManager, extensionManager) {
|
|
17320
17297
|
this.store = store;
|
|
17321
17298
|
this.mcpManager = mcpManager;
|
|
17322
17299
|
this.telemetryManager = telemetryManager;
|
|
@@ -17325,7 +17302,6 @@ class ProjectManager {
|
|
|
17325
17302
|
this.modelManager = modelManager;
|
|
17326
17303
|
this.agentProfileManager = agentProfileManager;
|
|
17327
17304
|
this.memoryManager = memoryManager;
|
|
17328
|
-
this.hookManager = hookManager;
|
|
17329
17305
|
this.promptsManager = promptsManager;
|
|
17330
17306
|
this.extensionManager = extensionManager;
|
|
17331
17307
|
this.worktreeManager = worktreeManager;
|
|
@@ -17348,7 +17324,6 @@ class ProjectManager {
|
|
|
17348
17324
|
this.worktreeManager,
|
|
17349
17325
|
this.agentProfileManager,
|
|
17350
17326
|
this.memoryManager,
|
|
17351
|
-
this.hookManager,
|
|
17352
17327
|
this.promptsManager,
|
|
17353
17328
|
this.extensionManager
|
|
17354
17329
|
);
|
|
@@ -21301,13 +21276,14 @@ class ModelManager {
|
|
|
21301
21276
|
"vertex-ai": vertexAiProviderStrategy,
|
|
21302
21277
|
"zai-plan": zaiPlanProviderStrategy
|
|
21303
21278
|
};
|
|
21279
|
+
extensionProviders = /* @__PURE__ */ new Map();
|
|
21304
21280
|
async init() {
|
|
21305
21281
|
try {
|
|
21306
21282
|
logger.info("Initializing ModelInfoManager...");
|
|
21307
21283
|
this.updateEnvVarsProviders();
|
|
21308
21284
|
await this.loadModelsInfo();
|
|
21309
21285
|
await this.loadModelOverrides();
|
|
21310
|
-
await this.loadProviderModels(this.
|
|
21286
|
+
await this.loadProviderModels(this.getProviders().filter((p) => !p.disabled));
|
|
21311
21287
|
logger.info("ModelInfoManager initialized successfully.", {
|
|
21312
21288
|
modelCount: Object.keys(this.modelsInfo).length
|
|
21313
21289
|
});
|
|
@@ -21545,7 +21521,8 @@ class ModelManager {
|
|
|
21545
21521
|
this.providerModels = {};
|
|
21546
21522
|
this.providerErrors = {};
|
|
21547
21523
|
}
|
|
21548
|
-
|
|
21524
|
+
const allProviders = this.getProviders();
|
|
21525
|
+
await this.loadProviderModels(allProviders.filter((p) => !p.disabled));
|
|
21549
21526
|
}
|
|
21550
21527
|
return {
|
|
21551
21528
|
models: Object.values(this.providerModels).flat(),
|
|
@@ -21604,7 +21581,7 @@ class ModelManager {
|
|
|
21604
21581
|
logger.info(`Added model override: ${providerId}/${modelId}`);
|
|
21605
21582
|
}
|
|
21606
21583
|
await this.saveModelOverrides();
|
|
21607
|
-
await this.loadProviderModels(this.
|
|
21584
|
+
await this.loadProviderModels(this.getProviders().filter((provider) => provider.id === providerId));
|
|
21608
21585
|
}
|
|
21609
21586
|
async deleteModel(providerId, modelId) {
|
|
21610
21587
|
await this.initPromise;
|
|
@@ -21616,7 +21593,7 @@ class ModelManager {
|
|
|
21616
21593
|
if (this.modelOverrides.length < initialLength) {
|
|
21617
21594
|
await this.saveModelOverrides();
|
|
21618
21595
|
logger.info(`Deleted model override: ${providerId}/${modelId}`);
|
|
21619
|
-
await this.loadProviderModels(this.
|
|
21596
|
+
await this.loadProviderModels(this.getProviders().filter((provider) => provider.id === providerId));
|
|
21620
21597
|
} else {
|
|
21621
21598
|
logger.warn(`Model override not found for deletion: ${providerId}/${modelId}`);
|
|
21622
21599
|
}
|
|
@@ -21644,12 +21621,12 @@ class ModelManager {
|
|
|
21644
21621
|
affectedProviderIds.add(providerId);
|
|
21645
21622
|
}
|
|
21646
21623
|
await this.saveModelOverrides();
|
|
21647
|
-
const affectedProviders = this.
|
|
21624
|
+
const affectedProviders = this.getProviders().filter((provider) => affectedProviderIds.has(provider.id));
|
|
21648
21625
|
await this.loadProviderModels(affectedProviders);
|
|
21649
21626
|
logger.info(`Bulk updated ${modelUpdates.length} model overrides for ${affectedProviderIds.size} providers`);
|
|
21650
21627
|
}
|
|
21651
21628
|
getAiderModelMapping(modelName, projectDir) {
|
|
21652
|
-
const providers = this.
|
|
21629
|
+
const providers = this.getProviders();
|
|
21653
21630
|
const [providerId, modelId] = extractProviderModel(modelName);
|
|
21654
21631
|
if (!providerId || !modelId) {
|
|
21655
21632
|
logger.error("Invalid provider/model format:", modelName);
|
|
@@ -21862,6 +21839,75 @@ class ModelManager {
|
|
|
21862
21839
|
}
|
|
21863
21840
|
return strategy.isRetryable(error);
|
|
21864
21841
|
}
|
|
21842
|
+
registerExtensionProviders(providers) {
|
|
21843
|
+
const newProfiles = [];
|
|
21844
|
+
for (const registered of providers) {
|
|
21845
|
+
const { provider } = registered;
|
|
21846
|
+
this.providerRegistry[provider.provider.name] = {
|
|
21847
|
+
...provider.strategy,
|
|
21848
|
+
createLlm: (profile2, model, settings, projectDir) => provider.strategy.createLlm(profile2, model, settings, projectDir),
|
|
21849
|
+
getUsageReport: provider.strategy.getUsageReport || getDefaultUsageReport,
|
|
21850
|
+
getProviderOptions: provider.strategy.getProviderOptions ? (_provider, model) => provider.strategy.getProviderOptions(model) : void 0,
|
|
21851
|
+
getProviderTools: provider.strategy.getProviderTools ? (_provider, model) => provider.strategy.getProviderTools(model) : void 0,
|
|
21852
|
+
getProviderParameters: provider.strategy.getProviderParameters ? (_provider, model) => provider.strategy.getProviderParameters(model) : void 0,
|
|
21853
|
+
getCacheControl: provider.strategy.getCacheControl ? (profile2) => provider.strategy.getCacheControl(profile2) : void 0,
|
|
21854
|
+
hasEnvVars: () => false,
|
|
21855
|
+
getAiderMapping: provider.strategy.getAiderMapping ? provider.strategy.getAiderMapping : (_provider, modelId) => ({
|
|
21856
|
+
modelName: modelId,
|
|
21857
|
+
environmentVariables: {}
|
|
21858
|
+
})
|
|
21859
|
+
};
|
|
21860
|
+
const profile = {
|
|
21861
|
+
id: provider.id,
|
|
21862
|
+
name: provider.name,
|
|
21863
|
+
provider: provider.provider,
|
|
21864
|
+
headers: provider.headers,
|
|
21865
|
+
extensionId: registered.extensionId
|
|
21866
|
+
};
|
|
21867
|
+
logger.info(`Registering extension provider: ${profile.name} (ID: ${profile.id}) from extension ${registered.extensionId}`);
|
|
21868
|
+
this.extensionProviders.set(`${registered.extensionId}:${provider.id}`, {
|
|
21869
|
+
provider,
|
|
21870
|
+
profile
|
|
21871
|
+
});
|
|
21872
|
+
newProfiles.push(profile);
|
|
21873
|
+
}
|
|
21874
|
+
if (newProfiles.length > 0) {
|
|
21875
|
+
logger.info(`[Models] Registered ${newProfiles.length} extension provider(s)`);
|
|
21876
|
+
void this.loadProviderModels(newProfiles);
|
|
21877
|
+
this.eventManager.sendProvidersUpdated(this.getProviders());
|
|
21878
|
+
}
|
|
21879
|
+
}
|
|
21880
|
+
unregisterExtensionProviders(extensionId) {
|
|
21881
|
+
const toRemove = [];
|
|
21882
|
+
for (const [key, entry] of this.extensionProviders) {
|
|
21883
|
+
if (entry.profile.extensionId === extensionId) {
|
|
21884
|
+
toRemove.push(key);
|
|
21885
|
+
}
|
|
21886
|
+
}
|
|
21887
|
+
if (toRemove.length === 0) {
|
|
21888
|
+
return;
|
|
21889
|
+
}
|
|
21890
|
+
for (const key of toRemove) {
|
|
21891
|
+
const entry = this.extensionProviders.get(key);
|
|
21892
|
+
delete this.providerRegistry[entry.provider.provider.name];
|
|
21893
|
+
delete this.providerModels[entry.profile.id];
|
|
21894
|
+
delete this.providerErrors[entry.profile.id];
|
|
21895
|
+
this.extensionProviders.delete(key);
|
|
21896
|
+
}
|
|
21897
|
+
logger.info(`[Models] Unregistered ${toRemove.length} extension provider(s) for extension ${extensionId}`);
|
|
21898
|
+
this.eventManager.sendProviderModelsUpdated({
|
|
21899
|
+
models: Object.values(this.providerModels).flat(),
|
|
21900
|
+
loading: false,
|
|
21901
|
+
errors: this.providerErrors
|
|
21902
|
+
});
|
|
21903
|
+
this.eventManager.sendProvidersUpdated(this.getProviders());
|
|
21904
|
+
}
|
|
21905
|
+
getProviders() {
|
|
21906
|
+
return [...this.store.getProviders(), ...this.getExtensionProviderProfiles()];
|
|
21907
|
+
}
|
|
21908
|
+
getExtensionProviderProfiles() {
|
|
21909
|
+
return [...this.extensionProviders.values()].map((e) => e.profile);
|
|
21910
|
+
}
|
|
21865
21911
|
}
|
|
21866
21912
|
class DataManager {
|
|
21867
21913
|
db;
|
|
@@ -22011,9 +22057,8 @@ class DataManager {
|
|
|
22011
22057
|
}
|
|
22012
22058
|
}
|
|
22013
22059
|
class TerminalManager {
|
|
22014
|
-
constructor(eventManager,
|
|
22060
|
+
constructor(eventManager, telemetryManager) {
|
|
22015
22061
|
this.eventManager = eventManager;
|
|
22016
|
-
this.worktreeManager = worktreeManager;
|
|
22017
22062
|
this.telemetryManager = telemetryManager;
|
|
22018
22063
|
}
|
|
22019
22064
|
terminals = /* @__PURE__ */ new Map();
|
|
@@ -22036,11 +22081,10 @@ class TerminalManager {
|
|
|
22036
22081
|
}
|
|
22037
22082
|
return [];
|
|
22038
22083
|
}
|
|
22039
|
-
async createTerminal(baseDir, taskId, cols = 80, rows = 24) {
|
|
22084
|
+
async createTerminal(baseDir, taskDir, taskId, cols = 80, rows = 24) {
|
|
22040
22085
|
const terminalId = uuid.v4();
|
|
22041
22086
|
try {
|
|
22042
|
-
const
|
|
22043
|
-
const cwd = worktree ? worktree.path : baseDir;
|
|
22087
|
+
const cwd = taskDir;
|
|
22044
22088
|
const shell = this.getShellCommand();
|
|
22045
22089
|
const args = this.getShellArgs();
|
|
22046
22090
|
logger.info("Creating terminal:", { terminalId, baseDir, cwd, shell, args, cols, rows });
|
|
@@ -23393,7 +23437,9 @@ class ExtensionFetcher {
|
|
|
23393
23437
|
return cached.extensions;
|
|
23394
23438
|
}
|
|
23395
23439
|
try {
|
|
23396
|
-
const
|
|
23440
|
+
const repoDir = await this.ensureRepoCloned(repoUrl);
|
|
23441
|
+
const extensionsPath = this.getExtensionsPath(repoUrl, repoDir);
|
|
23442
|
+
const extensions = await this.scanForExtensions(extensionsPath, repoUrl);
|
|
23397
23443
|
this.cache.set(repoUrl, {
|
|
23398
23444
|
timestamp: now,
|
|
23399
23445
|
extensions
|
|
@@ -23408,37 +23454,39 @@ class ExtensionFetcher {
|
|
|
23408
23454
|
throw error;
|
|
23409
23455
|
}
|
|
23410
23456
|
}
|
|
23411
|
-
|
|
23412
|
-
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "ext-repo-"));
|
|
23457
|
+
getRepoDirName(repoUrl) {
|
|
23413
23458
|
try {
|
|
23414
|
-
const
|
|
23415
|
-
|
|
23416
|
-
|
|
23417
|
-
|
|
23418
|
-
const extensionSubPath = this.getExtensionSubPath(repoUrl);
|
|
23419
|
-
if (extensionSubPath) {
|
|
23420
|
-
logger.info(`[ExtensionFetcher] Sparse cloning repository to ${tempDir}, path: ${extensionSubPath}`);
|
|
23421
|
-
await execWithShellPath(`git clone --depth 1 --sparse "${cloneUrl}" "${tempDir}"`, {
|
|
23422
|
-
cwd: os.tmpdir()
|
|
23423
|
-
});
|
|
23424
|
-
await execWithShellPath(`git -C "${tempDir}" sparse-checkout set "${extensionSubPath}"`, {
|
|
23425
|
-
cwd: tempDir
|
|
23426
|
-
});
|
|
23427
|
-
} else {
|
|
23428
|
-
logger.info(`[ExtensionFetcher] Cloning repository to ${tempDir}`);
|
|
23429
|
-
await execWithShellPath(`git clone --depth 1 "${cloneUrl}" "${tempDir}"`, {
|
|
23430
|
-
cwd: os.tmpdir()
|
|
23431
|
-
});
|
|
23459
|
+
const url = new URL(repoUrl);
|
|
23460
|
+
const pathParts = url.pathname.split("/").filter(Boolean);
|
|
23461
|
+
if (pathParts.length >= 2) {
|
|
23462
|
+
return `${pathParts[0]}-${pathParts[1]}`;
|
|
23432
23463
|
}
|
|
23433
|
-
|
|
23434
|
-
|
|
23435
|
-
|
|
23464
|
+
} catch {
|
|
23465
|
+
}
|
|
23466
|
+
return repoUrl.replace(/[^a-zA-Z0-9]/g, "-");
|
|
23467
|
+
}
|
|
23468
|
+
async ensureRepoCloned(repoUrl) {
|
|
23469
|
+
const cloneUrl = this.getCloneUrl(repoUrl);
|
|
23470
|
+
if (!cloneUrl) {
|
|
23471
|
+
throw new Error(`Invalid repository URL: ${repoUrl}`);
|
|
23472
|
+
}
|
|
23473
|
+
const dirName = this.getRepoDirName(repoUrl);
|
|
23474
|
+
const repoDir = path.join(EXTENSIONS_REPOS_CACHE_DIR, dirName);
|
|
23475
|
+
const dirExists = await this.fileExists(path.join(repoDir, ".git"));
|
|
23476
|
+
if (dirExists) {
|
|
23436
23477
|
try {
|
|
23437
|
-
|
|
23438
|
-
|
|
23439
|
-
|
|
23478
|
+
logger.debug(`[ExtensionFetcher] Pulling existing repository cache at ${repoDir}`);
|
|
23479
|
+
await execWithShellPath(`git -C "${repoDir}" pull`, { cwd: repoDir });
|
|
23480
|
+
return repoDir;
|
|
23481
|
+
} catch (pullError) {
|
|
23482
|
+
logger.warn(`[ExtensionFetcher] git pull failed for ${repoDir}, removing and re-cloning:`, pullError);
|
|
23483
|
+
await fs.rm(repoDir, { recursive: true, force: true });
|
|
23440
23484
|
}
|
|
23441
23485
|
}
|
|
23486
|
+
await fs.mkdir(EXTENSIONS_REPOS_CACHE_DIR, { recursive: true });
|
|
23487
|
+
logger.info(`[ExtensionFetcher] Cloning repository to ${repoDir}`);
|
|
23488
|
+
await execWithShellPath(`git clone "${cloneUrl}" "${repoDir}"`, { cwd: EXTENSIONS_REPOS_CACHE_DIR });
|
|
23489
|
+
return repoDir;
|
|
23442
23490
|
}
|
|
23443
23491
|
getCloneUrl(webUrl) {
|
|
23444
23492
|
try {
|
|
@@ -23470,17 +23518,11 @@ class ExtensionFetcher {
|
|
|
23470
23518
|
}
|
|
23471
23519
|
}
|
|
23472
23520
|
getExtensionsPath(repoUrl, clonedRepoPath) {
|
|
23473
|
-
|
|
23474
|
-
|
|
23475
|
-
|
|
23476
|
-
if (pathParts.length > 4 && pathParts[2] === "tree") {
|
|
23477
|
-
const extensionPath = pathParts.slice(4).join("/");
|
|
23478
|
-
return path.join(clonedRepoPath, extensionPath);
|
|
23479
|
-
}
|
|
23480
|
-
return clonedRepoPath;
|
|
23481
|
-
} catch {
|
|
23482
|
-
return clonedRepoPath;
|
|
23521
|
+
const subPath = this.getExtensionSubPath(repoUrl);
|
|
23522
|
+
if (subPath) {
|
|
23523
|
+
return path.join(clonedRepoPath, subPath);
|
|
23483
23524
|
}
|
|
23525
|
+
return clonedRepoPath;
|
|
23484
23526
|
}
|
|
23485
23527
|
async scanForExtensions(extensionsPath, repoUrl) {
|
|
23486
23528
|
const extensions = [];
|
|
@@ -23850,6 +23892,10 @@ class ExtensionManager {
|
|
|
23850
23892
|
reloadComponents: true
|
|
23851
23893
|
});
|
|
23852
23894
|
}
|
|
23895
|
+
const providers = this.getProviders(project);
|
|
23896
|
+
if (providers.length > 0) {
|
|
23897
|
+
this.modelManager.registerExtensionProviders(providers);
|
|
23898
|
+
}
|
|
23853
23899
|
this.debouncedNotifyListeners();
|
|
23854
23900
|
}
|
|
23855
23901
|
getExtensions(projectDir) {
|
|
@@ -23908,6 +23954,7 @@ class ExtensionManager {
|
|
|
23908
23954
|
}
|
|
23909
23955
|
}
|
|
23910
23956
|
this.registry.unregister(filePath);
|
|
23957
|
+
this.modelManager.unregisterExtensionProviders(extension.id);
|
|
23911
23958
|
logger.debug(`[Extensions] Unloaded extension: ${filePath}`);
|
|
23912
23959
|
}
|
|
23913
23960
|
findExtensionByPath(filePath) {
|
|
@@ -24336,6 +24383,39 @@ class ExtensionManager {
|
|
|
24336
24383
|
errors
|
|
24337
24384
|
};
|
|
24338
24385
|
}
|
|
24386
|
+
getProviders(project) {
|
|
24387
|
+
const collectedProviders = [];
|
|
24388
|
+
const allExtensions = this.registry.getExtensions(project?.baseDir);
|
|
24389
|
+
const extensions = this.filterEnabledExtensions(allExtensions);
|
|
24390
|
+
for (const loaded of extensions) {
|
|
24391
|
+
const { instance, metadata } = loaded;
|
|
24392
|
+
if (!instance.getProviders) {
|
|
24393
|
+
continue;
|
|
24394
|
+
}
|
|
24395
|
+
try {
|
|
24396
|
+
const context = new ExtensionContextImpl(loaded.id, metadata.name, this.store, this.modelManager, this.eventManager, project, void 0);
|
|
24397
|
+
const providers = instance.getProviders(context);
|
|
24398
|
+
if (!Array.isArray(providers)) {
|
|
24399
|
+
logger.error(`[Extensions] Extension '${metadata.name}' getProviders() did not return an array`);
|
|
24400
|
+
continue;
|
|
24401
|
+
}
|
|
24402
|
+
for (const provider of providers) {
|
|
24403
|
+
if (!provider.id || !provider.name || !provider.strategy) {
|
|
24404
|
+
logger.error(`[Extensions] Invalid provider from extension '${metadata.name}': missing id, name or strategy`);
|
|
24405
|
+
continue;
|
|
24406
|
+
}
|
|
24407
|
+
collectedProviders.push({
|
|
24408
|
+
extensionId: loaded.id,
|
|
24409
|
+
extensionName: metadata.name,
|
|
24410
|
+
provider
|
|
24411
|
+
});
|
|
24412
|
+
}
|
|
24413
|
+
} catch (error) {
|
|
24414
|
+
logger.error(`[Extensions] Failed to get providers from extension '${metadata.name}':`, error);
|
|
24415
|
+
}
|
|
24416
|
+
}
|
|
24417
|
+
return collectedProviders;
|
|
24418
|
+
}
|
|
24339
24419
|
getUIComponents(project, task) {
|
|
24340
24420
|
const collectedComponents = [];
|
|
24341
24421
|
const allExtensions = this.registry.getExtensions(project?.baseDir);
|
|
@@ -24506,7 +24586,7 @@ class ExtensionManager {
|
|
|
24506
24586
|
const allExtensions = [];
|
|
24507
24587
|
for (const repoUrl of repositories) {
|
|
24508
24588
|
try {
|
|
24509
|
-
const extensions = await this.fetcher.
|
|
24589
|
+
const extensions = await this.fetcher.fetchExtensionsFromRepo(repoUrl, true);
|
|
24510
24590
|
allExtensions.push(...extensions);
|
|
24511
24591
|
} catch (error) {
|
|
24512
24592
|
logger.error(`[ExtensionManager] Failed to fetch extensions from ${repoUrl}:`, error);
|
|
@@ -24549,33 +24629,19 @@ class ExtensionManager {
|
|
|
24549
24629
|
await fs.writeFile(targetPath, code, "utf-8");
|
|
24550
24630
|
logger.debug(`[Extensions] Installed single-file extension: ${extension.file}`);
|
|
24551
24631
|
} else if (extension.type === "folder" && extension.folder) {
|
|
24552
|
-
const
|
|
24553
|
-
|
|
24554
|
-
|
|
24555
|
-
|
|
24556
|
-
|
|
24557
|
-
}
|
|
24558
|
-
logger.debug(`[Extensions] Cloning repository to ${tempDir}`);
|
|
24559
|
-
await execWithShellPath(`git clone --depth 1 "${cloneUrl}" "${tempDir}"`);
|
|
24560
|
-
const extensionsPath = this.getExtensionsPath(repositoryUrl, tempDir);
|
|
24561
|
-
const sourcePath = path.join(extensionsPath, extension.folder);
|
|
24562
|
-
const targetPath = path.join(targetDir, extension.folder);
|
|
24563
|
-
if (!await this.fileExists(sourcePath)) {
|
|
24564
|
-
throw new Error(`Extension folder not found in repository: ${extension.folder}`);
|
|
24565
|
-
}
|
|
24566
|
-
await fs.cp(sourcePath, targetPath, { recursive: true });
|
|
24567
|
-
if (extension.hasDependencies) {
|
|
24568
|
-
logger.debug(`[Extensions] Installing dependencies for ${extension.name}...`);
|
|
24569
|
-
await this.installDependencies(targetPath);
|
|
24570
|
-
}
|
|
24571
|
-
logger.debug(`[Extensions] Installed folder extension: ${extension.folder}`);
|
|
24572
|
-
} finally {
|
|
24573
|
-
try {
|
|
24574
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
24575
|
-
} catch (cleanupError) {
|
|
24576
|
-
logger.warn(`[Extensions] Failed to cleanup temp directory ${tempDir}:`, cleanupError);
|
|
24577
|
-
}
|
|
24632
|
+
const repoDir = await this.fetcher.ensureRepoCloned(repositoryUrl);
|
|
24633
|
+
const extensionsPath = this.fetcher.getExtensionsPath(repositoryUrl, repoDir);
|
|
24634
|
+
const sourcePath = path.join(extensionsPath, extension.folder);
|
|
24635
|
+
const targetPath = path.join(targetDir, extension.folder);
|
|
24636
|
+
if (!await this.fileExists(sourcePath)) {
|
|
24637
|
+
throw new Error(`Extension folder not found in repository: ${extension.folder}`);
|
|
24578
24638
|
}
|
|
24639
|
+
await fs.cp(sourcePath, targetPath, { recursive: true });
|
|
24640
|
+
if (extension.hasDependencies) {
|
|
24641
|
+
logger.debug(`[Extensions] Installing dependencies for ${extension.name}...`);
|
|
24642
|
+
await this.installDependencies(targetPath);
|
|
24643
|
+
}
|
|
24644
|
+
logger.debug(`[Extensions] Installed folder extension: ${extension.folder}`);
|
|
24579
24645
|
}
|
|
24580
24646
|
await this.loadExtensionsForDir(targetDir, project);
|
|
24581
24647
|
logger.info(`[Extensions] Successfully installed ${extension.name}`);
|
|
@@ -24606,36 +24672,6 @@ class ExtensionManager {
|
|
|
24606
24672
|
child.on("error", reject);
|
|
24607
24673
|
});
|
|
24608
24674
|
}
|
|
24609
|
-
getCloneUrl(webUrl) {
|
|
24610
|
-
try {
|
|
24611
|
-
const url = new URL(webUrl);
|
|
24612
|
-
if (url.hostname !== "github.com") {
|
|
24613
|
-
return null;
|
|
24614
|
-
}
|
|
24615
|
-
const pathParts = url.pathname.split("/").filter(Boolean);
|
|
24616
|
-
if (pathParts.length < 2) {
|
|
24617
|
-
return null;
|
|
24618
|
-
}
|
|
24619
|
-
const owner = pathParts[0];
|
|
24620
|
-
const repo = pathParts[1];
|
|
24621
|
-
return `https://github.com/${owner}/${repo}.git`;
|
|
24622
|
-
} catch {
|
|
24623
|
-
return null;
|
|
24624
|
-
}
|
|
24625
|
-
}
|
|
24626
|
-
getExtensionsPath(repoUrl, clonedRepoPath) {
|
|
24627
|
-
try {
|
|
24628
|
-
const url = new URL(repoUrl);
|
|
24629
|
-
const pathParts = url.pathname.split("/").filter(Boolean);
|
|
24630
|
-
if (pathParts.length > 4 && pathParts[2] === "tree") {
|
|
24631
|
-
const extensionPath = pathParts.slice(4).join("/");
|
|
24632
|
-
return path.join(clonedRepoPath, extensionPath);
|
|
24633
|
-
}
|
|
24634
|
-
return clonedRepoPath;
|
|
24635
|
-
} catch {
|
|
24636
|
-
return clonedRepoPath;
|
|
24637
|
-
}
|
|
24638
|
-
}
|
|
24639
24675
|
/**
|
|
24640
24676
|
* Uninstall an extension
|
|
24641
24677
|
* @param extensionId - Extension identifier (filePath)
|
|
@@ -24674,10 +24710,72 @@ class ExtensionManager {
|
|
|
24674
24710
|
return false;
|
|
24675
24711
|
}
|
|
24676
24712
|
}
|
|
24713
|
+
/**
|
|
24714
|
+
* Update an extension from a repository, overwriting existing files
|
|
24715
|
+
* @param extensionId - Extension identifier (filePath of installed extension)
|
|
24716
|
+
* @param repositoryUrl - Repository URL where the extension is located
|
|
24717
|
+
* @param project - Optional project for project-level extension
|
|
24718
|
+
* @returns true if update succeeded
|
|
24719
|
+
*/
|
|
24720
|
+
async updateExtension(extensionId, repositoryUrl, project) {
|
|
24721
|
+
const projectDir = project?.baseDir;
|
|
24722
|
+
try {
|
|
24723
|
+
logger.debug(`[Extensions] Updating extension '${extensionId}' from ${repositoryUrl}`);
|
|
24724
|
+
const existingExtension = this.registry.getExtension(extensionId);
|
|
24725
|
+
if (!existingExtension) {
|
|
24726
|
+
throw new Error(`Extension '${extensionId}' not found`);
|
|
24727
|
+
}
|
|
24728
|
+
if (!existingExtension.filePath) {
|
|
24729
|
+
throw new Error(`Extension '${extensionId}' has no file path`);
|
|
24730
|
+
}
|
|
24731
|
+
const targetDir = projectDir ? path.join(projectDir, AIDER_DESK_EXTENSIONS_DIR) : AIDER_DESK_GLOBAL_EXTENSIONS_DIR;
|
|
24732
|
+
const availableExtensions = await this.getAvailableExtensions([repositoryUrl]);
|
|
24733
|
+
const extension = availableExtensions.find((ext) => ext.id === existingExtension.id);
|
|
24734
|
+
if (!extension) {
|
|
24735
|
+
throw new Error(`Extension '${existingExtension.id}' not found in repository`);
|
|
24736
|
+
}
|
|
24737
|
+
const githubRawBase = this.fetcher.getRawUrl(repositoryUrl);
|
|
24738
|
+
if (!githubRawBase) {
|
|
24739
|
+
throw new Error("Invalid GitHub repository URL");
|
|
24740
|
+
}
|
|
24741
|
+
await this.unloadExtensionsForDir(targetDir);
|
|
24742
|
+
if (extension.type === "single" && extension.file) {
|
|
24743
|
+
const url = `${githubRawBase}/${extension.file}`;
|
|
24744
|
+
const response = await fetch(url);
|
|
24745
|
+
if (!response.ok) {
|
|
24746
|
+
throw new Error(`Failed to download: ${response.statusText}`);
|
|
24747
|
+
}
|
|
24748
|
+
const code = await response.text();
|
|
24749
|
+
const targetPath = path.join(targetDir, extension.file);
|
|
24750
|
+
await fs.writeFile(targetPath, code, "utf-8");
|
|
24751
|
+
logger.debug(`[Extensions] Updated single-file extension: ${extension.file}`);
|
|
24752
|
+
} else if (extension.type === "folder" && extension.folder) {
|
|
24753
|
+
const repoDir = await this.fetcher.ensureRepoCloned(repositoryUrl);
|
|
24754
|
+
const extensionsPath = this.fetcher.getExtensionsPath(repositoryUrl, repoDir);
|
|
24755
|
+
const sourcePath = path.join(extensionsPath, extension.folder);
|
|
24756
|
+
const targetPath = path.join(targetDir, extension.folder);
|
|
24757
|
+
if (!await this.fileExists(sourcePath)) {
|
|
24758
|
+
throw new Error(`Extension folder not found in repository: ${extension.folder}`);
|
|
24759
|
+
}
|
|
24760
|
+
await fs.cp(sourcePath, targetPath, { recursive: true });
|
|
24761
|
+
const packageJsonPath = path.join(targetPath, "package.json");
|
|
24762
|
+
if (await this.fileExists(packageJsonPath)) {
|
|
24763
|
+
logger.debug(`[Extensions] Installing dependencies for ${extension.name}...`);
|
|
24764
|
+
await this.installDependencies(targetPath);
|
|
24765
|
+
}
|
|
24766
|
+
logger.debug(`[Extensions] Updated folder extension: ${extension.folder}`);
|
|
24767
|
+
}
|
|
24768
|
+
await this.loadExtensionsForDir(targetDir, project);
|
|
24769
|
+
logger.info(`[Extensions] Successfully updated ${extension.name}`);
|
|
24770
|
+
return true;
|
|
24771
|
+
} catch (error) {
|
|
24772
|
+
logger.error(`[Extensions] Failed to update extension '${extensionId}':`, error);
|
|
24773
|
+
return false;
|
|
24774
|
+
}
|
|
24775
|
+
}
|
|
24677
24776
|
/**
|
|
24678
24777
|
* Dispatches an event to all loaded and initialized extensions.
|
|
24679
|
-
*
|
|
24680
|
-
* to the same events as hooks. Events can be modified or blocked by extensions.
|
|
24778
|
+
* Events can be modified or blocked by extensions.
|
|
24681
24779
|
*
|
|
24682
24780
|
* @param eventName - The name of the event to dispatch
|
|
24683
24781
|
* @param event - The event payload
|
|
@@ -25062,7 +25160,9 @@ class EventsHandler {
|
|
|
25062
25160
|
}
|
|
25063
25161
|
async createTerminal(baseDir, taskId, cols, rows) {
|
|
25064
25162
|
try {
|
|
25065
|
-
|
|
25163
|
+
const task = this.projectManager.getProject(baseDir).getTask(taskId);
|
|
25164
|
+
const taskDir = task?.getTaskDir() ?? baseDir;
|
|
25165
|
+
return await this.terminalManager.createTerminal(baseDir, taskDir, taskId, cols, rows);
|
|
25066
25166
|
} catch (error) {
|
|
25067
25167
|
logger.error("Failed to create terminal:", { baseDir, error });
|
|
25068
25168
|
throw error;
|
|
@@ -25358,14 +25458,14 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
25358
25458
|
return providerModels;
|
|
25359
25459
|
}
|
|
25360
25460
|
getProviders() {
|
|
25361
|
-
return this.
|
|
25461
|
+
return this.modelManager.getProviders();
|
|
25362
25462
|
}
|
|
25363
25463
|
async updateProviders(providers) {
|
|
25364
25464
|
const oldProviders = this.store.getProviders();
|
|
25365
25465
|
this.store.setProviders(providers);
|
|
25366
25466
|
await this.modelManager.providersChanged(oldProviders, providers);
|
|
25367
25467
|
this.projectManager.modelsUpdated();
|
|
25368
|
-
this.eventManager.sendProvidersUpdated(
|
|
25468
|
+
this.eventManager.sendProvidersUpdated(this.modelManager.getProviders());
|
|
25369
25469
|
}
|
|
25370
25470
|
async upsertModel(providerId, modelId, model) {
|
|
25371
25471
|
await this.modelManager.upsertModel(providerId, modelId, model);
|
|
@@ -25453,8 +25553,8 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
25453
25553
|
async clearAllTodos(baseDir, taskId) {
|
|
25454
25554
|
return await this.projectManager.getProject(baseDir).getTask(taskId)?.clearAllTodos() || [];
|
|
25455
25555
|
}
|
|
25456
|
-
async initProjectRulesFile(baseDir, taskId) {
|
|
25457
|
-
return this.projectManager.getProject(baseDir).getTask(taskId)?.initProjectAgentsFile();
|
|
25556
|
+
async initProjectRulesFile(baseDir, taskId, args) {
|
|
25557
|
+
return this.projectManager.getProject(baseDir).getTask(taskId)?.initProjectAgentsFile(args);
|
|
25458
25558
|
}
|
|
25459
25559
|
async enableServer(username, password) {
|
|
25460
25560
|
const currentSettings = this.store.getSettings();
|
|
@@ -25571,6 +25671,16 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
25571
25671
|
async uninstallExtension(extensionId, projectDir) {
|
|
25572
25672
|
return await this.extensionManager.uninstallExtension(extensionId, projectDir);
|
|
25573
25673
|
}
|
|
25674
|
+
async updateExtension(extensionId, repositoryUrl, projectDir) {
|
|
25675
|
+
let project = void 0;
|
|
25676
|
+
if (projectDir) {
|
|
25677
|
+
project = this.projectManager.getProject(projectDir);
|
|
25678
|
+
if (!project) {
|
|
25679
|
+
throw new Error("Project not found");
|
|
25680
|
+
}
|
|
25681
|
+
}
|
|
25682
|
+
return await this.extensionManager.updateExtension(extensionId, repositoryUrl, project);
|
|
25683
|
+
}
|
|
25574
25684
|
getUIComponents(placement, projectDir, taskId) {
|
|
25575
25685
|
const project = projectDir ? this.projectManager.getProject(projectDir) : void 0;
|
|
25576
25686
|
const task = taskId && project ? project.getTask(taskId) ?? void 0 : void 0;
|
|
@@ -25609,216 +25719,6 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
25609
25719
|
return await this.extensionManager.executeUIExtensionAction(extensionId, componentId, action, args, project, task);
|
|
25610
25720
|
}
|
|
25611
25721
|
}
|
|
25612
|
-
class HookContextImpl {
|
|
25613
|
-
constructor(task, project) {
|
|
25614
|
-
this.task = task;
|
|
25615
|
-
this.project = project;
|
|
25616
|
-
}
|
|
25617
|
-
addInfoMessage(message) {
|
|
25618
|
-
this.task.addLogMessage("info", message);
|
|
25619
|
-
}
|
|
25620
|
-
addWarningMessage(message) {
|
|
25621
|
-
this.task.addLogMessage("warning", message);
|
|
25622
|
-
}
|
|
25623
|
-
addErrorMessage(message) {
|
|
25624
|
-
this.task.addLogMessage("error", message);
|
|
25625
|
-
}
|
|
25626
|
-
addLoadingMessage(message, finished = false) {
|
|
25627
|
-
this.task.addLogMessage("loading", message, finished);
|
|
25628
|
-
}
|
|
25629
|
-
async setTaskName(name) {
|
|
25630
|
-
await this.task.saveTask({ name });
|
|
25631
|
-
}
|
|
25632
|
-
async addContextMessage(role, content) {
|
|
25633
|
-
await this.task.addRoleContextMessage(role === "user" ? MessageRole.User : MessageRole.Assistant, content);
|
|
25634
|
-
}
|
|
25635
|
-
get taskData() {
|
|
25636
|
-
return this.task.task;
|
|
25637
|
-
}
|
|
25638
|
-
get projectDir() {
|
|
25639
|
-
return this.task.getProjectDir();
|
|
25640
|
-
}
|
|
25641
|
-
}
|
|
25642
|
-
class HookManager {
|
|
25643
|
-
globalHooks = [];
|
|
25644
|
-
projectHooksCache = /* @__PURE__ */ new Map();
|
|
25645
|
-
globalInitialized = false;
|
|
25646
|
-
globalWatcher = null;
|
|
25647
|
-
projectWatchers = /* @__PURE__ */ new Map();
|
|
25648
|
-
constructor() {
|
|
25649
|
-
}
|
|
25650
|
-
async init() {
|
|
25651
|
-
if (this.globalInitialized) {
|
|
25652
|
-
return;
|
|
25653
|
-
}
|
|
25654
|
-
this.globalHooks = await this.loadHooksFromDir(AIDER_DESK_GLOBAL_HOOKS_DIR);
|
|
25655
|
-
await this.setupGlobalWatcher();
|
|
25656
|
-
this.globalInitialized = true;
|
|
25657
|
-
}
|
|
25658
|
-
async setupGlobalWatcher() {
|
|
25659
|
-
if (this.globalWatcher) {
|
|
25660
|
-
await this.globalWatcher.close();
|
|
25661
|
-
}
|
|
25662
|
-
this.globalWatcher = await this.setupWatcherForDir(AIDER_DESK_GLOBAL_HOOKS_DIR, async () => {
|
|
25663
|
-
logger.debug("Global hooks changed, reloading...");
|
|
25664
|
-
this.globalHooks = await this.loadHooksFromDir(AIDER_DESK_GLOBAL_HOOKS_DIR);
|
|
25665
|
-
});
|
|
25666
|
-
}
|
|
25667
|
-
async reloadProjectHooks(projectDir) {
|
|
25668
|
-
const projectHooksDir = path.join(projectDir, AIDER_DESK_HOOKS_DIR);
|
|
25669
|
-
const hooks = await this.loadHooksFromDir(projectHooksDir);
|
|
25670
|
-
this.projectHooksCache.set(projectDir, hooks);
|
|
25671
|
-
if (!this.projectWatchers.has(projectDir)) {
|
|
25672
|
-
const watcher = await this.setupWatcherForDir(projectHooksDir, async () => {
|
|
25673
|
-
logger.debug(`Project hooks changed for ${projectDir}, reloading...`);
|
|
25674
|
-
const updatedHooks = await this.loadHooksFromDir(projectHooksDir);
|
|
25675
|
-
this.projectHooksCache.set(projectDir, updatedHooks);
|
|
25676
|
-
});
|
|
25677
|
-
if (watcher) {
|
|
25678
|
-
this.projectWatchers.set(projectDir, watcher);
|
|
25679
|
-
}
|
|
25680
|
-
}
|
|
25681
|
-
logger.debug(`Reloaded hooks for project: ${projectDir}`);
|
|
25682
|
-
}
|
|
25683
|
-
async stopWatchingProject(projectDir) {
|
|
25684
|
-
const watcher = this.projectWatchers.get(projectDir);
|
|
25685
|
-
if (watcher) {
|
|
25686
|
-
await watcher.close();
|
|
25687
|
-
this.projectWatchers.delete(projectDir);
|
|
25688
|
-
}
|
|
25689
|
-
this.projectHooksCache.delete(projectDir);
|
|
25690
|
-
logger.debug(`Stopped watching hooks for project: ${projectDir}`);
|
|
25691
|
-
}
|
|
25692
|
-
async setupWatcherForDir(dir, onChange) {
|
|
25693
|
-
try {
|
|
25694
|
-
const dirExists = await fs.access(dir).then(() => true).catch(() => false);
|
|
25695
|
-
if (!dirExists) {
|
|
25696
|
-
await fs.mkdir(dir, { recursive: true });
|
|
25697
|
-
}
|
|
25698
|
-
const debouncedOnChange = debounce(onChange, 1e3);
|
|
25699
|
-
const watcher = chokidar.watch(dir, {
|
|
25700
|
-
persistent: true,
|
|
25701
|
-
usePolling: true,
|
|
25702
|
-
ignoreInitial: true
|
|
25703
|
-
});
|
|
25704
|
-
watcher.on("add", debouncedOnChange).on("change", debouncedOnChange).on("unlink", debouncedOnChange).on("error", (error) => {
|
|
25705
|
-
logger.error(`Watcher error for hooks directory ${dir}:`, error);
|
|
25706
|
-
});
|
|
25707
|
-
return watcher;
|
|
25708
|
-
} catch (error) {
|
|
25709
|
-
logger.error(`Failed to setup watcher for hooks directory ${dir}:`, error);
|
|
25710
|
-
return null;
|
|
25711
|
-
}
|
|
25712
|
-
}
|
|
25713
|
-
async loadHooksFromDir(dir) {
|
|
25714
|
-
const hooks = [];
|
|
25715
|
-
try {
|
|
25716
|
-
const dirExists = await fs.access(dir).then(() => true).catch(() => false);
|
|
25717
|
-
if (!dirExists) {
|
|
25718
|
-
return hooks;
|
|
25719
|
-
}
|
|
25720
|
-
const files = await fs.readdir(dir);
|
|
25721
|
-
for (const file of files) {
|
|
25722
|
-
if (file.endsWith(".js")) {
|
|
25723
|
-
const filePath = path.join(dir, file);
|
|
25724
|
-
try {
|
|
25725
|
-
const resolvedPath = require.resolve(filePath);
|
|
25726
|
-
delete require.cache[resolvedPath];
|
|
25727
|
-
const hookFile = require(filePath);
|
|
25728
|
-
hooks.push(hookFile);
|
|
25729
|
-
logger.debug(`Loaded hook file: ${filePath}`);
|
|
25730
|
-
} catch (error) {
|
|
25731
|
-
logger.error(`Failed to load hook file ${filePath}:`, error);
|
|
25732
|
-
}
|
|
25733
|
-
}
|
|
25734
|
-
}
|
|
25735
|
-
} catch (error) {
|
|
25736
|
-
logger.error(`Failed to load hooks from ${dir}:`, error);
|
|
25737
|
-
}
|
|
25738
|
-
return hooks;
|
|
25739
|
-
}
|
|
25740
|
-
async trigger(hookName, event, task, project) {
|
|
25741
|
-
if (!this.globalInitialized) {
|
|
25742
|
-
await this.init();
|
|
25743
|
-
}
|
|
25744
|
-
let projectHooks = this.projectHooksCache.get(project.baseDir);
|
|
25745
|
-
if (projectHooks === void 0) {
|
|
25746
|
-
await this.reloadProjectHooks(project.baseDir);
|
|
25747
|
-
projectHooks = this.projectHooksCache.get(project.baseDir) || [];
|
|
25748
|
-
}
|
|
25749
|
-
const allHooks = [...this.globalHooks, ...projectHooks];
|
|
25750
|
-
const context = new HookContextImpl(task, project);
|
|
25751
|
-
let currentEvent = { ...event };
|
|
25752
|
-
let blocked = false;
|
|
25753
|
-
let hookResult = void 0;
|
|
25754
|
-
for (const hook of allHooks) {
|
|
25755
|
-
logger.debug(`Executing hook ${hookName}`, {
|
|
25756
|
-
hookName,
|
|
25757
|
-
event,
|
|
25758
|
-
hook
|
|
25759
|
-
});
|
|
25760
|
-
const hookFn = hook[hookName];
|
|
25761
|
-
if (typeof hookFn === "function") {
|
|
25762
|
-
logger.debug(`Hook function found for ${hookName}`, {
|
|
25763
|
-
hookName,
|
|
25764
|
-
event,
|
|
25765
|
-
hook
|
|
25766
|
-
});
|
|
25767
|
-
try {
|
|
25768
|
-
const result = await hookFn(currentEvent, context);
|
|
25769
|
-
logger.debug(`Hook function result for ${hookName}`, {
|
|
25770
|
-
hookName,
|
|
25771
|
-
event,
|
|
25772
|
-
hook,
|
|
25773
|
-
result
|
|
25774
|
-
});
|
|
25775
|
-
if (result === false) {
|
|
25776
|
-
if (hookName === "onHandleApproval") {
|
|
25777
|
-
hookResult = false;
|
|
25778
|
-
} else {
|
|
25779
|
-
blocked = true;
|
|
25780
|
-
}
|
|
25781
|
-
break;
|
|
25782
|
-
}
|
|
25783
|
-
if (result === true && hookName === "onHandleApproval") {
|
|
25784
|
-
hookResult = true;
|
|
25785
|
-
break;
|
|
25786
|
-
}
|
|
25787
|
-
if (typeof result === "string" && hookName === "onQuestionAsked") {
|
|
25788
|
-
hookResult = result;
|
|
25789
|
-
break;
|
|
25790
|
-
}
|
|
25791
|
-
if (result && typeof result === "object") {
|
|
25792
|
-
if (hookName === "onResponseMessageProcessed") {
|
|
25793
|
-
hookResult = result;
|
|
25794
|
-
} else {
|
|
25795
|
-
currentEvent = { ...currentEvent, ...result };
|
|
25796
|
-
}
|
|
25797
|
-
}
|
|
25798
|
-
if (result && hookName === "onToolFinished") {
|
|
25799
|
-
hookResult = result;
|
|
25800
|
-
currentEvent = { ...currentEvent, result };
|
|
25801
|
-
}
|
|
25802
|
-
} catch (error) {
|
|
25803
|
-
logger.error(`Error executing hook ${hookName}:`, error);
|
|
25804
|
-
}
|
|
25805
|
-
}
|
|
25806
|
-
}
|
|
25807
|
-
return { event: currentEvent, blocked, result: hookResult };
|
|
25808
|
-
}
|
|
25809
|
-
async dispose() {
|
|
25810
|
-
if (this.globalWatcher) {
|
|
25811
|
-
await this.globalWatcher.close();
|
|
25812
|
-
this.globalWatcher = null;
|
|
25813
|
-
}
|
|
25814
|
-
for (const watcher of this.projectWatchers.values()) {
|
|
25815
|
-
await watcher.close();
|
|
25816
|
-
}
|
|
25817
|
-
this.projectWatchers.clear();
|
|
25818
|
-
this.projectHooksCache.clear();
|
|
25819
|
-
logger.debug("HookManager disposed");
|
|
25820
|
-
}
|
|
25821
|
-
}
|
|
25822
25722
|
const registerConditionalHelpers = () => {
|
|
25823
25723
|
Handlebars.registerHelper("equals", (v1, v2) => v1 === v2);
|
|
25824
25724
|
Handlebars.registerHelper("not", (v) => !v);
|
|
@@ -26164,7 +26064,7 @@ ${content}
|
|
|
26164
26064
|
);
|
|
26165
26065
|
return ruleFilesContent.filter(Boolean).join("\n");
|
|
26166
26066
|
};
|
|
26167
|
-
|
|
26067
|
+
getInitProjectSystemPrompt = async (task) => {
|
|
26168
26068
|
const data = {};
|
|
26169
26069
|
return await this.render("init-project", data, task.getProjectDir(), task);
|
|
26170
26070
|
};
|
|
@@ -26231,10 +26131,6 @@ const initManagers = async (store, windowManager) => {
|
|
|
26231
26131
|
memoryManager.init().catch((error) => {
|
|
26232
26132
|
logger.error("[Memory] Memory system initialization failed, continuing without memories:", error);
|
|
26233
26133
|
});
|
|
26234
|
-
const hookManager = new HookManager();
|
|
26235
|
-
hookManager.init().catch((error) => {
|
|
26236
|
-
logger.error("[Hooks] Hook system initialization failed:", error);
|
|
26237
|
-
});
|
|
26238
26134
|
const extensionManager = new ExtensionManager(store, modelManager, eventManager, telemetryManager);
|
|
26239
26135
|
extensionManager.init().catch((error) => {
|
|
26240
26136
|
logger.error("[Extensions] Extension system initialization failed, continuing without extensions:", error);
|
|
@@ -26258,11 +26154,10 @@ const initManagers = async (store, windowManager) => {
|
|
|
26258
26154
|
worktreeManager,
|
|
26259
26155
|
agentProfileManager,
|
|
26260
26156
|
memoryManager,
|
|
26261
|
-
hookManager,
|
|
26262
26157
|
promptsManager,
|
|
26263
26158
|
extensionManager
|
|
26264
26159
|
);
|
|
26265
|
-
const terminalManager = new TerminalManager(eventManager,
|
|
26160
|
+
const terminalManager = new TerminalManager(eventManager, telemetryManager);
|
|
26266
26161
|
const versionsManager = new VersionsManager(eventManager, store);
|
|
26267
26162
|
const httpServer = http.createServer();
|
|
26268
26163
|
const cloudflareTunnelManager = new CloudflareTunnelManager();
|
|
@@ -26303,7 +26198,6 @@ const initManagers = async (store, windowManager) => {
|
|
|
26303
26198
|
mcpManager.close(),
|
|
26304
26199
|
telemetryManager.destroy(),
|
|
26305
26200
|
agentProfileManager.dispose(),
|
|
26306
|
-
hookManager.dispose(),
|
|
26307
26201
|
promptsManager.dispose(),
|
|
26308
26202
|
extensionManager.dispose()
|
|
26309
26203
|
]);
|
|
@@ -26936,10 +26830,12 @@ const migrateV12ToV13 = (settings) => {
|
|
|
26936
26830
|
};
|
|
26937
26831
|
};
|
|
26938
26832
|
const migrateProvidersV13toV14 = (settings) => {
|
|
26939
|
-
const providers = Object.entries(settings.llmProviders).map(
|
|
26940
|
-
|
|
26941
|
-
|
|
26942
|
-
|
|
26833
|
+
const providers = Object.entries(settings.llmProviders).map(
|
|
26834
|
+
([name, provider]) => ({
|
|
26835
|
+
id: name,
|
|
26836
|
+
provider
|
|
26837
|
+
})
|
|
26838
|
+
);
|
|
26943
26839
|
return providers;
|
|
26944
26840
|
};
|
|
26945
26841
|
const migrateSettingsV14toV15 = (settings) => {
|
|
@@ -27107,7 +27003,8 @@ const DEFAULT_SETTINGS = {
|
|
|
27107
27003
|
showTaskStateActions: true,
|
|
27108
27004
|
worktreeSymlinkFolders: ["node_modules", "vendor", "__pycache__", ".venv", "venv"],
|
|
27109
27005
|
contextCompactingThreshold: 0,
|
|
27110
|
-
contextCompactionType: ContextCompactionType.Compact
|
|
27006
|
+
contextCompactionType: ContextCompactionType.Compact,
|
|
27007
|
+
defaultWorkingMode: "local"
|
|
27111
27008
|
},
|
|
27112
27009
|
extensions: {
|
|
27113
27010
|
repositories: [AIDER_DESK_EXTENSIONS_REPO_URL],
|
|
@@ -27404,7 +27301,7 @@ const addProjectsFromEnv = async (store, modelManager, agentProfileManager) => {
|
|
|
27404
27301
|
const defaultAgentProfileId = agentProfileManager.getDefaultAgentProfileId();
|
|
27405
27302
|
const newProject = {
|
|
27406
27303
|
baseDir: projectPath.endsWith("/") ? projectPath.slice(0, -1) : projectPath,
|
|
27407
|
-
settings: getDefaultProjectSettings(store, providerModels.models || [], projectPath, defaultAgentProfileId),
|
|
27304
|
+
settings: getDefaultProjectSettings(store, providerModels.models || [], projectPath, defaultAgentProfileId, modelManager.getProviders()),
|
|
27408
27305
|
active: false
|
|
27409
27306
|
};
|
|
27410
27307
|
projectsFromEnv.push(newProject);
|